]> Pileus Git - ~andy/linux/commitdiff
Merge tag 'v3.11-rc5' into perf/core
authorIngo Molnar <mingo@kernel.org>
Thu, 15 Aug 2013 08:00:09 +0000 (10:00 +0200)
committerIngo Molnar <mingo@kernel.org>
Thu, 15 Aug 2013 08:00:09 +0000 (10:00 +0200)
Merge Linux 3.11-rc5, to sync up with the latest upstream fixes since -rc1.

Signed-off-by: Ingo Molnar <mingo@kernel.org>
128 files changed:
arch/powerpc/include/asm/perf_event_server.h
arch/powerpc/perf/power7-events-list.h [new file with mode: 0644]
arch/powerpc/perf/power7-pmu.c
arch/x86/Kconfig
arch/x86/include/asm/alternative.h
arch/x86/include/asm/tsc.h
arch/x86/kernel/alternative.c
arch/x86/kernel/cpu/perf_event.c
arch/x86/kernel/jump_label.c
arch/x86/kernel/kprobes/common.h
arch/x86/kernel/kprobes/core.c
arch/x86/kernel/kprobes/opt.c
arch/x86/kernel/traps.c
arch/x86/kernel/tsc.c
include/linux/sched.h
include/uapi/linux/perf_event.h
kernel/events/callchain.c
kernel/events/core.c
kernel/sched/core.c
kernel/sched/fair.c
kernel/sched/sched.h
kernel/watchdog.c
tools/lib/lk/Makefile
tools/lib/traceevent/Makefile
tools/lib/traceevent/event-parse.c
tools/lib/traceevent/event-parse.h
tools/lib/traceevent/kbuffer-parse.c [new file with mode: 0644]
tools/lib/traceevent/kbuffer.h [new file with mode: 0644]
tools/lib/traceevent/trace-seq.c
tools/perf/Documentation/perf-diff.txt
tools/perf/Documentation/perf-kvm.txt
tools/perf/Documentation/perf-list.txt
tools/perf/Documentation/perf-report.txt
tools/perf/Documentation/perf-stat.txt
tools/perf/Documentation/perf-top.txt
tools/perf/Documentation/perf-trace.txt
tools/perf/Makefile
tools/perf/arch/x86/Makefile
tools/perf/arch/x86/util/tsc.c [new file with mode: 0644]
tools/perf/arch/x86/util/tsc.h [new file with mode: 0644]
tools/perf/bench/mem-memcpy.c
tools/perf/builtin-annotate.c
tools/perf/builtin-diff.c
tools/perf/builtin-inject.c
tools/perf/builtin-kmem.c
tools/perf/builtin-kvm.c
tools/perf/builtin-list.c
tools/perf/builtin-mem.c
tools/perf/builtin-record.c
tools/perf/builtin-report.c
tools/perf/builtin-sched.c
tools/perf/builtin-script.c
tools/perf/builtin-stat.c
tools/perf/builtin-timechart.c
tools/perf/builtin-top.c
tools/perf/builtin-trace.c
tools/perf/config/Makefile
tools/perf/perf.h
tools/perf/python/twatch.py
tools/perf/tests/attr/test-record-group-sampling [new file with mode: 0644]
tools/perf/tests/builtin-test.c
tools/perf/tests/code-reading.c [new file with mode: 0644]
tools/perf/tests/dso-data.c
tools/perf/tests/evsel-tp-sched.c
tools/perf/tests/hists_link.c
tools/perf/tests/make
tools/perf/tests/parse-events.c
tools/perf/tests/perf-time-to-tsc.c [new file with mode: 0644]
tools/perf/tests/tests.h
tools/perf/tests/vmlinux-kallsyms.c
tools/perf/ui/browsers/annotate.c
tools/perf/ui/browsers/hists.c
tools/perf/ui/gtk/hists.c
tools/perf/ui/hist.c
tools/perf/ui/setup.c
tools/perf/ui/stdio/hist.c
tools/perf/util/annotate.c
tools/perf/util/callchain.c
tools/perf/util/callchain.h
tools/perf/util/cpumap.h
tools/perf/util/dso.c
tools/perf/util/dso.h
tools/perf/util/event.c
tools/perf/util/event.h
tools/perf/util/evlist.c
tools/perf/util/evlist.h
tools/perf/util/evsel.c
tools/perf/util/evsel.h
tools/perf/util/header.c
tools/perf/util/header.h
tools/perf/util/hist.c
tools/perf/util/hist.h
tools/perf/util/include/linux/string.h
tools/perf/util/machine.c
tools/perf/util/machine.h
tools/perf/util/map.c
tools/perf/util/map.h
tools/perf/util/parse-events.c
tools/perf/util/parse-events.h
tools/perf/util/parse-events.l
tools/perf/util/parse-events.y
tools/perf/util/pmu.c
tools/perf/util/pmu.h
tools/perf/util/scripting-engines/trace-event-perl.c
tools/perf/util/scripting-engines/trace-event-python.c
tools/perf/util/session.c
tools/perf/util/session.h
tools/perf/util/sort.c
tools/perf/util/sort.h
tools/perf/util/stat.c
tools/perf/util/stat.h
tools/perf/util/string.c
tools/perf/util/symbol-elf.c
tools/perf/util/symbol-minimal.c
tools/perf/util/symbol.c
tools/perf/util/symbol.h
tools/perf/util/thread.c
tools/perf/util/thread.h
tools/perf/util/tool.h
tools/perf/util/top.h
tools/perf/util/trace-event-info.c
tools/perf/util/trace-event-parse.c
tools/perf/util/trace-event-read.c
tools/perf/util/trace-event-scripting.c
tools/perf/util/trace-event.h
tools/perf/util/unwind.c
tools/perf/util/util.c
tools/perf/util/util.h

index 8b24926447544c2fed3eb44de6464071a20f15dc..3fd2f1b6f90617cf76b9e842e1093bc0db8d70ef 100644 (file)
@@ -138,11 +138,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)
diff --git a/arch/powerpc/perf/power7-events-list.h b/arch/powerpc/perf/power7-events-list.h
new file mode 100644 (file)
index 0000000..687790a
--- /dev/null
@@ -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)
index d1821b8bbc4c778024b9fbe424a0740671ca3c9d..56c67bca2f7558435110451b1e7659a20f84ef72 100644 (file)
 /*
  * 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 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
+#define EVENT(_name, _code) \
+       PME_##_name = _code,
+
+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);
-
-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);
-
-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);
+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);
+
+#define EVENT(_name, _code)     POWER_EVENT_ATTR(_name, _name);
+#include "power7-events-list.h"
+#undef EVENT
+
+#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),
-
-       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),
+       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),
+
+       #include "power7-events-list.h"
+       #undef EVENT
        NULL
 };
 
-
 static struct attribute_group power7_pmu_events_group = {
        .name = "events",
        .attrs = power7_events_attr,
index b32ebf92b0ce96b5798bdf2a48043986be6ba6ea..3106e0e2647af9391ccd64d50a1462d5b2d47e44 100644 (file)
@@ -81,7 +81,6 @@ config X86
        select HAVE_USER_RETURN_NOTIFIER
        select ARCH_BINFMT_ELF_RANDOMIZE_PIE
        select HAVE_ARCH_JUMP_LABEL
-       select HAVE_TEXT_POKE_SMP
        select HAVE_GENERIC_HARDIRQS
        select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
        select SPARSE_IRQ
@@ -2332,10 +2331,6 @@ config HAVE_ATOMIC_IOMAP
        def_bool y
        depends on X86_32
 
-config HAVE_TEXT_POKE_SMP
-       bool
-       select STOP_MACHINE if SMP
-
 config X86_DEV_DMA_OPS
        bool
        depends on X86_64 || STA2X11
index 58ed6d96a6acbd99169bbc8e964b6ba998d8774f..0a3f9c9f98d5ccf446980cfcf71b7055bb79ade8 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/stddef.h>
 #include <linux/stringify.h>
 #include <asm/asm.h>
+#include <asm/ptrace.h>
 
 /*
  * Alternative inline assembly for SMP.
@@ -220,20 +221,11 @@ extern void *text_poke_early(void *addr, const void *opcode, size_t len);
  * no thread can be preempted in the instructions being modified (no iret to an
  * invalid instruction possible) or if the instructions are changed from a
  * consistent state to another consistent state atomically.
- * More care must be taken when modifying code in the SMP case because of
- * Intel's errata. text_poke_smp() takes care that errata, but still
- * doesn't support NMI/MCE handler code modifying.
  * On the local CPU you need to be protected again NMI or MCE handlers seeing an
  * inconsistent instruction while you patch.
  */
-struct text_poke_param {
-       void *addr;
-       const void *opcode;
-       size_t len;
-};
-
 extern void *text_poke(void *addr, const void *opcode, size_t len);
-extern void *text_poke_smp(void *addr, const void *opcode, size_t len);
-extern void text_poke_smp_batch(struct text_poke_param *params, int n);
+extern int poke_int3_handler(struct pt_regs *regs);
+extern void *text_poke_bp(void *addr, const void *opcode, size_t len, void *handler);
 
 #endif /* _ASM_X86_ALTERNATIVE_H */
index c91e8b9d588b14d0f86b4ea7a82b5d07c8387c2c..235be70d5bb4d0e936fba155a785b82bb639ee1b 100644 (file)
@@ -49,6 +49,7 @@ extern void tsc_init(void);
 extern void mark_tsc_unstable(char *reason);
 extern int unsynchronized_tsc(void);
 extern int check_tsc_unstable(void);
+extern int check_tsc_disabled(void);
 extern unsigned long native_calibrate_tsc(void);
 
 extern int tsc_clocksource_reliable;
index c15cf9a25e279fc9934549a2c4a7d6faf26766fd..15e8563e5c244e0712c9696a3367067044a300b3 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/memory.h>
 #include <linux/stop_machine.h>
 #include <linux/slab.h>
+#include <linux/kdebug.h>
 #include <asm/alternative.h>
 #include <asm/sections.h>
 #include <asm/pgtable.h>
@@ -596,97 +597,93 @@ void *__kprobes text_poke(void *addr, const void *opcode, size_t len)
        return addr;
 }
 
-/*
- * Cross-modifying kernel text with stop_machine().
- * This code originally comes from immediate value.
- */
-static atomic_t stop_machine_first;
-static int wrote_text;
+static void do_sync_core(void *info)
+{
+       sync_core();
+}
 
-struct text_poke_params {
-       struct text_poke_param *params;
-       int nparams;
-};
+static bool bp_patching_in_progress;
+static void *bp_int3_handler, *bp_int3_addr;
 
-static int __kprobes stop_machine_text_poke(void *data)
+int poke_int3_handler(struct pt_regs *regs)
 {
-       struct text_poke_params *tpp = data;
-       struct text_poke_param *p;
-       int i;
+       /* bp_patching_in_progress */
+       smp_rmb();
 
-       if (atomic_xchg(&stop_machine_first, 0)) {
-               for (i = 0; i < tpp->nparams; i++) {
-                       p = &tpp->params[i];
-                       text_poke(p->addr, p->opcode, p->len);
-               }
-               smp_wmb();      /* Make sure other cpus see that this has run */
-               wrote_text = 1;
-       } else {
-               while (!wrote_text)
-                       cpu_relax();
-               smp_mb();       /* Load wrote_text before following execution */
-       }
+       if (likely(!bp_patching_in_progress))
+               return 0;
 
-       for (i = 0; i < tpp->nparams; i++) {
-               p = &tpp->params[i];
-               flush_icache_range((unsigned long)p->addr,
-                                  (unsigned long)p->addr + p->len);
-       }
-       /*
-        * Intel Archiecture Software Developer's Manual section 7.1.3 specifies
-        * that a core serializing instruction such as "cpuid" should be
-        * executed on _each_ core before the new instruction is made visible.
-        */
-       sync_core();
-       return 0;
-}
+       if (user_mode_vm(regs) || regs->ip != (unsigned long)bp_int3_addr)
+               return 0;
+
+       /* set up the specified breakpoint handler */
+       regs->ip = (unsigned long) bp_int3_handler;
+
+       return 1;
 
-/**
- * text_poke_smp - Update instructions on a live kernel on SMP
- * @addr: address to modify
- * @opcode: source of the copy
- * @len: length to copy
- *
- * Modify multi-byte instruction by using stop_machine() on SMP. This allows
- * user to poke/set multi-byte text on SMP. Only non-NMI/MCE code modifying
- * should be allowed, since stop_machine() does _not_ protect code against
- * NMI and MCE.
- *
- * Note: Must be called under get_online_cpus() and text_mutex.
- */
-void *__kprobes text_poke_smp(void *addr, const void *opcode, size_t len)
-{
-       struct text_poke_params tpp;
-       struct text_poke_param p;
-
-       p.addr = addr;
-       p.opcode = opcode;
-       p.len = len;
-       tpp.params = &p;
-       tpp.nparams = 1;
-       atomic_set(&stop_machine_first, 1);
-       wrote_text = 0;
-       /* Use __stop_machine() because the caller already got online_cpus. */
-       __stop_machine(stop_machine_text_poke, (void *)&tpp, cpu_online_mask);
-       return addr;
 }
 
 /**
- * text_poke_smp_batch - Update instructions on a live kernel on SMP
- * @params: an array of text_poke parameters
- * @n: the number of elements in params.
+ * text_poke_bp() -- update instructions on live kernel on SMP
+ * @addr:      address to patch
+ * @opcode:    opcode of new instruction
+ * @len:       length to copy
+ * @handler:   address to jump to when the temporary breakpoint is hit
  *
- * Modify multi-byte instruction by using stop_machine() on SMP. Since the
- * stop_machine() is heavy task, it is better to aggregate text_poke requests
- * and do it once if possible.
+ * Modify multi-byte instruction by using int3 breakpoint on SMP.
+ * We completely avoid stop_machine() here, and achieve the
+ * synchronization using int3 breakpoint.
  *
- * Note: Must be called under get_online_cpus() and text_mutex.
+ * The way it is done:
+ *     - add a int3 trap to the address that will be patched
+ *     - sync cores
+ *     - update all but the first byte of the patched range
+ *     - sync cores
+ *     - replace the first byte (int3) by the first byte of
+ *       replacing opcode
+ *     - sync cores
+ *
+ * Note: must be called under text_mutex.
  */
-void __kprobes text_poke_smp_batch(struct text_poke_param *params, int n)
+void *text_poke_bp(void *addr, const void *opcode, size_t len, void *handler)
 {
-       struct text_poke_params tpp = {.params = params, .nparams = n};
+       unsigned char int3 = 0xcc;
+
+       bp_int3_handler = handler;
+       bp_int3_addr = (u8 *)addr + sizeof(int3);
+       bp_patching_in_progress = true;
+       /*
+        * Corresponding read barrier in int3 notifier for
+        * making sure the in_progress flags is correctly ordered wrt.
+        * patching
+        */
+       smp_wmb();
+
+       text_poke(addr, &int3, sizeof(int3));
 
-       atomic_set(&stop_machine_first, 1);
-       wrote_text = 0;
-       __stop_machine(stop_machine_text_poke, (void *)&tpp, cpu_online_mask);
+       on_each_cpu(do_sync_core, NULL, 1);
+
+       if (len - sizeof(int3) > 0) {
+               /* patch all but the first byte */
+               text_poke((char *)addr + sizeof(int3),
+                         (const char *) opcode + sizeof(int3),
+                         len - sizeof(int3));
+               /*
+                * According to Intel, this core syncing is very likely
+                * not necessary and we'd be safe even without it. But
+                * better safe than sorry (plus there's not only Intel).
+                */
+               on_each_cpu(do_sync_core, NULL, 1);
+       }
+
+       /* patch the first byte */
+       text_poke(addr, opcode, sizeof(int3));
+
+       on_each_cpu(do_sync_core, NULL, 1);
+
+       bp_patching_in_progress = false;
+       smp_wmb();
+
+       return addr;
 }
+
index a7c7305030cc6c9600a601c1dd51f19560ac3830..8355c84b9729df767945c5f4c8a5f7c5db15fab8 100644 (file)
@@ -1884,6 +1884,7 @@ static struct pmu pmu = {
 void arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now)
 {
        userpg->cap_usr_time = 0;
+       userpg->cap_usr_time_zero = 0;
        userpg->cap_usr_rdpmc = x86_pmu.attr_rdpmc;
        userpg->pmc_width = x86_pmu.cntval_bits;
 
@@ -1897,6 +1898,11 @@ void arch_perf_update_userpage(struct perf_event_mmap_page *userpg, u64 now)
        userpg->time_mult = this_cpu_read(cyc2ns);
        userpg->time_shift = CYC2NS_SCALE_FACTOR;
        userpg->time_offset = this_cpu_read(cyc2ns_offset) - now;
+
+       if (sched_clock_stable && !check_tsc_disabled()) {
+               userpg->cap_usr_time_zero = 1;
+               userpg->time_zero = this_cpu_read(cyc2ns_offset);
+       }
 }
 
 /*
index 2889b3d438823ea58d6796355e08fb081c1896f7..460f5d9ceebb65c5337a6d3b57edbad7925b8c1a 100644 (file)
@@ -37,7 +37,19 @@ static void __jump_label_transform(struct jump_entry *entry,
        } else
                memcpy(&code, ideal_nops[NOP_ATOMIC5], JUMP_LABEL_NOP_SIZE);
 
-       (*poker)((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE);
+       /*
+        * Make text_poke_bp() a default fallback poker.
+        *
+        * At the time the change is being done, just ignore whether we
+        * are doing nop -> jump or jump -> nop transition, and assume
+        * always nop being the 'currently valid' instruction
+        *
+        */
+       if (poker)
+               (*poker)((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE);
+       else
+               text_poke_bp((void *)entry->code, &code, JUMP_LABEL_NOP_SIZE,
+                            (void *)entry->code + JUMP_LABEL_NOP_SIZE);
 }
 
 void arch_jump_label_transform(struct jump_entry *entry,
@@ -45,7 +57,7 @@ void arch_jump_label_transform(struct jump_entry *entry,
 {
        get_online_cpus();
        mutex_lock(&text_mutex);
-       __jump_label_transform(entry, type, text_poke_smp);
+       __jump_label_transform(entry, type, NULL);
        mutex_unlock(&text_mutex);
        put_online_cpus();
 }
index 2e9d4b5af036abf5a86868cb706e479df3911d23..c6ee63f927ab721dd542b016bcfb22d65a55f114 100644 (file)
@@ -82,14 +82,9 @@ extern void synthesize_reljump(void *from, void *to);
 extern void synthesize_relcall(void *from, void *to);
 
 #ifdef CONFIG_OPTPROBES
-extern int arch_init_optprobes(void);
 extern int setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter);
 extern unsigned long __recover_optprobed_insn(kprobe_opcode_t *buf, unsigned long addr);
 #else  /* !CONFIG_OPTPROBES */
-static inline int arch_init_optprobes(void)
-{
-       return 0;
-}
 static inline int setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter)
 {
        return 0;
index 211bce445522d541cc1bcc05e44e401b376f8093..cd49b2c96d32f9ede4bc47fa2c67f04803a78db7 100644 (file)
@@ -1068,7 +1068,7 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
 
 int __init arch_init_kprobes(void)
 {
-       return arch_init_optprobes();
+       return 0;
 }
 
 int __kprobes arch_trampoline_kprobe(struct kprobe *p)
index 76dc6f09572466426268955fbb11fdc845dca040..d71e994393760bca98efb30663f6bb5082b213c8 100644 (file)
@@ -371,31 +371,6 @@ int __kprobes arch_prepare_optimized_kprobe(struct optimized_kprobe *op)
        return 0;
 }
 
-#define MAX_OPTIMIZE_PROBES 256
-static struct text_poke_param *jump_poke_params;
-static struct jump_poke_buffer {
-       u8 buf[RELATIVEJUMP_SIZE];
-} *jump_poke_bufs;
-
-static void __kprobes setup_optimize_kprobe(struct text_poke_param *tprm,
-                                           u8 *insn_buf,
-                                           struct optimized_kprobe *op)
-{
-       s32 rel = (s32)((long)op->optinsn.insn -
-                       ((long)op->kp.addr + RELATIVEJUMP_SIZE));
-
-       /* Backup instructions which will be replaced by jump address */
-       memcpy(op->optinsn.copied_insn, op->kp.addr + INT3_SIZE,
-              RELATIVE_ADDR_SIZE);
-
-       insn_buf[0] = RELATIVEJUMP_OPCODE;
-       *(s32 *)(&insn_buf[1]) = rel;
-
-       tprm->addr = op->kp.addr;
-       tprm->opcode = insn_buf;
-       tprm->len = RELATIVEJUMP_SIZE;
-}
-
 /*
  * Replace breakpoints (int3) with relative jumps.
  * Caller must call with locking kprobe_mutex and text_mutex.
@@ -403,37 +378,38 @@ static void __kprobes setup_optimize_kprobe(struct text_poke_param *tprm,
 void __kprobes arch_optimize_kprobes(struct list_head *oplist)
 {
        struct optimized_kprobe *op, *tmp;
-       int c = 0;
+       u8 insn_buf[RELATIVEJUMP_SIZE];
 
        list_for_each_entry_safe(op, tmp, oplist, list) {
+               s32 rel = (s32)((long)op->optinsn.insn -
+                       ((long)op->kp.addr + RELATIVEJUMP_SIZE));
+
                WARN_ON(kprobe_disabled(&op->kp));
-               /* Setup param */
-               setup_optimize_kprobe(&jump_poke_params[c],
-                                     jump_poke_bufs[c].buf, op);
+
+               /* Backup instructions which will be replaced by jump address */
+               memcpy(op->optinsn.copied_insn, op->kp.addr + INT3_SIZE,
+                      RELATIVE_ADDR_SIZE);
+
+               insn_buf[0] = RELATIVEJUMP_OPCODE;
+               *(s32 *)(&insn_buf[1]) = rel;
+
+               text_poke_bp(op->kp.addr, insn_buf, RELATIVEJUMP_SIZE,
+                            op->optinsn.insn);
+
                list_del_init(&op->list);
-               if (++c >= MAX_OPTIMIZE_PROBES)
-                       break;
        }
-
-       /*
-        * text_poke_smp doesn't support NMI/MCE code modifying.
-        * However, since kprobes itself also doesn't support NMI/MCE
-        * code probing, it's not a problem.
-        */
-       text_poke_smp_batch(jump_poke_params, c);
 }
 
-static void __kprobes setup_unoptimize_kprobe(struct text_poke_param *tprm,
-                                             u8 *insn_buf,
-                                             struct optimized_kprobe *op)
+/* Replace a relative jump with a breakpoint (int3).  */
+void __kprobes arch_unoptimize_kprobe(struct optimized_kprobe *op)
 {
+       u8 insn_buf[RELATIVEJUMP_SIZE];
+
        /* Set int3 to first byte for kprobes */
        insn_buf[0] = BREAKPOINT_INSTRUCTION;
        memcpy(insn_buf + 1, op->optinsn.copied_insn, RELATIVE_ADDR_SIZE);
-
-       tprm->addr = op->kp.addr;
-       tprm->opcode = insn_buf;
-       tprm->len = RELATIVEJUMP_SIZE;
+       text_poke_bp(op->kp.addr, insn_buf, RELATIVEJUMP_SIZE,
+                    op->optinsn.insn);
 }
 
 /*
@@ -444,34 +420,11 @@ extern void arch_unoptimize_kprobes(struct list_head *oplist,
                                    struct list_head *done_list)
 {
        struct optimized_kprobe *op, *tmp;
-       int c = 0;
 
        list_for_each_entry_safe(op, tmp, oplist, list) {
-               /* Setup param */
-               setup_unoptimize_kprobe(&jump_poke_params[c],
-                                       jump_poke_bufs[c].buf, op);
+               arch_unoptimize_kprobe(op);
                list_move(&op->list, done_list);
-               if (++c >= MAX_OPTIMIZE_PROBES)
-                       break;
        }
-
-       /*
-        * text_poke_smp doesn't support NMI/MCE code modifying.
-        * However, since kprobes itself also doesn't support NMI/MCE
-        * code probing, it's not a problem.
-        */
-       text_poke_smp_batch(jump_poke_params, c);
-}
-
-/* Replace a relative jump with a breakpoint (int3).  */
-void __kprobes arch_unoptimize_kprobe(struct optimized_kprobe *op)
-{
-       u8 buf[RELATIVEJUMP_SIZE];
-
-       /* Set int3 to first byte for kprobes */
-       buf[0] = BREAKPOINT_INSTRUCTION;
-       memcpy(buf + 1, op->optinsn.copied_insn, RELATIVE_ADDR_SIZE);
-       text_poke_smp(op->kp.addr, buf, RELATIVEJUMP_SIZE);
 }
 
 int  __kprobes
@@ -491,22 +444,3 @@ setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter)
        }
        return 0;
 }
-
-int __kprobes arch_init_optprobes(void)
-{
-       /* Allocate code buffer and parameter array */
-       jump_poke_bufs = kmalloc(sizeof(struct jump_poke_buffer) *
-                                MAX_OPTIMIZE_PROBES, GFP_KERNEL);
-       if (!jump_poke_bufs)
-               return -ENOMEM;
-
-       jump_poke_params = kmalloc(sizeof(struct text_poke_param) *
-                                  MAX_OPTIMIZE_PROBES, GFP_KERNEL);
-       if (!jump_poke_params) {
-               kfree(jump_poke_bufs);
-               jump_poke_bufs = NULL;
-               return -ENOMEM;
-       }
-
-       return 0;
-}
index 1b23a1c9274696f108dcf2ad2482445523930de0..8c8093b146ca7cc565c48cb7ba66ca89deb06f9a 100644 (file)
@@ -58,6 +58,7 @@
 #include <asm/mce.h>
 #include <asm/fixmap.h>
 #include <asm/mach_traps.h>
+#include <asm/alternative.h>
 
 #ifdef CONFIG_X86_64
 #include <asm/x86_init.h>
@@ -327,6 +328,9 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
            ftrace_int3_handler(regs))
                return;
 #endif
+       if (poke_int3_handler(regs))
+               return;
+
        prev_state = exception_enter();
 #ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
        if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
index 6ff49247edf8f1e67bbd24dd15e34685bcafb010..930e5d48f560d017afd88f2af1bbf2d368dd1209 100644 (file)
@@ -89,6 +89,12 @@ int check_tsc_unstable(void)
 }
 EXPORT_SYMBOL_GPL(check_tsc_unstable);
 
+int check_tsc_disabled(void)
+{
+       return tsc_disabled;
+}
+EXPORT_SYMBOL_GPL(check_tsc_disabled);
+
 #ifdef CONFIG_X86_TSC
 int __init notsc_setup(char *str)
 {
index d722490da030c5906d991b7c76f8dc178edadee8..77c887b5d12f59dac0de740806d7f4447c2bb0b3 100644 (file)
@@ -1034,6 +1034,9 @@ struct task_struct {
 #ifdef CONFIG_SMP
        struct llist_node wake_entry;
        int on_cpu;
+       struct task_struct *last_wakee;
+       unsigned long wakee_flips;
+       unsigned long wakee_flip_decay_ts;
 #endif
        int on_rq;
 
index 0b1df41691e8cc2c5436ceeab4812c4753de6653..62c25a25291cfdb2d2e5cf18641566ba4e8e5b0b 100644 (file)
@@ -321,6 +321,7 @@ struct perf_event_attr {
 #define PERF_EVENT_IOC_PERIOD          _IOW('$', 4, __u64)
 #define PERF_EVENT_IOC_SET_OUTPUT      _IO ('$', 5)
 #define PERF_EVENT_IOC_SET_FILTER      _IOW('$', 6, char *)
+#define PERF_EVENT_IOC_ID              _IOR('$', 7, u64 *)
 
 enum perf_event_ioc_flags {
        PERF_IOC_FLAG_GROUP             = 1U << 0,
@@ -375,9 +376,12 @@ struct perf_event_mmap_page {
        __u64   time_running;           /* time event on cpu */
        union {
                __u64   capabilities;
-               __u64   cap_usr_time  : 1,
-                       cap_usr_rdpmc : 1,
-                       cap_____res   : 62;
+               struct {
+                       __u64   cap_usr_time            : 1,
+                               cap_usr_rdpmc           : 1,
+                               cap_usr_time_zero       : 1,
+                               cap_____res             : 61;
+               };
        };
 
        /*
@@ -418,12 +422,29 @@ struct perf_event_mmap_page {
        __u16   time_shift;
        __u32   time_mult;
        __u64   time_offset;
+       /*
+        * If cap_usr_time_zero, the hardware clock (e.g. TSC) can be calculated
+        * from sample timestamps.
+        *
+        *   time = timestamp - time_zero;
+        *   quot = time / time_mult;
+        *   rem  = time % time_mult;
+        *   cyc = (quot << time_shift) + (rem << time_shift) / time_mult;
+        *
+        * And vice versa:
+        *
+        *   quot = cyc >> time_shift;
+        *   rem  = cyc & ((1 << time_shift) - 1);
+        *   timestamp = time_zero + quot * time_mult +
+        *               ((rem * time_mult) >> time_shift);
+        */
+       __u64   time_zero;
 
                /*
                 * Hole for extension of the self monitor capabilities
                 */
 
-       __u64   __reserved[120];        /* align to 1k */
+       __u64   __reserved[119];        /* align to 1k */
 
        /*
         * Control data for the mmap() data buffer.
@@ -478,6 +499,16 @@ enum perf_event_type {
         * file will be supported by older perf tools, with these new optional
         * fields being ignored.
         *
+        * struct sample_id {
+        *      { u32                   pid, tid; } && PERF_SAMPLE_TID
+        *      { u64                   time;     } && PERF_SAMPLE_TIME
+        *      { u64                   id;       } && PERF_SAMPLE_ID
+        *      { u64                   stream_id;} && PERF_SAMPLE_STREAM_ID
+        *      { u32                   cpu, res; } && PERF_SAMPLE_CPU
+        * } && perf_event_attr::sample_id_all
+        */
+
+       /*
         * The MMAP events record the PROT_EXEC mappings so that we can
         * correlate userspace IPs to code. They have the following structure:
         *
@@ -498,6 +529,7 @@ enum perf_event_type {
         *      struct perf_event_header        header;
         *      u64                             id;
         *      u64                             lost;
+        *      struct sample_id                sample_id;
         * };
         */
        PERF_RECORD_LOST                        = 2,
@@ -508,6 +540,7 @@ enum perf_event_type {
         *
         *      u32                             pid, tid;
         *      char                            comm[];
+        *      struct sample_id                sample_id;
         * };
         */
        PERF_RECORD_COMM                        = 3,
@@ -518,6 +551,7 @@ enum perf_event_type {
         *      u32                             pid, ppid;
         *      u32                             tid, ptid;
         *      u64                             time;
+        *      struct sample_id                sample_id;
         * };
         */
        PERF_RECORD_EXIT                        = 4,
@@ -528,6 +562,7 @@ enum perf_event_type {
         *      u64                             time;
         *      u64                             id;
         *      u64                             stream_id;
+        *      struct sample_id                sample_id;
         * };
         */
        PERF_RECORD_THROTTLE                    = 5,
@@ -539,6 +574,7 @@ enum perf_event_type {
         *      u32                             pid, ppid;
         *      u32                             tid, ptid;
         *      u64                             time;
+        *      struct sample_id                sample_id;
         * };
         */
        PERF_RECORD_FORK                        = 7,
@@ -549,6 +585,7 @@ enum perf_event_type {
         *      u32                             pid, tid;
         *
         *      struct read_format              values;
+        *      struct sample_id                sample_id;
         * };
         */
        PERF_RECORD_READ                        = 8,
@@ -596,7 +633,7 @@ enum perf_event_type {
         *        u64                   dyn_size; } && PERF_SAMPLE_STACK_USER
         *
         *      { u64                   weight;   } && PERF_SAMPLE_WEIGHT
-        *      { u64                   data_src;     } && PERF_SAMPLE_DATA_SRC
+        *      { u64                   data_src; } && PERF_SAMPLE_DATA_SRC
         * };
         */
        PERF_RECORD_SAMPLE                      = 9,
index c77206184b8bd2c4c16cd981b9a383076f840b25..76a8bc5f62658a5004966fc4af7fc6f197d6f76d 100644 (file)
@@ -117,6 +117,8 @@ int get_callchain_buffers(void)
        err = alloc_callchain_buffers();
 exit:
        mutex_unlock(&callchain_mutex);
+       if (err)
+               atomic_dec(&nr_callchain_events);
 
        return err;
 }
index f86599e8c12371c7d78248e0c6b2a6869b9122ee..e82e70025d4233ea67cbb9b3ae280f759896ba09 100644 (file)
@@ -141,6 +141,7 @@ enum event_type_t {
 struct static_key_deferred perf_sched_events __read_mostly;
 static DEFINE_PER_CPU(atomic_t, perf_cgroup_events);
 static DEFINE_PER_CPU(atomic_t, perf_branch_stack_events);
+static DEFINE_PER_CPU(atomic_t, perf_freq_events);
 
 static atomic_t nr_mmap_events __read_mostly;
 static atomic_t nr_comm_events __read_mostly;
@@ -869,12 +870,8 @@ static void perf_pmu_rotate_start(struct pmu *pmu)
 
        WARN_ON(!irqs_disabled());
 
-       if (list_empty(&cpuctx->rotation_list)) {
-               int was_empty = list_empty(head);
+       if (list_empty(&cpuctx->rotation_list))
                list_add(&cpuctx->rotation_list, head);
-               if (was_empty)
-                       tick_nohz_full_kick();
-       }
 }
 
 static void get_ctx(struct perf_event_context *ctx)
@@ -1874,6 +1871,9 @@ static int  __perf_install_in_context(void *info)
        perf_pmu_enable(cpuctx->ctx.pmu);
        perf_ctx_unlock(cpuctx, task_ctx);
 
+       if (atomic_read(&__get_cpu_var(perf_freq_events)))
+               tick_nohz_full_kick();
+
        return 0;
 }
 
@@ -2811,10 +2811,11 @@ done:
 #ifdef CONFIG_NO_HZ_FULL
 bool perf_event_can_stop_tick(void)
 {
-       if (list_empty(&__get_cpu_var(rotation_list)))
-               return true;
-       else
+       if (atomic_read(&__get_cpu_var(perf_freq_events)) ||
+           __this_cpu_read(perf_throttled_count))
                return false;
+       else
+               return true;
 }
 #endif
 
@@ -3128,36 +3129,64 @@ static void free_event_rcu(struct rcu_head *head)
 static void ring_buffer_put(struct ring_buffer *rb);
 static void ring_buffer_detach(struct perf_event *event, struct ring_buffer *rb);
 
-static void free_event(struct perf_event *event)
+static void unaccount_event_cpu(struct perf_event *event, int cpu)
 {
-       irq_work_sync(&event->pending);
+       if (event->parent)
+               return;
+
+       if (has_branch_stack(event)) {
+               if (!(event->attach_state & PERF_ATTACH_TASK))
+                       atomic_dec(&per_cpu(perf_branch_stack_events, cpu));
+       }
+       if (is_cgroup_event(event))
+               atomic_dec(&per_cpu(perf_cgroup_events, cpu));
+
+       if (event->attr.freq)
+               atomic_dec(&per_cpu(perf_freq_events, cpu));
+}
+
+static void unaccount_event(struct perf_event *event)
+{
+       if (event->parent)
+               return;
+
+       if (event->attach_state & PERF_ATTACH_TASK)
+               static_key_slow_dec_deferred(&perf_sched_events);
+       if (event->attr.mmap || event->attr.mmap_data)
+               atomic_dec(&nr_mmap_events);
+       if (event->attr.comm)
+               atomic_dec(&nr_comm_events);
+       if (event->attr.task)
+               atomic_dec(&nr_task_events);
+       if (is_cgroup_event(event))
+               static_key_slow_dec_deferred(&perf_sched_events);
+       if (has_branch_stack(event))
+               static_key_slow_dec_deferred(&perf_sched_events);
+
+       unaccount_event_cpu(event, event->cpu);
+}
 
+static void __free_event(struct perf_event *event)
+{
        if (!event->parent) {
-               if (event->attach_state & PERF_ATTACH_TASK)
-                       static_key_slow_dec_deferred(&perf_sched_events);
-               if (event->attr.mmap || event->attr.mmap_data)
-                       atomic_dec(&nr_mmap_events);
-               if (event->attr.comm)
-                       atomic_dec(&nr_comm_events);
-               if (event->attr.task)
-                       atomic_dec(&nr_task_events);
                if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN)
                        put_callchain_buffers();
-               if (is_cgroup_event(event)) {
-                       atomic_dec(&per_cpu(perf_cgroup_events, event->cpu));
-                       static_key_slow_dec_deferred(&perf_sched_events);
-               }
-
-               if (has_branch_stack(event)) {
-                       static_key_slow_dec_deferred(&perf_sched_events);
-                       /* is system-wide event */
-                       if (!(event->attach_state & PERF_ATTACH_TASK)) {
-                               atomic_dec(&per_cpu(perf_branch_stack_events,
-                                                   event->cpu));
-                       }
-               }
        }
 
+       if (event->destroy)
+               event->destroy(event);
+
+       if (event->ctx)
+               put_ctx(event->ctx);
+
+       call_rcu(&event->rcu_head, free_event_rcu);
+}
+static void free_event(struct perf_event *event)
+{
+       irq_work_sync(&event->pending);
+
+       unaccount_event(event);
+
        if (event->rb) {
                struct ring_buffer *rb;
 
@@ -3180,13 +3209,8 @@ static void free_event(struct perf_event *event)
        if (is_cgroup_event(event))
                perf_detach_cgroup(event);
 
-       if (event->destroy)
-               event->destroy(event);
-
-       if (event->ctx)
-               put_ctx(event->ctx);
 
-       call_rcu(&event->rcu_head, free_event_rcu);
+       __free_event(event);
 }
 
 int perf_event_release_kernel(struct perf_event *event)
@@ -3544,6 +3568,15 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case PERF_EVENT_IOC_PERIOD:
                return perf_event_period(event, (u64 __user *)arg);
 
+       case PERF_EVENT_IOC_ID:
+       {
+               u64 id = primary_event_id(event);
+
+               if (copy_to_user((void __user *)arg, &id, sizeof(id)))
+                       return -EFAULT;
+               return 0;
+       }
+
        case PERF_EVENT_IOC_SET_OUTPUT:
        {
                int ret;
@@ -4355,7 +4388,8 @@ static void perf_output_read_group(struct perf_output_handle *handle,
        list_for_each_entry(sub, &leader->sibling_list, group_entry) {
                n = 0;
 
-               if (sub != event)
+               if ((sub != event) &&
+                   (sub->state == PERF_EVENT_STATE_ACTIVE))
                        sub->pmu->read(sub);
 
                values[n++] = perf_event_count(sub);
@@ -4462,20 +4496,6 @@ void perf_output_sample(struct perf_output_handle *handle,
                }
        }
 
-       if (!event->attr.watermark) {
-               int wakeup_events = event->attr.wakeup_events;
-
-               if (wakeup_events) {
-                       struct ring_buffer *rb = handle->rb;
-                       int events = local_inc_return(&rb->events);
-
-                       if (events >= wakeup_events) {
-                               local_sub(wakeup_events, &rb->events);
-                               local_inc(&rb->wakeup);
-                       }
-               }
-       }
-
        if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
                if (data->br_stack) {
                        size_t size;
@@ -4511,16 +4531,31 @@ void perf_output_sample(struct perf_output_handle *handle,
                }
        }
 
-       if (sample_type & PERF_SAMPLE_STACK_USER)
+       if (sample_type & PERF_SAMPLE_STACK_USER) {
                perf_output_sample_ustack(handle,
                                          data->stack_user_size,
                                          data->regs_user.regs);
+       }
 
        if (sample_type & PERF_SAMPLE_WEIGHT)
                perf_output_put(handle, data->weight);
 
        if (sample_type & PERF_SAMPLE_DATA_SRC)
                perf_output_put(handle, data->data_src.val);
+
+       if (!event->attr.watermark) {
+               int wakeup_events = event->attr.wakeup_events;
+
+               if (wakeup_events) {
+                       struct ring_buffer *rb = handle->rb;
+                       int events = local_inc_return(&rb->events);
+
+                       if (events >= wakeup_events) {
+                               local_sub(wakeup_events, &rb->events);
+                               local_inc(&rb->wakeup);
+                       }
+               }
+       }
 }
 
 void perf_prepare_sample(struct perf_event_header *header,
@@ -4680,12 +4715,10 @@ perf_event_read_event(struct perf_event *event,
        perf_output_end(&handle);
 }
 
-typedef int  (perf_event_aux_match_cb)(struct perf_event *event, void *data);
 typedef void (perf_event_aux_output_cb)(struct perf_event *event, void *data);
 
 static void
 perf_event_aux_ctx(struct perf_event_context *ctx,
-                  perf_event_aux_match_cb match,
                   perf_event_aux_output_cb output,
                   void *data)
 {
@@ -4696,15 +4729,12 @@ perf_event_aux_ctx(struct perf_event_context *ctx,
                        continue;
                if (!event_filter_match(event))
                        continue;
-               if (match(event, data))
-                       output(event, data);
+               output(event, data);
        }
 }
 
 static void
-perf_event_aux(perf_event_aux_match_cb match,
-              perf_event_aux_output_cb output,
-              void *data,
+perf_event_aux(perf_event_aux_output_cb output, void *data,
               struct perf_event_context *task_ctx)
 {
        struct perf_cpu_context *cpuctx;
@@ -4717,7 +4747,7 @@ perf_event_aux(perf_event_aux_match_cb match,
                cpuctx = get_cpu_ptr(pmu->pmu_cpu_context);
                if (cpuctx->unique_pmu != pmu)
                        goto next;
-               perf_event_aux_ctx(&cpuctx->ctx, match, output, data);
+               perf_event_aux_ctx(&cpuctx->ctx, output, data);
                if (task_ctx)
                        goto next;
                ctxn = pmu->task_ctx_nr;
@@ -4725,14 +4755,14 @@ perf_event_aux(perf_event_aux_match_cb match,
                        goto next;
                ctx = rcu_dereference(current->perf_event_ctxp[ctxn]);
                if (ctx)
-                       perf_event_aux_ctx(ctx, match, output, data);
+                       perf_event_aux_ctx(ctx, output, data);
 next:
                put_cpu_ptr(pmu->pmu_cpu_context);
        }
 
        if (task_ctx) {
                preempt_disable();
-               perf_event_aux_ctx(task_ctx, match, output, data);
+               perf_event_aux_ctx(task_ctx, output, data);
                preempt_enable();
        }
        rcu_read_unlock();
@@ -4759,6 +4789,12 @@ struct perf_task_event {
        } event_id;
 };
 
+static int perf_event_task_match(struct perf_event *event)
+{
+       return event->attr.comm || event->attr.mmap ||
+              event->attr.mmap_data || event->attr.task;
+}
+
 static void perf_event_task_output(struct perf_event *event,
                                   void *data)
 {
@@ -4768,6 +4804,9 @@ static void perf_event_task_output(struct perf_event *event,
        struct task_struct *task = task_event->task;
        int ret, size = task_event->event_id.header.size;
 
+       if (!perf_event_task_match(event))
+               return;
+
        perf_event_header__init_id(&task_event->event_id.header, &sample, event);
 
        ret = perf_output_begin(&handle, event,
@@ -4790,13 +4829,6 @@ out:
        task_event->event_id.header.size = size;
 }
 
-static int perf_event_task_match(struct perf_event *event,
-                                void *data __maybe_unused)
-{
-       return event->attr.comm || event->attr.mmap ||
-              event->attr.mmap_data || event->attr.task;
-}
-
 static void perf_event_task(struct task_struct *task,
                              struct perf_event_context *task_ctx,
                              int new)
@@ -4825,8 +4857,7 @@ static void perf_event_task(struct task_struct *task,
                },
        };
 
-       perf_event_aux(perf_event_task_match,
-                      perf_event_task_output,
+       perf_event_aux(perf_event_task_output,
                       &task_event,
                       task_ctx);
 }
@@ -4853,6 +4884,11 @@ struct perf_comm_event {
        } event_id;
 };
 
+static int perf_event_comm_match(struct perf_event *event)
+{
+       return event->attr.comm;
+}
+
 static void perf_event_comm_output(struct perf_event *event,
                                   void *data)
 {
@@ -4862,6 +4898,9 @@ static void perf_event_comm_output(struct perf_event *event,
        int size = comm_event->event_id.header.size;
        int ret;
 
+       if (!perf_event_comm_match(event))
+               return;
+
        perf_event_header__init_id(&comm_event->event_id.header, &sample, event);
        ret = perf_output_begin(&handle, event,
                                comm_event->event_id.header.size);
@@ -4883,12 +4922,6 @@ out:
        comm_event->event_id.header.size = size;
 }
 
-static int perf_event_comm_match(struct perf_event *event,
-                                void *data __maybe_unused)
-{
-       return event->attr.comm;
-}
-
 static void perf_event_comm_event(struct perf_comm_event *comm_event)
 {
        char comm[TASK_COMM_LEN];
@@ -4903,8 +4936,7 @@ static void perf_event_comm_event(struct perf_comm_event *comm_event)
 
        comm_event->event_id.header.size = sizeof(comm_event->event_id) + size;
 
-       perf_event_aux(perf_event_comm_match,
-                      perf_event_comm_output,
+       perf_event_aux(perf_event_comm_output,
                       comm_event,
                       NULL);
 }
@@ -4967,6 +4999,17 @@ struct perf_mmap_event {
        } event_id;
 };
 
+static int perf_event_mmap_match(struct perf_event *event,
+                                void *data)
+{
+       struct perf_mmap_event *mmap_event = data;
+       struct vm_area_struct *vma = mmap_event->vma;
+       int executable = vma->vm_flags & VM_EXEC;
+
+       return (!executable && event->attr.mmap_data) ||
+              (executable && event->attr.mmap);
+}
+
 static void perf_event_mmap_output(struct perf_event *event,
                                   void *data)
 {
@@ -4976,6 +5019,9 @@ static void perf_event_mmap_output(struct perf_event *event,
        int size = mmap_event->event_id.header.size;
        int ret;
 
+       if (!perf_event_mmap_match(event, data))
+               return;
+
        perf_event_header__init_id(&mmap_event->event_id.header, &sample, event);
        ret = perf_output_begin(&handle, event,
                                mmap_event->event_id.header.size);
@@ -4996,17 +5042,6 @@ out:
        mmap_event->event_id.header.size = size;
 }
 
-static int perf_event_mmap_match(struct perf_event *event,
-                                void *data)
-{
-       struct perf_mmap_event *mmap_event = data;
-       struct vm_area_struct *vma = mmap_event->vma;
-       int executable = vma->vm_flags & VM_EXEC;
-
-       return (!executable && event->attr.mmap_data) ||
-              (executable && event->attr.mmap);
-}
-
 static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
 {
        struct vm_area_struct *vma = mmap_event->vma;
@@ -5070,8 +5105,7 @@ got_name:
 
        mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size;
 
-       perf_event_aux(perf_event_mmap_match,
-                      perf_event_mmap_output,
+       perf_event_aux(perf_event_mmap_output,
                       mmap_event,
                       NULL);
 
@@ -5178,6 +5212,7 @@ static int __perf_event_overflow(struct perf_event *event,
                        __this_cpu_inc(perf_throttled_count);
                        hwc->interrupts = MAX_INTERRUPTS;
                        perf_log_throttle(event, 0);
+                       tick_nohz_full_kick();
                        ret = 1;
                }
        }
@@ -6443,6 +6478,43 @@ unlock:
        return pmu;
 }
 
+static void account_event_cpu(struct perf_event *event, int cpu)
+{
+       if (event->parent)
+               return;
+
+       if (has_branch_stack(event)) {
+               if (!(event->attach_state & PERF_ATTACH_TASK))
+                       atomic_inc(&per_cpu(perf_branch_stack_events, cpu));
+       }
+       if (is_cgroup_event(event))
+               atomic_inc(&per_cpu(perf_cgroup_events, cpu));
+
+       if (event->attr.freq)
+               atomic_inc(&per_cpu(perf_freq_events, cpu));
+}
+
+static void account_event(struct perf_event *event)
+{
+       if (event->parent)
+               return;
+
+       if (event->attach_state & PERF_ATTACH_TASK)
+               static_key_slow_inc(&perf_sched_events.key);
+       if (event->attr.mmap || event->attr.mmap_data)
+               atomic_inc(&nr_mmap_events);
+       if (event->attr.comm)
+               atomic_inc(&nr_comm_events);
+       if (event->attr.task)
+               atomic_inc(&nr_task_events);
+       if (has_branch_stack(event))
+               static_key_slow_inc(&perf_sched_events.key);
+       if (is_cgroup_event(event))
+               static_key_slow_inc(&perf_sched_events.key);
+
+       account_event_cpu(event, event->cpu);
+}
+
 /*
  * Allocate and initialize a event structure
  */
@@ -6457,7 +6529,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
        struct pmu *pmu;
        struct perf_event *event;
        struct hw_perf_event *hwc;
-       long err;
+       long err = -EINVAL;
 
        if ((unsigned)cpu >= nr_cpu_ids) {
                if (!task || cpu != -1)
@@ -6540,49 +6612,35 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
         * we currently do not support PERF_FORMAT_GROUP on inherited events
         */
        if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP))
-               goto done;
+               goto err_ns;
 
        pmu = perf_init_event(event);
-
-done:
-       err = 0;
        if (!pmu)
-               err = -EINVAL;
-       else if (IS_ERR(pmu))
+               goto err_ns;
+       else if (IS_ERR(pmu)) {
                err = PTR_ERR(pmu);
-
-       if (err) {
-               if (event->ns)
-                       put_pid_ns(event->ns);
-               kfree(event);
-               return ERR_PTR(err);
+               goto err_ns;
        }
 
        if (!event->parent) {
-               if (event->attach_state & PERF_ATTACH_TASK)
-                       static_key_slow_inc(&perf_sched_events.key);
-               if (event->attr.mmap || event->attr.mmap_data)
-                       atomic_inc(&nr_mmap_events);
-               if (event->attr.comm)
-                       atomic_inc(&nr_comm_events);
-               if (event->attr.task)
-                       atomic_inc(&nr_task_events);
                if (event->attr.sample_type & PERF_SAMPLE_CALLCHAIN) {
                        err = get_callchain_buffers();
-                       if (err) {
-                               free_event(event);
-                               return ERR_PTR(err);
-                       }
-               }
-               if (has_branch_stack(event)) {
-                       static_key_slow_inc(&perf_sched_events.key);
-                       if (!(event->attach_state & PERF_ATTACH_TASK))
-                               atomic_inc(&per_cpu(perf_branch_stack_events,
-                                                   event->cpu));
+                       if (err)
+                               goto err_pmu;
                }
        }
 
        return event;
+
+err_pmu:
+       if (event->destroy)
+               event->destroy(event);
+err_ns:
+       if (event->ns)
+               put_pid_ns(event->ns);
+       kfree(event);
+
+       return ERR_PTR(err);
 }
 
 static int perf_copy_attr(struct perf_event_attr __user *uattr,
@@ -6864,17 +6922,14 @@ SYSCALL_DEFINE5(perf_event_open,
 
        if (flags & PERF_FLAG_PID_CGROUP) {
                err = perf_cgroup_connect(pid, event, &attr, group_leader);
-               if (err)
-                       goto err_alloc;
-               /*
-                * one more event:
-                * - that has cgroup constraint on event->cpu
-                * - that may need work on context switch
-                */
-               atomic_inc(&per_cpu(perf_cgroup_events, event->cpu));
-               static_key_slow_inc(&perf_sched_events.key);
+               if (err) {
+                       __free_event(event);
+                       goto err_task;
+               }
        }
 
+       account_event(event);
+
        /*
         * Special case software events and allow them to be part of
         * any hardware group.
@@ -7070,6 +7125,8 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
                goto err;
        }
 
+       account_event(event);
+
        ctx = find_get_context(event->pmu, task, cpu);
        if (IS_ERR(ctx)) {
                err = PTR_ERR(ctx);
@@ -7106,6 +7163,7 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu)
        list_for_each_entry_safe(event, tmp, &src_ctx->event_list,
                                 event_entry) {
                perf_remove_from_context(event);
+               unaccount_event_cpu(event, src_cpu);
                put_ctx(src_ctx);
                list_add(&event->event_entry, &events);
        }
@@ -7118,6 +7176,7 @@ void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu)
                list_del(&event->event_entry);
                if (event->state >= PERF_EVENT_STATE_OFF)
                        event->state = PERF_EVENT_STATE_INACTIVE;
+               account_event_cpu(event, dst_cpu);
                perf_install_in_context(dst_ctx, event, dst_cpu);
                get_ctx(dst_ctx);
        }
index b7c32cb7bfebbd5912f06a421decb87b15c34bf9..6df0fbe53767bcce281f6e6bd0764fc41fc1abe1 100644 (file)
@@ -5083,18 +5083,23 @@ static void destroy_sched_domains(struct sched_domain *sd, int cpu)
  * two cpus are in the same cache domain, see cpus_share_cache().
  */
 DEFINE_PER_CPU(struct sched_domain *, sd_llc);
+DEFINE_PER_CPU(int, sd_llc_size);
 DEFINE_PER_CPU(int, sd_llc_id);
 
 static void update_top_cache_domain(int cpu)
 {
        struct sched_domain *sd;
        int id = cpu;
+       int size = 1;
 
        sd = highest_flag_domain(cpu, SD_SHARE_PKG_RESOURCES);
-       if (sd)
+       if (sd) {
                id = cpumask_first(sched_domain_span(sd));
+               size = cpumask_weight(sched_domain_span(sd));
+       }
 
        rcu_assign_pointer(per_cpu(sd_llc, cpu), sd);
+       per_cpu(sd_llc_size, cpu) = size;
        per_cpu(sd_llc_id, cpu) = id;
 }
 
index 9565645e320218f394f36eb0edbaa95236bbfcd1..10d729b02696bba6cdf65126514a222f1d905f72 100644 (file)
@@ -3017,6 +3017,23 @@ static unsigned long cpu_avg_load_per_task(int cpu)
        return 0;
 }
 
+static void record_wakee(struct task_struct *p)
+{
+       /*
+        * Rough decay (wiping) for cost saving, don't worry
+        * about the boundary, really active task won't care
+        * about the loss.
+        */
+       if (jiffies > current->wakee_flip_decay_ts + HZ) {
+               current->wakee_flips = 0;
+               current->wakee_flip_decay_ts = jiffies;
+       }
+
+       if (current->last_wakee != p) {
+               current->last_wakee = p;
+               current->wakee_flips++;
+       }
+}
 
 static void task_waking_fair(struct task_struct *p)
 {
@@ -3037,6 +3054,7 @@ static void task_waking_fair(struct task_struct *p)
 #endif
 
        se->vruntime -= min_vruntime;
+       record_wakee(p);
 }
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
@@ -3155,6 +3173,28 @@ static inline unsigned long effective_load(struct task_group *tg, int cpu,
 
 #endif
 
+static int wake_wide(struct task_struct *p)
+{
+       int factor = this_cpu_read(sd_llc_size);
+
+       /*
+        * Yeah, it's the switching-frequency, could means many wakee or
+        * rapidly switch, use factor here will just help to automatically
+        * adjust the loose-degree, so bigger node will lead to more pull.
+        */
+       if (p->wakee_flips > factor) {
+               /*
+                * wakee is somewhat hot, it needs certain amount of cpu
+                * resource, so if waker is far more hot, prefer to leave
+                * it alone.
+                */
+               if (current->wakee_flips > (factor * p->wakee_flips))
+                       return 1;
+       }
+
+       return 0;
+}
+
 static int wake_affine(struct sched_domain *sd, struct task_struct *p, int sync)
 {
        s64 this_load, load;
@@ -3164,6 +3204,13 @@ static int wake_affine(struct sched_domain *sd, struct task_struct *p, int sync)
        unsigned long weight;
        int balanced;
 
+       /*
+        * If we wake multiple tasks be careful to not bounce
+        * ourselves around too much.
+        */
+       if (wake_wide(p))
+               return 0;
+
        idx       = sd->wake_idx;
        this_cpu  = smp_processor_id();
        prev_cpu  = task_cpu(p);
@@ -4171,47 +4218,48 @@ static void update_blocked_averages(int cpu)
 }
 
 /*
- * Compute the cpu's hierarchical load factor for each task group.
+ * Compute the hierarchical load factor for cfs_rq and all its ascendants.
  * This needs to be done in a top-down fashion because the load of a child
  * group is a fraction of its parents load.
  */
-static int tg_load_down(struct task_group *tg, void *data)
-{
-       unsigned long load;
-       long cpu = (long)data;
-
-       if (!tg->parent) {
-               load = cpu_rq(cpu)->avg.load_avg_contrib;
-       } else {
-               load = tg->parent->cfs_rq[cpu]->h_load;
-               load = div64_ul(load * tg->se[cpu]->avg.load_avg_contrib,
-                               tg->parent->cfs_rq[cpu]->runnable_load_avg + 1);
-       }
-
-       tg->cfs_rq[cpu]->h_load = load;
-
-       return 0;
-}
-
-static void update_h_load(long cpu)
+static void update_cfs_rq_h_load(struct cfs_rq *cfs_rq)
 {
-       struct rq *rq = cpu_rq(cpu);
+       struct rq *rq = rq_of(cfs_rq);
+       struct sched_entity *se = cfs_rq->tg->se[cpu_of(rq)];
        unsigned long now = jiffies;
+       unsigned long load;
 
-       if (rq->h_load_throttle == now)
+       if (cfs_rq->last_h_load_update == now)
                return;
 
-       rq->h_load_throttle = now;
+       cfs_rq->h_load_next = NULL;
+       for_each_sched_entity(se) {
+               cfs_rq = cfs_rq_of(se);
+               cfs_rq->h_load_next = se;
+               if (cfs_rq->last_h_load_update == now)
+                       break;
+       }
 
-       rcu_read_lock();
-       walk_tg_tree(tg_load_down, tg_nop, (void *)cpu);
-       rcu_read_unlock();
+       if (!se) {
+               cfs_rq->h_load = rq->avg.load_avg_contrib;
+               cfs_rq->last_h_load_update = now;
+       }
+
+       while ((se = cfs_rq->h_load_next) != NULL) {
+               load = cfs_rq->h_load;
+               load = div64_ul(load * se->avg.load_avg_contrib,
+                               cfs_rq->runnable_load_avg + 1);
+               cfs_rq = group_cfs_rq(se);
+               cfs_rq->h_load = load;
+               cfs_rq->last_h_load_update = now;
+       }
 }
 
 static unsigned long task_h_load(struct task_struct *p)
 {
        struct cfs_rq *cfs_rq = task_cfs_rq(p);
 
+       update_cfs_rq_h_load(cfs_rq);
        return div64_ul(p->se.avg.load_avg_contrib * cfs_rq->h_load,
                        cfs_rq->runnable_load_avg + 1);
 }
@@ -4220,10 +4268,6 @@ static inline void update_blocked_averages(int cpu)
 {
 }
 
-static inline void update_h_load(long cpu)
-{
-}
-
 static unsigned long task_h_load(struct task_struct *p)
 {
        return p->se.avg.load_avg_contrib;
@@ -5108,7 +5152,6 @@ redo:
                env.src_rq    = busiest;
                env.loop_max  = min(sysctl_sched_nr_migrate, busiest->nr_running);
 
-               update_h_load(env.src_cpu);
 more_balance:
                local_irq_save(flags);
                double_rq_lock(env.dst_rq, busiest);
index ef0a7b2439dde25bdd3d4ee54c4801364e09607d..4c1cb8029feb484d81cd1c5f8c9c3ecd03d21795 100644 (file)
@@ -285,7 +285,6 @@ struct cfs_rq {
        /* Required to track per-cpu representation of a task_group */
        u32 tg_runnable_contrib;
        unsigned long tg_load_contrib;
-#endif /* CONFIG_FAIR_GROUP_SCHED */
 
        /*
         *   h_load = weight * f(tg)
@@ -294,6 +293,9 @@ struct cfs_rq {
         * this group.
         */
        unsigned long h_load;
+       u64 last_h_load_update;
+       struct sched_entity *h_load_next;
+#endif /* CONFIG_FAIR_GROUP_SCHED */
 #endif /* CONFIG_SMP */
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
@@ -429,9 +431,6 @@ struct rq {
 #ifdef CONFIG_FAIR_GROUP_SCHED
        /* list of leaf cfs_rq on this cpu: */
        struct list_head leaf_cfs_rq_list;
-#ifdef CONFIG_SMP
-       unsigned long h_load_throttle;
-#endif /* CONFIG_SMP */
 #endif /* CONFIG_FAIR_GROUP_SCHED */
 
 #ifdef CONFIG_RT_GROUP_SCHED
@@ -595,6 +594,7 @@ static inline struct sched_domain *highest_flag_domain(int cpu, int flag)
 }
 
 DECLARE_PER_CPU(struct sched_domain *, sd_llc);
+DECLARE_PER_CPU(int, sd_llc_size);
 DECLARE_PER_CPU(int, sd_llc_id);
 
 struct sched_group_power {
index 1241d8c91d5e75efb3ba08ff18109a0a4f84e12c..51c4f34d258ea397266e0dd1a96a16436415a38f 100644 (file)
@@ -553,14 +553,6 @@ void __init lockup_detector_init(void)
 {
        set_sample_period();
 
-#ifdef CONFIG_NO_HZ_FULL
-       if (watchdog_user_enabled) {
-               watchdog_user_enabled = 0;
-               pr_warning("Disabled lockup detectors by default for full dynticks\n");
-               pr_warning("You can reactivate it with 'sysctl -w kernel.watchdog=1'\n");
-       }
-#endif
-
        if (watchdog_user_enabled)
                watchdog_enable_all_cpus();
 }
index 280dd820543039140f6c44a00e4518a2de1c6078..3dba0a4aebbf5b36ac0f24638eaf557d28b15170 100644 (file)
@@ -3,21 +3,6 @@ include ../../scripts/Makefile.include
 CC = $(CROSS_COMPILE)gcc
 AR = $(CROSS_COMPILE)ar
 
-# Makefiles suck: This macro sets a default value of $(2) for the
-# variable named by $(1), unless the variable has been set by
-# environment or command line. This is necessary for CC and AR
-# because make sets default values, so the simpler ?= approach
-# won't work as expected.
-define allow-override
-  $(if $(or $(findstring environment,$(origin $(1))),\
-            $(findstring command line,$(origin $(1)))),,\
-    $(eval $(1) = $(2)))
-endef
-
-# Allow setting CC and AR, or setting CROSS_COMPILE as a prefix.
-$(call allow-override,CC,$(CROSS_COMPILE)gcc)
-$(call allow-override,AR,$(CROSS_COMPILE)ar)
-
 # guard against environment variables
 LIB_H=
 LIB_OBJS=
index 0b0a90787db64b010277005c5a7980f60d8fbdbc..0794acca46a39d433cdfee0d51d4aea25fc5ffe8 100644 (file)
@@ -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
index 82b0606dcb8ab04ed08920abf9c2c193e4ee42bf..d1c2a6a4cd32b125a981dca6779abfce1b50967d 100644 (file)
@@ -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;
index 7be7e89533e4fed9979dae191cbb2aa808c45121..c37b2026d04a991b5d70db230db6a56d67320b99 100644 (file)
@@ -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;
diff --git a/tools/lib/traceevent/kbuffer-parse.c b/tools/lib/traceevent/kbuffer-parse.c
new file mode 100644 (file)
index 0000000..dcc6652
--- /dev/null
@@ -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;
+}
diff --git a/tools/lib/traceevent/kbuffer.h b/tools/lib/traceevent/kbuffer.h
new file mode 100644 (file)
index 0000000..c831f64
--- /dev/null
@@ -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 */
index a57db805136a2c205c6284d65045c2cbd5ee471d..d7f2e68bc5b91d190e2ad5cd34388b45d957372a 100644 (file)
@@ -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
index 5b3123d5721f94286a62979a3aabad8c6c7a0e7f..fdfceee0ffd0ee3d046ca6bd5beef633fd619199 100644 (file)
@@ -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
 --------
index 326f2cb333cbc0949a28030eb3c8d109d6af59b0..ac84db2d23342aaa269255e34c9ab3f5d70a3e4f 100644 (file)
@@ -13,6 +13,7 @@ SYNOPSIS
        {top|record|report|diff|buildid-list}
 'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path>
        | --guestvmlinux=<path>] {top|record|report|diff|buildid-list|stat}
+'perf kvm stat [record|report|live] [<options>]
 
 DESCRIPTION
 -----------
@@ -50,6 +51,10 @@ There are a couple of variants of perf kvm:
   'perf kvm stat report' reports statistical data which includes events
   handled time, samples, and so on.
 
+  'perf kvm stat live' reports statistical data in a live mode (similar to
+  record + report but with statistical data updated live at a given display
+  rate).
+
 OPTIONS
 -------
 -i::
@@ -85,13 +90,50 @@ STAT REPORT OPTIONS
 --vcpu=<value>::
        analyze events which occures on this vcpu. (default: all vcpus)
 
---events=<value>::
-       events to be analyzed. Possible values: vmexit, mmio, ioport.
+--event=<value>::
+       event to be analyzed. Possible values: vmexit, mmio, ioport.
        (default: vmexit)
 -k::
 --key=<value>::
        Sorting key. Possible values: sample (default, sort by samples
        number), time (sort by average time).
+-p::
+--pid=::
+    Analyze events only for given process ID(s) (comma separated list).
+
+STAT LIVE OPTIONS
+-----------------
+-d::
+--display::
+        Time in seconds between display updates
+
+-m::
+--mmap-pages=::
+    Number of mmap data pages. Must be a power of two.
+
+-a::
+--all-cpus::
+        System-wide collection from all CPUs.
+
+-p::
+--pid=::
+    Analyze events only for given process ID(s) (comma separated list).
+
+--vcpu=<value>::
+       analyze events which occures on this vcpu. (default: all vcpus)
+
+
+--event=<value>::
+       event to be analyzed. Possible values: vmexit, mmio, ioport.
+       (default: vmexit)
+
+-k::
+--key=<value>::
+       Sorting key. Possible values: sample (default, sort by samples
+       number), time (sort by average time).
+
+--duration=<value>::
+       Show events other than HLT that take longer than duration usecs.
 
 SEE ALSO
 --------
index d1e39dc8c81077071cc7e6a1976c0ef7028982e5..6fce6a622206e93fbd7f3f50871a9dd567c86804 100644 (file)
@@ -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
 -----------
@@ -29,6 +29,8 @@ counted. The following modifiers exist:
  G - guest counting (in KVM guests)
  H - host counting (not in KVM guests)
  p - precise level
+ S - read sample value (PERF_SAMPLE_READ)
+ D - pin the event to the PMU
 
 The 'p' modifier can be used for specifying how precise the instruction
 address should be. The 'p' modifier can be specified multiple times:
@@ -104,6 +106,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.
 
index 66dab7410c1d352f3583f541de5d1160cffa9085..2b8097ee39d83c194fd8393a0a1a3e2b67e40b65 100644 (file)
@@ -115,7 +115,7 @@ OPTIONS
 --dump-raw-trace::
         Dump raw trace in ASCII.
 
--g [type,min[,limit],order]::
+-g [type,min[,limit],order[,key]]::
 --call-graph::
         Display call chains using type, min percent threshold, optional print
        limit and order.
@@ -129,12 +129,21 @@ OPTIONS
        - callee: callee based call graph.
        - caller: inverted caller based call graph.
 
-       Default: fractal,0.5,callee.
+       key can be:
+       - function: compare on functions
+       - address: compare on individual code addresses
+
+       Default: fractal,0.5,callee,function.
 
 -G::
 --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
 
index 2fe87fb558f0adfc8a5ad49ad4d73ac3651a5924..73c9759005a354eb8e052e7425f981bd16fb8c2a 100644 (file)
@@ -132,6 +132,11 @@ is a useful mode to detect imbalance between physical cores.  To enable this mod
 use --per-core in addition to -a. (system-wide).  The output includes the
 core number and the number of online logical processors on that physical processor.
 
+-D msecs::
+--initial-delay msecs::
+After starting the program, wait msecs before measuring. This is useful to
+filter out the startup phase of the program, which is often very different.
+
 EXAMPLES
 --------
 
index 7fdd1909e37601c7bfeb074800433bec3e8aa4ca..58d6598a968679fb4c020f9d932443a57f9feee8 100644 (file)
@@ -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).
index 68718ccdd17820cfb38b4daab837b321874a096f..3b3552a8959ed28e9f91a5d5b95dca4817c0b135 100644 (file)
@@ -26,6 +26,10 @@ OPTIONS
 --all-cpus::
         System-wide collection from all CPUs.
 
+-e::
+--expr::
+       List of events to show, currently only syscall names.
+
 -p::
 --pid=::
        Record events on existing process ID (comma separated list).
index 641fccddb249964e30de988a056f229e8265283f..e0d3d9f9677147694bb46eae0cd62d2601a2d276 100644 (file)
@@ -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
@@ -389,6 +389,10 @@ LIB_OBJS += $(OUTPUT)tests/bp_signal.o
 LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o
 LIB_OBJS += $(OUTPUT)tests/task-exit.o
 LIB_OBJS += $(OUTPUT)tests/sw-clock.o
+ifeq ($(ARCH),x86)
+LIB_OBJS += $(OUTPUT)tests/perf-time-to-tsc.o
+endif
+LIB_OBJS += $(OUTPUT)tests/code-reading.o
 
 BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
 BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
@@ -631,10 +635,10 @@ $(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-redundant-decls $<
 
 $(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS
-       $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
+       $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-undef -Wno-switch-default $<
 
 $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS
-       $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $<
+       $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs -Wno-undef -Wno-switch-default $<
 
 $(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS
        $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $<
index 815841c04eb2f9b6db3ebeb3692711559d74e655..8801fe02f206a93a97d7f7cc8a56094dffa1d05c 100644 (file)
@@ -6,3 +6,5 @@ ifndef NO_LIBUNWIND
 LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind.o
 endif
 LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
+LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/tsc.o
+LIB_H += arch/$(ARCH)/util/tsc.h
diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c
new file mode 100644 (file)
index 0000000..9570c2b
--- /dev/null
@@ -0,0 +1,59 @@
+#include <stdbool.h>
+#include <errno.h>
+
+#include <linux/perf_event.h>
+
+#include "../../perf.h"
+#include "../../util/types.h"
+#include "../../util/debug.h"
+#include "tsc.h"
+
+u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc)
+{
+       u64 t, quot, rem;
+
+       t = ns - tc->time_zero;
+       quot = t / tc->time_mult;
+       rem  = t % tc->time_mult;
+       return (quot << tc->time_shift) +
+              (rem << tc->time_shift) / tc->time_mult;
+}
+
+u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc)
+{
+       u64 quot, rem;
+
+       quot = cyc >> tc->time_shift;
+       rem  = cyc & ((1 << tc->time_shift) - 1);
+       return tc->time_zero + quot * tc->time_mult +
+              ((rem * tc->time_mult) >> tc->time_shift);
+}
+
+int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
+                            struct perf_tsc_conversion *tc)
+{
+       bool cap_usr_time_zero;
+       u32 seq;
+       int i = 0;
+
+       while (1) {
+               seq = pc->lock;
+               rmb();
+               tc->time_mult = pc->time_mult;
+               tc->time_shift = pc->time_shift;
+               tc->time_zero = pc->time_zero;
+               cap_usr_time_zero = pc->cap_usr_time_zero;
+               rmb();
+               if (pc->lock == seq && !(seq & 1))
+                       break;
+               if (++i > 10000) {
+                       pr_debug("failed to get perf_event_mmap_page lock\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (!cap_usr_time_zero)
+               return -EOPNOTSUPP;
+
+       return 0;
+}
diff --git a/tools/perf/arch/x86/util/tsc.h b/tools/perf/arch/x86/util/tsc.h
new file mode 100644 (file)
index 0000000..a24dec8
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
+#define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
+
+#include "../../util/types.h"
+
+struct perf_tsc_conversion {
+       u16 time_shift;
+       u32 time_mult;
+       u64 time_zero;
+};
+
+struct perf_event_mmap_page;
+
+int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
+                            struct perf_tsc_conversion *tc);
+
+u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc);
+u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc);
+
+#endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */
index 25fd3f1966f193e50ee60d12802d0538f8e18693..8cdca43016b250109d8bdd3ea7568eb13229a125 100644 (file)
@@ -117,6 +117,8 @@ static void alloc_mem(void **dst, void **src, size_t length)
        *src = zalloc(length);
        if (!*src)
                die("memory allocation failed - maybe length is too large?\n");
+       /* Make sure to always replace the zero pages even if MMAP_THRESH is crossed */
+       memset(*src, 0, length);
 }
 
 static u64 do_memcpy_cycle(memcpy_t fn, size_t len, bool prefault)
index db491e9a812b1b8cf7134f51fedd27c206e4f8fc..f988d380c52f4c87b7f30cf042608a85c025c845 100644 (file)
@@ -90,8 +90,7 @@ static int process_sample_event(struct perf_tool *tool,
        struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool);
        struct addr_location al;
 
-       if (perf_event__preprocess_sample(event, machine, &al, sample,
-                                         symbol__annotate_init) < 0) {
+       if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
                pr_warning("problem processing %d event, skipping it.\n",
                           event->header.type);
                return -1;
@@ -195,6 +194,8 @@ static int __cmd_annotate(struct perf_annotate *ann)
        if (session == NULL)
                return -ENOMEM;
 
+       machines__set_symbol_filter(&session->machines, symbol__annotate_init);
+
        if (ann->cpu_list) {
                ret = perf_session__cpu_bitmap(session, ann->cpu_list,
                                               ann->cpu_bitmap);
index 0aac5f3e594d87674af00d9a5ef6172d05c16baf..f28799e94f2a4bbbb1226da3cd78b908c7b1f610 100644 (file)
 #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:
@@ -247,7 +319,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
 {
        struct addr_location al;
 
-       if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) {
+       if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
                pr_warning("problem processing %d event, skipping it.\n",
                           event->header.type);
                return -1;
@@ -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 int __cmd_diff(void)
+static void data__fprintf(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;
+       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;
 
-       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;
+       list_for_each_entry(evsel_base, &evlist_base->entries, node) {
+               struct data__file *d;
+               int i;
 
-       for (i = 0; i < 2; ++i) {
-               ret = perf_session__process_events(session[i], &tool);
-               if (ret)
-                       goto out_delete;
-       }
+               data__for_each_file_new(i, d) {
+                       struct perf_evlist *evlist = d->session->evlist;
+                       struct perf_evsel *evsel;
 
-       evlist_old = older->evlist;
-       evlist_new = newer->evlist;
+                       evsel = evsel_match(evsel_base, evlist);
+                       if (!evsel)
+                               continue;
 
-       perf_evlist__collapse_resort(evlist_old);
-       perf_evlist__collapse_resort(evlist_new);
+                       d->hists = &evsel->hists;
 
-       list_for_each_entry(evsel, &evlist_new->entries, node) {
-               struct perf_evsel *evsel_old;
+                       hists__match(&evsel_base->hists, &evsel->hists);
 
-               evsel_old = evsel_match(evsel, evlist_old);
-               if (!evsel_old)
-                       continue;
+                       if (!show_baseline_only)
+                               hists__link(&evsel_base->hists,
+                                           &evsel->hists);
+               }
 
                fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
-                       perf_evsel__name(evsel));
+                       perf_evsel__name(evsel_base));
 
                first = false;
 
-               hists__process(&evsel_old->hists, &evsel->hists);
+               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);
        }
+}
 
-out_delete:
-       for (i = 0; i < 2; ++i)
-               perf_session__delete(session[i]);
+static int __cmd_diff(void)
+{
+       struct data__file *d;
+       int ret = -EINVAL, i;
+
+       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);
+       }
+
+       data_process();
+
+ out_delete:
+       data__for_each_file(i, d) {
+               if (d->session)
+                       perf_session__delete(d->session);
+
+               data__free(d);
+       }
+
+       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);
        };
+}
+
+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;
+
+       /* 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);
 
-       if (show_formula)
-               perf_hpp__column_enable(PERF_HPP__FORMULA);
+       BUG_ON(!dfmt->header);
+       return scnprintf(hpp->buf, hpp->size, dfmt->header);
+}
 
-       if (show_period) {
-               perf_hpp__column_enable(PERF_HPP__PERIOD);
-               perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE);
+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);
        }
 }
 
-int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
+static int data_init(int argc, const char **argv)
 {
-       sort_order = diff__default_sort_order;
-       argc = parse_options(argc, argv, options, diff_usage, 0);
+       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 > 2)
-                       usage_with_options(diff_usage, options);
-               if (argc == 2) {
-                       input_old = argv[0];
-                       input_new = argv[1];
-               } else
-                       input_new = argv[0];
+               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) {
-               input_old = "perf.data.host";
-               input_new = "perf.data.guest";
+               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 (symbol__init() < 0)
                return -1;
 
+       if (data_init(argc, argv) < 0)
+               return -1;
+
        ui_init();
 
        if (setup_sorting() < 0)
index 84ad6abe42586a3bcb6d04aa5696cae6bf59b043..1d8de2e4a40740bf38297ed5163ef1cc679f7c01 100644 (file)
@@ -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  = "-",
index 0259502638b47341cbd5c191391864b8f4df7dcf..b49f5c58e152df8d57806c94a91daa43ff96a5a1 100644 (file)
@@ -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;
index 24b78aecc9287bb018c26ac2668cacab20a3b789..fa2f3d79886aa9d623ac8e6bd9568b9db89a8713 100644 (file)
@@ -2,22 +2,26 @@
 #include "perf.h"
 
 #include "util/evsel.h"
+#include "util/evlist.h"
 #include "util/util.h"
 #include "util/cache.h"
 #include "util/symbol.h"
 #include "util/thread.h"
 #include "util/header.h"
 #include "util/session.h"
-
+#include "util/intlist.h"
 #include "util/parse-options.h"
 #include "util/trace-event.h"
 #include "util/debug.h"
 #include <lk/debugfs.h>
 #include "util/tool.h"
 #include "util/stat.h"
+#include "util/top.h"
 
 #include <sys/prctl.h>
+#include <sys/timerfd.h>
 
+#include <termios.h>
 #include <semaphore.h>
 #include <pthread.h>
 #include <math.h>
@@ -82,6 +86,8 @@ struct exit_reasons_table {
 
 struct perf_kvm_stat {
        struct perf_tool    tool;
+       struct perf_record_opts opts;
+       struct perf_evlist  *evlist;
        struct perf_session *session;
 
        const char *file_name;
@@ -96,10 +102,20 @@ struct perf_kvm_stat {
        struct kvm_events_ops *events_ops;
        key_cmp_fun compare;
        struct list_head kvm_events_cache[EVENTS_CACHE_SIZE];
+
        u64 total_time;
        u64 total_count;
+       u64 lost_events;
+       u64 duration;
+
+       const char *pid_str;
+       struct intlist *pid_list;
 
        struct rb_root result;
+
+       int timerfd;
+       unsigned int display_time;
+       bool live;
 };
 
 
@@ -320,6 +336,28 @@ static void init_kvm_event_record(struct perf_kvm_stat *kvm)
                INIT_LIST_HEAD(&kvm->kvm_events_cache[i]);
 }
 
+static void clear_events_cache_stats(struct list_head *kvm_events_cache)
+{
+       struct list_head *head;
+       struct kvm_event *event;
+       unsigned int i;
+       int j;
+
+       for (i = 0; i < EVENTS_CACHE_SIZE; i++) {
+               head = &kvm_events_cache[i];
+               list_for_each_entry(event, head, hash_entry) {
+                       /* reset stats for event */
+                       event->total.time = 0;
+                       init_stats(&event->total.stats);
+
+                       for (j = 0; j < event->max_vcpu; ++j) {
+                               event->vcpu[j].time = 0;
+                               init_stats(&event->vcpu[j].stats);
+                       }
+               }
+       }
+}
+
 static int kvm_events_hash_fn(u64 key)
 {
        return key & (EVENTS_CACHE_SIZE - 1);
@@ -436,7 +474,7 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
 static bool handle_end_event(struct perf_kvm_stat *kvm,
                             struct vcpu_event_record *vcpu_record,
                             struct event_key *key,
-                            u64 timestamp)
+                            struct perf_sample *sample)
 {
        struct kvm_event *event;
        u64 time_begin, time_diff;
@@ -472,9 +510,25 @@ static bool handle_end_event(struct perf_kvm_stat *kvm,
        vcpu_record->last_event = NULL;
        vcpu_record->start_time = 0;
 
-       BUG_ON(timestamp < time_begin);
+       /* seems to happen once in a while during live mode */
+       if (sample->time < time_begin) {
+               pr_debug("End time before begin time; skipping event.\n");
+               return true;
+       }
+
+       time_diff = sample->time - time_begin;
+
+       if (kvm->duration && time_diff > kvm->duration) {
+               char decode[32];
+
+               kvm->events_ops->decode_key(kvm, &event->key, decode);
+               if (strcmp(decode, "HLT")) {
+                       pr_info("%" PRIu64 " VM %d, vcpu %d: %s event took %" PRIu64 "usec\n",
+                                sample->time, sample->pid, vcpu_record->vcpu_id,
+                                decode, time_diff/1000);
+               }
+       }
 
-       time_diff = timestamp - time_begin;
        return update_kvm_event(event, vcpu, time_diff);
 }
 
@@ -521,7 +575,7 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm,
                return handle_begin_event(kvm, vcpu_record, &key, sample->time);
 
        if (kvm->events_ops->is_end_event(evsel, sample, &key))
-               return handle_end_event(kvm, vcpu_record, &key, sample->time);
+               return handle_end_event(kvm, vcpu_record, &key, sample);
 
        return true;
 }
@@ -550,6 +604,8 @@ static int compare_kvm_event_ ## func(struct kvm_event *one,                \
 GET_EVENT_KEY(time, time);
 COMPARE_EVENT_KEY(count, stats.n);
 COMPARE_EVENT_KEY(mean, stats.mean);
+GET_EVENT_KEY(max, stats.max);
+GET_EVENT_KEY(min, stats.min);
 
 #define DEF_SORT_NAME_KEY(name, compare_key)                           \
        { #name, compare_kvm_event_ ## compare_key }
@@ -639,43 +695,81 @@ static struct kvm_event *pop_from_result(struct rb_root *result)
        return container_of(node, struct kvm_event, rb);
 }
 
-static void print_vcpu_info(int vcpu)
+static void print_vcpu_info(struct perf_kvm_stat *kvm)
 {
+       int vcpu = kvm->trace_vcpu;
+
        pr_info("Analyze events for ");
 
+       if (kvm->live) {
+               if (kvm->opts.target.system_wide)
+                       pr_info("all VMs, ");
+               else if (kvm->opts.target.pid)
+                       pr_info("pid(s) %s, ", kvm->opts.target.pid);
+               else
+                       pr_info("dazed and confused on what is monitored, ");
+       }
+
        if (vcpu == -1)
                pr_info("all VCPUs:\n\n");
        else
                pr_info("VCPU %d:\n\n", vcpu);
 }
 
+static void show_timeofday(void)
+{
+       char date[64];
+       struct timeval tv;
+       struct tm ltime;
+
+       gettimeofday(&tv, NULL);
+       if (localtime_r(&tv.tv_sec, &ltime)) {
+               strftime(date, sizeof(date), "%H:%M:%S", &ltime);
+               pr_info("%s.%06ld", date, tv.tv_usec);
+       } else
+               pr_info("00:00:00.000000");
+
+       return;
+}
+
 static void print_result(struct perf_kvm_stat *kvm)
 {
        char decode[20];
        struct kvm_event *event;
        int vcpu = kvm->trace_vcpu;
 
+       if (kvm->live) {
+               puts(CONSOLE_CLEAR);
+               show_timeofday();
+       }
+
        pr_info("\n\n");
-       print_vcpu_info(vcpu);
+       print_vcpu_info(kvm);
        pr_info("%20s ", kvm->events_ops->name);
        pr_info("%10s ", "Samples");
        pr_info("%9s ", "Samples%");
 
        pr_info("%9s ", "Time%");
+       pr_info("%10s ", "Min Time");
+       pr_info("%10s ", "Max Time");
        pr_info("%16s ", "Avg time");
        pr_info("\n\n");
 
        while ((event = pop_from_result(&kvm->result))) {
-               u64 ecount, etime;
+               u64 ecount, etime, max, min;
 
                ecount = get_event_count(event, vcpu);
                etime = get_event_time(event, vcpu);
+               max = get_event_max(event, vcpu);
+               min = get_event_min(event, vcpu);
 
                kvm->events_ops->decode_key(kvm, &event->key, decode);
                pr_info("%20s ", decode);
                pr_info("%10llu ", (unsigned long long)ecount);
                pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100);
                pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100);
+               pr_info("%8" PRIu64 "us ", min / 1000);
+               pr_info("%8" PRIu64 "us ", max / 1000);
                pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3,
                        kvm_event_rel_stddev(vcpu, event));
                pr_info("\n");
@@ -683,6 +777,29 @@ static void print_result(struct perf_kvm_stat *kvm)
 
        pr_info("\nTotal Samples:%" PRIu64 ", Total events handled time:%.2fus.\n\n",
                kvm->total_count, kvm->total_time / 1e3);
+
+       if (kvm->lost_events)
+               pr_info("\nLost events: %" PRIu64 "\n\n", kvm->lost_events);
+}
+
+static int process_lost_event(struct perf_tool *tool,
+                             union perf_event *event __maybe_unused,
+                             struct perf_sample *sample __maybe_unused,
+                             struct machine *machine __maybe_unused)
+{
+       struct perf_kvm_stat *kvm = container_of(tool, struct perf_kvm_stat, tool);
+
+       kvm->lost_events++;
+       return 0;
+}
+
+static bool skip_sample(struct perf_kvm_stat *kvm,
+                       struct perf_sample *sample)
+{
+       if (kvm->pid_list && intlist__find(kvm->pid_list, sample->pid) == NULL)
+               return true;
+
+       return false;
 }
 
 static int process_sample_event(struct perf_tool *tool,
@@ -691,10 +808,14 @@ static int process_sample_event(struct perf_tool *tool,
                                struct perf_evsel *evsel,
                                struct machine *machine)
 {
-       struct thread *thread = machine__findnew_thread(machine, sample->tid);
+       struct thread *thread;
        struct perf_kvm_stat *kvm = container_of(tool, struct perf_kvm_stat,
                                                 tool);
 
+       if (skip_sample(kvm, sample))
+               return 0;
+
+       thread = machine__findnew_thread(machine, sample->tid);
        if (thread == NULL) {
                pr_debug("problem processing %d event, skipping it.\n",
                        event->header.type);
@@ -707,10 +828,20 @@ static int process_sample_event(struct perf_tool *tool,
        return 0;
 }
 
-static int get_cpu_isa(struct perf_session *session)
+static int cpu_isa_config(struct perf_kvm_stat *kvm)
 {
-       char *cpuid = session->header.env.cpuid;
-       int isa;
+       char buf[64], *cpuid;
+       int err, isa;
+
+       if (kvm->live) {
+               err = get_cpuid(buf, sizeof(buf));
+               if (err != 0) {
+                       pr_err("Failed to look up CPU type (Intel or AMD)\n");
+                       return err;
+               }
+               cpuid = buf;
+       } else
+               cpuid = kvm->session->header.env.cpuid;
 
        if (strstr(cpuid, "Intel"))
                isa = 1;
@@ -718,10 +849,361 @@ static int get_cpu_isa(struct perf_session *session)
                isa = 0;
        else {
                pr_err("CPU %s is not supported.\n", cpuid);
-               isa = -ENOTSUP;
+               return -ENOTSUP;
+       }
+
+       if (isa == 1) {
+               kvm->exit_reasons = vmx_exit_reasons;
+               kvm->exit_reasons_size = ARRAY_SIZE(vmx_exit_reasons);
+               kvm->exit_reasons_isa = "VMX";
+       }
+
+       return 0;
+}
+
+static bool verify_vcpu(int vcpu)
+{
+       if (vcpu != -1 && vcpu < 0) {
+               pr_err("Invalid vcpu:%d.\n", vcpu);
+               return false;
+       }
+
+       return true;
+}
+
+/* keeping the max events to a modest level to keep
+ * the processing of samples per mmap smooth.
+ */
+#define PERF_KVM__MAX_EVENTS_PER_MMAP  25
+
+static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx,
+                                  u64 *mmap_time)
+{
+       union perf_event *event;
+       struct perf_sample sample;
+       s64 n = 0;
+       int err;
+
+       *mmap_time = ULLONG_MAX;
+       while ((event = perf_evlist__mmap_read(kvm->evlist, idx)) != NULL) {
+               err = perf_evlist__parse_sample(kvm->evlist, event, &sample);
+               if (err) {
+                       pr_err("Failed to parse sample\n");
+                       return -1;
+               }
+
+               err = perf_session_queue_event(kvm->session, event, &sample, 0);
+               if (err) {
+                       pr_err("Failed to enqueue sample: %d\n", err);
+                       return -1;
+               }
+
+               /* save time stamp of our first sample for this mmap */
+               if (n == 0)
+                       *mmap_time = sample.time;
+
+               /* limit events per mmap handled all at once */
+               n++;
+               if (n == PERF_KVM__MAX_EVENTS_PER_MMAP)
+                       break;
+       }
+
+       return n;
+}
+
+static int perf_kvm__mmap_read(struct perf_kvm_stat *kvm)
+{
+       int i, err, throttled = 0;
+       s64 n, ntotal = 0;
+       u64 flush_time = ULLONG_MAX, mmap_time;
+
+       for (i = 0; i < kvm->evlist->nr_mmaps; i++) {
+               n = perf_kvm__mmap_read_idx(kvm, i, &mmap_time);
+               if (n < 0)
+                       return -1;
+
+               /* flush time is going to be the minimum of all the individual
+                * mmap times. Essentially, we flush all the samples queued up
+                * from the last pass under our minimal start time -- that leaves
+                * a very small race for samples to come in with a lower timestamp.
+                * The ioctl to return the perf_clock timestamp should close the
+                * race entirely.
+                */
+               if (mmap_time < flush_time)
+                       flush_time = mmap_time;
+
+               ntotal += n;
+               if (n == PERF_KVM__MAX_EVENTS_PER_MMAP)
+                       throttled = 1;
+       }
+
+       /* flush queue after each round in which we processed events */
+       if (ntotal) {
+               kvm->session->ordered_samples.next_flush = flush_time;
+               err = kvm->tool.finished_round(&kvm->tool, NULL, kvm->session);
+               if (err) {
+                       if (kvm->lost_events)
+                               pr_info("\nLost events: %" PRIu64 "\n\n",
+                                       kvm->lost_events);
+                       return err;
+               }
+       }
+
+       return throttled;
+}
+
+static volatile int done;
+
+static void sig_handler(int sig __maybe_unused)
+{
+       done = 1;
+}
+
+static int perf_kvm__timerfd_create(struct perf_kvm_stat *kvm)
+{
+       struct itimerspec new_value;
+       int rc = -1;
+
+       kvm->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK);
+       if (kvm->timerfd < 0) {
+               pr_err("timerfd_create failed\n");
+               goto out;
+       }
+
+       new_value.it_value.tv_sec = kvm->display_time;
+       new_value.it_value.tv_nsec = 0;
+       new_value.it_interval.tv_sec = kvm->display_time;
+       new_value.it_interval.tv_nsec = 0;
+
+       if (timerfd_settime(kvm->timerfd, 0, &new_value, NULL) != 0) {
+               pr_err("timerfd_settime failed: %d\n", errno);
+               close(kvm->timerfd);
+               goto out;
+       }
+
+       rc = 0;
+out:
+       return rc;
+}
+
+static int perf_kvm__handle_timerfd(struct perf_kvm_stat *kvm)
+{
+       uint64_t c;
+       int rc;
+
+       rc = read(kvm->timerfd, &c, sizeof(uint64_t));
+       if (rc < 0) {
+               if (errno == EAGAIN)
+                       return 0;
+
+               pr_err("Failed to read timer fd: %d\n", errno);
+               return -1;
+       }
+
+       if (rc != sizeof(uint64_t)) {
+               pr_err("Error reading timer fd - invalid size returned\n");
+               return -1;
+       }
+
+       if (c != 1)
+               pr_debug("Missed timer beats: %" PRIu64 "\n", c-1);
+
+       /* update display */
+       sort_result(kvm);
+       print_result(kvm);
+
+       /* reset counts */
+       clear_events_cache_stats(kvm->kvm_events_cache);
+       kvm->total_count = 0;
+       kvm->total_time = 0;
+       kvm->lost_events = 0;
+
+       return 0;
+}
+
+static int fd_set_nonblock(int fd)
+{
+       long arg = 0;
+
+       arg = fcntl(fd, F_GETFL);
+       if (arg < 0) {
+               pr_err("Failed to get current flags for fd %d\n", fd);
+               return -1;
+       }
+
+       if (fcntl(fd, F_SETFL, arg | O_NONBLOCK) < 0) {
+               pr_err("Failed to set non-block option on fd %d\n", fd);
+               return -1;
+       }
+
+       return 0;
+}
+
+static
+int perf_kvm__handle_stdin(struct termios *tc_now, struct termios *tc_save)
+{
+       int c;
+
+       tcsetattr(0, TCSANOW, tc_now);
+       c = getc(stdin);
+       tcsetattr(0, TCSAFLUSH, tc_save);
+
+       if (c == 'q')
+               return 1;
+
+       return 0;
+}
+
+static int kvm_events_live_report(struct perf_kvm_stat *kvm)
+{
+       struct pollfd *pollfds = NULL;
+       int nr_fds, nr_stdin, ret, err = -EINVAL;
+       struct termios tc, save;
+
+       /* live flag must be set first */
+       kvm->live = true;
+
+       ret = cpu_isa_config(kvm);
+       if (ret < 0)
+               return ret;
+
+       if (!verify_vcpu(kvm->trace_vcpu) ||
+           !select_key(kvm) ||
+           !register_kvm_events_ops(kvm)) {
+               goto out;
+       }
+
+       init_kvm_event_record(kvm);
+
+       tcgetattr(0, &save);
+       tc = save;
+       tc.c_lflag &= ~(ICANON | ECHO);
+       tc.c_cc[VMIN] = 0;
+       tc.c_cc[VTIME] = 0;
+
+       signal(SIGINT, sig_handler);
+       signal(SIGTERM, sig_handler);
+
+       /* copy pollfds -- need to add timerfd and stdin */
+       nr_fds = kvm->evlist->nr_fds;
+       pollfds = zalloc(sizeof(struct pollfd) * (nr_fds + 2));
+       if (!pollfds) {
+               err = -ENOMEM;
+               goto out;
        }
+       memcpy(pollfds, kvm->evlist->pollfd,
+               sizeof(struct pollfd) * kvm->evlist->nr_fds);
+
+       /* add timer fd */
+       if (perf_kvm__timerfd_create(kvm) < 0) {
+               err = -1;
+               goto out;
+       }
+
+       pollfds[nr_fds].fd = kvm->timerfd;
+       pollfds[nr_fds].events = POLLIN;
+       nr_fds++;
+
+       pollfds[nr_fds].fd = fileno(stdin);
+       pollfds[nr_fds].events = POLLIN;
+       nr_stdin = nr_fds;
+       nr_fds++;
+       if (fd_set_nonblock(fileno(stdin)) != 0)
+               goto out;
+
+       /* everything is good - enable the events and process */
+       perf_evlist__enable(kvm->evlist);
+
+       while (!done) {
+               int rc;
+
+               rc = perf_kvm__mmap_read(kvm);
+               if (rc < 0)
+                       break;
+
+               err = perf_kvm__handle_timerfd(kvm);
+               if (err)
+                       goto out;
+
+               if (pollfds[nr_stdin].revents & POLLIN)
+                       done = perf_kvm__handle_stdin(&tc, &save);
+
+               if (!rc && !done)
+                       err = poll(pollfds, nr_fds, 100);
+       }
+
+       perf_evlist__disable(kvm->evlist);
+
+       if (err == 0) {
+               sort_result(kvm);
+               print_result(kvm);
+       }
+
+out:
+       if (kvm->timerfd >= 0)
+               close(kvm->timerfd);
+
+       if (pollfds)
+               free(pollfds);
 
-       return isa;
+       return err;
+}
+
+static int kvm_live_open_events(struct perf_kvm_stat *kvm)
+{
+       int err, rc = -1;
+       struct perf_evsel *pos;
+       struct perf_evlist *evlist = kvm->evlist;
+
+       perf_evlist__config(evlist, &kvm->opts);
+
+       /*
+        * Note: exclude_{guest,host} do not apply here.
+        *       This command processes KVM tracepoints from host only
+        */
+       list_for_each_entry(pos, &evlist->entries, node) {
+               struct perf_event_attr *attr = &pos->attr;
+
+               /* make sure these *are* set */
+               attr->sample_type |= PERF_SAMPLE_TID;
+               attr->sample_type |= PERF_SAMPLE_TIME;
+               attr->sample_type |= PERF_SAMPLE_CPU;
+               attr->sample_type |= PERF_SAMPLE_RAW;
+               /* make sure these are *not*; want as small a sample as possible */
+               attr->sample_type &= ~PERF_SAMPLE_PERIOD;
+               attr->sample_type &= ~PERF_SAMPLE_IP;
+               attr->sample_type &= ~PERF_SAMPLE_CALLCHAIN;
+               attr->sample_type &= ~PERF_SAMPLE_ADDR;
+               attr->sample_type &= ~PERF_SAMPLE_READ;
+               attr->mmap = 0;
+               attr->comm = 0;
+               attr->task = 0;
+
+               attr->sample_period = 1;
+
+               attr->watermark = 0;
+               attr->wakeup_events = 1000;
+
+               /* will enable all once we are ready */
+               attr->disabled = 1;
+       }
+
+       err = perf_evlist__open(evlist);
+       if (err < 0) {
+               printf("Couldn't create the events: %s\n", strerror(errno));
+               goto out;
+       }
+
+       if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages, false) < 0) {
+               ui__error("Failed to mmap the events: %s\n", strerror(errno));
+               perf_evlist__close(evlist);
+               goto out;
+       }
+
+       rc = 0;
+
+out:
+       return rc;
 }
 
 static int read_events(struct perf_kvm_stat *kvm)
@@ -749,28 +1231,24 @@ static int read_events(struct perf_kvm_stat *kvm)
         * Do not use 'isa' recorded in kvm_exit tracepoint since it is not
         * traced in the old kernel.
         */
-       ret = get_cpu_isa(kvm->session);
-
+       ret = cpu_isa_config(kvm);
        if (ret < 0)
                return ret;
 
-       if (ret == 1) {
-               kvm->exit_reasons = vmx_exit_reasons;
-               kvm->exit_reasons_size = ARRAY_SIZE(vmx_exit_reasons);
-               kvm->exit_reasons_isa = "VMX";
-       }
-
        return perf_session__process_events(kvm->session, &kvm->tool);
 }
 
-static bool verify_vcpu(int vcpu)
+static int parse_target_str(struct perf_kvm_stat *kvm)
 {
-       if (vcpu != -1 && vcpu < 0) {
-               pr_err("Invalid vcpu:%d.\n", vcpu);
-               return false;
+       if (kvm->pid_str) {
+               kvm->pid_list = intlist__new(kvm->pid_str);
+               if (kvm->pid_list == NULL) {
+                       pr_err("Error parsing process id string\n");
+                       return -EINVAL;
+               }
        }
 
-       return true;
+       return 0;
 }
 
 static int kvm_events_report_vcpu(struct perf_kvm_stat *kvm)
@@ -778,6 +1256,9 @@ static int kvm_events_report_vcpu(struct perf_kvm_stat *kvm)
        int ret = -EINVAL;
        int vcpu = kvm->trace_vcpu;
 
+       if (parse_target_str(kvm) != 0)
+               goto exit;
+
        if (!verify_vcpu(vcpu))
                goto exit;
 
@@ -801,16 +1282,11 @@ exit:
        return ret;
 }
 
-static const char * const record_args[] = {
-       "record",
-       "-R",
-       "-f",
-       "-m", "1024",
-       "-c", "1",
-       "-e", "kvm:kvm_entry",
-       "-e", "kvm:kvm_exit",
-       "-e", "kvm:kvm_mmio",
-       "-e", "kvm:kvm_pio",
+static const char * const kvm_events_tp[] = {
+       "kvm:kvm_entry",
+       "kvm:kvm_exit",
+       "kvm:kvm_mmio",
+       "kvm:kvm_pio",
 };
 
 #define STRDUP_FAIL_EXIT(s)            \
@@ -826,8 +1302,16 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
 {
        unsigned int rec_argc, i, j;
        const char **rec_argv;
+       const char * const record_args[] = {
+               "record",
+               "-R",
+               "-f",
+               "-m", "1024",
+               "-c", "1",
+       };
 
-       rec_argc = ARRAY_SIZE(record_args) + argc + 2;
+       rec_argc = ARRAY_SIZE(record_args) + argc + 2 +
+                  2 * ARRAY_SIZE(kvm_events_tp);
        rec_argv = calloc(rec_argc + 1, sizeof(char *));
 
        if (rec_argv == NULL)
@@ -836,6 +1320,11 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv)
        for (i = 0; i < ARRAY_SIZE(record_args); i++)
                rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]);
 
+       for (j = 0; j < ARRAY_SIZE(kvm_events_tp); j++) {
+               rec_argv[i++] = "-e";
+               rec_argv[i++] = STRDUP_FAIL_EXIT(kvm_events_tp[j]);
+       }
+
        rec_argv[i++] = STRDUP_FAIL_EXIT("-o");
        rec_argv[i++] = STRDUP_FAIL_EXIT(kvm->file_name);
 
@@ -856,6 +1345,8 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
                OPT_STRING('k', "key", &kvm->sort_key, "sort-key",
                            "key for sorting: sample(sort by samples number)"
                            " time (sort by avg time)"),
+               OPT_STRING('p', "pid", &kvm->pid_str, "pid",
+                          "analyze events only for given process id(s)"),
                OPT_END()
        };
 
@@ -878,6 +1369,190 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv)
        return kvm_events_report_vcpu(kvm);
 }
 
+static struct perf_evlist *kvm_live_event_list(void)
+{
+       struct perf_evlist *evlist;
+       char *tp, *name, *sys;
+       unsigned int j;
+       int err = -1;
+
+       evlist = perf_evlist__new();
+       if (evlist == NULL)
+               return NULL;
+
+       for (j = 0; j < ARRAY_SIZE(kvm_events_tp); j++) {
+
+               tp = strdup(kvm_events_tp[j]);
+               if (tp == NULL)
+                       goto out;
+
+               /* split tracepoint into subsystem and name */
+               sys = tp;
+               name = strchr(tp, ':');
+               if (name == NULL) {
+                       pr_err("Error parsing %s tracepoint: subsystem delimiter not found\n",
+                               kvm_events_tp[j]);
+                       free(tp);
+                       goto out;
+               }
+               *name = '\0';
+               name++;
+
+               if (perf_evlist__add_newtp(evlist, sys, name, NULL)) {
+                       pr_err("Failed to add %s tracepoint to the list\n", kvm_events_tp[j]);
+                       free(tp);
+                       goto out;
+               }
+
+               free(tp);
+       }
+
+       err = 0;
+
+out:
+       if (err) {
+               perf_evlist__delete(evlist);
+               evlist = NULL;
+       }
+
+       return evlist;
+}
+
+static int kvm_events_live(struct perf_kvm_stat *kvm,
+                          int argc, const char **argv)
+{
+       char errbuf[BUFSIZ];
+       int err;
+
+       const struct option live_options[] = {
+               OPT_STRING('p', "pid", &kvm->opts.target.pid, "pid",
+                       "record events on existing process id"),
+               OPT_UINTEGER('m', "mmap-pages", &kvm->opts.mmap_pages,
+                       "number of mmap data pages"),
+               OPT_INCR('v', "verbose", &verbose,
+                       "be more verbose (show counter open errors, etc)"),
+               OPT_BOOLEAN('a', "all-cpus", &kvm->opts.target.system_wide,
+                       "system-wide collection from all CPUs"),
+               OPT_UINTEGER('d', "display", &kvm->display_time,
+                       "time in seconds between display updates"),
+               OPT_STRING(0, "event", &kvm->report_event, "report event",
+                       "event for reporting: vmexit, mmio, ioport"),
+               OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu,
+                       "vcpu id to report"),
+               OPT_STRING('k', "key", &kvm->sort_key, "sort-key",
+                       "key for sorting: sample(sort by samples number)"
+                       " time (sort by avg time)"),
+               OPT_U64(0, "duration", &kvm->duration,
+                   "show events other than HALT that take longer than duration usecs"),
+               OPT_END()
+       };
+       const char * const live_usage[] = {
+               "perf kvm stat live [<options>]",
+               NULL
+       };
+
+
+       /* event handling */
+       kvm->tool.sample = process_sample_event;
+       kvm->tool.comm   = perf_event__process_comm;
+       kvm->tool.exit   = perf_event__process_exit;
+       kvm->tool.fork   = perf_event__process_fork;
+       kvm->tool.lost   = process_lost_event;
+       kvm->tool.ordered_samples = true;
+       perf_tool__fill_defaults(&kvm->tool);
+
+       /* set defaults */
+       kvm->display_time = 1;
+       kvm->opts.user_interval = 1;
+       kvm->opts.mmap_pages = 512;
+       kvm->opts.target.uses_mmap = false;
+       kvm->opts.target.uid_str = NULL;
+       kvm->opts.target.uid = UINT_MAX;
+
+       symbol__init();
+       disable_buildid_cache();
+
+       use_browser = 0;
+       setup_browser(false);
+
+       if (argc) {
+               argc = parse_options(argc, argv, live_options,
+                                    live_usage, 0);
+               if (argc)
+                       usage_with_options(live_usage, live_options);
+       }
+
+       kvm->duration *= NSEC_PER_USEC;   /* convert usec to nsec */
+
+       /*
+        * target related setups
+        */
+       err = perf_target__validate(&kvm->opts.target);
+       if (err) {
+               perf_target__strerror(&kvm->opts.target, err, errbuf, BUFSIZ);
+               ui__warning("%s", errbuf);
+       }
+
+       if (perf_target__none(&kvm->opts.target))
+               kvm->opts.target.system_wide = true;
+
+
+       /*
+        * generate the event list
+        */
+       kvm->evlist = kvm_live_event_list();
+       if (kvm->evlist == NULL) {
+               err = -1;
+               goto out;
+       }
+
+       symbol_conf.nr_events = kvm->evlist->nr_entries;
+
+       if (perf_evlist__create_maps(kvm->evlist, &kvm->opts.target) < 0)
+               usage_with_options(live_usage, live_options);
+
+       /*
+        * perf session
+        */
+       kvm->session = perf_session__new(NULL, O_WRONLY, false, false, &kvm->tool);
+       if (kvm->session == NULL) {
+               err = -ENOMEM;
+               goto out;
+       }
+       kvm->session->evlist = kvm->evlist;
+       perf_session__set_id_hdr_size(kvm->session);
+
+
+       if (perf_target__has_task(&kvm->opts.target))
+               perf_event__synthesize_thread_map(&kvm->tool,
+                                                 kvm->evlist->threads,
+                                                 perf_event__process,
+                                                 &kvm->session->machines.host);
+       else
+               perf_event__synthesize_threads(&kvm->tool, perf_event__process,
+                                              &kvm->session->machines.host);
+
+
+       err = kvm_live_open_events(kvm);
+       if (err)
+               goto out;
+
+       err = kvm_events_live_report(kvm);
+
+out:
+       exit_browser(0);
+
+       if (kvm->session)
+               perf_session__delete(kvm->session);
+       kvm->session = NULL;
+       if (kvm->evlist) {
+               perf_evlist__delete_maps(kvm->evlist);
+               perf_evlist__delete(kvm->evlist);
+       }
+
+       return err;
+}
+
 static void print_kvm_stat_usage(void)
 {
        printf("Usage: perf kvm stat <command>\n\n");
@@ -885,6 +1560,7 @@ static void print_kvm_stat_usage(void)
        printf("# Available commands:\n");
        printf("\trecord: record kvm events\n");
        printf("\treport: report statistical data of kvm events\n");
+       printf("\tlive:   live reporting of statistical data of kvm events\n");
 
        printf("\nOtherwise, it is the alias of 'perf stat':\n");
 }
@@ -914,6 +1590,9 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv)
        if (!strncmp(argv[1], "rep", 3))
                return kvm_events_report(&kvm, argc - 1 , argv + 1);
 
+       if (!strncmp(argv[1], "live", 4))
+               return kvm_events_live(&kvm, argc - 1 , argv + 1);
+
 perf_stat:
        return cmd_stat(argc, argv, NULL);
 }
index 1948eceb517a6dcf18c974552f08d4ba1c515461..e79f423cc3022fff357e132d11d573a66f5ad7a6 100644 (file)
@@ -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 {
index a8ff6d264e502bb576db15bb175f1f67ffe743c4..706a1faa9559e27dc515850017a29d5c10780b43 100644 (file)
@@ -14,7 +14,6 @@ static const char     *mem_operation          = MEM_OPERATION_LOAD;
 struct perf_mem {
        struct perf_tool        tool;
        char const              *input_name;
-       symbol_filter_t         annotate_init;
        bool                    hide_unresolved;
        bool                    dump_raw;
        const char              *cpu_list;
@@ -69,8 +68,7 @@ dump_raw_samples(struct perf_tool *tool,
        struct addr_location al;
        const char *fmt;
 
-       if (perf_event__preprocess_sample(event, machine, &al, sample,
-                               mem->annotate_init) < 0) {
+       if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
                fprintf(stderr, "problem processing %d event, skipping it.\n",
                                event->header.type);
                return -1;
index ecca62e27b28f87a8a6c6d6aadf2515b1267f07b..a41ac41546c962df3e0801b230f06b4aa9730c7a 100644 (file)
@@ -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)
index 3662047cc6b1bcd881c8ca833988b2007cc66869..958a56a0e39e7f8a0ca14bbc52db3e5bdab4e0c4 100644 (file)
@@ -49,7 +49,6 @@ struct perf_report {
        bool                    mem_mode;
        struct perf_read_values show_threads_values;
        const char              *pretty_printing_style;
-       symbol_filter_t         annotate_init;
        const char              *cpu_list;
        const char              *symbol_filter_str;
        float                   min_percent;
@@ -89,7 +88,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 +179,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 +253,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;
        }
@@ -305,8 +304,7 @@ static int process_sample_event(struct perf_tool *tool,
        struct addr_location al;
        int ret;
 
-       if (perf_event__preprocess_sample(event, machine, &al, sample,
-                                         rep->annotate_init) < 0) {
+       if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
                fprintf(stderr, "problem processing %d event, skipping it.\n",
                        event->header.type);
                return -1;
@@ -497,7 +495,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 +506,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 +545,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 +570,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 +596,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;
 }
 
@@ -680,12 +665,23 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
        }
 
        /* get the call chain order */
-       if (!strcmp(tok2, "caller"))
+       if (!strncmp(tok2, "caller", strlen("caller")))
                callchain_param.order = ORDER_CALLER;
-       else if (!strcmp(tok2, "callee"))
+       else if (!strncmp(tok2, "callee", strlen("callee")))
                callchain_param.order = ORDER_CALLEE;
        else
                return -1;
+
+       /* Get the sort key */
+       tok2 = strtok(NULL, ",");
+       if (!tok2)
+               goto setup;
+       if (!strncmp(tok2, "function", strlen("function")))
+               callchain_param.key = CCKEY_FUNCTION;
+       else if (!strncmp(tok2, "address", strlen("address")))
+               callchain_param.key = CCKEY_ADDRESS;
+       else
+               return -1;
 setup:
        if (callchain_register_param(&callchain_param) < 0) {
                fprintf(stderr, "Can't register callchain params\n");
@@ -694,6 +690,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 +750,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,
@@ -780,10 +793,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
        OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
                    "Only display entries with parent-match"),
        OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
-                    "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit and callchain order. "
-                    "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt),
+                    "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). "
+                    "Default: fractal,0.5,callee,function", &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 +869,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();
        }
 
@@ -907,7 +922,8 @@ repeat:
         */
        if (use_browser == 1 && sort__has_sym) {
                symbol_conf.priv_size = sizeof(struct annotation);
-               report.annotate_init  = symbol__annotate_init;
+               machines__set_symbol_filter(&session->machines,
+                                           symbol__annotate_init);
                /*
                 * For searching by name on the "Browse map details".
                 * providing it only in verbose mode not to bloat too
@@ -931,14 +947,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) {
index fed9ae432c166d2f8d179bfb8a1fd15123112a7f..f809cc7fb7d935dc942a81d25283b290104f50b3 100644 (file)
@@ -109,8 +109,9 @@ struct trace_sched_handler {
        int (*wakeup_event)(struct perf_sched *sched, struct perf_evsel *evsel,
                            struct perf_sample *sample, struct machine *machine);
 
-       int (*fork_event)(struct perf_sched *sched, struct perf_evsel *evsel,
-                         struct perf_sample *sample);
+       /* PERF_RECORD_FORK event, not sched_process_fork tracepoint */
+       int (*fork_event)(struct perf_sched *sched, union perf_event *event,
+                         struct machine *machine);
 
        int (*migrate_task_event)(struct perf_sched *sched,
                                  struct perf_evsel *evsel,
@@ -717,22 +718,29 @@ static int replay_switch_event(struct perf_sched *sched,
        return 0;
 }
 
-static int replay_fork_event(struct perf_sched *sched, struct perf_evsel *evsel,
-                            struct perf_sample *sample)
+static int replay_fork_event(struct perf_sched *sched,
+                            union perf_event *event,
+                            struct machine *machine)
 {
-       const char *parent_comm = perf_evsel__strval(evsel, sample, "parent_comm"),
-                  *child_comm  = perf_evsel__strval(evsel, sample, "child_comm");
-       const u32 parent_pid  = perf_evsel__intval(evsel, sample, "parent_pid"),
-                 child_pid  = perf_evsel__intval(evsel, sample, "child_pid");
+       struct thread *child, *parent;
+
+       child = machine__findnew_thread(machine, event->fork.tid);
+       parent = machine__findnew_thread(machine, event->fork.ptid);
+
+       if (child == NULL || parent == NULL) {
+               pr_debug("thread does not exist on fork event: child %p, parent %p\n",
+                                child, parent);
+               return 0;
+       }
 
        if (verbose) {
-               printf("sched_fork event %p\n", evsel);
-               printf("... parent: %s/%d\n", parent_comm, parent_pid);
-               printf("...  child: %s/%d\n", child_comm, child_pid);
+               printf("fork event\n");
+               printf("... parent: %s/%d\n", parent->comm, parent->tid);
+               printf("...  child: %s/%d\n", child->comm, child->tid);
        }
 
-       register_pid(sched, parent_pid, parent_comm);
-       register_pid(sched, child_pid, child_comm);
+       register_pid(sched, parent->tid, parent->comm);
+       register_pid(sched, child->tid, child->comm);
        return 0;
 }
 
@@ -824,14 +832,6 @@ static int thread_atoms_insert(struct perf_sched *sched, struct thread *thread)
        return 0;
 }
 
-static int latency_fork_event(struct perf_sched *sched __maybe_unused,
-                             struct perf_evsel *evsel __maybe_unused,
-                             struct perf_sample *sample __maybe_unused)
-{
-       /* should insert the newcomer */
-       return 0;
-}
-
 static char sched_out_state(u64 prev_state)
 {
        const char *str = TASK_STATE_TO_CHAR_STR;
@@ -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");
        }
@@ -1379,25 +1379,20 @@ static int process_sched_runtime_event(struct perf_tool *tool,
        return 0;
 }
 
-static int process_sched_fork_event(struct perf_tool *tool,
-                                   struct perf_evsel *evsel,
-                                   struct perf_sample *sample,
-                                   struct machine *machine __maybe_unused)
+static int perf_sched__process_fork_event(struct perf_tool *tool,
+                                         union perf_event *event,
+                                         struct perf_sample *sample,
+                                         struct machine *machine)
 {
        struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
 
-       if (sched->tp_handler->fork_event)
-               return sched->tp_handler->fork_event(sched, evsel, sample);
+       /* run the fork event through the perf machineruy */
+       perf_event__process_fork(tool, event, sample, machine);
 
-       return 0;
-}
+       /* and then run additional processing needed for this command */
+       if (sched->tp_handler->fork_event)
+               return sched->tp_handler->fork_event(sched, event, machine);
 
-static int process_sched_exit_event(struct perf_tool *tool __maybe_unused,
-                                   struct perf_evsel *evsel,
-                                   struct perf_sample *sample __maybe_unused,
-                                   struct machine *machine __maybe_unused)
-{
-       pr_debug("sched_exit event %p\n", evsel);
        return 0;
 }
 
@@ -1425,15 +1420,8 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
                                                 struct perf_evsel *evsel,
                                                 struct machine *machine)
 {
-       struct thread *thread = machine__findnew_thread(machine, sample->tid);
        int err = 0;
 
-       if (thread == NULL) {
-               pr_debug("problem processing %s event, skipping it.\n",
-                        perf_evsel__name(evsel));
-               return -1;
-       }
-
        evsel->hists.stats.total_period += sample->period;
        hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 
@@ -1445,7 +1433,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
        return err;
 }
 
-static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
+static int perf_sched__read_events(struct perf_sched *sched,
                                   struct perf_session **psession)
 {
        const struct perf_evsel_str_handler handlers[] = {
@@ -1453,8 +1441,6 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
                { "sched:sched_stat_runtime", process_sched_runtime_event, },
                { "sched:sched_wakeup",       process_sched_wakeup_event, },
                { "sched:sched_wakeup_new",   process_sched_wakeup_event, },
-               { "sched:sched_process_fork", process_sched_fork_event, },
-               { "sched:sched_process_exit", process_sched_exit_event, },
                { "sched:sched_migrate_task", process_sched_migrate_task_event, },
        };
        struct perf_session *session;
@@ -1480,11 +1466,10 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
                sched->nr_lost_chunks = session->stats.nr_events[PERF_RECORD_LOST];
        }
 
-       if (destroy)
-               perf_session__delete(session);
-
        if (psession)
                *psession = session;
+       else
+               perf_session__delete(session);
 
        return 0;
 
@@ -1529,8 +1514,11 @@ static int perf_sched__lat(struct perf_sched *sched)
        struct perf_session *session;
 
        setup_pager();
-       if (perf_sched__read_events(sched, false, &session))
+
+       /* save session -- references to threads are held in work_list */
+       if (perf_sched__read_events(sched, &session))
                return -1;
+
        perf_sched__sort_lat(sched);
 
        printf("\n ---------------------------------------------------------------------------------------------------------------\n");
@@ -1565,7 +1553,7 @@ static int perf_sched__map(struct perf_sched *sched)
        sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF);
 
        setup_pager();
-       if (perf_sched__read_events(sched, true, NULL))
+       if (perf_sched__read_events(sched, NULL))
                return -1;
        print_bad_events(sched);
        return 0;
@@ -1580,7 +1568,7 @@ static int perf_sched__replay(struct perf_sched *sched)
 
        test_calibrations(sched);
 
-       if (perf_sched__read_events(sched, true, NULL))
+       if (perf_sched__read_events(sched, NULL))
                return -1;
 
        printf("nr_run_events:        %ld\n", sched->nr_run_events);
@@ -1639,7 +1627,6 @@ static int __cmd_record(int argc, const char **argv)
                "-e", "sched:sched_stat_sleep",
                "-e", "sched:sched_stat_iowait",
                "-e", "sched:sched_stat_runtime",
-               "-e", "sched:sched_process_exit",
                "-e", "sched:sched_process_fork",
                "-e", "sched:sched_wakeup",
                "-e", "sched:sched_migrate_task",
@@ -1662,28 +1649,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_sched__process_fork_event,
+               .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"),
@@ -1729,7 +1717,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
                .wakeup_event       = latency_wakeup_event,
                .switch_event       = latency_switch_event,
                .runtime_event      = latency_runtime_event,
-               .fork_event         = latency_fork_event,
                .migrate_task_event = latency_migrate_task_event,
        };
        struct trace_sched_handler map_ops  = {
index 92d4658f56fb55a93415dbccb441c552724b0402..2ad9d5b6fb3caa4c858cd768eb24cd181af7659a 100644 (file)
@@ -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);
@@ -65,6 +66,7 @@ struct output_option {
 static struct {
        bool user_set;
        bool wildcard_set;
+       unsigned int print_ip_opts;
        u64 fields;
        u64 invalid_fields;
 } output[PERF_TYPE_MAX] = {
@@ -234,6 +236,7 @@ static int perf_session__check_output_opt(struct perf_session *session)
 {
        int j;
        struct perf_evsel *evsel;
+       struct perf_event_attr *attr;
 
        for (j = 0; j < PERF_TYPE_MAX; ++j) {
                evsel = perf_session__find_first_evtype(session, j);
@@ -252,6 +255,24 @@ static int perf_session__check_output_opt(struct perf_session *session)
                if (evsel && output[j].fields &&
                        perf_evsel__check_attr(evsel, session))
                        return -1;
+
+               if (evsel == NULL)
+                       continue;
+
+               attr = &evsel->attr;
+
+               output[j].print_ip_opts = 0;
+               if (PRINT_FIELD(IP))
+                       output[j].print_ip_opts |= PRINT_IP_OPT_IP;
+
+               if (PRINT_FIELD(SYM))
+                       output[j].print_ip_opts |= PRINT_IP_OPT_SYM;
+
+               if (PRINT_FIELD(DSO))
+                       output[j].print_ip_opts |= PRINT_IP_OPT_DSO;
+
+               if (PRINT_FIELD(SYMOFFSET))
+                       output[j].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET;
        }
 
        return 0;
@@ -381,8 +402,8 @@ static void print_sample_bts(union perf_event *event,
                else
                        printf("\n");
                perf_evsel__print_ip(evsel, event, sample, machine,
-                                    PRINT_FIELD(SYM), PRINT_FIELD(DSO),
-                                    PRINT_FIELD(SYMOFFSET));
+                                    output[attr->type].print_ip_opts,
+                                    PERF_MAX_STACK_DEPTH);
        }
 
        printf(" => ");
@@ -396,10 +417,10 @@ static void print_sample_bts(union perf_event *event,
 
 static void process_event(union perf_event *event, struct perf_sample *sample,
                          struct perf_evsel *evsel, struct machine *machine,
-                         struct addr_location *al)
+                         struct thread *thread,
+                         struct addr_location *al __maybe_unused)
 {
        struct perf_event_attr *attr = &evsel->attr;
-       struct thread *thread = al->thread;
 
        if (output[attr->type].fields == 0)
                return;
@@ -422,9 +443,10 @@ static void process_event(union perf_event *event, struct perf_sample *sample,
                        printf(" ");
                else
                        printf("\n");
+
                perf_evsel__print_ip(evsel, event, sample, machine,
-                                    PRINT_FIELD(SYM), PRINT_FIELD(DSO),
-                                    PRINT_FIELD(SYMOFFSET));
+                                    output[attr->type].print_ip_opts,
+                                    PERF_MAX_STACK_DEPTH);
        }
 
        printf("\n");
@@ -498,7 +520,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
                return 0;
        }
 
-       if (perf_event__preprocess_sample(event, machine, &al, sample, 0) < 0) {
+       if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
                pr_err("problem processing %d event, skipping it.\n",
                       event->header.type);
                return -1;
@@ -510,7 +532,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
        if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
                return 0;
 
-       scripting_ops->process_event(event, sample, evsel, machine, &al);
+       scripting_ops->process_event(event, sample, evsel, machine, thread, &al);
 
        evsel->hists.stats.total_period += sample->period;
        return 0;
@@ -523,7 +545,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,
index 352fbd7ff4a1a16eda225109a9fb7e92460f6ed6..f686d5ff594e6b93c6e6f732e7a1c97aa18c4e49 100644 (file)
@@ -100,6 +100,7 @@ static const char           *pre_cmd                        = NULL;
 static const char              *post_cmd                       = NULL;
 static bool                    sync_run                        = false;
 static unsigned int            interval                        = 0;
+static unsigned int            initial_delay                   = 0;
 static bool                    forever                         = false;
 static struct timespec         ref_time;
 static struct cpu_map          *aggr_map;
@@ -254,7 +255,8 @@ static int create_perf_stat_counter(struct perf_evsel *evsel)
        if (!perf_target__has_task(&target) &&
            perf_evsel__is_group_leader(evsel)) {
                attr->disabled = 1;
-               attr->enable_on_exec = 1;
+               if (!initial_delay)
+                       attr->enable_on_exec = 1;
        }
 
        return perf_evsel__open_per_thread(evsel, evsel_list->threads);
@@ -414,6 +416,22 @@ static void print_interval(void)
                list_for_each_entry(counter, &evsel_list->entries, node)
                        print_counter_aggr(counter, prefix);
        }
+
+       fflush(output);
+}
+
+static void handle_initial_delay(void)
+{
+       struct perf_evsel *counter;
+
+       if (initial_delay) {
+               const int ncpus = cpu_map__nr(evsel_list->cpus),
+                       nthreads = thread_map__nr(evsel_list->threads);
+
+               usleep(initial_delay * 1000);
+               list_for_each_entry(counter, &evsel_list->entries, node)
+                       perf_evsel__enable(counter, ncpus, nthreads);
+       }
 }
 
 static int __run_perf_stat(int argc, const char **argv)
@@ -486,6 +504,7 @@ static int __run_perf_stat(int argc, const char **argv)
 
        if (forks) {
                perf_evlist__start_workload(evsel_list);
+               handle_initial_delay();
 
                if (interval) {
                        while (!waitpid(child_pid, &status, WNOHANG)) {
@@ -497,6 +516,7 @@ static int __run_perf_stat(int argc, const char **argv)
                if (WIFSIGNALED(status))
                        psignal(WTERMSIG(status), argv[0]);
        } else {
+               handle_initial_delay();
                while (!done) {
                        nanosleep(&ts, NULL);
                        if (interval)
@@ -1419,6 +1439,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
                     "aggregate counts per processor socket", AGGR_SOCKET),
        OPT_SET_UINT(0, "per-core", &aggr_mode,
                     "aggregate counts per physical processor core", AGGR_CORE),
+       OPT_UINTEGER('D', "delay", &initial_delay,
+                    "ms to wait before starting measurement after program start"),
        OPT_END()
        };
        const char * const stat_usage[] = {
index 4536a92b18f3196d9fa532ad193184d3d39b7af8..c2e02319347abd86c74f6ab0b44c37106c0fb238 100644 (file)
@@ -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 (!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);
-               }
+       if (sample->cpu > numcpus)
+               numcpus = sample->cpu;
+
+       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;
+}
 
-               else if (strcmp(event_str, "sched:sched_wakeup") == 0)
-                       sched_wakeup(sample->cpu, sample->time, sample->pid, te);
+static int
+process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused,
+                           struct perf_sample *sample)
+{
+       struct trace_entry *te = sample->raw_data;
 
-               else if (strcmp(event_str, "sched:sched_switch") == 0)
-                       sched_switch(sample->cpu, sample->time, te);
+       sched_switch(sample->cpu, sample->time, te);
+       return 0;
+}
 
 #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
-       }
+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;
index e06c4f8693306827df06134a765d615ce27ae1cc..e37521fc715a7313df807380376c326ab5d3e6bd 100644 (file)
@@ -40,6 +40,7 @@
 #include "util/xyarray.h"
 #include "util/sort.h"
 #include "util/intlist.h"
+#include "arch/common.h"
 
 #include "util/debug.h"
 
@@ -102,7 +103,8 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
        /*
         * We can't annotate with just /proc/kallsyms
         */
-       if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) {
+       if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
+           !dso__is_kcore(map->dso)) {
                pr_err("Can't annotate %s: No vmlinux file was found in the "
                       "path\n", sym->name);
                sleep(1);
@@ -237,8 +239,6 @@ out_unlock:
        pthread_mutex_unlock(&notes->lock);
 }
 
-static const char              CONSOLE_CLEAR[] = "\e[H\e[2J";
-
 static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel,
                                                     struct addr_location *al,
                                                     struct perf_sample *sample)
@@ -716,8 +716,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
        if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)
                top->exact_samples++;
 
-       if (perf_event__preprocess_sample(event, machine, &al, sample,
-                                         symbol_filter) < 0 ||
+       if (perf_event__preprocess_sample(event, machine, &al, sample) < 0 ||
            al.filtered)
                return;
 
@@ -772,8 +771,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 +937,14 @@ static int __cmd_top(struct perf_top *top)
        if (top->session == NULL)
                return -ENOMEM;
 
+       machines__set_symbol_filter(&top->session->machines, symbol_filter);
+
+       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"),
index ab3ed4af1466433755cb5946ed10c23928f336b7..120fdfb3d920f303de89027c8b9998b5ac8330ab 100644 (file)
@@ -1,11 +1,12 @@
+#include <traceevent/event-parse.h>
 #include "builtin.h"
 #include "util/color.h"
 #include "util/evlist.h"
 #include "util/machine.h"
 #include "util/thread.h"
 #include "util/parse-options.h"
+#include "util/strlist.h"
 #include "util/thread_map.h"
-#include "event-parse.h"
 
 #include <libaudit.h>
 #include <stdlib.h>
@@ -18,6 +19,7 @@ static struct syscall_fmt {
 } syscall_fmts[] = {
        { .name     = "access",     .errmsg = true, },
        { .name     = "arch_prctl", .errmsg = true, .alias = "prctl", },
+       { .name     = "connect",    .errmsg = true, },
        { .name     = "fstat",      .errmsg = true, .alias = "newfstat", },
        { .name     = "fstatat",    .errmsg = true, .alias = "newfstatat", },
        { .name     = "futex",      .errmsg = true, },
@@ -46,6 +48,7 @@ static struct syscall_fmt *syscall_fmt__find(const char *name)
 struct syscall {
        struct event_format *tp_format;
        const char          *name;
+       bool                filtered;
        struct syscall_fmt  *fmt;
 };
 
@@ -109,6 +112,7 @@ struct trace {
        struct perf_record_opts opts;
        struct machine          host;
        u64                     base_time;
+       struct strlist          *ev_qualifier;
        unsigned long           nr_events;
        bool                    sched;
        bool                    multiple_threads;
@@ -142,7 +146,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;
 }
@@ -225,6 +229,16 @@ static int trace__read_syscall_info(struct trace *trace, int id)
 
        sc = trace->syscalls.table + id;
        sc->name = name;
+
+       if (trace->ev_qualifier && !strlist__find(trace->ev_qualifier, name)) {
+               sc->filtered = true;
+               /*
+                * No need to do read tracepoint information since this will be
+                * filtered out.
+                */
+               return 0;
+       }
+
        sc->fmt  = syscall_fmt__find(sc->name);
 
        snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
@@ -301,11 +315,19 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
        char *msg;
        void *args;
        size_t printed = 0;
-       struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
+       struct thread *thread;
        struct syscall *sc = trace__syscall_info(trace, evsel, sample);
-       struct thread_trace *ttrace = thread__trace(thread);
+       struct thread_trace *ttrace;
+
+       if (sc == NULL)
+               return -1;
 
-       if (ttrace == NULL || sc == NULL)
+       if (sc->filtered)
+               return 0;
+
+       thread = machine__findnew_thread(&trace->host, sample->tid);
+       ttrace = thread__trace(thread);
+       if (ttrace == NULL)
                return -1;
 
        args = perf_evsel__rawptr(evsel, sample, "args");
@@ -344,11 +366,19 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
 {
        int ret;
        u64 duration = 0;
-       struct thread *thread = machine__findnew_thread(&trace->host, sample->tid);
-       struct thread_trace *ttrace = thread__trace(thread);
+       struct thread *thread;
        struct syscall *sc = trace__syscall_info(trace, evsel, sample);
+       struct thread_trace *ttrace;
+
+       if (sc == NULL)
+               return -1;
 
-       if (ttrace == NULL || sc == NULL)
+       if (sc->filtered)
+               return 0;
+
+       thread = machine__findnew_thread(&trace->host, sample->tid);
+       ttrace = thread__trace(thread);
+       if (ttrace == NULL)
                return -1;
 
        ret = perf_evsel__intval(evsel, sample, "ret");
@@ -593,7 +623,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);
        }
@@ -633,7 +663,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
                        .mmap_pages    = 1024,
                },
        };
+       const char *ev_qualifier_str = NULL;
        const struct option trace_options[] = {
+       OPT_STRING('e', "expr", &ev_qualifier_str, "expr",
+                   "list of events to trace"),
        OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
                    "trace events on existing process id"),
        OPT_STRING(0, "tid", &trace.opts.target.tid, "tid",
@@ -659,6 +692,14 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
 
        argc = parse_options(argc, argv, trace_options, trace_usage, 0);
 
+       if (ev_qualifier_str != NULL) {
+               trace.ev_qualifier = strlist__new(true, ev_qualifier_str);
+               if (trace.ev_qualifier == NULL) {
+                       puts("Not enough memory to parse event qualifier");
+                       return -ENOMEM;
+               }
+       }
+
        err = perf_target__validate(&trace.opts.target);
        if (err) {
                perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
index b5d9238cb181d893d515b941e8726f4ea5abc818..214e17e97e5c7ba5aa25545b465b8994ac666afc 100644 (file)
@@ -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
 
index 32bd102c32b6205b55ba4b78628963d39852575f..cf20187eee0a7345373dfc053c580bc1e77c4bb8 100644 (file)
 #ifndef NSEC_PER_SEC
 # define NSEC_PER_SEC                  1000000000ULL
 #endif
+#ifndef NSEC_PER_USEC
+# define NSEC_PER_USEC                 1000ULL
+#endif
 
 static inline unsigned long long rdclock(void)
 {
index b11cca584238e9a32636349b39511360fc85f5b0..2225162ee1fc169de376f04c62b6abb6df6611ea 100755 (executable)
@@ -21,7 +21,7 @@ def main():
        evsel = perf.evsel(task = 1, comm = 1, mmap = 0,
                           wakeup_events = 1, watermark = 1,
                           sample_id_all = 1,
-                          sample_type = perf.SAMPLE_PERIOD | perf.SAMPLE_TID | perf.SAMPLE_CPU | perf.SAMPLE_TID)
+                          sample_type = perf.SAMPLE_PERIOD | perf.SAMPLE_TID | perf.SAMPLE_CPU)
        evsel.open(cpus = cpus, threads = threads);
        evlist = perf.evlist(cpus, threads)
        evlist.add(evsel)
diff --git a/tools/perf/tests/attr/test-record-group-sampling b/tools/perf/tests/attr/test-record-group-sampling
new file mode 100644 (file)
index 0000000..658f5d6
--- /dev/null
@@ -0,0 +1,36 @@
+[config]
+command = record
+args    = -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1
+
+[event-1:base-record]
+fd=1
+group_fd=-1
+sample_type=343
+read_format=12
+inherit=0
+
+[event-2:base-record]
+fd=2
+group_fd=1
+
+# cache-misses
+type=0
+config=3
+
+# default | PERF_SAMPLE_READ
+sample_type=343
+
+# PERF_FORMAT_ID | PERF_FORMAT_GROUP
+read_format=12
+
+mmap=0
+comm=0
+enable_on_exec=0
+disabled=0
+
+# inherit is disabled for group sampling
+inherit=0
+
+# sampling disabled
+sample_freq=0
+sample_period=0
index 35b45f1466b52e1ac934c99ccc1af1447539ce30..f5af19244a0535543177e0151a73b772d4253838 100644 (file)
@@ -93,6 +93,16 @@ static struct test {
                .desc = "Test software clock events have valid period values",
                .func = test__sw_clock_freq,
        },
+#if defined(__x86_64__) || defined(__i386__)
+       {
+               .desc = "Test converting perf time to TSC",
+               .func = test__perf_time_to_tsc,
+       },
+#endif
+       {
+               .desc = "Test object code reading",
+               .func = test__code_reading,
+       },
        {
                .func = NULL,
        },
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c
new file mode 100644 (file)
index 0000000..df9afd9
--- /dev/null
@@ -0,0 +1,572 @@
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "parse-events.h"
+#include "evlist.h"
+#include "evsel.h"
+#include "thread_map.h"
+#include "cpumap.h"
+#include "machine.h"
+#include "event.h"
+#include "thread.h"
+
+#include "tests.h"
+
+#define BUFSZ  1024
+#define READLEN        128
+
+struct state {
+       u64 done[1024];
+       size_t done_cnt;
+};
+
+static unsigned int hex(char c)
+{
+       if (c >= '0' && c <= '9')
+               return c - '0';
+       if (c >= 'a' && c <= 'f')
+               return c - 'a' + 10;
+       return c - 'A' + 10;
+}
+
+static void read_objdump_line(const char *line, size_t line_len, void **buf,
+                             size_t *len)
+{
+       const char *p;
+       size_t i;
+
+       /* Skip to a colon */
+       p = strchr(line, ':');
+       if (!p)
+               return;
+       i = p + 1 - line;
+
+       /* Read bytes */
+       while (*len) {
+               char c1, c2;
+
+               /* Skip spaces */
+               for (; i < line_len; i++) {
+                       if (!isspace(line[i]))
+                               break;
+               }
+               /* Get 2 hex digits */
+               if (i >= line_len || !isxdigit(line[i]))
+                       break;
+               c1 = line[i++];
+               if (i >= line_len || !isxdigit(line[i]))
+                       break;
+               c2 = line[i++];
+               /* Followed by a space */
+               if (i < line_len && line[i] && !isspace(line[i]))
+                       break;
+               /* Store byte */
+               *(unsigned char *)*buf = (hex(c1) << 4) | hex(c2);
+               *buf += 1;
+               *len -= 1;
+       }
+}
+
+static int read_objdump_output(FILE *f, void **buf, size_t *len)
+{
+       char *line = NULL;
+       size_t line_len;
+       ssize_t ret;
+       int err = 0;
+
+       while (1) {
+               ret = getline(&line, &line_len, f);
+               if (feof(f))
+                       break;
+               if (ret < 0) {
+                       pr_debug("getline failed\n");
+                       err = -1;
+                       break;
+               }
+               read_objdump_line(line, ret, buf, len);
+       }
+
+       free(line);
+
+       return err;
+}
+
+static int read_via_objdump(const char *filename, u64 addr, void *buf,
+                           size_t len)
+{
+       char cmd[PATH_MAX * 2];
+       const char *fmt;
+       FILE *f;
+       int ret;
+
+       fmt = "%s -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s";
+       ret = snprintf(cmd, sizeof(cmd), fmt, "objdump", addr, addr + len,
+                      filename);
+       if (ret <= 0 || (size_t)ret >= sizeof(cmd))
+               return -1;
+
+       pr_debug("Objdump command is: %s\n", cmd);
+
+       /* Ignore objdump errors */
+       strcat(cmd, " 2>/dev/null");
+
+       f = popen(cmd, "r");
+       if (!f) {
+               pr_debug("popen failed\n");
+               return -1;
+       }
+
+       ret = read_objdump_output(f, &buf, &len);
+       if (len) {
+               pr_debug("objdump read too few bytes\n");
+               if (!ret)
+                       ret = len;
+       }
+
+       pclose(f);
+
+       return ret;
+}
+
+static int read_object_code(u64 addr, size_t len, u8 cpumode,
+                           struct thread *thread, struct machine *machine,
+                           struct state *state)
+{
+       struct addr_location al;
+       unsigned char buf1[BUFSZ];
+       unsigned char buf2[BUFSZ];
+       size_t ret_len;
+       u64 objdump_addr;
+       int ret;
+
+       pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr);
+
+       thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, addr,
+                             &al);
+       if (!al.map || !al.map->dso) {
+               pr_debug("thread__find_addr_map failed\n");
+               return -1;
+       }
+
+       pr_debug("File is: %s\n", al.map->dso->long_name);
+
+       if (al.map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
+           !dso__is_kcore(al.map->dso)) {
+               pr_debug("Unexpected kernel address - skipping\n");
+               return 0;
+       }
+
+       pr_debug("On file address is: %#"PRIx64"\n", al.addr);
+
+       if (len > BUFSZ)
+               len = BUFSZ;
+
+       /* Do not go off the map */
+       if (addr + len > al.map->end)
+               len = al.map->end - addr;
+
+       /* Read the object code using perf */
+       ret_len = dso__data_read_offset(al.map->dso, machine, al.addr, buf1,
+                                       len);
+       if (ret_len != len) {
+               pr_debug("dso__data_read_offset failed\n");
+               return -1;
+       }
+
+       /*
+        * Converting addresses for use by objdump requires more information.
+        * map__load() does that.  See map__rip_2objdump() for details.
+        */
+       if (map__load(al.map, NULL))
+               return -1;
+
+       /* objdump struggles with kcore - try each map only once */
+       if (dso__is_kcore(al.map->dso)) {
+               size_t d;
+
+               for (d = 0; d < state->done_cnt; d++) {
+                       if (state->done[d] == al.map->start) {
+                               pr_debug("kcore map tested already");
+                               pr_debug(" - skipping\n");
+                               return 0;
+                       }
+               }
+               if (state->done_cnt >= ARRAY_SIZE(state->done)) {
+                       pr_debug("Too many kcore maps - skipping\n");
+                       return 0;
+               }
+               state->done[state->done_cnt++] = al.map->start;
+       }
+
+       /* Read the object code using objdump */
+       objdump_addr = map__rip_2objdump(al.map, al.addr);
+       ret = read_via_objdump(al.map->dso->long_name, objdump_addr, buf2, len);
+       if (ret > 0) {
+               /*
+                * The kernel maps are inaccurate - assume objdump is right in
+                * that case.
+                */
+               if (cpumode == PERF_RECORD_MISC_KERNEL ||
+                   cpumode == PERF_RECORD_MISC_GUEST_KERNEL) {
+                       len -= ret;
+                       if (len) {
+                               pr_debug("Reducing len to %zu\n", len);
+                       } else if (dso__is_kcore(al.map->dso)) {
+                               /*
+                                * objdump cannot handle very large segments
+                                * that may be found in kcore.
+                                */
+                               pr_debug("objdump failed for kcore");
+                               pr_debug(" - skipping\n");
+                               return 0;
+                       } else {
+                               return -1;
+                       }
+               }
+       }
+       if (ret < 0) {
+               pr_debug("read_via_objdump failed\n");
+               return -1;
+       }
+
+       /* The results should be identical */
+       if (memcmp(buf1, buf2, len)) {
+               pr_debug("Bytes read differ from those read by objdump\n");
+               return -1;
+       }
+       pr_debug("Bytes read match those read by objdump\n");
+
+       return 0;
+}
+
+static int process_sample_event(struct machine *machine,
+                               struct perf_evlist *evlist,
+                               union perf_event *event, struct state *state)
+{
+       struct perf_sample sample;
+       struct thread *thread;
+       u8 cpumode;
+
+       if (perf_evlist__parse_sample(evlist, event, &sample)) {
+               pr_debug("perf_evlist__parse_sample failed\n");
+               return -1;
+       }
+
+       thread = machine__findnew_thread(machine, sample.pid);
+       if (!thread) {
+               pr_debug("machine__findnew_thread failed\n");
+               return -1;
+       }
+
+       cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
+
+       return read_object_code(sample.ip, READLEN, cpumode, thread, machine,
+                               state);
+}
+
+static int process_event(struct machine *machine, struct perf_evlist *evlist,
+                        union perf_event *event, struct state *state)
+{
+       if (event->header.type == PERF_RECORD_SAMPLE)
+               return process_sample_event(machine, evlist, event, state);
+
+       if (event->header.type < PERF_RECORD_MAX)
+               return machine__process_event(machine, event);
+
+       return 0;
+}
+
+static int process_events(struct machine *machine, struct perf_evlist *evlist,
+                         struct state *state)
+{
+       union perf_event *event;
+       int i, ret;
+
+       for (i = 0; i < evlist->nr_mmaps; i++) {
+               while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+                       ret = process_event(machine, evlist, event, state);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+       return 0;
+}
+
+static int comp(const void *a, const void *b)
+{
+       return *(int *)a - *(int *)b;
+}
+
+static void do_sort_something(void)
+{
+       int buf[40960], i;
+
+       for (i = 0; i < (int)ARRAY_SIZE(buf); i++)
+               buf[i] = ARRAY_SIZE(buf) - i - 1;
+
+       qsort(buf, ARRAY_SIZE(buf), sizeof(int), comp);
+
+       for (i = 0; i < (int)ARRAY_SIZE(buf); i++) {
+               if (buf[i] != i) {
+                       pr_debug("qsort failed\n");
+                       break;
+               }
+       }
+}
+
+static void sort_something(void)
+{
+       int i;
+
+       for (i = 0; i < 10; i++)
+               do_sort_something();
+}
+
+static void syscall_something(void)
+{
+       int pipefd[2];
+       int i;
+
+       for (i = 0; i < 1000; i++) {
+               if (pipe(pipefd) < 0) {
+                       pr_debug("pipe failed\n");
+                       break;
+               }
+               close(pipefd[1]);
+               close(pipefd[0]);
+       }
+}
+
+static void fs_something(void)
+{
+       const char *test_file_name = "temp-perf-code-reading-test-file--";
+       FILE *f;
+       int i;
+
+       for (i = 0; i < 1000; i++) {
+               f = fopen(test_file_name, "w+");
+               if (f) {
+                       fclose(f);
+                       unlink(test_file_name);
+               }
+       }
+}
+
+static void do_something(void)
+{
+       fs_something();
+
+       sort_something();
+
+       syscall_something();
+}
+
+enum {
+       TEST_CODE_READING_OK,
+       TEST_CODE_READING_NO_VMLINUX,
+       TEST_CODE_READING_NO_KCORE,
+       TEST_CODE_READING_NO_ACCESS,
+       TEST_CODE_READING_NO_KERNEL_OBJ,
+};
+
+static int do_test_code_reading(bool try_kcore)
+{
+       struct machines machines;
+       struct machine *machine;
+       struct thread *thread;
+       struct perf_record_opts opts = {
+               .mmap_pages          = UINT_MAX,
+               .user_freq           = UINT_MAX,
+               .user_interval       = ULLONG_MAX,
+               .freq                = 4000,
+               .target              = {
+                       .uses_mmap   = true,
+               },
+       };
+       struct state state = {
+               .done_cnt = 0,
+       };
+       struct thread_map *threads = NULL;
+       struct cpu_map *cpus = NULL;
+       struct perf_evlist *evlist = NULL;
+       struct perf_evsel *evsel = NULL;
+       int err = -1, ret;
+       pid_t pid;
+       struct map *map;
+       bool have_vmlinux, have_kcore, excl_kernel = false;
+
+       pid = getpid();
+
+       machines__init(&machines);
+       machine = &machines.host;
+
+       ret = machine__create_kernel_maps(machine);
+       if (ret < 0) {
+               pr_debug("machine__create_kernel_maps failed\n");
+               goto out_err;
+       }
+
+       /* Force the use of kallsyms instead of vmlinux to try kcore */
+       if (try_kcore)
+               symbol_conf.kallsyms_name = "/proc/kallsyms";
+
+       /* Load kernel map */
+       map = machine->vmlinux_maps[MAP__FUNCTION];
+       ret = map__load(map, NULL);
+       if (ret < 0) {
+               pr_debug("map__load failed\n");
+               goto out_err;
+       }
+       have_vmlinux = dso__is_vmlinux(map->dso);
+       have_kcore = dso__is_kcore(map->dso);
+
+       /* 2nd time through we just try kcore */
+       if (try_kcore && !have_kcore)
+               return TEST_CODE_READING_NO_KCORE;
+
+       /* No point getting kernel events if there is no kernel object */
+       if (!have_vmlinux && !have_kcore)
+               excl_kernel = true;
+
+       threads = thread_map__new_by_tid(pid);
+       if (!threads) {
+               pr_debug("thread_map__new_by_tid failed\n");
+               goto out_err;
+       }
+
+       ret = perf_event__synthesize_thread_map(NULL, threads,
+                                               perf_event__process, machine);
+       if (ret < 0) {
+               pr_debug("perf_event__synthesize_thread_map failed\n");
+               goto out_err;
+       }
+
+       thread = machine__findnew_thread(machine, pid);
+       if (!thread) {
+               pr_debug("machine__findnew_thread failed\n");
+               goto out_err;
+       }
+
+       cpus = cpu_map__new(NULL);
+       if (!cpus) {
+               pr_debug("cpu_map__new failed\n");
+               goto out_err;
+       }
+
+       while (1) {
+               const char *str;
+
+               evlist = perf_evlist__new();
+               if (!evlist) {
+                       pr_debug("perf_evlist__new failed\n");
+                       goto out_err;
+               }
+
+               perf_evlist__set_maps(evlist, cpus, threads);
+
+               if (excl_kernel)
+                       str = "cycles:u";
+               else
+                       str = "cycles";
+               pr_debug("Parsing event '%s'\n", str);
+               ret = parse_events(evlist, str);
+               if (ret < 0) {
+                       pr_debug("parse_events failed\n");
+                       goto out_err;
+               }
+
+               perf_evlist__config(evlist, &opts);
+
+               evsel = perf_evlist__first(evlist);
+
+               evsel->attr.comm = 1;
+               evsel->attr.disabled = 1;
+               evsel->attr.enable_on_exec = 0;
+
+               ret = perf_evlist__open(evlist);
+               if (ret < 0) {
+                       if (!excl_kernel) {
+                               excl_kernel = true;
+                               perf_evlist__delete(evlist);
+                               evlist = NULL;
+                               continue;
+                       }
+                       pr_debug("perf_evlist__open failed\n");
+                       goto out_err;
+               }
+               break;
+       }
+
+       ret = perf_evlist__mmap(evlist, UINT_MAX, false);
+       if (ret < 0) {
+               pr_debug("perf_evlist__mmap failed\n");
+               goto out_err;
+       }
+
+       perf_evlist__enable(evlist);
+
+       do_something();
+
+       perf_evlist__disable(evlist);
+
+       ret = process_events(machine, evlist, &state);
+       if (ret < 0)
+               goto out_err;
+
+       if (!have_vmlinux && !have_kcore && !try_kcore)
+               err = TEST_CODE_READING_NO_KERNEL_OBJ;
+       else if (!have_vmlinux && !try_kcore)
+               err = TEST_CODE_READING_NO_VMLINUX;
+       else if (excl_kernel)
+               err = TEST_CODE_READING_NO_ACCESS;
+       else
+               err = TEST_CODE_READING_OK;
+out_err:
+       if (evlist) {
+               perf_evlist__munmap(evlist);
+               perf_evlist__close(evlist);
+               perf_evlist__delete(evlist);
+       }
+       if (cpus)
+               cpu_map__delete(cpus);
+       if (threads)
+               thread_map__delete(threads);
+       machines__destroy_kernel_maps(&machines);
+       machine__delete_threads(machine);
+       machines__exit(&machines);
+
+       return err;
+}
+
+int test__code_reading(void)
+{
+       int ret;
+
+       ret = do_test_code_reading(false);
+       if (!ret)
+               ret = do_test_code_reading(true);
+
+       switch (ret) {
+       case TEST_CODE_READING_OK:
+               return 0;
+       case TEST_CODE_READING_NO_VMLINUX:
+               fprintf(stderr, " (no vmlinux)");
+               return 0;
+       case TEST_CODE_READING_NO_KCORE:
+               fprintf(stderr, " (no kcore)");
+               return 0;
+       case TEST_CODE_READING_NO_ACCESS:
+               fprintf(stderr, " (no access)");
+               return 0;
+       case TEST_CODE_READING_NO_KERNEL_OBJ:
+               fprintf(stderr, " (no kernel obj)");
+               return 0;
+       default:
+               return -1;
+       };
+}
index 5eaffa2de9c5816399a2b455f25857fec46a9613..dffe0551acaa717add54e96bfb864c8fbf6cf2d6 100644 (file)
 #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";
index a5d2fcc5ae35a0c81098bc6798d88ec01c85c18c..9b98c1554833ede7de9bdc8d7d312352ea863486 100644 (file)
@@ -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))
index 89085a9615e2f0878c975dc93403f74e345740f5..50bfb01183eace93ece9f9c73b7154608c506f1a 100644 (file)
@@ -220,7 +220,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
                        };
 
                        if (perf_event__preprocess_sample(&event, machine, &al,
-                                                         &sample, 0) < 0)
+                                                         &sample) < 0)
                                goto out;
 
                        he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1);
@@ -244,7 +244,7 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
                        };
 
                        if (perf_event__preprocess_sample(&event, machine, &al,
-                                                         &sample, 0) < 0)
+                                                         &sample) < 0)
                                goto out;
 
                        he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1);
index c441a28751283579652f01017aed8243e44c6246..2ca0abf1b2b6090165a4ccdb091729da4e7a1c48 100644 (file)
@@ -1,6 +1,8 @@
 PERF := .
 MK   := Makefile
 
+has = $(shell which $1 2>/dev/null)
+
 # standard single make variable specified
 make_clean_all      := clean all
 make_python_perf_so := python/perf.so
@@ -25,6 +27,13 @@ make_help           := help
 make_doc            := doc
 make_perf_o         := perf.o
 make_util_map_o     := util/map.o
+make_install        := install
+make_install_bin    := install-bin
+make_install_doc    := install-doc
+make_install_man    := install-man
+make_install_html   := install-html
+make_install_info   := install-info
+make_install_pdf    := install-pdf
 
 # all the NO_* variable combined
 make_minimal        := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1
@@ -50,14 +59,27 @@ run += make_no_backtrace
 run += make_no_libnuma
 run += make_no_libaudit
 run += make_no_libbionic
-run += make_tags
-run += make_cscope
 run += make_help
 run += make_doc
 run += make_perf_o
 run += make_util_map_o
+run += make_install
+run += make_install_bin
+# FIXME 'install-*' commented out till they're fixed
+# run += make_install_doc
+# run += make_install_man
+# run += make_install_html
+# run += make_install_info
+# run += make_install_pdf
 run += make_minimal
 
+ifneq ($(call has,ctags),)
+run += make_tags
+endif
+ifneq ($(call has,cscope),)
+run += make_cscope
+endif
+
 # $(run_O) contains same portion of $(run) tests with '_O' attached
 # to distinguish O=... tests
 run_O := $(addsuffix _O,$(run))
@@ -84,6 +106,31 @@ test_make_python_perf_so := test -f $(PERF)/python/perf.so
 test_make_perf_o     := test -f $(PERF)/perf.o
 test_make_util_map_o := test -f $(PERF)/util/map.o
 
+test_make_install       := test -x $$TMP_DEST/bin/perf
+test_make_install_O     := $(test_make_install)
+test_make_install_bin   := $(test_make_install)
+test_make_install_bin_O := $(test_make_install)
+
+# FIXME nothing gets installed
+test_make_install_man    := test -f $$TMP_DEST/share/man/man1/perf.1
+test_make_install_man_O  := $(test_make_install_man)
+
+# FIXME nothing gets installed
+test_make_install_doc    := $(test_ok)
+test_make_install_doc_O  := $(test_ok)
+
+# FIXME nothing gets installed
+test_make_install_html   := $(test_ok)
+test_make_install_html_O := $(test_ok)
+
+# FIXME nothing gets installed
+test_make_install_info   := $(test_ok)
+test_make_install_info_O := $(test_ok)
+
+# FIXME nothing gets installed
+test_make_install_pdf    := $(test_ok)
+test_make_install_pdf_O  := $(test_ok)
+
 # Kbuild tests only
 #test_make_python_perf_so_O := test -f $$TMP/tools/perf/python/perf.so
 #test_make_perf_o_O         := test -f $$TMP/tools/perf/perf.o
@@ -95,7 +142,7 @@ test_make_util_map_o_O := true
 test_default = test -x $(PERF)/perf
 test = $(if $(test_$1),$(test_$1),$(test_default))
 
-test_default_O = test -x $$TMP/perf
+test_default_O = test -x $$TMP_O/perf
 test_O = $(if $(test_$1),$(test_$1),$(test_default_O))
 
 all:
@@ -111,23 +158,27 @@ clean := @(cd $(PERF); make -s -f $(MK) clean >/dev/null)
 
 $(run):
        $(call clean)
-       @cmd="cd $(PERF) && make -f $(MK) $($@)"; \
+       @TMP_DEST=$$(mktemp -d); \
+       cmd="cd $(PERF) && make -f $(MK) DESTDIR=$$TMP_DEST $($@)"; \
        echo "- $@: $$cmd" && echo $$cmd > $@ && \
        ( eval $$cmd ) >> $@ 2>&1; \
        echo "  test: $(call test,$@)"; \
        $(call test,$@) && \
-       rm -f $@
+       rm -f $@ \
+       rm -rf $$TMP_DEST
 
 $(run_O):
        $(call clean)
-       @TMP=$$(mktemp -d); \
-       cmd="cd $(PERF) && make -f $(MK) $($(patsubst %_O,%,$@)) O=$$TMP"; \
+       @TMP_O=$$(mktemp -d); \
+       TMP_DEST=$$(mktemp -d); \
+       cmd="cd $(PERF) && make -f $(MK) O=$$TMP_O DESTDIR=$$TMP_DEST $($(patsubst %_O,%,$@))"; \
        echo "- $@: $$cmd" && echo $$cmd > $@ && \
        ( eval $$cmd ) >> $@ 2>&1 && \
        echo "  test: $(call test_O,$@)"; \
        $(call test_O,$@) && \
        rm -f $@ && \
-       rm -rf $$TMP
+       rm -rf $$TMP_O \
+       rm -rf $$TMP_DEST
 
 all: $(run) $(run_O)
        @echo OK
index 0275bab4ea9e83e95cf76d7408dc3649a5ccee4d..48114d164e9fb991898d18c28d027ba744181dad 100644 (file)
@@ -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)
 
@@ -460,6 +452,7 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist)
                        evsel->attr.exclude_kernel);
        TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+       TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
 
        return 0;
 }
@@ -528,6 +521,7 @@ static int test__group1(struct perf_evlist *evlist)
        TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* cycles:upp */
        evsel = perf_evsel__next(evsel);
@@ -543,6 +537,7 @@ static int test__group1(struct perf_evlist *evlist)
        TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        return 0;
 }
@@ -568,6 +563,7 @@ static int test__group2(struct perf_evlist *evlist)
        TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* cache-references + :u modifier */
        evsel = perf_evsel__next(evsel);
@@ -582,6 +578,7 @@ static int test__group2(struct perf_evlist *evlist)
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* cycles:k */
        evsel = perf_evsel__next(evsel);
@@ -595,6 +592,7 @@ static int test__group2(struct perf_evlist *evlist)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        return 0;
 }
@@ -623,6 +621,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
                !strcmp(leader->group_name, "group1"));
        TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* group1 cycles:kppp */
        evsel = perf_evsel__next(evsel);
@@ -639,6 +638,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
        TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* group2 cycles + G modifier */
        evsel = leader = perf_evsel__next(evsel);
@@ -656,6 +656,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
                !strcmp(leader->group_name, "group2"));
        TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* group2 1:3 + G modifier */
        evsel = perf_evsel__next(evsel);
@@ -669,6 +670,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* instructions:u */
        evsel = perf_evsel__next(evsel);
@@ -682,6 +684,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        return 0;
 }
@@ -709,6 +712,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* instructions:kp + p */
        evsel = perf_evsel__next(evsel);
@@ -724,6 +728,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        return 0;
 }
@@ -750,6 +755,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* instructions + G */
        evsel = perf_evsel__next(evsel);
@@ -764,6 +770,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
        TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* cycles:G */
        evsel = leader = perf_evsel__next(evsel);
@@ -780,6 +787,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
        TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
        TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
        TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
+       TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read);
 
        /* instructions:G */
        evsel = perf_evsel__next(evsel);
@@ -971,6 +979,142 @@ static int test__group_gh4(struct perf_evlist *evlist)
        return 0;
 }
 
+static int test__leader_sample1(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel, *leader;
+
+       TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
+
+       /* cycles - sampling group leader */
+       evsel = leader = perf_evlist__first(evlist);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config",
+                       PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
+       TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+       TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+       TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
+       TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+       TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
+
+       /* cache-misses - not sampling */
+       evsel = perf_evsel__next(evsel);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config",
+                       PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
+       TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+       TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+       TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+       TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
+
+       /* branch-misses - not sampling */
+       evsel = perf_evsel__next(evsel);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config",
+                       PERF_COUNT_HW_BRANCH_MISSES == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
+       TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+       TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+       TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
+       TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+       TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
+
+       return 0;
+}
+
+static int test__leader_sample2(struct perf_evlist *evlist __maybe_unused)
+{
+       struct perf_evsel *evsel, *leader;
+
+       TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+
+       /* instructions - sampling group leader */
+       evsel = leader = perf_evlist__first(evlist);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config",
+                       PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
+       TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+       TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+       TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
+       TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+       TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
+
+       /* branch-misses - not sampling */
+       evsel = perf_evsel__next(evsel);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config",
+                       PERF_COUNT_HW_BRANCH_MISSES == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
+       TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+       TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+       TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
+       TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+       TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read);
+
+       return 0;
+}
+
+static int test__checkevent_pinned_modifier(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel = perf_evlist__first(evlist);
+
+       TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+       TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+       TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+       TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
+       TEST_ASSERT_VAL("wrong pinned", evsel->attr.pinned);
+
+       return test__checkevent_symbolic_name(evlist);
+}
+
+static int test__pinned_group(struct perf_evlist *evlist)
+{
+       struct perf_evsel *evsel, *leader;
+
+       TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
+
+       /* cycles - group leader */
+       evsel = leader = perf_evlist__first(evlist);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config",
+                       PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
+       TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+       TEST_ASSERT_VAL("wrong pinned", evsel->attr.pinned);
+
+       /* cache-misses - can not be pinned, but will go on with the leader */
+       evsel = perf_evsel__next(evsel);
+       TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+       TEST_ASSERT_VAL("wrong config",
+                       PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
+
+       /* branch-misses - ditto */
+       evsel = perf_evsel__next(evsel);
+       TEST_ASSERT_VAL("wrong config",
+                       PERF_COUNT_HW_BRANCH_MISSES == evsel->attr.config);
+       TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
+
+       return 0;
+}
+
 static int count_tracepoints(void)
 {
        char events_path[PATH_MAX];
@@ -1187,6 +1331,22 @@ static struct evlist_test test__events[] = {
                .name  = "{cycles:G,cache-misses:H}:uG",
                .check = test__group_gh4,
        },
+       [38] = {
+               .name  = "{cycles,cache-misses,branch-misses}:S",
+               .check = test__leader_sample1,
+       },
+       [39] = {
+               .name  = "{instructions,branch-misses}:Su",
+               .check = test__leader_sample2,
+       },
+       [40] = {
+               .name  = "instructions:uDp",
+               .check = test__checkevent_pinned_modifier,
+       },
+       [41] = {
+               .name  = "{cycles,cache-misses,branch-misses}:D",
+               .check = test__pinned_group,
+       },
 };
 
 static struct evlist_test test__events_pmu[] = {
@@ -1254,24 +1414,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;
 }
diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c
new file mode 100644 (file)
index 0000000..0ab61b1
--- /dev/null
@@ -0,0 +1,177 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/prctl.h>
+
+#include "parse-events.h"
+#include "evlist.h"
+#include "evsel.h"
+#include "thread_map.h"
+#include "cpumap.h"
+#include "tests.h"
+
+#include "../arch/x86/util/tsc.h"
+
+#define CHECK__(x) {                           \
+       while ((x) < 0) {                       \
+               pr_debug(#x " failed!\n");      \
+               goto out_err;                   \
+       }                                       \
+}
+
+#define CHECK_NOT_NULL__(x) {                  \
+       while ((x) == NULL) {                   \
+               pr_debug(#x " failed!\n");      \
+               goto out_err;                   \
+       }                                       \
+}
+
+static u64 rdtsc(void)
+{
+       unsigned int low, high;
+
+       asm volatile("rdtsc" : "=a" (low), "=d" (high));
+
+       return low | ((u64)high) << 32;
+}
+
+/**
+ * test__perf_time_to_tsc - test converting perf time to TSC.
+ *
+ * This function implements a test that checks that the conversion of perf time
+ * to and from TSC is consistent with the order of events.  If the test passes
+ * %0 is returned, otherwise %-1 is returned.  If TSC conversion is not
+ * supported then then the test passes but " (not supported)" is printed.
+ */
+int test__perf_time_to_tsc(void)
+{
+       struct perf_record_opts opts = {
+               .mmap_pages          = UINT_MAX,
+               .user_freq           = UINT_MAX,
+               .user_interval       = ULLONG_MAX,
+               .freq                = 4000,
+               .target              = {
+                       .uses_mmap   = true,
+               },
+               .sample_time         = true,
+       };
+       struct thread_map *threads = NULL;
+       struct cpu_map *cpus = NULL;
+       struct perf_evlist *evlist = NULL;
+       struct perf_evsel *evsel = NULL;
+       int err = -1, ret, i;
+       const char *comm1, *comm2;
+       struct perf_tsc_conversion tc;
+       struct perf_event_mmap_page *pc;
+       union perf_event *event;
+       u64 test_tsc, comm1_tsc, comm2_tsc;
+       u64 test_time, comm1_time = 0, comm2_time = 0;
+
+       threads = thread_map__new(-1, getpid(), UINT_MAX);
+       CHECK_NOT_NULL__(threads);
+
+       cpus = cpu_map__new(NULL);
+       CHECK_NOT_NULL__(cpus);
+
+       evlist = perf_evlist__new();
+       CHECK_NOT_NULL__(evlist);
+
+       perf_evlist__set_maps(evlist, cpus, threads);
+
+       CHECK__(parse_events(evlist, "cycles:u"));
+
+       perf_evlist__config(evlist, &opts);
+
+       evsel = perf_evlist__first(evlist);
+
+       evsel->attr.comm = 1;
+       evsel->attr.disabled = 1;
+       evsel->attr.enable_on_exec = 0;
+
+       CHECK__(perf_evlist__open(evlist));
+
+       CHECK__(perf_evlist__mmap(evlist, UINT_MAX, false));
+
+       pc = evlist->mmap[0].base;
+       ret = perf_read_tsc_conversion(pc, &tc);
+       if (ret) {
+               if (ret == -EOPNOTSUPP) {
+                       fprintf(stderr, " (not supported)");
+                       return 0;
+               }
+               goto out_err;
+       }
+
+       perf_evlist__enable(evlist);
+
+       comm1 = "Test COMM 1";
+       CHECK__(prctl(PR_SET_NAME, (unsigned long)comm1, 0, 0, 0));
+
+       test_tsc = rdtsc();
+
+       comm2 = "Test COMM 2";
+       CHECK__(prctl(PR_SET_NAME, (unsigned long)comm2, 0, 0, 0));
+
+       perf_evlist__disable(evlist);
+
+       for (i = 0; i < evlist->nr_mmaps; i++) {
+               while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+                       struct perf_sample sample;
+
+                       if (event->header.type != PERF_RECORD_COMM ||
+                           (pid_t)event->comm.pid != getpid() ||
+                           (pid_t)event->comm.tid != getpid())
+                               continue;
+
+                       if (strcmp(event->comm.comm, comm1) == 0) {
+                               CHECK__(perf_evsel__parse_sample(evsel, event,
+                                                                &sample));
+                               comm1_time = sample.time;
+                       }
+                       if (strcmp(event->comm.comm, comm2) == 0) {
+                               CHECK__(perf_evsel__parse_sample(evsel, event,
+                                                                &sample));
+                               comm2_time = sample.time;
+                       }
+               }
+       }
+
+       if (!comm1_time || !comm2_time)
+               goto out_err;
+
+       test_time = tsc_to_perf_time(test_tsc, &tc);
+       comm1_tsc = perf_time_to_tsc(comm1_time, &tc);
+       comm2_tsc = perf_time_to_tsc(comm2_time, &tc);
+
+       pr_debug("1st event perf time %"PRIu64" tsc %"PRIu64"\n",
+                comm1_time, comm1_tsc);
+       pr_debug("rdtsc          time %"PRIu64" tsc %"PRIu64"\n",
+                test_time, test_tsc);
+       pr_debug("2nd event perf time %"PRIu64" tsc %"PRIu64"\n",
+                comm2_time, comm2_tsc);
+
+       if (test_time <= comm1_time ||
+           test_time >= comm2_time)
+               goto out_err;
+
+       if (test_tsc <= comm1_tsc ||
+           test_tsc >= comm2_tsc)
+               goto out_err;
+
+       err = 0;
+
+out_err:
+       if (evlist) {
+               perf_evlist__disable(evlist);
+               perf_evlist__munmap(evlist);
+               perf_evlist__close(evlist);
+               perf_evlist__delete(evlist);
+       }
+       if (cpus)
+               cpu_map__delete(cpus);
+       if (threads)
+               thread_map__delete(threads);
+
+       return err;
+}
index dd7feae2d37b83248fa8279891953503a69d3aec..c748f532b20f102970139bba03a7ff525d8f69c8 100644 (file)
@@ -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,
@@ -27,5 +35,7 @@ int test__bp_signal(void);
 int test__bp_signal_overflow(void);
 int test__task_exit(void);
 int test__sw_clock_freq(void);
+int test__perf_time_to_tsc(void);
+int test__code_reading(void);
 
 #endif /* TESTS_H */
index 7b4c4d26d1baed24e2c5999d76457616c78c5d91..2bd13edcbc1760b9d1428ece9f708ad5957e617e 100644 (file)
@@ -16,6 +16,8 @@ static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused,
        return 0;
 }
 
+#define UM(x) kallsyms_map->unmap_ip(kallsyms_map, (x))
+
 int test__vmlinux_matches_kallsyms(void)
 {
        int err = -1;
@@ -25,6 +27,7 @@ int test__vmlinux_matches_kallsyms(void)
        struct machine kallsyms, vmlinux;
        enum map_type type = MAP__FUNCTION;
        struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", };
+       u64 mem_start, mem_end;
 
        /*
         * Step 1:
@@ -73,7 +76,7 @@ int test__vmlinux_matches_kallsyms(void)
                goto out;
        }
 
-       ref_reloc_sym.addr = sym->start;
+       ref_reloc_sym.addr = UM(sym->start);
 
        /*
         * Step 5:
@@ -123,10 +126,14 @@ int test__vmlinux_matches_kallsyms(void)
                if (sym->start == sym->end)
                        continue;
 
-               first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL);
+               mem_start = vmlinux_map->unmap_ip(vmlinux_map, sym->start);
+               mem_end = vmlinux_map->unmap_ip(vmlinux_map, sym->end);
+
+               first_pair = machine__find_kernel_symbol(&kallsyms, type,
+                                                        mem_start, NULL, NULL);
                pair = first_pair;
 
-               if (pair && pair->start == sym->start) {
+               if (pair && UM(pair->start) == mem_start) {
 next_pair:
                        if (strcmp(sym->name, pair->name) == 0) {
                                /*
@@ -138,12 +145,20 @@ next_pair:
                                 * off the real size. More than that and we
                                 * _really_ have a problem.
                                 */
-                               s64 skew = sym->end - pair->end;
-                               if (llabs(skew) < page_size)
-                                       continue;
+                               s64 skew = mem_end - UM(pair->end);
+                               if (llabs(skew) >= page_size)
+                                       pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
+                                                mem_start, sym->name, mem_end,
+                                                UM(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:
@@ -152,7 +167,7 @@ detour:
                                if (nnd) {
                                        struct symbol *next = rb_entry(nnd, struct symbol, rb_node);
 
-                                       if (next->start == sym->start) {
+                                       if (UM(next->start) == mem_start) {
                                                pair = next;
                                                goto next_pair;
                                        }
@@ -165,10 +180,11 @@ detour:
                                }
 
                                pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n",
-                                        sym->start, sym->name, pair->name);
+                                        mem_start, sym->name, pair->name);
                        }
                } else
-                       pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name);
+                       pr_debug("%#" PRIx64 ": %s not on kallsyms\n",
+                                mem_start, sym->name);
 
                err = -1;
        }
@@ -201,16 +217,19 @@ detour:
        for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
                struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
 
-               pair = map_groups__find(&kallsyms.kmaps, type, pos->start);
+               mem_start = vmlinux_map->unmap_ip(vmlinux_map, pos->start);
+               mem_end = vmlinux_map->unmap_ip(vmlinux_map, pos->end);
+
+               pair = map_groups__find(&kallsyms.kmaps, type, mem_start);
                if (pair == NULL || pair->priv)
                        continue;
 
-               if (pair->start == pos->start) {
+               if (pair->start == mem_start) {
                        pair->priv = 1;
                        pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as",
                                pos->start, pos->end, pos->pgoff, pos->dso->name);
-                       if (pos->pgoff != pair->pgoff || pos->end != pair->end)
-                               pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "",
+                       if (mem_end != pair->end)
+                               pr_info(":\n*%" PRIx64 "-%" PRIx64 " %" PRIx64,
                                        pair->start, pair->end, pair->pgoff);
                        pr_info(" %s\n", pair->dso->name);
                        pair->priv = 1;
index cc64d3f7fc36e37b490f7e8501239a3ffa2cdd2d..08545ae46992cd5b284e279bf5ac131cf9c84e62 100644 (file)
@@ -428,6 +428,14 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser)
        browser->b.nr_entries = browser->nr_asm_entries;
 }
 
+#define SYM_TITLE_MAX_SIZE (PATH_MAX + 64)
+
+static int sym_title(struct symbol *sym, struct map *map, char *title,
+                    size_t sz)
+{
+       return snprintf(title, sz, "%s  %s", sym->name, map->dso->long_name);
+}
+
 static bool annotate_browser__callq(struct annotate_browser *browser,
                                    struct perf_evsel *evsel,
                                    struct hist_browser_timer *hbt)
@@ -438,6 +446,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
        struct annotation *notes;
        struct symbol *target;
        u64 ip;
+       char title[SYM_TITLE_MAX_SIZE];
 
        if (!ins__is_call(dl->ins))
                return false;
@@ -461,7 +470,8 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
 
        pthread_mutex_unlock(&notes->lock);
        symbol__tui_annotate(target, ms->map, evsel, hbt);
-       ui_browser__show_title(&browser->b, sym->name);
+       sym_title(sym, ms->map, title, sizeof(title));
+       ui_browser__show_title(&browser->b, title);
        return true;
 }
 
@@ -495,7 +505,7 @@ static bool annotate_browser__jump(struct annotate_browser *browser)
 
        dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
        if (dl == NULL) {
-               ui_helpline__puts("Invallid jump offset");
+               ui_helpline__puts("Invalid jump offset");
                return true;
        }
 
@@ -653,8 +663,10 @@ static int annotate_browser__run(struct annotate_browser *browser,
        const char *help = "Press 'h' for help on key bindings";
        int delay_secs = hbt ? hbt->refresh : 0;
        int key;
+       char title[SYM_TITLE_MAX_SIZE];
 
-       if (ui_browser__show(&browser->b, sym->name, help) < 0)
+       sym_title(sym, ms->map, title, sizeof(title));
+       if (ui_browser__show(&browser->b, title, help) < 0)
                return -1;
 
        annotate_browser__calc_percent(browser, evsel);
@@ -720,7 +732,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
                "s             Toggle source code view\n"
                "/             Search string\n"
                "r             Run available scripts\n"
-               "?             Search previous string\n");
+               "?             Search string backwards\n");
                        continue;
                case 'r':
                        {
index fc0bd3843d34a1675cb2ada755c307e3c50c2771..7ef36c360471e3f2ccef82d4bd2a4e4fb684eb14 100644 (file)
@@ -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);
index 9708dd5fb8f32f993d5e6ea3eca6ce2e3e546888..2ca66cc1160f4be05a06ae7d5e1a7210ce2c7f07 100644 (file)
@@ -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);                      \
@@ -108,8 +109,6 @@ __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
 
 void perf_gtk__init_hpp(void)
 {
-       perf_hpp__column_enable(PERF_HPP__OVERHEAD);
-
        perf_hpp__init();
 
        perf_hpp__format[PERF_HPP__OVERHEAD].color =
@@ -124,6 +123,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 +205,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 +228,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 +243,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 +261,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 +289,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 +309,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);
 }
 
index 4bf91b09d62db6684c9e66c985810303b3950af1..0a193281eba85ec12bbed36d85803b51d7d24464 100644 (file)
@@ -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)
index ae6a789cb0f62780a9fa088f7fe99939c798ef2d..47d9a571f261da9c65e36aa82f8dbdb70a838acc 100644 (file)
@@ -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;
        }
index ae7a75432249fe6fba3f7d4c45e8865200261ba1..5b4fb330f656d71f929f197c06387389dbc2dfed 100644 (file)
@@ -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, ".");
        }
index d102716c43a1b30beb08ed89f1f18edc60d66dd0..bfc5a27597d60e1f09b7f95ef691e37608f8c694 100644 (file)
@@ -110,10 +110,10 @@ static int jump__parse(struct ins_operands *ops)
 {
        const char *s = strchr(ops->raw, '+');
 
-       ops->target.addr = strtoll(ops->raw, NULL, 16);
+       ops->target.addr = strtoull(ops->raw, NULL, 16);
 
        if (s++ != NULL)
-               ops->target.offset = strtoll(s, NULL, 16);
+               ops->target.offset = strtoull(s, NULL, 16);
        else
                ops->target.offset = UINT64_MAX;
 
@@ -821,11 +821,55 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
        if (dl == NULL)
                return -1;
 
+       if (dl->ops.target.offset == UINT64_MAX)
+               dl->ops.target.offset = dl->ops.target.addr -
+                                       map__rip_2objdump(map, sym->start);
+
+       /*
+        * kcore has no symbols, so add the call target name if it is on the
+        * same map.
+        */
+       if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) {
+               struct symbol *s;
+               u64 ip = dl->ops.target.addr;
+
+               if (ip >= map->start && ip <= map->end) {
+                       ip = map->map_ip(map, ip);
+                       s = map__find_symbol(map, ip, NULL);
+                       if (s && s->start == ip)
+                               dl->ops.target.name = strdup(s->name);
+               }
+       }
+
        disasm__add(&notes->src->source, dl);
 
        return 0;
 }
 
+static void delete_last_nop(struct symbol *sym)
+{
+       struct annotation *notes = symbol__annotation(sym);
+       struct list_head *list = &notes->src->source;
+       struct disasm_line *dl;
+
+       while (!list_empty(list)) {
+               dl = list_entry(list->prev, struct disasm_line, node);
+
+               if (dl->ins && dl->ins->ops) {
+                       if (dl->ins->ops != &nop_ops)
+                               return;
+               } else {
+                       if (!strstr(dl->line, " nop ") &&
+                           !strstr(dl->line, " nopl ") &&
+                           !strstr(dl->line, " nopw "))
+                               return;
+               }
+
+               list_del(&dl->node);
+               disasm_line__free(dl);
+       }
+}
+
 int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize)
 {
        struct dso *dso = map->dso;
@@ -864,7 +908,8 @@ fallback:
                free_filename = false;
        }
 
-       if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) {
+       if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
+           !dso__is_kcore(dso)) {
                char bf[BUILD_ID_SIZE * 2 + 16] = " with build id ";
                char *build_id_msg = NULL;
 
@@ -898,7 +943,7 @@ fallback:
        snprintf(command, sizeof(command),
                 "%s %s%s --start-address=0x%016" PRIx64
                 " --stop-address=0x%016" PRIx64
-                " -d %s %s -C %s|grep -v %s|expand",
+                " -d %s %s -C %s 2>/dev/null|grep -v %s|expand",
                 objdump_path ? objdump_path : "objdump",
                 disassembler_style ? "-M " : "",
                 disassembler_style ? disassembler_style : "",
@@ -918,6 +963,13 @@ fallback:
                if (symbol__parse_objdump_line(sym, map, file, privsize) < 0)
                        break;
 
+       /*
+        * kallsyms does not have symbol sizes so there may a nop at the end.
+        * Remove it.
+        */
+       if (dso__is_kcore(dso))
+               delete_last_nop(sym);
+
        pclose(file);
 out_free_filename:
        if (free_filename)
index 42b6a632fe7b75bb297433e9ad9962814e5016d9..4fee33b229b0f8a67b664edbc2f092dc0bc0d235 100644 (file)
@@ -15,6 +15,7 @@
 #include <errno.h>
 #include <math.h>
 
+#include "hist.h"
 #include "util.h"
 #include "callchain.h"
 
@@ -327,7 +328,8 @@ append_chain(struct callchain_node *root,
        /*
         * Lookup in the current node
         * If we have a symbol, then compare the start to match
-        * anywhere inside a function.
+        * anywhere inside a function, unless function
+        * mode is disabled.
         */
        list_for_each_entry(cnode, &root->val, list) {
                struct callchain_cursor_node *node;
@@ -339,7 +341,8 @@ append_chain(struct callchain_node *root,
 
                sym = node->sym;
 
-               if (cnode->ms.sym && sym) {
+               if (cnode->ms.sym && sym &&
+                   callchain_param.key == CCKEY_FUNCTION) {
                        if (cnode->ms.sym->start != sym->start)
                                break;
                } else if (cnode->ip != node->ip)
index 3ee9f67d5af0bed457cb7ae22d5efe99ccdbfd8f..812d5a0ff2bcf7da3ce89315e6ad130a173adf10 100644 (file)
@@ -41,12 +41,18 @@ struct callchain_param;
 typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *,
                                 u64, struct callchain_param *);
 
+enum chain_key {
+       CCKEY_FUNCTION,
+       CCKEY_ADDRESS
+};
+
 struct callchain_param {
        enum chain_mode         mode;
        u32                     print_limit;
        double                  min_percent;
        sort_chain_func_t       sort;
        enum chain_order        order;
+       enum chain_key          key;
 };
 
 struct callchain_list {
index 9bed02e5fb3d9b35182a10fbc9e9de6b236a5e22..b123bb9d6f55ec2307f97f10acf43b8a56b38701 100644 (file)
@@ -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;
 }
index c4374f07603ce14b717f86d087680ab006f99e23..e3c1ff8512c827330d4afde013e19b7c5eeb58f2 100644 (file)
@@ -78,6 +78,8 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,
                         symbol_conf.symfs, build_id_hex, build_id_hex + 2);
                break;
 
+       case DSO_BINARY_TYPE__VMLINUX:
+       case DSO_BINARY_TYPE__GUEST_VMLINUX:
        case DSO_BINARY_TYPE__SYSTEM_PATH_DSO:
                snprintf(file, size, "%s%s",
                         symbol_conf.symfs, dso->long_name);
@@ -93,11 +95,14 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,
                         dso->long_name);
                break;
 
+       case DSO_BINARY_TYPE__KCORE:
+       case DSO_BINARY_TYPE__GUEST_KCORE:
+               snprintf(file, size, "%s", dso->long_name);
+               break;
+
        default:
        case DSO_BINARY_TYPE__KALLSYMS:
-       case DSO_BINARY_TYPE__VMLINUX:
        case DSO_BINARY_TYPE__GUEST_KALLSYMS:
-       case DSO_BINARY_TYPE__GUEST_VMLINUX:
        case DSO_BINARY_TYPE__JAVA_JIT:
        case DSO_BINARY_TYPE__NOT_FOUND:
                ret = -1;
@@ -419,6 +424,7 @@ struct dso *dso__new(const char *name)
                dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND;
                dso->data_type   = DSO_BINARY_TYPE__NOT_FOUND;
                dso->loaded = 0;
+               dso->rel = 0;
                dso->sorted_by_name = 0;
                dso->has_build_id = 0;
                dso->kernel = DSO_TYPE_USER;
index d51aaf272c683e26e76fbd9f9b57d44c7e467c07..b793053335d60fca3302fd34b27543cd6151b0fb 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/types.h>
 #include <linux/rbtree.h>
+#include <stdbool.h>
 #include "types.h"
 #include "map.h"
 
@@ -20,6 +21,8 @@ enum dso_binary_type {
        DSO_BINARY_TYPE__SYSTEM_PATH_DSO,
        DSO_BINARY_TYPE__GUEST_KMODULE,
        DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE,
+       DSO_BINARY_TYPE__KCORE,
+       DSO_BINARY_TYPE__GUEST_KCORE,
        DSO_BINARY_TYPE__NOT_FOUND,
 };
 
@@ -84,6 +87,7 @@ struct dso {
        u8               lname_alloc:1;
        u8               sorted_by_name;
        u8               loaded;
+       u8               rel;
        u8               build_id[BUILD_ID_SIZE];
        const char       *short_name;
        char             *long_name;
@@ -146,4 +150,17 @@ size_t dso__fprintf_buildid(struct dso *dso, FILE *fp);
 size_t dso__fprintf_symbols_by_name(struct dso *dso,
                                    enum map_type type, FILE *fp);
 size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp);
+
+static inline bool dso__is_vmlinux(struct dso *dso)
+{
+       return dso->data_type == DSO_BINARY_TYPE__VMLINUX ||
+              dso->data_type == DSO_BINARY_TYPE__GUEST_VMLINUX;
+}
+
+static inline bool dso__is_kcore(struct dso *dso)
+{
+       return dso->data_type == DSO_BINARY_TYPE__KCORE ||
+              dso->data_type == DSO_BINARY_TYPE__GUEST_KCORE;
+}
+
 #endif /* __PERF_DSO */
index 5cd13d768cecee1a630ca51715dba4b6d8f19f0b..49713ae4655182d14da39e210390879ecb10dd32 100644 (file)
@@ -595,6 +595,7 @@ void thread__find_addr_map(struct thread *self,
                           struct addr_location *al)
 {
        struct map_groups *mg = &self->mg;
+       bool load_map = false;
 
        al->thread = self;
        al->addr = addr;
@@ -609,11 +610,13 @@ void thread__find_addr_map(struct thread *self,
        if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
                al->level = 'k';
                mg = &machine->kmaps;
+               load_map = true;
        } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {
                al->level = '.';
        } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
                al->level = 'g';
                mg = &machine->kmaps;
+               load_map = true;
        } else {
                /*
                 * 'u' means guest os user space.
@@ -654,18 +657,25 @@ try_again:
                        mg = &machine->kmaps;
                        goto try_again;
                }
-       } else
+       } else {
+               /*
+                * Kernel maps might be changed when loading symbols so loading
+                * must be done prior to using kernel maps.
+                */
+               if (load_map)
+                       map__load(al->map, machine->symbol_filter);
                al->addr = al->map->map_ip(al->map, al->addr);
+       }
 }
 
 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)
+                               struct addr_location *al)
 {
        thread__find_addr_map(thread, machine, cpumode, type, addr, al);
        if (al->map != NULL)
-               al->sym = map__find_symbol(al->map, al->addr, filter);
+               al->sym = map__find_symbol(al->map, al->addr,
+                                          machine->symbol_filter);
        else
                al->sym = NULL;
 }
@@ -673,8 +683,7 @@ void thread__find_addr_location(struct thread *thread, struct machine *machine,
 int perf_event__preprocess_sample(const union perf_event *event,
                                  struct machine *machine,
                                  struct addr_location *al,
-                                 struct perf_sample *sample,
-                                 symbol_filter_t filter)
+                                 struct perf_sample *sample)
 {
        u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
        struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
@@ -686,7 +695,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?
         *
@@ -717,7 +726,8 @@ int perf_event__preprocess_sample(const union perf_event *event,
                                                   dso->long_name)))))
                        goto out_filtered;
 
-               al->sym = map__find_symbol(al->map, al->addr, filter);
+               al->sym = map__find_symbol(al->map, al->addr,
+                                          machine->symbol_filter);
        }
 
        if (symbol_conf.sym_list &&
index 181389535c0cf787e83bba1a75deb7346b19aab3..15db071d96b568693c360645f4757c5e23c54ad1 100644 (file)
@@ -80,6 +80,23 @@ struct stack_dump {
        char *data;
 };
 
+struct sample_read_value {
+       u64 value;
+       u64 id;
+};
+
+struct sample_read {
+       u64 time_enabled;
+       u64 time_running;
+       union {
+               struct {
+                       u64 nr;
+                       struct sample_read_value *values;
+               } group;
+               struct sample_read_value one;
+       };
+};
+
 struct perf_sample {
        u64 ip;
        u32 pid, tid;
@@ -97,6 +114,7 @@ struct perf_sample {
        struct branch_stack *branch_stack;
        struct regs_dump  user_regs;
        struct stack_dump user_stack;
+       struct sample_read read;
 };
 
 #define PERF_MEM_DATA_SRC_NONE \
@@ -116,7 +134,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,
@@ -216,8 +234,7 @@ struct addr_location;
 int perf_event__preprocess_sample(const union perf_event *self,
                                  struct machine *machine,
                                  struct addr_location *al,
-                                 struct perf_sample *sample,
-                                 symbol_filter_t filter);
+                                 struct perf_sample *sample);
 
 const char *perf_event__name(unsigned int id);
 
index 8065ce8fa9a5cdaea730e55667b7c5ed1d9b77f9..c7d111f745531450dbb25d80811fe518364ef815 100644 (file)
@@ -302,6 +302,24 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
 {
        u64 read_data[4] = { 0, };
        int id_idx = 1; /* The first entry is the counter value */
+       u64 id;
+       int ret;
+
+       ret = ioctl(fd, PERF_EVENT_IOC_ID, &id);
+       if (!ret)
+               goto add;
+
+       if (errno != ENOTTY)
+               return -1;
+
+       /* Legacy way to get event id.. All hail to old kernels! */
+
+       /*
+        * This way does not work with group format read, so bail
+        * out in that case.
+        */
+       if (perf_evlist__read_format(evlist) & PERF_FORMAT_GROUP)
+               return -1;
 
        if (!(evsel->attr.read_format & PERF_FORMAT_ID) ||
            read(fd, &read_data, sizeof(read_data)) == -1)
@@ -312,25 +330,39 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
        if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
                ++id_idx;
 
-       perf_evlist__id_add(evlist, evsel, cpu, thread, read_data[id_idx]);
+       id = read_data[id_idx];
+
+ add:
+       perf_evlist__id_add(evlist, evsel, cpu, thread, id);
        return 0;
 }
 
-struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
+struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id)
 {
        struct hlist_head *head;
        struct perf_sample_id *sid;
        int hash;
 
-       if (evlist->nr_entries == 1)
-               return perf_evlist__first(evlist);
-
        hash = hash_64(id, PERF_EVLIST__HLIST_BITS);
        head = &evlist->heads[hash];
 
        hlist_for_each_entry(sid, head, node)
                if (sid->id == id)
-                       return sid->evsel;
+                       return sid;
+
+       return NULL;
+}
+
+struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
+{
+       struct perf_sample_id *sid;
+
+       if (evlist->nr_entries == 1)
+               return perf_evlist__first(evlist);
+
+       sid = perf_evlist__id2sid(evlist, id);
+       if (sid)
+               return sid->evsel;
 
        if (!perf_evlist__sample_id_all(evlist))
                return perf_evlist__first(evlist);
@@ -403,16 +435,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 +457,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 +513,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 +549,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 +601,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);
@@ -666,6 +694,32 @@ u64 perf_evlist__sample_type(struct perf_evlist *evlist)
        return first->attr.sample_type;
 }
 
+bool perf_evlist__valid_read_format(struct perf_evlist *evlist)
+{
+       struct perf_evsel *first = perf_evlist__first(evlist), *pos = first;
+       u64 read_format = first->attr.read_format;
+       u64 sample_type = first->attr.sample_type;
+
+       list_for_each_entry_continue(pos, &evlist->entries, node) {
+               if (read_format != pos->attr.read_format)
+                       return false;
+       }
+
+       /* PERF_SAMPLE_READ imples PERF_FORMAT_ID. */
+       if ((sample_type & PERF_SAMPLE_READ) &&
+           !(read_format & PERF_FORMAT_ID)) {
+               return false;
+       }
+
+       return true;
+}
+
+u64 perf_evlist__read_format(struct perf_evlist *evlist)
+{
+       struct perf_evsel *first = perf_evlist__first(evlist);
+       return first->attr.read_format;
+}
+
 u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist)
 {
        struct perf_evsel *first = perf_evlist__first(evlist);
@@ -782,13 +836,6 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist,
                close(go_pipe[1]);
                fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
 
-               /*
-                * Do a dummy execvp to get the PLT entry resolved,
-                * so we avoid the resolver overhead on the real
-                * execvp call.
-                */
-               execvp("", (char **)argv);
-
                /*
                 * Tell the parent we're ready to go
                 */
@@ -838,7 +885,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!
index 0583d36252be940ac5f9fe6872526a38e4781fb7..327ababa67b6130991a0c6f32ea666c73b21babf 100644 (file)
@@ -78,6 +78,8 @@ void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
 
 struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);
 
+struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id);
+
 union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx);
 
 int perf_evlist__open(struct perf_evlist *evlist);
@@ -118,6 +120,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist);
 void __perf_evlist__set_leader(struct list_head *list);
 void perf_evlist__set_leader(struct perf_evlist *evlist);
 
+u64 perf_evlist__read_format(struct perf_evlist *evlist);
 u64 perf_evlist__sample_type(struct perf_evlist *evlist);
 bool perf_evlist__sample_id_all(struct perf_evlist *evlist);
 u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist);
@@ -127,6 +130,7 @@ int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *even
 
 bool perf_evlist__valid_sample_type(struct perf_evlist *evlist);
 bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist);
+bool perf_evlist__valid_read_format(struct perf_evlist *evlist);
 
 void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
                                   struct list_head *list,
index c9c7494506a1e7bfddde46ca242f0d583f2500e3..960394ea1e3aa006661798bf4413d9e42d9a201b 100644 (file)
@@ -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 {
@@ -490,6 +490,7 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
 void perf_evsel__config(struct perf_evsel *evsel,
                        struct perf_record_opts *opts)
 {
+       struct perf_evsel *leader = evsel->leader;
        struct perf_event_attr *attr = &evsel->attr;
        int track = !evsel->idx; /* only the first counter needs these */
 
@@ -499,6 +500,25 @@ void perf_evsel__config(struct perf_evsel *evsel,
        perf_evsel__set_sample_bit(evsel, IP);
        perf_evsel__set_sample_bit(evsel, TID);
 
+       if (evsel->sample_read) {
+               perf_evsel__set_sample_bit(evsel, READ);
+
+               /*
+                * We need ID even in case of single event, because
+                * PERF_SAMPLE_READ process ID specific data.
+                */
+               perf_evsel__set_sample_id(evsel);
+
+               /*
+                * Apply group format only if we belong to group
+                * with more than one members.
+                */
+               if (leader->nr_members > 1) {
+                       attr->read_format |= PERF_FORMAT_GROUP;
+                       attr->inherit = 0;
+               }
+       }
+
        /*
         * We default some events to a 1 default interval. But keep
         * it a weak assumption overridable by the user.
@@ -514,6 +534,15 @@ void perf_evsel__config(struct perf_evsel *evsel,
                }
        }
 
+       /*
+        * Disable sampling for all group members other
+        * than leader in case leader 'leads' the sampling.
+        */
+       if ((leader != evsel) && leader->sample_read) {
+               attr->sample_freq   = 0;
+               attr->sample_period = 0;
+       }
+
        if (opts->no_samples)
                attr->sample_freq = 0;
 
@@ -605,15 +634,15 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
        return evsel->fd != NULL ? 0 : -ENOMEM;
 }
 
-int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
-                          const char *filter)
+static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthreads,
+                         int ioc,  void *arg)
 {
        int cpu, thread;
 
        for (cpu = 0; cpu < ncpus; cpu++) {
                for (thread = 0; thread < nthreads; thread++) {
                        int fd = FD(evsel, cpu, thread),
-                           err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter);
+                           err = ioctl(fd, ioc, arg);
 
                        if (err)
                                return err;
@@ -623,6 +652,21 @@ int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
        return 0;
 }
 
+int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
+                          const char *filter)
+{
+       return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
+                                    PERF_EVENT_IOC_SET_FILTER,
+                                    (void *)filter);
+}
+
+int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads)
+{
+       return perf_evsel__run_ioctl(evsel, ncpus, nthreads,
+                                    PERF_EVENT_IOC_ENABLE,
+                                    0);
+}
+
 int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
 {
        evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id));
@@ -1096,8 +1140,34 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
        }
 
        if (type & PERF_SAMPLE_READ) {
-               fprintf(stderr, "PERF_SAMPLE_READ is unsupported for now\n");
-               return -1;
+               u64 read_format = evsel->attr.read_format;
+
+               if (read_format & PERF_FORMAT_GROUP)
+                       data->read.group.nr = *array;
+               else
+                       data->read.one.value = *array;
+
+               array++;
+
+               if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) {
+                       data->read.time_enabled = *array;
+                       array++;
+               }
+
+               if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) {
+                       data->read.time_running = *array;
+                       array++;
+               }
+
+               /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */
+               if (read_format & PERF_FORMAT_GROUP) {
+                       data->read.group.values = (struct sample_read_value *) array;
+                       array = (void *) array + data->read.group.nr *
+                               sizeof(struct sample_read_value);
+               } else {
+                       data->read.one.id = *array;
+                       array++;
+               }
        }
 
        if (type & PERF_SAMPLE_CALLCHAIN) {
@@ -1482,7 +1552,7 @@ out:
 bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
                          char *msg, size_t msgsize)
 {
-       if ((err == ENOENT || err == ENXIO) &&
+       if ((err == ENOENT || err == ENXIO || err == ENODEV) &&
            evsel->attr.type   == PERF_TYPE_HARDWARE &&
            evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) {
                /*
index 3f156ccc1acb4c7b93d85620438844f5ba3744ac..532a5f925da0563b0b6e9f36b0a51b2f0cd6e388 100644 (file)
@@ -38,6 +38,9 @@ struct perf_sample_id {
        struct hlist_node       node;
        u64                     id;
        struct perf_evsel       *evsel;
+
+       /* Holds total ID period value for PERF_SAMPLE_READ processing. */
+       u64                     period;
 };
 
 /** struct perf_evsel - event selector
@@ -76,6 +79,7 @@ struct perf_evsel {
        /* parse modifier helper */
        int                     exclude_GH;
        int                     nr_members;
+       int                     sample_read;
        struct perf_evsel       *leader;
        char                    *group_name;
 };
@@ -142,6 +146,7 @@ void perf_evsel__set_sample_id(struct perf_evsel *evsel);
 
 int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
                           const char *filter);
+int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads);
 
 int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
                             struct cpu_map *cpus);
index a4dafbee2511546a97b9b9c63cb84fe76e0d19eb..a33197a4fd21b4790191ca9f654498e0b660bced 100644 (file)
 
 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
@@ -748,18 +716,19 @@ static int build_cpu_topo(struct cpu_topo *tp, int cpu)
        char filename[MAXPATHLEN];
        char *buf = NULL, *p;
        size_t len = 0;
+       ssize_t sret;
        u32 i = 0;
        int ret = -1;
 
        sprintf(filename, CORE_SIB_FMT, cpu);
        fp = fopen(filename, "r");
        if (!fp)
-               return -1;
-
-       if (getline(&buf, &len, fp) <= 0)
-               goto done;
+               goto try_threads;
 
+       sret = getline(&buf, &len, fp);
        fclose(fp);
+       if (sret <= 0)
+               goto try_threads;
 
        p = strchr(buf, '\n');
        if (p)
@@ -775,7 +744,9 @@ static int build_cpu_topo(struct cpu_topo *tp, int cpu)
                buf = NULL;
                len = 0;
        }
+       ret = 0;
 
+try_threads:
        sprintf(filename, THRD_SIB_FMT, cpu);
        fp = fopen(filename, "r");
        if (!fp)
@@ -2257,7 +2228,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 +2275,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 +2289,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 +2306,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 +2320,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 +2377,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 +2483,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 +2505,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 +2576,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 +2627,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 +2731,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 +2799,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 +2869,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 +2909,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 +2955,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;
index 16a3e83c584e7a08003f6c7803fdf7cf8a99246f..307c9aed972ed0d6403fe58da4d9edae4109abdf 100644 (file)
@@ -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,
index b11a6cfdb414981c3f94814f8e4aa79aa83b8784..46a0d35a05e1f21aae097e7a67cea89bfe9ecddf 100644 (file)
@@ -24,7 +24,8 @@ enum hist_filter {
 struct callchain_param callchain_param = {
        .mode   = CHAIN_GRAPH_REL,
        .min_percent = 0.5,
-       .order  = ORDER_CALLEE
+       .order  = ORDER_CALLEE,
+       .key    = CCKEY_FUNCTION
 };
 
 u16 hists__col_len(struct hists *hists, enum hist_column col)
@@ -912,6 +913,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;
index 2d3790fd99bb13f34c616524c712966d3dcf5963..1329b6b6ffe61b0f5fb97d8582a837f554b3c752 100644 (file)
@@ -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 */
index 6f19c548ecc0bc96680a6a529ee844b829578396..97a80073822669accc928265fcf4b052c2e5c9cb 100644 (file)
@@ -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);
index b2ecad6ec46b1c87e9f2decc4b790cf497fa7933..4514e7e9b659df6244e060d1663eef2b0f00e3ce 100644 (file)
@@ -25,6 +25,8 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
        machine->kmaps.machine = machine;
        machine->pid = pid;
 
+       machine->symbol_filter = NULL;
+
        machine->root_dir = strdup(root_dir);
        if (machine->root_dir == NULL)
                return -ENOMEM;
@@ -95,6 +97,7 @@ void machines__init(struct machines *machines)
 {
        machine__init(&machines->host, "", HOST_KERNEL_ID);
        machines->guests = RB_ROOT;
+       machines->symbol_filter = NULL;
 }
 
 void machines__exit(struct machines *machines)
@@ -118,6 +121,8 @@ struct machine *machines__add(struct machines *machines, pid_t pid,
                return NULL;
        }
 
+       machine->symbol_filter = machines->symbol_filter;
+
        while (*p != NULL) {
                parent = *p;
                pos = rb_entry(parent, struct machine, rb_node);
@@ -133,6 +138,21 @@ struct machine *machines__add(struct machines *machines, pid_t pid,
        return machine;
 }
 
+void machines__set_symbol_filter(struct machines *machines,
+                                symbol_filter_t symbol_filter)
+{
+       struct rb_node *nd;
+
+       machines->symbol_filter = symbol_filter;
+       machines->host.symbol_filter = symbol_filter;
+
+       for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) {
+               struct machine *machine = rb_entry(nd, struct machine, rb_node);
+
+               machine->symbol_filter = symbol_filter;
+       }
+}
+
 struct machine *machines__find(struct machines *machines, pid_t pid)
 {
        struct rb_node **p = &machines->guests.rb_node;
@@ -233,7 +253,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 +261,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 +286,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 +296,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)
@@ -628,10 +648,8 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type,
        struct map *map = machine->vmlinux_maps[type];
        int ret = dso__load_vmlinux_path(map->dso, map, filter);
 
-       if (ret > 0) {
+       if (ret > 0)
                dso__set_loaded(map->dso, type);
-               map__reloc_vmlinux(map);
-       }
 
        return ret;
 }
@@ -808,7 +826,10 @@ static int machine__create_modules(struct machine *machine)
        free(line);
        fclose(file);
 
-       return machine__set_modules_path(machine);
+       if (machine__set_modules_path(machine) < 0) {
+               pr_debug("Problems setting modules path maps, continuing anyway...\n");
+       }
+       return 0;
 
 out_delete_line:
        free(line);
@@ -858,6 +879,18 @@ static void machine__set_kernel_mmap_len(struct machine *machine,
        }
 }
 
+static bool machine__uses_kcore(struct machine *machine)
+{
+       struct dso *dso;
+
+       list_for_each_entry(dso, &machine->kernel_dsos, node) {
+               if (dso__is_kcore(dso))
+                       return true;
+       }
+
+       return false;
+}
+
 static int machine__process_kernel_mmap_event(struct machine *machine,
                                              union perf_event *event)
 {
@@ -866,6 +899,10 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
        enum dso_kernel_type kernel_type;
        bool is_kernel_mmap;
 
+       /* If we have maps from kcore then we do not need or want any others */
+       if (machine__uses_kcore(machine))
+               return 0;
+
        machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
        if (machine__is_host(machine))
                kernel_type = DSO_TYPE_KERNEL;
@@ -1058,11 +1095,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;
 }
 
@@ -1094,7 +1130,7 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread,
                 * or else, the symbol is unknown
                 */
                thread__find_addr_location(thread, machine, m, MAP__FUNCTION,
-                               ip, &al, NULL);
+                               ip, &al);
                if (al.sym)
                        goto found;
        }
@@ -1112,8 +1148,8 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread,
 
        memset(&al, 0, sizeof(al));
 
-       thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, &al,
-                                  NULL);
+       thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr,
+                                  &al);
        ams->addr = addr;
        ams->al_addr = al.addr;
        ams->sym = al.sym;
@@ -1159,8 +1195,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;
@@ -1208,11 +1244,18 @@ static int machine__resolve_callchain_sample(struct machine *machine,
 
                al.filtered = false;
                thread__find_addr_location(thread, machine, cpumode,
-                                          MAP__FUNCTION, ip, &al, NULL);
+                                          MAP__FUNCTION, ip, &al);
                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 +1280,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;
 
index 77940680f1fca29f1e89fd1f7ea8885f3b0e3390..603ffba999d9bfa46d1345faefc6a8e1ba07782f 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/rbtree.h>
 #include "map.h"
 
+struct addr_location;
 struct branch_stack;
 struct perf_evsel;
 struct perf_sample;
@@ -28,6 +29,7 @@ struct machine {
        struct list_head  kernel_dsos;
        struct map_groups kmaps;
        struct map        *vmlinux_maps[MAP__NR_TYPES];
+       symbol_filter_t   symbol_filter;
 };
 
 static inline
@@ -36,7 +38,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);
@@ -50,6 +52,7 @@ typedef void (*machine__process_t)(struct machine *machine, void *data);
 struct machines {
        struct machine host;
        struct rb_root guests;
+       symbol_filter_t symbol_filter;
 };
 
 void machines__init(struct machines *machines);
@@ -67,6 +70,9 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid);
 void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size);
 char *machine__mmap_name(struct machine *machine, char *bf, size_t size);
 
+void machines__set_symbol_filter(struct machines *machines,
+                                symbol_filter_t symbol_filter);
+
 int machine__init(struct machine *machine, const char *root_dir, pid_t pid);
 void machine__exit(struct machine *machine);
 void machine__delete_dead_threads(struct machine *machine);
@@ -83,7 +89,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 +106,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);
 
index 8bcdf9e54089acaf4963eb8288305bcba4d047d6..9e8304ca343e4ce6d6b2e80d3a1b76e4808fadda 100644 (file)
@@ -182,12 +182,6 @@ int map__load(struct map *map, symbol_filter_t filter)
 #endif
                return -1;
        }
-       /*
-        * Only applies to the kernel, as its symtabs aren't relative like the
-        * module ones.
-        */
-       if (map->dso->kernel)
-               map__reloc_vmlinux(map);
 
        return 0;
 }
@@ -254,14 +248,18 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp)
 
 /*
  * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN.
- * map->dso->adjust_symbols==1 for ET_EXEC-like cases.
+ * map->dso->adjust_symbols==1 for ET_EXEC-like cases except ET_REL which is
+ * relative to section start.
  */
 u64 map__rip_2objdump(struct map *map, u64 rip)
 {
-       u64 addr = map->dso->adjust_symbols ?
-                       map->unmap_ip(map, rip) :       /* RIP -> IP */
-                       rip;
-       return addr;
+       if (!map->dso->adjust_symbols)
+               return rip;
+
+       if (map->dso->rel)
+               return rip - map->pgoff;
+
+       return map->unmap_ip(map, rip);
 }
 
 void map_groups__init(struct map_groups *mg)
@@ -513,35 +511,6 @@ int map_groups__clone(struct map_groups *mg,
        return 0;
 }
 
-static u64 map__reloc_map_ip(struct map *map, u64 ip)
-{
-       return ip + (s64)map->pgoff;
-}
-
-static u64 map__reloc_unmap_ip(struct map *map, u64 ip)
-{
-       return ip - (s64)map->pgoff;
-}
-
-void map__reloc_vmlinux(struct map *map)
-{
-       struct kmap *kmap = map__kmap(map);
-       s64 reloc;
-
-       if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr)
-               return;
-
-       reloc = (kmap->ref_reloc_sym->unrelocated_addr -
-                kmap->ref_reloc_sym->addr);
-
-       if (!reloc)
-               return;
-
-       map->map_ip   = map__reloc_map_ip;
-       map->unmap_ip = map__reloc_unmap_ip;
-       map->pgoff    = reloc;
-}
-
 void maps__insert(struct rb_root *maps, struct map *map)
 {
        struct rb_node **p = &maps->rb_node;
@@ -586,3 +555,21 @@ struct map *maps__find(struct rb_root *maps, u64 ip)
 
        return NULL;
 }
+
+struct map *maps__first(struct rb_root *maps)
+{
+       struct rb_node *first = rb_first(maps);
+
+       if (first)
+               return rb_entry(first, struct map, rb_node);
+       return NULL;
+}
+
+struct map *maps__next(struct map *map)
+{
+       struct rb_node *next = rb_next(&map->rb_node);
+
+       if (next)
+               return rb_entry(next, struct map, rb_node);
+       return NULL;
+}
index a887f2c9dfbb0248d89640ea9609cabdc8907849..2cc93cbf0e17a021574a69d3198da84608105eb6 100644 (file)
@@ -112,6 +112,8 @@ size_t __map_groups__fprintf_maps(struct map_groups *mg,
 void maps__insert(struct rb_root *maps, struct map *map);
 void maps__remove(struct rb_root *maps, struct map *map);
 struct map *maps__find(struct rb_root *maps, u64 addr);
+struct map *maps__first(struct rb_root *maps);
+struct map *maps__next(struct map *map);
 void map_groups__init(struct map_groups *mg);
 void map_groups__exit(struct map_groups *mg);
 int map_groups__clone(struct map_groups *mg,
@@ -139,6 +141,17 @@ static inline struct map *map_groups__find(struct map_groups *mg,
        return maps__find(&mg->maps[type], addr);
 }
 
+static inline struct map *map_groups__first(struct map_groups *mg,
+                                           enum map_type type)
+{
+       return maps__first(&mg->maps[type]);
+}
+
+static inline struct map *map_groups__next(struct map *map)
+{
+       return maps__next(map);
+}
+
 struct symbol *map_groups__find_symbol(struct map_groups *mg,
                                       enum map_type type, u64 addr,
                                       struct map **mapp,
index 995fc25db8c61c401a150b6813869047a7ccc7c3..9cba92386a8227a2d5bb54b6f7c5d6bda26ff6b0 100644 (file)
@@ -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)
 {
@@ -684,6 +687,8 @@ struct event_modifier {
        int eG;
        int precise;
        int exclude_GH;
+       int sample_read;
+       int pinned;
 };
 
 static int get_event_modifier(struct event_modifier *mod, char *str,
@@ -695,6 +700,8 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
        int eH = evsel ? evsel->attr.exclude_host : 0;
        int eG = evsel ? evsel->attr.exclude_guest : 0;
        int precise = evsel ? evsel->attr.precise_ip : 0;
+       int sample_read = 0;
+       int pinned = evsel ? evsel->attr.pinned : 0;
 
        int exclude = eu | ek | eh;
        int exclude_GH = evsel ? evsel->exclude_GH : 0;
@@ -727,6 +734,10 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
                        /* use of precise requires exclude_guest */
                        if (!exclude_GH)
                                eG = 1;
+               } else if (*str == 'S') {
+                       sample_read = 1;
+               } else if (*str == 'D') {
+                       pinned = 1;
                } else
                        break;
 
@@ -753,6 +764,9 @@ static int get_event_modifier(struct event_modifier *mod, char *str,
        mod->eG = eG;
        mod->precise = precise;
        mod->exclude_GH = exclude_GH;
+       mod->sample_read = sample_read;
+       mod->pinned = pinned;
+
        return 0;
 }
 
@@ -765,7 +779,7 @@ static int check_modifier(char *str)
        char *p = str;
 
        /* The sizeof includes 0 byte as well. */
-       if (strlen(str) > (sizeof("ukhGHppp") - 1))
+       if (strlen(str) > (sizeof("ukhGHpppSD") - 1))
                return -1;
 
        while (*p) {
@@ -803,6 +817,10 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add)
                evsel->attr.exclude_host   = mod.eH;
                evsel->attr.exclude_guest  = mod.eG;
                evsel->exclude_GH          = mod.exclude_GH;
+               evsel->sample_read         = mod.sample_read;
+
+               if (perf_evsel__is_group_leader(evsel))
+                       evsel->attr.pinned = mod.pinned;
        }
 
        return 0;
@@ -820,6 +838,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 +884,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 +1125,8 @@ int print_hwcache_events(const char *event_glob, bool name_only)
                }
        }
 
+       if (printed)
+               printf("\n");
        return printed;
 }
 
@@ -1133,11 +1181,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 +1286,4 @@ void parse_events__free_terms(struct list_head *terms)
 
        list_for_each_entry_safe(term, h, terms, list)
                free(term);
-
-       free(terms);
 }
index 8a4859315fd97266fa592ea775d993b9c003a48d..f1cb4c4b3c70acad8ae4e9febfa2a3c6b4681b6c 100644 (file)
@@ -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,
index e9d1134c2c68e97cdee78c889fd70156b5351497..0790452658b3ab84b667d644b11f31373c57fbaa 100644 (file)
@@ -82,7 +82,8 @@ num_hex               0x[a-fA-F0-9]+
 num_raw_hex    [a-fA-F0-9]+
 name           [a-zA-Z_*?][a-zA-Z0-9_*?]*
 name_minus     [a-zA-Z_*?][a-zA-Z0-9\-_*?]*
-modifier_event [ukhpGH]+
+/* If you add a modifier you need to update check_modifier() */
+modifier_event [ukhpGHSD]+
 modifier_bp    [rwx]{1,3}
 
 %%
index afc44c18dfe17b054fe01e10c779dbe747373558..4eb67ec333f19ce05f3c7ded7e6999e97af99f04 100644 (file)
@@ -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;
 }
index 4c6f9c490a8d79d0cc7f2367efd05440de735a7c..bc9d8069d37637835758d02e13ec3bc751d0f5e3 100644 (file)
@@ -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);
+}
index 32fe55b659fa93ed17e921c0bed99a07b99f09f6..6b2cbe2d4cc3a6d59a3d692770793c19ea984930 100644 (file)
@@ -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 */
index eacec859f2996143a7fda07e4c96544db1057e1d..a85e4ae5f3ac582381740f8b29460e6f83ae6bd6 100644 (file)
@@ -261,7 +261,8 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,
                                    struct perf_sample *sample,
                                    struct perf_evsel *evsel,
                                    struct machine *machine __maybe_unused,
-                                   struct addr_location *al)
+                                   struct thread *thread,
+                                       struct addr_location *al)
 {
        struct format_field *field;
        static char handler[256];
@@ -272,7 +273,6 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,
        int cpu = sample->cpu;
        void *data = sample->raw_data;
        unsigned long long nsecs = sample->time;
-       struct thread *thread = al->thread;
        char *comm = thread->comm;
 
        dSP;
@@ -351,7 +351,8 @@ static void perl_process_event_generic(union perf_event *event,
                                       struct perf_sample *sample,
                                       struct perf_evsel *evsel,
                                       struct machine *machine __maybe_unused,
-                                      struct addr_location *al __maybe_unused)
+                                      struct thread *thread __maybe_unused,
+                                          struct addr_location *al __maybe_unused)
 {
        dSP;
 
@@ -377,10 +378,11 @@ static void perl_process_event(union perf_event *event,
                               struct perf_sample *sample,
                               struct perf_evsel *evsel,
                               struct machine *machine,
-                              struct addr_location *al)
+                              struct thread *thread,
+                                  struct addr_location *al)
 {
-       perl_process_tracepoint(event, sample, evsel, machine, al);
-       perl_process_event_generic(event, sample, evsel, machine, al);
+       perl_process_tracepoint(event, sample, evsel, machine, thread, al);
+       perl_process_event_generic(event, sample, evsel, machine, thread, al);
 }
 
 static void run_start_sub(void)
index e87aa5d9696b41cca90c23bc9ba81529e449eab8..cc75a3cef388065f3164fe270487d2b06d394c72 100644 (file)
@@ -225,6 +225,7 @@ static void python_process_tracepoint(union perf_event *perf_event
                                 struct perf_sample *sample,
                                 struct perf_evsel *evsel,
                                 struct machine *machine __maybe_unused,
+                                struct thread *thread,
                                 struct addr_location *al)
 {
        PyObject *handler, *retval, *context, *t, *obj, *dict = NULL;
@@ -238,7 +239,6 @@ static void python_process_tracepoint(union perf_event *perf_event
        int cpu = sample->cpu;
        void *data = sample->raw_data;
        unsigned long long nsecs = sample->time;
-       struct thread *thread = al->thread;
        char *comm = thread->comm;
 
        t = PyTuple_New(MAX_FIELDS);
@@ -345,12 +345,12 @@ static void python_process_general_event(union perf_event *perf_event
                                         struct perf_sample *sample,
                                         struct perf_evsel *evsel,
                                         struct machine *machine __maybe_unused,
+                                        struct thread *thread,
                                         struct addr_location *al)
 {
        PyObject *handler, *retval, *t, *dict;
        static char handler_name[64];
        unsigned n = 0;
-       struct thread *thread = al->thread;
 
        /*
         * Use the MAX_FIELDS to make the function expandable, though
@@ -404,17 +404,18 @@ static void python_process_event(union perf_event *perf_event,
                                 struct perf_sample *sample,
                                 struct perf_evsel *evsel,
                                 struct machine *machine,
+                                struct thread *thread,
                                 struct addr_location *al)
 {
        switch (evsel->attr.type) {
        case PERF_TYPE_TRACEPOINT:
                python_process_tracepoint(perf_event, sample, evsel,
-                                         machine, al);
+                                         machine, thread, al);
                break;
        /* Reserve for future process_hw/sw/raw APIs */
        default:
                python_process_general_event(perf_event, sample, evsel,
-                                            machine, al);
+                                            machine, thread, al);
        }
 }
 
index cf1fe01b7e8989570991e88c8216211d30ee039e..de16a77368591054b01951b7b94720be353e88af 100644 (file)
@@ -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;
        }
@@ -71,6 +71,11 @@ static int perf_session__open(struct perf_session *self, bool force)
                goto out_close;
        }
 
+       if (!perf_evlist__valid_read_format(self->evlist)) {
+               pr_err("non matching read_format");
+               goto out_close;
+       }
+
        self->size = input_stat.st_size;
        return 0;
 
@@ -193,7 +198,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 +209,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,18 +246,11 @@ 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);
 
-static void perf_tool__fill_defaults(struct perf_tool *tool)
+void perf_tool__fill_defaults(struct perf_tool *tool)
 {
        if (tool->sample == NULL)
                tool->sample = process_event_sample_stub;
@@ -271,8 +272,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)
@@ -496,7 +495,7 @@ static int perf_session_deliver_event(struct perf_session *session,
                                      u64 file_offset);
 
 static int flush_sample_queue(struct perf_session *s,
-                              struct perf_tool *tool)
+                      struct perf_tool *tool)
 {
        struct ordered_samples *os = &s->ordered_samples;
        struct list_head *head = &os->samples;
@@ -644,7 +643,7 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s)
 
 #define MAX_SAMPLE_BUFFER      (64 * 1024 / sizeof(struct sample_queue))
 
-static int perf_session_queue_event(struct perf_session *s, union perf_event *event,
+int perf_session_queue_event(struct perf_session *s, union perf_event *event,
                                    struct perf_sample *sample, u64 file_offset)
 {
        struct ordered_samples *os = &s->ordered_samples;
@@ -755,6 +754,36 @@ static void perf_session__print_tstamp(struct perf_session *session,
                printf("%" PRIu64 " ", sample->time);
 }
 
+static void sample_read__printf(struct perf_sample *sample, u64 read_format)
+{
+       printf("... sample_read:\n");
+
+       if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+               printf("...... time enabled %016" PRIx64 "\n",
+                      sample->read.time_enabled);
+
+       if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+               printf("...... time running %016" PRIx64 "\n",
+                      sample->read.time_running);
+
+       if (read_format & PERF_FORMAT_GROUP) {
+               u64 i;
+
+               printf(".... group nr %" PRIu64 "\n", sample->read.group.nr);
+
+               for (i = 0; i < sample->read.group.nr; i++) {
+                       struct sample_read_value *value;
+
+                       value = &sample->read.group.values[i];
+                       printf("..... id %016" PRIx64
+                              ", value %016" PRIx64 "\n",
+                              value->id, value->value);
+               }
+       } else
+               printf("..... id %016" PRIx64 ", value %016" PRIx64 "\n",
+                       sample->read.one.id, sample->read.one.value);
+}
+
 static void dump_event(struct perf_session *session, union perf_event *event,
                       u64 file_offset, struct perf_sample *sample)
 {
@@ -804,6 +833,9 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
 
        if (sample_type & PERF_SAMPLE_DATA_SRC)
                printf(" . data_src: 0x%"PRIx64"\n", sample->data_src);
+
+       if (sample_type & PERF_SAMPLE_READ)
+               sample_read__printf(sample, evsel->attr.read_format);
 }
 
 static struct machine *
@@ -828,6 +860,75 @@ static struct machine *
        return &session->machines.host;
 }
 
+static int deliver_sample_value(struct perf_session *session,
+                               struct perf_tool *tool,
+                               union perf_event *event,
+                               struct perf_sample *sample,
+                               struct sample_read_value *v,
+                               struct machine *machine)
+{
+       struct perf_sample_id *sid;
+
+       sid = perf_evlist__id2sid(session->evlist, v->id);
+       if (sid) {
+               sample->id     = v->id;
+               sample->period = v->value - sid->period;
+               sid->period    = v->value;
+       }
+
+       if (!sid || sid->evsel == NULL) {
+               ++session->stats.nr_unknown_id;
+               return 0;
+       }
+
+       return tool->sample(tool, event, sample, sid->evsel, machine);
+}
+
+static int deliver_sample_group(struct perf_session *session,
+                               struct perf_tool *tool,
+                               union  perf_event *event,
+                               struct perf_sample *sample,
+                               struct machine *machine)
+{
+       int ret = -EINVAL;
+       u64 i;
+
+       for (i = 0; i < sample->read.group.nr; i++) {
+               ret = deliver_sample_value(session, tool, event, sample,
+                                          &sample->read.group.values[i],
+                                          machine);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+static int
+perf_session__deliver_sample(struct perf_session *session,
+                            struct perf_tool *tool,
+                            union  perf_event *event,
+                            struct perf_sample *sample,
+                            struct perf_evsel *evsel,
+                            struct machine *machine)
+{
+       /* We know evsel != NULL. */
+       u64 sample_type = evsel->attr.sample_type;
+       u64 read_format = evsel->attr.read_format;
+
+       /* Standard sample delievery. */
+       if (!(sample_type & PERF_SAMPLE_READ))
+               return tool->sample(tool, event, sample, evsel, machine);
+
+       /* For PERF_SAMPLE_READ we have either single or group mode. */
+       if (read_format & PERF_FORMAT_GROUP)
+               return deliver_sample_group(session, tool, event, sample,
+                                           machine);
+       else
+               return deliver_sample_value(session, tool, event, sample,
+                                           &sample->read.one, machine);
+}
+
 static int perf_session_deliver_event(struct perf_session *session,
                                      union perf_event *event,
                                      struct perf_sample *sample,
@@ -870,7 +971,8 @@ static int perf_session_deliver_event(struct perf_session *session,
                        ++session->stats.nr_unprocessable_samples;
                        return 0;
                }
-               return tool->sample(tool, event, sample, evsel, machine);
+               return perf_session__deliver_sample(session, tool, event,
+                                                   sample, evsel, machine);
        case PERF_RECORD_MMAP:
                return tool->mmap(tool, event, sample, machine);
        case PERF_RECORD_COMM:
@@ -921,16 +1023,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 +1191,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 +1263,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 +1348,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,
@@ -1295,12 +1401,15 @@ int perf_session__process_events(struct perf_session *self,
 
 bool perf_session__has_traces(struct perf_session *session, const char *msg)
 {
-       if (!(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_RAW)) {
-               pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
-               return false;
+       struct perf_evsel *evsel;
+
+       list_for_each_entry(evsel, &session->evlist->entries, node) {
+               if (evsel->attr.type == PERF_TYPE_TRACEPOINT)
+                       return true;
        }
 
-       return true;
+       pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
+       return false;
 }
 
 int maps__set_kallsyms_ref_reloc_sym(struct map **maps,
@@ -1383,13 +1492,18 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
 
 void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
                          struct perf_sample *sample, struct machine *machine,
-                         int print_sym, int print_dso, int print_symoffset)
+                         unsigned int print_opts, unsigned int stack_depth)
 {
        struct addr_location al;
        struct callchain_cursor_node *node;
-
-       if (perf_event__preprocess_sample(event, machine, &al, sample,
-                                         NULL) < 0) {
+       int print_ip = print_opts & PRINT_IP_OPT_IP;
+       int print_sym = print_opts & PRINT_IP_OPT_SYM;
+       int print_dso = print_opts & PRINT_IP_OPT_DSO;
+       int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET;
+       int print_oneline = print_opts & PRINT_IP_OPT_ONELINE;
+       char s = print_oneline ? ' ' : '\t';
+
+       if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
                error("problem processing %d event, skipping it.\n",
                        event->header.type);
                return;
@@ -1397,37 +1511,49 @@ 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;
                }
                callchain_cursor_commit(&callchain_cursor);
 
-               while (1) {
+               while (stack_depth) {
                        node = callchain_cursor_current(&callchain_cursor);
                        if (!node)
                                break;
 
-                       printf("\t%16" PRIx64, node->ip);
+                       if (print_ip)
+                               printf("%c%16" PRIx64, s, node->ip);
+
                        if (print_sym) {
                                printf(" ");
-                               symbol__fprintf_symname(node->sym, stdout);
+                               if (print_symoffset) {
+                                       al.addr = node->ip;
+                                       symbol__fprintf_symname_offs(node->sym, &al, stdout);
+                               } else
+                                       symbol__fprintf_symname(node->sym, stdout);
                        }
+
                        if (print_dso) {
                                printf(" (");
                                map__fprintf_dsoname(node->map, stdout);
                                printf(")");
                        }
-                       printf("\n");
+
+                       if (!print_oneline)
+                               printf("\n");
 
                        callchain_cursor_advance(&callchain_cursor);
+
+                       stack_depth--;
                }
 
        } else {
-               printf("%16" PRIx64, sample->ip);
+               if (print_ip)
+                       printf("%16" PRIx64, sample->ip);
+
                if (print_sym) {
                        printf(" ");
                        if (print_symoffset)
index f3b235ec7bf445f4beb3f8481255c6786c2d3e35..3aa75fb2225f7129daa12f7e86219f30928e5e42 100644 (file)
@@ -37,11 +37,16 @@ struct perf_session {
        int                     fd;
        bool                    fd_pipe;
        bool                    repipe;
-       char                    *cwd;
        struct ordered_samples  ordered_samples;
        char                    filename[1];
 };
 
+#define PRINT_IP_OPT_IP                (1<<0)
+#define PRINT_IP_OPT_SYM               (1<<1)
+#define PRINT_IP_OPT_DSO               (1<<2)
+#define PRINT_IP_OPT_SYMOFFSET (1<<3)
+#define PRINT_IP_OPT_ONELINE   (1<<4)
+
 struct perf_tool;
 
 struct perf_session *perf_session__new(const char *filename, int mode,
@@ -57,6 +62,11 @@ int __perf_session__process_events(struct perf_session *self,
 int perf_session__process_events(struct perf_session *self,
                                 struct perf_tool *tool);
 
+int perf_session_queue_event(struct perf_session *s, union perf_event *event,
+                            struct perf_sample *sample, u64 file_offset);
+
+void perf_tool__fill_defaults(struct perf_tool *tool);
+
 int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel,
                                    struct thread *thread,
                                    struct ip_callchain *chain,
@@ -99,7 +109,7 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
 
 void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
                          struct perf_sample *sample, struct machine *machine,
-                         int print_sym, int print_dso, int print_symoffset);
+                         unsigned int print_opts, unsigned int stack_depth);
 
 int perf_session__cpu_bitmap(struct perf_session *session,
                             const char *cpu_list, unsigned long *cpu_bitmap);
index 313a5a730112fda631d4286308056bf1bfbddfe2..5f118a089519a46bb6b370536660476bc6880eda 100644 (file)
@@ -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
@@ -872,6 +874,8 @@ static struct sort_dimension common_sort_dimensions[] = {
        DIM(SORT_PARENT, "parent", sort_parent),
        DIM(SORT_CPU, "cpu", sort_cpu),
        DIM(SORT_SRCLINE, "srcline", sort_srcline),
+       DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
+       DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
 };
 
 #undef DIM
@@ -891,8 +895,6 @@ static struct sort_dimension bstack_sort_dimensions[] = {
 #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) }
 
 static struct sort_dimension memory_sort_dimensions[] = {
-       DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
-       DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
        DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym),
        DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso),
        DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked),
index 45ac84c1e037595a9a0070142ef68a9f067aa422..4e80dbd271e77e245d41f2f36afdffdf8ae7ab4e 100644 (file)
@@ -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;
@@ -138,6 +143,8 @@ enum sort_type {
        SORT_PARENT,
        SORT_CPU,
        SORT_SRCLINE,
+       SORT_LOCAL_WEIGHT,
+       SORT_GLOBAL_WEIGHT,
 
        /* branch stack specific sort keys */
        __SORT_BRANCH_STACK,
@@ -149,9 +156,7 @@ enum sort_type {
 
        /* memory mode specific sort keys */
        __SORT_MEMORY_MODE,
-       SORT_LOCAL_WEIGHT = __SORT_MEMORY_MODE,
-       SORT_GLOBAL_WEIGHT,
-       SORT_MEM_DADDR_SYMBOL,
+       SORT_MEM_DADDR_SYMBOL = __SORT_MEMORY_MODE,
        SORT_MEM_DADDR_DSO,
        SORT_MEM_LOCKED,
        SORT_MEM_TLB,
@@ -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 */
index 7c59c28afcc524467f6d1824dccc2442e540c6a6..6506b3dfb6059f71aa6c345df21fcbc16e651604 100644 (file)
@@ -10,6 +10,12 @@ void update_stats(struct stats *stats, u64 val)
        delta = val - stats->mean;
        stats->mean += delta / stats->n;
        stats->M2 += delta*(val - stats->mean);
+
+       if (val > stats->max)
+               stats->max = val;
+
+       if (val < stats->min)
+               stats->min = val;
 }
 
 double avg_stats(struct stats *stats)
index 588367c3c767f2fd6ff7f3f317986756174a2b3c..ae8ccd7227cfa2d3f0a2e74ab06efab814ac3bfd 100644 (file)
@@ -6,6 +6,7 @@
 struct stats
 {
        double n, mean, M2;
+       u64 max, min;
 };
 
 void update_stats(struct stats *stats, u64 val);
@@ -13,4 +14,12 @@ double avg_stats(struct stats *stats);
 double stddev_stats(struct stats *stats);
 double rel_stddev_stats(double stddev, double avg);
 
+static inline void init_stats(struct stats *stats)
+{
+       stats->n    = 0.0;
+       stats->mean = 0.0;
+       stats->M2   = 0.0;
+       stats->min  = (u64) -1;
+       stats->max  = 0;
+}
 #endif
index 29c7b2cb252192b8cb9d184ef320c32265de832e..f0b0c008c5075005b562a2a5a2e938dd2644ea51 100644 (file)
@@ -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;
+}
index 4b12bf85032530686406ecc8b82c1c95cde0d25c..a7b9ab55738086c24d12d7e9ee8cb6143ffd47aa 100644 (file)
@@ -599,11 +599,13 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
        if (dso->kernel == DSO_TYPE_USER) {
                GElf_Shdr shdr;
                ss->adjust_symbols = (ehdr.e_type == ET_EXEC ||
+                               ehdr.e_type == ET_REL ||
                                elf_section_by_name(elf, &ehdr, &shdr,
                                                     ".gnu.prelink_undo",
                                                     NULL) != NULL);
        } else {
-               ss->adjust_symbols = 0;
+               ss->adjust_symbols = ehdr.e_type == ET_EXEC ||
+                                    ehdr.e_type == ET_REL;
        }
 
        ss->name   = strdup(name);
@@ -624,6 +626,37 @@ out_close:
        return err;
 }
 
+/**
+ * ref_reloc_sym_not_found - has kernel relocation symbol been found.
+ * @kmap: kernel maps and relocation reference symbol
+ *
+ * This function returns %true if we are dealing with the kernel maps and the
+ * relocation reference symbol has not yet been found.  Otherwise %false is
+ * returned.
+ */
+static bool ref_reloc_sym_not_found(struct kmap *kmap)
+{
+       return kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
+              !kmap->ref_reloc_sym->unrelocated_addr;
+}
+
+/**
+ * ref_reloc - kernel relocation offset.
+ * @kmap: kernel maps and relocation reference symbol
+ *
+ * This function returns the offset of kernel addresses as determined by using
+ * the relocation reference symbol i.e. if the kernel has not been relocated
+ * then the return value is zero.
+ */
+static u64 ref_reloc(struct kmap *kmap)
+{
+       if (kmap && kmap->ref_reloc_sym &&
+           kmap->ref_reloc_sym->unrelocated_addr)
+               return kmap->ref_reloc_sym->addr -
+                      kmap->ref_reloc_sym->unrelocated_addr;
+       return 0;
+}
+
 int dso__load_sym(struct dso *dso, struct map *map,
                  struct symsrc *syms_ss, struct symsrc *runtime_ss,
                  symbol_filter_t filter, int kmodule)
@@ -642,8 +675,17 @@ int dso__load_sym(struct dso *dso, struct map *map,
        Elf_Scn *sec, *sec_strndx;
        Elf *elf;
        int nr = 0;
+       bool remap_kernel = false, adjust_kernel_syms = false;
 
        dso->symtab_type = syms_ss->type;
+       dso->rel = syms_ss->ehdr.e_type == ET_REL;
+
+       /*
+        * Modules may already have symbols from kallsyms, but those symbols
+        * have the wrong values for the dso maps, so remove them.
+        */
+       if (kmodule && syms_ss->symtab)
+               symbols__delete(&dso->symbols[map->type]);
 
        if (!syms_ss->symtab) {
                syms_ss->symtab  = syms_ss->dynsym;
@@ -681,7 +723,31 @@ int dso__load_sym(struct dso *dso, struct map *map,
        nr_syms = shdr.sh_size / shdr.sh_entsize;
 
        memset(&sym, 0, sizeof(sym));
-       dso->adjust_symbols = runtime_ss->adjust_symbols;
+
+       /*
+        * The kernel relocation symbol is needed in advance in order to adjust
+        * kernel maps correctly.
+        */
+       if (ref_reloc_sym_not_found(kmap)) {
+               elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
+                       const char *elf_name = elf_sym__name(&sym, symstrs);
+
+                       if (strcmp(elf_name, kmap->ref_reloc_sym->name))
+                               continue;
+                       kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
+                       break;
+               }
+       }
+
+       dso->adjust_symbols = runtime_ss->adjust_symbols || ref_reloc(kmap);
+       /*
+        * Initial kernel and module mappings do not map to the dso.  For
+        * function mappings, flag the fixups.
+        */
+       if (map->type == MAP__FUNCTION && (dso->kernel || kmodule)) {
+               remap_kernel = true;
+               adjust_kernel_syms = dso->adjust_symbols;
+       }
        elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
                struct symbol *f;
                const char *elf_name = elf_sym__name(&sym, symstrs);
@@ -690,10 +756,6 @@ int dso__load_sym(struct dso *dso, struct map *map,
                const char *section_name;
                bool used_opd = false;
 
-               if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
-                   strcmp(elf_name, kmap->ref_reloc_sym->name) == 0)
-                       kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
-
                if (!is_label && !elf_sym__is_a(&sym, map->type))
                        continue;
 
@@ -745,20 +807,55 @@ int dso__load_sym(struct dso *dso, struct map *map,
                    (sym.st_value & 1))
                        --sym.st_value;
 
-               if (dso->kernel != DSO_TYPE_USER || kmodule) {
+               if (dso->kernel || kmodule) {
                        char dso_name[PATH_MAX];
 
+                       /* Adjust symbol to map to file offset */
+                       if (adjust_kernel_syms)
+                               sym.st_value -= shdr.sh_addr - shdr.sh_offset;
+
                        if (strcmp(section_name,
                                   (curr_dso->short_name +
                                    dso->short_name_len)) == 0)
                                goto new_symbol;
 
                        if (strcmp(section_name, ".text") == 0) {
+                               /*
+                                * The initial kernel mapping is based on
+                                * kallsyms and identity maps.  Overwrite it to
+                                * map to the kernel dso.
+                                */
+                               if (remap_kernel && dso->kernel) {
+                                       remap_kernel = false;
+                                       map->start = shdr.sh_addr +
+                                                    ref_reloc(kmap);
+                                       map->end = map->start + shdr.sh_size;
+                                       map->pgoff = shdr.sh_offset;
+                                       map->map_ip = map__map_ip;
+                                       map->unmap_ip = map__unmap_ip;
+                                       /* Ensure maps are correctly ordered */
+                                       map_groups__remove(kmap->kmaps, map);
+                                       map_groups__insert(kmap->kmaps, map);
+                               }
+
+                               /*
+                                * The initial module mapping is based on
+                                * /proc/modules mapped to offset zero.
+                                * Overwrite it to map to the module dso.
+                                */
+                               if (remap_kernel && kmodule) {
+                                       remap_kernel = false;
+                                       map->pgoff = shdr.sh_offset;
+                               }
+
                                curr_map = map;
                                curr_dso = dso;
                                goto new_symbol;
                        }
 
+                       if (!kmap)
+                               goto new_symbol;
+
                        snprintf(dso_name, sizeof(dso_name),
                                 "%s%s", dso->short_name, section_name);
 
@@ -781,8 +878,16 @@ int dso__load_sym(struct dso *dso, struct map *map,
                                        dso__delete(curr_dso);
                                        goto out_elf_end;
                                }
-                               curr_map->map_ip = identity__map_ip;
-                               curr_map->unmap_ip = identity__map_ip;
+                               if (adjust_kernel_syms) {
+                                       curr_map->start = shdr.sh_addr +
+                                                         ref_reloc(kmap);
+                                       curr_map->end = curr_map->start +
+                                                       shdr.sh_size;
+                                       curr_map->pgoff = shdr.sh_offset;
+                               } else {
+                                       curr_map->map_ip = identity__map_ip;
+                                       curr_map->unmap_ip = identity__map_ip;
+                               }
                                curr_dso->symtab_type = dso->symtab_type;
                                map_groups__insert(kmap->kmaps, curr_map);
                                dsos__add(&dso->node, curr_dso);
@@ -846,6 +951,57 @@ out_elf_end:
        return err;
 }
 
+static int elf_read_maps(Elf *elf, bool exe, mapfn_t mapfn, void *data)
+{
+       GElf_Phdr phdr;
+       size_t i, phdrnum;
+       int err;
+       u64 sz;
+
+       if (elf_getphdrnum(elf, &phdrnum))
+               return -1;
+
+       for (i = 0; i < phdrnum; i++) {
+               if (gelf_getphdr(elf, i, &phdr) == NULL)
+                       return -1;
+               if (phdr.p_type != PT_LOAD)
+                       continue;
+               if (exe) {
+                       if (!(phdr.p_flags & PF_X))
+                               continue;
+               } else {
+                       if (!(phdr.p_flags & PF_R))
+                               continue;
+               }
+               sz = min(phdr.p_memsz, phdr.p_filesz);
+               if (!sz)
+                       continue;
+               err = mapfn(phdr.p_vaddr, sz, phdr.p_offset, data);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
+                   bool *is_64_bit)
+{
+       int err;
+       Elf *elf;
+
+       elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+       if (elf == NULL)
+               return -1;
+
+       if (is_64_bit)
+               *is_64_bit = (gelf_getclass(elf) == ELFCLASS64);
+
+       err = elf_read_maps(elf, exe, mapfn, data);
+
+       elf_end(elf);
+       return err;
+}
+
 void symbol__elf_init(void)
 {
        elf_version(EV_CURRENT);
index a7390cde63bc74f8dae01422dd92bbc2e1a09d3c..3a802c300fc564adce713aebbd848a1b936e78c1 100644 (file)
@@ -301,6 +301,13 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused,
        return 0;
 }
 
+int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused,
+                   mapfn_t mapfn __maybe_unused, void *data __maybe_unused,
+                   bool *is_64_bit __maybe_unused)
+{
+       return -1;
+}
+
 void symbol__elf_init(void)
 {
 }
index d5528e1cc03a0d72a3676597897bdfd6bb6a811e..77f3b95bb46df07a0a3f2e1fbf307a741a584c72 100644 (file)
@@ -87,6 +87,7 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
 {
        s64 a;
        s64 b;
+       size_t na, nb;
 
        /* Prefer a symbol with non zero length */
        a = syma->end - syma->start;
@@ -120,11 +121,21 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
        else if (a > b)
                return SYMBOL_B;
 
-       /* If all else fails, choose the symbol with the longest name */
-       if (strlen(syma->name) >= strlen(symb->name))
+       /* Choose the symbol with the longest name */
+       na = strlen(syma->name);
+       nb = strlen(symb->name);
+       if (na > nb)
                return SYMBOL_A;
-       else
+       else if (na < nb)
+               return SYMBOL_B;
+
+       /* Avoid "SyS" kernel syscall aliases */
+       if (na >= 3 && !strncmp(syma->name, "SyS", 3))
+               return SYMBOL_B;
+       if (na >= 10 && !strncmp(syma->name, "compat_SyS", 10))
                return SYMBOL_B;
+
+       return SYMBOL_A;
 }
 
 void symbols__fixup_duplicate(struct rb_root *symbols)
@@ -316,6 +327,16 @@ static struct symbol *symbols__find(struct rb_root *symbols, u64 ip)
        return NULL;
 }
 
+static struct symbol *symbols__first(struct rb_root *symbols)
+{
+       struct rb_node *n = rb_first(symbols);
+
+       if (n)
+               return rb_entry(n, struct symbol, rb_node);
+
+       return NULL;
+}
+
 struct symbol_name_rb_node {
        struct rb_node  rb_node;
        struct symbol   sym;
@@ -386,6 +407,11 @@ struct symbol *dso__find_symbol(struct dso *dso,
        return symbols__find(&dso->symbols[type], addr);
 }
 
+struct symbol *dso__first_symbol(struct dso *dso, enum map_type type)
+{
+       return symbols__first(&dso->symbols[type]);
+}
+
 struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,
                                        const char *name)
 {
@@ -522,6 +548,53 @@ static int dso__load_all_kallsyms(struct dso *dso, const char *filename,
        return kallsyms__parse(filename, &args, map__process_kallsym_symbol);
 }
 
+static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map,
+                                        symbol_filter_t filter)
+{
+       struct map_groups *kmaps = map__kmap(map)->kmaps;
+       struct map *curr_map;
+       struct symbol *pos;
+       int count = 0, moved = 0;
+       struct rb_root *root = &dso->symbols[map->type];
+       struct rb_node *next = rb_first(root);
+
+       while (next) {
+               char *module;
+
+               pos = rb_entry(next, struct symbol, rb_node);
+               next = rb_next(&pos->rb_node);
+
+               module = strchr(pos->name, '\t');
+               if (module)
+                       *module = '\0';
+
+               curr_map = map_groups__find(kmaps, map->type, pos->start);
+
+               if (!curr_map || (filter && filter(curr_map, pos))) {
+                       rb_erase(&pos->rb_node, root);
+                       symbol__delete(pos);
+               } else {
+                       pos->start -= curr_map->start - curr_map->pgoff;
+                       if (pos->end)
+                               pos->end -= curr_map->start - curr_map->pgoff;
+                       if (curr_map != map) {
+                               rb_erase(&pos->rb_node, root);
+                               symbols__insert(
+                                       &curr_map->dso->symbols[curr_map->type],
+                                       pos);
+                               ++moved;
+                       } else {
+                               ++count;
+                       }
+               }
+       }
+
+       /* Symbols have been adjusted */
+       dso->adjust_symbols = 1;
+
+       return count + moved;
+}
+
 /*
  * Split the symbols into maps, making sure there are no overlaps, i.e. the
  * kernel range is broken in several maps, named [kernel].N, as we don't have
@@ -663,6 +736,161 @@ bool symbol__restricted_filename(const char *filename,
        return restricted;
 }
 
+struct kcore_mapfn_data {
+       struct dso *dso;
+       enum map_type type;
+       struct list_head maps;
+};
+
+static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data)
+{
+       struct kcore_mapfn_data *md = data;
+       struct map *map;
+
+       map = map__new2(start, md->dso, md->type);
+       if (map == NULL)
+               return -ENOMEM;
+
+       map->end = map->start + len;
+       map->pgoff = pgoff;
+
+       list_add(&map->node, &md->maps);
+
+       return 0;
+}
+
+/*
+ * If kallsyms is referenced by name then we look for kcore in the same
+ * directory.
+ */
+static bool kcore_filename_from_kallsyms_filename(char *kcore_filename,
+                                                 const char *kallsyms_filename)
+{
+       char *name;
+
+       strcpy(kcore_filename, kallsyms_filename);
+       name = strrchr(kcore_filename, '/');
+       if (!name)
+               return false;
+
+       if (!strcmp(name, "/kallsyms")) {
+               strcpy(name, "/kcore");
+               return true;
+       }
+
+       return false;
+}
+
+static int dso__load_kcore(struct dso *dso, struct map *map,
+                          const char *kallsyms_filename)
+{
+       struct map_groups *kmaps = map__kmap(map)->kmaps;
+       struct machine *machine = kmaps->machine;
+       struct kcore_mapfn_data md;
+       struct map *old_map, *new_map, *replacement_map = NULL;
+       bool is_64_bit;
+       int err, fd;
+       char kcore_filename[PATH_MAX];
+       struct symbol *sym;
+
+       /* This function requires that the map is the kernel map */
+       if (map != machine->vmlinux_maps[map->type])
+               return -EINVAL;
+
+       if (!kcore_filename_from_kallsyms_filename(kcore_filename,
+                                                  kallsyms_filename))
+               return -EINVAL;
+
+       md.dso = dso;
+       md.type = map->type;
+       INIT_LIST_HEAD(&md.maps);
+
+       fd = open(kcore_filename, O_RDONLY);
+       if (fd < 0)
+               return -EINVAL;
+
+       /* Read new maps into temporary lists */
+       err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md,
+                             &is_64_bit);
+       if (err)
+               goto out_err;
+
+       if (list_empty(&md.maps)) {
+               err = -EINVAL;
+               goto out_err;
+       }
+
+       /* Remove old maps */
+       old_map = map_groups__first(kmaps, map->type);
+       while (old_map) {
+               struct map *next = map_groups__next(old_map);
+
+               if (old_map != map)
+                       map_groups__remove(kmaps, old_map);
+               old_map = next;
+       }
+
+       /* Find the kernel map using the first symbol */
+       sym = dso__first_symbol(dso, map->type);
+       list_for_each_entry(new_map, &md.maps, node) {
+               if (sym && sym->start >= new_map->start &&
+                   sym->start < new_map->end) {
+                       replacement_map = new_map;
+                       break;
+               }
+       }
+
+       if (!replacement_map)
+               replacement_map = list_entry(md.maps.next, struct map, node);
+
+       /* Add new maps */
+       while (!list_empty(&md.maps)) {
+               new_map = list_entry(md.maps.next, struct map, node);
+               list_del(&new_map->node);
+               if (new_map == replacement_map) {
+                       map->start      = new_map->start;
+                       map->end        = new_map->end;
+                       map->pgoff      = new_map->pgoff;
+                       map->map_ip     = new_map->map_ip;
+                       map->unmap_ip   = new_map->unmap_ip;
+                       map__delete(new_map);
+                       /* Ensure maps are correctly ordered */
+                       map_groups__remove(kmaps, map);
+                       map_groups__insert(kmaps, map);
+               } else {
+                       map_groups__insert(kmaps, new_map);
+               }
+       }
+
+       /*
+        * Set the data type and long name so that kcore can be read via
+        * dso__data_read_addr().
+        */
+       if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
+               dso->data_type = DSO_BINARY_TYPE__GUEST_KCORE;
+       else
+               dso->data_type = DSO_BINARY_TYPE__KCORE;
+       dso__set_long_name(dso, strdup(kcore_filename));
+
+       close(fd);
+
+       if (map->type == MAP__FUNCTION)
+               pr_debug("Using %s for kernel object code\n", kcore_filename);
+       else
+               pr_debug("Using %s for kernel data\n", kcore_filename);
+
+       return 0;
+
+out_err:
+       while (!list_empty(&md.maps)) {
+               map = list_entry(md.maps.next, struct map, node);
+               list_del(&map->node);
+               map__delete(map);
+       }
+       close(fd);
+       return -EINVAL;
+}
+
 int dso__load_kallsyms(struct dso *dso, const char *filename,
                       struct map *map, symbol_filter_t filter)
 {
@@ -680,7 +908,10 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,
        else
                dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS;
 
-       return dso__split_kallsyms(dso, map, filter);
+       if (!dso__load_kcore(dso, map, filename))
+               return dso__split_kallsyms_for_kcore(dso, map, filter);
+       else
+               return dso__split_kallsyms(dso, map, filter);
 }
 
 static int dso__load_perf_map(struct dso *dso, struct map *map,
@@ -843,10 +1074,15 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
        if (!runtime_ss && syms_ss)
                runtime_ss = syms_ss;
 
-       if (syms_ss)
-               ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, 0);
-       else
+       if (syms_ss) {
+               int km;
+
+               km = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE ||
+                    dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE;
+               ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, km);
+       } else {
                ret = -1;
+       }
 
        if (ret > 0) {
                int nr_plt;
@@ -888,8 +1124,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;
@@ -903,6 +1142,10 @@ int dso__load_vmlinux(struct dso *dso, struct map *map,
        symsrc__destroy(&ss);
 
        if (err > 0) {
+               if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
+                       dso->data_type = DSO_BINARY_TYPE__GUEST_VMLINUX;
+               else
+                       dso->data_type = DSO_BINARY_TYPE__VMLINUX;
                dso__set_long_name(dso, (char *)vmlinux);
                dso__set_loaded(dso, map->type);
                pr_debug("Using %s for symbols\n", symfs_vmlinux);
@@ -975,7 +1218,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,
                        dso__set_long_name(dso,
                                           strdup(symbol_conf.vmlinux_name));
                        dso->lname_alloc = 1;
-                       goto out_fixup;
+                       return err;
                }
                return err;
        }
@@ -983,7 +1226,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map,
        if (vmlinux_path != NULL) {
                err = dso__load_vmlinux_path(dso, map, filter);
                if (err > 0)
-                       goto out_fixup;
+                       return err;
        }
 
        /* do not try local files if a symfs was given */
@@ -1042,9 +1285,8 @@ do_kallsyms:
                pr_debug("Using %s for symbols\n", kallsyms_filename);
        free(kallsyms_allocated_filename);
 
-       if (err > 0) {
+       if (err > 0 && !dso__is_kcore(dso)) {
                dso__set_long_name(dso, strdup("[kernel.kallsyms]"));
-out_fixup:
                map__fixup_start(map);
                map__fixup_end(map);
        }
@@ -1075,7 +1317,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,
                if (symbol_conf.default_guest_vmlinux_name != NULL) {
                        err = dso__load_vmlinux(dso, map,
                                symbol_conf.default_guest_vmlinux_name, filter);
-                       goto out_try_fixup;
+                       return err;
                }
 
                kallsyms_filename = symbol_conf.default_guest_kallsyms;
@@ -1089,13 +1331,9 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,
        err = dso__load_kallsyms(dso, kallsyms_filename, map, filter);
        if (err > 0)
                pr_debug("Using %s for symbols\n", kallsyms_filename);
-
-out_try_fixup:
-       if (err > 0) {
-               if (kallsyms_filename != NULL) {
-                       machine__mmap_name(machine, path, sizeof(path));
-                       dso__set_long_name(dso, strdup(path));
-               }
+       if (err > 0 && !dso__is_kcore(dso)) {
+               machine__mmap_name(machine, path, sizeof(path));
+               dso__set_long_name(dso, strdup(path));
                map__fixup_start(map);
                map__fixup_end(map);
        }
index 5f720dc076da66788f0b8cee30d43f3f17b3ff40..fd5b70ea29812334572fc7a6f5fad2c3ec7058e7 100644 (file)
@@ -215,6 +215,7 @@ struct symbol *dso__find_symbol(struct dso *dso, enum map_type type,
                                u64 addr);
 struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type,
                                        const char *name);
+struct symbol *dso__first_symbol(struct dso *dso, enum map_type type);
 
 int filename__read_build_id(const char *filename, void *bf, size_t size);
 int sysfs__read_build_id(const char *filename, void *bf, size_t size);
@@ -247,4 +248,8 @@ void symbols__fixup_duplicate(struct rb_root *symbols);
 void symbols__fixup_end(struct rb_root *symbols);
 void __map_groups__fixup_end(struct map_groups *mg, enum map_type type);
 
+typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data);
+int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data,
+                   bool *is_64_bit);
+
 #endif /* __PERF_SYMBOL */
index 40399cbcca775c253b0c691fca69346af40fe7f3..6feeb88eb5b0165f86c535601f0250553df4aaba 100644 (file)
@@ -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;
 }
index eeb7ac62b9e3ce99ad796a62a628010dd2dce927..13c62c9093920a69b3786d60a04778c24374c037 100644 (file)
@@ -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);
@@ -45,6 +45,15 @@ void thread__find_addr_map(struct thread *thread, struct machine *machine,
 
 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);
+                               struct addr_location *al);
+
+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 */
index b0e1aadba8d5b3dcfa044668baa46918dff83e5d..62b16b6165bafae5fd80cb79d1670628f1d81847 100644 (file)
@@ -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;
index df46be93d9022cbe1a4e12840f31e1763e9c014f..b554ffc462b653e73b7e8de84cfa53e2609be989 100644 (file)
@@ -39,6 +39,8 @@ struct perf_top {
        float              min_percent;
 };
 
+#define CONSOLE_CLEAR "\e[H\e[2J"
+
 size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size);
 void perf_top__reset_sample_counters(struct perf_top *top);
 #endif /* __PERF_TOP_H */
index 3917eb9a8479f7a4b11e6bd1f6e39f146f159523..f3c9e551bd353f39cd68bf52ec4c1c4ea6892a2f 100644 (file)
 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:
        /*
index 4454835a9ebccd5d826d3f054d8d3f1a25ffc109..fe7a27d67d2b7af23a596683509769a4f1bd6e38 100644 (file)
 #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();
index af215c0d2379bb1440e9bd43fd19ca09f0e6d50b..f2112270c663d1c09f201272b7d348e5f6cf220d 100644 (file)
 
 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;
-
-       if (do_read(header_event, size) < 0)
-               ret = -1;
+       skip(size);
 
-       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;
index 8715a1006d004b2c12e21d386b34ffae0165e17d..95199e4eea978c3961a86e5e2439e61b6a3ef8a1 100644 (file)
@@ -39,7 +39,8 @@ static void process_event_unsupported(union perf_event *event __maybe_unused,
                                      struct perf_sample *sample __maybe_unused,
                                      struct perf_evsel *evsel __maybe_unused,
                                      struct machine *machine __maybe_unused,
-                                     struct addr_location *al __maybe_unused)
+                                     struct thread *thread __maybe_unused,
+                                         struct addr_location *al __maybe_unused)
 {
 }
 
index 1978c398ad8745f873cbfd2ab732315bbd34e26a..fafe1a40444a2b0785e4d41b048ee0926786fcf9 100644 (file)
@@ -1,32 +1,18 @@
 #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;
 struct perf_sample;
 union perf_event;
 struct perf_tool;
+struct thread;
 
-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);
@@ -83,7 +69,8 @@ struct scripting_ops {
                               struct perf_sample *sample,
                               struct perf_evsel *evsel,
                               struct machine *machine,
-                              struct addr_location *al);
+                              struct thread *thread,
+                                  struct addr_location *al);
        int (*generate_script) (struct pevent *pevent, const char *outfile);
 };
 
index 958723ba3d2efaa3978a4870d6623f6e7feef601..2f891f7e70bf9251c849780ec008ded14a2776c9 100644 (file)
@@ -473,7 +473,7 @@ static int entry(u64 ip, struct thread *thread, struct machine *machine,
 
        thread__find_addr_location(thread, machine,
                                   PERF_RECORD_MISC_USER,
-                                  MAP__FUNCTION, ip, &al, NULL);
+                                  MAP__FUNCTION, ip, &al);
 
        e.ip = ip;
        e.map = al.map;
index 59d868add275d4cc5d23800c691f7e4e758a9a3d..6d17b18e915d56b47e02d16c4c27cae2a85764db 100644 (file)
@@ -269,3 +269,95 @@ 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);
+}
+
+int parse_nsec_time(const char *str, u64 *ptime)
+{
+       u64 time_sec, time_nsec;
+       char *end;
+
+       time_sec = strtoul(str, &end, 10);
+       if (*end != '.' && *end != '\0')
+               return -1;
+
+       if (*end == '.') {
+               int i;
+               char nsec_buf[10];
+
+               if (strlen(++end) > 9)
+                       return -1;
+
+               strncpy(nsec_buf, end, 9);
+               nsec_buf[9] = '\0';
+
+               /* make it nsec precision */
+               for (i = strlen(nsec_buf); i < 9; i++)
+                       nsec_buf[i] = '0';
+
+               time_nsec = strtoul(nsec_buf, &end, 10);
+               if (*end != '\0')
+                       return -1;
+       } else
+               time_nsec = 0;
+
+       *ptime = time_sec * NSEC_PER_SEC + time_nsec;
+       return 0;
+}
index 2732fad039088ab97369bbe6e727b950ef6a0dcb..a53535949043f32654a181c3728a421c52baf276 100644 (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).
@@ -205,6 +208,8 @@ static inline int has_extension(const char *filename, const char *ext)
 #define NSEC_PER_MSEC  1000000L
 #endif
 
+int parse_nsec_time(const char *str, u64 *ptime);
+
 extern unsigned char sane_ctype[256];
 #define GIT_SPACE              0x01
 #define GIT_DIGIT              0x02