#include <argp.h>
#include <dlfcn.h>
#include <elf.h>
-#include <endian.h>
#include <error.h>
#include <fcntl.h>
#include <inttypes.h>
#include <libintl.h>
-#include <link.h>
#include <locale.h>
#include <obstack.h>
#include <search.h>
-#include <stab.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <elf/ldsodefs.h>
#include <sys/gmon.h>
#include <sys/gmon_out.h>
#include <sys/mman.h>
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));
-
-/* Information about the stab debugging info. This should be in a
- head but it is not. */
-#define STRDXOFF (0)
-#define TYPEOFF (4)
-#define OTHEROFF (5)
-#define DESCOFF (6)
-#define VALOFF (8)
-#define STABSIZE (12)
+{
+ 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;
};
const char *name; /* User-provided name. */
struct link_map *map;
- const char *strtab; /* String table of shared object. */
+ const char *dynstrtab; /* Dynamic string table of shared object. */
const char *soname; /* Soname of shared object. */
uintptr_t lowpc;
unsigned int hashfraction;
int s_scale;
- void *stab_map;
- size_t stab_mapsize;
- const char *stab;
- size_t stab_size;
- const char *stabstr;
- size_t stabstr_size;
+ void *symbol_map;
+ size_t symbol_mapsize;
+ const ElfW(Sym) *symtab;
+ size_t symtab_size;
+ const char *strtab;
struct obstack ob_str;
struct obstack ob_sym;
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;
size_t pagesize = getpagesize ();
const char *shstrtab;
int idx;
- ElfW(Shdr) *stab_entry;
- ElfW(Shdr) *stabstr_entry;
+ ElfW(Shdr) *symtab_entry;
/* Since we use dlopen() we must be prepared to work around the sometimes
strange lookup rules for the shared objects. If we have a file foo.so
for (ph = map->l_phdr; ph < &map->l_phdr[map->l_phnum]; ++ph)
if (ph->p_type == PT_LOAD && (ph->p_flags & PF_X))
{
- ElfW(Addr) start = (ph->p_vaddr & ~(_dl_pagesize - 1));
- ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + _dl_pagesize - 1)
- & ~(_dl_pagesize - 1));
+ ElfW(Addr) start = (ph->p_vaddr & ~(pagesize - 1));
+ ElfW(Addr) end = ((ph->p_vaddr + ph->p_memsz + pagesize - 1)
+ & ~(pagesize - 1));
if (start < mapstart)
mapstart = start;
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;
if (do_test)
printf ("s_scale: %d\n", result->s_scale);
- /* Determine the string table. */
+ /* Determine the dynamic string table. */
if (map->l_info[DT_STRTAB] == NULL)
- result->strtab = NULL;
+ result->dynstrtab = NULL;
else
- result->strtab = (const char *) (map->l_addr
- + map->l_info[DT_STRTAB]->d_un.d_ptr);
+ result->dynstrtab = (const char *) (map->l_addr
+ + map->l_info[DT_STRTAB]->d_un.d_ptr);
if (do_test)
- printf ("string table: %p\n", result->strtab);
+ printf ("string table: %p\n", result->dynstrtab);
/* Determine the soname. */
if (map->l_info[DT_SONAME] == NULL)
result->soname = NULL;
else
- result->soname = result->strtab + map->l_info[DT_SONAME]->d_un.d_val;
+ result->soname = result->dynstrtab + map->l_info[DT_SONAME]->d_un.d_val;
if (do_test)
printf ("soname: %s\n", result->soname);
- /* Now the hard part, we have to load the debugging data. For now
- we support stabs only.
+ /* Now we have to load the symbol table.
First load the section header table. */
ehdr = (ElfW(Ehdr) *) map->l_addr;
error (EXIT_FAILURE, errno, _("Reopening shared object `%s' failed"));
/* Now map the section header. */
- ptr = mmap (NULL, (ehdr->e_phnum * sizeof (ElfW(Shdr))
+ ptr = mmap (NULL, (ehdr->e_shnum * sizeof (ElfW(Shdr))
+ (ehdr->e_shoff & (pagesize - 1))), PROT_READ,
MAP_SHARED|MAP_FILE, fd, ehdr->e_shoff & ~(pagesize - 1));
if (ptr == MAP_FAILED)
shstrtab = ((const char *) ptr
+ (shdr[ehdr->e_shstrndx].sh_offset & (pagesize - 1)));
- /* Search for the ".stab" and ".stabstr" section (and ".rel.stab" ?). */
- stab_entry = NULL;
- stabstr_entry = NULL;
+ /* Search for the ".symtab" section. */
+ symtab_entry = NULL;
for (idx = 0; idx < ehdr->e_shnum; ++idx)
- /* We only have to look for sections which are not loaded. */
- if (shdr[idx].sh_addr == 0)
+ if (shdr[idx].sh_type == SHT_SYMTAB
+ && strcmp (shstrtab + shdr[idx].sh_name, ".symtab") == 0)
{
- if (strcmp (shstrtab + shdr[idx].sh_name, ".stab") == 0)
- stab_entry = &shdr[idx];
- else if (strcmp (shstrtab + shdr[idx].sh_name, ".stabstr") == 0)
- stabstr_entry = &shdr[idx];
+ symtab_entry = &shdr[idx];
+ break;
}
- /* We don't need the sectin header string table anymore. */
+ /* We don't need the section header string table anymore. */
munmap (ptr, (shdr[ehdr->e_shstrndx].sh_size
+ (shdr[ehdr->e_shstrndx].sh_offset & (pagesize - 1))));
- if (stab_entry == NULL || stabstr_entry == NULL)
+ if (symtab_entry == NULL)
{
fprintf (stderr, _("\
*** The file `%s' is stripped: no detailed analysis possible\n"),
name);
- result->stab = NULL;
- result->stabstr = NULL;
+ result->symtab = NULL;
+ result->strtab = NULL;
}
else
{
- if (stab_entry->sh_offset + stab_entry->sh_size
- != stabstr_entry->sh_offset)
- abort ();
- if (stab_entry->sh_size % STABSIZE != 0)
- abort ();
-
- result->stab_map = mmap (NULL, (stab_entry->sh_size
- + stabstr_entry->sh_size
- + (stab_entry->sh_offset
- & (pagesize - 1))),
- PROT_READ, MAP_SHARED|MAP_FILE, fd,
- stab_entry->sh_offset & ~(pagesize - 1));
- if (result->stab_map == NULL)
- error (EXIT_FAILURE, errno, _("failed to load stab data:"));
-
- result->stab = ((const char *) result->stab_map
- + (stab_entry->sh_offset & (pagesize - 1)));
- result->stab_size = stab_entry->sh_size;
- result->stabstr = result->stab + stab_entry->sh_size;
- result->stabstr_size = stabstr_entry->sh_size;
- result->stab_mapsize = (stab_entry->sh_size + stabstr_entry->sh_size
- + (stab_entry->sh_offset & (pagesize - 1)));
+ ElfW(Off) min_offset, max_offset;
+ ElfW(Shdr) *strtab_entry;
+
+ strtab_entry = &shdr[symtab_entry->sh_link];
+
+ /* Find the minimum and maximum offsets that include both the symbol
+ table and the string table. */
+ if (symtab_entry->sh_offset < strtab_entry->sh_offset)
+ {
+ min_offset = symtab_entry->sh_offset & ~(pagesize - 1);
+ max_offset = strtab_entry->sh_offset + strtab_entry->sh_size;
+ }
+ else
+ {
+ min_offset = strtab_entry->sh_offset & ~(pagesize - 1);
+ max_offset = symtab_entry->sh_offset + symtab_entry->sh_size;
+ }
+
+ result->symbol_map = mmap (NULL, max_offset - min_offset,
+ PROT_READ, MAP_SHARED|MAP_FILE, fd,
+ min_offset);
+ if (result->symbol_map == NULL)
+ error (EXIT_FAILURE, errno, _("failed to load symbol data"));
+
+ result->symtab
+ = (const ElfW(Sym) *) ((const char *) result->symbol_map
+ + (symtab_entry->sh_offset - min_offset));
+ result->symtab_size = symtab_entry->sh_size;
+ result->strtab = ((const char *) result->symbol_map
+ + (strtab_entry->sh_offset - min_offset));
+ result->symbol_mapsize = max_offset - min_offset;
}
- /* Now we also don't need the sectio header table anymore. */
+ /* Now we also don't need the section header table anymore. */
munmap ((char *) shdr - (ehdr->e_shoff & (pagesize - 1)),
(ehdr->e_phnum * sizeof (ElfW(Shdr))
+ (ehdr->e_shoff & (pagesize - 1))));
static void
unload_shobj (struct shobj *shobj)
{
- munmap (shobj->stab_map, shobj->stab_mapsize);
+ munmap (shobj->symbol_map, shobj->symbol_mapsize);
dlclose (shobj->map);
}
/* 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;
}
printsym (const void *node, VISIT value, int level)
{
if (value == leaf || value == postorder)
- sortsym[symidx++] = *(const struct known_symbol **) node;
+ sortsym[symidx++] = *(struct known_symbol **) node;
}
{
void *load_addr = (void *) shobj->map->l_addr;
int n = 0;
- int idx;
- const char *last_name = NULL;
- uintptr_t last_addr = 0;
/* Initialize the obstacks. */
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
obstack_init (&shobj->ob_str);
obstack_init (&shobj->ob_sym);
+ obstack_init (&ob_list);
- /* Process the stabs. */
- for (idx = 0; idx < shobj->stab_size; idx += 12)
- if (*(shobj->stab + idx + TYPEOFF) == N_FUN)
- {
- const char *str = (shobj->stabstr
- + *((uint32_t *) (shobj->stab + idx + STRDXOFF)));
-
- if (*str != '\0')
- {
- last_name = str;
- last_addr = *((uint32_t *) (shobj->stab + idx + VALOFF));
- }
- else
+ /* Process the symbols. */
+ if (shobj->symtab)
+ {
+ const ElfW(Sym) *sym = shobj->symtab;
+ const ElfW(Sym) *sym_end
+ = (const ElfW(Sym) *) ((const char *) sym + shobj->symtab_size);
+ for (; sym < sym_end; sym++)
+ if ((ELFW(ST_TYPE) (sym->st_info) == STT_FUNC
+ || ELFW(ST_TYPE) (sym->st_info) == STT_NOTYPE)
+ && sym->st_size != 0)
{
- const char *endp;
- char *name0;
- struct known_symbol *newsym;
-
- if (last_name == NULL)
- abort ();
-
- endp = strchr (last_name, ':');
-
- name0 = (char *) obstack_copy0 (&shobj->ob_str, last_name,
- endp - last_name);
- if (name0 != NULL)
- newsym =
- (struct known_symbol *) obstack_alloc (&shobj->ob_sym,
+ struct known_symbol **existp;
+ struct known_symbol *newsym
+ = (struct known_symbol *) obstack_alloc (&shobj->ob_sym,
sizeof (*newsym));
- else
- /* Keep the stupid compiler happy. */
- newsym = NULL;
- if (name0 == NULL || newsym == NULL)
+ if (newsym == NULL)
error (EXIT_FAILURE, errno, _("cannot allocate symbol data"));
- newsym->name = name0;
- newsym->addr = last_addr;
- newsym->size = *((uint32_t *) (shobj->stab + idx + VALOFF));
+ newsym->name = &shobj->strtab[sym->st_name];
+ newsym->addr = sym->st_value;
+ newsym->size = sym->st_size;
newsym->ticks = 0;
-
- tsearch (newsym, &symroot, symorder);
- ++n;
-
- last_name = NULL;
- last_addr = 0;
+ 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);
+ }
}
- }
-
- if (shobj->stab == NULL)
+ }
+ else
{
/* Blarg, the binary is stripped. We have to rely on the
information contained in the dynamic section of the object. */
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);
+ }
+}