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:
Ingo Molnar
2014-08-24 12:08:20 +02:00
21 changed files with 828 additions and 247 deletions

View File

@@ -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,

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,
},

View 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;
}

View File

@@ -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

View File

@@ -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,
&current_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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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]");
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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,

View File

@@ -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,