Add the -m option to pmcstat.

This option prints the list of sampled PCs along with the function name,
the start and end addresses of this where their live within.

Reviewed by:	jkoshy
Tested by:	gnn
Sponsored by:	Nokia
This commit is contained in:
Attilio Rao 2008-11-25 23:24:29 +00:00
parent 38cc5da78e
commit c0252222a2
4 changed files with 69 additions and 11 deletions

View File

@ -48,6 +48,7 @@
.Op Fl d .Op Fl d
.Op Fl g .Op Fl g
.Op Fl k Ar kerneldir .Op Fl k Ar kerneldir
.Op Fl m Ar pathname
.Op Fl n Ar rate .Op Fl n Ar rate
.Op Fl o Ar outputfile .Op Fl o Ar outputfile
.Op Fl p Ar event-spec .Op Fl p Ar event-spec
@ -232,6 +233,19 @@ This directory specifies where
should look for the kernel and its modules. should look for the kernel and its modules.
The default is The default is
.Pa /boot/kernel . .Pa /boot/kernel .
.It Fl m Ar pathname
Print the sampled PCs with the name, the start and ending addresses
of the function within they live.
The
.Ar pathname
argument is mandatory and indicates where informations will be stored.
If argument
.Ar pathname
is a
.Dq Li -
this information is sent to the output file specified by the
.Fl o
option.
.It Fl n Ar rate .It Fl n Ar rate
Set the default sampling rate for subsequent sampling mode Set the default sampling rate for subsequent sampling mode
PMCs specified on the command line. PMCs specified on the command line.

View File

@ -594,7 +594,7 @@ main(int argc, char **argv)
} }
while ((option = getopt(argc, argv, while ((option = getopt(argc, argv,
"CD:EG:M:NO:P:R:S:Wc:dgk:n:o:p:qr:s:t:vw:z:")) != -1) "CD:EG:M:NO:P:R:S:Wc:dgk:m:n:o:p:qr:s:t:vw:z:")) != -1)
switch (option) { switch (option) {
case 'C': /* cumulative values */ case 'C': /* cumulative values */
use_cumulative_counts = !use_cumulative_counts; use_cumulative_counts = !use_cumulative_counts;
@ -644,6 +644,11 @@ main(int argc, char **argv)
args.pa_flags |= FLAG_HAS_KERNELPATH; args.pa_flags |= FLAG_HAS_KERNELPATH;
break; break;
case 'm':
args.pa_flags |= FLAG_WANTS_MAPPINGS;
graphfilename = optarg;
break;
case 'E': /* log process exit */ case 'E': /* log process exit */
do_logprocexit = !do_logprocexit; do_logprocexit = !do_logprocexit;
args.pa_required |= (FLAG_HAS_PROCESS_PMCS | args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
@ -827,7 +832,8 @@ main(int argc, char **argv)
if (argc) /* command line present */ if (argc) /* command line present */
args.pa_flags |= FLAG_HAS_COMMANDLINE; args.pa_flags |= FLAG_HAS_COMMANDLINE;
if (args.pa_flags & (FLAG_DO_GPROF | FLAG_DO_CALLGRAPHS)) if (args.pa_flags & (FLAG_DO_GPROF | FLAG_DO_CALLGRAPHS |
FLAG_WANTS_MAPPINGS))
args.pa_flags |= FLAG_DO_ANALYSIS; args.pa_flags |= FLAG_DO_ANALYSIS;
/* /*
@ -839,6 +845,16 @@ main(int argc, char **argv)
errx(EX_USAGE, "ERROR: options -O and -R are mutually " errx(EX_USAGE, "ERROR: options -O and -R are mutually "
"exclusive."); "exclusive.");
/* -m option is allowed with -R only. */
if (args.pa_flags & FLAG_WANTS_MAPPINGS && args.pa_inputpath == NULL)
errx(EX_USAGE, "ERROR: option -m requires an input file");
/* -m option is not allowed combined with -g or -G. */
if (args.pa_flags & FLAG_WANTS_MAPPINGS &&
args.pa_flags & (FLAG_DO_GPROF | FLAG_DO_CALLGRAPHS))
errx(EX_USAGE, "ERROR: option -m and -g | -G are mutually "
"exclusive");
if (args.pa_flags & FLAG_READ_LOGFILE) { if (args.pa_flags & FLAG_READ_LOGFILE) {
errmsg = NULL; errmsg = NULL;
if (args.pa_flags & FLAG_HAS_COMMANDLINE) if (args.pa_flags & FLAG_HAS_COMMANDLINE)
@ -980,6 +996,12 @@ main(int argc, char **argv)
"for writing", graphfilename); "for writing", graphfilename);
} }
} }
if (args.pa_flags & FLAG_WANTS_MAPPINGS) {
args.pa_graphfile = fopen(graphfilename, "w");
if (args.pa_graphfile == NULL)
err(EX_OSERR, "ERROR: cannot open \"%s\" for writing",
graphfilename);
}
/* if we've been asked to process a log file, do that and exit */ /* if we've been asked to process a log file, do that and exit */
if (args.pa_flags & FLAG_READ_LOGFILE) { if (args.pa_flags & FLAG_READ_LOGFILE) {

View File

@ -49,6 +49,7 @@
#define FLAG_DO_PRINT 0x00002000 /* -o */ #define FLAG_DO_PRINT 0x00002000 /* -o */
#define FLAG_DO_CALLGRAPHS 0x00004000 /* -G */ #define FLAG_DO_CALLGRAPHS 0x00004000 /* -G */
#define FLAG_DO_ANALYSIS 0x00008000 /* -g or -G */ #define FLAG_DO_ANALYSIS 0x00008000 /* -g or -G */
#define FLAG_WANTS_MAPPINGS 0x00010000 /* -m */
#define DEFAULT_SAMPLE_COUNT 65536 #define DEFAULT_SAMPLE_COUNT 65536
#define DEFAULT_WAIT_INTERVAL 5.0 #define DEFAULT_WAIT_INTERVAL 5.0

View File

@ -1969,9 +1969,10 @@ static int
pmcstat_analyze_log(struct pmcstat_args *a) pmcstat_analyze_log(struct pmcstat_args *a)
{ {
uint32_t cpu, cpuflags; uint32_t cpu, cpuflags;
uintfptr_t pc; uintfptr_t pc, newpc;
pid_t pid; pid_t pid;
struct pmcstat_image *image; struct pmcstat_image *image;
struct pmcstat_symbol *sym;
struct pmcstat_process *pp, *ppnew; struct pmcstat_process *pp, *ppnew;
struct pmcstat_pcmap *ppm, *ppmtmp; struct pmcstat_pcmap *ppm, *ppmtmp;
struct pmclog_ev ev; struct pmclog_ev ev;
@ -2085,21 +2086,41 @@ pmcstat_analyze_log(struct pmcstat_args *a)
pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid, pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid,
PMCSTAT_ALLOCATE); PMCSTAT_ALLOCATE);
pmcstat_record_callchain(pp, if ((a->pa_flags & FLAG_WANTS_MAPPINGS) == 0)
ev.pl_u.pl_cc.pl_pmcid, ev.pl_u.pl_cc.pl_npc, pmcstat_record_callchain(pp,
ev.pl_u.pl_cc.pl_pc, ev.pl_u.pl_cc.pl_pmcid,
PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), a); ev.pl_u.pl_cc.pl_npc, ev.pl_u.pl_cc.pl_pc,
PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), a);
if ((a->pa_flags & FLAG_DO_GPROF) == 0) if ((a->pa_flags &
(FLAG_DO_GPROF | FLAG_WANTS_MAPPINGS)) == 0)
break; break;
pc = ev.pl_u.pl_cc.pl_pc[0]; pc = ev.pl_u.pl_cc.pl_pc[0];
if ((ppm = pmcstat_process_find_map(pp, pc)) == NULL && if (PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags) == 0)
(ppm = pmcstat_process_find_map(pmcstat_kernproc, pp = pmcstat_kernproc;
pc)) == NULL) { /* unknown offset */ ppm = pmcstat_process_find_map(pp, pc);
if (ppm == NULL) {
/* Unknown offset. */
pmcstat_stats.ps_samples_unknown_offset++; pmcstat_stats.ps_samples_unknown_offset++;
break; break;
} }
if (a->pa_flags & FLAG_WANTS_MAPPINGS) {
image = ppm->ppm_image;
newpc = pc - (ppm->ppm_lowpc +
(image->pi_vaddr - image->pi_start));
sym = pmcstat_symbol_search(image, newpc);
if (sym == NULL)
break;
fprintf(a->pa_graphfile, "%p %s 0x%jx 0x%jx\n",
(void *)pc,
pmcstat_string_unintern(sym->ps_name),
(uintmax_t)(sym->ps_start +
image->pi_vaddr), (uintmax_t)(sym->ps_end +
image->pi_vaddr));
break;
}
pmcstat_image_increment_bucket(ppm, pc, pmcstat_image_increment_bucket(ppm, pc,
ev.pl_u.pl_cc.pl_pmcid, a); ev.pl_u.pl_cc.pl_pmcid, a);