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 { ...@@ -211,17 +211,11 @@ struct perf_event_attr {
__u32 wakeup_watermark; /* bytes before wakeup */ __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; __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) ...@@ -782,6 +782,9 @@ static void __perf_install_in_context(void *info)
add_event_to_ctx(event, ctx); 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 * Don't put the event on if it is disabled or if
* it is in a group and the group isn't on. * it is in a group and the group isn't on.
...@@ -925,6 +928,9 @@ static void __perf_event_enable(void *info) ...@@ -925,6 +928,9 @@ static void __perf_event_enable(void *info)
goto unlock; goto unlock;
__perf_event_mark_enabled(event, ctx); __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, * If the event is in a group and isn't the group leader,
* then don't put it on unless the group is on. * 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) ...@@ -1595,15 +1601,12 @@ static struct perf_event_context *find_get_context(pid_t pid, int cpu)
unsigned long flags; unsigned long flags;
int err; int err;
/* if (pid == -1 && cpu != -1) {
* If cpu is not a wildcard then this is a percpu event:
*/
if (cpu != -1) {
/* Must be root to operate on a CPU event: */ /* Must be root to operate on a CPU event: */
if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN))
return ERR_PTR(-EACCES); return ERR_PTR(-EACCES);
if (cpu < 0 || cpu > num_possible_cpus()) if (cpu < 0 || cpu >= nr_cpumask_bits)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
/* /*
...@@ -4564,7 +4567,7 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr, ...@@ -4564,7 +4567,7 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
if (attr->type >= PERF_TYPE_MAX) if (attr->type >= PERF_TYPE_MAX)
return -EINVAL; return -EINVAL;
if (attr->__reserved_1 || attr->__reserved_2 || attr->__reserved_3) if (attr->__reserved_1 || attr->__reserved_2)
return -EINVAL; return -EINVAL;
if (attr->sample_type & ~(PERF_SAMPLE_MAX-1)) 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 ...@@ -49,8 +49,9 @@ PROBE SYNTAX
------------ ------------
Probe points are defined by following 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. '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. 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). '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 ...@@ -39,6 +39,10 @@ OPTIONS
Only consider these symbols. CSV that understands Only consider these symbols. CSV that understands
file://filename entries. file://filename entries.
-s::
--sort=::
Sort by key(s): pid, comm, dso, symbol, parent.
-w:: -w::
--field-width=:: --field-width=::
Force each column width to the provided list, for large terminal 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 ...@@ -8,18 +8,43 @@ perf-trace - Read perf.data (created by perf record) and display trace output
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'perf trace' [-i <file> | --input=file] symbol_name 'perf trace' {record <script> | report <script> [args] }
DESCRIPTION DESCRIPTION
----------- -----------
This command reads the input file and displays the trace recorded. 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 OPTIONS
------- -------
-D:: -D::
--dump-raw-trace=:: --dump-raw-trace=::
Display verbose dump of the trace data. 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:: -s::
--script=:: --script=::
Process trace data with the given script ([lang]:script[.ext]). Process trace data with the given script ([lang]:script[.ext]).
......
...@@ -370,7 +370,6 @@ LIB_H += util/values.h ...@@ -370,7 +370,6 @@ LIB_H += util/values.h
LIB_H += util/sort.h LIB_H += util/sort.h
LIB_H += util/hist.h LIB_H += util/hist.h
LIB_H += util/thread.h LIB_H += util/thread.h
LIB_H += util/data_map.h
LIB_H += util/probe-finder.h LIB_H += util/probe-finder.h
LIB_H += util/probe-event.h LIB_H += util/probe-event.h
...@@ -428,6 +427,7 @@ BUILTIN_OBJS += bench/sched-messaging.o ...@@ -428,6 +427,7 @@ BUILTIN_OBJS += bench/sched-messaging.o
BUILTIN_OBJS += bench/sched-pipe.o BUILTIN_OBJS += bench/sched-pipe.o
BUILTIN_OBJS += bench/mem-memcpy.o BUILTIN_OBJS += bench/mem-memcpy.o
BUILTIN_OBJS += builtin-diff.o
BUILTIN_OBJS += builtin-help.o BUILTIN_OBJS += builtin-help.o
BUILTIN_OBJS += builtin-sched.o BUILTIN_OBJS += builtin-sched.o
BUILTIN_OBJS += builtin-buildid-list.o BUILTIN_OBJS += builtin-buildid-list.o
...@@ -996,8 +996,6 @@ install: all ...@@ -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/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/*.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/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 ifdef BUILT_INS
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
$(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' $(INSTALL) $(BUILT_INS) '$(DESTDIR_SQ)$(perfexec_instdir_SQ)'
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#include "util/sort.h" #include "util/sort.h"
#include "util/hist.h" #include "util/hist.h"
#include "util/session.h" #include "util/session.h"
#include "util/data_map.h"
static char const *input_name = "perf.data"; static char const *input_name = "perf.data";
...@@ -52,11 +51,6 @@ struct sym_priv { ...@@ -52,11 +51,6 @@ struct sym_priv {
struct sym_ext *ext; 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 const char *sym_hist_filter;
static int symbol_filter(struct map *map __used, struct symbol *sym) 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) ...@@ -122,30 +116,32 @@ static void hist_hit(struct hist_entry *he, u64 ip)
h->ip[offset]); 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; 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) if (he == NULL)
return -ENOMEM; return -ENOMEM;
hist_hit(he, al->addr); hist_hit(he, al->addr);
return 0; 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; struct addr_location al;
dump_printf("(IP, %d): %d: %p\n", event->header.misc, dump_printf("(IP, %d): %d: %p\n", event->header.misc,
event->ip.pid, (void *)(long)event->ip.ip); 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", fprintf(stderr, "problem processing %d event, skipping it.\n",
event->header.type); event->header.type);
return -1; 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, " fprintf(stderr, "problem incrementing symbol count, "
"skipping event\n"); "skipping event\n");
return -1; return -1;
...@@ -429,11 +425,11 @@ static void annotate_sym(struct hist_entry *he) ...@@ -429,11 +425,11 @@ static void annotate_sym(struct hist_entry *he)
free_source_line(he, len); free_source_line(he, len);
} }
static void find_annotations(void) static void perf_session__find_annotations(struct perf_session *self)
{ {
struct rb_node *nd; 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 hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
struct sym_priv *priv; struct sym_priv *priv;
...@@ -454,7 +450,7 @@ static void find_annotations(void) ...@@ -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_sample_event = process_sample_event,
.process_mmap_event = event__process_mmap, .process_mmap_event = event__process_mmap,
.process_comm_event = event__process_comm, .process_comm_event = event__process_comm,
...@@ -463,17 +459,14 @@ static struct perf_file_handler file_handler = { ...@@ -463,17 +459,14 @@ static struct perf_file_handler file_handler = {
static int __cmd_annotate(void) static int __cmd_annotate(void)
{ {
struct perf_session *session = perf_session__new(input_name, O_RDONLY, force);
struct thread *idle;
int ret; int ret;
struct perf_session *session;
session = perf_session__new(input_name, O_RDONLY, force);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
idle = register_idle_thread(); ret = perf_session__process_events(session, &event_ops);
register_perf_file_handler(&file_handler);
ret = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
if (ret) if (ret)
goto out_delete; goto out_delete;
...@@ -483,15 +476,14 @@ static int __cmd_annotate(void) ...@@ -483,15 +476,14 @@ static int __cmd_annotate(void)
} }
if (verbose > 3) if (verbose > 3)
threads__fprintf(stdout); perf_session__fprintf(session, stdout);
if (verbose > 2) if (verbose > 2)
dsos__fprintf(stdout); dsos__fprintf(stdout);
collapse__resort(); perf_session__collapse_resort(session);
output__resort(event__total[0]); perf_session__output_resort(session, session->event_total[0]);
perf_session__find_annotations(session);
find_annotations();
out_delete: out_delete:
perf_session__delete(session); perf_session__delete(session);
...@@ -524,29 +516,17 @@ static const struct option options[] = { ...@@ -524,29 +516,17 @@ static const struct option options[] = {
OPT_END() 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); argc = parse_options(argc, argv, options, annotate_usage, 0);
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);
}
}
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() < 0)
{
if (symbol__init(&symbol_conf) < 0)
return -1; return -1;
argc = parse_options(argc, argv, options, annotate_usage, 0); setup_sorting(annotate_usage, options);
setup_sorting();
if (argc) { if (argc) {
/* /*
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#include "builtin.h" #include "builtin.h"
#include "perf.h" #include "perf.h"
#include "util/cache.h" #include "util/cache.h"
#include "util/data_map.h"
#include "util/debug.h" #include "util/debug.h"
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/session.h" #include "util/session.h"
...@@ -55,8 +54,9 @@ static int perf_file_section__process_buildids(struct perf_file_section *self, ...@@ -55,8 +54,9 @@ static int perf_file_section__process_buildids(struct perf_file_section *self,
static int __cmd_buildid_list(void) static int __cmd_buildid_list(void)
{ {
int err = -1; 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) if (session == NULL)
return -1; 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 @@ ...@@ -12,7 +12,6 @@
#include "util/trace-event.h" #include "util/trace-event.h"
#include "util/debug.h" #include "util/debug.h"
#include "util/data_map.h"
#include <linux/rbtree.h> #include <linux/rbtree.h>
...@@ -21,8 +20,6 @@ typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); ...@@ -21,8 +20,6 @@ typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *);
static char const *input_name = "perf.data"; static char const *input_name = "perf.data";
static u64 sample_type;
static int alloc_flag; static int alloc_flag;
static int caller_flag; static int caller_flag;
...@@ -312,7 +309,7 @@ process_raw_event(event_t *raw_event __used, void *data, ...@@ -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 sample_data data;
struct thread *thread; struct thread *thread;
...@@ -322,7 +319,7 @@ static int process_sample_event(event_t *event) ...@@ -322,7 +319,7 @@ static int process_sample_event(event_t *event)
data.cpu = -1; data.cpu = -1;
data.period = 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", dump_printf("(IP, %d): %d/%d: %p period: %Ld\n",
event->header.misc, event->header.misc,
...@@ -330,7 +327,7 @@ static int process_sample_event(event_t *event) ...@@ -330,7 +327,7 @@ static int process_sample_event(event_t *event)
(void *)(long)data.ip, (void *)(long)data.ip,
(long long)data.period); (long long)data.period);
thread = threads__findnew(event->ip.pid); thread = perf_session__findnew(session, event->ip.pid);
if (thread == NULL) { if (thread == NULL) {
pr_debug("problem processing %d event, skipping it.\n", pr_debug("problem processing %d event, skipping it.\n",
event->header.type); event->header.type);
...@@ -345,11 +342,9 @@ static int process_sample_event(event_t *event) ...@@ -345,11 +342,9 @@ static int process_sample_event(event_t *event)
return 0; return 0;
} }
static int sample_type_check(u64 type) static int sample_type_check(struct perf_session *session)
{ {
sample_type = type; if (!(session->sample_type & PERF_SAMPLE_RAW)) {
if (!(sample_type & PERF_SAMPLE_RAW)) {
fprintf(stderr, fprintf(stderr,
"No trace sample to read. Did you call perf record " "No trace sample to read. Did you call perf record "
"without -R?"); "without -R?");
...@@ -359,28 +354,12 @@ static int sample_type_check(u64 type) ...@@ -359,28 +354,12 @@ static int sample_type_check(u64 type)
return 0; return 0;
} }
static struct perf_file_handler file_handler = { static struct perf_event_ops event_ops = {
.process_sample_event = process_sample_event, .process_sample_event = process_sample_event,
.process_comm_event = event__process_comm, .process_comm_event = event__process_comm,
.sample_type_check = sample_type_check, .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) static double fragmentation(unsigned long n_req, unsigned long n_alloc)
{ {
if (n_alloc == 0) if (n_alloc == 0)
...@@ -389,7 +368,8 @@ static double fragmentation(unsigned long n_req, unsigned long n_alloc) ...@@ -389,7 +368,8 @@ static double fragmentation(unsigned long n_req, unsigned long n_alloc)
return 100.0 - (100.0 * n_req / 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; struct rb_node *next;
...@@ -410,7 +390,7 @@ static void __print_result(struct rb_root *root, int n_lines, int is_caller) ...@@ -410,7 +390,7 @@ static void __print_result(struct rb_root *root, int n_lines, int is_caller)
if (is_caller) { if (is_caller) {
addr = data->call_site; addr = data->call_site;
if (!raw_ip) if (!raw_ip)
sym = map_groups__find_function(kmaps, addr, NULL); sym = map_groups__find_function(&session->kmaps, session, addr, NULL);
} else } else
addr = data->ptr; addr = data->ptr;
...@@ -451,12 +431,12 @@ static void print_summary(void) ...@@ -451,12 +431,12 @@ static void print_summary(void)
printf("Cross CPU allocations: %lu/%lu\n", nr_cross_allocs, nr_allocs); 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) if (caller_flag)
__print_result(&root_caller_sorted, caller_lines, 1); __print_result(&root_caller_sorted, session, caller_lines, 1);
if (alloc_flag) if (alloc_flag)
__print_result(&root_alloc_sorted, alloc_lines, 0); __print_result(&root_alloc_sorted, session, alloc_lines, 0);
print_summary(); print_summary();
} }
...@@ -524,12 +504,20 @@ static void sort_result(void) ...@@ -524,12 +504,20 @@ static void sort_result(void)
static int __cmd_kmem(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(); setup_pager();
read_events(); err = perf_session__process_events(session, &event_ops);
if (err != 0)
goto out_delete;
sort_result(); sort_result();
print_result(); print_result(session);
out_delete:
return 0; perf_session__delete(session);
return err;
} }
static const char * const kmem_usage[] = { static const char * const kmem_usage[] = {
...@@ -778,13 +766,13 @@ static int __cmd_record(int argc, const char **argv) ...@@ -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) 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); argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
if (!argc) if (!argc)
usage_with_options(kmem_usage, kmem_options); usage_with_options(kmem_usage, kmem_options);
symbol__init();
if (!strncmp(argv[0], "rec", 3)) { if (!strncmp(argv[0], "rec", 3)) {
return __cmd_record(argc, argv); return __cmd_record(argc, argv);
} else if (!strcmp(argv[0], "stat")) { } else if (!strcmp(argv[0], "stat")) {
......
...@@ -38,34 +38,29 @@ ...@@ -38,34 +38,29 @@
#include "util/strlist.h" #include "util/strlist.h"
#include "util/event.h" #include "util/event.h"
#include "util/debug.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-options.h"
#include "util/parse-events.h" /* For debugfs_path */ #include "util/parse-events.h" /* For debugfs_path */
#include "util/probe-finder.h" #include "util/probe-finder.h"
#include "util/probe-event.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_PATH_LEN 256
#define MAX_PROBES 128 #define MAX_PROBES 128
/* Session management structure */ /* Session management structure */
static struct { static struct {
char *vmlinux; bool need_dwarf;
char *release; bool list_events;
int need_dwarf; bool force_add;
int nr_probe; int nr_probe;
struct probe_point probes[MAX_PROBES]; struct probe_point probes[MAX_PROBES];
struct strlist *dellist; struct strlist *dellist;
struct perf_session *psession;
struct map *kmap;
} session; } session;
static bool listing;
/* Parse an event definition. Note that any error must die. */ /* Parse an event definition. Note that any error must die. */
static void parse_probe_event(const char *str) static void parse_probe_event(const char *str)
...@@ -77,7 +72,7 @@ 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); die("Too many probes (> %d) are specified.", MAX_PROBES);
/* Parse perf-probe event into probe_point */ /* 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); pr_debug("%d arguments\n", pp->nr_args);
} }
...@@ -120,34 +115,26 @@ static int opt_del_probe_event(const struct option *opt __used, ...@@ -120,34 +115,26 @@ static int opt_del_probe_event(const struct option *opt __used,
return 0; 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 #ifndef NO_LIBDWARF
static int open_default_vmlinux(void) static int open_vmlinux(void)
{ {
struct utsname uts; if (map__load(session.kmap, session.psession, NULL) < 0) {
char fname[MAX_PATH_LEN]; pr_debug("Failed to load kernel map.\n");
int fd, ret, i; return -EINVAL;
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;
} }
return fd; pr_debug("Try to open %s\n", session.kmap->dso->long_name);
return open(session.kmap->dso->long_name, O_RDONLY);
} }
#endif #endif
...@@ -163,21 +150,22 @@ static const struct option options[] = { ...@@ -163,21 +150,22 @@ static const struct option options[] = {
OPT_BOOLEAN('v', "verbose", &verbose, OPT_BOOLEAN('v', "verbose", &verbose,
"be more verbose (show parsed arguments, etc)"), "be more verbose (show parsed arguments, etc)"),
#ifndef NO_LIBDWARF #ifndef NO_LIBDWARF
OPT_STRING('k', "vmlinux", &session.vmlinux, "file", OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"vmlinux/module pathname"), "file", "vmlinux pathname"),
#endif #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_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
opt_del_probe_event), opt_del_probe_event),
OPT_CALLBACK('a', "add", NULL, OPT_CALLBACK('a', "add", NULL,
#ifdef NO_LIBDWARF #ifdef NO_LIBDWARF
"FUNC[+OFFS|%return] [ARG ...]", "[EVENT=]FUNC[+OFFS|%return] [ARG ...]",
#else #else
"FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]", "[EVENT=]FUNC[+OFFS|%return|:RLN][@SRC]|SRC:ALN [ARG ...]",
#endif #endif
"probe point definition, where\n" "probe point definition, where\n"
"\t\tGRP:\tGroup name (optional)\n" "\t\tGROUP:\tGroup name (optional)\n"
"\t\tNAME:\tEvent name\n" "\t\tEVENT:\tEvent name\n"
"\t\tFUNC:\tFunction name\n" "\t\tFUNC:\tFunction name\n"
"\t\tOFFS:\tOffset from function entry (in byte)\n" "\t\tOFFS:\tOffset from function entry (in byte)\n"
"\t\t%return:\tPut the probe at function return\n" "\t\t%return:\tPut the probe at function return\n"
...@@ -191,6 +179,8 @@ static const struct option options[] = { ...@@ -191,6 +179,8 @@ static const struct option options[] = {
#endif #endif
"\t\t\tkprobe-tracer argument format.)\n", "\t\t\tkprobe-tracer argument format.)\n",
opt_add_probe_event), opt_add_probe_event),
OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events"
" with existing name"),
OPT_END() OPT_END()
}; };
...@@ -204,13 +194,18 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -204,13 +194,18 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
argc = parse_options(argc, argv, options, probe_usage, argc = parse_options(argc, argv, options, probe_usage,
PARSE_OPT_STOP_AT_NON_OPTION); 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); 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); usage_with_options(probe_usage, options);
if (listing) { if (session.list_events) {
if (session.nr_probe != 0 || session.dellist) { if (session.nr_probe != 0 || session.dellist) {
pr_warning(" Error: Don't use --list with" pr_warning(" Error: Don't use --list with"
" --add/--del.\n"); " --add/--del.\n");
...@@ -227,17 +222,28 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -227,17 +222,28 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
return 0; 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) if (session.need_dwarf)
#ifdef NO_LIBDWARF #ifdef NO_LIBDWARF
die("Debuginfo-analysis is not supported"); die("Debuginfo-analysis is not supported");
#else /* !NO_LIBDWARF */ #else /* !NO_LIBDWARF */
pr_debug("Some probes require debuginfo.\n"); pr_debug("Some probes require debuginfo.\n");
if (session.vmlinux) { fd = open_vmlinux();
pr_debug("Try to open %s.", session.vmlinux);
fd = open(session.vmlinux, O_RDONLY);
} else
fd = open_default_vmlinux();
if (fd < 0) { if (fd < 0) {
if (session.need_dwarf) if (session.need_dwarf)
die("Could not open debuginfo file."); die("Could not open debuginfo file.");
...@@ -255,15 +261,22 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) ...@@ -255,15 +261,22 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
lseek(fd, SEEK_SET, 0); lseek(fd, SEEK_SET, 0);
ret = find_probepoint(fd, pp); ret = find_probepoint(fd, pp);
if (ret < 0) { if (ret > 0)
if (session.need_dwarf) continue;
die("Could not analyze debuginfo."); if (ret == 0) { /* No error but failed to find probe point. */
synthesize_perf_probe_point(pp);
pr_warning("An error occurred in debuginfo analysis. Try to use symbols.\n"); die("Probe point '%s' not found. - probe not added.",
break; 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. */ pr_debug("An error occurred in debuginfo analysis."
die("No probe point found."); " Try to use symbols.\n");
break;
} }
close(fd); close(fd);
...@@ -276,6 +289,7 @@ end_dwarf: ...@@ -276,6 +289,7 @@ end_dwarf:
if (pp->found) /* This probe is already found. */ if (pp->found) /* This probe is already found. */
continue; continue;
evaluate_probe_point(pp);
ret = synthesize_trace_kprobe_event(pp); ret = synthesize_trace_kprobe_event(pp);
if (ret == -E2BIG) if (ret == -E2BIG)
die("probe point definition becomes too long."); die("probe point definition becomes too long.");
...@@ -284,7 +298,8 @@ end_dwarf: ...@@ -284,7 +298,8 @@ end_dwarf:
} }
/* Settng up probe points */ /* 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; return 0;
} }
...@@ -123,7 +123,8 @@ static void write_event(event_t *buf, size_t size) ...@@ -123,7 +123,8 @@ static void write_event(event_t *buf, size_t size)
write_output(buf, 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); write_event(event, event->header.size);
return 0; return 0;
...@@ -277,7 +278,7 @@ static void create_counter(int counter, int cpu, pid_t pid) ...@@ -277,7 +278,7 @@ static void create_counter(int counter, int cpu, pid_t pid)
attr->mmap = track; attr->mmap = track;
attr->comm = track; attr->comm = track;
attr->inherit = (cpu < 0) && inherit; attr->inherit = inherit;
attr->disabled = 1; attr->disabled = 1;
try_again: try_again:
...@@ -401,7 +402,7 @@ static void atexit_header(void) ...@@ -401,7 +402,7 @@ static void atexit_header(void)
perf_header__write(&session->header, output, true); 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; int i, counter;
struct stat st; struct stat st;
...@@ -409,6 +410,8 @@ static int __cmd_record(int argc, const char **argv) ...@@ -409,6 +410,8 @@ static int __cmd_record(int argc, const char **argv)
int flags; int flags;
int err; int err;
unsigned long waking = 0; unsigned long waking = 0;
int child_ready_pipe[2], go_pipe[2];
char buf;
page_size = sysconf(_SC_PAGE_SIZE); page_size = sysconf(_SC_PAGE_SIZE);
nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
...@@ -419,11 +422,25 @@ static int __cmd_record(int argc, const char **argv) ...@@ -419,11 +422,25 @@ static int __cmd_record(int argc, const char **argv)
signal(SIGCHLD, sig_handler); signal(SIGCHLD, sig_handler);
signal(SIGINT, 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 (!stat(output_name, &st) && st.st_size) {
if (!force && !append_file) { if (!force) {
fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", if (!append_file) {
output_name); pr_err("Error, output file %s exists, use -A "
exit(-1); "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 { } else {
append_file = 0; append_file = 0;
...@@ -466,19 +483,65 @@ static int __cmd_record(int argc, const char **argv) ...@@ -466,19 +483,65 @@ static int __cmd_record(int argc, const char **argv)
atexit(atexit_header); atexit(atexit_header);
if (!system_wide) { if (target_pid == -1) {
pid = target_pid; pid = fork();
if (pid == -1) if (pid < 0) {
pid = getpid(); perror("failed to fork");
exit(-1);
}
open_counters(profile_cpu, pid); if (!pid) {
} else { close(child_ready_pipe[0]);
if (profile_cpu != -1) { close(go_pipe[1]);
open_counters(profile_cpu, target_pid); fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
} else {
for (i = 0; i < nr_cpus; i++) /*
open_counters(i, target_pid); * 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) { if (file_new) {
...@@ -488,33 +551,10 @@ static int __cmd_record(int argc, const char **argv) ...@@ -488,33 +551,10 @@ static int __cmd_record(int argc, const char **argv)
} }
if (!system_wide) if (!system_wide)
event__synthesize_thread(pid, process_synthesized_event); event__synthesize_thread(pid, process_synthesized_event,
session);
else else
event__synthesize_threads(process_synthesized_event); event__synthesize_threads(process_synthesized_event, session);
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;
}
if (realtime_prio) { if (realtime_prio) {
struct sched_param param; struct sched_param param;
...@@ -526,6 +566,11 @@ static int __cmd_record(int argc, const char **argv) ...@@ -526,6 +566,11 @@ static int __cmd_record(int argc, const char **argv)
} }
} }
/*
* Let the child rip
*/
close(go_pipe[1]);
for (;;) { for (;;) {
int hits = samples; int hits = samples;
...@@ -620,13 +665,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) ...@@ -620,13 +665,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
{ {
int counter; int counter;
symbol__init(0);
argc = parse_options(argc, argv, options, record_usage, argc = parse_options(argc, argv, options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc && target_pid == -1 && !system_wide) if (!argc && target_pid == -1 && (!system_wide || profile_cpu == -1))
usage_with_options(record_usage, options); usage_with_options(record_usage, options);
symbol__init();
if (!nr_counters) { if (!nr_counters) {
nr_counters = 1; nr_counters = 1;
attrs[0].type = PERF_TYPE_HARDWARE; attrs[0].type = PERF_TYPE_HARDWARE;
......
This diff is collapsed.
This diff is collapsed.
...@@ -30,15 +30,12 @@ ...@@ -30,15 +30,12 @@
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/parse-events.h" #include "util/parse-events.h"
#include "util/event.h" #include "util/event.h"
#include "util/data_map.h" #include "util/session.h"
#include "util/svghelper.h" #include "util/svghelper.h"
static char const *input_name = "perf.data"; static char const *input_name = "perf.data";
static char const *output_name = "output.svg"; static char const *output_name = "output.svg";
static u64 sample_type;
static unsigned int numcpus; static unsigned int numcpus;
static u64 min_freq; /* Lowest CPU frequency seen */ static u64 min_freq; /* Lowest CPU frequency seen */
static u64 max_freq; /* Highest CPU frequency seen */ static u64 max_freq; /* Highest CPU frequency seen */
...@@ -281,21 +278,19 @@ static int cpus_cstate_state[MAX_CPUS]; ...@@ -281,21 +278,19 @@ static int cpus_cstate_state[MAX_CPUS];
static u64 cpus_pstate_start_times[MAX_CPUS]; static u64 cpus_pstate_start_times[MAX_CPUS];
static u64 cpus_pstate_state[MAX_CPUS]; static u64 cpus_pstate_state[MAX_CPUS];
static int static int process_comm_event(event_t *event, struct perf_session *session __used)
process_comm_event(event_t *event)
{ {
pid_set_comm(event->comm.pid, event->comm.comm); pid_set_comm(event->comm.pid, event->comm.comm);
return 0; 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); pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
return 0; return 0;
} }
static int static int process_exit_event(event_t *event, struct perf_session *session __used)
process_exit_event(event_t *event)
{ {
pid_exit(event->fork.pid, event->fork.time); pid_exit(event->fork.pid, event->fork.time);
return 0; return 0;
...@@ -480,17 +475,16 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) ...@@ -480,17 +475,16 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
} }
static int static int process_sample_event(event_t *event, struct perf_session *session)
process_sample_event(event_t *event)
{ {
struct sample_data data; struct sample_data data;
struct trace_entry *te; struct trace_entry *te;
memset(&data, 0, sizeof(data)); 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) if (!first_time || first_time > data.time)
first_time = data.time; first_time = data.time;
if (last_time < data.time) if (last_time < data.time)
...@@ -498,7 +492,7 @@ process_sample_event(event_t *event) ...@@ -498,7 +492,7 @@ process_sample_event(event_t *event)
} }
te = (void *)data.raw_data; 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; char *event_str;
struct power_entry *pe; struct power_entry *pe;
...@@ -575,16 +569,16 @@ static void end_sample_processing(void) ...@@ -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; int cursor;
cursor = 0; cursor = 0;
if (sample_type & PERF_SAMPLE_IP) if (session->sample_type & PERF_SAMPLE_IP)
cursor++; cursor++;
if (sample_type & PERF_SAMPLE_TID) if (session->sample_type & PERF_SAMPLE_TID)
cursor++; cursor++;
if (sample_type & PERF_SAMPLE_TIME) if (session->sample_type & PERF_SAMPLE_TIME)
return event->sample.array[cursor]; return event->sample.array[cursor];
return 0; return 0;
} }
...@@ -594,8 +588,7 @@ static u64 sample_time(event_t *event) ...@@ -594,8 +588,7 @@ static u64 sample_time(event_t *event)
* We first queue all events, sorted backwards by insertion. * We first queue all events, sorted backwards by insertion.
* The order will get flipped later. * The order will get flipped later.
*/ */
static int static int queue_sample_event(event_t *event, struct perf_session *session)
queue_sample_event(event_t *event)
{ {
struct sample_wrapper *copy, *prev; struct sample_wrapper *copy, *prev;
int size; int size;
...@@ -609,7 +602,7 @@ queue_sample_event(event_t *event) ...@@ -609,7 +602,7 @@ queue_sample_event(event_t *event)
memset(copy, 0, size); memset(copy, 0, size);
copy->next = NULL; copy->next = NULL;
copy->timestamp = sample_time(event); copy->timestamp = sample_time(event, session);
memcpy(&copy->data, event, event->sample.header.size); memcpy(&copy->data, event, event->sample.header.size);
...@@ -1021,7 +1014,7 @@ static void write_svg_file(const char *filename) ...@@ -1021,7 +1014,7 @@ static void write_svg_file(const char *filename)
svg_close(); svg_close();
} }
static void process_samples(void) static void process_samples(struct perf_session *session)
{ {
struct sample_wrapper *cursor; struct sample_wrapper *cursor;
event_t *event; event_t *event;
...@@ -1032,15 +1025,13 @@ static void process_samples(void) ...@@ -1032,15 +1025,13 @@ static void process_samples(void)
while (cursor) { while (cursor) {
event = (void *)&cursor->data; event = (void *)&cursor->data;
cursor = cursor->next; 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 (!(session->sample_type & PERF_SAMPLE_RAW)) {
if (!(sample_type & PERF_SAMPLE_RAW)) {
fprintf(stderr, "No trace samples found in the file.\n" fprintf(stderr, "No trace samples found in the file.\n"
"Have you used 'perf timechart record' to record it?\n"); "Have you used 'perf timechart record' to record it?\n");
return -1; return -1;
...@@ -1049,7 +1040,7 @@ static int sample_type_check(u64 type) ...@@ -1049,7 +1040,7 @@ static int sample_type_check(u64 type)
return 0; return 0;
} }
static struct perf_file_handler file_handler = { static struct perf_event_ops event_ops = {
.process_comm_event = process_comm_event, .process_comm_event = process_comm_event,
.process_fork_event = process_fork_event, .process_fork_event = process_fork_event,
.process_exit_event = process_exit_event, .process_exit_event = process_exit_event,
...@@ -1065,13 +1056,11 @@ static int __cmd_timechart(void) ...@@ -1065,13 +1056,11 @@ static int __cmd_timechart(void)
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
register_perf_file_handler(&file_handler); ret = perf_session__process_events(session, &event_ops);
ret = perf_session__process_events(session, 0, &event__cwdlen, &event__cwd);
if (ret) if (ret)
goto out_delete; goto out_delete;
process_samples(); process_samples(session);
end_sample_processing(); end_sample_processing();
...@@ -1148,11 +1137,11 @@ static const struct option options[] = { ...@@ -1148,11 +1137,11 @@ static const struct option options[] = {
int cmd_timechart(int argc, const char **argv, const char *prefix __used) int cmd_timechart(int argc, const char **argv, const char *prefix __used)
{ {
symbol__init(0);
argc = parse_options(argc, argv, options, timechart_usage, argc = parse_options(argc, argv, options, timechart_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
symbol__init();
if (argc && !strncmp(argv[0], "rec", 3)) if (argc && !strncmp(argv[0], "rec", 3))
return __cmd_record(argc, argv); return __cmd_record(argc, argv);
else if (argc) else if (argc)
......
...@@ -20,8 +20,9 @@ ...@@ -20,8 +20,9 @@
#include "perf.h" #include "perf.h"
#include "util/symbol.h"
#include "util/color.h" #include "util/color.h"
#include "util/session.h"
#include "util/symbol.h"
#include "util/thread.h" #include "util/thread.h"
#include "util/util.h" #include "util/util.h"
#include <linux/rbtree.h> #include <linux/rbtree.h>
...@@ -79,7 +80,6 @@ static int dump_symtab = 0; ...@@ -79,7 +80,6 @@ static int dump_symtab = 0;
static bool hide_kernel_symbols = false; static bool hide_kernel_symbols = false;
static bool hide_user_symbols = false; static bool hide_user_symbols = false;
static struct winsize winsize; static struct winsize winsize;
struct symbol_conf symbol_conf;
/* /*
* Source * Source
...@@ -926,7 +926,8 @@ static int symbol_filter(struct map *map, struct symbol *sym) ...@@ -926,7 +926,8 @@ static int symbol_filter(struct map *map, struct symbol *sym)
return 0; 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; u64 ip = self->ip.ip;
struct sym_entry *syme; struct sym_entry *syme;
...@@ -946,8 +947,8 @@ static void event__process_sample(const event_t *self, int counter) ...@@ -946,8 +947,8 @@ static void event__process_sample(const event_t *self, int counter)
return; return;
} }
if (event__preprocess_sample(self, &al, symbol_filter) < 0 || if (event__preprocess_sample(self, session, &al, symbol_filter) < 0 ||
al.sym == NULL) al.sym == NULL || al.filtered)
return; return;
syme = symbol__priv(al.sym); syme = symbol__priv(al.sym);
...@@ -965,14 +966,14 @@ static void event__process_sample(const event_t *self, int counter) ...@@ -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) { switch (event->header.type) {
case PERF_RECORD_COMM: case PERF_RECORD_COMM:
event__process_comm(event); event__process_comm(event, session);
break; break;
case PERF_RECORD_MMAP: case PERF_RECORD_MMAP:
event__process_mmap(event); event__process_mmap(event, session);
break; break;
default: default:
break; break;
...@@ -999,7 +1000,8 @@ static unsigned int mmap_read_head(struct mmap_data *md) ...@@ -999,7 +1000,8 @@ static unsigned int mmap_read_head(struct mmap_data *md)
return head; 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 head = mmap_read_head(md);
unsigned int old = md->prev; unsigned int old = md->prev;
...@@ -1052,9 +1054,9 @@ static void mmap_read_counter(struct mmap_data *md) ...@@ -1052,9 +1054,9 @@ static void mmap_read_counter(struct mmap_data *md)
} }
if (event->header.type == PERF_RECORD_SAMPLE) if (event->header.type == PERF_RECORD_SAMPLE)
event__process_sample(event, md->counter); event__process_sample(event, self, md->counter);
else else
event__process(event); event__process(event, self);
old += size; old += size;
} }
...@@ -1064,13 +1066,13 @@ static void mmap_read_counter(struct mmap_data *md) ...@@ -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 pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];
static struct mmap_data mmap_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; int i, counter;
for (i = 0; i < nr_cpus; i++) { for (i = 0; i < nr_cpus; i++) {
for (counter = 0; counter < nr_counters; counter++) 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) ...@@ -1155,11 +1157,18 @@ static int __cmd_top(void)
pthread_t thread; pthread_t thread;
int i, counter; int i, counter;
int ret; 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) if (target_pid != -1)
event__synthesize_thread(target_pid, event__process); event__synthesize_thread(target_pid, event__process, session);
else else
event__synthesize_threads(event__process); event__synthesize_threads(event__process, session);
for (i = 0; i < nr_cpus; i++) { for (i = 0; i < nr_cpus; i++) {
group_fd = -1; group_fd = -1;
...@@ -1170,7 +1179,7 @@ static int __cmd_top(void) ...@@ -1170,7 +1179,7 @@ static int __cmd_top(void)
/* Wait for a minimal set of events before starting the snapshot */ /* Wait for a minimal set of events before starting the snapshot */
poll(event_array, nr_poll, 100); poll(event_array, nr_poll, 100);
mmap_read(); perf_session__mmap_read(session);
if (pthread_create(&thread, NULL, display_thread, NULL)) { if (pthread_create(&thread, NULL, display_thread, NULL)) {
printf("Could not create display thread.\n"); printf("Could not create display thread.\n");
...@@ -1190,7 +1199,7 @@ static int __cmd_top(void) ...@@ -1190,7 +1199,7 @@ static int __cmd_top(void)
while (1) { while (1) {
int hits = samples; int hits = samples;
mmap_read(); perf_session__mmap_read(session);
if (hits == samples) if (hits == samples)
ret = poll(event_array, nr_poll, 100); ret = poll(event_array, nr_poll, 100);
...@@ -1273,7 +1282,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) ...@@ -1273,7 +1282,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
(nr_counters + 1) * sizeof(unsigned long)); (nr_counters + 1) * sizeof(unsigned long));
if (symbol_conf.vmlinux_name == NULL) if (symbol_conf.vmlinux_name == NULL)
symbol_conf.try_vmlinux_path = true; symbol_conf.try_vmlinux_path = true;
if (symbol__init(&symbol_conf) < 0) if (symbol__init() < 0)
return -1; return -1;
if (delay_secs < 1) if (delay_secs < 1)
......
This diff is collapsed.
...@@ -17,6 +17,7 @@ extern int check_pager_config(const char *cmd); ...@@ -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_annotate(int argc, const char **argv, const char *prefix);
extern int cmd_bench(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_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_help(int argc, const char **argv, const char *prefix);
extern int cmd_sched(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); extern int cmd_list(int argc, const char **argv, const char *prefix);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
perf-annotate mainporcelain common perf-annotate mainporcelain common
perf-bench mainporcelain common perf-bench mainporcelain common
perf-buildid-list mainporcelain common perf-buildid-list mainporcelain common
perf-diff mainporcelain common
perf-list mainporcelain common perf-list mainporcelain common
perf-sched mainporcelain common perf-sched mainporcelain common
perf-record mainporcelain common perf-record mainporcelain common
......
...@@ -286,6 +286,7 @@ static void handle_internal_command(int argc, const char **argv) ...@@ -286,6 +286,7 @@ static void handle_internal_command(int argc, const char **argv)
const char *cmd = argv[0]; const char *cmd = argv[0];
static struct cmd_struct commands[] = { static struct cmd_struct commands[] = {
{ "buildid-list", cmd_buildid_list, 0 }, { "buildid-list", cmd_buildid_list, 0 },
{ "diff", cmd_diff, 0 },
{ "help", cmd_help, 0 }, { "help", cmd_help, 0 },
{ "list", cmd_list, 0 }, { "list", cmd_list, 0 },
{ "record", cmd_record, 0 }, { "record", cmd_record, 0 },
......
#!/bin/bash #!/bin/bash
# description: useless but exhaustive test script
perf trace -s ~/libexec/perf-core/scripts/perl/check-perf-trace.pl perf trace -s ~/libexec/perf-core/scripts/perl/check-perf-trace.pl
......
#!/bin/bash #!/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 #!/bin/bash
# description: system-wide r/w activity
perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl perf trace -s ~/libexec/perf-core/scripts/perl/rw-by-pid.pl
......
#!/bin/bash #!/bin/bash
# description: system-wide min/max/avg wakeup latency
perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl perf trace -s ~/libexec/perf-core/scripts/perl/wakeup-latency.pl
......
#!/bin/bash #!/bin/bash
# description: workqueue stats (ins/exe/create/destroy)
perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl perf trace -s ~/libexec/perf-core/scripts/perl/workqueue-stats.pl
......
...@@ -18,8 +18,9 @@ use lib "./Perf-Trace-Util/lib"; ...@@ -18,8 +18,9 @@ use lib "./Perf-Trace-Util/lib";
use Perf::Trace::Core; use Perf::Trace::Core;
use Perf::Trace::Util; use Perf::Trace::Util;
# change this to the comm of the program you're interested in my $usage = "perf trace -s rw-by-file.pl <comm>\n";
my $for_comm = "perf";
my $for_comm = shift or die $usage;
my %reads; my %reads;
my %writes; my %writes;
......
#include "data_map.h"
#include "symbol.h" #include "symbol.h"
#include "util.h" #include "util.h"
#include "debug.h" #include "debug.h"
#include "thread.h"
#include "session.h"
static int process_event_stub(event_t *event __used,
static struct perf_file_handler *curr_handler; struct perf_session *session __used)
static unsigned long mmap_window = 32;
static char __cwd[PATH_MAX];
static int process_event_stub(event_t *event __used)
{ {
dump_printf(": unhandled!\n"); dump_printf(": unhandled!\n");
return 0; 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) if (!handler->process_sample_event)
handler->process_sample_event = process_event_stub; handler->process_sample_event = process_event_stub;
...@@ -34,8 +31,6 @@ void register_perf_file_handler(struct perf_file_handler *handler) ...@@ -34,8 +31,6 @@ void register_perf_file_handler(struct perf_file_handler *handler)
handler->process_throttle_event = process_event_stub; handler->process_throttle_event = process_event_stub;
if (!handler->process_unthrottle_event) if (!handler->process_unthrottle_event)
handler->process_unthrottle_event = process_event_stub; handler->process_unthrottle_event = process_event_stub;
curr_handler = handler;
} }
static const char *event__name[] = { static const char *event__name[] = {
...@@ -61,8 +56,9 @@ void event__print_totals(void) ...@@ -61,8 +56,9 @@ void event__print_totals(void)
event__name[i], event__total[i]); event__name[i], event__total[i]);
} }
static int static int process_event(event_t *event, struct perf_session *session,
process_event(event_t *event, unsigned long offset, unsigned long head) struct perf_event_ops *ops,
unsigned long offset, unsigned long head)
{ {
trace_event(event); trace_event(event);
...@@ -77,25 +73,25 @@ process_event(event_t *event, unsigned long offset, unsigned long head) ...@@ -77,25 +73,25 @@ process_event(event_t *event, unsigned long offset, unsigned long head)
switch (event->header.type) { switch (event->header.type) {
case PERF_RECORD_SAMPLE: case PERF_RECORD_SAMPLE:
return curr_handler->process_sample_event(event); return ops->process_sample_event(event, session);
case PERF_RECORD_MMAP: case PERF_RECORD_MMAP:
return curr_handler->process_mmap_event(event); return ops->process_mmap_event(event, session);
case PERF_RECORD_COMM: case PERF_RECORD_COMM:
return curr_handler->process_comm_event(event); return ops->process_comm_event(event, session);
case PERF_RECORD_FORK: case PERF_RECORD_FORK:
return curr_handler->process_fork_event(event); return ops->process_fork_event(event, session);
case PERF_RECORD_EXIT: case PERF_RECORD_EXIT:
return curr_handler->process_exit_event(event); return ops->process_exit_event(event, session);
case PERF_RECORD_LOST: case PERF_RECORD_LOST:
return curr_handler->process_lost_event(event); return ops->process_lost_event(event, session);
case PERF_RECORD_READ: case PERF_RECORD_READ:
return curr_handler->process_read_event(event); return ops->process_read_event(event, session);
case PERF_RECORD_THROTTLE: case PERF_RECORD_THROTTLE:
return curr_handler->process_throttle_event(event); return ops->process_throttle_event(event, session);
case PERF_RECORD_UNTHROTTLE: case PERF_RECORD_UNTHROTTLE:
return curr_handler->process_unthrottle_event(event); return ops->process_unthrottle_event(event, session);
default: default:
curr_handler->total_unknown++; ops->total_unknown++;
return -1; return -1;
} }
} }
...@@ -129,44 +125,58 @@ out: ...@@ -129,44 +125,58 @@ out:
return err; 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 perf_session__process_events(struct perf_session *self,
int full_paths, int *cwdlen, char **cwd) struct perf_event_ops *ops)
{ {
int err; int err;
unsigned long head, shift; unsigned long head, shift;
unsigned long offset = 0; unsigned long offset = 0;
size_t page_size; size_t page_size;
u64 sample_type;
event_t *event; event_t *event;
uint32_t size; uint32_t size;
char *buf; char *buf;
if (curr_handler == NULL) { if (perf_session__register_idle_thread(self) == NULL)
pr_debug("Forgot to register perf file handler\n"); return -ENOMEM;
return -EINVAL;
} perf_event_ops__fill_defaults(ops);
page_size = getpagesize(); page_size = getpagesize();
head = self->header.data_offset; head = self->header.data_offset;
sample_type = perf_header__sample_type(&self->header); self->sample_type = perf_header__sample_type(&self->header);
err = -EINVAL; err = -EINVAL;
if (curr_handler->sample_type_check && if (ops->sample_type_check && ops->sample_type_check(self) < 0)
curr_handler->sample_type_check(sample_type) < 0)
goto out_err; goto out_err;
if (!full_paths) { if (!ops->full_paths) {
if (getcwd(__cwd, sizeof(__cwd)) == NULL) { char bf[PATH_MAX];
pr_err("failed to get the current directory\n");
if (getcwd(bf, sizeof(bf)) == NULL) {
err = -errno; err = -errno;
out_getcwd_err:
pr_err("failed to get the current directory\n");
goto out_err; goto out_err;
} }
*cwd = __cwd; self->cwd = strdup(bf);
*cwdlen = strlen(*cwd); if (self->cwd == NULL) {
} else { err = -ENOMEM;
*cwd = NULL; goto out_getcwd_err;
*cwdlen = 0; }
self->cwdlen = strlen(self->cwd);
} }
shift = page_size * (head / page_size); shift = page_size * (head / page_size);
...@@ -174,7 +184,7 @@ int perf_session__process_events(struct perf_session *self, ...@@ -174,7 +184,7 @@ int perf_session__process_events(struct perf_session *self,
head -= shift; head -= shift;
remap: 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); MAP_SHARED, self->fd, offset);
if (buf == MAP_FAILED) { if (buf == MAP_FAILED) {
pr_err("failed to mmap file\n"); pr_err("failed to mmap file\n");
...@@ -189,12 +199,12 @@ more: ...@@ -189,12 +199,12 @@ more:
if (!size) if (!size)
size = 8; size = 8;
if (head + event->header.size >= page_size * mmap_window) { if (head + event->header.size >= page_size * self->mmap_window) {
int munmap_ret; int munmap_ret;
shift = page_size * (head / page_size); 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); assert(munmap_ret == 0);
offset += shift; offset += shift;
...@@ -209,7 +219,7 @@ more: ...@@ -209,7 +219,7 @@ more:
(void *)(long)event->header.size, (void *)(long)event->header.size,
event->header.type); 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", dump_printf("%p [%p]: skipping unknown header type: %d\n",
(void *)(offset + head), (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 <linux/types.h>
#include "event.h" #include "event.h"
#include "debug.h" #include "debug.h"
#include "session.h"
#include "sort.h"
#include "string.h" #include "string.h"
#include "strlist.h"
#include "thread.h" #include "thread.h"
static pid_t event__synthesize_comm(pid_t pid, int full, 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; event_t ev;
char filename[PATH_MAX]; char filename[PATH_MAX];
...@@ -54,7 +59,7 @@ out_race: ...@@ -54,7 +59,7 @@ out_race:
if (!full) { if (!full) {
ev.comm.tid = pid; ev.comm.tid = pid;
process(&ev); process(&ev, session);
goto out_fclose; goto out_fclose;
} }
...@@ -72,7 +77,7 @@ out_race: ...@@ -72,7 +77,7 @@ out_race:
ev.comm.tid = pid; ev.comm.tid = pid;
process(&ev); process(&ev, session);
} }
closedir(tasks); closedir(tasks);
...@@ -86,7 +91,9 @@ out_failure: ...@@ -86,7 +91,9 @@ out_failure:
} }
static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, 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]; char filename[PATH_MAX];
FILE *fp; FILE *fp;
...@@ -141,7 +148,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid, ...@@ -141,7 +148,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
ev.mmap.pid = tgid; ev.mmap.pid = tgid;
ev.mmap.tid = pid; 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, ...@@ -149,15 +156,20 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
return 0; 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) if (tgid == -1)
return -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; DIR *proc;
struct dirent dirent, *next; struct dirent dirent, *next;
...@@ -171,24 +183,47 @@ void event__synthesize_threads(int (*process)(event_t *event)) ...@@ -171,24 +183,47 @@ void event__synthesize_threads(int (*process)(event_t *event))
if (*end) /* only interested in proper numerical dirents */ if (*end) /* only interested in proper numerical dirents */
continue; continue;
event__synthesize_thread(pid, process); event__synthesize_thread(pid, process, session);
} }
closedir(proc); closedir(proc);
} }
char *event__cwd; static void thread__comm_adjust(struct thread *self)
int event__cwdlen; {
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); 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"); dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
return -1; return -1;
} }
...@@ -196,18 +231,18 @@ int event__process_comm(event_t *self) ...@@ -196,18 +231,18 @@ int event__process_comm(event_t *self)
return 0; 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); 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; 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, 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", dump_printf(" %d/%d: [%p(%p) @ %p]: %s\n",
self->mmap.pid, self->mmap.tid, self->mmap.pid, self->mmap.tid,
...@@ -224,10 +259,10 @@ int event__process_mmap(event_t *self) ...@@ -224,10 +259,10 @@ int event__process_mmap(event_t *self)
return 0; 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 *thread = perf_session__findnew(session, self->fork.pid);
struct thread *parent = threads__findnew(self->fork.ppid); struct thread *parent = perf_session__findnew(session, self->fork.ppid);
dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
self->fork.ppid, self->fork.ptid); self->fork.ppid, self->fork.ptid);
...@@ -249,7 +284,8 @@ int event__process_task(event_t *self) ...@@ -249,7 +284,8 @@ int event__process_task(event_t *self)
return 0; 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, enum map_type type, u64 addr,
struct addr_location *al, struct addr_location *al,
symbol_filter_t filter) symbol_filter_t filter)
...@@ -261,7 +297,7 @@ void thread__find_addr_location(struct thread *self, u8 cpumode, ...@@ -261,7 +297,7 @@ void thread__find_addr_location(struct thread *self, u8 cpumode,
if (cpumode & PERF_RECORD_MISC_KERNEL) { if (cpumode & PERF_RECORD_MISC_KERNEL) {
al->level = 'k'; al->level = 'k';
mg = kmaps; mg = &session->kmaps;
} else if (cpumode & PERF_RECORD_MISC_USER) } else if (cpumode & PERF_RECORD_MISC_USER)
al->level = '.'; al->level = '.';
else { else {
...@@ -282,33 +318,73 @@ try_again: ...@@ -282,33 +318,73 @@ try_again:
* "[vdso]" dso, but for now lets use the old trick of looking * "[vdso]" dso, but for now lets use the old trick of looking
* in the whole kernel symbol list. * in the whole kernel symbol list.
*/ */
if ((long long)al->addr < 0 && mg != kmaps) { if ((long long)al->addr < 0 && mg != &session->kmaps) {
mg = kmaps; mg = &session->kmaps;
goto try_again; goto try_again;
} }
al->sym = NULL; al->sym = NULL;
} else { } else {
al->addr = al->map->map_ip(al->map, al->addr); 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, static void dso__calc_col_width(struct dso *self)
symbol_filter_t filter) {
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; 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) if (thread == NULL)
return -1; 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); 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); self->ip.ip, al, filter);
dump_printf(" ...... dso: %s\n", dump_printf(" ...... dso: %s\n",
al->map ? al->map->dso->long_name : al->map ? al->map->dso->long_name :
al->level == 'H' ? "[hypervisor]" : "<not found>"); 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; return 0;
} }
......
...@@ -149,29 +149,35 @@ void map__delete(struct map *self); ...@@ -149,29 +149,35 @@ void map__delete(struct map *self);
struct map *map__clone(struct map *self); struct map *map__clone(struct map *self);
int map__overlap(struct map *l, struct map *r); int map__overlap(struct map *l, struct map *r);
size_t map__fprintf(struct map *self, FILE *fp); 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 symbol *map__find_symbol_by_name(struct map *self, const char *name,
struct perf_session *session,
symbol_filter_t filter); symbol_filter_t filter);
void map__fixup_start(struct map *self); void map__fixup_start(struct map *self);
void map__fixup_end(struct map *self); void map__fixup_end(struct map *self);
int event__synthesize_thread(pid_t pid, int (*process)(event_t *event)); int event__synthesize_thread(pid_t pid,
void event__synthesize_threads(int (*process)(event_t *event)); int (*process)(event_t *event,
struct perf_session *session),
extern char *event__cwd; struct perf_session *session);
extern int event__cwdlen; void event__synthesize_threads(int (*process)(event_t *event,
extern struct events_stats event__stats; struct perf_session *session),
extern unsigned long event__total[PERF_RECORD_MAX]; struct perf_session *session);
int event__process_comm(event_t *self); int event__process_comm(event_t *self, struct perf_session *session);
int event__process_lost(event_t *self); int event__process_lost(event_t *self, struct perf_session *session);
int event__process_mmap(event_t *self); int event__process_mmap(event_t *self, struct perf_session *session);
int event__process_task(event_t *self); int event__process_task(event_t *self, struct perf_session *session);
struct addr_location; struct addr_location;
int event__preprocess_sample(const event_t *self, struct addr_location *al, int event__preprocess_sample(const event_t *self, struct perf_session *session,
symbol_filter_t filter); struct addr_location *al, symbol_filter_t filter);
int event__parse_sample(event_t *event, u64 type, struct sample_data *data); int event__parse_sample(event_t *event, u64 type, struct sample_data *data);
#endif /* __PERF_RECORD_H */ #endif /* __PERF_RECORD_H */
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
#include "header.h" #include "header.h"
#include "../perf.h" #include "../perf.h"
#include "trace-event.h" #include "trace-event.h"
#include "session.h"
#include "symbol.h" #include "symbol.h"
#include "data_map.h"
#include "debug.h" #include "debug.h"
/* /*
......
This diff is collapsed.
#ifndef __PERF_HIST_H #ifndef __PERF_HIST_H
#define __PERF_HIST_H #define __PERF_HIST_H
#include "../builtin.h"
#include "util.h" #include <linux/types.h>
#include "color.h"
#include <linux/list.h>
#include "cache.h"
#include <linux/rbtree.h>
#include "symbol.h"
#include "string.h"
#include "callchain.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 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 perf_session;
struct symbol *parent, struct hist_entry;
u64 count, bool *hit); 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__cmp(struct hist_entry *, struct hist_entry *);
extern int64_t hist_entry__collapse(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 *); 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 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 */ #endif /* __PERF_HIST_H */
...@@ -104,11 +104,16 @@ void map__fixup_end(struct map *self) ...@@ -104,11 +104,16 @@ void map__fixup_end(struct map *self)
#define DSO__DELETED "(deleted)" #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; 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 (nr < 0) {
if (self->dso->has_build_id) { if (self->dso->has_build_id) {
char sbuild_id[BUILD_ID_SIZE * 2 + 1]; char sbuild_id[BUILD_ID_SIZE * 2 + 1];
...@@ -143,19 +148,20 @@ static int map__load(struct map *self, symbol_filter_t filter) ...@@ -143,19 +148,20 @@ static int map__load(struct map *self, symbol_filter_t filter)
return 0; return 0;
} }
struct symbol *map__find_symbol(struct map *self, u64 addr, struct symbol *map__find_symbol(struct map *self, struct perf_session *session,
symbol_filter_t filter) 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 NULL;
return dso__find_symbol(self->dso, self->type, addr); return dso__find_symbol(self->dso, self->type, addr);
} }
struct symbol *map__find_symbol_by_name(struct map *self, const char *name, struct symbol *map__find_symbol_by_name(struct map *self, const char *name,
struct perf_session *session,
symbol_filter_t filter) 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 NULL;
if (!dso__sorted_by_name(self->dso, self->type)) if (!dso__sorted_by_name(self->dso, self->type))
......
This diff is collapsed.
#ifndef _PROBE_EVENT_H #ifndef _PROBE_EVENT_H
#define _PROBE_EVENT_H #define _PROBE_EVENT_H
#include <stdbool.h>
#include "probe-finder.h" #include "probe-finder.h"
#include "strlist.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 int synthesize_perf_probe_event(struct probe_point *pp);
extern void parse_trace_kprobe_event(const char *str, char **group, extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp);
char **event, struct probe_point *pp);
extern int synthesize_trace_kprobe_event(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 del_trace_kprobe_events(struct strlist *dellist);
extern void show_perf_probe_events(void); extern void show_perf_probe_events(void);
......
...@@ -687,10 +687,8 @@ int find_probepoint(int fd, struct probe_point *pp) ...@@ -687,10 +687,8 @@ int find_probepoint(int fd, struct probe_point *pp)
struct probe_finder pf = {.pp = pp}; struct probe_finder pf = {.pp = pp};
ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
if (ret != DW_DLV_OK) { if (ret != DW_DLV_OK)
pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO.\n");
return -ENOENT; return -ENOENT;
}
pp->found = 0; pp->found = 0;
while (++cu_number) { while (++cu_number) {
......
...@@ -12,6 +12,9 @@ static inline int is_c_varname(const char *name) ...@@ -12,6 +12,9 @@ static inline int is_c_varname(const char *name)
} }
struct probe_point { struct probe_point {
char *event; /* Event name */
char *group; /* Event group */
/* Inputs */ /* Inputs */
char *file; /* File name */ char *file; /* File name */
int line; /* Line number */ int line; /* Line number */
......
This diff is collapsed.
#ifndef __PERF_SESSION_H #ifndef __PERF_SESSION_H
#define __PERF_SESSION_H #define __PERF_SESSION_H
#include "event.h"
#include "header.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_session {
struct perf_header header; struct perf_header header;
unsigned long size; 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 fd;
int cwdlen;
char *cwd;
char filename[0]; 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); struct perf_session *perf_session__new(const char *filename, int mode, bool force);
void perf_session__delete(struct perf_session *self); 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 */ #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