Date: Thu, 7 Jun 2018 02:03:22 +0000 (UTC) From: Matt Macy <mmacy@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r334749 - in head: lib lib/libpmc sys/dev/hwpmc sys/sys usr.sbin usr.sbin/pmc usr.sbin/pmccontrol usr.sbin/pmcstat Message-ID: <201806070203.w5723Mcn093949@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mmacy Date: Thu Jun 7 02:03:22 2018 New Revision: 334749 URL: https://svnweb.freebsd.org/changeset/base/334749 Log: 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 Added: head/lib/libpmc/libpmc_json.cc (contents, props changed) head/lib/libpmc/pmcformat.h (contents, props changed) Modified: head/lib/Makefile head/lib/libpmc/Makefile head/lib/libpmc/pmclog.c head/lib/libpmc/pmclog.h head/sys/dev/hwpmc/hwpmc_logging.c head/sys/dev/hwpmc/hwpmc_mod.c head/sys/sys/pmc.h head/sys/sys/pmclog.h head/usr.sbin/Makefile head/usr.sbin/pmc/Makefile (contents, props changed) head/usr.sbin/pmc/cmd_pmc_filter.cc head/usr.sbin/pmccontrol/Makefile head/usr.sbin/pmcstat/Makefile Modified: head/lib/Makefile ============================================================================== --- head/lib/Makefile Thu Jun 7 00:55:17 2018 (r334748) +++ head/lib/Makefile Thu Jun 7 02:03:22 2018 (r334749) @@ -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 Modified: head/lib/libpmc/Makefile ============================================================================== --- head/lib/libpmc/Makefile Thu Jun 7 00:55:17 2018 (r334748) +++ head/lib/libpmc/Makefile Thu Jun 7 02:03:22 2018 (r334749) @@ -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} Added: head/lib/libpmc/libpmc_json.cc ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libpmc/libpmc_json.cc Thu Jun 7 02:03:22 2018 (r334749) @@ -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); + } +} + Added: head/lib/libpmc/pmcformat.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/lib/libpmc/pmcformat.h Thu Jun 7 02:03:22 2018 (r334749) @@ -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 Modified: head/lib/libpmc/pmclog.c ============================================================================== --- head/lib/libpmc/pmclog.c Thu Jun 7 00:55:17 2018 (r334748) +++ head/lib/libpmc/pmclog.c Thu Jun 7 02:03:22 2018 (r334749) @@ -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 *l 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 *l 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 *l } /* 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 *l 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 *l 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 *l 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 *l 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 *l 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 *l 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 *l 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 *l 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 *l 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 */ Modified: head/lib/libpmc/pmclog.h ============================================================================== --- head/lib/libpmc/pmclog.h Thu Jun 7 00:55:17 2018 (r334748) +++ head/lib/libpmc/pmclog.h Thu Jun 7 02:03:22 2018 (r334749) @@ -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]; }; Modified: head/sys/dev/hwpmc/hwpmc_logging.c ============================================================================== --- head/sys/dev/hwpmc/hwpmc_logging.c Thu Jun 7 00:55:17 2018 (r334748) +++ head/sys/dev/hwpmc/hwpmc_logging.c Thu Jun 7 02:03:22 2018 (r334749) @@ -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_o 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_sa 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, 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 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, ch 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 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_proc 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 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_pro 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, stru 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, stru 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); } Modified: head/sys/dev/hwpmc/hwpmc_mod.c ============================================================================== --- head/sys/dev/hwpmc/hwpmc_mod.c Thu Jun 7 00:55:17 2018 (r334748) +++ head/sys/dev/hwpmc/hwpmc_mod.c Thu Jun 7 02:03:22 2018 (r334749) @@ -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, stru 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; Modified: head/sys/sys/pmc.h ============================================================================== --- head/sys/sys/pmc.h Thu Jun 7 00:55:17 2018 (r334748) +++ head/sys/sys/pmc.h Thu Jun 7 02:03:22 2018 (r334749) @@ -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 _maxsa 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_ */ Modified: head/sys/sys/pmclog.h ============================================================================== --- head/sys/sys/pmclog.h Thu Jun 7 00:55:17 2018 (r334748) +++ head/sys/sys/pmclog.h Thu Jun 7 02:03:22 2018 (r334749) @@ -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; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201806070203.w5723Mcn093949>