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:
jkoshy 2006-03-26 12:20:54 +00:00
parent a3688cc84e
commit 48e5e4792d
16 changed files with 1399 additions and 508 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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.

View File

@ -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 */
/*

View File

@ -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>

View File

@ -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 "."

View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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.");

View File

@ -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