pmc: convert native to jsonl and track TSC value of samples
- add '-j' options to filter to enable converting native pmc log format to json lines format to enable the use of scripts and external tooling % pmc filter -j pmc.log pmc.jsonl - Record the tsc value in sampling interrupts as opposed to recording nanotime when the sample is copied to a global log in hardclock - potentially many milliseconds later. - At initialize record the tsc_freq and the time of day to give us an offset for translating the tsc values in callchain records
This commit is contained in:
parent
1b000b505b
commit
f992dd4b5c
@ -202,7 +202,9 @@ _libdl= libdl
|
||||
.endif
|
||||
|
||||
SUBDIR.${MK_OPENSSL}+= libmp
|
||||
.if (${COMPILER_TYPE} == "clang" || (${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} >= 60100))
|
||||
SUBDIR.${MK_PMC}+= libpmc libpmcstat
|
||||
.endif
|
||||
SUBDIR.${MK_RADIUS_SUPPORT}+= libradius
|
||||
SUBDIR.${MK_SENDMAIL}+= libmilter libsm libsmdb libsmutil
|
||||
SUBDIR.${MK_TELNET}+= libtelnet
|
||||
|
@ -3,8 +3,8 @@
|
||||
PACKAGE=lib${LIB}
|
||||
LIB= pmc
|
||||
|
||||
SRCS= libpmc.c pmclog.c libpmc_pmu_util.c
|
||||
INCS= pmc.h pmclog.h
|
||||
SRCS= libpmc.c pmclog.c libpmc_pmu_util.c libpmc_json.cc
|
||||
INCS= pmc.h pmclog.h pmcformat.h
|
||||
|
||||
CFLAGS+= -I${.CURDIR}
|
||||
|
||||
|
393
lib/libpmc/libpmc_json.cc
Normal file
393
lib/libpmc/libpmc_json.cc
Normal file
@ -0,0 +1,393 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2018, Matthew Macy
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <err.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <pmc.h>
|
||||
#include <pmclog.h>
|
||||
#include <assert.h>
|
||||
#include <string>
|
||||
#include <sysexits.h>
|
||||
#include <pmcformat.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
static const char *typenames[] = {
|
||||
"",
|
||||
"{\"type\": \"closelog\"}\n",
|
||||
"{\"type\": \"dropnotify\"}\n",
|
||||
"{\"type\": \"initialize\"",
|
||||
"",
|
||||
"{\"type\": \"pmcallocate\"",
|
||||
"{\"type\": \"pmcattach\"",
|
||||
"{\"type\": \"pmcdetach\"",
|
||||
"{\"type\": \"proccsw\"",
|
||||
"{\"type\": \"procexec\"",
|
||||
"{\"type\": \"procexit\"",
|
||||
"{\"type\": \"procfork\"",
|
||||
"{\"type\": \"sysexit\"",
|
||||
"{\"type\": \"userdata\"",
|
||||
"{\"type\": \"map_in\"",
|
||||
"{\"type\": \"map_out\"",
|
||||
"{\"type\": \"callchain\"",
|
||||
"{\"type\": \"pmcallocatedyn\"",
|
||||
"{\"type\": \"thr_create\"",
|
||||
"{\"type\": \"thr_exit\"",
|
||||
"{\"type\": \"proc_create\"",
|
||||
};
|
||||
|
||||
static string
|
||||
startentry(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[128];
|
||||
|
||||
snprintf(eventbuf, sizeof(eventbuf), "%s, \"tsc\": \"%jd\"",
|
||||
typenames[ev->pl_type], (intmax_t)ev->pl_ts.tv_sec);
|
||||
return (string(eventbuf));
|
||||
}
|
||||
|
||||
static string
|
||||
initialize_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[256];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"version\": \"0x%08x\", \"arch\": \"0x%08x\", \"cpuid\": \"%s\", "
|
||||
"\"tsc_freq\": \"%jd\", \"sec\": \"%jd\", \"nsec\": \"%jd\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_i.pl_version, ev->pl_u.pl_i.pl_arch,
|
||||
ev->pl_u.pl_i.pl_cpuid, (uintmax_t)ev->pl_u.pl_i.pl_tsc_freq,
|
||||
(uintmax_t)ev->pl_u.pl_i.pl_ts.tv_sec, (uintmax_t)ev->pl_u.pl_i.pl_ts.tv_nsec);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
pmcallocate_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[256];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"pmcid\": \"0x%08x\", \"event\": \"0x%08x\", \"flags\": \"0x%08x\", "
|
||||
"\"rate\": \"%jd\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_a.pl_pmcid, ev->pl_u.pl_a.pl_event,
|
||||
ev->pl_u.pl_a.pl_flags, (intmax_t)ev->pl_u.pl_a.pl_rate);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
pmcattach_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[2048];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\", \"pathname\": \"%s\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_t.pl_pmcid, ev->pl_u.pl_t.pl_pid,
|
||||
ev->pl_u.pl_t.pl_pathname);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
pmcdetach_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[128];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_d.pl_pmcid, ev->pl_u.pl_d.pl_pid);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
|
||||
static string
|
||||
proccsw_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[128];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf), "%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\" "
|
||||
"\"tid\": \"%d\", \"value\": \"0x%016jx\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_c.pl_pmcid, ev->pl_u.pl_c.pl_pid,
|
||||
ev->pl_u.pl_c.pl_tid, (uintmax_t)ev->pl_u.pl_c.pl_value);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
procexec_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[2048];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\", "
|
||||
"\"start\": \"0x%016jx\", \"pathname\": \"%s\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_x.pl_pmcid, ev->pl_u.pl_x.pl_pid,
|
||||
ev->pl_u.pl_x.pl_entryaddr, ev->pl_u.pl_x.pl_pathname);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
procexit_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[128];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\", "
|
||||
"\"value\": \"0x%016jx\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_e.pl_pmcid, ev->pl_u.pl_e.pl_pid,
|
||||
(uintmax_t)ev->pl_u.pl_e.pl_value);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
procfork_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[128];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"oldpid\": \"%d\", \"newpid\": \"%d\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_f.pl_oldpid, ev->pl_u.pl_f.pl_newpid);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
sysexit_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[128];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf), "%s, \"pid\": \"%d\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_se.pl_pid);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
userdata_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[128];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf), "%s, \"userdata\": \"0x%08x\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_u.pl_userdata);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
map_in_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[2048];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf), "%s, \"pid\": \"%d\", "
|
||||
"\"start\": \"0x%016jx\", \"pathname\": \"%s\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_mi.pl_pid,
|
||||
(uintmax_t)ev->pl_u.pl_mi.pl_start, ev->pl_u.pl_mi.pl_pathname);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
map_out_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[256];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf), "%s, \"pid\": \"%d\", "
|
||||
"\"start\": \"0x%016jx\", \"end\": \"0x%016jx\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_mi.pl_pid,
|
||||
(uintmax_t)ev->pl_u.pl_mi.pl_start,
|
||||
(uintmax_t)ev->pl_u.pl_mo.pl_end);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
callchain_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[1024];
|
||||
string result;
|
||||
uint32_t i;
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"pmcid\": \"0x%08x\", \"pid\": \"%d\", \"tid\": \"%d\", "
|
||||
"\"cpuflags\": \"0x%08x\", \"cpuflags2\": \"0x%08x\", \"pc\": [ ",
|
||||
startent.c_str(), ev->pl_u.pl_cc.pl_pmcid, ev->pl_u.pl_cc.pl_pid,
|
||||
ev->pl_u.pl_cc.pl_tid, ev->pl_u.pl_cc.pl_cpuflags, ev->pl_u.pl_cc.pl_cpuflags2);
|
||||
result = string(eventbuf);
|
||||
for (i = 0; i < ev->pl_u.pl_cc.pl_npc - 1; i++) {
|
||||
snprintf(eventbuf, sizeof(eventbuf), "\"0x%016jx\", ", ev->pl_u.pl_cc.pl_pc[i]);
|
||||
result += string(eventbuf);
|
||||
}
|
||||
snprintf(eventbuf, sizeof(eventbuf), "\"0x%016jx\"]}\n", ev->pl_u.pl_cc.pl_pc[i]);
|
||||
result += string(eventbuf);
|
||||
return (result);
|
||||
}
|
||||
|
||||
static string
|
||||
pmcallocatedyn_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[2048];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"pmcid\": \"0x%08x\", \"event\": \"%d\", \"flags\": \"0x%08x\", \"evname\": \"%s\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_ad.pl_pmcid, ev->pl_u.pl_ad.pl_event,
|
||||
ev->pl_u.pl_ad.pl_flags, ev->pl_u.pl_ad.pl_evname);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
proccreate_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[2048];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"pid\": \"%d\", \"flags\": \"0x%08x\", \"pcomm\": \"%s\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_pc.pl_pid,
|
||||
ev->pl_u.pl_pc.pl_flags, ev->pl_u.pl_pc.pl_pcomm);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
threadcreate_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[2048];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf),
|
||||
"%s, \"tid\": \"%d\", \"pid\": \"%d\", \"flags\": \"0x%08x\", \"tdname\": \"%s\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_tc.pl_tid, ev->pl_u.pl_tc.pl_pid,
|
||||
ev->pl_u.pl_tc.pl_flags, ev->pl_u.pl_tc.pl_tdname);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
threadexit_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
char eventbuf[256];
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
snprintf(eventbuf, sizeof(eventbuf), "%s, \"tid\": \"%d\"}\n",
|
||||
startent.c_str(), ev->pl_u.pl_te.pl_tid);
|
||||
return string(eventbuf);
|
||||
}
|
||||
|
||||
static string
|
||||
stub_to_json(struct pmclog_ev *ev)
|
||||
{
|
||||
string startent;
|
||||
|
||||
startent = startentry(ev);
|
||||
startent += string("}\n");
|
||||
return startent;
|
||||
}
|
||||
|
||||
typedef string (*jconv) (struct pmclog_ev*);
|
||||
|
||||
static jconv jsonconvert[] = {
|
||||
NULL,
|
||||
stub_to_json,
|
||||
stub_to_json,
|
||||
initialize_to_json,
|
||||
NULL,
|
||||
pmcallocate_to_json,
|
||||
pmcattach_to_json,
|
||||
pmcdetach_to_json,
|
||||
proccsw_to_json,
|
||||
procexec_to_json,
|
||||
procexit_to_json,
|
||||
procfork_to_json,
|
||||
sysexit_to_json,
|
||||
userdata_to_json,
|
||||
map_in_to_json,
|
||||
map_out_to_json,
|
||||
callchain_to_json,
|
||||
pmcallocatedyn_to_json,
|
||||
threadcreate_to_json,
|
||||
threadexit_to_json,
|
||||
proccreate_to_json,
|
||||
};
|
||||
|
||||
string
|
||||
event_to_json(struct pmclog_ev *ev){
|
||||
|
||||
switch (ev->pl_type) {
|
||||
case PMCLOG_TYPE_DROPNOTIFY:
|
||||
case PMCLOG_TYPE_CLOSELOG:
|
||||
case PMCLOG_TYPE_INITIALIZE:
|
||||
case PMCLOG_TYPE_PMCALLOCATE:
|
||||
case PMCLOG_TYPE_PMCATTACH:
|
||||
case PMCLOG_TYPE_PMCDETACH:
|
||||
case PMCLOG_TYPE_PROCCSW:
|
||||
case PMCLOG_TYPE_PROCEXEC:
|
||||
case PMCLOG_TYPE_PROCEXIT:
|
||||
case PMCLOG_TYPE_PROCFORK:
|
||||
case PMCLOG_TYPE_SYSEXIT:
|
||||
case PMCLOG_TYPE_USERDATA:
|
||||
case PMCLOG_TYPE_MAP_IN:
|
||||
case PMCLOG_TYPE_MAP_OUT:
|
||||
case PMCLOG_TYPE_CALLCHAIN:
|
||||
case PMCLOG_TYPE_PMCALLOCATEDYN:
|
||||
case PMCLOG_TYPE_THR_CREATE:
|
||||
case PMCLOG_TYPE_THR_EXIT:
|
||||
case PMCLOG_TYPE_PROC_CREATE:
|
||||
return jsonconvert[ev->pl_type](ev);
|
||||
default:
|
||||
errx(EX_USAGE, "ERROR: unrecognized event type: %d\n", ev->pl_type);
|
||||
}
|
||||
}
|
||||
|
33
lib/libpmc/pmcformat.h
Normal file
33
lib/libpmc/pmcformat.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2018, Matthew Macy
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
*/
|
||||
#ifndef __PMCFORMAT_H_
|
||||
#define __PMCFORMAT_H_
|
||||
std::string event_to_json(struct pmclog_ev *ev);
|
||||
#endif
|
@ -52,7 +52,7 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "libpmcinternal.h"
|
||||
|
||||
#define PMCLOG_BUFFER_SIZE 4096
|
||||
#define PMCLOG_BUFFER_SIZE 512*1024
|
||||
|
||||
/*
|
||||
* API NOTES
|
||||
@ -260,6 +260,7 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
uint32_t h, *le, npc, noop;
|
||||
enum pmclog_parser_state e;
|
||||
struct pmclog_parse_state *ps;
|
||||
struct pmclog_header *ph;
|
||||
|
||||
ps = (struct pmclog_parse_state *) cookie;
|
||||
|
||||
@ -278,8 +279,9 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
|
||||
PMCLOG_INITIALIZE_READER(le, ps->ps_saved);
|
||||
ev->pl_data = le;
|
||||
PMCLOG_READ32(le,h);
|
||||
ph = (struct pmclog_header *)(uintptr_t)le;
|
||||
|
||||
h = ph->pl_header;
|
||||
if (!PMCLOG_HEADER_CHECK_MAGIC(h)) {
|
||||
printf("bad magic\n");
|
||||
ps->ps_state = PL_STATE_ERROR;
|
||||
@ -288,8 +290,8 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
}
|
||||
|
||||
/* copy out the time stamp */
|
||||
PMCLOG_READ32(le,ev->pl_ts.tv_sec);
|
||||
PMCLOG_READ32(le,ev->pl_ts.tv_nsec);
|
||||
ev->pl_ts.tv_sec = ph->pl_tsc;
|
||||
le += sizeof(*ph)/4;
|
||||
|
||||
evlen = PMCLOG_HEADER_TO_LENGTH(h);
|
||||
|
||||
@ -310,7 +312,6 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_cc.pl_tid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_cc.pl_pmcid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_cc.pl_cpuflags);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_cc.pl_cpuflags2);
|
||||
PMCLOG_GET_CALLCHAIN_SIZE(ev->pl_u.pl_cc.pl_npc,evlen);
|
||||
for (npc = 0; npc < ev->pl_u.pl_cc.pl_npc; npc++)
|
||||
PMCLOG_READADDR(le,ev->pl_u.pl_cc.pl_pc[npc]);
|
||||
@ -326,6 +327,9 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
case PMCLOG_TYPE_INITIALIZE:
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_i.pl_version);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_i.pl_arch);
|
||||
PMCLOG_READ64(le,ev->pl_u.pl_i.pl_tsc_freq);
|
||||
memcpy(&ev->pl_u.pl_i.pl_ts, le, sizeof(struct timespec));
|
||||
le += sizeof(struct timespec)/4;
|
||||
PMCLOG_READSTRING(le, ev->pl_u.pl_i.pl_cpuid, PMC_CPUID_LEN);
|
||||
memcpy(ev->pl_u.pl_i.pl_cpuid, le, PMC_CPUID_LEN);
|
||||
ps->ps_cpuid = strdup(ev->pl_u.pl_i.pl_cpuid);
|
||||
@ -336,11 +340,13 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
case PMCLOG_TYPE_MAP_IN:
|
||||
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_map_in);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_mi.pl_pid);
|
||||
PMCLOG_READ32(le,noop);
|
||||
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_READ32(le,noop);
|
||||
PMCLOG_READADDR(le,ev->pl_u.pl_mo.pl_start);
|
||||
PMCLOG_READADDR(le,ev->pl_u.pl_mo.pl_end);
|
||||
break;
|
||||
@ -348,6 +354,7 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_pmcid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_event);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_a.pl_flags);
|
||||
PMCLOG_READ32(le,noop);
|
||||
PMCLOG_READ64(le,ev->pl_u.pl_a.pl_rate);
|
||||
ev->pl_u.pl_a.pl_evname = pmc_pmu_event_get_by_idx(ps->ps_cpuid, ev->pl_u.pl_a.pl_event);
|
||||
if (ev->pl_u.pl_a.pl_evname != NULL)
|
||||
@ -363,6 +370,7 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_ad.pl_pmcid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_ad.pl_event);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_ad.pl_flags);
|
||||
PMCLOG_READ32(le,noop);
|
||||
PMCLOG_READSTRING(le,ev->pl_u.pl_ad.pl_evname,PMC_NAME_MAX);
|
||||
break;
|
||||
case PMCLOG_TYPE_PMCATTACH:
|
||||
@ -376,8 +384,8 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_d.pl_pid);
|
||||
break;
|
||||
case PMCLOG_TYPE_PROCCSW:
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_c.pl_pmcid);
|
||||
PMCLOG_READ64(le,ev->pl_u.pl_c.pl_value);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_c.pl_pmcid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_c.pl_pid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_c.pl_tid);
|
||||
break;
|
||||
@ -385,14 +393,12 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_procexec);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pmcid);
|
||||
PMCLOG_READ32(le,noop);
|
||||
PMCLOG_READADDR(le,ev->pl_u.pl_x.pl_entryaddr);
|
||||
PMCLOG_READSTRING(le,ev->pl_u.pl_x.pl_pathname,pathlen);
|
||||
break;
|
||||
case PMCLOG_TYPE_PROCEXIT:
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_e.pl_pmcid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_e.pl_pid);
|
||||
PMCLOG_READ32(le,noop);
|
||||
PMCLOG_READ64(le,ev->pl_u.pl_e.pl_value);
|
||||
break;
|
||||
case PMCLOG_TYPE_PROCFORK:
|
||||
@ -409,6 +415,7 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_tc.pl_tid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_tc.pl_pid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_tc.pl_flags);
|
||||
PMCLOG_READ32(le,noop);
|
||||
memcpy(ev->pl_u.pl_tc.pl_tdname, le, MAXCOMLEN+1);
|
||||
break;
|
||||
case PMCLOG_TYPE_THR_EXIT:
|
||||
@ -417,7 +424,6 @@ pmclog_get_event(void *cookie, char **data, ssize_t *len,
|
||||
case PMCLOG_TYPE_PROC_CREATE:
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_pc.pl_pid);
|
||||
PMCLOG_READ32(le,ev->pl_u.pl_pc.pl_flags);
|
||||
PMCLOG_READ32(le,noop);
|
||||
memcpy(ev->pl_u.pl_pc.pl_pcomm, le, MAXCOMLEN+1);
|
||||
break;
|
||||
default: /* unknown record type */
|
||||
|
@ -64,6 +64,8 @@ struct pmclog_ev_closelog {
|
||||
struct pmclog_ev_initialize {
|
||||
uint32_t pl_version;
|
||||
uint32_t pl_arch;
|
||||
uint64_t pl_tsc_freq;
|
||||
struct timespec pl_ts;
|
||||
char pl_cpuid[PATH_MAX];
|
||||
};
|
||||
|
||||
|
@ -61,6 +61,10 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
#include <machine/clock.h>
|
||||
#endif
|
||||
|
||||
#ifdef NUMA
|
||||
#define NDOMAINS vm_ndomains
|
||||
#define curdomain PCPU_GET(domain)
|
||||
@ -124,29 +128,38 @@ static struct mtx pmc_kthread_mtx; /* sleep lock */
|
||||
((L) & 0xFFFF))
|
||||
|
||||
/* reserve LEN bytes of space and initialize the entry header */
|
||||
#define _PMCLOG_RESERVE_SAFE(PO,TYPE,LEN,ACTION) do { \
|
||||
#define _PMCLOG_RESERVE_SAFE(PO,TYPE,LEN,ACTION, TSC) do { \
|
||||
uint32_t *_le; \
|
||||
int _len = roundup((LEN), sizeof(uint32_t)); \
|
||||
struct pmclog_header *ph; \
|
||||
if ((_le = pmclog_reserve((PO), _len)) == NULL) { \
|
||||
ACTION; \
|
||||
} \
|
||||
*_le = _PMCLOG_TO_HEADER(TYPE,_len); \
|
||||
_le += 3 /* skip over timestamp */
|
||||
ACTION; \
|
||||
} \
|
||||
ph = (struct pmclog_header *)_le; \
|
||||
ph->pl_header =_PMCLOG_TO_HEADER(TYPE,_len); \
|
||||
ph->pl_tsc = (TSC); \
|
||||
_le += sizeof(*ph)/4 /* skip over timestamp */
|
||||
|
||||
/* reserve LEN bytes of space and initialize the entry header */
|
||||
#define _PMCLOG_RESERVE(PO,TYPE,LEN,ACTION) do { \
|
||||
uint32_t *_le; \
|
||||
int _len = roundup((LEN), sizeof(uint32_t)); \
|
||||
int _len = roundup((LEN), sizeof(uint32_t)); \
|
||||
uint64_t tsc; \
|
||||
struct pmclog_header *ph; \
|
||||
tsc = pmc_rdtsc(); \
|
||||
spinlock_enter(); \
|
||||
if ((_le = pmclog_reserve((PO), _len)) == NULL) { \
|
||||
spinlock_exit(); \
|
||||
ACTION; \
|
||||
} \
|
||||
*_le = _PMCLOG_TO_HEADER(TYPE,_len); \
|
||||
_le += 3 /* skip over timestamp */
|
||||
ph = (struct pmclog_header *)_le; \
|
||||
ph->pl_header =_PMCLOG_TO_HEADER(TYPE,_len); \
|
||||
ph->pl_tsc = tsc; \
|
||||
_le += sizeof(*ph)/4 /* skip over timestamp */
|
||||
|
||||
|
||||
#define PMCLOG_RESERVE_SAFE(P,T,L) _PMCLOG_RESERVE_SAFE(P,T,L,return)
|
||||
|
||||
#define PMCLOG_RESERVE_SAFE(P,T,L,TSC) _PMCLOG_RESERVE_SAFE(P,T,L,return,TSC)
|
||||
#define PMCLOG_RESERVE(P,T,L) _PMCLOG_RESERVE(P,T,L,return)
|
||||
#define PMCLOG_RESERVE_WITH_ERROR(P,T,L) _PMCLOG_RESERVE(P,T,L, \
|
||||
error=ENOMEM;goto error)
|
||||
@ -181,32 +194,32 @@ static struct mtx pmc_kthread_mtx; /* sleep lock */
|
||||
} while (0)
|
||||
|
||||
|
||||
#define TSDELTA 4
|
||||
/*
|
||||
* Assertions about the log file format.
|
||||
*/
|
||||
|
||||
CTASSERT(sizeof(struct pmclog_callchain) == 8*4 +
|
||||
CTASSERT(sizeof(struct pmclog_callchain) == 7*4 + TSDELTA +
|
||||
PMC_CALLCHAIN_DEPTH_MAX*sizeof(uintfptr_t));
|
||||
CTASSERT(sizeof(struct pmclog_closelog) == 4*4);
|
||||
CTASSERT(sizeof(struct pmclog_dropnotify) == 4*4);
|
||||
CTASSERT(sizeof(struct pmclog_map_in) == PATH_MAX +
|
||||
4*4 + sizeof(uintfptr_t));
|
||||
CTASSERT(sizeof(struct pmclog_closelog) == 3*4 + TSDELTA);
|
||||
CTASSERT(sizeof(struct pmclog_dropnotify) == 3*4 + TSDELTA);
|
||||
CTASSERT(sizeof(struct pmclog_map_in) == PATH_MAX + TSDELTA +
|
||||
5*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_pmcallocate) == 8*4);
|
||||
CTASSERT(sizeof(struct pmclog_pmcattach) == 6*4 + PATH_MAX);
|
||||
CTASSERT(offsetof(struct pmclog_pmcattach,pl_pathname) == 6*4);
|
||||
CTASSERT(sizeof(struct pmclog_pmcdetach) == 6*4);
|
||||
CTASSERT(sizeof(struct pmclog_proccsw) == 6*4 + 8);
|
||||
CTASSERT(sizeof(struct pmclog_procexec) == 6*4 + PATH_MAX +
|
||||
5*4 + TSDELTA + sizeof(uintfptr_t));
|
||||
CTASSERT(sizeof(struct pmclog_map_out) == 5*4 + 2*sizeof(uintfptr_t) + TSDELTA);
|
||||
CTASSERT(sizeof(struct pmclog_pmcallocate) == 9*4 + TSDELTA);
|
||||
CTASSERT(sizeof(struct pmclog_pmcattach) == 5*4 + PATH_MAX + TSDELTA);
|
||||
CTASSERT(offsetof(struct pmclog_pmcattach,pl_pathname) == 5*4 + TSDELTA);
|
||||
CTASSERT(sizeof(struct pmclog_pmcdetach) == 5*4 + TSDELTA);
|
||||
CTASSERT(sizeof(struct pmclog_proccsw) == 7*4 + 8 + TSDELTA);
|
||||
CTASSERT(sizeof(struct pmclog_procexec) == 5*4 + PATH_MAX +
|
||||
sizeof(uintfptr_t) + TSDELTA);
|
||||
CTASSERT(offsetof(struct pmclog_procexec,pl_pathname) == 5*4 + TSDELTA +
|
||||
sizeof(uintfptr_t));
|
||||
CTASSERT(offsetof(struct pmclog_procexec,pl_pathname) == 6*4 +
|
||||
sizeof(uintfptr_t));
|
||||
CTASSERT(sizeof(struct pmclog_procexit) == 6*4 + 8);
|
||||
CTASSERT(sizeof(struct pmclog_procfork) == 6*4);
|
||||
CTASSERT(sizeof(struct pmclog_sysexit) == 4*4);
|
||||
CTASSERT(sizeof(struct pmclog_userdata) == 4*4);
|
||||
CTASSERT(sizeof(struct pmclog_procexit) == 5*4 + 8 + TSDELTA);
|
||||
CTASSERT(sizeof(struct pmclog_procfork) == 5*4 + TSDELTA);
|
||||
CTASSERT(sizeof(struct pmclog_sysexit) == 6*4);
|
||||
CTASSERT(sizeof(struct pmclog_userdata) == 6*4);
|
||||
|
||||
/*
|
||||
* Log buffer structure
|
||||
@ -545,8 +558,6 @@ static uint32_t *
|
||||
pmclog_reserve(struct pmc_owner *po, int length)
|
||||
{
|
||||
uintptr_t newptr, oldptr;
|
||||
uint32_t *lh;
|
||||
struct timespec ts;
|
||||
struct pmclog_buffer *plb, **pplb;
|
||||
|
||||
PMCDBG2(LOG,ALL,1, "po=%p len=%d", po, length);
|
||||
@ -612,11 +623,6 @@ pmclog_reserve(struct pmc_owner *po, int length)
|
||||
oldptr = (uintptr_t) plb->plb_ptr;
|
||||
|
||||
done:
|
||||
lh = (uint32_t *) oldptr;
|
||||
lh++; /* skip header */
|
||||
getnanotime(&ts); /* fill in the timestamp */
|
||||
*lh++ = ts.tv_sec & 0xFFFFFFFF;
|
||||
*lh++ = ts.tv_nsec & 0xFFFFFFF;
|
||||
return ((uint32_t *) oldptr);
|
||||
fail:
|
||||
return (NULL);
|
||||
@ -693,6 +699,8 @@ int
|
||||
pmclog_configure_log(struct pmc_mdep *md, struct pmc_owner *po, int logfd)
|
||||
{
|
||||
struct proc *p;
|
||||
struct timespec ts;
|
||||
uint64_t tsc;
|
||||
int error;
|
||||
|
||||
sx_assert(&pmc_sx, SA_XLOCKED);
|
||||
@ -720,12 +728,21 @@ pmclog_configure_log(struct pmc_mdep *md, struct pmc_owner *po, int logfd)
|
||||
PROC_LOCK(p);
|
||||
p->p_flag |= P_HWPMC;
|
||||
PROC_UNLOCK(p);
|
||||
|
||||
nanotime(&ts);
|
||||
tsc = pmc_rdtsc();
|
||||
/* create a log initialization entry */
|
||||
PMCLOG_RESERVE_WITH_ERROR(po, INITIALIZE,
|
||||
sizeof(struct pmclog_initialize));
|
||||
PMCLOG_EMIT32(PMC_VERSION);
|
||||
PMCLOG_EMIT32(md->pmd_cputype);
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
PMCLOG_EMIT64(tsc_freq);
|
||||
#else
|
||||
/* other architectures will need to fill this in */
|
||||
PMCLOG_EMIT64(0);
|
||||
#endif
|
||||
memcpy(_le, &ts, sizeof(ts));
|
||||
_le += sizeof(ts)/4;
|
||||
PMCLOG_EMITSTRING(pmc_cpuid, PMC_CPUID_LEN);
|
||||
PMCLOG_DESPATCH_SYNC(po);
|
||||
|
||||
@ -917,13 +934,11 @@ pmclog_process_callchain(struct pmc *pm, struct pmc_sample *ps)
|
||||
ps->ps_nsamples * sizeof(uintfptr_t);
|
||||
po = pm->pm_owner;
|
||||
flags = PMC_CALLCHAIN_TO_CPUFLAGS(ps->ps_cpu,ps->ps_flags);
|
||||
PMCLOG_RESERVE_SAFE(po, CALLCHAIN, recordlen);
|
||||
PMCLOG_RESERVE_SAFE(po, CALLCHAIN, recordlen, ps->ps_tsc);
|
||||
PMCLOG_EMIT32(ps->ps_pid);
|
||||
PMCLOG_EMIT32(ps->ps_tid);
|
||||
PMCLOG_EMIT32(pm->pm_id);
|
||||
PMCLOG_EMIT32(flags);
|
||||
/* unused for now */
|
||||
PMCLOG_EMIT32(0);
|
||||
for (n = 0; n < ps->ps_nsamples; n++)
|
||||
PMCLOG_EMITADDR(ps->ps_pc[n]);
|
||||
PMCLOG_DESPATCH_SAFE(po);
|
||||
@ -957,6 +972,7 @@ pmclog_process_map_in(struct pmc_owner *po, pid_t pid, uintfptr_t start,
|
||||
|
||||
PMCLOG_RESERVE(po, MAP_IN, recordlen);
|
||||
PMCLOG_EMIT32(pid);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMITADDR(start);
|
||||
PMCLOG_EMITSTRING(path,pathlen);
|
||||
PMCLOG_DESPATCH_SYNC(po);
|
||||
@ -970,6 +986,7 @@ pmclog_process_map_out(struct pmc_owner *po, pid_t pid, uintfptr_t start,
|
||||
|
||||
PMCLOG_RESERVE(po, MAP_OUT, sizeof(struct pmclog_map_out));
|
||||
PMCLOG_EMIT32(pid);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMITADDR(start);
|
||||
PMCLOG_EMITADDR(end);
|
||||
PMCLOG_DESPATCH(po);
|
||||
@ -991,6 +1008,7 @@ pmclog_process_pmcallocate(struct pmc *pm)
|
||||
PMCLOG_EMIT32(pm->pm_id);
|
||||
PMCLOG_EMIT32(pm->pm_event);
|
||||
PMCLOG_EMIT32(pm->pm_flags);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMIT64(pm->pm_sc.pm_reloadcount);
|
||||
ps = pmc_soft_ev_acquire(pm->pm_event);
|
||||
if (ps != NULL)
|
||||
@ -1005,6 +1023,7 @@ pmclog_process_pmcallocate(struct pmc *pm)
|
||||
PMCLOG_EMIT32(pm->pm_id);
|
||||
PMCLOG_EMIT32(pm->pm_event);
|
||||
PMCLOG_EMIT32(pm->pm_flags);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMIT64(pm->pm_sc.pm_reloadcount);
|
||||
PMCLOG_DESPATCH_SYNC(po);
|
||||
}
|
||||
@ -1026,7 +1045,6 @@ pmclog_process_pmcattach(struct pmc *pm, pid_t pid, char *path)
|
||||
PMCLOG_RESERVE(po, PMCATTACH, recordlen);
|
||||
PMCLOG_EMIT32(pm->pm_id);
|
||||
PMCLOG_EMIT32(pid);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMITSTRING(path, pathlen);
|
||||
PMCLOG_DESPATCH_SYNC(po);
|
||||
}
|
||||
@ -1053,14 +1071,12 @@ pmclog_process_proccreate(struct pmc_owner *po, struct proc *p, int sync)
|
||||
PMCLOG_RESERVE(po, PROC_CREATE, sizeof(struct pmclog_proccreate));
|
||||
PMCLOG_EMIT32(p->p_pid);
|
||||
PMCLOG_EMIT32(p->p_flag);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMITSTRING(p->p_comm, MAXCOMLEN+1);
|
||||
PMCLOG_DESPATCH_SYNC(po);
|
||||
} else {
|
||||
PMCLOG_RESERVE(po, PROC_CREATE, sizeof(struct pmclog_proccreate));
|
||||
PMCLOG_EMIT32(p->p_pid);
|
||||
PMCLOG_EMIT32(p->p_flag);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMITSTRING(p->p_comm, MAXCOMLEN+1);
|
||||
PMCLOG_DESPATCH(po);
|
||||
}
|
||||
@ -1083,11 +1099,12 @@ pmclog_process_proccsw(struct pmc *pm, struct pmc_process *pp, pmc_value_t v, st
|
||||
|
||||
po = pm->pm_owner;
|
||||
|
||||
PMCLOG_RESERVE_SAFE(po, PROCCSW, sizeof(struct pmclog_proccsw));
|
||||
PMCLOG_EMIT32(pm->pm_id);
|
||||
PMCLOG_RESERVE_SAFE(po, PROCCSW, sizeof(struct pmclog_proccsw), pmc_rdtsc());
|
||||
PMCLOG_EMIT64(v);
|
||||
PMCLOG_EMIT32(pm->pm_id);
|
||||
PMCLOG_EMIT32(pp->pp_proc->p_pid);
|
||||
PMCLOG_EMIT32(td->td_tid);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_DESPATCH_SCHED_LOCK(po);
|
||||
}
|
||||
|
||||
@ -1104,7 +1121,6 @@ pmclog_process_procexec(struct pmc_owner *po, pmc_id_t pmid, pid_t pid,
|
||||
PMCLOG_RESERVE(po, PROCEXEC, recordlen);
|
||||
PMCLOG_EMIT32(pid);
|
||||
PMCLOG_EMIT32(pmid);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMITADDR(startaddr);
|
||||
PMCLOG_EMITSTRING(path,pathlen);
|
||||
PMCLOG_DESPATCH_SYNC(po);
|
||||
@ -1129,7 +1145,6 @@ pmclog_process_procexit(struct pmc *pm, struct pmc_process *pp)
|
||||
PMCLOG_RESERVE(po, PROCEXIT, sizeof(struct pmclog_procexit));
|
||||
PMCLOG_EMIT32(pm->pm_id);
|
||||
PMCLOG_EMIT32(pp->pp_proc->p_pid);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMIT64(pp->pp_pmcs[ri].pp_pmcval);
|
||||
PMCLOG_DESPATCH(po);
|
||||
}
|
||||
@ -1170,6 +1185,7 @@ pmclog_process_threadcreate(struct pmc_owner *po, struct thread *td, int sync)
|
||||
PMCLOG_EMIT32(td->td_tid);
|
||||
PMCLOG_EMIT32(p->p_pid);
|
||||
PMCLOG_EMIT32(p->p_flag);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMITSTRING(td->td_name, MAXCOMLEN+1);
|
||||
PMCLOG_DESPATCH_SYNC(po);
|
||||
} else {
|
||||
@ -1177,6 +1193,7 @@ pmclog_process_threadcreate(struct pmc_owner *po, struct thread *td, int sync)
|
||||
PMCLOG_EMIT32(td->td_tid);
|
||||
PMCLOG_EMIT32(p->p_pid);
|
||||
PMCLOG_EMIT32(p->p_flag);
|
||||
PMCLOG_EMIT32(0);
|
||||
PMCLOG_EMITSTRING(td->td_name, MAXCOMLEN+1);
|
||||
PMCLOG_DESPATCH(po);
|
||||
}
|
||||
|
@ -809,6 +809,19 @@ pmc_force_context_switch(void)
|
||||
pause("pmcctx", 1);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
pmc_rdtsc(void)
|
||||
{
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
if (__predict_true(amd_feature & AMDID_RDTSCP))
|
||||
return rdtscp();
|
||||
else
|
||||
return rdtsc();
|
||||
#else
|
||||
return get_cyclecount();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the file name for an executable. This is a simple wrapper
|
||||
* around vn_fullpath(9).
|
||||
@ -4676,6 +4689,8 @@ pmc_add_sample(int cpu, int ring, struct pmc *pm, struct trapframe *tf,
|
||||
ps->ps_td = td;
|
||||
ps->ps_pid = td->td_proc->p_pid;
|
||||
ps->ps_tid = td->td_tid;
|
||||
ps->ps_tsc = pmc_rdtsc();
|
||||
|
||||
ps->ps_cpu = cpu;
|
||||
ps->ps_flags = inuserspace ? PMC_CC_F_USERSPACE : 0;
|
||||
|
||||
|
@ -61,7 +61,7 @@
|
||||
*
|
||||
* The patch version is incremented for every bug fix.
|
||||
*/
|
||||
#define PMC_VERSION_MAJOR 0x07
|
||||
#define PMC_VERSION_MAJOR 0x08
|
||||
#define PMC_VERSION_MINOR 0x03
|
||||
#define PMC_VERSION_PATCH 0x0000
|
||||
|
||||
@ -939,6 +939,7 @@ struct pmc_sample {
|
||||
struct thread *ps_td; /* which thread */
|
||||
struct pmc *ps_pmc; /* interrupting PMC */
|
||||
uintptr_t *ps_pc; /* (const) callchain start */
|
||||
uint64_t ps_tsc; /* tsc value */
|
||||
};
|
||||
|
||||
#define PMC_SAMPLE_FREE ((uint16_t) 0)
|
||||
@ -1217,5 +1218,6 @@ int pmc_save_user_callchain(uintptr_t *_cc, int _maxsamples,
|
||||
struct pmc_mdep *pmc_mdep_alloc(int nclasses);
|
||||
void pmc_mdep_free(struct pmc_mdep *md);
|
||||
void pmc_flush_samples(int cpu);
|
||||
uint64_t pmc_rdtsc(void);
|
||||
#endif /* _KERNEL */
|
||||
#endif /* _SYS_PMC_H_ */
|
||||
|
@ -42,7 +42,7 @@ enum pmclog_type {
|
||||
PMCLOG_TYPE_CLOSELOG = 1,
|
||||
PMCLOG_TYPE_DROPNOTIFY = 2,
|
||||
PMCLOG_TYPE_INITIALIZE = 3,
|
||||
PMCLOG_TYPE_MAPPINGCHANGE = 4, /* unused in v1 */
|
||||
|
||||
PMCLOG_TYPE_PMCALLOCATE = 5,
|
||||
PMCLOG_TYPE_PMCATTACH = 6,
|
||||
PMCLOG_TYPE_PMCDETACH = 7,
|
||||
@ -94,10 +94,13 @@ enum pmclog_type {
|
||||
*/
|
||||
|
||||
#define PMCLOG_ENTRY_HEADER \
|
||||
uint32_t pl_header; \
|
||||
uint32_t pl_ts_sec; \
|
||||
uint32_t pl_ts_nsec;
|
||||
uint32_t pl_header; \
|
||||
uint32_t pl_spare; \
|
||||
uint64_t pl_tsc; \
|
||||
|
||||
struct pmclog_header {
|
||||
PMCLOG_ENTRY_HEADER;
|
||||
};
|
||||
|
||||
/*
|
||||
* The following structures are used to describe the size of each kind
|
||||
@ -115,7 +118,6 @@ struct pmclog_callchain {
|
||||
uint32_t pl_tid;
|
||||
uint32_t pl_pmcid;
|
||||
uint32_t pl_cpuflags;
|
||||
uint32_t pl_cpuflags2;
|
||||
/* 8 byte aligned */
|
||||
uintptr_t pl_pc[PMC_CALLCHAIN_DEPTH_MAX];
|
||||
} __packed;
|
||||
@ -127,25 +129,25 @@ struct pmclog_callchain {
|
||||
|
||||
struct pmclog_closelog {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pad;
|
||||
};
|
||||
|
||||
struct pmclog_dropnotify {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pad;
|
||||
};
|
||||
|
||||
struct pmclog_initialize {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_version; /* driver version */
|
||||
uint32_t pl_cpu; /* enum pmc_cputype */
|
||||
uint32_t pl_pad;
|
||||
uint64_t pl_tsc_freq;
|
||||
struct timespec pl_ts;
|
||||
char pl_cpuid[PMC_CPUID_LEN];
|
||||
} __packed;
|
||||
|
||||
struct pmclog_map_in {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pid;
|
||||
uint32_t pl_pad;
|
||||
uintfptr_t pl_start; /* 8 byte aligned */
|
||||
char pl_pathname[PATH_MAX];
|
||||
} __packed;
|
||||
@ -153,6 +155,7 @@ struct pmclog_map_in {
|
||||
struct pmclog_map_out {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pid;
|
||||
uint32_t pl_pad;
|
||||
uintfptr_t pl_start; /* 8 byte aligned */
|
||||
uintfptr_t pl_end;
|
||||
} __packed;
|
||||
@ -162,6 +165,7 @@ struct pmclog_pmcallocate {
|
||||
uint32_t pl_pmcid;
|
||||
uint32_t pl_event;
|
||||
uint32_t pl_flags;
|
||||
uint32_t pl_pad;
|
||||
uint64_t pl_rate;
|
||||
} __packed;
|
||||
|
||||
@ -169,7 +173,6 @@ struct pmclog_pmcattach {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pmcid;
|
||||
uint32_t pl_pid;
|
||||
uint32_t pl_pad;
|
||||
char pl_pathname[PATH_MAX];
|
||||
} __packed;
|
||||
|
||||
@ -177,30 +180,28 @@ struct pmclog_pmcdetach {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pmcid;
|
||||
uint32_t pl_pid;
|
||||
uint32_t pl_pad;
|
||||
} __packed;
|
||||
|
||||
struct pmclog_proccsw {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pmcid;
|
||||
uint64_t pl_value; /* keep 8 byte aligned */
|
||||
uint32_t pl_pmcid;
|
||||
uint32_t pl_pid;
|
||||
uint32_t pl_tid;
|
||||
uint32_t pl_pad;
|
||||
} __packed;
|
||||
|
||||
struct pmclog_proccreate {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pid;
|
||||
uint32_t pl_flags;
|
||||
uint32_t pl_pad;
|
||||
uint64_t pl_pcomm[MAXCOMLEN+1]; /* keep 8 byte aligned */
|
||||
char pl_pcomm[MAXCOMLEN+1]; /* keep 8 byte aligned */
|
||||
} __packed;
|
||||
|
||||
struct pmclog_procexec {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pid;
|
||||
uint32_t pl_pmcid;
|
||||
uint32_t pl_pad;
|
||||
uintfptr_t pl_start; /* keep 8 byte aligned */
|
||||
char pl_pathname[PATH_MAX];
|
||||
} __packed;
|
||||
@ -209,7 +210,6 @@ struct pmclog_procexit {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pmcid;
|
||||
uint32_t pl_pid;
|
||||
uint32_t pl_pad;
|
||||
uint64_t pl_value; /* keep 8 byte aligned */
|
||||
} __packed;
|
||||
|
||||
@ -217,12 +217,12 @@ struct pmclog_procfork {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_oldpid;
|
||||
uint32_t pl_newpid;
|
||||
uint32_t pl_pad;
|
||||
} __packed;
|
||||
|
||||
struct pmclog_sysexit {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_pid;
|
||||
uint32_t pl_pad;
|
||||
} __packed;
|
||||
|
||||
struct pmclog_threadcreate {
|
||||
@ -230,17 +230,20 @@ struct pmclog_threadcreate {
|
||||
uint32_t pl_tid;
|
||||
uint32_t pl_pid;
|
||||
uint32_t pl_flags;
|
||||
uint64_t pl_tdname[MAXCOMLEN+1]; /* keep 8 byte aligned */
|
||||
uint32_t pl_pad;
|
||||
char pl_tdname[MAXCOMLEN+1]; /* keep 8 byte aligned */
|
||||
} __packed;
|
||||
|
||||
struct pmclog_threadexit {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_tid;
|
||||
uint32_t pl_pad;
|
||||
} __packed;
|
||||
|
||||
struct pmclog_userdata {
|
||||
PMCLOG_ENTRY_HEADER
|
||||
uint32_t pl_userdata;
|
||||
uint32_t pl_pad;
|
||||
} __packed;
|
||||
|
||||
struct pmclog_pmcallocatedyn {
|
||||
@ -248,6 +251,7 @@ struct pmclog_pmcallocatedyn {
|
||||
uint32_t pl_pmcid;
|
||||
uint32_t pl_event;
|
||||
uint32_t pl_flags;
|
||||
uint32_t pl_pad;
|
||||
char pl_evname[PMC_NAME_MAX];
|
||||
} __packed;
|
||||
|
||||
|
@ -181,10 +181,10 @@ SUBDIR.${MK_PF}+= ftp-proxy
|
||||
SUBDIR.${MK_PKGBOOTSTRAP}+= pkg
|
||||
.if (${COMPILER_TYPE} == "clang" || (${COMPILER_TYPE} == "gcc" && ${COMPILER_VERSION} >= 60100))
|
||||
SUBDIR.${MK_PMC}+= pmc
|
||||
.endif
|
||||
SUBDIR.${MK_PMC}+= pmcannotate
|
||||
SUBDIR.${MK_PMC}+= pmccontrol
|
||||
SUBDIR.${MK_PMC}+= pmcstat
|
||||
.endif
|
||||
SUBDIR.${MK_PMC}+= pmcstudy
|
||||
SUBDIR.${MK_PORTSNAP}+= portsnap
|
||||
SUBDIR.${MK_PPP}+= ppp
|
||||
|
@ -5,7 +5,7 @@
|
||||
.include <src.opts.mk>
|
||||
PROG_CXX= pmc
|
||||
MAN=
|
||||
CXXFLAGS+=
|
||||
CXXFLAGS+= -O0
|
||||
|
||||
LIBADD= kvm pmc m ncursesw pmcstat elf
|
||||
|
||||
|
@ -72,9 +72,12 @@ __FBSDID("$FreeBSD$");
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <pmcformat.h>
|
||||
|
||||
using namespace std;
|
||||
using std::unordered_map;
|
||||
typedef unordered_map <int, std::string> idmap;
|
||||
typedef std::pair <int, std::string> identry;
|
||||
typedef unordered_map < int ,string > idmap;
|
||||
typedef pair < int ,string > identry;
|
||||
|
||||
#define LIST_MAX 64
|
||||
static struct option longopts[] = {
|
||||
@ -175,9 +178,27 @@ pmc_find_name(idmap & map, uint32_t id, char *list[LIST_MAX], int count)
|
||||
return (false);
|
||||
}
|
||||
|
||||
static void
|
||||
pmc_log_event(int fd, struct pmclog_ev *ev, bool json)
|
||||
{
|
||||
int len;
|
||||
void *buf;
|
||||
|
||||
if (json) {
|
||||
string ret = event_to_json(ev);
|
||||
buf = (void*)ret.c_str();
|
||||
len = ret.size();
|
||||
} else {
|
||||
len = ev->pl_len;
|
||||
buf = ev->pl_data;
|
||||
}
|
||||
if (write(fd, buf, len) != (ssize_t)len)
|
||||
errx(EX_OSERR, "ERROR: failed output write");
|
||||
}
|
||||
|
||||
static void
|
||||
pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidcount,
|
||||
char *events, char *processes, char *threads, bool exclusive, int infd,
|
||||
char *events, char *processes, char *threads, bool exclusive, bool json, int infd,
|
||||
int outfd)
|
||||
{
|
||||
struct pmclog_ev ev;
|
||||
@ -187,14 +208,15 @@ pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidco
|
||||
char cpuid[PMC_CPUID_LEN];
|
||||
char *proclist[LIST_MAX];
|
||||
char *threadlist[LIST_MAX];
|
||||
int i, pmccount, copies, eventcount, proccount, threadcount;
|
||||
int i, pmccount, copies, eventcount;
|
||||
int proccount, threadcount;
|
||||
uint32_t idx;
|
||||
idmap pidmap, tidmap;
|
||||
|
||||
if ((ps = static_cast < struct pmclog_parse_state *>(pmclog_open(infd)))== NULL)
|
||||
errx(EX_OSERR, "ERROR: Cannot allocate pmclog parse state: %s\n", strerror(errno));
|
||||
|
||||
proccount = eventcount = pmccount = 0;
|
||||
threadcount = proccount = eventcount = pmccount = 0;
|
||||
if (processes)
|
||||
parse_names(processes, proclist, &proccount);
|
||||
if (threads)
|
||||
@ -207,7 +229,6 @@ pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidco
|
||||
}
|
||||
if (events)
|
||||
parse_events(events, eventlist, &eventcount, cpuid);
|
||||
|
||||
lseek(infd, 0, SEEK_SET);
|
||||
pmclog_close(ps);
|
||||
if ((ps = static_cast < struct pmclog_parse_state *>(pmclog_open(infd)))== NULL)
|
||||
@ -233,8 +254,7 @@ pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidco
|
||||
if (ev.pl_type == PMCLOG_TYPE_PROC_CREATE)
|
||||
pidmap[ev.pl_u.pl_pc.pl_pid] = ev.pl_u.pl_pc.pl_pcomm;
|
||||
if (ev.pl_type != PMCLOG_TYPE_CALLCHAIN) {
|
||||
if (write(outfd, ev.pl_data, ev.pl_len) != (ssize_t)ev.pl_len)
|
||||
errx(EX_OSERR, "ERROR: failed output write");
|
||||
pmc_log_event(outfd, &ev, json);
|
||||
continue;
|
||||
}
|
||||
if (pidcount) {
|
||||
@ -274,8 +294,7 @@ pmc_filter_handler(uint32_t *lwplist, int lwpcount, uint32_t *pidlist, int pidco
|
||||
if (threadcount &&
|
||||
pmc_find_name(tidmap, ev.pl_u.pl_cc.pl_tid, threadlist, threadcount) == exclusive)
|
||||
continue;
|
||||
if (write(outfd, ev.pl_data, ev.pl_len) != (ssize_t)ev.pl_len)
|
||||
errx(EX_OSERR, "ERROR: failed output write");
|
||||
pmc_log_event(outfd, &ev, json);
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,16 +306,19 @@ cmd_pmc_filter(int argc, char **argv)
|
||||
uint32_t pidlist[LIST_MAX];
|
||||
int option, lwpcount, pidcount;
|
||||
int prelogfd, postlogfd;
|
||||
bool exclusive;
|
||||
bool exclusive, json;
|
||||
|
||||
threads = processes = lwps = pids = events = NULL;
|
||||
lwpcount = pidcount = 0;
|
||||
exclusive = false;
|
||||
while ((option = getopt_long(argc, argv, "e:p:t:xP:T:", longopts, NULL)) != -1) {
|
||||
json = exclusive = false;
|
||||
while ((option = getopt_long(argc, argv, "e:jp:t:xP:T:", longopts, NULL)) != -1) {
|
||||
switch (option) {
|
||||
case 'e':
|
||||
events = strdup(optarg);
|
||||
break;
|
||||
case 'j':
|
||||
json = true;
|
||||
break;
|
||||
case 'p':
|
||||
pids = strdup(optarg);
|
||||
break;
|
||||
@ -336,6 +358,6 @@ cmd_pmc_filter(int argc, char **argv)
|
||||
strerror(errno));
|
||||
|
||||
pmc_filter_handler(lwplist, lwpcount, pidlist, pidcount, events,
|
||||
processes, threads, exclusive, prelogfd, postlogfd);
|
||||
processes, threads, exclusive, json, prelogfd, postlogfd);
|
||||
return (0);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
PROG= pmccontrol
|
||||
PROG_CXX= pmccontrol
|
||||
MAN= pmccontrol.8
|
||||
|
||||
LIBADD+= pmc
|
||||
|
@ -2,7 +2,7 @@
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
PROG= pmcstat
|
||||
PROG_CXX= pmcstat
|
||||
MAN= pmcstat.8
|
||||
|
||||
LIBADD= kvm pmc m ncursesw pmcstat elf
|
||||
|
Loading…
Reference in New Issue
Block a user