Index: lib/libpmcstat/libpmcstat.h =================================================================== --- lib/libpmcstat/libpmcstat.h +++ lib/libpmcstat/libpmcstat.h @@ -107,8 +107,9 @@ #define FLAGS_HAS_CPUMASK 0x00040000 /* -c */ #define FLAG_HAS_DURATION 0x00080000 /* -l secs */ #define FLAG_DO_WIDE_GPROF_HC 0x00100000 /* -e */ -#define FLAG_SKIP_TOP_FN_RES 0x00200000 /* -I */ +#define FLAG_SKIP_TOP_FN_RES 0x00200000 /* -A */ #define FLAG_FILTER_THREAD_ID 0x00400000 /* -L */ +#define FLAG_SHOW_OFFSET 0x00800000 /* -I */ int pa_required; /* required features */ int pa_pplugin; /* pre-processing plugin */ Index: usr.sbin/pmcstat/pmcpl_callgraph.c =================================================================== --- usr.sbin/pmcstat/pmcpl_callgraph.c +++ usr.sbin/pmcstat/pmcpl_callgraph.c @@ -152,10 +152,12 @@ * Try determine the function at this offset. If we can't * find a function round leave the `pc' value alone. */ - if ((sym = pmcstat_symbol_search(image, pc)) != NULL) - pc = sym->ps_start; - else - pmcstat_stats.ps_samples_unknown_function++; + if (!(args.pa_flags & (FLAG_SKIP_TOP_FN_RES | FLAG_SHOW_OFFSET))) { + if ((sym = pmcstat_symbol_search(image, pc)) != NULL) + pc = sym->ps_start; + else + pmcstat_stats.ps_samples_unknown_function++; + } for (hash = i = 0; i < sizeof(uintfptr_t); i++) hash += (pc >> i) & 0xFF; @@ -488,19 +490,33 @@ sym = NULL; /* Format name. */ - if (!(args.pa_flags & FLAG_SKIP_TOP_FN_RES)) - sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func); - if (sym != NULL) { - snprintf(ns, sizeof(ns), "%s", - pmcstat_string_unintern(sym->ps_name)); - } else + sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func); + if (sym == NULL) { snprintf(ns, sizeof(ns), "%p", (void *)(cg->pcg_image->pi_vaddr + cg->pcg_func)); + } else { + switch (args.pa_flags & (FLAG_SKIP_TOP_FN_RES | FLAG_SHOW_OFFSET)) { + case FLAG_SKIP_TOP_FN_RES | FLAG_SHOW_OFFSET: + case FLAG_SKIP_TOP_FN_RES: + snprintf(ns, sizeof(ns), "%p", + (void *)(cg->pcg_image->pi_vaddr + cg->pcg_func)); + break; + case FLAG_SHOW_OFFSET: + snprintf(ns, sizeof(ns), "%s+%#0lx", + pmcstat_string_unintern(sym->ps_name), + cg->pcg_func - sym->ps_start); + break; + default: + snprintf(ns, sizeof(ns), "%s", + pmcstat_string_unintern(sym->ps_name)); + break; + } + } PMCSTAT_ATTRON(v_attrs); PMCSTAT_PRINTW("%5.5s", vs); PMCSTAT_ATTROFF(v_attrs); - PMCSTAT_PRINTW(" %-10.10s %-20.20s", + PMCSTAT_PRINTW(" %-10.10s %-30.30s", pmcstat_string_unintern(cg->pcg_image->pi_name), ns); @@ -624,7 +640,7 @@ qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *), pmcstat_cgnode_compare); - PMCSTAT_PRINTW("%5.5s %-10.10s %-20.20s %s\n", + PMCSTAT_PRINTW("%5.5s %-10.10s %-30.30s %s\n", "%SAMP", "IMAGE", "FUNCTION", "CALLERS"); nentries = min(pmcstat_displayheight - 2, nentries); Index: usr.sbin/pmcstat/pmcstat.8 =================================================================== --- usr.sbin/pmcstat/pmcstat.8 +++ usr.sbin/pmcstat/pmcstat.8 @@ -33,6 +33,7 @@ .Nd "performance measurement with performance monitoring hardware" .Sh SYNOPSIS .Nm +.Op Fl A .Op Fl C .Op Fl D Ar pathname .Op Fl E @@ -123,6 +124,8 @@ .Sh OPTIONS The following options are available: .Bl -tag -width indent +.It Fl A +Skip symbol lookup and display address instead. .It Fl C Toggle between showing cumulative or incremental counts for subsequent counting mode PMCs specified on the command line. @@ -161,7 +164,7 @@ .Fl o option. .It Fl I -Skip symbol lookup and display address instead. +Show the offset of the instruction pointer into the symbol. .It Fl L List all event names. .It Fl M Ar mapfilename @@ -222,10 +225,10 @@ .Ar event-spec . .It Fl T Use a top like mode for sampling PMCs. The following hotkeys -can be used: 'c+a' switch to accumulative mode, 'c+d' switch -to delta mode, 'm' merge PMCs, 'n' change view, 'p' show next -PMC, ' ' pause, 'q' quit. calltree only: 'f' cost under threshold -is seen as a dot. +can be used: 'A' toggle symbol resolving, 'c+a' switch to accumulative mode, 'c+d' +switch to delta mode, 'I' toggle showing offsets into symbols, 'm' merge PMCs, 'n' +change view, 'p' show next PMC, ' ' pause, 'q' quit. calltree only: 'f' cost under +threshold is seen as a dot. .It Fl U Toggle capturing user-space call traces while in kernel mode. The default is for sampling PMCs to capture user-space callchain information Index: usr.sbin/pmcstat/pmcstat.c =================================================================== --- usr.sbin/pmcstat/pmcstat.c +++ usr.sbin/pmcstat/pmcstat.c @@ -511,8 +511,12 @@ CPU_COPY(&rootmask, &cpumask); while ((option = getopt(argc, argv, - "CD:EF:G:ILM:NO:P:R:S:TUWZa:c:def:gi:k:l:m:n:o:p:qr:s:t:u:vw:z:")) != -1) + "ACD:EF:G:ILM:NO:P:R:S:TUWZa:c:def:gi:k:l:m:n:o:p:qr:s:t:u:vw:z:")) != -1) switch (option) { + case 'A': + args.pa_flags |= FLAG_SKIP_TOP_FN_RES; + break; + case 'a': /* Annotate + callgraph */ args.pa_flags |= FLAG_DO_ANNOTATE; args.pa_plugin = PMCSTAT_PL_ANNOTATE_CG; @@ -586,14 +590,15 @@ args.pa_plugin = PMCSTAT_PL_GPROF; break; - case 'I': - args.pa_flags |= FLAG_SKIP_TOP_FN_RES; - break; case 'i': args.pa_flags |= FLAG_FILTER_THREAD_ID; args.pa_tid = strtol(optarg, &end, 0); break; + case 'I': + args.pa_flags |= FLAG_SHOW_OFFSET; + break; + case 'k': /* pathname to the kernel */ free(args.pa_kernel); args.pa_kernel = strdup(optarg); Index: usr.sbin/pmcstat/pmcstat_log.c =================================================================== --- usr.sbin/pmcstat/pmcstat_log.c +++ usr.sbin/pmcstat/pmcstat_log.c @@ -612,6 +612,12 @@ c = wgetch(w); wprintw(w, "Key: %c => ", c); switch (c) { + case 'A': + if (args.pa_flags & FLAG_SKIP_TOP_FN_RES) + args.pa_flags &= ~FLAG_SKIP_TOP_FN_RES; + else + args.pa_flags |= FLAG_SKIP_TOP_FN_RES; + break; case 'c': wprintw(w, "enter mode 'd' or 'a' => "); c = wgetch(w); @@ -623,6 +629,12 @@ wprintw(w, "switching to accumulation mode"); } break; + case 'I': + if (args.pa_flags & FLAG_SHOW_OFFSET) + args.pa_flags &= ~FLAG_SHOW_OFFSET; + else + args.pa_flags |= FLAG_SHOW_OFFSET; + break; case 'm': pmcstat_mergepmc = !pmcstat_mergepmc; /*