License cleanup: add SPDX GPL-2.0 license identifier to files with no license
Many source files in the tree are missing licensing information, which
makes it harder for compliance tools to determine the correct license.
By default all files without license information are under the default
license of the kernel, which is GPL version 2.
Update the files which contain no license information with the 'GPL-2.0'
SPDX license identifier. The SPDX identifier is a legally binding
shorthand, which can be used instead of the full boiler plate text.
This patch is based on work done by Thomas Gleixner and Kate Stewart and
Philippe Ombredanne.
How this work was done:
Patches were generated and checked against linux-4.14-rc6 for a subset of
the use cases:
- file had no licensing information it it.
- file was a */uapi/* one with no licensing information in it,
- file was a */uapi/* one with existing licensing information,
Further patches will be generated in subsequent months to fix up cases
where non-standard license headers were used, and references to license
had to be inferred by heuristics based on keywords.
The analysis to determine which SPDX License Identifier to be applied to
a file was done in a spreadsheet of side by side results from of the
output of two independent scanners (ScanCode & Windriver) producing SPDX
tag:value files created by Philippe Ombredanne. Philippe prepared the
base worksheet, and did an initial spot review of a few 1000 files.
The 4.13 kernel was the starting point of the analysis with 60,537 files
assessed. Kate Stewart did a file by file comparison of the scanner
results in the spreadsheet to determine which SPDX license identifier(s)
to be applied to the file. She confirmed any determination that was not
immediately clear with lawyers working with the Linux Foundation.
Criteria used to select files for SPDX license identifier tagging was:
- Files considered eligible had to be source code files.
- Make and config files were included as candidates if they contained >5
lines of source
- File already had some variant of a license header in it (even if <5
lines).
All documentation files were explicitly excluded.
The following heuristics were used to determine which SPDX license
identifiers to apply.
- when both scanners couldn't find any license traces, file was
considered to have no license information in it, and the top level
COPYING file license applied.
For non */uapi/* files that summary was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 11139
and resulted in the first patch in this series.
If that file was a */uapi/* path one, it was "GPL-2.0 WITH
Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was:
SPDX license identifier # files
---------------------------------------------------|-------
GPL-2.0 WITH Linux-syscall-note 930
and resulted in the second patch in this series.
- if a file had some form of licensing information in it, and was one
of the */uapi/* ones, it was denoted with the Linux-syscall-note if
any GPL family license was found in the file or had no licensing in
it (per prior point). Results summary:
SPDX license identifier # files
---------------------------------------------------|------
GPL-2.0 WITH Linux-syscall-note 270
GPL-2.0+ WITH Linux-syscall-note 169
((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21
((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17
LGPL-2.1+ WITH Linux-syscall-note 15
GPL-1.0+ WITH Linux-syscall-note 14
((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5
LGPL-2.0+ WITH Linux-syscall-note 4
LGPL-2.1 WITH Linux-syscall-note 3
((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3
((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1
and that resulted in the third patch in this series.
- when the two scanners agreed on the detected license(s), that became
the concluded license(s).
- when there was disagreement between the two scanners (one detected a
license but the other didn't, or they both detected different
licenses) a manual inspection of the file occurred.
- In most cases a manual inspection of the information in the file
resulted in a clear resolution of the license that should apply (and
which scanner probably needed to revisit its heuristics).
- When it was not immediately clear, the license identifier was
confirmed with lawyers working with the Linux Foundation.
- If there was any question as to the appropriate license identifier,
the file was flagged for further research and to be revisited later
in time.
In total, over 70 hours of logged manual review was done on the
spreadsheet to determine the SPDX license identifiers to apply to the
source files by Kate, Philippe, Thomas and, in some cases, confirmation
by lawyers working with the Linux Foundation.
Kate also obtained a third independent scan of the 4.13 code base from
FOSSology, and compared selected files where the other two scanners
disagreed against that SPDX file, to see if there was new insights. The
Windriver scanner is based on an older version of FOSSology in part, so
they are related.
Thomas did random spot checks in about 500 files from the spreadsheets
for the uapi headers and agreed with SPDX license identifier in the
files he inspected. For the non-uapi files Thomas did random spot checks
in about 15000 files.
In initial set of patches against 4.14-rc6, 3 files were found to have
copy/paste license identifier errors, and have been fixed to reflect the
correct identifier.
Additionally Philippe spent 10 hours this week doing a detailed manual
inspection and review of the 12,461 patched files from the initial patch
version early this week with:
- a full scancode scan run, collecting the matched texts, detected
license ids and scores
- reviewing anything where there was a license detected (about 500+
files) to ensure that the applied SPDX license was correct
- reviewing anything where there was no detection but the patch license
was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied
SPDX license was correct
This produced a worksheet with 20 files needing minor correction. This
worksheet was then exported into 3 different .csv files for the
different types of files to be modified.
These .csv files were then reviewed by Greg. Thomas wrote a script to
parse the csv files and add the proper SPDX tag to the file, in the
format that the file expected. This script was further refined by Greg
based on the output to detect more types of files automatically and to
distinguish between header and source .c files (which need different
comment types.) Finally Greg ran the script using the .csv files to
generate the patches.
Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org>
Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2017-11-01 21:07:57 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2019-01-29 17:11:04 +07:00
|
|
|
#include "callchain.h"
|
2019-08-30 01:56:40 +07:00
|
|
|
#include "debug.h"
|
2019-08-30 21:11:01 +07:00
|
|
|
#include "dso.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"
|
2019-08-31 01:09:54 +07:00
|
|
|
#include "map_symbol.h"
|
|
|
|
#include "branch.h"
|
|
|
|
#include "mem-events.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"
|
2018-04-03 00:24:28 +07:00
|
|
|
#include "units.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"
|
2019-01-28 06:03:34 +07:00
|
|
|
#include "symbol.h"
|
2017-04-20 07:34:35 +07:00
|
|
|
#include "thread.h"
|
2019-11-07 14:47:14 +07:00
|
|
|
#include "block-info.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>
|
2018-04-03 00:24:28 +07:00
|
|
|
#include <inttypes.h>
|
2017-04-20 04:51:14 +07:00
|
|
|
#include <sys/param.h>
|
2019-08-30 21:54:00 +07:00
|
|
|
#include <linux/rbtree.h>
|
2019-08-30 02:18:59 +07:00
|
|
|
#include <linux/string.h>
|
2019-03-11 21:44:54 +07:00
|
|
|
#include <linux/time64.h>
|
2019-07-04 21:32:27 +07:00
|
|
|
#include <linux/zalloc.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;
|
|
|
|
|
2019-11-07 14:47:13 +07:00
|
|
|
if (h->block_info)
|
|
|
|
return;
|
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) {
|
2019-11-05 01:57:38 +07:00
|
|
|
if (h->branch_info->from.ms.sym) {
|
|
|
|
symlen = (int)h->branch_info->from.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;
|
2012-02-10 05:21:01 +07:00
|
|
|
hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
|
|
|
|
|
2019-11-05 01:57:38 +07:00
|
|
|
symlen = dso__name_len(h->branch_info->from.ms.map->dso);
|
2012-02-10 05:21:01 +07:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-11-05 01:57:38 +07:00
|
|
|
if (h->branch_info->to.ms.sym) {
|
|
|
|
symlen = (int)h->branch_info->to.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;
|
2012-02-10 05:21:01 +07:00
|
|
|
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
|
|
|
|
|
2019-11-05 01:57:38 +07:00
|
|
|
symlen = dso__name_len(h->branch_info->to.ms.map->dso);
|
2012-02-10 05:21:01 +07:00
|
|
|
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) {
|
2019-11-05 01:57:38 +07:00
|
|
|
if (h->mem_info->daddr.ms.sym) {
|
|
|
|
symlen = (int)h->mem_info->daddr.ms.sym->namelen + 4
|
2013-01-24 22:10:35 +07:00
|
|
|
+ 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
|
|
|
|
2019-11-05 01:57:38 +07:00
|
|
|
if (h->mem_info->iaddr.ms.sym) {
|
|
|
|
symlen = (int)h->mem_info->iaddr.ms.sym->namelen + 4
|
2015-10-06 01:06:08 +07:00
|
|
|
+ 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);
|
|
|
|
}
|
|
|
|
|
2019-11-05 01:57:38 +07:00
|
|
|
if (h->mem_info->daddr.ms.map) {
|
|
|
|
symlen = dso__name_len(h->mem_info->daddr.ms.map->dso);
|
2013-01-24 22:10:35 +07:00
|
|
|
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);
|
2019-08-24 04:03:38 +07:00
|
|
|
if (symbol_conf.nanosecs)
|
|
|
|
hists__new_col_len(hists, HISTC_TIME, 16);
|
|
|
|
else
|
|
|
|
hists__new_col_len(hists, HISTC_TIME, 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)
|
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_node *next = rb_first_cached(&hists->entries);
|
2012-08-20 11:52:05 +07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-11 21:44:54 +07:00
|
|
|
static long hist_time(unsigned long htime)
|
|
|
|
{
|
|
|
|
unsigned long time_quantum = symbol_conf.time_quantum;
|
|
|
|
if (time_quantum)
|
|
|
|
return (htime / time_quantum) * time_quantum;
|
|
|
|
return htime;
|
|
|
|
}
|
|
|
|
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_node *node = rb_first_cached(&he->hroot_out);
|
2016-02-24 22:13:49 +07:00
|
|
|
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)
|
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *root_in;
|
|
|
|
struct rb_root_cached *root_out;
|
2016-02-24 22:13:49 +07:00
|
|
|
|
|
|
|
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
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_erase_cached(&he->rb_node_in, root_in);
|
|
|
|
rb_erase_cached(&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
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_node *next = rb_first_cached(&hists->entries);
|
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 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)
|
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_node *next = rb_first_cached(&hists->entries);
|
2014-08-12 15:16:05 +07:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-28 16:23:03 +07:00
|
|
|
struct hist_entry *hists__get_entry(struct hists *hists, int idx)
|
|
|
|
{
|
|
|
|
struct rb_node *next = rb_first_cached(&hists->entries);
|
|
|
|
struct hist_entry *n;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while (next) {
|
|
|
|
n = rb_entry(next, struct hist_entry, rb_node);
|
|
|
|
if (i == idx)
|
|
|
|
return n;
|
|
|
|
|
|
|
|
next = rb_next(&n->rb_node);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
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,
|
2018-06-08 00:19:54 +07:00
|
|
|
bool sample_self,
|
|
|
|
size_t callchain_size)
|
2010-05-09 23:02:23 +07:00
|
|
|
{
|
2016-07-05 13:56:03 +07:00
|
|
|
*he = *template;
|
2018-06-08 00:19:54 +07:00
|
|
|
he->callchain_size = callchain_size;
|
2016-07-05 13:56:03 +07:00
|
|
|
|
|
|
|
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));
|
2019-03-05 22:25:30 +07:00
|
|
|
if (he->branch_info == NULL)
|
|
|
|
goto err;
|
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
|
|
|
|
2019-11-05 01:57:38 +07:00
|
|
|
map__get(he->branch_info->from.ms.map);
|
|
|
|
map__get(he->branch_info->to.ms.map);
|
2016-07-05 13:56:03 +07:00
|
|
|
}
|
2012-10-04 19:49:42 +07:00
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
if (he->mem_info) {
|
2019-11-05 01:57:38 +07:00
|
|
|
map__get(he->mem_info->iaddr.ms.map);
|
|
|
|
map__get(he->mem_info->daddr.ms.map);
|
2016-07-05 13:56:03 +07:00
|
|
|
}
|
2012-09-11 11:15:07 +07:00
|
|
|
|
perf hists: Check if a hist_entry has callchains before using them
So far if we use 'perf record -g' this will make
symbol_conf.use_callchain 'true' and logic will assume that all events
have callchains enabled, but ever since we added the possibility of
setting up callchains for some events (e.g.: -e
cycles/call-graph=dwarf/) while not for others, we limit usage scenarios
by looking at that symbol_conf.use_callchain global boolean, we better
look at each event attributes.
On the road to that we need to look if a hist_entry has callchains, that
is, to go from hist_entry->hists to the evsel that contains it, to then
look at evsel->sample_type for PERF_SAMPLE_CALLCHAIN.
The next step is to add a symbol_conf.ignore_callchains global, to use
in the places where what we really want to know is if callchains should
be ignored, even if present.
Then -g will mean just to select a callchain mode to be applied to all
events not explicitely setting some other callchain mode, i.e. a default
callchain mode, and --no-call-graph will set
symbol_conf.ignore_callchains with that clear intention.
That too will at some point become a per evsel thing, that tools can set
for all or just a few of its evsels.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: https://lkml.kernel.org/n/tip-0sas5cm4dsw2obn75g7ruz69@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-05-29 23:59:24 +07:00
|
|
|
if (hist_entry__has_callchains(he) && symbol_conf.use_callchain)
|
2016-07-05 13:56:03 +07:00
|
|
|
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);
|
2019-03-05 22:25:30 +07:00
|
|
|
if (he->raw_data == NULL)
|
|
|
|
goto err_infos;
|
2016-07-05 13:56:03 +07:00
|
|
|
}
|
2019-03-05 22:25:31 +07:00
|
|
|
|
|
|
|
if (he->srcline) {
|
|
|
|
he->srcline = strdup(he->srcline);
|
|
|
|
if (he->srcline == NULL)
|
|
|
|
goto err_rawdata;
|
|
|
|
}
|
|
|
|
|
2019-03-11 21:44:58 +07:00
|
|
|
if (symbol_conf.res_sample) {
|
|
|
|
he->res_samples = calloc(sizeof(struct res_sample),
|
|
|
|
symbol_conf.res_sample);
|
|
|
|
if (!he->res_samples)
|
|
|
|
goto err_srcline;
|
|
|
|
}
|
|
|
|
|
2016-07-05 13:56:03 +07:00
|
|
|
INIT_LIST_HEAD(&he->pairs.node);
|
|
|
|
thread__get(he->thread);
|
2018-12-07 02:18:18 +07:00
|
|
|
he->hroot_in = RB_ROOT_CACHED;
|
|
|
|
he->hroot_out = RB_ROOT_CACHED;
|
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;
|
2019-03-05 22:25:30 +07:00
|
|
|
|
2019-03-11 21:44:58 +07:00
|
|
|
err_srcline:
|
2019-07-04 22:06:20 +07:00
|
|
|
zfree(&he->srcline);
|
2019-03-11 21:44:58 +07:00
|
|
|
|
2019-03-05 22:25:31 +07:00
|
|
|
err_rawdata:
|
2019-07-04 22:06:20 +07:00
|
|
|
zfree(&he->raw_data);
|
2019-03-05 22:25:31 +07:00
|
|
|
|
2019-03-05 22:25:30 +07:00
|
|
|
err_infos:
|
|
|
|
if (he->branch_info) {
|
2019-11-05 01:57:38 +07:00
|
|
|
map__put(he->branch_info->from.ms.map);
|
|
|
|
map__put(he->branch_info->to.ms.map);
|
2019-07-04 22:06:20 +07:00
|
|
|
zfree(&he->branch_info);
|
2019-03-05 22:25:30 +07:00
|
|
|
}
|
|
|
|
if (he->mem_info) {
|
2019-11-05 01:57:38 +07:00
|
|
|
map__put(he->mem_info->iaddr.ms.map);
|
|
|
|
map__put(he->mem_info->daddr.ms.map);
|
2019-03-05 22:25:30 +07:00
|
|
|
}
|
|
|
|
err:
|
|
|
|
map__zput(he->ms.map);
|
2019-07-04 22:06:20 +07:00
|
|
|
zfree(&he->stat_acc);
|
2019-03-05 22:25:30 +07:00
|
|
|
return -ENOMEM;
|
2016-07-05 13:56:03 +07:00
|
|
|
}
|
|
|
|
|
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) {
|
2018-06-08 00:19:54 +07:00
|
|
|
err = hist_entry__init(he, template, sample_self, callchain_size);
|
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)
|
|
|
|
{
|
perf hists: Check if a hist_entry has callchains before using them
So far if we use 'perf record -g' this will make
symbol_conf.use_callchain 'true' and logic will assume that all events
have callchains enabled, but ever since we added the possibility of
setting up callchains for some events (e.g.: -e
cycles/call-graph=dwarf/) while not for others, we limit usage scenarios
by looking at that symbol_conf.use_callchain global boolean, we better
look at each event attributes.
On the road to that we need to look if a hist_entry has callchains, that
is, to go from hist_entry->hists to the evsel that contains it, to then
look at evsel->sample_type for PERF_SAMPLE_CALLCHAIN.
The next step is to add a symbol_conf.ignore_callchains global, to use
in the places where what we really want to know is if callchains should
be ignored, even if present.
Then -g will mean just to select a callchain mode to be applied to all
events not explicitely setting some other callchain mode, i.e. a default
callchain mode, and --no-call-graph will set
symbol_conf.ignore_callchains with that clear intention.
That too will at some point become a per evsel thing, that tools can set
for all or just a few of its evsels.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: https://lkml.kernel.org/n/tip-0sas5cm4dsw2obn75g7ruz69@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-05-29 23:59:24 +07:00
|
|
|
if (!hist_entry__has_callchains(he) || !symbol_conf.use_callchain)
|
2016-02-16 21:08:19 +07:00
|
|
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
bool leftmost = true;
|
2009-10-03 20:42:45 +07:00
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
p = &hists->entries_in->rb_root.rb_node;
|
2011-10-06 03:50:23 +07:00
|
|
|
|
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.
|
|
|
|
*/
|
2018-03-07 22:50:06 +07:00
|
|
|
mem_info__zput(entry->mem_info);
|
2013-04-01 18:35:18 +07:00
|
|
|
|
2019-06-28 16:22:59 +07:00
|
|
|
block_info__zput(entry->block_info);
|
|
|
|
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
else {
|
2009-10-03 20:42:45 +07:00
|
|
|
p = &(*p)->rb_right;
|
2018-12-07 02:18:18 +07:00
|
|
|
leftmost = false;
|
|
|
|
}
|
2009-10-03 20:42:45 +07:00
|
|
|
}
|
|
|
|
|
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);
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_insert_color_cached(&he->rb_node_in, hists->entries_in, leftmost);
|
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;
|
|
|
|
}
|
|
|
|
|
2019-03-11 21:44:58 +07:00
|
|
|
static unsigned random_max(unsigned high)
|
|
|
|
{
|
|
|
|
unsigned thresh = -high % high;
|
|
|
|
for (;;) {
|
|
|
|
unsigned r = random();
|
|
|
|
if (r >= thresh)
|
|
|
|
return r % high;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void hists__res_sample(struct hist_entry *he, struct perf_sample *sample)
|
|
|
|
{
|
|
|
|
struct res_sample *r;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
if (he->num_res < symbol_conf.res_sample) {
|
|
|
|
j = he->num_res++;
|
|
|
|
} else {
|
|
|
|
j = random_max(symbol_conf.res_sample);
|
|
|
|
}
|
|
|
|
r = &he->res_samples[j];
|
|
|
|
r->time = sample->time;
|
|
|
|
r->cpu = sample->cpu;
|
|
|
|
r->tid = sample->tid;
|
|
|
|
}
|
|
|
|
|
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,
|
2019-06-28 16:22:59 +07:00
|
|
|
struct block_info *block_info,
|
2016-07-05 13:56:05 +07:00
|
|
|
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 = {
|
2019-11-05 02:02:35 +07:00
|
|
|
.mg = al->mg,
|
2012-02-10 05:21:01 +07:00
|
|
|
.map = al->map,
|
|
|
|
.sym = al->sym,
|
|
|
|
},
|
2019-03-05 22:25:31 +07:00
|
|
|
.srcline = (char *) al->srcline,
|
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,
|
2019-06-28 16:22:59 +07:00
|
|
|
.block_info = block_info,
|
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,
|
2019-03-11 21:44:54 +07:00
|
|
|
.time = hist_time(sample->time),
|
2018-06-08 00:42:27 +07:00
|
|
|
}, *he = hists__findnew_entry(hists, &entry, al, sample_self);
|
2012-02-10 05:21:01 +07:00
|
|
|
|
2018-06-08 00:42:27 +07:00
|
|
|
if (!hists->has_callchains && he && he->callchain_size != 0)
|
|
|
|
hists->has_callchains = true;
|
2019-03-11 21:44:58 +07:00
|
|
|
if (he && symbol_conf.res_sample)
|
|
|
|
hists__res_sample(he, sample);
|
2018-06-08 00:42:27 +07:00
|
|
|
return he;
|
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)
|
|
|
|
{
|
2019-06-28 16:22:59 +07:00
|
|
|
return __hists__add_entry(hists, al, sym_parent, bi, mi, NULL,
|
2016-07-05 13:56:05 +07:00
|
|
|
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)
|
|
|
|
{
|
2019-06-28 16:22:59 +07:00
|
|
|
return __hists__add_entry(hists, al, sym_parent, bi, mi, NULL,
|
2016-07-05 13:56:05 +07:00
|
|
|
sample, sample_self, ops);
|
|
|
|
}
|
|
|
|
|
2019-06-28 16:22:59 +07:00
|
|
|
struct hist_entry *hists__add_entry_block(struct hists *hists,
|
|
|
|
struct addr_location *al,
|
|
|
|
struct block_info *block_info)
|
|
|
|
{
|
|
|
|
struct hist_entry entry = {
|
|
|
|
.block_info = block_info,
|
|
|
|
.hists = hists,
|
2019-11-07 14:47:16 +07:00
|
|
|
.ms = {
|
2019-11-05 02:02:35 +07:00
|
|
|
.mg = al->mg,
|
2019-11-07 14:47:16 +07:00
|
|
|
.map = al->map,
|
|
|
|
.sym = al->sym,
|
|
|
|
},
|
2019-06-28 16:22:59 +07:00
|
|
|
}, *he = hists__findnew_entry(hists, &entry, al, false);
|
|
|
|
|
|
|
|
return he;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2019-07-21 18:23:51 +07:00
|
|
|
struct 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;
|
|
|
|
|
2019-11-05 02:02:35 +07:00
|
|
|
al->mg = bi[i].to.ms.mg;
|
2019-11-05 01:57:38 +07:00
|
|
|
al->map = bi[i].to.ms.map;
|
|
|
|
al->sym = bi[i].to.ms.sym;
|
2013-10-30 07:40:34 +07:00
|
|
|
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;
|
2019-07-21 18:23:51 +07:00
|
|
|
struct 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;
|
|
|
|
|
2019-11-05 01:57:38 +07:00
|
|
|
if (iter->hide_unresolved && !(bi[i].from.ms.sym && bi[i].to.ms.sym))
|
2013-10-30 07:40:34 +07:00
|
|
|
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)
|
|
|
|
{
|
2019-07-21 18:23:51 +07:00
|
|
|
struct evsel *evsel = iter->evsel;
|
2013-10-30 07:40:34 +07:00
|
|
|
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;
|
2019-07-21 18:23:51 +07:00
|
|
|
struct evsel *evsel = iter->evsel;
|
2013-10-30 07:40:34 +07:00
|
|
|
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.
|
|
|
|
*/
|
2018-02-16 19:36:19 +07:00
|
|
|
he_cache = malloc(sizeof(*he_cache) * (callchain_cursor.nr + 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)
|
|
|
|
{
|
2019-07-21 18:23:51 +07:00
|
|
|
struct 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)
|
|
|
|
{
|
2019-07-21 18:23:51 +07:00
|
|
|
struct evsel *evsel = iter->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;
|
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 = {
|
2019-11-05 02:02:35 +07:00
|
|
|
.mg = al->mg,
|
2013-10-31 08:05:29 +07:00
|
|
|
.map = al->map,
|
|
|
|
.sym = al->sym,
|
|
|
|
},
|
2019-03-05 22:25:31 +07:00
|
|
|
.srcline = (char *) al->srcline,
|
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
|
|
|
|
perf hists: Check if a hist_entry has callchains before using them
So far if we use 'perf record -g' this will make
symbol_conf.use_callchain 'true' and logic will assume that all events
have callchains enabled, but ever since we added the possibility of
setting up callchains for some events (e.g.: -e
cycles/call-graph=dwarf/) while not for others, we limit usage scenarios
by looking at that symbol_conf.use_callchain global boolean, we better
look at each event attributes.
On the road to that we need to look if a hist_entry has callchains, that
is, to go from hist_entry->hists to the evsel that contains it, to then
look at evsel->sample_type for PERF_SAMPLE_CALLCHAIN.
The next step is to add a symbol_conf.ignore_callchains global, to use
in the places where what we really want to know is if callchains should
be ignored, even if present.
Then -g will mean just to select a callchain mode to be applied to all
events not explicitely setting some other callchain mode, i.e. a default
callchain mode, and --no-call-graph will set
symbol_conf.ignore_callchains with that clear intention.
That too will at some point become a per evsel thing, that tools can set
for all or just a few of its evsels.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: https://lkml.kernel.org/n/tip-0sas5cm4dsw2obn75g7ruz69@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-05-29 23:59:24 +07:00
|
|
|
if (hist_entry__has_callchains(he) && symbol_conf.use_callchain)
|
2014-12-22 11:44:14 +07:00
|
|
|
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;
|
|
|
|
|
2018-05-24 21:20:39 +07:00
|
|
|
if (al)
|
2017-01-06 13:23:31 +07:00
|
|
|
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);
|
2019-03-16 15:05:49 +07:00
|
|
|
if (err) {
|
|
|
|
map__put(alm);
|
2013-10-30 07:40:34 +07:00
|
|
|
return err;
|
2019-03-16 15:05:49 +07:00
|
|
|
}
|
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) {
|
2019-11-05 01:57:38 +07:00
|
|
|
map__zput(he->branch_info->from.ms.map);
|
|
|
|
map__zput(he->branch_info->to.ms.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) {
|
2019-11-05 01:57:38 +07:00
|
|
|
map__zput(he->mem_info->iaddr.ms.map);
|
|
|
|
map__zput(he->mem_info->daddr.ms.map);
|
2018-03-07 22:50:06 +07:00
|
|
|
mem_info__zput(he->mem_info);
|
2015-06-16 09:29:51 +07:00
|
|
|
}
|
|
|
|
|
2019-06-28 16:23:01 +07:00
|
|
|
if (he->block_info)
|
|
|
|
block_info__zput(he->block_info);
|
|
|
|
|
2019-03-11 21:44:58 +07:00
|
|
|
zfree(&he->res_samples);
|
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])
|
2019-07-04 22:06:20 +07:00
|
|
|
zfree(&he->srcfile);
|
2014-12-30 12:38:13 +07:00
|
|
|
free_callchain(he->callchain);
|
2019-07-04 22:06:20 +07:00
|
|
|
zfree(&he->trace_output);
|
|
|
|
zfree(&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
|
2018-12-03 17:22:00 +07:00
|
|
|
* pre-calculated max length for this column, otherwise don't bother adding
|
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
|
|
|
* 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,
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *root,
|
2016-02-24 22:13:34 +07:00
|
|
|
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
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_node **p = &root->rb_root.rb_node;
|
2016-02-24 22:13:34 +07:00
|
|
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
bool leftmost = true;
|
2016-02-24 22:13:34 +07:00
|
|
|
|
|
|
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
else {
|
2016-02-24 22:13:34 +07:00
|
|
|
p = &parent->rb_right;
|
2018-12-07 02:18:18 +07:00
|
|
|
leftmost = false;
|
|
|
|
}
|
2016-02-24 22:13:34 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_insert_color_cached(&new->rb_node_in, root, leftmost);
|
2016-02-24 22:13:34 +07:00
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hists__hierarchy_insert_entry(struct hists *hists,
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *root,
|
2016-02-24 22:13:34 +07:00
|
|
|
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;
|
|
|
|
|
perf hists: Check if a hist_entry has callchains before using them
So far if we use 'perf record -g' this will make
symbol_conf.use_callchain 'true' and logic will assume that all events
have callchains enabled, but ever since we added the possibility of
setting up callchains for some events (e.g.: -e
cycles/call-graph=dwarf/) while not for others, we limit usage scenarios
by looking at that symbol_conf.use_callchain global boolean, we better
look at each event attributes.
On the road to that we need to look if a hist_entry has callchains, that
is, to go from hist_entry->hists to the evsel that contains it, to then
look at evsel->sample_type for PERF_SAMPLE_CALLCHAIN.
The next step is to add a symbol_conf.ignore_callchains global, to use
in the places where what we really want to know is if callchains should
be ignored, even if present.
Then -g will mean just to select a callchain mode to be applied to all
events not explicitely setting some other callchain mode, i.e. a default
callchain mode, and --no-call-graph will set
symbol_conf.ignore_callchains with that clear intention.
That too will at some point become a per evsel thing, that tools can set
for all or just a few of its evsels.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: https://lkml.kernel.org/n/tip-0sas5cm4dsw2obn75g7ruz69@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-05-29 23:59:24 +07:00
|
|
|
if (hist_entry__has_callchains(new_he) &&
|
|
|
|
symbol_conf.use_callchain) {
|
2016-02-24 22:13:34 +07:00
|
|
|
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,
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *root,
|
2016-03-24 19:52:17 +07:00
|
|
|
struct hist_entry *he)
|
2009-09-28 20:32:55 +07:00
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_node **p = &root->rb_root.rb_node;
|
2009-09-28 20:32:55 +07:00
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *iter;
|
|
|
|
int64_t cmp;
|
2018-12-07 02:18:18 +07:00
|
|
|
bool leftmost = true;
|
2009-09-28 20:32:55 +07:00
|
|
|
|
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
|
|
|
|
perf hists: Check if a hist_entry has callchains before using them
So far if we use 'perf record -g' this will make
symbol_conf.use_callchain 'true' and logic will assume that all events
have callchains enabled, but ever since we added the possibility of
setting up callchains for some events (e.g.: -e
cycles/call-graph=dwarf/) while not for others, we limit usage scenarios
by looking at that symbol_conf.use_callchain global boolean, we better
look at each event attributes.
On the road to that we need to look if a hist_entry has callchains, that
is, to go from hist_entry->hists to the evsel that contains it, to then
look at evsel->sample_type for PERF_SAMPLE_CALLCHAIN.
The next step is to add a symbol_conf.ignore_callchains global, to use
in the places where what we really want to know is if callchains should
be ignored, even if present.
Then -g will mean just to select a callchain mode to be applied to all
events not explicitely setting some other callchain mode, i.e. a default
callchain mode, and --no-call-graph will set
symbol_conf.ignore_callchains with that clear intention.
That too will at some point become a per evsel thing, that tools can set
for all or just a few of its evsels.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: https://lkml.kernel.org/n/tip-0sas5cm4dsw2obn75g7ruz69@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-05-29 23:59:24 +07:00
|
|
|
if (hist_entry__has_callchains(he) && 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;
|
2018-12-07 02:18:18 +07:00
|
|
|
else {
|
2009-09-28 20:32:55 +07:00
|
|
|
p = &(*p)->rb_right;
|
2018-12-07 02:18:18 +07:00
|
|
|
leftmost = false;
|
|
|
|
}
|
2009-09-28 20:32:55 +07:00
|
|
|
}
|
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);
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_insert_color_cached(&he->rb_node_in, root, leftmost);
|
2016-02-16 21:08:25 +07:00
|
|
|
return 1;
|
2009-09-28 20:32:55 +07:00
|
|
|
}
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *hists__get_rotate_entries_in(struct hists *hists)
|
2009-09-28 20:32:55 +07:00
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *root;
|
2011-10-06 03:50:23 +07:00
|
|
|
|
|
|
|
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
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *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
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
next = rb_first_cached(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
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_erase_cached(&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
|
|
|
|
2019-11-05 06:27:11 +07:00
|
|
|
static int64_t 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;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
node = rb_first_cached(&hists->entries);
|
2016-03-09 20:47:02 +07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
static void hierarchy_insert_output_entry(struct rb_root_cached *root,
|
2016-02-24 22:13:35 +07:00
|
|
|
struct hist_entry *he)
|
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_node **p = &root->rb_root.rb_node;
|
2016-02-24 22:13:35 +07:00
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *iter;
|
2016-03-08 02:44:46 +07:00
|
|
|
struct perf_hpp_fmt *fmt;
|
2018-12-07 02:18:18 +07:00
|
|
|
bool leftmost = true;
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
else {
|
2016-02-24 22:13:35 +07:00
|
|
|
p = &parent->rb_right;
|
2018-12-07 02:18:18 +07:00
|
|
|
leftmost = false;
|
|
|
|
}
|
2016-02-24 22:13:35 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
rb_link_node(&he->rb_node, parent, p);
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_insert_color_cached(&he->rb_node, root, leftmost);
|
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,
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *root_in,
|
|
|
|
struct rb_root_cached *root_out,
|
2016-02-24 22:13:35 +07:00
|
|
|
u64 min_callchain_hits,
|
|
|
|
bool use_callchain)
|
|
|
|
{
|
|
|
|
struct rb_node *node;
|
|
|
|
struct hist_entry *he;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
*root_out = RB_ROOT_CACHED;
|
|
|
|
node = rb_first_cached(root_in);
|
2016-02-24 22:13:35 +07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
static void __hists__insert_output_entry(struct rb_root_cached *entries,
|
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 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
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_node **p = &entries->rb_root.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;
|
2018-12-07 02:18:18 +07:00
|
|
|
bool leftmost = true;
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
else {
|
2009-09-28 20:32:55 +07:00
|
|
|
p = &(*p)->rb_right;
|
2018-12-07 02:18:18 +07:00
|
|
|
leftmost = false;
|
|
|
|
}
|
2009-09-28 20:32:55 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
rb_link_node(&he->rb_node, parent, p);
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_insert_color_cached(&he->rb_node, entries, leftmost);
|
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,
|
2019-02-04 21:18:06 +07:00
|
|
|
bool use_callchain, hists__resort_cb_t cb,
|
|
|
|
void *cb_arg)
|
2009-09-28 20:32:55 +07:00
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *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;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
next = rb_first_cached(root);
|
|
|
|
hists->entries = RB_ROOT_CACHED;
|
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
|
|
|
|
2019-02-04 21:18:06 +07:00
|
|
|
if (cb && cb(n, cb_arg))
|
2016-08-02 01:02:34 +07:00
|
|
|
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
|
|
|
|
2019-07-21 18:23:51 +07:00
|
|
|
void perf_evsel__output_resort_cb(struct evsel *evsel, struct ui_progress *prog,
|
2019-02-04 21:18:07 +07:00
|
|
|
hists__resort_cb_t cb, void *cb_arg)
|
2016-01-18 16:23:59 +07:00
|
|
|
{
|
|
|
|
bool use_callchain;
|
|
|
|
|
|
|
|
if (evsel && symbol_conf.use_callchain && !symbol_conf.show_ref_callgraph)
|
2018-05-29 02:00:29 +07:00
|
|
|
use_callchain = evsel__has_callchain(evsel);
|
2016-01-18 16:23:59 +07:00
|
|
|
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;
|
|
|
|
|
2019-02-04 21:18:07 +07:00
|
|
|
output_resort(evsel__hists(evsel), prog, use_callchain, cb, cb_arg);
|
|
|
|
}
|
|
|
|
|
2019-07-21 18:23:51 +07:00
|
|
|
void perf_evsel__output_resort(struct evsel *evsel, struct ui_progress *prog)
|
2019-02-04 21:18:07 +07:00
|
|
|
{
|
|
|
|
return perf_evsel__output_resort_cb(evsel, prog, NULL, NULL);
|
2016-01-18 16:24:00 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void hists__output_resort(struct hists *hists, struct ui_progress *prog)
|
|
|
|
{
|
2019-02-04 21:18:06 +07:00
|
|
|
output_resort(hists, prog, symbol_conf.use_callchain, NULL, NULL);
|
2016-08-02 01:02:34 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void hists__output_resort_cb(struct hists *hists, struct ui_progress *prog,
|
|
|
|
hists__resort_cb_t cb)
|
|
|
|
{
|
2019-02-04 21:18:06 +07:00
|
|
|
output_resort(hists, prog, symbol_conf.use_callchain, cb, NULL);
|
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)) {
|
2018-12-07 02:18:18 +07:00
|
|
|
node = rb_last(&he->hroot_out.rb_root);
|
2016-02-24 22:13:36 +07:00
|
|
|
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))
|
2018-12-07 02:18:18 +07:00
|
|
|
node = rb_first_cached(&he->hroot_out);
|
2016-02-24 22:13:36 +07:00
|
|
|
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;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
node = rb_first_cached(&he->hroot_out);
|
2016-02-26 19:13:16 +07:00
|
|
|
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);
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
for (nd = rb_first_cached(&hists->entries); nd; nd = rb_next(nd)) {
|
2015-09-04 21:45:45 +07:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
static void resort_filtered_entry(struct rb_root_cached *root,
|
|
|
|
struct hist_entry *he)
|
2016-02-24 22:13:39 +07:00
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_node **p = &root->rb_root.rb_node;
|
2016-02-24 22:13:39 +07:00
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *iter;
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached new_root = RB_ROOT_CACHED;
|
2016-02-24 22:13:39 +07:00
|
|
|
struct rb_node *nd;
|
2018-12-07 02:18:18 +07:00
|
|
|
bool leftmost = true;
|
2016-02-24 22:13:39 +07:00
|
|
|
|
|
|
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
else {
|
2016-02-24 22:13:39 +07:00
|
|
|
p = &(*p)->rb_right;
|
2018-12-07 02:18:18 +07:00
|
|
|
leftmost = false;
|
|
|
|
}
|
2016-02-24 22:13:39 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
rb_link_node(&he->rb_node, parent, p);
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_insert_color_cached(&he->rb_node, root, leftmost);
|
2016-02-24 22:13:39 +07:00
|
|
|
|
|
|
|
if (he->leaf || he->filtered)
|
|
|
|
return;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
nd = rb_first_cached(&he->hroot_out);
|
2016-02-24 22:13:39 +07:00
|
|
|
while (nd) {
|
|
|
|
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
|
|
|
|
|
|
|
nd = rb_next(nd);
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_erase_cached(&h->rb_node, &he->hroot_out);
|
2016-02-24 22:13:39 +07:00
|
|
|
|
|
|
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached new_root = RB_ROOT_CACHED;
|
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);
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
nd = rb_first_cached(&hists->entries);
|
2016-02-24 22:13:38 +07:00
|
|
|
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.
|
|
|
|
*/
|
2018-12-07 02:18:18 +07:00
|
|
|
nd = rb_first_cached(&hists->entries);
|
2016-02-24 22:13:39 +07:00
|
|
|
while (nd) {
|
|
|
|
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
|
|
|
|
|
|
|
|
nd = rb_next(nd);
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_erase_cached(&h->rb_node, &hists->entries);
|
2016-02-24 22:13:39 +07:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *root;
|
2012-12-10 15:29:55 +07:00
|
|
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
bool leftmost = true;
|
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;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
p = &root->rb_root.rb_node;
|
2012-12-10 15:29:55 +07:00
|
|
|
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
else {
|
2012-11-09 04:03:09 +07:00
|
|
|
p = &(*p)->rb_right;
|
2018-12-07 02:18:18 +07:00
|
|
|
leftmost = false;
|
|
|
|
}
|
2012-11-09 04:03:09 +07:00
|
|
|
}
|
|
|
|
|
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);
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_insert_color_cached(&he->rb_node_in, root, leftmost);
|
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,
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *root,
|
2016-09-13 14:45:47 +07:00
|
|
|
struct hist_entry *pair)
|
|
|
|
{
|
|
|
|
struct rb_node **p;
|
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct hist_entry *he;
|
|
|
|
struct perf_hpp_fmt *fmt;
|
2018-12-07 02:18:18 +07:00
|
|
|
bool leftmost = true;
|
2016-09-13 14:45:47 +07:00
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
p = &root->rb_root.rb_node;
|
2016-09-13 14:45:47 +07:00
|
|
|
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;
|
2018-12-07 02:18:18 +07:00
|
|
|
else {
|
2016-09-13 14:45:47 +07:00
|
|
|
p = &parent->rb_right;
|
2018-12-07 02:18:18 +07:00
|
|
|
leftmost = false;
|
|
|
|
}
|
2016-09-13 14:45:47 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
he = hist_entry__new(pair, true);
|
|
|
|
if (he) {
|
|
|
|
rb_link_node(&he->rb_node_in, parent, p);
|
2018-12-07 02:18:18 +07:00
|
|
|
rb_insert_color_cached(&he->rb_node_in, root, leftmost);
|
2016-09-13 14:45:47 +07:00
|
|
|
|
|
|
|
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))
|
2018-12-07 02:18:18 +07:00
|
|
|
n = hists->entries_collapsed.rb_root.rb_node;
|
2012-12-10 15:29:55 +07:00
|
|
|
else
|
2018-12-07 02:18:18 +07:00
|
|
|
n = hists->entries_in->rb_root.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;
|
|
|
|
}
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
static struct hist_entry *hists__find_hierarchy_entry(struct rb_root_cached *root,
|
2016-09-13 14:45:46 +07:00
|
|
|
struct hist_entry *he)
|
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_node *n = root->rb_root.rb_node;
|
2016-09-13 14:45:46 +07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
static void hists__match_hierarchy(struct rb_root_cached *leader_root,
|
|
|
|
struct rb_root_cached *other_root)
|
2016-09-13 14:45:46 +07:00
|
|
|
{
|
|
|
|
struct rb_node *nd;
|
|
|
|
struct hist_entry *pos, *pair;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
for (nd = rb_first_cached(leader_root); nd; nd = rb_next(nd)) {
|
2016-09-13 14:45:46 +07:00
|
|
|
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)
|
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *root;
|
2012-11-09 03:54:33 +07:00
|
|
|
struct rb_node *nd;
|
2019-08-28 06:15:55 +07:00
|
|
|
struct hist_entry *pos, *pair;
|
2012-11-09 03:54:33 +07:00
|
|
|
|
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;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
for (nd = rb_first_cached(root); nd; nd = rb_next(nd)) {
|
2012-12-10 15:29:55 +07:00
|
|
|
pos = rb_entry(nd, struct hist_entry, rb_node_in);
|
2012-11-09 03:54:33 +07:00
|
|
|
pair = hists__find_entry(other, pos);
|
|
|
|
|
2019-08-28 06:15:55 +07:00
|
|
|
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,
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *leader_root,
|
|
|
|
struct rb_root_cached *other_root)
|
2016-09-13 14:45:47 +07:00
|
|
|
{
|
|
|
|
struct rb_node *nd;
|
|
|
|
struct hist_entry *pos, *leader;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
for (nd = rb_first_cached(other_root); nd; nd = rb_next(nd)) {
|
2016-09-13 14:45:47 +07:00
|
|
|
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)
|
|
|
|
{
|
2018-12-07 02:18:18 +07:00
|
|
|
struct rb_root_cached *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;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
for (nd = rb_first_cached(root); nd; nd = rb_next(nd)) {
|
2012-12-10 15:29:55 +07:00
|
|
|
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
|
|
|
|
2019-08-28 06:15:55 +07:00
|
|
|
int hists__unlink(struct hists *hists)
|
|
|
|
{
|
|
|
|
struct rb_root_cached *root;
|
|
|
|
struct rb_node *nd;
|
|
|
|
struct hist_entry *pos;
|
|
|
|
|
|
|
|
if (hists__has(hists, need_collapse))
|
|
|
|
root = &hists->entries_collapsed;
|
|
|
|
else
|
|
|
|
root = hists->entries_in;
|
|
|
|
|
|
|
|
for (nd = rb_first_cached(root); nd; nd = rb_next(nd)) {
|
|
|
|
pos = rb_entry(nd, struct hist_entry, rb_node_in);
|
|
|
|
list_del_init(&pos->pairs.node);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-18 22:24:49 +07:00
|
|
|
void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
|
2019-11-07 14:47:15 +07:00
|
|
|
struct perf_sample *sample, bool nonany_branch_mode,
|
|
|
|
u64 *total_cycles)
|
2015-07-18 22:24:49 +07:00
|
|
|
{
|
|
|
|
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;
|
2019-11-07 14:47:15 +07:00
|
|
|
|
|
|
|
if (total_cycles)
|
|
|
|
*total_cycles += bi[i].flags.cycles;
|
2015-07-18 22:24:49 +07:00
|
|
|
}
|
|
|
|
free(bi);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-11 01:49:21 +07:00
|
|
|
|
2019-07-21 18:23:52 +07:00
|
|
|
size_t perf_evlist__fprintf_nr_events(struct evlist *evlist, FILE *fp)
|
2014-10-11 01:49:21 +07:00
|
|
|
{
|
2019-07-21 18:23:51 +07:00
|
|
|
struct evsel *pos;
|
2014-10-11 01:49:21 +07:00
|
|
|
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
|
|
|
|
2018-04-03 00:24:28 +07:00
|
|
|
int __hists__scnprintf_title(struct hists *hists, char *bf, size_t size, bool show_freq)
|
|
|
|
{
|
|
|
|
char unit;
|
|
|
|
int printed;
|
|
|
|
const struct dso *dso = hists->dso_filter;
|
2019-05-27 13:11:49 +07:00
|
|
|
struct thread *thread = hists->thread_filter;
|
2018-04-03 00:24:28 +07:00
|
|
|
int socket_id = hists->socket_filter;
|
|
|
|
unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
|
|
|
|
u64 nr_events = hists->stats.total_period;
|
2019-07-21 18:23:51 +07:00
|
|
|
struct evsel *evsel = hists_to_evsel(hists);
|
2018-04-03 00:24:28 +07:00
|
|
|
const char *ev_name = perf_evsel__name(evsel);
|
|
|
|
char buf[512], sample_freq_str[64] = "";
|
|
|
|
size_t buflen = sizeof(buf);
|
|
|
|
char ref[30] = " show reference callgraph, ";
|
|
|
|
bool enable_ref = false;
|
|
|
|
|
|
|
|
if (symbol_conf.filter_relative) {
|
|
|
|
nr_samples = hists->stats.nr_non_filtered_samples;
|
|
|
|
nr_events = hists->stats.total_non_filtered_period;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (perf_evsel__is_group_event(evsel)) {
|
2019-07-21 18:23:51 +07:00
|
|
|
struct evsel *pos;
|
2018-04-03 00:24:28 +07:00
|
|
|
|
|
|
|
perf_evsel__group_desc(evsel, buf, buflen);
|
|
|
|
ev_name = buf;
|
|
|
|
|
|
|
|
for_each_group_member(pos, evsel) {
|
|
|
|
struct hists *pos_hists = evsel__hists(pos);
|
|
|
|
|
|
|
|
if (symbol_conf.filter_relative) {
|
|
|
|
nr_samples += pos_hists->stats.nr_non_filtered_samples;
|
|
|
|
nr_events += pos_hists->stats.total_non_filtered_period;
|
|
|
|
} else {
|
|
|
|
nr_samples += pos_hists->stats.nr_events[PERF_RECORD_SAMPLE];
|
|
|
|
nr_events += pos_hists->stats.total_period;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (symbol_conf.show_ref_callgraph &&
|
|
|
|
strstr(ev_name, "call-graph=no"))
|
|
|
|
enable_ref = true;
|
|
|
|
|
|
|
|
if (show_freq)
|
2019-07-21 18:24:29 +07:00
|
|
|
scnprintf(sample_freq_str, sizeof(sample_freq_str), " %d Hz,", evsel->core.attr.sample_freq);
|
2018-04-03 00:24:28 +07:00
|
|
|
|
|
|
|
nr_samples = convert_unit(nr_samples, &unit);
|
|
|
|
printed = scnprintf(bf, size,
|
|
|
|
"Samples: %lu%c of event%s '%s',%s%sEvent count (approx.): %" PRIu64,
|
2019-07-21 18:24:46 +07:00
|
|
|
nr_samples, unit, evsel->core.nr_members > 1 ? "s" : "",
|
2018-04-03 00:24:28 +07:00
|
|
|
ev_name, sample_freq_str, enable_ref ? ref : " ", nr_events);
|
|
|
|
|
|
|
|
|
|
|
|
if (hists->uid_filter_str)
|
|
|
|
printed += snprintf(bf + printed, size - printed,
|
|
|
|
", UID: %s", hists->uid_filter_str);
|
|
|
|
if (thread) {
|
|
|
|
if (hists__has(hists, thread)) {
|
|
|
|
printed += scnprintf(bf + printed, size - printed,
|
|
|
|
", Thread: %s(%d)",
|
|
|
|
(thread->comm_set ? thread__comm_str(thread) : ""),
|
|
|
|
thread->tid);
|
|
|
|
} else {
|
|
|
|
printed += scnprintf(bf + printed, size - printed,
|
|
|
|
", Thread: %s",
|
|
|
|
(thread->comm_set ? thread__comm_str(thread) : ""));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (dso)
|
|
|
|
printed += scnprintf(bf + printed, size - printed,
|
|
|
|
", DSO: %s", dso->short_name);
|
|
|
|
if (socket_id > -1)
|
|
|
|
printed += scnprintf(bf + printed, size - printed,
|
|
|
|
", Processor Socket: %d", socket_id);
|
|
|
|
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
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));
|
2018-12-07 02:18:18 +07:00
|
|
|
hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT_CACHED;
|
2014-10-10 02:16:00 +07:00
|
|
|
hists->entries_in = &hists->entries_in_array[0];
|
2018-12-07 02:18:18 +07:00
|
|
|
hists->entries_collapsed = RB_ROOT_CACHED;
|
|
|
|
hists->entries = RB_ROOT_CACHED;
|
2014-10-10 02:16:00 +07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
static void hists__delete_remaining_entries(struct rb_root_cached *root)
|
2015-12-10 14:53:20 +07:00
|
|
|
{
|
|
|
|
struct rb_node *node;
|
|
|
|
struct hist_entry *he;
|
|
|
|
|
2018-12-07 02:18:18 +07:00
|
|
|
while (!RB_EMPTY_ROOT(&root->rb_root)) {
|
|
|
|
node = rb_first_cached(root);
|
|
|
|
rb_erase_cached(node, root);
|
2015-12-10 14:53:20 +07:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-07-21 18:23:51 +07:00
|
|
|
static void hists_evsel__exit(struct evsel *evsel)
|
2015-12-09 09:11:29 +07:00
|
|
|
{
|
|
|
|
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) {
|
2019-07-04 22:13:46 +07:00
|
|
|
list_del_init(&fmt->list);
|
2016-03-08 02:44:45 +07:00
|
|
|
free(fmt);
|
|
|
|
}
|
2019-07-04 22:13:46 +07:00
|
|
|
list_del_init(&node->list);
|
2016-03-08 02:44:45 +07:00
|
|
|
free(node);
|
|
|
|
}
|
2015-12-09 09:11:29 +07:00
|
|
|
}
|
|
|
|
|
2019-07-21 18:23:51 +07:00
|
|
|
static int hists_evsel__init(struct evsel *evsel)
|
2016-01-07 16:14:10 +07:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|