Commit 4a98be82 authored by Thomas Gleixner's avatar Thomas Gleixner

Merge tag 'perf-core-for-mingo-5.1-20190311' of...

Merge tag 'perf-core-for-mingo-5.1-20190311' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent

Pull perf/core improvements and fixes from Arnaldo:

kernel:

  Stephane Eranian :

  - Restore mmap record type correctly when handling PERF_RECORD_MMAP2
    events, as the same template is used for all the threads interested
    in mmap events, some may want just PERF_RECORD_MMAP, while some
    may want the extra info in MMAP2 records.

perf probe:

  Adrian Hunter:

  - Fix getting the kernel map, because since changes related to x86 PTI
    entry trampolines handling, there are more than one kernel map.

perf script:

  Andi Kleen:

  - Support insn output for normal samples, i.e.:

    perf script -F ip,sym,insn --xed

    Will fetch the sample IP from the thread address space and feed it
    to Intel's XED disassembler, producing lines such as:

      ffffffffa4068804 native_write_msr            wrmsr
      ffffffffa415b95e __hrtimer_next_event_base   movq  0x18(%rax), %rdx

    That match 'perf annotate's output.

  - Make the --cpu filter apply to  PERF_RECORD_COMM/FORK/... events, in
    addition to PERF_RECORD_SAMPLE.

perf report:

  - Add a new --samples option to save a small random number of samples
    per hist entry, using a reservoir technique to select a representative
    number of samples.

    Then allow browsing the samples using 'perf script' as part of the hist
    entry context menu. This automatically adds the right filters, so only
    the thread or CPU of the sample is displayed. Then we use less' search
    functionality to directly jump to the time stamp of the selected sample.

    It uses different menus for assembler and source display.  Assembler
    needs xed installed and source needs debuginfo.

  - Fix the UI browser scripts pop up menu when there are many scripts
    available.

perf report:

  Andi Kleen:

  - Add 'time' sort option. E.g.:

    % perf report --sort time,overhead,symbol --time-quantum 1ms --stdio
    ...
         0.67%  277061.87300  [.] _dl_start
         0.50%  277061.87300  [.] f1
         0.50%  277061.87300  [.] f2
         0.33%  277061.87300  [.] main
         0.29%  277061.87300  [.] _dl_lookup_symbol_x
         0.29%  277061.87300  [.] dl_main
         0.29%  277061.87300  [.] do_lookup_x
         0.17%  277061.87300  [.] _dl_debug_initialize
         0.17%  277061.87300  [.] _dl_init_paths
         0.08%  277061.87300  [.] check_match
         0.04%  277061.87300  [.] _dl_count_modids
         1.33%  277061.87400  [.] f1
         1.33%  277061.87400  [.] f2
         1.33%  277061.87400  [.] main
         1.17%  277061.87500  [.] main
         1.08%  277061.87500  [.] f1
         1.08%  277061.87500  [.] f2
         1.00%  277061.87600  [.] main
         0.83%  277061.87600  [.] f1
         0.83%  277061.87600  [.] f2
         1.00%  277061.87700  [.] main

tools headers:

  Arnaldo Carvalho de Melo:

  - Update x86's syscall_64.tbl, no change in tools/perf behaviour.

  -  Sync copies asm-generic/unistd.h and linux/in with the kernel sources.

perf data:

  Jiri Olsa:

  - Prep work to support having perf.data stored as a directory, with one
    file per CPU, that ultimately will allow having one ring buffer reading
    thread per CPU.

Vendor events:

  Martin Liška:

  - perf PMU events for AMD Family 17h.

perf script python:

  Tony Jones:

  - Add python3 support for the remaining Intel PT related scripts, with
    these we should have a clean build of perf with python3 while still
    supporting the build with python2.

libbpf:

  Arnaldo Carvalho de Melo:

  - Fix the build on uCLibc, adding the missing stdarg.h since we use
    va_list in one typedef.
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parents c634dc6b dfcbc2f2
...@@ -7189,6 +7189,7 @@ static void perf_event_mmap_output(struct perf_event *event, ...@@ -7189,6 +7189,7 @@ static void perf_event_mmap_output(struct perf_event *event,
struct perf_output_handle handle; struct perf_output_handle handle;
struct perf_sample_data sample; struct perf_sample_data sample;
int size = mmap_event->event_id.header.size; int size = mmap_event->event_id.header.size;
u32 type = mmap_event->event_id.header.type;
int ret; int ret;
if (!perf_event_mmap_match(event, data)) if (!perf_event_mmap_match(event, data))
...@@ -7232,6 +7233,7 @@ static void perf_event_mmap_output(struct perf_event *event, ...@@ -7232,6 +7233,7 @@ static void perf_event_mmap_output(struct perf_event *event,
perf_output_end(&handle); perf_output_end(&handle);
out: out:
mmap_event->event_id.header.size = size; mmap_event->event_id.header.size = size;
mmap_event->event_id.header.type = type;
} }
static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
......
...@@ -17,5 +17,7 @@ ...@@ -17,5 +17,7 @@
#define __ARCH_WANT_RENAMEAT #define __ARCH_WANT_RENAMEAT
#define __ARCH_WANT_NEW_STAT #define __ARCH_WANT_NEW_STAT
#define __ARCH_WANT_SET_GET_RLIMIT
#define __ARCH_WANT_TIME32_SYSCALLS
#include <asm-generic/unistd.h> #include <asm-generic/unistd.h>
This diff is collapsed.
...@@ -292,10 +292,11 @@ struct sockaddr_in { ...@@ -292,10 +292,11 @@ struct sockaddr_in {
#define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000) #define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000)
/* Defines for Multicast INADDR */ /* Defines for Multicast INADDR */
#define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */ #define INADDR_UNSPEC_GROUP 0xe0000000U /* 224.0.0.0 */
#define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */ #define INADDR_ALLHOSTS_GROUP 0xe0000001U /* 224.0.0.1 */
#define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */ #define INADDR_ALLRTRS_GROUP 0xe0000002U /* 224.0.0.2 */
#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */ #define INADDR_ALLSNOOPERS_GROUP 0xe000006aU /* 224.0.0.106 */
#define INADDR_MAX_LOCAL_GROUP 0xe00000ffU /* 224.0.0.255 */
#endif #endif
/* <asm/byteorder.h> contains the htonl type stuff.. */ /* <asm/byteorder.h> contains the htonl type stuff.. */
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#ifndef __LIBBPF_LIBBPF_H #ifndef __LIBBPF_LIBBPF_H
#define __LIBBPF_LIBBPF_H #define __LIBBPF_LIBBPF_H
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
......
...@@ -584,6 +584,20 @@ llvm.*:: ...@@ -584,6 +584,20 @@ llvm.*::
llvm.opts:: llvm.opts::
Options passed to llc. Options passed to llc.
samples.*::
samples.context::
Define how many ns worth of time to show
around samples in perf report sample context browser.
scripts.*::
Any option defines a script that is added to the scripts menu
in the interactive perf browser and whose output is displayed.
The name of the option is the name, the value is a script command line.
The script gets the same options passed as a full perf script,
in particular -i perfdata file, --cpu, --tid
SEE ALSO SEE ALSO
-------- --------
linkperf:perf[1] linkperf:perf[1]
...@@ -105,6 +105,8 @@ OPTIONS ...@@ -105,6 +105,8 @@ OPTIONS
guest machine guest machine
- sample: Number of sample - sample: Number of sample
- period: Raw number of event count of sample - period: Raw number of event count of sample
- time: Separate the samples by time stamp with the resolution specified by
--time-quantum (default 100ms). Specify with overhead and before it.
By default, comm, dso and symbol keys are used. By default, comm, dso and symbol keys are used.
(i.e. --sort comm,dso,symbol) (i.e. --sort comm,dso,symbol)
...@@ -459,6 +461,10 @@ include::itrace.txt[] ...@@ -459,6 +461,10 @@ include::itrace.txt[]
--socket-filter:: --socket-filter::
Only report the samples on the processor socket that match with this filter Only report the samples on the processor socket that match with this filter
--samples=N::
Save N individual samples for each histogram entry to show context in perf
report tui browser.
--raw-trace:: --raw-trace::
When displaying traceevent output, do not use print fmt or plugins. When displaying traceevent output, do not use print fmt or plugins.
...@@ -477,6 +483,9 @@ include::itrace.txt[] ...@@ -477,6 +483,9 @@ include::itrace.txt[]
Please note that not all mmaps are stored, options affecting which ones Please note that not all mmaps are stored, options affecting which ones
are include 'perf record --data', for instance. are include 'perf record --data', for instance.
--ns::
Show time stamps in nanoseconds.
--stats:: --stats::
Display overall events statistics without any further processing. Display overall events statistics without any further processing.
(like the one at the end of the perf report -D command) (like the one at the end of the perf report -D command)
...@@ -494,6 +503,10 @@ include::itrace.txt[] ...@@ -494,6 +503,10 @@ include::itrace.txt[]
The period/hits keywords set the base the percentage is computed The period/hits keywords set the base the percentage is computed
on - the samples period or the number of samples (hits). on - the samples period or the number of samples (hits).
--time-quantum::
Configure time quantum for time sort key. Default 100ms.
Accepts s, us, ms, ns units.
include::callchain-overhead-calculation.txt[] include::callchain-overhead-calculation.txt[]
SEE ALSO SEE ALSO
......
...@@ -15,6 +15,7 @@ To see callchains in a more compact form: perf report -g folded ...@@ -15,6 +15,7 @@ To see callchains in a more compact form: perf report -g folded
Show individual samples with: perf script Show individual samples with: perf script
Limit to show entries above 5% only: perf report --percent-limit 5 Limit to show entries above 5% only: perf report --percent-limit 5
Profiling branch (mis)predictions with: perf record -b / perf report Profiling branch (mis)predictions with: perf record -b / perf report
To show assembler sample contexts use perf record -b / perf script -F +brstackinsn --xed
Treat branches as callchains: perf report --branch-history Treat branches as callchains: perf report --branch-history
To count events in every 1000 msec: perf stat -I 1000 To count events in every 1000 msec: perf stat -I 1000
Print event counts in CSV format with: perf stat -x, Print event counts in CSV format with: perf stat -x,
...@@ -34,3 +35,9 @@ Show current config key-value pairs: perf config --list ...@@ -34,3 +35,9 @@ Show current config key-value pairs: perf config --list
Show user configuration overrides: perf config --user --list Show user configuration overrides: perf config --user --list
To add Node.js USDT(User-Level Statically Defined Tracing): perf buildid-cache --add `which node` To add Node.js USDT(User-Level Statically Defined Tracing): perf buildid-cache --add `which node`
To report cacheline events from previous recording: perf c2c report To report cacheline events from previous recording: perf c2c report
To browse sample contexts use perf report --sample 10 and select in context menu
To separate samples by time use perf report --sort time,overhead,sym
To set sample time separation other than 100ms with --sort time use --time-quantum
Add -I to perf report to sample register values visible in perf report context.
To show IPC for sampling periods use perf record -e '{cycles,instructions}:S' and then browse context
To show context switches in perf report sample context add --switch-events to perf record.
...@@ -343,6 +343,8 @@ ...@@ -343,6 +343,8 @@
332 common statx __x64_sys_statx 332 common statx __x64_sys_statx
333 common io_pgetevents __x64_sys_io_pgetevents 333 common io_pgetevents __x64_sys_io_pgetevents
334 common rseq __x64_sys_rseq 334 common rseq __x64_sys_rseq
# don't use numbers 387 through 423, add new calls after the last
# 'common' entry
# #
# x32-specific system call numbers start at 512 to avoid cache impact # x32-specific system call numbers start at 512 to avoid cache impact
...@@ -361,7 +363,7 @@ ...@@ -361,7 +363,7 @@
520 x32 execve __x32_compat_sys_execve/ptregs 520 x32 execve __x32_compat_sys_execve/ptregs
521 x32 ptrace __x32_compat_sys_ptrace 521 x32 ptrace __x32_compat_sys_ptrace
522 x32 rt_sigpending __x32_compat_sys_rt_sigpending 522 x32 rt_sigpending __x32_compat_sys_rt_sigpending
523 x32 rt_sigtimedwait __x32_compat_sys_rt_sigtimedwait 523 x32 rt_sigtimedwait __x32_compat_sys_rt_sigtimedwait_time64
524 x32 rt_sigqueueinfo __x32_compat_sys_rt_sigqueueinfo 524 x32 rt_sigqueueinfo __x32_compat_sys_rt_sigqueueinfo
525 x32 sigaltstack __x32_compat_sys_sigaltstack 525 x32 sigaltstack __x32_compat_sys_sigaltstack
526 x32 timer_create __x32_compat_sys_timer_create 526 x32 timer_create __x32_compat_sys_timer_create
...@@ -375,7 +377,7 @@ ...@@ -375,7 +377,7 @@
534 x32 preadv __x32_compat_sys_preadv64 534 x32 preadv __x32_compat_sys_preadv64
535 x32 pwritev __x32_compat_sys_pwritev64 535 x32 pwritev __x32_compat_sys_pwritev64
536 x32 rt_tgsigqueueinfo __x32_compat_sys_rt_tgsigqueueinfo 536 x32 rt_tgsigqueueinfo __x32_compat_sys_rt_tgsigqueueinfo
537 x32 recvmmsg __x32_compat_sys_recvmmsg 537 x32 recvmmsg __x32_compat_sys_recvmmsg_time64
538 x32 sendmmsg __x32_compat_sys_sendmmsg 538 x32 sendmmsg __x32_compat_sys_sendmmsg
539 x32 process_vm_readv __x32_compat_sys_process_vm_readv 539 x32 process_vm_readv __x32_compat_sys_process_vm_readv
540 x32 process_vm_writev __x32_compat_sys_process_vm_writev 540 x32 process_vm_writev __x32_compat_sys_process_vm_writev
......
...@@ -14,5 +14,6 @@ perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o ...@@ -14,5 +14,6 @@ perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
perf-$(CONFIG_AUXTRACE) += auxtrace.o perf-$(CONFIG_AUXTRACE) += auxtrace.o
perf-$(CONFIG_AUXTRACE) += archinsn.o
perf-$(CONFIG_AUXTRACE) += intel-pt.o perf-$(CONFIG_AUXTRACE) += intel-pt.o
perf-$(CONFIG_AUXTRACE) += intel-bts.o perf-$(CONFIG_AUXTRACE) += intel-bts.o
// SPDX-License-Identifier: GPL-2.0
#include "perf.h"
#include "archinsn.h"
#include "util/intel-pt-decoder/insn.h"
#include "machine.h"
#include "thread.h"
#include "symbol.h"
void arch_fetch_insn(struct perf_sample *sample,
struct thread *thread,
struct machine *machine)
{
struct insn insn;
int len;
bool is64bit = false;
if (!sample->ip)
return;
len = thread__memcpy(thread, machine, sample->insn, sample->ip, sizeof(sample->insn), &is64bit);
if (len <= 0)
return;
insn_init(&insn, sample->insn, len, is64bit);
insn_get_length(&insn);
if (insn_complete(&insn) && insn.length <= len)
sample->insn_len = insn.length;
}
...@@ -392,7 +392,7 @@ static int record__process_auxtrace(struct perf_tool *tool, ...@@ -392,7 +392,7 @@ static int record__process_auxtrace(struct perf_tool *tool,
size_t padding; size_t padding;
u8 pad[8] = {0}; u8 pad[8] = {0};
if (!perf_data__is_pipe(data)) { if (!perf_data__is_pipe(data) && !perf_data__is_dir(data)) {
off_t file_offset; off_t file_offset;
int fd = perf_data__fd(data); int fd = perf_data__fd(data);
int err; int err;
...@@ -837,6 +837,8 @@ static void record__init_features(struct record *rec) ...@@ -837,6 +837,8 @@ static void record__init_features(struct record *rec)
if (!(rec->opts.use_clockid && rec->opts.clockid_res_ns)) if (!(rec->opts.use_clockid && rec->opts.clockid_res_ns))
perf_header__clear_feat(&session->header, HEADER_CLOCKID); perf_header__clear_feat(&session->header, HEADER_CLOCKID);
perf_header__clear_feat(&session->header, HEADER_DIR_FORMAT);
perf_header__clear_feat(&session->header, HEADER_STAT); perf_header__clear_feat(&session->header, HEADER_STAT);
} }
......
...@@ -47,9 +47,11 @@ ...@@ -47,9 +47,11 @@
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <regex.h> #include <regex.h>
#include "sane_ctype.h"
#include <signal.h> #include <signal.h>
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/stringify.h> #include <linux/stringify.h>
#include <linux/time64.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
...@@ -926,6 +928,43 @@ report_parse_callchain_opt(const struct option *opt, const char *arg, int unset) ...@@ -926,6 +928,43 @@ report_parse_callchain_opt(const struct option *opt, const char *arg, int unset)
return parse_callchain_report_opt(arg); return parse_callchain_report_opt(arg);
} }
static int
parse_time_quantum(const struct option *opt, const char *arg,
int unset __maybe_unused)
{
unsigned long *time_q = opt->value;
char *end;
*time_q = strtoul(arg, &end, 0);
if (end == arg)
goto parse_err;
if (*time_q == 0) {
pr_err("time quantum cannot be 0");
return -1;
}
while (isspace(*end))
end++;
if (*end == 0)
return 0;
if (!strcmp(end, "s")) {
*time_q *= NSEC_PER_SEC;
return 0;
}
if (!strcmp(end, "ms")) {
*time_q *= NSEC_PER_MSEC;
return 0;
}
if (!strcmp(end, "us")) {
*time_q *= NSEC_PER_USEC;
return 0;
}
if (!strcmp(end, "ns"))
return 0;
parse_err:
pr_err("Cannot parse time quantum `%s'\n", arg);
return -1;
}
int int
report_parse_ignore_callees_opt(const struct option *opt __maybe_unused, report_parse_ignore_callees_opt(const struct option *opt __maybe_unused,
const char *arg, int unset __maybe_unused) const char *arg, int unset __maybe_unused)
...@@ -1120,6 +1159,8 @@ int cmd_report(int argc, const char **argv) ...@@ -1120,6 +1159,8 @@ int cmd_report(int argc, const char **argv)
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel, OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
"Enable kernel symbol demangling"), "Enable kernel symbol demangling"),
OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"), OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
OPT_INTEGER(0, "samples", &symbol_conf.res_sample,
"Number of samples to save per histogram entry for individual browsing"),
OPT_CALLBACK(0, "percent-limit", &report, "percent", OPT_CALLBACK(0, "percent-limit", &report, "percent",
"Don't show entries under that percent", parse_percent_limit), "Don't show entries under that percent", parse_percent_limit),
OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
...@@ -1147,6 +1188,10 @@ int cmd_report(int argc, const char **argv) ...@@ -1147,6 +1188,10 @@ int cmd_report(int argc, const char **argv)
OPT_CALLBACK(0, "percent-type", &report.annotation_opts, "local-period", OPT_CALLBACK(0, "percent-type", &report.annotation_opts, "local-period",
"Set percent type local/global-period/hits", "Set percent type local/global-period/hits",
annotate_parse_percent_type), annotate_parse_percent_type),
OPT_BOOLEAN(0, "ns", &symbol_conf.nanosecs, "Show times in nanosecs"),
OPT_CALLBACK(0, "time-quantum", &symbol_conf.time_quantum, "time (ms|us|ns|s)",
"Set time quantum for time sort key (default 100ms)",
parse_time_quantum),
OPT_END() OPT_END()
}; };
struct perf_data data = { struct perf_data data = {
......
...@@ -29,10 +29,12 @@ ...@@ -29,10 +29,12 @@
#include "util/time-utils.h" #include "util/time-utils.h"
#include "util/path.h" #include "util/path.h"
#include "print_binary.h" #include "print_binary.h"
#include "archinsn.h"
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/stringify.h> #include <linux/stringify.h>
#include <linux/time64.h> #include <linux/time64.h>
#include <sys/utsname.h>
#include "asm/bug.h" #include "asm/bug.h"
#include "util/mem-events.h" #include "util/mem-events.h"
#include "util/dump-insn.h" #include "util/dump-insn.h"
...@@ -58,11 +60,11 @@ static bool no_callchain; ...@@ -58,11 +60,11 @@ static bool no_callchain;
static bool latency_format; static bool latency_format;
static bool system_wide; static bool system_wide;
static bool print_flags; static bool print_flags;
static bool nanosecs;
static const char *cpu_list; static const char *cpu_list;
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
static struct perf_stat_config stat_config; static struct perf_stat_config stat_config;
static int max_blocks; static int max_blocks;
static bool native_arch;
unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH; unsigned int scripting_max_stack = PERF_MAX_STACK_DEPTH;
...@@ -688,7 +690,7 @@ static int perf_sample__fprintf_start(struct perf_sample *sample, ...@@ -688,7 +690,7 @@ static int perf_sample__fprintf_start(struct perf_sample *sample,
secs = nsecs / NSEC_PER_SEC; secs = nsecs / NSEC_PER_SEC;
nsecs -= secs * NSEC_PER_SEC; nsecs -= secs * NSEC_PER_SEC;
if (nanosecs) if (symbol_conf.nanosecs)
printed += fprintf(fp, "%5lu.%09llu: ", secs, nsecs); printed += fprintf(fp, "%5lu.%09llu: ", secs, nsecs);
else { else {
char sample_time[32]; char sample_time[32];
...@@ -1227,6 +1229,12 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample, ...@@ -1227,6 +1229,12 @@ static int perf_sample__fprintf_callindent(struct perf_sample *sample,
return len + dlen; return len + dlen;
} }
__weak void arch_fetch_insn(struct perf_sample *sample __maybe_unused,
struct thread *thread __maybe_unused,
struct machine *machine __maybe_unused)
{
}
static int perf_sample__fprintf_insn(struct perf_sample *sample, static int perf_sample__fprintf_insn(struct perf_sample *sample,
struct perf_event_attr *attr, struct perf_event_attr *attr,
struct thread *thread, struct thread *thread,
...@@ -1234,9 +1242,12 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample, ...@@ -1234,9 +1242,12 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample,
{ {
int printed = 0; int printed = 0;
if (sample->insn_len == 0 && native_arch)
arch_fetch_insn(sample, thread, machine);
if (PRINT_FIELD(INSNLEN)) if (PRINT_FIELD(INSNLEN))
printed += fprintf(fp, " ilen: %d", sample->insn_len); printed += fprintf(fp, " ilen: %d", sample->insn_len);
if (PRINT_FIELD(INSN)) { if (PRINT_FIELD(INSN) && sample->insn_len) {
int i; int i;
printed += fprintf(fp, " insn:"); printed += fprintf(fp, " insn:");
...@@ -1922,6 +1933,13 @@ static int cleanup_scripting(void) ...@@ -1922,6 +1933,13 @@ static int cleanup_scripting(void)
return scripting_ops ? scripting_ops->stop_script() : 0; return scripting_ops ? scripting_ops->stop_script() : 0;
} }
static bool filter_cpu(struct perf_sample *sample)
{
if (cpu_list)
return !test_bit(sample->cpu, cpu_bitmap);
return false;
}
static int process_sample_event(struct perf_tool *tool, static int process_sample_event(struct perf_tool *tool,
union perf_event *event, union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
...@@ -1956,7 +1974,7 @@ static int process_sample_event(struct perf_tool *tool, ...@@ -1956,7 +1974,7 @@ static int process_sample_event(struct perf_tool *tool,
if (al.filtered) if (al.filtered)
goto out_put; goto out_put;
if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) if (filter_cpu(sample))
goto out_put; goto out_put;
if (scripting_ops) if (scripting_ops)
...@@ -2041,9 +2059,11 @@ static int process_comm_event(struct perf_tool *tool, ...@@ -2041,9 +2059,11 @@ static int process_comm_event(struct perf_tool *tool,
sample->tid = event->comm.tid; sample->tid = event->comm.tid;
sample->pid = event->comm.pid; sample->pid = event->comm.pid;
} }
perf_sample__fprintf_start(sample, thread, evsel, if (!filter_cpu(sample)) {
perf_sample__fprintf_start(sample, thread, evsel,
PERF_RECORD_COMM, stdout); PERF_RECORD_COMM, stdout);
perf_event__fprintf(event, stdout); perf_event__fprintf(event, stdout);
}
ret = 0; ret = 0;
out: out:
thread__put(thread); thread__put(thread);
...@@ -2077,9 +2097,11 @@ static int process_namespaces_event(struct perf_tool *tool, ...@@ -2077,9 +2097,11 @@ static int process_namespaces_event(struct perf_tool *tool,
sample->tid = event->namespaces.tid; sample->tid = event->namespaces.tid;
sample->pid = event->namespaces.pid; sample->pid = event->namespaces.pid;
} }
perf_sample__fprintf_start(sample, thread, evsel, if (!filter_cpu(sample)) {
PERF_RECORD_NAMESPACES, stdout); perf_sample__fprintf_start(sample, thread, evsel,
perf_event__fprintf(event, stdout); PERF_RECORD_NAMESPACES, stdout);
perf_event__fprintf(event, stdout);
}
ret = 0; ret = 0;
out: out:
thread__put(thread); thread__put(thread);
...@@ -2111,9 +2133,11 @@ static int process_fork_event(struct perf_tool *tool, ...@@ -2111,9 +2133,11 @@ static int process_fork_event(struct perf_tool *tool,
sample->tid = event->fork.tid; sample->tid = event->fork.tid;
sample->pid = event->fork.pid; sample->pid = event->fork.pid;
} }
perf_sample__fprintf_start(sample, thread, evsel, if (!filter_cpu(sample)) {
PERF_RECORD_FORK, stdout); perf_sample__fprintf_start(sample, thread, evsel,
perf_event__fprintf(event, stdout); PERF_RECORD_FORK, stdout);
perf_event__fprintf(event, stdout);
}
thread__put(thread); thread__put(thread);
return 0; return 0;
...@@ -2141,9 +2165,11 @@ static int process_exit_event(struct perf_tool *tool, ...@@ -2141,9 +2165,11 @@ static int process_exit_event(struct perf_tool *tool,
sample->tid = event->fork.tid; sample->tid = event->fork.tid;
sample->pid = event->fork.pid; sample->pid = event->fork.pid;
} }
perf_sample__fprintf_start(sample, thread, evsel, if (!filter_cpu(sample)) {
PERF_RECORD_EXIT, stdout); perf_sample__fprintf_start(sample, thread, evsel,
perf_event__fprintf(event, stdout); PERF_RECORD_EXIT, stdout);
perf_event__fprintf(event, stdout);
}
if (perf_event__process_exit(tool, event, sample, machine) < 0) if (perf_event__process_exit(tool, event, sample, machine) < 0)
err = -1; err = -1;
...@@ -2177,9 +2203,11 @@ static int process_mmap_event(struct perf_tool *tool, ...@@ -2177,9 +2203,11 @@ static int process_mmap_event(struct perf_tool *tool,
sample->tid = event->mmap.tid; sample->tid = event->mmap.tid;
sample->pid = event->mmap.pid; sample->pid = event->mmap.pid;
} }
perf_sample__fprintf_start(sample, thread, evsel, if (!filter_cpu(sample)) {
PERF_RECORD_MMAP, stdout); perf_sample__fprintf_start(sample, thread, evsel,
perf_event__fprintf(event, stdout); PERF_RECORD_MMAP, stdout);
perf_event__fprintf(event, stdout);
}
thread__put(thread); thread__put(thread);
return 0; return 0;
} }
...@@ -2209,9 +2237,11 @@ static int process_mmap2_event(struct perf_tool *tool, ...@@ -2209,9 +2237,11 @@ static int process_mmap2_event(struct perf_tool *tool,
sample->tid = event->mmap2.tid; sample->tid = event->mmap2.tid;
sample->pid = event->mmap2.pid; sample->pid = event->mmap2.pid;
} }
perf_sample__fprintf_start(sample, thread, evsel, if (!filter_cpu(sample)) {
PERF_RECORD_MMAP2, stdout); perf_sample__fprintf_start(sample, thread, evsel,
perf_event__fprintf(event, stdout); PERF_RECORD_MMAP2, stdout);
perf_event__fprintf(event, stdout);
}
thread__put(thread); thread__put(thread);
return 0; return 0;
} }
...@@ -2236,9 +2266,11 @@ static int process_switch_event(struct perf_tool *tool, ...@@ -2236,9 +2266,11 @@ static int process_switch_event(struct perf_tool *tool,
return -1; return -1;
} }
perf_sample__fprintf_start(sample, thread, evsel, if (!filter_cpu(sample)) {
PERF_RECORD_SWITCH, stdout); perf_sample__fprintf_start(sample, thread, evsel,
perf_event__fprintf(event, stdout); PERF_RECORD_SWITCH, stdout);
perf_event__fprintf(event, stdout);
}
thread__put(thread); thread__put(thread);
return 0; return 0;
} }
...@@ -2259,9 +2291,11 @@ process_lost_event(struct perf_tool *tool, ...@@ -2259,9 +2291,11 @@ process_lost_event(struct perf_tool *tool,
if (thread == NULL) if (thread == NULL)
return -1; return -1;
perf_sample__fprintf_start(sample, thread, evsel, if (!filter_cpu(sample)) {
PERF_RECORD_LOST, stdout); perf_sample__fprintf_start(sample, thread, evsel,
perf_event__fprintf(event, stdout); PERF_RECORD_LOST, stdout);
perf_event__fprintf(event, stdout);
}
thread__put(thread); thread__put(thread);
return 0; return 0;
} }
...@@ -2948,7 +2982,8 @@ static int check_ev_match(char *dir_name, char *scriptname, ...@@ -2948,7 +2982,8 @@ static int check_ev_match(char *dir_name, char *scriptname,
* will list all statically runnable scripts, select one, execute it and * will list all statically runnable scripts, select one, execute it and
* show the output in a perf browser. * show the output in a perf browser.
*/ */
int find_scripts(char **scripts_array, char **scripts_path_array) int find_scripts(char **scripts_array, char **scripts_path_array, int num,
int pathlen)
{ {
struct dirent *script_dirent, *lang_dirent; struct dirent *script_dirent, *lang_dirent;
char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN]; char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN];
...@@ -2993,7 +3028,10 @@ int find_scripts(char **scripts_array, char **scripts_path_array) ...@@ -2993,7 +3028,10 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
/* Skip those real time scripts: xxxtop.p[yl] */ /* Skip those real time scripts: xxxtop.p[yl] */
if (strstr(script_dirent->d_name, "top.")) if (strstr(script_dirent->d_name, "top."))
continue; continue;
sprintf(scripts_path_array[i], "%s/%s", lang_path, if (i >= num)
break;
snprintf(scripts_path_array[i], pathlen, "%s/%s",
lang_path,
script_dirent->d_name); script_dirent->d_name);
temp = strchr(script_dirent->d_name, '.'); temp = strchr(script_dirent->d_name, '.');
snprintf(scripts_array[i], snprintf(scripts_array[i],
...@@ -3232,7 +3270,7 @@ static int parse_insn_trace(const struct option *opt __maybe_unused, ...@@ -3232,7 +3270,7 @@ static int parse_insn_trace(const struct option *opt __maybe_unused,
{ {
parse_output_fields(NULL, "+insn,-event,-period", 0); parse_output_fields(NULL, "+insn,-event,-period", 0);
itrace_parse_synth_opts(opt, "i0ns", 0); itrace_parse_synth_opts(opt, "i0ns", 0);
nanosecs = true; symbol_conf.nanosecs = true;
return 0; return 0;
} }
...@@ -3250,7 +3288,7 @@ static int parse_call_trace(const struct option *opt __maybe_unused, ...@@ -3250,7 +3288,7 @@ static int parse_call_trace(const struct option *opt __maybe_unused,
{ {
parse_output_fields(NULL, "-ip,-addr,-event,-period,+callindent", 0); parse_output_fields(NULL, "-ip,-addr,-event,-period,+callindent", 0);
itrace_parse_synth_opts(opt, "cewp", 0); itrace_parse_synth_opts(opt, "cewp", 0);
nanosecs = true; symbol_conf.nanosecs = true;
return 0; return 0;
} }
...@@ -3260,7 +3298,7 @@ static int parse_callret_trace(const struct option *opt __maybe_unused, ...@@ -3260,7 +3298,7 @@ static int parse_callret_trace(const struct option *opt __maybe_unused,
{ {
parse_output_fields(NULL, "-ip,-addr,-event,-period,+callindent,+flags", 0); parse_output_fields(NULL, "-ip,-addr,-event,-period,+callindent,+flags", 0);
itrace_parse_synth_opts(opt, "crewp", 0); itrace_parse_synth_opts(opt, "crewp", 0);
nanosecs = true; symbol_conf.nanosecs = true;
return 0; return 0;
} }
...@@ -3277,6 +3315,7 @@ int cmd_script(int argc, const char **argv) ...@@ -3277,6 +3315,7 @@ int cmd_script(int argc, const char **argv)
.set = false, .set = false,
.default_no_sample = true, .default_no_sample = true,
}; };
struct utsname uts;
char *script_path = NULL; char *script_path = NULL;
const char **__argv; const char **__argv;
int i, j, err = 0; int i, j, err = 0;
...@@ -3395,7 +3434,7 @@ int cmd_script(int argc, const char **argv) ...@@ -3395,7 +3434,7 @@ int cmd_script(int argc, const char **argv)
OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"), OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
OPT_INTEGER(0, "max-blocks", &max_blocks, OPT_INTEGER(0, "max-blocks", &max_blocks,
"Maximum number of code blocks to dump with brstackinsn"), "Maximum number of code blocks to dump with brstackinsn"),
OPT_BOOLEAN(0, "ns", &nanosecs, OPT_BOOLEAN(0, "ns", &symbol_conf.nanosecs,
"Use 9 decimal places when displaying time"), "Use 9 decimal places when displaying time"),
OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts", OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
"Instruction Tracing options\n" ITRACE_HELP, "Instruction Tracing options\n" ITRACE_HELP,
...@@ -3615,6 +3654,12 @@ int cmd_script(int argc, const char **argv) ...@@ -3615,6 +3654,12 @@ int cmd_script(int argc, const char **argv)
if (symbol__init(&session->header.env) < 0) if (symbol__init(&session->header.env) < 0)
goto out_delete; goto out_delete;
uname(&uts);
if (!strcmp(uts.machine, session->header.env.arch) ||
(!strcmp(uts.machine, "x86_64") &&
!strcmp(session->header.env.arch, "i386")))
native_arch = true;
script.session = session; script.session = session;
script__setup_sample_type(&script); script__setup_sample_type(&script);
......
...@@ -40,5 +40,6 @@ int cmd_mem(int argc, const char **argv); ...@@ -40,5 +40,6 @@ int cmd_mem(int argc, const char **argv);
int cmd_data(int argc, const char **argv); int cmd_data(int argc, const char **argv);
int cmd_ftrace(int argc, const char **argv); int cmd_ftrace(int argc, const char **argv);
int find_scripts(char **scripts_array, char **scripts_path_array); int find_scripts(char **scripts_array, char **scripts_path_array, int num,
int pathlen);
#endif #endif
[
{
"EventName": "bp_l1_btb_correct",
"EventCode": "0x8a",
"BriefDescription": "L1 BTB Correction."
},
{
"EventName": "bp_l2_btb_correct",
"EventCode": "0x8b",
"BriefDescription": "L2 BTB Correction."
}
]
This diff is collapsed.
[
{
"EventName": "ex_ret_instr",
"EventCode": "0xc0",
"BriefDescription": "Retired Instructions."
},
{
"EventName": "ex_ret_cops",
"EventCode": "0xc1",
"BriefDescription": "Retired Uops.",
"PublicDescription": "The number of uOps retired. This includes all processor activity (instructions, exceptions, interrupts, microcode assists, etc.). The number of events logged per cycle can vary from 0 to 4."
},
{
"EventName": "ex_ret_brn",
"EventCode": "0xc2",
"BriefDescription": "[Retired Branch Instructions.",
"PublicDescription": "The number of branch instructions retired. This includes all types of architectural control flow changes, including exceptions and interrupts."
},
{
"EventName": "ex_ret_brn_misp",
"EventCode": "0xc3",
"BriefDescription": "Retired Branch Instructions Mispredicted.",
"PublicDescription": "The number of branch instructions retired, of any type, that were not correctly predicted. This includes those for which prediction is not attempted (far control transfers, exceptions and interrupts)."
},
{
"EventName": "ex_ret_brn_tkn",
"EventCode": "0xc4",
"BriefDescription": "Retired Taken Branch Instructions.",
"PublicDescription": "The number of taken branches that were retired. This includes all types of architectural control flow changes, including exceptions and interrupts."
},
{
"EventName": "ex_ret_brn_tkn_misp",
"EventCode": "0xc5",
"BriefDescription": "Retired Taken Branch Instructions Mispredicted.",
"PublicDescription": "The number of retired taken branch instructions that were mispredicted."
},
{
"EventName": "ex_ret_brn_far",
"EventCode": "0xc6",
"BriefDescription": "Retired Far Control Transfers.",
"PublicDescription": "The number of far control transfers retired including far call/jump/return, IRET, SYSCALL and SYSRET, plus exceptions and interrupts. Far control transfers are not subject to branch prediction."
},
{
"EventName": "ex_ret_brn_resync",
"EventCode": "0xc7",
"BriefDescription": "Retired Branch Resyncs.",
"PublicDescription": "The number of resync branches. These reflect pipeline restarts due to certain microcode assists and events such as writes to the active instruction stream, among other things. Each occurrence reflects a restart penalty similar to a branch mispredict. This is relatively rare."
},
{
"EventName": "ex_ret_near_ret",
"EventCode": "0xc8",
"BriefDescription": "Retired Near Returns.",
"PublicDescription": "The number of near return instructions (RET or RET Iw) retired."
},
{
"EventName": "ex_ret_near_ret_mispred",
"EventCode": "0xc9",
"BriefDescription": "Retired Near Returns Mispredicted.",
"PublicDescription": "The number of near returns retired that were not correctly predicted by the return address predictor. Each such mispredict incurs the same penalty as a mispredicted conditional branch instruction."
},
{
"EventName": "ex_ret_brn_ind_misp",
"EventCode": "0xca",
"BriefDescription": "Retired Indirect Branch Instructions Mispredicted.",
"PublicDescription": "Retired Indirect Branch Instructions Mispredicted."
},
{
"EventName": "ex_ret_mmx_fp_instr.sse_instr",
"EventCode": "0xcb",
"BriefDescription": "SSE instructions (SSE, SSE2, SSE3, SSSE3, SSE4A, SSE41, SSE42, AVX).",
"PublicDescription": "The number of MMX, SSE or x87 instructions retired. The UnitMask allows the selection of the individual classes of instructions as given in the table. Each increment represents one complete instruction. Since this event includes non-numeric instructions it is not suitable for measuring MFLOPS. SSE instructions (SSE, SSE2, SSE3, SSSE3, SSE4A, SSE41, SSE42, AVX).",
"UMask": "0x4"
},
{
"EventName": "ex_ret_mmx_fp_instr.mmx_instr",
"EventCode": "0xcb",
"BriefDescription": "MMX instructions.",
"PublicDescription": "The number of MMX, SSE or x87 instructions retired. The UnitMask allows the selection of the individual classes of instructions as given in the table. Each increment represents one complete instruction. Since this event includes non-numeric instructions it is not suitable for measuring MFLOPS. MMX instructions.",
"UMask": "0x2"
},
{
"EventName": "ex_ret_mmx_fp_instr.x87_instr",
"EventCode": "0xcb",
"BriefDescription": "x87 instructions.",
"PublicDescription": "The number of MMX, SSE or x87 instructions retired. The UnitMask allows the selection of the individual classes of instructions as given in the table. Each increment represents one complete instruction. Since this event includes non-numeric instructions it is not suitable for measuring MFLOPS. x87 instructions.",
"UMask": "0x1"
},
{
"EventName": "ex_ret_cond",
"EventCode": "0xd1",
"BriefDescription": "Retired Conditional Branch Instructions."
},
{
"EventName": "ex_ret_cond_misp",
"EventCode": "0xd2",
"BriefDescription": "Retired Conditional Branch Instructions Mispredicted."
},
{
"EventName": "ex_div_busy",
"EventCode": "0xd3",
"BriefDescription": "Div Cycles Busy count."
},
{
"EventName": "ex_div_count",
"EventCode": "0xd4",
"BriefDescription": "Div Op Count."
},
{
"EventName": "ex_tagged_ibs_ops.ibs_count_rollover",
"EventCode": "0x1cf",
"BriefDescription": "Number of times an op could not be tagged by IBS because of a previous tagged op that has not retired.",
"PublicDescription": "Tagged IBS Ops. Number of times an op could not be tagged by IBS because of a previous tagged op that has not retired.",
"UMask": "0x4"
},
{
"EventName": "ex_tagged_ibs_ops.ibs_tagged_ops_ret",
"EventCode": "0x1cf",
"BriefDescription": "Number of Ops tagged by IBS that retired.",
"PublicDescription": "Tagged IBS Ops. Number of Ops tagged by IBS that retired.",
"UMask": "0x2"
},
{
"EventName": "ex_tagged_ibs_ops.ibs_tagged_ops",
"EventCode": "0x1cf",
"BriefDescription": "Number of Ops tagged by IBS.",
"PublicDescription": "Tagged IBS Ops. Number of Ops tagged by IBS.",
"UMask": "0x1"
},
{
"EventName": "ex_ret_fus_brnch_inst",
"EventCode": "0x1d0",
"BriefDescription": "The number of fused retired branch instructions retired per cycle. The number of events logged per cycle can vary from 0 to 3."
}
]
[
{
"EventName": "fpu_pipe_assignment.dual",
"EventCode": "0x00",
"BriefDescription": "Total number multi-pipe uOps.",
"PublicDescription": "The number of operations (uOps) and dual-pipe uOps dispatched to each of the 4 FPU execution pipelines. This event reflects how busy the FPU pipelines are and may be used for workload characterization. This includes all operations performed by x87, MMX, and SSE instructions, including moves. Each increment represents a one- cycle dispatch event. This event is a speculative event. Since this event includes non-numeric operations it is not suitable for measuring MFLOPS. Total number multi-pipe uOps assigned to Pipe 3.",
"UMask": "0xf0"
},
{
"EventName": "fpu_pipe_assignment.total",
"EventCode": "0x00",
"BriefDescription": "Total number uOps.",
"PublicDescription": "The number of operations (uOps) and dual-pipe uOps dispatched to each of the 4 FPU execution pipelines. This event reflects how busy the FPU pipelines are and may be used for workload characterization. This includes all operations performed by x87, MMX, and SSE instructions, including moves. Each increment represents a one- cycle dispatch event. This event is a speculative event. Since this event includes non-numeric operations it is not suitable for measuring MFLOPS. Total number uOps assigned to Pipe 3.",
"UMask": "0xf"
},
{
"EventName": "fp_sched_empty",
"EventCode": "0x01",
"BriefDescription": "This is a speculative event. The number of cycles in which the FPU scheduler is empty. Note that some Ops like FP loads bypass the scheduler."
},
{
"EventName": "fp_retx87_fp_ops.all",
"EventCode": "0x02",
"BriefDescription": "All Ops.",
"PublicDescription": "The number of x87 floating-point Ops that have retired. The number of events logged per cycle can vary from 0 to 8.",
"UMask": "0x7"
},
{
"EventName": "fp_retx87_fp_ops.div_sqr_r_ops",
"EventCode": "0x02",
"BriefDescription": "Divide and square root Ops.",
"PublicDescription": "The number of x87 floating-point Ops that have retired. The number of events logged per cycle can vary from 0 to 8. Divide and square root Ops.",
"UMask": "0x4"
},
{
"EventName": "fp_retx87_fp_ops.mul_ops",
"EventCode": "0x02",
"BriefDescription": "Multiply Ops.",
"PublicDescription": "The number of x87 floating-point Ops that have retired. The number of events logged per cycle can vary from 0 to 8. Multiply Ops.",
"UMask": "0x2"
},
{
"EventName": "fp_retx87_fp_ops.add_sub_ops",
"EventCode": "0x02",
"BriefDescription": "Add/subtract Ops.",
"PublicDescription": "The number of x87 floating-point Ops that have retired. The number of events logged per cycle can vary from 0 to 8. Add/subtract Ops.",
"UMask": "0x1"
},
{
"EventName": "fp_ret_sse_avx_ops.all",
"EventCode": "0x03",
"BriefDescription": "All FLOPS.",
"PublicDescription": "This is a retire-based event. The number of retired SSE/AVX FLOPS. The number of events logged per cycle can vary from 0 to 64. This event can count above 15.",
"UMask": "0xff"
},
{
"EventName": "fp_ret_sse_avx_ops.dp_mult_add_flops",
"EventCode": "0x03",
"BriefDescription": "Double precision multiply-add FLOPS. Multiply-add counts as 2 FLOPS.",
"PublicDescription": "This is a retire-based event. The number of retired SSE/AVX FLOPS. The number of events logged per cycle can vary from 0 to 64. This event can count above 15. Double precision multiply-add FLOPS. Multiply-add counts as 2 FLOPS.",
"UMask": "0x80"
},
{
"EventName": "fp_ret_sse_avx_ops.dp_div_flops",
"EventCode": "0x03",
"BriefDescription": "Double precision divide/square root FLOPS.",
"PublicDescription": "This is a retire-based event. The number of retired SSE/AVX FLOPS. The number of events logged per cycle can vary from 0 to 64. This event can count above 15. Double precision divide/square root FLOPS.",
"UMask": "0x40"
},
{
"EventName": "fp_ret_sse_avx_ops.dp_mult_flops",
"EventCode": "0x03",
"BriefDescription": "Double precision multiply FLOPS.",
"PublicDescription": "This is a retire-based event. The number of retired SSE/AVX FLOPS. The number of events logged per cycle can vary from 0 to 64. This event can count above 15. Double precision multiply FLOPS.",
"UMask": "0x20"
},
{
"EventName": "fp_ret_sse_avx_ops.dp_add_sub_flops",
"EventCode": "0x03",
"BriefDescription": "Double precision add/subtract FLOPS.",
"PublicDescription": "This is a retire-based event. The number of retired SSE/AVX FLOPS. The number of events logged per cycle can vary from 0 to 64. This event can count above 15. Double precision add/subtract FLOPS.",
"UMask": "0x10"
},
{
"EventName": "fp_ret_sse_avx_ops.sp_mult_add_flops",
"EventCode": "0x03",
"BriefDescription": "Single precision multiply-add FLOPS. Multiply-add counts as 2 FLOPS.",
"PublicDescription": "This is a retire-based event. The number of retired SSE/AVX FLOPS. The number of events logged per cycle can vary from 0 to 64. This event can count above 15. Single precision multiply-add FLOPS. Multiply-add counts as 2 FLOPS.",
"UMask": "0x8"
},
{
"EventName": "fp_ret_sse_avx_ops.sp_div_flops",
"EventCode": "0x03",
"BriefDescription": "Single-precision divide/square root FLOPS.",
"PublicDescription": "This is a retire-based event. The number of retired SSE/AVX FLOPS. The number of events logged per cycle can vary from 0 to 64. This event can count above 15. Single-precision divide/square root FLOPS.",
"UMask": "0x4"
},
{
"EventName": "fp_ret_sse_avx_ops.sp_mult_flops",
"EventCode": "0x03",
"BriefDescription": "Single-precision multiply FLOPS.",
"PublicDescription": "This is a retire-based event. The number of retired SSE/AVX FLOPS. The number of events logged per cycle can vary from 0 to 64. This event can count above 15. Single-precision multiply FLOPS.",
"UMask": "0x2"
},
{
"EventName": "fp_ret_sse_avx_ops.sp_add_sub_flops",
"EventCode": "0x03",
"BriefDescription": "Single-precision add/subtract FLOPS.",
"PublicDescription": "This is a retire-based event. The number of retired SSE/AVX FLOPS. The number of events logged per cycle can vary from 0 to 64. This event can count above 15. Single-precision add/subtract FLOPS.",
"UMask": "0x1"
},
{
"EventName": "fp_num_mov_elim_scal_op.optimized",
"EventCode": "0x04",
"BriefDescription": "Number of Scalar Ops optimized.",
"PublicDescription": "This is a dispatch based speculative event, and is useful for measuring the effectiveness of the Move elimination and Scalar code optimization schemes. Number of Scalar Ops optimized.",
"UMask": "0x8"
},
{
"EventName": "fp_num_mov_elim_scal_op.opt_potential",
"EventCode": "0x04",
"BriefDescription": "Number of Ops that are candidates for optimization (have Z-bit either set or pass).",
"PublicDescription": "This is a dispatch based speculative event, and is useful for measuring the effectiveness of the Move elimination and Scalar code optimization schemes. Number of Ops that are candidates for optimization (have Z-bit either set or pass).",
"UMask": "0x4"
},
{
"EventName": "fp_num_mov_elim_scal_op.sse_mov_ops_elim",
"EventCode": "0x04",
"BriefDescription": "Number of SSE Move Ops eliminated.",
"PublicDescription": "This is a dispatch based speculative event, and is useful for measuring the effectiveness of the Move elimination and Scalar code optimization schemes. Number of SSE Move Ops eliminated.",
"UMask": "0x2"
},
{
"EventName": "fp_num_mov_elim_scal_op.sse_mov_ops",
"EventCode": "0x04",
"BriefDescription": "Number of SSE Move Ops.",
"PublicDescription": "This is a dispatch based speculative event, and is useful for measuring the effectiveness of the Move elimination and Scalar code optimization schemes. Number of SSE Move Ops.",
"UMask": "0x1"
},
{
"EventName": "fp_retired_ser_ops.x87_ctrl_ret",
"EventCode": "0x05",
"BriefDescription": "x87 control word mispredict traps due to mispredictions in RC or PC, or changes in mask bits.",
"PublicDescription": "The number of serializing Ops retired. x87 control word mispredict traps due to mispredictions in RC or PC, or changes in mask bits.",
"UMask": "0x8"
},
{
"EventName": "fp_retired_ser_ops.x87_bot_ret",
"EventCode": "0x05",
"BriefDescription": "x87 bottom-executing uOps retired.",
"PublicDescription": "The number of serializing Ops retired. x87 bottom-executing uOps retired.",
"UMask": "0x4"
},
{
"EventName": "fp_retired_ser_ops.sse_ctrl_ret",
"EventCode": "0x05",
"BriefDescription": "SSE control word mispredict traps due to mispredictions in RC, FTZ or DAZ, or changes in mask bits.",
"PublicDescription": "The number of serializing Ops retired. SSE control word mispredict traps due to mispredictions in RC, FTZ or DAZ, or changes in mask bits.",
"UMask": "0x2"
},
{
"EventName": "fp_retired_ser_ops.sse_bot_ret",
"EventCode": "0x05",
"BriefDescription": "SSE bottom-executing uOps retired.",
"PublicDescription": "The number of serializing Ops retired. SSE bottom-executing uOps retired.",
"UMask": "0x1"
}
]
[
{
"EventName": "ls_locks.bus_lock",
"EventCode": "0x25",
"BriefDescription": "Bus lock when a locked operations crosses a cache boundary or is done on an uncacheable memory type.",
"PublicDescription": "Bus lock when a locked operations crosses a cache boundary or is done on an uncacheable memory type.",
"UMask": "0x1"
},
{
"EventName": "ls_dispatch.ld_st_dispatch",
"EventCode": "0x29",
"BriefDescription": "Load-op-Stores.",
"PublicDescription": "Counts the number of operations dispatched to the LS unit. Unit Masks ADDed. Load-op-Stores.",
"UMask": "0x4"
},
{
"EventName": "ls_dispatch.store_dispatch",
"EventCode": "0x29",
"BriefDescription": "Counts the number of operations dispatched to the LS unit. Unit Masks ADDed.",
"PublicDescription": "Counts the number of operations dispatched to the LS unit. Unit Masks ADDed.",
"UMask": "0x2"
},
{
"EventName": "ls_dispatch.ld_dispatch",
"EventCode": "0x29",
"BriefDescription": "Counts the number of operations dispatched to the LS unit. Unit Masks ADDed.",
"PublicDescription": "Counts the number of operations dispatched to the LS unit. Unit Masks ADDed.",
"UMask": "0x1"
},
{
"EventName": "ls_stlf",
"EventCode": "0x35",
"BriefDescription": "Number of STLF hits."
},
{
"EventName": "ls_dc_accesses",
"EventCode": "0x40",
"BriefDescription": "The number of accesses to the data cache for load and store references. This may include certain microcode scratchpad accesses, although these are generally rare. Each increment represents an eight-byte access, although the instruction may only be accessing a portion of that. This event is a speculative event."
},
{
"EventName": "ls_l1_d_tlb_miss.all",
"EventCode": "0x45",
"BriefDescription": "L1 DTLB Miss or Reload off all sizes.",
"PublicDescription": "L1 DTLB Miss or Reload off all sizes.",
"UMask": "0xff"
},
{
"EventName": "ls_l1_d_tlb_miss.tlb_reload_1g_l2_miss",
"EventCode": "0x45",
"BriefDescription": "L1 DTLB Miss of a page of 1G size.",
"PublicDescription": "L1 DTLB Miss of a page of 1G size.",
"UMask": "0x80"
},
{
"EventName": "ls_l1_d_tlb_miss.tlb_reload_2m_l2_miss",
"EventCode": "0x45",
"BriefDescription": "L1 DTLB Miss of a page of 2M size.",
"PublicDescription": "L1 DTLB Miss of a page of 2M size.",
"UMask": "0x40"
},
{
"EventName": "ls_l1_d_tlb_miss.tlb_reload_32k_l2_miss",
"EventCode": "0x45",
"BriefDescription": "L1 DTLB Miss of a page of 32K size.",
"PublicDescription": "L1 DTLB Miss of a page of 32K size.",
"UMask": "0x20"
},
{
"EventName": "ls_l1_d_tlb_miss.tlb_reload_4k_l2_miss",
"EventCode": "0x45",
"BriefDescription": "L1 DTLB Miss of a page of 4K size.",
"PublicDescription": "L1 DTLB Miss of a page of 4K size.",
"UMask": "0x10"
},
{
"EventName": "ls_l1_d_tlb_miss.tlb_reload_1g_l2_hit",
"EventCode": "0x45",
"BriefDescription": "L1 DTLB Reload of a page of 1G size.",
"PublicDescription": "L1 DTLB Reload of a page of 1G size.",
"UMask": "0x8"
},
{
"EventName": "ls_l1_d_tlb_miss.tlb_reload_2m_l2_hit",
"EventCode": "0x45",
"BriefDescription": "L1 DTLB Reload of a page of 2M size.",
"PublicDescription": "L1 DTLB Reload of a page of 2M size.",
"UMask": "0x4"
},
{
"EventName": "ls_l1_d_tlb_miss.tlb_reload_32k_l2_hit",
"EventCode": "0x45",
"BriefDescription": "L1 DTLB Reload of a page of 32K size.",
"PublicDescription": "L1 DTLB Reload of a page of 32K size.",
"UMask": "0x2"
},
{
"EventName": "ls_l1_d_tlb_miss.tlb_reload_4k_l2_hit",
"EventCode": "0x45",
"BriefDescription": "L1 DTLB Reload of a page of 4K size.",
"PublicDescription": "L1 DTLB Reload of a page of 4K size.",
"UMask": "0x1"
},
{
"EventName": "ls_tablewalker.perf_mon_tablewalk_alloc_iside",
"EventCode": "0x46",
"BriefDescription": "Tablewalker allocation.",
"PublicDescription": "Tablewalker allocation.",
"UMask": "0xc"
},
{
"EventName": "ls_tablewalker.perf_mon_tablewalk_alloc_dside",
"EventCode": "0x46",
"BriefDescription": "Tablewalker allocation.",
"PublicDescription": "Tablewalker allocation.",
"UMask": "0x3"
},
{
"EventName": "ls_misal_accesses",
"EventCode": "0x47",
"BriefDescription": "Misaligned loads."
},
{
"EventName": "ls_pref_instr_disp.prefetch_nta",
"EventCode": "0x4b",
"BriefDescription": "Software Prefetch Instructions (PREFETCHNTA instruction) Dispatched.",
"PublicDescription": "Software Prefetch Instructions (PREFETCHNTA instruction) Dispatched.",
"UMask": "0x4"
},
{
"EventName": "ls_pref_instr_disp.store_prefetch_w",
"EventCode": "0x4b",
"BriefDescription": "Software Prefetch Instructions (3DNow PREFETCHW instruction) Dispatched.",
"PublicDescription": "Software Prefetch Instructions (3DNow PREFETCHW instruction) Dispatched.",
"UMask": "0x2"
},
{
"EventName": "ls_pref_instr_disp.load_prefetch_w",
"EventCode": "0x4b",
"BriefDescription": "Prefetch, Prefetch_T0_T1_T2.",
"PublicDescription": "Software Prefetch Instructions Dispatched. Prefetch, Prefetch_T0_T1_T2.",
"UMask": "0x1"
},
{
"EventName": "ls_inef_sw_pref.mab_mch_cnt",
"EventCode": "0x52",
"BriefDescription": "The number of software prefetches that did not fetch data outside of the processor core.",
"PublicDescription": "The number of software prefetches that did not fetch data outside of the processor core.",
"UMask": "0x2"
},
{
"EventName": "ls_inef_sw_pref.data_pipe_sw_pf_dc_hit",
"EventCode": "0x52",
"BriefDescription": "The number of software prefetches that did not fetch data outside of the processor core.",
"PublicDescription": "The number of software prefetches that did not fetch data outside of the processor core.",
"UMask": "0x1"
},
{
"EventName": "ls_not_halted_cyc",
"EventCode": "0x76",
"BriefDescription": "Cycles not in Halt."
}
]
[
{
"EventName": "ic_oc_mode_switch.oc_ic_mode_switch",
"EventCode": "0x28a",
"BriefDescription": "OC to IC mode switch.",
"PublicDescription": "OC Mode Switch. OC to IC mode switch.",
"UMask": "0x2"
},
{
"EventName": "ic_oc_mode_switch.ic_oc_mode_switch",
"EventCode": "0x28a",
"BriefDescription": "IC to OC mode switch.",
"PublicDescription": "OC Mode Switch. IC to OC mode switch.",
"UMask": "0x1"
},
{
"EventName": "de_dis_dispatch_token_stalls0.retire_token_stall",
"EventCode": "0xaf",
"BriefDescription": "RETIRE Tokens unavailable.",
"PublicDescription": "Cycles where a dispatch group is valid but does not get dispatched due to a token stall. RETIRE Tokens unavailable.",
"UMask": "0x40"
},
{
"EventName": "de_dis_dispatch_token_stalls0.agsq_token_stall",
"EventCode": "0xaf",
"BriefDescription": "AGSQ Tokens unavailable.",
"PublicDescription": "Cycles where a dispatch group is valid but does not get dispatched due to a token stall. AGSQ Tokens unavailable.",
"UMask": "0x20"
},
{
"EventName": "de_dis_dispatch_token_stalls0.alu_token_stall",
"EventCode": "0xaf",
"BriefDescription": "ALU tokens total unavailable.",
"PublicDescription": "Cycles where a dispatch group is valid but does not get dispatched due to a token stall. ALU tokens total unavailable.",
"UMask": "0x10"
},
{
"EventName": "de_dis_dispatch_token_stalls0.alsq3_0_token_stall",
"EventCode": "0xaf",
"BriefDescription": "Cycles where a dispatch group is valid but does not get dispatched due to a token stall.",
"PublicDescription": "Cycles where a dispatch group is valid but does not get dispatched due to a token stall.",
"UMask": "0x8"
},
{
"EventName": "de_dis_dispatch_token_stalls0.alsq3_token_stall",
"EventCode": "0xaf",
"BriefDescription": "ALSQ 3 Tokens unavailable.",
"PublicDescription": "Cycles where a dispatch group is valid but does not get dispatched due to a token stall. ALSQ 3 Tokens unavailable.",
"UMask": "0x4"
},
{
"EventName": "de_dis_dispatch_token_stalls0.alsq2_token_stall",
"EventCode": "0xaf",
"BriefDescription": "ALSQ 2 Tokens unavailable.",
"PublicDescription": "Cycles where a dispatch group is valid but does not get dispatched due to a token stall. ALSQ 2 Tokens unavailable.",
"UMask": "0x2"
},
{
"EventName": "de_dis_dispatch_token_stalls0.alsq1_token_stall",
"EventCode": "0xaf",
"BriefDescription": "ALSQ 1 Tokens unavailable.",
"PublicDescription": "Cycles where a dispatch group is valid but does not get dispatched due to a token stall. ALSQ 1 Tokens unavailable.",
"UMask": "0x1"
}
]
...@@ -33,3 +33,4 @@ GenuineIntel-6-25,v2,westmereep-sp,core ...@@ -33,3 +33,4 @@ GenuineIntel-6-25,v2,westmereep-sp,core
GenuineIntel-6-2F,v2,westmereex,core GenuineIntel-6-2F,v2,westmereex,core
GenuineIntel-6-55-[01234],v1,skylakex,core GenuineIntel-6-55-[01234],v1,skylakex,core
GenuineIntel-6-55-[56789ABCDEF],v1,cascadelakex,core GenuineIntel-6-55-[56789ABCDEF],v1,cascadelakex,core
AuthenticAMD-23-[[:xdigit:]]+,v1,amdfam17h,core
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details. # more details.
from __future__ import print_function
import os import os
import sys import sys
import struct import struct
...@@ -199,6 +201,18 @@ import datetime ...@@ -199,6 +201,18 @@ import datetime
from PySide.QtSql import * from PySide.QtSql import *
if sys.version_info < (3, 0):
def toserverstr(str):
return str
def toclientstr(str):
return str
else:
# Assume UTF-8 server_encoding and client_encoding
def toserverstr(str):
return bytes(str, "UTF_8")
def toclientstr(str):
return bytes(str, "UTF_8")
# Need to access PostgreSQL C library directly to use COPY FROM STDIN # Need to access PostgreSQL C library directly to use COPY FROM STDIN
from ctypes import * from ctypes import *
libpq = CDLL("libpq.so.5") libpq = CDLL("libpq.so.5")
...@@ -234,12 +248,17 @@ perf_db_export_mode = True ...@@ -234,12 +248,17 @@ perf_db_export_mode = True
perf_db_export_calls = False perf_db_export_calls = False
perf_db_export_callchains = False perf_db_export_callchains = False
def printerr(*args, **kw_args):
print(*args, file=sys.stderr, **kw_args)
def printdate(*args, **kw_args):
print(datetime.datetime.today(), *args, sep=' ', **kw_args)
def usage(): def usage():
print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>] [<callchains>]" printerr("Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>] [<callchains>]")
print >> sys.stderr, "where: columns 'all' or 'branches'" printerr("where: columns 'all' or 'branches'")
print >> sys.stderr, " calls 'calls' => create calls and call_paths table" printerr(" calls 'calls' => create calls and call_paths table")
print >> sys.stderr, " callchains 'callchains' => create call_paths table" printerr(" callchains 'callchains' => create call_paths table")
raise Exception("Too few arguments") raise Exception("Too few arguments")
if (len(sys.argv) < 2): if (len(sys.argv) < 2):
...@@ -273,7 +292,7 @@ def do_query(q, s): ...@@ -273,7 +292,7 @@ def do_query(q, s):
return return
raise Exception("Query failed: " + q.lastError().text()) raise Exception("Query failed: " + q.lastError().text())
print datetime.datetime.today(), "Creating database..." printdate("Creating database...")
db = QSqlDatabase.addDatabase('QPSQL') db = QSqlDatabase.addDatabase('QPSQL')
query = QSqlQuery(db) query = QSqlQuery(db)
...@@ -506,12 +525,12 @@ do_query(query, 'CREATE VIEW samples_view AS ' ...@@ -506,12 +525,12 @@ do_query(query, 'CREATE VIEW samples_view AS '
' FROM samples') ' FROM samples')
file_header = struct.pack("!11sii", "PGCOPY\n\377\r\n\0", 0, 0) file_header = struct.pack("!11sii", b"PGCOPY\n\377\r\n\0", 0, 0)
file_trailer = "\377\377" file_trailer = b"\377\377"
def open_output_file(file_name): def open_output_file(file_name):
path_name = output_dir_name + "/" + file_name path_name = output_dir_name + "/" + file_name
file = open(path_name, "w+") file = open(path_name, "wb+")
file.write(file_header) file.write(file_header)
return file return file
...@@ -526,13 +545,13 @@ def copy_output_file_direct(file, table_name): ...@@ -526,13 +545,13 @@ def copy_output_file_direct(file, table_name):
# Use COPY FROM STDIN because security may prevent postgres from accessing the files directly # Use COPY FROM STDIN because security may prevent postgres from accessing the files directly
def copy_output_file(file, table_name): def copy_output_file(file, table_name):
conn = PQconnectdb("dbname = " + dbname) conn = PQconnectdb(toclientstr("dbname = " + dbname))
if (PQstatus(conn)): if (PQstatus(conn)):
raise Exception("COPY FROM STDIN PQconnectdb failed") raise Exception("COPY FROM STDIN PQconnectdb failed")
file.write(file_trailer) file.write(file_trailer)
file.seek(0) file.seek(0)
sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')" sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')"
res = PQexec(conn, sql) res = PQexec(conn, toclientstr(sql))
if (PQresultStatus(res) != 4): if (PQresultStatus(res) != 4):
raise Exception("COPY FROM STDIN PQexec failed") raise Exception("COPY FROM STDIN PQexec failed")
data = file.read(65536) data = file.read(65536)
...@@ -566,7 +585,7 @@ if perf_db_export_calls: ...@@ -566,7 +585,7 @@ if perf_db_export_calls:
call_file = open_output_file("call_table.bin") call_file = open_output_file("call_table.bin")
def trace_begin(): def trace_begin():
print datetime.datetime.today(), "Writing to intermediate files..." printdate("Writing to intermediate files...")
# id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs # id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs
evsel_table(0, "unknown") evsel_table(0, "unknown")
machine_table(0, 0, "unknown") machine_table(0, 0, "unknown")
...@@ -582,7 +601,7 @@ def trace_begin(): ...@@ -582,7 +601,7 @@ def trace_begin():
unhandled_count = 0 unhandled_count = 0
def trace_end(): def trace_end():
print datetime.datetime.today(), "Copying to database..." printdate("Copying to database...")
copy_output_file(evsel_file, "selected_events") copy_output_file(evsel_file, "selected_events")
copy_output_file(machine_file, "machines") copy_output_file(machine_file, "machines")
copy_output_file(thread_file, "threads") copy_output_file(thread_file, "threads")
...@@ -597,7 +616,7 @@ def trace_end(): ...@@ -597,7 +616,7 @@ def trace_end():
if perf_db_export_calls: if perf_db_export_calls:
copy_output_file(call_file, "calls") copy_output_file(call_file, "calls")
print datetime.datetime.today(), "Removing intermediate files..." printdate("Removing intermediate files...")
remove_output_file(evsel_file) remove_output_file(evsel_file)
remove_output_file(machine_file) remove_output_file(machine_file)
remove_output_file(thread_file) remove_output_file(thread_file)
...@@ -612,7 +631,7 @@ def trace_end(): ...@@ -612,7 +631,7 @@ def trace_end():
if perf_db_export_calls: if perf_db_export_calls:
remove_output_file(call_file) remove_output_file(call_file)
os.rmdir(output_dir_name) os.rmdir(output_dir_name)
print datetime.datetime.today(), "Adding primary keys" printdate("Adding primary keys")
do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE machines ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE machines ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE threads ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE threads ADD PRIMARY KEY (id)')
...@@ -627,7 +646,7 @@ def trace_end(): ...@@ -627,7 +646,7 @@ def trace_end():
if perf_db_export_calls: if perf_db_export_calls:
do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)')
print datetime.datetime.today(), "Adding foreign keys" printdate("Adding foreign keys")
do_query(query, 'ALTER TABLE threads ' do_query(query, 'ALTER TABLE threads '
'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),' 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)') 'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)')
...@@ -663,8 +682,8 @@ def trace_end(): ...@@ -663,8 +682,8 @@ def trace_end():
do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)') do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)')
if (unhandled_count): if (unhandled_count):
print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events" printdate("Warning: ", unhandled_count, " unhandled events")
print datetime.datetime.today(), "Done" printdate("Done")
def trace_unhandled(event_name, context, event_fields_dict): def trace_unhandled(event_name, context, event_fields_dict):
global unhandled_count global unhandled_count
...@@ -674,12 +693,14 @@ def sched__sched_switch(*x): ...@@ -674,12 +693,14 @@ def sched__sched_switch(*x):
pass pass
def evsel_table(evsel_id, evsel_name, *x): def evsel_table(evsel_id, evsel_name, *x):
evsel_name = toserverstr(evsel_name)
n = len(evsel_name) n = len(evsel_name)
fmt = "!hiqi" + str(n) + "s" fmt = "!hiqi" + str(n) + "s"
value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name) value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name)
evsel_file.write(value) evsel_file.write(value)
def machine_table(machine_id, pid, root_dir, *x): def machine_table(machine_id, pid, root_dir, *x):
root_dir = toserverstr(root_dir)
n = len(root_dir) n = len(root_dir)
fmt = "!hiqiii" + str(n) + "s" fmt = "!hiqiii" + str(n) + "s"
value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir) value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir)
...@@ -690,6 +711,7 @@ def thread_table(thread_id, machine_id, process_id, pid, tid, *x): ...@@ -690,6 +711,7 @@ def thread_table(thread_id, machine_id, process_id, pid, tid, *x):
thread_file.write(value) thread_file.write(value)
def comm_table(comm_id, comm_str, *x): def comm_table(comm_id, comm_str, *x):
comm_str = toserverstr(comm_str)
n = len(comm_str) n = len(comm_str)
fmt = "!hiqi" + str(n) + "s" fmt = "!hiqi" + str(n) + "s"
value = struct.pack(fmt, 2, 8, comm_id, n, comm_str) value = struct.pack(fmt, 2, 8, comm_id, n, comm_str)
...@@ -701,6 +723,9 @@ def comm_thread_table(comm_thread_id, comm_id, thread_id, *x): ...@@ -701,6 +723,9 @@ def comm_thread_table(comm_thread_id, comm_id, thread_id, *x):
comm_thread_file.write(value) comm_thread_file.write(value)
def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x): def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x):
short_name = toserverstr(short_name)
long_name = toserverstr(long_name)
build_id = toserverstr(build_id)
n1 = len(short_name) n1 = len(short_name)
n2 = len(long_name) n2 = len(long_name)
n3 = len(build_id) n3 = len(build_id)
...@@ -709,12 +734,14 @@ def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x): ...@@ -709,12 +734,14 @@ def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x):
dso_file.write(value) dso_file.write(value)
def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x): def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x):
symbol_name = toserverstr(symbol_name)
n = len(symbol_name) n = len(symbol_name)
fmt = "!hiqiqiqiqiii" + str(n) + "s" fmt = "!hiqiqiqiqiii" + str(n) + "s"
value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name) value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name)
symbol_file.write(value) symbol_file.write(value)
def branch_type_table(branch_type, name, *x): def branch_type_table(branch_type, name, *x):
name = toserverstr(name)
n = len(name) n = len(name)
fmt = "!hiii" + str(n) + "s" fmt = "!hiii" + str(n) + "s"
value = struct.pack(fmt, 2, 4, branch_type, n, name) value = struct.pack(fmt, 2, 4, branch_type, n, name)
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details. # more details.
from __future__ import print_function
import os import os
import sys import sys
import struct import struct
...@@ -60,11 +62,17 @@ perf_db_export_mode = True ...@@ -60,11 +62,17 @@ perf_db_export_mode = True
perf_db_export_calls = False perf_db_export_calls = False
perf_db_export_callchains = False perf_db_export_callchains = False
def printerr(*args, **keyword_args):
print(*args, file=sys.stderr, **keyword_args)
def printdate(*args, **kw_args):
print(datetime.datetime.today(), *args, sep=' ', **kw_args)
def usage(): def usage():
print >> sys.stderr, "Usage is: export-to-sqlite.py <database name> [<columns>] [<calls>] [<callchains>]" printerr("Usage is: export-to-sqlite.py <database name> [<columns>] [<calls>] [<callchains>]");
print >> sys.stderr, "where: columns 'all' or 'branches'" printerr("where: columns 'all' or 'branches'");
print >> sys.stderr, " calls 'calls' => create calls and call_paths table" printerr(" calls 'calls' => create calls and call_paths table");
print >> sys.stderr, " callchains 'callchains' => create call_paths table" printerr(" callchains 'callchains' => create call_paths table");
raise Exception("Too few arguments") raise Exception("Too few arguments")
if (len(sys.argv) < 2): if (len(sys.argv) < 2):
...@@ -100,7 +108,7 @@ def do_query_(q): ...@@ -100,7 +108,7 @@ def do_query_(q):
return return
raise Exception("Query failed: " + q.lastError().text()) raise Exception("Query failed: " + q.lastError().text())
print datetime.datetime.today(), "Creating database..." printdate("Creating database ...")
db_exists = False db_exists = False
try: try:
...@@ -378,7 +386,7 @@ if perf_db_export_calls: ...@@ -378,7 +386,7 @@ if perf_db_export_calls:
call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)") call_query.prepare("INSERT INTO calls VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
def trace_begin(): def trace_begin():
print datetime.datetime.today(), "Writing records..." printdate("Writing records...")
do_query(query, 'BEGIN TRANSACTION') do_query(query, 'BEGIN TRANSACTION')
# id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs # id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs
evsel_table(0, "unknown") evsel_table(0, "unknown")
...@@ -397,14 +405,14 @@ unhandled_count = 0 ...@@ -397,14 +405,14 @@ unhandled_count = 0
def trace_end(): def trace_end():
do_query(query, 'END TRANSACTION') do_query(query, 'END TRANSACTION')
print datetime.datetime.today(), "Adding indexes" printdate("Adding indexes")
if perf_db_export_calls: if perf_db_export_calls:
do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)') do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)') do_query(query, 'CREATE INDEX pid_idx ON calls (parent_id)')
if (unhandled_count): if (unhandled_count):
print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events" printdate("Warning: ", unhandled_count, " unhandled events")
print datetime.datetime.today(), "Done" printdate("Done")
def trace_unhandled(event_name, context, event_fields_dict): def trace_unhandled(event_name, context, event_fields_dict):
global unhandled_count global unhandled_count
......
...@@ -88,11 +88,20 @@ ...@@ -88,11 +88,20 @@
# 7fab593ea956 48 89 15 3b 13 22 00 movq %rdx, 0x22133b(%rip) # 7fab593ea956 48 89 15 3b 13 22 00 movq %rdx, 0x22133b(%rip)
# 8107675243232 2 ls 22011 22011 hardware interrupt No 7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel]) # 8107675243232 2 ls 22011 22011 hardware interrupt No 7fab593ea956 _dl_start+0x26 (ld-2.19.so) -> ffffffff86a012e0 page_fault ([kernel])
from __future__ import print_function
import sys import sys
import weakref import weakref
import threading import threading
import string import string
import cPickle try:
# Python2
import cPickle as pickle
# size of pickled integer big enough for record size
glb_nsz = 8
except ImportError:
import pickle
glb_nsz = 16
import re import re
import os import os
from PySide.QtCore import * from PySide.QtCore import *
...@@ -102,6 +111,15 @@ from decimal import * ...@@ -102,6 +111,15 @@ from decimal import *
from ctypes import * from ctypes import *
from multiprocessing import Process, Array, Value, Event from multiprocessing import Process, Array, Value, Event
# xrange is range in Python3
try:
xrange
except NameError:
xrange = range
def printerr(*args, **keyword_args):
print(*args, file=sys.stderr, **keyword_args)
# Data formatting helpers # Data formatting helpers
def tohex(ip): def tohex(ip):
...@@ -1004,10 +1022,6 @@ class ChildDataItemFinder(): ...@@ -1004,10 +1022,6 @@ class ChildDataItemFinder():
glb_chunk_sz = 10000 glb_chunk_sz = 10000
# size of pickled integer big enough for record size
glb_nsz = 8
# Background process for SQL data fetcher # Background process for SQL data fetcher
class SQLFetcherProcess(): class SQLFetcherProcess():
...@@ -1066,7 +1080,7 @@ class SQLFetcherProcess(): ...@@ -1066,7 +1080,7 @@ class SQLFetcherProcess():
return True return True
if space >= glb_nsz: if space >= glb_nsz:
# Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer # Use 0 (or space < glb_nsz) to mean there is no more at the top of the buffer
nd = cPickle.dumps(0, cPickle.HIGHEST_PROTOCOL) nd = pickle.dumps(0, pickle.HIGHEST_PROTOCOL)
self.buffer[self.local_head : self.local_head + len(nd)] = nd self.buffer[self.local_head : self.local_head + len(nd)] = nd
self.local_head = 0 self.local_head = 0
if self.local_tail - self.local_head > sz: if self.local_tail - self.local_head > sz:
...@@ -1084,9 +1098,9 @@ class SQLFetcherProcess(): ...@@ -1084,9 +1098,9 @@ class SQLFetcherProcess():
self.wait_event.wait() self.wait_event.wait()
def AddToBuffer(self, obj): def AddToBuffer(self, obj):
d = cPickle.dumps(obj, cPickle.HIGHEST_PROTOCOL) d = pickle.dumps(obj, pickle.HIGHEST_PROTOCOL)
n = len(d) n = len(d)
nd = cPickle.dumps(n, cPickle.HIGHEST_PROTOCOL) nd = pickle.dumps(n, pickle.HIGHEST_PROTOCOL)
sz = n + glb_nsz sz = n + glb_nsz
self.WaitForSpace(sz) self.WaitForSpace(sz)
pos = self.local_head pos = self.local_head
...@@ -1198,12 +1212,12 @@ class SQLFetcher(QObject): ...@@ -1198,12 +1212,12 @@ class SQLFetcher(QObject):
pos = self.local_tail pos = self.local_tail
if len(self.buffer) - pos < glb_nsz: if len(self.buffer) - pos < glb_nsz:
pos = 0 pos = 0
n = cPickle.loads(self.buffer[pos : pos + glb_nsz]) n = pickle.loads(self.buffer[pos : pos + glb_nsz])
if n == 0: if n == 0:
pos = 0 pos = 0
n = cPickle.loads(self.buffer[0 : glb_nsz]) n = pickle.loads(self.buffer[0 : glb_nsz])
pos += glb_nsz pos += glb_nsz
obj = cPickle.loads(self.buffer[pos : pos + n]) obj = pickle.loads(self.buffer[pos : pos + n])
self.local_tail = pos + n self.local_tail = pos + n
return obj return obj
...@@ -2973,7 +2987,7 @@ class DBRef(): ...@@ -2973,7 +2987,7 @@ class DBRef():
def Main(): def Main():
if (len(sys.argv) < 2): if (len(sys.argv) < 2):
print >> sys.stderr, "Usage is: exported-sql-viewer.py {<database name> | --help-only}" printerr("Usage is: exported-sql-viewer.py {<database name> | --help-only}");
raise Exception("Too few arguments") raise Exception("Too few arguments")
dbname = sys.argv[1] dbname = sys.argv[1]
...@@ -2986,8 +3000,8 @@ def Main(): ...@@ -2986,8 +3000,8 @@ def Main():
is_sqlite3 = False is_sqlite3 = False
try: try:
f = open(dbname) f = open(dbname, "rb")
if f.read(15) == "SQLite format 3": if f.read(15) == b'SQLite format 3':
is_sqlite3 = True is_sqlite3 = True
f.close() f.close()
except: except:
......
...@@ -611,14 +611,16 @@ void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence) ...@@ -611,14 +611,16 @@ void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence)
browser->top = browser->entries; browser->top = browser->entries;
break; break;
case SEEK_CUR: case SEEK_CUR:
browser->top = browser->top + browser->top_idx + offset; browser->top = (char **)browser->top + offset;
break; break;
case SEEK_END: case SEEK_END:
browser->top = browser->top + browser->nr_entries - 1 + offset; browser->top = (char **)browser->entries + browser->nr_entries - 1 + offset;
break; break;
default: default:
return; return;
} }
assert((char **)browser->top < (char **)browser->entries + browser->nr_entries);
assert((char **)browser->top >= (char **)browser->entries);
} }
unsigned int ui_browser__argv_refresh(struct ui_browser *browser) unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
...@@ -630,7 +632,9 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser) ...@@ -630,7 +632,9 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
browser->top = browser->entries; browser->top = browser->entries;
pos = (char **)browser->top; pos = (char **)browser->top;
while (idx < browser->nr_entries) { while (idx < browser->nr_entries &&
row < (unsigned)SLtt_Screen_Rows - 1) {
assert(pos < (char **)browser->entries + browser->nr_entries);
if (!browser->filter || !browser->filter(browser, *pos)) { if (!browser->filter || !browser->filter(browser, *pos)) {
ui_browser__gotorc(browser, row, 0); ui_browser__gotorc(browser, row, 0);
browser->write(browser, pos, row); browser->write(browser, pos, row);
......
...@@ -3,6 +3,7 @@ perf-y += hists.o ...@@ -3,6 +3,7 @@ perf-y += hists.o
perf-y += map.o perf-y += map.o
perf-y += scripts.o perf-y += scripts.o
perf-y += header.o perf-y += header.o
perf-y += res_sample.o
CFLAGS_annotate.o += -DENABLE_SLFUTURE_CONST CFLAGS_annotate.o += -DENABLE_SLFUTURE_CONST
CFLAGS_hists.o += -DENABLE_SLFUTURE_CONST CFLAGS_hists.o += -DENABLE_SLFUTURE_CONST
......
...@@ -750,7 +750,7 @@ static int annotate_browser__run(struct annotate_browser *browser, ...@@ -750,7 +750,7 @@ static int annotate_browser__run(struct annotate_browser *browser,
continue; continue;
case 'r': case 'r':
{ {
script_browse(NULL); script_browse(NULL, NULL);
continue; continue;
} }
case 'k': case 'k':
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <string.h> #include <string.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <sys/ttydefaults.h> #include <sys/ttydefaults.h>
#include <linux/time64.h>
#include "../../util/callchain.h" #include "../../util/callchain.h"
#include "../../util/evsel.h" #include "../../util/evsel.h"
...@@ -30,6 +31,7 @@ ...@@ -30,6 +31,7 @@
#include "srcline.h" #include "srcline.h"
#include "string2.h" #include "string2.h"
#include "units.h" #include "units.h"
#include "time-utils.h"
#include "sane_ctype.h" #include "sane_ctype.h"
...@@ -1224,6 +1226,8 @@ void hist_browser__init_hpp(void) ...@@ -1224,6 +1226,8 @@ void hist_browser__init_hpp(void)
hist_browser__hpp_color_overhead_guest_us; hist_browser__hpp_color_overhead_guest_us;
perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color = perf_hpp__format[PERF_HPP__OVERHEAD_ACC].color =
hist_browser__hpp_color_overhead_acc; hist_browser__hpp_color_overhead_acc;
res_sample_init();
} }
static int hist_browser__show_entry(struct hist_browser *browser, static int hist_browser__show_entry(struct hist_browser *browser,
...@@ -2338,9 +2342,12 @@ static int switch_data_file(void) ...@@ -2338,9 +2342,12 @@ static int switch_data_file(void)
} }
struct popup_action { struct popup_action {
unsigned long time;
struct thread *thread; struct thread *thread;
struct map_symbol ms; struct map_symbol ms;
int socket; int socket;
struct perf_evsel *evsel;
enum rstype rstype;
int (*fn)(struct hist_browser *browser, struct popup_action *act); int (*fn)(struct hist_browser *browser, struct popup_action *act);
}; };
...@@ -2527,45 +2534,136 @@ static int ...@@ -2527,45 +2534,136 @@ static int
do_run_script(struct hist_browser *browser __maybe_unused, do_run_script(struct hist_browser *browser __maybe_unused,
struct popup_action *act) struct popup_action *act)
{ {
char script_opt[64]; char *script_opt;
memset(script_opt, 0, sizeof(script_opt)); int len;
int n = 0;
len = 100;
if (act->thread)
len += strlen(thread__comm_str(act->thread));
else if (act->ms.sym)
len += strlen(act->ms.sym->name);
script_opt = malloc(len);
if (!script_opt)
return -1;
script_opt[0] = 0;
if (act->thread) { if (act->thread) {
scnprintf(script_opt, sizeof(script_opt), " -c %s ", n = scnprintf(script_opt, len, " -c %s ",
thread__comm_str(act->thread)); thread__comm_str(act->thread));
} else if (act->ms.sym) { } else if (act->ms.sym) {
scnprintf(script_opt, sizeof(script_opt), " -S %s ", n = scnprintf(script_opt, len, " -S %s ",
act->ms.sym->name); act->ms.sym->name);
} }
script_browse(script_opt); if (act->time) {
char start[32], end[32];
unsigned long starttime = act->time;
unsigned long endtime = act->time + symbol_conf.time_quantum;
if (starttime == endtime) { /* Display 1ms as fallback */
starttime -= 1*NSEC_PER_MSEC;
endtime += 1*NSEC_PER_MSEC;
}
timestamp__scnprintf_usec(starttime, start, sizeof start);
timestamp__scnprintf_usec(endtime, end, sizeof end);
n += snprintf(script_opt + n, len - n, " --time %s,%s", start, end);
}
script_browse(script_opt, act->evsel);
free(script_opt);
return 0; return 0;
} }
static int static int
add_script_opt(struct hist_browser *browser __maybe_unused, do_res_sample_script(struct hist_browser *browser __maybe_unused,
struct popup_action *act)
{
struct hist_entry *he;
he = hist_browser__selected_entry(browser);
res_sample_browse(he->res_samples, he->num_res, act->evsel, act->rstype);
return 0;
}
static int
add_script_opt_2(struct hist_browser *browser __maybe_unused,
struct popup_action *act, char **optstr, struct popup_action *act, char **optstr,
struct thread *thread, struct symbol *sym) struct thread *thread, struct symbol *sym,
struct perf_evsel *evsel, const char *tstr)
{ {
if (thread) { if (thread) {
if (asprintf(optstr, "Run scripts for samples of thread [%s]", if (asprintf(optstr, "Run scripts for samples of thread [%s]%s",
thread__comm_str(thread)) < 0) thread__comm_str(thread), tstr) < 0)
return 0; return 0;
} else if (sym) { } else if (sym) {
if (asprintf(optstr, "Run scripts for samples of symbol [%s]", if (asprintf(optstr, "Run scripts for samples of symbol [%s]%s",
sym->name) < 0) sym->name, tstr) < 0)
return 0; return 0;
} else { } else {
if (asprintf(optstr, "Run scripts for all samples") < 0) if (asprintf(optstr, "Run scripts for all samples%s", tstr) < 0)
return 0; return 0;
} }
act->thread = thread; act->thread = thread;
act->ms.sym = sym; act->ms.sym = sym;
act->evsel = evsel;
act->fn = do_run_script; act->fn = do_run_script;
return 1; return 1;
} }
static int
add_script_opt(struct hist_browser *browser,
struct popup_action *act, char **optstr,
struct thread *thread, struct symbol *sym,
struct perf_evsel *evsel)
{
int n, j;
struct hist_entry *he;
n = add_script_opt_2(browser, act, optstr, thread, sym, evsel, "");
he = hist_browser__selected_entry(browser);
if (sort_order && strstr(sort_order, "time")) {
char tstr[128];
optstr++;
act++;
j = sprintf(tstr, " in ");
j += timestamp__scnprintf_usec(he->time, tstr + j,
sizeof tstr - j);
j += sprintf(tstr + j, "-");
timestamp__scnprintf_usec(he->time + symbol_conf.time_quantum,
tstr + j, sizeof tstr - j);
n += add_script_opt_2(browser, act, optstr, thread, sym,
evsel, tstr);
act->time = he->time;
}
return n;
}
static int
add_res_sample_opt(struct hist_browser *browser __maybe_unused,
struct popup_action *act, char **optstr,
struct res_sample *res_sample,
struct perf_evsel *evsel,
enum rstype type)
{
if (!res_sample)
return 0;
if (asprintf(optstr, "Show context for individual samples %s",
type == A_ASM ? "with assembler" :
type == A_SOURCE ? "with source" : "") < 0)
return 0;
act->fn = do_res_sample_script;
act->evsel = evsel;
act->rstype = type;
return 1;
}
static int static int
do_switch_data(struct hist_browser *browser __maybe_unused, do_switch_data(struct hist_browser *browser __maybe_unused,
struct popup_action *act __maybe_unused) struct popup_action *act __maybe_unused)
...@@ -3031,7 +3129,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, ...@@ -3031,7 +3129,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
nr_options += add_script_opt(browser, nr_options += add_script_opt(browser,
&actions[nr_options], &actions[nr_options],
&options[nr_options], &options[nr_options],
thread, NULL); thread, NULL, evsel);
} }
/* /*
* Note that browser->selection != NULL * Note that browser->selection != NULL
...@@ -3046,11 +3144,24 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, ...@@ -3046,11 +3144,24 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
nr_options += add_script_opt(browser, nr_options += add_script_opt(browser,
&actions[nr_options], &actions[nr_options],
&options[nr_options], &options[nr_options],
NULL, browser->selection->sym); NULL, browser->selection->sym,
evsel);
} }
} }
nr_options += add_script_opt(browser, &actions[nr_options], nr_options += add_script_opt(browser, &actions[nr_options],
&options[nr_options], NULL, NULL); &options[nr_options], NULL, NULL, evsel);
nr_options += add_res_sample_opt(browser, &actions[nr_options],
&options[nr_options],
hist_browser__selected_entry(browser)->res_samples,
evsel, A_NORMAL);
nr_options += add_res_sample_opt(browser, &actions[nr_options],
&options[nr_options],
hist_browser__selected_entry(browser)->res_samples,
evsel, A_ASM);
nr_options += add_res_sample_opt(browser, &actions[nr_options],
&options[nr_options],
hist_browser__selected_entry(browser)->res_samples,
evsel, A_SOURCE);
nr_options += add_switch_opt(browser, &actions[nr_options], nr_options += add_switch_opt(browser, &actions[nr_options],
&options[nr_options]); &options[nr_options]);
skip_scripting: skip_scripting:
......
// SPDX-License-Identifier: GPL-2.0
/* Display a menu with individual samples to browse with perf script */
#include "util.h"
#include "hist.h"
#include "evsel.h"
#include "hists.h"
#include "sort.h"
#include "config.h"
#include "time-utils.h"
#include <linux/time64.h>
static u64 context_len = 10 * NSEC_PER_MSEC;
static int res_sample_config(const char *var, const char *value, void *data __maybe_unused)
{
if (!strcmp(var, "samples.context"))
return perf_config_u64(&context_len, var, value);
return 0;
}
void res_sample_init(void)
{
perf_config(res_sample_config, NULL);
}
int res_sample_browse(struct res_sample *res_samples, int num_res,
struct perf_evsel *evsel, enum rstype rstype)
{
char **names;
int i, n;
int choice;
char *cmd;
char pbuf[256], tidbuf[32], cpubuf[32];
const char *perf = perf_exe(pbuf, sizeof pbuf);
char trange[128], tsample[64];
struct res_sample *r;
char extra_format[256];
names = calloc(num_res, sizeof(char *));
if (!names)
return -1;
for (i = 0; i < num_res; i++) {
char tbuf[64];
timestamp__scnprintf_nsec(res_samples[i].time, tbuf, sizeof tbuf);
if (asprintf(&names[i], "%s: CPU %d tid %d", tbuf,
res_samples[i].cpu, res_samples[i].tid) < 0) {
while (--i >= 0)
free(names[i]);
free(names);
return -1;
}
}
choice = ui__popup_menu(num_res, names);
for (i = 0; i < num_res; i++)
free(names[i]);
free(names);
if (choice < 0 || choice >= num_res)
return -1;
r = &res_samples[choice];
n = timestamp__scnprintf_nsec(r->time - context_len, trange, sizeof trange);
trange[n++] = ',';
timestamp__scnprintf_nsec(r->time + context_len, trange + n, sizeof trange - n);
timestamp__scnprintf_nsec(r->time, tsample, sizeof tsample);
attr_to_script(extra_format, &evsel->attr);
if (asprintf(&cmd, "%s script %s%s --time %s %s%s %s%s --ns %s %s %s %s %s | less +/%s",
perf,
input_name ? "-i " : "",
input_name ? input_name : "",
trange,
r->cpu >= 0 ? "--cpu " : "",
r->cpu >= 0 ? (sprintf(cpubuf, "%d", r->cpu), cpubuf) : "",
r->tid ? "--tid " : "",
r->tid ? (sprintf(tidbuf, "%d", r->tid), tidbuf) : "",
extra_format,
rstype == A_ASM ? "-F +insn --xed" :
rstype == A_SOURCE ? "-F +srcline,+srccode" : "",
symbol_conf.inline_name ? "--inline" : "",
"--show-lost-events ",
r->tid ? "--show-switch-events --show-task-events " : "",
tsample) < 0)
return -1;
run_script(cmd);
free(cmd);
return 0;
}
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <elf.h>
#include <inttypes.h>
#include <sys/ttydefaults.h>
#include <string.h>
#include "../../util/sort.h" #include "../../util/sort.h"
#include "../../util/util.h" #include "../../util/util.h"
#include "../../util/hist.h" #include "../../util/hist.h"
#include "../../util/debug.h" #include "../../util/debug.h"
#include "../../util/symbol.h" #include "../../util/symbol.h"
#include "../browser.h" #include "../browser.h"
#include "../helpline.h"
#include "../libslang.h" #include "../libslang.h"
#include "config.h"
/* 2048 lines should be enough for a script output */
#define MAX_LINES 2048
/* 160 bytes for one output line */
#define AVERAGE_LINE_LEN 160
struct script_line {
struct list_head node;
char line[AVERAGE_LINE_LEN];
};
struct perf_script_browser {
struct ui_browser b;
struct list_head entries;
const char *script_name;
int nr_lines;
};
#define SCRIPT_NAMELEN 128 #define SCRIPT_NAMELEN 128
#define SCRIPT_MAX_NO 64 #define SCRIPT_MAX_NO 64
...@@ -40,149 +18,169 @@ struct perf_script_browser { ...@@ -40,149 +18,169 @@ struct perf_script_browser {
*/ */
#define SCRIPT_FULLPATH_LEN 256 #define SCRIPT_FULLPATH_LEN 256
struct script_config {
const char **names;
char **paths;
int index;
const char *perf;
char extra_format[256];
};
void attr_to_script(char *extra_format, struct perf_event_attr *attr)
{
extra_format[0] = 0;
if (attr->read_format & PERF_FORMAT_GROUP)
strcat(extra_format, " -F +metric");
if (attr->sample_type & PERF_SAMPLE_BRANCH_STACK)
strcat(extra_format, " -F +brstackinsn --xed");
if (attr->sample_type & PERF_SAMPLE_REGS_INTR)
strcat(extra_format, " -F +iregs");
if (attr->sample_type & PERF_SAMPLE_REGS_USER)
strcat(extra_format, " -F +uregs");
if (attr->sample_type & PERF_SAMPLE_PHYS_ADDR)
strcat(extra_format, " -F +phys_addr");
}
static int add_script_option(const char *name, const char *opt,
struct script_config *c)
{
c->names[c->index] = name;
if (asprintf(&c->paths[c->index],
"%s script %s -F +metric %s %s",
c->perf, opt, symbol_conf.inline_name ? " --inline" : "",
c->extra_format) < 0)
return -1;
c->index++;
return 0;
}
static int scripts_config(const char *var, const char *value, void *data)
{
struct script_config *c = data;
if (!strstarts(var, "scripts."))
return -1;
if (c->index >= SCRIPT_MAX_NO)
return -1;
c->names[c->index] = strdup(var + 7);
if (!c->names[c->index])
return -1;
if (asprintf(&c->paths[c->index], "%s %s", value,
c->extra_format) < 0)
return -1;
c->index++;
return 0;
}
/* /*
* When success, will copy the full path of the selected script * When success, will copy the full path of the selected script
* into the buffer pointed by script_name, and return 0. * into the buffer pointed by script_name, and return 0.
* Return -1 on failure. * Return -1 on failure.
*/ */
static int list_scripts(char *script_name) static int list_scripts(char *script_name, bool *custom,
struct perf_evsel *evsel)
{ {
char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO]; char *buf, *paths[SCRIPT_MAX_NO], *names[SCRIPT_MAX_NO];
int i, num, choice, ret = -1; int i, num, choice;
int ret = 0;
int max_std, custom_perf;
char pbuf[256];
const char *perf = perf_exe(pbuf, sizeof pbuf);
struct script_config scriptc = {
.names = (const char **)names,
.paths = paths,
.perf = perf
};
script_name[0] = 0;
/* Preset the script name to SCRIPT_NAMELEN */ /* Preset the script name to SCRIPT_NAMELEN */
buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN)); buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN));
if (!buf) if (!buf)
return ret; return -1;
for (i = 0; i < SCRIPT_MAX_NO; i++) { if (evsel)
names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN); attr_to_script(scriptc.extra_format, &evsel->attr);
add_script_option("Show individual samples", "", &scriptc);
add_script_option("Show individual samples with assembler", "-F +insn --xed",
&scriptc);
add_script_option("Show individual samples with source", "-F +srcline,+srccode",
&scriptc);
perf_config(scripts_config, &scriptc);
custom_perf = scriptc.index;
add_script_option("Show samples with custom perf script arguments", "", &scriptc);
i = scriptc.index;
max_std = i;
for (; i < SCRIPT_MAX_NO; i++) {
names[i] = buf + (i - max_std) * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN);
paths[i] = names[i] + SCRIPT_NAMELEN; paths[i] = names[i] + SCRIPT_NAMELEN;
} }
num = find_scripts(names, paths); num = find_scripts(names + max_std, paths + max_std, SCRIPT_MAX_NO - max_std,
if (num > 0) { SCRIPT_FULLPATH_LEN);
choice = ui__popup_menu(num, names); if (num < 0)
if (choice < num && choice >= 0) { num = 0;
strcpy(script_name, paths[choice]); choice = ui__popup_menu(num + max_std, (char * const *)names);
ret = 0; if (choice < 0) {
} ret = -1;
goto out;
} }
if (choice == custom_perf) {
char script_args[50];
int key = ui_browser__input_window("perf script command",
"Enter perf script command line (without perf script prefix)",
script_args, "", 0);
if (key != K_ENTER)
return -1;
sprintf(script_name, "%s script %s", perf, script_args);
} else if (choice < num + max_std) {
strcpy(script_name, paths[choice]);
}
*custom = choice >= max_std;
out:
free(buf); free(buf);
for (i = 0; i < max_std; i++)
free(paths[i]);
return ret; return ret;
} }
static void script_browser__write(struct ui_browser *browser, void run_script(char *cmd)
void *entry, int row)
{ {
struct script_line *sline = list_entry(entry, struct script_line, node); pr_debug("Running %s\n", cmd);
bool current_entry = ui_browser__is_current_entry(browser, row); SLang_reset_tty();
if (system(cmd) < 0)
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : pr_warning("Cannot run %s\n", cmd);
HE_COLORSET_NORMAL); /*
* SLang doesn't seem to reset the whole terminal, so be more
ui_browser__write_nstring(browser, sline->line, browser->width); * forceful to get back to the original state.
*/
printf("\033[c\033[H\033[J");
fflush(stdout);
SLang_init_tty(0, 0, 0);
SLsmg_refresh();
} }
static int script_browser__run(struct perf_script_browser *browser) int script_browse(const char *script_opt, struct perf_evsel *evsel)
{ {
int key; char *cmd, script_name[SCRIPT_FULLPATH_LEN];
bool custom = false;
if (ui_browser__show(&browser->b, browser->script_name, memset(script_name, 0, SCRIPT_FULLPATH_LEN);
"Press ESC to exit") < 0) if (list_scripts(script_name, &custom, evsel))
return -1; return -1;
while (1) { if (asprintf(&cmd, "%s%s %s %s%s 2>&1 | less",
key = ui_browser__run(&browser->b, 0); custom ? "perf script -s " : "",
script_name,
/* We can add some special key handling here if needed */ script_opt ? script_opt : "",
break; input_name ? "-i " : "",
} input_name ? input_name : "") < 0)
ui_browser__hide(&browser->b);
return key;
}
int script_browse(const char *script_opt)
{
char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN];
char *line = NULL;
size_t len = 0;
ssize_t retlen;
int ret = -1, nr_entries = 0;
FILE *fp;
void *buf;
struct script_line *sline;
struct perf_script_browser script = {
.b = {
.refresh = ui_browser__list_head_refresh,
.seek = ui_browser__list_head_seek,
.write = script_browser__write,
},
.script_name = script_name,
};
INIT_LIST_HEAD(&script.entries);
/* Save each line of the output in one struct script_line object. */
buf = zalloc((sizeof(*sline)) * MAX_LINES);
if (!buf)
return -1; return -1;
sline = buf;
memset(script_name, 0, SCRIPT_FULLPATH_LEN);
if (list_scripts(script_name))
goto exit;
sprintf(cmd, "perf script -s %s ", script_name);
if (script_opt) run_script(cmd);
strcat(cmd, script_opt); free(cmd);
if (input_name) { return 0;
strcat(cmd, " -i ");
strcat(cmd, input_name);
}
strcat(cmd, " 2>&1");
fp = popen(cmd, "r");
if (!fp)
goto exit;
while ((retlen = getline(&line, &len, fp)) != -1) {
strncpy(sline->line, line, AVERAGE_LINE_LEN);
/* If one output line is very large, just cut it short */
if (retlen >= AVERAGE_LINE_LEN) {
sline->line[AVERAGE_LINE_LEN - 1] = '\0';
sline->line[AVERAGE_LINE_LEN - 2] = '\n';
}
list_add_tail(&sline->node, &script.entries);
if (script.b.width < retlen)
script.b.width = retlen;
if (nr_entries++ >= MAX_LINES - 1)
break;
sline++;
}
if (script.b.width > AVERAGE_LINE_LEN)
script.b.width = AVERAGE_LINE_LEN;
free(line);
pclose(fp);
script.nr_lines = nr_entries;
script.b.nr_entries = nr_entries;
script.b.entries = &script.entries;
ret = script_browser__run(&script);
exit:
free(buf);
return ret;
} }
#ifndef INSN_H
#define INSN_H 1
struct perf_sample;
struct machine;
struct thread;
void arch_fetch_insn(struct perf_sample *sample,
struct thread *thread,
struct machine *machine);
#endif
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "data.h" #include "data.h"
#include "util.h" #include "util.h"
#include "debug.h" #include "debug.h"
#include "header.h"
static void close_dir(struct perf_data_file *files, int nr) static void close_dir(struct perf_data_file *files, int nr)
{ {
...@@ -34,12 +35,16 @@ int perf_data__create_dir(struct perf_data *data, int nr) ...@@ -34,12 +35,16 @@ int perf_data__create_dir(struct perf_data *data, int nr)
struct perf_data_file *files = NULL; struct perf_data_file *files = NULL;
int i, ret = -1; int i, ret = -1;
if (WARN_ON(!data->is_dir))
return -EINVAL;
files = zalloc(nr * sizeof(*files)); files = zalloc(nr * sizeof(*files));
if (!files) if (!files)
return -ENOMEM; return -ENOMEM;
data->dir.files = files; data->dir.version = PERF_DIR_VERSION;
data->dir.nr = nr; data->dir.files = files;
data->dir.nr = nr;
for (i = 0; i < nr; i++) { for (i = 0; i < nr; i++) {
struct perf_data_file *file = &files[i]; struct perf_data_file *file = &files[i];
...@@ -69,6 +74,13 @@ int perf_data__open_dir(struct perf_data *data) ...@@ -69,6 +74,13 @@ int perf_data__open_dir(struct perf_data *data)
DIR *dir; DIR *dir;
int nr = 0; int nr = 0;
if (WARN_ON(!data->is_dir))
return -EINVAL;
/* The version is provided by DIR_FORMAT feature. */
if (WARN_ON(data->dir.version != PERF_DIR_VERSION))
return -1;
dir = opendir(data->path); dir = opendir(data->path);
if (!dir) if (!dir)
return -EINVAL; return -EINVAL;
...@@ -118,6 +130,26 @@ int perf_data__open_dir(struct perf_data *data) ...@@ -118,6 +130,26 @@ int perf_data__open_dir(struct perf_data *data)
return ret; return ret;
} }
int perf_data__update_dir(struct perf_data *data)
{
int i;
if (WARN_ON(!data->is_dir))
return -EINVAL;
for (i = 0; i < data->dir.nr; i++) {
struct perf_data_file *file = &data->dir.files[i];
struct stat st;
if (fstat(file->fd, &st))
return -1;
file->size = st.st_size;
}
return 0;
}
static bool check_pipe(struct perf_data *data) static bool check_pipe(struct perf_data *data)
{ {
struct stat st; struct stat st;
...@@ -173,6 +205,16 @@ static int check_backup(struct perf_data *data) ...@@ -173,6 +205,16 @@ static int check_backup(struct perf_data *data)
return 0; return 0;
} }
static bool is_dir(struct perf_data *data)
{
struct stat st;
if (stat(data->path, &st))
return false;
return (st.st_mode & S_IFMT) == S_IFDIR;
}
static int open_file_read(struct perf_data *data) static int open_file_read(struct perf_data *data)
{ {
struct stat st; struct stat st;
...@@ -254,6 +296,30 @@ static int open_file_dup(struct perf_data *data) ...@@ -254,6 +296,30 @@ static int open_file_dup(struct perf_data *data)
return open_file(data); return open_file(data);
} }
static int open_dir(struct perf_data *data)
{
int ret;
/*
* So far we open only the header, so we can read the data version and
* layout.
*/
if (asprintf(&data->file.path, "%s/header", data->path) < 0)
return -1;
if (perf_data__is_write(data) &&
mkdir(data->path, S_IRWXU) < 0)
return -1;
ret = open_file(data);
/* Cleanup whatever we managed to create so far. */
if (ret && perf_data__is_write(data))
rm_rf_perf_data(data->path);
return ret;
}
int perf_data__open(struct perf_data *data) int perf_data__open(struct perf_data *data)
{ {
if (check_pipe(data)) if (check_pipe(data))
...@@ -265,11 +331,18 @@ int perf_data__open(struct perf_data *data) ...@@ -265,11 +331,18 @@ int perf_data__open(struct perf_data *data)
if (check_backup(data)) if (check_backup(data))
return -1; return -1;
return open_file_dup(data); if (perf_data__is_read(data))
data->is_dir = is_dir(data);
return perf_data__is_dir(data) ?
open_dir(data) : open_file_dup(data);
} }
void perf_data__close(struct perf_data *data) void perf_data__close(struct perf_data *data)
{ {
if (perf_data__is_dir(data))
perf_data__close_dir(data);
zfree(&data->file.path); zfree(&data->file.path);
close(data->file.fd); close(data->file.fd);
} }
...@@ -326,3 +399,20 @@ int perf_data__switch(struct perf_data *data, ...@@ -326,3 +399,20 @@ int perf_data__switch(struct perf_data *data,
free(new_filepath); free(new_filepath);
return ret; return ret;
} }
unsigned long perf_data__size(struct perf_data *data)
{
u64 size = data->file.size;
int i;
if (!data->is_dir)
return size;
for (i = 0; i < data->dir.nr; i++) {
struct perf_data_file *file = &data->dir.files[i];
size += file->size;
}
return size;
}
...@@ -19,10 +19,12 @@ struct perf_data { ...@@ -19,10 +19,12 @@ struct perf_data {
const char *path; const char *path;
struct perf_data_file file; struct perf_data_file file;
bool is_pipe; bool is_pipe;
bool is_dir;
bool force; bool force;
enum perf_data_mode mode; enum perf_data_mode mode;
struct { struct {
u64 version;
struct perf_data_file *files; struct perf_data_file *files;
int nr; int nr;
} dir; } dir;
...@@ -43,14 +45,14 @@ static inline int perf_data__is_pipe(struct perf_data *data) ...@@ -43,14 +45,14 @@ static inline int perf_data__is_pipe(struct perf_data *data)
return data->is_pipe; return data->is_pipe;
} }
static inline int perf_data__fd(struct perf_data *data) static inline bool perf_data__is_dir(struct perf_data *data)
{ {
return data->file.fd; return data->is_dir;
} }
static inline unsigned long perf_data__size(struct perf_data *data) static inline int perf_data__fd(struct perf_data *data)
{ {
return data->file.size; return data->file.fd;
} }
int perf_data__open(struct perf_data *data); int perf_data__open(struct perf_data *data);
...@@ -73,4 +75,6 @@ int perf_data__switch(struct perf_data *data, ...@@ -73,4 +75,6 @@ int perf_data__switch(struct perf_data *data,
int perf_data__create_dir(struct perf_data *data, int nr); int perf_data__create_dir(struct perf_data *data, int nr);
int perf_data__open_dir(struct perf_data *data); int perf_data__open_dir(struct perf_data *data);
void perf_data__close_dir(struct perf_data *data); void perf_data__close_dir(struct perf_data *data);
int perf_data__update_dir(struct perf_data *data);
unsigned long perf_data__size(struct perf_data *data);
#endif /* __PERF_DATA_H */ #endif /* __PERF_DATA_H */
...@@ -861,6 +861,21 @@ static int write_clockid(struct feat_fd *ff, ...@@ -861,6 +861,21 @@ static int write_clockid(struct feat_fd *ff,
sizeof(ff->ph->env.clockid_res_ns)); sizeof(ff->ph->env.clockid_res_ns));
} }
static int write_dir_format(struct feat_fd *ff,
struct perf_evlist *evlist __maybe_unused)
{
struct perf_session *session;
struct perf_data *data;
session = container_of(ff->ph, struct perf_session, header);
data = session->data;
if (WARN_ON(!perf_data__is_dir(data)))
return -1;
return do_write(ff, &data->dir.version, sizeof(data->dir.version));
}
static int cpu_cache_level__sort(const void *a, const void *b) static int cpu_cache_level__sort(const void *a, const void *b)
{ {
struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a; struct cpu_cache_level *cache_a = (struct cpu_cache_level *)a;
...@@ -1341,6 +1356,17 @@ static void print_clockid(struct feat_fd *ff, FILE *fp) ...@@ -1341,6 +1356,17 @@ static void print_clockid(struct feat_fd *ff, FILE *fp)
ff->ph->env.clockid_res_ns * 1000); ff->ph->env.clockid_res_ns * 1000);
} }
static void print_dir_format(struct feat_fd *ff, FILE *fp)
{
struct perf_session *session;
struct perf_data *data;
session = container_of(ff->ph, struct perf_session, header);
data = session->data;
fprintf(fp, "# directory data version : %"PRIu64"\n", data->dir.version);
}
static void free_event_desc(struct perf_evsel *events) static void free_event_desc(struct perf_evsel *events)
{ {
struct perf_evsel *evsel; struct perf_evsel *evsel;
...@@ -2373,6 +2399,21 @@ static int process_clockid(struct feat_fd *ff, ...@@ -2373,6 +2399,21 @@ static int process_clockid(struct feat_fd *ff,
return 0; return 0;
} }
static int process_dir_format(struct feat_fd *ff,
void *_data __maybe_unused)
{
struct perf_session *session;
struct perf_data *data;
session = container_of(ff->ph, struct perf_session, header);
data = session->data;
if (WARN_ON(!perf_data__is_dir(data)))
return -1;
return do_read_u64(ff, &data->dir.version);
}
struct feature_ops { struct feature_ops {
int (*write)(struct feat_fd *ff, struct perf_evlist *evlist); int (*write)(struct feat_fd *ff, struct perf_evlist *evlist);
void (*print)(struct feat_fd *ff, FILE *fp); void (*print)(struct feat_fd *ff, FILE *fp);
...@@ -2432,7 +2473,8 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { ...@@ -2432,7 +2473,8 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPN(CACHE, cache, true), FEAT_OPN(CACHE, cache, true),
FEAT_OPR(SAMPLE_TIME, sample_time, false), FEAT_OPR(SAMPLE_TIME, sample_time, false),
FEAT_OPR(MEM_TOPOLOGY, mem_topology, true), FEAT_OPR(MEM_TOPOLOGY, mem_topology, true),
FEAT_OPR(CLOCKID, clockid, false) FEAT_OPR(CLOCKID, clockid, false),
FEAT_OPN(DIR_FORMAT, dir_format, false)
}; };
struct header_print_data { struct header_print_data {
......
...@@ -39,6 +39,7 @@ enum { ...@@ -39,6 +39,7 @@ enum {
HEADER_SAMPLE_TIME, HEADER_SAMPLE_TIME,
HEADER_MEM_TOPOLOGY, HEADER_MEM_TOPOLOGY,
HEADER_CLOCKID, HEADER_CLOCKID,
HEADER_DIR_FORMAT,
HEADER_LAST_FEATURE, HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256, HEADER_FEAT_BITS = 256,
}; };
...@@ -48,6 +49,10 @@ enum perf_header_version { ...@@ -48,6 +49,10 @@ enum perf_header_version {
PERF_HEADER_VERSION_2, PERF_HEADER_VERSION_2,
}; };
enum perf_dir_version {
PERF_DIR_VERSION = 1,
};
struct perf_file_section { struct perf_file_section {
u64 offset; u64 offset;
u64 size; u64 size;
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <math.h> #include <math.h>
#include <inttypes.h> #include <inttypes.h>
#include <sys/param.h> #include <sys/param.h>
#include <linux/time64.h>
static bool hists__filter_entry_by_dso(struct hists *hists, static bool hists__filter_entry_by_dso(struct hists *hists,
struct hist_entry *he); struct hist_entry *he);
...@@ -192,6 +193,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) ...@@ -192,6 +193,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3); hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3);
hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12); hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12); hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
hists__new_col_len(hists, HISTC_TIME, 12);
if (h->srcline) { if (h->srcline) {
len = MAX(strlen(h->srcline), strlen(sort_srcline.se_header)); len = MAX(strlen(h->srcline), strlen(sort_srcline.se_header));
...@@ -246,6 +248,14 @@ static void he_stat__add_cpumode_period(struct he_stat *he_stat, ...@@ -246,6 +248,14 @@ static void he_stat__add_cpumode_period(struct he_stat *he_stat,
} }
} }
static long hist_time(unsigned long htime)
{
unsigned long time_quantum = symbol_conf.time_quantum;
if (time_quantum)
return (htime / time_quantum) * time_quantum;
return htime;
}
static void he_stat__add_period(struct he_stat *he_stat, u64 period, static void he_stat__add_period(struct he_stat *he_stat, u64 period,
u64 weight) u64 weight)
{ {
...@@ -426,6 +436,13 @@ static int hist_entry__init(struct hist_entry *he, ...@@ -426,6 +436,13 @@ static int hist_entry__init(struct hist_entry *he,
goto err_rawdata; goto err_rawdata;
} }
if (symbol_conf.res_sample) {
he->res_samples = calloc(sizeof(struct res_sample),
symbol_conf.res_sample);
if (!he->res_samples)
goto err_srcline;
}
INIT_LIST_HEAD(&he->pairs.node); INIT_LIST_HEAD(&he->pairs.node);
thread__get(he->thread); thread__get(he->thread);
he->hroot_in = RB_ROOT_CACHED; he->hroot_in = RB_ROOT_CACHED;
...@@ -436,6 +453,9 @@ static int hist_entry__init(struct hist_entry *he, ...@@ -436,6 +453,9 @@ static int hist_entry__init(struct hist_entry *he,
return 0; return 0;
err_srcline:
free(he->srcline);
err_rawdata: err_rawdata:
free(he->raw_data); free(he->raw_data);
...@@ -593,6 +613,32 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists, ...@@ -593,6 +613,32 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
return he; return he;
} }
static unsigned random_max(unsigned high)
{
unsigned thresh = -high % high;
for (;;) {
unsigned r = random();
if (r >= thresh)
return r % high;
}
}
static void hists__res_sample(struct hist_entry *he, struct perf_sample *sample)
{
struct res_sample *r;
int j;
if (he->num_res < symbol_conf.res_sample) {
j = he->num_res++;
} else {
j = random_max(symbol_conf.res_sample);
}
r = &he->res_samples[j];
r->time = sample->time;
r->cpu = sample->cpu;
r->tid = sample->tid;
}
static struct hist_entry* static struct hist_entry*
__hists__add_entry(struct hists *hists, __hists__add_entry(struct hists *hists,
struct addr_location *al, struct addr_location *al,
...@@ -635,10 +681,13 @@ __hists__add_entry(struct hists *hists, ...@@ -635,10 +681,13 @@ __hists__add_entry(struct hists *hists,
.raw_data = sample->raw_data, .raw_data = sample->raw_data,
.raw_size = sample->raw_size, .raw_size = sample->raw_size,
.ops = ops, .ops = ops,
.time = hist_time(sample->time),
}, *he = hists__findnew_entry(hists, &entry, al, sample_self); }, *he = hists__findnew_entry(hists, &entry, al, sample_self);
if (!hists->has_callchains && he && he->callchain_size != 0) if (!hists->has_callchains && he && he->callchain_size != 0)
hists->has_callchains = true; hists->has_callchains = true;
if (he && symbol_conf.res_sample)
hists__res_sample(he, sample);
return he; return he;
} }
...@@ -1162,6 +1211,7 @@ void hist_entry__delete(struct hist_entry *he) ...@@ -1162,6 +1211,7 @@ void hist_entry__delete(struct hist_entry *he)
mem_info__zput(he->mem_info); mem_info__zput(he->mem_info);
} }
zfree(&he->res_samples);
zfree(&he->stat_acc); zfree(&he->stat_acc);
free_srcline(he->srcline); free_srcline(he->srcline);
if (he->srcfile && he->srcfile[0]) if (he->srcfile && he->srcfile[0])
......
...@@ -31,6 +31,7 @@ enum hist_filter { ...@@ -31,6 +31,7 @@ enum hist_filter {
enum hist_column { enum hist_column {
HISTC_SYMBOL, HISTC_SYMBOL,
HISTC_TIME,
HISTC_DSO, HISTC_DSO,
HISTC_THREAD, HISTC_THREAD,
HISTC_COMM, HISTC_COMM,
...@@ -432,9 +433,18 @@ struct hist_browser_timer { ...@@ -432,9 +433,18 @@ struct hist_browser_timer {
}; };
struct annotation_options; struct annotation_options;
struct res_sample;
enum rstype {
A_NORMAL,
A_ASM,
A_SOURCE
};
#ifdef HAVE_SLANG_SUPPORT #ifdef HAVE_SLANG_SUPPORT
#include "../ui/keysyms.h" #include "../ui/keysyms.h"
void attr_to_script(char *buf, struct perf_event_attr *attr);
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel, int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
struct hist_browser_timer *hbt, struct hist_browser_timer *hbt,
struct annotation_options *annotation_opts); struct annotation_options *annotation_opts);
...@@ -449,7 +459,13 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, ...@@ -449,7 +459,13 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
struct perf_env *env, struct perf_env *env,
bool warn_lost_event, bool warn_lost_event,
struct annotation_options *annotation_options); struct annotation_options *annotation_options);
int script_browse(const char *script_opt);
int script_browse(const char *script_opt, struct perf_evsel *evsel);
void run_script(char *cmd);
int res_sample_browse(struct res_sample *res_samples, int num_res,
struct perf_evsel *evsel, enum rstype rstype);
void res_sample_init(void);
#else #else
static inline static inline
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
...@@ -478,11 +494,22 @@ static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused, ...@@ -478,11 +494,22 @@ static inline int hist_entry__tui_annotate(struct hist_entry *he __maybe_unused,
return 0; return 0;
} }
static inline int script_browse(const char *script_opt __maybe_unused) static inline int script_browse(const char *script_opt __maybe_unused,
struct perf_evsel *evsel __maybe_unused)
{ {
return 0; return 0;
} }
static inline int res_sample_browse(struct res_sample *res_samples __maybe_unused,
int num_res __maybe_unused,
struct perf_evsel *evsel __maybe_unused,
enum rstype rstype __maybe_unused)
{
return 0;
}
static inline void res_sample_init(void) {}
#define K_LEFT -1000 #define K_LEFT -1000
#define K_RIGHT -2000 #define K_RIGHT -2000
#define K_SWITCH_INPUT_DATA -3000 #define K_SWITCH_INPUT_DATA -3000
......
...@@ -160,8 +160,10 @@ static struct map *kernel_get_module_map(const char *module) ...@@ -160,8 +160,10 @@ static struct map *kernel_get_module_map(const char *module)
if (module && strchr(module, '/')) if (module && strchr(module, '/'))
return dso__new_map(module); return dso__new_map(module);
if (!module) if (!module) {
module = "kernel"; pos = machine__kernel_map(host_machine);
return map__get(pos);
}
for (pos = maps__first(maps); pos; pos = map__next(pos)) { for (pos = maps__first(maps); pos; pos = map__next(pos)) {
/* short_name is "[module]" */ /* short_name is "[module]" */
......
...@@ -152,6 +152,10 @@ struct perf_session *perf_session__new(struct perf_data *data, ...@@ -152,6 +152,10 @@ struct perf_session *perf_session__new(struct perf_data *data,
} }
perf_evlist__init_trace_event_sample_raw(session->evlist); perf_evlist__init_trace_event_sample_raw(session->evlist);
/* Open the directory data. */
if (data->is_dir && perf_data__open_dir(data))
goto out_delete;
} }
} else { } else {
session->machines.host.env = &perf_env; session->machines.host.env = &perf_env;
...@@ -1843,10 +1847,17 @@ fetch_mmaped_event(struct perf_session *session, ...@@ -1843,10 +1847,17 @@ fetch_mmaped_event(struct perf_session *session,
#define NUM_MMAPS 128 #define NUM_MMAPS 128
#endif #endif
struct reader;
typedef s64 (*reader_cb_t)(struct perf_session *session,
union perf_event *event,
u64 file_offset);
struct reader { struct reader {
int fd; int fd;
u64 data_size; u64 data_size;
u64 data_offset; u64 data_offset;
reader_cb_t process;
}; };
static int static int
...@@ -1917,7 +1928,7 @@ reader__process_events(struct reader *rd, struct perf_session *session, ...@@ -1917,7 +1928,7 @@ reader__process_events(struct reader *rd, struct perf_session *session,
size = event->header.size; size = event->header.size;
if (size < sizeof(struct perf_event_header) || if (size < sizeof(struct perf_event_header) ||
(skip = perf_session__process_event(session, event, file_pos)) < 0) { (skip = rd->process(session, event, file_pos)) < 0) {
pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n",
file_offset + head, event->header.size, file_offset + head, event->header.size,
event->header.type); event->header.type);
...@@ -1943,12 +1954,20 @@ reader__process_events(struct reader *rd, struct perf_session *session, ...@@ -1943,12 +1954,20 @@ reader__process_events(struct reader *rd, struct perf_session *session,
return err; return err;
} }
static s64 process_simple(struct perf_session *session,
union perf_event *event,
u64 file_offset)
{
return perf_session__process_event(session, event, file_offset);
}
static int __perf_session__process_events(struct perf_session *session) static int __perf_session__process_events(struct perf_session *session)
{ {
struct reader rd = { struct reader rd = {
.fd = perf_data__fd(session->data), .fd = perf_data__fd(session->data),
.data_size = session->header.data_size, .data_size = session->header.data_size,
.data_offset = session->header.data_offset, .data_offset = session->header.data_offset,
.process = process_simple,
}; };
struct ordered_events *oe = &session->ordered_events; struct ordered_events *oe = &session->ordered_events;
struct perf_tool *tool = session->tool; struct perf_tool *tool = session->tool;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <inttypes.h> #include <inttypes.h>
#include <regex.h> #include <regex.h>
#include <linux/mman.h> #include <linux/mman.h>
#include <linux/time64.h>
#include "sort.h" #include "sort.h"
#include "hist.h" #include "hist.h"
#include "comm.h" #include "comm.h"
...@@ -15,6 +16,7 @@ ...@@ -15,6 +16,7 @@
#include <traceevent/event-parse.h> #include <traceevent/event-parse.h>
#include "mem-events.h" #include "mem-events.h"
#include "annotate.h" #include "annotate.h"
#include "time-utils.h"
#include <linux/kernel.h> #include <linux/kernel.h>
regex_t parent_regex; regex_t parent_regex;
...@@ -654,6 +656,42 @@ struct sort_entry sort_socket = { ...@@ -654,6 +656,42 @@ struct sort_entry sort_socket = {
.se_width_idx = HISTC_SOCKET, .se_width_idx = HISTC_SOCKET,
}; };
/* --sort time */
static int64_t
sort__time_cmp(struct hist_entry *left, struct hist_entry *right)
{
return right->time - left->time;
}
static int hist_entry__time_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width)
{
unsigned long secs;
unsigned long long nsecs;
char he_time[32];
nsecs = he->time;
secs = nsecs / NSEC_PER_SEC;
nsecs -= secs * NSEC_PER_SEC;
if (symbol_conf.nanosecs)
snprintf(he_time, sizeof he_time, "%5lu.%09llu: ",
secs, nsecs);
else
timestamp__scnprintf_usec(he->time, he_time,
sizeof(he_time));
return repsep_snprintf(bf, size, "%-.*s", width, he_time);
}
struct sort_entry sort_time = {
.se_header = "Time",
.se_cmp = sort__time_cmp,
.se_snprintf = hist_entry__time_snprintf,
.se_width_idx = HISTC_TIME,
};
/* --sort trace */ /* --sort trace */
static char *get_trace_output(struct hist_entry *he) static char *get_trace_output(struct hist_entry *he)
...@@ -1634,6 +1672,7 @@ static struct sort_dimension common_sort_dimensions[] = { ...@@ -1634,6 +1672,7 @@ static struct sort_dimension common_sort_dimensions[] = {
DIM(SORT_DSO_SIZE, "dso_size", sort_dso_size), DIM(SORT_DSO_SIZE, "dso_size", sort_dso_size),
DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id), DIM(SORT_CGROUP_ID, "cgroup_id", sort_cgroup_id),
DIM(SORT_SYM_IPC_NULL, "ipc_null", sort_sym_ipc_null), DIM(SORT_SYM_IPC_NULL, "ipc_null", sort_sym_ipc_null),
DIM(SORT_TIME, "time", sort_time),
}; };
#undef DIM #undef DIM
......
...@@ -47,6 +47,12 @@ extern struct sort_entry sort_srcline; ...@@ -47,6 +47,12 @@ extern struct sort_entry sort_srcline;
extern enum sort_type sort__first_dimension; extern enum sort_type sort__first_dimension;
extern const char default_mem_sort_order[]; extern const char default_mem_sort_order[];
struct res_sample {
u64 time;
int cpu;
int tid;
};
struct he_stat { struct he_stat {
u64 period; u64 period;
u64 period_sys; u64 period_sys;
...@@ -135,10 +141,13 @@ struct hist_entry { ...@@ -135,10 +141,13 @@ struct hist_entry {
char *srcfile; char *srcfile;
struct symbol *parent; struct symbol *parent;
struct branch_info *branch_info; struct branch_info *branch_info;
long time;
struct hists *hists; struct hists *hists;
struct mem_info *mem_info; struct mem_info *mem_info;
void *raw_data; void *raw_data;
u32 raw_size; u32 raw_size;
int num_res;
struct res_sample *res_samples;
void *trace_output; void *trace_output;
struct perf_hpp_list *hpp_list; struct perf_hpp_list *hpp_list;
struct hist_entry *parent_he; struct hist_entry *parent_he;
...@@ -231,6 +240,7 @@ enum sort_type { ...@@ -231,6 +240,7 @@ enum sort_type {
SORT_DSO_SIZE, SORT_DSO_SIZE,
SORT_CGROUP_ID, SORT_CGROUP_ID,
SORT_SYM_IPC_NULL, SORT_SYM_IPC_NULL,
SORT_TIME,
/* branch stack specific sort keys */ /* branch stack specific sort keys */
__SORT_BRANCH_STACK, __SORT_BRANCH_STACK,
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <string.h> #include <string.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mman.h> #include <linux/mman.h>
#include <linux/time64.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/param.h> #include <sys/param.h>
...@@ -39,15 +40,18 @@ int vmlinux_path__nr_entries; ...@@ -39,15 +40,18 @@ int vmlinux_path__nr_entries;
char **vmlinux_path; char **vmlinux_path;
struct symbol_conf symbol_conf = { struct symbol_conf symbol_conf = {
.nanosecs = false,
.use_modules = true, .use_modules = true,
.try_vmlinux_path = true, .try_vmlinux_path = true,
.demangle = true, .demangle = true,
.demangle_kernel = false, .demangle_kernel = false,
.cumulate_callchain = true, .cumulate_callchain = true,
.time_quantum = 100 * NSEC_PER_MSEC, /* 100ms */
.show_hist_headers = true, .show_hist_headers = true,
.symfs = "", .symfs = "",
.event_group = true, .event_group = true,
.inline_name = true, .inline_name = true,
.res_sample = 0,
}; };
static enum dso_binary_type binary_type_symtab[] = { static enum dso_binary_type binary_type_symtab[] = {
......
...@@ -8,6 +8,7 @@ struct strlist; ...@@ -8,6 +8,7 @@ struct strlist;
struct intlist; struct intlist;
struct symbol_conf { struct symbol_conf {
bool nanosecs;
unsigned short priv_size; unsigned short priv_size;
bool try_vmlinux_path, bool try_vmlinux_path,
init_annotation, init_annotation,
...@@ -55,6 +56,7 @@ struct symbol_conf { ...@@ -55,6 +56,7 @@ struct symbol_conf {
*sym_list_str, *sym_list_str,
*col_width_list_str, *col_width_list_str,
*bt_stop_list_str; *bt_stop_list_str;
unsigned long time_quantum;
struct strlist *dso_list, struct strlist *dso_list,
*comm_list, *comm_list,
*sym_list, *sym_list,
...@@ -66,6 +68,7 @@ struct symbol_conf { ...@@ -66,6 +68,7 @@ struct symbol_conf {
struct intlist *pid_list, struct intlist *pid_list,
*tid_list; *tid_list;
const char *symfs; const char *symfs;
int res_sample;
}; };
extern struct symbol_conf symbol_conf; extern struct symbol_conf symbol_conf;
......
...@@ -453,6 +453,14 @@ int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz) ...@@ -453,6 +453,14 @@ int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz)
return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec); return scnprintf(buf, sz, "%"PRIu64".%06"PRIu64, sec, usec);
} }
int timestamp__scnprintf_nsec(u64 timestamp, char *buf, size_t sz)
{
u64 sec = timestamp / NSEC_PER_SEC,
nsec = timestamp % NSEC_PER_SEC;
return scnprintf(buf, sz, "%" PRIu64 ".%09" PRIu64, sec, nsec);
}
int fetch_current_timestamp(char *buf, size_t sz) int fetch_current_timestamp(char *buf, size_t sz)
{ {
struct timeval tv; struct timeval tv;
......
...@@ -30,6 +30,7 @@ int perf_time__parse_for_ranges(const char *str, struct perf_session *session, ...@@ -30,6 +30,7 @@ int perf_time__parse_for_ranges(const char *str, struct perf_session *session,
int *range_size, int *range_num); int *range_size, int *range_num);
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz); int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz);
int timestamp__scnprintf_nsec(u64 timestamp, char *buf, size_t sz);
int fetch_current_timestamp(char *buf, size_t sz); int fetch_current_timestamp(char *buf, size_t sz);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment