Commit 71c62b24 authored by Ingo Molnar's avatar Ingo Molnar

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

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

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

 User visible changes:

  * Add a visual cue for toggle zeroing of samples in 'perf top' (Taeung Song)

  * Fix for double free in 'perf stat' when using some specific invalid
    command line combo (Yasser Shalabi)

 Infrastructure changes:

  * Add option to copy events when queuing for sorting across cpu buffers
    and enable it for 'perf kvm stat live', to avoid having events left
    in the queue pointing to the ring buffer be rewritten in high volume
    sessions.  (Alexander Yarygin, improving work done by David Ahern):

  * Document sysfs events/ interfaces (Cody P Schafer)

  * Add support to new style format of kernel PMU event. (Kan Liang)

  * Fix typos in perf/Documentation (Masanari Iida)

  * Improve callchains when using libunwind (Namhyung Kim)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents ec4212d8 673d659f
...@@ -196,10 +196,10 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as: ...@@ -196,10 +196,10 @@ If specified the 'Weighted diff' column is displayed with value 'd' computed as:
- period being the hist entry period value - period being the hist entry period value
- WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option - WEIGHT-A/WEIGHT-B being user supplied weights in the the '-c' option
behind ':' separator like '-c wdiff:1,2'. behind ':' separator like '-c wdiff:1,2'.
- WIEGHT-A being the weight of the data file - WEIGHT-A being the weight of the data file
- WIEGHT-B being the weight of the baseline data file - WEIGHT-B being the weight of the baseline data file
SEE ALSO SEE ALSO
-------- --------
......
...@@ -100,7 +100,7 @@ OPTIONS ...@@ -100,7 +100,7 @@ OPTIONS
STAT REPORT OPTIONS STAT REPORT OPTIONS
------------------- -------------------
--vcpu=<value>:: --vcpu=<value>::
analyze events which occures on this vcpu. (default: all vcpus) analyze events which occur on this vcpu. (default: all vcpus)
--event=<value>:: --event=<value>::
event to be analyzed. Possible values: vmexit, mmio (x86 only), event to be analyzed. Possible values: vmexit, mmio (x86 only),
...@@ -134,7 +134,7 @@ STAT LIVE OPTIONS ...@@ -134,7 +134,7 @@ STAT LIVE OPTIONS
Analyze events only for given process ID(s) (comma separated list). Analyze events only for given process ID(s) (comma separated list).
--vcpu=<value>:: --vcpu=<value>::
analyze events which occures on this vcpu. (default: all vcpus) analyze events which occur on this vcpu. (default: all vcpus)
--event=<value>:: --event=<value>::
......
...@@ -19,7 +19,7 @@ various perf commands with the -e option. ...@@ -19,7 +19,7 @@ various perf commands with the -e option.
EVENT MODIFIERS EVENT MODIFIERS
--------------- ---------------
Events can optionally have a modifer by appending a colon and one or Events can optionally have a modifier by appending a colon and one or
more modifiers. Modifiers allow the user to restrict the events to be more modifiers. Modifiers allow the user to restrict the events to be
counted. The following modifiers exist: counted. The following modifiers exist:
......
...@@ -146,7 +146,7 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. ...@@ -146,7 +146,7 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
-N:: -N::
--no-buildid-cache:: --no-buildid-cache::
Do not update the builid cache. This saves some overhead in situations Do not update the buildid cache. This saves some overhead in situations
where the information in the perf.data file (which includes buildids) where the information in the perf.data file (which includes buildids)
is sufficient. is sufficient.
......
...@@ -181,8 +181,8 @@ strings for flag and symbolic fields. These correspond to the strings ...@@ -181,8 +181,8 @@ strings for flag and symbolic fields. These correspond to the strings
and values parsed from the 'print fmt' fields of the event format and values parsed from the 'print fmt' fields of the event format
files: files:
flag_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the flag field $field_name of event $event_name flag_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the flag field $field_name of event $event_name
symbol_str($event_name, $field_name, $field_value) - returns the string represention corresponding to $field_value for the symbolic field $field_name of event $event_name symbol_str($event_name, $field_name, $field_value) - returns the string representation corresponding to $field_value for the symbolic field $field_name of event $event_name
Perf::Trace::Context Module Perf::Trace::Context Module
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
......
...@@ -263,7 +263,7 @@ and having the counts we've tallied as values. ...@@ -263,7 +263,7 @@ and having the counts we've tallied as values.
The print_syscall_totals() function iterates over the entries in the The print_syscall_totals() function iterates over the entries in the
dictionary and displays a line for each entry containing the syscall dictionary and displays a line for each entry containing the syscall
name (the dictonary keys contain the syscall ids, which are passed to name (the dictionary keys contain the syscall ids, which are passed to
the Util function syscall_name(), which translates the raw syscall the Util function syscall_name(), which translates the raw syscall
numbers to the corresponding syscall name strings). The output is numbers to the corresponding syscall name strings). The output is
displayed after all the events in the trace have been processed, by displayed after all the events in the trace have been processed, by
...@@ -576,8 +576,8 @@ strings for flag and symbolic fields. These correspond to the strings ...@@ -576,8 +576,8 @@ strings for flag and symbolic fields. These correspond to the strings
and values parsed from the 'print fmt' fields of the event format and values parsed from the 'print fmt' fields of the event format
files: files:
flag_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the flag field field_name of event event_name flag_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the flag field field_name of event event_name
symbol_str(event_name, field_name, field_value) - returns the string represention corresponding to field_value for the symbolic field field_name of event event_name symbol_str(event_name, field_name, field_value) - returns the string representation corresponding to field_value for the symbolic field field_name of event event_name
The *autodict* function returns a special kind of Python The *autodict* function returns a special kind of Python
dictionary that implements Perl's 'autovivifying' hashes in Python dictionary that implements Perl's 'autovivifying' hashes in Python
......
...@@ -140,7 +140,7 @@ OPTIONS ...@@ -140,7 +140,7 @@ OPTIONS
"Overriding previous field request for all events." "Overriding previous field request for all events."
Alternativey, consider the order: Alternatively, consider the order:
-f comm,tid,time,ip,sym -f trace: -f comm,tid,time,ip,sym -f trace:
......
...@@ -25,7 +25,7 @@ OPTIONS ...@@ -25,7 +25,7 @@ OPTIONS
------- -------
-s:: -s::
--skip:: --skip::
Tests to skip (comma separater numeric list). Tests to skip (comma separated numeric list).
-v:: -v::
--verbose:: --verbose::
......
...@@ -20,7 +20,7 @@ scheduling events, etc. ...@@ -20,7 +20,7 @@ scheduling events, etc.
This is a live mode tool in addition to working with perf.data files like This is a live mode tool in addition to working with perf.data files like
the other perf tools. Files can be generated using the 'perf record' command the other perf tools. Files can be generated using the 'perf record' command
but the session needs to include the raw_syscalls events (-e 'raw_syscalls:*'). but the session needs to include the raw_syscalls events (-e 'raw_syscalls:*').
Alernatively, the 'perf trace record' can be used as a shortcut to Alternatively, 'perf trace record' can be used as a shortcut to
automatically include the raw_syscalls events when writing events to a file. automatically include the raw_syscalls events when writing events to a file.
The following options apply to perf trace; options to perf trace record are The following options apply to perf trace; options to perf trace record are
......
...@@ -376,7 +376,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, ...@@ -376,7 +376,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread,
struct perf_sample *sample) struct perf_sample *sample)
{ {
/* Only kvm_entry records vcpu id. */ /* Only kvm_entry records vcpu id. */
if (!thread->priv && kvm_entry_event(evsel)) { if (!thread__priv(thread) && kvm_entry_event(evsel)) {
struct vcpu_event_record *vcpu_record; struct vcpu_event_record *vcpu_record;
vcpu_record = zalloc(sizeof(*vcpu_record)); vcpu_record = zalloc(sizeof(*vcpu_record));
...@@ -386,10 +386,10 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, ...@@ -386,10 +386,10 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread,
} }
vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID); vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID);
thread->priv = vcpu_record; thread__set_priv(thread, vcpu_record);
} }
return thread->priv; return thread__priv(thread);
} }
static bool handle_kvm_event(struct perf_kvm_stat *kvm, static bool handle_kvm_event(struct perf_kvm_stat *kvm,
...@@ -1358,6 +1358,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, ...@@ -1358,6 +1358,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
} }
kvm->session->evlist = kvm->evlist; kvm->session->evlist = kvm->evlist;
perf_session__set_id_hdr_size(kvm->session); perf_session__set_id_hdr_size(kvm->session);
ordered_events__set_copy_on_queue(&kvm->session->ordered_events, true);
machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target, machine__synthesize_threads(&kvm->session->machines.host, &kvm->opts.target,
kvm->evlist->threads, false); kvm->evlist->threads, false);
err = kvm_live_open_events(kvm); err = kvm_live_open_events(kvm);
......
...@@ -257,6 +257,13 @@ static int report__setup_sample_type(struct report *rep) ...@@ -257,6 +257,13 @@ static int report__setup_sample_type(struct report *rep)
} }
} }
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) {
if ((sample_type & PERF_SAMPLE_REGS_USER) &&
(sample_type & PERF_SAMPLE_STACK_USER))
callchain_param.record_mode = CALLCHAIN_DWARF;
else
callchain_param.record_mode = CALLCHAIN_FP;
}
return 0; return 0;
} }
......
...@@ -1189,13 +1189,13 @@ static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) ...@@ -1189,13 +1189,13 @@ static struct thread_trace *thread__trace(struct thread *thread, FILE *fp)
if (thread == NULL) if (thread == NULL)
goto fail; goto fail;
if (thread->priv == NULL) if (thread__priv(thread) == NULL)
thread->priv = thread_trace__new(); thread__set_priv(thread, thread_trace__new());
if (thread->priv == NULL) if (thread__priv(thread) == NULL)
goto fail; goto fail;
ttrace = thread->priv; ttrace = thread__priv(thread);
++ttrace->nr_events; ++ttrace->nr_events;
return ttrace; return ttrace;
...@@ -1248,7 +1248,7 @@ struct trace { ...@@ -1248,7 +1248,7 @@ struct trace {
static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname)
{ {
struct thread_trace *ttrace = thread->priv; struct thread_trace *ttrace = thread__priv(thread);
if (fd > ttrace->paths.max) { if (fd > ttrace->paths.max) {
char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *));
...@@ -1301,7 +1301,7 @@ static int thread__read_fd_path(struct thread *thread, int fd) ...@@ -1301,7 +1301,7 @@ static int thread__read_fd_path(struct thread *thread, int fd)
static const char *thread__fd_path(struct thread *thread, int fd, static const char *thread__fd_path(struct thread *thread, int fd,
struct trace *trace) struct trace *trace)
{ {
struct thread_trace *ttrace = thread->priv; struct thread_trace *ttrace = thread__priv(thread);
if (ttrace == NULL) if (ttrace == NULL)
return NULL; return NULL;
...@@ -1338,7 +1338,7 @@ static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size, ...@@ -1338,7 +1338,7 @@ static size_t syscall_arg__scnprintf_close_fd(char *bf, size_t size,
{ {
int fd = arg->val; int fd = arg->val;
size_t printed = syscall_arg__scnprintf_fd(bf, size, arg); size_t printed = syscall_arg__scnprintf_fd(bf, size, arg);
struct thread_trace *ttrace = arg->thread->priv; struct thread_trace *ttrace = thread__priv(arg->thread);
if (ttrace && fd >= 0 && fd <= ttrace->paths.max) if (ttrace && fd >= 0 && fd <= ttrace->paths.max)
zfree(&ttrace->paths.table[fd]); zfree(&ttrace->paths.table[fd]);
...@@ -2381,7 +2381,7 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv) ...@@ -2381,7 +2381,7 @@ static int trace__fprintf_one_thread(struct thread *thread, void *priv)
FILE *fp = data->fp; FILE *fp = data->fp;
size_t printed = data->printed; size_t printed = data->printed;
struct trace *trace = data->trace; struct trace *trace = data->trace;
struct thread_trace *ttrace = thread->priv; struct thread_trace *ttrace = thread__priv(thread);
double ratio; double ratio;
if (ttrace == NULL) if (ttrace == NULL)
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "perf_regs.h" #include "perf_regs.h"
#include "map.h" #include "map.h"
#include "thread.h" #include "thread.h"
#include "callchain.h"
static int mmap_handler(struct perf_tool *tool __maybe_unused, static int mmap_handler(struct perf_tool *tool __maybe_unused,
union perf_event *event, union perf_event *event,
...@@ -120,6 +121,8 @@ int test__dwarf_unwind(void) ...@@ -120,6 +121,8 @@ int test__dwarf_unwind(void)
return -1; return -1;
} }
callchain_param.record_mode = CALLCHAIN_DWARF;
if (init_live_machine(machine)) { if (init_live_machine(machine)) {
pr_err("Could not init machine\n"); pr_err("Could not init machine\n");
goto out; goto out;
......
...@@ -457,6 +457,36 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist) ...@@ -457,6 +457,36 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist)
return 0; return 0;
} }
static int test__checkevent_pmu_events_mix(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = perf_evlist__first(evlist);
/* pmu-event:u */
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong exclude_user",
!evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel",
evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
/* cpu/pmu-event/u*/
evsel = perf_evsel__next(evsel);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong exclude_user",
!evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel",
evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned);
return 0;
}
static int test__checkterms_simple(struct list_head *terms) static int test__checkterms_simple(struct list_head *terms)
{ {
struct parse_events_term *term; struct parse_events_term *term;
...@@ -1554,6 +1584,12 @@ static int test_pmu_events(void) ...@@ -1554,6 +1584,12 @@ static int test_pmu_events(void)
e.check = test__checkevent_pmu_events; e.check = test__checkevent_pmu_events;
ret = test_event(&e); ret = test_event(&e);
if (ret)
break;
snprintf(name, MAX_NAME, "%s:u,cpu/event=%s/u", ent->d_name, ent->d_name);
e.name = name;
e.check = test__checkevent_pmu_events_mix;
ret = test_event(&e);
#undef MAX_NAME #undef MAX_NAME
} }
......
...@@ -35,7 +35,9 @@ struct hist_browser { ...@@ -35,7 +35,9 @@ struct hist_browser {
extern void hist_browser__init_hpp(void); extern void hist_browser__init_hpp(void);
static int hists__browser_title(struct hists *hists, char *bf, size_t size); static int hists__browser_title(struct hists *hists,
struct hist_browser_timer *hbt,
char *bf, size_t size);
static void hist_browser__update_nr_entries(struct hist_browser *hb); static void hist_browser__update_nr_entries(struct hist_browser *hb);
static struct rb_node *hists__filter_entries(struct rb_node *nd, static struct rb_node *hists__filter_entries(struct rb_node *nd,
...@@ -390,7 +392,7 @@ static int hist_browser__run(struct hist_browser *browser, ...@@ -390,7 +392,7 @@ static int hist_browser__run(struct hist_browser *browser,
browser->b.entries = &browser->hists->entries; browser->b.entries = &browser->hists->entries;
browser->b.nr_entries = hist_browser__nr_entries(browser); browser->b.nr_entries = hist_browser__nr_entries(browser);
hists__browser_title(browser->hists, title, sizeof(title)); hists__browser_title(browser->hists, hbt, title, sizeof(title));
if (ui_browser__show(&browser->b, title, if (ui_browser__show(&browser->b, title,
"Press '?' for help on key bindings") < 0) "Press '?' for help on key bindings") < 0)
...@@ -417,7 +419,8 @@ static int hist_browser__run(struct hist_browser *browser, ...@@ -417,7 +419,8 @@ static int hist_browser__run(struct hist_browser *browser,
ui_browser__warn_lost_events(&browser->b); ui_browser__warn_lost_events(&browser->b);
} }
hists__browser_title(browser->hists, title, sizeof(title)); hists__browser_title(browser->hists,
hbt, title, sizeof(title));
ui_browser__show_title(&browser->b, title); ui_browser__show_title(&browser->b, title);
continue; continue;
} }
...@@ -1204,7 +1207,15 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *browser ...@@ -1204,7 +1207,15 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *browser
return browser->he_selection->thread; return browser->he_selection->thread;
} }
static int hists__browser_title(struct hists *hists, char *bf, size_t size) /* Check whether the browser is for 'top' or 'report' */
static inline bool is_report_browser(void *timer)
{
return timer == NULL;
}
static int hists__browser_title(struct hists *hists,
struct hist_browser_timer *hbt,
char *bf, size_t size)
{ {
char unit; char unit;
int printed; int printed;
...@@ -1258,6 +1269,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size) ...@@ -1258,6 +1269,13 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size)
if (dso) if (dso)
printed += scnprintf(bf + printed, size - printed, printed += scnprintf(bf + printed, size - printed,
", DSO: %s", dso->short_name); ", DSO: %s", dso->short_name);
if (!is_report_browser(hbt)) {
struct perf_top *top = hbt->arg;
if (top->zero)
printed += scnprintf(bf + printed, size - printed, " [z]");
}
return printed; return printed;
} }
...@@ -1269,12 +1287,6 @@ static inline void free_popup_options(char **options, int n) ...@@ -1269,12 +1287,6 @@ static inline void free_popup_options(char **options, int n)
zfree(&options[i]); zfree(&options[i]);
} }
/* Check whether the browser is for 'top' or 'report' */
static inline bool is_report_browser(void *timer)
{
return timer == NULL;
}
/* /*
* Only runtime switching of perf data file will make "input_name" point * Only runtime switching of perf data file will make "input_name" point
* to a malloced buffer. So add "is_input_name_malloced" flag to decide * to a malloced buffer. So add "is_input_name_malloced" flag to decide
......
...@@ -1003,6 +1003,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) ...@@ -1003,6 +1003,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target)
out_delete_threads: out_delete_threads:
thread_map__delete(evlist->threads); thread_map__delete(evlist->threads);
evlist->threads = NULL;
return -1; return -1;
} }
......
#include <string.h> #include <string.h>
void *memdup(const void *src, size_t len); void *memdup(const void *src, size_t len);
int str_append(char **s, int *len, const char *a);
#include <linux/list.h> #include <linux/list.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/string.h>
#include "ordered-events.h" #include "ordered-events.h"
#include "evlist.h" #include "evlist.h"
#include "session.h" #include "session.h"
...@@ -57,11 +58,45 @@ static void queue_event(struct ordered_events *oe, struct ordered_event *new) ...@@ -57,11 +58,45 @@ static void queue_event(struct ordered_events *oe, struct ordered_event *new)
} }
} }
static union perf_event *__dup_event(struct ordered_events *oe,
union perf_event *event)
{
union perf_event *new_event = NULL;
if (oe->cur_alloc_size < oe->max_alloc_size) {
new_event = memdup(event, event->header.size);
if (new_event)
oe->cur_alloc_size += event->header.size;
}
return new_event;
}
static union perf_event *dup_event(struct ordered_events *oe,
union perf_event *event)
{
return oe->copy_on_queue ? __dup_event(oe, event) : event;
}
static void free_dup_event(struct ordered_events *oe, union perf_event *event)
{
if (oe->copy_on_queue) {
oe->cur_alloc_size -= event->header.size;
free(event);
}
}
#define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct ordered_event)) #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct ordered_event))
static struct ordered_event *alloc_event(struct ordered_events *oe) static struct ordered_event *alloc_event(struct ordered_events *oe,
union perf_event *event)
{ {
struct list_head *cache = &oe->cache; struct list_head *cache = &oe->cache;
struct ordered_event *new = NULL; struct ordered_event *new = NULL;
union perf_event *new_event;
new_event = dup_event(oe, event);
if (!new_event)
return NULL;
if (!list_empty(cache)) { if (!list_empty(cache)) {
new = list_entry(cache->next, struct ordered_event, list); new = list_entry(cache->next, struct ordered_event, list);
...@@ -74,8 +109,10 @@ static struct ordered_event *alloc_event(struct ordered_events *oe) ...@@ -74,8 +109,10 @@ static struct ordered_event *alloc_event(struct ordered_events *oe)
size_t size = MAX_SAMPLE_BUFFER * sizeof(*new); size_t size = MAX_SAMPLE_BUFFER * sizeof(*new);
oe->buffer = malloc(size); oe->buffer = malloc(size);
if (!oe->buffer) if (!oe->buffer) {
free_dup_event(oe, new_event);
return NULL; return NULL;
}
pr("alloc size %" PRIu64 "B (+%zu), max %" PRIu64 "B\n", pr("alloc size %" PRIu64 "B (+%zu), max %" PRIu64 "B\n",
oe->cur_alloc_size, size, oe->max_alloc_size); oe->cur_alloc_size, size, oe->max_alloc_size);
...@@ -90,15 +127,17 @@ static struct ordered_event *alloc_event(struct ordered_events *oe) ...@@ -90,15 +127,17 @@ static struct ordered_event *alloc_event(struct ordered_events *oe)
pr("allocation limit reached %" PRIu64 "B\n", oe->max_alloc_size); pr("allocation limit reached %" PRIu64 "B\n", oe->max_alloc_size);
} }
new->event = new_event;
return new; return new;
} }
struct ordered_event * struct ordered_event *
ordered_events__new(struct ordered_events *oe, u64 timestamp) ordered_events__new(struct ordered_events *oe, u64 timestamp,
union perf_event *event)
{ {
struct ordered_event *new; struct ordered_event *new;
new = alloc_event(oe); new = alloc_event(oe, event);
if (new) { if (new) {
new->timestamp = timestamp; new->timestamp = timestamp;
queue_event(oe, new); queue_event(oe, new);
...@@ -111,6 +150,7 @@ void ordered_events__delete(struct ordered_events *oe, struct ordered_event *eve ...@@ -111,6 +150,7 @@ void ordered_events__delete(struct ordered_events *oe, struct ordered_event *eve
{ {
list_move(&event->list, &oe->cache); list_move(&event->list, &oe->cache);
oe->nr_events--; oe->nr_events--;
free_dup_event(oe, event->event);
} }
static int __ordered_events__flush(struct perf_session *s, static int __ordered_events__flush(struct perf_session *s,
...@@ -240,6 +280,7 @@ void ordered_events__free(struct ordered_events *oe) ...@@ -240,6 +280,7 @@ void ordered_events__free(struct ordered_events *oe)
event = list_entry(oe->to_free.next, struct ordered_event, list); event = list_entry(oe->to_free.next, struct ordered_event, list);
list_del(&event->list); list_del(&event->list);
free_dup_event(oe, event->event);
free(event); free(event);
} }
} }
...@@ -34,9 +34,11 @@ struct ordered_events { ...@@ -34,9 +34,11 @@ struct ordered_events {
int buffer_idx; int buffer_idx;
unsigned int nr_events; unsigned int nr_events;
enum oe_flush last_flush_type; enum oe_flush last_flush_type;
bool copy_on_queue;
}; };
struct ordered_event *ordered_events__new(struct ordered_events *oe, u64 timestamp); struct ordered_event *ordered_events__new(struct ordered_events *oe, u64 timestamp,
union perf_event *event);
void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event); void ordered_events__delete(struct ordered_events *oe, struct ordered_event *event);
int ordered_events__flush(struct perf_session *s, struct perf_tool *tool, int ordered_events__flush(struct perf_session *s, struct perf_tool *tool,
enum oe_flush how); enum oe_flush how);
...@@ -48,4 +50,10 @@ void ordered_events__set_alloc_size(struct ordered_events *oe, u64 size) ...@@ -48,4 +50,10 @@ void ordered_events__set_alloc_size(struct ordered_events *oe, u64 size)
{ {
oe->max_alloc_size = size; oe->max_alloc_size = size;
} }
static inline
void ordered_events__set_copy_on_queue(struct ordered_events *oe, bool copy)
{
oe->copy_on_queue = copy;
}
#endif /* __ORDERED_EVENTS_H */ #endif /* __ORDERED_EVENTS_H */
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#include "parse-options.h" #include "parse-options.h"
#include "parse-events.h" #include "parse-events.h"
#include "exec_cmd.h" #include "exec_cmd.h"
#include "linux/string.h" #include "string.h"
#include "symbol.h" #include "symbol.h"
#include "cache.h" #include "cache.h"
#include "header.h" #include "header.h"
...@@ -30,6 +30,15 @@ extern int parse_events_debug; ...@@ -30,6 +30,15 @@ extern int parse_events_debug;
#endif #endif
int parse_events_parse(void *data, void *scanner); int parse_events_parse(void *data, void *scanner);
static struct perf_pmu_event_symbol *perf_pmu_events_list;
/*
* The variable indicates the number of supported pmu event symbols.
* 0 means not initialized and ready to init
* -1 means failed to init, don't try anymore
* >0 is the number of supported pmu event symbols
*/
static int perf_pmu_events_list_num;
static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = { static struct event_symbol event_symbols_hw[PERF_COUNT_HW_MAX] = {
[PERF_COUNT_HW_CPU_CYCLES] = { [PERF_COUNT_HW_CPU_CYCLES] = {
.symbol = "cpu-cycles", .symbol = "cpu-cycles",
...@@ -863,30 +872,111 @@ int parse_events_name(struct list_head *list, char *name) ...@@ -863,30 +872,111 @@ int parse_events_name(struct list_head *list, char *name)
return 0; return 0;
} }
static int parse_events__scanner(const char *str, void *data, int start_token); static int
comp_pmu(const void *p1, const void *p2)
{
struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1;
struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2;
return strcmp(pmu1->symbol, pmu2->symbol);
}
static void perf_pmu__parse_cleanup(void)
{
if (perf_pmu_events_list_num > 0) {
struct perf_pmu_event_symbol *p;
int i;
for (i = 0; i < perf_pmu_events_list_num; i++) {
p = perf_pmu_events_list + i;
free(p->symbol);
}
free(perf_pmu_events_list);
perf_pmu_events_list = NULL;
perf_pmu_events_list_num = 0;
}
}
#define SET_SYMBOL(str, stype) \
do { \
p->symbol = str; \
if (!p->symbol) \
goto err; \
p->type = stype; \
} while (0)
static int parse_events_fixup(int ret, const char *str, void *data, /*
int start_token) * Read the pmu events list from sysfs
* Save it into perf_pmu_events_list
*/
static void perf_pmu__parse_init(void)
{ {
char *o = strdup(str);
char *s = NULL; struct perf_pmu *pmu = NULL;
char *t = o; struct perf_pmu_alias *alias;
char *p;
int len = 0; int len = 0;
if (!o) pmu = perf_pmu__find("cpu");
return ret; if ((pmu == NULL) || list_empty(&pmu->aliases)) {
while ((p = strsep(&t, ",")) != NULL) { perf_pmu_events_list_num = -1;
if (s) return;
str_append(&s, &len, ",");
str_append(&s, &len, "cpu/");
str_append(&s, &len, p);
str_append(&s, &len, "/");
} }
free(o); list_for_each_entry(alias, &pmu->aliases, list) {
if (!s) if (strchr(alias->name, '-'))
return -ENOMEM; len++;
return parse_events__scanner(s, data, start_token); len++;
}
perf_pmu_events_list = malloc(sizeof(struct perf_pmu_event_symbol) * len);
if (!perf_pmu_events_list)
return;
perf_pmu_events_list_num = len;
len = 0;
list_for_each_entry(alias, &pmu->aliases, list) {
struct perf_pmu_event_symbol *p = perf_pmu_events_list + len;
char *tmp = strchr(alias->name, '-');
if (tmp != NULL) {
SET_SYMBOL(strndup(alias->name, tmp - alias->name),
PMU_EVENT_SYMBOL_PREFIX);
p++;
SET_SYMBOL(strdup(++tmp), PMU_EVENT_SYMBOL_SUFFIX);
len += 2;
} else {
SET_SYMBOL(strdup(alias->name), PMU_EVENT_SYMBOL);
len++;
}
}
qsort(perf_pmu_events_list, len,
sizeof(struct perf_pmu_event_symbol), comp_pmu);
return;
err:
perf_pmu__parse_cleanup();
}
enum perf_pmu_event_symbol_type
perf_pmu__parse_check(const char *name)
{
struct perf_pmu_event_symbol p, *r;
/* scan kernel pmu events from sysfs if needed */
if (perf_pmu_events_list_num == 0)
perf_pmu__parse_init();
/*
* name "cpu" could be prefix of cpu-cycles or cpu// events.
* cpu-cycles has been handled by hardcode.
* So it must be cpu// events, not kernel pmu event.
*/
if ((perf_pmu_events_list_num <= 0) || !strcmp(name, "cpu"))
return PMU_EVENT_SYMBOL_ERR;
p.symbol = strdup(name);
r = bsearch(&p, perf_pmu_events_list,
(size_t) perf_pmu_events_list_num,
sizeof(struct perf_pmu_event_symbol), comp_pmu);
free(p.symbol);
return r ? r->type : PMU_EVENT_SYMBOL_ERR;
} }
static int parse_events__scanner(const char *str, void *data, int start_token) static int parse_events__scanner(const char *str, void *data, int start_token)
...@@ -909,8 +999,6 @@ static int parse_events__scanner(const char *str, void *data, int start_token) ...@@ -909,8 +999,6 @@ static int parse_events__scanner(const char *str, void *data, int start_token)
parse_events__flush_buffer(buffer, scanner); parse_events__flush_buffer(buffer, scanner);
parse_events__delete_buffer(buffer, scanner); parse_events__delete_buffer(buffer, scanner);
parse_events_lex_destroy(scanner); parse_events_lex_destroy(scanner);
if (ret && !strchr(str, '/'))
ret = parse_events_fixup(ret, str, data, start_token);
return ret; return ret;
} }
...@@ -945,6 +1033,7 @@ int parse_events(struct perf_evlist *evlist, const char *str) ...@@ -945,6 +1033,7 @@ int parse_events(struct perf_evlist *evlist, const char *str)
int ret; int ret;
ret = parse_events__scanner(str, &data, PE_START_EVENTS); ret = parse_events__scanner(str, &data, PE_START_EVENTS);
perf_pmu__parse_cleanup();
if (!ret) { if (!ret) {
int entries = data.idx - evlist->nr_entries; int entries = data.idx - evlist->nr_entries;
perf_evlist__splice_list_tail(evlist, &data.list, entries); perf_evlist__splice_list_tail(evlist, &data.list, entries);
......
...@@ -35,6 +35,18 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); ...@@ -35,6 +35,18 @@ extern int parse_filter(const struct option *opt, const char *str, int unset);
#define EVENTS_HELP_MAX (128*1024) #define EVENTS_HELP_MAX (128*1024)
enum perf_pmu_event_symbol_type {
PMU_EVENT_SYMBOL_ERR, /* not a PMU EVENT */
PMU_EVENT_SYMBOL, /* normal style PMU event */
PMU_EVENT_SYMBOL_PREFIX, /* prefix of pre-suf style event */
PMU_EVENT_SYMBOL_SUFFIX, /* suffix of pre-suf style event */
};
struct perf_pmu_event_symbol {
char *symbol;
enum perf_pmu_event_symbol_type type;
};
enum { enum {
PARSE_EVENTS__TERM_TYPE_NUM, PARSE_EVENTS__TERM_TYPE_NUM,
PARSE_EVENTS__TERM_TYPE_STR, PARSE_EVENTS__TERM_TYPE_STR,
...@@ -95,6 +107,8 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx, ...@@ -95,6 +107,8 @@ int parse_events_add_breakpoint(struct list_head *list, int *idx,
void *ptr, char *type); void *ptr, char *type);
int parse_events_add_pmu(struct list_head *list, int *idx, int parse_events_add_pmu(struct list_head *list, int *idx,
char *pmu , struct list_head *head_config); char *pmu , struct list_head *head_config);
enum perf_pmu_event_symbol_type
perf_pmu__parse_check(const char *name);
void parse_events__set_leader(char *name, struct list_head *list); void parse_events__set_leader(char *name, struct list_head *list);
void parse_events_update_lists(struct list_head *list_event, void parse_events_update_lists(struct list_head *list_event,
struct list_head *list_all); struct list_head *list_all);
......
...@@ -51,6 +51,24 @@ static int str(yyscan_t scanner, int token) ...@@ -51,6 +51,24 @@ static int str(yyscan_t scanner, int token)
return token; return token;
} }
static int pmu_str_check(yyscan_t scanner)
{
YYSTYPE *yylval = parse_events_get_lval(scanner);
char *text = parse_events_get_text(scanner);
yylval->str = strdup(text);
switch (perf_pmu__parse_check(text)) {
case PMU_EVENT_SYMBOL_PREFIX:
return PE_PMU_EVENT_PRE;
case PMU_EVENT_SYMBOL_SUFFIX:
return PE_PMU_EVENT_SUF;
case PMU_EVENT_SYMBOL:
return PE_KERNEL_PMU_EVENT;
default:
return PE_NAME;
}
}
static int sym(yyscan_t scanner, int type, int config) static int sym(yyscan_t scanner, int type, int config)
{ {
YYSTYPE *yylval = parse_events_get_lval(scanner); YYSTYPE *yylval = parse_events_get_lval(scanner);
...@@ -178,6 +196,16 @@ alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_AL ...@@ -178,6 +196,16 @@ alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_AL
emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); }
dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); }
/*
* We have to handle the kernel PMU event cycles-ct/cycles-t/mem-loads/mem-stores separately.
* Because the prefix cycles is mixed up with cpu-cycles.
* loads and stores are mixed up with cache event
*/
cycles-ct { return str(yyscanner, PE_KERNEL_PMU_EVENT); }
cycles-t { return str(yyscanner, PE_KERNEL_PMU_EVENT); }
mem-loads { return str(yyscanner, PE_KERNEL_PMU_EVENT); }
mem-stores { return str(yyscanner, PE_KERNEL_PMU_EVENT); }
L1-dcache|l1-d|l1d|L1-data | L1-dcache|l1-d|l1d|L1-data |
L1-icache|l1-i|l1i|L1-instruction | L1-icache|l1-i|l1i|L1-instruction |
LLC|L2 | LLC|L2 |
...@@ -199,7 +227,7 @@ r{num_raw_hex} { return raw(yyscanner); } ...@@ -199,7 +227,7 @@ r{num_raw_hex} { return raw(yyscanner); }
{num_hex} { return value(yyscanner, 16); } {num_hex} { return value(yyscanner, 16); }
{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); } {modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
{name} { return str(yyscanner, PE_NAME); } {name} { return pmu_str_check(yyscanner); }
"/" { BEGIN(config); return '/'; } "/" { BEGIN(config); return '/'; }
- { return '-'; } - { return '-'; }
, { BEGIN(event); return ','; } , { BEGIN(event); return ','; }
......
...@@ -47,6 +47,7 @@ static inc_group_count(struct list_head *list, ...@@ -47,6 +47,7 @@ static inc_group_count(struct list_head *list,
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP %token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
%token PE_ERROR %token PE_ERROR
%token PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
%type <num> PE_VALUE %type <num> PE_VALUE
%type <num> PE_VALUE_SYM_HW %type <num> PE_VALUE_SYM_HW
%type <num> PE_VALUE_SYM_SW %type <num> PE_VALUE_SYM_SW
...@@ -58,6 +59,7 @@ static inc_group_count(struct list_head *list, ...@@ -58,6 +59,7 @@ static inc_group_count(struct list_head *list,
%type <str> PE_MODIFIER_EVENT %type <str> PE_MODIFIER_EVENT
%type <str> PE_MODIFIER_BP %type <str> PE_MODIFIER_BP
%type <str> PE_EVENT_NAME %type <str> PE_EVENT_NAME
%type <str> PE_PMU_EVENT_PRE PE_PMU_EVENT_SUF PE_KERNEL_PMU_EVENT
%type <num> value_sym %type <num> value_sym
%type <head> event_config %type <head> event_config
%type <term> event_term %type <term> event_term
...@@ -220,6 +222,44 @@ PE_NAME '/' '/' ...@@ -220,6 +222,44 @@ PE_NAME '/' '/'
ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL)); ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, NULL));
$$ = list; $$ = list;
} }
|
PE_KERNEL_PMU_EVENT sep_dc
{
struct parse_events_evlist *data = _data;
struct list_head *head;
struct parse_events_term *term;
struct list_head *list;
ALLOC_LIST(head);
ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
$1, 1));
list_add_tail(&term->list, head);
ALLOC_LIST(list);
ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head));
parse_events__free_terms(head);
$$ = list;
}
|
PE_PMU_EVENT_PRE '-' PE_PMU_EVENT_SUF sep_dc
{
struct parse_events_evlist *data = _data;
struct list_head *head;
struct parse_events_term *term;
struct list_head *list;
char pmu_name[128];
snprintf(&pmu_name, 128, "%s-%s", $1, $3);
ALLOC_LIST(head);
ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER,
&pmu_name, 1));
list_add_tail(&term->list, head);
ALLOC_LIST(list);
ABORT_ON(parse_events_add_pmu(list, &data->idx, "cpu", head));
parse_events__free_terms(head);
$$ = list;
}
value_sym: value_sym:
PE_VALUE_SYM_HW PE_VALUE_SYM_HW
......
...@@ -12,16 +12,6 @@ ...@@ -12,16 +12,6 @@
#include "parse-events.h" #include "parse-events.h"
#include "cpumap.h" #include "cpumap.h"
#define UNIT_MAX_LEN 31 /* max length for event unit name */
struct perf_pmu_alias {
char *name;
struct list_head terms; /* HEAD struct parse_events_term -> list */
struct list_head list; /* ELEM */
char unit[UNIT_MAX_LEN+1];
double scale;
};
struct perf_pmu_format { struct perf_pmu_format {
char *name; char *name;
int value; int value;
......
...@@ -30,6 +30,16 @@ struct perf_pmu_info { ...@@ -30,6 +30,16 @@ struct perf_pmu_info {
double scale; double scale;
}; };
#define UNIT_MAX_LEN 31 /* max length for event unit name */
struct perf_pmu_alias {
char *name;
struct list_head terms; /* HEAD struct parse_events_term -> list */
struct list_head list; /* ELEM */
char unit[UNIT_MAX_LEN+1];
double scale;
};
struct perf_pmu *perf_pmu__find(const char *name); struct perf_pmu *perf_pmu__find(const char *name);
int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
struct list_head *head_terms); struct list_head *head_terms);
......
...@@ -532,17 +532,16 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event, ...@@ -532,17 +532,16 @@ int perf_session_queue_event(struct perf_session *s, union perf_event *event,
return -EINVAL; return -EINVAL;
} }
new = ordered_events__new(oe, timestamp); new = ordered_events__new(oe, timestamp, event);
if (!new) { if (!new) {
ordered_events__flush(s, tool, OE_FLUSH__HALF); ordered_events__flush(s, tool, OE_FLUSH__HALF);
new = ordered_events__new(oe, timestamp); new = ordered_events__new(oe, timestamp, event);
} }
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
new->file_offset = file_offset; new->file_offset = file_offset;
new->event = event;
return 0; return 0;
} }
......
...@@ -357,27 +357,3 @@ void *memdup(const void *src, size_t len) ...@@ -357,27 +357,3 @@ void *memdup(const void *src, size_t len)
return p; return p;
} }
/**
* str_append - reallocate string and append another
* @s: pointer to string pointer
* @len: pointer to len (initialized)
* @a: string to append.
*/
int str_append(char **s, int *len, const char *a)
{
int olen = *s ? strlen(*s) : 0;
int nlen = olen + strlen(a) + 1;
if (*len < nlen) {
*len = *len * 2;
if (*len < nlen)
*len = nlen;
*s = realloc(*s, *len);
if (!*s)
return -ENOMEM;
if (olen == 0)
**s = 0;
}
strcat(*s, a);
return 0;
}
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "util.h" #include "util.h"
#include "debug.h" #include "debug.h"
#include "comm.h" #include "comm.h"
#include "unwind.h"
int thread__init_map_groups(struct thread *thread, struct machine *machine) int thread__init_map_groups(struct thread *thread, struct machine *machine)
{ {
...@@ -37,6 +38,9 @@ struct thread *thread__new(pid_t pid, pid_t tid) ...@@ -37,6 +38,9 @@ struct thread *thread__new(pid_t pid, pid_t tid)
thread->cpu = -1; thread->cpu = -1;
INIT_LIST_HEAD(&thread->comm_list); INIT_LIST_HEAD(&thread->comm_list);
if (unwind__prepare_access(thread) < 0)
goto err_thread;
comm_str = malloc(32); comm_str = malloc(32);
if (!comm_str) if (!comm_str)
goto err_thread; goto err_thread;
...@@ -48,6 +52,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) ...@@ -48,6 +52,7 @@ struct thread *thread__new(pid_t pid, pid_t tid)
goto err_thread; goto err_thread;
list_add(&comm->list, &thread->comm_list); list_add(&comm->list, &thread->comm_list);
} }
return thread; return thread;
...@@ -69,6 +74,7 @@ void thread__delete(struct thread *thread) ...@@ -69,6 +74,7 @@ void thread__delete(struct thread *thread)
list_del(&comm->list); list_del(&comm->list);
comm__free(comm); comm__free(comm);
} }
unwind__finish_access(thread);
free(thread); free(thread);
} }
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <libunwind.h> #include <libunwind.h>
#include <libunwind-ptrace.h> #include <libunwind-ptrace.h>
#include "callchain.h"
#include "thread.h" #include "thread.h"
#include "session.h" #include "session.h"
#include "perf_regs.h" #include "perf_regs.h"
...@@ -525,12 +526,12 @@ static unw_accessors_t accessors = { ...@@ -525,12 +526,12 @@ static unw_accessors_t accessors = {
.get_proc_name = get_proc_name, .get_proc_name = get_proc_name,
}; };
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, int unwind__prepare_access(struct thread *thread)
void *arg, int max_stack)
{ {
unw_addr_space_t addr_space; unw_addr_space_t addr_space;
unw_cursor_t c;
int ret; if (callchain_param.record_mode != CALLCHAIN_DWARF)
return 0;
addr_space = unw_create_addr_space(&accessors, 0); addr_space = unw_create_addr_space(&accessors, 0);
if (!addr_space) { if (!addr_space) {
...@@ -538,6 +539,33 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, ...@@ -538,6 +539,33 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
return -ENOMEM; return -ENOMEM;
} }
thread__set_priv(thread, addr_space);
return 0;
}
void unwind__finish_access(struct thread *thread)
{
unw_addr_space_t addr_space;
if (callchain_param.record_mode != CALLCHAIN_DWARF)
return;
addr_space = thread__priv(thread);
unw_destroy_addr_space(addr_space);
}
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
void *arg, int max_stack)
{
unw_addr_space_t addr_space;
unw_cursor_t c;
int ret;
addr_space = thread__priv(ui->thread);
if (addr_space == NULL)
return -1;
ret = unw_init_remote(&c, addr_space, ui); ret = unw_init_remote(&c, addr_space, ui);
if (ret) if (ret)
display_error(ret); display_error(ret);
...@@ -549,7 +577,6 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, ...@@ -549,7 +577,6 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0; ret = ip ? entry(ip, ui->thread, ui->machine, cb, arg) : 0;
} }
unw_destroy_addr_space(addr_space);
return ret; return ret;
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/types.h> #include <linux/types.h>
#include "event.h" #include "event.h"
#include "symbol.h" #include "symbol.h"
#include "thread.h"
struct unwind_entry { struct unwind_entry {
struct map *map; struct map *map;
...@@ -21,6 +22,15 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, ...@@ -21,6 +22,15 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
/* libunwind specific */ /* libunwind specific */
#ifdef HAVE_LIBUNWIND_SUPPORT #ifdef HAVE_LIBUNWIND_SUPPORT
int libunwind__arch_reg_id(int regnum); int libunwind__arch_reg_id(int regnum);
int unwind__prepare_access(struct thread *thread);
void unwind__finish_access(struct thread *thread);
#else
static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
{
return 0;
}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
#endif #endif
#else #else
static inline int static inline int
...@@ -33,5 +43,12 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused, ...@@ -33,5 +43,12 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
{ {
return 0; return 0;
} }
static inline int unwind__prepare_access(struct thread *thread __maybe_unused)
{
return 0;
}
static inline void unwind__finish_access(struct thread *thread __maybe_unused) {}
#endif /* HAVE_DWARF_UNWIND_SUPPORT */ #endif /* HAVE_DWARF_UNWIND_SUPPORT */
#endif /* __UNWIND_H */ #endif /* __UNWIND_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment