2010-05-14 00:47:16 +07:00
|
|
|
#include "util.h"
|
2010-05-21 17:48:39 +07:00
|
|
|
#include "build-id.h"
|
2009-09-28 20:32:55 +07:00
|
|
|
#include "hist.h"
|
2017-01-06 13:23:31 +07:00
|
|
|
#include "map.h"
|
2009-12-14 22:10:39 +07:00
|
|
|
#include "session.h"
|
perf tools: Add 'cgroup_id' sort order keyword
This patch introduces a cgroup identifier entry field in perf report to
identify or distinguish data of different cgroups. It uses the device
number and inode number of cgroup namespace, included in perf data with
the new PERF_RECORD_NAMESPACES event, as cgroup identifier.
With the assumption that each container is created with it's own cgroup
namespace, this allows assessment/analysis of multiple containers at
once.
A simple test for this would be to clone a few processes passing
SIGCHILD & CLONE_NEWCROUP flags to each of them, execute shell and run
different workloads on each of those contexts, while running perf
record command with --namespaces option.
Shown below is the output of perf report, sorted with cgroup identifier,
on perf.data generated with the above test scenario, clearly indicating
one context's considerable use of kernel memory in comparison with
others:
$ perf report -s cgroup_id,sample --stdio
#
# Total Lost Samples: 0
#
# Samples: 5K of event 'kmem:kmalloc'
# Event count (approx.): 5965
#
# Overhead cgroup id (dev/inode) Samples
# ........ ..................... ............
#
81.27% 3/0xeffffffb 4848
16.24% 3/0xf00000d0 969
1.16% 3/0xf00000ce 69
0.82% 3/0xf00000cf 49
0.50% 0/0x0 30
While this is a start, there is further scope of improving this. For
example, instead of cgroup namespace's device and inode numbers, dev
and inode numbers of some or all namespaces may be used to distinguish
which processes are running in a given container context.
Also, scripts to map device and inode info to containers sounds
plausible for better tracing of containers.
Signed-off-by: Hari Bathini <hbathini@linux.vnet.ibm.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>
Cc: Aravinda Prasad <aravinda@linux.vnet.ibm.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sargun Dhillon <sargun@sargun.me>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/148891933338.25309.756882900782042645.stgit@hbathini.in.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-03-08 03:42:13 +07:00
|
|
|
#include "namespaces.h"
|
2009-12-14 22:10:39 +07:00
|
|
|
#include "sort.h"
|
2014-10-11 01:49:21 +07:00
|
|
|
#include "evlist.h"
|
2013-01-22 16:09:33 +07:00
|
|
|
#include "evsel.h"
|
2013-10-30 07:40:34 +07:00
|
|
|
#include "annotate.h"
|
2017-04-18 02:30:49 +07:00
|
|
|
#include "srcline.h"
|
2017-04-20 07:34:35 +07:00
|
|
|
#include "thread.h"
|
2014-12-22 11:44:10 +07:00
|
|
|
#include "ui/progress.h"
|
2017-04-18 20:46:11 +07:00
|
|
|
#include <errno.h>
|
2009-12-16 23:31:49 +07:00
|
|
|
#include <math.h>
|
2017-04-20 04:51:14 +07:00
|
|
|
#include <sys/param.h>
|
2009-09-28 20:32:55 +07:00
|
|
|
|
2011-10-19 22:09:10 +07:00
|
|
|
static bool hists__filter_entry_by_dso(struct hists *hists,
|
|
|
|
struct hist_entry *he);
|
|
|
|
static bool hists__filter_entry_by_thread(struct hists *hists,
|
|
|
|
struct hist_entry *he);
|
2012-03-16 15:50:51 +07:00
|
|
|
static bool hists__filter_entry_by_symbol(struct hists *hists,
|
|
|
|
struct hist_entry *he);
|
2015-09-04 21:45:44 +07:00
|
|
|
static bool hists__filter_entry_by_socket(struct hists *hists,
|
|
|
|
struct hist_entry *he);
|
2011-10-19 22:09:10 +07:00
|
|
|
|
2011-09-26 22:33:28 +07:00
|
|
|
u16 hists__col_len(struct hists *hists, enum hist_column col)
|
2010-07-21 00:42:52 +07:00
|
|
|
{
|
2011-09-26 22:33:28 +07:00
|
|
|
return hists->col_len[col];
|
2010-07-21 00:42:52 +07:00
|
|
|
}
|
|
|
|
|
2011-09-26 22:33:28 +07:00
|
|
|
void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len)
|
2010-07-21 00:42:52 +07:00
|
|
|
{
|
2011-09-26 22:33:28 +07:00
|
|
|
hists->col_len[col] = len;
|
2010-07-21 00:42:52 +07:00
|
|
|
}
|
|
|
|
|
2011-09-26 22:33:28 +07:00
|
|
|
bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len)
|
2010-07-21 00:42:52 +07:00
|
|
|
{
|
2011-09-26 22:33:28 +07:00
|
|
|
if (len > hists__col_len(hists, col)) {
|
|
|
|
hists__set_col_len(hists, col, len);
|
2010-07-21 00:42:52 +07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-08-20 11:52:05 +07:00
|
|
|
void hists__reset_col_len(struct hists *hists)
|
2010-07-21 00:42:52 +07:00
|
|
|
{
|
|
|
|
enum hist_column col;
|
|
|
|
|
|
|
|
for (col = 0; col < HISTC_NR_COLS; ++col)
|
2011-09-26 22:33:28 +07:00
|
|
|
hists__set_col_len(hists, col, 0);
|
2010-07-21 00:42:52 +07:00
|
|
|
}
|
|
|
|
|
2012-02-10 05:21:01 +07:00
|
|
|
static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
|
|
|
|
{
|
|
|
|
const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
|
|
|
|
|
|
|
|
if (hists__col_len(hists, dso) < unresolved_col_width &&
|
|
|
|
!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
|
|
|
|
!symbol_conf.dso_list)
|
|
|
|
hists__set_col_len(hists, dso, unresolved_col_width);
|
|
|
|
}
|
|
|
|
|
2012-08-20 11:52:05 +07:00
|
|
|
void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
|
2010-07-21 00:42:52 +07:00
|
|
|
{
|
2012-02-10 05:21:01 +07:00
|
|
|
const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
|
2013-01-24 22:10:35 +07:00
|
|
|
int symlen;
|
2010-07-21 00:42:52 +07:00
|
|
|
u16 len;
|
|
|
|
|
2013-04-01 18:35:19 +07:00
|
|
|
/*
|
|
|
|
* +4 accounts for '[x] ' priv level info
|
|
|
|
* +2 accounts for 0x prefix on raw addresses
|
|
|
|
* +3 accounts for ' y ' symtab origin info
|
|
|
|
*/
|
|
|
|
if (h->ms.sym) {
|
|
|
|
symlen = h->ms.sym->namelen + 4;
|
2017-02-17 15:17:38 +07:00
|
|
|
if (verbose > 0)
|
2013-04-01 18:35:19 +07:00
|
|
|
symlen += BITS_PER_LONG / 4 + 2 + 3;
|
|
|
|
hists__new_col_len(hists, HISTC_SYMBOL, symlen);
|
|
|
|
} else {
|
2013-01-24 22:10:35 +07:00
|
|
|
symlen = unresolved_col_width + 4 + 2;
|
|
|
|
hists__new_col_len(hists, HISTC_SYMBOL, symlen);
|
2012-02-10 05:21:01 +07:00
|
|
|
hists__set_unres_dso_col_len(hists, HISTC_DSO);
|
2013-01-24 22:10:35 +07:00
|
|
|
}
|
2010-07-21 00:42:52 +07:00
|
|
|
|
|
|
|
len = thread__comm_len(h->thread);
|
2011-09-26 22:33:28 +07:00
|
|
|
if (hists__new_col_len(hists, HISTC_COMM, len))
|
2016-06-21 04:58:19 +07:00
|
|
|
hists__set_col_len(hists, HISTC_THREAD, len + 8);
|
2010-07-21 00:42:52 +07:00
|
|
|
|
|
|
|
if (h->ms.map) {
|
|
|
|
len = dso__name_len(h->ms.map->dso);
|
2011-09-26 22:33:28 +07:00
|
|
|
hists__new_col_len(hists, HISTC_DSO, len);
|
2010-07-21 00:42:52 +07:00
|
|
|
}
|
2012-02-10 05:21:01 +07:00
|
|
|
|
2012-12-27 16:11:42 +07:00
|
|
|
if (h->parent)
|
|
|
|
hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen);
|
|
|
|
|
2012-02-10 05:21:01 +07:00
|
|
|
if (h->branch_info) {
|
|
|
|
if (h->branch_info->from.sym) {
|
|
|
|
symlen = (int)h->branch_info->from.sym->namelen + 4;
|
2017-02-17 15:17:38 +07:00
|
|
|
if (verbose > 0)
|
2013-04-01 18:35:19 +07:00
|
|
|
symlen += BITS_PER_LONG / 4 + 2 + 3;
|
2012-02-10 05:21:01 +07:00
|
|
|
hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
|
|
|
|
|
|
|
|
symlen = dso__name_len(h->branch_info->from.map->dso);
|
|
|
|
hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
|
|
|
|
} else {
|
|
|
|
symlen = unresolved_col_width + 4 + 2;
|
|
|
|
hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
|
|
|
|
hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (h->branch_info->to.sym) {
|
|
|
|
symlen = (int)h->branch_info->to.sym->namelen + 4;
|
2017-02-17 15:17:38 +07:00
|
|
|
if (verbose > 0)
|
2013-04-01 18:35:19 +07:00
|
|
|
symlen += BITS_PER_LONG / 4 + 2 + 3;
|
2012-02-10 05:21:01 +07:00
|
|
|
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
|
|
|
|
|
|
|
|
symlen = dso__name_len(h->branch_info->to.map->dso);
|
|
|
|
hists__new_col_len(hists, HISTC_DSO_TO, symlen);
|
|
|
|
} else {
|
|
|
|
symlen = unresolved_col_width + 4 + 2;
|
|
|
|
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
|
|
|
|
hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
|
|
|
|
}
|
2016-05-21 03:15:08 +07:00
|
|
|
|
|
|
|
if (h->branch_info->srcline_from)
|
|
|
|
hists__new_col_len(hists, HISTC_SRCLINE_FROM,
|
|
|
|
strlen(h->branch_info->srcline_from));
|
|
|
|
if (h->branch_info->srcline_to)
|
|
|
|
hists__new_col_len(hists, HISTC_SRCLINE_TO,
|
|
|
|
strlen(h->branch_info->srcline_to));
|
2012-02-10 05:21:01 +07:00
|
|
|
}
|
2013-01-24 22:10:35 +07:00
|
|
|
|
|
|
|
if (h->mem_info) {
|
|
|
|
if (h->mem_info->daddr.sym) {
|
|
|
|
symlen = (int)h->mem_info->daddr.sym->namelen + 4
|
|
|
|
+ unresolved_col_width + 2;
|
|
|
|
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
|
|
|
|
symlen);
|
2014-06-01 20:38:29 +07:00
|
|
|
hists__new_col_len(hists, HISTC_MEM_DCACHELINE,
|
|
|
|
symlen + 1);
|
2013-01-24 22:10:35 +07:00
|
|
|
} else {
|
|
|
|
symlen = unresolved_col_width + 4 + 2;
|
|
|
|
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL,
|
|
|
|
symlen);
|
2016-01-20 18:56:33 +07:00
|
|
|
hists__new_col_len(hists, HISTC_MEM_DCACHELINE,
|
|
|
|
symlen);
|
2013-01-24 22:10:35 +07:00
|
|
|
}
|
2015-10-06 01:06:08 +07:00
|
|
|
|
|
|
|
if (h->mem_info->iaddr.sym) {
|
|
|
|
symlen = (int)h->mem_info->iaddr.sym->namelen + 4
|
|
|
|
+ unresolved_col_width + 2;
|
|
|
|
hists__new_col_len(hists, HISTC_MEM_IADDR_SYMBOL,
|
|
|
|
symlen);
|
|
|
|
} else {
|
|
|
|
symlen = unresolved_col_width + 4 + 2;
|
|
|
|
hists__new_col_len(hists, HISTC_MEM_IADDR_SYMBOL,
|
|
|
|
symlen);
|
|
|
|
}
|
|
|
|
|
2013-01-24 22:10:35 +07:00
|
|
|
if (h->mem_info->daddr.map) {
|
|
|
|
symlen = dso__name_len(h->mem_info->daddr.map->dso);
|
|
|
|
hists__new_col_len(hists, HISTC_MEM_DADDR_DSO,
|
|
|
|
symlen);
|
|
|
|
} else {
|
|
|
|
symlen = unresolved_col_width + 4 + 2;
|
|
|
|
hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO);
|
|
|
|
}
|
2017-08-30 00:11:09 +07:00
|
|
|
|
|
|
|
hists__new_col_len(hists, HISTC_MEM_PHYS_DADDR,
|
|
|
|
unresolved_col_width + 4 + 2);
|
|
|
|
|
2013-01-24 22:10:35 +07:00
|
|
|
} else {
|
|
|
|
symlen = unresolved_col_width + 4 + 2;
|
|
|
|
hists__new_col_len(hists, HISTC_MEM_DADDR_SYMBOL, symlen);
|
2015-10-06 01:06:08 +07:00
|
|
|
hists__new_col_len(hists, HISTC_MEM_IADDR_SYMBOL, symlen);
|
2013-01-24 22:10:35 +07:00
|
|
|
hists__set_unres_dso_col_len(hists, HISTC_MEM_DADDR_DSO);
|
|
|
|
}
|
|
|
|
|
perf tools: Add 'cgroup_id' sort order keyword
This patch introduces a cgroup identifier entry field in perf report to
identify or distinguish data of different cgroups. It uses the device
number and inode number of cgroup namespace, included in perf data with
the new PERF_RECORD_NAMESPACES event, as cgroup identifier.
With the assumption that each container is created with it's own cgroup
namespace, this allows assessment/analysis of multiple containers at
once.
A simple test for this would be to clone a few processes passing
SIGCHILD & CLONE_NEWCROUP flags to each of them, execute shell and run
different workloads on each of those contexts, while running perf
record command with --namespaces option.
Shown below is the output of perf report, sorted with cgroup identifier,
on perf.data generated with the above test scenario, clearly indicating
one context's considerable use of kernel memory in comparison with
others:
$ perf report -s cgroup_id,sample --stdio
#
# Total Lost Samples: 0
#
# Samples: 5K of event 'kmem:kmalloc'
# Event count (approx.): 5965
#
# Overhead cgroup id (dev/inode) Samples
# ........ ..................... ............
#
81.27% 3/0xeffffffb 4848
16.24% 3/0xf00000d0 969
1.16% 3/0xf00000ce 69
0.82% 3/0xf00000cf 49
0.50% 0/0x0 30
While this is a start, there is further scope of improving this. For
example, instead of cgroup namespace's device and inode numbers, dev
and inode numbers of some or all namespaces may be used to distinguish
which processes are running in a given container context.
Also, scripts to map device and inode info to containers sounds
plausible for better tracing of containers.
Signed-off-by: Hari Bathini <hbathini@linux.vnet.ibm.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>
Cc: Aravinda Prasad <aravinda@linux.vnet.ibm.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sargun Dhillon <sargun@sargun.me>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/148891933338.25309.756882900782042645.stgit@hbathini.in.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-03-08 03:42:13 +07:00
|
|
|
hists__new_col_len(hists, HISTC_CGROUP_ID, 20);
|
2015-09-09 22:14:00 +07:00
|
|
|
hists__new_col_len(hists, HISTC_CPU, 3);
|
2015-09-04 21:45:43 +07:00
|
|
|
hists__new_col_len(hists, HISTC_SOCKET, 6);
|
2013-01-24 22:10:35 +07:00
|
|
|
hists__new_col_len(hists, HISTC_MEM_LOCKED, 6);
|
|
|
|
hists__new_col_len(hists, HISTC_MEM_TLB, 22);
|
|
|
|
hists__new_col_len(hists, HISTC_MEM_SNOOP, 12);
|
|
|
|
hists__new_col_len(hists, HISTC_MEM_LVL, 21 + 3);
|
|
|
|
hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
|
|
|
|
hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
|
2013-09-20 21:40:43 +07:00
|
|
|
|
2016-09-19 20:10:10 +07:00
|
|
|
if (h->srcline) {
|
|
|
|
len = MAX(strlen(h->srcline), strlen(sort_srcline.se_header));
|
|
|
|
hists__new_col_len(hists, HISTC_SRCLINE, len);
|
|
|
|
}
|
2015-08-11 02:53:54 +07:00
|
|
|
|
2015-08-08 05:54:24 +07:00
|
|
|
if (h->srcfile)
|
|
|
|
hists__new_col_len(hists, HISTC_SRCFILE, strlen(h->srcfile));
|
|
|
|
|
2013-09-20 21:40:43 +07:00
|
|
|
if (h->transaction)
|
|
|
|
hists__new_col_len(hists, HISTC_TRANSACTION,
|
|
|
|
hist_entry__transaction_len());
|
2016-02-21 21:22:38 +07:00
|
|
|
|
|
|
|
if (h->trace_output)
|
|
|
|
hists__new_col_len(hists, HISTC_TRACE, strlen(h->trace_output));
|
2010-07-21 00:42:52 +07:00
|
|
|
}
|
|
|
|
|
2012-08-20 11:52:05 +07:00
|
|
|
void hists__output_recalc_col_len(struct hists *hists, int max_rows)
|
|
|
|
{
|
|
|
|
struct rb_node *next = rb_first(&hists->entries);
|
|
|
|
struct hist_entry *n;
|
|
|
|
int row = 0;
|
|
|
|
|
|
|
|
hists__reset_col_len(hists);
|
|
|
|
|
|
|
|
while (next && row++ < max_rows) {
|
|
|
|
n = rb_entry(next, struct hist_entry, rb_node);
|
|
|
|
if (!n->filtered)
|
|
|
|
hists__calc_col_len(hists, n);
|
|
|
|
next = rb_next(&n->rb_node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-14 12:25:37 +07:00
|
|
|
static void he_stat__add_cpumode_period(struct he_stat *he_stat,
|
|
|
|
unsigned int cpumode, u64 period)
|
2010-04-19 12:32:50 +07:00
|
|
|
{
|
2010-05-09 23:02:23 +07:00
|
|
|
switch (cpumode) {
|
2010-04-19 12:32:50 +07:00
|
|
|
case PERF_RECORD_MISC_KERNEL:
|
2014-01-14 12:25:37 +07:00
|
|
|
he_stat->period_sys += period;
|
2010-04-19 12:32:50 +07:00
|
|
|
break;
|
|
|
|
case PERF_RECORD_MISC_USER:
|
2014-01-14 12:25:37 +07:00
|
|
|
he_stat->period_us += period;
|
2010-04-19 12:32:50 +07:00
|
|
|
break;
|
|
|
|
case PERF_RECORD_MISC_GUEST_KERNEL:
|
2014-01-14 12:25:37 +07:00
|
|
|
he_stat->period_guest_sys += period;
|
2010-04-19 12:32:50 +07:00
|
|
|
break;
|
|
|
|
case PERF_RECORD_MISC_GUEST_USER:
|
2014-01-14 12:25:37 +07:00
|
|
|
he_stat->period_guest_us += period;
|
2010-04-19 12:32:50 +07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-24 22:10:29 +07:00
|
|
|
static void he_stat__add_period(struct he_stat *he_stat, u64 period,
|
|
|
|
u64 weight)
|
2012-10-04 19:49:43 +07:00
|
|
|
{
|
2013-01-24 22:10:35 +07:00
|
|
|
|
2012-10-04 19:49:43 +07:00
|
|
|
he_stat->period += period;
|
2013-01-24 22:10:29 +07:00
|
|
|
he_stat->weight += weight;
|
2012-10-04 19:49:43 +07:00
|
|
|
he_stat->nr_events += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src)
|
|
|
|
{
|
|
|
|
dest->period += src->period;
|
|
|
|
dest->period_sys += src->period_sys;
|
|
|
|
dest->period_us += src->period_us;
|
|
|
|
dest->period_guest_sys += src->period_guest_sys;
|
|
|
|
dest->period_guest_us += src->period_guest_us;
|
|
|
|
dest->nr_events += src->nr_events;
|
2013-01-24 22:10:29 +07:00
|
|
|
dest->weight += src->weight;
|
2012-10-04 19:49:43 +07:00
|
|
|
}
|
|
|
|
|
2014-01-14 12:25:37 +07:00
|
|
|
static void he_stat__decay(struct he_stat *he_stat)
|
perf top: Reuse the 'report' hist_entry/hists classes
This actually fixes several problems we had in the old 'perf top':
1. Unresolved symbols not show, limitation that came from the old
"KernelTop" codebase, to solve it we would need to do changes
that would make sym_entry have most of the hist_entry fields.
2. It was using the number of samples, not the sum of sample->period.
And brings the --sort code that allows us to have all the views in
'perf report', for instance:
[root@emilia ~]# perf top --sort dso
PerfTop: 5903 irqs/sec kernel:77.5% exact: 0.0% [1000Hz cycles], (all, 8 CPUs)
------------------------------------------------------------------------------
31.59% libcrypto.so.1.0.0
21.55% [kernel]
18.57% libpython2.6.so.1.0
7.04% libc-2.12.so
6.99% _backend_agg.so
4.72% sshd
1.48% multiarray.so
1.39% libfreetype.so.6.3.22
1.37% perf
0.71% libgobject-2.0.so.0.2200.5
0.53% [tg3]
0.48% libglib-2.0.so.0.2200.5
0.44% libstdc++.so.6.0.13
0.40% libcairo.so.2.10800.8
0.38% libm-2.12.so
0.34% umath.so
0.30% libgdk-x11-2.0.so.0.1800.9
0.22% libpthread-2.12.so
0.20% libgtk-x11-2.0.so.0.1800.9
0.20% librt-2.12.so
0.15% _path.so
0.13% libpango-1.0.so.0.2800.1
0.11% libatlas.so.3.0
0.09% ft2font.so
0.09% libpangoft2-1.0.so.0.2800.1
0.08% libX11.so.6.3.0
0.07% [vdso]
0.06% cyclictest
^C
All the filter lists can be used as well: --dsos, --comms, --symbols,
etc.
The 'perf report' TUI is also reused, being possible to apply all the
zoom operations, do annotation, etc.
This change will allow multiple simplifications in the symbol system as
well, that will be detailed in upcoming changesets.
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-xzaaldxq7zhqrrxdxjifk1mh@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-10-06 05:16:15 +07:00
|
|
|
{
|
2014-01-14 12:25:37 +07:00
|
|
|
he_stat->period = (he_stat->period * 7) / 8;
|
|
|
|
he_stat->nr_events = (he_stat->nr_events * 7) / 8;
|
2013-01-24 22:10:29 +07:00
|
|
|
/* XXX need decay for weight too? */
|
perf top: Reuse the 'report' hist_entry/hists classes
This actually fixes several problems we had in the old 'perf top':
1. Unresolved symbols not show, limitation that came from the old
"KernelTop" codebase, to solve it we would need to do changes
that would make sym_entry have most of the hist_entry fields.
2. It was using the number of samples, not the sum of sample->period.
And brings the --sort code that allows us to have all the views in
'perf report', for instance:
[root@emilia ~]# perf top --sort dso
PerfTop: 5903 irqs/sec kernel:77.5% exact: 0.0% [1000Hz cycles], (all, 8 CPUs)
------------------------------------------------------------------------------
31.59% libcrypto.so.1.0.0
21.55% [kernel]
18.57% libpython2.6.so.1.0
7.04% libc-2.12.so
6.99% _backend_agg.so
4.72% sshd
1.48% multiarray.so
1.39% libfreetype.so.6.3.22
1.37% perf
0.71% libgobject-2.0.so.0.2200.5
0.53% [tg3]
0.48% libglib-2.0.so.0.2200.5
0.44% libstdc++.so.6.0.13
0.40% libcairo.so.2.10800.8
0.38% libm-2.12.so
0.34% umath.so
0.30% libgdk-x11-2.0.so.0.1800.9
0.22% libpthread-2.12.so
0.20% libgtk-x11-2.0.so.0.1800.9
0.20% librt-2.12.so
0.15% _path.so
0.13% libpango-1.0.so.0.2800.1
0.11% libatlas.so.3.0
0.09% ft2font.so
0.09% libpangoft2-1.0.so.0.2800.1
0.08% libX11.so.6.3.0
0.07% [vdso]
0.06% cyclictest
^C
All the filter lists can be used as well: --dsos, --comms, --symbols,
etc.
The 'perf report' TUI is also reused, being possible to apply all the
zoom operations, do annotation, etc.
This change will allow multiple simplifications in the symbol system as
well, that will be detailed in upcoming changesets.
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-xzaaldxq7zhqrrxdxjifk1mh@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-10-06 05:16:15 +07:00
|
|
|
}
|
|
|
|
|
2016-02-24 22:13:49 +07:00
|
|
|
static void hists__delete_entry(struct hists *hists, struct hist_entry *he);
|
|
|
|
|
perf top: Reuse the 'report' hist_entry/hists classes
This actually fixes several problems we had in the old 'perf top':
1. Unresolved symbols not show, limitation that came from the old
"KernelTop" codebase, to solve it we would need to do changes
that would make sym_entry have most of the hist_entry fields.
2. It was using the number of samples, not the sum of sample->period.
And brings the --sort code that allows us to have all the views in
'perf report', for instance:
[root@emilia ~]# perf top --sort dso
PerfTop: 5903 irqs/sec kernel:77.5% exact: 0.0% [1000Hz cycles], (all, 8 CPUs)
------------------------------------------------------------------------------
31.59% libcrypto.so.1.0.0
21.55% [kernel]
18.57% libpython2.6.so.1.0
7.04% libc-2.12.so
6.99% _backend_agg.so
4.72% sshd
1.48% multiarray.so
1.39% libfreetype.so.6.3.22
1.37% perf
0.71% libgobject-2.0.so.0.2200.5
0.53% [tg3]
0.48% libglib-2.0.so.0.2200.5
0.44% libstdc++.so.6.0.13
0.40% libcairo.so.2.10800.8
0.38% libm-2.12.so
0.34% umath.so
0.30% libgdk-x11-2.0.so.0.1800.9
0.22% libpthread-2.12.so
0.20% libgtk-x11-2.0.so.0.1800.9
0.20% librt-2.12.so
0.15% _path.so
0.13% libpango-1.0.so.0.2800.1
0.11% libatlas.so.3.0
0.09% ft2font.so
0.09% libpangoft2-1.0.so.0.2800.1
0.08% libX11.so.6.3.0
0.07% [vdso]
0.06% cyclictest
^C
All the filter lists can be used as well: --dsos, --comms, --symbols,
etc.
The 'perf report' TUI is also reused, being possible to apply all the
zoom operations, do annotation, etc.
This change will allow multiple simplifications in the symbol system as
well, that will be detailed in upcoming changesets.
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-xzaaldxq7zhqrrxdxjifk1mh@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-10-06 05:16:15 +07:00
|
|
|
static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
|
|
|
|
{
|
2012-10-04 19:49:41 +07:00
|
|
|
u64 prev_period = he->stat.period;
|
2014-04-22 11:44:23 +07:00
|
|
|
u64 diff;
|
2011-10-20 15:45:44 +07:00
|
|
|
|
|
|
|
if (prev_period == 0)
|
2011-10-13 18:01:33 +07:00
|
|
|
return true;
|
2011-10-20 15:45:44 +07:00
|
|
|
|
2014-01-14 12:25:37 +07:00
|
|
|
he_stat__decay(&he->stat);
|
2012-09-11 11:15:07 +07:00
|
|
|
if (symbol_conf.cumulate_callchain)
|
|
|
|
he_stat__decay(he->stat_acc);
|
2016-01-05 10:06:00 +07:00
|
|
|
decay_callchain(he->callchain);
|
2011-10-20 15:45:44 +07:00
|
|
|
|
2014-04-22 11:44:23 +07:00
|
|
|
diff = prev_period - he->stat.period;
|
|
|
|
|
2016-02-24 22:13:49 +07:00
|
|
|
if (!he->depth) {
|
|
|
|
hists->stats.total_period -= diff;
|
|
|
|
if (!he->filtered)
|
|
|
|
hists->stats.total_non_filtered_period -= diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!he->leaf) {
|
|
|
|
struct hist_entry *child;
|
|
|
|
struct rb_node *node = rb_first(&he->hroot_out);
|
|
|
|
while (node) {
|
|
|
|
child = rb_entry(node, struct hist_entry, rb_node);
|
|
|
|
node = rb_next(node);
|
|
|
|
|
|
|
|
if (hists__decay_entry(hists, child))
|
|
|
|
hists__delete_entry(hists, child);
|
|
|
|
}
|
|
|
|
}
|
2011-10-20 15:45:44 +07:00
|
|
|
|
2012-10-04 19:49:41 +07:00
|
|
|
return he->stat.period == 0;
|
perf top: Reuse the 'report' hist_entry/hists classes
This actually fixes several problems we had in the old 'perf top':
1. Unresolved symbols not show, limitation that came from the old
"KernelTop" codebase, to solve it we would need to do changes
that would make sym_entry have most of the hist_entry fields.
2. It was using the number of samples, not the sum of sample->period.
And brings the --sort code that allows us to have all the views in
'perf report', for instance:
[root@emilia ~]# perf top --sort dso
PerfTop: 5903 irqs/sec kernel:77.5% exact: 0.0% [1000Hz cycles], (all, 8 CPUs)
------------------------------------------------------------------------------
31.59% libcrypto.so.1.0.0
21.55% [kernel]
18.57% libpython2.6.so.1.0
7.04% libc-2.12.so
6.99% _backend_agg.so
4.72% sshd
1.48% multiarray.so
1.39% libfreetype.so.6.3.22
1.37% perf
0.71% libgobject-2.0.so.0.2200.5
0.53% [tg3]
0.48% libglib-2.0.so.0.2200.5
0.44% libstdc++.so.6.0.13
0.40% libcairo.so.2.10800.8
0.38% libm-2.12.so
0.34% umath.so
0.30% libgdk-x11-2.0.so.0.1800.9
0.22% libpthread-2.12.so
0.20% libgtk-x11-2.0.so.0.1800.9
0.20% librt-2.12.so
0.15% _path.so
0.13% libpango-1.0.so.0.2800.1
0.11% libatlas.so.3.0
0.09% ft2font.so
0.09% libpangoft2-1.0.so.0.2800.1
0.08% libX11.so.6.3.0
0.07% [vdso]
0.06% cyclictest
^C
All the filter lists can be used as well: --dsos, --comms, --symbols,
etc.
The 'perf report' TUI is also reused, being possible to apply all the
zoom operations, do annotation, etc.
This change will allow multiple simplifications in the symbol system as
well, that will be detailed in upcoming changesets.
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-xzaaldxq7zhqrrxdxjifk1mh@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-10-06 05:16:15 +07:00
|
|
|
}
|
|
|
|
|
2014-12-19 22:41:28 +07:00
|
|
|
static void hists__delete_entry(struct hists *hists, struct hist_entry *he)
|
|
|
|
{
|
2016-02-24 22:13:49 +07:00
|
|
|
struct rb_root *root_in;
|
|
|
|
struct rb_root *root_out;
|
|
|
|
|
|
|
|
if (he->parent_he) {
|
|
|
|
root_in = &he->parent_he->hroot_in;
|
|
|
|
root_out = &he->parent_he->hroot_out;
|
|
|
|
} else {
|
2016-05-03 18:54:42 +07:00
|
|
|
if (hists__has(hists, need_collapse))
|
2016-02-24 22:13:49 +07:00
|
|
|
root_in = &hists->entries_collapsed;
|
|
|
|
else
|
|
|
|
root_in = hists->entries_in;
|
|
|
|
root_out = &hists->entries;
|
|
|
|
}
|
2014-12-19 22:41:28 +07:00
|
|
|
|
2016-02-24 22:13:49 +07:00
|
|
|
rb_erase(&he->rb_node_in, root_in);
|
|
|
|
rb_erase(&he->rb_node, root_out);
|
2014-12-19 22:41:28 +07:00
|
|
|
|
|
|
|
--hists->nr_entries;
|
|
|
|
if (!he->filtered)
|
|
|
|
--hists->nr_non_filtered_entries;
|
|
|
|
|
|
|
|
hist_entry__delete(he);
|
|
|
|
}
|
|
|
|
|
2013-05-14 09:09:01 +07:00
|
|
|
void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
|
perf top: Reuse the 'report' hist_entry/hists classes
This actually fixes several problems we had in the old 'perf top':
1. Unresolved symbols not show, limitation that came from the old
"KernelTop" codebase, to solve it we would need to do changes
that would make sym_entry have most of the hist_entry fields.
2. It was using the number of samples, not the sum of sample->period.
And brings the --sort code that allows us to have all the views in
'perf report', for instance:
[root@emilia ~]# perf top --sort dso
PerfTop: 5903 irqs/sec kernel:77.5% exact: 0.0% [1000Hz cycles], (all, 8 CPUs)
------------------------------------------------------------------------------
31.59% libcrypto.so.1.0.0
21.55% [kernel]
18.57% libpython2.6.so.1.0
7.04% libc-2.12.so
6.99% _backend_agg.so
4.72% sshd
1.48% multiarray.so
1.39% libfreetype.so.6.3.22
1.37% perf
0.71% libgobject-2.0.so.0.2200.5
0.53% [tg3]
0.48% libglib-2.0.so.0.2200.5
0.44% libstdc++.so.6.0.13
0.40% libcairo.so.2.10800.8
0.38% libm-2.12.so
0.34% umath.so
0.30% libgdk-x11-2.0.so.0.1800.9
0.22% libpthread-2.12.so
0.20% libgtk-x11-2.0.so.0.1800.9
0.20% librt-2.12.so
0.15% _path.so
0.13% libpango-1.0.so.0.2800.1
0.11% libatlas.so.3.0
0.09% ft2font.so
0.09% libpangoft2-1.0.so.0.2800.1
0.08% libX11.so.6.3.0
0.07% [vdso]
0.06% cyclictest
^C
All the filter lists can be used as well: --dsos, --comms, --symbols,
etc.
The 'perf report' TUI is also reused, being possible to apply all the
zoom operations, do annotation, etc.
This change will allow multiple simplifications in the symbol system as
well, that will be detailed in upcoming changesets.
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-xzaaldxq7zhqrrxdxjifk1mh@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-10-06 05:16:15 +07:00
|
|
|
{
|
|
|
|
struct rb_node *next = rb_first(&hists->entries);
|
|
|
|
struct hist_entry *n;
|
|
|
|
|
|
|
|
while (next) {
|
|
|
|
n = rb_entry(next, struct hist_entry, rb_node);
|
|
|
|
next = rb_next(&n->rb_node);
|
2011-10-17 18:05:04 +07:00
|
|
|
if (((zap_user && n->level == '.') ||
|
|
|
|
(zap_kernel && n->level != '.') ||
|
2015-03-18 03:18:58 +07:00
|
|
|
hists__decay_entry(hists, n))) {
|
2014-12-19 22:41:28 +07:00
|
|
|
hists__delete_entry(hists, n);
|
perf top: Reuse the 'report' hist_entry/hists classes
This actually fixes several problems we had in the old 'perf top':
1. Unresolved symbols not show, limitation that came from the old
"KernelTop" codebase, to solve it we would need to do changes
that would make sym_entry have most of the hist_entry fields.
2. It was using the number of samples, not the sum of sample->period.
And brings the --sort code that allows us to have all the views in
'perf report', for instance:
[root@emilia ~]# perf top --sort dso
PerfTop: 5903 irqs/sec kernel:77.5% exact: 0.0% [1000Hz cycles], (all, 8 CPUs)
------------------------------------------------------------------------------
31.59% libcrypto.so.1.0.0
21.55% [kernel]
18.57% libpython2.6.so.1.0
7.04% libc-2.12.so
6.99% _backend_agg.so
4.72% sshd
1.48% multiarray.so
1.39% libfreetype.so.6.3.22
1.37% perf
0.71% libgobject-2.0.so.0.2200.5
0.53% [tg3]
0.48% libglib-2.0.so.0.2200.5
0.44% libstdc++.so.6.0.13
0.40% libcairo.so.2.10800.8
0.38% libm-2.12.so
0.34% umath.so
0.30% libgdk-x11-2.0.so.0.1800.9
0.22% libpthread-2.12.so
0.20% libgtk-x11-2.0.so.0.1800.9
0.20% librt-2.12.so
0.15% _path.so
0.13% libpango-1.0.so.0.2800.1
0.11% libatlas.so.3.0
0.09% ft2font.so
0.09% libpangoft2-1.0.so.0.2800.1
0.08% libX11.so.6.3.0
0.07% [vdso]
0.06% cyclictest
^C
All the filter lists can be used as well: --dsos, --comms, --symbols,
etc.
The 'perf report' TUI is also reused, being possible to apply all the
zoom operations, do annotation, etc.
This change will allow multiple simplifications in the symbol system as
well, that will be detailed in upcoming changesets.
Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-xzaaldxq7zhqrrxdxjifk1mh@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-10-06 05:16:15 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-12 15:16:05 +07:00
|
|
|
void hists__delete_entries(struct hists *hists)
|
|
|
|
{
|
|
|
|
struct rb_node *next = rb_first(&hists->entries);
|
|
|
|
struct hist_entry *n;
|
|
|
|
|
|
|
|
while (next) {
|
|
|
|
n = rb_entry(next, struct hist_entry, rb_node);
|
|
|
|
next = rb_next(&n->rb_node);
|
|
|
|
|
2014-12-19 22:41:28 +07:00
|
|
|
hists__delete_entry(hists, n);
|
2014-08-12 15:16:05 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-28 20:32:55 +07:00
|
|
|
/*
|
2010-05-15 00:19:35 +07:00
|
|
|
* histogram, sorted on item, collects periods
|
2009-09-28 20:32:55 +07:00
|
|
|
*/
|
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
static int hist_entry__init(struct hist_entry *he,
|
|
|
|
struct hist_entry *template,
|
|
|
|
bool sample_self)
|
2010-05-09 23:02:23 +07:00
|
|
|
{
|
2016-07-05 13:56:03 +07:00
|
|
|
*he = *template;
|
|
|
|
|
|
|
|
if (symbol_conf.cumulate_callchain) {
|
|
|
|
he->stat_acc = malloc(sizeof(he->stat));
|
|
|
|
if (he->stat_acc == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
memcpy(he->stat_acc, &he->stat, sizeof(he->stat));
|
|
|
|
if (!sample_self)
|
|
|
|
memset(&he->stat, 0, sizeof(he->stat));
|
|
|
|
}
|
2012-09-11 11:15:07 +07:00
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
map__get(he->ms.map);
|
|
|
|
|
|
|
|
if (he->branch_info) {
|
|
|
|
/*
|
|
|
|
* This branch info is (a part of) allocated from
|
|
|
|
* sample__resolve_bstack() and will be freed after
|
|
|
|
* adding new entries. So we need to save a copy.
|
|
|
|
*/
|
|
|
|
he->branch_info = malloc(sizeof(*he->branch_info));
|
|
|
|
if (he->branch_info == NULL) {
|
|
|
|
map__zput(he->ms.map);
|
|
|
|
free(he->stat_acc);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2012-09-11 11:15:07 +07:00
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
memcpy(he->branch_info, template->branch_info,
|
|
|
|
sizeof(*he->branch_info));
|
2010-05-09 23:02:23 +07:00
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
map__get(he->branch_info->from.map);
|
|
|
|
map__get(he->branch_info->to.map);
|
|
|
|
}
|
2012-10-04 19:49:42 +07:00
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
if (he->mem_info) {
|
|
|
|
map__get(he->mem_info->iaddr.map);
|
|
|
|
map__get(he->mem_info->daddr.map);
|
|
|
|
}
|
2012-09-11 11:15:07 +07:00
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
if (symbol_conf.use_callchain)
|
|
|
|
callchain_init(he->callchain);
|
2013-04-01 18:35:17 +07:00
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
if (he->raw_data) {
|
|
|
|
he->raw_data = memdup(he->raw_data, he->raw_size);
|
2013-04-01 18:35:17 +07:00
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
if (he->raw_data == NULL) {
|
|
|
|
map__put(he->ms.map);
|
|
|
|
if (he->branch_info) {
|
|
|
|
map__put(he->branch_info->from.map);
|
|
|
|
map__put(he->branch_info->to.map);
|
|
|
|
free(he->branch_info);
|
|
|
|
}
|
|
|
|
if (he->mem_info) {
|
|
|
|
map__put(he->mem_info->iaddr.map);
|
|
|
|
map__put(he->mem_info->daddr.map);
|
|
|
|
}
|
|
|
|
free(he->stat_acc);
|
|
|
|
return -ENOMEM;
|
2013-01-14 21:02:45 +07:00
|
|
|
}
|
2016-07-05 13:56:03 +07:00
|
|
|
}
|
|
|
|
INIT_LIST_HEAD(&he->pairs.node);
|
|
|
|
thread__get(he->thread);
|
2016-09-13 14:45:48 +07:00
|
|
|
he->hroot_in = RB_ROOT;
|
|
|
|
he->hroot_out = RB_ROOT;
|
2013-01-14 21:02:45 +07:00
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
if (!symbol_conf.report_hierarchy)
|
|
|
|
he->leaf = true;
|
2013-01-24 22:10:35 +07:00
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-05 13:56:04 +07:00
|
|
|
static void *hist_entry__zalloc(size_t size)
|
|
|
|
{
|
|
|
|
return zalloc(size + sizeof(struct hist_entry));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hist_entry__free(void *ptr)
|
|
|
|
{
|
|
|
|
free(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct hist_entry_ops default_ops = {
|
|
|
|
.new = hist_entry__zalloc,
|
|
|
|
.free = hist_entry__free,
|
|
|
|
};
|
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
static struct hist_entry *hist_entry__new(struct hist_entry *template,
|
|
|
|
bool sample_self)
|
|
|
|
{
|
2016-07-05 13:56:04 +07:00
|
|
|
struct hist_entry_ops *ops = template->ops;
|
2016-07-05 13:56:03 +07:00
|
|
|
size_t callchain_size = 0;
|
|
|
|
struct hist_entry *he;
|
|
|
|
int err = 0;
|
2016-02-24 22:13:34 +07:00
|
|
|
|
2016-07-05 13:56:04 +07:00
|
|
|
if (!ops)
|
|
|
|
ops = template->ops = &default_ops;
|
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
if (symbol_conf.use_callchain)
|
|
|
|
callchain_size = sizeof(struct callchain_root);
|
|
|
|
|
2016-07-05 13:56:04 +07:00
|
|
|
he = ops->new(callchain_size);
|
2016-07-05 13:56:03 +07:00
|
|
|
if (he) {
|
|
|
|
err = hist_entry__init(he, template, sample_self);
|
2016-07-05 13:56:04 +07:00
|
|
|
if (err) {
|
|
|
|
ops->free(he);
|
|
|
|
he = NULL;
|
|
|
|
}
|
2010-05-09 23:02:23 +07:00
|
|
|
}
|
|
|
|
|
2012-01-04 21:27:03 +07:00
|
|
|
return he;
|
2010-05-09 23:02:23 +07:00
|
|
|
}
|
|
|
|
|
2010-07-21 19:19:41 +07:00
|
|
|
static u8 symbol__parent_filter(const struct symbol *parent)
|
|
|
|
{
|
|
|
|
if (symbol_conf.exclude_other && parent == NULL)
|
|
|
|
return 1 << HIST_FILTER__PARENT;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-16 21:08:19 +07:00
|
|
|
static void hist_entry__add_callchain_period(struct hist_entry *he, u64 period)
|
|
|
|
{
|
|
|
|
if (!symbol_conf.use_callchain)
|
|
|
|
return;
|
|
|
|
|
|
|
|
he->hists->callchain_period += period;
|
|
|
|
if (!he->filtered)
|
|
|
|
he->hists->callchain_non_filtered_period += period;
|
|
|
|
}
|
|
|
|
|
2015-05-19 21:31:22 +07:00
|
|
|
static struct hist_entry *hists__findnew_entry(struct hists *hists,
|
|
|
|
struct hist_entry *entry,
|
|
|
|
struct addr_location *al,
|
|
|
|
bool sample_self)
|
2009-10-03 20:42:45 +07:00
|
|
|
{
|
2011-10-06 03:50:23 +07:00
|
|
|
struct rb_node **p;
|
2009-10-03 20:42:45 +07:00
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *he;
|
2013-10-01 21:22:15 +07:00
|
|
|
int64_t cmp;
|
2013-12-18 12:21:11 +07:00
|
|
|
u64 period = entry->stat.period;
|
|
|
|
u64 weight = entry->stat.weight;
|
2009-10-03 20:42:45 +07:00
|
|
|
|
2011-10-06 03:50:23 +07:00
|
|
|
p = &hists->entries_in->rb_node;
|
|
|
|
|
2009-10-03 20:42:45 +07:00
|
|
|
while (*p != NULL) {
|
|
|
|
parent = *p;
|
2011-10-06 03:50:23 +07:00
|
|
|
he = rb_entry(parent, struct hist_entry, rb_node_in);
|
2009-10-03 20:42:45 +07:00
|
|
|
|
2012-12-10 15:29:54 +07:00
|
|
|
/*
|
|
|
|
* Make sure that it receives arguments in a same order as
|
|
|
|
* hist_entry__collapse() so that we can use an appropriate
|
|
|
|
* function when searching an entry regardless which sort
|
|
|
|
* keys were used.
|
|
|
|
*/
|
|
|
|
cmp = hist_entry__cmp(he, entry);
|
2009-10-03 20:42:45 +07:00
|
|
|
|
|
|
|
if (!cmp) {
|
2016-01-27 22:40:49 +07:00
|
|
|
if (sample_self) {
|
2012-09-11 11:34:27 +07:00
|
|
|
he_stat__add_period(&he->stat, period, weight);
|
2016-02-16 21:08:19 +07:00
|
|
|
hist_entry__add_callchain_period(he, period);
|
2016-01-27 22:40:49 +07:00
|
|
|
}
|
2012-09-11 11:15:07 +07:00
|
|
|
if (symbol_conf.cumulate_callchain)
|
|
|
|
he_stat__add_period(he->stat_acc, period, weight);
|
2012-03-27 14:14:18 +07:00
|
|
|
|
2013-04-01 18:35:18 +07:00
|
|
|
/*
|
2014-01-22 23:05:06 +07:00
|
|
|
* This mem info was allocated from sample__resolve_mem
|
2013-04-01 18:35:18 +07:00
|
|
|
* and will not be used anymore.
|
|
|
|
*/
|
2013-12-28 02:55:14 +07:00
|
|
|
zfree(&entry->mem_info);
|
2013-04-01 18:35:18 +07:00
|
|
|
|
2012-03-27 14:14:18 +07:00
|
|
|
/* If the map of an existing hist_entry has
|
|
|
|
* become out-of-date due to an exec() or
|
|
|
|
* similar, update it. Otherwise we will
|
|
|
|
* mis-adjust symbol addresses when computing
|
|
|
|
* the history counter to increment.
|
|
|
|
*/
|
|
|
|
if (he->ms.map != entry->ms.map) {
|
2015-06-16 09:29:51 +07:00
|
|
|
map__put(he->ms.map);
|
|
|
|
he->ms.map = map__get(entry->ms.map);
|
2012-03-27 14:14:18 +07:00
|
|
|
}
|
2010-05-09 23:02:23 +07:00
|
|
|
goto out;
|
2009-10-03 20:42:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cmp < 0)
|
|
|
|
p = &(*p)->rb_left;
|
|
|
|
else
|
|
|
|
p = &(*p)->rb_right;
|
|
|
|
}
|
|
|
|
|
2012-09-11 11:34:27 +07:00
|
|
|
he = hist_entry__new(entry, sample_self);
|
2009-10-03 20:42:45 +07:00
|
|
|
if (!he)
|
2013-05-14 09:09:02 +07:00
|
|
|
return NULL;
|
2011-10-06 03:50:23 +07:00
|
|
|
|
2016-01-27 22:40:49 +07:00
|
|
|
if (sample_self)
|
2016-02-16 21:08:19 +07:00
|
|
|
hist_entry__add_callchain_period(he, period);
|
|
|
|
hists->nr_entries++;
|
2014-12-22 11:44:09 +07:00
|
|
|
|
2011-10-06 03:50:23 +07:00
|
|
|
rb_link_node(&he->rb_node_in, parent, p);
|
|
|
|
rb_insert_color(&he->rb_node_in, hists->entries_in);
|
2010-05-09 23:02:23 +07:00
|
|
|
out:
|
2012-09-11 11:34:27 +07:00
|
|
|
if (sample_self)
|
|
|
|
he_stat__add_cpumode_period(&he->stat, al->cpumode, period);
|
2012-09-11 11:15:07 +07:00
|
|
|
if (symbol_conf.cumulate_callchain)
|
|
|
|
he_stat__add_cpumode_period(he->stat_acc, al->cpumode, period);
|
2009-10-03 20:42:45 +07:00
|
|
|
return he;
|
|
|
|
}
|
|
|
|
|
2016-07-05 13:56:05 +07:00
|
|
|
static struct hist_entry*
|
|
|
|
__hists__add_entry(struct hists *hists,
|
|
|
|
struct addr_location *al,
|
|
|
|
struct symbol *sym_parent,
|
|
|
|
struct branch_info *bi,
|
|
|
|
struct mem_info *mi,
|
|
|
|
struct perf_sample *sample,
|
|
|
|
bool sample_self,
|
|
|
|
struct hist_entry_ops *ops)
|
2012-02-10 05:21:01 +07:00
|
|
|
{
|
perf tools: Add 'cgroup_id' sort order keyword
This patch introduces a cgroup identifier entry field in perf report to
identify or distinguish data of different cgroups. It uses the device
number and inode number of cgroup namespace, included in perf data with
the new PERF_RECORD_NAMESPACES event, as cgroup identifier.
With the assumption that each container is created with it's own cgroup
namespace, this allows assessment/analysis of multiple containers at
once.
A simple test for this would be to clone a few processes passing
SIGCHILD & CLONE_NEWCROUP flags to each of them, execute shell and run
different workloads on each of those contexts, while running perf
record command with --namespaces option.
Shown below is the output of perf report, sorted with cgroup identifier,
on perf.data generated with the above test scenario, clearly indicating
one context's considerable use of kernel memory in comparison with
others:
$ perf report -s cgroup_id,sample --stdio
#
# Total Lost Samples: 0
#
# Samples: 5K of event 'kmem:kmalloc'
# Event count (approx.): 5965
#
# Overhead cgroup id (dev/inode) Samples
# ........ ..................... ............
#
81.27% 3/0xeffffffb 4848
16.24% 3/0xf00000d0 969
1.16% 3/0xf00000ce 69
0.82% 3/0xf00000cf 49
0.50% 0/0x0 30
While this is a start, there is further scope of improving this. For
example, instead of cgroup namespace's device and inode numbers, dev
and inode numbers of some or all namespaces may be used to distinguish
which processes are running in a given container context.
Also, scripts to map device and inode info to containers sounds
plausible for better tracing of containers.
Signed-off-by: Hari Bathini <hbathini@linux.vnet.ibm.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>
Cc: Aravinda Prasad <aravinda@linux.vnet.ibm.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sargun Dhillon <sargun@sargun.me>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/148891933338.25309.756882900782042645.stgit@hbathini.in.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-03-08 03:42:13 +07:00
|
|
|
struct namespaces *ns = thread__namespaces(al->thread);
|
2012-02-10 05:21:01 +07:00
|
|
|
struct hist_entry entry = {
|
|
|
|
.thread = al->thread,
|
2013-09-13 14:28:57 +07:00
|
|
|
.comm = thread__comm(al->thread),
|
perf tools: Add 'cgroup_id' sort order keyword
This patch introduces a cgroup identifier entry field in perf report to
identify or distinguish data of different cgroups. It uses the device
number and inode number of cgroup namespace, included in perf data with
the new PERF_RECORD_NAMESPACES event, as cgroup identifier.
With the assumption that each container is created with it's own cgroup
namespace, this allows assessment/analysis of multiple containers at
once.
A simple test for this would be to clone a few processes passing
SIGCHILD & CLONE_NEWCROUP flags to each of them, execute shell and run
different workloads on each of those contexts, while running perf
record command with --namespaces option.
Shown below is the output of perf report, sorted with cgroup identifier,
on perf.data generated with the above test scenario, clearly indicating
one context's considerable use of kernel memory in comparison with
others:
$ perf report -s cgroup_id,sample --stdio
#
# Total Lost Samples: 0
#
# Samples: 5K of event 'kmem:kmalloc'
# Event count (approx.): 5965
#
# Overhead cgroup id (dev/inode) Samples
# ........ ..................... ............
#
81.27% 3/0xeffffffb 4848
16.24% 3/0xf00000d0 969
1.16% 3/0xf00000ce 69
0.82% 3/0xf00000cf 49
0.50% 0/0x0 30
While this is a start, there is further scope of improving this. For
example, instead of cgroup namespace's device and inode numbers, dev
and inode numbers of some or all namespaces may be used to distinguish
which processes are running in a given container context.
Also, scripts to map device and inode info to containers sounds
plausible for better tracing of containers.
Signed-off-by: Hari Bathini <hbathini@linux.vnet.ibm.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>
Cc: Aravinda Prasad <aravinda@linux.vnet.ibm.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sargun Dhillon <sargun@sargun.me>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/148891933338.25309.756882900782042645.stgit@hbathini.in.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-03-08 03:42:13 +07:00
|
|
|
.cgroup_id = {
|
|
|
|
.dev = ns ? ns->link_info[CGROUP_NS_INDEX].dev : 0,
|
|
|
|
.ino = ns ? ns->link_info[CGROUP_NS_INDEX].ino : 0,
|
|
|
|
},
|
2012-02-10 05:21:01 +07:00
|
|
|
.ms = {
|
|
|
|
.map = al->map,
|
|
|
|
.sym = al->sym,
|
|
|
|
},
|
perf report: Use srcline from callchain for hist entries
This also removes the symbol name from the srcline column, more on this
below.
This ensures we use the correct srcline, which could originate from a
potentially inlined function. The hist entries used to query for the
srcline based purely on the IP, which leads to wrong results for inlined
entries.
Before:
~~~~~
perf report --inline -s srcline -g none --stdio
...
# Children Self Source:Line
# ........ ........ ..................................................................................................................................
#
94.23% 0.00% __libc_start_main+18446603487898210537
94.23% 0.00% _start+41
44.58% 0.00% main+100
44.58% 0.00% std::_Norm_helper<true>::_S_do_it<double>+100
44.58% 0.00% std::__complex_abs+100
44.58% 0.00% std::abs<double>+100
44.58% 0.00% std::norm<double>+100
36.01% 0.00% hypot+18446603487892193300
25.81% 0.00% main+41
25.81% 0.00% std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator()+41
25.81% 0.00% std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> >+41
25.75% 25.75% random.h:143
18.39% 0.00% main+57
18.39% 0.00% std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator()+57
18.39% 0.00% std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> >+57
13.80% 13.80% random.tcc:3330
5.64% 0.00% ??:0
4.13% 4.13% __hypot_finite+163
4.13% 0.00% __hypot_finite+18446603487892193443
...
~~~~~
After:
~~~~~
perf report --inline -s srcline -g none --stdio
...
# Children Self Source:Line
# ........ ........ ...........................................
#
94.30% 1.19% main.cpp:39
94.23% 0.00% __libc_start_main+18446603487898210537
94.23% 0.00% _start+41
48.44% 1.70% random.h:1823
48.44% 0.00% random.h:1814
46.74% 2.53% random.h:185
44.68% 0.10% complex:589
44.68% 0.00% complex:597
44.68% 0.00% complex:654
44.68% 0.00% complex:664
40.61% 13.80% random.tcc:3330
36.01% 0.00% hypot+18446603487892193300
26.81% 0.00% random.h:151
26.81% 0.00% random.h:332
25.75% 25.75% random.h:143
5.64% 0.00% ??:0
4.13% 4.13% __hypot_finite+163
4.13% 0.00% __hypot_finite+18446603487892193443
...
~~~~~
Note that this change removes the symbol from the source:line hist
column. If this information is desired, users should explicitly query
for it if needed. I.e. run this command instead:
~~~~~
perf report --inline -s sym,srcline -g none --stdio
...
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 1K of event 'cycles:uppp'
# Event count (approx.): 1381229476
#
# Children Self Symbol Source:Line
# ........ ........ ................................................................................................................................... ...........................................
#
94.30% 1.19% [.] main main.cpp:39
94.23% 0.00% [.] __libc_start_main __libc_start_main+18446603487898210537
94.23% 0.00% [.] _start _start+41
48.44% 0.00% [.] std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > (inlined) random.h:1814
48.44% 0.00% [.] std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > (inlined) random.h:1823
46.74% 0.00% [.] std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator() (inlined) random.h:185
44.68% 0.00% [.] std::_Norm_helper<true>::_S_do_it<double> (inlined) complex:654
44.68% 0.00% [.] std::__complex_abs (inlined) complex:589
44.68% 0.00% [.] std::abs<double> (inlined) complex:597
44.68% 0.00% [.] std::norm<double> (inlined) complex:664
39.80% 13.59% [.] std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3330
36.01% 0.00% [.] hypot hypot+18446603487892193300
26.81% 0.00% [.] std::__detail::__mod<unsigned long, 2147483647ul, 16807ul, 0ul> (inlined) random.h:151
26.81% 0.00% [.] std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>::operator() (inlined) random.h:332
25.75% 0.00% [.] std::__detail::_Mod<unsigned long, 2147483647ul, 16807ul, 0ul, true, true>::__calc (inlined) random.h:143
25.19% 25.19% [.] std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:143
4.13% 4.13% [.] __hypot_finite __hypot_finite+163
4.13% 0.00% [.] __hypot_finite __hypot_finite+18446603487892193443
...
~~~~~
Compared to the old behavior, this reduces duplication in the output.
Before we used to print the symbol name in the srcline column even
when the sym column was explicitly requested. I.e. the output was:
~~~~~
perf report --inline -s sym,srcline -g none --stdio
...
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 1K of event 'cycles:uppp'
# Event count (approx.): 1381229476
#
# Children Self Symbol Source:Line
# ........ ........ ................................................................................................................................... ..................................................................................................................................
#
94.23% 0.00% [.] __libc_start_main __libc_start_main+18446603487898210537
94.23% 0.00% [.] _start _start+41
44.58% 0.00% [.] main main+100
44.58% 0.00% [.] std::_Norm_helper<true>::_S_do_it<double> (inlined) std::_Norm_helper<true>::_S_do_it<double>+100
44.58% 0.00% [.] std::__complex_abs (inlined) std::__complex_abs+100
44.58% 0.00% [.] std::abs<double> (inlined) std::abs<double>+100
44.58% 0.00% [.] std::norm<double> (inlined) std::norm<double>+100
36.01% 0.00% [.] hypot hypot+18446603487892193300
25.81% 0.00% [.] main main+41
25.81% 0.00% [.] std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator() (inlined) std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator()+41
25.81% 0.00% [.] std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > (inlined) std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> >+41
25.69% 25.69% [.] std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:143
18.39% 0.00% [.] main main+57
18.39% 0.00% [.] std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator() (inlined) std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator()+57
18.39% 0.00% [.] std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > (inlined) std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> >+57
13.80% 13.80% [.] std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3330
4.13% 4.13% [.] __hypot_finite __hypot_finite+163
4.13% 0.00% [.] __hypot_finite __hypot_finite+18446603487892193443
...
~~~~~
Signed-off-by: Milian Wolff <milian.wolff@kdab.com>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jin Yao <yao.jin@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20171019113836.5548-5-milian.wolff@kdab.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-10-19 18:38:35 +07:00
|
|
|
.srcline = al->srcline ? strdup(al->srcline) : NULL,
|
2015-09-04 21:45:42 +07:00
|
|
|
.socket = al->socket,
|
2014-05-27 23:28:05 +07:00
|
|
|
.cpu = al->cpu,
|
|
|
|
.cpumode = al->cpumode,
|
|
|
|
.ip = al->addr,
|
|
|
|
.level = al->level,
|
2012-10-04 19:49:41 +07:00
|
|
|
.stat = {
|
2012-10-04 19:49:42 +07:00
|
|
|
.nr_events = 1,
|
2015-12-23 00:06:58 +07:00
|
|
|
.period = sample->period,
|
|
|
|
.weight = sample->weight,
|
2012-10-04 19:49:41 +07:00
|
|
|
},
|
2012-02-10 05:21:01 +07:00
|
|
|
.parent = sym_parent,
|
2014-03-18 04:18:54 +07:00
|
|
|
.filtered = symbol__parent_filter(sym_parent) | al->filtered,
|
2013-10-23 05:01:31 +07:00
|
|
|
.hists = hists,
|
2013-10-31 13:56:03 +07:00
|
|
|
.branch_info = bi,
|
|
|
|
.mem_info = mi,
|
2015-12-23 00:06:58 +07:00
|
|
|
.transaction = sample->transaction,
|
2015-12-24 09:16:17 +07:00
|
|
|
.raw_data = sample->raw_data,
|
|
|
|
.raw_size = sample->raw_size,
|
2016-07-05 13:56:05 +07:00
|
|
|
.ops = ops,
|
2012-02-10 05:21:01 +07:00
|
|
|
};
|
|
|
|
|
2015-05-19 21:31:22 +07:00
|
|
|
return hists__findnew_entry(hists, &entry, al, sample_self);
|
2012-02-10 05:21:01 +07:00
|
|
|
}
|
|
|
|
|
2016-07-05 13:56:05 +07:00
|
|
|
struct hist_entry *hists__add_entry(struct hists *hists,
|
|
|
|
struct addr_location *al,
|
|
|
|
struct symbol *sym_parent,
|
|
|
|
struct branch_info *bi,
|
|
|
|
struct mem_info *mi,
|
|
|
|
struct perf_sample *sample,
|
|
|
|
bool sample_self)
|
|
|
|
{
|
|
|
|
return __hists__add_entry(hists, al, sym_parent, bi, mi,
|
|
|
|
sample, sample_self, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct hist_entry *hists__add_entry_ops(struct hists *hists,
|
|
|
|
struct hist_entry_ops *ops,
|
|
|
|
struct addr_location *al,
|
|
|
|
struct symbol *sym_parent,
|
|
|
|
struct branch_info *bi,
|
|
|
|
struct mem_info *mi,
|
|
|
|
struct perf_sample *sample,
|
|
|
|
bool sample_self)
|
|
|
|
{
|
|
|
|
return __hists__add_entry(hists, al, sym_parent, bi, mi,
|
|
|
|
sample, sample_self, ops);
|
|
|
|
}
|
|
|
|
|
2013-10-30 07:40:34 +07:00
|
|
|
static int
|
|
|
|
iter_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
|
|
|
|
struct addr_location *al __maybe_unused)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_add_next_nop_entry(struct hist_entry_iter *iter __maybe_unused,
|
|
|
|
struct addr_location *al __maybe_unused)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_prepare_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)
|
|
|
|
{
|
|
|
|
struct perf_sample *sample = iter->sample;
|
|
|
|
struct mem_info *mi;
|
|
|
|
|
|
|
|
mi = sample__resolve_mem(sample, al);
|
|
|
|
if (mi == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
iter->priv = mi;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_add_single_mem_entry(struct hist_entry_iter *iter, struct addr_location *al)
|
|
|
|
{
|
|
|
|
u64 cost;
|
|
|
|
struct mem_info *mi = iter->priv;
|
2014-10-09 23:13:41 +07:00
|
|
|
struct hists *hists = evsel__hists(iter->evsel);
|
2015-12-23 00:06:58 +07:00
|
|
|
struct perf_sample *sample = iter->sample;
|
2013-10-30 07:40:34 +07:00
|
|
|
struct hist_entry *he;
|
|
|
|
|
|
|
|
if (mi == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-12-23 00:06:58 +07:00
|
|
|
cost = sample->weight;
|
2013-10-30 07:40:34 +07:00
|
|
|
if (!cost)
|
|
|
|
cost = 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* must pass period=weight in order to get the correct
|
|
|
|
* sorting from hists__collapse_resort() which is solely
|
|
|
|
* based on periods. We want sorting be done on nr_events * weight
|
|
|
|
* and this is indirectly achieved by passing period=weight here
|
|
|
|
* and the he_stat__add_period() function.
|
|
|
|
*/
|
2015-12-23 00:06:58 +07:00
|
|
|
sample->period = cost;
|
|
|
|
|
2016-06-15 01:19:21 +07:00
|
|
|
he = hists__add_entry(hists, al, iter->parent, NULL, mi,
|
|
|
|
sample, true);
|
2013-10-30 07:40:34 +07:00
|
|
|
if (!he)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
iter->he = he;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-01-07 15:02:25 +07:00
|
|
|
iter_finish_mem_entry(struct hist_entry_iter *iter,
|
|
|
|
struct addr_location *al __maybe_unused)
|
2013-10-30 07:40:34 +07:00
|
|
|
{
|
|
|
|
struct perf_evsel *evsel = iter->evsel;
|
2014-10-09 23:13:41 +07:00
|
|
|
struct hists *hists = evsel__hists(evsel);
|
2013-10-30 07:40:34 +07:00
|
|
|
struct hist_entry *he = iter->he;
|
|
|
|
int err = -EINVAL;
|
|
|
|
|
|
|
|
if (he == NULL)
|
|
|
|
goto out;
|
|
|
|
|
2014-10-09 23:13:41 +07:00
|
|
|
hists__inc_nr_samples(hists, he->filtered);
|
2013-10-30 07:40:34 +07:00
|
|
|
|
|
|
|
err = hist_entry__append_callchain(he, iter->sample);
|
|
|
|
|
|
|
|
out:
|
|
|
|
/*
|
2015-05-19 21:31:22 +07:00
|
|
|
* We don't need to free iter->priv (mem_info) here since the mem info
|
|
|
|
* was either already freed in hists__findnew_entry() or passed to a
|
|
|
|
* new hist entry by hist_entry__new().
|
2013-10-30 07:40:34 +07:00
|
|
|
*/
|
|
|
|
iter->priv = NULL;
|
|
|
|
|
|
|
|
iter->he = NULL;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_prepare_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
|
|
|
|
{
|
|
|
|
struct branch_info *bi;
|
|
|
|
struct perf_sample *sample = iter->sample;
|
|
|
|
|
|
|
|
bi = sample__resolve_bstack(sample, al);
|
|
|
|
if (!bi)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
iter->curr = 0;
|
|
|
|
iter->total = sample->branch_stack->nr;
|
|
|
|
|
|
|
|
iter->priv = bi;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-07-18 19:13:14 +07:00
|
|
|
iter_add_single_branch_entry(struct hist_entry_iter *iter __maybe_unused,
|
2013-10-30 07:40:34 +07:00
|
|
|
struct addr_location *al __maybe_unused)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
|
|
|
|
{
|
|
|
|
struct branch_info *bi = iter->priv;
|
|
|
|
int i = iter->curr;
|
|
|
|
|
|
|
|
if (bi == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (iter->curr >= iter->total)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
al->map = bi[i].to.map;
|
|
|
|
al->sym = bi[i].to.sym;
|
|
|
|
al->addr = bi[i].to.addr;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_add_next_branch_entry(struct hist_entry_iter *iter, struct addr_location *al)
|
|
|
|
{
|
2014-01-07 15:02:25 +07:00
|
|
|
struct branch_info *bi;
|
2013-10-30 07:40:34 +07:00
|
|
|
struct perf_evsel *evsel = iter->evsel;
|
2014-10-09 23:13:41 +07:00
|
|
|
struct hists *hists = evsel__hists(evsel);
|
2015-12-23 00:06:58 +07:00
|
|
|
struct perf_sample *sample = iter->sample;
|
2013-10-30 07:40:34 +07:00
|
|
|
struct hist_entry *he = NULL;
|
|
|
|
int i = iter->curr;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
bi = iter->priv;
|
|
|
|
|
|
|
|
if (iter->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The report shows the percentage of total branches captured
|
|
|
|
* and not events sampled. Thus we use a pseudo period of 1.
|
|
|
|
*/
|
2015-12-23 00:06:58 +07:00
|
|
|
sample->period = 1;
|
|
|
|
sample->weight = bi->flags.cycles ? bi->flags.cycles : 1;
|
|
|
|
|
2016-06-15 01:19:21 +07:00
|
|
|
he = hists__add_entry(hists, al, iter->parent, &bi[i], NULL,
|
|
|
|
sample, true);
|
2013-10-30 07:40:34 +07:00
|
|
|
if (he == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-10-09 23:13:41 +07:00
|
|
|
hists__inc_nr_samples(hists, he->filtered);
|
2013-10-30 07:40:34 +07:00
|
|
|
|
|
|
|
out:
|
|
|
|
iter->he = he;
|
|
|
|
iter->curr++;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_finish_branch_entry(struct hist_entry_iter *iter,
|
|
|
|
struct addr_location *al __maybe_unused)
|
|
|
|
{
|
|
|
|
zfree(&iter->priv);
|
|
|
|
iter->he = NULL;
|
|
|
|
|
|
|
|
return iter->curr >= iter->total ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_prepare_normal_entry(struct hist_entry_iter *iter __maybe_unused,
|
|
|
|
struct addr_location *al __maybe_unused)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_add_single_normal_entry(struct hist_entry_iter *iter, struct addr_location *al)
|
|
|
|
{
|
|
|
|
struct perf_evsel *evsel = iter->evsel;
|
|
|
|
struct perf_sample *sample = iter->sample;
|
|
|
|
struct hist_entry *he;
|
|
|
|
|
2016-06-15 01:19:21 +07:00
|
|
|
he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
|
|
|
|
sample, true);
|
2013-10-30 07:40:34 +07:00
|
|
|
if (he == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
iter->he = he;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-01-07 15:02:25 +07:00
|
|
|
iter_finish_normal_entry(struct hist_entry_iter *iter,
|
|
|
|
struct addr_location *al __maybe_unused)
|
2013-10-30 07:40:34 +07:00
|
|
|
{
|
|
|
|
struct hist_entry *he = iter->he;
|
|
|
|
struct perf_evsel *evsel = iter->evsel;
|
|
|
|
struct perf_sample *sample = iter->sample;
|
|
|
|
|
|
|
|
if (he == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
iter->he = NULL;
|
|
|
|
|
2014-10-09 23:13:41 +07:00
|
|
|
hists__inc_nr_samples(evsel__hists(evsel), he->filtered);
|
2013-10-30 07:40:34 +07:00
|
|
|
|
|
|
|
return hist_entry__append_callchain(he, sample);
|
|
|
|
}
|
|
|
|
|
2012-09-11 12:13:04 +07:00
|
|
|
static int
|
2015-09-25 20:15:47 +07:00
|
|
|
iter_prepare_cumulative_entry(struct hist_entry_iter *iter,
|
2012-09-11 12:13:04 +07:00
|
|
|
struct addr_location *al __maybe_unused)
|
|
|
|
{
|
2013-10-31 08:05:29 +07:00
|
|
|
struct hist_entry **he_cache;
|
|
|
|
|
2012-09-11 12:13:04 +07:00
|
|
|
callchain_cursor_commit(&callchain_cursor);
|
2013-10-31 08:05:29 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is for detecting cycles or recursions so that they're
|
|
|
|
* cumulated only one time to prevent entries more than 100%
|
|
|
|
* overhead.
|
|
|
|
*/
|
2015-09-25 20:15:47 +07:00
|
|
|
he_cache = malloc(sizeof(*he_cache) * (iter->max_stack + 1));
|
2013-10-31 08:05:29 +07:00
|
|
|
if (he_cache == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
iter->priv = he_cache;
|
|
|
|
iter->curr = 0;
|
|
|
|
|
2012-09-11 12:13:04 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_add_single_cumulative_entry(struct hist_entry_iter *iter,
|
|
|
|
struct addr_location *al)
|
|
|
|
{
|
|
|
|
struct perf_evsel *evsel = iter->evsel;
|
2014-10-09 23:13:41 +07:00
|
|
|
struct hists *hists = evsel__hists(evsel);
|
2012-09-11 12:13:04 +07:00
|
|
|
struct perf_sample *sample = iter->sample;
|
2013-10-31 08:05:29 +07:00
|
|
|
struct hist_entry **he_cache = iter->priv;
|
2012-09-11 12:13:04 +07:00
|
|
|
struct hist_entry *he;
|
|
|
|
int err = 0;
|
|
|
|
|
2016-06-15 01:19:21 +07:00
|
|
|
he = hists__add_entry(hists, al, iter->parent, NULL, NULL,
|
|
|
|
sample, true);
|
2012-09-11 12:13:04 +07:00
|
|
|
if (he == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
iter->he = he;
|
2013-10-31 08:05:29 +07:00
|
|
|
he_cache[iter->curr++] = he;
|
2012-09-11 12:13:04 +07:00
|
|
|
|
2014-12-22 11:44:14 +07:00
|
|
|
hist_entry__append_callchain(he, sample);
|
2013-12-26 15:44:10 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to re-initialize the cursor since callchain_append()
|
|
|
|
* advanced the cursor to the end.
|
|
|
|
*/
|
|
|
|
callchain_cursor_commit(&callchain_cursor);
|
|
|
|
|
2014-10-09 23:13:41 +07:00
|
|
|
hists__inc_nr_samples(hists, he->filtered);
|
2012-09-11 12:13:04 +07:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_next_cumulative_entry(struct hist_entry_iter *iter,
|
|
|
|
struct addr_location *al)
|
|
|
|
{
|
|
|
|
struct callchain_cursor_node *node;
|
|
|
|
|
|
|
|
node = callchain_cursor_current(&callchain_cursor);
|
|
|
|
if (node == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2013-10-31 11:58:30 +07:00
|
|
|
return fill_callchain_info(al, node, iter->hide_unresolved);
|
2012-09-11 12:13:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
|
|
|
|
struct addr_location *al)
|
|
|
|
{
|
|
|
|
struct perf_evsel *evsel = iter->evsel;
|
|
|
|
struct perf_sample *sample = iter->sample;
|
2013-10-31 08:05:29 +07:00
|
|
|
struct hist_entry **he_cache = iter->priv;
|
2012-09-11 12:13:04 +07:00
|
|
|
struct hist_entry *he;
|
2013-10-31 08:05:29 +07:00
|
|
|
struct hist_entry he_tmp = {
|
2015-08-11 01:45:55 +07:00
|
|
|
.hists = evsel__hists(evsel),
|
2013-10-31 08:05:29 +07:00
|
|
|
.cpu = al->cpu,
|
|
|
|
.thread = al->thread,
|
|
|
|
.comm = thread__comm(al->thread),
|
|
|
|
.ip = al->addr,
|
|
|
|
.ms = {
|
|
|
|
.map = al->map,
|
|
|
|
.sym = al->sym,
|
|
|
|
},
|
perf report: Use srcline from callchain for hist entries
This also removes the symbol name from the srcline column, more on this
below.
This ensures we use the correct srcline, which could originate from a
potentially inlined function. The hist entries used to query for the
srcline based purely on the IP, which leads to wrong results for inlined
entries.
Before:
~~~~~
perf report --inline -s srcline -g none --stdio
...
# Children Self Source:Line
# ........ ........ ..................................................................................................................................
#
94.23% 0.00% __libc_start_main+18446603487898210537
94.23% 0.00% _start+41
44.58% 0.00% main+100
44.58% 0.00% std::_Norm_helper<true>::_S_do_it<double>+100
44.58% 0.00% std::__complex_abs+100
44.58% 0.00% std::abs<double>+100
44.58% 0.00% std::norm<double>+100
36.01% 0.00% hypot+18446603487892193300
25.81% 0.00% main+41
25.81% 0.00% std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator()+41
25.81% 0.00% std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> >+41
25.75% 25.75% random.h:143
18.39% 0.00% main+57
18.39% 0.00% std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator()+57
18.39% 0.00% std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> >+57
13.80% 13.80% random.tcc:3330
5.64% 0.00% ??:0
4.13% 4.13% __hypot_finite+163
4.13% 0.00% __hypot_finite+18446603487892193443
...
~~~~~
After:
~~~~~
perf report --inline -s srcline -g none --stdio
...
# Children Self Source:Line
# ........ ........ ...........................................
#
94.30% 1.19% main.cpp:39
94.23% 0.00% __libc_start_main+18446603487898210537
94.23% 0.00% _start+41
48.44% 1.70% random.h:1823
48.44% 0.00% random.h:1814
46.74% 2.53% random.h:185
44.68% 0.10% complex:589
44.68% 0.00% complex:597
44.68% 0.00% complex:654
44.68% 0.00% complex:664
40.61% 13.80% random.tcc:3330
36.01% 0.00% hypot+18446603487892193300
26.81% 0.00% random.h:151
26.81% 0.00% random.h:332
25.75% 25.75% random.h:143
5.64% 0.00% ??:0
4.13% 4.13% __hypot_finite+163
4.13% 0.00% __hypot_finite+18446603487892193443
...
~~~~~
Note that this change removes the symbol from the source:line hist
column. If this information is desired, users should explicitly query
for it if needed. I.e. run this command instead:
~~~~~
perf report --inline -s sym,srcline -g none --stdio
...
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 1K of event 'cycles:uppp'
# Event count (approx.): 1381229476
#
# Children Self Symbol Source:Line
# ........ ........ ................................................................................................................................... ...........................................
#
94.30% 1.19% [.] main main.cpp:39
94.23% 0.00% [.] __libc_start_main __libc_start_main+18446603487898210537
94.23% 0.00% [.] _start _start+41
48.44% 0.00% [.] std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > (inlined) random.h:1814
48.44% 0.00% [.] std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > (inlined) random.h:1823
46.74% 0.00% [.] std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator() (inlined) random.h:185
44.68% 0.00% [.] std::_Norm_helper<true>::_S_do_it<double> (inlined) complex:654
44.68% 0.00% [.] std::__complex_abs (inlined) complex:589
44.68% 0.00% [.] std::abs<double> (inlined) complex:597
44.68% 0.00% [.] std::norm<double> (inlined) complex:664
39.80% 13.59% [.] std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3330
36.01% 0.00% [.] hypot hypot+18446603487892193300
26.81% 0.00% [.] std::__detail::__mod<unsigned long, 2147483647ul, 16807ul, 0ul> (inlined) random.h:151
26.81% 0.00% [.] std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>::operator() (inlined) random.h:332
25.75% 0.00% [.] std::__detail::_Mod<unsigned long, 2147483647ul, 16807ul, 0ul, true, true>::__calc (inlined) random.h:143
25.19% 25.19% [.] std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:143
4.13% 4.13% [.] __hypot_finite __hypot_finite+163
4.13% 0.00% [.] __hypot_finite __hypot_finite+18446603487892193443
...
~~~~~
Compared to the old behavior, this reduces duplication in the output.
Before we used to print the symbol name in the srcline column even
when the sym column was explicitly requested. I.e. the output was:
~~~~~
perf report --inline -s sym,srcline -g none --stdio
...
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 1K of event 'cycles:uppp'
# Event count (approx.): 1381229476
#
# Children Self Symbol Source:Line
# ........ ........ ................................................................................................................................... ..................................................................................................................................
#
94.23% 0.00% [.] __libc_start_main __libc_start_main+18446603487898210537
94.23% 0.00% [.] _start _start+41
44.58% 0.00% [.] main main+100
44.58% 0.00% [.] std::_Norm_helper<true>::_S_do_it<double> (inlined) std::_Norm_helper<true>::_S_do_it<double>+100
44.58% 0.00% [.] std::__complex_abs (inlined) std::__complex_abs+100
44.58% 0.00% [.] std::abs<double> (inlined) std::abs<double>+100
44.58% 0.00% [.] std::norm<double> (inlined) std::norm<double>+100
36.01% 0.00% [.] hypot hypot+18446603487892193300
25.81% 0.00% [.] main main+41
25.81% 0.00% [.] std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator() (inlined) std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator()+41
25.81% 0.00% [.] std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > (inlined) std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> >+41
25.69% 25.69% [.] std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:143
18.39% 0.00% [.] main main+57
18.39% 0.00% [.] std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator() (inlined) std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator()+57
18.39% 0.00% [.] std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > (inlined) std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> >+57
13.80% 13.80% [.] std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3330
4.13% 4.13% [.] __hypot_finite __hypot_finite+163
4.13% 0.00% [.] __hypot_finite __hypot_finite+18446603487892193443
...
~~~~~
Signed-off-by: Milian Wolff <milian.wolff@kdab.com>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jin Yao <yao.jin@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20171019113836.5548-5-milian.wolff@kdab.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-10-19 18:38:35 +07:00
|
|
|
.srcline = al->srcline ? strdup(al->srcline) : NULL,
|
2013-10-31 08:05:29 +07:00
|
|
|
.parent = iter->parent,
|
2015-12-24 09:16:17 +07:00
|
|
|
.raw_data = sample->raw_data,
|
|
|
|
.raw_size = sample->raw_size,
|
2013-10-31 08:05:29 +07:00
|
|
|
};
|
|
|
|
int i;
|
2013-12-26 15:44:10 +07:00
|
|
|
struct callchain_cursor cursor;
|
|
|
|
|
|
|
|
callchain_cursor_snapshot(&cursor, &callchain_cursor);
|
|
|
|
|
|
|
|
callchain_cursor_advance(&callchain_cursor);
|
2013-10-31 08:05:29 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if there's duplicate entries in the callchain.
|
|
|
|
* It's possible that it has cycles or recursive calls.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < iter->curr; i++) {
|
2014-01-07 15:02:25 +07:00
|
|
|
if (hist_entry__cmp(he_cache[i], &he_tmp) == 0) {
|
|
|
|
/* to avoid calling callback function */
|
|
|
|
iter->he = NULL;
|
2013-10-31 08:05:29 +07:00
|
|
|
return 0;
|
2014-01-07 15:02:25 +07:00
|
|
|
}
|
2013-10-31 08:05:29 +07:00
|
|
|
}
|
2012-09-11 12:13:04 +07:00
|
|
|
|
2016-06-15 01:19:21 +07:00
|
|
|
he = hists__add_entry(evsel__hists(evsel), al, iter->parent, NULL, NULL,
|
|
|
|
sample, false);
|
2012-09-11 12:13:04 +07:00
|
|
|
if (he == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
iter->he = he;
|
2013-10-31 08:05:29 +07:00
|
|
|
he_cache[iter->curr++] = he;
|
2012-09-11 12:13:04 +07:00
|
|
|
|
2014-12-22 11:44:14 +07:00
|
|
|
if (symbol_conf.use_callchain)
|
|
|
|
callchain_append(he->callchain, &cursor, sample->period);
|
2012-09-11 12:13:04 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iter_finish_cumulative_entry(struct hist_entry_iter *iter,
|
|
|
|
struct addr_location *al __maybe_unused)
|
|
|
|
{
|
2013-10-31 08:05:29 +07:00
|
|
|
zfree(&iter->priv);
|
2012-09-11 12:13:04 +07:00
|
|
|
iter->he = NULL;
|
2013-10-31 08:05:29 +07:00
|
|
|
|
2012-09-11 12:13:04 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-30 07:40:34 +07:00
|
|
|
const struct hist_iter_ops hist_iter_mem = {
|
|
|
|
.prepare_entry = iter_prepare_mem_entry,
|
|
|
|
.add_single_entry = iter_add_single_mem_entry,
|
|
|
|
.next_entry = iter_next_nop_entry,
|
|
|
|
.add_next_entry = iter_add_next_nop_entry,
|
|
|
|
.finish_entry = iter_finish_mem_entry,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct hist_iter_ops hist_iter_branch = {
|
|
|
|
.prepare_entry = iter_prepare_branch_entry,
|
|
|
|
.add_single_entry = iter_add_single_branch_entry,
|
|
|
|
.next_entry = iter_next_branch_entry,
|
|
|
|
.add_next_entry = iter_add_next_branch_entry,
|
|
|
|
.finish_entry = iter_finish_branch_entry,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct hist_iter_ops hist_iter_normal = {
|
|
|
|
.prepare_entry = iter_prepare_normal_entry,
|
|
|
|
.add_single_entry = iter_add_single_normal_entry,
|
|
|
|
.next_entry = iter_next_nop_entry,
|
|
|
|
.add_next_entry = iter_add_next_nop_entry,
|
|
|
|
.finish_entry = iter_finish_normal_entry,
|
|
|
|
};
|
|
|
|
|
2012-09-11 12:13:04 +07:00
|
|
|
const struct hist_iter_ops hist_iter_cumulative = {
|
|
|
|
.prepare_entry = iter_prepare_cumulative_entry,
|
|
|
|
.add_single_entry = iter_add_single_cumulative_entry,
|
|
|
|
.next_entry = iter_next_cumulative_entry,
|
|
|
|
.add_next_entry = iter_add_next_cumulative_entry,
|
|
|
|
.finish_entry = iter_finish_cumulative_entry,
|
|
|
|
};
|
|
|
|
|
2013-10-30 07:40:34 +07:00
|
|
|
int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
|
2014-01-07 15:02:25 +07:00
|
|
|
int max_stack_depth, void *arg)
|
2013-10-30 07:40:34 +07:00
|
|
|
{
|
|
|
|
int err, err2;
|
2017-01-06 13:23:31 +07:00
|
|
|
struct map *alm = NULL;
|
|
|
|
|
|
|
|
if (al && al->map)
|
|
|
|
alm = map__get(al->map);
|
2013-10-30 07:40:34 +07:00
|
|
|
|
2016-04-15 00:48:07 +07:00
|
|
|
err = sample__resolve_callchain(iter->sample, &callchain_cursor, &iter->parent,
|
2015-05-19 15:04:10 +07:00
|
|
|
iter->evsel, al, max_stack_depth);
|
2013-10-30 07:40:34 +07:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2015-09-25 20:15:47 +07:00
|
|
|
iter->max_stack = max_stack_depth;
|
|
|
|
|
2013-10-30 07:40:34 +07:00
|
|
|
err = iter->ops->prepare_entry(iter, al);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = iter->ops->add_single_entry(iter, al);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2014-01-07 15:02:25 +07:00
|
|
|
if (iter->he && iter->add_entry_cb) {
|
|
|
|
err = iter->add_entry_cb(iter, al, true, arg);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2013-10-30 07:40:34 +07:00
|
|
|
while (iter->ops->next_entry(iter, al)) {
|
|
|
|
err = iter->ops->add_next_entry(iter, al);
|
|
|
|
if (err)
|
|
|
|
break;
|
2014-01-07 15:02:25 +07:00
|
|
|
|
|
|
|
if (iter->he && iter->add_entry_cb) {
|
|
|
|
err = iter->add_entry_cb(iter, al, false, arg);
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
2013-10-30 07:40:34 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
err2 = iter->ops->finish_entry(iter, al);
|
|
|
|
if (!err)
|
|
|
|
err = err2;
|
|
|
|
|
2017-01-06 13:23:31 +07:00
|
|
|
map__put(alm);
|
|
|
|
|
2013-10-30 07:40:34 +07:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2009-09-28 20:32:55 +07:00
|
|
|
int64_t
|
|
|
|
hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
|
|
|
|
{
|
2016-01-18 16:24:24 +07:00
|
|
|
struct hists *hists = left->hists;
|
2014-03-03 10:07:47 +07:00
|
|
|
struct perf_hpp_fmt *fmt;
|
2009-09-28 20:32:55 +07:00
|
|
|
int64_t cmp = 0;
|
|
|
|
|
2016-01-18 16:24:24 +07:00
|
|
|
hists__for_each_sort_list(hists, fmt) {
|
2016-02-27 01:52:43 +07:00
|
|
|
if (perf_hpp__is_dynamic_entry(fmt) &&
|
|
|
|
!perf_hpp__defined_dynamic_entry(fmt, hists))
|
|
|
|
continue;
|
|
|
|
|
2015-01-08 07:45:46 +07:00
|
|
|
cmp = fmt->cmp(fmt, left, right);
|
2009-09-28 20:32:55 +07:00
|
|
|
if (cmp)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t
|
|
|
|
hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)
|
|
|
|
{
|
2016-01-18 16:24:24 +07:00
|
|
|
struct hists *hists = left->hists;
|
2014-03-03 10:07:47 +07:00
|
|
|
struct perf_hpp_fmt *fmt;
|
2009-09-28 20:32:55 +07:00
|
|
|
int64_t cmp = 0;
|
|
|
|
|
2016-01-18 16:24:24 +07:00
|
|
|
hists__for_each_sort_list(hists, fmt) {
|
2016-02-27 01:52:43 +07:00
|
|
|
if (perf_hpp__is_dynamic_entry(fmt) &&
|
|
|
|
!perf_hpp__defined_dynamic_entry(fmt, hists))
|
|
|
|
continue;
|
|
|
|
|
2015-01-08 07:45:46 +07:00
|
|
|
cmp = fmt->collapse(fmt, left, right);
|
2009-09-28 20:32:55 +07:00
|
|
|
if (cmp)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return cmp;
|
|
|
|
}
|
|
|
|
|
2014-12-19 22:31:40 +07:00
|
|
|
void hist_entry__delete(struct hist_entry *he)
|
2009-09-28 20:32:55 +07:00
|
|
|
{
|
2016-07-05 13:56:04 +07:00
|
|
|
struct hist_entry_ops *ops = he->ops;
|
|
|
|
|
2015-03-03 08:21:35 +07:00
|
|
|
thread__zput(he->thread);
|
2015-06-16 09:29:51 +07:00
|
|
|
map__zput(he->ms.map);
|
|
|
|
|
|
|
|
if (he->branch_info) {
|
|
|
|
map__zput(he->branch_info->from.map);
|
|
|
|
map__zput(he->branch_info->to.map);
|
2016-05-21 03:15:08 +07:00
|
|
|
free_srcline(he->branch_info->srcline_from);
|
|
|
|
free_srcline(he->branch_info->srcline_to);
|
2015-06-16 09:29:51 +07:00
|
|
|
zfree(&he->branch_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (he->mem_info) {
|
|
|
|
map__zput(he->mem_info->iaddr.map);
|
|
|
|
map__zput(he->mem_info->daddr.map);
|
|
|
|
zfree(&he->mem_info);
|
|
|
|
}
|
|
|
|
|
2012-09-11 11:15:07 +07:00
|
|
|
zfree(&he->stat_acc);
|
2013-09-11 12:09:28 +07:00
|
|
|
free_srcline(he->srcline);
|
2015-08-08 05:54:24 +07:00
|
|
|
if (he->srcfile && he->srcfile[0])
|
|
|
|
free(he->srcfile);
|
2014-12-30 12:38:13 +07:00
|
|
|
free_callchain(he->callchain);
|
2015-12-23 00:07:03 +07:00
|
|
|
free(he->trace_output);
|
2015-12-24 09:16:17 +07:00
|
|
|
free(he->raw_data);
|
2016-07-05 13:56:04 +07:00
|
|
|
ops->free(he);
|
2009-09-28 20:32:55 +07:00
|
|
|
}
|
|
|
|
|
perf hists: Do column alignment on the format iterator
We were doing column alignment in the format function for each cell,
returning a string padded with spaces so that when the next column is
printed the cursor is at its column alignment.
This ends up needlessly printing trailing spaces, do it at the format
iterator, that is where we know if it is needed, i.e. if there is more
columns to be printed.
This eliminates the need for triming lines when doing a dump using 'P'
in the TUI browser and also produces far saner results with things like
piping 'perf report' to 'less'.
Right now only the formatters for sym->name and the 'locked' column
(perf mem report), that are the ones that end up at the end of lines
in the default 'perf report', 'perf top' and 'perf mem report' tools,
the others will be done in a subsequent patch.
In the end the 'width' parameter for the formatters now mean, in
'printf' terms, the 'precision', where before it was the field 'width'.
Reported-by: Dave Jones <davej@codemonkey.org.uk>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/n/tip-s7iwl2gj23w92l6tibnrcqzr@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-02-12 03:14:13 +07:00
|
|
|
/*
|
|
|
|
* If this is not the last column, then we need to pad it according to the
|
|
|
|
* pre-calculated max lenght for this column, otherwise don't bother adding
|
|
|
|
* spaces because that would break viewing this with, for instance, 'less',
|
|
|
|
* that would show tons of trailing spaces when a long C++ demangled method
|
|
|
|
* names is sampled.
|
|
|
|
*/
|
|
|
|
int hist_entry__snprintf_alignment(struct hist_entry *he, struct perf_hpp *hpp,
|
|
|
|
struct perf_hpp_fmt *fmt, int printed)
|
|
|
|
{
|
|
|
|
if (!list_is_last(&fmt->list, &he->hists->hpp_list->fields)) {
|
2016-06-15 01:19:20 +07:00
|
|
|
const int width = fmt->width(fmt, hpp, he->hists);
|
perf hists: Do column alignment on the format iterator
We were doing column alignment in the format function for each cell,
returning a string padded with spaces so that when the next column is
printed the cursor is at its column alignment.
This ends up needlessly printing trailing spaces, do it at the format
iterator, that is where we know if it is needed, i.e. if there is more
columns to be printed.
This eliminates the need for triming lines when doing a dump using 'P'
in the TUI browser and also produces far saner results with things like
piping 'perf report' to 'less'.
Right now only the formatters for sym->name and the 'locked' column
(perf mem report), that are the ones that end up at the end of lines
in the default 'perf report', 'perf top' and 'perf mem report' tools,
the others will be done in a subsequent patch.
In the end the 'width' parameter for the formatters now mean, in
'printf' terms, the 'precision', where before it was the field 'width'.
Reported-by: Dave Jones <davej@codemonkey.org.uk>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/n/tip-s7iwl2gj23w92l6tibnrcqzr@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-02-12 03:14:13 +07:00
|
|
|
if (printed < width) {
|
|
|
|
advance_hpp(hpp, printed);
|
|
|
|
printed = scnprintf(hpp->buf, hpp->size, "%-*s", width - printed, " ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
2009-09-28 20:32:55 +07:00
|
|
|
/*
|
|
|
|
* collapse the histogram
|
|
|
|
*/
|
|
|
|
|
2016-02-24 22:13:34 +07:00
|
|
|
static void hists__apply_filters(struct hists *hists, struct hist_entry *he);
|
2016-03-09 20:46:58 +07:00
|
|
|
static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *he,
|
|
|
|
enum hist_filter type);
|
|
|
|
|
|
|
|
typedef bool (*fmt_chk_fn)(struct perf_hpp_fmt *fmt);
|
|
|
|
|
|
|
|
static bool check_thread_entry(struct perf_hpp_fmt *fmt)
|
|
|
|
{
|
|
|
|
return perf_hpp__is_thread_entry(fmt) || perf_hpp__is_comm_entry(fmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hist_entry__check_and_remove_filter(struct hist_entry *he,
|
|
|
|
enum hist_filter type,
|
|
|
|
fmt_chk_fn check)
|
|
|
|
{
|
|
|
|
struct perf_hpp_fmt *fmt;
|
|
|
|
bool type_match = false;
|
|
|
|
struct hist_entry *parent = he->parent_he;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case HIST_FILTER__THREAD:
|
|
|
|
if (symbol_conf.comm_list == NULL &&
|
|
|
|
symbol_conf.pid_list == NULL &&
|
|
|
|
symbol_conf.tid_list == NULL)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
case HIST_FILTER__DSO:
|
|
|
|
if (symbol_conf.dso_list == NULL)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
case HIST_FILTER__SYMBOL:
|
|
|
|
if (symbol_conf.sym_list == NULL)
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
case HIST_FILTER__PARENT:
|
|
|
|
case HIST_FILTER__GUEST:
|
|
|
|
case HIST_FILTER__HOST:
|
|
|
|
case HIST_FILTER__SOCKET:
|
2016-08-17 19:55:23 +07:00
|
|
|
case HIST_FILTER__C2C:
|
2016-03-09 20:46:58 +07:00
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if it's filtered by own fmt, it has to have filter bits */
|
|
|
|
perf_hpp_list__for_each_format(he->hpp_list, fmt) {
|
|
|
|
if (check(fmt)) {
|
|
|
|
type_match = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type_match) {
|
|
|
|
/*
|
|
|
|
* If the filter is for current level entry, propagate
|
|
|
|
* filter marker to parents. The marker bit was
|
|
|
|
* already set by default so it only needs to clear
|
|
|
|
* non-filtered entries.
|
|
|
|
*/
|
|
|
|
if (!(he->filtered & (1 << type))) {
|
|
|
|
while (parent) {
|
|
|
|
parent->filtered &= ~(1 << type);
|
|
|
|
parent = parent->parent_he;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If current entry doesn't have matching formats, set
|
|
|
|
* filter marker for upper level entries. it will be
|
|
|
|
* cleared if its lower level entries is not filtered.
|
|
|
|
*
|
|
|
|
* For lower-level entries, it inherits parent's
|
|
|
|
* filter bit so that lower level entries of a
|
|
|
|
* non-filtered entry won't set the filter marker.
|
|
|
|
*/
|
|
|
|
if (parent == NULL)
|
|
|
|
he->filtered |= (1 << type);
|
|
|
|
else
|
|
|
|
he->filtered |= (parent->filtered & (1 << type));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hist_entry__apply_hierarchy_filters(struct hist_entry *he)
|
|
|
|
{
|
|
|
|
hist_entry__check_and_remove_filter(he, HIST_FILTER__THREAD,
|
|
|
|
check_thread_entry);
|
|
|
|
|
|
|
|
hist_entry__check_and_remove_filter(he, HIST_FILTER__DSO,
|
|
|
|
perf_hpp__is_dso_entry);
|
|
|
|
|
|
|
|
hist_entry__check_and_remove_filter(he, HIST_FILTER__SYMBOL,
|
|
|
|
perf_hpp__is_sym_entry);
|
|
|
|
|
|
|
|
hists__apply_filters(he->hists, he);
|
|
|
|
}
|
2016-02-24 22:13:34 +07:00
|
|
|
|
|
|
|
static struct hist_entry *hierarchy_insert_entry(struct hists *hists,
|
|
|
|
struct rb_root *root,
|
|
|
|
struct hist_entry *he,
|
2016-03-09 20:46:58 +07:00
|
|
|
struct hist_entry *parent_he,
|
2016-03-08 02:44:46 +07:00
|
|
|
struct perf_hpp_list *hpp_list)
|
2016-02-24 22:13:34 +07:00
|
|
|
{
|
|
|
|
struct rb_node **p = &root->rb_node;
|
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *iter, *new;
|
2016-03-08 02:44:46 +07:00
|
|
|
struct perf_hpp_fmt *fmt;
|
2016-02-24 22:13:34 +07:00
|
|
|
int64_t cmp;
|
|
|
|
|
|
|
|
while (*p != NULL) {
|
|
|
|
parent = *p;
|
|
|
|
iter = rb_entry(parent, struct hist_entry, rb_node_in);
|
|
|
|
|
2016-03-08 02:44:46 +07:00
|
|
|
cmp = 0;
|
|
|
|
perf_hpp_list__for_each_sort_list(hpp_list, fmt) {
|
|
|
|
cmp = fmt->collapse(fmt, iter, he);
|
|
|
|
if (cmp)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-02-24 22:13:34 +07:00
|
|
|
if (!cmp) {
|
|
|
|
he_stat__add_stat(&iter->stat, &he->stat);
|
|
|
|
return iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmp < 0)
|
|
|
|
p = &parent->rb_left;
|
|
|
|
else
|
|
|
|
p = &parent->rb_right;
|
|
|
|
}
|
|
|
|
|
|
|
|
new = hist_entry__new(he, true);
|
|
|
|
if (new == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
hists->nr_entries++;
|
|
|
|
|
2016-03-08 02:44:46 +07:00
|
|
|
/* save related format list for output */
|
|
|
|
new->hpp_list = hpp_list;
|
2016-03-09 20:46:58 +07:00
|
|
|
new->parent_he = parent_he;
|
|
|
|
|
|
|
|
hist_entry__apply_hierarchy_filters(new);
|
2016-02-24 22:13:34 +07:00
|
|
|
|
|
|
|
/* some fields are now passed to 'new' */
|
2016-03-08 02:44:46 +07:00
|
|
|
perf_hpp_list__for_each_sort_list(hpp_list, fmt) {
|
|
|
|
if (perf_hpp__is_trace_entry(fmt) || perf_hpp__is_dynamic_entry(fmt))
|
|
|
|
he->trace_output = NULL;
|
|
|
|
else
|
|
|
|
new->trace_output = NULL;
|
2016-02-24 22:13:34 +07:00
|
|
|
|
2016-03-08 02:44:46 +07:00
|
|
|
if (perf_hpp__is_srcline_entry(fmt))
|
|
|
|
he->srcline = NULL;
|
|
|
|
else
|
|
|
|
new->srcline = NULL;
|
2016-02-24 22:13:34 +07:00
|
|
|
|
2016-03-08 02:44:46 +07:00
|
|
|
if (perf_hpp__is_srcfile_entry(fmt))
|
|
|
|
he->srcfile = NULL;
|
|
|
|
else
|
|
|
|
new->srcfile = NULL;
|
|
|
|
}
|
2016-02-24 22:13:34 +07:00
|
|
|
|
|
|
|
rb_link_node(&new->rb_node_in, parent, p);
|
|
|
|
rb_insert_color(&new->rb_node_in, root);
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hists__hierarchy_insert_entry(struct hists *hists,
|
|
|
|
struct rb_root *root,
|
|
|
|
struct hist_entry *he)
|
|
|
|
{
|
2016-03-08 02:44:46 +07:00
|
|
|
struct perf_hpp_list_node *node;
|
2016-02-24 22:13:34 +07:00
|
|
|
struct hist_entry *new_he = NULL;
|
|
|
|
struct hist_entry *parent = NULL;
|
|
|
|
int depth = 0;
|
|
|
|
int ret = 0;
|
|
|
|
|
2016-03-08 02:44:46 +07:00
|
|
|
list_for_each_entry(node, &hists->hpp_formats, list) {
|
|
|
|
/* skip period (overhead) and elided columns */
|
|
|
|
if (node->level == 0 || node->skip)
|
2016-02-24 22:13:34 +07:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/* insert copy of 'he' for each fmt into the hierarchy */
|
2016-03-09 20:46:58 +07:00
|
|
|
new_he = hierarchy_insert_entry(hists, root, he, parent, &node->hpp);
|
2016-02-24 22:13:34 +07:00
|
|
|
if (new_he == NULL) {
|
|
|
|
ret = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
root = &new_he->hroot_in;
|
|
|
|
new_he->depth = depth++;
|
|
|
|
parent = new_he;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (new_he) {
|
|
|
|
new_he->leaf = true;
|
|
|
|
|
|
|
|
if (symbol_conf.use_callchain) {
|
|
|
|
callchain_cursor_reset(&callchain_cursor);
|
|
|
|
if (callchain_merge(&callchain_cursor,
|
|
|
|
new_he->callchain,
|
|
|
|
he->callchain) < 0)
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 'he' is no longer used */
|
|
|
|
hist_entry__delete(he);
|
|
|
|
|
|
|
|
/* return 0 (or -1) since it already applied filters */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-03-24 19:52:17 +07:00
|
|
|
static int hists__collapse_insert_entry(struct hists *hists,
|
|
|
|
struct rb_root *root,
|
|
|
|
struct hist_entry *he)
|
2009-09-28 20:32:55 +07:00
|
|
|
{
|
2009-12-14 20:37:11 +07:00
|
|
|
struct rb_node **p = &root->rb_node;
|
2009-09-28 20:32:55 +07:00
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *iter;
|
|
|
|
int64_t cmp;
|
|
|
|
|
2016-02-24 22:13:34 +07:00
|
|
|
if (symbol_conf.report_hierarchy)
|
|
|
|
return hists__hierarchy_insert_entry(hists, root, he);
|
|
|
|
|
2009-09-28 20:32:55 +07:00
|
|
|
while (*p != NULL) {
|
|
|
|
parent = *p;
|
2011-10-06 03:50:23 +07:00
|
|
|
iter = rb_entry(parent, struct hist_entry, rb_node_in);
|
2009-09-28 20:32:55 +07:00
|
|
|
|
|
|
|
cmp = hist_entry__collapse(iter, he);
|
|
|
|
|
|
|
|
if (!cmp) {
|
2016-02-16 21:08:25 +07:00
|
|
|
int ret = 0;
|
|
|
|
|
2012-10-04 19:49:43 +07:00
|
|
|
he_stat__add_stat(&iter->stat, &he->stat);
|
2012-09-11 11:15:07 +07:00
|
|
|
if (symbol_conf.cumulate_callchain)
|
|
|
|
he_stat__add_stat(iter->stat_acc, he->stat_acc);
|
2012-09-26 14:47:28 +07:00
|
|
|
|
2011-01-14 10:51:58 +07:00
|
|
|
if (symbol_conf.use_callchain) {
|
2012-05-31 12:43:26 +07:00
|
|
|
callchain_cursor_reset(&callchain_cursor);
|
2016-02-16 21:08:25 +07:00
|
|
|
if (callchain_merge(&callchain_cursor,
|
|
|
|
iter->callchain,
|
|
|
|
he->callchain) < 0)
|
|
|
|
ret = -1;
|
2011-01-14 10:51:58 +07:00
|
|
|
}
|
2014-12-19 22:31:40 +07:00
|
|
|
hist_entry__delete(he);
|
2016-02-16 21:08:25 +07:00
|
|
|
return ret;
|
2009-09-28 20:32:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cmp < 0)
|
|
|
|
p = &(*p)->rb_left;
|
|
|
|
else
|
|
|
|
p = &(*p)->rb_right;
|
|
|
|
}
|
2014-12-22 11:44:10 +07:00
|
|
|
hists->nr_entries++;
|
2009-09-28 20:32:55 +07:00
|
|
|
|
2011-10-06 03:50:23 +07:00
|
|
|
rb_link_node(&he->rb_node_in, parent, p);
|
|
|
|
rb_insert_color(&he->rb_node_in, root);
|
2016-02-16 21:08:25 +07:00
|
|
|
return 1;
|
2009-09-28 20:32:55 +07:00
|
|
|
}
|
|
|
|
|
2016-01-07 16:14:10 +07:00
|
|
|
struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
|
2009-09-28 20:32:55 +07:00
|
|
|
{
|
2011-10-06 03:50:23 +07:00
|
|
|
struct rb_root *root;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&hists->lock);
|
|
|
|
|
|
|
|
root = hists->entries_in;
|
|
|
|
if (++hists->entries_in > &hists->entries_in_array[1])
|
|
|
|
hists->entries_in = &hists->entries_in_array[0];
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&hists->lock);
|
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2011-10-19 22:09:10 +07:00
|
|
|
static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
|
|
|
|
{
|
|
|
|
hists__filter_entry_by_dso(hists, he);
|
|
|
|
hists__filter_entry_by_thread(hists, he);
|
2012-03-16 15:50:51 +07:00
|
|
|
hists__filter_entry_by_symbol(hists, he);
|
2015-09-04 21:45:44 +07:00
|
|
|
hists__filter_entry_by_socket(hists, he);
|
2011-10-19 22:09:10 +07:00
|
|
|
}
|
|
|
|
|
2016-02-16 21:08:25 +07:00
|
|
|
int hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
|
2011-10-06 03:50:23 +07:00
|
|
|
{
|
|
|
|
struct rb_root *root;
|
2009-09-28 20:32:55 +07:00
|
|
|
struct rb_node *next;
|
|
|
|
struct hist_entry *n;
|
2016-02-16 21:08:25 +07:00
|
|
|
int ret;
|
2009-09-28 20:32:55 +07:00
|
|
|
|
2016-05-03 18:54:42 +07:00
|
|
|
if (!hists__has(hists, need_collapse))
|
2016-02-16 21:08:25 +07:00
|
|
|
return 0;
|
2009-09-28 20:32:55 +07:00
|
|
|
|
2014-12-22 11:44:10 +07:00
|
|
|
hists->nr_entries = 0;
|
|
|
|
|
2011-10-06 03:50:23 +07:00
|
|
|
root = hists__get_rotate_entries_in(hists);
|
2014-12-22 11:44:10 +07:00
|
|
|
|
2011-10-06 03:50:23 +07:00
|
|
|
next = rb_first(root);
|
2009-12-14 20:37:11 +07:00
|
|
|
|
2009-09-28 20:32:55 +07:00
|
|
|
while (next) {
|
2013-09-18 02:34:28 +07:00
|
|
|
if (session_done())
|
|
|
|
break;
|
2011-10-06 03:50:23 +07:00
|
|
|
n = rb_entry(next, struct hist_entry, rb_node_in);
|
|
|
|
next = rb_next(&n->rb_node_in);
|
2009-09-28 20:32:55 +07:00
|
|
|
|
2011-10-06 03:50:23 +07:00
|
|
|
rb_erase(&n->rb_node_in, root);
|
2016-02-16 21:08:25 +07:00
|
|
|
ret = hists__collapse_insert_entry(hists, &hists->entries_collapsed, n);
|
|
|
|
if (ret < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (ret) {
|
2011-10-19 22:09:10 +07:00
|
|
|
/*
|
|
|
|
* If it wasn't combined with one of the entries already
|
|
|
|
* collapsed, we need to apply the filters that may have
|
|
|
|
* been set by, say, the hist_browser.
|
|
|
|
*/
|
|
|
|
hists__apply_filters(hists, n);
|
|
|
|
}
|
2013-10-11 12:15:38 +07:00
|
|
|
if (prog)
|
|
|
|
ui_progress__update(prog, 1);
|
2009-09-28 20:32:55 +07:00
|
|
|
}
|
2016-02-16 21:08:25 +07:00
|
|
|
return 0;
|
2011-10-06 03:50:23 +07:00
|
|
|
}
|
2009-12-14 20:37:11 +07:00
|
|
|
|
2014-03-03 12:18:00 +07:00
|
|
|
static int hist_entry__sort(struct hist_entry *a, struct hist_entry *b)
|
2013-01-22 16:09:33 +07:00
|
|
|
{
|
2016-01-18 16:24:24 +07:00
|
|
|
struct hists *hists = a->hists;
|
2014-03-03 12:18:00 +07:00
|
|
|
struct perf_hpp_fmt *fmt;
|
|
|
|
int64_t cmp = 0;
|
2013-01-22 16:09:33 +07:00
|
|
|
|
2016-01-18 16:24:24 +07:00
|
|
|
hists__for_each_sort_list(hists, fmt) {
|
2015-12-23 00:07:08 +07:00
|
|
|
if (perf_hpp__should_skip(fmt, a->hists))
|
2014-03-18 11:00:59 +07:00
|
|
|
continue;
|
|
|
|
|
2015-01-08 07:45:46 +07:00
|
|
|
cmp = fmt->sort(fmt, a, b);
|
2014-03-03 12:18:00 +07:00
|
|
|
if (cmp)
|
2013-01-22 16:09:33 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-03-03 12:18:00 +07:00
|
|
|
return cmp;
|
2013-01-22 16:09:33 +07:00
|
|
|
}
|
|
|
|
|
2014-04-24 14:37:26 +07:00
|
|
|
static void hists__reset_filter_stats(struct hists *hists)
|
|
|
|
{
|
|
|
|
hists->nr_non_filtered_entries = 0;
|
|
|
|
hists->stats.total_non_filtered_period = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void hists__reset_stats(struct hists *hists)
|
|
|
|
{
|
|
|
|
hists->nr_entries = 0;
|
|
|
|
hists->stats.total_period = 0;
|
|
|
|
|
|
|
|
hists__reset_filter_stats(hists);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hists__inc_filter_stats(struct hists *hists, struct hist_entry *h)
|
|
|
|
{
|
|
|
|
hists->nr_non_filtered_entries++;
|
|
|
|
hists->stats.total_non_filtered_period += h->stat.period;
|
|
|
|
}
|
|
|
|
|
|
|
|
void hists__inc_stats(struct hists *hists, struct hist_entry *h)
|
|
|
|
{
|
|
|
|
if (!h->filtered)
|
|
|
|
hists__inc_filter_stats(hists, h);
|
|
|
|
|
|
|
|
hists->nr_entries++;
|
|
|
|
hists->stats.total_period += h->stat.period;
|
|
|
|
}
|
|
|
|
|
2016-03-09 20:47:02 +07:00
|
|
|
static void hierarchy_recalc_total_periods(struct hists *hists)
|
|
|
|
{
|
|
|
|
struct rb_node *node;
|
|
|
|
struct hist_entry *he;
|
|
|
|
|
|
|
|
node = rb_first(&hists->entries);
|
|
|
|
|
|
|
|
hists->stats.total_period = 0;
|
|
|
|
hists->stats.total_non_filtered_period = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* recalculate total period using top-level entries only
|
|
|
|
* since lower level entries only see non-filtered entries
|
|
|
|
* but upper level entries have sum of both entries.
|
|
|
|
*/
|
|
|
|
while (node) {
|
|
|
|
he = rb_entry(node, struct hist_entry, rb_node);
|
|
|
|
node = rb_next(node);
|
|
|
|
|
|
|
|
hists->stats.total_period += he->stat.period;
|
|
|
|
if (!he->filtered)
|
|
|
|
hists->stats.total_non_filtered_period += he->stat.period;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-24 22:13:35 +07:00
|
|
|
static void hierarchy_insert_output_entry(struct rb_root *root,
|
|
|
|
struct hist_entry *he)
|
|
|
|
{
|
|
|
|
struct rb_node **p = &root->rb_node;
|
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *iter;
|
2016-03-08 02:44:46 +07:00
|
|
|
struct perf_hpp_fmt *fmt;
|
2016-02-24 22:13:35 +07:00
|
|
|
|
|
|
|
while (*p != NULL) {
|
|
|
|
parent = *p;
|
|
|
|
iter = rb_entry(parent, struct hist_entry, rb_node);
|
|
|
|
|
|
|
|
if (hist_entry__sort(he, iter) > 0)
|
|
|
|
p = &parent->rb_left;
|
|
|
|
else
|
|
|
|
p = &parent->rb_right;
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_link_node(&he->rb_node, parent, p);
|
|
|
|
rb_insert_color(&he->rb_node, root);
|
2016-02-27 01:52:47 +07:00
|
|
|
|
|
|
|
/* update column width of dynamic entry */
|
2016-03-08 02:44:46 +07:00
|
|
|
perf_hpp_list__for_each_sort_list(he->hpp_list, fmt) {
|
|
|
|
if (perf_hpp__is_dynamic_entry(fmt))
|
|
|
|
fmt->sort(fmt, he, NULL);
|
|
|
|
}
|
2016-02-24 22:13:35 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void hists__hierarchy_output_resort(struct hists *hists,
|
|
|
|
struct ui_progress *prog,
|
|
|
|
struct rb_root *root_in,
|
|
|
|
struct rb_root *root_out,
|
|
|
|
u64 min_callchain_hits,
|
|
|
|
bool use_callchain)
|
|
|
|
{
|
|
|
|
struct rb_node *node;
|
|
|
|
struct hist_entry *he;
|
|
|
|
|
|
|
|
*root_out = RB_ROOT;
|
|
|
|
node = rb_first(root_in);
|
|
|
|
|
|
|
|
while (node) {
|
|
|
|
he = rb_entry(node, struct hist_entry, rb_node_in);
|
|
|
|
node = rb_next(node);
|
|
|
|
|
|
|
|
hierarchy_insert_output_entry(root_out, he);
|
|
|
|
|
|
|
|
if (prog)
|
|
|
|
ui_progress__update(prog, 1);
|
|
|
|
|
2016-11-08 20:08:33 +07:00
|
|
|
hists->nr_entries++;
|
|
|
|
if (!he->filtered) {
|
|
|
|
hists->nr_non_filtered_entries++;
|
|
|
|
hists__calc_col_len(hists, he);
|
|
|
|
}
|
|
|
|
|
2016-02-24 22:13:35 +07:00
|
|
|
if (!he->leaf) {
|
|
|
|
hists__hierarchy_output_resort(hists, prog,
|
|
|
|
&he->hroot_in,
|
|
|
|
&he->hroot_out,
|
|
|
|
min_callchain_hits,
|
|
|
|
use_callchain);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!use_callchain)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (callchain_param.mode == CHAIN_GRAPH_REL) {
|
|
|
|
u64 total = he->stat.period;
|
|
|
|
|
|
|
|
if (symbol_conf.cumulate_callchain)
|
|
|
|
total = he->stat_acc->period;
|
|
|
|
|
|
|
|
min_callchain_hits = total * (callchain_param.min_percent / 100);
|
|
|
|
}
|
|
|
|
|
|
|
|
callchain_param.sort(&he->sorted_chain, he->callchain,
|
|
|
|
min_callchain_hits, &callchain_param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
perf hist: Introduce hists class and move lots of methods to it
In cbbc79a we introduced support for multiple events by introducing a
new "event_stat_id" struct and then made several perf_session methods
receive a point to it instead of a pointer to perf_session, and kept the
event_stats and hists rb_tree in perf_session.
While working on the new newt based browser, I realised that it would be
better to introduce a new class, "hists" (short for "histograms"),
renaming the "event_stat_id" struct and the perf_session methods that
were really "hists" methods, as they manipulate only struct hists
members, not touching anything in the other perf_session members.
Other optimizations, such as calculating the maximum lenght of a symbol
name present in an hists instance will be possible as we add them,
avoiding a re-traversal just for finding that information.
The rationale for the name "hists" to replace "event_stat_id" is that we
may have multiple sets of hists for the same event_stat id, as, for
instance, the 'perf diff' tool has, so event stat id is not what
characterizes what this struct and the functions that manipulate it do.
Cc: Eric B Munson <ebmunson@us.ibm.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <new-submission>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2010-05-10 23:04:11 +07:00
|
|
|
static void __hists__insert_output_entry(struct rb_root *entries,
|
|
|
|
struct hist_entry *he,
|
2015-08-11 17:30:48 +07:00
|
|
|
u64 min_callchain_hits,
|
|
|
|
bool use_callchain)
|
2009-09-28 20:32:55 +07:00
|
|
|
{
|
perf hist: Introduce hists class and move lots of methods to it
In cbbc79a we introduced support for multiple events by introducing a
new "event_stat_id" struct and then made several perf_session methods
receive a point to it instead of a pointer to perf_session, and kept the
event_stats and hists rb_tree in perf_session.
While working on the new newt based browser, I realised that it would be
better to introduce a new class, "hists" (short for "histograms"),
renaming the "event_stat_id" struct and the perf_session methods that
were really "hists" methods, as they manipulate only struct hists
members, not touching anything in the other perf_session members.
Other optimizations, such as calculating the maximum lenght of a symbol
name present in an hists instance will be possible as we add them,
avoiding a re-traversal just for finding that information.
The rationale for the name "hists" to replace "event_stat_id" is that we
may have multiple sets of hists for the same event_stat id, as, for
instance, the 'perf diff' tool has, so event stat id is not what
characterizes what this struct and the functions that manipulate it do.
Cc: Eric B Munson <ebmunson@us.ibm.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <new-submission>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2010-05-10 23:04:11 +07:00
|
|
|
struct rb_node **p = &entries->rb_node;
|
2009-09-28 20:32:55 +07:00
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *iter;
|
2016-02-27 01:52:47 +07:00
|
|
|
struct perf_hpp_fmt *fmt;
|
2009-09-28 20:32:55 +07:00
|
|
|
|
2016-01-27 22:40:48 +07:00
|
|
|
if (use_callchain) {
|
|
|
|
if (callchain_param.mode == CHAIN_GRAPH_REL) {
|
|
|
|
u64 total = he->stat.period;
|
|
|
|
|
|
|
|
if (symbol_conf.cumulate_callchain)
|
|
|
|
total = he->stat_acc->period;
|
|
|
|
|
|
|
|
min_callchain_hits = total * (callchain_param.min_percent / 100);
|
|
|
|
}
|
2010-04-02 19:50:42 +07:00
|
|
|
callchain_param.sort(&he->sorted_chain, he->callchain,
|
2009-09-28 20:32:55 +07:00
|
|
|
min_callchain_hits, &callchain_param);
|
2016-01-27 22:40:48 +07:00
|
|
|
}
|
2009-09-28 20:32:55 +07:00
|
|
|
|
|
|
|
while (*p != NULL) {
|
|
|
|
parent = *p;
|
|
|
|
iter = rb_entry(parent, struct hist_entry, rb_node);
|
|
|
|
|
2014-03-03 12:18:00 +07:00
|
|
|
if (hist_entry__sort(he, iter) > 0)
|
2009-09-28 20:32:55 +07:00
|
|
|
p = &(*p)->rb_left;
|
|
|
|
else
|
|
|
|
p = &(*p)->rb_right;
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_link_node(&he->rb_node, parent, p);
|
perf hist: Introduce hists class and move lots of methods to it
In cbbc79a we introduced support for multiple events by introducing a
new "event_stat_id" struct and then made several perf_session methods
receive a point to it instead of a pointer to perf_session, and kept the
event_stats and hists rb_tree in perf_session.
While working on the new newt based browser, I realised that it would be
better to introduce a new class, "hists" (short for "histograms"),
renaming the "event_stat_id" struct and the perf_session methods that
were really "hists" methods, as they manipulate only struct hists
members, not touching anything in the other perf_session members.
Other optimizations, such as calculating the maximum lenght of a symbol
name present in an hists instance will be possible as we add them,
avoiding a re-traversal just for finding that information.
The rationale for the name "hists" to replace "event_stat_id" is that we
may have multiple sets of hists for the same event_stat id, as, for
instance, the 'perf diff' tool has, so event stat id is not what
characterizes what this struct and the functions that manipulate it do.
Cc: Eric B Munson <ebmunson@us.ibm.com>
Cc: Frédéric Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Tom Zanussi <tzanussi@gmail.com>
LKML-Reference: <new-submission>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2010-05-10 23:04:11 +07:00
|
|
|
rb_insert_color(&he->rb_node, entries);
|
2016-02-27 01:52:47 +07:00
|
|
|
|
|
|
|
perf_hpp_list__for_each_sort_list(&perf_hpp_list, fmt) {
|
|
|
|
if (perf_hpp__is_dynamic_entry(fmt) &&
|
|
|
|
perf_hpp__defined_dynamic_entry(fmt, he->hists))
|
|
|
|
fmt->sort(fmt, he, NULL); /* update column width */
|
|
|
|
}
|
2009-09-28 20:32:55 +07:00
|
|
|
}
|
|
|
|
|
2016-01-18 16:23:59 +07:00
|
|
|
static void output_resort(struct hists *hists, struct ui_progress *prog,
|
2016-08-02 01:02:34 +07:00
|
|
|
bool use_callchain, hists__resort_cb_t cb)
|
2009-09-28 20:32:55 +07:00
|
|
|
{
|
2011-10-06 03:50:23 +07:00
|
|
|
struct rb_root *root;
|
2009-09-28 20:32:55 +07:00
|
|
|
struct rb_node *next;
|
|
|
|
struct hist_entry *n;
|
2016-02-16 21:08:19 +07:00
|
|
|
u64 callchain_total;
|
2009-09-28 20:32:55 +07:00
|
|
|
u64 min_callchain_hits;
|
|
|
|
|
2016-02-16 21:08:19 +07:00
|
|
|
callchain_total = hists->callchain_period;
|
|
|
|
if (symbol_conf.filter_relative)
|
|
|
|
callchain_total = hists->callchain_non_filtered_period;
|
|
|
|
|
|
|
|
min_callchain_hits = callchain_total * (callchain_param.min_percent / 100);
|
2009-09-28 20:32:55 +07:00
|
|
|
|
2016-02-24 22:13:35 +07:00
|
|
|
hists__reset_stats(hists);
|
|
|
|
hists__reset_col_len(hists);
|
|
|
|
|
|
|
|
if (symbol_conf.report_hierarchy) {
|
2016-03-09 20:47:02 +07:00
|
|
|
hists__hierarchy_output_resort(hists, prog,
|
|
|
|
&hists->entries_collapsed,
|
|
|
|
&hists->entries,
|
|
|
|
min_callchain_hits,
|
|
|
|
use_callchain);
|
|
|
|
hierarchy_recalc_total_periods(hists);
|
|
|
|
return;
|
2016-02-24 22:13:35 +07:00
|
|
|
}
|
|
|
|
|
2016-05-03 18:54:42 +07:00
|
|
|
if (hists__has(hists, need_collapse))
|
2011-10-06 03:50:23 +07:00
|
|
|
root = &hists->entries_collapsed;
|
|
|
|
else
|
|
|
|
root = hists->entries_in;
|
|
|
|
|
|
|
|
next = rb_first(root);
|
|
|
|
hists->entries = RB_ROOT;
|
2009-09-28 20:32:55 +07:00
|
|
|
|
|
|
|
while (next) {
|
2011-10-06 03:50:23 +07:00
|
|
|
n = rb_entry(next, struct hist_entry, rb_node_in);
|
|
|
|
next = rb_next(&n->rb_node_in);
|
2009-09-28 20:32:55 +07:00
|
|
|
|
2016-08-02 01:02:34 +07:00
|
|
|
if (cb && cb(n))
|
|
|
|
continue;
|
|
|
|
|
2015-08-11 17:30:48 +07:00
|
|
|
__hists__insert_output_entry(&hists->entries, n, min_callchain_hits, use_callchain);
|
2014-04-24 14:21:46 +07:00
|
|
|
hists__inc_stats(hists, n);
|
2014-04-24 14:25:19 +07:00
|
|
|
|
|
|
|
if (!n->filtered)
|
|
|
|
hists__calc_col_len(hists, n);
|
2014-12-22 11:44:10 +07:00
|
|
|
|
|
|
|
if (prog)
|
|
|
|
ui_progress__update(prog, 1);
|
2009-09-28 20:32:55 +07:00
|
|
|
}
|
2011-10-06 03:50:23 +07:00
|
|
|
}
|
2009-12-14 20:37:11 +07:00
|
|
|
|
2016-01-18 16:24:00 +07:00
|
|
|
void perf_evsel__output_resort(struct perf_evsel *evsel, struct ui_progress *prog)
|
2016-01-18 16:23:59 +07:00
|
|
|
{
|
|
|
|
bool use_callchain;
|
|
|
|
|
|
|
|
if (evsel && symbol_conf.use_callchain && !symbol_conf.show_ref_callgraph)
|
|
|
|
use_callchain = evsel->attr.sample_type & PERF_SAMPLE_CALLCHAIN;
|
|
|
|
else
|
|
|
|
use_callchain = symbol_conf.use_callchain;
|
|
|
|
|
perf report: Make --branch-history work without callgraphs(-g) option in perf record
perf record -b -g <command>
perf report --branch-history
This merges the LBRs with the callgraphs.
However it would be nice if it also works without callgraphs (-g) set in
perf record, so that only the LBRs are displayed. But currently perf
report errors in this case. For example,
perf record -b <command>
perf report --branch-history
Error:
Selected -g or --branch-history but no callchain data. Did
you call 'perf record' without -g?
This patch displays the LBRs only even if callgraphs(-g) is not enabled
in perf record.
Change log:
v2: According to Milian Wolff's comment, change the obsolete error
message. Now the error message is:
┌─Error:─────────────────────────────────────┐
│Selected -g or --branch-history. │
│But no callchain or branch data. │
│Did you call 'perf record' without -g or -b?│
│ │
│ │
│Press any key... │
└────────────────────────────────────────────┘
When passing the last parameter to hists__fprintf,
changes "|" to "||".
hists__fprintf(hists, !quiet, 0, 0, rep->min_percent, stdout,
symbol_conf.use_callchain || symbol_conf.show_branchflag_count);
Signed-off-by: Yao Jin <yao.jin@linux.intel.com>
Reviewed-by: Andi Kleen <ak@linux.intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/1494240182-28899-1-git-send-email-yao.jin@linux.intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-05-08 17:43:02 +07:00
|
|
|
use_callchain |= symbol_conf.show_branchflag_count;
|
|
|
|
|
2016-08-02 01:02:34 +07:00
|
|
|
output_resort(evsel__hists(evsel), prog, use_callchain, NULL);
|
2016-01-18 16:24:00 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void hists__output_resort(struct hists *hists, struct ui_progress *prog)
|
|
|
|
{
|
2016-08-02 01:02:34 +07:00
|
|
|
output_resort(hists, prog, symbol_conf.use_callchain, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void hists__output_resort_cb(struct hists *hists, struct ui_progress *prog,
|
|
|
|
hists__resort_cb_t cb)
|
|
|
|
{
|
|
|
|
output_resort(hists, prog, symbol_conf.use_callchain, cb);
|
2016-01-18 16:23:59 +07:00
|
|
|
}
|
|
|
|
|
2016-02-24 22:13:36 +07:00
|
|
|
static bool can_goto_child(struct hist_entry *he, enum hierarchy_move_dir hmd)
|
|
|
|
{
|
|
|
|
if (he->leaf || hmd == HMD_FORCE_SIBLING)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (he->unfolded || hmd == HMD_FORCE_CHILD)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rb_node *rb_hierarchy_last(struct rb_node *node)
|
|
|
|
{
|
|
|
|
struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node);
|
|
|
|
|
|
|
|
while (can_goto_child(he, HMD_NORMAL)) {
|
|
|
|
node = rb_last(&he->hroot_out);
|
|
|
|
he = rb_entry(node, struct hist_entry, rb_node);
|
|
|
|
}
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rb_node *__rb_hierarchy_next(struct rb_node *node, enum hierarchy_move_dir hmd)
|
|
|
|
{
|
|
|
|
struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node);
|
|
|
|
|
|
|
|
if (can_goto_child(he, hmd))
|
|
|
|
node = rb_first(&he->hroot_out);
|
|
|
|
else
|
|
|
|
node = rb_next(node);
|
|
|
|
|
|
|
|
while (node == NULL) {
|
|
|
|
he = he->parent_he;
|
|
|
|
if (he == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
node = rb_next(&he->rb_node);
|
|
|
|
}
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct rb_node *rb_hierarchy_prev(struct rb_node *node)
|
|
|
|
{
|
|
|
|
struct hist_entry *he = rb_entry(node, struct hist_entry, rb_node);
|
|
|
|
|
|
|
|
node = rb_prev(node);
|
|
|
|
if (node)
|
|
|
|
return rb_hierarchy_last(node);
|
|
|
|
|
|
|
|
he = he->parent_he;
|
|
|
|
if (he == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return &he->rb_node;
|
|
|
|
}
|
|
|
|
|
2016-02-26 19:13:16 +07:00
|
|
|
bool hist_entry__has_hierarchy_children(struct hist_entry *he, float limit)
|
|
|
|
{
|
|
|
|
struct rb_node *node;
|
|
|
|
struct hist_entry *child;
|
|
|
|
float percent;
|
|
|
|
|
|
|
|
if (he->leaf)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
node = rb_first(&he->hroot_out);
|
|
|
|
child = rb_entry(node, struct hist_entry, rb_node);
|
|
|
|
|
|
|
|
while (node && child->filtered) {
|
|
|
|
node = rb_next(node);
|
|
|
|
child = rb_entry(node, struct hist_entry, rb_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node)
|
|
|
|
percent = hist_entry__get_percent_limit(child);
|
|
|
|
else
|
|
|
|
percent = 0;
|
|
|
|
|
|
|
|
return node && percent >= limit;
|
|
|
|
}
|
|
|
|
|
2011-09-26 22:33:28 +07:00
|
|
|
static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h,
|
2010-07-16 22:35:07 +07:00
|
|
|
enum hist_filter filter)
|
|
|
|
{
|
|
|
|
h->filtered &= ~(1 << filter);
|
2016-02-24 22:13:38 +07:00
|
|
|
|
|
|
|
if (symbol_conf.report_hierarchy) {
|
|
|
|
struct hist_entry *parent = h->parent_he;
|
|
|
|
|
|
|
|
while (parent) {
|
|
|
|
he_stat__add_stat(&parent->stat, &h->stat);
|
|
|
|
|
|
|
|
parent->filtered &= ~(1 << filter);
|
|
|
|
|
|
|
|
if (parent->filtered)
|
|
|
|
goto next;
|
|
|
|
|
|
|
|
/* force fold unfiltered entry for simplicity */
|
|
|
|
parent->unfolded = false;
|
2016-02-26 19:13:19 +07:00
|
|
|
parent->has_no_entry = false;
|
2016-02-24 22:13:38 +07:00
|
|
|
parent->row_offset = 0;
|
|
|
|
parent->nr_rows = 0;
|
|
|
|
next:
|
|
|
|
parent = parent->parent_he;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-16 22:35:07 +07:00
|
|
|
if (h->filtered)
|
|
|
|
return;
|
|
|
|
|
2014-04-24 14:44:16 +07:00
|
|
|
/* force fold unfiltered entry for simplicity */
|
2015-05-05 21:55:46 +07:00
|
|
|
h->unfolded = false;
|
2016-02-26 19:13:19 +07:00
|
|
|
h->has_no_entry = false;
|
2010-07-27 03:13:40 +07:00
|
|
|
h->row_offset = 0;
|
2015-03-11 19:36:03 +07:00
|
|
|
h->nr_rows = 0;
|
2014-04-24 14:37:26 +07:00
|
|
|
|
2013-12-26 13:11:52 +07:00
|
|
|
hists->stats.nr_non_filtered_samples += h->stat.nr_events;
|
2010-07-16 22:35:07 +07:00
|
|
|
|
2014-04-24 14:37:26 +07:00
|
|
|
hists__inc_filter_stats(hists, h);
|
2011-09-26 22:33:28 +07:00
|
|
|
hists__calc_col_len(hists, h);
|
2010-07-16 22:35:07 +07:00
|
|
|
}
|
|
|
|
|
2011-10-19 22:09:10 +07:00
|
|
|
|
|
|
|
static bool hists__filter_entry_by_dso(struct hists *hists,
|
|
|
|
struct hist_entry *he)
|
|
|
|
{
|
|
|
|
if (hists->dso_filter != NULL &&
|
|
|
|
(he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) {
|
|
|
|
he->filtered |= (1 << HIST_FILTER__DSO);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hists__filter_entry_by_thread(struct hists *hists,
|
|
|
|
struct hist_entry *he)
|
|
|
|
{
|
|
|
|
if (hists->thread_filter != NULL &&
|
|
|
|
he->thread != hists->thread_filter) {
|
|
|
|
he->filtered |= (1 << HIST_FILTER__THREAD);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-03-16 15:50:51 +07:00
|
|
|
static bool hists__filter_entry_by_symbol(struct hists *hists,
|
|
|
|
struct hist_entry *he)
|
|
|
|
{
|
|
|
|
if (hists->symbol_filter_str != NULL &&
|
|
|
|
(!he->ms.sym || strstr(he->ms.sym->name,
|
|
|
|
hists->symbol_filter_str) == NULL)) {
|
|
|
|
he->filtered |= (1 << HIST_FILTER__SYMBOL);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-09-04 21:45:44 +07:00
|
|
|
static bool hists__filter_entry_by_socket(struct hists *hists,
|
|
|
|
struct hist_entry *he)
|
|
|
|
{
|
|
|
|
if ((hists->socket_filter > -1) &&
|
|
|
|
(he->socket != hists->socket_filter)) {
|
|
|
|
he->filtered |= (1 << HIST_FILTER__SOCKET);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-20 08:15:21 +07:00
|
|
|
typedef bool (*filter_fn_t)(struct hists *hists, struct hist_entry *he);
|
|
|
|
|
|
|
|
static void hists__filter_by_type(struct hists *hists, int type, filter_fn_t filter)
|
2015-09-04 21:45:45 +07:00
|
|
|
{
|
|
|
|
struct rb_node *nd;
|
|
|
|
|
|
|
|
hists->stats.nr_non_filtered_samples = 0;
|
|
|
|
|
|
|
|
hists__reset_filter_stats(hists);
|
|
|
|
hists__reset_col_len(hists);
|
|
|
|
|
|
|
|
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
|
|
|
|
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
|
|
|
|
2016-01-20 08:15:21 +07:00
|
|
|
if (filter(hists, h))
|
2015-09-04 21:45:45 +07:00
|
|
|
continue;
|
|
|
|
|
2016-01-20 08:15:21 +07:00
|
|
|
hists__remove_entry_filter(hists, h, type);
|
2015-09-04 21:45:45 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-24 22:13:39 +07:00
|
|
|
static void resort_filtered_entry(struct rb_root *root, struct hist_entry *he)
|
|
|
|
{
|
|
|
|
struct rb_node **p = &root->rb_node;
|
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *iter;
|
|
|
|
struct rb_root new_root = RB_ROOT;
|
|
|
|
struct rb_node *nd;
|
|
|
|
|
|
|
|
while (*p != NULL) {
|
|
|
|
parent = *p;
|
|
|
|
iter = rb_entry(parent, struct hist_entry, rb_node);
|
|
|
|
|
|
|
|
if (hist_entry__sort(he, iter) > 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);
|
|
|
|
|
|
|
|
if (he->leaf || he->filtered)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nd = rb_first(&he->hroot_out);
|
|
|
|
while (nd) {
|
|
|
|
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
|
|
|
|
|
|
|
nd = rb_next(nd);
|
|
|
|
rb_erase(&h->rb_node, &he->hroot_out);
|
|
|
|
|
|
|
|
resort_filtered_entry(&new_root, h);
|
|
|
|
}
|
|
|
|
|
|
|
|
he->hroot_out = new_root;
|
|
|
|
}
|
|
|
|
|
2016-02-24 22:13:38 +07:00
|
|
|
static void hists__filter_hierarchy(struct hists *hists, int type, const void *arg)
|
|
|
|
{
|
|
|
|
struct rb_node *nd;
|
2016-02-24 22:13:39 +07:00
|
|
|
struct rb_root new_root = RB_ROOT;
|
2016-02-24 22:13:38 +07:00
|
|
|
|
|
|
|
hists->stats.nr_non_filtered_samples = 0;
|
|
|
|
|
|
|
|
hists__reset_filter_stats(hists);
|
|
|
|
hists__reset_col_len(hists);
|
|
|
|
|
|
|
|
nd = rb_first(&hists->entries);
|
|
|
|
while (nd) {
|
|
|
|
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = hist_entry__filter(h, type, arg);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* case 1. non-matching type
|
|
|
|
* zero out the period, set filter marker and move to child
|
|
|
|
*/
|
|
|
|
if (ret < 0) {
|
|
|
|
memset(&h->stat, 0, sizeof(h->stat));
|
|
|
|
h->filtered |= (1 << type);
|
|
|
|
|
|
|
|
nd = __rb_hierarchy_next(&h->rb_node, HMD_FORCE_CHILD);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* case 2. matched type (filter out)
|
|
|
|
* set filter marker and move to next
|
|
|
|
*/
|
|
|
|
else if (ret == 1) {
|
|
|
|
h->filtered |= (1 << type);
|
|
|
|
|
|
|
|
nd = __rb_hierarchy_next(&h->rb_node, HMD_FORCE_SIBLING);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* case 3. ok (not filtered)
|
|
|
|
* add period to hists and parents, erase the filter marker
|
|
|
|
* and move to next sibling
|
|
|
|
*/
|
|
|
|
else {
|
|
|
|
hists__remove_entry_filter(hists, h, type);
|
|
|
|
|
|
|
|
nd = __rb_hierarchy_next(&h->rb_node, HMD_FORCE_SIBLING);
|
|
|
|
}
|
|
|
|
}
|
2016-02-24 22:13:39 +07:00
|
|
|
|
2016-03-09 20:47:02 +07:00
|
|
|
hierarchy_recalc_total_periods(hists);
|
|
|
|
|
2016-02-24 22:13:39 +07:00
|
|
|
/*
|
|
|
|
* resort output after applying a new filter since filter in a lower
|
|
|
|
* hierarchy can change periods in a upper hierarchy.
|
|
|
|
*/
|
|
|
|
nd = rb_first(&hists->entries);
|
|
|
|
while (nd) {
|
|
|
|
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
|
|
|
|
|
|
|
nd = rb_next(nd);
|
|
|
|
rb_erase(&h->rb_node, &hists->entries);
|
|
|
|
|
|
|
|
resort_filtered_entry(&new_root, h);
|
|
|
|
}
|
|
|
|
|
|
|
|
hists->entries = new_root;
|
2016-02-24 22:13:38 +07:00
|
|
|
}
|
|
|
|
|
2016-01-20 08:15:21 +07:00
|
|
|
void hists__filter_by_thread(struct hists *hists)
|
|
|
|
{
|
2016-02-24 22:13:38 +07:00
|
|
|
if (symbol_conf.report_hierarchy)
|
|
|
|
hists__filter_hierarchy(hists, HIST_FILTER__THREAD,
|
|
|
|
hists->thread_filter);
|
|
|
|
else
|
|
|
|
hists__filter_by_type(hists, HIST_FILTER__THREAD,
|
|
|
|
hists__filter_entry_by_thread);
|
2016-01-20 08:15:21 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void hists__filter_by_dso(struct hists *hists)
|
|
|
|
{
|
2016-02-24 22:13:38 +07:00
|
|
|
if (symbol_conf.report_hierarchy)
|
|
|
|
hists__filter_hierarchy(hists, HIST_FILTER__DSO,
|
|
|
|
hists->dso_filter);
|
|
|
|
else
|
|
|
|
hists__filter_by_type(hists, HIST_FILTER__DSO,
|
|
|
|
hists__filter_entry_by_dso);
|
2016-01-20 08:15:21 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void hists__filter_by_symbol(struct hists *hists)
|
|
|
|
{
|
2016-02-24 22:13:38 +07:00
|
|
|
if (symbol_conf.report_hierarchy)
|
|
|
|
hists__filter_hierarchy(hists, HIST_FILTER__SYMBOL,
|
|
|
|
hists->symbol_filter_str);
|
|
|
|
else
|
|
|
|
hists__filter_by_type(hists, HIST_FILTER__SYMBOL,
|
|
|
|
hists__filter_entry_by_symbol);
|
2016-01-20 08:15:21 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void hists__filter_by_socket(struct hists *hists)
|
|
|
|
{
|
2016-02-24 22:13:38 +07:00
|
|
|
if (symbol_conf.report_hierarchy)
|
|
|
|
hists__filter_hierarchy(hists, HIST_FILTER__SOCKET,
|
|
|
|
&hists->socket_filter);
|
|
|
|
else
|
|
|
|
hists__filter_by_type(hists, HIST_FILTER__SOCKET,
|
|
|
|
hists__filter_entry_by_socket);
|
2016-01-20 08:15:21 +07:00
|
|
|
}
|
|
|
|
|
2012-12-19 02:24:46 +07:00
|
|
|
void events_stats__inc(struct events_stats *stats, u32 type)
|
|
|
|
{
|
|
|
|
++stats->nr_events[0];
|
|
|
|
++stats->nr_events[type];
|
|
|
|
}
|
|
|
|
|
2011-09-26 22:33:28 +07:00
|
|
|
void hists__inc_nr_events(struct hists *hists, u32 type)
|
2010-05-14 20:36:42 +07:00
|
|
|
{
|
2012-12-19 02:24:46 +07:00
|
|
|
events_stats__inc(&hists->stats, type);
|
2010-05-14 20:36:42 +07:00
|
|
|
}
|
2012-11-09 03:54:33 +07:00
|
|
|
|
2014-05-28 12:12:18 +07:00
|
|
|
void hists__inc_nr_samples(struct hists *hists, bool filtered)
|
|
|
|
{
|
|
|
|
events_stats__inc(&hists->stats, PERF_RECORD_SAMPLE);
|
|
|
|
if (!filtered)
|
|
|
|
hists->stats.nr_non_filtered_samples++;
|
|
|
|
}
|
|
|
|
|
2012-11-09 04:03:09 +07:00
|
|
|
static struct hist_entry *hists__add_dummy_entry(struct hists *hists,
|
|
|
|
struct hist_entry *pair)
|
|
|
|
{
|
2012-12-10 15:29:55 +07:00
|
|
|
struct rb_root *root;
|
|
|
|
struct rb_node **p;
|
2012-11-09 04:03:09 +07:00
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *he;
|
2013-10-01 21:22:15 +07:00
|
|
|
int64_t cmp;
|
2012-11-09 04:03:09 +07:00
|
|
|
|
2016-05-03 18:54:42 +07:00
|
|
|
if (hists__has(hists, need_collapse))
|
2012-12-10 15:29:55 +07:00
|
|
|
root = &hists->entries_collapsed;
|
|
|
|
else
|
|
|
|
root = hists->entries_in;
|
|
|
|
|
|
|
|
p = &root->rb_node;
|
|
|
|
|
2012-11-09 04:03:09 +07:00
|
|
|
while (*p != NULL) {
|
|
|
|
parent = *p;
|
2012-12-10 15:29:55 +07:00
|
|
|
he = rb_entry(parent, struct hist_entry, rb_node_in);
|
2012-11-09 04:03:09 +07:00
|
|
|
|
2012-12-10 15:29:55 +07:00
|
|
|
cmp = hist_entry__collapse(he, pair);
|
2012-11-09 04:03:09 +07:00
|
|
|
|
|
|
|
if (!cmp)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (cmp < 0)
|
|
|
|
p = &(*p)->rb_left;
|
|
|
|
else
|
|
|
|
p = &(*p)->rb_right;
|
|
|
|
}
|
|
|
|
|
2012-09-11 11:34:27 +07:00
|
|
|
he = hist_entry__new(pair, true);
|
2012-11-09 04:03:09 +07:00
|
|
|
if (he) {
|
2012-11-12 23:20:03 +07:00
|
|
|
memset(&he->stat, 0, sizeof(he->stat));
|
|
|
|
he->hists = hists;
|
2016-04-25 13:28:09 +07:00
|
|
|
if (symbol_conf.cumulate_callchain)
|
|
|
|
memset(he->stat_acc, 0, sizeof(he->stat));
|
2012-12-10 15:29:55 +07:00
|
|
|
rb_link_node(&he->rb_node_in, parent, p);
|
|
|
|
rb_insert_color(&he->rb_node_in, root);
|
2014-04-24 14:21:46 +07:00
|
|
|
hists__inc_stats(hists, he);
|
2012-12-02 03:18:20 +07:00
|
|
|
he->dummy = true;
|
2012-11-09 04:03:09 +07:00
|
|
|
}
|
|
|
|
out:
|
|
|
|
return he;
|
|
|
|
}
|
|
|
|
|
2016-09-13 14:45:47 +07:00
|
|
|
static struct hist_entry *add_dummy_hierarchy_entry(struct hists *hists,
|
|
|
|
struct rb_root *root,
|
|
|
|
struct hist_entry *pair)
|
|
|
|
{
|
|
|
|
struct rb_node **p;
|
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *he;
|
|
|
|
struct perf_hpp_fmt *fmt;
|
|
|
|
|
|
|
|
p = &root->rb_node;
|
|
|
|
while (*p != NULL) {
|
|
|
|
int64_t cmp = 0;
|
|
|
|
|
|
|
|
parent = *p;
|
|
|
|
he = rb_entry(parent, struct hist_entry, rb_node_in);
|
|
|
|
|
|
|
|
perf_hpp_list__for_each_sort_list(he->hpp_list, fmt) {
|
|
|
|
cmp = fmt->collapse(fmt, he, pair);
|
|
|
|
if (cmp)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!cmp)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (cmp < 0)
|
|
|
|
p = &parent->rb_left;
|
|
|
|
else
|
|
|
|
p = &parent->rb_right;
|
|
|
|
}
|
|
|
|
|
|
|
|
he = hist_entry__new(pair, true);
|
|
|
|
if (he) {
|
|
|
|
rb_link_node(&he->rb_node_in, parent, p);
|
|
|
|
rb_insert_color(&he->rb_node_in, root);
|
|
|
|
|
|
|
|
he->dummy = true;
|
|
|
|
he->hists = hists;
|
|
|
|
memset(&he->stat, 0, sizeof(he->stat));
|
|
|
|
hists__inc_stats(hists, he);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return he;
|
|
|
|
}
|
|
|
|
|
2012-11-09 03:54:33 +07:00
|
|
|
static struct hist_entry *hists__find_entry(struct hists *hists,
|
|
|
|
struct hist_entry *he)
|
|
|
|
{
|
2012-12-10 15:29:55 +07:00
|
|
|
struct rb_node *n;
|
|
|
|
|
2016-05-03 18:54:42 +07:00
|
|
|
if (hists__has(hists, need_collapse))
|
2012-12-10 15:29:55 +07:00
|
|
|
n = hists->entries_collapsed.rb_node;
|
|
|
|
else
|
|
|
|
n = hists->entries_in->rb_node;
|
2012-11-09 03:54:33 +07:00
|
|
|
|
|
|
|
while (n) {
|
2012-12-10 15:29:55 +07:00
|
|
|
struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node_in);
|
|
|
|
int64_t cmp = hist_entry__collapse(iter, he);
|
2012-11-09 03:54:33 +07:00
|
|
|
|
|
|
|
if (cmp < 0)
|
|
|
|
n = n->rb_left;
|
|
|
|
else if (cmp > 0)
|
|
|
|
n = n->rb_right;
|
|
|
|
else
|
|
|
|
return iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-09-13 14:45:46 +07:00
|
|
|
static struct hist_entry *hists__find_hierarchy_entry(struct rb_root *root,
|
|
|
|
struct hist_entry *he)
|
|
|
|
{
|
|
|
|
struct rb_node *n = root->rb_node;
|
|
|
|
|
|
|
|
while (n) {
|
|
|
|
struct hist_entry *iter;
|
|
|
|
struct perf_hpp_fmt *fmt;
|
|
|
|
int64_t cmp = 0;
|
|
|
|
|
|
|
|
iter = rb_entry(n, struct hist_entry, rb_node_in);
|
|
|
|
perf_hpp_list__for_each_sort_list(he->hpp_list, fmt) {
|
|
|
|
cmp = fmt->collapse(fmt, iter, he);
|
|
|
|
if (cmp)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmp < 0)
|
|
|
|
n = n->rb_left;
|
|
|
|
else if (cmp > 0)
|
|
|
|
n = n->rb_right;
|
|
|
|
else
|
|
|
|
return iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hists__match_hierarchy(struct rb_root *leader_root,
|
|
|
|
struct rb_root *other_root)
|
|
|
|
{
|
|
|
|
struct rb_node *nd;
|
|
|
|
struct hist_entry *pos, *pair;
|
|
|
|
|
|
|
|
for (nd = rb_first(leader_root); nd; nd = rb_next(nd)) {
|
|
|
|
pos = rb_entry(nd, struct hist_entry, rb_node_in);
|
|
|
|
pair = hists__find_hierarchy_entry(other_root, pos);
|
|
|
|
|
|
|
|
if (pair) {
|
|
|
|
hist_entry__add_pair(pair, pos);
|
|
|
|
hists__match_hierarchy(&pos->hroot_in, &pair->hroot_in);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-09 03:54:33 +07:00
|
|
|
/*
|
|
|
|
* Look for pairs to link to the leader buckets (hist_entries):
|
|
|
|
*/
|
|
|
|
void hists__match(struct hists *leader, struct hists *other)
|
|
|
|
{
|
2012-12-10 15:29:55 +07:00
|
|
|
struct rb_root *root;
|
2012-11-09 03:54:33 +07:00
|
|
|
struct rb_node *nd;
|
|
|
|
struct hist_entry *pos, *pair;
|
|
|
|
|
2016-09-13 14:45:46 +07:00
|
|
|
if (symbol_conf.report_hierarchy) {
|
|
|
|
/* hierarchy report always collapses entries */
|
|
|
|
return hists__match_hierarchy(&leader->entries_collapsed,
|
|
|
|
&other->entries_collapsed);
|
|
|
|
}
|
|
|
|
|
2016-05-03 18:54:42 +07:00
|
|
|
if (hists__has(leader, need_collapse))
|
2012-12-10 15:29:55 +07:00
|
|
|
root = &leader->entries_collapsed;
|
|
|
|
else
|
|
|
|
root = leader->entries_in;
|
|
|
|
|
|
|
|
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
|
|
|
|
pos = rb_entry(nd, struct hist_entry, rb_node_in);
|
2012-11-09 03:54:33 +07:00
|
|
|
pair = hists__find_entry(other, pos);
|
|
|
|
|
|
|
|
if (pair)
|
2012-11-29 13:38:34 +07:00
|
|
|
hist_entry__add_pair(pair, pos);
|
2012-11-09 03:54:33 +07:00
|
|
|
}
|
|
|
|
}
|
2012-11-09 04:03:09 +07:00
|
|
|
|
2016-09-13 14:45:47 +07:00
|
|
|
static int hists__link_hierarchy(struct hists *leader_hists,
|
|
|
|
struct hist_entry *parent,
|
|
|
|
struct rb_root *leader_root,
|
|
|
|
struct rb_root *other_root)
|
|
|
|
{
|
|
|
|
struct rb_node *nd;
|
|
|
|
struct hist_entry *pos, *leader;
|
|
|
|
|
|
|
|
for (nd = rb_first(other_root); nd; nd = rb_next(nd)) {
|
|
|
|
pos = rb_entry(nd, struct hist_entry, rb_node_in);
|
|
|
|
|
|
|
|
if (hist_entry__has_pairs(pos)) {
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
list_for_each_entry(leader, &pos->pairs.head, pairs.node) {
|
|
|
|
if (leader->hists == leader_hists) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found)
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
leader = add_dummy_hierarchy_entry(leader_hists,
|
|
|
|
leader_root, pos);
|
|
|
|
if (leader == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* do not point parent in the pos */
|
|
|
|
leader->parent_he = parent;
|
|
|
|
|
|
|
|
hist_entry__add_pair(pos, leader);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pos->leaf) {
|
|
|
|
if (hists__link_hierarchy(leader_hists, leader,
|
|
|
|
&leader->hroot_in,
|
|
|
|
&pos->hroot_in) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-09 04:03:09 +07:00
|
|
|
/*
|
|
|
|
* Look for entries in the other hists that are not present in the leader, if
|
|
|
|
* we find them, just add a dummy entry on the leader hists, with period=0,
|
|
|
|
* nr_events=0, to serve as the list header.
|
|
|
|
*/
|
|
|
|
int hists__link(struct hists *leader, struct hists *other)
|
|
|
|
{
|
2012-12-10 15:29:55 +07:00
|
|
|
struct rb_root *root;
|
2012-11-09 04:03:09 +07:00
|
|
|
struct rb_node *nd;
|
|
|
|
struct hist_entry *pos, *pair;
|
|
|
|
|
2016-09-13 14:45:47 +07:00
|
|
|
if (symbol_conf.report_hierarchy) {
|
|
|
|
/* hierarchy report always collapses entries */
|
|
|
|
return hists__link_hierarchy(leader, NULL,
|
|
|
|
&leader->entries_collapsed,
|
|
|
|
&other->entries_collapsed);
|
|
|
|
}
|
|
|
|
|
2016-05-03 18:54:42 +07:00
|
|
|
if (hists__has(other, need_collapse))
|
2012-12-10 15:29:55 +07:00
|
|
|
root = &other->entries_collapsed;
|
|
|
|
else
|
|
|
|
root = other->entries_in;
|
|
|
|
|
|
|
|
for (nd = rb_first(root); nd; nd = rb_next(nd)) {
|
|
|
|
pos = rb_entry(nd, struct hist_entry, rb_node_in);
|
2012-11-09 04:03:09 +07:00
|
|
|
|
|
|
|
if (!hist_entry__has_pairs(pos)) {
|
|
|
|
pair = hists__add_dummy_entry(leader, pos);
|
|
|
|
if (pair == NULL)
|
|
|
|
return -1;
|
2012-11-29 13:38:34 +07:00
|
|
|
hist_entry__add_pair(pos, pair);
|
2012-11-09 04:03:09 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-01-14 09:52:48 +07:00
|
|
|
|
2015-07-18 22:24:49 +07:00
|
|
|
void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
|
|
|
|
struct perf_sample *sample, bool nonany_branch_mode)
|
|
|
|
{
|
|
|
|
struct branch_info *bi;
|
|
|
|
|
|
|
|
/* If we have branch cycles always annotate them. */
|
|
|
|
if (bs && bs->nr && bs->entries[0].flags.cycles) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
bi = sample__resolve_bstack(sample, al);
|
|
|
|
if (bi) {
|
|
|
|
struct addr_map_symbol *prev = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore errors, still want to process the
|
|
|
|
* other entries.
|
|
|
|
*
|
|
|
|
* For non standard branch modes always
|
|
|
|
* force no IPC (prev == NULL)
|
|
|
|
*
|
|
|
|
* Note that perf stores branches reversed from
|
|
|
|
* program order!
|
|
|
|
*/
|
|
|
|
for (i = bs->nr - 1; i >= 0; i--) {
|
|
|
|
addr_map_symbol__account_cycles(&bi[i].from,
|
|
|
|
nonany_branch_mode ? NULL : prev,
|
|
|
|
bi[i].flags.cycles);
|
|
|
|
prev = &bi[i].to;
|
|
|
|
}
|
|
|
|
free(bi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-11 01:49:21 +07:00
|
|
|
|
|
|
|
size_t perf_evlist__fprintf_nr_events(struct perf_evlist *evlist, FILE *fp)
|
|
|
|
{
|
|
|
|
struct perf_evsel *pos;
|
|
|
|
size_t ret = 0;
|
|
|
|
|
2016-06-23 21:26:15 +07:00
|
|
|
evlist__for_each_entry(evlist, pos) {
|
2014-10-11 01:49:21 +07:00
|
|
|
ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos));
|
|
|
|
ret += events_stats__fprintf(&evsel__hists(pos)->stats, fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-01-14 09:52:48 +07:00
|
|
|
u64 hists__total_period(struct hists *hists)
|
|
|
|
{
|
|
|
|
return symbol_conf.filter_relative ? hists->stats.total_non_filtered_period :
|
|
|
|
hists->stats.total_period;
|
|
|
|
}
|
2014-02-07 10:06:07 +07:00
|
|
|
|
|
|
|
int parse_filter_percentage(const struct option *opt __maybe_unused,
|
|
|
|
const char *arg, int unset __maybe_unused)
|
|
|
|
{
|
|
|
|
if (!strcmp(arg, "relative"))
|
|
|
|
symbol_conf.filter_relative = true;
|
|
|
|
else if (!strcmp(arg, "absolute"))
|
|
|
|
symbol_conf.filter_relative = false;
|
2017-01-24 23:44:10 +07:00
|
|
|
else {
|
2017-03-30 16:54:40 +07:00
|
|
|
pr_debug("Invalid percentage: %s\n", arg);
|
2014-02-07 10:06:07 +07:00
|
|
|
return -1;
|
2017-01-24 23:44:10 +07:00
|
|
|
}
|
2014-02-07 10:06:07 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-01-14 10:02:15 +07:00
|
|
|
|
|
|
|
int perf_hist_config(const char *var, const char *value)
|
|
|
|
{
|
|
|
|
if (!strcmp(var, "hist.percentage"))
|
|
|
|
return parse_filter_percentage(NULL, value, 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-10-10 02:16:00 +07:00
|
|
|
|
2016-01-18 16:24:22 +07:00
|
|
|
int __hists__init(struct hists *hists, struct perf_hpp_list *hpp_list)
|
2014-10-10 02:16:00 +07:00
|
|
|
{
|
|
|
|
memset(hists, 0, sizeof(*hists));
|
|
|
|
hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
|
|
|
|
hists->entries_in = &hists->entries_in_array[0];
|
|
|
|
hists->entries_collapsed = RB_ROOT;
|
|
|
|
hists->entries = RB_ROOT;
|
|
|
|
pthread_mutex_init(&hists->lock, NULL);
|
2015-09-04 21:45:44 +07:00
|
|
|
hists->socket_filter = -1;
|
2016-01-18 16:24:22 +07:00
|
|
|
hists->hpp_list = hpp_list;
|
2016-03-08 02:44:45 +07:00
|
|
|
INIT_LIST_HEAD(&hists->hpp_formats);
|
2014-10-10 02:16:00 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-12-10 14:53:20 +07:00
|
|
|
static void hists__delete_remaining_entries(struct rb_root *root)
|
|
|
|
{
|
|
|
|
struct rb_node *node;
|
|
|
|
struct hist_entry *he;
|
|
|
|
|
|
|
|
while (!RB_EMPTY_ROOT(root)) {
|
|
|
|
node = rb_first(root);
|
|
|
|
rb_erase(node, root);
|
|
|
|
|
|
|
|
he = rb_entry(node, struct hist_entry, rb_node_in);
|
|
|
|
hist_entry__delete(he);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hists__delete_all_entries(struct hists *hists)
|
|
|
|
{
|
|
|
|
hists__delete_entries(hists);
|
|
|
|
hists__delete_remaining_entries(&hists->entries_in_array[0]);
|
|
|
|
hists__delete_remaining_entries(&hists->entries_in_array[1]);
|
|
|
|
hists__delete_remaining_entries(&hists->entries_collapsed);
|
|
|
|
}
|
|
|
|
|
2015-12-09 09:11:29 +07:00
|
|
|
static void hists_evsel__exit(struct perf_evsel *evsel)
|
|
|
|
{
|
|
|
|
struct hists *hists = evsel__hists(evsel);
|
2016-03-08 02:44:45 +07:00
|
|
|
struct perf_hpp_fmt *fmt, *pos;
|
|
|
|
struct perf_hpp_list_node *node, *tmp;
|
2015-12-09 09:11:29 +07:00
|
|
|
|
2015-12-10 14:53:20 +07:00
|
|
|
hists__delete_all_entries(hists);
|
2016-03-08 02:44:45 +07:00
|
|
|
|
|
|
|
list_for_each_entry_safe(node, tmp, &hists->hpp_formats, list) {
|
|
|
|
perf_hpp_list__for_each_format_safe(&node->hpp, fmt, pos) {
|
|
|
|
list_del(&fmt->list);
|
|
|
|
free(fmt);
|
|
|
|
}
|
|
|
|
list_del(&node->list);
|
|
|
|
free(node);
|
|
|
|
}
|
2015-12-09 09:11:29 +07:00
|
|
|
}
|
|
|
|
|
2016-01-07 16:14:10 +07:00
|
|
|
static int hists_evsel__init(struct perf_evsel *evsel)
|
|
|
|
{
|
|
|
|
struct hists *hists = evsel__hists(evsel);
|
|
|
|
|
2016-01-18 16:24:22 +07:00
|
|
|
__hists__init(hists, &perf_hpp_list);
|
2016-01-07 16:14:10 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-10-10 02:16:00 +07:00
|
|
|
/*
|
|
|
|
* XXX We probably need a hists_evsel__exit() to free the hist_entries
|
|
|
|
* stored in the rbtree...
|
|
|
|
*/
|
|
|
|
|
|
|
|
int hists__init(void)
|
|
|
|
{
|
|
|
|
int err = perf_evsel__object_config(sizeof(struct hists_evsel),
|
2015-12-09 09:11:29 +07:00
|
|
|
hists_evsel__init,
|
|
|
|
hists_evsel__exit);
|
2014-10-10 02:16:00 +07:00
|
|
|
if (err)
|
|
|
|
fputs("FATAL ERROR: Couldn't setup hists class\n", stderr);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
2016-01-18 16:24:13 +07:00
|
|
|
|
|
|
|
void perf_hpp_list__init(struct perf_hpp_list *list)
|
|
|
|
{
|
|
|
|
INIT_LIST_HEAD(&list->fields);
|
|
|
|
INIT_LIST_HEAD(&list->sorts);
|
|
|
|
}
|