Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1644868 > unrolled thread
| Started by | Milian Wolff <milian.wolff@kdab.com> |
|---|---|
| First post | 2017-05-18 21:40 +0200 |
| Last post | 2017-05-22 14:10 +0200 |
| Articles | 12 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH 0/7] generate full callchain cursor entries for inlined frames Milian Wolff <milian.wolff@kdab.com> - 2017-05-18 21:40 +0200
[PATCH 3/7] perf report: create real callchain entries for inlined frames Milian Wolff <milian.wolff@kdab.com> - 2017-05-18 21:40 +0200
Re: [PATCH 3/7] perf report: create real callchain entries for inlined frames Namhyung Kim <namhyung@kernel.org> - 2017-05-22 14:30 +0200
Re: [PATCH 3/7] perf report: create real callchain entries for inlined frames Milian Wolff <milian.wolff@kdab.com> - 2017-05-24 13:50 +0200
Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames Milian Wolff <milian.wolff@kdab.com> - 2017-05-18 22:10 +0200
Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames Namhyung Kim <namhyung@kernel.org> - 2017-05-22 11:10 +0200
Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames Milian Wolff <milian.wolff@kdab.com> - 2017-05-24 13:50 +0200
Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames Milian Wolff <milian.wolff@kdab.com> - 2017-05-24 15:50 +0200
Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames Namhyung Kim <namhyung@kernel.org> - 2017-05-24 17:10 +0200
Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames Milian Wolff <milian.wolff@kdab.com> - 2017-05-29 20:40 +0200
Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames Namhyung Kim <namhyung@kernel.org> - 2017-05-30 03:40 +0200
Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames Namhyung Kim <namhyung@kernel.org> - 2017-05-22 14:10 +0200
| From | Milian Wolff <milian.wolff@kdab.com> |
|---|---|
| Date | 2017-05-18 21:40 +0200 |
| Subject | [PATCH 0/7] generate full callchain cursor entries for inlined frames |
| Message-ID | <tIzrb-7No-3@gated-at.bofh.it> |
This series of patches completely reworks the way inline frames are handled.
Instead of querying for the inline nodes on-demand in the individual tools,
we now create proper callchain nodes for inlined frames. The advantages this
approach brings are numerous:
- less duplicated code in the individual browser
- aggregated cost for inlined frames for the --children top-down list
- various bug fixes that arose from querying for a srcline/symbol based on
the IP of a sample, which will always point to the last inlined frame
instead of the corresponding non-inlined frame
- overall much better support for visualizing cost for heavily-inlined C++
code, which simply was confusing and unreliably before
- srcline honors the global setting as to whether full paths or basenames
should be shown
For comparison, below lists the output before and after for `perf script`
and `perf report`. The example file I used to generate the perf data is:
~~~~~
$ cat inlining.cpp
#include <complex>
#include <cmath>
#include <random>
#include <iostream>
using namespace std;
int main()
{
uniform_real_distribution<double> uniform(-1E5, 1E5);
default_random_engine engine;
double s = 0;
for (int i = 0; i < 10000000; ++i) {
s += norm(complex<double>(uniform(engine), uniform(engine)));
}
cout << s << '\n';
return 0;
}
$ g++ -O2 -g -o inlining inlining.cpp
$ perf record --call-graph dwarf ./inlining
~~~~~
Now, the (broken) status-quo looks like this. Look for "NOTE:" to see some
of my comments that outline the various issues I'm trying to solve by this
patch series.
~~~~~
$ perf script --inline
...
inlining 11083 97459.356656: 33680 cycles:
214f7 __hypot_finite (/usr/lib/libm-2.25.so)
ace3 hypot (/usr/lib/libm-2.25.so)
a4a main (/home/milian/projects/src/perf-tests/inlining)
std::__complex_abs
std::abs<double>
std::_Norm_helper<true>::_S_do_it<double>
std::norm<double>
main
20510 __libc_start_main (/usr/lib/libc-2.25.so)
bd9 _start (/home/milian/projects/src/perf-tests/inlining)
# NOTE: the above inlined stack is confusing: the a4a is an address into main,
# which is the non-inlined symbol. the entry with the address should be
# at the end of the stack, where it's actually duplicated once more but
# there it's missing the address
...
$ perf report -s sym -g srcline -i perf.inlining.data --inline --stdio
...
--38.86%--_start
__libc_start_main
|
|--15.68%--main random.tcc:3326
| /home/milian/projects/src/perf-tests/inlining.cpp:14 (inline)
| /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
| /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
| /usr/include/c++/6.3.1/bits/random.h:185 (inline)
| /usr/include/c++/6.3.1/bits/random.tcc:3326 (inline)
|
|--10.36%--main random.h:143
| /home/milian/projects/src/perf-tests/inlining.cpp:14 (inline)
| /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
| /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
| /usr/include/c++/6.3.1/bits/random.h:185 (inline)
| /usr/include/c++/6.3.1/bits/random.tcc:3332 (inline)
| /usr/include/c++/6.3.1/bits/random.h:332 (inline)
| /usr/include/c++/6.3.1/bits/random.h:151 (inline)
| /usr/include/c++/6.3.1/bits/random.h:143 (inline)
|
|--5.66%--main random.tcc:3332
| /home/milian/projects/src/perf-tests/inlining.cpp:14 (inline)
| /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
| /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
| /usr/include/c++/6.3.1/bits/random.h:185 (inline)
| /usr/include/c++/6.3.1/bits/random.tcc:3332 (inline)
...
# NOTE: the grouping is totally off because the first and last frame of the
inline nodes is completely bogus, since the IP is used to find the sym/srcline
which is different from the actual inlined sym/srcline.
also, the code currently displays either the inlined function name or
the corresponding filename (but in full length, instead of just the basename).
$ perf report -s sym -g srcline -i perf.inlining.data --inline --stdio --no-children
...
38.86% [.] main
|
|--15.68%--main random.tcc:3326
| /usr/include/c++/6.3.1/bits/random.tcc:3326 (inline)
| /usr/include/c++/6.3.1/bits/random.h:185 (inline)
| /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
| /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
| /home/milian/projects/src/perf-tests/inlining.cpp:14 (inline)
| __libc_start_main
| _start
...
# NOTE: the srcline for main is wrong, it should be inlining.cpp:14,
i.e. what is displayed in the line below (see also perf script issue above)
~~~~~
Afterwards, all of the above issues are resolved:
~~~~~
$ perf script --inline
...
inlining 11083 97459.356656: 33680 cycles:
214f7 __hypot_finite (/usr/lib/libm-2.25.so)
ace3 hypot (/usr/lib/libm-2.25.so)
a4a std::__complex_abs (inlined)
a4a std::abs<double> (inlined)
a4a std::_Norm_helper<true>::_S_do_it<double> (inlined)
a4a std::norm<double> (inlined)
a4a main (/home/milian/projects/src/perf-tests/inlining)
20510 __libc_start_main (/usr/lib/libc-2.25.so)
bd9 _start (/home/milian/projects/src/perf-tests/inlining)
...
# NOTE: only one main entry, at the correct position.
we do display the (repeated) instruction pointer as that ensures
interoperability with e.g. the stackcollapse-perf.pl script
$ perf report -s sym -g srcline -i perf.inlining.data --inline --stdio
...
100.00% 38.86% [.] main
|
|--61.14%--main inlining.cpp:14
| std::norm<double> complex:664 (inlined)
| std::_Norm_helper<true>::_S_do_it<double> complex:654 (inlined)
| std::abs<double> complex:597 (inlined)
| std::__complex_abs complex:589 (inlined)
| |
| |--60.29%--hypot
| | |
| | --56.03%--__hypot_finite
| |
| --0.85%--cabs
|
--38.86%--_start
__libc_start_main
|
|--38.19%--main inlining.cpp:14
| |
| |--35.59%--std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:1809 (inlined)
| | std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:1818 (inlined)
| | |
| | --34.37%--std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator() random.h:185 (inlined)
| | |
| | |--17.91%--std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3332 (inlined)
| | | |
| | | --12.24%--std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>::operator() random.h:332 (inlined)
| | | std::__detail::__mod<unsigned long, 2147483647ul, 16807ul, 0ul> random.h:151 (inlined)
| | | |
| | | |--10.36%--std::__detail::_Mod<unsigned long, 2147483647ul, 16807ul, 0ul, true, true>::__calc random.h:143 (inlined)
| | | |
| | | --1.88%--std::__detail::_Mod<unsigned long, 2147483647ul, 16807ul, 0ul, true, true>::__calc random.h:141 (inlined)
| | |
| | |--15.68%--std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3326 (inlined)
| | |
| | --0.79%--std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3335 (inlined)
| |
| --1.99%--std::norm<double> complex:664 (inlined)
| std::_Norm_helper<true>::_S_do_it<double> complex:654 (inlined)
| std::abs<double> complex:597 (inlined)
| std::__complex_abs complex:589 (inlined)
|
--0.67%--main inlining.cpp:13
...
# NOTE: still somewhat confusing due to the _start and __libc_start_main frames
that actually are *above* the main frame. But at least the stuff below
properly splits up and shows that mutiple functions got inlined into
inlining.cpp:14, not just one as before.
$ perf report -s sym -g srcline -i perf.inlining.data --inline --stdio --no-children
...
38.86% [.] main
|
|--15.68%--std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3326 (inlined)
| std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator() random.h:185 (inlined)
| std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:1818 (inlined)
| std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:1809 (inlined)
| main inlining.cpp:14
| __libc_start_main
| _start
...
# NOTE: the first and last entry of the inline stack have the correct symbol and srcline now
both function and srcline is shown, as well as the (inlined) suffix
only the basename of the srcline is shown
~~~~~
Milian Wolff (7):
perf report: remove code to handle inline frames from browsers
perf util: take elf_name as const string in dso__demangle_sym
perf report: create real callchain entries for inlined frames
perf report: use srcline from inlined frames
perf report: fall-back to function name comparison for -g srcline
perf report: mark inlined frames in output by " (inlined)" suffix
perf script: mark inlined frames and do not print DSO for them
tools/perf/ui/browsers/hists.c | 183 +++------------------------------------
tools/perf/ui/stdio/hist.c | 80 +----------------
tools/perf/util/callchain.c | 52 +++++------
tools/perf/util/callchain.h | 5 +-
tools/perf/util/dso.c | 2 +
tools/perf/util/dso.h | 1 +
tools/perf/util/evsel_fprintf.c | 37 +-------
tools/perf/util/hist.c | 5 --
tools/perf/util/machine.c | 54 +++++++++++-
tools/perf/util/sort.h | 1 -
tools/perf/util/srcline.c | 183 ++++++++++++++++++++++++++++++---------
tools/perf/util/srcline.h | 19 +++-
tools/perf/util/symbol-elf.c | 2 +-
tools/perf/util/symbol-minimal.c | 2 +-
tools/perf/util/symbol.h | 3 +-
15 files changed, 264 insertions(+), 365 deletions(-)
--
2.13.0
[toc] | [next] | [standalone]
| From | Milian Wolff <milian.wolff@kdab.com> |
|---|---|
| Date | 2017-05-18 21:40 +0200 |
| Subject | [PATCH 3/7] perf report: create real callchain entries for inlined frames |
| Message-ID | <tIzrc-7No-35@gated-at.bofh.it> |
| In reply to | #1644868 |
The inlined frames use a fake symbol that is tracked in a special
map inside the dso, which is always sorted by name. All other
entries of the symbol beside the function name are unused for
inline frames. The advantage of this approach is that all existing
users of the callchain API can now transparently display inlined
frames without having to patch their code.
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Yao Jin <yao.jin@linux.intel.com>
Signed-off-by: Milian Wolff <milian.wolff@kdab.com>
---
tools/perf/util/dso.c | 2 +
tools/perf/util/dso.h | 1 +
tools/perf/util/machine.c | 37 +++++++++++++
tools/perf/util/srcline.c | 136 ++++++++++++++++++++++++++++++++++++----------
tools/perf/util/srcline.h | 16 +++++-
tools/perf/util/symbol.h | 1 +
6 files changed, 162 insertions(+), 31 deletions(-)
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index a96a99d2369f..3563d190e6f0 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -9,6 +9,7 @@
#include "compress.h"
#include "path.h"
#include "symbol.h"
+#include "srcline.h"
#include "dso.h"
#include "machine.h"
#include "auxtrace.h"
@@ -1132,6 +1133,7 @@ void dso__delete(struct dso *dso)
dso->long_name);
for (i = 0; i < MAP__NR_TYPES; ++i)
symbols__delete(&dso->symbols[i]);
+ inlines__tree_delete(&dso->inlined_nodes);
if (dso->short_name_allocated) {
zfree((char **)&dso->short_name);
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 12350b171727..26e717278d0d 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -139,6 +139,7 @@ struct dso {
struct rb_root *root; /* root of rbtree that rb_node is in */
struct rb_root symbols[MAP__NR_TYPES];
struct rb_root symbol_names[MAP__NR_TYPES];
+ struct rb_root inlined_nodes;
struct {
u64 addr;
struct symbol *symbol;
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index d97e014c3df3..64bfa8b43706 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -2031,12 +2031,49 @@ static int thread__resolve_callchain_sample(struct thread *thread,
return 0;
}
+static int append_inlines(struct callchain_cursor *cursor,
+ struct map *map, struct symbol *sym, u64 ip)
+{
+ struct inline_node *inline_node;
+ struct inline_list *ilist;
+ u64 addr;
+
+ if (!symbol_conf.inline_name || !map || !sym)
+ return 1;
+
+ addr = map__rip_2objdump(map, ip);
+
+ inline_node = inlines__tree_find(&map->dso->inlined_nodes, addr);
+ if (!inline_node) {
+ inline_node = dso__parse_addr_inlines(map->dso, addr, sym);
+ if (!inline_node)
+ return 1;
+
+ inlines__tree_insert(&map->dso->inlined_nodes, inline_node);
+ }
+
+ list_for_each_entry(ilist, &inline_node->val, list) {
+ int ret = callchain_cursor_append(cursor, ip, map,
+ ilist->symbol, false,
+ NULL, 0, 0);
+
+ if (ret != 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static int unwind_entry(struct unwind_entry *entry, void *arg)
{
struct callchain_cursor *cursor = arg;
if (symbol_conf.hide_unresolved && entry->sym == NULL)
return 0;
+
+ if (append_inlines(cursor, entry->map, entry->sym, entry->ip) == 0)
+ return 0;
+
return callchain_cursor_append(cursor, entry->ip,
entry->map, entry->sym,
false, NULL, 0, 0);
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c
index ebc88a74e67b..25ceed8eb790 100644
--- a/tools/perf/util/srcline.c
+++ b/tools/perf/util/srcline.c
@@ -33,29 +33,19 @@ static const char *dso__name(struct dso *dso)
return dso_name;
}
-static int inline_list__append(char *filename, char *funcname, int line_nr,
- struct inline_node *node, struct dso *dso)
+static int inline_list__append(struct symbol *symbol, char *filename, int line_nr,
+ struct inline_node *node)
{
struct inline_list *ilist;
- char *demangled;
ilist = zalloc(sizeof(*ilist));
if (ilist == NULL)
return -1;
+ ilist->symbol = symbol;
ilist->filename = filename;
ilist->line_nr = line_nr;
- if (dso != NULL) {
- demangled = dso__demangle_sym(dso, 0, funcname);
- if (demangled == NULL) {
- ilist->funcname = funcname;
- } else {
- ilist->funcname = demangled;
- free(funcname);
- }
- }
-
if (callchain_param.order == ORDER_CALLEE)
list_add_tail(&ilist->list, &node->val);
else
@@ -203,19 +193,52 @@ static void addr2line_cleanup(struct a2l_data *a2l)
#define MAX_INLINE_NEST 1024
+static struct symbol *new_inline_sym(struct dso *dso,
+ struct symbol *base_sym,
+ const char *funcname)
+{
+ struct symbol *inline_sym;
+ char *demangled = NULL;
+
+ if (dso) {
+ demangled = dso__demangle_sym(dso, 0, funcname);
+ if (demangled)
+ funcname = demangled;
+ }
+
+ if (strcmp(funcname, base_sym->name) == 0) {
+ // reuse the real, existing symbol
+ inline_sym = base_sym;
+ } else {
+ // create a fake symbol for the inline frame
+ inline_sym = symbol__new(base_sym ? base_sym->start : 0,
+ base_sym ? base_sym->end : 0,
+ base_sym ? base_sym->binding : 0,
+ funcname);
+ if (inline_sym)
+ inline_sym->inlined = 1;
+ }
+
+ free(demangled);
+
+ return inline_sym;
+}
+
static int inline_list__append_dso_a2l(struct dso *dso,
- struct inline_node *node)
+ struct inline_node *node,
+ struct symbol *sym)
{
struct a2l_data *a2l = dso->a2l;
- char *funcname = a2l->funcname ? strdup(a2l->funcname) : NULL;
char *filename = a2l->filename ? strdup(a2l->filename) : NULL;
+ struct symbol *inline_sym = new_inline_sym(dso, sym, a2l->funcname);
- return inline_list__append(filename, funcname, a2l->line, node, dso);
+ return inline_list__append(inline_sym, filename, a2l->line, node);
}
static int addr2line(const char *dso_name, u64 addr,
char **file, unsigned int *line, struct dso *dso,
- bool unwind_inlines, struct inline_node *node)
+ bool unwind_inlines, struct inline_node *node,
+ struct symbol *sym)
{
int ret = 0;
struct a2l_data *a2l = dso->a2l;
@@ -241,7 +264,7 @@ static int addr2line(const char *dso_name, u64 addr,
if (unwind_inlines) {
int cnt = 0;
- if (node && inline_list__append_dso_a2l(dso, node))
+ if (node && inline_list__append_dso_a2l(dso, node, sym))
return 0;
while (bfd_find_inliner_info(a2l->abfd, &a2l->filename,
@@ -249,7 +272,7 @@ static int addr2line(const char *dso_name, u64 addr,
cnt++ < MAX_INLINE_NEST) {
if (node != NULL) {
- if (inline_list__append_dso_a2l(dso, node))
+ if (inline_list__append_dso_a2l(dso, node, sym))
return 0;
// found at least one inline frame
ret = 1;
@@ -281,7 +304,7 @@ void dso__free_a2l(struct dso *dso)
}
static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
- struct dso *dso)
+ struct dso *dso, struct symbol *sym)
{
struct inline_node *node;
@@ -294,7 +317,7 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
INIT_LIST_HEAD(&node->val);
node->addr = addr;
- if (!addr2line(dso_name, addr, NULL, NULL, dso, TRUE, node))
+ if (!addr2line(dso_name, addr, NULL, NULL, dso, TRUE, node, sym))
goto out_free_inline_node;
if (list_empty(&node->val))
@@ -334,7 +357,8 @@ static int addr2line(const char *dso_name, u64 addr,
char **file, unsigned int *line_nr,
struct dso *dso __maybe_unused,
bool unwind_inlines __maybe_unused,
- struct inline_node *node __maybe_unused)
+ struct inline_node *node __maybe_unused,
+ struct symbol *sym __maybe_unused)
{
FILE *fp;
char cmd[PATH_MAX];
@@ -374,7 +398,7 @@ void dso__free_a2l(struct dso *dso __maybe_unused)
}
static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
- struct dso *dso __maybe_unused)
+ struct dso *dso __maybe_unused, struct symbol *sym)
{
FILE *fp;
char cmd[PATH_MAX];
@@ -407,8 +431,7 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
goto out;
}
- if (inline_list__append(filename, NULL, line_nr, node,
- NULL) != 0)
+ if (inline_list__append(sym, filename, line_nr, node) != 0)
goto out;
filename = NULL;
@@ -448,7 +471,8 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
if (dso_name == NULL)
goto out;
- if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines, NULL))
+ if (!addr2line(dso_name, addr, &file, &line, dso,
+ unwind_inlines, NULL, sym))
goto out;
if (asprintf(&srcline, "%s:%u",
@@ -494,7 +518,8 @@ char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
return __get_srcline(dso, addr, sym, show_sym, show_addr, false);
}
-struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
+struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr,
+ struct symbol *sym)
{
const char *dso_name;
@@ -502,7 +527,7 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
if (dso_name == NULL)
return NULL;
- return addr2inlines(dso_name, addr, dso);
+ return addr2inlines(dso_name, addr, dso, sym);
}
void inline_node__delete(struct inline_node *node)
@@ -512,9 +537,62 @@ void inline_node__delete(struct inline_node *node)
list_for_each_entry_safe(ilist, tmp, &node->val, list) {
list_del_init(&ilist->list);
zfree(&ilist->filename);
- zfree(&ilist->funcname);
+ // only the inlined symbols are owned by the list
+ if (ilist->symbol && ilist->symbol->inlined)
+ symbol__delete(ilist->symbol);
free(ilist);
}
free(node);
}
+
+void inlines__tree_insert(struct rb_root *tree, struct inline_node *inlines)
+{
+ struct rb_node **p = &tree->rb_node;
+ struct rb_node *parent = NULL;
+ const u64 addr = inlines->addr;
+ struct inline_node *i;
+
+ while (*p != NULL) {
+ parent = *p;
+ i = rb_entry(parent, struct inline_node, rb_node);
+ if (addr < i->addr)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&inlines->rb_node, parent, p);
+ rb_insert_color(&inlines->rb_node, tree);
+}
+
+struct inline_node *inlines__tree_find(struct rb_root *tree, u64 addr)
+{
+ struct rb_node *n = tree->rb_node;
+
+ while (n) {
+ struct inline_node *i = rb_entry(n, struct inline_node,
+ rb_node);
+
+ if (addr < i->addr)
+ n = n->rb_left;
+ else if (addr > i->addr)
+ n = n->rb_right;
+ else
+ return i;
+ }
+
+ return NULL;
+}
+
+void inlines__tree_delete(struct rb_root *tree)
+{
+ struct inline_node *pos;
+ struct rb_node *next = rb_first(tree);
+
+ while (next) {
+ pos = rb_entry(next, struct inline_node, rb_node);
+ next = rb_next(&pos->rb_node);
+ rb_erase(&pos->rb_node, tree);
+ inline_node__delete(pos);
+ }
+}
diff --git a/tools/perf/util/srcline.h b/tools/perf/util/srcline.h
index 7b52ba88676e..ad9726987b80 100644
--- a/tools/perf/util/srcline.h
+++ b/tools/perf/util/srcline.h
@@ -2,6 +2,7 @@
#define PERF_SRCLINE_H
#include <linux/list.h>
+#include <linux/rbtree.h>
#include <linux/types.h>
struct dso;
@@ -17,8 +18,8 @@ void free_srcline(char *srcline);
#define SRCLINE_UNKNOWN ((char *) "??:0")
struct inline_list {
+ struct symbol *symbol;
char *filename;
- char *funcname;
unsigned int line_nr;
struct list_head list;
};
@@ -26,9 +27,20 @@ struct inline_list {
struct inline_node {
u64 addr;
struct list_head val;
+ struct rb_node rb_node;
};
-struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr);
+// parse inlined frames for the given address
+struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr,
+ struct symbol *sym);
+// free resources associated to the inline node list
void inline_node__delete(struct inline_node *node);
+// insert the inline node list into the DSO, which will take ownership
+void inlines__tree_insert(struct rb_root *tree, struct inline_node *inlines);
+// find previously inserted inline node list
+struct inline_node *inlines__tree_find(struct rb_root *tree, u64 addr);
+// delete all nodes within the tree of inline_node s
+void inlines__tree_delete(struct rb_root *tree);
+
#endif /* PERF_SRCLINE_H */
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index f0b08810d7fa..b358570ce615 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -59,6 +59,7 @@ struct symbol {
u8 binding;
u8 idle:1;
u8 ignore:1;
+ u8 inlined:1;
u8 arch_sym;
char name[0];
};
--
2.13.0
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2017-05-22 14:30 +0200 |
| Subject | Re: [PATCH 3/7] perf report: create real callchain entries for inlined frames |
| Message-ID | <tJUDg-5Es-9@gated-at.bofh.it> |
| In reply to | #1644869 |
On Thu, May 18, 2017 at 09:34:07PM +0200, Milian Wolff wrote:
> +
> + if (strcmp(funcname, base_sym->name) == 0) {
> + // reuse the real, existing symbol
I don't know whether it's required by coding style guide but please
use C-style block comment if possible (especially for multiline comments).
> + inline_sym = base_sym;
> + } else {
> + // create a fake symbol for the inline frame
> + inline_sym = symbol__new(base_sym ? base_sym->start : 0,
> + base_sym ? base_sym->end : 0,
> + base_sym ? base_sym->binding : 0,
> + funcname);
> + if (inline_sym)
> + inline_sym->inlined = 1;
> + }
> +
> + free(demangled);
> +
> + return inline_sym;
> +}
> +
> static int inline_list__append_dso_a2l(struct dso *dso,
> - struct inline_node *node)
> + struct inline_node *node,
> + struct symbol *sym)
> {
> struct a2l_data *a2l = dso->a2l;
> - char *funcname = a2l->funcname ? strdup(a2l->funcname) : NULL;
> char *filename = a2l->filename ? strdup(a2l->filename) : NULL;
> + struct symbol *inline_sym = new_inline_sym(dso, sym, a2l->funcname);
>
> - return inline_list__append(filename, funcname, a2l->line, node, dso);
> + return inline_list__append(inline_sym, filename, a2l->line, node);
> }
>
> static int addr2line(const char *dso_name, u64 addr,
> char **file, unsigned int *line, struct dso *dso,
> - bool unwind_inlines, struct inline_node *node)
> + bool unwind_inlines, struct inline_node *node,
> + struct symbol *sym)
> {
> int ret = 0;
> struct a2l_data *a2l = dso->a2l;
> @@ -241,7 +264,7 @@ static int addr2line(const char *dso_name, u64 addr,
> if (unwind_inlines) {
> int cnt = 0;
>
> - if (node && inline_list__append_dso_a2l(dso, node))
> + if (node && inline_list__append_dso_a2l(dso, node, sym))
> return 0;
>
> while (bfd_find_inliner_info(a2l->abfd, &a2l->filename,
> @@ -249,7 +272,7 @@ static int addr2line(const char *dso_name, u64 addr,
> cnt++ < MAX_INLINE_NEST) {
>
> if (node != NULL) {
> - if (inline_list__append_dso_a2l(dso, node))
> + if (inline_list__append_dso_a2l(dso, node, sym))
> return 0;
> // found at least one inline frame
> ret = 1;
> @@ -281,7 +304,7 @@ void dso__free_a2l(struct dso *dso)
> }
>
> static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
> - struct dso *dso)
> + struct dso *dso, struct symbol *sym)
> {
> struct inline_node *node;
>
> @@ -294,7 +317,7 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
> INIT_LIST_HEAD(&node->val);
> node->addr = addr;
>
> - if (!addr2line(dso_name, addr, NULL, NULL, dso, TRUE, node))
> + if (!addr2line(dso_name, addr, NULL, NULL, dso, TRUE, node, sym))
> goto out_free_inline_node;
>
> if (list_empty(&node->val))
> @@ -334,7 +357,8 @@ static int addr2line(const char *dso_name, u64 addr,
> char **file, unsigned int *line_nr,
> struct dso *dso __maybe_unused,
> bool unwind_inlines __maybe_unused,
> - struct inline_node *node __maybe_unused)
> + struct inline_node *node __maybe_unused,
> + struct symbol *sym __maybe_unused)
> {
> FILE *fp;
> char cmd[PATH_MAX];
> @@ -374,7 +398,7 @@ void dso__free_a2l(struct dso *dso __maybe_unused)
> }
>
> static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
> - struct dso *dso __maybe_unused)
> + struct dso *dso __maybe_unused, struct symbol *sym)
> {
> FILE *fp;
> char cmd[PATH_MAX];
> @@ -407,8 +431,7 @@ static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
> goto out;
> }
>
> - if (inline_list__append(filename, NULL, line_nr, node,
> - NULL) != 0)
> + if (inline_list__append(sym, filename, line_nr, node) != 0)
> goto out;
>
> filename = NULL;
> @@ -448,7 +471,8 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
> if (dso_name == NULL)
> goto out;
>
> - if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines, NULL))
> + if (!addr2line(dso_name, addr, &file, &line, dso,
> + unwind_inlines, NULL, sym))
> goto out;
>
> if (asprintf(&srcline, "%s:%u",
> @@ -494,7 +518,8 @@ char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
> return __get_srcline(dso, addr, sym, show_sym, show_addr, false);
> }
>
> -struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
> +struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr,
> + struct symbol *sym)
> {
> const char *dso_name;
>
> @@ -502,7 +527,7 @@ struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr)
> if (dso_name == NULL)
> return NULL;
>
> - return addr2inlines(dso_name, addr, dso);
> + return addr2inlines(dso_name, addr, dso, sym);
> }
>
> void inline_node__delete(struct inline_node *node)
> @@ -512,9 +537,62 @@ void inline_node__delete(struct inline_node *node)
> list_for_each_entry_safe(ilist, tmp, &node->val, list) {
> list_del_init(&ilist->list);
> zfree(&ilist->filename);
> - zfree(&ilist->funcname);
> + // only the inlined symbols are owned by the list
> + if (ilist->symbol && ilist->symbol->inlined)
> + symbol__delete(ilist->symbol);
> free(ilist);
> }
>
> free(node);
> }
> +
> +void inlines__tree_insert(struct rb_root *tree, struct inline_node *inlines)
> +{
> + struct rb_node **p = &tree->rb_node;
> + struct rb_node *parent = NULL;
> + const u64 addr = inlines->addr;
> + struct inline_node *i;
> +
> + while (*p != NULL) {
> + parent = *p;
> + i = rb_entry(parent, struct inline_node, rb_node);
> + if (addr < i->addr)
> + p = &(*p)->rb_left;
> + else
> + p = &(*p)->rb_right;
> + }
> + rb_link_node(&inlines->rb_node, parent, p);
> + rb_insert_color(&inlines->rb_node, tree);
> +}
> +
> +struct inline_node *inlines__tree_find(struct rb_root *tree, u64 addr)
> +{
> + struct rb_node *n = tree->rb_node;
> +
> + while (n) {
> + struct inline_node *i = rb_entry(n, struct inline_node,
> + rb_node);
> +
> + if (addr < i->addr)
> + n = n->rb_left;
> + else if (addr > i->addr)
> + n = n->rb_right;
> + else
> + return i;
> + }
> +
> + return NULL;
> +}
> +
> +void inlines__tree_delete(struct rb_root *tree)
> +{
> + struct inline_node *pos;
> + struct rb_node *next = rb_first(tree);
> +
> + while (next) {
> + pos = rb_entry(next, struct inline_node, rb_node);
> + next = rb_next(&pos->rb_node);
> + rb_erase(&pos->rb_node, tree);
> + inline_node__delete(pos);
> + }
> +}
> diff --git a/tools/perf/util/srcline.h b/tools/perf/util/srcline.h
> index 7b52ba88676e..ad9726987b80 100644
> --- a/tools/perf/util/srcline.h
> +++ b/tools/perf/util/srcline.h
> @@ -2,6 +2,7 @@
> #define PERF_SRCLINE_H
>
> #include <linux/list.h>
> +#include <linux/rbtree.h>
> #include <linux/types.h>
>
> struct dso;
> @@ -17,8 +18,8 @@ void free_srcline(char *srcline);
> #define SRCLINE_UNKNOWN ((char *) "??:0")
>
> struct inline_list {
> + struct symbol *symbol;
> char *filename;
> - char *funcname;
> unsigned int line_nr;
> struct list_head list;
> };
> @@ -26,9 +27,20 @@ struct inline_list {
> struct inline_node {
> u64 addr;
> struct list_head val;
> + struct rb_node rb_node;
> };
>
> -struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr);
> +// parse inlined frames for the given address
> +struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr,
> + struct symbol *sym);
> +// free resources associated to the inline node list
> void inline_node__delete(struct inline_node *node);
>
> +// insert the inline node list into the DSO, which will take ownership
> +void inlines__tree_insert(struct rb_root *tree, struct inline_node *inlines);
> +// find previously inserted inline node list
> +struct inline_node *inlines__tree_find(struct rb_root *tree, u64 addr);
> +// delete all nodes within the tree of inline_node s
> +void inlines__tree_delete(struct rb_root *tree);
> +
> #endif /* PERF_SRCLINE_H */
> diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
> index f0b08810d7fa..b358570ce615 100644
> --- a/tools/perf/util/symbol.h
> +++ b/tools/perf/util/symbol.h
> @@ -59,6 +59,7 @@ struct symbol {
> u8 binding;
> u8 idle:1;
> u8 ignore:1;
> + u8 inlined:1;
> u8 arch_sym;
> char name[0];
> };
> --
> 2.13.0
>
[toc] | [prev] | [next] | [standalone]
| From | Milian Wolff <milian.wolff@kdab.com> |
|---|---|
| Date | 2017-05-24 13:50 +0200 |
| Subject | Re: [PATCH 3/7] perf report: create real callchain entries for inlined frames |
| Message-ID | <tKCXF-1Q9-25@gated-at.bofh.it> |
| In reply to | #1646831 |
[Multipart message — attachments visible in raw view] — view raw
On Monday, May 22, 2017 2:19:46 PM CEST Namhyung Kim wrote:
> On Thu, May 18, 2017 at 09:34:07PM +0200, Milian Wolff wrote:
> > +
> > + if (strcmp(funcname, base_sym->name) == 0) {
> > + // reuse the real, existing symbol
>
> I don't know whether it's required by coding style guide but please
> use C-style block comment if possible (especially for multiline comments).
OK. Since the patch check script didn't complain I thought it was OK to use
this style here.
I'll update the patch series accordingly.
Thanks
--
Milian Wolff | milian.wolff@kdab.com | Software Engineer
KDAB (Deutschland) GmbH&Co KG, a KDAB Group company
Tel: +49-30-521325470
KDAB - The Qt Experts
[toc] | [prev] | [next] | [standalone]
| From | Milian Wolff <milian.wolff@kdab.com> |
|---|---|
| Date | 2017-05-18 22:10 +0200 |
| Message-ID | <tIzUd-8fL-1@gated-at.bofh.it> |
| In reply to | #1644868 |
[Multipart message — attachments visible in raw view] — view raw
On Donnerstag, 18. Mai 2017 21:34:04 CEST Milian Wolff wrote: > This series of patches completely reworks the way inline frames are handled. > Instead of querying for the inline nodes on-demand in the individual tools, > we now create proper callchain nodes for inlined frames. The advantages > this approach brings are numerous: > > - less duplicated code in the individual browser > - aggregated cost for inlined frames for the --children top-down list > - various bug fixes that arose from querying for a srcline/symbol based on > the IP of a sample, which will always point to the last inlined frame > instead of the corresponding non-inlined frame > - overall much better support for visualizing cost for heavily-inlined C++ > code, which simply was confusing and unreliably before > - srcline honors the global setting as to whether full paths or basenames > should be shown > > For comparison, below lists the output before and after for `perf script` > and `perf report`. The example file I used to generate the perf data is: And of course shortly after sending this patch series I notice the first issues ;-) The new behavior shows confusing results for `-g function` because match_chain uses sym->start. I fixed this locally to compare the actual function name if either of the two symbols is an inlined fake symbol: https://github.com/milianw/linux/commit/ a1fa4486c19976f36d6ee7df263676e888c2bdb9 Before resending this whole series, it would be great if people could review the stuff in here such that I know whether I'm on the right track. You can checkout my clone from github for quick testing: https://github.com/milianw/linux/commits/wip/distinguish-inliners Thanks -- Milian Wolff | milian.wolff@kdab.com | Software Engineer KDAB (Deutschland) GmbH&Co KG, a KDAB Group company Tel: +49-30-521325470 KDAB - The Qt Experts
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2017-05-22 11:10 +0200 |
| Subject | Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames |
| Message-ID | <tJRvI-3yY-29@gated-at.bofh.it> |
| In reply to | #1644884 |
Hi Milian, On Thu, May 18, 2017 at 10:05:36PM +0200, Milian Wolff wrote: > On Donnerstag, 18. Mai 2017 21:34:04 CEST Milian Wolff wrote: > > This series of patches completely reworks the way inline frames are handled. > > Instead of querying for the inline nodes on-demand in the individual tools, > > we now create proper callchain nodes for inlined frames. The advantages > > this approach brings are numerous: > > > > - less duplicated code in the individual browser > > - aggregated cost for inlined frames for the --children top-down list > > - various bug fixes that arose from querying for a srcline/symbol based on > > the IP of a sample, which will always point to the last inlined frame > > instead of the corresponding non-inlined frame > > - overall much better support for visualizing cost for heavily-inlined C++ > > code, which simply was confusing and unreliably before > > - srcline honors the global setting as to whether full paths or basenames > > should be shown > > > > For comparison, below lists the output before and after for `perf script` > > and `perf report`. The example file I used to generate the perf data is: > > And of course shortly after sending this patch series I notice the first > issues ;-) The new behavior shows confusing results for `-g function` because > match_chain uses sym->start. I fixed this locally to compare the actual > function name if either of the two symbols is an inlined fake symbol: Why not making the fake symbol has start addr of the sample IP and length of 1. The histogram sort code also compares the sym->start which might confuse the output of the children mode too IMHO. Thanks, Namhyung > > https://github.com/milianw/linux/commit/ > a1fa4486c19976f36d6ee7df263676e888c2bdb9 > > Before resending this whole series, it would be great if people could review > the stuff in here such that I know whether I'm on the right track. > > You can checkout my clone from github for quick testing: > > https://github.com/milianw/linux/commits/wip/distinguish-inliners > > Thanks > > -- > Milian Wolff | milian.wolff@kdab.com | Software Engineer > KDAB (Deutschland) GmbH&Co KG, a KDAB Group company > Tel: +49-30-521325470 > KDAB - The Qt Experts
[toc] | [prev] | [next] | [standalone]
| From | Milian Wolff <milian.wolff@kdab.com> |
|---|---|
| Date | 2017-05-24 13:50 +0200 |
| Message-ID | <tKCXE-1Q9-9@gated-at.bofh.it> |
| In reply to | #1646633 |
[Multipart message — attachments visible in raw view] — view raw
On Monday, May 22, 2017 11:06:43 AM CEST Namhyung Kim wrote: > Hi Milian, > > On Thu, May 18, 2017 at 10:05:36PM +0200, Milian Wolff wrote: > > On Donnerstag, 18. Mai 2017 21:34:04 CEST Milian Wolff wrote: > > > This series of patches completely reworks the way inline frames are > > > handled. Instead of querying for the inline nodes on-demand in the > > > individual tools, we now create proper callchain nodes for inlined > > > frames. The advantages this approach brings are numerous: > > > > > > - less duplicated code in the individual browser > > > - aggregated cost for inlined frames for the --children top-down list > > > - various bug fixes that arose from querying for a srcline/symbol based > > > on > > > > > > the IP of a sample, which will always point to the last inlined frame > > > instead of the corresponding non-inlined frame > > > > > > - overall much better support for visualizing cost for heavily-inlined > > > C++ > > > > > > code, which simply was confusing and unreliably before > > > > > > - srcline honors the global setting as to whether full paths or > > > basenames > > > > > > should be shown > > > > > > For comparison, below lists the output before and after for `perf > > > script` > > > > > and `perf report`. The example file I used to generate the perf data is: > > And of course shortly after sending this patch series I notice the first > > issues ;-) The new behavior shows confusing results for `-g function` > > because match_chain uses sym->start. I fixed this locally to compare the > > actual > > function name if either of the two symbols is an inlined fake symbol: > > Why not making the fake symbol has start addr of the sample IP and > length of 1. The histogram sort code also compares the sym->start > which might confuse the output of the children mode too IMHO. I can try that out, thank you for the suggestion. But I think it can easily break in different ways. I.e. when the same inline function gets used at different IPs, it should actually be considered to be the same function when we group/merge/aggregate. I updated the `match_chain` function accordingly, to do a symname / srcline comparison on inlined frames, instead of relying on the symbol start/end. I think using the IP for the fake symbols won't be more reliable here, don't you think? In the end, I think we'll always have to special-case inlined fake symbols when we aggregate data, since the sym start/end is always going to be some arbitrary value that may or may not be what we want it to be. Doing the explicit comparison on e.g. srcline/symname is always going to be the most reliable option, as it also directly results in a proper aggregation based on the strings that the user will see in the end. Cheers -- Milian Wolff | milian.wolff@kdab.com | Software Engineer KDAB (Deutschland) GmbH&Co KG, a KDAB Group company Tel: +49-30-521325470 KDAB - The Qt Experts
[toc] | [prev] | [next] | [standalone]
| From | Milian Wolff <milian.wolff@kdab.com> |
|---|---|
| Date | 2017-05-24 15:50 +0200 |
| Message-ID | <tKEPN-2YG-39@gated-at.bofh.it> |
| In reply to | #1649516 |
[Multipart message — attachments visible in raw view] — view raw
On Wednesday, May 24, 2017 1:46:04 PM CEST Milian Wolff wrote:
> On Monday, May 22, 2017 11:06:43 AM CEST Namhyung Kim wrote:
> > Hi Milian,
> >
> > On Thu, May 18, 2017 at 10:05:36PM +0200, Milian Wolff wrote:
> > > On Donnerstag, 18. Mai 2017 21:34:04 CEST Milian Wolff wrote:
> > > > This series of patches completely reworks the way inline frames are
> > > > handled. Instead of querying for the inline nodes on-demand in the
> > > > individual tools, we now create proper callchain nodes for inlined
> > > > frames. The advantages this approach brings are numerous:
> > > >
> > > > - less duplicated code in the individual browser
> > > > - aggregated cost for inlined frames for the --children top-down list
> > > > - various bug fixes that arose from querying for a srcline/symbol
> > > > based
> > > > on
> > > >
> > > > the IP of a sample, which will always point to the last inlined
> > > > frame
> > > > instead of the corresponding non-inlined frame
> > > >
> > > > - overall much better support for visualizing cost for heavily-inlined
> > > > C++
> > > >
> > > > code, which simply was confusing and unreliably before
> > > >
> > > > - srcline honors the global setting as to whether full paths or
> > > > basenames
> > > >
> > > > should be shown
> > > >
> > > > For comparison, below lists the output before and after for `perf
> > > > script`
> > >
> > > > and `perf report`. The example file I used to generate the perf data
is:
> > > And of course shortly after sending this patch series I notice the first
> > > issues ;-) The new behavior shows confusing results for `-g function`
> > > because match_chain uses sym->start. I fixed this locally to compare the
> > > actual
> >
> > > function name if either of the two symbols is an inlined fake symbol:
> > Why not making the fake symbol has start addr of the sample IP and
> > length of 1. The histogram sort code also compares the sym->start
> > which might confuse the output of the children mode too IMHO.
>
> I can try that out, thank you for the suggestion. But I think it can easily
> break in different ways. I.e. when the same inline function gets used at
> different IPs, it should actually be considered to be the same function when
> we group/merge/aggregate. I updated the `match_chain` function accordingly,
> to do a symname / srcline comparison on inlined frames, instead of relying
> on the symbol start/end. I think using the IP for the fake symbols won't be
> more reliable here, don't you think?
>
> In the end, I think we'll always have to special-case inlined fake symbols
> when we aggregate data, since the sym start/end is always going to be some
> arbitrary value that may or may not be what we want it to be. Doing the
> explicit comparison on e.g. srcline/symname is always going to be the most
> reliable option, as it also directly results in a proper aggregation based
> on the strings that the user will see in the end.
I haven't yet tried it out, but I think I can come up with a way to break your
approach easily. Assume the following pseudo-code:
void tail()
{
instr1; // IP1
instr2; // IP2
}
void mid()
{
tail();
}
void main()
{
mid();
}
Now, assume both `tail` and `mid` get inlined into `main`. If we get one
sample each for both IP1 and IP2, we want the following merged structure if we
merge based on symbol:
sym | incl | self
main | 2 | 0
mid | 2 | 0
tail | 2 | 2
If we would give the inlined fake-symbols a start of the IP, i.e. either IP1
or IP2, then we would end up with this (unexpected) behavior instead:
sym | incl | self
main | 2 | 0
mid | 1 | 0
mid | 1 | 0
tail | 1 | 1
tail | 1 | 1
The reason is that the fake symbols for the inlined frames would be considered
to be different functions since their start/end are not equal. This is "wrong"
in my eyes - we really have to do symbol name comparisons for inlined frames,
and also include srcline if that is desired.
If you think the above is not a valid assessment, I'll try to change my patch
series to use the IP + 1 trick you suggest. But I really don't think it's
going to work.
Cheers
--
Milian Wolff | milian.wolff@kdab.com | Software Engineer
KDAB (Deutschland) GmbH&Co KG, a KDAB Group company
Tel: +49-30-521325470
KDAB - The Qt Experts
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2017-05-24 17:10 +0200 |
| Subject | Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames |
| Message-ID | <tKG5c-3Wx-25@gated-at.bofh.it> |
| In reply to | #1649637 |
On Wed, May 24, 2017 at 03:42:59PM +0200, Milian Wolff wrote:
> On Wednesday, May 24, 2017 1:46:04 PM CEST Milian Wolff wrote:
> > On Monday, May 22, 2017 11:06:43 AM CEST Namhyung Kim wrote:
> > > Why not making the fake symbol has start addr of the sample IP and
> > > length of 1. The histogram sort code also compares the sym->start
> > > which might confuse the output of the children mode too IMHO.
> >
> > I can try that out, thank you for the suggestion. But I think it can easily
> > break in different ways. I.e. when the same inline function gets used at
> > different IPs, it should actually be considered to be the same function when
> > we group/merge/aggregate. I updated the `match_chain` function accordingly,
> > to do a symname / srcline comparison on inlined frames, instead of relying
> > on the symbol start/end. I think using the IP for the fake symbols won't be
> > more reliable here, don't you think?
> >
> > In the end, I think we'll always have to special-case inlined fake symbols
> > when we aggregate data, since the sym start/end is always going to be some
> > arbitrary value that may or may not be what we want it to be. Doing the
> > explicit comparison on e.g. srcline/symname is always going to be the most
> > reliable option, as it also directly results in a proper aggregation based
> > on the strings that the user will see in the end.
>
> I haven't yet tried it out, but I think I can come up with a way to break your
> approach easily. Assume the following pseudo-code:
>
> void tail()
> {
> instr1; // IP1
> instr2; // IP2
> }
>
> void mid()
> {
> tail();
> }
>
> void main()
> {
> mid();
> }
>
> Now, assume both `tail` and `mid` get inlined into `main`. If we get one
> sample each for both IP1 and IP2, we want the following merged structure if we
> merge based on symbol:
>
> sym | incl | self
> main | 2 | 0
> mid | 2 | 0
> tail | 2 | 2
>
> If we would give the inlined fake-symbols a start of the IP, i.e. either IP1
> or IP2, then we would end up with this (unexpected) behavior instead:
>
> sym | incl | self
> main | 2 | 0
> mid | 1 | 0
> mid | 1 | 0
> tail | 1 | 1
> tail | 1 | 1
>
> The reason is that the fake symbols for the inlined frames would be considered
> to be different functions since their start/end are not equal. This is "wrong"
> in my eyes - we really have to do symbol name comparisons for inlined frames,
> and also include srcline if that is desired.
That would depend on how we treat inlined function instances. Each
instance might be considered as same or not - but I think it'd be
better treating them as same for simplicity.
Also currently perf aggregates samples using symbol name, but a new
sort key might be added to use symbol address later. Thus it'd be
better to be prepared for such change.
>
> If you think the above is not a valid assessment, I'll try to change my patch
> series to use the IP + 1 trick you suggest. But I really don't think it's
> going to work.
So I agree that we should do symbol name comparison, but I still
prefer setting fake symbol address to [IP, +1]. That would reduce
memory space for annotate as well.
Thanks,
Namhyung
>
> Cheers
> --
> Milian Wolff | milian.wolff@kdab.com | Software Engineer
> KDAB (Deutschland) GmbH&Co KG, a KDAB Group company
> Tel: +49-30-521325470
> KDAB - The Qt Experts
[toc] | [prev] | [next] | [standalone]
| From | Milian Wolff <milian.wolff@kdab.com> |
|---|---|
| Date | 2017-05-29 20:40 +0200 |
| Message-ID | <tMxK9-3Px-1@gated-at.bofh.it> |
| In reply to | #1649696 |
[Multipart message — attachments visible in raw view] — view raw
On Mittwoch, 24. Mai 2017 17:02:37 CEST Namhyung Kim wrote:
> On Wed, May 24, 2017 at 03:42:59PM +0200, Milian Wolff wrote:
> > On Wednesday, May 24, 2017 1:46:04 PM CEST Milian Wolff wrote:
> > > On Monday, May 22, 2017 11:06:43 AM CEST Namhyung Kim wrote:
> > > > Why not making the fake symbol has start addr of the sample IP and
> > > > length of 1. The histogram sort code also compares the sym->start
> > > > which might confuse the output of the children mode too IMHO.
> > >
> > > I can try that out, thank you for the suggestion. But I think it can
> > > easily
> > > break in different ways. I.e. when the same inline function gets used at
> > > different IPs, it should actually be considered to be the same function
> > > when we group/merge/aggregate. I updated the `match_chain` function
> > > accordingly, to do a symname / srcline comparison on inlined frames,
> > > instead of relying on the symbol start/end. I think using the IP for
> > > the fake symbols won't be more reliable here, don't you think?
> > >
> > > In the end, I think we'll always have to special-case inlined fake
> > > symbols
> > > when we aggregate data, since the sym start/end is always going to be
> > > some
> > > arbitrary value that may or may not be what we want it to be. Doing the
> > > explicit comparison on e.g. srcline/symname is always going to be the
> > > most
> > > reliable option, as it also directly results in a proper aggregation
> > > based
> > > on the strings that the user will see in the end.
> >
> > I haven't yet tried it out, but I think I can come up with a way to break
> > your approach easily. Assume the following pseudo-code:
> >
> > void tail()
> > {
> >
> > instr1; // IP1
> > instr2; // IP2
> >
> > }
> >
> > void mid()
> > {
> >
> > tail();
> >
> > }
> >
> > void main()
> > {
> >
> > mid();
> >
> > }
> >
> > Now, assume both `tail` and `mid` get inlined into `main`. If we get one
> > sample each for both IP1 and IP2, we want the following merged structure
> > if we merge based on symbol:
> >
> > sym | incl | self
> > main | 2 | 0
> > mid | 2 | 0
> > tail | 2 | 2
> >
> > If we would give the inlined fake-symbols a start of the IP, i.e. either
> > IP1 or IP2, then we would end up with this (unexpected) behavior instead:
> >
> > sym | incl | self
> > main | 2 | 0
> > mid | 1 | 0
> > mid | 1 | 0
> > tail | 1 | 1
> > tail | 1 | 1
> >
> > The reason is that the fake symbols for the inlined frames would be
> > considered to be different functions since their start/end are not equal.
> > This is "wrong" in my eyes - we really have to do symbol name comparisons
> > for inlined frames, and also include srcline if that is desired.
>
> That would depend on how we treat inlined function instances. Each
> instance might be considered as same or not - but I think it'd be
> better treating them as same for simplicity.
>
> Also currently perf aggregates samples using symbol name, but a new
> sort key might be added to use symbol address later. Thus it'd be
> better to be prepared for such change.
>
> > If you think the above is not a valid assessment, I'll try to change my
> > patch series to use the IP + 1 trick you suggest. But I really don't
> > think it's going to work.
>
> So I agree that we should do symbol name comparison, but I still
> prefer setting fake symbol address to [IP, +1]. That would reduce
> memory space for annotate as well.
Can you expand on this? I can implement this, but without having a way to test
I don't know whether I'm doing it right or not ;-)
Note that fake symbols cannot be annotated from `perf report` currently. The
browser just does nothing - no error, nothing. So I'm really unsure how this
would influence the "memory space for annotate".
Thanks
--
Milian Wolff | milian.wolff@kdab.com | Software Engineer
KDAB (Deutschland) GmbH&Co KG, a KDAB Group company
Tel: +49-30-521325470
KDAB - The Qt Experts
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2017-05-30 03:40 +0200 |
| Subject | Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames |
| Message-ID | <tMEiB-7P-7@gated-at.bofh.it> |
| In reply to | #1652683 |
On Mon, May 29, 2017 at 08:36:25PM +0200, Milian Wolff wrote: > On Mittwoch, 24. Mai 2017 17:02:37 CEST Namhyung Kim wrote: > > On Wed, May 24, 2017 at 03:42:59PM +0200, Milian Wolff wrote: > > > If you think the above is not a valid assessment, I'll try to change my > > > patch series to use the IP + 1 trick you suggest. But I really don't > > > think it's going to work. > > > > So I agree that we should do symbol name comparison, but I still > > prefer setting fake symbol address to [IP, +1]. That would reduce > > memory space for annotate as well. > > Can you expand on this? I can implement this, but without having a way to test > I don't know whether I'm doing it right or not ;-) > > Note that fake symbols cannot be annotated from `perf report` currently. The > browser just does nothing - no error, nothing. So I'm really unsure how this > would influence the "memory space for annotate". Hmm.. nevermind. I missed that it cannot be annotated. And if it aggregates symbols by name, start address might not be necessary. Thanks, Namhyung
[toc] | [prev] | [next] | [standalone]
| From | Namhyung Kim <namhyung@kernel.org> |
|---|---|
| Date | 2017-05-22 14:10 +0200 |
| Subject | Re: [PATCH 0/7] generate full callchain cursor entries for inlined frames |
| Message-ID | <tJUjV-5vZ-29@gated-at.bofh.it> |
| In reply to | #1644868 |
On Thu, May 18, 2017 at 09:34:04PM +0200, Milian Wolff wrote:
> This series of patches completely reworks the way inline frames are handled.
> Instead of querying for the inline nodes on-demand in the individual tools,
> we now create proper callchain nodes for inlined frames. The advantages this
> approach brings are numerous:
>
> - less duplicated code in the individual browser
> - aggregated cost for inlined frames for the --children top-down list
> - various bug fixes that arose from querying for a srcline/symbol based on
> the IP of a sample, which will always point to the last inlined frame
> instead of the corresponding non-inlined frame
> - overall much better support for visualizing cost for heavily-inlined C++
> code, which simply was confusing and unreliably before
> - srcline honors the global setting as to whether full paths or basenames
> should be shown
>
> For comparison, below lists the output before and after for `perf script`
> and `perf report`. The example file I used to generate the perf data is:
>
> ~~~~~
> $ cat inlining.cpp
> #include <complex>
> #include <cmath>
> #include <random>
> #include <iostream>
>
> using namespace std;
>
> int main()
> {
> uniform_real_distribution<double> uniform(-1E5, 1E5);
> default_random_engine engine;
> double s = 0;
> for (int i = 0; i < 10000000; ++i) {
> s += norm(complex<double>(uniform(engine), uniform(engine)));
> }
> cout << s << '\n';
> return 0;
> }
> $ g++ -O2 -g -o inlining inlining.cpp
> $ perf record --call-graph dwarf ./inlining
> ~~~~~
>
> Now, the (broken) status-quo looks like this. Look for "NOTE:" to see some
> of my comments that outline the various issues I'm trying to solve by this
> patch series.
>
> ~~~~~
> $ perf script --inline
> ...
> inlining 11083 97459.356656: 33680 cycles:
> 214f7 __hypot_finite (/usr/lib/libm-2.25.so)
> ace3 hypot (/usr/lib/libm-2.25.so)
> a4a main (/home/milian/projects/src/perf-tests/inlining)
> std::__complex_abs
> std::abs<double>
> std::_Norm_helper<true>::_S_do_it<double>
> std::norm<double>
> main
> 20510 __libc_start_main (/usr/lib/libc-2.25.so)
> bd9 _start (/home/milian/projects/src/perf-tests/inlining)
> # NOTE: the above inlined stack is confusing: the a4a is an address into main,
> # which is the non-inlined symbol. the entry with the address should be
> # at the end of the stack, where it's actually duplicated once more but
> # there it's missing the address
Omitting address was to distinguish inlined entries from others. I
didn't think it's a problem by itself (missed inter-operability below),
but duplicated entries should be removed.
> ...
> $ perf report -s sym -g srcline -i perf.inlining.data --inline --stdio
> ...
> --38.86%--_start
> __libc_start_main
> |
> |--15.68%--main random.tcc:3326
> | /home/milian/projects/src/perf-tests/inlining.cpp:14 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:185 (inline)
> | /usr/include/c++/6.3.1/bits/random.tcc:3326 (inline)
> |
> |--10.36%--main random.h:143
> | /home/milian/projects/src/perf-tests/inlining.cpp:14 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:185 (inline)
> | /usr/include/c++/6.3.1/bits/random.tcc:3332 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:332 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:151 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:143 (inline)
> |
> |--5.66%--main random.tcc:3332
> | /home/milian/projects/src/perf-tests/inlining.cpp:14 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:185 (inline)
> | /usr/include/c++/6.3.1/bits/random.tcc:3332 (inline)
> ...
> # NOTE: the grouping is totally off because the first and last frame of the
> inline nodes is completely bogus, since the IP is used to find the sym/srcline
> which is different from the actual inlined sym/srcline.
> also, the code currently displays either the inlined function name or
> the corresponding filename (but in full length, instead of just the basename).
Yes, inlined nodes should have correct IP for later use.
>
> $ perf report -s sym -g srcline -i perf.inlining.data --inline --stdio --no-children
> ...
> 38.86% [.] main
> |
> |--15.68%--main random.tcc:3326
> | /usr/include/c++/6.3.1/bits/random.tcc:3326 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:185 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:1818 (inline)
> | /usr/include/c++/6.3.1/bits/random.h:1809 (inline)
> | /home/milian/projects/src/perf-tests/inlining.cpp:14 (inline)
> | __libc_start_main
> | _start
> ...
> # NOTE: the srcline for main is wrong, it should be inlining.cpp:14,
> i.e. what is displayed in the line below (see also perf script issue above)
OK.
> ~~~~~
>
> Afterwards, all of the above issues are resolved:
>
> ~~~~~
> $ perf script --inline
> ...
> inlining 11083 97459.356656: 33680 cycles:
> 214f7 __hypot_finite (/usr/lib/libm-2.25.so)
> ace3 hypot (/usr/lib/libm-2.25.so)
> a4a std::__complex_abs (inlined)
> a4a std::abs<double> (inlined)
> a4a std::_Norm_helper<true>::_S_do_it<double> (inlined)
> a4a std::norm<double> (inlined)
> a4a main (/home/milian/projects/src/perf-tests/inlining)
> 20510 __libc_start_main (/usr/lib/libc-2.25.so)
> bd9 _start (/home/milian/projects/src/perf-tests/inlining)
> ...
> # NOTE: only one main entry, at the correct position.
> we do display the (repeated) instruction pointer as that ensures
> interoperability with e.g. the stackcollapse-perf.pl script
Looks good.
>
> $ perf report -s sym -g srcline -i perf.inlining.data --inline --stdio
> ...
> 100.00% 38.86% [.] main
> |
> |--61.14%--main inlining.cpp:14
> | std::norm<double> complex:664 (inlined)
> | std::_Norm_helper<true>::_S_do_it<double> complex:654 (inlined)
> | std::abs<double> complex:597 (inlined)
> | std::__complex_abs complex:589 (inlined)
> | |
> | |--60.29%--hypot
> | | |
> | | --56.03%--__hypot_finite
> | |
> | --0.85%--cabs
> |
> --38.86%--_start
> __libc_start_main
> |
> |--38.19%--main inlining.cpp:14
> | |
> | |--35.59%--std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:1809 (inlined)
> | | std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:1818 (inlined)
> | | |
> | | --34.37%--std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator() random.h:185 (inlined)
> | | |
> | | |--17.91%--std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3332 (inlined)
> | | | |
> | | | --12.24%--std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>::operator() random.h:332 (inlined)
> | | | std::__detail::__mod<unsigned long, 2147483647ul, 16807ul, 0ul> random.h:151 (inlined)
> | | | |
> | | | |--10.36%--std::__detail::_Mod<unsigned long, 2147483647ul, 16807ul, 0ul, true, true>::__calc random.h:143 (inlined)
> | | | |
> | | | --1.88%--std::__detail::_Mod<unsigned long, 2147483647ul, 16807ul, 0ul, true, true>::__calc random.h:141 (inlined)
> | | |
> | | |--15.68%--std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3326 (inlined)
> | | |
> | | --0.79%--std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3335 (inlined)
> | |
> | --1.99%--std::norm<double> complex:664 (inlined)
> | std::_Norm_helper<true>::_S_do_it<double> complex:654 (inlined)
> | std::abs<double> complex:597 (inlined)
> | std::__complex_abs complex:589 (inlined)
> |
> --0.67%--main inlining.cpp:13
> ...
>
> # NOTE: still somewhat confusing due to the _start and __libc_start_main frames
> that actually are *above* the main frame. But at least the stuff below
> properly splits up and shows that mutiple functions got inlined into
> inlining.cpp:14, not just one as before.
Right. Current callchain mode has the problem. It shows a mix of
callchains of a sample and children of the sample. I proposed a patch
[1] to handle it but I failed to find time to update it as Jiri
suggested.
[1] https://lkml.org/lkml/2014/8/14/49
>
> $ perf report -s sym -g srcline -i perf.inlining.data --inline --stdio --no-children
> ...
> 38.86% [.] main
> |
> |--15.68%--std::generate_canonical<double, 53ul, std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.tcc:3326 (inlined)
> | std::__detail::_Adaptor<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul>, double>::operator() random.h:185 (inlined)
> | std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:1818 (inlined)
> | std::uniform_real_distribution<double>::operator()<std::linear_congruential_engine<unsigned long, 16807ul, 0ul, 2147483647ul> > random.h:1809 (inlined)
> | main inlining.cpp:14
> | __libc_start_main
> | _start
> ...
> # NOTE: the first and last entry of the inline stack have the correct symbol and srcline now
> both function and srcline is shown, as well as the (inlined) suffix
> only the basename of the srcline is shown
Nice work!
But I found some problems in both of your patch and current code
during testing (mostly for the children mode). I'll send a fix soon.
Thanks,
Namhyung
> ~~~~~
>
> Milian Wolff (7):
> perf report: remove code to handle inline frames from browsers
> perf util: take elf_name as const string in dso__demangle_sym
> perf report: create real callchain entries for inlined frames
> perf report: use srcline from inlined frames
> perf report: fall-back to function name comparison for -g srcline
> perf report: mark inlined frames in output by " (inlined)" suffix
> perf script: mark inlined frames and do not print DSO for them
>
> tools/perf/ui/browsers/hists.c | 183 +++------------------------------------
> tools/perf/ui/stdio/hist.c | 80 +----------------
> tools/perf/util/callchain.c | 52 +++++------
> tools/perf/util/callchain.h | 5 +-
> tools/perf/util/dso.c | 2 +
> tools/perf/util/dso.h | 1 +
> tools/perf/util/evsel_fprintf.c | 37 +-------
> tools/perf/util/hist.c | 5 --
> tools/perf/util/machine.c | 54 +++++++++++-
> tools/perf/util/sort.h | 1 -
> tools/perf/util/srcline.c | 183 ++++++++++++++++++++++++++++++---------
> tools/perf/util/srcline.h | 19 +++-
> tools/perf/util/symbol-elf.c | 2 +-
> tools/perf/util/symbol-minimal.c | 2 +-
> tools/perf/util/symbol.h | 3 +-
> 15 files changed, 264 insertions(+), 365 deletions(-)
>
> --
> 2.13.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-perf-users" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web