perf/core improvements and fixes:
. Add missing 'finished_round' event forwarding in 'perf inject', from Adrian Hunter. . Assorted tidy ups, from Adrian Hunter. . Fall back to sysfs event names when parsing fails, from Andi Kleen. . List pmu events in perf list, from Andi Kleen. . Cleanup some memory allocation/freeing uses, from David Ahern. . Add option to collapse undesired parts of call graph, from Greg Price. . Prep work for multi perf data file storage, from Jiri Olsa. . Add support for more than two files comparision in 'perf diff', from Jiri Olsa . A few more 'perf test' improvements, from Jiri Olsa . libtraceevent cleanups, from Namhyung Kim. . Remove odd build stall in 'perf sched' by moving a large struct initialization from a local variable to a global one, from Namhyung Kim. . Add support for callchains in the gtk UI, from Namhyung Kim. . Do not apply symfs for an absolute vmlinux path, fix from Namhyung Kim. . Use default include path notation for libtraceevent, from Robert Richter. . Fix 'make tools/perf', from Robert Richter. . Make Power7 events available, from Runzhen Wang. . Add --objdump option to 'perf top', from Sukadev Bhattiprolu. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.13 (GNU/Linux) iQIcBAABAgAGBQJR6EOuAAoJENZQFvNTUqpAqjEP/1Ist/eR9be8YhljMz8Yxl1o JXktgxSkMS/n59lRibUuGZrgPKPNxivK6AEbnbZxzZoHDBS8tnAAOXUuUVTtNCoT YsQurQjCmyXHIvYqwMaYarrhoIv33LdJyshskW3GZ81UfeeC6QoC56he3VTg1dEd k8snS4F8LJpBQizRJN6s959nF+pyw16wqiGYKJ80G1nhPTsStz8NSSWdCRVbyXl9 fG0S/lLvUfilGT/ixHcvS62ENHiErL4N6jGNV4XeQqoADhrQvCwziDr+BORfJB9K udbO0PFS5uR4HOGNqZOPZfPxW8cTUXV9cCscLScKEVUghKz9rzHbPTTSDejXna/h cqLjRW1xpWUmIRY7Y5zSoBJIsh2t3vo4TkZoRNZxhCexoOT/qIUL6bWVZoxqaKzG xwL4DopOvb/DdUDkb+UB9+9kW4rDoMR1wUb6XXuGx8EqM8LHiA3TAPcGwmNh/IM6 4maUhgyOFad3rm5mcjO7IoCU7NxoWR1dKsjGYteZeZv17X30UfRFwIIH+8l2Ma4o bpBQ4DKu07jXRUZXcUajOhj7cZO+UmE6c4YoAPpv+CQ1YKVH4/YHYf6RBWdjGLqV kqOrQuSopVztmaglrh93e+cYkfvNjZAC2EOzhx+UxcPqh9zh1Pil8LE1DwjChW5m /Y/NPH98nDvXY3N4vlLK =xk/v -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: * Add missing 'finished_round' event forwarding in 'perf inject', from Adrian Hunter. * Assorted tidy ups, from Adrian Hunter. * Fall back to sysfs event names when parsing fails, from Andi Kleen. * List pmu events in perf list, from Andi Kleen. * Cleanup some memory allocation/freeing uses, from David Ahern. * Add option to collapse undesired parts of call graph, from Greg Price. * Prep work for multi perf data file storage, from Jiri Olsa. * Add support for more than two files comparision in 'perf diff', from Jiri Olsa * A few more 'perf test' improvements, from Jiri Olsa * libtraceevent cleanups, from Namhyung Kim. * Remove odd build stall in 'perf sched' by moving a large struct initialization from a local variable to a global one, from Namhyung Kim. * Add support for callchains in the gtk UI, from Namhyung Kim. * Do not apply symfs for an absolute vmlinux path, fix from Namhyung Kim. * Use default include path notation for libtraceevent, from Robert Richter. * Fix 'make tools/perf', from Robert Richter. * Make Power7 events available, from Runzhen Wang. * Add --objdump option to 'perf top', from Sukadev Bhattiprolu. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
5a9821321e
|
@ -142,11 +142,11 @@ extern ssize_t power_events_sysfs_show(struct device *dev,
|
|||
#define EVENT_PTR(_id, _suffix) &EVENT_VAR(_id, _suffix).attr.attr
|
||||
|
||||
#define EVENT_ATTR(_name, _id, _suffix) \
|
||||
PMU_EVENT_ATTR(_name, EVENT_VAR(_id, _suffix), PME_PM_##_id, \
|
||||
PMU_EVENT_ATTR(_name, EVENT_VAR(_id, _suffix), PME_##_id, \
|
||||
power_events_sysfs_show)
|
||||
|
||||
#define GENERIC_EVENT_ATTR(_name, _id) EVENT_ATTR(_name, _id, _g)
|
||||
#define GENERIC_EVENT_PTR(_id) EVENT_PTR(_id, _g)
|
||||
|
||||
#define POWER_EVENT_ATTR(_name, _id) EVENT_ATTR(PM_##_name, _id, _p)
|
||||
#define POWER_EVENT_ATTR(_name, _id) EVENT_ATTR(_name, _id, _p)
|
||||
#define POWER_EVENT_PTR(_id) EVENT_PTR(_id, _p)
|
||||
|
|
|
@ -0,0 +1,548 @@
|
|||
/*
|
||||
* Performance counter support for POWER7 processors.
|
||||
*
|
||||
* Copyright 2013 Runzhen Wang, IBM Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
EVENT(PM_IC_DEMAND_L2_BR_ALL, 0x04898)
|
||||
EVENT(PM_GCT_UTIL_7_TO_10_SLOTS, 0x020a0)
|
||||
EVENT(PM_PMC2_SAVED, 0x10022)
|
||||
EVENT(PM_CMPLU_STALL_DFU, 0x2003c)
|
||||
EVENT(PM_VSU0_16FLOP, 0x0a0a4)
|
||||
EVENT(PM_MRK_LSU_DERAT_MISS, 0x3d05a)
|
||||
EVENT(PM_MRK_ST_CMPL, 0x10034)
|
||||
EVENT(PM_NEST_PAIR3_ADD, 0x40881)
|
||||
EVENT(PM_L2_ST_DISP, 0x46180)
|
||||
EVENT(PM_L2_CASTOUT_MOD, 0x16180)
|
||||
EVENT(PM_ISEG, 0x020a4)
|
||||
EVENT(PM_MRK_INST_TIMEO, 0x40034)
|
||||
EVENT(PM_L2_RCST_DISP_FAIL_ADDR, 0x36282)
|
||||
EVENT(PM_LSU1_DC_PREF_STREAM_CONFIRM, 0x0d0b6)
|
||||
EVENT(PM_IERAT_WR_64K, 0x040be)
|
||||
EVENT(PM_MRK_DTLB_MISS_16M, 0x4d05e)
|
||||
EVENT(PM_IERAT_MISS, 0x100f6)
|
||||
EVENT(PM_MRK_PTEG_FROM_LMEM, 0x4d052)
|
||||
EVENT(PM_FLOP, 0x100f4)
|
||||
EVENT(PM_THRD_PRIO_4_5_CYC, 0x040b4)
|
||||
EVENT(PM_BR_PRED_TA, 0x040aa)
|
||||
EVENT(PM_CMPLU_STALL_FXU, 0x20014)
|
||||
EVENT(PM_EXT_INT, 0x200f8)
|
||||
EVENT(PM_VSU_FSQRT_FDIV, 0x0a888)
|
||||
EVENT(PM_MRK_LD_MISS_EXPOSED_CYC, 0x1003e)
|
||||
EVENT(PM_LSU1_LDF, 0x0c086)
|
||||
EVENT(PM_IC_WRITE_ALL, 0x0488c)
|
||||
EVENT(PM_LSU0_SRQ_STFWD, 0x0c0a0)
|
||||
EVENT(PM_PTEG_FROM_RL2L3_MOD, 0x1c052)
|
||||
EVENT(PM_MRK_DATA_FROM_L31_SHR, 0x1d04e)
|
||||
EVENT(PM_DATA_FROM_L21_MOD, 0x3c046)
|
||||
EVENT(PM_VSU1_SCAL_DOUBLE_ISSUED, 0x0b08a)
|
||||
EVENT(PM_VSU0_8FLOP, 0x0a0a0)
|
||||
EVENT(PM_POWER_EVENT1, 0x1006e)
|
||||
EVENT(PM_DISP_CLB_HELD_BAL, 0x02092)
|
||||
EVENT(PM_VSU1_2FLOP, 0x0a09a)
|
||||
EVENT(PM_LWSYNC_HELD, 0x0209a)
|
||||
EVENT(PM_PTEG_FROM_DL2L3_SHR, 0x3c054)
|
||||
EVENT(PM_INST_FROM_L21_MOD, 0x34046)
|
||||
EVENT(PM_IERAT_XLATE_WR_16MPLUS, 0x040bc)
|
||||
EVENT(PM_IC_REQ_ALL, 0x04888)
|
||||
EVENT(PM_DSLB_MISS, 0x0d090)
|
||||
EVENT(PM_L3_MISS, 0x1f082)
|
||||
EVENT(PM_LSU0_L1_PREF, 0x0d0b8)
|
||||
EVENT(PM_VSU_SCALAR_SINGLE_ISSUED, 0x0b884)
|
||||
EVENT(PM_LSU1_DC_PREF_STREAM_CONFIRM_STRIDE, 0x0d0be)
|
||||
EVENT(PM_L2_INST, 0x36080)
|
||||
EVENT(PM_VSU0_FRSP, 0x0a0b4)
|
||||
EVENT(PM_FLUSH_DISP, 0x02082)
|
||||
EVENT(PM_PTEG_FROM_L2MISS, 0x4c058)
|
||||
EVENT(PM_VSU1_DQ_ISSUED, 0x0b09a)
|
||||
EVENT(PM_CMPLU_STALL_LSU, 0x20012)
|
||||
EVENT(PM_MRK_DATA_FROM_DMEM, 0x1d04a)
|
||||
EVENT(PM_LSU_FLUSH_ULD, 0x0c8b0)
|
||||
EVENT(PM_PTEG_FROM_LMEM, 0x4c052)
|
||||
EVENT(PM_MRK_DERAT_MISS_16M, 0x3d05c)
|
||||
EVENT(PM_THRD_ALL_RUN_CYC, 0x2000c)
|
||||
EVENT(PM_MEM0_PREFETCH_DISP, 0x20083)
|
||||
EVENT(PM_MRK_STALL_CMPLU_CYC_COUNT, 0x3003f)
|
||||
EVENT(PM_DATA_FROM_DL2L3_MOD, 0x3c04c)
|
||||
EVENT(PM_VSU_FRSP, 0x0a8b4)
|
||||
EVENT(PM_MRK_DATA_FROM_L21_MOD, 0x3d046)
|
||||
EVENT(PM_PMC1_OVERFLOW, 0x20010)
|
||||
EVENT(PM_VSU0_SINGLE, 0x0a0a8)
|
||||
EVENT(PM_MRK_PTEG_FROM_L3MISS, 0x2d058)
|
||||
EVENT(PM_MRK_PTEG_FROM_L31_SHR, 0x2d056)
|
||||
EVENT(PM_VSU0_VECTOR_SP_ISSUED, 0x0b090)
|
||||
EVENT(PM_VSU1_FEST, 0x0a0ba)
|
||||
EVENT(PM_MRK_INST_DISP, 0x20030)
|
||||
EVENT(PM_VSU0_COMPLEX_ISSUED, 0x0b096)
|
||||
EVENT(PM_LSU1_FLUSH_UST, 0x0c0b6)
|
||||
EVENT(PM_INST_CMPL, 0x00002)
|
||||
EVENT(PM_FXU_IDLE, 0x1000e)
|
||||
EVENT(PM_LSU0_FLUSH_ULD, 0x0c0b0)
|
||||
EVENT(PM_MRK_DATA_FROM_DL2L3_MOD, 0x3d04c)
|
||||
EVENT(PM_LSU_LMQ_SRQ_EMPTY_ALL_CYC, 0x3001c)
|
||||
EVENT(PM_LSU1_REJECT_LMQ_FULL, 0x0c0a6)
|
||||
EVENT(PM_INST_PTEG_FROM_L21_MOD, 0x3e056)
|
||||
EVENT(PM_INST_FROM_RL2L3_MOD, 0x14042)
|
||||
EVENT(PM_SHL_CREATED, 0x05082)
|
||||
EVENT(PM_L2_ST_HIT, 0x46182)
|
||||
EVENT(PM_DATA_FROM_DMEM, 0x1c04a)
|
||||
EVENT(PM_L3_LD_MISS, 0x2f082)
|
||||
EVENT(PM_FXU1_BUSY_FXU0_IDLE, 0x4000e)
|
||||
EVENT(PM_DISP_CLB_HELD_RES, 0x02094)
|
||||
EVENT(PM_L2_SN_SX_I_DONE, 0x36382)
|
||||
EVENT(PM_GRP_CMPL, 0x30004)
|
||||
EVENT(PM_STCX_CMPL, 0x0c098)
|
||||
EVENT(PM_VSU0_2FLOP, 0x0a098)
|
||||
EVENT(PM_L3_PREF_MISS, 0x3f082)
|
||||
EVENT(PM_LSU_SRQ_SYNC_CYC, 0x0d096)
|
||||
EVENT(PM_LSU_REJECT_ERAT_MISS, 0x20064)
|
||||
EVENT(PM_L1_ICACHE_MISS, 0x200fc)
|
||||
EVENT(PM_LSU1_FLUSH_SRQ, 0x0c0be)
|
||||
EVENT(PM_LD_REF_L1_LSU0, 0x0c080)
|
||||
EVENT(PM_VSU0_FEST, 0x0a0b8)
|
||||
EVENT(PM_VSU_VECTOR_SINGLE_ISSUED, 0x0b890)
|
||||
EVENT(PM_FREQ_UP, 0x4000c)
|
||||
EVENT(PM_DATA_FROM_LMEM, 0x3c04a)
|
||||
EVENT(PM_LSU1_LDX, 0x0c08a)
|
||||
EVENT(PM_PMC3_OVERFLOW, 0x40010)
|
||||
EVENT(PM_MRK_BR_MPRED, 0x30036)
|
||||
EVENT(PM_SHL_MATCH, 0x05086)
|
||||
EVENT(PM_MRK_BR_TAKEN, 0x10036)
|
||||
EVENT(PM_CMPLU_STALL_BRU, 0x4004e)
|
||||
EVENT(PM_ISLB_MISS, 0x0d092)
|
||||
EVENT(PM_CYC, 0x0001e)
|
||||
EVENT(PM_DISP_HELD_THERMAL, 0x30006)
|
||||
EVENT(PM_INST_PTEG_FROM_RL2L3_SHR, 0x2e054)
|
||||
EVENT(PM_LSU1_SRQ_STFWD, 0x0c0a2)
|
||||
EVENT(PM_GCT_NOSLOT_BR_MPRED, 0x4001a)
|
||||
EVENT(PM_1PLUS_PPC_CMPL, 0x100f2)
|
||||
EVENT(PM_PTEG_FROM_DMEM, 0x2c052)
|
||||
EVENT(PM_VSU_2FLOP, 0x0a898)
|
||||
EVENT(PM_GCT_FULL_CYC, 0x04086)
|
||||
EVENT(PM_MRK_DATA_FROM_L3_CYC, 0x40020)
|
||||
EVENT(PM_LSU_SRQ_S0_ALLOC, 0x0d09d)
|
||||
EVENT(PM_MRK_DERAT_MISS_4K, 0x1d05c)
|
||||
EVENT(PM_BR_MPRED_TA, 0x040ae)
|
||||
EVENT(PM_INST_PTEG_FROM_L2MISS, 0x4e058)
|
||||
EVENT(PM_DPU_HELD_POWER, 0x20006)
|
||||
EVENT(PM_RUN_INST_CMPL, 0x400fa)
|
||||
EVENT(PM_MRK_VSU_FIN, 0x30032)
|
||||
EVENT(PM_LSU_SRQ_S0_VALID, 0x0d09c)
|
||||
EVENT(PM_GCT_EMPTY_CYC, 0x20008)
|
||||
EVENT(PM_IOPS_DISP, 0x30014)
|
||||
EVENT(PM_RUN_SPURR, 0x10008)
|
||||
EVENT(PM_PTEG_FROM_L21_MOD, 0x3c056)
|
||||
EVENT(PM_VSU0_1FLOP, 0x0a080)
|
||||
EVENT(PM_SNOOP_TLBIE, 0x0d0b2)
|
||||
EVENT(PM_DATA_FROM_L3MISS, 0x2c048)
|
||||
EVENT(PM_VSU_SINGLE, 0x0a8a8)
|
||||
EVENT(PM_DTLB_MISS_16G, 0x1c05e)
|
||||
EVENT(PM_CMPLU_STALL_VECTOR, 0x2001c)
|
||||
EVENT(PM_FLUSH, 0x400f8)
|
||||
EVENT(PM_L2_LD_HIT, 0x36182)
|
||||
EVENT(PM_NEST_PAIR2_AND, 0x30883)
|
||||
EVENT(PM_VSU1_1FLOP, 0x0a082)
|
||||
EVENT(PM_IC_PREF_REQ, 0x0408a)
|
||||
EVENT(PM_L3_LD_HIT, 0x2f080)
|
||||
EVENT(PM_GCT_NOSLOT_IC_MISS, 0x2001a)
|
||||
EVENT(PM_DISP_HELD, 0x10006)
|
||||
EVENT(PM_L2_LD, 0x16080)
|
||||
EVENT(PM_LSU_FLUSH_SRQ, 0x0c8bc)
|
||||
EVENT(PM_BC_PLUS_8_CONV, 0x040b8)
|
||||
EVENT(PM_MRK_DATA_FROM_L31_MOD_CYC, 0x40026)
|
||||
EVENT(PM_CMPLU_STALL_VECTOR_LONG, 0x4004a)
|
||||
EVENT(PM_L2_RCST_BUSY_RC_FULL, 0x26282)
|
||||
EVENT(PM_TB_BIT_TRANS, 0x300f8)
|
||||
EVENT(PM_THERMAL_MAX, 0x40006)
|
||||
EVENT(PM_LSU1_FLUSH_ULD, 0x0c0b2)
|
||||
EVENT(PM_LSU1_REJECT_LHS, 0x0c0ae)
|
||||
EVENT(PM_LSU_LRQ_S0_ALLOC, 0x0d09f)
|
||||
EVENT(PM_L3_CO_L31, 0x4f080)
|
||||
EVENT(PM_POWER_EVENT4, 0x4006e)
|
||||
EVENT(PM_DATA_FROM_L31_SHR, 0x1c04e)
|
||||
EVENT(PM_BR_UNCOND, 0x0409e)
|
||||
EVENT(PM_LSU1_DC_PREF_STREAM_ALLOC, 0x0d0aa)
|
||||
EVENT(PM_PMC4_REWIND, 0x10020)
|
||||
EVENT(PM_L2_RCLD_DISP, 0x16280)
|
||||
EVENT(PM_THRD_PRIO_2_3_CYC, 0x040b2)
|
||||
EVENT(PM_MRK_PTEG_FROM_L2MISS, 0x4d058)
|
||||
EVENT(PM_IC_DEMAND_L2_BHT_REDIRECT, 0x04098)
|
||||
EVENT(PM_LSU_DERAT_MISS, 0x200f6)
|
||||
EVENT(PM_IC_PREF_CANCEL_L2, 0x04094)
|
||||
EVENT(PM_MRK_FIN_STALL_CYC_COUNT, 0x1003d)
|
||||
EVENT(PM_BR_PRED_CCACHE, 0x040a0)
|
||||
EVENT(PM_GCT_UTIL_1_TO_2_SLOTS, 0x0209c)
|
||||
EVENT(PM_MRK_ST_CMPL_INT, 0x30034)
|
||||
EVENT(PM_LSU_TWO_TABLEWALK_CYC, 0x0d0a6)
|
||||
EVENT(PM_MRK_DATA_FROM_L3MISS, 0x2d048)
|
||||
EVENT(PM_GCT_NOSLOT_CYC, 0x100f8)
|
||||
EVENT(PM_LSU_SET_MPRED, 0x0c0a8)
|
||||
EVENT(PM_FLUSH_DISP_TLBIE, 0x0208a)
|
||||
EVENT(PM_VSU1_FCONV, 0x0a0b2)
|
||||
EVENT(PM_DERAT_MISS_16G, 0x4c05c)
|
||||
EVENT(PM_INST_FROM_LMEM, 0x3404a)
|
||||
EVENT(PM_IC_DEMAND_L2_BR_REDIRECT, 0x0409a)
|
||||
EVENT(PM_CMPLU_STALL_SCALAR_LONG, 0x20018)
|
||||
EVENT(PM_INST_PTEG_FROM_L2, 0x1e050)
|
||||
EVENT(PM_PTEG_FROM_L2, 0x1c050)
|
||||
EVENT(PM_MRK_DATA_FROM_L21_SHR_CYC, 0x20024)
|
||||
EVENT(PM_MRK_DTLB_MISS_4K, 0x2d05a)
|
||||
EVENT(PM_VSU0_FPSCR, 0x0b09c)
|
||||
EVENT(PM_VSU1_VECT_DOUBLE_ISSUED, 0x0b082)
|
||||
EVENT(PM_MRK_PTEG_FROM_RL2L3_MOD, 0x1d052)
|
||||
EVENT(PM_MEM0_RQ_DISP, 0x10083)
|
||||
EVENT(PM_L2_LD_MISS, 0x26080)
|
||||
EVENT(PM_VMX_RESULT_SAT_1, 0x0b0a0)
|
||||
EVENT(PM_L1_PREF, 0x0d8b8)
|
||||
EVENT(PM_MRK_DATA_FROM_LMEM_CYC, 0x2002c)
|
||||
EVENT(PM_GRP_IC_MISS_NONSPEC, 0x1000c)
|
||||
EVENT(PM_PB_NODE_PUMP, 0x10081)
|
||||
EVENT(PM_SHL_MERGED, 0x05084)
|
||||
EVENT(PM_NEST_PAIR1_ADD, 0x20881)
|
||||
EVENT(PM_DATA_FROM_L3, 0x1c048)
|
||||
EVENT(PM_LSU_FLUSH, 0x0208e)
|
||||
EVENT(PM_LSU_SRQ_SYNC_COUNT, 0x0d097)
|
||||
EVENT(PM_PMC2_OVERFLOW, 0x30010)
|
||||
EVENT(PM_LSU_LDF, 0x0c884)
|
||||
EVENT(PM_POWER_EVENT3, 0x3006e)
|
||||
EVENT(PM_DISP_WT, 0x30008)
|
||||
EVENT(PM_CMPLU_STALL_REJECT, 0x40016)
|
||||
EVENT(PM_IC_BANK_CONFLICT, 0x04082)
|
||||
EVENT(PM_BR_MPRED_CR_TA, 0x048ae)
|
||||
EVENT(PM_L2_INST_MISS, 0x36082)
|
||||
EVENT(PM_CMPLU_STALL_ERAT_MISS, 0x40018)
|
||||
EVENT(PM_NEST_PAIR2_ADD, 0x30881)
|
||||
EVENT(PM_MRK_LSU_FLUSH, 0x0d08c)
|
||||
EVENT(PM_L2_LDST, 0x16880)
|
||||
EVENT(PM_INST_FROM_L31_SHR, 0x1404e)
|
||||
EVENT(PM_VSU0_FIN, 0x0a0bc)
|
||||
EVENT(PM_LARX_LSU, 0x0c894)
|
||||
EVENT(PM_INST_FROM_RMEM, 0x34042)
|
||||
EVENT(PM_DISP_CLB_HELD_TLBIE, 0x02096)
|
||||
EVENT(PM_MRK_DATA_FROM_DMEM_CYC, 0x2002e)
|
||||
EVENT(PM_BR_PRED_CR, 0x040a8)
|
||||
EVENT(PM_LSU_REJECT, 0x10064)
|
||||
EVENT(PM_GCT_UTIL_3_TO_6_SLOTS, 0x0209e)
|
||||
EVENT(PM_CMPLU_STALL_END_GCT_NOSLOT, 0x10028)
|
||||
EVENT(PM_LSU0_REJECT_LMQ_FULL, 0x0c0a4)
|
||||
EVENT(PM_VSU_FEST, 0x0a8b8)
|
||||
EVENT(PM_NEST_PAIR0_AND, 0x10883)
|
||||
EVENT(PM_PTEG_FROM_L3, 0x2c050)
|
||||
EVENT(PM_POWER_EVENT2, 0x2006e)
|
||||
EVENT(PM_IC_PREF_CANCEL_PAGE, 0x04090)
|
||||
EVENT(PM_VSU0_FSQRT_FDIV, 0x0a088)
|
||||
EVENT(PM_MRK_GRP_CMPL, 0x40030)
|
||||
EVENT(PM_VSU0_SCAL_DOUBLE_ISSUED, 0x0b088)
|
||||
EVENT(PM_GRP_DISP, 0x3000a)
|
||||
EVENT(PM_LSU0_LDX, 0x0c088)
|
||||
EVENT(PM_DATA_FROM_L2, 0x1c040)
|
||||
EVENT(PM_MRK_DATA_FROM_RL2L3_MOD, 0x1d042)
|
||||
EVENT(PM_LD_REF_L1, 0x0c880)
|
||||
EVENT(PM_VSU0_VECT_DOUBLE_ISSUED, 0x0b080)
|
||||
EVENT(PM_VSU1_2FLOP_DOUBLE, 0x0a08e)
|
||||
EVENT(PM_THRD_PRIO_6_7_CYC, 0x040b6)
|
||||
EVENT(PM_BC_PLUS_8_RSLV_TAKEN, 0x040ba)
|
||||
EVENT(PM_BR_MPRED_CR, 0x040ac)
|
||||
EVENT(PM_L3_CO_MEM, 0x4f082)
|
||||
EVENT(PM_LD_MISS_L1, 0x400f0)
|
||||
EVENT(PM_DATA_FROM_RL2L3_MOD, 0x1c042)
|
||||
EVENT(PM_LSU_SRQ_FULL_CYC, 0x1001a)
|
||||
EVENT(PM_TABLEWALK_CYC, 0x10026)
|
||||
EVENT(PM_MRK_PTEG_FROM_RMEM, 0x3d052)
|
||||
EVENT(PM_LSU_SRQ_STFWD, 0x0c8a0)
|
||||
EVENT(PM_INST_PTEG_FROM_RMEM, 0x3e052)
|
||||
EVENT(PM_FXU0_FIN, 0x10004)
|
||||
EVENT(PM_LSU1_L1_SW_PREF, 0x0c09e)
|
||||
EVENT(PM_PTEG_FROM_L31_MOD, 0x1c054)
|
||||
EVENT(PM_PMC5_OVERFLOW, 0x10024)
|
||||
EVENT(PM_LD_REF_L1_LSU1, 0x0c082)
|
||||
EVENT(PM_INST_PTEG_FROM_L21_SHR, 0x4e056)
|
||||
EVENT(PM_CMPLU_STALL_THRD, 0x1001c)
|
||||
EVENT(PM_DATA_FROM_RMEM, 0x3c042)
|
||||
EVENT(PM_VSU0_SCAL_SINGLE_ISSUED, 0x0b084)
|
||||
EVENT(PM_BR_MPRED_LSTACK, 0x040a6)
|
||||
EVENT(PM_MRK_DATA_FROM_RL2L3_MOD_CYC, 0x40028)
|
||||
EVENT(PM_LSU0_FLUSH_UST, 0x0c0b4)
|
||||
EVENT(PM_LSU_NCST, 0x0c090)
|
||||
EVENT(PM_BR_TAKEN, 0x20004)
|
||||
EVENT(PM_INST_PTEG_FROM_LMEM, 0x4e052)
|
||||
EVENT(PM_GCT_NOSLOT_BR_MPRED_IC_MISS, 0x4001c)
|
||||
EVENT(PM_DTLB_MISS_4K, 0x2c05a)
|
||||
EVENT(PM_PMC4_SAVED, 0x30022)
|
||||
EVENT(PM_VSU1_PERMUTE_ISSUED, 0x0b092)
|
||||
EVENT(PM_SLB_MISS, 0x0d890)
|
||||
EVENT(PM_LSU1_FLUSH_LRQ, 0x0c0ba)
|
||||
EVENT(PM_DTLB_MISS, 0x300fc)
|
||||
EVENT(PM_VSU1_FRSP, 0x0a0b6)
|
||||
EVENT(PM_VSU_VECTOR_DOUBLE_ISSUED, 0x0b880)
|
||||
EVENT(PM_L2_CASTOUT_SHR, 0x16182)
|
||||
EVENT(PM_DATA_FROM_DL2L3_SHR, 0x3c044)
|
||||
EVENT(PM_VSU1_STF, 0x0b08e)
|
||||
EVENT(PM_ST_FIN, 0x200f0)
|
||||
EVENT(PM_PTEG_FROM_L21_SHR, 0x4c056)
|
||||
EVENT(PM_L2_LOC_GUESS_WRONG, 0x26480)
|
||||
EVENT(PM_MRK_STCX_FAIL, 0x0d08e)
|
||||
EVENT(PM_LSU0_REJECT_LHS, 0x0c0ac)
|
||||
EVENT(PM_IC_PREF_CANCEL_HIT, 0x04092)
|
||||
EVENT(PM_L3_PREF_BUSY, 0x4f080)
|
||||
EVENT(PM_MRK_BRU_FIN, 0x2003a)
|
||||
EVENT(PM_LSU1_NCLD, 0x0c08e)
|
||||
EVENT(PM_INST_PTEG_FROM_L31_MOD, 0x1e054)
|
||||
EVENT(PM_LSU_NCLD, 0x0c88c)
|
||||
EVENT(PM_LSU_LDX, 0x0c888)
|
||||
EVENT(PM_L2_LOC_GUESS_CORRECT, 0x16480)
|
||||
EVENT(PM_THRESH_TIMEO, 0x10038)
|
||||
EVENT(PM_L3_PREF_ST, 0x0d0ae)
|
||||
EVENT(PM_DISP_CLB_HELD_SYNC, 0x02098)
|
||||
EVENT(PM_VSU_SIMPLE_ISSUED, 0x0b894)
|
||||
EVENT(PM_VSU1_SINGLE, 0x0a0aa)
|
||||
EVENT(PM_DATA_TABLEWALK_CYC, 0x3001a)
|
||||
EVENT(PM_L2_RC_ST_DONE, 0x36380)
|
||||
EVENT(PM_MRK_PTEG_FROM_L21_MOD, 0x3d056)
|
||||
EVENT(PM_LARX_LSU1, 0x0c096)
|
||||
EVENT(PM_MRK_DATA_FROM_RMEM, 0x3d042)
|
||||
EVENT(PM_DISP_CLB_HELD, 0x02090)
|
||||
EVENT(PM_DERAT_MISS_4K, 0x1c05c)
|
||||
EVENT(PM_L2_RCLD_DISP_FAIL_ADDR, 0x16282)
|
||||
EVENT(PM_SEG_EXCEPTION, 0x028a4)
|
||||
EVENT(PM_FLUSH_DISP_SB, 0x0208c)
|
||||
EVENT(PM_L2_DC_INV, 0x26182)
|
||||
EVENT(PM_PTEG_FROM_DL2L3_MOD, 0x4c054)
|
||||
EVENT(PM_DSEG, 0x020a6)
|
||||
EVENT(PM_BR_PRED_LSTACK, 0x040a2)
|
||||
EVENT(PM_VSU0_STF, 0x0b08c)
|
||||
EVENT(PM_LSU_FX_FIN, 0x10066)
|
||||
EVENT(PM_DERAT_MISS_16M, 0x3c05c)
|
||||
EVENT(PM_MRK_PTEG_FROM_DL2L3_MOD, 0x4d054)
|
||||
EVENT(PM_GCT_UTIL_11_PLUS_SLOTS, 0x020a2)
|
||||
EVENT(PM_INST_FROM_L3, 0x14048)
|
||||
EVENT(PM_MRK_IFU_FIN, 0x3003a)
|
||||
EVENT(PM_ITLB_MISS, 0x400fc)
|
||||
EVENT(PM_VSU_STF, 0x0b88c)
|
||||
EVENT(PM_LSU_FLUSH_UST, 0x0c8b4)
|
||||
EVENT(PM_L2_LDST_MISS, 0x26880)
|
||||
EVENT(PM_FXU1_FIN, 0x40004)
|
||||
EVENT(PM_SHL_DEALLOCATED, 0x05080)
|
||||
EVENT(PM_L2_SN_M_WR_DONE, 0x46382)
|
||||
EVENT(PM_LSU_REJECT_SET_MPRED, 0x0c8a8)
|
||||
EVENT(PM_L3_PREF_LD, 0x0d0ac)
|
||||
EVENT(PM_L2_SN_M_RD_DONE, 0x46380)
|
||||
EVENT(PM_MRK_DERAT_MISS_16G, 0x4d05c)
|
||||
EVENT(PM_VSU_FCONV, 0x0a8b0)
|
||||
EVENT(PM_ANY_THRD_RUN_CYC, 0x100fa)
|
||||
EVENT(PM_LSU_LMQ_FULL_CYC, 0x0d0a4)
|
||||
EVENT(PM_MRK_LSU_REJECT_LHS, 0x0d082)
|
||||
EVENT(PM_MRK_LD_MISS_L1_CYC, 0x4003e)
|
||||
EVENT(PM_MRK_DATA_FROM_L2_CYC, 0x20020)
|
||||
EVENT(PM_INST_IMC_MATCH_DISP, 0x30016)
|
||||
EVENT(PM_MRK_DATA_FROM_RMEM_CYC, 0x4002c)
|
||||
EVENT(PM_VSU0_SIMPLE_ISSUED, 0x0b094)
|
||||
EVENT(PM_CMPLU_STALL_DIV, 0x40014)
|
||||
EVENT(PM_MRK_PTEG_FROM_RL2L3_SHR, 0x2d054)
|
||||
EVENT(PM_VSU_FMA_DOUBLE, 0x0a890)
|
||||
EVENT(PM_VSU_4FLOP, 0x0a89c)
|
||||
EVENT(PM_VSU1_FIN, 0x0a0be)
|
||||
EVENT(PM_NEST_PAIR1_AND, 0x20883)
|
||||
EVENT(PM_INST_PTEG_FROM_RL2L3_MOD, 0x1e052)
|
||||
EVENT(PM_RUN_CYC, 0x200f4)
|
||||
EVENT(PM_PTEG_FROM_RMEM, 0x3c052)
|
||||
EVENT(PM_LSU_LRQ_S0_VALID, 0x0d09e)
|
||||
EVENT(PM_LSU0_LDF, 0x0c084)
|
||||
EVENT(PM_FLUSH_COMPLETION, 0x30012)
|
||||
EVENT(PM_ST_MISS_L1, 0x300f0)
|
||||
EVENT(PM_L2_NODE_PUMP, 0x36480)
|
||||
EVENT(PM_INST_FROM_DL2L3_SHR, 0x34044)
|
||||
EVENT(PM_MRK_STALL_CMPLU_CYC, 0x3003e)
|
||||
EVENT(PM_VSU1_DENORM, 0x0a0ae)
|
||||
EVENT(PM_MRK_DATA_FROM_L31_SHR_CYC, 0x20026)
|
||||
EVENT(PM_NEST_PAIR0_ADD, 0x10881)
|
||||
EVENT(PM_INST_FROM_L3MISS, 0x24048)
|
||||
EVENT(PM_EE_OFF_EXT_INT, 0x02080)
|
||||
EVENT(PM_INST_PTEG_FROM_DMEM, 0x2e052)
|
||||
EVENT(PM_INST_FROM_DL2L3_MOD, 0x3404c)
|
||||
EVENT(PM_PMC6_OVERFLOW, 0x30024)
|
||||
EVENT(PM_VSU_2FLOP_DOUBLE, 0x0a88c)
|
||||
EVENT(PM_TLB_MISS, 0x20066)
|
||||
EVENT(PM_FXU_BUSY, 0x2000e)
|
||||
EVENT(PM_L2_RCLD_DISP_FAIL_OTHER, 0x26280)
|
||||
EVENT(PM_LSU_REJECT_LMQ_FULL, 0x0c8a4)
|
||||
EVENT(PM_IC_RELOAD_SHR, 0x04096)
|
||||
EVENT(PM_GRP_MRK, 0x10031)
|
||||
EVENT(PM_MRK_ST_NEST, 0x20034)
|
||||
EVENT(PM_VSU1_FSQRT_FDIV, 0x0a08a)
|
||||
EVENT(PM_LSU0_FLUSH_LRQ, 0x0c0b8)
|
||||
EVENT(PM_LARX_LSU0, 0x0c094)
|
||||
EVENT(PM_IBUF_FULL_CYC, 0x04084)
|
||||
EVENT(PM_MRK_DATA_FROM_DL2L3_SHR_CYC, 0x2002a)
|
||||
EVENT(PM_LSU_DC_PREF_STREAM_ALLOC, 0x0d8a8)
|
||||
EVENT(PM_GRP_MRK_CYC, 0x10030)
|
||||
EVENT(PM_MRK_DATA_FROM_RL2L3_SHR_CYC, 0x20028)
|
||||
EVENT(PM_L2_GLOB_GUESS_CORRECT, 0x16482)
|
||||
EVENT(PM_LSU_REJECT_LHS, 0x0c8ac)
|
||||
EVENT(PM_MRK_DATA_FROM_LMEM, 0x3d04a)
|
||||
EVENT(PM_INST_PTEG_FROM_L3, 0x2e050)
|
||||
EVENT(PM_FREQ_DOWN, 0x3000c)
|
||||
EVENT(PM_PB_RETRY_NODE_PUMP, 0x30081)
|
||||
EVENT(PM_INST_FROM_RL2L3_SHR, 0x1404c)
|
||||
EVENT(PM_MRK_INST_ISSUED, 0x10032)
|
||||
EVENT(PM_PTEG_FROM_L3MISS, 0x2c058)
|
||||
EVENT(PM_RUN_PURR, 0x400f4)
|
||||
EVENT(PM_MRK_GRP_IC_MISS, 0x40038)
|
||||
EVENT(PM_MRK_DATA_FROM_L3, 0x1d048)
|
||||
EVENT(PM_CMPLU_STALL_DCACHE_MISS, 0x20016)
|
||||
EVENT(PM_PTEG_FROM_RL2L3_SHR, 0x2c054)
|
||||
EVENT(PM_LSU_FLUSH_LRQ, 0x0c8b8)
|
||||
EVENT(PM_MRK_DERAT_MISS_64K, 0x2d05c)
|
||||
EVENT(PM_INST_PTEG_FROM_DL2L3_MOD, 0x4e054)
|
||||
EVENT(PM_L2_ST_MISS, 0x26082)
|
||||
EVENT(PM_MRK_PTEG_FROM_L21_SHR, 0x4d056)
|
||||
EVENT(PM_LWSYNC, 0x0d094)
|
||||
EVENT(PM_LSU0_DC_PREF_STREAM_CONFIRM_STRIDE, 0x0d0bc)
|
||||
EVENT(PM_MRK_LSU_FLUSH_LRQ, 0x0d088)
|
||||
EVENT(PM_INST_IMC_MATCH_CMPL, 0x100f0)
|
||||
EVENT(PM_NEST_PAIR3_AND, 0x40883)
|
||||
EVENT(PM_PB_RETRY_SYS_PUMP, 0x40081)
|
||||
EVENT(PM_MRK_INST_FIN, 0x30030)
|
||||
EVENT(PM_MRK_PTEG_FROM_DL2L3_SHR, 0x3d054)
|
||||
EVENT(PM_INST_FROM_L31_MOD, 0x14044)
|
||||
EVENT(PM_MRK_DTLB_MISS_64K, 0x3d05e)
|
||||
EVENT(PM_LSU_FIN, 0x30066)
|
||||
EVENT(PM_MRK_LSU_REJECT, 0x40064)
|
||||
EVENT(PM_L2_CO_FAIL_BUSY, 0x16382)
|
||||
EVENT(PM_MEM0_WQ_DISP, 0x40083)
|
||||
EVENT(PM_DATA_FROM_L31_MOD, 0x1c044)
|
||||
EVENT(PM_THERMAL_WARN, 0x10016)
|
||||
EVENT(PM_VSU0_4FLOP, 0x0a09c)
|
||||
EVENT(PM_BR_MPRED_CCACHE, 0x040a4)
|
||||
EVENT(PM_CMPLU_STALL_IFU, 0x4004c)
|
||||
EVENT(PM_L1_DEMAND_WRITE, 0x0408c)
|
||||
EVENT(PM_FLUSH_BR_MPRED, 0x02084)
|
||||
EVENT(PM_MRK_DTLB_MISS_16G, 0x1d05e)
|
||||
EVENT(PM_MRK_PTEG_FROM_DMEM, 0x2d052)
|
||||
EVENT(PM_L2_RCST_DISP, 0x36280)
|
||||
EVENT(PM_CMPLU_STALL, 0x4000a)
|
||||
EVENT(PM_LSU_PARTIAL_CDF, 0x0c0aa)
|
||||
EVENT(PM_DISP_CLB_HELD_SB, 0x020a8)
|
||||
EVENT(PM_VSU0_FMA_DOUBLE, 0x0a090)
|
||||
EVENT(PM_FXU0_BUSY_FXU1_IDLE, 0x3000e)
|
||||
EVENT(PM_IC_DEMAND_CYC, 0x10018)
|
||||
EVENT(PM_MRK_DATA_FROM_L21_SHR, 0x3d04e)
|
||||
EVENT(PM_MRK_LSU_FLUSH_UST, 0x0d086)
|
||||
EVENT(PM_INST_PTEG_FROM_L3MISS, 0x2e058)
|
||||
EVENT(PM_VSU_DENORM, 0x0a8ac)
|
||||
EVENT(PM_MRK_LSU_PARTIAL_CDF, 0x0d080)
|
||||
EVENT(PM_INST_FROM_L21_SHR, 0x3404e)
|
||||
EVENT(PM_IC_PREF_WRITE, 0x0408e)
|
||||
EVENT(PM_BR_PRED, 0x0409c)
|
||||
EVENT(PM_INST_FROM_DMEM, 0x1404a)
|
||||
EVENT(PM_IC_PREF_CANCEL_ALL, 0x04890)
|
||||
EVENT(PM_LSU_DC_PREF_STREAM_CONFIRM, 0x0d8b4)
|
||||
EVENT(PM_MRK_LSU_FLUSH_SRQ, 0x0d08a)
|
||||
EVENT(PM_MRK_FIN_STALL_CYC, 0x1003c)
|
||||
EVENT(PM_L2_RCST_DISP_FAIL_OTHER, 0x46280)
|
||||
EVENT(PM_VSU1_DD_ISSUED, 0x0b098)
|
||||
EVENT(PM_PTEG_FROM_L31_SHR, 0x2c056)
|
||||
EVENT(PM_DATA_FROM_L21_SHR, 0x3c04e)
|
||||
EVENT(PM_LSU0_NCLD, 0x0c08c)
|
||||
EVENT(PM_VSU1_4FLOP, 0x0a09e)
|
||||
EVENT(PM_VSU1_8FLOP, 0x0a0a2)
|
||||
EVENT(PM_VSU_8FLOP, 0x0a8a0)
|
||||
EVENT(PM_LSU_LMQ_SRQ_EMPTY_CYC, 0x2003e)
|
||||
EVENT(PM_DTLB_MISS_64K, 0x3c05e)
|
||||
EVENT(PM_THRD_CONC_RUN_INST, 0x300f4)
|
||||
EVENT(PM_MRK_PTEG_FROM_L2, 0x1d050)
|
||||
EVENT(PM_PB_SYS_PUMP, 0x20081)
|
||||
EVENT(PM_VSU_FIN, 0x0a8bc)
|
||||
EVENT(PM_MRK_DATA_FROM_L31_MOD, 0x1d044)
|
||||
EVENT(PM_THRD_PRIO_0_1_CYC, 0x040b0)
|
||||
EVENT(PM_DERAT_MISS_64K, 0x2c05c)
|
||||
EVENT(PM_PMC2_REWIND, 0x30020)
|
||||
EVENT(PM_INST_FROM_L2, 0x14040)
|
||||
EVENT(PM_GRP_BR_MPRED_NONSPEC, 0x1000a)
|
||||
EVENT(PM_INST_DISP, 0x200f2)
|
||||
EVENT(PM_MEM0_RD_CANCEL_TOTAL, 0x30083)
|
||||
EVENT(PM_LSU0_DC_PREF_STREAM_CONFIRM, 0x0d0b4)
|
||||
EVENT(PM_L1_DCACHE_RELOAD_VALID, 0x300f6)
|
||||
EVENT(PM_VSU_SCALAR_DOUBLE_ISSUED, 0x0b888)
|
||||
EVENT(PM_L3_PREF_HIT, 0x3f080)
|
||||
EVENT(PM_MRK_PTEG_FROM_L31_MOD, 0x1d054)
|
||||
EVENT(PM_CMPLU_STALL_STORE, 0x2004a)
|
||||
EVENT(PM_MRK_FXU_FIN, 0x20038)
|
||||
EVENT(PM_PMC4_OVERFLOW, 0x10010)
|
||||
EVENT(PM_MRK_PTEG_FROM_L3, 0x2d050)
|
||||
EVENT(PM_LSU0_LMQ_LHR_MERGE, 0x0d098)
|
||||
EVENT(PM_BTAC_HIT, 0x0508a)
|
||||
EVENT(PM_L3_RD_BUSY, 0x4f082)
|
||||
EVENT(PM_LSU0_L1_SW_PREF, 0x0c09c)
|
||||
EVENT(PM_INST_FROM_L2MISS, 0x44048)
|
||||
EVENT(PM_LSU0_DC_PREF_STREAM_ALLOC, 0x0d0a8)
|
||||
EVENT(PM_L2_ST, 0x16082)
|
||||
EVENT(PM_VSU0_DENORM, 0x0a0ac)
|
||||
EVENT(PM_MRK_DATA_FROM_DL2L3_SHR, 0x3d044)
|
||||
EVENT(PM_BR_PRED_CR_TA, 0x048aa)
|
||||
EVENT(PM_VSU0_FCONV, 0x0a0b0)
|
||||
EVENT(PM_MRK_LSU_FLUSH_ULD, 0x0d084)
|
||||
EVENT(PM_BTAC_MISS, 0x05088)
|
||||
EVENT(PM_MRK_LD_MISS_EXPOSED_CYC_COUNT, 0x1003f)
|
||||
EVENT(PM_MRK_DATA_FROM_L2, 0x1d040)
|
||||
EVENT(PM_LSU_DCACHE_RELOAD_VALID, 0x0d0a2)
|
||||
EVENT(PM_VSU_FMA, 0x0a884)
|
||||
EVENT(PM_LSU0_FLUSH_SRQ, 0x0c0bc)
|
||||
EVENT(PM_LSU1_L1_PREF, 0x0d0ba)
|
||||
EVENT(PM_IOPS_CMPL, 0x10014)
|
||||
EVENT(PM_L2_SYS_PUMP, 0x36482)
|
||||
EVENT(PM_L2_RCLD_BUSY_RC_FULL, 0x46282)
|
||||
EVENT(PM_LSU_LMQ_S0_ALLOC, 0x0d0a1)
|
||||
EVENT(PM_FLUSH_DISP_SYNC, 0x02088)
|
||||
EVENT(PM_MRK_DATA_FROM_DL2L3_MOD_CYC, 0x4002a)
|
||||
EVENT(PM_L2_IC_INV, 0x26180)
|
||||
EVENT(PM_MRK_DATA_FROM_L21_MOD_CYC, 0x40024)
|
||||
EVENT(PM_L3_PREF_LDST, 0x0d8ac)
|
||||
EVENT(PM_LSU_SRQ_EMPTY_CYC, 0x40008)
|
||||
EVENT(PM_LSU_LMQ_S0_VALID, 0x0d0a0)
|
||||
EVENT(PM_FLUSH_PARTIAL, 0x02086)
|
||||
EVENT(PM_VSU1_FMA_DOUBLE, 0x0a092)
|
||||
EVENT(PM_1PLUS_PPC_DISP, 0x400f2)
|
||||
EVENT(PM_DATA_FROM_L2MISS, 0x200fe)
|
||||
EVENT(PM_SUSPENDED, 0x00000)
|
||||
EVENT(PM_VSU0_FMA, 0x0a084)
|
||||
EVENT(PM_CMPLU_STALL_SCALAR, 0x40012)
|
||||
EVENT(PM_STCX_FAIL, 0x0c09a)
|
||||
EVENT(PM_VSU0_FSQRT_FDIV_DOUBLE, 0x0a094)
|
||||
EVENT(PM_DC_PREF_DST, 0x0d0b0)
|
||||
EVENT(PM_VSU1_SCAL_SINGLE_ISSUED, 0x0b086)
|
||||
EVENT(PM_L3_HIT, 0x1f080)
|
||||
EVENT(PM_L2_GLOB_GUESS_WRONG, 0x26482)
|
||||
EVENT(PM_MRK_DFU_FIN, 0x20032)
|
||||
EVENT(PM_INST_FROM_L1, 0x04080)
|
||||
EVENT(PM_BRU_FIN, 0x10068)
|
||||
EVENT(PM_IC_DEMAND_REQ, 0x04088)
|
||||
EVENT(PM_VSU1_FSQRT_FDIV_DOUBLE, 0x0a096)
|
||||
EVENT(PM_VSU1_FMA, 0x0a086)
|
||||
EVENT(PM_MRK_LD_MISS_L1, 0x20036)
|
||||
EVENT(PM_VSU0_2FLOP_DOUBLE, 0x0a08c)
|
||||
EVENT(PM_LSU_DC_PREF_STRIDED_STREAM_CONFIRM, 0x0d8bc)
|
||||
EVENT(PM_INST_PTEG_FROM_L31_SHR, 0x2e056)
|
||||
EVENT(PM_MRK_LSU_REJECT_ERAT_MISS, 0x30064)
|
||||
EVENT(PM_MRK_DATA_FROM_L2MISS, 0x4d048)
|
||||
EVENT(PM_DATA_FROM_RL2L3_SHR, 0x1c04c)
|
||||
EVENT(PM_INST_FROM_PREF, 0x14046)
|
||||
EVENT(PM_VSU1_SQ, 0x0b09e)
|
||||
EVENT(PM_L2_LD_DISP, 0x36180)
|
||||
EVENT(PM_L2_DISP_ALL, 0x46080)
|
||||
EVENT(PM_THRD_GRP_CMPL_BOTH_CYC, 0x10012)
|
||||
EVENT(PM_VSU_FSQRT_FDIV_DOUBLE, 0x0a894)
|
||||
EVENT(PM_BR_MPRED, 0x400f6)
|
||||
EVENT(PM_INST_PTEG_FROM_DL2L3_SHR, 0x3e054)
|
||||
EVENT(PM_VSU_1FLOP, 0x0a880)
|
||||
EVENT(PM_HV_CYC, 0x2000a)
|
||||
EVENT(PM_MRK_LSU_FIN, 0x40032)
|
||||
EVENT(PM_MRK_DATA_FROM_RL2L3_SHR, 0x1d04c)
|
||||
EVENT(PM_DTLB_MISS_16M, 0x4c05e)
|
||||
EVENT(PM_LSU1_LMQ_LHR_MERGE, 0x0d09a)
|
||||
EVENT(PM_IFU_FIN, 0x40066)
|
|
@ -53,37 +53,13 @@
|
|||
/*
|
||||
* Power7 event codes.
|
||||
*/
|
||||
#define PME_PM_CYC 0x1e
|
||||
#define PME_PM_GCT_NOSLOT_CYC 0x100f8
|
||||
#define PME_PM_CMPLU_STALL 0x4000a
|
||||
#define PME_PM_INST_CMPL 0x2
|
||||
#define PME_PM_LD_REF_L1 0xc880
|
||||
#define PME_PM_LD_MISS_L1 0x400f0
|
||||
#define PME_PM_BRU_FIN 0x10068
|
||||
#define PME_PM_BR_MPRED 0x400f6
|
||||
#define EVENT(_name, _code) \
|
||||
PME_##_name = _code,
|
||||
|
||||
#define PME_PM_CMPLU_STALL_FXU 0x20014
|
||||
#define PME_PM_CMPLU_STALL_DIV 0x40014
|
||||
#define PME_PM_CMPLU_STALL_SCALAR 0x40012
|
||||
#define PME_PM_CMPLU_STALL_SCALAR_LONG 0x20018
|
||||
#define PME_PM_CMPLU_STALL_VECTOR 0x2001c
|
||||
#define PME_PM_CMPLU_STALL_VECTOR_LONG 0x4004a
|
||||
#define PME_PM_CMPLU_STALL_LSU 0x20012
|
||||
#define PME_PM_CMPLU_STALL_REJECT 0x40016
|
||||
#define PME_PM_CMPLU_STALL_ERAT_MISS 0x40018
|
||||
#define PME_PM_CMPLU_STALL_DCACHE_MISS 0x20016
|
||||
#define PME_PM_CMPLU_STALL_STORE 0x2004a
|
||||
#define PME_PM_CMPLU_STALL_THRD 0x1001c
|
||||
#define PME_PM_CMPLU_STALL_IFU 0x4004c
|
||||
#define PME_PM_CMPLU_STALL_BRU 0x4004e
|
||||
#define PME_PM_GCT_NOSLOT_IC_MISS 0x2001a
|
||||
#define PME_PM_GCT_NOSLOT_BR_MPRED 0x4001a
|
||||
#define PME_PM_GCT_NOSLOT_BR_MPRED_IC_MISS 0x4001c
|
||||
#define PME_PM_GRP_CMPL 0x30004
|
||||
#define PME_PM_1PLUS_PPC_CMPL 0x100f2
|
||||
#define PME_PM_CMPLU_STALL_DFU 0x2003c
|
||||
#define PME_PM_RUN_CYC 0x200f4
|
||||
#define PME_PM_RUN_INST_CMPL 0x400fa
|
||||
enum {
|
||||
#include "power7-events-list.h"
|
||||
};
|
||||
#undef EVENT
|
||||
|
||||
/*
|
||||
* Layout of constraint bits:
|
||||
|
@ -398,96 +374,36 @@ static int power7_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
|
|||
};
|
||||
|
||||
|
||||
GENERIC_EVENT_ATTR(cpu-cycles, CYC);
|
||||
GENERIC_EVENT_ATTR(stalled-cycles-frontend, GCT_NOSLOT_CYC);
|
||||
GENERIC_EVENT_ATTR(stalled-cycles-backend, CMPLU_STALL);
|
||||
GENERIC_EVENT_ATTR(instructions, INST_CMPL);
|
||||
GENERIC_EVENT_ATTR(cache-references, LD_REF_L1);
|
||||
GENERIC_EVENT_ATTR(cache-misses, LD_MISS_L1);
|
||||
GENERIC_EVENT_ATTR(branch-instructions, BRU_FIN);
|
||||
GENERIC_EVENT_ATTR(branch-misses, BR_MPRED);
|
||||
GENERIC_EVENT_ATTR(cpu-cycles, PM_CYC);
|
||||
GENERIC_EVENT_ATTR(stalled-cycles-frontend, PM_GCT_NOSLOT_CYC);
|
||||
GENERIC_EVENT_ATTR(stalled-cycles-backend, PM_CMPLU_STALL);
|
||||
GENERIC_EVENT_ATTR(instructions, PM_INST_CMPL);
|
||||
GENERIC_EVENT_ATTR(cache-references, PM_LD_REF_L1);
|
||||
GENERIC_EVENT_ATTR(cache-misses, PM_LD_MISS_L1);
|
||||
GENERIC_EVENT_ATTR(branch-instructions, PM_BRU_FIN);
|
||||
GENERIC_EVENT_ATTR(branch-misses, PM_BR_MPRED);
|
||||
|
||||
POWER_EVENT_ATTR(CYC, CYC);
|
||||
POWER_EVENT_ATTR(GCT_NOSLOT_CYC, GCT_NOSLOT_CYC);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL, CMPLU_STALL);
|
||||
POWER_EVENT_ATTR(INST_CMPL, INST_CMPL);
|
||||
POWER_EVENT_ATTR(LD_REF_L1, LD_REF_L1);
|
||||
POWER_EVENT_ATTR(LD_MISS_L1, LD_MISS_L1);
|
||||
POWER_EVENT_ATTR(BRU_FIN, BRU_FIN)
|
||||
POWER_EVENT_ATTR(BR_MPRED, BR_MPRED);
|
||||
#define EVENT(_name, _code) POWER_EVENT_ATTR(_name, _name);
|
||||
#include "power7-events-list.h"
|
||||
#undef EVENT
|
||||
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_FXU, CMPLU_STALL_FXU);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_DIV, CMPLU_STALL_DIV);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_SCALAR, CMPLU_STALL_SCALAR);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_SCALAR_LONG, CMPLU_STALL_SCALAR_LONG);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_VECTOR, CMPLU_STALL_VECTOR);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_VECTOR_LONG, CMPLU_STALL_VECTOR_LONG);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_LSU, CMPLU_STALL_LSU);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_REJECT, CMPLU_STALL_REJECT);
|
||||
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_ERAT_MISS, CMPLU_STALL_ERAT_MISS);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_DCACHE_MISS, CMPLU_STALL_DCACHE_MISS);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_STORE, CMPLU_STALL_STORE);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_THRD, CMPLU_STALL_THRD);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_IFU, CMPLU_STALL_IFU);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_BRU, CMPLU_STALL_BRU);
|
||||
POWER_EVENT_ATTR(GCT_NOSLOT_IC_MISS, GCT_NOSLOT_IC_MISS);
|
||||
|
||||
POWER_EVENT_ATTR(GCT_NOSLOT_BR_MPRED, GCT_NOSLOT_BR_MPRED);
|
||||
POWER_EVENT_ATTR(GCT_NOSLOT_BR_MPRED_IC_MISS, GCT_NOSLOT_BR_MPRED_IC_MISS);
|
||||
POWER_EVENT_ATTR(GRP_CMPL, GRP_CMPL);
|
||||
POWER_EVENT_ATTR(1PLUS_PPC_CMPL, 1PLUS_PPC_CMPL);
|
||||
POWER_EVENT_ATTR(CMPLU_STALL_DFU, CMPLU_STALL_DFU);
|
||||
POWER_EVENT_ATTR(RUN_CYC, RUN_CYC);
|
||||
POWER_EVENT_ATTR(RUN_INST_CMPL, RUN_INST_CMPL);
|
||||
#define EVENT(_name, _code) POWER_EVENT_PTR(_name),
|
||||
|
||||
static struct attribute *power7_events_attr[] = {
|
||||
GENERIC_EVENT_PTR(CYC),
|
||||
GENERIC_EVENT_PTR(GCT_NOSLOT_CYC),
|
||||
GENERIC_EVENT_PTR(CMPLU_STALL),
|
||||
GENERIC_EVENT_PTR(INST_CMPL),
|
||||
GENERIC_EVENT_PTR(LD_REF_L1),
|
||||
GENERIC_EVENT_PTR(LD_MISS_L1),
|
||||
GENERIC_EVENT_PTR(BRU_FIN),
|
||||
GENERIC_EVENT_PTR(BR_MPRED),
|
||||
GENERIC_EVENT_PTR(PM_CYC),
|
||||
GENERIC_EVENT_PTR(PM_GCT_NOSLOT_CYC),
|
||||
GENERIC_EVENT_PTR(PM_CMPLU_STALL),
|
||||
GENERIC_EVENT_PTR(PM_INST_CMPL),
|
||||
GENERIC_EVENT_PTR(PM_LD_REF_L1),
|
||||
GENERIC_EVENT_PTR(PM_LD_MISS_L1),
|
||||
GENERIC_EVENT_PTR(PM_BRU_FIN),
|
||||
GENERIC_EVENT_PTR(PM_BR_MPRED),
|
||||
|
||||
POWER_EVENT_PTR(CYC),
|
||||
POWER_EVENT_PTR(GCT_NOSLOT_CYC),
|
||||
POWER_EVENT_PTR(CMPLU_STALL),
|
||||
POWER_EVENT_PTR(INST_CMPL),
|
||||
POWER_EVENT_PTR(LD_REF_L1),
|
||||
POWER_EVENT_PTR(LD_MISS_L1),
|
||||
POWER_EVENT_PTR(BRU_FIN),
|
||||
POWER_EVENT_PTR(BR_MPRED),
|
||||
|
||||
POWER_EVENT_PTR(CMPLU_STALL_FXU),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_DIV),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_SCALAR),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_SCALAR_LONG),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_VECTOR),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_VECTOR_LONG),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_LSU),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_REJECT),
|
||||
|
||||
POWER_EVENT_PTR(CMPLU_STALL_ERAT_MISS),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_DCACHE_MISS),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_STORE),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_THRD),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_IFU),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_BRU),
|
||||
POWER_EVENT_PTR(GCT_NOSLOT_IC_MISS),
|
||||
POWER_EVENT_PTR(GCT_NOSLOT_BR_MPRED),
|
||||
|
||||
POWER_EVENT_PTR(GCT_NOSLOT_BR_MPRED_IC_MISS),
|
||||
POWER_EVENT_PTR(GRP_CMPL),
|
||||
POWER_EVENT_PTR(1PLUS_PPC_CMPL),
|
||||
POWER_EVENT_PTR(CMPLU_STALL_DFU),
|
||||
POWER_EVENT_PTR(RUN_CYC),
|
||||
POWER_EVENT_PTR(RUN_INST_CMPL),
|
||||
#include "power7-events-list.h"
|
||||
#undef EVENT
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
static struct attribute_group power7_pmu_events_group = {
|
||||
.name = "events",
|
||||
.attrs = power7_events_attr,
|
||||
|
|
|
@ -39,13 +39,8 @@ bindir_relative = bin
|
|||
bindir = $(prefix)/$(bindir_relative)
|
||||
man_dir = $(prefix)/share/man
|
||||
man_dir_SQ = '$(subst ','\'',$(man_dir))'
|
||||
html_install = $(prefix)/share/kernelshark/html
|
||||
html_install_SQ = '$(subst ','\'',$(html_install))'
|
||||
img_install = $(prefix)/share/kernelshark/html/images
|
||||
img_install_SQ = '$(subst ','\'',$(img_install))'
|
||||
|
||||
export man_dir man_dir_SQ html_install html_install_SQ INSTALL
|
||||
export img_install img_install_SQ
|
||||
export man_dir man_dir_SQ INSTALL
|
||||
export DESTDIR DESTDIR_SQ
|
||||
|
||||
# copy a bit from Linux kbuild
|
||||
|
@ -76,10 +71,7 @@ $(if $(BUILD_OUTPUT),, \
|
|||
|
||||
all: sub-make
|
||||
|
||||
gui: force
|
||||
$(call build_output, all_cmd)
|
||||
|
||||
$(filter-out gui,$(MAKECMDGOALS)): sub-make
|
||||
$(MAKECMDGOALS): sub-make
|
||||
|
||||
sub-make: force
|
||||
$(call build_output, $(MAKECMDGOALS))
|
||||
|
@ -189,6 +181,7 @@ $(obj)/%.o: $(src)/%.c
|
|||
$(Q)$(call do_compile)
|
||||
|
||||
PEVENT_LIB_OBJS = event-parse.o trace-seq.o parse-filter.o parse-utils.o
|
||||
PEVENT_LIB_OBJS += kbuffer-parse.o
|
||||
|
||||
ALL_OBJS = $(PEVENT_LIB_OBJS)
|
||||
|
||||
|
@ -258,9 +251,6 @@ define check_deps
|
|||
$(RM) $@.$$$$
|
||||
endef
|
||||
|
||||
$(gui_deps): ks_version.h
|
||||
$(non_gui_deps): tc_version.h
|
||||
|
||||
$(all_deps): .%.d: $(src)/%.c
|
||||
$(Q)$(call check_deps)
|
||||
|
||||
|
@ -300,7 +290,7 @@ define do_install
|
|||
$(INSTALL) $1 '$(DESTDIR_SQ)$2'
|
||||
endef
|
||||
|
||||
install_lib: all_cmd install_plugins install_python
|
||||
install_lib: all_cmd
|
||||
$(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ))
|
||||
|
||||
install: install_lib
|
||||
|
|
|
@ -5450,10 +5450,9 @@ int pevent_register_print_function(struct pevent *pevent,
|
|||
* If @id is >= 0, then it is used to find the event.
|
||||
* else @sys_name and @event_name are used.
|
||||
*/
|
||||
int pevent_register_event_handler(struct pevent *pevent,
|
||||
int id, char *sys_name, char *event_name,
|
||||
pevent_event_handler_func func,
|
||||
void *context)
|
||||
int pevent_register_event_handler(struct pevent *pevent, int id,
|
||||
const char *sys_name, const char *event_name,
|
||||
pevent_event_handler_func func, void *context)
|
||||
{
|
||||
struct event_format *event;
|
||||
struct event_handler *handle;
|
||||
|
|
|
@ -69,6 +69,7 @@ struct trace_seq {
|
|||
};
|
||||
|
||||
void trace_seq_init(struct trace_seq *s);
|
||||
void trace_seq_reset(struct trace_seq *s);
|
||||
void trace_seq_destroy(struct trace_seq *s);
|
||||
|
||||
extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
|
||||
|
@ -399,6 +400,7 @@ struct pevent {
|
|||
|
||||
int cpus;
|
||||
int long_size;
|
||||
int page_size;
|
||||
|
||||
struct cmdline *cmdlines;
|
||||
struct cmdline_list *cmdlist;
|
||||
|
@ -561,7 +563,8 @@ int pevent_print_num_field(struct trace_seq *s, const char *fmt,
|
|||
struct event_format *event, const char *name,
|
||||
struct pevent_record *record, int err);
|
||||
|
||||
int pevent_register_event_handler(struct pevent *pevent, int id, char *sys_name, char *event_name,
|
||||
int pevent_register_event_handler(struct pevent *pevent, int id,
|
||||
const char *sys_name, const char *event_name,
|
||||
pevent_event_handler_func func, void *context);
|
||||
int pevent_register_print_function(struct pevent *pevent,
|
||||
pevent_func_handler func,
|
||||
|
@ -619,6 +622,16 @@ static inline void pevent_set_long_size(struct pevent *pevent, int long_size)
|
|||
pevent->long_size = long_size;
|
||||
}
|
||||
|
||||
static inline int pevent_get_page_size(struct pevent *pevent)
|
||||
{
|
||||
return pevent->page_size;
|
||||
}
|
||||
|
||||
static inline void pevent_set_page_size(struct pevent *pevent, int _page_size)
|
||||
{
|
||||
pevent->page_size = _page_size;
|
||||
}
|
||||
|
||||
static inline int pevent_is_file_bigendian(struct pevent *pevent)
|
||||
{
|
||||
return pevent->file_bigendian;
|
||||
|
|
|
@ -0,0 +1,732 @@
|
|||
/*
|
||||
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "kbuffer.h"
|
||||
|
||||
#define MISSING_EVENTS (1 << 31)
|
||||
#define MISSING_STORED (1 << 30)
|
||||
|
||||
#define COMMIT_MASK ((1 << 27) - 1)
|
||||
|
||||
enum {
|
||||
KBUFFER_FL_HOST_BIG_ENDIAN = (1<<0),
|
||||
KBUFFER_FL_BIG_ENDIAN = (1<<1),
|
||||
KBUFFER_FL_LONG_8 = (1<<2),
|
||||
KBUFFER_FL_OLD_FORMAT = (1<<3),
|
||||
};
|
||||
|
||||
#define ENDIAN_MASK (KBUFFER_FL_HOST_BIG_ENDIAN | KBUFFER_FL_BIG_ENDIAN)
|
||||
|
||||
/** kbuffer
|
||||
* @timestamp - timestamp of current event
|
||||
* @lost_events - # of lost events between this subbuffer and previous
|
||||
* @flags - special flags of the kbuffer
|
||||
* @subbuffer - pointer to the sub-buffer page
|
||||
* @data - pointer to the start of data on the sub-buffer page
|
||||
* @index - index from @data to the @curr event data
|
||||
* @curr - offset from @data to the start of current event
|
||||
* (includes metadata)
|
||||
* @next - offset from @data to the start of next event
|
||||
* @size - The size of data on @data
|
||||
* @start - The offset from @subbuffer where @data lives
|
||||
*
|
||||
* @read_4 - Function to read 4 raw bytes (may swap)
|
||||
* @read_8 - Function to read 8 raw bytes (may swap)
|
||||
* @read_long - Function to read a long word (4 or 8 bytes with needed swap)
|
||||
*/
|
||||
struct kbuffer {
|
||||
unsigned long long timestamp;
|
||||
long long lost_events;
|
||||
unsigned long flags;
|
||||
void *subbuffer;
|
||||
void *data;
|
||||
unsigned int index;
|
||||
unsigned int curr;
|
||||
unsigned int next;
|
||||
unsigned int size;
|
||||
unsigned int start;
|
||||
|
||||
unsigned int (*read_4)(void *ptr);
|
||||
unsigned long long (*read_8)(void *ptr);
|
||||
unsigned long long (*read_long)(struct kbuffer *kbuf, void *ptr);
|
||||
int (*next_event)(struct kbuffer *kbuf);
|
||||
};
|
||||
|
||||
static void *zmalloc(size_t size)
|
||||
{
|
||||
return calloc(1, size);
|
||||
}
|
||||
|
||||
static int host_is_bigendian(void)
|
||||
{
|
||||
unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 };
|
||||
unsigned int *ptr;
|
||||
|
||||
ptr = (unsigned int *)str;
|
||||
return *ptr == 0x01020304;
|
||||
}
|
||||
|
||||
static int do_swap(struct kbuffer *kbuf)
|
||||
{
|
||||
return ((kbuf->flags & KBUFFER_FL_HOST_BIG_ENDIAN) + kbuf->flags) &
|
||||
ENDIAN_MASK;
|
||||
}
|
||||
|
||||
static unsigned long long __read_8(void *ptr)
|
||||
{
|
||||
unsigned long long data = *(unsigned long long *)ptr;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static unsigned long long __read_8_sw(void *ptr)
|
||||
{
|
||||
unsigned long long data = *(unsigned long long *)ptr;
|
||||
unsigned long long swap;
|
||||
|
||||
swap = ((data & 0xffULL) << 56) |
|
||||
((data & (0xffULL << 8)) << 40) |
|
||||
((data & (0xffULL << 16)) << 24) |
|
||||
((data & (0xffULL << 24)) << 8) |
|
||||
((data & (0xffULL << 32)) >> 8) |
|
||||
((data & (0xffULL << 40)) >> 24) |
|
||||
((data & (0xffULL << 48)) >> 40) |
|
||||
((data & (0xffULL << 56)) >> 56);
|
||||
|
||||
return swap;
|
||||
}
|
||||
|
||||
static unsigned int __read_4(void *ptr)
|
||||
{
|
||||
unsigned int data = *(unsigned int *)ptr;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static unsigned int __read_4_sw(void *ptr)
|
||||
{
|
||||
unsigned int data = *(unsigned int *)ptr;
|
||||
unsigned int swap;
|
||||
|
||||
swap = ((data & 0xffULL) << 24) |
|
||||
((data & (0xffULL << 8)) << 8) |
|
||||
((data & (0xffULL << 16)) >> 8) |
|
||||
((data & (0xffULL << 24)) >> 24);
|
||||
|
||||
return swap;
|
||||
}
|
||||
|
||||
static unsigned long long read_8(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return kbuf->read_8(ptr);
|
||||
}
|
||||
|
||||
static unsigned int read_4(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return kbuf->read_4(ptr);
|
||||
}
|
||||
|
||||
static unsigned long long __read_long_8(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return kbuf->read_8(ptr);
|
||||
}
|
||||
|
||||
static unsigned long long __read_long_4(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return kbuf->read_4(ptr);
|
||||
}
|
||||
|
||||
static unsigned long long read_long(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return kbuf->read_long(kbuf, ptr);
|
||||
}
|
||||
|
||||
static int calc_index(struct kbuffer *kbuf, void *ptr)
|
||||
{
|
||||
return (unsigned long)ptr - (unsigned long)kbuf->data;
|
||||
}
|
||||
|
||||
static int __next_event(struct kbuffer *kbuf);
|
||||
|
||||
/**
|
||||
* kbuffer_alloc - allocat a new kbuffer
|
||||
* @size; enum to denote size of word
|
||||
* @endian: enum to denote endianness
|
||||
*
|
||||
* Allocates and returns a new kbuffer.
|
||||
*/
|
||||
struct kbuffer *
|
||||
kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian)
|
||||
{
|
||||
struct kbuffer *kbuf;
|
||||
int flags = 0;
|
||||
|
||||
switch (size) {
|
||||
case KBUFFER_LSIZE_4:
|
||||
break;
|
||||
case KBUFFER_LSIZE_8:
|
||||
flags |= KBUFFER_FL_LONG_8;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (endian) {
|
||||
case KBUFFER_ENDIAN_LITTLE:
|
||||
break;
|
||||
case KBUFFER_ENDIAN_BIG:
|
||||
flags |= KBUFFER_FL_BIG_ENDIAN;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kbuf = zmalloc(sizeof(*kbuf));
|
||||
if (!kbuf)
|
||||
return NULL;
|
||||
|
||||
kbuf->flags = flags;
|
||||
|
||||
if (host_is_bigendian())
|
||||
kbuf->flags |= KBUFFER_FL_HOST_BIG_ENDIAN;
|
||||
|
||||
if (do_swap(kbuf)) {
|
||||
kbuf->read_8 = __read_8_sw;
|
||||
kbuf->read_4 = __read_4_sw;
|
||||
} else {
|
||||
kbuf->read_8 = __read_8;
|
||||
kbuf->read_4 = __read_4;
|
||||
}
|
||||
|
||||
if (kbuf->flags & KBUFFER_FL_LONG_8)
|
||||
kbuf->read_long = __read_long_8;
|
||||
else
|
||||
kbuf->read_long = __read_long_4;
|
||||
|
||||
/* May be changed by kbuffer_set_old_format() */
|
||||
kbuf->next_event = __next_event;
|
||||
|
||||
return kbuf;
|
||||
}
|
||||
|
||||
/** kbuffer_free - free an allocated kbuffer
|
||||
* @kbuf: The kbuffer to free
|
||||
*
|
||||
* Can take NULL as a parameter.
|
||||
*/
|
||||
void kbuffer_free(struct kbuffer *kbuf)
|
||||
{
|
||||
free(kbuf);
|
||||
}
|
||||
|
||||
static unsigned int type4host(struct kbuffer *kbuf,
|
||||
unsigned int type_len_ts)
|
||||
{
|
||||
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
|
||||
return (type_len_ts >> 29) & 3;
|
||||
else
|
||||
return type_len_ts & 3;
|
||||
}
|
||||
|
||||
static unsigned int len4host(struct kbuffer *kbuf,
|
||||
unsigned int type_len_ts)
|
||||
{
|
||||
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
|
||||
return (type_len_ts >> 27) & 7;
|
||||
else
|
||||
return (type_len_ts >> 2) & 7;
|
||||
}
|
||||
|
||||
static unsigned int type_len4host(struct kbuffer *kbuf,
|
||||
unsigned int type_len_ts)
|
||||
{
|
||||
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
|
||||
return (type_len_ts >> 27) & ((1 << 5) - 1);
|
||||
else
|
||||
return type_len_ts & ((1 << 5) - 1);
|
||||
}
|
||||
|
||||
static unsigned int ts4host(struct kbuffer *kbuf,
|
||||
unsigned int type_len_ts)
|
||||
{
|
||||
if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN)
|
||||
return type_len_ts & ((1 << 27) - 1);
|
||||
else
|
||||
return type_len_ts >> 5;
|
||||
}
|
||||
|
||||
/*
|
||||
* Linux 2.6.30 and earlier (not much ealier) had a different
|
||||
* ring buffer format. It should be obsolete, but we handle it anyway.
|
||||
*/
|
||||
enum old_ring_buffer_type {
|
||||
OLD_RINGBUF_TYPE_PADDING,
|
||||
OLD_RINGBUF_TYPE_TIME_EXTEND,
|
||||
OLD_RINGBUF_TYPE_TIME_STAMP,
|
||||
OLD_RINGBUF_TYPE_DATA,
|
||||
};
|
||||
|
||||
static unsigned int old_update_pointers(struct kbuffer *kbuf)
|
||||
{
|
||||
unsigned long long extend;
|
||||
unsigned int type_len_ts;
|
||||
unsigned int type;
|
||||
unsigned int len;
|
||||
unsigned int delta;
|
||||
unsigned int length;
|
||||
void *ptr = kbuf->data + kbuf->curr;
|
||||
|
||||
type_len_ts = read_4(kbuf, ptr);
|
||||
ptr += 4;
|
||||
|
||||
type = type4host(kbuf, type_len_ts);
|
||||
len = len4host(kbuf, type_len_ts);
|
||||
delta = ts4host(kbuf, type_len_ts);
|
||||
|
||||
switch (type) {
|
||||
case OLD_RINGBUF_TYPE_PADDING:
|
||||
kbuf->next = kbuf->size;
|
||||
return 0;
|
||||
|
||||
case OLD_RINGBUF_TYPE_TIME_EXTEND:
|
||||
extend = read_4(kbuf, ptr);
|
||||
extend <<= TS_SHIFT;
|
||||
extend += delta;
|
||||
delta = extend;
|
||||
ptr += 4;
|
||||
break;
|
||||
|
||||
case OLD_RINGBUF_TYPE_TIME_STAMP:
|
||||
/* should never happen! */
|
||||
kbuf->curr = kbuf->size;
|
||||
kbuf->next = kbuf->size;
|
||||
kbuf->index = kbuf->size;
|
||||
return -1;
|
||||
default:
|
||||
if (len)
|
||||
length = len * 4;
|
||||
else {
|
||||
length = read_4(kbuf, ptr);
|
||||
length -= 4;
|
||||
ptr += 4;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
kbuf->timestamp += delta;
|
||||
kbuf->index = calc_index(kbuf, ptr);
|
||||
kbuf->next = kbuf->index + length;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static int __old_next_event(struct kbuffer *kbuf)
|
||||
{
|
||||
int type;
|
||||
|
||||
do {
|
||||
kbuf->curr = kbuf->next;
|
||||
if (kbuf->next >= kbuf->size)
|
||||
return -1;
|
||||
type = old_update_pointers(kbuf);
|
||||
} while (type == OLD_RINGBUF_TYPE_TIME_EXTEND || type == OLD_RINGBUF_TYPE_PADDING);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
translate_data(struct kbuffer *kbuf, void *data, void **rptr,
|
||||
unsigned long long *delta, int *length)
|
||||
{
|
||||
unsigned long long extend;
|
||||
unsigned int type_len_ts;
|
||||
unsigned int type_len;
|
||||
|
||||
type_len_ts = read_4(kbuf, data);
|
||||
data += 4;
|
||||
|
||||
type_len = type_len4host(kbuf, type_len_ts);
|
||||
*delta = ts4host(kbuf, type_len_ts);
|
||||
|
||||
switch (type_len) {
|
||||
case KBUFFER_TYPE_PADDING:
|
||||
*length = read_4(kbuf, data);
|
||||
data += *length;
|
||||
break;
|
||||
|
||||
case KBUFFER_TYPE_TIME_EXTEND:
|
||||
extend = read_4(kbuf, data);
|
||||
data += 4;
|
||||
extend <<= TS_SHIFT;
|
||||
extend += *delta;
|
||||
*delta = extend;
|
||||
*length = 0;
|
||||
break;
|
||||
|
||||
case KBUFFER_TYPE_TIME_STAMP:
|
||||
data += 12;
|
||||
*length = 0;
|
||||
break;
|
||||
case 0:
|
||||
*length = read_4(kbuf, data) - 4;
|
||||
*length = (*length + 3) & ~3;
|
||||
data += 4;
|
||||
break;
|
||||
default:
|
||||
*length = type_len * 4;
|
||||
break;
|
||||
}
|
||||
|
||||
*rptr = data;
|
||||
|
||||
return type_len;
|
||||
}
|
||||
|
||||
static unsigned int update_pointers(struct kbuffer *kbuf)
|
||||
{
|
||||
unsigned long long delta;
|
||||
unsigned int type_len;
|
||||
int length;
|
||||
void *ptr = kbuf->data + kbuf->curr;
|
||||
|
||||
type_len = translate_data(kbuf, ptr, &ptr, &delta, &length);
|
||||
|
||||
kbuf->timestamp += delta;
|
||||
kbuf->index = calc_index(kbuf, ptr);
|
||||
kbuf->next = kbuf->index + length;
|
||||
|
||||
return type_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_translate_data - read raw data to get a record
|
||||
* @swap: Set to 1 if bytes in words need to be swapped when read
|
||||
* @data: The raw data to read
|
||||
* @size: Address to store the size of the event data.
|
||||
*
|
||||
* Returns a pointer to the event data. To determine the entire
|
||||
* record size (record metadata + data) just add the difference between
|
||||
* @data and the returned value to @size.
|
||||
*/
|
||||
void *kbuffer_translate_data(int swap, void *data, unsigned int *size)
|
||||
{
|
||||
unsigned long long delta;
|
||||
struct kbuffer kbuf;
|
||||
int type_len;
|
||||
int length;
|
||||
void *ptr;
|
||||
|
||||
if (swap) {
|
||||
kbuf.read_8 = __read_8_sw;
|
||||
kbuf.read_4 = __read_4_sw;
|
||||
kbuf.flags = host_is_bigendian() ? 0 : KBUFFER_FL_BIG_ENDIAN;
|
||||
} else {
|
||||
kbuf.read_8 = __read_8;
|
||||
kbuf.read_4 = __read_4;
|
||||
kbuf.flags = host_is_bigendian() ? KBUFFER_FL_BIG_ENDIAN: 0;
|
||||
}
|
||||
|
||||
type_len = translate_data(&kbuf, data, &ptr, &delta, &length);
|
||||
switch (type_len) {
|
||||
case KBUFFER_TYPE_PADDING:
|
||||
case KBUFFER_TYPE_TIME_EXTEND:
|
||||
case KBUFFER_TYPE_TIME_STAMP:
|
||||
return NULL;
|
||||
};
|
||||
|
||||
*size = length;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static int __next_event(struct kbuffer *kbuf)
|
||||
{
|
||||
int type;
|
||||
|
||||
do {
|
||||
kbuf->curr = kbuf->next;
|
||||
if (kbuf->next >= kbuf->size)
|
||||
return -1;
|
||||
type = update_pointers(kbuf);
|
||||
} while (type == KBUFFER_TYPE_TIME_EXTEND || type == KBUFFER_TYPE_PADDING);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int next_event(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->next_event(kbuf);
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_next_event - increment the current pointer
|
||||
* @kbuf: The kbuffer to read
|
||||
* @ts: Address to store the next record's timestamp (may be NULL to ignore)
|
||||
*
|
||||
* Increments the pointers into the subbuffer of the kbuffer to point to the
|
||||
* next event so that the next kbuffer_read_event() will return a
|
||||
* new event.
|
||||
*
|
||||
* Returns the data of the next event if a new event exists on the subbuffer,
|
||||
* NULL otherwise.
|
||||
*/
|
||||
void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!kbuf || !kbuf->subbuffer)
|
||||
return NULL;
|
||||
|
||||
ret = next_event(kbuf);
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
if (ts)
|
||||
*ts = kbuf->timestamp;
|
||||
|
||||
return kbuf->data + kbuf->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_load_subbuffer - load a new subbuffer into the kbuffer
|
||||
* @kbuf: The kbuffer to load
|
||||
* @subbuffer: The subbuffer to load into @kbuf.
|
||||
*
|
||||
* Load a new subbuffer (page) into @kbuf. This will reset all
|
||||
* the pointers and update the @kbuf timestamp. The next read will
|
||||
* return the first event on @subbuffer.
|
||||
*
|
||||
* Returns 0 on succes, -1 otherwise.
|
||||
*/
|
||||
int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer)
|
||||
{
|
||||
unsigned long long flags;
|
||||
void *ptr = subbuffer;
|
||||
|
||||
if (!kbuf || !subbuffer)
|
||||
return -1;
|
||||
|
||||
kbuf->subbuffer = subbuffer;
|
||||
|
||||
kbuf->timestamp = read_8(kbuf, ptr);
|
||||
ptr += 8;
|
||||
|
||||
kbuf->curr = 0;
|
||||
|
||||
if (kbuf->flags & KBUFFER_FL_LONG_8)
|
||||
kbuf->start = 16;
|
||||
else
|
||||
kbuf->start = 12;
|
||||
|
||||
kbuf->data = subbuffer + kbuf->start;
|
||||
|
||||
flags = read_long(kbuf, ptr);
|
||||
kbuf->size = (unsigned int)flags & COMMIT_MASK;
|
||||
|
||||
if (flags & MISSING_EVENTS) {
|
||||
if (flags & MISSING_STORED) {
|
||||
ptr = kbuf->data + kbuf->size;
|
||||
kbuf->lost_events = read_long(kbuf, ptr);
|
||||
} else
|
||||
kbuf->lost_events = -1;
|
||||
} else
|
||||
kbuf->lost_events = 0;
|
||||
|
||||
kbuf->index = 0;
|
||||
kbuf->next = 0;
|
||||
|
||||
next_event(kbuf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_read_event - read the next event in the kbuffer subbuffer
|
||||
* @kbuf: The kbuffer to read from
|
||||
* @ts: The address to store the timestamp of the event (may be NULL to ignore)
|
||||
*
|
||||
* Returns a pointer to the data part of the current event.
|
||||
* NULL if no event is left on the subbuffer.
|
||||
*/
|
||||
void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts)
|
||||
{
|
||||
if (!kbuf || !kbuf->subbuffer)
|
||||
return NULL;
|
||||
|
||||
if (kbuf->curr >= kbuf->size)
|
||||
return NULL;
|
||||
|
||||
if (ts)
|
||||
*ts = kbuf->timestamp;
|
||||
return kbuf->data + kbuf->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_timestamp - Return the timestamp of the current event
|
||||
* @kbuf: The kbuffer to read from
|
||||
*
|
||||
* Returns the timestamp of the current (next) event.
|
||||
*/
|
||||
unsigned long long kbuffer_timestamp(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_read_at_offset - read the event that is at offset
|
||||
* @kbuf: The kbuffer to read from
|
||||
* @offset: The offset into the subbuffer
|
||||
* @ts: The address to store the timestamp of the event (may be NULL to ignore)
|
||||
*
|
||||
* The @offset must be an index from the @kbuf subbuffer beginning.
|
||||
* If @offset is bigger than the stored subbuffer, NULL will be returned.
|
||||
*
|
||||
* Returns the data of the record that is at @offset. Note, @offset does
|
||||
* not need to be the start of the record, the offset just needs to be
|
||||
* in the record (or beginning of it).
|
||||
*
|
||||
* Note, the kbuf timestamp and pointers are updated to the
|
||||
* returned record. That is, kbuffer_read_event() will return the same
|
||||
* data and timestamp, and kbuffer_next_event() will increment from
|
||||
* this record.
|
||||
*/
|
||||
void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset,
|
||||
unsigned long long *ts)
|
||||
{
|
||||
void *data;
|
||||
|
||||
if (offset < kbuf->start)
|
||||
offset = 0;
|
||||
else
|
||||
offset -= kbuf->start;
|
||||
|
||||
/* Reset the buffer */
|
||||
kbuffer_load_subbuffer(kbuf, kbuf->subbuffer);
|
||||
|
||||
while (kbuf->curr < offset) {
|
||||
data = kbuffer_next_event(kbuf, ts);
|
||||
if (!data)
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_subbuffer_size - the size of the loaded subbuffer
|
||||
* @kbuf: The kbuffer to read from
|
||||
*
|
||||
* Returns the size of the subbuffer. Note, this size is
|
||||
* where the last event resides. The stored subbuffer may actually be
|
||||
* bigger due to padding and such.
|
||||
*/
|
||||
int kbuffer_subbuffer_size(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_curr_index - Return the index of the record
|
||||
* @kbuf: The kbuffer to read from
|
||||
*
|
||||
* Returns the index from the start of the data part of
|
||||
* the subbuffer to the current location. Note this is not
|
||||
* from the start of the subbuffer. An index of zero will
|
||||
* point to the first record. Use kbuffer_curr_offset() for
|
||||
* the actually offset (that can be used by kbuffer_read_at_offset())
|
||||
*/
|
||||
int kbuffer_curr_index(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->curr;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_curr_offset - Return the offset of the record
|
||||
* @kbuf: The kbuffer to read from
|
||||
*
|
||||
* Returns the offset from the start of the subbuffer to the
|
||||
* current location.
|
||||
*/
|
||||
int kbuffer_curr_offset(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->curr + kbuf->start;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_event_size - return the size of the event data
|
||||
* @kbuf: The kbuffer to read
|
||||
*
|
||||
* Returns the size of the event data (the payload not counting
|
||||
* the meta data of the record) of the current event.
|
||||
*/
|
||||
int kbuffer_event_size(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->next - kbuf->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_curr_size - return the size of the entire record
|
||||
* @kbuf: The kbuffer to read
|
||||
*
|
||||
* Returns the size of the entire record (meta data and payload)
|
||||
* of the current event.
|
||||
*/
|
||||
int kbuffer_curr_size(struct kbuffer *kbuf)
|
||||
{
|
||||
return kbuf->next - kbuf->curr;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_missed_events - return the # of missed events from last event.
|
||||
* @kbuf: The kbuffer to read from
|
||||
*
|
||||
* Returns the # of missed events (if recorded) before the current
|
||||
* event. Note, only events on the beginning of a subbuffer can
|
||||
* have missed events, all other events within the buffer will be
|
||||
* zero.
|
||||
*/
|
||||
int kbuffer_missed_events(struct kbuffer *kbuf)
|
||||
{
|
||||
/* Only the first event can have missed events */
|
||||
if (kbuf->curr)
|
||||
return 0;
|
||||
|
||||
return kbuf->lost_events;
|
||||
}
|
||||
|
||||
/**
|
||||
* kbuffer_set_old_forma - set the kbuffer to use the old format parsing
|
||||
* @kbuf: The kbuffer to set
|
||||
*
|
||||
* This is obsolete (or should be). The first kernels to use the
|
||||
* new ring buffer had a slightly different ring buffer format
|
||||
* (2.6.30 and earlier). It is still somewhat supported by kbuffer,
|
||||
* but should not be counted on in the future.
|
||||
*/
|
||||
void kbuffer_set_old_format(struct kbuffer *kbuf)
|
||||
{
|
||||
kbuf->flags |= KBUFFER_FL_OLD_FORMAT;
|
||||
|
||||
kbuf->next_event = __old_next_event;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License (not later!)
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#ifndef _KBUFFER_H
|
||||
#define _KBUFFER_H
|
||||
|
||||
#ifndef TS_SHIFT
|
||||
#define TS_SHIFT 27
|
||||
#endif
|
||||
|
||||
enum kbuffer_endian {
|
||||
KBUFFER_ENDIAN_BIG,
|
||||
KBUFFER_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
enum kbuffer_long_size {
|
||||
KBUFFER_LSIZE_4,
|
||||
KBUFFER_LSIZE_8,
|
||||
};
|
||||
|
||||
enum {
|
||||
KBUFFER_TYPE_PADDING = 29,
|
||||
KBUFFER_TYPE_TIME_EXTEND = 30,
|
||||
KBUFFER_TYPE_TIME_STAMP = 31,
|
||||
};
|
||||
|
||||
struct kbuffer;
|
||||
|
||||
struct kbuffer *kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian);
|
||||
void kbuffer_free(struct kbuffer *kbuf);
|
||||
int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer);
|
||||
void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts);
|
||||
void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts);
|
||||
unsigned long long kbuffer_timestamp(struct kbuffer *kbuf);
|
||||
|
||||
void *kbuffer_translate_data(int swap, void *data, unsigned int *size);
|
||||
|
||||
void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, unsigned long long *ts);
|
||||
|
||||
int kbuffer_curr_index(struct kbuffer *kbuf);
|
||||
|
||||
int kbuffer_curr_offset(struct kbuffer *kbuf);
|
||||
int kbuffer_curr_size(struct kbuffer *kbuf);
|
||||
int kbuffer_event_size(struct kbuffer *kbuf);
|
||||
int kbuffer_missed_events(struct kbuffer *kbuf);
|
||||
int kbuffer_subbuffer_size(struct kbuffer *kbuf);
|
||||
|
||||
void kbuffer_set_old_format(struct kbuffer *kbuf);
|
||||
|
||||
#endif /* _K_BUFFER_H */
|
|
@ -48,6 +48,19 @@ void trace_seq_init(struct trace_seq *s)
|
|||
s->buffer = malloc_or_die(s->buffer_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* trace_seq_reset - re-initialize the trace_seq structure
|
||||
* @s: a pointer to the trace_seq structure to reset
|
||||
*/
|
||||
void trace_seq_reset(struct trace_seq *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
TRACE_SEQ_CHECK(s);
|
||||
s->len = 0;
|
||||
s->readpos = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* trace_seq_destroy - free up memory of a trace_seq
|
||||
* @s: a pointer to the trace_seq to free the buffer
|
||||
|
|
|
@ -3,17 +3,17 @@ perf-diff(1)
|
|||
|
||||
NAME
|
||||
----
|
||||
perf-diff - Read two perf.data files and display the differential profile
|
||||
perf-diff - Read perf.data files and display the differential profile
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf diff' [oldfile] [newfile]
|
||||
'perf diff' [baseline file] [data file1] [[data file2] ... ]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command displays the performance difference amongst two perf.data files
|
||||
captured via perf record.
|
||||
This command displays the performance difference amongst two or more perf.data
|
||||
files captured via perf record.
|
||||
|
||||
If no parameters are passed it will assume perf.data.old and perf.data.
|
||||
|
||||
|
@ -75,8 +75,6 @@ OPTIONS
|
|||
-c::
|
||||
--compute::
|
||||
Differential computation selection - delta,ratio,wdiff (default is delta).
|
||||
If '+' is specified as a first character, the output is sorted based
|
||||
on the computation results.
|
||||
See COMPARISON METHODS section for more info.
|
||||
|
||||
-p::
|
||||
|
@ -87,6 +85,63 @@ OPTIONS
|
|||
--formula::
|
||||
Show formula for given computation.
|
||||
|
||||
-o::
|
||||
--order::
|
||||
Specify compute sorting column number.
|
||||
|
||||
COMPARISON
|
||||
----------
|
||||
The comparison is governed by the baseline file. The baseline perf.data
|
||||
file is iterated for samples. All other perf.data files specified on
|
||||
the command line are searched for the baseline sample pair. If the pair
|
||||
is found, specified computation is made and result is displayed.
|
||||
|
||||
All samples from non-baseline perf.data files, that do not match any
|
||||
baseline entry, are displayed with empty space within baseline column
|
||||
and possible computation results (delta) in their related column.
|
||||
|
||||
Example files samples:
|
||||
- file A with samples f1, f2, f3, f4, f6
|
||||
- file B with samples f2, f4, f5
|
||||
- file C with samples f1, f2, f5
|
||||
|
||||
Example output:
|
||||
x - computation takes place for pair
|
||||
b - baseline sample percentage
|
||||
|
||||
- perf diff A B C
|
||||
|
||||
baseline/A compute/B compute/C samples
|
||||
---------------------------------------
|
||||
b x f1
|
||||
b x x f2
|
||||
b f3
|
||||
b x f4
|
||||
b f6
|
||||
x x f5
|
||||
|
||||
- perf diff B A C
|
||||
|
||||
baseline/B compute/A compute/C samples
|
||||
---------------------------------------
|
||||
b x x f2
|
||||
b x f4
|
||||
b x f5
|
||||
x x f1
|
||||
x f3
|
||||
x f6
|
||||
|
||||
- perf diff C B A
|
||||
|
||||
baseline/C compute/B compute/A samples
|
||||
---------------------------------------
|
||||
b x f1
|
||||
b x x f2
|
||||
b x f5
|
||||
x f3
|
||||
x x f4
|
||||
x f6
|
||||
|
||||
COMPARISON METHODS
|
||||
------------------
|
||||
delta
|
||||
|
@ -96,7 +151,7 @@ If specified the 'Delta' column is displayed with value 'd' computed as:
|
|||
d = A->period_percent - B->period_percent
|
||||
|
||||
with:
|
||||
- A/B being matching hist entry from first/second file specified
|
||||
- A/B being matching hist entry from data/baseline file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period_percent being the % of the hist entry period value within
|
||||
|
@ -109,24 +164,26 @@ If specified the 'Ratio' column is displayed with value 'r' computed as:
|
|||
r = A->period / B->period
|
||||
|
||||
with:
|
||||
- A/B being matching hist entry from first/second file specified
|
||||
- A/B being matching hist entry from data/baseline file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period being the hist entry period value
|
||||
|
||||
wdiff
|
||||
~~~~~
|
||||
wdiff:WEIGHT-B,WEIGHT-A
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
If specified the 'Weighted diff' column is displayed with value 'd' computed as:
|
||||
|
||||
d = B->period * WEIGHT-A - A->period * WEIGHT-B
|
||||
|
||||
- A/B being matching hist entry from first/second file specified
|
||||
- A/B being matching hist entry from data/baseline file specified
|
||||
(or perf.data/perf.data.old) respectively.
|
||||
|
||||
- period being the hist entry period value
|
||||
|
||||
- WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option
|
||||
behind ':' separator like '-c wdiff:1,2'.
|
||||
- WIEGHT-A being the weight of the data file
|
||||
- WIEGHT-B being the weight of the baseline data file
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
|
|
@ -8,7 +8,7 @@ perf-list - List all symbolic event types
|
|||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf list' [hw|sw|cache|tracepoint|event_glob]
|
||||
'perf list' [hw|sw|cache|tracepoint|pmu|event_glob]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
|
@ -104,6 +104,8 @@ To limit the list use:
|
|||
'subsys_glob:event_glob' to filter by tracepoint subsystems such as sched,
|
||||
block, etc.
|
||||
|
||||
. 'pmu' to print the kernel supplied PMU events.
|
||||
|
||||
. If none of the above is matched, it will apply the supplied glob to all
|
||||
events, printing the ones that match.
|
||||
|
||||
|
|
|
@ -135,6 +135,11 @@ OPTIONS
|
|||
--inverted::
|
||||
alias for inverted caller based call graph.
|
||||
|
||||
--ignore-callees=<regex>::
|
||||
Ignore callees of the function(s) matching the given regex.
|
||||
This has the effect of collecting the callers of each such
|
||||
function into one place in the call-graph tree.
|
||||
|
||||
--pretty=<key>::
|
||||
Pretty printing style. key: normal, raw
|
||||
|
||||
|
|
|
@ -155,6 +155,11 @@ Default is to monitor all CPUS.
|
|||
|
||||
Default: fractal,0.5,callee.
|
||||
|
||||
--ignore-callees=<regex>::
|
||||
Ignore callees of the function(s) matching the given regex.
|
||||
This has the effect of collecting the callers of each such
|
||||
function into one place in the call-graph tree.
|
||||
|
||||
--percent-limit::
|
||||
Do not show entries which have an overhead under that percent.
|
||||
(Default: 0).
|
||||
|
|
|
@ -124,7 +124,7 @@ strip-libs = $(filter-out -l%,$(1))
|
|||
ifneq ($(OUTPUT),)
|
||||
TE_PATH=$(OUTPUT)
|
||||
ifneq ($(subdir),)
|
||||
LK_PATH=$(objtree)/lib/lk/
|
||||
LK_PATH=$(OUTPUT)/../lib/lk/
|
||||
else
|
||||
LK_PATH=$(OUTPUT)
|
||||
endif
|
||||
|
@ -281,7 +281,7 @@ LIB_H += util/cpumap.h
|
|||
LIB_H += util/top.h
|
||||
LIB_H += $(ARCH_INCLUDE)
|
||||
LIB_H += util/cgroup.h
|
||||
LIB_H += $(TRACE_EVENT_DIR)event-parse.h
|
||||
LIB_H += $(LIB_INCLUDE)traceevent/event-parse.h
|
||||
LIB_H += util/target.h
|
||||
LIB_H += util/rblist.h
|
||||
LIB_H += util/intlist.h
|
||||
|
|
|
@ -18,15 +18,53 @@
|
|||
#include "util/util.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
static char const *input_old = "perf.data.old",
|
||||
*input_new = "perf.data";
|
||||
static char diff__default_sort_order[] = "dso,symbol";
|
||||
static bool force;
|
||||
/* Diff command specific HPP columns. */
|
||||
enum {
|
||||
PERF_HPP_DIFF__BASELINE,
|
||||
PERF_HPP_DIFF__PERIOD,
|
||||
PERF_HPP_DIFF__PERIOD_BASELINE,
|
||||
PERF_HPP_DIFF__DELTA,
|
||||
PERF_HPP_DIFF__RATIO,
|
||||
PERF_HPP_DIFF__WEIGHTED_DIFF,
|
||||
PERF_HPP_DIFF__FORMULA,
|
||||
|
||||
PERF_HPP_DIFF__MAX_INDEX
|
||||
};
|
||||
|
||||
struct diff_hpp_fmt {
|
||||
struct perf_hpp_fmt fmt;
|
||||
int idx;
|
||||
char *header;
|
||||
int header_width;
|
||||
};
|
||||
|
||||
struct data__file {
|
||||
struct perf_session *session;
|
||||
const char *file;
|
||||
int idx;
|
||||
struct hists *hists;
|
||||
struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX];
|
||||
};
|
||||
|
||||
static struct data__file *data__files;
|
||||
static int data__files_cnt;
|
||||
|
||||
#define data__for_each_file_start(i, d, s) \
|
||||
for (i = s, d = &data__files[s]; \
|
||||
i < data__files_cnt; \
|
||||
i++, d = &data__files[i])
|
||||
|
||||
#define data__for_each_file(i, d) data__for_each_file_start(i, d, 0)
|
||||
#define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1)
|
||||
|
||||
static char diff__default_sort_order[] = "dso,symbol";
|
||||
static bool force;
|
||||
static bool show_period;
|
||||
static bool show_formula;
|
||||
static bool show_baseline_only;
|
||||
static bool sort_compute;
|
||||
static unsigned int sort_compute;
|
||||
|
||||
static s64 compute_wdiff_w1;
|
||||
static s64 compute_wdiff_w2;
|
||||
|
@ -46,6 +84,47 @@ const char *compute_names[COMPUTE_MAX] = {
|
|||
|
||||
static int compute;
|
||||
|
||||
static int compute_2_hpp[COMPUTE_MAX] = {
|
||||
[COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA,
|
||||
[COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO,
|
||||
[COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF,
|
||||
};
|
||||
|
||||
#define MAX_COL_WIDTH 70
|
||||
|
||||
static struct header_column {
|
||||
const char *name;
|
||||
int width;
|
||||
} columns[PERF_HPP_DIFF__MAX_INDEX] = {
|
||||
[PERF_HPP_DIFF__BASELINE] = {
|
||||
.name = "Baseline",
|
||||
},
|
||||
[PERF_HPP_DIFF__PERIOD] = {
|
||||
.name = "Period",
|
||||
.width = 14,
|
||||
},
|
||||
[PERF_HPP_DIFF__PERIOD_BASELINE] = {
|
||||
.name = "Base period",
|
||||
.width = 14,
|
||||
},
|
||||
[PERF_HPP_DIFF__DELTA] = {
|
||||
.name = "Delta",
|
||||
.width = 7,
|
||||
},
|
||||
[PERF_HPP_DIFF__RATIO] = {
|
||||
.name = "Ratio",
|
||||
.width = 14,
|
||||
},
|
||||
[PERF_HPP_DIFF__WEIGHTED_DIFF] = {
|
||||
.name = "Weighted diff",
|
||||
.width = 14,
|
||||
},
|
||||
[PERF_HPP_DIFF__FORMULA] = {
|
||||
.name = "Formula",
|
||||
.width = MAX_COL_WIDTH,
|
||||
}
|
||||
};
|
||||
|
||||
static int setup_compute_opt_wdiff(char *opt)
|
||||
{
|
||||
char *w1_str = opt;
|
||||
|
@ -109,13 +188,6 @@ static int setup_compute(const struct option *opt, const char *str,
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (*str == '+') {
|
||||
sort_compute = true;
|
||||
cstr = (char *) ++str;
|
||||
if (!*str)
|
||||
return 0;
|
||||
}
|
||||
|
||||
option = strchr(str, ':');
|
||||
if (option) {
|
||||
unsigned len = option++ - str;
|
||||
|
@ -145,42 +217,42 @@ static int setup_compute(const struct option *opt, const char *str,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
double perf_diff__period_percent(struct hist_entry *he, u64 period)
|
||||
static double period_percent(struct hist_entry *he, u64 period)
|
||||
{
|
||||
u64 total = he->hists->stats.total_period;
|
||||
return (period * 100.0) / total;
|
||||
}
|
||||
|
||||
double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair)
|
||||
static double compute_delta(struct hist_entry *he, struct hist_entry *pair)
|
||||
{
|
||||
double new_percent = perf_diff__period_percent(he, he->stat.period);
|
||||
double old_percent = perf_diff__period_percent(pair, pair->stat.period);
|
||||
double old_percent = period_percent(he, he->stat.period);
|
||||
double new_percent = period_percent(pair, pair->stat.period);
|
||||
|
||||
he->diff.period_ratio_delta = new_percent - old_percent;
|
||||
he->diff.computed = true;
|
||||
return he->diff.period_ratio_delta;
|
||||
pair->diff.period_ratio_delta = new_percent - old_percent;
|
||||
pair->diff.computed = true;
|
||||
return pair->diff.period_ratio_delta;
|
||||
}
|
||||
|
||||
double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair)
|
||||
static double compute_ratio(struct hist_entry *he, struct hist_entry *pair)
|
||||
{
|
||||
double new_period = he->stat.period;
|
||||
double old_period = pair->stat.period;
|
||||
double old_period = he->stat.period ?: 1;
|
||||
double new_period = pair->stat.period;
|
||||
|
||||
he->diff.computed = true;
|
||||
he->diff.period_ratio = new_period / old_period;
|
||||
return he->diff.period_ratio;
|
||||
pair->diff.computed = true;
|
||||
pair->diff.period_ratio = new_period / old_period;
|
||||
return pair->diff.period_ratio;
|
||||
}
|
||||
|
||||
s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
|
||||
static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair)
|
||||
{
|
||||
u64 new_period = he->stat.period;
|
||||
u64 old_period = pair->stat.period;
|
||||
u64 old_period = he->stat.period;
|
||||
u64 new_period = pair->stat.period;
|
||||
|
||||
he->diff.computed = true;
|
||||
he->diff.wdiff = new_period * compute_wdiff_w2 -
|
||||
old_period * compute_wdiff_w1;
|
||||
pair->diff.computed = true;
|
||||
pair->diff.wdiff = new_period * compute_wdiff_w2 -
|
||||
old_period * compute_wdiff_w1;
|
||||
|
||||
return he->diff.wdiff;
|
||||
return pair->diff.wdiff;
|
||||
}
|
||||
|
||||
static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
|
||||
|
@ -189,15 +261,15 @@ static int formula_delta(struct hist_entry *he, struct hist_entry *pair,
|
|||
return scnprintf(buf, size,
|
||||
"(%" PRIu64 " * 100 / %" PRIu64 ") - "
|
||||
"(%" PRIu64 " * 100 / %" PRIu64 ")",
|
||||
he->stat.period, he->hists->stats.total_period,
|
||||
pair->stat.period, pair->hists->stats.total_period);
|
||||
pair->stat.period, pair->hists->stats.total_period,
|
||||
he->stat.period, he->hists->stats.total_period);
|
||||
}
|
||||
|
||||
static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
|
||||
char *buf, size_t size)
|
||||
{
|
||||
double new_period = he->stat.period;
|
||||
double old_period = pair->stat.period;
|
||||
double old_period = he->stat.period;
|
||||
double new_period = pair->stat.period;
|
||||
|
||||
return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period);
|
||||
}
|
||||
|
@ -205,16 +277,16 @@ static int formula_ratio(struct hist_entry *he, struct hist_entry *pair,
|
|||
static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair,
|
||||
char *buf, size_t size)
|
||||
{
|
||||
u64 new_period = he->stat.period;
|
||||
u64 old_period = pair->stat.period;
|
||||
u64 old_period = he->stat.period;
|
||||
u64 new_period = pair->stat.period;
|
||||
|
||||
return scnprintf(buf, size,
|
||||
"(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")",
|
||||
new_period, compute_wdiff_w2, old_period, compute_wdiff_w1);
|
||||
}
|
||||
|
||||
int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair,
|
||||
char *buf, size_t size)
|
||||
static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair,
|
||||
char *buf, size_t size)
|
||||
{
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
|
@ -299,6 +371,29 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist)
|
|||
}
|
||||
}
|
||||
|
||||
static struct hist_entry*
|
||||
get_pair_data(struct hist_entry *he, struct data__file *d)
|
||||
{
|
||||
if (hist_entry__has_pairs(he)) {
|
||||
struct hist_entry *pair;
|
||||
|
||||
list_for_each_entry(pair, &he->pairs.head, pairs.node)
|
||||
if (pair->hists == d->hists)
|
||||
return pair;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct hist_entry*
|
||||
get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt)
|
||||
{
|
||||
void *ptr = dfmt - dfmt->idx;
|
||||
struct data__file *d = container_of(ptr, struct data__file, fmt);
|
||||
|
||||
return get_pair_data(he, d);
|
||||
}
|
||||
|
||||
static void hists__baseline_only(struct hists *hists)
|
||||
{
|
||||
struct rb_root *root;
|
||||
|
@ -333,22 +428,24 @@ static void hists__precompute(struct hists *hists)
|
|||
|
||||
next = rb_first(root);
|
||||
while (next != NULL) {
|
||||
struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in);
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
struct hist_entry *he, *pair;
|
||||
|
||||
he = rb_entry(next, struct hist_entry, rb_node_in);
|
||||
next = rb_next(&he->rb_node_in);
|
||||
|
||||
pair = get_pair_data(he, &data__files[sort_compute]);
|
||||
if (!pair)
|
||||
continue;
|
||||
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
perf_diff__compute_delta(he, pair);
|
||||
compute_delta(he, pair);
|
||||
break;
|
||||
case COMPUTE_RATIO:
|
||||
perf_diff__compute_ratio(he, pair);
|
||||
compute_ratio(he, pair);
|
||||
break;
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
perf_diff__compute_wdiff(he, pair);
|
||||
compute_wdiff(he, pair);
|
||||
break;
|
||||
default:
|
||||
BUG_ON(1);
|
||||
|
@ -367,7 +464,7 @@ static int64_t cmp_doubles(double l, double r)
|
|||
}
|
||||
|
||||
static int64_t
|
||||
hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
|
||||
__hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
|
||||
int c)
|
||||
{
|
||||
switch (c) {
|
||||
|
@ -399,6 +496,36 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right,
|
||||
int c)
|
||||
{
|
||||
bool pairs_left = hist_entry__has_pairs(left);
|
||||
bool pairs_right = hist_entry__has_pairs(right);
|
||||
struct hist_entry *p_right, *p_left;
|
||||
|
||||
if (!pairs_left && !pairs_right)
|
||||
return 0;
|
||||
|
||||
if (!pairs_left || !pairs_right)
|
||||
return pairs_left ? -1 : 1;
|
||||
|
||||
p_left = get_pair_data(left, &data__files[sort_compute]);
|
||||
p_right = get_pair_data(right, &data__files[sort_compute]);
|
||||
|
||||
if (!p_left && !p_right)
|
||||
return 0;
|
||||
|
||||
if (!p_left || !p_right)
|
||||
return p_left ? -1 : 1;
|
||||
|
||||
/*
|
||||
* We have 2 entries of same kind, let's
|
||||
* make the data comparison.
|
||||
*/
|
||||
return __hist_entry__cmp_compute(p_left, p_right, c);
|
||||
}
|
||||
|
||||
static void insert_hist_entry_by_compute(struct rb_root *root,
|
||||
struct hist_entry *he,
|
||||
int c)
|
||||
|
@ -448,75 +575,121 @@ static void hists__compute_resort(struct hists *hists)
|
|||
}
|
||||
}
|
||||
|
||||
static void hists__process(struct hists *old, struct hists *new)
|
||||
static void hists__process(struct hists *hists)
|
||||
{
|
||||
hists__match(new, old);
|
||||
|
||||
if (show_baseline_only)
|
||||
hists__baseline_only(new);
|
||||
else
|
||||
hists__link(new, old);
|
||||
hists__baseline_only(hists);
|
||||
|
||||
if (sort_compute) {
|
||||
hists__precompute(new);
|
||||
hists__compute_resort(new);
|
||||
hists__precompute(hists);
|
||||
hists__compute_resort(hists);
|
||||
} else {
|
||||
hists__output_resort(new);
|
||||
hists__output_resort(hists);
|
||||
}
|
||||
|
||||
hists__fprintf(new, true, 0, 0, 0, stdout);
|
||||
hists__fprintf(hists, true, 0, 0, 0, stdout);
|
||||
}
|
||||
|
||||
static void data__fprintf(void)
|
||||
{
|
||||
struct data__file *d;
|
||||
int i;
|
||||
|
||||
fprintf(stdout, "# Data files:\n");
|
||||
|
||||
data__for_each_file(i, d)
|
||||
fprintf(stdout, "# [%d] %s %s\n",
|
||||
d->idx, d->file,
|
||||
!d->idx ? "(Baseline)" : "");
|
||||
|
||||
fprintf(stdout, "#\n");
|
||||
}
|
||||
|
||||
static void data_process(void)
|
||||
{
|
||||
struct perf_evlist *evlist_base = data__files[0].session->evlist;
|
||||
struct perf_evsel *evsel_base;
|
||||
bool first = true;
|
||||
|
||||
list_for_each_entry(evsel_base, &evlist_base->entries, node) {
|
||||
struct data__file *d;
|
||||
int i;
|
||||
|
||||
data__for_each_file_new(i, d) {
|
||||
struct perf_evlist *evlist = d->session->evlist;
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
evsel = evsel_match(evsel_base, evlist);
|
||||
if (!evsel)
|
||||
continue;
|
||||
|
||||
d->hists = &evsel->hists;
|
||||
|
||||
hists__match(&evsel_base->hists, &evsel->hists);
|
||||
|
||||
if (!show_baseline_only)
|
||||
hists__link(&evsel_base->hists,
|
||||
&evsel->hists);
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
|
||||
perf_evsel__name(evsel_base));
|
||||
|
||||
first = false;
|
||||
|
||||
if (verbose || data__files_cnt > 2)
|
||||
data__fprintf();
|
||||
|
||||
hists__process(&evsel_base->hists);
|
||||
}
|
||||
}
|
||||
|
||||
static void data__free(struct data__file *d)
|
||||
{
|
||||
int col;
|
||||
|
||||
for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) {
|
||||
struct diff_hpp_fmt *fmt = &d->fmt[col];
|
||||
|
||||
free(fmt->header);
|
||||
}
|
||||
}
|
||||
|
||||
static int __cmd_diff(void)
|
||||
{
|
||||
int ret, i;
|
||||
#define older (session[0])
|
||||
#define newer (session[1])
|
||||
struct perf_session *session[2];
|
||||
struct perf_evlist *evlist_new, *evlist_old;
|
||||
struct perf_evsel *evsel;
|
||||
bool first = true;
|
||||
struct data__file *d;
|
||||
int ret = -EINVAL, i;
|
||||
|
||||
older = perf_session__new(input_old, O_RDONLY, force, false,
|
||||
&tool);
|
||||
newer = perf_session__new(input_new, O_RDONLY, force, false,
|
||||
&tool);
|
||||
if (session[0] == NULL || session[1] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
ret = perf_session__process_events(session[i], &tool);
|
||||
if (ret)
|
||||
data__for_each_file(i, d) {
|
||||
d->session = perf_session__new(d->file, O_RDONLY, force,
|
||||
false, &tool);
|
||||
if (!d->session) {
|
||||
pr_err("Failed to open %s\n", d->file);
|
||||
ret = -ENOMEM;
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
ret = perf_session__process_events(d->session, &tool);
|
||||
if (ret) {
|
||||
pr_err("Failed to process %s\n", d->file);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
perf_evlist__collapse_resort(d->session->evlist);
|
||||
}
|
||||
|
||||
evlist_old = older->evlist;
|
||||
evlist_new = newer->evlist;
|
||||
data_process();
|
||||
|
||||
perf_evlist__collapse_resort(evlist_old);
|
||||
perf_evlist__collapse_resort(evlist_new);
|
||||
out_delete:
|
||||
data__for_each_file(i, d) {
|
||||
if (d->session)
|
||||
perf_session__delete(d->session);
|
||||
|
||||
list_for_each_entry(evsel, &evlist_new->entries, node) {
|
||||
struct perf_evsel *evsel_old;
|
||||
|
||||
evsel_old = evsel_match(evsel, evlist_old);
|
||||
if (!evsel_old)
|
||||
continue;
|
||||
|
||||
fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
|
||||
perf_evsel__name(evsel));
|
||||
|
||||
first = false;
|
||||
|
||||
hists__process(&evsel_old->hists, &evsel->hists);
|
||||
data__free(d);
|
||||
}
|
||||
|
||||
out_delete:
|
||||
for (i = 0; i < 2; ++i)
|
||||
perf_session__delete(session[i]);
|
||||
free(data__files);
|
||||
return ret;
|
||||
#undef older
|
||||
#undef newer
|
||||
}
|
||||
|
||||
static const char * const diff_usage[] = {
|
||||
|
@ -555,61 +728,310 @@ static const struct option options[] = {
|
|||
"columns '.' is reserved."),
|
||||
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
|
||||
"Look for files with symbols relative to this directory"),
|
||||
OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static void ui_init(void)
|
||||
static double baseline_percent(struct hist_entry *he)
|
||||
{
|
||||
/*
|
||||
* Display baseline/delta/ratio
|
||||
* formula/periods columns.
|
||||
*/
|
||||
perf_hpp__column_enable(PERF_HPP__BASELINE);
|
||||
struct hists *hists = he->hists;
|
||||
return 100.0 * he->stat.period / hists->stats.total_period;
|
||||
}
|
||||
|
||||
switch (compute) {
|
||||
case COMPUTE_DELTA:
|
||||
perf_hpp__column_enable(PERF_HPP__DELTA);
|
||||
static int hpp__color_baseline(struct perf_hpp_fmt *fmt,
|
||||
struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct diff_hpp_fmt *dfmt =
|
||||
container_of(fmt, struct diff_hpp_fmt, fmt);
|
||||
double percent = baseline_percent(he);
|
||||
char pfmt[20] = " ";
|
||||
|
||||
if (!he->dummy) {
|
||||
scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1);
|
||||
return percent_color_snprintf(hpp->buf, hpp->size,
|
||||
pfmt, percent);
|
||||
} else
|
||||
return scnprintf(hpp->buf, hpp->size, "%*s",
|
||||
dfmt->header_width, pfmt);
|
||||
}
|
||||
|
||||
static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size)
|
||||
{
|
||||
double percent = baseline_percent(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
|
||||
int ret = 0;
|
||||
|
||||
if (!he->dummy)
|
||||
ret = scnprintf(buf, size, fmt, percent);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size)
|
||||
{
|
||||
switch (idx) {
|
||||
case PERF_HPP_DIFF__PERIOD_BASELINE:
|
||||
scnprintf(buf, size, "%" PRIu64, he->stat.period);
|
||||
break;
|
||||
case COMPUTE_RATIO:
|
||||
perf_hpp__column_enable(PERF_HPP__RATIO);
|
||||
|
||||
default:
|
||||
break;
|
||||
case COMPUTE_WEIGHTED_DIFF:
|
||||
perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair,
|
||||
int idx, char *buf, size_t size)
|
||||
{
|
||||
double diff;
|
||||
double ratio;
|
||||
s64 wdiff;
|
||||
|
||||
switch (idx) {
|
||||
case PERF_HPP_DIFF__DELTA:
|
||||
if (pair->diff.computed)
|
||||
diff = pair->diff.period_ratio_delta;
|
||||
else
|
||||
diff = compute_delta(he, pair);
|
||||
|
||||
if (fabs(diff) >= 0.01)
|
||||
scnprintf(buf, size, "%+4.2F%%", diff);
|
||||
break;
|
||||
|
||||
case PERF_HPP_DIFF__RATIO:
|
||||
/* No point for ratio number if we are dummy.. */
|
||||
if (he->dummy)
|
||||
break;
|
||||
|
||||
if (pair->diff.computed)
|
||||
ratio = pair->diff.period_ratio;
|
||||
else
|
||||
ratio = compute_ratio(he, pair);
|
||||
|
||||
if (ratio > 0.0)
|
||||
scnprintf(buf, size, "%14.6F", ratio);
|
||||
break;
|
||||
|
||||
case PERF_HPP_DIFF__WEIGHTED_DIFF:
|
||||
/* No point for wdiff number if we are dummy.. */
|
||||
if (he->dummy)
|
||||
break;
|
||||
|
||||
if (pair->diff.computed)
|
||||
wdiff = pair->diff.wdiff;
|
||||
else
|
||||
wdiff = compute_wdiff(he, pair);
|
||||
|
||||
if (wdiff != 0)
|
||||
scnprintf(buf, size, "%14ld", wdiff);
|
||||
break;
|
||||
|
||||
case PERF_HPP_DIFF__FORMULA:
|
||||
formula_fprintf(he, pair, buf, size);
|
||||
break;
|
||||
|
||||
case PERF_HPP_DIFF__PERIOD:
|
||||
scnprintf(buf, size, "%" PRIu64, pair->stat.period);
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG_ON(1);
|
||||
};
|
||||
}
|
||||
|
||||
if (show_formula)
|
||||
perf_hpp__column_enable(PERF_HPP__FORMULA);
|
||||
static void
|
||||
__hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt,
|
||||
char *buf, size_t size)
|
||||
{
|
||||
struct hist_entry *pair = get_pair_fmt(he, dfmt);
|
||||
int idx = dfmt->idx;
|
||||
|
||||
if (show_period) {
|
||||
perf_hpp__column_enable(PERF_HPP__PERIOD);
|
||||
perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE);
|
||||
/* baseline is special */
|
||||
if (idx == PERF_HPP_DIFF__BASELINE)
|
||||
hpp__entry_baseline(he, buf, size);
|
||||
else {
|
||||
if (pair)
|
||||
hpp__entry_pair(he, pair, idx, buf, size);
|
||||
else
|
||||
hpp__entry_unpair(he, idx, buf, size);
|
||||
}
|
||||
}
|
||||
|
||||
static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp,
|
||||
struct hist_entry *he)
|
||||
{
|
||||
struct diff_hpp_fmt *dfmt =
|
||||
container_of(_fmt, struct diff_hpp_fmt, fmt);
|
||||
char buf[MAX_COL_WIDTH] = " ";
|
||||
|
||||
__hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH);
|
||||
|
||||
if (symbol_conf.field_sep)
|
||||
return scnprintf(hpp->buf, hpp->size, "%s", buf);
|
||||
else
|
||||
return scnprintf(hpp->buf, hpp->size, "%*s",
|
||||
dfmt->header_width, buf);
|
||||
}
|
||||
|
||||
static int hpp__header(struct perf_hpp_fmt *fmt,
|
||||
struct perf_hpp *hpp)
|
||||
{
|
||||
struct diff_hpp_fmt *dfmt =
|
||||
container_of(fmt, struct diff_hpp_fmt, fmt);
|
||||
|
||||
BUG_ON(!dfmt->header);
|
||||
return scnprintf(hpp->buf, hpp->size, dfmt->header);
|
||||
}
|
||||
|
||||
static int hpp__width(struct perf_hpp_fmt *fmt,
|
||||
struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
struct diff_hpp_fmt *dfmt =
|
||||
container_of(fmt, struct diff_hpp_fmt, fmt);
|
||||
|
||||
BUG_ON(dfmt->header_width <= 0);
|
||||
return dfmt->header_width;
|
||||
}
|
||||
|
||||
static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt)
|
||||
{
|
||||
#define MAX_HEADER_NAME 100
|
||||
char buf_indent[MAX_HEADER_NAME];
|
||||
char buf[MAX_HEADER_NAME];
|
||||
const char *header = NULL;
|
||||
int width = 0;
|
||||
|
||||
BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX);
|
||||
header = columns[dfmt->idx].name;
|
||||
width = columns[dfmt->idx].width;
|
||||
|
||||
/* Only our defined HPP fmts should appear here. */
|
||||
BUG_ON(!header);
|
||||
|
||||
if (data__files_cnt > 2)
|
||||
scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx);
|
||||
|
||||
#define NAME (data__files_cnt > 2 ? buf : header)
|
||||
dfmt->header_width = width;
|
||||
width = (int) strlen(NAME);
|
||||
if (dfmt->header_width < width)
|
||||
dfmt->header_width = width;
|
||||
|
||||
scnprintf(buf_indent, MAX_HEADER_NAME, "%*s",
|
||||
dfmt->header_width, NAME);
|
||||
|
||||
dfmt->header = strdup(buf_indent);
|
||||
#undef MAX_HEADER_NAME
|
||||
#undef NAME
|
||||
}
|
||||
|
||||
static void data__hpp_register(struct data__file *d, int idx)
|
||||
{
|
||||
struct diff_hpp_fmt *dfmt = &d->fmt[idx];
|
||||
struct perf_hpp_fmt *fmt = &dfmt->fmt;
|
||||
|
||||
dfmt->idx = idx;
|
||||
|
||||
fmt->header = hpp__header;
|
||||
fmt->width = hpp__width;
|
||||
fmt->entry = hpp__entry_global;
|
||||
|
||||
/* TODO more colors */
|
||||
if (idx == PERF_HPP_DIFF__BASELINE)
|
||||
fmt->color = hpp__color_baseline;
|
||||
|
||||
init_header(d, dfmt);
|
||||
perf_hpp__column_register(fmt);
|
||||
}
|
||||
|
||||
static void ui_init(void)
|
||||
{
|
||||
struct data__file *d;
|
||||
int i;
|
||||
|
||||
data__for_each_file(i, d) {
|
||||
|
||||
/*
|
||||
* Baseline or compute realted columns:
|
||||
*
|
||||
* PERF_HPP_DIFF__BASELINE
|
||||
* PERF_HPP_DIFF__DELTA
|
||||
* PERF_HPP_DIFF__RATIO
|
||||
* PERF_HPP_DIFF__WEIGHTED_DIFF
|
||||
*/
|
||||
data__hpp_register(d, i ? compute_2_hpp[compute] :
|
||||
PERF_HPP_DIFF__BASELINE);
|
||||
|
||||
/*
|
||||
* And the rest:
|
||||
*
|
||||
* PERF_HPP_DIFF__FORMULA
|
||||
* PERF_HPP_DIFF__PERIOD
|
||||
* PERF_HPP_DIFF__PERIOD_BASELINE
|
||||
*/
|
||||
if (show_formula && i)
|
||||
data__hpp_register(d, PERF_HPP_DIFF__FORMULA);
|
||||
|
||||
if (show_period)
|
||||
data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD :
|
||||
PERF_HPP_DIFF__PERIOD_BASELINE);
|
||||
}
|
||||
}
|
||||
|
||||
static int data_init(int argc, const char **argv)
|
||||
{
|
||||
struct data__file *d;
|
||||
static const char *defaults[] = {
|
||||
"perf.data.old",
|
||||
"perf.data",
|
||||
};
|
||||
bool use_default = true;
|
||||
int i;
|
||||
|
||||
data__files_cnt = 2;
|
||||
|
||||
if (argc) {
|
||||
if (argc == 1)
|
||||
defaults[1] = argv[0];
|
||||
else {
|
||||
data__files_cnt = argc;
|
||||
use_default = false;
|
||||
}
|
||||
} else if (symbol_conf.default_guest_vmlinux_name ||
|
||||
symbol_conf.default_guest_kallsyms) {
|
||||
defaults[0] = "perf.data.host";
|
||||
defaults[1] = "perf.data.guest";
|
||||
}
|
||||
|
||||
if (sort_compute >= (unsigned int) data__files_cnt) {
|
||||
pr_err("Order option out of limit.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data__files = zalloc(sizeof(*data__files) * data__files_cnt);
|
||||
if (!data__files)
|
||||
return -ENOMEM;
|
||||
|
||||
data__for_each_file(i, d) {
|
||||
d->file = use_default ? defaults[i] : argv[i];
|
||||
d->idx = i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
sort_order = diff__default_sort_order;
|
||||
argc = parse_options(argc, argv, options, diff_usage, 0);
|
||||
if (argc) {
|
||||
if (argc > 2)
|
||||
usage_with_options(diff_usage, options);
|
||||
if (argc == 2) {
|
||||
input_old = argv[0];
|
||||
input_new = argv[1];
|
||||
} else
|
||||
input_new = argv[0];
|
||||
} else if (symbol_conf.default_guest_vmlinux_name ||
|
||||
symbol_conf.default_guest_kallsyms) {
|
||||
input_old = "perf.data.host";
|
||||
input_new = "perf.data.guest";
|
||||
}
|
||||
|
||||
if (symbol__init() < 0)
|
||||
return -1;
|
||||
|
||||
if (data_init(argc, argv) < 0)
|
||||
return -1;
|
||||
|
||||
ui_init();
|
||||
|
||||
if (setup_sorting() < 0)
|
||||
|
|
|
@ -38,8 +38,7 @@ struct event_entry {
|
|||
};
|
||||
|
||||
static int perf_event__repipe_synth(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct machine *machine __maybe_unused)
|
||||
union perf_event *event)
|
||||
{
|
||||
struct perf_inject *inject = container_of(tool, struct perf_inject, tool);
|
||||
uint32_t size;
|
||||
|
@ -65,39 +64,28 @@ static int perf_event__repipe_op2_synth(struct perf_tool *tool,
|
|||
struct perf_session *session
|
||||
__maybe_unused)
|
||||
{
|
||||
return perf_event__repipe_synth(tool, event, NULL);
|
||||
return perf_event__repipe_synth(tool, event);
|
||||
}
|
||||
|
||||
static int perf_event__repipe_event_type_synth(struct perf_tool *tool,
|
||||
union perf_event *event)
|
||||
{
|
||||
return perf_event__repipe_synth(tool, event, NULL);
|
||||
}
|
||||
|
||||
static int perf_event__repipe_tracing_data_synth(union perf_event *event,
|
||||
struct perf_session *session
|
||||
__maybe_unused)
|
||||
{
|
||||
return perf_event__repipe_synth(NULL, event, NULL);
|
||||
}
|
||||
|
||||
static int perf_event__repipe_attr(union perf_event *event,
|
||||
struct perf_evlist **pevlist __maybe_unused)
|
||||
static int perf_event__repipe_attr(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_evlist **pevlist)
|
||||
{
|
||||
int ret;
|
||||
ret = perf_event__process_attr(event, pevlist);
|
||||
|
||||
ret = perf_event__process_attr(tool, event, pevlist);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return perf_event__repipe_synth(NULL, event, NULL);
|
||||
return perf_event__repipe_synth(tool, event);
|
||||
}
|
||||
|
||||
static int perf_event__repipe(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine)
|
||||
struct machine *machine __maybe_unused)
|
||||
{
|
||||
return perf_event__repipe_synth(tool, event, machine);
|
||||
return perf_event__repipe_synth(tool, event);
|
||||
}
|
||||
|
||||
typedef int (*inject_handler)(struct perf_tool *tool,
|
||||
|
@ -119,7 +107,7 @@ static int perf_event__repipe_sample(struct perf_tool *tool,
|
|||
|
||||
build_id__mark_dso_hit(tool, event, sample, evsel, machine);
|
||||
|
||||
return perf_event__repipe_synth(tool, event, machine);
|
||||
return perf_event__repipe_synth(tool, event);
|
||||
}
|
||||
|
||||
static int perf_event__repipe_mmap(struct perf_tool *tool,
|
||||
|
@ -148,13 +136,14 @@ static int perf_event__repipe_fork(struct perf_tool *tool,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int perf_event__repipe_tracing_data(union perf_event *event,
|
||||
static int perf_event__repipe_tracing_data(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_session *session)
|
||||
{
|
||||
int err;
|
||||
|
||||
perf_event__repipe_synth(NULL, event, NULL);
|
||||
err = perf_event__process_tracing_data(event, session);
|
||||
perf_event__repipe_synth(tool, event);
|
||||
err = perf_event__process_tracing_data(tool, event, session);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -407,8 +396,8 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
.throttle = perf_event__repipe,
|
||||
.unthrottle = perf_event__repipe,
|
||||
.attr = perf_event__repipe_attr,
|
||||
.event_type = perf_event__repipe_event_type_synth,
|
||||
.tracing_data = perf_event__repipe_tracing_data_synth,
|
||||
.tracing_data = perf_event__repipe_op2_synth,
|
||||
.finished_round = perf_event__repipe_op2_synth,
|
||||
.build_id = perf_event__repipe_op2_synth,
|
||||
},
|
||||
.input_name = "-",
|
||||
|
|
|
@ -313,7 +313,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
|||
return -1;
|
||||
}
|
||||
|
||||
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
|
||||
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid);
|
||||
|
||||
if (evsel->handler.func != NULL) {
|
||||
tracepoint_handler f = evsel->handler.func;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "util/parse-events.h"
|
||||
#include "util/cache.h"
|
||||
#include "util/pmu.h"
|
||||
|
||||
int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
|
@ -37,6 +38,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
else if (strcmp(argv[i], "cache") == 0 ||
|
||||
strcmp(argv[i], "hwcache") == 0)
|
||||
print_hwcache_events(NULL, false);
|
||||
else if (strcmp(argv[i], "pmu") == 0)
|
||||
print_pmu_events(NULL, false);
|
||||
else if (strcmp(argv[i], "--raw-dump") == 0)
|
||||
print_events(NULL, true);
|
||||
else {
|
||||
|
|
|
@ -474,13 +474,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
|||
goto out_delete_session;
|
||||
}
|
||||
|
||||
err = perf_event__synthesize_event_types(tool, process_synthesized_event,
|
||||
machine);
|
||||
if (err < 0) {
|
||||
pr_err("Couldn't synthesize event_types.\n");
|
||||
goto out_delete_session;
|
||||
}
|
||||
|
||||
if (have_tracepoints(&evsel_list->entries)) {
|
||||
/*
|
||||
* FIXME err <= 0 here actually means that
|
||||
|
@ -904,7 +897,6 @@ const struct option record_options[] = {
|
|||
int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
int err = -ENOMEM;
|
||||
struct perf_evsel *pos;
|
||||
struct perf_evlist *evsel_list;
|
||||
struct perf_record *rec = &record;
|
||||
char errbuf[BUFSIZ];
|
||||
|
@ -968,11 +960,6 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0)
|
||||
usage_with_options(record_usage, record_options);
|
||||
|
||||
list_for_each_entry(pos, &evsel_list->entries, node) {
|
||||
if (perf_header__push_event(pos->attr.config, perf_evsel__name(pos)))
|
||||
goto out_free_fd;
|
||||
}
|
||||
|
||||
if (rec->opts.user_interval != ULLONG_MAX)
|
||||
rec->opts.default_interval = rec->opts.user_interval;
|
||||
if (rec->opts.user_freq != UINT_MAX)
|
||||
|
|
|
@ -89,7 +89,7 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool,
|
|||
if ((sort__has_parent || symbol_conf.use_callchain) &&
|
||||
sample->callchain) {
|
||||
err = machine__resolve_callchain(machine, evsel, al->thread,
|
||||
sample, &parent);
|
||||
sample, &parent, al);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
|
|||
if ((sort__has_parent || symbol_conf.use_callchain)
|
||||
&& sample->callchain) {
|
||||
err = machine__resolve_callchain(machine, evsel, al->thread,
|
||||
sample, &parent);
|
||||
sample, &parent, al);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
|
|||
|
||||
if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
|
||||
err = machine__resolve_callchain(machine, evsel, al->thread,
|
||||
sample, &parent);
|
||||
sample, &parent, al);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
@ -497,7 +497,7 @@ static int __cmd_report(struct perf_report *rep)
|
|||
ret = perf_session__cpu_bitmap(session, rep->cpu_list,
|
||||
rep->cpu_bitmap);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (use_browser <= 0)
|
||||
|
@ -508,11 +508,11 @@ static int __cmd_report(struct perf_report *rep)
|
|||
|
||||
ret = perf_report__setup_sample_type(rep);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
return ret;
|
||||
|
||||
ret = perf_session__process_events(session, &rep->tool);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
return ret;
|
||||
|
||||
kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION];
|
||||
kernel_kmap = map__kmap(kernel_map);
|
||||
|
@ -547,7 +547,7 @@ static int __cmd_report(struct perf_report *rep)
|
|||
|
||||
if (dump_trace) {
|
||||
perf_session__fprintf_nr_events(session, stdout);
|
||||
goto out_delete;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nr_samples = 0;
|
||||
|
@ -572,7 +572,7 @@ static int __cmd_report(struct perf_report *rep)
|
|||
|
||||
if (nr_samples == 0) {
|
||||
ui__error("The %s file has no samples!\n", session->filename);
|
||||
goto out_delete;
|
||||
return 0;
|
||||
}
|
||||
|
||||
list_for_each_entry(pos, &session->evlist->entries, node)
|
||||
|
@ -598,19 +598,6 @@ static int __cmd_report(struct perf_report *rep)
|
|||
} else
|
||||
perf_evlist__tty_browse_hists(session->evlist, rep, help);
|
||||
|
||||
out_delete:
|
||||
/*
|
||||
* Speed up the exit process, for large files this can
|
||||
* take quite a while.
|
||||
*
|
||||
* XXX Enable this when using valgrind or if we ever
|
||||
* librarize this command.
|
||||
*
|
||||
* Also experiment with obstacks to see how much speed
|
||||
* up we'll get here.
|
||||
*
|
||||
* perf_session__delete(session);
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -694,6 +681,24 @@ setup:
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
report_parse_ignore_callees_opt(const struct option *opt __maybe_unused,
|
||||
const char *arg, int unset __maybe_unused)
|
||||
{
|
||||
if (arg) {
|
||||
int err = regcomp(&ignore_callees_regex, arg, REG_EXTENDED);
|
||||
if (err) {
|
||||
char buf[BUFSIZ];
|
||||
regerror(err, &ignore_callees_regex, buf, sizeof(buf));
|
||||
pr_err("Invalid --ignore-callees regex: %s\n%s", arg, buf);
|
||||
return -1;
|
||||
}
|
||||
have_ignore_callees = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_branch_mode(const struct option *opt __maybe_unused,
|
||||
const char *str __maybe_unused, int unset)
|
||||
|
@ -736,7 +741,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
.lost = perf_event__process_lost,
|
||||
.read = process_read_event,
|
||||
.attr = perf_event__process_attr,
|
||||
.event_type = perf_event__process_event_type,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
|
@ -784,6 +788,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
"Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt),
|
||||
OPT_BOOLEAN('G', "inverted", &report.inverted_callchain,
|
||||
"alias for inverted call graph"),
|
||||
OPT_CALLBACK(0, "ignore-callees", NULL, "regex",
|
||||
"ignore callees of these functions in call graphs",
|
||||
report_parse_ignore_callees_opt),
|
||||
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
|
||||
"only consider symbols in these dsos"),
|
||||
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
|
||||
|
@ -853,7 +860,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
setup_browser(true);
|
||||
else {
|
||||
use_browser = 0;
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
perf_hpp__init();
|
||||
}
|
||||
|
||||
|
@ -931,14 +937,6 @@ repeat:
|
|||
if (parent_pattern != default_parent_pattern) {
|
||||
if (sort_dimension__add("parent") < 0)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* Only show the parent fields if we explicitly
|
||||
* sort that way. If we only use parent machinery
|
||||
* for filtering, we don't want it.
|
||||
*/
|
||||
if (!strstr(sort_order, "parent"))
|
||||
sort_parent.elide = 1;
|
||||
}
|
||||
|
||||
if (argc) {
|
||||
|
|
|
@ -1075,7 +1075,7 @@ static int latency_migrate_task_event(struct perf_sched *sched,
|
|||
if (!atoms) {
|
||||
if (thread_atoms_insert(sched, migrant))
|
||||
return -1;
|
||||
register_pid(sched, migrant->pid, migrant->comm);
|
||||
register_pid(sched, migrant->tid, migrant->comm);
|
||||
atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
|
||||
if (!atoms) {
|
||||
pr_err("migration-event: Internal tree error");
|
||||
|
@ -1115,7 +1115,7 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_
|
|||
sched->all_runtime += work_list->total_runtime;
|
||||
sched->all_count += work_list->nb_atoms;
|
||||
|
||||
ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->pid);
|
||||
ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->tid);
|
||||
|
||||
for (i = 0; i < 24 - ret; i++)
|
||||
printf(" ");
|
||||
|
@ -1131,9 +1131,9 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_
|
|||
|
||||
static int pid_cmp(struct work_atoms *l, struct work_atoms *r)
|
||||
{
|
||||
if (l->thread->pid < r->thread->pid)
|
||||
if (l->thread->tid < r->thread->tid)
|
||||
return -1;
|
||||
if (l->thread->pid > r->thread->pid)
|
||||
if (l->thread->tid > r->thread->tid)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -1321,7 +1321,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
|||
printf("*");
|
||||
|
||||
if (sched->curr_thread[cpu]) {
|
||||
if (sched->curr_thread[cpu]->pid)
|
||||
if (sched->curr_thread[cpu]->tid)
|
||||
printf("%2s ", sched->curr_thread[cpu]->shortname);
|
||||
else
|
||||
printf(". ");
|
||||
|
@ -1332,7 +1332,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
|||
printf(" %12.6f secs ", (double)timestamp/1e9);
|
||||
if (new_shortname) {
|
||||
printf("%s => %s:%d\n",
|
||||
sched_in->shortname, sched_in->comm, sched_in->pid);
|
||||
sched_in->shortname, sched_in->comm, sched_in->tid);
|
||||
} else {
|
||||
printf("\n");
|
||||
}
|
||||
|
@ -1662,28 +1662,29 @@ static int __cmd_record(int argc, const char **argv)
|
|||
return cmd_record(i, rec_argv, NULL);
|
||||
}
|
||||
|
||||
static const char default_sort_order[] = "avg, max, switch, runtime";
|
||||
static struct perf_sched sched = {
|
||||
.tool = {
|
||||
.sample = perf_sched__process_tracepoint_sample,
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_event__process_fork,
|
||||
.ordered_samples = true,
|
||||
},
|
||||
.cmp_pid = LIST_HEAD_INIT(sched.cmp_pid),
|
||||
.sort_list = LIST_HEAD_INIT(sched.sort_list),
|
||||
.start_work_mutex = PTHREAD_MUTEX_INITIALIZER,
|
||||
.work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER,
|
||||
.curr_pid = { [0 ... MAX_CPUS - 1] = -1 },
|
||||
.sort_order = default_sort_order,
|
||||
.replay_repeat = 10,
|
||||
.profile_cpu = -1,
|
||||
.next_shortname1 = 'A',
|
||||
.next_shortname2 = '0',
|
||||
};
|
||||
|
||||
int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
const char default_sort_order[] = "avg, max, switch, runtime";
|
||||
struct perf_sched sched = {
|
||||
.tool = {
|
||||
.sample = perf_sched__process_tracepoint_sample,
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_event__process_fork,
|
||||
.ordered_samples = true,
|
||||
},
|
||||
.cmp_pid = LIST_HEAD_INIT(sched.cmp_pid),
|
||||
.sort_list = LIST_HEAD_INIT(sched.sort_list),
|
||||
.start_work_mutex = PTHREAD_MUTEX_INITIALIZER,
|
||||
.work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER,
|
||||
.curr_pid = { [0 ... MAX_CPUS - 1] = -1 },
|
||||
.sort_order = default_sort_order,
|
||||
.replay_repeat = 10,
|
||||
.profile_cpu = -1,
|
||||
.next_shortname1 = 'A',
|
||||
.next_shortname2 = '0',
|
||||
};
|
||||
const struct option latency_options[] = {
|
||||
OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]",
|
||||
"sort by key(s): runtime, switch, avg, max"),
|
||||
|
|
|
@ -24,6 +24,7 @@ static u64 last_timestamp;
|
|||
static u64 nr_unordered;
|
||||
extern const struct option record_options[];
|
||||
static bool no_callchain;
|
||||
static bool latency_format;
|
||||
static bool system_wide;
|
||||
static const char *cpu_list;
|
||||
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
|
@ -523,7 +524,6 @@ static struct perf_tool perf_script = {
|
|||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.attr = perf_event__process_attr,
|
||||
.event_type = perf_event__process_event_type,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
* of the License.
|
||||
*/
|
||||
|
||||
#include <traceevent/event-parse.h>
|
||||
|
||||
#include "builtin.h"
|
||||
|
||||
#include "util/util.h"
|
||||
|
@ -19,6 +21,7 @@
|
|||
#include "util/color.h"
|
||||
#include <linux/list.h>
|
||||
#include "util/cache.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/evsel.h"
|
||||
#include <linux/rbtree.h>
|
||||
#include "util/symbol.h"
|
||||
|
@ -328,25 +331,6 @@ struct wakeup_entry {
|
|||
int success;
|
||||
};
|
||||
|
||||
/*
|
||||
* trace_flag_type is an enumeration that holds different
|
||||
* states when a trace occurs. These are:
|
||||
* IRQS_OFF - interrupts were disabled
|
||||
* IRQS_NOSUPPORT - arch does not support irqs_disabled_flags
|
||||
* NEED_RESCED - reschedule is requested
|
||||
* HARDIRQ - inside an interrupt handler
|
||||
* SOFTIRQ - inside a softirq handler
|
||||
*/
|
||||
enum trace_flag_type {
|
||||
TRACE_FLAG_IRQS_OFF = 0x01,
|
||||
TRACE_FLAG_IRQS_NOSUPPORT = 0x02,
|
||||
TRACE_FLAG_NEED_RESCHED = 0x04,
|
||||
TRACE_FLAG_HARDIRQ = 0x08,
|
||||
TRACE_FLAG_SOFTIRQ = 0x10,
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct sched_switch {
|
||||
struct trace_entry te;
|
||||
char prev_comm[TASK_COMM_LEN];
|
||||
|
@ -479,6 +463,8 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
|
|||
}
|
||||
}
|
||||
|
||||
typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample);
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
|
@ -486,8 +472,6 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
|||
struct perf_evsel *evsel,
|
||||
struct machine *machine __maybe_unused)
|
||||
{
|
||||
struct trace_entry *te;
|
||||
|
||||
if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
|
||||
if (!first_time || first_time > sample->time)
|
||||
first_time = sample->time;
|
||||
|
@ -495,69 +479,90 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
|||
last_time = sample->time;
|
||||
}
|
||||
|
||||
te = (void *)sample->raw_data;
|
||||
if ((evsel->attr.sample_type & PERF_SAMPLE_RAW) && sample->raw_size > 0) {
|
||||
char *event_str;
|
||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||
struct power_entry_old *peo;
|
||||
peo = (void *)te;
|
||||
#endif
|
||||
/*
|
||||
* FIXME: use evsel, its already mapped from id to perf_evsel,
|
||||
* remove perf_header__find_event infrastructure bits.
|
||||
* Mapping all these "power:cpu_idle" strings to the tracepoint
|
||||
* ID and then just comparing against evsel->attr.config.
|
||||
*
|
||||
* e.g.:
|
||||
*
|
||||
* if (evsel->attr.config == power_cpu_idle_id)
|
||||
*/
|
||||
event_str = perf_header__find_event(te->type);
|
||||
if (sample->cpu > numcpus)
|
||||
numcpus = sample->cpu;
|
||||
|
||||
if (!event_str)
|
||||
return 0;
|
||||
|
||||
if (sample->cpu > numcpus)
|
||||
numcpus = sample->cpu;
|
||||
|
||||
if (strcmp(event_str, "power:cpu_idle") == 0) {
|
||||
struct power_processor_entry *ppe = (void *)te;
|
||||
if (ppe->state == (u32)PWR_EVENT_EXIT)
|
||||
c_state_end(ppe->cpu_id, sample->time);
|
||||
else
|
||||
c_state_start(ppe->cpu_id, sample->time,
|
||||
ppe->state);
|
||||
}
|
||||
else if (strcmp(event_str, "power:cpu_frequency") == 0) {
|
||||
struct power_processor_entry *ppe = (void *)te;
|
||||
p_state_change(ppe->cpu_id, sample->time, ppe->state);
|
||||
}
|
||||
|
||||
else if (strcmp(event_str, "sched:sched_wakeup") == 0)
|
||||
sched_wakeup(sample->cpu, sample->time, sample->pid, te);
|
||||
|
||||
else if (strcmp(event_str, "sched:sched_switch") == 0)
|
||||
sched_switch(sample->cpu, sample->time, te);
|
||||
|
||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||
if (use_old_power_events) {
|
||||
if (strcmp(event_str, "power:power_start") == 0)
|
||||
c_state_start(peo->cpu_id, sample->time,
|
||||
peo->value);
|
||||
|
||||
else if (strcmp(event_str, "power:power_end") == 0)
|
||||
c_state_end(sample->cpu, sample->time);
|
||||
|
||||
else if (strcmp(event_str,
|
||||
"power:power_frequency") == 0)
|
||||
p_state_change(peo->cpu_id, sample->time,
|
||||
peo->value);
|
||||
}
|
||||
#endif
|
||||
if (evsel->handler.func != NULL) {
|
||||
tracepoint_handler f = evsel->handler.func;
|
||||
return f(evsel, sample);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct power_processor_entry *ppe = sample->raw_data;
|
||||
|
||||
if (ppe->state == (u32) PWR_EVENT_EXIT)
|
||||
c_state_end(ppe->cpu_id, sample->time);
|
||||
else
|
||||
c_state_start(ppe->cpu_id, sample->time, ppe->state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct power_processor_entry *ppe = sample->raw_data;
|
||||
|
||||
p_state_change(ppe->cpu_id, sample->time, ppe->state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
process_sample_sched_wakeup(struct perf_evsel *evsel __maybe_unused,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct trace_entry *te = sample->raw_data;
|
||||
|
||||
sched_wakeup(sample->cpu, sample->time, sample->pid, te);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct trace_entry *te = sample->raw_data;
|
||||
|
||||
sched_switch(sample->cpu, sample->time, te);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||
static int
|
||||
process_sample_power_start(struct perf_evsel *evsel __maybe_unused,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct power_entry_old *peo = sample->raw_data;
|
||||
|
||||
c_state_start(peo->cpu_id, sample->time, peo->value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
process_sample_power_end(struct perf_evsel *evsel __maybe_unused,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
c_state_end(sample->cpu, sample->time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
struct power_entry_old *peo = sample->raw_data;
|
||||
|
||||
p_state_change(peo->cpu_id, sample->time, peo->value);
|
||||
return 0;
|
||||
}
|
||||
#endif /* SUPPORT_OLD_POWER_EVENTS */
|
||||
|
||||
/*
|
||||
* After the last sample we need to wrap up the current C/P state
|
||||
* and close out each CPU for these.
|
||||
|
@ -974,6 +979,17 @@ static int __cmd_timechart(const char *output_name)
|
|||
.sample = process_sample_event,
|
||||
.ordered_samples = true,
|
||||
};
|
||||
const struct perf_evsel_str_handler power_tracepoints[] = {
|
||||
{ "power:cpu_idle", process_sample_cpu_idle },
|
||||
{ "power:cpu_frequency", process_sample_cpu_frequency },
|
||||
{ "sched:sched_wakeup", process_sample_sched_wakeup },
|
||||
{ "sched:sched_switch", process_sample_sched_switch },
|
||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||
{ "power:power_start", process_sample_power_start },
|
||||
{ "power:power_end", process_sample_power_end },
|
||||
{ "power:power_frequency", process_sample_power_frequency },
|
||||
#endif
|
||||
};
|
||||
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
|
||||
0, false, &perf_timechart);
|
||||
int ret = -EINVAL;
|
||||
|
@ -984,6 +1000,12 @@ static int __cmd_timechart(const char *output_name)
|
|||
if (!perf_session__has_traces(session, "timechart record"))
|
||||
goto out_delete;
|
||||
|
||||
if (perf_session__set_tracepoints_handlers(session,
|
||||
power_tracepoints)) {
|
||||
pr_err("Initializing session tracepoint handlers failed\n");
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
ret = perf_session__process_events(session, &perf_timechart);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "util/xyarray.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/intlist.h"
|
||||
#include "arch/common.h"
|
||||
|
||||
#include "util/debug.h"
|
||||
|
||||
|
@ -772,8 +773,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
|
|||
sample->callchain) {
|
||||
err = machine__resolve_callchain(machine, evsel,
|
||||
al.thread, sample,
|
||||
&parent);
|
||||
|
||||
&parent, &al);
|
||||
if (err)
|
||||
return;
|
||||
}
|
||||
|
@ -939,6 +939,12 @@ static int __cmd_top(struct perf_top *top)
|
|||
if (top->session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!objdump_path) {
|
||||
ret = perf_session_env__lookup_objdump(&top->session->header.env);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
ret = perf_top__setup_sample_type(top);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
@ -1102,6 +1108,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts,
|
||||
"mode[,dump_size]", record_callchain_help,
|
||||
&parse_callchain_opt, "fp"),
|
||||
OPT_CALLBACK(0, "ignore-callees", NULL, "regex",
|
||||
"ignore callees of these functions in call graphs",
|
||||
report_parse_ignore_callees_opt),
|
||||
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
|
||||
"Show a column with the sum of periods"),
|
||||
OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
|
||||
|
@ -1114,6 +1123,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
|||
"Interleave source code with assembly code (default)"),
|
||||
OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw,
|
||||
"Display raw encoding of assembly instructions (default)"),
|
||||
OPT_STRING(0, "objdump", &objdump_path, "path",
|
||||
"objdump binary to use for disassembly and annotations"),
|
||||
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
|
||||
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
||||
OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <traceevent/event-parse.h>
|
||||
#include "builtin.h"
|
||||
#include "util/color.h"
|
||||
#include "util/evlist.h"
|
||||
|
@ -5,7 +6,6 @@
|
|||
#include "util/thread.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/thread_map.h"
|
||||
#include "event-parse.h"
|
||||
|
||||
#include <libaudit.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -142,7 +142,7 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre
|
|||
printed += fprintf_duration(duration, fp);
|
||||
|
||||
if (trace->multiple_threads)
|
||||
printed += fprintf(fp, "%d ", thread->pid);
|
||||
printed += fprintf(fp, "%d ", thread->tid);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
@ -593,7 +593,7 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp)
|
|||
color = PERF_COLOR_YELLOW;
|
||||
|
||||
printed += color_fprintf(fp, color, "%20s", thread->comm);
|
||||
printed += fprintf(fp, " - %-5d :%11lu [", thread->pid, ttrace->nr_events);
|
||||
printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events);
|
||||
printed += color_fprintf(fp, color, "%5.1f%%", ratio);
|
||||
printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms);
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ ifneq ($(obj-perf),)
|
|||
obj-perf := $(abspath $(obj-perf))/
|
||||
endif
|
||||
|
||||
LIB_INCLUDE := $(srctree)/tools/lib/
|
||||
|
||||
# include ARCH specific config
|
||||
-include $(src-perf)/arch/$(ARCH)/Makefile
|
||||
|
||||
|
@ -121,8 +123,7 @@ endif
|
|||
|
||||
CFLAGS += -I$(src-perf)/util
|
||||
CFLAGS += -I$(src-perf)
|
||||
CFLAGS += -I$(TRACE_EVENT_DIR)
|
||||
CFLAGS += -I$(srctree)/tools/lib/
|
||||
CFLAGS += -I$(LIB_INCLUDE)
|
||||
|
||||
CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
|
||||
|
||||
|
|
|
@ -10,14 +10,6 @@
|
|||
#include "symbol.h"
|
||||
#include "tests.h"
|
||||
|
||||
#define TEST_ASSERT_VAL(text, cond) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static char *test_file(int size)
|
||||
{
|
||||
static char buf_templ[] = "/tmp/test-XXXXXX";
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include <traceevent/event-parse.h>
|
||||
#include "evsel.h"
|
||||
#include "tests.h"
|
||||
#include "event-parse.h"
|
||||
|
||||
static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name,
|
||||
int size, bool should_be_signed)
|
||||
|
@ -49,7 +49,7 @@ int test__perf_evsel__tp_sched_test(void)
|
|||
if (perf_evsel__test_field(evsel, "prev_prio", 4, true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "prev_state", 8, true))
|
||||
if (perf_evsel__test_field(evsel, "prev_state", sizeof(long), true))
|
||||
ret = -1;
|
||||
|
||||
if (perf_evsel__test_field(evsel, "next_comm", 16, true))
|
||||
|
|
|
@ -7,14 +7,6 @@
|
|||
#include "tests.h"
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
#define TEST_ASSERT_VAL(text, cond) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \
|
||||
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD)
|
||||
|
||||
|
@ -1254,24 +1246,20 @@ static int test_events(struct evlist_test *events, unsigned cnt)
|
|||
|
||||
static int test_term(struct terms_test *t)
|
||||
{
|
||||
struct list_head *terms;
|
||||
struct list_head terms;
|
||||
int ret;
|
||||
|
||||
terms = malloc(sizeof(*terms));
|
||||
if (!terms)
|
||||
return -ENOMEM;
|
||||
INIT_LIST_HEAD(&terms);
|
||||
|
||||
INIT_LIST_HEAD(terms);
|
||||
|
||||
ret = parse_events_terms(terms, t->str);
|
||||
ret = parse_events_terms(&terms, t->str);
|
||||
if (ret) {
|
||||
pr_debug("failed to parse terms '%s', err %d\n",
|
||||
t->str , ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = t->check(terms);
|
||||
parse_events__free_terms(terms);
|
||||
ret = t->check(&terms);
|
||||
parse_events__free_terms(&terms);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
#ifndef TESTS_H
|
||||
#define TESTS_H
|
||||
|
||||
#define TEST_ASSERT_VAL(text, cond) \
|
||||
do { \
|
||||
if (!(cond)) { \
|
||||
pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
enum {
|
||||
TEST_OK = 0,
|
||||
TEST_FAIL = -1,
|
||||
|
|
|
@ -139,11 +139,18 @@ next_pair:
|
|||
* _really_ have a problem.
|
||||
*/
|
||||
s64 skew = sym->end - pair->end;
|
||||
if (llabs(skew) < page_size)
|
||||
continue;
|
||||
if (llabs(skew) >= page_size)
|
||||
pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
|
||||
sym->start, sym->name, sym->end, pair->end);
|
||||
|
||||
/*
|
||||
* Do not count this as a failure, because we
|
||||
* could really find a case where it's not
|
||||
* possible to get proper function end from
|
||||
* kallsyms.
|
||||
*/
|
||||
continue;
|
||||
|
||||
pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
|
||||
sym->start, sym->name, sym->end, pair->end);
|
||||
} else {
|
||||
struct rb_node *nnd;
|
||||
detour:
|
||||
|
|
|
@ -685,8 +685,10 @@ static u64 __hpp_get_##_field(struct hist_entry *he) \
|
|||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
static int \
|
||||
hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\
|
||||
struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \
|
||||
}
|
||||
|
@ -701,8 +703,6 @@ __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
|
|||
|
||||
void hist_browser__init_hpp(void)
|
||||
{
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
|
||||
perf_hpp__init();
|
||||
|
||||
perf_hpp__format[PERF_HPP__OVERHEAD].color =
|
||||
|
@ -762,9 +762,9 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
|||
first = false;
|
||||
|
||||
if (fmt->color) {
|
||||
width -= fmt->color(&hpp, entry);
|
||||
width -= fmt->color(fmt, &hpp, entry);
|
||||
} else {
|
||||
width -= fmt->entry(&hpp, entry);
|
||||
width -= fmt->entry(fmt, &hpp, entry);
|
||||
slsmg_printf("%s", s);
|
||||
}
|
||||
}
|
||||
|
@ -1256,7 +1256,7 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
|
|||
printed += scnprintf(bf + printed, size - printed,
|
||||
", Thread: %s(%d)",
|
||||
(thread->comm_set ? thread->comm : ""),
|
||||
thread->pid);
|
||||
thread->tid);
|
||||
if (dso)
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
", DSO: %s", dso->short_name);
|
||||
|
@ -1579,7 +1579,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
|||
asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
|
||||
(browser->hists->thread_filter ? "out of" : "into"),
|
||||
(thread->comm_set ? thread->comm : ""),
|
||||
thread->pid) > 0)
|
||||
thread->tid) > 0)
|
||||
zoom_thread = nr_options++;
|
||||
|
||||
if (dso != NULL &&
|
||||
|
@ -1702,7 +1702,7 @@ zoom_out_thread:
|
|||
} else {
|
||||
ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
|
||||
thread->comm_set ? thread->comm : "",
|
||||
thread->pid);
|
||||
thread->tid);
|
||||
browser->hists->thread_filter = thread;
|
||||
sort_thread.elide = true;
|
||||
pstack__push(fstack, &browser->hists->thread_filter);
|
||||
|
|
|
@ -91,7 +91,8 @@ static u64 he_get_##_field(struct hist_entry *he) \
|
|||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int perf_gtk__hpp_color_##_type(struct perf_hpp *hpp, \
|
||||
static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp, \
|
||||
struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__color_fmt(hpp, he, he_get_##_field); \
|
||||
|
@ -124,6 +125,81 @@ void perf_gtk__init_hpp(void)
|
|||
perf_gtk__hpp_color_overhead_guest_us;
|
||||
}
|
||||
|
||||
static void callchain_list__sym_name(struct callchain_list *cl,
|
||||
char *bf, size_t bfsize)
|
||||
{
|
||||
if (cl->ms.sym)
|
||||
scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
|
||||
else
|
||||
scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
|
||||
}
|
||||
|
||||
static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store,
|
||||
GtkTreeIter *parent, int col, u64 total)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
bool has_single_node = (rb_first(root) == rb_last(root));
|
||||
|
||||
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
|
||||
struct callchain_node *node;
|
||||
struct callchain_list *chain;
|
||||
GtkTreeIter iter, new_parent;
|
||||
bool need_new_parent;
|
||||
double percent;
|
||||
u64 hits, child_total;
|
||||
|
||||
node = rb_entry(nd, struct callchain_node, rb_node);
|
||||
|
||||
hits = callchain_cumul_hits(node);
|
||||
percent = 100.0 * hits / total;
|
||||
|
||||
new_parent = *parent;
|
||||
need_new_parent = !has_single_node && (node->val_nr > 1);
|
||||
|
||||
list_for_each_entry(chain, &node->val, list) {
|
||||
char buf[128];
|
||||
|
||||
gtk_tree_store_append(store, &iter, &new_parent);
|
||||
|
||||
scnprintf(buf, sizeof(buf), "%5.2f%%", percent);
|
||||
gtk_tree_store_set(store, &iter, 0, buf, -1);
|
||||
|
||||
callchain_list__sym_name(chain, buf, sizeof(buf));
|
||||
gtk_tree_store_set(store, &iter, col, buf, -1);
|
||||
|
||||
if (need_new_parent) {
|
||||
/*
|
||||
* Only show the top-most symbol in a callchain
|
||||
* if it's not the only callchain.
|
||||
*/
|
||||
new_parent = iter;
|
||||
need_new_parent = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
child_total = node->children_hit;
|
||||
else
|
||||
child_total = total;
|
||||
|
||||
/* Now 'iter' contains info of the last callchain_list */
|
||||
perf_gtk__add_callchain(&node->rb_root, store, &iter, col,
|
||||
child_total);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_row_activated(GtkTreeView *view, GtkTreePath *path,
|
||||
GtkTreeViewColumn *col __maybe_unused,
|
||||
gpointer user_data __maybe_unused)
|
||||
{
|
||||
bool expanded = gtk_tree_view_row_expanded(view, path);
|
||||
|
||||
if (expanded)
|
||||
gtk_tree_view_collapse_row(view, path);
|
||||
else
|
||||
gtk_tree_view_expand_row(view, path, FALSE);
|
||||
}
|
||||
|
||||
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
||||
float min_pcnt)
|
||||
{
|
||||
|
@ -131,10 +207,11 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
GType col_types[MAX_COLUMNS];
|
||||
GtkCellRenderer *renderer;
|
||||
struct sort_entry *se;
|
||||
GtkListStore *store;
|
||||
GtkTreeStore *store;
|
||||
struct rb_node *nd;
|
||||
GtkWidget *view;
|
||||
int col_idx;
|
||||
int sym_col = -1;
|
||||
int nr_cols;
|
||||
char s[512];
|
||||
|
||||
|
@ -153,10 +230,13 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
if (se->elide)
|
||||
continue;
|
||||
|
||||
if (se == &sort_sym)
|
||||
sym_col = nr_cols;
|
||||
|
||||
col_types[nr_cols++] = G_TYPE_STRING;
|
||||
}
|
||||
|
||||
store = gtk_list_store_newv(nr_cols, col_types);
|
||||
store = gtk_tree_store_newv(nr_cols, col_types);
|
||||
|
||||
view = gtk_tree_view_new();
|
||||
|
||||
|
@ -165,7 +245,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
col_idx = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
fmt->header(&hpp);
|
||||
fmt->header(fmt, &hpp);
|
||||
|
||||
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
|
||||
-1, ltrim(s),
|
||||
|
@ -183,6 +263,18 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
col_idx++, NULL);
|
||||
}
|
||||
|
||||
for (col_idx = 0; col_idx < nr_cols; col_idx++) {
|
||||
GtkTreeViewColumn *column;
|
||||
|
||||
column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx);
|
||||
gtk_tree_view_column_set_resizable(column, TRUE);
|
||||
|
||||
if (col_idx == sym_col) {
|
||||
gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view),
|
||||
column);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
|
||||
|
||||
g_object_unref(GTK_TREE_MODEL(store));
|
||||
|
@ -199,17 +291,17 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
if (percent < min_pcnt)
|
||||
continue;
|
||||
|
||||
gtk_list_store_append(store, &iter);
|
||||
gtk_tree_store_append(store, &iter, NULL);
|
||||
|
||||
col_idx = 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
if (fmt->color)
|
||||
fmt->color(&hpp, h);
|
||||
fmt->color(fmt, &hpp, h);
|
||||
else
|
||||
fmt->entry(&hpp, h);
|
||||
fmt->entry(fmt, &hpp, h);
|
||||
|
||||
gtk_list_store_set(store, &iter, col_idx++, s, -1);
|
||||
gtk_tree_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list) {
|
||||
|
@ -219,10 +311,26 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
|||
se->se_snprintf(h, s, ARRAY_SIZE(s),
|
||||
hists__col_len(hists, se->se_width_idx));
|
||||
|
||||
gtk_list_store_set(store, &iter, col_idx++, s, -1);
|
||||
gtk_tree_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain && sort__has_sym) {
|
||||
u64 total;
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
total = h->stat.period;
|
||||
else
|
||||
total = hists->stats.total_period;
|
||||
|
||||
perf_gtk__add_callchain(&h->sorted_chain, store, &iter,
|
||||
sym_col, total);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
|
||||
|
||||
g_signal_connect(view, "row-activated",
|
||||
G_CALLBACK(on_row_activated), NULL);
|
||||
gtk_container_add(GTK_CONTAINER(window), view);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <math.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#include "../util/hist.h"
|
||||
#include "../util/util.h"
|
||||
|
@ -79,7 +80,8 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
|
|||
}
|
||||
|
||||
#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
|
||||
static int hpp__header_##_type(struct perf_hpp *hpp) \
|
||||
static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp) \
|
||||
{ \
|
||||
int len = _min_width; \
|
||||
\
|
||||
|
@ -92,7 +94,8 @@ static int hpp__header_##_type(struct perf_hpp *hpp) \
|
|||
}
|
||||
|
||||
#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \
|
||||
static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused) \
|
||||
static int hpp__width_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp __maybe_unused) \
|
||||
{ \
|
||||
int len = _min_width; \
|
||||
\
|
||||
|
@ -110,14 +113,16 @@ static u64 he_get_##_field(struct hist_entry *he) \
|
|||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \
|
||||
(hpp_snprint_fn)percent_color_snprintf, true); \
|
||||
}
|
||||
|
||||
#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
|
||||
static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
|
||||
return __hpp__fmt(hpp, he, he_get_##_field, fmt, \
|
||||
|
@ -130,7 +135,8 @@ static u64 he_get_raw_##_field(struct hist_entry *he) \
|
|||
return he->stat._field; \
|
||||
} \
|
||||
\
|
||||
static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
|
||||
return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \
|
||||
|
@ -157,196 +163,6 @@ HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
|
|||
HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
|
||||
HPP_RAW_FNS(period, "Period", period, 12, 12)
|
||||
|
||||
|
||||
static int hpp__header_baseline(struct perf_hpp *hpp)
|
||||
{
|
||||
return scnprintf(hpp->buf, hpp->size, "Baseline");
|
||||
}
|
||||
|
||||
static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
static double baseline_percent(struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
struct hists *pair_hists = pair ? pair->hists : NULL;
|
||||
double percent = 0.0;
|
||||
|
||||
if (pair) {
|
||||
u64 total_period = pair_hists->stats.total_period;
|
||||
u64 base_period = pair->stat.period;
|
||||
|
||||
percent = 100.0 * base_period / total_period;
|
||||
}
|
||||
|
||||
return percent;
|
||||
}
|
||||
|
||||
static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = baseline_percent(he);
|
||||
|
||||
if (hist_entry__has_pairs(he) || symbol_conf.field_sep)
|
||||
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
|
||||
else
|
||||
return scnprintf(hpp->buf, hpp->size, " ");
|
||||
}
|
||||
|
||||
static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
double percent = baseline_percent(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%";
|
||||
|
||||
if (hist_entry__has_pairs(he) || symbol_conf.field_sep)
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, percent);
|
||||
else
|
||||
return scnprintf(hpp->buf, hpp->size, " ");
|
||||
}
|
||||
|
||||
static int hpp__header_period_baseline(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%12s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Period Base");
|
||||
}
|
||||
|
||||
static int hpp__width_period_baseline(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
u64 period = pair ? pair->stat.period : 0;
|
||||
const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64;
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, period);
|
||||
}
|
||||
|
||||
static int hpp__header_delta(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Delta");
|
||||
}
|
||||
|
||||
static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
|
||||
static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s";
|
||||
char buf[32] = " ";
|
||||
double diff = 0.0;
|
||||
|
||||
if (pair) {
|
||||
if (he->diff.computed)
|
||||
diff = he->diff.period_ratio_delta;
|
||||
else
|
||||
diff = perf_diff__compute_delta(he, pair);
|
||||
} else
|
||||
diff = perf_diff__period_percent(he, he->stat.period);
|
||||
|
||||
if (fabs(diff) >= 0.01)
|
||||
scnprintf(buf, sizeof(buf), "%+4.2F%%", diff);
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
static int hpp__header_ratio(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Ratio");
|
||||
}
|
||||
|
||||
static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 14;
|
||||
}
|
||||
|
||||
static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
|
||||
char buf[32] = " ";
|
||||
double ratio = 0.0;
|
||||
|
||||
if (pair) {
|
||||
if (he->diff.computed)
|
||||
ratio = he->diff.period_ratio;
|
||||
else
|
||||
ratio = perf_diff__compute_ratio(he, pair);
|
||||
}
|
||||
|
||||
if (ratio > 0.0)
|
||||
scnprintf(buf, sizeof(buf), "%+14.6F", ratio);
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
static int hpp__header_wdiff(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Weighted diff");
|
||||
}
|
||||
|
||||
static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 14;
|
||||
}
|
||||
|
||||
static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%14s";
|
||||
char buf[32] = " ";
|
||||
s64 wdiff = 0;
|
||||
|
||||
if (pair) {
|
||||
if (he->diff.computed)
|
||||
wdiff = he->diff.wdiff;
|
||||
else
|
||||
wdiff = perf_diff__compute_wdiff(he, pair);
|
||||
}
|
||||
|
||||
if (wdiff != 0)
|
||||
scnprintf(buf, sizeof(buf), "%14ld", wdiff);
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
static int hpp__header_formula(struct perf_hpp *hpp)
|
||||
{
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%70s";
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, "Formula");
|
||||
}
|
||||
|
||||
static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused)
|
||||
{
|
||||
return 70;
|
||||
}
|
||||
|
||||
static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he)
|
||||
{
|
||||
struct hist_entry *pair = hist_entry__next_pair(he);
|
||||
const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s";
|
||||
char buf[96] = " ";
|
||||
|
||||
if (pair)
|
||||
perf_diff__formula(he, pair, buf, sizeof(buf));
|
||||
|
||||
return scnprintf(hpp->buf, hpp->size, fmt, buf);
|
||||
}
|
||||
|
||||
#define HPP__COLOR_PRINT_FNS(_name) \
|
||||
{ \
|
||||
.header = hpp__header_ ## _name, \
|
||||
|
@ -363,19 +179,13 @@ static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he)
|
|||
}
|
||||
|
||||
struct perf_hpp_fmt perf_hpp__format[] = {
|
||||
HPP__COLOR_PRINT_FNS(baseline),
|
||||
HPP__COLOR_PRINT_FNS(overhead),
|
||||
HPP__COLOR_PRINT_FNS(overhead_sys),
|
||||
HPP__COLOR_PRINT_FNS(overhead_us),
|
||||
HPP__COLOR_PRINT_FNS(overhead_guest_sys),
|
||||
HPP__COLOR_PRINT_FNS(overhead_guest_us),
|
||||
HPP__PRINT_FNS(samples),
|
||||
HPP__PRINT_FNS(period),
|
||||
HPP__PRINT_FNS(period_baseline),
|
||||
HPP__PRINT_FNS(delta),
|
||||
HPP__PRINT_FNS(ratio),
|
||||
HPP__PRINT_FNS(wdiff),
|
||||
HPP__PRINT_FNS(formula)
|
||||
HPP__PRINT_FNS(period)
|
||||
};
|
||||
|
||||
LIST_HEAD(perf_hpp__list);
|
||||
|
@ -396,6 +206,8 @@ LIST_HEAD(perf_hpp__list);
|
|||
|
||||
void perf_hpp__init(void)
|
||||
{
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
|
||||
if (symbol_conf.show_cpu_utilization) {
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS);
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD_US);
|
||||
|
@ -424,46 +236,6 @@ void perf_hpp__column_enable(unsigned col)
|
|||
perf_hpp__column_register(&perf_hpp__format[col]);
|
||||
}
|
||||
|
||||
static inline void advance_hpp(struct perf_hpp *hpp, int inc)
|
||||
{
|
||||
hpp->buf += inc;
|
||||
hpp->size -= inc;
|
||||
}
|
||||
|
||||
int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
bool color)
|
||||
{
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
char *start = hpp->buf;
|
||||
int ret;
|
||||
bool first = true;
|
||||
|
||||
if (symbol_conf.exclude_other && !he->parent)
|
||||
return 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
/*
|
||||
* If there's no field_sep, we still need
|
||||
* to display initial ' '.
|
||||
*/
|
||||
if (!sep || !first) {
|
||||
ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " ");
|
||||
advance_hpp(hpp, ret);
|
||||
} else
|
||||
first = false;
|
||||
|
||||
if (color && fmt->color)
|
||||
ret = fmt->color(hpp, he);
|
||||
else
|
||||
ret = fmt->entry(hpp, he);
|
||||
|
||||
advance_hpp(hpp, ret);
|
||||
}
|
||||
|
||||
return hpp->buf - start;
|
||||
}
|
||||
|
||||
int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,
|
||||
struct hists *hists)
|
||||
{
|
||||
|
@ -499,7 +271,7 @@ unsigned int hists__sort_list_width(struct hists *hists)
|
|||
if (i)
|
||||
ret += 2;
|
||||
|
||||
ret += fmt->width(&dummy_hpp);
|
||||
ret += fmt->width(fmt, &dummy_hpp);
|
||||
}
|
||||
|
||||
list_for_each_entry(se, &hist_entry__sort_list, list)
|
||||
|
|
|
@ -30,7 +30,6 @@ void setup_browser(bool fallback_to_pager)
|
|||
if (fallback_to_pager)
|
||||
setup_pager();
|
||||
|
||||
perf_hpp__column_enable(PERF_HPP__OVERHEAD);
|
||||
perf_hpp__init();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -308,6 +308,47 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
|
|||
return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
|
||||
}
|
||||
|
||||
static inline void advance_hpp(struct perf_hpp *hpp, int inc)
|
||||
{
|
||||
hpp->buf += inc;
|
||||
hpp->size -= inc;
|
||||
}
|
||||
|
||||
static int hist_entry__period_snprintf(struct perf_hpp *hpp,
|
||||
struct hist_entry *he,
|
||||
bool color)
|
||||
{
|
||||
const char *sep = symbol_conf.field_sep;
|
||||
struct perf_hpp_fmt *fmt;
|
||||
char *start = hpp->buf;
|
||||
int ret;
|
||||
bool first = true;
|
||||
|
||||
if (symbol_conf.exclude_other && !he->parent)
|
||||
return 0;
|
||||
|
||||
perf_hpp__for_each_format(fmt) {
|
||||
/*
|
||||
* If there's no field_sep, we still need
|
||||
* to display initial ' '.
|
||||
*/
|
||||
if (!sep || !first) {
|
||||
ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " ");
|
||||
advance_hpp(hpp, ret);
|
||||
} else
|
||||
first = false;
|
||||
|
||||
if (color && fmt->color)
|
||||
ret = fmt->color(fmt, hpp, he);
|
||||
else
|
||||
ret = fmt->entry(fmt, hpp, he);
|
||||
|
||||
advance_hpp(hpp, ret);
|
||||
}
|
||||
|
||||
return hpp->buf - start;
|
||||
}
|
||||
|
||||
static int hist_entry__fprintf(struct hist_entry *he, size_t size,
|
||||
struct hists *hists, FILE *fp)
|
||||
{
|
||||
|
@ -365,7 +406,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
|||
else
|
||||
first = false;
|
||||
|
||||
fmt->header(&dummy_hpp);
|
||||
fmt->header(fmt, &dummy_hpp);
|
||||
fprintf(fp, "%s", bf);
|
||||
}
|
||||
|
||||
|
@ -410,7 +451,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
|
|||
else
|
||||
first = false;
|
||||
|
||||
width = fmt->width(&dummy_hpp);
|
||||
width = fmt->width(fmt, &dummy_hpp);
|
||||
for (i = 0; i < width; i++)
|
||||
fprintf(fp, ".");
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ static inline int cpu_map__nr(const struct cpu_map *map)
|
|||
return map ? map->nr : 1;
|
||||
}
|
||||
|
||||
static inline bool cpu_map__all(const struct cpu_map *map)
|
||||
static inline bool cpu_map__empty(const struct cpu_map *map)
|
||||
{
|
||||
return map ? map->map[0] == -1 : true;
|
||||
}
|
||||
|
|
|
@ -686,7 +686,7 @@ int perf_event__preprocess_sample(const union perf_event *event,
|
|||
!strlist__has_entry(symbol_conf.comm_list, thread->comm))
|
||||
goto out_filtered;
|
||||
|
||||
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
|
||||
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid);
|
||||
/*
|
||||
* Have we already created the kernel maps for this machine?
|
||||
*
|
||||
|
|
|
@ -116,7 +116,7 @@ struct build_id_event {
|
|||
enum perf_user_event_type { /* above any possible kernel type */
|
||||
PERF_RECORD_USER_TYPE_START = 64,
|
||||
PERF_RECORD_HEADER_ATTR = 64,
|
||||
PERF_RECORD_HEADER_EVENT_TYPE = 65,
|
||||
PERF_RECORD_HEADER_EVENT_TYPE = 65, /* depreceated */
|
||||
PERF_RECORD_HEADER_TRACING_DATA = 66,
|
||||
PERF_RECORD_HEADER_BUILD_ID = 67,
|
||||
PERF_RECORD_FINISHED_ROUND = 68,
|
||||
|
|
|
@ -403,16 +403,20 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx)
|
|||
return event;
|
||||
}
|
||||
|
||||
static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx)
|
||||
{
|
||||
if (evlist->mmap[idx].base != NULL) {
|
||||
munmap(evlist->mmap[idx].base, evlist->mmap_len);
|
||||
evlist->mmap[idx].base = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void perf_evlist__munmap(struct perf_evlist *evlist)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||
if (evlist->mmap[i].base != NULL) {
|
||||
munmap(evlist->mmap[i].base, evlist->mmap_len);
|
||||
evlist->mmap[i].base = NULL;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < evlist->nr_mmaps; i++)
|
||||
__perf_evlist__munmap(evlist, i);
|
||||
|
||||
free(evlist->mmap);
|
||||
evlist->mmap = NULL;
|
||||
|
@ -421,7 +425,7 @@ void perf_evlist__munmap(struct perf_evlist *evlist)
|
|||
static int perf_evlist__alloc_mmap(struct perf_evlist *evlist)
|
||||
{
|
||||
evlist->nr_mmaps = cpu_map__nr(evlist->cpus);
|
||||
if (cpu_map__all(evlist->cpus))
|
||||
if (cpu_map__empty(evlist->cpus))
|
||||
evlist->nr_mmaps = thread_map__nr(evlist->threads);
|
||||
evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap));
|
||||
return evlist->mmap != NULL ? 0 : -ENOMEM;
|
||||
|
@ -477,12 +481,8 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m
|
|||
return 0;
|
||||
|
||||
out_unmap:
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++) {
|
||||
if (evlist->mmap[cpu].base != NULL) {
|
||||
munmap(evlist->mmap[cpu].base, evlist->mmap_len);
|
||||
evlist->mmap[cpu].base = NULL;
|
||||
}
|
||||
}
|
||||
for (cpu = 0; cpu < nr_cpus; cpu++)
|
||||
__perf_evlist__munmap(evlist, cpu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -517,12 +517,8 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in
|
|||
return 0;
|
||||
|
||||
out_unmap:
|
||||
for (thread = 0; thread < nr_threads; thread++) {
|
||||
if (evlist->mmap[thread].base != NULL) {
|
||||
munmap(evlist->mmap[thread].base, evlist->mmap_len);
|
||||
evlist->mmap[thread].base = NULL;
|
||||
}
|
||||
}
|
||||
for (thread = 0; thread < nr_threads; thread++)
|
||||
__perf_evlist__munmap(evlist, thread);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -573,7 +569,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (cpu_map__all(cpus))
|
||||
if (cpu_map__empty(cpus))
|
||||
return perf_evlist__mmap_per_thread(evlist, prot, mask);
|
||||
|
||||
return perf_evlist__mmap_per_cpu(evlist, prot, mask);
|
||||
|
@ -838,7 +834,7 @@ out_close_ready_pipe:
|
|||
int perf_evlist__start_workload(struct perf_evlist *evlist)
|
||||
{
|
||||
if (evlist->workload.cork_fd > 0) {
|
||||
char bf;
|
||||
char bf = 0;
|
||||
int ret;
|
||||
/*
|
||||
* Remove the cork, let it rip!
|
||||
|
|
|
@ -9,17 +9,17 @@
|
|||
|
||||
#include <byteswap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include "asm/bug.h"
|
||||
#include <lk/debugfs.h>
|
||||
#include "event-parse.h"
|
||||
#include <traceevent/event-parse.h>
|
||||
#include <linux/hw_breakpoint.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include "asm/bug.h"
|
||||
#include "evsel.h"
|
||||
#include "evlist.h"
|
||||
#include "util.h"
|
||||
#include "cpumap.h"
|
||||
#include "thread_map.h"
|
||||
#include "target.h"
|
||||
#include <linux/hw_breakpoint.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include "perf_regs.h"
|
||||
|
||||
static struct {
|
||||
|
|
|
@ -25,41 +25,9 @@
|
|||
|
||||
static bool no_buildid_cache = false;
|
||||
|
||||
static int trace_event_count;
|
||||
static struct perf_trace_event_type *trace_events;
|
||||
|
||||
static u32 header_argc;
|
||||
static const char **header_argv;
|
||||
|
||||
int perf_header__push_event(u64 id, const char *name)
|
||||
{
|
||||
struct perf_trace_event_type *nevents;
|
||||
|
||||
if (strlen(name) > MAX_EVENT_NAME)
|
||||
pr_warning("Event %s will be truncated\n", name);
|
||||
|
||||
nevents = realloc(trace_events, (trace_event_count + 1) * sizeof(*trace_events));
|
||||
if (nevents == NULL)
|
||||
return -ENOMEM;
|
||||
trace_events = nevents;
|
||||
|
||||
memset(&trace_events[trace_event_count], 0, sizeof(struct perf_trace_event_type));
|
||||
trace_events[trace_event_count].event_id = id;
|
||||
strncpy(trace_events[trace_event_count].name, name, MAX_EVENT_NAME - 1);
|
||||
trace_event_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *perf_header__find_event(u64 id)
|
||||
{
|
||||
int i;
|
||||
for (i = 0 ; i < trace_event_count; i++) {
|
||||
if (trace_events[i].event_id == id)
|
||||
return trace_events[i].name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* magic2 = "PERFILE2"
|
||||
* must be a numerical value to let the endianness
|
||||
|
@ -2257,7 +2225,7 @@ static int perf_header__adds_write(struct perf_header *header,
|
|||
|
||||
sec_size = sizeof(*feat_sec) * nr_sections;
|
||||
|
||||
sec_start = header->data_offset + header->data_size;
|
||||
sec_start = header->feat_offset;
|
||||
lseek(fd, sec_start + sec_size, SEEK_SET);
|
||||
|
||||
for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) {
|
||||
|
@ -2304,6 +2272,7 @@ int perf_session__write_header(struct perf_session *session,
|
|||
struct perf_file_attr f_attr;
|
||||
struct perf_header *header = &session->header;
|
||||
struct perf_evsel *evsel;
|
||||
u64 attr_offset;
|
||||
int err;
|
||||
|
||||
lseek(fd, sizeof(f_header), SEEK_SET);
|
||||
|
@ -2317,7 +2286,7 @@ int perf_session__write_header(struct perf_session *session,
|
|||
}
|
||||
}
|
||||
|
||||
header->attr_offset = lseek(fd, 0, SEEK_CUR);
|
||||
attr_offset = lseek(fd, 0, SEEK_CUR);
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
f_attr = (struct perf_file_attr){
|
||||
|
@ -2334,17 +2303,8 @@ int perf_session__write_header(struct perf_session *session,
|
|||
}
|
||||
}
|
||||
|
||||
header->event_offset = lseek(fd, 0, SEEK_CUR);
|
||||
header->event_size = trace_event_count * sizeof(struct perf_trace_event_type);
|
||||
if (trace_events) {
|
||||
err = do_write(fd, trace_events, header->event_size);
|
||||
if (err < 0) {
|
||||
pr_debug("failed to write perf header events\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
header->data_offset = lseek(fd, 0, SEEK_CUR);
|
||||
header->feat_offset = header->data_offset + header->data_size;
|
||||
|
||||
if (at_exit) {
|
||||
err = perf_header__adds_write(header, evlist, fd);
|
||||
|
@ -2357,17 +2317,14 @@ int perf_session__write_header(struct perf_session *session,
|
|||
.size = sizeof(f_header),
|
||||
.attr_size = sizeof(f_attr),
|
||||
.attrs = {
|
||||
.offset = header->attr_offset,
|
||||
.offset = attr_offset,
|
||||
.size = evlist->nr_entries * sizeof(f_attr),
|
||||
},
|
||||
.data = {
|
||||
.offset = header->data_offset,
|
||||
.size = header->data_size,
|
||||
},
|
||||
.event_types = {
|
||||
.offset = header->event_offset,
|
||||
.size = header->event_size,
|
||||
},
|
||||
/* event_types is ignored, store zeros */
|
||||
};
|
||||
|
||||
memcpy(&f_header.adds_features, &header->adds_features, sizeof(header->adds_features));
|
||||
|
@ -2417,7 +2374,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
|
|||
|
||||
sec_size = sizeof(*feat_sec) * nr_sections;
|
||||
|
||||
lseek(fd, header->data_offset + header->data_size, SEEK_SET);
|
||||
lseek(fd, header->feat_offset, SEEK_SET);
|
||||
|
||||
err = perf_header__getbuffer64(header, fd, feat_sec, sec_size);
|
||||
if (err < 0)
|
||||
|
@ -2523,6 +2480,7 @@ static int check_magic_endian(u64 magic, uint64_t hdr_sz,
|
|||
/* check for legacy format */
|
||||
ret = memcmp(&magic, __perf_magic1, sizeof(magic));
|
||||
if (ret == 0) {
|
||||
ph->version = PERF_HEADER_VERSION_1;
|
||||
pr_debug("legacy perf.data format\n");
|
||||
if (is_pipe)
|
||||
return try_all_pipe_abis(hdr_sz, ph);
|
||||
|
@ -2544,6 +2502,7 @@ static int check_magic_endian(u64 magic, uint64_t hdr_sz,
|
|||
return -1;
|
||||
|
||||
ph->needs_swap = true;
|
||||
ph->version = PERF_HEADER_VERSION_2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2614,10 +2573,9 @@ int perf_file_header__read(struct perf_file_header *header,
|
|||
memcpy(&ph->adds_features, &header->adds_features,
|
||||
sizeof(ph->adds_features));
|
||||
|
||||
ph->event_offset = header->event_types.offset;
|
||||
ph->event_size = header->event_types.size;
|
||||
ph->data_offset = header->data.offset;
|
||||
ph->data_size = header->data.size;
|
||||
ph->feat_offset = header->data.offset + header->data.size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2666,19 +2624,17 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int perf_header__read_pipe(struct perf_session *session, int fd)
|
||||
static int perf_header__read_pipe(struct perf_session *session)
|
||||
{
|
||||
struct perf_header *header = &session->header;
|
||||
struct perf_pipe_file_header f_header;
|
||||
|
||||
if (perf_file_header__read_pipe(&f_header, header, fd,
|
||||
if (perf_file_header__read_pipe(&f_header, header, session->fd,
|
||||
session->repipe) < 0) {
|
||||
pr_debug("incompatible file format\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
session->fd = fd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2772,20 +2728,21 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int perf_session__read_header(struct perf_session *session, int fd)
|
||||
int perf_session__read_header(struct perf_session *session)
|
||||
{
|
||||
struct perf_header *header = &session->header;
|
||||
struct perf_file_header f_header;
|
||||
struct perf_file_attr f_attr;
|
||||
u64 f_id;
|
||||
int nr_attrs, nr_ids, i, j;
|
||||
int fd = session->fd;
|
||||
|
||||
session->evlist = perf_evlist__new();
|
||||
if (session->evlist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (session->fd_pipe)
|
||||
return perf_header__read_pipe(session, fd);
|
||||
return perf_header__read_pipe(session);
|
||||
|
||||
if (perf_file_header__read(&f_header, header, fd) < 0)
|
||||
return -EINVAL;
|
||||
|
@ -2839,22 +2796,9 @@ int perf_session__read_header(struct perf_session *session, int fd)
|
|||
|
||||
symbol_conf.nr_events = nr_attrs;
|
||||
|
||||
if (f_header.event_types.size) {
|
||||
lseek(fd, f_header.event_types.offset, SEEK_SET);
|
||||
trace_events = malloc(f_header.event_types.size);
|
||||
if (trace_events == NULL)
|
||||
return -ENOMEM;
|
||||
if (perf_header__getbuffer64(header, fd, trace_events,
|
||||
f_header.event_types.size))
|
||||
goto out_errno;
|
||||
trace_event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
|
||||
}
|
||||
|
||||
perf_header__process_sections(header, fd, &session->pevent,
|
||||
perf_file_section__process);
|
||||
|
||||
lseek(fd, header->data_offset, SEEK_SET);
|
||||
|
||||
if (perf_evlist__prepare_tracepoint_events(session->evlist,
|
||||
session->pevent))
|
||||
goto out_delete_evlist;
|
||||
|
@ -2922,7 +2866,8 @@ int perf_event__synthesize_attrs(struct perf_tool *tool,
|
|||
return err;
|
||||
}
|
||||
|
||||
int perf_event__process_attr(union perf_event *event,
|
||||
int perf_event__process_attr(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_evlist **pevlist)
|
||||
{
|
||||
u32 i, ids, n_ids;
|
||||
|
@ -2961,64 +2906,6 @@ int perf_event__process_attr(union perf_event *event,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_event_type(struct perf_tool *tool,
|
||||
u64 event_id, char *name,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
{
|
||||
union perf_event ev;
|
||||
size_t size = 0;
|
||||
int err = 0;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
|
||||
ev.event_type.event_type.event_id = event_id;
|
||||
memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME);
|
||||
strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1);
|
||||
|
||||
ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE;
|
||||
size = strlen(ev.event_type.event_type.name);
|
||||
size = PERF_ALIGN(size, sizeof(u64));
|
||||
ev.event_type.header.size = sizeof(ev.event_type) -
|
||||
(sizeof(ev.event_type.event_type.name) - size);
|
||||
|
||||
err = process(tool, &ev, NULL, machine);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_event_types(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_trace_event_type *type;
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < trace_event_count; i++) {
|
||||
type = &trace_events[i];
|
||||
|
||||
err = perf_event__synthesize_event_type(tool, type->event_id,
|
||||
type->name, process,
|
||||
machine);
|
||||
if (err) {
|
||||
pr_debug("failed to create perf header event type\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__process_event_type(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event)
|
||||
{
|
||||
if (perf_header__push_event(event->event_type.event_type.event_id,
|
||||
event->event_type.event_type.name) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
|
||||
struct perf_evlist *evlist,
|
||||
perf_event__handler_t process)
|
||||
|
@ -3065,7 +2952,8 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
|
|||
return aligned_size;
|
||||
}
|
||||
|
||||
int perf_event__process_tracing_data(union perf_event *event,
|
||||
int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_session *session)
|
||||
{
|
||||
ssize_t size_read, padding, size = event->tracing_data.size;
|
||||
|
|
|
@ -34,6 +34,11 @@ enum {
|
|||
HEADER_FEAT_BITS = 256,
|
||||
};
|
||||
|
||||
enum perf_header_version {
|
||||
PERF_HEADER_VERSION_1,
|
||||
PERF_HEADER_VERSION_2,
|
||||
};
|
||||
|
||||
struct perf_file_section {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
|
@ -45,6 +50,7 @@ struct perf_file_header {
|
|||
u64 attr_size;
|
||||
struct perf_file_section attrs;
|
||||
struct perf_file_section data;
|
||||
/* event_types is ignored */
|
||||
struct perf_file_section event_types;
|
||||
DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
|
||||
};
|
||||
|
@ -84,28 +90,24 @@ struct perf_session_env {
|
|||
};
|
||||
|
||||
struct perf_header {
|
||||
bool needs_swap;
|
||||
s64 attr_offset;
|
||||
u64 data_offset;
|
||||
u64 data_size;
|
||||
u64 event_offset;
|
||||
u64 event_size;
|
||||
enum perf_header_version version;
|
||||
bool needs_swap;
|
||||
u64 data_offset;
|
||||
u64 data_size;
|
||||
u64 feat_offset;
|
||||
DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
|
||||
struct perf_session_env env;
|
||||
struct perf_session_env env;
|
||||
};
|
||||
|
||||
struct perf_evlist;
|
||||
struct perf_session;
|
||||
|
||||
int perf_session__read_header(struct perf_session *session, int fd);
|
||||
int perf_session__read_header(struct perf_session *session);
|
||||
int perf_session__write_header(struct perf_session *session,
|
||||
struct perf_evlist *evlist,
|
||||
int fd, bool at_exit);
|
||||
int perf_header__write_pipe(int fd);
|
||||
|
||||
int perf_header__push_event(u64 id, const char *name);
|
||||
char *perf_header__find_event(u64 id);
|
||||
|
||||
void perf_header__set_feat(struct perf_header *header, int feat);
|
||||
void perf_header__clear_feat(struct perf_header *header, int feat);
|
||||
bool perf_header__has_feat(const struct perf_header *header, int feat);
|
||||
|
@ -130,22 +132,14 @@ int perf_event__synthesize_attr(struct perf_tool *tool,
|
|||
int perf_event__synthesize_attrs(struct perf_tool *tool,
|
||||
struct perf_session *session,
|
||||
perf_event__handler_t process);
|
||||
int perf_event__process_attr(union perf_event *event, struct perf_evlist **pevlist);
|
||||
|
||||
int perf_event__synthesize_event_type(struct perf_tool *tool,
|
||||
u64 event_id, char *name,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
int perf_event__synthesize_event_types(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
int perf_event__process_event_type(struct perf_tool *tool,
|
||||
union perf_event *event);
|
||||
int perf_event__process_attr(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_evlist **pevlist);
|
||||
|
||||
int perf_event__synthesize_tracing_data(struct perf_tool *tool,
|
||||
int fd, struct perf_evlist *evlist,
|
||||
perf_event__handler_t process);
|
||||
int perf_event__process_tracing_data(union perf_event *event,
|
||||
int perf_event__process_tracing_data(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
int perf_event__synthesize_build_id(struct perf_tool *tool,
|
||||
|
|
|
@ -912,6 +912,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
|
|||
rb_link_node(&he->rb_node_in, parent, p);
|
||||
rb_insert_color(&he->rb_node_in, root);
|
||||
hists__inc_nr_entries(hists, he);
|
||||
he->dummy = true;
|
||||
}
|
||||
out:
|
||||
return he;
|
||||
|
|
|
@ -141,10 +141,12 @@ struct perf_hpp {
|
|||
};
|
||||
|
||||
struct perf_hpp_fmt {
|
||||
int (*header)(struct perf_hpp *hpp);
|
||||
int (*width)(struct perf_hpp *hpp);
|
||||
int (*color)(struct perf_hpp *hpp, struct hist_entry *he);
|
||||
int (*entry)(struct perf_hpp *hpp, struct hist_entry *he);
|
||||
int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp);
|
||||
int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp);
|
||||
int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct hist_entry *he);
|
||||
int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
|
||||
struct hist_entry *he);
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
@ -157,7 +159,7 @@ extern struct list_head perf_hpp__list;
|
|||
extern struct perf_hpp_fmt perf_hpp__format[];
|
||||
|
||||
enum {
|
||||
PERF_HPP__BASELINE,
|
||||
/* Matches perf_hpp__format array. */
|
||||
PERF_HPP__OVERHEAD,
|
||||
PERF_HPP__OVERHEAD_SYS,
|
||||
PERF_HPP__OVERHEAD_US,
|
||||
|
@ -165,11 +167,6 @@ enum {
|
|||
PERF_HPP__OVERHEAD_GUEST_US,
|
||||
PERF_HPP__SAMPLES,
|
||||
PERF_HPP__PERIOD,
|
||||
PERF_HPP__PERIOD_BASELINE,
|
||||
PERF_HPP__DELTA,
|
||||
PERF_HPP__RATIO,
|
||||
PERF_HPP__WEIGHTED_DIFF,
|
||||
PERF_HPP__FORMULA,
|
||||
|
||||
PERF_HPP__MAX_INDEX
|
||||
};
|
||||
|
@ -177,8 +174,6 @@ enum {
|
|||
void perf_hpp__init(void);
|
||||
void perf_hpp__column_register(struct perf_hpp_fmt *format);
|
||||
void perf_hpp__column_enable(unsigned col);
|
||||
int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
|
||||
bool color);
|
||||
|
||||
struct perf_evlist;
|
||||
|
||||
|
@ -245,11 +240,4 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused,
|
|||
#endif
|
||||
|
||||
unsigned int hists__sort_list_width(struct hists *self);
|
||||
|
||||
double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair);
|
||||
double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair);
|
||||
s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair);
|
||||
int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair,
|
||||
char *buf, size_t size);
|
||||
double perf_diff__period_percent(struct hist_entry *he, u64 period);
|
||||
#endif /* __PERF_HIST_H */
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <string.h>
|
||||
|
||||
void *memdup(const void *src, size_t len);
|
||||
int str_append(char **s, int *len, const char *a);
|
||||
|
|
|
@ -233,7 +233,7 @@ void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size)
|
|||
return;
|
||||
}
|
||||
|
||||
static struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid,
|
||||
static struct thread *__machine__findnew_thread(struct machine *machine, pid_t tid,
|
||||
bool create)
|
||||
{
|
||||
struct rb_node **p = &machine->threads.rb_node;
|
||||
|
@ -241,23 +241,23 @@ static struct thread *__machine__findnew_thread(struct machine *machine, pid_t p
|
|||
struct thread *th;
|
||||
|
||||
/*
|
||||
* Font-end cache - PID lookups come in blocks,
|
||||
* Front-end cache - TID lookups come in blocks,
|
||||
* so most of the time we dont have to look up
|
||||
* the full rbtree:
|
||||
*/
|
||||
if (machine->last_match && machine->last_match->pid == pid)
|
||||
if (machine->last_match && machine->last_match->tid == tid)
|
||||
return machine->last_match;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
th = rb_entry(parent, struct thread, rb_node);
|
||||
|
||||
if (th->pid == pid) {
|
||||
if (th->tid == tid) {
|
||||
machine->last_match = th;
|
||||
return th;
|
||||
}
|
||||
|
||||
if (pid < th->pid)
|
||||
if (tid < th->tid)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
|
@ -266,7 +266,7 @@ static struct thread *__machine__findnew_thread(struct machine *machine, pid_t p
|
|||
if (!create)
|
||||
return NULL;
|
||||
|
||||
th = thread__new(pid);
|
||||
th = thread__new(tid);
|
||||
if (th != NULL) {
|
||||
rb_link_node(&th->rb_node, parent, p);
|
||||
rb_insert_color(&th->rb_node, &machine->threads);
|
||||
|
@ -276,14 +276,14 @@ static struct thread *__machine__findnew_thread(struct machine *machine, pid_t p
|
|||
return th;
|
||||
}
|
||||
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid)
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t tid)
|
||||
{
|
||||
return __machine__findnew_thread(machine, pid, true);
|
||||
return __machine__findnew_thread(machine, tid, true);
|
||||
}
|
||||
|
||||
struct thread *machine__find_thread(struct machine *machine, pid_t pid)
|
||||
struct thread *machine__find_thread(struct machine *machine, pid_t tid)
|
||||
{
|
||||
return __machine__findnew_thread(machine, pid, false);
|
||||
return __machine__findnew_thread(machine, tid, false);
|
||||
}
|
||||
|
||||
int machine__process_comm_event(struct machine *machine, union perf_event *event)
|
||||
|
@ -1058,11 +1058,10 @@ int machine__process_event(struct machine *machine, union perf_event *event)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool symbol__match_parent_regex(struct symbol *sym)
|
||||
static bool symbol__match_regex(struct symbol *sym, regex_t *regex)
|
||||
{
|
||||
if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
|
||||
if (sym->name && !regexec(regex, sym->name, 0, NULL, 0))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1159,8 +1158,8 @@ struct branch_info *machine__resolve_bstack(struct machine *machine,
|
|||
static int machine__resolve_callchain_sample(struct machine *machine,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent)
|
||||
|
||||
struct symbol **parent,
|
||||
struct addr_location *root_al)
|
||||
{
|
||||
u8 cpumode = PERF_RECORD_MISC_USER;
|
||||
unsigned int i;
|
||||
|
@ -1211,8 +1210,15 @@ static int machine__resolve_callchain_sample(struct machine *machine,
|
|||
MAP__FUNCTION, ip, &al, NULL);
|
||||
if (al.sym != NULL) {
|
||||
if (sort__has_parent && !*parent &&
|
||||
symbol__match_parent_regex(al.sym))
|
||||
symbol__match_regex(al.sym, &parent_regex))
|
||||
*parent = al.sym;
|
||||
else if (have_ignore_callees && root_al &&
|
||||
symbol__match_regex(al.sym, &ignore_callees_regex)) {
|
||||
/* Treat this symbol as the root,
|
||||
forgetting its callees. */
|
||||
*root_al = al;
|
||||
callchain_cursor_reset(&callchain_cursor);
|
||||
}
|
||||
if (!symbol_conf.use_callchain)
|
||||
break;
|
||||
}
|
||||
|
@ -1237,15 +1243,13 @@ int machine__resolve_callchain(struct machine *machine,
|
|||
struct perf_evsel *evsel,
|
||||
struct thread *thread,
|
||||
struct perf_sample *sample,
|
||||
struct symbol **parent)
|
||||
|
||||
struct symbol **parent,
|
||||
struct addr_location *root_al)
|
||||
{
|
||||
int ret;
|
||||
|
||||
callchain_cursor_reset(&callchain_cursor);
|
||||
|
||||
ret = machine__resolve_callchain_sample(machine, thread,
|
||||
sample->callchain, parent);
|
||||
sample->callchain, parent, root_al);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <linux/rbtree.h>
|
||||
#include "map.h"
|
||||
|
||||
struct addr_location;
|
||||
struct branch_stack;
|
||||
struct perf_evsel;
|
||||
struct perf_sample;
|
||||
|
@ -36,7 +37,7 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type)
|
|||
return machine->vmlinux_maps[type];
|
||||
}
|
||||
|
||||
struct thread *machine__find_thread(struct machine *machine, pid_t pid);
|
||||
struct thread *machine__find_thread(struct machine *machine, pid_t tid);
|
||||
|
||||
int machine__process_comm_event(struct machine *machine, union perf_event *event);
|
||||
int machine__process_exit_event(struct machine *machine, union perf_event *event);
|
||||
|
@ -83,7 +84,8 @@ int machine__resolve_callchain(struct machine *machine,
|
|||
struct perf_evsel *evsel,
|
||||
struct thread *thread,
|
||||
struct perf_sample *sample,
|
||||
struct symbol **parent);
|
||||
struct symbol **parent,
|
||||
struct addr_location *root_al);
|
||||
|
||||
/*
|
||||
* Default guest kernel is defined by parameter --guestkallsyms
|
||||
|
@ -99,7 +101,7 @@ static inline bool machine__is_host(struct machine *machine)
|
|||
return machine ? machine->pid == HOST_KERNEL_ID : false;
|
||||
}
|
||||
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid);
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t tid);
|
||||
|
||||
size_t machine__fprintf(struct machine *machine, FILE *fp);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include "parse-options.h"
|
||||
#include "parse-events.h"
|
||||
#include "exec_cmd.h"
|
||||
#include "string.h"
|
||||
#include "linux/string.h"
|
||||
#include "symbol.h"
|
||||
#include "cache.h"
|
||||
#include "header.h"
|
||||
|
@ -217,6 +217,29 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct tracepoint_path *tracepoint_name_to_path(const char *name)
|
||||
{
|
||||
struct tracepoint_path *path = zalloc(sizeof(*path));
|
||||
char *str = strchr(name, ':');
|
||||
|
||||
if (path == NULL || str == NULL) {
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path->system = strndup(name, str - name);
|
||||
path->name = strdup(str+1);
|
||||
|
||||
if (path->system == NULL || path->name == NULL) {
|
||||
free(path->system);
|
||||
free(path->name);
|
||||
free(path);
|
||||
path = NULL;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
const char *event_type(int type)
|
||||
{
|
||||
switch (type) {
|
||||
|
@ -241,40 +264,29 @@ const char *event_type(int type)
|
|||
|
||||
|
||||
|
||||
static int __add_event(struct list_head **_list, int *idx,
|
||||
static int __add_event(struct list_head *list, int *idx,
|
||||
struct perf_event_attr *attr,
|
||||
char *name, struct cpu_map *cpus)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
struct list_head *list = *_list;
|
||||
|
||||
if (!list) {
|
||||
list = malloc(sizeof(*list));
|
||||
if (!list)
|
||||
return -ENOMEM;
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
|
||||
event_attr_init(attr);
|
||||
|
||||
evsel = perf_evsel__new(attr, (*idx)++);
|
||||
if (!evsel) {
|
||||
free(list);
|
||||
if (!evsel)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
evsel->cpus = cpus;
|
||||
if (name)
|
||||
evsel->name = strdup(name);
|
||||
list_add_tail(&evsel->node, list);
|
||||
*_list = list;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_event(struct list_head **_list, int *idx,
|
||||
static int add_event(struct list_head *list, int *idx,
|
||||
struct perf_event_attr *attr, char *name)
|
||||
{
|
||||
return __add_event(_list, idx, attr, name, NULL);
|
||||
return __add_event(list, idx, attr, name, NULL);
|
||||
}
|
||||
|
||||
static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
|
||||
|
@ -295,7 +307,7 @@ static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES]
|
|||
return -1;
|
||||
}
|
||||
|
||||
int parse_events_add_cache(struct list_head **list, int *idx,
|
||||
int parse_events_add_cache(struct list_head *list, int *idx,
|
||||
char *type, char *op_result1, char *op_result2)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
@ -356,31 +368,21 @@ int parse_events_add_cache(struct list_head **list, int *idx,
|
|||
return add_event(list, idx, &attr, name);
|
||||
}
|
||||
|
||||
static int add_tracepoint(struct list_head **listp, int *idx,
|
||||
static int add_tracepoint(struct list_head *list, int *idx,
|
||||
char *sys_name, char *evt_name)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
struct list_head *list = *listp;
|
||||
|
||||
if (!list) {
|
||||
list = malloc(sizeof(*list));
|
||||
if (!list)
|
||||
return -ENOMEM;
|
||||
INIT_LIST_HEAD(list);
|
||||
}
|
||||
|
||||
evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++);
|
||||
if (!evsel) {
|
||||
free(list);
|
||||
if (!evsel)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
list_add_tail(&evsel->node, list);
|
||||
*listp = list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_tracepoint_multi_event(struct list_head **list, int *idx,
|
||||
static int add_tracepoint_multi_event(struct list_head *list, int *idx,
|
||||
char *sys_name, char *evt_name)
|
||||
{
|
||||
char evt_path[MAXPATHLEN];
|
||||
|
@ -412,7 +414,7 @@ static int add_tracepoint_multi_event(struct list_head **list, int *idx,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int add_tracepoint_event(struct list_head **list, int *idx,
|
||||
static int add_tracepoint_event(struct list_head *list, int *idx,
|
||||
char *sys_name, char *evt_name)
|
||||
{
|
||||
return strpbrk(evt_name, "*?") ?
|
||||
|
@ -420,7 +422,7 @@ static int add_tracepoint_event(struct list_head **list, int *idx,
|
|||
add_tracepoint(list, idx, sys_name, evt_name);
|
||||
}
|
||||
|
||||
static int add_tracepoint_multi_sys(struct list_head **list, int *idx,
|
||||
static int add_tracepoint_multi_sys(struct list_head *list, int *idx,
|
||||
char *sys_name, char *evt_name)
|
||||
{
|
||||
struct dirent *events_ent;
|
||||
|
@ -452,7 +454,7 @@ static int add_tracepoint_multi_sys(struct list_head **list, int *idx,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int parse_events_add_tracepoint(struct list_head **list, int *idx,
|
||||
int parse_events_add_tracepoint(struct list_head *list, int *idx,
|
||||
char *sys, char *event)
|
||||
{
|
||||
int ret;
|
||||
|
@ -507,7 +509,7 @@ do { \
|
|||
return 0;
|
||||
}
|
||||
|
||||
int parse_events_add_breakpoint(struct list_head **list, int *idx,
|
||||
int parse_events_add_breakpoint(struct list_head *list, int *idx,
|
||||
void *ptr, char *type)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
@ -588,7 +590,7 @@ static int config_attr(struct perf_event_attr *attr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int parse_events_add_numeric(struct list_head **list, int *idx,
|
||||
int parse_events_add_numeric(struct list_head *list, int *idx,
|
||||
u32 type, u64 config,
|
||||
struct list_head *head_config)
|
||||
{
|
||||
|
@ -621,7 +623,7 @@ static char *pmu_event_name(struct list_head *head_terms)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int parse_events_add_pmu(struct list_head **list, int *idx,
|
||||
int parse_events_add_pmu(struct list_head *list, int *idx,
|
||||
char *name, struct list_head *head_config)
|
||||
{
|
||||
struct perf_event_attr attr;
|
||||
|
@ -664,6 +666,7 @@ void parse_events__set_leader(char *name, struct list_head *list)
|
|||
leader->group_name = name ? strdup(name) : NULL;
|
||||
}
|
||||
|
||||
/* list_event is assumed to point to malloc'ed memory */
|
||||
void parse_events_update_lists(struct list_head *list_event,
|
||||
struct list_head *list_all)
|
||||
{
|
||||
|
@ -820,6 +823,32 @@ int parse_events_name(struct list_head *list, char *name)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parse_events__scanner(const char *str, void *data, int start_token);
|
||||
|
||||
static int parse_events_fixup(int ret, const char *str, void *data,
|
||||
int start_token)
|
||||
{
|
||||
char *o = strdup(str);
|
||||
char *s = NULL;
|
||||
char *t = o;
|
||||
char *p;
|
||||
int len = 0;
|
||||
|
||||
if (!o)
|
||||
return ret;
|
||||
while ((p = strsep(&t, ",")) != NULL) {
|
||||
if (s)
|
||||
str_append(&s, &len, ",");
|
||||
str_append(&s, &len, "cpu/");
|
||||
str_append(&s, &len, p);
|
||||
str_append(&s, &len, "/");
|
||||
}
|
||||
free(o);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
return parse_events__scanner(s, data, start_token);
|
||||
}
|
||||
|
||||
static int parse_events__scanner(const char *str, void *data, int start_token)
|
||||
{
|
||||
YY_BUFFER_STATE buffer;
|
||||
|
@ -840,6 +869,8 @@ static int parse_events__scanner(const char *str, void *data, int start_token)
|
|||
parse_events__flush_buffer(buffer, scanner);
|
||||
parse_events__delete_buffer(buffer, scanner);
|
||||
parse_events_lex_destroy(scanner);
|
||||
if (ret && !strchr(str, '/'))
|
||||
ret = parse_events_fixup(ret, str, data, start_token);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1079,6 +1110,8 @@ int print_hwcache_events(const char *event_glob, bool name_only)
|
|||
}
|
||||
}
|
||||
|
||||
if (printed)
|
||||
printf("\n");
|
||||
return printed;
|
||||
}
|
||||
|
||||
|
@ -1133,11 +1166,12 @@ void print_events(const char *event_glob, bool name_only)
|
|||
|
||||
print_hwcache_events(event_glob, name_only);
|
||||
|
||||
print_pmu_events(event_glob, name_only);
|
||||
|
||||
if (event_glob != NULL)
|
||||
return;
|
||||
|
||||
if (!name_only) {
|
||||
printf("\n");
|
||||
printf(" %-50s [%s]\n",
|
||||
"rNNN",
|
||||
event_type_descriptors[PERF_TYPE_RAW]);
|
||||
|
@ -1237,6 +1271,4 @@ void parse_events__free_terms(struct list_head *terms)
|
|||
|
||||
list_for_each_entry_safe(term, h, terms, list)
|
||||
free(term);
|
||||
|
||||
free(terms);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ struct tracepoint_path {
|
|||
};
|
||||
|
||||
extern struct tracepoint_path *tracepoint_id_to_path(u64 config);
|
||||
extern struct tracepoint_path *tracepoint_name_to_path(const char *name);
|
||||
extern bool have_tracepoints(struct list_head *evlist);
|
||||
|
||||
const char *event_type(int type);
|
||||
|
@ -84,16 +85,16 @@ void parse_events__free_terms(struct list_head *terms);
|
|||
int parse_events__modifier_event(struct list_head *list, char *str, bool add);
|
||||
int parse_events__modifier_group(struct list_head *list, char *event_mod);
|
||||
int parse_events_name(struct list_head *list, char *name);
|
||||
int parse_events_add_tracepoint(struct list_head **list, int *idx,
|
||||
int parse_events_add_tracepoint(struct list_head *list, int *idx,
|
||||
char *sys, char *event);
|
||||
int parse_events_add_numeric(struct list_head **list, int *idx,
|
||||
int parse_events_add_numeric(struct list_head *list, int *idx,
|
||||
u32 type, u64 config,
|
||||
struct list_head *head_config);
|
||||
int parse_events_add_cache(struct list_head **list, int *idx,
|
||||
int parse_events_add_cache(struct list_head *list, int *idx,
|
||||
char *type, char *op_result1, char *op_result2);
|
||||
int parse_events_add_breakpoint(struct list_head **list, int *idx,
|
||||
int parse_events_add_breakpoint(struct list_head *list, int *idx,
|
||||
void *ptr, char *type);
|
||||
int parse_events_add_pmu(struct list_head **list, int *idx,
|
||||
int parse_events_add_pmu(struct list_head *list, int *idx,
|
||||
char *pmu , struct list_head *head_config);
|
||||
void parse_events__set_leader(char *name, struct list_head *list);
|
||||
void parse_events_update_lists(struct list_head *list_event,
|
||||
|
|
|
@ -22,6 +22,13 @@ do { \
|
|||
YYABORT; \
|
||||
} while (0)
|
||||
|
||||
#define ALLOC_LIST(list) \
|
||||
do { \
|
||||
list = malloc(sizeof(*list)); \
|
||||
ABORT_ON(!list); \
|
||||
INIT_LIST_HEAD(list); \
|
||||
} while (0)
|
||||
|
||||
static inc_group_count(struct list_head *list,
|
||||
struct parse_events_evlist *data)
|
||||
{
|
||||
|
@ -196,9 +203,10 @@ event_pmu:
|
|||
PE_NAME '/' event_config '/'
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
|
||||
ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3));
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, $3));
|
||||
parse_events__free_terms($3);
|
||||
$$ = list;
|
||||
}
|
||||
|
@ -212,11 +220,12 @@ event_legacy_symbol:
|
|||
value_sym '/' event_config '/'
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
int type = $1 >> 16;
|
||||
int config = $1 & 255;
|
||||
|
||||
ABORT_ON(parse_events_add_numeric(&list, &data->idx,
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_numeric(list, &data->idx,
|
||||
type, config, $3));
|
||||
parse_events__free_terms($3);
|
||||
$$ = list;
|
||||
|
@ -225,11 +234,12 @@ value_sym '/' event_config '/'
|
|||
value_sym sep_slash_dc
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
int type = $1 >> 16;
|
||||
int config = $1 & 255;
|
||||
|
||||
ABORT_ON(parse_events_add_numeric(&list, &data->idx,
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_numeric(list, &data->idx,
|
||||
type, config, NULL));
|
||||
$$ = list;
|
||||
}
|
||||
|
@ -238,27 +248,30 @@ event_legacy_cache:
|
|||
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
|
||||
ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5));
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, $5));
|
||||
$$ = list;
|
||||
}
|
||||
|
|
||||
PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
|
||||
ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, NULL));
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, NULL));
|
||||
$$ = list;
|
||||
}
|
||||
|
|
||||
PE_NAME_CACHE_TYPE
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
|
||||
ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL));
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_cache(list, &data->idx, $1, NULL, NULL));
|
||||
$$ = list;
|
||||
}
|
||||
|
||||
|
@ -266,9 +279,10 @@ event_legacy_mem:
|
|||
PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
|
||||
ABORT_ON(parse_events_add_breakpoint(&list, &data->idx,
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_breakpoint(list, &data->idx,
|
||||
(void *) $2, $4));
|
||||
$$ = list;
|
||||
}
|
||||
|
@ -276,9 +290,10 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc
|
|||
PE_PREFIX_MEM PE_VALUE sep_dc
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
|
||||
ABORT_ON(parse_events_add_breakpoint(&list, &data->idx,
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_breakpoint(list, &data->idx,
|
||||
(void *) $2, NULL));
|
||||
$$ = list;
|
||||
}
|
||||
|
@ -287,9 +302,10 @@ event_legacy_tracepoint:
|
|||
PE_NAME ':' PE_NAME
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
|
||||
ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3));
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_tracepoint(list, &data->idx, $1, $3));
|
||||
$$ = list;
|
||||
}
|
||||
|
||||
|
@ -297,9 +313,10 @@ event_legacy_numeric:
|
|||
PE_VALUE ':' PE_VALUE
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
|
||||
ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL));
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_numeric(list, &data->idx, (u32)$1, $3, NULL));
|
||||
$$ = list;
|
||||
}
|
||||
|
||||
|
@ -307,9 +324,10 @@ event_legacy_raw:
|
|||
PE_RAW
|
||||
{
|
||||
struct parse_events_evlist *data = _data;
|
||||
struct list_head *list = NULL;
|
||||
struct list_head *list;
|
||||
|
||||
ABORT_ON(parse_events_add_numeric(&list, &data->idx,
|
||||
ALLOC_LIST(list);
|
||||
ABORT_ON(parse_events_add_numeric(list, &data->idx,
|
||||
PERF_TYPE_RAW, $1, NULL));
|
||||
$$ = list;
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head)
|
|||
* located at:
|
||||
* /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
|
||||
*/
|
||||
static int pmu_format(char *name, struct list_head *format)
|
||||
static int pmu_format(const char *name, struct list_head *format)
|
||||
{
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
|
@ -162,7 +162,7 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
|
|||
* Reading the pmu event aliases definition, which should be located at:
|
||||
* /sys/bus/event_source/devices/<dev>/events as sysfs group attributes.
|
||||
*/
|
||||
static int pmu_aliases(char *name, struct list_head *head)
|
||||
static int pmu_aliases(const char *name, struct list_head *head)
|
||||
{
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
|
@ -208,7 +208,7 @@ static int pmu_alias_terms(struct perf_pmu_alias *alias,
|
|||
* located at:
|
||||
* /sys/bus/event_source/devices/<dev>/type as sysfs attribute.
|
||||
*/
|
||||
static int pmu_type(char *name, __u32 *type)
|
||||
static int pmu_type(const char *name, __u32 *type)
|
||||
{
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
|
@ -266,7 +266,7 @@ static void pmu_read_sysfs(void)
|
|||
closedir(dir);
|
||||
}
|
||||
|
||||
static struct cpu_map *pmu_cpumask(char *name)
|
||||
static struct cpu_map *pmu_cpumask(const char *name)
|
||||
{
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
|
@ -293,7 +293,7 @@ static struct cpu_map *pmu_cpumask(char *name)
|
|||
return cpus;
|
||||
}
|
||||
|
||||
static struct perf_pmu *pmu_lookup(char *name)
|
||||
static struct perf_pmu *pmu_lookup(const char *name)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
LIST_HEAD(format);
|
||||
|
@ -330,7 +330,7 @@ static struct perf_pmu *pmu_lookup(char *name)
|
|||
return pmu;
|
||||
}
|
||||
|
||||
static struct perf_pmu *pmu_find(char *name)
|
||||
static struct perf_pmu *pmu_find(const char *name)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
|
||||
|
@ -356,7 +356,7 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct perf_pmu *perf_pmu__find(char *name)
|
||||
struct perf_pmu *perf_pmu__find(const char *name)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
|
||||
|
@ -564,3 +564,76 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to)
|
|||
for (b = from; b <= to; b++)
|
||||
set_bit(b, bits);
|
||||
}
|
||||
|
||||
static char *format_alias(char *buf, int len, struct perf_pmu *pmu,
|
||||
struct perf_pmu_alias *alias)
|
||||
{
|
||||
snprintf(buf, len, "%s/%s/", pmu->name, alias->name);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu,
|
||||
struct perf_pmu_alias *alias)
|
||||
{
|
||||
snprintf(buf, len, "%s OR %s/%s/", alias->name, pmu->name, alias->name);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int cmp_string(const void *a, const void *b)
|
||||
{
|
||||
const char * const *as = a;
|
||||
const char * const *bs = b;
|
||||
return strcmp(*as, *bs);
|
||||
}
|
||||
|
||||
void print_pmu_events(const char *event_glob, bool name_only)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
struct perf_pmu_alias *alias;
|
||||
char buf[1024];
|
||||
int printed = 0;
|
||||
int len, j;
|
||||
char **aliases;
|
||||
|
||||
pmu = NULL;
|
||||
len = 0;
|
||||
while ((pmu = perf_pmu__scan(pmu)) != NULL)
|
||||
list_for_each_entry(alias, &pmu->aliases, list)
|
||||
len++;
|
||||
aliases = malloc(sizeof(char *) * len);
|
||||
if (!aliases)
|
||||
return;
|
||||
pmu = NULL;
|
||||
j = 0;
|
||||
while ((pmu = perf_pmu__scan(pmu)) != NULL)
|
||||
list_for_each_entry(alias, &pmu->aliases, list) {
|
||||
char *name = format_alias(buf, sizeof(buf), pmu, alias);
|
||||
bool is_cpu = !strcmp(pmu->name, "cpu");
|
||||
|
||||
if (event_glob != NULL &&
|
||||
!(strglobmatch(name, event_glob) ||
|
||||
(!is_cpu && strglobmatch(alias->name,
|
||||
event_glob))))
|
||||
continue;
|
||||
aliases[j] = name;
|
||||
if (is_cpu && !name_only)
|
||||
aliases[j] = format_alias_or(buf, sizeof(buf),
|
||||
pmu, alias);
|
||||
aliases[j] = strdup(aliases[j]);
|
||||
j++;
|
||||
}
|
||||
len = j;
|
||||
qsort(aliases, len, sizeof(char *), cmp_string);
|
||||
for (j = 0; j < len; j++) {
|
||||
if (name_only) {
|
||||
printf("%s ", aliases[j]);
|
||||
continue;
|
||||
}
|
||||
printf(" %-50s [Kernel PMU event]\n", aliases[j]);
|
||||
free(aliases[j]);
|
||||
printed++;
|
||||
}
|
||||
if (printed)
|
||||
printf("\n");
|
||||
free(aliases);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum {
|
||||
PERF_PMU_FORMAT_VALUE_CONFIG,
|
||||
|
@ -21,7 +22,7 @@ struct perf_pmu {
|
|||
struct list_head list;
|
||||
};
|
||||
|
||||
struct perf_pmu *perf_pmu__find(char *name);
|
||||
struct perf_pmu *perf_pmu__find(const char *name);
|
||||
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
|
||||
struct list_head *head_terms);
|
||||
int perf_pmu__config_terms(struct list_head *formats,
|
||||
|
@ -40,5 +41,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head);
|
|||
|
||||
struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
|
||||
|
||||
void print_pmu_events(const char *event_glob, bool name_only);
|
||||
|
||||
int perf_pmu__test(void);
|
||||
#endif /* __PMU_H */
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <traceevent/event-parse.h>
|
||||
|
||||
#include <byteswap.h>
|
||||
#include <unistd.h>
|
||||
|
@ -12,7 +13,6 @@
|
|||
#include "sort.h"
|
||||
#include "util.h"
|
||||
#include "cpumap.h"
|
||||
#include "event-parse.h"
|
||||
#include "perf_regs.h"
|
||||
#include "vdso.h"
|
||||
|
||||
|
@ -24,7 +24,7 @@ static int perf_session__open(struct perf_session *self, bool force)
|
|||
self->fd_pipe = true;
|
||||
self->fd = STDIN_FILENO;
|
||||
|
||||
if (perf_session__read_header(self, self->fd) < 0)
|
||||
if (perf_session__read_header(self) < 0)
|
||||
pr_err("incompatible file format (rerun with -v to learn more)");
|
||||
|
||||
return 0;
|
||||
|
@ -56,7 +56,7 @@ static int perf_session__open(struct perf_session *self, bool force)
|
|||
goto out_close;
|
||||
}
|
||||
|
||||
if (perf_session__read_header(self, self->fd) < 0) {
|
||||
if (perf_session__read_header(self) < 0) {
|
||||
pr_err("incompatible file format (rerun with -v to learn more)");
|
||||
goto out_close;
|
||||
}
|
||||
|
@ -193,7 +193,9 @@ void perf_session__delete(struct perf_session *self)
|
|||
vdso__exit();
|
||||
}
|
||||
|
||||
static int process_event_synth_tracing_data_stub(union perf_event *event
|
||||
static int process_event_synth_tracing_data_stub(struct perf_tool *tool
|
||||
__maybe_unused,
|
||||
union perf_event *event
|
||||
__maybe_unused,
|
||||
struct perf_session *session
|
||||
__maybe_unused)
|
||||
|
@ -202,7 +204,8 @@ static int process_event_synth_tracing_data_stub(union perf_event *event
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int process_event_synth_attr_stub(union perf_event *event __maybe_unused,
|
||||
static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_evlist **pevlist
|
||||
__maybe_unused)
|
||||
{
|
||||
|
@ -238,13 +241,6 @@ static int process_finished_round_stub(struct perf_tool *tool __maybe_unused,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int process_event_type_stub(struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_finished_round(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
@ -271,8 +267,6 @@ static void perf_tool__fill_defaults(struct perf_tool *tool)
|
|||
tool->unthrottle = process_event_stub;
|
||||
if (tool->attr == NULL)
|
||||
tool->attr = process_event_synth_attr_stub;
|
||||
if (tool->event_type == NULL)
|
||||
tool->event_type = process_event_type_stub;
|
||||
if (tool->tracing_data == NULL)
|
||||
tool->tracing_data = process_event_synth_tracing_data_stub;
|
||||
if (tool->build_id == NULL)
|
||||
|
@ -921,16 +915,14 @@ static int perf_session__process_user_event(struct perf_session *session, union
|
|||
/* These events are processed right away */
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_HEADER_ATTR:
|
||||
err = tool->attr(event, &session->evlist);
|
||||
err = tool->attr(tool, event, &session->evlist);
|
||||
if (err == 0)
|
||||
perf_session__set_id_hdr_size(session);
|
||||
return err;
|
||||
case PERF_RECORD_HEADER_EVENT_TYPE:
|
||||
return tool->event_type(tool, event);
|
||||
case PERF_RECORD_HEADER_TRACING_DATA:
|
||||
/* setup for reading amidst mmap */
|
||||
lseek(session->fd, file_offset, SEEK_SET);
|
||||
return tool->tracing_data(event, session);
|
||||
return tool->tracing_data(tool, event, session);
|
||||
case PERF_RECORD_HEADER_BUILD_ID:
|
||||
return tool->build_id(tool, event, session);
|
||||
case PERF_RECORD_FINISHED_ROUND:
|
||||
|
@ -1091,8 +1083,10 @@ more:
|
|||
perf_event_header__bswap(&event->header);
|
||||
|
||||
size = event->header.size;
|
||||
if (size == 0)
|
||||
size = 8;
|
||||
if (size < sizeof(struct perf_event_header)) {
|
||||
pr_err("bad event header size\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (size > cur_size) {
|
||||
void *new = realloc(buf, size);
|
||||
|
@ -1161,8 +1155,12 @@ fetch_mmaped_event(struct perf_session *session,
|
|||
if (session->header.needs_swap)
|
||||
perf_event_header__bswap(&event->header);
|
||||
|
||||
if (head + event->header.size > mmap_size)
|
||||
if (head + event->header.size > mmap_size) {
|
||||
/* We're not fetching the event so swap back again */
|
||||
if (session->header.needs_swap)
|
||||
perf_event_header__bswap(&event->header);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
@ -1242,7 +1240,7 @@ more:
|
|||
|
||||
size = event->header.size;
|
||||
|
||||
if (size == 0 ||
|
||||
if (size < sizeof(struct perf_event_header) ||
|
||||
perf_session__process_event(session, event, tool, file_pos) < 0) {
|
||||
pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",
|
||||
file_offset + head, event->header.size,
|
||||
|
@ -1397,9 +1395,8 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
|
|||
|
||||
if (symbol_conf.use_callchain && sample->callchain) {
|
||||
|
||||
|
||||
if (machine__resolve_callchain(machine, evsel, al.thread,
|
||||
sample, NULL) != 0) {
|
||||
sample, NULL, NULL) != 0) {
|
||||
if (verbose)
|
||||
error("Failed to resolve callchain. Skipping\n");
|
||||
return;
|
||||
|
|
|
@ -37,7 +37,6 @@ struct perf_session {
|
|||
int fd;
|
||||
bool fd_pipe;
|
||||
bool repipe;
|
||||
char *cwd;
|
||||
struct ordered_samples ordered_samples;
|
||||
char filename[1];
|
||||
};
|
||||
|
|
|
@ -7,6 +7,8 @@ const char default_parent_pattern[] = "^sys_|^do_page_fault";
|
|||
const char *parent_pattern = default_parent_pattern;
|
||||
const char default_sort_order[] = "comm,dso,symbol";
|
||||
const char *sort_order = default_sort_order;
|
||||
regex_t ignore_callees_regex;
|
||||
int have_ignore_callees = 0;
|
||||
int sort__need_collapse = 0;
|
||||
int sort__has_parent = 0;
|
||||
int sort__has_sym = 0;
|
||||
|
@ -55,14 +57,14 @@ static int64_t cmp_null(void *l, void *r)
|
|||
static int64_t
|
||||
sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
return right->thread->pid - left->thread->pid;
|
||||
return right->thread->tid - left->thread->tid;
|
||||
}
|
||||
|
||||
static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
return repsep_snprintf(bf, size, "%*s:%5d", width - 6,
|
||||
self->thread->comm ?: "", self->thread->pid);
|
||||
self->thread->comm ?: "", self->thread->tid);
|
||||
}
|
||||
|
||||
struct sort_entry sort_thread = {
|
||||
|
@ -77,7 +79,7 @@ struct sort_entry sort_thread = {
|
|||
static int64_t
|
||||
sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
return right->thread->pid - left->thread->pid;
|
||||
return right->thread->tid - left->thread->tid;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
|
|
|
@ -29,6 +29,8 @@ extern const char *sort_order;
|
|||
extern const char default_parent_pattern[];
|
||||
extern const char *parent_pattern;
|
||||
extern const char default_sort_order[];
|
||||
extern regex_t ignore_callees_regex;
|
||||
extern int have_ignore_callees;
|
||||
extern int sort__need_collapse;
|
||||
extern int sort__has_parent;
|
||||
extern int sort__has_sym;
|
||||
|
@ -87,6 +89,9 @@ struct hist_entry {
|
|||
|
||||
struct hist_entry_diff diff;
|
||||
|
||||
/* We are added by hists__add_dummy_entry. */
|
||||
bool dummy;
|
||||
|
||||
/* XXX These two should move to some tree widget lib */
|
||||
u16 row_offset;
|
||||
u16 nr_rows;
|
||||
|
@ -183,4 +188,6 @@ int setup_sorting(void);
|
|||
extern int sort_dimension__add(const char *);
|
||||
void sort__setup_elide(FILE *fp);
|
||||
|
||||
int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset);
|
||||
|
||||
#endif /* __PERF_SORT_H */
|
||||
|
|
|
@ -387,3 +387,27 @@ void *memdup(const void *src, size_t len)
|
|||
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* str_append - reallocate string and append another
|
||||
* @s: pointer to string pointer
|
||||
* @len: pointer to len (initialized)
|
||||
* @a: string to append.
|
||||
*/
|
||||
int str_append(char **s, int *len, const char *a)
|
||||
{
|
||||
int olen = *s ? strlen(*s) : 0;
|
||||
int nlen = olen + strlen(a) + 1;
|
||||
if (*len < nlen) {
|
||||
*len = *len * 2;
|
||||
if (*len < nlen)
|
||||
*len = nlen;
|
||||
*s = realloc(*s, *len);
|
||||
if (!*s)
|
||||
return -ENOMEM;
|
||||
if (olen == 0)
|
||||
**s = 0;
|
||||
}
|
||||
strcat(*s, a);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -888,8 +888,11 @@ int dso__load_vmlinux(struct dso *dso, struct map *map,
|
|||
char symfs_vmlinux[PATH_MAX];
|
||||
enum dso_binary_type symtab_type;
|
||||
|
||||
snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s",
|
||||
symbol_conf.symfs, vmlinux);
|
||||
if (vmlinux[0] == '/')
|
||||
snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux);
|
||||
else
|
||||
snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s",
|
||||
symbol_conf.symfs, vmlinux);
|
||||
|
||||
if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
|
||||
symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX;
|
||||
|
|
|
@ -7,17 +7,17 @@
|
|||
#include "util.h"
|
||||
#include "debug.h"
|
||||
|
||||
struct thread *thread__new(pid_t pid)
|
||||
struct thread *thread__new(pid_t tid)
|
||||
{
|
||||
struct thread *self = zalloc(sizeof(*self));
|
||||
|
||||
if (self != NULL) {
|
||||
map_groups__init(&self->mg);
|
||||
self->pid = pid;
|
||||
self->tid = tid;
|
||||
self->ppid = -1;
|
||||
self->comm = malloc(32);
|
||||
if (self->comm)
|
||||
snprintf(self->comm, 32, ":%d", self->pid);
|
||||
snprintf(self->comm, 32, ":%d", self->tid);
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -57,7 +57,7 @@ int thread__comm_len(struct thread *self)
|
|||
|
||||
size_t thread__fprintf(struct thread *thread, FILE *fp)
|
||||
{
|
||||
return fprintf(fp, "Thread %d %s\n", thread->pid, thread->comm) +
|
||||
return fprintf(fp, "Thread %d %s\n", thread->tid, thread->comm) +
|
||||
map_groups__fprintf(&thread->mg, verbose, fp);
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ int thread__fork(struct thread *self, struct thread *parent)
|
|||
if (map_groups__clone(&self->mg, &parent->mg, i) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
self->ppid = parent->pid;
|
||||
self->ppid = parent->tid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ struct thread {
|
|||
struct list_head node;
|
||||
};
|
||||
struct map_groups mg;
|
||||
pid_t pid;
|
||||
pid_t tid;
|
||||
pid_t ppid;
|
||||
char shortname[3];
|
||||
bool comm_set;
|
||||
|
@ -24,7 +24,7 @@ struct thread {
|
|||
|
||||
struct machine;
|
||||
|
||||
struct thread *thread__new(pid_t pid);
|
||||
struct thread *thread__new(pid_t tid);
|
||||
void thread__delete(struct thread *self);
|
||||
|
||||
int thread__set_comm(struct thread *self, const char *comm);
|
||||
|
@ -47,4 +47,14 @@ void thread__find_addr_location(struct thread *thread, struct machine *machine,
|
|||
u8 cpumode, enum map_type type, u64 addr,
|
||||
struct addr_location *al,
|
||||
symbol_filter_t filter);
|
||||
|
||||
static inline void *thread__priv(struct thread *thread)
|
||||
{
|
||||
return thread->priv;
|
||||
}
|
||||
|
||||
static inline void thread__set_priv(struct thread *thread, void *p)
|
||||
{
|
||||
thread->priv = p;
|
||||
}
|
||||
#endif /* __PERF_THREAD_H */
|
||||
|
|
|
@ -18,12 +18,9 @@ typedef int (*event_sample)(struct perf_tool *tool, union perf_event *event,
|
|||
typedef int (*event_op)(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine);
|
||||
|
||||
typedef int (*event_attr_op)(union perf_event *event,
|
||||
typedef int (*event_attr_op)(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_evlist **pevlist);
|
||||
typedef int (*event_simple_op)(struct perf_tool *tool, union perf_event *event);
|
||||
|
||||
typedef int (*event_synth_op)(union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
typedef int (*event_op2)(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
@ -39,8 +36,7 @@ struct perf_tool {
|
|||
throttle,
|
||||
unthrottle;
|
||||
event_attr_op attr;
|
||||
event_synth_op tracing_data;
|
||||
event_simple_op event_type;
|
||||
event_op2 tracing_data;
|
||||
event_op2 finished_round,
|
||||
build_id;
|
||||
bool ordered_samples;
|
||||
|
|
|
@ -46,65 +46,6 @@
|
|||
static int output_fd;
|
||||
|
||||
|
||||
static const char *find_debugfs(void)
|
||||
{
|
||||
const char *path = perf_debugfs_mount(NULL);
|
||||
|
||||
if (!path)
|
||||
pr_debug("Your kernel does not support the debugfs filesystem");
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the path to the debugfs/tracing
|
||||
* Allocates the string and stores it.
|
||||
*/
|
||||
static const char *find_tracing_dir(void)
|
||||
{
|
||||
static char *tracing;
|
||||
static int tracing_found;
|
||||
const char *debugfs;
|
||||
|
||||
if (tracing_found)
|
||||
return tracing;
|
||||
|
||||
debugfs = find_debugfs();
|
||||
if (!debugfs)
|
||||
return NULL;
|
||||
|
||||
tracing = malloc(strlen(debugfs) + 9);
|
||||
if (!tracing)
|
||||
return NULL;
|
||||
|
||||
sprintf(tracing, "%s/tracing", debugfs);
|
||||
|
||||
tracing_found = 1;
|
||||
return tracing;
|
||||
}
|
||||
|
||||
static char *get_tracing_file(const char *name)
|
||||
{
|
||||
const char *tracing;
|
||||
char *file;
|
||||
|
||||
tracing = find_tracing_dir();
|
||||
if (!tracing)
|
||||
return NULL;
|
||||
|
||||
file = malloc(strlen(tracing) + strlen(name) + 2);
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
sprintf(file, "%s/%s", tracing, name);
|
||||
return file;
|
||||
}
|
||||
|
||||
static void put_tracing_file(char *file)
|
||||
{
|
||||
free(file);
|
||||
}
|
||||
|
||||
int bigendian(void)
|
||||
{
|
||||
unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0};
|
||||
|
@ -160,7 +101,7 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int read_header_files(void)
|
||||
static int record_header_files(void)
|
||||
{
|
||||
char *path;
|
||||
struct stat st;
|
||||
|
@ -299,7 +240,7 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int read_ftrace_files(struct tracepoint_path *tps)
|
||||
static int record_ftrace_files(struct tracepoint_path *tps)
|
||||
{
|
||||
char *path;
|
||||
int ret;
|
||||
|
@ -328,7 +269,7 @@ static bool system_in_tp_list(char *sys, struct tracepoint_path *tps)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int read_event_files(struct tracepoint_path *tps)
|
||||
static int record_event_files(struct tracepoint_path *tps)
|
||||
{
|
||||
struct dirent *dent;
|
||||
struct stat st;
|
||||
|
@ -403,7 +344,7 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int read_proc_kallsyms(void)
|
||||
static int record_proc_kallsyms(void)
|
||||
{
|
||||
unsigned int size;
|
||||
const char *path = "/proc/kallsyms";
|
||||
|
@ -421,7 +362,7 @@ static int read_proc_kallsyms(void)
|
|||
return record_file(path, 4);
|
||||
}
|
||||
|
||||
static int read_ftrace_printk(void)
|
||||
static int record_ftrace_printk(void)
|
||||
{
|
||||
unsigned int size;
|
||||
char *path;
|
||||
|
@ -473,12 +414,27 @@ get_tracepoints_path(struct list_head *pattrs)
|
|||
if (pos->attr.type != PERF_TYPE_TRACEPOINT)
|
||||
continue;
|
||||
++nr_tracepoints;
|
||||
|
||||
if (pos->name) {
|
||||
ppath->next = tracepoint_name_to_path(pos->name);
|
||||
if (ppath->next)
|
||||
goto next;
|
||||
|
||||
if (strchr(pos->name, ':') == NULL)
|
||||
goto try_id;
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
try_id:
|
||||
ppath->next = tracepoint_id_to_path(pos->attr.config);
|
||||
if (!ppath->next) {
|
||||
error:
|
||||
pr_debug("No memory to alloc tracepoints list\n");
|
||||
put_tracepoints_path(&path);
|
||||
return NULL;
|
||||
}
|
||||
next:
|
||||
ppath = ppath->next;
|
||||
}
|
||||
|
||||
|
@ -520,8 +476,6 @@ static int tracing_data_header(void)
|
|||
else
|
||||
buf[0] = 0;
|
||||
|
||||
read_trace_init(buf[0], buf[0]);
|
||||
|
||||
if (write(output_fd, buf, 1) != 1)
|
||||
return -1;
|
||||
|
||||
|
@ -583,19 +537,19 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
|
|||
err = tracing_data_header();
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_header_files();
|
||||
err = record_header_files();
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_ftrace_files(tps);
|
||||
err = record_ftrace_files(tps);
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_event_files(tps);
|
||||
err = record_event_files(tps);
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_proc_kallsyms();
|
||||
err = record_proc_kallsyms();
|
||||
if (err)
|
||||
goto out;
|
||||
err = read_ftrace_printk();
|
||||
err = record_ftrace_printk();
|
||||
|
||||
out:
|
||||
/*
|
||||
|
|
|
@ -28,12 +28,6 @@
|
|||
#include "util.h"
|
||||
#include "trace-event.h"
|
||||
|
||||
int header_page_size_size;
|
||||
int header_page_ts_size;
|
||||
int header_page_data_offset;
|
||||
|
||||
bool latency_format;
|
||||
|
||||
struct pevent *read_trace_init(int file_bigendian, int host_bigendian)
|
||||
{
|
||||
struct pevent *pevent = pevent_alloc();
|
||||
|
|
|
@ -39,10 +39,6 @@
|
|||
|
||||
static int input_fd;
|
||||
|
||||
int file_bigendian;
|
||||
int host_bigendian;
|
||||
static int long_size;
|
||||
|
||||
static ssize_t trace_data_size;
|
||||
static bool repipe;
|
||||
|
||||
|
@ -216,7 +212,7 @@ static int read_ftrace_printk(struct pevent *pevent)
|
|||
static int read_header_files(struct pevent *pevent)
|
||||
{
|
||||
unsigned long long size;
|
||||
char *header_event;
|
||||
char *header_page;
|
||||
char buf[BUFSIZ];
|
||||
int ret = 0;
|
||||
|
||||
|
@ -229,13 +225,26 @@ static int read_header_files(struct pevent *pevent)
|
|||
}
|
||||
|
||||
size = read8(pevent);
|
||||
skip(size);
|
||||
|
||||
/*
|
||||
* The size field in the page is of type long,
|
||||
* use that instead, since it represents the kernel.
|
||||
*/
|
||||
long_size = header_page_size_size;
|
||||
header_page = malloc(size);
|
||||
if (header_page == NULL)
|
||||
return -1;
|
||||
|
||||
if (do_read(header_page, size) < 0) {
|
||||
pr_debug("did not read header page");
|
||||
free(header_page);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!pevent_parse_header_page(pevent, header_page, size,
|
||||
pevent_get_long_size(pevent))) {
|
||||
/*
|
||||
* The commit field in the page is of type long,
|
||||
* use that instead, since it represents the kernel.
|
||||
*/
|
||||
pevent_set_long_size(pevent, pevent->header_page_size_size);
|
||||
}
|
||||
free(header_page);
|
||||
|
||||
if (do_read(buf, 13) < 0)
|
||||
return -1;
|
||||
|
@ -246,14 +255,8 @@ static int read_header_files(struct pevent *pevent)
|
|||
}
|
||||
|
||||
size = read8(pevent);
|
||||
header_event = malloc(size);
|
||||
if (header_event == NULL)
|
||||
return -1;
|
||||
skip(size);
|
||||
|
||||
if (do_read(header_event, size) < 0)
|
||||
ret = -1;
|
||||
|
||||
free(header_event);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -349,6 +352,10 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)
|
|||
int show_funcs = 0;
|
||||
int show_printk = 0;
|
||||
ssize_t size = -1;
|
||||
int file_bigendian;
|
||||
int host_bigendian;
|
||||
int file_long_size;
|
||||
int file_page_size;
|
||||
struct pevent *pevent;
|
||||
int err;
|
||||
|
||||
|
@ -391,12 +398,15 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe)
|
|||
|
||||
if (do_read(buf, 1) < 0)
|
||||
goto out;
|
||||
long_size = buf[0];
|
||||
file_long_size = buf[0];
|
||||
|
||||
page_size = read4(pevent);
|
||||
if (!page_size)
|
||||
file_page_size = read4(pevent);
|
||||
if (!file_page_size)
|
||||
goto out;
|
||||
|
||||
pevent_set_long_size(pevent, file_long_size);
|
||||
pevent_set_page_size(pevent, file_page_size);
|
||||
|
||||
err = read_header_files(pevent);
|
||||
if (err)
|
||||
goto out;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#ifndef _PERF_UTIL_TRACE_EVENT_H
|
||||
#define _PERF_UTIL_TRACE_EVENT_H
|
||||
|
||||
#include <traceevent/event-parse.h>
|
||||
#include "parse-events.h"
|
||||
#include "event-parse.h"
|
||||
#include "session.h"
|
||||
|
||||
struct machine;
|
||||
|
@ -10,23 +10,8 @@ struct perf_sample;
|
|||
union perf_event;
|
||||
struct perf_tool;
|
||||
|
||||
extern int header_page_size_size;
|
||||
extern int header_page_ts_size;
|
||||
extern int header_page_data_offset;
|
||||
|
||||
extern bool latency_format;
|
||||
extern struct pevent *perf_pevent;
|
||||
|
||||
enum {
|
||||
RINGBUF_TYPE_PADDING = 29,
|
||||
RINGBUF_TYPE_TIME_EXTEND = 30,
|
||||
RINGBUF_TYPE_TIME_STAMP = 31,
|
||||
};
|
||||
|
||||
#ifndef TS_SHIFT
|
||||
#define TS_SHIFT 27
|
||||
#endif
|
||||
|
||||
int bigendian(void);
|
||||
|
||||
struct pevent *read_trace_init(int file_bigendian, int host_bigendian);
|
||||
|
|
|
@ -269,3 +269,62 @@ void perf_debugfs_set_path(const char *mntpt)
|
|||
snprintf(debugfs_mountpoint, strlen(debugfs_mountpoint), "%s", mntpt);
|
||||
set_tracing_events_path(mntpt);
|
||||
}
|
||||
|
||||
static const char *find_debugfs(void)
|
||||
{
|
||||
const char *path = perf_debugfs_mount(NULL);
|
||||
|
||||
if (!path)
|
||||
fprintf(stderr, "Your kernel does not support the debugfs filesystem");
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the path to the debugfs/tracing
|
||||
* Allocates the string and stores it.
|
||||
*/
|
||||
const char *find_tracing_dir(void)
|
||||
{
|
||||
static char *tracing;
|
||||
static int tracing_found;
|
||||
const char *debugfs;
|
||||
|
||||
if (tracing_found)
|
||||
return tracing;
|
||||
|
||||
debugfs = find_debugfs();
|
||||
if (!debugfs)
|
||||
return NULL;
|
||||
|
||||
tracing = malloc(strlen(debugfs) + 9);
|
||||
if (!tracing)
|
||||
return NULL;
|
||||
|
||||
sprintf(tracing, "%s/tracing", debugfs);
|
||||
|
||||
tracing_found = 1;
|
||||
return tracing;
|
||||
}
|
||||
|
||||
char *get_tracing_file(const char *name)
|
||||
{
|
||||
const char *tracing;
|
||||
char *file;
|
||||
|
||||
tracing = find_tracing_dir();
|
||||
if (!tracing)
|
||||
return NULL;
|
||||
|
||||
file = malloc(strlen(tracing) + strlen(name) + 2);
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
sprintf(file, "%s/%s", tracing, name);
|
||||
return file;
|
||||
}
|
||||
|
||||
void put_tracing_file(char *file)
|
||||
{
|
||||
free(file);
|
||||
}
|
||||
|
|
|
@ -80,6 +80,9 @@ extern char buildid_dir[];
|
|||
extern char tracing_events_path[];
|
||||
extern void perf_debugfs_set_path(const char *mountpoint);
|
||||
const char *perf_debugfs_mount(const char *mountpoint);
|
||||
const char *find_tracing_dir(void);
|
||||
char *get_tracing_file(const char *name);
|
||||
void put_tracing_file(char *file);
|
||||
|
||||
/* On most systems <limits.h> would have given us this, but
|
||||
* not on some systems (e.g. GNU/Hurd).
|
||||
|
|
Loading…
Reference in New Issue