506 lines
18 KiB
C
506 lines
18 KiB
C
/* $NetBSD: pmc.c,v 1.12 2005/06/02 02:14:58 lukem Exp $ */
|
|
|
|
/*
|
|
* Copyright 2000 Wasabi Systems, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Written by Frank van der Linden for Wasabi Systems, Inc.
|
|
*
|
|
* 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.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed for the NetBSD Project by
|
|
* Wasabi Systems, Inc.
|
|
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
|
|
* or promote products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
|
|
* 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>
|
|
|
|
#ifndef lint
|
|
__RCSID("$NetBSD: pmc.c,v 1.12 2005/06/02 02:14:58 lukem Exp $");
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <machine/sysarch.h>
|
|
#include <machine/specialreg.h>
|
|
#include <sys/wait.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
struct pmc_name2val {
|
|
const char *name;
|
|
int val;
|
|
int unit;
|
|
};
|
|
|
|
const struct pmc_name2val i586_names[] = {
|
|
{ "tlb-data-miss", PMC5_DATA_TLB_MISS, 0 },
|
|
{ "tlb-ins-miss", PMC5_INST_TLB_MISS, 0 },
|
|
{ "l1cache-ins-miss", PMC5_INST_CACHE_MISS, 0 },
|
|
{ "l1cache-data-miss", PMC5_DATA_RW_MISS, 0 },
|
|
{ "l1cache-data-miss-read", PMC5_DATA_READ_MISS, 0 },
|
|
{ "l1cache-data-miss-write", PMC5_DATA_WRITE_MISS, 0 },
|
|
{ "l1cache-writeback", PMC5_DATA_LINES_WBACK, 0 },
|
|
{ "l1cache-writeback-hit", PMC5_WRITE_M_E, 0 },
|
|
{ "l2cache-data-snoop", PMC5_DATA_CACHE_SNOOP, 0 },
|
|
{ "l2cache-data-snoop-hit", PMC5_DATA_CACHE_SNOOP_HIT, 0 },
|
|
{ "mem-read", PMC5_DATA_READ, 0 },
|
|
{ "mem-write", PMC5_DATA_WRITE, 0 },
|
|
{ "mem-access", PMC5_DATA_RW, 0 },
|
|
{ "mem-access-both-pipes", PMC5_MEM_ACCESS_BOTH_PIPES, 0 },
|
|
{ "mem-bank-conflicts", PMC5_BANK_CONFLICTS, 0 },
|
|
{ "mem-misalign-ref", PMC5_MISALIGNED_DATA, 0 },
|
|
{ "mem-uncached-read", PMC5_NONCACHE_MEM_READ, 0 },
|
|
{ "seg-load-any", PMC5_SEGMENT_REG_LOAD, 0 },
|
|
{ "branch", PMC5_BRANCHES, 0 },
|
|
{ "branch-btb-hit", PMC5_BTB_HITS, 0 },
|
|
{ "branch-taken", PMC5_BRANCH_TAKEN, 0 },
|
|
{ "ins-read", PMC5_INST_READ, 0 },
|
|
{ "ins-pipeline-flush", PMC5_PIPELINE_FLUSH, 0 },
|
|
{ "ins-executed", PMC5_INST_EXECUTED, 0 },
|
|
{ "ins-executed-vpipe", PMC5_INST_EXECUTED_V_PIPE, 0 },
|
|
{ "ins-stall-agi", PMC5_AGI_STALL, 0 },
|
|
{ "ins-stall-write", PMC5_WRITE_BACKUP_STALL, 0 },
|
|
{ "ins-stall-data", PMC5_DATA_READ_STALL, 0 },
|
|
{ "ins-stall-writeline", PMC5_WRITE_E_M_STALL, 0 },
|
|
{ "bus-utilization", PMC5_BUS_UTILIZATION, 0 },
|
|
{ "bus-locked", PMC5_LOCKED_BUS, 0 },
|
|
{ "bus-io-cycle", PMC5_IO_CYCLE, 0 },
|
|
{ "fpu-flops", PMC5_FLOPS, 0 },
|
|
{ "int-hw", PMC5_HARDWARE_INTR, 0 },
|
|
{ "break-match0", PMC5_BP0_MATCH, 0 },
|
|
{ "break-match1", PMC5_BP1_MATCH, 0 },
|
|
{ "break-match2", PMC5_BP2_MATCH, 0 },
|
|
{ "break-match3", PMC5_BP3_MATCH, 0 },
|
|
};
|
|
|
|
const struct pmc_name2val i686_names[] = {
|
|
{ "mem-refs", PMC6_DATA_MEM_REFS, 0 },
|
|
{ "l1cache-lines", PMC6_DCU_LINES_IN, 0 },
|
|
{ "l1cache-mlines", PMC6_DCU_M_LINES_IN, 0 },
|
|
{ "l1cache-mlines-evict", PMC6_DCU_M_LINES_OUT, 0 },
|
|
{ "l1cache-miss-wait", PMC6_DCU_MISS_OUTSTANDING, 0 },
|
|
{ "ins-fetch", PMC6_IFU_IFETCH, 0 },
|
|
{ "ins-fetch-misses", PMC6_IFU_IFETCH_MISS, 0 },
|
|
{ "itlb-misses", PMC6_IFU_IFETCH_MISS, 0 },
|
|
{ "insfetch-mem-stall", PMC6_IFU_MEM_STALL, 0 },
|
|
{ "insfetch-decode-stall", PMC6_ILD_STALL, 0 },
|
|
{ "l2cache-insfetch", PMC6_L2_IFETCH, 0x0f },
|
|
{ "l2cache-data-loads", PMC6_L2_LD, 0x0f },
|
|
{ "l2cache-data-stores", PMC6_L2_ST, 0x0f },
|
|
{ "l2cache-lines", PMC6_L2_LINES_IN, 0 },
|
|
{ "l2cache-lines-evict", PMC6_L2_LINES_OUT, 0 },
|
|
{ "l2cache-mlines", PMC6_L2_M_LINES_INM, 0 },
|
|
{ "l2cache-mlines-evict", PMC6_L2_M_LINES_OUTM, 0x0f },
|
|
{ "l2cache-reqs", PMC6_L2_RQSTS, 0 },
|
|
{ "l2cache-addr-strobes", PMC6_L2_ADS, 0 },
|
|
{ "l2cache-data-busy", PMC6_L2_DBUS_BUSY, 0 },
|
|
{ "l2cache-data-busy-read", PMC6_L2_DBUS_BUSY_RD },
|
|
{ "bus-drdy-clocks-self", PMC6_BUS_DRDY_CLOCKS, 0x00 },
|
|
{ "bus-drdy-clocks-any", PMC6_BUS_DRDY_CLOCKS, 0x20 },
|
|
{ "bus-lock-clocks-self", PMC6_BUS_LOCK_CLOCKS, 0x00 },
|
|
{ "bus-lock-clocks-any", PMC6_BUS_LOCK_CLOCKS, 0x20 },
|
|
{ "bus-req-outstanding-self", PMC6_BUS_REQ_OUTSTANDING, 0x00 },
|
|
{ "bus-req-outstanding-any", PMC6_BUS_REQ_OUTSTANDING, 0x20 },
|
|
{ "bus-burst-reads-self", PMC6_BUS_TRAN_BRD, 0x00 },
|
|
{ "bus-burst-reads-any", PMC6_BUS_TRAN_BRD, 0x20 },
|
|
{ "bus-read-for-ownership-self",PMC6_BUS_TRAN_RFO, 0x00 },
|
|
{ "bus-read-for-ownership-any", PMC6_BUS_TRAN_RFO, 0x20 },
|
|
{ "bus-write-back-self", PMC6_BUS_TRANS_WB, 0x00 },
|
|
{ "bus-write-back-any", PMC6_BUS_TRANS_WB, 0x20 },
|
|
{ "bus-ins-fetches-self", PMC6_BUS_TRAN_IFETCH, 0x00 },
|
|
{ "bus-ins-fetches-any", PMC6_BUS_TRAN_IFETCH, 0x20 },
|
|
{ "bus-invalidates-self", PMC6_BUS_TRAN_INVAL, 0x00 },
|
|
{ "bus-invalidates-any", PMC6_BUS_TRAN_INVAL, 0x20 },
|
|
{ "bus-partial-writes-self", PMC6_BUS_TRAN_PWR, 0x00 },
|
|
{ "bus-partial-writes-any", PMC6_BUS_TRAN_PWR, 0x20 },
|
|
{ "bus-partial-trans-self", PMC6_BUS_TRANS_P, 0x00 },
|
|
{ "bus-partial-trans-any", PMC6_BUS_TRANS_P, 0x20 },
|
|
{ "bus-io-trans-self", PMC6_BUS_TRANS_IO, 0x00 },
|
|
{ "bus-io-trans-any", PMC6_BUS_TRANS_IO, 0x20 },
|
|
{ "bus-deferred-trans-self", PMC6_BUS_TRAN_DEF, 0x00 },
|
|
{ "bus-deferred-trans-any", PMC6_BUS_TRAN_DEF, 0x20 },
|
|
{ "bus-burst-trans-self", PMC6_BUS_TRAN_BURST, 0x00 },
|
|
{ "bus-burst-trans-any", PMC6_BUS_TRAN_BURST, 0x20 },
|
|
{ "bus-total-trans-self", PMC6_BUS_TRAN_ANY, 0x00 },
|
|
{ "bus-total-trans-any", PMC6_BUS_TRAN_ANY, 0x20 },
|
|
{ "bus-mem-trans-self", PMC6_BUS_TRAN_MEM, 0x00 },
|
|
{ "bus-mem-trans-any", PMC6_BUS_TRAN_MEM, 0x20 },
|
|
{ "bus-recv-cycles", PMC6_BUS_DATA_RCV, 0 },
|
|
{ "bus-bnr-cycles", PMC6_BUS_BNR_DRV, 0 },
|
|
{ "bus-hit-cycles", PMC6_BUS_HIT_DRV, 0 },
|
|
{ "bus-hitm-cycles", PMC6_BUS_HITM_DRDV, 0 },
|
|
{ "bus-snoop-stall", PMC6_BUS_SNOOP_STALL, 0 },
|
|
{ "fpu-flops", PMC6_FLOPS, 0 },
|
|
{ "fpu-comp-ops", PMC6_FP_COMP_OPS_EXE, 0 },
|
|
{ "fpu-except-assist", PMC6_FP_ASSIST, 0 },
|
|
{ "fpu-mul", PMC6_MUL, 0 },
|
|
{ "fpu-div", PMC6_DIV, 0 },
|
|
{ "fpu-div-busy", PMC6_CYCLES_DIV_BUSY, 0 },
|
|
{ "mem-sb-blocks", PMC6_LD_BLOCKS, 0 },
|
|
{ "mem-sb-drains", PMC6_SB_DRAINS, 0 },
|
|
{ "mem-misalign-ref", PMC6_MISALIGN_MEM_REF, 0 },
|
|
{ "ins-pref-dispatch-nta", PMC6_EMON_KNI_PREF_DISPATCHED, 0x01 },
|
|
{ "ins-pref-dispatch-t1", PMC6_EMON_KNI_PREF_DISPATCHED, 0x01 },
|
|
{ "ins-pref-dispatch-t2", PMC6_EMON_KNI_PREF_DISPATCHED, 0x02 },
|
|
{ "ins-pref-dispatch-weak", PMC6_EMON_KNI_PREF_DISPATCHED, 0x03 },
|
|
{ "ins-pref-miss-nta", PMC6_EMON_KNI_PREF_MISS, 0x01 },
|
|
{ "ins-pref-miss-t1", PMC6_EMON_KNI_PREF_MISS, 0x01 },
|
|
{ "ins-pref-miss-t2", PMC6_EMON_KNI_PREF_MISS, 0x02 },
|
|
{ "ins-pref-miss-weak", PMC6_EMON_KNI_PREF_MISS, 0x03 },
|
|
{ "ins-retired", PMC6_INST_RETIRED, 0 },
|
|
{ "uops-retired", PMC6_UOPS_RETIRED, 0 },
|
|
{ "ins-decoded", PMC6_INST_DECODED, 0 },
|
|
{ "ins-stream-retired-packed-scalar",
|
|
PMC6_EMON_KNI_INST_RETIRED, 0x00 },
|
|
{ "ins-stream-retired-scalar",
|
|
PMC6_EMON_KNI_INST_RETIRED, 0x01 },
|
|
{ "ins-stream-comp-retired-packed-scalar",
|
|
PMC6_EMON_KNI_COMP_INST_RET, 0x00 },
|
|
{ "ins-stream-comp-retired--scalar",
|
|
PMC6_EMON_KNI_COMP_INST_RET, 0x01 },
|
|
{ "int-hw", PMC6_HW_INT_RX, 0 },
|
|
{ "int-cycles-masked", PMC6_CYCLES_INT_MASKED, 0 },
|
|
{ "int-cycles-masked-pending",
|
|
PMC6_CYCLES_INT_PENDING_AND_MASKED, 0 },
|
|
{ "branch-retired", PMC6_BR_INST_RETIRED, 0 },
|
|
{ "branch-miss-retired", PMC6_BR_MISS_PRED_RETIRED, 0 },
|
|
{ "branch-taken-retired", PMC6_BR_TAKEN_RETIRED, 0 },
|
|
{ "branch-taken-mispred-retired", PMC6_BR_MISS_PRED_TAKEN_RET, 0 },
|
|
{ "branch-decoded", PMC6_BR_INST_DECODED, 0 },
|
|
{ "branch-btb-miss", PMC6_BTB_MISSES, 0 },
|
|
{ "branch-bogus", PMC6_BR_BOGUS, 0 },
|
|
{ "branch-baclear", PMC6_BACLEARS, 0 },
|
|
{ "stall-resource", PMC6_RESOURCE_STALLS, 0 },
|
|
{ "stall-partial", PMC6_PARTIAL_RAT_STALLS, 0 },
|
|
{ "seg-loads", PMC6_SEGMENT_REG_LOADS, 0 },
|
|
{ "unhalted-cycles", PMC6_CPU_CLK_UNHALTED, 0 },
|
|
{ "mmx-exec", PMC6_MMX_INSTR_EXEC, 0 },
|
|
{ "mmx-sat-exec", PMC6_MMX_SAT_INSTR_EXEC, 0 },
|
|
{ "mmx-uops-exec", PMC6_MMX_UOPS_EXEC, 0x0f },
|
|
{ "mmx-exec-packed-mul", PMC6_MMX_INSTR_TYPE_EXEC, 0x01 },
|
|
{ "mmx-exec-packed-shift", PMC6_MMX_INSTR_TYPE_EXEC, 0x02 },
|
|
{ "mmx-exec-pack-ops", PMC6_MMX_INSTR_TYPE_EXEC, 0x04 },
|
|
{ "mmx-exec-unpack-ops", PMC6_MMX_INSTR_TYPE_EXEC, 0x08 },
|
|
{ "mmx-exec-packed-logical", PMC6_MMX_INSTR_TYPE_EXEC, 0x10 },
|
|
{ "mmx-exec-packed-arith", PMC6_MMX_INSTR_TYPE_EXEC, 0x20 },
|
|
{ "mmx-trans-mmx-float", PMC6_FP_MMX_TRANS, 0x00 },
|
|
{ "mmx-trans-float-mmx", PMC6_FP_MMX_TRANS, 0x01 },
|
|
{ "mmx-assist", PMC6_MMX_ASSIST, 0 },
|
|
{ "mmx-retire", PMC6_MMX_INSTR_RET, 0 },
|
|
{ "seg-rename-stalls-es", PMC6_SEG_RENAME_STALLS, 0x01 },
|
|
{ "seg-rename-stalls-ds", PMC6_SEG_RENAME_STALLS, 0x02 },
|
|
{ "seg-rename-stalls-fs", PMC6_SEG_RENAME_STALLS, 0x04 },
|
|
{ "seg-rename-stalls-gs", PMC6_SEG_RENAME_STALLS, 0x08 },
|
|
{ "seg-rename-stalls-all", PMC6_SEG_RENAME_STALLS, 0x0f },
|
|
{ "seg-rename-es", PMC6_SEG_REG_RENAMES, 0x01 },
|
|
{ "seg-rename-ds", PMC6_SEG_REG_RENAMES, 0x02 },
|
|
{ "seg-rename-fs", PMC6_SEG_REG_RENAMES, 0x04 },
|
|
{ "seg-rename-gs", PMC6_SEG_REG_RENAMES, 0x08 },
|
|
{ "seg-rename-all", PMC6_SEG_REG_RENAMES, 0x0f },
|
|
{ "seg-rename-retire", PMC6_RET_SEG_RENAMES, 0 },
|
|
};
|
|
|
|
const struct pmc_name2val k7_names[] = {
|
|
{ "seg-load-all", K7_SEGMENT_REG_LOADS, 0x7f },
|
|
{ "seg-load-es", K7_SEGMENT_REG_LOADS, 0x01 },
|
|
{ "seg-load-cs", K7_SEGMENT_REG_LOADS, 0x02 },
|
|
{ "seg-load-ss", K7_SEGMENT_REG_LOADS, 0x04 },
|
|
{ "seg-load-ds", K7_SEGMENT_REG_LOADS, 0x08 },
|
|
{ "seg-load-fs", K7_SEGMENT_REG_LOADS, 0x10 },
|
|
{ "seg-load-gs", K7_SEGMENT_REG_LOADS, 0x20 },
|
|
{ "seg-load-hs", K7_SEGMENT_REG_LOADS, 0x40 },
|
|
{ "seg-load-stall", K7_SEGMENT_LOAD_STALL, 0 },
|
|
{ "l1cache-access", K7_DATA_CACHE_ACCESS, 0 },
|
|
{ "l1cache-miss", K7_DATA_CACHE_MISS, 0 },
|
|
{ "l1cache-refill", K7_DATA_CACHE_REFILL, 0x1f },
|
|
{ "l1cache-refill-invalid", K7_DATA_CACHE_REFILL, 0x01 },
|
|
{ "l1cache-refill-shared", K7_DATA_CACHE_REFILL, 0x02 },
|
|
{ "l1cache-refill-exclusive", K7_DATA_CACHE_REFILL, 0x04 },
|
|
{ "l1cache-refill-owner", K7_DATA_CACHE_REFILL, 0x08 },
|
|
{ "l1cache-refill-modified", K7_DATA_CACHE_REFILL, 0x10 },
|
|
{ "l1cache-load", K7_DATA_CACHE_REFILL_SYSTEM, 0x1f },
|
|
{ "l1cache-load-invalid", K7_DATA_CACHE_REFILL_SYSTEM, 0x01 },
|
|
{ "l1cache-load-shared", K7_DATA_CACHE_REFILL_SYSTEM, 0x02 },
|
|
{ "l1cache-load-exclusive", K7_DATA_CACHE_REFILL_SYSTEM, 0x04 },
|
|
{ "l1cache-load-owner", K7_DATA_CACHE_REFILL_SYSTEM, 0x08 },
|
|
{ "l1cache-load-modified", K7_DATA_CACHE_REFILL_SYSTEM, 0x10 },
|
|
{ "l1cache-writeback", K7_DATA_CACHE_WBACK, 0x1f },
|
|
{ "l1cache-writeback-invalid", K7_DATA_CACHE_WBACK, 0x01 },
|
|
{ "l1cache-writeback-shared", K7_DATA_CACHE_WBACK, 0x02 },
|
|
{ "l1cache-writeback-exclusive",K7_DATA_CACHE_WBACK, 0x04 },
|
|
{ "l1cache-writeback-owner", K7_DATA_CACHE_WBACK, 0x08 },
|
|
{ "l1cache-writeback-modified", K7_DATA_CACHE_WBACK, 0x10 },
|
|
{ "l2cache-access", K7_L2_REQUEST, 0xff },
|
|
{ "l2cache-tag-read", K7_L2_REQUEST, 0x01 },
|
|
{ "l2cache-tag-write", K7_L2_REQUEST, 0x02 },
|
|
{ "l2cache-inst-read", K7_L2_REQUEST, 0x04 },
|
|
{ "l2cache-inst-load", K7_L2_REQUEST, 0x08 },
|
|
{ "l2cache-data-store", K7_L2_REQUEST, 0x10 },
|
|
{ "l2cache-data-loadmem", K7_L2_REQUEST, 0x20 },
|
|
{ "l2cache-data-write", K7_L2_REQUEST, 0x40 },
|
|
{ "l2cache-data-move", K7_L2_REQUEST, 0x80 },
|
|
{ "l2cache-access-busy", K7_L2_REQUEST_BUSY, 0 },
|
|
{ "l2cache-hit", K7_L2_DTLB_HIT, 0 },
|
|
{ "l2cache-miss", K7_L2_DTLB_MISS, 0 },
|
|
{ "mem-misalign-ref", K7_MISALIGNED_DATA_REF, 0 },
|
|
{ "mem-access", K7_SYSTEM_REQUEST, 0 },
|
|
{ "mem-access-uc", K7_SYSTEM_REQUEST_TYPE, 0x01 },
|
|
{ "mem-access-wc", K7_SYSTEM_REQUEST_TYPE, 0x04 },
|
|
{ "mem-access-wt", K7_SYSTEM_REQUEST_TYPE, 0x10 },
|
|
{ "mem-access-wp", K7_SYSTEM_REQUEST_TYPE, 0x20 },
|
|
{ "mem-access-wb", K7_SYSTEM_REQUEST_TYPE, 0x40 },
|
|
{ "ins-fetch", K7_IFU_IFETCH, 0 },
|
|
{ "ins-fetch-miss", K7_IFU_IFETCH_MISS, 0 },
|
|
{ "ins-refill-l2", K7_IFU_REFILL_FROM_L2, 0 },
|
|
{ "ins-refill-mem", K7_IFU_REFILL_FROM_SYSTEM, 0 },
|
|
{ "ins-fetch-stall", K7_IFU_STALL, 0 },
|
|
{ "ins-retired", K7_RETIRED_INST, 0 },
|
|
{ "ins-empty", K7_INSTRUCTION_DECODER_EMPTY, 0 },
|
|
{ "itlb-miss-l1", K7_ITLB_L1_MISS, 0 },
|
|
{ "itlb-miss-l2", K7_ITLB_L2_MISS, 0 },
|
|
{ "ops-retired", K7_RETIRED_OPS, 0 },
|
|
{ "branch-retired", K7_RETIRED_BRANCHES, 0 },
|
|
{ "branch-miss-retired", K7_RETIRED_BRANCH_MISPREDICTED, 0 },
|
|
{ "branch-taken-retired", K7_RETIRED_TAKEN_BRANCH, 0 },
|
|
{ "branch-taked-miss-retired",
|
|
K7_RETIRED_TAKEN_BRANCH_MISPREDICTED, 0 },
|
|
{ "branch-far-retired",
|
|
K7_RETIRED_FAR_CONTROL_TRANSFER, 0 },
|
|
{ "branch-resync-retired", K7_RETIRED_RESYNC_BRANCH, 0 },
|
|
{ "branch-near-retired", K7_RETIRED_NEAR_RETURNS, 0 },
|
|
{ "branch-near-miss-retired",
|
|
K7_RETIRED_NEAR_RETURNS_MISPREDICTED, 0 },
|
|
{ "branch-indirect-miss-retired",
|
|
K7_RETIRED_INDIRECT_MISPREDICTED, 0 },
|
|
{ "int-hw", K7_HW_INTR_RECV, 0 },
|
|
{ "int-cycles-masked", K7_CYCLES_INT_MASKED, 0 },
|
|
{ "int-cycles-masked-pending",
|
|
K7_CYCLES_INT_PENDING_AND_MASKED, 0 },
|
|
{ "break-match0", K7_BP0_MATCH, 0 },
|
|
{ "break-match1", K7_BP1_MATCH, 0 },
|
|
{ "break-match2", K7_BP2_MATCH, 0 },
|
|
{ "break-match3", K7_BP3_MATCH, 0 },
|
|
};
|
|
|
|
struct pmc_name2val_cpus {
|
|
int type;
|
|
const struct pmc_name2val *pmc_names;
|
|
int size;
|
|
} pmc_cpus[] = {
|
|
{ PMC_TYPE_I586, i586_names,
|
|
sizeof(i586_names)/sizeof(struct pmc_name2val) },
|
|
{ PMC_TYPE_I686, i686_names,
|
|
sizeof(i686_names)/sizeof(struct pmc_name2val) },
|
|
{ PMC_TYPE_K7, k7_names,
|
|
sizeof(k7_names)/sizeof(struct pmc_name2val) },
|
|
};
|
|
|
|
|
|
static const struct pmc_name2val_cpus *
|
|
pmc_lookup_cpu(int type)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof(pmc_cpus)/sizeof(struct pmc_name2val_cpus);
|
|
i++) {
|
|
if (pmc_cpus[i].type == type)
|
|
return (&pmc_cpus[i]);
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
|
|
static const struct pmc_name2val *
|
|
find_pmc_name(const struct pmc_name2val_cpus *pncp, const char *name)
|
|
{
|
|
int i;
|
|
const struct pmc_name2val *pnp = NULL;
|
|
|
|
for (i = 0; i < pncp->size; i++) {
|
|
if (strcmp(pncp->pmc_names[i].name, name) == 0) {
|
|
pnp = &pncp->pmc_names[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (pnp);
|
|
}
|
|
|
|
static void
|
|
list_pmc_names(const struct pmc_name2val_cpus *pncp)
|
|
{
|
|
int i, n, left, pairs;
|
|
|
|
printf("Supported performance counter events:\n");
|
|
n = pncp->size;
|
|
pairs = n / 2;
|
|
left = n % 2;
|
|
|
|
for (i = 0; i < pairs; i++)
|
|
printf(" %37s %37s\n", pncp->pmc_names[i * 2].name,
|
|
pncp->pmc_names[i * 2 + 1].name);
|
|
if (left != 0)
|
|
printf("\t%37s\n", pncp->pmc_names[n - 1].name);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
|
|
fprintf(stderr, "usage: %s -h\n"
|
|
" %s -C\n"
|
|
" %s -c <event> command [options] ...\n",
|
|
getprogname(), getprogname(), getprogname());
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int c, status, ret0, ret1, errn0, errn1;
|
|
char *event = NULL;
|
|
const struct pmc_name2val_cpus *pncp;
|
|
const struct pmc_name2val *pnp;
|
|
struct i386_pmc_info_args pi;
|
|
struct i386_pmc_startstop_args pss0, pss1;
|
|
struct i386_pmc_read_args pr0, pr1;
|
|
pid_t pid;
|
|
|
|
errn0 = errn1 = 0;
|
|
|
|
if (i386_pmc_info(&pi) < 0)
|
|
errx(2, "PMC support is not compiled into the kernel");
|
|
|
|
pncp = pmc_lookup_cpu(pi.type);
|
|
if (pncp == NULL)
|
|
errx(3, "PMC counters are not supported for your CPU (0x%x)",
|
|
pi.type);
|
|
|
|
pnp = NULL;
|
|
while ((c = getopt(argc, argv, "Cc:h")) != -1) {
|
|
switch (c) {
|
|
case 'C':
|
|
if (argc != 2)
|
|
usage();
|
|
/*
|
|
* Just clear both counters. Useful if
|
|
* a previous run got killed and did not
|
|
* clean up.
|
|
*/
|
|
memset(&pss0, 0, sizeof pss0);
|
|
i386_pmc_startstop(&pss0);
|
|
pss0.counter = 1;
|
|
i386_pmc_startstop(&pss0);
|
|
exit(0);
|
|
case 'c':
|
|
event = optarg;
|
|
pnp = find_pmc_name(pncp, event);
|
|
break;
|
|
case 'h':
|
|
if (argc != 2)
|
|
usage();
|
|
list_pmc_names(pncp);
|
|
exit(0);
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
|
|
if (pnp == NULL || argc <= optind || event == NULL)
|
|
usage();
|
|
|
|
memset(&pss0, 0, sizeof pss0);
|
|
memset(&pss1, 0, sizeof pss1);
|
|
pss0.event = pss1.event = pnp->val;
|
|
pss0.unit = pss1.unit = pnp->unit;
|
|
pss0.flags = PMC_SETUP_USER;
|
|
pss0.counter = 0;
|
|
pss1.flags = PMC_SETUP_KERNEL;
|
|
pss1.counter = 1;
|
|
|
|
/*
|
|
* XXX should catch signals and tidy up in the parent.
|
|
*/
|
|
if (i386_pmc_startstop(&pss0) < 0)
|
|
err(4, "pmc_start user");
|
|
pss0.flags = 0;
|
|
if (i386_pmc_startstop(&pss1) < 0)
|
|
err(5, "pmc_start kernel");
|
|
pss1.flags = 0;
|
|
|
|
pid = vfork();
|
|
|
|
switch(pid) {
|
|
case -1:
|
|
errx(5, "vfork");
|
|
case 0:
|
|
execvp(argv[optind], &argv[optind]);
|
|
errx(6, "execvp");
|
|
}
|
|
|
|
(void)signal(SIGINT, SIG_IGN);
|
|
(void)signal(SIGQUIT, SIG_IGN);
|
|
|
|
waitpid(pid, &status, 0);
|
|
if (!WIFEXITED(status))
|
|
return 0;
|
|
|
|
/*
|
|
* Do not immediately exit on errors below. The counters
|
|
* must be stopped first, or subsequent runs will get
|
|
* EBUSY.
|
|
*/
|
|
pr0.counter = 0;
|
|
ret0 = i386_pmc_read(&pr0);
|
|
if (ret0 < 0)
|
|
errn0 = errno;
|
|
pr1.counter = 1;
|
|
ret1 = i386_pmc_read(&pr1);
|
|
if (ret1 < 0)
|
|
errn1 = errno;
|
|
|
|
if (i386_pmc_startstop(&pss0) < 0)
|
|
warn("pmc_stop user");
|
|
if (i386_pmc_startstop(&pss1) < 0)
|
|
warn("pmc_stop kernel");
|
|
|
|
if (ret0 < 0) {
|
|
errno = errn0;
|
|
errx(6, "pmc_read");
|
|
}
|
|
if (ret1 < 0) {
|
|
errno = errn1;
|
|
errx(7, "pmc_read");
|
|
}
|
|
|
|
printf("%s: user %llu kernel %llu\n", event, pr0.val, pr1.val);
|
|
|
|
return 0;
|
|
}
|