/* Callgraph implementation. Copyright (C) 2011 Free Software Foundation, Inc. Contributed by Sriraman Tallam (tmsriram@google.com). This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING3. If not see . */ #include "callgraph.h" #include #include #include #include #include /*****************************************************************************/ /* section_map hashtable definition and helpers. */ /* Maps section name to its corresponding object handle and section index. */ static htab_t section_map = NULL; /* Hashtable helper for section_map htab. */ static hashval_t section_map_htab_hash_descriptor (const void *p) { const Section_id *s = (const Section_id *)p; const char *name = s->name; return htab_hash_string(name); } /* Hashtable helper for section_map htab. */ static int section_map_htab_eq_descriptor (const void *p1, const void *p2) { const Section_id *s1 = (const Section_id *)p1; const char *c1 = s1->name; const char *c2 = (const char *)p2; return (strcmp (c1, c2) == 0); } /*****************************************************************************/ /*****************************************************************************/ /* function_map hashtable definition and helpers. Maps function name to a unique Node. */ static htab_t function_map = NULL; static unsigned int last_function_id = 0; /* Hashtable helper for function_map htab. */ static hashval_t function_map_htab_hash_descriptor (const void *p) { const Node *s = (const Node *)p; const char *name = s->name; return htab_hash_string(name); } /* Hashtable helper for section_map htab. */ static int function_map_htab_eq_descriptor (const void *p1, const void *p2) { const Node *s1 = (const Node *)p1; const char *c1 = s1->name; const char *c2 = (const char *)p2; return (strcmp (c1, c2) == 0); } /*****************************************************************************/ /*****************************************************************************/ /* edge_map hashtable definition and helpers. Maps two node ids to a unique edge. */ static htab_t edge_map = NULL; static inline hashval_t edge_hash_function (unsigned int id1, unsigned int id2) { return (id1 << 16) | id2; } /* Hashtable helper for edge_map htab. */ static hashval_t edge_map_htab_hash_descriptor (const void *p) { Edge *e = (Edge *) p; return edge_hash_function (e->first_function->id, e->second_function->id); } /* Hashtable helper for edge_map htab. */ static int edge_map_htab_eq_descriptor (const void *p1, const void *p2) { Edge *e1 = (Edge *) p1; Raw_edge *r1 = (Raw_edge *) p2; return ((e1->first_function->id == r1->n1->id) && (e1->second_function->id == r1->n2->id)); } /*****************************************************************************/ /* Chain of all the created nodes. */ Node *node_chain = NULL; /* Number of nodes that correspond to functions which will be reordered. */ unsigned int num_real_nodes = 0; /* Chain of all edges in the merged callgraph. */ Edge *active_edges = NULL; /* Chain of all the merged edges. */ Edge *inactive_edges = NULL; /* Initial value of number of functions to allocate hash tables. */ const int NUM_FUNCTIONS = 100; /* Reads off the next string from the char stream CONTENTS and updates READ_LENGTH to the length of the string read. The value of CONTENTS is updated to start at the next string. */ static char * get_next_string (char **contents, unsigned int *read_length) { char *s = *contents; *read_length = strlen (*contents) + 1; *contents += *read_length; return s; } /* Add an EDGE to the list of edges in the call graph. */ static void add_edge_to_list (Edge *edge) { assert (edge != NULL); edge->next = active_edges; if (active_edges != NULL) active_edges->prev = edge; active_edges = edge; } /* Remove the edge from the list of edges in the call graph. This is done when the nodes corresponding to this edge are merged. */ static void remove_edge_from_list (Edge * curr_edge) { assert (curr_edge != NULL); if (curr_edge->prev != NULL) curr_edge->prev->next = curr_edge->next; if (curr_edge->next != NULL) curr_edge->next->prev = curr_edge->prev; if (active_edges == curr_edge) active_edges = curr_edge->next; curr_edge->next = NULL; curr_edge->prev = NULL; /* Add to inactive edges to be freed later. */ curr_edge->next = inactive_edges; inactive_edges = curr_edge; return; } /* Adds the WEIGHT value to the edge count of CALLER and CALLEE. */ static void update_edge (Node *n1, Node *n2, unsigned int weight) { void **slot; Raw_edge re, *r; Edge *e; if (n1->id == n2->id) return; if (weight == 0) return; if (edge_map == NULL) { edge_map = htab_create ((NUM_FUNCTIONS * 2), edge_map_htab_hash_descriptor, edge_map_htab_eq_descriptor , NULL); assert (edge_map != NULL); } r = &re; init_raw_edge (r, n1, n2); slot = htab_find_slot_with_hash (edge_map, r, edge_hash_function (r->n1->id, r->n2->id), INSERT); if (*slot == NULL) { e = make_edge (r->n1, r->n2, weight); *slot = e; add_edge_to_list (e); } else { e = *slot; e->weight += weight; } } /* Create a unique node for a function. */ static Node * get_function_node (char *name) { void **slot = NULL; Node *node; if (function_map == NULL) { function_map = htab_create (NUM_FUNCTIONS, function_map_htab_hash_descriptor, function_map_htab_eq_descriptor , NULL); assert (function_map != NULL); } slot = htab_find_slot_with_hash (function_map, name, htab_hash_string (name), INSERT); if (*slot == NULL) { node = make_node (last_function_id, name); /* Chain the node to the node_chain. */ node->next = node_chain; node_chain = node; *slot = node; last_function_id++; } else { node = (Node *)*slot; } return node; } /* Dumper funcction to print the list of functions that will be considered for re-ordering. */ void dump_functions () { Node *node = node_chain; while (node) { if (node->is_real_node) fprintf (stderr, "Dumping function %s\n", node->name); node = node->next; } } /* Dump all the edges existing in the callgraph. */ void dump_edges (FILE *fp) { Edge *it; for (it = active_edges; it != NULL; it = it->next) { fprintf (fp,"# %s ---- (%u)---- %s\n", it->first_function->name, it->weight, it->second_function->name); } } /* HEADER_LEN is the length of string 'Function '. */ const int HEADER_LEN = 9; /* Parse the section contents of ".gnu.callgraph.text" sections and create call graph edges with appropriate weights. The section contents have the following format : Function .... */ void parse_callgraph_section_contents (unsigned char *section_contents, unsigned int length) { char *contents; char *caller; unsigned int read_length = 0, curr_length = 0; Node *caller_node; /* First string in contents is 'Function '. */ assert (length > 0); contents = (char*) (section_contents); caller = get_next_string (&contents, &read_length); assert (read_length > HEADER_LEN); caller = caller + HEADER_LEN; curr_length = read_length; caller_node = get_function_node (caller); num_real_nodes++; while (curr_length < length) { /* Read callee, weight tuples. */ char *callee; char *weight_str; unsigned int weight; Node *callee_node; callee = get_next_string (&contents, &read_length); curr_length += read_length; callee_node = get_function_node (callee); assert (curr_length < length); weight_str = get_next_string (&contents, &read_length); weight = atoi (weight_str); curr_length += read_length; update_edge (caller_node, callee_node, weight); } } /* Traverse the list of edges and find the edge with the maximum weight. */ static Edge * find_max_edge () { Edge *it, *max_edge; if (active_edges == NULL) return NULL; max_edge = active_edges; assert (!active_edges->is_merged); it = active_edges->next; for (;it != NULL; it = it->next) { assert (!it->is_merged); if (edge_lower (max_edge , it)) max_edge = it; } return max_edge; } /* Change the EDGE from OLD_NODE to KEPT_NODE to be between NEW_NODE and KEPT_NODE. */ static void merge_edge (Edge *edge, Node *new_node, Node *old_node, Node *kept_node) { void **slot; Raw_edge re, *r; r = &re; init_raw_edge (r, new_node, kept_node); slot = htab_find_slot_with_hash (edge_map, r, edge_hash_function (r->n1->id, r->n2->id), INSERT); if (*slot == NULL) { reset_functions (edge, new_node, kept_node); *slot = edge; add_edge_to_node (new_node, edge); } else { Edge *new_edge = *slot; new_edge->weight += edge->weight; edge->is_merged = 1; remove_edge_from_list (edge); } } /* Merge the two nodes in this EDGE. The new node's edges are the union of the edges of the original nodes. */ static void collapse_edge (Edge * edge) { Edge_list *it; Node *kept_node = edge->first_function; Node *merged_node = edge->second_function; /* Go through all merged_node edges and merge with kept_node. */ for (it = merged_node->edge_list; it != NULL; it = it->next) { Node *other_node = NULL; Edge *this_edge = it->edge; if (this_edge->is_merged) continue; if (this_edge == edge) continue; assert (this_edge->first_function->id == merged_node->id || this_edge->second_function->id == merged_node->id); other_node = (this_edge->first_function->id == merged_node->id) ? this_edge->second_function : this_edge->first_function; merge_edge (this_edge, kept_node, merged_node, other_node); } merge_node (kept_node, merged_node); edge->is_merged = 1; remove_edge_from_list (edge); } /* Make node N a real node if it can be reordered, that is, its .text section is available. */ static void set_node_type (Node *n) { void *slot; char *name = n->name; slot = htab_find_with_hash (section_map, name, htab_hash_string (name)); if (slot != NULL) set_as_real_node (n); } void find_pettis_hansen_function_layout (FILE *fp) { Node *n_it; Edge *it; assert (node_chain != NULL); assert (active_edges != NULL); assert (fp != NULL); dump_edges (fp); /* Go over all the nodes and set it as real node only if a corresponding function section exists. */ for (n_it = node_chain; n_it != NULL; n_it = n_it->next) set_node_type (n_it); /* Set edge types. A WEAK_EDGE has one of its nodes corresponding to a function that cannot be re-ordered. */ for (it = active_edges; it != NULL; it = it->next) set_edge_type (it); it = find_max_edge (); while (it != NULL) { collapse_edge (it); it = find_max_edge (); } } /* Maps the function name corresponding to section SECTION_NAME to the object handle and the section index. */ void map_section_name_to_index (char *section_name, void *handle, int shndx) { void **slot; const char *sections[] = {".text.hot.", ".text.unlikely.", ".text.cold.", ".text.startup.", ".text." }; char *function_name = NULL; int i; for (i = 0; i < ARRAY_SIZE (sections); ++i) { if (is_prefix_of (sections[i], section_name)) { function_name = section_name + strlen (sections[i]); break; } } assert (function_name != NULL); /* Allocate section_map. */ if (section_map == NULL) { section_map = htab_create (NUM_FUNCTIONS, section_map_htab_hash_descriptor, section_map_htab_eq_descriptor , NULL); assert (section_map != NULL); } slot = htab_find_slot_with_hash (section_map, function_name, htab_hash_string (function_name), INSERT); if (*slot == NULL) *slot = make_section_id (function_name, section_name, handle, shndx); } static void write_out_node (FILE *fp, char *name, void **handles, unsigned int *shndx, int position) { void *slot; slot = htab_find_with_hash (section_map, name, htab_hash_string (name)); if (slot != NULL) { Section_id *s = (Section_id *)slot; handles[position] = s->handle; shndx[position] = s->shndx; fprintf (fp, "%s\n", s->full_name); /* No more use of full_name */ free (s->full_name); } } /* Visit each node and print the chain of merged nodes to the file. */ unsigned int get_layout (FILE *fp, void*** handles, unsigned int** shndx) { Node *it; int position = 0; assert (fp != NULL); *handles = XNEWVEC (void *, num_real_nodes); *shndx = XNEWVEC (unsigned int, num_real_nodes); /* Dump edges to the final reordering file. */ for (it = node_chain; it != NULL; it = it->next) { Node *node; if (it->is_merged) continue; if (it->is_real_node) { write_out_node (fp, it->name, *handles, *shndx, position); position++; } node = it->merge_next; while (node != NULL) { if (node->is_real_node) { write_out_node (fp, node->name, *handles, *shndx, position); position++; } node = node->merge_next; } } return position; } /* Free all heap objects. */ void cleanup () { Node *node; /* Free all nodes and edge_lists. */ for (node = node_chain; node != NULL; ) { Node *next_node = node->next; Edge_list *it; for (it = node->edge_list; it != NULL; ) { Edge_list *next_it = it->next; free (it); it = next_it; } free (node); node = next_node; } /* Free all active_edges. */ free_edge_chain (active_edges); /* Free all inactive edges. */ free_edge_chain (inactive_edges); /* Delete all htabs. */ htab_delete (section_map); htab_delete (function_map); htab_delete (edge_map); } /* Check if the callgraph is empty. */ unsigned int is_callgraph_empty () { if (active_edges == NULL) return 1; return 0; }