diff options
author | jkoshy <jkoshy@FreeBSD.org> | 2005-06-09 19:45:09 +0000 |
---|---|---|
committer | jkoshy <jkoshy@FreeBSD.org> | 2005-06-09 19:45:09 +0000 |
commit | 1d3209ab83aac3089f15e00934e922d222a4ecf0 (patch) | |
tree | 4970329c2802c6329dd4f6e781d84b27dbf8f412 /lib | |
parent | 4421a087425df7cc08a5671152d0ec7410bdb33e (diff) | |
download | FreeBSD-src-1d3209ab83aac3089f15e00934e922d222a4ecf0.zip FreeBSD-src-1d3209ab83aac3089f15e00934e922d222a4ecf0.tar.gz |
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.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libpmc/Makefile | 14 | ||||
-rw-r--r-- | lib/libpmc/libpmc.c | 1464 | ||||
-rw-r--r-- | lib/libpmc/pmc.3 | 104 | ||||
-rw-r--r-- | lib/libpmc/pmc.h | 50 | ||||
-rw-r--r-- | lib/libpmc/pmclog.3 | 276 | ||||
-rw-r--r-- | lib/libpmc/pmclog.c | 532 | ||||
-rw-r--r-- | lib/libpmc/pmclog.h | 146 |
7 files changed, 1833 insertions, 753 deletions
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 <bsd.lib.mk> 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,14 +290,387 @@ 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__) + +/* + * 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) +}; + +#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), + 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), + 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), + 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), + NULLMASK +}; + +/* dc one bit ecc error */ +static const struct pmc_masks k8_mask_dobee[] = { + __K8MASK(scrubber, 0), + __K8MASK(piggyback, 1), + NULLMASK +}; + +/* dc dispatched prefetch instructions */ +static const struct pmc_masks k8_mask_ddpi[] = { + __K8MASK(load, 0), + __K8MASK(store, 1), + __K8MASK(nta, 2), + NULLMASK +}; + +/* dc dcache accesses by locks */ +static const struct pmc_masks k8_mask_dabl[] = { + __K8MASK(accesses, 0), + __K8MASK(misses, 1), + 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), + 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), + NULLMASK +}; + +/* bu fill into l2 */ +static const struct pmc_masks k8_mask_bfil[] = { + __K8MASK(dirty-l2-victim, 0), + __K8MASK(victim-from-l2, 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), + 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), + 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), + 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), + 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), + 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), + 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), + 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), + 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), + 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" + +static int +k8_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; + + pmc_config->pm_caps |= PMC_CAP_READ; + pmc_config->pm_md.pm_amd.pm_amd_config = 0; + + if (pe == PMC_EV_TSC_TSC) { + /* TSC events must be unqualified. */ + if (ctrspec && *ctrspec != '\0') + return -1; + return 0; + } + + pmask = NULL; + evmask = 0; + +#define __K8SETMASK(M) pmask = k8_mask_##M + + /* setup parsing tables */ + switch (pe) { + case PMC_EV_K8_FP_DISPATCHED_FPU_OPS: + __K8SETMASK(fdfo); + break; + case PMC_EV_K8_LS_SEGMENT_REGISTER_LOAD: + __K8SETMASK(lsrl); + break; + case PMC_EV_K8_LS_LOCKED_OPERATION: + __K8SETMASK(llo); + 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); + break; + case PMC_EV_K8_DC_ONE_BIT_ECC_ERROR: + __K8SETMASK(dobee); + break; + case PMC_EV_K8_DC_DISPATCHED_PREFETCH_INSTRUCTIONS: + __K8SETMASK(ddpi); + break; + case PMC_EV_K8_DC_DCACHE_ACCESSES_BY_LOCKS: + __K8SETMASK(dabl); + break; + case PMC_EV_K8_BU_INTERNAL_L2_REQUEST: + __K8SETMASK(bilr); + break; + case PMC_EV_K8_BU_FILL_REQUEST_L2_MISS: + __K8SETMASK(bfrlm); + break; + case PMC_EV_K8_BU_FILL_INTO_L2: + __K8SETMASK(bfil); + break; + case PMC_EV_K8_FR_RETIRED_FPU_INSTRUCTIONS: + __K8SETMASK(frfi); + break; + case PMC_EV_K8_FR_RETIRED_FASTPATH_DOUBLE_OP_INSTRUCTIONS: + __K8SETMASK(frfdoi); + break; + case PMC_EV_K8_FR_FPU_EXCEPTIONS: + __K8SETMASK(ffe); + break; + case PMC_EV_K8_NB_MEMORY_CONTROLLER_PAGE_ACCESS_EVENT: + __K8SETMASK(nmcpae); + break; + case PMC_EV_K8_NB_MEMORY_CONTROLLER_TURNAROUND: + __K8SETMASK(nmct); + break; + case PMC_EV_K8_NB_MEMORY_CONTROLLER_BYPASS_SATURATION: + __K8SETMASK(nmcbs); + break; + case PMC_EV_K8_NB_SIZED_COMMANDS: + __K8SETMASK(nsc); + break; + case PMC_EV_K8_NB_PROBE_RESULT: + __K8SETMASK(npr); + 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); + break; + + default: + break; /* no options defined */ + } + + 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; + + count = strtol(q, &e, 0); + if (e == q || *e != '\0') + return -1; + + 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, K8_KW_INV)) { + pmc_config->pm_caps |= PMC_CAP_INVERT; + } 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, K8_KW_OS)) { + pmc_config->pm_caps |= PMC_CAP_SYSTEM; + } else if (KWMATCH(p, K8_KW_USR)) { + pmc_config->pm_caps |= PMC_CAP_USER; + } else + return -1; + } + + /* other post processing */ + + 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 */ + 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 = 0x01; /* Rev C and later: #instrs */ + pmc_config->pm_caps |= PMC_CAP_QUALIFIER; + } + 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; + } + } + + 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__) + /* * Intel P4 PMCs */ @@ -629,7 +1006,8 @@ p4_allocate_pmc(enum pmc_event pe, char *ctrspec, 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_p4.pm_p4_cccrconfig = + pmc_config->pm_md.pm_p4.pm_p4_escrconfig = 0; if (pe == PMC_EV_TSC_TSC) { /* TSC must not be further qualified */ @@ -838,7 +1216,7 @@ p4_allocate_pmc(enum pmc_event pe, char *ctrspec, return -1; pmc_config->pm_caps |= PMC_CAP_TAGGING; - pmc_config->pm_p4_escrconfig |= + 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, '='); @@ -850,8 +1228,10 @@ p4_allocate_pmc(enum pmc_event pe, char *ctrspec, 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); + 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 @@ -865,7 +1245,7 @@ p4_allocate_pmc(enum pmc_event pe, char *ctrspec, pmc_config->pm_caps |= PMC_CAP_EDGE; /* fill in thread activity mask */ - pmc_config->pm_p4_cccrconfig |= + pmc_config->pm_md.pm_p4.pm_p4_cccrconfig |= P4_CCCR_TO_ACTIVE_THREAD(cccractivemask); if (evmask) @@ -896,12 +1276,29 @@ p4_allocate_pmc(enum pmc_event pe, char *ctrspec, } } - pmc_config->pm_p4_escrconfig = P4_ESCR_TO_EVENT_MASK(evmask); + 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. */ @@ -1034,7 +1431,7 @@ p6_allocate_pmc(enum pmc_event pe, char *ctrspec, const struct pmc_masks *pm, *pmask; pmc_config->pm_caps |= PMC_CAP_READ; - pmc_config->pm_p6_config = 0; + pmc_config->pm_md.pm_ppro.pm_ppro_config = 0; if (pe == PMC_EV_TSC_TSC) { if (ctrspec && *ctrspec != '\0') @@ -1113,7 +1510,8 @@ p6_allocate_pmc(enum pmc_event pe, char *ctrspec, if (e == q || *e != '\0') return -1; pmc_config->pm_caps |= PMC_CAP_THRESHOLD; - pmc_config->pm_p6_config |= P6_EVSEL_TO_CMASK(count); + 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)) { @@ -1222,396 +1620,274 @@ p6_allocate_pmc(enum pmc_event pe, char *ctrspec, } if (pmc_config->pm_caps & PMC_CAP_QUALIFIER) - pmc_config->pm_p6_config |= P6_EVSEL_TO_UMASK(evmask); + pmc_config->pm_md.pm_ppro.pm_ppro_config |= + P6_EVSEL_TO_UMASK(evmask); return 0; } +#endif + /* - * Pentium style PMCs + * API entry points */ -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) +int +pmc_allocate(const char *ctrspec, enum pmc_mode mode, + uint32_t flags, int cpu, pmc_id_t *pmcid) { - return -1 || pe || ctrspec || pmc_config; /* shut up gcc */ -} + 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; -#elif defined(__amd64__) + spec_copy = NULL; + retval = -1; -/* - * AMD K8 PMCs. - * - * These are very similar to AMD K7 PMCs, but support more kinds of - * events. - */ + if (mode != PMC_MODE_SS && mode != PMC_MODE_TS && + mode != PMC_MODE_SC && mode != PMC_MODE_TC) { + errno = EINVAL; + goto out; + } -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) -}; + /* 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; + } -#define __K8MASK(N,V) PMCMASK(N,(1 << (V))) + if (spec_copy == NULL) + spec_copy = strdup(ctrspec); -/* - * Parsing tables - */ + r = spec_copy; + ctrname = strsep(&r, ","); -/* 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 -}; + /* look for the given counter name */ -/* 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 -}; + for (pe = PMC_EVENT_FIRST; pe < (PMC_EVENT_LAST+1); pe++) + if (!strcmp(ctrname, pmc_event_table[pe].pm_ev_name)) + break; -/* 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 -}; + if (pe > PMC_EVENT_LAST) { + errno = EINVAL; + goto out; + } -/* 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 -}; + 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; -/* dc one bit ecc error */ -static const struct pmc_masks k8_mask_dobee[] = { - __K8MASK(scrubber, 0), - __K8MASK(piggyback, 1), - NULLMASK -}; + if (PMC_IS_SAMPLING_MODE(mode)) + pmc_config.pm_caps |= PMC_CAP_INTERRUPT; -/* dc dispatched prefetch instructions */ -static const struct pmc_masks k8_mask_ddpi[] = { - __K8MASK(load, 0), - __K8MASK(store, 1), - __K8MASK(nta, 2), - NULLMASK -}; + if (pmc_mdep_allocate_pmc(pe, r, &pmc_config) < 0) { + errno = EINVAL; + goto out; + } -/* dc dcache accesses by locks */ -static const struct pmc_masks k8_mask_dabl[] = { - __K8MASK(accesses, 0), - __K8MASK(misses, 1), - NULLMASK -}; + if (PMC_CALL(PMCALLOCATE, &pmc_config) < 0) + goto out; -/* 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 -}; + *pmcid = pmc_config.pm_pmcid; -/* 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 -}; + retval = 0; -/* bu fill into l2 */ -static const struct pmc_masks k8_mask_bfil[] = { - __K8MASK(dirty-l2-victim, 0), - __K8MASK(victim-from-l2, 1), - NULLMASK -}; + out: + if (spec_copy) + free(spec_copy); -/* 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 -}; + return retval; +} -/* 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 -}; +int +pmc_attach(pmc_id_t pmc, pid_t pid) +{ + struct pmc_op_pmcattach pmc_attach_args; -/* 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 -}; + pmc_attach_args.pm_pmc = pmc; + pmc_attach_args.pm_pid = pid; -/* 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 -}; + return PMC_CALL(PMCATTACH, &pmc_attach_args); +} -/* 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 -}; +int +pmc_capabilities(pmc_id_t pmcid, uint32_t *caps) +{ + unsigned int i; + enum pmc_class cl; -/* 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 -}; + 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; +} -/* 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 -}; +int +pmc_configure_logfile(int fd) +{ + struct pmc_op_configurelog cla; -/* 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 -}; + cla.pm_logfd = fd; + if (PMC_CALL(CONFIGURELOG, &cla) < 0) + return -1; + return 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 -}; +int +pmc_cpuinfo(const struct pmc_cpuinfo **pci) +{ + if (pmc_syscall == -1) { + errno = ENXIO; + return -1; + } -#undef __K8MASK + /* kernel<->library, library<->userland interfaces are identical */ + *pci = (struct pmc_cpuinfo *) &cpu_info; + return 0; +} -#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" +int +pmc_detach(pmc_id_t pmc, pid_t pid) +{ + struct pmc_op_pmcattach pmc_detach_args; -static int -k8_allocate_pmc(enum pmc_event pe, char *ctrspec, - struct pmc_op_pmcallocate *pmc_config) + 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) { - char *e, *p, *q; - int n; - uint32_t count, evmask; - const struct pmc_masks *pm, *pmask; + struct pmc_op_pmcadmin ssa; - pmc_config->pm_caps |= PMC_CAP_READ; - pmc_config->pm_amd_config = 0; + ssa.pm_cpu = cpu; + ssa.pm_pmc = pmc; + ssa.pm_state = PMC_STATE_DISABLED; + return PMC_CALL(PMCADMIN, &ssa); +} - if (pe == PMC_EV_TSC_TSC) { - /* TSC events must be unqualified. */ - if (ctrspec && *ctrspec != '\0') - return -1; - return 0; - } +int +pmc_enable(int cpu, int pmc) +{ + struct pmc_op_pmcadmin ssa; - pmask = NULL; - evmask = 0; + ssa.pm_cpu = cpu; + ssa.pm_pmc = pmc; + ssa.pm_state = PMC_STATE_FREE; + return PMC_CALL(PMCADMIN, &ssa); +} -#define __K8SETMASK(M) pmask = k8_mask_##M +/* + * 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. + */ - /* setup parsing tables */ - switch (pe) { - case PMC_EV_K8_FP_DISPATCHED_FPU_OPS: - __K8SETMASK(fdfo); - break; - case PMC_EV_K8_LS_SEGMENT_REGISTER_LOAD: - __K8SETMASK(lsrl); - break; - case PMC_EV_K8_LS_LOCKED_OPERATION: - __K8SETMASK(llo); - 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); - break; - case PMC_EV_K8_DC_ONE_BIT_ECC_ERROR: - __K8SETMASK(dobee); - break; - case PMC_EV_K8_DC_DISPATCHED_PREFETCH_INSTRUCTIONS: - __K8SETMASK(ddpi); - break; - case PMC_EV_K8_DC_DCACHE_ACCESSES_BY_LOCKS: - __K8SETMASK(dabl); - break; - case PMC_EV_K8_BU_INTERNAL_L2_REQUEST: - __K8SETMASK(bilr); - break; - case PMC_EV_K8_BU_FILL_REQUEST_L2_MISS: - __K8SETMASK(bfrlm); - break; - case PMC_EV_K8_BU_FILL_INTO_L2: - __K8SETMASK(bfil); - break; - case PMC_EV_K8_FR_RETIRED_FPU_INSTRUCTIONS: - __K8SETMASK(frfi); - break; - case PMC_EV_K8_FR_RETIRED_FASTPATH_DOUBLE_OP_INSTRUCTIONS: - __K8SETMASK(frfdoi); - break; - case PMC_EV_K8_FR_FPU_EXCEPTIONS: - __K8SETMASK(ffe); - break; - case PMC_EV_K8_NB_MEMORY_CONTROLLER_PAGE_ACCESS_EVENT: - __K8SETMASK(nmcpae); +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_EV_K8_NB_MEMORY_CONTROLLER_TURNAROUND: - __K8SETMASK(nmct); + 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_EV_K8_NB_MEMORY_CONTROLLER_BYPASS_SATURATION: - __K8SETMASK(nmcbs); + 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_EV_K8_NB_SIZED_COMMANDS: - __K8SETMASK(nsc); + 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_EV_K8_NB_PROBE_RESULT: - __K8SETMASK(npr); + 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_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_CLASS_P4: + ev = &pmc_event_table[PMC_EV_P4_FIRST]; + count = PMC_EV_P4_LAST - PMC_EV_P4_FIRST + 1; break; - default: - break; /* no options defined */ + errno = EINVAL; + 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; + if ((names = malloc(count * sizeof(const char *))) == NULL) + return -1; - count = strtol(q, &e, 0); - if (e == q || *e != '\0') - return -1; + *eventnames = names; + *nevents = count; - pmc_config->pm_caps |= PMC_CAP_THRESHOLD; - pmc_config->pm_amd_config |= K8_PMC_TO_COUNTER(count); + for (;count--; ev++, names++) + *names = ev->pm_ev_name; + return 0; +} - } 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 "=")) { - 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)) { - pmc_config->pm_caps |= PMC_CAP_SYSTEM; - } else if (KWMATCH(p, K8_KW_USR)) { - pmc_config->pm_caps |= PMC_CAP_USER; - } else - return -1; - } +int +pmc_flush_logfile(void) +{ + return PMC_CALL(FLUSHLOG,0); +} - /* other post processing */ +int +pmc_get_driver_stats(struct pmc_driverstats *ds) +{ + struct pmc_op_getdriverstats gms; - 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 */ - 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 = 0x01; /* Rev C and later: #instrs */ - pmc_config->pm_caps |= PMC_CAP_QUALIFIER; - } - 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; - } - } + if (PMC_CALL(GETDRIVERSTATS, &gms) < 0) + return -1; - if (pmc_config->pm_caps & PMC_CAP_QUALIFIER) - pmc_config->pm_amd_config = K8_PMC_TO_UNITMASK(evmask); + /* 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; } -#endif -/* - * API entry points - */ +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,128 +1965,145 @@ 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; + int i; - spec_copy = NULL; - retval = -1; + /* + * 'cap' should have a single bit set and should be in + * range. + */ - if (mode != PMC_MODE_SS && mode != PMC_MODE_TS && - mode != PMC_MODE_SC && mode != PMC_MODE_TC) { + if ((cap & (cap - 1)) || cap < PMC_CAP_FIRST || + cap > PMC_CAP_LAST) { errno = EINVAL; - goto out; + return NULL; } - /* 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); + i = ffs(cap); - r = spec_copy; - ctrname = strsep(&r, ","); + return pmc_capability_names[i - 1]; +} - /* look for the given counter name */ +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]; - for (pe = PMC_EVENT_FIRST; pe < (PMC_EVENT_LAST+1); pe++) - if (!strcmp(ctrname, pmc_event_table[pe].pm_ev_name)) - break; + errno = EINVAL; + return NULL; +} - if (pe > PMC_EVENT_LAST) { - errno = EINVAL; - goto out; - } +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; +} - 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; +const char * +pmc_name_of_disposition(enum pmc_disp pd) +{ + if ((int) pd >= PMC_DISP_FIRST && + pd <= PMC_DISP_LAST) + return pmc_disposition_names[pd]; - if (PMC_IS_SAMPLING_MODE(mode)) - pmc_config.pm_caps |= PMC_CAP_INTERRUPT; + errno = EINVAL; + return NULL; +} - if (pmc_mdep_allocate_pmc(pe, r, &pmc_config) < 0) { - errno = EINVAL; - goto out; - } +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(PMCALLOCATE, &pmc_config) < 0) - goto out; + errno = EINVAL; + return NULL; +} - *pmcid = pmc_config.pm_pmcid; +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]; - retval = 0; + errno = EINVAL; + return NULL; +} - out: - if (spec_copy) - free(spec_copy); +const char * +pmc_name_of_state(enum pmc_state ps) +{ + if ((int) ps >= PMC_STATE_FIRST && + ps <= PMC_STATE_LAST) + return pmc_state_names[ps]; - return retval; + errno = EINVAL; + return NULL; } int -pmc_attach(pmc_id_t pmc, pid_t pid) +pmc_ncpu(void) { - struct pmc_op_pmcattach pmc_attach_args; - - pmc_attach_args.pm_pmc = pmc; - pmc_attach_args.pm_pid = pid; + if (pmc_syscall == -1) { + errno = ENXIO; + return -1; + } - return PMC_CALL(PMCATTACH, &pmc_attach_args); + return cpu_info.pm_ncpu; } int -pmc_detach(pmc_id_t pmc, pid_t pid) +pmc_npmc(int cpu) { - struct pmc_op_pmcattach pmc_detach_args; + if (pmc_syscall == -1) { + errno = ENXIO; + return -1; + } - pmc_detach_args.pm_pmc = pmc; - pmc_detach_args.pm_pid = pid; + if (cpu < 0 || cpu >= (int) cpu_info.pm_ncpu) { + errno = EINVAL; + return -1; + } - return PMC_CALL(PMCDETACH, &pmc_detach_args); + return cpu_info.pm_npmc; } int -pmc_release(pmc_id_t pmc) +pmc_pmcinfo(int cpu, struct pmc_pmcinfo **ppmci) { - struct pmc_op_simple pmc_release_args; + int nbytes, npmc; + struct pmc_op_getpmcinfo *pmci; - pmc_release_args.pm_pmcid = pmc; + if ((npmc = pmc_npmc(cpu)) < 0) + return -1; - return PMC_CALL(PMCRELEASE, &pmc_release_args); -} + nbytes = sizeof(struct pmc_op_getpmcinfo) + + npmc * sizeof(struct pmc_info); -int -pmc_start(pmc_id_t pmc) -{ - struct pmc_op_simple pmc_start_args; + if ((pmci = calloc(1, nbytes)) == NULL) + return -1; - pmc_start_args.pm_pmcid = pmc; - return PMC_CALL(PMCSTART, &pmc_start_args); -} + pmci->pm_cpu = cpu; -int -pmc_stop(pmc_id_t pmc) -{ - struct pmc_op_simple pmc_stop_args; + if (PMC_CALL(GETPMCINFO, pmci) < 0) { + free(pmci); + return -1; + } - pmc_stop_args.pm_pmcid = pmc; - return PMC_CALL(PMCSTOP, &pmc_stop_args); + /* kernel<->library, library<->userland interfaces are identical */ + *ppmci = (struct pmc_pmcinfo *) pmci; + + return 0; } int @@ -1830,15 +2124,13 @@ pmc_read(pmc_id_t pmc, pmc_value_t *value) } int -pmc_write(pmc_id_t pmc, pmc_value_t value) +pmc_release(pmc_id_t pmc) { - struct pmc_op_pmcrw pmc_write_op; + struct pmc_op_simple pmc_release_args; - pmc_write_op.pm_pmcid = pmc; - pmc_write_op.pm_flags = PMC_F_NEWVALUE; - pmc_write_op.pm_value = value; + pmc_release_args.pm_pmcid = pmc; - return PMC_CALL(PMCRW, &pmc_write_op); + return PMC_CALL(PMCRELEASE, &pmc_release_args); } int @@ -1874,111 +2166,21 @@ pmc_set(pmc_id_t pmc, pmc_value_t value) } 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_get_driver_stats(struct pmc_op_getdriverstats *gms) -{ - return PMC_CALL(GETDRIVERSTATS, gms); -} - -int -pmc_ncpu(void) -{ - if (pmc_syscall == -1) { - errno = ENXIO; - return -1; - } - - return cpu_info.pm_ncpu; -} - -int -pmc_npmc(int cpu) -{ - if (pmc_syscall == -1) { - errno = ENXIO; - return -1; - } - - if (cpu < 0 || cpu >= (int) cpu_info.pm_ncpu) { - errno = EINVAL; - return -1; - } - - return cpu_info.pm_npmc; -} - -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_start(pmc_id_t pmc) { - int nbytes, npmc, saved_errno; - struct pmc_op_getpmcinfo *pmci; - - if ((npmc = pmc_npmc(cpu)) < 0) - return -1; - - nbytes = sizeof(struct pmc_op_getpmcinfo) + - npmc * sizeof(struct pmc_info); - - if ((pmci = calloc(1, nbytes)) == NULL) - return -1; - - pmci->pm_cpu = cpu; - - if (PMC_CALL(GETPMCINFO, pmci) < 0) { - saved_errno = errno; - free(pmci); - errno = saved_errno; - return -1; - } + struct pmc_op_simple pmc_start_args; - *ppmci = pmci; - return 0; + pmc_start_args.pm_pmcid = pmc; + return PMC_CALL(PMCSTART, &pmc_start_args); } int -pmc_cpuinfo(const struct pmc_op_getcpuinfo **pci) +pmc_stop(pmc_id_t pmc) { - if (pmc_syscall == -1) { - errno = ENXIO; - return -1; - } + struct pmc_op_simple pmc_stop_args; - *pci = &cpu_info; - return 0; + pmc_stop_args.pm_pmcid = pmc; + return PMC_CALL(PMCSTOP, &pmc_stop_args); } int @@ -1997,182 +2199,22 @@ pmc_width(pmc_id_t pmcid, uint32_t *width) } 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; -} - -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; -} - -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]; - - errno = EINVAL; - return NULL; -} - -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; -} - -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; - - errno = EINVAL; - return NULL; -} - -const char * -pmc_name_of_state(enum pmc_state ps) -{ - if ((int) ps >= PMC_STATE_FIRST && - ps <= PMC_STATE_LAST) - return pmc_state_names[ps]; - - errno = EINVAL; - return NULL; -} - -const char * -pmc_name_of_disposition(enum pmc_disp pd) -{ - if ((int) pd >= PMC_DISP_FIRST && - pd <= PMC_DISP_LAST) - return pmc_disposition_names[pd]; - - errno = EINVAL; - return NULL; -} - -const char * -pmc_name_of_capability(enum pmc_caps cap) -{ - 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); - - return pmc_capability_names[i - 1]; -} - -/* - * 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 <sys/pmc.h> /* + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/pmc.h> +#include <sys/pmclog.h> + +#include <assert.h> +#include <errno.h> +#include <pmc.h> +#include <pmclog.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> + +#include <machine/pmc_mdep.h> + +#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 <sys/pmclog.h> + +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 + |