1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
4 * Copyright (C) James Liggett 2006 <jrliggett@cox.net>
6 * gprof-call-graph.c is free software.
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2, or (at your option) any later version.
12 * plugin.c is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 * See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with plugin.c. See the file "COPYING". If not,
19 * write to: The Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
24 #include "gprof-call-graph.h"
26 #define BLOCK_SEPARATOR "-----------------------------------------------"
28 struct _GProfCallGraphPriv
30 GList
*blocks
; /* Straight list of all call graph blocks */
31 GList
*root
; /* List of strings that refer to
32 * "root functions," or functions without
34 GHashTable
*lookup_table
; /* Find function blocks by name */
38 gprof_call_graph_init (GProfCallGraph
*self
)
40 self
->priv
= g_new0 (GProfCallGraphPriv
, 1);
42 self
->priv
->lookup_table
= g_hash_table_new (g_str_hash
, g_str_equal
);
46 gprof_call_graph_finalize (GObject
*obj
)
51 self
= (GProfCallGraph
*) obj
;
53 g_hash_table_destroy (self
->priv
->lookup_table
);
55 current
= self
->priv
->blocks
;
59 gprof_call_graph_block_free (GPROF_CALL_GRAPH_BLOCK (current
->data
));
60 current
= g_list_next (current
);
63 g_list_free (self
->priv
->blocks
);
64 g_list_free (self
->priv
->root
);
70 gprof_call_graph_class_init (GProfCallGraphClass
*klass
)
72 GObjectClass
*object_class
;
74 object_class
= (GObjectClass
*) klass
;
76 object_class
->finalize
= gprof_call_graph_finalize
;
80 get_primary_line_fields(gchar
*buffer
)
83 gchar
*line
; /* line data without leading index number */
84 gchar
*calls_field
; /* Pointer to string that begins at the calls field */
86 gint pos
; /* Where are we in the buffer? */
88 string_table
= g_new0 (gchar
*, 6); /* NULL terminated */
90 /* Skip over index number */
91 line
= strchr (buffer
, ']') + 1;
93 /* Read the first 3 fields */
96 for (i
= 0; i
< 3; i
++)
97 string_table
[i
] = read_to_whitespace (&line
[pos
], &pos
, pos
);
99 /* In some cases, uncalled functions will have empty calls field */
100 calls_field
= strip_whitespace (&line
[pos
]);
102 /* If the field begins with a number, we have the field. Otherwise,
103 * calls_field points to the function name. */
105 /* In some cases, index numbers at the end of lines will be in parenthesis
106 * instead of square brackets. */
108 if (g_ascii_isdigit(calls_field
[0]))
110 string_table
[3] = read_to_whitespace (&line
[pos
], &pos
, pos
);
111 string_table
[4] = read_to_delimiter (&line
[pos
], " [");
113 if (!string_table
[4])
114 string_table
[4] = read_to_delimiter (&line
[pos
], " (");
118 string_table
[3] = g_strdup ("0");
119 string_table
[4] = read_to_delimiter (calls_field
, " [");
121 if (!string_table
[4])
122 string_table
[4] = read_to_delimiter (calls_field
, " (");
125 g_free (calls_field
);
131 get_secondary_line_fields(gchar
*buffer
)
133 gchar
**string_table
;
134 gchar
*line
; /* Pointer to the beginning of the current field */
135 gint num_fields
; /* Number of fields read from line */
139 /* Secondary lines can have a maximum of 4 fields. In some cases (as in
140 * recursive functions) lines may have only 2. Continue to read
141 * fields until we encounter one that doesn't begin with a digit. This
142 * tells us that we've reached the last field in the line. */
144 string_table
= g_new0 (gchar
*, 5); /* NULL terminated */
148 line
= strip_whitespace (buffer
);
150 while (g_ascii_isdigit(line
[0]))
152 string_table
[num_fields
] = read_to_whitespace (&buffer
[pos
], &pos
, pos
);
154 line
= strip_whitespace (&buffer
[pos
]);
160 /* If we only read one field, it refers to the calls field. If we didn't
161 * read any fields here, this tells us that the primary line that this
162 * line belongs to refers to an uncalled function */
166 string_table
[2] = string_table
[0];
168 for (i
= 0; i
< 2; i
++)
169 string_table
[i
] = g_strdup ("0");
171 else if (num_fields
== 0)
173 g_free (string_table
);
177 /* In some cases, index numbers at the end of lines will be in parenthesis
178 * instead of square brackets. */
179 string_table
[3] = read_to_delimiter (&buffer
[pos
], " [");
181 if (!string_table
[3])
182 string_table
[3] = read_to_delimiter (&buffer
[pos
], " (");
188 gprof_call_graph_add_block (GProfCallGraph
*self
, GProfCallGraphBlock
*block
)
190 GProfCallGraphBlockEntry
*entry
;
193 entry
= gprof_call_graph_block_get_primary_entry (block
);
194 name
= gprof_call_graph_block_entry_get_name (entry
);
196 self
->priv
->blocks
= g_list_append (self
->priv
->blocks
, block
);
197 g_hash_table_insert (self
->priv
->lookup_table
, name
, block
);
199 if (!gprof_call_graph_block_has_parents (block
))
200 self
->priv
->root
= g_list_append (self
->priv
->root
, block
);
204 gprof_call_graph_get_type (void)
206 static GType obj_type
= 0;
210 static const GTypeInfo obj_info
=
212 sizeof (GProfCallGraphClass
),
213 (GBaseInitFunc
) NULL
,
214 (GBaseFinalizeFunc
) NULL
,
215 (GClassInitFunc
) gprof_call_graph_class_init
,
216 (GClassFinalizeFunc
) NULL
,
217 NULL
, /* class_data */
218 sizeof (GProfCallGraph
),
220 (GInstanceInitFunc
) gprof_call_graph_init
,
221 NULL
/* value_table */
223 obj_type
= g_type_register_static (G_TYPE_OBJECT
,
224 "GProfCallGraph", &obj_info
,
231 gprof_call_graph_new (FILE *stream
, GProfFlatProfile
*flat_profile
)
233 gchar buffer
[PATH_MAX
];
236 GProfCallGraphBlock
*block
;
237 GProfCallGraphBlockEntry
*entry
;
239 GProfCallGraph
*call_graph
;
240 gboolean found_primary
= FALSE
;
242 call_graph
= g_object_new (GPROF_CALL_GRAPH_TYPE
, NULL
);
244 /* Find the beginning of the call graph. The call graph begins with
248 /* Don't loop infinitely if we don't have any data */
249 if (!fgets (buffer
, PATH_MAX
, stream
))
252 } while (strncmp ("index", buffer
, 5) != 0);
256 while (fgets (buffer
, PATH_MAX
, stream
))
258 /* If the first character of the line is 12, that's the end of the call
265 block
= gprof_call_graph_block_new ();
266 found_primary
= FALSE
;
269 /* Remove the newline from the buffer */
270 length
= strlen (buffer
);
271 buffer
[length
- 1] = 0;
273 /* If we encounter the block separator, add the block to the graph */
274 if (strcmp (BLOCK_SEPARATOR
, buffer
) == 0)
276 gprof_call_graph_add_block (call_graph
, block
);
281 /* If the buffer begins with an index number, treat it as a primary
283 if (buffer
[0] == '[')
285 found_primary
= TRUE
;
287 fields
= get_primary_line_fields (buffer
);
288 entry
= gprof_call_graph_block_primary_entry_new (fields
);
292 gprof_call_graph_block_add_primary_entry (block
, entry
);
296 fields
= get_secondary_line_fields (buffer
);
298 /* If we don't get any fields, that means this is a
299 * placeholder line.*/
303 entry
= gprof_call_graph_block_secondary_entry_new (fields
);
304 entry_name
= gprof_call_graph_block_entry_get_name (entry
);
308 if (gprof_flat_profile_find_entry(flat_profile
, entry_name
))
311 gprof_call_graph_block_add_child_entry (block
, entry
);
313 gprof_call_graph_block_add_parent_entry (block
, entry
);
316 gprof_call_graph_block_entry_free (entry
);
325 gprof_call_graph_free (GProfCallGraph
*self
)
327 g_object_unref (self
);
330 GProfCallGraphBlock
*
331 gprof_call_graph_find_block (GProfCallGraph
*self
, gchar
*name
)
333 return GPROF_CALL_GRAPH_BLOCK (g_hash_table_lookup (self
->priv
->lookup_table
,
337 GProfCallGraphBlock
*
338 gprof_call_graph_get_first_block (GProfCallGraph
*self
, GList
**iter
)
340 *iter
= self
->priv
->blocks
;
343 return GPROF_CALL_GRAPH_BLOCK ((*iter
)->data
);
348 GProfCallGraphBlock
*
349 gprof_call_graph_get_root (GProfCallGraph
*self
, GList
**iter
)
351 *iter
= self
->priv
->root
;
354 return GPROF_CALL_GRAPH_BLOCK ((*iter
)->data
);
360 gprof_call_graph_dump (GProfCallGraph
*self
, FILE *stream
)
362 GProfCallGraphBlockEntry
*function
;
363 GProfCallGraphBlockEntry
*current_parent
;
364 GProfCallGraphBlockEntry
*current_child
;
365 GList
*current_block
;
366 GList
*current_child_iter
;
367 GList
*current_parent_iter
;
369 current_block
= self
->priv
->blocks
;
371 while (current_block
)
373 function
= gprof_call_graph_block_get_primary_entry (current_block
->data
);
375 fprintf (stream
, "Function: %s\n",
376 gprof_call_graph_block_entry_get_name (function
));
377 fprintf (stream
, "Time: %0.2f\n",
378 gprof_call_graph_block_entry_get_time_perc (function
));
379 fprintf (stream
, "Self sec: %0.2f\n",
380 gprof_call_graph_block_entry_get_self_sec (function
));
381 fprintf (stream
, "Child sec: %0.2f\n",
382 gprof_call_graph_block_entry_get_child_sec (function
));
383 fprintf (stream
, "Calls: %s\n",
384 gprof_call_graph_block_entry_get_calls (function
));
385 fprintf (stream
, "Recursive: %s\n\n",
386 gprof_call_graph_block_is_recursive (current_block
->data
) ? "Yes" : "No");
388 fprintf (stream
, "Called: \n");
390 current_child
= gprof_call_graph_block_get_first_child (current_block
->data
,
391 ¤t_child_iter
);
393 while (current_child
)
395 fprintf (stream
, "%s %0.2f, %0.2f, %0.2f, %s\n",
396 gprof_call_graph_block_entry_get_name (current_child
),
397 gprof_call_graph_block_entry_get_time_perc (current_child
),
398 gprof_call_graph_block_entry_get_self_sec (current_child
),
399 gprof_call_graph_block_entry_get_child_sec (current_child
),
400 gprof_call_graph_block_entry_get_calls (current_child
));
402 current_child
= gprof_call_graph_block_entry_get_next (current_child_iter
,
403 ¤t_child_iter
);
405 fprintf (stream
, "\n");
407 fprintf (stream
, "Called by: \n");
409 current_parent
= gprof_call_graph_block_get_first_parent (current_block
->data
,
410 ¤t_parent_iter
);
411 while (current_parent
)
413 fprintf (stream
, "%s %0.2f, %0.2f, %0.2f, %s\n",
414 gprof_call_graph_block_entry_get_name (current_parent
),
415 gprof_call_graph_block_entry_get_time_perc (current_parent
),
416 gprof_call_graph_block_entry_get_self_sec (current_parent
),
417 gprof_call_graph_block_entry_get_child_sec (current_parent
),
418 gprof_call_graph_block_entry_get_calls (current_parent
));
420 current_parent
= gprof_call_graph_block_entry_get_next (current_parent_iter
,
421 ¤t_parent_iter
);
423 fprintf (stream
, "\n---\n\n");
424 current_block
= g_list_next (current_block
);