Index: usr.sbin/Makefile =================================================================== --- usr.sbin/Makefile (revision 185199) +++ usr.sbin/Makefile (working copy) @@ -119,6 +119,7 @@ pciconf \ periodic \ ${_pkg_install} \ + ${_pmcannotate} \ ${_pmccontrol} \ ${_pmcstat} \ ${_pnpinfo} \ @@ -348,6 +349,7 @@ # XXX MK_TOOLCHAIN? .if ${MK_PMC} != "no" +_pmcannotate= pmcannotate _pmccontrol= pmccontrol _pmcstat= pmcstat .endif Index: usr.sbin/pmcstat/pmcstat.8 =================================================================== --- usr.sbin/pmcstat/pmcstat.8 (revision 185199) +++ usr.sbin/pmcstat/pmcstat.8 (working copy) @@ -48,6 +48,7 @@ .Op Fl d .Op Fl g .Op Fl k Ar kerneldir +.Op Fl m Ar pathname .Op Fl n Ar rate .Op Fl o Ar outputfile .Op Fl p Ar event-spec @@ -232,6 +233,19 @@ should look for the kernel and its modules. The default is .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 Set the default sampling rate for subsequent sampling mode PMCs specified on the command line. Index: usr.sbin/pmcstat/pmcstat.h =================================================================== --- usr.sbin/pmcstat/pmcstat.h (revision 185199) +++ usr.sbin/pmcstat/pmcstat.h (working copy) @@ -49,6 +49,7 @@ #define FLAG_DO_PRINT 0x00002000 /* -o */ #define FLAG_DO_CALLGRAPHS 0x00004000 /* -G */ #define FLAG_DO_ANALYSIS 0x00008000 /* -g or -G */ +#define FLAG_WANTS_MAPPINGS 0x00010000 /* -m */ #define DEFAULT_SAMPLE_COUNT 65536 #define DEFAULT_WAIT_INTERVAL 5.0 Index: usr.sbin/pmcstat/pmcstat.c =================================================================== --- usr.sbin/pmcstat/pmcstat.c (revision 185199) +++ usr.sbin/pmcstat/pmcstat.c (working copy) @@ -594,7 +594,7 @@ } 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) { case 'C': /* cumulative values */ use_cumulative_counts = !use_cumulative_counts; @@ -644,6 +644,11 @@ args.pa_flags |= FLAG_HAS_KERNELPATH; break; + case 'm': + args.pa_flags |= FLAG_WANTS_MAPPINGS; + graphfilename = optarg; + break; + case 'E': /* log process exit */ do_logprocexit = !do_logprocexit; args.pa_required |= (FLAG_HAS_PROCESS_PMCS | @@ -827,7 +832,8 @@ if (argc) /* command line present */ 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; /* @@ -839,6 +845,16 @@ errx(EX_USAGE, "ERROR: options -O and -R are mutually " "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) { errmsg = NULL; if (args.pa_flags & FLAG_HAS_COMMANDLINE) @@ -980,6 +996,12 @@ "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 (args.pa_flags & FLAG_READ_LOGFILE) { Index: usr.sbin/pmcstat/pmcstat_log.c =================================================================== --- usr.sbin/pmcstat/pmcstat_log.c (revision 185199) +++ usr.sbin/pmcstat/pmcstat_log.c (working copy) @@ -1969,9 +1969,10 @@ pmcstat_analyze_log(struct pmcstat_args *a) { uint32_t cpu, cpuflags; - uintfptr_t pc; + uintfptr_t pc, newpc; pid_t pid; struct pmcstat_image *image; + struct pmcstat_symbol *sym; struct pmcstat_process *pp, *ppnew; struct pmcstat_pcmap *ppm, *ppmtmp; struct pmclog_ev ev; @@ -2085,21 +2086,41 @@ pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid, PMCSTAT_ALLOCATE); - pmcstat_record_callchain(pp, - ev.pl_u.pl_cc.pl_pmcid, 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_WANTS_MAPPINGS) == 0) + pmcstat_record_callchain(pp, + ev.pl_u.pl_cc.pl_pmcid, + 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; pc = ev.pl_u.pl_cc.pl_pc[0]; - if ((ppm = pmcstat_process_find_map(pp, pc)) == NULL && - (ppm = pmcstat_process_find_map(pmcstat_kernproc, - pc)) == NULL) { /* unknown offset */ + if (PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags) == 0) + pp = pmcstat_kernproc; + ppm = pmcstat_process_find_map(pp, pc); + if (ppm == NULL) { + + /* Unknown offset. */ pmcstat_stats.ps_samples_unknown_offset++; 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, ev.pl_u.pl_cc.pl_pmcid, a);