diff options
author | Ingo Molnar <mingo@kernel.org> | 2015-11-24 03:07:28 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-11-24 03:07:28 -0500 |
commit | 9327ca73474225e5d6f1f96807a90f359124118f (patch) | |
tree | f25e0882c41f219103dd7e2fc5d8d1de05224240 /tools/perf | |
parent | b7883a1c4f75edb62fc49da6000c59fb881e3c7b (diff) | |
parent | 646a6e846c4dc3812c614fd061603b6db5b8d380 (diff) |
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
User visible changes:
- Allow callchain order (caller, callee) to the libdw and libunwind based DWARF
unwinders (Jiri Olsa)
- Add missing parent_val initialization in the callchain code, fixing a
SEGFAULT when using callchains with 'perf top' (Jiri Olsa)
- Add initial 'perf config' command, for now just with a --list command to the
contents of the configuration file in use and a basic man page describing
its format, commands for doing edits and detailed documentation are being
reviewed and proof-read. (Taeung Song)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/Build | 1 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-config.txt | 103 | ||||
-rw-r--r-- | tools/perf/builtin-config.c | 66 | ||||
-rw-r--r-- | tools/perf/builtin.h | 1 | ||||
-rw-r--r-- | tools/perf/command-list.txt | 1 | ||||
-rw-r--r-- | tools/perf/perf.c | 1 | ||||
-rw-r--r-- | tools/perf/tests/dwarf-unwind.c | 22 | ||||
-rw-r--r-- | tools/perf/util/callchain.h | 1 | ||||
-rw-r--r-- | tools/perf/util/unwind-libdw.c | 53 | ||||
-rw-r--r-- | tools/perf/util/unwind-libdw.h | 2 | ||||
-rw-r--r-- | tools/perf/util/unwind-libunwind.c | 60 |
11 files changed, 272 insertions, 39 deletions
diff --git a/tools/perf/Build b/tools/perf/Build index 72237455b400..2c7aaf2ba119 100644 --- a/tools/perf/Build +++ b/tools/perf/Build | |||
@@ -1,5 +1,6 @@ | |||
1 | perf-y += builtin-bench.o | 1 | perf-y += builtin-bench.o |
2 | perf-y += builtin-annotate.o | 2 | perf-y += builtin-annotate.o |
3 | perf-y += builtin-config.o | ||
3 | perf-y += builtin-diff.o | 4 | perf-y += builtin-diff.o |
4 | perf-y += builtin-evlist.o | 5 | perf-y += builtin-evlist.o |
5 | perf-y += builtin-help.o | 6 | perf-y += builtin-help.o |
diff --git a/tools/perf/Documentation/perf-config.txt b/tools/perf/Documentation/perf-config.txt new file mode 100644 index 000000000000..b9ca1e304158 --- /dev/null +++ b/tools/perf/Documentation/perf-config.txt | |||
@@ -0,0 +1,103 @@ | |||
1 | perf-config(1) | ||
2 | ============== | ||
3 | |||
4 | NAME | ||
5 | ---- | ||
6 | perf-config - Get and set variables in a configuration file. | ||
7 | |||
8 | SYNOPSIS | ||
9 | -------- | ||
10 | [verse] | ||
11 | 'perf config' -l | --list | ||
12 | |||
13 | DESCRIPTION | ||
14 | ----------- | ||
15 | You can manage variables in a configuration file with this command. | ||
16 | |||
17 | OPTIONS | ||
18 | ------- | ||
19 | |||
20 | -l:: | ||
21 | --list:: | ||
22 | Show current config variables, name and value, for all sections. | ||
23 | |||
24 | CONFIGURATION FILE | ||
25 | ------------------ | ||
26 | |||
27 | The perf configuration file contains many variables to change various | ||
28 | aspects of each of its tools, including output, disk usage, etc. | ||
29 | The '$HOME/.perfconfig' file is used to store a per-user configuration. | ||
30 | The file '$(sysconfdir)/perfconfig' can be used to | ||
31 | store a system-wide default configuration. | ||
32 | |||
33 | Syntax | ||
34 | ~~~~~~ | ||
35 | |||
36 | The file consist of sections. A section starts with its name | ||
37 | surrounded by square brackets and continues till the next section | ||
38 | begins. Each variable must be in a section, and have the form | ||
39 | 'name = value', for example: | ||
40 | |||
41 | [section] | ||
42 | name1 = value1 | ||
43 | name2 = value2 | ||
44 | |||
45 | Section names are case sensitive and can contain any characters except | ||
46 | newline (double quote `"` and backslash have to be escaped as `\"` and `\\`, | ||
47 | respectively). Section headers can't span multiple lines. | ||
48 | |||
49 | Example | ||
50 | ~~~~~~~ | ||
51 | |||
52 | Given a $HOME/.perfconfig like this: | ||
53 | |||
54 | # | ||
55 | # This is the config file, and | ||
56 | # a '#' and ';' character indicates a comment | ||
57 | # | ||
58 | |||
59 | [colors] | ||
60 | # Color variables | ||
61 | top = red, default | ||
62 | medium = green, default | ||
63 | normal = lightgray, default | ||
64 | selected = white, lightgray | ||
65 | code = blue, default | ||
66 | addr = magenta, default | ||
67 | root = white, blue | ||
68 | |||
69 | [tui] | ||
70 | # Defaults if linked with libslang | ||
71 | report = on | ||
72 | annotate = on | ||
73 | top = on | ||
74 | |||
75 | [buildid] | ||
76 | # Default, disable using /dev/null | ||
77 | dir = ~/.debug | ||
78 | |||
79 | [annotate] | ||
80 | # Defaults | ||
81 | hide_src_code = false | ||
82 | use_offset = true | ||
83 | jump_arrows = true | ||
84 | show_nr_jumps = false | ||
85 | |||
86 | [help] | ||
87 | # Format can be man, info, web or html | ||
88 | format = man | ||
89 | autocorrect = 0 | ||
90 | |||
91 | [ui] | ||
92 | show-headers = true | ||
93 | |||
94 | [call-graph] | ||
95 | # fp (framepointer), dwarf | ||
96 | record-mode = fp | ||
97 | print-type = graph | ||
98 | order = caller | ||
99 | sort-key = function | ||
100 | |||
101 | SEE ALSO | ||
102 | -------- | ||
103 | linkperf:perf[1] | ||
diff --git a/tools/perf/builtin-config.c b/tools/perf/builtin-config.c new file mode 100644 index 000000000000..427ea7a705b8 --- /dev/null +++ b/tools/perf/builtin-config.c | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * builtin-config.c | ||
3 | * | ||
4 | * Copyright (C) 2015, Taeung Song <treeze.taeung@gmail.com> | ||
5 | * | ||
6 | */ | ||
7 | #include "builtin.h" | ||
8 | |||
9 | #include "perf.h" | ||
10 | |||
11 | #include "util/cache.h" | ||
12 | #include "util/parse-options.h" | ||
13 | #include "util/util.h" | ||
14 | #include "util/debug.h" | ||
15 | |||
16 | static const char * const config_usage[] = { | ||
17 | "perf config [options]", | ||
18 | NULL | ||
19 | }; | ||
20 | |||
21 | enum actions { | ||
22 | ACTION_LIST = 1 | ||
23 | } actions; | ||
24 | |||
25 | static struct option config_options[] = { | ||
26 | OPT_SET_UINT('l', "list", &actions, | ||
27 | "show current config variables", ACTION_LIST), | ||
28 | OPT_END() | ||
29 | }; | ||
30 | |||
31 | static int show_config(const char *key, const char *value, | ||
32 | void *cb __maybe_unused) | ||
33 | { | ||
34 | if (value) | ||
35 | printf("%s=%s\n", key, value); | ||
36 | else | ||
37 | printf("%s\n", key); | ||
38 | |||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused) | ||
43 | { | ||
44 | int ret = 0; | ||
45 | |||
46 | argc = parse_options(argc, argv, config_options, config_usage, | ||
47 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
48 | |||
49 | switch (actions) { | ||
50 | case ACTION_LIST: | ||
51 | if (argc) { | ||
52 | pr_err("Error: takes no arguments\n"); | ||
53 | parse_options_usage(config_usage, config_options, "l", 1); | ||
54 | } else { | ||
55 | ret = perf_config(show_config, NULL); | ||
56 | if (ret < 0) | ||
57 | pr_err("Nothing configured, " | ||
58 | "please check your ~/.perfconfig file\n"); | ||
59 | } | ||
60 | break; | ||
61 | default: | ||
62 | usage_with_options(config_usage, config_options); | ||
63 | } | ||
64 | |||
65 | return ret; | ||
66 | } | ||
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index 3688ad29085f..3f871b54e261 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h | |||
@@ -17,6 +17,7 @@ extern int cmd_annotate(int argc, const char **argv, const char *prefix); | |||
17 | extern int cmd_bench(int argc, const char **argv, const char *prefix); | 17 | extern int cmd_bench(int argc, const char **argv, const char *prefix); |
18 | extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix); | 18 | extern int cmd_buildid_cache(int argc, const char **argv, const char *prefix); |
19 | extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); | 19 | extern int cmd_buildid_list(int argc, const char **argv, const char *prefix); |
20 | extern int cmd_config(int argc, const char **argv, const char *prefix); | ||
20 | extern int cmd_diff(int argc, const char **argv, const char *prefix); | 21 | extern int cmd_diff(int argc, const char **argv, const char *prefix); |
21 | extern int cmd_evlist(int argc, const char **argv, const char *prefix); | 22 | extern int cmd_evlist(int argc, const char **argv, const char *prefix); |
22 | extern int cmd_help(int argc, const char **argv, const char *prefix); | 23 | extern int cmd_help(int argc, const char **argv, const char *prefix); |
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt index 00fcaf8a5b8d..acc3ea7d90b7 100644 --- a/tools/perf/command-list.txt +++ b/tools/perf/command-list.txt | |||
@@ -9,6 +9,7 @@ perf-buildid-cache mainporcelain common | |||
9 | perf-buildid-list mainporcelain common | 9 | perf-buildid-list mainporcelain common |
10 | perf-data mainporcelain common | 10 | perf-data mainporcelain common |
11 | perf-diff mainporcelain common | 11 | perf-diff mainporcelain common |
12 | perf-config mainporcelain common | ||
12 | perf-evlist mainporcelain common | 13 | perf-evlist mainporcelain common |
13 | perf-inject mainporcelain common | 14 | perf-inject mainporcelain common |
14 | perf-kmem mainporcelain common | 15 | perf-kmem mainporcelain common |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 3d4c7c09adea..4bee53c3f796 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -39,6 +39,7 @@ struct cmd_struct { | |||
39 | static struct cmd_struct commands[] = { | 39 | static struct cmd_struct commands[] = { |
40 | { "buildid-cache", cmd_buildid_cache, 0 }, | 40 | { "buildid-cache", cmd_buildid_cache, 0 }, |
41 | { "buildid-list", cmd_buildid_list, 0 }, | 41 | { "buildid-list", cmd_buildid_list, 0 }, |
42 | { "config", cmd_config, 0 }, | ||
42 | { "diff", cmd_diff, 0 }, | 43 | { "diff", cmd_diff, 0 }, |
43 | { "evlist", cmd_evlist, 0 }, | 44 | { "evlist", cmd_evlist, 0 }, |
44 | { "help", cmd_help, 0 }, | 45 | { "help", cmd_help, 0 }, |
diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index 01f0b61de53d..b2357e8115a2 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c | |||
@@ -51,6 +51,12 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
51 | "krava_1", | 51 | "krava_1", |
52 | "test__dwarf_unwind" | 52 | "test__dwarf_unwind" |
53 | }; | 53 | }; |
54 | /* | ||
55 | * The funcs[MAX_STACK] array index, based on the | ||
56 | * callchain order setup. | ||
57 | */ | ||
58 | int idx = callchain_param.order == ORDER_CALLER ? | ||
59 | MAX_STACK - *cnt - 1 : *cnt; | ||
54 | 60 | ||
55 | if (*cnt >= MAX_STACK) { | 61 | if (*cnt >= MAX_STACK) { |
56 | pr_debug("failed: crossed the max stack value %d\n", MAX_STACK); | 62 | pr_debug("failed: crossed the max stack value %d\n", MAX_STACK); |
@@ -63,8 +69,10 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
63 | return -1; | 69 | return -1; |
64 | } | 70 | } |
65 | 71 | ||
66 | pr_debug("got: %s 0x%" PRIx64 "\n", symbol, entry->ip); | 72 | (*cnt)++; |
67 | return strcmp((const char *) symbol, funcs[(*cnt)++]); | 73 | pr_debug("got: %s 0x%" PRIx64 ", expecting %s\n", |
74 | symbol, entry->ip, funcs[idx]); | ||
75 | return strcmp((const char *) symbol, funcs[idx]); | ||
68 | } | 76 | } |
69 | 77 | ||
70 | __attribute__ ((noinline)) | 78 | __attribute__ ((noinline)) |
@@ -105,8 +113,16 @@ static int compare(void *p1, void *p2) | |||
105 | /* Any possible value should be 'thread' */ | 113 | /* Any possible value should be 'thread' */ |
106 | struct thread *thread = *(struct thread **)p1; | 114 | struct thread *thread = *(struct thread **)p1; |
107 | 115 | ||
108 | if (global_unwind_retval == -INT_MAX) | 116 | if (global_unwind_retval == -INT_MAX) { |
117 | /* Call unwinder twice for both callchain orders. */ | ||
118 | callchain_param.order = ORDER_CALLER; | ||
119 | |||
109 | global_unwind_retval = unwind_thread(thread); | 120 | global_unwind_retval = unwind_thread(thread); |
121 | if (!global_unwind_retval) { | ||
122 | callchain_param.order = ORDER_CALLEE; | ||
123 | global_unwind_retval = unwind_thread(thread); | ||
124 | } | ||
125 | } | ||
110 | 126 | ||
111 | return p1 - p2; | 127 | return p1 - p2; |
112 | } | 128 | } |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 6e9b5f2099e1..8ac8f043004c 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -143,6 +143,7 @@ extern __thread struct callchain_cursor callchain_cursor; | |||
143 | static inline void callchain_init(struct callchain_root *root) | 143 | static inline void callchain_init(struct callchain_root *root) |
144 | { | 144 | { |
145 | INIT_LIST_HEAD(&root->node.val); | 145 | INIT_LIST_HEAD(&root->node.val); |
146 | INIT_LIST_HEAD(&root->node.parent_val); | ||
146 | 147 | ||
147 | root->node.parent = NULL; | 148 | root->node.parent = NULL; |
148 | root->node.hit = 0; | 149 | root->node.hit = 0; |
diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 2dcfe9a7c8d0..db8142ba7cb9 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/types.h> | 11 | #include <linux/types.h> |
12 | #include "event.h" | 12 | #include "event.h" |
13 | #include "perf_regs.h" | 13 | #include "perf_regs.h" |
14 | #include "callchain.h" | ||
14 | 15 | ||
15 | static char *debuginfo_path; | 16 | static char *debuginfo_path; |
16 | 17 | ||
@@ -52,25 +53,28 @@ static int report_module(u64 ip, struct unwind_info *ui) | |||
52 | return __report_module(&al, ip, ui); | 53 | return __report_module(&al, ip, ui); |
53 | } | 54 | } |
54 | 55 | ||
56 | /* | ||
57 | * Store all entries within entries array, | ||
58 | * we will process it after we finish unwind. | ||
59 | */ | ||
55 | static int entry(u64 ip, struct unwind_info *ui) | 60 | static int entry(u64 ip, struct unwind_info *ui) |
56 | 61 | ||
57 | { | 62 | { |
58 | struct unwind_entry e; | 63 | struct unwind_entry *e = &ui->entries[ui->idx++]; |
59 | struct addr_location al; | 64 | struct addr_location al; |
60 | 65 | ||
61 | if (__report_module(&al, ip, ui)) | 66 | if (__report_module(&al, ip, ui)) |
62 | return -1; | 67 | return -1; |
63 | 68 | ||
64 | e.ip = ip; | 69 | e->ip = ip; |
65 | e.map = al.map; | 70 | e->map = al.map; |
66 | e.sym = al.sym; | 71 | e->sym = al.sym; |
67 | 72 | ||
68 | pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", | 73 | pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", |
69 | al.sym ? al.sym->name : "''", | 74 | al.sym ? al.sym->name : "''", |
70 | ip, | 75 | ip, |
71 | al.map ? al.map->map_ip(al.map, ip) : (u64) 0); | 76 | al.map ? al.map->map_ip(al.map, ip) : (u64) 0); |
72 | 77 | return 0; | |
73 | return ui->cb(&e, ui->arg); | ||
74 | } | 78 | } |
75 | 79 | ||
76 | static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp) | 80 | static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp) |
@@ -168,7 +172,7 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | |||
168 | struct perf_sample *data, | 172 | struct perf_sample *data, |
169 | int max_stack) | 173 | int max_stack) |
170 | { | 174 | { |
171 | struct unwind_info ui = { | 175 | struct unwind_info *ui, ui_buf = { |
172 | .sample = data, | 176 | .sample = data, |
173 | .thread = thread, | 177 | .thread = thread, |
174 | .machine = thread->mg->machine, | 178 | .machine = thread->mg->machine, |
@@ -177,35 +181,54 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | |||
177 | .max_stack = max_stack, | 181 | .max_stack = max_stack, |
178 | }; | 182 | }; |
179 | Dwarf_Word ip; | 183 | Dwarf_Word ip; |
180 | int err = -EINVAL; | 184 | int err = -EINVAL, i; |
181 | 185 | ||
182 | if (!data->user_regs.regs) | 186 | if (!data->user_regs.regs) |
183 | return -EINVAL; | 187 | return -EINVAL; |
184 | 188 | ||
185 | ui.dwfl = dwfl_begin(&offline_callbacks); | 189 | ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack); |
186 | if (!ui.dwfl) | 190 | if (!ui) |
191 | return -ENOMEM; | ||
192 | |||
193 | *ui = ui_buf; | ||
194 | |||
195 | ui->dwfl = dwfl_begin(&offline_callbacks); | ||
196 | if (!ui->dwfl) | ||
187 | goto out; | 197 | goto out; |
188 | 198 | ||
189 | err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); | 199 | err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); |
190 | if (err) | 200 | if (err) |
191 | goto out; | 201 | goto out; |
192 | 202 | ||
193 | err = report_module(ip, &ui); | 203 | err = report_module(ip, ui); |
194 | if (err) | 204 | if (err) |
195 | goto out; | 205 | goto out; |
196 | 206 | ||
197 | if (!dwfl_attach_state(ui.dwfl, EM_NONE, thread->tid, &callbacks, &ui)) | 207 | if (!dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui)) |
198 | goto out; | 208 | goto out; |
199 | 209 | ||
200 | err = dwfl_getthread_frames(ui.dwfl, thread->tid, frame_callback, &ui); | 210 | err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui); |
201 | 211 | ||
202 | if (err && !ui.max_stack) | 212 | if (err && !ui->max_stack) |
203 | err = 0; | 213 | err = 0; |
204 | 214 | ||
215 | /* | ||
216 | * Display what we got based on the order setup. | ||
217 | */ | ||
218 | for (i = 0; i < ui->idx && !err; i++) { | ||
219 | int j = i; | ||
220 | |||
221 | if (callchain_param.order == ORDER_CALLER) | ||
222 | j = ui->idx - i - 1; | ||
223 | |||
224 | err = ui->entries[j].ip ? ui->cb(&ui->entries[j], ui->arg) : 0; | ||
225 | } | ||
226 | |||
205 | out: | 227 | out: |
206 | if (err) | 228 | if (err) |
207 | pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1)); | 229 | pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1)); |
208 | 230 | ||
209 | dwfl_end(ui.dwfl); | 231 | dwfl_end(ui->dwfl); |
232 | free(ui); | ||
210 | return 0; | 233 | return 0; |
211 | } | 234 | } |
diff --git a/tools/perf/util/unwind-libdw.h b/tools/perf/util/unwind-libdw.h index 417a1426f3ad..58328669ed16 100644 --- a/tools/perf/util/unwind-libdw.h +++ b/tools/perf/util/unwind-libdw.h | |||
@@ -16,6 +16,8 @@ struct unwind_info { | |||
16 | unwind_entry_cb_t cb; | 16 | unwind_entry_cb_t cb; |
17 | void *arg; | 17 | void *arg; |
18 | int max_stack; | 18 | int max_stack; |
19 | int idx; | ||
20 | struct unwind_entry entries[]; | ||
19 | }; | 21 | }; |
20 | 22 | ||
21 | #endif /* __PERF_UNWIND_LIBDW_H */ | 23 | #endif /* __PERF_UNWIND_LIBDW_H */ |
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index c83832b555e5..3c258a0e4092 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c | |||
@@ -614,23 +614,48 @@ void unwind__finish_access(struct thread *thread) | |||
614 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | 614 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, |
615 | void *arg, int max_stack) | 615 | void *arg, int max_stack) |
616 | { | 616 | { |
617 | u64 val; | ||
618 | unw_word_t ips[max_stack]; | ||
617 | unw_addr_space_t addr_space; | 619 | unw_addr_space_t addr_space; |
618 | unw_cursor_t c; | 620 | unw_cursor_t c; |
619 | int ret; | 621 | int ret, i = 0; |
620 | |||
621 | addr_space = thread__priv(ui->thread); | ||
622 | if (addr_space == NULL) | ||
623 | return -1; | ||
624 | 622 | ||
625 | ret = unw_init_remote(&c, addr_space, ui); | 623 | ret = perf_reg_value(&val, &ui->sample->user_regs, PERF_REG_IP); |
626 | if (ret) | 624 | if (ret) |
627 | display_error(ret); | 625 | return ret; |
628 | 626 | ||
629 | while (!ret && (unw_step(&c) > 0) && max_stack--) { | 627 | ips[i++] = (unw_word_t) val; |
630 | unw_word_t ip; | ||
631 | 628 | ||
632 | unw_get_reg(&c, UNW_REG_IP, &ip); | 629 | /* |
633 | ret = ip ? entry(ip, ui->thread, cb, arg) : 0; | 630 | * If we need more than one entry, do the DWARF |
631 | * unwind itself. | ||
632 | */ | ||
633 | if (max_stack - 1 > 0) { | ||
634 | addr_space = thread__priv(ui->thread); | ||
635 | if (addr_space == NULL) | ||
636 | return -1; | ||
637 | |||
638 | ret = unw_init_remote(&c, addr_space, ui); | ||
639 | if (ret) | ||
640 | display_error(ret); | ||
641 | |||
642 | while (!ret && (unw_step(&c) > 0) && i < max_stack) { | ||
643 | unw_get_reg(&c, UNW_REG_IP, &ips[i]); | ||
644 | ++i; | ||
645 | } | ||
646 | |||
647 | max_stack = i; | ||
648 | } | ||
649 | |||
650 | /* | ||
651 | * Display what we got based on the order setup. | ||
652 | */ | ||
653 | for (i = 0; i < max_stack && !ret; i++) { | ||
654 | int j = i; | ||
655 | |||
656 | if (callchain_param.order == ORDER_CALLER) | ||
657 | j = max_stack - i - 1; | ||
658 | ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0; | ||
634 | } | 659 | } |
635 | 660 | ||
636 | return ret; | 661 | return ret; |
@@ -640,24 +665,17 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | |||
640 | struct thread *thread, | 665 | struct thread *thread, |
641 | struct perf_sample *data, int max_stack) | 666 | struct perf_sample *data, int max_stack) |
642 | { | 667 | { |
643 | u64 ip; | ||
644 | struct unwind_info ui = { | 668 | struct unwind_info ui = { |
645 | .sample = data, | 669 | .sample = data, |
646 | .thread = thread, | 670 | .thread = thread, |
647 | .machine = thread->mg->machine, | 671 | .machine = thread->mg->machine, |
648 | }; | 672 | }; |
649 | int ret; | ||
650 | 673 | ||
651 | if (!data->user_regs.regs) | 674 | if (!data->user_regs.regs) |
652 | return -EINVAL; | 675 | return -EINVAL; |
653 | 676 | ||
654 | ret = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); | 677 | if (max_stack <= 0) |
655 | if (ret) | 678 | return -EINVAL; |
656 | return ret; | ||
657 | |||
658 | ret = entry(ip, thread, cb, arg); | ||
659 | if (ret) | ||
660 | return -ENOMEM; | ||
661 | 679 | ||
662 | return --max_stack > 0 ? get_entries(&ui, cb, arg, max_stack) : 0; | 680 | return get_entries(&ui, cb, arg, max_stack); |
663 | } | 681 | } |