{"spec_id":"flamegraph-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\nflamegraph-basic: Flame Graph for Performance Profiling\nLibrary: plotly 6.8.0 | Python 3.13.13\nQuality: 88/100 | Updated: 2026-06-08\n\"\"\"\n\nimport os\n\nimport plotly.graph_objects as go\n\n\n# Theme tokens (Imprint palette + theme-adaptive chrome)\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nGRID = \"rgba(26,26,23,0.15)\" if THEME == \"light\" else \"rgba(240,239,232,0.15)\"\n\n# Imprint warm trio (semantic exception: flame graphs conventionally use warm colors).\n# Hot path uses matte red as the \"alert / bottleneck\" semantic anchor; non-hot bars\n# alternate ochre and amber for high-contrast neighbour differentiation.\nHOT_COLOR = \"#AE3030\"  # Imprint matte red — semantic anchor for bottleneck\nOCHRE = \"#BD8233\"  # Imprint ochre\nAMBER = \"#DDCC77\"  # Imprint amber semantic anchor (warning)\nBAR_EDGE = PAGE_BG  # subtle inter-bar separation against page surface\n\n# Data — simulated CPU profiling: web request handler stack samples\n# Format: (semicolon_stack_path, self_samples)\nraw_stacks = [\n    (\"main\", 0),\n    (\"main;process_request\", 0),\n    (\"main;process_request;parse_headers\", 120),\n    (\"main;process_request;parse_headers;validate_encoding\", 45),\n    (\"main;process_request;parse_body\", 80),\n    (\"main;process_request;parse_body;decode_json\", 60),\n    (\"main;process_request;parse_body;decode_json;alloc_buffer\", 25),\n    (\"main;process_request;route_handler\", 0),\n    (\"main;process_request;route_handler;auth_check\", 90),\n    (\"main;process_request;route_handler;auth_check;verify_token\", 70),\n    (\"main;process_request;route_handler;auth_check;verify_token;decrypt\", 55),\n    (\"main;process_request;route_handler;auth_check;load_session\", 40),\n    (\"main;process_request;route_handler;query_db\", 0),\n    (\"main;process_request;route_handler;query_db;connect\", 30),\n    (\"main;process_request;route_handler;query_db;execute_sql\", 150),\n    (\"main;process_request;route_handler;query_db;execute_sql;optimize_plan\", 85),\n    (\"main;process_request;route_handler;query_db;execute_sql;fetch_rows\", 110),\n    (\"main;process_request;route_handler;query_db;execute_sql;fetch_rows;deserialize\", 65),\n    (\"main;process_request;route_handler;render_template\", 95),\n    (\"main;process_request;route_handler;render_template;compile\", 50),\n    (\"main;process_request;route_handler;render_template;escape_html\", 35),\n    (\"main;process_request;send_response\", 0),\n    (\"main;process_request;send_response;compress\", 75),\n    (\"main;process_request;send_response;write_socket\", 55),\n    (\"main;gc_collect\", 40),\n    (\"main;gc_collect;mark_sweep\", 30),\n    (\"main;gc_collect;compact_heap\", 20),\n    (\"main;log_metrics\", 25),\n    (\"main;log_metrics;serialize\", 15),\n]\n\nstack_self = dict(raw_stacks)\n\n# Collect all stacks (including intermediate ancestors that may have no self samples)\nall_stacks = set()\nfor stack, _ in raw_stacks:\n    parts = stack.split(\";\")\n    for i in range(len(parts)):\n        all_stacks.add(\";\".join(parts[: i + 1]))\n\n# Inclusive samples = self + sum of all descendants\ninclusive = {}\nfor stack in all_stacks:\n    total = stack_self.get(stack, 0)\n    prefix = stack + \";\"\n    for other, samples in stack_self.items():\n        if other.startswith(prefix) and samples > 0:\n            total += samples\n    inclusive[stack] = total\n\ntotal_samples = inclusive[\"main\"]\n\n# Build children map and sort siblings alphabetically (flame graph convention)\nchildren_map = {}\nfor stack in all_stacks:\n    parts = stack.split(\";\")\n    if len(parts) > 1:\n        parent = \";\".join(parts[:-1])\n        children_map.setdefault(parent, []).append(stack)\n\nfor parent in children_map:\n    children_map[parent].sort(key=lambda s: s.split(\";\")[-1])\n\n# Assign x positions iteratively (DFS via worklist)\nbars = []\nwork_stack = [(\"main\", 0.0)]\nwhile work_stack:\n    stack, x_start = work_stack.pop()\n    width = inclusive[stack] / total_samples\n    depth = stack.count(\";\")\n    func_name = stack.split(\";\")[-1]\n    bars.append((x_start, width, depth, func_name, inclusive[stack], stack))\n    if stack in children_map:\n        child_x = x_start\n        for child in reversed(children_map[stack]):\n            child_width = inclusive[child] / total_samples\n            work_stack.append((child, child_x))\n            child_x += child_width\n\n# Identify hot path: widest bar at each depth\nhot_path_stacks = set()\ndepth_bars = {}\nfor _, _, depth, _, samples, stack in bars:\n    if depth not in depth_bars or samples > depth_bars[depth][1]:\n        depth_bars[depth] = (stack, samples)\nfor stack, _ in depth_bars.values():\n    hot_path_stacks.add(stack)\n\nmax_depth = max(b[2] for b in bars)\n\n# Color assignment: hot path -> matte red; others alternate ochre/amber so neighbours\n# at the same depth never share a color (deterministic by sibling x-position rank).\nsibling_rank = {}\nby_depth = {}\nfor x_start, _w, depth, _f, _s, stack in bars:\n    by_depth.setdefault(depth, []).append((x_start, stack))\nfor items in by_depth.values():\n    items.sort()\n    for rank, (_x, stack) in enumerate(items):\n        sibling_rank[stack] = rank\n\n# Plot — group bars by color into batched horizontal-bar traces (idiomatic Plotly)\ngroups = {\"hot\": [], \"ochre\": [], \"amber\": []}\nfor x_start, width, depth, func_name, samples, stack in bars:\n    if stack in hot_path_stacks:\n        key = \"hot\"\n    elif sibling_rank[stack] % 2 == 0:\n        key = \"ochre\"\n    else:\n        key = \"amber\"\n    groups[key].append((x_start, width, depth, func_name, samples, stack))\n\ncolor_for = {\"hot\": HOT_COLOR, \"ochre\": OCHRE, \"amber\": AMBER}\nlegend_label = {\"hot\": \"Hot path (widest at depth)\", \"ochre\": \"\", \"amber\": \"\"}\n\nbar_height = 0.98  # flame-graph convention: depth rows touch; BAR_EDGE provides hairline separation\nfig = go.Figure()\n\n# Only \"Hot path\" appears in legend — ochre/amber alternation is decorative, not semantic\nshow_in_legend = {\"hot\": True, \"ochre\": False, \"amber\": False}\n\nfor key in (\"ochre\", \"amber\", \"hot\"):\n    group = groups[key]\n    if not group:\n        continue\n    widths = [g[1] for g in group]\n    bases = [g[0] for g in group]\n    depths = [g[2] for g in group]\n    hover_texts = [\n        f\"<b>{g[3]}</b><br>Stack: {g[5]}<br>Samples: {g[4]} ({g[1] * 100:.1f}%)<extra></extra>\" for g in group\n    ]\n    fig.add_trace(\n        go.Bar(\n            x=widths,\n            y=depths,\n            base=bases,\n            orientation=\"h\",\n            marker={\"color\": color_for[key], \"line\": {\"color\": BAR_EDGE, \"width\": 0.6}},\n            width=bar_height,\n            name=legend_label[key],\n            showlegend=show_in_legend[key],\n            hovertemplate=hover_texts,\n        )\n    )\n\n# Function-name labels — strict pixel-fit gate so labels never overflow bar bounds.\n# Logical canvas = 800 px × scale 4 = 3200 px. Inner plot width = 800 - margin.l - margin.r.\n# At 13 px monospace, each glyph is ~7.8 logical px; drop the label if even a 3-char + ellipsis\n# truncation would not fit — the hover tooltip already carries the full function name.\nINNER_PLOT_PX = 800 - 80 - 40\nPX_PER_CHAR = 7.8\nMIN_FIT_CHARS = 4\n\nfor x_start, width, depth, func_name, _samples, stack in bars:\n    bar_px = INNER_PLOT_PX * width\n    max_chars = int(bar_px / PX_PER_CHAR)\n    if max_chars < MIN_FIT_CHARS:\n        continue\n    if len(func_name) <= max_chars:\n        display_text = func_name\n    else:\n        display_text = func_name[: max_chars - 1] + \"…\"\n    is_hot = stack in hot_path_stacks\n    font_color = \"#F0EFE8\" if is_hot else INK\n    fig.add_annotation(\n        x=x_start + width / 2,\n        y=depth,\n        text=f\"<b>{display_text}</b>\" if is_hot else display_text,\n        showarrow=False,\n        font={\"size\": 13, \"color\": font_color, \"family\": \"Consolas, Monaco, monospace\"},\n        xanchor=\"center\",\n        yanchor=\"middle\",\n    )\n\n# Style\ntitle_text = \"flamegraph-basic · python · plotly · anyplot.ai\"\nfig.update_layout(\n    autosize=False,\n    title={\"text\": title_text, \"font\": {\"size\": 16, \"color\": INK}, \"x\": 0.5, \"xanchor\": \"center\"},\n    barmode=\"overlay\",\n    bargap=0,\n    hovermode=\"closest\",\n    hoverlabel={\n        \"bgcolor\": ELEVATED_BG,\n        \"bordercolor\": INK_SOFT,\n        \"font\": {\"family\": \"Consolas, Monaco, monospace\", \"size\": 12, \"color\": INK},\n        \"align\": \"left\",\n    },\n    xaxis={\n        \"title\": {\"text\": \"Proportion of Total Samples\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"range\": [0, 1],\n        \"tickformat\": \".0%\",\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"linecolor\": INK_SOFT,\n    },\n    yaxis={\n        \"title\": {\"text\": \"Stack Depth (root → leaf)\", \"font\": {\"size\": 12, \"color\": INK}},\n        \"tickfont\": {\"size\": 10, \"color\": INK_SOFT},\n        \"dtick\": 1,\n        \"range\": [-0.6, max_depth + 0.6],\n        \"showgrid\": False,\n        \"zeroline\": False,\n        \"linecolor\": INK_SOFT,\n    },\n    legend={\n        \"bgcolor\": \"rgba(0,0,0,0)\",\n        \"bordercolor\": \"rgba(0,0,0,0)\",\n        \"borderwidth\": 0,\n        \"font\": {\"size\": 10, \"color\": INK_SOFT},\n        \"orientation\": \"h\",\n        \"x\": 0.99,\n        \"xanchor\": \"right\",\n        \"y\": 1.02,\n        \"yanchor\": \"bottom\",\n        \"itemclick\": False,\n        \"itemdoubleclick\": False,\n    },\n    margin={\"l\": 80, \"r\": 40, \"t\": 90, \"b\": 60},\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    font={\"color\": INK},\n)\n\n# Save — hard target 3200×1800 (landscape): width=800, height=450, scale=4\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\n# Interactive HTML: cleaner Plotly modebar — drop selection tools that don't apply to flame graphs\nfig.write_html(\n    f\"plot-{THEME}.html\",\n    include_plotlyjs=\"cdn\",\n    config={\n        \"displaylogo\": False,\n        \"modeBarButtonsToRemove\": [\"select2d\", \"lasso2d\", \"autoScale2d\"],\n        \"toImageButtonOptions\": {\"format\": \"png\", \"filename\": \"flamegraph-basic\", \"scale\": 4},\n    },\n)\n"}