{"spec_id":"flamegraph-basic","library":"seaborn","language":"python","code":"\"\"\" anyplot.ai\nflamegraph-basic: Flame Graph for Performance Profiling\nLibrary: seaborn 0.13.2 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-06-08\n\"\"\"\n\nimport hashlib\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport seaborn as sns\nfrom matplotlib.patches import Rectangle\n\n\n# Theme-adaptive chrome 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# Imprint palette warm anchors. Flame graphs use a warm palette by domain\n# convention; staying inside the Imprint palette means picking its closest\n# warm members rather than inventing custom hexes.\nIMPRINT_AMBER = \"#DDCC77\"\nIMPRINT_OCHRE = \"#BD8233\"\nIMPRINT_RED = \"#AE3030\"\n\nsns.set_theme(\n    style=\"ticks\",\n    context=\"paper\",\n    rc={\n        \"figure.facecolor\": PAGE_BG,\n        \"axes.facecolor\": PAGE_BG,\n        \"axes.edgecolor\": INK_SOFT,\n        \"axes.labelcolor\": INK,\n        \"axes.titlecolor\": INK,\n        \"text.color\": INK,\n        \"xtick.color\": INK_SOFT,\n        \"ytick.color\": INK_SOFT,\n        \"grid.color\": INK,\n        \"grid.alpha\": 0.15,\n        \"legend.facecolor\": ELEVATED_BG,\n        \"legend.edgecolor\": INK_SOFT,\n    },\n)\n\n# Warm flame colormap from Imprint warm anchors via seaborn.blend_palette —\n# the seaborn-native way to build a perceptually smooth multi-stop gradient.\nflame_cmap = sns.blend_palette([IMPRINT_AMBER, IMPRINT_OCHRE, IMPRINT_RED], n_colors=256, as_cmap=True)\n\n# Simulated CPU profiling stacks — ~65 stack traces, 7 depth levels.\n# Models a web-service request handler with realistic bottleneck patterns.\nstacks = {\n    \"main\": 950,\n    \"main;init_config\": 50,\n    \"main;process_request\": 600,\n    \"main;cleanup\": 80,\n    \"main;log_metrics\": 180,\n    \"main;health_check\": 40,\n    \"main;init_config;load_env\": 25,\n    \"main;init_config;parse_args\": 20,\n    \"main;process_request;parse_headers\": 80,\n    \"main;process_request;authenticate\": 120,\n    \"main;process_request;handle_route\": 350,\n    \"main;process_request;send_response\": 30,\n    \"main;process_request;log_request\": 15,\n    \"main;cleanup;close_connections\": 45,\n    \"main;cleanup;flush_logs\": 30,\n    \"main;log_metrics;collect_stats\": 90,\n    \"main;log_metrics;write_to_disk\": 60,\n    \"main;log_metrics;aggregate\": 25,\n    \"main;health_check;ping_db\": 20,\n    \"main;health_check;check_memory\": 15,\n    \"main;process_request;parse_headers;decode_utf8\": 35,\n    \"main;process_request;parse_headers;validate_content_type\": 30,\n    \"main;process_request;parse_headers;extract_cookies\": 10,\n    \"main;process_request;authenticate;verify_token\": 70,\n    \"main;process_request;authenticate;check_permissions\": 40,\n    \"main;process_request;handle_route;query_database\": 200,\n    \"main;process_request;handle_route;serialize_response\": 90,\n    \"main;process_request;handle_route;compress\": 40,\n    \"main;process_request;handle_route;cache_lookup\": 15,\n    \"main;process_request;send_response;write_headers\": 15,\n    \"main;process_request;send_response;write_body\": 10,\n    \"main;log_metrics;collect_stats;cpu_usage\": 40,\n    \"main;log_metrics;collect_stats;mem_usage\": 35,\n    \"main;log_metrics;collect_stats;disk_io\": 10,\n    \"main;log_metrics;write_to_disk;buffer_flush\": 35,\n    \"main;log_metrics;write_to_disk;fsync\": 20,\n    \"main;log_metrics;aggregate;compute_p99\": 15,\n    \"main;log_metrics;aggregate;compute_mean\": 8,\n    \"main;cleanup;close_connections;tcp_shutdown\": 25,\n    \"main;cleanup;close_connections;release_pool\": 15,\n    \"main;process_request;authenticate;verify_token;decode_jwt\": 35,\n    \"main;process_request;authenticate;verify_token;check_expiry\": 20,\n    \"main;process_request;authenticate;verify_token;validate_sig\": 12,\n    \"main;process_request;authenticate;check_permissions;load_acl\": 22,\n    \"main;process_request;authenticate;check_permissions;match_role\": 14,\n    \"main;process_request;handle_route;query_database;build_sql\": 50,\n    \"main;process_request;handle_route;query_database;execute\": 120,\n    \"main;process_request;handle_route;query_database;fetch_rows\": 25,\n    \"main;process_request;handle_route;serialize_response;to_json\": 70,\n    \"main;process_request;handle_route;serialize_response;validate_schema\": 15,\n    \"main;process_request;handle_route;compress;gzip_encode\": 30,\n    \"main;process_request;handle_route;compress;set_headers\": 8,\n    \"main;log_metrics;collect_stats;cpu_usage;read_proc\": 25,\n    \"main;log_metrics;collect_stats;cpu_usage;calc_percent\": 12,\n    \"main;process_request;handle_route;query_database;execute;prepare_stmt\": 40,\n    \"main;process_request;handle_route;query_database;execute;send_query\": 55,\n    \"main;process_request;handle_route;query_database;execute;parse_result\": 20,\n    \"main;process_request;handle_route;serialize_response;to_json;encode_fields\": 40,\n    \"main;process_request;handle_route;serialize_response;to_json;format_dates\": 20,\n    \"main;process_request;authenticate;verify_token;decode_jwt;base64_decode\": 18,\n    \"main;process_request;authenticate;verify_token;decode_jwt;parse_claims\": 12,\n    \"main;process_request;handle_route;query_database;execute;send_query;tcp_write\": 30,\n    \"main;process_request;handle_route;query_database;execute;send_query;await_ack\": 20,\n}\n\ntotal_samples = stacks[\"main\"]\n\n# Group frames by depth\nframes = {}\nfor stack_path, samples in stacks.items():\n    depth = stack_path.count(\";\")\n    frames.setdefault(depth, []).append((stack_path, stack_path.split(\";\")[-1], samples))\n\n# Lay out x-positions: each child sits inside its parent's span, siblings adjacent.\npositions = {\"main\": (0, total_samples)}\nfor depth in sorted(frames):\n    if depth == 0:\n        continue\n    children_by_parent = {}\n    for stack_path, func_name, samples in frames[depth]:\n        parent = \";\".join(stack_path.split(\";\")[:-1])\n        children_by_parent.setdefault(parent, []).append((stack_path, func_name, samples))\n    for parent, children in children_by_parent.items():\n        if parent not in positions:\n            continue\n        parent_x, _ = positions[parent]\n        children.sort(key=lambda c: c[1])\n        cursor = parent_x\n        for stack_path, _func_name, samples in children:\n            positions[stack_path] = (cursor, samples)\n            cursor += samples\n\nmax_depth = max(frames)\nn_rows = max_depth + 1\n\n# Build a 2D grid as the heatmap substrate. Each sample → RES columns so adjacent\n# bars share cell edges with no antialiased seam. Empty regions stay NaN + masked.\nRES = 4\nn_cols = total_samples * RES\n\n\ndef color_for(name):\n    # hashlib so colors stay identical across PYTHONHASHSEED-randomised processes.\n    h = int(hashlib.md5(name.encode()).hexdigest(), 16) & 0xFFFF\n    return 0.18 + (h / 0xFFFF) * 0.78\n\n\ngrid = np.full((n_rows, n_cols), np.nan)\nfor stack_path, (x_pos, width) in positions.items():\n    depth = stack_path.count(\";\")\n    func_name = stack_path.split(\";\")[-1]\n    grid[depth, int(x_pos * RES) : int((x_pos + width) * RES)] = color_for(func_name)\n\n# Flip rows so depth 0 (root) renders at the bottom of the heatmap.\ngrid_display = grid[::-1]\nmask = np.isnan(grid_display)\n\n# Canvas — figsize × dpi → 3200 × 1800 px. No bbox_inches=\"tight\" on save.\nfig, ax = plt.subplots(figsize=(8, 4.5), dpi=400)\n\nsns.heatmap(\n    grid_display,\n    ax=ax,\n    cmap=flame_cmap,\n    mask=mask,\n    cbar=False,\n    linewidths=0,\n    xticklabels=False,\n    yticklabels=False,\n    vmin=0.0,\n    vmax=1.0,\n    rasterized=True,\n)\n\n# In-bar function-name labels. Bars below 0.045 fraction skip labels — at 400px\n# web preview their truncated 5pt text was borderline and didn't add value.\nfor stack_path, (x_pos, width) in positions.items():\n    fraction = width / total_samples\n    if fraction <= 0.045:\n        continue\n\n    func_name = stack_path.split(\";\")[-1]\n    pct = fraction * 100\n    label = f\"{func_name} ({pct:.0f}%)\" if fraction > 0.07 else func_name\n\n    fs = 7 if fraction > 0.09 else 6\n    samples_per_char = {7: 9.0, 6: 7.6}[fs]\n    max_chars = max(3, int(width / samples_per_char))\n    if len(label) > max_chars:\n        label = label[: max(3, max_chars - 1)] + \"…\"\n\n    depth = stack_path.count(\";\")\n    r, g, b = flame_cmap(color_for(func_name))[:3]\n    luminance = 0.299 * r + 0.587 * g + 0.114 * b\n    text_color = INK if luminance > 0.55 else \"#FAF8F1\"\n\n    ax.text(\n        (x_pos + width / 2) * RES,\n        (max_depth - depth) + 0.5,\n        label,\n        ha=\"center\",\n        va=\"center\",\n        fontsize=fs,\n        fontweight=\"semibold\" if fraction > 0.12 else \"regular\",\n        color=text_color,\n        clip_on=True,\n        zorder=5,\n    )\n\n# Brand-green outline on the hot path (greatest-width child from root to leaf) —\n# gives the eye an obvious bottleneck focal point at a glance.\nHOT_GREEN = \"#009E73\"\nhot_path = [\"main\"]\ncurrent = \"main\"\nwhile True:\n    candidates = [\n        (p, w)\n        for p, (_, w) in positions.items()\n        if p.startswith(current + \";\") and p.count(\";\") == current.count(\";\") + 1\n    ]\n    if not candidates:\n        break\n    current = max(candidates, key=lambda c: c[1])[0]\n    hot_path.append(current)\n\nfor stack_path in hot_path:\n    x_pos, width = positions[stack_path]\n    depth = stack_path.count(\";\")\n    ax.add_patch(\n        Rectangle(\n            (x_pos * RES, max_depth - depth),\n            width * RES,\n            1,\n            fill=False,\n            edgecolor=HOT_GREEN,\n            linewidth=1.5,\n            zorder=4,\n            clip_on=True,\n        )\n    )\n\n# Axes\nax.set_xlabel(\"Samples\", fontsize=10)\nax.set_ylabel(\"Stack depth\", fontsize=10)\nax.set_title(\"flamegraph-basic · python · seaborn · anyplot.ai\", fontsize=12, fontweight=\"medium\", pad=10)\nax.tick_params(axis=\"both\", labelsize=8, length=0)\n\nax.set_yticks([i + 0.5 for i in range(n_rows)])\nax.set_yticklabels([f\"D{d}\" for d in range(max_depth, -1, -1)])\n\nxtick_positions = np.arange(0, total_samples + 1, 200)\nax.set_xticks(xtick_positions * RES)\nax.set_xticklabels([str(int(x)) for x in xtick_positions])\n\n# Heatmap forces all spines on; remove top & right per default style.\nsns.despine(ax=ax, top=True, right=True)\nax.spines[\"left\"].set_color(INK_SOFT)\nax.spines[\"bottom\"].set_color(INK_SOFT)\n\n# Subtle vertical guides only — horizontal grid would fight the stacked bars.\nax.xaxis.grid(True, alpha=0.12, linewidth=0.5, color=INK)\nax.yaxis.grid(False)\nax.set_axisbelow(False)\n\n# Pad margins via subplots_adjust — tight_layout/bbox_inches='tight' would\n# shave the canvas off-target and fail the post-render size gate.\nfig.subplots_adjust(left=0.06, right=0.985, top=0.92, bottom=0.11)\n\nplt.savefig(f\"plot-{THEME}.png\", dpi=400, facecolor=PAGE_BG)\n"}