From 67209246da5ba912e96b1c0319d93b392133874a Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 9 Jan 2026 15:25:24 +0000 Subject: [PATCH] Add colours and some more edges to executor visualization graph --- Python/optimizer.c | 149 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 130 insertions(+), 19 deletions(-) diff --git a/Python/optimizer.c b/Python/optimizer.c index 73617f6ca26425..c4ace2571eb1ff 100644 --- a/Python/optimizer.c +++ b/Python/optimizer.c @@ -1888,6 +1888,24 @@ _Py_Executors_InvalidateCold(PyInterpreterState *interp) _Py_Executors_InvalidateAll(interp, 0); } +static int +escape_angles(const char *input, Py_ssize_t size, char *buffer) { + int written = 0; + for (Py_ssize_t i = 0; i < size; i++) { + char c = input[i]; + if (c == '<' || c == '>') { + buffer[written++] = '&'; + buffer[written++] = c == '>' ? 'g' : 'l'; + buffer[written++] = 't'; + buffer[written++] = ';'; + } + else { + buffer[written++] = c; + } + } + return written; +} + static void write_str(PyObject *str, FILE *out) { @@ -1899,7 +1917,17 @@ write_str(PyObject *str, FILE *out) } const char *encoded_str = PyBytes_AsString(encoded_obj); Py_ssize_t encoded_size = PyBytes_Size(encoded_obj); - fwrite(encoded_str, 1, encoded_size, out); + char buffer[120]; + bool truncated = false; + if (encoded_size> 24) { + encoded_size = 24; + truncated = true; + } + int size = escape_angles(encoded_str, encoded_size, buffer); + fwrite(buffer, 1, size, out); + if (truncated) { + fwrite("...", 1, 3, out); + } Py_DECREF(encoded_obj); } @@ -1921,6 +1949,85 @@ find_line_number(PyCodeObject *code, _PyExecutorObject *executor) return -1; } +#define RED "#ff0000" +#define WHITE "#ffffff" +#define BLUE "#0000ff" +#define BLACK "#000000" +#define LOOP "#00c000" + +const char *COLORS[10] = { + "9", + "8", + "7", + "6", + "5", + "4", + "3", + "2", + "1", + WHITE, +}; + +#ifdef Py_STATS +const char * +get_background_color(_PyUOpInstruction const *inst, uint64_t max_hotness) +{ + uint64_t hotness = inst->execution_count; + int index = (hotness * 10)/max_hotness; + if (index> 9) { + index = 9; + } + if (index < 0) { + index = 0; + } + return COLORS[index]; +} + +const char * +get_foreground_color(_PyUOpInstruction const *inst, uint64_t max_hotness) +{ + if(_PyUop_Uncached[inst->opcode] == _DEOPT) { + return RED; + } + uint64_t hotness = inst->execution_count; + int index = (hotness * 10)/max_hotness; + if (index> 3) { + return BLACK; + } + return WHITE; +} +#endif + +static void +write_row_for_uop(_PyExecutorObject *executor, uint32_t i, FILE *out) +{ + /* Write row for uop. + * The `port` is a marker so that outgoing edges can + * be placed correctly. If a row is marked `port=17`, + * then the outgoing edge is `{EXEC_NAME}:17 -> {TARGET}` + * https://graphviz.readthedocs.io/en/stable/manual.html#node-ports-compass + */ + _PyUOpInstruction const *inst = &executor->trace[i]; + const char *opname = _PyOpcode_uop_name[inst->opcode]; +#ifdef Py_STATS + const char *bg_color = get_background_color(inst, executor->trace[0].execution_count); + const char *color = get_foreground_color(inst, executor->trace[0].execution_count); + fprintf(out, "
%s -- %" PRIu64 "
\n", + i, color, bg_color, color, opname, inst->execution_count); +#else + const char *color = (_PyUop_Uncached[inst->opcode] == _DEOPT) ? RED : BLACK; + fprintf(out, "
%s op0=%" PRIu64 "
\n", i, color, opname, inst->operand0); +#endif +} + +static bool +is_stop(_PyUOpInstruction const *inst) +{ + uint16_t base_opcode = _PyUop_Uncached[inst->opcode]; + return (base_opcode == _EXIT_TRACE || base_opcode == _DEOPT || base_opcode == _JUMP_TO_TOP); +} + + /* Writes the node and outgoing edges for a single tracelet in graphviz format. * Each tracelet is presented as a table of the uops it contains. * If Py_STATS is enabled, execution counts are included. @@ -1948,21 +2055,8 @@ executor_to_gv(_PyExecutorObject *executor, FILE *out) fprintf(out, ": %d\n", line); } for (uint32_t i = 0; i < executor->code_size; i++) { - /* Write row for uop. - * The `port` is a marker so that outgoing edges can - * be placed correctly. If a row is marked `port=17`, - * then the outgoing edge is `{EXEC_NAME}:17 -> {TARGET}` - * https://graphviz.readthedocs.io/en/stable/manual.html#node-ports-compass - */ - _PyUOpInstruction const *inst = &executor->trace[i]; - uint16_t base_opcode = _PyUop_Uncached[inst->opcode]; - const char *opname = _PyOpcode_uop_name[base_opcode]; -#ifdef Py_STATS - fprintf(out, "
%s -- %" PRIu64 "
\n", i, opname, inst->execution_count); -#else - fprintf(out, "
%s op0=%" PRIu64 "
\n", i, opname, inst->operand0); -#endif - if (base_opcode == _EXIT_TRACE || base_opcode == _JUMP_TO_TOP) { + write_row_for_uop(executor, i, out); + if (is_stop(&executor->trace[i])) { break; } } @@ -1977,6 +2071,10 @@ executor_to_gv(_PyExecutorObject *executor, FILE *out) uint16_t base_opcode = _PyUop_Uncached[inst->opcode]; uint16_t flags = _PyUop_Flags[base_opcode]; _PyExitData *exit = NULL; + if (base_opcode == _JUMP_TO_TOP) { + fprintf(out, "executor_%p:i%d -> executor_%p:i%d [color = \"" LOOP "\"]\n", executor, i, executor, inst->jump_target); + break; + } if (base_opcode == _EXIT_TRACE) { exit = (_PyExitData *)inst->operand0; } @@ -1987,10 +2085,22 @@ executor_to_gv(_PyExecutorObject *executor, FILE *out) assert(base_exit_opcode == _EXIT_TRACE || base_exit_opcode == _DYNAMIC_EXIT); exit = (_PyExitData *)exit_inst->operand0; } - if (exit != NULL && exit->executor != cold && exit->executor != cold_dynamic) { - fprintf(out, "executor_%p:i%d -> executor_%p:start\n", executor, i, exit->executor); + if (exit != NULL) { + if (exit->executor == cold || exit->executor == cold_dynamic) { +#ifdef Py_STATS + /* Only mark as have cold exit if it has actually exited */ + uint64_t diff = inst->execution_count - executor->trace[i+1].execution_count; + if (diff) { + fprintf(out, "cold_%p%d [ label = \"%" PRIu64 "\" shape = ellipse color=\"" BLUE "\" ]\n", executor, i, diff); + fprintf(out, "executor_%p:i%d -> cold_%p%d\n", executor, i, executor, i); + } +#endif + } + else { + fprintf(out, "executor_%p:i%d -> executor_%p:start\n", executor, i, exit->executor); + } } - if (base_opcode == _EXIT_TRACE || base_opcode == _JUMP_TO_TOP) { + if (is_stop(inst)) { break; } } @@ -2002,6 +2112,7 @@ _PyDumpExecutors(FILE *out) { fprintf(out, "digraph ideal {\n\n"); fprintf(out, " rankdir = \"LR\"\n\n"); + fprintf(out, " node [colorscheme=greys9]\n"); PyInterpreterState *interp = PyInterpreterState_Get(); for (_PyExecutorObject *exec = interp->executor_list_head; exec != NULL;) { executor_to_gv(exec, out);

AltStyle によって変換されたページ (->オリジナル) /