Commit 8aedf8a6 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'perf-fixes-for-linus' of...

Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (52 commits)
  perf record: Use per-task-per-cpu events for inherited events
  perf record: Properly synchronize child creation
  perf events: Allow per-task-per-cpu counters
  perf diff: Percent calcs should use double values
  perf diff: Change the default sort order to "dso,symbol"
  perf diff: Use perf_session__fprintf_hists just like 'perf record'
  perf report: Fix cut'n'paste error recently introduced
  perf session: Move perf report specific hits out of perf_session__fprintf_hists
  perf tools: Move hist entries printing routines from perf report
  perf report: Generalize perf_session__fprintf_hists()
  perf symbols: Move symbol filtering to event__preprocess_sample()
  perf symbols: Adopt the strlists for dso, comm
  perf symbols: Make symbol_conf global
  perf probe: Fix to show which probe point is not found
  perf probe: Check symbols in symtab/kallsyms
  perf probe: Check build-id of vmlinux
  perf probe: Reject second attempt of adding same-name event
  perf probe: Support event name for --add option
  perf probe: Add glob matching support on --del
  perf probe: Use strlist__for_each macros in probe-event.c
  ...
parents bac5e54c 60ab2716
......@@ -211,17 +211,11 @@ struct perf_event_attr {
__u32 wakeup_watermark; /* bytes before wakeup */
};
struct { /* Hardware breakpoint info */
__u64 bp_addr;
__u32 bp_type;
__u32 bp_len;
__u64 __bp_reserved_1;
__u64 __bp_reserved_2;
};
__u32 __reserved_2;
__u64 __reserved_3;
__u64 bp_addr;
__u32 bp_type;
__u32 bp_len;
};
/*
......
......@@ -782,6 +782,9 @@ static void __perf_install_in_context(void *info)
add_event_to_ctx(event, ctx);
if (event->cpu != -1 && event->cpu != smp_processor_id())
goto unlock;
/*
* Don't put the event on if it is disabled or if
* it is in a group and the group isn't on.
......@@ -925,6 +928,9 @@ static void __perf_event_enable(void *info)
goto unlock;
__perf_event_mark_enabled(event, ctx);
if (event->cpu != -1 && event->cpu != smp_processor_id())
goto unlock;
/*
* If the event is in a group and isn't the group leader,
* then don't put it on unless the group is on.
......@@ -1595,15 +1601,12 @@ static struct perf_event_context *find_get_context(pid_t pid, int cpu)
unsigned long flags;
int err;
/*
* If cpu is not a wildcard then this is a percpu event:
*/
if (cpu != -1) {
if (pid == -1 && cpu != -1) {
/* Must be root to operate on a CPU event: */
if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN))
return ERR_PTR(-EACCES);
if (cpu < 0 || cpu > num_possible_cpus())
if (cpu < 0 || cpu >= nr_cpumask_bits)
return ERR_PTR(-EINVAL);
/*
......@@ -4564,7 +4567,7 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
if (attr->type >= PERF_TYPE_MAX)
return -EINVAL;
if (attr->__reserved_1 || attr->__reserved_2 || attr->__reserved_3)
if (attr->__reserved_1 || attr->__reserved_2)
return -EINVAL;
if (attr->sample_type & ~(PERF_SAMPLE_MAX-1))
......
perf-diff(1)
==============
NAME
----
perf-diff - Read two perf.data files and display the differential profile
SYNOPSIS
--------
[verse]
'perf diff' [oldfile] [newfile]
DESCRIPTION
-----------
This command displays the performance difference amongst two perf.data files
captured via perf record.
If no parameters are passed it will assume perf.data.old and perf.data.
OPTIONS
-------
-d::
--dsos=::
Only consider symbols in these dsos. CSV that understands
file://filename entries.
-C::
--comms=::
Only consider symbols in these comms. CSV that understands
file://filename entries.
-S::
--symbols=::
Only consider these symbols. CSV that understands
file://filename entries.
-s::
--sort=::
Sort by key(s): pid, comm, dso, symbol.
-t::
--field-separator=::
Use a special separator character and don't pad with spaces, replacing
all occurances of this separator in symbol names (and other output)
with a '.' character, that thus it's the only non valid separator.
-v::
--verbose::
Be verbose, for instance, show the raw counts in addition to the
diff.
SEE ALSO
--------
linkperf:perf-record[1]
......@@ -49,8 +49,9 @@ PROBE SYNTAX
------------
Probe points are defined by following syntax.
"FUNC[+OFFS|:RLN|%return][@SRC]|SRC:ALN [ARG ...]"
"[EVENT=]FUNC[+OFFS|:RLN|%return][@SRC]|SRC:ALN [ARG ...]"
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, 'RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. In addition, 'SRC' specifies a source file which has that function.
It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number.
'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).
......
......@@ -39,6 +39,10 @@ OPTIONS
Only consider these symbols. CSV that understands
file://filename entries.
-s::
--sort=::
Sort by key(s): pid, comm, dso, symbol, parent.
-w::
--field-width=::
Force each column width to the provided list, for large terminal
......
......@@ -8,18 +8,43 @@ perf-trace - Read perf.data (created by perf record) and display trace output
SYNOPSIS
--------
[verse]
'perf trace' [-i <file> | --input=file] symbol_name
'perf trace' {record <script> | report <script> [args] }
DESCRIPTION
-----------
This command reads the input file and displays the trace recorded.
There are several variants of perf trace:
'perf trace' to see a detailed trace of the workload that was
recorded.
'perf trace record <script>' to record the events required for 'perf
trace report'. <script> is the name displayed in the output of
'perf trace --list' i.e. the actual script name minus any language
extension.
'perf trace report <script>' to run and display the results of
<script>. <script> is the name displayed in the output of 'perf
trace --list' i.e. the actual script name minus any language
extension. The perf.data output from a previous run of 'perf trace
record <script>' is used and should be present for this command to
succeed.
OPTIONS
-------
-D::
--dump-raw-trace=::
Display verbose dump of the trace data.
-L::
--Latency=::
Show latency attributes (irqs/preemption disabled, etc).
-l::
--list=::
Display a list of available trace scripts.
-s::
--script=::
Process trace data with the given script ([lang]:script[.ext]).
......
......@@ -370,7 +370,6 @@ LIB_H += util/values.h
LIB_H += util/sort.h
LIB_H += util/hist.h
LIB_H += util/thread.h
LIB_H += util/data_map.h
LIB_H += util/probe-finder.h
LIB_H += util/probe-event.h
......@@ -428,6 +427,7 @@ BUILTIN_OBJS += bench/sched-messaging.o
BUILTIN_OBJS += bench/sched-pipe.o
BUILTIN_OBJS += bench/mem-memcpy.o
BUILTIN_OBJS += builtin-diff.o
BUILTIN_OBJS += builtin-help.o
BUILTIN_OBJS += builtin-sched.o
BUILTIN_OBJS += builtin-buildid-list.o
......@@ -996,8 +996,6 @@ install: all
$(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace'
$(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl'
$(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin'
$(INSTALL) scripts/perl/Perf-Trace-Util/Makefile.PL -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util'
$(INSTALL) scripts/perl/Perf-Trace-Util/README -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util'
ifdef BUILT_INS
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
$(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
......
......@@ -26,7 +26,6 @@
#include "util/sort.h"
#include "util/hist.h"
#include "util/session.h"
#include "util/data_map.h"
static char const *input_name = "perf.data";
......@@ -52,11 +51,6 @@ struct sym_priv {
struct sym_ext *ext;
};
static struct symbol_conf symbol_conf = {
.priv_size = sizeof(struct sym_priv),
.try_vmlinux_path = true,
};
static const char *sym_hist_filter;
static int symbol_filter(struct map *map __used, struct symbol *sym)
......@@ -122,30 +116,32 @@ static void hist_hit(struct hist_entry *he, u64 ip)
h->ip[offset]);
}
static int hist_entry__add(struct addr_location *al, u64 count)
static int perf_session__add_hist_entry(struct perf_session *self,
struct addr_location *al, u64 count)
{
bool hit;
struct hist_entry *he = __hist_entry__add(al, NULL, count, &hit);
struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL,
count, &hit);
if (he == NULL)
return -ENOMEM;
hist_hit(he, al->addr);
return 0;
}
static int process_sample_event(event_t *event)
static int process_sample_event(event_t *event, struct perf_session *session)
{
struct addr_location al;
dump_printf("(IP, %d): %d: %p\n", event->header.misc,
event->ip.pid, (void *)(long)event->ip.ip);
if (event__preprocess_sample(event, &al, symbol_filter) < 0) {
if (event__preprocess_sample(event, session, &al, symbol_filter) < 0) {
fprintf(stderr, "problem processing %d event, skipping it.\n",
event->header.type);
return -1;
}
if (hist_entry__add(&al, 1)) {
if (!al.filtered && perf_session__add_hist_entry(session, &al, 1)) {
fprintf(stderr, "problem incrementing symbol count, "
"skipping event\n");
return -1;
......@@ -429,11 +425,11 @@ static void annotate_sym(struct hist_entry *he)
free_source_line(he, len);
}
static void find_annotations(void)
static void perf_session__find_annotations(struct perf_session *self)
{
struct rb_node *nd;
for (nd = rb_first(&output_hists); nd; nd = rb_next(nd)) {
for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) {
struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
struct sym_priv *priv;
......@@ -454,7 +450,7 @@ static void find_annotations(void)
}
}
static struct perf_file_handler file_handler = {
static struct perf_event_ops event_ops = {
.process_sample_event = process_sample_event,
.process_mmap_event = event__process_mmap,
.process_comm_event = event__process_comm,
......@@ -463,17 +459,14 @@ static struct perf_file_handler file_handler = {
static int __cmd_annotate(void)
{
struct perf_session *session = perf_session__new(input_name, O_RDONLY, force);
struct thread *idle;
int ret;
struct perf_session *session;
session = perf_session__new(input_name, O_RDONLY, force);
if (session == NULL)
return -ENOMEM;
idle = register_idle_thread();
register_perf_file_handler(&file_handler);
ret = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
ret = perf_session__process_events(session, &event_ops);
if (ret)
goto out_delete;
......@@ -483,15 +476,14 @@ static int __cmd_annotate(void)
}
if (verbose > 3)
threads__fprintf(stdout);
perf_session__fprintf(session, stdout);
if (verbose > 2)
dsos__fprintf(stdout);
collapse__resort();
output__resort(event__total[0]);
find_annotations();
perf_session__collapse_resort(session);
perf_session__output_resort(session, session->event_total[0]);
perf_session__find_annotations(session);
out_delete:
perf_session__delete(session);
......@@ -524,29 +516,17 @@ static const struct option options[] = {
OPT_END()
};
static void setup_sorting(void)
int cmd_annotate(int argc, const char **argv, const char *prefix __used)
{
char *tmp, *tok, *str = strdup(sort_order);
for (tok = strtok_r(str, ", ", &tmp);
tok; tok = strtok_r(NULL, ", ", &tmp)) {
if (sort_dimension__add(tok) < 0) {
error("Unknown --sort key: `%s'", tok);
usage_with_options(annotate_usage, options);
}
}
argc = parse_options(argc, argv, options, annotate_usage, 0);
free(str);
}
symbol_conf.priv_size = sizeof(struct sym_priv);
symbol_conf.try_vmlinux_path = true;
int cmd_annotate(int argc, const char **argv, const char *prefix __used)
{
if (symbol__init(&symbol_conf) < 0)
if (symbol__init() < 0)
return -1;
argc = parse_options(argc, argv, options, annotate_usage, 0);
setup_sorting();
setup_sorting(annotate_usage, options);
if (argc) {
/*
......
......@@ -9,7 +9,6 @@
#include "builtin.h"
#include "perf.h"
#include "util/cache.h"
#include "util/data_map.h"
#include "util/debug.h"
#include "util/parse-options.h"
#include "util/session.h"
......@@ -55,8 +54,9 @@ static int perf_file_section__process_buildids(struct perf_file_section *self,
static int __cmd_buildid_list(void)
{
int err = -1;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, force);
struct perf_session *session;
session = perf_session__new(input_name, O_RDONLY, force);
if (session == NULL)
return -1;
......
/*
* builtin-diff.c
*
* Builtin diff command: Analyze two perf.data input files, look up and read
* DSOs and symbol information, sort them and produce a diff.
*/
#include "builtin.h"
#include "util/debug.h"
#include "util/event.h"
#include "util/hist.h"
#include "util/session.h"
#include "util/sort.h"
#include "util/symbol.h"
#include "util/util.h"
#include <stdlib.h>
static char const *input_old = "perf.data.old",
*input_new = "perf.data";
static char diff__default_sort_order[] = "dso,symbol";
static int force;
static bool show_displacement;
static int perf_session__add_hist_entry(struct perf_session *self,
struct addr_location *al, u64 count)
{
bool hit;
struct hist_entry *he = __perf_session__add_hist_entry(self, al, NULL,
count, &hit);
if (he == NULL)
return -ENOMEM;
if (hit)
he->count += count;
return 0;
}
static int diff__process_sample_event(event_t *event, struct perf_session *session)
{
struct addr_location al;
struct sample_data data = { .period = 1, };
dump_printf("(IP, %d): %d: %p\n", event->header.misc,
event->ip.pid, (void *)(long)event->ip.ip);
if (event__preprocess_sample(event, session, &al, NULL) < 0) {
pr_warning("problem processing %d event, skipping it.\n",
event->header.type);
return -1;
}
if (al.filtered)
return 0;
event__parse_sample(event, session->sample_type, &data);
if (al.sym && perf_session__add_hist_entry(session, &al, data.period)) {
pr_warning("problem incrementing symbol count, skipping event\n");
return -1;
}
session->events_stats.total += data.period;
return 0;
}
static struct perf_event_ops event_ops = {
.process_sample_event = diff__process_sample_event,
.process_mmap_event = event__process_mmap,
.process_comm_event = event__process_comm,
.process_exit_event = event__process_task,
.process_fork_event = event__process_task,
.process_lost_event = event__process_lost,
};
static void perf_session__insert_hist_entry_by_name(struct rb_root *root,
struct hist_entry *he)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent = NULL;
struct hist_entry *iter;
while (*p != NULL) {
int cmp;
parent = *p;
iter = rb_entry(parent, struct hist_entry, rb_node);
cmp = strcmp(he->map->dso->name, iter->map->dso->name);
if (cmp > 0)
p = &(*p)->rb_left;
else if (cmp < 0)
p = &(*p)->rb_right;
else {
cmp = strcmp(he->sym->name, iter->sym->name);
if (cmp > 0)
p = &(*p)->rb_left;
else
p = &(*p)->rb_right;
}
}
rb_link_node(&he->rb_node, parent, p);
rb_insert_color(&he->rb_node, root);
}
static void perf_session__resort_by_name(struct perf_session *self)
{
unsigned long position = 1;
struct rb_root tmp = RB_ROOT;
struct rb_node *next = rb_first(&self->hists);
while (next != NULL) {
struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node);
next = rb_next(&n->rb_node);
rb_erase(&n->rb_node, &self->hists);
n->position = position++;
perf_session__insert_hist_entry_by_name(&tmp, n);
}
self->hists = tmp;
}
static struct hist_entry *
perf_session__find_hist_entry_by_name(struct perf_session *self,
struct hist_entry *he)
{
struct rb_node *n = self->hists.rb_node;
while (n) {
struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node);
int cmp = strcmp(he->map->dso->name, iter->map->dso->name);
if (cmp > 0)
n = n->rb_left;
else if (cmp < 0)
n = n->rb_right;
else {
cmp = strcmp(he->sym->name, iter->sym->name);
if (cmp > 0)
n = n->rb_left;
else if (cmp < 0)
n = n->rb_right;
else
return iter;
}
}
return NULL;
}
static void perf_session__match_hists(struct perf_session *old_session,
struct perf_session *new_session)
{
struct rb_node *nd;
perf_session__resort_by_name(old_session);
for (nd = rb_first(&new_session->hists); nd; nd = rb_next(nd)) {
struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node);
pos->pair = perf_session__find_hist_entry_by_name(old_session, pos);
}
}
static int __cmd_diff(void)
{
int ret, i;
struct perf_session *session[2];
session[0] = perf_session__new(input_old, O_RDONLY, force);
session[1] = perf_session__new(input_new, O_RDONLY, force);
if (session[0] == NULL || session[1] == NULL)
return -ENOMEM;
for (i = 0; i < 2; ++i) {
ret = perf_session__process_events(session[i], &event_ops);
if (ret)
goto out_delete;
perf_session__output_resort(session[i], session[i]->events_stats.total);
}
perf_session__match_hists(session[0], session[1]);
perf_session__fprintf_hists(session[1], session[0],
show_displacement, stdout);
out_delete:
for (i = 0; i < 2; ++i)
perf_session__delete(session[i]);
return ret;
}
static const char *const diff_usage[] = {
"perf diff [<options>] [old_file] [new_file]",
};
static const struct option options[] = {
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show symbol address, etc)"),
OPT_BOOLEAN('m', "displacement", &show_displacement,
"Show position displacement relative to baseline"),
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
"dump raw trace in ASCII"),
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
"load module symbols - WARNING: use only with -k and LIVE kernel"),
OPT_BOOLEAN('P', "full-paths", &event_ops.full_paths,
"Don't shorten the pathnames taking into account the cwd"),
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
"only consider symbols in these dsos"),
OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
"only consider symbols in these comms"),
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
"only consider these symbols"),
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
"sort by key(s): pid, comm, dso, symbol, parent"),
OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
"separator for columns, no spaces will be added between "
"columns '.' is reserved."),
OPT_END()
};
int cmd_diff(int argc, const char **argv, const char *prefix __used)
{
sort_order = diff__default_sort_order;
argc = parse_options(argc, argv, options, diff_usage, 0);
if (argc) {
if (argc > 2)
usage_with_options(diff_usage, options);
if (argc == 2) {
input_old = argv[0];
input_new = argv[1];
} else
input_new = argv[0];
}
symbol_conf.exclude_other = false;
if (symbol__init() < 0)
return -1;
setup_sorting(diff_usage, options);
setup_pager();
sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL);
sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", NULL);
sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", NULL);
return __cmd_diff();
}
......@@ -12,7 +12,6 @@
#include "util/trace-event.h"
#include "util/debug.h"
#include "util/data_map.h"
#include <linux/rbtree.h>
......@@ -21,8 +20,6 @@ typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
static char const *input_name = "perf.data";
static u64 sample_type;
static int alloc_flag;
static int caller_flag;
......@@ -312,7 +309,7 @@ process_raw_event(event_t *raw_event __used, void *data,
}
}
static int process_sample_event(event_t *event)
static int process_sample_event(event_t *event, struct perf_session *session)
{
struct sample_data data;
struct thread *thread;
......@@ -322,7 +319,7 @@ static int process_sample_event(event_t *event)
data.cpu = -1;
data.period = 1;
event__parse_sample(event, sample_type, &data);
event__parse_sample(event, session->sample_type, &data);
dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
event->header.misc,
......@@ -330,7 +327,7 @@ static int process_sample_event(event_t *event)
(void *)(long)data.ip,
(long long)data.period);
thread = threads__findnew(event->ip.pid);
thread = perf_session__findnew(session, event->ip.pid);
if (thread == NULL) {
pr_debug("problem processing %d event, skipping it.\n",
event->header.type);
......@@ -345,11 +342,9 @@ static int process_sample_event(event_t *event)
return 0;
}
static int sample_type_check(u64 type)
static int sample_type_check(struct perf_session *session)
{
sample_type = type;
if (!(sample_type & PERF_SAMPLE_RAW)) {
if (!(session->sample_type & PERF_SAMPLE_RAW)) {
fprintf(stderr,
"No trace sample to read. Did you call perf record "
"without -R?");
......@@ -359,28 +354,12 @@ static int sample_type_check(u64 type)
return 0;
}
static struct perf_file_handler file_handler = {
static struct perf_event_ops event_ops = {
.process_sample_event = process_sample_event,
.process_comm_event = event__process_comm,
.sample_type_check = sample_type_check,
};
static int read_events(void)
{
int err;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
if (session == NULL)
return -ENOMEM;
register_idle_thread();
register_perf_file_handler(&file_handler);
err = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
perf_session__delete(session);
return err;
}
static double fragmentation(unsigned long n_req, unsigned long n_alloc)
{
if (n_alloc == 0)
......@@ -389,7 +368,8 @@ static double fragmentation(unsigned long n_req, unsigned long n_alloc)
return 100.0 - (100.0 * n_req / n_alloc);
}
static void __print_result(struct rb_root *root, int n_lines, int is_caller)
static void __print_result(struct rb_root *root, struct perf_session *session,
int n_lines, int is_caller)
{
struct rb_node *next;
......@@ -410,7 +390,7 @@ static void __print_result(struct rb_root *root, int n_lines, int is_caller)
if (is_caller) {
addr = data->call_site;
if (!raw_ip)
sym = map_groups__find_function(kmaps, addr, NULL);
sym = map_groups__find_function(&session->kmaps, session, addr, NULL);
} else
addr = data->ptr;
......@@ -451,12 +431,12 @@ static void print_summary(void)
printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs);
}
static void print_result(void)
static void print_result(struct perf_session *session)
{
if (caller_flag)
__print_result(&root_caller_sorted, caller_lines, 1);
__print_result(&root_caller_sorted, session, caller_lines, 1);
if (alloc_flag)
__print_result(&root_alloc_sorted, alloc_lines, 0);
__print_result(&root_alloc_sorted, session, alloc_lines, 0);
print_summary();
}
......@@ -524,12 +504,20 @@ static void sort_result(void)
static int __cmd_kmem(void)
{
int err;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, 0);
if (session == NULL)
return -ENOMEM;
setup_pager();
read_events();
err = perf_session__process_events(session, &event_ops);
if (err != 0)
goto out_delete;
sort_result();
print_result();
return 0;
print_result(session);
out_delete:
perf_session__delete(session);
return err;
}
static const char * const kmem_usage[] = {
......@@ -778,13 +766,13 @@ static int __cmd_record(int argc, const char **argv)
int cmd_kmem(int argc, const char **argv, const char *prefix __used)
{
symbol__init(0);
argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
if (!argc)
usage_with_options(kmem_usage, kmem_options);
symbol__init();
if (!strncmp(argv[0], "rec", 3)) {
return __cmd_record(argc, argv);
} else if (!strcmp(argv[0], "stat")) {
......
......@@ -38,34 +38,29 @@
#include "util/strlist.h"
#include "util/event.h"
#include "util/debug.h"
#include "util/symbol.h"
#include "util/thread.h"
#include "util/session.h"
#include "util/parse-options.h"
#include "util/parse-events.h" /* For debugfs_path */
#include "util/probe-finder.h"
#include "util/probe-event.h"
/* Default vmlinux search paths */
#define NR_SEARCH_PATH 4
const char *default_search_path[NR_SEARCH_PATH] = {
"/lib/modules/%s/build/vmlinux", /* Custom build kernel */
"/usr/lib/debug/lib/modules/%s/vmlinux", /* Red Hat debuginfo */
"/boot/vmlinux-debug-%s", /* Ubuntu */
"./vmlinux", /* CWD */
};
#define MAX_PATH_LEN 256
#define MAX_PROBES 128
/* Session management structure */
static struct {
char *vmlinux;
char *release;
int need_dwarf;
bool need_dwarf;
bool list_events;
bool force_add;
int nr_probe;
struct probe_point probes[MAX_PROBES];
struct strlist *dellist;
struct perf_session *psession;
struct map *kmap;
} session;
static bool listing;
/* Parse an event definition. Note that any error must die. */
static void parse_probe_event(const char *str)
......@@ -77,7 +72,7 @@ static void parse_probe_event(const char *str)
die("Too many probes (> %d) are specified.", MAX_PROBES);
/* Parse perf-probe event into probe_point */
session.need_dwarf = parse_perf_probe_event(str, pp);
parse_perf_probe_event(str, pp, &session.need_dwarf);
pr_debug("%d arguments\n", pp->nr_args);
}
......@@ -120,34 +115,26 @@ static int opt_del_probe_event(const struct option *opt __used,
return 0;
}
/* Currently just checking function name from symbol map */
static void evaluate_probe_point(struct probe_point *pp)
{
struct symbol *sym;
sym = map__find_symbol_by_name(session.kmap, pp->function,
session.psession, NULL);
if (!sym)
die("Kernel symbol \'%s\' not found - probe not added.",
pp->function);
}
#ifndef NO_LIBDWARF
static int open_default_vmlinux(void)
static int open_vmlinux(void)
{
struct utsname uts;
char fname[MAX_PATH_LEN];
int fd, ret, i;
ret = uname(&uts);
if (ret) {
pr_debug("uname() failed.\n");
return -errno;
}
session.release = uts.release;
for (i = 0; i < NR_SEARCH_PATH; i++) {
ret = snprintf(fname, MAX_PATH_LEN,
default_search_path[i], session.release);
if (ret >= MAX_PATH_LEN || ret < 0) {
pr_debug("Filename(%d,%s) is too long.\n", i,
uts.release);
errno = E2BIG;
return -E2BIG;
}
pr_debug("try to open %s\n", fname);
fd = open(fname, O_RDONLY);
if (fd >= 0)
break;
if (map__load(session.kmap, session.psession, NULL) < 0) {
pr_debug("Failed to load kernel map.\n");
return -EINVAL;
}
return fd;
pr_debug("Try to open %s\n", session.kmap->dso->long_name);
return open(session.kmap->dso->long_name, O_RDONLY);
}
#endif
......@@ -163,21 +150,22 @@ static const struct option options[] = {
OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show parsed arguments, etc)"),
#ifndef NO_LIBDWARF
OPT_STRING('k', "vmlinux", &session.vmlinux, "file",
"vmlinux/module pathname"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
#endif
OPT_BOOLEAN('l', "list", &listing, "list up current probe events"),
OPT_BOOLEAN('l', "list", &session.list_events,
"list up current probe events"),
OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
opt_del_probe_event),
OPT_CALLBACK('a', "add", NULL,
#ifdef NO_LIBDWARF
"FUNC[+OFFS|%return] [ARG ...]",
"[EVENT=]FUNC[+OFFS|%return] [ARG ...]",
#else
"FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]",
"[EVENT=]FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]",
#endif
"probe point definition, where\n"
"\t\tGRP:\tGroup name (optional)\n"
"\t\tNAME:\tEvent name\n"
"\t\tGROUP:\tGroup name (optional)\n"
"\t\tEVENT:\tEvent name\n"
"\t\tFUNC:\tFunction name\n"
"\t\tOFFS:\tOffset from function entry (in byte)\n"
"\t\t%return:\tPut the probe at function return\n"
......@@ -191,6 +179,8 @@ static const struct option options[] = {
#endif
"\t\t\tkprobe-tracer argument format.)\n",
opt_add_probe_event),
OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events"
" with existing name"),
OPT_END()
};
......@@ -204,13 +194,18 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
argc = parse_options(argc, argv, options, probe_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc > 0)
if (argc > 0) {
if (strcmp(argv[0], "-") == 0) {
pr_warning(" Error: '-' is not supported.\n");
usage_with_options(probe_usage, options);
}
parse_probe_event_argv(argc, argv);
}
if ((session.nr_probe == 0 && !session.dellist && !listing))
if ((!session.nr_probe && !session.dellist && !session.list_events))
usage_with_options(probe_usage, options);
if (listing) {
if (session.list_events) {
if (session.nr_probe != 0 || session.dellist) {
pr_warning(" Error: Don't use --list with"
" --add/--del.\n");
......@@ -227,17 +222,28 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
return 0;
}
/* Initialize symbol maps for vmlinux */
symbol_conf.sort_by_name = true;
if (symbol_conf.vmlinux_name == NULL)
symbol_conf.try_vmlinux_path = true;
if (symbol__init() < 0)
die("Failed to init symbol map.");
session.psession = perf_session__new(NULL, O_WRONLY, false);
if (session.psession == NULL)
die("Failed to init perf_session.");
session.kmap = map_groups__find_by_name(&session.psession->kmaps,
MAP__FUNCTION,
"[kernel.kallsyms]");
if (!session.kmap)
die("Could not find kernel map.\n");
if (session.need_dwarf)
#ifdef NO_LIBDWARF
die("Debuginfo-analysis is not supported");
#else /* !NO_LIBDWARF */
pr_debug("Some probes require debuginfo.\n");
if (session.vmlinux) {
pr_debug("Try to open %s.", session.vmlinux);
fd = open(session.vmlinux, O_RDONLY);
} else
fd = open_default_vmlinux();
fd = open_vmlinux();
if (fd < 0) {
if (session.need_dwarf)
die("Could not open debuginfo file.");
......@@ -255,15 +261,22 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
lseek(fd, SEEK_SET, 0);
ret = find_probepoint(fd, pp);
if (ret < 0) {
if (session.need_dwarf)
die("Could not analyze debuginfo.");
pr_warning("An error occurred in debuginfo analysis. Try to use symbols.\n");
break;
if (ret > 0)
continue;
if (ret == 0) { /* No error but failed to find probe point. */
synthesize_perf_probe_point(pp);
die("Probe point '%s' not found. - probe not added.",
pp->probes[0]);
}
/* Error path */
if (session.need_dwarf) {
if (ret == -ENOENT)
pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n");
die("Could not analyze debuginfo.");
}
if (ret == 0) /* No error but failed to find probe point. */
die("No probe point found.");
pr_debug("An error occurred in debuginfo analysis."
" Try to use symbols.\n");
break;
}
close(fd);
......@@ -276,6 +289,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
if (pp->found) /* This probe is already found. */
continue;
evaluate_probe_point(pp);
ret = synthesize_trace_kprobe_event(pp);
if (ret == -E2BIG)
die("probe point definition becomes too long.");
......@@ -284,7 +298,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
}
/* Settng up probe points */
add_trace_kprobe_events(session.probes, session.nr_probe);
add_trace_kprobe_events(session.probes, session.nr_probe,
session.force_add);
return 0;
}
......@@ -123,7 +123,8 @@ static void write_event(event_t *buf, size_t size)
write_output(buf, size);
}
static int process_synthesized_event(event_t *event)
static int process_synthesized_event(event_t *event,
struct perf_session *self __used)
{
write_event(event, event->header.size);
return 0;
......@@ -277,7 +278,7 @@ static void create_counter(int counter, int cpu, pid_t pid)
attr->mmap = track;
attr->comm = track;
attr->inherit = (cpu < 0) && inherit;
attr->inherit = inherit;
attr->disabled = 1;
try_again:
......@@ -401,7 +402,7 @@ static void atexit_header(void)
perf_header__write(&session->header, output, true);
}
static int __cmd_record(int argc, const char **argv)
static int __cmd_record(int argc __used, const char **argv)
{
int i, counter;
struct stat st;
......@@ -409,6 +410,8 @@ static int __cmd_record(int argc, const char **argv)
int flags;
int err;
unsigned long waking = 0;
int child_ready_pipe[2], go_pipe[2];
char buf;
page_size = sysconf(_SC_PAGE_SIZE);
nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
......@@ -419,11 +422,25 @@ static int __cmd_record(int argc, const char **argv)
signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler);
if (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0) {
perror("failed to create pipes");
exit(-1);
}
if (!stat(output_name, &st) && st.st_size) {
if (!force && !append_file) {
fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n",
output_name);
exit(-1);
if (!force) {
if (!append_file) {
pr_err("Error, output file %s exists, use -A "
"to append or -f to overwrite.\n",
output_name);
exit(-1);
}
} else {
char oldname[PATH_MAX];
snprintf(oldname, sizeof(oldname), "%s.old",
output_name);
unlink(oldname);
rename(output_name, oldname);
}
} else {
append_file = 0;
......@@ -466,19 +483,65 @@ static int __cmd_record(int argc, const char **argv)
atexit(atexit_header);
if (!system_wide) {
pid = target_pid;
if (pid == -1)
pid = getpid();
if (target_pid == -1) {
pid = fork();
if (pid < 0) {
perror("failed to fork");
exit(-1);
}
open_counters(profile_cpu, pid);
} else {
if (profile_cpu != -1) {
open_counters(profile_cpu, target_pid);
} else {
for (i = 0; i < nr_cpus; i++)
open_counters(i, target_pid);
if (!pid) {
close(child_ready_pipe[0]);
close(go_pipe[1]);
fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
/*
* Do a dummy execvp to get the PLT entry resolved,
* so we avoid the resolver overhead on the real
* execvp call.
*/
execvp("", (char **)argv);
/*
* Tell the parent we're ready to go
*/
close(child_ready_pipe[1]);
/*
* Wait until the parent tells us to go.
*/
if (read(go_pipe[0], &buf, 1) == -1)
perror("unable to read pipe");
execvp(argv[0], (char **)argv);
perror(argv[0]);
exit(-1);
}
child_pid = pid;
if (!system_wide)
target_pid = pid;
close(child_ready_pipe[1]);
close(go_pipe[0]);
/*
* wait for child to settle
*/
if (read(child_ready_pipe[0], &buf, 1) == -1) {
perror("unable to read pipe");
exit(-1);
}
close(child_ready_pipe[0]);
}
if ((!system_wide && !inherit) || profile_cpu != -1) {
open_counters(profile_cpu, target_pid);
} else {
for (i = 0; i < nr_cpus; i++)
open_counters(i, target_pid);
}
if (file_new) {
......@@ -488,33 +551,10 @@ static int __cmd_record(int argc, const char **argv)
}
if (!system_wide)
event__synthesize_thread(pid, process_synthesized_event);
event__synthesize_thread(pid, process_synthesized_event,
session);
else
event__synthesize_threads(process_synthesized_event);
if (target_pid == -1 && argc) {
pid = fork();
if (pid < 0)
die("failed to fork");
if (!pid) {
if (execvp(argv[0], (char **)argv)) {
perror(argv[0]);
exit(-1);
}
} else {
/*
* Wait a bit for the execv'ed child to appear
* and be updated in /proc
* FIXME: Do you know a less heuristical solution?
*/
usleep(1000);
event__synthesize_thread(pid,
process_synthesized_event);
}
child_pid = pid;
}
event__synthesize_threads(process_synthesized_event, session);
if (realtime_prio) {
struct sched_param param;
......@@ -526,6 +566,11 @@ static int __cmd_record(int argc, const char **argv)
}
}
/*
* Let the child rip
*/
close(go_pipe[1]);
for (;;) {
int hits = samples;
......@@ -620,13 +665,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
{
int counter;
symbol__init(0);
argc = parse_options(argc, argv, options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc && target_pid == -1 && !system_wide)
PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc && target_pid == -1 && (!system_wide || profile_cpu == -1))
usage_with_options(record_usage, options);
symbol__init();
if (!nr_counters) {
nr_counters = 1;
attrs[0].type = PERF_TYPE_HARDWARE;
......
This diff is collapsed.
This diff is collapsed.
......@@ -30,15 +30,12 @@
#include "util/parse-options.h"
#include "util/parse-events.h"
#include "util/event.h"
#include "util/data_map.h"
#include "util/session.h"
#include "util/svghelper.h"
static char const *input_name = "perf.data";
static char const *output_name = "output.svg";
static u64 sample_type;
static unsigned int numcpus;
static u64 min_freq; /* Lowest CPU frequency seen */
static u64 max_freq; /* Highest CPU frequency seen */
......@@ -281,21 +278,19 @@ static int cpus_cstate_state[MAX_CPUS];
static u64 cpus_pstate_start_times[MAX_CPUS];
static u64 cpus_pstate_state[MAX_CPUS];
static int
process_comm_event(event_t *event)
static int process_comm_event(event_t *event, struct perf_session *session __used)
{
pid_set_comm(event->comm.pid, event->comm.comm);
return 0;
}
static int
process_fork_event(event_t *event)
static int process_fork_event(event_t *event, struct perf_session *session __used)
{
pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
return 0;
}
static int
process_exit_event(event_t *event)
static int process_exit_event(event_t *event, struct perf_session *session __used)
{
pid_exit(event->fork.pid, event->fork.time);
return 0;
......@@ -480,17 +475,16 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
}
static int
process_sample_event(event_t *event)
static int process_sample_event(event_t *event, struct perf_session *session)
{
struct sample_data data;
struct trace_entry *te;
memset(&data, 0, sizeof(data));
event__parse_sample(event, sample_type, &data);
event__parse_sample(event, session->sample_type, &data);
if (sample_type & PERF_SAMPLE_TIME) {
if (session->sample_type & PERF_SAMPLE_TIME) {
if (!first_time || first_time > data.time)
first_time = data.time;
if (last_time < data.time)
......@@ -498,7 +492,7 @@ process_sample_event(event_t *event)
}
te = (void *)data.raw_data;
if (sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) {
if (session->sample_type & PERF_SAMPLE_RAW && data.raw_size > 0) {
char *event_str;
struct power_entry *pe;
......@@ -575,16 +569,16 @@ static void end_sample_processing(void)
}
}
static u64 sample_time(event_t *event)
static u64 sample_time(event_t *event, const struct perf_session *session)
{
int cursor;
cursor = 0;
if (sample_type & PERF_SAMPLE_IP)
if (session->sample_type & PERF_SAMPLE_IP)
cursor++;
if (sample_type & PERF_SAMPLE_TID)
if (session->sample_type & PERF_SAMPLE_TID)
cursor++;
if (sample_type & PERF_SAMPLE_TIME)
if (session->sample_type & PERF_SAMPLE_TIME)
return event->sample.array[cursor];
return 0;
}
......@@ -594,8 +588,7 @@ static u64 sample_time(event_t *event)
* We first queue all events, sorted backwards by insertion.
* The order will get flipped later.
*/
static int
queue_sample_event(event_t *event)
static int queue_sample_event(event_t *event, struct perf_session *session)
{
struct sample_wrapper *copy, *prev;
int size;
......@@ -609,7 +602,7 @@ queue_sample_event(event_t *event)
memset(copy, 0, size);
copy->next = NULL;
copy->timestamp = sample_time(event);
copy->timestamp = sample_time(event, session);
memcpy(&copy->data, event, event->sample.header.size);
......@@ -1021,7 +1014,7 @@ static void write_svg_file(const char *filename)
svg_close();
}
static void process_samples(void)
static void process_samples(struct perf_session *session)
{
struct sample_wrapper *cursor;
event_t *event;
......@@ -1032,15 +1025,13 @@ static void process_samples(void)
while (cursor) {
event = (void *)&cursor->data;
cursor = cursor->next;
process_sample_event(event);
process_sample_event(event, session);
}
}
static int sample_type_check(u64 type)
static int sample_type_check(struct perf_session *session)
{
sample_type = type;
if (!(sample_type & PERF_SAMPLE_RAW)) {
if (!(session->sample_type & PERF_SAMPLE_RAW)) {
fprintf(stderr, "No trace samples found in the file.\n"
"Have you used 'perf timechart record' to record it?\n");
return -1;
......@@ -1049,7 +1040,7 @@ static int sample_type_check(u64 type)
return 0;
}
static struct perf_file_handler file_handler = {
static struct perf_event_ops event_ops = {
.process_comm_event = process_comm_event,
.process_fork_event = process_fork_event,
.process_exit_event = process_exit_event,
......@@ -1065,13 +1056,11 @@ static int __cmd_timechart(void)
if (session == NULL)
return -ENOMEM;
register_perf_file_handler(&file_handler);
ret = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
ret = perf_session__process_events(session, &event_ops);
if (ret)
goto out_delete;
process_samples();
process_samples(session);
end_sample_processing();
......@@ -1148,11 +1137,11 @@ static const struct option options[] = {
int cmd_timechart(int argc, const char **argv, const char *prefix __used)
{
symbol__init(0);
argc = parse_options(argc, argv, options, timechart_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
symbol__init();
if (argc && !strncmp(argv[0], "rec", 3))
return __cmd_record(argc, argv);
else if (argc)
......
......@@ -20,8 +20,9 @@
#include "perf.h"
#include "util/symbol.h"
#include "util/color.h"
#include "util/session.h"
#include "util/symbol.h"
#include "util/thread.h"
#include "util/util.h"
#include <linux/rbtree.h>
......@@ -79,7 +80,6 @@ static int dump_symtab = 0;
static bool hide_kernel_symbols = false;
static bool hide_user_symbols = false;
static struct winsize winsize;
struct symbol_conf symbol_conf;
/*
* Source
......@@ -926,7 +926,8 @@ static int symbol_filter(struct map *map, struct symbol *sym)
return 0;
}
static void event__process_sample(const event_t *self, int counter)
static void event__process_sample(const event_t *self,
struct perf_session *session, int counter)
{
u64 ip = self->ip.ip;
struct sym_entry *syme;
......@@ -946,8 +947,8 @@ static void event__process_sample(const event_t *self, int counter)
return;
}
if (event__preprocess_sample(self, &al, symbol_filter) < 0 ||
al.sym == NULL)
if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 ||
al.sym == NULL || al.filtered)
return;
syme = symbol__priv(al.sym);
......@@ -965,14 +966,14 @@ static void event__process_sample(const event_t *self, int counter)
}
}
static int event__process(event_t *event)
static int event__process(event_t *event, struct perf_session *session)
{
switch (event->header.type) {
case PERF_RECORD_COMM:
event__process_comm(event);
event__process_comm(event, session);
break;
case PERF_RECORD_MMAP:
event__process_mmap(event);
event__process_mmap(event, session);
break;
default:
break;
......@@ -999,7 +1000,8 @@ static unsigned int mmap_read_head(struct mmap_data *md)
return head;
}
static void mmap_read_counter(struct mmap_data *md)
static void perf_session__mmap_read_counter(struct perf_session *self,
struct mmap_data *md)
{
unsigned int head = mmap_read_head(md);
unsigned int old = md->prev;
......@@ -1052,9 +1054,9 @@ static void mmap_read_counter(struct mmap_data *md)
}
if (event->header.type == PERF_RECORD_SAMPLE)
event__process_sample(event, md->counter);
event__process_sample(event, self, md->counter);
else
event__process(event);
event__process(event, self);
old += size;
}
......@@ -1064,13 +1066,13 @@ static void mmap_read_counter(struct mmap_data *md)
static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];
static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];
static void mmap_read(void)
static void perf_session__mmap_read(struct perf_session *self)
{
int i, counter;
for (i = 0; i < nr_cpus; i++) {
for (counter = 0; counter < nr_counters; counter++)
mmap_read_counter(&mmap_array[i][counter]);
perf_session__mmap_read_counter(self, &mmap_array[i][counter]);
}
}
......@@ -1155,11 +1157,18 @@ static int __cmd_top(void)
pthread_t thread;
int i, counter;
int ret;
/*
* FIXME: perf_session__new should allow passing a O_MMAP, so that all this
* mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
*/
struct perf_session *session = perf_session__new(NULL, O_WRONLY, false);
if (session == NULL)
return -ENOMEM;
if (target_pid != -1)
event__synthesize_thread(target_pid, event__process);
event__synthesize_thread(target_pid, event__process, session);
else
event__synthesize_threads(event__process);
event__synthesize_threads(event__process, session);
for (i = 0; i < nr_cpus; i++) {
group_fd = -1;
......@@ -1170,7 +1179,7 @@ static int __cmd_top(void)
/* Wait for a minimal set of events before starting the snapshot */
poll(event_array, nr_poll, 100);
mmap_read();
perf_session__mmap_read(session);
if (pthread_create(&thread, NULL, display_thread, NULL)) {
printf("Could not create display thread.\n");
......@@ -1190,7 +1199,7 @@ static int __cmd_top(void)
while (1) {
int hits = samples;
mmap_read();
perf_session__mmap_read(session);
if (hits == samples)
ret = poll(event_array, nr_poll, 100);
......@@ -1273,7 +1282,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
(nr_counters + 1) * sizeof(unsigned long));
if (symbol_conf.vmlinux_name == NULL)
symbol_conf.try_vmlinux_path = true;
if (symbol__init(&symbol_conf) < 0)
if (symbol__init() < 0)
return -1;
if (delay_secs < 1)
......
This diff is collapsed.
......@@ -17,6 +17,7 @@ extern int check_pager_config(const char *cmd);
extern int cmd_annotate(int argc, const char **argv, const char *prefix);
extern int cmd_bench(int argc, const char **argv, const char *prefix);
extern int cmd_buildid_list(int argc, const char **argv, const char *prefix);
extern int cmd_diff(int argc, const char **argv, const char *prefix);
extern int cmd_help(int argc, const char **argv, const char *prefix);
extern int cmd_sched(int argc, const char **argv, const char *prefix);
extern int cmd_list(int argc, const char **argv, const char *prefix);
......
......@@ -5,6 +5,7 @@
perf-annotate mainporcelain common
perf-bench mainporcelain common
perf-buildid-list mainporcelain common
perf-diff mainporcelain common
perf-list mainporcelain common
perf-sched mainporcelain common
perf-record mainporcelain common
......
......@@ -286,6 +286,7 @@ static void handle_internal_command(int argc, const char **argv)
const char *cmd = argv[0];
static struct cmd_struct commands[] = {
{ "buildid-list", cmd_buildid_list, 0 },
{ "diff", cmd_diff, 0 },
{ "help", cmd_help, 0 },
{ "list", cmd_list, 0 },
{ "record", cmd_record, 0 },
......
#!/bin/bash
# description: useless but exhaustive test script
perf trace -s ~/libexec/perf-core/scripts/perl/check-perf-trace.pl
......
#!/bin/bash
perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl
# description: r/w activity for a program, by file
# args: <comm>
perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-file.pl $1
#!/bin/bash
# description: system-wide r/w activity
perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl
......
#!/bin/bash
# description: system-wide min/max/avg wakeup latency
perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl
......
#!/bin/bash
# description: workqueue stats (ins/exe/create/destroy)
perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl
......
......@@ -18,8 +18,9 @@ use lib "./Perf-Trace-Util/lib";
use Perf::Trace::Core;
use Perf::Trace::Util;
# change this to the comm of the program you're interested in
my $for_comm = "perf";
my $usage = "perf trace -s rw-by-file.pl <comm>\n";
my $for_comm = shift or die $usage;
my %reads;
my %writes;
......
#include "data_map.h"
#include "symbol.h"
#include "util.h"
#include "debug.h"
#include "thread.h"
#include "session.h"
static struct perf_file_handler *curr_handler;
static unsigned long mmap_window = 32;
static char __cwd[PATH_MAX];
static int process_event_stub(event_t *event __used)
static int process_event_stub(event_t *event __used,
struct perf_session *session __used)
{
dump_printf(": unhandled!\n");
return 0;
}
void register_perf_file_handler(struct perf_file_handler *handler)
static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
{
if (!handler->process_sample_event)
handler->process_sample_event = process_event_stub;
......@@ -34,8 +31,6 @@ void register_perf_file_handler(struct perf_file_handler *handler)
handler->process_throttle_event = process_event_stub;
if (!handler->process_unthrottle_event)
handler->process_unthrottle_event = process_event_stub;
curr_handler = handler;
}
static const char *event__name[] = {
......@@ -61,8 +56,9 @@ void event__print_totals(void)
event__name[i], event__total[i]);
}
static int
process_event(event_t *event, unsigned long offset, unsigned long head)
static int process_event(event_t *event, struct perf_session *session,
struct perf_event_ops *ops,
unsigned long offset, unsigned long head)
{
trace_event(event);
......@@ -77,25 +73,25 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
switch (event->header.type) {
case PERF_RECORD_SAMPLE:
return curr_handler->process_sample_event(event);
return ops->process_sample_event(event, session);
case PERF_RECORD_MMAP:
return curr_handler->process_mmap_event(event);
return ops->process_mmap_event(event, session);
case PERF_RECORD_COMM:
return curr_handler->process_comm_event(event);
return ops->process_comm_event(event, session);
case PERF_RECORD_FORK:
return curr_handler->process_fork_event(event);
return ops->process_fork_event(event, session);
case PERF_RECORD_EXIT:
return curr_handler->process_exit_event(event);
return ops->process_exit_event(event, session);
case PERF_RECORD_LOST:
return curr_handler->process_lost_event(event);
return ops->process_lost_event(event, session);
case PERF_RECORD_READ:
return curr_handler->process_read_event(event);
return ops->process_read_event(event, session);
case PERF_RECORD_THROTTLE:
return curr_handler->process_throttle_event(event);
return ops->process_throttle_event(event, session);
case PERF_RECORD_UNTHROTTLE:
return curr_handler->process_unthrottle_event(event);
return ops->process_unthrottle_event(event, session);
default:
curr_handler->total_unknown++;
ops->total_unknown++;
return -1;
}
}
......@@ -129,44 +125,58 @@ int perf_header__read_build_ids(int input, u64 offset, u64 size)
return err;
}
static struct thread *perf_session__register_idle_thread(struct perf_session *self)
{
struct thread *thread = perf_session__findnew(self, 0);
if (!thread || thread__set_comm(thread, "swapper")) {
pr_err("problem inserting idle task.\n");
thread = NULL;
}
return thread;
}
int perf_session__process_events(struct perf_session *self,
int full_paths, int *cwdlen, char **cwd)
struct perf_event_ops *ops)
{
int err;
unsigned long head, shift;
unsigned long offset = 0;
size_t page_size;
u64 sample_type;
event_t *event;
uint32_t size;
char *buf;
if (curr_handler == NULL) {
pr_debug("Forgot to register perf file handler\n");
return -EINVAL;
}
if (perf_session__register_idle_thread(self) == NULL)
return -ENOMEM;
perf_event_ops__fill_defaults(ops);
page_size = getpagesize();
head = self->header.data_offset;
sample_type = perf_header__sample_type(&self->header);
self->sample_type = perf_header__sample_type(&self->header);
err = -EINVAL;
if (curr_handler->sample_type_check &&
curr_handler->sample_type_check(sample_type) < 0)
if (ops->sample_type_check && ops->sample_type_check(self) < 0)
goto out_err;
if (!full_paths) {
if (getcwd(__cwd, sizeof(__cwd)) == NULL) {
pr_err("failed to get the current directory\n");
if (!ops->full_paths) {
char bf[PATH_MAX];
if (getcwd(bf, sizeof(bf)) == NULL) {
err = -errno;
out_getcwd_err:
pr_err("failed to get the current directory\n");
goto out_err;
}
*cwd = __cwd;
*cwdlen = strlen(*cwd);
} else {
*cwd = NULL;
*cwdlen = 0;
self->cwd = strdup(bf);
if (self->cwd == NULL) {
err = -ENOMEM;
goto out_getcwd_err;
}
self->cwdlen = strlen(self->cwd);
}
shift = page_size * (head / page_size);
......@@ -174,7 +184,7 @@ int perf_session__process_events(struct perf_session *self,
head -= shift;
remap:
buf = mmap(NULL, page_size * mmap_window, PROT_READ,
buf = mmap(NULL, page_size * self->mmap_window, PROT_READ,
MAP_SHARED, self->fd, offset);
if (buf == MAP_FAILED) {
pr_err("failed to mmap file\n");
......@@ -189,12 +199,12 @@ int perf_session__process_events(struct perf_session *self,
if (!size)
size = 8;
if (head + event->header.size >= page_size * mmap_window) {
if (head + event->header.size >= page_size * self->mmap_window) {
int munmap_ret;
shift = page_size * (head / page_size);
munmap_ret = munmap(buf, page_size * mmap_window);
munmap_ret = munmap(buf, page_size * self->mmap_window);
assert(munmap_ret == 0);
offset += shift;
......@@ -209,7 +219,7 @@ int perf_session__process_events(struct perf_session *self,
(void *)(long)event->header.size,
event->header.type);
if (!size || process_event(event, offset, head) < 0) {
if (!size || process_event(event, self, ops, offset, head) < 0) {
dump_printf("%p [%p]: skipping unknown header type: %d\n",
(void *)(offset + head),
......
#ifndef __PERF_DATAMAP_H
#define __PERF_DATAMAP_H
#include "event.h"
#include "header.h"
#include "session.h"
typedef int (*event_type_handler_t)(event_t *);
struct perf_file_handler {
event_type_handler_t process_sample_event;
event_type_handler_t process_mmap_event;
event_type_handler_t process_comm_event;
event_type_handler_t process_fork_event;
event_type_handler_t process_exit_event;
event_type_handler_t process_lost_event;
event_type_handler_t process_read_event;
event_type_handler_t process_throttle_event;
event_type_handler_t process_unthrottle_event;
int (*sample_type_check)(u64 sample_type);
unsigned long total_unknown;
};
void register_perf_file_handler(struct perf_file_handler *handler);
int perf_session__process_events(struct perf_session *self,
int full_paths, int *cwdlen, char **cwd);
int perf_header__read_build_ids(int input, u64 offset, u64 file_size);
#endif
#include <linux/types.h>
#include "event.h"
#include "debug.h"
#include "session.h"
#include "sort.h"
#include "string.h"
#include "strlist.h"
#include "thread.h"
static pid_t event__synthesize_comm(pid_t pid, int full,
int (*process)(event_t *event))
int (*process)(event_t *event,
struct perf_session *session),
struct perf_session *session)
{
event_t ev;
char filename[PATH_MAX];
......@@ -54,7 +59,7 @@ static pid_t event__synthesize_comm(pid_t pid, int full,
if (!full) {
ev.comm.tid = pid;
process(&ev);
process(&ev, session);
goto out_fclose;
}
......@@ -72,7 +77,7 @@ static pid_t event__synthesize_comm(pid_t pid, int full,
ev.comm.tid = pid;
process(&ev);
process(&ev, session);
}
closedir(tasks);
......@@ -86,7 +91,9 @@ static pid_t event__synthesize_comm(pid_t pid, int full,
}
static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
int (*process)(event_t *event))
int (*process)(event_t *event,
struct perf_session *session),
struct perf_session *session)
{
char filename[PATH_MAX];
FILE *fp;
......@@ -141,7 +148,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
ev.mmap.pid = tgid;
ev.mmap.tid = pid;
process(&ev);
process(&ev, session);
}
}
......@@ -149,15 +156,20 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
return 0;
}
int event__synthesize_thread(pid_t pid, int (*process)(event_t *event))
int event__synthesize_thread(pid_t pid,
int (*process)(event_t *event,
struct perf_session *session),
struct perf_session *session)
{
pid_t tgid = event__synthesize_comm(pid, 1, process);
pid_t tgid = event__synthesize_comm(pid, 1, process, session);
if (tgid == -1)
return -1;
return event__synthesize_mmap_events(pid, tgid, process);
return event__synthesize_mmap_events(pid, tgid, process, session);
}
void event__synthesize_threads(int (*process)(event_t *event))
void event__synthesize_threads(int (*process)(event_t *event,
struct perf_session *session),
struct perf_session *session)
{
DIR *proc;
struct dirent dirent, *next;
......@@ -171,24 +183,47 @@ void event__synthesize_threads(int (*process)(event_t *event))
if (*end) /* only interested in proper numerical dirents */
continue;
event__synthesize_thread(pid, process);
event__synthesize_thread(pid, process, session);
}
closedir(proc);
}
char *event__cwd;
int event__cwdlen;
static void thread__comm_adjust(struct thread *self)
{
char *comm = self->comm;
struct events_stats event__stats;
if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
(!symbol_conf.comm_list ||
strlist__has_entry(symbol_conf.comm_list, comm))) {
unsigned int slen = strlen(comm);
int event__process_comm(event_t *self)
if (slen > comms__col_width) {
comms__col_width = slen;
threads__col_width = slen + 6;
}
}
}
static int thread__set_comm_adjust(struct thread *self, const char *comm)
{
struct thread *thread = threads__findnew(self->comm.pid);
int ret = thread__set_comm(self, comm);
if (ret)
return ret;
thread__comm_adjust(self);
return 0;
}
int event__process_comm(event_t *self, struct perf_session *session)
{
struct thread *thread = perf_session__findnew(session, self->comm.pid);
dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid);
if (thread == NULL || thread__set_comm(thread, self->comm.comm)) {
if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) {
dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
return -1;
}
......@@ -196,18 +231,18 @@ int event__process_comm(event_t *self)
return 0;
}
int event__process_lost(event_t *self)
int event__process_lost(event_t *self, struct perf_session *session)
{
dump_printf(": id:%Ld: lost:%Ld\n", self->lost.id, self->lost.lost);
event__stats.lost += self->lost.lost;
session->events_stats.lost += self->lost.lost;
return 0;
}
int event__process_mmap(event_t *self)
int event__process_mmap(event_t *self, struct perf_session *session)
{
struct thread *thread = threads__findnew(self->mmap.pid);
struct thread *thread = perf_session__findnew(session, self->mmap.pid);
struct map *map = map__new(&self->mmap, MAP__FUNCTION,
event__cwd, event__cwdlen);
session->cwd, session->cwdlen);
dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n",
self->mmap.pid, self->mmap.tid,
......@@ -224,10 +259,10 @@ int event__process_mmap(event_t *self)
return 0;
}
int event__process_task(event_t *self)
int event__process_task(event_t *self, struct perf_session *session)
{
struct thread *thread = threads__findnew(self->fork.pid);
struct thread *parent = threads__findnew(self->fork.ppid);
struct thread *thread = perf_session__findnew(session, self->fork.pid);
struct thread *parent = perf_session__findnew(session, self->fork.ppid);
dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
self->fork.ppid, self->fork.ptid);
......@@ -249,7 +284,8 @@ int event__process_task(event_t *self)
return 0;
}
void thread__find_addr_location(struct thread *self, u8 cpumode,
void thread__find_addr_location(struct thread *self,
struct perf_session *session, u8 cpumode,
enum map_type type, u64 addr,
struct addr_location *al,
symbol_filter_t filter)
......@@ -261,7 +297,7 @@ void thread__find_addr_location(struct thread *self, u8 cpumode,
if (cpumode & PERF_RECORD_MISC_KERNEL) {
al->level = 'k';
mg = kmaps;
mg = &session->kmaps;
} else if (cpumode & PERF_RECORD_MISC_USER)
al->level = '.';
else {
......@@ -282,33 +318,73 @@ void thread__find_addr_location(struct thread *self, u8 cpumode,
* "[vdso]" dso, but for now lets use the old trick of looking
* in the whole kernel symbol list.
*/
if ((long long)al->addr < 0 && mg != kmaps) {
mg = kmaps;
if ((long long)al->addr < 0 && mg != &session->kmaps) {
mg = &session->kmaps;
goto try_again;
}
al->sym = NULL;
} else {
al->addr = al->map->map_ip(al->map, al->addr);
al->sym = map__find_symbol(al->map, al->addr, filter);
al->sym = map__find_symbol(al->map, session, al->addr, filter);
}
}
int event__preprocess_sample(const event_t *self, struct addr_location *al,
symbol_filter_t filter)
static void dso__calc_col_width(struct dso *self)
{
if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
(!symbol_conf.dso_list ||
strlist__has_entry(symbol_conf.dso_list, self->name))) {
unsigned int slen = strlen(self->name);
if (slen > dsos__col_width)
dsos__col_width = slen;
}
self->slen_calculated = 1;
}
int event__preprocess_sample(const event_t *self, struct perf_session *session,
struct addr_location *al, symbol_filter_t filter)
{
u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
struct thread *thread = threads__findnew(self->ip.pid);
struct thread *thread = perf_session__findnew(session, self->ip.pid);
if (thread == NULL)
return -1;
if (symbol_conf.comm_list &&
!strlist__has_entry(symbol_conf.comm_list, thread->comm))
goto out_filtered;
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
thread__find_addr_location(thread, cpumode, MAP__FUNCTION,
thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION,
self->ip.ip, al, filter);
dump_printf(" ...... dso: %s\n",
al->map ? al->map->dso->long_name :
al->level == 'H' ? "[hypervisor]" : "<not found>");
/*
* We have to do this here as we may have a dso with no symbol hit that
* has a name longer than the ones with symbols sampled.
*/
if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated)
dso__calc_col_width(al->map->dso);
if (symbol_conf.dso_list &&
(!al->map || !al->map->dso ||
!(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) ||
(al->map->dso->short_name != al->map->dso->long_name &&
strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name)))))
goto out_filtered;
if (symbol_conf.sym_list && al->sym &&
!strlist__has_entry(symbol_conf.sym_list, al->sym->name))
goto out_filtered;
al->filtered = false;
return 0;
out_filtered:
al->filtered = true;
return 0;
}
......
......@@ -149,29 +149,35 @@ void map__delete(struct map *self);
struct map *map__clone(struct map *self);
int map__overlap(struct map *l, struct map *r);
size_t map__fprintf(struct map *self, FILE *fp);
struct symbol *map__find_symbol(struct map *self, u64 addr,
symbol_filter_t filter);
struct perf_session;
int map__load(struct map *self, struct perf_session *session,
symbol_filter_t filter);
struct symbol *map__find_symbol(struct map *self, struct perf_session *session,
u64 addr, symbol_filter_t filter);
struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
struct perf_session *session,
symbol_filter_t filter);
void map__fixup_start(struct map *self);
void map__fixup_end(struct map *self);
int event__synthesize_thread(pid_t pid, int (*process)(event_t *event));
void event__synthesize_threads(int (*process)(event_t *event));
extern char *event__cwd;
extern int event__cwdlen;
extern struct events_stats event__stats;
extern unsigned long event__total[PERF_RECORD_MAX];
int event__synthesize_thread(pid_t pid,
int (*process)(event_t *event,
struct perf_session *session),
struct perf_session *session);
void event__synthesize_threads(int (*process)(event_t *event,
struct perf_session *session),
struct perf_session *session);
int event__process_comm(event_t *self);
int event__process_lost(event_t *self);
int event__process_mmap(event_t *self);
int event__process_task(event_t *self);
int event__process_comm(event_t *self, struct perf_session *session);
int event__process_lost(event_t *self, struct perf_session *session);
int event__process_mmap(event_t *self, struct perf_session *session);
int event__process_task(event_t *self, struct perf_session *session);
struct addr_location;
int event__preprocess_sample(const event_t *self, struct addr_location *al,
symbol_filter_t filter);
int event__preprocess_sample(const event_t *self, struct perf_session *session,
struct addr_location *al, symbol_filter_t filter);
int event__parse_sample(event_t *event, u64 type, struct sample_data *data);
#endif /* __PERF_RECORD_H */
......@@ -8,8 +8,8 @@
#include "header.h"
#include "../perf.h"
#include "trace-event.h"
#include "session.h"
#include "symbol.h"
#include "data_map.h"
#include "debug.h"
/*
......
This diff is collapsed.
#ifndef __PERF_HIST_H
#define __PERF_HIST_H
#include "../builtin.h"
#include "util.h"
#include "color.h"
#include <linux/list.h>
#include "cache.h"
#include <linux/rbtree.h>
#include "symbol.h"
#include "string.h"
#include <linux/types.h>
#include "callchain.h"
#include "strlist.h"
#include "values.h"
#include "../perf.h"
#include "debug.h"
#include "header.h"
#include "parse-options.h"
#include "parse-events.h"
#include "thread.h"
#include "sort.h"
extern struct rb_root hist;
extern struct rb_root collapse_hists;
extern struct rb_root output_hists;
extern int callchain;
extern struct callchain_param callchain_param;
extern unsigned long total;
extern unsigned long total_mmap;
extern unsigned long total_comm;
extern unsigned long total_fork;
extern unsigned long total_unknown;
extern unsigned long total_lost;
struct hist_entry *__hist_entry__add(struct addr_location *al,
struct symbol *parent,
u64 count, bool *hit);
struct perf_session;
struct hist_entry;
struct addr_location;
struct symbol;
struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self,
struct addr_location *al,
struct symbol *parent,
u64 count, bool *hit);
extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
extern void hist_entry__free(struct hist_entry *);
extern void collapse__insert_entry(struct hist_entry *);
extern void collapse__resort(void);
extern void output__insert_entry(struct hist_entry *, u64);
extern void output__resort(u64);
void hist_entry__free(struct hist_entry *);
void perf_session__output_resort(struct perf_session *self, u64 total_samples);
void perf_session__collapse_resort(struct perf_session *self);
size_t perf_session__fprintf_hists(struct perf_session *self,
struct perf_session *pair,
bool show_displacement, FILE *fp);
#endif /* __PERF_HIST_H */
......@@ -104,11 +104,16 @@ void map__fixup_end(struct map *self)
#define DSO__DELETED "(deleted)"
static int map__load(struct map *self, symbol_filter_t filter)
int map__load(struct map *self, struct perf_session *session,
symbol_filter_t filter)
{
const char *name = self->dso->long_name;
int nr = dso__load(self->dso, self, filter);
int nr;
if (dso__loaded(self->dso, self->type))
return 0;
nr = dso__load(self->dso, self, session, filter);
if (nr < 0) {
if (self->dso->has_build_id) {
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
......@@ -143,19 +148,20 @@ static int map__load(struct map *self, symbol_filter_t filter)
return 0;
}
struct symbol *map__find_symbol(struct map *self, u64 addr,
symbol_filter_t filter)
struct symbol *map__find_symbol(struct map *self, struct perf_session *session,
u64 addr, symbol_filter_t filter)
{
if (!dso__loaded(self->dso, self->type) && map__load(self, filter) < 0)
if (map__load(self, session, filter) < 0)
return NULL;
return dso__find_symbol(self->dso, self->type, addr);
}
struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
struct perf_session *session,
symbol_filter_t filter)
{
if (!dso__loaded(self->dso, self->type) && map__load(self, filter) < 0)
if (map__load(self, session, filter) < 0)
return NULL;
if (!dso__sorted_by_name(self->dso, self->type))
......
This diff is collapsed.
#ifndef _PROBE_EVENT_H
#define _PROBE_EVENT_H
#include <stdbool.h>
#include "probe-finder.h"
#include "strlist.h"
extern int parse_perf_probe_event(const char *str, struct probe_point *pp);
extern void parse_perf_probe_event(const char *str, struct probe_point *pp,
bool *need_dwarf);
extern int synthesize_perf_probe_point(struct probe_point *pp);
extern int synthesize_perf_probe_event(struct probe_point *pp);
extern void parse_trace_kprobe_event(const char *str, char **group,
char **event, struct probe_point *pp);
extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp);
extern int synthesize_trace_kprobe_event(struct probe_point *pp);
extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes);
extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
bool force_add);
extern void del_trace_kprobe_events(struct strlist *dellist);
extern void show_perf_probe_events(void);
......
......@@ -687,10 +687,8 @@ int find_probepoint(int fd, struct probe_point *pp)
struct probe_finder pf = {.pp = pp};
ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
if (ret != DW_DLV_OK) {
pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO.\n");
if (ret != DW_DLV_OK)
return -ENOENT;
}
pp->found = 0;
while (++cu_number) {
......
......@@ -12,6 +12,9 @@ static inline int is_c_varname(const char *name)
}
struct probe_point {
char *event; /* Event name */
char *group; /* Event group */
/* Inputs */
char *file; /* File name */
int line; /* Line number */
......
This diff is collapsed.
#ifndef __PERF_SESSION_H
#define __PERF_SESSION_H
#include "event.h"
#include "header.h"
#include "thread.h"
#include <linux/rbtree.h>
#include "../../../include/linux/perf_event.h"
struct ip_callchain;
struct thread;
struct symbol;
struct perf_session {
struct perf_header header;
unsigned long size;
unsigned long mmap_window;
struct map_groups kmaps;
struct rb_root threads;
struct thread *last_match;
struct events_stats events_stats;
unsigned long event_total[PERF_RECORD_MAX];
struct rb_root hists;
u64 sample_type;
int fd;
int cwdlen;
char *cwd;
char filename[0];
};
typedef int (*event_op)(event_t *self, struct perf_session *session);
struct perf_event_ops {
event_op process_sample_event;
event_op process_mmap_event;
event_op process_comm_event;
event_op process_fork_event;
event_op process_exit_event;
event_op process_lost_event;
event_op process_read_event;
event_op process_throttle_event;
event_op process_unthrottle_event;
int (*sample_type_check)(struct perf_session *session);
unsigned long total_unknown;
bool full_paths;
};
struct perf_session *perf_session__new(const char *filename, int mode, bool force);
void perf_session__delete(struct perf_session *self);
int perf_session__process_events(struct perf_session *self,
struct perf_event_ops *event_ops);
struct symbol **perf_session__resolve_callchain(struct perf_session *self,
struct thread *thread,
struct ip_callchain *chain,
struct symbol **parent);
int perf_header__read_build_ids(int input, u64 offset, u64 file_size);
#endif /* __PERF_SESSION_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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