diff options
Diffstat (limited to 'tools')
52 files changed, 953 insertions, 129 deletions
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt index d6b2a4f2108b..c7f5f55634ac 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt @@ -8,7 +8,7 @@ perf-lock - Analyze lock events SYNOPSIS -------- [verse] -'perf lock' {record|report|trace} +'perf lock' {record|report|script|info} DESCRIPTION ----------- @@ -20,10 +20,13 @@ and statistics with this 'perf lock' command. produces the file "perf.data" which contains tracing results of lock events. - 'perf lock trace' shows raw lock events. - 'perf lock report' reports statistical data. + 'perf lock script' shows raw lock events. + + 'perf lock info' shows metadata like threads or addresses + of lock instances. + COMMON OPTIONS -------------- @@ -47,6 +50,17 @@ REPORT OPTIONS Sorting key. Possible values: acquired (default), contended, wait_total, wait_max, wait_min. +INFO OPTIONS +------------ + +-t:: +--threads:: + dump thread list in perf.data + +-m:: +--map:: + dump map of lock instances (address:name table) + SEE ALSO -------- linkperf:perf[1] diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 2937f7e14bb7..ff9a66e0d4e4 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -58,6 +58,10 @@ OPTIONS --tid=:: Record events on existing thread ID. +-u:: +--uid=:: + Record events in threads owned by uid. Name or number. + -r:: --realtime=:: Collect data with this RT SCHED_FIFO priority. diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 2f6cef43da25..e9cbfcddfa3f 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -115,7 +115,7 @@ OPTIONS -f:: --fields:: Comma separated list of fields to print. Options are: - comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr. + comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff. Field list can be prepended with the type, trace, sw or hw, to indicate to which event type the field list applies. e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace @@ -200,6 +200,9 @@ OPTIONS It currently includes: cpu and numa topology of the host system. It can only be used with the perf script report mode. +--show-kernel-path:: + Try to resolve the path of [kernel.kallsyms] + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-script-perl[1], diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index b1a5bbbfebef..ab1454ed450f 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -78,6 +78,10 @@ Default is to monitor all CPUS. --tid=<tid>:: Profile events on existing thread ID. +-u:: +--uid=:: + Record events in threads owned by uid. Name or number. + -r <priority>:: --realtime=<priority>:: Collect data with this RT SCHED_FIFO priority. diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 7c12650165ae..64df5de12ca8 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -61,7 +61,7 @@ ifeq ($(ARCH),x86_64) ifeq (${IS_X86_64}, 1) RAW_ARCH := x86_64 ARCH_CFLAGS := -DARCH_X86_64 - ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S + ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S endif endif @@ -359,8 +359,10 @@ BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o ifeq ($(RAW_ARCH),x86_64) BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o +BUILTIN_OBJS += $(OUTPUT)bench/mem-memset-x86-64-asm.o endif BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o +BUILTIN_OBJS += $(OUTPUT)bench/mem-memset.o BUILTIN_OBJS += $(OUTPUT)builtin-diff.o BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o @@ -792,7 +794,6 @@ help: @echo ' quick-install-html - install the html documentation quickly' @echo '' @echo 'Perf maintainer targets:' - @echo ' distclean - alias to clean' @echo ' clean - clean all binary objects and build output' doc: diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h index f7781c6267c0..a09bece6dad2 100644 --- a/tools/perf/bench/bench.h +++ b/tools/perf/bench/bench.h @@ -4,6 +4,7 @@ extern int bench_sched_messaging(int argc, const char **argv, const char *prefix); extern int bench_sched_pipe(int argc, const char **argv, const char *prefix); extern int bench_mem_memcpy(int argc, const char **argv, const char *prefix __used); +extern int bench_mem_memset(int argc, const char **argv, const char *prefix); #define BENCH_FORMAT_DEFAULT_STR "default" #define BENCH_FORMAT_DEFAULT 0 diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm-def.h b/tools/perf/bench/mem-memcpy-x86-64-asm-def.h index d588b87696fc..d66ab799b35f 100644 --- a/tools/perf/bench/mem-memcpy-x86-64-asm-def.h +++ b/tools/perf/bench/mem-memcpy-x86-64-asm-def.h @@ -2,3 +2,11 @@ MEMCPY_FN(__memcpy, "x86-64-unrolled", "unrolled memcpy() in arch/x86/lib/memcpy_64.S") + +MEMCPY_FN(memcpy_c, + "x86-64-movsq", + "movsq-based memcpy() in arch/x86/lib/memcpy_64.S") + +MEMCPY_FN(memcpy_c_e, + "x86-64-movsb", + "movsb-based memcpy() in arch/x86/lib/memcpy_64.S") diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm.S b/tools/perf/bench/mem-memcpy-x86-64-asm.S index 185a96d66dd1..fcd9cf00600a 100644 --- a/tools/perf/bench/mem-memcpy-x86-64-asm.S +++ b/tools/perf/bench/mem-memcpy-x86-64-asm.S @@ -1,4 +1,8 @@ - +#define memcpy MEMCPY /* don't hide glibc's memcpy() */ +#define altinstr_replacement text +#define globl p2align 4; .globl +#define Lmemcpy_c globl memcpy_c; memcpy_c +#define Lmemcpy_c_e globl memcpy_c_e; memcpy_c_e #include "../../../arch/x86/lib/memcpy_64.S" /* * We need to provide note.GNU-stack section, saying that we want diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index db82021f4b91..71557225bf92 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c @@ -5,7 +5,6 @@ * * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> */ -#include <ctype.h> #include "../perf.h" #include "../util/util.h" @@ -24,6 +23,7 @@ static const char *length_str = "1MB"; static const char *routine = "default"; +static int iterations = 1; static bool use_clock; static int clock_fd; static bool only_prefault; @@ -35,6 +35,8 @@ static const struct option options[] = { "available unit: B, MB, GB (upper and lower)"), OPT_STRING('r', "routine", &routine, "default", "Specify routine to copy"), + OPT_INTEGER('i', "iterations", &iterations, + "repeat memcpy() invocation this number of times"), OPT_BOOLEAN('c', "clock", &use_clock, "Use CPU clock for measuring"), OPT_BOOLEAN('o', "only-prefault", &only_prefault, @@ -121,6 +123,7 @@ static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault) { u64 clock_start = 0ULL, clock_end = 0ULL; void *src = NULL, *dst = NULL; + int i; alloc_mem(&src, &dst, len); @@ -128,7 +131,8 @@ static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault) fn(dst, src, len); clock_start = get_clock(); - fn(dst, src, len); + for (i = 0; i < iterations; ++i) + fn(dst, src, len); clock_end = get_clock(); free(src); @@ -140,6 +144,7 @@ static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault) { struct timeval tv_start, tv_end, tv_diff; void *src = NULL, *dst = NULL; + int i; alloc_mem(&src, &dst, len); @@ -147,7 +152,8 @@ static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault) fn(dst, src, len); BUG_ON(gettimeofday(&tv_start, NULL)); - fn(dst, src, len); + for (i = 0; i < iterations; ++i) + fn(dst, src, len); BUG_ON(gettimeofday(&tv_end, NULL)); timersub(&tv_end, &tv_start, &tv_diff); diff --git a/tools/perf/bench/mem-memset-arch.h b/tools/perf/bench/mem-memset-arch.h new file mode 100644 index 000000000000..a040fa77665b --- /dev/null +++ b/tools/perf/bench/mem-memset-arch.h @@ -0,0 +1,12 @@ + +#ifdef ARCH_X86_64 + +#define MEMSET_FN(fn, name, desc) \ + extern void *fn(void *, int, size_t); + +#include "mem-memset-x86-64-asm-def.h" + +#undef MEMSET_FN + +#endif + diff --git a/tools/perf/bench/mem-memset-x86-64-asm-def.h b/tools/perf/bench/mem-memset-x86-64-asm-def.h new file mode 100644 index 000000000000..a71dff97c1f5 --- /dev/null +++ b/tools/perf/bench/mem-memset-x86-64-asm-def.h @@ -0,0 +1,12 @@ + +MEMSET_FN(__memset, + "x86-64-unrolled", + "unrolled memset() in arch/x86/lib/memset_64.S") + +MEMSET_FN(memset_c, + "x86-64-stosq", + "movsq-based memset() in arch/x86/lib/memset_64.S") + +MEMSET_FN(memset_c_e, + "x86-64-stosb", + "movsb-based memset() in arch/x86/lib/memset_64.S") diff --git a/tools/perf/bench/mem-memset-x86-64-asm.S b/tools/perf/bench/mem-memset-x86-64-asm.S new file mode 100644 index 000000000000..cb9217063776 --- /dev/null +++ b/tools/perf/bench/mem-memset-x86-64-asm.S @@ -0,0 +1,6 @@ +#define memset MEMSET /* don't hide glibc's memset() */ +#define altinstr_replacement text +#define globl p2align 4; .globl +#define Lmemset_c globl memset_c; memset_c +#define Lmemset_c_e globl memset_c_e; memset_c_e +#include "../../../arch/x86/lib/memset_64.S" diff --git a/tools/perf/bench/mem-memset.c b/tools/perf/bench/mem-memset.c new file mode 100644 index 000000000000..e9079185bd72 --- /dev/null +++ b/tools/perf/bench/mem-memset.c @@ -0,0 +1,297 @@ +/* + * mem-memset.c + * + * memset: Simple memory set in various ways + * + * Trivial clone of mem-memcpy.c. + */ + +#include "../perf.h" +#include "../util/util.h" +#include "../util/parse-options.h" +#include "../util/header.h" +#include "bench.h" +#include "mem-memset-arch.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <errno.h> + +#define K 1024 + +static const char *length_str = "1MB"; +static const char *routine = "default"; +static int iterations = 1; +static bool use_clock; +static int clock_fd; +static bool only_prefault; +static bool no_prefault; + +static const struct option options[] = { + OPT_STRING('l', "length", &length_str, "1MB", + "Specify length of memory to copy. " + "available unit: B, MB, GB (upper and lower)"), + OPT_STRING('r', "routine", &routine, "default", + "Specify routine to copy"), + OPT_INTEGER('i', "iterations", &iterations, + "repeat memset() invocation this number of times"), + OPT_BOOLEAN('c', "clock", &use_clock, + "Use CPU clock for measuring"), + OPT_BOOLEAN('o', "only-prefault", &only_prefault, + "Show only the result with page faults before memset()"), + OPT_BOOLEAN('n', "no-prefault", &no_prefault, + "Show only the result without page faults before memset()"), + OPT_END() +}; + +typedef void *(*memset_t)(void *, int, size_t); + +struct routine { + const char *name; + const char *desc; + memset_t fn; +}; + +static const struct routine routines[] = { + { "default", + "Default memset() provided by glibc", + memset }, +#ifdef ARCH_X86_64 + +#define MEMSET_FN(fn, name, desc) { name, desc, fn }, +#include "mem-memset-x86-64-asm-def.h" +#undef MEMSET_FN + +#endif + + { NULL, + NULL, + NULL } +}; + +static const char * const bench_mem_memset_usage[] = { + "perf bench mem memset <options>", + NULL +}; + +static struct perf_event_attr clock_attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES +}; + +static void init_clock(void) +{ + clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0); + + if (clock_fd < 0 && errno == ENOSYS) + die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); + else + BUG_ON(clock_fd < 0); +} + +static u64 get_clock(void) +{ + int ret; + u64 clk; + + ret = read(clock_fd, &clk, sizeof(u64)); + BUG_ON(ret != sizeof(u64)); + + return clk; +} + +static double timeval2double(struct timeval *ts) +{ + return (double)ts->tv_sec + + (double)ts->tv_usec / (double)1000000; +} + +static void alloc_mem(void **dst, size_t length) +{ + *dst = zalloc(length); + if (!dst) + die("memory allocation failed - maybe length is too large?\n"); +} + +static u64 do_memset_clock(memset_t fn, size_t len, bool prefault) +{ + u64 clock_start = 0ULL, clock_end = 0ULL; + void *dst = NULL; + int i; + + alloc_mem(&dst, len); + + if (prefault) + fn(dst, -1, len); + + clock_start = get_clock(); + for (i = 0; i < iterations; ++i) + fn(dst, i, len); + clock_end = get_clock(); + + free(dst); + return clock_end - clock_start; +} + +static double do_memset_gettimeofday(memset_t fn, size_t len, bool prefault) +{ + struct timeval tv_start, tv_end, tv_diff; + void *dst = NULL; + int i; + + alloc_mem(&dst, len); + + if (prefault) + fn(dst, -1, len); + + BUG_ON(gettimeofday(&tv_start, NULL)); + for (i = 0; i < iterations; ++i) + fn(dst, i, len); + BUG_ON(gettimeofday(&tv_end, NULL)); + + timersub(&tv_end, &tv_start, &tv_diff); + + free(dst); + return (double)((double)len / timeval2double(&tv_diff)); +} + +#define pf (no_prefault ? 0 : 1) + +#define print_bps(x) do { \ + if (x < K) \ + printf(" %14lf B/Sec", x); \ + else if (x < K * K) \ + printf(" %14lfd KB/Sec", x / K); \ + else if (x < K * K * K) \ + printf(" %14lf MB/Sec", x / K / K); \ + else \ + printf(" %14lf GB/Sec", x / K / K / K); \ + } while (0) + +int bench_mem_memset(int argc, const char **argv, + const char *prefix __used) +{ + int i; + size_t len; + double result_bps[2]; + u64 result_clock[2]; + + argc = parse_options(argc, argv, options, + bench_mem_memset_usage, 0); + + if (use_clock) + init_clock(); + + len = (size_t)perf_atoll((char *)length_str); + + result_clock[0] = result_clock[1] = 0ULL; + result_bps[0] = result_bps[1] = 0.0; + + if ((s64)len <= 0) { + fprintf(stderr, "Invalid length:%s\n", length_str); + return 1; + } + + /* same to without specifying either of prefault and no-prefault */ + if (only_prefault && no_prefault) + only_prefault = no_prefault = false; + + for (i = 0; routines[i].name; i++) { + if (!strcmp(routines[i].name, routine)) + break; + } + if (!routines[i].name) { + printf("Unknown routine:%s\n", routine); + printf("Available routines...\n"); + for (i = 0; routines[i].name; i++) { + printf("\t%s ... %s\n", + routines[i].name, routines[i].desc); + } + return 1; + } + + if (bench_format == BENCH_FORMAT_DEFAULT) + printf("# Copying %s Bytes ...\n\n", length_str); + + if (!only_prefault && !no_prefault) { + /* show both of results */ + if (use_clock) { + result_clock[0] = + do_memset_clock(routines[i].fn, len, false); + result_clock[1] = + do_memset_clock(routines[i].fn, len, true); + } else { + result_bps[0] = + do_memset_gettimeofday(routines[i].fn, + len, false); + result_bps[1] = + do_memset_gettimeofday(routines[i].fn, + len, true); + } + } else { + if (use_clock) { + result_clock[pf] = + do_memset_clock(routines[i].fn, + len, only_prefault); + } else { + result_bps[pf] = + do_memset_gettimeofday(routines[i].fn, + len, only_prefault); + } + } + + switch (bench_format) { + case BENCH_FORMAT_DEFAULT: + if (!only_prefault && !no_prefault) { + if (use_clock) { + printf(" %14lf Clock/Byte\n", + (double)result_clock[0] + / (double)len); + printf(" %14lf Clock/Byte (with prefault)\n ", + (double)result_clock[1] + / (double)len); + } else { + print_bps(result_bps[0]); + printf("\n"); + print_bps(result_bps[1]); + printf(" (with prefault)\n"); + } + } else { + if (use_clock) { + printf(" %14lf Clock/Byte", + (double)result_clock[pf] + / (double)len); + } else + print_bps(result_bps[pf]); + + printf("%s\n", only_prefault ? " (with prefault)" : ""); + } + break; + case BENCH_FORMAT_SIMPLE: + if (!only_prefault && !no_prefault) { + if (use_clock) { + printf("%lf %lf\n", + (double)result_clock[0] / (double)len, + (double)result_clock[1] / (double)len); + } else { + printf("%lf %lf\n", + result_bps[0], result_bps[1]); + } + } else { + if (use_clock) { + printf("%lf\n", (double)result_clock[pf] + / (double)len); + } else + printf("%lf\n", result_bps[pf]); + } + break; + default: + /* reaching this means there's some disaster: */ + die("unknown format: %d\n", bench_format); + break; + } + + return 0; +} diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index fcb96269852a..b0e74ab2d7a2 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c @@ -52,6 +52,9 @@ static struct bench_suite mem_suites[] = { { "memcpy", "Simple memory copy in various ways", bench_mem_memcpy }, + { "memset", + "Simple memory set in various ways", + bench_mem_memset }, suite_all, { NULL, NULL, diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 2296c391d0f5..12c814838993 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -922,12 +922,12 @@ static const struct option info_options[] = { OPT_BOOLEAN('t', "threads", &info_threads, "dump thread list in perf.data"), OPT_BOOLEAN('m', "map", &info_map, - "map of lock instances (name:address table)"), + "map of lock instances (address:name table)"), OPT_END() }; static const char * const lock_usage[] = { - "perf lock [<options>] {record|trace|report}", + "perf lock [<options>] {record|report|script|info}", NULL }; diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index fb8566181f27..4935c09dd5b5 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c @@ -58,7 +58,7 @@ static struct { struct perf_probe_event events[MAX_PROBES]; struct strlist *dellist; struct line_range line_range; - const char *target_module; + const char *target; int max_probe_points; struct strfilter *filter; } params; @@ -246,7 +246,7 @@ static const struct option options[] = { "file", "vmlinux pathname"), OPT_STRING('s', "source", &symbol_conf.source_prefix, "directory", "path to kernel source"), - OPT_STRING('m', "module", ¶ms.target_module, + OPT_STRING('m', "module", ¶ms.target, "modname|path", "target module name (for online) or path (for offline)"), #endif @@ -333,7 +333,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) if (!params.filter) params.filter = strfilter__new(DEFAULT_FUNC_FILTER, NULL); - ret = show_available_funcs(params.target_module, + ret = show_available_funcs(params.target, params.filter); strfilter__delete(params.filter); if (ret < 0) @@ -354,7 +354,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) usage_with_options(probe_usage, options); } - ret = show_line_range(¶ms.line_range, params.target_module); + ret = show_line_range(¶ms.line_range, params.target); if (ret < 0) pr_err(" Error: Failed to show lines. (%d)\n", ret); return ret; @@ -371,7 +371,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ret = show_available_vars(params.events, params.nevents, params.max_probe_points, - params.target_module, + params.target, params.filter, params.show_ext_vars); strfilter__delete(params.filter); @@ -393,7 +393,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) if (params.nevents) { ret = add_perf_probe_events(params.events, params.nevents, params.max_probe_points, - params.target_module, + params.target, params.force_add); if (ret < 0) { pr_err(" Error: Failed to add events. (%d)\n", ret); diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 0abfb18b911f..f8d9a545dd6e 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -44,6 +44,7 @@ struct perf_record { struct perf_evlist *evlist; struct perf_session *session; const char *progname; + const char *uid_str; int output; unsigned int page_size; int realtime_prio; @@ -385,7 +386,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) { struct stat st; int flags; - int err, output; + int err, output, feat; unsigned long waking = 0; const bool forks = argc > 0; struct machine *machine; @@ -452,8 +453,14 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) rec->session = session; - if (!rec->no_buildid) - perf_header__set_feat(&session->header, HEADER_BUILD_ID); + for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) + perf_header__set_feat(&session->header, feat); + + if (rec->no_buildid) + perf_header__clear_feat(&session->header, HEADER_BUILD_ID); + + if (!have_tracepoints(&evsel_list->entries)) + perf_header__clear_feat(&session->header, HEADER_TRACE_INFO); if (!rec->file_new) { err = perf_session__read_header(session, output); @@ -461,22 +468,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) goto out_delete_session; } - if (have_tracepoints(&evsel_list->entries)) - perf_header__set_feat(&session->header, HEADER_TRACE_INFO); - - perf_header__set_feat(&session->header, HEADER_HOSTNAME); - perf_header__set_feat(&session->header, HEADER_OSRELEASE); - perf_header__set_feat(&session->header, HEADER_ARCH); - perf_header__set_feat(&session->header, HEADER_CPUDESC); - perf_header__set_feat(&session->header, HEADER_NRCPUS); - perf_header__set_feat(&session->header, HEADER_EVENT_DESC); - perf_header__set_feat(&session->header, HEADER_CMDLINE); - perf_header__set_feat(&session->header, HEADER_VERSION); - perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); - perf_header__set_feat(&session->header, HEADER_TOTAL_MEM); - perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY); - perf_header__set_feat(&session->header, HEADER_CPUID); - if (forks) { err = perf_evlist__prepare_workload(evsel_list, opts, argv); if (err < 0) { @@ -727,6 +718,7 @@ const struct option record_options[] = { OPT_CALLBACK('G', "cgroup", &record.evlist, "name", "monitor event in cgroup name only", parse_cgroups), + OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"), OPT_END() }; @@ -748,7 +740,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) argc = parse_options(argc, argv, record_options, record_usage, PARSE_OPT_STOP_AT_NON_OPTION); if (!argc && rec->opts.target_pid == -1 && rec->opts.target_tid == -1 && - !rec->opts.system_wide && !rec->opts.cpu_list) + !rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str) usage_with_options(record_usage, record_options); if (rec->force && rec->append_file) { @@ -788,11 +780,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) goto out_symbol_exit; } + rec->opts.uid = parse_target_uid(rec->uid_str, rec->opts.target_tid, + rec->opts.target_pid); + if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1) + goto out_free_fd; + if (rec->opts.target_pid != -1) rec->opts.target_tid = rec->opts.target_pid; if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid, - rec->opts.target_tid, rec->opts.cpu_list) < 0) + rec->opts.target_tid, rec->opts.uid, + rec->opts.cpu_list) < 0) usage_with_options(record_usage, record_options); list_for_each_entry(pos, &evsel_list->entries, node) { diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index bb68ddf257b7..d4ce733b9eba 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -40,6 +40,7 @@ enum perf_output_field { PERF_OUTPUT_SYM = 1U << 8, PERF_OUTPUT_DSO = 1U << 9, PERF_OUTPUT_ADDR = 1U << 10, + PERF_OUTPUT_SYMOFFSET = 1U << 11, }; struct output_option { @@ -57,6 +58,7 @@ struct output_option { {.str = "sym", .field = PERF_OUTPUT_SYM}, {.str = "dso", .field = PERF_OUTPUT_DSO}, {.str = "addr", .field = PERF_OUTPUT_ADDR}, + {.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET}, }; /* default set to maintain compatibility with current format */ @@ -193,6 +195,11 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, "to symbols.\n"); return -EINVAL; } + if (PRINT_FIELD(SYMOFFSET) && !PRINT_FIELD(SYM)) { + pr_err("Display of offsets requested but symbol is not" + "selected.\n"); + return -EINVAL; + } if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { pr_err("Display of DSO requested but neither sample IP nor " "sample address\nis selected. Hence, no addresses to convert " @@ -300,10 +307,17 @@ static void print_sample_start(struct perf_sample *sample, } else evname = __event_name(attr->type, attr->config); - printf("%s: ", evname ? evname : "(unknown)"); + printf("%s: ", evname ? evname : "[unknown]"); } } +static bool is_bts_event(struct perf_event_attr *attr) +{ + return ((attr->type == PERF_TYPE_HARDWARE) && + (attr->config & PERF_COUNT_HW_BRANCH_INSTRUCTIONS) && + (attr->sample_period == 1)); +} + static bool sample_addr_correlates_sym(struct perf_event_attr *attr) { if ((attr->type == PERF_TYPE_SOFTWARE) && @@ -312,6 +326,9 @@ static bool sample_addr_correlates_sym(struct perf_event_attr *attr) (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ))) return true; + if (is_bts_event(attr)) + return true; + return false; } @@ -323,7 +340,6 @@ static void print_sample_addr(union perf_event *event, { struct addr_location al; u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - const char *symname, *dsoname; printf("%16" PRIx64, sample->addr); @@ -343,22 +359,46 @@ static void print_sample_addr(union perf_event *event, al.sym = map__find_symbol(al.map, al.addr, NULL); if (PRINT_FIELD(SYM)) { - if (al.sym && al.sym->name) - symname = al.sym->name; + printf(" "); + if (PRINT_FIELD(SYMOFFSET)) + symbol__fprintf_symname_offs(al.sym, &al, stdout); else - symname = ""; - - printf(" %16s", symname); + symbol__fprintf_symname(al.sym, stdout); } if (PRINT_FIELD(DSO)) { - if (al.map && al.map->dso && al.map->dso->name) - dsoname = al.map->dso->name; - else - dsoname = ""; + printf(" ("); + map__fprintf_dsoname(al.map, stdout); + printf(")"); + } +} - printf(" (%s)", dsoname); +static void print_sample_bts(union perf_event *event, + struct perf_sample *sample, + struct perf_evsel *evsel, + struct machine *machine, + struct thread *thread) +{ + struct perf_event_attr *attr = &evsel->attr; + + /* print branch_from information */ + if (PRINT_FIELD(IP)) { + if (!symbol_conf.use_callchain) + printf(" "); + else + printf("\n"); + perf_event__print_ip(event, sample, machine, evsel, + PRINT_FIELD(SYM), PRINT_FIELD(DSO), + PRINT_FIELD(SYMOFFSET)); } + + printf(" => "); + + /* print branch_to information */ + if (PRINT_FIELD(ADDR)) + print_sample_addr(event, sample, machine, thread, attr); + + printf("\n"); } static void process_event(union perf_event *event __unused, @@ -374,6 +414,11 @@ static void process_event(union perf_event *event __unused, print_sample_start(sample, thread, attr); + if (is_bts_event(attr)) { + print_sample_bts(event, sample, evsel, machine, thread); + return; + } + if (PRINT_FIELD(TRACE)) print_trace_event(sample->cpu, sample->raw_data, sample->raw_size); @@ -387,7 +432,8 @@ static void process_event(union perf_event *event __unused, else printf("\n"); perf_event__print_ip(event, sample, machine, evsel, - PRINT_FIELD(SYM), PRINT_FIELD(DSO)); + PRINT_FIELD(SYM), PRINT_FIELD(DSO), + PRINT_FIELD(SYMOFFSET)); } printf("\n"); @@ -1097,7 +1143,10 @@ static const struct option options[] = { OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", "Look for files with symbols relative to this directory"), OPT_CALLBACK('f', "fields", NULL, "str", - "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr", + "comma separated output fields prepend with 'type:'. " + "Valid types: hw,sw,trace,raw. " + "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," + "addr,symoff", parse_output_fields), OPT_BOOLEAN('a', "all-cpus", &system_wide, "system-wide collection from all CPUs"), @@ -1106,6 +1155,9 @@ static const struct option options[] = { "only display events for these comms"), OPT_BOOLEAN('I', "show-info", &show_full_info, "display extended information from perf.data file"), + OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, + "Show the path of [kernel.kallsyms]"), + OPT_END() }; diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index f5d2a63eba66..459b8620a5d9 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -1201,7 +1201,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) if (target_pid != -1) target_tid = target_pid; - evsel_list->threads = thread_map__new(target_pid, target_tid); + evsel_list->threads = thread_map__new(target_pid, target_tid, UINT_MAX); if (evsel_list->threads == NULL) { pr_err("Problems finding threads of monitor\n"); usage_with_options(stat_usage, options); diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 3854e869dce1..70c4eb2bdf72 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c @@ -15,6 +15,8 @@ #include "util/thread_map.h" #include "../../include/linux/hw_breakpoint.h" +#include <sys/mman.h> + static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym) { bool *visited = symbol__priv(sym); @@ -276,7 +278,7 @@ static int test__open_syscall_event(void) return -1; } - threads = thread_map__new(-1, getpid()); + threads = thread_map__new(-1, getpid(), UINT_MAX); if (threads == NULL) { pr_debug("thread_map__new\n"); return -1; @@ -342,7 +344,7 @@ static int test__open_syscall_event_on_all_cpus(void) return -1; } - threads = thread_map__new(-1, getpid()); + threads = thread_map__new(-1, getpid(), UINT_MAX); if (threads == NULL) { pr_debug("thread_map__new\n"); return -1; @@ -490,7 +492,7 @@ static int test__basic_mmap(void) expected_nr_events[i] = random() % 257; } - threads = thread_map__new(-1, getpid()); + threads = thread_map__new(-1, getpid(), UINT_MAX); if (threads == NULL) { pr_debug("thread_map__new\n"); return -1; @@ -1054,7 +1056,7 @@ static int test__PERF_RECORD(void) * we're monitoring, the one forked there. */ err = perf_evlist__create_maps(evlist, opts.target_pid, - opts.target_tid, opts.cpu_list); + opts.target_tid, UINT_MAX, opts.cpu_list); if (err < 0) { pr_debug("Not enough memory to create thread/cpu maps\n"); goto out_delete_evlist; @@ -1296,6 +1298,173 @@ out: return (err < 0 || errs > 0) ? -1 : 0; } + +#if defined(__x86_64__) || defined(__i386__) + +#define barrier() asm volatile("" ::: "memory") + +static u64 rdpmc(unsigned int counter) +{ + unsigned int low, high; + + asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); + + return low | ((u64)high) << 32; +} + +static u64 rdtsc(void) +{ + unsigned int low, high; + + asm volatile("rdtsc" : "=a" (low), "=d" (high)); + + return low | ((u64)high) << 32; +} + +static u64 mmap_read_self(void *addr) +{ + struct perf_event_mmap_page *pc = addr; + u32 seq, idx, time_mult = 0, time_shift = 0; + u64 count, cyc = 0, time_offset = 0, enabled, running, delta; + + do { + seq = pc->lock; + barrier(); + + enabled = pc->time_enabled; + running = pc->time_running; + + if (enabled != running) { + cyc = rdtsc(); + time_mult = pc->time_mult; + time_shift = pc->time_shift; + time_offset = pc->time_offset; + } + + idx = pc->index; + count = pc->offset; + if (idx) + count += rdpmc(idx - 1); + + barrier(); + } while (pc->lock != seq); + + if (enabled != running) { + u64 quot, rem; + + quot = (cyc >> time_shift); + rem = cyc & ((1 << time_shift) - 1); + delta = time_offset + quot * time_mult + + ((rem * time_mult) >> time_shift); + + enabled += delta; + if (idx) + running += delta; + + quot = count / running; + rem = count % running; + count = quot * enabled + (rem * enabled) / running; + } + + return count; +} + +/* + * If the RDPMC instruction faults then signal this back to the test parent task: + */ +static void segfault_handler(int sig __used, siginfo_t *info __used, void *uc __used) +{ + exit(-1); +} + +static int __test__rdpmc(void) +{ + long page_size = sysconf(_SC_PAGE_SIZE); + volatile int tmp = 0; + u64 i, loops = 1000; + int n; + int fd; + void *addr; + struct perf_event_attr attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_INSTRUCTIONS, + .exclude_kernel = 1, + }; + u64 delta_sum = 0; + struct sigaction sa; + + sigfillset(&sa.sa_mask); + sa.sa_sigaction = segfault_handler; + sigaction(SIGSEGV, &sa, NULL); + + fprintf(stderr, "\n\n"); + + fd = sys_perf_event_open(&attr, 0, -1, -1, 0); + if (fd < 0) { + die("Error: sys_perf_event_open() syscall returned " + "with %d (%s)\n", fd, strerror(errno)); + } + + addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); + if (addr == (void *)(-1)) { + die("Error: mmap() syscall returned " + "with (%s)\n", strerror(errno)); + } + + for (n = 0; n < 6; n++) { + u64 stamp, now, delta; + + stamp = mmap_read_self(addr); + + for (i = 0; i < loops; i++) + tmp++; + + now = mmap_read_self(addr); + loops *= 10; + + delta = now - stamp; + fprintf(stderr, "%14d: %14Lu\n", n, (long long)delta); + + delta_sum += delta; + } + + munmap(addr, page_size); + close(fd); + + fprintf(stderr, " "); + + if (!delta_sum) + return -1; + + return 0; +} + +static int test__rdpmc(void) +{ + int status = 0; + int wret = 0; + int ret; + int pid; + + pid = fork(); + if (pid < 0) + return -1; + + if (!pid) { + ret = __test__rdpmc(); + + exit(ret); + } + + wret = waitpid(pid, &status, 0); + if (wret < 0 || status) + return -1; + + return 0; +} + +#endif + static struct test { const char *desc; int (*func)(void); @@ -1320,6 +1489,12 @@ static struct test { .desc = "parse events tests", .func = test__parse_events, }, +#if defined(__x86_64__) || defined(__i386__) + { + .desc = "x86 rdpmc test", + .func = test__rdpmc, + }, +#endif { .desc = "Validate PERF_RECORD_* events & perf_sample fields", .func = test__PERF_RECORD, @@ -1412,7 +1587,5 @@ int cmd_test(int argc, const char **argv, const char *prefix __used) if (symbol__init() < 0) return -1; - setup_pager(); - return __cmd_test(argc, argv); } diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index dd162aa24baa..d869b214ada2 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -64,7 +64,6 @@ #include <linux/unistd.h> #include <linux/types.h> - void get_term_dimensions(struct winsize *ws) { char *s = getenv("LINES"); @@ -544,10 +543,20 @@ static void perf_top__sort_new_samples(void *arg) static void *display_thread_tui(void *arg) { + struct perf_evsel *pos; struct perf_top *top = arg; const char *help = "For a higher level overview, try: perf top --sort comm,dso"; perf_top__sort_new_samples(top); + + /* + * Initialize the uid_filter_str, in the future the TUI will allow + * Zooming in/out UIDs. For now juse use whatever the user passed + * via --uid. + */ + list_for_each_entry(pos, &top->evlist->entries, node) + pos->hists.uid_filter_str = top->uid_str; + perf_evlist__tui_browse_hists(top->evlist, help, perf_top__sort_new_samples, top, top->delay_secs); @@ -956,7 +965,7 @@ static int __cmd_top(struct perf_top *top) if (ret) goto out_delete; - if (top->target_tid != -1) + if (top->target_tid != -1 || top->uid != UINT_MAX) perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, perf_event__process, &top->session->host_machine); @@ -1096,6 +1105,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) .delay_secs = 2, .target_pid = -1, .target_tid = -1, + .uid = UINT_MAX, .freq = 1000, /* 1 KHz */ .sample_id_all_avail = true, .mmap_pages = 128, @@ -1169,6 +1179,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) "Display raw encoding of assembly instructions (default)"), OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", "Specify disassembler style (e.g. -M intel for intel syntax)"), + OPT_STRING('u', "uid", &top.uid_str, "user", "user to profile"), OPT_END() }; @@ -1194,6 +1205,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) setup_browser(false); + top.uid = parse_target_uid(top.uid_str, top.target_tid, top.target_pid); + if (top.uid_str != NULL && top.uid == UINT_MAX - 1) + goto out_delete_evlist; + /* CPU and PID are mutually exclusive */ if (top.target_tid > 0 && top.cpu_list) { printf("WARNING: PID switch overriding CPU\n"); @@ -1205,7 +1220,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) top.target_tid = top.target_pid; if (perf_evlist__create_maps(top.evlist, top.target_pid, - top.target_tid, top.cpu_list) < 0) + top.target_tid, top.uid, top.cpu_list) < 0) usage_with_options(top_usage, options); if (!top.evlist->nr_entries && @@ -1269,6 +1284,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) status = __cmd_top(&top); +out_delete_evlist: perf_evlist__delete(top.evlist); return status; diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 64f8bee31ced..92af1688bae4 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -188,6 +188,7 @@ void pthread__unblock_sigwinch(void); struct perf_record_opts { pid_t target_pid; pid_t target_tid; + uid_t uid; bool call_graph; bool group; bool inherit_stat; diff --git a/tools/perf/python/twatch.py b/tools/perf/python/twatch.py index df638c438a9f..b11cca584238 100755 --- a/tools/perf/python/twatch.py +++ b/tools/perf/python/twatch.py @@ -19,7 +19,7 @@ def main(): cpus = perf.cpu_map() threads = perf.thread_map() evsel = perf.evsel(task = 1, comm = 1, mmap = 0, - wakeup_events = 1, sample_period = 1, + wakeup_events = 1, watermark = 1, sample_id_all = 1, sample_type = perf.SAMPLE_PERIOD | perf.SAMPLE_TID | perf.SAMPLE_CPU | perf.SAMPLE_TID) evsel.open(cpus = cpus, threads = threads); diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 6893eec693ab..adc72f09914d 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -166,6 +166,17 @@ out: return cpus; } +size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp) +{ + int i; + size_t printed = fprintf(fp, "%d cpu%s: ", + map->nr, map->nr > 1 ? "s" : ""); + for (i = 0; i < map->nr; ++i) + printed += fprintf(fp, "%s%d", i ? ", " : "", map->map[i]); + + return printed + fprintf(fp, "\n"); +} + struct cpu_map *cpu_map__dummy_new(void) { struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int)); diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 072c0a374794..c41518573c6a 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -1,6 +1,8 @@ #ifndef __PERF_CPUMAP_H #define __PERF_CPUMAP_H +#include <stdio.h> + struct cpu_map { int nr; int map[]; @@ -10,4 +12,6 @@ struct cpu_map *cpu_map__new(const char *cpu_list); struct cpu_map *cpu_map__dummy_new(void); void cpu_map__delete(struct cpu_map *map); +size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); + #endif /* __PERF_CPUMAP_H */ diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 3f16e08a5c8d..a57a8cfc5d90 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -97,9 +97,9 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) ++evlist->nr_entries; } -static void perf_evlist__splice_list_tail(struct perf_evlist *evlist, - struct list_head *list, - int nr_entries) +void perf_evlist__splice_list_tail(struct perf_evlist *evlist, + struct list_head *list, + int nr_entries) { list_splice_tail(list, &evlist->entries); evlist->nr_entries += nr_entries; @@ -594,14 +594,14 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, } int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, - pid_t target_tid, const char *cpu_list) + pid_t target_tid, uid_t uid, const char *cpu_list) { - evlist->threads = thread_map__new(target_pid, target_tid); + evlist->threads = thread_map__new(target_pid, target_tid, uid); if (evlist->threads == NULL) return -1; - if (cpu_list == NULL && target_tid != -1) + if (uid != UINT_MAX || (cpu_list == NULL && target_tid != -1)) evlist->cpus = cpu_map__dummy_new(); else evlist->cpus = cpu_map__new(cpu_list); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 8922aeed0467..1b4282be8fe7 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -107,7 +107,7 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, } int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, - pid_t target_tid, const char *cpu_list); + pid_t tid, uid_t uid, const char *cpu_list); void perf_evlist__delete_maps(struct perf_evlist *evlist); int perf_evlist__set_filters(struct perf_evlist *evlist); @@ -117,4 +117,9 @@ u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist); bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist); bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist); + +void perf_evlist__splice_list_tail(struct perf_evlist *evlist, + struct list_head *list, + int nr_entries); + #endif /* __PERF_EVLIST_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 7132ee834e0e..9a11f9edac12 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -536,7 +536,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, } if (type & PERF_SAMPLE_READ) { - fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); + fprintf(stderr, "PERF_SAMPLE_READ is unsupported for now\n"); return -1; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index ac4ec956024e..e68f617d082f 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -11,6 +11,7 @@ enum { HEADER_RESERVED = 0, /* always cleared */ + HEADER_FIRST_FEATURE = 1, HEADER_TRACE_INFO = 1, HEADER_BUILD_ID, diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index f55f0a8d1f81..0d486135d10f 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -55,6 +55,7 @@ struct hists { u64 nr_entries; const struct thread *thread_filter; const struct dso *dso_filter; + const char *uid_filter_str; pthread_mutex_t lock; struct events_stats stats; u64 event_stream; diff --git a/tools/perf/util/include/asm/dwarf2.h b/tools/perf/util/include/asm/dwarf2.h index bb4198e7837a..afe38199e922 100644 --- a/tools/perf/util/include/asm/dwarf2.h +++ b/tools/perf/util/include/asm/dwarf2.h @@ -2,10 +2,12 @@ #ifndef PERF_DWARF2_H #define PERF_DWARF2_H -/* dwarf2.h ... dummy header file for including arch/x86/lib/memcpy_64.S */ +/* dwarf2.h ... dummy header file for including arch/x86/lib/mem{cpy,set}_64.S */ #define CFI_STARTPROC #define CFI_ENDPROC +#define CFI_REMEMBER_STATE +#define CFI_RESTORE_STATE #endif /* PERF_DWARF2_H */ diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 316aa0ab7122..dea6d1c1a954 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -212,6 +212,21 @@ size_t map__fprintf(struct map *self, FILE *fp) self->start, self->end, self->pgoff, self->dso->name); } +size_t map__fprintf_dsoname(struct map *map, FILE *fp) +{ + const char *dsoname; + + if (map && map->dso && (map->dso->name || map->dso->long_name)) { + if (symbol_conf.show_kernel_path && map->dso->long_name) + dsoname = map->dso->long_name; + else if (map->dso->name) + dsoname = map->dso->name; + } else + dsoname = "[unknown]"; + + return fprintf(fp, "%s", dsoname); +} + /* * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. * map->dso->adjust_symbols==1 for ET_EXEC-like cases. diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 2b8017f8a930..b100c20b7f94 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -118,6 +118,7 @@ void map__delete(struct map *self); struct map *map__clone(struct map *self); int map__overlap(struct map *l, struct map *r); size_t map__fprintf(struct map *self, FILE *fp); +size_t map__fprintf_dsoname(struct map *map, FILE *fp); int map__load(struct map *self, symbol_filter_t filter); struct symbol *map__find_symbol(struct map *self, diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 29cb65459811..c1a513e56764 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -273,10 +273,10 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, /* Try to find perf_probe_event with debuginfo */ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs, - int max_tevs, const char *module) + int max_tevs, const char *target) { bool need_dwarf = perf_probe_event_need_dwarf(pev); - struct debuginfo *dinfo = open_debuginfo(module); + struct debuginfo *dinfo = open_debuginfo(target); int ntevs, ret = 0; if (!dinfo) { @@ -295,9 +295,9 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, if (ntevs > 0) { /* Succeeded to find trace events */ pr_debug("find %d probe_trace_events.\n", ntevs); - if (module) + if (target) ret = add_module_to_probe_trace_events(*tevs, ntevs, - module); + target); return ret < 0 ? ret : ntevs; } @@ -1729,7 +1729,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, } ret = 0; - printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); + printf("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); for (i = 0; i < ntevs; i++) { tev = &tevs[i]; if (pev->event) @@ -1784,7 +1784,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, if (ret >= 0) { /* Show how to use the event. */ - printf("\nYou can now use it on all perf tools, such as:\n\n"); + printf("\nYou can now use it in all perf tools, such as:\n\n"); printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, tev->event); } @@ -1796,14 +1796,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, static int convert_to_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs, - int max_tevs, const char *module) + int max_tevs, const char *target) { struct symbol *sym; int ret = 0, i; struct probe_trace_event *tev; /* Convert perf_probe_event with debuginfo */ - ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); + ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); if (ret != 0) return ret; /* Found in debuginfo or got an error */ @@ -1819,8 +1819,8 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, goto error; } - if (module) { - tev->point.module = strdup(module); + if (target) { + tev->point.module = strdup(target); if (tev->point.module == NULL) { ret = -ENOMEM; goto error; @@ -1884,7 +1884,7 @@ struct __event_package { }; int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, - int max_tevs, const char *module, bool force_add) + int max_tevs, const char *target, bool force_add) { int i, j, ret; struct __event_package *pkgs; @@ -1907,7 +1907,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, ret = convert_to_probe_trace_events(pkgs[i].pev, &pkgs[i].tevs, max_tevs, - module); + target); if (ret < 0) goto end; pkgs[i].ntevs = ret; @@ -1959,7 +1959,7 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) goto error; } - printf("Remove event: %s\n", ent->s); + printf("Removed event: %s\n", ent->s); return 0; error: pr_warning("Failed to delete event: %s\n", strerror(-ret)); @@ -2063,7 +2063,7 @@ static int filter_available_functions(struct map *map __unused, return 1; } -int show_available_funcs(const char *module, struct strfilter *_filter) +int show_available_funcs(const char *target, struct strfilter *_filter) { struct map *map; int ret; @@ -2074,9 +2074,9 @@ int show_available_funcs(const char *module, struct strfilter *_filter) if (ret < 0) return ret; - map = kernel_get_module_map(module); + map = kernel_get_module_map(target); if (!map) { - pr_err("Failed to find %s map.\n", (module) ? : "kernel"); + pr_err("Failed to find %s map.\n", (target) ? : "kernel"); return -EINVAL; } available_func_filter = _filter; diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 5d732621a462..67dc4aed721c 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -30,7 +30,6 @@ #include <stdlib.h> #include <string.h> #include <stdarg.h> -#include <ctype.h> #include <dwarf-regs.h> #include <linux/bitops.h> diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 9dd47a4f2596..e03b58a48424 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -425,14 +425,14 @@ struct pyrf_thread_map { static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, PyObject *args, PyObject *kwargs) { - static char *kwlist[] = { "pid", "tid", NULL }; - int pid = -1, tid = -1; + static char *kwlist[] = { "pid", "tid", "uid", NULL }; + int pid = -1, tid = -1, uid = UINT_MAX; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", - kwlist, &pid, &tid)) + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iii", + kwlist, &pid, &tid, &uid)) return -1; - pthreads->threads = thread_map__new(pid, tid); + pthreads->threads = thread_map__new(pid, tid, uid); if (pthreads->threads == NULL) return -1; return 0; diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 0b2a48783172..c2623c6f9b51 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -24,7 +24,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> #include <errno.h> #include "../../perf.h" diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b5ca2558c7bb..552c1c50eea1 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1293,10 +1293,9 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, struct machine *machine, struct perf_evsel *evsel, - int print_sym, int print_dso) + int print_sym, int print_dso, int print_symoffset) { struct addr_location al; - const char *symname, *dsoname; struct callchain_cursor *cursor = &evsel->hists.callchain_cursor; struct callchain_cursor_node *node; @@ -1324,20 +1323,13 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, printf("\t%16" PRIx64, node->ip); if (print_sym) { - if (node->sym && node->sym->name) - symname = node->sym->name; - else - symname = ""; - - printf(" %s", symname); + printf(" "); + symbol__fprintf_symname(node->sym, stdout); } if (print_dso) { - if (node->map && node->map->dso && node->map->dso->name) - dsoname = node->map->dso->name; - else - dsoname = ""; - - printf(" (%s)", dsoname); + printf(" ("); + map__fprintf_dsoname(al.map, stdout); + printf(")"); } printf("\n"); @@ -1347,21 +1339,18 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, } else { printf("%16" PRIx64, sample->ip); if (print_sym) { - if (al.sym && al.sym->name) - symname = al.sym->name; + printf(" "); + if (print_symoffset) + symbol__fprintf_symname_offs(al.sym, &al, + stdout); else - symname = ""; - - printf(" %s", symname); + symbol__fprintf_symname(al.sym, stdout); } if (print_dso) { - if (al.map && al.map->dso && al.map->dso->name) - dsoname = al.map->dso->name; - else - dsoname = ""; - - printf(" (%s)", dsoname); + printf(" ("); + map__fprintf_dsoname(al.map, stdout); + printf(")"); } } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 37bc38381fb6..c8d90178e7de 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -147,7 +147,7 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, struct machine *machine, struct perf_evsel *evsel, - int print_sym, int print_dso); + int print_sym, int print_dso, int print_symoffset); int perf_session__cpu_bitmap(struct perf_session *session, const char *cpu_list, unsigned long *cpu_bitmap); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 0975438c3e72..fc6e12fe4b44 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -263,6 +263,28 @@ static size_t symbol__fprintf(struct symbol *sym, FILE *fp) sym->name); } +size_t symbol__fprintf_symname_offs(const struct symbol *sym, + const struct addr_location *al, FILE *fp) +{ + unsigned long offset; + size_t length; + + if (sym && sym->name) { + length = fprintf(fp, "%s", sym->name); + if (al) { + offset = al->addr - sym->start; + length += fprintf(fp, "+0x%lx", offset); + } + return length; + } else + return fprintf(fp, "[unknown]"); +} + +size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) +{ + return symbol__fprintf_symname_offs(sym, NULL, fp); +} + void dso__set_long_name(struct dso *dso, char *name) { if (name == NULL) diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 123c2e14353e..2a683d4fc918 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -70,6 +70,7 @@ struct symbol_conf { unsigned short priv_size; unsigned short nr_events; bool try_vmlinux_path, + show_kernel_path, use_modules, sort_by_name, show_nr_samples, @@ -241,6 +242,9 @@ void machines__destroy_guest_kernel_maps(struct rb_root *machines); int symbol__init(void); void symbol__exit(void); +size_t symbol__fprintf_symname_offs(const struct symbol *sym, + const struct addr_location *al, FILE *fp); +size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); bool symbol_type__is_a(char symbol_type, enum map_type map_type); size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index a5df131b77c3..3d4b6c5931b9 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -1,6 +1,11 @@ #include <dirent.h> +#include <limits.h> +#include <stdbool.h> #include <stdlib.h> #include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> #include "thread_map.h" /* Skip "." and ".." directories */ @@ -23,7 +28,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid) sprintf(name, "/proc/%d/task", pid); items = scandir(name, &namelist, filter, NULL); if (items <= 0) - return NULL; + return NULL; threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); if (threads != NULL) { @@ -51,10 +56,99 @@ struct thread_map *thread_map__new_by_tid(pid_t tid) return threads; } -struct thread_map *thread_map__new(pid_t pid, pid_t tid) +struct thread_map *thread_map__new_by_uid(uid_t uid) +{ + DIR *proc; + int max_threads = 32, items, i; + char path[256]; + struct dirent dirent, *next, **namelist = NULL; + struct thread_map *threads = malloc(sizeof(*threads) + + max_threads * sizeof(pid_t)); + if (threads == NULL) + goto out; + + proc = opendir("/proc"); + if (proc == NULL) + goto out_free_threads; + + threads->nr = 0; + + while (!readdir_r(proc, &dirent, &next) && next) { + char *end; + bool grow = false; + struct stat st; + pid_t pid = strtol(dirent.d_name, &end, 10); + + if (*end) /* only interested in proper numerical dirents */ + continue; + + snprintf(path, sizeof(path), "/proc/%s", dirent.d_name); + + if (stat(path, &st) != 0) + continue; + + if (st.st_uid != uid) + continue; + + snprintf(path, sizeof(path), "/proc/%d/task", pid); + items = scandir(path, &namelist, filter, NULL); + if (items <= 0) + goto out_free_closedir; + + while (threads->nr + items >= max_threads) { + max_threads *= 2; + grow = true; + } + + if (grow) { + struct thread_map *tmp; + + tmp = realloc(threads, (sizeof(*threads) + + max_threads * sizeof(pid_t))); + if (tmp == NULL) + goto out_free_namelist; + + threads = tmp; + } + + for (i = 0; i < items; i++) + threads->map[threads->nr + i] = atoi(namelist[i]->d_name); + + for (i = 0; i < items; i++) + free(namelist[i]); + free(namelist); + + threads->nr += items; + } + +out_closedir: + closedir(proc); +out: + return threads; + +out_free_threads: + free(threads); + return NULL; + +out_free_namelist: + for (i = 0; i < items; i++) + free(namelist[i]); + free(namelist); + +out_free_closedir: + free(threads); + threads = NULL; + goto out_closedir; +} + +struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid) { if (pid != -1) return thread_map__new_by_pid(pid); + + if (tid == -1 && uid != UINT_MAX) + return thread_map__new_by_uid(uid); + return thread_map__new_by_tid(tid); } @@ -62,3 +156,14 @@ void thread_map__delete(struct thread_map *threads) { free(threads); } + +size_t thread_map__fprintf(struct thread_map *threads, FILE *fp) +{ + int i; + size_t printed = fprintf(fp, "%d thread%s: ", + threads->nr, threads->nr > 1 ? "s" : ""); + for (i = 0; i < threads->nr; ++i) + printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]); + + return printed + fprintf(fp, "\n"); +} diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 3cb907311409..c75ddbaba005 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h @@ -2,6 +2,7 @@ #define __PERF_THREAD_MAP_H #include <sys/types.h> +#include <stdio.h> struct thread_map { int nr; @@ -10,6 +11,10 @@ struct thread_map { struct thread_map *thread_map__new_by_pid(pid_t pid); struct thread_map *thread_map__new_by_tid(pid_t tid); -struct thread_map *thread_map__new(pid_t pid, pid_t tid); +struct thread_map *thread_map__new_by_uid(uid_t uid); +struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid); void thread_map__delete(struct thread_map *threads); + +size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); + #endif /* __PERF_THREAD_MAP_H */ diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 500471dffa4f..e4370ca27193 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -75,6 +75,9 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) else if (top->target_tid != -1) ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d", top->target_tid); + else if (top->uid_str != NULL) + ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", + top->uid_str); else ret += SNPRINTF(bf + ret, size - ret, " (all"); diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index a248f3c2c60d..def3e53e0fe0 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -24,6 +24,7 @@ struct perf_top { int print_entries, count_filter, delay_secs; int freq; pid_t target_pid, target_tid; + uid_t uid; bool hide_kernel_symbols, hide_user_symbols, zero; bool system_wide; bool use_tui, use_stdio; @@ -45,6 +46,7 @@ struct perf_top { int realtime_prio; int sym_pcnt_filter; const char *sym_filter; + const char *uid_str; }; size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 1a8d4dc4f386..e0a4f652f289 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -25,7 +25,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> #include <errno.h> #include "../perf.h" diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index f55cc3a765a1..b9592e0de8d7 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -33,7 +33,6 @@ #include <pthread.h> #include <fcntl.h> #include <unistd.h> -#include <ctype.h> #include <errno.h> #include "../perf.h" diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index a3fdf55f317b..18ae6c1831d3 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -22,7 +22,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <ctype.h> #include <errno.h> #include "../perf.h" diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index e81aef1f2569..bfba0490c098 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c @@ -839,6 +839,9 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, nr_events = convert_unit(nr_events, &unit); printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name); + if (self->uid_filter_str) + printed += snprintf(bf + printed, size - printed, + ", UID: %s", self->uid_filter_str); if (thread) printed += snprintf(bf + printed, size - printed, ", Thread: %s(%d)", diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index 6905bcc8be2d..eca6575abfd0 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c @@ -3,9 +3,9 @@ #include <newt.h> #include <inttypes.h> #include <sys/ttydefaults.h> -#include <ctype.h> #include <string.h> #include <linux/bitops.h> +#include "../../util.h" #include "../../debug.h" #include "../../symbol.h" #include "../browser.h" diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c index d76d1c0ff98f..d0c013934f30 100644 --- a/tools/perf/util/usage.c +++ b/tools/perf/util/usage.c @@ -7,6 +7,7 @@ * Copyright (C) Linus Torvalds, 2005 */ #include "util.h" +#include "debug.h" static void report(const char *prefix, const char *err, va_list params) { @@ -81,3 +82,41 @@ void warning(const char *warn, ...) warn_routine(warn, params); va_end(params); } + +uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid) +{ + struct passwd pwd, *result; + char buf[1024]; + + if (str == NULL) + return UINT_MAX; + + /* CPU and PID are mutually exclusive */ + if (tid > 0 || pid > 0) { + ui__warning("PID/TID switch overriding UID\n"); + sleep(1); + return UINT_MAX; + } + + getpwnam_r(str, &pwd, buf, sizeof(buf), &result); + + if (result == NULL) { + char *endptr; + int uid = strtol(str, &endptr, 10); + + if (*endptr != '\0') { + ui__error("Invalid user %s\n", str); + return UINT_MAX - 1; + } + + getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); + + if (result == NULL) { + ui__error("Problems obtaining information for user %s\n", + str); + return UINT_MAX - 1; + } + } + + return result->pw_uid; +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index ecf9898169c8..232d17ef3e60 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -245,6 +245,8 @@ struct perf_event_attr; void event_attr_init(struct perf_event_attr *attr); +uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid); + #define _STR(x) #x #define STR(x) _STR(x) |