The kernel profiling facility does not currently provide a call graph of system startup. With the patch below, it does.
Time profiling is not the purpose of this patch and remains limited. One printf() is lost. The information it provided can be gathered at build time. A kernel that would previously have printed "No memory for profiling" will probably not boot. The address of etext is used as a compile-time constant by less-than-pure means. I don't know if it works on all platforms. Tested on -current i386. --- /dev/null Sat Sep 23 02:15:07 2006 +++ sys/conf/gen_addr_etext Sat Sep 23 00:36:45 2006 @@ -0,0 +1,19 @@ +#!/bin/sh +f=addr_etext.h +l='#define ADDR_ETEXT 0x\' +if [ $# = 0 ]; then + echo "$l" > $f + echo '0 + KERNBASE + (1<<16)' >> $f + exit 0 +fi +if [ $# = 1 ]; then + a=$(objdump -t "$1" | awk '/ etext$/{print $1}') + [ -z "$a" ] && exit 2 + b=$(sed -n 2p < $f) + [ -z "$b" ] && exit 2 + [ x"$a" = x"$b" ] && exit 1 + echo "$l" > $f + echo "$a" >> $f + exit 0 +fi +exit 2 Index: sys/kern/init_main.c =================================================================== RCS file: /cvs/src/sys/kern/init_main.c,v retrieving revision 1.130 diff -u -r1.130 init_main.c --- sys/kern/init_main.c 6 May 2006 23:02:36 -0000 1.130 +++ sys/kern/init_main.c 23 Sep 2006 02:15:08 -0000 @@ -191,6 +191,11 @@ extern void endtsleep(void *); extern void realitexpire(void *); +#ifdef GPROF + /* Initialize kernel profiling. */ + kmstartup(); +#endif + /* * Initialize the current process pointer (curproc) before * any possible traps/probes to simplify trap processing. @@ -347,6 +352,9 @@ /* Start real time and statistics clocks. */ initclocks(); +#ifdef GPROF + startprofclock(&proc0); +#endif /* Lock the kernel on behalf of proc0. */ KERNEL_PROC_LOCK(p); @@ -385,11 +393,6 @@ domaininit(); if_attachdomain(); splx(s); - -#ifdef GPROF - /* Initialize kernel profiling. */ - kmstartup(); -#endif #if !defined(NO_PROPOLICE) { Index: sys/kern/subr_prof.c =================================================================== RCS file: /cvs/src/sys/kern/subr_prof.c,v retrieving revision 1.15 diff -u -r1.15 subr_prof.c --- sys/kern/subr_prof.c 9 Dec 2005 09:09:52 -0000 1.15 +++ sys/kern/subr_prof.c 23 Sep 2006 02:15:08 -0000 @@ -45,52 +45,49 @@ #ifdef GPROF #include <sys/malloc.h> #include <sys/gmon.h> -#include <uvm/uvm_extern.h> +#include "addr_etext.h" /* * Froms is actually a bunch of unsigned shorts indexing tos */ struct gmonparam _gmonparam = { GMON_PROF_OFF }; -extern char etext[]; - +#define LOWPC ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER)) +#define HIGHPC ROUNDUP((ADDR_ETEXT), HISTFRACTION * sizeof(HISTCOUNTER)) +#define TEXTSIZE (HIGHPC - LOWPC) +#define KCOUNTSIZE (TEXTSIZE / HISTFRACTION) +#define FROMSSIZE (TEXTSIZE / HASHFRACTION) +#define TOLIM (TEXTSIZE * ARCDENSITY / 100) +#define TOLIMIT (TOLIM < MINARCS ? MINARCS : TOLIM > MAXARCS ? MAXARCS : TOLIM) +#define TOSSIZE (TOLIMIT * sizeof(struct tostruct)) +#define SIZE (KCOUNTSIZE + FROMSSIZE + TOSSIZE) void kmstartup(void) { + static char buf[round_page(SIZE)]; char *cp; struct gmonparam *p = &_gmonparam; - int size; /* * Round lowpc and highpc to multiples of the density we're using * so the rest of the scaling (here and in gprof) stays in ints. */ - p->lowpc = ROUNDDOWN(KERNBASE, HISTFRACTION * sizeof(HISTCOUNTER)); - p->highpc = ROUNDUP((u_long)etext, HISTFRACTION * sizeof(HISTCOUNTER)); - p->textsize = p->highpc - p->lowpc; - printf("Profiling kernel, textsize=%ld [%lx..%lx]\n", - p->textsize, p->lowpc, p->highpc); - p->kcountsize = p->textsize / HISTFRACTION; + p->lowpc = LOWPC; + p->highpc = HIGHPC; + p->textsize = TEXTSIZE; + p->kcountsize = KCOUNTSIZE; p->hashfraction = HASHFRACTION; - p->fromssize = p->textsize / HASHFRACTION; - p->tolimit = p->textsize * ARCDENSITY / 100; - if (p->tolimit < MINARCS) - p->tolimit = MINARCS; - else if (p->tolimit > MAXARCS) - p->tolimit = MAXARCS; - p->tossize = p->tolimit * sizeof(struct tostruct); - size = p->kcountsize + p->fromssize + p->tossize; - cp = (char *)uvm_km_zalloc(kernel_map, round_page(size)); - if (cp == 0) { - printf("No memory for profiling.\n"); - return; - } + p->fromssize = FROMSSIZE; + p->tolimit = TOLIMIT; + p->tossize = TOSSIZE; + cp = buf; p->tos = (struct tostruct *)cp; cp += p->tossize; p->kcount = (u_short *)cp; cp += p->kcountsize; p->froms = (u_short *)cp; + p->state = GMON_PROF_ON; } /* Index: usr.sbin/config/mkmakefile.c =================================================================== RCS file: /cvs/src/usr.sbin/config/mkmakefile.c,v retrieving revision 1.20 diff -u -r1.20 mkmakefile.c --- usr.sbin/config/mkmakefile.c 6 May 2006 11:31:46 -0000 1.20 +++ usr.sbin/config/mkmakefile.c 23 Sep 2006 02:15:08 -0000 @@ -167,6 +167,8 @@ #endif } +static int option_gprof = 0; + static int emitdefs(FILE *fp) { @@ -177,6 +179,8 @@ return (1); sp = ""; for (nv = options; nv != NULL; nv = nv->nv_next) { + if (!strcmp(nv->nv_name, "GPROF")) + option_gprof = 1; if (ht_lookup(defopttab, nv->nv_name) != NULL) continue; if (fprintf(fp, "%s-D%s", sp, nv->nv_name) < 0) @@ -444,9 +448,15 @@ if (fprintf(fp, "\n" "\t${SYSTEM_LD_HEAD}\n" "\t${SYSTEM_LD} swap%s.o\n" - "\t${SYSTEM_LD_TAIL}\n" + "\t${SYSTEM_LD_TAIL}\n", swname) < 0) + return (1); + if (option_gprof && fputs( + "\tif sh $S/conf/gen_addr_etext $@; then make $@;" + " else [ $$? = 1 ]; fi\n", fp) < 0) + return (1); + if (fprintf(fp, "\n" - "swap%s.o: ", swname, swname) < 0) + "swap%s.o: ", swname) < 0) return (1); if (cf->cf_root != NULL) { if (fprintf(fp, "swap%s.c\n", nm) < 0) @@ -457,6 +467,13 @@ } if (fputs("\t${NORMAL_C}\n\n", fp) < 0) return (1); + } + if (option_gprof) { + size_t len = strlen(srcdir) + 30; + char *buf = emalloc(len); + snprintf(buf, len, "sh %s/conf/gen_addr_etext", srcdir); + system(buf); + free(buf); } return (0); }