pmc: Provide full path to modules from kernel linker

This unifies the user object and kernel module paths in libpmcstat,
allows modules loaded from non-standard locations (e.g. from a user's
home directory when testing) to be found and, since buffer is what all
the warnings here use (they were never updated when buffer_modules were
added to pick based on where the file was found) has the side-effect of
ensuring the messages are correct.

This includes obsoleting the now-superfluous -k option in pmcstat.

This change breaks the hwpmc ABI and will be followed by a bump to the
pmc major version.

Reviewed by:	jhb, jkoshy, mhorne
Differential Revision:	https://reviews.freebsd.org/D40048
This commit is contained in:
Jessica Clarke 2023-05-31 00:15:34 +01:00
parent 8e63e787ab
commit 53d0b9e438
6 changed files with 11 additions and 90 deletions

View file

@ -98,7 +98,7 @@ struct pmcstat_args {
#define FLAG_READ_LOGFILE 0x00000200 /* -R file */
#define FLAG_DO_GPROF 0x00000400 /* -g */
#define FLAG_HAS_SAMPLESDIR 0x00000800 /* -D dir */
#define FLAG_HAS_KERNELPATH 0x00001000 /* -k kernel */
/* was FLAG_HAS_KERNELPATH 0x00001000 */
#define FLAG_DO_PRINT 0x00002000 /* -o */
#define FLAG_DO_CALLGRAPHS 0x00004000 /* -G or -F */
#define FLAG_DO_ANNOTATE 0x00008000 /* -m */
@ -121,7 +121,6 @@ struct pmcstat_args {
char *pa_outputpath; /* path to output log */
void *pa_logparser; /* log file parser */
const char *pa_fsroot; /* FS root where executables reside */
char *pa_kernel; /* pathname of the kernel */
const char *pa_samplesdir; /* directory for profile files */
const char *pa_mapfilename;/* mapfile name */
FILE *pa_graphfile; /* where to send the callgraph */

View file

@ -315,7 +315,6 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image,
GElf_Shdr sh;
enum pmcstat_image_type image_type;
char buffer[PATH_MAX];
char buffer_modules[PATH_MAX];
assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN);
@ -330,32 +329,19 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image,
assert(path != NULL);
/*
* Look for kernel modules under FSROOT/KERNELPATH/NAME and
* FSROOT/boot/modules/NAME, and user mode executable objects
* under FSROOT/PATHNAME.
* Look for files under FSROOT/PATHNAME.
*/
if (image->pi_iskernelmodule) {
(void) snprintf(buffer, sizeof(buffer), "%s%s/%s",
args->pa_fsroot, args->pa_kernel, path);
(void) snprintf(buffer_modules, sizeof(buffer_modules),
"%s/boot/modules/%s", args->pa_fsroot, path);
} else {
(void) snprintf(buffer, sizeof(buffer), "%s%s",
args->pa_fsroot, path);
}
(void) snprintf(buffer, sizeof(buffer), "%s%s",
args->pa_fsroot, path);
e = NULL;
fd = open(buffer, O_RDONLY, 0);
if (fd < 0 && !image->pi_iskernelmodule) {
if (fd < 0) {
warnx("WARNING: Cannot open \"%s\".",
buffer);
goto done;
}
if (fd < 0 && (fd = open(buffer_modules, O_RDONLY, 0)) < 0) {
warnx("WARNING: Cannot open \"%s\" or \"%s\".",
buffer, buffer_modules);
goto done;
}
if (elf_version(EV_CURRENT) == EV_NONE) {
warnx("WARNING: failed to init elf\n");
goto done;

View file

@ -5402,7 +5402,7 @@ pmc_kld_load(void *arg __unused, linker_file_t lf)
CK_LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
pmclog_process_map_in(po, (pid_t) -1,
(uintfptr_t) lf->address, lf->filename);
(uintfptr_t) lf->address, lf->pathname);
PMC_EPOCH_EXIT();
/*

View file

@ -2119,7 +2119,7 @@ linker_hwpmc_list_objects(void)
i = 0;
TAILQ_FOREACH(lf, &linker_files, link) {
/* Save the info for this linker file. */
kobase[i].pm_file = lf->filename;
kobase[i].pm_file = lf->pathname;
kobase[i].pm_address = (uintptr_t)lf->address;
i++;
}

View file

@ -57,7 +57,6 @@
.Op Fl f Ar pluginopt
.Op Fl g
.Op Fl i Ar lwp
.Op Fl k Ar kerneldir
.Op Fl l Ar secs
.Op Fl m Ar pathname
.Op Fl n Ar rate
@ -336,17 +335,6 @@ which you can get from
.Xr ps 1
.Fl o
.Li lwp .
.It Fl k Ar kerneldir
Set the pathname of the kernel directory to argument
.Ar kerneldir .
This directory specifies where
.Nm
should look for the kernel and its modules.
The default is to use the path of the running kernel obtained from the
.Va kern.bootfile
sysctl.
Modules will also be searched for in /boot/modules if not found in
.Ar kerneldir .
.It Fl l Ar secs
Set system-wide performance measurement duration for
.Ar secs

View file

@ -383,7 +383,6 @@ pmcstat_show_usage(void)
"\t -f spec\t pass \"spec\" to as plugin option\n"
"\t -g\t\t produce gprof(1) compatible profiles\n"
"\t -i lwp\t\t filter on thread id \"lwp\" in post-processing\n"
"\t -k dir\t\t set the path to the kernel\n"
"\t -l secs\t set duration time\n"
"\t -m file\t print sampled PCs to \"file\"\n"
"\t -n rate\t set sampling rate\n"
@ -456,7 +455,7 @@ main(int argc, char **argv)
int use_cumulative_counts;
short cf, cb;
uint64_t current_sampling_count;
char *end, *tmp, *event;
char *end, *event;
const char *errmsg, *graphfilename;
enum pmcstat_state runstate;
struct pmc_driverstats ds_start, ds_end;
@ -465,7 +464,6 @@ main(int argc, char **argv)
struct kevent kev;
struct winsize ws;
struct stat sb;
char buffer[PATH_MAX];
uint32_t caps;
check_driver_stats = 0;
@ -510,16 +508,6 @@ main(int argc, char **argv)
caps = 0;
CPU_ZERO(&cpumask);
/* Default to using the running system kernel. */
len = 0;
if (sysctlbyname("kern.bootfile", NULL, &len, NULL, 0) == -1)
err(EX_OSERR, "ERROR: Cannot determine path of running kernel");
args.pa_kernel = malloc(len);
if (args.pa_kernel == NULL)
errx(EX_SOFTWARE, "ERROR: Out of memory.");
if (sysctlbyname("kern.bootfile", args.pa_kernel, &len, NULL, 0) == -1)
err(EX_OSERR, "ERROR: Cannot determine path of running kernel");
len = sizeof(domains);
if (sysctlbyname("vm.ndomains", &domains, &len, NULL, 0) == -1)
err(EX_OSERR, "ERROR: Cannot get number of domains");
@ -623,12 +611,8 @@ main(int argc, char **argv)
break;
case 'k': /* pathname to the kernel */
free(args.pa_kernel);
args.pa_kernel = strdup(optarg);
if (args.pa_kernel == NULL)
errx(EX_SOFTWARE, "ERROR: Out of memory");
args.pa_required |= FLAG_DO_ANALYSIS;
args.pa_flags |= FLAG_HAS_KERNELPATH;
warnx("WARNING: -k is obsolete, has no effect "
"and will be removed in FreeBSD 15.");
break;
case 'L':
@ -1029,12 +1013,6 @@ main(int argc, char **argv)
"ERROR: option -O is used only with options -E, -P, -S and -W."
);
/* -k kernel path require -g/-G/-m/-T or -R */
if ((args.pa_flags & FLAG_HAS_KERNELPATH) &&
(args.pa_flags & FLAG_DO_ANALYSIS) == 0 &&
(args.pa_flags & FLAG_READ_LOGFILE) == 0)
errx(EX_USAGE, "ERROR: option -k is only used with -g/-R/-m/-T.");
/* -D only applies to gprof output mode (-g) */
if ((args.pa_flags & FLAG_HAS_SAMPLESDIR) &&
(args.pa_flags & FLAG_DO_GPROF) == 0)
@ -1058,36 +1036,6 @@ main(int argc, char **argv)
"ERROR: option -O is required if counting and sampling PMCs are specified together."
);
/*
* Check if 'kerneldir' refers to a file rather than a
* directory. If so, use `dirname path` to determine the
* kernel directory.
*/
(void) snprintf(buffer, sizeof(buffer), "%s%s", args.pa_fsroot,
args.pa_kernel);
if (stat(buffer, &sb) < 0)
err(EX_OSERR, "ERROR: Cannot locate kernel \"%s\"",
buffer);
if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode))
errx(EX_USAGE, "ERROR: \"%s\": Unsupported file type.",
buffer);
if (!S_ISDIR(sb.st_mode)) {
tmp = args.pa_kernel;
args.pa_kernel = strdup(dirname(args.pa_kernel));
if (args.pa_kernel == NULL)
errx(EX_SOFTWARE, "ERROR: Out of memory");
free(tmp);
(void) snprintf(buffer, sizeof(buffer), "%s%s",
args.pa_fsroot, args.pa_kernel);
if (stat(buffer, &sb) < 0)
err(EX_OSERR, "ERROR: Cannot stat \"%s\"",
buffer);
if (!S_ISDIR(sb.st_mode))
errx(EX_USAGE,
"ERROR: \"%s\" is not a directory.",
buffer);
}
/*
* If we have a callgraph be created, select the outputfile.
*/