From f992dd4b5c6635e079c572fe1b59e34f0e28ad6d Mon Sep 17 00:00:00 2001 From: Matt Macy Date: Thu, 7 Jun 2018 02:03:22 +0000 Subject: [PATCH] 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 --- lib/Makefile | 2 + lib/libpmc/Makefile | 4 +- lib/libpmc/libpmc_json.cc | 393 +++++++++++++++++++++++++++++++++ lib/libpmc/pmcformat.h | 33 +++ lib/libpmc/pmclog.c | 24 +- lib/libpmc/pmclog.h | 2 + sys/dev/hwpmc/hwpmc_logging.c | 111 ++++++---- sys/dev/hwpmc/hwpmc_mod.c | 15 ++ sys/sys/pmc.h | 4 +- sys/sys/pmclog.h | 38 ++-- usr.sbin/Makefile | 2 +- usr.sbin/pmc/Makefile | 2 +- usr.sbin/pmc/cmd_pmc_filter.cc | 50 +++-- usr.sbin/pmccontrol/Makefile | 2 +- usr.sbin/pmcstat/Makefile | 2 +- 15 files changed, 590 insertions(+), 94 deletions(-) create mode 100644 lib/libpmc/libpmc_json.cc create mode 100644 lib/libpmc/pmcformat.h diff --git a/lib/Makefile b/lib/Makefile index 675b7a44cae9..9775b59101d2 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 diff --git a/lib/libpmc/Makefile b/lib/libpmc/Makefile index d6e771a39f46..19e9eb75fc20 100644 --- a/lib/libpmc/Makefile +++ b/lib/libpmc/Makefile @@ -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} diff --git a/lib/libpmc/libpmc_json.cc b/lib/libpmc/libpmc_json.cc new file mode 100644 index 000000000000..e2b8b0637f3e --- /dev/null +++ b/lib/libpmc/libpmc_json.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); + } +} + diff --git a/lib/libpmc/pmcformat.h b/lib/libpmc/pmcformat.h new file mode 100644 index 000000000000..813873e854eb --- /dev/null +++ b/lib/libpmc/pmcformat.h @@ -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 diff --git a/lib/libpmc/pmclog.c b/lib/libpmc/pmclog.c index 2df9d97d1e6a..9569cd9da2b5 100644 --- a/lib/libpmc/pmclog.c +++ b/lib/libpmc/pmclog.c @@ -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 */ diff --git a/lib/libpmc/pmclog.h b/lib/libpmc/pmclog.h index 4293984ba476..1aea2cc0d071 100644 --- a/lib/libpmc/pmclog.h +++ b/lib/libpmc/pmclog.h @@ -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]; }; diff --git a/sys/dev/hwpmc/hwpmc_logging.c b/sys/dev/hwpmc/hwpmc_logging.c index 1f314518549a..2cc42e5096a7 100644 --- a/sys/dev/hwpmc/hwpmc_logging.c +++ b/sys/dev/hwpmc/hwpmc_logging.c @@ -61,6 +61,10 @@ __FBSDID("$FreeBSD$"); #include #include +#if defined(__i386__) || defined(__amd64__) +#include +#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); } diff --git a/sys/dev/hwpmc/hwpmc_mod.c b/sys/dev/hwpmc/hwpmc_mod.c index 0a8b347447fe..1d5e28ab30ad 100644 --- a/sys/dev/hwpmc/hwpmc_mod.c +++ b/sys/dev/hwpmc/hwpmc_mod.c @@ -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; diff --git a/sys/sys/pmc.h b/sys/sys/pmc.h index a1e24bc8d47b..428d1f381990 100644 --- a/sys/sys/pmc.h +++ b/sys/sys/pmc.h @@ -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_ */ diff --git a/sys/sys/pmclog.h b/sys/sys/pmclog.h index c0b645292cd6..51a8a20d14a6 100644 --- a/sys/sys/pmclog.h +++ b/sys/sys/pmclog.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; diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index af327d42e97a..0eb9880fcd1d 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -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 diff --git a/usr.sbin/pmc/Makefile b/usr.sbin/pmc/Makefile index 21d727e63aeb..c0a48372b0ae 100644 --- a/usr.sbin/pmc/Makefile +++ b/usr.sbin/pmc/Makefile @@ -5,7 +5,7 @@ .include PROG_CXX= pmc MAN= -CXXFLAGS+= +CXXFLAGS+= -O0 LIBADD= kvm pmc m ncursesw pmcstat elf diff --git a/usr.sbin/pmc/cmd_pmc_filter.cc b/usr.sbin/pmc/cmd_pmc_filter.cc index 3892066c7695..ecb5d3d4edf8 100644 --- a/usr.sbin/pmc/cmd_pmc_filter.cc +++ b/usr.sbin/pmc/cmd_pmc_filter.cc @@ -72,9 +72,12 @@ __FBSDID("$FreeBSD$"); #include #include +#include + +using namespace std; using std::unordered_map; -typedef unordered_map idmap; -typedef std::pair 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); } diff --git a/usr.sbin/pmccontrol/Makefile b/usr.sbin/pmccontrol/Makefile index 1940b1ffe123..340c12811a4c 100644 --- a/usr.sbin/pmccontrol/Makefile +++ b/usr.sbin/pmccontrol/Makefile @@ -2,7 +2,7 @@ # $FreeBSD$ # -PROG= pmccontrol +PROG_CXX= pmccontrol MAN= pmccontrol.8 LIBADD+= pmc diff --git a/usr.sbin/pmcstat/Makefile b/usr.sbin/pmcstat/Makefile index 472067767ab2..0233ad48f8d9 100644 --- a/usr.sbin/pmcstat/Makefile +++ b/usr.sbin/pmcstat/Makefile @@ -2,7 +2,7 @@ # $FreeBSD$ # -PROG= pmcstat +PROG_CXX= pmcstat MAN= pmcstat.8 LIBADD= kvm pmc m ncursesw pmcstat elf