From 1d3209ab83aac3089f15e00934e922d222a4ecf0 Mon Sep 17 00:00:00 2001 From: jkoshy Date: Thu, 9 Jun 2005 19:45:09 +0000 Subject: MFP4: - Implement sampling modes and logging support in hwpmc(4). - Separate MI and MD parts of hwpmc(4) and allow sharing of PMC implementations across different architectures. Add support for P4 (EMT64) style PMCs to the amd64 code. - New pmcstat(8) options: -E (exit time counts) -W (counts every context switch), -R (print log file). - pmc(3) API changes, improve our ability to keep ABI compatibility in the future. Add more 'alias' names for commonly used events. - bug fixes & documentation. --- lib/libpmc/Makefile | 14 +- lib/libpmc/libpmc.c | 2786 ++++++++++++++++++++++++++------------------------- lib/libpmc/pmc.3 | 104 +- lib/libpmc/pmc.h | 50 +- lib/libpmc/pmclog.3 | 276 +++++ lib/libpmc/pmclog.c | 532 ++++++++++ lib/libpmc/pmclog.h | 146 +++ 7 files changed, 2494 insertions(+), 1414 deletions(-) create mode 100644 lib/libpmc/pmclog.3 create mode 100644 lib/libpmc/pmclog.c create mode 100644 lib/libpmc/pmclog.h (limited to 'lib/libpmc') diff --git a/lib/libpmc/Makefile b/lib/libpmc/Makefile index 7d24d85..c2560bd 100644 --- a/lib/libpmc/Makefile +++ b/lib/libpmc/Makefile @@ -2,12 +2,12 @@ LIB= pmc -SRCS= libpmc.c -INCS= pmc.h +SRCS= libpmc.c pmclog.c +INCS= pmc.h pmclog.h WARNS?= 6 -MAN= pmc.3 +MAN= pmc.3 pmclog.3 MLINKS+= \ pmc.3 pmc_allocate.3 \ @@ -19,6 +19,7 @@ MLINKS+= \ pmc.3 pmc_disable.3 \ pmc.3 pmc_enable.3 \ pmc.3 pmc_event_names_of_class.3 \ + pmc.3 pmc_flush_logfile.3 \ pmc.3 pmc_get_driver_stats.3 \ pmc.3 pmc_init.3 \ pmc.3 pmc_name_of_capability.3 \ @@ -38,6 +39,13 @@ MLINKS+= \ pmc.3 pmc_stop.3 \ pmc.3 pmc_width.3 \ pmc.3 pmc_write.3 \ + pmc.3 pmc_writelog.3 \ pmc.3 pmc_x86_get_msr.3 +MLINKS+= \ + pmclog.3 pmclog_open.3 \ + pmclog.3 pmclog_close.3 \ + pmclog.3 pmclog_feed.3 \ + pmclog.3 pmclog_read.3 + .include diff --git a/lib/libpmc/libpmc.c b/lib/libpmc/libpmc.c index 272d25a..09cc2b4 100644 --- a/lib/libpmc/libpmc.c +++ b/lib/libpmc/libpmc.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003,2004 Joseph Koshy + * Copyright (c) 2003-2005 Joseph Koshy * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,14 +46,17 @@ __FBSDID("$FreeBSD$"); #if defined(__i386__) static int k7_allocate_pmc(enum pmc_event _pe, char *_ctrspec, struct pmc_op_pmcallocate *_pmc_config); -static int p6_allocate_pmc(enum pmc_event _pe, char *_ctrspec, +#endif +#if defined(__amd64__) +static int k8_allocate_pmc(enum pmc_event _pe, char *_ctrspec, struct pmc_op_pmcallocate *_pmc_config); +#endif +#if defined(__i386__) static int p4_allocate_pmc(enum pmc_event _pe, char *_ctrspec, struct pmc_op_pmcallocate *_pmc_config); static int p5_allocate_pmc(enum pmc_event _pe, char *_ctrspec, struct pmc_op_pmcallocate *_pmc_config); -#elif defined(__amd64__) -static int k8_allocate_pmc(enum pmc_event _pe, char *_ctrspec, +static int p6_allocate_pmc(enum pmc_event _pe, char *_ctrspec, struct pmc_op_pmcallocate *_pmc_config); #endif @@ -212,7 +215,7 @@ k7_allocate_pmc(enum pmc_event pe, char *ctrspec, int c, has_unitmask; uint32_t count, unitmask; - pmc_config->pm_amd_config = 0; + pmc_config->pm_md.pm_amd.pm_amd_config = 0; pmc_config->pm_caps |= PMC_CAP_READ; if (pe == PMC_EV_TSC_TSC) { @@ -226,7 +229,7 @@ k7_allocate_pmc(enum pmc_event pe, char *ctrspec, pe == PMC_EV_K7_DC_REFILLS_FROM_SYSTEM || pe == PMC_EV_K7_DC_WRITEBACKS) { has_unitmask = 1; - unitmask = K7_PMC_UNITMASK_MOESI; + unitmask = AMD_PMC_UNITMASK_MOESI; } else unitmask = has_unitmask = 0; @@ -243,7 +246,8 @@ k7_allocate_pmc(enum pmc_event pe, char *ctrspec, return -1; pmc_config->pm_caps |= PMC_CAP_THRESHOLD; - pmc_config->pm_amd_config |= K7_PMC_TO_COUNTER(count); + pmc_config->pm_md.pm_amd.pm_amd_config |= + AMD_PMC_TO_COUNTER(count); } else if (KWMATCH(p, K7_KW_EDGE)) { pmc_config->pm_caps |= PMC_CAP_EDGE; @@ -261,15 +265,15 @@ k7_allocate_pmc(enum pmc_event pe, char *ctrspec, while ((c = tolower(*q++)) != 0) if (c == 'm') - unitmask |= K7_PMC_UNITMASK_M; + unitmask |= AMD_PMC_UNITMASK_M; else if (c == 'o') - unitmask |= K7_PMC_UNITMASK_O; + unitmask |= AMD_PMC_UNITMASK_O; else if (c == 'e') - unitmask |= K7_PMC_UNITMASK_E; + unitmask |= AMD_PMC_UNITMASK_E; else if (c == 's') - unitmask |= K7_PMC_UNITMASK_S; + unitmask |= AMD_PMC_UNITMASK_S; else if (c == 'i') - unitmask |= K7_PMC_UNITMASK_I; + unitmask |= AMD_PMC_UNITMASK_I; else if (c == '+') continue; else @@ -286,523 +290,317 @@ k7_allocate_pmc(enum pmc_event pe, char *ctrspec, if (has_unitmask) { pmc_config->pm_caps |= PMC_CAP_QUALIFIER; - pmc_config->pm_amd_config |= - K7_PMC_TO_UNITMASK(unitmask); + pmc_config->pm_md.pm_amd.pm_amd_config |= + AMD_PMC_TO_UNITMASK(unitmask); } return 0; } +#endif + +#if defined(__amd64__) + /* - * Intel P4 PMCs + * AMD K8 PMCs. + * + * These are very similar to AMD K7 PMCs, but support more kinds of + * events. */ -static struct pmc_event_alias p4_aliases[] = { - EV_ALIAS("branches", "p4-branch-retired,mask=mmtp+mmtm"), - EV_ALIAS("branch-mispredicts", "p4-mispred-branch-retired"), +static struct pmc_event_alias k8_aliases[] = { + EV_ALIAS("branches", "k8-fr-retired-taken-branches"), + EV_ALIAS("branch-mispredicts", + "k8-fr-retired-taken-branches-mispredicted"), EV_ALIAS("cycles", "tsc"), - EV_ALIAS("instructions", - "p4-instr-retired,mask=nbogusntag+nbogustag"), + EV_ALIAS("dc-misses", "k8-dc-miss"), + EV_ALIAS("ic-misses", "k8-ic-miss"), + EV_ALIAS("instructions", "k8-fr-retired-x86-instructions"), + EV_ALIAS("interrupts", "k8-fr-taken-hardware-interrupts"), EV_ALIAS(NULL, NULL) }; -#define P4_KW_ACTIVE "active" -#define P4_KW_ACTIVE_ANY "any" -#define P4_KW_ACTIVE_BOTH "both" -#define P4_KW_ACTIVE_NONE "none" -#define P4_KW_ACTIVE_SINGLE "single" -#define P4_KW_BUSREQTYPE "busreqtype" -#define P4_KW_CASCADE "cascade" -#define P4_KW_EDGE "edge" -#define P4_KW_INV "complement" -#define P4_KW_OS "os" -#define P4_KW_MASK "mask" -#define P4_KW_PRECISE "precise" -#define P4_KW_TAG "tag" -#define P4_KW_THRESHOLD "threshold" -#define P4_KW_USR "usr" +#define __K8MASK(N,V) PMCMASK(N,(1 << (V))) -#define __P4MASK(N,V) PMCMASK(N, (1 << (V))) +/* + * Parsing tables + */ -static const struct pmc_masks p4_mask_tcdm[] = { /* tc deliver mode */ - __P4MASK(dd, 0), - __P4MASK(db, 1), - __P4MASK(di, 2), - __P4MASK(bd, 3), - __P4MASK(bb, 4), - __P4MASK(bi, 5), - __P4MASK(id, 6), - __P4MASK(ib, 7), +/* fp dispatched fpu ops */ +static const struct pmc_masks k8_mask_fdfo[] = { + __K8MASK(add-pipe-excluding-junk-ops, 0), + __K8MASK(multiply-pipe-excluding-junk-ops, 1), + __K8MASK(store-pipe-excluding-junk-ops, 2), + __K8MASK(add-pipe-junk-ops, 3), + __K8MASK(multiply-pipe-junk-ops, 4), + __K8MASK(store-pipe-junk-ops, 5), NULLMASK }; -static const struct pmc_masks p4_mask_bfr[] = { /* bpu fetch request */ - __P4MASK(tcmiss, 0), - NULLMASK, -}; - -static const struct pmc_masks p4_mask_ir[] = { /* itlb reference */ - __P4MASK(hit, 0), - __P4MASK(miss, 1), - __P4MASK(hit-uc, 2), +/* ls segment register loads */ +static const struct pmc_masks k8_mask_lsrl[] = { + __K8MASK(es, 0), + __K8MASK(cs, 1), + __K8MASK(ss, 2), + __K8MASK(ds, 3), + __K8MASK(fs, 4), + __K8MASK(gs, 5), + __K8MASK(hs, 6), NULLMASK }; -static const struct pmc_masks p4_mask_memcan[] = { /* memory cancel */ - __P4MASK(st-rb-full, 2), - __P4MASK(64k-conf, 3), +/* ls locked operation */ +static const struct pmc_masks k8_mask_llo[] = { + __K8MASK(locked-instructions, 0), + __K8MASK(cycles-in-request, 1), + __K8MASK(cycles-to-complete, 2), NULLMASK }; -static const struct pmc_masks p4_mask_memcomp[] = { /* memory complete */ - __P4MASK(lsc, 0), - __P4MASK(ssc, 1), +/* dc refill from {l2,system} and dc copyback */ +static const struct pmc_masks k8_mask_dc[] = { + __K8MASK(invalid, 0), + __K8MASK(shared, 1), + __K8MASK(exclusive, 2), + __K8MASK(owner, 3), + __K8MASK(modified, 4), NULLMASK }; -static const struct pmc_masks p4_mask_lpr[] = { /* load port replay */ - __P4MASK(split-ld, 1), +/* dc one bit ecc error */ +static const struct pmc_masks k8_mask_dobee[] = { + __K8MASK(scrubber, 0), + __K8MASK(piggyback, 1), NULLMASK }; -static const struct pmc_masks p4_mask_spr[] = { /* store port replay */ - __P4MASK(split-st, 1), +/* dc dispatched prefetch instructions */ +static const struct pmc_masks k8_mask_ddpi[] = { + __K8MASK(load, 0), + __K8MASK(store, 1), + __K8MASK(nta, 2), NULLMASK }; -static const struct pmc_masks p4_mask_mlr[] = { /* mob load replay */ - __P4MASK(no-sta, 1), - __P4MASK(no-std, 3), - __P4MASK(partial-data, 4), - __P4MASK(unalgn-addr, 5), +/* dc dcache accesses by locks */ +static const struct pmc_masks k8_mask_dabl[] = { + __K8MASK(accesses, 0), + __K8MASK(misses, 1), NULLMASK }; -static const struct pmc_masks p4_mask_pwt[] = { /* page walk type */ - __P4MASK(dtmiss, 0), - __P4MASK(itmiss, 1), +/* bu internal l2 request */ +static const struct pmc_masks k8_mask_bilr[] = { + __K8MASK(ic-fill, 0), + __K8MASK(dc-fill, 1), + __K8MASK(tlb-reload, 2), + __K8MASK(tag-snoop, 3), + __K8MASK(cancelled, 4), NULLMASK }; -static const struct pmc_masks p4_mask_bcr[] = { /* bsq cache reference */ - __P4MASK(rd-2ndl-hits, 0), - __P4MASK(rd-2ndl-hite, 1), - __P4MASK(rd-2ndl-hitm, 2), - __P4MASK(rd-3rdl-hits, 3), - __P4MASK(rd-3rdl-hite, 4), - __P4MASK(rd-3rdl-hitm, 5), - __P4MASK(rd-2ndl-miss, 8), - __P4MASK(rd-3rdl-miss, 9), - __P4MASK(wr-2ndl-miss, 10), +/* bu fill request l2 miss */ +static const struct pmc_masks k8_mask_bfrlm[] = { + __K8MASK(ic-fill, 0), + __K8MASK(dc-fill, 1), + __K8MASK(tlb-reload, 2), NULLMASK }; -static const struct pmc_masks p4_mask_ia[] = { /* ioq allocation */ - __P4MASK(all-read, 5), - __P4MASK(all-write, 6), - __P4MASK(mem-uc, 7), - __P4MASK(mem-wc, 8), - __P4MASK(mem-wt, 9), - __P4MASK(mem-wp, 10), - __P4MASK(mem-wb, 11), - __P4MASK(own, 13), - __P4MASK(other, 14), - __P4MASK(prefetch, 15), +/* bu fill into l2 */ +static const struct pmc_masks k8_mask_bfil[] = { + __K8MASK(dirty-l2-victim, 0), + __K8MASK(victim-from-l2, 1), NULLMASK }; -static const struct pmc_masks p4_mask_iae[] = { /* ioq active entries */ - __P4MASK(all-read, 5), - __P4MASK(all-write, 6), - __P4MASK(mem-uc, 7), - __P4MASK(mem-wc, 8), - __P4MASK(mem-wt, 9), - __P4MASK(mem-wp, 10), - __P4MASK(mem-wb, 11), - __P4MASK(own, 13), - __P4MASK(other, 14), - __P4MASK(prefetch, 15), +/* fr retired fpu instructions */ +static const struct pmc_masks k8_mask_frfi[] = { + __K8MASK(x87, 0), + __K8MASK(mmx-3dnow, 1), + __K8MASK(packed-sse-sse2, 2), + __K8MASK(scalar-sse-sse2, 3), NULLMASK }; -static const struct pmc_masks p4_mask_fda[] = { /* fsb data activity */ - __P4MASK(drdy-drv, 0), - __P4MASK(drdy-own, 1), - __P4MASK(drdy-other, 2), - __P4MASK(dbsy-drv, 3), - __P4MASK(dbsy-own, 4), - __P4MASK(dbsy-other, 5), +/* fr retired fastpath double op instructions */ +static const struct pmc_masks k8_mask_frfdoi[] = { + __K8MASK(low-op-pos-0, 0), + __K8MASK(low-op-pos-1, 1), + __K8MASK(low-op-pos-2, 2), NULLMASK }; -static const struct pmc_masks p4_mask_ba[] = { /* bsq allocation */ - __P4MASK(req-type0, 0), - __P4MASK(req-type1, 1), - __P4MASK(req-len0, 2), - __P4MASK(req-len1, 3), - __P4MASK(req-io-type, 5), - __P4MASK(req-lock-type, 6), - __P4MASK(req-cache-type, 7), - __P4MASK(req-split-type, 8), - __P4MASK(req-dem-type, 9), - __P4MASK(req-ord-type, 10), - __P4MASK(mem-type0, 11), - __P4MASK(mem-type1, 12), - __P4MASK(mem-type2, 13), +/* fr fpu exceptions */ +static const struct pmc_masks k8_mask_ffe[] = { + __K8MASK(x87-reclass-microfaults, 0), + __K8MASK(sse-retype-microfaults, 1), + __K8MASK(sse-reclass-microfaults, 2), + __K8MASK(sse-and-x87-microtraps, 3), NULLMASK }; -static const struct pmc_masks p4_mask_sia[] = { /* sse input assist */ - __P4MASK(all, 15), +/* nb memory controller page access event */ +static const struct pmc_masks k8_mask_nmcpae[] = { + __K8MASK(page-hit, 0), + __K8MASK(page-miss, 1), + __K8MASK(page-conflict, 2), NULLMASK }; -static const struct pmc_masks p4_mask_psu[] = { /* packed sp uop */ - __P4MASK(all, 15), +/* nb memory controller turnaround */ +static const struct pmc_masks k8_mask_nmct[] = { + __K8MASK(dimm-turnaround, 0), + __K8MASK(read-to-write-turnaround, 1), + __K8MASK(write-to-read-turnaround, 2), NULLMASK }; -static const struct pmc_masks p4_mask_pdu[] = { /* packed dp uop */ - __P4MASK(all, 15), +/* nb memory controller bypass saturation */ +static const struct pmc_masks k8_mask_nmcbs[] = { + __K8MASK(memory-controller-hi-pri-bypass, 0), + __K8MASK(memory-controller-lo-pri-bypass, 1), + __K8MASK(dram-controller-interface-bypass, 2), + __K8MASK(dram-controller-queue-bypass, 3), NULLMASK }; -static const struct pmc_masks p4_mask_ssu[] = { /* scalar sp uop */ - __P4MASK(all, 15), +/* nb sized commands */ +static const struct pmc_masks k8_mask_nsc[] = { + __K8MASK(nonpostwrszbyte, 0), + __K8MASK(nonpostwrszdword, 1), + __K8MASK(postwrszbyte, 2), + __K8MASK(postwrszdword, 3), + __K8MASK(rdszbyte, 4), + __K8MASK(rdszdword, 5), + __K8MASK(rdmodwr, 6), NULLMASK }; -static const struct pmc_masks p4_mask_sdu[] = { /* scalar dp uop */ - __P4MASK(all, 15), +/* nb probe result */ +static const struct pmc_masks k8_mask_npr[] = { + __K8MASK(probe-miss, 0), + __K8MASK(probe-hit, 1), + __K8MASK(probe-hit-dirty-no-memory-cancel, 2), + __K8MASK(probe-hit-dirty-with-memory-cancel, 3), NULLMASK }; -static const struct pmc_masks p4_mask_64bmu[] = { /* 64 bit mmx uop */ - __P4MASK(all, 15), - NULLMASK -}; - -static const struct pmc_masks p4_mask_128bmu[] = { /* 128 bit mmx uop */ - __P4MASK(all, 15), - NULLMASK -}; - -static const struct pmc_masks p4_mask_xfu[] = { /* X87 fp uop */ - __P4MASK(all, 15), - NULLMASK -}; - -static const struct pmc_masks p4_mask_xsmu[] = { /* x87 simd moves uop */ - __P4MASK(allp0, 3), - __P4MASK(allp2, 4), - NULLMASK -}; - -static const struct pmc_masks p4_mask_gpe[] = { /* global power events */ - __P4MASK(running, 0), - NULLMASK -}; - -static const struct pmc_masks p4_mask_tmx[] = { /* TC ms xfer */ - __P4MASK(cisc, 0), - NULLMASK -}; - -static const struct pmc_masks p4_mask_uqw[] = { /* uop queue writes */ - __P4MASK(from-tc-build, 0), - __P4MASK(from-tc-deliver, 1), - __P4MASK(from-rom, 2), - NULLMASK -}; - -static const struct pmc_masks p4_mask_rmbt[] = { - /* retired mispred branch type */ - __P4MASK(conditional, 1), - __P4MASK(call, 2), - __P4MASK(return, 3), - __P4MASK(indirect, 4), - NULLMASK -}; - -static const struct pmc_masks p4_mask_rbt[] = { /* retired branch type */ - __P4MASK(conditional, 1), - __P4MASK(call, 2), - __P4MASK(retired, 3), - __P4MASK(indirect, 4), - NULLMASK -}; - -static const struct pmc_masks p4_mask_rs[] = { /* resource stall */ - __P4MASK(sbfull, 5), - NULLMASK -}; - -static const struct pmc_masks p4_mask_wb[] = { /* WC buffer */ - __P4MASK(wcb-evicts, 0), - __P4MASK(wcb-full-evict, 1), - NULLMASK -}; - -static const struct pmc_masks p4_mask_fee[] = { /* front end event */ - __P4MASK(nbogus, 0), - __P4MASK(bogus, 1), - NULLMASK -}; - -static const struct pmc_masks p4_mask_ee[] = { /* execution event */ - __P4MASK(nbogus0, 0), - __P4MASK(nbogus1, 1), - __P4MASK(nbogus2, 2), - __P4MASK(nbogus3, 3), - __P4MASK(bogus0, 4), - __P4MASK(bogus1, 5), - __P4MASK(bogus2, 6), - __P4MASK(bogus3, 7), - NULLMASK -}; - -static const struct pmc_masks p4_mask_re[] = { /* replay event */ - __P4MASK(nbogus, 0), - __P4MASK(bogus, 1), - NULLMASK -}; - -static const struct pmc_masks p4_mask_insret[] = { /* instr retired */ - __P4MASK(nbogusntag, 0), - __P4MASK(nbogustag, 1), - __P4MASK(bogusntag, 2), - __P4MASK(bogustag, 3), - NULLMASK -}; - -static const struct pmc_masks p4_mask_ur[] = { /* uops retired */ - __P4MASK(nbogus, 0), - __P4MASK(bogus, 1), - NULLMASK -}; - -static const struct pmc_masks p4_mask_ut[] = { /* uop type */ - __P4MASK(tagloads, 1), - __P4MASK(tagstores, 2), - NULLMASK -}; - -static const struct pmc_masks p4_mask_br[] = { /* branch retired */ - __P4MASK(mmnp, 0), - __P4MASK(mmnm, 1), - __P4MASK(mmtp, 2), - __P4MASK(mmtm, 3), - NULLMASK -}; - -static const struct pmc_masks p4_mask_mbr[] = { /* mispred branch retired */ - __P4MASK(nbogus, 0), +/* nb hypertransport bus bandwidth */ +static const struct pmc_masks k8_mask_nhbb[] = { /* HT bus bandwidth */ + __K8MASK(command, 0), + __K8MASK(data, 1), + __K8MASK(buffer-release, 2), + __K8MASK(nop, 3), NULLMASK }; -static const struct pmc_masks p4_mask_xa[] = { /* x87 assist */ - __P4MASK(fpsu, 0), - __P4MASK(fpso, 1), - __P4MASK(poao, 2), - __P4MASK(poau, 3), - __P4MASK(prea, 4), - NULLMASK -}; +#undef __K8MASK -static const struct pmc_masks p4_mask_machclr[] = { /* machine clear */ - __P4MASK(clear, 0), - __P4MASK(moclear, 2), - __P4MASK(smclear, 3), - NULLMASK -}; +#define K8_KW_COUNT "count" +#define K8_KW_EDGE "edge" +#define K8_KW_INV "inv" +#define K8_KW_MASK "mask" +#define K8_KW_OS "os" +#define K8_KW_USR "usr" -/* P4 event parser */ static int -p4_allocate_pmc(enum pmc_event pe, char *ctrspec, +k8_allocate_pmc(enum pmc_event pe, char *ctrspec, struct pmc_op_pmcallocate *pmc_config) { - - char *e, *p, *q; - int count, has_tag, has_busreqtype, n; - uint32_t evmask, cccractivemask; - const struct pmc_masks *pm, *pmask; + char *e, *p, *q; + int n; + uint32_t count, evmask; + const struct pmc_masks *pm, *pmask; pmc_config->pm_caps |= PMC_CAP_READ; - pmc_config->pm_p4_cccrconfig = pmc_config->pm_p4_escrconfig = 0; + pmc_config->pm_md.pm_amd.pm_amd_config = 0; if (pe == PMC_EV_TSC_TSC) { - /* TSC must not be further qualified */ + /* TSC events must be unqualified. */ if (ctrspec && *ctrspec != '\0') return -1; return 0; } - pmask = NULL; - evmask = 0; - cccractivemask = 0x3; - has_tag = has_busreqtype = 0; - pmc_config->pm_caps |= PMC_CAP_WRITE; + pmask = NULL; + evmask = 0; -#define __P4SETMASK(M) do { \ - pmask = p4_mask_##M; \ -} while (0) +#define __K8SETMASK(M) pmask = k8_mask_##M + /* setup parsing tables */ switch (pe) { - case PMC_EV_P4_TC_DELIVER_MODE: - __P4SETMASK(tcdm); - break; - case PMC_EV_P4_BPU_FETCH_REQUEST: - __P4SETMASK(bfr); + case PMC_EV_K8_FP_DISPATCHED_FPU_OPS: + __K8SETMASK(fdfo); break; - case PMC_EV_P4_ITLB_REFERENCE: - __P4SETMASK(ir); + case PMC_EV_K8_LS_SEGMENT_REGISTER_LOAD: + __K8SETMASK(lsrl); break; - case PMC_EV_P4_MEMORY_CANCEL: - __P4SETMASK(memcan); + case PMC_EV_K8_LS_LOCKED_OPERATION: + __K8SETMASK(llo); break; - case PMC_EV_P4_MEMORY_COMPLETE: - __P4SETMASK(memcomp); + case PMC_EV_K8_DC_REFILL_FROM_L2: + case PMC_EV_K8_DC_REFILL_FROM_SYSTEM: + case PMC_EV_K8_DC_COPYBACK: + __K8SETMASK(dc); break; - case PMC_EV_P4_LOAD_PORT_REPLAY: - __P4SETMASK(lpr); + case PMC_EV_K8_DC_ONE_BIT_ECC_ERROR: + __K8SETMASK(dobee); break; - case PMC_EV_P4_STORE_PORT_REPLAY: - __P4SETMASK(spr); + case PMC_EV_K8_DC_DISPATCHED_PREFETCH_INSTRUCTIONS: + __K8SETMASK(ddpi); break; - case PMC_EV_P4_MOB_LOAD_REPLAY: - __P4SETMASK(mlr); + case PMC_EV_K8_DC_DCACHE_ACCESSES_BY_LOCKS: + __K8SETMASK(dabl); break; - case PMC_EV_P4_PAGE_WALK_TYPE: - __P4SETMASK(pwt); + case PMC_EV_K8_BU_INTERNAL_L2_REQUEST: + __K8SETMASK(bilr); break; - case PMC_EV_P4_BSQ_CACHE_REFERENCE: - __P4SETMASK(bcr); + case PMC_EV_K8_BU_FILL_REQUEST_L2_MISS: + __K8SETMASK(bfrlm); break; - case PMC_EV_P4_IOQ_ALLOCATION: - __P4SETMASK(ia); - has_busreqtype = 1; + case PMC_EV_K8_BU_FILL_INTO_L2: + __K8SETMASK(bfil); break; - case PMC_EV_P4_IOQ_ACTIVE_ENTRIES: - __P4SETMASK(iae); - has_busreqtype = 1; + case PMC_EV_K8_FR_RETIRED_FPU_INSTRUCTIONS: + __K8SETMASK(frfi); break; - case PMC_EV_P4_FSB_DATA_ACTIVITY: - __P4SETMASK(fda); + case PMC_EV_K8_FR_RETIRED_FASTPATH_DOUBLE_OP_INSTRUCTIONS: + __K8SETMASK(frfdoi); break; - case PMC_EV_P4_BSQ_ALLOCATION: - __P4SETMASK(ba); + case PMC_EV_K8_FR_FPU_EXCEPTIONS: + __K8SETMASK(ffe); break; - case PMC_EV_P4_SSE_INPUT_ASSIST: - __P4SETMASK(sia); + case PMC_EV_K8_NB_MEMORY_CONTROLLER_PAGE_ACCESS_EVENT: + __K8SETMASK(nmcpae); break; - case PMC_EV_P4_PACKED_SP_UOP: - __P4SETMASK(psu); + case PMC_EV_K8_NB_MEMORY_CONTROLLER_TURNAROUND: + __K8SETMASK(nmct); break; - case PMC_EV_P4_PACKED_DP_UOP: - __P4SETMASK(pdu); + case PMC_EV_K8_NB_MEMORY_CONTROLLER_BYPASS_SATURATION: + __K8SETMASK(nmcbs); break; - case PMC_EV_P4_SCALAR_SP_UOP: - __P4SETMASK(ssu); + case PMC_EV_K8_NB_SIZED_COMMANDS: + __K8SETMASK(nsc); break; - case PMC_EV_P4_SCALAR_DP_UOP: - __P4SETMASK(sdu); + case PMC_EV_K8_NB_PROBE_RESULT: + __K8SETMASK(npr); break; - case PMC_EV_P4_64BIT_MMX_UOP: - __P4SETMASK(64bmu); - break; - case PMC_EV_P4_128BIT_MMX_UOP: - __P4SETMASK(128bmu); - break; - case PMC_EV_P4_X87_FP_UOP: - __P4SETMASK(xfu); - break; - case PMC_EV_P4_X87_SIMD_MOVES_UOP: - __P4SETMASK(xsmu); - break; - case PMC_EV_P4_GLOBAL_POWER_EVENTS: - __P4SETMASK(gpe); - break; - case PMC_EV_P4_TC_MS_XFER: - __P4SETMASK(tmx); - break; - case PMC_EV_P4_UOP_QUEUE_WRITES: - __P4SETMASK(uqw); - break; - case PMC_EV_P4_RETIRED_MISPRED_BRANCH_TYPE: - __P4SETMASK(rmbt); - break; - case PMC_EV_P4_RETIRED_BRANCH_TYPE: - __P4SETMASK(rbt); - break; - case PMC_EV_P4_RESOURCE_STALL: - __P4SETMASK(rs); - break; - case PMC_EV_P4_WC_BUFFER: - __P4SETMASK(wb); - break; - case PMC_EV_P4_BSQ_ACTIVE_ENTRIES: - case PMC_EV_P4_B2B_CYCLES: - case PMC_EV_P4_BNR: - case PMC_EV_P4_SNOOP: - case PMC_EV_P4_RESPONSE: - break; - case PMC_EV_P4_FRONT_END_EVENT: - __P4SETMASK(fee); - break; - case PMC_EV_P4_EXECUTION_EVENT: - __P4SETMASK(ee); - break; - case PMC_EV_P4_REPLAY_EVENT: - __P4SETMASK(re); - break; - case PMC_EV_P4_INSTR_RETIRED: - __P4SETMASK(insret); - break; - case PMC_EV_P4_UOPS_RETIRED: - __P4SETMASK(ur); - break; - case PMC_EV_P4_UOP_TYPE: - __P4SETMASK(ut); - break; - case PMC_EV_P4_BRANCH_RETIRED: - __P4SETMASK(br); - break; - case PMC_EV_P4_MISPRED_BRANCH_RETIRED: - __P4SETMASK(mbr); - break; - case PMC_EV_P4_X87_ASSIST: - __P4SETMASK(xa); - break; - case PMC_EV_P4_MACHINE_CLEAR: - __P4SETMASK(machclr); + case PMC_EV_K8_NB_HT_BUS0_BANDWIDTH: + case PMC_EV_K8_NB_HT_BUS1_BANDWIDTH: + case PMC_EV_K8_NB_HT_BUS2_BANDWIDTH: + __K8SETMASK(nhbb); break; + default: - return -1; + break; /* no options defined */ } - /* process additional flags */ - while ((p = strsep(&ctrspec, ",")) != NULL) { - if (KWPREFIXMATCH(p, P4_KW_ACTIVE)) { - q = strchr(p, '='); - if (*++q == '\0') /* skip '=' */ - return -1; - - if (strcmp(q, P4_KW_ACTIVE_NONE) == 0) - cccractivemask = 0x0; - else if (strcmp(q, P4_KW_ACTIVE_SINGLE) == 0) - cccractivemask = 0x1; - else if (strcmp(q, P4_KW_ACTIVE_BOTH) == 0) - cccractivemask = 0x2; - else if (strcmp(q, P4_KW_ACTIVE_ANY) == 0) - cccractivemask = 0x3; - else - return -1; - - } else if (KWPREFIXMATCH(p, P4_KW_BUSREQTYPE)) { - if (has_busreqtype == 0) - return -1; + pmc_config->pm_caps |= PMC_CAP_WRITE; + while ((p = strsep(&ctrspec, ",")) != NULL) { + if (KWPREFIXMATCH(p, K8_KW_COUNT "=")) { q = strchr(p, '='); if (*++q == '\0') /* skip '=' */ return -1; @@ -810,808 +608,1286 @@ p4_allocate_pmc(enum pmc_event pe, char *ctrspec, count = strtol(q, &e, 0); if (e == q || *e != '\0') return -1; - evmask = (evmask & ~0x1F) | (count & 0x1F); - } else if (KWMATCH(p, P4_KW_CASCADE)) - pmc_config->pm_caps |= PMC_CAP_CASCADE; - else if (KWMATCH(p, P4_KW_EDGE)) + + pmc_config->pm_caps |= PMC_CAP_THRESHOLD; + pmc_config->pm_md.pm_amd.pm_amd_config |= + AMD_PMC_TO_COUNTER(count); + + } else if (KWMATCH(p, K8_KW_EDGE)) { pmc_config->pm_caps |= PMC_CAP_EDGE; - else if (KWMATCH(p, P4_KW_INV)) + } else if (KWMATCH(p, K8_KW_INV)) { pmc_config->pm_caps |= PMC_CAP_INVERT; - else if (KWPREFIXMATCH(p, P4_KW_MASK "=")) { + } else if (KWPREFIXMATCH(p, K8_KW_MASK "=")) { if ((n = pmc_parse_mask(pmask, p, &evmask)) < 0) return -1; pmc_config->pm_caps |= PMC_CAP_QUALIFIER; - } else if (KWMATCH(p, P4_KW_OS)) + } else if (KWMATCH(p, K8_KW_OS)) { pmc_config->pm_caps |= PMC_CAP_SYSTEM; - else if (KWMATCH(p, P4_KW_PRECISE)) - pmc_config->pm_caps |= PMC_CAP_PRECISE; - else if (KWPREFIXMATCH(p, P4_KW_TAG "=")) { - if (has_tag == 0) - return -1; - - q = strchr(p, '='); - if (*++q == '\0') /* skip '=' */ - return -1; - - count = strtol(q, &e, 0); - if (e == q || *e != '\0') - return -1; - - pmc_config->pm_caps |= PMC_CAP_TAGGING; - pmc_config->pm_p4_escrconfig |= - P4_ESCR_TO_TAG_VALUE(count); - } else if (KWPREFIXMATCH(p, P4_KW_THRESHOLD "=")) { - q = strchr(p, '='); - if (*++q == '\0') /* skip '=' */ - return -1; - - count = strtol(q, &e, 0); - if (e == q || *e != '\0') - return -1; - - pmc_config->pm_caps |= PMC_CAP_THRESHOLD; - pmc_config->pm_p4_cccrconfig &= ~P4_CCCR_THRESHOLD_MASK; - pmc_config->pm_p4_cccrconfig |= P4_CCCR_TO_THRESHOLD(count); - } else if (KWMATCH(p, P4_KW_USR)) + } else if (KWMATCH(p, K8_KW_USR)) { pmc_config->pm_caps |= PMC_CAP_USER; - else + } else return -1; } /* other post processing */ - if (pe == PMC_EV_P4_IOQ_ALLOCATION || - pe == PMC_EV_P4_FSB_DATA_ACTIVITY || - pe == PMC_EV_P4_BSQ_ALLOCATION) - pmc_config->pm_caps |= PMC_CAP_EDGE; - - /* fill in thread activity mask */ - pmc_config->pm_p4_cccrconfig |= - P4_CCCR_TO_ACTIVE_THREAD(cccractivemask); - - if (evmask) - pmc_config->pm_caps |= PMC_CAP_QUALIFIER; switch (pe) { - case PMC_EV_P4_FSB_DATA_ACTIVITY: - if ((evmask & 0x06) == 0x06 || - (evmask & 0x18) == 0x18) - return -1; /* can't have own+other bits together */ - if (evmask == 0) /* default:drdy-{drv,own}+dbsy{drv,own} */ - evmask = 0x1D; + case PMC_EV_K8_FP_DISPATCHED_FPU_OPS: + case PMC_EV_K8_FP_CYCLES_WITH_NO_FPU_OPS_RETIRED: + case PMC_EV_K8_FP_DISPATCHED_FPU_FAST_FLAG_OPS: + case PMC_EV_K8_FR_RETIRED_FASTPATH_DOUBLE_OP_INSTRUCTIONS: + case PMC_EV_K8_FR_RETIRED_FPU_INSTRUCTIONS: + case PMC_EV_K8_FR_FPU_EXCEPTIONS: + /* XXX only available in rev B and later */ break; - case PMC_EV_P4_MACHINE_CLEAR: - /* only one bit is allowed to be set */ - if ((evmask & (evmask - 1)) != 0) + case PMC_EV_K8_DC_DCACHE_ACCESSES_BY_LOCKS: + /* XXX only available in rev C and later */ + break; + case PMC_EV_K8_LS_LOCKED_OPERATION: + /* XXX CPU Rev A,B evmask is to be zero */ + if (evmask & (evmask - 1)) /* > 1 bit set */ return -1; if (evmask == 0) { - evmask = 0x1; /* 'CLEAR' */ + evmask = 0x01; /* Rev C and later: #instrs */ pmc_config->pm_caps |= PMC_CAP_QUALIFIER; } break; default: - if (evmask == 0 && pmask) { + if (evmask == 0 && pmask != NULL) { for (pm = pmask; pm->pm_name; pm++) evmask |= pm->pm_value; pmc_config->pm_caps |= PMC_CAP_QUALIFIER; } } - pmc_config->pm_p4_escrconfig = P4_ESCR_TO_EVENT_MASK(evmask); + if (pmc_config->pm_caps & PMC_CAP_QUALIFIER) + pmc_config->pm_md.pm_amd.pm_amd_config = + AMD_PMC_TO_UNITMASK(evmask); return 0; } +#endif + +#if defined(__i386__) + /* - * Pentium Pro style PMCs. These PMCs are found in Pentium II, Pentium III, - * and Pentium M CPUs. + * Intel P4 PMCs */ -static struct pmc_event_alias p6_aliases[] = { - EV_ALIAS("branches", "p6-br-inst-retired"), - EV_ALIAS("branch-mispredicts", "p6-br-miss-pred-retired"), +static struct pmc_event_alias p4_aliases[] = { + EV_ALIAS("branches", "p4-branch-retired,mask=mmtp+mmtm"), + EV_ALIAS("branch-mispredicts", "p4-mispred-branch-retired"), EV_ALIAS("cycles", "tsc"), - EV_ALIAS("dc-misses", "p6-dcu-lines-in"), - EV_ALIAS("ic-misses", "p6-ifu-ifetch-miss"), - EV_ALIAS("instructions", "p6-inst-retired"), - EV_ALIAS("interrupts", "p6-hw-int-rx"), + EV_ALIAS("instructions", + "p4-instr-retired,mask=nbogusntag+nbogustag"), EV_ALIAS(NULL, NULL) }; -#define P6_KW_CMASK "cmask" -#define P6_KW_EDGE "edge" -#define P6_KW_INV "inv" -#define P6_KW_OS "os" -#define P6_KW_UMASK "umask" -#define P6_KW_USR "usr" +#define P4_KW_ACTIVE "active" +#define P4_KW_ACTIVE_ANY "any" +#define P4_KW_ACTIVE_BOTH "both" +#define P4_KW_ACTIVE_NONE "none" +#define P4_KW_ACTIVE_SINGLE "single" +#define P4_KW_BUSREQTYPE "busreqtype" +#define P4_KW_CASCADE "cascade" +#define P4_KW_EDGE "edge" +#define P4_KW_INV "complement" +#define P4_KW_OS "os" +#define P4_KW_MASK "mask" +#define P4_KW_PRECISE "precise" +#define P4_KW_TAG "tag" +#define P4_KW_THRESHOLD "threshold" +#define P4_KW_USR "usr" -static struct pmc_masks p6_mask_mesi[] = { - PMCMASK(m, 0x01), - PMCMASK(e, 0x02), - PMCMASK(s, 0x04), - PMCMASK(i, 0x08), +#define __P4MASK(N,V) PMCMASK(N, (1 << (V))) + +static const struct pmc_masks p4_mask_tcdm[] = { /* tc deliver mode */ + __P4MASK(dd, 0), + __P4MASK(db, 1), + __P4MASK(di, 2), + __P4MASK(bd, 3), + __P4MASK(bb, 4), + __P4MASK(bi, 5), + __P4MASK(id, 6), + __P4MASK(ib, 7), NULLMASK }; -static struct pmc_masks p6_mask_mesihw[] = { - PMCMASK(m, 0x01), - PMCMASK(e, 0x02), - PMCMASK(s, 0x04), - PMCMASK(i, 0x08), - PMCMASK(nonhw, 0x00), - PMCMASK(hw, 0x10), - PMCMASK(both, 0x30), - NULLMASK +static const struct pmc_masks p4_mask_bfr[] = { /* bpu fetch request */ + __P4MASK(tcmiss, 0), + NULLMASK, }; -static struct pmc_masks p6_mask_hw[] = { - PMCMASK(nonhw, 0x00), - PMCMASK(hw, 0x10), - PMCMASK(both, 0x30), +static const struct pmc_masks p4_mask_ir[] = { /* itlb reference */ + __P4MASK(hit, 0), + __P4MASK(miss, 1), + __P4MASK(hit-uc, 2), NULLMASK }; -static struct pmc_masks p6_mask_any[] = { - PMCMASK(self, 0x00), - PMCMASK(any, 0x20), +static const struct pmc_masks p4_mask_memcan[] = { /* memory cancel */ + __P4MASK(st-rb-full, 2), + __P4MASK(64k-conf, 3), NULLMASK }; -static struct pmc_masks p6_mask_ekp[] = { - PMCMASK(nta, 0x00), - PMCMASK(t1, 0x01), - PMCMASK(t2, 0x02), - PMCMASK(wos, 0x03), +static const struct pmc_masks p4_mask_memcomp[] = { /* memory complete */ + __P4MASK(lsc, 0), + __P4MASK(ssc, 1), NULLMASK }; -static struct pmc_masks p6_mask_pps[] = { - PMCMASK(packed-and-scalar, 0x00), - PMCMASK(scalar, 0x01), +static const struct pmc_masks p4_mask_lpr[] = { /* load port replay */ + __P4MASK(split-ld, 1), NULLMASK }; -static struct pmc_masks p6_mask_mite[] = { - PMCMASK(packed-multiply, 0x01), - PMCMASK(packed-shift, 0x02), - PMCMASK(pack, 0x04), - PMCMASK(unpack, 0x08), - PMCMASK(packed-logical, 0x10), - PMCMASK(packed-arithmetic, 0x20), +static const struct pmc_masks p4_mask_spr[] = { /* store port replay */ + __P4MASK(split-st, 1), NULLMASK }; -static struct pmc_masks p6_mask_fmt[] = { - PMCMASK(mmxtofp, 0x00), - PMCMASK(fptommx, 0x01), +static const struct pmc_masks p4_mask_mlr[] = { /* mob load replay */ + __P4MASK(no-sta, 1), + __P4MASK(no-std, 3), + __P4MASK(partial-data, 4), + __P4MASK(unalgn-addr, 5), NULLMASK }; -static struct pmc_masks p6_mask_sr[] = { - PMCMASK(es, 0x01), - PMCMASK(ds, 0x02), - PMCMASK(fs, 0x04), - PMCMASK(gs, 0x08), +static const struct pmc_masks p4_mask_pwt[] = { /* page walk type */ + __P4MASK(dtmiss, 0), + __P4MASK(itmiss, 1), NULLMASK }; -static struct pmc_masks p6_mask_eet[] = { - PMCMASK(all, 0x00), - PMCMASK(freq, 0x02), +static const struct pmc_masks p4_mask_bcr[] = { /* bsq cache reference */ + __P4MASK(rd-2ndl-hits, 0), + __P4MASK(rd-2ndl-hite, 1), + __P4MASK(rd-2ndl-hitm, 2), + __P4MASK(rd-3rdl-hits, 3), + __P4MASK(rd-3rdl-hite, 4), + __P4MASK(rd-3rdl-hitm, 5), + __P4MASK(rd-2ndl-miss, 8), + __P4MASK(rd-3rdl-miss, 9), + __P4MASK(wr-2ndl-miss, 10), NULLMASK }; -static struct pmc_masks p6_mask_efur[] = { - PMCMASK(all, 0x00), - PMCMASK(loadop, 0x01), - PMCMASK(stdsta, 0x02), +static const struct pmc_masks p4_mask_ia[] = { /* ioq allocation */ + __P4MASK(all-read, 5), + __P4MASK(all-write, 6), + __P4MASK(mem-uc, 7), + __P4MASK(mem-wc, 8), + __P4MASK(mem-wt, 9), + __P4MASK(mem-wp, 10), + __P4MASK(mem-wb, 11), + __P4MASK(own, 13), + __P4MASK(other, 14), + __P4MASK(prefetch, 15), NULLMASK }; -static struct pmc_masks p6_mask_essir[] = { - PMCMASK(sse-packed-single, 0x00), - PMCMASK(sse-packed-single-scalar-single, 0x01), - PMCMASK(sse2-packed-double, 0x02), - PMCMASK(sse2-scalar-double, 0x03), +static const struct pmc_masks p4_mask_iae[] = { /* ioq active entries */ + __P4MASK(all-read, 5), + __P4MASK(all-write, 6), + __P4MASK(mem-uc, 7), + __P4MASK(mem-wc, 8), + __P4MASK(mem-wt, 9), + __P4MASK(mem-wp, 10), + __P4MASK(mem-wb, 11), + __P4MASK(own, 13), + __P4MASK(other, 14), + __P4MASK(prefetch, 15), NULLMASK }; -static struct pmc_masks p6_mask_esscir[] = { - PMCMASK(sse-packed-single, 0x00), - PMCMASK(sse-scalar-single, 0x01), - PMCMASK(sse2-packed-double, 0x02), - PMCMASK(sse2-scalar-double, 0x03), +static const struct pmc_masks p4_mask_fda[] = { /* fsb data activity */ + __P4MASK(drdy-drv, 0), + __P4MASK(drdy-own, 1), + __P4MASK(drdy-other, 2), + __P4MASK(dbsy-drv, 3), + __P4MASK(dbsy-own, 4), + __P4MASK(dbsy-other, 5), NULLMASK }; -/* P6 event parser */ -static int -p6_allocate_pmc(enum pmc_event pe, char *ctrspec, - struct pmc_op_pmcallocate *pmc_config) -{ - char *e, *p, *q; - uint32_t evmask; - int count, n; - const struct pmc_masks *pm, *pmask; +static const struct pmc_masks p4_mask_ba[] = { /* bsq allocation */ + __P4MASK(req-type0, 0), + __P4MASK(req-type1, 1), + __P4MASK(req-len0, 2), + __P4MASK(req-len1, 3), + __P4MASK(req-io-type, 5), + __P4MASK(req-lock-type, 6), + __P4MASK(req-cache-type, 7), + __P4MASK(req-split-type, 8), + __P4MASK(req-dem-type, 9), + __P4MASK(req-ord-type, 10), + __P4MASK(mem-type0, 11), + __P4MASK(mem-type1, 12), + __P4MASK(mem-type2, 13), + NULLMASK +}; - pmc_config->pm_caps |= PMC_CAP_READ; - pmc_config->pm_p6_config = 0; +static const struct pmc_masks p4_mask_sia[] = { /* sse input assist */ + __P4MASK(all, 15), + NULLMASK +}; - if (pe == PMC_EV_TSC_TSC) { - if (ctrspec && *ctrspec != '\0') - return -1; - return 0; - } +static const struct pmc_masks p4_mask_psu[] = { /* packed sp uop */ + __P4MASK(all, 15), + NULLMASK +}; - pmc_config->pm_caps |= PMC_CAP_WRITE; - evmask = 0; +static const struct pmc_masks p4_mask_pdu[] = { /* packed dp uop */ + __P4MASK(all, 15), + NULLMASK +}; -#define P6MASKSET(M) pmask = p6_mask_ ## M +static const struct pmc_masks p4_mask_ssu[] = { /* scalar sp uop */ + __P4MASK(all, 15), + NULLMASK +}; - switch(pe) { - case PMC_EV_P6_L2_IFETCH: P6MASKSET(mesi); break; - case PMC_EV_P6_L2_LD: P6MASKSET(mesi); break; - case PMC_EV_P6_L2_ST: P6MASKSET(mesi); break; - case PMC_EV_P6_L2_RQSTS: P6MASKSET(mesi); break; - case PMC_EV_P6_BUS_DRDY_CLOCKS: - case PMC_EV_P6_BUS_LOCK_CLOCKS: - case PMC_EV_P6_BUS_TRAN_BRD: - case PMC_EV_P6_BUS_TRAN_RFO: - case PMC_EV_P6_BUS_TRANS_WB: - case PMC_EV_P6_BUS_TRAN_IFETCH: - case PMC_EV_P6_BUS_TRAN_INVAL: - case PMC_EV_P6_BUS_TRAN_PWR: - case PMC_EV_P6_BUS_TRANS_P: - case PMC_EV_P6_BUS_TRANS_IO: - case PMC_EV_P6_BUS_TRAN_DEF: - case PMC_EV_P6_BUS_TRAN_BURST: - case PMC_EV_P6_BUS_TRAN_ANY: - case PMC_EV_P6_BUS_TRAN_MEM: - P6MASKSET(any); break; - case PMC_EV_P6_EMON_KNI_PREF_DISPATCHED: - case PMC_EV_P6_EMON_KNI_PREF_MISS: - P6MASKSET(ekp); break; - case PMC_EV_P6_EMON_KNI_INST_RETIRED: - case PMC_EV_P6_EMON_KNI_COMP_INST_RET: - P6MASKSET(pps); break; - case PMC_EV_P6_MMX_INSTR_TYPE_EXEC: - P6MASKSET(mite); break; - case PMC_EV_P6_FP_MMX_TRANS: - P6MASKSET(fmt); break; - case PMC_EV_P6_SEG_RENAME_STALLS: - case PMC_EV_P6_SEG_REG_RENAMES: - P6MASKSET(sr); break; - case PMC_EV_P6_EMON_EST_TRANS: - P6MASKSET(eet); break; - case PMC_EV_P6_EMON_FUSED_UOPS_RET: - P6MASKSET(efur); break; - case PMC_EV_P6_EMON_SSE_SSE2_INST_RETIRED: - P6MASKSET(essir); break; - case PMC_EV_P6_EMON_SSE_SSE2_COMP_INST_RETIRED: - P6MASKSET(esscir); break; - default: - pmask = NULL; - break; - } +static const struct pmc_masks p4_mask_sdu[] = { /* scalar dp uop */ + __P4MASK(all, 15), + NULLMASK +}; - /* Pentium M PMCs have a few events with different semantics */ - if (cpu_info.pm_cputype == PMC_CPU_INTEL_PM) { - if (pe == PMC_EV_P6_L2_LD || - pe == PMC_EV_P6_L2_LINES_IN || - pe == PMC_EV_P6_L2_LINES_OUT) - P6MASKSET(mesihw); - else if (pe == PMC_EV_P6_L2_M_LINES_OUTM) - P6MASKSET(hw); - } +static const struct pmc_masks p4_mask_64bmu[] = { /* 64 bit mmx uop */ + __P4MASK(all, 15), + NULLMASK +}; - /* Parse additional modifiers if present */ - while ((p = strsep(&ctrspec, ",")) != NULL) { - if (KWPREFIXMATCH(p, P6_KW_CMASK "=")) { - q = strchr(p, '='); - if (*++q == '\0') /* skip '=' */ - return -1; - count = strtol(q, &e, 0); - if (e == q || *e != '\0') - return -1; - pmc_config->pm_caps |= PMC_CAP_THRESHOLD; - pmc_config->pm_p6_config |= P6_EVSEL_TO_CMASK(count); - } else if (KWMATCH(p, P6_KW_EDGE)) { - pmc_config->pm_caps |= PMC_CAP_EDGE; - } else if (KWMATCH(p, P6_KW_INV)) { - pmc_config->pm_caps |= PMC_CAP_INVERT; - } else if (KWMATCH(p, P6_KW_OS)) { - pmc_config->pm_caps |= PMC_CAP_SYSTEM; - } else if (KWPREFIXMATCH(p, P6_KW_UMASK "=")) { - evmask = 0; - if ((n = pmc_parse_mask(pmask, p, &evmask)) < 0) - return -1; - if ((pe == PMC_EV_P6_BUS_DRDY_CLOCKS || - pe == PMC_EV_P6_BUS_LOCK_CLOCKS || - pe == PMC_EV_P6_BUS_TRAN_BRD || - pe == PMC_EV_P6_BUS_TRAN_RFO || - pe == PMC_EV_P6_BUS_TRAN_IFETCH || - pe == PMC_EV_P6_BUS_TRAN_INVAL || - pe == PMC_EV_P6_BUS_TRAN_PWR || - pe == PMC_EV_P6_BUS_TRAN_DEF || - pe == PMC_EV_P6_BUS_TRAN_BURST || - pe == PMC_EV_P6_BUS_TRAN_ANY || - pe == PMC_EV_P6_BUS_TRAN_MEM || - pe == PMC_EV_P6_BUS_TRANS_IO || - pe == PMC_EV_P6_BUS_TRANS_P || - pe == PMC_EV_P6_BUS_TRANS_WB || - pe == PMC_EV_P6_EMON_EST_TRANS || - pe == PMC_EV_P6_EMON_FUSED_UOPS_RET || - pe == PMC_EV_P6_EMON_KNI_COMP_INST_RET || - pe == PMC_EV_P6_EMON_KNI_INST_RETIRED || - pe == PMC_EV_P6_EMON_KNI_PREF_DISPATCHED || - pe == PMC_EV_P6_EMON_KNI_PREF_MISS || - pe == PMC_EV_P6_EMON_SSE_SSE2_COMP_INST_RETIRED || - pe == PMC_EV_P6_EMON_SSE_SSE2_INST_RETIRED || - pe == PMC_EV_P6_FP_MMX_TRANS) - && (n > 1)) - return -1; /* only one mask keyword allowed */ - pmc_config->pm_caps |= PMC_CAP_QUALIFIER; - } else if (KWMATCH(p, P6_KW_USR)) { - pmc_config->pm_caps |= PMC_CAP_USER; - } else - return -1; - } - - /* post processing */ - switch (pe) { - - /* - * The following events default to an evmask of 0 - */ - - /* default => 'self' */ - case PMC_EV_P6_BUS_DRDY_CLOCKS: - case PMC_EV_P6_BUS_LOCK_CLOCKS: - case PMC_EV_P6_BUS_TRAN_BRD: - case PMC_EV_P6_BUS_TRAN_RFO: - case PMC_EV_P6_BUS_TRANS_WB: - case PMC_EV_P6_BUS_TRAN_IFETCH: - case PMC_EV_P6_BUS_TRAN_INVAL: - case PMC_EV_P6_BUS_TRAN_PWR: - case PMC_EV_P6_BUS_TRANS_P: - case PMC_EV_P6_BUS_TRANS_IO: - case PMC_EV_P6_BUS_TRAN_DEF: - case PMC_EV_P6_BUS_TRAN_BURST: - case PMC_EV_P6_BUS_TRAN_ANY: - case PMC_EV_P6_BUS_TRAN_MEM: - - /* default => 'nta' */ - case PMC_EV_P6_EMON_KNI_PREF_DISPATCHED: - case PMC_EV_P6_EMON_KNI_PREF_MISS: - - /* default => 'packed and scalar' */ - case PMC_EV_P6_EMON_KNI_INST_RETIRED: - case PMC_EV_P6_EMON_KNI_COMP_INST_RET: - - /* default => 'mmx to fp transitions' */ - case PMC_EV_P6_FP_MMX_TRANS: - - /* default => 'SSE Packed Single' */ - case PMC_EV_P6_EMON_SSE_SSE2_INST_RETIRED: - case PMC_EV_P6_EMON_SSE_SSE2_COMP_INST_RETIRED: - - /* default => 'all fused micro-ops' */ - case PMC_EV_P6_EMON_FUSED_UOPS_RET: - - /* default => 'all transitions' */ - case PMC_EV_P6_EMON_EST_TRANS: - break; - - case PMC_EV_P6_MMX_UOPS_EXEC: - evmask = 0x0F; /* only value allowed */ - break; - - default: - - /* - * For all other events, set the default event mask - * to a logical OR of all the allowed event mask bits. - */ - - if (evmask == 0 && pmask) { - for (pm = pmask; pm->pm_name; pm++) - evmask |= pm->pm_value; - pmc_config->pm_caps |= PMC_CAP_QUALIFIER; - } - - break; - } - - if (pmc_config->pm_caps & PMC_CAP_QUALIFIER) - pmc_config->pm_p6_config |= P6_EVSEL_TO_UMASK(evmask); - - return 0; -} - -/* - * Pentium style PMCs - */ - -static struct pmc_event_alias p5_aliases[] = { - EV_ALIAS("cycles", "tsc"), - EV_ALIAS(NULL, NULL) -}; - -static int -p5_allocate_pmc(enum pmc_event pe, char *ctrspec, - struct pmc_op_pmcallocate *pmc_config) -{ - return -1 || pe || ctrspec || pmc_config; /* shut up gcc */ -} - -#elif defined(__amd64__) - -/* - * AMD K8 PMCs. - * - * These are very similar to AMD K7 PMCs, but support more kinds of - * events. - */ - -static struct pmc_event_alias k8_aliases[] = { - EV_ALIAS("branches", "k8-fr-retired-taken-branches"), - EV_ALIAS("branch-mispredicts", - "k8-fr-retired-taken-branches-mispredicted"), - EV_ALIAS("cycles", "tsc"), - EV_ALIAS("dc-misses", "k8-dc-miss"), - EV_ALIAS("ic-misses", "k8-ic-miss"), - EV_ALIAS("instructions", "k8-fr-retired-x86-instructions"), - EV_ALIAS("interrupts", "k8-fr-taken-hardware-interrupts"), - EV_ALIAS(NULL, NULL) +static const struct pmc_masks p4_mask_128bmu[] = { /* 128 bit mmx uop */ + __P4MASK(all, 15), + NULLMASK }; -#define __K8MASK(N,V) PMCMASK(N,(1 << (V))) - -/* - * Parsing tables - */ - -/* fp dispatched fpu ops */ -static const struct pmc_masks k8_mask_fdfo[] = { - __K8MASK(add-pipe-excluding-junk-ops, 0), - __K8MASK(multiply-pipe-excluding-junk-ops, 1), - __K8MASK(store-pipe-excluding-junk-ops, 2), - __K8MASK(add-pipe-junk-ops, 3), - __K8MASK(multiply-pipe-junk-ops, 4), - __K8MASK(store-pipe-junk-ops, 5), +static const struct pmc_masks p4_mask_xfu[] = { /* X87 fp uop */ + __P4MASK(all, 15), NULLMASK }; -/* ls segment register loads */ -static const struct pmc_masks k8_mask_lsrl[] = { - __K8MASK(es, 0), - __K8MASK(cs, 1), - __K8MASK(ss, 2), - __K8MASK(ds, 3), - __K8MASK(fs, 4), - __K8MASK(gs, 5), - __K8MASK(hs, 6), +static const struct pmc_masks p4_mask_xsmu[] = { /* x87 simd moves uop */ + __P4MASK(allp0, 3), + __P4MASK(allp2, 4), NULLMASK }; -/* ls locked operation */ -static const struct pmc_masks k8_mask_llo[] = { - __K8MASK(locked-instructions, 0), - __K8MASK(cycles-in-request, 1), - __K8MASK(cycles-to-complete, 2), +static const struct pmc_masks p4_mask_gpe[] = { /* global power events */ + __P4MASK(running, 0), NULLMASK }; -/* dc refill from {l2,system} and dc copyback */ -static const struct pmc_masks k8_mask_dc[] = { - __K8MASK(invalid, 0), - __K8MASK(shared, 1), - __K8MASK(exclusive, 2), - __K8MASK(owner, 3), - __K8MASK(modified, 4), +static const struct pmc_masks p4_mask_tmx[] = { /* TC ms xfer */ + __P4MASK(cisc, 0), NULLMASK }; -/* dc one bit ecc error */ -static const struct pmc_masks k8_mask_dobee[] = { - __K8MASK(scrubber, 0), - __K8MASK(piggyback, 1), +static const struct pmc_masks p4_mask_uqw[] = { /* uop queue writes */ + __P4MASK(from-tc-build, 0), + __P4MASK(from-tc-deliver, 1), + __P4MASK(from-rom, 2), NULLMASK }; -/* dc dispatched prefetch instructions */ -static const struct pmc_masks k8_mask_ddpi[] = { - __K8MASK(load, 0), - __K8MASK(store, 1), - __K8MASK(nta, 2), +static const struct pmc_masks p4_mask_rmbt[] = { + /* retired mispred branch type */ + __P4MASK(conditional, 1), + __P4MASK(call, 2), + __P4MASK(return, 3), + __P4MASK(indirect, 4), NULLMASK }; -/* dc dcache accesses by locks */ -static const struct pmc_masks k8_mask_dabl[] = { - __K8MASK(accesses, 0), - __K8MASK(misses, 1), +static const struct pmc_masks p4_mask_rbt[] = { /* retired branch type */ + __P4MASK(conditional, 1), + __P4MASK(call, 2), + __P4MASK(retired, 3), + __P4MASK(indirect, 4), NULLMASK }; -/* bu internal l2 request */ -static const struct pmc_masks k8_mask_bilr[] = { - __K8MASK(ic-fill, 0), - __K8MASK(dc-fill, 1), - __K8MASK(tlb-reload, 2), - __K8MASK(tag-snoop, 3), - __K8MASK(cancelled, 4), +static const struct pmc_masks p4_mask_rs[] = { /* resource stall */ + __P4MASK(sbfull, 5), NULLMASK }; -/* bu fill request l2 miss */ -static const struct pmc_masks k8_mask_bfrlm[] = { - __K8MASK(ic-fill, 0), - __K8MASK(dc-fill, 1), - __K8MASK(tlb-reload, 2), +static const struct pmc_masks p4_mask_wb[] = { /* WC buffer */ + __P4MASK(wcb-evicts, 0), + __P4MASK(wcb-full-evict, 1), NULLMASK }; -/* bu fill into l2 */ -static const struct pmc_masks k8_mask_bfil[] = { - __K8MASK(dirty-l2-victim, 0), - __K8MASK(victim-from-l2, 1), +static const struct pmc_masks p4_mask_fee[] = { /* front end event */ + __P4MASK(nbogus, 0), + __P4MASK(bogus, 1), NULLMASK }; -/* fr retired fpu instructions */ -static const struct pmc_masks k8_mask_frfi[] = { - __K8MASK(x87, 0), - __K8MASK(mmx-3dnow, 1), - __K8MASK(packed-sse-sse2, 2), - __K8MASK(scalar-sse-sse2, 3), +static const struct pmc_masks p4_mask_ee[] = { /* execution event */ + __P4MASK(nbogus0, 0), + __P4MASK(nbogus1, 1), + __P4MASK(nbogus2, 2), + __P4MASK(nbogus3, 3), + __P4MASK(bogus0, 4), + __P4MASK(bogus1, 5), + __P4MASK(bogus2, 6), + __P4MASK(bogus3, 7), NULLMASK }; -/* fr retired fastpath double op instructions */ -static const struct pmc_masks k8_mask_frfdoi[] = { - __K8MASK(low-op-pos-0, 0), - __K8MASK(low-op-pos-1, 1), - __K8MASK(low-op-pos-2, 2), +static const struct pmc_masks p4_mask_re[] = { /* replay event */ + __P4MASK(nbogus, 0), + __P4MASK(bogus, 1), NULLMASK }; -/* fr fpu exceptions */ -static const struct pmc_masks k8_mask_ffe[] = { - __K8MASK(x87-reclass-microfaults, 0), - __K8MASK(sse-retype-microfaults, 1), - __K8MASK(sse-reclass-microfaults, 2), - __K8MASK(sse-and-x87-microtraps, 3), +static const struct pmc_masks p4_mask_insret[] = { /* instr retired */ + __P4MASK(nbogusntag, 0), + __P4MASK(nbogustag, 1), + __P4MASK(bogusntag, 2), + __P4MASK(bogustag, 3), NULLMASK }; -/* nb memory controller page access event */ -static const struct pmc_masks k8_mask_nmcpae[] = { - __K8MASK(page-hit, 0), - __K8MASK(page-miss, 1), - __K8MASK(page-conflict, 2), +static const struct pmc_masks p4_mask_ur[] = { /* uops retired */ + __P4MASK(nbogus, 0), + __P4MASK(bogus, 1), NULLMASK }; -/* nb memory controller turnaround */ -static const struct pmc_masks k8_mask_nmct[] = { - __K8MASK(dimm-turnaround, 0), - __K8MASK(read-to-write-turnaround, 1), - __K8MASK(write-to-read-turnaround, 2), +static const struct pmc_masks p4_mask_ut[] = { /* uop type */ + __P4MASK(tagloads, 1), + __P4MASK(tagstores, 2), NULLMASK }; -/* nb memory controller bypass saturation */ -static const struct pmc_masks k8_mask_nmcbs[] = { - __K8MASK(memory-controller-hi-pri-bypass, 0), - __K8MASK(memory-controller-lo-pri-bypass, 1), - __K8MASK(dram-controller-interface-bypass, 2), - __K8MASK(dram-controller-queue-bypass, 3), +static const struct pmc_masks p4_mask_br[] = { /* branch retired */ + __P4MASK(mmnp, 0), + __P4MASK(mmnm, 1), + __P4MASK(mmtp, 2), + __P4MASK(mmtm, 3), NULLMASK }; -/* nb sized commands */ -static const struct pmc_masks k8_mask_nsc[] = { - __K8MASK(nonpostwrszbyte, 0), - __K8MASK(nonpostwrszdword, 1), - __K8MASK(postwrszbyte, 2), - __K8MASK(postwrszdword, 3), - __K8MASK(rdszbyte, 4), - __K8MASK(rdszdword, 5), - __K8MASK(rdmodwr, 6), +static const struct pmc_masks p4_mask_mbr[] = { /* mispred branch retired */ + __P4MASK(nbogus, 0), NULLMASK }; -/* nb probe result */ -static const struct pmc_masks k8_mask_npr[] = { - __K8MASK(probe-miss, 0), - __K8MASK(probe-hit, 1), - __K8MASK(probe-hit-dirty-no-memory-cancel, 2), - __K8MASK(probe-hit-dirty-with-memory-cancel, 3), +static const struct pmc_masks p4_mask_xa[] = { /* x87 assist */ + __P4MASK(fpsu, 0), + __P4MASK(fpso, 1), + __P4MASK(poao, 2), + __P4MASK(poau, 3), + __P4MASK(prea, 4), NULLMASK }; -/* nb hypertransport bus bandwidth */ -static const struct pmc_masks k8_mask_nhbb[] = { /* HT bus bandwidth */ - __K8MASK(command, 0), - __K8MASK(data, 1), - __K8MASK(buffer-release, 2), - __K8MASK(nop, 3), +static const struct pmc_masks p4_mask_machclr[] = { /* machine clear */ + __P4MASK(clear, 0), + __P4MASK(moclear, 2), + __P4MASK(smclear, 3), NULLMASK }; -#undef __K8MASK - -#define K8_KW_COUNT "count" -#define K8_KW_EDGE "edge" -#define K8_KW_INV "inv" -#define K8_KW_MASK "mask" -#define K8_KW_OS "os" -#define K8_KW_USR "usr" - +/* P4 event parser */ static int -k8_allocate_pmc(enum pmc_event pe, char *ctrspec, +p4_allocate_pmc(enum pmc_event pe, char *ctrspec, struct pmc_op_pmcallocate *pmc_config) { - char *e, *p, *q; - int n; - uint32_t count, evmask; - const struct pmc_masks *pm, *pmask; + + char *e, *p, *q; + int count, has_tag, has_busreqtype, n; + uint32_t evmask, cccractivemask; + const struct pmc_masks *pm, *pmask; pmc_config->pm_caps |= PMC_CAP_READ; - pmc_config->pm_amd_config = 0; + pmc_config->pm_md.pm_p4.pm_p4_cccrconfig = + pmc_config->pm_md.pm_p4.pm_p4_escrconfig = 0; if (pe == PMC_EV_TSC_TSC) { - /* TSC events must be unqualified. */ + /* TSC must not be further qualified */ if (ctrspec && *ctrspec != '\0') return -1; return 0; } - pmask = NULL; - evmask = 0; + pmask = NULL; + evmask = 0; + cccractivemask = 0x3; + has_tag = has_busreqtype = 0; + pmc_config->pm_caps |= PMC_CAP_WRITE; -#define __K8SETMASK(M) pmask = k8_mask_##M +#define __P4SETMASK(M) do { \ + pmask = p4_mask_##M; \ +} while (0) - /* setup parsing tables */ switch (pe) { - case PMC_EV_K8_FP_DISPATCHED_FPU_OPS: - __K8SETMASK(fdfo); + case PMC_EV_P4_TC_DELIVER_MODE: + __P4SETMASK(tcdm); break; - case PMC_EV_K8_LS_SEGMENT_REGISTER_LOAD: - __K8SETMASK(lsrl); + case PMC_EV_P4_BPU_FETCH_REQUEST: + __P4SETMASK(bfr); break; - case PMC_EV_K8_LS_LOCKED_OPERATION: - __K8SETMASK(llo); + case PMC_EV_P4_ITLB_REFERENCE: + __P4SETMASK(ir); break; - case PMC_EV_K8_DC_REFILL_FROM_L2: - case PMC_EV_K8_DC_REFILL_FROM_SYSTEM: - case PMC_EV_K8_DC_COPYBACK: - __K8SETMASK(dc); + case PMC_EV_P4_MEMORY_CANCEL: + __P4SETMASK(memcan); break; - case PMC_EV_K8_DC_ONE_BIT_ECC_ERROR: - __K8SETMASK(dobee); + case PMC_EV_P4_MEMORY_COMPLETE: + __P4SETMASK(memcomp); break; - case PMC_EV_K8_DC_DISPATCHED_PREFETCH_INSTRUCTIONS: - __K8SETMASK(ddpi); + case PMC_EV_P4_LOAD_PORT_REPLAY: + __P4SETMASK(lpr); break; - case PMC_EV_K8_DC_DCACHE_ACCESSES_BY_LOCKS: - __K8SETMASK(dabl); + case PMC_EV_P4_STORE_PORT_REPLAY: + __P4SETMASK(spr); break; - case PMC_EV_K8_BU_INTERNAL_L2_REQUEST: - __K8SETMASK(bilr); + case PMC_EV_P4_MOB_LOAD_REPLAY: + __P4SETMASK(mlr); break; - case PMC_EV_K8_BU_FILL_REQUEST_L2_MISS: - __K8SETMASK(bfrlm); + case PMC_EV_P4_PAGE_WALK_TYPE: + __P4SETMASK(pwt); break; - case PMC_EV_K8_BU_FILL_INTO_L2: - __K8SETMASK(bfil); + case PMC_EV_P4_BSQ_CACHE_REFERENCE: + __P4SETMASK(bcr); break; - case PMC_EV_K8_FR_RETIRED_FPU_INSTRUCTIONS: - __K8SETMASK(frfi); + case PMC_EV_P4_IOQ_ALLOCATION: + __P4SETMASK(ia); + has_busreqtype = 1; break; - case PMC_EV_K8_FR_RETIRED_FASTPATH_DOUBLE_OP_INSTRUCTIONS: - __K8SETMASK(frfdoi); + case PMC_EV_P4_IOQ_ACTIVE_ENTRIES: + __P4SETMASK(iae); + has_busreqtype = 1; break; - case PMC_EV_K8_FR_FPU_EXCEPTIONS: - __K8SETMASK(ffe); + case PMC_EV_P4_FSB_DATA_ACTIVITY: + __P4SETMASK(fda); break; - case PMC_EV_K8_NB_MEMORY_CONTROLLER_PAGE_ACCESS_EVENT: - __K8SETMASK(nmcpae); + case PMC_EV_P4_BSQ_ALLOCATION: + __P4SETMASK(ba); break; - case PMC_EV_K8_NB_MEMORY_CONTROLLER_TURNAROUND: - __K8SETMASK(nmct); + case PMC_EV_P4_SSE_INPUT_ASSIST: + __P4SETMASK(sia); break; - case PMC_EV_K8_NB_MEMORY_CONTROLLER_BYPASS_SATURATION: - __K8SETMASK(nmcbs); + case PMC_EV_P4_PACKED_SP_UOP: + __P4SETMASK(psu); break; - case PMC_EV_K8_NB_SIZED_COMMANDS: - __K8SETMASK(nsc); + case PMC_EV_P4_PACKED_DP_UOP: + __P4SETMASK(pdu); break; - case PMC_EV_K8_NB_PROBE_RESULT: - __K8SETMASK(npr); + case PMC_EV_P4_SCALAR_SP_UOP: + __P4SETMASK(ssu); break; - case PMC_EV_K8_NB_HT_BUS0_BANDWIDTH: - case PMC_EV_K8_NB_HT_BUS1_BANDWIDTH: - case PMC_EV_K8_NB_HT_BUS2_BANDWIDTH: - __K8SETMASK(nhbb); + case PMC_EV_P4_SCALAR_DP_UOP: + __P4SETMASK(sdu); + break; + case PMC_EV_P4_64BIT_MMX_UOP: + __P4SETMASK(64bmu); + break; + case PMC_EV_P4_128BIT_MMX_UOP: + __P4SETMASK(128bmu); + break; + case PMC_EV_P4_X87_FP_UOP: + __P4SETMASK(xfu); + break; + case PMC_EV_P4_X87_SIMD_MOVES_UOP: + __P4SETMASK(xsmu); + break; + case PMC_EV_P4_GLOBAL_POWER_EVENTS: + __P4SETMASK(gpe); + break; + case PMC_EV_P4_TC_MS_XFER: + __P4SETMASK(tmx); + break; + case PMC_EV_P4_UOP_QUEUE_WRITES: + __P4SETMASK(uqw); + break; + case PMC_EV_P4_RETIRED_MISPRED_BRANCH_TYPE: + __P4SETMASK(rmbt); + break; + case PMC_EV_P4_RETIRED_BRANCH_TYPE: + __P4SETMASK(rbt); + break; + case PMC_EV_P4_RESOURCE_STALL: + __P4SETMASK(rs); + break; + case PMC_EV_P4_WC_BUFFER: + __P4SETMASK(wb); + break; + case PMC_EV_P4_BSQ_ACTIVE_ENTRIES: + case PMC_EV_P4_B2B_CYCLES: + case PMC_EV_P4_BNR: + case PMC_EV_P4_SNOOP: + case PMC_EV_P4_RESPONSE: + break; + case PMC_EV_P4_FRONT_END_EVENT: + __P4SETMASK(fee); + break; + case PMC_EV_P4_EXECUTION_EVENT: + __P4SETMASK(ee); + break; + case PMC_EV_P4_REPLAY_EVENT: + __P4SETMASK(re); + break; + case PMC_EV_P4_INSTR_RETIRED: + __P4SETMASK(insret); + break; + case PMC_EV_P4_UOPS_RETIRED: + __P4SETMASK(ur); + break; + case PMC_EV_P4_UOP_TYPE: + __P4SETMASK(ut); + break; + case PMC_EV_P4_BRANCH_RETIRED: + __P4SETMASK(br); + break; + case PMC_EV_P4_MISPRED_BRANCH_RETIRED: + __P4SETMASK(mbr); + break; + case PMC_EV_P4_X87_ASSIST: + __P4SETMASK(xa); + break; + case PMC_EV_P4_MACHINE_CLEAR: + __P4SETMASK(machclr); break; - default: - break; /* no options defined */ + return -1; } - pmc_config->pm_caps |= PMC_CAP_WRITE; - + /* process additional flags */ while ((p = strsep(&ctrspec, ",")) != NULL) { - if (KWPREFIXMATCH(p, K8_KW_COUNT "=")) { + if (KWPREFIXMATCH(p, P4_KW_ACTIVE)) { q = strchr(p, '='); if (*++q == '\0') /* skip '=' */ return -1; - count = strtol(q, &e, 0); - if (e == q || *e != '\0') + if (strcmp(q, P4_KW_ACTIVE_NONE) == 0) + cccractivemask = 0x0; + else if (strcmp(q, P4_KW_ACTIVE_SINGLE) == 0) + cccractivemask = 0x1; + else if (strcmp(q, P4_KW_ACTIVE_BOTH) == 0) + cccractivemask = 0x2; + else if (strcmp(q, P4_KW_ACTIVE_ANY) == 0) + cccractivemask = 0x3; + else return -1; - pmc_config->pm_caps |= PMC_CAP_THRESHOLD; - pmc_config->pm_amd_config |= K8_PMC_TO_COUNTER(count); + } else if (KWPREFIXMATCH(p, P4_KW_BUSREQTYPE)) { + if (has_busreqtype == 0) + return -1; - } else if (KWMATCH(p, K8_KW_EDGE)) { - pmc_config->pm_caps |= PMC_CAP_EDGE; - } else if (KWMATCH(p, K8_KW_INV)) { - pmc_config->pm_caps |= PMC_CAP_INVERT; - } else if (KWPREFIXMATCH(p, K8_KW_MASK "=")) { + q = strchr(p, '='); + if (*++q == '\0') /* skip '=' */ + return -1; + + count = strtol(q, &e, 0); + if (e == q || *e != '\0') + return -1; + evmask = (evmask & ~0x1F) | (count & 0x1F); + } else if (KWMATCH(p, P4_KW_CASCADE)) + pmc_config->pm_caps |= PMC_CAP_CASCADE; + else if (KWMATCH(p, P4_KW_EDGE)) + pmc_config->pm_caps |= PMC_CAP_EDGE; + else if (KWMATCH(p, P4_KW_INV)) + pmc_config->pm_caps |= PMC_CAP_INVERT; + else if (KWPREFIXMATCH(p, P4_KW_MASK "=")) { if ((n = pmc_parse_mask(pmask, p, &evmask)) < 0) return -1; pmc_config->pm_caps |= PMC_CAP_QUALIFIER; - } else if (KWMATCH(p, K8_KW_OS)) { + } else if (KWMATCH(p, P4_KW_OS)) pmc_config->pm_caps |= PMC_CAP_SYSTEM; - } else if (KWMATCH(p, K8_KW_USR)) { + else if (KWMATCH(p, P4_KW_PRECISE)) + pmc_config->pm_caps |= PMC_CAP_PRECISE; + else if (KWPREFIXMATCH(p, P4_KW_TAG "=")) { + if (has_tag == 0) + return -1; + + q = strchr(p, '='); + if (*++q == '\0') /* skip '=' */ + return -1; + + count = strtol(q, &e, 0); + if (e == q || *e != '\0') + return -1; + + pmc_config->pm_caps |= PMC_CAP_TAGGING; + pmc_config->pm_md.pm_p4.pm_p4_escrconfig |= + P4_ESCR_TO_TAG_VALUE(count); + } else if (KWPREFIXMATCH(p, P4_KW_THRESHOLD "=")) { + q = strchr(p, '='); + if (*++q == '\0') /* skip '=' */ + return -1; + + count = strtol(q, &e, 0); + if (e == q || *e != '\0') + return -1; + + pmc_config->pm_caps |= PMC_CAP_THRESHOLD; + pmc_config->pm_md.pm_p4.pm_p4_cccrconfig &= + ~P4_CCCR_THRESHOLD_MASK; + pmc_config->pm_md.pm_p4.pm_p4_cccrconfig |= + P4_CCCR_TO_THRESHOLD(count); + } else if (KWMATCH(p, P4_KW_USR)) pmc_config->pm_caps |= PMC_CAP_USER; - } else + else return -1; } /* other post processing */ + if (pe == PMC_EV_P4_IOQ_ALLOCATION || + pe == PMC_EV_P4_FSB_DATA_ACTIVITY || + pe == PMC_EV_P4_BSQ_ALLOCATION) + pmc_config->pm_caps |= PMC_CAP_EDGE; + + /* fill in thread activity mask */ + pmc_config->pm_md.pm_p4.pm_p4_cccrconfig |= + P4_CCCR_TO_ACTIVE_THREAD(cccractivemask); + + if (evmask) + pmc_config->pm_caps |= PMC_CAP_QUALIFIER; switch (pe) { - case PMC_EV_K8_FP_DISPATCHED_FPU_OPS: - case PMC_EV_K8_FP_CYCLES_WITH_NO_FPU_OPS_RETIRED: - case PMC_EV_K8_FP_DISPATCHED_FPU_FAST_FLAG_OPS: - case PMC_EV_K8_FR_RETIRED_FASTPATH_DOUBLE_OP_INSTRUCTIONS: - case PMC_EV_K8_FR_RETIRED_FPU_INSTRUCTIONS: - case PMC_EV_K8_FR_FPU_EXCEPTIONS: - /* XXX only available in rev B and later */ - break; - case PMC_EV_K8_DC_DCACHE_ACCESSES_BY_LOCKS: - /* XXX only available in rev C and later */ + case PMC_EV_P4_FSB_DATA_ACTIVITY: + if ((evmask & 0x06) == 0x06 || + (evmask & 0x18) == 0x18) + return -1; /* can't have own+other bits together */ + if (evmask == 0) /* default:drdy-{drv,own}+dbsy{drv,own} */ + evmask = 0x1D; break; - case PMC_EV_K8_LS_LOCKED_OPERATION: - /* XXX CPU Rev A,B evmask is to be zero */ - if (evmask & (evmask - 1)) /* > 1 bit set */ + case PMC_EV_P4_MACHINE_CLEAR: + /* only one bit is allowed to be set */ + if ((evmask & (evmask - 1)) != 0) return -1; if (evmask == 0) { - evmask = 0x01; /* Rev C and later: #instrs */ + evmask = 0x1; /* 'CLEAR' */ + pmc_config->pm_caps |= PMC_CAP_QUALIFIER; + } + break; + default: + if (evmask == 0 && pmask) { + for (pm = pmask; pm->pm_name; pm++) + evmask |= pm->pm_value; pmc_config->pm_caps |= PMC_CAP_QUALIFIER; } + } + + pmc_config->pm_md.pm_p4.pm_p4_escrconfig = + P4_ESCR_TO_EVENT_MASK(evmask); + + return 0; +} + +/* + * Pentium style PMCs + */ + +static struct pmc_event_alias p5_aliases[] = { + EV_ALIAS("cycles", "tsc"), + EV_ALIAS(NULL, NULL) +}; + +static int +p5_allocate_pmc(enum pmc_event pe, char *ctrspec, + struct pmc_op_pmcallocate *pmc_config) +{ + return -1 || pe || ctrspec || pmc_config; /* shut up gcc */ +} + +/* + * Pentium Pro style PMCs. These PMCs are found in Pentium II, Pentium III, + * and Pentium M CPUs. + */ + +static struct pmc_event_alias p6_aliases[] = { + EV_ALIAS("branches", "p6-br-inst-retired"), + EV_ALIAS("branch-mispredicts", "p6-br-miss-pred-retired"), + EV_ALIAS("cycles", "tsc"), + EV_ALIAS("dc-misses", "p6-dcu-lines-in"), + EV_ALIAS("ic-misses", "p6-ifu-ifetch-miss"), + EV_ALIAS("instructions", "p6-inst-retired"), + EV_ALIAS("interrupts", "p6-hw-int-rx"), + EV_ALIAS(NULL, NULL) +}; + +#define P6_KW_CMASK "cmask" +#define P6_KW_EDGE "edge" +#define P6_KW_INV "inv" +#define P6_KW_OS "os" +#define P6_KW_UMASK "umask" +#define P6_KW_USR "usr" + +static struct pmc_masks p6_mask_mesi[] = { + PMCMASK(m, 0x01), + PMCMASK(e, 0x02), + PMCMASK(s, 0x04), + PMCMASK(i, 0x08), + NULLMASK +}; + +static struct pmc_masks p6_mask_mesihw[] = { + PMCMASK(m, 0x01), + PMCMASK(e, 0x02), + PMCMASK(s, 0x04), + PMCMASK(i, 0x08), + PMCMASK(nonhw, 0x00), + PMCMASK(hw, 0x10), + PMCMASK(both, 0x30), + NULLMASK +}; + +static struct pmc_masks p6_mask_hw[] = { + PMCMASK(nonhw, 0x00), + PMCMASK(hw, 0x10), + PMCMASK(both, 0x30), + NULLMASK +}; + +static struct pmc_masks p6_mask_any[] = { + PMCMASK(self, 0x00), + PMCMASK(any, 0x20), + NULLMASK +}; + +static struct pmc_masks p6_mask_ekp[] = { + PMCMASK(nta, 0x00), + PMCMASK(t1, 0x01), + PMCMASK(t2, 0x02), + PMCMASK(wos, 0x03), + NULLMASK +}; + +static struct pmc_masks p6_mask_pps[] = { + PMCMASK(packed-and-scalar, 0x00), + PMCMASK(scalar, 0x01), + NULLMASK +}; + +static struct pmc_masks p6_mask_mite[] = { + PMCMASK(packed-multiply, 0x01), + PMCMASK(packed-shift, 0x02), + PMCMASK(pack, 0x04), + PMCMASK(unpack, 0x08), + PMCMASK(packed-logical, 0x10), + PMCMASK(packed-arithmetic, 0x20), + NULLMASK +}; + +static struct pmc_masks p6_mask_fmt[] = { + PMCMASK(mmxtofp, 0x00), + PMCMASK(fptommx, 0x01), + NULLMASK +}; + +static struct pmc_masks p6_mask_sr[] = { + PMCMASK(es, 0x01), + PMCMASK(ds, 0x02), + PMCMASK(fs, 0x04), + PMCMASK(gs, 0x08), + NULLMASK +}; + +static struct pmc_masks p6_mask_eet[] = { + PMCMASK(all, 0x00), + PMCMASK(freq, 0x02), + NULLMASK +}; + +static struct pmc_masks p6_mask_efur[] = { + PMCMASK(all, 0x00), + PMCMASK(loadop, 0x01), + PMCMASK(stdsta, 0x02), + NULLMASK +}; + +static struct pmc_masks p6_mask_essir[] = { + PMCMASK(sse-packed-single, 0x00), + PMCMASK(sse-packed-single-scalar-single, 0x01), + PMCMASK(sse2-packed-double, 0x02), + PMCMASK(sse2-scalar-double, 0x03), + NULLMASK +}; + +static struct pmc_masks p6_mask_esscir[] = { + PMCMASK(sse-packed-single, 0x00), + PMCMASK(sse-scalar-single, 0x01), + PMCMASK(sse2-packed-double, 0x02), + PMCMASK(sse2-scalar-double, 0x03), + NULLMASK +}; + +/* P6 event parser */ +static int +p6_allocate_pmc(enum pmc_event pe, char *ctrspec, + struct pmc_op_pmcallocate *pmc_config) +{ + char *e, *p, *q; + uint32_t evmask; + int count, n; + const struct pmc_masks *pm, *pmask; + + pmc_config->pm_caps |= PMC_CAP_READ; + pmc_config->pm_md.pm_ppro.pm_ppro_config = 0; + + if (pe == PMC_EV_TSC_TSC) { + if (ctrspec && *ctrspec != '\0') + return -1; + return 0; + } + + pmc_config->pm_caps |= PMC_CAP_WRITE; + evmask = 0; + +#define P6MASKSET(M) pmask = p6_mask_ ## M + + switch(pe) { + case PMC_EV_P6_L2_IFETCH: P6MASKSET(mesi); break; + case PMC_EV_P6_L2_LD: P6MASKSET(mesi); break; + case PMC_EV_P6_L2_ST: P6MASKSET(mesi); break; + case PMC_EV_P6_L2_RQSTS: P6MASKSET(mesi); break; + case PMC_EV_P6_BUS_DRDY_CLOCKS: + case PMC_EV_P6_BUS_LOCK_CLOCKS: + case PMC_EV_P6_BUS_TRAN_BRD: + case PMC_EV_P6_BUS_TRAN_RFO: + case PMC_EV_P6_BUS_TRANS_WB: + case PMC_EV_P6_BUS_TRAN_IFETCH: + case PMC_EV_P6_BUS_TRAN_INVAL: + case PMC_EV_P6_BUS_TRAN_PWR: + case PMC_EV_P6_BUS_TRANS_P: + case PMC_EV_P6_BUS_TRANS_IO: + case PMC_EV_P6_BUS_TRAN_DEF: + case PMC_EV_P6_BUS_TRAN_BURST: + case PMC_EV_P6_BUS_TRAN_ANY: + case PMC_EV_P6_BUS_TRAN_MEM: + P6MASKSET(any); break; + case PMC_EV_P6_EMON_KNI_PREF_DISPATCHED: + case PMC_EV_P6_EMON_KNI_PREF_MISS: + P6MASKSET(ekp); break; + case PMC_EV_P6_EMON_KNI_INST_RETIRED: + case PMC_EV_P6_EMON_KNI_COMP_INST_RET: + P6MASKSET(pps); break; + case PMC_EV_P6_MMX_INSTR_TYPE_EXEC: + P6MASKSET(mite); break; + case PMC_EV_P6_FP_MMX_TRANS: + P6MASKSET(fmt); break; + case PMC_EV_P6_SEG_RENAME_STALLS: + case PMC_EV_P6_SEG_REG_RENAMES: + P6MASKSET(sr); break; + case PMC_EV_P6_EMON_EST_TRANS: + P6MASKSET(eet); break; + case PMC_EV_P6_EMON_FUSED_UOPS_RET: + P6MASKSET(efur); break; + case PMC_EV_P6_EMON_SSE_SSE2_INST_RETIRED: + P6MASKSET(essir); break; + case PMC_EV_P6_EMON_SSE_SSE2_COMP_INST_RETIRED: + P6MASKSET(esscir); break; + default: + pmask = NULL; + break; + } + + /* Pentium M PMCs have a few events with different semantics */ + if (cpu_info.pm_cputype == PMC_CPU_INTEL_PM) { + if (pe == PMC_EV_P6_L2_LD || + pe == PMC_EV_P6_L2_LINES_IN || + pe == PMC_EV_P6_L2_LINES_OUT) + P6MASKSET(mesihw); + else if (pe == PMC_EV_P6_L2_M_LINES_OUTM) + P6MASKSET(hw); + } + + /* Parse additional modifiers if present */ + while ((p = strsep(&ctrspec, ",")) != NULL) { + if (KWPREFIXMATCH(p, P6_KW_CMASK "=")) { + q = strchr(p, '='); + if (*++q == '\0') /* skip '=' */ + return -1; + count = strtol(q, &e, 0); + if (e == q || *e != '\0') + return -1; + pmc_config->pm_caps |= PMC_CAP_THRESHOLD; + pmc_config->pm_md.pm_ppro.pm_ppro_config |= + P6_EVSEL_TO_CMASK(count); + } else if (KWMATCH(p, P6_KW_EDGE)) { + pmc_config->pm_caps |= PMC_CAP_EDGE; + } else if (KWMATCH(p, P6_KW_INV)) { + pmc_config->pm_caps |= PMC_CAP_INVERT; + } else if (KWMATCH(p, P6_KW_OS)) { + pmc_config->pm_caps |= PMC_CAP_SYSTEM; + } else if (KWPREFIXMATCH(p, P6_KW_UMASK "=")) { + evmask = 0; + if ((n = pmc_parse_mask(pmask, p, &evmask)) < 0) + return -1; + if ((pe == PMC_EV_P6_BUS_DRDY_CLOCKS || + pe == PMC_EV_P6_BUS_LOCK_CLOCKS || + pe == PMC_EV_P6_BUS_TRAN_BRD || + pe == PMC_EV_P6_BUS_TRAN_RFO || + pe == PMC_EV_P6_BUS_TRAN_IFETCH || + pe == PMC_EV_P6_BUS_TRAN_INVAL || + pe == PMC_EV_P6_BUS_TRAN_PWR || + pe == PMC_EV_P6_BUS_TRAN_DEF || + pe == PMC_EV_P6_BUS_TRAN_BURST || + pe == PMC_EV_P6_BUS_TRAN_ANY || + pe == PMC_EV_P6_BUS_TRAN_MEM || + pe == PMC_EV_P6_BUS_TRANS_IO || + pe == PMC_EV_P6_BUS_TRANS_P || + pe == PMC_EV_P6_BUS_TRANS_WB || + pe == PMC_EV_P6_EMON_EST_TRANS || + pe == PMC_EV_P6_EMON_FUSED_UOPS_RET || + pe == PMC_EV_P6_EMON_KNI_COMP_INST_RET || + pe == PMC_EV_P6_EMON_KNI_INST_RETIRED || + pe == PMC_EV_P6_EMON_KNI_PREF_DISPATCHED || + pe == PMC_EV_P6_EMON_KNI_PREF_MISS || + pe == PMC_EV_P6_EMON_SSE_SSE2_COMP_INST_RETIRED || + pe == PMC_EV_P6_EMON_SSE_SSE2_INST_RETIRED || + pe == PMC_EV_P6_FP_MMX_TRANS) + && (n > 1)) + return -1; /* only one mask keyword allowed */ + pmc_config->pm_caps |= PMC_CAP_QUALIFIER; + } else if (KWMATCH(p, P6_KW_USR)) { + pmc_config->pm_caps |= PMC_CAP_USER; + } else + return -1; + } + + /* post processing */ + switch (pe) { + + /* + * The following events default to an evmask of 0 + */ + + /* default => 'self' */ + case PMC_EV_P6_BUS_DRDY_CLOCKS: + case PMC_EV_P6_BUS_LOCK_CLOCKS: + case PMC_EV_P6_BUS_TRAN_BRD: + case PMC_EV_P6_BUS_TRAN_RFO: + case PMC_EV_P6_BUS_TRANS_WB: + case PMC_EV_P6_BUS_TRAN_IFETCH: + case PMC_EV_P6_BUS_TRAN_INVAL: + case PMC_EV_P6_BUS_TRAN_PWR: + case PMC_EV_P6_BUS_TRANS_P: + case PMC_EV_P6_BUS_TRANS_IO: + case PMC_EV_P6_BUS_TRAN_DEF: + case PMC_EV_P6_BUS_TRAN_BURST: + case PMC_EV_P6_BUS_TRAN_ANY: + case PMC_EV_P6_BUS_TRAN_MEM: + + /* default => 'nta' */ + case PMC_EV_P6_EMON_KNI_PREF_DISPATCHED: + case PMC_EV_P6_EMON_KNI_PREF_MISS: + + /* default => 'packed and scalar' */ + case PMC_EV_P6_EMON_KNI_INST_RETIRED: + case PMC_EV_P6_EMON_KNI_COMP_INST_RET: + + /* default => 'mmx to fp transitions' */ + case PMC_EV_P6_FP_MMX_TRANS: + + /* default => 'SSE Packed Single' */ + case PMC_EV_P6_EMON_SSE_SSE2_INST_RETIRED: + case PMC_EV_P6_EMON_SSE_SSE2_COMP_INST_RETIRED: + + /* default => 'all fused micro-ops' */ + case PMC_EV_P6_EMON_FUSED_UOPS_RET: + + /* default => 'all transitions' */ + case PMC_EV_P6_EMON_EST_TRANS: + break; + + case PMC_EV_P6_MMX_UOPS_EXEC: + evmask = 0x0F; /* only value allowed */ + break; + + default: + + /* + * For all other events, set the default event mask + * to a logical OR of all the allowed event mask bits. + */ + + if (evmask == 0 && pmask) { + for (pm = pmask; pm->pm_name; pm++) + evmask |= pm->pm_value; + pmc_config->pm_caps |= PMC_CAP_QUALIFIER; + } + + break; + } + + if (pmc_config->pm_caps & PMC_CAP_QUALIFIER) + pmc_config->pm_md.pm_ppro.pm_ppro_config |= + P6_EVSEL_TO_UMASK(evmask); + + return 0; +} + +#endif + +/* + * API entry points + */ + + +int +pmc_allocate(const char *ctrspec, enum pmc_mode mode, + uint32_t flags, int cpu, pmc_id_t *pmcid) +{ + int retval; + enum pmc_event pe; + char *r, *spec_copy; + const char *ctrname; + const struct pmc_event_alias *p; + struct pmc_op_pmcallocate pmc_config; + + spec_copy = NULL; + retval = -1; + + if (mode != PMC_MODE_SS && mode != PMC_MODE_TS && + mode != PMC_MODE_SC && mode != PMC_MODE_TC) { + errno = EINVAL; + goto out; + } + + /* replace an event alias with the canonical event specifier */ + if (pmc_mdep_event_aliases) + for (p = pmc_mdep_event_aliases; p->pm_alias; p++) + if (!strcmp(ctrspec, p->pm_alias)) { + spec_copy = strdup(p->pm_spec); + break; + } + + if (spec_copy == NULL) + spec_copy = strdup(ctrspec); + + r = spec_copy; + ctrname = strsep(&r, ","); + + /* look for the given counter name */ + + for (pe = PMC_EVENT_FIRST; pe < (PMC_EVENT_LAST+1); pe++) + if (!strcmp(ctrname, pmc_event_table[pe].pm_ev_name)) + break; + + if (pe > PMC_EVENT_LAST) { + errno = EINVAL; + goto out; + } + + bzero(&pmc_config, sizeof(pmc_config)); + pmc_config.pm_ev = pmc_event_table[pe].pm_ev_code; + pmc_config.pm_class = pmc_event_table[pe].pm_ev_class; + pmc_config.pm_cpu = cpu; + pmc_config.pm_mode = mode; + pmc_config.pm_flags = flags; + + if (PMC_IS_SAMPLING_MODE(mode)) + pmc_config.pm_caps |= PMC_CAP_INTERRUPT; + + if (pmc_mdep_allocate_pmc(pe, r, &pmc_config) < 0) { + errno = EINVAL; + goto out; + } + + if (PMC_CALL(PMCALLOCATE, &pmc_config) < 0) + goto out; + + *pmcid = pmc_config.pm_pmcid; + + retval = 0; + + out: + if (spec_copy) + free(spec_copy); + + return retval; +} + +int +pmc_attach(pmc_id_t pmc, pid_t pid) +{ + struct pmc_op_pmcattach pmc_attach_args; + + pmc_attach_args.pm_pmc = pmc; + pmc_attach_args.pm_pid = pid; + + return PMC_CALL(PMCATTACH, &pmc_attach_args); +} + +int +pmc_capabilities(pmc_id_t pmcid, uint32_t *caps) +{ + unsigned int i; + enum pmc_class cl; + + cl = PMC_ID_TO_CLASS(pmcid); + for (i = 0; i < cpu_info.pm_nclass; i++) + if (cpu_info.pm_classes[i].pm_class == cl) { + *caps = cpu_info.pm_classes[i].pm_caps; + return 0; + } + return EINVAL; +} + +int +pmc_configure_logfile(int fd) +{ + struct pmc_op_configurelog cla; + + cla.pm_logfd = fd; + if (PMC_CALL(CONFIGURELOG, &cla) < 0) + return -1; + return 0; +} + +int +pmc_cpuinfo(const struct pmc_cpuinfo **pci) +{ + if (pmc_syscall == -1) { + errno = ENXIO; + return -1; + } + + /* kernel<->library, library<->userland interfaces are identical */ + *pci = (struct pmc_cpuinfo *) &cpu_info; + return 0; +} + +int +pmc_detach(pmc_id_t pmc, pid_t pid) +{ + struct pmc_op_pmcattach pmc_detach_args; + + pmc_detach_args.pm_pmc = pmc; + pmc_detach_args.pm_pid = pid; + + return PMC_CALL(PMCDETACH, &pmc_detach_args); +} + +int +pmc_disable(int cpu, int pmc) +{ + struct pmc_op_pmcadmin ssa; + + ssa.pm_cpu = cpu; + ssa.pm_pmc = pmc; + ssa.pm_state = PMC_STATE_DISABLED; + return PMC_CALL(PMCADMIN, &ssa); +} + +int +pmc_enable(int cpu, int pmc) +{ + struct pmc_op_pmcadmin ssa; + + ssa.pm_cpu = cpu; + ssa.pm_pmc = pmc; + ssa.pm_state = PMC_STATE_FREE; + return PMC_CALL(PMCADMIN, &ssa); +} + +/* + * Return a list of events known to a given PMC class. 'cl' is the + * PMC class identifier, 'eventnames' is the returned list of 'const + * char *' pointers pointing to the names of the events. 'nevents' is + * the number of event name pointers returned. + * + * The space for 'eventnames' is allocated using malloc(3). The caller + * is responsible for freeing this space when done. + */ + +int +pmc_event_names_of_class(enum pmc_class cl, const char ***eventnames, + int *nevents) +{ + int count; + const char **names; + const struct pmc_event_descr *ev; + + switch (cl) + { + case PMC_CLASS_TSC: + ev = &pmc_event_table[PMC_EV_TSC_TSC]; + count = 1; + break; + case PMC_CLASS_K7: + ev = &pmc_event_table[PMC_EV_K7_FIRST]; + count = PMC_EV_K7_LAST - PMC_EV_K7_FIRST + 1; + break; + case PMC_CLASS_K8: + ev = &pmc_event_table[PMC_EV_K8_FIRST]; + count = PMC_EV_K8_LAST - PMC_EV_K8_FIRST + 1; + break; + case PMC_CLASS_P5: + ev = &pmc_event_table[PMC_EV_P5_FIRST]; + count = PMC_EV_P5_LAST - PMC_EV_P5_FIRST + 1; + break; + case PMC_CLASS_P6: + ev = &pmc_event_table[PMC_EV_P6_FIRST]; + count = PMC_EV_P6_LAST - PMC_EV_P6_FIRST + 1; + break; + case PMC_CLASS_P4: + ev = &pmc_event_table[PMC_EV_P4_FIRST]; + count = PMC_EV_P4_LAST - PMC_EV_P4_FIRST + 1; break; default: - if (evmask == 0 && pmask != NULL) { - for (pm = pmask; pm->pm_name; pm++) - evmask |= pm->pm_value; - pmc_config->pm_caps |= PMC_CAP_QUALIFIER; - } + errno = EINVAL; + return -1; } - if (pmc_config->pm_caps & PMC_CAP_QUALIFIER) - pmc_config->pm_amd_config = K8_PMC_TO_UNITMASK(evmask); + if ((names = malloc(count * sizeof(const char *))) == NULL) + return -1; + *eventnames = names; + *nevents = count; + + for (;count--; ev++, names++) + *names = ev->pm_ev_name; return 0; } -#endif -/* - * API entry points - */ +int +pmc_flush_logfile(void) +{ + return PMC_CALL(FLUSHLOG,0); +} + +int +pmc_get_driver_stats(struct pmc_driverstats *ds) +{ + struct pmc_op_getdriverstats gms; + + if (PMC_CALL(GETDRIVERSTATS, &gms) < 0) + return -1; + + /* copy out fields in the current userland<->library interface */ + ds->pm_intr_ignored = gms.pm_intr_ignored; + ds->pm_intr_processed = gms.pm_intr_processed; + ds->pm_intr_bufferfull = gms.pm_intr_bufferfull; + ds->pm_syscalls = gms.pm_syscalls; + ds->pm_syscall_errors = gms.pm_syscall_errors; + ds->pm_buffer_requests = gms.pm_buffer_requests; + ds->pm_buffer_requests_failed = gms.pm_buffer_requests_failed; + ds->pm_log_sweeps = gms.pm_log_sweeps; + + return 0; +} + +int +pmc_get_msr(pmc_id_t pmc, uint32_t *msr) +{ + struct pmc_op_getmsr gm; + + gm.pm_pmcid = pmc; + if (PMC_CALL(PMCGETMSR, &gm) < 0) + return -1; + *msr = gm.pm_msr; + return 0; +} int pmc_init(void) @@ -1633,12 +1909,13 @@ pmc_init(void) pmc_syscall = pmc_modstat.data.intval; - /* check ABI version against compiled-in version */ + /* check the kernel module's ABI against our compiled-in version */ + abi_version = PMC_VERSION; if (PMC_CALL(GETMODULEVERSION, &abi_version) < 0) return (pmc_syscall = -1); - /* ignore patch numbers for the comparision */ - if ((abi_version & 0xFFFF0000) != (PMC_VERSION & 0xFFFF0000)) { + /* ignore patch & minor numbers for the comparision */ + if ((abi_version & 0xFF000000) != (PMC_VERSION & 0xFF000000)) { errno = EPROGMISMATCH; return (pmc_syscall = -1); } @@ -1688,207 +1965,90 @@ pmc_init(void) return 0; } -int -pmc_allocate(const char *ctrspec, enum pmc_mode mode, - uint32_t flags, int cpu, pmc_id_t *pmcid) +const char * +pmc_name_of_capability(enum pmc_caps cap) { - int retval; - enum pmc_event pe; - char *r, *spec_copy; - const char *ctrname; - const struct pmc_event_alias *p; - struct pmc_op_pmcallocate pmc_config; - - spec_copy = NULL; - retval = -1; - - if (mode != PMC_MODE_SS && mode != PMC_MODE_TS && - mode != PMC_MODE_SC && mode != PMC_MODE_TC) { - errno = EINVAL; - goto out; - } - - /* replace an event alias with the canonical event specifier */ - if (pmc_mdep_event_aliases) - for (p = pmc_mdep_event_aliases; p->pm_alias; p++) - if (!strcmp(ctrspec, p->pm_alias)) { - spec_copy = strdup(p->pm_spec); - break; - } - - if (spec_copy == NULL) - spec_copy = strdup(ctrspec); - - r = spec_copy; - ctrname = strsep(&r, ","); - - /* look for the given counter name */ - - for (pe = PMC_EVENT_FIRST; pe < (PMC_EVENT_LAST+1); pe++) - if (!strcmp(ctrname, pmc_event_table[pe].pm_ev_name)) - break; - - if (pe > PMC_EVENT_LAST) { - errno = EINVAL; - goto out; - } - - bzero(&pmc_config, sizeof(pmc_config)); - pmc_config.pm_ev = pmc_event_table[pe].pm_ev_code; - pmc_config.pm_class = pmc_event_table[pe].pm_ev_class; - pmc_config.pm_cpu = cpu; - pmc_config.pm_mode = mode; - pmc_config.pm_flags = flags; + int i; - if (PMC_IS_SAMPLING_MODE(mode)) - pmc_config.pm_caps |= PMC_CAP_INTERRUPT; + /* + * 'cap' should have a single bit set and should be in + * range. + */ - if (pmc_mdep_allocate_pmc(pe, r, &pmc_config) < 0) { + if ((cap & (cap - 1)) || cap < PMC_CAP_FIRST || + cap > PMC_CAP_LAST) { errno = EINVAL; - goto out; + return NULL; } - if (PMC_CALL(PMCALLOCATE, &pmc_config) < 0) - goto out; - - *pmcid = pmc_config.pm_pmcid; - - retval = 0; - - out: - if (spec_copy) - free(spec_copy); - - return retval; -} - -int -pmc_attach(pmc_id_t pmc, pid_t pid) -{ - struct pmc_op_pmcattach pmc_attach_args; - - pmc_attach_args.pm_pmc = pmc; - pmc_attach_args.pm_pid = pid; - - return PMC_CALL(PMCATTACH, &pmc_attach_args); -} - -int -pmc_detach(pmc_id_t pmc, pid_t pid) -{ - struct pmc_op_pmcattach pmc_detach_args; - - pmc_detach_args.pm_pmc = pmc; - pmc_detach_args.pm_pid = pid; - - return PMC_CALL(PMCDETACH, &pmc_detach_args); -} - -int -pmc_release(pmc_id_t pmc) -{ - struct pmc_op_simple pmc_release_args; - - pmc_release_args.pm_pmcid = pmc; - - return PMC_CALL(PMCRELEASE, &pmc_release_args); -} - -int -pmc_start(pmc_id_t pmc) -{ - struct pmc_op_simple pmc_start_args; - - pmc_start_args.pm_pmcid = pmc; - return PMC_CALL(PMCSTART, &pmc_start_args); -} - -int -pmc_stop(pmc_id_t pmc) -{ - struct pmc_op_simple pmc_stop_args; + i = ffs(cap); - pmc_stop_args.pm_pmcid = pmc; - return PMC_CALL(PMCSTOP, &pmc_stop_args); + return pmc_capability_names[i - 1]; } -int -pmc_read(pmc_id_t pmc, pmc_value_t *value) +const char * +pmc_name_of_class(enum pmc_class pc) { - struct pmc_op_pmcrw pmc_read_op; - - pmc_read_op.pm_pmcid = pmc; - pmc_read_op.pm_flags = PMC_F_OLDVALUE; - pmc_read_op.pm_value = -1; - - if (PMC_CALL(PMCRW, &pmc_read_op) < 0) - return -1; - - *value = pmc_read_op.pm_value; + if ((int) pc >= PMC_CLASS_FIRST && + pc <= PMC_CLASS_LAST) + return pmc_class_names[pc]; - return 0; + errno = EINVAL; + return NULL; } -int -pmc_write(pmc_id_t pmc, pmc_value_t value) +const char * +pmc_name_of_cputype(enum pmc_cputype cp) { - struct pmc_op_pmcrw pmc_write_op; - - pmc_write_op.pm_pmcid = pmc; - pmc_write_op.pm_flags = PMC_F_NEWVALUE; - pmc_write_op.pm_value = value; - - return PMC_CALL(PMCRW, &pmc_write_op); + if ((int) cp >= PMC_CPU_FIRST && + cp <= PMC_CPU_LAST) + return pmc_cputype_names[cp]; + errno = EINVAL; + return NULL; } -int -pmc_rw(pmc_id_t pmc, pmc_value_t newvalue, pmc_value_t *oldvaluep) +const char * +pmc_name_of_disposition(enum pmc_disp pd) { - struct pmc_op_pmcrw pmc_rw_op; - - pmc_rw_op.pm_pmcid = pmc; - pmc_rw_op.pm_flags = PMC_F_NEWVALUE | PMC_F_OLDVALUE; - pmc_rw_op.pm_value = newvalue; - - if (PMC_CALL(PMCRW, &pmc_rw_op) < 0) - return -1; - - *oldvaluep = pmc_rw_op.pm_value; + if ((int) pd >= PMC_DISP_FIRST && + pd <= PMC_DISP_LAST) + return pmc_disposition_names[pd]; - return 0; + errno = EINVAL; + return NULL; } -int -pmc_set(pmc_id_t pmc, pmc_value_t value) +const char * +pmc_name_of_event(enum pmc_event pe) { - struct pmc_op_pmcsetcount sc; - - sc.pm_pmcid = pmc; - sc.pm_count = value; - - if (PMC_CALL(PMCSETCOUNT, &sc) < 0) - return -1; - - return 0; + if ((int) pe >= PMC_EVENT_FIRST && + pe <= PMC_EVENT_LAST) + return pmc_event_table[pe].pm_ev_name; + errno = EINVAL; + return NULL; } -int -pmc_configure_logfile(int fd) -{ - struct pmc_op_configurelog cla; - - cla.pm_logfd = fd; - if (PMC_CALL(CONFIGURELOG, &cla) < 0) - return -1; - - return 0; +const char * +pmc_name_of_mode(enum pmc_mode pm) +{ + if ((int) pm >= PMC_MODE_FIRST && + pm <= PMC_MODE_LAST) + return pmc_mode_names[pm]; + + errno = EINVAL; + return NULL; } -int -pmc_get_driver_stats(struct pmc_op_getdriverstats *gms) +const char * +pmc_name_of_state(enum pmc_state ps) { - return PMC_CALL(GETDRIVERSTATS, gms); + if ((int) ps >= PMC_STATE_FIRST && + ps <= PMC_STATE_LAST) + return pmc_state_names[ps]; + + errno = EINVAL; + return NULL; } int @@ -1919,32 +2079,9 @@ pmc_npmc(int cpu) } int -pmc_enable(int cpu, int pmc) -{ - struct pmc_op_pmcadmin ssa; - - ssa.pm_cpu = cpu; - ssa.pm_pmc = pmc; - ssa.pm_state = PMC_STATE_FREE; - return PMC_CALL(PMCADMIN, &ssa); -} - -int -pmc_disable(int cpu, int pmc) -{ - struct pmc_op_pmcadmin ssa; - - ssa.pm_cpu = cpu; - ssa.pm_pmc = pmc; - ssa.pm_state = PMC_STATE_DISABLED; - return PMC_CALL(PMCADMIN, &ssa); -} - - -int -pmc_pmcinfo(int cpu, struct pmc_op_getpmcinfo **ppmci) +pmc_pmcinfo(int cpu, struct pmc_pmcinfo **ppmci) { - int nbytes, npmc, saved_errno; + int nbytes, npmc; struct pmc_op_getpmcinfo *pmci; if ((npmc = pmc_npmc(cpu)) < 0) @@ -1959,220 +2096,125 @@ pmc_pmcinfo(int cpu, struct pmc_op_getpmcinfo **ppmci) pmci->pm_cpu = cpu; if (PMC_CALL(GETPMCINFO, pmci) < 0) { - saved_errno = errno; free(pmci); - errno = saved_errno; return -1; } - *ppmci = pmci; + /* kernel<->library, library<->userland interfaces are identical */ + *ppmci = (struct pmc_pmcinfo *) pmci; + return 0; } int -pmc_cpuinfo(const struct pmc_op_getcpuinfo **pci) +pmc_read(pmc_id_t pmc, pmc_value_t *value) { - if (pmc_syscall == -1) { - errno = ENXIO; + struct pmc_op_pmcrw pmc_read_op; + + pmc_read_op.pm_pmcid = pmc; + pmc_read_op.pm_flags = PMC_F_OLDVALUE; + pmc_read_op.pm_value = -1; + + if (PMC_CALL(PMCRW, &pmc_read_op) < 0) return -1; - } - *pci = &cpu_info; + *value = pmc_read_op.pm_value; + return 0; } int -pmc_width(pmc_id_t pmcid, uint32_t *width) +pmc_release(pmc_id_t pmc) { - unsigned int i; - enum pmc_class cl; + struct pmc_op_simple pmc_release_args; - cl = PMC_ID_TO_CLASS(pmcid); - for (i = 0; i < cpu_info.pm_nclass; i++) - if (cpu_info.pm_classes[i].pm_class == cl) { - *width = cpu_info.pm_classes[i].pm_width; - return 0; - } - return EINVAL; + pmc_release_args.pm_pmcid = pmc; + + return PMC_CALL(PMCRELEASE, &pmc_release_args); } int -pmc_capabilities(pmc_id_t pmcid, uint32_t *caps) +pmc_rw(pmc_id_t pmc, pmc_value_t newvalue, pmc_value_t *oldvaluep) { - unsigned int i; - enum pmc_class cl; + struct pmc_op_pmcrw pmc_rw_op; - cl = PMC_ID_TO_CLASS(pmcid); - for (i = 0; i < cpu_info.pm_nclass; i++) - if (cpu_info.pm_classes[i].pm_class == cl) { - *caps = cpu_info.pm_classes[i].pm_caps; - return 0; - } - return EINVAL; -} + pmc_rw_op.pm_pmcid = pmc; + pmc_rw_op.pm_flags = PMC_F_NEWVALUE | PMC_F_OLDVALUE; + pmc_rw_op.pm_value = newvalue; -const char * -pmc_name_of_cputype(enum pmc_cputype cp) -{ - if ((int) cp >= PMC_CPU_FIRST && - cp <= PMC_CPU_LAST) - return pmc_cputype_names[cp]; - errno = EINVAL; - return NULL; -} + if (PMC_CALL(PMCRW, &pmc_rw_op) < 0) + return -1; -const char * -pmc_name_of_class(enum pmc_class pc) -{ - if ((int) pc >= PMC_CLASS_FIRST && - pc <= PMC_CLASS_LAST) - return pmc_class_names[pc]; + *oldvaluep = pmc_rw_op.pm_value; - errno = EINVAL; - return NULL; + return 0; } -const char * -pmc_name_of_mode(enum pmc_mode pm) +int +pmc_set(pmc_id_t pmc, pmc_value_t value) { - if ((int) pm >= PMC_MODE_FIRST && - pm <= PMC_MODE_LAST) - return pmc_mode_names[pm]; + struct pmc_op_pmcsetcount sc; - errno = EINVAL; - return NULL; -} + sc.pm_pmcid = pmc; + sc.pm_count = value; -const char * -pmc_name_of_event(enum pmc_event pe) -{ - if ((int) pe >= PMC_EVENT_FIRST && - pe <= PMC_EVENT_LAST) - return pmc_event_table[pe].pm_ev_name; + if (PMC_CALL(PMCSETCOUNT, &sc) < 0) + return -1; + + return 0; - errno = EINVAL; - return NULL; } -const char * -pmc_name_of_state(enum pmc_state ps) +int +pmc_start(pmc_id_t pmc) { - if ((int) ps >= PMC_STATE_FIRST && - ps <= PMC_STATE_LAST) - return pmc_state_names[ps]; + struct pmc_op_simple pmc_start_args; - errno = EINVAL; - return NULL; + pmc_start_args.pm_pmcid = pmc; + return PMC_CALL(PMCSTART, &pmc_start_args); } -const char * -pmc_name_of_disposition(enum pmc_disp pd) +int +pmc_stop(pmc_id_t pmc) { - if ((int) pd >= PMC_DISP_FIRST && - pd <= PMC_DISP_LAST) - return pmc_disposition_names[pd]; + struct pmc_op_simple pmc_stop_args; - errno = EINVAL; - return NULL; + pmc_stop_args.pm_pmcid = pmc; + return PMC_CALL(PMCSTOP, &pmc_stop_args); } -const char * -pmc_name_of_capability(enum pmc_caps cap) +int +pmc_width(pmc_id_t pmcid, uint32_t *width) { - int i; - - /* - * 'cap' should have a single bit set and should be in - * range. - */ - - if ((cap & (cap - 1)) || cap < PMC_CAP_FIRST || - cap > PMC_CAP_LAST) { - errno = EINVAL; - return NULL; - } - - i = ffs(cap); + unsigned int i; + enum pmc_class cl; - return pmc_capability_names[i - 1]; + cl = PMC_ID_TO_CLASS(pmcid); + for (i = 0; i < cpu_info.pm_nclass; i++) + if (cpu_info.pm_classes[i].pm_class == cl) { + *width = cpu_info.pm_classes[i].pm_width; + return 0; + } + return EINVAL; } -/* - * Return a list of events known to a given PMC class. 'cl' is the - * PMC class identifier, 'eventnames' is the returned list of 'const - * char *' pointers pointing to the names of the events. 'nevents' is - * the number of event name pointers returned. - * - * The space for 'eventnames' is allocated using malloc(3). The caller - * is responsible for freeing this space when done. - */ - int -pmc_event_names_of_class(enum pmc_class cl, const char ***eventnames, - int *nevents) +pmc_write(pmc_id_t pmc, pmc_value_t value) { - int count; - const char **names; - const struct pmc_event_descr *ev; - - switch (cl) - { - case PMC_CLASS_TSC: - ev = &pmc_event_table[PMC_EV_TSC_TSC]; - count = 1; - break; - case PMC_CLASS_K7: - ev = &pmc_event_table[PMC_EV_K7_FIRST]; - count = PMC_EV_K7_LAST - PMC_EV_K7_FIRST + 1; - break; - case PMC_CLASS_K8: - ev = &pmc_event_table[PMC_EV_K8_FIRST]; - count = PMC_EV_K8_LAST - PMC_EV_K8_FIRST + 1; - break; - case PMC_CLASS_P5: - ev = &pmc_event_table[PMC_EV_P5_FIRST]; - count = PMC_EV_P5_LAST - PMC_EV_P5_FIRST + 1; - break; - case PMC_CLASS_P6: - ev = &pmc_event_table[PMC_EV_P6_FIRST]; - count = PMC_EV_P6_LAST - PMC_EV_P6_FIRST + 1; - break; - case PMC_CLASS_P4: - ev = &pmc_event_table[PMC_EV_P4_FIRST]; - count = PMC_EV_P4_LAST - PMC_EV_P4_FIRST + 1; - break; - default: - errno = EINVAL; - return -1; - } - - if ((names = malloc(count * sizeof(const char *))) == NULL) - return -1; + struct pmc_op_pmcrw pmc_write_op; - *eventnames = names; - *nevents = count; + pmc_write_op.pm_pmcid = pmc; + pmc_write_op.pm_flags = PMC_F_NEWVALUE; + pmc_write_op.pm_value = value; - for (;count--; ev++, names++) - *names = ev->pm_ev_name; - return 0; + return PMC_CALL(PMCRW, &pmc_write_op); } -/* - * Architecture specific APIs - */ - -#if defined(__i386__) || defined(__amd64__) - int -pmc_x86_get_msr(pmc_id_t pmc, uint32_t *msr) +pmc_writelog(uint32_t userdata) { - struct pmc_op_x86_getmsr gm; + struct pmc_op_writelog wl; - gm.pm_pmcid = pmc; - if (PMC_CALL(PMCX86GETMSR, &gm) < 0) - return -1; - *msr = gm.pm_msr; - return 0; + wl.pm_userdata = userdata; + return PMC_CALL(WRITELOG, &wl); } - -#endif diff --git a/lib/libpmc/pmc.3 b/lib/libpmc/pmc.3 index 0612ce7..7a771d4 100644 --- a/lib/libpmc/pmc.3 +++ b/lib/libpmc/pmc.3 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2003 Joseph Koshy. All rights reserved. +.\" Copyright (c) 2003-2005 Joseph Koshy. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -36,7 +36,9 @@ .Nm pmc_disable , .Nm pmc_enable , .Nm pmc_event_names_of_class , +.Nm pmc_flush_logfile , .Nm pmc_get_driver_stats , +.Nm pmc_get_msr , .Nm pmc_init , .Nm pmc_name_of_capability , .Nm pmc_name_of_class , @@ -53,9 +55,9 @@ .Nm pmc_set , .Nm pmc_start , .Nm pmc_stop , -.Nm pmc_write , .Nm pmc_width , -.Nm pmc_x86_get_msr +.Nm pmc_write , +.Nm pmc_writelog .Nd programming API for using hardware performance monitoring counters .Sh LIBRARY .Lb libpmc @@ -79,7 +81,7 @@ .Ft int .Fn pmc_configure_logfile "int fd" .Ft int -.Fn pmc_cpuinfo "const struct pmc_op_getcpuinfo **cpu_info" +.Fn pmc_cpuinfo "const struct pmc_cpuinfo **cpu_info" .Ft int .Fo pmc_detach .Fa "pmc_id_t pmcid" @@ -96,7 +98,11 @@ .Fa "int *nevents" .Fc .Ft int -.Fn pmc_get_driver_stats "struct pmc_op_getdriverstats *gms" +.Fn pmc_flush_logfile "void" +.Ft int +.Fn pmc_get_driver_stats "struct pmc_driverstats *gms" +.Ft int +.Fn pmc_get_msr "pmc_id_t pmc" "uint32_t *msr" .Ft int .Fn pmc_init "void" .Ft "const char *" @@ -118,7 +124,7 @@ .Ft int .Fn pmc_npmc "uint32_t cpu" .Ft int -.Fn pmc_pmcinfo "uint32_t cpu" "struct pmc_op_getpmcinfo **pmc_info" +.Fn pmc_pmcinfo "uint32_t cpu" "struct pmc_pmcinfo **pmc_info" .Ft int .Fn pmc_read "pmc_id_t pmc" "pmc_value_t *value" .Ft int @@ -134,9 +140,9 @@ .Ft int .Fn pmc_write "pmc_id_t pmc" "pmc_value_t value" .Ft int -.Fn pmc_width "pmc_id_t pmc" "uint32_t *width" +.Fn pmc_writelog "uint32_t userdata" .Ft int -.Fn pmc_x86_get_msr "int pmc" "uint32_t *msr" +.Fn pmc_width "pmc_id_t pmc" "uint32_t *width" .Sh DESCRIPTION These functions implement a high-level library for using the system's hardware performance counters. @@ -276,9 +282,24 @@ The .Fn pmc_configure_logfile function causes the .Xr hwpmc 4 -driver to log system wide performance data to file corresponding +driver to log performance data to file corresponding to the process' file handle .Fa fd . +If argument +.Fa fd +is -1, then any previously configured logging is reset +and all data queued to be written are discarded. +.Pp +The +.Fn pmc_flush_logfile +function will send all data queued inside the +.Xr hwpmc 4 +driver to the configured log file before returning. +The +.Fn pmc_writelog +function will append a log entry containing the argument +.Fa userdata +to the log file. .Pp .Fn pmc_set configures an sampling PMC @@ -307,8 +328,19 @@ module is unloaded using processes that have PMCs allocated to them will be sent a SIGBUS signal. .It SIGIO -Attempting to read a PMC that is not currently attached to a running -process will cause a SIGIO signal to be sent to the reader. +The +.Xr hwpmc 4 +driver will send a PMC owning process a SIGIO signal if: +.Bl -bullet +.It +If any process-mode PMC allocated by it loses all its +target processes. +.It +If the driver encounters an error when writing log data to a +configured log file. +This error may be retrieved by a subsequent call to +.Fn pmc_flush_logfile . +.El .El .Ss CONVENIENCE FUNCTIONS .Fn pmc_ncpu @@ -321,10 +353,18 @@ returns the number of PMCs supported on CPU sets argument .Fa cpu_info to point to a structure with information about the system's CPUs. +Function .Fn pmc_pmcinfo returns information about the current state of CPU .Fa cpu Ap s PMCs. +This function sets argument +.Fa *pmc_info +to point to a memory area allocated with +.Xr calloc 3 . +The caller is expected to +.Fn free +the area when done. .Pp The functions .Fn pmc_name_of_capability , @@ -370,7 +410,7 @@ is the index of the PMC to be operated on. Only the super-user is allowed to enable and disable PMCs. .Ss X86 ARCHITECTURE SPECIFIC API The -.Fn pmc_x86_get_msr +.Fn pmc_get_msr function returns the processor model specific register number associated with .Fa pmc . @@ -3096,25 +3136,39 @@ was unrecognized for this cpu type. .Pp Calls to .Fn pmc_attach , +.Fn pmc_configure_logfile , .Fn pmc_detach , +.Fn pmc_disable , +.Fn pmc_enable , +.Fn pmc_get_driver_stats , +.Fn pmc_get_msr , +.Fn pmc_read , .Fn pmc_release , +.Fn pmc_rw , +.Fn pmc_set , .Fn pmc_start , .Fn pmc_stop , -.Fn pmc_read , .Fn pmc_write , -.Fn pmc_rw , -.Fn pmc_set , -.Fn pmc_configure_logfile , -.Fn pmc_get_driver_stats , -.Fn pmc_enable , -.Fn pmc_disable , and -.Fn pmc_x86_get_msr +.Fn pmc_writelog may fail with the errors described in .Xr hwpmc 4 . +.Pp +If a log file was configured using +.Fn pmc_configure_logfile +and the +.Xr hwpmc 4 +driver encountered an error while logging data to it, then +logging will be stopped and a subsequent call to +.Fn pmc_flush_logfile +will fail with the error code seen by the +.Xr hwpmc 4 +driver. .Sh SEE ALSO .Xr modfind 2 , .Xr modstat 2 , +.Xr calloc 3 , +.Xr pmclog 3 , .Xr hwpmc 4 , .Xr pmccontrol 8 , .Xr pmcreport 8 , @@ -3126,12 +3180,6 @@ The information returned by and possibly .Fn pmc_npmc should really be available all the time, through a better designed -interface. -.Pp -The API for -.Fn pmc_cpuinfo -and -.Fn pmc_pmcinfo -expose too much of the underlying +interface and not just when .Xr hwpmc 4 -driver's internals to userland. +is present in the kernel. diff --git a/lib/libpmc/pmc.h b/lib/libpmc/pmc.h index 7ee257b..ee3f772 100644 --- a/lib/libpmc/pmc.h +++ b/lib/libpmc/pmc.h @@ -32,6 +32,39 @@ #include /* + * Driver statistics. + */ +struct pmc_driverstats { + int pm_intr_ignored; /* #interrupts ignored */ + int pm_intr_processed; /* #interrupts processed */ + int pm_intr_bufferfull; /* #interrupts with ENOSPC */ + int pm_syscalls; /* #syscalls */ + int pm_syscall_errors; /* #syscalls with errors */ + int pm_buffer_requests; /* #buffer requests */ + int pm_buffer_requests_failed; /* #failed buffer requests */ + int pm_log_sweeps; /* #sample buffer processing passes */ +}; + +/* + * CPU information. + */ +struct pmc_cpuinfo { + enum pmc_cputype pm_cputype; /* the kind of CPU */ + uint32_t pm_ncpu; /* number of CPUs */ + uint32_t pm_npmc; /* #PMCs per CPU */ + uint32_t pm_nclass; /* #classes of PMCs */ + struct pmc_classinfo pm_classes[PMC_CLASS_MAX]; +}; + +/* + * Current PMC state. + */ +struct pmc_pmcinfo { + int32_t pm_cpu; /* CPU number */ + struct pmc_info pm_pmcs[]; /* NPMC structs */ +}; + +/* * Prototypes */ @@ -40,10 +73,12 @@ int pmc_allocate(const char *_ctrspec, enum pmc_mode _mode, uint32_t _flags, int pmc_attach(pmc_id_t _pmcid, pid_t _pid); int pmc_capabilities(pmc_id_t _pmc, uint32_t *_caps); int pmc_configure_logfile(int _fd); +int pmc_flush_logfile(void); int pmc_detach(pmc_id_t _pmcid, pid_t _pid); int pmc_disable(int _cpu, int _pmc); int pmc_enable(int _cpu, int _pmc); -int pmc_get_driver_stats(struct pmc_op_getdriverstats *_gms); +int pmc_get_driver_stats(struct pmc_driverstats *_gms); +int pmc_get_msr(pmc_id_t _pmc, uint32_t *_msr); int pmc_init(void); int pmc_read(pmc_id_t _pmc, pmc_value_t *_value); int pmc_release(pmc_id_t _pmc); @@ -53,11 +88,12 @@ int pmc_start(pmc_id_t _pmc); int pmc_stop(pmc_id_t _pmc); int pmc_width(pmc_id_t _pmc, uint32_t *_width); int pmc_write(pmc_id_t _pmc, pmc_value_t _value); +int pmc_writelog(uint32_t _udata); int pmc_ncpu(void); int pmc_npmc(int _cpu); -int pmc_cpuinfo(const struct pmc_op_getcpuinfo **_cpu_info); -int pmc_pmcinfo(int _cpu, struct pmc_op_getpmcinfo **_pmc_info); +int pmc_cpuinfo(const struct pmc_cpuinfo **_cpu_info); +int pmc_pmcinfo(int _cpu, struct pmc_pmcinfo **_pmc_info); const char *pmc_name_of_capability(uint32_t _c); const char *pmc_name_of_class(enum pmc_class _pc); @@ -70,12 +106,4 @@ const char *pmc_name_of_state(enum pmc_state _ps); int pmc_event_names_of_class(enum pmc_class _cl, const char ***_eventnames, int *_nevents); -/* - * Architecture specific extensions - */ - -#if __i386__ || __amd64__ -int pmc_x86_get_msr(pmc_id_t _pmc, uint32_t *_msr); -#endif - #endif diff --git a/lib/libpmc/pmclog.3 b/lib/libpmc/pmclog.3 new file mode 100644 index 0000000..1487e90 --- /dev/null +++ b/lib/libpmc/pmclog.3 @@ -0,0 +1,276 @@ +.\" Copyright (c) 2005 Joseph Koshy. All rights reserved. +.\" +.\" 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 Joseph Koshy ``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 Joseph Koshy 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$ +.\" +.Dd Jun 1, 2005 +.Os +.Dt PMCLOG 3 +.Sh NAME +.Nm pmclog_open , +.Nm pmclog_close , +.Nm pmclog_read , +.Nm pmclog_feed +.Nd parse event log data generated by +.Xr hwpmc 4 +.Sh LIBRARY +.Lb libpmc +.Sh SYNOPSIS +.In pmclog.h +.Ft "void *" +.Fn pmclog_open "int fd" +.Ft void +.Fn pmclog_close "void *cookie" +.Ft int +.Fn pmclog_read "void *cookie" "struct pmclog_ev *ev" +.Ft int +.Fn pmclog_feed "void *cookie" "char *data" "int len" +.Sh DESCRIPTION +These functions provide a way for application programs to extract +events from an event stream generated by +.Xr hwpmc 4 . +.Pp +A new event log parser is allocated using +.Fn pmclog_open . +Argument +.Fa fd +may be a file descriptor opened for reading if the event stream is +present in a file, or the constant +.Dv PMCLOG_FD_NONE +for an event stream present in memory. +This function returns a cookie that is passed into the other functions +in this API set. +.Pp +Function +.Fn pmclog_read +returns the next available event in the event stream associated with +argument +.Fa cookie . +Argument +.Fa ev +points to an event descriptor that which will contain the result of a +successfully parsed event. +.Pp +An event descriptor returned by +.Fn pmclog_read +has the following structure: +.Bd -literal +struct pmclog_ev { + enum pmclog_state pl_state; /* parser state after 'get_event()' */ + off_t pl_offset; /* byte offset in stream */ + size_t pl_count; /* count of records so far */ + struct timespec pl_ts; /* log entry timestamp */ + enum pmclog_type pl_type; /* log entry kind */ + union { /* log entry data */ + struct pmclog_ev_allocate pl_a; + struct pmclog_ev_proccsw pl_c; + struct pmclog_ev_dropnotify pl_d; + struct pmclog_ev_procexit pl_e; + struct pmclog_ev_initialize pl_i; + struct pmclog_ev_pcsample pl_s; + struct pmclog_ev_pmcattach pl_t; + struct pmclog_ev_userdata pl_u; + struct pmclog_ev_procexec pl_x; + } pl_u; +}; +.Ed +.Pp +The current state of the parser is recorded in +.Va pl_state . +This field can take on the following values: +.Bl -tag -width "PMCLOG_REQUIRE_DATA" -compact +.It Dv PMCLOG_EOF +.Pq For file based parsers only +An end-of-file condition was encountered on the configured file +descriptor. +.It Dv PMCLOG_ERROR +An error occurred during parsing. +.It Dv PMCLOG_OK +A complete event record was read into +.Fa "*ev" . +.It Dv PMCLOG_REQUIRE_DATA +There was insufficient data in the event stream to assemble a complete +event record. +For memory based parsers, more data can be fed to the +parser using function +.Fn pmclog_feed . +For file based parsers, function +.Fn pmclog_read +may be retried when data is available on the configured file +descriptor. +.El +.Pp +The rest of the event structure is valid only if field +.Va pl_state +contains +.Dv PMCLOG_OK . +Field +.Va pl_offset +contains the offset of the current record in the byte stream. +Field +.Va pl_count +contains the serial number of this event. +Field +.Va pl_ts +contains a timestamp with the system time when the event occurred. +Field +.Va pl_type +denotes the kind of the event returned in argument +.Fa *ev +and is one of the following: +.Bl -tag -width XXXXXXXXXXXXXXXXXXXXXXX -compact +.It Dv PMCLOG_TYPE_DROPNOTIFY +a marker indicating that +.Xr hwpmc 4 +had to drop data due to a resource constraint. +.It Dv PMCLOG_TYPE_INITIALIZE +an initialization record. +This is usually the first record in a log file. +.It Dv PMCLOG_TYPE_PCSAMPLE +A record containing an instruction pointer sample. +.It Dv PMCLOG_TYPE_PMCALLOCATE +A record describing a PMC allocation operation. +.It Dv PMCLOG_TYPE_PMCATTACH +A record describing a PMC attach operation. +.It Dv PMCLOG_TYPE_PROCCSW +A record describing a PMC reading at the time of a process context switch. +.It Dv PMCLOG_TYPE_PROCEXIT +A record describing the accumulated PMC reading for a process at the +time of +.Xr _exit 2 . +.It Dv PMCLOG_TYPE_PROCEXEC +A record describing an +.Xr execve 2 +by a target process. +.It Dv PMCLOG_TYPE_USERDATA +A record containing user data. +.El +.Pp +Function +.Fn pmclog_feed +is used with parsers configured to parse memory based event streams. +It is intended to be called when function +.Fn pmclog_read +indicates the need for more data by a returning +.Dv PMCLOG_REQUIRE_DATA +in field +.Va pl_state +of its event structure argument. +Argument +.Fa data +points to the start of a memory buffer containing fresh event data. +Argument +.Fa len +indicates the number of bytes of data available. +The memory range +.Bq data , data+len +must remain valid till the next time +.Fn pmclog_read +returns an error. +It is an error to use +.Fn pmclog_feed +on a parser configured to parse file data. +.Pp +Function +.Fn pmclog_close +releases the internal state allocated by a prior call +to +.Fn pmclog_open . +.Sh RETURN VALUES +Function +.Fn pmclog_open +will return a non-NULL value if successful or NULL otherwise. +.Pp +Function +.Fn pmclog_read +will return 0 in case a complete event record was successfully read, +or will return -1 and will set the +.Va pl_state +field of the event record to the appropriate code in case of an error. +.Pp +Function +.Fn pmclog_feed +will return 0 on success or -1 in case of failure. +.Sh EXAMPLES +A template for using the log file parsing API is shown below in psuedocode: +.Bd -literal +void *parser; /* cookie */ +struct pmclog_ev ev; /* parsed event */ +int fd; /* file descriptor */ + +fd = open(filename, O_RDONLY); /* open log file */ +parser = pmclog_open(fd); /* initialize parser */ +if (parser == NULL) + --handle an out of memory error--; + +/* read and parse data */ +while (pmclog_read(parser, &ev) == 0) { + assert(ev.pl_state == PMCLOG_OK); + /* process the event */ + switch (ev.pl_type) { + case PMCLOG_TYPE_ALLOCATE: + --process a pmc allocation record-- + break; + case PMCLOG_TYPE_PROCCSW: + --process a thread context switch record-- + break; + case PMCLOG_TYPE_PCSAMPLE: + --process a PC sample-- + break; + --and so on-- + } +} + +/* examine parser state */ +switch (ev.pl_state) { +case PMCLOG_EOF: + --normal termination-- + break; +case PMCLOG_ERROR: + --look at errno here-- + break; +case PMCLOG_REQUIRE_DATA: + --arrange for more data to be available for parsing-- + break; +default: + assert(0); + /*NOTREACHED*/ +} + +pmclog_close(parser); /* cleanup */ +.Ed +.Sh ERRORS +A call to +.Fn pmclog_init_parser +may fail with any of the errors returned by +.Xr malloc 3 . +.Pp +A call to +.Fn pmclog_read +for a file based parser may fail with any of the errors returned by +.Xr read 2 . +.Sh SEE ALSO +.Xr read 2 , +.Xr malloc 3 , +.Xr pmc 3 , +.Xr hwpmc 4 diff --git a/lib/libpmc/pmclog.c b/lib/libpmc/pmclog.c new file mode 100644 index 0000000..8772c58 --- /dev/null +++ b/lib/libpmc/pmclog.c @@ -0,0 +1,532 @@ +/*- + * Copyright (c) 2005 Joseph Koshy + * All rights reserved. + * + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PMCLOG_BUFFER_SIZE 4096 + +/* + * API NOTES + * + * The pmclog(3) API is oriented towards parsing an event stream in + * "realtime", i.e., from an data source that may or may not preserve + * record boundaries -- for example when the data source is elsewhere + * on a network. The API allows data to be fed into the parser zero + * or more bytes at a time. + * + * The state for a log file parser is maintained in a 'struct + * pmclog_parse_state'. Parser invocations are done by calling + * 'pmclog_read()'; this function will inform the caller when a + * complete event is parsed. + * + * The parser first assembles a complete log file event in an internal + * work area (see "ps_saved" below). Once a complete log file event + * is read, the parser then parses it and converts it to an event + * descriptor usable by the client. We could possibly avoid this two + * step process by directly parsing the input log to set fields in the + * event record. However the parser's state machine would get + * insanely complicated, and this code is unlikely to be used in + * performance critical paths. + */ + +enum pmclog_parser_state { + PL_STATE_NEW_RECORD, /* in-between records */ + PL_STATE_EXPECTING_HEADER, /* header being read */ + PL_STATE_PARTIAL_RECORD, /* header present but not the record */ + PL_STATE_ERROR /* parsing error encountered */ +}; + +struct pmclog_parse_state { + enum pmclog_parser_state ps_state; + enum pmc_cputype ps_arch; /* log file architecture */ + uint32_t ps_version; /* hwpmc version */ + int ps_initialized; /* whether initialized */ + int ps_count; /* count of records processed */ + off_t ps_offset; /* stream byte offset */ + union pmclog_entry ps_saved; /* saved partial log entry */ + int ps_svcount; /* #bytes saved */ + int ps_fd; /* active fd or -1 */ + char *ps_buffer; /* scratch buffer if fd != -1 */ + char *ps_data; /* current parse pointer */ + size_t ps_len; /* length of buffered data */ +}; + +#define PMCLOG_HEADER_FROM_SAVED_STATE(PS) \ + (* ((uint32_t *) &(PS)->ps_saved)) + +#define PMCLOG_INITIALIZE_READER(LE,A) LE = (uint32_t *) &(A) +#define PMCLOG_READ32(LE,V) do { \ + (V) = *(LE)++; \ + } while (0) +#define PMCLOG_READ64(LE,V) do { \ + uint64_t _v; \ + _v = (uint64_t) *(LE)++; \ + _v |= ((uint64_t) *(LE)++) << 32; \ + (V) = _v; \ + } while (0) + +#define PMCLOG_READSTRING(LE,DST,LEN) strlcpy((DST), (char *) (LE), (LEN)) + +/* + * Assemble a log record from '*len' octets starting from address '*data'. + * Update 'data' and 'len' to reflect the number of bytes consumed. + * + * '*data' is potentially an unaligned address and '*len' octets may + * not be enough to complete a event record. + */ + +static enum pmclog_parser_state +pmclog_get_record(struct pmclog_parse_state *ps, char **data, ssize_t *len) +{ + int avail, copylen, recordsize, used; + uint32_t h; + const int HEADERSIZE = sizeof(uint32_t); + char *src, *dst; + + if ((avail = *len) <= 0) + return (ps->ps_state = PL_STATE_ERROR); + + src = *data; + h = used = 0; + + if (ps->ps_state == PL_STATE_NEW_RECORD) + ps->ps_svcount = 0; + + dst = (char *) &ps->ps_saved + ps->ps_svcount; + + switch (ps->ps_state) { + case PL_STATE_NEW_RECORD: + + /* + * Transitions: + * + * Case A: avail < headersize + * -> 'expecting header' + * + * Case B: avail >= headersize + * B.1: avail < recordsize + * -> 'partial record' + * B.2: avail >= recordsize + * -> 'new record' + */ + + copylen = avail < HEADERSIZE ? avail : HEADERSIZE; + bcopy(src, dst, copylen); + ps->ps_svcount = used = copylen; + + if (copylen < HEADERSIZE) { + ps->ps_state = PL_STATE_EXPECTING_HEADER; + goto done; + } + + src += copylen; + dst += copylen; + + h = PMCLOG_HEADER_FROM_SAVED_STATE(ps); + recordsize = PMCLOG_HEADER_TO_LENGTH(h); + + if (recordsize <= 0) + goto error; + + if (recordsize <= avail) { /* full record available */ + bcopy(src, dst, recordsize - copylen); + ps->ps_svcount = used = recordsize; + goto done; + } + + /* header + a partial record is available */ + bcopy(src, dst, avail - copylen); + ps->ps_svcount = used = avail; + ps->ps_state = PL_STATE_PARTIAL_RECORD; + + break; + + case PL_STATE_EXPECTING_HEADER: + + /* + * Transitions: + * + * Case C: avail+saved < headersize + * -> 'expecting header' + * + * Case D: avail+saved >= headersize + * D.1: avail+saved < recordsize + * -> 'partial record' + * D.2: avail+saved >= recordsize + * -> 'new record' + * (see PARTIAL_RECORD handling below) + */ + + if (avail + ps->ps_svcount < HEADERSIZE) { + bcopy(src, dst, avail); + ps->ps_svcount += avail; + used = avail; + break; + } + + used = copylen = HEADERSIZE - ps->ps_svcount; + bcopy(src, dst, copylen); + src += copylen; + dst += copylen; + avail -= copylen; + ps->ps_svcount += copylen; + + /*FALLTHROUGH*/ + + case PL_STATE_PARTIAL_RECORD: + + /* + * Transitions: + * + * Case E: avail+saved < recordsize + * -> 'partial record' + * + * Case F: avail+saved >= recordsize + * -> 'new record' + */ + + h = PMCLOG_HEADER_FROM_SAVED_STATE(ps); + recordsize = PMCLOG_HEADER_TO_LENGTH(h); + + if (recordsize <= 0) + goto error; + + if (avail + ps->ps_svcount < recordsize) { + copylen = avail; + ps->ps_state = PL_STATE_PARTIAL_RECORD; + } else { + copylen = recordsize - ps->ps_svcount; + ps->ps_state = PL_STATE_NEW_RECORD; + } + + bcopy(src, dst, copylen); + ps->ps_svcount += copylen; + used += copylen; + break; + + default: + goto error; + } + + done: + *data += used; + *len -= used; + return ps->ps_state; + + error: + ps->ps_state = PL_STATE_ERROR; + return ps->ps_state; +} + +/* + * Get an event from the stream pointed to by '*data'. '*len' + * indicates the number of bytes available to parse. Arguments + * '*data' and '*len' are updated to indicate the number of bytes + * consumed. + */ + +static int +pmclog_get_event(void *cookie, char **data, ssize_t *len, + struct pmclog_ev *ev) +{ + int evlen, pathlen; + uint32_t h, *le; + enum pmclog_parser_state e; + struct pmclog_parse_state *ps; + + ps = (struct pmclog_parse_state *) cookie; + + assert(ps->ps_state != PL_STATE_ERROR); + + if ((e = pmclog_get_record(ps,data,len)) == PL_STATE_ERROR) { + ev->pl_state = PMCLOG_ERROR; + return -1; + } + + if (e != PL_STATE_NEW_RECORD) { + ev->pl_state = PMCLOG_REQUIRE_DATA; + return -1; + } + + PMCLOG_INITIALIZE_READER(le, ps->ps_saved); + + PMCLOG_READ32(le,h); + + if (!PMCLOG_HEADER_CHECK_MAGIC(h)) { + ps->ps_state = PL_STATE_ERROR; + ev->pl_state = PMCLOG_ERROR; + return -1; + } + + /* copy out the time stamp */ + PMCLOG_READ32(le,ev->pl_ts.tv_sec); + PMCLOG_READ32(le,ev->pl_ts.tv_nsec); + + evlen = PMCLOG_HEADER_TO_LENGTH(h); + +#define PMCLOG_GET_PATHLEN(P,E,TYPE) do { \ + (P) = (E) - offsetof(struct TYPE, pl_pathname); \ + if ((P) > PATH_MAX || (P) < 0) \ + goto error; \ + } while (0) + + switch (ev->pl_type = PMCLOG_HEADER_TO_TYPE(h)) { + case PMCLOG_TYPE_CLOSELOG: + case PMCLOG_TYPE_DROPNOTIFY: + /* nothing to do */ + break; + case PMCLOG_TYPE_INITIALIZE: + PMCLOG_READ32(le,ev->pl_u.pl_i.pl_version); + PMCLOG_READ32(le,ev->pl_u.pl_i.pl_arch); + ps->ps_version = ev->pl_u.pl_i.pl_version; + ps->ps_arch = ev->pl_u.pl_i.pl_arch; + ps->ps_initialized = 1; + break; + case PMCLOG_TYPE_MAPPINGCHANGE: + PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_mappingchange); + PMCLOG_READ32(le,ev->pl_u.pl_m.pl_type); + PMCLOG_READADDR(le,ev->pl_u.pl_m.pl_start); + PMCLOG_READADDR(le,ev->pl_u.pl_m.pl_end); + PMCLOG_READ32(le,ev->pl_u.pl_m.pl_pid); + PMCLOG_READSTRING(le, ev->pl_u.pl_m.pl_pathname, pathlen); + break; + case PMCLOG_TYPE_PCSAMPLE: + PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pid); + PMCLOG_READADDR(le,ev->pl_u.pl_s.pl_pc); + PMCLOG_READ32(le,ev->pl_u.pl_s.pl_pmcid); + break; + case PMCLOG_TYPE_PMCALLOCATE: + 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); + if ((ev->pl_u.pl_a.pl_evname = + pmc_name_of_event(ev->pl_u.pl_a.pl_event)) == NULL) + goto error; + break; + case PMCLOG_TYPE_PMCATTACH: + PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_pmcattach); + PMCLOG_READ32(le,ev->pl_u.pl_t.pl_pmcid); + PMCLOG_READ32(le,ev->pl_u.pl_t.pl_pid); + PMCLOG_READSTRING(le,ev->pl_u.pl_t.pl_pathname,pathlen); + break; + case PMCLOG_TYPE_PMCDETACH: + PMCLOG_READ32(le,ev->pl_u.pl_d.pl_pmcid); + 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_pid); + break; + case PMCLOG_TYPE_PROCEXEC: + PMCLOG_GET_PATHLEN(pathlen,evlen,pmclog_procexec); + PMCLOG_READ32(le,ev->pl_u.pl_x.pl_pid); + 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_READ64(le,ev->pl_u.pl_e.pl_value); + PMCLOG_READ32(le,ev->pl_u.pl_e.pl_pid); + break; + case PMCLOG_TYPE_PROCFORK: + PMCLOG_READ32(le,ev->pl_u.pl_f.pl_oldpid); + PMCLOG_READ32(le,ev->pl_u.pl_f.pl_newpid); + break; + case PMCLOG_TYPE_SYSEXIT: + PMCLOG_READ32(le,ev->pl_u.pl_se.pl_pid); + break; + case PMCLOG_TYPE_USERDATA: + PMCLOG_READ32(le,ev->pl_u.pl_u.pl_userdata); + break; + default: /* unknown record type */ + ps->ps_state = PL_STATE_ERROR; + ev->pl_state = PMCLOG_ERROR; + return -1; + } + + ev->pl_offset = (ps->ps_offset += evlen); + ev->pl_count = (ps->ps_count += 1); + ev->pl_state = PMCLOG_OK; + return 0; + + error: + ev->pl_state = PMCLOG_ERROR; + ps->ps_state = PL_STATE_ERROR; + return -1; +} + +/* + * Extract and return the next event from the byte stream. + * + * Returns 0 and sets the event's state to PMCLOG_OK in case an event + * was successfully parsed. Otherwise this function returns -1 and + * sets the event's state to one of PMCLOG_REQUIRE_DATA (if more data + * is needed) or PMCLOG_EOF (if an EOF was seen) or PMCLOG_ERROR if + * a parse error was encountered. + */ + +int +pmclog_read(void *cookie, struct pmclog_ev *ev) +{ + ssize_t nread; + struct pmclog_parse_state *ps; + + ps = (struct pmclog_parse_state *) cookie; + + if (ps->ps_state == PL_STATE_ERROR) { + ev->pl_state = PMCLOG_ERROR; + return -1; + } + + /* + * If there isn't enough data left for a new event try and get + * more data. + */ + if (ps->ps_len == 0) { + ev->pl_state = PMCLOG_REQUIRE_DATA; + + /* + * If we have a valid file descriptor to read from, attempt + * to read from that. This read may return with an error, + * (which may be EAGAIN or other recoverable error), or + * can return EOF. + */ + if (ps->ps_fd != PMCLOG_FD_NONE) { + nread = read(ps->ps_fd, ps->ps_buffer, + PMCLOG_BUFFER_SIZE); + + if (nread <= 0) { + ev->pl_state = nread < 0 ? PMCLOG_ERROR : + PMCLOG_EOF; + return -1; + } + + ps->ps_len = nread; + ps->ps_data = ps->ps_buffer; + } else + return -1; + } + + assert(ps->ps_len > 0); + + /* + * Retrieve one event from the byte stream. + */ + return pmclog_get_event(ps, &ps->ps_data, &ps->ps_len, ev); +} + +/* + * Feed data to a memory based parser. + * + * The memory area pointed to by 'data' needs to be valid till the + * next error return from pmclog_next_event(). + */ + +int +pmclog_feed(void *cookie, char *data, int len) +{ + struct pmclog_parse_state *ps; + + ps = (struct pmclog_parse_state *) cookie; + + if (len < 0 || /* invalid length */ + ps->ps_buffer || /* called for a file parser */ + ps->ps_len != 0) /* unnecessary call */ + return -1; + + ps->ps_data = data; + ps->ps_len = len; + + return 0; +} + +/* + * Allocate and initialize parser state. + */ + +void * +pmclog_open(int fd) +{ + struct pmclog_parse_state *ps; + + if ((ps = (struct pmclog_parse_state *) malloc(sizeof(*ps))) == NULL) + return NULL; + + ps->ps_state = PL_STATE_NEW_RECORD; + ps->ps_arch = -1; + ps->ps_initialized = 0; + ps->ps_count = 0; + ps->ps_offset = (off_t) 0; + bzero(&ps->ps_saved, sizeof(ps->ps_saved)); + ps->ps_svcount = 0; + ps->ps_fd = fd; + ps->ps_data = NULL; + ps->ps_buffer = NULL; + ps->ps_len = 0; + + /* allocate space for a work area */ + if (ps->ps_fd != PMCLOG_FD_NONE) { + if ((ps->ps_buffer = malloc(PMCLOG_BUFFER_SIZE)) == NULL) + return NULL; + } + + return ps; +} + + +/* + * Free up parser state. + */ + +void +pmclog_close(void *cookie) +{ + struct pmclog_parse_state *ps; + + ps = (struct pmclog_parse_state *) cookie; + + if (ps->ps_buffer) + free(ps->ps_buffer); + + free(ps); +} diff --git a/lib/libpmc/pmclog.h b/lib/libpmc/pmclog.h new file mode 100644 index 0000000..3e3119e --- /dev/null +++ b/lib/libpmc/pmclog.h @@ -0,0 +1,146 @@ +/*- + * Copyright (c) 2005 Joseph Koshy + * All rights reserved. + * + * 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 _PMCLOG_H_ +#define _PMCLOG_H_ + +#include + +enum pmclog_state { + PMCLOG_OK, + PMCLOG_EOF, + PMCLOG_REQUIRE_DATA, + PMCLOG_ERROR +}; + +struct pmclog_ev_dropnotify { +}; + +struct pmclog_ev_closelog { +}; + +struct pmclog_ev_initialize { + uint32_t pl_version; + uint32_t pl_arch; +}; + +struct pmclog_ev_mappingchange { + uint32_t pl_type; + pid_t pl_pid; + uintfptr_t pl_start; + uintfptr_t pl_end; + char pl_pathname[PATH_MAX]; +}; + +struct pmclog_ev_pcsample { + uintfptr_t pl_pc; + pid_t pl_pid; + pmc_id_t pl_pmcid; +}; + +struct pmclog_ev_pmcallocate { + uint32_t pl_event; + const char * pl_evname; + uint32_t pl_flags; + pmc_id_t pl_pmcid; +}; + +struct pmclog_ev_pmcattach { + pmc_id_t pl_pmcid; + pid_t pl_pid; + char pl_pathname[PATH_MAX]; +}; + +struct pmclog_ev_pmcdetach { + pmc_id_t pl_pmcid; + pid_t pl_pid; +}; + +struct pmclog_ev_proccsw { + pid_t pl_pid; + pmc_id_t pl_pmcid; + pmc_value_t pl_value; +}; + +struct pmclog_ev_procexec { + pid_t pl_pid; + char pl_pathname[PATH_MAX]; +}; + +struct pmclog_ev_procexit { + uint32_t pl_pid; + pmc_id_t pl_pmcid; + pmc_value_t pl_value; +}; + +struct pmclog_ev_procfork { + pid_t pl_oldpid; + pid_t pl_newpid; +}; + +struct pmclog_ev_sysexit { + pid_t pl_pid; +}; + +struct pmclog_ev_userdata { + uint32_t pl_userdata; +}; + +struct pmclog_ev { + enum pmclog_state pl_state; /* state after 'get_event()' */ + off_t pl_offset; /* byte offset in stream */ + size_t pl_count; /* count of records so far */ + struct timespec pl_ts; /* log entry timestamp */ + enum pmclog_type pl_type; /* type of log entry */ + union { /* log entry data */ + struct pmclog_ev_closelog pl_cl; + struct pmclog_ev_dropnotify pl_dn; + struct pmclog_ev_initialize pl_i; + struct pmclog_ev_mappingchange pl_m; + struct pmclog_ev_pcsample pl_s; + struct pmclog_ev_pmcallocate pl_a; + struct pmclog_ev_pmcattach pl_t; + struct pmclog_ev_pmcdetach pl_d; + struct pmclog_ev_proccsw pl_c; + struct pmclog_ev_procexec pl_x; + struct pmclog_ev_procexit pl_e; + struct pmclog_ev_procfork pl_f; + struct pmclog_ev_sysexit pl_se; + struct pmclog_ev_userdata pl_u; + } pl_u; +}; + +#define PMCLOG_FD_NONE (-1) + +void *pmclog_open(int _fd); +int pmclog_feed(void *_cookie, char *_data, int _len); +int pmclog_read(void *_cookie, struct pmclog_ev *_ev); +void pmclog_close(void *_cookie); + +#endif + -- cgit v1.1