static void print_version (FILE *stream, struct argp_state *state);
void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
-#define OPT_COUNT_TOTAL 1
-#define OPT_TEST 2
+#define OPT_TEST 1
/* Definitions of arguments for argp functions. */
static const struct argp_option options[] =
{
{ NULL, 0, NULL, 0, N_("Output selection:") },
- { "count-total", OPT_COUNT_TOTAL, NULL, 0,
- N_("print number of invocations for each function") },
+ { "flat-profile", 'p', NULL, 0,
+ N_("generate flat profile with counts and ticks") },
+ { "graph", 'q', NULL, 0, N_("generate call graph") },
+
{ "test", OPT_TEST, NULL, OPTION_HIDDEN, NULL },
{ NULL, 0, NULL, 0, NULL }
};
static enum
{
NONE = 0,
- COUNT_TOTAL
+ FLAT_MODE = 1 << 0,
+ CALL_GRAPH_MODE = 1 << 1,
+
+ DEFAULT_MODE = FLAT_MODE | CALL_GRAPH_MODE
} mode;
/* If nonzero the total number of invocations of a function is emitted. */
/* Strcuture describing calls. */
struct here_fromstruct
- {
- struct here_cg_arc_record volatile *here;
- uint16_t link;
- };
+{
+ struct here_cg_arc_record volatile *here;
+ uint16_t link;
+};
/* We define a special type to address the elements of the arc table.
This is basically the `gmon_cg_arc_record' format but it includes
the room for the tag and it uses real types. */
struct here_cg_arc_record
- {
- uintptr_t from_pc;
- uintptr_t self_pc;
- uint32_t count;
- } __attribute__ ((packed));
+{
+ uintptr_t from_pc;
+ uintptr_t self_pc;
+ uint32_t count;
+} __attribute__ ((packed));
+
+
+struct known_symbol;
+struct arc_list
+{
+ size_t idx;
+ uintmax_t count;
+
+ struct arc_list *next;
+};
+
+static struct obstack ob_list;
struct known_symbol
size_t size;
uintmax_t ticks;
+ uintmax_t calls;
+
+ struct arc_list *froms;
+ struct arc_list *tos;
};
off_t size;
char *hist;
+ struct gmon_hist_hdr *hist_hdr;
uint16_t *kcount;
uint32_t narcs; /* Number of arcs in toset. */
struct here_cg_arc_record *data;
static struct profdata *load_profdata (const char *name, struct shobj *shobj);
static void unload_profdata (struct profdata *profdata);
static void count_total_ticks (struct shobj *shobj, struct profdata *profdata);
+static void count_calls (struct shobj *shobj, struct profdata *profdata);
static void read_symbols (struct shobj *shobj);
+static void add_arcs (struct profdata *profdata);
+static void generate_flat_profile (struct profdata *profdata);
+static void generate_call_graph (struct profdata *profdata);
int
read_symbols (shobj_handle);
+ /* Count the ticks. */
+ count_total_ticks (shobj_handle, profdata_handle);
+
+ /* Count the calls. */
+ count_calls (shobj_handle, profdata_handle);
+
+ /* Add the arc information. */
+ add_arcs (profdata_handle);
+
+ /* If no mode is specified fall back to the default mode. */
+ if (mode == NONE)
+ mode = DEFAULT_MODE;
+
/* Do some work. */
- switch (mode)
- {
- case COUNT_TOTAL:
- count_total_ticks (shobj_handle, profdata_handle);
- {
- size_t n;
- for (n = 0; n < symidx; ++n)
- if (sortsym[n]->ticks != 0)
- printf ("Name: %-30s, Ticks: %" PRIdMAX "\n", sortsym[n]->name,
- sortsym[n]->ticks);
- printf ("Total ticks: %" PRIdMAX "\n", total_ticks);
- }
- break;
- case NONE:
- /* Do nothing. */
- break;
- default:
- assert (! "Internal error");
- }
+ if (mode & FLAT_MODE)
+ generate_flat_profile (profdata_handle);
+
+ if (mode & CALL_GRAPH_MODE)
+ generate_call_graph (profdata_handle);
/* Free the resources. */
unload_shobj (shobj_handle);
{
switch (key)
{
- case OPT_COUNT_TOTAL:
- mode = COUNT_TOTAL;
+ case 'p':
+ mode |= FLAT_MODE;
+ break;
+ case 'q':
+ mode |= CALL_GRAPH_MODE;
break;
case OPT_TEST:
do_test = 1;
else
log_hashfraction = -1;
if (do_test)
- printf ("hashfraction = %d\ndivider = %d\n",
+ printf ("hashfraction = %d\ndivider = %Zu\n",
result->hashfraction,
result->hashfraction * sizeof (struct here_fromstruct));
result->tossize = textsize / HASHFRACTION;
/* Pointer to data after the header. */
result->hist = (char *) ((struct gmon_hdr *) addr + 1);
+ result->hist_hdr = (struct gmon_hist_hdr *) ((char *) result->hist
+ + sizeof (uint32_t));
result->kcount = (uint16_t *) ((char *) result->hist + sizeof (uint32_t)
+ sizeof (struct gmon_hist_hdr));
*(char **) hist_hdr.high_pc = (char *) shobj->highpc - shobj->map->l_addr;
if (do_test)
printf ("low_pc = %p\nhigh_pc = %p\n",
- hist_hdr.low_pc, hist_hdr.high_pc);
+ *(char **) hist_hdr.low_pc, *(char **) hist_hdr.high_pc);
*(int32_t *) hist_hdr.hist_size = shobj->kcountsize / sizeof (HISTCOUNTER);
*(int32_t *) hist_hdr.prof_rate = __profile_frequency ();
strncpy (hist_hdr.dimen, "seconds", sizeof (hist_hdr.dimen));
/* Test whether the header of the profiling data is ok. */
if (memcmp (addr, &gmon_hdr, sizeof (struct gmon_hdr)) != 0
|| *(uint32_t *) result->hist != GMON_TAG_TIME_HIST
- || memcmp (result->hist + sizeof (uint32_t), &hist_hdr,
+ || memcmp (result->hist_hdr, &hist_hdr,
sizeof (struct gmon_hist_hdr)) != 0
|| narcsp[-1] != GMON_TAG_CG_ARC)
{
}
+static size_t
+find_symbol (uintptr_t addr)
+{
+ size_t sidx = 0;
+
+ while (sidx < symidx)
+ {
+ uintptr_t start = sortsym[sidx]->addr;
+ uintptr_t end = start + sortsym[sidx]->size;
+
+ if (addr >= start && addr < end)
+ return sidx;
+
+ if (addr < start)
+ break;
+
+ ++sidx;
+ }
+
+ return (size_t) -1l;
+}
+
+
+static void
+count_calls (struct shobj *shobj, struct profdata *profdata)
+{
+ struct here_cg_arc_record *data = profdata->data;
+ uint32_t narcs = profdata->narcs;
+ uint32_t cnt;
+
+ for (cnt = 0; cnt < narcs; ++cnt)
+ {
+ uintptr_t here = data[cnt].self_pc;
+ size_t symbol_idx;
+
+ /* Find the symbol for this address. */
+ symbol_idx = find_symbol (here);
+ if (symbol_idx != (size_t) -1l)
+ sortsym[symbol_idx]->calls += data[cnt].count;
+ }
+}
+
+
static int
symorder (const void *o1, const void *o2)
{
- const struct known_symbol *p1 = (struct known_symbol *) o1;
- const struct known_symbol *p2 = (struct known_symbol *) o2;
+ const struct known_symbol *p1 = (const struct known_symbol *) o1;
+ const struct known_symbol *p2 = (const struct known_symbol *) o2;
return p1->addr - p2->addr;
}
#define obstack_chunk_free free
obstack_init (&shobj->ob_str);
obstack_init (&shobj->ob_sym);
+ obstack_init (&ob_list);
/* Process the symbols. */
if (shobj->symtab)
|| ELFW(ST_TYPE) (sym->st_info) == STT_NOTYPE)
&& sym->st_size != 0)
{
+ struct known_symbol **existp;
struct known_symbol *newsym
= (struct known_symbol *) obstack_alloc (&shobj->ob_sym,
sizeof (*newsym));
newsym->addr = sym->st_value;
newsym->size = sym->st_size;
newsym->ticks = 0;
-
- tsearch (newsym, &symroot, symorder);
- ++n;
+ newsym->calls = 0;
+
+ existp = tfind (newsym, &symroot, symorder);
+ if (existp == NULL)
+ {
+ /* New function. */
+ tsearch (newsym, &symroot, symorder);
+ ++n;
+ }
+ else
+ {
+ /* The function is already defined. See whether we have
+ a better name here. */
+ if ((*existp)->name[0] == '_' && newsym->name[0] != '_')
+ *existp = newsym;
+ else
+ /* We don't need the allocated memory. */
+ obstack_free (&shobj->ob_sym, newsym);
+ }
}
}
else
dynamic symbol table!! */
while ((void *) symtab < (void *) strtab)
{
- if (/*(ELFW(ST_TYPE)(symtab->st_info) == STT_FUNC
- || ELFW(ST_TYPE)(symtab->st_info) == STT_NOTYPE)
- &&*/ symtab->st_size != 0)
+ if ((ELFW(ST_TYPE)(symtab->st_info) == STT_FUNC
+ || ELFW(ST_TYPE)(symtab->st_info) == STT_NOTYPE)
+ && symtab->st_size != 0)
{
struct known_symbol *newsym;
+ struct known_symbol **existp;
newsym =
(struct known_symbol *) obstack_alloc (&shobj->ob_sym,
newsym->addr = symtab->st_value;
newsym->size = symtab->st_size;
newsym->ticks = 0;
-
- tsearch (newsym, &symroot, symorder);
- ++n;
+ newsym->froms = NULL;
+ newsym->tos = NULL;
+
+ existp = tfind (newsym, &symroot, symorder);
+ if (existp == NULL)
+ {
+ /* New function. */
+ tsearch (newsym, &symroot, symorder);
+ ++n;
+ }
+ else
+ {
+ /* The function is already defined. See whether we have
+ a better name here. */
+ if ((*existp)->name[0] == '_' && newsym->name[0] != '_')
+ *existp = newsym;
+ else
+ /* We don't need the allocated memory. */
+ obstack_free (&shobj->ob_sym, newsym);
+ }
}
}
twalk (symroot, printsym);
}
+
+
+static void
+add_arcs (struct profdata *profdata)
+{
+ uint32_t narcs = profdata->narcs;
+ struct here_cg_arc_record *data = profdata->data;
+ uint32_t cnt;
+
+ for (cnt = 0; cnt < narcs; ++cnt)
+ {
+ /* First add the incoming arc. */
+ size_t sym_idx = find_symbol (data[cnt].self_pc);
+
+ if (sym_idx != (size_t) -1l)
+ {
+ struct known_symbol *sym = sortsym[sym_idx];
+ struct arc_list *runp = sym->froms;
+
+ while (runp != NULL
+ && ((data[cnt].from_pc == 0 && runp->idx != (size_t) -1l)
+ || (data[cnt].from_pc != 0
+ && (runp->idx == (size_t) -1l
+ || data[cnt].from_pc < sortsym[runp->idx]->addr
+ || (data[cnt].from_pc
+ >= (sortsym[runp->idx]->addr
+ + sortsym[runp->idx]->size))))))
+ runp = runp->next;
+
+ if (runp == NULL)
+ {
+ /* We need a new entry. */
+ struct arc_list *newp = (struct arc_list *)
+ obstack_alloc (&ob_list, sizeof (struct arc_list));
+
+ if (data[cnt].from_pc == 0)
+ newp->idx = (size_t) -1l;
+ else
+ newp->idx = find_symbol (data[cnt].from_pc);
+ newp->count = data[cnt].count;
+ newp->next = sym->froms;
+ sym->froms = newp;
+ }
+ else
+ /* Increment the counter for the found entry. */
+ runp->count += data[cnt].count;
+ }
+
+ /* Now add it to the appropriate outgoing list. */
+ sym_idx = find_symbol (data[cnt].from_pc);
+ if (sym_idx != (size_t) -1l)
+ {
+ struct known_symbol *sym = sortsym[sym_idx];
+ struct arc_list *runp = sym->tos;
+
+ while (runp != NULL
+ && (runp->idx == (size_t) -1l
+ || data[cnt].self_pc < sortsym[runp->idx]->addr
+ || data[cnt].self_pc >= (sortsym[runp->idx]->addr
+ + sortsym[runp->idx]->size)))
+ runp = runp->next;
+
+ if (runp == NULL)
+ {
+ /* We need a new entry. */
+ struct arc_list *newp = (struct arc_list *)
+ obstack_alloc (&ob_list, sizeof (struct arc_list));
+
+ newp->idx = find_symbol (data[cnt].self_pc);
+ newp->count = data[cnt].count;
+ newp->next = sym->tos;
+ sym->tos = newp;
+ }
+ else
+ /* Increment the counter for the found entry. */
+ runp->count += data[cnt].count;
+ }
+ }
+}
+
+
+static int
+countorder (const void *p1, const void *p2)
+{
+ struct known_symbol *s1 = (struct known_symbol *) p1;
+ struct known_symbol *s2 = (struct known_symbol *) p2;
+
+ if (s1->ticks != s2->ticks)
+ return (int) (s2->ticks - s1->ticks);
+
+ if (s1->calls != s2->calls)
+ return (int) (s2->calls - s1->calls);
+
+ return strcmp (s1->name, s2->name);
+}
+
+
+static double tick_unit;
+static uintmax_t cumu_ticks;
+
+static void
+printflat (const void *node, VISIT value, int level)
+{
+ if (value == leaf || value == postorder)
+ {
+ struct known_symbol *s = *(struct known_symbol **) node;
+
+ cumu_ticks += s->ticks;
+
+ printf ("%6.2f%10.2f%9.2f%9" PRIdMAX "%9.2f %s\n",
+ total_ticks ? (100.0 * s->ticks) / total_ticks : 0.0,
+ tick_unit * cumu_ticks,
+ tick_unit * s->ticks,
+ s->calls,
+ s->calls ? (s->ticks * 1000000) * tick_unit / s->calls : 0,
+ /* FIXME: don't know about called functions. */
+ s->name);
+ }
+}
+
+
+/* ARGUSED */
+static void
+freenoop (void *p)
+{
+}
+
+
+static void
+generate_flat_profile (struct profdata *profdata)
+{
+ size_t n;
+ void *data = NULL;
+
+ tick_unit = 1.0 / *(uint32_t *) profdata->hist_hdr->prof_rate;
+
+ printf ("Flat profile:\n\n"
+ "Each sample counts as %g %s.\n",
+ tick_unit, profdata->hist_hdr->dimen);
+ fputs (" % cumulative self self total\n"
+ " time seconds seconds calls us/call us/call name\n",
+ stdout);
+
+ for (n = 0; n < symidx; ++n)
+ if (sortsym[n]->calls != 0 || sortsym[n]->ticks != 0)
+ tsearch (sortsym[n], &data, countorder);
+
+ twalk (data, printflat);
+
+ tdestroy (data, freenoop);
+}
+
+
+static void
+generate_call_graph (struct profdata *profdata)
+{
+ size_t cnt;
+
+ puts ("\nindex % time self children called name\n");
+
+ for (cnt = 0; cnt < symidx; ++cnt)
+ if (sortsym[cnt]->froms != NULL || sortsym[cnt]->tos != NULL)
+ {
+ struct arc_list *runp;
+ size_t n;
+
+ /* First print the from-information. */
+ runp = sortsym[cnt]->froms;
+ while (runp != NULL)
+ {
+ printf (" %8.2f%8.2f%9" PRIdMAX "/%-9" PRIdMAX " %s",
+ (runp->idx != (size_t) -1l
+ ? sortsym[runp->idx]->ticks * tick_unit : 0.0),
+ 0.0, /* FIXME: what's time for the childern, recursive */
+ runp->count, sortsym[cnt]->calls,
+ (runp->idx != (size_t) -1l ?
+ sortsym[runp->idx]->name : "<UNKNOWN>"));
+
+ if (runp->idx != (size_t) -1l)
+ printf (" [%Zd]", runp->idx);
+ putchar_unlocked ('\n');
+
+ runp = runp->next;
+ }
+
+ /* Info abount the function itself. */
+ n = printf ("[%Zu]", cnt);
+ printf ("%*s%5.1f%8.2f%8.2f%9" PRIdMAX " %s [%Zd]\n",
+ 7 - n, " ",
+ total_ticks ? (100.0 * sortsym[cnt]->ticks) / total_ticks : 0,
+ sortsym[cnt]->ticks * tick_unit,
+ 0.0, /* FIXME: what's time for the childern, recursive */
+ sortsym[cnt]->calls,
+ sortsym[cnt]->name, cnt);
+
+ /* Info about the functions this function calls. */
+ runp = sortsym[cnt]->tos;
+ while (runp != NULL)
+ {
+ printf (" %8.2f%8.2f%9" PRIdMAX "/",
+ (runp->idx != (size_t) -1l
+ ? sortsym[runp->idx]->ticks * tick_unit : 0.0),
+ 0.0, /* FIXME: what's time for the childern, recursive */
+ runp->count);
+
+ if (runp->idx != (size_t) -1l)
+ printf ("%-9" PRIdMAX " %s [%Zd]\n",
+ sortsym[runp->idx]->calls,
+ sortsym[runp->idx]->name,
+ runp->idx);
+ else
+ fputs ("??? <UNKNOWN>\n\n", stdout);
+
+ runp = runp->next;
+ }
+
+ fputs ("-----------------------------------------------\n", stdout);
+ }
+}