/* Callgraph implementation. Copyright (C) 2011 Free Software Foundation, Inc. Contributed by Sriraman Tallam (tmsriram@google.com) and Easwaran Raman (eraman@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)); } /*****************************************************************************/ /* Keep track of all allocated memory. */ typedef struct { void *ptr; void *next; } mm_node; mm_node *mm_node_chain = NULL; void push_allocated_ptr (void *ptr) { mm_node *node = XNEW (mm_node); node->ptr = ptr; node->next = mm_node_chain; mm_node_chain = node; } /* 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. UPDATE_CONTENTS tells if CONTENTS must be moved past the read string to the next string. To peek at the string, UPDATE_CONTENTS can be set to false. */ static char * get_next_string (char **contents, unsigned int *read_length, int update_contents) { char *s = *contents; *read_length = strlen (*contents) + 1; if (update_contents) *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 long long 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; } /* Update the computed node weight for n2, which is the sum of its incoming edge weights. */ n2->computed_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 (%llu, %llu) ---- (%llu)---- %s (%llu, %llu)\n", it->first_function->name, it->first_function->weight, it->first_function->computed_weight, it->weight, it->second_function->name, it->second_function->weight, it->second_function->computed_weight); } } /* For file local functions, append a unique identifier corresponding to the file, FILE_HANDLE, to the NAME to keep the name unique. */ static char * canonicalize_function_name (void *file_handle, char *name) { /* Number of hexadecimal digits in file_handle, plus length of "0x". */ const int FILE_HANDLE_LEN = sizeof (void *) * 2 + 2; char *canonical_name; /* File local functions have _ZL prefix in the mangled name. */ /* XXX: Handle file local functions exhaustively, like functions in anonymous name spaces. */ if (!is_prefix_of ("_ZL", name)) return name; XNEWVEC_ALLOC (canonical_name, char, (strlen(name) + FILE_HANDLE_LEN + 2)); sprintf (canonical_name, "%s.%p", name, file_handle); return canonical_name; } /* 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 Weight (optional line) ColdWeight (optional line) .... */ void parse_callgraph_section_contents (void *file_handle, unsigned char *section_contents, unsigned int length) { char *contents; char *caller; char *node_weight_s = NULL; unsigned int read_length = 0, curr_length = 0; Node *caller_node; /* HEADER_LEN is the length of string 'Function '. */ const int HEADER_LEN = 9; /* Prefix of line containing node weights. */ const char *NODE_WEIGHT_PREFIX = "Weight "; /* Prefix of line containing max bb count of cold split part. */ const char *SPLIT_FUNCTION_PREFIX = "ColdWeight "; /* First string in contents is 'Function '. */ assert (length > 0); contents = (char*) (section_contents); caller = get_next_string (&contents, &read_length, 1); assert (read_length > HEADER_LEN); caller = canonicalize_function_name (file_handle, caller + HEADER_LEN); curr_length = read_length; caller_node = get_function_node (caller); /* Check if next string is a node weight, which has the format "Weight ". We could have callgraph sections with or without node weights. */ /* Peek at the next string. */ if (curr_length < length) node_weight_s = get_next_string (&contents, &read_length, 0); if (node_weight_s != NULL && is_prefix_of (NODE_WEIGHT_PREFIX, node_weight_s)) { char *max_count_s; unsigned long long max_count; unsigned long long node_weight = atoll (node_weight_s + strlen (NODE_WEIGHT_PREFIX)); /* Functions like comdats only have one caller_node and can have multiple node weights from multiple modules. */ caller_node->weight += node_weight; /* Find the space and get the max_count. */ max_count_s = strstr (node_weight_s + strlen (NODE_WEIGHT_PREFIX), " "); if (max_count_s != NULL) { max_count = atoll (max_count_s + 1); /* Functions like comdats only have one caller_node and can have multiple node weights from multiple modules. */ caller_node->max_count += max_count; } /* Actually read the node weight here. */ get_next_string (&contents, &read_length, 1); curr_length += read_length; } /* If the function is split it could have the weight of the split cold section here as "SplitWeight ". */ /* Peek at the next string. */ if (curr_length < length) node_weight_s = get_next_string (&contents, &read_length, 0); if (node_weight_s != NULL && is_prefix_of (SPLIT_FUNCTION_PREFIX, node_weight_s)) { unsigned long long split_weight = atoll (node_weight_s + strlen (SPLIT_FUNCTION_PREFIX)); caller_node->split_weight = split_weight; /* Actually read the node weight here. */ get_next_string (&contents, &read_length, 1); curr_length += read_length; } while (curr_length < length) { /* Read callee, weight tuples. */ char *callee; char *weight_str; unsigned long long weight; Node *callee_node; callee = get_next_string (&contents, &read_length, 1); curr_length += read_length; /* We can have multiple header lines; such a situation arises when we've linked objects into a shared library, and we use that library as input to the linker for something else. Deal gracefully with such cases. */ if (strncmp (callee, "Function ", HEADER_LEN) == 0) continue; callee = canonicalize_function_name (file_handle, callee); callee_node = get_function_node (callee); assert (curr_length < length); weight_str = get_next_string (&contents, &read_length, 1); weight = atoll (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) { /* Update the section instance corresponding to the node instance. Assign the weights from the node instance to the section instance. */ Section_id *s = (Section_id *)(slot); Section_id *s_comdat; assert (s->weight == 0 && s->computed_weight == 0 && s->max_count == 0); s->weight = n->weight; s->computed_weight = n->computed_weight; s->max_count = n->max_count; /* If s is split into a cold section, assign the split weight to the max count of the split section. Use this also for the weight of the split section. */ if (s->split_section) { s->split_section->max_count = s->split_section->weight = n->split_weight; /* If split_section is comdat, update all the comdat candidates for weight. */ s_comdat = s->split_section->comdat_group; while (s_comdat != NULL) { /* Set the different weights for comdat candidates. No need to se computed_weight as it is zero for split sections. A split cold section is never called, it is only jumped into from the parent section. */ s_comdat->weight = s->split_section->weight; s_comdat->max_count = s->split_section->max_count; s_comdat = s_comdat->comdat_group; } } /* If s is comdat, update all the comdat candidates for weight. */ s_comdat = s->comdat_group; while (s_comdat != NULL) { s_comdat->weight = s->weight; s_comdat->computed_weight = s->computed_weight; s_comdat->max_count = s->max_count; s_comdat = s_comdat->comdat_group; } set_as_real_node (n); num_real_nodes++; } } /* Return true if WEIGHT is more than the cutoff, specified either as as percent, CUTOFF_P, of MAX or as an absolute value, CUTOFF_A. */ int edge_over_cutoff (unsigned long long weight, unsigned long long max, unsigned int cutoff_p, unsigned long long cutoff_a) { /* First check if weight if more than cutoff_p% of max. */ if (((double)(max) * (cutoff_p/100.0)) >= (double) weight) return 0; if (cutoff_a >= weight) return 0; return 1; } /* Edge cutoff is used to discard callgraph edges that are not above a certain threshold. cutoff_p is to express this as a percent of the maximum value and cutoff_a is used to express this as an absolute value. */ extern unsigned int edge_cutoff_p; extern unsigned long long edge_cutoff_a; void find_pettis_hansen_function_layout (FILE *fp) { Node *n_it; Edge *it; unsigned int max_edge_value = 0; assert (node_chain != NULL); assert (active_edges != NULL); if (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 (); if (it != NULL) max_edge_value = it->weight; while (it != NULL) { if (!edge_over_cutoff (it->weight, max_edge_value, edge_cutoff_p, edge_cutoff_a)) { if (fp !=NULL) fprintf (fp, "Not considering edge with weight %llu and below\n", it->weight); break; } collapse_edge (it); it = find_max_edge (); } } /* The list of sections created, excluding comdat duplicates. */ Section_id *first_section = NULL; /* The number of sections. */ int num_sections = 0; const int NUM_SECTION_TYPES = 4; const char *section_types[] = {".text.hot.", ".text.unlikely.", ".text.startup.", ".text." }; /* For sections that are not in the callgraph, the priority gives the importance of each section type. Sections are grouped according to priority, higher priority (lower number). */ const int section_priority[] = {0, 3, 1, 2}; /* Order in which the sections must be laid out is given by section_position[section_type]. The order in which the section types are laid out from address low to high are: .text.unlikely, .text.startup, .text., .text.hot followed by the sections grouped by the callgraph. */ const int section_position[] = {3, 0, 1, 2}; /* The position of the sections grouped using the callgraph. It comes after all the sections not present in the callgraph are laid out. */ #define CALLGRAPH_POSITION NUM_SECTION_TYPES /* 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; char *function_name = NULL; int i, section_type = -1; for (i = 0; i < ARRAY_SIZE (section_types); ++i) { if (is_prefix_of (section_types[i], section_name)) { function_name = section_name + strlen (section_types[i]); section_type = i; break; } } assert (function_name != NULL && section_type >= 0); function_name = canonicalize_function_name (handle, function_name); num_sections++; /* 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) { Section_id *section = make_section_id (function_name, section_name, section_type, handle, shndx); /* Chain it to the list of sections. */ section->next = first_section; first_section = section; *slot = section; } else { /* Handle function splitting here. With function splitting, the split function sections have the same name and they are in the same module. Here, we only care about the section that is marked with prefix like ".text.hot". The other section is cold. The plugin should not be adding this cold section to the section_map. In get_layout it will later be picked up when processing the non-callgraph sections and it will laid out appropriately. */ Section_id *kept = (Section_id *)(*slot); Section_id *section = make_section_id (function_name, section_name, section_type, handle, shndx); int is_split_function = 0; Section_id *split_comdat = NULL; /* Check if this is a split function. The modules are the same means this is not comdat and we assume it is split. It can be split and comdat too, in which case we have to search the comdat list of kept. */ if (kept->handle == handle) is_split_function = 1; else if (kept->comdat_group != NULL) { split_comdat = kept; do { if (split_comdat->comdat_group->handle == handle) break; split_comdat = split_comdat->comdat_group; } while (split_comdat->comdat_group != NULL); } /* It is split and it is comdat. */ if (split_comdat != NULL && split_comdat->comdat_group != NULL) { /* The comdat_section that is split. */ Section_id *comdat_section = split_comdat->comdat_group; Section_id *cold_section = NULL; /* If the existing section is cold, the newly detected split must be hot. */ if (is_prefix_of (".text.unlikely", comdat_section->full_name)) { assert (!is_prefix_of (".text.unlikely", section_name)); cold_section = comdat_section; /* Replace the comdat_section in the kept section list with the new section. */ split_comdat->comdat_group = section; section->comdat_group = comdat_section->comdat_group; comdat_section->comdat_group = NULL; } else { assert (is_prefix_of (".text.unlikely", section_name)); cold_section = section; } assert (cold_section != NULL && cold_section->comdat_group == NULL); cold_section->is_split_cold_section = 1; /* The cold section must be added to the unlikely chain of comdat groups. */ if (kept->split_section == NULL) { /* This happens if no comdat function in this group so far has been split. */ kept->split_section = cold_section; } else { /* Add the cold_section to the unlikely chain of comdats. */ cold_section->comdat_group = kept->split_section->comdat_group; kept->split_section->comdat_group = cold_section; } } /* It is split and it is not comdat. */ else if (is_split_function) { Section_id *cold_section = NULL; /* Function splitting means that the "hot" part is really the relevant section and the other section is unlikely executed and should not be part of the callgraph. */ /* Store the new section in the section list. */ section->next = first_section; first_section = section; /* If the existing section is cold, the newly detected split must be hot. */ if (is_prefix_of (".text.unlikely", kept->full_name)) { assert (!is_prefix_of (".text.unlikely", section_name)); /* The kept section was the unlikely section. Change the section in section_map to be the new section which is the hot one. */ *slot = section; /* Record the split cold section in the hot section. */ section->split_section = kept; /* Comdats and function splitting are already handled. */ assert (kept->comdat_group == NULL); cold_section = kept; } else { /* Record the split cold section in the hot section. */ assert (is_prefix_of (".text.unlikely", section_name)); kept->split_section = section; cold_section = section; } assert (cold_section != NULL && cold_section->comdat_group == NULL); cold_section->is_split_cold_section = 1; } else { /* The function already exists, it must be a COMDAT. Only one section in the comdat group will be kept, we don't know which. Chain all the comdat sections in the same comdat group to be emitted together later. Keep one section as representative (kept) and update its section_type to be equal to the type of the highest priority section in the group. */ /* Two comdats in the same group can have different priorities. This ensures that the "kept" comdat section has the priority of the highest section in that comdat group. This is necessary because the plugin does not know which section will be kept. */ if (section_priority[kept->section_type] > section_priority[section_type]) kept->section_type = section_type; section->comdat_group = kept->comdat_group; kept->comdat_group = section; } } } /* Add section S to the chain SECTION_START ... SECTION_END. If it is a comdat, get all the comdat sections in the group. Chain these sections to SECTION_END. Set SECTION_START if it is NULL. */ static void write_out_node (Section_id *s, Section_id **section_start, Section_id **section_end) { assert (s != NULL && s->processed == 0); s->processed = 1; if (*section_start == NULL) { *section_start = s; *section_end = s; } else { (*section_end)->group = s; *section_end = s; } /* Print all other sections in the same comdat group. */ while (s->comdat_group) { s = s->comdat_group; s->processed = 1; (*section_end)->group = s; *section_end = s; } } /* Find the max of a, b and c. */ static unsigned long long get_max (unsigned long long a, unsigned long long b, unsigned long long c) { unsigned long long max = a; if (b > max) max = b; if (c > max) max = c; return max; } /* This is true if the max count of any bb in a function should be used as the node weight rather than the count of the entry bb. */ extern int use_max_count; /* Comparison function for sorting two sections a and b by their weight. */ static int section_weight_compare (const void *a, const void *b) { Section_id *s_a = *(Section_id **)a; Section_id *s_b = *(Section_id **)b; assert (use_max_count <= 1); unsigned long long max_sa_weight = get_max (s_a->weight, s_a->computed_weight, s_a->max_count * use_max_count); unsigned long long max_sb_weight = get_max (s_b->weight, s_b->computed_weight, s_b->max_count * use_max_count); if (max_sa_weight < max_sb_weight) return -1; else if (max_sa_weight == max_sb_weight) return 0; return 1; } /* s is a pointer to a section and the group of sections is linked via s->group. The output is the list of sections sorted by their node weights (which is the maximum of their profile count, computed weights or the max bb count if use_max_count is true). */ static Section_id * sort_section_group (Section_id *s) { Section_id **sort_array; Section_id *s_tmp; int num_elements = 0; int i; if (s == NULL) return s; s_tmp = s; while (s_tmp != NULL) { num_elements++; s_tmp = s_tmp->group; } if (num_elements == 1) return s; XNEWVEC_ALLOC (sort_array, Section_id *, num_elements); s_tmp = s; for (i = 0; i < num_elements; ++i) { sort_array[i] = s_tmp; s_tmp = s_tmp->group; } for (i = 0; i < num_elements; ++i) { sort_array[i]->group = NULL; } qsort (sort_array, num_elements, sizeof (Section_id *), section_weight_compare); s_tmp = sort_array[0]; for (i = 1; i < num_elements; ++i) { s_tmp->group = sort_array[i]; s_tmp = s_tmp->group; } s_tmp->group = NULL; return sort_array[0]; } /* If sort_name_prefix is true then the sections not touched by the callgraph are grouped according to their name prefix. When sort_name_prefix is zero, all the sections are put together and sorted according to their node weights. The default value of sort_name_prefix is 0. Even when sections are grouped by their prefix, each group is sorted by the node weights. */ extern int sort_name_prefix; static int section_position_index (int section_type) { assert (section_type >= 0 && section_type < NUM_SECTION_TYPES); if (!sort_name_prefix) return 0; else return section_position[section_type]; } /* Track where the unlikely sections start and end. This will be needed if the unlikely sections need to be split into a separate segment. */ int unlikely_segment_start = -1; int unlikely_segment_end = -1; /* This value is used to determine the profile threshold below which the section is considered unlikely. The default is zero. */ extern unsigned long long unlikely_segment_profile_cutoff; /* Visit each node and print the chain of merged nodes to the file. Update HANDLES and SHNDX to contain the ordered list of sections. */ unsigned int get_layout (FILE *fp, void*** handles, unsigned int** shndx) { Node *n_it; int i = 0; int position; void *slot; int unlikely_section_index; /* Form NUM_SECTION_TYPES + 1 groups of sections. Index 5 corresponds to the list of sections that correspond to functions in the callgraph. For other sections, they are grouped by section_type and stored in index: section_position[section_type]). SECTION_START points to the first section in each section group and SECTION_END points to the last. */ Section_id *section_start[NUM_SECTION_TYPES + 1]; Section_id *section_end[NUM_SECTION_TYPES + 1]; Section_id *s_it; XNEWVEC_ALLOC (*handles, void *, num_sections); XNEWVEC_ALLOC (*shndx, unsigned int, num_sections); for (i = 0; i < NUM_SECTION_TYPES + 1; i++) { section_start[i] = NULL; section_end[i] = NULL; } /* Dump edges to the final reordering file. */ for (n_it = node_chain; n_it != NULL; n_it = n_it->next) { Section_id *s; Node *node; /* First, only consider nodes that are real and that have other nodes merged with it. */ if (n_it->is_merged || !n_it->is_real_node || !n_it->merge_next) continue; slot = htab_find_with_hash (section_map, n_it->name, htab_hash_string (n_it->name)); assert (slot != NULL); s = (Section_id *)slot; write_out_node (s, §ion_start[CALLGRAPH_POSITION], §ion_end[CALLGRAPH_POSITION]); if (fp) fprintf (fp, "# Callgraph group : %s", n_it->name); node = n_it->merge_next; while (node != NULL) { if (node->is_real_node) { slot = htab_find_with_hash (section_map, node->name, htab_hash_string (node->name)); assert (slot != NULL); s = (Section_id *)slot; write_out_node (s, §ion_start[CALLGRAPH_POSITION], §ion_end[CALLGRAPH_POSITION]); if (fp) fprintf (fp, " %s", node->name); } node = node->merge_next; } if (fp) fprintf (fp, "\n"); } /* Now handle all the sections that were not processed above during callgraph handling. Go through all the sections and sort unprocessed sections into different section_type groups. */ s_it = first_section; while (s_it) { if (!s_it->processed) { int index = section_position_index(s_it->section_type); write_out_node (s_it, §ion_start[index], §ion_end[index]); } s_it = s_it->next; } /* Determine the unlikely section index */ unlikely_section_index = -1; for (i = 0; i < ARRAY_SIZE (section_types); ++i) if (strcmp (".text.unlikely.", section_types[i]) == 0) break; assert (i < ARRAY_SIZE (section_types)); unlikely_section_index = section_position_index(i); position = 0; for (i = 0; i < NUM_SECTION_TYPES + 1; ++i) { s_it = section_start[i]; if (s_it == NULL) continue; /* Sort all section groups by weight except the callgraph group. */ if (i != CALLGRAPH_POSITION) s_it = sort_section_group (s_it); /* Start the unlikely segment if necessary. */ assert (use_max_count <= 1); if (i == unlikely_section_index && (get_max (s_it->weight, s_it->computed_weight, s_it->max_count * use_max_count) <= unlikely_segment_profile_cutoff)) { assert (unlikely_segment_start == -1); unlikely_segment_start = position; if (fp != NULL) fprintf (fp, "=== Unlikely sections start ===\n"); } do { assert (position < num_sections); (*handles)[position] = s_it->handle; (*shndx)[position] = s_it->shndx; /* Check if this section will end the unlikely segment. */ if (i == unlikely_section_index && unlikely_segment_start >= 0 && unlikely_segment_start != position && unlikely_segment_end == -1 && (get_max (s_it->weight, s_it->computed_weight, s_it->max_count * use_max_count) > unlikely_segment_profile_cutoff)) { unlikely_segment_end = position - 1; if (fp != NULL) fprintf (fp, "=== Unlikely sections end ===\n"); } position++; if (fp != NULL) { fprintf (fp, "%s entry count = %llu computed = %llu " "max count = %llu split = %d\n", s_it->full_name, s_it->weight, s_it->computed_weight, s_it->max_count, s_it->is_split_cold_section); } s_it = s_it->group; } while (s_it); /* End the unlikely segment if it has not been done already. */ if (i == unlikely_section_index && unlikely_segment_start != -1 && unlikely_segment_end == -1) { unlikely_segment_end = position - 1; if (fp != NULL) fprintf (fp, "=== Unlikely sections end ===\n"); } } return position; } void cleanup () { /* Go through heap allocated objects and free them. */ while (mm_node_chain) { mm_node *node = mm_node_chain; free (node->ptr); mm_node_chain = node->next; free (node); } /* 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; }