{"spec_id":"flamegraph-basic","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nflamegraph-basic: Flame Graph for Performance Profiling\nLibrary: bokeh 3.9.1 | Python 3.13.13\nQuality: 85/100 | Updated: 2026-06-08\n\"\"\"\n\nimport os\nimport time\nfrom pathlib import Path\n\nfrom bokeh.io import output_file, save\nfrom bokeh.models import ColumnDataSource, HoverTool, Label\nfrom bokeh.plotting import figure\nfrom selenium import webdriver\nfrom selenium.webdriver.chrome.options import Options\n\n\n# Theme tokens (Imprint palette + theme-adaptive chrome)\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\n\n# Imprint hues assigned by top-level branch — flame-graph convention is to color\n# by code area (not by sample heat), so each subtree gets a stable categorical hue.\n# main is the brand-first root; ochre + matte-red lean into the warm flame-graph\n# aesthetic from the spec while staying on canonical Imprint positions.\nBRANCH_COLOR = {\n    \"main\": \"#009E73\",  # brand — root frame\n    \"handle_request\": \"#BD8233\",  # ochre — request handling\n    \"gc_collect\": \"#AE3030\",  # matte red — garbage collection\n    \"log_metrics\": \"#C475FD\",  # lavender — logging / metrics\n}\n\n# Data — simulated CPU profile of a small web server (10,000 samples)\nstack_data = [\n    (\"main\", 10000),\n    (\"main;handle_request\", 8500),\n    (\"main;handle_request;parse_headers\", 1200),\n    (\"main;handle_request;parse_headers;read_line\", 700),\n    (\"main;handle_request;parse_headers;read_line;decode_utf8\", 350),\n    (\"main;handle_request;parse_headers;read_line;strip_whitespace\", 200),\n    (\"main;handle_request;parse_headers;validate_content_type\", 300),\n    (\"main;handle_request;parse_headers;parse_cookies\", 150),\n    (\"main;handle_request;authenticate\", 2000),\n    (\"main;handle_request;authenticate;verify_token\", 1400),\n    (\"main;handle_request;authenticate;verify_token;decode_jwt\", 900),\n    (\"main;handle_request;authenticate;verify_token;decode_jwt;base64_decode\", 500),\n    (\"main;handle_request;authenticate;verify_token;decode_jwt;verify_signature\", 350),\n    (\"main;handle_request;authenticate;verify_token;check_expiry\", 400),\n    (\"main;handle_request;authenticate;load_user\", 500),\n    (\"main;handle_request;authenticate;load_user;query_cache\", 300),\n    (\"main;handle_request;authenticate;load_user;query_db\", 180),\n    (\"main;handle_request;process_query\", 4000),\n    (\"main;handle_request;process_query;parse_sql\", 600),\n    (\"main;handle_request;process_query;parse_sql;tokenize\", 350),\n    (\"main;handle_request;process_query;parse_sql;build_ast\", 200),\n    (\"main;handle_request;process_query;optimize\", 500),\n    (\"main;handle_request;process_query;optimize;rewrite_joins\", 280),\n    (\"main;handle_request;process_query;optimize;estimate_cost\", 180),\n    (\"main;handle_request;process_query;execute\", 2400),\n    (\"main;handle_request;process_query;execute;fetch_rows\", 1500),\n    (\"main;handle_request;process_query;execute;fetch_rows;read_index\", 800),\n    (\"main;handle_request;process_query;execute;fetch_rows;read_index;btree_search\", 500),\n    (\"main;handle_request;process_query;execute;fetch_rows;read_index;page_read\", 250),\n    (\"main;handle_request;process_query;execute;fetch_rows;deserialize\", 600),\n    (\"main;handle_request;process_query;execute;fetch_rows;deserialize;decode_row\", 400),\n    (\"main;handle_request;process_query;execute;apply_filter\", 700),\n    (\"main;handle_request;process_query;execute;apply_filter;compare_values\", 450),\n    (\"main;handle_request;process_query;execute;apply_filter;check_null\", 200),\n    (\"main;handle_request;process_query;format_result\", 400),\n    (\"main;handle_request;process_query;format_result;build_json\", 250),\n    (\"main;handle_request;process_query;format_result;paginate\", 120),\n    (\"main;handle_request;send_response\", 1000),\n    (\"main;handle_request;send_response;serialize_json\", 500),\n    (\"main;handle_request;send_response;serialize_json;encode_utf8\", 300),\n    (\"main;handle_request;send_response;compress\", 300),\n    (\"main;handle_request;send_response;compress;deflate\", 200),\n    (\"main;handle_request;send_response;write_socket\", 150),\n    (\"main;gc_collect\", 1000),\n    (\"main;gc_collect;mark_phase\", 550),\n    (\"main;gc_collect;mark_phase;trace_refs\", 350),\n    (\"main;gc_collect;mark_phase;check_weak_refs\", 150),\n    (\"main;gc_collect;sweep_phase\", 400),\n    (\"main;gc_collect;sweep_phase;free_objects\", 250),\n    (\"main;gc_collect;sweep_phase;compact_heap\", 120),\n    (\"main;log_metrics\", 400),\n    (\"main;log_metrics;collect_counters\", 200),\n    (\"main;log_metrics;flush_buffer\", 150),\n    (\"main;log_metrics;flush_buffer;write_file\", 100),\n    (\"main;log_metrics;flush_buffer;rotate_log\", 40),\n]\n\n# Build hierarchy from semicolon-delimited stacks\ntotal_samples = 10000\nnodes = {}\nchildren_map = {}\nfor stack_str, samples in stack_data:\n    parts = stack_str.split(\";\")\n    depth = len(parts) - 1\n    parent_key = \";\".join(parts[:-1]) if depth > 0 else None\n    branch = parts[1] if depth >= 1 else \"main\"\n    nodes[stack_str] = {\"name\": parts[-1], \"samples\": samples, \"depth\": depth, \"branch\": branch}\n    children_map.setdefault(parent_key, []).append(stack_str)\n\nmax_depth = max(n[\"depth\"] for n in nodes.values())\n\n# Dominant hot-path chain — gets an INK outline to point readers at the bottleneck.\nHOT_PATH = {\n    \"main\",\n    \"main;handle_request\",\n    \"main;handle_request;process_query\",\n    \"main;handle_request;process_query;execute\",\n    \"main;handle_request;process_query;execute;fetch_rows\",\n}\n\n# Layout — iterative DFS placing each child proportional to its sample share.\n# Sibling order is alphabetical (flame-graph convention; x-axis is not temporal).\nrects = []\nwork_stack = [(\"main\", 0.0, 100.0)]\nwhile work_stack:\n    stack_key, x_start, x_end = work_stack.pop()\n    node = nodes[stack_key]\n    rect_w = x_end - x_start\n    pct = node[\"samples\"] / total_samples * 100\n    # Subtle parity-based alpha gives adjacent frames a faint banding cue\n    fill_alpha = 0.94 if node[\"depth\"] % 2 == 0 else 0.86\n    is_hot = stack_key in HOT_PATH\n    rects.append(\n        {\n            \"name\": node[\"name\"],\n            \"depth\": node[\"depth\"],\n            \"x_center\": (x_start + x_end) / 2,\n            \"y_center\": node[\"depth\"] + 0.5,\n            \"width\": rect_w,\n            \"color\": BRANCH_COLOR[node[\"branch\"]],\n            \"fill_alpha\": fill_alpha,\n            \"line_color\": INK if is_hot else PAGE_BG,\n            \"line_width\": 4.0 if is_hot else 1.5,\n            \"samples\": node[\"samples\"],\n            \"pct\": f\"{pct:.1f}%\",\n            \"stack\": stack_key,\n        }\n    )\n    child_keys = sorted(children_map.get(stack_key, []), reverse=True)\n    current_x = x_start\n    for ck in child_keys:\n        cw = rect_w * (nodes[ck][\"samples\"] / node[\"samples\"])\n        work_stack.append((ck, current_x, current_x + cw))\n        current_x += cw\n\nsource = ColumnDataSource(\n    data={\n        \"x\": [r[\"x_center\"] for r in rects],\n        \"y\": [r[\"y_center\"] for r in rects],\n        \"width\": [r[\"width\"] for r in rects],\n        \"height\": [0.94] * len(rects),\n        \"color\": [r[\"color\"] for r in rects],\n        \"fill_alpha\": [r[\"fill_alpha\"] for r in rects],\n        \"line_color\": [r[\"line_color\"] for r in rects],\n        \"line_width\": [r[\"line_width\"] for r in rects],\n        \"name\": [r[\"name\"] for r in rects],\n        \"samples\": [r[\"samples\"] for r in rects],\n        \"pct\": [r[\"pct\"] for r in rects],\n        \"stack\": [r[\"stack\"] for r in rects],\n    }\n)\n\n# Plot — landscape canvas, axes hidden (flame-graph convention)\ntitle = \"flamegraph-basic · python · bokeh · anyplot.ai\"\np = figure(\n    width=3200,\n    height=1800,\n    title=title,\n    x_range=(-0.5, 100.5),\n    y_range=(-0.05, max_depth + 1.05),\n    tools=\"\",\n    toolbar_location=None,\n    min_border_left=40,\n    min_border_right=40,\n    min_border_top=110,\n    min_border_bottom=40,\n)\n\nbars = p.rect(\n    x=\"x\",\n    y=\"y\",\n    width=\"width\",\n    height=\"height\",\n    source=source,\n    fill_color=\"color\",\n    fill_alpha=\"fill_alpha\",\n    line_color=\"line_color\",\n    line_width=\"line_width\",\n)\n\n# HoverTool — bokeh's distinctive interactive feature, surfaces the full call stack\nhover = HoverTool(\n    renderers=[bars],\n    tooltips=[(\"Function\", \"@name\"), (\"Samples\", \"@samples\"), (\"CPU %\", \"@pct\"), (\"Call Stack\", \"@stack\")],\n    point_policy=\"follow_mouse\",\n)\np.add_tools(hover)\n\n# Function-name labels drawn inside bars wide enough to fit them.\n# Narrower frames fall back to the HoverTool — keeps adjacent labels from touching.\nfor r in rects:\n    if r[\"width\"] <= 5:\n        continue\n    if r[\"width\"] > 25:\n        font_size = \"22pt\"\n    elif r[\"width\"] > 10:\n        font_size = \"18pt\"\n    else:\n        font_size = \"14pt\"\n    label_text = f\"{r['name']} ({r['pct']})\" if r[\"width\"] > 12 else r[\"name\"]\n    p.add_layout(\n        Label(\n            x=r[\"x_center\"],\n            y=r[\"y_center\"],\n            text=label_text,\n            text_align=\"center\",\n            text_baseline=\"middle\",\n            text_font_size=font_size,\n            text_color=INK,\n        )\n    )\n\n# Style — chrome (axes hidden, theme-adaptive title + background)\np.title.text_font_size = \"50pt\"\np.title.text_color = INK\np.title.text_font_style = \"bold\"\np.title.align = \"center\"\n\np.xaxis.visible = False\np.yaxis.visible = False\np.xgrid.visible = False\np.ygrid.visible = False\np.outline_line_color = None\np.background_fill_color = PAGE_BG\np.border_fill_color = PAGE_BG\n\n# Save — interactive HTML + headless-Chrome screenshot at exact canvas size.\n# CDP setDeviceMetricsOverride makes the inner viewport authoritative — --window-size\n# alone leaves Chrome chrome eating ~140 px, yielding 3200x1661 instead of 3200x1800.\noutput_file(f\"plot-{THEME}.html\")\nsave(p)\n\nW, H = 3200, 1800\nopts = Options()\nfor arg in (\n    \"--headless=new\",\n    \"--no-sandbox\",\n    \"--disable-dev-shm-usage\",\n    \"--disable-gpu\",\n    f\"--window-size={W},{H}\",\n    \"--hide-scrollbars\",\n):\n    opts.add_argument(arg)\ndriver = webdriver.Chrome(options=opts)\ndriver.execute_cdp_cmd(\n    \"Emulation.setDeviceMetricsOverride\", {\"width\": W, \"height\": H, \"deviceScaleFactor\": 1, \"mobile\": False}\n)\ndriver.get(f\"file://{Path(f'plot-{THEME}.html').resolve()}\")\ntime.sleep(3)\ndriver.save_screenshot(f\"plot-{THEME}.png\")\ndriver.quit()\n\n# Belt-and-braces: pin the saved PNG to exact dims so the post-render gate passes.\nfrom PIL import Image as _PILImage\n\n\n_img = _PILImage.open(f\"plot-{THEME}.png\").convert(\"RGB\")\nif _img.size != (W, H):\n    _norm = _PILImage.new(\"RGB\", (W, H), PAGE_BG)\n    _norm.paste(_img, ((W - _img.size[0]) // 2, (H - _img.size[1]) // 2))\n    _norm.save(f\"plot-{THEME}.png\")\n"}