]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge branch 'perf/urgent' into perf/core
authorIngo Molnar <mingo@elte.hu>
Tue, 6 Dec 2011 05:42:35 +0000 (06:42 +0100)
committerIngo Molnar <mingo@elte.hu>
Tue, 6 Dec 2011 05:43:49 +0000 (06:43 +0100)
Merge reason: Add these cherry-picked commits so that future changes
              on perf/core don't conflict.

Signed-off-by: Ingo Molnar <mingo@elte.hu>
87 files changed:
Documentation/kernel-parameters.txt
Documentation/trace/events.txt
arch/Kconfig
arch/x86/include/asm/insn.h
arch/x86/lib/inat.c
arch/x86/lib/insn.c
arch/x86/lib/x86-opcode-map.txt
arch/x86/oprofile/Makefile
arch/x86/oprofile/init.c
arch/x86/oprofile/nmi_int.c
arch/x86/oprofile/nmi_timer_int.c [deleted file]
arch/x86/tools/Makefile
arch/x86/tools/gen-insn-attr-x86.awk
arch/x86/tools/insn_sanity.c [new file with mode: 0644]
drivers/oprofile/nmi_timer_int.c [new file with mode: 0644]
drivers/oprofile/oprof.c
drivers/oprofile/oprof.h
drivers/oprofile/timer_int.c
kernel/events/Makefile
kernel/events/callchain.c [new file with mode: 0644]
kernel/events/core.c
kernel/events/internal.h
kernel/lockdep.c
kernel/trace/trace.c
kernel/trace/trace.h
kernel/trace/trace_events_filter.c
kernel/trace/trace_irqsoff.c
kernel/trace/trace_sched_wakeup.c
tools/perf/Documentation/perf-annotate.txt
tools/perf/Documentation/perf-report.txt
tools/perf/Documentation/perf-script.txt
tools/perf/Documentation/perf-test.txt
tools/perf/Makefile
tools/perf/builtin-annotate.c
tools/perf/builtin-diff.c
tools/perf/builtin-inject.c
tools/perf/builtin-kmem.c
tools/perf/builtin-lock.c
tools/perf/builtin-probe.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-test.c
tools/perf/builtin-timechart.c
tools/perf/builtin-top.c
tools/perf/perf.c
tools/perf/perf.h
tools/perf/util/annotate.c
tools/perf/util/annotate.h
tools/perf/util/build-id.c
tools/perf/util/build-id.h
tools/perf/util/callchain.h
tools/perf/util/cgroup.c
tools/perf/util/debugfs.c
tools/perf/util/debugfs.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.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/probe-finder.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/setup.py
tools/perf/util/symbol.h
tools/perf/util/thread.c
tools/perf/util/thread.h
tools/perf/util/tool.h [new file with mode: 0644]
tools/perf/util/top.h
tools/perf/util/trace-event-info.c
tools/perf/util/trace-event-scripting.c
tools/perf/util/trace-event.h
tools/perf/util/ui/browsers/annotate.c
tools/perf/util/ui/browsers/hists.c
tools/perf/util/ui/progress.c

index a0c5c5f4fce6e9587346a4a049c9725e5ca45de5..fd5c913c33c14ee26dc1ff48ed9d7b5af8ddc0b2 100644 (file)
@@ -1885,6 +1885,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        arch_perfmon: [X86] Force use of architectural
                                perfmon on Intel CPUs instead of the
                                CPU specific event set.
+                       timer: [X86] Force use of architectural NMI
+                               timer mode (see also oprofile.timer
+                               for generic hr timer mode)
 
        oops=panic      Always panic on oopses. Default is to just kill the
                        process, but there is a small probability of
index b510564aac7ebcc22ab036c7662392d0006000d7..bb24c2a0e870dc873ef8e8250f77d44d76a017b9 100644 (file)
@@ -191,8 +191,6 @@ And for string fields they are:
 
 Currently, only exact string matches are supported.
 
-Currently, the maximum number of predicates in a filter is 16.
-
 5.2 Setting filters
 -------------------
 
index 4b0669cbb3b01d4c0eb66f364859cb6f65654693..2505740b81d2e24696ff4420f2f9af86713b817b 100644 (file)
@@ -30,6 +30,10 @@ config OPROFILE_EVENT_MULTIPLEX
 config HAVE_OPROFILE
        bool
 
+config OPROFILE_NMI_TIMER
+       def_bool y
+       depends on PERF_EVENTS && HAVE_PERF_EVENTS_NMI
+
 config KPROBES
        bool "Kprobes"
        depends on MODULES
index 88c765e16410beecf7be2bfdad19b0cf4918ec46..74df3f1eddfd5403f68dc1ef83028348a6f4a9e6 100644 (file)
@@ -137,6 +137,13 @@ static inline int insn_is_avx(struct insn *insn)
        return (insn->vex_prefix.value != 0);
 }
 
+/* Ensure this instruction is decoded completely */
+static inline int insn_complete(struct insn *insn)
+{
+       return insn->opcode.got && insn->modrm.got && insn->sib.got &&
+               insn->displacement.got && insn->immediate.got;
+}
+
 static inline insn_byte_t insn_vex_m_bits(struct insn *insn)
 {
        if (insn->vex_prefix.nbytes == 2)       /* 2 bytes VEX */
index 46fc4ee09fc490ecbb6a0d312b524fc92ff3e81b..88ad5fbda6e189a5021f58cac5371db26ff7c1f7 100644 (file)
@@ -82,9 +82,16 @@ insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m,
        const insn_attr_t *table;
        if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX)
                return 0;
-       table = inat_avx_tables[vex_m][vex_p];
+       /* At first, this checks the master table */
+       table = inat_avx_tables[vex_m][0];
        if (!table)
                return 0;
+       if (!inat_is_group(table[opcode]) && vex_p) {
+               /* If this is not a group, get attribute directly */
+               table = inat_avx_tables[vex_m][vex_p];
+               if (!table)
+                       return 0;
+       }
        return table[opcode];
 }
 
index 374562ed67048c7d2dd72231f7933e0f0cb109ad..5a1f9f3e3fbbb67873b55daa98afabb8589e54f1 100644 (file)
@@ -202,7 +202,7 @@ void insn_get_opcode(struct insn *insn)
                m = insn_vex_m_bits(insn);
                p = insn_vex_p_bits(insn);
                insn->attr = inat_get_avx_attribute(op, m, p);
-               if (!inat_accept_vex(insn->attr))
+               if (!inat_accept_vex(insn->attr) && !inat_is_group(insn->attr))
                        insn->attr = 0; /* This instruction is bad */
                goto end;       /* VEX has only 1 byte for opcode */
        }
@@ -249,6 +249,8 @@ void insn_get_modrm(struct insn *insn)
                        pfx = insn_last_prefix(insn);
                        insn->attr = inat_get_group_attribute(mod, pfx,
                                                              insn->attr);
+                       if (insn_is_avx(insn) && !inat_accept_vex(insn->attr))
+                               insn->attr = 0; /* This is bad */
                }
        }
 
index a793da5e560e3c88804850e957d00a6024b4eb65..5b83c51c12e02e8146aba31c0236ff1e7d42d4b0 100644 (file)
@@ -1,5 +1,11 @@
 # x86 Opcode Maps
 #
+# This is (mostly) based on following documentations.
+# - Intel(R) 64 and IA-32 Architectures Software Developer's Manual Vol.2
+#   (#325383-040US, October 2011)
+# - Intel(R) Advanced Vector Extensions Programming Reference
+#   (#319433-011,JUNE 2011).
+#
 #<Opcode maps>
 # Table: table-name
 # Referrer: escaped-name
 # EndTable
 #
 # AVX Superscripts
-#  (VEX): this opcode can accept VEX prefix.
-#  (oVEX): this opcode requires VEX prefix.
-#  (o128): this opcode only supports 128bit VEX.
-#  (o256): this opcode only supports 256bit VEX.
+#  (v): this opcode requires VEX prefix.
+#  (v1): this opcode only supports 128bit VEX.
+#
+# Last Prefix Superscripts
+#  - (66): the last prefix is 0x66
+#  - (F3): the last prefix is 0xF3
+#  - (F2): the last prefix is 0xF2
 #
 
 Table: one byte opcode
@@ -199,8 +208,8 @@ a0: MOV AL,Ob
 a1: MOV rAX,Ov
 a2: MOV Ob,AL
 a3: MOV Ov,rAX
-a4: MOVS/B Xb,Yb
-a5: MOVS/W/D/Q Xv,Yv
+a4: MOVS/B Yb,Xb
+a5: MOVS/W/D/Q Yv,Xv
 a6: CMPS/B Xb,Yb
 a7: CMPS/W/D Xv,Yv
 a8: TEST AL,Ib
@@ -233,8 +242,8 @@ c0: Grp2 Eb,Ib (1A)
 c1: Grp2 Ev,Ib (1A)
 c2: RETN Iw (f64)
 c3: RETN
-c4: LES Gz,Mp (i64) | 3bytes-VEX (Prefix)
-c5: LDS Gz,Mp (i64) | 2bytes-VEX (Prefix)
+c4: LES Gz,Mp (i64) | VEX+2byte (Prefix)
+c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix)
 c6: Grp11 Eb,Ib (1A)
 c7: Grp11 Ev,Iz (1A)
 c8: ENTER Iw,Ib
@@ -320,14 +329,19 @@ AVXcode: 1
 # 3DNow! uses the last imm byte as opcode extension.
 0f: 3DNow! Pq,Qq,Ib
 # 0x0f 0x10-0x1f
-10: movups Vps,Wps (VEX) | movss Vss,Wss (F3),(VEX),(o128) | movupd Vpd,Wpd (66),(VEX) | movsd Vsd,Wsd (F2),(VEX),(o128)
-11: movups Wps,Vps (VEX) | movss Wss,Vss (F3),(VEX),(o128) | movupd Wpd,Vpd (66),(VEX) | movsd Wsd,Vsd (F2),(VEX),(o128)
-12: movlps Vq,Mq (VEX),(o128) | movlpd Vq,Mq (66),(VEX),(o128) | movhlps Vq,Uq (VEX),(o128) | movddup Vq,Wq (F2),(VEX) | movsldup Vq,Wq (F3),(VEX)
-13: mpvlps Mq,Vq (VEX),(o128) | movlpd Mq,Vq (66),(VEX),(o128)
-14: unpcklps Vps,Wq (VEX) | unpcklpd Vpd,Wq (66),(VEX)
-15: unpckhps Vps,Wq (VEX) | unpckhpd Vpd,Wq (66),(VEX)
-16: movhps Vq,Mq (VEX),(o128) | movhpd Vq,Mq (66),(VEX),(o128) | movlsps Vq,Uq (VEX),(o128) | movshdup Vq,Wq (F3),(VEX)
-17: movhps Mq,Vq (VEX),(o128) | movhpd Mq,Vq (66),(VEX),(o128)
+# NOTE: According to Intel SDM opcode map, vmovups and vmovupd has no operands
+# but it actually has operands. And also, vmovss and vmovsd only accept 128bit.
+# MOVSS/MOVSD has too many forms(3) on SDM. This map just shows a typical form.
+# Many AVX instructions lack v1 superscript, according to Intel AVX-Prgramming
+# Reference A.1
+10: vmovups Vps,Wps | vmovupd Vpd,Wpd (66) | vmovss Vx,Hx,Wss (F3),(v1) | vmovsd Vx,Hx,Wsd (F2),(v1)
+11: vmovups Wps,Vps | vmovupd Wpd,Vpd (66) | vmovss Wss,Hx,Vss (F3),(v1) | vmovsd Wsd,Hx,Vsd (F2),(v1)
+12: vmovlps Vq,Hq,Mq (v1) | vmovhlps Vq,Hq,Uq (v1) | vmovlpd Vq,Hq,Mq (66),(v1) | vmovsldup Vx,Wx (F3) | vmovddup Vx,Wx (F2)
+13: vmovlps Mq,Vq (v1) | vmovlpd Mq,Vq (66),(v1)
+14: vunpcklps Vx,Hx,Wx | vunpcklpd Vx,Hx,Wx (66)
+15: vunpckhps Vx,Hx,Wx | vunpckhpd Vx,Hx,Wx (66)
+16: vmovhps Vdq,Hq,Mq (v1) | vmovlhps Vdq,Hq,Uq (v1) | vmovhpd Vdq,Hq,Mq (66),(v1) | vmovshdup Vx,Wx (F3)
+17: vmovhps Mq,Vq (v1) | vmovhpd Mq,Vq (66),(v1)
 18: Grp16 (1A)
 19:
 1a:
@@ -345,14 +359,14 @@ AVXcode: 1
 25:
 26:
 27:
-28: movaps Vps,Wps (VEX) | movapd Vpd,Wpd (66),(VEX)
-29: movaps Wps,Vps (VEX) | movapd Wpd,Vpd (66),(VEX)
-2a: cvtpi2ps Vps,Qpi | cvtsi2ss Vss,Ed/q (F3),(VEX),(o128) | cvtpi2pd Vpd,Qpi (66) | cvtsi2sd Vsd,Ed/q (F2),(VEX),(o128)
-2b: movntps Mps,Vps (VEX) | movntpd Mpd,Vpd (66),(VEX)
-2c: cvttps2pi Ppi,Wps | cvttss2si  Gd/q,Wss (F3),(VEX),(o128) | cvttpd2pi Ppi,Wpd (66) | cvttsd2si Gd/q,Wsd (F2),(VEX),(o128)
-2d: cvtps2pi Ppi,Wps | cvtss2si Gd/q,Wss (F3),(VEX),(o128) | cvtpd2pi Qpi,Wpd (66) | cvtsd2si Gd/q,Wsd (F2),(VEX),(o128)
-2e: ucomiss Vss,Wss (VEX),(o128) | ucomisd  Vsd,Wsd (66),(VEX),(o128)
-2f: comiss Vss,Wss (VEX),(o128) | comisd  Vsd,Wsd (66),(VEX),(o128)
+28: vmovaps Vps,Wps | vmovapd Vpd,Wpd (66)
+29: vmovaps Wps,Vps | vmovapd Wpd,Vpd (66)
+2a: cvtpi2ps Vps,Qpi | cvtpi2pd Vpd,Qpi (66) | vcvtsi2ss Vss,Hss,Ey (F3),(v1) | vcvtsi2sd Vsd,Hsd,Ey (F2),(v1)
+2b: vmovntps Mps,Vps | vmovntpd Mpd,Vpd (66)
+2c: cvttps2pi Ppi,Wps | cvttpd2pi Ppi,Wpd (66) | vcvttss2si Gy,Wss (F3),(v1) | vcvttsd2si Gy,Wsd (F2),(v1)
+2d: cvtps2pi Ppi,Wps | cvtpd2pi Qpi,Wpd (66) | vcvtss2si Gy,Wss (F3),(v1) | vcvtsd2si Gy,Wsd (F2),(v1)
+2e: vucomiss Vss,Wss (v1) | vucomisd  Vsd,Wsd (66),(v1)
+2f: vcomiss Vss,Wss (v1) | vcomisd  Vsd,Wsd (66),(v1)
 # 0x0f 0x30-0x3f
 30: WRMSR
 31: RDTSC
@@ -388,65 +402,66 @@ AVXcode: 1
 4e: CMOVLE/NG Gv,Ev
 4f: CMOVNLE/G Gv,Ev
 # 0x0f 0x50-0x5f
-50: movmskps Gd/q,Ups (VEX) | movmskpd Gd/q,Upd (66),(VEX)
-51: sqrtps Vps,Wps (VEX) | sqrtss Vss,Wss (F3),(VEX),(o128) | sqrtpd Vpd,Wpd (66),(VEX) | sqrtsd Vsd,Wsd (F2),(VEX),(o128)
-52: rsqrtps Vps,Wps (VEX) | rsqrtss Vss,Wss (F3),(VEX),(o128)
-53: rcpps Vps,Wps (VEX) | rcpss Vss,Wss (F3),(VEX),(o128)
-54: andps Vps,Wps (VEX) | andpd Vpd,Wpd (66),(VEX)
-55: andnps Vps,Wps (VEX) | andnpd Vpd,Wpd (66),(VEX)
-56: orps Vps,Wps (VEX) | orpd Vpd,Wpd (66),(VEX)
-57: xorps Vps,Wps (VEX) | xorpd Vpd,Wpd (66),(VEX)
-58: addps Vps,Wps (VEX) | addss Vss,Wss (F3),(VEX),(o128) | addpd Vpd,Wpd (66),(VEX) | addsd Vsd,Wsd (F2),(VEX),(o128)
-59: mulps Vps,Wps (VEX) | mulss Vss,Wss (F3),(VEX),(o128) | mulpd Vpd,Wpd (66),(VEX) | mulsd Vsd,Wsd (F2),(VEX),(o128)
-5a: cvtps2pd Vpd,Wps (VEX) | cvtss2sd Vsd,Wss (F3),(VEX),(o128) | cvtpd2ps Vps,Wpd (66),(VEX) | cvtsd2ss Vsd,Wsd (F2),(VEX),(o128)
-5b: cvtdq2ps Vps,Wdq (VEX) | cvtps2dq Vdq,Wps (66),(VEX) | cvttps2dq Vdq,Wps (F3),(VEX)
-5c: subps Vps,Wps (VEX) | subss Vss,Wss (F3),(VEX),(o128) | subpd Vpd,Wpd (66),(VEX) | subsd Vsd,Wsd (F2),(VEX),(o128)
-5d: minps Vps,Wps (VEX) | minss Vss,Wss (F3),(VEX),(o128) | minpd Vpd,Wpd (66),(VEX) | minsd Vsd,Wsd (F2),(VEX),(o128)
-5e: divps Vps,Wps (VEX) | divss Vss,Wss (F3),(VEX),(o128) | divpd Vpd,Wpd (66),(VEX) | divsd Vsd,Wsd (F2),(VEX),(o128)
-5f: maxps Vps,Wps (VEX) | maxss Vss,Wss (F3),(VEX),(o128) | maxpd Vpd,Wpd (66),(VEX) | maxsd Vsd,Wsd (F2),(VEX),(o128)
+50: vmovmskps Gy,Ups | vmovmskpd Gy,Upd (66)
+51: vsqrtps Vps,Wps | vsqrtpd Vpd,Wpd (66) | vsqrtss Vss,Hss,Wss (F3),(v1) | vsqrtsd Vsd,Hsd,Wsd (F2),(v1)
+52: vrsqrtps Vps,Wps | vrsqrtss Vss,Hss,Wss (F3),(v1)
+53: vrcpps Vps,Wps | vrcpss Vss,Hss,Wss (F3),(v1)
+54: vandps Vps,Hps,Wps | vandpd Vpd,Hpd,Wpd (66)
+55: vandnps Vps,Hps,Wps | vandnpd Vpd,Hpd,Wpd (66)
+56: vorps Vps,Hps,Wps | vorpd Vpd,Hpd,Wpd (66)
+57: vxorps Vps,Hps,Wps | vxorpd Vpd,Hpd,Wpd (66)
+58: vaddps Vps,Hps,Wps | vaddpd Vpd,Hpd,Wpd (66) | vaddss Vss,Hss,Wss (F3),(v1) | vaddsd Vsd,Hsd,Wsd (F2),(v1)
+59: vmulps Vps,Hps,Wps | vmulpd Vpd,Hpd,Wpd (66) | vmulss Vss,Hss,Wss (F3),(v1) | vmulsd Vsd,Hsd,Wsd (F2),(v1)
+5a: vcvtps2pd Vpd,Wps | vcvtpd2ps Vps,Wpd (66) | vcvtss2sd Vsd,Hx,Wss (F3),(v1) | vcvtsd2ss Vss,Hx,Wsd (F2),(v1)
+5b: vcvtdq2ps Vps,Wdq | vcvtps2dq Vdq,Wps (66) | vcvttps2dq Vdq,Wps (F3)
+5c: vsubps Vps,Hps,Wps | vsubpd Vpd,Hpd,Wpd (66) | vsubss Vss,Hss,Wss (F3),(v1) | vsubsd Vsd,Hsd,Wsd (F2),(v1)
+5d: vminps Vps,Hps,Wps | vminpd Vpd,Hpd,Wpd (66) | vminss Vss,Hss,Wss (F3),(v1) | vminsd Vsd,Hsd,Wsd (F2),(v1)
+5e: vdivps Vps,Hps,Wps | vdivpd Vpd,Hpd,Wpd (66) | vdivss Vss,Hss,Wss (F3),(v1) | vdivsd Vsd,Hsd,Wsd (F2),(v1)
+5f: vmaxps Vps,Hps,Wps | vmaxpd Vpd,Hpd,Wpd (66) | vmaxss Vss,Hss,Wss (F3),(v1) | vmaxsd Vsd,Hsd,Wsd (F2),(v1)
 # 0x0f 0x60-0x6f
-60: punpcklbw Pq,Qd | punpcklbw Vdq,Wdq (66),(VEX),(o128)
-61: punpcklwd Pq,Qd | punpcklwd Vdq,Wdq (66),(VEX),(o128)
-62: punpckldq Pq,Qd | punpckldq Vdq,Wdq (66),(VEX),(o128)
-63: packsswb Pq,Qq | packsswb Vdq,Wdq (66),(VEX),(o128)
-64: pcmpgtb Pq,Qq | pcmpgtb Vdq,Wdq (66),(VEX),(o128)
-65: pcmpgtw Pq,Qq | pcmpgtw Vdq,Wdq (66),(VEX),(o128)
-66: pcmpgtd Pq,Qq | pcmpgtd Vdq,Wdq (66),(VEX),(o128)
-67: packuswb Pq,Qq | packuswb Vdq,Wdq (66),(VEX),(o128)
-68: punpckhbw Pq,Qd | punpckhbw Vdq,Wdq (66),(VEX),(o128)
-69: punpckhwd Pq,Qd | punpckhwd Vdq,Wdq (66),(VEX),(o128)
-6a: punpckhdq Pq,Qd | punpckhdq Vdq,Wdq (66),(VEX),(o128)
-6b: packssdw Pq,Qd | packssdw Vdq,Wdq (66),(VEX),(o128)
-6c: punpcklqdq Vdq,Wdq (66),(VEX),(o128)
-6d: punpckhqdq Vdq,Wdq (66),(VEX),(o128)
-6e: movd/q/ Pd,Ed/q | movd/q Vdq,Ed/q (66),(VEX),(o128)
-6f: movq Pq,Qq | movdqa Vdq,Wdq (66),(VEX) | movdqu Vdq,Wdq (F3),(VEX)
+60: punpcklbw Pq,Qd | vpunpcklbw Vx,Hx,Wx (66),(v1)
+61: punpcklwd Pq,Qd | vpunpcklwd Vx,Hx,Wx (66),(v1)
+62: punpckldq Pq,Qd | vpunpckldq Vx,Hx,Wx (66),(v1)
+63: packsswb Pq,Qq | vpacksswb Vx,Hx,Wx (66),(v1)
+64: pcmpgtb Pq,Qq | vpcmpgtb Vx,Hx,Wx (66),(v1)
+65: pcmpgtw Pq,Qq | vpcmpgtw Vx,Hx,Wx (66),(v1)
+66: pcmpgtd Pq,Qq | vpcmpgtd Vx,Hx,Wx (66),(v1)
+67: packuswb Pq,Qq | vpackuswb Vx,Hx,Wx (66),(v1)
+68: punpckhbw Pq,Qd | vpunpckhbw Vx,Hx,Wx (66),(v1)
+69: punpckhwd Pq,Qd | vpunpckhwd Vx,Hx,Wx (66),(v1)
+6a: punpckhdq Pq,Qd | vpunpckhdq Vx,Hx,Wx (66),(v1)
+6b: packssdw Pq,Qd | vpackssdw Vx,Hx,Wx (66),(v1)
+6c: vpunpcklqdq Vx,Hx,Wx (66),(v1)
+6d: vpunpckhqdq Vx,Hx,Wx (66),(v1)
+6e: movd/q Pd,Ey | vmovd/q Vy,Ey (66),(v1)
+6f: movq Pq,Qq | vmovdqa Vx,Wx (66) | vmovdqu Vx,Wx (F3)
 # 0x0f 0x70-0x7f
-70: pshufw Pq,Qq,Ib | pshufd Vdq,Wdq,Ib (66),(VEX),(o128) | pshufhw Vdq,Wdq,Ib (F3),(VEX),(o128) | pshuflw VdqWdq,Ib (F2),(VEX),(o128)
+70: pshufw Pq,Qq,Ib | vpshufd Vx,Wx,Ib (66),(v1) | vpshufhw Vx,Wx,Ib (F3),(v1) | vpshuflw Vx,Wx,Ib (F2),(v1)
 71: Grp12 (1A)
 72: Grp13 (1A)
 73: Grp14 (1A)
-74: pcmpeqb Pq,Qq | pcmpeqb Vdq,Wdq (66),(VEX),(o128)
-75: pcmpeqw Pq,Qq | pcmpeqw Vdq,Wdq (66),(VEX),(o128)
-76: pcmpeqd Pq,Qq | pcmpeqd Vdq,Wdq (66),(VEX),(o128)
-77: emms/vzeroupper/vzeroall (VEX)
-78: VMREAD Ed/q,Gd/q
-79: VMWRITE Gd/q,Ed/q
+74: pcmpeqb Pq,Qq | vpcmpeqb Vx,Hx,Wx (66),(v1)
+75: pcmpeqw Pq,Qq | vpcmpeqw Vx,Hx,Wx (66),(v1)
+76: pcmpeqd Pq,Qq | vpcmpeqd Vx,Hx,Wx (66),(v1)
+# Note: Remove (v), because vzeroall and vzeroupper becomes emms without VEX.
+77: emms | vzeroupper | vzeroall
+78: VMREAD Ey,Gy
+79: VMWRITE Gy,Ey
 7a:
 7b:
-7c: haddps Vps,Wps (F2),(VEX) | haddpd Vpd,Wpd (66),(VEX)
-7d: hsubps Vps,Wps (F2),(VEX) | hsubpd Vpd,Wpd (66),(VEX)
-7e: movd/q Ed/q,Pd | movd/q Ed/q,Vdq (66),(VEX),(o128) | movq Vq,Wq (F3),(VEX),(o128)
-7f: movq Qq,Pq | movdqa Wdq,Vdq (66),(VEX) | movdqu Wdq,Vdq (F3),(VEX)
+7c: vhaddpd Vpd,Hpd,Wpd (66) | vhaddps Vps,Hps,Wps (F2)
+7d: vhsubpd Vpd,Hpd,Wpd (66) | vhsubps Vps,Hps,Wps (F2)
+7e: movd/q Ey,Pd | vmovd/q Ey,Vy (66),(v1) | vmovq Vq,Wq (F3),(v1)
+7f: movq Qq,Pq | vmovdqa Wx,Vx (66) | vmovdqu Wx,Vx (F3)
 # 0x0f 0x80-0x8f
 80: JO Jz (f64)
 81: JNO Jz (f64)
-82: JB/JNAE/JC Jz (f64)
-83: JNB/JAE/JNC Jz (f64)
-84: JZ/JE Jz (f64)
-85: JNZ/JNE Jz (f64)
+82: JB/JC/JNAE Jz (f64)
+83: JAE/JNB/JNC Jz (f64)
+84: JE/JZ Jz (f64)
+85: JNE/JNZ Jz (f64)
 86: JBE/JNA Jz (f64)
-87: JNBE/JA Jz (f64)
+87: JA/JNBE Jz (f64)
 88: JS Jz (f64)
 89: JNS Jz (f64)
 8a: JP/JPE Jz (f64)
@@ -502,18 +517,18 @@ b8: JMPE | POPCNT Gv,Ev (F3)
 b9: Grp10 (1A)
 ba: Grp8 Ev,Ib (1A)
 bb: BTC Ev,Gv
-bc: BSF Gv,Ev
-bd: BSR Gv,Ev
+bc: BSF Gv,Ev | TZCNT Gv,Ev (F3)
+bd: BSR Gv,Ev | LZCNT Gv,Ev (F3)
 be: MOVSX Gv,Eb
 bf: MOVSX Gv,Ew
 # 0x0f 0xc0-0xcf
 c0: XADD Eb,Gb
 c1: XADD Ev,Gv
-c2: cmpps Vps,Wps,Ib (VEX) | cmpss Vss,Wss,Ib (F3),(VEX),(o128) | cmppd Vpd,Wpd,Ib (66),(VEX) | cmpsd Vsd,Wsd,Ib (F2),(VEX)
-c3: movnti Md/q,Gd/q
-c4: pinsrw Pq,Rd/q/Mw,Ib | pinsrw Vdq,Rd/q/Mw,Ib (66),(VEX),(o128)
-c5: pextrw Gd,Nq,Ib | pextrw Gd,Udq,Ib (66),(VEX),(o128)
-c6: shufps Vps,Wps,Ib (VEX) | shufpd Vpd,Wpd,Ib (66),(VEX)
+c2: vcmpps Vps,Hps,Wps,Ib | vcmppd Vpd,Hpd,Wpd,Ib (66) | vcmpss Vss,Hss,Wss,Ib (F3),(v1) | vcmpsd Vsd,Hsd,Wsd,Ib (F2),(v1)
+c3: movnti My,Gy
+c4: pinsrw Pq,Ry/Mw,Ib | vpinsrw Vdq,Hdq,Ry/Mw,Ib (66),(v1)
+c5: pextrw Gd,Nq,Ib | vpextrw Gd,Udq,Ib (66),(v1)
+c6: vshufps Vps,Hps,Wps,Ib | vshufpd Vpd,Hpd,Wpd,Ib (66)
 c7: Grp9 (1A)
 c8: BSWAP RAX/EAX/R8/R8D
 c9: BSWAP RCX/ECX/R9/R9D
@@ -524,55 +539,55 @@ cd: BSWAP RBP/EBP/R13/R13D
 ce: BSWAP RSI/ESI/R14/R14D
 cf: BSWAP RDI/EDI/R15/R15D
 # 0x0f 0xd0-0xdf
-d0: addsubps Vps,Wps (F2),(VEX) | addsubpd Vpd,Wpd (66),(VEX)
-d1: psrlw Pq,Qq | psrlw Vdq,Wdq (66),(VEX),(o128)
-d2: psrld Pq,Qq | psrld Vdq,Wdq (66),(VEX),(o128)
-d3: psrlq Pq,Qq | psrlq Vdq,Wdq (66),(VEX),(o128)
-d4: paddq Pq,Qq | paddq Vdq,Wdq (66),(VEX),(o128)
-d5: pmullw Pq,Qq | pmullw Vdq,Wdq (66),(VEX),(o128)
-d6: movq Wq,Vq (66),(VEX),(o128) | movq2dq Vdq,Nq (F3) | movdq2q Pq,Uq (F2)
-d7: pmovmskb Gd,Nq | pmovmskb Gd,Udq (66),(VEX),(o128)
-d8: psubusb Pq,Qq | psubusb Vdq,Wdq (66),(VEX),(o128)
-d9: psubusw Pq,Qq | psubusw Vdq,Wdq (66),(VEX),(o128)
-da: pminub Pq,Qq | pminub Vdq,Wdq (66),(VEX),(o128)
-db: pand Pq,Qq | pand Vdq,Wdq (66),(VEX),(o128)
-dc: paddusb Pq,Qq | paddusb Vdq,Wdq (66),(VEX),(o128)
-dd: paddusw Pq,Qq | paddusw Vdq,Wdq (66),(VEX),(o128)
-de: pmaxub Pq,Qq | pmaxub Vdq,Wdq (66),(VEX),(o128)
-df: pandn Pq,Qq | pandn Vdq,Wdq (66),(VEX),(o128)
+d0: vaddsubpd Vpd,Hpd,Wpd (66) | vaddsubps Vps,Hps,Wps (F2)
+d1: psrlw Pq,Qq | vpsrlw Vx,Hx,Wx (66),(v1)
+d2: psrld Pq,Qq | vpsrld Vx,Hx,Wx (66),(v1)
+d3: psrlq Pq,Qq | vpsrlq Vx,Hx,Wx (66),(v1)
+d4: paddq Pq,Qq | vpaddq Vx,Hx,Wx (66),(v1)
+d5: pmullw Pq,Qq | vpmullw Vx,Hx,Wx (66),(v1)
+d6: vmovq Wq,Vq (66),(v1) | movq2dq Vdq,Nq (F3) | movdq2q Pq,Uq (F2)
+d7: pmovmskb Gd,Nq | vpmovmskb Gd,Ux (66),(v1)
+d8: psubusb Pq,Qq | vpsubusb Vx,Hx,Wx (66),(v1)
+d9: psubusw Pq,Qq | vpsubusw Vx,Hx,Wx (66),(v1)
+da: pminub Pq,Qq | vpminub Vx,Hx,Wx (66),(v1)
+db: pand Pq,Qq | vpand Vx,Hx,Wx (66),(v1)
+dc: paddusb Pq,Qq | vpaddusb Vx,Hx,Wx (66),(v1)
+dd: paddusw Pq,Qq | vpaddusw Vx,Hx,Wx (66),(v1)
+de: pmaxub Pq,Qq | vpmaxub Vx,Hx,Wx (66),(v1)
+df: pandn Pq,Qq | vpandn Vx,Hx,Wx (66),(v1)
 # 0x0f 0xe0-0xef
-e0: pavgb Pq,Qq | pavgb Vdq,Wdq (66),(VEX),(o128)
-e1: psraw Pq,Qq | psraw Vdq,Wdq (66),(VEX),(o128)
-e2: psrad Pq,Qq | psrad Vdq,Wdq (66),(VEX),(o128)
-e3: pavgw Pq,Qq | pavgw Vdq,Wdq (66),(VEX),(o128)
-e4: pmulhuw Pq,Qq | pmulhuw Vdq,Wdq (66),(VEX),(o128)
-e5: pmulhw Pq,Qq | pmulhw Vdq,Wdq (66),(VEX),(o128)
-e6: cvtpd2dq Vdq,Wpd (F2),(VEX) | cvttpd2dq Vdq,Wpd (66),(VEX) | cvtdq2pd Vpd,Wdq (F3),(VEX)
-e7: movntq Mq,Pq | movntdq Mdq,Vdq (66),(VEX)
-e8: psubsb Pq,Qq | psubsb Vdq,Wdq (66),(VEX),(o128)
-e9: psubsw Pq,Qq | psubsw Vdq,Wdq (66),(VEX),(o128)
-ea: pminsw Pq,Qq | pminsw Vdq,Wdq (66),(VEX),(o128)
-eb: por Pq,Qq | por Vdq,Wdq (66),(VEX),(o128)
-ec: paddsb Pq,Qq | paddsb Vdq,Wdq (66),(VEX),(o128)
-ed: paddsw Pq,Qq | paddsw Vdq,Wdq (66),(VEX),(o128)
-ee: pmaxsw Pq,Qq | pmaxsw Vdq,Wdq (66),(VEX),(o128)
-ef: pxor Pq,Qq | pxor Vdq,Wdq (66),(VEX),(o128)
+e0: pavgb Pq,Qq | vpavgb Vx,Hx,Wx (66),(v1)
+e1: psraw Pq,Qq | vpsraw Vx,Hx,Wx (66),(v1)
+e2: psrad Pq,Qq | vpsrad Vx,Hx,Wx (66),(v1)
+e3: pavgw Pq,Qq | vpavgw Vx,Hx,Wx (66),(v1)
+e4: pmulhuw Pq,Qq | vpmulhuw Vx,Hx,Wx (66),(v1)
+e5: pmulhw Pq,Qq | vpmulhw Vx,Hx,Wx (66),(v1)
+e6: vcvttpd2dq Vx,Wpd (66) | vcvtdq2pd Vx,Wdq (F3) | vcvtpd2dq Vx,Wpd (F2)
+e7: movntq Mq,Pq | vmovntdq Mx,Vx (66)
+e8: psubsb Pq,Qq | vpsubsb Vx,Hx,Wx (66),(v1)
+e9: psubsw Pq,Qq | vpsubsw Vx,Hx,Wx (66),(v1)
+ea: pminsw Pq,Qq | vpminsw Vx,Hx,Wx (66),(v1)
+eb: por Pq,Qq | vpor Vx,Hx,Wx (66),(v1)
+ec: paddsb Pq,Qq | vpaddsb Vx,Hx,Wx (66),(v1)
+ed: paddsw Pq,Qq | vpaddsw Vx,Hx,Wx (66),(v1)
+ee: pmaxsw Pq,Qq | vpmaxsw Vx,Hx,Wx (66),(v1)
+ef: pxor Pq,Qq | vpxor Vx,Hx,Wx (66),(v1)
 # 0x0f 0xf0-0xff
-f0: lddqu Vdq,Mdq (F2),(VEX)
-f1: psllw Pq,Qq | psllw Vdq,Wdq (66),(VEX),(o128)
-f2: pslld Pq,Qq | pslld Vdq,Wdq (66),(VEX),(o128)
-f3: psllq Pq,Qq | psllq Vdq,Wdq (66),(VEX),(o128)
-f4: pmuludq Pq,Qq | pmuludq Vdq,Wdq (66),(VEX),(o128)
-f5: pmaddwd Pq,Qq | pmaddwd Vdq,Wdq (66),(VEX),(o128)
-f6: psadbw Pq,Qq | psadbw Vdq,Wdq (66),(VEX),(o128)
-f7: maskmovq Pq,Nq | maskmovdqu Vdq,Udq (66),(VEX),(o128)
-f8: psubb Pq,Qq | psubb Vdq,Wdq (66),(VEX),(o128)
-f9: psubw Pq,Qq | psubw Vdq,Wdq (66),(VEX),(o128)
-fa: psubd Pq,Qq | psubd Vdq,Wdq (66),(VEX),(o128)
-fb: psubq Pq,Qq | psubq Vdq,Wdq (66),(VEX),(o128)
-fc: paddb Pq,Qq | paddb Vdq,Wdq (66),(VEX),(o128)
-fd: paddw Pq,Qq | paddw Vdq,Wdq (66),(VEX),(o128)
-fe: paddd Pq,Qq | paddd Vdq,Wdq (66),(VEX),(o128)
+f0: vlddqu Vx,Mx (F2)
+f1: psllw Pq,Qq | vpsllw Vx,Hx,Wx (66),(v1)
+f2: pslld Pq,Qq | vpslld Vx,Hx,Wx (66),(v1)
+f3: psllq Pq,Qq | vpsllq Vx,Hx,Wx (66),(v1)
+f4: pmuludq Pq,Qq | vpmuludq Vx,Hx,Wx (66),(v1)
+f5: pmaddwd Pq,Qq | vpmaddwd Vx,Hx,Wx (66),(v1)
+f6: psadbw Pq,Qq | vpsadbw Vx,Hx,Wx (66),(v1)
+f7: maskmovq Pq,Nq | vmaskmovdqu Vx,Ux (66),(v1)
+f8: psubb Pq,Qq | vpsubb Vx,Hx,Wx (66),(v1)
+f9: psubw Pq,Qq | vpsubw Vx,Hx,Wx (66),(v1)
+fa: psubd Pq,Qq | vpsubd Vx,Hx,Wx (66),(v1)
+fb: psubq Pq,Qq | vpsubq Vx,Hx,Wx (66),(v1)
+fc: paddb Pq,Qq | vpaddb Vx,Hx,Wx (66),(v1)
+fd: paddw Pq,Qq | vpaddw Vx,Hx,Wx (66),(v1)
+fe: paddd Pq,Qq | vpaddd Vx,Hx,Wx (66),(v1)
 ff:
 EndTable
 
@@ -580,155 +595,193 @@ Table: 3-byte opcode 1 (0x0f 0x38)
 Referrer: 3-byte escape 1
 AVXcode: 2
 # 0x0f 0x38 0x00-0x0f
-00: pshufb Pq,Qq | pshufb Vdq,Wdq (66),(VEX),(o128)
-01: phaddw Pq,Qq | phaddw Vdq,Wdq (66),(VEX),(o128)
-02: phaddd Pq,Qq | phaddd Vdq,Wdq (66),(VEX),(o128)
-03: phaddsw Pq,Qq | phaddsw Vdq,Wdq (66),(VEX),(o128)
-04: pmaddubsw Pq,Qq | pmaddubsw Vdq,Wdq (66),(VEX),(o128)
-05: phsubw Pq,Qq | phsubw Vdq,Wdq (66),(VEX),(o128)
-06: phsubd Pq,Qq | phsubd Vdq,Wdq (66),(VEX),(o128)
-07: phsubsw Pq,Qq | phsubsw Vdq,Wdq (66),(VEX),(o128)
-08: psignb Pq,Qq | psignb Vdq,Wdq (66),(VEX),(o128)
-09: psignw Pq,Qq | psignw Vdq,Wdq (66),(VEX),(o128)
-0a: psignd Pq,Qq | psignd Vdq,Wdq (66),(VEX),(o128)
-0b: pmulhrsw Pq,Qq | pmulhrsw Vdq,Wdq (66),(VEX),(o128)
-0c: Vpermilps /r (66),(oVEX)
-0d: Vpermilpd /r (66),(oVEX)
-0e: vtestps /r (66),(oVEX)
-0f: vtestpd /r (66),(oVEX)
+00: pshufb Pq,Qq | vpshufb Vx,Hx,Wx (66),(v1)
+01: phaddw Pq,Qq | vphaddw Vx,Hx,Wx (66),(v1)
+02: phaddd Pq,Qq | vphaddd Vx,Hx,Wx (66),(v1)
+03: phaddsw Pq,Qq | vphaddsw Vx,Hx,Wx (66),(v1)
+04: pmaddubsw Pq,Qq | vpmaddubsw Vx,Hx,Wx (66),(v1)
+05: phsubw Pq,Qq | vphsubw Vx,Hx,Wx (66),(v1)
+06: phsubd Pq,Qq | vphsubd Vx,Hx,Wx (66),(v1)
+07: phsubsw Pq,Qq | vphsubsw Vx,Hx,Wx (66),(v1)
+08: psignb Pq,Qq | vpsignb Vx,Hx,Wx (66),(v1)
+09: psignw Pq,Qq | vpsignw Vx,Hx,Wx (66),(v1)
+0a: psignd Pq,Qq | vpsignd Vx,Hx,Wx (66),(v1)
+0b: pmulhrsw Pq,Qq | vpmulhrsw Vx,Hx,Wx (66),(v1)
+0c: vpermilps Vx,Hx,Wx (66),(v)
+0d: vpermilpd Vx,Hx,Wx (66),(v)
+0e: vtestps Vx,Wx (66),(v)
+0f: vtestpd Vx,Wx (66),(v)
 # 0x0f 0x38 0x10-0x1f
 10: pblendvb Vdq,Wdq (66)
 11:
 12:
-13:
+13: vcvtph2ps Vx,Wx,Ib (66),(v)
 14: blendvps Vdq,Wdq (66)
 15: blendvpd Vdq,Wdq (66)
-16:
-17: ptest Vdq,Wdq (66),(VEX)
-18: vbroadcastss /r (66),(oVEX)
-19: vbroadcastsd /r (66),(oVEX),(o256)
-1a: vbroadcastf128 /r (66),(oVEX),(o256)
+16: vpermps Vqq,Hqq,Wqq (66),(v)
+17: vptest Vx,Wx (66)
+18: vbroadcastss Vx,Wd (66),(v)
+19: vbroadcastsd Vqq,Wq (66),(v)
+1a: vbroadcastf128 Vqq,Mdq (66),(v)
 1b:
-1c: pabsb Pq,Qq | pabsb Vdq,Wdq (66),(VEX),(o128)
-1d: pabsw Pq,Qq | pabsw Vdq,Wdq (66),(VEX),(o128)
-1e: pabsd Pq,Qq | pabsd Vdq,Wdq (66),(VEX),(o128)
+1c: pabsb Pq,Qq | vpabsb Vx,Wx (66),(v1)
+1d: pabsw Pq,Qq | vpabsw Vx,Wx (66),(v1)
+1e: pabsd Pq,Qq | vpabsd Vx,Wx (66),(v1)
 1f:
 # 0x0f 0x38 0x20-0x2f
-20: pmovsxbw Vdq,Udq/Mq (66),(VEX),(o128)
-21: pmovsxbd Vdq,Udq/Md (66),(VEX),(o128)
-22: pmovsxbq Vdq,Udq/Mw (66),(VEX),(o128)
-23: pmovsxwd Vdq,Udq/Mq (66),(VEX),(o128)
-24: pmovsxwq Vdq,Udq/Md (66),(VEX),(o128)
-25: pmovsxdq Vdq,Udq/Mq (66),(VEX),(o128)
+20: vpmovsxbw Vx,Ux/Mq (66),(v1)
+21: vpmovsxbd Vx,Ux/Md (66),(v1)
+22: vpmovsxbq Vx,Ux/Mw (66),(v1)
+23: vpmovsxwd Vx,Ux/Mq (66),(v1)
+24: vpmovsxwq Vx,Ux/Md (66),(v1)
+25: vpmovsxdq Vx,Ux/Mq (66),(v1)
 26:
 27:
-28: pmuldq Vdq,Wdq (66),(VEX),(o128)
-29: pcmpeqq Vdq,Wdq (66),(VEX),(o128)
-2a: movntdqa Vdq,Mdq (66),(VEX),(o128)
-2b: packusdw Vdq,Wdq (66),(VEX),(o128)
-2c: vmaskmovps(ld) /r (66),(oVEX)
-2d: vmaskmovpd(ld) /r (66),(oVEX)
-2e: vmaskmovps(st) /r (66),(oVEX)
-2f: vmaskmovpd(st) /r (66),(oVEX)
+28: vpmuldq Vx,Hx,Wx (66),(v1)
+29: vpcmpeqq Vx,Hx,Wx (66),(v1)
+2a: vmovntdqa Vx,Mx (66),(v1)
+2b: vpackusdw Vx,Hx,Wx (66),(v1)
+2c: vmaskmovps Vx,Hx,Mx (66),(v)
+2d: vmaskmovpd Vx,Hx,Mx (66),(v)
+2e: vmaskmovps Mx,Hx,Vx (66),(v)
+2f: vmaskmovpd Mx,Hx,Vx (66),(v)
 # 0x0f 0x38 0x30-0x3f
-30: pmovzxbw Vdq,Udq/Mq (66),(VEX),(o128)
-31: pmovzxbd Vdq,Udq/Md (66),(VEX),(o128)
-32: pmovzxbq Vdq,Udq/Mw (66),(VEX),(o128)
-33: pmovzxwd Vdq,Udq/Mq (66),(VEX),(o128)
-34: pmovzxwq Vdq,Udq/Md (66),(VEX),(o128)
-35: pmovzxdq Vdq,Udq/Mq (66),(VEX),(o128)
-36:
-37: pcmpgtq Vdq,Wdq (66),(VEX),(o128)
-38: pminsb Vdq,Wdq (66),(VEX),(o128)
-39: pminsd Vdq,Wdq (66),(VEX),(o128)
-3a: pminuw Vdq,Wdq (66),(VEX),(o128)
-3b: pminud Vdq,Wdq (66),(VEX),(o128)
-3c: pmaxsb Vdq,Wdq (66),(VEX),(o128)
-3d: pmaxsd Vdq,Wdq (66),(VEX),(o128)
-3e: pmaxuw Vdq,Wdq (66),(VEX),(o128)
-3f: pmaxud Vdq,Wdq (66),(VEX),(o128)
+30: vpmovzxbw Vx,Ux/Mq (66),(v1)
+31: vpmovzxbd Vx,Ux/Md (66),(v1)
+32: vpmovzxbq Vx,Ux/Mw (66),(v1)
+33: vpmovzxwd Vx,Ux/Mq (66),(v1)
+34: vpmovzxwq Vx,Ux/Md (66),(v1)
+35: vpmovzxdq Vx,Ux/Mq (66),(v1)
+36: vpermd Vqq,Hqq,Wqq (66),(v)
+37: vpcmpgtq Vx,Hx,Wx (66),(v1)
+38: vpminsb Vx,Hx,Wx (66),(v1)
+39: vpminsd Vx,Hx,Wx (66),(v1)
+3a: vpminuw Vx,Hx,Wx (66),(v1)
+3b: vpminud Vx,Hx,Wx (66),(v1)
+3c: vpmaxsb Vx,Hx,Wx (66),(v1)
+3d: vpmaxsd Vx,Hx,Wx (66),(v1)
+3e: vpmaxuw Vx,Hx,Wx (66),(v1)
+3f: vpmaxud Vx,Hx,Wx (66),(v1)
 # 0x0f 0x38 0x40-0x8f
-40: pmulld Vdq,Wdq (66),(VEX),(o128)
-41: phminposuw Vdq,Wdq (66),(VEX),(o128)
-80: INVEPT Gd/q,Mdq (66)
-81: INVPID Gd/q,Mdq (66)
+40: vpmulld Vx,Hx,Wx (66),(v1)
+41: vphminposuw Vdq,Wdq (66),(v1)
+42:
+43:
+44:
+45: vpsrlvd/q Vx,Hx,Wx (66),(v)
+46: vpsravd Vx,Hx,Wx (66),(v)
+47: vpsllvd/q Vx,Hx,Wx (66),(v)
+# Skip 0x48-0x57
+58: vpbroadcastd Vx,Wx (66),(v)
+59: vpbroadcastq Vx,Wx (66),(v)
+5a: vbroadcasti128 Vqq,Mdq (66),(v)
+# Skip 0x5b-0x77
+78: vpbroadcastb Vx,Wx (66),(v)
+79: vpbroadcastw Vx,Wx (66),(v)
+# Skip 0x7a-0x7f
+80: INVEPT Gy,Mdq (66)
+81: INVPID Gy,Mdq (66)
+82: INVPCID Gy,Mdq (66)
+8c: vpmaskmovd/q Vx,Hx,Mx (66),(v)
+8e: vpmaskmovd/q Mx,Vx,Hx (66),(v)
 # 0x0f 0x38 0x90-0xbf (FMA)
-96: vfmaddsub132pd/ps /r (66),(VEX)
-97: vfmsubadd132pd/ps /r (66),(VEX)
-98: vfmadd132pd/ps /r (66),(VEX)
-99: vfmadd132sd/ss /r (66),(VEX),(o128)
-9a: vfmsub132pd/ps /r (66),(VEX)
-9b: vfmsub132sd/ss /r (66),(VEX),(o128)
-9c: vfnmadd132pd/ps /r (66),(VEX)
-9d: vfnmadd132sd/ss /r (66),(VEX),(o128)
-9e: vfnmsub132pd/ps /r (66),(VEX)
-9f: vfnmsub132sd/ss /r (66),(VEX),(o128)
-a6: vfmaddsub213pd/ps /r (66),(VEX)
-a7: vfmsubadd213pd/ps /r (66),(VEX)
-a8: vfmadd213pd/ps /r (66),(VEX)
-a9: vfmadd213sd/ss /r (66),(VEX),(o128)
-aa: vfmsub213pd/ps /r (66),(VEX)
-ab: vfmsub213sd/ss /r (66),(VEX),(o128)
-ac: vfnmadd213pd/ps /r (66),(VEX)
-ad: vfnmadd213sd/ss /r (66),(VEX),(o128)
-ae: vfnmsub213pd/ps /r (66),(VEX)
-af: vfnmsub213sd/ss /r (66),(VEX),(o128)
-b6: vfmaddsub231pd/ps /r (66),(VEX)
-b7: vfmsubadd231pd/ps /r (66),(VEX)
-b8: vfmadd231pd/ps /r (66),(VEX)
-b9: vfmadd231sd/ss /r (66),(VEX),(o128)
-ba: vfmsub231pd/ps /r (66),(VEX)
-bb: vfmsub231sd/ss /r (66),(VEX),(o128)
-bc: vfnmadd231pd/ps /r (66),(VEX)
-bd: vfnmadd231sd/ss /r (66),(VEX),(o128)
-be: vfnmsub231pd/ps /r (66),(VEX)
-bf: vfnmsub231sd/ss /r (66),(VEX),(o128)
+90: vgatherdd/q Vx,Hx,Wx (66),(v)
+91: vgatherqd/q Vx,Hx,Wx (66),(v)
+92: vgatherdps/d Vx,Hx,Wx (66),(v)
+93: vgatherqps/d Vx,Hx,Wx (66),(v)
+94:
+95:
+96: vfmaddsub132ps/d Vx,Hx,Wx (66),(v)
+97: vfmsubadd132ps/d Vx,Hx,Wx (66),(v)
+98: vfmadd132ps/d Vx,Hx,Wx (66),(v)
+99: vfmadd132ss/d Vx,Hx,Wx (66),(v),(v1)
+9a: vfmsub132ps/d Vx,Hx,Wx (66),(v)
+9b: vfmsub132ss/d Vx,Hx,Wx (66),(v),(v1)
+9c: vfnmadd132ps/d Vx,Hx,Wx (66),(v)
+9d: vfnmadd132ss/d Vx,Hx,Wx (66),(v),(v1)
+9e: vfnmsub132ps/d Vx,Hx,Wx (66),(v)
+9f: vfnmsub132ss/d Vx,Hx,Wx (66),(v),(v1)
+a6: vfmaddsub213ps/d Vx,Hx,Wx (66),(v)
+a7: vfmsubadd213ps/d Vx,Hx,Wx (66),(v)
+a8: vfmadd213ps/d Vx,Hx,Wx (66),(v)
+a9: vfmadd213ss/d Vx,Hx,Wx (66),(v),(v1)
+aa: vfmsub213ps/d Vx,Hx,Wx (66),(v)
+ab: vfmsub213ss/d Vx,Hx,Wx (66),(v),(v1)
+ac: vfnmadd213ps/d Vx,Hx,Wx (66),(v)
+ad: vfnmadd213ss/d Vx,Hx,Wx (66),(v),(v1)
+ae: vfnmsub213ps/d Vx,Hx,Wx (66),(v)
+af: vfnmsub213ss/d Vx,Hx,Wx (66),(v),(v1)
+b6: vfmaddsub231ps/d Vx,Hx,Wx (66),(v)
+b7: vfmsubadd231ps/d Vx,Hx,Wx (66),(v)
+b8: vfmadd231ps/d Vx,Hx,Wx (66),(v)
+b9: vfmadd231ss/d Vx,Hx,Wx (66),(v),(v1)
+ba: vfmsub231ps/d Vx,Hx,Wx (66),(v)
+bb: vfmsub231ss/d Vx,Hx,Wx (66),(v),(v1)
+bc: vfnmadd231ps/d Vx,Hx,Wx (66),(v)
+bd: vfnmadd231ss/d Vx,Hx,Wx (66),(v),(v1)
+be: vfnmsub231ps/d Vx,Hx,Wx (66),(v)
+bf: vfnmsub231ss/d Vx,Hx,Wx (66),(v),(v1)
 # 0x0f 0x38 0xc0-0xff
-db: aesimc Vdq,Wdq (66),(VEX),(o128)
-dc: aesenc Vdq,Wdq (66),(VEX),(o128)
-dd: aesenclast Vdq,Wdq (66),(VEX),(o128)
-de: aesdec Vdq,Wdq (66),(VEX),(o128)
-df: aesdeclast Vdq,Wdq (66),(VEX),(o128)
-f0: MOVBE Gv,Mv | CRC32 Gd,Eb (F2)
-f1: MOVBE Mv,Gv | CRC32 Gd,Ev (F2)
+db: VAESIMC Vdq,Wdq (66),(v1)
+dc: VAESENC Vdq,Hdq,Wdq (66),(v1)
+dd: VAESENCLAST Vdq,Hdq,Wdq (66),(v1)
+de: VAESDEC Vdq,Hdq,Wdq (66),(v1)
+df: VAESDECLAST Vdq,Hdq,Wdq (66),(v1)
+f0: MOVBE Gy,My | MOVBE Gw,Mw (66) | CRC32 Gd,Eb (F2)
+f1: MOVBE My,Gy | MOVBE Mw,Gw (66) | CRC32 Gd,Ey (F2)
+f3: ANDN Gy,By,Ey (v)
+f4: Grp17 (1A)
+f5: BZHI Gy,Ey,By (v) | PEXT Gy,By,Ey (F3),(v) | PDEP Gy,By,Ey (F2),(v)
+f6: MULX By,Gy,rDX,Ey (F2),(v)
+f7: BEXTR Gy,Ey,By (v) | SHLX Gy,Ey,By (66),(v) | SARX Gy,Ey,By (F3),(v) | SHRX Gy,Ey,By (F2),(v)
 EndTable
 
 Table: 3-byte opcode 2 (0x0f 0x3a)
 Referrer: 3-byte escape 2
 AVXcode: 3
 # 0x0f 0x3a 0x00-0xff
-04: vpermilps /r,Ib (66),(oVEX)
-05: vpermilpd /r,Ib (66),(oVEX)
-06: vperm2f128 /r,Ib (66),(oVEX),(o256)
-08: roundps Vdq,Wdq,Ib (66),(VEX)
-09: roundpd Vdq,Wdq,Ib (66),(VEX)
-0a: roundss Vss,Wss,Ib (66),(VEX),(o128)
-0b: roundsd Vsd,Wsd,Ib (66),(VEX),(o128)
-0c: blendps Vdq,Wdq,Ib (66),(VEX)
-0d: blendpd Vdq,Wdq,Ib (66),(VEX)
-0e: pblendw Vdq,Wdq,Ib (66),(VEX),(o128)
-0f: palignr Pq,Qq,Ib | palignr Vdq,Wdq,Ib (66),(VEX),(o128)
-14: pextrb Rd/Mb,Vdq,Ib (66),(VEX),(o128)
-15: pextrw Rd/Mw,Vdq,Ib (66),(VEX),(o128)
-16: pextrd/pextrq Ed/q,Vdq,Ib (66),(VEX),(o128)
-17: extractps Ed,Vdq,Ib (66),(VEX),(o128)
-18: vinsertf128 /r,Ib (66),(oVEX),(o256)
-19: vextractf128 /r,Ib (66),(oVEX),(o256)
-20: pinsrb Vdq,Rd/q/Mb,Ib (66),(VEX),(o128)
-21: insertps Vdq,Udq/Md,Ib (66),(VEX),(o128)
-22: pinsrd/pinsrq Vdq,Ed/q,Ib (66),(VEX),(o128)
-40: dpps Vdq,Wdq,Ib (66),(VEX)
-41: dppd Vdq,Wdq,Ib (66),(VEX),(o128)
-42: mpsadbw Vdq,Wdq,Ib (66),(VEX),(o128)
-44: pclmulq Vdq,Wdq,Ib (66),(VEX),(o128)
-4a: vblendvps /r,Ib (66),(oVEX)
-4b: vblendvpd /r,Ib (66),(oVEX)
-4c: vpblendvb /r,Ib (66),(oVEX),(o128)
-60: pcmpestrm Vdq,Wdq,Ib (66),(VEX),(o128)
-61: pcmpestri Vdq,Wdq,Ib (66),(VEX),(o128)
-62: pcmpistrm Vdq,Wdq,Ib (66),(VEX),(o128)
-63: pcmpistri Vdq,Wdq,Ib (66),(VEX),(o128)
-df: aeskeygenassist Vdq,Wdq,Ib (66),(VEX),(o128)
+00: vpermq Vqq,Wqq,Ib (66),(v)
+01: vpermpd Vqq,Wqq,Ib (66),(v)
+02: vpblendd Vx,Hx,Wx,Ib (66),(v)
+03:
+04: vpermilps Vx,Wx,Ib (66),(v)
+05: vpermilpd Vx,Wx,Ib (66),(v)
+06: vperm2f128 Vqq,Hqq,Wqq,Ib (66),(v)
+07:
+08: vroundps Vx,Wx,Ib (66)
+09: vroundpd Vx,Wx,Ib (66)
+0a: vroundss Vss,Wss,Ib (66),(v1)
+0b: vroundsd Vsd,Wsd,Ib (66),(v1)
+0c: vblendps Vx,Hx,Wx,Ib (66)
+0d: vblendpd Vx,Hx,Wx,Ib (66)
+0e: vpblendw Vx,Hx,Wx,Ib (66),(v1)
+0f: palignr Pq,Qq,Ib | vpalignr Vx,Hx,Wx,Ib (66),(v1)
+14: vpextrb Rd/Mb,Vdq,Ib (66),(v1)
+15: vpextrw Rd/Mw,Vdq,Ib (66),(v1)
+16: vpextrd/q Ey,Vdq,Ib (66),(v1)
+17: vextractps Ed,Vdq,Ib (66),(v1)
+18: vinsertf128 Vqq,Hqq,Wqq,Ib (66),(v)
+19: vextractf128 Wdq,Vqq,Ib (66),(v)
+1d: vcvtps2ph Wx,Vx,Ib (66),(v)
+20: vpinsrb Vdq,Hdq,Ry/Mb,Ib (66),(v1)
+21: vinsertps Vdq,Hdq,Udq/Md,Ib (66),(v1)
+22: vpinsrd/q Vdq,Hdq,Ey,Ib (66),(v1)
+38: vinserti128 Vqq,Hqq,Wqq,Ib (66),(v)
+39: vextracti128 Wdq,Vqq,Ib (66),(v)
+40: vdpps Vx,Hx,Wx,Ib (66)
+41: vdppd Vdq,Hdq,Wdq,Ib (66),(v1)
+42: vmpsadbw Vx,Hx,Wx,Ib (66),(v1)
+44: vpclmulqdq Vdq,Hdq,Wdq,Ib (66),(v1)
+46: vperm2i128 Vqq,Hqq,Wqq,Ib (66),(v)
+4a: vblendvps Vx,Hx,Wx,Lx (66),(v)
+4b: vblendvpd Vx,Hx,Wx,Lx (66),(v)
+4c: vpblendvb Vx,Hx,Wx,Lx (66),(v1)
+60: vpcmpestrm Vdq,Wdq,Ib (66),(v1)
+61: vpcmpestri Vdq,Wdq,Ib (66),(v1)
+62: vpcmpistrm Vdq,Wdq,Ib (66),(v1)
+63: vpcmpistri Vdq,Wdq,Ib (66),(v1)
+df: VAESKEYGEN Vdq,Wdq,Ib (66),(v1)
+f0: RORX Gy,Ey,Ib (F2),(v)
 EndTable
 
 GrpTable: Grp1
@@ -790,7 +843,7 @@ GrpTable: Grp5
 2: CALLN Ev (f64)
 3: CALLF Ep
 4: JMPN Ev (f64)
-5: JMPF Ep
+5: JMPF Mp
 6: PUSH Ev (d64)
 7:
 EndTable
@@ -807,7 +860,7 @@ EndTable
 GrpTable: Grp7
 0: SGDT Ms | VMCALL (001),(11B) | VMLAUNCH (010),(11B) | VMRESUME (011),(11B) | VMXOFF (100),(11B)
 1: SIDT Ms | MONITOR (000),(11B) | MWAIT (001)
-2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B)
+2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B) | VMFUNC (100),(11B)
 3: LIDT Ms
 4: SMSW Mw/Rv
 5:
@@ -824,44 +877,45 @@ EndTable
 
 GrpTable: Grp9
 1: CMPXCHG8B/16B Mq/Mdq
-6: VMPTRLD Mq | VMCLEAR Mq (66) | VMXON Mq (F3)
-7: VMPTRST Mq
+6: VMPTRLD Mq | VMCLEAR Mq (66) | VMXON Mq (F3) | RDRAND Rv (11B)
+7: VMPTRST Mq | VMPTRST Mq (F3)
 EndTable
 
 GrpTable: Grp10
 EndTable
 
 GrpTable: Grp11
+# Note: the operands are given by group opcode
 0: MOV
 EndTable
 
 GrpTable: Grp12
-2: psrlw Nq,Ib (11B) | psrlw Udq,Ib (66),(11B),(VEX),(o128)
-4: psraw Nq,Ib (11B) | psraw Udq,Ib (66),(11B),(VEX),(o128)
-6: psllw Nq,Ib (11B) | psllw Udq,Ib (66),(11B),(VEX),(o128)
+2: psrlw Nq,Ib (11B) | vpsrlw Hx,Ux,Ib (66),(11B),(v1)
+4: psraw Nq,Ib (11B) | vpsraw Hx,Ux,Ib (66),(11B),(v1)
+6: psllw Nq,Ib (11B) | vpsllw Hx,Ux,Ib (66),(11B),(v1)
 EndTable
 
 GrpTable: Grp13
-2: psrld Nq,Ib (11B) | psrld Udq,Ib (66),(11B),(VEX),(o128)
-4: psrad Nq,Ib (11B) | psrad Udq,Ib (66),(11B),(VEX),(o128)
-6: pslld Nq,Ib (11B) | pslld Udq,Ib (66),(11B),(VEX),(o128)
+2: psrld Nq,Ib (11B) | vpsrld Hx,Ux,Ib (66),(11B),(v1)
+4: psrad Nq,Ib (11B) | vpsrad Hx,Ux,Ib (66),(11B),(v1)
+6: pslld Nq,Ib (11B) | vpslld Hx,Ux,Ib (66),(11B),(v1)
 EndTable
 
 GrpTable: Grp14
-2: psrlq Nq,Ib (11B) | psrlq Udq,Ib (66),(11B),(VEX),(o128)
-3: psrldq Udq,Ib (66),(11B),(VEX),(o128)
-6: psllq Nq,Ib (11B) | psllq Udq,Ib (66),(11B),(VEX),(o128)
-7: pslldq Udq,Ib (66),(11B),(VEX),(o128)
+2: psrlq Nq,Ib (11B) | vpsrlq Hx,Ux,Ib (66),(11B),(v1)
+3: vpsrldq Hx,Ux,Ib (66),(11B),(v1)
+6: psllq Nq,Ib (11B) | vpsllq Hx,Ux,Ib (66),(11B),(v1)
+7: vpslldq Hx,Ux,Ib (66),(11B),(v1)
 EndTable
 
 GrpTable: Grp15
-0: fxsave
-1: fxstor
-2: ldmxcsr (VEX)
-3: stmxcsr (VEX)
+0: fxsave | RDFSBASE Ry (F3),(11B)
+1: fxstor | RDGSBASE Ry (F3),(11B)
+2: vldmxcsr Md (v1) | WRFSBASE Ry (F3),(11B)
+3: vstmxcsr Md (v1) | WRGSBASE Ry (F3),(11B)
 4: XSAVE
 5: XRSTOR | lfence (11B)
-6: mfence (11B)
+6: XSAVEOPT | mfence (11B)
 7: clflush | sfence (11B)
 EndTable
 
@@ -872,6 +926,12 @@ GrpTable: Grp16
 3: prefetch T2
 EndTable
 
+GrpTable: Grp17
+1: BLSR By,Ey (v)
+2: BLSMSK By,Ey (v)
+3: BLSI By,Ey (v)
+EndTable
+
 # AMD's Prefetch Group
 GrpTable: GrpP
 0: PREFETCH
index 446902b2a6b6a2fac5080d062e8d86b1caea8aae..1599f568f0e2fc5e7eef53c63fdce878820b1b25 100644 (file)
@@ -4,9 +4,8 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
                oprof.o cpu_buffer.o buffer_sync.o \
                event_buffer.o oprofile_files.o \
                oprofilefs.o oprofile_stats.o  \
-               timer_int.o )
+               timer_int.o nmi_timer_int.o )
 
 oprofile-y                             := $(DRIVER_OBJS) init.o backtrace.o
 oprofile-$(CONFIG_X86_LOCAL_APIC)      += nmi_int.o op_model_amd.o \
                                           op_model_ppro.o op_model_p4.o
-oprofile-$(CONFIG_X86_IO_APIC)         += nmi_timer_int.o
index f148cf65267836d66e1fa666d612dca5669950c3..9e138d00ad36d1cd900284ba6549f9d64b19122e 100644 (file)
  * with the NMI mode driver.
  */
 
+#ifdef CONFIG_X86_LOCAL_APIC
 extern int op_nmi_init(struct oprofile_operations *ops);
-extern int op_nmi_timer_init(struct oprofile_operations *ops);
 extern void op_nmi_exit(void);
-extern void x86_backtrace(struct pt_regs * const regs, unsigned int depth);
+#else
+static int op_nmi_init(struct oprofile_operations *ops) { return -ENODEV; }
+static void op_nmi_exit(void) { }
+#endif
 
-static int nmi_timer;
+extern void x86_backtrace(struct pt_regs * const regs, unsigned int depth);
 
 int __init oprofile_arch_init(struct oprofile_operations *ops)
 {
-       int ret;
-
-       ret = -ENODEV;
-
-#ifdef CONFIG_X86_LOCAL_APIC
-       ret = op_nmi_init(ops);
-#endif
-       nmi_timer = (ret != 0);
-#ifdef CONFIG_X86_IO_APIC
-       if (nmi_timer)
-               ret = op_nmi_timer_init(ops);
-#endif
        ops->backtrace = x86_backtrace;
-
-       return ret;
+       return op_nmi_init(ops);
 }
 
-
 void oprofile_arch_exit(void)
 {
-#ifdef CONFIG_X86_LOCAL_APIC
-       if (!nmi_timer)
-               op_nmi_exit();
-#endif
+       op_nmi_exit();
 }
index 75f9528e037283b4dabaef93f8bcf9ab3c9984fb..26b8a8514ee566e8a7d235a06de1037c2641adec 100644 (file)
@@ -595,24 +595,36 @@ static int __init p4_init(char **cpu_type)
        return 0;
 }
 
-static int force_arch_perfmon;
-static int force_cpu_type(const char *str, struct kernel_param *kp)
+enum __force_cpu_type {
+       reserved = 0,           /* do not force */
+       timer,
+       arch_perfmon,
+};
+
+static int force_cpu_type;
+
+static int set_cpu_type(const char *str, struct kernel_param *kp)
 {
-       if (!strcmp(str, "arch_perfmon")) {
-               force_arch_perfmon = 1;
+       if (!strcmp(str, "timer")) {
+               force_cpu_type = timer;
+               printk(KERN_INFO "oprofile: forcing NMI timer mode\n");
+       } else if (!strcmp(str, "arch_perfmon")) {
+               force_cpu_type = arch_perfmon;
                printk(KERN_INFO "oprofile: forcing architectural perfmon\n");
+       } else {
+               force_cpu_type = 0;
        }
 
        return 0;
 }
-module_param_call(cpu_type, force_cpu_type, NULL, NULL, 0);
+module_param_call(cpu_type, set_cpu_type, NULL, NULL, 0);
 
 static int __init ppro_init(char **cpu_type)
 {
        __u8 cpu_model = boot_cpu_data.x86_model;
        struct op_x86_model_spec *spec = &op_ppro_spec; /* default */
 
-       if (force_arch_perfmon && cpu_has_arch_perfmon)
+       if (force_cpu_type == arch_perfmon && cpu_has_arch_perfmon)
                return 0;
 
        /*
@@ -679,6 +691,9 @@ int __init op_nmi_init(struct oprofile_operations *ops)
        if (!cpu_has_apic)
                return -ENODEV;
 
+       if (force_cpu_type == timer)
+               return -ENODEV;
+
        switch (vendor) {
        case X86_VENDOR_AMD:
                /* Needs to be at least an Athlon (or hammer in 32bit mode) */
diff --git a/arch/x86/oprofile/nmi_timer_int.c b/arch/x86/oprofile/nmi_timer_int.c
deleted file mode 100644 (file)
index 7f8052c..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * @file nmi_timer_int.c
- *
- * @remark Copyright 2003 OProfile authors
- * @remark Read the file COPYING
- *
- * @author Zwane Mwaikambo <zwane@linuxpower.ca>
- */
-
-#include <linux/init.h>
-#include <linux/smp.h>
-#include <linux/errno.h>
-#include <linux/oprofile.h>
-#include <linux/rcupdate.h>
-#include <linux/kdebug.h>
-
-#include <asm/nmi.h>
-#include <asm/apic.h>
-#include <asm/ptrace.h>
-
-static int profile_timer_exceptions_notify(unsigned int val, struct pt_regs *regs)
-{
-       oprofile_add_sample(regs, 0);
-       return NMI_HANDLED;
-}
-
-static int timer_start(void)
-{
-       if (register_nmi_handler(NMI_LOCAL, profile_timer_exceptions_notify,
-                                       0, "oprofile-timer"))
-               return 1;
-       return 0;
-}
-
-
-static void timer_stop(void)
-{
-       unregister_nmi_handler(NMI_LOCAL, "oprofile-timer");
-       synchronize_sched();  /* Allow already-started NMIs to complete. */
-}
-
-
-int __init op_nmi_timer_init(struct oprofile_operations *ops)
-{
-       ops->start = timer_start;
-       ops->stop = timer_stop;
-       ops->cpu_type = "timer";
-       printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
-       return 0;
-}
index f82082677337e8fc769ebdef16e747e4b6ce00ba..d511aa97533abd5d2d3f2144a44d5026d638bcd5 100644 (file)
@@ -18,14 +18,21 @@ chkobjdump = $(srctree)/arch/x86/tools/chkobjdump.awk
 quiet_cmd_posttest = TEST    $@
       cmd_posttest = ($(OBJDUMP) -v | $(AWK) -f $(chkobjdump)) || $(OBJDUMP) -d -j .text $(objtree)/vmlinux | $(AWK) -f $(distill_awk) | $(obj)/test_get_len $(posttest_64bit) $(posttest_verbose)
 
-posttest: $(obj)/test_get_len vmlinux
+quiet_cmd_sanitytest = TEST    $@
+      cmd_sanitytest = $(obj)/insn_sanity $(posttest_64bit) -m 1000000
+
+posttest: $(obj)/test_get_len vmlinux $(obj)/insn_sanity
        $(call cmd,posttest)
+       $(call cmd,sanitytest)
 
-hostprogs-y    := test_get_len
+hostprogs-y    += test_get_len insn_sanity
 
 # -I needed for generated C source and C source which in the kernel tree.
 HOSTCFLAGS_test_get_len.o := -Wall -I$(objtree)/arch/x86/lib/ -I$(srctree)/arch/x86/include/ -I$(srctree)/arch/x86/lib/ -I$(srctree)/include/
 
+HOSTCFLAGS_insn_sanity.o := -Wall -I$(objtree)/arch/x86/lib/ -I$(srctree)/arch/x86/include/ -I$(srctree)/arch/x86/lib/ -I$(srctree)/include/
+
 # Dependencies are also needed.
 $(obj)/test_get_len.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c
 
+$(obj)/insn_sanity.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c
index eaf11f52fc0bd8e74513ad8e83a65cb37c1ba3cc..5f6a5b6c3a159842b4d3efc9aa3f45abb0a605d4 100644 (file)
@@ -47,7 +47,7 @@ BEGIN {
        sep_expr = "^\\|$"
        group_expr = "^Grp[0-9A-Za-z]+"
 
-       imm_expr = "^[IJAO][a-z]"
+       imm_expr = "^[IJAOL][a-z]"
        imm_flag["Ib"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
        imm_flag["Jb"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
        imm_flag["Iw"] = "INAT_MAKE_IMM(INAT_IMM_WORD)"
@@ -59,6 +59,7 @@ BEGIN {
        imm_flag["Iv"] = "INAT_MAKE_IMM(INAT_IMM_VWORD)"
        imm_flag["Ob"] = "INAT_MOFFSET"
        imm_flag["Ov"] = "INAT_MOFFSET"
+       imm_flag["Lx"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
 
        modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])"
        force64_expr = "\\([df]64\\)"
@@ -70,8 +71,12 @@ BEGIN {
        lprefix3_expr = "\\(F2\\)"
        max_lprefix = 4
 
-       vexok_expr = "\\(VEX\\)"
-       vexonly_expr = "\\(oVEX\\)"
+       # All opcodes starting with lower-case 'v' or with (v1) superscript
+       # accepts VEX prefix
+       vexok_opcode_expr = "^v.*"
+       vexok_expr = "\\(v1\\)"
+       # All opcodes with (v) superscript supports *only* VEX prefix
+       vexonly_expr = "\\(v\\)"
 
        prefix_expr = "\\(Prefix\\)"
        prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ"
@@ -85,8 +90,8 @@ BEGIN {
        prefix_num["SEG=GS"] = "INAT_PFX_GS"
        prefix_num["SEG=SS"] = "INAT_PFX_SS"
        prefix_num["Address-Size"] = "INAT_PFX_ADDRSZ"
-       prefix_num["2bytes-VEX"] = "INAT_PFX_VEX2"
-       prefix_num["3bytes-VEX"] = "INAT_PFX_VEX3"
+       prefix_num["VEX+1byte"] = "INAT_PFX_VEX2"
+       prefix_num["VEX+2byte"] = "INAT_PFX_VEX3"
 
        clear_vars()
 }
@@ -310,12 +315,10 @@ function convert_operands(count,opnd,       i,j,imm,mod)
                if (match(opcode, fpu_expr))
                        flags = add_flags(flags, "INAT_MODRM")
 
-               # check VEX only code
+               # check VEX codes
                if (match(ext, vexonly_expr))
                        flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY")
-
-               # check VEX only code
-               if (match(ext, vexok_expr))
+               else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))
                        flags = add_flags(flags, "INAT_VEXOK")
 
                # check prefixes
diff --git a/arch/x86/tools/insn_sanity.c b/arch/x86/tools/insn_sanity.c
new file mode 100644 (file)
index 0000000..cc2f8c1
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * x86 decoder sanity test - based on test_get_insn.c
+ *
+ * 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.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2009
+ * Copyright (C) Hitachi, Ltd., 2011
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define unlikely(cond) (cond)
+#define ARRAY_SIZE(a)  (sizeof(a)/sizeof(a[0]))
+
+#include <asm/insn.h>
+#include <inat.c>
+#include <insn.c>
+
+/*
+ * Test of instruction analysis against tampering.
+ * Feed random binary to instruction decoder and ensure not to
+ * access out-of-instruction-buffer.
+ */
+
+#define DEFAULT_MAX_ITER       10000
+#define INSN_NOP 0x90
+
+static const char      *prog;          /* Program name */
+static int             verbose;        /* Verbosity */
+static int             x86_64;         /* x86-64 bit mode flag */
+static unsigned int    seed;           /* Random seed */
+static unsigned long   iter_start;     /* Start of iteration number */
+static unsigned long   iter_end = DEFAULT_MAX_ITER;    /* End of iteration number */
+static FILE            *input_file;    /* Input file name */
+
+static void usage(const char *err)
+{
+       if (err)
+               fprintf(stderr, "Error: %s\n\n", err);
+       fprintf(stderr, "Usage: %s [-y|-n|-v] [-s seed[,no]] [-m max] [-i input]\n", prog);
+       fprintf(stderr, "\t-y   64bit mode\n");
+       fprintf(stderr, "\t-n   32bit mode\n");
+       fprintf(stderr, "\t-v   Verbosity(-vv dumps any decoded result)\n");
+       fprintf(stderr, "\t-s   Give a random seed (and iteration number)\n");
+       fprintf(stderr, "\t-m   Give a maximum iteration number\n");
+       fprintf(stderr, "\t-i   Give an input file with decoded binary\n");
+       exit(1);
+}
+
+static void dump_field(FILE *fp, const char *name, const char *indent,
+                      struct insn_field *field)
+{
+       fprintf(fp, "%s.%s = {\n", indent, name);
+       fprintf(fp, "%s\t.value = %d, bytes[] = {%x, %x, %x, %x},\n",
+               indent, field->value, field->bytes[0], field->bytes[1],
+               field->bytes[2], field->bytes[3]);
+       fprintf(fp, "%s\t.got = %d, .nbytes = %d},\n", indent,
+               field->got, field->nbytes);
+}
+
+static void dump_insn(FILE *fp, struct insn *insn)
+{
+       fprintf(fp, "Instruction = {\n");
+       dump_field(fp, "prefixes", "\t",        &insn->prefixes);
+       dump_field(fp, "rex_prefix", "\t",      &insn->rex_prefix);
+       dump_field(fp, "vex_prefix", "\t",      &insn->vex_prefix);
+       dump_field(fp, "opcode", "\t",          &insn->opcode);
+       dump_field(fp, "modrm", "\t",           &insn->modrm);
+       dump_field(fp, "sib", "\t",             &insn->sib);
+       dump_field(fp, "displacement", "\t",    &insn->displacement);
+       dump_field(fp, "immediate1", "\t",      &insn->immediate1);
+       dump_field(fp, "immediate2", "\t",      &insn->immediate2);
+       fprintf(fp, "\t.attr = %x, .opnd_bytes = %d, .addr_bytes = %d,\n",
+               insn->attr, insn->opnd_bytes, insn->addr_bytes);
+       fprintf(fp, "\t.length = %d, .x86_64 = %d, .kaddr = %p}\n",
+               insn->length, insn->x86_64, insn->kaddr);
+}
+
+static void dump_stream(FILE *fp, const char *msg, unsigned long nr_iter,
+                       unsigned char *insn_buf, struct insn *insn)
+{
+       int i;
+
+       fprintf(fp, "%s:\n", msg);
+
+       dump_insn(fp, insn);
+
+       fprintf(fp, "You can reproduce this with below command(s);\n");
+
+       /* Input a decoded instruction sequence directly */
+       fprintf(fp, " $ echo ");
+       for (i = 0; i < MAX_INSN_SIZE; i++)
+               fprintf(fp, " %02x", insn_buf[i]);
+       fprintf(fp, " | %s -i -\n", prog);
+
+       if (!input_file) {
+               fprintf(fp, "Or \n");
+               /* Give a seed and iteration number */
+               fprintf(fp, " $ %s -s 0x%x,%lu\n", prog, seed, nr_iter);
+       }
+}
+
+static void init_random_seed(void)
+{
+       int fd;
+
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd < 0)
+               goto fail;
+
+       if (read(fd, &seed, sizeof(seed)) != sizeof(seed))
+               goto fail;
+
+       close(fd);
+       return;
+fail:
+       usage("Failed to open /dev/urandom");
+}
+
+/* Read given instruction sequence from the input file */
+static int read_next_insn(unsigned char *insn_buf)
+{
+       char buf[256]  = "", *tmp;
+       int i;
+
+       tmp = fgets(buf, ARRAY_SIZE(buf), input_file);
+       if (tmp == NULL || feof(input_file))
+               return 0;
+
+       for (i = 0; i < MAX_INSN_SIZE; i++) {
+               insn_buf[i] = (unsigned char)strtoul(tmp, &tmp, 16);
+               if (*tmp != ' ')
+                       break;
+       }
+
+       return i;
+}
+
+static int generate_insn(unsigned char *insn_buf)
+{
+       int i;
+
+       if (input_file)
+               return read_next_insn(insn_buf);
+
+       /* Fills buffer with random binary up to MAX_INSN_SIZE */
+       for (i = 0; i < MAX_INSN_SIZE - 1; i += 2)
+               *(unsigned short *)(&insn_buf[i]) = random() & 0xffff;
+
+       while (i < MAX_INSN_SIZE)
+               insn_buf[i++] = random() & 0xff;
+
+       return i;
+}
+
+static void parse_args(int argc, char **argv)
+{
+       int c;
+       char *tmp = NULL;
+       int set_seed = 0;
+
+       prog = argv[0];
+       while ((c = getopt(argc, argv, "ynvs:m:i:")) != -1) {
+               switch (c) {
+               case 'y':
+                       x86_64 = 1;
+                       break;
+               case 'n':
+                       x86_64 = 0;
+                       break;
+               case 'v':
+                       verbose++;
+                       break;
+               case 'i':
+                       if (strcmp("-", optarg) == 0)
+                               input_file = stdin;
+                       else
+                               input_file = fopen(optarg, "r");
+                       if (!input_file)
+                               usage("Failed to open input file");
+                       break;
+               case 's':
+                       seed = (unsigned int)strtoul(optarg, &tmp, 0);
+                       if (*tmp == ',') {
+                               optarg = tmp + 1;
+                               iter_start = strtoul(optarg, &tmp, 0);
+                       }
+                       if (*tmp != '\0' || tmp == optarg)
+                               usage("Failed to parse seed");
+                       set_seed = 1;
+                       break;
+               case 'm':
+                       iter_end = strtoul(optarg, &tmp, 0);
+                       if (*tmp != '\0' || tmp == optarg)
+                               usage("Failed to parse max_iter");
+                       break;
+               default:
+                       usage(NULL);
+               }
+       }
+
+       /* Check errors */
+       if (iter_end < iter_start)
+               usage("Max iteration number must be bigger than iter-num");
+
+       if (set_seed && input_file)
+               usage("Don't use input file (-i) with random seed (-s)");
+
+       /* Initialize random seed */
+       if (!input_file) {
+               if (!set_seed)  /* No seed is given */
+                       init_random_seed();
+               srand(seed);
+       }
+}
+
+int main(int argc, char **argv)
+{
+       struct insn insn;
+       int insns = 0;
+       int errors = 0;
+       unsigned long i;
+       unsigned char insn_buf[MAX_INSN_SIZE * 2];
+
+       parse_args(argc, argv);
+
+       /* Prepare stop bytes with NOPs */
+       memset(insn_buf + MAX_INSN_SIZE, INSN_NOP, MAX_INSN_SIZE);
+
+       for (i = 0; i < iter_end; i++) {
+               if (generate_insn(insn_buf) <= 0)
+                       break;
+
+               if (i < iter_start)     /* Skip to given iteration number */
+                       continue;
+
+               /* Decode an instruction */
+               insn_init(&insn, insn_buf, x86_64);
+               insn_get_length(&insn);
+
+               if (insn.next_byte <= insn.kaddr ||
+                   insn.kaddr + MAX_INSN_SIZE < insn.next_byte) {
+                       /* Access out-of-range memory */
+                       dump_stream(stderr, "Error: Found an access violation", i, insn_buf, &insn);
+                       errors++;
+               } else if (verbose && !insn_complete(&insn))
+                       dump_stream(stdout, "Info: Found an undecodable input", i, insn_buf, &insn);
+               else if (verbose >= 2)
+                       dump_insn(stdout, &insn);
+               insns++;
+       }
+
+       fprintf(stdout, "%s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n", (errors) ? "Failure" : "Success", insns, (input_file) ? "given" : "random", errors, seed);
+
+       return errors ? 1 : 0;
+}
diff --git a/drivers/oprofile/nmi_timer_int.c b/drivers/oprofile/nmi_timer_int.c
new file mode 100644 (file)
index 0000000..76f1c93
--- /dev/null
@@ -0,0 +1,173 @@
+/**
+ * @file nmi_timer_int.c
+ *
+ * @remark Copyright 2011 Advanced Micro Devices, Inc.
+ *
+ * @author Robert Richter <robert.richter@amd.com>
+ */
+
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/errno.h>
+#include <linux/oprofile.h>
+#include <linux/perf_event.h>
+
+#ifdef CONFIG_OPROFILE_NMI_TIMER
+
+static DEFINE_PER_CPU(struct perf_event *, nmi_timer_events);
+static int ctr_running;
+
+static struct perf_event_attr nmi_timer_attr = {
+       .type           = PERF_TYPE_HARDWARE,
+       .config         = PERF_COUNT_HW_CPU_CYCLES,
+       .size           = sizeof(struct perf_event_attr),
+       .pinned         = 1,
+       .disabled       = 1,
+};
+
+static void nmi_timer_callback(struct perf_event *event,
+                              struct perf_sample_data *data,
+                              struct pt_regs *regs)
+{
+       event->hw.interrupts = 0;       /* don't throttle interrupts */
+       oprofile_add_sample(regs, 0);
+}
+
+static int nmi_timer_start_cpu(int cpu)
+{
+       struct perf_event *event = per_cpu(nmi_timer_events, cpu);
+
+       if (!event) {
+               event = perf_event_create_kernel_counter(&nmi_timer_attr, cpu, NULL,
+                                                        nmi_timer_callback, NULL);
+               if (IS_ERR(event))
+                       return PTR_ERR(event);
+               per_cpu(nmi_timer_events, cpu) = event;
+       }
+
+       if (event && ctr_running)
+               perf_event_enable(event);
+
+       return 0;
+}
+
+static void nmi_timer_stop_cpu(int cpu)
+{
+       struct perf_event *event = per_cpu(nmi_timer_events, cpu);
+
+       if (event && ctr_running)
+               perf_event_disable(event);
+}
+
+static int nmi_timer_cpu_notifier(struct notifier_block *b, unsigned long action,
+                                 void *data)
+{
+       int cpu = (unsigned long)data;
+       switch (action) {
+       case CPU_DOWN_FAILED:
+       case CPU_ONLINE:
+               nmi_timer_start_cpu(cpu);
+               break;
+       case CPU_DOWN_PREPARE:
+               nmi_timer_stop_cpu(cpu);
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block nmi_timer_cpu_nb = {
+       .notifier_call = nmi_timer_cpu_notifier
+};
+
+static int nmi_timer_start(void)
+{
+       int cpu;
+
+       get_online_cpus();
+       ctr_running = 1;
+       for_each_online_cpu(cpu)
+               nmi_timer_start_cpu(cpu);
+       put_online_cpus();
+
+       return 0;
+}
+
+static void nmi_timer_stop(void)
+{
+       int cpu;
+
+       get_online_cpus();
+       for_each_online_cpu(cpu)
+               nmi_timer_stop_cpu(cpu);
+       ctr_running = 0;
+       put_online_cpus();
+}
+
+static void nmi_timer_shutdown(void)
+{
+       struct perf_event *event;
+       int cpu;
+
+       get_online_cpus();
+       unregister_cpu_notifier(&nmi_timer_cpu_nb);
+       for_each_possible_cpu(cpu) {
+               event = per_cpu(nmi_timer_events, cpu);
+               if (!event)
+                       continue;
+               perf_event_disable(event);
+               per_cpu(nmi_timer_events, cpu) = NULL;
+               perf_event_release_kernel(event);
+       }
+
+       put_online_cpus();
+}
+
+static int nmi_timer_setup(void)
+{
+       int cpu, err;
+       u64 period;
+
+       /* clock cycles per tick: */
+       period = (u64)cpu_khz * 1000;
+       do_div(period, HZ);
+       nmi_timer_attr.sample_period = period;
+
+       get_online_cpus();
+       err = register_cpu_notifier(&nmi_timer_cpu_nb);
+       if (err)
+               goto out;
+       /* can't attach events to offline cpus: */
+       for_each_online_cpu(cpu) {
+               err = nmi_timer_start_cpu(cpu);
+               if (err)
+                       break;
+       }
+       if (err)
+               nmi_timer_shutdown();
+out:
+       put_online_cpus();
+       return err;
+}
+
+int __init op_nmi_timer_init(struct oprofile_operations *ops)
+{
+       int err = 0;
+
+       err = nmi_timer_setup();
+       if (err)
+               return err;
+       nmi_timer_shutdown();           /* only check, don't alloc */
+
+       ops->create_files       = NULL;
+       ops->setup              = nmi_timer_setup;
+       ops->shutdown           = nmi_timer_shutdown;
+       ops->start              = nmi_timer_start;
+       ops->stop               = nmi_timer_stop;
+       ops->cpu_type           = "timer";
+
+       printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
+
+       return 0;
+}
+
+#endif
index f8c752e408a663d55adf7e84ca4fb42aa93d1d02..ed2c3ec07024d0bcb04686a64510f1a607bfded7 100644 (file)
@@ -246,37 +246,31 @@ static int __init oprofile_init(void)
        int err;
 
        /* always init architecture to setup backtrace support */
+       timer_mode = 0;
        err = oprofile_arch_init(&oprofile_ops);
+       if (!err) {
+               if (!timer && !oprofilefs_register())
+                       return 0;
+               oprofile_arch_exit();
+       }
 
-       timer_mode = err || timer;      /* fall back to timer mode on errors */
-       if (timer_mode) {
-               if (!err)
-                       oprofile_arch_exit();
+       /* setup timer mode: */
+       timer_mode = 1;
+       /* no nmi timer mode if oprofile.timer is set */
+       if (timer || op_nmi_timer_init(&oprofile_ops)) {
                err = oprofile_timer_init(&oprofile_ops);
                if (err)
                        return err;
        }
 
-       err = oprofilefs_register();
-       if (!err)
-               return 0;
-
-       /* failed */
-       if (timer_mode)
-               oprofile_timer_exit();
-       else
-               oprofile_arch_exit();
-
-       return err;
+       return oprofilefs_register();
 }
 
 
 static void __exit oprofile_exit(void)
 {
        oprofilefs_unregister();
-       if (timer_mode)
-               oprofile_timer_exit();
-       else
+       if (!timer_mode)
                oprofile_arch_exit();
 }
 
index 177b73de5e5f158cbb31fe27f8e31b9363c3a255..769fb0fcac4449bd2865905cdf92f60f734e2e23 100644 (file)
@@ -36,6 +36,15 @@ struct dentry;
 void oprofile_create_files(struct super_block *sb, struct dentry *root);
 int oprofile_timer_init(struct oprofile_operations *ops);
 void oprofile_timer_exit(void);
+#ifdef CONFIG_OPROFILE_NMI_TIMER
+int op_nmi_timer_init(struct oprofile_operations *ops);
+#else
+static inline int op_nmi_timer_init(struct oprofile_operations *ops)
+{
+       return -ENODEV;
+}
+#endif
+
 
 int oprofile_set_ulong(unsigned long *addr, unsigned long val);
 int oprofile_set_timeout(unsigned long time);
index 878fba1265829cdab586a145d86a332b5ce32874..93404f72dfa8197a9f2292c70e5988424614a9f0 100644 (file)
@@ -97,24 +97,24 @@ static struct notifier_block __refdata oprofile_cpu_notifier = {
        .notifier_call = oprofile_cpu_notify,
 };
 
-int oprofile_timer_init(struct oprofile_operations *ops)
+static int oprofile_hrtimer_setup(void)
 {
-       int rc;
-
-       rc = register_hotcpu_notifier(&oprofile_cpu_notifier);
-       if (rc)
-               return rc;
-       ops->create_files = NULL;
-       ops->setup = NULL;
-       ops->shutdown = NULL;
-       ops->start = oprofile_hrtimer_start;
-       ops->stop = oprofile_hrtimer_stop;
-       ops->cpu_type = "timer";
-       printk(KERN_INFO "oprofile: using timer interrupt.\n");
-       return 0;
+       return register_hotcpu_notifier(&oprofile_cpu_notifier);
 }
 
-void oprofile_timer_exit(void)
+static void oprofile_hrtimer_shutdown(void)
 {
        unregister_hotcpu_notifier(&oprofile_cpu_notifier);
 }
+
+int oprofile_timer_init(struct oprofile_operations *ops)
+{
+       ops->create_files       = NULL;
+       ops->setup              = oprofile_hrtimer_setup;
+       ops->shutdown           = oprofile_hrtimer_shutdown;
+       ops->start              = oprofile_hrtimer_start;
+       ops->stop               = oprofile_hrtimer_stop;
+       ops->cpu_type           = "timer";
+       printk(KERN_INFO "oprofile: using timer interrupt.\n");
+       return 0;
+}
index 89e5e8aa4c36605de76beda71b43576debb3536e..22d901f9caf44ec245edb24fb742a4664afadb95 100644 (file)
@@ -2,5 +2,5 @@ ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_core.o = -pg
 endif
 
-obj-y := core.o ring_buffer.o
+obj-y := core.o ring_buffer.o callchain.o
 obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c
new file mode 100644 (file)
index 0000000..057e24b
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Performance events callchain code, extracted from core.c:
+ *
+ *  Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
+ *  Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar
+ *  Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ *  Copyright  ©  2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
+ *
+ * For licensing details see kernel-base/COPYING
+ */
+
+#include <linux/perf_event.h>
+#include <linux/slab.h>
+#include "internal.h"
+
+struct callchain_cpus_entries {
+       struct rcu_head                 rcu_head;
+       struct perf_callchain_entry     *cpu_entries[0];
+};
+
+static DEFINE_PER_CPU(int, callchain_recursion[PERF_NR_CONTEXTS]);
+static atomic_t nr_callchain_events;
+static DEFINE_MUTEX(callchain_mutex);
+static struct callchain_cpus_entries *callchain_cpus_entries;
+
+
+__weak void perf_callchain_kernel(struct perf_callchain_entry *entry,
+                                 struct pt_regs *regs)
+{
+}
+
+__weak void perf_callchain_user(struct perf_callchain_entry *entry,
+                               struct pt_regs *regs)
+{
+}
+
+static void release_callchain_buffers_rcu(struct rcu_head *head)
+{
+       struct callchain_cpus_entries *entries;
+       int cpu;
+
+       entries = container_of(head, struct callchain_cpus_entries, rcu_head);
+
+       for_each_possible_cpu(cpu)
+               kfree(entries->cpu_entries[cpu]);
+
+       kfree(entries);
+}
+
+static void release_callchain_buffers(void)
+{
+       struct callchain_cpus_entries *entries;
+
+       entries = callchain_cpus_entries;
+       rcu_assign_pointer(callchain_cpus_entries, NULL);
+       call_rcu(&entries->rcu_head, release_callchain_buffers_rcu);
+}
+
+static int alloc_callchain_buffers(void)
+{
+       int cpu;
+       int size;
+       struct callchain_cpus_entries *entries;
+
+       /*
+        * We can't use the percpu allocation API for data that can be
+        * accessed from NMI. Use a temporary manual per cpu allocation
+        * until that gets sorted out.
+        */
+       size = offsetof(struct callchain_cpus_entries, cpu_entries[nr_cpu_ids]);
+
+       entries = kzalloc(size, GFP_KERNEL);
+       if (!entries)
+               return -ENOMEM;
+
+       size = sizeof(struct perf_callchain_entry) * PERF_NR_CONTEXTS;
+
+       for_each_possible_cpu(cpu) {
+               entries->cpu_entries[cpu] = kmalloc_node(size, GFP_KERNEL,
+                                                        cpu_to_node(cpu));
+               if (!entries->cpu_entries[cpu])
+                       goto fail;
+       }
+
+       rcu_assign_pointer(callchain_cpus_entries, entries);
+
+       return 0;
+
+fail:
+       for_each_possible_cpu(cpu)
+               kfree(entries->cpu_entries[cpu]);
+       kfree(entries);
+
+       return -ENOMEM;
+}
+
+int get_callchain_buffers(void)
+{
+       int err = 0;
+       int count;
+
+       mutex_lock(&callchain_mutex);
+
+       count = atomic_inc_return(&nr_callchain_events);
+       if (WARN_ON_ONCE(count < 1)) {
+               err = -EINVAL;
+               goto exit;
+       }
+
+       if (count > 1) {
+               /* If the allocation failed, give up */
+               if (!callchain_cpus_entries)
+                       err = -ENOMEM;
+               goto exit;
+       }
+
+       err = alloc_callchain_buffers();
+       if (err)
+               release_callchain_buffers();
+exit:
+       mutex_unlock(&callchain_mutex);
+
+       return err;
+}
+
+void put_callchain_buffers(void)
+{
+       if (atomic_dec_and_mutex_lock(&nr_callchain_events, &callchain_mutex)) {
+               release_callchain_buffers();
+               mutex_unlock(&callchain_mutex);
+       }
+}
+
+static struct perf_callchain_entry *get_callchain_entry(int *rctx)
+{
+       int cpu;
+       struct callchain_cpus_entries *entries;
+
+       *rctx = get_recursion_context(__get_cpu_var(callchain_recursion));
+       if (*rctx == -1)
+               return NULL;
+
+       entries = rcu_dereference(callchain_cpus_entries);
+       if (!entries)
+               return NULL;
+
+       cpu = smp_processor_id();
+
+       return &entries->cpu_entries[cpu][*rctx];
+}
+
+static void
+put_callchain_entry(int rctx)
+{
+       put_recursion_context(__get_cpu_var(callchain_recursion), rctx);
+}
+
+struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
+{
+       int rctx;
+       struct perf_callchain_entry *entry;
+
+
+       entry = get_callchain_entry(&rctx);
+       if (rctx == -1)
+               return NULL;
+
+       if (!entry)
+               goto exit_put;
+
+       entry->nr = 0;
+
+       if (!user_mode(regs)) {
+               perf_callchain_store(entry, PERF_CONTEXT_KERNEL);
+               perf_callchain_kernel(entry, regs);
+               if (current->mm)
+                       regs = task_pt_regs(current);
+               else
+                       regs = NULL;
+       }
+
+       if (regs) {
+               perf_callchain_store(entry, PERF_CONTEXT_USER);
+               perf_callchain_user(entry, regs);
+       }
+
+exit_put:
+       put_callchain_entry(rctx);
+
+       return entry;
+}
index 600c1629b64db7c3fdb88c1e4c6d08412118ed1b..a355ffb0b28f8974c42fb3797b706da040a43789 100644 (file)
@@ -1325,6 +1325,7 @@ retry:
        }
        raw_spin_unlock_irq(&ctx->lock);
 }
+EXPORT_SYMBOL_GPL(perf_event_disable);
 
 static void perf_set_shadow_time(struct perf_event *event,
                                 struct perf_event_context *ctx,
@@ -1809,6 +1810,7 @@ retry:
 out:
        raw_spin_unlock_irq(&ctx->lock);
 }
+EXPORT_SYMBOL_GPL(perf_event_enable);
 
 int perf_event_refresh(struct perf_event *event, int refresh)
 {
@@ -2573,215 +2575,6 @@ static u64 perf_event_read(struct perf_event *event)
        return perf_event_count(event);
 }
 
-/*
- * Callchain support
- */
-
-struct callchain_cpus_entries {
-       struct rcu_head                 rcu_head;
-       struct perf_callchain_entry     *cpu_entries[0];
-};
-
-static DEFINE_PER_CPU(int, callchain_recursion[PERF_NR_CONTEXTS]);
-static atomic_t nr_callchain_events;
-static DEFINE_MUTEX(callchain_mutex);
-struct callchain_cpus_entries *callchain_cpus_entries;
-
-
-__weak void perf_callchain_kernel(struct perf_callchain_entry *entry,
-                                 struct pt_regs *regs)
-{
-}
-
-__weak void perf_callchain_user(struct perf_callchain_entry *entry,
-                               struct pt_regs *regs)
-{
-}
-
-static void release_callchain_buffers_rcu(struct rcu_head *head)
-{
-       struct callchain_cpus_entries *entries;
-       int cpu;
-
-       entries = container_of(head, struct callchain_cpus_entries, rcu_head);
-
-       for_each_possible_cpu(cpu)
-               kfree(entries->cpu_entries[cpu]);
-
-       kfree(entries);
-}
-
-static void release_callchain_buffers(void)
-{
-       struct callchain_cpus_entries *entries;
-
-       entries = callchain_cpus_entries;
-       rcu_assign_pointer(callchain_cpus_entries, NULL);
-       call_rcu(&entries->rcu_head, release_callchain_buffers_rcu);
-}
-
-static int alloc_callchain_buffers(void)
-{
-       int cpu;
-       int size;
-       struct callchain_cpus_entries *entries;
-
-       /*
-        * We can't use the percpu allocation API for data that can be
-        * accessed from NMI. Use a temporary manual per cpu allocation
-        * until that gets sorted out.
-        */
-       size = offsetof(struct callchain_cpus_entries, cpu_entries[nr_cpu_ids]);
-
-       entries = kzalloc(size, GFP_KERNEL);
-       if (!entries)
-               return -ENOMEM;
-
-       size = sizeof(struct perf_callchain_entry) * PERF_NR_CONTEXTS;
-
-       for_each_possible_cpu(cpu) {
-               entries->cpu_entries[cpu] = kmalloc_node(size, GFP_KERNEL,
-                                                        cpu_to_node(cpu));
-               if (!entries->cpu_entries[cpu])
-                       goto fail;
-       }
-
-       rcu_assign_pointer(callchain_cpus_entries, entries);
-
-       return 0;
-
-fail:
-       for_each_possible_cpu(cpu)
-               kfree(entries->cpu_entries[cpu]);
-       kfree(entries);
-
-       return -ENOMEM;
-}
-
-static int get_callchain_buffers(void)
-{
-       int err = 0;
-       int count;
-
-       mutex_lock(&callchain_mutex);
-
-       count = atomic_inc_return(&nr_callchain_events);
-       if (WARN_ON_ONCE(count < 1)) {
-               err = -EINVAL;
-               goto exit;
-       }
-
-       if (count > 1) {
-               /* If the allocation failed, give up */
-               if (!callchain_cpus_entries)
-                       err = -ENOMEM;
-               goto exit;
-       }
-
-       err = alloc_callchain_buffers();
-       if (err)
-               release_callchain_buffers();
-exit:
-       mutex_unlock(&callchain_mutex);
-
-       return err;
-}
-
-static void put_callchain_buffers(void)
-{
-       if (atomic_dec_and_mutex_lock(&nr_callchain_events, &callchain_mutex)) {
-               release_callchain_buffers();
-               mutex_unlock(&callchain_mutex);
-       }
-}
-
-static int get_recursion_context(int *recursion)
-{
-       int rctx;
-
-       if (in_nmi())
-               rctx = 3;
-       else if (in_irq())
-               rctx = 2;
-       else if (in_softirq())
-               rctx = 1;
-       else
-               rctx = 0;
-
-       if (recursion[rctx])
-               return -1;
-
-       recursion[rctx]++;
-       barrier();
-
-       return rctx;
-}
-
-static inline void put_recursion_context(int *recursion, int rctx)
-{
-       barrier();
-       recursion[rctx]--;
-}
-
-static struct perf_callchain_entry *get_callchain_entry(int *rctx)
-{
-       int cpu;
-       struct callchain_cpus_entries *entries;
-
-       *rctx = get_recursion_context(__get_cpu_var(callchain_recursion));
-       if (*rctx == -1)
-               return NULL;
-
-       entries = rcu_dereference(callchain_cpus_entries);
-       if (!entries)
-               return NULL;
-
-       cpu = smp_processor_id();
-
-       return &entries->cpu_entries[cpu][*rctx];
-}
-
-static void
-put_callchain_entry(int rctx)
-{
-       put_recursion_context(__get_cpu_var(callchain_recursion), rctx);
-}
-
-static struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
-{
-       int rctx;
-       struct perf_callchain_entry *entry;
-
-
-       entry = get_callchain_entry(&rctx);
-       if (rctx == -1)
-               return NULL;
-
-       if (!entry)
-               goto exit_put;
-
-       entry->nr = 0;
-
-       if (!user_mode(regs)) {
-               perf_callchain_store(entry, PERF_CONTEXT_KERNEL);
-               perf_callchain_kernel(entry, regs);
-               if (current->mm)
-                       regs = task_pt_regs(current);
-               else
-                       regs = NULL;
-       }
-
-       if (regs) {
-               perf_callchain_store(entry, PERF_CONTEXT_USER);
-               perf_callchain_user(entry, regs);
-       }
-
-exit_put:
-       put_callchain_entry(rctx);
-
-       return entry;
-}
-
 /*
  * Initialize the perf_event context in a task_struct:
  */
@@ -4816,7 +4609,6 @@ static void perf_swevent_overflow(struct perf_event *event, u64 overflow,
        struct hw_perf_event *hwc = &event->hw;
        int throttle = 0;
 
-       data->period = event->hw.last_period;
        if (!overflow)
                overflow = perf_swevent_set_period(event);
 
@@ -4850,6 +4642,12 @@ static void perf_swevent_event(struct perf_event *event, u64 nr,
        if (!is_sampling_event(event))
                return;
 
+       if ((event->attr.sample_type & PERF_SAMPLE_PERIOD) && !event->attr.freq) {
+               data->period = nr;
+               return perf_swevent_overflow(event, 1, data, regs);
+       } else
+               data->period = event->hw.last_period;
+
        if (nr == 1 && hwc->sample_period == 1 && !event->attr.freq)
                return perf_swevent_overflow(event, 1, data, regs);
 
index 64568a699375f105232eb588963da8707f926295..b0b107f90afc983a0a7abd4b2f0f9519670f2275 100644 (file)
@@ -1,6 +1,10 @@
 #ifndef _KERNEL_EVENTS_INTERNAL_H
 #define _KERNEL_EVENTS_INTERNAL_H
 
+#include <linux/hardirq.h>
+
+/* Buffer handling */
+
 #define RING_BUFFER_WRITABLE           0x01
 
 struct ring_buffer {
@@ -67,7 +71,7 @@ static inline int page_order(struct ring_buffer *rb)
 }
 #endif
 
-static unsigned long perf_data_size(struct ring_buffer *rb)
+static inline unsigned long perf_data_size(struct ring_buffer *rb)
 {
        return rb->nr_pages << (PAGE_SHIFT + page_order(rb));
 }
@@ -96,4 +100,37 @@ __output_copy(struct perf_output_handle *handle,
        } while (len);
 }
 
+/* Callchain handling */
+extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs);
+extern int get_callchain_buffers(void);
+extern void put_callchain_buffers(void);
+
+static inline int get_recursion_context(int *recursion)
+{
+       int rctx;
+
+       if (in_nmi())
+               rctx = 3;
+       else if (in_irq())
+               rctx = 2;
+       else if (in_softirq())
+               rctx = 1;
+       else
+               rctx = 0;
+
+       if (recursion[rctx])
+               return -1;
+
+       recursion[rctx]++;
+       barrier();
+
+       return rctx;
+}
+
+static inline void put_recursion_context(int *recursion, int rctx)
+{
+       barrier();
+       recursion[rctx]--;
+}
+
 #endif /* _KERNEL_EVENTS_INTERNAL_H */
index e69434b070da3f922909ece9417627e11234dcd6..d2fab46a1c94e33cedb094e556a708370349f898 100644 (file)
@@ -498,36 +498,32 @@ void get_usage_chars(struct lock_class *class, char usage[LOCK_USAGE_CHARS])
        usage[i] = '\0';
 }
 
-static int __print_lock_name(struct lock_class *class)
+static void __print_lock_name(struct lock_class *class)
 {
        char str[KSYM_NAME_LEN];
        const char *name;
 
-       name = class->name;
-       if (!name)
-               name = __get_key_name(class->key, str);
-
-       return printk("%s", name);
-}
-
-static void print_lock_name(struct lock_class *class)
-{
-       char str[KSYM_NAME_LEN], usage[LOCK_USAGE_CHARS];
-       const char *name;
-
-       get_usage_chars(class, usage);
-
        name = class->name;
        if (!name) {
                name = __get_key_name(class->key, str);
-               printk(" (%s", name);
+               printk("%s", name);
        } else {
-               printk(" (%s", name);
+               printk("%s", name);
                if (class->name_version > 1)
                        printk("#%d", class->name_version);
                if (class->subclass)
                        printk("/%d", class->subclass);
        }
+}
+
+static void print_lock_name(struct lock_class *class)
+{
+       char usage[LOCK_USAGE_CHARS];
+
+       get_usage_chars(class, usage);
+
+       printk(" (");
+       __print_lock_name(class);
        printk("){%s}", usage);
 }
 
index f2bd275bb60f5c63630ee4483c02a0f33d53e46d..9e158cc84ced3878a68a06823f170f16dd1ec663 100644 (file)
@@ -2140,6 +2140,21 @@ enum print_line_t print_trace_line(struct trace_iterator *iter)
        return print_trace_fmt(iter);
 }
 
+void trace_latency_header(struct seq_file *m)
+{
+       struct trace_iterator *iter = m->private;
+
+       /* print nothing if the buffers are empty */
+       if (trace_empty(iter))
+               return;
+
+       if (iter->iter_flags & TRACE_FILE_LAT_FMT)
+               print_trace_header(m, iter);
+
+       if (!(trace_flags & TRACE_ITER_VERBOSE))
+               print_lat_help_header(m);
+}
+
 void trace_default_header(struct seq_file *m)
 {
        struct trace_iterator *iter = m->private;
index 092e1f8d18dc88757280362b8ae77b97d890c3ad..f8ec2291b522a354952fdbf9134bed2dc248f13b 100644 (file)
@@ -370,6 +370,7 @@ void trace_graph_function(struct trace_array *tr,
                    unsigned long ip,
                    unsigned long parent_ip,
                    unsigned long flags, int pc);
+void trace_latency_header(struct seq_file *m);
 void trace_default_header(struct seq_file *m);
 void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
 int trace_empty(struct trace_iterator *iter);
index 95dc31efd6dd503dbd169a159dac4c5048f5a568..f04cc3136bd370b6fbf9007cb7067ec1476892d4 100644 (file)
 #include "trace.h"
 #include "trace_output.h"
 
+#define DEFAULT_SYS_FILTER_MESSAGE                                     \
+       "### global filter ###\n"                                       \
+       "# Use this to set filters for multiple events.\n"              \
+       "# Only events with the given fields will be affected.\n"       \
+       "# If no events are modified, an error message will be displayed here"
+
 enum filter_op_ids
 {
        OP_OR,
@@ -646,7 +652,7 @@ void print_subsystem_event_filter(struct event_subsystem *system,
        if (filter && filter->filter_string)
                trace_seq_printf(s, "%s\n", filter->filter_string);
        else
-               trace_seq_printf(s, "none\n");
+               trace_seq_printf(s, DEFAULT_SYS_FILTER_MESSAGE "\n");
        mutex_unlock(&event_mutex);
 }
 
@@ -1838,7 +1844,10 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
        if (!filter)
                goto out;
 
-       replace_filter_string(filter, filter_string);
+       /* System filters just show a default message */
+       kfree(filter->filter_string);
+       filter->filter_string = NULL;
+
        /*
         * No event actually uses the system filter
         * we can free it without synchronize_sched().
@@ -1848,14 +1857,12 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
 
        parse_init(ps, filter_ops, filter_string);
        err = filter_parse(ps);
-       if (err) {
-               append_filter_err(ps, system->filter);
-               goto out;
-       }
+       if (err)
+               goto err_filter;
 
        err = replace_system_preds(system, ps, filter_string);
        if (err)
-               append_filter_err(ps, system->filter);
+               goto err_filter;
 
 out:
        filter_opstack_clear(ps);
@@ -1865,6 +1872,11 @@ out_unlock:
        mutex_unlock(&event_mutex);
 
        return err;
+
+err_filter:
+       replace_filter_string(filter, filter_string);
+       append_filter_err(ps, system->filter);
+       goto out;
 }
 
 #ifdef CONFIG_PERF_EVENTS
index 20dad0d7a1633c551eec45e7f708dca4ac48b38c..99d20e9203686420e74af5c2eca6387deb375028 100644 (file)
@@ -280,9 +280,20 @@ static enum print_line_t irqsoff_print_line(struct trace_iterator *iter)
 }
 
 static void irqsoff_graph_return(struct ftrace_graph_ret *trace) { }
-static void irqsoff_print_header(struct seq_file *s) { }
 static void irqsoff_trace_open(struct trace_iterator *iter) { }
 static void irqsoff_trace_close(struct trace_iterator *iter) { }
+
+#ifdef CONFIG_FUNCTION_TRACER
+static void irqsoff_print_header(struct seq_file *s)
+{
+       trace_default_header(s);
+}
+#else
+static void irqsoff_print_header(struct seq_file *s)
+{
+       trace_latency_header(s);
+}
+#endif /* CONFIG_FUNCTION_TRACER */
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 
 /*
index e4a70c0c71b64d43cc11420f8399b62fc81074c4..ff791ea48b570b9f8afe00624d622dd4f6af47e7 100644 (file)
@@ -280,9 +280,20 @@ static enum print_line_t wakeup_print_line(struct trace_iterator *iter)
 }
 
 static void wakeup_graph_return(struct ftrace_graph_ret *trace) { }
-static void wakeup_print_header(struct seq_file *s) { }
 static void wakeup_trace_open(struct trace_iterator *iter) { }
 static void wakeup_trace_close(struct trace_iterator *iter) { }
+
+#ifdef CONFIG_FUNCTION_TRACER
+static void wakeup_print_header(struct seq_file *s)
+{
+       trace_default_header(s);
+}
+#else
+static void wakeup_print_header(struct seq_file *s)
+{
+       trace_latency_header(s);
+}
+#endif /* CONFIG_FUNCTION_TRACER */
 #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
 
 /*
index fe6762ed56bd64ddeb57cdec377a623b782567da..476029d30621b08aea50aa275875167beabe5447 100644 (file)
@@ -66,7 +66,7 @@ OPTIONS
        used. This interfaces starts by centering on the line with more
        samples, TAB/UNTAB cycles through the lines with more samples.
 
--c::
+-C::
 --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
        be provided as a comma-separated list with no space: 0,1. Ranges of
        CPUs are specified with -: 0-2. Default is to report samples on all
index 212f24d672e1ac2f88b5c2f4a8fb84863fd5e820..dc85392a5ac75a081ddf025702321f3027aa44d5 100644 (file)
@@ -39,7 +39,7 @@ OPTIONS
 -T::
 --threads::
        Show per-thread event counters
--C::
+-c::
 --comms=::
        Only consider symbols in these comms. CSV that understands
        file://filename entries.
@@ -128,7 +128,7 @@ OPTIONS
 --symfs=<directory>::
         Look for files with symbols relative to this directory.
 
--c::
+-C::
 --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
        be provided as a comma-separated list with no space: 0,1. Ranges of
        CPUs are specified with -: 0-2. Default is to report samples on all
index dec87ecb530e482e273c64365cbe7fb926541e3f..7f61eaaf9ab882b657206fcc63bf0952a590bd73 100644 (file)
@@ -182,12 +182,17 @@ OPTIONS
 --hide-call-graph::
         When printing symbols do not display call chain.
 
--c::
+-C::
 --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
        be provided as a comma-separated list with no space: 0,1. Ranges of
        CPUs are specified with -: 0-2. Default is to report samples on all
        CPUs.
 
+-c::
+--comms=::
+       Only display events for these comms. CSV that understands
+       file://filename entries.
+
 -I::
 --show-info::
        Display extended information about the perf.data file. This adds
index 2c3b462f64b00531b4c8a7e5fc5cb6224dfc88d6..b24ac40fcd58cad9391ddee7495370e3eb2f814a 100644 (file)
@@ -8,13 +8,19 @@ perf-test - Runs sanity tests.
 SYNOPSIS
 --------
 [verse]
-'perf test <options>'
+'perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]'
 
 DESCRIPTION
 -----------
 This command does assorted sanity tests, initially through linked routines but
 also will look for a directory with more tests in the form of scripts.
 
+To get a list of available tests use 'perf test list', specifying a test name
+fragment will show all tests that have it.
+
+To run just specific tests, inform test name fragments or the numbers obtained
+from 'perf test list'.
+
 OPTIONS
 -------
 -v::
index b98e3075646b43f8f3218f45289f65068ea7ef31..ac86d67b636e06b3bcf931c9d6f6a012d4f2801f 100644 (file)
@@ -278,6 +278,7 @@ LIB_H += util/strbuf.h
 LIB_H += util/strlist.h
 LIB_H += util/strfilter.h
 LIB_H += util/svghelper.h
+LIB_H += util/tool.h
 LIB_H += util/run-command.h
 LIB_H += util/sigchain.h
 LIB_H += util/symbol.h
index 46b4c24f338ef4fe51e98a30d7f60f6b9e1010bb..d449645c5ef1e1a6da0a339625e7244be5c961c5 100644 (file)
 #include "util/sort.h"
 #include "util/hist.h"
 #include "util/session.h"
+#include "util/tool.h"
 
 #include <linux/bitmap.h>
 
-static char            const *input_name = "perf.data";
-
-static bool            force, use_tui, use_stdio;
-
-static bool            full_paths;
-
-static bool            print_line;
-
-static const char *sym_hist_filter;
-
-static const char      *cpu_list;
-static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
+struct perf_annotate {
+       struct perf_tool tool;
+       char const *input_name;
+       bool       force, use_tui, use_stdio;
+       bool       full_paths;
+       bool       print_line;
+       const char *sym_hist_filter;
+       const char *cpu_list;
+       DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
+};
 
-static int perf_evlist__add_sample(struct perf_evlist *evlist,
-                                  struct perf_sample *sample,
-                                  struct perf_evsel *evsel,
-                                  struct addr_location *al)
+static int perf_evsel__add_sample(struct perf_evsel *evsel,
+                                 struct perf_sample *sample,
+                                 struct addr_location *al,
+                                 struct perf_annotate *ann)
 {
        struct hist_entry *he;
        int ret;
 
-       if (sym_hist_filter != NULL &&
-           (al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
+       if (ann->sym_hist_filter != NULL &&
+           (al->sym == NULL ||
+            strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
                /* We're only interested in a symbol named sym_hist_filter */
                if (al->sym != NULL) {
                        rb_erase(&al->sym->rb_node,
@@ -69,8 +69,7 @@ static int perf_evlist__add_sample(struct perf_evlist *evlist,
        ret = 0;
        if (he->ms.sym != NULL) {
                struct annotation *notes = symbol__annotation(he->ms.sym);
-               if (notes->src == NULL &&
-                   symbol__alloc_hist(he->ms.sym, evlist->nr_entries) < 0)
+               if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0)
                        return -ENOMEM;
 
                ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
@@ -81,25 +80,26 @@ static int perf_evlist__add_sample(struct perf_evlist *evlist,
        return ret;
 }
 
-static int process_sample_event(union perf_event *event,
+static int process_sample_event(struct perf_tool *tool,
+                               union perf_event *event,
                                struct perf_sample *sample,
                                struct perf_evsel *evsel,
-                               struct perf_session *session)
+                               struct machine *machine)
 {
+       struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool);
        struct addr_location al;
 
-       if (perf_event__preprocess_sample(event, session, &al, sample,
+       if (perf_event__preprocess_sample(event, machine, &al, sample,
                                          symbol__annotate_init) < 0) {
                pr_warning("problem processing %d event, skipping it.\n",
                           event->header.type);
                return -1;
        }
 
-       if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
+       if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap))
                return 0;
 
-       if (!al.filtered &&
-           perf_evlist__add_sample(session->evlist, sample, evsel, &al)) {
+       if (!al.filtered && perf_evsel__add_sample(evsel, sample, &al, ann)) {
                pr_warning("problem incrementing symbol count, "
                           "skipping event\n");
                return -1;
@@ -108,14 +108,15 @@ static int process_sample_event(union perf_event *event,
        return 0;
 }
 
-static int hist_entry__tty_annotate(struct hist_entry *he, int evidx)
+static int hist_entry__tty_annotate(struct hist_entry *he, int evidx,
+                                   struct perf_annotate *ann)
 {
        return symbol__tty_annotate(he->ms.sym, he->ms.map, evidx,
-                                   print_line, full_paths, 0, 0);
+                                   ann->print_line, ann->full_paths, 0, 0);
 }
 
 static void hists__find_annotations(struct hists *self, int evidx,
-                                   int nr_events)
+                                   struct perf_annotate *ann)
 {
        struct rb_node *nd = rb_first(&self->entries), *next;
        int key = K_RIGHT;
@@ -138,8 +139,7 @@ find_next:
                }
 
                if (use_browser > 0) {
-                       key = hist_entry__tui_annotate(he, evidx, nr_events,
-                                                      NULL, NULL, 0);
+                       key = hist_entry__tui_annotate(he, evidx, NULL, NULL, 0);
                        switch (key) {
                        case K_RIGHT:
                                next = rb_next(nd);
@@ -154,7 +154,7 @@ find_next:
                        if (next != NULL)
                                nd = next;
                } else {
-                       hist_entry__tty_annotate(he, evidx);
+                       hist_entry__tty_annotate(he, evidx, ann);
                        nd = rb_next(nd);
                        /*
                         * Since we have a hist_entry per IP for the same
@@ -167,33 +167,26 @@ find_next:
        }
 }
 
-static struct perf_event_ops event_ops = {
-       .sample = process_sample_event,
-       .mmap   = perf_event__process_mmap,
-       .comm   = perf_event__process_comm,
-       .fork   = perf_event__process_task,
-       .ordered_samples = true,
-       .ordering_requires_timestamps = true,
-};
-
-static int __cmd_annotate(void)
+static int __cmd_annotate(struct perf_annotate *ann)
 {
        int ret;
        struct perf_session *session;
        struct perf_evsel *pos;
        u64 total_nr_samples;
 
-       session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
+       session = perf_session__new(ann->input_name, O_RDONLY,
+                                   ann->force, false, &ann->tool);
        if (session == NULL)
                return -ENOMEM;
 
-       if (cpu_list) {
-               ret = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap);
+       if (ann->cpu_list) {
+               ret = perf_session__cpu_bitmap(session, ann->cpu_list,
+                                              ann->cpu_bitmap);
                if (ret)
                        goto out_delete;
        }
 
-       ret = perf_session__process_events(session, &event_ops);
+       ret = perf_session__process_events(session, &ann->tool);
        if (ret)
                goto out_delete;
 
@@ -217,13 +210,12 @@ static int __cmd_annotate(void)
                        total_nr_samples += nr_samples;
                        hists__collapse_resort(hists);
                        hists__output_resort(hists);
-                       hists__find_annotations(hists, pos->idx,
-                                               session->evlist->nr_entries);
+                       hists__find_annotations(hists, pos->idx, ann);
                }
        }
 
        if (total_nr_samples == 0) {
-               ui__warning("The %s file has no samples!\n", input_name);
+               ui__warning("The %s file has no samples!\n", ann->input_name);
                goto out_delete;
        }
 out_delete:
@@ -247,29 +239,42 @@ static const char * const annotate_usage[] = {
        NULL
 };
 
-static const struct option options[] = {
-       OPT_STRING('i', "input", &input_name, "file",
+int cmd_annotate(int argc, const char **argv, const char *prefix __used)
+{
+       struct perf_annotate annotate = {
+               .tool = {
+                       .sample = process_sample_event,
+                       .mmap   = perf_event__process_mmap,
+                       .comm   = perf_event__process_comm,
+                       .fork   = perf_event__process_task,
+                       .ordered_samples = true,
+                       .ordering_requires_timestamps = true,
+               },
+               .input_name = "perf.data",
+       };
+       const struct option options[] = {
+       OPT_STRING('i', "input", &annotate.input_name, "file",
                    "input file name"),
        OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
                   "only consider symbols in these dsos"),
-       OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
+       OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol",
                    "symbol to annotate"),
-       OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
+       OPT_BOOLEAN('f', "force", &annotate.force, "don't complain, do it"),
        OPT_INCR('v', "verbose", &verbose,
                    "be more verbose (show symbol address, etc)"),
        OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
                    "dump raw trace in ASCII"),
-       OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
-       OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
+       OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"),
+       OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"),
        OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
                   "file", "vmlinux pathname"),
        OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
-       OPT_BOOLEAN('l', "print-line", &print_line,
+       OPT_BOOLEAN('l', "print-line", &annotate.print_line,
                    "print matching source lines (may be slow)"),
-       OPT_BOOLEAN('P', "full-paths", &full_paths,
+       OPT_BOOLEAN('P', "full-paths", &annotate.full_paths,
                    "Don't shorten the displayed pathnames"),
-       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
+       OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"),
        OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
                   "Look for files with symbols relative to this directory"),
        OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
@@ -279,15 +284,13 @@ static const struct option options[] = {
        OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
                   "Specify disassembler style (e.g. -M intel for intel syntax)"),
        OPT_END()
-};
+       };
 
-int cmd_annotate(int argc, const char **argv, const char *prefix __used)
-{
        argc = parse_options(argc, argv, options, annotate_usage, 0);
 
-       if (use_stdio)
+       if (annotate.use_stdio)
                use_browser = 0;
-       else if (use_tui)
+       else if (annotate.use_tui)
                use_browser = 1;
 
        setup_browser(true);
@@ -308,7 +311,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
                if (argc > 1)
                        usage_with_options(annotate_usage, options);
 
-               sym_hist_filter = argv[0];
+               annotate.sym_hist_filter = argv[0];
        }
 
        if (field_sep && *field_sep == '.') {
@@ -316,5 +319,5 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
                return -1;
        }
 
-       return __cmd_annotate();
+       return __cmd_annotate(&annotate);
 }
index b39f3a1ee7dcaa9c363089aba18dde450fba6e18..4f19513d7dda068eed48141ee4b3b300381cdc3d 100644 (file)
@@ -9,7 +9,9 @@
 #include "util/debug.h"
 #include "util/event.h"
 #include "util/hist.h"
+#include "util/evsel.h"
 #include "util/session.h"
+#include "util/tool.h"
 #include "util/sort.h"
 #include "util/symbol.h"
 #include "util/util.h"
@@ -30,14 +32,15 @@ static int hists__add_entry(struct hists *self,
        return -ENOMEM;
 }
 
-static int diff__process_sample_event(union perf_event *event,
+static int diff__process_sample_event(struct perf_tool *tool __used,
+                                     union perf_event *event,
                                      struct perf_sample *sample,
                                      struct perf_evsel *evsel __used,
-                                     struct perf_session *session)
+                                     struct machine *machine)
 {
        struct addr_location al;
 
-       if (perf_event__preprocess_sample(event, session, &al, sample, NULL) < 0) {
+       if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) {
                pr_warning("problem processing %d event, skipping it.\n",
                           event->header.type);
                return -1;
@@ -46,16 +49,16 @@ static int diff__process_sample_event(union perf_event *event,
        if (al.filtered || al.sym == NULL)
                return 0;
 
-       if (hists__add_entry(&session->hists, &al, sample->period)) {
+       if (hists__add_entry(&evsel->hists, &al, sample->period)) {
                pr_warning("problem incrementing symbol period, skipping event\n");
                return -1;
        }
 
-       session->hists.stats.total_period += sample->period;
+       evsel->hists.stats.total_period += sample->period;
        return 0;
 }
 
-static struct perf_event_ops event_ops = {
+static struct perf_tool perf_diff = {
        .sample = diff__process_sample_event,
        .mmap   = perf_event__process_mmap,
        .comm   = perf_event__process_comm,
@@ -145,13 +148,13 @@ static int __cmd_diff(void)
        int ret, i;
        struct perf_session *session[2];
 
-       session[0] = perf_session__new(input_old, O_RDONLY, force, false, &event_ops);
-       session[1] = perf_session__new(input_new, O_RDONLY, force, false, &event_ops);
+       session[0] = perf_session__new(input_old, O_RDONLY, force, false, &perf_diff);
+       session[1] = perf_session__new(input_new, O_RDONLY, force, false, &perf_diff);
        if (session[0] == NULL || session[1] == NULL)
                return -ENOMEM;
 
        for (i = 0; i < 2; ++i) {
-               ret = perf_session__process_events(session[i], &event_ops);
+               ret = perf_session__process_events(session[i], &perf_diff);
                if (ret)
                        goto out_delete;
        }
index 8dfc12bb119b91c2af2e6b07f956e9f45d342788..09c106193e65df339dccfc788bcc75c1ff83ef82 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "perf.h"
 #include "util/session.h"
+#include "util/tool.h"
 #include "util/debug.h"
 
 #include "util/parse-options.h"
@@ -16,8 +17,9 @@
 static char            const *input_name = "-";
 static bool            inject_build_ids;
 
-static int perf_event__repipe_synth(union perf_event *event,
-                                   struct perf_session *session __used)
+static int perf_event__repipe_synth(struct perf_tool *tool __used,
+                                   union perf_event *event,
+                                   struct machine *machine __used)
 {
        uint32_t size;
        void *buf = event;
@@ -36,41 +38,70 @@ static int perf_event__repipe_synth(union perf_event *event,
        return 0;
 }
 
-static int perf_event__repipe(union perf_event *event,
+static int perf_event__repipe_op2_synth(struct perf_tool *tool,
+                                       union perf_event *event,
+                                       struct perf_session *session __used)
+{
+       return perf_event__repipe_synth(tool, event, NULL);
+}
+
+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 __used)
+{
+       return perf_event__repipe_synth(NULL, event, NULL);
+}
+
+static int perf_event__repipe_attr(union perf_event *event,
+                                  struct perf_evlist **pevlist __used)
+{
+       return perf_event__repipe_synth(NULL, event, NULL);
+}
+
+static int perf_event__repipe(struct perf_tool *tool,
+                             union perf_event *event,
                              struct perf_sample *sample __used,
-                             struct perf_session *session)
+                             struct machine *machine)
 {
-       return perf_event__repipe_synth(event, session);
+       return perf_event__repipe_synth(tool, event, machine);
 }
 
-static int perf_event__repipe_sample(union perf_event *event,
+static int perf_event__repipe_sample(struct perf_tool *tool,
+                                    union perf_event *event,
                              struct perf_sample *sample __used,
                              struct perf_evsel *evsel __used,
-                             struct perf_session *session)
+                             struct machine *machine)
 {
-       return perf_event__repipe_synth(event, session);
+       return perf_event__repipe_synth(tool, event, machine);
 }
 
-static int perf_event__repipe_mmap(union perf_event *event,
+static int perf_event__repipe_mmap(struct perf_tool *tool,
+                                  union perf_event *event,
                                   struct perf_sample *sample,
-                                  struct perf_session *session)
+                                  struct machine *machine)
 {
        int err;
 
-       err = perf_event__process_mmap(event, sample, session);
-       perf_event__repipe(event, sample, session);
+       err = perf_event__process_mmap(tool, event, sample, machine);
+       perf_event__repipe(tool, event, sample, machine);
 
        return err;
 }
 
-static int perf_event__repipe_task(union perf_event *event,
+static int perf_event__repipe_task(struct perf_tool *tool,
+                                  union perf_event *event,
                                   struct perf_sample *sample,
-                                  struct perf_session *session)
+                                  struct machine *machine)
 {
        int err;
 
-       err = perf_event__process_task(event, sample, session);
-       perf_event__repipe(event, sample, session);
+       err = perf_event__process_task(tool, event, sample, machine);
+       perf_event__repipe(tool, event, sample, machine);
 
        return err;
 }
@@ -80,7 +111,7 @@ static int perf_event__repipe_tracing_data(union perf_event *event,
 {
        int err;
 
-       perf_event__repipe_synth(event, session);
+       perf_event__repipe_synth(NULL, event, NULL);
        err = perf_event__process_tracing_data(event, session);
 
        return err;
@@ -100,10 +131,10 @@ static int dso__read_build_id(struct dso *self)
        return -1;
 }
 
-static int dso__inject_build_id(struct dso *self, struct perf_session *session)
+static int dso__inject_build_id(struct dso *self, struct perf_tool *tool,
+                               struct machine *machine)
 {
        u16 misc = PERF_RECORD_MISC_USER;
-       struct machine *machine;
        int err;
 
        if (dso__read_build_id(self) < 0) {
@@ -111,17 +142,11 @@ static int dso__inject_build_id(struct dso *self, struct perf_session *session)
                return -1;
        }
 
-       machine = perf_session__find_host_machine(session);
-       if (machine == NULL) {
-               pr_err("Can't find machine for session\n");
-               return -1;
-       }
-
        if (self->kernel)
                misc = PERF_RECORD_MISC_KERNEL;
 
-       err = perf_event__synthesize_build_id(self, misc, perf_event__repipe,
-                                             machine, session);
+       err = perf_event__synthesize_build_id(tool, self, misc, perf_event__repipe,
+                                             machine);
        if (err) {
                pr_err("Can't synthesize build_id event for %s\n", self->long_name);
                return -1;
@@ -130,10 +155,11 @@ static int dso__inject_build_id(struct dso *self, struct perf_session *session)
        return 0;
 }
 
-static int perf_event__inject_buildid(union perf_event *event,
+static int perf_event__inject_buildid(struct perf_tool *tool,
+                                     union perf_event *event,
                                      struct perf_sample *sample,
                                      struct perf_evsel *evsel __used,
-                                     struct perf_session *session)
+                                     struct machine *machine)
 {
        struct addr_location al;
        struct thread *thread;
@@ -141,21 +167,21 @@ static int perf_event__inject_buildid(union perf_event *event,
 
        cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
 
-       thread = perf_session__findnew(session, event->ip.pid);
+       thread = machine__findnew_thread(machine, event->ip.pid);
        if (thread == NULL) {
                pr_err("problem processing %d event, skipping it.\n",
                       event->header.type);
                goto repipe;
        }
 
-       thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
-                             event->ip.pid, event->ip.ip, &al);
+       thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
+                             event->ip.ip, &al);
 
        if (al.map != NULL) {
                if (!al.map->dso->hit) {
                        al.map->dso->hit = 1;
                        if (map__load(al.map, NULL) >= 0) {
-                               dso__inject_build_id(al.map->dso, session);
+                               dso__inject_build_id(al.map->dso, tool, machine);
                                /*
                                 * If this fails, too bad, let the other side
                                 * account this as unresolved.
@@ -168,24 +194,24 @@ static int perf_event__inject_buildid(union perf_event *event,
        }
 
 repipe:
-       perf_event__repipe(event, sample, session);
+       perf_event__repipe(tool, event, sample, machine);
        return 0;
 }
 
-struct perf_event_ops inject_ops = {
+struct perf_tool perf_inject = {
        .sample         = perf_event__repipe_sample,
        .mmap           = perf_event__repipe,
        .comm           = perf_event__repipe,
        .fork           = perf_event__repipe,
        .exit           = perf_event__repipe,
        .lost           = perf_event__repipe,
-       .read           = perf_event__repipe,
+       .read           = perf_event__repipe_sample,
        .throttle       = perf_event__repipe,
        .unthrottle     = perf_event__repipe,
-       .attr           = perf_event__repipe_synth,
-       .event_type     = perf_event__repipe_synth,
-       .tracing_data   = perf_event__repipe_synth,
-       .build_id       = perf_event__repipe_synth,
+       .attr           = perf_event__repipe_attr,
+       .event_type     = perf_event__repipe_event_type_synth,
+       .tracing_data   = perf_event__repipe_tracing_data_synth,
+       .build_id       = perf_event__repipe_op2_synth,
 };
 
 extern volatile int session_done;
@@ -203,17 +229,17 @@ static int __cmd_inject(void)
        signal(SIGINT, sig_handler);
 
        if (inject_build_ids) {
-               inject_ops.sample       = perf_event__inject_buildid;
-               inject_ops.mmap         = perf_event__repipe_mmap;
-               inject_ops.fork         = perf_event__repipe_task;
-               inject_ops.tracing_data = perf_event__repipe_tracing_data;
+               perf_inject.sample       = perf_event__inject_buildid;
+               perf_inject.mmap         = perf_event__repipe_mmap;
+               perf_inject.fork         = perf_event__repipe_task;
+               perf_inject.tracing_data = perf_event__repipe_tracing_data;
        }
 
-       session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops);
+       session = perf_session__new(input_name, O_RDONLY, false, true, &perf_inject);
        if (session == NULL)
                return -ENOMEM;
 
-       ret = perf_session__process_events(session, &inject_ops);
+       ret = perf_session__process_events(session, &perf_inject);
 
        perf_session__delete(session);
 
index 225e963df105964166d5e5c8c252f76635bf7e37..886174e9525b5e275be2dd9c16b6385c0c800f8c 100644 (file)
@@ -7,6 +7,7 @@
 #include "util/thread.h"
 #include "util/header.h"
 #include "util/session.h"
+#include "util/tool.h"
 
 #include "util/parse-options.h"
 #include "util/trace-event.h"
@@ -303,12 +304,13 @@ static void process_raw_event(union perf_event *raw_event __used, void *data,
        }
 }
 
-static int process_sample_event(union perf_event *event,
+static int process_sample_event(struct perf_tool *tool __used,
+                               union perf_event *event,
                                struct perf_sample *sample,
                                struct perf_evsel *evsel __used,
-                               struct perf_session *session)
+                               struct machine *machine)
 {
-       struct thread *thread = perf_session__findnew(session, event->ip.pid);
+       struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
 
        if (thread == NULL) {
                pr_debug("problem processing %d event, skipping it.\n",
@@ -324,7 +326,7 @@ static int process_sample_event(union perf_event *event,
        return 0;
 }
 
-static struct perf_event_ops event_ops = {
+static struct perf_tool perf_kmem = {
        .sample                 = process_sample_event,
        .comm                   = perf_event__process_comm,
        .ordered_samples        = true,
@@ -483,7 +485,7 @@ static int __cmd_kmem(void)
 {
        int err = -EINVAL;
        struct perf_session *session = perf_session__new(input_name, O_RDONLY,
-                                                        0, false, &event_ops);
+                                                        0, false, &perf_kmem);
        if (session == NULL)
                return -ENOMEM;
 
@@ -494,7 +496,7 @@ static int __cmd_kmem(void)
                goto out_delete;
 
        setup_pager();
-       err = perf_session__process_events(session, &event_ops);
+       err = perf_session__process_events(session, &perf_kmem);
        if (err != 0)
                goto out_delete;
        sort_result();
index 899080ace26761c497935e1ad0480faa628bf7c9..4db5e52930672d1ab1d3b5f230357bdf355e0335 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "util/debug.h"
 #include "util/session.h"
+#include "util/tool.h"
 
 #include <sys/types.h>
 #include <sys/prctl.h>
@@ -845,12 +846,13 @@ static void dump_info(void)
                die("Unknown type of information\n");
 }
 
-static int process_sample_event(union perf_event *event,
+static int process_sample_event(struct perf_tool *tool __used,
+                               union perf_event *event,
                                struct perf_sample *sample,
                                struct perf_evsel *evsel __used,
-                               struct perf_session *s)
+                               struct machine *machine)
 {
-       struct thread *thread = perf_session__findnew(s, sample->tid);
+       struct thread *thread = machine__findnew_thread(machine, sample->tid);
 
        if (thread == NULL) {
                pr_debug("problem processing %d event, skipping it.\n",
@@ -863,7 +865,7 @@ static int process_sample_event(union perf_event *event,
        return 0;
 }
 
-static struct perf_event_ops eops = {
+static struct perf_tool eops = {
        .sample                 = process_sample_event,
        .comm                   = perf_event__process_comm,
        .ordered_samples        = true,
index 710ae3d0a489ba9aac850cf9c28baff6b267d9b6..59d43abfbfec1ce22b44ac9c2db18cd29f0ce612 100644 (file)
@@ -46,7 +46,6 @@
 
 #define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*"
 #define DEFAULT_FUNC_FILTER "!_*"
-#define MAX_PATH_LEN 256
 
 /* Session management structure */
 static struct {
index 6ab58cc99d53effeced21668df5605e634913e6b..766fa0a91a320547490d66c7a9452fbd2a1b863f 100644 (file)
@@ -22,6 +22,7 @@
 #include "util/evsel.h"
 #include "util/debug.h"
 #include "util/session.h"
+#include "util/tool.h"
 #include "util/symbol.h"
 #include "util/cpumap.h"
 #include "util/thread_map.h"
@@ -35,55 +36,36 @@ enum write_mode_t {
        WRITE_APPEND
 };
 
-static u64                     user_interval                   = ULLONG_MAX;
-static u64                     default_interval                =      0;
-
-static unsigned int            page_size;
-static unsigned int            mmap_pages                      = UINT_MAX;
-static unsigned int            user_freq                       = UINT_MAX;
-static int                     freq                            =   1000;
-static int                     output;
-static int                     pipe_output                     =      0;
-static const char              *output_name                    = NULL;
-static bool                    group                           =  false;
-static int                     realtime_prio                   =      0;
-static bool                    nodelay                         =  false;
-static bool                    raw_samples                     =  false;
-static bool                    sample_id_all_avail             =   true;
-static bool                    system_wide                     =  false;
-static pid_t                   target_pid                      =     -1;
-static pid_t                   target_tid                      =     -1;
-static pid_t                   child_pid                       =     -1;
-static bool                    no_inherit                      =  false;
-static enum write_mode_t       write_mode                      = WRITE_FORCE;
-static bool                    call_graph                      =  false;
-static bool                    inherit_stat                    =  false;
-static bool                    no_samples                      =  false;
-static bool                    sample_address                  =  false;
-static bool                    sample_time                     =  false;
-static bool                    no_buildid                      =  false;
-static bool                    no_buildid_cache                =  false;
-static struct perf_evlist      *evsel_list;
-
-static long                    samples                         =      0;
-static u64                     bytes_written                   =      0;
-
-static int                     file_new                        =      1;
-static off_t                   post_processing_offset;
-
-static struct perf_session     *session;
-static const char              *cpu_list;
-static const char               *progname;
-
-static void advance_output(size_t size)
+struct perf_record {
+       struct perf_tool        tool;
+       struct perf_record_opts opts;
+       u64                     bytes_written;
+       const char              *output_name;
+       struct perf_evlist      *evlist;
+       struct perf_session     *session;
+       const char              *progname;
+       int                     output;
+       unsigned int            page_size;
+       int                     realtime_prio;
+       enum write_mode_t       write_mode;
+       bool                    no_buildid;
+       bool                    no_buildid_cache;
+       bool                    force;
+       bool                    file_new;
+       bool                    append_file;
+       long                    samples;
+       off_t                   post_processing_offset;
+};
+
+static void advance_output(struct perf_record *rec, size_t size)
 {
-       bytes_written += size;
+       rec->bytes_written += size;
 }
 
-static void write_output(void *buf, size_t size)
+static void write_output(struct perf_record *rec, void *buf, size_t size)
 {
        while (size) {
-               int ret = write(output, buf, size);
+               int ret = write(rec->output, buf, size);
 
                if (ret < 0)
                        die("failed to write");
@@ -91,30 +73,33 @@ static void write_output(void *buf, size_t size)
                size -= ret;
                buf += ret;
 
-               bytes_written += ret;
+               rec->bytes_written += ret;
        }
 }
 
-static int process_synthesized_event(union perf_event *event,
+static int process_synthesized_event(struct perf_tool *tool,
+                                    union perf_event *event,
                                     struct perf_sample *sample __used,
-                                    struct perf_session *self __used)
+                                    struct machine *machine __used)
 {
-       write_output(event, event->header.size);
+       struct perf_record *rec = container_of(tool, struct perf_record, tool);
+       write_output(rec, event, event->header.size);
        return 0;
 }
 
-static void mmap_read(struct perf_mmap *md)
+static void perf_record__mmap_read(struct perf_record *rec,
+                                  struct perf_mmap *md)
 {
        unsigned int head = perf_mmap__read_head(md);
        unsigned int old = md->prev;
-       unsigned char *data = md->base + page_size;
+       unsigned char *data = md->base + rec->page_size;
        unsigned long size;
        void *buf;
 
        if (old == head)
                return;
 
-       samples++;
+       rec->samples++;
 
        size = head - old;
 
@@ -123,14 +108,14 @@ static void mmap_read(struct perf_mmap *md)
                size = md->mask + 1 - (old & md->mask);
                old += size;
 
-               write_output(buf, size);
+               write_output(rec, buf, size);
        }
 
        buf = &data[old & md->mask];
        size = head - old;
        old += size;
 
-       write_output(buf, size);
+       write_output(rec, buf, size);
 
        md->prev = old;
        perf_mmap__write_tail(md, old);
@@ -149,17 +134,18 @@ static void sig_handler(int sig)
        signr = sig;
 }
 
-static void sig_atexit(void)
+static void perf_record__sig_exit(int exit_status __used, void *arg)
 {
+       struct perf_record *rec = arg;
        int status;
 
-       if (child_pid > 0) {
+       if (rec->evlist->workload.pid > 0) {
                if (!child_finished)
-                       kill(child_pid, SIGTERM);
+                       kill(rec->evlist->workload.pid, SIGTERM);
 
                wait(&status);
                if (WIFSIGNALED(status))
-                       psignal(WTERMSIG(status), progname);
+                       psignal(WTERMSIG(status), rec->progname);
        }
 
        if (signr == -1 || signr == SIGUSR1)
@@ -169,78 +155,6 @@ static void sig_atexit(void)
        kill(getpid(), signr);
 }
 
-static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist)
-{
-       struct perf_event_attr *attr = &evsel->attr;
-       int track = !evsel->idx; /* only the first counter needs these */
-
-       attr->disabled          = 1;
-       attr->inherit           = !no_inherit;
-       attr->read_format       = PERF_FORMAT_TOTAL_TIME_ENABLED |
-                                 PERF_FORMAT_TOTAL_TIME_RUNNING |
-                                 PERF_FORMAT_ID;
-
-       attr->sample_type       |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
-
-       if (evlist->nr_entries > 1)
-               attr->sample_type |= PERF_SAMPLE_ID;
-
-       /*
-        * We default some events to a 1 default interval. But keep
-        * it a weak assumption overridable by the user.
-        */
-       if (!attr->sample_period || (user_freq != UINT_MAX &&
-                                    user_interval != ULLONG_MAX)) {
-               if (freq) {
-                       attr->sample_type       |= PERF_SAMPLE_PERIOD;
-                       attr->freq              = 1;
-                       attr->sample_freq       = freq;
-               } else {
-                       attr->sample_period = default_interval;
-               }
-       }
-
-       if (no_samples)
-               attr->sample_freq = 0;
-
-       if (inherit_stat)
-               attr->inherit_stat = 1;
-
-       if (sample_address) {
-               attr->sample_type       |= PERF_SAMPLE_ADDR;
-               attr->mmap_data = track;
-       }
-
-       if (call_graph)
-               attr->sample_type       |= PERF_SAMPLE_CALLCHAIN;
-
-       if (system_wide)
-               attr->sample_type       |= PERF_SAMPLE_CPU;
-
-       if (sample_id_all_avail &&
-           (sample_time || system_wide || !no_inherit || cpu_list))
-               attr->sample_type       |= PERF_SAMPLE_TIME;
-
-       if (raw_samples) {
-               attr->sample_type       |= PERF_SAMPLE_TIME;
-               attr->sample_type       |= PERF_SAMPLE_RAW;
-               attr->sample_type       |= PERF_SAMPLE_CPU;
-       }
-
-       if (nodelay) {
-               attr->watermark = 0;
-               attr->wakeup_events = 1;
-       }
-
-       attr->mmap              = track;
-       attr->comm              = track;
-
-       if (target_pid == -1 && target_tid == -1 && !system_wide) {
-               attr->disabled = 1;
-               attr->enable_on_exec = 1;
-       }
-}
-
 static bool perf_evlist__equal(struct perf_evlist *evlist,
                               struct perf_evlist *other)
 {
@@ -260,15 +174,17 @@ static bool perf_evlist__equal(struct perf_evlist *evlist,
        return true;
 }
 
-static void open_counters(struct perf_evlist *evlist)
+static void perf_record__open(struct perf_record *rec)
 {
        struct perf_evsel *pos, *first;
-
-       if (evlist->cpus->map[0] < 0)
-               no_inherit = true;
+       struct perf_evlist *evlist = rec->evlist;
+       struct perf_session *session = rec->session;
+       struct perf_record_opts *opts = &rec->opts;
 
        first = list_entry(evlist->entries.next, struct perf_evsel, node);
 
+       perf_evlist__config_attrs(evlist, opts);
+
        list_for_each_entry(pos, &evlist->entries, node) {
                struct perf_event_attr *attr = &pos->attr;
                struct xyarray *group_fd = NULL;
@@ -286,29 +202,27 @@ static void open_counters(struct perf_evlist *evlist)
                 */
                bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
 
-               if (group && pos != first)
+               if (opts->group && pos != first)
                        group_fd = first->fd;
-
-               config_attr(pos, evlist);
 retry_sample_id:
-               attr->sample_id_all = sample_id_all_avail ? 1 : 0;
+               attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0;
 try_again:
-               if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group,
-                                    group_fd) < 0) {
+               if (perf_evsel__open(pos, evlist->cpus, evlist->threads,
+                                    opts->group, group_fd) < 0) {
                        int err = errno;
 
                        if (err == EPERM || err == EACCES) {
                                ui__error_paranoid();
                                exit(EXIT_FAILURE);
-                       } else if (err ==  ENODEV && cpu_list) {
+                       } else if (err ==  ENODEV && opts->cpu_list) {
                                die("No such device - did you specify"
                                        " an out-of-range profile CPU?\n");
-                       } else if (err == EINVAL && sample_id_all_avail) {
+                       } else if (err == EINVAL && opts->sample_id_all_avail) {
                                /*
                                 * Old kernel, no attr->sample_id_type_all field
                                 */
-                               sample_id_all_avail = false;
-                               if (!sample_time && !raw_samples && !time_needed)
+                               opts->sample_id_all_avail = false;
+                               if (!opts->sample_time && !opts->raw_samples && !time_needed)
                                        attr->sample_type &= ~PERF_SAMPLE_TIME;
 
                                goto retry_sample_id;
@@ -358,10 +272,10 @@ try_again:
                exit(-1);
        }
 
-       if (perf_evlist__mmap(evlist, mmap_pages, false) < 0)
+       if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0)
                die("failed to mmap with %d (%s)\n", errno, strerror(errno));
 
-       if (file_new)
+       if (rec->file_new)
                session->evlist = evlist;
        else {
                if (!perf_evlist__equal(session->evlist, evlist)) {
@@ -373,29 +287,32 @@ try_again:
        perf_session__update_sample_type(session);
 }
 
-static int process_buildids(void)
+static int process_buildids(struct perf_record *rec)
 {
-       u64 size = lseek(output, 0, SEEK_CUR);
+       u64 size = lseek(rec->output, 0, SEEK_CUR);
 
        if (size == 0)
                return 0;
 
-       session->fd = output;
-       return __perf_session__process_events(session, post_processing_offset,
-                                             size - post_processing_offset,
+       rec->session->fd = rec->output;
+       return __perf_session__process_events(rec->session, rec->post_processing_offset,
+                                             size - rec->post_processing_offset,
                                              size, &build_id__mark_dso_hit_ops);
 }
 
-static void atexit_header(void)
+static void perf_record__exit(int status __used, void *arg)
 {
-       if (!pipe_output) {
-               session->header.data_size += bytes_written;
-
-               if (!no_buildid)
-                       process_buildids();
-               perf_session__write_header(session, evsel_list, output, true);
-               perf_session__delete(session);
-               perf_evlist__delete(evsel_list);
+       struct perf_record *rec = arg;
+
+       if (!rec->opts.pipe_output) {
+               rec->session->header.data_size += rec->bytes_written;
+
+               if (!rec->no_buildid)
+                       process_buildids(rec);
+               perf_session__write_header(rec->session, rec->evlist,
+                                          rec->output, true);
+               perf_session__delete(rec->session);
+               perf_evlist__delete(rec->evlist);
                symbol__exit();
        }
 }
@@ -403,7 +320,7 @@ static void atexit_header(void)
 static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
 {
        int err;
-       struct perf_session *psession = data;
+       struct perf_tool *tool = data;
 
        if (machine__is_host(machine))
                return;
@@ -416,8 +333,8 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
         *method is used to avoid symbol missing when the first addr is
         *in module instead of in guest kernel.
         */
-       err = perf_event__synthesize_modules(process_synthesized_event,
-                                            psession, machine);
+       err = perf_event__synthesize_modules(tool, process_synthesized_event,
+                                            machine);
        if (err < 0)
                pr_err("Couldn't record guest kernel [%d]'s reference"
                       " relocation symbol.\n", machine->pid);
@@ -426,12 +343,11 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
         * We use _stext for guest kernel because guest kernel's /proc/kallsyms
         * have no _text sometimes.
         */
-       err = perf_event__synthesize_kernel_mmap(process_synthesized_event,
-                                                psession, machine, "_text");
+       err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
+                                                machine, "_text");
        if (err < 0)
-               err = perf_event__synthesize_kernel_mmap(process_synthesized_event,
-                                                        psession, machine,
-                                                        "_stext");
+               err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
+                                                        machine, "_stext");
        if (err < 0)
                pr_err("Couldn't record guest kernel [%d]'s reference"
                       " relocation symbol.\n", machine->pid);
@@ -442,73 +358,71 @@ static struct perf_event_header finished_round_event = {
        .type = PERF_RECORD_FINISHED_ROUND,
 };
 
-static void mmap_read_all(void)
+static void perf_record__mmap_read_all(struct perf_record *rec)
 {
        int i;
 
-       for (i = 0; i < evsel_list->nr_mmaps; i++) {
-               if (evsel_list->mmap[i].base)
-                       mmap_read(&evsel_list->mmap[i]);
+       for (i = 0; i < rec->evlist->nr_mmaps; i++) {
+               if (rec->evlist->mmap[i].base)
+                       perf_record__mmap_read(rec, &rec->evlist->mmap[i]);
        }
 
-       if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
-               write_output(&finished_round_event, sizeof(finished_round_event));
+       if (perf_header__has_feat(&rec->session->header, HEADER_TRACE_INFO))
+               write_output(rec, &finished_round_event, sizeof(finished_round_event));
 }
 
-static int __cmd_record(int argc, const char **argv)
+static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
 {
        struct stat st;
        int flags;
-       int err;
+       int err, output;
        unsigned long waking = 0;
-       int child_ready_pipe[2], go_pipe[2];
        const bool forks = argc > 0;
-       char buf;
        struct machine *machine;
+       struct perf_tool *tool = &rec->tool;
+       struct perf_record_opts *opts = &rec->opts;
+       struct perf_evlist *evsel_list = rec->evlist;
+       const char *output_name = rec->output_name;
+       struct perf_session *session;
 
-       progname = argv[0];
+       rec->progname = argv[0];
 
-       page_size = sysconf(_SC_PAGE_SIZE);
+       rec->page_size = sysconf(_SC_PAGE_SIZE);
 
-       atexit(sig_atexit);
+       on_exit(perf_record__sig_exit, rec);
        signal(SIGCHLD, sig_handler);
        signal(SIGINT, sig_handler);
        signal(SIGUSR1, sig_handler);
 
-       if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
-               perror("failed to create pipes");
-               exit(-1);
-       }
-
        if (!output_name) {
                if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode))
-                       pipe_output = 1;
+                       opts->pipe_output = true;
                else
-                       output_name = "perf.data";
+                       rec->output_name = output_name = "perf.data";
        }
        if (output_name) {
                if (!strcmp(output_name, "-"))
-                       pipe_output = 1;
+                       opts->pipe_output = true;
                else if (!stat(output_name, &st) && st.st_size) {
-                       if (write_mode == WRITE_FORCE) {
+                       if (rec->write_mode == WRITE_FORCE) {
                                char oldname[PATH_MAX];
                                snprintf(oldname, sizeof(oldname), "%s.old",
                                         output_name);
                                unlink(oldname);
                                rename(output_name, oldname);
                        }
-               } else if (write_mode == WRITE_APPEND) {
-                       write_mode = WRITE_FORCE;
+               } else if (rec->write_mode == WRITE_APPEND) {
+                       rec->write_mode = WRITE_FORCE;
                }
        }
 
        flags = O_CREAT|O_RDWR;
-       if (write_mode == WRITE_APPEND)
-               file_new = 0;
+       if (rec->write_mode == WRITE_APPEND)
+               rec->file_new = 0;
        else
                flags |= O_TRUNC;
 
-       if (pipe_output)
+       if (opts->pipe_output)
                output = STDOUT_FILENO;
        else
                output = open(output_name, flags, S_IRUSR | S_IWUSR);
@@ -517,17 +431,21 @@ static int __cmd_record(int argc, const char **argv)
                exit(-1);
        }
 
+       rec->output = output;
+
        session = perf_session__new(output_name, O_WRONLY,
-                                   write_mode == WRITE_FORCE, false, NULL);
+                                   rec->write_mode == WRITE_FORCE, false, NULL);
        if (session == NULL) {
                pr_err("Not enough memory for reading perf file header\n");
                return -1;
        }
 
-       if (!no_buildid)
+       rec->session = session;
+
+       if (!rec->no_buildid)
                perf_header__set_feat(&session->header, HEADER_BUILD_ID);
 
-       if (!file_new) {
+       if (!rec->file_new) {
                err = perf_session__read_header(session, output);
                if (err < 0)
                        goto out_delete_session;
@@ -549,94 +467,50 @@ static int __cmd_record(int argc, const char **argv)
        perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY);
        perf_header__set_feat(&session->header, HEADER_CPUID);
 
-       /* 512 kiB: default amount of unprivileged mlocked memory */
-       if (mmap_pages == UINT_MAX)
-               mmap_pages = (512 * 1024) / page_size;
-
        if (forks) {
-               child_pid = fork();
-               if (child_pid < 0) {
-                       perror("failed to fork");
-                       exit(-1);
-               }
-
-               if (!child_pid) {
-                       if (pipe_output)
-                               dup2(2, 1);
-                       close(child_ready_pipe[0]);
-                       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
-                        */
-                       close(child_ready_pipe[1]);
-
-                       /*
-                        * Wait until the parent tells us to go.
-                        */
-                       if (read(go_pipe[0], &buf, 1) == -1)
-                               perror("unable to read pipe");
-
-                       execvp(argv[0], (char **)argv);
-
-                       perror(argv[0]);
-                       kill(getppid(), SIGUSR1);
-                       exit(-1);
-               }
-
-               if (!system_wide && target_tid == -1 && target_pid == -1)
-                       evsel_list->threads->map[0] = child_pid;
-
-               close(child_ready_pipe[1]);
-               close(go_pipe[0]);
-               /*
-                * wait for child to settle
-                */
-               if (read(child_ready_pipe[0], &buf, 1) == -1) {
-                       perror("unable to read pipe");
-                       exit(-1);
+               err = perf_evlist__prepare_workload(evsel_list, opts, argv);
+               if (err < 0) {
+                       pr_err("Couldn't run the workload!\n");
+                       goto out_delete_session;
                }
-               close(child_ready_pipe[0]);
        }
 
-       open_counters(evsel_list);
+       perf_record__open(rec);
 
        /*
-        * perf_session__delete(session) will be called at atexit_header()
+        * perf_session__delete(session) will be called at perf_record__exit()
         */
-       atexit(atexit_header);
+       on_exit(perf_record__exit, rec);
 
-       if (pipe_output) {
+       if (opts->pipe_output) {
                err = perf_header__write_pipe(output);
                if (err < 0)
                        return err;
-       } else if (file_new) {
+       } else if (rec->file_new) {
                err = perf_session__write_header(session, evsel_list,
                                                 output, false);
                if (err < 0)
                        return err;
        }
 
-       post_processing_offset = lseek(output, 0, SEEK_CUR);
+       rec->post_processing_offset = lseek(output, 0, SEEK_CUR);
+
+       machine = perf_session__find_host_machine(session);
+       if (!machine) {
+               pr_err("Couldn't find native kernel information.\n");
+               return -1;
+       }
 
-       if (pipe_output) {
-               err = perf_session__synthesize_attrs(session,
-                                                    process_synthesized_event);
+       if (opts->pipe_output) {
+               err = perf_event__synthesize_attrs(tool, session,
+                                                  process_synthesized_event);
                if (err < 0) {
                        pr_err("Couldn't synthesize attrs.\n");
                        return err;
                }
 
-               err = perf_event__synthesize_event_types(process_synthesized_event,
-                                                        session);
+               err = perf_event__synthesize_event_types(tool, process_synthesized_event,
+                                                        machine);
                if (err < 0) {
                        pr_err("Couldn't synthesize event_types.\n");
                        return err;
@@ -651,56 +525,49 @@ static int __cmd_record(int argc, const char **argv)
                         * return this more properly and also
                         * propagate errors that now are calling die()
                         */
-                       err = perf_event__synthesize_tracing_data(output, evsel_list,
-                                                                 process_synthesized_event,
-                                                                 session);
+                       err = perf_event__synthesize_tracing_data(tool, output, evsel_list,
+                                                                 process_synthesized_event);
                        if (err <= 0) {
                                pr_err("Couldn't record tracing data.\n");
                                return err;
                        }
-                       advance_output(err);
+                       advance_output(rec, err);
                }
        }
 
-       machine = perf_session__find_host_machine(session);
-       if (!machine) {
-               pr_err("Couldn't find native kernel information.\n");
-               return -1;
-       }
-
-       err = perf_event__synthesize_kernel_mmap(process_synthesized_event,
-                                                session, machine, "_text");
+       err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
+                                                machine, "_text");
        if (err < 0)
-               err = perf_event__synthesize_kernel_mmap(process_synthesized_event,
-                                                        session, machine, "_stext");
+               err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
+                                                        machine, "_stext");
        if (err < 0)
                pr_err("Couldn't record kernel reference relocation symbol\n"
                       "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
                       "Check /proc/kallsyms permission or run as root.\n");
 
-       err = perf_event__synthesize_modules(process_synthesized_event,
-                                            session, machine);
+       err = perf_event__synthesize_modules(tool, process_synthesized_event,
+                                            machine);
        if (err < 0)
                pr_err("Couldn't record kernel module information.\n"
                       "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
                       "Check /proc/modules permission or run as root.\n");
 
        if (perf_guest)
-               perf_session__process_machines(session,
+               perf_session__process_machines(session, tool,
                                               perf_event__synthesize_guest_os);
 
-       if (!system_wide)
-               perf_event__synthesize_thread_map(evsel_list->threads,
+       if (!opts->system_wide)
+               perf_event__synthesize_thread_map(tool, evsel_list->threads,
                                                  process_synthesized_event,
-                                                 session);
+                                                 machine);
        else
-               perf_event__synthesize_threads(process_synthesized_event,
-                                              session);
+               perf_event__synthesize_threads(tool, process_synthesized_event,
+                                              machine);
 
-       if (realtime_prio) {
+       if (rec->realtime_prio) {
                struct sched_param param;
 
-               param.sched_priority = realtime_prio;
+               param.sched_priority = rec->realtime_prio;
                if (sched_setscheduler(0, SCHED_FIFO, &param)) {
                        pr_err("Could not set realtime priority.\n");
                        exit(-1);
@@ -713,14 +580,14 @@ static int __cmd_record(int argc, const char **argv)
         * Let the child rip
         */
        if (forks)
-               close(go_pipe[1]);
+               perf_evlist__start_workload(evsel_list);
 
        for (;;) {
-               int hits = samples;
+               int hits = rec->samples;
 
-               mmap_read_all();
+               perf_record__mmap_read_all(rec);
 
-               if (hits == samples) {
+               if (hits == rec->samples) {
                        if (done)
                                break;
                        err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1);
@@ -741,9 +608,9 @@ static int __cmd_record(int argc, const char **argv)
         */
        fprintf(stderr,
                "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n",
-               (double)bytes_written / 1024.0 / 1024.0,
+               (double)rec->bytes_written / 1024.0 / 1024.0,
                output_name,
-               bytes_written / 24);
+               rec->bytes_written / 24);
 
        return 0;
 
@@ -758,58 +625,88 @@ static const char * const record_usage[] = {
        NULL
 };
 
-static bool force, append_file;
+/*
+ * XXX Ideally would be local to cmd_record() and passed to a perf_record__new
+ * because we need to have access to it in perf_record__exit, that is called
+ * after cmd_record() exits, but since record_options need to be accessible to
+ * builtin-script, leave it here.
+ *
+ * At least we don't ouch it in all the other functions here directly.
+ *
+ * Just say no to tons of global variables, sigh.
+ */
+static struct perf_record record = {
+       .opts = {
+               .target_pid          = -1,
+               .target_tid          = -1,
+               .mmap_pages          = UINT_MAX,
+               .user_freq           = UINT_MAX,
+               .user_interval       = ULLONG_MAX,
+               .freq                = 1000,
+               .sample_id_all_avail = true,
+       },
+       .write_mode = WRITE_FORCE,
+       .file_new   = true,
+};
 
+/*
+ * XXX Will stay a global variable till we fix builtin-script.c to stop messing
+ * with it and switch to use the library functions in perf_evlist that came
+ * from builtin-record.c, i.e. use perf_record_opts,
+ * perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record',
+ * using pipes, etc.
+ */
 const struct option record_options[] = {
-       OPT_CALLBACK('e', "event", &evsel_list, "event",
+       OPT_CALLBACK('e', "event", &record.evlist, "event",
                     "event selector. use 'perf list' to list available events",
                     parse_events_option),
-       OPT_CALLBACK(0, "filter", &evsel_list, "filter",
+       OPT_CALLBACK(0, "filter", &record.evlist, "filter",
                     "event filter", parse_filter),
-       OPT_INTEGER('p', "pid", &target_pid,
+       OPT_INTEGER('p', "pid", &record.opts.target_pid,
                    "record events on existing process id"),
-       OPT_INTEGER('t', "tid", &target_tid,
+       OPT_INTEGER('t', "tid", &record.opts.target_tid,
                    "record events on existing thread id"),
-       OPT_INTEGER('r', "realtime", &realtime_prio,
+       OPT_INTEGER('r', "realtime", &record.realtime_prio,
                    "collect data with this RT SCHED_FIFO priority"),
-       OPT_BOOLEAN('D', "no-delay", &nodelay,
+       OPT_BOOLEAN('D', "no-delay", &record.opts.no_delay,
                    "collect data without buffering"),
-       OPT_BOOLEAN('R', "raw-samples", &raw_samples,
+       OPT_BOOLEAN('R', "raw-samples", &record.opts.raw_samples,
                    "collect raw sample records from all opened counters"),
-       OPT_BOOLEAN('a', "all-cpus", &system_wide,
+       OPT_BOOLEAN('a', "all-cpus", &record.opts.system_wide,
                            "system-wide collection from all CPUs"),
-       OPT_BOOLEAN('A', "append", &append_file,
+       OPT_BOOLEAN('A', "append", &record.append_file,
                            "append to the output file to do incremental profiling"),
-       OPT_STRING('C', "cpu", &cpu_list, "cpu",
+       OPT_STRING('C', "cpu", &record.opts.cpu_list, "cpu",
                    "list of cpus to monitor"),
-       OPT_BOOLEAN('f', "force", &force,
+       OPT_BOOLEAN('f', "force", &record.force,
                        "overwrite existing data file (deprecated)"),
-       OPT_U64('c', "count", &user_interval, "event period to sample"),
-       OPT_STRING('o', "output", &output_name, "file",
+       OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"),
+       OPT_STRING('o', "output", &record.output_name, "file",
                    "output file name"),
-       OPT_BOOLEAN('i', "no-inherit", &no_inherit,
+       OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit,
                    "child tasks do not inherit counters"),
-       OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"),
-       OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"),
-       OPT_BOOLEAN(0, "group", &group,
+       OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"),
+       OPT_UINTEGER('m', "mmap-pages", &record.opts.mmap_pages,
+                    "number of mmap data pages"),
+       OPT_BOOLEAN(0, "group", &record.opts.group,
                    "put the counters into a counter group"),
-       OPT_BOOLEAN('g', "call-graph", &call_graph,
+       OPT_BOOLEAN('g', "call-graph", &record.opts.call_graph,
                    "do call-graph (stack chain/backtrace) recording"),
        OPT_INCR('v', "verbose", &verbose,
                    "be more verbose (show counter open errors, etc)"),
        OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
-       OPT_BOOLEAN('s', "stat", &inherit_stat,
+       OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat,
                    "per thread counts"),
-       OPT_BOOLEAN('d', "data", &sample_address,
+       OPT_BOOLEAN('d', "data", &record.opts.sample_address,
                    "Sample addresses"),
-       OPT_BOOLEAN('T', "timestamp", &sample_time, "Sample timestamps"),
-       OPT_BOOLEAN('n', "no-samples", &no_samples,
+       OPT_BOOLEAN('T', "timestamp", &record.opts.sample_time, "Sample timestamps"),
+       OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples,
                    "don't sample"),
-       OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache,
+       OPT_BOOLEAN('N', "no-buildid-cache", &record.no_buildid_cache,
                    "do not update the buildid cache"),
-       OPT_BOOLEAN('B', "no-buildid", &no_buildid,
+       OPT_BOOLEAN('B', "no-buildid", &record.no_buildid,
                    "do not collect buildids in perf.data"),
-       OPT_CALLBACK('G', "cgroup", &evsel_list, "name",
+       OPT_CALLBACK('G', "cgroup", &record.evlist, "name",
                     "monitor event in cgroup name only",
                     parse_cgroups),
        OPT_END()
@@ -819,6 +716,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
 {
        int err = -ENOMEM;
        struct perf_evsel *pos;
+       struct perf_evlist *evsel_list;
+       struct perf_record *rec = &record;
 
        perf_header__set_cmdline(argc, argv);
 
@@ -826,23 +725,25 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
        if (evsel_list == NULL)
                return -ENOMEM;
 
+       rec->evlist = evsel_list;
+
        argc = parse_options(argc, argv, record_options, record_usage,
                            PARSE_OPT_STOP_AT_NON_OPTION);
-       if (!argc && target_pid == -1 && target_tid == -1 &&
-               !system_wide && !cpu_list)
+       if (!argc && rec->opts.target_pid == -1 && rec->opts.target_tid == -1 &&
+               !rec->opts.system_wide && !rec->opts.cpu_list)
                usage_with_options(record_usage, record_options);
 
-       if (force && append_file) {
+       if (rec->force && rec->append_file) {
                fprintf(stderr, "Can't overwrite and append at the same time."
                                " You need to choose between -f and -A");
                usage_with_options(record_usage, record_options);
-       } else if (append_file) {
-               write_mode = WRITE_APPEND;
+       } else if (rec->append_file) {
+               rec->write_mode = WRITE_APPEND;
        } else {
-               write_mode = WRITE_FORCE;
+               rec->write_mode = WRITE_FORCE;
        }
 
-       if (nr_cgroups && !system_wide) {
+       if (nr_cgroups && !rec->opts.system_wide) {
                fprintf(stderr, "cgroup monitoring only available in"
                        " system-wide mode\n");
                usage_with_options(record_usage, record_options);
@@ -860,7 +761,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
 "If some relocation was applied (e.g. kexec) symbols may be misresolved\n"
 "even with a suitable vmlinux or kallsyms file.\n\n");
 
-       if (no_buildid_cache || no_buildid)
+       if (rec->no_buildid_cache || rec->no_buildid)
                disable_buildid_cache();
 
        if (evsel_list->nr_entries == 0 &&
@@ -869,43 +770,37 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
                goto out_symbol_exit;
        }
 
-       if (target_pid != -1)
-               target_tid = target_pid;
+       if (rec->opts.target_pid != -1)
+               rec->opts.target_tid = rec->opts.target_pid;
 
-       if (perf_evlist__create_maps(evsel_list, target_pid,
-                                    target_tid, cpu_list) < 0)
+       if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid,
+                                    rec->opts.target_tid, rec->opts.cpu_list) < 0)
                usage_with_options(record_usage, record_options);
 
        list_for_each_entry(pos, &evsel_list->entries, node) {
-               if (perf_evsel__alloc_fd(pos, evsel_list->cpus->nr,
-                                        evsel_list->threads->nr) < 0)
-                       goto out_free_fd;
                if (perf_header__push_event(pos->attr.config, event_name(pos)))
                        goto out_free_fd;
        }
 
-       if (perf_evlist__alloc_pollfd(evsel_list) < 0)
-               goto out_free_fd;
-
-       if (user_interval != ULLONG_MAX)
-               default_interval = user_interval;
-       if (user_freq != UINT_MAX)
-               freq = user_freq;
+       if (rec->opts.user_interval != ULLONG_MAX)
+               rec->opts.default_interval = rec->opts.user_interval;
+       if (rec->opts.user_freq != UINT_MAX)
+               rec->opts.freq = rec->opts.user_freq;
 
        /*
         * User specified count overrides default frequency.
         */
-       if (default_interval)
-               freq = 0;
-       else if (freq) {
-               default_interval = freq;
+       if (rec->opts.default_interval)
+               rec->opts.freq = 0;
+       else if (rec->opts.freq) {
+               rec->opts.default_interval = rec->opts.freq;
        } else {
                fprintf(stderr, "frequency and count are zero, aborting\n");
                err = -EINVAL;
                goto out_free_fd;
        }
 
-       err = __cmd_record(argc, argv);
+       err = __cmd_record(&record, argc, argv);
 out_free_fd:
        perf_evlist__delete_maps(evsel_list);
 out_symbol_exit:
index 4d7c8340c3267c82cca1b1a266f36b8ed3c1b793..ece7c5d3f504e7a664ca6d3d6c55962305db6f3c 100644 (file)
@@ -25,6 +25,7 @@
 #include "util/evsel.h"
 #include "util/header.h"
 #include "util/session.h"
+#include "util/tool.h"
 
 #include "util/parse-options.h"
 #include "util/parse-events.h"
 
 #include <linux/bitmap.h>
 
-static char            const *input_name = "perf.data";
-
-static bool            force, use_tui, use_stdio;
-static bool            hide_unresolved;
-static bool            dont_use_callchains;
-static bool            show_full_info;
-
-static bool            show_threads;
-static struct perf_read_values show_threads_values;
-
-static const char      default_pretty_printing_style[] = "normal";
-static const char      *pretty_printing_style = default_pretty_printing_style;
-
-static char            callchain_default_opt[] = "fractal,0.5,callee";
-static bool            inverted_callchain;
-static symbol_filter_t annotate_init;
-
-static const char      *cpu_list;
-static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
+struct perf_report {
+       struct perf_tool        tool;
+       struct perf_session     *session;
+       char const              *input_name;
+       bool                    force, use_tui, use_stdio;
+       bool                    hide_unresolved;
+       bool                    dont_use_callchains;
+       bool                    show_full_info;
+       bool                    show_threads;
+       bool                    inverted_callchain;
+       struct perf_read_values show_threads_values;
+       const char              *pretty_printing_style;
+       symbol_filter_t         annotate_init;
+       const char              *cpu_list;
+       DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
+};
 
-static int perf_session__add_hist_entry(struct perf_session *session,
-                                       struct addr_location *al,
-                                       struct perf_sample *sample,
-                                       struct perf_evsel *evsel)
+static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
+                                     struct addr_location *al,
+                                     struct perf_sample *sample,
+                                     struct machine *machine)
 {
        struct symbol *parent = NULL;
        int err = 0;
        struct hist_entry *he;
 
        if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
-               err = perf_session__resolve_callchain(session, al->thread,
-                                                     sample->callchain, &parent);
+               err = machine__resolve_callchain(machine, evsel, al->thread,
+                                                sample->callchain, &parent);
                if (err)
                        return err;
        }
@@ -76,7 +74,8 @@ static int perf_session__add_hist_entry(struct perf_session *session,
                return -ENOMEM;
 
        if (symbol_conf.use_callchain) {
-               err = callchain_append(he->callchain, &session->callchain_cursor,
+               err = callchain_append(he->callchain,
+                                      &evsel->hists.callchain_cursor,
                                       sample->period);
                if (err)
                        return err;
@@ -92,8 +91,7 @@ static int perf_session__add_hist_entry(struct perf_session *session,
                assert(evsel != NULL);
 
                err = -ENOMEM;
-               if (notes->src == NULL &&
-                   symbol__alloc_hist(he->ms.sym, session->evlist->nr_entries) < 0)
+               if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0)
                        goto out;
 
                err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
@@ -106,30 +104,32 @@ out:
 }
 
 
-static int process_sample_event(union perf_event *event,
+static int process_sample_event(struct perf_tool *tool,
+                               union perf_event *event,
                                struct perf_sample *sample,
                                struct perf_evsel *evsel,
-                               struct perf_session *session)
+                               struct machine *machine)
 {
+       struct perf_report *rep = container_of(tool, struct perf_report, tool);
        struct addr_location al;
 
-       if (perf_event__preprocess_sample(event, session, &al, sample,
-                                         annotate_init) < 0) {
+       if (perf_event__preprocess_sample(event, machine, &al, sample,
+                                         rep->annotate_init) < 0) {
                fprintf(stderr, "problem processing %d event, skipping it.\n",
                        event->header.type);
                return -1;
        }
 
-       if (al.filtered || (hide_unresolved && al.sym == NULL))
+       if (al.filtered || (rep->hide_unresolved && al.sym == NULL))
                return 0;
 
-       if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
+       if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
                return 0;
 
        if (al.map != NULL)
                al.map->dso->hit = 1;
 
-       if (perf_session__add_hist_entry(session, &al, sample, evsel)) {
+       if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) {
                pr_debug("problem incrementing symbol period, skipping event\n");
                return -1;
        }
@@ -137,15 +137,17 @@ static int process_sample_event(union perf_event *event,
        return 0;
 }
 
-static int process_read_event(union perf_event *event,
+static int process_read_event(struct perf_tool *tool,
+                             union perf_event *event,
                              struct perf_sample *sample __used,
-                             struct perf_session *session)
+                             struct perf_evsel *evsel,
+                             struct machine *machine __used)
 {
-       struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist,
-                                                        event->read.id);
-       if (show_threads) {
+       struct perf_report *rep = container_of(tool, struct perf_report, tool);
+
+       if (rep->show_threads) {
                const char *name = evsel ? event_name(evsel) : "unknown";
-               perf_read_values_add_value(&show_threads_values,
+               perf_read_values_add_value(&rep->show_threads_values,
                                           event->read.pid, event->read.tid,
                                           event->read.id,
                                           name,
@@ -159,8 +161,10 @@ static int process_read_event(union perf_event *event,
        return 0;
 }
 
-static int perf_session__setup_sample_type(struct perf_session *self)
+static int perf_report__setup_sample_type(struct perf_report *rep)
 {
+       struct perf_session *self = rep->session;
+
        if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
                if (sort__has_parent) {
                        ui__warning("Selected --sort parent, but no "
@@ -173,7 +177,8 @@ static int perf_session__setup_sample_type(struct perf_session *self)
                                    "you call 'perf record' without -g?\n");
                        return -1;
                }
-       } else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE &&
+       } else if (!rep->dont_use_callchains &&
+                  callchain_param.mode != CHAIN_NONE &&
                   !symbol_conf.use_callchain) {
                        symbol_conf.use_callchain = true;
                        if (callchain_register_param(&callchain_param) < 0) {
@@ -186,22 +191,6 @@ static int perf_session__setup_sample_type(struct perf_session *self)
        return 0;
 }
 
-static struct perf_event_ops event_ops = {
-       .sample          = process_sample_event,
-       .mmap            = perf_event__process_mmap,
-       .comm            = perf_event__process_comm,
-       .exit            = perf_event__process_task,
-       .fork            = perf_event__process_task,
-       .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,
-       .ordering_requires_timestamps = true,
-};
-
 extern volatile int session_done;
 
 static void sig_handler(int sig __used)
@@ -224,6 +213,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
 }
 
 static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
+                                        struct perf_report *rep,
                                         const char *help)
 {
        struct perf_evsel *pos;
@@ -241,18 +231,18 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
            parent_pattern == default_parent_pattern) {
                fprintf(stdout, "#\n# (%s)\n#\n", help);
 
-               if (show_threads) {
-                       bool style = !strcmp(pretty_printing_style, "raw");
-                       perf_read_values_display(stdout, &show_threads_values,
+               if (rep->show_threads) {
+                       bool style = !strcmp(rep->pretty_printing_style, "raw");
+                       perf_read_values_display(stdout, &rep->show_threads_values,
                                                 style);
-                       perf_read_values_destroy(&show_threads_values);
+                       perf_read_values_destroy(&rep->show_threads_values);
                }
        }
 
        return 0;
 }
 
-static int __cmd_report(void)
+static int __cmd_report(struct perf_report *rep)
 {
        int ret = -EINVAL;
        u64 nr_samples;
@@ -264,27 +254,31 @@ static int __cmd_report(void)
 
        signal(SIGINT, sig_handler);
 
-       session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
+       session = perf_session__new(rep->input_name, O_RDONLY,
+                                   rep->force, false, &rep->tool);
        if (session == NULL)
                return -ENOMEM;
 
-       if (cpu_list) {
-               ret = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap);
+       rep->session = session;
+
+       if (rep->cpu_list) {
+               ret = perf_session__cpu_bitmap(session, rep->cpu_list,
+                                              rep->cpu_bitmap);
                if (ret)
                        goto out_delete;
        }
 
        if (use_browser <= 0)
-               perf_session__fprintf_info(session, stdout, show_full_info);
+               perf_session__fprintf_info(session, stdout, rep->show_full_info);
 
-       if (show_threads)
-               perf_read_values_init(&show_threads_values);
+       if (rep->show_threads)
+               perf_read_values_init(&rep->show_threads_values);
 
-       ret = perf_session__setup_sample_type(session);
+       ret = perf_report__setup_sample_type(rep);
        if (ret)
                goto out_delete;
 
-       ret = perf_session__process_events(session, &event_ops);
+       ret = perf_session__process_events(session, &rep->tool);
        if (ret)
                goto out_delete;
 
@@ -327,7 +321,8 @@ static int __cmd_report(void)
        }
 
        if (nr_samples == 0) {
-               ui__warning("The %s file has no samples!\n", input_name);
+               ui__warning("The %s file has no samples!\n",
+                           rep->input_name);
                goto out_delete;
        }
 
@@ -335,7 +330,7 @@ static int __cmd_report(void)
                perf_evlist__tui_browse_hists(session->evlist, help,
                                              NULL, NULL, 0);
        } else
-               perf_evlist__tty_browse_hists(session->evlist, help);
+               perf_evlist__tty_browse_hists(session->evlist, rep, help);
 
 out_delete:
        /*
@@ -354,9 +349,9 @@ out_delete:
 }
 
 static int
-parse_callchain_opt(const struct option *opt __used, const char *arg,
-                   int unset)
+parse_callchain_opt(const struct option *opt, const char *arg, int unset)
 {
+       struct perf_report *rep = (struct perf_report *)opt->value;
        char *tok, *tok2;
        char *endptr;
 
@@ -364,7 +359,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg,
         * --no-call-graph
         */
        if (unset) {
-               dont_use_callchains = true;
+               rep->dont_use_callchains = true;
                return 0;
        }
 
@@ -433,13 +428,34 @@ setup:
        return 0;
 }
 
-static const char * const report_usage[] = {
-       "perf report [<options>] <command>",
-       NULL
-};
-
-static const struct option options[] = {
-       OPT_STRING('i', "input", &input_name, "file",
+int cmd_report(int argc, const char **argv, const char *prefix __used)
+{
+       char callchain_default_opt[] = "fractal,0.5,callee";
+       const char * const report_usage[] = {
+               "perf report [<options>] <command>",
+               NULL
+       };
+       struct perf_report report = {
+               .tool = {
+                       .sample          = process_sample_event,
+                       .mmap            = perf_event__process_mmap,
+                       .comm            = perf_event__process_comm,
+                       .exit            = perf_event__process_task,
+                       .fork            = perf_event__process_task,
+                       .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,
+                       .ordering_requires_timestamps = true,
+               },
+               .input_name              = "perf.data",
+               .pretty_printing_style   = "normal",
+       };
+       const struct option options[] = {
+       OPT_STRING('i', "input", &report.input_name, "file",
                    "input file name"),
        OPT_INCR('v', "verbose", &verbose,
                    "be more verbose (show symbol address, etc)"),
@@ -449,17 +465,18 @@ static const struct option options[] = {
                   "file", "vmlinux pathname"),
        OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
                   "file", "kallsyms pathname"),
-       OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
+       OPT_BOOLEAN('f', "force", &report.force, "don't complain, do it"),
        OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
                    "load module symbols - WARNING: use only with -k and LIVE kernel"),
        OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
                    "Show a column with the number of samples"),
-       OPT_BOOLEAN('T', "threads", &show_threads,
+       OPT_BOOLEAN('T', "threads", &report.show_threads,
                    "Show per-thread event counters"),
-       OPT_STRING(0, "pretty", &pretty_printing_style, "key",
+       OPT_STRING(0, "pretty", &report.pretty_printing_style, "key",
                   "pretty printing style key: normal raw"),
-       OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
-       OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
+       OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"),
+       OPT_BOOLEAN(0, "stdio", &report.use_stdio,
+                   "Use the stdio interface"),
        OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
                   "sort by key(s): pid, comm, dso, symbol, parent"),
        OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
@@ -468,13 +485,14 @@ static const struct option options[] = {
                   "regex filter to identify parent, see: '--sort parent'"),
        OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
                    "Only display entries with parent-match"),
-       OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent, call_order",
+       OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent, call_order",
                     "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold and callchain order. "
                     "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt),
-       OPT_BOOLEAN('G', "inverted", &inverted_callchain, "alias for inverted call graph"),
+       OPT_BOOLEAN('G', "inverted", &report.inverted_callchain,
+                   "alias for inverted call graph"),
        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...]",
+       OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
                   "only consider symbols in these comms"),
        OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
                   "only consider these symbols"),
@@ -484,12 +502,13 @@ static const struct option options[] = {
        OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
                   "separator for columns, no spaces will be added between "
                   "columns '.' is reserved."),
-       OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
+       OPT_BOOLEAN('U', "hide-unresolved", &report.hide_unresolved,
                    "Only display entries resolved to a symbol"),
        OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
                    "Look for files with symbols relative to this directory"),
-       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
-       OPT_BOOLEAN('I', "show-info", &show_full_info,
+       OPT_STRING('C', "cpu", &report.cpu_list, "cpu",
+                  "list of cpus to profile"),
+       OPT_BOOLEAN('I', "show-info", &report.show_full_info,
                    "Display extended information about perf.data file"),
        OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
                    "Interleave source code with assembly code (default)"),
@@ -500,21 +519,19 @@ static const struct option options[] = {
        OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
                    "Show a column with the sum of periods"),
        OPT_END()
-};
+       };
 
-int cmd_report(int argc, const char **argv, const char *prefix __used)
-{
        argc = parse_options(argc, argv, options, report_usage, 0);
 
-       if (use_stdio)
+       if (report.use_stdio)
                use_browser = 0;
-       else if (use_tui)
+       else if (report.use_tui)
                use_browser = 1;
 
-       if (inverted_callchain)
+       if (report.inverted_callchain)
                callchain_param.order = ORDER_CALLER;
 
-       if (strcmp(input_name, "-") != 0)
+       if (strcmp(report.input_name, "-") != 0)
                setup_browser(true);
        else
                use_browser = 0;
@@ -525,7 +542,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
         */
        if (use_browser > 0) {
                symbol_conf.priv_size = sizeof(struct annotation);
-               annotate_init         = symbol__annotate_init;
+               report.annotate_init  = symbol__annotate_init;
                /*
                 * For searching by name on the "Browse map details".
                 * providing it only in verbose mode not to bloat too
@@ -572,5 +589,5 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
        sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
        sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
 
-       return __cmd_report();
+       return __cmd_report(&report);
 }
index 5177964943e72ecd96881daf3bd48b71c1937faa..6284ed2317f2e6697a4f8227c20d86e55ea11e43 100644 (file)
@@ -2,11 +2,14 @@
 #include "perf.h"
 
 #include "util/util.h"
+#include "util/evlist.h"
 #include "util/cache.h"
+#include "util/evsel.h"
 #include "util/symbol.h"
 #include "util/thread.h"
 #include "util/header.h"
 #include "util/session.h"
+#include "util/tool.h"
 
 #include "util/parse-options.h"
 #include "util/trace-event.h"
@@ -723,21 +726,21 @@ struct trace_migrate_task_event {
 
 struct trace_sched_handler {
        void (*switch_event)(struct trace_switch_event *,
-                            struct perf_session *,
+                            struct machine *,
                             struct event *,
                             int cpu,
                             u64 timestamp,
                             struct thread *thread);
 
        void (*runtime_event)(struct trace_runtime_event *,
-                             struct perf_session *,
+                             struct machine *,
                              struct event *,
                              int cpu,
                              u64 timestamp,
                              struct thread *thread);
 
        void (*wakeup_event)(struct trace_wakeup_event *,
-                            struct perf_session *,
+                            struct machine *,
                             struct event *,
                             int cpu,
                             u64 timestamp,
@@ -750,7 +753,7 @@ struct trace_sched_handler {
                           struct thread *thread);
 
        void (*migrate_task_event)(struct trace_migrate_task_event *,
-                          struct perf_session *session,
+                          struct machine *machine,
                           struct event *,
                           int cpu,
                           u64 timestamp,
@@ -760,7 +763,7 @@ struct trace_sched_handler {
 
 static void
 replay_wakeup_event(struct trace_wakeup_event *wakeup_event,
-                   struct perf_session *session __used,
+                   struct machine *machine __used,
                    struct event *event,
                    int cpu __used,
                    u64 timestamp __used,
@@ -787,7 +790,7 @@ static u64 cpu_last_switched[MAX_CPUS];
 
 static void
 replay_switch_event(struct trace_switch_event *switch_event,
-                   struct perf_session *session __used,
+                   struct machine *machine __used,
                    struct event *event,
                    int cpu,
                    u64 timestamp,
@@ -1021,7 +1024,7 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
 
 static void
 latency_switch_event(struct trace_switch_event *switch_event,
-                    struct perf_session *session,
+                    struct machine *machine,
                     struct event *event __used,
                     int cpu,
                     u64 timestamp,
@@ -1045,8 +1048,8 @@ latency_switch_event(struct trace_switch_event *switch_event,
                die("hm, delta: %" PRIu64 " < 0 ?\n", delta);
 
 
-       sched_out = perf_session__findnew(session, switch_event->prev_pid);
-       sched_in = perf_session__findnew(session, switch_event->next_pid);
+       sched_out = machine__findnew_thread(machine, switch_event->prev_pid);
+       sched_in = machine__findnew_thread(machine, switch_event->next_pid);
 
        out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid);
        if (!out_events) {
@@ -1074,13 +1077,13 @@ latency_switch_event(struct trace_switch_event *switch_event,
 
 static void
 latency_runtime_event(struct trace_runtime_event *runtime_event,
-                    struct perf_session *session,
+                    struct machine *machine,
                     struct event *event __used,
                     int cpu,
                     u64 timestamp,
                     struct thread *this_thread __used)
 {
-       struct thread *thread = perf_session__findnew(session, runtime_event->pid);
+       struct thread *thread = machine__findnew_thread(machine, runtime_event->pid);
        struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid);
 
        BUG_ON(cpu >= MAX_CPUS || cpu < 0);
@@ -1097,7 +1100,7 @@ latency_runtime_event(struct trace_runtime_event *runtime_event,
 
 static void
 latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
-                    struct perf_session *session,
+                    struct machine *machine,
                     struct event *__event __used,
                     int cpu __used,
                     u64 timestamp,
@@ -1111,7 +1114,7 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
        if (!wakeup_event->success)
                return;
 
-       wakee = perf_session__findnew(session, wakeup_event->pid);
+       wakee = machine__findnew_thread(machine, wakeup_event->pid);
        atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid);
        if (!atoms) {
                thread_atoms_insert(wakee);
@@ -1145,7 +1148,7 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
 
 static void
 latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event,
-                    struct perf_session *session,
+                    struct machine *machine,
                     struct event *__event __used,
                     int cpu __used,
                     u64 timestamp,
@@ -1161,7 +1164,7 @@ latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event,
        if (profile_cpu == -1)
                return;
 
-       migrant = perf_session__findnew(session, migrate_task_event->pid);
+       migrant = machine__findnew_thread(machine, migrate_task_event->pid);
        atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid);
        if (!atoms) {
                thread_atoms_insert(migrant);
@@ -1356,12 +1359,13 @@ static void sort_lat(void)
 static struct trace_sched_handler *trace_handler;
 
 static void
-process_sched_wakeup_event(void *data, struct perf_session *session,
+process_sched_wakeup_event(struct perf_tool *tool __used,
                           struct event *event,
-                          int cpu __used,
-                          u64 timestamp __used,
-                          struct thread *thread __used)
+                          struct perf_sample *sample,
+                          struct machine *machine,
+                          struct thread *thread)
 {
+       void *data = sample->raw_data;
        struct trace_wakeup_event wakeup_event;
 
        FILL_COMMON_FIELDS(wakeup_event, event, data);
@@ -1373,8 +1377,8 @@ process_sched_wakeup_event(void *data, struct perf_session *session,
        FILL_FIELD(wakeup_event, cpu, event, data);
 
        if (trace_handler->wakeup_event)
-               trace_handler->wakeup_event(&wakeup_event, session, event,
-                                           cpu, timestamp, thread);
+               trace_handler->wakeup_event(&wakeup_event, machine, event,
+                                           sample->cpu, sample->time, thread);
 }
 
 /*
@@ -1392,7 +1396,7 @@ static char next_shortname2 = '0';
 
 static void
 map_switch_event(struct trace_switch_event *switch_event,
-                struct perf_session *session,
+                struct machine *machine,
                 struct event *event __used,
                 int this_cpu,
                 u64 timestamp,
@@ -1420,8 +1424,8 @@ map_switch_event(struct trace_switch_event *switch_event,
                die("hm, delta: %" PRIu64 " < 0 ?\n", delta);
 
 
-       sched_out = perf_session__findnew(session, switch_event->prev_pid);
-       sched_in = perf_session__findnew(session, switch_event->next_pid);
+       sched_out = machine__findnew_thread(machine, switch_event->prev_pid);
+       sched_in = machine__findnew_thread(machine, switch_event->next_pid);
 
        curr_thread[this_cpu] = sched_in;
 
@@ -1469,14 +1473,15 @@ map_switch_event(struct trace_switch_event *switch_event,
        }
 }
 
-
 static void
-process_sched_switch_event(void *data, struct perf_session *session,
+process_sched_switch_event(struct perf_tool *tool __used,
                           struct event *event,
-                          int this_cpu,
-                          u64 timestamp __used,
-                          struct thread *thread __used)
+                          struct perf_sample *sample,
+                          struct machine *machine,
+                          struct thread *thread)
 {
+       int this_cpu = sample->cpu;
+       void *data = sample->raw_data;
        struct trace_switch_event switch_event;
 
        FILL_COMMON_FIELDS(switch_event, event, data);
@@ -1498,19 +1503,20 @@ process_sched_switch_event(void *data, struct perf_session *session,
                        nr_context_switch_bugs++;
        }
        if (trace_handler->switch_event)
-               trace_handler->switch_event(&switch_event, session, event,
-                                           this_cpu, timestamp, thread);
+               trace_handler->switch_event(&switch_event, machine, event,
+                                           this_cpu, sample->time, thread);
 
        curr_pid[this_cpu] = switch_event.next_pid;
 }
 
 static void
-process_sched_runtime_event(void *data, struct perf_session *session,
-                          struct event *event,
-                          int cpu __used,
-                          u64 timestamp __used,
-                          struct thread *thread __used)
+process_sched_runtime_event(struct perf_tool *tool __used,
+                           struct event *event,
+                           struct perf_sample *sample,
+                           struct machine *machine,
+                           struct thread *thread)
 {
+       void *data = sample->raw_data;
        struct trace_runtime_event runtime_event;
 
        FILL_ARRAY(runtime_event, comm, event, data);
@@ -1519,16 +1525,18 @@ process_sched_runtime_event(void *data, struct perf_session *session,
        FILL_FIELD(runtime_event, vruntime, event, data);
 
        if (trace_handler->runtime_event)
-               trace_handler->runtime_event(&runtime_event, session, event, cpu, timestamp, thread);
+               trace_handler->runtime_event(&runtime_event, machine, event,
+                                            sample->cpu, sample->time, thread);
 }
 
 static void
-process_sched_fork_event(void *data,
+process_sched_fork_event(struct perf_tool *tool __used,
                         struct event *event,
-                        int cpu __used,
-                        u64 timestamp __used,
-                        struct thread *thread __used)
+                        struct perf_sample *sample,
+                        struct machine *machine __used,
+                        struct thread *thread)
 {
+       void *data = sample->raw_data;
        struct trace_fork_event fork_event;
 
        FILL_COMMON_FIELDS(fork_event, event, data);
@@ -1540,13 +1548,14 @@ process_sched_fork_event(void *data,
 
        if (trace_handler->fork_event)
                trace_handler->fork_event(&fork_event, event,
-                                         cpu, timestamp, thread);
+                                         sample->cpu, sample->time, thread);
 }
 
 static void
-process_sched_exit_event(struct event *event,
-                        int cpu __used,
-                        u64 timestamp __used,
+process_sched_exit_event(struct perf_tool *tool __used,
+                        struct event *event,
+                        struct perf_sample *sample __used,
+                        struct machine *machine __used,
                         struct thread *thread __used)
 {
        if (verbose)
@@ -1554,12 +1563,13 @@ process_sched_exit_event(struct event *event,
 }
 
 static void
-process_sched_migrate_task_event(void *data, struct perf_session *session,
-                          struct event *event,
-                          int cpu __used,
-                          u64 timestamp __used,
-                          struct thread *thread __used)
+process_sched_migrate_task_event(struct perf_tool *tool __used,
+                                struct event *event,
+                                struct perf_sample *sample,
+                                struct machine *machine,
+                                struct thread *thread)
 {
+       void *data = sample->raw_data;
        struct trace_migrate_task_event migrate_task_event;
 
        FILL_COMMON_FIELDS(migrate_task_event, event, data);
@@ -1570,67 +1580,47 @@ process_sched_migrate_task_event(void *data, struct perf_session *session,
        FILL_FIELD(migrate_task_event, cpu, event, data);
 
        if (trace_handler->migrate_task_event)
-               trace_handler->migrate_task_event(&migrate_task_event, session,
-                                                event, cpu, timestamp, thread);
+               trace_handler->migrate_task_event(&migrate_task_event, machine,
+                                                 event, sample->cpu,
+                                                 sample->time, thread);
 }
 
-static void process_raw_event(union perf_event *raw_event __used,
-                             struct perf_session *session, void *data, int cpu,
-                             u64 timestamp, struct thread *thread)
-{
-       struct event *event;
-       int type;
-
-
-       type = trace_parse_common_type(data);
-       event = trace_find_event(type);
-
-       if (!strcmp(event->name, "sched_switch"))
-               process_sched_switch_event(data, session, event, cpu, timestamp, thread);
-       if (!strcmp(event->name, "sched_stat_runtime"))
-               process_sched_runtime_event(data, session, event, cpu, timestamp, thread);
-       if (!strcmp(event->name, "sched_wakeup"))
-               process_sched_wakeup_event(data, session, event, cpu, timestamp, thread);
-       if (!strcmp(event->name, "sched_wakeup_new"))
-               process_sched_wakeup_event(data, session, event, cpu, timestamp, thread);
-       if (!strcmp(event->name, "sched_process_fork"))
-               process_sched_fork_event(data, event, cpu, timestamp, thread);
-       if (!strcmp(event->name, "sched_process_exit"))
-               process_sched_exit_event(event, cpu, timestamp, thread);
-       if (!strcmp(event->name, "sched_migrate_task"))
-               process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread);
-}
+typedef void (*tracepoint_handler)(struct perf_tool *tool, struct event *event,
+                                  struct perf_sample *sample,
+                                  struct machine *machine,
+                                  struct thread *thread);
 
-static int process_sample_event(union perf_event *event,
-                               struct perf_sample *sample,
-                               struct perf_evsel *evsel __used,
-                               struct perf_session *session)
+static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
+                                                union perf_event *event __used,
+                                                struct perf_sample *sample,
+                                                struct perf_evsel *evsel,
+                                                struct machine *machine)
 {
-       struct thread *thread;
-
-       if (!(session->sample_type & PERF_SAMPLE_RAW))
-               return 0;
+       struct thread *thread = machine__findnew_thread(machine, sample->pid);
 
-       thread = perf_session__findnew(session, sample->pid);
        if (thread == NULL) {
-               pr_debug("problem processing %d event, skipping it.\n",
-                        event->header.type);
+               pr_debug("problem processing %s event, skipping it.\n",
+                        evsel->name);
                return -1;
        }
 
-       dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
+       evsel->hists.stats.total_period += sample->period;
+       hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
 
-       if (profile_cpu != -1 && profile_cpu != (int)sample->cpu)
-               return 0;
+       if (evsel->handler.func != NULL) {
+               tracepoint_handler f = evsel->handler.func;
 
-       process_raw_event(event, session, sample->raw_data, sample->cpu,
-                         sample->time, thread);
+               if (evsel->handler.data == NULL)
+                       evsel->handler.data = trace_find_event(evsel->attr.config);
+
+               f(tool, evsel->handler.data, sample, machine, thread);
+       }
 
        return 0;
 }
 
-static struct perf_event_ops event_ops = {
-       .sample                 = process_sample_event,
+static struct perf_tool perf_sched = {
+       .sample                 = perf_sched__process_tracepoint_sample,
        .comm                   = perf_event__process_comm,
        .lost                   = perf_event__process_lost,
        .fork                   = perf_event__process_task,
@@ -1640,13 +1630,25 @@ static struct perf_event_ops event_ops = {
 static void read_events(bool destroy, struct perf_session **psession)
 {
        int err = -EINVAL;
+       const struct perf_evsel_str_handler handlers[] = {
+               { "sched:sched_switch",       process_sched_switch_event, },
+               { "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 = perf_session__new(input_name, O_RDONLY,
-                                                        0, false, &event_ops);
+                                                        0, false, &perf_sched);
        if (session == NULL)
                die("No Memory");
 
+       err = perf_evlist__set_tracepoints_handlers_array(session->evlist, handlers);
+       assert(err == 0);
+
        if (perf_session__has_traces(session, "record -R")) {
-               err = perf_session__process_events(session, &event_ops);
+               err = perf_session__process_events(session, &perf_sched);
                if (err)
                        die("Failed to process events, error %d", err);
 
index 2f62a2952269c2f58752d272154e10e7174805e9..619d6dcaa1d972203fe8ee1be8b43428b1fa2cbe 100644 (file)
@@ -7,6 +7,7 @@
 #include "util/header.h"
 #include "util/parse-options.h"
 #include "util/session.h"
+#include "util/tool.h"
 #include "util/symbol.h"
 #include "util/thread.h"
 #include "util/trace-event.h"
@@ -315,7 +316,7 @@ static bool sample_addr_correlates_sym(struct perf_event_attr *attr)
 
 static void print_sample_addr(union perf_event *event,
                          struct perf_sample *sample,
-                         struct perf_session *session,
+                         struct machine *machine,
                          struct thread *thread,
                          struct perf_event_attr *attr)
 {
@@ -328,11 +329,11 @@ static void print_sample_addr(union perf_event *event,
        if (!sample_addr_correlates_sym(attr))
                return;
 
-       thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
-                             event->ip.pid, sample->addr, &al);
+       thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
+                             sample->addr, &al);
        if (!al.map)
-               thread__find_addr_map(thread, session, cpumode, MAP__VARIABLE,
-                                     event->ip.pid, sample->addr, &al);
+               thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE,
+                                     sample->addr, &al);
 
        al.cpu = sample->cpu;
        al.sym = NULL;
@@ -362,7 +363,7 @@ static void print_sample_addr(union perf_event *event,
 static void process_event(union perf_event *event __unused,
                          struct perf_sample *sample,
                          struct perf_evsel *evsel,
-                         struct perf_session *session,
+                         struct machine *machine,
                          struct thread *thread)
 {
        struct perf_event_attr *attr = &evsel->attr;
@@ -377,15 +378,15 @@ static void process_event(union perf_event *event __unused,
                                  sample->raw_size);
 
        if (PRINT_FIELD(ADDR))
-               print_sample_addr(event, sample, session, thread, attr);
+               print_sample_addr(event, sample, machine, thread, attr);
 
        if (PRINT_FIELD(IP)) {
                if (!symbol_conf.use_callchain)
                        printf(" ");
                else
                        printf("\n");
-               perf_session__print_ip(event, sample, session,
-                                             PRINT_FIELD(SYM), PRINT_FIELD(DSO));
+               perf_event__print_ip(event, sample, machine, evsel,
+                                    PRINT_FIELD(SYM), PRINT_FIELD(DSO));
        }
 
        printf("\n");
@@ -434,12 +435,14 @@ static int cleanup_scripting(void)
 
 static char const              *input_name = "perf.data";
 
-static int process_sample_event(union perf_event *event,
+static int process_sample_event(struct perf_tool *tool __used,
+                               union perf_event *event,
                                struct perf_sample *sample,
                                struct perf_evsel *evsel,
-                               struct perf_session *session)
+                               struct machine *machine)
 {
-       struct thread *thread = perf_session__findnew(session, event->ip.pid);
+       struct addr_location al;
+       struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
 
        if (thread == NULL) {
                pr_debug("problem processing %d event, skipping it.\n",
@@ -458,16 +461,25 @@ static int process_sample_event(union perf_event *event,
                return 0;
        }
 
+       if (perf_event__preprocess_sample(event, machine, &al, sample, 0) < 0) {
+               pr_err("problem processing %d event, skipping it.\n",
+                      event->header.type);
+               return -1;
+       }
+
+       if (al.filtered)
+               return 0;
+
        if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
                return 0;
 
-       scripting_ops->process_event(event, sample, evsel, session, thread);
+       scripting_ops->process_event(event, sample, evsel, machine, thread);
 
-       session->hists.stats.total_period += sample->period;
+       evsel->hists.stats.total_period += sample->period;
        return 0;
 }
 
-static struct perf_event_ops event_ops = {
+static struct perf_tool perf_script = {
        .sample          = process_sample_event,
        .mmap            = perf_event__process_mmap,
        .comm            = perf_event__process_comm,
@@ -494,7 +506,7 @@ static int __cmd_script(struct perf_session *session)
 
        signal(SIGINT, sig_handler);
 
-       ret = perf_session__process_events(session, &event_ops);
+       ret = perf_session__process_events(session, &perf_script);
 
        if (debug_mode)
                pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered);
@@ -1083,7 +1095,9 @@ static const struct option options[] = {
        OPT_CALLBACK('f', "fields", NULL, "str",
                     "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr",
                     parse_output_fields),
-       OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
+       OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
+       OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
+                  "only display events for these comms"),
        OPT_BOOLEAN('I', "show-info", &show_full_info,
                    "display extended information from perf.data file"),
        OPT_END()
@@ -1261,7 +1275,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
        if (!script_name)
                setup_pager();
 
-       session = perf_session__new(input_name, O_RDONLY, 0, false, &event_ops);
+       session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_script);
        if (session == NULL)
                return -ENOMEM;
 
index 7d98676808d8722a39623de8983642f987cea7cc..4356144f1214b990a4db0b9dc170463e70103f9f 100644 (file)
@@ -1107,22 +1107,13 @@ static const struct option options[] = {
  */
 static int add_default_attributes(void)
 {
-       struct perf_evsel *pos;
-       size_t attr_nr = 0;
-       size_t c;
-
        /* Set attrs if no event is selected and !null_run: */
        if (null_run)
                return 0;
 
        if (!evsel_list->nr_entries) {
-               for (c = 0; c < ARRAY_SIZE(default_attrs); c++) {
-                       pos = perf_evsel__new(default_attrs + c, c + attr_nr);
-                       if (pos == NULL)
-                               return -1;
-                       perf_evlist__add(evsel_list, pos);
-               }
-               attr_nr += c;
+               if (perf_evlist__add_attrs_array(evsel_list, default_attrs) < 0)
+                       return -1;
        }
 
        /* Detailed events get appended to the event list: */
@@ -1131,38 +1122,21 @@ static int add_default_attributes(void)
                return 0;
 
        /* Append detailed run extra attributes: */
-       for (c = 0; c < ARRAY_SIZE(detailed_attrs); c++) {
-               pos = perf_evsel__new(detailed_attrs + c, c + attr_nr);
-               if (pos == NULL)
-                       return -1;
-               perf_evlist__add(evsel_list, pos);
-       }
-       attr_nr += c;
+       if (perf_evlist__add_attrs_array(evsel_list, detailed_attrs) < 0)
+               return -1;
 
        if (detailed_run < 2)
                return 0;
 
        /* Append very detailed run extra attributes: */
-       for (c = 0; c < ARRAY_SIZE(very_detailed_attrs); c++) {
-               pos = perf_evsel__new(very_detailed_attrs + c, c + attr_nr);
-               if (pos == NULL)
-                       return -1;
-               perf_evlist__add(evsel_list, pos);
-       }
+       if (perf_evlist__add_attrs_array(evsel_list, very_detailed_attrs) < 0)
+               return -1;
 
        if (detailed_run < 3)
                return 0;
 
        /* Append very, very detailed run extra attributes: */
-       for (c = 0; c < ARRAY_SIZE(very_very_detailed_attrs); c++) {
-               pos = perf_evsel__new(very_very_detailed_attrs + c, c + attr_nr);
-               if (pos == NULL)
-                       return -1;
-               perf_evlist__add(evsel_list, pos);
-       }
-
-
-       return 0;
+       return perf_evlist__add_attrs_array(evsel_list, very_very_detailed_attrs);
 }
 
 int cmd_stat(int argc, const char **argv, const char *prefix __used)
@@ -1266,8 +1240,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
 
        list_for_each_entry(pos, &evsel_list->entries, node) {
                if (perf_evsel__alloc_stat_priv(pos) < 0 ||
-                   perf_evsel__alloc_counts(pos, evsel_list->cpus->nr) < 0 ||
-                   perf_evsel__alloc_fd(pos, evsel_list->cpus->nr, evsel_list->threads->nr) < 0)
+                   perf_evsel__alloc_counts(pos, evsel_list->cpus->nr) < 0)
                        goto out_free_fd;
        }
 
index 831d1baeac370ebd330d0e6e970f025f34e76e44..6173f780dce0a614633a35d262721bdd379f3f07 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "util/cache.h"
 #include "util/debug.h"
+#include "util/debugfs.h"
 #include "util/evlist.h"
 #include "util/parse-options.h"
 #include "util/parse-events.h"
@@ -14,8 +15,6 @@
 #include "util/thread_map.h"
 #include "../../include/linux/hw_breakpoint.h"
 
-static long page_size;
-
 static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym)
 {
        bool *visited = symbol__priv(sym);
@@ -31,6 +30,7 @@ static int test__vmlinux_matches_kallsyms(void)
        struct map *kallsyms_map, *vmlinux_map;
        struct machine kallsyms, vmlinux;
        enum map_type type = MAP__FUNCTION;
+       long page_size = sysconf(_SC_PAGE_SIZE);
        struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", };
 
        /*
@@ -247,7 +247,7 @@ static int trace_event__id(const char *evname)
 
        if (asprintf(&filename,
                     "%s/syscalls/%s/id",
-                    debugfs_path, evname) < 0)
+                    tracing_events_path, evname) < 0)
                return -1;
 
        fd = open(filename, O_RDONLY);
@@ -841,6 +841,336 @@ static int test__parse_events(void)
 
        return ret;
 }
+
+static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t **maskp,
+                                        size_t *sizep)
+{
+       cpu_set_t *mask;
+       size_t size;
+       int i, cpu = -1, nrcpus = 1024;
+realloc:
+       mask = CPU_ALLOC(nrcpus);
+       size = CPU_ALLOC_SIZE(nrcpus);
+       CPU_ZERO_S(size, mask);
+
+       if (sched_getaffinity(pid, size, mask) == -1) {
+               CPU_FREE(mask);
+               if (errno == EINVAL && nrcpus < (1024 << 8)) {
+                       nrcpus = nrcpus << 2;
+                       goto realloc;
+               }
+               perror("sched_getaffinity");
+                       return -1;
+       }
+
+       for (i = 0; i < nrcpus; i++) {
+               if (CPU_ISSET_S(i, size, mask)) {
+                       if (cpu == -1) {
+                               cpu = i;
+                               *maskp = mask;
+                               *sizep = size;
+                       } else
+                               CPU_CLR_S(i, size, mask);
+               }
+       }
+
+       if (cpu == -1)
+               CPU_FREE(mask);
+
+       return cpu;
+}
+
+static int test__PERF_RECORD(void)
+{
+       struct perf_record_opts opts = {
+               .target_pid = -1,
+               .target_tid = -1,
+               .no_delay   = true,
+               .freq       = 10,
+               .mmap_pages = 256,
+               .sample_id_all_avail = true,
+       };
+       cpu_set_t *cpu_mask = NULL;
+       size_t cpu_mask_size = 0;
+       struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
+       struct perf_evsel *evsel;
+       struct perf_sample sample;
+       const char *cmd = "sleep";
+       const char *argv[] = { cmd, "1", NULL, };
+       char *bname;
+       u64 sample_type, prev_time = 0;
+       bool found_cmd_mmap = false,
+            found_libc_mmap = false,
+            found_vdso_mmap = false,
+            found_ld_mmap = false;
+       int err = -1, errs = 0, i, wakeups = 0, sample_size;
+       u32 cpu;
+       int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
+
+       if (evlist == NULL || argv == NULL) {
+               pr_debug("Not enough memory to create evlist\n");
+               goto out;
+       }
+
+       /*
+        * We need at least one evsel in the evlist, use the default
+        * one: "cycles".
+        */
+       err = perf_evlist__add_default(evlist);
+       if (err < 0) {
+               pr_debug("Not enough memory to create evsel\n");
+               goto out_delete_evlist;
+       }
+
+       /*
+        * Create maps of threads and cpus to monitor. In this case
+        * we start with all threads and cpus (-1, -1) but then in
+        * perf_evlist__prepare_workload we'll fill in the only thread
+        * we're monitoring, the one forked there.
+        */
+       err = perf_evlist__create_maps(evlist, opts.target_pid,
+                                      opts.target_tid, opts.cpu_list);
+       if (err < 0) {
+               pr_debug("Not enough memory to create thread/cpu maps\n");
+               goto out_delete_evlist;
+       }
+
+       /*
+        * Prepare the workload in argv[] to run, it'll fork it, and then wait
+        * for perf_evlist__start_workload() to exec it. This is done this way
+        * so that we have time to open the evlist (calling sys_perf_event_open
+        * on all the fds) and then mmap them.
+        */
+       err = perf_evlist__prepare_workload(evlist, &opts, argv);
+       if (err < 0) {
+               pr_debug("Couldn't run the workload!\n");
+               goto out_delete_evlist;
+       }
+
+       /*
+        * Config the evsels, setting attr->comm on the first one, etc.
+        */
+       evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
+       evsel->attr.sample_type |= PERF_SAMPLE_CPU;
+       evsel->attr.sample_type |= PERF_SAMPLE_TID;
+       evsel->attr.sample_type |= PERF_SAMPLE_TIME;
+       perf_evlist__config_attrs(evlist, &opts);
+
+       err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask,
+                                           &cpu_mask_size);
+       if (err < 0) {
+               pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno));
+               goto out_delete_evlist;
+       }
+
+       cpu = err;
+
+       /*
+        * So that we can check perf_sample.cpu on all the samples.
+        */
+       if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, cpu_mask) < 0) {
+               pr_debug("sched_setaffinity: %s\n", strerror(errno));
+               goto out_free_cpu_mask;
+       }
+
+       /*
+        * Call sys_perf_event_open on all the fds on all the evsels,
+        * grouping them if asked to.
+        */
+       err = perf_evlist__open(evlist, opts.group);
+       if (err < 0) {
+               pr_debug("perf_evlist__open: %s\n", strerror(errno));
+               goto out_delete_evlist;
+       }
+
+       /*
+        * mmap the first fd on a given CPU and ask for events for the other
+        * fds in the same CPU to be injected in the same mmap ring buffer
+        * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)).
+        */
+       err = perf_evlist__mmap(evlist, opts.mmap_pages, false);
+       if (err < 0) {
+               pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
+               goto out_delete_evlist;
+       }
+
+       /*
+        * We'll need these two to parse the PERF_SAMPLE_* fields in each
+        * event.
+        */
+       sample_type = perf_evlist__sample_type(evlist);
+       sample_size = __perf_evsel__sample_size(sample_type);
+
+       /*
+        * Now that all is properly set up, enable the events, they will
+        * count just on workload.pid, which will start...
+        */
+       perf_evlist__enable(evlist);
+
+       /*
+        * Now!
+        */
+       perf_evlist__start_workload(evlist);
+
+       while (1) {
+               int before = total_events;
+
+               for (i = 0; i < evlist->nr_mmaps; i++) {
+                       union perf_event *event;
+
+                       while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+                               const u32 type = event->header.type;
+                               const char *name = perf_event__name(type);
+
+                               ++total_events;
+                               if (type < PERF_RECORD_MAX)
+                                       nr_events[type]++;
+
+                               err = perf_event__parse_sample(event, sample_type,
+                                                              sample_size, true,
+                                                              &sample, false);
+                               if (err < 0) {
+                                       if (verbose)
+                                               perf_event__fprintf(event, stderr);
+                                       pr_debug("Couldn't parse sample\n");
+                                       goto out_err;
+                               }
+
+                               if (verbose) {
+                                       pr_info("%" PRIu64" %d ", sample.time, sample.cpu);
+                                       perf_event__fprintf(event, stderr);
+                               }
+
+                               if (prev_time > sample.time) {
+                                       pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n",
+                                                name, prev_time, sample.time);
+                                       ++errs;
+                               }
+
+                               prev_time = sample.time;
+
+                               if (sample.cpu != cpu) {
+                                       pr_debug("%s with unexpected cpu, expected %d, got %d\n",
+                                                name, cpu, sample.cpu);
+                                       ++errs;
+                               }
+
+                               if ((pid_t)sample.pid != evlist->workload.pid) {
+                                       pr_debug("%s with unexpected pid, expected %d, got %d\n",
+                                                name, evlist->workload.pid, sample.pid);
+                                       ++errs;
+                               }
+
+                               if ((pid_t)sample.tid != evlist->workload.pid) {
+                                       pr_debug("%s with unexpected tid, expected %d, got %d\n",
+                                                name, evlist->workload.pid, sample.tid);
+                                       ++errs;
+                               }
+
+                               if ((type == PERF_RECORD_COMM ||
+                                    type == PERF_RECORD_MMAP ||
+                                    type == PERF_RECORD_FORK ||
+                                    type == PERF_RECORD_EXIT) &&
+                                    (pid_t)event->comm.pid != evlist->workload.pid) {
+                                       pr_debug("%s with unexpected pid/tid\n", name);
+                                       ++errs;
+                               }
+
+                               if ((type == PERF_RECORD_COMM ||
+                                    type == PERF_RECORD_MMAP) &&
+                                    event->comm.pid != event->comm.tid) {
+                                       pr_debug("%s with different pid/tid!\n", name);
+                                       ++errs;
+                               }
+
+                               switch (type) {
+                               case PERF_RECORD_COMM:
+                                       if (strcmp(event->comm.comm, cmd)) {
+                                               pr_debug("%s with unexpected comm!\n", name);
+                                               ++errs;
+                                       }
+                                       break;
+                               case PERF_RECORD_EXIT:
+                                       goto found_exit;
+                               case PERF_RECORD_MMAP:
+                                       bname = strrchr(event->mmap.filename, '/');
+                                       if (bname != NULL) {
+                                               if (!found_cmd_mmap)
+                                                       found_cmd_mmap = !strcmp(bname + 1, cmd);
+                                               if (!found_libc_mmap)
+                                                       found_libc_mmap = !strncmp(bname + 1, "libc", 4);
+                                               if (!found_ld_mmap)
+                                                       found_ld_mmap = !strncmp(bname + 1, "ld", 2);
+                                       } else if (!found_vdso_mmap)
+                                               found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]");
+                                       break;
+
+                               case PERF_RECORD_SAMPLE:
+                                       /* Just ignore samples for now */
+                                       break;
+                               default:
+                                       pr_debug("Unexpected perf_event->header.type %d!\n",
+                                                type);
+                                       ++errs;
+                               }
+                       }
+               }
+
+               /*
+                * We don't use poll here because at least at 3.1 times the
+                * PERF_RECORD_{!SAMPLE} events don't honour
+                * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does.
+                */
+               if (total_events == before && false)
+                       poll(evlist->pollfd, evlist->nr_fds, -1);
+
+               sleep(1);
+               if (++wakeups > 5) {
+                       pr_debug("No PERF_RECORD_EXIT event!\n");
+                       break;
+               }
+       }
+
+found_exit:
+       if (nr_events[PERF_RECORD_COMM] > 1) {
+               pr_debug("Excessive number of PERF_RECORD_COMM events!\n");
+               ++errs;
+       }
+
+       if (nr_events[PERF_RECORD_COMM] == 0) {
+               pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd);
+               ++errs;
+       }
+
+       if (!found_cmd_mmap) {
+               pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd);
+               ++errs;
+       }
+
+       if (!found_libc_mmap) {
+               pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc");
+               ++errs;
+       }
+
+       if (!found_ld_mmap) {
+               pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld");
+               ++errs;
+       }
+
+       if (!found_vdso_mmap) {
+               pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]");
+               ++errs;
+       }
+out_err:
+       perf_evlist__munmap(evlist);
+out_free_cpu_mask:
+       CPU_FREE(cpu_mask);
+out_delete_evlist:
+       perf_evlist__delete(evlist);
+out:
+       return (err < 0 || errs > 0) ? -1 : 0;
+}
+
 static struct test {
        const char *desc;
        int (*func)(void);
@@ -865,46 +1195,90 @@ static struct test {
                .desc = "parse events tests",
                .func = test__parse_events,
        },
+       {
+               .desc = "Validate PERF_RECORD_* events & perf_sample fields",
+               .func = test__PERF_RECORD,
+       },
        {
                .func = NULL,
        },
 };
 
-static int __cmd_test(void)
+static bool perf_test__matches(int curr, int argc, const char *argv[])
 {
-       int i = 0;
+       int i;
+
+       if (argc == 0)
+               return true;
 
-       page_size = sysconf(_SC_PAGE_SIZE);
+       for (i = 0; i < argc; ++i) {
+               char *end;
+               long nr = strtoul(argv[i], &end, 10);
+
+               if (*end == '\0') {
+                       if (nr == curr + 1)
+                               return true;
+                       continue;
+               }
+
+               if (strstr(tests[curr].desc, argv[i]))
+                       return true;
+       }
+
+       return false;
+}
+
+static int __cmd_test(int argc, const char *argv[])
+{
+       int i = 0;
 
        while (tests[i].func) {
-               int err;
-               pr_info("%2d: %s:", i + 1, tests[i].desc);
+               int curr = i++, err;
+
+               if (!perf_test__matches(curr, argc, argv))
+                       continue;
+
+               pr_info("%2d: %s:", i, tests[curr].desc);
                pr_debug("\n--- start ---\n");
-               err = tests[i].func();
-               pr_debug("---- end ----\n%s:", tests[i].desc);
+               err = tests[curr].func();
+               pr_debug("---- end ----\n%s:", tests[curr].desc);
                pr_info(" %s\n", err ? "FAILED!\n" : "Ok");
-               ++i;
        }
 
        return 0;
 }
 
-static const char * const test_usage[] = {
-       "perf test [<options>]",
-       NULL,
-};
+static int perf_test__list(int argc, const char **argv)
+{
+       int i = 0;
 
-static const struct option test_options[] = {
+       while (tests[i].func) {
+               int curr = i++;
+
+               if (argc > 1 && !strstr(tests[curr].desc, argv[1]))
+                       continue;
+
+               pr_info("%2d: %s\n", i, tests[curr].desc);
+       }
+
+       return 0;
+}
+
+int cmd_test(int argc, const char **argv, const char *prefix __used)
+{
+       const char * const test_usage[] = {
+       "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]",
+       NULL,
+       };
+       const struct option test_options[] = {
        OPT_INTEGER('v', "verbose", &verbose,
                    "be more verbose (show symbol address, etc)"),
        OPT_END()
-};
+       };
 
-int cmd_test(int argc, const char **argv, const char *prefix __used)
-{
        argc = parse_options(argc, argv, test_options, test_usage, 0);
-       if (argc)
-               usage_with_options(test_usage, test_options);
+       if (argc >= 1 && !strcmp(argv[0], "list"))
+               return perf_test__list(argc, argv);
 
        symbol_conf.priv_size = sizeof(int);
        symbol_conf.sort_by_name = true;
@@ -915,5 +1289,5 @@ int cmd_test(int argc, const char **argv, const char *prefix __used)
 
        setup_pager();
 
-       return __cmd_test();
+       return __cmd_test(argc, argv);
 }
index aa26f4d66d1020eb075942dc42fdc874d7dfaddc..135376a37f974e24cc18db3c0466046bfae4690f 100644 (file)
@@ -19,6 +19,7 @@
 #include "util/color.h"
 #include <linux/list.h>
 #include "util/cache.h"
+#include "util/evsel.h"
 #include <linux/rbtree.h>
 #include "util/symbol.h"
 #include "util/callchain.h"
@@ -31,6 +32,7 @@
 #include "util/event.h"
 #include "util/session.h"
 #include "util/svghelper.h"
+#include "util/tool.h"
 
 #define SUPPORT_OLD_POWER_EVENTS 1
 #define PWR_EVENT_EXIT -1
@@ -273,25 +275,28 @@ static int cpus_cstate_state[MAX_CPUS];
 static u64 cpus_pstate_start_times[MAX_CPUS];
 static u64 cpus_pstate_state[MAX_CPUS];
 
-static int process_comm_event(union perf_event *event,
+static int process_comm_event(struct perf_tool *tool __used,
+                             union perf_event *event,
                              struct perf_sample *sample __used,
-                             struct perf_session *session __used)
+                             struct machine *machine __used)
 {
        pid_set_comm(event->comm.tid, event->comm.comm);
        return 0;
 }
 
-static int process_fork_event(union perf_event *event,
+static int process_fork_event(struct perf_tool *tool __used,
+                             union perf_event *event,
                              struct perf_sample *sample __used,
-                             struct perf_session *session __used)
+                             struct machine *machine __used)
 {
        pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
        return 0;
 }
 
-static int process_exit_event(union perf_event *event,
+static int process_exit_event(struct perf_tool *tool __used,
+                             union perf_event *event,
                              struct perf_sample *sample __used,
-                             struct perf_session *session __used)
+                             struct machine *machine __used)
 {
        pid_exit(event->fork.pid, event->fork.time);
        return 0;
@@ -486,14 +491,15 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
 }
 
 
-static int process_sample_event(union perf_event *event __used,
+static int process_sample_event(struct perf_tool *tool __used,
+                               union perf_event *event __used,
                                struct perf_sample *sample,
-                               struct perf_evsel *evsel __used,
-                               struct perf_session *session)
+                               struct perf_evsel *evsel,
+                               struct machine *machine __used)
 {
        struct trace_entry *te;
 
-       if (session->sample_type & PERF_SAMPLE_TIME) {
+       if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
                if (!first_time || first_time > sample->time)
                        first_time = sample->time;
                if (last_time < sample->time)
@@ -501,7 +507,7 @@ static int process_sample_event(union perf_event *event __used,
        }
 
        te = (void *)sample->raw_data;
-       if (session->sample_type & PERF_SAMPLE_RAW && sample->raw_size > 0) {
+       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;
@@ -974,7 +980,7 @@ static void write_svg_file(const char *filename)
        svg_close();
 }
 
-static struct perf_event_ops event_ops = {
+static struct perf_tool perf_timechart = {
        .comm                   = process_comm_event,
        .fork                   = process_fork_event,
        .exit                   = process_exit_event,
@@ -985,7 +991,7 @@ static struct perf_event_ops event_ops = {
 static int __cmd_timechart(void)
 {
        struct perf_session *session = perf_session__new(input_name, O_RDONLY,
-                                                        0, false, &event_ops);
+                                                        0, false, &perf_timechart);
        int ret = -EINVAL;
 
        if (session == NULL)
@@ -994,7 +1000,7 @@ static int __cmd_timechart(void)
        if (!perf_session__has_traces(session, "timechart record"))
                goto out_delete;
 
-       ret = perf_session__process_events(session, &event_ops);
+       ret = perf_session__process_events(session, &perf_timechart);
        if (ret)
                goto out_delete;
 
index c9cdedb581348aa158677b28f6465734abcb50d4..c3836b966ccfbf85b2501f4c97288058f1439553 100644 (file)
 #include <linux/unistd.h>
 #include <linux/types.h>
 
-static struct perf_top top = {
-       .count_filter           = 5,
-       .delay_secs             = 2,
-       .target_pid             = -1,
-       .target_tid             = -1,
-       .freq                   = 1000, /* 1 KHz */
-};
-
-static bool                    system_wide                     =  false;
-
-static bool                    use_tui, use_stdio;
-
-static bool                    sort_has_symbols;
-
-static bool                    dont_use_callchains;
-static char                    callchain_default_opt[]         = "fractal,0.5,callee";
-
-
-static int                     default_interval                =      0;
-
-static bool                    kptr_restrict_warned;
-static bool                    vmlinux_warned;
-static bool                    inherit                         =  false;
-static int                     realtime_prio                   =      0;
-static bool                    group                           =  false;
-static bool                    sample_id_all_avail             =   true;
-static unsigned int            mmap_pages                      =    128;
-
-static bool                    dump_symtab                     =  false;
-
-static struct winsize          winsize;
-
-static const char              *sym_filter                     =   NULL;
-static int                     sym_pcnt_filter                 =      5;
-
-/*
- * Source functions
- */
 
 void get_term_dimensions(struct winsize *ws)
 {
@@ -125,21 +87,23 @@ void get_term_dimensions(struct winsize *ws)
        ws->ws_col = 80;
 }
 
-static void update_print_entries(struct winsize *ws)
+static void perf_top__update_print_entries(struct perf_top *top)
 {
-       top.print_entries = ws->ws_row;
+       top->print_entries = top->winsize.ws_row;
 
-       if (top.print_entries > 9)
-               top.print_entries -= 9;
+       if (top->print_entries > 9)
+               top->print_entries -= 9;
 }
 
-static void sig_winch_handler(int sig __used)
+static void perf_top__sig_winch(int sig __used, siginfo_t *info __used, void *arg)
 {
-       get_term_dimensions(&winsize);
-       update_print_entries(&winsize);
+       struct perf_top *top = arg;
+
+       get_term_dimensions(&top->winsize);
+       perf_top__update_print_entries(top);
 }
 
-static int parse_source(struct hist_entry *he)
+static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he)
 {
        struct symbol *sym;
        struct annotation *notes;
@@ -170,7 +134,7 @@ static int parse_source(struct hist_entry *he)
 
        pthread_mutex_lock(&notes->lock);
 
-       if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) {
+       if (symbol__alloc_hist(sym) < 0) {
                pthread_mutex_unlock(&notes->lock);
                pr_err("Not enough memory for annotating '%s' symbol!\n",
                       sym->name);
@@ -181,7 +145,7 @@ static int parse_source(struct hist_entry *he)
        err = symbol__annotate(sym, map, 0);
        if (err == 0) {
 out_assign:
-               top.sym_filter_entry = he;
+               top->sym_filter_entry = he;
        }
 
        pthread_mutex_unlock(&notes->lock);
@@ -194,14 +158,16 @@ static void __zero_source_counters(struct hist_entry *he)
        symbol__annotate_zero_histograms(sym);
 }
 
-static void record_precise_ip(struct hist_entry *he, int counter, u64 ip)
+static void perf_top__record_precise_ip(struct perf_top *top,
+                                       struct hist_entry *he,
+                                       int counter, u64 ip)
 {
        struct annotation *notes;
        struct symbol *sym;
 
        if (he == NULL || he->ms.sym == NULL ||
-           ((top.sym_filter_entry == NULL ||
-             top.sym_filter_entry->ms.sym != he->ms.sym) && use_browser != 1))
+           ((top->sym_filter_entry == NULL ||
+             top->sym_filter_entry->ms.sym != he->ms.sym) && use_browser != 1))
                return;
 
        sym = he->ms.sym;
@@ -210,8 +176,7 @@ static void record_precise_ip(struct hist_entry *he, int counter, u64 ip)
        if (pthread_mutex_trylock(&notes->lock))
                return;
 
-       if (notes->src == NULL &&
-           symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) {
+       if (notes->src == NULL && symbol__alloc_hist(sym) < 0) {
                pthread_mutex_unlock(&notes->lock);
                pr_err("Not enough memory for annotating '%s' symbol!\n",
                       sym->name);
@@ -225,8 +190,9 @@ static void record_precise_ip(struct hist_entry *he, int counter, u64 ip)
        pthread_mutex_unlock(&notes->lock);
 }
 
-static void show_details(struct hist_entry *he)
+static void perf_top__show_details(struct perf_top *top)
 {
+       struct hist_entry *he = top->sym_filter_entry;
        struct annotation *notes;
        struct symbol *symbol;
        int more;
@@ -242,15 +208,15 @@ static void show_details(struct hist_entry *he)
        if (notes->src == NULL)
                goto out_unlock;
 
-       printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name);
-       printf("  Events  Pcnt (>=%d%%)\n", sym_pcnt_filter);
+       printf("Showing %s for %s\n", event_name(top->sym_evsel), symbol->name);
+       printf("  Events  Pcnt (>=%d%%)\n", top->sym_pcnt_filter);
 
-       more = symbol__annotate_printf(symbol, he->ms.map, top.sym_evsel->idx,
-                                      0, sym_pcnt_filter, top.print_entries, 4);
-       if (top.zero)
-               symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx);
+       more = symbol__annotate_printf(symbol, he->ms.map, top->sym_evsel->idx,
+                                      0, top->sym_pcnt_filter, top->print_entries, 4);
+       if (top->zero)
+               symbol__annotate_zero_histogram(symbol, top->sym_evsel->idx);
        else
-               symbol__annotate_decay_histogram(symbol, top.sym_evsel->idx);
+               symbol__annotate_decay_histogram(symbol, top->sym_evsel->idx);
        if (more != 0)
                printf("%d lines not displayed, maybe increase display entries [e]\n", more);
 out_unlock:
@@ -259,11 +225,9 @@ out_unlock:
 
 static const char              CONSOLE_CLEAR[] = "\e[H\e[2J";
 
-static struct hist_entry *
-       perf_session__add_hist_entry(struct perf_session *session,
-                                    struct addr_location *al,
-                                    struct perf_sample *sample,
-                                    struct perf_evsel *evsel)
+static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel,
+                                                    struct addr_location *al,
+                                                    struct perf_sample *sample)
 {
        struct hist_entry *he;
 
@@ -271,50 +235,51 @@ static struct hist_entry *
        if (he == NULL)
                return NULL;
 
-       session->hists.stats.total_period += sample->period;
+       evsel->hists.stats.total_period += sample->period;
        hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
        return he;
 }
 
-static void print_sym_table(void)
+static void perf_top__print_sym_table(struct perf_top *top)
 {
        char bf[160];
        int printed = 0;
-       const int win_width = winsize.ws_col - 1;
+       const int win_width = top->winsize.ws_col - 1;
 
        puts(CONSOLE_CLEAR);
 
-       perf_top__header_snprintf(&top, bf, sizeof(bf));
+       perf_top__header_snprintf(top, bf, sizeof(bf));
        printf("%s\n", bf);
 
-       perf_top__reset_sample_counters(&top);
+       perf_top__reset_sample_counters(top);
 
        printf("%-*.*s\n", win_width, win_width, graph_dotted_line);
 
-       if (top.sym_evsel->hists.stats.nr_lost_warned !=
-           top.sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) {
-               top.sym_evsel->hists.stats.nr_lost_warned =
-                       top.sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST];
+       if (top->sym_evsel->hists.stats.nr_lost_warned !=
+           top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST]) {
+               top->sym_evsel->hists.stats.nr_lost_warned =
+                       top->sym_evsel->hists.stats.nr_events[PERF_RECORD_LOST];
                color_fprintf(stdout, PERF_COLOR_RED,
                              "WARNING: LOST %d chunks, Check IO/CPU overload",
-                             top.sym_evsel->hists.stats.nr_lost_warned);
+                             top->sym_evsel->hists.stats.nr_lost_warned);
                ++printed;
        }
 
-       if (top.sym_filter_entry) {
-               show_details(top.sym_filter_entry);
+       if (top->sym_filter_entry) {
+               perf_top__show_details(top);
                return;
        }
 
-       hists__collapse_resort_threaded(&top.sym_evsel->hists);
-       hists__output_resort_threaded(&top.sym_evsel->hists);
-       hists__decay_entries_threaded(&top.sym_evsel->hists,
-                                     top.hide_user_symbols,
-                                     top.hide_kernel_symbols);
-       hists__output_recalc_col_len(&top.sym_evsel->hists, winsize.ws_row - 3);
+       hists__collapse_resort_threaded(&top->sym_evsel->hists);
+       hists__output_resort_threaded(&top->sym_evsel->hists);
+       hists__decay_entries_threaded(&top->sym_evsel->hists,
+                                     top->hide_user_symbols,
+                                     top->hide_kernel_symbols);
+       hists__output_recalc_col_len(&top->sym_evsel->hists,
+                                    top->winsize.ws_row - 3);
        putchar('\n');
-       hists__fprintf(&top.sym_evsel->hists, NULL, false, false,
-                      winsize.ws_row - 4 - printed, win_width, stdout);
+       hists__fprintf(&top->sym_evsel->hists, NULL, false, false,
+                      top->winsize.ws_row - 4 - printed, win_width, stdout);
 }
 
 static void prompt_integer(int *target, const char *msg)
@@ -352,17 +317,17 @@ static void prompt_percent(int *target, const char *msg)
                *target = tmp;
 }
 
-static void prompt_symbol(struct hist_entry **target, const char *msg)
+static void perf_top__prompt_symbol(struct perf_top *top, const char *msg)
 {
        char *buf = malloc(0), *p;
-       struct hist_entry *syme = *target, *n, *found = NULL;
+       struct hist_entry *syme = top->sym_filter_entry, *n, *found = NULL;
        struct rb_node *next;
        size_t dummy = 0;
 
        /* zero counters of active symbol */
        if (syme) {
                __zero_source_counters(syme);
-               *target = NULL;
+               top->sym_filter_entry = NULL;
        }
 
        fprintf(stdout, "\n%s: ", msg);
@@ -373,7 +338,7 @@ static void prompt_symbol(struct hist_entry **target, const char *msg)
        if (p)
                *p = 0;
 
-       next = rb_first(&top.sym_evsel->hists.entries);
+       next = rb_first(&top->sym_evsel->hists.entries);
        while (next) {
                n = rb_entry(next, struct hist_entry, rb_node);
                if (n->ms.sym && !strcmp(buf, n->ms.sym->name)) {
@@ -388,45 +353,45 @@ static void prompt_symbol(struct hist_entry **target, const char *msg)
                sleep(1);
                return;
        } else
-               parse_source(found);
+               perf_top__parse_source(top, found);
 
 out_free:
        free(buf);
 }
 
-static void print_mapped_keys(void)
+static void perf_top__print_mapped_keys(struct perf_top *top)
 {
        char *name = NULL;
 
-       if (top.sym_filter_entry) {
-               struct symbol *sym = top.sym_filter_entry->ms.sym;
+       if (top->sym_filter_entry) {
+               struct symbol *sym = top->sym_filter_entry->ms.sym;
                name = sym->name;
        }
 
        fprintf(stdout, "\nMapped keys:\n");
-       fprintf(stdout, "\t[d]     display refresh delay.             \t(%d)\n", top.delay_secs);
-       fprintf(stdout, "\t[e]     display entries (lines).           \t(%d)\n", top.print_entries);
+       fprintf(stdout, "\t[d]     display refresh delay.             \t(%d)\n", top->delay_secs);
+       fprintf(stdout, "\t[e]     display entries (lines).           \t(%d)\n", top->print_entries);
 
-       if (top.evlist->nr_entries > 1)
-               fprintf(stdout, "\t[E]     active event counter.              \t(%s)\n", event_name(top.sym_evsel));
+       if (top->evlist->nr_entries > 1)
+               fprintf(stdout, "\t[E]     active event counter.              \t(%s)\n", event_name(top->sym_evsel));
 
-       fprintf(stdout, "\t[f]     profile display filter (count).    \t(%d)\n", top.count_filter);
+       fprintf(stdout, "\t[f]     profile display filter (count).    \t(%d)\n", top->count_filter);
 
-       fprintf(stdout, "\t[F]     annotate display filter (percent). \t(%d%%)\n", sym_pcnt_filter);
+       fprintf(stdout, "\t[F]     annotate display filter (percent). \t(%d%%)\n", top->sym_pcnt_filter);
        fprintf(stdout, "\t[s]     annotate symbol.                   \t(%s)\n", name?: "NULL");
        fprintf(stdout, "\t[S]     stop annotation.\n");
 
        fprintf(stdout,
                "\t[K]     hide kernel_symbols symbols.     \t(%s)\n",
-               top.hide_kernel_symbols ? "yes" : "no");
+               top->hide_kernel_symbols ? "yes" : "no");
        fprintf(stdout,
                "\t[U]     hide user symbols.               \t(%s)\n",
-               top.hide_user_symbols ? "yes" : "no");
-       fprintf(stdout, "\t[z]     toggle sample zeroing.             \t(%d)\n", top.zero ? 1 : 0);
+               top->hide_user_symbols ? "yes" : "no");
+       fprintf(stdout, "\t[z]     toggle sample zeroing.             \t(%d)\n", top->zero ? 1 : 0);
        fprintf(stdout, "\t[qQ]    quit.\n");
 }
 
-static int key_mapped(int c)
+static int perf_top__key_mapped(struct perf_top *top, int c)
 {
        switch (c) {
                case 'd':
@@ -442,7 +407,7 @@ static int key_mapped(int c)
                case 'S':
                        return 1;
                case 'E':
-                       return top.evlist->nr_entries > 1 ? 1 : 0;
+                       return top->evlist->nr_entries > 1 ? 1 : 0;
                default:
                        break;
        }
@@ -450,13 +415,13 @@ static int key_mapped(int c)
        return 0;
 }
 
-static void handle_keypress(int c)
+static void perf_top__handle_keypress(struct perf_top *top, int c)
 {
-       if (!key_mapped(c)) {
+       if (!perf_top__key_mapped(top, c)) {
                struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
                struct termios tc, save;
 
-               print_mapped_keys();
+               perf_top__print_mapped_keys(top);
                fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
                fflush(stdout);
 
@@ -471,81 +436,86 @@ static void handle_keypress(int c)
                c = getc(stdin);
 
                tcsetattr(0, TCSAFLUSH, &save);
-               if (!key_mapped(c))
+               if (!perf_top__key_mapped(top, c))
                        return;
        }
 
        switch (c) {
                case 'd':
-                       prompt_integer(&top.delay_secs, "Enter display delay");
-                       if (top.delay_secs < 1)
-                               top.delay_secs = 1;
+                       prompt_integer(&top->delay_secs, "Enter display delay");
+                       if (top->delay_secs < 1)
+                               top->delay_secs = 1;
                        break;
                case 'e':
-                       prompt_integer(&top.print_entries, "Enter display entries (lines)");
-                       if (top.print_entries == 0) {
-                               sig_winch_handler(SIGWINCH);
-                               signal(SIGWINCH, sig_winch_handler);
+                       prompt_integer(&top->print_entries, "Enter display entries (lines)");
+                       if (top->print_entries == 0) {
+                               struct sigaction act = {
+                                       .sa_sigaction = perf_top__sig_winch,
+                                       .sa_flags     = SA_SIGINFO,
+                               };
+                               perf_top__sig_winch(SIGWINCH, NULL, top);
+                               sigaction(SIGWINCH, &act, NULL);
                        } else
                                signal(SIGWINCH, SIG_DFL);
                        break;
                case 'E':
-                       if (top.evlist->nr_entries > 1) {
+                       if (top->evlist->nr_entries > 1) {
                                /* Select 0 as the default event: */
                                int counter = 0;
 
                                fprintf(stderr, "\nAvailable events:");
 
-                               list_for_each_entry(top.sym_evsel, &top.evlist->entries, node)
-                                       fprintf(stderr, "\n\t%d %s", top.sym_evsel->idx, event_name(top.sym_evsel));
+                               list_for_each_entry(top->sym_evsel, &top->evlist->entries, node)
+                                       fprintf(stderr, "\n\t%d %s", top->sym_evsel->idx, event_name(top->sym_evsel));
 
                                prompt_integer(&counter, "Enter details event counter");
 
-                               if (counter >= top.evlist->nr_entries) {
-                                       top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node);
-                                       fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top.sym_evsel));
+                               if (counter >= top->evlist->nr_entries) {
+                                       top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node);
+                                       fprintf(stderr, "Sorry, no such event, using %s.\n", event_name(top->sym_evsel));
                                        sleep(1);
                                        break;
                                }
-                               list_for_each_entry(top.sym_evsel, &top.evlist->entries, node)
-                                       if (top.sym_evsel->idx == counter)
+                               list_for_each_entry(top->sym_evsel, &top->evlist->entries, node)
+                                       if (top->sym_evsel->idx == counter)
                                                break;
                        } else
-                               top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node);
+                               top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node);
                        break;
                case 'f':
-                       prompt_integer(&top.count_filter, "Enter display event count filter");
+                       prompt_integer(&top->count_filter, "Enter display event count filter");
                        break;
                case 'F':
-                       prompt_percent(&sym_pcnt_filter, "Enter details display event filter (percent)");
+                       prompt_percent(&top->sym_pcnt_filter,
+                                      "Enter details display event filter (percent)");
                        break;
                case 'K':
-                       top.hide_kernel_symbols = !top.hide_kernel_symbols;
+                       top->hide_kernel_symbols = !top->hide_kernel_symbols;
                        break;
                case 'q':
                case 'Q':
                        printf("exiting.\n");
-                       if (dump_symtab)
-                               perf_session__fprintf_dsos(top.session, stderr);
+                       if (top->dump_symtab)
+                               perf_session__fprintf_dsos(top->session, stderr);
                        exit(0);
                case 's':
-                       prompt_symbol(&top.sym_filter_entry, "Enter details symbol");
+                       perf_top__prompt_symbol(top, "Enter details symbol");
                        break;
                case 'S':
-                       if (!top.sym_filter_entry)
+                       if (!top->sym_filter_entry)
                                break;
                        else {
-                               struct hist_entry *syme = top.sym_filter_entry;
+                               struct hist_entry *syme = top->sym_filter_entry;
 
-                               top.sym_filter_entry = NULL;
+                               top->sym_filter_entry = NULL;
                                __zero_source_counters(syme);
                        }
                        break;
                case 'U':
-                       top.hide_user_symbols = !top.hide_user_symbols;
+                       top->hide_user_symbols = !top->hide_user_symbols;
                        break;
                case 'z':
-                       top.zero = !top.zero;
+                       top->zero = !top->zero;
                        break;
                default:
                        break;
@@ -563,28 +533,30 @@ static void perf_top__sort_new_samples(void *arg)
        hists__collapse_resort_threaded(&t->sym_evsel->hists);
        hists__output_resort_threaded(&t->sym_evsel->hists);
        hists__decay_entries_threaded(&t->sym_evsel->hists,
-                                     top.hide_user_symbols,
-                                     top.hide_kernel_symbols);
+                                     t->hide_user_symbols,
+                                     t->hide_kernel_symbols);
 }
 
-static void *display_thread_tui(void *arg __used)
+static void *display_thread_tui(void *arg)
 {
+       struct perf_top *top = arg;
        const char *help = "For a higher level overview, try: perf top --sort comm,dso";
 
-       perf_top__sort_new_samples(&top);
-       perf_evlist__tui_browse_hists(top.evlist, help,
+       perf_top__sort_new_samples(top);
+       perf_evlist__tui_browse_hists(top->evlist, help,
                                      perf_top__sort_new_samples,
-                                     &top, top.delay_secs);
+                                     top, top->delay_secs);
 
        exit_browser(0);
        exit(0);
        return NULL;
 }
 
-static void *display_thread(void *arg __used)
+static void *display_thread(void *arg)
 {
        struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
        struct termios tc, save;
+       struct perf_top *top = arg;
        int delay_msecs, c;
 
        tcgetattr(0, &save);
@@ -595,13 +567,13 @@ static void *display_thread(void *arg __used)
 
        pthread__unblock_sigwinch();
 repeat:
-       delay_msecs = top.delay_secs * 1000;
+       delay_msecs = top->delay_secs * 1000;
        tcsetattr(0, TCSANOW, &tc);
        /* trash return*/
        getc(stdin);
 
        while (1) {
-               print_sym_table();
+               perf_top__print_sym_table(top);
                /*
                 * Either timeout expired or we got an EINTR due to SIGWINCH,
                 * refresh screen in both cases.
@@ -621,7 +593,7 @@ process_hotkey:
        c = getc(stdin);
        tcsetattr(0, TCSAFLUSH, &save);
 
-       handle_keypress(c);
+       perf_top__handle_keypress(top, c);
        goto repeat;
 
        return NULL;
@@ -673,47 +645,17 @@ static int symbol_filter(struct map *map __used, struct symbol *sym)
        return 0;
 }
 
-static void perf_event__process_sample(const union perf_event *event,
+static void perf_event__process_sample(struct perf_tool *tool,
+                                      const union perf_event *event,
                                       struct perf_evsel *evsel,
                                       struct perf_sample *sample,
-                                      struct perf_session *session)
+                                      struct machine *machine)
 {
+       struct perf_top *top = container_of(tool, struct perf_top, tool);
        struct symbol *parent = NULL;
        u64 ip = event->ip.ip;
        struct addr_location al;
-       struct machine *machine;
        int err;
-       u8 origin = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
-
-       ++top.samples;
-
-       switch (origin) {
-       case PERF_RECORD_MISC_USER:
-               ++top.us_samples;
-               if (top.hide_user_symbols)
-                       return;
-               machine = perf_session__find_host_machine(session);
-               break;
-       case PERF_RECORD_MISC_KERNEL:
-               ++top.kernel_samples;
-               if (top.hide_kernel_symbols)
-                       return;
-               machine = perf_session__find_host_machine(session);
-               break;
-       case PERF_RECORD_MISC_GUEST_KERNEL:
-               ++top.guest_kernel_samples;
-               machine = perf_session__find_machine(session, event->ip.pid);
-               break;
-       case PERF_RECORD_MISC_GUEST_USER:
-               ++top.guest_us_samples;
-               /*
-                * TODO: we don't process guest user from host side
-                * except simple counting.
-                */
-               return;
-       default:
-               return;
-       }
 
        if (!machine && perf_guest) {
                pr_err("Can't find guest [%d]'s kernel information\n",
@@ -722,14 +664,14 @@ static void perf_event__process_sample(const union perf_event *event,
        }
 
        if (event->header.misc & PERF_RECORD_MISC_EXACT_IP)
-               top.exact_samples++;
+               top->exact_samples++;
 
-       if (perf_event__preprocess_sample(event, session, &al, sample,
+       if (perf_event__preprocess_sample(event, machine, &al, sample,
                                          symbol_filter) < 0 ||
            al.filtered)
                return;
 
-       if (!kptr_restrict_warned &&
+       if (!top->kptr_restrict_warned &&
            symbol_conf.kptr_restrict &&
            al.cpumode == PERF_RECORD_MISC_KERNEL) {
                ui__warning(
@@ -740,7 +682,7 @@ static void perf_event__process_sample(const union perf_event *event,
                          " modules" : "");
                if (use_browser <= 0)