Index: usr.sbin/pmcstat/pmcpl_gprof.c =================================================================== --- usr.sbin/pmcstat/pmcpl_gprof.c +++ usr.sbin/pmcstat/pmcpl_gprof.c @@ -74,6 +74,12 @@ #include "pmcpl_callgraph.h" #include "pmcpl_gprof.h" +typedef uint64_t WIDEHISTCOUNTER; + +static HISTCOUNTER hc_max; +static WIDEHISTCOUNTER whc_max; +static int hc_sz=0; + /* * struct pmcstat_gmonfile tracks a given 'gmon.out' file. These * files are mmap()'ed in as needed. @@ -99,6 +105,7 @@ struct pmcstat_image *_image); static pmcstat_interned_string pmcstat_gmon_create_name(const char *_sd, struct pmcstat_image *_img, pmc_id_t _pmcid); +static void pmcstat_gmon_histcounter_size(void); static void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf); static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf); @@ -106,6 +113,32 @@ pmcstat_image *_i, pmc_id_t _id); /* + * Determine the correct histcounter size and the maximum, based on + * the flags the user passed. + */ + +static void +pmcstat_gmon_histcounter_size() { + + /* Determine the correct histcounter size and maximum. */ + if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) { + hc_sz = sizeof(WIDEHISTCOUNTER); + if ((WIDEHISTCOUNTER) 0 < (WIDEHISTCOUNTER) -1) + whc_max = (WIDEHISTCOUNTER) -1; + else + whc_max = (WIDEHISTCOUNTER) ~((WIDEHISTCOUNTER) 1 << + ((sizeof(WIDEHISTCOUNTER) * CHAR_BIT) - 1)); + } else { + hc_sz = sizeof(HISTCOUNTER); + if ((HISTCOUNTER) 0 < (HISTCOUNTER) -1) + hc_max = (HISTCOUNTER) -1; + else + hc_max = (HISTCOUNTER) ~((HISTCOUNTER) 1 << + ((sizeof(HISTCOUNTER) * CHAR_BIT) - 1)); + } +} + +/* * Create a gmon.out file and size it. */ @@ -126,11 +159,15 @@ gm.lpc = image->pi_start; gm.hpc = image->pi_end; - gm.ncnt = (pgf->pgf_nbuckets * sizeof(HISTCOUNTER)) + - sizeof(struct gmonhdr); + gm.ncnt = (pgf->pgf_nbuckets * hc_sz) + sizeof(struct gmonhdr); gm.version = GMONVERSION; gm.profrate = 0; /* use ticks */ - gm.histcounter_type = 0; /* compatibility with moncontrol() */ + if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) { + gm.histcounter_type = hc_sz * CHAR_BIT; + if (!((WIDEHISTCOUNTER) 0 < (WIDEHISTCOUNTER) -1)) + gm.histcounter_type *= (-1); + } else + gm.histcounter_type = 0; /* compatibility with moncontrol() */ gm.spare[0] = gm.spare[1] = 0; /* Write out the gmon header */ @@ -400,6 +437,7 @@ struct pmcstat_gmonfile *pgf; uintfptr_t bucket; HISTCOUNTER *hc; + WIDEHISTCOUNTER *whc; pmc_id_t pmcid; (void) nsamples; (void) usermode; (void) cpu; @@ -437,6 +475,9 @@ */ pgf = pmcstat_image_find_gmonfile(image, pmcid); if (pgf == NULL) { + if (hc_sz == 0) + pmcstat_gmon_histcounter_size(); + if ((pgf = calloc(1, sizeof(*pgf))) == NULL) err(EX_OSERR, "ERROR:"); @@ -448,7 +489,7 @@ pgf->pgf_nbuckets = (image->pi_end - image->pi_start) / FUNCTION_ALIGNMENT; /* see */ pgf->pgf_ndatabytes = sizeof(struct gmonhdr) + - pgf->pgf_nbuckets * sizeof(HISTCOUNTER); + pgf->pgf_nbuckets * hc_sz; pgf->pgf_nsamples = 0; pgf->pgf_file = NULL; @@ -474,14 +515,25 @@ assert(bucket < pgf->pgf_nbuckets); - hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + - sizeof(struct gmonhdr)); - - /* saturating add */ - if (hc[bucket] < 0xFFFFU) /* XXX tie this to sizeof(HISTCOUNTER) */ - hc[bucket]++; - else /* mark that an overflow occurred */ - pgf->pgf_overflow = 1; + if (args.pa_flags & FLAG_DO_WIDE_GPROF_HC) { + whc = (WIDEHISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + + sizeof(struct gmonhdr)); + + /* saturating add */ + if (whc[bucket] < whc_max) + whc[bucket]++; + else /* mark that an overflow occurred */ + pgf->pgf_overflow = 1; + } else { + hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata + + sizeof(struct gmonhdr)); + + /* saturating add */ + if (hc[bucket] < hc_max) + hc[bucket]++; + else /* mark that an overflow occurred */ + pgf->pgf_overflow = 1; + } pgf->pgf_nsamples++; } Index: usr.sbin/pmcstat/pmcstat.h =================================================================== --- usr.sbin/pmcstat/pmcstat.h +++ usr.sbin/pmcstat/pmcstat.h @@ -55,6 +55,7 @@ #define FLAG_DO_ANALYSIS 0x00020000 /* -g or -G or -m or -T */ #define FLAGS_HAS_CPUMASK 0x00040000 /* -c */ #define FLAG_HAS_DURATION 0x00080000 /* -l secs */ +#define FLAG_DO_WIDE_GPROF_HC 0x00100000 /* -e */ #define DEFAULT_SAMPLE_COUNT 65536 #define DEFAULT_WAIT_INTERVAL 5.0 Index: usr.sbin/pmcstat/pmcstat.8 =================================================================== --- usr.sbin/pmcstat/pmcstat.8 +++ usr.sbin/pmcstat/pmcstat.8 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 27, 2015 +.Dd November 13, 2015 .Dt PMCSTAT 8 .Os .Sh NAME @@ -49,6 +49,7 @@ .Op Fl a Ar pathname .Op Fl c Ar cpu-spec .Op Fl d +.Op Fl e .Op Fl f Ar pluginopt .Op Fl g .Op Fl k Ar kerneldir @@ -260,6 +261,12 @@ .Fl P , or .Fl S ) . +.It Fl e +Specify that gprof profile files will use a wide history counter. +This will produce files in a format compatible with FreeBSD's +.Xr gprof 1 +program; however, other tools that do not fully parse a BSD-style +gprof header may have trouble parsing these files. .It Fl f Ar pluginopt Pass option string to the active plugin. .br Index: usr.sbin/pmcstat/pmcstat.c =================================================================== --- usr.sbin/pmcstat/pmcstat.c +++ usr.sbin/pmcstat/pmcstat.c @@ -506,6 +506,7 @@ "\t -a file\t print sampled PCs and callgraph to \"file\"\n" "\t -c cpu-list\t set cpus for subsequent system-wide PMCs\n" "\t -d\t\t (toggle) track descendants\n" + "\t -e\t\t use wide history counter for gprof(1) output\n" "\t -f spec\t pass \"spec\" to as plugin option\n" "\t -g\t\t produce gprof(1) compatible profiles\n" "\t -k dir\t\t set the path to the kernel\n" @@ -627,7 +628,7 @@ CPU_COPY(&rootmask, &cpumask); while ((option = getopt(argc, argv, - "CD:EF:G:M:NO:P:R:S:TWa:c:df:gk:l:m:n:o:p:qr:s:t:vw:z:")) != -1) + "CD:EF:G:M:NO:P:R:S:TWa:c:def:gk:l:m:n:o:p:qr:s:t:vw:z:")) != -1) switch (option) { case 'a': /* Annotate + callgraph */ args.pa_flags |= FLAG_DO_ANNOTATE; @@ -668,6 +669,10 @@ args.pa_required |= FLAG_HAS_PROCESS_PMCS; break; + case 'e': /* wide gprof metrics */ + args.pa_flags |= FLAG_DO_WIDE_GPROF_HC; + break; + case 'F': /* produce a system-wide calltree */ args.pa_flags |= FLAG_DO_CALLGRAPHS; args.pa_plugin = PMCSTAT_PL_CALLTREE; @@ -1022,6 +1027,13 @@ "ERROR: options -g/-G/-m/-T require sampling PMCs or -R to be specified." ); + /* check if -e was specified without -g */ + if ((args.pa_flags & FLAG_DO_WIDE_GPROF_HC) && + !(args.pa_flags & FLAG_DO_GPROF)) + errx(EX_USAGE, +"ERROR: option -e requires gprof mode to be specified." + ); + /* check if -O was spuriously specified */ if ((args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE) && (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0)