* [PATCH 00/24] perf tools: Still more preparation for Intel PT
@ 2014-08-15 19:08 Adrian Hunter
2014-08-15 19:08 ` [PATCH 01/24] perf tools: Add a test for tracking with sched_switch Adrian Hunter
` (23 more replies)
0 siblings, 24 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Hi
Here is the next selection of patches. That are based on top of
759e612bf96627b64fcafe4174b3f6f2dedf2c0d perf stat: Use strerror_r instead of strerror
They can also be found here:
http://git.infradead.org/users/ahunter/linux-perf.git/shortlog/refs/heads/for-acme
Adrian Hunter (23):
perf tools: Add a test for tracking with sched_switch
perf scripting: Add 'flush' callback to scripting API
perf tools: Rename machine__get_kernel_start_addr()
perf tools: Add machine__kernel_ip()
perf tools: Let a user specify a PMU event without any config terms
perf tools: Let default config be defined for a PMU
perf tools: Add perf_pmu__scan_file()
perf tools: Add id index
perf pmu: Let pmu's with no events show up on perf list
perf session: Add perf_session__deliver_synth_event()
perf tools: Add a thread stack for synthesizing call chains
perf tools: Add facility to export data in database-friendly way
perf tools: Extend Python script interface to export data in a database-friendly way
perf tools: Add Python script to export to postgresql
perf tools: Add branch type to db export
perf tools: Add branch_type and in_tx to Python export
perf tools: Enhance the thread stack to output call/return data
perf tools: Add call information to the database export API
perf tools: Add call information to Python export
perf tools: Defer export of comms that were not 'set'
perf tools: Add perf-with-kcore script
perf tools: Build programs to copy 32-bit compatibility VDSOs
perf tools: Add support for 32-bit compatibility VDSOs
Alexander Shishkin (1):
perf tools: Add feature checks to .gitignore
tools/perf/.gitignore | 2 +
tools/perf/Makefile.perf | 48 +-
tools/perf/builtin-inject.c | 1 +
tools/perf/builtin-script.c | 13 +
tools/perf/config/Makefile | 25 +-
tools/perf/config/Makefile.arch | 8 +
tools/perf/config/feature-checks/Makefile | 10 +-
tools/perf/config/feature-checks/test-compile.c | 4 +
tools/perf/perf-read-vdso.c | 34 +
tools/perf/perf-with-kcore.sh | 259 ++++++++
.../scripts/python/bin/export-to-postgresql-record | 8 +
.../scripts/python/bin/export-to-postgresql-report | 29 +
tools/perf/scripts/python/export-to-postgresql.py | 444 +++++++++++++
tools/perf/tests/builtin-test.c | 4 +
tools/perf/tests/pmu.c | 2 +-
tools/perf/tests/switch-tracking.c | 572 +++++++++++++++++
tools/perf/tests/tests.h | 1 +
tools/perf/util/comm.h | 1 +
tools/perf/util/db-export.c | 426 +++++++++++++
tools/perf/util/db-export.h | 107 ++++
tools/perf/util/dso.h | 1 +
tools/perf/util/event.c | 7 +-
tools/perf/util/event.h | 41 ++
tools/perf/util/evlist.c | 26 +-
tools/perf/util/evsel.h | 4 +
tools/perf/util/find-vdso-map.c | 30 +
tools/perf/util/machine.c | 31 +-
tools/perf/util/machine.h | 18 +
tools/perf/util/parse-events.c | 13 +-
tools/perf/util/parse-events.y | 10 +
tools/perf/util/pmu.c | 92 ++-
tools/perf/util/pmu.h | 13 +-
.../perf/util/scripting-engines/trace-event-perl.c | 6 +
.../util/scripting-engines/trace-event-python.c | 399 +++++++++++-
tools/perf/util/session.c | 136 ++++
tools/perf/util/session.h | 15 +
tools/perf/util/symbol.h | 1 +
tools/perf/util/thread-stack.c | 690 +++++++++++++++++++++
tools/perf/util/thread-stack.h | 79 +++
tools/perf/util/thread.c | 3 +
tools/perf/util/thread.h | 4 +
tools/perf/util/tool.h | 3 +-
tools/perf/util/trace-event-scripting.c | 7 +
tools/perf/util/trace-event.h | 1 +
tools/perf/util/vdso.c | 207 ++++++-
tools/perf/util/vdso.h | 4 +-
46 files changed, 3765 insertions(+), 74 deletions(-)
create mode 100644 tools/perf/config/feature-checks/test-compile.c
create mode 100644 tools/perf/perf-read-vdso.c
create mode 100644 tools/perf/perf-with-kcore.sh
create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-record
create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-report
create mode 100644 tools/perf/scripts/python/export-to-postgresql.py
create mode 100644 tools/perf/tests/switch-tracking.c
create mode 100644 tools/perf/util/db-export.c
create mode 100644 tools/perf/util/db-export.h
create mode 100644 tools/perf/util/find-vdso-map.c
create mode 100644 tools/perf/util/thread-stack.c
create mode 100644 tools/perf/util/thread-stack.h
Regards
Adrian
^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 01/24] perf tools: Add a test for tracking with sched_switch
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-20 19:48 ` Arnaldo Carvalho de Melo
` (2 more replies)
2014-08-15 19:08 ` [PATCH 02/24] perf scripting: Add 'flush' callback to scripting API Adrian Hunter
` (22 subsequent siblings)
23 siblings, 3 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Add a test that checks that sched_switch events and
tracking events can be recorded for a workload using the
evsel->system_wide and evsel->tracking flags (respectively)
with other events sometimes enabled or disabled.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/Makefile.perf | 1 +
tools/perf/tests/builtin-test.c | 4 +
tools/perf/tests/switch-tracking.c | 572 +++++++++++++++++++++++++++++++++++++
tools/perf/tests/tests.h | 1 +
4 files changed, 578 insertions(+)
create mode 100644 tools/perf/tests/switch-tracking.c
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 1ea31e2..95e832b 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -425,6 +425,7 @@ endif
endif
LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o
LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o
+LIB_OBJS += $(OUTPUT)tests/switch-tracking.o
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 9948136..6a4145e 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -154,6 +154,10 @@ static struct test {
.func = test__hists_cumulate,
},
{
+ .desc = "Test tracking with sched_switch",
+ .func = test__switch_tracking,
+ },
+ {
.func = NULL,
},
};
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c
new file mode 100644
index 0000000..50c82d5
--- /dev/null
+++ b/tools/perf/tests/switch-tracking.c
@@ -0,0 +1,572 @@
+#include <sys/time.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include "parse-events.h"
+#include "evlist.h"
+#include "evsel.h"
+#include "thread_map.h"
+#include "cpumap.h"
+#include "tests.h"
+
+static int spin_sleep(void)
+{
+ struct timeval start, now, diff, maxtime;
+ struct timespec ts;
+ int err, i;
+
+ maxtime.tv_sec = 0;
+ maxtime.tv_usec = 50000;
+
+ err = gettimeofday(&start, NULL);
+ if (err)
+ return err;
+
+ /* Spin for 50ms */
+ while (1) {
+ for (i = 0; i < 1000; i++)
+ barrier();
+
+ err = gettimeofday(&now, NULL);
+ if (err)
+ return err;
+
+ timersub(&now, &start, &diff);
+ if (timercmp(&diff, &maxtime, > /* For checkpatch */))
+ break;
+ }
+
+ ts.tv_nsec = 50 * 1000 * 1000;
+ ts.tv_sec = 0;
+
+ /* Sleep for 50ms */
+ err = nanosleep(&ts, NULL);
+ if (err == EINTR)
+ err = 0;
+
+ return err;
+}
+
+struct switch_tracking {
+ struct perf_evsel *switch_evsel;
+ struct perf_evsel *cycles_evsel;
+ pid_t *tids;
+ int nr_tids;
+ int comm_seen[4];
+ int cycles_before_comm_1;
+ int cycles_between_comm_2_and_comm_3;
+ int cycles_after_comm_4;
+};
+
+static int check_comm(struct switch_tracking *switch_tracking,
+ union perf_event *event, const char *comm, int nr)
+{
+ if (event->header.type == PERF_RECORD_COMM &&
+ (pid_t)event->comm.pid == getpid() &&
+ (pid_t)event->comm.tid == getpid() &&
+ strcmp(event->comm.comm, comm) == 0) {
+ if (switch_tracking->comm_seen[nr]) {
+ pr_debug("Duplicate comm event\n");
+ return -1;
+ }
+ switch_tracking->comm_seen[nr] = 1;
+ pr_debug3("comm event: %s nr: %d\n", event->comm.comm, nr);
+ return 1;
+ }
+ return 0;
+}
+
+static int check_cpu(struct switch_tracking *switch_tracking, int cpu)
+{
+ int i, nr = cpu + 1;
+
+ if (cpu < 0)
+ return -1;
+
+ if (!switch_tracking->tids) {
+ switch_tracking->tids = calloc(nr, sizeof(pid_t));
+ if (!switch_tracking->tids)
+ return -1;
+ for (i = 0; i < nr; i++)
+ switch_tracking->tids[i] = -1;
+ switch_tracking->nr_tids = nr;
+ return 0;
+ }
+
+ if (cpu >= switch_tracking->nr_tids) {
+ void *addr;
+
+ addr = realloc(switch_tracking->tids, nr * sizeof(pid_t));
+ if (!addr)
+ return -1;
+ switch_tracking->tids = addr;
+ for (i = switch_tracking->nr_tids; i < nr; i++)
+ switch_tracking->tids[i] = -1;
+ switch_tracking->nr_tids = nr;
+ return 0;
+ }
+
+ return 0;
+}
+
+static int process_sample_event(struct perf_evlist *evlist,
+ union perf_event *event,
+ struct switch_tracking *switch_tracking)
+{
+ struct perf_sample sample;
+ struct perf_evsel *evsel;
+ pid_t next_tid, prev_tid;
+ int cpu, err;
+
+ if (perf_evlist__parse_sample(evlist, event, &sample)) {
+ pr_debug("perf_evlist__parse_sample failed\n");
+ return -1;
+ }
+
+ evsel = perf_evlist__id2evsel(evlist, sample.id);
+ if (evsel == switch_tracking->switch_evsel) {
+ next_tid = perf_evsel__intval(evsel, &sample, "next_pid");
+ prev_tid = perf_evsel__intval(evsel, &sample, "prev_pid");
+ cpu = sample.cpu;
+ pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n",
+ cpu, prev_tid, next_tid);
+ err = check_cpu(switch_tracking, cpu);
+ if (err)
+ return err;
+ /*
+ * Check for no missing sched_switch events i.e. that the
+ * evsel->system_wide flag has worked.
+ */
+ if (switch_tracking->tids[cpu] != -1 &&
+ switch_tracking->tids[cpu] != prev_tid) {
+ pr_debug("Missing sched_switch events\n");
+ return -1;
+ }
+ switch_tracking->tids[cpu] = next_tid;
+ }
+
+ if (evsel == switch_tracking->cycles_evsel) {
+ pr_debug3("cycles event\n");
+ if (!switch_tracking->comm_seen[0])
+ switch_tracking->cycles_before_comm_1 = 1;
+ if (switch_tracking->comm_seen[1] &&
+ !switch_tracking->comm_seen[2])
+ switch_tracking->cycles_between_comm_2_and_comm_3 = 1;
+ if (switch_tracking->comm_seen[3])
+ switch_tracking->cycles_after_comm_4 = 1;
+ }
+
+ return 0;
+}
+
+static int process_event(struct perf_evlist *evlist, union perf_event *event,
+ struct switch_tracking *switch_tracking)
+{
+ if (event->header.type == PERF_RECORD_SAMPLE)
+ return process_sample_event(evlist, event, switch_tracking);
+
+ if (event->header.type == PERF_RECORD_COMM) {
+ int err, done = 0;
+
+ err = check_comm(switch_tracking, event, "Test COMM 1", 0);
+ if (err < 0)
+ return -1;
+ done += err;
+ err = check_comm(switch_tracking, event, "Test COMM 2", 1);
+ if (err < 0)
+ return -1;
+ done += err;
+ err = check_comm(switch_tracking, event, "Test COMM 3", 2);
+ if (err < 0)
+ return -1;
+ done += err;
+ err = check_comm(switch_tracking, event, "Test COMM 4", 3);
+ if (err < 0)
+ return -1;
+ done += err;
+ if (done != 1) {
+ pr_debug("Unexpected comm event\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+struct event_node {
+ struct list_head list;
+ union perf_event *event;
+ u64 event_time;
+};
+
+static int add_event(struct perf_evlist *evlist, struct list_head *events,
+ union perf_event *event)
+{
+ struct perf_sample sample;
+ struct event_node *node;
+
+ node = malloc(sizeof(struct event_node));
+ if (!node) {
+ pr_debug("malloc failed\n");
+ return -1;
+ }
+ node->event = event;
+ list_add(&node->list, events);
+
+ if (perf_evlist__parse_sample(evlist, event, &sample)) {
+ pr_debug("perf_evlist__parse_sample failed\n");
+ return -1;
+ }
+
+ if (!sample.time) {
+ pr_debug("event with no time\n");
+ return -1;
+ }
+
+ node->event_time = sample.time;
+
+ return 0;
+}
+
+static void free_event_nodes(struct list_head *events)
+{
+ struct event_node *node;
+
+ while (!list_empty(events)) {
+ node = list_entry(events->next, struct event_node, list);
+ list_del(&node->list);
+ free(node);
+ }
+}
+
+static int compar(const void *a, const void *b)
+{
+ const struct event_node *nodea = a;
+ const struct event_node *nodeb = b;
+ s64 cmp = nodea->event_time - nodeb->event_time;
+
+ return cmp;
+}
+
+static int process_events(struct perf_evlist *evlist,
+ struct switch_tracking *switch_tracking)
+{
+ union perf_event *event;
+ unsigned pos, cnt = 0;
+ LIST_HEAD(events);
+ struct event_node *events_array, *node;
+ int i, ret;
+
+ for (i = 0; i < evlist->nr_mmaps; i++) {
+ while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+ cnt += 1;
+ ret = add_event(evlist, &events, event);
+ perf_evlist__mmap_consume(evlist, i);
+ if (ret < 0)
+ goto out_free_nodes;
+ }
+ }
+
+ events_array = calloc(cnt, sizeof(struct event_node));
+ if (!events_array) {
+ pr_debug("calloc failed\n");
+ ret = -1;
+ goto out_free_nodes;
+ }
+
+ pos = 0;
+ list_for_each_entry(node, &events, list)
+ events_array[pos++] = *node;
+
+ qsort(events_array, cnt, sizeof(struct event_node), compar);
+
+ for (pos = 0; pos < cnt; pos++) {
+ ret = process_event(evlist, events_array[pos].event,
+ switch_tracking);
+ if (ret < 0)
+ goto out_free;
+ }
+
+ ret = 0;
+out_free:
+ pr_debug("%u events recorded\n", cnt);
+ free(events_array);
+out_free_nodes:
+ free_event_nodes(&events);
+ return ret;
+}
+
+/**
+ * test__switch_tracking - test using sched_switch and tracking events.
+ *
+ * This function implements a test that checks that sched_switch events and
+ * tracking events can be recorded for a workload (current process) using the
+ * evsel->system_wide and evsel->tracking flags (respectively) with other events
+ * sometimes enabled or disabled.
+ */
+int test__switch_tracking(void)
+{
+ const char *sched_switch = "sched:sched_switch";
+ struct switch_tracking switch_tracking = {0};
+ struct record_opts opts = {
+ .mmap_pages = UINT_MAX,
+ .user_freq = UINT_MAX,
+ .user_interval = ULLONG_MAX,
+ .freq = 4000,
+ .target = {
+ .uses_mmap = true,
+ },
+ };
+ struct thread_map *threads = NULL;
+ struct cpu_map *cpus = NULL;
+ struct perf_evlist *evlist = NULL;
+ struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel;
+ struct perf_evsel *switch_evsel, *tracking_evsel;
+ const char *comm;
+ int err = -1;
+
+ threads = thread_map__new(-1, getpid(), UINT_MAX);
+ if (!threads) {
+ pr_debug("thread_map__new failed!\n");
+ goto out_err;
+ }
+
+ cpus = cpu_map__new(NULL);
+ if (!cpus) {
+ pr_debug("cpu_map__new failed!\n");
+ goto out_err;
+ }
+
+ evlist = perf_evlist__new();
+ if (!evlist) {
+ pr_debug("perf_evlist__new failed!\n");
+ goto out_err;
+ }
+
+ perf_evlist__set_maps(evlist, cpus, threads);
+
+ /* First event */
+ err = parse_events(evlist, "cpu-clock:u");
+ if (err) {
+ pr_debug("Failed to parse event dummy:u\n");
+ goto out_err;
+ }
+
+ cpu_clocks_evsel = perf_evlist__last(evlist);
+
+ /* Second event */
+ err = parse_events(evlist, "cycles:u");
+ if (err) {
+ pr_debug("Failed to parse event cycles:u\n");
+ goto out_err;
+ }
+
+ cycles_evsel = perf_evlist__last(evlist);
+
+ /* Third event */
+ if (!perf_evlist__can_select_event(evlist, sched_switch)) {
+ fprintf(stderr, " (no sched_switch)");
+ err = 0;
+ goto out;
+ }
+
+ err = parse_events(evlist, sched_switch);
+ if (err) {
+ pr_debug("Failed to parse event %s\n", sched_switch);
+ goto out_err;
+ }
+
+ switch_evsel = perf_evlist__last(evlist);
+
+ perf_evsel__set_sample_bit(switch_evsel, CPU);
+ perf_evsel__set_sample_bit(switch_evsel, TIME);
+
+ switch_evsel->system_wide = true;
+ switch_evsel->no_aux_samples = true;
+ switch_evsel->immediate = true;
+
+ /* Test moving an event to the front */
+ if (cycles_evsel == perf_evlist__first(evlist)) {
+ pr_debug("cycles event already at front");
+ goto out_err;
+ }
+ perf_evlist__to_front(evlist, cycles_evsel);
+ if (cycles_evsel != perf_evlist__first(evlist)) {
+ pr_debug("Failed to move cycles event to front");
+ goto out_err;
+ }
+
+ perf_evsel__set_sample_bit(cycles_evsel, CPU);
+ perf_evsel__set_sample_bit(cycles_evsel, TIME);
+
+ /* Fourth event */
+ err = parse_events(evlist, "dummy:u");
+ if (err) {
+ pr_debug("Failed to parse event dummy:u\n");
+ goto out_err;
+ }
+
+ tracking_evsel = perf_evlist__last(evlist);
+
+ perf_evlist__set_tracking_event(evlist, tracking_evsel);
+
+ tracking_evsel->attr.freq = 0;
+ tracking_evsel->attr.sample_period = 1;
+
+ perf_evsel__set_sample_bit(tracking_evsel, TIME);
+
+ /* Config events */
+ perf_evlist__config(evlist, &opts);
+
+ /* Check moved event is still at the front */
+ if (cycles_evsel != perf_evlist__first(evlist)) {
+ pr_debug("Front event no longer at front");
+ goto out_err;
+ }
+
+ /* Check tracking event is tracking */
+ if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) {
+ pr_debug("Tracking event not tracking\n");
+ goto out_err;
+ }
+
+ /* Check non-tracking events are not tracking */
+ evlist__for_each(evlist, evsel) {
+ if (evsel != tracking_evsel) {
+ if (evsel->attr.mmap || evsel->attr.comm) {
+ pr_debug("Non-tracking event is tracking\n");
+ goto out_err;
+ }
+ }
+ }
+
+ if (perf_evlist__open(evlist) < 0) {
+ fprintf(stderr, " (not supported)");
+ err = 0;
+ goto out;
+ }
+
+ err = perf_evlist__mmap(evlist, UINT_MAX, false);
+ if (err) {
+ pr_debug("perf_evlist__mmap failed!\n");
+ goto out_err;
+ }
+
+ perf_evlist__enable(evlist);
+
+ err = perf_evlist__disable_event(evlist, cpu_clocks_evsel);
+ if (err) {
+ pr_debug("perf_evlist__disable_event failed!\n");
+ goto out_err;
+ }
+
+ err = spin_sleep();
+ if (err) {
+ pr_debug("spin_sleep failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 1";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = perf_evlist__disable_event(evlist, cycles_evsel);
+ if (err) {
+ pr_debug("perf_evlist__disable_event failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 2";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = spin_sleep();
+ if (err) {
+ pr_debug("spin_sleep failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 3";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = perf_evlist__enable_event(evlist, cycles_evsel);
+ if (err) {
+ pr_debug("perf_evlist__disable_event failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 4";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = spin_sleep();
+ if (err) {
+ pr_debug("spin_sleep failed!\n");
+ goto out_err;
+ }
+
+ perf_evlist__disable(evlist);
+
+ switch_tracking.switch_evsel = switch_evsel;
+ switch_tracking.cycles_evsel = cycles_evsel;
+
+ err = process_events(evlist, &switch_tracking);
+
+ zfree(&switch_tracking.tids);
+
+ if (err)
+ goto out_err;
+
+ /* Check all 4 comm events were seen i.e. that evsel->tracking works */
+ if (!switch_tracking.comm_seen[0] || !switch_tracking.comm_seen[1] ||
+ !switch_tracking.comm_seen[2] || !switch_tracking.comm_seen[3]) {
+ pr_debug("Missing comm events\n");
+ goto out_err;
+ }
+
+ /* Check cycles event got enabled */
+ if (!switch_tracking.cycles_before_comm_1) {
+ pr_debug("Missing cycles events\n");
+ goto out_err;
+ }
+
+ /* Check cycles event got disabled */
+ if (switch_tracking.cycles_between_comm_2_and_comm_3) {
+ pr_debug("cycles events even though event was disabled\n");
+ goto out_err;
+ }
+
+ /* Check cycles event got enabled again */
+ if (!switch_tracking.cycles_after_comm_4) {
+ pr_debug("Missing cycles events\n");
+ goto out_err;
+ }
+out:
+ if (evlist) {
+ perf_evlist__disable(evlist);
+ perf_evlist__delete(evlist);
+ } else {
+ cpu_map__delete(cpus);
+ thread_map__delete(threads);
+ }
+
+ return err;
+
+out_err:
+ err = -1;
+ goto out;
+}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index ed64790..be8be10 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -48,6 +48,7 @@ int test__mmap_thread_lookup(void);
int test__thread_mg_share(void);
int test__hists_output(void);
int test__hists_cumulate(void);
+int test__switch_tracking(void);
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 02/24] perf scripting: Add 'flush' callback to scripting API
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
2014-08-15 19:08 ` [PATCH 01/24] perf tools: Add a test for tracking with sched_switch Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-24 14:58 ` [tip:perf/core] " tip-bot for Adrian Hunter
2014-08-15 19:08 ` [PATCH 03/24] perf tools: Rename machine__get_kernel_start_addr() Adrian Hunter
` (21 subsequent siblings)
23 siblings, 1 reply; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
In order to defer some output via the scripting API, there needs to be a
callback after session processing but before the session is deleted.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1406786474-9306-28-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/builtin-script.c | 13 +++++++++++++
tools/perf/util/scripting-engines/trace-event-perl.c | 6 ++++++
tools/perf/util/scripting-engines/trace-event-python.c | 6 ++++++
tools/perf/util/trace-event-scripting.c | 7 +++++++
tools/perf/util/trace-event.h | 1 +
5 files changed, 33 insertions(+)
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index c1b7029..02dce92 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -485,6 +485,11 @@ static int default_start_script(const char *script __maybe_unused,
return 0;
}
+static int default_flush_script(void)
+{
+ return 0;
+}
+
static int default_stop_script(void)
{
return 0;
@@ -498,6 +503,7 @@ static int default_generate_script(struct pevent *pevent __maybe_unused,
static struct scripting_ops default_scripting_ops = {
.start_script = default_start_script,
+ .flush_script = default_flush_script,
.stop_script = default_stop_script,
.process_event = process_event,
.generate_script = default_generate_script,
@@ -513,6 +519,11 @@ static void setup_scripting(void)
scripting_ops = &default_scripting_ops;
}
+static int flush_scripting(void)
+{
+ return scripting_ops->flush_script();
+}
+
static int cleanup_scripting(void)
{
pr_debug("\nperf script stopped\n");
@@ -1813,6 +1824,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
err = __cmd_script(&script);
+ flush_scripting();
+
out_delete:
perf_session__delete(session);
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index b2dba9c..0a01bac 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -432,6 +432,11 @@ error:
return err;
}
+static int perl_flush_script(void)
+{
+ return 0;
+}
+
/*
* Stop trace script
*/
@@ -633,6 +638,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
struct scripting_ops perl_scripting_ops = {
.name = "Perl",
.start_script = perl_start_script,
+ .flush_script = perl_flush_script,
.stop_script = perl_stop_script,
.process_event = perl_process_event,
.generate_script = perl_generate_script,
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 26e5f14..56ba07c 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -639,6 +639,11 @@ error:
return err;
}
+static int python_flush_script(void)
+{
+ return 0;
+}
+
/*
* Stop trace script
*/
@@ -823,6 +828,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile)
struct scripting_ops python_scripting_ops = {
.name = "Python",
.start_script = python_start_script,
+ .flush_script = python_flush_script,
.stop_script = python_stop_script,
.process_event = python_process_event,
.generate_script = python_generate_script,
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index 57aaccc..5c9bdd1 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -30,6 +30,11 @@
struct scripting_context *scripting_context;
+static int flush_script_unsupported(void)
+{
+ return 0;
+}
+
static int stop_script_unsupported(void)
{
return 0;
@@ -74,6 +79,7 @@ static int python_generate_script_unsupported(struct pevent *pevent
struct scripting_ops python_scripting_unsupported_ops = {
.name = "Python",
.start_script = python_start_script_unsupported,
+ .flush_script = flush_script_unsupported,
.stop_script = stop_script_unsupported,
.process_event = process_event_unsupported,
.generate_script = python_generate_script_unsupported,
@@ -137,6 +143,7 @@ static int perl_generate_script_unsupported(struct pevent *pevent
struct scripting_ops perl_scripting_unsupported_ops = {
.name = "Perl",
.start_script = perl_start_script_unsupported,
+ .flush_script = flush_script_unsupported,
.stop_script = stop_script_unsupported,
.process_event = process_event_unsupported,
.generate_script = perl_generate_script_unsupported,
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 7b6d686..52aaa19 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -64,6 +64,7 @@ struct perf_session;
struct scripting_ops {
const char *name;
int (*start_script) (const char *script, int argc, const char **argv);
+ int (*flush_script) (void);
int (*stop_script) (void);
void (*process_event) (union perf_event *event,
struct perf_sample *sample,
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 03/24] perf tools: Rename machine__get_kernel_start_addr()
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
2014-08-15 19:08 ` [PATCH 01/24] perf tools: Add a test for tracking with sched_switch Adrian Hunter
2014-08-15 19:08 ` [PATCH 02/24] perf scripting: Add 'flush' callback to scripting API Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-24 14:58 ` [tip:perf/core] perf machine: Rename machine__get_kernel_start_addr() method tip-bot for Adrian Hunter
2014-08-15 19:08 ` [PATCH 04/24] perf tools: Add machine__kernel_ip() Adrian Hunter
` (20 subsequent siblings)
23 siblings, 1 reply; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Rename machine__get_kernel_start_addr() to
machine__get_running_kernel_start() so that
a new function, with a similar name to the
original name, can be added that gets the
kernel start address from the kernel map.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/machine.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index b093b93..37f8dc5 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -593,8 +593,8 @@ const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL};
* Returns the name of the start symbol in *symbol_name. Pass in NULL as
* symbol_name if it's not that important.
*/
-static u64 machine__get_kernel_start_addr(struct machine *machine,
- const char **symbol_name)
+static u64 machine__get_running_kernel_start(struct machine *machine,
+ const char **symbol_name)
{
char filename[PATH_MAX];
int i;
@@ -621,7 +621,7 @@ static u64 machine__get_kernel_start_addr(struct machine *machine,
int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
{
enum map_type type;
- u64 start = machine__get_kernel_start_addr(machine, NULL);
+ u64 start = machine__get_running_kernel_start(machine, NULL);
for (type = 0; type < MAP__NR_TYPES; ++type) {
struct kmap *kmap;
@@ -940,7 +940,7 @@ int machine__create_kernel_maps(struct machine *machine)
{
struct dso *kernel = machine__get_kernel(machine);
const char *name;
- u64 addr = machine__get_kernel_start_addr(machine, &name);
+ u64 addr = machine__get_running_kernel_start(machine, &name);
if (!addr)
return -1;
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 04/24] perf tools: Add machine__kernel_ip()
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (2 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 03/24] perf tools: Rename machine__get_kernel_start_addr() Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-24 14:58 ` [tip:perf/core] " tip-bot for Adrian Hunter
2014-08-15 19:08 ` [PATCH 05/24] perf tools: Let a user specify a PMU event without any config terms Adrian Hunter
` (19 subsequent siblings)
23 siblings, 1 reply; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Add a function to determine if an address is in
the kernel. This is based on the kernel function
kernel_ip().
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/event.c | 6 +++---
tools/perf/util/machine.c | 23 +++++++++++++++++++++++
tools/perf/util/machine.h | 17 +++++++++++++++++
3 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 1398c83..ed55819 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -784,9 +784,9 @@ try_again:
* "[vdso]" dso, but for now lets use the old trick of looking
* in the whole kernel symbol list.
*/
- if ((long long)al->addr < 0 &&
- cpumode == PERF_RECORD_MISC_USER &&
- machine && mg != &machine->kmaps) {
+ if (cpumode == PERF_RECORD_MISC_USER && machine &&
+ mg != &machine->kmaps &&
+ machine__kernel_ip(machine, al->addr)) {
mg = &machine->kmaps;
load_map = true;
goto try_again;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 37f8dc5..e00daf0 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -32,6 +32,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
machine->symbol_filter = NULL;
machine->id_hdr_size = 0;
machine->comm_exec = false;
+ machine->kernel_start = 0;
machine->root_dir = strdup(root_dir);
if (machine->root_dir == NULL)
@@ -1559,3 +1560,25 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
return 0;
}
+
+int machine__get_kernel_start(struct machine *machine)
+{
+ struct map *map = machine__kernel_map(machine, MAP__FUNCTION);
+ int err = 0;
+
+ /*
+ * The only addresses above 2^63 are kernel addresses of a 64-bit
+ * kernel. Note that addresses are unsigned so that on a 32-bit system
+ * all addresses including kernel addresses are less than 2^32. In
+ * that case (32-bit system), if the kernel mapping is unknown, all
+ * addresses will be assumed to be in user space - see
+ * machine__kernel_ip().
+ */
+ machine->kernel_start = 1ULL << 63;
+ if (map) {
+ err = map__load(map, machine->symbol_filter);
+ if (map->start)
+ machine->kernel_start = map->start;
+ }
+ return err;
+}
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 61216e0..6a6bcc1 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -36,6 +36,7 @@ struct machine {
struct list_head kernel_dsos;
struct map_groups kmaps;
struct map *vmlinux_maps[MAP__NR_TYPES];
+ u64 kernel_start;
symbol_filter_t symbol_filter;
pid_t *current_tid;
};
@@ -46,6 +47,22 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type)
return machine->vmlinux_maps[type];
}
+int machine__get_kernel_start(struct machine *machine);
+
+static inline u64 machine__kernel_start(struct machine *machine)
+{
+ if (!machine->kernel_start)
+ machine__get_kernel_start(machine);
+ return machine->kernel_start;
+}
+
+static inline bool machine__kernel_ip(struct machine *machine, u64 ip)
+{
+ u64 kernel_start = machine__kernel_start(machine);
+
+ return ip >= kernel_start;
+}
+
struct thread *machine__find_thread(struct machine *machine, pid_t pid,
pid_t tid);
struct comm *machine__thread_exec_comm(struct machine *machine,
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 05/24] perf tools: Let a user specify a PMU event without any config terms
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (3 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 04/24] perf tools: Add machine__kernel_ip() Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-09-19 5:20 ` [tip:perf/core] " tip-bot for Adrian Hunter
2014-08-15 19:08 ` [PATCH 06/24] perf tools: Let default config be defined for a PMU Adrian Hunter
` (18 subsequent siblings)
23 siblings, 1 reply; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
This enables a PMU event to be specified in the form:
pmu//
which is effectively the same as:
pmu/config=0/
This patch is a precursor to defining
default config for a PMU.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/parse-events.c | 6 ++++++
tools/perf/util/parse-events.y | 10 ++++++++++
2 files changed, 16 insertions(+)
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index e34c81a..e756288 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -645,6 +645,12 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
memset(&attr, 0, sizeof(attr));
+ if (!head_config) {
+ attr.type = pmu->type;
+ evsel = __add_event(list, idx, &attr, NULL, pmu->cpus);
+ return evsel ? 0 : -ENOMEM;
+ }
+
if (perf_pmu__check_alias(pmu, head_config, &unit, &scale))
return -EINVAL;
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 0bc87ba..55fab6a 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -210,6 +210,16 @@ PE_NAME '/' event_config '/'
parse_events__free_terms($3);
$$ = list;
}
+|
+PE_NAME '/' '/'
+{
+ struct parse_events_evlist *data = _data;
+ struct list_head *list;
+
+ ALLOC_LIST(list);
+ ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL));
+ $$ = list;
+}
value_sym:
PE_VALUE_SYM_HW
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 06/24] perf tools: Let default config be defined for a PMU
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (4 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 05/24] perf tools: Let a user specify a PMU event without any config terms Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 07/24] perf tools: Add perf_pmu__scan_file() Adrian Hunter
` (17 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
This allows default config terms to be provided
for a PMU. So, for example, when the Intel PT
PMU is added, it will be possible to specify:
intel_pt//
which will be the same as:
intel_pt/tsc=1,noretcomp=0/
meaning that the trace should contain
TSC timestamps and perform 'return
compression'.
An important consideration of this
patch is that it must be possible
to overwrite the default values.
That has meant changing the logic
so that a zero value can replace
a non-zero value.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/tests/pmu.c | 2 +-
tools/perf/util/parse-events.c | 7 ++++++-
tools/perf/util/pmu.c | 42 ++++++++++++++++++++++++++----------------
tools/perf/util/pmu.h | 9 ++++++++-
4 files changed, 41 insertions(+), 19 deletions(-)
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c
index 12b322f..eeb68bb1 100644
--- a/tools/perf/tests/pmu.c
+++ b/tools/perf/tests/pmu.c
@@ -152,7 +152,7 @@ int test__pmu(void)
if (ret)
break;
- ret = perf_pmu__config_terms(&formats, &attr, terms);
+ ret = perf_pmu__config_terms(&formats, &attr, terms, false);
if (ret)
break;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index e756288..61be3e6 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -643,7 +643,12 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
if (!pmu)
return -EINVAL;
- memset(&attr, 0, sizeof(attr));
+ if (pmu->default_config) {
+ memcpy(&attr, pmu->default_config,
+ sizeof(struct perf_event_attr));
+ } else {
+ memset(&attr, 0, sizeof(attr));
+ }
if (!head_config) {
attr.type = pmu->type;
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 9bf5827..438bb26 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -2,6 +2,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
+#include <stdbool.h>
#include <dirent.h>
#include <api/fs/fs.h>
#include <locale.h>
@@ -387,6 +388,12 @@ static struct cpu_map *pmu_cpumask(const char *name)
return cpus;
}
+struct perf_event_attr *__attribute__((weak))
+perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
+{
+ return NULL;
+}
+
static struct perf_pmu *pmu_lookup(const char *name)
{
struct perf_pmu *pmu;
@@ -421,6 +428,9 @@ static struct perf_pmu *pmu_lookup(const char *name)
pmu->name = strdup(name);
pmu->type = type;
list_add_tail(&pmu->list, &pmus);
+
+ pmu->default_config = perf_pmu__get_default_config(pmu);
+
return pmu;
}
@@ -479,28 +489,24 @@ pmu_find_format(struct list_head *formats, char *name)
}
/*
- * Returns value based on the format definition (format parameter)
+ * Sets value based on the format definition (format parameter)
* and unformated value (value parameter).
- *
- * TODO maybe optimize a little ;)
*/
-static __u64 pmu_format_value(unsigned long *format, __u64 value)
+static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v,
+ bool zero)
{
unsigned long fbit, vbit;
- __u64 v = 0;
for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) {
if (!test_bit(fbit, format))
continue;
- if (!(value & (1llu << vbit++)))
- continue;
-
- v |= (1llu << fbit);
+ if (value & (1llu << vbit++))
+ *v |= (1llu << fbit);
+ else if (zero)
+ *v &= ~(1llu << fbit);
}
-
- return v;
}
/*
@@ -509,7 +515,8 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value)
*/
static int pmu_config_term(struct list_head *formats,
struct perf_event_attr *attr,
- struct parse_events_term *term)
+ struct parse_events_term *term,
+ bool zero)
{
struct perf_pmu_format *format;
__u64 *vp;
@@ -548,18 +555,19 @@ static int pmu_config_term(struct list_head *formats,
* non-hardcoded terms, here's the place to translate
* them into value.
*/
- *vp |= pmu_format_value(format->bits, term->val.num);
+ pmu_format_value(format->bits, term->val.num, vp, zero);
return 0;
}
int perf_pmu__config_terms(struct list_head *formats,
struct perf_event_attr *attr,
- struct list_head *head_terms)
+ struct list_head *head_terms,
+ bool zero)
{
struct parse_events_term *term;
list_for_each_entry(term, head_terms, list)
- if (pmu_config_term(formats, attr, term))
+ if (pmu_config_term(formats, attr, term, zero))
return -EINVAL;
return 0;
@@ -573,8 +581,10 @@ int perf_pmu__config_terms(struct list_head *formats,
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms)
{
+ bool zero = !!pmu->default_config;
+
attr->type = pmu->type;
- return perf_pmu__config_terms(&pmu->format, attr, head_terms);
+ return perf_pmu__config_terms(&pmu->format, attr, head_terms, zero);
}
static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 1c1e2ee..413b9a6 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -13,9 +13,12 @@ enum {
#define PERF_PMU_FORMAT_BITS 64
+struct perf_event_attr;
+
struct perf_pmu {
char *name;
__u32 type;
+ struct perf_event_attr *default_config;
struct cpu_map *cpus;
struct list_head format; /* HEAD struct perf_pmu_format -> list */
struct list_head aliases; /* HEAD struct perf_pmu_alias -> list */
@@ -27,7 +30,8 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms);
int perf_pmu__config_terms(struct list_head *formats,
struct perf_event_attr *attr,
- struct list_head *head_terms);
+ struct list_head *head_terms,
+ bool zero);
int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
const char **unit, double *scale);
struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
@@ -46,4 +50,7 @@ void print_pmu_events(const char *event_glob, bool name_only);
bool pmu_have_event(const char *pname, const char *name);
int perf_pmu__test(void);
+
+struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
+
#endif /* __PMU_H */
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 07/24] perf tools: Add perf_pmu__scan_file()
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (5 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 06/24] perf tools: Let default config be defined for a PMU Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 08/24] perf tools: Add id index Adrian Hunter
` (16 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Add a function to scan a sysfs file within the pmu device
directory.
This will be used to read capability values from the PMU
'caps' subdirectory.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/pmu.c | 37 +++++++++++++++++++++++++++++++++++++
tools/perf/util/pmu.h | 3 +++
2 files changed, 40 insertions(+)
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 438bb26..22a4ad5 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -3,6 +3,7 @@
#include <unistd.h>
#include <stdio.h>
#include <stdbool.h>
+#include <stdarg.h>
#include <dirent.h>
#include <api/fs/fs.h>
#include <locale.h>
@@ -804,3 +805,39 @@ bool pmu_have_event(const char *pname, const char *name)
}
return false;
}
+
+static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name)
+{
+ struct stat st;
+ char path[PATH_MAX];
+ const char *sysfs;
+
+ sysfs = sysfs__mountpoint();
+ if (!sysfs)
+ return NULL;
+
+ snprintf(path, PATH_MAX,
+ "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name);
+
+ if (stat(path, &st) < 0)
+ return NULL;
+
+ return fopen(path, "r");
+}
+
+int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
+ ...)
+{
+ va_list args;
+ FILE *file;
+ int ret = EOF;
+
+ va_start(args, fmt);
+ file = perf_pmu__open_file(pmu, name);
+ if (file) {
+ ret = vfscanf(file, fmt, args);
+ fclose(file);
+ }
+ va_end(args);
+ return ret;
+}
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 413b9a6..0f5c0a8 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -49,6 +49,9 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
void print_pmu_events(const char *event_glob, bool name_only);
bool pmu_have_event(const char *pname, const char *name);
+int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt,
+ ...) __attribute__((format(scanf, 3, 4)));
+
int perf_pmu__test(void);
struct perf_event_attr *perf_pmu__get_default_config(struct perf_pmu *pmu);
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 08/24] perf tools: Add id index
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (6 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 07/24] perf tools: Add perf_pmu__scan_file() Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 09/24] perf pmu: Let pmu's with no events show up on perf list Adrian Hunter
` (15 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Add an index of the event identifiers.
This is needed to queue Instruction
Trace samples according to the mmap
buffer from which they were recorded.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/builtin-inject.c | 1 +
tools/perf/util/event.c | 1 +
tools/perf/util/event.h | 15 ++++++
tools/perf/util/evlist.c | 26 ++++++++--
tools/perf/util/evsel.h | 3 ++
tools/perf/util/session.c | 122 ++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/session.h | 10 ++++
tools/perf/util/tool.h | 3 +-
8 files changed, 177 insertions(+), 4 deletions(-)
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 3a62b6b..101344c 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -410,6 +410,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
.tracing_data = perf_event__repipe_op2_synth,
.finished_round = perf_event__repipe_op2_synth,
.build_id = perf_event__repipe_op2_synth,
+ .id_index = perf_event__repipe_op2_synth,
},
.input_name = "-",
.samples = LIST_HEAD_INIT(inject.samples),
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index ed55819..1727574 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -28,6 +28,7 @@ static const char *perf_event__names[] = {
[PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
[PERF_RECORD_HEADER_BUILD_ID] = "BUILD_ID",
[PERF_RECORD_FINISHED_ROUND] = "FINISHED_ROUND",
+ [PERF_RECORD_ID_INDEX] = "ID_INDEX",
};
const char *perf_event__name(unsigned int id)
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 7eb7107..9a017fa 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -187,6 +187,7 @@ enum perf_user_event_type { /* above any possible kernel type */
PERF_RECORD_HEADER_TRACING_DATA = 66,
PERF_RECORD_HEADER_BUILD_ID = 67,
PERF_RECORD_FINISHED_ROUND = 68,
+ PERF_RECORD_ID_INDEX = 69,
PERF_RECORD_HEADER_MAX
};
@@ -213,6 +214,19 @@ struct tracing_data_event {
u32 size;
};
+struct id_index_entry {
+ u64 id;
+ u64 idx;
+ u64 cpu;
+ u64 tid;
+};
+
+struct id_index_event {
+ struct perf_event_header header;
+ u64 nr;
+ struct id_index_entry entries[0];
+};
+
union perf_event {
struct perf_event_header header;
struct mmap_event mmap;
@@ -227,6 +241,7 @@ union perf_event {
struct event_type_event event_type;
struct tracing_data_event tracing_data;
struct build_id_event build_id;
+ struct id_index_event id_index;
};
void perf_event__print_totals(void);
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index a3e28b4..46dd44b 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -489,6 +489,22 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
return 0;
}
+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 = evlist->threads->map[thread];
+ else
+ sid->tid = -1;
+}
+
struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id)
{
struct hlist_head *head;
@@ -724,9 +740,13 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
return -1;
}
- if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
- perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0)
- 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;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 7bc314b..d46d28e 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -38,6 +38,9 @@ struct perf_sample_id {
struct hlist_node node;
u64 id;
struct perf_evsel *evsel;
+ int idx;
+ int cpu;
+ pid_t tid;
/* Holds total ID period value for PERF_SAMPLE_READ processing. */
u64 period;
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 6d2d50d..7ffd1a3 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -228,6 +228,15 @@ static int process_finished_round(struct perf_tool *tool,
union perf_event *event,
struct perf_session *session);
+static int process_id_index_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_session *perf_session
+ __maybe_unused)
+{
+ dump_printf(": unhandled!\n");
+ return 0;
+}
+
void perf_tool__fill_defaults(struct perf_tool *tool)
{
if (tool->sample == NULL)
@@ -262,6 +271,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
else
tool->finished_round = process_finished_round_stub;
}
+ if (tool->id_index == NULL)
+ tool->id_index = process_id_index_stub;
}
static void swap_sample_id_all(union perf_event *event, void *data)
@@ -460,6 +471,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
[PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap,
[PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap,
[PERF_RECORD_HEADER_BUILD_ID] = NULL,
+ [PERF_RECORD_ID_INDEX] = perf_event__all64_swap,
[PERF_RECORD_HEADER_MAX] = NULL,
};
@@ -905,6 +917,8 @@ static s64 perf_session__process_user_event(struct perf_session *session,
return tool->build_id(tool, event, session);
case PERF_RECORD_FINISHED_ROUND:
return tool->finished_round(tool, event, session);
+ case PERF_RECORD_ID_INDEX:
+ return tool->id_index(tool, event, session);
default:
return -EINVAL;
}
@@ -1618,3 +1632,111 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
out:
return err;
}
+
+int perf_event__process_id_index(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_session *session)
+{
+ struct perf_evlist *evlist = session->evlist;
+ struct id_index_event *ie = &event->id_index;
+ size_t i, nr, max_nr;
+
+ max_nr = (ie->header.size - sizeof(struct id_index_event)) /
+ sizeof(struct id_index_entry);
+ nr = ie->nr;
+ if (nr > max_nr)
+ return -EINVAL;
+
+ if (dump_trace)
+ fprintf(stdout, " nr: %zu\n", nr);
+
+ for (i = 0; i < nr; i++) {
+ struct id_index_entry *e = &ie->entries[i];
+ struct perf_sample_id *sid;
+
+ if (dump_trace) {
+ fprintf(stdout, " ... id: %"PRIu64, e->id);
+ fprintf(stdout, " idx: %"PRIu64, e->idx);
+ fprintf(stdout, " cpu: %"PRId64, e->cpu);
+ fprintf(stdout, " tid: %"PRId64"\n", e->tid);
+ }
+
+ sid = perf_evlist__id2sid(evlist, e->id);
+ if (!sid)
+ return -ENOENT;
+ sid->idx = e->idx;
+ sid->cpu = e->cpu;
+ sid->tid = e->tid;
+ }
+ return 0;
+}
+
+int perf_event__synthesize_id_index(struct perf_tool *tool,
+ perf_event__handler_t process,
+ struct perf_evlist *evlist,
+ struct machine *machine)
+{
+ union perf_event *ev;
+ struct perf_evsel *evsel;
+ size_t nr = 0, i = 0, sz, max_nr, n;
+ int err;
+
+ pr_debug2("Synthesizing id index\n");
+
+ max_nr = (UINT16_MAX - sizeof(struct id_index_event)) /
+ sizeof(struct id_index_entry);
+
+ list_for_each_entry(evsel, &evlist->entries, node)
+ nr += evsel->ids;
+
+ n = nr > max_nr ? max_nr : nr;
+ sz = sizeof(struct id_index_event) + n * sizeof(struct id_index_entry);
+ ev = zalloc(sz);
+ if (!ev)
+ return -ENOMEM;
+
+ ev->id_index.header.type = PERF_RECORD_ID_INDEX;
+ ev->id_index.header.size = sz;
+ ev->id_index.nr = n;
+
+ list_for_each_entry(evsel, &evlist->entries, node) {
+ u32 j;
+
+ for (j = 0; j < evsel->ids; j++) {
+ struct id_index_entry *e;
+ struct perf_sample_id *sid;
+
+ if (i >= n) {
+ err = process(tool, ev, NULL, machine);
+ if (err)
+ goto out_err;
+ nr -= n;
+ i = 0;
+ }
+
+ e = &ev->id_index.entries[i++];
+
+ e->id = evsel->id[j];
+
+ sid = perf_evlist__id2sid(evlist, e->id);
+ if (!sid) {
+ free(ev);
+ return -ENOENT;
+ }
+
+ e->idx = sid->idx;
+ e->cpu = sid->cpu;
+ e->tid = sid->tid;
+ }
+ }
+
+ sz = sizeof(struct id_index_event) + nr * sizeof(struct id_index_entry);
+ ev->id_index.header.size = sz;
+ ev->id_index.nr = nr;
+
+ err = process(tool, ev, NULL, machine);
+out_err:
+ free(ev);
+
+ return err;
+}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 8dd41ca..b3a8fa9 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -127,4 +127,14 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session,
extern volatile int session_done;
#define session_done() (*(volatile int *)(&session_done))
+
+int perf_event__process_id_index(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_session *session);
+
+int perf_event__synthesize_id_index(struct perf_tool *tool,
+ perf_event__handler_t process,
+ struct perf_evlist *evlist,
+ struct machine *machine);
+
#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h
index f116369..bb2708b 100644
--- a/tools/perf/util/tool.h
+++ b/tools/perf/util/tool.h
@@ -39,7 +39,8 @@ struct perf_tool {
event_attr_op attr;
event_op2 tracing_data;
event_op2 finished_round,
- build_id;
+ build_id,
+ id_index;
bool ordered_events;
bool ordering_requires_timestamps;
};
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 09/24] perf pmu: Let pmu's with no events show up on perf list
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (7 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 08/24] perf tools: Add id index Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 10/24] perf session: Add perf_session__deliver_synth_event() Adrian Hunter
` (14 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
perf list only lists PMUs with events. Add a
flag to cause a PMU to be also listed separately.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/pmu.c | 13 +++++++++++--
tools/perf/util/pmu.h | 1 +
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 22a4ad5..7cfc481 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -749,15 +749,18 @@ void print_pmu_events(const char *event_glob, bool name_only)
pmu = NULL;
len = 0;
- while ((pmu = perf_pmu__scan(pmu)) != NULL)
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list)
len++;
+ if (pmu->selectable)
+ len++;
+ }
aliases = malloc(sizeof(char *) * len);
if (!aliases)
return;
pmu = NULL;
j = 0;
- while ((pmu = perf_pmu__scan(pmu)) != NULL)
+ while ((pmu = perf_pmu__scan(pmu)) != NULL) {
list_for_each_entry(alias, &pmu->aliases, list) {
char *name = format_alias(buf, sizeof(buf), pmu, alias);
bool is_cpu = !strcmp(pmu->name, "cpu");
@@ -774,6 +777,12 @@ void print_pmu_events(const char *event_glob, bool name_only)
aliases[j] = strdup(aliases[j]);
j++;
}
+ if (pmu->selectable) {
+ scnprintf(buf, sizeof(buf), "%s//", pmu->name);
+ aliases[j] = strdup(buf);
+ j++;
+ }
+ }
len = j;
qsort(aliases, len, sizeof(char *), cmp_string);
for (j = 0; j < len; j++) {
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 0f5c0a8..bb54858 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -18,6 +18,7 @@ struct perf_event_attr;
struct perf_pmu {
char *name;
__u32 type;
+ bool selectable;
struct perf_event_attr *default_config;
struct cpu_map *cpus;
struct list_head format; /* HEAD struct perf_pmu_format -> list */
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 10/24] perf session: Add perf_session__deliver_synth_event()
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (8 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 09/24] perf pmu: Let pmu's with no events show up on perf list Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 11/24] perf tools: Add a thread stack for synthesizing call chains Adrian Hunter
` (13 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Add a function to deliver synthesized events from
within a session.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/session.c | 14 ++++++++++++++
tools/perf/util/session.h | 5 +++++
2 files changed, 19 insertions(+)
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 7ffd1a3..cb0f26d 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -924,6 +924,20 @@ static s64 perf_session__process_user_event(struct perf_session *session,
}
}
+int perf_session__deliver_synth_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_tool *tool)
+{
+ events_stats__inc(&session->stats, event->header.type);
+
+ if (event->header.type >= PERF_RECORD_USER_TYPE_START)
+ return perf_session__process_user_event(session, event, tool,
+ 0);
+
+ return perf_session__deliver_event(session, event, sample, tool, 0);
+}
+
static void event_swap(union perf_event *event, bool sample_id_all)
{
perf_event__swap_op swap;
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index b3a8fa9..c0f4c2c 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -128,6 +128,11 @@ extern volatile int session_done;
#define session_done() (*(volatile int *)(&session_done))
+int perf_session__deliver_synth_event(struct perf_session *session,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_tool *tool);
+
int perf_event__process_id_index(struct perf_tool *tool,
union perf_event *event,
struct perf_session *session);
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 11/24] perf tools: Add a thread stack for synthesizing call chains
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (9 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 10/24] perf session: Add perf_session__deliver_synth_event() Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 12/24] perf tools: Add facility to export data in database-friendly way Adrian Hunter
` (12 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Add a thread stack for synthesizing call chains from call
and return events.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/Makefile.perf | 2 +
tools/perf/util/event.h | 26 +++++++
tools/perf/util/thread-stack.c | 151 +++++++++++++++++++++++++++++++++++++++++
tools/perf/util/thread-stack.h | 32 +++++++++
tools/perf/util/thread.c | 3 +
tools/perf/util/thread.h | 3 +
6 files changed, 217 insertions(+)
create mode 100644 tools/perf/util/thread-stack.c
create mode 100644 tools/perf/util/thread-stack.h
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 95e832b..f754067 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -303,6 +303,7 @@ LIB_H += ui/util.h
LIB_H += ui/ui.h
LIB_H += util/data.h
LIB_H += util/kvm-stat.h
+LIB_H += util/thread-stack.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
@@ -379,6 +380,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o
LIB_OBJS += $(OUTPUT)util/data.o
LIB_OBJS += $(OUTPUT)util/tsc.o
LIB_OBJS += $(OUTPUT)util/cloexec.o
+LIB_OBJS += $(OUTPUT)util/thread-stack.o
LIB_OBJS += $(OUTPUT)ui/setup.o
LIB_OBJS += $(OUTPUT)ui/helpline.o
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 9a017fa..17048f7 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -143,6 +143,32 @@ struct branch_stack {
struct branch_entry entries[0];
};
+enum {
+ PERF_FLAG_BRANCH = 1ULL << 0,
+ PERF_FLAG_CALL = 1ULL << 1,
+ PERF_FLAG_RETURN = 1ULL << 2,
+ PERF_FLAG_CONDITIONAL = 1ULL << 3,
+ PERF_FLAG_SYSCALLRET = 1ULL << 4,
+ PERF_FLAG_ASYNC = 1ULL << 5,
+ PERF_FLAG_INTERRUPT = 1ULL << 6,
+ PERF_FLAG_TX_ABORT = 1ULL << 7,
+ PERF_FLAG_TRACE_BEGIN = 1ULL << 8,
+ PERF_FLAG_TRACE_END = 1ULL << 9,
+ PERF_FLAG_IN_TX = 1ULL << 10,
+};
+
+#define PERF_BRANCH_MASK (\
+ PERF_FLAG_BRANCH |\
+ PERF_FLAG_CALL |\
+ PERF_FLAG_RETURN |\
+ PERF_FLAG_CONDITIONAL |\
+ PERF_FLAG_SYSCALLRET |\
+ PERF_FLAG_ASYNC |\
+ PERF_FLAG_INTERRUPT |\
+ PERF_FLAG_TX_ABORT |\
+ PERF_FLAG_TRACE_BEGIN |\
+ PERF_FLAG_TRACE_END)
+
struct perf_sample {
u64 ip;
u32 pid, tid;
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
new file mode 100644
index 0000000..c1ca2a9
--- /dev/null
+++ b/tools/perf/util/thread-stack.c
@@ -0,0 +1,151 @@
+/*
+ * thread-stack.c: Synthesize a thread's stack using call / return events
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include "thread.h"
+#include "event.h"
+#include "util.h"
+#include "thread-stack.h"
+
+#define STACK_GROWTH 4096
+
+struct thread_stack_entry {
+ u64 ret_addr;
+};
+
+struct thread_stack {
+ struct thread_stack_entry *stack;
+ size_t cnt;
+ size_t sz;
+ u64 trace_nr;
+};
+
+static void thread_stack__grow(struct thread_stack *ts)
+{
+ struct thread_stack_entry *new_stack;
+ size_t sz, new_sz;
+
+ new_sz = ts->sz + STACK_GROWTH;
+ sz = new_sz * sizeof(struct thread_stack_entry);
+ new_stack = realloc(ts->stack, sz);
+ if (new_stack) {
+ ts->stack = new_stack;
+ ts->sz = new_sz;
+ }
+}
+
+static struct thread_stack *thread_stack__new(void)
+{
+ struct thread_stack *ts;
+
+ ts = zalloc(sizeof(struct thread_stack));
+ if (!ts)
+ return NULL;
+
+ thread_stack__grow(ts);
+ if (!ts->stack) {
+ free(ts);
+ return NULL;
+ }
+
+ return ts;
+}
+
+static void thread_stack__push(struct thread_stack *ts, u64 ret_addr)
+{
+ if (ts->cnt == ts->sz) {
+ thread_stack__grow(ts);
+ if (ts->cnt == ts->sz)
+ ts->cnt = 0;
+ }
+
+ ts->stack[ts->cnt++].ret_addr = ret_addr;
+}
+
+static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
+{
+ if (!ts->cnt)
+ return;
+
+ if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) {
+ ts->cnt -= 1;
+ } else {
+ size_t i = ts->cnt - 1;
+
+ while (i--) {
+ if (ts->stack[i].ret_addr == ret_addr) {
+ ts->cnt = i;
+ return;
+ }
+ }
+ }
+}
+
+void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
+ u64 to_ip, u16 insn_len, u64 trace_nr)
+{
+ if (!thread)
+ return;
+
+ if (!thread->ts) {
+ thread->ts = thread_stack__new();
+ if (!thread->ts)
+ return;
+ thread->ts->trace_nr = trace_nr;
+ }
+
+ if (trace_nr != thread->ts->trace_nr) {
+ thread->ts->trace_nr = trace_nr;
+ thread->ts->cnt = 0;
+ }
+
+ if (flags & PERF_FLAG_CALL) {
+ u64 ret_addr;
+
+ if (!to_ip)
+ return;
+ ret_addr = from_ip + insn_len;
+ if (ret_addr == to_ip)
+ return; /* Zero-length calls are excluded */
+ thread_stack__push(thread->ts, ret_addr);
+ } else if (flags & PERF_FLAG_RETURN) {
+ if (!from_ip)
+ return;
+ thread_stack__pop(thread->ts, to_ip);
+ }
+}
+
+void thread_stack__free(struct thread *thread)
+{
+ if (thread->ts) {
+ zfree(&thread->ts->stack);
+ zfree(&thread->ts);
+ }
+}
+
+void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
+ size_t sz, u64 ip)
+{
+ size_t i;
+
+ if (!thread || !thread->ts)
+ chain->nr = 1;
+ else
+ chain->nr = min(sz, thread->ts->cnt + 1);
+
+ chain->ips[0] = ip;
+
+ for (i = 1; i < chain->nr; i++)
+ chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;
+}
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
new file mode 100644
index 0000000..c0ba4cf
--- /dev/null
+++ b/tools/perf/util/thread-stack.h
@@ -0,0 +1,32 @@
+/*
+ * thread-stack.h: Synthesize a thread's stack using call / return events
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __PERF_THREAD_STACK_H
+#define __PERF_THREAD_STACK_H
+
+#include <sys/types.h>
+
+#include <linux/types.h>
+
+struct thread;
+struct ip_callchain;
+
+void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
+ u64 to_ip, u16 insn_len, u64 trace_nr);
+void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
+ size_t sz, u64 ip);
+void thread_stack__free(struct thread *thread);
+
+#endif
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index a9df7f2..088c036 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -4,6 +4,7 @@
#include <string.h>
#include "session.h"
#include "thread.h"
+#include "thread-stack.h"
#include "util.h"
#include "debug.h"
#include "comm.h"
@@ -61,6 +62,8 @@ void thread__delete(struct thread *thread)
{
struct comm *comm, *tmp;
+ thread_stack__free(thread);
+
if (thread->mg) {
map_groups__put(thread->mg);
thread->mg = NULL;
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 8c75fa7..a057820 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -8,6 +8,8 @@
#include "symbol.h"
#include <strlist.h>
+struct thread_stack;
+
struct thread {
union {
struct rb_node rb_node;
@@ -25,6 +27,7 @@ struct thread {
int comm_len;
void *priv;
+ struct thread_stack *ts;
};
struct machine;
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 12/24] perf tools: Add facility to export data in database-friendly way
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (10 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 11/24] perf tools: Add a thread stack for synthesizing call chains Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 13/24] perf tools: Extend Python script interface to export data in a " Adrian Hunter
` (11 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
This patch introduces an abstraction for exporting sample
data in a database-friendly way. The abstraction does not
implement the actual output. A subsequent patch takes this
facility into use for extending the script interface.
The abstraction is needed because static data like symbols,
dsos, comms etc need to be exported only once. That means
allocating them a unique identifier and recording it on each
structure. The member 'db_id' is used for that. 'db_id'
is just a 64-bit sequence number.
Exporting centres around the db_export__sample() function
which exports the associated data structures if they have
not yet been allocated a db_id.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/Makefile.perf | 2 +
tools/perf/util/comm.h | 1 +
tools/perf/util/db-export.c | 268 ++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/db-export.h | 86 ++++++++++++++
tools/perf/util/dso.h | 1 +
tools/perf/util/evsel.h | 1 +
tools/perf/util/machine.h | 1 +
tools/perf/util/symbol.h | 1 +
tools/perf/util/thread.h | 1 +
9 files changed, 362 insertions(+)
create mode 100644 tools/perf/util/db-export.c
create mode 100644 tools/perf/util/db-export.h
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index f754067..df1b164 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -304,6 +304,7 @@ LIB_H += ui/ui.h
LIB_H += util/data.h
LIB_H += util/kvm-stat.h
LIB_H += util/thread-stack.h
+LIB_H += util/db-export.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
@@ -381,6 +382,7 @@ LIB_OBJS += $(OUTPUT)util/data.o
LIB_OBJS += $(OUTPUT)util/tsc.o
LIB_OBJS += $(OUTPUT)util/cloexec.o
LIB_OBJS += $(OUTPUT)util/thread-stack.o
+LIB_OBJS += $(OUTPUT)util/db-export.o
LIB_OBJS += $(OUTPUT)ui/setup.o
LIB_OBJS += $(OUTPUT)ui/helpline.o
diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h
index 51c10ab..99e7021 100644
--- a/tools/perf/util/comm.h
+++ b/tools/perf/util/comm.h
@@ -10,6 +10,7 @@ struct comm_str;
struct comm {
struct comm_str *comm_str;
u64 start;
+ u64 db_id;
struct list_head list;
bool exec;
};
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
new file mode 100644
index 0000000..53d0e75
--- /dev/null
+++ b/tools/perf/util/db-export.c
@@ -0,0 +1,268 @@
+/*
+ * db-export.c: Support for exporting data suitable for import to a database
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <errno.h>
+
+#include "evsel.h"
+#include "machine.h"
+#include "thread.h"
+#include "comm.h"
+#include "symbol.h"
+#include "event.h"
+#include "db-export.h"
+
+int db_export__init(struct db_export *dbe)
+{
+ memset(dbe, 0, sizeof(struct db_export));
+ return 0;
+}
+
+void db_export__exit(struct db_export *dbe __maybe_unused)
+{
+}
+
+int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel)
+{
+ if (evsel->db_id)
+ return 0;
+
+ evsel->db_id = ++dbe->evsel_last_db_id;
+
+ if (dbe->export_evsel)
+ return dbe->export_evsel(dbe, evsel);
+
+ return 0;
+}
+
+int db_export__machine(struct db_export *dbe, struct machine *machine)
+{
+ if (machine->db_id)
+ return 0;
+
+ machine->db_id = ++dbe->machine_last_db_id;
+
+ if (dbe->export_machine)
+ return dbe->export_machine(dbe, machine);
+
+ return 0;
+}
+
+int db_export__thread(struct db_export *dbe, struct thread *thread,
+ struct machine *machine, struct comm *comm)
+{
+ u64 main_thread_db_id = 0;
+ int err;
+
+ if (thread->db_id)
+ return 0;
+
+ thread->db_id = ++dbe->thread_last_db_id;
+
+ if (thread->pid_ != -1) {
+ struct thread *main_thread;
+
+ if (thread->pid_ == thread->tid) {
+ main_thread = thread;
+ } else {
+ main_thread = machine__findnew_thread(machine,
+ thread->pid_,
+ thread->pid_);
+ if (!main_thread)
+ return -ENOMEM;
+ err = db_export__thread(dbe, main_thread, machine,
+ comm);
+ if (err)
+ return err;
+ if (comm) {
+ err = db_export__comm_thread(dbe, comm, thread);
+ if (err)
+ return err;
+ }
+ }
+ main_thread_db_id = main_thread->db_id;
+ }
+
+ if (dbe->export_thread)
+ return dbe->export_thread(dbe, thread, main_thread_db_id,
+ machine);
+
+ return 0;
+}
+
+int db_export__comm(struct db_export *dbe, struct comm *comm,
+ struct thread *main_thread)
+{
+ int err;
+
+ if (comm->db_id)
+ return 0;
+
+ comm->db_id = ++dbe->comm_last_db_id;
+
+ if (dbe->export_comm) {
+ err = dbe->export_comm(dbe, comm);
+ if (err)
+ return err;
+ }
+
+ return db_export__comm_thread(dbe, comm, main_thread);
+}
+
+int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
+ struct thread *thread)
+{
+ u64 db_id;
+
+ db_id = ++dbe->comm_thread_last_db_id;
+
+ if (dbe->export_comm_thread)
+ return dbe->export_comm_thread(dbe, db_id, comm, thread);
+
+ return 0;
+}
+
+int db_export__dso(struct db_export *dbe, struct dso *dso,
+ struct machine *machine)
+{
+ if (dso->db_id)
+ return 0;
+
+ dso->db_id = ++dbe->dso_last_db_id;
+
+ if (dbe->export_dso)
+ return dbe->export_dso(dbe, dso, machine);
+
+ return 0;
+}
+
+int db_export__symbol(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso)
+{
+ if (sym->db_id)
+ return 0;
+
+ sym->db_id = ++dbe->symbol_last_db_id;
+
+ if (dbe->export_symbol)
+ return dbe->export_symbol(dbe, sym, dso);
+
+ return 0;
+}
+
+static struct thread *get_main_thread(struct machine *machine,
+ struct thread *thread)
+{
+ if (thread->pid_ == thread->tid)
+ return thread;
+
+ if (thread->pid_ == -1)
+ return NULL;
+
+ return machine__find_thread(machine, thread->pid_, thread->pid_);
+}
+
+static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
+ u64 *dso_db_id, u64 *sym_db_id, u64 *offset)
+{
+ int err;
+
+ if (al->map) {
+ struct dso *dso = al->map->dso;
+
+ err = db_export__dso(dbe, dso, al->machine);
+ if (err)
+ return err;
+ *dso_db_id = dso->db_id;
+
+ if (!al->sym) {
+ al->sym = symbol__new(al->addr, 0, 0, "unknown");
+ if (al->sym)
+ symbols__insert(&dso->symbols[al->map->type],
+ al->sym);
+ }
+
+ if (al->sym) {
+ err = db_export__symbol(dbe, al->sym, dso);
+ if (err)
+ return err;
+ *sym_db_id = al->sym->db_id;
+ *offset = al->addr - al->sym->start;
+ }
+ }
+
+ return 0;
+}
+
+int db_export__sample(struct db_export *dbe, union perf_event *event,
+ struct perf_sample *sample, struct perf_evsel *evsel,
+ struct thread *thread, struct addr_location *al)
+{
+ struct export_sample es = {
+ .event = event,
+ .sample = sample,
+ .evsel = evsel,
+ .thread = thread,
+ .al = al,
+ };
+ struct thread *main_thread;
+ struct comm *comm = NULL;
+ int err;
+
+ err = db_export__evsel(dbe, evsel);
+ if (err)
+ return err;
+
+ err = db_export__machine(dbe, al->machine);
+ if (err)
+ return err;
+
+ main_thread = get_main_thread(al->machine, thread);
+ if (main_thread)
+ comm = machine__thread_exec_comm(al->machine, main_thread);
+
+ err = db_export__thread(dbe, thread, al->machine, comm);
+ if (err)
+ return err;
+
+ if (comm) {
+ err = db_export__comm(dbe, comm, main_thread);
+ if (err)
+ return err;
+ es.comm_db_id = comm->db_id;
+ }
+
+ es.db_id = ++dbe->sample_last_db_id;
+
+ err = db_ids_from_al(dbe, al, &es.dso_db_id, &es.sym_db_id, &es.offset);
+ if (err)
+ return err;
+
+ if ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
+ sample_addr_correlates_sym(&evsel->attr)) {
+ struct addr_location addr_al;
+
+ perf_event__preprocess_sample_addr(event, sample, al->machine,
+ thread, &addr_al);
+ err = db_ids_from_al(dbe, &addr_al, &es.addr_dso_db_id,
+ &es.addr_sym_db_id, &es.addr_offset);
+ if (err)
+ return err;
+ }
+
+ if (dbe->export_sample)
+ return dbe->export_sample(dbe, &es);
+
+ return 0;
+}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
new file mode 100644
index 0000000..b3643e8
--- /dev/null
+++ b/tools/perf/util/db-export.h
@@ -0,0 +1,86 @@
+/*
+ * db-export.h: Support for exporting data suitable for import to a database
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef __PERF_DB_EXPORT_H
+#define __PERF_DB_EXPORT_H
+
+#include <linux/types.h>
+
+struct perf_evsel;
+struct machine;
+struct thread;
+struct comm;
+struct dso;
+struct perf_sample;
+struct addr_location;
+
+struct export_sample {
+ union perf_event *event;
+ struct perf_sample *sample;
+ struct perf_evsel *evsel;
+ struct thread *thread;
+ struct addr_location *al;
+ u64 db_id;
+ u64 comm_db_id;
+ u64 dso_db_id;
+ u64 sym_db_id;
+ u64 offset; /* ip offset from symbol start */
+ u64 addr_dso_db_id;
+ u64 addr_sym_db_id;
+ u64 addr_offset; /* addr offset from symbol start */
+};
+
+struct db_export {
+ int (*export_evsel)(struct db_export *dbe, struct perf_evsel *evsel);
+ int (*export_machine)(struct db_export *dbe, struct machine *machine);
+ int (*export_thread)(struct db_export *dbe, struct thread *thread,
+ u64 main_thread_db_id, struct machine *machine);
+ int (*export_comm)(struct db_export *dbe, struct comm *comm);
+ int (*export_comm_thread)(struct db_export *dbe, u64 db_id,
+ struct comm *comm, struct thread *thread);
+ int (*export_dso)(struct db_export *dbe, struct dso *dso,
+ struct machine *machine);
+ int (*export_symbol)(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso);
+ int (*export_sample)(struct db_export *dbe, struct export_sample *es);
+ u64 evsel_last_db_id;
+ u64 machine_last_db_id;
+ u64 thread_last_db_id;
+ u64 comm_last_db_id;
+ u64 comm_thread_last_db_id;
+ u64 dso_last_db_id;
+ u64 symbol_last_db_id;
+ u64 sample_last_db_id;
+};
+
+int db_export__init(struct db_export *dbe);
+void db_export__exit(struct db_export *dbe);
+int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel);
+int db_export__machine(struct db_export *dbe, struct machine *machine);
+int db_export__thread(struct db_export *dbe, struct thread *thread,
+ struct machine *machine, struct comm *comm);
+int db_export__comm(struct db_export *dbe, struct comm *comm,
+ struct thread *main_thread);
+int db_export__comm_thread(struct db_export *dbe, struct comm *comm,
+ struct thread *thread);
+int db_export__dso(struct db_export *dbe, struct dso *dso,
+ struct machine *machine);
+int db_export__symbol(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso);
+int db_export__sample(struct db_export *dbe, union perf_event *event,
+ struct perf_sample *sample, struct perf_evsel *evsel,
+ struct thread *thread, struct addr_location *al);
+
+#endif
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 5e463c0..1c72a33 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -101,6 +101,7 @@ struct dso {
enum dso_swap_type needs_swap;
enum dso_binary_type symtab_type;
enum dso_binary_type binary_type;
+ u64 db_id;
u8 adjust_symbols:1;
u8 has_build_id:1;
u8 has_srcline:1;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index d46d28e..378e670 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -96,6 +96,7 @@ struct perf_evsel {
int sample_read;
struct perf_evsel *leader;
char *group_name;
+ u64 db_id;
};
union u64_swap {
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 6a6bcc1..f385f23 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -39,6 +39,7 @@ struct machine {
u64 kernel_start;
symbol_filter_t symbol_filter;
pid_t *current_tid;
+ u64 db_id;
};
static inline
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 3f95ea0..45e277b 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -73,6 +73,7 @@ struct symbol {
struct rb_node rb_node;
u64 start;
u64 end;
+ u64 db_id;
u16 namelen;
u8 binding;
bool ignore;
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index a057820..a3ebb21 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -25,6 +25,7 @@ struct thread {
bool dead; /* if set thread has exited */
struct list_head comm_list;
int comm_len;
+ u64 db_id;
void *priv;
struct thread_stack *ts;
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 13/24] perf tools: Extend Python script interface to export data in a database-friendly way
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (11 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 12/24] perf tools: Add facility to export data in database-friendly way Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 14/24] perf tools: Add Python script to export to postgresql Adrian Hunter
` (10 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Use the new db_export facility to export data in a
database-friendly way.
A Python script selects the db_export mode by setting
a global variable 'perf_db_export_mode' to True. The
script then optionally implements functions to receive
table rows. The functions are:
evsel_table
machine_table
thread_table
comm_table
dso_table
symbol_table
sample_table
An example script is provided in a subsequent patch.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
.../util/scripting-engines/trace-event-python.c | 281 ++++++++++++++++++++-
1 file changed, 279 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 56ba07c..400c7d6 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -24,6 +24,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdbool.h>
#include <errno.h>
#include "../../perf.h"
@@ -32,6 +33,9 @@
#include "../util.h"
#include "../event.h"
#include "../thread.h"
+#include "../comm.h"
+#include "../machine.h"
+#include "../db-export.h"
#include "../trace-event.h"
#include "../machine.h"
@@ -52,6 +56,21 @@ static int zero_flag_atom;
static PyObject *main_module, *main_dict;
+struct tables {
+ struct db_export dbe;
+ PyObject *evsel_handler;
+ PyObject *machine_handler;
+ PyObject *thread_handler;
+ PyObject *comm_handler;
+ PyObject *comm_thread_handler;
+ PyObject *dso_handler;
+ PyObject *symbol_handler;
+ PyObject *sample_handler;
+ bool db_export_mode;
+};
+
+static struct tables tables_global;
+
static void handler_call_die(const char *handler_name) NORETURN;
static void handler_call_die(const char *handler_name)
{
@@ -474,6 +493,210 @@ static void python_process_tracepoint(struct perf_sample *sample,
Py_DECREF(t);
}
+static PyObject *tuple_new(unsigned int sz)
+{
+ PyObject *t;
+
+ t = PyTuple_New(sz);
+ if (!t)
+ Py_FatalError("couldn't create Python tuple");
+ return t;
+}
+
+static int tuple_set_u64(PyObject *t, unsigned int pos, u64 val)
+{
+#if BITS_PER_LONG == 64
+ return PyTuple_SetItem(t, pos, PyInt_FromLong(val));
+#endif
+#if BITS_PER_LONG == 32
+ return PyTuple_SetItem(t, pos, PyLong_FromLongLong(val));
+#endif
+}
+
+static int tuple_set_s32(PyObject *t, unsigned int pos, s32 val)
+{
+ return PyTuple_SetItem(t, pos, PyInt_FromLong(val));
+}
+
+static int tuple_set_string(PyObject *t, unsigned int pos, const char *s)
+{
+ return PyTuple_SetItem(t, pos, PyString_FromString(s));
+}
+
+static int python_export_evsel(struct db_export *dbe, struct perf_evsel *evsel)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(2);
+
+ tuple_set_u64(t, 0, evsel->db_id);
+ tuple_set_string(t, 1, perf_evsel__name(evsel));
+
+ call_object(tables->evsel_handler, t, "evsel_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_machine(struct db_export *dbe,
+ struct machine *machine)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(3);
+
+ tuple_set_u64(t, 0, machine->db_id);
+ tuple_set_s32(t, 1, machine->pid);
+ tuple_set_string(t, 2, machine->root_dir ? machine->root_dir : "");
+
+ call_object(tables->machine_handler, t, "machine_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_thread(struct db_export *dbe, struct thread *thread,
+ u64 main_thread_db_id, struct machine *machine)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(5);
+
+ tuple_set_u64(t, 0, thread->db_id);
+ tuple_set_u64(t, 1, machine->db_id);
+ tuple_set_u64(t, 2, main_thread_db_id);
+ tuple_set_s32(t, 3, thread->pid_);
+ tuple_set_s32(t, 4, thread->tid);
+
+ call_object(tables->thread_handler, t, "thread_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_comm(struct db_export *dbe, struct comm *comm)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(2);
+
+ tuple_set_u64(t, 0, comm->db_id);
+ tuple_set_string(t, 1, comm__str(comm));
+
+ call_object(tables->comm_handler, t, "comm_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_comm_thread(struct db_export *dbe, u64 db_id,
+ struct comm *comm, struct thread *thread)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(3);
+
+ tuple_set_u64(t, 0, db_id);
+ tuple_set_u64(t, 1, comm->db_id);
+ tuple_set_u64(t, 2, thread->db_id);
+
+ call_object(tables->comm_thread_handler, t, "comm_thread_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_dso(struct db_export *dbe, struct dso *dso,
+ struct machine *machine)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ char sbuild_id[BUILD_ID_SIZE * 2 + 1];
+ PyObject *t;
+
+ build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id);
+
+ t = tuple_new(5);
+
+ tuple_set_u64(t, 0, dso->db_id);
+ tuple_set_u64(t, 1, machine->db_id);
+ tuple_set_string(t, 2, dso->short_name);
+ tuple_set_string(t, 3, dso->long_name);
+ tuple_set_string(t, 4, sbuild_id);
+
+ call_object(tables->dso_handler, t, "dso_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_symbol(struct db_export *dbe, struct symbol *sym,
+ struct dso *dso)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(6);
+
+ tuple_set_u64(t, 0, sym->db_id);
+ tuple_set_u64(t, 1, dso->db_id);
+ tuple_set_u64(t, 2, sym->start);
+ tuple_set_u64(t, 3, sym->end);
+ tuple_set_s32(t, 4, sym->binding);
+ tuple_set_string(t, 5, sym->name);
+
+ call_object(tables->symbol_handler, t, "symbol_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_sample(struct db_export *dbe,
+ struct export_sample *es)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(19);
+
+ tuple_set_u64(t, 0, es->db_id);
+ tuple_set_u64(t, 1, es->evsel->db_id);
+ tuple_set_u64(t, 2, es->al->machine->db_id);
+ tuple_set_u64(t, 3, es->thread->db_id);
+ tuple_set_u64(t, 4, es->comm_db_id);
+ tuple_set_u64(t, 5, es->dso_db_id);
+ tuple_set_u64(t, 6, es->sym_db_id);
+ tuple_set_u64(t, 7, es->offset);
+ tuple_set_u64(t, 8, es->sample->ip);
+ tuple_set_u64(t, 9, es->sample->time);
+ tuple_set_s32(t, 10, es->sample->cpu);
+ tuple_set_u64(t, 11, es->addr_dso_db_id);
+ tuple_set_u64(t, 12, es->addr_sym_db_id);
+ tuple_set_u64(t, 13, es->addr_offset);
+ tuple_set_u64(t, 14, es->sample->addr);
+ tuple_set_u64(t, 15, es->sample->period);
+ tuple_set_u64(t, 16, es->sample->weight);
+ tuple_set_u64(t, 17, es->sample->transaction);
+ tuple_set_u64(t, 18, es->sample->data_src);
+
+ call_object(tables->sample_handler, t, "sample_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
static void python_process_general_event(struct perf_sample *sample,
struct perf_evsel *evsel,
struct thread *thread,
@@ -550,19 +773,25 @@ exit:
Py_DECREF(t);
}
-static void python_process_event(union perf_event *event __maybe_unused,
+static void python_process_event(union perf_event *event,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct thread *thread,
struct addr_location *al)
{
+ struct tables *tables = &tables_global;
+
switch (evsel->attr.type) {
case PERF_TYPE_TRACEPOINT:
python_process_tracepoint(sample, evsel, thread, al);
break;
/* Reserve for future process_hw/sw/raw APIs */
default:
- python_process_general_event(sample, evsel, thread, al);
+ if (tables->db_export_mode)
+ db_export__sample(&tables->dbe, event, sample, evsel,
+ thread, al);
+ else
+ python_process_general_event(sample, evsel, thread, al);
}
}
@@ -588,11 +817,53 @@ error:
return -1;
}
+#define SET_TABLE_HANDLER_(name, handler_name, table_name) do { \
+ tables->handler_name = get_handler(#table_name); \
+ if (tables->handler_name) \
+ tables->dbe.export_ ## name = python_export_ ## name; \
+} while (0)
+
+#define SET_TABLE_HANDLER(name) \
+ SET_TABLE_HANDLER_(name, name ## _handler, name ## _table)
+
+static void set_table_handlers(struct tables *tables)
+{
+ const char *perf_db_export_mode = "perf_db_export_mode";
+ PyObject *db_export_mode;
+ int ret;
+
+ memset(tables, 0, sizeof(struct tables));
+ if (db_export__init(&tables->dbe))
+ Py_FatalError("failed to initialize export");
+
+ db_export_mode = PyDict_GetItemString(main_dict, perf_db_export_mode);
+ if (!db_export_mode)
+ return;
+
+ ret = PyObject_IsTrue(db_export_mode);
+ if (ret == -1)
+ handler_call_die(perf_db_export_mode);
+ if (!ret)
+ return;
+
+ tables->db_export_mode = true;
+
+ SET_TABLE_HANDLER(evsel);
+ SET_TABLE_HANDLER(machine);
+ SET_TABLE_HANDLER(thread);
+ SET_TABLE_HANDLER(comm);
+ SET_TABLE_HANDLER(comm_thread);
+ SET_TABLE_HANDLER(dso);
+ SET_TABLE_HANDLER(symbol);
+ SET_TABLE_HANDLER(sample);
+}
+
/*
* Start trace script
*/
static int python_start_script(const char *script, int argc, const char **argv)
{
+ struct tables *tables = &tables_global;
const char **command_line;
char buf[PATH_MAX];
int i, err = 0;
@@ -631,6 +902,8 @@ static int python_start_script(const char *script, int argc, const char **argv)
free(command_line);
+ set_table_handlers(tables);
+
return err;
error:
Py_Finalize();
@@ -649,8 +922,12 @@ static int python_flush_script(void)
*/
static int python_stop_script(void)
{
+ struct tables *tables = &tables_global;
+
try_call_object("trace_end", NULL);
+ db_export__exit(&tables->dbe);
+
Py_XDECREF(main_dict);
Py_XDECREF(main_module);
Py_Finalize();
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 14/24] perf tools: Add Python script to export to postgresql
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (12 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 13/24] perf tools: Extend Python script interface to export data in a " Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 15/24] perf tools: Add branch type to db export Adrian Hunter
` (9 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Add a Python script to export to a postgresql database.
The script requires the Python psycopg2 module. The
caller of the script must be able to create postgresql
databases.
The script takes the database name as a parameter. The
database and database tables are created. Data is written
to flat files which are then imported using SQL COPY FROM.
Example:
$ perf record ls
...
$ perf script report export-to-postgresql lsdb
2014-02-14 10:55:38.631431 Creating database...
2014-02-14 10:55:39.291958 Writing to intermediate files...
2014-02-14 10:55:39.350280 Copying to database...
2014-02-14 10:55:39.358536 Removing intermediate files...
2014-02-14 10:55:39.358665 Adding primary keys
2014-02-14 10:55:39.658697 Adding foreign keys
2014-02-14 10:55:39.667412 Done
$ psql lsdb
lsdb=# select * from samples_view;
id | time | cpu | pid | tid | command | event | ip_hex | symbol | sym_offset | dso_short_name | to_ip_hex | to_symbol | to_sym_offset | to_dso_short_name
----+-----------------+-----+-------+-------+---------+--------+------------------+------------------------------+------------+-------------------+-----------+-----------+---------------+-------------------
1 | 592112202825015 | -1 | 17339 | 17339 | :17339 | cycles | ffffffff8104d24a | native_write_msr_safe | 10 | [kernel.kallsyms] | 0 | unknown | 0 | unknown
2 | 592112203258804 | -1 | 17339 | 17339 | :17339 | cycles | ffffffff8104d24a | native_write_msr_safe | 10 | [kernel.kallsyms] | 0 | unknown | 0 | unknown
3 | 592112203988119 | -1 | 17339 | 17339 | :17339 | cycles | ffffffff8104d24a | native_write_msr_safe | 10 | [kernel.kallsyms] | 0 | unknown | 0 | unknown
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
.../scripts/python/bin/export-to-postgresql-record | 8 +
.../scripts/python/bin/export-to-postgresql-report | 24 ++
tools/perf/scripts/python/export-to-postgresql.py | 360 +++++++++++++++++++++
3 files changed, 392 insertions(+)
create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-record
create mode 100644 tools/perf/scripts/python/bin/export-to-postgresql-report
create mode 100644 tools/perf/scripts/python/export-to-postgresql.py
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-record b/tools/perf/scripts/python/bin/export-to-postgresql-record
new file mode 100644
index 0000000..221d66e
--- /dev/null
+++ b/tools/perf/scripts/python/bin/export-to-postgresql-record
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+#
+# export perf data to a postgresql database. Can cover
+# perf ip samples (excluding the tracepoints). No special
+# record requirements, just record what you want to export.
+#
+perf record $@
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/tools/perf/scripts/python/bin/export-to-postgresql-report
new file mode 100644
index 0000000..a8fdd15
--- /dev/null
+++ b/tools/perf/scripts/python/bin/export-to-postgresql-report
@@ -0,0 +1,24 @@
+#!/bin/bash
+# description: export perf data to a postgresql database
+# args: [database name] [columns]
+n_args=0
+for i in "$@"
+do
+ if expr match "$i" "-" > /dev/null ; then
+ break
+ fi
+ n_args=$(( $n_args + 1 ))
+done
+if [ "$n_args" -gt 2 ] ; then
+ echo "usage: export-to-postgresql-report [database name] [columns]"
+ exit
+fi
+if [ "$n_args" -gt 1 ] ; then
+ dbname=$1
+ columns=$2
+ shift 2
+elif [ "$n_args" -gt 0 ] ; then
+ dbname=$1
+ shift
+fi
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
new file mode 100644
index 0000000..d8f6df0
--- /dev/null
+++ b/tools/perf/scripts/python/export-to-postgresql.py
@@ -0,0 +1,360 @@
+# export-to-postgresql.py: export perf data to a postgresql database
+# Copyright (c) 2014, Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+
+import os
+import sys
+import struct
+import datetime
+
+from PySide.QtSql import *
+
+# Need to access PostgreSQL C library directly to use COPY FROM STDIN
+from ctypes import *
+libpq = CDLL("libpq.so.5")
+PQconnectdb = libpq.PQconnectdb
+PQconnectdb.restype = c_void_p
+PQfinish = libpq.PQfinish
+PQstatus = libpq.PQstatus
+PQexec = libpq.PQexec
+PQexec.restype = c_void_p
+PQresultStatus = libpq.PQresultStatus
+PQputCopyData = libpq.PQputCopyData
+PQputCopyData.argtypes = [ c_void_p, c_void_p, c_int ]
+PQputCopyEnd = libpq.PQputCopyEnd
+PQputCopyEnd.argtypes = [ c_void_p, c_void_p ]
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+# These perf imports are not used at present
+#from perf_trace_context import *
+#from Core import *
+
+perf_db_export_mode = True
+
+def usage():
+ print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>]"
+ print >> sys.stderr, "where: columns 'all' or 'branches'"
+ raise Exception("Too few arguments")
+
+if (len(sys.argv) < 2):
+ usage()
+
+dbname = sys.argv[1]
+
+if (len(sys.argv) >= 3):
+ columns = sys.argv[2]
+else:
+ columns = "all"
+
+if columns not in ("all", "branches"):
+ usage()
+
+branches = (columns == "branches")
+
+output_dir_name = os.getcwd() + "/" + dbname + "-perf-data"
+os.mkdir(output_dir_name)
+
+def do_query(q, s):
+ if (q.exec_(s)):
+ return
+ raise Exception("Query failed: " + q.lastError().text())
+
+print datetime.datetime.today(), "Creating database..."
+
+db = QSqlDatabase.addDatabase('QPSQL')
+query = QSqlQuery(db)
+db.setDatabaseName('postgres')
+db.open()
+try:
+ do_query(query, 'CREATE DATABASE ' + dbname)
+except:
+ os.rmdir(output_dir_name)
+ raise
+query.finish()
+query.clear()
+db.close()
+
+db.setDatabaseName(dbname)
+db.open()
+
+query = QSqlQuery(db)
+do_query(query, 'SET client_min_messages TO WARNING')
+
+do_query(query, 'CREATE TABLE selected_events ('
+ 'id bigint NOT NULL,'
+ 'name varchar(80))')
+do_query(query, 'CREATE TABLE machines ('
+ 'id bigint NOT NULL,'
+ 'pid integer,'
+ 'root_dir varchar(4096))')
+do_query(query, 'CREATE TABLE threads ('
+ 'id bigint NOT NULL,'
+ 'machine_id bigint,'
+ 'process_id bigint,'
+ 'pid integer,'
+ 'tid integer)')
+do_query(query, 'CREATE TABLE comms ('
+ 'id bigint NOT NULL,'
+ 'comm varchar(16))')
+do_query(query, 'CREATE TABLE comm_threads ('
+ 'id bigint NOT NULL,'
+ 'comm_id bigint,'
+ 'thread_id bigint)')
+do_query(query, 'CREATE TABLE dsos ('
+ 'id bigint NOT NULL,'
+ 'machine_id bigint,'
+ 'short_name varchar(256),'
+ 'long_name varchar(4096),'
+ 'build_id varchar(64))')
+do_query(query, 'CREATE TABLE symbols ('
+ 'id bigint NOT NULL,'
+ 'dso_id bigint,'
+ 'sym_start bigint,'
+ 'sym_end bigint,'
+ 'binding integer,'
+ 'name varchar(2048))')
+if branches:
+ do_query(query, 'CREATE TABLE samples ('
+ 'id bigint NOT NULL,'
+ 'evsel_id bigint,'
+ 'machine_id bigint,'
+ 'thread_id bigint,'
+ 'comm_id bigint,'
+ 'dso_id bigint,'
+ 'symbol_id bigint,'
+ 'sym_offset bigint,'
+ 'ip bigint,'
+ 'time bigint,'
+ 'cpu integer,'
+ 'to_dso_id bigint,'
+ 'to_symbol_id bigint,'
+ 'to_sym_offset bigint,'
+ 'to_ip bigint)')
+else:
+ do_query(query, 'CREATE TABLE samples ('
+ 'id bigint NOT NULL,'
+ 'evsel_id bigint,'
+ 'machine_id bigint,'
+ 'thread_id bigint,'
+ 'comm_id bigint,'
+ 'dso_id bigint,'
+ 'symbol_id bigint,'
+ 'sym_offset bigint,'
+ 'ip bigint,'
+ 'time bigint,'
+ 'cpu integer,'
+ 'to_dso_id bigint,'
+ 'to_symbol_id bigint,'
+ 'to_sym_offset bigint,'
+ 'to_ip bigint,'
+ 'period bigint,'
+ 'weight bigint,'
+ 'transaction bigint,'
+ 'data_src bigint)')
+
+do_query(query, 'CREATE VIEW samples_view AS '
+ 'SELECT '
+ 'id,'
+ 'time,'
+ 'cpu,'
+ '(SELECT pid FROM threads WHERE id = thread_id) AS pid,'
+ '(SELECT tid FROM threads WHERE id = thread_id) AS tid,'
+ '(SELECT comm FROM comms WHERE id = comm_id) AS command,'
+ '(SELECT name FROM selected_events WHERE id = evsel_id) AS event,'
+ 'to_hex(ip) AS ip_hex,'
+ '(SELECT name FROM symbols WHERE id = symbol_id) AS symbol,'
+ 'sym_offset,'
+ '(SELECT short_name FROM dsos WHERE id = dso_id) AS dso_short_name,'
+ 'to_hex(to_ip) AS to_ip_hex,'
+ '(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,'
+ 'to_sym_offset,'
+ '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name'
+ ' FROM samples')
+
+
+file_header = struct.pack("!11sii", "PGCOPY\n\377\r\n\0", 0, 0)
+file_trailer = "\377\377"
+
+def open_output_file(file_name):
+ path_name = output_dir_name + "/" + file_name
+ file = open(path_name, "w+")
+ file.write(file_header)
+ return file
+
+def close_output_file(file):
+ file.write(file_trailer)
+ file.close()
+
+def copy_output_file_direct(file, table_name):
+ close_output_file(file)
+ sql = "COPY " + table_name + " FROM '" + file.name + "' (FORMAT 'binary')"
+ do_query(query, sql)
+
+# Use COPY FROM STDIN because security may prevent postgres from accessing the files directly
+def copy_output_file(file, table_name):
+ conn = PQconnectdb("dbname = " + dbname)
+ if (PQstatus(conn)):
+ raise Exception("COPY FROM STDIN PQconnectdb failed")
+ file.write(file_trailer)
+ file.seek(0)
+ sql = "COPY " + table_name + " FROM STDIN (FORMAT 'binary')"
+ res = PQexec(conn, sql)
+ if (PQresultStatus(res) != 4):
+ raise Exception("COPY FROM STDIN PQexec failed")
+ data = file.read(65536)
+ while (len(data)):
+ ret = PQputCopyData(conn, data, len(data))
+ if (ret != 1):
+ raise Exception("COPY FROM STDIN PQputCopyData failed, error " + str(ret))
+ data = file.read(65536)
+ ret = PQputCopyEnd(conn, None)
+ if (ret != 1):
+ raise Exception("COPY FROM STDIN PQputCopyEnd failed, error " + str(ret))
+ PQfinish(conn)
+
+def remove_output_file(file):
+ name = file.name
+ file.close()
+ os.unlink(name)
+
+evsel_file = open_output_file("evsel_table.bin")
+machine_file = open_output_file("machine_table.bin")
+thread_file = open_output_file("thread_table.bin")
+comm_file = open_output_file("comm_table.bin")
+comm_thread_file = open_output_file("comm_thread_table.bin")
+dso_file = open_output_file("dso_table.bin")
+symbol_file = open_output_file("symbol_table.bin")
+sample_file = open_output_file("sample_table.bin")
+
+def trace_begin():
+ print datetime.datetime.today(), "Writing to intermediate files..."
+ # id == 0 means unknown. It is easier to create records for them than replace the zeroes with NULLs
+ evsel_table(0, "unknown")
+ machine_table(0, 0, "unknown")
+ thread_table(0, 0, 0, -1, -1)
+ comm_table(0, "unknown")
+ dso_table(0, 0, "unknown", "unknown", "")
+ symbol_table(0, 0, 0, 0, 0, "unknown")
+
+unhandled_count = 0
+
+def trace_end():
+ print datetime.datetime.today(), "Copying to database..."
+ copy_output_file(evsel_file, "selected_events")
+ copy_output_file(machine_file, "machines")
+ copy_output_file(thread_file, "threads")
+ copy_output_file(comm_file, "comms")
+ copy_output_file(comm_thread_file, "comm_threads")
+ copy_output_file(dso_file, "dsos")
+ copy_output_file(symbol_file, "symbols")
+ copy_output_file(sample_file, "samples")
+
+ print datetime.datetime.today(), "Removing intermediate files..."
+ remove_output_file(evsel_file)
+ remove_output_file(machine_file)
+ remove_output_file(thread_file)
+ remove_output_file(comm_file)
+ remove_output_file(comm_thread_file)
+ remove_output_file(dso_file)
+ remove_output_file(symbol_file)
+ remove_output_file(sample_file)
+ os.rmdir(output_dir_name)
+ print datetime.datetime.today(), "Adding primary keys"
+ do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE machines ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE threads ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE comms ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)')
+
+ print datetime.datetime.today(), "Adding foreign keys"
+ do_query(query, 'ALTER TABLE threads '
+ 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
+ 'ADD CONSTRAINT processfk FOREIGN KEY (process_id) REFERENCES threads (id)')
+ do_query(query, 'ALTER TABLE comm_threads '
+ 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
+ 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id)')
+ do_query(query, 'ALTER TABLE dsos '
+ 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id)')
+ do_query(query, 'ALTER TABLE symbols '
+ 'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos (id)')
+ do_query(query, 'ALTER TABLE samples '
+ 'ADD CONSTRAINT evselfk FOREIGN KEY (evsel_id) REFERENCES selected_events (id),'
+ 'ADD CONSTRAINT machinefk FOREIGN KEY (machine_id) REFERENCES machines (id),'
+ 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),'
+ 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
+ 'ADD CONSTRAINT dsofk FOREIGN KEY (dso_id) REFERENCES dsos (id),'
+ 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),'
+ 'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),'
+ 'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)')
+
+ if (unhandled_count):
+ print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events"
+ print datetime.datetime.today(), "Done"
+
+def trace_unhandled(event_name, context, event_fields_dict):
+ global unhandled_count
+ unhandled_count += 1
+
+def sched__sched_switch(*x):
+ pass
+
+def evsel_table(evsel_id, evsel_name, *x):
+ n = len(evsel_name)
+ fmt = "!hiqi" + str(n) + "s"
+ value = struct.pack(fmt, 2, 8, evsel_id, n, evsel_name)
+ evsel_file.write(value)
+
+def machine_table(machine_id, pid, root_dir, *x):
+ n = len(root_dir)
+ fmt = "!hiqiii" + str(n) + "s"
+ value = struct.pack(fmt, 3, 8, machine_id, 4, pid, n, root_dir)
+ machine_file.write(value)
+
+def thread_table(thread_id, machine_id, process_id, pid, tid, *x):
+ value = struct.pack("!hiqiqiqiiii", 5, 8, thread_id, 8, machine_id, 8, process_id, 4, pid, 4, tid)
+ thread_file.write(value)
+
+def comm_table(comm_id, comm_str, *x):
+ n = len(comm_str)
+ fmt = "!hiqi" + str(n) + "s"
+ value = struct.pack(fmt, 2, 8, comm_id, n, comm_str)
+ comm_file.write(value)
+
+def comm_thread_table(comm_thread_id, comm_id, thread_id, *x):
+ fmt = "!hiqiqiq"
+ value = struct.pack(fmt, 3, 8, comm_thread_id, 8, comm_id, 8, thread_id)
+ comm_thread_file.write(value)
+
+def dso_table(dso_id, machine_id, short_name, long_name, build_id, *x):
+ n1 = len(short_name)
+ n2 = len(long_name)
+ n3 = len(build_id)
+ fmt = "!hiqiqi" + str(n1) + "si" + str(n2) + "si" + str(n3) + "s"
+ value = struct.pack(fmt, 5, 8, dso_id, 8, machine_id, n1, short_name, n2, long_name, n3, build_id)
+ dso_file.write(value)
+
+def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x):
+ n = len(symbol_name)
+ fmt = "!hiqiqiqiqiii" + str(n) + "s"
+ value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name)
+ symbol_file.write(value)
+
+def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, *x):
+ if branches:
+ value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiq", 15, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip)
+ else:
+ value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiq", 19, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src)
+ sample_file.write(value)
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 15/24] perf tools: Add branch type to db export
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (13 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 14/24] perf tools: Add Python script to export to postgresql Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 16/24] perf tools: Add branch_type and in_tx to Python export Adrian Hunter
` (8 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Add the ability to export branch types through the
database export facility.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/db-export.c | 48 +++++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/db-export.h | 6 ++++++
2 files changed, 54 insertions(+)
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index 53d0e75..c5b6e02 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -205,6 +205,15 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,
return 0;
}
+int db_export__branch_type(struct db_export *dbe, u32 branch_type,
+ const char *name)
+{
+ if (dbe->export_branch_type)
+ return dbe->export_branch_type(dbe, branch_type, name);
+
+ return 0;
+}
+
int db_export__sample(struct db_export *dbe, union perf_event *event,
struct perf_sample *sample, struct perf_evsel *evsel,
struct thread *thread, struct addr_location *al)
@@ -266,3 +275,42 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
return 0;
}
+
+static struct {
+ u32 branch_type;
+ const char *name;
+} branch_types[] = {
+ {0, "no branch"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_CALL, "call"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_RETURN, "return"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_CONDITIONAL, "conditional jump"},
+ {PERF_FLAG_BRANCH, "unconditional jump"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_CALL | PERF_FLAG_INTERRUPT,
+ "software interrupt"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_RETURN | PERF_FLAG_INTERRUPT,
+ "return from interrupt"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_CALL | PERF_FLAG_SYSCALLRET,
+ "system call"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_RETURN | PERF_FLAG_SYSCALLRET,
+ "return from system call"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_ASYNC, "asynchronous branch"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_CALL | PERF_FLAG_ASYNC |
+ PERF_FLAG_INTERRUPT, "hardware interrupt"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_TX_ABORT, "transaction abort"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_TRACE_BEGIN, "trace begin"},
+ {PERF_FLAG_BRANCH | PERF_FLAG_TRACE_END, "trace end"},
+ {0, NULL}
+};
+
+int db_export__branch_types(struct db_export *dbe)
+{
+ int i, err = 0;
+
+ for (i = 0; branch_types[i].name ; i++) {
+ err = db_export__branch_type(dbe, branch_types[i].branch_type,
+ branch_types[i].name);
+ if (err)
+ break;
+ }
+ return err;
+}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
index b3643e8..e4baa45 100644
--- a/tools/perf/util/db-export.h
+++ b/tools/perf/util/db-export.h
@@ -54,6 +54,8 @@ struct db_export {
struct machine *machine);
int (*export_symbol)(struct db_export *dbe, struct symbol *sym,
struct dso *dso);
+ int (*export_branch_type)(struct db_export *dbe, u32 branch_type,
+ const char *name);
int (*export_sample)(struct db_export *dbe, struct export_sample *es);
u64 evsel_last_db_id;
u64 machine_last_db_id;
@@ -79,8 +81,12 @@ int db_export__dso(struct db_export *dbe, struct dso *dso,
struct machine *machine);
int db_export__symbol(struct db_export *dbe, struct symbol *sym,
struct dso *dso);
+int db_export__branch_type(struct db_export *dbe, u32 branch_type,
+ const char *name);
int db_export__sample(struct db_export *dbe, union perf_event *event,
struct perf_sample *sample, struct perf_evsel *evsel,
struct thread *thread, struct addr_location *al);
+int db_export__branch_types(struct db_export *dbe);
+
#endif
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 16/24] perf tools: Add branch_type and in_tx to Python export
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (14 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 15/24] perf tools: Add branch type to db export Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 17/24] perf tools: Enhance the thread stack to output call/return data Adrian Hunter
` (7 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Add branch_type and in_tx to Python db export and
the export-to-postgresql.py script.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/scripts/python/export-to-postgresql.py | 32 ++++++++++++++++++----
.../util/scripting-engines/trace-event-python.c | 30 +++++++++++++++++++-
2 files changed, 55 insertions(+), 7 deletions(-)
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
index d8f6df0..bb79aec 100644
--- a/tools/perf/scripts/python/export-to-postgresql.py
+++ b/tools/perf/scripts/python/export-to-postgresql.py
@@ -123,6 +123,10 @@ do_query(query, 'CREATE TABLE symbols ('
'sym_end bigint,'
'binding integer,'
'name varchar(2048))')
+do_query(query, 'CREATE TABLE branch_types ('
+ 'id integer NOT NULL,'
+ 'name varchar(80))')
+
if branches:
do_query(query, 'CREATE TABLE samples ('
'id bigint NOT NULL,'
@@ -139,7 +143,9 @@ if branches:
'to_dso_id bigint,'
'to_symbol_id bigint,'
'to_sym_offset bigint,'
- 'to_ip bigint)')
+ 'to_ip bigint,'
+ 'branch_type integer,'
+ 'in_tx boolean)')
else:
do_query(query, 'CREATE TABLE samples ('
'id bigint NOT NULL,'
@@ -160,7 +166,9 @@ else:
'period bigint,'
'weight bigint,'
'transaction bigint,'
- 'data_src bigint)')
+ 'data_src bigint,'
+ 'branch_type integer,'
+ 'in_tx boolean)')
do_query(query, 'CREATE VIEW samples_view AS '
'SELECT '
@@ -178,7 +186,9 @@ do_query(query, 'CREATE VIEW samples_view AS '
'to_hex(to_ip) AS to_ip_hex,'
'(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,'
'to_sym_offset,'
- '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name'
+ '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,'
+ '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,'
+ 'in_tx'
' FROM samples')
@@ -234,6 +244,7 @@ comm_file = open_output_file("comm_table.bin")
comm_thread_file = open_output_file("comm_thread_table.bin")
dso_file = open_output_file("dso_table.bin")
symbol_file = open_output_file("symbol_table.bin")
+branch_type_file = open_output_file("branch_type_table.bin")
sample_file = open_output_file("sample_table.bin")
def trace_begin():
@@ -257,6 +268,7 @@ def trace_end():
copy_output_file(comm_thread_file, "comm_threads")
copy_output_file(dso_file, "dsos")
copy_output_file(symbol_file, "symbols")
+ copy_output_file(branch_type_file, "branch_types")
copy_output_file(sample_file, "samples")
print datetime.datetime.today(), "Removing intermediate files..."
@@ -267,6 +279,7 @@ def trace_end():
remove_output_file(comm_thread_file)
remove_output_file(dso_file)
remove_output_file(symbol_file)
+ remove_output_file(branch_type_file)
remove_output_file(sample_file)
os.rmdir(output_dir_name)
print datetime.datetime.today(), "Adding primary keys"
@@ -277,6 +290,7 @@ def trace_end():
do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)')
print datetime.datetime.today(), "Adding foreign keys"
@@ -352,9 +366,15 @@ def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x
value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name)
symbol_file.write(value)
-def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, *x):
+def branch_type_table(branch_type, name, *x):
+ n = len(name)
+ fmt = "!hiii" + str(n) + "s"
+ value = struct.pack(fmt, 2, 4, branch_type, n, name)
+ branch_type_file.write(value)
+
+def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, *x):
if branches:
- value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiq", 15, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip)
+ value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiB", 17, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx)
else:
- value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiq", 19, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src)
+ value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx)
sample_file.write(value)
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 400c7d6..881d595 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -65,6 +65,7 @@ struct tables {
PyObject *comm_thread_handler;
PyObject *dso_handler;
PyObject *symbol_handler;
+ PyObject *branch_type_handler;
PyObject *sample_handler;
bool db_export_mode;
};
@@ -662,13 +663,31 @@ static int python_export_symbol(struct db_export *dbe, struct symbol *sym,
return 0;
}
+static int python_export_branch_type(struct db_export *dbe, u32 branch_type,
+ const char *name)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+
+ t = tuple_new(2);
+
+ tuple_set_s32(t, 0, branch_type);
+ tuple_set_string(t, 1, name);
+
+ call_object(tables->branch_type_handler, t, "branch_type_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
static int python_export_sample(struct db_export *dbe,
struct export_sample *es)
{
struct tables *tables = container_of(dbe, struct tables, dbe);
PyObject *t;
- t = tuple_new(19);
+ t = tuple_new(21);
tuple_set_u64(t, 0, es->db_id);
tuple_set_u64(t, 1, es->evsel->db_id);
@@ -689,6 +708,8 @@ static int python_export_sample(struct db_export *dbe,
tuple_set_u64(t, 16, es->sample->weight);
tuple_set_u64(t, 17, es->sample->transaction);
tuple_set_u64(t, 18, es->sample->data_src);
+ tuple_set_s32(t, 19, es->sample->flags & PERF_BRANCH_MASK);
+ tuple_set_s32(t, 20, !!(es->sample->flags & PERF_FLAG_IN_TX));
call_object(tables->sample_handler, t, "sample_table");
@@ -855,6 +876,7 @@ static void set_table_handlers(struct tables *tables)
SET_TABLE_HANDLER(comm_thread);
SET_TABLE_HANDLER(dso);
SET_TABLE_HANDLER(symbol);
+ SET_TABLE_HANDLER(branch_type);
SET_TABLE_HANDLER(sample);
}
@@ -904,6 +926,12 @@ static int python_start_script(const char *script, int argc, const char **argv)
set_table_handlers(tables);
+ if (tables->db_export_mode) {
+ err = db_export__branch_types(&tables->dbe);
+ if (err)
+ goto error;
+ }
+
return err;
error:
Py_Finalize();
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 17/24] perf tools: Enhance the thread stack to output call/return data
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (15 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 16/24] perf tools: Add branch_type and in_tx to Python export Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 18/24] perf tools: Add call information to the database export API Adrian Hunter
` (6 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Enhance the thread stack to output detailed information
about paired calls and returns.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/thread-stack.c | 547 ++++++++++++++++++++++++++++++++++++++++-
tools/perf/util/thread-stack.h | 47 ++++
2 files changed, 590 insertions(+), 4 deletions(-)
diff --git a/tools/perf/util/thread-stack.c b/tools/perf/util/thread-stack.c
index c1ca2a9..444d132 100644
--- a/tools/perf/util/thread-stack.c
+++ b/tools/perf/util/thread-stack.c
@@ -13,15 +13,48 @@
*
*/
+#include <linux/rbtree.h>
+#include <linux/list.h>
#include "thread.h"
#include "event.h"
+#include "machine.h"
#include "util.h"
+#include "debug.h"
+#include "symbol.h"
+#include "comm.h"
#include "thread-stack.h"
-#define STACK_GROWTH 4096
+#define CALL_PATH_BLOCK_SHIFT 8
+#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT)
+#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1)
+
+struct call_path_block {
+ struct call_path cp[CALL_PATH_BLOCK_SIZE];
+ struct list_head node;
+};
+
+struct call_path_root {
+ struct call_path call_path;
+ struct list_head blocks;
+ size_t next;
+ size_t sz;
+};
+
+struct call_return_processor {
+ struct call_path_root *cpr;
+ int (*process)(struct call_return *cr, void *data);
+ void *data;
+};
+
+#define STACK_GROWTH 2048
struct thread_stack_entry {
u64 ret_addr;
+ u64 timestamp;
+ u64 ref;
+ u64 branch_count;
+ struct call_path *cp;
+ bool no_call;
};
struct thread_stack {
@@ -29,6 +62,11 @@ struct thread_stack {
size_t cnt;
size_t sz;
u64 trace_nr;
+ u64 branch_count;
+ u64 kernel_start;
+ u64 last_time;
+ struct call_return_processor *crp;
+ struct comm *comm;
};
static void thread_stack__grow(struct thread_stack *ts)
@@ -45,7 +83,8 @@ static void thread_stack__grow(struct thread_stack *ts)
}
}
-static struct thread_stack *thread_stack__new(void)
+static struct thread_stack *thread_stack__new(struct thread *thread,
+ struct call_return_processor *crp)
{
struct thread_stack *ts;
@@ -59,6 +98,12 @@ static struct thread_stack *thread_stack__new(void)
return NULL;
}
+ if (thread->mg && thread->mg->machine)
+ ts->kernel_start = machine__kernel_start(thread->mg->machine);
+ else
+ ts->kernel_start = 1ULL << 63;
+ ts->crp = crp;
+
return ts;
}
@@ -92,6 +137,64 @@ static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr)
}
}
+static bool thread_stack__in_kernel(struct thread_stack *ts)
+{
+ if (!ts->cnt)
+ return false;
+
+ return ts->stack[ts->cnt - 1].cp->in_kernel;
+}
+
+static int thread_stack__call_return(struct thread *thread,
+ struct thread_stack *ts, size_t idx,
+ u64 timestamp, u64 ref, bool no_return)
+{
+ struct call_return_processor *crp = ts->crp;
+ struct thread_stack_entry *tse;
+ struct call_return cr = {
+ .thread = thread,
+ .comm = ts->comm,
+ .db_id = 0,
+ };
+
+ tse = &ts->stack[idx];
+ cr.cp = tse->cp;
+ cr.call_time = tse->timestamp;
+ cr.return_time = timestamp;
+ cr.branch_count = ts->branch_count - tse->branch_count;
+ cr.call_ref = tse->ref;
+ cr.return_ref = ref;
+ if (tse->no_call)
+ cr.flags |= CALL_RETURN_NO_CALL;
+ if (no_return)
+ cr.flags |= CALL_RETURN_NO_RETURN;
+
+ return crp->process(&cr, crp->data);
+}
+
+static int thread_stack__flush(struct thread *thread, struct thread_stack *ts)
+{
+ struct call_return_processor *crp = ts->crp;
+ int err;
+
+ if (!crp) {
+ ts->cnt = 0;
+ return 0;
+ }
+
+ while (ts->cnt) {
+ err = thread_stack__call_return(thread, ts, --ts->cnt,
+ ts->last_time, 0, true);
+ if (err) {
+ pr_err("Error flushing thread stack!\n");
+ ts->cnt = 0;
+ return err;
+ }
+ }
+
+ return 0;
+}
+
void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
u64 to_ip, u16 insn_len, u64 trace_nr)
{
@@ -99,17 +202,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
return;
if (!thread->ts) {
- thread->ts = thread_stack__new();
+ thread->ts = thread_stack__new(thread, NULL);
if (!thread->ts)
return;
thread->ts->trace_nr = trace_nr;
}
if (trace_nr != thread->ts->trace_nr) {
+ if (thread->ts->trace_nr)
+ thread_stack__flush(thread, thread->ts);
thread->ts->trace_nr = trace_nr;
- thread->ts->cnt = 0;
}
+ /* Stop here if thread_stack__process() is in use */
+ if (thread->ts->crp)
+ return;
+
if (flags & PERF_FLAG_CALL) {
u64 ret_addr;
@@ -126,9 +234,22 @@ void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
}
}
+void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr)
+{
+ if (!thread || !thread->ts)
+ return;
+
+ if (trace_nr != thread->ts->trace_nr) {
+ if (thread->ts->trace_nr)
+ thread_stack__flush(thread, thread->ts);
+ thread->ts->trace_nr = trace_nr;
+ }
+}
+
void thread_stack__free(struct thread *thread)
{
if (thread->ts) {
+ thread_stack__flush(thread, thread->ts);
zfree(&thread->ts->stack);
zfree(&thread->ts);
}
@@ -149,3 +270,421 @@ void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
for (i = 1; i < chain->nr; i++)
chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr;
}
+
+static void call_path__init(struct call_path *cp, struct call_path *parent,
+ struct symbol *sym, u64 ip, bool in_kernel)
+{
+ cp->parent = parent;
+ cp->sym = sym;
+ cp->ip = sym ? 0 : ip;
+ cp->db_id = 0;
+ cp->in_kernel = in_kernel;
+ RB_CLEAR_NODE(&cp->rb_node);
+ cp->children = RB_ROOT;
+}
+
+static struct call_path_root *call_path_root__new(void)
+{
+ struct call_path_root *cpr;
+
+ cpr = zalloc(sizeof(struct call_path_root));
+ if (!cpr)
+ return NULL;
+ call_path__init(&cpr->call_path, NULL, NULL, 0, false);
+ INIT_LIST_HEAD(&cpr->blocks);
+ return cpr;
+}
+
+static void call_path_root__free(struct call_path_root *cpr)
+{
+ struct call_path_block *pos, *n;
+
+ list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
+ list_del(&pos->node);
+ free(pos);
+ }
+ free(cpr);
+}
+
+static struct call_path *call_path__new(struct call_path_root *cpr,
+ struct call_path *parent,
+ struct symbol *sym, u64 ip,
+ bool in_kernel)
+{
+ struct call_path_block *cpb;
+ struct call_path *cp;
+ size_t n;
+
+ if (cpr->next < cpr->sz) {
+ cpb = list_last_entry(&cpr->blocks, struct call_path_block,
+ node);
+ } else {
+ cpb = zalloc(sizeof(struct call_path_block));
+ if (!cpb)
+ return NULL;
+ list_add_tail(&cpb->node, &cpr->blocks);
+ cpr->sz += CALL_PATH_BLOCK_SIZE;
+ }
+
+ n = cpr->next++ & CALL_PATH_BLOCK_MASK;
+ cp = &cpb->cp[n];
+
+ call_path__init(cp, parent, sym, ip, in_kernel);
+
+ return cp;
+}
+
+static struct call_path *call_path__findnew(struct call_path_root *cpr,
+ struct call_path *parent,
+ struct symbol *sym, u64 ip, u64 ks)
+{
+ struct rb_node **p;
+ struct rb_node *node_parent = NULL;
+ struct call_path *cp;
+ bool in_kernel = ip >= ks;
+
+ if (sym)
+ ip = 0;
+
+ if (!parent)
+ return call_path__new(cpr, parent, sym, ip, in_kernel);
+
+ p = &parent->children.rb_node;
+ while (*p != NULL) {
+ node_parent = *p;
+ cp = rb_entry(node_parent, struct call_path, rb_node);
+
+ if (cp->sym == sym && cp->ip == ip)
+ return cp;
+
+ if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+
+ cp = call_path__new(cpr, parent, sym, ip, in_kernel);
+ if (!cp)
+ return NULL;
+
+ rb_link_node(&cp->rb_node, node_parent, p);
+ rb_insert_color(&cp->rb_node, &parent->children);
+
+ return cp;
+}
+
+struct call_return_processor *
+call_return_processor__new(int (*process)(struct call_return *cr, void *data),
+ void *data)
+{
+ struct call_return_processor *crp;
+
+ crp = zalloc(sizeof(struct call_return_processor));
+ if (!crp)
+ return NULL;
+ crp->cpr = call_path_root__new();
+ if (!crp->cpr)
+ goto out_free;
+ crp->process = process;
+ crp->data = data;
+ return crp;
+
+out_free:
+ free(crp);
+ return NULL;
+}
+
+void call_return_processor__free(struct call_return_processor *crp)
+{
+ if (crp) {
+ call_path_root__free(crp->cpr);
+ free(crp);
+ }
+}
+
+static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr,
+ u64 timestamp, u64 ref, struct call_path *cp,
+ bool no_call)
+{
+ struct thread_stack_entry *tse;
+
+ if (ts->cnt == ts->sz) {
+ thread_stack__grow(ts);
+ if (ts->cnt == ts->sz)
+ return -ENOMEM;
+ }
+
+ tse = &ts->stack[ts->cnt++];
+ tse->ret_addr = ret_addr;
+ tse->timestamp = timestamp;
+ tse->ref = ref;
+ tse->branch_count = ts->branch_count;
+ tse->cp = cp;
+ tse->no_call = no_call;
+
+ return 0;
+}
+
+static int thread_stack__pop_cp(struct thread *thread, struct thread_stack *ts,
+ u64 ret_addr, u64 timestamp, u64 ref,
+ struct symbol *sym)
+{
+ int err;
+
+ if (!ts->cnt)
+ return 1;
+
+ if (ts->cnt == 1) {
+ struct thread_stack_entry *tse = &ts->stack[0];
+
+ if (tse->cp->sym == sym)
+ return thread_stack__call_return(thread, ts, --ts->cnt,
+ timestamp, ref, false);
+ }
+
+ if (ts->stack[ts->cnt - 1].ret_addr == ret_addr) {
+ return thread_stack__call_return(thread, ts, --ts->cnt,
+ timestamp, ref, false);
+ } else {
+ size_t i = ts->cnt - 1;
+
+ while (i--) {
+ if (ts->stack[i].ret_addr != ret_addr)
+ continue;
+ i += 1;
+ while (ts->cnt > i) {
+ err = thread_stack__call_return(thread, ts,
+ --ts->cnt,
+ timestamp, ref,
+ true);
+ if (err)
+ return err;
+ }
+ return thread_stack__call_return(thread, ts, --ts->cnt,
+ timestamp, ref, false);
+ }
+ }
+
+ return 1;
+}
+
+static int thread_stack__bottom(struct thread *thread, struct thread_stack *ts,
+ struct perf_sample *sample,
+ struct addr_location *from_al,
+ struct addr_location *to_al, u64 ref)
+{
+ struct call_path_root *cpr = ts->crp->cpr;
+ struct call_path *cp;
+ struct symbol *sym;
+ u64 ip;
+
+ if (sample->ip) {
+ ip = sample->ip;
+ sym = from_al->sym;
+ } else if (sample->addr) {
+ ip = sample->addr;
+ sym = to_al->sym;
+ } else {
+ return 0;
+ }
+
+ cp = call_path__findnew(cpr, &cpr->call_path, sym, ip,
+ ts->kernel_start);
+ if (!cp)
+ return -ENOMEM;
+
+ return thread_stack__push_cp(thread->ts, ip, sample->time, ref, cp,
+ true);
+}
+
+static int thread_stack__no_call_return(struct thread *thread,
+ struct thread_stack *ts,
+ struct perf_sample *sample,
+ struct addr_location *from_al,
+ struct addr_location *to_al, u64 ref)
+{
+ struct call_path_root *cpr = ts->crp->cpr;
+ struct call_path *cp, *parent;
+ u64 ks = ts->kernel_start;
+ int err;
+
+ if (sample->ip >= ks && sample->addr < ks) {
+ /* Return to userspace, so pop all kernel addresses */
+ while (thread_stack__in_kernel(ts)) {
+ err = thread_stack__call_return(thread, ts, --ts->cnt,
+ sample->time, ref,
+ true);
+ if (err)
+ return err;
+ }
+
+ /* If the stack is empty, push the userspace address */
+ if (!ts->cnt) {
+ cp = call_path__findnew(cpr, &cpr->call_path,
+ to_al->sym, sample->addr,
+ ts->kernel_start);
+ if (!cp)
+ return -ENOMEM;
+ return thread_stack__push_cp(ts, 0, sample->time, ref,
+ cp, true);
+ }
+ } else if (thread_stack__in_kernel(ts) && sample->ip < ks) {
+ /* Return to userspace, so pop all kernel addresses */
+ while (thread_stack__in_kernel(ts)) {
+ err = thread_stack__call_return(thread, ts, --ts->cnt,
+ sample->time, ref,
+ true);
+ if (err)
+ return err;
+ }
+ }
+
+ if (ts->cnt)
+ parent = ts->stack[ts->cnt - 1].cp;
+ else
+ parent = &cpr->call_path;
+
+ /* This 'return' had no 'call', so push and pop top of stack */
+ cp = call_path__findnew(cpr, parent, from_al->sym, sample->ip,
+ ts->kernel_start);
+ if (!cp)
+ return -ENOMEM;
+
+ err = thread_stack__push_cp(ts, sample->addr, sample->time, ref, cp,
+ true);
+ if (err)
+ return err;
+
+ return thread_stack__pop_cp(thread, ts, sample->addr, sample->time, ref,
+ to_al->sym);
+}
+
+static int thread_stack__trace_begin(struct thread *thread,
+ struct thread_stack *ts, u64 timestamp,
+ u64 ref)
+{
+ struct thread_stack_entry *tse;
+ int err;
+
+ if (!ts->cnt)
+ return 0;
+
+ /* Pop trace end */
+ tse = &ts->stack[ts->cnt - 1];
+ if (tse->cp->sym == NULL && tse->cp->ip == 0) {
+ err = thread_stack__call_return(thread, ts, --ts->cnt,
+ timestamp, ref, false);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int thread_stack__trace_end(struct thread_stack *ts,
+ struct perf_sample *sample, u64 ref)
+{
+ struct call_path_root *cpr = ts->crp->cpr;
+ struct call_path *cp;
+ u64 ret_addr;
+
+ /* No point having 'trace end' on the bottom of the stack */
+ if (!ts->cnt || (ts->cnt == 1 && ts->stack[0].ref == ref))
+ return 0;
+
+ cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp, NULL, 0,
+ ts->kernel_start);
+ if (!cp)
+ return -ENOMEM;
+
+ ret_addr = sample->ip + sample->insn_len;
+
+ return thread_stack__push_cp(ts, ret_addr, sample->time, ref, cp,
+ false);
+}
+
+int thread_stack__process(struct thread *thread, struct comm *comm,
+ struct perf_sample *sample,
+ struct addr_location *from_al,
+ struct addr_location *to_al, u64 ref,
+ struct call_return_processor *crp)
+{
+ struct thread_stack *ts = thread->ts;
+ int err = 0;
+
+ if (ts) {
+ if (!ts->crp) {
+ /* Supersede thread_stack__event() */
+ thread_stack__free(thread);
+ thread->ts = thread_stack__new(thread, crp);
+ if (!thread->ts)
+ return -ENOMEM;
+ ts = thread->ts;
+ ts->comm = comm;
+ }
+ } else {
+ thread->ts = thread_stack__new(thread, crp);
+ if (!thread->ts)
+ return -ENOMEM;
+ ts = thread->ts;
+ ts->comm = comm;
+ }
+
+ /* Flush stack on exec */
+ if (ts->comm != comm && thread->pid_ == thread->tid) {
+ err = thread_stack__flush(thread, ts);
+ if (err)
+ return err;
+ ts->comm = comm;
+ }
+
+ /* If the stack is empty, put the current symbol on the stack */
+ if (!ts->cnt) {
+ err = thread_stack__bottom(thread, ts, sample, from_al, to_al,
+ ref);
+ if (err)
+ return err;
+ }
+
+ ts->branch_count += 1;
+ ts->last_time = sample->time;
+
+ if (sample->flags & PERF_FLAG_CALL) {
+ struct call_path_root *cpr = ts->crp->cpr;
+ struct call_path *cp;
+ u64 ret_addr;
+
+ if (!sample->ip || !sample->addr)
+ return 0;
+
+ ret_addr = sample->ip + sample->insn_len;
+ if (ret_addr == sample->addr)
+ return 0; /* Zero-length calls are excluded */
+
+ cp = call_path__findnew(cpr, ts->stack[ts->cnt - 1].cp,
+ to_al->sym, sample->addr,
+ ts->kernel_start);
+ if (!cp)
+ return -ENOMEM;
+ err = thread_stack__push_cp(ts, ret_addr, sample->time, ref,
+ cp, false);
+ } else if (sample->flags & PERF_FLAG_RETURN) {
+ if (!sample->ip || !sample->addr)
+ return 0;
+
+ err = thread_stack__pop_cp(thread, ts, sample->addr,
+ sample->time, ref, from_al->sym);
+ if (err) {
+ if (err < 0)
+ return err;
+ err = thread_stack__no_call_return(thread, ts, sample,
+ from_al, to_al, ref);
+ }
+ } else if (sample->flags & PERF_FLAG_TRACE_BEGIN) {
+ err = thread_stack__trace_begin(thread, ts, sample->time, ref);
+ } else if (sample->flags & PERF_FLAG_TRACE_END) {
+ err = thread_stack__trace_end(ts, sample, ref);
+ }
+
+ return err;
+}
diff --git a/tools/perf/util/thread-stack.h b/tools/perf/util/thread-stack.h
index c0ba4cf..5bca14e 100644
--- a/tools/perf/util/thread-stack.h
+++ b/tools/perf/util/thread-stack.h
@@ -19,14 +19,61 @@
#include <sys/types.h>
#include <linux/types.h>
+#include <linux/rbtree.h>
struct thread;
+struct comm;
struct ip_callchain;
+struct symbol;
+struct dso;
+struct call_return_processor;
+struct comm;
+struct perf_sample;
+struct addr_location;
+
+enum {
+ CALL_RETURN_NO_CALL = 1 << 0,
+ CALL_RETURN_NO_RETURN = 1 << 1,
+};
+
+struct call_return {
+ struct thread *thread;
+ struct comm *comm;
+ struct call_path *cp;
+ u64 call_time;
+ u64 return_time;
+ u64 branch_count;
+ u64 call_ref;
+ u64 return_ref;
+ u64 db_id;
+ u32 flags;
+};
+
+struct call_path {
+ struct call_path *parent;
+ struct symbol *sym;
+ u64 ip; /* Only if sym is null */
+ u64 db_id;
+ bool in_kernel;
+ struct rb_node rb_node;
+ struct rb_root children;
+};
void thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,
u64 to_ip, u16 insn_len, u64 trace_nr);
+void thread_stack__set_trace_nr(struct thread *thread, u64 trace_nr);
void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,
size_t sz, u64 ip);
void thread_stack__free(struct thread *thread);
+struct call_return_processor *
+call_return_processor__new(int (*process)(struct call_return *cr, void *data),
+ void *data);
+void call_return_processor__free(struct call_return_processor *crp);
+int thread_stack__process(struct thread *thread, struct comm *comm,
+ struct perf_sample *sample,
+ struct addr_location *from_al,
+ struct addr_location *to_al, u64 ref,
+ struct call_return_processor *crp);
+
#endif
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 18/24] perf tools: Add call information to the database export API
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (16 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 17/24] perf tools: Enhance the thread stack to output call/return data Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 19/24] perf tools: Add call information to Python export Adrian Hunter
` (5 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Make it possible for the database export API to use the
enhanced thread stack and export detailed information
about paired calls and returns.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/db-export.c | 52 ++++++++++++++++++++++++++++++++++++++++++++-
tools/perf/util/db-export.h | 12 +++++++++++
2 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index c5b6e02..ca79616 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -21,6 +21,7 @@
#include "comm.h"
#include "symbol.h"
#include "event.h"
+#include "thread-stack.h"
#include "db-export.h"
int db_export__init(struct db_export *dbe)
@@ -29,8 +30,10 @@ int db_export__init(struct db_export *dbe)
return 0;
}
-void db_export__exit(struct db_export *dbe __maybe_unused)
+void db_export__exit(struct db_export *dbe)
{
+ call_return_processor__free(dbe->crp);
+ dbe->crp = NULL;
}
int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel)
@@ -268,6 +271,13 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
&es.addr_sym_db_id, &es.addr_offset);
if (err)
return err;
+ if (dbe->crp) {
+ err = thread_stack__process(thread, comm, sample, al,
+ &addr_al, es.db_id,
+ dbe->crp);
+ if (err)
+ return err;
+ }
}
if (dbe->export_sample)
@@ -314,3 +324,43 @@ int db_export__branch_types(struct db_export *dbe)
}
return err;
}
+
+int db_export__call_path(struct db_export *dbe, struct call_path *cp)
+{
+ int err;
+
+ if (cp->db_id)
+ return 0;
+
+ if (cp->parent) {
+ err = db_export__call_path(dbe, cp->parent);
+ if (err)
+ return err;
+ }
+
+ cp->db_id = ++dbe->call_path_last_db_id;
+
+ if (dbe->export_call_path)
+ return dbe->export_call_path(dbe, cp);
+
+ return 0;
+}
+
+int db_export__call_return(struct db_export *dbe, struct call_return *cr)
+{
+ int err;
+
+ if (cr->db_id)
+ return 0;
+
+ err = db_export__call_path(dbe, cr->cp);
+ if (err)
+ return err;
+
+ cr->db_id = ++dbe->call_return_last_db_id;
+
+ if (dbe->export_call_return)
+ return dbe->export_call_return(dbe, cr);
+
+ return 0;
+}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
index e4baa45..dd5ac2a 100644
--- a/tools/perf/util/db-export.h
+++ b/tools/perf/util/db-export.h
@@ -25,6 +25,9 @@ struct comm;
struct dso;
struct perf_sample;
struct addr_location;
+struct call_return_processor;
+struct call_path;
+struct call_return;
struct export_sample {
union perf_event *event;
@@ -57,6 +60,10 @@ struct db_export {
int (*export_branch_type)(struct db_export *dbe, u32 branch_type,
const char *name);
int (*export_sample)(struct db_export *dbe, struct export_sample *es);
+ int (*export_call_path)(struct db_export *dbe, struct call_path *cp);
+ int (*export_call_return)(struct db_export *dbe,
+ struct call_return *cr);
+ struct call_return_processor *crp;
u64 evsel_last_db_id;
u64 machine_last_db_id;
u64 thread_last_db_id;
@@ -65,6 +72,8 @@ struct db_export {
u64 dso_last_db_id;
u64 symbol_last_db_id;
u64 sample_last_db_id;
+ u64 call_path_last_db_id;
+ u64 call_return_last_db_id;
};
int db_export__init(struct db_export *dbe);
@@ -89,4 +98,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
int db_export__branch_types(struct db_export *dbe);
+int db_export__call_path(struct db_export *dbe, struct call_path *cp);
+int db_export__call_return(struct db_export *dbe, struct call_return *cr);
+
#endif
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 19/24] perf tools: Add call information to Python export
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (17 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 18/24] perf tools: Add call information to the database export API Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 20/24] perf tools: Defer export of comms that were not 'set' Adrian Hunter
` (4 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Add the ability to export detailed information about
paired calls and returns to Python db export and
the export-to-postgresql.py script.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
.../scripts/python/bin/export-to-postgresql-report | 15 ++--
tools/perf/scripts/python/export-to-postgresql.py | 66 ++++++++++++++++-
.../util/scripting-engines/trace-event-python.c | 84 +++++++++++++++++++++-
3 files changed, 158 insertions(+), 7 deletions(-)
diff --git a/tools/perf/scripts/python/bin/export-to-postgresql-report b/tools/perf/scripts/python/bin/export-to-postgresql-report
index a8fdd15..cd335b6 100644
--- a/tools/perf/scripts/python/bin/export-to-postgresql-report
+++ b/tools/perf/scripts/python/bin/export-to-postgresql-report
@@ -1,6 +1,6 @@
#!/bin/bash
# description: export perf data to a postgresql database
-# args: [database name] [columns]
+# args: [database name] [columns] [calls]
n_args=0
for i in "$@"
do
@@ -9,11 +9,16 @@ do
fi
n_args=$(( $n_args + 1 ))
done
-if [ "$n_args" -gt 2 ] ; then
- echo "usage: export-to-postgresql-report [database name] [columns]"
+if [ "$n_args" -gt 3 ] ; then
+ echo "usage: export-to-postgresql-report [database name] [columns] [calls]"
exit
fi
-if [ "$n_args" -gt 1 ] ; then
+if [ "$n_args" -gt 2 ] ; then
+ dbname=$1
+ columns=$2
+ calls=$3
+ shift 3
+elif [ "$n_args" -gt 1 ] ; then
dbname=$1
columns=$2
shift 2
@@ -21,4 +26,4 @@ elif [ "$n_args" -gt 0 ] ; then
dbname=$1
shift
fi
-perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls
diff --git a/tools/perf/scripts/python/export-to-postgresql.py b/tools/perf/scripts/python/export-to-postgresql.py
index bb79aec..4cdafd8 100644
--- a/tools/perf/scripts/python/export-to-postgresql.py
+++ b/tools/perf/scripts/python/export-to-postgresql.py
@@ -40,10 +40,12 @@ sys.path.append(os.environ['PERF_EXEC_PATH'] + \
#from Core import *
perf_db_export_mode = True
+perf_db_export_calls = False
def usage():
- print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>]"
+ print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>]"
print >> sys.stderr, "where: columns 'all' or 'branches'"
+ print >> sys.stderr, " calls 'calls' => create calls table"
raise Exception("Too few arguments")
if (len(sys.argv) < 2):
@@ -61,6 +63,12 @@ if columns not in ("all", "branches"):
branches = (columns == "branches")
+if (len(sys.argv) >= 4):
+ if (sys.argv[3] == "calls"):
+ perf_db_export_calls = True
+ else:
+ usage()
+
output_dir_name = os.getcwd() + "/" + dbname + "-perf-data"
os.mkdir(output_dir_name)
@@ -170,6 +178,25 @@ else:
'branch_type integer,'
'in_tx boolean)')
+if perf_db_export_calls:
+ do_query(query, 'CREATE TABLE call_paths ('
+ 'id bigint NOT NULL,'
+ 'parent_id bigint,'
+ 'symbol_id bigint,'
+ 'ip bigint)')
+ do_query(query, 'CREATE TABLE calls ('
+ 'id bigint NOT NULL,'
+ 'thread_id bigint,'
+ 'comm_id bigint,'
+ 'call_path_id bigint,'
+ 'call_time bigint,'
+ 'return_time bigint,'
+ 'branch_count bigint,'
+ 'call_id bigint,'
+ 'return_id bigint,'
+ 'parent_call_path_id bigint,'
+ 'flags integer)')
+
do_query(query, 'CREATE VIEW samples_view AS '
'SELECT '
'id,'
@@ -246,6 +273,9 @@ dso_file = open_output_file("dso_table.bin")
symbol_file = open_output_file("symbol_table.bin")
branch_type_file = open_output_file("branch_type_table.bin")
sample_file = open_output_file("sample_table.bin")
+if perf_db_export_calls:
+ call_path_file = open_output_file("call_path_table.bin")
+ call_file = open_output_file("call_table.bin")
def trace_begin():
print datetime.datetime.today(), "Writing to intermediate files..."
@@ -256,6 +286,9 @@ def trace_begin():
comm_table(0, "unknown")
dso_table(0, 0, "unknown", "unknown", "")
symbol_table(0, 0, 0, 0, 0, "unknown")
+ sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ if perf_db_export_calls:
+ call_path_table(0, 0, 0, 0)
unhandled_count = 0
@@ -270,6 +303,9 @@ def trace_end():
copy_output_file(symbol_file, "symbols")
copy_output_file(branch_type_file, "branch_types")
copy_output_file(sample_file, "samples")
+ if perf_db_export_calls:
+ copy_output_file(call_path_file, "call_paths")
+ copy_output_file(call_file, "calls")
print datetime.datetime.today(), "Removing intermediate files..."
remove_output_file(evsel_file)
@@ -281,6 +317,9 @@ def trace_end():
remove_output_file(symbol_file)
remove_output_file(branch_type_file)
remove_output_file(sample_file)
+ if perf_db_export_calls:
+ remove_output_file(call_path_file)
+ remove_output_file(call_file)
os.rmdir(output_dir_name)
print datetime.datetime.today(), "Adding primary keys"
do_query(query, 'ALTER TABLE selected_events ADD PRIMARY KEY (id)')
@@ -292,6 +331,9 @@ def trace_end():
do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)')
do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)')
+ if perf_db_export_calls:
+ do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)')
+ do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)')
print datetime.datetime.today(), "Adding foreign keys"
do_query(query, 'ALTER TABLE threads '
@@ -313,6 +355,18 @@ def trace_end():
'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),'
'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),'
'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)')
+ if perf_db_export_calls:
+ do_query(query, 'ALTER TABLE call_paths '
+ 'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),'
+ 'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)')
+ do_query(query, 'ALTER TABLE calls '
+ 'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),'
+ 'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
+ 'ADD CONSTRAINT call_pathfk FOREIGN KEY (call_path_id) REFERENCES call_paths (id),'
+ 'ADD CONSTRAINT callfk FOREIGN KEY (call_id) REFERENCES samples (id),'
+ 'ADD CONSTRAINT returnfk FOREIGN KEY (return_id) REFERENCES samples (id),'
+ 'ADD CONSTRAINT parent_call_pathfk FOREIGN KEY (parent_call_path_id) REFERENCES call_paths (id)')
+ do_query(query, 'CREATE INDEX pcpid_idx ON calls (parent_call_path_id)')
if (unhandled_count):
print datetime.datetime.today(), "Warning: ", unhandled_count, " unhandled events"
@@ -378,3 +432,13 @@ def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, sy
else:
value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx)
sample_file.write(value)
+
+def call_path_table(cp_id, parent_id, symbol_id, ip, *x):
+ fmt = "!hiqiqiqiq"
+ value = struct.pack(fmt, 4, 8, cp_id, 8, parent_id, 8, symbol_id, 8, ip)
+ call_path_file.write(value)
+
+def call_return_table(cr_id, thread_id, comm_id, call_path_id, call_time, return_time, branch_count, call_id, return_id, parent_call_path_id, flags, *x):
+ fmt = "!hiqiqiqiqiqiqiqiqiqiqii"
+ value = struct.pack(fmt, 11, 8, cr_id, 8, thread_id, 8, comm_id, 8, call_path_id, 8, call_time, 8, return_time, 8, branch_count, 8, call_id, 8, return_id, 8, parent_call_path_id, 4, flags)
+ call_file.write(value)
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 881d595..ed754d0 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -36,6 +36,7 @@
#include "../comm.h"
#include "../machine.h"
#include "../db-export.h"
+#include "../thread-stack.h"
#include "../trace-event.h"
#include "../machine.h"
@@ -67,6 +68,8 @@ struct tables {
PyObject *symbol_handler;
PyObject *branch_type_handler;
PyObject *sample_handler;
+ PyObject *call_path_handler;
+ PyObject *call_return_handler;
bool db_export_mode;
};
@@ -718,6 +721,64 @@ static int python_export_sample(struct db_export *dbe,
return 0;
}
+static int python_export_call_path(struct db_export *dbe, struct call_path *cp)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ PyObject *t;
+ u64 parent_db_id, sym_db_id;
+
+ parent_db_id = cp->parent ? cp->parent->db_id : 0;
+ sym_db_id = cp->sym ? cp->sym->db_id : 0;
+
+ t = tuple_new(4);
+
+ tuple_set_u64(t, 0, cp->db_id);
+ tuple_set_u64(t, 1, parent_db_id);
+ tuple_set_u64(t, 2, sym_db_id);
+ tuple_set_u64(t, 3, cp->ip);
+
+ call_object(tables->call_path_handler, t, "call_path_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_export_call_return(struct db_export *dbe,
+ struct call_return *cr)
+{
+ struct tables *tables = container_of(dbe, struct tables, dbe);
+ u64 comm_db_id = cr->comm ? cr->comm->db_id : 0;
+ PyObject *t;
+
+ t = tuple_new(11);
+
+ tuple_set_u64(t, 0, cr->db_id);
+ tuple_set_u64(t, 1, cr->thread->db_id);
+ tuple_set_u64(t, 2, comm_db_id);
+ tuple_set_u64(t, 3, cr->cp->db_id);
+ tuple_set_u64(t, 4, cr->call_time);
+ tuple_set_u64(t, 5, cr->return_time);
+ tuple_set_u64(t, 6, cr->branch_count);
+ tuple_set_u64(t, 7, cr->call_ref);
+ tuple_set_u64(t, 8, cr->return_ref);
+ tuple_set_u64(t, 9, cr->cp->parent->db_id);
+ tuple_set_s32(t, 10, cr->flags);
+
+ call_object(tables->call_return_handler, t, "call_return_table");
+
+ Py_DECREF(t);
+
+ return 0;
+}
+
+static int python_process_call_return(struct call_return *cr, void *data)
+{
+ struct db_export *dbe = data;
+
+ return db_export__call_return(dbe, cr);
+}
+
static void python_process_general_event(struct perf_sample *sample,
struct perf_evsel *evsel,
struct thread *thread,
@@ -850,7 +911,9 @@ error:
static void set_table_handlers(struct tables *tables)
{
const char *perf_db_export_mode = "perf_db_export_mode";
- PyObject *db_export_mode;
+ const char *perf_db_export_calls = "perf_db_export_calls";
+ PyObject *db_export_mode, *db_export_calls;
+ bool export_calls = false;
int ret;
memset(tables, 0, sizeof(struct tables));
@@ -867,6 +930,23 @@ static void set_table_handlers(struct tables *tables)
if (!ret)
return;
+ tables->dbe.crp = NULL;
+ db_export_calls = PyDict_GetItemString(main_dict, perf_db_export_calls);
+ if (db_export_calls) {
+ ret = PyObject_IsTrue(db_export_calls);
+ if (ret == -1)
+ handler_call_die(perf_db_export_calls);
+ export_calls = !!ret;
+ }
+
+ if (export_calls) {
+ tables->dbe.crp =
+ call_return_processor__new(python_process_call_return,
+ &tables->dbe);
+ if (!tables->dbe.crp)
+ Py_FatalError("failed to create calls processor");
+ }
+
tables->db_export_mode = true;
SET_TABLE_HANDLER(evsel);
@@ -878,6 +958,8 @@ static void set_table_handlers(struct tables *tables)
SET_TABLE_HANDLER(symbol);
SET_TABLE_HANDLER(branch_type);
SET_TABLE_HANDLER(sample);
+ SET_TABLE_HANDLER(call_path);
+ SET_TABLE_HANDLER(call_return);
}
/*
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 20/24] perf tools: Defer export of comms that were not 'set'
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (18 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 19/24] perf tools: Add call information to Python export Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 21/24] perf tools: Add perf-with-kcore script Adrian Hunter
` (3 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Tracing for a workload begins before the comm event
is seen, which results in the initial comm having a
string of the form ":<pid>" (e.g. ":12345"). In order
to export the correct string, defer the export until
the new script 'flush' callback.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/db-export.c | 62 +++++++++++++++++++++-
tools/perf/util/db-export.h | 3 ++
.../util/scripting-engines/trace-event-python.c | 4 +-
3 files changed, 67 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/db-export.c b/tools/perf/util/db-export.c
index ca79616..ef61cf7 100644
--- a/tools/perf/util/db-export.c
+++ b/tools/perf/util/db-export.c
@@ -21,17 +21,74 @@
#include "comm.h"
#include "symbol.h"
#include "event.h"
+#include "util.h"
#include "thread-stack.h"
#include "db-export.h"
+struct deferred_export {
+ struct list_head node;
+ struct comm *comm;
+};
+
+static int db_export__deferred(struct db_export *dbe)
+{
+ struct deferred_export *de;
+ int err;
+
+ while (!list_empty(&dbe->deferred)) {
+ de = list_entry(dbe->deferred.next, struct deferred_export,
+ node);
+ err = dbe->export_comm(dbe, de->comm);
+ list_del(&de->node);
+ free(de);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static void db_export__free_deferred(struct db_export *dbe)
+{
+ struct deferred_export *de;
+
+ while (!list_empty(&dbe->deferred)) {
+ de = list_entry(dbe->deferred.next, struct deferred_export,
+ node);
+ list_del(&de->node);
+ free(de);
+ }
+}
+
+static int db_export__defer_comm(struct db_export *dbe, struct comm *comm)
+{
+ struct deferred_export *de;
+
+ de = zalloc(sizeof(struct deferred_export));
+ if (!de)
+ return -ENOMEM;
+
+ de->comm = comm;
+ list_add_tail(&de->node, &dbe->deferred);
+
+ return 0;
+}
+
int db_export__init(struct db_export *dbe)
{
memset(dbe, 0, sizeof(struct db_export));
+ INIT_LIST_HEAD(&dbe->deferred);
return 0;
}
+int db_export__flush(struct db_export *dbe)
+{
+ return db_export__deferred(dbe);
+}
+
void db_export__exit(struct db_export *dbe)
{
+ db_export__free_deferred(dbe);
call_return_processor__free(dbe->crp);
dbe->crp = NULL;
}
@@ -115,7 +172,10 @@ int db_export__comm(struct db_export *dbe, struct comm *comm,
comm->db_id = ++dbe->comm_last_db_id;
if (dbe->export_comm) {
- err = dbe->export_comm(dbe, comm);
+ if (main_thread->comm_set)
+ err = dbe->export_comm(dbe, comm);
+ else
+ err = db_export__defer_comm(dbe, comm);
if (err)
return err;
}
diff --git a/tools/perf/util/db-export.h b/tools/perf/util/db-export.h
index dd5ac2a..adbd22d 100644
--- a/tools/perf/util/db-export.h
+++ b/tools/perf/util/db-export.h
@@ -17,6 +17,7 @@
#define __PERF_DB_EXPORT_H
#include <linux/types.h>
+#include <linux/list.h>
struct perf_evsel;
struct machine;
@@ -74,9 +75,11 @@ struct db_export {
u64 sample_last_db_id;
u64 call_path_last_db_id;
u64 call_return_last_db_id;
+ struct list_head deferred;
};
int db_export__init(struct db_export *dbe);
+int db_export__flush(struct db_export *dbe);
void db_export__exit(struct db_export *dbe);
int db_export__evsel(struct db_export *dbe, struct perf_evsel *evsel);
int db_export__machine(struct db_export *dbe, struct machine *machine);
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index ed754d0..6c0ce62 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -1024,7 +1024,9 @@ error:
static int python_flush_script(void)
{
- return 0;
+ struct tables *tables = &tables_global;
+
+ return db_export__flush(&tables->dbe);
}
/*
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 21/24] perf tools: Add perf-with-kcore script
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (19 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 20/24] perf tools: Defer export of comms that were not 'set' Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 22/24] perf tools: Build programs to copy 32-bit compatibility VDSOs Adrian Hunter
` (2 subsequent siblings)
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Decoding an Intel PT trace of the kernel requires
an accurate kernel object image. This is provided
by making a copy of kcore. However the copy needs
to be made under the same conditions as the original
recording, and then it needs to be associated with
the perf.data file. The perf-with-kcore script does
that.
The script also checks the permissions on the buildid
cache and can be used to fix them. That is needed
for distributions where root does not have a home
directory and consequently writes to the same
buildid cache as the user, resulting in cached files
that the user does not have access to.
Example:
$ ./perf-with-kcore
Usage: perf-with-kcore <perf sub-command> <perf.data directory> [<sub-command options> [ -- <workload>]]
<perf sub-command> can be record, script, report or inject
or: perf-with-kcore fix_buildid_cache_permissions
$ ./perf-with-kcore record pt_uname -e intel_pt// -- uname
Recording
Using /home/ahunter/bin/perf
perf version 3.15.rc3.g4549ba
/home/ahunter/bin/perf record -o pt_uname/perf.data -e intel_pt// -- uname
Linux
[ perf record: Woken up 3 times to write data ]
[ perf record: Captured and wrote 0.023 MB pt_uname/perf.data ]
Copying kcore
[sudo] password for ahunter:
Done
$ tools/perf/perf-with-kcore.sh script pt_uname | head
Using /home/ahunter/bin/perf
perf version 3.15.rc3.g4549ba
/home/ahunter/bin/perf script -i pt_uname/perf.data --kallsyms=pt_uname/kcore_dir/kallsyms
swapper 0 [002] 161533.969666: sched:sched_switch: swapper/2:0 [120] R ==> perf:11316 [120]
:11315 11315 [003] 161533.969704: sched:sched_switch: perf:11315 [120] S ==> swapper/3:0 [120]
:11316 11316 [002] 161533.969783: sched:sched_switch: perf:11316 [120] R ==> migration/2:33 [0]
:33 33 [002] 161533.969791: sched:sched_switch: migration/2:33 [0] S ==> swapper/2:0 [120]
swapper 0 [003] 161533.969792: sched:sched_switch: swapper/3:0 [120] R ==> perf:11316 [120]
:11316 11316 [003] 161533.970062: branches: 0 [unknown] ([unknown]) => ffffffff810532fa native_write_msr_safe ([kernel.kallsyms])
:11316 11316 [003] 161533.970062: branches: ffffffff810532fd native_write_msr_safe ([kernel.kallsyms]) => ffffffff81035b31 pt_config_start ([kernel.kallsyms])
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/.gitignore | 1 +
tools/perf/Makefile.perf | 5 +-
tools/perf/perf-with-kcore.sh | 259 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 264 insertions(+), 1 deletion(-)
create mode 100644 tools/perf/perf-with-kcore.sh
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index 782d86e..717221e 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -15,6 +15,7 @@ perf.data
perf.data.old
output.svg
perf-archive
+perf-with-kcore
tags
TAGS
cscope*
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index df1b164..fd76af2 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -126,6 +126,7 @@ PYRF_OBJS =
SCRIPT_SH =
SCRIPT_SH += perf-archive.sh
+SCRIPT_SH += perf-with-kcore.sh
grep-libs = $(filter -l%,$(1))
strip-libs = $(filter-out -l%,$(1))
@@ -882,6 +883,8 @@ install-bin: all install-gtk
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
$(call QUIET_INSTALL, perf-archive) \
$(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
+ $(call QUIET_INSTALL, perf-with-kcore) \
+ $(INSTALL) $(OUTPUT)perf-with-kcore -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
ifndef NO_LIBPERL
$(call QUIET_INSTALL, perl-scripts) \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'; \
@@ -927,7 +930,7 @@ config-clean:
@$(MAKE) -C config/feature-checks clean >/dev/null
clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean
- $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
+ $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
$(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
diff --git a/tools/perf/perf-with-kcore.sh b/tools/perf/perf-with-kcore.sh
new file mode 100644
index 0000000..c7ff90a
--- /dev/null
+++ b/tools/perf/perf-with-kcore.sh
@@ -0,0 +1,259 @@
+#!/bin/bash
+# perf-with-kcore: use perf with a copy of kcore
+# Copyright (c) 2014, Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# more details.
+
+set -e
+
+usage()
+{
+ echo "Usage: perf-with-kcore <perf sub-command> <perf.data directory> [<sub-command options> [ -- <workload>]]" >&2
+ echo " <perf sub-command> can be record, script, report or inject" >&2
+ echo " or: perf-with-kcore fix_buildid_cache_permissions" >&2
+ exit 1
+}
+
+find_perf()
+{
+ if [ -n "$PERF" ] ; then
+ return
+ fi
+ PERF=`which perf || true`
+ if [ -z "$PERF" ] ; then
+ echo "Failed to find perf" >&2
+ exit 1
+ fi
+ if [ ! -x "$PERF" ] ; then
+ echo "Failed to find perf" >&2
+ exit 1
+ fi
+ echo "Using $PERF"
+ "$PERF" version
+}
+
+copy_kcore()
+{
+ echo "Copying kcore"
+
+ if [ $EUID -eq 0 ] ; then
+ SUDO=""
+ else
+ SUDO="sudo"
+ fi
+
+ rm -f perf.data.junk
+ ("$PERF" record -o perf.data.junk $PERF_OPTIONS -- sleep 60) >/dev/null 2>/dev/null &
+ PERF_PID=$!
+
+ # Need to make sure that perf has started
+ sleep 1
+
+ KCORE=$(($SUDO "$PERF" buildid-cache -v -f -k /proc/kcore >/dev/null) 2>&1)
+ case "$KCORE" in
+ "kcore added to build-id cache directory "*)
+ KCORE_DIR=${KCORE#"kcore added to build-id cache directory "}
+ ;;
+ *)
+ kill $PERF_PID
+ wait >/dev/null 2>/dev/null || true
+ rm perf.data.junk
+ echo "$KCORE"
+ echo "Failed to find kcore" >&2
+ exit 1
+ ;;
+ esac
+
+ kill $PERF_PID
+ wait >/dev/null 2>/dev/null || true
+ rm perf.data.junk
+
+ $SUDO cp -a "$KCORE_DIR" "$(pwd)/$PERF_DATA_DIR"
+ $SUDO rm -f "$KCORE_DIR/kcore"
+ $SUDO rm -f "$KCORE_DIR/kallsyms"
+ $SUDO rm -f "$KCORE_DIR/modules"
+ $SUDO rmdir "$KCORE_DIR"
+
+ KCORE_DIR_BASENAME=$(basename "$KCORE_DIR")
+ KCORE_DIR="$(pwd)/$PERF_DATA_DIR/$KCORE_DIR_BASENAME"
+
+ $SUDO chown $UID "$KCORE_DIR"
+ $SUDO chown $UID "$KCORE_DIR/kcore"
+ $SUDO chown $UID "$KCORE_DIR/kallsyms"
+ $SUDO chown $UID "$KCORE_DIR/modules"
+
+ $SUDO chgrp $GROUPS "$KCORE_DIR"
+ $SUDO chgrp $GROUPS "$KCORE_DIR/kcore"
+ $SUDO chgrp $GROUPS "$KCORE_DIR/kallsyms"
+ $SUDO chgrp $GROUPS "$KCORE_DIR/modules"
+
+ ln -s "$KCORE_DIR_BASENAME" "$PERF_DATA_DIR/kcore_dir"
+}
+
+fix_buildid_cache_permissions()
+{
+ if [ $EUID -ne 0 ] ; then
+ echo "This script must be run as root via sudo " >&2
+ exit 1
+ fi
+
+ if [ -z "$SUDO_USER" ] ; then
+ echo "This script must be run via sudo" >&2
+ exit 1
+ fi
+
+ USER_HOME=$(bash <<< "echo ~$SUDO_USER")
+
+ if [ "$HOME" != "$USER_HOME" ] ; then
+ echo "Fix unnecessary because root has a home: $HOME" >&2
+ exit 1
+ fi
+
+ echo "Fixing buildid cache permissions"
+
+ find "$USER_HOME/.debug" -xdev -type d ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \;
+ find "$USER_HOME/.debug" -xdev -type f -links 1 ! -user "$SUDO_USER" -ls -exec chown "$SUDO_USER" \{\} \;
+ find "$USER_HOME/.debug" -xdev -type l ! -user "$SUDO_USER" -ls -exec chown -h "$SUDO_USER" \{\} \;
+
+ if [ -n "$SUDO_GID" ] ; then
+ find "$USER_HOME/.debug" -xdev -type d ! -group "$SUDO_GID" -ls -exec chgrp "$SUDO_GID" \{\} \;
+ find "$USER_HOME/.debug" -xdev -type f -links 1 ! -group "$SUDO_GID" -ls -exec chgrp "$SUDO_GID" \{\} \;
+ find "$USER_HOME/.debug" -xdev -type l ! -group "$SUDO_GID" -ls -exec chgrp -h "$SUDO_GID" \{\} \;
+ fi
+
+ echo "Done"
+}
+
+check_buildid_cache_permissions()
+{
+ if [ $EUID -eq 0 ] ; then
+ return
+ fi
+
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type d ! -user "$USER" -print -quit)
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type f -links 1 ! -user "$USER" -print -quit)
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type l ! -user "$USER" -print -quit)
+
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type d ! -group "$GROUPS" -print -quit)
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type f -links 1 ! -group "$GROUPS" -print -quit)
+ PERMISSIONS_OK+=$(find "$HOME/.debug" -xdev -type l ! -group "$GROUPS" -print -quit)
+
+ if [ -n "$PERMISSIONS_OK" ] ; then
+ echo "*** WARNING *** buildid cache permissions may need fixing" >&2
+ fi
+}
+
+record()
+{
+ echo "Recording"
+
+ if [ $EUID -ne 0 ] ; then
+
+ if [ "$(cat /proc/sys/kernel/kptr_restrict)" -ne 0 ] ; then
+ echo "*** WARNING *** /proc/sys/kernel/kptr_restrict prevents access to kernel addresses" >&2
+ fi
+
+ if echo "$PERF_OPTIONS" | grep -q ' -a \|^-a \| -a$\|^-a$\| --all-cpus \|^--all-cpus \| --all-cpus$\|^--all-cpus$' ; then
+ echo "*** WARNING *** system-wide tracing without root access will not be able to read all necessary information from /proc" >&2
+ fi
+
+ if echo "$PERF_OPTIONS" | grep -q 'intel_pt\|intel_bts\| -I\|^-I' ; then
+ if [ "$(cat /proc/sys/kernel/perf_event_paranoid)" -gt -1 ] ; then
+ echo "*** WARNING *** /proc/sys/kernel/perf_event_paranoid restricts buffer size and tracepoint (sched_switch) use" >&2
+ fi
+
+ if echo "$PERF_OPTIONS" | grep -q ' --per-thread \|^--per-thread \| --per-thread$\|^--per-thread$' ; then
+ true
+ elif echo "$PERF_OPTIONS" | grep -q ' -t \|^-t \| -t$\|^-t$' ; then
+ true
+ elif [ ! -r /sys/kernel/debug -o ! -x /sys/kernel/debug ] ; then
+ echo "*** WARNING *** /sys/kernel/debug permissions prevent tracepoint (sched_switch) use" >&2
+ fi
+ fi
+ fi
+
+ if [ -z "$1" ] ; then
+ echo "Workload is required for recording" >&2
+ usage
+ fi
+
+ if [ -e "$PERF_DATA_DIR" ] ; then
+ echo "'$PERF_DATA_DIR' exists" >&2
+ exit 1
+ fi
+
+ find_perf
+
+ mkdir "$PERF_DATA_DIR"
+
+ echo "$PERF record -o $PERF_DATA_DIR/perf.data $PERF_OPTIONS -- $*"
+ "$PERF" record -o "$PERF_DATA_DIR/perf.data" $PERF_OPTIONS -- $* || true
+
+ if rmdir "$PERF_DATA_DIR" > /dev/null 2>/dev/null ; then
+ exit 1
+ fi
+
+ copy_kcore
+
+ echo "Done"
+}
+
+subcommand()
+{
+ find_perf
+ check_buildid_cache_permissions
+ echo "$PERF $PERF_SUB_COMMAND -i $PERF_DATA_DIR/perf.data --kallsyms=$PERF_DATA_DIR/kcore_dir/kallsyms $*"
+ "$PERF" $PERF_SUB_COMMAND -i "$PERF_DATA_DIR/perf.data" "--kallsyms=$PERF_DATA_DIR/kcore_dir/kallsyms" $*
+}
+
+if [ "$1" = "fix_buildid_cache_permissions" ] ; then
+ fix_buildid_cache_permissions
+ exit 0
+fi
+
+PERF_SUB_COMMAND=$1
+PERF_DATA_DIR=$2
+shift || true
+shift || true
+
+if [ -z "$PERF_SUB_COMMAND" ] ; then
+ usage
+fi
+
+if [ -z "$PERF_DATA_DIR" ] ; then
+ usage
+fi
+
+case "$PERF_SUB_COMMAND" in
+"record")
+ while [ "$1" != "--" ] ; do
+ PERF_OPTIONS+="$1 "
+ shift || break
+ done
+ if [ "$1" != "--" ] ; then
+ echo "Options and workload are required for recording" >&2
+ usage
+ fi
+ shift
+ record $*
+;;
+"script")
+ subcommand $*
+;;
+"report")
+ subcommand $*
+;;
+"inject")
+ subcommand $*
+;;
+*)
+ usage
+;;
+esac
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 22/24] perf tools: Build programs to copy 32-bit compatibility VDSOs
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (20 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 21/24] perf tools: Add perf-with-kcore script Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-09-15 15:47 ` Arnaldo Carvalho de Melo
2014-08-15 19:08 ` [PATCH 23/24] perf tools: Add support for " Adrian Hunter
2014-08-15 19:08 ` [PATCH 24/24] perf tools: Add feature checks to .gitignore Adrian Hunter
23 siblings, 1 reply; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
perf tools copy VDSO out of memory. However, on 64-bit
machines there may be 32-bit compatibility VDOs also. To
copy those requires separate 32-bit executables. This
patch adds to the build additional programs perf-read-vdso32
and perf-read-vdsox32 for 32-bit and x32 respectively.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/Makefile.perf | 38 ++++++++++++++++++++++---
tools/perf/config/Makefile | 25 +++++++++++++++-
tools/perf/config/Makefile.arch | 8 ++++++
tools/perf/config/feature-checks/Makefile | 10 ++++++-
tools/perf/config/feature-checks/test-compile.c | 4 +++
tools/perf/perf-read-vdso.c | 34 ++++++++++++++++++++++
tools/perf/util/find-vdso-map.c | 30 +++++++++++++++++++
tools/perf/util/vdso.c | 37 ++++--------------------
8 files changed, 149 insertions(+), 37 deletions(-)
create mode 100644 tools/perf/config/feature-checks/test-compile.c
create mode 100644 tools/perf/perf-read-vdso.c
create mode 100644 tools/perf/util/find-vdso-map.c
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index fd76af2..731785e 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -60,6 +60,12 @@ include config/utilities.mak
#
# Define NO_LIBDW_DWARF_UNWIND if you do not want libdw support
# for dwarf backtrace post unwind.
+#
+# Define NO_PERF_READ_VDSO32 if you do not want to build perf-read-vdso32
+# for reading the 32-bit compatibility VDSO in 64-bit mode
+#
+# Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32
+# for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode
ifeq ($(srctree),)
srctree := $(patsubst %/,%,$(dir $(shell pwd)))
@@ -171,11 +177,16 @@ $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS)
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH))
-#
-# Single 'perf' binary right now:
-#
PROGRAMS += $(OUTPUT)perf
+ifndef NO_PERF_READ_VDSO32
+PROGRAMS += $(OUTPUT)perf-read-vdso32
+endif
+
+ifndef NO_PERF_READ_VDSOX32
+PROGRAMS += $(OUTPUT)perf-read-vdsox32
+endif
+
# what 'all' will build and 'install' will install, in perfexecdir
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
@@ -306,6 +317,7 @@ LIB_H += util/data.h
LIB_H += util/kvm-stat.h
LIB_H += util/thread-stack.h
LIB_H += util/db-export.h
+LIB_H += util/find-vdso-map.c
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
@@ -735,6 +747,16 @@ $(OUTPUT)scripts/python/Perf-Trace-Util/Context.o: scripts/python/Perf-Trace-Uti
$(OUTPUT)perf-%: %.o $(PERFLIBS)
$(QUIET_LINK)$(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(filter %.o,$^) $(LIBS)
+ifndef NO_PERF_READ_VDSO32
+$(OUTPUT)perf-read-vdso32: perf-read-vdso.c util/find-vdso-map.c
+ $(QUIET_CC)$(CC) -m32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c
+endif
+
+ifndef NO_PERF_READ_VDSOX32
+$(OUTPUT)perf-read-vdsox32: perf-read-vdso.c util/find-vdso-map.c
+ $(QUIET_CC)$(CC) -mx32 $(filter -static,$(LDFLAGS)) -Wall -Werror -o $@ perf-read-vdso.c
+endif
+
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst perf-%,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
@@ -879,6 +901,14 @@ install-bin: all install-gtk
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'; \
$(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)'; \
$(LN) '$(DESTDIR_SQ)$(bindir_SQ)/perf' '$(DESTDIR_SQ)$(bindir_SQ)/trace'
+ifndef NO_PERF_READ_VDSO32
+ $(call QUIET_INSTALL, perf-read-vdso32) \
+ $(INSTALL) $(OUTPUT)perf-read-vdso32 '$(DESTDIR_SQ)$(bindir_SQ)';
+endif
+ifndef NO_PERF_READ_VDSOX32
+ $(call QUIET_INSTALL, perf-read-vdsox32) \
+ $(INSTALL) $(OUTPUT)perf-read-vdsox32 '$(DESTDIR_SQ)$(bindir_SQ)';
+endif
$(call QUIET_INSTALL, libexec) \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
$(call QUIET_INSTALL, perf-archive) \
@@ -931,7 +961,7 @@ config-clean:
clean: $(LIBTRACEEVENT)-clean $(LIBAPIKFS)-clean config-clean
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(OUTPUT)perf.o $(LANG_BINDINGS) $(GTK_OBJS)
- $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf
+ $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32
$(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS $(OUTPUT)PERF-FEATURES $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex*
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean
$(python-clean)
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile
index 75d4c23..48a5251 100644
--- a/tools/perf/config/Makefile
+++ b/tools/perf/config/Makefile
@@ -234,7 +234,9 @@ VF_FEATURE_TESTS = \
bionic \
liberty \
liberty-z \
- cplus-demangle
+ cplus-demangle \
+ compile-32 \
+ compile-x32
# Set FEATURE_CHECK_(C|LD)FLAGS-all for all CORE_FEATURE_TESTS features.
# If in the future we need per-feature checks/flags for features not
@@ -620,6 +622,27 @@ ifdef HAVE_KVM_STAT_SUPPORT
CFLAGS += -DHAVE_KVM_STAT_SUPPORT
endif
+ifeq (${IS_64_BIT}, 1)
+ ifndef NO_PERF_READ_VDSO32
+ $(call feature_check,compile-32)
+ ifneq ($(feature-compile-32), 1)
+ NO_PERF_READ_VDSO32 := 1
+ endif
+ endif
+ ifneq (${IS_X86_64}, 1)
+ NO_PERF_READ_VDSOX32 := 1
+ endif
+ ifndef NO_PERF_READ_VDSOX32
+ $(call feature_check,compile-x32)
+ ifneq ($(feature-compile-x32), 1)
+ NO_PERF_READ_VDSOX32 := 1
+ endif
+ endif
+else
+ NO_PERF_READ_VDSO32 := 1
+ NO_PERF_READ_VDSOX32 := 1
+endif
+
# Among the variables below, these:
# perfexecdir
# template_dir
diff --git a/tools/perf/config/Makefile.arch b/tools/perf/config/Makefile.arch
index 4b06719..851cd01 100644
--- a/tools/perf/config/Makefile.arch
+++ b/tools/perf/config/Makefile.arch
@@ -21,3 +21,11 @@ ifeq ($(ARCH),x86_64)
RAW_ARCH := x86_64
endif
endif
+
+ifeq (${IS_X86_64}, 1)
+ IS_64_BIT := 1
+else ifeq ($(ARCH),x86)
+ IS_64_BIT := 0
+else
+ IS_64_BIT := $(shell echo __LP64__ | ${CC} ${CFLAGS} -E -x c - | tail -n 1)
+endif
diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile
index 72ab298..7c68ec7 100644
--- a/tools/perf/config/feature-checks/Makefile
+++ b/tools/perf/config/feature-checks/Makefile
@@ -27,7 +27,9 @@ FILES= \
test-libunwind-debug-frame.bin \
test-stackprotector-all.bin \
test-timerfd.bin \
- test-libdw-dwarf-unwind.bin
+ test-libdw-dwarf-unwind.bin \
+ test-compile-32.bin \
+ test-compile-x32.bin
CC := $(CROSS_COMPILE)gcc -MD
PKG_CONFIG := $(CROSS_COMPILE)pkg-config
@@ -131,6 +133,12 @@ test-libdw-dwarf-unwind.bin:
test-sync-compare-and-swap.bin:
$(BUILD) -Werror
+test-compile-32.bin:
+ $(CC) -m32 -o $(OUTPUT)$@ test-compile.c
+
+test-compile-x32.bin:
+ $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c
+
-include *.d
###############################
diff --git a/tools/perf/config/feature-checks/test-compile.c b/tools/perf/config/feature-checks/test-compile.c
new file mode 100644
index 0000000..31dbf45
--- /dev/null
+++ b/tools/perf/config/feature-checks/test-compile.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+ return 0;
+}
diff --git a/tools/perf/perf-read-vdso.c b/tools/perf/perf-read-vdso.c
new file mode 100644
index 0000000..764e254
--- /dev/null
+++ b/tools/perf/perf-read-vdso.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <string.h>
+
+#define VDSO__MAP_NAME "[vdso]"
+
+/*
+ * Include definition of find_vdso_map() also used in util/vdso.c for
+ * building perf.
+ */
+#include "util/find-vdso-map.c"
+
+int main(void)
+{
+ void *start, *end;
+ size_t size, written;
+
+ if (find_vdso_map(&start, &end))
+ return 1;
+
+ size = end - start;
+
+ while (size) {
+ written = fwrite(start, 1, size, stdout);
+ if (!written)
+ return 1;
+ start += written;
+ size -= written;
+ }
+
+ if (fflush(stdout))
+ return 1;
+
+ return 0;
+}
diff --git a/tools/perf/util/find-vdso-map.c b/tools/perf/util/find-vdso-map.c
new file mode 100644
index 0000000..95ef1cf
--- /dev/null
+++ b/tools/perf/util/find-vdso-map.c
@@ -0,0 +1,30 @@
+static int find_vdso_map(void **start, void **end)
+{
+ FILE *maps;
+ char line[128];
+ int found = 0;
+
+ maps = fopen("/proc/self/maps", "r");
+ if (!maps) {
+ fprintf(stderr, "vdso: cannot open maps\n");
+ return -1;
+ }
+
+ while (!found && fgets(line, sizeof(line), maps)) {
+ int m = -1;
+
+ /* We care only about private r-x mappings. */
+ if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
+ start, end, &m))
+ continue;
+ if (m < 0)
+ continue;
+
+ if (!strncmp(&line[m], VDSO__MAP_NAME,
+ sizeof(VDSO__MAP_NAME) - 1))
+ found = 1;
+ }
+
+ fclose(maps);
+ return !found;
+}
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index adca693..f51390a 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -15,6 +15,12 @@
#include "linux/string.h"
#include "debug.h"
+/*
+ * Include definition of find_vdso_map() also used in perf-read-vdso.c for
+ * building perf-read-vdso32 and perf-read-vdsox32.
+ */
+#include "find-vdso-map.c"
+
#define VDSO__TEMP_FILE_NAME "/tmp/perf-vdso.so-XXXXXX"
struct vdso_file {
@@ -40,37 +46,6 @@ static struct vdso_info *vdso_info__new(void)
return memdup(&vdso_info_init, sizeof(vdso_info_init));
}
-static int find_vdso_map(void **start, void **end)
-{
- FILE *maps;
- char line[128];
- int found = 0;
-
- maps = fopen("/proc/self/maps", "r");
- if (!maps) {
- pr_err("vdso: cannot open maps\n");
- return -1;
- }
-
- while (!found && fgets(line, sizeof(line), maps)) {
- int m = -1;
-
- /* We care only about private r-x mappings. */
- if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
- start, end, &m))
- continue;
- if (m < 0)
- continue;
-
- if (!strncmp(&line[m], VDSO__MAP_NAME,
- sizeof(VDSO__MAP_NAME) - 1))
- found = 1;
- }
-
- fclose(maps);
- return !found;
-}
-
static char *get_file(struct vdso_file *vdso_file)
{
char *vdso = NULL;
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 23/24] perf tools: Add support for 32-bit compatibility VDSOs
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (21 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 22/24] perf tools: Build programs to copy 32-bit compatibility VDSOs Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
2014-08-15 19:08 ` [PATCH 24/24] perf tools: Add feature checks to .gitignore Adrian Hunter
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
'perf record' post-processes the event stream to create
a list of build-ids for object files for which sample
events have been recorded. That results in those object
files being recorded in the build-id cache.
In the case of VDSO, perf tools reads it from memory
and copies it into a temporary file, which as decribed
above, gets added to the build-id cache.
Then when the perf.data file is processed by other
tools, the build-id of VDSO is listed in the perf.data
file and the VDSO can be read from the build-id cache.
In that case the name of the map, the short name of the
DSO, and the entry in the build-id cache are all
"[vdso]".
However, in the 64-bit case, there also can be 32-bit
compatibility VDSOs.
A previous patch added programs "perf-read-vdso32" and
"perf read-vdsox32". This patch uses those programs to
read the correct VDSO for a thread and create a
temporary file just as for the 64-bit VDSO. The map
name and the entry in the build-id cache are still
"[vdso]" but the DSO short name becomes "[vdso32]"
and "[vdsox32]" respectively.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
tools/perf/util/vdso.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++-
tools/perf/util/vdso.h | 4 +-
2 files changed, 172 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
index f51390a..69daef6 100644
--- a/tools/perf/util/vdso.c
+++ b/tools/perf/util/vdso.c
@@ -12,6 +12,7 @@
#include "util.h"
#include "symbol.h"
#include "machine.h"
+#include "thread.h"
#include "linux/string.h"
#include "debug.h"
@@ -28,10 +29,15 @@ struct vdso_file {
bool error;
char temp_file_name[sizeof(VDSO__TEMP_FILE_NAME)];
const char *dso_name;
+ const char *read_prog;
};
struct vdso_info {
struct vdso_file vdso;
+#if BITS_PER_LONG == 64
+ struct vdso_file vdso32;
+ struct vdso_file vdsox32;
+#endif
};
static struct vdso_info *vdso_info__new(void)
@@ -41,6 +47,18 @@ static struct vdso_info *vdso_info__new(void)
.temp_file_name = VDSO__TEMP_FILE_NAME,
.dso_name = DSO__NAME_VDSO,
},
+#if BITS_PER_LONG == 64
+ .vdso32 = {
+ .temp_file_name = VDSO__TEMP_FILE_NAME,
+ .dso_name = DSO__NAME_VDSO32,
+ .read_prog = "perf-read-vdso32",
+ },
+ .vdsox32 = {
+ .temp_file_name = VDSO__TEMP_FILE_NAME,
+ .dso_name = DSO__NAME_VDSOX32,
+ .read_prog = "perf-read-vdsox32",
+ },
+#endif
};
return memdup(&vdso_info_init, sizeof(vdso_info_init));
@@ -92,6 +110,12 @@ void vdso__exit(struct machine *machine)
if (vdso_info->vdso.found)
unlink(vdso_info->vdso.temp_file_name);
+#if BITS_PER_LONG == 64
+ if (vdso_info->vdso32.found)
+ unlink(vdso_info->vdso32.temp_file_name);
+ if (vdso_info->vdsox32.found)
+ unlink(vdso_info->vdsox32.temp_file_name);
+#endif
zfree(&machine->vdso_info);
}
@@ -110,6 +134,143 @@ static struct dso *vdso__new(struct machine *machine, const char *short_name,
return dso;
}
+#if BITS_PER_LONG == 64
+
+static enum dso_type machine__thread_dso_type(struct machine *machine,
+ struct thread *thread)
+{
+ enum dso_type dso_type = DSO__TYPE_UNKNOWN;
+ struct map *map;
+ struct dso *dso;
+
+ map = map_groups__first(thread->mg, MAP__FUNCTION);
+ for (; map ; map = map_groups__next(map)) {
+ dso = map->dso;
+ if (!dso || dso->long_name[0] != '/')
+ continue;
+ dso_type = dso__type(dso, machine);
+ if (dso_type != DSO__TYPE_UNKNOWN)
+ break;
+ }
+
+ return dso_type;
+}
+
+static int vdso__do_copy_compat(FILE *f, int fd)
+{
+ char buf[4096];
+ size_t count;
+
+ while (1) {
+ count = fread(buf, 1, sizeof(buf), f);
+ if (ferror(f))
+ return -errno;
+ if (feof(f))
+ break;
+ if (count && writen(fd, buf, count) != (ssize_t)count)
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int vdso__copy_compat(const char *prog, int fd)
+{
+ FILE *f;
+ int err;
+
+ f = popen(prog, "r");
+ if (!f)
+ return -errno;
+
+ err = vdso__do_copy_compat(f, fd);
+
+ if (pclose(f) == -1)
+ return -errno;
+
+ return err;
+}
+
+static int vdso__create_compat_file(const char *prog, char *temp_name)
+{
+ int fd, err;
+
+ fd = mkstemp(temp_name);
+ if (fd < 0)
+ return -errno;
+
+ err = vdso__copy_compat(prog, fd);
+
+ if (close(fd) == -1)
+ return -errno;
+
+ return err;
+}
+
+static const char *vdso__get_compat_file(struct vdso_file *vdso_file)
+{
+ int err;
+
+ if (vdso_file->found)
+ return vdso_file->temp_file_name;
+
+ if (vdso_file->error)
+ return NULL;
+
+ err = vdso__create_compat_file(vdso_file->read_prog,
+ vdso_file->temp_file_name);
+ if (err) {
+ pr_err("%s failed, error %d\n", vdso_file->read_prog, err);
+ vdso_file->error = true;
+ return NULL;
+ }
+
+ vdso_file->found = true;
+
+ return vdso_file->temp_file_name;
+}
+
+static struct dso *vdso__findnew_compat(struct machine *machine,
+ struct vdso_file *vdso_file)
+{
+ const char *file_name;
+ struct dso *dso;
+
+ dso = dsos__find(&machine->user_dsos, vdso_file->dso_name, true);
+ if (dso)
+ return dso;
+
+ file_name = vdso__get_compat_file(vdso_file);
+ if (!file_name)
+ return NULL;
+
+ return vdso__new(machine, vdso_file->dso_name, file_name);
+}
+
+static int vdso__dso_findnew_compat(struct machine *machine,
+ struct thread *thread,
+ struct vdso_info *vdso_info,
+ struct dso **dso)
+{
+ enum dso_type dso_type;
+
+ dso_type = machine__thread_dso_type(machine, thread);
+ switch (dso_type) {
+ case DSO__TYPE_32BIT:
+ *dso = vdso__findnew_compat(machine, &vdso_info->vdso32);
+ return 1;
+ case DSO__TYPE_X32BIT:
+ *dso = vdso__findnew_compat(machine, &vdso_info->vdsox32);
+ return 1;
+ case DSO__TYPE_UNKNOWN:
+ case DSO__TYPE_64BIT:
+ default:
+ return 0;
+ }
+}
+
+#endif
+
struct dso *vdso__dso_findnew(struct machine *machine,
struct thread *thread __maybe_unused)
{
@@ -123,6 +284,11 @@ struct dso *vdso__dso_findnew(struct machine *machine,
if (!vdso_info)
return NULL;
+#if BITS_PER_LONG == 64
+ if (vdso__dso_findnew_compat(machine, thread, vdso_info, &dso))
+ return dso;
+#endif
+
dso = dsos__find(&machine->user_dsos, DSO__NAME_VDSO, true);
if (!dso) {
char *file;
@@ -139,5 +305,7 @@ struct dso *vdso__dso_findnew(struct machine *machine,
bool dso__is_vdso(struct dso *dso)
{
- return !strcmp(dso->short_name, DSO__NAME_VDSO);
+ return !strcmp(dso->short_name, DSO__NAME_VDSO) ||
+ !strcmp(dso->short_name, DSO__NAME_VDSO32) ||
+ !strcmp(dso->short_name, DSO__NAME_VDSOX32);
}
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h
index af9d692..d97da16 100644
--- a/tools/perf/util/vdso.h
+++ b/tools/perf/util/vdso.h
@@ -7,7 +7,9 @@
#define VDSO__MAP_NAME "[vdso]"
-#define DSO__NAME_VDSO "[vdso]"
+#define DSO__NAME_VDSO "[vdso]"
+#define DSO__NAME_VDSO32 "[vdso32]"
+#define DSO__NAME_VDSOX32 "[vdsox32]"
static inline bool is_vdso_map(const char *filename)
{
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 24/24] perf tools: Add feature checks to .gitignore
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
` (22 preceding siblings ...)
2014-08-15 19:08 ` [PATCH 23/24] perf tools: Add support for " Adrian Hunter
@ 2014-08-15 19:08 ` Adrian Hunter
23 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-15 19:08 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
From: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
tools/perf/.gitignore | 1 +
1 file changed, 1 insertion(+)
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index 717221e..af50829 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -25,3 +25,4 @@ config.mak.autogen
*-flex.*
*.pyc
*.pyo
+config/feature-checks/test-*
--
1.8.3.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 01/24] perf tools: Add a test for tracking with sched_switch
2014-08-15 19:08 ` [PATCH 01/24] perf tools: Add a test for tracking with sched_switch Adrian Hunter
@ 2014-08-20 19:48 ` Arnaldo Carvalho de Melo
2014-08-29 13:52 ` Adrian Hunter
2014-08-21 16:58 ` Arnaldo Carvalho de Melo
2014-08-24 14:58 ` [tip:perf/core] perf tests: " tip-bot for Adrian Hunter
2 siblings, 1 reply; 40+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-08-20 19:48 UTC (permalink / raw)
To: Adrian Hunter
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Em Fri, Aug 15, 2014 at 10:08:36PM +0300, Adrian Hunter escreveu:
> Add a test that checks that sched_switch events and
> tracking events can be recorded for a workload using the
> evsel->system_wide and evsel->tracking flags (respectively)
> with other events sometimes enabled or disabled.
Really nice exercise! Gives lots of evlist/evsel routines a good test,
checking for expected behaviour, really good, thanks.
One thing I noticed was that it uses parse_events() for creating events,
a perhaps simpler equivalent would be:
switch_evsel = perf_evsel__newtp("sched", "sched_switch");
And then go on, like you did, configuring whatever attribute one wants
to have set, like what you get from ":u", and the other things you
touched, like:
switch_evsel->system_wide = true;
switch_evsel->no_aux_samples = true;
switch_evsel->immediate = true;
And when the evsel is all set up, if dealing with evlists, like in this
case, just do a:
perf_evlist__add(evlist, switch_evsel);
Looks more natural than using parse_events(evlist, "sched:sched_switch");
To then infer that it must be the last entry at this point to retrieve
it using:
switch_evsel = perf_evlist__last(evlist);
But, for the current architecture, that should be just a clarification,
not a requirement for this specific test.
Applying!
- Arnaldo
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
> tools/perf/Makefile.perf | 1 +
> tools/perf/tests/builtin-test.c | 4 +
> tools/perf/tests/switch-tracking.c | 572 +++++++++++++++++++++++++++++++++++++
> tools/perf/tests/tests.h | 1 +
> 4 files changed, 578 insertions(+)
> create mode 100644 tools/perf/tests/switch-tracking.c
>
> diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
> index 1ea31e2..95e832b 100644
> --- a/tools/perf/Makefile.perf
> +++ b/tools/perf/Makefile.perf
> @@ -425,6 +425,7 @@ endif
> endif
> LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o
> LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o
> +LIB_OBJS += $(OUTPUT)tests/switch-tracking.o
>
> BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
> BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
> diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
> index 9948136..6a4145e 100644
> --- a/tools/perf/tests/builtin-test.c
> +++ b/tools/perf/tests/builtin-test.c
> @@ -154,6 +154,10 @@ static struct test {
> .func = test__hists_cumulate,
> },
> {
> + .desc = "Test tracking with sched_switch",
> + .func = test__switch_tracking,
> + },
> + {
> .func = NULL,
> },
> };
> diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c
> new file mode 100644
> index 0000000..50c82d5
> --- /dev/null
> +++ b/tools/perf/tests/switch-tracking.c
> @@ -0,0 +1,572 @@
> +#include <sys/time.h>
> +#include <sys/prctl.h>
> +#include <time.h>
> +#include <stdlib.h>
> +
> +#include "parse-events.h"
> +#include "evlist.h"
> +#include "evsel.h"
> +#include "thread_map.h"
> +#include "cpumap.h"
> +#include "tests.h"
> +
> +static int spin_sleep(void)
> +{
> + struct timeval start, now, diff, maxtime;
> + struct timespec ts;
> + int err, i;
> +
> + maxtime.tv_sec = 0;
> + maxtime.tv_usec = 50000;
> +
> + err = gettimeofday(&start, NULL);
> + if (err)
> + return err;
> +
> + /* Spin for 50ms */
> + while (1) {
> + for (i = 0; i < 1000; i++)
> + barrier();
> +
> + err = gettimeofday(&now, NULL);
> + if (err)
> + return err;
> +
> + timersub(&now, &start, &diff);
> + if (timercmp(&diff, &maxtime, > /* For checkpatch */))
> + break;
> + }
> +
> + ts.tv_nsec = 50 * 1000 * 1000;
> + ts.tv_sec = 0;
> +
> + /* Sleep for 50ms */
> + err = nanosleep(&ts, NULL);
> + if (err == EINTR)
> + err = 0;
> +
> + return err;
> +}
> +
> +struct switch_tracking {
> + struct perf_evsel *switch_evsel;
> + struct perf_evsel *cycles_evsel;
> + pid_t *tids;
> + int nr_tids;
> + int comm_seen[4];
> + int cycles_before_comm_1;
> + int cycles_between_comm_2_and_comm_3;
> + int cycles_after_comm_4;
> +};
> +
> +static int check_comm(struct switch_tracking *switch_tracking,
> + union perf_event *event, const char *comm, int nr)
> +{
> + if (event->header.type == PERF_RECORD_COMM &&
> + (pid_t)event->comm.pid == getpid() &&
> + (pid_t)event->comm.tid == getpid() &&
> + strcmp(event->comm.comm, comm) == 0) {
> + if (switch_tracking->comm_seen[nr]) {
> + pr_debug("Duplicate comm event\n");
> + return -1;
> + }
> + switch_tracking->comm_seen[nr] = 1;
> + pr_debug3("comm event: %s nr: %d\n", event->comm.comm, nr);
> + return 1;
> + }
> + return 0;
> +}
> +
> +static int check_cpu(struct switch_tracking *switch_tracking, int cpu)
> +{
> + int i, nr = cpu + 1;
> +
> + if (cpu < 0)
> + return -1;
> +
> + if (!switch_tracking->tids) {
> + switch_tracking->tids = calloc(nr, sizeof(pid_t));
> + if (!switch_tracking->tids)
> + return -1;
> + for (i = 0; i < nr; i++)
> + switch_tracking->tids[i] = -1;
> + switch_tracking->nr_tids = nr;
> + return 0;
> + }
> +
> + if (cpu >= switch_tracking->nr_tids) {
> + void *addr;
> +
> + addr = realloc(switch_tracking->tids, nr * sizeof(pid_t));
> + if (!addr)
> + return -1;
> + switch_tracking->tids = addr;
> + for (i = switch_tracking->nr_tids; i < nr; i++)
> + switch_tracking->tids[i] = -1;
> + switch_tracking->nr_tids = nr;
> + return 0;
> + }
> +
> + return 0;
> +}
> +
> +static int process_sample_event(struct perf_evlist *evlist,
> + union perf_event *event,
> + struct switch_tracking *switch_tracking)
> +{
> + struct perf_sample sample;
> + struct perf_evsel *evsel;
> + pid_t next_tid, prev_tid;
> + int cpu, err;
> +
> + if (perf_evlist__parse_sample(evlist, event, &sample)) {
> + pr_debug("perf_evlist__parse_sample failed\n");
> + return -1;
> + }
> +
> + evsel = perf_evlist__id2evsel(evlist, sample.id);
> + if (evsel == switch_tracking->switch_evsel) {
> + next_tid = perf_evsel__intval(evsel, &sample, "next_pid");
> + prev_tid = perf_evsel__intval(evsel, &sample, "prev_pid");
> + cpu = sample.cpu;
> + pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n",
> + cpu, prev_tid, next_tid);
> + err = check_cpu(switch_tracking, cpu);
> + if (err)
> + return err;
> + /*
> + * Check for no missing sched_switch events i.e. that the
> + * evsel->system_wide flag has worked.
> + */
> + if (switch_tracking->tids[cpu] != -1 &&
> + switch_tracking->tids[cpu] != prev_tid) {
> + pr_debug("Missing sched_switch events\n");
> + return -1;
> + }
> + switch_tracking->tids[cpu] = next_tid;
> + }
> +
> + if (evsel == switch_tracking->cycles_evsel) {
> + pr_debug3("cycles event\n");
> + if (!switch_tracking->comm_seen[0])
> + switch_tracking->cycles_before_comm_1 = 1;
> + if (switch_tracking->comm_seen[1] &&
> + !switch_tracking->comm_seen[2])
> + switch_tracking->cycles_between_comm_2_and_comm_3 = 1;
> + if (switch_tracking->comm_seen[3])
> + switch_tracking->cycles_after_comm_4 = 1;
> + }
> +
> + return 0;
> +}
> +
> +static int process_event(struct perf_evlist *evlist, union perf_event *event,
> + struct switch_tracking *switch_tracking)
> +{
> + if (event->header.type == PERF_RECORD_SAMPLE)
> + return process_sample_event(evlist, event, switch_tracking);
> +
> + if (event->header.type == PERF_RECORD_COMM) {
> + int err, done = 0;
> +
> + err = check_comm(switch_tracking, event, "Test COMM 1", 0);
> + if (err < 0)
> + return -1;
> + done += err;
> + err = check_comm(switch_tracking, event, "Test COMM 2", 1);
> + if (err < 0)
> + return -1;
> + done += err;
> + err = check_comm(switch_tracking, event, "Test COMM 3", 2);
> + if (err < 0)
> + return -1;
> + done += err;
> + err = check_comm(switch_tracking, event, "Test COMM 4", 3);
> + if (err < 0)
> + return -1;
> + done += err;
> + if (done != 1) {
> + pr_debug("Unexpected comm event\n");
> + return -1;
> + }
> + }
> +
> + return 0;
> +}
> +
> +struct event_node {
> + struct list_head list;
> + union perf_event *event;
> + u64 event_time;
> +};
> +
> +static int add_event(struct perf_evlist *evlist, struct list_head *events,
> + union perf_event *event)
> +{
> + struct perf_sample sample;
> + struct event_node *node;
> +
> + node = malloc(sizeof(struct event_node));
> + if (!node) {
> + pr_debug("malloc failed\n");
> + return -1;
> + }
> + node->event = event;
> + list_add(&node->list, events);
> +
> + if (perf_evlist__parse_sample(evlist, event, &sample)) {
> + pr_debug("perf_evlist__parse_sample failed\n");
> + return -1;
> + }
> +
> + if (!sample.time) {
> + pr_debug("event with no time\n");
> + return -1;
> + }
> +
> + node->event_time = sample.time;
> +
> + return 0;
> +}
> +
> +static void free_event_nodes(struct list_head *events)
> +{
> + struct event_node *node;
> +
> + while (!list_empty(events)) {
> + node = list_entry(events->next, struct event_node, list);
> + list_del(&node->list);
> + free(node);
> + }
> +}
> +
> +static int compar(const void *a, const void *b)
> +{
> + const struct event_node *nodea = a;
> + const struct event_node *nodeb = b;
> + s64 cmp = nodea->event_time - nodeb->event_time;
> +
> + return cmp;
> +}
> +
> +static int process_events(struct perf_evlist *evlist,
> + struct switch_tracking *switch_tracking)
> +{
> + union perf_event *event;
> + unsigned pos, cnt = 0;
> + LIST_HEAD(events);
> + struct event_node *events_array, *node;
> + int i, ret;
> +
> + for (i = 0; i < evlist->nr_mmaps; i++) {
> + while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
> + cnt += 1;
> + ret = add_event(evlist, &events, event);
> + perf_evlist__mmap_consume(evlist, i);
> + if (ret < 0)
> + goto out_free_nodes;
> + }
> + }
> +
> + events_array = calloc(cnt, sizeof(struct event_node));
> + if (!events_array) {
> + pr_debug("calloc failed\n");
> + ret = -1;
> + goto out_free_nodes;
> + }
> +
> + pos = 0;
> + list_for_each_entry(node, &events, list)
> + events_array[pos++] = *node;
> +
> + qsort(events_array, cnt, sizeof(struct event_node), compar);
> +
> + for (pos = 0; pos < cnt; pos++) {
> + ret = process_event(evlist, events_array[pos].event,
> + switch_tracking);
> + if (ret < 0)
> + goto out_free;
> + }
> +
> + ret = 0;
> +out_free:
> + pr_debug("%u events recorded\n", cnt);
> + free(events_array);
> +out_free_nodes:
> + free_event_nodes(&events);
> + return ret;
> +}
> +
> +/**
> + * test__switch_tracking - test using sched_switch and tracking events.
> + *
> + * This function implements a test that checks that sched_switch events and
> + * tracking events can be recorded for a workload (current process) using the
> + * evsel->system_wide and evsel->tracking flags (respectively) with other events
> + * sometimes enabled or disabled.
> + */
> +int test__switch_tracking(void)
> +{
> + const char *sched_switch = "sched:sched_switch";
> + struct switch_tracking switch_tracking = {0};
> + struct record_opts opts = {
> + .mmap_pages = UINT_MAX,
> + .user_freq = UINT_MAX,
> + .user_interval = ULLONG_MAX,
> + .freq = 4000,
> + .target = {
> + .uses_mmap = true,
> + },
> + };
> + struct thread_map *threads = NULL;
> + struct cpu_map *cpus = NULL;
> + struct perf_evlist *evlist = NULL;
> + struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel;
> + struct perf_evsel *switch_evsel, *tracking_evsel;
> + const char *comm;
> + int err = -1;
> +
> + threads = thread_map__new(-1, getpid(), UINT_MAX);
> + if (!threads) {
> + pr_debug("thread_map__new failed!\n");
> + goto out_err;
> + }
> +
> + cpus = cpu_map__new(NULL);
> + if (!cpus) {
> + pr_debug("cpu_map__new failed!\n");
> + goto out_err;
> + }
> +
> + evlist = perf_evlist__new();
> + if (!evlist) {
> + pr_debug("perf_evlist__new failed!\n");
> + goto out_err;
> + }
> +
> + perf_evlist__set_maps(evlist, cpus, threads);
> +
> + /* First event */
> + err = parse_events(evlist, "cpu-clock:u");
> + if (err) {
> + pr_debug("Failed to parse event dummy:u\n");
> + goto out_err;
> + }
> +
> + cpu_clocks_evsel = perf_evlist__last(evlist);
> +
> + /* Second event */
> + err = parse_events(evlist, "cycles:u");
> + if (err) {
> + pr_debug("Failed to parse event cycles:u\n");
> + goto out_err;
> + }
> +
> + cycles_evsel = perf_evlist__last(evlist);
> +
> + /* Third event */
> + if (!perf_evlist__can_select_event(evlist, sched_switch)) {
> + fprintf(stderr, " (no sched_switch)");
> + err = 0;
> + goto out;
> + }
> +
> + err = parse_events(evlist, sched_switch);
> + if (err) {
> + pr_debug("Failed to parse event %s\n", sched_switch);
> + goto out_err;
> + }
> +
> + switch_evsel = perf_evlist__last(evlist);
> +
> + perf_evsel__set_sample_bit(switch_evsel, CPU);
> + perf_evsel__set_sample_bit(switch_evsel, TIME);
> +
> + switch_evsel->system_wide = true;
> + switch_evsel->no_aux_samples = true;
> + switch_evsel->immediate = true;
> +
> + /* Test moving an event to the front */
> + if (cycles_evsel == perf_evlist__first(evlist)) {
> + pr_debug("cycles event already at front");
> + goto out_err;
> + }
> + perf_evlist__to_front(evlist, cycles_evsel);
> + if (cycles_evsel != perf_evlist__first(evlist)) {
> + pr_debug("Failed to move cycles event to front");
> + goto out_err;
> + }
> +
> + perf_evsel__set_sample_bit(cycles_evsel, CPU);
> + perf_evsel__set_sample_bit(cycles_evsel, TIME);
> +
> + /* Fourth event */
> + err = parse_events(evlist, "dummy:u");
> + if (err) {
> + pr_debug("Failed to parse event dummy:u\n");
> + goto out_err;
> + }
> +
> + tracking_evsel = perf_evlist__last(evlist);
> +
> + perf_evlist__set_tracking_event(evlist, tracking_evsel);
> +
> + tracking_evsel->attr.freq = 0;
> + tracking_evsel->attr.sample_period = 1;
> +
> + perf_evsel__set_sample_bit(tracking_evsel, TIME);
> +
> + /* Config events */
> + perf_evlist__config(evlist, &opts);
> +
> + /* Check moved event is still at the front */
> + if (cycles_evsel != perf_evlist__first(evlist)) {
> + pr_debug("Front event no longer at front");
> + goto out_err;
> + }
> +
> + /* Check tracking event is tracking */
> + if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) {
> + pr_debug("Tracking event not tracking\n");
> + goto out_err;
> + }
> +
> + /* Check non-tracking events are not tracking */
> + evlist__for_each(evlist, evsel) {
> + if (evsel != tracking_evsel) {
> + if (evsel->attr.mmap || evsel->attr.comm) {
> + pr_debug("Non-tracking event is tracking\n");
> + goto out_err;
> + }
> + }
> + }
> +
> + if (perf_evlist__open(evlist) < 0) {
> + fprintf(stderr, " (not supported)");
> + err = 0;
> + goto out;
> + }
> +
> + err = perf_evlist__mmap(evlist, UINT_MAX, false);
> + if (err) {
> + pr_debug("perf_evlist__mmap failed!\n");
> + goto out_err;
> + }
> +
> + perf_evlist__enable(evlist);
> +
> + err = perf_evlist__disable_event(evlist, cpu_clocks_evsel);
> + if (err) {
> + pr_debug("perf_evlist__disable_event failed!\n");
> + goto out_err;
> + }
> +
> + err = spin_sleep();
> + if (err) {
> + pr_debug("spin_sleep failed!\n");
> + goto out_err;
> + }
> +
> + comm = "Test COMM 1";
> + err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
> + if (err) {
> + pr_debug("PR_SET_NAME failed!\n");
> + goto out_err;
> + }
> +
> + err = perf_evlist__disable_event(evlist, cycles_evsel);
> + if (err) {
> + pr_debug("perf_evlist__disable_event failed!\n");
> + goto out_err;
> + }
> +
> + comm = "Test COMM 2";
> + err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
> + if (err) {
> + pr_debug("PR_SET_NAME failed!\n");
> + goto out_err;
> + }
> +
> + err = spin_sleep();
> + if (err) {
> + pr_debug("spin_sleep failed!\n");
> + goto out_err;
> + }
> +
> + comm = "Test COMM 3";
> + err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
> + if (err) {
> + pr_debug("PR_SET_NAME failed!\n");
> + goto out_err;
> + }
> +
> + err = perf_evlist__enable_event(evlist, cycles_evsel);
> + if (err) {
> + pr_debug("perf_evlist__disable_event failed!\n");
> + goto out_err;
> + }
> +
> + comm = "Test COMM 4";
> + err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
> + if (err) {
> + pr_debug("PR_SET_NAME failed!\n");
> + goto out_err;
> + }
> +
> + err = spin_sleep();
> + if (err) {
> + pr_debug("spin_sleep failed!\n");
> + goto out_err;
> + }
> +
> + perf_evlist__disable(evlist);
> +
> + switch_tracking.switch_evsel = switch_evsel;
> + switch_tracking.cycles_evsel = cycles_evsel;
> +
> + err = process_events(evlist, &switch_tracking);
> +
> + zfree(&switch_tracking.tids);
> +
> + if (err)
> + goto out_err;
> +
> + /* Check all 4 comm events were seen i.e. that evsel->tracking works */
> + if (!switch_tracking.comm_seen[0] || !switch_tracking.comm_seen[1] ||
> + !switch_tracking.comm_seen[2] || !switch_tracking.comm_seen[3]) {
> + pr_debug("Missing comm events\n");
> + goto out_err;
> + }
> +
> + /* Check cycles event got enabled */
> + if (!switch_tracking.cycles_before_comm_1) {
> + pr_debug("Missing cycles events\n");
> + goto out_err;
> + }
> +
> + /* Check cycles event got disabled */
> + if (switch_tracking.cycles_between_comm_2_and_comm_3) {
> + pr_debug("cycles events even though event was disabled\n");
> + goto out_err;
> + }
> +
> + /* Check cycles event got enabled again */
> + if (!switch_tracking.cycles_after_comm_4) {
> + pr_debug("Missing cycles events\n");
> + goto out_err;
> + }
> +out:
> + if (evlist) {
> + perf_evlist__disable(evlist);
> + perf_evlist__delete(evlist);
> + } else {
> + cpu_map__delete(cpus);
> + thread_map__delete(threads);
> + }
> +
> + return err;
> +
> +out_err:
> + err = -1;
> + goto out;
> +}
> diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
> index ed64790..be8be10 100644
> --- a/tools/perf/tests/tests.h
> +++ b/tools/perf/tests/tests.h
> @@ -48,6 +48,7 @@ int test__mmap_thread_lookup(void);
> int test__thread_mg_share(void);
> int test__hists_output(void);
> int test__hists_cumulate(void);
> +int test__switch_tracking(void);
>
> #if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
> #ifdef HAVE_DWARF_UNWIND_SUPPORT
> --
> 1.8.3.2
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 01/24] perf tools: Add a test for tracking with sched_switch
2014-08-15 19:08 ` [PATCH 01/24] perf tools: Add a test for tracking with sched_switch Adrian Hunter
2014-08-20 19:48 ` Arnaldo Carvalho de Melo
@ 2014-08-21 16:58 ` Arnaldo Carvalho de Melo
2014-08-24 14:58 ` [tip:perf/core] perf tests: " tip-bot for Adrian Hunter
2 siblings, 0 replies; 40+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-08-21 16:58 UTC (permalink / raw)
To: Adrian Hunter
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Em Fri, Aug 15, 2014 at 10:08:36PM +0300, Adrian Hunter escreveu:
> + * test__switch_tracking - test using sched_switch and tracking events.
> + *
> + * This function implements a test that checks that sched_switch events and
> + * tracking events can be recorded for a workload (current process) using the
> + * evsel->system_wide and evsel->tracking flags (respectively) with other events
> + * sometimes enabled or disabled.
> + */
> +int test__switch_tracking(void)
> +{
> + const char *sched_switch = "sched:sched_switch";
> + struct switch_tracking switch_tracking = {0};
> + struct record_opts opts = {
308 int test__switch_tracking(void)
309 {
310 const char *sched_switch = "sched:sched_switch";
311 struct switch_tracking switch_tracking = {0};
312 struct record_opts opts = {
313 .mmap_pages = UINT_MAX,
314 .user_freq = UINT_MAX,
315 .user_interval = ULLONG_MAX,
316 .freq = 4000,
317 .target = {
318 .uses_mmap = true,
319 },
320 };
321 struct thread_map *threads = NULL;
Fails to build on f14, still building on other distros, will fix after lunch,
no need to send any fix, just FYI:
CC /tmp/build/perf/arch/x86/util/header.o
cc1: warnings being treated as errors
tests/switch-tracking.c: In function ‘test__switch_tracking’:
tests/switch-tracking.c:311:9: error: missing initializer
tests/switch-tracking.c:311:9: error: (near initialization for ‘switch_tracking.cycles_evsel’)
make[1]: *** [/tmp/build/perf/tests/switch-tracking.o] Error 1
make[1]: *** Waiting for unfinished jobs....
make: *** [install] Error 2
make: Leaving directory `/home/acme/git/linux/tools/perf'
real 0m19.399s
user 0m53.823s
sys 0m7.346s
[acme@fedora14 linux]$ fg
-bash: fg: current: no such job
[acme@fedora14
^ permalink raw reply [flat|nested] 40+ messages in thread
* [tip:perf/core] perf tests: Add a test for tracking with sched_switch
2014-08-15 19:08 ` [PATCH 01/24] perf tools: Add a test for tracking with sched_switch Adrian Hunter
2014-08-20 19:48 ` Arnaldo Carvalho de Melo
2014-08-21 16:58 ` Arnaldo Carvalho de Melo
@ 2014-08-24 14:58 ` tip-bot for Adrian Hunter
2 siblings, 0 replies; 40+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-08-24 14:58 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, eranian, paulus, hpa, mingo, peterz,
namhyung, jolsa, fweisbec, adrian.hunter, dsahern, tglx
Commit-ID: d44bc558297222d9a621fff2eca3f880e91c49f7
Gitweb: http://git.kernel.org/tip/d44bc558297222d9a621fff2eca3f880e91c49f7
Author: Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Fri, 15 Aug 2014 22:08:36 +0300
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 22 Aug 2014 13:10:35 -0300
perf tests: Add a test for tracking with sched_switch
Add a test that checks that sched_switch events and tracking events can
be recorded for a workload using the evsel->system_wide and
evsel->tracking flags (respectively) with other events sometimes enabled
or disabled.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1408129739-17368-2-git-send-email-adrian.hunter@intel.com
[ Fix build on fedora14 by using a designated initializer for the sched_switch variable ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/Makefile.perf | 1 +
tools/perf/tests/builtin-test.c | 4 +
tools/perf/tests/switch-tracking.c | 572 +++++++++++++++++++++++++++++++++++++
tools/perf/tests/tests.h | 1 +
4 files changed, 578 insertions(+)
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 1ea31e2..95e832b 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -425,6 +425,7 @@ endif
endif
LIB_OBJS += $(OUTPUT)tests/mmap-thread-lookup.o
LIB_OBJS += $(OUTPUT)tests/thread-mg-share.o
+LIB_OBJS += $(OUTPUT)tests/switch-tracking.o
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 9948136..6a4145e 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -154,6 +154,10 @@ static struct test {
.func = test__hists_cumulate,
},
{
+ .desc = "Test tracking with sched_switch",
+ .func = test__switch_tracking,
+ },
+ {
.func = NULL,
},
};
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c
new file mode 100644
index 0000000..cc68648
--- /dev/null
+++ b/tools/perf/tests/switch-tracking.c
@@ -0,0 +1,572 @@
+#include <sys/time.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include "parse-events.h"
+#include "evlist.h"
+#include "evsel.h"
+#include "thread_map.h"
+#include "cpumap.h"
+#include "tests.h"
+
+static int spin_sleep(void)
+{
+ struct timeval start, now, diff, maxtime;
+ struct timespec ts;
+ int err, i;
+
+ maxtime.tv_sec = 0;
+ maxtime.tv_usec = 50000;
+
+ err = gettimeofday(&start, NULL);
+ if (err)
+ return err;
+
+ /* Spin for 50ms */
+ while (1) {
+ for (i = 0; i < 1000; i++)
+ barrier();
+
+ err = gettimeofday(&now, NULL);
+ if (err)
+ return err;
+
+ timersub(&now, &start, &diff);
+ if (timercmp(&diff, &maxtime, > /* For checkpatch */))
+ break;
+ }
+
+ ts.tv_nsec = 50 * 1000 * 1000;
+ ts.tv_sec = 0;
+
+ /* Sleep for 50ms */
+ err = nanosleep(&ts, NULL);
+ if (err == EINTR)
+ err = 0;
+
+ return err;
+}
+
+struct switch_tracking {
+ struct perf_evsel *switch_evsel;
+ struct perf_evsel *cycles_evsel;
+ pid_t *tids;
+ int nr_tids;
+ int comm_seen[4];
+ int cycles_before_comm_1;
+ int cycles_between_comm_2_and_comm_3;
+ int cycles_after_comm_4;
+};
+
+static int check_comm(struct switch_tracking *switch_tracking,
+ union perf_event *event, const char *comm, int nr)
+{
+ if (event->header.type == PERF_RECORD_COMM &&
+ (pid_t)event->comm.pid == getpid() &&
+ (pid_t)event->comm.tid == getpid() &&
+ strcmp(event->comm.comm, comm) == 0) {
+ if (switch_tracking->comm_seen[nr]) {
+ pr_debug("Duplicate comm event\n");
+ return -1;
+ }
+ switch_tracking->comm_seen[nr] = 1;
+ pr_debug3("comm event: %s nr: %d\n", event->comm.comm, nr);
+ return 1;
+ }
+ return 0;
+}
+
+static int check_cpu(struct switch_tracking *switch_tracking, int cpu)
+{
+ int i, nr = cpu + 1;
+
+ if (cpu < 0)
+ return -1;
+
+ if (!switch_tracking->tids) {
+ switch_tracking->tids = calloc(nr, sizeof(pid_t));
+ if (!switch_tracking->tids)
+ return -1;
+ for (i = 0; i < nr; i++)
+ switch_tracking->tids[i] = -1;
+ switch_tracking->nr_tids = nr;
+ return 0;
+ }
+
+ if (cpu >= switch_tracking->nr_tids) {
+ void *addr;
+
+ addr = realloc(switch_tracking->tids, nr * sizeof(pid_t));
+ if (!addr)
+ return -1;
+ switch_tracking->tids = addr;
+ for (i = switch_tracking->nr_tids; i < nr; i++)
+ switch_tracking->tids[i] = -1;
+ switch_tracking->nr_tids = nr;
+ return 0;
+ }
+
+ return 0;
+}
+
+static int process_sample_event(struct perf_evlist *evlist,
+ union perf_event *event,
+ struct switch_tracking *switch_tracking)
+{
+ struct perf_sample sample;
+ struct perf_evsel *evsel;
+ pid_t next_tid, prev_tid;
+ int cpu, err;
+
+ if (perf_evlist__parse_sample(evlist, event, &sample)) {
+ pr_debug("perf_evlist__parse_sample failed\n");
+ return -1;
+ }
+
+ evsel = perf_evlist__id2evsel(evlist, sample.id);
+ if (evsel == switch_tracking->switch_evsel) {
+ next_tid = perf_evsel__intval(evsel, &sample, "next_pid");
+ prev_tid = perf_evsel__intval(evsel, &sample, "prev_pid");
+ cpu = sample.cpu;
+ pr_debug3("sched_switch: cpu: %d prev_tid %d next_tid %d\n",
+ cpu, prev_tid, next_tid);
+ err = check_cpu(switch_tracking, cpu);
+ if (err)
+ return err;
+ /*
+ * Check for no missing sched_switch events i.e. that the
+ * evsel->system_wide flag has worked.
+ */
+ if (switch_tracking->tids[cpu] != -1 &&
+ switch_tracking->tids[cpu] != prev_tid) {
+ pr_debug("Missing sched_switch events\n");
+ return -1;
+ }
+ switch_tracking->tids[cpu] = next_tid;
+ }
+
+ if (evsel == switch_tracking->cycles_evsel) {
+ pr_debug3("cycles event\n");
+ if (!switch_tracking->comm_seen[0])
+ switch_tracking->cycles_before_comm_1 = 1;
+ if (switch_tracking->comm_seen[1] &&
+ !switch_tracking->comm_seen[2])
+ switch_tracking->cycles_between_comm_2_and_comm_3 = 1;
+ if (switch_tracking->comm_seen[3])
+ switch_tracking->cycles_after_comm_4 = 1;
+ }
+
+ return 0;
+}
+
+static int process_event(struct perf_evlist *evlist, union perf_event *event,
+ struct switch_tracking *switch_tracking)
+{
+ if (event->header.type == PERF_RECORD_SAMPLE)
+ return process_sample_event(evlist, event, switch_tracking);
+
+ if (event->header.type == PERF_RECORD_COMM) {
+ int err, done = 0;
+
+ err = check_comm(switch_tracking, event, "Test COMM 1", 0);
+ if (err < 0)
+ return -1;
+ done += err;
+ err = check_comm(switch_tracking, event, "Test COMM 2", 1);
+ if (err < 0)
+ return -1;
+ done += err;
+ err = check_comm(switch_tracking, event, "Test COMM 3", 2);
+ if (err < 0)
+ return -1;
+ done += err;
+ err = check_comm(switch_tracking, event, "Test COMM 4", 3);
+ if (err < 0)
+ return -1;
+ done += err;
+ if (done != 1) {
+ pr_debug("Unexpected comm event\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+struct event_node {
+ struct list_head list;
+ union perf_event *event;
+ u64 event_time;
+};
+
+static int add_event(struct perf_evlist *evlist, struct list_head *events,
+ union perf_event *event)
+{
+ struct perf_sample sample;
+ struct event_node *node;
+
+ node = malloc(sizeof(struct event_node));
+ if (!node) {
+ pr_debug("malloc failed\n");
+ return -1;
+ }
+ node->event = event;
+ list_add(&node->list, events);
+
+ if (perf_evlist__parse_sample(evlist, event, &sample)) {
+ pr_debug("perf_evlist__parse_sample failed\n");
+ return -1;
+ }
+
+ if (!sample.time) {
+ pr_debug("event with no time\n");
+ return -1;
+ }
+
+ node->event_time = sample.time;
+
+ return 0;
+}
+
+static void free_event_nodes(struct list_head *events)
+{
+ struct event_node *node;
+
+ while (!list_empty(events)) {
+ node = list_entry(events->next, struct event_node, list);
+ list_del(&node->list);
+ free(node);
+ }
+}
+
+static int compar(const void *a, const void *b)
+{
+ const struct event_node *nodea = a;
+ const struct event_node *nodeb = b;
+ s64 cmp = nodea->event_time - nodeb->event_time;
+
+ return cmp;
+}
+
+static int process_events(struct perf_evlist *evlist,
+ struct switch_tracking *switch_tracking)
+{
+ union perf_event *event;
+ unsigned pos, cnt = 0;
+ LIST_HEAD(events);
+ struct event_node *events_array, *node;
+ int i, ret;
+
+ for (i = 0; i < evlist->nr_mmaps; i++) {
+ while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+ cnt += 1;
+ ret = add_event(evlist, &events, event);
+ perf_evlist__mmap_consume(evlist, i);
+ if (ret < 0)
+ goto out_free_nodes;
+ }
+ }
+
+ events_array = calloc(cnt, sizeof(struct event_node));
+ if (!events_array) {
+ pr_debug("calloc failed\n");
+ ret = -1;
+ goto out_free_nodes;
+ }
+
+ pos = 0;
+ list_for_each_entry(node, &events, list)
+ events_array[pos++] = *node;
+
+ qsort(events_array, cnt, sizeof(struct event_node), compar);
+
+ for (pos = 0; pos < cnt; pos++) {
+ ret = process_event(evlist, events_array[pos].event,
+ switch_tracking);
+ if (ret < 0)
+ goto out_free;
+ }
+
+ ret = 0;
+out_free:
+ pr_debug("%u events recorded\n", cnt);
+ free(events_array);
+out_free_nodes:
+ free_event_nodes(&events);
+ return ret;
+}
+
+/**
+ * test__switch_tracking - test using sched_switch and tracking events.
+ *
+ * This function implements a test that checks that sched_switch events and
+ * tracking events can be recorded for a workload (current process) using the
+ * evsel->system_wide and evsel->tracking flags (respectively) with other events
+ * sometimes enabled or disabled.
+ */
+int test__switch_tracking(void)
+{
+ const char *sched_switch = "sched:sched_switch";
+ struct switch_tracking switch_tracking = { .tids = NULL, };
+ struct record_opts opts = {
+ .mmap_pages = UINT_MAX,
+ .user_freq = UINT_MAX,
+ .user_interval = ULLONG_MAX,
+ .freq = 4000,
+ .target = {
+ .uses_mmap = true,
+ },
+ };
+ struct thread_map *threads = NULL;
+ struct cpu_map *cpus = NULL;
+ struct perf_evlist *evlist = NULL;
+ struct perf_evsel *evsel, *cpu_clocks_evsel, *cycles_evsel;
+ struct perf_evsel *switch_evsel, *tracking_evsel;
+ const char *comm;
+ int err = -1;
+
+ threads = thread_map__new(-1, getpid(), UINT_MAX);
+ if (!threads) {
+ pr_debug("thread_map__new failed!\n");
+ goto out_err;
+ }
+
+ cpus = cpu_map__new(NULL);
+ if (!cpus) {
+ pr_debug("cpu_map__new failed!\n");
+ goto out_err;
+ }
+
+ evlist = perf_evlist__new();
+ if (!evlist) {
+ pr_debug("perf_evlist__new failed!\n");
+ goto out_err;
+ }
+
+ perf_evlist__set_maps(evlist, cpus, threads);
+
+ /* First event */
+ err = parse_events(evlist, "cpu-clock:u");
+ if (err) {
+ pr_debug("Failed to parse event dummy:u\n");
+ goto out_err;
+ }
+
+ cpu_clocks_evsel = perf_evlist__last(evlist);
+
+ /* Second event */
+ err = parse_events(evlist, "cycles:u");
+ if (err) {
+ pr_debug("Failed to parse event cycles:u\n");
+ goto out_err;
+ }
+
+ cycles_evsel = perf_evlist__last(evlist);
+
+ /* Third event */
+ if (!perf_evlist__can_select_event(evlist, sched_switch)) {
+ fprintf(stderr, " (no sched_switch)");
+ err = 0;
+ goto out;
+ }
+
+ err = parse_events(evlist, sched_switch);
+ if (err) {
+ pr_debug("Failed to parse event %s\n", sched_switch);
+ goto out_err;
+ }
+
+ switch_evsel = perf_evlist__last(evlist);
+
+ perf_evsel__set_sample_bit(switch_evsel, CPU);
+ perf_evsel__set_sample_bit(switch_evsel, TIME);
+
+ switch_evsel->system_wide = true;
+ switch_evsel->no_aux_samples = true;
+ switch_evsel->immediate = true;
+
+ /* Test moving an event to the front */
+ if (cycles_evsel == perf_evlist__first(evlist)) {
+ pr_debug("cycles event already at front");
+ goto out_err;
+ }
+ perf_evlist__to_front(evlist, cycles_evsel);
+ if (cycles_evsel != perf_evlist__first(evlist)) {
+ pr_debug("Failed to move cycles event to front");
+ goto out_err;
+ }
+
+ perf_evsel__set_sample_bit(cycles_evsel, CPU);
+ perf_evsel__set_sample_bit(cycles_evsel, TIME);
+
+ /* Fourth event */
+ err = parse_events(evlist, "dummy:u");
+ if (err) {
+ pr_debug("Failed to parse event dummy:u\n");
+ goto out_err;
+ }
+
+ tracking_evsel = perf_evlist__last(evlist);
+
+ perf_evlist__set_tracking_event(evlist, tracking_evsel);
+
+ tracking_evsel->attr.freq = 0;
+ tracking_evsel->attr.sample_period = 1;
+
+ perf_evsel__set_sample_bit(tracking_evsel, TIME);
+
+ /* Config events */
+ perf_evlist__config(evlist, &opts);
+
+ /* Check moved event is still at the front */
+ if (cycles_evsel != perf_evlist__first(evlist)) {
+ pr_debug("Front event no longer at front");
+ goto out_err;
+ }
+
+ /* Check tracking event is tracking */
+ if (!tracking_evsel->attr.mmap || !tracking_evsel->attr.comm) {
+ pr_debug("Tracking event not tracking\n");
+ goto out_err;
+ }
+
+ /* Check non-tracking events are not tracking */
+ evlist__for_each(evlist, evsel) {
+ if (evsel != tracking_evsel) {
+ if (evsel->attr.mmap || evsel->attr.comm) {
+ pr_debug("Non-tracking event is tracking\n");
+ goto out_err;
+ }
+ }
+ }
+
+ if (perf_evlist__open(evlist) < 0) {
+ fprintf(stderr, " (not supported)");
+ err = 0;
+ goto out;
+ }
+
+ err = perf_evlist__mmap(evlist, UINT_MAX, false);
+ if (err) {
+ pr_debug("perf_evlist__mmap failed!\n");
+ goto out_err;
+ }
+
+ perf_evlist__enable(evlist);
+
+ err = perf_evlist__disable_event(evlist, cpu_clocks_evsel);
+ if (err) {
+ pr_debug("perf_evlist__disable_event failed!\n");
+ goto out_err;
+ }
+
+ err = spin_sleep();
+ if (err) {
+ pr_debug("spin_sleep failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 1";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = perf_evlist__disable_event(evlist, cycles_evsel);
+ if (err) {
+ pr_debug("perf_evlist__disable_event failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 2";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = spin_sleep();
+ if (err) {
+ pr_debug("spin_sleep failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 3";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = perf_evlist__enable_event(evlist, cycles_evsel);
+ if (err) {
+ pr_debug("perf_evlist__disable_event failed!\n");
+ goto out_err;
+ }
+
+ comm = "Test COMM 4";
+ err = prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0);
+ if (err) {
+ pr_debug("PR_SET_NAME failed!\n");
+ goto out_err;
+ }
+
+ err = spin_sleep();
+ if (err) {
+ pr_debug("spin_sleep failed!\n");
+ goto out_err;
+ }
+
+ perf_evlist__disable(evlist);
+
+ switch_tracking.switch_evsel = switch_evsel;
+ switch_tracking.cycles_evsel = cycles_evsel;
+
+ err = process_events(evlist, &switch_tracking);
+
+ zfree(&switch_tracking.tids);
+
+ if (err)
+ goto out_err;
+
+ /* Check all 4 comm events were seen i.e. that evsel->tracking works */
+ if (!switch_tracking.comm_seen[0] || !switch_tracking.comm_seen[1] ||
+ !switch_tracking.comm_seen[2] || !switch_tracking.comm_seen[3]) {
+ pr_debug("Missing comm events\n");
+ goto out_err;
+ }
+
+ /* Check cycles event got enabled */
+ if (!switch_tracking.cycles_before_comm_1) {
+ pr_debug("Missing cycles events\n");
+ goto out_err;
+ }
+
+ /* Check cycles event got disabled */
+ if (switch_tracking.cycles_between_comm_2_and_comm_3) {
+ pr_debug("cycles events even though event was disabled\n");
+ goto out_err;
+ }
+
+ /* Check cycles event got enabled again */
+ if (!switch_tracking.cycles_after_comm_4) {
+ pr_debug("Missing cycles events\n");
+ goto out_err;
+ }
+out:
+ if (evlist) {
+ perf_evlist__disable(evlist);
+ perf_evlist__delete(evlist);
+ } else {
+ cpu_map__delete(cpus);
+ thread_map__delete(threads);
+ }
+
+ return err;
+
+out_err:
+ err = -1;
+ goto out;
+}
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index ed64790..be8be10 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -48,6 +48,7 @@ int test__mmap_thread_lookup(void);
int test__thread_mg_share(void);
int test__hists_output(void);
int test__hists_cumulate(void);
+int test__switch_tracking(void);
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
#ifdef HAVE_DWARF_UNWIND_SUPPORT
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [tip:perf/core] perf scripting: Add 'flush' callback to scripting API
2014-08-15 19:08 ` [PATCH 02/24] perf scripting: Add 'flush' callback to scripting API Adrian Hunter
@ 2014-08-24 14:58 ` tip-bot for Adrian Hunter
0 siblings, 0 replies; 40+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-08-24 14:58 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, eranian, paulus, hpa, mingo, peterz,
namhyung, jolsa, fweisbec, adrian.hunter, dsahern, tglx
Commit-ID: d445dd2a78eed884adf3b3426b078fe69d2516d8
Gitweb: http://git.kernel.org/tip/d445dd2a78eed884adf3b3426b078fe69d2516d8
Author: Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Fri, 15 Aug 2014 22:08:37 +0300
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 22 Aug 2014 13:12:11 -0300
perf scripting: Add 'flush' callback to scripting API
In order to defer some output via the scripting API, there needs to be a
callback after session processing but before the session is deleted.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1408129739-17368-3-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/builtin-script.c | 13 +++++++++++++
tools/perf/util/scripting-engines/trace-event-perl.c | 6 ++++++
tools/perf/util/scripting-engines/trace-event-python.c | 6 ++++++
tools/perf/util/trace-event-scripting.c | 7 +++++++
tools/perf/util/trace-event.h | 1 +
5 files changed, 33 insertions(+)
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index c1b7029..02dce92 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -485,6 +485,11 @@ static int default_start_script(const char *script __maybe_unused,
return 0;
}
+static int default_flush_script(void)
+{
+ return 0;
+}
+
static int default_stop_script(void)
{
return 0;
@@ -498,6 +503,7 @@ static int default_generate_script(struct pevent *pevent __maybe_unused,
static struct scripting_ops default_scripting_ops = {
.start_script = default_start_script,
+ .flush_script = default_flush_script,
.stop_script = default_stop_script,
.process_event = process_event,
.generate_script = default_generate_script,
@@ -513,6 +519,11 @@ static void setup_scripting(void)
scripting_ops = &default_scripting_ops;
}
+static int flush_scripting(void)
+{
+ return scripting_ops->flush_script();
+}
+
static int cleanup_scripting(void)
{
pr_debug("\nperf script stopped\n");
@@ -1813,6 +1824,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
err = __cmd_script(&script);
+ flush_scripting();
+
out_delete:
perf_session__delete(session);
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index b2dba9c..0a01bac 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -432,6 +432,11 @@ error:
return err;
}
+static int perl_flush_script(void)
+{
+ return 0;
+}
+
/*
* Stop trace script
*/
@@ -633,6 +638,7 @@ static int perl_generate_script(struct pevent *pevent, const char *outfile)
struct scripting_ops perl_scripting_ops = {
.name = "Perl",
.start_script = perl_start_script,
+ .flush_script = perl_flush_script,
.stop_script = perl_stop_script,
.process_event = perl_process_event,
.generate_script = perl_generate_script,
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 26e5f14..56ba07c 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -639,6 +639,11 @@ error:
return err;
}
+static int python_flush_script(void)
+{
+ return 0;
+}
+
/*
* Stop trace script
*/
@@ -823,6 +828,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile)
struct scripting_ops python_scripting_ops = {
.name = "Python",
.start_script = python_start_script,
+ .flush_script = python_flush_script,
.stop_script = python_stop_script,
.process_event = python_process_event,
.generate_script = python_generate_script,
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index 57aaccc..5c9bdd1 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -30,6 +30,11 @@
struct scripting_context *scripting_context;
+static int flush_script_unsupported(void)
+{
+ return 0;
+}
+
static int stop_script_unsupported(void)
{
return 0;
@@ -74,6 +79,7 @@ static int python_generate_script_unsupported(struct pevent *pevent
struct scripting_ops python_scripting_unsupported_ops = {
.name = "Python",
.start_script = python_start_script_unsupported,
+ .flush_script = flush_script_unsupported,
.stop_script = stop_script_unsupported,
.process_event = process_event_unsupported,
.generate_script = python_generate_script_unsupported,
@@ -137,6 +143,7 @@ static int perl_generate_script_unsupported(struct pevent *pevent
struct scripting_ops perl_scripting_unsupported_ops = {
.name = "Perl",
.start_script = perl_start_script_unsupported,
+ .flush_script = flush_script_unsupported,
.stop_script = stop_script_unsupported,
.process_event = process_event_unsupported,
.generate_script = perl_generate_script_unsupported,
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 7b6d686..52aaa19 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -64,6 +64,7 @@ struct perf_session;
struct scripting_ops {
const char *name;
int (*start_script) (const char *script, int argc, const char **argv);
+ int (*flush_script) (void);
int (*stop_script) (void);
void (*process_event) (union perf_event *event,
struct perf_sample *sample,
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [tip:perf/core] perf machine: Rename machine__get_kernel_start_addr() method
2014-08-15 19:08 ` [PATCH 03/24] perf tools: Rename machine__get_kernel_start_addr() Adrian Hunter
@ 2014-08-24 14:58 ` tip-bot for Adrian Hunter
0 siblings, 0 replies; 40+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-08-24 14:58 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, eranian, paulus, hpa, mingo, peterz,
namhyung, jolsa, fweisbec, adrian.hunter, dsahern, tglx
Commit-ID: 4b99375b38fa137f501cfa60b70e3f0a9da39c93
Gitweb: http://git.kernel.org/tip/4b99375b38fa137f501cfa60b70e3f0a9da39c93
Author: Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Fri, 15 Aug 2014 22:08:38 +0300
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 22 Aug 2014 13:12:11 -0300
perf machine: Rename machine__get_kernel_start_addr() method
Rename machine__get_kernel_start_addr() to
machine__get_running_kernel_start() so that a new function, with a
similar name to the original name, can be added that gets the kernel
start address from the kernel map.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1408129739-17368-4-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/machine.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index b093b93..37f8dc5 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -593,8 +593,8 @@ const char *ref_reloc_sym_names[] = {"_text", "_stext", NULL};
* Returns the name of the start symbol in *symbol_name. Pass in NULL as
* symbol_name if it's not that important.
*/
-static u64 machine__get_kernel_start_addr(struct machine *machine,
- const char **symbol_name)
+static u64 machine__get_running_kernel_start(struct machine *machine,
+ const char **symbol_name)
{
char filename[PATH_MAX];
int i;
@@ -621,7 +621,7 @@ static u64 machine__get_kernel_start_addr(struct machine *machine,
int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel)
{
enum map_type type;
- u64 start = machine__get_kernel_start_addr(machine, NULL);
+ u64 start = machine__get_running_kernel_start(machine, NULL);
for (type = 0; type < MAP__NR_TYPES; ++type) {
struct kmap *kmap;
@@ -940,7 +940,7 @@ int machine__create_kernel_maps(struct machine *machine)
{
struct dso *kernel = machine__get_kernel(machine);
const char *name;
- u64 addr = machine__get_kernel_start_addr(machine, &name);
+ u64 addr = machine__get_running_kernel_start(machine, &name);
if (!addr)
return -1;
^ permalink raw reply related [flat|nested] 40+ messages in thread
* [tip:perf/core] perf tools: Add machine__kernel_ip()
2014-08-15 19:08 ` [PATCH 04/24] perf tools: Add machine__kernel_ip() Adrian Hunter
@ 2014-08-24 14:58 ` tip-bot for Adrian Hunter
0 siblings, 0 replies; 40+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-08-24 14:58 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, eranian, paulus, hpa, mingo, peterz,
namhyung, jolsa, fweisbec, adrian.hunter, dsahern, tglx
Commit-ID: fbe2af45f6bd27ee69fd775303c936c3af4a4807
Gitweb: http://git.kernel.org/tip/fbe2af45f6bd27ee69fd775303c936c3af4a4807
Author: Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Fri, 15 Aug 2014 22:08:39 +0300
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 22 Aug 2014 13:12:12 -0300
perf tools: Add machine__kernel_ip()
Add a function to determine if an address is in the kernel. This is
based on the kernel function kernel_ip().
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1408129739-17368-5-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/event.c | 6 +++---
tools/perf/util/machine.c | 23 +++++++++++++++++++++++
tools/perf/util/machine.h | 17 +++++++++++++++++
3 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 1398c83..ed55819 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -784,9 +784,9 @@ try_again:
* "[vdso]" dso, but for now lets use the old trick of looking
* in the whole kernel symbol list.
*/
- if ((long long)al->addr < 0 &&
- cpumode == PERF_RECORD_MISC_USER &&
- machine && mg != &machine->kmaps) {
+ if (cpumode == PERF_RECORD_MISC_USER && machine &&
+ mg != &machine->kmaps &&
+ machine__kernel_ip(machine, al->addr)) {
mg = &machine->kmaps;
load_map = true;
goto try_again;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 37f8dc5..e00daf0 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -32,6 +32,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid)
machine->symbol_filter = NULL;
machine->id_hdr_size = 0;
machine->comm_exec = false;
+ machine->kernel_start = 0;
machine->root_dir = strdup(root_dir);
if (machine->root_dir == NULL)
@@ -1559,3 +1560,25 @@ int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
return 0;
}
+
+int machine__get_kernel_start(struct machine *machine)
+{
+ struct map *map = machine__kernel_map(machine, MAP__FUNCTION);
+ int err = 0;
+
+ /*
+ * The only addresses above 2^63 are kernel addresses of a 64-bit
+ * kernel. Note that addresses are unsigned so that on a 32-bit system
+ * all addresses including kernel addresses are less than 2^32. In
+ * that case (32-bit system), if the kernel mapping is unknown, all
+ * addresses will be assumed to be in user space - see
+ * machine__kernel_ip().
+ */
+ machine->kernel_start = 1ULL << 63;
+ if (map) {
+ err = map__load(map, machine->symbol_filter);
+ if (map->start)
+ machine->kernel_start = map->start;
+ }
+ return err;
+}
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 61216e0..6a6bcc1 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -36,6 +36,7 @@ struct machine {
struct list_head kernel_dsos;
struct map_groups kmaps;
struct map *vmlinux_maps[MAP__NR_TYPES];
+ u64 kernel_start;
symbol_filter_t symbol_filter;
pid_t *current_tid;
};
@@ -46,6 +47,22 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type)
return machine->vmlinux_maps[type];
}
+int machine__get_kernel_start(struct machine *machine);
+
+static inline u64 machine__kernel_start(struct machine *machine)
+{
+ if (!machine->kernel_start)
+ machine__get_kernel_start(machine);
+ return machine->kernel_start;
+}
+
+static inline bool machine__kernel_ip(struct machine *machine, u64 ip)
+{
+ u64 kernel_start = machine__kernel_start(machine);
+
+ return ip >= kernel_start;
+}
+
struct thread *machine__find_thread(struct machine *machine, pid_t pid,
pid_t tid);
struct comm *machine__thread_exec_comm(struct machine *machine,
^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 01/24] perf tools: Add a test for tracking with sched_switch
2014-08-20 19:48 ` Arnaldo Carvalho de Melo
@ 2014-08-29 13:52 ` Adrian Hunter
2014-08-29 15:06 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 40+ messages in thread
From: Adrian Hunter @ 2014-08-29 13:52 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
On 08/20/2014 10:48 PM, Arnaldo Carvalho de Melo wrote:
> Em Fri, Aug 15, 2014 at 10:08:36PM +0300, Adrian Hunter escreveu:
>> Add a test that checks that sched_switch events and
>> tracking events can be recorded for a workload using the
>> evsel->system_wide and evsel->tracking flags (respectively)
>> with other events sometimes enabled or disabled.
>
> Really nice exercise! Gives lots of evlist/evsel routines a good test,
> checking for expected behaviour, really good, thanks.
>
> One thing I noticed was that it uses parse_events() for creating events,
> a perhaps simpler equivalent would be:
>
> switch_evsel = perf_evsel__newtp("sched", "sched_switch");
>
> And then go on, like you did, configuring whatever attribute one wants
> to have set, like what you get from ":u", and the other things you
> touched, like:
>
> switch_evsel->system_wide = true;
> switch_evsel->no_aux_samples = true;
> switch_evsel->immediate = true;
>
> And when the evsel is all set up, if dealing with evlists, like in this
> case, just do a:
>
> perf_evlist__add(evlist, switch_evsel);
>
> Looks more natural than using parse_events(evlist, "sched:sched_switch");
>
> To then infer that it must be the last entry at this point to retrieve
> it using:
>
> switch_evsel = perf_evlist__last(evlist);
>
> But, for the current architecture, that should be just a clarification,
> not a requirement for this specific test.
I will keep that in mind.
> Applying!
Thank you!
AFAICT the other 20 patches still apply cleanly to your tree, so I am not
going to send any more for now.
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 01/24] perf tools: Add a test for tracking with sched_switch
2014-08-29 13:52 ` Adrian Hunter
@ 2014-08-29 15:06 ` Arnaldo Carvalho de Melo
2014-08-29 15:18 ` Jiri Olsa
0 siblings, 1 reply; 40+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-08-29 15:06 UTC (permalink / raw)
To: Adrian Hunter
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Em Fri, Aug 29, 2014 at 04:52:16PM +0300, Adrian Hunter escreveu:
> On 08/20/2014 10:48 PM, Arnaldo Carvalho de Melo wrote:
> > But, for the current architecture, that should be just a clarification,
> > not a requirement for this specific test.
> I will keep that in mind.
> > Applying!
> Thank you!
> AFAICT the other 20 patches still apply cleanly to your tree, so I am not
> going to send any more for now.
Ok, I'll try to process more of those, the db interface probably, after
I try using it for some simple tests.
About the PMU ones, that I would like to hear from other people dealing
with that area, like Jiri or even PeterZ, that IIRC discussed sysfs
interface aspects in the past, Jiri, Peter?
- Arnaldo
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 01/24] perf tools: Add a test for tracking with sched_switch
2014-08-29 15:06 ` Arnaldo Carvalho de Melo
@ 2014-08-29 15:18 ` Jiri Olsa
2014-08-29 18:38 ` Adrian Hunter
0 siblings, 1 reply; 40+ messages in thread
From: Jiri Olsa @ 2014-08-29 15:18 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Adrian Hunter, Peter Zijlstra, linux-kernel, David Ahern,
Frederic Weisbecker, Namhyung Kim, Paul Mackerras,
Stephane Eranian
On Fri, Aug 29, 2014 at 12:06:34PM -0300, Arnaldo Carvalho de Melo wrote:
> Em Fri, Aug 29, 2014 at 04:52:16PM +0300, Adrian Hunter escreveu:
> > On 08/20/2014 10:48 PM, Arnaldo Carvalho de Melo wrote:
> > > But, for the current architecture, that should be just a clarification,
> > > not a requirement for this specific test.
>
> > I will keep that in mind.
>
> > > Applying!
>
> > Thank you!
>
> > AFAICT the other 20 patches still apply cleanly to your tree, so I am not
> > going to send any more for now.
>
> Ok, I'll try to process more of those, the db interface probably, after
> I try using it for some simple tests.
>
> About the PMU ones, that I would like to hear from other people dealing
> with that area, like Jiri or even PeterZ, that IIRC discussed sysfs
> interface aspects in the past, Jiri, Peter?
I reviewed those, but I remember not getting an answer
for my question about that
http://marc.info/?t=140533254600001&r=1&w=2
jirka
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 01/24] perf tools: Add a test for tracking with sched_switch
2014-08-29 15:18 ` Jiri Olsa
@ 2014-08-29 18:38 ` Adrian Hunter
0 siblings, 0 replies; 40+ messages in thread
From: Adrian Hunter @ 2014-08-29 18:38 UTC (permalink / raw)
To: Jiri Olsa, Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Namhyung Kim, Paul Mackerras, Stephane Eranian
On 29/08/2014 6:18 p.m., Jiri Olsa wrote:
> On Fri, Aug 29, 2014 at 12:06:34PM -0300, Arnaldo Carvalho de Melo wrote:
>> Em Fri, Aug 29, 2014 at 04:52:16PM +0300, Adrian Hunter escreveu:
>>> On 08/20/2014 10:48 PM, Arnaldo Carvalho de Melo wrote:
>>>> But, for the current architecture, that should be just a clarification,
>>>> not a requirement for this specific test.
>>
>>> I will keep that in mind.
>>
>>>> Applying!
>>
>>> Thank you!
>>
>>> AFAICT the other 20 patches still apply cleanly to your tree, so I am not
>>> going to send any more for now.
>>
>> Ok, I'll try to process more of those, the db interface probably, after
>> I try using it for some simple tests.
>>
>> About the PMU ones, that I would like to hear from other people dealing
>> with that area, like Jiri or even PeterZ, that IIRC discussed sysfs
>> interface aspects in the past, Jiri, Peter?
>
> I reviewed those, but I remember not getting an answer
> for my question about that
> http://marc.info/?t=140533254600001&r=1&w=2
I guess I didn't understand what you meant. I will reply to that thread.
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 22/24] perf tools: Build programs to copy 32-bit compatibility VDSOs
2014-08-15 19:08 ` [PATCH 22/24] perf tools: Build programs to copy 32-bit compatibility VDSOs Adrian Hunter
@ 2014-09-15 15:47 ` Arnaldo Carvalho de Melo
2014-09-15 15:50 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 40+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-09-15 15:47 UTC (permalink / raw)
To: Adrian Hunter
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Em Fri, Aug 15, 2014 at 10:08:57PM +0300, Adrian Hunter escreveu:
> perf tools copy VDSO out of memory. However, on 64-bit machines there
> may be 32-bit compatibility VDOs also. To copy those requires
> separate 32-bit executables. This patch adds to the build additional
> programs perf-read-vdso32 and perf-read-vdsox32 for 32-bit and x32
> respectively.
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
<SNIP>
> tools/perf/config/feature-checks/test-compile.c | 4 +++
<SNIP>
> 8 files changed, 149 insertions(+), 37 deletions(-)
<SNIP>
> create mode 100644 tools/perf/config/feature-checks/test-compile.c
<SNIP>
In resolving conflicts that happen because I haven't merged some other
files (thread_stack, etC) I noticed that this patch introduces something
that is explicitely listed in a .gitignore file:
[acme@zoo linux]$ git show d316f16dddd8c7780c727bbd507338e4147c9280
commit d316f16dddd8c7780c727bbd507338e4147c9280
Author: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Date: Fri Aug 15 22:08:59 2014 +0300
perf tools: Add feature checks to .gitignore
<SNIP>
+++ b/tools/perf/.gitignore
@@ -24,3 +24,4 @@ config.mak.autogen
*-flex.*
*.pyc
*.pyo
+config/feature-checks/test-*
[acme@zoo linux]$
So I think we should use some different name for this test-compile.c
file?
I'll leave those two csets out of this pull req, so that you can rebase
what is left on top of what is being merged and we can do one more
progress step in having all this merged, ok?
I mean:
>> Up-to-date versions can be found from 15 August:
>>
>> patchwork:
>>
>> https://patchwork.kernel.org/patch/4729131/
>> https://patchwork.kernel.org/patch/4729111/
- Arnaldo
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 22/24] perf tools: Build programs to copy 32-bit compatibility VDSOs
2014-09-15 15:47 ` Arnaldo Carvalho de Melo
@ 2014-09-15 15:50 ` Arnaldo Carvalho de Melo
2014-09-16 6:59 ` Adrian Hunter
0 siblings, 1 reply; 40+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-09-15 15:50 UTC (permalink / raw)
To: Adrian Hunter
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Em Mon, Sep 15, 2014 at 12:47:52PM -0300, Arnaldo Carvalho de Melo escreveu:
> Em Fri, Aug 15, 2014 at 10:08:57PM +0300, Adrian Hunter escreveu:
>
> > perf tools copy VDSO out of memory. However, on 64-bit machines there
> > may be 32-bit compatibility VDOs also. To copy those requires
> > separate 32-bit executables. This patch adds to the build additional
> > programs perf-read-vdso32 and perf-read-vdsox32 for 32-bit and x32
> > respectively.
>
> > Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> <SNIP>
> > tools/perf/config/feature-checks/test-compile.c | 4 +++
> <SNIP>
> > 8 files changed, 149 insertions(+), 37 deletions(-)
> <SNIP>
> > create mode 100644 tools/perf/config/feature-checks/test-compile.c
> <SNIP>
>
>
> In resolving conflicts that happen because I haven't merged some other
> files (thread_stack, etC) I noticed that this patch introduces something
> that is explicitely listed in a .gitignore file:
>
> [acme@zoo linux]$ git show d316f16dddd8c7780c727bbd507338e4147c9280
> commit d316f16dddd8c7780c727bbd507338e4147c9280
> Author: Alexander Shishkin <alexander.shishkin@linux.intel.com>
> Date: Fri Aug 15 22:08:59 2014 +0300
>
> perf tools: Add feature checks to .gitignore
>
> <SNIP>
>
> +++ b/tools/perf/.gitignore
> @@ -24,3 +24,4 @@ config.mak.autogen
> *-flex.*
> *.pyc
> *.pyo
> +config/feature-checks/test-*
> [acme@zoo linux]$
>
>
> So I think we should use some different name for this test-compile.c
> file?
>
> I'll leave those two csets out of this pull req, so that you can rebase
> what is left on top of what is being merged and we can do one more
> progress step in having all this merged, ok?
Since we can't ignore all test- prefixed files there, I'm removing the
patch that added it to .gitignore as well:
commit d316f16dddd8c7780c727bbd507338e4147c9280
Author: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Date: Fri Aug 15 22:08:59 2014 +0300
Aftewards I'll revisit this, because without this .gitignore patch I
think your patch will be ok.
- Arnaldo
> I mean:
>
> >> Up-to-date versions can be found from 15 August:
> >>
> >> patchwork:
> >>
> >> https://patchwork.kernel.org/patch/4729131/
> >> https://patchwork.kernel.org/patch/4729111/
>
>
> - Arnaldo
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 22/24] perf tools: Build programs to copy 32-bit compatibility VDSOs
2014-09-15 15:50 ` Arnaldo Carvalho de Melo
@ 2014-09-16 6:59 ` Adrian Hunter
2014-09-17 14:52 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 40+ messages in thread
From: Adrian Hunter @ 2014-09-16 6:59 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
On 09/15/2014 06:50 PM, Arnaldo Carvalho de Melo wrote:
> Em Mon, Sep 15, 2014 at 12:47:52PM -0300, Arnaldo Carvalho de Melo escreveu:
>> Em Fri, Aug 15, 2014 at 10:08:57PM +0300, Adrian Hunter escreveu:
>>
>>> perf tools copy VDSO out of memory. However, on 64-bit machines there
>>> may be 32-bit compatibility VDOs also. To copy those requires
>>> separate 32-bit executables. This patch adds to the build additional
>>> programs perf-read-vdso32 and perf-read-vdsox32 for 32-bit and x32
>>> respectively.
>>
>>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
>> <SNIP>
>>> tools/perf/config/feature-checks/test-compile.c | 4 +++
>> <SNIP>
>>> 8 files changed, 149 insertions(+), 37 deletions(-)
>> <SNIP>
>>> create mode 100644 tools/perf/config/feature-checks/test-compile.c
>> <SNIP>
>>
>>
>> In resolving conflicts that happen because I haven't merged some other
>> files (thread_stack, etC) I noticed that this patch introduces something
>> that is explicitely listed in a .gitignore file:
>>
>> [acme@zoo linux]$ git show d316f16dddd8c7780c727bbd507338e4147c9280
>> commit d316f16dddd8c7780c727bbd507338e4147c9280
>> Author: Alexander Shishkin <alexander.shishkin@linux.intel.com>
>> Date: Fri Aug 15 22:08:59 2014 +0300
>>
>> perf tools: Add feature checks to .gitignore
>>
>> <SNIP>
>>
>> +++ b/tools/perf/.gitignore
>> @@ -24,3 +24,4 @@ config.mak.autogen
>> *-flex.*
>> *.pyc
>> *.pyo
>> +config/feature-checks/test-*
>> [acme@zoo linux]$
>>
>>
>> So I think we should use some different name for this test-compile.c
>> file?
The name is ok.
>>
>> I'll leave those two csets out of this pull req, so that you can rebase
>> what is left on top of what is being merged and we can do one more
>> progress step in having all this merged, ok?
>
> Since we can't ignore all test- prefixed files there, I'm removing the
> patch that added it to .gitignore as well:
>
> commit d316f16dddd8c7780c727bbd507338e4147c9280
> Author: Alexander Shishkin <alexander.shishkin@linux.intel.com>
> Date: Fri Aug 15 22:08:59 2014 +0300
>
> Aftewards I'll revisit this, because without this .gitignore patch I
> think your patch will be ok.
The .gitignore patch is wrong. I picked it up because Alex had it in
his tree but it must be very old because it was superseded by:
commit 56560ec692c142bb9ee404764e3b67999031ad19
Author: Chunwei Chen <tuxoko@gmail.com>
Date: Sat Dec 21 13:48:11 2013 +0800
perf config: Ignore generated files in feature-checks
1. Rename the test-* binary files to test-*.bin for easier pattern matching as
suggested by Ingo.
2. Ignore *.bin and *.d files.
Signed-off-by: Chunwei Chen <tuxoko@gmail.com>
Reviewed-by: Ingo Molnar <mingo@kernel.org>
Acked-by: Jiri Olsa <jolsa@redhat.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@redhat.com>
Link: http://lkml.kernel.org/r/52B52B9B.50708@gmail.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
>
> - Arnaldo
>
>> I mean:
>>
>>>> Up-to-date versions can be found from 15 August:
>>>>
>>>> patchwork:
>>>>
>>>> https://patchwork.kernel.org/patch/4729131/
>>>> https://patchwork.kernel.org/patch/4729111/
>>
>>
>> - Arnaldo
>
>
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 22/24] perf tools: Build programs to copy 32-bit compatibility VDSOs
2014-09-16 6:59 ` Adrian Hunter
@ 2014-09-17 14:52 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 40+ messages in thread
From: Arnaldo Carvalho de Melo @ 2014-09-17 14:52 UTC (permalink / raw)
To: Adrian Hunter
Cc: Peter Zijlstra, linux-kernel, David Ahern, Frederic Weisbecker,
Jiri Olsa, Namhyung Kim, Paul Mackerras, Stephane Eranian
Em Tue, Sep 16, 2014 at 09:59:33AM +0300, Adrian Hunter escreveu:
> On 09/15/2014 06:50 PM, Arnaldo Carvalho de Melo wrote:
> > Em Mon, Sep 15, 2014 at 12:47:52PM -0300, Arnaldo Carvalho de Melo escreveu:
> >> Em Fri, Aug 15, 2014 at 10:08:57PM +0300, Adrian Hunter escreveu:
> >>
> >>> perf tools copy VDSO out of memory. However, on 64-bit machines there
> >>> may be 32-bit compatibility VDOs also. To copy those requires
> >>> separate 32-bit executables. This patch adds to the build additional
> >>> programs perf-read-vdso32 and perf-read-vdsox32 for 32-bit and x32
> >>> respectively.
> >>
> >>> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> >> <SNIP>
> >>> tools/perf/config/feature-checks/test-compile.c | 4 +++
> >> <SNIP>
> >>> 8 files changed, 149 insertions(+), 37 deletions(-)
> >> <SNIP>
> >>> create mode 100644 tools/perf/config/feature-checks/test-compile.c
> >> <SNIP>
> >>
> >>
> >> In resolving conflicts that happen because I haven't merged some other
> >> files (thread_stack, etC) I noticed that this patch introduces something
> >> that is explicitely listed in a .gitignore file:
> >>
> >> [acme@zoo linux]$ git show d316f16dddd8c7780c727bbd507338e4147c9280
> >> commit d316f16dddd8c7780c727bbd507338e4147c9280
> >> Author: Alexander Shishkin <alexander.shishkin@linux.intel.com>
> >> Date: Fri Aug 15 22:08:59 2014 +0300
> >>
> >> perf tools: Add feature checks to .gitignore
> >>
> >> <SNIP>
> >>
> >> +++ b/tools/perf/.gitignore
> >> @@ -24,3 +24,4 @@ config.mak.autogen
> >> *-flex.*
> >> *.pyc
> >> *.pyo
> >> +config/feature-checks/test-*
> >> [acme@zoo linux]$
> >>
> >>
> >> So I think we should use some different name for this test-compile.c
> >> file?
>
> The name is ok.
Ok
> >>
> >> I'll leave those two csets out of this pull req, so that you can rebase
> >> what is left on top of what is being merged and we can do one more
> >> progress step in having all this merged, ok?
> >
> > Since we can't ignore all test- prefixed files there, I'm removing the
> > patch that added it to .gitignore as well:
> >
> > commit d316f16dddd8c7780c727bbd507338e4147c9280
> > Author: Alexander Shishkin <alexander.shishkin@linux.intel.com>
> > Date: Fri Aug 15 22:08:59 2014 +0300
> >
> > Aftewards I'll revisit this, because without this .gitignore patch I
> > think your patch will be ok.
>
> The .gitignore patch is wrong. I picked it up because Alex had it in
> his tree but it must be very old because it was superseded by:
>
> commit 56560ec692c142bb9ee404764e3b67999031ad19
> Author: Chunwei Chen <tuxoko@gmail.com>
> Date: Sat Dec 21 13:48:11 2013 +0800
>
> perf config: Ignore generated files in feature-checks
>
> 1. Rename the test-* binary files to test-*.bin for easier pattern matching as
> suggested by Ingo.
> 2. Ignore *.bin and *.d files.
>
> Signed-off-by: Chunwei Chen <tuxoko@gmail.com>
> Reviewed-by: Ingo Molnar <mingo@kernel.org>
> Acked-by: Jiri Olsa <jolsa@redhat.com>
> Cc: Ingo Molnar <mingo@kernel.org>
> Cc: Jiri Olsa <jolsa@redhat.com>
> Link: http://lkml.kernel.org/r/52B52B9B.50708@gmail.com
> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Thanks, I'll fix this up.
- Arnaldo
^ permalink raw reply [flat|nested] 40+ messages in thread
* [tip:perf/core] perf tools: Let a user specify a PMU event without any config terms
2014-08-15 19:08 ` [PATCH 05/24] perf tools: Let a user specify a PMU event without any config terms Adrian Hunter
@ 2014-09-19 5:20 ` tip-bot for Adrian Hunter
0 siblings, 0 replies; 40+ messages in thread
From: tip-bot for Adrian Hunter @ 2014-09-19 5:20 UTC (permalink / raw)
To: linux-tip-commits
Cc: acme, linux-kernel, eranian, paulus, hpa, mingo, peterz,
namhyung, jolsa, fweisbec, adrian.hunter, dsahern, tglx
Commit-ID: ad96227349901838e1a7f96f1dc22d96a97520c0
Gitweb: http://git.kernel.org/tip/ad96227349901838e1a7f96f1dc22d96a97520c0
Author: Adrian Hunter <adrian.hunter@intel.com>
AuthorDate: Fri, 15 Aug 2014 22:08:40 +0300
Committer: Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Wed, 17 Sep 2014 17:08:08 -0300
perf tools: Let a user specify a PMU event without any config terms
This enables a PMU event to be specified in the form:
pmu//
which is effectively the same as:
pmu/config=0/
This patch is a precursor to defining default config for a PMU.
Further explanation extracted from lkml thread:
Imagine that the 'tsc' term did not exist.
Intel PT trace data would not contain TSC packets, and the decoder would
not know how to decode them.
Then imagine that a new version of the hardware adds 'tsc'.
It is such a useful feature that we want it by default, but older
versions of the tools don't know how to decode it, so the kernel cannot
turn it on by default.
It is similar to why the kernel does not select perf_event_attr.mmap2 by
default.
The kernel doesn't know whether the tool supports it.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/r/1408129739-17368-6-git-send-email-adrian.hunter@intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/parse-events.c | 6 ++++++
tools/perf/util/parse-events.y | 10 ++++++++++
2 files changed, 16 insertions(+)
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index e34c81a..e756288 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -645,6 +645,12 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
memset(&attr, 0, sizeof(attr));
+ if (!head_config) {
+ attr.type = pmu->type;
+ evsel = __add_event(list, idx, &attr, NULL, pmu->cpus);
+ return evsel ? 0 : -ENOMEM;
+ }
+
if (perf_pmu__check_alias(pmu, head_config, &unit, &scale))
return -EINVAL;
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 0bc87ba..55fab6a 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -210,6 +210,16 @@ PE_NAME '/' event_config '/'
parse_events__free_terms($3);
$$ = list;
}
+|
+PE_NAME '/' '/'
+{
+ struct parse_events_evlist *data = _data;
+ struct list_head *list;
+
+ ALLOC_LIST(list);
+ ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL));
+ $$ = list;
+}
value_sym:
PE_VALUE_SYM_HW
^ permalink raw reply related [flat|nested] 40+ messages in thread
end of thread, other threads:[~2014-09-19 5:21 UTC | newest]
Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-15 19:08 [PATCH 00/24] perf tools: Still more preparation for Intel PT Adrian Hunter
2014-08-15 19:08 ` [PATCH 01/24] perf tools: Add a test for tracking with sched_switch Adrian Hunter
2014-08-20 19:48 ` Arnaldo Carvalho de Melo
2014-08-29 13:52 ` Adrian Hunter
2014-08-29 15:06 ` Arnaldo Carvalho de Melo
2014-08-29 15:18 ` Jiri Olsa
2014-08-29 18:38 ` Adrian Hunter
2014-08-21 16:58 ` Arnaldo Carvalho de Melo
2014-08-24 14:58 ` [tip:perf/core] perf tests: " tip-bot for Adrian Hunter
2014-08-15 19:08 ` [PATCH 02/24] perf scripting: Add 'flush' callback to scripting API Adrian Hunter
2014-08-24 14:58 ` [tip:perf/core] " tip-bot for Adrian Hunter
2014-08-15 19:08 ` [PATCH 03/24] perf tools: Rename machine__get_kernel_start_addr() Adrian Hunter
2014-08-24 14:58 ` [tip:perf/core] perf machine: Rename machine__get_kernel_start_addr() method tip-bot for Adrian Hunter
2014-08-15 19:08 ` [PATCH 04/24] perf tools: Add machine__kernel_ip() Adrian Hunter
2014-08-24 14:58 ` [tip:perf/core] " tip-bot for Adrian Hunter
2014-08-15 19:08 ` [PATCH 05/24] perf tools: Let a user specify a PMU event without any config terms Adrian Hunter
2014-09-19 5:20 ` [tip:perf/core] " tip-bot for Adrian Hunter
2014-08-15 19:08 ` [PATCH 06/24] perf tools: Let default config be defined for a PMU Adrian Hunter
2014-08-15 19:08 ` [PATCH 07/24] perf tools: Add perf_pmu__scan_file() Adrian Hunter
2014-08-15 19:08 ` [PATCH 08/24] perf tools: Add id index Adrian Hunter
2014-08-15 19:08 ` [PATCH 09/24] perf pmu: Let pmu's with no events show up on perf list Adrian Hunter
2014-08-15 19:08 ` [PATCH 10/24] perf session: Add perf_session__deliver_synth_event() Adrian Hunter
2014-08-15 19:08 ` [PATCH 11/24] perf tools: Add a thread stack for synthesizing call chains Adrian Hunter
2014-08-15 19:08 ` [PATCH 12/24] perf tools: Add facility to export data in database-friendly way Adrian Hunter
2014-08-15 19:08 ` [PATCH 13/24] perf tools: Extend Python script interface to export data in a " Adrian Hunter
2014-08-15 19:08 ` [PATCH 14/24] perf tools: Add Python script to export to postgresql Adrian Hunter
2014-08-15 19:08 ` [PATCH 15/24] perf tools: Add branch type to db export Adrian Hunter
2014-08-15 19:08 ` [PATCH 16/24] perf tools: Add branch_type and in_tx to Python export Adrian Hunter
2014-08-15 19:08 ` [PATCH 17/24] perf tools: Enhance the thread stack to output call/return data Adrian Hunter
2014-08-15 19:08 ` [PATCH 18/24] perf tools: Add call information to the database export API Adrian Hunter
2014-08-15 19:08 ` [PATCH 19/24] perf tools: Add call information to Python export Adrian Hunter
2014-08-15 19:08 ` [PATCH 20/24] perf tools: Defer export of comms that were not 'set' Adrian Hunter
2014-08-15 19:08 ` [PATCH 21/24] perf tools: Add perf-with-kcore script Adrian Hunter
2014-08-15 19:08 ` [PATCH 22/24] perf tools: Build programs to copy 32-bit compatibility VDSOs Adrian Hunter
2014-09-15 15:47 ` Arnaldo Carvalho de Melo
2014-09-15 15:50 ` Arnaldo Carvalho de Melo
2014-09-16 6:59 ` Adrian Hunter
2014-09-17 14:52 ` Arnaldo Carvalho de Melo
2014-08-15 19:08 ` [PATCH 23/24] perf tools: Add support for " Adrian Hunter
2014-08-15 19:08 ` [PATCH 24/24] perf tools: Add feature checks to .gitignore Adrian Hunter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).