{"spec_id":"flamegraph-basic","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nflamegraph-basic: Flame Graph for Performance Profiling\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-06-08\n\"\"\"\n\nimport os\n\nimport matplotlib.colors as mcolors\nimport matplotlib.patches as mpatches\nimport matplotlib.patheffects as pe\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n\n# Theme tokens\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Warm flame palette built from Imprint anchors (amber → ochre → matte red).\n# Spec calls for the conventional warm flame-graph aesthetic; these three\n# stops are the Imprint palette members that map onto that convention.\nWARM_AMBER = \"#DDCC77\"  # Imprint amber anchor (cool / low-sample)\nWARM_OCHRE = \"#BD8233\"  # Imprint position 4 (warm midtone)\nWARM_RED = \"#AE3030\"  # Imprint position 5 (hot / high-sample)\n\n# Data — simulated CPU profiling stacks with sample counts.\n# Depth-first ordering so each parent is inserted before its children\n# (the layout loop below relies on dict insertion order).\nstacks = {\n    \"main\": 950,\n    \"main;process_request\": 800,\n    \"main;process_request;parse_input\": 180,\n    \"main;process_request;parse_input;tokenize\": 95,\n    \"main;process_request;parse_input;tokenize;split_lines\": 50,\n    \"main;process_request;parse_input;tokenize;split_lines;find_newline\": 30,\n    \"main;process_request;parse_input;tokenize;regex_match\": 35,\n    \"main;process_request;parse_input;tokenize;regex_match;nfa_run\": 22,\n    \"main;process_request;parse_input;validate\": 45,\n    \"main;process_request;parse_input;validate;check_schema\": 25,\n    \"main;process_request;parse_input;validate;check_schema;lookup_field\": 15,\n    \"main;process_request;parse_input;validate;check_types\": 15,\n    \"main;process_request;parse_input;normalize_keys\": 30,\n    \"main;process_request;parse_input;normalize_keys;lowercase\": 18,\n    \"main;process_request;compute\": 450,\n    \"main;process_request;compute;matrix_multiply\": 280,\n    \"main;process_request;compute;matrix_multiply;dot_product\": 180,\n    \"main;process_request;compute;matrix_multiply;dot_product;simd_loop\": 110,\n    \"main;process_request;compute;matrix_multiply;dot_product;accumulate\": 55,\n    \"main;process_request;compute;matrix_multiply;allocate_buffer\": 50,\n    \"main;process_request;compute;matrix_multiply;allocate_buffer;malloc\": 30,\n    \"main;process_request;compute;matrix_multiply;allocate_buffer;zero_memory\": 15,\n    \"main;process_request;compute;matrix_multiply;prefetch_data\": 30,\n    \"main;process_request;compute;matrix_multiply;prefetch_data;cache_warm\": 20,\n    \"main;process_request;compute;transform\": 100,\n    \"main;process_request;compute;transform;normalize\": 55,\n    \"main;process_request;compute;transform;normalize;compute_mean\": 30,\n    \"main;process_request;compute;transform;normalize;subtract_mean\": 18,\n    \"main;process_request;compute;transform;scale\": 30,\n    \"main;process_request;compute;aggregate\": 50,\n    \"main;process_request;compute;aggregate;group_by\": 28,\n    \"main;process_request;compute;aggregate;group_by;hash_keys\": 18,\n    \"main;process_request;compute;aggregate;reduce\": 15,\n    \"main;process_request;send_response\": 120,\n    \"main;process_request;send_response;serialize\": 70,\n    \"main;process_request;send_response;serialize;to_json\": 40,\n    \"main;process_request;send_response;serialize;to_json;format_value\": 25,\n    \"main;process_request;send_response;serialize;escape_strings\": 20,\n    \"main;process_request;send_response;compress\": 25,\n    \"main;process_request;send_response;compress;gzip_encode\": 18,\n    \"main;process_request;send_response;write_socket\": 20,\n    \"main;process_request;send_response;write_socket;syscall_write\": 15,\n    \"main;process_request;log_request\": 30,\n    \"main;initialize\": 100,\n    \"main;initialize;load_config\": 55,\n    \"main;initialize;load_config;read_file\": 30,\n    \"main;initialize;load_config;read_file;open_fd\": 15,\n    \"main;initialize;load_config;parse_yaml\": 20,\n    \"main;initialize;load_config;parse_yaml;tokenize_yaml\": 12,\n    \"main;initialize;setup_logging\": 25,\n    \"main;initialize;setup_logging;open_handlers\": 15,\n    \"main;initialize;setup_logging;open_handlers;create_socket\": 8,\n    \"main;initialize;register_handlers\": 15,\n    \"main;gc_collect\": 40,\n    \"main;gc_collect;mark_phase\": 25,\n    \"main;gc_collect;mark_phase;scan_roots\": 15,\n    \"main;gc_collect;mark_phase;scan_roots;walk_stack\": 10,\n    \"main;gc_collect;sweep_phase\": 12,\n}\n\ntotal_samples = stacks[\"main\"]\n\n# Identify the hot path (widest child at each depth)\nhot_path_stacks = {\"main\"}\ncurrent = \"main\"\nwhile True:\n    children = {\n        k: v for k, v in stacks.items() if k.startswith(current + \";\") and k.count(\";\") == current.count(\";\") + 1\n    }\n    if not children:\n        break\n    hottest = max(children, key=children.get)\n    hot_path_stacks.add(hottest)\n    current = hottest\n\n# Build flame graph rectangles with parent offset tracking\npositions = {\"main\": (0.0, total_samples)}\nrects = []\nparent_offsets = {}\n\nfor stack_path, samples in stacks.items():\n    parts = stack_path.split(\";\")\n    depth = len(parts) - 1\n    func_name = parts[-1]\n    is_hot = stack_path in hot_path_stacks\n\n    if depth == 0:\n        rects.append((depth, func_name, 0.0, samples, is_hot))\n        continue\n\n    parent = \";\".join(parts[:-1])\n    if parent not in positions:\n        continue\n\n    parent_x, _ = positions[parent]\n    x_start = parent_offsets.get(parent, parent_x)\n    positions[stack_path] = (x_start, samples)\n    parent_offsets[parent] = x_start + samples\n    rects.append((depth, func_name, x_start, samples, is_hot))\n\n# Warm sequential cmap built from Imprint palette members\nflame_cmap = mcolors.LinearSegmentedColormap.from_list(\"flame_imprint\", [WARM_AMBER, WARM_OCHRE, WARM_RED], N=256)\n\n# Plot — canvas 3200x1800 (figsize 8x4.5 @ dpi 400)\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400, facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\nbar_height = 0.92\nmax_depth = max(r[0] for r in rects)\n\n# Heuristic for whether a label fits inside its bar width (in data units).\n# Axes width ≈ 82% of figsize width; x-axis spans `total_samples + 25`.\n# A character at fontsize N occupies ~N * 0.55 / 72 inches horizontally.\nsamples_per_inch = (total_samples + 25) / (8 * 0.82)\n\nfor depth, func_name, x_start, width, _is_hot in rects:\n    proportion = width / total_samples\n    color_val = np.clip(proportion**0.6 * 1.8, 0.05, 1.0)\n    color = flame_cmap(color_val)\n\n    rect = mpatches.Rectangle(\n        (x_start, depth - bar_height / 2),\n        width,\n        bar_height,\n        facecolor=color,\n        edgecolor=PAGE_BG,\n        linewidth=0.6,\n        zorder=2,\n    )\n    ax.add_patch(rect)\n\n    bar_fraction = width / total_samples\n    if bar_fraction < 0.04:\n        continue\n\n    fontsize = 9 if bar_fraction > 0.18 else 7.5\n    fontweight = \"bold\" if bar_fraction > 0.18 else \"medium\"\n    char_w_samples = fontsize * 0.55 / 72 * samples_per_inch\n\n    # Try name + percentage first; fall back to name only; skip if neither fits.\n    candidate = f\"{func_name} ({bar_fraction:.0%})\"\n    if len(candidate) * char_w_samples > width * 0.92:\n        candidate = func_name\n    if len(candidate) * char_w_samples > width * 0.92:\n        continue\n    label = candidate\n\n    # Light text on the darker (red) end of the cmap, dark text on the\n    # lighter (amber/ochre) end — data colors are theme-independent so\n    # this decision uses color_val, not THEME.\n    light_bar = color_val < 0.6\n    text_color = \"#1A1A17\" if light_bar else \"#FBEFE2\"\n    stroke_color = \"#FAF8F1AA\" if light_bar else \"#00000055\"\n    path_effects = [pe.withStroke(linewidth=1.4, foreground=stroke_color)]\n\n    ax.text(\n        x_start + width / 2,\n        depth,\n        label,\n        ha=\"center\",\n        va=\"center\",\n        fontsize=fontsize,\n        fontweight=fontweight,\n        color=text_color,\n        clip_on=True,\n        path_effects=path_effects,\n        zorder=5,\n    )\n\n# Hot path annotation pointing to the deepest hot path bar\nhot_leaf = max((r for r in rects if r[4]), key=lambda r: r[0])\nleaf_cx = hot_leaf[2] + hot_leaf[3] / 2\nax.annotate(\n    \"  Hot path (CPU bottleneck)  \",\n    xy=(leaf_cx, hot_leaf[0] + bar_height / 2 + 0.02),\n    xytext=(leaf_cx + 260, hot_leaf[0] + 1.25),\n    fontsize=8,\n    fontweight=\"semibold\",\n    color=INK,\n    ha=\"center\",\n    arrowprops={\"arrowstyle\": \"-|>\", \"color\": INK_SOFT, \"lw\": 1.0, \"connectionstyle\": \"arc3,rad=0.25\"},\n    bbox={\n        \"boxstyle\": \"round,pad=0.4\",\n        \"facecolor\": ELEVATED_BG,\n        \"edgecolor\": INK_SOFT,\n        \"alpha\": 0.95,\n        \"linewidth\": 0.6,\n    },\n    zorder=10,\n)\n\n# Style\nax.set_xlim(-10, total_samples + 15)\nax.set_ylim(-0.6, max_depth + 1.7)\nax.set_xlabel(\"CPU Samples\", fontsize=10, color=INK, labelpad=6)\nax.set_ylabel(\"Stack Depth\", fontsize=10, color=INK, labelpad=6)\nax.set_title(\"flamegraph-basic · python · matplotlib · anyplot.ai\", fontsize=12, fontweight=\"medium\", pad=10, color=INK)\nax.tick_params(axis=\"both\", labelsize=8, colors=INK_SOFT)\nax.set_yticks(range(max_depth + 1))\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_linewidth(0.6)\n    ax.spines[s].set_color(INK_SOFT)\n\nplt.tight_layout()\n# Do NOT use bbox_inches=\"tight\" — it would shave the canvas off-target.\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}