{"spec_id":"flamegraph-basic","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nflamegraph-basic: Flame Graph for Performance Profiling\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-06-08\n\"\"\"\n\nimport importlib\nimport os\nimport re\nimport sys\nfrom collections import defaultdict\n\nimport cairosvg\n\n\n# Import pygal package (avoid name collision with this filename)\n_saved = sys.path.pop(0)\n_pygal = importlib.import_module(\"pygal\")\n_Style = importlib.import_module(\"pygal.style\").Style\nsys.path.insert(0, _saved)\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\nINK_DARK = \"#1A1A17\"  # for bright bars\nINK_LIGHT = \"#F0EFE8\"  # for dark bars (improves contrast on matte-red rows)\n\n# Warm flame-graph gradient built from Imprint anchors: amber → ochre → matte red.\n# Flame graphs carry a strong domain convention for warm yellow/orange/red coloring\n# (Brendan Gregg), so the Semantic Exception in default-style-guide.md applies —\n# we derive the gradient from Imprint members rather than substituting a foreign cmap.\nIMPRINT_AMBER = (0xDD, 0xCC, 0x77)\nIMPRINT_OCHRE = (0xBD, 0x82, 0x33)\nIMPRINT_RED = (0xAE, 0x30, 0x30)\n\n# Data — simulated CPU profile of a web service request handler (Brendan-Gregg\n# style stack-trace + sample-count format). 42 unique stacks across 6 depth levels.\nstacks = [\n    (\"main\", 1000),\n    (\"main;process_request\", 780),\n    (\"main;process_request;parse_headers\", 140),\n    (\"main;process_request;parse_headers;decode_base64\", 55),\n    (\"main;process_request;parse_headers;validate_utf8\", 40),\n    (\"main;process_request;parse_headers;normalize_case\", 25),\n    (\"main;process_request;parse_headers;strip_whitespace\", 15),\n    (\"main;process_request;route_dispatch\", 200),\n    (\"main;process_request;route_dispatch;regex_match\", 95),\n    (\"main;process_request;route_dispatch;regex_match;compile_pattern\", 35),\n    (\"main;process_request;route_dispatch;regex_match;walk_dfa\", 50),\n    (\"main;process_request;route_dispatch;lookup_handler\", 70),\n    (\"main;process_request;route_dispatch;lookup_handler;hash_path\", 30),\n    (\"main;process_request;route_dispatch;lookup_handler;cache_get\", 25),\n    (\"main;process_request;execute_handler\", 340),\n    (\"main;process_request;execute_handler;authorize\", 60),\n    (\"main;process_request;execute_handler;authorize;verify_token\", 35),\n    (\"main;process_request;execute_handler;authorize;check_acl\", 20),\n    (\"main;process_request;execute_handler;db_query\", 160),\n    (\"main;process_request;execute_handler;db_query;connect_pool\", 25),\n    (\"main;process_request;execute_handler;db_query;execute_sql\", 90),\n    (\"main;process_request;execute_handler;db_query;execute_sql;send_buffer\", 40),\n    (\"main;process_request;execute_handler;db_query;execute_sql;await_result\", 35),\n    (\"main;process_request;execute_handler;db_query;fetch_rows\", 30),\n    (\"main;process_request;execute_handler;serialize_json\", 80),\n    (\"main;process_request;execute_handler;serialize_json;encode_utf8\", 35),\n    (\"main;process_request;execute_handler;serialize_json;escape_strings\", 25),\n    (\"main;process_request;execute_handler;template_render\", 40),\n    (\"main;process_request;execute_handler;template_render;walk_ast\", 25),\n    (\"main;process_request;send_response\", 100),\n    (\"main;process_request;send_response;compress_gzip\", 55),\n    (\"main;process_request;send_response;compress_gzip;deflate_block\", 35),\n    (\"main;process_request;send_response;write_socket\", 30),\n    (\"main;gc_collect\", 90),\n    (\"main;gc_collect;mark_sweep\", 50),\n    (\"main;gc_collect;mark_sweep;scan_roots\", 25),\n    (\"main;gc_collect;compact_heap\", 25),\n    (\"main;log_metrics\", 70),\n    (\"main;log_metrics;format_json\", 30),\n    (\"main;log_metrics;write_buffer\", 25),\n    (\"main;log_metrics;flush_socket\", 15),\n    (\"main;idle_wait\", 60),\n]\n\ntotal_samples = 1000\n\n# Build call-stack tree\ntree = {}\nfor stack_path, samples in stacks:\n    node = tree\n    for part in stack_path.split(\";\"):\n        if part not in node:\n            node[part] = {\"_samples\": 0}\n        node = node[part]\n    node[\"_samples\"] = samples\n\n# BFS to compute rectangles per depth: (depth, x_start_frac, width_frac, label, samples)\nrectangles = []\nqueue = [(tree, 0, 0.0)]\nwhile queue:\n    current_node, depth, x_offset = queue.pop(0)\n    for name in sorted(current_node.keys()):\n        if name.startswith(\"_\"):\n            continue\n        child = current_node[name]\n        samples = child.get(\"_samples\", 0)\n        if samples == 0:\n            continue\n        w = samples / total_samples\n        rectangles.append((depth, x_offset, w, name, samples))\n        child_x = x_offset\n        for child_name in sorted(child.keys()):\n            if child_name.startswith(\"_\"):\n                continue\n            grandchild = child[child_name]\n            gs = grandchild.get(\"_samples\", 0)\n            if gs > 0:\n                queue.append(({child_name: grandchild}, depth + 1, child_x))\n                child_x += gs / total_samples\n        x_offset += w\n\ndepth_rects = defaultdict(list)\nfor depth, x_frac, w_frac, label, samples in rectangles:\n    depth_rects[depth].append((x_frac, w_frac, label, samples))\nfor d in depth_rects:\n    depth_rects[d].sort()\n\nmax_depth = max(depth_rects.keys())\nnum_levels = max_depth + 1\n\n# Build ordered segments per depth with transparent spacers for alignment\nall_segments = []\nfor d in range(num_levels):\n    segments = []\n    current_x = 0.0\n    for x_frac, w_frac, label, samples in depth_rects.get(d, []):\n        gap = x_frac - current_x\n        if gap > 1e-4:\n            segments.append((gap * total_samples, \"\", True))\n        segments.append((samples, label, False))\n        current_x = x_frac + w_frac\n    trailing = 1.0 - current_x\n    if trailing > 1e-4:\n        segments.append((trailing * total_samples, \"\", True))\n    all_segments.append(segments)\n\nmax_segs = max(len(s) for s in all_segments)\n\n# Pre-compute one warm color per depth: amber → ochre → matte red\n# Also pick a per-depth label ink: light text on low-luminance bars stays punchy\n# on the matte-red rows in both themes (the dark-theme weakness flagged in review).\ndepth_colors = []\ndepth_label_inks = []\nfor d in range(num_levels):\n    t = d / max_depth if max_depth else 0.0\n    if t < 0.5:\n        c0, c1, u = IMPRINT_AMBER, IMPRINT_OCHRE, t / 0.5\n    else:\n        c0, c1, u = IMPRINT_OCHRE, IMPRINT_RED, (t - 0.5) / 0.5\n    r = round(c0[0] + (c1[0] - c0[0]) * u)\n    g = round(c0[1] + (c1[1] - c0[1]) * u)\n    b = round(c0[2] + (c1[2] - c0[2]) * u)\n    depth_colors.append(f\"#{r:02X}{g:02X}{b:02X}\")\n    perceived_lum = 0.299 * r + 0.587 * g + 0.114 * b\n    depth_label_inks.append(INK_DARK if perceived_lum >= 130 else INK_LIGHT)\n\n# Style\ncustom_style = _Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    guide_stroke_color=PAGE_BG,\n    guide_stroke_dasharray=\"0,0\",\n    major_guide_stroke_color=PAGE_BG,\n    major_guide_stroke_dasharray=\"0,0\",\n    colors=(\"#BD8233\",),\n    title_font_size=66,\n    label_font_size=56,\n    major_label_font_size=36,\n    legend_font_size=44,\n    value_font_size=30,\n    value_label_font_size=30,\n    tooltip_font_size=28,\n    font_family=\"DejaVu Sans Mono, Menlo, Consolas, monospace\",\n    stroke_width=1.5,\n)\n\n# Chart — flame graph as a HorizontalStackedBar, one row per stack depth\nchart = _pygal.HorizontalStackedBar(\n    width=3200,\n    height=1800,\n    style=custom_style,\n    title=\"flamegraph-basic · python · pygal · anyplot.ai\",\n    x_title=\"Samples\",\n    y_title=\"Call Stack Depth\",\n    show_legend=False,\n    show_y_guides=False,\n    show_x_guides=False,\n    print_values=False,\n    print_labels=True,\n    spacing=0,\n    rounded_bars=0,\n    margin_top=20,\n    margin_bottom=30,\n    margin_right=90,\n    margin_left=20,\n    truncate_label=-1,\n    truncate_legend=-1,\n)\n\n# Collapse the 6% inter-row gap pygal inserts by default — flame-graph rows must touch\nchart._series_margin = 0.0\n\n# Row labels: depth 0 at bottom, leaves on top (matches Brendan Gregg convention)\nchart.x_labels = [f\"Depth {d}\" for d in range(num_levels)]\n\n# In-bar label gating: render label only if it fits inside its bar\n# (value_font_size=30 source-px in a monospace font ≈ 18 px-per-char at this canvas)\nchart_usable_width_px = 3200 - 220 - 60\npx_per_char = 18\n\n# Add one series per stack-column position; each value carries its own bar color + label\nfor col in range(max_segs):\n    values = []\n    for d in range(num_levels):\n        segs = all_segments[d]\n        if col >= len(segs):\n            values.append(None)\n            continue\n        value, label, is_spacer = segs[col]\n        if is_spacer:\n            values.append(\n                {\"value\": value, \"color\": \"transparent\", \"style\": \"stroke: none; fill: transparent; opacity: 0\"}\n            )\n            continue\n        color = depth_colors[d]\n        with_value = f\"{label} ({int(value)})\"\n        bar_px = (value / total_samples) * chart_usable_width_px\n        if len(with_value) * px_per_char <= bar_px:\n            display = with_value\n        elif len(label) * px_per_char <= bar_px:\n            display = label\n        else:\n            display = \"\"\n        values.append(\n            {\n                \"value\": value,\n                \"color\": color,\n                \"label\": display,\n                \"style\": f\"stroke: {PAGE_BG}; stroke-width: 1.2; fill: {color}\",\n            }\n        )\n    chart.add(\"\", values)\n\n# Output — render SVG, recolor in-bar labels per bar luminance, then write PNG + HTML.\n# pygal's CSS sets `.label { fill: ... }` globally. We inject an inline style on each\n# in-bar `<text class=\"label\">` based on its y-position → depth → light/dark ink, so\n# the matte-red deepest bars get a light ink (the dark-theme weakness flagged in review).\nsvg_content = chart.render(is_unicode=True)\n\n# Collect unique y-positions from in-bar label text elements (pygal emits x,y,class order)\nlabel_y_pattern = re.compile(r'<text\\b[^>]*\\by=\"([0-9.]+)\"[^>]*\\bclass=\"label\"')\nunique_y_values = sorted({float(y) for y in label_y_pattern.findall(svg_content)})\n# Row centers from bottom (depth 0) up to deepest leaf (depth num_levels-1)\nrow_centers_bottom_up = list(reversed(unique_y_values))\n\n\ndef _y_to_depth(y_val):\n    return min(range(len(row_centers_bottom_up)), key=lambda d: abs(row_centers_bottom_up[d] - y_val))\n\n\ndef _recolor_label(match):\n    attrs, body = match.group(1), match.group(2)\n    y_match = re.search(r'\\by=\"([0-9.]+)\"', attrs)\n    if not y_match:\n        return match.group(0)\n    depth = _y_to_depth(float(y_match.group(1)))\n    return f'<text{attrs} style=\"fill:{depth_label_inks[depth]}\">{body}'\n\n\nlabel_pattern = re.compile(r'<text\\b([^>]*\\bclass=\"label\"[^>]*)>([^<]*)')\nsvg_content = label_pattern.sub(_recolor_label, svg_content)\n\ncairosvg.svg2png(bytestring=svg_content.encode(\"utf-8\"), write_to=f\"plot-{THEME}.png\")\nhtml_content = f\"\"\"<!DOCTYPE html>\n<html>\n<head>\n    <meta charset=\"utf-8\">\n    <title>flamegraph-basic · pygal · anyplot.ai</title>\n    <style>\n        body {{ margin: 0; display: flex; justify-content: center;\n               align-items: center; min-height: 100vh; background: {PAGE_BG}; }}\n        .chart {{ max-width: 100%; height: auto; margin: 0; }}\n    </style>\n</head>\n<body>\n    <figure class=\"chart\">\n        {svg_content}\n    </figure>\n</body>\n</html>\n\"\"\"\n\nwith open(f\"plot-{THEME}.html\", \"w\", encoding=\"utf-8\") as fout:\n    fout.write(html_content)\n"}