From 8812ad412f851216d6c39488a7e563ccc5c604cc Mon Sep 17 00:00:00 2001 From: Alexey Budankov Date: Tue, 3 Dec 2019 14:43:33 +0300 Subject: tools bitmap: Implement bitmap_equal() operation at bitmap API Extend tools bitmap API with bitmap_equal() implementation. The implementation has been derived from the kernel. Extend tools bitmap API with bitmap_free() implementation for symmetry with bitmap_alloc() function. Signed-off-by: Alexey Budankov Acked-by: Jiri Olsa Cc: Alexander Shishkin Cc: Andi Kleen Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/43757993-0b28-d8af-a6c7-ede12e3a6877@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/linux/bitmap.h | 30 ++++++++++++++++++++++++++++++ tools/lib/bitmap.c | 15 +++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'tools') diff --git a/tools/include/linux/bitmap.h b/tools/include/linux/bitmap.h index 05dca5c203f3..477a1cae513f 100644 --- a/tools/include/linux/bitmap.h +++ b/tools/include/linux/bitmap.h @@ -15,6 +15,8 @@ void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, const unsigned long *bitmap2, int bits); int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, const unsigned long *bitmap2, unsigned int bits); +int __bitmap_equal(const unsigned long *bitmap1, + const unsigned long *bitmap2, unsigned int bits); void bitmap_clear(unsigned long *map, unsigned int start, int len); #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1))) @@ -123,6 +125,15 @@ static inline unsigned long *bitmap_alloc(int nbits) return calloc(1, BITS_TO_LONGS(nbits) * sizeof(unsigned long)); } +/* + * bitmap_free - Free bitmap + * @bitmap: pointer to bitmap + */ +static inline void bitmap_free(unsigned long *bitmap) +{ + free(bitmap); +} + /* * bitmap_scnprintf - print bitmap list into buffer * @bitmap: bitmap @@ -148,4 +159,23 @@ static inline int bitmap_and(unsigned long *dst, const unsigned long *src1, return __bitmap_and(dst, src1, src2, nbits); } +#ifdef __LITTLE_ENDIAN +#define BITMAP_MEM_ALIGNMENT 8 +#else +#define BITMAP_MEM_ALIGNMENT (8 * sizeof(unsigned long)) +#endif +#define BITMAP_MEM_MASK (BITMAP_MEM_ALIGNMENT - 1) +#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) + +static inline int bitmap_equal(const unsigned long *src1, + const unsigned long *src2, unsigned int nbits) +{ + if (small_const_nbits(nbits)) + return !((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits)); + if (__builtin_constant_p(nbits & BITMAP_MEM_MASK) && + IS_ALIGNED(nbits, BITMAP_MEM_ALIGNMENT)) + return !memcmp(src1, src2, nbits / 8); + return __bitmap_equal(src1, src2, nbits); +} + #endif /* _PERF_BITOPS_H */ diff --git a/tools/lib/bitmap.c b/tools/lib/bitmap.c index 38494782be06..5043747ef6c5 100644 --- a/tools/lib/bitmap.c +++ b/tools/lib/bitmap.c @@ -71,3 +71,18 @@ int __bitmap_and(unsigned long *dst, const unsigned long *bitmap1, BITMAP_LAST_WORD_MASK(bits)); return result != 0; } + +int __bitmap_equal(const unsigned long *bitmap1, + const unsigned long *bitmap2, unsigned int bits) +{ + unsigned int k, lim = bits/BITS_PER_LONG; + for (k = 0; k < lim; ++k) + if (bitmap1[k] != bitmap2[k]) + return 0; + + if (bits % BITS_PER_LONG) + if ((bitmap1[k] ^ bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits)) + return 0; + + return 1; +} -- cgit From 9c080c0279a80057cad3dfc05d09fb283ddf72f4 Mon Sep 17 00:00:00 2001 From: Alexey Budankov Date: Tue, 3 Dec 2019 14:44:18 +0300 Subject: perf mmap: Declare type for cpu mask of arbitrary length Declare a dedicated struct map_cpu_mask type for cpu masks of arbitrary length. The mask is available thru bits pointer and the mask length is kept in nbits field. MMAP_CPU_MASK_BYTES() macro returns mask storage size in bytes. The mmap_cpu_mask__scnprintf() function can be used to log text representation of the mask. Committer notes: To print the 'nbits' struct member we must use %zd, since it is a size_t, this fixes the build in some toolchains/arches. Signed-off-by: Alexey Budankov Acked-by: Jiri Olsa Cc: Alexander Shishkin Cc: Andi Kleen Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/0fd2454f-477f-d15a-f4ee-79bcbd2585ff@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/mmap.c | 12 ++++++++++++ tools/perf/util/mmap.h | 11 +++++++++++ 2 files changed, 23 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index 063d1b93c53d..2ee4faacca21 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -23,6 +23,18 @@ #include "mmap.h" #include "../perf.h" #include /* page_size */ +#include + +#define MASK_SIZE 1023 +void mmap_cpu_mask__scnprintf(struct mmap_cpu_mask *mask, const char *tag) +{ + char buf[MASK_SIZE + 1]; + size_t len; + + len = bitmap_scnprintf(mask->bits, mask->nbits, buf, MASK_SIZE); + buf[len] = '\0'; + pr_debug("%p: %s mask[%zd]: %s\n", mask, tag, mask->nbits, buf); +} size_t mmap__mmap_len(struct mmap *map) { diff --git a/tools/perf/util/mmap.h b/tools/perf/util/mmap.h index bee4e83f7109..ef51667fabcb 100644 --- a/tools/perf/util/mmap.h +++ b/tools/perf/util/mmap.h @@ -15,6 +15,15 @@ #include "event.h" struct aiocb; + +struct mmap_cpu_mask { + unsigned long *bits; + size_t nbits; +}; + +#define MMAP_CPU_MASK_BYTES(m) \ + (BITS_TO_LONGS(((struct mmap_cpu_mask *)m)->nbits) * sizeof(unsigned long)) + /** * struct mmap - perf's ring buffer mmap details * @@ -52,4 +61,6 @@ int perf_mmap__push(struct mmap *md, void *to, size_t mmap__mmap_len(struct mmap *map); +void mmap_cpu_mask__scnprintf(struct mmap_cpu_mask *mask, const char *tag); + #endif /*__PERF_MMAP_H */ -- cgit From 8384a2600c7ddfc875f64e160d8b423aca4e203a Mon Sep 17 00:00:00 2001 From: Alexey Budankov Date: Tue, 3 Dec 2019 14:45:27 +0300 Subject: perf record: Adapt affinity to machines with #CPUs > 1K Use struct mmap_cpu_mask type for the tool's thread and mmap data buffers to overcome current 1024 CPUs mask size limitation of cpu_set_t type. Currently glibc's cpu_set_t type has an internal mask size limit of 1024 CPUs. Moving to the 'struct mmap_cpu_mask' type allows overcoming that limit. The tools bitmap API is used to manipulate objects of 'struct mmap_cpu_mask' type. Committer notes: To print the 'nbits' struct member we must use %zd, since it is a size_t, this fixes the build in some toolchains/arches. Reported-by: Andi Kleen Signed-off-by: Alexey Budankov Acked-by: Jiri Olsa Cc: Alexander Shishkin Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/96d7e2ff-ce8b-c1e0-d52c-aa59ea96f0ea@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 28 ++++++++++++++++++++++------ tools/perf/util/mmap.c | 28 ++++++++++++++++++++++------ tools/perf/util/mmap.h | 2 +- 3 files changed, 45 insertions(+), 13 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index fb19ef63cc35..4c301466101b 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -62,6 +62,7 @@ #include #include #include +#include struct switch_output { bool enabled; @@ -93,7 +94,7 @@ struct record { bool timestamp_boundary; struct switch_output switch_output; unsigned long long samples; - cpu_set_t affinity_mask; + struct mmap_cpu_mask affinity_mask; unsigned long output_max_size; /* = 0: unlimited */ }; @@ -961,10 +962,15 @@ static struct perf_event_header finished_round_event = { static void record__adjust_affinity(struct record *rec, struct mmap *map) { if (rec->opts.affinity != PERF_AFFINITY_SYS && - !CPU_EQUAL(&rec->affinity_mask, &map->affinity_mask)) { - CPU_ZERO(&rec->affinity_mask); - CPU_OR(&rec->affinity_mask, &rec->affinity_mask, &map->affinity_mask); - sched_setaffinity(0, sizeof(rec->affinity_mask), &rec->affinity_mask); + !bitmap_equal(rec->affinity_mask.bits, map->affinity_mask.bits, + rec->affinity_mask.nbits)) { + bitmap_zero(rec->affinity_mask.bits, rec->affinity_mask.nbits); + bitmap_or(rec->affinity_mask.bits, rec->affinity_mask.bits, + map->affinity_mask.bits, rec->affinity_mask.nbits); + sched_setaffinity(0, MMAP_CPU_MASK_BYTES(&rec->affinity_mask), + (cpu_set_t *)rec->affinity_mask.bits); + if (verbose == 2) + mmap_cpu_mask__scnprintf(&rec->affinity_mask, "thread"); } } @@ -2433,7 +2439,6 @@ int cmd_record(int argc, const char **argv) # undef REASON #endif - CPU_ZERO(&rec->affinity_mask); rec->opts.affinity = PERF_AFFINITY_SYS; rec->evlist = evlist__new(); @@ -2499,6 +2504,16 @@ int cmd_record(int argc, const char **argv) symbol__init(NULL); + if (rec->opts.affinity != PERF_AFFINITY_SYS) { + rec->affinity_mask.nbits = cpu__max_cpu(); + rec->affinity_mask.bits = bitmap_alloc(rec->affinity_mask.nbits); + if (!rec->affinity_mask.bits) { + pr_err("Failed to allocate thread mask for %zd cpus\n", rec->affinity_mask.nbits); + return -ENOMEM; + } + pr_debug2("thread mask[%zd]: empty\n", rec->affinity_mask.nbits); + } + err = record__auxtrace_init(rec); if (err) goto out; @@ -2613,6 +2628,7 @@ int cmd_record(int argc, const char **argv) err = __cmd_record(&record, argc, argv); out: + bitmap_free(rec->affinity_mask.bits); evlist__delete(rec->evlist); symbol__exit(); auxtrace_record__free(rec->itr); diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index 2ee4faacca21..3b664fa673a6 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -219,6 +219,8 @@ static void perf_mmap__aio_munmap(struct mmap *map __maybe_unused) void mmap__munmap(struct mmap *map) { + bitmap_free(map->affinity_mask.bits); + perf_mmap__aio_munmap(map); if (map->data != NULL) { munmap(map->data, mmap__mmap_len(map)); @@ -227,7 +229,7 @@ void mmap__munmap(struct mmap *map) auxtrace_mmap__munmap(&map->auxtrace_mmap); } -static void build_node_mask(int node, cpu_set_t *mask) +static void build_node_mask(int node, struct mmap_cpu_mask *mask) { int c, cpu, nr_cpus; const struct perf_cpu_map *cpu_map = NULL; @@ -240,17 +242,23 @@ static void build_node_mask(int node, cpu_set_t *mask) for (c = 0; c < nr_cpus; c++) { cpu = cpu_map->map[c]; /* map c index to online cpu index */ if (cpu__get_node(cpu) == node) - CPU_SET(cpu, mask); + set_bit(cpu, mask->bits); } } -static void perf_mmap__setup_affinity_mask(struct mmap *map, struct mmap_params *mp) +static int perf_mmap__setup_affinity_mask(struct mmap *map, struct mmap_params *mp) { - CPU_ZERO(&map->affinity_mask); + map->affinity_mask.nbits = cpu__max_cpu(); + map->affinity_mask.bits = bitmap_alloc(map->affinity_mask.nbits); + if (!map->affinity_mask.bits) + return -1; + if (mp->affinity == PERF_AFFINITY_NODE && cpu__max_node() > 1) build_node_mask(cpu__get_node(map->core.cpu), &map->affinity_mask); else if (mp->affinity == PERF_AFFINITY_CPU) - CPU_SET(map->core.cpu, &map->affinity_mask); + set_bit(map->core.cpu, map->affinity_mask.bits); + + return 0; } int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu) @@ -261,7 +269,15 @@ int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, int cpu) return -1; } - perf_mmap__setup_affinity_mask(map, mp); + if (mp->affinity != PERF_AFFINITY_SYS && + perf_mmap__setup_affinity_mask(map, mp)) { + pr_debug2("failed to alloc mmap affinity mask, error %d\n", + errno); + return -1; + } + + if (verbose == 2) + mmap_cpu_mask__scnprintf(&map->affinity_mask, "mmap"); map->core.flush = mp->flush; diff --git a/tools/perf/util/mmap.h b/tools/perf/util/mmap.h index ef51667fabcb..9d5f589f02ae 100644 --- a/tools/perf/util/mmap.h +++ b/tools/perf/util/mmap.h @@ -40,7 +40,7 @@ struct mmap { int nr_cblocks; } aio; #endif - cpu_set_t affinity_mask; + struct mmap_cpu_mask affinity_mask; void *data; int comp_level; }; -- cgit From c30d630d1bcfad8d2f70ff0cbb2a86d5a43bc152 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 4 Dec 2019 10:39:25 -0700 Subject: perf sched timehist: Add support for filtering on CPU Allow user to limit output to one or more CPUs. Really helpful on systems with a large number of cpus. Committer testing: # perf sched record -a sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 1.765 MB perf.data (1412 samples) ] [root@quaco ~]# perf sched timehist | head Samples do not have callchains. time cpu task name wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ --------- --------- --------- 66307.802686 [0000] perf[13086] 0.000 0.000 0.000 66307.802700 [0000] migration/0[12] 0.000 0.001 0.014 66307.802766 [0001] perf[13086] 0.000 0.000 0.000 66307.802774 [0001] migration/1[15] 0.000 0.001 0.007 66307.802841 [0002] perf[13086] 0.000 0.000 0.000 66307.802849 [0002] migration/2[20] 0.000 0.001 0.008 66307.802913 [0003] perf[13086] 0.000 0.000 0.000 # # perf sched timehist --cpu 2 | head Samples do not have callchains. time cpu task name wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ --------- --------- --------- 66307.802841 [0002] perf[13086] 0.000 0.000 0.000 66307.802849 [0002] migration/2[20] 0.000 0.001 0.008 66307.964485 [0002] 0.000 0.000 161.635 66307.964811 [0002] CPU 0/KVM[3589/3561] 0.000 0.056 0.325 66307.965477 [0002] 0.325 0.000 0.666 66307.965553 [0002] CPU 0/KVM[3589/3561] 0.666 0.024 0.076 66307.966456 [0002] 0.076 0.000 0.903 # Signed-off-by: David Ahern Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Link: http://lore.kernel.org/lkml/20191204173925.66976-1-dsahern@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-sched.txt | 4 ++++ tools/perf/builtin-sched.c | 13 +++++++++++++ 2 files changed, 17 insertions(+) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 63f938b887dd..5fbe42bd599b 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt @@ -110,6 +110,10 @@ OPTIONS for 'perf sched timehist' --max-stack:: Maximum number of functions to display in backtrace, default 5. +-C=:: +--cpu=:: + Only show events for the given CPU(s) (comma separated list). + -p=:: --pid=:: Only show events for given process ID (comma separated list). diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 8a12d71364c3..82fcc2c15fe4 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -51,6 +51,9 @@ #define SYM_LEN 129 #define MAX_PID 1024000 +static const char *cpu_list; +static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); + struct sched_atom; struct task_desc { @@ -2008,6 +2011,9 @@ static void timehist_print_sample(struct perf_sched *sched, char nstr[30]; u64 wait_time; + if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) + return; + timestamp__scnprintf_usec(t, tstr, sizeof(tstr)); printf("%15s [%04d] ", tstr, sample->cpu); @@ -2994,6 +3000,12 @@ static int perf_sched__timehist(struct perf_sched *sched) if (IS_ERR(session)) return PTR_ERR(session); + if (cpu_list) { + err = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap); + if (err < 0) + goto out; + } + evlist = session->evlist; symbol__init(&session->header.env); @@ -3429,6 +3441,7 @@ int cmd_sched(int argc, const char **argv) "analyze events only for given process id(s)"), OPT_STRING('t', "tid", &symbol_conf.tid_list_str, "tid[,tid...]", "analyze events only for given thread id(s)"), + OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"), OPT_PARENT(sched_options) }; -- cgit From 6ae9c10b7cd50ac9080880204f8d9ff6381b2869 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 5 Dec 2019 16:09:59 -0300 Subject: perf tests bp_signal: Show expected versus obtained values To help understand failures. Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-c951j3gvrgnrsyg7ki7pwkiz@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/bp_signal.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c index 415903b48578..da8ec1e8e064 100644 --- a/tools/perf/tests/bp_signal.c +++ b/tools/perf/tests/bp_signal.c @@ -263,20 +263,20 @@ int test__bp_signal(struct test *test __maybe_unused, int subtest __maybe_unused if (count1 == 11) pr_debug("failed: RF EFLAG recursion issue detected\n"); else - pr_debug("failed: wrong count for bp1%lld\n", count1); + pr_debug("failed: wrong count for bp1: %lld, expected 1\n", count1); } if (overflows != 3) - pr_debug("failed: wrong overflow hit\n"); + pr_debug("failed: wrong overflow (%d) hit, expected 3\n", overflows); if (overflows_2 != 3) - pr_debug("failed: wrong overflow_2 hit\n"); + pr_debug("failed: wrong overflow_2 (%d) hit, expected 3\n", overflows_2); if (count2 != 3) - pr_debug("failed: wrong count for bp2\n"); + pr_debug("failed: wrong count for bp2 (%lld), expected 3\n", count2); if (count3 != 2) - pr_debug("failed: wrong count for bp3\n"); + pr_debug("failed: wrong count for bp3 (%lld), expected 2\n", count3); return count1 == 1 && overflows == 3 && count2 == 3 && overflows_2 == 3 && count3 == 2 ? TEST_OK : TEST_FAIL; -- cgit From 3ce311afb5583cf3d3b7f54ab088949da28aea05 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 6 Dec 2019 22:06:11 +0100 Subject: libperf: Move to tools/lib/perf Move libperf from its current location under tools/perf to a separate directory under tools/lib/. Also change various paths (mainly includes) to reflect the libperf move to a separate directory and add a new directory under MANIFEST. Signed-off-by: Jiri Olsa Tested-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20191206210612.8676-2-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/Build | 13 + tools/lib/perf/Documentation/Makefile | 7 + tools/lib/perf/Documentation/man/libperf.rst | 100 ++++ tools/lib/perf/Documentation/tutorial/tutorial.rst | 123 ++++ tools/lib/perf/Makefile | 188 ++++++ tools/lib/perf/core.c | 38 ++ tools/lib/perf/cpumap.c | 345 +++++++++++ tools/lib/perf/evlist.c | 641 +++++++++++++++++++++ tools/lib/perf/evsel.c | 301 ++++++++++ tools/lib/perf/include/internal/cpumap.h | 19 + tools/lib/perf/include/internal/evlist.h | 127 ++++ tools/lib/perf/include/internal/evsel.h | 63 ++ tools/lib/perf/include/internal/lib.h | 12 + tools/lib/perf/include/internal/mmap.h | 55 ++ tools/lib/perf/include/internal/tests.h | 33 ++ tools/lib/perf/include/internal/threadmap.h | 23 + tools/lib/perf/include/internal/xyarray.h | 36 ++ tools/lib/perf/include/perf/core.h | 25 + tools/lib/perf/include/perf/cpumap.h | 28 + tools/lib/perf/include/perf/event.h | 385 +++++++++++++ tools/lib/perf/include/perf/evlist.h | 49 ++ tools/lib/perf/include/perf/evsel.h | 40 ++ tools/lib/perf/include/perf/mmap.h | 15 + tools/lib/perf/include/perf/threadmap.h | 20 + tools/lib/perf/internal.h | 23 + tools/lib/perf/lib.c | 48 ++ tools/lib/perf/libperf.map | 51 ++ tools/lib/perf/libperf.pc.template | 11 + tools/lib/perf/mmap.c | 275 +++++++++ tools/lib/perf/tests/Makefile | 38 ++ tools/lib/perf/tests/test-cpumap.c | 31 + tools/lib/perf/tests/test-evlist.c | 413 +++++++++++++ tools/lib/perf/tests/test-evsel.c | 135 +++++ tools/lib/perf/tests/test-threadmap.c | 31 + tools/lib/perf/threadmap.c | 91 +++ tools/lib/perf/xyarray.c | 33 ++ tools/perf/MANIFEST | 1 + tools/perf/Makefile.config | 2 +- tools/perf/Makefile.perf | 2 +- tools/perf/lib/Build | 13 - tools/perf/lib/Documentation/Makefile | 7 - tools/perf/lib/Documentation/man/libperf.rst | 100 ---- tools/perf/lib/Documentation/tutorial/tutorial.rst | 123 ---- tools/perf/lib/Makefile | 188 ------ tools/perf/lib/core.c | 38 -- tools/perf/lib/cpumap.c | 345 ----------- tools/perf/lib/evlist.c | 641 --------------------- tools/perf/lib/evsel.c | 301 ---------- tools/perf/lib/include/internal/cpumap.h | 19 - tools/perf/lib/include/internal/evlist.h | 127 ---- tools/perf/lib/include/internal/evsel.h | 63 -- tools/perf/lib/include/internal/lib.h | 12 - tools/perf/lib/include/internal/mmap.h | 55 -- tools/perf/lib/include/internal/tests.h | 33 -- tools/perf/lib/include/internal/threadmap.h | 23 - tools/perf/lib/include/internal/xyarray.h | 36 -- tools/perf/lib/include/perf/core.h | 25 - tools/perf/lib/include/perf/cpumap.h | 28 - tools/perf/lib/include/perf/event.h | 385 ------------- tools/perf/lib/include/perf/evlist.h | 49 -- tools/perf/lib/include/perf/evsel.h | 40 -- tools/perf/lib/include/perf/mmap.h | 15 - tools/perf/lib/include/perf/threadmap.h | 20 - tools/perf/lib/internal.h | 23 - tools/perf/lib/lib.c | 48 -- tools/perf/lib/libperf.map | 51 -- tools/perf/lib/libperf.pc.template | 11 - tools/perf/lib/mmap.c | 275 --------- tools/perf/lib/tests/Makefile | 38 -- tools/perf/lib/tests/test-cpumap.c | 31 - tools/perf/lib/tests/test-evlist.c | 413 ------------- tools/perf/lib/tests/test-evsel.c | 135 ----- tools/perf/lib/tests/test-threadmap.c | 31 - tools/perf/lib/threadmap.c | 91 --- tools/perf/lib/xyarray.c | 33 -- 75 files changed, 3869 insertions(+), 3868 deletions(-) create mode 100644 tools/lib/perf/Build create mode 100644 tools/lib/perf/Documentation/Makefile create mode 100644 tools/lib/perf/Documentation/man/libperf.rst create mode 100644 tools/lib/perf/Documentation/tutorial/tutorial.rst create mode 100644 tools/lib/perf/Makefile create mode 100644 tools/lib/perf/core.c create mode 100644 tools/lib/perf/cpumap.c create mode 100644 tools/lib/perf/evlist.c create mode 100644 tools/lib/perf/evsel.c create mode 100644 tools/lib/perf/include/internal/cpumap.h create mode 100644 tools/lib/perf/include/internal/evlist.h create mode 100644 tools/lib/perf/include/internal/evsel.h create mode 100644 tools/lib/perf/include/internal/lib.h create mode 100644 tools/lib/perf/include/internal/mmap.h create mode 100644 tools/lib/perf/include/internal/tests.h create mode 100644 tools/lib/perf/include/internal/threadmap.h create mode 100644 tools/lib/perf/include/internal/xyarray.h create mode 100644 tools/lib/perf/include/perf/core.h create mode 100644 tools/lib/perf/include/perf/cpumap.h create mode 100644 tools/lib/perf/include/perf/event.h create mode 100644 tools/lib/perf/include/perf/evlist.h create mode 100644 tools/lib/perf/include/perf/evsel.h create mode 100644 tools/lib/perf/include/perf/mmap.h create mode 100644 tools/lib/perf/include/perf/threadmap.h create mode 100644 tools/lib/perf/internal.h create mode 100644 tools/lib/perf/lib.c create mode 100644 tools/lib/perf/libperf.map create mode 100644 tools/lib/perf/libperf.pc.template create mode 100644 tools/lib/perf/mmap.c create mode 100644 tools/lib/perf/tests/Makefile create mode 100644 tools/lib/perf/tests/test-cpumap.c create mode 100644 tools/lib/perf/tests/test-evlist.c create mode 100644 tools/lib/perf/tests/test-evsel.c create mode 100644 tools/lib/perf/tests/test-threadmap.c create mode 100644 tools/lib/perf/threadmap.c create mode 100644 tools/lib/perf/xyarray.c delete mode 100644 tools/perf/lib/Build delete mode 100644 tools/perf/lib/Documentation/Makefile delete mode 100644 tools/perf/lib/Documentation/man/libperf.rst delete mode 100644 tools/perf/lib/Documentation/tutorial/tutorial.rst delete mode 100644 tools/perf/lib/Makefile delete mode 100644 tools/perf/lib/core.c delete mode 100644 tools/perf/lib/cpumap.c delete mode 100644 tools/perf/lib/evlist.c delete mode 100644 tools/perf/lib/evsel.c delete mode 100644 tools/perf/lib/include/internal/cpumap.h delete mode 100644 tools/perf/lib/include/internal/evlist.h delete mode 100644 tools/perf/lib/include/internal/evsel.h delete mode 100644 tools/perf/lib/include/internal/lib.h delete mode 100644 tools/perf/lib/include/internal/mmap.h delete mode 100644 tools/perf/lib/include/internal/tests.h delete mode 100644 tools/perf/lib/include/internal/threadmap.h delete mode 100644 tools/perf/lib/include/internal/xyarray.h delete mode 100644 tools/perf/lib/include/perf/core.h delete mode 100644 tools/perf/lib/include/perf/cpumap.h delete mode 100644 tools/perf/lib/include/perf/event.h delete mode 100644 tools/perf/lib/include/perf/evlist.h delete mode 100644 tools/perf/lib/include/perf/evsel.h delete mode 100644 tools/perf/lib/include/perf/mmap.h delete mode 100644 tools/perf/lib/include/perf/threadmap.h delete mode 100644 tools/perf/lib/internal.h delete mode 100644 tools/perf/lib/lib.c delete mode 100644 tools/perf/lib/libperf.map delete mode 100644 tools/perf/lib/libperf.pc.template delete mode 100644 tools/perf/lib/mmap.c delete mode 100644 tools/perf/lib/tests/Makefile delete mode 100644 tools/perf/lib/tests/test-cpumap.c delete mode 100644 tools/perf/lib/tests/test-evlist.c delete mode 100644 tools/perf/lib/tests/test-evsel.c delete mode 100644 tools/perf/lib/tests/test-threadmap.c delete mode 100644 tools/perf/lib/threadmap.c delete mode 100644 tools/perf/lib/xyarray.c (limited to 'tools') diff --git a/tools/lib/perf/Build b/tools/lib/perf/Build new file mode 100644 index 000000000000..2ef9a4ec6d99 --- /dev/null +++ b/tools/lib/perf/Build @@ -0,0 +1,13 @@ +libperf-y += core.o +libperf-y += cpumap.o +libperf-y += threadmap.o +libperf-y += evsel.o +libperf-y += evlist.o +libperf-y += mmap.o +libperf-y += zalloc.o +libperf-y += xyarray.o +libperf-y += lib.o + +$(OUTPUT)zalloc.o: ../../lib/zalloc.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) diff --git a/tools/lib/perf/Documentation/Makefile b/tools/lib/perf/Documentation/Makefile new file mode 100644 index 000000000000..586425a88795 --- /dev/null +++ b/tools/lib/perf/Documentation/Makefile @@ -0,0 +1,7 @@ +all: + rst2man man/libperf.rst > man/libperf.7 + rst2pdf tutorial/tutorial.rst + +clean: + rm -f man/libperf.7 + rm -f tutorial/tutorial.pdf diff --git a/tools/lib/perf/Documentation/man/libperf.rst b/tools/lib/perf/Documentation/man/libperf.rst new file mode 100644 index 000000000000..09a270fccb9c --- /dev/null +++ b/tools/lib/perf/Documentation/man/libperf.rst @@ -0,0 +1,100 @@ +.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) + +libperf + +The libperf library provides an API to access the linux kernel perf +events subsystem. It provides the following high level objects: + + - struct perf_cpu_map + - struct perf_thread_map + - struct perf_evlist + - struct perf_evsel + +reference +========= +Function reference by header files: + +perf/core.h +----------- +.. code-block:: c + + typedef int (\*libperf_print_fn_t)(enum libperf_print_level level, + const char \*, va_list ap); + + void libperf_set_print(libperf_print_fn_t fn); + +perf/cpumap.h +------------- +.. code-block:: c + + struct perf_cpu_map \*perf_cpu_map__dummy_new(void); + struct perf_cpu_map \*perf_cpu_map__new(const char \*cpu_list); + struct perf_cpu_map \*perf_cpu_map__read(FILE \*file); + struct perf_cpu_map \*perf_cpu_map__get(struct perf_cpu_map \*map); + void perf_cpu_map__put(struct perf_cpu_map \*map); + int perf_cpu_map__cpu(const struct perf_cpu_map \*cpus, int idx); + int perf_cpu_map__nr(const struct perf_cpu_map \*cpus); + perf_cpu_map__for_each_cpu(cpu, idx, cpus) + +perf/threadmap.h +---------------- +.. code-block:: c + + struct perf_thread_map \*perf_thread_map__new_dummy(void); + void perf_thread_map__set_pid(struct perf_thread_map \*map, int thread, pid_t pid); + char \*perf_thread_map__comm(struct perf_thread_map \*map, int thread); + struct perf_thread_map \*perf_thread_map__get(struct perf_thread_map \*map); + void perf_thread_map__put(struct perf_thread_map \*map); + +perf/evlist.h +------------- +.. code-block:: + + void perf_evlist__init(struct perf_evlist \*evlist); + void perf_evlist__add(struct perf_evlist \*evlist, + struct perf_evsel \*evsel); + void perf_evlist__remove(struct perf_evlist \*evlist, + struct perf_evsel \*evsel); + struct perf_evlist \*perf_evlist__new(void); + void perf_evlist__delete(struct perf_evlist \*evlist); + struct perf_evsel\* perf_evlist__next(struct perf_evlist \*evlist, + struct perf_evsel \*evsel); + int perf_evlist__open(struct perf_evlist \*evlist); + void perf_evlist__close(struct perf_evlist \*evlist); + void perf_evlist__enable(struct perf_evlist \*evlist); + void perf_evlist__disable(struct perf_evlist \*evlist); + perf_evlist__for_each_evsel(evlist, pos) + void perf_evlist__set_maps(struct perf_evlist \*evlist, + struct perf_cpu_map \*cpus, + struct perf_thread_map \*threads); + +perf/evsel.h +------------ +.. code-block:: c + + struct perf_counts_values { + union { + struct { + uint64_t val; + uint64_t ena; + uint64_t run; + }; + uint64_t values[3]; + }; + }; + + void perf_evsel__init(struct perf_evsel \*evsel, + struct perf_event_attr \*attr); + struct perf_evsel \*perf_evsel__new(struct perf_event_attr \*attr); + void perf_evsel__delete(struct perf_evsel \*evsel); + int perf_evsel__open(struct perf_evsel \*evsel, struct perf_cpu_map \*cpus, + struct perf_thread_map \*threads); + void perf_evsel__close(struct perf_evsel \*evsel); + int perf_evsel__read(struct perf_evsel \*evsel, int cpu, int thread, + struct perf_counts_values \*count); + int perf_evsel__enable(struct perf_evsel \*evsel); + int perf_evsel__disable(struct perf_evsel \*evsel); + int perf_evsel__apply_filter(struct perf_evsel \*evsel, const char \*filter); + struct perf_cpu_map \*perf_evsel__cpus(struct perf_evsel \*evsel); + struct perf_thread_map \*perf_evsel__threads(struct perf_evsel \*evsel); + struct perf_event_attr \*perf_evsel__attr(struct perf_evsel \*evsel); diff --git a/tools/lib/perf/Documentation/tutorial/tutorial.rst b/tools/lib/perf/Documentation/tutorial/tutorial.rst new file mode 100644 index 000000000000..7be7bc27b385 --- /dev/null +++ b/tools/lib/perf/Documentation/tutorial/tutorial.rst @@ -0,0 +1,123 @@ +.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) + +libperf tutorial +================ + +Compile and install libperf from kernel sources +=============================================== +.. code-block:: bash + + git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git + cd linux/tools/perf/lib + make + sudo make install prefix=/usr + +Libperf object +============== +The libperf library provides several high level objects: + +struct perf_cpu_map + Provides a cpu list abstraction. + +struct perf_thread_map + Provides a thread list abstraction. + +struct perf_evsel + Provides an abstraction for single a perf event. + +struct perf_evlist + Gathers several struct perf_evsel object and performs functions on all of them. + +The exported API binds these objects together, +for full reference see the libperf.7 man page. + +Examples +======== +Examples aim to explain libperf functionality on simple use cases. +They are based in on a checked out linux kernel git tree: + +.. code-block:: bash + + $ cd tools/perf/lib/Documentation/tutorial/ + $ ls -d ex-* + ex-1-compile ex-2-evsel-stat ex-3-evlist-stat + +ex-1-compile example +==================== +This example shows the basic usage of *struct perf_cpu_map*, +how to create it and display its cpus: + +.. code-block:: bash + + $ cd ex-1-compile/ + $ make + gcc -o test test.c -lperf + $ ./test + 0 1 2 3 4 5 6 7 + + +The full code listing is here: + +.. code-block:: c + + 1 #include + 2 + 3 int main(int argc, char **Argv) + 4 { + 5 struct perf_cpu_map *cpus; + 6 int cpu, tmp; + 7 + 8 cpus = perf_cpu_map__new(NULL); + 9 + 10 perf_cpu_map__for_each_cpu(cpu, tmp, cpus) + 11 fprintf(stdout, "%d ", cpu); + 12 + 13 fprintf(stdout, "\n"); + 14 + 15 perf_cpu_map__put(cpus); + 16 return 0; + 17 } + + +First you need to include the proper header to have *struct perf_cpumap* +declaration and functions: + +.. code-block:: c + + 1 #include + + +The *struct perf_cpumap* object is created by *perf_cpu_map__new* call. +The *NULL* argument asks it to populate the object with the current online CPUs list: + +.. code-block:: c + + 8 cpus = perf_cpu_map__new(NULL); + +This is paired with a *perf_cpu_map__put*, that drops its reference at the end, possibly deleting it. + +.. code-block:: c + + 15 perf_cpu_map__put(cpus); + +The iteration through the *struct perf_cpumap* CPUs is done using the *perf_cpu_map__for_each_cpu* +macro which requires 3 arguments: + +- cpu - the cpu numer +- tmp - iteration helper variable +- cpus - the *struct perf_cpumap* object + +.. code-block:: c + + 10 perf_cpu_map__for_each_cpu(cpu, tmp, cpus) + 11 fprintf(stdout, "%d ", cpu); + +ex-2-evsel-stat example +======================= + +TBD + +ex-3-evlist-stat example +======================== + +TBD diff --git a/tools/lib/perf/Makefile b/tools/lib/perf/Makefile new file mode 100644 index 000000000000..768dd423730b --- /dev/null +++ b/tools/lib/perf/Makefile @@ -0,0 +1,188 @@ +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +# Most of this file is copied from tools/lib/bpf/Makefile + +LIBPERF_VERSION = 0 +LIBPERF_PATCHLEVEL = 0 +LIBPERF_EXTRAVERSION = 1 + +MAKEFLAGS += --no-print-directory + +ifeq ($(srctree),) +srctree := $(patsubst %/,%,$(dir $(CURDIR))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +srctree := $(patsubst %/,%,$(dir $(srctree))) +#$(info Determined 'srctree' to be $(srctree)) +endif + +INSTALL = install + +# Use DESTDIR for installing into a different root directory. +# This is useful for building a package. The program will be +# installed in this directory as if it was the root directory. +# Then the build tool can move it later. +DESTDIR ?= +DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' + +include $(srctree)/tools/scripts/Makefile.include +include $(srctree)/tools/scripts/Makefile.arch + +ifeq ($(LP64), 1) + libdir_relative = lib64 +else + libdir_relative = lib +endif + +prefix ?= +libdir = $(prefix)/$(libdir_relative) + +# Shell quotes +libdir_SQ = $(subst ','\'',$(libdir)) +libdir_relative_SQ = $(subst ','\'',$(libdir_relative)) + +ifeq ("$(origin V)", "command line") + VERBOSE = $(V) +endif +ifndef VERBOSE + VERBOSE = 0 +endif + +ifeq ($(VERBOSE),1) + Q = +else + Q = @ +endif + +# Set compile option CFLAGS +ifdef EXTRA_CFLAGS + CFLAGS := $(EXTRA_CFLAGS) +else + CFLAGS := -g -Wall +endif + +INCLUDES = \ +-I$(srctree)/tools/lib/perf/include \ +-I$(srctree)/tools/lib/ \ +-I$(srctree)/tools/include \ +-I$(srctree)/tools/arch/$(SRCARCH)/include/ \ +-I$(srctree)/tools/arch/$(SRCARCH)/include/uapi \ +-I$(srctree)/tools/include/uapi + +# Append required CFLAGS +override CFLAGS += $(EXTRA_WARNINGS) +override CFLAGS += -Werror -Wall +override CFLAGS += -fPIC +override CFLAGS += $(INCLUDES) +override CFLAGS += -fvisibility=hidden + +all: + +export srctree OUTPUT CC LD CFLAGS V +export DESTDIR DESTDIR_SQ + +include $(srctree)/tools/build/Makefile.include + +VERSION_SCRIPT := libperf.map + +PATCHLEVEL = $(LIBPERF_PATCHLEVEL) +EXTRAVERSION = $(LIBPERF_EXTRAVERSION) +VERSION = $(LIBPERF_VERSION).$(LIBPERF_PATCHLEVEL).$(LIBPERF_EXTRAVERSION) + +LIBPERF_SO := $(OUTPUT)libperf.so.$(VERSION) +LIBPERF_A := $(OUTPUT)libperf.a +LIBPERF_IN := $(OUTPUT)libperf-in.o +LIBPERF_PC := $(OUTPUT)libperf.pc + +LIBPERF_ALL := $(LIBPERF_A) $(OUTPUT)libperf.so* + +LIB_DIR := $(srctree)/tools/lib/api/ + +ifneq ($(OUTPUT),) +ifneq ($(subdir),) + API_PATH=$(OUTPUT)/../lib/api/ +else + API_PATH=$(OUTPUT) +endif +else + API_PATH=$(LIB_DIR) +endif + +LIBAPI = $(API_PATH)libapi.a +export LIBAPI + +$(LIBAPI): FORCE + $(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) $(OUTPUT)libapi.a + +$(LIBAPI)-clean: + $(call QUIET_CLEAN, libapi) + $(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null + +$(LIBPERF_IN): FORCE + $(Q)$(MAKE) $(build)=libperf + +$(LIBPERF_A): $(LIBPERF_IN) + $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIBPERF_IN) + +$(LIBPERF_SO): $(LIBPERF_IN) $(LIBAPI) + $(QUIET_LINK)$(CC) --shared -Wl,-soname,libperf.so \ + -Wl,--version-script=$(VERSION_SCRIPT) $^ -o $@ + @ln -sf $(@F) $(OUTPUT)libperf.so + @ln -sf $(@F) $(OUTPUT)libperf.so.$(LIBPERF_VERSION) + + +libs: $(LIBPERF_A) $(LIBPERF_SO) $(LIBPERF_PC) + +all: fixdep + $(Q)$(MAKE) libs + +clean: $(LIBAPI)-clean + $(call QUIET_CLEAN, libperf) $(RM) $(LIBPERF_A) \ + *.o *~ *.a *.so *.so.$(VERSION) *.so.$(LIBPERF_VERSION) .*.d .*.cmd LIBPERF-CFLAGS $(LIBPERF_PC) + $(Q)$(MAKE) -C tests clean + +tests: libs + $(Q)$(MAKE) -C tests + $(Q)$(MAKE) -C tests run + +$(LIBPERF_PC): + $(QUIET_GEN)sed -e "s|@PREFIX@|$(prefix)|" \ + -e "s|@LIBDIR@|$(libdir_SQ)|" \ + -e "s|@VERSION@|$(VERSION)|" \ + < libperf.pc.template > $@ + +define do_install_mkdir + if [ ! -d '$(DESTDIR_SQ)$1' ]; then \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$1'; \ + fi +endef + +define do_install + if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ + $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ + fi; \ + $(INSTALL) $1 $(if $3,-m $3,) '$(DESTDIR_SQ)$2' +endef + +install_lib: libs + $(call QUIET_INSTALL, $(LIBPERF_ALL)) \ + $(call do_install_mkdir,$(libdir_SQ)); \ + cp -fpR $(LIBPERF_ALL) $(DESTDIR)$(libdir_SQ) + +install_headers: + $(call QUIET_INSTALL, headers) \ + $(call do_install,include/perf/core.h,$(prefix)/include/perf,644); \ + $(call do_install,include/perf/cpumap.h,$(prefix)/include/perf,644); \ + $(call do_install,include/perf/threadmap.h,$(prefix)/include/perf,644); \ + $(call do_install,include/perf/evlist.h,$(prefix)/include/perf,644); \ + $(call do_install,include/perf/evsel.h,$(prefix)/include/perf,644); \ + $(call do_install,include/perf/event.h,$(prefix)/include/perf,644); \ + $(call do_install,include/perf/mmap.h,$(prefix)/include/perf,644); + +install_pkgconfig: $(LIBPERF_PC) + $(call QUIET_INSTALL, $(LIBPERF_PC)) \ + $(call do_install,$(LIBPERF_PC),$(libdir_SQ)/pkgconfig,644) + +install: install_lib install_headers install_pkgconfig + +FORCE: + +.PHONY: all install clean tests FORCE diff --git a/tools/lib/perf/core.c b/tools/lib/perf/core.c new file mode 100644 index 000000000000..58fc894b76c5 --- /dev/null +++ b/tools/lib/perf/core.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define __printf(a, b) __attribute__((format(printf, a, b))) + +#include +#include +#include +#include +#include +#include +#include "internal.h" + +static int __base_pr(enum libperf_print_level level __maybe_unused, const char *format, + va_list args) +{ + return vfprintf(stderr, format, args); +} + +static libperf_print_fn_t __libperf_pr = __base_pr; + +__printf(2, 3) +void libperf_print(enum libperf_print_level level, const char *format, ...) +{ + va_list args; + + if (!__libperf_pr) + return; + + va_start(args, format); + __libperf_pr(level, format, args); + va_end(args); +} + +void libperf_init(libperf_print_fn_t fn) +{ + page_size = sysconf(_SC_PAGE_SIZE); + __libperf_pr = fn; +} diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c new file mode 100644 index 000000000000..f93f4e703e4c --- /dev/null +++ b/tools/lib/perf/cpumap.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct perf_cpu_map *perf_cpu_map__dummy_new(void) +{ + struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int)); + + if (cpus != NULL) { + cpus->nr = 1; + cpus->map[0] = -1; + refcount_set(&cpus->refcnt, 1); + } + + return cpus; +} + +static void cpu_map__delete(struct perf_cpu_map *map) +{ + if (map) { + WARN_ONCE(refcount_read(&map->refcnt) != 0, + "cpu_map refcnt unbalanced\n"); + free(map); + } +} + +struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map) +{ + if (map) + refcount_inc(&map->refcnt); + return map; +} + +void perf_cpu_map__put(struct perf_cpu_map *map) +{ + if (map && refcount_dec_and_test(&map->refcnt)) + cpu_map__delete(map); +} + +static struct perf_cpu_map *cpu_map__default_new(void) +{ + struct perf_cpu_map *cpus; + int nr_cpus; + + nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); + if (nr_cpus < 0) + return NULL; + + cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int)); + if (cpus != NULL) { + int i; + + for (i = 0; i < nr_cpus; ++i) + cpus->map[i] = i; + + cpus->nr = nr_cpus; + refcount_set(&cpus->refcnt, 1); + } + + return cpus; +} + +static int cmp_int(const void *a, const void *b) +{ + return *(const int *)a - *(const int*)b; +} + +static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus) +{ + size_t payload_size = nr_cpus * sizeof(int); + struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + payload_size); + int i, j; + + if (cpus != NULL) { + memcpy(cpus->map, tmp_cpus, payload_size); + qsort(cpus->map, nr_cpus, sizeof(int), cmp_int); + /* Remove dups */ + j = 0; + for (i = 0; i < nr_cpus; i++) { + if (i == 0 || cpus->map[i] != cpus->map[i - 1]) + cpus->map[j++] = cpus->map[i]; + } + cpus->nr = j; + assert(j <= nr_cpus); + refcount_set(&cpus->refcnt, 1); + } + + return cpus; +} + +struct perf_cpu_map *perf_cpu_map__read(FILE *file) +{ + struct perf_cpu_map *cpus = NULL; + int nr_cpus = 0; + int *tmp_cpus = NULL, *tmp; + int max_entries = 0; + int n, cpu, prev; + char sep; + + sep = 0; + prev = -1; + for (;;) { + n = fscanf(file, "%u%c", &cpu, &sep); + if (n <= 0) + break; + if (prev >= 0) { + int new_max = nr_cpus + cpu - prev - 1; + + WARN_ONCE(new_max >= MAX_NR_CPUS, "Perf can support %d CPUs. " + "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS); + + if (new_max >= max_entries) { + max_entries = new_max + MAX_NR_CPUS / 2; + tmp = realloc(tmp_cpus, max_entries * sizeof(int)); + if (tmp == NULL) + goto out_free_tmp; + tmp_cpus = tmp; + } + + while (++prev < cpu) + tmp_cpus[nr_cpus++] = prev; + } + if (nr_cpus == max_entries) { + max_entries += MAX_NR_CPUS; + tmp = realloc(tmp_cpus, max_entries * sizeof(int)); + if (tmp == NULL) + goto out_free_tmp; + tmp_cpus = tmp; + } + + tmp_cpus[nr_cpus++] = cpu; + if (n == 2 && sep == '-') + prev = cpu; + else + prev = -1; + if (n == 1 || sep == '\n') + break; + } + + if (nr_cpus > 0) + cpus = cpu_map__trim_new(nr_cpus, tmp_cpus); + else + cpus = cpu_map__default_new(); +out_free_tmp: + free(tmp_cpus); + return cpus; +} + +static struct perf_cpu_map *cpu_map__read_all_cpu_map(void) +{ + struct perf_cpu_map *cpus = NULL; + FILE *onlnf; + + onlnf = fopen("/sys/devices/system/cpu/online", "r"); + if (!onlnf) + return cpu_map__default_new(); + + cpus = perf_cpu_map__read(onlnf); + fclose(onlnf); + return cpus; +} + +struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list) +{ + struct perf_cpu_map *cpus = NULL; + unsigned long start_cpu, end_cpu = 0; + char *p = NULL; + int i, nr_cpus = 0; + int *tmp_cpus = NULL, *tmp; + int max_entries = 0; + + if (!cpu_list) + return cpu_map__read_all_cpu_map(); + + /* + * must handle the case of empty cpumap to cover + * TOPOLOGY header for NUMA nodes with no CPU + * ( e.g., because of CPU hotplug) + */ + if (!isdigit(*cpu_list) && *cpu_list != '\0') + goto out; + + while (isdigit(*cpu_list)) { + p = NULL; + start_cpu = strtoul(cpu_list, &p, 0); + if (start_cpu >= INT_MAX + || (*p != '\0' && *p != ',' && *p != '-')) + goto invalid; + + if (*p == '-') { + cpu_list = ++p; + p = NULL; + end_cpu = strtoul(cpu_list, &p, 0); + + if (end_cpu >= INT_MAX || (*p != '\0' && *p != ',')) + goto invalid; + + if (end_cpu < start_cpu) + goto invalid; + } else { + end_cpu = start_cpu; + } + + WARN_ONCE(end_cpu >= MAX_NR_CPUS, "Perf can support %d CPUs. " + "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS); + + for (; start_cpu <= end_cpu; start_cpu++) { + /* check for duplicates */ + for (i = 0; i < nr_cpus; i++) + if (tmp_cpus[i] == (int)start_cpu) + goto invalid; + + if (nr_cpus == max_entries) { + max_entries += MAX_NR_CPUS; + tmp = realloc(tmp_cpus, max_entries * sizeof(int)); + if (tmp == NULL) + goto invalid; + tmp_cpus = tmp; + } + tmp_cpus[nr_cpus++] = (int)start_cpu; + } + if (*p) + ++p; + + cpu_list = p; + } + + if (nr_cpus > 0) + cpus = cpu_map__trim_new(nr_cpus, tmp_cpus); + else if (*cpu_list != '\0') + cpus = cpu_map__default_new(); + else + cpus = perf_cpu_map__dummy_new(); +invalid: + free(tmp_cpus); +out: + return cpus; +} + +int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx) +{ + if (idx < cpus->nr) + return cpus->map[idx]; + + return -1; +} + +int perf_cpu_map__nr(const struct perf_cpu_map *cpus) +{ + return cpus ? cpus->nr : 1; +} + +bool perf_cpu_map__empty(const struct perf_cpu_map *map) +{ + return map ? map->map[0] == -1 : true; +} + +int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu) +{ + int i; + + for (i = 0; i < cpus->nr; ++i) { + if (cpus->map[i] == cpu) + return i; + } + + return -1; +} + +int perf_cpu_map__max(struct perf_cpu_map *map) +{ + int i, max = -1; + + for (i = 0; i < map->nr; i++) { + if (map->map[i] > max) + max = map->map[i]; + } + + return max; +} + +/* + * Merge two cpumaps + * + * orig either gets freed and replaced with a new map, or reused + * with no reference count change (similar to "realloc") + * other has its reference count increased. + */ + +struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, + struct perf_cpu_map *other) +{ + int *tmp_cpus; + int tmp_len; + int i, j, k; + struct perf_cpu_map *merged; + + if (!orig && !other) + return NULL; + if (!orig) { + perf_cpu_map__get(other); + return other; + } + if (!other) + return orig; + if (orig->nr == other->nr && + !memcmp(orig->map, other->map, orig->nr * sizeof(int))) + return orig; + + tmp_len = orig->nr + other->nr; + tmp_cpus = malloc(tmp_len * sizeof(int)); + if (!tmp_cpus) + return NULL; + + /* Standard merge algorithm from wikipedia */ + i = j = k = 0; + while (i < orig->nr && j < other->nr) { + if (orig->map[i] <= other->map[j]) { + if (orig->map[i] == other->map[j]) + j++; + tmp_cpus[k++] = orig->map[i++]; + } else + tmp_cpus[k++] = other->map[j++]; + } + + while (i < orig->nr) + tmp_cpus[k++] = orig->map[i++]; + + while (j < other->nr) + tmp_cpus[k++] = other->map[j++]; + assert(k <= tmp_len); + + merged = cpu_map__trim_new(k, tmp_cpus); + free(tmp_cpus); + perf_cpu_map__put(orig); + return merged; +} diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c new file mode 100644 index 000000000000..ae9e65aa2491 --- /dev/null +++ b/tools/lib/perf/evlist.c @@ -0,0 +1,641 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void perf_evlist__init(struct perf_evlist *evlist) +{ + int i; + + for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) + INIT_HLIST_HEAD(&evlist->heads[i]); + INIT_LIST_HEAD(&evlist->entries); + evlist->nr_entries = 0; + fdarray__init(&evlist->pollfd, 64); +} + +static void __perf_evlist__propagate_maps(struct perf_evlist *evlist, + struct perf_evsel *evsel) +{ + /* + * We already have cpus for evsel (via PMU sysfs) so + * keep it, if there's no target cpu list defined. + */ + if (!evsel->own_cpus || evlist->has_user_cpus) { + perf_cpu_map__put(evsel->cpus); + evsel->cpus = perf_cpu_map__get(evlist->cpus); + } else if (evsel->cpus != evsel->own_cpus) { + perf_cpu_map__put(evsel->cpus); + evsel->cpus = perf_cpu_map__get(evsel->own_cpus); + } + + perf_thread_map__put(evsel->threads); + evsel->threads = perf_thread_map__get(evlist->threads); + evlist->all_cpus = perf_cpu_map__merge(evlist->all_cpus, evsel->cpus); +} + +static void perf_evlist__propagate_maps(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + perf_evlist__for_each_evsel(evlist, evsel) + __perf_evlist__propagate_maps(evlist, evsel); +} + +void perf_evlist__add(struct perf_evlist *evlist, + struct perf_evsel *evsel) +{ + list_add_tail(&evsel->node, &evlist->entries); + evlist->nr_entries += 1; + __perf_evlist__propagate_maps(evlist, evsel); +} + +void perf_evlist__remove(struct perf_evlist *evlist, + struct perf_evsel *evsel) +{ + list_del_init(&evsel->node); + evlist->nr_entries -= 1; +} + +struct perf_evlist *perf_evlist__new(void) +{ + struct perf_evlist *evlist = zalloc(sizeof(*evlist)); + + if (evlist != NULL) + perf_evlist__init(evlist); + + return evlist; +} + +struct perf_evsel * +perf_evlist__next(struct perf_evlist *evlist, struct perf_evsel *prev) +{ + struct perf_evsel *next; + + if (!prev) { + next = list_first_entry(&evlist->entries, + struct perf_evsel, + node); + } else { + next = list_next_entry(prev, node); + } + + /* Empty list is noticed here so don't need checking on entry. */ + if (&next->node == &evlist->entries) + return NULL; + + return next; +} + +static void perf_evlist__purge(struct perf_evlist *evlist) +{ + struct perf_evsel *pos, *n; + + perf_evlist__for_each_entry_safe(evlist, n, pos) { + list_del_init(&pos->node); + perf_evsel__delete(pos); + } + + evlist->nr_entries = 0; +} + +void perf_evlist__exit(struct perf_evlist *evlist) +{ + perf_cpu_map__put(evlist->cpus); + perf_thread_map__put(evlist->threads); + evlist->cpus = NULL; + evlist->threads = NULL; + fdarray__exit(&evlist->pollfd); +} + +void perf_evlist__delete(struct perf_evlist *evlist) +{ + if (evlist == NULL) + return; + + perf_evlist__munmap(evlist); + perf_evlist__close(evlist); + perf_evlist__purge(evlist); + perf_evlist__exit(evlist); + free(evlist); +} + +void perf_evlist__set_maps(struct perf_evlist *evlist, + struct perf_cpu_map *cpus, + struct perf_thread_map *threads) +{ + /* + * Allow for the possibility that one or another of the maps isn't being + * changed i.e. don't put it. Note we are assuming the maps that are + * being applied are brand new and evlist is taking ownership of the + * original reference count of 1. If that is not the case it is up to + * the caller to increase the reference count. + */ + if (cpus != evlist->cpus) { + perf_cpu_map__put(evlist->cpus); + evlist->cpus = perf_cpu_map__get(cpus); + } + + if (threads != evlist->threads) { + perf_thread_map__put(evlist->threads); + evlist->threads = perf_thread_map__get(threads); + } + + perf_evlist__propagate_maps(evlist); +} + +int perf_evlist__open(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + int err; + + perf_evlist__for_each_entry(evlist, evsel) { + err = perf_evsel__open(evsel, evsel->cpus, evsel->threads); + if (err < 0) + goto out_err; + } + + return 0; + +out_err: + perf_evlist__close(evlist); + return err; +} + +void perf_evlist__close(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + perf_evlist__for_each_entry_reverse(evlist, evsel) + perf_evsel__close(evsel); +} + +void perf_evlist__enable(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + perf_evlist__for_each_entry(evlist, evsel) + perf_evsel__enable(evsel); +} + +void perf_evlist__disable(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + perf_evlist__for_each_entry(evlist, evsel) + perf_evsel__disable(evsel); +} + +u64 perf_evlist__read_format(struct perf_evlist *evlist) +{ + struct perf_evsel *first = perf_evlist__first(evlist); + + return first->attr.read_format; +} + +#define SID(e, x, y) xyarray__entry(e->sample_id, x, y) + +static void perf_evlist__id_hash(struct perf_evlist *evlist, + struct perf_evsel *evsel, + int cpu, int thread, u64 id) +{ + int hash; + struct perf_sample_id *sid = SID(evsel, cpu, thread); + + sid->id = id; + sid->evsel = evsel; + hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); + hlist_add_head(&sid->node, &evlist->heads[hash]); +} + +void perf_evlist__id_add(struct perf_evlist *evlist, + struct perf_evsel *evsel, + int cpu, int thread, u64 id) +{ + perf_evlist__id_hash(evlist, evsel, cpu, thread, id); + evsel->id[evsel->ids++] = id; +} + +int perf_evlist__id_add_fd(struct perf_evlist *evlist, + struct perf_evsel *evsel, + int cpu, int thread, int fd) +{ + u64 read_data[4] = { 0, }; + int id_idx = 1; /* The first entry is the counter value */ + u64 id; + int ret; + + ret = ioctl(fd, PERF_EVENT_IOC_ID, &id); + if (!ret) + goto add; + + if (errno != ENOTTY) + return -1; + + /* Legacy way to get event id.. All hail to old kernels! */ + + /* + * This way does not work with group format read, so bail + * out in that case. + */ + if (perf_evlist__read_format(evlist) & PERF_FORMAT_GROUP) + return -1; + + if (!(evsel->attr.read_format & PERF_FORMAT_ID) || + read(fd, &read_data, sizeof(read_data)) == -1) + return -1; + + if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + ++id_idx; + if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + ++id_idx; + + id = read_data[id_idx]; + +add: + perf_evlist__id_add(evlist, evsel, cpu, thread, id); + return 0; +} + +int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) +{ + int nr_cpus = perf_cpu_map__nr(evlist->cpus); + int nr_threads = perf_thread_map__nr(evlist->threads); + int nfds = 0; + struct perf_evsel *evsel; + + perf_evlist__for_each_entry(evlist, evsel) { + if (evsel->system_wide) + nfds += nr_cpus; + else + nfds += nr_cpus * nr_threads; + } + + if (fdarray__available_entries(&evlist->pollfd) < nfds && + fdarray__grow(&evlist->pollfd, nfds) < 0) + return -ENOMEM; + + return 0; +} + +int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, + void *ptr, short revent) +{ + int pos = fdarray__add(&evlist->pollfd, fd, revent | POLLERR | POLLHUP); + + if (pos >= 0) { + evlist->pollfd.priv[pos].ptr = ptr; + fcntl(fd, F_SETFL, O_NONBLOCK); + } + + return pos; +} + +static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd, + void *arg __maybe_unused) +{ + struct perf_mmap *map = fda->priv[fd].ptr; + + if (map) + perf_mmap__put(map); +} + +int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask) +{ + return fdarray__filter(&evlist->pollfd, revents_and_mask, + perf_evlist__munmap_filtered, NULL); +} + +int perf_evlist__poll(struct perf_evlist *evlist, int timeout) +{ + return fdarray__poll(&evlist->pollfd, timeout); +} + +static struct perf_mmap* perf_evlist__alloc_mmap(struct perf_evlist *evlist, bool overwrite) +{ + int i; + struct perf_mmap *map; + + map = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); + if (!map) + return NULL; + + for (i = 0; i < evlist->nr_mmaps; i++) { + struct perf_mmap *prev = i ? &map[i - 1] : NULL; + + /* + * When the perf_mmap() call is made we grab one refcount, plus + * one extra to let perf_mmap__consume() get the last + * events after all real references (perf_mmap__get()) are + * dropped. + * + * Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and + * thus does perf_mmap__get() on it. + */ + perf_mmap__init(&map[i], prev, overwrite, NULL); + } + + return map; +} + +static void perf_evlist__set_sid_idx(struct perf_evlist *evlist, + struct perf_evsel *evsel, int idx, int cpu, + int thread) +{ + struct perf_sample_id *sid = SID(evsel, cpu, thread); + + sid->idx = idx; + if (evlist->cpus && cpu >= 0) + sid->cpu = evlist->cpus->map[cpu]; + else + sid->cpu = -1; + if (!evsel->system_wide && evlist->threads && thread >= 0) + sid->tid = perf_thread_map__pid(evlist->threads, thread); + else + sid->tid = -1; +} + +static struct perf_mmap* +perf_evlist__mmap_cb_get(struct perf_evlist *evlist, bool overwrite, int idx) +{ + struct perf_mmap *maps; + + maps = overwrite ? evlist->mmap_ovw : evlist->mmap; + + if (!maps) { + maps = perf_evlist__alloc_mmap(evlist, overwrite); + if (!maps) + return NULL; + + if (overwrite) + evlist->mmap_ovw = maps; + else + evlist->mmap = maps; + } + + return &maps[idx]; +} + +#define FD(e, x, y) (*(int *) xyarray__entry(e->fd, x, y)) + +static int +perf_evlist__mmap_cb_mmap(struct perf_mmap *map, struct perf_mmap_param *mp, + int output, int cpu) +{ + return perf_mmap__mmap(map, mp, output, cpu); +} + +static void perf_evlist__set_mmap_first(struct perf_evlist *evlist, struct perf_mmap *map, + bool overwrite) +{ + if (overwrite) + evlist->mmap_ovw_first = map; + else + evlist->mmap_first = map; +} + +static int +mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, + int idx, struct perf_mmap_param *mp, int cpu_idx, + int thread, int *_output, int *_output_overwrite) +{ + int evlist_cpu = perf_cpu_map__cpu(evlist->cpus, cpu_idx); + struct perf_evsel *evsel; + int revent; + + perf_evlist__for_each_entry(evlist, evsel) { + bool overwrite = evsel->attr.write_backward; + struct perf_mmap *map; + int *output, fd, cpu; + + if (evsel->system_wide && thread) + continue; + + cpu = perf_cpu_map__idx(evsel->cpus, evlist_cpu); + if (cpu == -1) + continue; + + map = ops->get(evlist, overwrite, idx); + if (map == NULL) + return -ENOMEM; + + if (overwrite) { + mp->prot = PROT_READ; + output = _output_overwrite; + } else { + mp->prot = PROT_READ | PROT_WRITE; + output = _output; + } + + fd = FD(evsel, cpu, thread); + + if (*output == -1) { + *output = fd; + + /* + * The last one will be done at perf_mmap__consume(), so that we + * make sure we don't prevent tools from consuming every last event in + * the ring buffer. + * + * I.e. we can get the POLLHUP meaning that the fd doesn't exist + * anymore, but the last events for it are still in the ring buffer, + * waiting to be consumed. + * + * Tools can chose to ignore this at their own discretion, but the + * evlist layer can't just drop it when filtering events in + * perf_evlist__filter_pollfd(). + */ + refcount_set(&map->refcnt, 2); + + if (ops->mmap(map, mp, *output, evlist_cpu) < 0) + return -1; + + if (!idx) + perf_evlist__set_mmap_first(evlist, map, overwrite); + } else { + if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) + return -1; + + perf_mmap__get(map); + } + + revent = !overwrite ? POLLIN : 0; + + if (!evsel->system_wide && + perf_evlist__add_pollfd(evlist, fd, map, revent) < 0) { + perf_mmap__put(map); + return -1; + } + + if (evsel->attr.read_format & PERF_FORMAT_ID) { + if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread, + fd) < 0) + return -1; + perf_evlist__set_sid_idx(evlist, evsel, idx, cpu, + thread); + } + } + + return 0; +} + +static int +mmap_per_thread(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, + struct perf_mmap_param *mp) +{ + int thread; + int nr_threads = perf_thread_map__nr(evlist->threads); + + for (thread = 0; thread < nr_threads; thread++) { + int output = -1; + int output_overwrite = -1; + + if (ops->idx) + ops->idx(evlist, mp, thread, false); + + if (mmap_per_evsel(evlist, ops, thread, mp, 0, thread, + &output, &output_overwrite)) + goto out_unmap; + } + + return 0; + +out_unmap: + perf_evlist__munmap(evlist); + return -1; +} + +static int +mmap_per_cpu(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, + struct perf_mmap_param *mp) +{ + int nr_threads = perf_thread_map__nr(evlist->threads); + int nr_cpus = perf_cpu_map__nr(evlist->cpus); + int cpu, thread; + + for (cpu = 0; cpu < nr_cpus; cpu++) { + int output = -1; + int output_overwrite = -1; + + if (ops->idx) + ops->idx(evlist, mp, cpu, true); + + for (thread = 0; thread < nr_threads; thread++) { + if (mmap_per_evsel(evlist, ops, cpu, mp, cpu, + thread, &output, &output_overwrite)) + goto out_unmap; + } + } + + return 0; + +out_unmap: + perf_evlist__munmap(evlist); + return -1; +} + +static int perf_evlist__nr_mmaps(struct perf_evlist *evlist) +{ + int nr_mmaps; + + nr_mmaps = perf_cpu_map__nr(evlist->cpus); + if (perf_cpu_map__empty(evlist->cpus)) + nr_mmaps = perf_thread_map__nr(evlist->threads); + + return nr_mmaps; +} + +int perf_evlist__mmap_ops(struct perf_evlist *evlist, + struct perf_evlist_mmap_ops *ops, + struct perf_mmap_param *mp) +{ + struct perf_evsel *evsel; + const struct perf_cpu_map *cpus = evlist->cpus; + const struct perf_thread_map *threads = evlist->threads; + + if (!ops || !ops->get || !ops->mmap) + return -EINVAL; + + mp->mask = evlist->mmap_len - page_size - 1; + + evlist->nr_mmaps = perf_evlist__nr_mmaps(evlist); + + perf_evlist__for_each_entry(evlist, evsel) { + if ((evsel->attr.read_format & PERF_FORMAT_ID) && + evsel->sample_id == NULL && + perf_evsel__alloc_id(evsel, perf_cpu_map__nr(cpus), threads->nr) < 0) + return -ENOMEM; + } + + if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0) + return -ENOMEM; + + if (perf_cpu_map__empty(cpus)) + return mmap_per_thread(evlist, ops, mp); + + return mmap_per_cpu(evlist, ops, mp); +} + +int perf_evlist__mmap(struct perf_evlist *evlist, int pages) +{ + struct perf_mmap_param mp; + struct perf_evlist_mmap_ops ops = { + .get = perf_evlist__mmap_cb_get, + .mmap = perf_evlist__mmap_cb_mmap, + }; + + evlist->mmap_len = (pages + 1) * page_size; + + return perf_evlist__mmap_ops(evlist, &ops, &mp); +} + +void perf_evlist__munmap(struct perf_evlist *evlist) +{ + int i; + + if (evlist->mmap) { + for (i = 0; i < evlist->nr_mmaps; i++) + perf_mmap__munmap(&evlist->mmap[i]); + } + + if (evlist->mmap_ovw) { + for (i = 0; i < evlist->nr_mmaps; i++) + perf_mmap__munmap(&evlist->mmap_ovw[i]); + } + + zfree(&evlist->mmap); + zfree(&evlist->mmap_ovw); +} + +struct perf_mmap* +perf_evlist__next_mmap(struct perf_evlist *evlist, struct perf_mmap *map, + bool overwrite) +{ + if (map) + return map->next; + + return overwrite ? evlist->mmap_ovw_first : evlist->mmap_first; +} diff --git a/tools/lib/perf/evsel.c b/tools/lib/perf/evsel.c new file mode 100644 index 000000000000..4dc06289f4c7 --- /dev/null +++ b/tools/lib/perf/evsel.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr) +{ + INIT_LIST_HEAD(&evsel->node); + evsel->attr = *attr; +} + +struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) +{ + struct perf_evsel *evsel = zalloc(sizeof(*evsel)); + + if (evsel != NULL) + perf_evsel__init(evsel, attr); + + return evsel; +} + +void perf_evsel__delete(struct perf_evsel *evsel) +{ + free(evsel); +} + +#define FD(e, x, y) (*(int *) xyarray__entry(e->fd, x, y)) + +int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) +{ + evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); + + if (evsel->fd) { + int cpu, thread; + for (cpu = 0; cpu < ncpus; cpu++) { + for (thread = 0; thread < nthreads; thread++) { + FD(evsel, cpu, thread) = -1; + } + } + } + + return evsel->fd != NULL ? 0 : -ENOMEM; +} + +static int +sys_perf_event_open(struct perf_event_attr *attr, + pid_t pid, int cpu, int group_fd, + unsigned long flags) +{ + return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); +} + +int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, + struct perf_thread_map *threads) +{ + int cpu, thread, err = 0; + + if (cpus == NULL) { + static struct perf_cpu_map *empty_cpu_map; + + if (empty_cpu_map == NULL) { + empty_cpu_map = perf_cpu_map__dummy_new(); + if (empty_cpu_map == NULL) + return -ENOMEM; + } + + cpus = empty_cpu_map; + } + + if (threads == NULL) { + static struct perf_thread_map *empty_thread_map; + + if (empty_thread_map == NULL) { + empty_thread_map = perf_thread_map__new_dummy(); + if (empty_thread_map == NULL) + return -ENOMEM; + } + + threads = empty_thread_map; + } + + if (evsel->fd == NULL && + perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) + return -ENOMEM; + + for (cpu = 0; cpu < cpus->nr; cpu++) { + for (thread = 0; thread < threads->nr; thread++) { + int fd; + + fd = sys_perf_event_open(&evsel->attr, + threads->map[thread].pid, + cpus->map[cpu], -1, 0); + + if (fd < 0) + return -errno; + + FD(evsel, cpu, thread) = fd; + } + } + + return err; +} + +static void perf_evsel__close_fd_cpu(struct perf_evsel *evsel, int cpu) +{ + int thread; + + for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) { + if (FD(evsel, cpu, thread) >= 0) + close(FD(evsel, cpu, thread)); + FD(evsel, cpu, thread) = -1; + } +} + +void perf_evsel__close_fd(struct perf_evsel *evsel) +{ + int cpu; + + for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) + perf_evsel__close_fd_cpu(evsel, cpu); +} + +void perf_evsel__free_fd(struct perf_evsel *evsel) +{ + xyarray__delete(evsel->fd); + evsel->fd = NULL; +} + +void perf_evsel__close(struct perf_evsel *evsel) +{ + if (evsel->fd == NULL) + return; + + perf_evsel__close_fd(evsel); + perf_evsel__free_fd(evsel); +} + +void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu) +{ + if (evsel->fd == NULL) + return; + + perf_evsel__close_fd_cpu(evsel, cpu); +} + +int perf_evsel__read_size(struct perf_evsel *evsel) +{ + u64 read_format = evsel->attr.read_format; + int entry = sizeof(u64); /* value */ + int size = 0; + int nr = 1; + + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + size += sizeof(u64); + + if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + size += sizeof(u64); + + if (read_format & PERF_FORMAT_ID) + entry += sizeof(u64); + + if (read_format & PERF_FORMAT_GROUP) { + nr = evsel->nr_members; + size += sizeof(u64); + } + + size += entry * nr; + return size; +} + +int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, + struct perf_counts_values *count) +{ + size_t size = perf_evsel__read_size(evsel); + + memset(count, 0, sizeof(*count)); + + if (FD(evsel, cpu, thread) < 0) + return -EINVAL; + + if (readn(FD(evsel, cpu, thread), count->values, size) <= 0) + return -errno; + + return 0; +} + +static int perf_evsel__run_ioctl(struct perf_evsel *evsel, + int ioc, void *arg, + int cpu) +{ + int thread; + + for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) { + int fd = FD(evsel, cpu, thread), + err = ioctl(fd, ioc, arg); + + if (err) + return err; + } + + return 0; +} + +int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu) +{ + return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, cpu); +} + +int perf_evsel__enable(struct perf_evsel *evsel) +{ + int i; + int err = 0; + + for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++) + err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, i); + return err; +} + +int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu) +{ + return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, cpu); +} + +int perf_evsel__disable(struct perf_evsel *evsel) +{ + int i; + int err = 0; + + for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++) + err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, i); + return err; +} + +int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter) +{ + int err = 0, i; + + for (i = 0; i < evsel->cpus->nr && !err; i++) + err = perf_evsel__run_ioctl(evsel, + PERF_EVENT_IOC_SET_FILTER, + (void *)filter, i); + return err; +} + +struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) +{ + return evsel->cpus; +} + +struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel) +{ + return evsel->threads; +} + +struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel) +{ + return &evsel->attr; +} + +int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) +{ + if (ncpus == 0 || nthreads == 0) + return 0; + + if (evsel->system_wide) + nthreads = 1; + + evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); + if (evsel->sample_id == NULL) + return -ENOMEM; + + evsel->id = zalloc(ncpus * nthreads * sizeof(u64)); + if (evsel->id == NULL) { + xyarray__delete(evsel->sample_id); + evsel->sample_id = NULL; + return -ENOMEM; + } + + return 0; +} + +void perf_evsel__free_id(struct perf_evsel *evsel) +{ + xyarray__delete(evsel->sample_id); + evsel->sample_id = NULL; + zfree(&evsel->id); + evsel->ids = 0; +} diff --git a/tools/lib/perf/include/internal/cpumap.h b/tools/lib/perf/include/internal/cpumap.h new file mode 100644 index 000000000000..840d4032587b --- /dev/null +++ b/tools/lib/perf/include/internal/cpumap.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_INTERNAL_CPUMAP_H +#define __LIBPERF_INTERNAL_CPUMAP_H + +#include + +struct perf_cpu_map { + refcount_t refcnt; + int nr; + int map[]; +}; + +#ifndef MAX_NR_CPUS +#define MAX_NR_CPUS 2048 +#endif + +int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu); + +#endif /* __LIBPERF_INTERNAL_CPUMAP_H */ diff --git a/tools/lib/perf/include/internal/evlist.h b/tools/lib/perf/include/internal/evlist.h new file mode 100644 index 000000000000..74dc8c3f0b66 --- /dev/null +++ b/tools/lib/perf/include/internal/evlist.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_INTERNAL_EVLIST_H +#define __LIBPERF_INTERNAL_EVLIST_H + +#include +#include +#include + +#define PERF_EVLIST__HLIST_BITS 8 +#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) + +struct perf_cpu_map; +struct perf_thread_map; +struct perf_mmap_param; + +struct perf_evlist { + struct list_head entries; + int nr_entries; + bool has_user_cpus; + struct perf_cpu_map *cpus; + struct perf_cpu_map *all_cpus; + struct perf_thread_map *threads; + int nr_mmaps; + size_t mmap_len; + struct fdarray pollfd; + struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; + struct perf_mmap *mmap; + struct perf_mmap *mmap_ovw; + struct perf_mmap *mmap_first; + struct perf_mmap *mmap_ovw_first; +}; + +typedef void +(*perf_evlist_mmap__cb_idx_t)(struct perf_evlist*, struct perf_mmap_param*, int, bool); +typedef struct perf_mmap* +(*perf_evlist_mmap__cb_get_t)(struct perf_evlist*, bool, int); +typedef int +(*perf_evlist_mmap__cb_mmap_t)(struct perf_mmap*, struct perf_mmap_param*, int, int); + +struct perf_evlist_mmap_ops { + perf_evlist_mmap__cb_idx_t idx; + perf_evlist_mmap__cb_get_t get; + perf_evlist_mmap__cb_mmap_t mmap; +}; + +int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); +int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, + void *ptr, short revent); + +int perf_evlist__mmap_ops(struct perf_evlist *evlist, + struct perf_evlist_mmap_ops *ops, + struct perf_mmap_param *mp); + +void perf_evlist__init(struct perf_evlist *evlist); +void perf_evlist__exit(struct perf_evlist *evlist); + +/** + * __perf_evlist__for_each_entry - iterate thru all the evsels + * @list: list_head instance to iterate + * @evsel: struct perf_evsel iterator + */ +#define __perf_evlist__for_each_entry(list, evsel) \ + list_for_each_entry(evsel, list, node) + +/** + * evlist__for_each_entry - iterate thru all the evsels + * @evlist: perf_evlist instance to iterate + * @evsel: struct perf_evsel iterator + */ +#define perf_evlist__for_each_entry(evlist, evsel) \ + __perf_evlist__for_each_entry(&(evlist)->entries, evsel) + +/** + * __perf_evlist__for_each_entry_reverse - iterate thru all the evsels in reverse order + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __perf_evlist__for_each_entry_reverse(list, evsel) \ + list_for_each_entry_reverse(evsel, list, node) + +/** + * perf_evlist__for_each_entry_reverse - iterate thru all the evsels in reverse order + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define perf_evlist__for_each_entry_reverse(evlist, evsel) \ + __perf_evlist__for_each_entry_reverse(&(evlist)->entries, evsel) + +/** + * __perf_evlist__for_each_entry_safe - safely iterate thru all the evsels + * @list: list_head instance to iterate + * @tmp: struct evsel temp iterator + * @evsel: struct evsel iterator + */ +#define __perf_evlist__for_each_entry_safe(list, tmp, evsel) \ + list_for_each_entry_safe(evsel, tmp, list, node) + +/** + * perf_evlist__for_each_entry_safe - safely iterate thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + * @tmp: struct evsel temp iterator + */ +#define perf_evlist__for_each_entry_safe(evlist, tmp, evsel) \ + __perf_evlist__for_each_entry_safe(&(evlist)->entries, tmp, evsel) + +static inline struct perf_evsel *perf_evlist__first(struct perf_evlist *evlist) +{ + return list_entry(evlist->entries.next, struct perf_evsel, node); +} + +static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) +{ + return list_entry(evlist->entries.prev, struct perf_evsel, node); +} + +u64 perf_evlist__read_format(struct perf_evlist *evlist); + +void perf_evlist__id_add(struct perf_evlist *evlist, + struct perf_evsel *evsel, + int cpu, int thread, u64 id); + +int perf_evlist__id_add_fd(struct perf_evlist *evlist, + struct perf_evsel *evsel, + int cpu, int thread, int fd); + +#endif /* __LIBPERF_INTERNAL_EVLIST_H */ diff --git a/tools/lib/perf/include/internal/evsel.h b/tools/lib/perf/include/internal/evsel.h new file mode 100644 index 000000000000..1ffd083b235e --- /dev/null +++ b/tools/lib/perf/include/internal/evsel.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_INTERNAL_EVSEL_H +#define __LIBPERF_INTERNAL_EVSEL_H + +#include +#include +#include +#include + +struct perf_cpu_map; +struct perf_thread_map; +struct xyarray; + +/* + * Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are + * more than one entry in the evlist. + */ +struct perf_sample_id { + struct hlist_node node; + u64 id; + struct perf_evsel *evsel; + /* + * 'idx' will be used for AUX area sampling. A sample will have AUX area + * data that will be queued for decoding, where there are separate + * queues for each CPU (per-cpu tracing) or task (per-thread tracing). + * The sample ID can be used to lookup 'idx' which is effectively the + * queue number. + */ + int idx; + int cpu; + pid_t tid; + + /* Holds total ID period value for PERF_SAMPLE_READ processing. */ + u64 period; +}; + +struct perf_evsel { + struct list_head node; + struct perf_event_attr attr; + struct perf_cpu_map *cpus; + struct perf_cpu_map *own_cpus; + struct perf_thread_map *threads; + struct xyarray *fd; + struct xyarray *sample_id; + u64 *id; + u32 ids; + + /* parse modifier helper */ + int nr_members; + bool system_wide; +}; + +void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr); +int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); +void perf_evsel__close_fd(struct perf_evsel *evsel); +void perf_evsel__free_fd(struct perf_evsel *evsel); +int perf_evsel__read_size(struct perf_evsel *evsel); +int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter); + +int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); +void perf_evsel__free_id(struct perf_evsel *evsel); + +#endif /* __LIBPERF_INTERNAL_EVSEL_H */ diff --git a/tools/lib/perf/include/internal/lib.h b/tools/lib/perf/include/internal/lib.h new file mode 100644 index 000000000000..5175d491b2d4 --- /dev/null +++ b/tools/lib/perf/include/internal/lib.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_INTERNAL_LIB_H +#define __LIBPERF_INTERNAL_LIB_H + +#include + +extern unsigned int page_size; + +ssize_t readn(int fd, void *buf, size_t n); +ssize_t writen(int fd, const void *buf, size_t n); + +#endif /* __LIBPERF_INTERNAL_CPUMAP_H */ diff --git a/tools/lib/perf/include/internal/mmap.h b/tools/lib/perf/include/internal/mmap.h new file mode 100644 index 000000000000..be7556e0a2b2 --- /dev/null +++ b/tools/lib/perf/include/internal/mmap.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_INTERNAL_MMAP_H +#define __LIBPERF_INTERNAL_MMAP_H + +#include +#include +#include +#include + +/* perf sample has 16 bits size limit */ +#define PERF_SAMPLE_MAX_SIZE (1 << 16) + +struct perf_mmap; + +typedef void (*libperf_unmap_cb_t)(struct perf_mmap *map); + +/** + * struct perf_mmap - perf's ring buffer mmap details + * + * @refcnt - e.g. code using PERF_EVENT_IOC_SET_OUTPUT to share this + */ +struct perf_mmap { + void *base; + int mask; + int fd; + int cpu; + refcount_t refcnt; + u64 prev; + u64 start; + u64 end; + bool overwrite; + u64 flush; + libperf_unmap_cb_t unmap_cb; + char event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8); + struct perf_mmap *next; +}; + +struct perf_mmap_param { + int prot; + int mask; +}; + +size_t perf_mmap__mmap_len(struct perf_mmap *map); + +void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, + bool overwrite, libperf_unmap_cb_t unmap_cb); +int perf_mmap__mmap(struct perf_mmap *map, struct perf_mmap_param *mp, + int fd, int cpu); +void perf_mmap__munmap(struct perf_mmap *map); +void perf_mmap__get(struct perf_mmap *map); +void perf_mmap__put(struct perf_mmap *map); + +u64 perf_mmap__read_head(struct perf_mmap *map); + +#endif /* __LIBPERF_INTERNAL_MMAP_H */ diff --git a/tools/lib/perf/include/internal/tests.h b/tools/lib/perf/include/internal/tests.h new file mode 100644 index 000000000000..2093e8868a67 --- /dev/null +++ b/tools/lib/perf/include/internal/tests.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_INTERNAL_TESTS_H +#define __LIBPERF_INTERNAL_TESTS_H + +#include + +int tests_failed; + +#define __T_START \ +do { \ + fprintf(stdout, "- running %s...", __FILE__); \ + fflush(NULL); \ + tests_failed = 0; \ +} while (0) + +#define __T_END \ +do { \ + if (tests_failed) \ + fprintf(stdout, " FAILED (%d)\n", tests_failed); \ + else \ + fprintf(stdout, "OK\n"); \ +} while (0) + +#define __T(text, cond) \ +do { \ + if (!(cond)) { \ + fprintf(stderr, "FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ + tests_failed++; \ + return -1; \ + } \ +} while (0) + +#endif /* __LIBPERF_INTERNAL_TESTS_H */ diff --git a/tools/lib/perf/include/internal/threadmap.h b/tools/lib/perf/include/internal/threadmap.h new file mode 100644 index 000000000000..df748baf9eda --- /dev/null +++ b/tools/lib/perf/include/internal/threadmap.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_INTERNAL_THREADMAP_H +#define __LIBPERF_INTERNAL_THREADMAP_H + +#include +#include +#include + +struct thread_map_data { + pid_t pid; + char *comm; +}; + +struct perf_thread_map { + refcount_t refcnt; + int nr; + int err_thread; + struct thread_map_data map[]; +}; + +struct perf_thread_map *perf_thread_map__realloc(struct perf_thread_map *map, int nr); + +#endif /* __LIBPERF_INTERNAL_THREADMAP_H */ diff --git a/tools/lib/perf/include/internal/xyarray.h b/tools/lib/perf/include/internal/xyarray.h new file mode 100644 index 000000000000..51e35d6c8ec4 --- /dev/null +++ b/tools/lib/perf/include/internal/xyarray.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_INTERNAL_XYARRAY_H +#define __LIBPERF_INTERNAL_XYARRAY_H + +#include +#include + +struct xyarray { + size_t row_size; + size_t entry_size; + size_t entries; + size_t max_x; + size_t max_y; + char contents[] __aligned(8); +}; + +struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size); +void xyarray__delete(struct xyarray *xy); +void xyarray__reset(struct xyarray *xy); + +static inline void *xyarray__entry(struct xyarray *xy, int x, int y) +{ + return &xy->contents[x * xy->row_size + y * xy->entry_size]; +} + +static inline int xyarray__max_y(struct xyarray *xy) +{ + return xy->max_y; +} + +static inline int xyarray__max_x(struct xyarray *xy) +{ + return xy->max_x; +} + +#endif /* __LIBPERF_INTERNAL_XYARRAY_H */ diff --git a/tools/lib/perf/include/perf/core.h b/tools/lib/perf/include/perf/core.h new file mode 100644 index 000000000000..a3f6d68edad7 --- /dev/null +++ b/tools/lib/perf/include/perf/core.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_CORE_H +#define __LIBPERF_CORE_H + +#include + +#ifndef LIBPERF_API +#define LIBPERF_API __attribute__((visibility("default"))) +#endif + +enum libperf_print_level { + LIBPERF_ERR, + LIBPERF_WARN, + LIBPERF_INFO, + LIBPERF_DEBUG, + LIBPERF_DEBUG2, + LIBPERF_DEBUG3, +}; + +typedef int (*libperf_print_fn_t)(enum libperf_print_level level, + const char *, va_list ap); + +LIBPERF_API void libperf_init(libperf_print_fn_t fn); + +#endif /* __LIBPERF_CORE_H */ diff --git a/tools/lib/perf/include/perf/cpumap.h b/tools/lib/perf/include/perf/cpumap.h new file mode 100644 index 000000000000..6a17ad730cbc --- /dev/null +++ b/tools/lib/perf/include/perf/cpumap.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_CPUMAP_H +#define __LIBPERF_CPUMAP_H + +#include +#include +#include + +struct perf_cpu_map; + +LIBPERF_API struct perf_cpu_map *perf_cpu_map__dummy_new(void); +LIBPERF_API struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list); +LIBPERF_API struct perf_cpu_map *perf_cpu_map__read(FILE *file); +LIBPERF_API struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map); +LIBPERF_API struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, + struct perf_cpu_map *other); +LIBPERF_API void perf_cpu_map__put(struct perf_cpu_map *map); +LIBPERF_API int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx); +LIBPERF_API int perf_cpu_map__nr(const struct perf_cpu_map *cpus); +LIBPERF_API bool perf_cpu_map__empty(const struct perf_cpu_map *map); +LIBPERF_API int perf_cpu_map__max(struct perf_cpu_map *map); + +#define perf_cpu_map__for_each_cpu(cpu, idx, cpus) \ + for ((idx) = 0, (cpu) = perf_cpu_map__cpu(cpus, idx); \ + (idx) < perf_cpu_map__nr(cpus); \ + (idx)++, (cpu) = perf_cpu_map__cpu(cpus, idx)) + +#endif /* __LIBPERF_CPUMAP_H */ diff --git a/tools/lib/perf/include/perf/event.h b/tools/lib/perf/include/perf/event.h new file mode 100644 index 000000000000..18106899cb4e --- /dev/null +++ b/tools/lib/perf/include/perf/event.h @@ -0,0 +1,385 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_EVENT_H +#define __LIBPERF_EVENT_H + +#include +#include +#include +#include +#include /* pid_t */ + +struct perf_record_mmap { + struct perf_event_header header; + __u32 pid, tid; + __u64 start; + __u64 len; + __u64 pgoff; + char filename[PATH_MAX]; +}; + +struct perf_record_mmap2 { + struct perf_event_header header; + __u32 pid, tid; + __u64 start; + __u64 len; + __u64 pgoff; + __u32 maj; + __u32 min; + __u64 ino; + __u64 ino_generation; + __u32 prot; + __u32 flags; + char filename[PATH_MAX]; +}; + +struct perf_record_comm { + struct perf_event_header header; + __u32 pid, tid; + char comm[16]; +}; + +struct perf_record_namespaces { + struct perf_event_header header; + __u32 pid, tid; + __u64 nr_namespaces; + struct perf_ns_link_info link_info[]; +}; + +struct perf_record_fork { + struct perf_event_header header; + __u32 pid, ppid; + __u32 tid, ptid; + __u64 time; +}; + +struct perf_record_lost { + struct perf_event_header header; + __u64 id; + __u64 lost; +}; + +struct perf_record_lost_samples { + struct perf_event_header header; + __u64 lost; +}; + +/* + * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID + */ +struct perf_record_read { + struct perf_event_header header; + __u32 pid, tid; + __u64 value; + __u64 time_enabled; + __u64 time_running; + __u64 id; +}; + +struct perf_record_throttle { + struct perf_event_header header; + __u64 time; + __u64 id; + __u64 stream_id; +}; + +#ifndef KSYM_NAME_LEN +#define KSYM_NAME_LEN 256 +#endif + +struct perf_record_ksymbol { + struct perf_event_header header; + __u64 addr; + __u32 len; + __u16 ksym_type; + __u16 flags; + char name[KSYM_NAME_LEN]; +}; + +struct perf_record_bpf_event { + struct perf_event_header header; + __u16 type; + __u16 flags; + __u32 id; + + /* for bpf_prog types */ + __u8 tag[BPF_TAG_SIZE]; // prog tag +}; + +struct perf_record_sample { + struct perf_event_header header; + __u64 array[]; +}; + +struct perf_record_switch { + struct perf_event_header header; + __u32 next_prev_pid; + __u32 next_prev_tid; +}; + +struct perf_record_header_attr { + struct perf_event_header header; + struct perf_event_attr attr; + __u64 id[]; +}; + +enum { + PERF_CPU_MAP__CPUS = 0, + PERF_CPU_MAP__MASK = 1, +}; + +struct cpu_map_entries { + __u16 nr; + __u16 cpu[]; +}; + +struct perf_record_record_cpu_map { + __u16 nr; + __u16 long_size; + unsigned long mask[]; +}; + +struct perf_record_cpu_map_data { + __u16 type; + char data[]; +}; + +struct perf_record_cpu_map { + struct perf_event_header header; + struct perf_record_cpu_map_data data; +}; + +enum { + PERF_EVENT_UPDATE__UNIT = 0, + PERF_EVENT_UPDATE__SCALE = 1, + PERF_EVENT_UPDATE__NAME = 2, + PERF_EVENT_UPDATE__CPUS = 3, +}; + +struct perf_record_event_update_cpus { + struct perf_record_cpu_map_data cpus; +}; + +struct perf_record_event_update_scale { + double scale; +}; + +struct perf_record_event_update { + struct perf_event_header header; + __u64 type; + __u64 id; + char data[]; +}; + +#define MAX_EVENT_NAME 64 + +struct perf_trace_event_type { + __u64 event_id; + char name[MAX_EVENT_NAME]; +}; + +struct perf_record_header_event_type { + struct perf_event_header header; + struct perf_trace_event_type event_type; +}; + +struct perf_record_header_tracing_data { + struct perf_event_header header; + __u32 size; +}; + +struct perf_record_header_build_id { + struct perf_event_header header; + pid_t pid; + __u8 build_id[24]; + char filename[]; +}; + +struct id_index_entry { + __u64 id; + __u64 idx; + __u64 cpu; + __u64 tid; +}; + +struct perf_record_id_index { + struct perf_event_header header; + __u64 nr; + struct id_index_entry entries[0]; +}; + +struct perf_record_auxtrace_info { + struct perf_event_header header; + __u32 type; + __u32 reserved__; /* For alignment */ + __u64 priv[]; +}; + +struct perf_record_auxtrace { + struct perf_event_header header; + __u64 size; + __u64 offset; + __u64 reference; + __u32 idx; + __u32 tid; + __u32 cpu; + __u32 reserved__; /* For alignment */ +}; + +#define MAX_AUXTRACE_ERROR_MSG 64 + +struct perf_record_auxtrace_error { + struct perf_event_header header; + __u32 type; + __u32 code; + __u32 cpu; + __u32 pid; + __u32 tid; + __u32 fmt; + __u64 ip; + __u64 time; + char msg[MAX_AUXTRACE_ERROR_MSG]; +}; + +struct perf_record_aux { + struct perf_event_header header; + __u64 aux_offset; + __u64 aux_size; + __u64 flags; +}; + +struct perf_record_itrace_start { + struct perf_event_header header; + __u32 pid; + __u32 tid; +}; + +struct perf_record_thread_map_entry { + __u64 pid; + char comm[16]; +}; + +struct perf_record_thread_map { + struct perf_event_header header; + __u64 nr; + struct perf_record_thread_map_entry entries[]; +}; + +enum { + PERF_STAT_CONFIG_TERM__AGGR_MODE = 0, + PERF_STAT_CONFIG_TERM__INTERVAL = 1, + PERF_STAT_CONFIG_TERM__SCALE = 2, + PERF_STAT_CONFIG_TERM__MAX = 3, +}; + +struct perf_record_stat_config_entry { + __u64 tag; + __u64 val; +}; + +struct perf_record_stat_config { + struct perf_event_header header; + __u64 nr; + struct perf_record_stat_config_entry data[]; +}; + +struct perf_record_stat { + struct perf_event_header header; + + __u64 id; + __u32 cpu; + __u32 thread; + + union { + struct { + __u64 val; + __u64 ena; + __u64 run; + }; + __u64 values[3]; + }; +}; + +struct perf_record_stat_round { + struct perf_event_header header; + __u64 type; + __u64 time; +}; + +struct perf_record_time_conv { + struct perf_event_header header; + __u64 time_shift; + __u64 time_mult; + __u64 time_zero; +}; + +struct perf_record_header_feature { + struct perf_event_header header; + __u64 feat_id; + char data[]; +}; + +struct perf_record_compressed { + struct perf_event_header header; + char data[]; +}; + +enum perf_user_event_type { /* above any possible kernel type */ + PERF_RECORD_USER_TYPE_START = 64, + PERF_RECORD_HEADER_ATTR = 64, + PERF_RECORD_HEADER_EVENT_TYPE = 65, /* deprecated */ + PERF_RECORD_HEADER_TRACING_DATA = 66, + PERF_RECORD_HEADER_BUILD_ID = 67, + PERF_RECORD_FINISHED_ROUND = 68, + PERF_RECORD_ID_INDEX = 69, + PERF_RECORD_AUXTRACE_INFO = 70, + PERF_RECORD_AUXTRACE = 71, + PERF_RECORD_AUXTRACE_ERROR = 72, + PERF_RECORD_THREAD_MAP = 73, + PERF_RECORD_CPU_MAP = 74, + PERF_RECORD_STAT_CONFIG = 75, + PERF_RECORD_STAT = 76, + PERF_RECORD_STAT_ROUND = 77, + PERF_RECORD_EVENT_UPDATE = 78, + PERF_RECORD_TIME_CONV = 79, + PERF_RECORD_HEADER_FEATURE = 80, + PERF_RECORD_COMPRESSED = 81, + PERF_RECORD_HEADER_MAX +}; + +union perf_event { + struct perf_event_header header; + struct perf_record_mmap mmap; + struct perf_record_mmap2 mmap2; + struct perf_record_comm comm; + struct perf_record_namespaces namespaces; + struct perf_record_fork fork; + struct perf_record_lost lost; + struct perf_record_lost_samples lost_samples; + struct perf_record_read read; + struct perf_record_throttle throttle; + struct perf_record_sample sample; + struct perf_record_bpf_event bpf; + struct perf_record_ksymbol ksymbol; + struct perf_record_header_attr attr; + struct perf_record_event_update event_update; + struct perf_record_header_event_type event_type; + struct perf_record_header_tracing_data tracing_data; + struct perf_record_header_build_id build_id; + struct perf_record_id_index id_index; + struct perf_record_auxtrace_info auxtrace_info; + struct perf_record_auxtrace auxtrace; + struct perf_record_auxtrace_error auxtrace_error; + struct perf_record_aux aux; + struct perf_record_itrace_start itrace_start; + struct perf_record_switch context_switch; + struct perf_record_thread_map thread_map; + struct perf_record_cpu_map cpu_map; + struct perf_record_stat_config stat_config; + struct perf_record_stat stat; + struct perf_record_stat_round stat_round; + struct perf_record_time_conv time_conv; + struct perf_record_header_feature feat; + struct perf_record_compressed pack; +}; + +#endif /* __LIBPERF_EVENT_H */ diff --git a/tools/lib/perf/include/perf/evlist.h b/tools/lib/perf/include/perf/evlist.h new file mode 100644 index 000000000000..0a7479dc13bf --- /dev/null +++ b/tools/lib/perf/include/perf/evlist.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_EVLIST_H +#define __LIBPERF_EVLIST_H + +#include +#include + +struct perf_evlist; +struct perf_evsel; +struct perf_cpu_map; +struct perf_thread_map; + +LIBPERF_API void perf_evlist__add(struct perf_evlist *evlist, + struct perf_evsel *evsel); +LIBPERF_API void perf_evlist__remove(struct perf_evlist *evlist, + struct perf_evsel *evsel); +LIBPERF_API struct perf_evlist *perf_evlist__new(void); +LIBPERF_API void perf_evlist__delete(struct perf_evlist *evlist); +LIBPERF_API struct perf_evsel* perf_evlist__next(struct perf_evlist *evlist, + struct perf_evsel *evsel); +LIBPERF_API int perf_evlist__open(struct perf_evlist *evlist); +LIBPERF_API void perf_evlist__close(struct perf_evlist *evlist); +LIBPERF_API void perf_evlist__enable(struct perf_evlist *evlist); +LIBPERF_API void perf_evlist__disable(struct perf_evlist *evlist); + +#define perf_evlist__for_each_evsel(evlist, pos) \ + for ((pos) = perf_evlist__next((evlist), NULL); \ + (pos) != NULL; \ + (pos) = perf_evlist__next((evlist), (pos))) + +LIBPERF_API void perf_evlist__set_maps(struct perf_evlist *evlist, + struct perf_cpu_map *cpus, + struct perf_thread_map *threads); +LIBPERF_API int perf_evlist__poll(struct perf_evlist *evlist, int timeout); +LIBPERF_API int perf_evlist__filter_pollfd(struct perf_evlist *evlist, + short revents_and_mask); + +LIBPERF_API int perf_evlist__mmap(struct perf_evlist *evlist, int pages); +LIBPERF_API void perf_evlist__munmap(struct perf_evlist *evlist); + +LIBPERF_API struct perf_mmap *perf_evlist__next_mmap(struct perf_evlist *evlist, + struct perf_mmap *map, + bool overwrite); +#define perf_evlist__for_each_mmap(evlist, pos, overwrite) \ + for ((pos) = perf_evlist__next_mmap((evlist), NULL, overwrite); \ + (pos) != NULL; \ + (pos) = perf_evlist__next_mmap((evlist), (pos), overwrite)) + +#endif /* __LIBPERF_EVLIST_H */ diff --git a/tools/lib/perf/include/perf/evsel.h b/tools/lib/perf/include/perf/evsel.h new file mode 100644 index 000000000000..c82ec39a4ad0 --- /dev/null +++ b/tools/lib/perf/include/perf/evsel.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_EVSEL_H +#define __LIBPERF_EVSEL_H + +#include +#include + +struct perf_evsel; +struct perf_event_attr; +struct perf_cpu_map; +struct perf_thread_map; + +struct perf_counts_values { + union { + struct { + uint64_t val; + uint64_t ena; + uint64_t run; + }; + uint64_t values[3]; + }; +}; + +LIBPERF_API struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr); +LIBPERF_API void perf_evsel__delete(struct perf_evsel *evsel); +LIBPERF_API int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, + struct perf_thread_map *threads); +LIBPERF_API void perf_evsel__close(struct perf_evsel *evsel); +LIBPERF_API void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu); +LIBPERF_API int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, + struct perf_counts_values *count); +LIBPERF_API int perf_evsel__enable(struct perf_evsel *evsel); +LIBPERF_API int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu); +LIBPERF_API int perf_evsel__disable(struct perf_evsel *evsel); +LIBPERF_API int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu); +LIBPERF_API struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel); +LIBPERF_API struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel); +LIBPERF_API struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel); + +#endif /* __LIBPERF_EVSEL_H */ diff --git a/tools/lib/perf/include/perf/mmap.h b/tools/lib/perf/include/perf/mmap.h new file mode 100644 index 000000000000..9508ad90d8b9 --- /dev/null +++ b/tools/lib/perf/include/perf/mmap.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_MMAP_H +#define __LIBPERF_MMAP_H + +#include + +struct perf_mmap; +union perf_event; + +LIBPERF_API void perf_mmap__consume(struct perf_mmap *map); +LIBPERF_API int perf_mmap__read_init(struct perf_mmap *map); +LIBPERF_API void perf_mmap__read_done(struct perf_mmap *map); +LIBPERF_API union perf_event *perf_mmap__read_event(struct perf_mmap *map); + +#endif /* __LIBPERF_MMAP_H */ diff --git a/tools/lib/perf/include/perf/threadmap.h b/tools/lib/perf/include/perf/threadmap.h new file mode 100644 index 000000000000..a7c50de8d010 --- /dev/null +++ b/tools/lib/perf/include/perf/threadmap.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_THREADMAP_H +#define __LIBPERF_THREADMAP_H + +#include +#include + +struct perf_thread_map; + +LIBPERF_API struct perf_thread_map *perf_thread_map__new_dummy(void); + +LIBPERF_API void perf_thread_map__set_pid(struct perf_thread_map *map, int thread, pid_t pid); +LIBPERF_API char *perf_thread_map__comm(struct perf_thread_map *map, int thread); +LIBPERF_API int perf_thread_map__nr(struct perf_thread_map *threads); +LIBPERF_API pid_t perf_thread_map__pid(struct perf_thread_map *map, int thread); + +LIBPERF_API struct perf_thread_map *perf_thread_map__get(struct perf_thread_map *map); +LIBPERF_API void perf_thread_map__put(struct perf_thread_map *map); + +#endif /* __LIBPERF_THREADMAP_H */ diff --git a/tools/lib/perf/internal.h b/tools/lib/perf/internal.h new file mode 100644 index 000000000000..2c27e158de6b --- /dev/null +++ b/tools/lib/perf/internal.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LIBPERF_INTERNAL_H +#define __LIBPERF_INTERNAL_H + +#include + +void libperf_print(enum libperf_print_level level, + const char *format, ...) + __attribute__((format(printf, 2, 3))); + +#define __pr(level, fmt, ...) \ +do { \ + libperf_print(level, "libperf: " fmt, ##__VA_ARGS__); \ +} while (0) + +#define pr_err(fmt, ...) __pr(LIBPERF_ERR, fmt, ##__VA_ARGS__) +#define pr_warning(fmt, ...) __pr(LIBPERF_WARN, fmt, ##__VA_ARGS__) +#define pr_info(fmt, ...) __pr(LIBPERF_INFO, fmt, ##__VA_ARGS__) +#define pr_debug(fmt, ...) __pr(LIBPERF_DEBUG, fmt, ##__VA_ARGS__) +#define pr_debug2(fmt, ...) __pr(LIBPERF_DEBUG2, fmt, ##__VA_ARGS__) +#define pr_debug3(fmt, ...) __pr(LIBPERF_DEBUG3, fmt, ##__VA_ARGS__) + +#endif /* __LIBPERF_INTERNAL_H */ diff --git a/tools/lib/perf/lib.c b/tools/lib/perf/lib.c new file mode 100644 index 000000000000..18658931fc71 --- /dev/null +++ b/tools/lib/perf/lib.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +unsigned int page_size; + +static ssize_t ion(bool is_read, int fd, void *buf, size_t n) +{ + void *buf_start = buf; + size_t left = n; + + while (left) { + /* buf must be treated as const if !is_read. */ + ssize_t ret = is_read ? read(fd, buf, left) : + write(fd, buf, left); + + if (ret < 0 && errno == EINTR) + continue; + if (ret <= 0) + return ret; + + left -= ret; + buf += ret; + } + + BUG_ON((size_t)(buf - buf_start) != n); + return n; +} + +/* + * Read exactly 'n' bytes or return an error. + */ +ssize_t readn(int fd, void *buf, size_t n) +{ + return ion(true, fd, buf, n); +} + +/* + * Write exactly 'n' bytes or return an error. + */ +ssize_t writen(int fd, const void *buf, size_t n) +{ + /* ion does not modify buf. */ + return ion(false, fd, (void *)buf, n); +} diff --git a/tools/lib/perf/libperf.map b/tools/lib/perf/libperf.map new file mode 100644 index 000000000000..7be1af8a546c --- /dev/null +++ b/tools/lib/perf/libperf.map @@ -0,0 +1,51 @@ +LIBPERF_0.0.1 { + global: + libperf_init; + perf_cpu_map__dummy_new; + perf_cpu_map__get; + perf_cpu_map__put; + perf_cpu_map__new; + perf_cpu_map__read; + perf_cpu_map__nr; + perf_cpu_map__cpu; + perf_cpu_map__empty; + perf_cpu_map__max; + perf_thread_map__new_dummy; + perf_thread_map__set_pid; + perf_thread_map__comm; + perf_thread_map__nr; + perf_thread_map__pid; + perf_thread_map__get; + perf_thread_map__put; + perf_evsel__new; + perf_evsel__delete; + perf_evsel__enable; + perf_evsel__disable; + perf_evsel__open; + perf_evsel__close; + perf_evsel__read; + perf_evsel__cpus; + perf_evsel__threads; + perf_evsel__attr; + perf_evlist__new; + perf_evlist__delete; + perf_evlist__open; + perf_evlist__close; + perf_evlist__enable; + perf_evlist__disable; + perf_evlist__add; + perf_evlist__remove; + perf_evlist__next; + perf_evlist__set_maps; + perf_evlist__poll; + perf_evlist__mmap; + perf_evlist__munmap; + perf_evlist__filter_pollfd; + perf_evlist__next_mmap; + perf_mmap__consume; + perf_mmap__read_init; + perf_mmap__read_done; + perf_mmap__read_event; + local: + *; +}; diff --git a/tools/lib/perf/libperf.pc.template b/tools/lib/perf/libperf.pc.template new file mode 100644 index 000000000000..117e4a237b55 --- /dev/null +++ b/tools/lib/perf/libperf.pc.template @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) + +prefix=@PREFIX@ +libdir=@LIBDIR@ +includedir=${prefix}/include + +Name: libperf +Description: perf library +Version: @VERSION@ +Libs: -L${libdir} -lperf +Cflags: -I${includedir} diff --git a/tools/lib/perf/mmap.c b/tools/lib/perf/mmap.c new file mode 100644 index 000000000000..79d5ed6c38cc --- /dev/null +++ b/tools/lib/perf/mmap.c @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, + bool overwrite, libperf_unmap_cb_t unmap_cb) +{ + map->fd = -1; + map->overwrite = overwrite; + map->unmap_cb = unmap_cb; + refcount_set(&map->refcnt, 0); + if (prev) + prev->next = map; +} + +size_t perf_mmap__mmap_len(struct perf_mmap *map) +{ + return map->mask + 1 + page_size; +} + +int perf_mmap__mmap(struct perf_mmap *map, struct perf_mmap_param *mp, + int fd, int cpu) +{ + map->prev = 0; + map->mask = mp->mask; + map->base = mmap(NULL, perf_mmap__mmap_len(map), mp->prot, + MAP_SHARED, fd, 0); + if (map->base == MAP_FAILED) { + map->base = NULL; + return -1; + } + + map->fd = fd; + map->cpu = cpu; + return 0; +} + +void perf_mmap__munmap(struct perf_mmap *map) +{ + if (map && map->base != NULL) { + munmap(map->base, perf_mmap__mmap_len(map)); + map->base = NULL; + map->fd = -1; + refcount_set(&map->refcnt, 0); + } + if (map && map->unmap_cb) + map->unmap_cb(map); +} + +void perf_mmap__get(struct perf_mmap *map) +{ + refcount_inc(&map->refcnt); +} + +void perf_mmap__put(struct perf_mmap *map) +{ + BUG_ON(map->base && refcount_read(&map->refcnt) == 0); + + if (refcount_dec_and_test(&map->refcnt)) + perf_mmap__munmap(map); +} + +static inline void perf_mmap__write_tail(struct perf_mmap *md, u64 tail) +{ + ring_buffer_write_tail(md->base, tail); +} + +u64 perf_mmap__read_head(struct perf_mmap *map) +{ + return ring_buffer_read_head(map->base); +} + +static bool perf_mmap__empty(struct perf_mmap *map) +{ + struct perf_event_mmap_page *pc = map->base; + + return perf_mmap__read_head(map) == map->prev && !pc->aux_size; +} + +void perf_mmap__consume(struct perf_mmap *map) +{ + if (!map->overwrite) { + u64 old = map->prev; + + perf_mmap__write_tail(map, old); + } + + if (refcount_read(&map->refcnt) == 1 && perf_mmap__empty(map)) + perf_mmap__put(map); +} + +static int overwrite_rb_find_range(void *buf, int mask, u64 *start, u64 *end) +{ + struct perf_event_header *pheader; + u64 evt_head = *start; + int size = mask + 1; + + pr_debug2("%s: buf=%p, start=%"PRIx64"\n", __func__, buf, *start); + pheader = (struct perf_event_header *)(buf + (*start & mask)); + while (true) { + if (evt_head - *start >= (unsigned int)size) { + pr_debug("Finished reading overwrite ring buffer: rewind\n"); + if (evt_head - *start > (unsigned int)size) + evt_head -= pheader->size; + *end = evt_head; + return 0; + } + + pheader = (struct perf_event_header *)(buf + (evt_head & mask)); + + if (pheader->size == 0) { + pr_debug("Finished reading overwrite ring buffer: get start\n"); + *end = evt_head; + return 0; + } + + evt_head += pheader->size; + pr_debug3("move evt_head: %"PRIx64"\n", evt_head); + } + WARN_ONCE(1, "Shouldn't get here\n"); + return -1; +} + +/* + * Report the start and end of the available data in ringbuffer + */ +static int __perf_mmap__read_init(struct perf_mmap *md) +{ + u64 head = perf_mmap__read_head(md); + u64 old = md->prev; + unsigned char *data = md->base + page_size; + unsigned long size; + + md->start = md->overwrite ? head : old; + md->end = md->overwrite ? old : head; + + if ((md->end - md->start) < md->flush) + return -EAGAIN; + + size = md->end - md->start; + if (size > (unsigned long)(md->mask) + 1) { + if (!md->overwrite) { + WARN_ONCE(1, "failed to keep up with mmap data. (warn only once)\n"); + + md->prev = head; + perf_mmap__consume(md); + return -EAGAIN; + } + + /* + * Backward ring buffer is full. We still have a chance to read + * most of data from it. + */ + if (overwrite_rb_find_range(data, md->mask, &md->start, &md->end)) + return -EINVAL; + } + + return 0; +} + +int perf_mmap__read_init(struct perf_mmap *map) +{ + /* + * Check if event was unmapped due to a POLLHUP/POLLERR. + */ + if (!refcount_read(&map->refcnt)) + return -ENOENT; + + return __perf_mmap__read_init(map); +} + +/* + * Mandatory for overwrite mode + * The direction of overwrite mode is backward. + * The last perf_mmap__read() will set tail to map->core.prev. + * Need to correct the map->core.prev to head which is the end of next read. + */ +void perf_mmap__read_done(struct perf_mmap *map) +{ + /* + * Check if event was unmapped due to a POLLHUP/POLLERR. + */ + if (!refcount_read(&map->refcnt)) + return; + + map->prev = perf_mmap__read_head(map); +} + +/* When check_messup is true, 'end' must points to a good entry */ +static union perf_event *perf_mmap__read(struct perf_mmap *map, + u64 *startp, u64 end) +{ + unsigned char *data = map->base + page_size; + union perf_event *event = NULL; + int diff = end - *startp; + + if (diff >= (int)sizeof(event->header)) { + size_t size; + + event = (union perf_event *)&data[*startp & map->mask]; + size = event->header.size; + + if (size < sizeof(event->header) || diff < (int)size) + return NULL; + + /* + * Event straddles the mmap boundary -- header should always + * be inside due to u64 alignment of output. + */ + if ((*startp & map->mask) + size != ((*startp + size) & map->mask)) { + unsigned int offset = *startp; + unsigned int len = min(sizeof(*event), size), cpy; + void *dst = map->event_copy; + + do { + cpy = min(map->mask + 1 - (offset & map->mask), len); + memcpy(dst, &data[offset & map->mask], cpy); + offset += cpy; + dst += cpy; + len -= cpy; + } while (len); + + event = (union perf_event *)map->event_copy; + } + + *startp += size; + } + + return event; +} + +/* + * Read event from ring buffer one by one. + * Return one event for each call. + * + * Usage: + * perf_mmap__read_init() + * while(event = perf_mmap__read_event()) { + * //process the event + * perf_mmap__consume() + * } + * perf_mmap__read_done() + */ +union perf_event *perf_mmap__read_event(struct perf_mmap *map) +{ + union perf_event *event; + + /* + * Check if event was unmapped due to a POLLHUP/POLLERR. + */ + if (!refcount_read(&map->refcnt)) + return NULL; + + /* non-overwirte doesn't pause the ringbuffer */ + if (!map->overwrite) + map->end = perf_mmap__read_head(map); + + event = perf_mmap__read(map, &map->start, map->end); + + if (!map->overwrite) + map->prev = map->start; + + return event; +} diff --git a/tools/lib/perf/tests/Makefile b/tools/lib/perf/tests/Makefile new file mode 100644 index 000000000000..96841775feaf --- /dev/null +++ b/tools/lib/perf/tests/Makefile @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) + +TESTS = test-cpumap test-threadmap test-evlist test-evsel + +TESTS_SO := $(addsuffix -so,$(TESTS)) +TESTS_A := $(addsuffix -a,$(TESTS)) + +# Set compile option CFLAGS +ifdef EXTRA_CFLAGS + CFLAGS := $(EXTRA_CFLAGS) +else + CFLAGS := -g -Wall +endif + +all: + +include $(srctree)/tools/scripts/Makefile.include + +INCLUDE = -I$(srctree)/tools/lib/perf/include -I$(srctree)/tools/include -I$(srctree)/tools/lib + +$(TESTS_A): FORCE + $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a $(LIBAPI) + +$(TESTS_SO): FORCE + $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -L.. -o $@ $(subst -so,.c,$@) $(LIBAPI) -lperf + +all: $(TESTS_A) $(TESTS_SO) + +run: + @echo "running static:" + @for i in $(TESTS_A); do ./$$i; done + @echo "running dynamic:" + @for i in $(TESTS_SO); do LD_LIBRARY_PATH=../ ./$$i; done + +clean: + $(call QUIET_CLEAN, tests)$(RM) $(TESTS_A) $(TESTS_SO) + +.PHONY: all clean FORCE diff --git a/tools/lib/perf/tests/test-cpumap.c b/tools/lib/perf/tests/test-cpumap.c new file mode 100644 index 000000000000..c8d45091e7c2 --- /dev/null +++ b/tools/lib/perf/tests/test-cpumap.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +static int libperf_print(enum libperf_print_level level, + const char *fmt, va_list ap) +{ + return vfprintf(stderr, fmt, ap); +} + +int main(int argc, char **argv) +{ + struct perf_cpu_map *cpus; + + __T_START; + + libperf_init(libperf_print); + + cpus = perf_cpu_map__dummy_new(); + if (!cpus) + return -1; + + perf_cpu_map__get(cpus); + perf_cpu_map__put(cpus); + perf_cpu_map__put(cpus); + + __T_END; + return 0; +} diff --git a/tools/lib/perf/tests/test-evlist.c b/tools/lib/perf/tests/test-evlist.c new file mode 100644 index 000000000000..6d8ebe0c2504 --- /dev/null +++ b/tools/lib/perf/tests/test-evlist.c @@ -0,0 +1,413 @@ +// SPDX-License-Identifier: GPL-2.0 +#define _GNU_SOURCE // needed for sched.h to get sched_[gs]etaffinity and CPU_(ZERO,SET) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int libperf_print(enum libperf_print_level level, + const char *fmt, va_list ap) +{ + return vfprintf(stderr, fmt, ap); +} + +static int test_stat_cpu(void) +{ + struct perf_cpu_map *cpus; + struct perf_evlist *evlist; + struct perf_evsel *evsel; + struct perf_event_attr attr1 = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_CPU_CLOCK, + }; + struct perf_event_attr attr2 = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_TASK_CLOCK, + }; + int err, cpu, tmp; + + cpus = perf_cpu_map__new(NULL); + __T("failed to create cpus", cpus); + + evlist = perf_evlist__new(); + __T("failed to create evlist", evlist); + + evsel = perf_evsel__new(&attr1); + __T("failed to create evsel1", evsel); + + perf_evlist__add(evlist, evsel); + + evsel = perf_evsel__new(&attr2); + __T("failed to create evsel2", evsel); + + perf_evlist__add(evlist, evsel); + + perf_evlist__set_maps(evlist, cpus, NULL); + + err = perf_evlist__open(evlist); + __T("failed to open evsel", err == 0); + + perf_evlist__for_each_evsel(evlist, evsel) { + cpus = perf_evsel__cpus(evsel); + + perf_cpu_map__for_each_cpu(cpu, tmp, cpus) { + struct perf_counts_values counts = { .val = 0 }; + + perf_evsel__read(evsel, cpu, 0, &counts); + __T("failed to read value for evsel", counts.val != 0); + } + } + + perf_evlist__close(evlist); + perf_evlist__delete(evlist); + + perf_cpu_map__put(cpus); + return 0; +} + +static int test_stat_thread(void) +{ + struct perf_counts_values counts = { .val = 0 }; + struct perf_thread_map *threads; + struct perf_evlist *evlist; + struct perf_evsel *evsel; + struct perf_event_attr attr1 = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_CPU_CLOCK, + }; + struct perf_event_attr attr2 = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_TASK_CLOCK, + }; + int err; + + threads = perf_thread_map__new_dummy(); + __T("failed to create threads", threads); + + perf_thread_map__set_pid(threads, 0, 0); + + evlist = perf_evlist__new(); + __T("failed to create evlist", evlist); + + evsel = perf_evsel__new(&attr1); + __T("failed to create evsel1", evsel); + + perf_evlist__add(evlist, evsel); + + evsel = perf_evsel__new(&attr2); + __T("failed to create evsel2", evsel); + + perf_evlist__add(evlist, evsel); + + perf_evlist__set_maps(evlist, NULL, threads); + + err = perf_evlist__open(evlist); + __T("failed to open evsel", err == 0); + + perf_evlist__for_each_evsel(evlist, evsel) { + perf_evsel__read(evsel, 0, 0, &counts); + __T("failed to read value for evsel", counts.val != 0); + } + + perf_evlist__close(evlist); + perf_evlist__delete(evlist); + + perf_thread_map__put(threads); + return 0; +} + +static int test_stat_thread_enable(void) +{ + struct perf_counts_values counts = { .val = 0 }; + struct perf_thread_map *threads; + struct perf_evlist *evlist; + struct perf_evsel *evsel; + struct perf_event_attr attr1 = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_CPU_CLOCK, + .disabled = 1, + }; + struct perf_event_attr attr2 = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_TASK_CLOCK, + .disabled = 1, + }; + int err; + + threads = perf_thread_map__new_dummy(); + __T("failed to create threads", threads); + + perf_thread_map__set_pid(threads, 0, 0); + + evlist = perf_evlist__new(); + __T("failed to create evlist", evlist); + + evsel = perf_evsel__new(&attr1); + __T("failed to create evsel1", evsel); + + perf_evlist__add(evlist, evsel); + + evsel = perf_evsel__new(&attr2); + __T("failed to create evsel2", evsel); + + perf_evlist__add(evlist, evsel); + + perf_evlist__set_maps(evlist, NULL, threads); + + err = perf_evlist__open(evlist); + __T("failed to open evsel", err == 0); + + perf_evlist__for_each_evsel(evlist, evsel) { + perf_evsel__read(evsel, 0, 0, &counts); + __T("failed to read value for evsel", counts.val == 0); + } + + perf_evlist__enable(evlist); + + perf_evlist__for_each_evsel(evlist, evsel) { + perf_evsel__read(evsel, 0, 0, &counts); + __T("failed to read value for evsel", counts.val != 0); + } + + perf_evlist__disable(evlist); + + perf_evlist__close(evlist); + perf_evlist__delete(evlist); + + perf_thread_map__put(threads); + return 0; +} + +static int test_mmap_thread(void) +{ + struct perf_evlist *evlist; + struct perf_evsel *evsel; + struct perf_mmap *map; + struct perf_cpu_map *cpus; + struct perf_thread_map *threads; + struct perf_event_attr attr = { + .type = PERF_TYPE_TRACEPOINT, + .sample_period = 1, + .wakeup_watermark = 1, + .disabled = 1, + }; + char path[PATH_MAX]; + int id, err, pid, go_pipe[2]; + union perf_event *event; + char bf; + int count = 0; + + snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id", + sysfs__mountpoint()); + + if (filename__read_int(path, &id)) { + fprintf(stderr, "error: failed to get tracepoint id: %s\n", path); + return -1; + } + + attr.config = id; + + err = pipe(go_pipe); + __T("failed to create pipe", err == 0); + + fflush(NULL); + + pid = fork(); + if (!pid) { + int i; + + read(go_pipe[0], &bf, 1); + + /* Generate 100 prctl calls. */ + for (i = 0; i < 100; i++) + prctl(0, 0, 0, 0, 0); + + exit(0); + } + + threads = perf_thread_map__new_dummy(); + __T("failed to create threads", threads); + + cpus = perf_cpu_map__dummy_new(); + __T("failed to create cpus", cpus); + + perf_thread_map__set_pid(threads, 0, pid); + + evlist = perf_evlist__new(); + __T("failed to create evlist", evlist); + + evsel = perf_evsel__new(&attr); + __T("failed to create evsel1", evsel); + + perf_evlist__add(evlist, evsel); + + perf_evlist__set_maps(evlist, cpus, threads); + + err = perf_evlist__open(evlist); + __T("failed to open evlist", err == 0); + + err = perf_evlist__mmap(evlist, 4); + __T("failed to mmap evlist", err == 0); + + perf_evlist__enable(evlist); + + /* kick the child and wait for it to finish */ + write(go_pipe[1], &bf, 1); + waitpid(pid, NULL, 0); + + /* + * There's no need to call perf_evlist__disable, + * monitored process is dead now. + */ + + perf_evlist__for_each_mmap(evlist, map, false) { + if (perf_mmap__read_init(map) < 0) + continue; + + while ((event = perf_mmap__read_event(map)) != NULL) { + count++; + perf_mmap__consume(map); + } + + perf_mmap__read_done(map); + } + + /* calls perf_evlist__munmap/perf_evlist__close */ + perf_evlist__delete(evlist); + + perf_thread_map__put(threads); + perf_cpu_map__put(cpus); + + /* + * The generated prctl calls should match the + * number of events in the buffer. + */ + __T("failed count", count == 100); + + return 0; +} + +static int test_mmap_cpus(void) +{ + struct perf_evlist *evlist; + struct perf_evsel *evsel; + struct perf_mmap *map; + struct perf_cpu_map *cpus; + struct perf_event_attr attr = { + .type = PERF_TYPE_TRACEPOINT, + .sample_period = 1, + .wakeup_watermark = 1, + .disabled = 1, + }; + cpu_set_t saved_mask; + char path[PATH_MAX]; + int id, err, cpu, tmp; + union perf_event *event; + int count = 0; + + snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id", + sysfs__mountpoint()); + + if (filename__read_int(path, &id)) { + fprintf(stderr, "error: failed to get tracepoint id: %s\n", path); + return -1; + } + + attr.config = id; + + cpus = perf_cpu_map__new(NULL); + __T("failed to create cpus", cpus); + + evlist = perf_evlist__new(); + __T("failed to create evlist", evlist); + + evsel = perf_evsel__new(&attr); + __T("failed to create evsel1", evsel); + + perf_evlist__add(evlist, evsel); + + perf_evlist__set_maps(evlist, cpus, NULL); + + err = perf_evlist__open(evlist); + __T("failed to open evlist", err == 0); + + err = perf_evlist__mmap(evlist, 4); + __T("failed to mmap evlist", err == 0); + + perf_evlist__enable(evlist); + + err = sched_getaffinity(0, sizeof(saved_mask), &saved_mask); + __T("sched_getaffinity failed", err == 0); + + perf_cpu_map__for_each_cpu(cpu, tmp, cpus) { + cpu_set_t mask; + + CPU_ZERO(&mask); + CPU_SET(cpu, &mask); + + err = sched_setaffinity(0, sizeof(mask), &mask); + __T("sched_setaffinity failed", err == 0); + + prctl(0, 0, 0, 0, 0); + } + + err = sched_setaffinity(0, sizeof(saved_mask), &saved_mask); + __T("sched_setaffinity failed", err == 0); + + perf_evlist__disable(evlist); + + perf_evlist__for_each_mmap(evlist, map, false) { + if (perf_mmap__read_init(map) < 0) + continue; + + while ((event = perf_mmap__read_event(map)) != NULL) { + count++; + perf_mmap__consume(map); + } + + perf_mmap__read_done(map); + } + + /* calls perf_evlist__munmap/perf_evlist__close */ + perf_evlist__delete(evlist); + + /* + * The generated prctl events should match the + * number of cpus or be bigger (we are system-wide). + */ + __T("failed count", count >= perf_cpu_map__nr(cpus)); + + perf_cpu_map__put(cpus); + + return 0; +} + +int main(int argc, char **argv) +{ + __T_START; + + libperf_init(libperf_print); + + test_stat_cpu(); + test_stat_thread(); + test_stat_thread_enable(); + test_mmap_thread(); + test_mmap_cpus(); + + __T_END; + return 0; +} diff --git a/tools/lib/perf/tests/test-evsel.c b/tools/lib/perf/tests/test-evsel.c new file mode 100644 index 000000000000..135722ac965b --- /dev/null +++ b/tools/lib/perf/tests/test-evsel.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include + +static int libperf_print(enum libperf_print_level level, + const char *fmt, va_list ap) +{ + return vfprintf(stderr, fmt, ap); +} + +static int test_stat_cpu(void) +{ + struct perf_cpu_map *cpus; + struct perf_evsel *evsel; + struct perf_event_attr attr = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_CPU_CLOCK, + }; + int err, cpu, tmp; + + cpus = perf_cpu_map__new(NULL); + __T("failed to create cpus", cpus); + + evsel = perf_evsel__new(&attr); + __T("failed to create evsel", evsel); + + err = perf_evsel__open(evsel, cpus, NULL); + __T("failed to open evsel", err == 0); + + perf_cpu_map__for_each_cpu(cpu, tmp, cpus) { + struct perf_counts_values counts = { .val = 0 }; + + perf_evsel__read(evsel, cpu, 0, &counts); + __T("failed to read value for evsel", counts.val != 0); + } + + perf_evsel__close(evsel); + perf_evsel__delete(evsel); + + perf_cpu_map__put(cpus); + return 0; +} + +static int test_stat_thread(void) +{ + struct perf_counts_values counts = { .val = 0 }; + struct perf_thread_map *threads; + struct perf_evsel *evsel; + struct perf_event_attr attr = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_TASK_CLOCK, + }; + int err; + + threads = perf_thread_map__new_dummy(); + __T("failed to create threads", threads); + + perf_thread_map__set_pid(threads, 0, 0); + + evsel = perf_evsel__new(&attr); + __T("failed to create evsel", evsel); + + err = perf_evsel__open(evsel, NULL, threads); + __T("failed to open evsel", err == 0); + + perf_evsel__read(evsel, 0, 0, &counts); + __T("failed to read value for evsel", counts.val != 0); + + perf_evsel__close(evsel); + perf_evsel__delete(evsel); + + perf_thread_map__put(threads); + return 0; +} + +static int test_stat_thread_enable(void) +{ + struct perf_counts_values counts = { .val = 0 }; + struct perf_thread_map *threads; + struct perf_evsel *evsel; + struct perf_event_attr attr = { + .type = PERF_TYPE_SOFTWARE, + .config = PERF_COUNT_SW_TASK_CLOCK, + .disabled = 1, + }; + int err; + + threads = perf_thread_map__new_dummy(); + __T("failed to create threads", threads); + + perf_thread_map__set_pid(threads, 0, 0); + + evsel = perf_evsel__new(&attr); + __T("failed to create evsel", evsel); + + err = perf_evsel__open(evsel, NULL, threads); + __T("failed to open evsel", err == 0); + + perf_evsel__read(evsel, 0, 0, &counts); + __T("failed to read value for evsel", counts.val == 0); + + err = perf_evsel__enable(evsel); + __T("failed to enable evsel", err == 0); + + perf_evsel__read(evsel, 0, 0, &counts); + __T("failed to read value for evsel", counts.val != 0); + + err = perf_evsel__disable(evsel); + __T("failed to enable evsel", err == 0); + + perf_evsel__close(evsel); + perf_evsel__delete(evsel); + + perf_thread_map__put(threads); + return 0; +} + +int main(int argc, char **argv) +{ + __T_START; + + libperf_init(libperf_print); + + test_stat_cpu(); + test_stat_thread(); + test_stat_thread_enable(); + + __T_END; + return 0; +} diff --git a/tools/lib/perf/tests/test-threadmap.c b/tools/lib/perf/tests/test-threadmap.c new file mode 100644 index 000000000000..7dc4d6fbedde --- /dev/null +++ b/tools/lib/perf/tests/test-threadmap.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +static int libperf_print(enum libperf_print_level level, + const char *fmt, va_list ap) +{ + return vfprintf(stderr, fmt, ap); +} + +int main(int argc, char **argv) +{ + struct perf_thread_map *threads; + + __T_START; + + libperf_init(libperf_print); + + threads = perf_thread_map__new_dummy(); + if (!threads) + return -1; + + perf_thread_map__get(threads); + perf_thread_map__put(threads); + perf_thread_map__put(threads); + + __T_END; + return 0; +} diff --git a/tools/lib/perf/threadmap.c b/tools/lib/perf/threadmap.c new file mode 100644 index 000000000000..e92c368b0a6c --- /dev/null +++ b/tools/lib/perf/threadmap.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include + +static void perf_thread_map__reset(struct perf_thread_map *map, int start, int nr) +{ + size_t size = (nr - start) * sizeof(map->map[0]); + + memset(&map->map[start], 0, size); + map->err_thread = -1; +} + +struct perf_thread_map *perf_thread_map__realloc(struct perf_thread_map *map, int nr) +{ + size_t size = sizeof(*map) + sizeof(map->map[0]) * nr; + int start = map ? map->nr : 0; + + map = realloc(map, size); + /* + * We only realloc to add more items, let's reset new items. + */ + if (map) + perf_thread_map__reset(map, start, nr); + + return map; +} + +#define thread_map__alloc(__nr) perf_thread_map__realloc(NULL, __nr) + +void perf_thread_map__set_pid(struct perf_thread_map *map, int thread, pid_t pid) +{ + map->map[thread].pid = pid; +} + +char *perf_thread_map__comm(struct perf_thread_map *map, int thread) +{ + return map->map[thread].comm; +} + +struct perf_thread_map *perf_thread_map__new_dummy(void) +{ + struct perf_thread_map *threads = thread_map__alloc(1); + + if (threads != NULL) { + perf_thread_map__set_pid(threads, 0, -1); + threads->nr = 1; + refcount_set(&threads->refcnt, 1); + } + return threads; +} + +static void perf_thread_map__delete(struct perf_thread_map *threads) +{ + if (threads) { + int i; + + WARN_ONCE(refcount_read(&threads->refcnt) != 0, + "thread map refcnt unbalanced\n"); + for (i = 0; i < threads->nr; i++) + free(perf_thread_map__comm(threads, i)); + free(threads); + } +} + +struct perf_thread_map *perf_thread_map__get(struct perf_thread_map *map) +{ + if (map) + refcount_inc(&map->refcnt); + return map; +} + +void perf_thread_map__put(struct perf_thread_map *map) +{ + if (map && refcount_dec_and_test(&map->refcnt)) + perf_thread_map__delete(map); +} + +int perf_thread_map__nr(struct perf_thread_map *threads) +{ + return threads ? threads->nr : 1; +} + +pid_t perf_thread_map__pid(struct perf_thread_map *map, int thread) +{ + return map->map[thread].pid; +} diff --git a/tools/lib/perf/xyarray.c b/tools/lib/perf/xyarray.c new file mode 100644 index 000000000000..dcd901d154bb --- /dev/null +++ b/tools/lib/perf/xyarray.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include + +struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size) +{ + size_t row_size = ylen * entry_size; + struct xyarray *xy = zalloc(sizeof(*xy) + xlen * row_size); + + if (xy != NULL) { + xy->entry_size = entry_size; + xy->row_size = row_size; + xy->entries = xlen * ylen; + xy->max_x = xlen; + xy->max_y = ylen; + } + + return xy; +} + +void xyarray__reset(struct xyarray *xy) +{ + size_t n = xy->entries * xy->entry_size; + + memset(xy->contents, 0, n); +} + +void xyarray__delete(struct xyarray *xy) +{ + free(xy); +} diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 4934edb5adfd..5d7b947320fb 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -7,6 +7,7 @@ tools/lib/traceevent tools/lib/api tools/lib/bpf tools/lib/subcmd +tools/lib/perf tools/lib/argv_split.c tools/lib/ctype.c tools/lib/hweight.c diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index c90f4146e5a2..80e55e796be9 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -286,7 +286,7 @@ ifeq ($(DEBUG),0) endif endif -INC_FLAGS += -I$(src-perf)/lib/include +INC_FLAGS += -I$(srctree)/tools/lib/perf/include INC_FLAGS += -I$(src-perf)/util/include INC_FLAGS += -I$(src-perf)/arch/$(SRCARCH)/include INC_FLAGS += -I$(srctree)/tools/include/ diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index eae5d5e95952..3eda9d4b88e7 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -230,7 +230,7 @@ LIB_DIR = $(srctree)/tools/lib/api/ TRACE_EVENT_DIR = $(srctree)/tools/lib/traceevent/ BPF_DIR = $(srctree)/tools/lib/bpf/ SUBCMD_DIR = $(srctree)/tools/lib/subcmd/ -LIBPERF_DIR = $(srctree)/tools/perf/lib/ +LIBPERF_DIR = $(srctree)/tools/lib/perf/ # Set FEATURE_TESTS to 'all' so all possible feature checkers are executed. # Without this setting the output feature dump file misses some features, for diff --git a/tools/perf/lib/Build b/tools/perf/lib/Build deleted file mode 100644 index 2ef9a4ec6d99..000000000000 --- a/tools/perf/lib/Build +++ /dev/null @@ -1,13 +0,0 @@ -libperf-y += core.o -libperf-y += cpumap.o -libperf-y += threadmap.o -libperf-y += evsel.o -libperf-y += evlist.o -libperf-y += mmap.o -libperf-y += zalloc.o -libperf-y += xyarray.o -libperf-y += lib.o - -$(OUTPUT)zalloc.o: ../../lib/zalloc.c FORCE - $(call rule_mkdir) - $(call if_changed_dep,cc_o_c) diff --git a/tools/perf/lib/Documentation/Makefile b/tools/perf/lib/Documentation/Makefile deleted file mode 100644 index 586425a88795..000000000000 --- a/tools/perf/lib/Documentation/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: - rst2man man/libperf.rst > man/libperf.7 - rst2pdf tutorial/tutorial.rst - -clean: - rm -f man/libperf.7 - rm -f tutorial/tutorial.pdf diff --git a/tools/perf/lib/Documentation/man/libperf.rst b/tools/perf/lib/Documentation/man/libperf.rst deleted file mode 100644 index 09a270fccb9c..000000000000 --- a/tools/perf/lib/Documentation/man/libperf.rst +++ /dev/null @@ -1,100 +0,0 @@ -.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) - -libperf - -The libperf library provides an API to access the linux kernel perf -events subsystem. It provides the following high level objects: - - - struct perf_cpu_map - - struct perf_thread_map - - struct perf_evlist - - struct perf_evsel - -reference -========= -Function reference by header files: - -perf/core.h ------------ -.. code-block:: c - - typedef int (\*libperf_print_fn_t)(enum libperf_print_level level, - const char \*, va_list ap); - - void libperf_set_print(libperf_print_fn_t fn); - -perf/cpumap.h -------------- -.. code-block:: c - - struct perf_cpu_map \*perf_cpu_map__dummy_new(void); - struct perf_cpu_map \*perf_cpu_map__new(const char \*cpu_list); - struct perf_cpu_map \*perf_cpu_map__read(FILE \*file); - struct perf_cpu_map \*perf_cpu_map__get(struct perf_cpu_map \*map); - void perf_cpu_map__put(struct perf_cpu_map \*map); - int perf_cpu_map__cpu(const struct perf_cpu_map \*cpus, int idx); - int perf_cpu_map__nr(const struct perf_cpu_map \*cpus); - perf_cpu_map__for_each_cpu(cpu, idx, cpus) - -perf/threadmap.h ----------------- -.. code-block:: c - - struct perf_thread_map \*perf_thread_map__new_dummy(void); - void perf_thread_map__set_pid(struct perf_thread_map \*map, int thread, pid_t pid); - char \*perf_thread_map__comm(struct perf_thread_map \*map, int thread); - struct perf_thread_map \*perf_thread_map__get(struct perf_thread_map \*map); - void perf_thread_map__put(struct perf_thread_map \*map); - -perf/evlist.h -------------- -.. code-block:: - - void perf_evlist__init(struct perf_evlist \*evlist); - void perf_evlist__add(struct perf_evlist \*evlist, - struct perf_evsel \*evsel); - void perf_evlist__remove(struct perf_evlist \*evlist, - struct perf_evsel \*evsel); - struct perf_evlist \*perf_evlist__new(void); - void perf_evlist__delete(struct perf_evlist \*evlist); - struct perf_evsel\* perf_evlist__next(struct perf_evlist \*evlist, - struct perf_evsel \*evsel); - int perf_evlist__open(struct perf_evlist \*evlist); - void perf_evlist__close(struct perf_evlist \*evlist); - void perf_evlist__enable(struct perf_evlist \*evlist); - void perf_evlist__disable(struct perf_evlist \*evlist); - perf_evlist__for_each_evsel(evlist, pos) - void perf_evlist__set_maps(struct perf_evlist \*evlist, - struct perf_cpu_map \*cpus, - struct perf_thread_map \*threads); - -perf/evsel.h ------------- -.. code-block:: c - - struct perf_counts_values { - union { - struct { - uint64_t val; - uint64_t ena; - uint64_t run; - }; - uint64_t values[3]; - }; - }; - - void perf_evsel__init(struct perf_evsel \*evsel, - struct perf_event_attr \*attr); - struct perf_evsel \*perf_evsel__new(struct perf_event_attr \*attr); - void perf_evsel__delete(struct perf_evsel \*evsel); - int perf_evsel__open(struct perf_evsel \*evsel, struct perf_cpu_map \*cpus, - struct perf_thread_map \*threads); - void perf_evsel__close(struct perf_evsel \*evsel); - int perf_evsel__read(struct perf_evsel \*evsel, int cpu, int thread, - struct perf_counts_values \*count); - int perf_evsel__enable(struct perf_evsel \*evsel); - int perf_evsel__disable(struct perf_evsel \*evsel); - int perf_evsel__apply_filter(struct perf_evsel \*evsel, const char \*filter); - struct perf_cpu_map \*perf_evsel__cpus(struct perf_evsel \*evsel); - struct perf_thread_map \*perf_evsel__threads(struct perf_evsel \*evsel); - struct perf_event_attr \*perf_evsel__attr(struct perf_evsel \*evsel); diff --git a/tools/perf/lib/Documentation/tutorial/tutorial.rst b/tools/perf/lib/Documentation/tutorial/tutorial.rst deleted file mode 100644 index 7be7bc27b385..000000000000 --- a/tools/perf/lib/Documentation/tutorial/tutorial.rst +++ /dev/null @@ -1,123 +0,0 @@ -.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) - -libperf tutorial -================ - -Compile and install libperf from kernel sources -=============================================== -.. code-block:: bash - - git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git - cd linux/tools/perf/lib - make - sudo make install prefix=/usr - -Libperf object -============== -The libperf library provides several high level objects: - -struct perf_cpu_map - Provides a cpu list abstraction. - -struct perf_thread_map - Provides a thread list abstraction. - -struct perf_evsel - Provides an abstraction for single a perf event. - -struct perf_evlist - Gathers several struct perf_evsel object and performs functions on all of them. - -The exported API binds these objects together, -for full reference see the libperf.7 man page. - -Examples -======== -Examples aim to explain libperf functionality on simple use cases. -They are based in on a checked out linux kernel git tree: - -.. code-block:: bash - - $ cd tools/perf/lib/Documentation/tutorial/ - $ ls -d ex-* - ex-1-compile ex-2-evsel-stat ex-3-evlist-stat - -ex-1-compile example -==================== -This example shows the basic usage of *struct perf_cpu_map*, -how to create it and display its cpus: - -.. code-block:: bash - - $ cd ex-1-compile/ - $ make - gcc -o test test.c -lperf - $ ./test - 0 1 2 3 4 5 6 7 - - -The full code listing is here: - -.. code-block:: c - - 1 #include - 2 - 3 int main(int argc, char **Argv) - 4 { - 5 struct perf_cpu_map *cpus; - 6 int cpu, tmp; - 7 - 8 cpus = perf_cpu_map__new(NULL); - 9 - 10 perf_cpu_map__for_each_cpu(cpu, tmp, cpus) - 11 fprintf(stdout, "%d ", cpu); - 12 - 13 fprintf(stdout, "\n"); - 14 - 15 perf_cpu_map__put(cpus); - 16 return 0; - 17 } - - -First you need to include the proper header to have *struct perf_cpumap* -declaration and functions: - -.. code-block:: c - - 1 #include - - -The *struct perf_cpumap* object is created by *perf_cpu_map__new* call. -The *NULL* argument asks it to populate the object with the current online CPUs list: - -.. code-block:: c - - 8 cpus = perf_cpu_map__new(NULL); - -This is paired with a *perf_cpu_map__put*, that drops its reference at the end, possibly deleting it. - -.. code-block:: c - - 15 perf_cpu_map__put(cpus); - -The iteration through the *struct perf_cpumap* CPUs is done using the *perf_cpu_map__for_each_cpu* -macro which requires 3 arguments: - -- cpu - the cpu numer -- tmp - iteration helper variable -- cpus - the *struct perf_cpumap* object - -.. code-block:: c - - 10 perf_cpu_map__for_each_cpu(cpu, tmp, cpus) - 11 fprintf(stdout, "%d ", cpu); - -ex-2-evsel-stat example -======================= - -TBD - -ex-3-evlist-stat example -======================== - -TBD diff --git a/tools/perf/lib/Makefile b/tools/perf/lib/Makefile deleted file mode 100644 index 0f233638ef1f..000000000000 --- a/tools/perf/lib/Makefile +++ /dev/null @@ -1,188 +0,0 @@ -# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) -# Most of this file is copied from tools/lib/bpf/Makefile - -LIBPERF_VERSION = 0 -LIBPERF_PATCHLEVEL = 0 -LIBPERF_EXTRAVERSION = 1 - -MAKEFLAGS += --no-print-directory - -ifeq ($(srctree),) -srctree := $(patsubst %/,%,$(dir $(CURDIR))) -srctree := $(patsubst %/,%,$(dir $(srctree))) -srctree := $(patsubst %/,%,$(dir $(srctree))) -#$(info Determined 'srctree' to be $(srctree)) -endif - -INSTALL = install - -# Use DESTDIR for installing into a different root directory. -# This is useful for building a package. The program will be -# installed in this directory as if it was the root directory. -# Then the build tool can move it later. -DESTDIR ?= -DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' - -include $(srctree)/tools/scripts/Makefile.include -include $(srctree)/tools/scripts/Makefile.arch - -ifeq ($(LP64), 1) - libdir_relative = lib64 -else - libdir_relative = lib -endif - -prefix ?= -libdir = $(prefix)/$(libdir_relative) - -# Shell quotes -libdir_SQ = $(subst ','\'',$(libdir)) -libdir_relative_SQ = $(subst ','\'',$(libdir_relative)) - -ifeq ("$(origin V)", "command line") - VERBOSE = $(V) -endif -ifndef VERBOSE - VERBOSE = 0 -endif - -ifeq ($(VERBOSE),1) - Q = -else - Q = @ -endif - -# Set compile option CFLAGS -ifdef EXTRA_CFLAGS - CFLAGS := $(EXTRA_CFLAGS) -else - CFLAGS := -g -Wall -endif - -INCLUDES = \ --I$(srctree)/tools/perf/lib/include \ --I$(srctree)/tools/lib/ \ --I$(srctree)/tools/include \ --I$(srctree)/tools/arch/$(SRCARCH)/include/ \ --I$(srctree)/tools/arch/$(SRCARCH)/include/uapi \ --I$(srctree)/tools/include/uapi - -# Append required CFLAGS -override CFLAGS += $(EXTRA_WARNINGS) -override CFLAGS += -Werror -Wall -override CFLAGS += -fPIC -override CFLAGS += $(INCLUDES) -override CFLAGS += -fvisibility=hidden - -all: - -export srctree OUTPUT CC LD CFLAGS V -export DESTDIR DESTDIR_SQ - -include $(srctree)/tools/build/Makefile.include - -VERSION_SCRIPT := libperf.map - -PATCHLEVEL = $(LIBPERF_PATCHLEVEL) -EXTRAVERSION = $(LIBPERF_EXTRAVERSION) -VERSION = $(LIBPERF_VERSION).$(LIBPERF_PATCHLEVEL).$(LIBPERF_EXTRAVERSION) - -LIBPERF_SO := $(OUTPUT)libperf.so.$(VERSION) -LIBPERF_A := $(OUTPUT)libperf.a -LIBPERF_IN := $(OUTPUT)libperf-in.o -LIBPERF_PC := $(OUTPUT)libperf.pc - -LIBPERF_ALL := $(LIBPERF_A) $(OUTPUT)libperf.so* - -LIB_DIR := $(srctree)/tools/lib/api/ - -ifneq ($(OUTPUT),) -ifneq ($(subdir),) - API_PATH=$(OUTPUT)/../lib/api/ -else - API_PATH=$(OUTPUT) -endif -else - API_PATH=$(LIB_DIR) -endif - -LIBAPI = $(API_PATH)libapi.a -export LIBAPI - -$(LIBAPI): FORCE - $(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) $(OUTPUT)libapi.a - -$(LIBAPI)-clean: - $(call QUIET_CLEAN, libapi) - $(Q)$(MAKE) -C $(LIB_DIR) O=$(OUTPUT) clean >/dev/null - -$(LIBPERF_IN): FORCE - $(Q)$(MAKE) $(build)=libperf - -$(LIBPERF_A): $(LIBPERF_IN) - $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIBPERF_IN) - -$(LIBPERF_SO): $(LIBPERF_IN) $(LIBAPI) - $(QUIET_LINK)$(CC) --shared -Wl,-soname,libperf.so \ - -Wl,--version-script=$(VERSION_SCRIPT) $^ -o $@ - @ln -sf $(@F) $(OUTPUT)libperf.so - @ln -sf $(@F) $(OUTPUT)libperf.so.$(LIBPERF_VERSION) - - -libs: $(LIBPERF_A) $(LIBPERF_SO) $(LIBPERF_PC) - -all: fixdep - $(Q)$(MAKE) libs - -clean: $(LIBAPI)-clean - $(call QUIET_CLEAN, libperf) $(RM) $(LIBPERF_A) \ - *.o *~ *.a *.so *.so.$(VERSION) *.so.$(LIBPERF_VERSION) .*.d .*.cmd LIBPERF-CFLAGS $(LIBPERF_PC) - $(Q)$(MAKE) -C tests clean - -tests: libs - $(Q)$(MAKE) -C tests - $(Q)$(MAKE) -C tests run - -$(LIBPERF_PC): - $(QUIET_GEN)sed -e "s|@PREFIX@|$(prefix)|" \ - -e "s|@LIBDIR@|$(libdir_SQ)|" \ - -e "s|@VERSION@|$(VERSION)|" \ - < libperf.pc.template > $@ - -define do_install_mkdir - if [ ! -d '$(DESTDIR_SQ)$1' ]; then \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$1'; \ - fi -endef - -define do_install - if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ - $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ - fi; \ - $(INSTALL) $1 $(if $3,-m $3,) '$(DESTDIR_SQ)$2' -endef - -install_lib: libs - $(call QUIET_INSTALL, $(LIBPERF_ALL)) \ - $(call do_install_mkdir,$(libdir_SQ)); \ - cp -fpR $(LIBPERF_ALL) $(DESTDIR)$(libdir_SQ) - -install_headers: - $(call QUIET_INSTALL, headers) \ - $(call do_install,include/perf/core.h,$(prefix)/include/perf,644); \ - $(call do_install,include/perf/cpumap.h,$(prefix)/include/perf,644); \ - $(call do_install,include/perf/threadmap.h,$(prefix)/include/perf,644); \ - $(call do_install,include/perf/evlist.h,$(prefix)/include/perf,644); \ - $(call do_install,include/perf/evsel.h,$(prefix)/include/perf,644); \ - $(call do_install,include/perf/event.h,$(prefix)/include/perf,644); \ - $(call do_install,include/perf/mmap.h,$(prefix)/include/perf,644); - -install_pkgconfig: $(LIBPERF_PC) - $(call QUIET_INSTALL, $(LIBPERF_PC)) \ - $(call do_install,$(LIBPERF_PC),$(libdir_SQ)/pkgconfig,644) - -install: install_lib install_headers install_pkgconfig - -FORCE: - -.PHONY: all install clean tests FORCE diff --git a/tools/perf/lib/core.c b/tools/perf/lib/core.c deleted file mode 100644 index 58fc894b76c5..000000000000 --- a/tools/perf/lib/core.c +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#define __printf(a, b) __attribute__((format(printf, a, b))) - -#include -#include -#include -#include -#include -#include -#include "internal.h" - -static int __base_pr(enum libperf_print_level level __maybe_unused, const char *format, - va_list args) -{ - return vfprintf(stderr, format, args); -} - -static libperf_print_fn_t __libperf_pr = __base_pr; - -__printf(2, 3) -void libperf_print(enum libperf_print_level level, const char *format, ...) -{ - va_list args; - - if (!__libperf_pr) - return; - - va_start(args, format); - __libperf_pr(level, format, args); - va_end(args); -} - -void libperf_init(libperf_print_fn_t fn) -{ - page_size = sysconf(_SC_PAGE_SIZE); - __libperf_pr = fn; -} diff --git a/tools/perf/lib/cpumap.c b/tools/perf/lib/cpumap.c deleted file mode 100644 index f93f4e703e4c..000000000000 --- a/tools/perf/lib/cpumap.c +++ /dev/null @@ -1,345 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct perf_cpu_map *perf_cpu_map__dummy_new(void) -{ - struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int)); - - if (cpus != NULL) { - cpus->nr = 1; - cpus->map[0] = -1; - refcount_set(&cpus->refcnt, 1); - } - - return cpus; -} - -static void cpu_map__delete(struct perf_cpu_map *map) -{ - if (map) { - WARN_ONCE(refcount_read(&map->refcnt) != 0, - "cpu_map refcnt unbalanced\n"); - free(map); - } -} - -struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map) -{ - if (map) - refcount_inc(&map->refcnt); - return map; -} - -void perf_cpu_map__put(struct perf_cpu_map *map) -{ - if (map && refcount_dec_and_test(&map->refcnt)) - cpu_map__delete(map); -} - -static struct perf_cpu_map *cpu_map__default_new(void) -{ - struct perf_cpu_map *cpus; - int nr_cpus; - - nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); - if (nr_cpus < 0) - return NULL; - - cpus = malloc(sizeof(*cpus) + nr_cpus * sizeof(int)); - if (cpus != NULL) { - int i; - - for (i = 0; i < nr_cpus; ++i) - cpus->map[i] = i; - - cpus->nr = nr_cpus; - refcount_set(&cpus->refcnt, 1); - } - - return cpus; -} - -static int cmp_int(const void *a, const void *b) -{ - return *(const int *)a - *(const int*)b; -} - -static struct perf_cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus) -{ - size_t payload_size = nr_cpus * sizeof(int); - struct perf_cpu_map *cpus = malloc(sizeof(*cpus) + payload_size); - int i, j; - - if (cpus != NULL) { - memcpy(cpus->map, tmp_cpus, payload_size); - qsort(cpus->map, nr_cpus, sizeof(int), cmp_int); - /* Remove dups */ - j = 0; - for (i = 0; i < nr_cpus; i++) { - if (i == 0 || cpus->map[i] != cpus->map[i - 1]) - cpus->map[j++] = cpus->map[i]; - } - cpus->nr = j; - assert(j <= nr_cpus); - refcount_set(&cpus->refcnt, 1); - } - - return cpus; -} - -struct perf_cpu_map *perf_cpu_map__read(FILE *file) -{ - struct perf_cpu_map *cpus = NULL; - int nr_cpus = 0; - int *tmp_cpus = NULL, *tmp; - int max_entries = 0; - int n, cpu, prev; - char sep; - - sep = 0; - prev = -1; - for (;;) { - n = fscanf(file, "%u%c", &cpu, &sep); - if (n <= 0) - break; - if (prev >= 0) { - int new_max = nr_cpus + cpu - prev - 1; - - WARN_ONCE(new_max >= MAX_NR_CPUS, "Perf can support %d CPUs. " - "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS); - - if (new_max >= max_entries) { - max_entries = new_max + MAX_NR_CPUS / 2; - tmp = realloc(tmp_cpus, max_entries * sizeof(int)); - if (tmp == NULL) - goto out_free_tmp; - tmp_cpus = tmp; - } - - while (++prev < cpu) - tmp_cpus[nr_cpus++] = prev; - } - if (nr_cpus == max_entries) { - max_entries += MAX_NR_CPUS; - tmp = realloc(tmp_cpus, max_entries * sizeof(int)); - if (tmp == NULL) - goto out_free_tmp; - tmp_cpus = tmp; - } - - tmp_cpus[nr_cpus++] = cpu; - if (n == 2 && sep == '-') - prev = cpu; - else - prev = -1; - if (n == 1 || sep == '\n') - break; - } - - if (nr_cpus > 0) - cpus = cpu_map__trim_new(nr_cpus, tmp_cpus); - else - cpus = cpu_map__default_new(); -out_free_tmp: - free(tmp_cpus); - return cpus; -} - -static struct perf_cpu_map *cpu_map__read_all_cpu_map(void) -{ - struct perf_cpu_map *cpus = NULL; - FILE *onlnf; - - onlnf = fopen("/sys/devices/system/cpu/online", "r"); - if (!onlnf) - return cpu_map__default_new(); - - cpus = perf_cpu_map__read(onlnf); - fclose(onlnf); - return cpus; -} - -struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list) -{ - struct perf_cpu_map *cpus = NULL; - unsigned long start_cpu, end_cpu = 0; - char *p = NULL; - int i, nr_cpus = 0; - int *tmp_cpus = NULL, *tmp; - int max_entries = 0; - - if (!cpu_list) - return cpu_map__read_all_cpu_map(); - - /* - * must handle the case of empty cpumap to cover - * TOPOLOGY header for NUMA nodes with no CPU - * ( e.g., because of CPU hotplug) - */ - if (!isdigit(*cpu_list) && *cpu_list != '\0') - goto out; - - while (isdigit(*cpu_list)) { - p = NULL; - start_cpu = strtoul(cpu_list, &p, 0); - if (start_cpu >= INT_MAX - || (*p != '\0' && *p != ',' && *p != '-')) - goto invalid; - - if (*p == '-') { - cpu_list = ++p; - p = NULL; - end_cpu = strtoul(cpu_list, &p, 0); - - if (end_cpu >= INT_MAX || (*p != '\0' && *p != ',')) - goto invalid; - - if (end_cpu < start_cpu) - goto invalid; - } else { - end_cpu = start_cpu; - } - - WARN_ONCE(end_cpu >= MAX_NR_CPUS, "Perf can support %d CPUs. " - "Consider raising MAX_NR_CPUS\n", MAX_NR_CPUS); - - for (; start_cpu <= end_cpu; start_cpu++) { - /* check for duplicates */ - for (i = 0; i < nr_cpus; i++) - if (tmp_cpus[i] == (int)start_cpu) - goto invalid; - - if (nr_cpus == max_entries) { - max_entries += MAX_NR_CPUS; - tmp = realloc(tmp_cpus, max_entries * sizeof(int)); - if (tmp == NULL) - goto invalid; - tmp_cpus = tmp; - } - tmp_cpus[nr_cpus++] = (int)start_cpu; - } - if (*p) - ++p; - - cpu_list = p; - } - - if (nr_cpus > 0) - cpus = cpu_map__trim_new(nr_cpus, tmp_cpus); - else if (*cpu_list != '\0') - cpus = cpu_map__default_new(); - else - cpus = perf_cpu_map__dummy_new(); -invalid: - free(tmp_cpus); -out: - return cpus; -} - -int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx) -{ - if (idx < cpus->nr) - return cpus->map[idx]; - - return -1; -} - -int perf_cpu_map__nr(const struct perf_cpu_map *cpus) -{ - return cpus ? cpus->nr : 1; -} - -bool perf_cpu_map__empty(const struct perf_cpu_map *map) -{ - return map ? map->map[0] == -1 : true; -} - -int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu) -{ - int i; - - for (i = 0; i < cpus->nr; ++i) { - if (cpus->map[i] == cpu) - return i; - } - - return -1; -} - -int perf_cpu_map__max(struct perf_cpu_map *map) -{ - int i, max = -1; - - for (i = 0; i < map->nr; i++) { - if (map->map[i] > max) - max = map->map[i]; - } - - return max; -} - -/* - * Merge two cpumaps - * - * orig either gets freed and replaced with a new map, or reused - * with no reference count change (similar to "realloc") - * other has its reference count increased. - */ - -struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, - struct perf_cpu_map *other) -{ - int *tmp_cpus; - int tmp_len; - int i, j, k; - struct perf_cpu_map *merged; - - if (!orig && !other) - return NULL; - if (!orig) { - perf_cpu_map__get(other); - return other; - } - if (!other) - return orig; - if (orig->nr == other->nr && - !memcmp(orig->map, other->map, orig->nr * sizeof(int))) - return orig; - - tmp_len = orig->nr + other->nr; - tmp_cpus = malloc(tmp_len * sizeof(int)); - if (!tmp_cpus) - return NULL; - - /* Standard merge algorithm from wikipedia */ - i = j = k = 0; - while (i < orig->nr && j < other->nr) { - if (orig->map[i] <= other->map[j]) { - if (orig->map[i] == other->map[j]) - j++; - tmp_cpus[k++] = orig->map[i++]; - } else - tmp_cpus[k++] = other->map[j++]; - } - - while (i < orig->nr) - tmp_cpus[k++] = orig->map[i++]; - - while (j < other->nr) - tmp_cpus[k++] = other->map[j++]; - assert(k <= tmp_len); - - merged = cpu_map__trim_new(k, tmp_cpus); - free(tmp_cpus); - perf_cpu_map__put(orig); - return merged; -} diff --git a/tools/perf/lib/evlist.c b/tools/perf/lib/evlist.c deleted file mode 100644 index ae9e65aa2491..000000000000 --- a/tools/perf/lib/evlist.c +++ /dev/null @@ -1,641 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void perf_evlist__init(struct perf_evlist *evlist) -{ - int i; - - for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) - INIT_HLIST_HEAD(&evlist->heads[i]); - INIT_LIST_HEAD(&evlist->entries); - evlist->nr_entries = 0; - fdarray__init(&evlist->pollfd, 64); -} - -static void __perf_evlist__propagate_maps(struct perf_evlist *evlist, - struct perf_evsel *evsel) -{ - /* - * We already have cpus for evsel (via PMU sysfs) so - * keep it, if there's no target cpu list defined. - */ - if (!evsel->own_cpus || evlist->has_user_cpus) { - perf_cpu_map__put(evsel->cpus); - evsel->cpus = perf_cpu_map__get(evlist->cpus); - } else if (evsel->cpus != evsel->own_cpus) { - perf_cpu_map__put(evsel->cpus); - evsel->cpus = perf_cpu_map__get(evsel->own_cpus); - } - - perf_thread_map__put(evsel->threads); - evsel->threads = perf_thread_map__get(evlist->threads); - evlist->all_cpus = perf_cpu_map__merge(evlist->all_cpus, evsel->cpus); -} - -static void perf_evlist__propagate_maps(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel; - - perf_evlist__for_each_evsel(evlist, evsel) - __perf_evlist__propagate_maps(evlist, evsel); -} - -void perf_evlist__add(struct perf_evlist *evlist, - struct perf_evsel *evsel) -{ - list_add_tail(&evsel->node, &evlist->entries); - evlist->nr_entries += 1; - __perf_evlist__propagate_maps(evlist, evsel); -} - -void perf_evlist__remove(struct perf_evlist *evlist, - struct perf_evsel *evsel) -{ - list_del_init(&evsel->node); - evlist->nr_entries -= 1; -} - -struct perf_evlist *perf_evlist__new(void) -{ - struct perf_evlist *evlist = zalloc(sizeof(*evlist)); - - if (evlist != NULL) - perf_evlist__init(evlist); - - return evlist; -} - -struct perf_evsel * -perf_evlist__next(struct perf_evlist *evlist, struct perf_evsel *prev) -{ - struct perf_evsel *next; - - if (!prev) { - next = list_first_entry(&evlist->entries, - struct perf_evsel, - node); - } else { - next = list_next_entry(prev, node); - } - - /* Empty list is noticed here so don't need checking on entry. */ - if (&next->node == &evlist->entries) - return NULL; - - return next; -} - -static void perf_evlist__purge(struct perf_evlist *evlist) -{ - struct perf_evsel *pos, *n; - - perf_evlist__for_each_entry_safe(evlist, n, pos) { - list_del_init(&pos->node); - perf_evsel__delete(pos); - } - - evlist->nr_entries = 0; -} - -void perf_evlist__exit(struct perf_evlist *evlist) -{ - perf_cpu_map__put(evlist->cpus); - perf_thread_map__put(evlist->threads); - evlist->cpus = NULL; - evlist->threads = NULL; - fdarray__exit(&evlist->pollfd); -} - -void perf_evlist__delete(struct perf_evlist *evlist) -{ - if (evlist == NULL) - return; - - perf_evlist__munmap(evlist); - perf_evlist__close(evlist); - perf_evlist__purge(evlist); - perf_evlist__exit(evlist); - free(evlist); -} - -void perf_evlist__set_maps(struct perf_evlist *evlist, - struct perf_cpu_map *cpus, - struct perf_thread_map *threads) -{ - /* - * Allow for the possibility that one or another of the maps isn't being - * changed i.e. don't put it. Note we are assuming the maps that are - * being applied are brand new and evlist is taking ownership of the - * original reference count of 1. If that is not the case it is up to - * the caller to increase the reference count. - */ - if (cpus != evlist->cpus) { - perf_cpu_map__put(evlist->cpus); - evlist->cpus = perf_cpu_map__get(cpus); - } - - if (threads != evlist->threads) { - perf_thread_map__put(evlist->threads); - evlist->threads = perf_thread_map__get(threads); - } - - perf_evlist__propagate_maps(evlist); -} - -int perf_evlist__open(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel; - int err; - - perf_evlist__for_each_entry(evlist, evsel) { - err = perf_evsel__open(evsel, evsel->cpus, evsel->threads); - if (err < 0) - goto out_err; - } - - return 0; - -out_err: - perf_evlist__close(evlist); - return err; -} - -void perf_evlist__close(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel; - - perf_evlist__for_each_entry_reverse(evlist, evsel) - perf_evsel__close(evsel); -} - -void perf_evlist__enable(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel; - - perf_evlist__for_each_entry(evlist, evsel) - perf_evsel__enable(evsel); -} - -void perf_evlist__disable(struct perf_evlist *evlist) -{ - struct perf_evsel *evsel; - - perf_evlist__for_each_entry(evlist, evsel) - perf_evsel__disable(evsel); -} - -u64 perf_evlist__read_format(struct perf_evlist *evlist) -{ - struct perf_evsel *first = perf_evlist__first(evlist); - - return first->attr.read_format; -} - -#define SID(e, x, y) xyarray__entry(e->sample_id, x, y) - -static void perf_evlist__id_hash(struct perf_evlist *evlist, - struct perf_evsel *evsel, - int cpu, int thread, u64 id) -{ - int hash; - struct perf_sample_id *sid = SID(evsel, cpu, thread); - - sid->id = id; - sid->evsel = evsel; - hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); - hlist_add_head(&sid->node, &evlist->heads[hash]); -} - -void perf_evlist__id_add(struct perf_evlist *evlist, - struct perf_evsel *evsel, - int cpu, int thread, u64 id) -{ - perf_evlist__id_hash(evlist, evsel, cpu, thread, id); - evsel->id[evsel->ids++] = id; -} - -int perf_evlist__id_add_fd(struct perf_evlist *evlist, - struct perf_evsel *evsel, - int cpu, int thread, int fd) -{ - u64 read_data[4] = { 0, }; - int id_idx = 1; /* The first entry is the counter value */ - u64 id; - int ret; - - ret = ioctl(fd, PERF_EVENT_IOC_ID, &id); - if (!ret) - goto add; - - if (errno != ENOTTY) - return -1; - - /* Legacy way to get event id.. All hail to old kernels! */ - - /* - * This way does not work with group format read, so bail - * out in that case. - */ - if (perf_evlist__read_format(evlist) & PERF_FORMAT_GROUP) - return -1; - - if (!(evsel->attr.read_format & PERF_FORMAT_ID) || - read(fd, &read_data, sizeof(read_data)) == -1) - return -1; - - if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) - ++id_idx; - if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) - ++id_idx; - - id = read_data[id_idx]; - -add: - perf_evlist__id_add(evlist, evsel, cpu, thread, id); - return 0; -} - -int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) -{ - int nr_cpus = perf_cpu_map__nr(evlist->cpus); - int nr_threads = perf_thread_map__nr(evlist->threads); - int nfds = 0; - struct perf_evsel *evsel; - - perf_evlist__for_each_entry(evlist, evsel) { - if (evsel->system_wide) - nfds += nr_cpus; - else - nfds += nr_cpus * nr_threads; - } - - if (fdarray__available_entries(&evlist->pollfd) < nfds && - fdarray__grow(&evlist->pollfd, nfds) < 0) - return -ENOMEM; - - return 0; -} - -int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, - void *ptr, short revent) -{ - int pos = fdarray__add(&evlist->pollfd, fd, revent | POLLERR | POLLHUP); - - if (pos >= 0) { - evlist->pollfd.priv[pos].ptr = ptr; - fcntl(fd, F_SETFL, O_NONBLOCK); - } - - return pos; -} - -static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd, - void *arg __maybe_unused) -{ - struct perf_mmap *map = fda->priv[fd].ptr; - - if (map) - perf_mmap__put(map); -} - -int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask) -{ - return fdarray__filter(&evlist->pollfd, revents_and_mask, - perf_evlist__munmap_filtered, NULL); -} - -int perf_evlist__poll(struct perf_evlist *evlist, int timeout) -{ - return fdarray__poll(&evlist->pollfd, timeout); -} - -static struct perf_mmap* perf_evlist__alloc_mmap(struct perf_evlist *evlist, bool overwrite) -{ - int i; - struct perf_mmap *map; - - map = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); - if (!map) - return NULL; - - for (i = 0; i < evlist->nr_mmaps; i++) { - struct perf_mmap *prev = i ? &map[i - 1] : NULL; - - /* - * When the perf_mmap() call is made we grab one refcount, plus - * one extra to let perf_mmap__consume() get the last - * events after all real references (perf_mmap__get()) are - * dropped. - * - * Each PERF_EVENT_IOC_SET_OUTPUT points to this mmap and - * thus does perf_mmap__get() on it. - */ - perf_mmap__init(&map[i], prev, overwrite, NULL); - } - - return map; -} - -static void perf_evlist__set_sid_idx(struct perf_evlist *evlist, - struct perf_evsel *evsel, int idx, int cpu, - int thread) -{ - struct perf_sample_id *sid = SID(evsel, cpu, thread); - - sid->idx = idx; - if (evlist->cpus && cpu >= 0) - sid->cpu = evlist->cpus->map[cpu]; - else - sid->cpu = -1; - if (!evsel->system_wide && evlist->threads && thread >= 0) - sid->tid = perf_thread_map__pid(evlist->threads, thread); - else - sid->tid = -1; -} - -static struct perf_mmap* -perf_evlist__mmap_cb_get(struct perf_evlist *evlist, bool overwrite, int idx) -{ - struct perf_mmap *maps; - - maps = overwrite ? evlist->mmap_ovw : evlist->mmap; - - if (!maps) { - maps = perf_evlist__alloc_mmap(evlist, overwrite); - if (!maps) - return NULL; - - if (overwrite) - evlist->mmap_ovw = maps; - else - evlist->mmap = maps; - } - - return &maps[idx]; -} - -#define FD(e, x, y) (*(int *) xyarray__entry(e->fd, x, y)) - -static int -perf_evlist__mmap_cb_mmap(struct perf_mmap *map, struct perf_mmap_param *mp, - int output, int cpu) -{ - return perf_mmap__mmap(map, mp, output, cpu); -} - -static void perf_evlist__set_mmap_first(struct perf_evlist *evlist, struct perf_mmap *map, - bool overwrite) -{ - if (overwrite) - evlist->mmap_ovw_first = map; - else - evlist->mmap_first = map; -} - -static int -mmap_per_evsel(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, - int idx, struct perf_mmap_param *mp, int cpu_idx, - int thread, int *_output, int *_output_overwrite) -{ - int evlist_cpu = perf_cpu_map__cpu(evlist->cpus, cpu_idx); - struct perf_evsel *evsel; - int revent; - - perf_evlist__for_each_entry(evlist, evsel) { - bool overwrite = evsel->attr.write_backward; - struct perf_mmap *map; - int *output, fd, cpu; - - if (evsel->system_wide && thread) - continue; - - cpu = perf_cpu_map__idx(evsel->cpus, evlist_cpu); - if (cpu == -1) - continue; - - map = ops->get(evlist, overwrite, idx); - if (map == NULL) - return -ENOMEM; - - if (overwrite) { - mp->prot = PROT_READ; - output = _output_overwrite; - } else { - mp->prot = PROT_READ | PROT_WRITE; - output = _output; - } - - fd = FD(evsel, cpu, thread); - - if (*output == -1) { - *output = fd; - - /* - * The last one will be done at perf_mmap__consume(), so that we - * make sure we don't prevent tools from consuming every last event in - * the ring buffer. - * - * I.e. we can get the POLLHUP meaning that the fd doesn't exist - * anymore, but the last events for it are still in the ring buffer, - * waiting to be consumed. - * - * Tools can chose to ignore this at their own discretion, but the - * evlist layer can't just drop it when filtering events in - * perf_evlist__filter_pollfd(). - */ - refcount_set(&map->refcnt, 2); - - if (ops->mmap(map, mp, *output, evlist_cpu) < 0) - return -1; - - if (!idx) - perf_evlist__set_mmap_first(evlist, map, overwrite); - } else { - if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) - return -1; - - perf_mmap__get(map); - } - - revent = !overwrite ? POLLIN : 0; - - if (!evsel->system_wide && - perf_evlist__add_pollfd(evlist, fd, map, revent) < 0) { - perf_mmap__put(map); - return -1; - } - - if (evsel->attr.read_format & PERF_FORMAT_ID) { - if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread, - fd) < 0) - return -1; - perf_evlist__set_sid_idx(evlist, evsel, idx, cpu, - thread); - } - } - - return 0; -} - -static int -mmap_per_thread(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, - struct perf_mmap_param *mp) -{ - int thread; - int nr_threads = perf_thread_map__nr(evlist->threads); - - for (thread = 0; thread < nr_threads; thread++) { - int output = -1; - int output_overwrite = -1; - - if (ops->idx) - ops->idx(evlist, mp, thread, false); - - if (mmap_per_evsel(evlist, ops, thread, mp, 0, thread, - &output, &output_overwrite)) - goto out_unmap; - } - - return 0; - -out_unmap: - perf_evlist__munmap(evlist); - return -1; -} - -static int -mmap_per_cpu(struct perf_evlist *evlist, struct perf_evlist_mmap_ops *ops, - struct perf_mmap_param *mp) -{ - int nr_threads = perf_thread_map__nr(evlist->threads); - int nr_cpus = perf_cpu_map__nr(evlist->cpus); - int cpu, thread; - - for (cpu = 0; cpu < nr_cpus; cpu++) { - int output = -1; - int output_overwrite = -1; - - if (ops->idx) - ops->idx(evlist, mp, cpu, true); - - for (thread = 0; thread < nr_threads; thread++) { - if (mmap_per_evsel(evlist, ops, cpu, mp, cpu, - thread, &output, &output_overwrite)) - goto out_unmap; - } - } - - return 0; - -out_unmap: - perf_evlist__munmap(evlist); - return -1; -} - -static int perf_evlist__nr_mmaps(struct perf_evlist *evlist) -{ - int nr_mmaps; - - nr_mmaps = perf_cpu_map__nr(evlist->cpus); - if (perf_cpu_map__empty(evlist->cpus)) - nr_mmaps = perf_thread_map__nr(evlist->threads); - - return nr_mmaps; -} - -int perf_evlist__mmap_ops(struct perf_evlist *evlist, - struct perf_evlist_mmap_ops *ops, - struct perf_mmap_param *mp) -{ - struct perf_evsel *evsel; - const struct perf_cpu_map *cpus = evlist->cpus; - const struct perf_thread_map *threads = evlist->threads; - - if (!ops || !ops->get || !ops->mmap) - return -EINVAL; - - mp->mask = evlist->mmap_len - page_size - 1; - - evlist->nr_mmaps = perf_evlist__nr_mmaps(evlist); - - perf_evlist__for_each_entry(evlist, evsel) { - if ((evsel->attr.read_format & PERF_FORMAT_ID) && - evsel->sample_id == NULL && - perf_evsel__alloc_id(evsel, perf_cpu_map__nr(cpus), threads->nr) < 0) - return -ENOMEM; - } - - if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0) - return -ENOMEM; - - if (perf_cpu_map__empty(cpus)) - return mmap_per_thread(evlist, ops, mp); - - return mmap_per_cpu(evlist, ops, mp); -} - -int perf_evlist__mmap(struct perf_evlist *evlist, int pages) -{ - struct perf_mmap_param mp; - struct perf_evlist_mmap_ops ops = { - .get = perf_evlist__mmap_cb_get, - .mmap = perf_evlist__mmap_cb_mmap, - }; - - evlist->mmap_len = (pages + 1) * page_size; - - return perf_evlist__mmap_ops(evlist, &ops, &mp); -} - -void perf_evlist__munmap(struct perf_evlist *evlist) -{ - int i; - - if (evlist->mmap) { - for (i = 0; i < evlist->nr_mmaps; i++) - perf_mmap__munmap(&evlist->mmap[i]); - } - - if (evlist->mmap_ovw) { - for (i = 0; i < evlist->nr_mmaps; i++) - perf_mmap__munmap(&evlist->mmap_ovw[i]); - } - - zfree(&evlist->mmap); - zfree(&evlist->mmap_ovw); -} - -struct perf_mmap* -perf_evlist__next_mmap(struct perf_evlist *evlist, struct perf_mmap *map, - bool overwrite) -{ - if (map) - return map->next; - - return overwrite ? evlist->mmap_ovw_first : evlist->mmap_first; -} diff --git a/tools/perf/lib/evsel.c b/tools/perf/lib/evsel.c deleted file mode 100644 index 4dc06289f4c7..000000000000 --- a/tools/perf/lib/evsel.c +++ /dev/null @@ -1,301 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr) -{ - INIT_LIST_HEAD(&evsel->node); - evsel->attr = *attr; -} - -struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr) -{ - struct perf_evsel *evsel = zalloc(sizeof(*evsel)); - - if (evsel != NULL) - perf_evsel__init(evsel, attr); - - return evsel; -} - -void perf_evsel__delete(struct perf_evsel *evsel) -{ - free(evsel); -} - -#define FD(e, x, y) (*(int *) xyarray__entry(e->fd, x, y)) - -int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) -{ - evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); - - if (evsel->fd) { - int cpu, thread; - for (cpu = 0; cpu < ncpus; cpu++) { - for (thread = 0; thread < nthreads; thread++) { - FD(evsel, cpu, thread) = -1; - } - } - } - - return evsel->fd != NULL ? 0 : -ENOMEM; -} - -static int -sys_perf_event_open(struct perf_event_attr *attr, - pid_t pid, int cpu, int group_fd, - unsigned long flags) -{ - return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); -} - -int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, - struct perf_thread_map *threads) -{ - int cpu, thread, err = 0; - - if (cpus == NULL) { - static struct perf_cpu_map *empty_cpu_map; - - if (empty_cpu_map == NULL) { - empty_cpu_map = perf_cpu_map__dummy_new(); - if (empty_cpu_map == NULL) - return -ENOMEM; - } - - cpus = empty_cpu_map; - } - - if (threads == NULL) { - static struct perf_thread_map *empty_thread_map; - - if (empty_thread_map == NULL) { - empty_thread_map = perf_thread_map__new_dummy(); - if (empty_thread_map == NULL) - return -ENOMEM; - } - - threads = empty_thread_map; - } - - if (evsel->fd == NULL && - perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) - return -ENOMEM; - - for (cpu = 0; cpu < cpus->nr; cpu++) { - for (thread = 0; thread < threads->nr; thread++) { - int fd; - - fd = sys_perf_event_open(&evsel->attr, - threads->map[thread].pid, - cpus->map[cpu], -1, 0); - - if (fd < 0) - return -errno; - - FD(evsel, cpu, thread) = fd; - } - } - - return err; -} - -static void perf_evsel__close_fd_cpu(struct perf_evsel *evsel, int cpu) -{ - int thread; - - for (thread = 0; thread < xyarray__max_y(evsel->fd); ++thread) { - if (FD(evsel, cpu, thread) >= 0) - close(FD(evsel, cpu, thread)); - FD(evsel, cpu, thread) = -1; - } -} - -void perf_evsel__close_fd(struct perf_evsel *evsel) -{ - int cpu; - - for (cpu = 0; cpu < xyarray__max_x(evsel->fd); cpu++) - perf_evsel__close_fd_cpu(evsel, cpu); -} - -void perf_evsel__free_fd(struct perf_evsel *evsel) -{ - xyarray__delete(evsel->fd); - evsel->fd = NULL; -} - -void perf_evsel__close(struct perf_evsel *evsel) -{ - if (evsel->fd == NULL) - return; - - perf_evsel__close_fd(evsel); - perf_evsel__free_fd(evsel); -} - -void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu) -{ - if (evsel->fd == NULL) - return; - - perf_evsel__close_fd_cpu(evsel, cpu); -} - -int perf_evsel__read_size(struct perf_evsel *evsel) -{ - u64 read_format = evsel->attr.read_format; - int entry = sizeof(u64); /* value */ - int size = 0; - int nr = 1; - - if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) - size += sizeof(u64); - - if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) - size += sizeof(u64); - - if (read_format & PERF_FORMAT_ID) - entry += sizeof(u64); - - if (read_format & PERF_FORMAT_GROUP) { - nr = evsel->nr_members; - size += sizeof(u64); - } - - size += entry * nr; - return size; -} - -int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, - struct perf_counts_values *count) -{ - size_t size = perf_evsel__read_size(evsel); - - memset(count, 0, sizeof(*count)); - - if (FD(evsel, cpu, thread) < 0) - return -EINVAL; - - if (readn(FD(evsel, cpu, thread), count->values, size) <= 0) - return -errno; - - return 0; -} - -static int perf_evsel__run_ioctl(struct perf_evsel *evsel, - int ioc, void *arg, - int cpu) -{ - int thread; - - for (thread = 0; thread < xyarray__max_y(evsel->fd); thread++) { - int fd = FD(evsel, cpu, thread), - err = ioctl(fd, ioc, arg); - - if (err) - return err; - } - - return 0; -} - -int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu) -{ - return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, cpu); -} - -int perf_evsel__enable(struct perf_evsel *evsel) -{ - int i; - int err = 0; - - for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++) - err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_ENABLE, NULL, i); - return err; -} - -int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu) -{ - return perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, cpu); -} - -int perf_evsel__disable(struct perf_evsel *evsel) -{ - int i; - int err = 0; - - for (i = 0; i < xyarray__max_x(evsel->fd) && !err; i++) - err = perf_evsel__run_ioctl(evsel, PERF_EVENT_IOC_DISABLE, NULL, i); - return err; -} - -int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter) -{ - int err = 0, i; - - for (i = 0; i < evsel->cpus->nr && !err; i++) - err = perf_evsel__run_ioctl(evsel, - PERF_EVENT_IOC_SET_FILTER, - (void *)filter, i); - return err; -} - -struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) -{ - return evsel->cpus; -} - -struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel) -{ - return evsel->threads; -} - -struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel) -{ - return &evsel->attr; -} - -int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) -{ - if (ncpus == 0 || nthreads == 0) - return 0; - - if (evsel->system_wide) - nthreads = 1; - - evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); - if (evsel->sample_id == NULL) - return -ENOMEM; - - evsel->id = zalloc(ncpus * nthreads * sizeof(u64)); - if (evsel->id == NULL) { - xyarray__delete(evsel->sample_id); - evsel->sample_id = NULL; - return -ENOMEM; - } - - return 0; -} - -void perf_evsel__free_id(struct perf_evsel *evsel) -{ - xyarray__delete(evsel->sample_id); - evsel->sample_id = NULL; - zfree(&evsel->id); - evsel->ids = 0; -} diff --git a/tools/perf/lib/include/internal/cpumap.h b/tools/perf/lib/include/internal/cpumap.h deleted file mode 100644 index 840d4032587b..000000000000 --- a/tools/perf/lib/include/internal/cpumap.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_INTERNAL_CPUMAP_H -#define __LIBPERF_INTERNAL_CPUMAP_H - -#include - -struct perf_cpu_map { - refcount_t refcnt; - int nr; - int map[]; -}; - -#ifndef MAX_NR_CPUS -#define MAX_NR_CPUS 2048 -#endif - -int perf_cpu_map__idx(struct perf_cpu_map *cpus, int cpu); - -#endif /* __LIBPERF_INTERNAL_CPUMAP_H */ diff --git a/tools/perf/lib/include/internal/evlist.h b/tools/perf/lib/include/internal/evlist.h deleted file mode 100644 index 74dc8c3f0b66..000000000000 --- a/tools/perf/lib/include/internal/evlist.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_INTERNAL_EVLIST_H -#define __LIBPERF_INTERNAL_EVLIST_H - -#include -#include -#include - -#define PERF_EVLIST__HLIST_BITS 8 -#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) - -struct perf_cpu_map; -struct perf_thread_map; -struct perf_mmap_param; - -struct perf_evlist { - struct list_head entries; - int nr_entries; - bool has_user_cpus; - struct perf_cpu_map *cpus; - struct perf_cpu_map *all_cpus; - struct perf_thread_map *threads; - int nr_mmaps; - size_t mmap_len; - struct fdarray pollfd; - struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; - struct perf_mmap *mmap; - struct perf_mmap *mmap_ovw; - struct perf_mmap *mmap_first; - struct perf_mmap *mmap_ovw_first; -}; - -typedef void -(*perf_evlist_mmap__cb_idx_t)(struct perf_evlist*, struct perf_mmap_param*, int, bool); -typedef struct perf_mmap* -(*perf_evlist_mmap__cb_get_t)(struct perf_evlist*, bool, int); -typedef int -(*perf_evlist_mmap__cb_mmap_t)(struct perf_mmap*, struct perf_mmap_param*, int, int); - -struct perf_evlist_mmap_ops { - perf_evlist_mmap__cb_idx_t idx; - perf_evlist_mmap__cb_get_t get; - perf_evlist_mmap__cb_mmap_t mmap; -}; - -int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); -int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, - void *ptr, short revent); - -int perf_evlist__mmap_ops(struct perf_evlist *evlist, - struct perf_evlist_mmap_ops *ops, - struct perf_mmap_param *mp); - -void perf_evlist__init(struct perf_evlist *evlist); -void perf_evlist__exit(struct perf_evlist *evlist); - -/** - * __perf_evlist__for_each_entry - iterate thru all the evsels - * @list: list_head instance to iterate - * @evsel: struct perf_evsel iterator - */ -#define __perf_evlist__for_each_entry(list, evsel) \ - list_for_each_entry(evsel, list, node) - -/** - * evlist__for_each_entry - iterate thru all the evsels - * @evlist: perf_evlist instance to iterate - * @evsel: struct perf_evsel iterator - */ -#define perf_evlist__for_each_entry(evlist, evsel) \ - __perf_evlist__for_each_entry(&(evlist)->entries, evsel) - -/** - * __perf_evlist__for_each_entry_reverse - iterate thru all the evsels in reverse order - * @list: list_head instance to iterate - * @evsel: struct evsel iterator - */ -#define __perf_evlist__for_each_entry_reverse(list, evsel) \ - list_for_each_entry_reverse(evsel, list, node) - -/** - * perf_evlist__for_each_entry_reverse - iterate thru all the evsels in reverse order - * @evlist: evlist instance to iterate - * @evsel: struct evsel iterator - */ -#define perf_evlist__for_each_entry_reverse(evlist, evsel) \ - __perf_evlist__for_each_entry_reverse(&(evlist)->entries, evsel) - -/** - * __perf_evlist__for_each_entry_safe - safely iterate thru all the evsels - * @list: list_head instance to iterate - * @tmp: struct evsel temp iterator - * @evsel: struct evsel iterator - */ -#define __perf_evlist__for_each_entry_safe(list, tmp, evsel) \ - list_for_each_entry_safe(evsel, tmp, list, node) - -/** - * perf_evlist__for_each_entry_safe - safely iterate thru all the evsels - * @evlist: evlist instance to iterate - * @evsel: struct evsel iterator - * @tmp: struct evsel temp iterator - */ -#define perf_evlist__for_each_entry_safe(evlist, tmp, evsel) \ - __perf_evlist__for_each_entry_safe(&(evlist)->entries, tmp, evsel) - -static inline struct perf_evsel *perf_evlist__first(struct perf_evlist *evlist) -{ - return list_entry(evlist->entries.next, struct perf_evsel, node); -} - -static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) -{ - return list_entry(evlist->entries.prev, struct perf_evsel, node); -} - -u64 perf_evlist__read_format(struct perf_evlist *evlist); - -void perf_evlist__id_add(struct perf_evlist *evlist, - struct perf_evsel *evsel, - int cpu, int thread, u64 id); - -int perf_evlist__id_add_fd(struct perf_evlist *evlist, - struct perf_evsel *evsel, - int cpu, int thread, int fd); - -#endif /* __LIBPERF_INTERNAL_EVLIST_H */ diff --git a/tools/perf/lib/include/internal/evsel.h b/tools/perf/lib/include/internal/evsel.h deleted file mode 100644 index 1ffd083b235e..000000000000 --- a/tools/perf/lib/include/internal/evsel.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_INTERNAL_EVSEL_H -#define __LIBPERF_INTERNAL_EVSEL_H - -#include -#include -#include -#include - -struct perf_cpu_map; -struct perf_thread_map; -struct xyarray; - -/* - * Per fd, to map back from PERF_SAMPLE_ID to evsel, only used when there are - * more than one entry in the evlist. - */ -struct perf_sample_id { - struct hlist_node node; - u64 id; - struct perf_evsel *evsel; - /* - * 'idx' will be used for AUX area sampling. A sample will have AUX area - * data that will be queued for decoding, where there are separate - * queues for each CPU (per-cpu tracing) or task (per-thread tracing). - * The sample ID can be used to lookup 'idx' which is effectively the - * queue number. - */ - int idx; - int cpu; - pid_t tid; - - /* Holds total ID period value for PERF_SAMPLE_READ processing. */ - u64 period; -}; - -struct perf_evsel { - struct list_head node; - struct perf_event_attr attr; - struct perf_cpu_map *cpus; - struct perf_cpu_map *own_cpus; - struct perf_thread_map *threads; - struct xyarray *fd; - struct xyarray *sample_id; - u64 *id; - u32 ids; - - /* parse modifier helper */ - int nr_members; - bool system_wide; -}; - -void perf_evsel__init(struct perf_evsel *evsel, struct perf_event_attr *attr); -int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); -void perf_evsel__close_fd(struct perf_evsel *evsel); -void perf_evsel__free_fd(struct perf_evsel *evsel); -int perf_evsel__read_size(struct perf_evsel *evsel); -int perf_evsel__apply_filter(struct perf_evsel *evsel, const char *filter); - -int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); -void perf_evsel__free_id(struct perf_evsel *evsel); - -#endif /* __LIBPERF_INTERNAL_EVSEL_H */ diff --git a/tools/perf/lib/include/internal/lib.h b/tools/perf/lib/include/internal/lib.h deleted file mode 100644 index 5175d491b2d4..000000000000 --- a/tools/perf/lib/include/internal/lib.h +++ /dev/null @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_INTERNAL_LIB_H -#define __LIBPERF_INTERNAL_LIB_H - -#include - -extern unsigned int page_size; - -ssize_t readn(int fd, void *buf, size_t n); -ssize_t writen(int fd, const void *buf, size_t n); - -#endif /* __LIBPERF_INTERNAL_CPUMAP_H */ diff --git a/tools/perf/lib/include/internal/mmap.h b/tools/perf/lib/include/internal/mmap.h deleted file mode 100644 index be7556e0a2b2..000000000000 --- a/tools/perf/lib/include/internal/mmap.h +++ /dev/null @@ -1,55 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_INTERNAL_MMAP_H -#define __LIBPERF_INTERNAL_MMAP_H - -#include -#include -#include -#include - -/* perf sample has 16 bits size limit */ -#define PERF_SAMPLE_MAX_SIZE (1 << 16) - -struct perf_mmap; - -typedef void (*libperf_unmap_cb_t)(struct perf_mmap *map); - -/** - * struct perf_mmap - perf's ring buffer mmap details - * - * @refcnt - e.g. code using PERF_EVENT_IOC_SET_OUTPUT to share this - */ -struct perf_mmap { - void *base; - int mask; - int fd; - int cpu; - refcount_t refcnt; - u64 prev; - u64 start; - u64 end; - bool overwrite; - u64 flush; - libperf_unmap_cb_t unmap_cb; - char event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8); - struct perf_mmap *next; -}; - -struct perf_mmap_param { - int prot; - int mask; -}; - -size_t perf_mmap__mmap_len(struct perf_mmap *map); - -void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, - bool overwrite, libperf_unmap_cb_t unmap_cb); -int perf_mmap__mmap(struct perf_mmap *map, struct perf_mmap_param *mp, - int fd, int cpu); -void perf_mmap__munmap(struct perf_mmap *map); -void perf_mmap__get(struct perf_mmap *map); -void perf_mmap__put(struct perf_mmap *map); - -u64 perf_mmap__read_head(struct perf_mmap *map); - -#endif /* __LIBPERF_INTERNAL_MMAP_H */ diff --git a/tools/perf/lib/include/internal/tests.h b/tools/perf/lib/include/internal/tests.h deleted file mode 100644 index 2093e8868a67..000000000000 --- a/tools/perf/lib/include/internal/tests.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_INTERNAL_TESTS_H -#define __LIBPERF_INTERNAL_TESTS_H - -#include - -int tests_failed; - -#define __T_START \ -do { \ - fprintf(stdout, "- running %s...", __FILE__); \ - fflush(NULL); \ - tests_failed = 0; \ -} while (0) - -#define __T_END \ -do { \ - if (tests_failed) \ - fprintf(stdout, " FAILED (%d)\n", tests_failed); \ - else \ - fprintf(stdout, "OK\n"); \ -} while (0) - -#define __T(text, cond) \ -do { \ - if (!(cond)) { \ - fprintf(stderr, "FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ - tests_failed++; \ - return -1; \ - } \ -} while (0) - -#endif /* __LIBPERF_INTERNAL_TESTS_H */ diff --git a/tools/perf/lib/include/internal/threadmap.h b/tools/perf/lib/include/internal/threadmap.h deleted file mode 100644 index df748baf9eda..000000000000 --- a/tools/perf/lib/include/internal/threadmap.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_INTERNAL_THREADMAP_H -#define __LIBPERF_INTERNAL_THREADMAP_H - -#include -#include -#include - -struct thread_map_data { - pid_t pid; - char *comm; -}; - -struct perf_thread_map { - refcount_t refcnt; - int nr; - int err_thread; - struct thread_map_data map[]; -}; - -struct perf_thread_map *perf_thread_map__realloc(struct perf_thread_map *map, int nr); - -#endif /* __LIBPERF_INTERNAL_THREADMAP_H */ diff --git a/tools/perf/lib/include/internal/xyarray.h b/tools/perf/lib/include/internal/xyarray.h deleted file mode 100644 index 51e35d6c8ec4..000000000000 --- a/tools/perf/lib/include/internal/xyarray.h +++ /dev/null @@ -1,36 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_INTERNAL_XYARRAY_H -#define __LIBPERF_INTERNAL_XYARRAY_H - -#include -#include - -struct xyarray { - size_t row_size; - size_t entry_size; - size_t entries; - size_t max_x; - size_t max_y; - char contents[] __aligned(8); -}; - -struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size); -void xyarray__delete(struct xyarray *xy); -void xyarray__reset(struct xyarray *xy); - -static inline void *xyarray__entry(struct xyarray *xy, int x, int y) -{ - return &xy->contents[x * xy->row_size + y * xy->entry_size]; -} - -static inline int xyarray__max_y(struct xyarray *xy) -{ - return xy->max_y; -} - -static inline int xyarray__max_x(struct xyarray *xy) -{ - return xy->max_x; -} - -#endif /* __LIBPERF_INTERNAL_XYARRAY_H */ diff --git a/tools/perf/lib/include/perf/core.h b/tools/perf/lib/include/perf/core.h deleted file mode 100644 index a3f6d68edad7..000000000000 --- a/tools/perf/lib/include/perf/core.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_CORE_H -#define __LIBPERF_CORE_H - -#include - -#ifndef LIBPERF_API -#define LIBPERF_API __attribute__((visibility("default"))) -#endif - -enum libperf_print_level { - LIBPERF_ERR, - LIBPERF_WARN, - LIBPERF_INFO, - LIBPERF_DEBUG, - LIBPERF_DEBUG2, - LIBPERF_DEBUG3, -}; - -typedef int (*libperf_print_fn_t)(enum libperf_print_level level, - const char *, va_list ap); - -LIBPERF_API void libperf_init(libperf_print_fn_t fn); - -#endif /* __LIBPERF_CORE_H */ diff --git a/tools/perf/lib/include/perf/cpumap.h b/tools/perf/lib/include/perf/cpumap.h deleted file mode 100644 index 6a17ad730cbc..000000000000 --- a/tools/perf/lib/include/perf/cpumap.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_CPUMAP_H -#define __LIBPERF_CPUMAP_H - -#include -#include -#include - -struct perf_cpu_map; - -LIBPERF_API struct perf_cpu_map *perf_cpu_map__dummy_new(void); -LIBPERF_API struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list); -LIBPERF_API struct perf_cpu_map *perf_cpu_map__read(FILE *file); -LIBPERF_API struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map); -LIBPERF_API struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, - struct perf_cpu_map *other); -LIBPERF_API void perf_cpu_map__put(struct perf_cpu_map *map); -LIBPERF_API int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx); -LIBPERF_API int perf_cpu_map__nr(const struct perf_cpu_map *cpus); -LIBPERF_API bool perf_cpu_map__empty(const struct perf_cpu_map *map); -LIBPERF_API int perf_cpu_map__max(struct perf_cpu_map *map); - -#define perf_cpu_map__for_each_cpu(cpu, idx, cpus) \ - for ((idx) = 0, (cpu) = perf_cpu_map__cpu(cpus, idx); \ - (idx) < perf_cpu_map__nr(cpus); \ - (idx)++, (cpu) = perf_cpu_map__cpu(cpus, idx)) - -#endif /* __LIBPERF_CPUMAP_H */ diff --git a/tools/perf/lib/include/perf/event.h b/tools/perf/lib/include/perf/event.h deleted file mode 100644 index 18106899cb4e..000000000000 --- a/tools/perf/lib/include/perf/event.h +++ /dev/null @@ -1,385 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_EVENT_H -#define __LIBPERF_EVENT_H - -#include -#include -#include -#include -#include /* pid_t */ - -struct perf_record_mmap { - struct perf_event_header header; - __u32 pid, tid; - __u64 start; - __u64 len; - __u64 pgoff; - char filename[PATH_MAX]; -}; - -struct perf_record_mmap2 { - struct perf_event_header header; - __u32 pid, tid; - __u64 start; - __u64 len; - __u64 pgoff; - __u32 maj; - __u32 min; - __u64 ino; - __u64 ino_generation; - __u32 prot; - __u32 flags; - char filename[PATH_MAX]; -}; - -struct perf_record_comm { - struct perf_event_header header; - __u32 pid, tid; - char comm[16]; -}; - -struct perf_record_namespaces { - struct perf_event_header header; - __u32 pid, tid; - __u64 nr_namespaces; - struct perf_ns_link_info link_info[]; -}; - -struct perf_record_fork { - struct perf_event_header header; - __u32 pid, ppid; - __u32 tid, ptid; - __u64 time; -}; - -struct perf_record_lost { - struct perf_event_header header; - __u64 id; - __u64 lost; -}; - -struct perf_record_lost_samples { - struct perf_event_header header; - __u64 lost; -}; - -/* - * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID - */ -struct perf_record_read { - struct perf_event_header header; - __u32 pid, tid; - __u64 value; - __u64 time_enabled; - __u64 time_running; - __u64 id; -}; - -struct perf_record_throttle { - struct perf_event_header header; - __u64 time; - __u64 id; - __u64 stream_id; -}; - -#ifndef KSYM_NAME_LEN -#define KSYM_NAME_LEN 256 -#endif - -struct perf_record_ksymbol { - struct perf_event_header header; - __u64 addr; - __u32 len; - __u16 ksym_type; - __u16 flags; - char name[KSYM_NAME_LEN]; -}; - -struct perf_record_bpf_event { - struct perf_event_header header; - __u16 type; - __u16 flags; - __u32 id; - - /* for bpf_prog types */ - __u8 tag[BPF_TAG_SIZE]; // prog tag -}; - -struct perf_record_sample { - struct perf_event_header header; - __u64 array[]; -}; - -struct perf_record_switch { - struct perf_event_header header; - __u32 next_prev_pid; - __u32 next_prev_tid; -}; - -struct perf_record_header_attr { - struct perf_event_header header; - struct perf_event_attr attr; - __u64 id[]; -}; - -enum { - PERF_CPU_MAP__CPUS = 0, - PERF_CPU_MAP__MASK = 1, -}; - -struct cpu_map_entries { - __u16 nr; - __u16 cpu[]; -}; - -struct perf_record_record_cpu_map { - __u16 nr; - __u16 long_size; - unsigned long mask[]; -}; - -struct perf_record_cpu_map_data { - __u16 type; - char data[]; -}; - -struct perf_record_cpu_map { - struct perf_event_header header; - struct perf_record_cpu_map_data data; -}; - -enum { - PERF_EVENT_UPDATE__UNIT = 0, - PERF_EVENT_UPDATE__SCALE = 1, - PERF_EVENT_UPDATE__NAME = 2, - PERF_EVENT_UPDATE__CPUS = 3, -}; - -struct perf_record_event_update_cpus { - struct perf_record_cpu_map_data cpus; -}; - -struct perf_record_event_update_scale { - double scale; -}; - -struct perf_record_event_update { - struct perf_event_header header; - __u64 type; - __u64 id; - char data[]; -}; - -#define MAX_EVENT_NAME 64 - -struct perf_trace_event_type { - __u64 event_id; - char name[MAX_EVENT_NAME]; -}; - -struct perf_record_header_event_type { - struct perf_event_header header; - struct perf_trace_event_type event_type; -}; - -struct perf_record_header_tracing_data { - struct perf_event_header header; - __u32 size; -}; - -struct perf_record_header_build_id { - struct perf_event_header header; - pid_t pid; - __u8 build_id[24]; - char filename[]; -}; - -struct id_index_entry { - __u64 id; - __u64 idx; - __u64 cpu; - __u64 tid; -}; - -struct perf_record_id_index { - struct perf_event_header header; - __u64 nr; - struct id_index_entry entries[0]; -}; - -struct perf_record_auxtrace_info { - struct perf_event_header header; - __u32 type; - __u32 reserved__; /* For alignment */ - __u64 priv[]; -}; - -struct perf_record_auxtrace { - struct perf_event_header header; - __u64 size; - __u64 offset; - __u64 reference; - __u32 idx; - __u32 tid; - __u32 cpu; - __u32 reserved__; /* For alignment */ -}; - -#define MAX_AUXTRACE_ERROR_MSG 64 - -struct perf_record_auxtrace_error { - struct perf_event_header header; - __u32 type; - __u32 code; - __u32 cpu; - __u32 pid; - __u32 tid; - __u32 fmt; - __u64 ip; - __u64 time; - char msg[MAX_AUXTRACE_ERROR_MSG]; -}; - -struct perf_record_aux { - struct perf_event_header header; - __u64 aux_offset; - __u64 aux_size; - __u64 flags; -}; - -struct perf_record_itrace_start { - struct perf_event_header header; - __u32 pid; - __u32 tid; -}; - -struct perf_record_thread_map_entry { - __u64 pid; - char comm[16]; -}; - -struct perf_record_thread_map { - struct perf_event_header header; - __u64 nr; - struct perf_record_thread_map_entry entries[]; -}; - -enum { - PERF_STAT_CONFIG_TERM__AGGR_MODE = 0, - PERF_STAT_CONFIG_TERM__INTERVAL = 1, - PERF_STAT_CONFIG_TERM__SCALE = 2, - PERF_STAT_CONFIG_TERM__MAX = 3, -}; - -struct perf_record_stat_config_entry { - __u64 tag; - __u64 val; -}; - -struct perf_record_stat_config { - struct perf_event_header header; - __u64 nr; - struct perf_record_stat_config_entry data[]; -}; - -struct perf_record_stat { - struct perf_event_header header; - - __u64 id; - __u32 cpu; - __u32 thread; - - union { - struct { - __u64 val; - __u64 ena; - __u64 run; - }; - __u64 values[3]; - }; -}; - -struct perf_record_stat_round { - struct perf_event_header header; - __u64 type; - __u64 time; -}; - -struct perf_record_time_conv { - struct perf_event_header header; - __u64 time_shift; - __u64 time_mult; - __u64 time_zero; -}; - -struct perf_record_header_feature { - struct perf_event_header header; - __u64 feat_id; - char data[]; -}; - -struct perf_record_compressed { - struct perf_event_header header; - char data[]; -}; - -enum perf_user_event_type { /* above any possible kernel type */ - PERF_RECORD_USER_TYPE_START = 64, - PERF_RECORD_HEADER_ATTR = 64, - PERF_RECORD_HEADER_EVENT_TYPE = 65, /* deprecated */ - PERF_RECORD_HEADER_TRACING_DATA = 66, - PERF_RECORD_HEADER_BUILD_ID = 67, - PERF_RECORD_FINISHED_ROUND = 68, - PERF_RECORD_ID_INDEX = 69, - PERF_RECORD_AUXTRACE_INFO = 70, - PERF_RECORD_AUXTRACE = 71, - PERF_RECORD_AUXTRACE_ERROR = 72, - PERF_RECORD_THREAD_MAP = 73, - PERF_RECORD_CPU_MAP = 74, - PERF_RECORD_STAT_CONFIG = 75, - PERF_RECORD_STAT = 76, - PERF_RECORD_STAT_ROUND = 77, - PERF_RECORD_EVENT_UPDATE = 78, - PERF_RECORD_TIME_CONV = 79, - PERF_RECORD_HEADER_FEATURE = 80, - PERF_RECORD_COMPRESSED = 81, - PERF_RECORD_HEADER_MAX -}; - -union perf_event { - struct perf_event_header header; - struct perf_record_mmap mmap; - struct perf_record_mmap2 mmap2; - struct perf_record_comm comm; - struct perf_record_namespaces namespaces; - struct perf_record_fork fork; - struct perf_record_lost lost; - struct perf_record_lost_samples lost_samples; - struct perf_record_read read; - struct perf_record_throttle throttle; - struct perf_record_sample sample; - struct perf_record_bpf_event bpf; - struct perf_record_ksymbol ksymbol; - struct perf_record_header_attr attr; - struct perf_record_event_update event_update; - struct perf_record_header_event_type event_type; - struct perf_record_header_tracing_data tracing_data; - struct perf_record_header_build_id build_id; - struct perf_record_id_index id_index; - struct perf_record_auxtrace_info auxtrace_info; - struct perf_record_auxtrace auxtrace; - struct perf_record_auxtrace_error auxtrace_error; - struct perf_record_aux aux; - struct perf_record_itrace_start itrace_start; - struct perf_record_switch context_switch; - struct perf_record_thread_map thread_map; - struct perf_record_cpu_map cpu_map; - struct perf_record_stat_config stat_config; - struct perf_record_stat stat; - struct perf_record_stat_round stat_round; - struct perf_record_time_conv time_conv; - struct perf_record_header_feature feat; - struct perf_record_compressed pack; -}; - -#endif /* __LIBPERF_EVENT_H */ diff --git a/tools/perf/lib/include/perf/evlist.h b/tools/perf/lib/include/perf/evlist.h deleted file mode 100644 index 0a7479dc13bf..000000000000 --- a/tools/perf/lib/include/perf/evlist.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_EVLIST_H -#define __LIBPERF_EVLIST_H - -#include -#include - -struct perf_evlist; -struct perf_evsel; -struct perf_cpu_map; -struct perf_thread_map; - -LIBPERF_API void perf_evlist__add(struct perf_evlist *evlist, - struct perf_evsel *evsel); -LIBPERF_API void perf_evlist__remove(struct perf_evlist *evlist, - struct perf_evsel *evsel); -LIBPERF_API struct perf_evlist *perf_evlist__new(void); -LIBPERF_API void perf_evlist__delete(struct perf_evlist *evlist); -LIBPERF_API struct perf_evsel* perf_evlist__next(struct perf_evlist *evlist, - struct perf_evsel *evsel); -LIBPERF_API int perf_evlist__open(struct perf_evlist *evlist); -LIBPERF_API void perf_evlist__close(struct perf_evlist *evlist); -LIBPERF_API void perf_evlist__enable(struct perf_evlist *evlist); -LIBPERF_API void perf_evlist__disable(struct perf_evlist *evlist); - -#define perf_evlist__for_each_evsel(evlist, pos) \ - for ((pos) = perf_evlist__next((evlist), NULL); \ - (pos) != NULL; \ - (pos) = perf_evlist__next((evlist), (pos))) - -LIBPERF_API void perf_evlist__set_maps(struct perf_evlist *evlist, - struct perf_cpu_map *cpus, - struct perf_thread_map *threads); -LIBPERF_API int perf_evlist__poll(struct perf_evlist *evlist, int timeout); -LIBPERF_API int perf_evlist__filter_pollfd(struct perf_evlist *evlist, - short revents_and_mask); - -LIBPERF_API int perf_evlist__mmap(struct perf_evlist *evlist, int pages); -LIBPERF_API void perf_evlist__munmap(struct perf_evlist *evlist); - -LIBPERF_API struct perf_mmap *perf_evlist__next_mmap(struct perf_evlist *evlist, - struct perf_mmap *map, - bool overwrite); -#define perf_evlist__for_each_mmap(evlist, pos, overwrite) \ - for ((pos) = perf_evlist__next_mmap((evlist), NULL, overwrite); \ - (pos) != NULL; \ - (pos) = perf_evlist__next_mmap((evlist), (pos), overwrite)) - -#endif /* __LIBPERF_EVLIST_H */ diff --git a/tools/perf/lib/include/perf/evsel.h b/tools/perf/lib/include/perf/evsel.h deleted file mode 100644 index c82ec39a4ad0..000000000000 --- a/tools/perf/lib/include/perf/evsel.h +++ /dev/null @@ -1,40 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_EVSEL_H -#define __LIBPERF_EVSEL_H - -#include -#include - -struct perf_evsel; -struct perf_event_attr; -struct perf_cpu_map; -struct perf_thread_map; - -struct perf_counts_values { - union { - struct { - uint64_t val; - uint64_t ena; - uint64_t run; - }; - uint64_t values[3]; - }; -}; - -LIBPERF_API struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr); -LIBPERF_API void perf_evsel__delete(struct perf_evsel *evsel); -LIBPERF_API int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, - struct perf_thread_map *threads); -LIBPERF_API void perf_evsel__close(struct perf_evsel *evsel); -LIBPERF_API void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu); -LIBPERF_API int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, - struct perf_counts_values *count); -LIBPERF_API int perf_evsel__enable(struct perf_evsel *evsel); -LIBPERF_API int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu); -LIBPERF_API int perf_evsel__disable(struct perf_evsel *evsel); -LIBPERF_API int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu); -LIBPERF_API struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel); -LIBPERF_API struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel); -LIBPERF_API struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel); - -#endif /* __LIBPERF_EVSEL_H */ diff --git a/tools/perf/lib/include/perf/mmap.h b/tools/perf/lib/include/perf/mmap.h deleted file mode 100644 index 9508ad90d8b9..000000000000 --- a/tools/perf/lib/include/perf/mmap.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_MMAP_H -#define __LIBPERF_MMAP_H - -#include - -struct perf_mmap; -union perf_event; - -LIBPERF_API void perf_mmap__consume(struct perf_mmap *map); -LIBPERF_API int perf_mmap__read_init(struct perf_mmap *map); -LIBPERF_API void perf_mmap__read_done(struct perf_mmap *map); -LIBPERF_API union perf_event *perf_mmap__read_event(struct perf_mmap *map); - -#endif /* __LIBPERF_MMAP_H */ diff --git a/tools/perf/lib/include/perf/threadmap.h b/tools/perf/lib/include/perf/threadmap.h deleted file mode 100644 index a7c50de8d010..000000000000 --- a/tools/perf/lib/include/perf/threadmap.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_THREADMAP_H -#define __LIBPERF_THREADMAP_H - -#include -#include - -struct perf_thread_map; - -LIBPERF_API struct perf_thread_map *perf_thread_map__new_dummy(void); - -LIBPERF_API void perf_thread_map__set_pid(struct perf_thread_map *map, int thread, pid_t pid); -LIBPERF_API char *perf_thread_map__comm(struct perf_thread_map *map, int thread); -LIBPERF_API int perf_thread_map__nr(struct perf_thread_map *threads); -LIBPERF_API pid_t perf_thread_map__pid(struct perf_thread_map *map, int thread); - -LIBPERF_API struct perf_thread_map *perf_thread_map__get(struct perf_thread_map *map); -LIBPERF_API void perf_thread_map__put(struct perf_thread_map *map); - -#endif /* __LIBPERF_THREADMAP_H */ diff --git a/tools/perf/lib/internal.h b/tools/perf/lib/internal.h deleted file mode 100644 index 2c27e158de6b..000000000000 --- a/tools/perf/lib/internal.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LIBPERF_INTERNAL_H -#define __LIBPERF_INTERNAL_H - -#include - -void libperf_print(enum libperf_print_level level, - const char *format, ...) - __attribute__((format(printf, 2, 3))); - -#define __pr(level, fmt, ...) \ -do { \ - libperf_print(level, "libperf: " fmt, ##__VA_ARGS__); \ -} while (0) - -#define pr_err(fmt, ...) __pr(LIBPERF_ERR, fmt, ##__VA_ARGS__) -#define pr_warning(fmt, ...) __pr(LIBPERF_WARN, fmt, ##__VA_ARGS__) -#define pr_info(fmt, ...) __pr(LIBPERF_INFO, fmt, ##__VA_ARGS__) -#define pr_debug(fmt, ...) __pr(LIBPERF_DEBUG, fmt, ##__VA_ARGS__) -#define pr_debug2(fmt, ...) __pr(LIBPERF_DEBUG2, fmt, ##__VA_ARGS__) -#define pr_debug3(fmt, ...) __pr(LIBPERF_DEBUG3, fmt, ##__VA_ARGS__) - -#endif /* __LIBPERF_INTERNAL_H */ diff --git a/tools/perf/lib/lib.c b/tools/perf/lib/lib.c deleted file mode 100644 index 18658931fc71..000000000000 --- a/tools/perf/lib/lib.c +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include - -unsigned int page_size; - -static ssize_t ion(bool is_read, int fd, void *buf, size_t n) -{ - void *buf_start = buf; - size_t left = n; - - while (left) { - /* buf must be treated as const if !is_read. */ - ssize_t ret = is_read ? read(fd, buf, left) : - write(fd, buf, left); - - if (ret < 0 && errno == EINTR) - continue; - if (ret <= 0) - return ret; - - left -= ret; - buf += ret; - } - - BUG_ON((size_t)(buf - buf_start) != n); - return n; -} - -/* - * Read exactly 'n' bytes or return an error. - */ -ssize_t readn(int fd, void *buf, size_t n) -{ - return ion(true, fd, buf, n); -} - -/* - * Write exactly 'n' bytes or return an error. - */ -ssize_t writen(int fd, const void *buf, size_t n) -{ - /* ion does not modify buf. */ - return ion(false, fd, (void *)buf, n); -} diff --git a/tools/perf/lib/libperf.map b/tools/perf/lib/libperf.map deleted file mode 100644 index 7be1af8a546c..000000000000 --- a/tools/perf/lib/libperf.map +++ /dev/null @@ -1,51 +0,0 @@ -LIBPERF_0.0.1 { - global: - libperf_init; - perf_cpu_map__dummy_new; - perf_cpu_map__get; - perf_cpu_map__put; - perf_cpu_map__new; - perf_cpu_map__read; - perf_cpu_map__nr; - perf_cpu_map__cpu; - perf_cpu_map__empty; - perf_cpu_map__max; - perf_thread_map__new_dummy; - perf_thread_map__set_pid; - perf_thread_map__comm; - perf_thread_map__nr; - perf_thread_map__pid; - perf_thread_map__get; - perf_thread_map__put; - perf_evsel__new; - perf_evsel__delete; - perf_evsel__enable; - perf_evsel__disable; - perf_evsel__open; - perf_evsel__close; - perf_evsel__read; - perf_evsel__cpus; - perf_evsel__threads; - perf_evsel__attr; - perf_evlist__new; - perf_evlist__delete; - perf_evlist__open; - perf_evlist__close; - perf_evlist__enable; - perf_evlist__disable; - perf_evlist__add; - perf_evlist__remove; - perf_evlist__next; - perf_evlist__set_maps; - perf_evlist__poll; - perf_evlist__mmap; - perf_evlist__munmap; - perf_evlist__filter_pollfd; - perf_evlist__next_mmap; - perf_mmap__consume; - perf_mmap__read_init; - perf_mmap__read_done; - perf_mmap__read_event; - local: - *; -}; diff --git a/tools/perf/lib/libperf.pc.template b/tools/perf/lib/libperf.pc.template deleted file mode 100644 index 117e4a237b55..000000000000 --- a/tools/perf/lib/libperf.pc.template +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) - -prefix=@PREFIX@ -libdir=@LIBDIR@ -includedir=${prefix}/include - -Name: libperf -Description: perf library -Version: @VERSION@ -Libs: -L${libdir} -lperf -Cflags: -I${includedir} diff --git a/tools/perf/lib/mmap.c b/tools/perf/lib/mmap.c deleted file mode 100644 index 79d5ed6c38cc..000000000000 --- a/tools/perf/lib/mmap.c +++ /dev/null @@ -1,275 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "internal.h" - -void perf_mmap__init(struct perf_mmap *map, struct perf_mmap *prev, - bool overwrite, libperf_unmap_cb_t unmap_cb) -{ - map->fd = -1; - map->overwrite = overwrite; - map->unmap_cb = unmap_cb; - refcount_set(&map->refcnt, 0); - if (prev) - prev->next = map; -} - -size_t perf_mmap__mmap_len(struct perf_mmap *map) -{ - return map->mask + 1 + page_size; -} - -int perf_mmap__mmap(struct perf_mmap *map, struct perf_mmap_param *mp, - int fd, int cpu) -{ - map->prev = 0; - map->mask = mp->mask; - map->base = mmap(NULL, perf_mmap__mmap_len(map), mp->prot, - MAP_SHARED, fd, 0); - if (map->base == MAP_FAILED) { - map->base = NULL; - return -1; - } - - map->fd = fd; - map->cpu = cpu; - return 0; -} - -void perf_mmap__munmap(struct perf_mmap *map) -{ - if (map && map->base != NULL) { - munmap(map->base, perf_mmap__mmap_len(map)); - map->base = NULL; - map->fd = -1; - refcount_set(&map->refcnt, 0); - } - if (map && map->unmap_cb) - map->unmap_cb(map); -} - -void perf_mmap__get(struct perf_mmap *map) -{ - refcount_inc(&map->refcnt); -} - -void perf_mmap__put(struct perf_mmap *map) -{ - BUG_ON(map->base && refcount_read(&map->refcnt) == 0); - - if (refcount_dec_and_test(&map->refcnt)) - perf_mmap__munmap(map); -} - -static inline void perf_mmap__write_tail(struct perf_mmap *md, u64 tail) -{ - ring_buffer_write_tail(md->base, tail); -} - -u64 perf_mmap__read_head(struct perf_mmap *map) -{ - return ring_buffer_read_head(map->base); -} - -static bool perf_mmap__empty(struct perf_mmap *map) -{ - struct perf_event_mmap_page *pc = map->base; - - return perf_mmap__read_head(map) == map->prev && !pc->aux_size; -} - -void perf_mmap__consume(struct perf_mmap *map) -{ - if (!map->overwrite) { - u64 old = map->prev; - - perf_mmap__write_tail(map, old); - } - - if (refcount_read(&map->refcnt) == 1 && perf_mmap__empty(map)) - perf_mmap__put(map); -} - -static int overwrite_rb_find_range(void *buf, int mask, u64 *start, u64 *end) -{ - struct perf_event_header *pheader; - u64 evt_head = *start; - int size = mask + 1; - - pr_debug2("%s: buf=%p, start=%"PRIx64"\n", __func__, buf, *start); - pheader = (struct perf_event_header *)(buf + (*start & mask)); - while (true) { - if (evt_head - *start >= (unsigned int)size) { - pr_debug("Finished reading overwrite ring buffer: rewind\n"); - if (evt_head - *start > (unsigned int)size) - evt_head -= pheader->size; - *end = evt_head; - return 0; - } - - pheader = (struct perf_event_header *)(buf + (evt_head & mask)); - - if (pheader->size == 0) { - pr_debug("Finished reading overwrite ring buffer: get start\n"); - *end = evt_head; - return 0; - } - - evt_head += pheader->size; - pr_debug3("move evt_head: %"PRIx64"\n", evt_head); - } - WARN_ONCE(1, "Shouldn't get here\n"); - return -1; -} - -/* - * Report the start and end of the available data in ringbuffer - */ -static int __perf_mmap__read_init(struct perf_mmap *md) -{ - u64 head = perf_mmap__read_head(md); - u64 old = md->prev; - unsigned char *data = md->base + page_size; - unsigned long size; - - md->start = md->overwrite ? head : old; - md->end = md->overwrite ? old : head; - - if ((md->end - md->start) < md->flush) - return -EAGAIN; - - size = md->end - md->start; - if (size > (unsigned long)(md->mask) + 1) { - if (!md->overwrite) { - WARN_ONCE(1, "failed to keep up with mmap data. (warn only once)\n"); - - md->prev = head; - perf_mmap__consume(md); - return -EAGAIN; - } - - /* - * Backward ring buffer is full. We still have a chance to read - * most of data from it. - */ - if (overwrite_rb_find_range(data, md->mask, &md->start, &md->end)) - return -EINVAL; - } - - return 0; -} - -int perf_mmap__read_init(struct perf_mmap *map) -{ - /* - * Check if event was unmapped due to a POLLHUP/POLLERR. - */ - if (!refcount_read(&map->refcnt)) - return -ENOENT; - - return __perf_mmap__read_init(map); -} - -/* - * Mandatory for overwrite mode - * The direction of overwrite mode is backward. - * The last perf_mmap__read() will set tail to map->core.prev. - * Need to correct the map->core.prev to head which is the end of next read. - */ -void perf_mmap__read_done(struct perf_mmap *map) -{ - /* - * Check if event was unmapped due to a POLLHUP/POLLERR. - */ - if (!refcount_read(&map->refcnt)) - return; - - map->prev = perf_mmap__read_head(map); -} - -/* When check_messup is true, 'end' must points to a good entry */ -static union perf_event *perf_mmap__read(struct perf_mmap *map, - u64 *startp, u64 end) -{ - unsigned char *data = map->base + page_size; - union perf_event *event = NULL; - int diff = end - *startp; - - if (diff >= (int)sizeof(event->header)) { - size_t size; - - event = (union perf_event *)&data[*startp & map->mask]; - size = event->header.size; - - if (size < sizeof(event->header) || diff < (int)size) - return NULL; - - /* - * Event straddles the mmap boundary -- header should always - * be inside due to u64 alignment of output. - */ - if ((*startp & map->mask) + size != ((*startp + size) & map->mask)) { - unsigned int offset = *startp; - unsigned int len = min(sizeof(*event), size), cpy; - void *dst = map->event_copy; - - do { - cpy = min(map->mask + 1 - (offset & map->mask), len); - memcpy(dst, &data[offset & map->mask], cpy); - offset += cpy; - dst += cpy; - len -= cpy; - } while (len); - - event = (union perf_event *)map->event_copy; - } - - *startp += size; - } - - return event; -} - -/* - * Read event from ring buffer one by one. - * Return one event for each call. - * - * Usage: - * perf_mmap__read_init() - * while(event = perf_mmap__read_event()) { - * //process the event - * perf_mmap__consume() - * } - * perf_mmap__read_done() - */ -union perf_event *perf_mmap__read_event(struct perf_mmap *map) -{ - union perf_event *event; - - /* - * Check if event was unmapped due to a POLLHUP/POLLERR. - */ - if (!refcount_read(&map->refcnt)) - return NULL; - - /* non-overwirte doesn't pause the ringbuffer */ - if (!map->overwrite) - map->end = perf_mmap__read_head(map); - - event = perf_mmap__read(map, &map->start, map->end); - - if (!map->overwrite) - map->prev = map->start; - - return event; -} diff --git a/tools/perf/lib/tests/Makefile b/tools/perf/lib/tests/Makefile deleted file mode 100644 index a43cd08c5c03..000000000000 --- a/tools/perf/lib/tests/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) - -TESTS = test-cpumap test-threadmap test-evlist test-evsel - -TESTS_SO := $(addsuffix -so,$(TESTS)) -TESTS_A := $(addsuffix -a,$(TESTS)) - -# Set compile option CFLAGS -ifdef EXTRA_CFLAGS - CFLAGS := $(EXTRA_CFLAGS) -else - CFLAGS := -g -Wall -endif - -all: - -include $(srctree)/tools/scripts/Makefile.include - -INCLUDE = -I$(srctree)/tools/perf/lib/include -I$(srctree)/tools/include -I$(srctree)/tools/lib - -$(TESTS_A): FORCE - $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -o $@ $(subst -a,.c,$@) ../libperf.a $(LIBAPI) - -$(TESTS_SO): FORCE - $(QUIET_LINK)$(CC) $(INCLUDE) $(CFLAGS) -L.. -o $@ $(subst -so,.c,$@) $(LIBAPI) -lperf - -all: $(TESTS_A) $(TESTS_SO) - -run: - @echo "running static:" - @for i in $(TESTS_A); do ./$$i; done - @echo "running dynamic:" - @for i in $(TESTS_SO); do LD_LIBRARY_PATH=../ ./$$i; done - -clean: - $(call QUIET_CLEAN, tests)$(RM) $(TESTS_A) $(TESTS_SO) - -.PHONY: all clean FORCE diff --git a/tools/perf/lib/tests/test-cpumap.c b/tools/perf/lib/tests/test-cpumap.c deleted file mode 100644 index c8d45091e7c2..000000000000 --- a/tools/perf/lib/tests/test-cpumap.c +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include - -static int libperf_print(enum libperf_print_level level, - const char *fmt, va_list ap) -{ - return vfprintf(stderr, fmt, ap); -} - -int main(int argc, char **argv) -{ - struct perf_cpu_map *cpus; - - __T_START; - - libperf_init(libperf_print); - - cpus = perf_cpu_map__dummy_new(); - if (!cpus) - return -1; - - perf_cpu_map__get(cpus); - perf_cpu_map__put(cpus); - perf_cpu_map__put(cpus); - - __T_END; - return 0; -} diff --git a/tools/perf/lib/tests/test-evlist.c b/tools/perf/lib/tests/test-evlist.c deleted file mode 100644 index 6d8ebe0c2504..000000000000 --- a/tools/perf/lib/tests/test-evlist.c +++ /dev/null @@ -1,413 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#define _GNU_SOURCE // needed for sched.h to get sched_[gs]etaffinity and CPU_(ZERO,SET) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int libperf_print(enum libperf_print_level level, - const char *fmt, va_list ap) -{ - return vfprintf(stderr, fmt, ap); -} - -static int test_stat_cpu(void) -{ - struct perf_cpu_map *cpus; - struct perf_evlist *evlist; - struct perf_evsel *evsel; - struct perf_event_attr attr1 = { - .type = PERF_TYPE_SOFTWARE, - .config = PERF_COUNT_SW_CPU_CLOCK, - }; - struct perf_event_attr attr2 = { - .type = PERF_TYPE_SOFTWARE, - .config = PERF_COUNT_SW_TASK_CLOCK, - }; - int err, cpu, tmp; - - cpus = perf_cpu_map__new(NULL); - __T("failed to create cpus", cpus); - - evlist = perf_evlist__new(); - __T("failed to create evlist", evlist); - - evsel = perf_evsel__new(&attr1); - __T("failed to create evsel1", evsel); - - perf_evlist__add(evlist, evsel); - - evsel = perf_evsel__new(&attr2); - __T("failed to create evsel2", evsel); - - perf_evlist__add(evlist, evsel); - - perf_evlist__set_maps(evlist, cpus, NULL); - - err = perf_evlist__open(evlist); - __T("failed to open evsel", err == 0); - - perf_evlist__for_each_evsel(evlist, evsel) { - cpus = perf_evsel__cpus(evsel); - - perf_cpu_map__for_each_cpu(cpu, tmp, cpus) { - struct perf_counts_values counts = { .val = 0 }; - - perf_evsel__read(evsel, cpu, 0, &counts); - __T("failed to read value for evsel", counts.val != 0); - } - } - - perf_evlist__close(evlist); - perf_evlist__delete(evlist); - - perf_cpu_map__put(cpus); - return 0; -} - -static int test_stat_thread(void) -{ - struct perf_counts_values counts = { .val = 0 }; - struct perf_thread_map *threads; - struct perf_evlist *evlist; - struct perf_evsel *evsel; - struct perf_event_attr attr1 = { - .type = PERF_TYPE_SOFTWARE, - .config = PERF_COUNT_SW_CPU_CLOCK, - }; - struct perf_event_attr attr2 = { - .type = PERF_TYPE_SOFTWARE, - .config = PERF_COUNT_SW_TASK_CLOCK, - }; - int err; - - threads = perf_thread_map__new_dummy(); - __T("failed to create threads", threads); - - perf_thread_map__set_pid(threads, 0, 0); - - evlist = perf_evlist__new(); - __T("failed to create evlist", evlist); - - evsel = perf_evsel__new(&attr1); - __T("failed to create evsel1", evsel); - - perf_evlist__add(evlist, evsel); - - evsel = perf_evsel__new(&attr2); - __T("failed to create evsel2", evsel); - - perf_evlist__add(evlist, evsel); - - perf_evlist__set_maps(evlist, NULL, threads); - - err = perf_evlist__open(evlist); - __T("failed to open evsel", err == 0); - - perf_evlist__for_each_evsel(evlist, evsel) { - perf_evsel__read(evsel, 0, 0, &counts); - __T("failed to read value for evsel", counts.val != 0); - } - - perf_evlist__close(evlist); - perf_evlist__delete(evlist); - - perf_thread_map__put(threads); - return 0; -} - -static int test_stat_thread_enable(void) -{ - struct perf_counts_values counts = { .val = 0 }; - struct perf_thread_map *threads; - struct perf_evlist *evlist; - struct perf_evsel *evsel; - struct perf_event_attr attr1 = { - .type = PERF_TYPE_SOFTWARE, - .config = PERF_COUNT_SW_CPU_CLOCK, - .disabled = 1, - }; - struct perf_event_attr attr2 = { - .type = PERF_TYPE_SOFTWARE, - .config = PERF_COUNT_SW_TASK_CLOCK, - .disabled = 1, - }; - int err; - - threads = perf_thread_map__new_dummy(); - __T("failed to create threads", threads); - - perf_thread_map__set_pid(threads, 0, 0); - - evlist = perf_evlist__new(); - __T("failed to create evlist", evlist); - - evsel = perf_evsel__new(&attr1); - __T("failed to create evsel1", evsel); - - perf_evlist__add(evlist, evsel); - - evsel = perf_evsel__new(&attr2); - __T("failed to create evsel2", evsel); - - perf_evlist__add(evlist, evsel); - - perf_evlist__set_maps(evlist, NULL, threads); - - err = perf_evlist__open(evlist); - __T("failed to open evsel", err == 0); - - perf_evlist__for_each_evsel(evlist, evsel) { - perf_evsel__read(evsel, 0, 0, &counts); - __T("failed to read value for evsel", counts.val == 0); - } - - perf_evlist__enable(evlist); - - perf_evlist__for_each_evsel(evlist, evsel) { - perf_evsel__read(evsel, 0, 0, &counts); - __T("failed to read value for evsel", counts.val != 0); - } - - perf_evlist__disable(evlist); - - perf_evlist__close(evlist); - perf_evlist__delete(evlist); - - perf_thread_map__put(threads); - return 0; -} - -static int test_mmap_thread(void) -{ - struct perf_evlist *evlist; - struct perf_evsel *evsel; - struct perf_mmap *map; - struct perf_cpu_map *cpus; - struct perf_thread_map *threads; - struct perf_event_attr attr = { - .type = PERF_TYPE_TRACEPOINT, - .sample_period = 1, - .wakeup_watermark = 1, - .disabled = 1, - }; - char path[PATH_MAX]; - int id, err, pid, go_pipe[2]; - union perf_event *event; - char bf; - int count = 0; - - snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id", - sysfs__mountpoint()); - - if (filename__read_int(path, &id)) { - fprintf(stderr, "error: failed to get tracepoint id: %s\n", path); - return -1; - } - - attr.config = id; - - err = pipe(go_pipe); - __T("failed to create pipe", err == 0); - - fflush(NULL); - - pid = fork(); - if (!pid) { - int i; - - read(go_pipe[0], &bf, 1); - - /* Generate 100 prctl calls. */ - for (i = 0; i < 100; i++) - prctl(0, 0, 0, 0, 0); - - exit(0); - } - - threads = perf_thread_map__new_dummy(); - __T("failed to create threads", threads); - - cpus = perf_cpu_map__dummy_new(); - __T("failed to create cpus", cpus); - - perf_thread_map__set_pid(threads, 0, pid); - - evlist = perf_evlist__new(); - __T("failed to create evlist", evlist); - - evsel = perf_evsel__new(&attr); - __T("failed to create evsel1", evsel); - - perf_evlist__add(evlist, evsel); - - perf_evlist__set_maps(evlist, cpus, threads); - - err = perf_evlist__open(evlist); - __T("failed to open evlist", err == 0); - - err = perf_evlist__mmap(evlist, 4); - __T("failed to mmap evlist", err == 0); - - perf_evlist__enable(evlist); - - /* kick the child and wait for it to finish */ - write(go_pipe[1], &bf, 1); - waitpid(pid, NULL, 0); - - /* - * There's no need to call perf_evlist__disable, - * monitored process is dead now. - */ - - perf_evlist__for_each_mmap(evlist, map, false) { - if (perf_mmap__read_init(map) < 0) - continue; - - while ((event = perf_mmap__read_event(map)) != NULL) { - count++; - perf_mmap__consume(map); - } - - perf_mmap__read_done(map); - } - - /* calls perf_evlist__munmap/perf_evlist__close */ - perf_evlist__delete(evlist); - - perf_thread_map__put(threads); - perf_cpu_map__put(cpus); - - /* - * The generated prctl calls should match the - * number of events in the buffer. - */ - __T("failed count", count == 100); - - return 0; -} - -static int test_mmap_cpus(void) -{ - struct perf_evlist *evlist; - struct perf_evsel *evsel; - struct perf_mmap *map; - struct perf_cpu_map *cpus; - struct perf_event_attr attr = { - .type = PERF_TYPE_TRACEPOINT, - .sample_period = 1, - .wakeup_watermark = 1, - .disabled = 1, - }; - cpu_set_t saved_mask; - char path[PATH_MAX]; - int id, err, cpu, tmp; - union perf_event *event; - int count = 0; - - snprintf(path, PATH_MAX, "%s/kernel/debug/tracing/events/syscalls/sys_enter_prctl/id", - sysfs__mountpoint()); - - if (filename__read_int(path, &id)) { - fprintf(stderr, "error: failed to get tracepoint id: %s\n", path); - return -1; - } - - attr.config = id; - - cpus = perf_cpu_map__new(NULL); - __T("failed to create cpus", cpus); - - evlist = perf_evlist__new(); - __T("failed to create evlist", evlist); - - evsel = perf_evsel__new(&attr); - __T("failed to create evsel1", evsel); - - perf_evlist__add(evlist, evsel); - - perf_evlist__set_maps(evlist, cpus, NULL); - - err = perf_evlist__open(evlist); - __T("failed to open evlist", err == 0); - - err = perf_evlist__mmap(evlist, 4); - __T("failed to mmap evlist", err == 0); - - perf_evlist__enable(evlist); - - err = sched_getaffinity(0, sizeof(saved_mask), &saved_mask); - __T("sched_getaffinity failed", err == 0); - - perf_cpu_map__for_each_cpu(cpu, tmp, cpus) { - cpu_set_t mask; - - CPU_ZERO(&mask); - CPU_SET(cpu, &mask); - - err = sched_setaffinity(0, sizeof(mask), &mask); - __T("sched_setaffinity failed", err == 0); - - prctl(0, 0, 0, 0, 0); - } - - err = sched_setaffinity(0, sizeof(saved_mask), &saved_mask); - __T("sched_setaffinity failed", err == 0); - - perf_evlist__disable(evlist); - - perf_evlist__for_each_mmap(evlist, map, false) { - if (perf_mmap__read_init(map) < 0) - continue; - - while ((event = perf_mmap__read_event(map)) != NULL) { - count++; - perf_mmap__consume(map); - } - - perf_mmap__read_done(map); - } - - /* calls perf_evlist__munmap/perf_evlist__close */ - perf_evlist__delete(evlist); - - /* - * The generated prctl events should match the - * number of cpus or be bigger (we are system-wide). - */ - __T("failed count", count >= perf_cpu_map__nr(cpus)); - - perf_cpu_map__put(cpus); - - return 0; -} - -int main(int argc, char **argv) -{ - __T_START; - - libperf_init(libperf_print); - - test_stat_cpu(); - test_stat_thread(); - test_stat_thread_enable(); - test_mmap_thread(); - test_mmap_cpus(); - - __T_END; - return 0; -} diff --git a/tools/perf/lib/tests/test-evsel.c b/tools/perf/lib/tests/test-evsel.c deleted file mode 100644 index 135722ac965b..000000000000 --- a/tools/perf/lib/tests/test-evsel.c +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include - -static int libperf_print(enum libperf_print_level level, - const char *fmt, va_list ap) -{ - return vfprintf(stderr, fmt, ap); -} - -static int test_stat_cpu(void) -{ - struct perf_cpu_map *cpus; - struct perf_evsel *evsel; - struct perf_event_attr attr = { - .type = PERF_TYPE_SOFTWARE, - .config = PERF_COUNT_SW_CPU_CLOCK, - }; - int err, cpu, tmp; - - cpus = perf_cpu_map__new(NULL); - __T("failed to create cpus", cpus); - - evsel = perf_evsel__new(&attr); - __T("failed to create evsel", evsel); - - err = perf_evsel__open(evsel, cpus, NULL); - __T("failed to open evsel", err == 0); - - perf_cpu_map__for_each_cpu(cpu, tmp, cpus) { - struct perf_counts_values counts = { .val = 0 }; - - perf_evsel__read(evsel, cpu, 0, &counts); - __T("failed to read value for evsel", counts.val != 0); - } - - perf_evsel__close(evsel); - perf_evsel__delete(evsel); - - perf_cpu_map__put(cpus); - return 0; -} - -static int test_stat_thread(void) -{ - struct perf_counts_values counts = { .val = 0 }; - struct perf_thread_map *threads; - struct perf_evsel *evsel; - struct perf_event_attr attr = { - .type = PERF_TYPE_SOFTWARE, - .config = PERF_COUNT_SW_TASK_CLOCK, - }; - int err; - - threads = perf_thread_map__new_dummy(); - __T("failed to create threads", threads); - - perf_thread_map__set_pid(threads, 0, 0); - - evsel = perf_evsel__new(&attr); - __T("failed to create evsel", evsel); - - err = perf_evsel__open(evsel, NULL, threads); - __T("failed to open evsel", err == 0); - - perf_evsel__read(evsel, 0, 0, &counts); - __T("failed to read value for evsel", counts.val != 0); - - perf_evsel__close(evsel); - perf_evsel__delete(evsel); - - perf_thread_map__put(threads); - return 0; -} - -static int test_stat_thread_enable(void) -{ - struct perf_counts_values counts = { .val = 0 }; - struct perf_thread_map *threads; - struct perf_evsel *evsel; - struct perf_event_attr attr = { - .type = PERF_TYPE_SOFTWARE, - .config = PERF_COUNT_SW_TASK_CLOCK, - .disabled = 1, - }; - int err; - - threads = perf_thread_map__new_dummy(); - __T("failed to create threads", threads); - - perf_thread_map__set_pid(threads, 0, 0); - - evsel = perf_evsel__new(&attr); - __T("failed to create evsel", evsel); - - err = perf_evsel__open(evsel, NULL, threads); - __T("failed to open evsel", err == 0); - - perf_evsel__read(evsel, 0, 0, &counts); - __T("failed to read value for evsel", counts.val == 0); - - err = perf_evsel__enable(evsel); - __T("failed to enable evsel", err == 0); - - perf_evsel__read(evsel, 0, 0, &counts); - __T("failed to read value for evsel", counts.val != 0); - - err = perf_evsel__disable(evsel); - __T("failed to enable evsel", err == 0); - - perf_evsel__close(evsel); - perf_evsel__delete(evsel); - - perf_thread_map__put(threads); - return 0; -} - -int main(int argc, char **argv) -{ - __T_START; - - libperf_init(libperf_print); - - test_stat_cpu(); - test_stat_thread(); - test_stat_thread_enable(); - - __T_END; - return 0; -} diff --git a/tools/perf/lib/tests/test-threadmap.c b/tools/perf/lib/tests/test-threadmap.c deleted file mode 100644 index 7dc4d6fbedde..000000000000 --- a/tools/perf/lib/tests/test-threadmap.c +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include - -static int libperf_print(enum libperf_print_level level, - const char *fmt, va_list ap) -{ - return vfprintf(stderr, fmt, ap); -} - -int main(int argc, char **argv) -{ - struct perf_thread_map *threads; - - __T_START; - - libperf_init(libperf_print); - - threads = perf_thread_map__new_dummy(); - if (!threads) - return -1; - - perf_thread_map__get(threads); - perf_thread_map__put(threads); - perf_thread_map__put(threads); - - __T_END; - return 0; -} diff --git a/tools/perf/lib/threadmap.c b/tools/perf/lib/threadmap.c deleted file mode 100644 index e92c368b0a6c..000000000000 --- a/tools/perf/lib/threadmap.c +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include - -static void perf_thread_map__reset(struct perf_thread_map *map, int start, int nr) -{ - size_t size = (nr - start) * sizeof(map->map[0]); - - memset(&map->map[start], 0, size); - map->err_thread = -1; -} - -struct perf_thread_map *perf_thread_map__realloc(struct perf_thread_map *map, int nr) -{ - size_t size = sizeof(*map) + sizeof(map->map[0]) * nr; - int start = map ? map->nr : 0; - - map = realloc(map, size); - /* - * We only realloc to add more items, let's reset new items. - */ - if (map) - perf_thread_map__reset(map, start, nr); - - return map; -} - -#define thread_map__alloc(__nr) perf_thread_map__realloc(NULL, __nr) - -void perf_thread_map__set_pid(struct perf_thread_map *map, int thread, pid_t pid) -{ - map->map[thread].pid = pid; -} - -char *perf_thread_map__comm(struct perf_thread_map *map, int thread) -{ - return map->map[thread].comm; -} - -struct perf_thread_map *perf_thread_map__new_dummy(void) -{ - struct perf_thread_map *threads = thread_map__alloc(1); - - if (threads != NULL) { - perf_thread_map__set_pid(threads, 0, -1); - threads->nr = 1; - refcount_set(&threads->refcnt, 1); - } - return threads; -} - -static void perf_thread_map__delete(struct perf_thread_map *threads) -{ - if (threads) { - int i; - - WARN_ONCE(refcount_read(&threads->refcnt) != 0, - "thread map refcnt unbalanced\n"); - for (i = 0; i < threads->nr; i++) - free(perf_thread_map__comm(threads, i)); - free(threads); - } -} - -struct perf_thread_map *perf_thread_map__get(struct perf_thread_map *map) -{ - if (map) - refcount_inc(&map->refcnt); - return map; -} - -void perf_thread_map__put(struct perf_thread_map *map) -{ - if (map && refcount_dec_and_test(&map->refcnt)) - perf_thread_map__delete(map); -} - -int perf_thread_map__nr(struct perf_thread_map *threads) -{ - return threads ? threads->nr : 1; -} - -pid_t perf_thread_map__pid(struct perf_thread_map *map, int thread) -{ - return map->map[thread].pid; -} diff --git a/tools/perf/lib/xyarray.c b/tools/perf/lib/xyarray.c deleted file mode 100644 index dcd901d154bb..000000000000 --- a/tools/perf/lib/xyarray.c +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include - -struct xyarray *xyarray__new(int xlen, int ylen, size_t entry_size) -{ - size_t row_size = ylen * entry_size; - struct xyarray *xy = zalloc(sizeof(*xy) + xlen * row_size); - - if (xy != NULL) { - xy->entry_size = entry_size; - xy->row_size = row_size; - xy->entries = xlen * ylen; - xy->max_x = xlen; - xy->max_y = ylen; - } - - return xy; -} - -void xyarray__reset(struct xyarray *xy) -{ - size_t n = xy->entries * xy->entry_size; - - memset(xy->contents, 0, n); -} - -void xyarray__delete(struct xyarray *xy) -{ - free(xy); -} -- cgit From 81de3bf37a8bf58ecdbef608d16ddb0f4bbb71ca Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 6 Dec 2019 22:06:12 +0100 Subject: libperf: Add man pages Change the man page generation to asciidoc, because it's easier to use and has been more commonly used in related projects. Remove the current rst pages. Add 3 man pages to have a base for more additions: libperf.3 - overall description libperf-counting.7 - counting basics explained on simple example libperf-sampling.7 - sampling basics explained on simple example The plan is to add more man pages to cover the basic API. The build generates html and man pages: $ cd tools/lib/perf/Documentation $ make ASCIIDOC libperf.xml XMLTO libperf.3 ASCIIDOC libperf-counting.xml XMLTO libperf-counting.7 ASCIIDOC libperf-sampling.xml XMLTO libperf-sampling.7 ASCIIDOC libperf.html ASCIIDOC libperf-counting.html ASCIIDOC libperf-sampling.html Add the following install targets: install-man - man pages install-html - html version of man pages install-examples - examples mentioned in the man pages Signed-off-by: Jiri Olsa Cc: Alexander Shishkin Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20191206210612.8676-3-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/Documentation/Makefile | 159 ++++++++++++- tools/lib/perf/Documentation/asciidoc.conf | 120 ++++++++++ tools/lib/perf/Documentation/examples/sampling.c | 119 ++++++++++ tools/lib/perf/Documentation/libperf-counting.txt | 211 ++++++++++++++++++ tools/lib/perf/Documentation/libperf-sampling.txt | 243 ++++++++++++++++++++ tools/lib/perf/Documentation/libperf.txt | 246 +++++++++++++++++++++ tools/lib/perf/Documentation/man/libperf.rst | 100 --------- tools/lib/perf/Documentation/manpage-1.72.xsl | 14 ++ tools/lib/perf/Documentation/manpage-base.xsl | 35 +++ .../perf/Documentation/manpage-bold-literal.xsl | 17 ++ tools/lib/perf/Documentation/manpage-normal.xsl | 13 ++ .../lib/perf/Documentation/manpage-suppress-sp.xsl | 21 ++ tools/lib/perf/Documentation/tutorial/tutorial.rst | 123 ----------- tools/lib/perf/Makefile | 5 +- 14 files changed, 1197 insertions(+), 229 deletions(-) create mode 100644 tools/lib/perf/Documentation/asciidoc.conf create mode 100644 tools/lib/perf/Documentation/examples/sampling.c create mode 100644 tools/lib/perf/Documentation/libperf-counting.txt create mode 100644 tools/lib/perf/Documentation/libperf-sampling.txt create mode 100644 tools/lib/perf/Documentation/libperf.txt delete mode 100644 tools/lib/perf/Documentation/man/libperf.rst create mode 100644 tools/lib/perf/Documentation/manpage-1.72.xsl create mode 100644 tools/lib/perf/Documentation/manpage-base.xsl create mode 100644 tools/lib/perf/Documentation/manpage-bold-literal.xsl create mode 100644 tools/lib/perf/Documentation/manpage-normal.xsl create mode 100644 tools/lib/perf/Documentation/manpage-suppress-sp.xsl delete mode 100644 tools/lib/perf/Documentation/tutorial/tutorial.rst (limited to 'tools') diff --git a/tools/lib/perf/Documentation/Makefile b/tools/lib/perf/Documentation/Makefile index 586425a88795..972754082a85 100644 --- a/tools/lib/perf/Documentation/Makefile +++ b/tools/lib/perf/Documentation/Makefile @@ -1,7 +1,156 @@ -all: - rst2man man/libperf.rst > man/libperf.7 - rst2pdf tutorial/tutorial.rst +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +# Most of this file is copied from tools/perf/Documentation/Makefile + +include ../../../scripts/Makefile.include +include ../../../scripts/utilities.mak + +MAN3_TXT = libperf.txt +MAN7_TXT = libperf-counting.txt libperf-sampling.txt +MAN_EX = examples/*.c + +MAN_TXT = $(MAN3_TXT) $(MAN7_TXT) + +_MAN_XML = $(patsubst %.txt,%.xml,$(MAN_TXT)) +_MAN_HTML = $(patsubst %.txt,%.html,$(MAN_TXT)) +_MAN_3 = $(patsubst %.txt,%.3,$(MAN3_TXT)) +_MAN_7 = $(patsubst %.txt,%.7,$(MAN7_TXT)) + +MAN_XML = $(addprefix $(OUTPUT),$(_MAN_XML)) +MAN_HTML = $(addprefix $(OUTPUT),$(_MAN_HTML)) +MAN_3 = $(addprefix $(OUTPUT),$(_MAN_3)) +MAN_7 = $(addprefix $(OUTPUT),$(_MAN_7)) +MAN_X = $(MAN_3) $(MAN_7) + +# Make the path relative to DESTDIR, not prefix +ifndef DESTDIR + prefix ?=$(HOME) +endif + +mandir ?= $(prefix)/share/man +man3dir = $(mandir)/man3 +man7dir = $(mandir)/man7 + +docdir ?= $(prefix)/share/doc/libperf +htmldir = $(docdir)/html +exdir = $(docdir)/examples + +ASCIIDOC = asciidoc +ASCIIDOC_EXTRA = --unsafe -f asciidoc.conf +ASCIIDOC_HTML = xhtml11 +MANPAGE_XSL = manpage-normal.xsl +XMLTO_EXTRA = +XMLTO =xmlto + +INSTALL ?= install +RM ?= rm -f + +# For asciidoc ... +# -7.1.2, no extra settings are needed. +# 8.0-, set ASCIIDOC8. +# + +# For docbook-xsl ... +# -1.68.1, set ASCIIDOC_NO_ROFF? (based on changelog from 1.73.0) +# 1.69.0, no extra settings are needed? +# 1.69.1-1.71.0, set DOCBOOK_SUPPRESS_SP? +# 1.71.1, no extra settings are needed? +# 1.72.0, set DOCBOOK_XSL_172. +# 1.73.0-, set ASCIIDOC_NO_ROFF + +# If you had been using DOCBOOK_XSL_172 in an attempt to get rid +# of 'the ".ft C" problem' in your generated manpages, and you +# instead ended up with weird characters around callouts, try +# using ASCIIDOC_NO_ROFF instead (it works fine with ASCIIDOC8). + +ifdef ASCIIDOC8 + ASCIIDOC_EXTRA += -a asciidoc7compatible +endif +ifdef DOCBOOK_XSL_172 + ASCIIDOC_EXTRA += -a libperf-asciidoc-no-roff + MANPAGE_XSL = manpage-1.72.xsl +else + ifdef ASCIIDOC_NO_ROFF + # docbook-xsl after 1.72 needs the regular XSL, but will not + # pass-thru raw roff codes from asciidoc.conf, so turn them off. + ASCIIDOC_EXTRA += -a libperf-asciidoc-no-roff + endif +endif +ifdef MAN_BOLD_LITERAL + XMLTO_EXTRA += -m manpage-bold-literal.xsl +endif +ifdef DOCBOOK_SUPPRESS_SP + XMLTO_EXTRA += -m manpage-suppress-sp.xsl +endif + +DESTDIR ?= +DESTDIR_SQ = '$(subst ','\'',$(DESTDIR))' + +export DESTDIR DESTDIR_SQ + +# Please note that there is a minor bug in asciidoc. +# The version after 6.0.3 _will_ include the patch found here: +# http://marc.theaimsgroup.com/?l=libtraceevent&m=111558757202243&w=2 +# +# Until that version is released you may have to apply the patch +# yourself - yes, all 6 characters of it! + +QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir +QUIET_SUBDIR1 = + +ifneq ($(findstring $(MAKEFLAGS),w),w) + PRINT_DIR = --no-print-directory +else # "make -w" + NO_SUBDIR = : +endif + +ifneq ($(findstring $(MAKEFLAGS),s),s) + ifneq ($(V),1) + QUIET_ASCIIDOC = @echo ' ASCIIDOC '$@; + QUIET_XMLTO = @echo ' XMLTO '$@; + endif +endif + +all: $(MAN_X) $(MAN_HTML) + +$(MAN_HTML) $(MAN_X): asciidoc.conf + +install-man: all + $(call QUIET_INSTALL, man) \ + $(INSTALL) -d -m 755 $(DESTDIR)$(man3dir); \ + $(INSTALL) -m 644 $(MAN_3) $(DESTDIR)$(man3dir); \ + $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir); \ + $(INSTALL) -m 644 $(MAN_7) $(DESTDIR)$(man7dir); + +install-html: + $(call QUIET_INSTALL, html) \ + $(INSTALL) -d -m 755 $(DESTDIR)$(htmldir); \ + $(INSTALL) -m 644 $(MAN_HTML) $(DESTDIR)$(htmldir); \ + +install-examples: + $(call QUIET_INSTALL, examples) \ + $(INSTALL) -d -m 755 $(DESTDIR)$(exdir); \ + $(INSTALL) -m 644 $(MAN_EX) $(DESTDIR)$(exdir); \ + +CLEAN_FILES = \ + $(MAN_XML) $(addsuffix +,$(MAN_XML)) \ + $(MAN_HTML) $(addsuffix +,$(MAN_HTML)) \ + $(MAN_X) clean: - rm -f man/libperf.7 - rm -f tutorial/tutorial.pdf + $(call QUIET_CLEAN, Documentation) $(RM) $(CLEAN_FILES) + +$(MAN_3): $(OUTPUT)%.3: %.xml + $(QUIET_XMLTO)$(XMLTO) -o $(OUTPUT). -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< + +$(MAN_7): $(OUTPUT)%.7: %.xml + $(QUIET_XMLTO)$(XMLTO) -o $(OUTPUT). -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< + +$(MAN_XML): $(OUTPUT)%.xml: %.txt + $(QUIET_ASCIIDOC)$(ASCIIDOC) -b docbook -d manpage \ + $(ASCIIDOC_EXTRA) -alibperf_version=$(EVENT_PARSE_VERSION) -o $@+ $< && \ + mv $@+ $@ + +$(MAN_HTML): $(OUTPUT)%.html: %.txt + $(QUIET_ASCIIDOC)$(ASCIIDOC) -b $(ASCIIDOC_HTML) -d manpage \ + $(ASCIIDOC_EXTRA) -aperf_version=$(EVENT_PARSE_VERSION) -o $@+ $< && \ + mv $@+ $@ diff --git a/tools/lib/perf/Documentation/asciidoc.conf b/tools/lib/perf/Documentation/asciidoc.conf new file mode 100644 index 000000000000..9d5a5a5ee091 --- /dev/null +++ b/tools/lib/perf/Documentation/asciidoc.conf @@ -0,0 +1,120 @@ +## linktep: macro +# +# Usage: linktep:command[manpage-section] +# +# Note, {0} is the manpage section, while {target} is the command. +# +# Show TEP link as: (
); if section is defined, else just show +# the command. + +[macros] +(?su)[\\]?(?Plinktep):(?P\S*?)\[(?P.*?)\]= + +[attributes] +asterisk=* +plus=+ +caret=^ +startsb=[ +endsb=] +tilde=~ + +ifdef::backend-docbook[] +[linktep-inlinemacro] +{0%{target}} +{0#} +{0#{target}{0}} +{0#} +endif::backend-docbook[] + +ifdef::backend-docbook[] +ifndef::tep-asciidoc-no-roff[] +# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. +# v1.72 breaks with this because it replaces dots not in roff requests. +[listingblock] +{title} + +ifdef::doctype-manpage[] + .ft C +endif::doctype-manpage[] +| +ifdef::doctype-manpage[] + .ft +endif::doctype-manpage[] + +{title#} +endif::tep-asciidoc-no-roff[] + +ifdef::tep-asciidoc-no-roff[] +ifdef::doctype-manpage[] +# The following two small workarounds insert a simple paragraph after screen +[listingblock] +{title} + +| + +{title#} + +[verseblock] +{title} +{title%} +{title#} +| + +{title#} +{title%} +endif::doctype-manpage[] +endif::tep-asciidoc-no-roff[] +endif::backend-docbook[] + +ifdef::doctype-manpage[] +ifdef::backend-docbook[] +[header] +template::[header-declarations] + + +{mantitle} +{manvolnum} +libperf +{libperf_version} +libperf Manual + + + {manname1} + {manname2} + {manname3} + {manname4} + {manname5} + {manname6} + {manname7} + {manname8} + {manname9} + {manname10} + {manname11} + {manname12} + {manname13} + {manname14} + {manname15} + {manname16} + {manname17} + {manname18} + {manname19} + {manname20} + {manname21} + {manname22} + {manname23} + {manname24} + {manname25} + {manname26} + {manname27} + {manname28} + {manname29} + {manname30} + {manpurpose} + +endif::backend-docbook[] +endif::doctype-manpage[] + +ifdef::backend-xhtml11[] +[linktep-inlinemacro] +{target}{0?({0})} +endif::backend-xhtml11[] diff --git a/tools/lib/perf/Documentation/examples/sampling.c b/tools/lib/perf/Documentation/examples/sampling.c new file mode 100644 index 000000000000..8e1a926a9cfe --- /dev/null +++ b/tools/lib/perf/Documentation/examples/sampling.c @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int libperf_print(enum libperf_print_level level, + const char *fmt, va_list ap) +{ + return vfprintf(stderr, fmt, ap); +} + +union u64_swap { + __u64 val64; + __u32 val32[2]; +}; + +int main(int argc, char **argv) +{ + struct perf_evlist *evlist; + struct perf_evsel *evsel; + struct perf_mmap *map; + struct perf_cpu_map *cpus; + struct perf_event_attr attr = { + .type = PERF_TYPE_HARDWARE, + .config = PERF_COUNT_HW_CPU_CYCLES, + .disabled = 1, + .freq = 1, + .sample_freq = 10, + .sample_type = PERF_SAMPLE_IP|PERF_SAMPLE_TID|PERF_SAMPLE_CPU|PERF_SAMPLE_PERIOD, + }; + int err = -1; + union perf_event *event; + + libperf_init(libperf_print); + + cpus = perf_cpu_map__new(NULL); + if (!cpus) { + fprintf(stderr, "failed to create cpus\n"); + return -1; + } + + evlist = perf_evlist__new(); + if (!evlist) { + fprintf(stderr, "failed to create evlist\n"); + goto out_cpus; + } + + evsel = perf_evsel__new(&attr); + if (!evsel) { + fprintf(stderr, "failed to create cycles\n"); + goto out_cpus; + } + + perf_evlist__add(evlist, evsel); + + perf_evlist__set_maps(evlist, cpus, NULL); + + err = perf_evlist__open(evlist); + if (err) { + fprintf(stderr, "failed to open evlist\n"); + goto out_evlist; + } + + err = perf_evlist__mmap(evlist, 4); + if (err) { + fprintf(stderr, "failed to mmap evlist\n"); + goto out_evlist; + } + + perf_evlist__enable(evlist); + sleep(3); + perf_evlist__disable(evlist); + + perf_evlist__for_each_mmap(evlist, map, false) { + if (perf_mmap__read_init(map) < 0) + continue; + + while ((event = perf_mmap__read_event(map)) != NULL) { + int cpu, pid, tid; + __u64 ip, period, *array; + union u64_swap u; + + array = event->sample.array; + + ip = *array; + array++; + + u.val64 = *array; + pid = u.val32[0]; + tid = u.val32[1]; + array++; + + u.val64 = *array; + cpu = u.val32[0]; + array++; + + period = *array; + + fprintf(stdout, "cpu %3d, pid %6d, tid %6d, ip %20llx, period %20llu\n", + cpu, pid, tid, ip, period); + + perf_mmap__consume(map); + } + + perf_mmap__read_done(map); + } + +out_evlist: + perf_evlist__delete(evlist); +out_cpus: + perf_cpu_map__put(cpus); + return err; +} diff --git a/tools/lib/perf/Documentation/libperf-counting.txt b/tools/lib/perf/Documentation/libperf-counting.txt new file mode 100644 index 000000000000..cae9757f49c1 --- /dev/null +++ b/tools/lib/perf/Documentation/libperf-counting.txt @@ -0,0 +1,211 @@ +libperf-counting(7) +=================== + +NAME +---- +libperf-counting - counting interface + +DESCRIPTION +----------- +The counting interface provides API to meassure and get count for specific perf events. + +The following test tries to explain count on `counting.c` example. + +It is by no means complete guide to counting, but shows libperf basic API for counting. + +The `counting.c` comes with libbperf package and can be compiled and run like: + +[source,bash] +-- +$ gcc -o counting counting.c -lperf +$ sudo ./counting +count 176792, enabled 176944, run 176944 +count 176242, enabled 176242, run 176242 +-- + +It requires root access, because of the `PERF_COUNT_SW_CPU_CLOCK` event, +which is available only for root. + +The `counting.c` example monitors two events on the current process and displays their count, in a nutshel it: + +* creates events +* adds them to the event list +* opens and enables events through the event list +* does some workload +* disables events +* reads and displays event counts +* destroys the event list + +The first thing you need to do before using libperf is to call init function: + +[source,c] +-- + 8 static int libperf_print(enum libperf_print_level level, + 9 const char *fmt, va_list ap) + 10 { + 11 return vfprintf(stderr, fmt, ap); + 12 } + + 14 int main(int argc, char **argv) + 15 { + ... + 35 libperf_init(libperf_print); +-- + +It will setup the library and sets function for debug output from library. + +The `libperf_print` callback will receive any message with its debug level, +defined as: + +[source,c] +-- +enum libperf_print_level { + LIBPERF_ERR, + LIBPERF_WARN, + LIBPERF_INFO, + LIBPERF_DEBUG, + LIBPERF_DEBUG2, + LIBPERF_DEBUG3, +}; +-- + +Once the setup is complete we start by defining specific events using the `struct perf_event_attr`. + +We create software events for cpu and task: + +[source,c] +-- + 20 struct perf_event_attr attr1 = { + 21 .type = PERF_TYPE_SOFTWARE, + 22 .config = PERF_COUNT_SW_CPU_CLOCK, + 23 .read_format = PERF_FORMAT_TOTAL_TIME_ENABLED|PERF_FORMAT_TOTAL_TIME_RUNNING, + 24 .disabled = 1, + 25 }; + 26 struct perf_event_attr attr2 = { + 27 .type = PERF_TYPE_SOFTWARE, + 28 .config = PERF_COUNT_SW_TASK_CLOCK, + 29 .read_format = PERF_FORMAT_TOTAL_TIME_ENABLED|PERF_FORMAT_TOTAL_TIME_RUNNING, + 30 .disabled = 1, + 31 }; +-- + +The `read_format` setup tells perf to include timing details together with each count. + +Next step is to prepare threads map. + +In this case we will monitor current process, so we create threads map with single pid (0): + +[source,c] +-- + 37 threads = perf_thread_map__new_dummy(); + 38 if (!threads) { + 39 fprintf(stderr, "failed to create threads\n"); + 40 return -1; + 41 } + 42 + 43 perf_thread_map__set_pid(threads, 0, 0); +-- + +Now we create libperf's event list, which will serve as holder for the events we want: + +[source,c] +-- + 45 evlist = perf_evlist__new(); + 46 if (!evlist) { + 47 fprintf(stderr, "failed to create evlist\n"); + 48 goto out_threads; + 49 } +-- + +We create libperf's events for the attributes we defined earlier and add them to the list: + +[source,c] +-- + 51 evsel = perf_evsel__new(&attr1); + 52 if (!evsel) { + 53 fprintf(stderr, "failed to create evsel1\n"); + 54 goto out_evlist; + 55 } + 56 + 57 perf_evlist__add(evlist, evsel); + 58 + 59 evsel = perf_evsel__new(&attr2); + 60 if (!evsel) { + 61 fprintf(stderr, "failed to create evsel2\n"); + 62 goto out_evlist; + 63 } + 64 + 65 perf_evlist__add(evlist, evsel); +-- + +Configure event list with the thread map and open events: + +[source,c] +-- + 67 perf_evlist__set_maps(evlist, NULL, threads); + 68 + 69 err = perf_evlist__open(evlist); + 70 if (err) { + 71 fprintf(stderr, "failed to open evsel\n"); + 72 goto out_evlist; + 73 } +-- + +Both events are created as disabled (note the `disabled = 1` assignment above), +so we need to enable the whole list explicitely (both events). + +From this moment events are counting and we can do our workload. + +When we are done we disable the events list. + +[source,c] +-- + 75 perf_evlist__enable(evlist); + 76 + 77 while (count--); + 78 + 79 perf_evlist__disable(evlist); +-- + +Now we need to get the counts from events, following code iterates throught the events list and read counts: + +[source,c] +-- + 81 perf_evlist__for_each_evsel(evlist, evsel) { + 82 perf_evsel__read(evsel, 0, 0, &counts); + 83 fprintf(stdout, "count %llu, enabled %llu, run %llu\n", + 84 counts.val, counts.ena, counts.run); + 85 } +-- + +And finaly cleanup. + +We close the whole events list (both events) and remove it together with the threads map: + +[source,c] +-- + 87 perf_evlist__close(evlist); + 88 + 89 out_evlist: + 90 perf_evlist__delete(evlist); + 91 out_threads: + 92 perf_thread_map__put(threads); + 93 return err; + 94 } +-- + +REPORTING BUGS +-------------- +Report bugs to . + +LICENSE +------- +libperf is Free Software licensed under the GNU LGPL 2.1 + +RESOURCES +--------- +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git + +SEE ALSO +-------- +libperf(3), libperf-sampling(7) diff --git a/tools/lib/perf/Documentation/libperf-sampling.txt b/tools/lib/perf/Documentation/libperf-sampling.txt new file mode 100644 index 000000000000..d71a7b4fcf5f --- /dev/null +++ b/tools/lib/perf/Documentation/libperf-sampling.txt @@ -0,0 +1,243 @@ +libperf-sampling(7) +=================== + +NAME +---- +libperf-sampling - sampling interface + + +DESCRIPTION +----------- +The sampling interface provides API to meassure and get count for specific perf events. + +The following test tries to explain count on `sampling.c` example. + +It is by no means complete guide to sampling, but shows libperf basic API for sampling. + +The `sampling.c` comes with libbperf package and can be compiled and run like: + +[source,bash] +-- +$ gcc -o sampling sampling.c -lperf +$ sudo ./sampling +cpu 0, pid 0, tid 0, ip ffffffffad06c4e6, period 1 +cpu 0, pid 4465, tid 4469, ip ffffffffad118748, period 18322959 +cpu 0, pid 0, tid 0, ip ffffffffad115722, period 33544846 +cpu 0, pid 4465, tid 4470, ip 7f84fe0cdad6, period 23687474 +cpu 0, pid 0, tid 0, ip ffffffffad9e0349, period 34255790 +cpu 0, pid 4465, tid 4469, ip ffffffffad136581, period 38664069 +cpu 0, pid 0, tid 0, ip ffffffffad9e55e2, period 21922384 +cpu 0, pid 4465, tid 4470, ip 7f84fe0ebebf, period 17655175 +... +-- + +It requires root access, because it uses hardware cycles event. + +The `sampling.c` example profiles/samples all CPUs with hardware cycles, in a nutshel it: + +- creates events +- adds them to the event list +- opens and enables events through the event list +- sleeps for 3 seconds +- disables events +- reads and displays recorded samples +- destroys the event list + +The first thing you need to do before using libperf is to call init function: + +[source,c] +-- + 12 static int libperf_print(enum libperf_print_level level, + 13 const char *fmt, va_list ap) + 14 { + 15 return vfprintf(stderr, fmt, ap); + 16 } + + 23 int main(int argc, char **argv) + 24 { + ... + 40 libperf_init(libperf_print); +-- + +It will setup the library and sets function for debug output from library. + +The `libperf_print` callback will receive any message with its debug level, +defined as: + +[source,c] +-- +enum libperf_print_level { + LIBPERF_ERR, + LIBPERF_WARN, + LIBPERF_INFO, + LIBPERF_DEBUG, + LIBPERF_DEBUG2, + LIBPERF_DEBUG3, +}; +-- + +Once the setup is complete we start by defining cycles event using the `struct perf_event_attr`: + +[source,c] +-- + 29 struct perf_event_attr attr = { + 30 .type = PERF_TYPE_HARDWARE, + 31 .config = PERF_COUNT_HW_CPU_CYCLES, + 32 .disabled = 1, + 33 .freq = 1, + 34 .sample_freq = 10, + 35 .sample_type = PERF_SAMPLE_IP|PERF_SAMPLE_TID|PERF_SAMPLE_CPU|PERF_SAMPLE_PERIOD, + 36 }; +-- + +Next step is to prepare cpus map. + +In this case we will monitor all the available CPUs: + +[source,c] +-- + 42 cpus = perf_cpu_map__new(NULL); + 43 if (!cpus) { + 44 fprintf(stderr, "failed to create cpus\n"); + 45 return -1; + 46 } +-- + +Now we create libperf's event list, which will serve as holder for the cycles event: + +[source,c] +-- + 48 evlist = perf_evlist__new(); + 49 if (!evlist) { + 50 fprintf(stderr, "failed to create evlist\n"); + 51 goto out_cpus; + 52 } +-- + +We create libperf's event for the cycles attribute we defined earlier and add it to the list: + +[source,c] +-- + 54 evsel = perf_evsel__new(&attr); + 55 if (!evsel) { + 56 fprintf(stderr, "failed to create cycles\n"); + 57 goto out_cpus; + 58 } + 59 + 60 perf_evlist__add(evlist, evsel); +-- + +Configure event list with the cpus map and open event: + +[source,c] +-- + 62 perf_evlist__set_maps(evlist, cpus, NULL); + 63 + 64 err = perf_evlist__open(evlist); + 65 if (err) { + 66 fprintf(stderr, "failed to open evlist\n"); + 67 goto out_evlist; + 68 } +-- + +Once the events list is open, we can create memory maps AKA perf ring buffers: + +[source,c] +-- + 70 err = perf_evlist__mmap(evlist, 4); + 71 if (err) { + 72 fprintf(stderr, "failed to mmap evlist\n"); + 73 goto out_evlist; + 74 } +-- + +The event is created as disabled (note the `disabled = 1` assignment above), +so we need to enable the events list explicitely. + +From this moment the cycles event is sampling. + +We will sleep for 3 seconds while the ring buffers get data from all CPUs, then we disable the events list. + +[source,c] +-- + 76 perf_evlist__enable(evlist); + 77 sleep(3); + 78 perf_evlist__disable(evlist); +-- + +Following code walks through the ring buffers and reads stored events/samples: + +[source,c] +-- + 80 perf_evlist__for_each_mmap(evlist, map, false) { + 81 if (perf_mmap__read_init(map) < 0) + 82 continue; + 83 + 84 while ((event = perf_mmap__read_event(map)) != NULL) { + + /* process event */ + +108 perf_mmap__consume(map); +109 } +110 perf_mmap__read_done(map); +111 } + +-- + +Each sample needs to get parsed: + +[source,c] +-- + 85 int cpu, pid, tid; + 86 __u64 ip, period, *array; + 87 union u64_swap u; + 88 + 89 array = event->sample.array; + 90 + 91 ip = *array; + 92 array++; + 93 + 94 u.val64 = *array; + 95 pid = u.val32[0]; + 96 tid = u.val32[1]; + 97 array++; + 98 + 99 u.val64 = *array; +100 cpu = u.val32[0]; +101 array++; +102 +103 period = *array; +104 +105 fprintf(stdout, "cpu %3d, pid %6d, tid %6d, ip %20llx, period %20llu\n", +106 cpu, pid, tid, ip, period); +-- + +And finaly cleanup. + +We close the whole events list (both events) and remove it together with the threads map: + +[source,c] +-- +113 out_evlist: +114 perf_evlist__delete(evlist); +115 out_cpus: +116 perf_cpu_map__put(cpus); +117 return err; +118 } +-- + +REPORTING BUGS +-------------- +Report bugs to . + +LICENSE +------- +libperf is Free Software licensed under the GNU LGPL 2.1 + +RESOURCES +--------- +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git + +SEE ALSO +-------- +libperf(3), libperf-counting(7) diff --git a/tools/lib/perf/Documentation/libperf.txt b/tools/lib/perf/Documentation/libperf.txt new file mode 100644 index 000000000000..5a6bb512789d --- /dev/null +++ b/tools/lib/perf/Documentation/libperf.txt @@ -0,0 +1,246 @@ +libperf(3) +========== + +NAME +---- +libperf - Linux kernel perf event library + + +SYNOPSIS +-------- +*Generic API:* + +[source,c] +-- + #include + + enum libperf_print_level { + LIBPERF_ERR, + LIBPERF_WARN, + LIBPERF_INFO, + LIBPERF_DEBUG, + LIBPERF_DEBUG2, + LIBPERF_DEBUG3, + }; + + typedef int (*libperf_print_fn_t)(enum libperf_print_level level, + const char *, va_list ap); + + void libperf_init(libperf_print_fn_t fn); +-- + +*API to handle cpu maps:* + +[source,c] +-- + #include + + struct perf_cpu_map; + + struct perf_cpu_map *perf_cpu_map__dummy_new(void); + struct perf_cpu_map *perf_cpu_map__new(const char *cpu_list); + struct perf_cpu_map *perf_cpu_map__read(FILE *file); + struct perf_cpu_map *perf_cpu_map__get(struct perf_cpu_map *map); + struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, + struct perf_cpu_map *other); + void perf_cpu_map__put(struct perf_cpu_map *map); + int perf_cpu_map__cpu(const struct perf_cpu_map *cpus, int idx); + int perf_cpu_map__nr(const struct perf_cpu_map *cpus); + bool perf_cpu_map__empty(const struct perf_cpu_map *map); + int perf_cpu_map__max(struct perf_cpu_map *map); + + #define perf_cpu_map__for_each_cpu(cpu, idx, cpus) +-- + +*API to handle thread maps:* + +[source,c] +-- + #include + + struct perf_thread_map; + + struct perf_thread_map *perf_thread_map__new_dummy(void); + + void perf_thread_map__set_pid(struct perf_thread_map *map, int thread, pid_t pid); + char *perf_thread_map__comm(struct perf_thread_map *map, int thread); + int perf_thread_map__nr(struct perf_thread_map *threads); + pid_t perf_thread_map__pid(struct perf_thread_map *map, int thread); + + struct perf_thread_map *perf_thread_map__get(struct perf_thread_map *map); + void perf_thread_map__put(struct perf_thread_map *map); +-- + +*API to handle event lists:* + +[source,c] +-- + #include + + struct perf_evlist; + + void perf_evlist__add(struct perf_evlist *evlist, + struct perf_evsel *evsel); + void perf_evlist__remove(struct perf_evlist *evlist, + struct perf_evsel *evsel); + struct perf_evlist *perf_evlist__new(void); + void perf_evlist__delete(struct perf_evlist *evlist); + struct perf_evsel* perf_evlist__next(struct perf_evlist *evlist, + struct perf_evsel *evsel); + int perf_evlist__open(struct perf_evlist *evlist); + void perf_evlist__close(struct perf_evlist *evlist); + void perf_evlist__enable(struct perf_evlist *evlist); + void perf_evlist__disable(struct perf_evlist *evlist); + + #define perf_evlist__for_each_evsel(evlist, pos) + + void perf_evlist__set_maps(struct perf_evlist *evlist, + struct perf_cpu_map *cpus, + struct perf_thread_map *threads); + int perf_evlist__poll(struct perf_evlist *evlist, int timeout); + int perf_evlist__filter_pollfd(struct perf_evlist *evlist, + short revents_and_mask); + + int perf_evlist__mmap(struct perf_evlist *evlist, int pages); + void perf_evlist__munmap(struct perf_evlist *evlist); + + struct perf_mmap *perf_evlist__next_mmap(struct perf_evlist *evlist, + struct perf_mmap *map, + bool overwrite); + + #define perf_evlist__for_each_mmap(evlist, pos, overwrite) +-- + +*API to handle events:* + +[source,c] +-- + #include * + + struct perf_evsel; + + struct perf_counts_values { + union { + struct { + uint64_t val; + uint64_t ena; + uint64_t run; + }; + uint64_t values[3]; + }; + }; + + struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr); + void perf_evsel__delete(struct perf_evsel *evsel); + int perf_evsel__open(struct perf_evsel *evsel, struct perf_cpu_map *cpus, + struct perf_thread_map *threads); + void perf_evsel__close(struct perf_evsel *evsel); + void perf_evsel__close_cpu(struct perf_evsel *evsel, int cpu); + int perf_evsel__read(struct perf_evsel *evsel, int cpu, int thread, + struct perf_counts_values *count); + int perf_evsel__enable(struct perf_evsel *evsel); + int perf_evsel__enable_cpu(struct perf_evsel *evsel, int cpu); + int perf_evsel__disable(struct perf_evsel *evsel); + int perf_evsel__disable_cpu(struct perf_evsel *evsel, int cpu); + struct perf_cpu_map *perf_evsel__cpus(struct perf_evsel *evsel); + struct perf_thread_map *perf_evsel__threads(struct perf_evsel *evsel); + struct perf_event_attr *perf_evsel__attr(struct perf_evsel *evsel); +-- + +*API to handle maps (perf ring buffers):* + +[source,c] +-- + #include + + struct perf_mmap; + + void perf_mmap__consume(struct perf_mmap *map); + int perf_mmap__read_init(struct perf_mmap *map); + void perf_mmap__read_done(struct perf_mmap *map); + union perf_event *perf_mmap__read_event(struct perf_mmap *map); +-- + +*Structures to access perf API events:* + +[source,c] +-- + #include + + struct perf_record_mmap; + struct perf_record_mmap2; + struct perf_record_comm; + struct perf_record_namespaces; + struct perf_record_fork; + struct perf_record_lost; + struct perf_record_lost_samples; + struct perf_record_read; + struct perf_record_throttle; + struct perf_record_ksymbol; + struct perf_record_bpf_event; + struct perf_record_sample; + struct perf_record_switch; + struct perf_record_header_attr; + struct perf_record_record_cpu_map; + struct perf_record_cpu_map_data; + struct perf_record_cpu_map; + struct perf_record_event_update_cpus; + struct perf_record_event_update_scale; + struct perf_record_event_update; + struct perf_trace_event_type; + struct perf_record_header_event_type; + struct perf_record_header_tracing_data; + struct perf_record_header_build_id; + struct perf_record_id_index; + struct perf_record_auxtrace_info; + struct perf_record_auxtrace; + struct perf_record_auxtrace_error; + struct perf_record_aux; + struct perf_record_itrace_start; + struct perf_record_thread_map_entry; + struct perf_record_thread_map; + struct perf_record_stat_config_entry; + struct perf_record_stat_config; + struct perf_record_stat; + struct perf_record_stat_round; + struct perf_record_time_conv; + struct perf_record_header_feature; + struct perf_record_compressed; +-- + +DESCRIPTION +----------- +The libperf library provides an API to access the linux kernel perf +events subsystem. + +Following objects are key to the libperf interface: + +[horizontal] + +struct perf_cpu_map:: Provides a cpu list abstraction. + +struct perf_thread_map:: Provides a thread list abstraction. + +struct perf_evsel:: Provides an abstraction for single a perf event. + +struct perf_evlist:: Gathers several struct perf_evsel object and performs functions on all of them. + +struct perf_mmap:: Provides an abstraction for accessing perf ring buffer. + +The exported API functions bind these objects together. + +REPORTING BUGS +-------------- +Report bugs to . + +LICENSE +------- +libperf is Free Software licensed under the GNU LGPL 2.1 + +RESOURCES +--------- +https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git + +SEE ALSO +-------- +libperf-sampling(7), libperf-counting(7) diff --git a/tools/lib/perf/Documentation/man/libperf.rst b/tools/lib/perf/Documentation/man/libperf.rst deleted file mode 100644 index 09a270fccb9c..000000000000 --- a/tools/lib/perf/Documentation/man/libperf.rst +++ /dev/null @@ -1,100 +0,0 @@ -.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) - -libperf - -The libperf library provides an API to access the linux kernel perf -events subsystem. It provides the following high level objects: - - - struct perf_cpu_map - - struct perf_thread_map - - struct perf_evlist - - struct perf_evsel - -reference -========= -Function reference by header files: - -perf/core.h ------------ -.. code-block:: c - - typedef int (\*libperf_print_fn_t)(enum libperf_print_level level, - const char \*, va_list ap); - - void libperf_set_print(libperf_print_fn_t fn); - -perf/cpumap.h -------------- -.. code-block:: c - - struct perf_cpu_map \*perf_cpu_map__dummy_new(void); - struct perf_cpu_map \*perf_cpu_map__new(const char \*cpu_list); - struct perf_cpu_map \*perf_cpu_map__read(FILE \*file); - struct perf_cpu_map \*perf_cpu_map__get(struct perf_cpu_map \*map); - void perf_cpu_map__put(struct perf_cpu_map \*map); - int perf_cpu_map__cpu(const struct perf_cpu_map \*cpus, int idx); - int perf_cpu_map__nr(const struct perf_cpu_map \*cpus); - perf_cpu_map__for_each_cpu(cpu, idx, cpus) - -perf/threadmap.h ----------------- -.. code-block:: c - - struct perf_thread_map \*perf_thread_map__new_dummy(void); - void perf_thread_map__set_pid(struct perf_thread_map \*map, int thread, pid_t pid); - char \*perf_thread_map__comm(struct perf_thread_map \*map, int thread); - struct perf_thread_map \*perf_thread_map__get(struct perf_thread_map \*map); - void perf_thread_map__put(struct perf_thread_map \*map); - -perf/evlist.h -------------- -.. code-block:: - - void perf_evlist__init(struct perf_evlist \*evlist); - void perf_evlist__add(struct perf_evlist \*evlist, - struct perf_evsel \*evsel); - void perf_evlist__remove(struct perf_evlist \*evlist, - struct perf_evsel \*evsel); - struct perf_evlist \*perf_evlist__new(void); - void perf_evlist__delete(struct perf_evlist \*evlist); - struct perf_evsel\* perf_evlist__next(struct perf_evlist \*evlist, - struct perf_evsel \*evsel); - int perf_evlist__open(struct perf_evlist \*evlist); - void perf_evlist__close(struct perf_evlist \*evlist); - void perf_evlist__enable(struct perf_evlist \*evlist); - void perf_evlist__disable(struct perf_evlist \*evlist); - perf_evlist__for_each_evsel(evlist, pos) - void perf_evlist__set_maps(struct perf_evlist \*evlist, - struct perf_cpu_map \*cpus, - struct perf_thread_map \*threads); - -perf/evsel.h ------------- -.. code-block:: c - - struct perf_counts_values { - union { - struct { - uint64_t val; - uint64_t ena; - uint64_t run; - }; - uint64_t values[3]; - }; - }; - - void perf_evsel__init(struct perf_evsel \*evsel, - struct perf_event_attr \*attr); - struct perf_evsel \*perf_evsel__new(struct perf_event_attr \*attr); - void perf_evsel__delete(struct perf_evsel \*evsel); - int perf_evsel__open(struct perf_evsel \*evsel, struct perf_cpu_map \*cpus, - struct perf_thread_map \*threads); - void perf_evsel__close(struct perf_evsel \*evsel); - int perf_evsel__read(struct perf_evsel \*evsel, int cpu, int thread, - struct perf_counts_values \*count); - int perf_evsel__enable(struct perf_evsel \*evsel); - int perf_evsel__disable(struct perf_evsel \*evsel); - int perf_evsel__apply_filter(struct perf_evsel \*evsel, const char \*filter); - struct perf_cpu_map \*perf_evsel__cpus(struct perf_evsel \*evsel); - struct perf_thread_map \*perf_evsel__threads(struct perf_evsel \*evsel); - struct perf_event_attr \*perf_evsel__attr(struct perf_evsel \*evsel); diff --git a/tools/lib/perf/Documentation/manpage-1.72.xsl b/tools/lib/perf/Documentation/manpage-1.72.xsl new file mode 100644 index 000000000000..b4d315cb8c47 --- /dev/null +++ b/tools/lib/perf/Documentation/manpage-1.72.xsl @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/tools/lib/perf/Documentation/manpage-base.xsl b/tools/lib/perf/Documentation/manpage-base.xsl new file mode 100644 index 000000000000..a264fa616093 --- /dev/null +++ b/tools/lib/perf/Documentation/manpage-base.xsl @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + sp + + + + + + + + br + + + diff --git a/tools/lib/perf/Documentation/manpage-bold-literal.xsl b/tools/lib/perf/Documentation/manpage-bold-literal.xsl new file mode 100644 index 000000000000..608eb5df6281 --- /dev/null +++ b/tools/lib/perf/Documentation/manpage-bold-literal.xsl @@ -0,0 +1,17 @@ + + + + + + + fB + + + fR + + + diff --git a/tools/lib/perf/Documentation/manpage-normal.xsl b/tools/lib/perf/Documentation/manpage-normal.xsl new file mode 100644 index 000000000000..a48f5b11f3dc --- /dev/null +++ b/tools/lib/perf/Documentation/manpage-normal.xsl @@ -0,0 +1,13 @@ + + + + + + +\ +. + + diff --git a/tools/lib/perf/Documentation/manpage-suppress-sp.xsl b/tools/lib/perf/Documentation/manpage-suppress-sp.xsl new file mode 100644 index 000000000000..a63c7632a87d --- /dev/null +++ b/tools/lib/perf/Documentation/manpage-suppress-sp.xsl @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + diff --git a/tools/lib/perf/Documentation/tutorial/tutorial.rst b/tools/lib/perf/Documentation/tutorial/tutorial.rst deleted file mode 100644 index 7be7bc27b385..000000000000 --- a/tools/lib/perf/Documentation/tutorial/tutorial.rst +++ /dev/null @@ -1,123 +0,0 @@ -.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) - -libperf tutorial -================ - -Compile and install libperf from kernel sources -=============================================== -.. code-block:: bash - - git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git - cd linux/tools/perf/lib - make - sudo make install prefix=/usr - -Libperf object -============== -The libperf library provides several high level objects: - -struct perf_cpu_map - Provides a cpu list abstraction. - -struct perf_thread_map - Provides a thread list abstraction. - -struct perf_evsel - Provides an abstraction for single a perf event. - -struct perf_evlist - Gathers several struct perf_evsel object and performs functions on all of them. - -The exported API binds these objects together, -for full reference see the libperf.7 man page. - -Examples -======== -Examples aim to explain libperf functionality on simple use cases. -They are based in on a checked out linux kernel git tree: - -.. code-block:: bash - - $ cd tools/perf/lib/Documentation/tutorial/ - $ ls -d ex-* - ex-1-compile ex-2-evsel-stat ex-3-evlist-stat - -ex-1-compile example -==================== -This example shows the basic usage of *struct perf_cpu_map*, -how to create it and display its cpus: - -.. code-block:: bash - - $ cd ex-1-compile/ - $ make - gcc -o test test.c -lperf - $ ./test - 0 1 2 3 4 5 6 7 - - -The full code listing is here: - -.. code-block:: c - - 1 #include - 2 - 3 int main(int argc, char **Argv) - 4 { - 5 struct perf_cpu_map *cpus; - 6 int cpu, tmp; - 7 - 8 cpus = perf_cpu_map__new(NULL); - 9 - 10 perf_cpu_map__for_each_cpu(cpu, tmp, cpus) - 11 fprintf(stdout, "%d ", cpu); - 12 - 13 fprintf(stdout, "\n"); - 14 - 15 perf_cpu_map__put(cpus); - 16 return 0; - 17 } - - -First you need to include the proper header to have *struct perf_cpumap* -declaration and functions: - -.. code-block:: c - - 1 #include - - -The *struct perf_cpumap* object is created by *perf_cpu_map__new* call. -The *NULL* argument asks it to populate the object with the current online CPUs list: - -.. code-block:: c - - 8 cpus = perf_cpu_map__new(NULL); - -This is paired with a *perf_cpu_map__put*, that drops its reference at the end, possibly deleting it. - -.. code-block:: c - - 15 perf_cpu_map__put(cpus); - -The iteration through the *struct perf_cpumap* CPUs is done using the *perf_cpu_map__for_each_cpu* -macro which requires 3 arguments: - -- cpu - the cpu numer -- tmp - iteration helper variable -- cpus - the *struct perf_cpumap* object - -.. code-block:: c - - 10 perf_cpu_map__for_each_cpu(cpu, tmp, cpus) - 11 fprintf(stdout, "%d ", cpu); - -ex-2-evsel-stat example -======================= - -TBD - -ex-3-evlist-stat example -======================== - -TBD diff --git a/tools/lib/perf/Makefile b/tools/lib/perf/Makefile index 768dd423730b..3718d65cffac 100644 --- a/tools/lib/perf/Makefile +++ b/tools/lib/perf/Makefile @@ -181,7 +181,10 @@ install_pkgconfig: $(LIBPERF_PC) $(call QUIET_INSTALL, $(LIBPERF_PC)) \ $(call do_install,$(LIBPERF_PC),$(libdir_SQ)/pkgconfig,644) -install: install_lib install_headers install_pkgconfig +install_doc: + $(Q)$(MAKE) -C Documentation install-man install-html install-examples + +install: install_lib install_headers install_pkgconfig install_doc FORCE: -- cgit From 6794200fa3c9c3e6759dae099145f23e4310f4f7 Mon Sep 17 00:00:00 2001 From: Andrey Zhizhikin Date: Wed, 11 Dec 2019 08:01:09 +0000 Subject: tools lib api fs: Fix gcc9 stringop-truncation compilation error GCC9 introduced string hardening mechanisms, which exhibits the error during fs api compilation: error: '__builtin_strncpy' specified bound 4096 equals destination size [-Werror=stringop-truncation] This comes when the length of copy passed to strncpy is is equal to destination size, which could potentially lead to buffer overflow. There is a need to mitigate this potential issue by limiting the size of destination by 1 and explicitly terminate the destination with NULL. Signed-off-by: Andrey Zhizhikin Reviewed-by: Petr Mladek Acked-by: Jiri Olsa Cc: Alexei Starovoitov Cc: Andrii Nakryiko Cc: Daniel Borkmann Cc: Kefeng Wang Cc: Martin KaFai Lau Cc: Petr Mladek Cc: Sergey Senozhatsky Cc: Song Liu Cc: Yonghong Song Cc: bpf@vger.kernel.org Cc: netdev@vger.kernel.org Link: http://lore.kernel.org/lkml/20191211080109.18765-1-andrey.zhizhikin@leica-geosystems.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/api/fs/fs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/lib/api/fs/fs.c b/tools/lib/api/fs/fs.c index 11b3885e833e..027b18f7ed8c 100644 --- a/tools/lib/api/fs/fs.c +++ b/tools/lib/api/fs/fs.c @@ -210,6 +210,7 @@ static bool fs__env_override(struct fs *fs) size_t name_len = strlen(fs->name); /* name + "_PATH" + '\0' */ char upper_name[name_len + 5 + 1]; + memcpy(upper_name, fs->name, name_len); mem_toupper(upper_name, name_len); strcpy(&upper_name[name_len], "_PATH"); @@ -219,7 +220,8 @@ static bool fs__env_override(struct fs *fs) return false; fs->found = true; - strncpy(fs->path, override_path, sizeof(fs->path)); + strncpy(fs->path, override_path, sizeof(fs->path) - 1); + fs->path[sizeof(fs->path) - 1] = '\0'; return true; } -- cgit From 3f7774033e6820d25beee5cf7aefa11d4968b951 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 16 Dec 2019 13:22:33 -0300 Subject: perf hists browser: Restore ESC as "Zoom out" of DSO/thread/etc We need to set actions->ms.map since 599a2f38a989 ("perf hists browser: Check sort keys before hot key actions"), as in that patch we bail out if map is NULL. Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Namhyung Kim Fixes: 599a2f38a989 ("perf hists browser: Check sort keys before hot key actions") Link: https://lkml.kernel.org/n/tip-wp1ssoewy6zihwwexqpohv0j@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index d4d3558fdef4..cfc6172ecab7 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -3062,6 +3062,7 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, continue; } + actions->ms.map = map; top = pstack__peek(browser->pstack); if (top == &browser->hists->dso_filter) { /* -- cgit From 9218a9132f83d2c08cd23c1fd8e8e9b63b47cb5f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 12 Dec 2019 10:02:33 -0300 Subject: perf report/top: Make ENTER consistently bring up menu When callchains are present the ENTER key switches from bringing up the menu that offers Annotation, Zoom by DSO, etc to expanding/collapsing one callchain level, causing confusion, fix it by making it consistently bring up the menu and use '+' to expand/collapse one callchain level. Next patch will also add an entry to the menu to allow expanding/collapsing, so that people used to ENTER expanding one callchain level can quickly find it and use it instead. Reported-by: Linus Torvalds Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-bjz35omktig8cwn6lbj1ifns@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index cfc6172ecab7..fefa505d4fa8 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -706,7 +706,7 @@ int hist_browser__run(struct hist_browser *browser, const char *help, browser->show_headers = !browser->show_headers; hist_browser__update_rows(browser); break; - case K_ENTER: + case '+': if (hist_browser__toggle_fold(browser)) break; /* fall thru */ @@ -2858,6 +2858,7 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, "For symbolic views (--sort has sym):\n\n" \ "ENTER Zoom into DSO/Threads & Annotate current symbol\n" \ "ESC Zoom out\n" \ + "+ Expand/Collapse one callchain level\n" \ "a Annotate current symbol\n" \ "C Collapse all callchains\n" \ "d Zoom into current DSO\n" \ -- cgit From d5a599d9890f51cb2cabfa21f8c38bb6f51f4bb2 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 12 Dec 2019 10:58:21 -0300 Subject: perf report/top: Add menu entry for toggling callchain expansion Since previously pressing ENTER toggled expansion/collapse of callchain entries and now brings up the same menu used when callchains are not present, add an entry so that users can quickly figure out the change in behaviour. Its worth mentioning that we also always had 'e'/'c' to expand/collapse all entries in a hist entry and 'E'/'C' for all hist entries. Suggested-by: Linus Torvalds Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-f9o03jo29fypvd8ly3j49d36@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index fefa505d4fa8..1b5a5990dddb 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2527,6 +2527,26 @@ add_dso_opt(struct hist_browser *browser, struct popup_action *act, return 1; } +static int do_toggle_callchain(struct hist_browser *browser, struct popup_action *act __maybe_unused) +{ + hist_browser__toggle_fold(browser); + return 0; +} + +static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr) +{ + struct hist_entry *he = browser->he_selection; + + if (!he->has_children) + return 0; + + if (asprintf(optstr, "Expand/Collapse callchain") < 0) + return 0; + + act->fn = do_toggle_callchain; + return 1; +} + static int do_browse_map(struct hist_browser *browser __maybe_unused, struct popup_action *act) @@ -3137,6 +3157,7 @@ skip_annotation: &options[nr_options], thread); nr_options += add_dso_opt(browser, &actions[nr_options], &options[nr_options], map); + nr_options += add_callchain_toggle_opt(browser, &actions[nr_options], &options[nr_options]); nr_options += add_map_opt(browser, &actions[nr_options], &options[nr_options], browser->selection ? -- cgit From bdc633fec50be7e6856b9dee89af9bb7c5e9a04f Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 12 Dec 2019 11:48:23 -0300 Subject: perf report/top: Improve toggle callchain menu option Taking into account the current status of the callchain, i.e. if folded, show "Expand", otherwise "Collapse", also show the name of the entry that will be affected and mention the hotkeys for expanding/collapsing all callchains below the main entry, the one that appears with/without callchains. Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Linus Torvalds Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-03arm6poo8463k5tfcfp7gkk@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 54 +++++++++++++++++++++++++++++++++++++++--- tools/perf/util/sort.c | 3 +-- tools/perf/util/sort.h | 2 ++ 3 files changed, 54 insertions(+), 5 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 1b5a5990dddb..a4413d983216 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -391,6 +391,52 @@ static void hist_entry__init_have_children(struct hist_entry *he) he->init_have_children = true; } +static bool hist_browser__selection_has_children(struct hist_browser *browser) +{ + struct hist_entry *he = browser->he_selection; + struct map_symbol *ms = browser->selection; + + if (!he || !ms) + return false; + + if (ms == &he->ms) + return he->has_children; + + return container_of(ms, struct callchain_list, ms)->has_children; +} + +static bool hist_browser__selection_unfolded(struct hist_browser *browser) +{ + struct hist_entry *he = browser->he_selection; + struct map_symbol *ms = browser->selection; + + if (!he || !ms) + return false; + + if (ms == &he->ms) + return he->unfolded; + + return container_of(ms, struct callchain_list, ms)->unfolded; +} + +static char *hist_browser__selection_sym_name(struct hist_browser *browser, char *bf, size_t size) +{ + struct hist_entry *he = browser->he_selection; + struct map_symbol *ms = browser->selection; + struct callchain_list *callchain_entry; + + if (!he || !ms) + return NULL; + + if (ms == &he->ms) { + hist_entry__sym_snprintf(he, bf, size, 0); + return bf + 4; // skip the level, e.g. '[k] ' + } + + callchain_entry = container_of(ms, struct callchain_list, ms); + return callchain_list__sym_name(callchain_entry, bf, size, browser->show_dso); +} + static bool hist_browser__toggle_fold(struct hist_browser *browser) { struct hist_entry *he = browser->he_selection; @@ -2535,12 +2581,14 @@ static int do_toggle_callchain(struct hist_browser *browser, struct popup_action static int add_callchain_toggle_opt(struct hist_browser *browser, struct popup_action *act, char **optstr) { - struct hist_entry *he = browser->he_selection; + char sym_name[512]; - if (!he->has_children) + if (!hist_browser__selection_has_children(browser)) return 0; - if (asprintf(optstr, "Expand/Collapse callchain") < 0) + if (asprintf(optstr, "%s [%s] callchain (one level, same as '+' hotkey, use 'e'/'c' for the whole main level entry)", + hist_browser__selection_unfolded(browser) ? "Collapse" : "Expand", + hist_browser__selection_sym_name(browser, sym_name, sizeof(sym_name))) < 0) return 0; act->fn = do_toggle_callchain; diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 9fcba2872130..ab0cfd790ad0 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -324,8 +324,7 @@ static int _hist_entry__sym_snprintf(struct map_symbol *ms, return ret; } -static int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, - size_t size, unsigned int width) +int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width) { return _hist_entry__sym_snprintf(&he->ms, he->ip, he->level, bf, size, width); diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 5aff9542d9b7..6c862d62d052 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -164,6 +164,8 @@ static __pure inline bool hist_entry__has_callchains(struct hist_entry *he) return he->callchain_size != 0; } +int hist_entry__sym_snprintf(struct hist_entry *he, char *bf, size_t size, unsigned int width); + static inline bool hist_entry__has_pairs(struct hist_entry *he) { return !list_empty(&he->pairs.node); -- cgit From 632003f400d341592b6af8d96bd83b74a0329fe3 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 12 Dec 2019 11:55:54 -0300 Subject: perf hists browser: Generalize the do_zoom_dso() function We'll use it to provide a top level hotkey to zoom into the kernel dso directly. Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Linus Torvalds Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-ae9cjel6v05wjnz9r6z77b6x@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index a4413d983216..8aba1aeea0eb 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2530,11 +2530,8 @@ add_thread_opt(struct hist_browser *browser, struct popup_action *act, return 1; } -static int -do_zoom_dso(struct hist_browser *browser, struct popup_action *act) +static int hists_browser__zoom_map(struct hist_browser *browser, struct map *map) { - struct map *map = act->ms.map; - if (!hists__has(browser->hists, dso) || map == NULL) return 0; @@ -2556,6 +2553,12 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act) return 0; } +static int +do_zoom_dso(struct hist_browser *browser, struct popup_action *act) +{ + return hists_browser__zoom_map(browser, act->ms.map); +} + static int add_dso_opt(struct hist_browser *browser, struct popup_action *act, char **optstr, struct map *map) -- cgit From 209f4e70a2f10bc6819eb20c5bc3988be31972c6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 12 Dec 2019 13:06:36 -0300 Subject: perf report/top: Add 'k' hotkey to zoom directly into the kernel map As a convenience, equivalent to pressing Enter in a line with a kernel symbol and then selecting "Zoom" into the kernel DSO. Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Linus Torvalds Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-vbnlnrpyfvz9deqoobtc3dz7@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 8aba1aeea0eb..6dfdd8d5a743 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -18,7 +18,9 @@ #include "../../util/evlist.h" #include "../../util/header.h" #include "../../util/hist.h" +#include "../../util/machine.h" #include "../../util/map.h" +#include "../../util/maps.h" #include "../../util/symbol.h" #include "../../util/map_symbol.h" #include "../../util/branch.h" @@ -2566,7 +2568,7 @@ add_dso_opt(struct hist_browser *browser, struct popup_action *act, if (!hists__has(browser->hists, dso) || map == NULL) return 0; - if (asprintf(optstr, "Zoom %s %s DSO", + if (asprintf(optstr, "Zoom %s %s DSO (use the 'k' hotkey to zoom directly into the kernel)", browser->hists->dso_filter ? "out of" : "into", __map__is_kernel(map) ? "the Kernel" : map->dso->short_name) < 0) return 0; @@ -2936,6 +2938,7 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, "E Expand all callchains\n" \ "F Toggle percentage of filtered entries\n" \ "H Display column headers\n" \ + "k Zoom into the kernel map\n" \ "L Change percent limit\n" \ "m Display context menu\n" \ "S Zoom into current Processor Socket\n" \ @@ -3033,6 +3036,10 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, actions->ms.map = map; do_zoom_dso(browser, actions); continue; + case 'k': + if (browser->selection != NULL) + hists_browser__zoom_map(browser, browser->selection->maps->machine->vmlinux_map); + continue; case 'V': verbose = (verbose + 1) % 4; browser->show_dso = verbose > 0; -- cgit From d10ec006dcd7b20b2eb7e9ef32fc6f83b0112893 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 12 Dec 2019 15:31:40 -0300 Subject: perf hists browser: Allow passing an initial hotkey Sometimes we're in an outer code, like the main hists browser popup menu and the user follows a suggestion about using some hotkey, and that hotkey is really handled by hists_browser__run(), so allow for calling it with that hotkey, making it handle it instead of waiting for the user to press one. Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Linus Torvalds Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-xv2l7i6o4urn37nv1h40ryfs@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 4 +- tools/perf/ui/browsers/hists.c | 153 +++++++++++++++++++++-------------------- tools/perf/ui/browsers/hists.h | 2 +- 3 files changed, 82 insertions(+), 77 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index e69f44941aad..346351260c0b 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -2384,7 +2384,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he) c2c_browser__update_nr_entries(browser); while (1) { - key = hist_browser__run(browser, "? - help", true); + key = hist_browser__run(browser, "? - help", true, 0); switch (key) { case 's': @@ -2453,7 +2453,7 @@ static int perf_c2c__hists_browse(struct hists *hists) c2c_browser__update_nr_entries(browser); while (1) { - key = hist_browser__run(browser, "? - help", true); + key = hist_browser__run(browser, "? - help", true, 0); switch (key) { case 'q': diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 6dfdd8d5a743..ac118aef5ed1 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -672,10 +672,81 @@ static int hist_browser__title(struct hist_browser *browser, char *bf, size_t si return browser->title ? browser->title(browser, bf, size) : 0; } +static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_lost_event, char *title, int key) +{ + switch (key) { + case K_TIMER: { + struct hist_browser_timer *hbt = browser->hbt; + u64 nr_entries; + + WARN_ON_ONCE(!hbt); + + if (hbt) + hbt->timer(hbt->arg); + + if (hist_browser__has_filter(browser) || symbol_conf.report_hierarchy) + hist_browser__update_nr_entries(browser); + + nr_entries = hist_browser__nr_entries(browser); + ui_browser__update_nr_entries(&browser->b, nr_entries); + + if (warn_lost_event && + (browser->hists->stats.nr_lost_warned != + browser->hists->stats.nr_events[PERF_RECORD_LOST])) { + browser->hists->stats.nr_lost_warned = + browser->hists->stats.nr_events[PERF_RECORD_LOST]; + ui_browser__warn_lost_events(&browser->b); + } + + hist_browser__title(browser, title, sizeof(title)); + ui_browser__show_title(&browser->b, title); + break; + } + case 'D': { /* Debug */ + struct hist_entry *h = rb_entry(browser->b.top, struct hist_entry, rb_node); + static int seq; + + ui_helpline__pop(); + ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", + seq++, browser->b.nr_entries, browser->hists->nr_entries, + browser->b.extra_title_lines, browser->b.rows, + browser->b.index, browser->b.top_idx, h->row_offset, h->nr_rows); + } + break; + case 'C': + /* Collapse the whole world. */ + hist_browser__set_folding(browser, false); + break; + case 'c': + /* Collapse the selected entry. */ + hist_browser__set_folding_selected(browser, false); + break; + case 'E': + /* Expand the whole world. */ + hist_browser__set_folding(browser, true); + break; + case 'e': + /* Expand the selected entry. */ + hist_browser__set_folding_selected(browser, true); + break; + case 'H': + browser->show_headers = !browser->show_headers; + hist_browser__update_rows(browser); + break; + case '+': + if (hist_browser__toggle_fold(browser)) + break; + /* fall thru */ + default: + return -1; + } + + return 0; +} + int hist_browser__run(struct hist_browser *browser, const char *help, - bool warn_lost_event) + bool warn_lost_event, int key) { - int key; char title[160]; struct hist_browser_timer *hbt = browser->hbt; int delay_secs = hbt ? hbt->refresh : 0; @@ -688,79 +759,14 @@ int hist_browser__run(struct hist_browser *browser, const char *help, if (ui_browser__show(&browser->b, title, "%s", help) < 0) return -1; + if (key && hist_browser__handle_hotkey(browser, warn_lost_event, title, key)) + goto out; + while (1) { key = ui_browser__run(&browser->b, delay_secs); - switch (key) { - case K_TIMER: { - u64 nr_entries; - - WARN_ON_ONCE(!hbt); - - if (hbt) - hbt->timer(hbt->arg); - - if (hist_browser__has_filter(browser) || - symbol_conf.report_hierarchy) - hist_browser__update_nr_entries(browser); - - nr_entries = hist_browser__nr_entries(browser); - ui_browser__update_nr_entries(&browser->b, nr_entries); - - if (warn_lost_event && - (browser->hists->stats.nr_lost_warned != - browser->hists->stats.nr_events[PERF_RECORD_LOST])) { - browser->hists->stats.nr_lost_warned = - browser->hists->stats.nr_events[PERF_RECORD_LOST]; - ui_browser__warn_lost_events(&browser->b); - } - - hist_browser__title(browser, title, sizeof(title)); - ui_browser__show_title(&browser->b, title); - continue; - } - case 'D': { /* Debug */ - static int seq; - struct hist_entry *h = rb_entry(browser->b.top, - struct hist_entry, rb_node); - ui_helpline__pop(); - ui_helpline__fpush("%d: nr_ent=(%d,%d), etl: %d, rows=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", - seq++, browser->b.nr_entries, - browser->hists->nr_entries, - browser->b.extra_title_lines, - browser->b.rows, - browser->b.index, - browser->b.top_idx, - h->row_offset, h->nr_rows); - } - break; - case 'C': - /* Collapse the whole world. */ - hist_browser__set_folding(browser, false); - break; - case 'c': - /* Collapse the selected entry. */ - hist_browser__set_folding_selected(browser, false); - break; - case 'E': - /* Expand the whole world. */ - hist_browser__set_folding(browser, true); + if (hist_browser__handle_hotkey(browser, warn_lost_event, title, key)) break; - case 'e': - /* Expand the selected entry. */ - hist_browser__set_folding_selected(browser, true); - break; - case 'H': - browser->show_headers = !browser->show_headers; - hist_browser__update_rows(browser); - break; - case '+': - if (hist_browser__toggle_fold(browser)) - break; - /* fall thru */ - default: - goto out; - } } out: ui_browser__hide(&browser->b); @@ -2994,8 +3000,7 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, nr_options = 0; - key = hist_browser__run(browser, helpline, - warn_lost_event); + key = hist_browser__run(browser, helpline, warn_lost_event, 0); if (browser->he_selection != NULL) { thread = hist_browser__selected_thread(browser); @@ -3573,7 +3578,7 @@ int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, memset(&action, 0, sizeof(action)); while (1) { - key = hist_browser__run(browser, "? - help", true); + key = hist_browser__run(browser, "? - help", true, 0); switch (key) { case 'q': diff --git a/tools/perf/ui/browsers/hists.h b/tools/perf/ui/browsers/hists.h index 078f2f2c7abd..1e938d9ffa5e 100644 --- a/tools/perf/ui/browsers/hists.h +++ b/tools/perf/ui/browsers/hists.h @@ -34,7 +34,7 @@ struct hist_browser { struct hist_browser *hist_browser__new(struct hists *hists); void hist_browser__delete(struct hist_browser *browser); int hist_browser__run(struct hist_browser *browser, const char *help, - bool warn_lost_event); + bool warn_lost_event, int key); void hist_browser__init(struct hist_browser *browser, struct hists *hists); #endif /* _PERF_UI_BROWSER_HISTS_H_ */ -- cgit From d07126560cab572539621702137eeeeb2a4edf30 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 16 Dec 2019 12:23:34 -0300 Subject: tools ui popup: Allow returning hotkeys With this patch if an optional pointer is passed to ui__popup_menu() then when any key that is not being handled (ENTER, ESC, etc) is typed, it'll record that key in the pointer and return, allowing for hotkey processing on the caller. If NULL is passed, no change in logic, unhandled keys continue to be ignored. Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Linus Torvalds Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-6ojn19mqzgmrdm8kdoigic0m@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 4 ++-- tools/perf/ui/browsers/res_sample.c | 2 +- tools/perf/ui/browsers/scripts.c | 2 +- tools/perf/ui/tui/util.c | 12 ++++++++---- tools/perf/ui/util.h | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index ac118aef5ed1..c44b508f9e06 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2393,7 +2393,7 @@ close_file_and_continue: closedir(pwd_dir); if (nr_options) { - choice = ui__popup_menu(nr_options, options); + choice = ui__popup_menu(nr_options, options, NULL); if (choice < nr_options && choice >= 0) { tmp = strdup(abs_path[choice]); if (tmp) { @@ -3279,7 +3279,7 @@ skip_scripting: do { struct popup_action *act; - choice = ui__popup_menu(nr_options, options); + choice = ui__popup_menu(nr_options, options, NULL); if (choice == -1 || choice >= nr_options) break; diff --git a/tools/perf/ui/browsers/res_sample.c b/tools/perf/ui/browsers/res_sample.c index 76d356a18790..7cb2d6678039 100644 --- a/tools/perf/ui/browsers/res_sample.c +++ b/tools/perf/ui/browsers/res_sample.c @@ -56,7 +56,7 @@ int res_sample_browse(struct res_sample *res_samples, int num_res, return -1; } } - choice = ui__popup_menu(num_res, names); + choice = ui__popup_menu(num_res, names, NULL); for (i = 0; i < num_res; i++) zfree(&names[i]); free(names); diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c index fc733a6354d4..47d2c7a8cbe1 100644 --- a/tools/perf/ui/browsers/scripts.c +++ b/tools/perf/ui/browsers/scripts.c @@ -126,7 +126,7 @@ static int list_scripts(char *script_name, bool *custom, SCRIPT_FULLPATH_LEN); if (num < 0) num = 0; - choice = ui__popup_menu(num + max_std, (char * const *)names); + choice = ui__popup_menu(num + max_std, (char * const *)names, NULL); if (choice < 0) { ret = -1; goto out; diff --git a/tools/perf/ui/tui/util.c b/tools/perf/ui/tui/util.c index b98dd0e31dc1..0f562e2cb1e8 100644 --- a/tools/perf/ui/tui/util.c +++ b/tools/perf/ui/tui/util.c @@ -23,7 +23,7 @@ static void ui_browser__argv_write(struct ui_browser *browser, ui_browser__write_nstring(browser, *arg, browser->width); } -static int popup_menu__run(struct ui_browser *menu) +static int popup_menu__run(struct ui_browser *menu, int *keyp) { int key; @@ -45,6 +45,11 @@ static int popup_menu__run(struct ui_browser *menu) key = -1; break; default: + if (keyp) { + *keyp = key; + key = menu->nr_entries; + break; + } continue; } @@ -55,7 +60,7 @@ static int popup_menu__run(struct ui_browser *menu) return key; } -int ui__popup_menu(int argc, char * const argv[]) +int ui__popup_menu(int argc, char * const argv[], int *keyp) { struct ui_browser menu = { .entries = (void *)argv, @@ -64,8 +69,7 @@ int ui__popup_menu(int argc, char * const argv[]) .write = ui_browser__argv_write, .nr_entries = argc, }; - - return popup_menu__run(&menu); + return popup_menu__run(&menu, keyp); } int ui_browser__input_window(const char *title, const char *text, char *input, diff --git a/tools/perf/ui/util.h b/tools/perf/ui/util.h index 40891942f465..e30cea807564 100644 --- a/tools/perf/ui/util.h +++ b/tools/perf/ui/util.h @@ -5,7 +5,7 @@ #include int ui__getch(int delay_secs); -int ui__popup_menu(int argc, char * const argv[]); +int ui__popup_menu(int argc, char * const argv[], int *keyp); int ui__help_window(const char *text); int ui__dialog_yesno(const char *msg); void __ui__info_window(const char *title, const char *text, const char *exit_msg); -- cgit From 4c8b9c0f4281c8517542c26425aade3a31988575 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 16 Dec 2019 13:27:47 -0300 Subject: perf report/top: Allow pressing hotkeys in the options popup menu When the users presses ENTER in the main 'perf report/top' screen a popup menu is presented, in it some hotkeys are suggested as alternatives to using the menu, or for additional features. At that point the user may try those hotkeys, so allow for that by recording the key used and exiting, the caller then can check for that possibility and process the hotkey. I.e. try pressing ENTER, and then 'k' to exit and zoom into the kernel map, using ESC then zooms out, etc. Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Linus Torvalds Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-ujfq3fw44kf6qrtfajl5dcsp@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index c44b508f9e06..8776b1cb29b7 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2995,12 +2995,13 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, while (1) { struct thread *thread = NULL; struct map *map = NULL; - int choice = 0; + int choice; int socked_id = -1; - nr_options = 0; - - key = hist_browser__run(browser, helpline, warn_lost_event, 0); + key = 0; // reset key +do_hotkey: // key came straight from options ui__popup_menu() + choice = nr_options = 0; + key = hist_browser__run(browser, helpline, warn_lost_event, key); if (browser->he_selection != NULL) { thread = hist_browser__selected_thread(browser); @@ -3279,10 +3280,13 @@ skip_scripting: do { struct popup_action *act; - choice = ui__popup_menu(nr_options, options, NULL); - if (choice == -1 || choice >= nr_options) + choice = ui__popup_menu(nr_options, options, &key); + if (choice == -1) break; + if (choice == nr_options) + goto do_hotkey; + act = &actions[choice]; key = act->fn(browser, act); } while (key == 1); -- cgit From ea537f22f6e5b3e2026fc58419cc937d833b1a61 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 16 Dec 2019 18:21:16 -0300 Subject: perf report/top: Do not offer annotation for symbols without samples This can happen in the --children mode, i.e. the default mode when callchains are present, where one of the main entries may be a callchain entry with no samples. So far we were not providing any information about why an annotation couldn't be provided even offering the Annotation option in the popup menu. Work is needed to allow for no-samples "annotation', i.e. to show the disassembly anyway and allow for navigation, etc. Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Linus Torvalds Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-0hhzj2de15o88cguy7h66zre@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 8776b1cb29b7..3bec8de89880 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -2465,7 +2465,8 @@ add_annotate_opt(struct hist_browser *browser __maybe_unused, struct popup_action *act, char **optstr, struct map_symbol *ms) { - if (ms->sym == NULL || ms->map->dso->annotate_warned) + if (ms->sym == NULL || ms->map->dso->annotate_warned || + symbol__annotation(ms->sym)->src == NULL) return 0; if (asprintf(optstr, "Annotate %s", ms->sym->name) < 0) @@ -3031,6 +3032,14 @@ do_hotkey: // key came straight from options ui__popup_menu() browser->selection->map->dso->annotate_warned) continue; + if (symbol__annotation(browser->selection->sym)->src == NULL) { + ui_browser__warning(&browser->b, delay_secs * 2, + "No samples for the \"%s\" symbol.\n\n" + "Probably appeared just in a callchain", + browser->selection->sym->name); + continue; + } + actions->ms.map = browser->selection->map; actions->ms.sym = browser->selection->sym; do_annotate(browser, actions); -- cgit From e6d6abfc447a65e949c1e883e66e1450903d2fbd Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 17 Dec 2019 10:39:04 -0300 Subject: perf report/top: Make 'e' visible in the help and make it toggle showing callchains The 'e' and 'c' hotkeys were present for a long time, but not documented in the help window, change 'e' to be a toggle so that it gets consistent with other toggles like '+' and document it in the help window. Keep 'c' as is for people used to it but don't document, as it is easier to just use 'e' to show/hide all the callchains for a top level histogram entry. Reviewed-by: Jiri Olsa Cc: Adrian Hunter Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Linus Torvalds Cc: Namhyung Kim Link: https://lkml.kernel.org/n/tip-pmyi5x34stlqmyu81rci94x9@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/hists.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 3bec8de89880..f36dee499320 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -407,6 +407,11 @@ static bool hist_browser__selection_has_children(struct hist_browser *browser) return container_of(ms, struct callchain_list, ms)->has_children; } +static bool hist_browser__he_selection_unfolded(struct hist_browser *browser) +{ + return browser->he_selection ? browser->he_selection->unfolded : false; +} + static bool hist_browser__selection_unfolded(struct hist_browser *browser) { struct hist_entry *he = browser->he_selection; @@ -727,7 +732,7 @@ static int hist_browser__handle_hotkey(struct hist_browser *browser, bool warn_l break; case 'e': /* Expand the selected entry. */ - hist_browser__set_folding_selected(browser, true); + hist_browser__set_folding_selected(browser, !hist_browser__he_selection_unfolded(browser)); break; case 'H': browser->show_headers = !browser->show_headers; @@ -2942,6 +2947,7 @@ static int perf_evsel__hists_browse(struct evsel *evsel, int nr_events, "a Annotate current symbol\n" \ "C Collapse all callchains\n" \ "d Zoom into current DSO\n" \ + "e Expand/Collapse main entry callchains\n" \ "E Expand all callchains\n" \ "F Toggle percentage of filtered entries\n" \ "H Display column headers\n" \ -- cgit From 6c4798d3f08b81c2c52936b10e0fa872590c96ae Mon Sep 17 00:00:00 2001 From: Vitaly Chikunov Date: Tue, 24 Dec 2019 20:20:29 +0300 Subject: tools lib: Fix builds when glibc contains strlcpy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable a couple of compilation warnings (which are treated as errors) on strlcpy() definition and declaration, allowing users to compile perf and kernel (objtool) when: 1. glibc have strlcpy() (such as in ALT Linux since 2004) objtool and perf build fails with this (in gcc): In file included from exec-cmd.c:3: tools/include/linux/string.h:20:15: error: redundant redeclaration of ‘strlcpy’ [-Werror=redundant-decls] 20 | extern size_t strlcpy(char *dest, const char *src, size_t size); 2. clang ignores `-Wredundant-decls', but produces another warning when building perf: CC util/string.o ../lib/string.c:99:8: error: attribute declaration must precede definition [-Werror,-Wignored-attributes] size_t __weak strlcpy(char *dest, const char *src, size_t size) ../../tools/include/linux/compiler.h:66:34: note: expanded from macro '__weak' # define __weak __attribute__((weak)) /usr/include/bits/string_fortified.h:151:8: note: previous definition is here __NTH (strlcpy (char *__restrict __dest, const char *__restrict __src, Committer notes: The #pragma GCC diagnostic directive was introduced in gcc 4.6, so check for that as well. Fixes: ce99091 ("perf tools: Move strlcpy() from perf to tools/lib/string.c") Fixes: 0215d59 ("tools lib: Reinstate strlcpy() header guard with __UCLIBC__") Resolves: https://bugzilla.kernel.org/show_bug.cgi?id=118481 Signed-off-by: Vitaly Chikunov Reviewed-by: Dmitry Levin Cc: Dmitry Levin Cc: Josh Poimboeuf Cc: kbuild test robot Cc: Peter Zijlstra Cc: stable@vger.kernel.org Cc: Vineet Gupta Link: http://lore.kernel.org/lkml/20191224172029.19690-1-vt@altlinux.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/linux/string.h | 8 ++++++++ tools/lib/string.c | 7 +++++++ 2 files changed, 15 insertions(+) (limited to 'tools') diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h index 980cb9266718..5e9e781905ed 100644 --- a/tools/include/linux/string.h +++ b/tools/include/linux/string.h @@ -17,7 +17,15 @@ int strtobool(const char *s, bool *res); * However uClibc headers also define __GLIBC__ hence the hack below */ #if defined(__GLIBC__) && !defined(__UCLIBC__) +// pragma diagnostic was introduced in gcc 4.6 +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wredundant-decls" +#endif extern size_t strlcpy(char *dest, const char *src, size_t size); +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +#pragma GCC diagnostic pop +#endif #endif char *str_error_r(int errnum, char *buf, size_t buflen); diff --git a/tools/lib/string.c b/tools/lib/string.c index f2ae1b87c719..f645343815de 100644 --- a/tools/lib/string.c +++ b/tools/lib/string.c @@ -96,6 +96,10 @@ int strtobool(const char *s, bool *res) * If libc has strlcpy() then that version will override this * implementation: */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wignored-attributes" +#endif size_t __weak strlcpy(char *dest, const char *src, size_t size) { size_t ret = strlen(src); @@ -107,6 +111,9 @@ size_t __weak strlcpy(char *dest, const char *src, size_t size) } return ret; } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif /** * skip_spaces - Removes leading whitespace from @str. -- cgit From 411c0ec0b8131457cf52812de29f11dcbf491ce6 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 28 Dec 2019 18:13:14 +0100 Subject: perf clang: Fix build with Clang 9 LLVM D59377 (included in Clang 9) refactored Clang VFS construction a bit, which broke perf clang build. Let's fix it. Signed-off-by: Maciej S. Szmigiero Reviewed-by: Dennis Schridde Cc: Alexander Shishkin Cc: clang-built-linux@googlegroups.com Cc: Denis Pronin Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Naohiro Aota Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20191228171314.946469-2-mail@maciej.szmigiero.name Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/c++/clang.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tools') diff --git a/tools/perf/util/c++/clang.cpp b/tools/perf/util/c++/clang.cpp index fc361c3f8570..c8885dfa3667 100644 --- a/tools/perf/util/c++/clang.cpp +++ b/tools/perf/util/c++/clang.cpp @@ -71,7 +71,11 @@ getModuleFromSource(llvm::opt::ArgStringList CFlags, CompilerInstance Clang; Clang.createDiagnostics(); +#if CLANG_VERSION_MAJOR < 9 Clang.setVirtualFileSystem(&*VFS); +#else + Clang.createFileManager(&*VFS); +#endif #if CLANG_VERSION_MAJOR < 4 IntrusiveRefCntPtr CI = -- cgit From d8007772a5541b4711d1286b788ad4295b2c7eaa Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Sat, 28 Dec 2019 18:13:13 +0100 Subject: tools build: Fix test-clang.cpp with Clang 8+ LLVM rL344140 (included in Clang 8+) moved VFS from Clang to LLVM, so paths to its include files have changed. This broke the Clang test in tools/build - let's fix it. Signed-off-by: Maciej S. Szmigiero Cc: Alexander Shishkin Cc: Denis Pronin Cc: Dennis Schridde Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Naohiro Aota Cc: Peter Zijlstra Cc: clang-built-linux@googlegroups.com Link: http://lore.kernel.org/lkml/20191228171314.946469-1-mail@maciej.szmigiero.name Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/feature/test-clang.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tools') diff --git a/tools/build/feature/test-clang.cpp b/tools/build/feature/test-clang.cpp index a2b3f092d2f0..7d87075cd1c5 100644 --- a/tools/build/feature/test-clang.cpp +++ b/tools/build/feature/test-clang.cpp @@ -1,9 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 +#include "clang/Basic/Version.h" +#if CLANG_VERSION_MAJOR < 8 #include "clang/Basic/VirtualFileSystem.h" +#endif #include "clang/Driver/Driver.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/ManagedStatic.h" +#if CLANG_VERSION_MAJOR >= 8 +#include "llvm/Support/VirtualFileSystem.h" +#endif #include "llvm/Support/raw_ostream.h" using namespace clang; -- cgit From aa9d1f8334dfe220aff58c6bb4daf1fdae81add6 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 3 Jan 2020 10:36:43 -0800 Subject: perf report: Clarify in help that --children is default Refer to --no-children, which is what most people probably want. Signed-off-by: Andi Kleen Cc: Jiri Olsa LPU-Reference: 20200103183643.149150-1-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index de988589d99b..3048c1b95b4c 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -1164,7 +1164,8 @@ int cmd_report(int argc, const char **argv) report_callchain_help, &report_parse_callchain_opt, callchain_default_opt), OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain, - "Accumulate callchains of children and show total overhead as well"), + "Accumulate callchains of children and show total overhead as well. " + "Enabled by default, use --no-children to disable."), OPT_INTEGER(0, "max-stack", &report.max_stack, "Set the maximum stack depth when parsing the callchain, " "anything beyond the specified depth will be ignored. " -- cgit From 3b0b16bf8cb92ae67968c1abb7b335032b899b33 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 7 Jan 2020 13:04:44 -0800 Subject: perf tools: Support --prefix/--prefix-strip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The objdump utility has useful --prefix / --prefix-strip options to allow changing source code file names hardcoded into executables' debug info. Add options to 'perf report', 'perf top' and 'perf annotate', which are then passed to objdump. $ mkdir foo $ echo 'main() { for (;;); }' > foo/foo.c $ gcc -g foo/foo.c foo/foo.c:1:1: warning: return type defaults to ‘int’ [-Wimplicit-int] 1 | main() { for (;;); } | ^~~~ $ perf record ./a.out ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.230 MB perf.data (5721 samples) ] $ mv foo bar $ perf annotate $ perf annotate --prefix=/home/ak/lsrc/git/bar --prefix-strip=5 Signed-off-by: Andi Kleen Tested-by: Jiri Olsa LPU-Reference: 20200107210444.214071-1-andi@firstfloor.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-annotate.txt | 6 ++++++ tools/perf/Documentation/perf-report.txt | 6 ++++++ tools/perf/Documentation/perf-top.txt | 6 ++++++ tools/perf/builtin-annotate.c | 7 +++++++ tools/perf/builtin-report.c | 7 +++++++ tools/perf/builtin-top.c | 7 +++++++ tools/perf/util/annotate.c | 19 +++++++++++++++++-- tools/perf/util/annotate.h | 5 +++++ 8 files changed, 61 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index e8c972f89357..1b5042f134a8 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt @@ -112,6 +112,12 @@ OPTIONS --objdump=:: Path to objdump binary. +--prefix=PREFIX:: +--prefix-strip=N:: + Remove first N entries from source file path names in executables + and add PREFIX. This allows to display source code compiled on systems + with different file system layout. + --skip-missing:: Skip symbols that cannot be annotated. diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 8dbe2119686a..db61f16ffa56 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -367,6 +367,12 @@ OPTIONS --objdump=:: Path to objdump binary. +--prefix=PREFIX:: +--prefix-strip=N:: + Remove first N entries from source file path names in executables + and add PREFIX. This allows to display source code compiled on systems + with different file system layout. + --group:: Show event group information together. It forces group output also if there are no groups defined in data file. diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 5596129a71cf..324b6b53c86b 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -158,6 +158,12 @@ Default is to monitor all CPUS. -M:: --disassembler-style=:: Set disassembler style for objdump. +--prefix=PREFIX:: +--prefix-strip=N:: + Remove first N entries from source file path names in executables + and add PREFIX. This allows to display source code compiled on systems + with different file system layout. + --source:: Interleave source code with assembly code. Enabled by default, disable with --no-source. diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 5898662bc8fb..ff61795a4d13 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -535,6 +535,10 @@ int cmd_annotate(int argc, const char **argv) "Display raw encoding of assembly instructions (default)"), OPT_STRING('M', "disassembler-style", &annotate.opts.disassembler_style, "disassembler style", "Specify disassembler style (e.g. -M intel for intel syntax)"), + OPT_STRING(0, "prefix", &annotate.opts.prefix, "prefix", + "Add prefix to source file path names in programs (with --prefix-strip)"), + OPT_STRING(0, "prefix-strip", &annotate.opts.prefix_strip, "N", + "Strip first N entries of source file path name in programs (with --prefix)"), OPT_STRING(0, "objdump", &annotate.opts.objdump_path, "path", "objdump binary to use for disassembly and annotations"), OPT_BOOLEAN(0, "group", &symbol_conf.event_group, @@ -574,6 +578,9 @@ int cmd_annotate(int argc, const char **argv) annotate.sym_hist_filter = argv[0]; } + if (annotate_check_args(&annotate.opts) < 0) + return -EINVAL; + if (symbol_conf.show_nr_samples && annotate.use_gtk) { pr_err("--show-nr-samples is not available in --gtk mode at this time\n"); return ret; diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3048c1b95b4c..627bb6570988 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -1208,6 +1208,10 @@ int cmd_report(int argc, const char **argv) "Display raw encoding of assembly instructions (default)"), OPT_STRING('M', "disassembler-style", &report.annotation_opts.disassembler_style, "disassembler style", "Specify disassembler style (e.g. -M intel for intel syntax)"), + OPT_STRING(0, "prefix", &report.annotation_opts.prefix, "prefix", + "Add prefix to source file path names in programs (with --prefix-strip)"), + OPT_STRING(0, "prefix-strip", &report.annotation_opts.prefix_strip, "N", + "Strip first N entries of source file path name in programs (with --prefix)"), OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, "Show a column with the sum of periods"), OPT_BOOLEAN_SET(0, "group", &symbol_conf.event_group, &report.group_set, @@ -1287,6 +1291,9 @@ int cmd_report(int argc, const char **argv) report.symbol_filter_str = argv[0]; } + if (annotate_check_args(&report.annotation_opts) < 0) + return -EINVAL; + if (report.mmaps_mode) report.tasks_mode = true; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 795e353de095..8affcab75604 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1512,6 +1512,10 @@ int cmd_top(int argc, const char **argv) "objdump binary to use for disassembly and annotations"), OPT_STRING('M', "disassembler-style", &top.annotation_opts.disassembler_style, "disassembler style", "Specify disassembler style (e.g. -M intel for intel syntax)"), + OPT_STRING(0, "prefix", &top.annotation_opts.prefix, "prefix", + "Add prefix to source file path names in programs (with --prefix-strip)"), + OPT_STRING(0, "prefix-strip", &top.annotation_opts.prefix_strip, "N", + "Strip first N entries of source file path name in programs (with --prefix)"), OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), OPT_CALLBACK(0, "percent-limit", &top, "percent", "Don't show entries under that percent", parse_percent_limit), @@ -1582,6 +1586,9 @@ int cmd_top(int argc, const char **argv) if (argc) usage_with_options(top_usage, options); + if (annotate_check_args(&top.annotation_opts) < 0) + goto out_delete_evlist; + if (!top.evlist->core.nr_entries && perf_evlist__add_default(top.evlist) < 0) { pr_err("Not enough memory for event selector list\n"); diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index f5e77ed237e8..ca73fb74ad03 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1966,14 +1966,20 @@ static int symbol__disassemble(struct symbol *sym, struct annotate_args *args) err = asprintf(&command, "%s %s%s --start-address=0x%016" PRIx64 " --stop-address=0x%016" PRIx64 - " -l -d %s %s -C \"$1\"", + " -l -d %s %s %s %c%s%c %s%s -C \"$1\"", opts->objdump_path ?: "objdump", opts->disassembler_style ? "-M " : "", opts->disassembler_style ?: "", map__rip_2objdump(map, sym->start), map__rip_2objdump(map, sym->end), opts->show_asm_raw ? "" : "--no-show-raw-insn", - opts->annotate_src ? "-S" : ""); + opts->annotate_src ? "-S" : "", + opts->prefix ? "--prefix " : "", + opts->prefix ? '"' : ' ', + opts->prefix ?: "", + opts->prefix ? '"' : ' ', + opts->prefix_strip ? "--prefix-strip=" : "", + opts->prefix_strip ?: ""); if (err < 0) { pr_err("Failure allocating memory for the command to run\n"); @@ -3204,3 +3210,12 @@ out: free(str1); return err; } + +int annotate_check_args(struct annotation_options *args) +{ + if (args->prefix_strip && !args->prefix) { + pr_err("--prefix-strip requires --prefix\n"); + return -1; + } + return 0; +} diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 7075d98f69d9..455403e8fede 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -94,6 +94,8 @@ struct annotation_options { int context; const char *objdump_path; const char *disassembler_style; + const char *prefix; + const char *prefix_strip; unsigned int percent_type; }; @@ -415,4 +417,7 @@ void annotation_config__init(void); int annotate_parse_percent_type(const struct option *opt, const char *_str, int unset); + +int annotate_check_args(struct annotation_options *args); + #endif /* __PERF_ANNOTATE_H */ -- cgit From c3314a74f86dc00827e0945c8e5039fc3aebaa3c Mon Sep 17 00:00:00 2001 From: Jin Yao Date: Wed, 8 Jan 2020 03:17:45 +0800 Subject: perf report: Fix no libunwind compiled warning break s390 issue Commit 800d3f561659 ("perf report: Add warning when libunwind not compiled in") breaks the s390 platform. S390 uses libdw-dwarf-unwind for call chain unwinding and had no support for libunwind. So the warning "Please install libunwind development packages during the perf build." caused the confusion even if the call-graph is displayed correctly. This patch adds checking for HAVE_DWARF_SUPPORT, which is set when libdw-dwarf-unwind is compiled in. Fixes: 800d3f561659 ("perf report: Add warning when libunwind not compiled in") Signed-off-by: Jin Yao Reviewed-by: Thomas Richter Tested-by: Thomas Richter Acked-by: Jiri Olsa Cc: Alexander Shishkin Cc: Andi Kleen Cc: Jin Yao Cc: Kan Liang Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20200107191745.18415-1-yao.jin@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 627bb6570988..9483b3f0cae3 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -412,10 +412,10 @@ static int report__setup_sample_type(struct report *rep) PERF_SAMPLE_BRANCH_ANY)) rep->nonany_branch_mode = true; -#ifndef HAVE_LIBUNWIND_SUPPORT +#if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_DWARF_SUPPORT) if (dwarf_callchain_users) { - ui__warning("Please install libunwind development packages " - "during the perf build.\n"); + ui__warning("Please install libunwind or libdw " + "development packages during the perf build.\n"); } #endif -- cgit From cb71f7d43ece3d5a4f400f510c61b2ec7c9ce9a1 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Fri, 10 Jan 2020 16:15:37 +0100 Subject: libperf: Setup initial evlist::all_cpus value Jann Horn reported crash in perf ftrace because evlist::all_cpus isn't initialized if there's evlist without events, which is the case for perf ftrace. Adding initial initialization of evlist::all_cpus from given cpus, regardless of events in the evlist. Fixes: 7736627b865d ("perf stat: Use affinity for closing file descriptors") Reported-by: Jann Horn Signed-off-by: Jiri Olsa Acked-by: Andi Kleen Cc: Alexander Shishkin Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20200110151537.153012-1-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/evlist.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools') diff --git a/tools/lib/perf/evlist.c b/tools/lib/perf/evlist.c index ae9e65aa2491..5b9f2ca50591 100644 --- a/tools/lib/perf/evlist.c +++ b/tools/lib/perf/evlist.c @@ -164,6 +164,9 @@ void perf_evlist__set_maps(struct perf_evlist *evlist, evlist->threads = perf_thread_map__get(threads); } + if (!evlist->all_cpus && cpus) + evlist->all_cpus = perf_cpu_map__get(cpus); + perf_evlist__propagate_maps(evlist); } -- cgit From fc8c0a99223367b071c83711259d754b6bb7a379 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Sun, 12 Jan 2020 20:22:59 +0100 Subject: perf tools: Use %define api.pure full instead of %pure-parser bison deprecated the "%pure-parser" directive in favor of "%define api.pure full". The api.pure got introduced in bison 2.3 (Oct 2007), so it seems safe to use it without any version check. Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: Clark Williams Cc: Jiri Olsa Cc: Namhyung Kim Cc: Ravi Bangoria Cc: Thomas Gleixner Link: http://lore.kernel.org/lkml/20200112192259.GA35080@krava Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/expr.y | 3 ++- tools/perf/util/parse-events.y | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y index f9a20a39b64a..7d226241f1d7 100644 --- a/tools/perf/util/expr.y +++ b/tools/perf/util/expr.y @@ -12,7 +12,8 @@ #define MAXIDLEN 256 %} -%pure-parser +%define api.pure full + %parse-param { double *final_val } %parse-param { struct parse_ctx *ctx } %parse-param { const char **pp } diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index e2eea4e601b4..94f8bcd83582 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y @@ -1,4 +1,4 @@ -%pure-parser +%define api.pure full %parse-param {void *_parse_state} %parse-param {void *scanner} %lex-param {void* scanner} -- cgit From 604e2139a1026793b8c2172bd92c7e9d039a5cf0 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 13 Jan 2020 11:43:57 +0100 Subject: perf ui gtk: Add missing zalloc object When we moved zalloc.o to the library we missed gtk library which needs it compiled in, otherwise the missing __zfree symbol will cause the library to fail to load. Adding the zalloc object to the gtk library build. Fixes: 7f7c536f23e6 ("tools lib: Adopt zalloc()/zfree() from tools/perf") Signed-off-by: Jiri Olsa Cc: Alexander Shishkin Cc: Jelle van der Waa Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20200113104358.123511-1-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/gtk/Build | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tools') diff --git a/tools/perf/ui/gtk/Build b/tools/perf/ui/gtk/Build index ec22e899a224..9b5d5cbb7af7 100644 --- a/tools/perf/ui/gtk/Build +++ b/tools/perf/ui/gtk/Build @@ -7,3 +7,8 @@ gtk-y += util.o gtk-y += helpline.o gtk-y += progress.o gtk-y += annotate.o +gtk-y += zalloc.o + +$(OUTPUT)ui/gtk/zalloc.o: ../lib/zalloc.c FORCE + $(call rule_mkdir) + $(call if_changed_dep,cc_o_c) -- cgit From 93e843f95f095aeb533ab67ac4718f848d38dfa0 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 13 Jan 2020 11:43:58 +0100 Subject: perf/ui/gtk: Fix gtk2 build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ravi Bangoria reported an issue when doing the gtk2 feature detection on Fedora 31, where some types got deprecated: /usr/include/gtk-2.0/gtk/gtktypeutils.h:236:1: error: ‘GTypeDebugFlags’ is deprecated [-Werror=deprecated-declarations] 236 | void gtk_type_init (GTypeDebugFlags debug_flags); Fix this for perf by allowing the compile to pass with deprecated symbols via the -Wno-deprecated-declarations compiler directive. Reported-by: Ravi Bangoria Signed-off-by: Jiri Olsa Tested-by: Ravi Bangoria Cc: Alexander Shishkin Cc: Jelle van der Waa Cc: Michael Petlan Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20200113104358.123511-2-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/feature/Makefile | 2 +- tools/perf/ui/gtk/Build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tools') diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index f30a89046aa3..7ac0d8088565 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -197,7 +197,7 @@ $(OUTPUT)test-libcrypto.bin: $(BUILD) -lcrypto $(OUTPUT)test-gtk2.bin: - $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) + $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) -Wno-deprecated-declarations $(OUTPUT)test-gtk2-infobar.bin: $(BUILD) $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) diff --git a/tools/perf/ui/gtk/Build b/tools/perf/ui/gtk/Build index 9b5d5cbb7af7..eef708c502f4 100644 --- a/tools/perf/ui/gtk/Build +++ b/tools/perf/ui/gtk/Build @@ -1,4 +1,4 @@ -CFLAGS_gtk += -fPIC $(GTK_CFLAGS) +CFLAGS_gtk += -fPIC $(GTK_CFLAGS) -Wno-deprecated-declarations gtk-y += browser.o gtk-y += hists.o -- cgit From 49e0b6f4e95aa3ade8f512c50d1ccc113fe917b4 Mon Sep 17 00:00:00 2001 From: Cengiz Can Date: Mon, 13 Jan 2020 20:44:39 +0300 Subject: perf beauty sockaddr: Fix augmented syscall format warning The sockaddr related examples given in `tools/perf/examples/bpf/augmented_syscalls.c` almost always use `long`s to represent most of their fields. However, `size_t syscall_arg__scnprintf_sockaddr(..)` has a `scnprintf` call that uses `"%#x"` as format string. This throws a warning (whenever the syscall argument is `unsigned long`). Added `l` identifier to indicate that the `arg->value` is an unsigned long. Not sure about the complications of this with x86 though. Signed-off-by: Cengiz Can Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20200113174438.102975-1-cengiz@kernel.wtf Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/sockaddr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/trace/beauty/sockaddr.c b/tools/perf/trace/beauty/sockaddr.c index 173c8f760763..e0c13e6a5788 100644 --- a/tools/perf/trace/beauty/sockaddr.c +++ b/tools/perf/trace/beauty/sockaddr.c @@ -72,5 +72,5 @@ size_t syscall_arg__scnprintf_sockaddr(char *bf, size_t size, struct syscall_arg if (arg->augmented.args) return syscall_arg__scnprintf_augmented_sockaddr(arg, bf, size); - return scnprintf(bf, size, "%#x", arg->val); + return scnprintf(bf, size, "%#lx", arg->val); } -- cgit From c1c8013ec34d7163431d18367808ea40b2e305f8 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Wed, 8 Jan 2020 20:30:30 -0800 Subject: perf c2c: Fix return type for histogram sorting comparision functions Commit 722ddfde366f ("perf tools: Fix time sorting") changed - correctly so - hist_entry__sort to return int64. Unfortunately several of the builtin-c2c.c comparison routines only happened to work due the cast caused by the wrong return type. This causes meaningless ordering of both the cacheline list, and the cacheline details page. E.g a simple: perf c2c record -a sleep 3 perf c2c report will result in cacheline table like ================================================= Shared Data Cache Line Table ================================================= # # ------- Cacheline ---------- Total Tot - LLC Load Hitm - - Store Reference - - Load Dram - LLC Total - Core Load Hit - - LLC Load Hit - # Index Address Node PA cnt records Hitm Total Lcl Rmt Total L1Hit L1Miss Lcl Rmt Ld Miss Loads FB L1 L2 Llc Rmt # ..... .............. .... ...... ....... ...... ..... ..... ... .... ..... ...... ...... .... ...... ..... ..... ..... ... .... ....... 0 0x7f0d27ffba00 N/A 0 52 0.12% 13 6 7 12 12 0 0 7 14 40 4 16 0 0 0 1 0x7f0d27ff61c0 N/A 0 6353 14.04% 1475 801 674 779 779 0 0 718 1392 5574 1299 1967 0 115 0 2 0x7f0d26d3ec80 N/A 0 71 0.15% 16 4 12 13 13 0 0 12 24 58 1 20 0 9 0 3 0x7f0d26d3ec00 N/A 0 98 0.22% 23 17 6 19 19 0 0 6 12 79 0 40 0 10 0 i.e. with the list not being ordered by Total Hitm. Fixes: 722ddfde366f ("perf tools: Fix time sorting") Signed-off-by: Andres Freund Tested-by: Michael Petlan Acked-by: Jiri Olsa Cc: Alexander Shishkin Cc: Andi Kleen Cc: Namhyung Kim Cc: Peter Zijlstra Cc: stable@vger.kernel.org # v3.16+ Link: http://lore.kernel.org/lkml/20200109043030.233746-1-andres@anarazel.de Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'tools') diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 346351260c0b..246ac0b4d54f 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -595,8 +595,8 @@ tot_hitm_cmp(struct perf_hpp_fmt *fmt __maybe_unused, { struct c2c_hist_entry *c2c_left; struct c2c_hist_entry *c2c_right; - unsigned int tot_hitm_left; - unsigned int tot_hitm_right; + uint64_t tot_hitm_left; + uint64_t tot_hitm_right; c2c_left = container_of(left, struct c2c_hist_entry, he); c2c_right = container_of(right, struct c2c_hist_entry, he); @@ -629,7 +629,8 @@ __f ## _cmp(struct perf_hpp_fmt *fmt __maybe_unused, \ \ c2c_left = container_of(left, struct c2c_hist_entry, he); \ c2c_right = container_of(right, struct c2c_hist_entry, he); \ - return c2c_left->stats.__f - c2c_right->stats.__f; \ + return (uint64_t) c2c_left->stats.__f - \ + (uint64_t) c2c_right->stats.__f; \ } #define STAT_FN(__f) \ @@ -682,7 +683,8 @@ ld_llcmiss_cmp(struct perf_hpp_fmt *fmt __maybe_unused, c2c_left = container_of(left, struct c2c_hist_entry, he); c2c_right = container_of(right, struct c2c_hist_entry, he); - return llc_miss(&c2c_left->stats) - llc_miss(&c2c_right->stats); + return (uint64_t) llc_miss(&c2c_left->stats) - + (uint64_t) llc_miss(&c2c_right->stats); } static uint64_t total_records(struct c2c_stats *stats) -- cgit From 8af19d66b956401bab1ef24049eec9421be93862 Mon Sep 17 00:00:00 2001 From: Michael Petlan Date: Tue, 14 Jan 2020 11:42:36 +0100 Subject: perf header: Use last modification time for timestamp Using .st_ctime clobbers the timestamp information in perf report header whenever any operation is done with the file. Even tar-ing and untar-ing the perf.data file (which preserves the file last modification timestamp) doesn't prevent that: [Michael@Diego tmp]$ ls -l perf.data -> -rw-------. 1 Michael Michael 169888 Dec 2 15:23 perf.data [Michael@Diego tmp]$ perf report --header-only # ======== -> # captured on : Mon Dec 2 15:23:42 2019 [...] [Michael@Diego tmp]$ tar c perf.data | xz > perf.data.tar.xz [Michael@Diego tmp]$ mkdir aaa [Michael@Diego tmp]$ cd aaa [Michael@Diego aaa]$ xzcat ../perf.data.tar.xz | tar x [Michael@Diego aaa]$ ls -l -a total 172 drwxrwxr-x. 2 Michael Michael 23 Jan 14 11:26 . drwxrwxr-x. 6 Michael Michael 4096 Jan 14 11:26 .. -> -rw-------. 1 Michael Michael 169888 Dec 2 15:23 perf.data [Michael@Diego aaa]$ perf report --header-only # ======== -> # captured on : Tue Jan 14 11:26:16 2020 [...] When using .st_mtime instead, correct information is printed: [Michael@Diego aaa]$ ~/acme/tools/perf/perf report --header-only # ======== -> # captured on : Mon Dec 2 15:23:42 2019 [...] Signed-off-by: Michael Petlan Acked-by: Jiri Olsa LPU-Reference: 20200114104236.31555-1-mpetlan@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tools') diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 93ad27830e2b..4246e7447e54 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -2922,7 +2922,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) if (ret == -1) return -1; - stctime = st.st_ctime; + stctime = st.st_mtime; fprintf(fp, "# captured on : %s", ctime(&stctime)); fprintf(fp, "# header version : %u\n", header->version); -- cgit