Print cgraph_uid in function header
[official-gcc.git] / gcc-4_6-mobile-vtable-security / gcc / dyn-ipa.c
blobad05e90ef4e6bb280f7b2fcef6f68b00f6b11f62
1 /* Compile this one with gcc. */
2 /* Copyright (C) 2009. Free Software Foundation, Inc.
3 Contributed by Xinliang David Li (davidxl@google.com) and
4 Raksit Ashok (raksit@google.com)
6 This file is part of GCC.
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 3, or (at your option) any later
11 version.
13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. */
27 #include "tconfig.h"
28 #include "tsystem.h"
29 #include "coretypes.h"
30 #include "tm.h"
32 #if defined(inhibit_libc)
33 #define IN_LIBGCOV (-1)
34 #else
35 #undef NULL /* Avoid errors if stdio.h and our stddef.h mismatch. */
36 #include <stdio.h>
37 #include <stdlib.h>
38 #define IN_LIBGCOV 1
39 #if defined(L_gcov)
40 #define GCOV_LINKAGE /* nothing */
41 #endif
42 #endif
43 #include "gcov-io.h"
45 struct dyn_pointer_set;
47 #define XNEWVEC(type,ne) (type *)malloc(sizeof(type) * (ne))
48 #define XNEW(type) (type *)malloc(sizeof(type))
49 #define XDELETEVEC(p) free(p)
50 #define XDELETE(p) free(p)
52 struct dyn_cgraph_node
54 struct dyn_cgraph_edge *callees;
55 struct dyn_cgraph_edge *callers;
56 struct dyn_pointer_set *imported_modules;
58 gcov_type guid;
59 gcov_type sum_in_count;
60 gcov_unsigned_t visited;
63 struct dyn_cgraph_edge
65 struct dyn_cgraph_node *caller;
66 struct dyn_cgraph_node *callee;
67 struct dyn_cgraph_edge *next_caller;
68 struct dyn_cgraph_edge *next_callee;
69 gcov_type count;
72 struct dyn_module_info
74 struct dyn_pointer_set *imported_modules;
75 gcov_unsigned_t max_func_ident;
78 struct dyn_cgraph
80 struct dyn_pointer_set **call_graph_nodes;
81 struct gcov_info **modules;
82 /* supplement module information */
83 struct dyn_module_info *sup_modules;
84 const struct gcov_fn_info ***functions;
85 unsigned num_modules;
86 unsigned num_nodes_executed;
89 struct dyn_pointer_set
91 size_t log_slots;
92 size_t n_slots; /* n_slots = 2^log_slots */
93 size_t n_elements;
95 void **slots;
96 unsigned (*get_key) (const void *);
100 #if defined(inhibit_libc)
101 __gcov_build_callgraph (void) {}
102 #else
104 void __gcov_compute_module_groups (void) ATTRIBUTE_HIDDEN;
105 void __gcov_finalize_dyn_callgraph (void) ATTRIBUTE_HIDDEN;
106 static void gcov_dump_callgraph (gcov_type);
107 static void gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node);
108 static void gcov_dump_cgraph_node (struct dyn_cgraph_node *node,
109 unsigned m, unsigned f);
110 static void
111 gcov_dump_cgraph_node_dot (struct dyn_cgraph_node *node,
112 unsigned m, unsigned f,
113 gcov_type cutoff_count);
114 static void
115 pointer_set_destroy (struct dyn_pointer_set *pset);
116 static void **
117 pointer_set_find_or_insert (struct dyn_pointer_set *pset, unsigned key);
118 static struct dyn_pointer_set *
119 pointer_set_create (unsigned (*get_key) (const void *));
121 static struct dyn_cgraph the_dyn_call_graph;
122 static int total_zero_count = 0;
123 static int total_insane_count = 0;
125 static void
126 init_dyn_cgraph_node (struct dyn_cgraph_node *node, gcov_type guid)
128 node->callees = 0;
129 node->callers = 0;
130 node->imported_modules = 0;
131 node->guid = guid;
132 node->visited = 0;
135 /* Return (module_id - 1). FUNC_GUID is the global unique id. */
137 static inline gcov_unsigned_t
138 get_module_idx_from_func_glob_uid (gcov_type func_guid)
140 return EXTRACT_MODULE_ID_FROM_GLOBAL_ID (func_guid) - 1;
143 /* Return (module_id - 1) for MODULE_INFO. */
145 static inline gcov_unsigned_t
146 get_module_idx (const struct gcov_info *module_info)
148 return module_info->mod_info->ident - 1;
151 /* Return intra-module function id given function global unique id
152 FUNC_GUID. */
154 static inline gcov_unsigned_t
155 get_intra_module_func_id (gcov_type func_guid)
157 return EXTRACT_FUNC_ID_FROM_GLOBAL_ID (func_guid);
160 /* Return the pointer to the dynamic call graph node for FUNC_GUID. */
162 static inline struct dyn_cgraph_node *
163 get_cgraph_node (gcov_type func_guid)
165 gcov_unsigned_t mod_id, func_id;
167 mod_id = get_module_idx_from_func_glob_uid (func_guid);
169 /* This is to workaround: calls in __static_initialization_and_destruction
170 should not be instrumented as the module id context for the callees have
171 not setup yet -- this leads to mod_id == (unsigned) (0 - 1). Multithreaded
172 programs may also produce insane func_guid in the profile counter. */
173 if (mod_id >= the_dyn_call_graph.num_modules)
174 return 0;
176 func_id = get_intra_module_func_id (func_guid);
177 if (func_id > the_dyn_call_graph.sup_modules[mod_id].max_func_ident)
178 return 0;
180 return *(pointer_set_find_or_insert
181 (the_dyn_call_graph.call_graph_nodes[mod_id], func_id));
184 /* Return the gcov_info pointer for module with id MODULE_ID. */
186 static inline struct gcov_info *
187 get_module_info (gcov_unsigned_t module_id)
189 return the_dyn_call_graph.modules[module_id];
192 struct gcov_info *__gcov_list ATTRIBUTE_HIDDEN;
194 static inline unsigned
195 cgraph_node_get_key (const void *p)
197 return get_intra_module_func_id (((const struct dyn_cgraph_node *) p)->guid);
200 /* Initialize dynamic call graph. */
202 static void
203 init_dyn_call_graph (void)
205 unsigned num_modules = 0;
206 struct gcov_info *gi_ptr;
208 the_dyn_call_graph.call_graph_nodes = 0;
209 the_dyn_call_graph.modules = 0;
210 the_dyn_call_graph.functions = 0;
211 the_dyn_call_graph.num_nodes_executed = 0;
213 gi_ptr = __gcov_list;
215 for (; gi_ptr; gi_ptr = gi_ptr->next)
216 num_modules++;
218 the_dyn_call_graph.num_modules = num_modules;
220 the_dyn_call_graph.modules
221 = XNEWVEC (struct gcov_info *, num_modules);
223 the_dyn_call_graph.sup_modules
224 = XNEWVEC (struct dyn_module_info, num_modules);
225 memset (the_dyn_call_graph.sup_modules, 0,
226 num_modules * sizeof (struct dyn_module_info));
228 the_dyn_call_graph.functions
229 = XNEWVEC (const struct gcov_fn_info **, num_modules);
231 the_dyn_call_graph.call_graph_nodes
232 = XNEWVEC (struct dyn_pointer_set *, num_modules);
234 gi_ptr = __gcov_list;
236 for (; gi_ptr; gi_ptr = gi_ptr->next)
238 unsigned c_ix = 0, t_ix, j, mod_id, fi_stride, max_func_ident = 0;
239 struct dyn_cgraph_node *node;
241 mod_id = get_module_idx (gi_ptr);
243 the_dyn_call_graph.modules[mod_id] = gi_ptr;
245 the_dyn_call_graph.functions[mod_id]
246 = XNEWVEC (const struct gcov_fn_info *, gi_ptr->n_functions);
248 for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
249 if ((1 << t_ix) & gi_ptr->ctr_mask)
250 c_ix++;
252 fi_stride = offsetof (struct gcov_fn_info, n_ctrs) + c_ix * sizeof (unsigned);
253 if (__alignof__ (struct gcov_fn_info) > sizeof (unsigned))
255 fi_stride += __alignof__ (struct gcov_fn_info) - 1;
256 fi_stride &= ~(__alignof__ (struct gcov_fn_info) - 1);
259 for (j = 0; j < gi_ptr->n_functions; j++)
261 const struct gcov_fn_info *fi_ptr = (const struct gcov_fn_info *)
262 ((const char *) gi_ptr->functions + j * fi_stride);
263 the_dyn_call_graph.functions[mod_id][j] = fi_ptr;
264 if (fi_ptr->ident > max_func_ident)
265 max_func_ident = fi_ptr->ident;
269 the_dyn_call_graph.call_graph_nodes[mod_id]
270 = pointer_set_create (cgraph_node_get_key);
272 the_dyn_call_graph.sup_modules[mod_id].max_func_ident = max_func_ident;
274 for (j = 0; j < gi_ptr->n_functions; j++)
276 const struct gcov_fn_info *fi_ptr
277 = the_dyn_call_graph.functions[mod_id][j];
278 *(pointer_set_find_or_insert
279 (the_dyn_call_graph.call_graph_nodes[mod_id], fi_ptr->ident))
280 = node = XNEW (struct dyn_cgraph_node);
281 the_dyn_call_graph.call_graph_nodes[mod_id]->n_elements++;
282 init_dyn_cgraph_node (node, GEN_FUNC_GLOBAL_ID (gi_ptr->mod_info->ident,
283 fi_ptr->ident));
288 /* Free up memory allocated for dynamic call graph. */
290 void
291 __gcov_finalize_dyn_callgraph (void)
293 unsigned i;
294 struct gcov_info *gi_ptr;
296 for (i = 0; i < the_dyn_call_graph.num_modules; i++)
298 gi_ptr = the_dyn_call_graph.modules[i];
299 const struct gcov_fn_info *fi_ptr;
300 unsigned f_ix;
301 for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
303 struct dyn_cgraph_node *node;
304 struct dyn_cgraph_edge *callees, *next_callee;
305 fi_ptr = the_dyn_call_graph.functions[i][f_ix];
306 node = *(pointer_set_find_or_insert
307 (the_dyn_call_graph.call_graph_nodes[i], fi_ptr->ident));
308 gcc_assert (node);
309 callees = node->callees;
311 if (!callees)
312 continue;
313 while (callees != 0)
315 next_callee = callees->next_callee;
316 XDELETE (callees);
317 callees = next_callee;
319 if (node->imported_modules)
320 pointer_set_destroy (node->imported_modules);
322 if (the_dyn_call_graph.call_graph_nodes[i])
323 pointer_set_destroy (the_dyn_call_graph.call_graph_nodes[i]);
324 if (the_dyn_call_graph.functions[i])
325 XDELETEVEC (the_dyn_call_graph.functions[i]);
326 /* Now delete sup modules */
327 if (the_dyn_call_graph.sup_modules[i].imported_modules)
328 pointer_set_destroy (the_dyn_call_graph.sup_modules[i].imported_modules);
330 XDELETEVEC (the_dyn_call_graph.call_graph_nodes);
331 XDELETEVEC (the_dyn_call_graph.functions);
332 XDELETEVEC (the_dyn_call_graph.sup_modules);
333 XDELETEVEC (the_dyn_call_graph.modules);
336 /* Add outgoing edge OUT_EDGE for caller node CALLER. */
338 static void
339 gcov_add_out_edge (struct dyn_cgraph_node *caller,
340 struct dyn_cgraph_edge *out_edge)
342 if (!caller->callees)
343 caller->callees = out_edge;
344 else
346 out_edge->next_callee = caller->callees;
347 caller->callees = out_edge;
351 /* Add incoming edge IN_EDGE for callee node CALLEE. */
353 static void
354 gcov_add_in_edge (struct dyn_cgraph_node *callee,
355 struct dyn_cgraph_edge *in_edge)
357 if (!callee->callers)
358 callee->callers = in_edge;
359 else
361 in_edge->next_caller = callee->callers;
362 callee->callers = in_edge;
366 /* Add a call graph edge between caller CALLER and callee CALLEE.
367 The edge count is COUNT. */
369 static void
370 gcov_add_cgraph_edge (struct dyn_cgraph_node *caller,
371 struct dyn_cgraph_node *callee,
372 gcov_type count)
374 struct dyn_cgraph_edge *new_edge = XNEW (struct dyn_cgraph_edge);
375 new_edge->caller = caller;
376 new_edge->callee = callee;
377 new_edge->count = count;
378 new_edge->next_caller = 0;
379 new_edge->next_callee = 0;
381 gcov_add_out_edge (caller, new_edge);
382 gcov_add_in_edge (callee, new_edge);
385 /* Add call graph edges from direct calls for caller CALLER. DIR_CALL_COUNTERS
386 is the array of call counters. N_COUNTS is the number of counters. */
388 static void
389 gcov_build_callgraph_dc_fn (struct dyn_cgraph_node *caller,
390 gcov_type *dir_call_counters,
391 unsigned n_counts)
393 unsigned i;
395 for (i = 0; i < n_counts; i += 2)
397 struct dyn_cgraph_node *callee;
398 gcov_type count;
399 gcov_type callee_guid = dir_call_counters[i];
401 count = dir_call_counters[i + 1];
402 if (count == 0)
404 total_zero_count++;
405 continue;
407 callee = get_cgraph_node (callee_guid);
408 if (!callee)
410 total_insane_count++;
411 continue;
413 gcov_add_cgraph_edge (caller, callee, count);
417 /* Add call graph edges from indirect calls for caller CALLER. ICALL_COUNTERS
418 is the array of icall counters. N_COUNTS is the number of counters. */
420 static void
421 gcov_build_callgraph_ic_fn (struct dyn_cgraph_node *caller,
422 gcov_type *icall_counters,
423 unsigned n_counts)
425 unsigned i, j;
427 for (i = 0; i < n_counts; i += GCOV_ICALL_TOPN_NCOUNTS)
429 gcov_type *value_array = &icall_counters[i + 1];
430 for (j = 0; j < GCOV_ICALL_TOPN_NCOUNTS - 1; j += 2)
432 struct dyn_cgraph_node *callee;
433 gcov_type count;
434 gcov_type callee_guid = value_array[j];
436 count = value_array[j + 1];
437 /* Do not update zero count edge count
438 * as it means there is no target in this entry. */
439 if (count == 0)
440 continue;
441 callee = get_cgraph_node (callee_guid);
442 if (!callee)
444 total_insane_count++;
445 continue;
447 gcov_add_cgraph_edge (caller, callee, count);
452 /* Build the dynamic call graph. */
454 static void
455 gcov_build_callgraph (void)
457 struct gcov_info *gi_ptr;
458 unsigned t_ix, m_ix;
460 init_dyn_call_graph ();
462 for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
464 const struct gcov_fn_info *fi_ptr;
465 unsigned c_ix, f_ix, n_counts, dp_cix = 0, ip_cix = 0;
466 gcov_type *dcall_profile_values, *icall_profile_values;
467 gcov_type *arcs_values = 0; unsigned arcs_cix;
469 gi_ptr = the_dyn_call_graph.modules[m_ix];
471 dcall_profile_values = 0;
472 icall_profile_values = 0;
473 c_ix = 0;
474 for (t_ix = 0; t_ix < GCOV_COUNTERS; t_ix++)
475 if ((1 << t_ix) & gi_ptr->ctr_mask)
477 if (t_ix == GCOV_COUNTER_DIRECT_CALL)
479 dcall_profile_values = gi_ptr->counts[c_ix].values;
480 dp_cix = c_ix;
482 if (t_ix == GCOV_COUNTER_ICALL_TOPNV)
484 icall_profile_values = gi_ptr->counts[c_ix].values;
485 ip_cix = c_ix;
487 if (t_ix == GCOV_COUNTER_ARCS)
489 arcs_values = gi_ptr->counts[c_ix].values;
490 arcs_cix = c_ix;
492 c_ix++;
495 if (dcall_profile_values == 0 && icall_profile_values == 0)
496 continue;
498 for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
500 struct dyn_cgraph_node *caller;
501 fi_ptr = the_dyn_call_graph.functions[m_ix][f_ix];
502 caller = *(pointer_set_find_or_insert
503 (the_dyn_call_graph.call_graph_nodes[m_ix],
504 fi_ptr->ident));
505 gcc_assert (caller);
506 if (dcall_profile_values)
508 unsigned offset;
509 n_counts = fi_ptr->n_ctrs[dp_cix];
510 offset = fi_ptr->dc_offset;
511 gcov_build_callgraph_dc_fn (caller,
512 dcall_profile_values + offset,
513 n_counts);
515 if (icall_profile_values)
517 n_counts = fi_ptr->n_ctrs[ip_cix];
518 gcov_build_callgraph_ic_fn (caller, icall_profile_values, n_counts);
519 icall_profile_values += n_counts;
521 if (arcs_values && 0)
523 gcov_type total_arc_count = 0;
524 unsigned arc;
525 n_counts = fi_ptr->n_ctrs[arcs_cix];
526 for (arc = 0; arc < n_counts; arc++)
527 total_arc_count += arcs_values[arc];
528 if (total_arc_count != 0)
529 the_dyn_call_graph.num_nodes_executed++;
530 arcs_values += n_counts;
537 static inline size_t
538 hash1 (unsigned p, unsigned long max, unsigned long logmax)
540 const unsigned long long A = 0x9e3779b97f4a7c16ull;
541 const unsigned long long shift = 64 - logmax;
543 return ((A * (unsigned long) p) >> shift) & (max - 1);
546 /* Allocate an empty imported-modules set. */
548 static struct dyn_pointer_set *
549 pointer_set_create (unsigned (*get_key) (const void *))
551 struct dyn_pointer_set *result = XNEW (struct dyn_pointer_set);
553 result->n_elements = 0;
554 result->log_slots = 8;
555 result->n_slots = (size_t) 1 << result->log_slots;
557 result->slots = XNEWVEC (void *, result->n_slots);
558 memset (result->slots, 0, sizeof (void *) * result->n_slots);
559 result->get_key = get_key;
561 return result;
564 /* Reclaim all memory associated with PSET. */
566 static void
567 pointer_set_destroy (struct dyn_pointer_set *pset)
569 size_t i;
570 for (i = 0; i < pset->n_slots; i++)
571 if (pset->slots[i])
572 XDELETE (pset->slots[i]);
573 XDELETEVEC (pset->slots);
574 XDELETE (pset);
577 /* Subroutine of pointer_set_find_or_insert. Return the insertion slot for KEY
578 into an empty element of SLOTS, an array of length N_SLOTS. */
579 static inline size_t
580 insert_aux (unsigned key, void **slots,
581 size_t n_slots, size_t log_slots,
582 unsigned (*get_key) (const void *))
584 size_t n = hash1 (key, n_slots, log_slots);
585 while (1)
587 if (slots[n] == 0 || get_key (slots[n]) == key)
588 return n;
589 else
591 ++n;
592 if (n == n_slots)
593 n = 0;
598 /* Find slot for KEY. KEY must be nonnull. */
600 static void **
601 pointer_set_find_or_insert (struct dyn_pointer_set *pset, unsigned key)
603 size_t n;
605 /* For simplicity, expand the set even if KEY is already there. This can be
606 superfluous but can happen at most once. */
607 if (pset->n_elements > pset->n_slots / 4)
609 size_t new_log_slots = pset->log_slots + 1;
610 size_t new_n_slots = pset->n_slots * 2;
611 void **new_slots = XNEWVEC (void *, new_n_slots);
612 memset (new_slots, 0, sizeof (void *) * new_n_slots);
613 size_t i;
615 for (i = 0; i < pset->n_slots; ++i)
617 void *value = pset->slots[i];
618 if (!value)
619 continue;
620 n = insert_aux (pset->get_key (value), new_slots, new_n_slots,
621 new_log_slots, pset->get_key);
622 new_slots[n] = value;
625 XDELETEVEC (pset->slots);
626 pset->n_slots = new_n_slots;
627 pset->log_slots = new_log_slots;
628 pset->slots = new_slots;
631 n = insert_aux (key, pset->slots, pset->n_slots, pset->log_slots,
632 pset->get_key);
633 return &pset->slots[n];
636 /* Pass each pointer in PSET to the function in FN, together with the fixed
637 parameters DATA1, DATA2, DATA3. If FN returns false, the iteration stops. */
639 static void
640 pointer_set_traverse (const struct dyn_pointer_set *pset,
641 int (*fn) (const void *, void *, void *, void *),
642 void *data1, void *data2, void *data3)
644 size_t i;
645 for (i = 0; i < pset->n_slots; ++i)
646 if (pset->slots[i] && !fn (pset->slots[i], data1, data2, data3))
647 break;
650 static int
651 imp_mod_set_insert (struct dyn_pointer_set *p, const struct gcov_info *imp_mod,
652 double wt)
654 struct dyn_imp_mod **m = (struct dyn_imp_mod **)
655 pointer_set_find_or_insert (p, imp_mod->mod_info->ident);
656 if (*m)
658 (*m)->weight += wt;
659 return 1;
661 else
663 *m = XNEW (struct dyn_imp_mod);
664 (*m)->imp_mod = imp_mod;
665 (*m)->weight = wt;
666 p->n_elements++;
667 return 0;
671 /* Callback function to propagate import module (VALUE) from callee to
672 caller's imported-module-set (DATA1).
673 The weight is scaled by the scaling-factor (DATA2) before propagation,
674 and accumulated into DATA3. */
675 static int
676 gcov_propagate_imp_modules (const void *value, void *data1, void *data2,
677 void *data3)
679 const struct dyn_imp_mod *m = (const struct dyn_imp_mod *) value;
680 struct dyn_pointer_set *receiving_set = (struct dyn_pointer_set *) data1;
681 double *scale = (double *) data2;
682 double *sum = (double *) data3;
683 double wt = m->weight;
684 if (scale)
685 wt *= *scale;
686 if (sum)
687 (*sum) += wt;
688 imp_mod_set_insert (receiving_set, m->imp_mod, wt);
689 return 1;
692 static int
693 sort_by_count (const void *pa, const void *pb)
695 const struct dyn_cgraph_edge *edge_a = *(struct dyn_cgraph_edge * const *)pa;
696 const struct dyn_cgraph_edge *edge_b = *(struct dyn_cgraph_edge * const *)pb;
698 /* This can overvlow. */
699 /* return edge_b->count - edge_a->count; */
700 if (edge_b->count > edge_a->count)
701 return 1;
702 else if (edge_b->count == edge_a->count)
703 return 0;
704 else
705 return -1;
708 /* Compute the hot callgraph edge threhold. */
710 static gcov_type
711 gcov_compute_cutoff_count (void)
713 unsigned m_ix, capacity, i;
714 unsigned num_edges = 0;
715 gcov_type cutoff_count;
716 double total, cum, cum_cutoff;
717 struct dyn_cgraph_edge **edges;
718 struct gcov_info *gi_ptr;
719 char *cutoff_str;
720 char *num_perc_str;
721 unsigned cutoff_perc;
722 unsigned num_perc;
723 int do_dump;
725 capacity = 100;
726 /* allocate an edge array */
727 edges = XNEWVEC (struct dyn_cgraph_edge*, capacity);
728 /* First count the number of edges. */
729 for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
731 const struct gcov_fn_info *fi_ptr;
732 unsigned f_ix;
734 gi_ptr = the_dyn_call_graph.modules[m_ix];
736 for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
738 struct dyn_cgraph_node *node;
739 struct dyn_cgraph_edge *callees;
741 fi_ptr = the_dyn_call_graph.functions[m_ix][f_ix];
743 node = *(pointer_set_find_or_insert
744 (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident));
745 gcc_assert (node);
747 callees = node->callees;
748 while (callees != 0)
750 num_edges++;
751 if (num_edges < capacity)
752 edges[num_edges - 1] = callees;
753 else
755 capacity = capacity + (capacity >> 1);
756 edges = (struct dyn_cgraph_edge **)realloc (edges, sizeof (void*) * capacity);
757 edges[num_edges - 1] = callees;
759 callees = callees->next_callee;
764 /* Now sort */
765 qsort (edges, num_edges, sizeof (void *), sort_by_count);
766 #define CUM_CUTOFF_PERCENT 80
767 #define MIN_NUM_EDGE_PERCENT 0
768 cutoff_str = getenv ("GCOV_DYN_CGRAPH_CUTOFF");
769 if (cutoff_str && strlen (cutoff_str))
771 if ((num_perc_str = strchr (cutoff_str, ':')))
773 *num_perc_str = '\0';
774 num_perc_str++;
776 cutoff_perc = atoi (cutoff_str);
777 if (num_perc_str)
778 num_perc = atoi (num_perc_str);
779 else
780 num_perc = MIN_NUM_EDGE_PERCENT;
782 else
784 cutoff_perc = CUM_CUTOFF_PERCENT;
785 num_perc = MIN_NUM_EDGE_PERCENT;
788 total = 0;
789 cum = 0;
790 for (i = 0; i < num_edges; i++)
791 total += edges[i]->count;
793 cum_cutoff = (total * cutoff_perc)/100;
794 do_dump = (getenv ("GCOV_DYN_CGRAPH_DUMP") != 0);
795 for (i = 0; i < num_edges; i++)
797 cum += edges[i]->count;
798 if (do_dump)
799 fprintf (stderr, "// edge[%d] count = %.0f [%llx --> %llx]\n",
800 i, (double) edges[i]->count,
801 (long long) edges[i]->caller->guid,
802 (long long) edges[i]->callee->guid);
803 if (cum >= cum_cutoff && (i * 100 >= num_edges * num_perc))
805 cutoff_count = edges[i]->count;
806 break;
810 if (do_dump)
811 fprintf (stderr, "// total = %.0f cum = %.0f cum/total = %.0f%%"
812 " cutoff_count = %lld [total edges: %d hot edges: %d perc: %d%%]\n"
813 " total_zero_count_edges = %d total_insane_count_edgess = %d\n"
814 " total_nodes_executed = %d\n",
815 total, cum, (cum * 100)/total, (long long) cutoff_count,
816 num_edges, i, (i * 100)/num_edges, total_zero_count,
817 total_insane_count, the_dyn_call_graph.num_nodes_executed);
819 XDELETEVEC (edges);
820 return cutoff_count;
823 static inline unsigned
824 imp_mod_get_key (const void *p)
826 return ((const struct dyn_imp_mod *) p)->imp_mod->mod_info->ident;
829 /* Return the imported module set for NODE. */
831 static struct dyn_pointer_set *
832 gcov_get_imp_module_set (struct dyn_cgraph_node *node)
834 if (!node->imported_modules)
835 node->imported_modules = pointer_set_create (imp_mod_get_key);
837 return node->imported_modules;
840 /* Return the imported module set for MODULE MI. */
842 static struct dyn_pointer_set *
843 gcov_get_module_imp_module_set (struct dyn_module_info *mi)
845 if (!mi->imported_modules)
846 mi->imported_modules = pointer_set_create (imp_mod_get_key);
848 return mi->imported_modules;
851 /* Callback function to mark if a module needs to be exported. */
853 static int
854 gcov_mark_export_modules (const void *value,
855 void *data1 ATTRIBUTE_UNUSED,
856 void *data2 ATTRIBUTE_UNUSED,
857 void *data3 ATTRIBUTE_UNUSED)
859 const struct gcov_info *module_info
860 = ((const struct dyn_imp_mod *) value)->imp_mod;
862 module_info->mod_info->is_exported = 1;
863 return 1;
866 struct gcov_import_mod_array
868 const struct dyn_imp_mod **imported_modules;
869 struct gcov_info *importing_module;
870 unsigned len;
873 /* Callback function to compute pointer set size. */
875 static int
876 gcov_compute_mset_size (const void *value ATTRIBUTE_UNUSED,
877 void *data1,
878 void *data2 ATTRIBUTE_UNUSED,
879 void *data3 ATTRIBUTE_UNUSED)
881 unsigned *len = (unsigned *) data1;
882 (*len)++;
883 return 1;
886 /* Callback function to collect imported modules. */
888 static int
889 gcov_collect_imported_modules (const void *value,
890 void *data1,
891 void *data2 ATTRIBUTE_UNUSED,
892 void *data3 ATTRIBUTE_UNUSED)
894 struct gcov_import_mod_array *out_array;
895 const struct dyn_imp_mod *m
896 = (const struct dyn_imp_mod *) value;
898 out_array = (struct gcov_import_mod_array *) data1;
900 if (m->imp_mod != out_array->importing_module)
901 out_array->imported_modules[out_array->len++] = m;
903 return 1;
906 /* Comparator for sorting imported modules using weights. */
908 static int
909 sort_by_module_wt (const void *pa, const void *pb)
911 const struct dyn_imp_mod *m_a = *((const struct dyn_imp_mod * const *) pa);
912 const struct dyn_imp_mod *m_b = *((const struct dyn_imp_mod * const *) pb);
914 /* We want to sort in descending order of weights. */
915 if (m_a->weight < m_b->weight)
916 return +1;
917 if (m_a->weight > m_b->weight)
918 return -1;
919 return get_module_idx (m_a->imp_mod) - get_module_idx (m_b->imp_mod);
922 /* Return a dynamic array of imported modules that is sorted for
923 the importing module MOD_INFO. The length of the array is returned
924 in *LEN. */
926 const struct dyn_imp_mod **
927 gcov_get_sorted_import_module_array (struct gcov_info *mod_info,
928 unsigned *len)
930 unsigned mod_id;
931 struct dyn_module_info *sup_mod_info;
932 unsigned array_len = 0;
933 struct gcov_import_mod_array imp_array;
935 mod_id = get_module_idx (mod_info);
936 sup_mod_info = &the_dyn_call_graph.sup_modules[mod_id];
938 if (sup_mod_info->imported_modules == 0)
939 return 0;
941 pointer_set_traverse (sup_mod_info->imported_modules,
942 gcov_compute_mset_size, &array_len, 0, 0);
943 imp_array.imported_modules = XNEWVEC (const struct dyn_imp_mod *, array_len);
944 imp_array.len = 0;
945 imp_array.importing_module = mod_info;
946 pointer_set_traverse (sup_mod_info->imported_modules,
947 gcov_collect_imported_modules, &imp_array, 0, 0);
948 *len = imp_array.len;
949 qsort (imp_array.imported_modules, imp_array.len,
950 sizeof (void *), sort_by_module_wt);
951 return imp_array.imported_modules;
954 /* Compute modules that are needed for NODE (for cross module inlining).
955 CUTTOFF_COUNT is the call graph edge count cutoff value.
956 IMPORT_SCALE is the scaling-factor (percent) by which to scale the
957 weights of imported modules of a callee before propagating them to
958 the caller, if the callee and caller are in different modules.
960 Each imported module is assigned a weight that corresponds to the
961 expected benefit due to cross-module inlining. When the imported modules
962 are written out, they are sorted with highest weight first.
964 The following example illustrates how the weight is computed:
966 Suppose we are processing call-graph node A. It calls function B 50 times,
967 which calls function C 1000 times, and function E 800 times. Lets say B has
968 another in-edge from function D, with edge-count of 50. Say all the
969 functions are in separate modules (modules a, b, c, d, e, respectively):
973 | 50
975 50 v 1000
976 A --------> B ----------> C
978 | 800
983 Nodes are processed in depth-first order, so when processing A, we first
984 process B. For node B, we are going to add module c to the imported-module
985 set, with weight 1000 (edge-count), and module e with weight 800.
986 Coming back to A, we are going to add the imported-module-set of B to A,
987 after doing some scaling.
988 The first scaling factor comes from the fact that A calls B 50 times, but B
989 has in-edge-count total of 100. So this scaling factor is 50/100 = 0.5
990 The second scaling factor is that since B is in a different module than A,
991 we want to slightly downgrade imported modules of B, before adding to the
992 imported-modules set of A. This scaling factor has a default value of 50%
993 (can be set via env variable GCOV_DYN_IMPORT_SCALE).
994 So we end up adding modules c and e to the imported-set of A, with weights
995 0.5*0.5*1000=250 and 0.5*0.5*800=200, respectively.
997 Next, we have to add module b itself to A. The weight is computed as the
998 edge-count plus the sum of scaled-weights of all modules in the
999 imported-module set of B, i.e., 50 + 250 + 200 = 500.
1001 In computing the weight of module b, we add the sum of scaled-weights of
1002 imported modules of b, because it doesn't make sense to import c, e in
1003 module a, until module b is imported. */
1005 static void
1006 gcov_process_cgraph_node (struct dyn_cgraph_node *node,
1007 gcov_type cutoff_count,
1008 unsigned import_scale)
1010 unsigned mod_id;
1011 struct dyn_cgraph_edge *callees;
1012 struct dyn_cgraph_edge *callers;
1013 node->visited = 1;
1014 node->sum_in_count = 0;
1016 callers = node->callers;
1017 while (callers)
1019 node->sum_in_count += callers->count;
1020 callers = callers->next_caller;
1023 callees = node->callees;
1024 mod_id = get_module_idx_from_func_glob_uid (node->guid);
1026 while (callees)
1028 if (!callees->callee->visited)
1029 gcov_process_cgraph_node (callees->callee,
1030 cutoff_count,
1031 import_scale);
1032 callees = callees->next_callee;
1035 callees = node->callees;
1036 while (callees)
1038 if (callees->count >= cutoff_count)
1040 unsigned callee_mod_id;
1041 struct dyn_pointer_set *imp_modules
1042 = gcov_get_imp_module_set (node);
1044 callee_mod_id
1045 = get_module_idx_from_func_glob_uid (callees->callee->guid);
1047 double callee_mod_wt = (double) callees->count;
1048 if (callees->callee->imported_modules)
1050 double scale = ((double) callees->count) /
1051 ((double) callees->callee->sum_in_count);
1052 /* Reduce weight if callee is in different module. */
1053 if (mod_id != callee_mod_id)
1054 scale = (scale * import_scale) / 100.0;
1055 pointer_set_traverse (callees->callee->imported_modules,
1056 gcov_propagate_imp_modules,
1057 imp_modules, &scale, &callee_mod_wt);
1059 if (mod_id != callee_mod_id)
1061 struct gcov_info *callee_mod_info
1062 = get_module_info (callee_mod_id);
1063 imp_mod_set_insert (imp_modules, callee_mod_info, callee_mod_wt);
1067 callees = callees->next_callee;
1071 /* Compute module grouping using CUTOFF_COUNT as the hot edge
1072 threshold. */
1074 #define DEFAULT_IMPORT_SCALE 100
1075 static void
1076 gcov_compute_module_groups (gcov_type cutoff_count)
1078 unsigned m_ix;
1079 struct gcov_info *gi_ptr;
1080 const char *import_scale_str;
1081 unsigned import_scale = DEFAULT_IMPORT_SCALE;
1083 import_scale_str = getenv ("GCOV_DYN_IMPORT_SCALE");
1084 if (import_scale_str && strlen (import_scale_str))
1085 import_scale = atoi (import_scale_str);
1087 for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
1089 const struct gcov_fn_info *fi_ptr;
1090 unsigned f_ix;
1092 gi_ptr = the_dyn_call_graph.modules[m_ix];
1094 for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
1096 struct dyn_cgraph_node *node;
1098 fi_ptr = the_dyn_call_graph.functions[m_ix][f_ix];
1099 node = *(pointer_set_find_or_insert
1100 (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident));
1101 gcc_assert (node);
1102 if (node->visited)
1103 continue;
1105 gcov_process_cgraph_node (node, cutoff_count, import_scale);
1109 for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
1111 const struct gcov_fn_info *fi_ptr;
1112 unsigned f_ix;
1114 gi_ptr = the_dyn_call_graph.modules[m_ix];
1116 for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
1118 struct dyn_cgraph_node *node;
1119 unsigned mod_id;
1120 struct dyn_pointer_set *imp_modules;
1122 fi_ptr = the_dyn_call_graph.functions[m_ix][f_ix];
1123 node = *(pointer_set_find_or_insert
1124 (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident));
1125 gcc_assert (node);
1127 if (!node->imported_modules)
1128 continue;
1130 mod_id = get_module_idx_from_func_glob_uid (node->guid);
1131 gcc_assert (mod_id == m_ix);
1133 imp_modules
1134 = gcov_get_module_imp_module_set (
1135 &the_dyn_call_graph.sup_modules[mod_id]);
1137 pointer_set_traverse (node->imported_modules,
1138 gcov_propagate_imp_modules,
1139 imp_modules, 0, 0);
1143 /* Now compute the export attribute */
1144 for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
1146 struct dyn_module_info *mi
1147 = &the_dyn_call_graph.sup_modules[m_ix];
1148 if (mi->imported_modules)
1149 pointer_set_traverse (mi->imported_modules,
1150 gcov_mark_export_modules, 0, 0, 0);
1154 /* For each module, compute at random, the group of imported modules,
1155 that is of size at most MAX_GROUP_SIZE. */
1157 static void
1158 gcov_compute_random_module_groups (unsigned max_group_size)
1160 unsigned m_ix;
1162 if (max_group_size > the_dyn_call_graph.num_modules)
1163 max_group_size = the_dyn_call_graph.num_modules;
1165 for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
1167 struct dyn_pointer_set *imp_modules =
1168 gcov_get_module_imp_module_set (&the_dyn_call_graph.sup_modules[m_ix]);
1169 int cur_group_size = random () % max_group_size;
1170 int i = 0;
1171 while (i < cur_group_size)
1173 struct gcov_info *imp_mod_info;
1174 unsigned mod_id = random () % the_dyn_call_graph.num_modules;
1175 if (mod_id == m_ix)
1176 continue;
1177 imp_mod_info = get_module_info (mod_id);
1178 if (!imp_mod_set_insert (imp_modules, imp_mod_info, 1.0))
1179 i++;
1183 /* Now compute the export attribute */
1184 for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
1186 struct dyn_module_info *mi
1187 = &the_dyn_call_graph.sup_modules[m_ix];
1188 if (mi->imported_modules)
1189 pointer_set_traverse (mi->imported_modules,
1190 gcov_mark_export_modules, 0, 0, 0);
1194 /* Write out MOD_INFO into the gcda file. IS_PRIMARY is a flag
1195 indicating if the module is the primary module in the group. */
1197 static void
1198 gcov_write_module_info (const struct gcov_info *mod_info,
1199 unsigned is_primary)
1201 gcov_unsigned_t len = 0, filename_len = 0, src_filename_len = 0, i, j;
1202 gcov_unsigned_t num_strings;
1203 gcov_unsigned_t *aligned_fname;
1204 struct gcov_module_info *module_info = mod_info->mod_info;
1205 filename_len = (strlen (module_info->da_filename) +
1206 sizeof (gcov_unsigned_t)) / sizeof (gcov_unsigned_t);
1207 src_filename_len = (strlen (module_info->source_filename) +
1208 sizeof (gcov_unsigned_t)) / sizeof (gcov_unsigned_t);
1209 len = filename_len + src_filename_len;
1210 len += 2; /* each name string is led by a length. */
1212 num_strings = module_info->num_quote_paths + module_info->num_bracket_paths +
1213 module_info->num_cpp_defines + module_info->num_cpp_includes +
1214 module_info->num_cl_args;
1215 for (i = 0; i < num_strings; i++)
1217 gcov_unsigned_t string_len
1218 = (strlen (module_info->string_array[i]) + sizeof (gcov_unsigned_t))
1219 / sizeof (gcov_unsigned_t);
1220 len += string_len;
1221 len += 1; /* Each string is lead by a length. */
1224 len += 9; /* 9 more fields */
1226 gcov_write_tag_length (GCOV_TAG_MODULE_INFO, len);
1227 gcov_write_unsigned (module_info->ident);
1228 gcov_write_unsigned (is_primary);
1229 gcov_write_unsigned (module_info->is_exported);
1230 gcov_write_unsigned (module_info->lang);
1231 gcov_write_unsigned (module_info->num_quote_paths);
1232 gcov_write_unsigned (module_info->num_bracket_paths);
1233 gcov_write_unsigned (module_info->num_cpp_defines);
1234 gcov_write_unsigned (module_info->num_cpp_includes);
1235 gcov_write_unsigned (module_info->num_cl_args);
1237 /* Now write the filenames */
1238 aligned_fname = (gcov_unsigned_t *) alloca ((filename_len + src_filename_len + 2) *
1239 sizeof (gcov_unsigned_t));
1240 memset (aligned_fname, 0,
1241 (filename_len + src_filename_len + 2) * sizeof (gcov_unsigned_t));
1242 aligned_fname[0] = filename_len;
1243 strcpy ((char*) (aligned_fname + 1), module_info->da_filename);
1244 aligned_fname[filename_len + 1] = src_filename_len;
1245 strcpy ((char*) (aligned_fname + filename_len + 2), module_info->source_filename);
1247 for (i = 0; i < (filename_len + src_filename_len + 2); i++)
1248 gcov_write_unsigned (aligned_fname[i]);
1250 /* Now write the string array. */
1251 for (j = 0; j < num_strings; j++)
1253 gcov_unsigned_t *aligned_string;
1254 gcov_unsigned_t string_len =
1255 (strlen (module_info->string_array[j]) + sizeof (gcov_unsigned_t)) /
1256 sizeof (gcov_unsigned_t);
1257 aligned_string = (gcov_unsigned_t *)
1258 alloca ((string_len + 1) * sizeof (gcov_unsigned_t));
1259 memset (aligned_string, 0, (string_len + 1) * sizeof (gcov_unsigned_t));
1260 aligned_string[0] = string_len;
1261 strcpy ((char*) (aligned_string + 1), module_info->string_array[j]);
1262 for (i = 0; i < (string_len + 1); i++)
1263 gcov_write_unsigned (aligned_string[i]);
1267 /* Write out MOD_INFO and its imported modules into gcda file. */
1269 void
1270 gcov_write_module_infos (struct gcov_info *mod_info)
1272 unsigned mod_id, imp_len = 0;
1273 const struct dyn_imp_mod **imp_mods;
1275 mod_id = get_module_idx (mod_info);
1276 gcov_write_module_info (mod_info, 1);
1278 imp_mods = gcov_get_sorted_import_module_array (mod_info, &imp_len);
1279 if (imp_mods)
1281 unsigned i;
1283 for (i = 0; i < imp_len; i++)
1285 const struct gcov_info *imp_mod = imp_mods[i]->imp_mod;
1286 gcov_write_module_info (imp_mod, 0);
1288 free (imp_mods);
1292 /* Compute module groups needed for L-IPO compilation. */
1294 void
1295 __gcov_compute_module_groups (void)
1297 gcov_type cut_off_count;
1298 char *seed = getenv ("LIPO_RANDOM_GROUPING");
1299 char *max_group_size = seed ? strchr (seed, ':') : 0;
1301 if (seed && max_group_size)
1303 *max_group_size = '\0';
1304 max_group_size++;
1305 srandom (atoi (seed));
1306 init_dyn_call_graph ();
1307 gcov_compute_random_module_groups (atoi (max_group_size));
1308 return;
1311 /* First compute dynamic call graph. */
1312 gcov_build_callgraph ();
1314 cut_off_count = gcov_compute_cutoff_count ();
1316 gcov_compute_module_groups (cut_off_count);
1318 gcov_dump_callgraph (cut_off_count);
1322 /* Dumper function for NODE. */
1323 static void
1324 gcov_dump_cgraph_node_short (struct dyn_cgraph_node *node)
1326 unsigned mod_id, func_id;
1327 struct gcov_info *mod_info;
1328 mod_id = get_module_idx_from_func_glob_uid (node->guid);
1329 func_id = get_intra_module_func_id (node->guid);
1331 mod_info = the_dyn_call_graph.modules[mod_id];
1333 fprintf (stderr, "NODE(%llx) module(%s) func(%u)",
1334 (long long)node->guid,
1335 mod_info->mod_info->source_filename, func_id);
1338 /* Dumper function for NODE. M is the module id and F is the function id. */
1340 static void
1341 gcov_dump_cgraph_node (struct dyn_cgraph_node *node, unsigned m, unsigned f)
1343 unsigned mod_id, func_id;
1344 struct gcov_info *mod_info;
1345 struct dyn_cgraph_edge *callers;
1346 struct dyn_cgraph_edge *callees;
1348 mod_id = get_module_idx_from_func_glob_uid (node->guid);
1349 func_id = get_intra_module_func_id (node->guid);
1350 gcc_assert (mod_id == m && func_id == f);
1352 mod_info = the_dyn_call_graph.modules[mod_id];
1354 fprintf (stderr, "NODE(%llx) module(%s) func(%x)\n",
1355 (long long) node->guid,
1356 mod_info->mod_info->source_filename, f);
1358 /* Now dump callers. */
1359 callers = node->callers;
1360 fprintf (stderr, "\t[CALLERS]\n");
1361 while (callers != 0)
1363 fprintf (stderr,"\t\t[count=%ld] ", (long) callers->count);
1364 gcov_dump_cgraph_node_short (callers->caller);
1365 fprintf (stderr,"\n");
1366 callers = callers->next_caller;
1369 callees = node->callees;
1370 fprintf (stderr, "\t[CALLEES]\n");
1371 while (callees != 0)
1373 fprintf (stderr,"\t\t[count=%ld] ", (long) callees->count);
1374 gcov_dump_cgraph_node_short (callees->callee);
1375 fprintf (stderr,"\n");
1376 callees = callees->next_callee;
1380 /* Dumper function for NODE. M is the module id and F is the function id. */
1382 static void
1383 gcov_dump_cgraph_node_dot (struct dyn_cgraph_node *node,
1384 unsigned m, unsigned f,
1385 gcov_type cutoff_count)
1387 unsigned mod_id, func_id, imp_len = 0, i;
1388 struct gcov_info *mod_info;
1389 const struct dyn_imp_mod **imp_mods;
1390 struct dyn_cgraph_edge *callees;
1392 mod_id = get_module_idx_from_func_glob_uid (node->guid);
1393 func_id = get_intra_module_func_id (node->guid);
1394 gcc_assert (mod_id == m && func_id == f);
1396 mod_info = the_dyn_call_graph.modules[mod_id];
1398 fprintf (stderr, "NODE_%llx[label=\"MODULE\\n(%s)\\n FUNC(%x)\\n",
1399 (long long) node->guid, mod_info->mod_info->source_filename, f);
1401 imp_mods = gcov_get_sorted_import_module_array (mod_info, &imp_len);
1402 fprintf (stderr, "IMPORTS:\\n");
1403 if (imp_mods)
1405 for (i = 0; i < imp_len; i++)
1406 fprintf (stderr, "%s\\n", imp_mods[i]->imp_mod->mod_info->source_filename);
1407 fprintf (stderr, "\"]\n");
1408 free (imp_mods);
1410 else
1411 fprintf (stderr, "\"]\n");
1413 callees = node->callees;
1414 while (callees != 0)
1416 if (callees->count >= cutoff_count)
1417 fprintf (stderr, "NODE_%llx -> NODE_%llx[label=%lld color=red]\n",
1418 (long long) node->guid, (long long) callees->callee->guid,
1419 (long long) callees->count);
1420 else
1421 fprintf (stderr, "NODE_%llx -> NODE_%llx[label=%lld color=blue]\n",
1422 (long long) node->guid, (long long) callees->callee->guid,
1423 (long long) callees->count);
1424 callees = callees->next_callee;
1428 /* Dump dynamic call graph. CUTOFF_COUNT is the computed hot edge threshold. */
1430 static void
1431 gcov_dump_callgraph (gcov_type cutoff_count)
1433 struct gcov_info *gi_ptr;
1434 unsigned m_ix;
1435 const char *dyn_cgraph_dump = 0;
1437 dyn_cgraph_dump = getenv ("GCOV_DYN_CGRAPH_DUMP");
1439 if (!dyn_cgraph_dump || !strlen (dyn_cgraph_dump))
1440 return;
1442 fprintf (stderr,"digraph dyn_call_graph {\n");
1443 fprintf (stderr,"node[shape=box]\nsize=\"11,8.5\"\n");
1445 for (m_ix = 0; m_ix < the_dyn_call_graph.num_modules; m_ix++)
1447 const struct gcov_fn_info *fi_ptr;
1448 unsigned f_ix;
1450 gi_ptr = the_dyn_call_graph.modules[m_ix];
1452 for (f_ix = 0; f_ix < gi_ptr->n_functions; f_ix++)
1454 struct dyn_cgraph_node *node;
1455 fi_ptr = the_dyn_call_graph.functions[m_ix][f_ix];
1457 node = *(pointer_set_find_or_insert
1458 (the_dyn_call_graph.call_graph_nodes[m_ix], fi_ptr->ident));
1459 gcc_assert (node);
1461 /* skip dead functions */
1462 if (!node->callees && !node->callers)
1463 continue;
1465 if (dyn_cgraph_dump[0] == '1')
1466 gcov_dump_cgraph_node (node, m_ix, fi_ptr->ident);
1467 else
1468 gcov_dump_cgraph_node_dot (node, m_ix, fi_ptr->ident,
1469 cutoff_count);
1472 fprintf (stderr,"}\n");
1476 #endif