mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-03 20:34:23 -04:00
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
User visible changes:
* Don't try to find DSOs in SYSV maps (Don Zickus)
* Fallback to MAP__FUNCTION if daddr maps are NULL,
i.e. addresses get looked upon more maps (Don Zickus)
* Kernel fix to properly handle exited tasks, by returning POLLHUP values
on perf event file descriptors. Tooling changes will come next, but were
tested with this kernel fix. (Jiri Olsa)
* Add +field argument support for --field option, so that one can add
fields to the default list of fields to show, i.e. now one can just do:
perf report --fields +pid
And the pid will appear in addition to the default fields. (Jiri Olsa)
Infrastructure changes:
* More Intel PT prep stuff, including:
- Add a 'perf test' for tracking with sched_switch
- Add 'flush' callback to scripting API
* hists browser (used in top and report) refactorings, getting rid of unused
variables and reducing source code size by handling similar cases in a
fewer functions (Namhyung Kim).
* Explicitly include util/debug.h for powerpc, was being indirectly included,
broke the build when some change made it stop being included. (Sukadev
Bhattiprolu)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
@@ -269,6 +269,7 @@ struct pmu {
|
||||
* enum perf_event_active_state - the states of a event
|
||||
*/
|
||||
enum perf_event_active_state {
|
||||
PERF_EVENT_STATE_EXIT = -3,
|
||||
PERF_EVENT_STATE_ERROR = -2,
|
||||
PERF_EVENT_STATE_OFF = -1,
|
||||
PERF_EVENT_STATE_INACTIVE = 0,
|
||||
|
||||
@@ -3600,7 +3600,8 @@ perf_read_hw(struct perf_event *event, char __user *buf, size_t count)
|
||||
* error state (i.e. because it was pinned but it couldn't be
|
||||
* scheduled on to the CPU at some point).
|
||||
*/
|
||||
if (event->state == PERF_EVENT_STATE_ERROR)
|
||||
if ((event->state == PERF_EVENT_STATE_ERROR) ||
|
||||
(event->state == PERF_EVENT_STATE_EXIT))
|
||||
return 0;
|
||||
|
||||
if (count < event->read_size)
|
||||
@@ -3627,9 +3628,13 @@ static unsigned int perf_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
struct perf_event *event = file->private_data;
|
||||
struct ring_buffer *rb;
|
||||
unsigned int events = POLL_HUP;
|
||||
unsigned int events = POLLHUP;
|
||||
|
||||
poll_wait(file, &event->waitq, wait);
|
||||
|
||||
if (event->state == PERF_EVENT_STATE_EXIT)
|
||||
return events;
|
||||
|
||||
/*
|
||||
* Pin the event->rb by taking event->mmap_mutex; otherwise
|
||||
* perf_event_set_output() can swizzle our rb and make us miss wakeups.
|
||||
@@ -7588,6 +7593,9 @@ __perf_event_exit_task(struct perf_event *child_event,
|
||||
if (child_event->parent) {
|
||||
sync_child_event(child_event, child);
|
||||
free_event(child_event);
|
||||
} else {
|
||||
child_event->state = PERF_EVENT_STATE_EXIT;
|
||||
perf_event_wakeup(child_event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "util/thread.h"
|
||||
#include "util/callchain.h"
|
||||
#include "util/debug.h"
|
||||
|
||||
/*
|
||||
* When saving the callchain on Power, the kernel conservatively saves
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -433,18 +433,13 @@ static bool perf_top__handle_keypress(struct perf_top *top, int c)
|
||||
|
||||
if (!perf_top__key_mapped(top, c)) {
|
||||
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
|
||||
struct termios tc, save;
|
||||
struct termios save;
|
||||
|
||||
perf_top__print_mapped_keys(top);
|
||||
fprintf(stdout, "\nEnter selection, or unmapped key to continue: ");
|
||||
fflush(stdout);
|
||||
|
||||
tcgetattr(0, &save);
|
||||
tc = save;
|
||||
tc.c_lflag &= ~(ICANON | ECHO);
|
||||
tc.c_cc[VMIN] = 0;
|
||||
tc.c_cc[VTIME] = 0;
|
||||
tcsetattr(0, TCSANOW, &tc);
|
||||
set_term_quiet_input(&save);
|
||||
|
||||
poll(&stdin_poll, 1, -1);
|
||||
c = getc(stdin);
|
||||
|
||||
@@ -153,6 +153,10 @@ static struct test {
|
||||
.desc = "Test cumulation of child hist entries",
|
||||
.func = test__hists_cumulate,
|
||||
},
|
||||
{
|
||||
.desc = "Test tracking with sched_switch",
|
||||
.func = test__switch_tracking,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
||||
572
tools/perf/tests/switch-tracking.c
Normal file
572
tools/perf/tests/switch-tracking.c
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -477,26 +477,87 @@ static char *callchain_list__sym_name(struct callchain_list *cl,
|
||||
return bf;
|
||||
}
|
||||
|
||||
struct callchain_print_arg {
|
||||
/* for hists browser */
|
||||
off_t row_offset;
|
||||
bool is_current_entry;
|
||||
|
||||
/* for file dump */
|
||||
FILE *fp;
|
||||
int printed;
|
||||
};
|
||||
|
||||
typedef void (*print_callchain_entry_fn)(struct hist_browser *browser,
|
||||
struct callchain_list *chain,
|
||||
const char *str, int offset,
|
||||
unsigned short row,
|
||||
struct callchain_print_arg *arg);
|
||||
|
||||
static void hist_browser__show_callchain_entry(struct hist_browser *browser,
|
||||
struct callchain_list *chain,
|
||||
const char *str, int offset,
|
||||
unsigned short row,
|
||||
struct callchain_print_arg *arg)
|
||||
{
|
||||
int color, width;
|
||||
char folded_sign = callchain_list__folded(chain);
|
||||
|
||||
color = HE_COLORSET_NORMAL;
|
||||
width = browser->b.width - (offset + 2);
|
||||
if (ui_browser__is_current_entry(&browser->b, row)) {
|
||||
browser->selection = &chain->ms;
|
||||
color = HE_COLORSET_SELECTED;
|
||||
arg->is_current_entry = true;
|
||||
}
|
||||
|
||||
ui_browser__set_color(&browser->b, color);
|
||||
hist_browser__gotorc(browser, row, 0);
|
||||
slsmg_write_nstring(" ", offset);
|
||||
slsmg_printf("%c ", folded_sign);
|
||||
slsmg_write_nstring(str, width);
|
||||
}
|
||||
|
||||
static void hist_browser__fprintf_callchain_entry(struct hist_browser *b __maybe_unused,
|
||||
struct callchain_list *chain,
|
||||
const char *str, int offset,
|
||||
unsigned short row __maybe_unused,
|
||||
struct callchain_print_arg *arg)
|
||||
{
|
||||
char folded_sign = callchain_list__folded(chain);
|
||||
|
||||
arg->printed += fprintf(arg->fp, "%*s%c %s\n", offset, " ",
|
||||
folded_sign, str);
|
||||
}
|
||||
|
||||
typedef bool (*check_output_full_fn)(struct hist_browser *browser,
|
||||
unsigned short row);
|
||||
|
||||
static bool hist_browser__check_output_full(struct hist_browser *browser,
|
||||
unsigned short row)
|
||||
{
|
||||
return browser->b.rows == row;
|
||||
}
|
||||
|
||||
static bool hist_browser__check_dump_full(struct hist_browser *browser __maybe_unused,
|
||||
unsigned short row __maybe_unused)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#define LEVEL_OFFSET_STEP 3
|
||||
|
||||
static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browser,
|
||||
struct callchain_node *chain_node,
|
||||
u64 total, int level,
|
||||
unsigned short row,
|
||||
off_t *row_offset,
|
||||
bool *is_current_entry)
|
||||
static int hist_browser__show_callchain(struct hist_browser *browser,
|
||||
struct rb_root *root, int level,
|
||||
unsigned short row, u64 total,
|
||||
print_callchain_entry_fn print,
|
||||
struct callchain_print_arg *arg,
|
||||
check_output_full_fn is_output_full)
|
||||
{
|
||||
struct rb_node *node;
|
||||
int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
|
||||
u64 new_total, remaining;
|
||||
int first_row = row, offset = level * LEVEL_OFFSET_STEP;
|
||||
u64 new_total;
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
new_total = chain_node->children_hit;
|
||||
else
|
||||
new_total = total;
|
||||
|
||||
remaining = new_total;
|
||||
node = rb_first(&chain_node->rb_root);
|
||||
node = rb_first(root);
|
||||
while (node) {
|
||||
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
|
||||
struct rb_node *next = rb_next(node);
|
||||
@@ -506,30 +567,28 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
|
||||
int first = true;
|
||||
int extra_offset = 0;
|
||||
|
||||
remaining -= cumul;
|
||||
|
||||
list_for_each_entry(chain, &child->val, list) {
|
||||
char bf[1024], *alloc_str;
|
||||
const char *str;
|
||||
int color;
|
||||
bool was_first = first;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
else if (level > 1)
|
||||
extra_offset = LEVEL_OFFSET_STEP;
|
||||
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
if (*row_offset != 0) {
|
||||
--*row_offset;
|
||||
if (arg->row_offset != 0) {
|
||||
arg->row_offset--;
|
||||
goto do_next;
|
||||
}
|
||||
|
||||
alloc_str = NULL;
|
||||
str = callchain_list__sym_name(chain, bf, sizeof(bf),
|
||||
browser->show_dso);
|
||||
if (was_first) {
|
||||
double percent = cumul * 100.0 / new_total;
|
||||
|
||||
if (was_first && level > 1) {
|
||||
double percent = cumul * 100.0 / total;
|
||||
|
||||
if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
|
||||
str = "Not enough memory!";
|
||||
@@ -537,22 +596,11 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
|
||||
str = alloc_str;
|
||||
}
|
||||
|
||||
color = HE_COLORSET_NORMAL;
|
||||
width = browser->b.width - (offset + extra_offset + 2);
|
||||
if (ui_browser__is_current_entry(&browser->b, row)) {
|
||||
browser->selection = &chain->ms;
|
||||
color = HE_COLORSET_SELECTED;
|
||||
*is_current_entry = true;
|
||||
}
|
||||
print(browser, chain, str, offset + extra_offset, row, arg);
|
||||
|
||||
ui_browser__set_color(&browser->b, color);
|
||||
hist_browser__gotorc(browser, row, 0);
|
||||
slsmg_write_nstring(" ", offset + extra_offset);
|
||||
slsmg_printf("%c ", folded_sign);
|
||||
slsmg_write_nstring(str, width);
|
||||
free(alloc_str);
|
||||
|
||||
if (++row == browser->b.rows)
|
||||
if (is_output_full(browser, ++row))
|
||||
goto out;
|
||||
do_next:
|
||||
if (folded_sign == '+')
|
||||
@@ -561,92 +609,24 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
|
||||
|
||||
if (folded_sign == '-') {
|
||||
const int new_level = level + (extra_offset ? 2 : 1);
|
||||
row += hist_browser__show_callchain_node_rb_tree(browser, child, new_total,
|
||||
new_level, row, row_offset,
|
||||
is_current_entry);
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
new_total = child->children_hit;
|
||||
else
|
||||
new_total = total;
|
||||
|
||||
row += hist_browser__show_callchain(browser, &child->rb_root,
|
||||
new_level, row, new_total,
|
||||
print, arg, is_output_full);
|
||||
}
|
||||
if (row == browser->b.rows)
|
||||
goto out;
|
||||
if (is_output_full(browser, row))
|
||||
break;
|
||||
node = next;
|
||||
}
|
||||
out:
|
||||
return row - first_row;
|
||||
}
|
||||
|
||||
static int hist_browser__show_callchain_node(struct hist_browser *browser,
|
||||
struct callchain_node *node,
|
||||
int level, unsigned short row,
|
||||
off_t *row_offset,
|
||||
bool *is_current_entry)
|
||||
{
|
||||
struct callchain_list *chain;
|
||||
int first_row = row,
|
||||
offset = level * LEVEL_OFFSET_STEP,
|
||||
width = browser->b.width - offset;
|
||||
char folded_sign = ' ';
|
||||
|
||||
list_for_each_entry(chain, &node->val, list) {
|
||||
char bf[1024], *s;
|
||||
int color;
|
||||
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
|
||||
if (*row_offset != 0) {
|
||||
--*row_offset;
|
||||
continue;
|
||||
}
|
||||
|
||||
color = HE_COLORSET_NORMAL;
|
||||
if (ui_browser__is_current_entry(&browser->b, row)) {
|
||||
browser->selection = &chain->ms;
|
||||
color = HE_COLORSET_SELECTED;
|
||||
*is_current_entry = true;
|
||||
}
|
||||
|
||||
s = callchain_list__sym_name(chain, bf, sizeof(bf),
|
||||
browser->show_dso);
|
||||
hist_browser__gotorc(browser, row, 0);
|
||||
ui_browser__set_color(&browser->b, color);
|
||||
slsmg_write_nstring(" ", offset);
|
||||
slsmg_printf("%c ", folded_sign);
|
||||
slsmg_write_nstring(s, width - 2);
|
||||
|
||||
if (++row == browser->b.rows)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (folded_sign == '-')
|
||||
row += hist_browser__show_callchain_node_rb_tree(browser, node,
|
||||
browser->hists->stats.total_period,
|
||||
level + 1, row,
|
||||
row_offset,
|
||||
is_current_entry);
|
||||
out:
|
||||
return row - first_row;
|
||||
}
|
||||
|
||||
static int hist_browser__show_callchain(struct hist_browser *browser,
|
||||
struct rb_root *chain,
|
||||
int level, unsigned short row,
|
||||
off_t *row_offset,
|
||||
bool *is_current_entry)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
int first_row = row;
|
||||
|
||||
for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
|
||||
struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
|
||||
|
||||
row += hist_browser__show_callchain_node(browser, node, level,
|
||||
row, row_offset,
|
||||
is_current_entry);
|
||||
if (row == browser->b.rows)
|
||||
break;
|
||||
}
|
||||
|
||||
return row - first_row;
|
||||
}
|
||||
|
||||
struct hpp_arg {
|
||||
struct ui_browser *b;
|
||||
char folded_sign;
|
||||
@@ -818,10 +798,21 @@ static int hist_browser__show_entry(struct hist_browser *browser,
|
||||
--row_offset;
|
||||
|
||||
if (folded_sign == '-' && row != browser->b.rows) {
|
||||
printed += hist_browser__show_callchain(browser, &entry->sorted_chain,
|
||||
1, row, &row_offset,
|
||||
¤t_entry);
|
||||
if (current_entry)
|
||||
u64 total = hists__total_period(entry->hists);
|
||||
struct callchain_print_arg arg = {
|
||||
.row_offset = row_offset,
|
||||
.is_current_entry = current_entry,
|
||||
};
|
||||
|
||||
if (symbol_conf.cumulate_callchain)
|
||||
total = entry->stat_acc->period;
|
||||
|
||||
printed += hist_browser__show_callchain(browser,
|
||||
&entry->sorted_chain, 1, row, total,
|
||||
hist_browser__show_callchain_entry, &arg,
|
||||
hist_browser__check_output_full);
|
||||
|
||||
if (arg.is_current_entry)
|
||||
browser->he_selection = entry;
|
||||
}
|
||||
|
||||
@@ -1077,113 +1068,21 @@ static void ui_browser__hists_seek(struct ui_browser *browser,
|
||||
}
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *browser,
|
||||
struct callchain_node *chain_node,
|
||||
u64 total, int level,
|
||||
FILE *fp)
|
||||
{
|
||||
struct rb_node *node;
|
||||
int offset = level * LEVEL_OFFSET_STEP;
|
||||
u64 new_total, remaining;
|
||||
int printed = 0;
|
||||
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
new_total = chain_node->children_hit;
|
||||
else
|
||||
new_total = total;
|
||||
|
||||
remaining = new_total;
|
||||
node = rb_first(&chain_node->rb_root);
|
||||
while (node) {
|
||||
struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
|
||||
struct rb_node *next = rb_next(node);
|
||||
u64 cumul = callchain_cumul_hits(child);
|
||||
struct callchain_list *chain;
|
||||
char folded_sign = ' ';
|
||||
int first = true;
|
||||
int extra_offset = 0;
|
||||
|
||||
remaining -= cumul;
|
||||
|
||||
list_for_each_entry(chain, &child->val, list) {
|
||||
char bf[1024], *alloc_str;
|
||||
const char *str;
|
||||
bool was_first = first;
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
extra_offset = LEVEL_OFFSET_STEP;
|
||||
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
|
||||
alloc_str = NULL;
|
||||
str = callchain_list__sym_name(chain, bf, sizeof(bf),
|
||||
browser->show_dso);
|
||||
if (was_first) {
|
||||
double percent = cumul * 100.0 / new_total;
|
||||
|
||||
if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
|
||||
str = "Not enough memory!";
|
||||
else
|
||||
str = alloc_str;
|
||||
}
|
||||
|
||||
printed += fprintf(fp, "%*s%c %s\n", offset + extra_offset, " ", folded_sign, str);
|
||||
free(alloc_str);
|
||||
if (folded_sign == '+')
|
||||
break;
|
||||
}
|
||||
|
||||
if (folded_sign == '-') {
|
||||
const int new_level = level + (extra_offset ? 2 : 1);
|
||||
printed += hist_browser__fprintf_callchain_node_rb_tree(browser, child, new_total,
|
||||
new_level, fp);
|
||||
}
|
||||
|
||||
node = next;
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
|
||||
struct callchain_node *node,
|
||||
int level, FILE *fp)
|
||||
{
|
||||
struct callchain_list *chain;
|
||||
int offset = level * LEVEL_OFFSET_STEP;
|
||||
char folded_sign = ' ';
|
||||
int printed = 0;
|
||||
|
||||
list_for_each_entry(chain, &node->val, list) {
|
||||
char bf[1024], *s;
|
||||
|
||||
folded_sign = callchain_list__folded(chain);
|
||||
s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
|
||||
printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
|
||||
}
|
||||
|
||||
if (folded_sign == '-')
|
||||
printed += hist_browser__fprintf_callchain_node_rb_tree(browser, node,
|
||||
browser->hists->stats.total_period,
|
||||
level + 1, fp);
|
||||
return printed;
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_callchain(struct hist_browser *browser,
|
||||
struct rb_root *chain, int level, FILE *fp)
|
||||
struct hist_entry *he, FILE *fp)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
int printed = 0;
|
||||
u64 total = hists__total_period(he->hists);
|
||||
struct callchain_print_arg arg = {
|
||||
.fp = fp,
|
||||
};
|
||||
|
||||
for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
|
||||
struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
|
||||
if (symbol_conf.cumulate_callchain)
|
||||
total = he->stat_acc->period;
|
||||
|
||||
printed += hist_browser__fprintf_callchain_node(browser, node, level, fp);
|
||||
}
|
||||
|
||||
return printed;
|
||||
hist_browser__show_callchain(browser, &he->sorted_chain, 1, 0, total,
|
||||
hist_browser__fprintf_callchain_entry, &arg,
|
||||
hist_browser__check_dump_full);
|
||||
return arg.printed;
|
||||
}
|
||||
|
||||
static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
||||
@@ -1222,7 +1121,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
||||
printed += fprintf(fp, "%s\n", rtrim(s));
|
||||
|
||||
if (folded_sign == '-')
|
||||
printed += hist_browser__fprintf_callchain(browser, &he->sorted_chain, 1, fp);
|
||||
printed += hist_browser__fprintf_callchain(browser, he, fp);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \
|
||||
static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \
|
||||
struct perf_hpp *hpp, struct hist_entry *he) \
|
||||
{ \
|
||||
return hpp__fmt_acc(fmt, hpp, he, he_get_##_field, " %*.2f%%", \
|
||||
return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \
|
||||
hpp_entry_scnprintf, true); \
|
||||
}
|
||||
|
||||
@@ -452,7 +452,7 @@ void perf_hpp__init(void)
|
||||
/*
|
||||
* If user specified field order, no need to setup default fields.
|
||||
*/
|
||||
if (field_order)
|
||||
if (is_strict_order(field_order))
|
||||
return;
|
||||
|
||||
if (symbol_conf.cumulate_callchain) {
|
||||
@@ -519,7 +519,7 @@ void perf_hpp__column_disable(unsigned col)
|
||||
|
||||
void perf_hpp__cancel_cumulate(void)
|
||||
{
|
||||
if (field_order)
|
||||
if (is_strict_order(field_order))
|
||||
return;
|
||||
|
||||
perf_hpp__column_disable(PERF_HPP__OVERHEAD_ACC);
|
||||
|
||||
@@ -784,9 +784,9 @@ void thread__find_addr_map(struct thread *thread,
|
||||
* "[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;
|
||||
|
||||
@@ -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)
|
||||
@@ -593,8 +594,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 +622,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 +941,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;
|
||||
|
||||
@@ -1313,6 +1314,16 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread,
|
||||
|
||||
thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr,
|
||||
&al);
|
||||
if (al.map == NULL) {
|
||||
/*
|
||||
* some shared data regions have execute bit set which puts
|
||||
* their mapping in the MAP__FUNCTION type array.
|
||||
* Check there as a fallback option before dropping the sample.
|
||||
*/
|
||||
thread__find_addr_location(thread, machine, m, MAP__FUNCTION, addr,
|
||||
&al);
|
||||
}
|
||||
|
||||
ams->addr = addr;
|
||||
ams->al_addr = al.addr;
|
||||
ams->sym = al.sym;
|
||||
@@ -1559,3 +1570,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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -31,6 +31,7 @@ static inline int is_anon_memory(const char *filename)
|
||||
static inline int is_no_dso_memory(const char *filename)
|
||||
{
|
||||
return !strncmp(filename, "[stack", 6) ||
|
||||
!strncmp(filename, "/SYSV",5) ||
|
||||
!strcmp(filename, "[heap]");
|
||||
}
|
||||
|
||||
|
||||
@@ -432,6 +432,11 @@ static int perl_start_script(const char *script, int argc, const char **argv)
|
||||
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,
|
||||
|
||||
@@ -639,6 +639,11 @@ static int python_start_script(const char *script, int argc, const char **argv)
|
||||
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,
|
||||
|
||||
@@ -1453,7 +1453,7 @@ static int __setup_sorting(void)
|
||||
int ret = 0;
|
||||
|
||||
if (sort_keys == NULL) {
|
||||
if (field_order) {
|
||||
if (is_strict_order(field_order)) {
|
||||
/*
|
||||
* If user specified field order but no sort order,
|
||||
* we'll honor it and not add default sort orders.
|
||||
@@ -1639,23 +1639,36 @@ static void reset_dimensions(void)
|
||||
memory_sort_dimensions[i].taken = 0;
|
||||
}
|
||||
|
||||
bool is_strict_order(const char *order)
|
||||
{
|
||||
return order && (*order != '+');
|
||||
}
|
||||
|
||||
static int __setup_output_field(void)
|
||||
{
|
||||
char *tmp, *tok, *str;
|
||||
int ret = 0;
|
||||
char *tmp, *tok, *str, *strp;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (field_order == NULL)
|
||||
return 0;
|
||||
|
||||
reset_dimensions();
|
||||
|
||||
str = strdup(field_order);
|
||||
strp = str = strdup(field_order);
|
||||
if (str == NULL) {
|
||||
error("Not enough memory to setup output fields");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (tok = strtok_r(str, ", ", &tmp);
|
||||
if (!is_strict_order(field_order))
|
||||
strp++;
|
||||
|
||||
if (!strlen(strp)) {
|
||||
error("Invalid --fields key: `+'");
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (tok = strtok_r(strp, ", ", &tmp);
|
||||
tok; tok = strtok_r(NULL, ", ", &tmp)) {
|
||||
ret = output_field_add(tok);
|
||||
if (ret == -EINVAL) {
|
||||
@@ -1667,6 +1680,7 @@ static int __setup_output_field(void)
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -218,4 +218,5 @@ void perf_hpp__set_elide(int idx, bool elide);
|
||||
|
||||
int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset);
|
||||
|
||||
bool is_strict_order(const char *order);
|
||||
#endif /* __PERF_SORT_H */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user