Commit d64fe8e6 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo-3' of...

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

Pull new perf tool feature from Arnaldo Carvalho de Melo:

" User visible changes:

  - Generate perf.data files from 'perf stat', to tap into the scripting
    capabilities perf has instead of defining a 'perf stat' specific scripting
    support to calculate event ratios, etc. Simple example:

    $ perf stat record -e cycles usleep 1

     Performance counter stats for 'usleep 1':

           1,134,996      cycles

         0.000670644 seconds time elapsed

    $ perf stat report

     Performance counter stats for '/home/acme/bin/perf stat record -e cycles usleep 1':

           1,134,996      cycles

         0.000670644 seconds time elapsed

    $

    It generates PERF_RECORD_ userspace records to store the details:

    $ perf report -D | grep PERF_RECORD
    0xf0 [0x28]: PERF_RECORD_THREAD_MAP nr: 1 thread: 27637
    0x118 [0x12]: PERF_RECORD_CPU_MAP nr: 1 cpu: 65535
    0x12a [0x40]: PERF_RECORD_STAT_CONFIG
    0x16a [0x30]: PERF_RECORD_STAT
    -1 -1 0x19a [0x40]: PERF_RECORD_MMAP -1/0: [0xffffffff81000000(0x1f000000) @ 0xffffffff81000000]: x [kernel.kallsyms]_text
    0x1da [0x18]: PERF_RECORD_STAT_ROUND
    [acme@ssdandy linux]$

    An effort was made to make perf.data files generated like this to not
    generate cryptic messages when processed by older tools.

    The 'perf script' bits need rebasing, will go up later.

  Jiri's cover letter for this series:

  The initial attempt defined its own formula lang and allowed triggering user's
  script on the end of the stat command:

    http://marc.info/?l=linux-kernel&m=136742146322273&w=2

  This patchset abandons the idea of new formula language and rather adds support
  to:

    - store stat data into perf.data file
    - add python support to process stat events

  Basically it allows to store stat data into perf.data and post process it with
  python scripts in a similar way we do for sampling data.

  The stat data are stored in new stat, stat-round, stat-config user events.
    stat        - stored for each read syscall of the counter
    stat round  - stored for each interval or end of the command invocation
    stat config - stores all the config information needed to process data
                  so report tool could restore the same output as record

  The python script can now define 'stat__<eventname>_<modifier>' functions
  to get stat events data and 'stat__interval' to get stat-round data.

  See CPI script example in scripts/python/stat-cpi.py."

Also a few other changes:

User visible changes:

  - Make command line options always available, even when they
    depend on some feature being enabled, warning the user about
    use of such options (Wang Nan)

  - Support --vmlinux in perf record, useful, so far, for eBPF,
    where we will set up events that will be used in the record
    session (He Kuang)

  - Automatically disable collecting branch flags and cycles with
    --call-graph lbr. This allows avoiding a bunch of extra MSR
    reads in the PMI on Skylake.  (Andi Kleen)

Infrastructure changes:

  - Dump the stack when a 'perf test -v ' entry segfaults, so far we
    would have to run it under gdb with 'set follow-fork-mode child'
    set to get a proper backtrace (Arnaldo Carvalho de Melo)

  - Initialize the refcnt in 'struct thread' to 1 and fixup its
    users accordingly, so that we try to have the same refcount
    model accross the perf codebase (Arnaldo Carvalho de Melo)

  - More prep work for moving the subcmd infrastructure out of
    tools/perf/ and into tools/lib/subcmd/ to be used by other
    tools/ living utilities (Josh Poimboeuf)

  - Fix 'perf test' hist testcases when kptr_restrict is on (Namhyung Kim)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 141a361e 89af4e05
...@@ -10,6 +10,8 @@ SYNOPSIS ...@@ -10,6 +10,8 @@ SYNOPSIS
[verse] [verse]
'perf stat' [-e <EVENT> | --event=EVENT] [-a] <command> 'perf stat' [-e <EVENT> | --event=EVENT] [-a] <command>
'perf stat' [-e <EVENT> | --event=EVENT] [-a] -- <command> [<options>] 'perf stat' [-e <EVENT> | --event=EVENT] [-a] -- <command> [<options>]
'perf stat' [-e <EVENT> | --event=EVENT] [-a] record [-o file] -- <command> [<options>]
'perf stat' report [-i file]
DESCRIPTION DESCRIPTION
----------- -----------
...@@ -22,6 +24,11 @@ OPTIONS ...@@ -22,6 +24,11 @@ OPTIONS
<command>...:: <command>...::
Any command you can specify in a shell. Any command you can specify in a shell.
record::
See STAT RECORD.
report::
See STAT REPORT.
-e:: -e::
--event=:: --event=::
...@@ -159,6 +166,33 @@ filter out the startup phase of the program, which is often very different. ...@@ -159,6 +166,33 @@ filter out the startup phase of the program, which is often very different.
Print statistics of transactional execution if supported. Print statistics of transactional execution if supported.
STAT RECORD
-----------
Stores stat data into perf data file.
-o file::
--output file::
Output file name.
STAT REPORT
-----------
Reads and reports stat data from perf data file.
-i file::
--input file::
Input file name.
--per-socket::
Aggregate counts per processor socket for system-wide mode measurements.
--per-core::
Aggregate counts per physical processor for system-wide mode measurements.
-A::
--no-aggr::
Do not aggregate counts across all monitored CPUs.
EXAMPLES EXAMPLES
-------- --------
......
...@@ -452,6 +452,8 @@ static void record__init_features(struct record *rec) ...@@ -452,6 +452,8 @@ static void record__init_features(struct record *rec)
if (!rec->opts.full_auxtrace) if (!rec->opts.full_auxtrace)
perf_header__clear_feat(&session->header, HEADER_AUXTRACE); perf_header__clear_feat(&session->header, HEADER_AUXTRACE);
perf_header__clear_feat(&session->header, HEADER_STAT);
} }
static volatile int workload_exec_errno; static volatile int workload_exec_errno;
......
This diff is collapsed.
...@@ -34,6 +34,9 @@ perf-y += thread-map.o ...@@ -34,6 +34,9 @@ perf-y += thread-map.o
perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o llvm-src-prologue.o
perf-y += bpf.o perf-y += bpf.o
perf-y += topology.o perf-y += topology.o
perf-y += cpumap.o
perf-y += stat.o
perf-y += event_update.o
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
$(call rule_mkdir) $(call rule_mkdir)
......
...@@ -179,6 +179,30 @@ static struct test generic_tests[] = { ...@@ -179,6 +179,30 @@ static struct test generic_tests[] = {
.get_desc = test__bpf_subtest_get_desc, .get_desc = test__bpf_subtest_get_desc,
}, },
}, },
{
.desc = "Test thread map synthesize",
.func = test__thread_map_synthesize,
},
{
.desc = "Test cpu map synthesize",
.func = test__cpu_map_synthesize,
},
{
.desc = "Test stat config synthesize",
.func = test__synthesize_stat_config,
},
{
.desc = "Test stat synthesize",
.func = test__synthesize_stat,
},
{
.desc = "Test stat round synthesize",
.func = test__synthesize_stat_round,
},
{
.desc = "Test attr update synthesize",
.func = test__event_update,
},
{ {
.func = NULL, .func = NULL,
}, },
......
#include "tests.h"
#include "cpumap.h"
static int process_event_mask(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct cpu_map_event *map_event = &event->cpu_map;
struct cpu_map_mask *mask;
struct cpu_map_data *data;
struct cpu_map *map;
int i;
data = &map_event->data;
TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__MASK);
mask = (struct cpu_map_mask *)data->data;
TEST_ASSERT_VAL("wrong nr", mask->nr == 1);
for (i = 0; i < 20; i++) {
TEST_ASSERT_VAL("wrong cpu", test_bit(i, mask->mask));
}
map = cpu_map__new_data(data);
TEST_ASSERT_VAL("wrong nr", map->nr == 20);
for (i = 0; i < 20; i++) {
TEST_ASSERT_VAL("wrong cpu", map->map[i] == i);
}
cpu_map__put(map);
return 0;
}
static int process_event_cpus(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct cpu_map_event *map_event = &event->cpu_map;
struct cpu_map_entries *cpus;
struct cpu_map_data *data;
struct cpu_map *map;
data = &map_event->data;
TEST_ASSERT_VAL("wrong type", data->type == PERF_CPU_MAP__CPUS);
cpus = (struct cpu_map_entries *)data->data;
TEST_ASSERT_VAL("wrong nr", cpus->nr == 2);
TEST_ASSERT_VAL("wrong cpu", cpus->cpu[0] == 1);
TEST_ASSERT_VAL("wrong cpu", cpus->cpu[1] == 256);
map = cpu_map__new_data(data);
TEST_ASSERT_VAL("wrong nr", map->nr == 2);
TEST_ASSERT_VAL("wrong cpu", map->map[0] == 1);
TEST_ASSERT_VAL("wrong cpu", map->map[1] == 256);
TEST_ASSERT_VAL("wrong refcnt", atomic_read(&map->refcnt) == 1);
cpu_map__put(map);
return 0;
}
int test__cpu_map_synthesize(int subtest __maybe_unused)
{
struct cpu_map *cpus;
/* This one is better stores in mask. */
cpus = cpu_map__new("0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19");
TEST_ASSERT_VAL("failed to synthesize map",
!perf_event__synthesize_cpu_map(NULL, cpus, process_event_mask, NULL));
cpu_map__put(cpus);
/* This one is better stores in cpu values. */
cpus = cpu_map__new("1,256");
TEST_ASSERT_VAL("failed to synthesize map",
!perf_event__synthesize_cpu_map(NULL, cpus, process_event_cpus, NULL));
cpu_map__put(cpus);
return 0;
}
#include <linux/compiler.h>
#include "evlist.h"
#include "evsel.h"
#include "machine.h"
#include "tests.h"
#include "debug.h"
static int process_event_unit(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct event_update_event *ev = (struct event_update_event *) event;
TEST_ASSERT_VAL("wrong id", ev->id == 123);
TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__UNIT);
TEST_ASSERT_VAL("wrong unit", !strcmp(ev->data, "KRAVA"));
return 0;
}
static int process_event_scale(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct event_update_event *ev = (struct event_update_event *) event;
struct event_update_event_scale *ev_data;
ev_data = (struct event_update_event_scale *) ev->data;
TEST_ASSERT_VAL("wrong id", ev->id == 123);
TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__SCALE);
TEST_ASSERT_VAL("wrong scale", ev_data->scale = 0.123);
return 0;
}
struct event_name {
struct perf_tool tool;
const char *name;
};
static int process_event_name(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct event_name *tmp = container_of(tool, struct event_name, tool);
struct event_update_event *ev = (struct event_update_event*) event;
TEST_ASSERT_VAL("wrong id", ev->id == 123);
TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__NAME);
TEST_ASSERT_VAL("wrong name", !strcmp(ev->data, tmp->name));
return 0;
}
static int process_event_cpus(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct event_update_event *ev = (struct event_update_event*) event;
struct event_update_event_cpus *ev_data;
struct cpu_map *map;
ev_data = (struct event_update_event_cpus*) ev->data;
map = cpu_map__new_data(&ev_data->cpus);
TEST_ASSERT_VAL("wrong id", ev->id == 123);
TEST_ASSERT_VAL("wrong type", ev->type == PERF_EVENT_UPDATE__CPUS);
TEST_ASSERT_VAL("wrong cpus", map->nr == 3);
TEST_ASSERT_VAL("wrong cpus", map->map[0] == 1);
TEST_ASSERT_VAL("wrong cpus", map->map[1] == 2);
TEST_ASSERT_VAL("wrong cpus", map->map[2] == 3);
cpu_map__put(map);
return 0;
}
int test__event_update(int subtest __maybe_unused)
{
struct perf_evlist *evlist;
struct perf_evsel *evsel;
struct event_name tmp;
evlist = perf_evlist__new_default();
TEST_ASSERT_VAL("failed to get evlist", evlist);
evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("failed to allos ids",
!perf_evsel__alloc_id(evsel, 1, 1));
perf_evlist__id_add(evlist, evsel, 0, 0, 123);
evsel->unit = strdup("KRAVA");
TEST_ASSERT_VAL("failed to synthesize attr update unit",
!perf_event__synthesize_event_update_unit(NULL, evsel, process_event_unit));
evsel->scale = 0.123;
TEST_ASSERT_VAL("failed to synthesize attr update scale",
!perf_event__synthesize_event_update_scale(NULL, evsel, process_event_scale));
tmp.name = perf_evsel__name(evsel);
TEST_ASSERT_VAL("failed to synthesize attr update name",
!perf_event__synthesize_event_update_name(&tmp.tool, evsel, process_event_name));
evsel->own_cpus = cpu_map__new("1,2,3");
TEST_ASSERT_VAL("failed to synthesize attr update cpus",
!perf_event__synthesize_event_update_cpus(&tmp.tool, evsel, process_event_cpus));
cpu_map__put(evsel->own_cpus);
return 0;
}
#include <linux/compiler.h>
#include "event.h"
#include "tests.h"
#include "stat.h"
#include "counts.h"
#include "debug.h"
static bool has_term(struct stat_config_event *config,
u64 tag, u64 val)
{
unsigned i;
for (i = 0; i < config->nr; i++) {
if ((config->data[i].tag == tag) &&
(config->data[i].val == val))
return true;
}
return false;
}
static int process_stat_config_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct stat_config_event *config = &event->stat_config;
struct perf_stat_config stat_config;
#define HAS(term, val) \
has_term(config, PERF_STAT_CONFIG_TERM__##term, val)
TEST_ASSERT_VAL("wrong nr", config->nr == PERF_STAT_CONFIG_TERM__MAX);
TEST_ASSERT_VAL("wrong aggr_mode", HAS(AGGR_MODE, AGGR_CORE));
TEST_ASSERT_VAL("wrong scale", HAS(SCALE, 1));
TEST_ASSERT_VAL("wrong interval", HAS(INTERVAL, 1));
#undef HAS
perf_event__read_stat_config(&stat_config, config);
TEST_ASSERT_VAL("wrong aggr_mode", stat_config.aggr_mode == AGGR_CORE);
TEST_ASSERT_VAL("wrong scale", stat_config.scale == 1);
TEST_ASSERT_VAL("wrong interval", stat_config.interval == 1);
return 0;
}
int test__synthesize_stat_config(int subtest __maybe_unused)
{
struct perf_stat_config stat_config = {
.aggr_mode = AGGR_CORE,
.scale = 1,
.interval = 1,
};
TEST_ASSERT_VAL("failed to synthesize stat_config",
!perf_event__synthesize_stat_config(NULL, &stat_config, process_stat_config_event, NULL));
return 0;
}
static int process_stat_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct stat_event *st = &event->stat;
TEST_ASSERT_VAL("wrong cpu", st->cpu == 1);
TEST_ASSERT_VAL("wrong thread", st->thread == 2);
TEST_ASSERT_VAL("wrong id", st->id == 3);
TEST_ASSERT_VAL("wrong val", st->val == 100);
TEST_ASSERT_VAL("wrong run", st->ena == 200);
TEST_ASSERT_VAL("wrong ena", st->run == 300);
return 0;
}
int test__synthesize_stat(int subtest __maybe_unused)
{
struct perf_counts_values count;
count.val = 100;
count.ena = 200;
count.run = 300;
TEST_ASSERT_VAL("failed to synthesize stat_config",
!perf_event__synthesize_stat(NULL, 1, 2, 3, &count, process_stat_event, NULL));
return 0;
}
static int process_stat_round_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct stat_round_event *stat_round = &event->stat_round;
TEST_ASSERT_VAL("wrong time", stat_round->time == 0xdeadbeef);
TEST_ASSERT_VAL("wrong type", stat_round->type == PERF_STAT_ROUND_TYPE__INTERVAL);
return 0;
}
int test__synthesize_stat_round(int subtest __maybe_unused)
{
TEST_ASSERT_VAL("failed to synthesize stat_config",
!perf_event__synthesize_stat_round(NULL, 0xdeadbeef, PERF_STAT_ROUND_TYPE__INTERVAL,
process_stat_round_event, NULL));
return 0;
}
...@@ -79,6 +79,12 @@ int test__bpf(int subtest); ...@@ -79,6 +79,12 @@ int test__bpf(int subtest);
const char *test__bpf_subtest_get_desc(int subtest); const char *test__bpf_subtest_get_desc(int subtest);
int test__bpf_subtest_get_nr(void); int test__bpf_subtest_get_nr(void);
int test_session_topology(int subtest); int test_session_topology(int subtest);
int test__thread_map_synthesize(int subtest);
int test__cpu_map_synthesize(int subtest);
int test__synthesize_stat_config(int subtest);
int test__synthesize_stat(int subtest);
int test__synthesize_stat_round(int subtest);
int test__event_update(int subtest);
#if defined(__arm__) || defined(__aarch64__) #if defined(__arm__) || defined(__aarch64__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT #ifdef HAVE_DWARF_UNWIND_SUPPORT
......
...@@ -40,3 +40,46 @@ int test__thread_map(int subtest __maybe_unused) ...@@ -40,3 +40,46 @@ int test__thread_map(int subtest __maybe_unused)
thread_map__put(map); thread_map__put(map);
return 0; return 0;
} }
static int process_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct machine *machine __maybe_unused)
{
struct thread_map_event *map = &event->thread_map;
struct thread_map *threads;
TEST_ASSERT_VAL("wrong nr", map->nr == 1);
TEST_ASSERT_VAL("wrong pid", map->entries[0].pid == (u64) getpid());
TEST_ASSERT_VAL("wrong comm", !strcmp(map->entries[0].comm, "perf"));
threads = thread_map__new_event(&event->thread_map);
TEST_ASSERT_VAL("failed to alloc map", threads);
TEST_ASSERT_VAL("wrong nr", threads->nr == 1);
TEST_ASSERT_VAL("wrong pid",
thread_map__pid(threads, 0) == getpid());
TEST_ASSERT_VAL("wrong comm",
thread_map__comm(threads, 0) &&
!strcmp(thread_map__comm(threads, 0), "perf"));
TEST_ASSERT_VAL("wrong refcnt",
atomic_read(&threads->refcnt) == 1);
thread_map__put(threads);
return 0;
}
int test__thread_map_synthesize(int subtest __maybe_unused)
{
struct thread_map *threads;
/* test map on current pid */
threads = thread_map__new_by_pid(getpid());
TEST_ASSERT_VAL("failed to alloc map", threads);
thread_map__read_comms(threads);
TEST_ASSERT_VAL("failed to synthesize map",
!perf_event__synthesize_thread_map2(NULL, threads, process_event, NULL));
return 0;
}
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <linux/bitmap.h>
#include "asm/bug.h" #include "asm/bug.h"
static struct cpu_map *cpu_map__default_new(void) static struct cpu_map *cpu_map__default_new(void)
...@@ -179,6 +180,47 @@ struct cpu_map *cpu_map__new(const char *cpu_list) ...@@ -179,6 +180,47 @@ struct cpu_map *cpu_map__new(const char *cpu_list)
return cpus; return cpus;
} }
static struct cpu_map *cpu_map__from_entries(struct cpu_map_entries *cpus)
{
struct cpu_map *map;
map = cpu_map__empty_new(cpus->nr);
if (map) {
unsigned i;
for (i = 0; i < cpus->nr; i++)
map->map[i] = (int)cpus->cpu[i];
}
return map;
}
static struct cpu_map *cpu_map__from_mask(struct cpu_map_mask *mask)
{
struct cpu_map *map;
int nr, nbits = mask->nr * mask->long_size * BITS_PER_BYTE;
nr = bitmap_weight(mask->mask, nbits);
map = cpu_map__empty_new(nr);
if (map) {
int cpu, i = 0;
for_each_set_bit(cpu, mask->mask, nbits)
map->map[i++] = cpu;
}
return map;
}
struct cpu_map *cpu_map__new_data(struct cpu_map_data *data)
{
if (data->type == PERF_CPU_MAP__CPUS)
return cpu_map__from_entries((struct cpu_map_entries *)data->data);
else
return cpu_map__from_mask((struct cpu_map_mask *)data->data);
}
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp) size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp)
{ {
int i; int i;
......
...@@ -17,6 +17,7 @@ struct cpu_map { ...@@ -17,6 +17,7 @@ struct cpu_map {
struct cpu_map *cpu_map__new(const char *cpu_list); struct cpu_map *cpu_map__new(const char *cpu_list);
struct cpu_map *cpu_map__empty_new(int nr); struct cpu_map *cpu_map__empty_new(int nr);
struct cpu_map *cpu_map__dummy_new(void); struct cpu_map *cpu_map__dummy_new(void);
struct cpu_map *cpu_map__new_data(struct cpu_map_data *data);
struct cpu_map *cpu_map__read(FILE *file); struct cpu_map *cpu_map__read(FILE *file);
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
int cpu_map__get_socket_id(int cpu); int cpu_map__get_socket_id(int cpu);
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "thread.h" #include "thread.h"
#include "thread_map.h" #include "thread_map.h"
#include "symbol/kallsyms.h" #include "symbol/kallsyms.h"
#include "asm/bug.h"
#include "stat.h"
static const char *perf_event__names[] = { static const char *perf_event__names[] = {
[0] = "TOTAL", [0] = "TOTAL",
...@@ -37,6 +39,12 @@ static const char *perf_event__names[] = { ...@@ -37,6 +39,12 @@ static const char *perf_event__names[] = {
[PERF_RECORD_AUXTRACE_INFO] = "AUXTRACE_INFO", [PERF_RECORD_AUXTRACE_INFO] = "AUXTRACE_INFO",
[PERF_RECORD_AUXTRACE] = "AUXTRACE", [PERF_RECORD_AUXTRACE] = "AUXTRACE",
[PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR", [PERF_RECORD_AUXTRACE_ERROR] = "AUXTRACE_ERROR",
[PERF_RECORD_THREAD_MAP] = "THREAD_MAP",
[PERF_RECORD_CPU_MAP] = "CPU_MAP",
[PERF_RECORD_STAT_CONFIG] = "STAT_CONFIG",
[PERF_RECORD_STAT] = "STAT",
[PERF_RECORD_STAT_ROUND] = "STAT_ROUND",
[PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE",
}; };
const char *perf_event__name(unsigned int id) const char *perf_event__name(unsigned int id)
...@@ -699,6 +707,274 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, ...@@ -699,6 +707,274 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
return err; return err;
} }
int perf_event__synthesize_thread_map2(struct perf_tool *tool,
struct thread_map *threads,
perf_event__handler_t process,
struct machine *machine)
{
union perf_event *event;
int i, err, size;
size = sizeof(event->thread_map);
size += threads->nr * sizeof(event->thread_map.entries[0]);
event = zalloc(size);
if (!event)
return -ENOMEM;
event->header.type = PERF_RECORD_THREAD_MAP;
event->header.size = size;
event->thread_map.nr = threads->nr;
for (i = 0; i < threads->nr; i++) {
struct thread_map_event_entry *entry = &event->thread_map.entries[i];
char *comm = thread_map__comm(threads, i);
if (!comm)
comm = (char *) "";
entry->pid = thread_map__pid(threads, i);
strncpy((char *) &entry->comm, comm, sizeof(entry->comm));
}
err = process(tool, event, NULL, machine);
free(event);
return err;
}
static void synthesize_cpus(struct cpu_map_entries *cpus,
struct cpu_map *map)
{
int i;
cpus->nr = map->nr;
for (i = 0; i < map->nr; i++)
cpus->cpu[i] = map->map[i];
}
static void synthesize_mask(struct cpu_map_mask *mask,
struct cpu_map *map, int max)
{
int i;
mask->nr = BITS_TO_LONGS(max);
mask->long_size = sizeof(long);
for (i = 0; i < map->nr; i++)
set_bit(map->map[i], mask->mask);
}
static size_t cpus_size(struct cpu_map *map)
{
return sizeof(struct cpu_map_entries) + map->nr * sizeof(u16);
}
static size_t mask_size(struct cpu_map *map, int *max)
{
int i;
*max = 0;
for (i = 0; i < map->nr; i++) {
/* bit possition of the cpu is + 1 */
int bit = map->map[i] + 1;
if (bit > *max)
*max = bit;
}
return sizeof(struct cpu_map_mask) + BITS_TO_LONGS(*max) * sizeof(long);
}
void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max)
{
size_t size_cpus, size_mask;
bool is_dummy = cpu_map__empty(map);
/*
* Both array and mask data have variable size based
* on the number of cpus and their actual values.
* The size of the 'struct cpu_map_data' is:
*
* array = size of 'struct cpu_map_entries' +
* number of cpus * sizeof(u64)
*
* mask = size of 'struct cpu_map_mask' +
* maximum cpu bit converted to size of longs
*
* and finaly + the size of 'struct cpu_map_data'.
*/
size_cpus = cpus_size(map);
size_mask = mask_size(map, max);
if (is_dummy || (size_cpus < size_mask)) {
*size += size_cpus;
*type = PERF_CPU_MAP__CPUS;
} else {
*size += size_mask;
*type = PERF_CPU_MAP__MASK;
}
*size += sizeof(struct cpu_map_data);
return zalloc(*size);
}
void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map,
u16 type, int max)
{
data->type = type;
switch (type) {
case PERF_CPU_MAP__CPUS:
synthesize_cpus((struct cpu_map_entries *) data->data, map);
break;
case PERF_CPU_MAP__MASK:
synthesize_mask((struct cpu_map_mask *) data->data, map, max);
default:
break;
};
}
static struct cpu_map_event* cpu_map_event__new(struct cpu_map *map)
{
size_t size = sizeof(struct cpu_map_event);
struct cpu_map_event *event;
int max;
u16 type;
event = cpu_map_data__alloc(map, &size, &type, &max);
if (!event)
return NULL;
event->header.type = PERF_RECORD_CPU_MAP;
event->header.size = size;
event->data.type = type;
cpu_map_data__synthesize(&event->data, map, type, max);
return event;
}
int perf_event__synthesize_cpu_map(struct perf_tool *tool,
struct cpu_map *map,
perf_event__handler_t process,
struct machine *machine)
{
struct cpu_map_event *event;
int err;
event = cpu_map_event__new(map);
if (!event)
return -ENOMEM;
err = process(tool, (union perf_event *) event, NULL, machine);
free(event);
return err;
}
int perf_event__synthesize_stat_config(struct perf_tool *tool,
struct perf_stat_config *config,
perf_event__handler_t process,
struct machine *machine)
{
struct stat_config_event *event;
int size, i = 0, err;
size = sizeof(*event);
size += (PERF_STAT_CONFIG_TERM__MAX * sizeof(event->data[0]));
event = zalloc(size);
if (!event)
return -ENOMEM;
event->header.type = PERF_RECORD_STAT_CONFIG;
event->header.size = size;
event->nr = PERF_STAT_CONFIG_TERM__MAX;
#define ADD(__term, __val) \
event->data[i].tag = PERF_STAT_CONFIG_TERM__##__term; \
event->data[i].val = __val; \
i++;
ADD(AGGR_MODE, config->aggr_mode)
ADD(INTERVAL, config->interval)
ADD(SCALE, config->scale)
WARN_ONCE(i != PERF_STAT_CONFIG_TERM__MAX,
"stat config terms unbalanced\n");
#undef ADD
err = process(tool, (union perf_event *) event, NULL, machine);
free(event);
return err;
}
int perf_event__synthesize_stat(struct perf_tool *tool,
u32 cpu, u32 thread, u64 id,
struct perf_counts_values *count,
perf_event__handler_t process,
struct machine *machine)
{
struct stat_event event;
event.header.type = PERF_RECORD_STAT;
event.header.size = sizeof(event);
event.header.misc = 0;
event.id = id;
event.cpu = cpu;
event.thread = thread;
event.val = count->val;
event.ena = count->ena;
event.run = count->run;
return process(tool, (union perf_event *) &event, NULL, machine);
}
int perf_event__synthesize_stat_round(struct perf_tool *tool,
u64 evtime, u64 type,
perf_event__handler_t process,
struct machine *machine)
{
struct stat_round_event event;
event.header.type = PERF_RECORD_STAT_ROUND;
event.header.size = sizeof(event);
event.header.misc = 0;
event.time = evtime;
event.type = type;
return process(tool, (union perf_event *) &event, NULL, machine);
}
void perf_event__read_stat_config(struct perf_stat_config *config,
struct stat_config_event *event)
{
unsigned i;
for (i = 0; i < event->nr; i++) {
switch (event->data[i].tag) {
#define CASE(__term, __val) \
case PERF_STAT_CONFIG_TERM__##__term: \
config->__val = event->data[i].val; \
break;
CASE(AGGR_MODE, aggr_mode)
CASE(SCALE, scale)
CASE(INTERVAL, interval)
#undef CASE
default:
pr_warning("unknown stat config term %" PRIu64 "\n",
event->data[i].tag);
}
}
}
size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)
{ {
const char *s; const char *s;
...@@ -783,6 +1059,38 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) ...@@ -783,6 +1059,38 @@ size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp)
event->mmap2.filename); event->mmap2.filename);
} }
size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp)
{
struct thread_map *threads = thread_map__new_event(&event->thread_map);
size_t ret;
ret = fprintf(fp, " nr: ");
if (threads)
ret += thread_map__fprintf(threads, fp);
else
ret += fprintf(fp, "failed to get threads from event\n");
thread_map__put(threads);
return ret;
}
size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp)
{
struct cpu_map *cpus = cpu_map__new_data(&event->cpu_map.data);
size_t ret;
ret = fprintf(fp, " nr: ");
if (cpus)
ret += cpu_map__fprintf(cpus, fp);
else
ret += fprintf(fp, "failed to get cpumap from event\n");
cpu_map__put(cpus);
return ret;
}
int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, int perf_event__process_mmap(struct perf_tool *tool __maybe_unused,
union perf_event *event, union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
......
...@@ -226,6 +226,12 @@ enum perf_user_event_type { /* above any possible kernel type */ ...@@ -226,6 +226,12 @@ enum perf_user_event_type { /* above any possible kernel type */
PERF_RECORD_AUXTRACE_INFO = 70, PERF_RECORD_AUXTRACE_INFO = 70,
PERF_RECORD_AUXTRACE = 71, PERF_RECORD_AUXTRACE = 71,
PERF_RECORD_AUXTRACE_ERROR = 72, 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_HEADER_MAX PERF_RECORD_HEADER_MAX
}; };
...@@ -270,12 +276,61 @@ struct events_stats { ...@@ -270,12 +276,61 @@ struct events_stats {
u32 nr_proc_map_timeout; u32 nr_proc_map_timeout;
}; };
enum {
PERF_CPU_MAP__CPUS = 0,
PERF_CPU_MAP__MASK = 1,
};
struct cpu_map_entries {
u16 nr;
u16 cpu[];
};
struct cpu_map_mask {
u16 nr;
u16 long_size;
unsigned long mask[];
};
struct cpu_map_data {
u16 type;
char data[];
};
struct cpu_map_event {
struct perf_event_header header;
struct cpu_map_data data;
};
struct attr_event { struct attr_event {
struct perf_event_header header; struct perf_event_header header;
struct perf_event_attr attr; struct perf_event_attr attr;
u64 id[]; u64 id[];
}; };
enum {
PERF_EVENT_UPDATE__UNIT = 0,
PERF_EVENT_UPDATE__SCALE = 1,
PERF_EVENT_UPDATE__NAME = 2,
PERF_EVENT_UPDATE__CPUS = 3,
};
struct event_update_event_cpus {
struct cpu_map_data cpus;
};
struct event_update_event_scale {
double scale;
};
struct event_update_event {
struct perf_event_header header;
u64 type;
u64 id;
char data[];
};
#define MAX_EVENT_NAME 64 #define MAX_EVENT_NAME 64
struct perf_trace_event_type { struct perf_trace_event_type {
...@@ -356,6 +411,63 @@ struct context_switch_event { ...@@ -356,6 +411,63 @@ struct context_switch_event {
u32 next_prev_tid; u32 next_prev_tid;
}; };
struct thread_map_event_entry {
u64 pid;
char comm[16];
};
struct thread_map_event {
struct perf_event_header header;
u64 nr;
struct thread_map_event_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 stat_config_event_entry {
u64 tag;
u64 val;
};
struct stat_config_event {
struct perf_event_header header;
u64 nr;
struct stat_config_event_entry data[];
};
struct stat_event {
struct perf_event_header header;
u64 id;
u32 cpu;
u32 thread;
union {
struct {
u64 val;
u64 ena;
u64 run;
};
u64 values[3];
};
};
enum {
PERF_STAT_ROUND_TYPE__INTERVAL = 0,
PERF_STAT_ROUND_TYPE__FINAL = 1,
};
struct stat_round_event {
struct perf_event_header header;
u64 type;
u64 time;
};
union perf_event { union perf_event {
struct perf_event_header header; struct perf_event_header header;
struct mmap_event mmap; struct mmap_event mmap;
...@@ -368,6 +480,7 @@ union perf_event { ...@@ -368,6 +480,7 @@ union perf_event {
struct throttle_event throttle; struct throttle_event throttle;
struct sample_event sample; struct sample_event sample;
struct attr_event attr; struct attr_event attr;
struct event_update_event event_update;
struct event_type_event event_type; struct event_type_event event_type;
struct tracing_data_event tracing_data; struct tracing_data_event tracing_data;
struct build_id_event build_id; struct build_id_event build_id;
...@@ -378,12 +491,20 @@ union perf_event { ...@@ -378,12 +491,20 @@ union perf_event {
struct aux_event aux; struct aux_event aux;
struct itrace_start_event itrace_start; struct itrace_start_event itrace_start;
struct context_switch_event context_switch; struct context_switch_event context_switch;
struct thread_map_event thread_map;
struct cpu_map_event cpu_map;
struct stat_config_event stat_config;
struct stat_event stat;
struct stat_round_event stat_round;
}; };
void perf_event__print_totals(void); void perf_event__print_totals(void);
struct perf_tool; struct perf_tool;
struct thread_map; struct thread_map;
struct cpu_map;
struct perf_stat_config;
struct perf_counts_values;
typedef int (*perf_event__handler_t)(struct perf_tool *tool, typedef int (*perf_event__handler_t)(struct perf_tool *tool,
union perf_event *event, union perf_event *event,
...@@ -395,6 +516,14 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool, ...@@ -395,6 +516,14 @@ int perf_event__synthesize_thread_map(struct perf_tool *tool,
perf_event__handler_t process, perf_event__handler_t process,
struct machine *machine, bool mmap_data, struct machine *machine, bool mmap_data,
unsigned int proc_map_timeout); unsigned int proc_map_timeout);
int perf_event__synthesize_thread_map2(struct perf_tool *tool,
struct thread_map *threads,
perf_event__handler_t process,
struct machine *machine);
int perf_event__synthesize_cpu_map(struct perf_tool *tool,
struct cpu_map *cpus,
perf_event__handler_t process,
struct machine *machine);
int perf_event__synthesize_threads(struct perf_tool *tool, int perf_event__synthesize_threads(struct perf_tool *tool,
perf_event__handler_t process, perf_event__handler_t process,
struct machine *machine, bool mmap_data, struct machine *machine, bool mmap_data,
...@@ -402,7 +531,21 @@ int perf_event__synthesize_threads(struct perf_tool *tool, ...@@ -402,7 +531,21 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
perf_event__handler_t process, perf_event__handler_t process,
struct machine *machine); struct machine *machine);
int perf_event__synthesize_stat_config(struct perf_tool *tool,
struct perf_stat_config *config,
perf_event__handler_t process,
struct machine *machine);
void perf_event__read_stat_config(struct perf_stat_config *config,
struct stat_config_event *event);
int perf_event__synthesize_stat(struct perf_tool *tool,
u32 cpu, u32 thread, u64 id,
struct perf_counts_values *count,
perf_event__handler_t process,
struct machine *machine);
int perf_event__synthesize_stat_round(struct perf_tool *tool,
u64 time, u64 type,
perf_event__handler_t process,
struct machine *machine);
int perf_event__synthesize_modules(struct perf_tool *tool, int perf_event__synthesize_modules(struct perf_tool *tool,
perf_event__handler_t process, perf_event__handler_t process,
struct machine *machine); struct machine *machine);
...@@ -499,9 +642,14 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp); ...@@ -499,9 +642,14 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp); size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp); size_t perf_event__fprintf_itrace_start(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp); size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_thread_map(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp);
size_t perf_event__fprintf(union perf_event *event, FILE *fp); size_t perf_event__fprintf(union perf_event *event, FILE *fp);
u64 kallsyms__get_function_start(const char *kallsyms_filename, u64 kallsyms__get_function_start(const char *kallsyms_filename,
const char *symbol_name); const char *symbol_name);
void *cpu_map_data__alloc(struct cpu_map *map, size_t *size, u16 *type, int *max);
void cpu_map_data__synthesize(struct cpu_map_data *data, struct cpu_map *map,
u16 type, int max);
#endif /* __PERF_RECORD_H */ #endif /* __PERF_RECORD_H */
...@@ -534,9 +534,9 @@ void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, ...@@ -534,9 +534,9 @@ void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
evsel->id[evsel->ids++] = id; evsel->id[evsel->ids++] = id;
} }
static int perf_evlist__id_add_fd(struct perf_evlist *evlist, int perf_evlist__id_add_fd(struct perf_evlist *evlist,
struct perf_evsel *evsel, struct perf_evsel *evsel,
int cpu, int thread, int fd) int cpu, int thread, int fd)
{ {
u64 read_data[4] = { 0, }; u64 read_data[4] = { 0, };
int id_idx = 1; /* The first entry is the counter value */ int id_idx = 1; /* The first entry is the counter value */
......
...@@ -97,6 +97,9 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, ...@@ -97,6 +97,9 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist,
void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
int cpu, int thread, u64 id); 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);
int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist); int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);
......
...@@ -868,6 +868,13 @@ static int write_auxtrace(int fd, struct perf_header *h, ...@@ -868,6 +868,13 @@ static int write_auxtrace(int fd, struct perf_header *h,
return err; return err;
} }
static int write_stat(int fd __maybe_unused,
struct perf_header *h __maybe_unused,
struct perf_evlist *evlist __maybe_unused)
{
return 0;
}
static void print_hostname(struct perf_header *ph, int fd __maybe_unused, static void print_hostname(struct perf_header *ph, int fd __maybe_unused,
FILE *fp) FILE *fp)
{ {
...@@ -1159,6 +1166,12 @@ static void print_auxtrace(struct perf_header *ph __maybe_unused, ...@@ -1159,6 +1166,12 @@ static void print_auxtrace(struct perf_header *ph __maybe_unused,
fprintf(fp, "# contains AUX area data (e.g. instruction trace)\n"); fprintf(fp, "# contains AUX area data (e.g. instruction trace)\n");
} }
static void print_stat(struct perf_header *ph __maybe_unused,
int fd __maybe_unused, FILE *fp)
{
fprintf(fp, "# contains stat data\n");
}
static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused,
FILE *fp) FILE *fp)
{ {
...@@ -1948,6 +1961,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { ...@@ -1948,6 +1961,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings),
FEAT_OPP(HEADER_GROUP_DESC, group_desc), FEAT_OPP(HEADER_GROUP_DESC, group_desc),
FEAT_OPP(HEADER_AUXTRACE, auxtrace), FEAT_OPP(HEADER_AUXTRACE, auxtrace),
FEAT_OPA(HEADER_STAT, stat),
}; };
struct header_print_data { struct header_print_data {
...@@ -2686,6 +2700,152 @@ int perf_event__synthesize_attr(struct perf_tool *tool, ...@@ -2686,6 +2700,152 @@ int perf_event__synthesize_attr(struct perf_tool *tool,
return err; return err;
} }
static struct event_update_event *
event_update_event__new(size_t size, u64 type, u64 id)
{
struct event_update_event *ev;
size += sizeof(*ev);
size = PERF_ALIGN(size, sizeof(u64));
ev = zalloc(size);
if (ev) {
ev->header.type = PERF_RECORD_EVENT_UPDATE;
ev->header.size = (u16)size;
ev->type = type;
ev->id = id;
}
return ev;
}
int
perf_event__synthesize_event_update_unit(struct perf_tool *tool,
struct perf_evsel *evsel,
perf_event__handler_t process)
{
struct event_update_event *ev;
size_t size = strlen(evsel->unit);
int err;
ev = event_update_event__new(size + 1, PERF_EVENT_UPDATE__UNIT, evsel->id[0]);
if (ev == NULL)
return -ENOMEM;
strncpy(ev->data, evsel->unit, size);
err = process(tool, (union perf_event *)ev, NULL, NULL);
free(ev);
return err;
}
int
perf_event__synthesize_event_update_scale(struct perf_tool *tool,
struct perf_evsel *evsel,
perf_event__handler_t process)
{
struct event_update_event *ev;
struct event_update_event_scale *ev_data;
int err;
ev = event_update_event__new(sizeof(*ev_data), PERF_EVENT_UPDATE__SCALE, evsel->id[0]);
if (ev == NULL)
return -ENOMEM;
ev_data = (struct event_update_event_scale *) ev->data;
ev_data->scale = evsel->scale;
err = process(tool, (union perf_event*) ev, NULL, NULL);
free(ev);
return err;
}
int
perf_event__synthesize_event_update_name(struct perf_tool *tool,
struct perf_evsel *evsel,
perf_event__handler_t process)
{
struct event_update_event *ev;
size_t len = strlen(evsel->name);
int err;
ev = event_update_event__new(len + 1, PERF_EVENT_UPDATE__NAME, evsel->id[0]);
if (ev == NULL)
return -ENOMEM;
strncpy(ev->data, evsel->name, len);
err = process(tool, (union perf_event*) ev, NULL, NULL);
free(ev);
return err;
}
int
perf_event__synthesize_event_update_cpus(struct perf_tool *tool,
struct perf_evsel *evsel,
perf_event__handler_t process)
{
size_t size = sizeof(struct event_update_event);
struct event_update_event *ev;
int max, err;
u16 type;
if (!evsel->own_cpus)
return 0;
ev = cpu_map_data__alloc(evsel->own_cpus, &size, &type, &max);
if (!ev)
return -ENOMEM;
ev->header.type = PERF_RECORD_EVENT_UPDATE;
ev->header.size = (u16)size;
ev->type = PERF_EVENT_UPDATE__CPUS;
ev->id = evsel->id[0];
cpu_map_data__synthesize((struct cpu_map_data *) ev->data,
evsel->own_cpus,
type, max);
err = process(tool, (union perf_event*) ev, NULL, NULL);
free(ev);
return err;
}
size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp)
{
struct event_update_event *ev = &event->event_update;
struct event_update_event_scale *ev_scale;
struct event_update_event_cpus *ev_cpus;
struct cpu_map *map;
size_t ret;
ret = fprintf(fp, "\n... id: %" PRIu64 "\n", ev->id);
switch (ev->type) {
case PERF_EVENT_UPDATE__SCALE:
ev_scale = (struct event_update_event_scale *) ev->data;
ret += fprintf(fp, "... scale: %f\n", ev_scale->scale);
break;
case PERF_EVENT_UPDATE__UNIT:
ret += fprintf(fp, "... unit: %s\n", ev->data);
break;
case PERF_EVENT_UPDATE__NAME:
ret += fprintf(fp, "... name: %s\n", ev->data);
break;
case PERF_EVENT_UPDATE__CPUS:
ev_cpus = (struct event_update_event_cpus *) ev->data;
ret += fprintf(fp, "... ");
map = cpu_map__new_data(&ev_cpus->cpus);
if (map)
ret += cpu_map__fprintf(map, fp);
else
ret += fprintf(fp, "failed to get cpus\n");
break;
default:
ret += fprintf(fp, "... unknown type\n");
break;
}
return ret;
}
int perf_event__synthesize_attrs(struct perf_tool *tool, int perf_event__synthesize_attrs(struct perf_tool *tool,
struct perf_session *session, struct perf_session *session,
perf_event__handler_t process) perf_event__handler_t process)
...@@ -2745,6 +2905,51 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused, ...@@ -2745,6 +2905,51 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused,
return 0; return 0;
} }
int perf_event__process_event_update(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_evlist **pevlist)
{
struct event_update_event *ev = &event->event_update;
struct event_update_event_scale *ev_scale;
struct event_update_event_cpus *ev_cpus;
struct perf_evlist *evlist;
struct perf_evsel *evsel;
struct cpu_map *map;
if (!pevlist || *pevlist == NULL)
return -EINVAL;
evlist = *pevlist;
evsel = perf_evlist__id2evsel(evlist, ev->id);
if (evsel == NULL)
return -EINVAL;
switch (ev->type) {
case PERF_EVENT_UPDATE__UNIT:
evsel->unit = strdup(ev->data);
break;
case PERF_EVENT_UPDATE__NAME:
evsel->name = strdup(ev->data);
break;
case PERF_EVENT_UPDATE__SCALE:
ev_scale = (struct event_update_event_scale *) ev->data;
evsel->scale = ev_scale->scale;
case PERF_EVENT_UPDATE__CPUS:
ev_cpus = (struct event_update_event_cpus *) ev->data;
map = cpu_map__new_data(&ev_cpus->cpus);
if (map)
evsel->own_cpus = map;
else
pr_err("failed to get event_update cpus\n");
default:
break;
}
return 0;
}
int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
struct perf_evlist *evlist, struct perf_evlist *evlist,
perf_event__handler_t process) perf_event__handler_t process)
......
...@@ -31,6 +31,7 @@ enum { ...@@ -31,6 +31,7 @@ enum {
HEADER_PMU_MAPPINGS, HEADER_PMU_MAPPINGS,
HEADER_GROUP_DESC, HEADER_GROUP_DESC,
HEADER_AUXTRACE, HEADER_AUXTRACE,
HEADER_STAT,
HEADER_LAST_FEATURE, HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256, HEADER_FEAT_BITS = 256,
}; };
...@@ -105,8 +106,24 @@ int perf_event__synthesize_attr(struct perf_tool *tool, ...@@ -105,8 +106,24 @@ int perf_event__synthesize_attr(struct perf_tool *tool,
int perf_event__synthesize_attrs(struct perf_tool *tool, int perf_event__synthesize_attrs(struct perf_tool *tool,
struct perf_session *session, struct perf_session *session,
perf_event__handler_t process); perf_event__handler_t process);
int perf_event__synthesize_event_update_unit(struct perf_tool *tool,
struct perf_evsel *evsel,
perf_event__handler_t process);
int perf_event__synthesize_event_update_scale(struct perf_tool *tool,
struct perf_evsel *evsel,
perf_event__handler_t process);
int perf_event__synthesize_event_update_name(struct perf_tool *tool,
struct perf_evsel *evsel,
perf_event__handler_t process);
int perf_event__synthesize_event_update_cpus(struct perf_tool *tool,
struct perf_evsel *evsel,
perf_event__handler_t process);
int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, int perf_event__process_attr(struct perf_tool *tool, union perf_event *event,
struct perf_evlist **pevlist); struct perf_evlist **pevlist);
int perf_event__process_event_update(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_evlist **pevlist);
size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp);
int perf_event__synthesize_tracing_data(struct perf_tool *tool, int perf_event__synthesize_tracing_data(struct perf_tool *tool,
int fd, struct perf_evlist *evlist, int fd, struct perf_evlist *evlist,
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "asm/bug.h" #include "asm/bug.h"
#include "auxtrace.h" #include "auxtrace.h"
#include "thread-stack.h" #include "thread-stack.h"
#include "stat.h"
static int perf_session__deliver_event(struct perf_session *session, static int perf_session__deliver_event(struct perf_session *session,
union perf_event *event, union perf_event *event,
...@@ -36,6 +37,9 @@ static int perf_session__open(struct perf_session *session) ...@@ -36,6 +37,9 @@ static int perf_session__open(struct perf_session *session)
if (perf_data_file__is_pipe(file)) if (perf_data_file__is_pipe(file))
return 0; return 0;
if (perf_header__has_feat(&session->header, HEADER_STAT))
return 0;
if (!perf_evlist__valid_sample_type(session->evlist)) { if (!perf_evlist__valid_sample_type(session->evlist)) {
pr_err("non matching sample_type\n"); pr_err("non matching sample_type\n");
return -1; return -1;
...@@ -205,6 +209,18 @@ static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused, ...@@ -205,6 +209,18 @@ static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused,
return 0; return 0;
} }
static int process_event_synth_event_update_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_evlist **pevlist
__maybe_unused)
{
if (dump_trace)
perf_event__fprintf_event_update(event, stdout);
dump_printf(": unhandled!\n");
return 0;
}
static int process_event_sample_stub(struct perf_tool *tool __maybe_unused, static int process_event_sample_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused, union perf_event *event __maybe_unused,
struct perf_sample *sample __maybe_unused, struct perf_sample *sample __maybe_unused,
...@@ -296,6 +312,67 @@ int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused, ...@@ -296,6 +312,67 @@ int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused,
return 0; return 0;
} }
static
int process_event_thread_map_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_session *session __maybe_unused)
{
if (dump_trace)
perf_event__fprintf_thread_map(event, stdout);
dump_printf(": unhandled!\n");
return 0;
}
static
int process_event_cpu_map_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_session *session __maybe_unused)
{
if (dump_trace)
perf_event__fprintf_cpu_map(event, stdout);
dump_printf(": unhandled!\n");
return 0;
}
static
int process_event_stat_config_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_session *session __maybe_unused)
{
if (dump_trace)
perf_event__fprintf_stat_config(event, stdout);
dump_printf(": unhandled!\n");
return 0;
}
static int process_stat_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_session *perf_session
__maybe_unused)
{
if (dump_trace)
perf_event__fprintf_stat(event, stdout);
dump_printf(": unhandled!\n");
return 0;
}
static int process_stat_round_stub(struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_session *perf_session
__maybe_unused)
{
if (dump_trace)
perf_event__fprintf_stat_round(event, stdout);
dump_printf(": unhandled!\n");
return 0;
}
void perf_tool__fill_defaults(struct perf_tool *tool) void perf_tool__fill_defaults(struct perf_tool *tool)
{ {
if (tool->sample == NULL) if (tool->sample == NULL)
...@@ -328,6 +405,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) ...@@ -328,6 +405,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
tool->unthrottle = process_event_stub; tool->unthrottle = process_event_stub;
if (tool->attr == NULL) if (tool->attr == NULL)
tool->attr = process_event_synth_attr_stub; tool->attr = process_event_synth_attr_stub;
if (tool->event_update == NULL)
tool->event_update = process_event_synth_event_update_stub;
if (tool->tracing_data == NULL) if (tool->tracing_data == NULL)
tool->tracing_data = process_event_synth_tracing_data_stub; tool->tracing_data = process_event_synth_tracing_data_stub;
if (tool->build_id == NULL) if (tool->build_id == NULL)
...@@ -346,6 +425,16 @@ void perf_tool__fill_defaults(struct perf_tool *tool) ...@@ -346,6 +425,16 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
tool->auxtrace = process_event_auxtrace_stub; tool->auxtrace = process_event_auxtrace_stub;
if (tool->auxtrace_error == NULL) if (tool->auxtrace_error == NULL)
tool->auxtrace_error = process_event_auxtrace_error_stub; tool->auxtrace_error = process_event_auxtrace_error_stub;
if (tool->thread_map == NULL)
tool->thread_map = process_event_thread_map_stub;
if (tool->cpu_map == NULL)
tool->cpu_map = process_event_cpu_map_stub;
if (tool->stat_config == NULL)
tool->stat_config = process_event_stat_config_stub;
if (tool->stat == NULL)
tool->stat = process_stat_stub;
if (tool->stat_round == NULL)
tool->stat_round = process_stat_round_stub;
} }
static void swap_sample_id_all(union perf_event *event, void *data) static void swap_sample_id_all(union perf_event *event, void *data)
...@@ -569,6 +658,13 @@ static void perf_event__hdr_attr_swap(union perf_event *event, ...@@ -569,6 +658,13 @@ static void perf_event__hdr_attr_swap(union perf_event *event,
mem_bswap_64(event->attr.id, size); mem_bswap_64(event->attr.id, size);
} }
static void perf_event__event_update_swap(union perf_event *event,
bool sample_id_all __maybe_unused)
{
event->event_update.type = bswap_64(event->event_update.type);
event->event_update.id = bswap_64(event->event_update.id);
}
static void perf_event__event_type_swap(union perf_event *event, static void perf_event__event_type_swap(union perf_event *event,
bool sample_id_all __maybe_unused) bool sample_id_all __maybe_unused)
{ {
...@@ -616,6 +712,81 @@ static void perf_event__auxtrace_error_swap(union perf_event *event, ...@@ -616,6 +712,81 @@ static void perf_event__auxtrace_error_swap(union perf_event *event,
event->auxtrace_error.ip = bswap_64(event->auxtrace_error.ip); event->auxtrace_error.ip = bswap_64(event->auxtrace_error.ip);
} }
static void perf_event__thread_map_swap(union perf_event *event,
bool sample_id_all __maybe_unused)
{
unsigned i;
event->thread_map.nr = bswap_64(event->thread_map.nr);
for (i = 0; i < event->thread_map.nr; i++)
event->thread_map.entries[i].pid = bswap_64(event->thread_map.entries[i].pid);
}
static void perf_event__cpu_map_swap(union perf_event *event,
bool sample_id_all __maybe_unused)
{
struct cpu_map_data *data = &event->cpu_map.data;
struct cpu_map_entries *cpus;
struct cpu_map_mask *mask;
unsigned i;
data->type = bswap_64(data->type);
switch (data->type) {
case PERF_CPU_MAP__CPUS:
cpus = (struct cpu_map_entries *)data->data;
cpus->nr = bswap_16(cpus->nr);
for (i = 0; i < cpus->nr; i++)
cpus->cpu[i] = bswap_16(cpus->cpu[i]);
break;
case PERF_CPU_MAP__MASK:
mask = (struct cpu_map_mask *) data->data;
mask->nr = bswap_16(mask->nr);
mask->long_size = bswap_16(mask->long_size);
switch (mask->long_size) {
case 4: mem_bswap_32(&mask->mask, mask->nr); break;
case 8: mem_bswap_64(&mask->mask, mask->nr); break;
default:
pr_err("cpu_map swap: unsupported long size\n");
}
default:
break;
}
}
static void perf_event__stat_config_swap(union perf_event *event,
bool sample_id_all __maybe_unused)
{
u64 size;
size = event->stat_config.nr * sizeof(event->stat_config.data[0]);
size += 1; /* nr item itself */
mem_bswap_64(&event->stat_config.nr, size);
}
static void perf_event__stat_swap(union perf_event *event,
bool sample_id_all __maybe_unused)
{
event->stat.id = bswap_64(event->stat.id);
event->stat.thread = bswap_32(event->stat.thread);
event->stat.cpu = bswap_32(event->stat.cpu);
event->stat.val = bswap_64(event->stat.val);
event->stat.ena = bswap_64(event->stat.ena);
event->stat.run = bswap_64(event->stat.run);
}
static void perf_event__stat_round_swap(union perf_event *event,
bool sample_id_all __maybe_unused)
{
event->stat_round.type = bswap_64(event->stat_round.type);
event->stat_round.time = bswap_64(event->stat_round.time);
}
typedef void (*perf_event__swap_op)(union perf_event *event, typedef void (*perf_event__swap_op)(union perf_event *event,
bool sample_id_all); bool sample_id_all);
...@@ -643,6 +814,12 @@ static perf_event__swap_op perf_event__swap_ops[] = { ...@@ -643,6 +814,12 @@ static perf_event__swap_op perf_event__swap_ops[] = {
[PERF_RECORD_AUXTRACE_INFO] = perf_event__auxtrace_info_swap, [PERF_RECORD_AUXTRACE_INFO] = perf_event__auxtrace_info_swap,
[PERF_RECORD_AUXTRACE] = perf_event__auxtrace_swap, [PERF_RECORD_AUXTRACE] = perf_event__auxtrace_swap,
[PERF_RECORD_AUXTRACE_ERROR] = perf_event__auxtrace_error_swap, [PERF_RECORD_AUXTRACE_ERROR] = perf_event__auxtrace_error_swap,
[PERF_RECORD_THREAD_MAP] = perf_event__thread_map_swap,
[PERF_RECORD_CPU_MAP] = perf_event__cpu_map_swap,
[PERF_RECORD_STAT_CONFIG] = perf_event__stat_config_swap,
[PERF_RECORD_STAT] = perf_event__stat_swap,
[PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap,
[PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap,
[PERF_RECORD_HEADER_MAX] = NULL, [PERF_RECORD_HEADER_MAX] = NULL,
}; };
...@@ -1154,6 +1331,8 @@ static s64 perf_session__process_user_event(struct perf_session *session, ...@@ -1154,6 +1331,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
perf_session__set_comm_exec(session); perf_session__set_comm_exec(session);
} }
return err; return err;
case PERF_RECORD_EVENT_UPDATE:
return tool->event_update(tool, event, &session->evlist);
case PERF_RECORD_HEADER_EVENT_TYPE: case PERF_RECORD_HEADER_EVENT_TYPE:
/* /*
* Depreceated, but we need to handle it for sake * Depreceated, but we need to handle it for sake
...@@ -1179,6 +1358,16 @@ static s64 perf_session__process_user_event(struct perf_session *session, ...@@ -1179,6 +1358,16 @@ static s64 perf_session__process_user_event(struct perf_session *session,
case PERF_RECORD_AUXTRACE_ERROR: case PERF_RECORD_AUXTRACE_ERROR:
perf_session__auxtrace_error_inc(session, event); perf_session__auxtrace_error_inc(session, event);
return tool->auxtrace_error(tool, event, session); return tool->auxtrace_error(tool, event, session);
case PERF_RECORD_THREAD_MAP:
return tool->thread_map(tool, event, session);
case PERF_RECORD_CPU_MAP:
return tool->cpu_map(tool, event, session);
case PERF_RECORD_STAT_CONFIG:
return tool->stat_config(tool, event, session);
case PERF_RECORD_STAT:
return tool->stat(tool, event, session);
case PERF_RECORD_STAT_ROUND:
return tool->stat_round(tool, event, session);
default: default:
return -EINVAL; return -EINVAL;
} }
......
...@@ -341,3 +341,65 @@ int perf_stat_process_counter(struct perf_stat_config *config, ...@@ -341,3 +341,65 @@ int perf_stat_process_counter(struct perf_stat_config *config,
return 0; return 0;
} }
int perf_event__process_stat_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_session *session)
{
struct perf_counts_values count;
struct stat_event *st = &event->stat;
struct perf_evsel *counter;
count.val = st->val;
count.ena = st->ena;
count.run = st->run;
counter = perf_evlist__id2evsel(session->evlist, st->id);
if (!counter) {
pr_err("Failed to resolve counter for stat event.\n");
return -EINVAL;
}
*perf_counts(counter->counts, st->cpu, st->thread) = count;
counter->supported = true;
return 0;
}
size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp)
{
struct stat_event *st = (struct stat_event *) event;
size_t ret;
ret = fprintf(fp, "\n... id %" PRIu64 ", cpu %d, thread %d\n",
st->id, st->cpu, st->thread);
ret += fprintf(fp, "... value %" PRIu64 ", enabled %" PRIu64 ", running %" PRIu64 "\n",
st->val, st->ena, st->run);
return ret;
}
size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp)
{
struct stat_round_event *rd = (struct stat_round_event *)event;
size_t ret;
ret = fprintf(fp, "\n... time %" PRIu64 ", type %s\n", rd->time,
rd->type == PERF_STAT_ROUND_TYPE__FINAL ? "FINAL" : "INTERVAL");
return ret;
}
size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp)
{
struct perf_stat_config sc;
size_t ret;
perf_event__read_stat_config(&sc, &event->stat_config);
ret = fprintf(fp, "\n");
ret += fprintf(fp, "... aggr_mode %d\n", sc.aggr_mode);
ret += fprintf(fp, "... scale %d\n", sc.scale);
ret += fprintf(fp, "... interval %u\n", sc.interval);
return ret;
}
...@@ -90,4 +90,14 @@ void perf_evlist__reset_stats(struct perf_evlist *evlist); ...@@ -90,4 +90,14 @@ void perf_evlist__reset_stats(struct perf_evlist *evlist);
int perf_stat_process_counter(struct perf_stat_config *config, int perf_stat_process_counter(struct perf_stat_config *config,
struct perf_evsel *counter); struct perf_evsel *counter);
struct perf_tool;
union perf_event;
struct perf_session;
int perf_event__process_stat_event(struct perf_tool *tool,
union perf_event *event,
struct perf_session *session);
size_t perf_event__fprintf_stat(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_stat_round(union perf_event *event, FILE *fp);
size_t perf_event__fprintf_stat_config(union perf_event *event, FILE *fp);
#endif #endif
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "thread_map.h" #include "thread_map.h"
#include "util.h" #include "util.h"
#include "debug.h" #include "debug.h"
#include "event.h"
/* Skip "." and ".." directories */ /* Skip "." and ".." directories */
static int filter(const struct dirent *dir) static int filter(const struct dirent *dir)
...@@ -409,3 +410,29 @@ void thread_map__read_comms(struct thread_map *threads) ...@@ -409,3 +410,29 @@ void thread_map__read_comms(struct thread_map *threads)
for (i = 0; i < threads->nr; ++i) for (i = 0; i < threads->nr; ++i)
comm_init(threads, i); comm_init(threads, i);
} }
static void thread_map__copy_event(struct thread_map *threads,
struct thread_map_event *event)
{
unsigned i;
threads->nr = (int) event->nr;
for (i = 0; i < event->nr; i++) {
thread_map__set_pid(threads, i, (pid_t) event->entries[i].pid);
threads->map[i].comm = strndup(event->entries[i].comm, 16);
}
atomic_set(&threads->refcnt, 1);
}
struct thread_map *thread_map__new_event(struct thread_map_event *event)
{
struct thread_map *threads;
threads = thread_map__alloc(event->nr);
if (threads)
thread_map__copy_event(threads, event);
return threads;
}
...@@ -16,11 +16,14 @@ struct thread_map { ...@@ -16,11 +16,14 @@ struct thread_map {
struct thread_map_data map[]; struct thread_map_data map[];
}; };
struct thread_map_event;
struct thread_map *thread_map__new_dummy(void); struct thread_map *thread_map__new_dummy(void);
struct thread_map *thread_map__new_by_pid(pid_t pid); struct thread_map *thread_map__new_by_pid(pid_t pid);
struct thread_map *thread_map__new_by_tid(pid_t tid); struct thread_map *thread_map__new_by_tid(pid_t tid);
struct thread_map *thread_map__new_by_uid(uid_t uid); struct thread_map *thread_map__new_by_uid(uid_t uid);
struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid); struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid);
struct thread_map *thread_map__new_event(struct thread_map_event *event);
struct thread_map *thread_map__get(struct thread_map *map); struct thread_map *thread_map__get(struct thread_map *map);
void thread_map__put(struct thread_map *map); void thread_map__put(struct thread_map *map);
......
...@@ -50,12 +50,18 @@ struct perf_tool { ...@@ -50,12 +50,18 @@ struct perf_tool {
throttle, throttle,
unthrottle; unthrottle;
event_attr_op attr; event_attr_op attr;
event_attr_op event_update;
event_op2 tracing_data; event_op2 tracing_data;
event_oe finished_round; event_oe finished_round;
event_op2 build_id, event_op2 build_id,
id_index, id_index,
auxtrace_info, auxtrace_info,
auxtrace_error; auxtrace_error,
thread_map,
cpu_map,
stat_config,
stat,
stat_round;
event_op3 auxtrace; event_op3 auxtrace;
bool ordered_events; bool ordered_events;
bool ordering_requires_timestamps; bool ordering_requires_timestamps;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment