MFP4: Support for profiling dynamically loaded objects.
Kernel changes: Inform hwpmc of executable objects brought into the system by kldload() and mmap(), and of their removal by kldunload() and munmap(). A helper function linker_hwpmc_list_objects() has been added to "sys/kern/kern_linker.c" and is used by hwpmc to retrieve the list of currently loaded kernel modules. The unused `MAPPINGCHANGE' event has been deprecated in favour of separate `MAP_IN' and `MAP_OUT' events; this change reduces space wastage in the log. Bump the hwpmc's ABI version to "2.0.00". Teach hwpmc(4) to handle the map change callbacks. Change the default per-cpu sample buffer size to hold 32 samples (up from 16). Increment __FreeBSD_version. libpmc(3) changes: Update libpmc(3) to deal with the new events in the log file; bring the pmclog(3) manual page in sync with the code. pmcstat(8) changes: Introduce new options to pmcstat(8): "-r" (root fs path), "-M" (mapfile name), "-q"/"-v" (verbosity control). Option "-k" now takes a kernel directory as its argument but will also work with the older invocation syntax. Rework string handling in pmcstat(8) to use an opaque type for interned strings. Clean up ELF parsing code and add support for tracking dynamic object mappings reported by a v2.0.00 hwpmc(4). Report statistics at the end of a log conversion run depending on the requested verbosity level. Reviewed by: jhb, dds (kernel parts of an earlier patch) Tested by: gallatin (earlier patch)
This commit is contained in:
parent
a3688cc84e
commit
48e5e4792d
@ -1,4 +1,4 @@
|
||||
.\" Copyright (c) 2005 Joseph Koshy. All rights reserved.
|
||||
.\" Copyright (c) 2005-2006 Joseph Koshy. All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
@ -23,7 +23,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 1, 2005
|
||||
.Dd March 26, 2006
|
||||
.Os
|
||||
.Dt PMCLOG 3
|
||||
.Sh NAME
|
||||
@ -82,15 +82,21 @@ struct pmclog_ev {
|
||||
struct timespec pl_ts; /* log entry timestamp */
|
||||
enum pmclog_type pl_type; /* log entry kind */
|
||||
union { /* log entry data */
|
||||
struct pmclog_ev_allocate pl_a;
|
||||
struct pmclog_ev_proccsw pl_c;
|
||||
struct pmclog_ev_dropnotify pl_d;
|
||||
struct pmclog_ev_procexit pl_e;
|
||||
struct pmclog_ev_initialize pl_i;
|
||||
struct pmclog_ev_pcsample pl_s;
|
||||
struct pmclog_ev_pmcattach pl_t;
|
||||
struct pmclog_ev_userdata pl_u;
|
||||
struct pmclog_ev_procexec pl_x;
|
||||
struct pmclog_ev_closelog pl_cl;
|
||||
struct pmclog_ev_dropnotify pl_d;
|
||||
struct pmclog_ev_initialize pl_i;
|
||||
struct pmclog_ev_map_in pl_mi;
|
||||
struct pmclog_ev_map_out pl_mo;
|
||||
struct pmclog_ev_pcsample pl_s;
|
||||
struct pmclog_ev_pmcallocate pl_a;
|
||||
struct pmclog_ev_pmcattach pl_t;
|
||||
struct pmclog_ev_pmcdetach pl_d;
|
||||
struct pmclog_ev_proccsw pl_c;
|
||||
struct pmclog_ev_procexec pl_x;
|
||||
struct pmclog_ev_procexit pl_e;
|
||||
struct pmclog_ev_procfork pl_f;
|
||||
struct pmclog_ev_sysexit pl_e;
|
||||
struct pmclog_ev_userdata pl_u;
|
||||
} pl_u;
|
||||
};
|
||||
.Ed
|
||||
@ -149,8 +155,20 @@ had to drop data due to a resource constraint.
|
||||
.It Dv PMCLOG_TYPE_INITIALIZE
|
||||
An initialization record.
|
||||
This is the first record in a log file.
|
||||
.It Dv PMCLOG_TYPE_MAPPINGCHANGE
|
||||
A record describing an address space change for a process.
|
||||
.It Dv PMCLOG_TYPE_MAP_IN
|
||||
A record describing the introduction of a mapping to an executable
|
||||
object by a
|
||||
.Xr kldload 2
|
||||
or
|
||||
.Xr mmap 2
|
||||
system call.
|
||||
.It Dv PMCLOG_TYPE_MAP_OUT
|
||||
A record describing the removal of a mapping to an executable
|
||||
object by a
|
||||
.Xr kldunload 2
|
||||
or
|
||||
.Xr munmap 2
|
||||
system call.
|
||||
.It Dv PMCLOG_TYPE_PCSAMPLE
|
||||
A record containing an instruction pointer sample.
|
||||
.It Dv PMCLOG_TYPE_PMCALLOCATE
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2005 Joseph Koshy
|
||||
* Copyright (c) 2005-2006 Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -322,13 +322,16 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
ps->ps_arch = ev->pl_u.pl_i.pl_arch;
|
||||
ps->ps_initialized = 1;
|
||||
break;
|
||||
case PMCLOG_TYPE_MAPPINGCHANGE:
|
||||
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_mappingchange);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_m.pl_type);
|
||||
PMCLOG_READADDR(le,ev->pl_u.pl_m.pl_start);
|
||||
PMCLOG_READADDR(le,ev->pl_u.pl_m.pl_end);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_m.pl_pid);
|
||||
PMCLOG_READSTRING(le, ev->pl_u.pl_m.pl_pathname, pathlen);
|
||||
case PMCLOG_TYPE_MAP_IN:
|
||||
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_map_in);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_mi.pl_pid);
|
||||
PMCLOG_READADDR(le,ev->pl_u.pl_mi.pl_start);
|
||||
PMCLOG_READSTRING(le, ev->pl_u.pl_mi.pl_pathname, pathlen);
|
||||
break;
|
||||
case PMCLOG_TYPE_MAP_OUT:
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_mo.pl_pid);
|
||||
PMCLOG_READADDR(le,ev->pl_u.pl_mo.pl_start);
|
||||
PMCLOG_READADDR(le,ev->pl_u.pl_mo.pl_end);
|
||||
break;
|
||||
case PMCLOG_TYPE_PCSAMPLE:
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pid);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2005 Joseph Koshy
|
||||
* Copyright (c) 2005-2006 Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -49,12 +49,16 @@ struct pmclog_ev_initialize {
|
||||
uint32_t pl_arch;
|
||||
};
|
||||
|
||||
struct pmclog_ev_mappingchange {
|
||||
uint32_t pl_type;
|
||||
struct pmclog_ev_map_in {
|
||||
pid_t pl_pid;
|
||||
uintfptr_t pl_start;
|
||||
char pl_pathname[PATH_MAX];
|
||||
};
|
||||
|
||||
struct pmclog_ev_map_out {
|
||||
pid_t pl_pid;
|
||||
uintfptr_t pl_start;
|
||||
uintfptr_t pl_end;
|
||||
char pl_pathname[PATH_MAX];
|
||||
};
|
||||
|
||||
struct pmclog_ev_pcsample {
|
||||
@ -124,7 +128,8 @@ struct pmclog_ev {
|
||||
struct pmclog_ev_closelog pl_cl;
|
||||
struct pmclog_ev_dropnotify pl_dn;
|
||||
struct pmclog_ev_initialize pl_i;
|
||||
struct pmclog_ev_mappingchange pl_m;
|
||||
struct pmclog_ev_map_in pl_mi;
|
||||
struct pmclog_ev_map_out pl_mo;
|
||||
struct pmclog_ev_pcsample pl_s;
|
||||
struct pmclog_ev_pmcallocate pl_a;
|
||||
struct pmclog_ev_pmcattach pl_t;
|
||||
|
@ -137,10 +137,11 @@ static struct mtx pmc_kthread_mtx; /* sleep lock */
|
||||
|
||||
CTASSERT(sizeof(struct pmclog_closelog) == 3*4);
|
||||
CTASSERT(sizeof(struct pmclog_dropnotify) == 3*4);
|
||||
CTASSERT(sizeof(struct pmclog_mappingchange) == PATH_MAX +
|
||||
5*4 + 2*sizeof(uintfptr_t));
|
||||
CTASSERT(offsetof(struct pmclog_mappingchange,pl_pathname) ==
|
||||
5*4 + 2*sizeof(uintfptr_t));
|
||||
CTASSERT(sizeof(struct pmclog_map_in) == PATH_MAX +
|
||||
4*4 + sizeof(uintfptr_t));
|
||||
CTASSERT(offsetof(struct pmclog_map_in,pl_pathname) ==
|
||||
4*4 + sizeof(uintfptr_t));
|
||||
CTASSERT(sizeof(struct pmclog_map_out) == 4*4 + 2*sizeof(uintfptr_t));
|
||||
CTASSERT(sizeof(struct pmclog_pcsample) == 6*4 + sizeof(uintfptr_t));
|
||||
CTASSERT(sizeof(struct pmclog_pmcallocate) == 6*4);
|
||||
CTASSERT(sizeof(struct pmclog_pmcattach) == 5*4 + PATH_MAX);
|
||||
@ -728,24 +729,36 @@ pmclog_process_dropnotify(struct pmc_owner *po)
|
||||
}
|
||||
|
||||
void
|
||||
pmclog_process_mappingchange(struct pmc_owner *po, pid_t pid, int type,
|
||||
uintfptr_t start, uintfptr_t end, char *path)
|
||||
pmclog_process_map_in(struct pmc_owner *po, pid_t pid, uintfptr_t start,
|
||||
const char *path)
|
||||
{
|
||||
int pathlen, recordlen;
|
||||
|
||||
KASSERT(path != NULL, ("[pmclog,%d] map-in, null path", __LINE__));
|
||||
|
||||
pathlen = strlen(path) + 1; /* #bytes for path name */
|
||||
recordlen = offsetof(struct pmclog_mappingchange, pl_pathname) +
|
||||
recordlen = offsetof(struct pmclog_map_in, pl_pathname) +
|
||||
pathlen;
|
||||
|
||||
PMCLOG_RESERVE(po,MAPPINGCHANGE,recordlen);
|
||||
PMCLOG_EMIT32(type);
|
||||
PMCLOG_EMITADDR(start);
|
||||
PMCLOG_EMITADDR(end);
|
||||
PMCLOG_RESERVE(po, MAP_IN, recordlen);
|
||||
PMCLOG_EMIT32(pid);
|
||||
PMCLOG_EMITADDR(start);
|
||||
PMCLOG_EMITSTRING(path,pathlen);
|
||||
PMCLOG_DESPATCH(po);
|
||||
}
|
||||
|
||||
void
|
||||
pmclog_process_map_out(struct pmc_owner *po, pid_t pid, uintfptr_t start,
|
||||
uintfptr_t end)
|
||||
{
|
||||
KASSERT(start <= end, ("[pmclog,%d] start > end", __LINE__));
|
||||
|
||||
PMCLOG_RESERVE(po, MAP_OUT, sizeof(struct pmclog_map_out));
|
||||
PMCLOG_EMIT32(pid);
|
||||
PMCLOG_EMITADDR(start);
|
||||
PMCLOG_EMITADDR(end);
|
||||
PMCLOG_DESPATCH(po);
|
||||
}
|
||||
|
||||
void
|
||||
pmclog_process_pcsample(struct pmc *pm, struct pmc_sample *ps)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2005 Joseph Koshy
|
||||
* Copyright (c) 2003-2006 Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -53,6 +53,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/systm.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#include <sys/linker.h> /* needs to be after <sys/malloc.h> */
|
||||
|
||||
#include <machine/atomic.h>
|
||||
#include <machine/md_var.h>
|
||||
|
||||
@ -1410,6 +1412,122 @@ pmc_process_csw_out(struct thread *td)
|
||||
critical_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Log a KLD operation.
|
||||
*/
|
||||
|
||||
static void
|
||||
pmc_process_kld_load(struct pmckern_map_in *pkm)
|
||||
{
|
||||
struct pmc_owner *po;
|
||||
|
||||
sx_assert(&pmc_sx, SX_LOCKED);
|
||||
|
||||
/*
|
||||
* Notify owners of system sampling PMCs about KLD operations.
|
||||
*/
|
||||
|
||||
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
|
||||
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
|
||||
pmclog_process_map_in(po, (pid_t) -1, pkm->pm_address,
|
||||
(char *) pkm->pm_file);
|
||||
|
||||
/*
|
||||
* TODO: Notify owners of (all) process-sampling PMCs too.
|
||||
*/
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
pmc_process_kld_unload(struct pmckern_map_out *pkm)
|
||||
{
|
||||
struct pmc_owner *po;
|
||||
|
||||
sx_assert(&pmc_sx, SX_LOCKED);
|
||||
|
||||
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
|
||||
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
|
||||
pmclog_process_map_out(po, (pid_t) -1,
|
||||
pkm->pm_address, pkm->pm_address + pkm->pm_size);
|
||||
|
||||
/*
|
||||
* TODO: Notify owners of process-sampling PMCs.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* A mapping change for a process.
|
||||
*/
|
||||
|
||||
static void
|
||||
pmc_process_mmap(struct thread *td, struct pmckern_map_in *pkm)
|
||||
{
|
||||
int ri;
|
||||
pid_t pid;
|
||||
char *fullpath, *freepath;
|
||||
const struct pmc *pm;
|
||||
struct pmc_owner *po;
|
||||
const struct pmc_process *pp;
|
||||
|
||||
freepath = fullpath = NULL;
|
||||
pmc_getfilename((struct vnode *) pkm->pm_file, &fullpath, &freepath);
|
||||
|
||||
pid = td->td_proc->p_pid;
|
||||
|
||||
/* Inform owners of all system-wide sampling PMCs. */
|
||||
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
|
||||
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
|
||||
pmclog_process_map_in(po, pid, pkm->pm_address, fullpath);
|
||||
|
||||
if ((pp = pmc_find_process_descriptor(td->td_proc, 0)) == NULL)
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* Inform sampling PMC owners tracking this process.
|
||||
*/
|
||||
for (ri = 0; ri < md->pmd_npmc; ri++)
|
||||
if ((pm = pp->pp_pmcs[ri].pp_pmc) != NULL &&
|
||||
PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
|
||||
pmclog_process_map_in(pm->pm_owner,
|
||||
pid, pkm->pm_address, fullpath);
|
||||
|
||||
done:
|
||||
if (freepath)
|
||||
FREE(freepath, M_TEMP);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Log an munmap request.
|
||||
*/
|
||||
|
||||
static void
|
||||
pmc_process_munmap(struct thread *td, struct pmckern_map_out *pkm)
|
||||
{
|
||||
int ri;
|
||||
pid_t pid;
|
||||
struct pmc_owner *po;
|
||||
const struct pmc *pm;
|
||||
const struct pmc_process *pp;
|
||||
|
||||
pid = td->td_proc->p_pid;
|
||||
|
||||
LIST_FOREACH(po, &pmc_ss_owners, po_ssnext)
|
||||
if (po->po_flags & PMC_PO_OWNS_LOGFILE)
|
||||
pmclog_process_map_out(po, pid, pkm->pm_address,
|
||||
pkm->pm_address + pkm->pm_size);
|
||||
|
||||
if ((pp = pmc_find_process_descriptor(td->td_proc, 0)) == NULL)
|
||||
return;
|
||||
|
||||
for (ri = 0; ri < md->pmd_npmc; ri++)
|
||||
if ((pm = pp->pp_pmcs[ri].pp_pmc) != NULL &&
|
||||
PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
|
||||
pmclog_process_map_out(po, pid, pkm->pm_address,
|
||||
pkm->pm_address + pkm->pm_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* The 'hook' invoked from the kernel proper
|
||||
*/
|
||||
@ -1417,13 +1535,16 @@ pmc_process_csw_out(struct thread *td)
|
||||
|
||||
#ifdef DEBUG
|
||||
const char *pmc_hooknames[] = {
|
||||
/* these strings correspond to PMC_FN_* in <sys/pmckern.h> */
|
||||
"",
|
||||
"EXIT",
|
||||
"EXEC",
|
||||
"FORK",
|
||||
"CSW-IN",
|
||||
"CSW-OUT",
|
||||
"SAMPLE"
|
||||
"SAMPLE",
|
||||
"KLDLOAD",
|
||||
"KLDUNLOAD",
|
||||
"MMAP",
|
||||
"MUNMAP"
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -1585,6 +1706,27 @@ pmc_hook_handler(struct thread *td, int function, void *arg)
|
||||
pmc_process_samples(PCPU_GET(cpuid));
|
||||
break;
|
||||
|
||||
|
||||
case PMC_FN_KLD_LOAD:
|
||||
sx_assert(&pmc_sx, SX_LOCKED);
|
||||
pmc_process_kld_load((struct pmckern_map_in *) arg);
|
||||
break;
|
||||
|
||||
case PMC_FN_KLD_UNLOAD:
|
||||
sx_assert(&pmc_sx, SX_LOCKED);
|
||||
pmc_process_kld_unload((struct pmckern_map_out *) arg);
|
||||
break;
|
||||
|
||||
case PMC_FN_MMAP:
|
||||
sx_assert(&pmc_sx, SX_LOCKED);
|
||||
pmc_process_mmap(td, (struct pmckern_map_in *) arg);
|
||||
break;
|
||||
|
||||
case PMC_FN_MUNMAP:
|
||||
sx_assert(&pmc_sx, SX_LOCKED);
|
||||
pmc_process_munmap(td, (struct pmckern_map_out *) arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
KASSERT(0, ("[pmc,%d] unknown hook %d\n", __LINE__, function));
|
||||
@ -2237,6 +2379,8 @@ pmc_start(struct pmc *pm)
|
||||
po->po_sscount++;
|
||||
}
|
||||
|
||||
/* TODO: dump system wide process mappings to the log? */
|
||||
|
||||
/*
|
||||
* Move to the CPU associated with this
|
||||
* PMC, and start the hardware.
|
||||
@ -2408,10 +2552,11 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
|
||||
case PMC_OP_CONFIGURELOG:
|
||||
{
|
||||
struct proc *p;
|
||||
struct pmc *pm;
|
||||
struct pmc_owner *po;
|
||||
struct pmckern_map_in *km, *kmbase;
|
||||
struct pmc_op_configurelog cl;
|
||||
struct proc *p;
|
||||
|
||||
sx_assert(&pmc_sx, SX_XLOCKED);
|
||||
|
||||
@ -2446,6 +2591,21 @@ pmc_syscall_handler(struct thread *td, void *syscall_args)
|
||||
}
|
||||
} else
|
||||
error = EINVAL;
|
||||
|
||||
if (error)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Log the current set of kernel modules.
|
||||
*/
|
||||
kmbase = linker_hwpmc_list_objects();
|
||||
for (km = kmbase; km->pm_file != NULL; km++) {
|
||||
PMCDBG(LOG,REG,1,"%s %p", (char *) km->pm_file,
|
||||
(void *) km->pm_address);
|
||||
pmclog_process_map_in(po, (pid_t) -1, km->pm_address,
|
||||
km->pm_file);
|
||||
}
|
||||
FREE(kmbase, M_LINKER);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_ddb.h"
|
||||
#include "opt_hwpmc_hooks.h"
|
||||
#include "opt_mac.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
@ -51,6 +52,10 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "linker_if.h"
|
||||
|
||||
#ifdef HWPMC_HOOKS
|
||||
#include <sys/pmckern.h>
|
||||
#endif
|
||||
|
||||
#ifdef KLD_DEBUG
|
||||
int kld_debug = 0;
|
||||
#endif
|
||||
@ -751,6 +756,9 @@ linker_ddb_symbol_values(c_linker_sym_t sym, linker_symval_t *symval)
|
||||
int
|
||||
kldload(struct thread *td, struct kldload_args *uap)
|
||||
{
|
||||
#ifdef HWPMC_HOOKS
|
||||
struct pmckern_map_in pkm;
|
||||
#endif
|
||||
char *kldname, *modname;
|
||||
char *pathname = NULL;
|
||||
linker_file_t lf;
|
||||
@ -786,6 +794,11 @@ kldload(struct thread *td, struct kldload_args *uap)
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
#ifdef HWPMC_HOOKS
|
||||
pkm.pm_file = lf->filename;
|
||||
pkm.pm_address = (uintptr_t) lf->address;
|
||||
PMC_CALL_HOOK(td, PMC_FN_KLD_LOAD, (void *) &pkm);
|
||||
#endif
|
||||
lf->userrefs++;
|
||||
td->td_retval[0] = lf->id;
|
||||
out:
|
||||
@ -801,6 +814,9 @@ kldload(struct thread *td, struct kldload_args *uap)
|
||||
static int
|
||||
kern_kldunload(struct thread *td, int fileid, int flags)
|
||||
{
|
||||
#ifdef HWPMC_HOOKS
|
||||
struct pmckern_map_out pkm;
|
||||
#endif
|
||||
linker_file_t lf;
|
||||
int error = 0;
|
||||
|
||||
@ -825,11 +841,21 @@ kern_kldunload(struct thread *td, int fileid, int flags)
|
||||
goto out;
|
||||
}
|
||||
lf->userrefs--;
|
||||
#ifdef HWPMC_HOOKS
|
||||
/* Save data needed by hwpmc(4) before unloading the kld. */
|
||||
pkm.pm_address = (uintptr_t) lf->address;
|
||||
pkm.pm_size = lf->size;
|
||||
#endif
|
||||
error = linker_file_unload(lf, flags);
|
||||
if (error)
|
||||
lf->userrefs++;
|
||||
} else
|
||||
error = ENOENT;
|
||||
|
||||
#ifdef HWPMC_HOOKS
|
||||
if (error == 0)
|
||||
PMC_CALL_HOOK(td, PMC_FN_KLD_UNLOAD, (void *) &pkm);
|
||||
#endif
|
||||
out:
|
||||
mtx_unlock(&Giant);
|
||||
return (error);
|
||||
@ -1659,6 +1685,58 @@ linker_basename(const char *path)
|
||||
return (filename);
|
||||
}
|
||||
|
||||
#ifdef HWPMC_HOOKS
|
||||
|
||||
/*
|
||||
* Inform hwpmc about the set of kernel modules currently loaded.
|
||||
*/
|
||||
void *
|
||||
linker_hwpmc_list_objects(void)
|
||||
{
|
||||
int nobjects, nmappings;
|
||||
linker_file_t lf;
|
||||
struct pmckern_map_in *ko, *kobase;
|
||||
|
||||
nmappings = 15; /* a reasonable default */
|
||||
|
||||
retry:
|
||||
/* allocate nmappings+1 entries */
|
||||
MALLOC(kobase, struct pmckern_map_in *,
|
||||
(nmappings + 1) * sizeof(struct pmckern_map_in), M_LINKER,
|
||||
M_WAITOK | M_ZERO);
|
||||
|
||||
nobjects = 0;
|
||||
mtx_lock(&kld_mtx);
|
||||
TAILQ_FOREACH(lf, &linker_files, link)
|
||||
nobjects++;
|
||||
|
||||
KASSERT(nobjects > 0, ("linker_hpwmc_list_objects: no kernel "
|
||||
"objects?"));
|
||||
|
||||
if (nobjects > nmappings) {
|
||||
nmappings = nobjects;
|
||||
FREE(kobase, M_LINKER);
|
||||
mtx_unlock(&kld_mtx);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
ko = kobase;
|
||||
TAILQ_FOREACH(lf, &linker_files, link) {
|
||||
ko->pm_file = lf->filename;
|
||||
ko->pm_address = (uintptr_t) lf->address;
|
||||
ko++;
|
||||
}
|
||||
|
||||
/* The last entry of the malloced area comprises of all zeros. */
|
||||
KASSERT(ko->pm_file == NULL,
|
||||
("linker_hwpmc_list_objects: last object not NULL"));
|
||||
|
||||
mtx_unlock(&kld_mtx);
|
||||
|
||||
return ((void *) kobase);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Find a file which contains given module and load it, if "parent" is not
|
||||
* NULL, register a reference to it.
|
||||
|
@ -172,6 +172,9 @@ int linker_ddb_search_symbol(caddr_t _value, c_linker_sym_t *_sym,
|
||||
int linker_ddb_symbol_values(c_linker_sym_t _sym, linker_symval_t *_symval);
|
||||
|
||||
|
||||
/* HWPMC helper */
|
||||
void *linker_hwpmc_list_objects(void);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
|
||||
/*
|
||||
|
@ -57,7 +57,7 @@
|
||||
* is created, otherwise 1.
|
||||
*/
|
||||
#undef __FreeBSD_version
|
||||
#define __FreeBSD_version 700014 /* Master, propagated to newvers */
|
||||
#define __FreeBSD_version 700015 /* Master, propagated to newvers */
|
||||
|
||||
#ifndef LOCORE
|
||||
#include <sys/types.h>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2005, Joseph Koshy
|
||||
* Copyright (c) 2003-2006, Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -50,9 +50,9 @@
|
||||
*
|
||||
* The patch version is incremented for every bug fix.
|
||||
*/
|
||||
#define PMC_VERSION_MAJOR 0x01
|
||||
#define PMC_VERSION_MINOR 0x02
|
||||
#define PMC_VERSION_PATCH 0x0003
|
||||
#define PMC_VERSION_MAJOR 0x02
|
||||
#define PMC_VERSION_MINOR 0x00
|
||||
#define PMC_VERSION_PATCH 0x0000
|
||||
|
||||
#define PMC_VERSION (PMC_VERSION_MAJOR << 24 | \
|
||||
PMC_VERSION_MINOR << 16 | PMC_VERSION_PATCH)
|
||||
@ -538,7 +538,7 @@ struct pmc_op_getmsr {
|
||||
#define PMC_MTXPOOL_SIZE 32
|
||||
#define PMC_LOG_BUFFER_SIZE 4
|
||||
#define PMC_NLOGBUFFERS 16
|
||||
#define PMC_NSAMPLES 16
|
||||
#define PMC_NSAMPLES 32
|
||||
|
||||
#define PMC_SYSCTL_NAME_PREFIX "kern." PMC_MODULE_NAME "."
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2005, Joseph Koshy
|
||||
* Copyright (c) 2003-2006, Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -43,10 +43,24 @@
|
||||
#define PMC_FN_CSW_IN 2
|
||||
#define PMC_FN_CSW_OUT 3
|
||||
#define PMC_FN_DO_SAMPLES 4
|
||||
#define PMC_FN_KLD_LOAD 5
|
||||
#define PMC_FN_KLD_UNLOAD 6
|
||||
#define PMC_FN_MMAP 7
|
||||
#define PMC_FN_MUNMAP 8
|
||||
|
||||
struct pmckern_procexec {
|
||||
int pm_credentialschanged;
|
||||
uintptr_t pm_entryaddr;
|
||||
uintfptr_t pm_entryaddr;
|
||||
};
|
||||
|
||||
struct pmckern_map_in {
|
||||
void *pm_file; /* filename or vnode pointer */
|
||||
uintfptr_t pm_address; /* address object is loaded at */
|
||||
};
|
||||
|
||||
struct pmckern_map_out {
|
||||
uintfptr_t pm_address; /* start address of region */
|
||||
size_t pm_size; /* size of unmapped region */
|
||||
};
|
||||
|
||||
/* hook */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2005 Joseph Koshy
|
||||
* Copyright (c) 2005-2006, Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -32,10 +32,11 @@
|
||||
#include <sys/pmc.h>
|
||||
|
||||
enum pmclog_type {
|
||||
/* V1 ABI */
|
||||
PMCLOG_TYPE_CLOSELOG,
|
||||
PMCLOG_TYPE_DROPNOTIFY,
|
||||
PMCLOG_TYPE_INITIALIZE,
|
||||
PMCLOG_TYPE_MAPPINGCHANGE,
|
||||
PMCLOG_TYPE_MAPPINGCHANGE, /* unused in v1 */
|
||||
PMCLOG_TYPE_PCSAMPLE,
|
||||
PMCLOG_TYPE_PMCALLOCATE,
|
||||
PMCLOG_TYPE_PMCATTACH,
|
||||
@ -45,11 +46,19 @@ enum pmclog_type {
|
||||
PMCLOG_TYPE_PROCEXIT,
|
||||
PMCLOG_TYPE_PROCFORK,
|
||||
PMCLOG_TYPE_SYSEXIT,
|
||||
PMCLOG_TYPE_USERDATA
|
||||
PMCLOG_TYPE_USERDATA,
|
||||
/*
|
||||
* V2 ABI
|
||||
*
|
||||
* The MAP_{IN,OUT} event types obsolete the MAPPING_CHANGE
|
||||
* event type of the older (V1) ABI.
|
||||
*/
|
||||
PMCLOG_TYPE_MAP_IN,
|
||||
PMCLOG_TYPE_MAP_OUT
|
||||
};
|
||||
|
||||
#define PMCLOG_MAPPING_INSERT 0x01
|
||||
#define PMCLOG_MAPPING_DELETE 0x02
|
||||
#define PMCLOG_MAPPING_INSERT 0x01 /* obsolete */
|
||||
#define PMCLOG_MAPPING_DELETE 0x02 /* obsolete */
|
||||
|
||||
/*
|
||||
* A log entry descriptor comprises of a 32 bit header and a 64 bit
|
||||
@ -98,15 +107,19 @@ struct pmclog_initialize {
|
||||
uint32_t pl_cpu; /* enum pmc_cputype */
|
||||
} __packed;
|
||||
|
||||
struct pmclog_mappingchange {
|
||||
struct pmclog_map_in {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_type;
|
||||
uintfptr_t pl_start; /* 8 byte aligned */
|
||||
uintfptr_t pl_end;
|
||||
uint32_t pl_pid;
|
||||
uintfptr_t pl_start; /* 8 byte aligned */
|
||||
char pl_pathname[PATH_MAX];
|
||||
} __packed;
|
||||
|
||||
struct pmclog_map_out {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pid;
|
||||
uintfptr_t pl_start; /* 8 byte aligned */
|
||||
uintfptr_t pl_end;
|
||||
} __packed;
|
||||
|
||||
struct pmclog_pcsample {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
@ -178,6 +191,8 @@ union pmclog_entry { /* only used to size scratch areas */
|
||||
struct pmclog_closelog pl_cl;
|
||||
struct pmclog_dropnotify pl_dn;
|
||||
struct pmclog_initialize pl_i;
|
||||
struct pmclog_map_in pl_mi;
|
||||
struct pmclog_map_out pl_mo;
|
||||
struct pmclog_pcsample pl_s;
|
||||
struct pmclog_pmcallocate pl_a;
|
||||
struct pmclog_pmcattach pl_t;
|
||||
@ -212,8 +227,10 @@ int pmclog_flush(struct pmc_owner *_po);
|
||||
void pmclog_initialize(void);
|
||||
void pmclog_process_closelog(struct pmc_owner *po);
|
||||
void pmclog_process_dropnotify(struct pmc_owner *po);
|
||||
void pmclog_process_mappingchange(struct pmc_owner *po, pid_t pid, int type,
|
||||
uintfptr_t start, uintfptr_t end, char *path);
|
||||
void pmclog_process_map_in(struct pmc_owner *po, pid_t pid,
|
||||
uintfptr_t start, const char *path);
|
||||
void pmclog_process_map_out(struct pmc_owner *po, pid_t pid,
|
||||
uintfptr_t start, uintfptr_t end);
|
||||
void pmclog_process_pcsample(struct pmc *_pm, struct pmc_sample *_ps);
|
||||
void pmclog_process_pmcallocate(struct pmc *_pm);
|
||||
void pmclog_process_pmcattach(struct pmc *_pm, pid_t _pid, char *_path);
|
||||
|
@ -44,6 +44,7 @@
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_compat.h"
|
||||
#include "opt_hwpmc_hooks.h"
|
||||
#include "opt_mac.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
@ -79,6 +80,10 @@ __FBSDID("$FreeBSD$");
|
||||
#include <vm/vm_page.h>
|
||||
#include <vm/vm_kern.h>
|
||||
|
||||
#ifdef HWPMC_HOOKS
|
||||
#include <sys/pmckern.h>
|
||||
#endif
|
||||
|
||||
#ifndef _SYS_SYSPROTO_H_
|
||||
struct sbrk_args {
|
||||
int incr;
|
||||
@ -201,6 +206,9 @@ mmap(td, uap)
|
||||
struct thread *td;
|
||||
struct mmap_args *uap;
|
||||
{
|
||||
#ifdef HWPMC_HOOKS
|
||||
struct pmckern_map_in pkm;
|
||||
#endif
|
||||
struct file *fp;
|
||||
struct vnode *vp;
|
||||
vm_offset_t addr;
|
||||
@ -364,6 +372,15 @@ mmap(td, uap)
|
||||
|
||||
error = vm_mmap(&vms->vm_map, &addr, size, prot, maxprot,
|
||||
flags, handle_type, handle, pos);
|
||||
#ifdef HWPMC_HOOKS
|
||||
/* inform hwpmc(4) if an executable is being mapped */
|
||||
if (error == 0 && handle_type == OBJT_VNODE &&
|
||||
(prot & PROT_EXEC)) {
|
||||
pkm.pm_file = handle;
|
||||
pkm.pm_address = (uintptr_t) addr;
|
||||
PMC_CALL_HOOK(td, PMC_FN_MMAP, (void *) &pkm);
|
||||
}
|
||||
#endif
|
||||
if (error == 0)
|
||||
td->td_retval[0] = (register_t) (addr + pageoff);
|
||||
done:
|
||||
@ -495,6 +512,10 @@ munmap(td, uap)
|
||||
struct thread *td;
|
||||
struct munmap_args *uap;
|
||||
{
|
||||
#ifdef HWPMC_HOOKS
|
||||
struct pmckern_map_out pkm;
|
||||
vm_map_entry_t entry;
|
||||
#endif
|
||||
vm_offset_t addr;
|
||||
vm_size_t size, pageoff;
|
||||
vm_map_t map;
|
||||
@ -525,6 +546,26 @@ munmap(td, uap)
|
||||
vm_map_unlock(map);
|
||||
return (EINVAL);
|
||||
}
|
||||
#ifdef HWPMC_HOOKS
|
||||
/*
|
||||
* Inform hwpmc if the address range being unmapped contains
|
||||
* an executable region.
|
||||
*/
|
||||
if (vm_map_lookup_entry(map, addr, &entry)) {
|
||||
for (;
|
||||
entry != &map->header && entry->start < addr + size;
|
||||
entry = entry->next) {
|
||||
if (vm_map_check_protection(map, entry->start,
|
||||
entry->end, VM_PROT_EXECUTE) == TRUE) {
|
||||
pkm.pm_address = (uintptr_t) addr;
|
||||
pkm.pm_size = (size_t) size;
|
||||
PMC_CALL_HOOK(td, PMC_FN_MUNMAP,
|
||||
(void *) &pkm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* returns nothing but KERN_SUCCESS anyway */
|
||||
vm_map_delete(map, addr, addr + size);
|
||||
vm_map_unlock(map);
|
||||
|
@ -1,4 +1,4 @@
|
||||
.\" Copyright (c) 2003 Joseph Koshy. All rights reserved.
|
||||
.\" Copyright (c) 2003-2006 Joseph Koshy. All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
@ -23,7 +23,7 @@
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd July 1, 2005
|
||||
.Dd March 26, 2006
|
||||
.Os
|
||||
.Dt PMCSTAT 8
|
||||
.Sh NAME
|
||||
@ -34,6 +34,7 @@
|
||||
.Op Fl C
|
||||
.Op Fl D Ar pathname
|
||||
.Op Fl E
|
||||
.Op Fl M Ar mapfilename
|
||||
.Op Fl O Ar logfilename
|
||||
.Op Fl P Ar event-spec
|
||||
.Op Fl R Ar logfilename
|
||||
@ -42,12 +43,15 @@
|
||||
.Op Fl c Ar cpu
|
||||
.Op Fl d
|
||||
.Op Fl g
|
||||
.Op Fl k Ar kernelfile
|
||||
.Op Fl k Ar kerneldir
|
||||
.Op Fl n Ar rate
|
||||
.Op Fl o Ar outputfile
|
||||
.Op Fl p Ar event-spec
|
||||
.Op Fl q
|
||||
.Op Fl r Ar fsroot
|
||||
.Op Fl s Ar event-spec
|
||||
.Op Fl t Ar pid
|
||||
.Op Fl v
|
||||
.Op Fl w Ar secs
|
||||
.Op Ar command Op Ar args
|
||||
.Sh DESCRIPTION
|
||||
@ -116,6 +120,21 @@ complex pipeline of processes when used in conjunction with the
|
||||
.Fl d
|
||||
option.
|
||||
The default is to not to enable per-process tracking.
|
||||
.It Fl M Ar mapfilename
|
||||
Write the mapping between executable objects encountered in the event
|
||||
log and the abbreviated pathnames used for
|
||||
.Xr gprof 1
|
||||
profiles to file
|
||||
.Ar mapfilename .
|
||||
If this option is not specified, mapping information is not written.
|
||||
Argument
|
||||
.Ar mapfilename
|
||||
may be a
|
||||
.Dq Li -
|
||||
in which case this mapping information is sent to the output
|
||||
file configured by the
|
||||
.Fl o
|
||||
option.
|
||||
.It Fl O Ar logfilename
|
||||
Send logging output to file
|
||||
.Ar logfilename .
|
||||
@ -159,11 +178,14 @@ A separate profile file is generated for each executable object
|
||||
encountered.
|
||||
Profile files are placed in sub-directories named by their PMC
|
||||
event name.
|
||||
.It Fl k Ar kernelfile
|
||||
Set the pathname of the kernel to argument
|
||||
.Ar kernelfile .
|
||||
.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
|
||||
.Pa /boot/kernel/kernel .
|
||||
.Pa /boot/kernel .
|
||||
.It Fl n Ar rate
|
||||
Set the default sampling rate for subsequent sampling mode
|
||||
PMCs specified on the command line.
|
||||
@ -179,6 +201,14 @@ The default is to send output to
|
||||
Allocate a process mode counting PMC measuring hardware events
|
||||
specified in
|
||||
.Ar event-spec .
|
||||
.It Fl q
|
||||
Decrease verbosity.
|
||||
.It Fl r Ar fsroot
|
||||
Set the top of the filesystem hierarchy under which executables
|
||||
are located to argument
|
||||
.Ar fsroot .
|
||||
The default is
|
||||
.Pa / .
|
||||
.It Fl s Ar event-spec
|
||||
Allocate a system mode counting PMC measuring hardware events
|
||||
specified in
|
||||
@ -189,6 +219,8 @@ Attach all process mode PMCs allocated to the process with PID
|
||||
The option is not allowed in conjunction with specifying a
|
||||
command using
|
||||
.Ar command .
|
||||
.It Fl v
|
||||
Increase verbosity.
|
||||
.It Fl w Ar secs
|
||||
Print the values of all counting mode PMCs every
|
||||
.Ar secs
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2005, Joseph Koshy
|
||||
* Copyright (c) 2003-2006, Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <pmc.h>
|
||||
@ -110,7 +111,7 @@ pmcstat_cleanup(struct pmcstat_args *a)
|
||||
}
|
||||
|
||||
if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))
|
||||
pmcstat_shutdown_logging();
|
||||
pmcstat_shutdown_logging(a);
|
||||
}
|
||||
|
||||
void
|
||||
@ -317,6 +318,7 @@ pmcstat_show_usage(void)
|
||||
"\t -C\t\t (toggle) show cumulative counts\n"
|
||||
"\t -D path\t create profiles in directory \"path\"\n"
|
||||
"\t -E\t\t (toggle) show counts at process exit\n"
|
||||
"\t -M file\t print executable/gmon file map to \"file\"\n"
|
||||
"\t -O file\t send log output to \"file\"\n"
|
||||
"\t -P spec\t allocate a process-private sampling PMC\n"
|
||||
"\t -R file\t read events from \"file\"\n"
|
||||
@ -325,12 +327,15 @@ pmcstat_show_usage(void)
|
||||
"\t -c cpu\t\t set cpu for subsequent system-wide PMCs\n"
|
||||
"\t -d\t\t (toggle) track descendants\n"
|
||||
"\t -g\t\t produce gprof(1) compatible profiles\n"
|
||||
"\t -k file\t set the path to the kernel\n"
|
||||
"\t -k dir\t set the path to the kernel\n"
|
||||
"\t -n rate\t set sampling rate\n"
|
||||
"\t -o file\t send print output to \"file\"\n"
|
||||
"\t -p spec\t allocate a process-private counting PMC\n"
|
||||
"\t -q\t\t suppress verbosity\n"
|
||||
"\t -r fsroot\t specify FS root directory\n"
|
||||
"\t -s spec\t allocate a system-wide counting PMC\n"
|
||||
"\t -t pid\t\t attach to running process with pid \"pid\"\n"
|
||||
"\t -v\t\t increase verbosity\n"
|
||||
"\t -w secs\t set printing time interval"
|
||||
);
|
||||
}
|
||||
@ -350,7 +355,7 @@ main(int argc, char **argv)
|
||||
int pipefd[2];
|
||||
int use_cumulative_counts;
|
||||
pid_t pid;
|
||||
char *end;
|
||||
char *end, *tmp;
|
||||
const char *errmsg;
|
||||
enum pmcstat_state runstate;
|
||||
struct pmc_driverstats ds_start, ds_end;
|
||||
@ -359,6 +364,7 @@ main(int argc, char **argv)
|
||||
struct kevent kev;
|
||||
struct winsize ws;
|
||||
struct stat sb;
|
||||
char buffer[PATH_MAX];
|
||||
|
||||
check_driver_stats = 0;
|
||||
current_cpu = 0;
|
||||
@ -369,19 +375,22 @@ main(int argc, char **argv)
|
||||
use_cumulative_counts = 0;
|
||||
args.pa_required = 0;
|
||||
args.pa_flags = 0;
|
||||
args.pa_verbosity = 1;
|
||||
args.pa_pid = (pid_t) -1;
|
||||
args.pa_logfd = -1;
|
||||
args.pa_fsroot = "";
|
||||
args.pa_kernel = strdup("/boot/kernel");
|
||||
args.pa_samplesdir = ".";
|
||||
args.pa_kernel = "/boot/kernel/kernel";
|
||||
args.pa_printfile = stderr;
|
||||
args.pa_interval = DEFAULT_WAIT_INTERVAL;
|
||||
args.pa_mapfilename = NULL;
|
||||
STAILQ_INIT(&args.pa_head);
|
||||
bzero(&ds_start, sizeof(ds_start));
|
||||
bzero(&ds_end, sizeof(ds_end));
|
||||
ev = NULL;
|
||||
|
||||
while ((option = getopt(argc, argv, "CD:EO:P:R:S:Wc:dgk:n:o:p:s:t:w:"))
|
||||
!= -1)
|
||||
while ((option = getopt(argc, argv,
|
||||
"CD:EM:O:P:R:S:Wc:dgk:n:o:p:qr:s:t:vw:")) != -1)
|
||||
switch (option) {
|
||||
case 'C': /* cumulative values */
|
||||
use_cumulative_counts = !use_cumulative_counts;
|
||||
@ -419,7 +428,8 @@ main(int argc, char **argv)
|
||||
break;
|
||||
|
||||
case 'k': /* pathname to the kernel */
|
||||
args.pa_kernel = optarg;
|
||||
free(args.pa_kernel);
|
||||
args.pa_kernel = strdup(optarg);
|
||||
args.pa_required |= FLAG_DO_GPROF;
|
||||
args.pa_flags |= FLAG_HAS_KERNELPATH;
|
||||
break;
|
||||
@ -430,6 +440,10 @@ main(int argc, char **argv)
|
||||
FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE);
|
||||
break;
|
||||
|
||||
case 'M': /* mapfile */
|
||||
args.pa_mapfilename = optarg;
|
||||
break;
|
||||
|
||||
case 'p': /* process virtual counting PMC */
|
||||
case 's': /* system-wide counting PMC */
|
||||
case 'P': /* process virtual sampling PMC */
|
||||
@ -523,6 +537,14 @@ main(int argc, char **argv)
|
||||
args.pa_flags |= FLAG_HAS_OUTPUT_LOGFILE;
|
||||
break;
|
||||
|
||||
case 'q': /* quiet mode */
|
||||
args.pa_verbosity = 0;
|
||||
break;
|
||||
|
||||
case 'r': /* root FS path */
|
||||
args.pa_fsroot = optarg;
|
||||
break;
|
||||
|
||||
case 'R': /* read an existing log file */
|
||||
if (args.pa_logparser != NULL)
|
||||
errx(EX_USAGE, "ERROR: option -R may only be "
|
||||
@ -544,6 +566,10 @@ main(int argc, char **argv)
|
||||
args.pa_pid = pid;
|
||||
break;
|
||||
|
||||
case 'v': /* verbose */
|
||||
args.pa_verbosity++;
|
||||
break;
|
||||
|
||||
case 'w': /* wait interval */
|
||||
interval = strtod(optarg, &end);
|
||||
if (*end != '\0' || interval <= 0)
|
||||
@ -658,14 +684,22 @@ main(int argc, char **argv)
|
||||
"ERROR: option -O is used only with options "
|
||||
"-E, -P, -S and -W.");
|
||||
|
||||
/* -D dir and -k kernel path require -g */
|
||||
/* -D dir and -k kernel path require -g or -R */
|
||||
if ((args.pa_flags & FLAG_HAS_KERNELPATH) &&
|
||||
((args.pa_flags & FLAG_DO_GPROF) == 0))
|
||||
errx(EX_USAGE, "ERROR: option -k is only used with -g.");
|
||||
(args.pa_flags & FLAG_DO_GPROF) == 0 &&
|
||||
(args.pa_flags & FLAG_READ_LOGFILE) == 0)
|
||||
errx(EX_USAGE, "ERROR: option -k is only used with -g/-R.");
|
||||
|
||||
if ((args.pa_flags & FLAG_HAS_SAMPLESDIR) &&
|
||||
((args.pa_flags & FLAG_DO_GPROF) == 0))
|
||||
errx(EX_USAGE, "ERROR: option -D is only used with -g.");
|
||||
(args.pa_flags & FLAG_DO_GPROF) == 0 &&
|
||||
(args.pa_flags & FLAG_READ_LOGFILE) == 0)
|
||||
errx(EX_USAGE, "ERROR: option -D is only used with -g/-R.");
|
||||
|
||||
/* -M mapfile requires -g or -R */
|
||||
if (args.pa_mapfilename != NULL &&
|
||||
(args.pa_flags & FLAG_DO_GPROF) == 0 &&
|
||||
(args.pa_flags & FLAG_READ_LOGFILE) == 0)
|
||||
errx(EX_USAGE, "ERROR: option -M is only used with -g/-R.");
|
||||
|
||||
/*
|
||||
* Disallow textual output of sampling PMCs if counting PMCs
|
||||
@ -678,6 +712,35 @@ main(int argc, char **argv)
|
||||
errx(EX_USAGE, "ERROR: option -O is required if counting and "
|
||||
"sampling PMCs are specified together.");
|
||||
|
||||
/*
|
||||
* Check if "-k kerneldir" was specified, and if whether 'kerneldir'
|
||||
* actually refers to a a file. If so, use `dirname path` to determine
|
||||
* the kernel directory.
|
||||
*/
|
||||
if (args.pa_flags & FLAG_HAS_KERNELPATH) {
|
||||
(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));
|
||||
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've been asked to process a log file, do that and exit */
|
||||
if (args.pa_flags & FLAG_READ_LOGFILE) {
|
||||
/*
|
||||
@ -688,13 +751,14 @@ main(int argc, char **argv)
|
||||
args.pa_flags |= FLAG_DO_PRINT;
|
||||
|
||||
pmcstat_initialize_logging(&args);
|
||||
if ((args.pa_logfd = pmcstat_open(args.pa_inputpath,
|
||||
if ((args.pa_logfd = pmcstat_open_log(args.pa_inputpath,
|
||||
PMCSTAT_OPEN_FOR_READ)) < 0)
|
||||
err(EX_OSERR, "ERROR: Cannot open \"%s\" for "
|
||||
"reading", args.pa_inputpath);
|
||||
if ((args.pa_logparser = pmclog_open(args.pa_logfd)) == NULL)
|
||||
err(EX_OSERR, "ERROR: Cannot create parser");
|
||||
pmcstat_process_log(&args);
|
||||
pmcstat_shutdown_logging(&args);
|
||||
exit(EX_OK);
|
||||
}
|
||||
|
||||
@ -721,7 +785,8 @@ main(int argc, char **argv)
|
||||
*/
|
||||
if (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) {
|
||||
if (args.pa_outputpath) {
|
||||
if ((args.pa_logfd = pmcstat_open(args.pa_outputpath,
|
||||
if ((args.pa_logfd =
|
||||
pmcstat_open_log(args.pa_outputpath,
|
||||
PMCSTAT_OPEN_FOR_WRITE)) < 0)
|
||||
err(EX_OSERR, "ERROR: Cannot open \"%s\" for "
|
||||
"writing", args.pa_outputpath);
|
||||
@ -966,17 +1031,21 @@ main(int argc, char **argv)
|
||||
|
||||
pmcstat_cleanup(&args);
|
||||
|
||||
free(args.pa_kernel);
|
||||
|
||||
/* check if the driver lost any samples or events */
|
||||
if (check_driver_stats) {
|
||||
if (pmc_get_driver_stats(&ds_end) < 0)
|
||||
err(EX_OSERR, "ERROR: Cannot retrieve driver "
|
||||
"statistics");
|
||||
if (ds_start.pm_intr_bufferfull != ds_end.pm_intr_bufferfull)
|
||||
if (ds_start.pm_intr_bufferfull != ds_end.pm_intr_bufferfull &&
|
||||
args.pa_verbosity > 0)
|
||||
warnx("WARNING: some samples were dropped. Please "
|
||||
"consider tuning the \"kern.hwpmc.nsamples\" "
|
||||
"tunable.");
|
||||
if (ds_start.pm_buffer_requests_failed !=
|
||||
ds_end.pm_buffer_requests_failed)
|
||||
ds_end.pm_buffer_requests_failed &&
|
||||
args.pa_verbosity > 0)
|
||||
warnx("WARNING: some events were discarded. Please "
|
||||
"consider tuning the \"kern.hwpmc.nbuffers\" "
|
||||
"tunable.");
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2005, Joseph Koshy
|
||||
* Copyright (c) 2005-2006, Joseph Koshy
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -93,14 +93,17 @@ struct pmcstat_ev {
|
||||
struct pmcstat_args {
|
||||
int pa_flags; /* argument flags */
|
||||
int pa_required; /* required features */
|
||||
int pa_verbosity; /* verbosity level */
|
||||
pid_t pa_pid; /* attached to pid */
|
||||
FILE *pa_printfile; /* where to send printed output */
|
||||
int pa_logfd; /* output log file */
|
||||
char *pa_inputpath; /* path to input log */
|
||||
char *pa_outputpath; /* path to output log */
|
||||
void *pa_logparser; /* log file parser */
|
||||
const char *pa_kernel; /* pathname of the kernel */
|
||||
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 */
|
||||
double pa_interval; /* printing interval in seconds */
|
||||
int pa_argc;
|
||||
char **pa_argv;
|
||||
@ -111,17 +114,15 @@ struct pmcstat_args {
|
||||
void pmcstat_cleanup(struct pmcstat_args *_a);
|
||||
int pmcstat_close_log(struct pmcstat_args *_a);
|
||||
void pmcstat_initialize_logging(struct pmcstat_args *_a);
|
||||
int pmcstat_open(const char *_p, int _mode);
|
||||
int pmcstat_open_log(const char *_p, int _mode);
|
||||
void pmcstat_print_counters(struct pmcstat_args *_a);
|
||||
void pmcstat_print_headers(struct pmcstat_args *_a);
|
||||
void pmcstat_print_pmcs(struct pmcstat_args *_a);
|
||||
void pmcstat_setup_process(struct pmcstat_args *_a);
|
||||
void pmcstat_show_usage(void);
|
||||
void pmcstat_shutdown_logging(void);
|
||||
void pmcstat_shutdown_logging(struct pmcstat_args *_a);
|
||||
void pmcstat_start_pmcs(struct pmcstat_args *_a);
|
||||
void pmcstat_start_process(struct pmcstat_args *_a);
|
||||
int pmcstat_process_log(struct pmcstat_args *_a);
|
||||
int pmcstat_print_log(struct pmcstat_args *_a);
|
||||
int pmcstat_convert_log(struct pmcstat_args *_a);
|
||||
|
||||
#endif /* _PMCSTAT_H_ */
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user