static void remove_duplicate_nodes(struct call_graph *graph) { int i = 0; int j = 0; qsort(graph->node_list.ptr, graph->node_list.size, graph->node_list.elemsize, cmp_node); while (i < graph->node_list.size) { struct cg_node *n = CG_NODE(graph, i); struct cg_node *l = CG_NODE(graph, j - 1); if (!j || n->offset != l->offset) { if (i != j) memcpy(l + 1, n, sizeof(*l)); j++; } i++; } graph->node_list.size = j; }
static void relabel_sources(struct call_graph *graph) { int i = 0; /* Node index */ int j = 0; /* Edge index */ /* Identify the source nodes for each edge */ qsort(graph->edge_from.ptr, graph->edge_from.size, graph->edge_from.elemsize, cmp_branch_by_src); while (j < graph->edge_from.size) { struct cg_edge *br = CG_EDGE_FROM(graph, j); struct cg_node *n; /* Skip over nodes which are too early for this edge */ while (i + 1 < graph->node_list.size && CG_NODE(graph, i + 1)->offset <= br->src) i++; n = CG_NODE(graph, i); if (n->offset <= br->src) br->src = n->offset; j++; } }
static void cgraph_summary(struct call_graph *graph) { int i; int j = 0; /* Edge from index */ int k = 0; /* Edge to index */ for (i = 0; i < graph->node_list.size; i++) { struct cg_node *n = CG_NODE(graph, i); int from_count = 0; int to_count = 0; char name[64]; while (j < graph->edge_from.size && CG_EDGE_FROM(graph, j)->src < n->offset) j++; while (k < graph->edge_to.size && CG_EDGE_TO(graph, k)->dst < n->offset) k++; while (j < graph->edge_from.size && CG_EDGE_FROM(graph, j)->src == n->offset) { from_count++; j++; } while (k < graph->edge_to.size && CG_EDGE_TO(graph, k)->dst == n->offset) { to_count++; k++; } print_address(n->offset, name, sizeof(name), 0); printc("0x%04x [%3d ==> %3d] %s\n", n->offset, to_count, from_count, name); } }
static bool dump_layer_cb(cg_node_t *node, void *user_data) { cg_pipeline_layer_t *layer = CG_PIPELINE_LAYER(node); print_debug_state_t *state = user_data; int layer_id = *state->node_id_ptr; print_debug_state_t state_out; c_string_t *changes_label; bool changes = false; if (state->parent_id >= 0) c_string_append_printf(state->graph, "%*slayer%p -> layer%p;\n", state->indent, "", layer->_parent.parent, layer); c_string_append_printf(state->graph, "%*slayer%p [label=\"layer=0x%p\\n" "ref count=%d\" " "color=\"blue\"];\n", state->indent, "", layer, layer, CG_OBJECT(layer)->ref_count); changes_label = c_string_new(""); c_string_append_printf(changes_label, "%*slayer%p -> layer_state%d [weight=100];\n" "%*slayer_state%d [shape=box label=\"", state->indent, "", layer, layer_id, state->indent, "", layer_id); if (layer->differences & CG_PIPELINE_LAYER_STATE_UNIT) { changes = true; c_string_append_printf( changes_label, "\\lunit=%u\\n", layer->unit_index); } if (layer->differences & CG_PIPELINE_LAYER_STATE_TEXTURE_DATA) { changes = true; c_string_append_printf( changes_label, "\\ltexture=%p\\n", layer->texture); } if (changes) { c_string_append_printf(changes_label, "\"];\n"); c_string_append(state->graph, changes_label->str); c_string_free(changes_label, true); } state_out.parent_id = layer_id; state_out.node_id_ptr = state->node_id_ptr; (*state_out.node_id_ptr)++; state_out.graph = state->graph; state_out.indent = state->indent + 2; _cg_pipeline_node_foreach_child(CG_NODE(layer), dump_layer_cb, &state_out); return true; }
static bool dump_pipeline_cb(cg_node_t *node, void *user_data) { cg_pipeline_t *pipeline = CG_PIPELINE(node); print_debug_state_t *state = user_data; int pipeline_id = *state->node_id_ptr; print_debug_state_t state_out; c_string_t *changes_label; bool changes = false; bool layers = false; if (state->parent_id >= 0) c_string_append_printf(state->graph, "%*spipeline%d -> pipeline%d;\n", state->indent, "", state->parent_id, pipeline_id); c_string_append_printf(state->graph, "%*spipeline%d [label=\"pipeline=0x%p\\n" "ref count=%d\\n" "breadcrumb=\\\"%s\\\"\" color=\"red\"];\n", state->indent, "", pipeline_id, pipeline, CG_OBJECT(pipeline)->ref_count, pipeline->has_static_breadcrumb ? #ifdef CG_DEBUG_ENABLED pipeline->static_breadcrumb : "NULL" #else "NULL" #endif ); changes_label = c_string_new(""); c_string_append_printf(changes_label, "%*spipeline%d -> pipeline_state%d [weight=100];\n" "%*spipeline_state%d [shape=box label=\"", state->indent, "", pipeline_id, pipeline_id, state->indent, "", pipeline_id); if (pipeline->differences & CG_PIPELINE_STATE_COLOR) { changes = true; c_string_append_printf(changes_label, "\\lcolor=0x%02X%02X%02X%02X\\n", cg_color_get_red_byte(&pipeline->color), cg_color_get_green_byte(&pipeline->color), cg_color_get_blue_byte(&pipeline->color), cg_color_get_alpha_byte(&pipeline->color)); } if (pipeline->differences & CG_PIPELINE_STATE_BLEND) { const char *blend_enable_name; changes = true; switch (pipeline->blend_enable) { case CG_PIPELINE_BLEND_ENABLE_AUTOMATIC: blend_enable_name = "AUTO"; break; case CG_PIPELINE_BLEND_ENABLE_ENABLED: blend_enable_name = "ENABLED"; break; case CG_PIPELINE_BLEND_ENABLE_DISABLED: blend_enable_name = "DISABLED"; break; default: blend_enable_name = "UNKNOWN"; } c_string_append_printf( changes_label, "\\lblend=%s\\n", blend_enable_name); } if (pipeline->differences & CG_PIPELINE_STATE_LAYERS) { changes = true; layers = true; c_string_append_printf( changes_label, "\\ln_layers=%d\\n", pipeline->n_layers); } if (changes) { c_string_append_printf(changes_label, "\"];\n"); c_string_append(state->graph, changes_label->str); c_string_free(changes_label, true); } if (layers) { c_llist_foreach(pipeline->layer_differences, (c_iter_func_t)dump_layer_ref_cb, state); } state_out.parent_id = pipeline_id; state_out.node_id_ptr = state->node_id_ptr; (*state_out.node_id_ptr)++; state_out.graph = state->graph; state_out.indent = state->indent + 2; _cg_pipeline_node_foreach_child( CG_NODE(pipeline), dump_pipeline_cb, &state_out); return true; }
static void cgraph_func_info(struct call_graph *graph, address_t addr) { int i = 0; int j = 0; int k = 0; char name[64]; struct cg_node *n; while (i + 1 < graph->node_list.size && CG_NODE(graph, i + 1)->offset <= addr) i++; if (i >= graph->node_list.size || CG_NODE(graph, i)->offset > addr) { printc("No information for address 0x%04x\n", addr); return; } n = CG_NODE(graph, i); while (j < graph->edge_from.size && CG_EDGE_FROM(graph, j)->src < n->offset) j++; while (k < graph->edge_to.size && CG_EDGE_TO(graph, k)->dst < n->offset) k++; print_address(n->offset, name, sizeof(name), 0); printc("0x%04x %s:\n", n->offset, name); if (j < graph->edge_from.size && CG_EDGE_FROM(graph, j)->src == n->offset) { printc(" Callees:\n"); while (j < graph->edge_from.size) { struct cg_edge *e = CG_EDGE_FROM(graph, j); if (e->src != n->offset) break; print_address(e->dst, name, sizeof(name), 0); printc(" %s%s\n", e->is_tail_call ? "*" : "", name); j++; } printc("\n"); } if (k < graph->edge_to.size && CG_EDGE_TO(graph, k)->dst == n->offset) { printc(" Callers:\n"); while (k < graph->edge_to.size) { struct cg_edge *e = CG_EDGE_TO(graph, k); if (e->dst != n->offset) break; print_address(e->src, name, sizeof(name), 0); printc(" %s%s\n", e->is_tail_call ? "*" : "", name); k++; } } }