diff options
author | Tejun Heo <tj@kernel.org> | 2011-11-28 12:46:22 -0500 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2011-11-28 12:46:22 -0500 |
commit | d4bbf7e7759afc172e2bfbc5c416324590049cdd (patch) | |
tree | 7eab5ee5481cd3dcf1162329fec827177640018a /tools/perf/util | |
parent | a150439c4a97db379f0ed6faa46fbbb6e7bf3cb2 (diff) | |
parent | 401d0069cb344f401bc9d264c31db55876ff78c0 (diff) |
Merge branch 'master' into x86/memblock
Conflicts & resolutions:
* arch/x86/xen/setup.c
dc91c728fd "xen: allow extra memory to be in multiple regions"
24aa07882b "memblock, x86: Replace memblock_x86_reserve/free..."
conflicted on xen_add_extra_mem() updates. The resolution is
trivial as the latter just want to replace
memblock_x86_reserve_range() with memblock_reserve().
* drivers/pci/intel-iommu.c
166e9278a3f "x86/ia64: intel-iommu: move to drivers/iommu/"
5dfe8660a3d "bootmem: Replace work_with_active_regions() with..."
conflicted as the former moved the file under drivers/iommu/.
Resolved by applying the chnages from the latter on the moved
file.
* mm/Kconfig
6661672053a "memblock: add NO_BOOTMEM config symbol"
c378ddd53f9 "memblock, x86: Make ARCH_DISCARD_MEMBLOCK a config option"
conflicted trivially. Both added config options. Just
letting both add their own options resolves the conflict.
* mm/memblock.c
d1f0ece6cdc "mm/memblock.c: small function definition fixes"
ed7b56a799c "memblock: Remove memblock_memory_can_coalesce()"
confliected. The former updates function removed by the
latter. Resolution is trivial.
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'tools/perf/util')
58 files changed, 5152 insertions, 2291 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index e01af2b1a469..119e996035c8 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -16,6 +16,8 @@ | |||
16 | #include "annotate.h" | 16 | #include "annotate.h" |
17 | #include <pthread.h> | 17 | #include <pthread.h> |
18 | 18 | ||
19 | const char *disassembler_style; | ||
20 | |||
19 | int symbol__annotate_init(struct map *map __used, struct symbol *sym) | 21 | int symbol__annotate_init(struct map *map __used, struct symbol *sym) |
20 | { | 22 | { |
21 | struct annotation *notes = symbol__annotation(sym); | 23 | struct annotation *notes = symbol__annotation(sym); |
@@ -308,9 +310,12 @@ fallback: | |||
308 | } | 310 | } |
309 | err = -ENOENT; | 311 | err = -ENOENT; |
310 | dso->annotate_warned = 1; | 312 | dso->annotate_warned = 1; |
311 | pr_err("Can't annotate %s: No vmlinux file%s was found in the " | 313 | pr_err("Can't annotate %s:\n\n" |
312 | "path.\nPlease use 'perf buildid-cache -av vmlinux' or " | 314 | "No vmlinux file%s\nwas found in the path.\n\n" |
313 | "--vmlinux vmlinux.\n", | 315 | "Please use:\n\n" |
316 | " perf buildid-cache -av vmlinux\n\n" | ||
317 | "or:\n\n" | ||
318 | " --vmlinux vmlinux", | ||
314 | sym->name, build_id_msg ?: ""); | 319 | sym->name, build_id_msg ?: ""); |
315 | goto out_free_filename; | 320 | goto out_free_filename; |
316 | } | 321 | } |
@@ -323,10 +328,15 @@ fallback: | |||
323 | dso, dso->long_name, sym, sym->name); | 328 | dso, dso->long_name, sym, sym->name); |
324 | 329 | ||
325 | snprintf(command, sizeof(command), | 330 | snprintf(command, sizeof(command), |
326 | "objdump --start-address=0x%016" PRIx64 | 331 | "objdump %s%s --start-address=0x%016" PRIx64 |
327 | " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", | 332 | " --stop-address=0x%016" PRIx64 |
333 | " -d %s %s -C %s|grep -v %s|expand", | ||
334 | disassembler_style ? "-M " : "", | ||
335 | disassembler_style ? disassembler_style : "", | ||
328 | map__rip_2objdump(map, sym->start), | 336 | map__rip_2objdump(map, sym->start), |
329 | map__rip_2objdump(map, sym->end), | 337 | map__rip_2objdump(map, sym->end), |
338 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", | ||
339 | symbol_conf.annotate_src ? "-S" : "", | ||
330 | symfs_filename, filename); | 340 | symfs_filename, filename); |
331 | 341 | ||
332 | pr_debug("Executing: %s\n", command); | 342 | pr_debug("Executing: %s\n", command); |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index c2c286896801..d9072523d342 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -91,13 +91,18 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | |||
91 | #ifdef NO_NEWT_SUPPORT | 91 | #ifdef NO_NEWT_SUPPORT |
92 | static inline int symbol__tui_annotate(struct symbol *sym __used, | 92 | static inline int symbol__tui_annotate(struct symbol *sym __used, |
93 | struct map *map __used, | 93 | struct map *map __used, |
94 | int evidx __used, int refresh __used) | 94 | int evidx __used, |
95 | void(*timer)(void *arg) __used, | ||
96 | void *arg __used, int delay_secs __used) | ||
95 | { | 97 | { |
96 | return 0; | 98 | return 0; |
97 | } | 99 | } |
98 | #else | 100 | #else |
99 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 101 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
100 | int refresh); | 102 | int nr_events, void(*timer)(void *arg), void *arg, |
103 | int delay_secs); | ||
101 | #endif | 104 | #endif |
102 | 105 | ||
106 | extern const char *disassembler_style; | ||
107 | |||
103 | #endif /* __PERF_ANNOTATE_H */ | 108 | #endif /* __PERF_ANNOTATE_H */ |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 1a79df9f739f..9b4ff16cac96 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -14,6 +14,11 @@ enum chain_mode { | |||
14 | CHAIN_GRAPH_REL | 14 | CHAIN_GRAPH_REL |
15 | }; | 15 | }; |
16 | 16 | ||
17 | enum chain_order { | ||
18 | ORDER_CALLER, | ||
19 | ORDER_CALLEE | ||
20 | }; | ||
21 | |||
17 | struct callchain_node { | 22 | struct callchain_node { |
18 | struct callchain_node *parent; | 23 | struct callchain_node *parent; |
19 | struct list_head siblings; | 24 | struct list_head siblings; |
@@ -41,6 +46,7 @@ struct callchain_param { | |||
41 | u32 print_limit; | 46 | u32 print_limit; |
42 | double min_percent; | 47 | double min_percent; |
43 | sort_chain_func_t sort; | 48 | sort_chain_func_t sort; |
49 | enum chain_order order; | ||
44 | }; | 50 | }; |
45 | 51 | ||
46 | struct callchain_list { | 52 | struct callchain_list { |
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index e191eb9a667f..521c38a79190 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
@@ -200,7 +200,7 @@ static int __color_vfprintf(FILE *fp, const char *color, const char *fmt, | |||
200 | * Auto-detect: | 200 | * Auto-detect: |
201 | */ | 201 | */ |
202 | if (perf_use_color_default < 0) { | 202 | if (perf_use_color_default < 0) { |
203 | if (isatty(1) || pager_in_use()) | 203 | if (isatty(fileno(fp)) || pager_in_use()) |
204 | perf_use_color_default = 1; | 204 | perf_use_color_default = 1; |
205 | else | 205 | else |
206 | perf_use_color_default = 0; | 206 | perf_use_color_default = 0; |
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c index e02d78cae70f..80d9598db31a 100644 --- a/tools/perf/util/config.c +++ b/tools/perf/util/config.c | |||
@@ -341,7 +341,7 @@ const char *perf_config_dirname(const char *name, const char *value) | |||
341 | 341 | ||
342 | static int perf_default_core_config(const char *var __used, const char *value __used) | 342 | static int perf_default_core_config(const char *var __used, const char *value __used) |
343 | { | 343 | { |
344 | /* Add other config variables here and to Documentation/config.txt. */ | 344 | /* Add other config variables here. */ |
345 | return 0; | 345 | return 0; |
346 | } | 346 | } |
347 | 347 | ||
@@ -350,7 +350,7 @@ int perf_default_config(const char *var, const char *value, void *dummy __used) | |||
350 | if (!prefixcmp(var, "core.")) | 350 | if (!prefixcmp(var, "core.")) |
351 | return perf_default_core_config(var, value); | 351 | return perf_default_core_config(var, value); |
352 | 352 | ||
353 | /* Add other config variables here and to Documentation/config.txt. */ | 353 | /* Add other config variables here. */ |
354 | return 0; | 354 | return 0; |
355 | } | 355 | } |
356 | 356 | ||
@@ -399,7 +399,6 @@ static int perf_config_global(void) | |||
399 | int perf_config(config_fn_t fn, void *data) | 399 | int perf_config(config_fn_t fn, void *data) |
400 | { | 400 | { |
401 | int ret = 0, found = 0; | 401 | int ret = 0, found = 0; |
402 | char *repo_config = NULL; | ||
403 | const char *home = NULL; | 402 | const char *home = NULL; |
404 | 403 | ||
405 | /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ | 404 | /* Setting $PERF_CONFIG makes perf read _only_ the given config file. */ |
@@ -414,19 +413,32 @@ int perf_config(config_fn_t fn, void *data) | |||
414 | home = getenv("HOME"); | 413 | home = getenv("HOME"); |
415 | if (perf_config_global() && home) { | 414 | if (perf_config_global() && home) { |
416 | char *user_config = strdup(mkpath("%s/.perfconfig", home)); | 415 | char *user_config = strdup(mkpath("%s/.perfconfig", home)); |
417 | if (!access(user_config, R_OK)) { | 416 | struct stat st; |
418 | ret += perf_config_from_file(fn, user_config, data); | 417 | |
419 | found += 1; | 418 | if (user_config == NULL) { |
419 | warning("Not enough memory to process %s/.perfconfig, " | ||
420 | "ignoring it.", home); | ||
421 | goto out; | ||
420 | } | 422 | } |
421 | free(user_config); | ||
422 | } | ||
423 | 423 | ||
424 | repo_config = perf_pathdup("config"); | 424 | if (stat(user_config, &st) < 0) |
425 | if (!access(repo_config, R_OK)) { | 425 | goto out_free; |
426 | ret += perf_config_from_file(fn, repo_config, data); | 426 | |
427 | if (st.st_uid && (st.st_uid != geteuid())) { | ||
428 | warning("File %s not owned by current user or root, " | ||
429 | "ignoring it.", user_config); | ||
430 | goto out_free; | ||
431 | } | ||
432 | |||
433 | if (!st.st_size) | ||
434 | goto out_free; | ||
435 | |||
436 | ret += perf_config_from_file(fn, user_config, data); | ||
427 | found += 1; | 437 | found += 1; |
438 | out_free: | ||
439 | free(user_config); | ||
428 | } | 440 | } |
429 | free(repo_config); | 441 | out: |
430 | if (found == 0) | 442 | if (found == 0) |
431 | return -1; | 443 | return -1; |
432 | return ret; | 444 | return ret; |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 155749d74350..26817daa2961 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -47,19 +47,20 @@ int dump_printf(const char *fmt, ...) | |||
47 | } | 47 | } |
48 | 48 | ||
49 | #ifdef NO_NEWT_SUPPORT | 49 | #ifdef NO_NEWT_SUPPORT |
50 | void ui__warning(const char *format, ...) | 50 | int ui__warning(const char *format, ...) |
51 | { | 51 | { |
52 | va_list args; | 52 | va_list args; |
53 | 53 | ||
54 | va_start(args, format); | 54 | va_start(args, format); |
55 | vfprintf(stderr, format, args); | 55 | vfprintf(stderr, format, args); |
56 | va_end(args); | 56 | va_end(args); |
57 | return 0; | ||
57 | } | 58 | } |
58 | #endif | 59 | #endif |
59 | 60 | ||
60 | void ui__warning_paranoid(void) | 61 | int ui__error_paranoid(void) |
61 | { | 62 | { |
62 | ui__warning("Permission error - are you root?\n" | 63 | return ui__error("Permission error - are you root?\n" |
63 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" | 64 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" |
64 | " -1 - Not paranoid at all\n" | 65 | " -1 - Not paranoid at all\n" |
65 | " 0 - Disallow raw tracepoint access for unpriv\n" | 66 | " 0 - Disallow raw tracepoint access for unpriv\n" |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index fd53db47e3de..f2ce88d04f54 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -19,23 +19,18 @@ static inline int ui_helpline__show_help(const char *format __used, va_list ap _ | |||
19 | return 0; | 19 | return 0; |
20 | } | 20 | } |
21 | 21 | ||
22 | static inline struct ui_progress *ui_progress__new(const char *title __used, | 22 | static inline void ui_progress__update(u64 curr __used, u64 total __used, |
23 | u64 total __used) | 23 | const char *title __used) {} |
24 | { | ||
25 | return (struct ui_progress *)1; | ||
26 | } | ||
27 | |||
28 | static inline void ui_progress__update(struct ui_progress *self __used, | ||
29 | u64 curr __used) {} | ||
30 | 24 | ||
31 | static inline void ui_progress__delete(struct ui_progress *self __used) {} | 25 | #define ui__error(format, arg...) ui__warning(format, ##arg) |
32 | #else | 26 | #else |
33 | extern char ui_helpline__last_msg[]; | 27 | extern char ui_helpline__last_msg[]; |
34 | int ui_helpline__show_help(const char *format, va_list ap); | 28 | int ui_helpline__show_help(const char *format, va_list ap); |
35 | #include "ui/progress.h" | 29 | #include "ui/progress.h" |
30 | int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); | ||
36 | #endif | 31 | #endif |
37 | 32 | ||
38 | void ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); | 33 | int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); |
39 | void ui__warning_paranoid(void); | 34 | int ui__error_paranoid(void); |
40 | 35 | ||
41 | #endif /* __PERF_DEBUG_H */ | 36 | #endif /* __PERF_DEBUG_H */ |
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c new file mode 100644 index 000000000000..ee51e9b4dc09 --- /dev/null +++ b/tools/perf/util/dwarf-aux.c | |||
@@ -0,0 +1,843 @@ | |||
1 | /* | ||
2 | * dwarf-aux.c : libdw auxiliary interfaces | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <stdbool.h> | ||
21 | #include "util.h" | ||
22 | #include "debug.h" | ||
23 | #include "dwarf-aux.h" | ||
24 | |||
25 | /** | ||
26 | * cu_find_realpath - Find the realpath of the target file | ||
27 | * @cu_die: A DIE(dwarf information entry) of CU(compilation Unit) | ||
28 | * @fname: The tail filename of the target file | ||
29 | * | ||
30 | * Find the real(long) path of @fname in @cu_die. | ||
31 | */ | ||
32 | const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
33 | { | ||
34 | Dwarf_Files *files; | ||
35 | size_t nfiles, i; | ||
36 | const char *src = NULL; | ||
37 | int ret; | ||
38 | |||
39 | if (!fname) | ||
40 | return NULL; | ||
41 | |||
42 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); | ||
43 | if (ret != 0) | ||
44 | return NULL; | ||
45 | |||
46 | for (i = 0; i < nfiles; i++) { | ||
47 | src = dwarf_filesrc(files, i, NULL, NULL); | ||
48 | if (strtailcmp(src, fname) == 0) | ||
49 | break; | ||
50 | } | ||
51 | if (i == nfiles) | ||
52 | return NULL; | ||
53 | return src; | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * cu_get_comp_dir - Get the path of compilation directory | ||
58 | * @cu_die: a CU DIE | ||
59 | * | ||
60 | * Get the path of compilation directory of given @cu_die. | ||
61 | * Since this depends on DW_AT_comp_dir, older gcc will not | ||
62 | * embedded it. In that case, this returns NULL. | ||
63 | */ | ||
64 | const char *cu_get_comp_dir(Dwarf_Die *cu_die) | ||
65 | { | ||
66 | Dwarf_Attribute attr; | ||
67 | if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) | ||
68 | return NULL; | ||
69 | return dwarf_formstring(&attr); | ||
70 | } | ||
71 | |||
72 | /** | ||
73 | * cu_find_lineinfo - Get a line number and file name for given address | ||
74 | * @cu_die: a CU DIE | ||
75 | * @addr: An address | ||
76 | * @fname: a pointer which returns the file name string | ||
77 | * @lineno: a pointer which returns the line number | ||
78 | * | ||
79 | * Find a line number and file name for @addr in @cu_die. | ||
80 | */ | ||
81 | int cu_find_lineinfo(Dwarf_Die *cu_die, unsigned long addr, | ||
82 | const char **fname, int *lineno) | ||
83 | { | ||
84 | Dwarf_Line *line; | ||
85 | Dwarf_Addr laddr; | ||
86 | |||
87 | line = dwarf_getsrc_die(cu_die, (Dwarf_Addr)addr); | ||
88 | if (line && dwarf_lineaddr(line, &laddr) == 0 && | ||
89 | addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) { | ||
90 | *fname = dwarf_linesrc(line, NULL, NULL); | ||
91 | if (!*fname) | ||
92 | /* line number is useless without filename */ | ||
93 | *lineno = 0; | ||
94 | } | ||
95 | |||
96 | return *lineno ?: -ENOENT; | ||
97 | } | ||
98 | |||
99 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data); | ||
100 | |||
101 | /** | ||
102 | * cu_walk_functions_at - Walk on function DIEs at given address | ||
103 | * @cu_die: A CU DIE | ||
104 | * @addr: An address | ||
105 | * @callback: A callback which called with found DIEs | ||
106 | * @data: A user data | ||
107 | * | ||
108 | * Walk on function DIEs at given @addr in @cu_die. Passed DIEs | ||
109 | * should be subprogram or inlined-subroutines. | ||
110 | */ | ||
111 | int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
112 | int (*callback)(Dwarf_Die *, void *), void *data) | ||
113 | { | ||
114 | Dwarf_Die die_mem; | ||
115 | Dwarf_Die *sc_die; | ||
116 | int ret = -ENOENT; | ||
117 | |||
118 | /* Inlined function could be recursive. Trace it until fail */ | ||
119 | for (sc_die = die_find_realfunc(cu_die, addr, &die_mem); | ||
120 | sc_die != NULL; | ||
121 | sc_die = die_find_child(sc_die, __die_find_inline_cb, &addr, | ||
122 | &die_mem)) { | ||
123 | ret = callback(sc_die, data); | ||
124 | if (ret) | ||
125 | break; | ||
126 | } | ||
127 | |||
128 | return ret; | ||
129 | |||
130 | } | ||
131 | |||
132 | /** | ||
133 | * die_compare_name - Compare diename and tname | ||
134 | * @dw_die: a DIE | ||
135 | * @tname: a string of target name | ||
136 | * | ||
137 | * Compare the name of @dw_die and @tname. Return false if @dw_die has no name. | ||
138 | */ | ||
139 | bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
140 | { | ||
141 | const char *name; | ||
142 | name = dwarf_diename(dw_die); | ||
143 | return name ? (strcmp(tname, name) == 0) : false; | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * die_get_call_lineno - Get callsite line number of inline-function instance | ||
148 | * @in_die: a DIE of an inlined function instance | ||
149 | * | ||
150 | * Get call-site line number of @in_die. This means from where the inline | ||
151 | * function is called. | ||
152 | */ | ||
153 | int die_get_call_lineno(Dwarf_Die *in_die) | ||
154 | { | ||
155 | Dwarf_Attribute attr; | ||
156 | Dwarf_Word ret; | ||
157 | |||
158 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
159 | return -ENOENT; | ||
160 | |||
161 | dwarf_formudata(&attr, &ret); | ||
162 | return (int)ret; | ||
163 | } | ||
164 | |||
165 | /** | ||
166 | * die_get_type - Get type DIE | ||
167 | * @vr_die: a DIE of a variable | ||
168 | * @die_mem: where to store a type DIE | ||
169 | * | ||
170 | * Get a DIE of the type of given variable (@vr_die), and store | ||
171 | * it to die_mem. Return NULL if fails to get a type DIE. | ||
172 | */ | ||
173 | Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
174 | { | ||
175 | Dwarf_Attribute attr; | ||
176 | |||
177 | if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) && | ||
178 | dwarf_formref_die(&attr, die_mem)) | ||
179 | return die_mem; | ||
180 | else | ||
181 | return NULL; | ||
182 | } | ||
183 | |||
184 | /* Get a type die, but skip qualifiers */ | ||
185 | static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
186 | { | ||
187 | int tag; | ||
188 | |||
189 | do { | ||
190 | vr_die = die_get_type(vr_die, die_mem); | ||
191 | if (!vr_die) | ||
192 | break; | ||
193 | tag = dwarf_tag(vr_die); | ||
194 | } while (tag == DW_TAG_const_type || | ||
195 | tag == DW_TAG_restrict_type || | ||
196 | tag == DW_TAG_volatile_type || | ||
197 | tag == DW_TAG_shared_type); | ||
198 | |||
199 | return vr_die; | ||
200 | } | ||
201 | |||
202 | /** | ||
203 | * die_get_real_type - Get a type die, but skip qualifiers and typedef | ||
204 | * @vr_die: a DIE of a variable | ||
205 | * @die_mem: where to store a type DIE | ||
206 | * | ||
207 | * Get a DIE of the type of given variable (@vr_die), and store | ||
208 | * it to die_mem. Return NULL if fails to get a type DIE. | ||
209 | * If the type is qualifiers (e.g. const) or typedef, this skips it | ||
210 | * and tries to find real type (structure or basic types, e.g. int). | ||
211 | */ | ||
212 | Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
213 | { | ||
214 | do { | ||
215 | vr_die = __die_get_real_type(vr_die, die_mem); | ||
216 | } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef); | ||
217 | |||
218 | return vr_die; | ||
219 | } | ||
220 | |||
221 | /* Get attribute and translate it as a udata */ | ||
222 | static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, | ||
223 | Dwarf_Word *result) | ||
224 | { | ||
225 | Dwarf_Attribute attr; | ||
226 | |||
227 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | ||
228 | dwarf_formudata(&attr, result) != 0) | ||
229 | return -ENOENT; | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | /* Get attribute and translate it as a sdata */ | ||
235 | static int die_get_attr_sdata(Dwarf_Die *tp_die, unsigned int attr_name, | ||
236 | Dwarf_Sword *result) | ||
237 | { | ||
238 | Dwarf_Attribute attr; | ||
239 | |||
240 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | ||
241 | dwarf_formsdata(&attr, result) != 0) | ||
242 | return -ENOENT; | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | /** | ||
248 | * die_is_signed_type - Check whether a type DIE is signed or not | ||
249 | * @tp_die: a DIE of a type | ||
250 | * | ||
251 | * Get the encoding of @tp_die and return true if the encoding | ||
252 | * is signed. | ||
253 | */ | ||
254 | bool die_is_signed_type(Dwarf_Die *tp_die) | ||
255 | { | ||
256 | Dwarf_Word ret; | ||
257 | |||
258 | if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) | ||
259 | return false; | ||
260 | |||
261 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | ||
262 | ret == DW_ATE_signed_fixed); | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * die_get_data_member_location - Get the data-member offset | ||
267 | * @mb_die: a DIE of a member of a data structure | ||
268 | * @offs: The offset of the member in the data structure | ||
269 | * | ||
270 | * Get the offset of @mb_die in the data structure including @mb_die, and | ||
271 | * stores result offset to @offs. If any error occurs this returns errno. | ||
272 | */ | ||
273 | int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) | ||
274 | { | ||
275 | Dwarf_Attribute attr; | ||
276 | Dwarf_Op *expr; | ||
277 | size_t nexpr; | ||
278 | int ret; | ||
279 | |||
280 | if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) | ||
281 | return -ENOENT; | ||
282 | |||
283 | if (dwarf_formudata(&attr, offs) != 0) { | ||
284 | /* DW_AT_data_member_location should be DW_OP_plus_uconst */ | ||
285 | ret = dwarf_getlocation(&attr, &expr, &nexpr); | ||
286 | if (ret < 0 || nexpr == 0) | ||
287 | return -ENOENT; | ||
288 | |||
289 | if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { | ||
290 | pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", | ||
291 | expr[0].atom, nexpr); | ||
292 | return -ENOTSUP; | ||
293 | } | ||
294 | *offs = (Dwarf_Word)expr[0].number; | ||
295 | } | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | /* Get the call file index number in CU DIE */ | ||
300 | static int die_get_call_fileno(Dwarf_Die *in_die) | ||
301 | { | ||
302 | Dwarf_Sword idx; | ||
303 | |||
304 | if (die_get_attr_sdata(in_die, DW_AT_call_file, &idx) == 0) | ||
305 | return (int)idx; | ||
306 | else | ||
307 | return -ENOENT; | ||
308 | } | ||
309 | |||
310 | /* Get the declared file index number in CU DIE */ | ||
311 | static int die_get_decl_fileno(Dwarf_Die *pdie) | ||
312 | { | ||
313 | Dwarf_Sword idx; | ||
314 | |||
315 | if (die_get_attr_sdata(pdie, DW_AT_decl_file, &idx) == 0) | ||
316 | return (int)idx; | ||
317 | else | ||
318 | return -ENOENT; | ||
319 | } | ||
320 | |||
321 | /** | ||
322 | * die_get_call_file - Get callsite file name of inlined function instance | ||
323 | * @in_die: a DIE of an inlined function instance | ||
324 | * | ||
325 | * Get call-site file name of @in_die. This means from which file the inline | ||
326 | * function is called. | ||
327 | */ | ||
328 | const char *die_get_call_file(Dwarf_Die *in_die) | ||
329 | { | ||
330 | Dwarf_Die cu_die; | ||
331 | Dwarf_Files *files; | ||
332 | int idx; | ||
333 | |||
334 | idx = die_get_call_fileno(in_die); | ||
335 | if (idx < 0 || !dwarf_diecu(in_die, &cu_die, NULL, NULL) || | ||
336 | dwarf_getsrcfiles(&cu_die, &files, NULL) != 0) | ||
337 | return NULL; | ||
338 | |||
339 | return dwarf_filesrc(files, idx, NULL, NULL); | ||
340 | } | ||
341 | |||
342 | |||
343 | /** | ||
344 | * die_find_child - Generic DIE search function in DIE tree | ||
345 | * @rt_die: a root DIE | ||
346 | * @callback: a callback function | ||
347 | * @data: a user data passed to the callback function | ||
348 | * @die_mem: a buffer for result DIE | ||
349 | * | ||
350 | * Trace DIE tree from @rt_die and call @callback for each child DIE. | ||
351 | * If @callback returns DIE_FIND_CB_END, this stores the DIE into | ||
352 | * @die_mem and returns it. If @callback returns DIE_FIND_CB_CONTINUE, | ||
353 | * this continues to trace the tree. Optionally, @callback can return | ||
354 | * DIE_FIND_CB_CHILD and DIE_FIND_CB_SIBLING, those means trace only | ||
355 | * the children and trace only the siblings respectively. | ||
356 | * Returns NULL if @callback can't find any appropriate DIE. | ||
357 | */ | ||
358 | Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
359 | int (*callback)(Dwarf_Die *, void *), | ||
360 | void *data, Dwarf_Die *die_mem) | ||
361 | { | ||
362 | Dwarf_Die child_die; | ||
363 | int ret; | ||
364 | |||
365 | ret = dwarf_child(rt_die, die_mem); | ||
366 | if (ret != 0) | ||
367 | return NULL; | ||
368 | |||
369 | do { | ||
370 | ret = callback(die_mem, data); | ||
371 | if (ret == DIE_FIND_CB_END) | ||
372 | return die_mem; | ||
373 | |||
374 | if ((ret & DIE_FIND_CB_CHILD) && | ||
375 | die_find_child(die_mem, callback, data, &child_die)) { | ||
376 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
377 | return die_mem; | ||
378 | } | ||
379 | } while ((ret & DIE_FIND_CB_SIBLING) && | ||
380 | dwarf_siblingof(die_mem, die_mem) == 0); | ||
381 | |||
382 | return NULL; | ||
383 | } | ||
384 | |||
385 | struct __addr_die_search_param { | ||
386 | Dwarf_Addr addr; | ||
387 | Dwarf_Die *die_mem; | ||
388 | }; | ||
389 | |||
390 | /* die_find callback for non-inlined function search */ | ||
391 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
392 | { | ||
393 | struct __addr_die_search_param *ad = data; | ||
394 | |||
395 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | ||
396 | dwarf_haspc(fn_die, ad->addr)) { | ||
397 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
398 | return DWARF_CB_ABORT; | ||
399 | } | ||
400 | return DWARF_CB_OK; | ||
401 | } | ||
402 | |||
403 | /** | ||
404 | * die_find_realfunc - Search a non-inlined function at given address | ||
405 | * @cu_die: a CU DIE which including @addr | ||
406 | * @addr: target address | ||
407 | * @die_mem: a buffer for result DIE | ||
408 | * | ||
409 | * Search a non-inlined function DIE which includes @addr. Stores the | ||
410 | * DIE to @die_mem and returns it if found. Returns NULl if failed. | ||
411 | */ | ||
412 | Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
413 | Dwarf_Die *die_mem) | ||
414 | { | ||
415 | struct __addr_die_search_param ad; | ||
416 | ad.addr = addr; | ||
417 | ad.die_mem = die_mem; | ||
418 | /* dwarf_getscopes can't find subprogram. */ | ||
419 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) | ||
420 | return NULL; | ||
421 | else | ||
422 | return die_mem; | ||
423 | } | ||
424 | |||
425 | /* die_find callback for inline function search */ | ||
426 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) | ||
427 | { | ||
428 | Dwarf_Addr *addr = data; | ||
429 | |||
430 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | ||
431 | dwarf_haspc(die_mem, *addr)) | ||
432 | return DIE_FIND_CB_END; | ||
433 | |||
434 | return DIE_FIND_CB_CONTINUE; | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * die_find_inlinefunc - Search an inlined function at given address | ||
439 | * @cu_die: a CU DIE which including @addr | ||
440 | * @addr: target address | ||
441 | * @die_mem: a buffer for result DIE | ||
442 | * | ||
443 | * Search an inlined function DIE which includes @addr. Stores the | ||
444 | * DIE to @die_mem and returns it if found. Returns NULl if failed. | ||
445 | * If several inlined functions are expanded recursively, this trace | ||
446 | * it and returns deepest one. | ||
447 | */ | ||
448 | Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
449 | Dwarf_Die *die_mem) | ||
450 | { | ||
451 | Dwarf_Die tmp_die; | ||
452 | |||
453 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die); | ||
454 | if (!sp_die) | ||
455 | return NULL; | ||
456 | |||
457 | /* Inlined function could be recursive. Trace it until fail */ | ||
458 | while (sp_die) { | ||
459 | memcpy(die_mem, sp_die, sizeof(Dwarf_Die)); | ||
460 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, | ||
461 | &tmp_die); | ||
462 | } | ||
463 | |||
464 | return die_mem; | ||
465 | } | ||
466 | |||
467 | struct __instance_walk_param { | ||
468 | void *addr; | ||
469 | int (*callback)(Dwarf_Die *, void *); | ||
470 | void *data; | ||
471 | int retval; | ||
472 | }; | ||
473 | |||
474 | static int __die_walk_instances_cb(Dwarf_Die *inst, void *data) | ||
475 | { | ||
476 | struct __instance_walk_param *iwp = data; | ||
477 | Dwarf_Attribute attr_mem; | ||
478 | Dwarf_Die origin_mem; | ||
479 | Dwarf_Attribute *attr; | ||
480 | Dwarf_Die *origin; | ||
481 | int tmp; | ||
482 | |||
483 | attr = dwarf_attr(inst, DW_AT_abstract_origin, &attr_mem); | ||
484 | if (attr == NULL) | ||
485 | return DIE_FIND_CB_CONTINUE; | ||
486 | |||
487 | origin = dwarf_formref_die(attr, &origin_mem); | ||
488 | if (origin == NULL || origin->addr != iwp->addr) | ||
489 | return DIE_FIND_CB_CONTINUE; | ||
490 | |||
491 | /* Ignore redundant instances */ | ||
492 | if (dwarf_tag(inst) == DW_TAG_inlined_subroutine) { | ||
493 | dwarf_decl_line(origin, &tmp); | ||
494 | if (die_get_call_lineno(inst) == tmp) { | ||
495 | tmp = die_get_decl_fileno(origin); | ||
496 | if (die_get_call_fileno(inst) == tmp) | ||
497 | return DIE_FIND_CB_CONTINUE; | ||
498 | } | ||
499 | } | ||
500 | |||
501 | iwp->retval = iwp->callback(inst, iwp->data); | ||
502 | |||
503 | return (iwp->retval) ? DIE_FIND_CB_END : DIE_FIND_CB_CONTINUE; | ||
504 | } | ||
505 | |||
506 | /** | ||
507 | * die_walk_instances - Walk on instances of given DIE | ||
508 | * @or_die: an abstract original DIE | ||
509 | * @callback: a callback function which is called with instance DIE | ||
510 | * @data: user data | ||
511 | * | ||
512 | * Walk on the instances of give @in_die. @in_die must be an inlined function | ||
513 | * declartion. This returns the return value of @callback if it returns | ||
514 | * non-zero value, or -ENOENT if there is no instance. | ||
515 | */ | ||
516 | int die_walk_instances(Dwarf_Die *or_die, int (*callback)(Dwarf_Die *, void *), | ||
517 | void *data) | ||
518 | { | ||
519 | Dwarf_Die cu_die; | ||
520 | Dwarf_Die die_mem; | ||
521 | struct __instance_walk_param iwp = { | ||
522 | .addr = or_die->addr, | ||
523 | .callback = callback, | ||
524 | .data = data, | ||
525 | .retval = -ENOENT, | ||
526 | }; | ||
527 | |||
528 | if (dwarf_diecu(or_die, &cu_die, NULL, NULL) == NULL) | ||
529 | return -ENOENT; | ||
530 | |||
531 | die_find_child(&cu_die, __die_walk_instances_cb, &iwp, &die_mem); | ||
532 | |||
533 | return iwp.retval; | ||
534 | } | ||
535 | |||
536 | /* Line walker internal parameters */ | ||
537 | struct __line_walk_param { | ||
538 | bool recursive; | ||
539 | line_walk_callback_t callback; | ||
540 | void *data; | ||
541 | int retval; | ||
542 | }; | ||
543 | |||
544 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
545 | { | ||
546 | struct __line_walk_param *lw = data; | ||
547 | Dwarf_Addr addr = 0; | ||
548 | const char *fname; | ||
549 | int lineno; | ||
550 | |||
551 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
552 | fname = die_get_call_file(in_die); | ||
553 | lineno = die_get_call_lineno(in_die); | ||
554 | if (fname && lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
555 | lw->retval = lw->callback(fname, lineno, addr, lw->data); | ||
556 | if (lw->retval != 0) | ||
557 | return DIE_FIND_CB_END; | ||
558 | } | ||
559 | } | ||
560 | if (!lw->recursive) | ||
561 | /* Don't need to search recursively */ | ||
562 | return DIE_FIND_CB_SIBLING; | ||
563 | |||
564 | if (addr) { | ||
565 | fname = dwarf_decl_file(in_die); | ||
566 | if (fname && dwarf_decl_line(in_die, &lineno) == 0) { | ||
567 | lw->retval = lw->callback(fname, lineno, addr, lw->data); | ||
568 | if (lw->retval != 0) | ||
569 | return DIE_FIND_CB_END; | ||
570 | } | ||
571 | } | ||
572 | |||
573 | /* Continue to search nested inlined function call-sites */ | ||
574 | return DIE_FIND_CB_CONTINUE; | ||
575 | } | ||
576 | |||
577 | /* Walk on lines of blocks included in given DIE */ | ||
578 | static int __die_walk_funclines(Dwarf_Die *sp_die, bool recursive, | ||
579 | line_walk_callback_t callback, void *data) | ||
580 | { | ||
581 | struct __line_walk_param lw = { | ||
582 | .recursive = recursive, | ||
583 | .callback = callback, | ||
584 | .data = data, | ||
585 | .retval = 0, | ||
586 | }; | ||
587 | Dwarf_Die die_mem; | ||
588 | Dwarf_Addr addr; | ||
589 | const char *fname; | ||
590 | int lineno; | ||
591 | |||
592 | /* Handle function declaration line */ | ||
593 | fname = dwarf_decl_file(sp_die); | ||
594 | if (fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
595 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
596 | lw.retval = callback(fname, lineno, addr, data); | ||
597 | if (lw.retval != 0) | ||
598 | goto done; | ||
599 | } | ||
600 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
601 | done: | ||
602 | return lw.retval; | ||
603 | } | ||
604 | |||
605 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
606 | { | ||
607 | struct __line_walk_param *lw = data; | ||
608 | |||
609 | lw->retval = __die_walk_funclines(sp_die, true, lw->callback, lw->data); | ||
610 | if (lw->retval != 0) | ||
611 | return DWARF_CB_ABORT; | ||
612 | |||
613 | return DWARF_CB_OK; | ||
614 | } | ||
615 | |||
616 | /** | ||
617 | * die_walk_lines - Walk on lines inside given DIE | ||
618 | * @rt_die: a root DIE (CU, subprogram or inlined_subroutine) | ||
619 | * @callback: callback routine | ||
620 | * @data: user data | ||
621 | * | ||
622 | * Walk on all lines inside given @rt_die and call @callback on each line. | ||
623 | * If the @rt_die is a function, walk only on the lines inside the function, | ||
624 | * otherwise @rt_die must be a CU DIE. | ||
625 | * Note that this walks not only dwarf line list, but also function entries | ||
626 | * and inline call-site. | ||
627 | */ | ||
628 | int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, void *data) | ||
629 | { | ||
630 | Dwarf_Lines *lines; | ||
631 | Dwarf_Line *line; | ||
632 | Dwarf_Addr addr; | ||
633 | const char *fname; | ||
634 | int lineno, ret = 0; | ||
635 | Dwarf_Die die_mem, *cu_die; | ||
636 | size_t nlines, i; | ||
637 | |||
638 | /* Get the CU die */ | ||
639 | if (dwarf_tag(rt_die) != DW_TAG_compile_unit) | ||
640 | cu_die = dwarf_diecu(rt_die, &die_mem, NULL, NULL); | ||
641 | else | ||
642 | cu_die = rt_die; | ||
643 | if (!cu_die) { | ||
644 | pr_debug2("Failed to get CU from given DIE.\n"); | ||
645 | return -EINVAL; | ||
646 | } | ||
647 | |||
648 | /* Get lines list in the CU */ | ||
649 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
650 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
651 | return -ENOENT; | ||
652 | } | ||
653 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
654 | |||
655 | /* Walk on the lines on lines list */ | ||
656 | for (i = 0; i < nlines; i++) { | ||
657 | line = dwarf_onesrcline(lines, i); | ||
658 | if (line == NULL || | ||
659 | dwarf_lineno(line, &lineno) != 0 || | ||
660 | dwarf_lineaddr(line, &addr) != 0) { | ||
661 | pr_debug2("Failed to get line info. " | ||
662 | "Possible error in debuginfo.\n"); | ||
663 | continue; | ||
664 | } | ||
665 | /* Filter lines based on address */ | ||
666 | if (rt_die != cu_die) | ||
667 | /* | ||
668 | * Address filtering | ||
669 | * The line is included in given function, and | ||
670 | * no inline block includes it. | ||
671 | */ | ||
672 | if (!dwarf_haspc(rt_die, addr) || | ||
673 | die_find_inlinefunc(rt_die, addr, &die_mem)) | ||
674 | continue; | ||
675 | /* Get source line */ | ||
676 | fname = dwarf_linesrc(line, NULL, NULL); | ||
677 | |||
678 | ret = callback(fname, lineno, addr, data); | ||
679 | if (ret != 0) | ||
680 | return ret; | ||
681 | } | ||
682 | |||
683 | /* | ||
684 | * Dwarf lines doesn't include function declarations and inlined | ||
685 | * subroutines. We have to check functions list or given function. | ||
686 | */ | ||
687 | if (rt_die != cu_die) | ||
688 | /* | ||
689 | * Don't need walk functions recursively, because nested | ||
690 | * inlined functions don't have lines of the specified DIE. | ||
691 | */ | ||
692 | ret = __die_walk_funclines(rt_die, false, callback, data); | ||
693 | else { | ||
694 | struct __line_walk_param param = { | ||
695 | .callback = callback, | ||
696 | .data = data, | ||
697 | .retval = 0, | ||
698 | }; | ||
699 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
700 | ret = param.retval; | ||
701 | } | ||
702 | |||
703 | return ret; | ||
704 | } | ||
705 | |||
706 | struct __find_variable_param { | ||
707 | const char *name; | ||
708 | Dwarf_Addr addr; | ||
709 | }; | ||
710 | |||
711 | static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) | ||
712 | { | ||
713 | struct __find_variable_param *fvp = data; | ||
714 | int tag; | ||
715 | |||
716 | tag = dwarf_tag(die_mem); | ||
717 | if ((tag == DW_TAG_formal_parameter || | ||
718 | tag == DW_TAG_variable) && | ||
719 | die_compare_name(die_mem, fvp->name)) | ||
720 | return DIE_FIND_CB_END; | ||
721 | |||
722 | if (dwarf_haspc(die_mem, fvp->addr)) | ||
723 | return DIE_FIND_CB_CONTINUE; | ||
724 | else | ||
725 | return DIE_FIND_CB_SIBLING; | ||
726 | } | ||
727 | |||
728 | /** | ||
729 | * die_find_variable_at - Find a given name variable at given address | ||
730 | * @sp_die: a function DIE | ||
731 | * @name: variable name | ||
732 | * @addr: address | ||
733 | * @die_mem: a buffer for result DIE | ||
734 | * | ||
735 | * Find a variable DIE called @name at @addr in @sp_die. | ||
736 | */ | ||
737 | Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | ||
738 | Dwarf_Addr addr, Dwarf_Die *die_mem) | ||
739 | { | ||
740 | struct __find_variable_param fvp = { .name = name, .addr = addr}; | ||
741 | |||
742 | return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp, | ||
743 | die_mem); | ||
744 | } | ||
745 | |||
746 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) | ||
747 | { | ||
748 | const char *name = data; | ||
749 | |||
750 | if ((dwarf_tag(die_mem) == DW_TAG_member) && | ||
751 | die_compare_name(die_mem, name)) | ||
752 | return DIE_FIND_CB_END; | ||
753 | |||
754 | return DIE_FIND_CB_SIBLING; | ||
755 | } | ||
756 | |||
757 | /** | ||
758 | * die_find_member - Find a given name member in a data structure | ||
759 | * @st_die: a data structure type DIE | ||
760 | * @name: member name | ||
761 | * @die_mem: a buffer for result DIE | ||
762 | * | ||
763 | * Find a member DIE called @name in @st_die. | ||
764 | */ | ||
765 | Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
766 | Dwarf_Die *die_mem) | ||
767 | { | ||
768 | return die_find_child(st_die, __die_find_member_cb, (void *)name, | ||
769 | die_mem); | ||
770 | } | ||
771 | |||
772 | /** | ||
773 | * die_get_typename - Get the name of given variable DIE | ||
774 | * @vr_die: a variable DIE | ||
775 | * @buf: a buffer for result type name | ||
776 | * @len: a max-length of @buf | ||
777 | * | ||
778 | * Get the name of @vr_die and stores it to @buf. Return the actual length | ||
779 | * of type name if succeeded. Return -E2BIG if @len is not enough long, and | ||
780 | * Return -ENOENT if failed to find type name. | ||
781 | * Note that the result will stores typedef name if possible, and stores | ||
782 | * "*(function_type)" if the type is a function pointer. | ||
783 | */ | ||
784 | int die_get_typename(Dwarf_Die *vr_die, char *buf, int len) | ||
785 | { | ||
786 | Dwarf_Die type; | ||
787 | int tag, ret, ret2; | ||
788 | const char *tmp = ""; | ||
789 | |||
790 | if (__die_get_real_type(vr_die, &type) == NULL) | ||
791 | return -ENOENT; | ||
792 | |||
793 | tag = dwarf_tag(&type); | ||
794 | if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type) | ||
795 | tmp = "*"; | ||
796 | else if (tag == DW_TAG_subroutine_type) { | ||
797 | /* Function pointer */ | ||
798 | ret = snprintf(buf, len, "(function_type)"); | ||
799 | return (ret >= len) ? -E2BIG : ret; | ||
800 | } else { | ||
801 | if (!dwarf_diename(&type)) | ||
802 | return -ENOENT; | ||
803 | if (tag == DW_TAG_union_type) | ||
804 | tmp = "union "; | ||
805 | else if (tag == DW_TAG_structure_type) | ||
806 | tmp = "struct "; | ||
807 | /* Write a base name */ | ||
808 | ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type)); | ||
809 | return (ret >= len) ? -E2BIG : ret; | ||
810 | } | ||
811 | ret = die_get_typename(&type, buf, len); | ||
812 | if (ret > 0) { | ||
813 | ret2 = snprintf(buf + ret, len - ret, "%s", tmp); | ||
814 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
815 | } | ||
816 | return ret; | ||
817 | } | ||
818 | |||
819 | /** | ||
820 | * die_get_varname - Get the name and type of given variable DIE | ||
821 | * @vr_die: a variable DIE | ||
822 | * @buf: a buffer for type and variable name | ||
823 | * @len: the max-length of @buf | ||
824 | * | ||
825 | * Get the name and type of @vr_die and stores it in @buf as "type\tname". | ||
826 | */ | ||
827 | int die_get_varname(Dwarf_Die *vr_die, char *buf, int len) | ||
828 | { | ||
829 | int ret, ret2; | ||
830 | |||
831 | ret = die_get_typename(vr_die, buf, len); | ||
832 | if (ret < 0) { | ||
833 | pr_debug("Failed to get type, make it unknown.\n"); | ||
834 | ret = snprintf(buf, len, "(unknown_type)"); | ||
835 | } | ||
836 | if (ret > 0) { | ||
837 | ret2 = snprintf(buf + ret, len - ret, "\t%s", | ||
838 | dwarf_diename(vr_die)); | ||
839 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
840 | } | ||
841 | return ret; | ||
842 | } | ||
843 | |||
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h new file mode 100644 index 000000000000..6ce1717784b7 --- /dev/null +++ b/tools/perf/util/dwarf-aux.h | |||
@@ -0,0 +1,111 @@ | |||
1 | #ifndef _DWARF_AUX_H | ||
2 | #define _DWARF_AUX_H | ||
3 | /* | ||
4 | * dwarf-aux.h : libdw auxiliary interfaces | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <dwarf.h> | ||
23 | #include <elfutils/libdw.h> | ||
24 | #include <elfutils/libdwfl.h> | ||
25 | #include <elfutils/version.h> | ||
26 | |||
27 | /* Find the realpath of the target file */ | ||
28 | extern const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname); | ||
29 | |||
30 | /* Get DW_AT_comp_dir (should be NULL with older gcc) */ | ||
31 | extern const char *cu_get_comp_dir(Dwarf_Die *cu_die); | ||
32 | |||
33 | /* Get a line number and file name for given address */ | ||
34 | extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | ||
35 | const char **fname, int *lineno); | ||
36 | |||
37 | /* Walk on funcitons at given address */ | ||
38 | extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
39 | int (*callback)(Dwarf_Die *, void *), void *data); | ||
40 | |||
41 | /* Compare diename and tname */ | ||
42 | extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); | ||
43 | |||
44 | /* Get callsite line number of inline-function instance */ | ||
45 | extern int die_get_call_lineno(Dwarf_Die *in_die); | ||
46 | |||
47 | /* Get callsite file name of inlined function instance */ | ||
48 | extern const char *die_get_call_file(Dwarf_Die *in_die); | ||
49 | |||
50 | /* Get type die */ | ||
51 | extern Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); | ||
52 | |||
53 | /* Get a type die, but skip qualifiers and typedef */ | ||
54 | extern Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); | ||
55 | |||
56 | /* Check whether the DIE is signed or not */ | ||
57 | extern bool die_is_signed_type(Dwarf_Die *tp_die); | ||
58 | |||
59 | /* Get data_member_location offset */ | ||
60 | extern int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs); | ||
61 | |||
62 | /* Return values for die_find_child() callbacks */ | ||
63 | enum { | ||
64 | DIE_FIND_CB_END = 0, /* End of Search */ | ||
65 | DIE_FIND_CB_CHILD = 1, /* Search only children */ | ||
66 | DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ | ||
67 | DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ | ||
68 | }; | ||
69 | |||
70 | /* Search child DIEs */ | ||
71 | extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
72 | int (*callback)(Dwarf_Die *, void *), | ||
73 | void *data, Dwarf_Die *die_mem); | ||
74 | |||
75 | /* Search a non-inlined function including given address */ | ||
76 | extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
77 | Dwarf_Die *die_mem); | ||
78 | |||
79 | /* Search an inlined function including given address */ | ||
80 | extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
81 | Dwarf_Die *die_mem); | ||
82 | |||
83 | /* Walk on the instances of given DIE */ | ||
84 | extern int die_walk_instances(Dwarf_Die *in_die, | ||
85 | int (*callback)(Dwarf_Die *, void *), void *data); | ||
86 | |||
87 | /* Walker on lines (Note: line number will not be sorted) */ | ||
88 | typedef int (* line_walk_callback_t) (const char *fname, int lineno, | ||
89 | Dwarf_Addr addr, void *data); | ||
90 | |||
91 | /* | ||
92 | * Walk on lines inside given DIE. If the DIE is a subprogram, walk only on | ||
93 | * the lines inside the subprogram, otherwise the DIE must be a CU DIE. | ||
94 | */ | ||
95 | extern int die_walk_lines(Dwarf_Die *rt_die, line_walk_callback_t callback, | ||
96 | void *data); | ||
97 | |||
98 | /* Find a variable called 'name' at given address */ | ||
99 | extern Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | ||
100 | Dwarf_Addr addr, Dwarf_Die *die_mem); | ||
101 | |||
102 | /* Find a member called 'name' */ | ||
103 | extern Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
104 | Dwarf_Die *die_mem); | ||
105 | |||
106 | /* Get the name of given variable DIE */ | ||
107 | extern int die_get_typename(Dwarf_Die *vr_die, char *buf, int len); | ||
108 | |||
109 | /* Get the name and type of given variable DIE, stored as "type\tname" */ | ||
110 | extern int die_get_varname(Dwarf_Die *vr_die, char *buf, int len); | ||
111 | #endif | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 3c1b8a632101..437f8ca679a0 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -169,12 +169,17 @@ static int perf_event__synthesize_mmap_events(union perf_event *event, | |||
169 | continue; | 169 | continue; |
170 | pbf += n + 3; | 170 | pbf += n + 3; |
171 | if (*pbf == 'x') { /* vm_exec */ | 171 | if (*pbf == 'x') { /* vm_exec */ |
172 | char anonstr[] = "//anon\n"; | ||
172 | char *execname = strchr(bf, '/'); | 173 | char *execname = strchr(bf, '/'); |
173 | 174 | ||
174 | /* Catch VDSO */ | 175 | /* Catch VDSO */ |
175 | if (execname == NULL) | 176 | if (execname == NULL) |
176 | execname = strstr(bf, "[vdso]"); | 177 | execname = strstr(bf, "[vdso]"); |
177 | 178 | ||
179 | /* Catch anonymous mmaps */ | ||
180 | if ((execname == NULL) && !strstr(bf, "[")) | ||
181 | execname = anonstr; | ||
182 | |||
178 | if (execname == NULL) | 183 | if (execname == NULL) |
179 | continue; | 184 | continue; |
180 | 185 | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 1d7f66488a88..357a85b85248 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -186,6 +186,6 @@ const char *perf_event__name(unsigned int id); | |||
186 | 186 | ||
187 | int perf_event__parse_sample(const union perf_event *event, u64 type, | 187 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
188 | int sample_size, bool sample_id_all, | 188 | int sample_size, bool sample_id_all, |
189 | struct perf_sample *sample); | 189 | struct perf_sample *sample, bool swapped); |
190 | 190 | ||
191 | #endif /* __PERF_RECORD_H */ | 191 | #endif /* __PERF_RECORD_H */ |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index b021ea9265c3..fbb4b4ab9cc6 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -85,10 +85,45 @@ int perf_evlist__add_default(struct perf_evlist *evlist) | |||
85 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); | 85 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); |
86 | 86 | ||
87 | if (evsel == NULL) | 87 | if (evsel == NULL) |
88 | return -ENOMEM; | 88 | goto error; |
89 | |||
90 | /* use strdup() because free(evsel) assumes name is allocated */ | ||
91 | evsel->name = strdup("cycles"); | ||
92 | if (!evsel->name) | ||
93 | goto error_free; | ||
89 | 94 | ||
90 | perf_evlist__add(evlist, evsel); | 95 | perf_evlist__add(evlist, evsel); |
91 | return 0; | 96 | return 0; |
97 | error_free: | ||
98 | perf_evsel__delete(evsel); | ||
99 | error: | ||
100 | return -ENOMEM; | ||
101 | } | ||
102 | |||
103 | void perf_evlist__disable(struct perf_evlist *evlist) | ||
104 | { | ||
105 | int cpu, thread; | ||
106 | struct perf_evsel *pos; | ||
107 | |||
108 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | ||
109 | list_for_each_entry(pos, &evlist->entries, node) { | ||
110 | for (thread = 0; thread < evlist->threads->nr; thread++) | ||
111 | ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_DISABLE); | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | |||
116 | void perf_evlist__enable(struct perf_evlist *evlist) | ||
117 | { | ||
118 | int cpu, thread; | ||
119 | struct perf_evsel *pos; | ||
120 | |||
121 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | ||
122 | list_for_each_entry(pos, &evlist->entries, node) { | ||
123 | for (thread = 0; thread < evlist->threads->nr; thread++) | ||
124 | ioctl(FD(pos, cpu, thread), PERF_EVENT_IOC_ENABLE); | ||
125 | } | ||
126 | } | ||
92 | } | 127 | } |
93 | 128 | ||
94 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 129 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
@@ -498,3 +533,39 @@ bool perf_evlist__sample_id_all(const struct perf_evlist *evlist) | |||
498 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 533 | first = list_entry(evlist->entries.next, struct perf_evsel, node); |
499 | return first->attr.sample_id_all; | 534 | return first->attr.sample_id_all; |
500 | } | 535 | } |
536 | |||
537 | void perf_evlist__set_selected(struct perf_evlist *evlist, | ||
538 | struct perf_evsel *evsel) | ||
539 | { | ||
540 | evlist->selected = evsel; | ||
541 | } | ||
542 | |||
543 | int perf_evlist__open(struct perf_evlist *evlist, bool group) | ||
544 | { | ||
545 | struct perf_evsel *evsel, *first; | ||
546 | int err, ncpus, nthreads; | ||
547 | |||
548 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
549 | |||
550 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
551 | struct xyarray *group_fd = NULL; | ||
552 | |||
553 | if (group && evsel != first) | ||
554 | group_fd = first->fd; | ||
555 | |||
556 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads, | ||
557 | group, group_fd); | ||
558 | if (err < 0) | ||
559 | goto out_err; | ||
560 | } | ||
561 | |||
562 | return 0; | ||
563 | out_err: | ||
564 | ncpus = evlist->cpus ? evlist->cpus->nr : 1; | ||
565 | nthreads = evlist->threads ? evlist->threads->nr : 1; | ||
566 | |||
567 | list_for_each_entry_reverse(evsel, &evlist->entries, node) | ||
568 | perf_evsel__close(evsel, ncpus, nthreads); | ||
569 | |||
570 | return err; | ||
571 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index b2b862374f37..1779ffef7828 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -25,6 +25,7 @@ struct perf_evlist { | |||
25 | struct pollfd *pollfd; | 25 | struct pollfd *pollfd; |
26 | struct thread_map *threads; | 26 | struct thread_map *threads; |
27 | struct cpu_map *cpus; | 27 | struct cpu_map *cpus; |
28 | struct perf_evsel *selected; | ||
28 | }; | 29 | }; |
29 | 30 | ||
30 | struct perf_evsel; | 31 | struct perf_evsel; |
@@ -49,10 +50,18 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | |||
49 | 50 | ||
50 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | 51 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); |
51 | 52 | ||
53 | int perf_evlist__open(struct perf_evlist *evlist, bool group); | ||
54 | |||
52 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist); | 55 | int perf_evlist__alloc_mmap(struct perf_evlist *evlist); |
53 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); | 56 | int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite); |
54 | void perf_evlist__munmap(struct perf_evlist *evlist); | 57 | void perf_evlist__munmap(struct perf_evlist *evlist); |
55 | 58 | ||
59 | void perf_evlist__disable(struct perf_evlist *evlist); | ||
60 | void perf_evlist__enable(struct perf_evlist *evlist); | ||
61 | |||
62 | void perf_evlist__set_selected(struct perf_evlist *evlist, | ||
63 | struct perf_evsel *evsel); | ||
64 | |||
56 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | 65 | static inline void perf_evlist__set_maps(struct perf_evlist *evlist, |
57 | struct cpu_map *cpus, | 66 | struct cpu_map *cpus, |
58 | struct thread_map *threads) | 67 | struct thread_map *threads) |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 0239eb87b232..e42626422587 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -7,6 +7,8 @@ | |||
7 | * Released under the GPL v2. (and only v2, not any later version) | 7 | * Released under the GPL v2. (and only v2, not any later version) |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <byteswap.h> | ||
11 | #include "asm/bug.h" | ||
10 | #include "evsel.h" | 12 | #include "evsel.h" |
11 | #include "evlist.h" | 13 | #include "evlist.h" |
12 | #include "util.h" | 14 | #include "util.h" |
@@ -14,6 +16,7 @@ | |||
14 | #include "thread_map.h" | 16 | #include "thread_map.h" |
15 | 17 | ||
16 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 18 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
19 | #define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0)) | ||
17 | 20 | ||
18 | int __perf_evsel__sample_size(u64 sample_type) | 21 | int __perf_evsel__sample_size(u64 sample_type) |
19 | { | 22 | { |
@@ -37,6 +40,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
37 | evsel->idx = idx; | 40 | evsel->idx = idx; |
38 | evsel->attr = *attr; | 41 | evsel->attr = *attr; |
39 | INIT_LIST_HEAD(&evsel->node); | 42 | INIT_LIST_HEAD(&evsel->node); |
43 | hists__init(&evsel->hists); | ||
40 | } | 44 | } |
41 | 45 | ||
42 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 46 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
@@ -201,15 +205,16 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
201 | } | 205 | } |
202 | 206 | ||
203 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 207 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
204 | struct thread_map *threads, bool group) | 208 | struct thread_map *threads, bool group, |
209 | struct xyarray *group_fds) | ||
205 | { | 210 | { |
206 | int cpu, thread; | 211 | int cpu, thread; |
207 | unsigned long flags = 0; | 212 | unsigned long flags = 0; |
208 | int pid = -1; | 213 | int pid = -1, err; |
209 | 214 | ||
210 | if (evsel->fd == NULL && | 215 | if (evsel->fd == NULL && |
211 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 216 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) |
212 | return -1; | 217 | return -ENOMEM; |
213 | 218 | ||
214 | if (evsel->cgrp) { | 219 | if (evsel->cgrp) { |
215 | flags = PERF_FLAG_PID_CGROUP; | 220 | flags = PERF_FLAG_PID_CGROUP; |
@@ -217,7 +222,7 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
217 | } | 222 | } |
218 | 223 | ||
219 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 224 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
220 | int group_fd = -1; | 225 | int group_fd = group_fds ? GROUP_FD(group_fds, cpu) : -1; |
221 | 226 | ||
222 | for (thread = 0; thread < threads->nr; thread++) { | 227 | for (thread = 0; thread < threads->nr; thread++) { |
223 | 228 | ||
@@ -228,8 +233,10 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
228 | pid, | 233 | pid, |
229 | cpus->map[cpu], | 234 | cpus->map[cpu], |
230 | group_fd, flags); | 235 | group_fd, flags); |
231 | if (FD(evsel, cpu, thread) < 0) | 236 | if (FD(evsel, cpu, thread) < 0) { |
237 | err = -errno; | ||
232 | goto out_close; | 238 | goto out_close; |
239 | } | ||
233 | 240 | ||
234 | if (group && group_fd == -1) | 241 | if (group && group_fd == -1) |
235 | group_fd = FD(evsel, cpu, thread); | 242 | group_fd = FD(evsel, cpu, thread); |
@@ -246,7 +253,17 @@ out_close: | |||
246 | } | 253 | } |
247 | thread = threads->nr; | 254 | thread = threads->nr; |
248 | } while (--cpu >= 0); | 255 | } while (--cpu >= 0); |
249 | return -1; | 256 | return err; |
257 | } | ||
258 | |||
259 | void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
260 | { | ||
261 | if (evsel->fd == NULL) | ||
262 | return; | ||
263 | |||
264 | perf_evsel__close_fd(evsel, ncpus, nthreads); | ||
265 | perf_evsel__free_fd(evsel); | ||
266 | evsel->fd = NULL; | ||
250 | } | 267 | } |
251 | 268 | ||
252 | static struct { | 269 | static struct { |
@@ -266,7 +283,8 @@ static struct { | |||
266 | }; | 283 | }; |
267 | 284 | ||
268 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 285 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
269 | struct thread_map *threads, bool group) | 286 | struct thread_map *threads, bool group, |
287 | struct xyarray *group_fd) | ||
270 | { | 288 | { |
271 | if (cpus == NULL) { | 289 | if (cpus == NULL) { |
272 | /* Work around old compiler warnings about strict aliasing */ | 290 | /* Work around old compiler warnings about strict aliasing */ |
@@ -276,19 +294,23 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
276 | if (threads == NULL) | 294 | if (threads == NULL) |
277 | threads = &empty_thread_map.map; | 295 | threads = &empty_thread_map.map; |
278 | 296 | ||
279 | return __perf_evsel__open(evsel, cpus, threads, group); | 297 | return __perf_evsel__open(evsel, cpus, threads, group, group_fd); |
280 | } | 298 | } |
281 | 299 | ||
282 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | 300 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
283 | struct cpu_map *cpus, bool group) | 301 | struct cpu_map *cpus, bool group, |
302 | struct xyarray *group_fd) | ||
284 | { | 303 | { |
285 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group); | 304 | return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group, |
305 | group_fd); | ||
286 | } | 306 | } |
287 | 307 | ||
288 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | 308 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
289 | struct thread_map *threads, bool group) | 309 | struct thread_map *threads, bool group, |
310 | struct xyarray *group_fd) | ||
290 | { | 311 | { |
291 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group); | 312 | return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group, |
313 | group_fd); | ||
292 | } | 314 | } |
293 | 315 | ||
294 | static int perf_event__parse_id_sample(const union perf_event *event, u64 type, | 316 | static int perf_event__parse_id_sample(const union perf_event *event, u64 type, |
@@ -342,10 +364,20 @@ static bool sample_overlap(const union perf_event *event, | |||
342 | 364 | ||
343 | int perf_event__parse_sample(const union perf_event *event, u64 type, | 365 | int perf_event__parse_sample(const union perf_event *event, u64 type, |
344 | int sample_size, bool sample_id_all, | 366 | int sample_size, bool sample_id_all, |
345 | struct perf_sample *data) | 367 | struct perf_sample *data, bool swapped) |
346 | { | 368 | { |
347 | const u64 *array; | 369 | const u64 *array; |
348 | 370 | ||
371 | /* | ||
372 | * used for cross-endian analysis. See git commit 65014ab3 | ||
373 | * for why this goofiness is needed. | ||
374 | */ | ||
375 | union { | ||
376 | u64 val64; | ||
377 | u32 val32[2]; | ||
378 | } u; | ||
379 | |||
380 | |||
349 | data->cpu = data->pid = data->tid = -1; | 381 | data->cpu = data->pid = data->tid = -1; |
350 | data->stream_id = data->id = data->time = -1ULL; | 382 | data->stream_id = data->id = data->time = -1ULL; |
351 | 383 | ||
@@ -366,9 +398,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
366 | } | 398 | } |
367 | 399 | ||
368 | if (type & PERF_SAMPLE_TID) { | 400 | if (type & PERF_SAMPLE_TID) { |
369 | u32 *p = (u32 *)array; | 401 | u.val64 = *array; |
370 | data->pid = p[0]; | 402 | if (swapped) { |
371 | data->tid = p[1]; | 403 | /* undo swap of u64, then swap on individual u32s */ |
404 | u.val64 = bswap_64(u.val64); | ||
405 | u.val32[0] = bswap_32(u.val32[0]); | ||
406 | u.val32[1] = bswap_32(u.val32[1]); | ||
407 | } | ||
408 | |||
409 | data->pid = u.val32[0]; | ||
410 | data->tid = u.val32[1]; | ||
372 | array++; | 411 | array++; |
373 | } | 412 | } |
374 | 413 | ||
@@ -377,6 +416,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
377 | array++; | 416 | array++; |
378 | } | 417 | } |
379 | 418 | ||
419 | data->addr = 0; | ||
380 | if (type & PERF_SAMPLE_ADDR) { | 420 | if (type & PERF_SAMPLE_ADDR) { |
381 | data->addr = *array; | 421 | data->addr = *array; |
382 | array++; | 422 | array++; |
@@ -394,8 +434,15 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
394 | } | 434 | } |
395 | 435 | ||
396 | if (type & PERF_SAMPLE_CPU) { | 436 | if (type & PERF_SAMPLE_CPU) { |
397 | u32 *p = (u32 *)array; | 437 | |
398 | data->cpu = *p; | 438 | u.val64 = *array; |
439 | if (swapped) { | ||
440 | /* undo swap of u64, then swap on individual u32s */ | ||
441 | u.val64 = bswap_64(u.val64); | ||
442 | u.val32[0] = bswap_32(u.val32[0]); | ||
443 | } | ||
444 | |||
445 | data->cpu = u.val32[0]; | ||
399 | array++; | 446 | array++; |
400 | } | 447 | } |
401 | 448 | ||
@@ -422,18 +469,27 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
422 | } | 469 | } |
423 | 470 | ||
424 | if (type & PERF_SAMPLE_RAW) { | 471 | if (type & PERF_SAMPLE_RAW) { |
425 | u32 *p = (u32 *)array; | 472 | const u64 *pdata; |
473 | |||
474 | u.val64 = *array; | ||
475 | if (WARN_ONCE(swapped, | ||
476 | "Endianness of raw data not corrected!\n")) { | ||
477 | /* undo swap of u64, then swap on individual u32s */ | ||
478 | u.val64 = bswap_64(u.val64); | ||
479 | u.val32[0] = bswap_32(u.val32[0]); | ||
480 | u.val32[1] = bswap_32(u.val32[1]); | ||
481 | } | ||
426 | 482 | ||
427 | if (sample_overlap(event, array, sizeof(u32))) | 483 | if (sample_overlap(event, array, sizeof(u32))) |
428 | return -EFAULT; | 484 | return -EFAULT; |
429 | 485 | ||
430 | data->raw_size = *p; | 486 | data->raw_size = u.val32[0]; |
431 | p++; | 487 | pdata = (void *) array + sizeof(u32); |
432 | 488 | ||
433 | if (sample_overlap(event, p, data->raw_size)) | 489 | if (sample_overlap(event, pdata, data->raw_size)) |
434 | return -EFAULT; | 490 | return -EFAULT; |
435 | 491 | ||
436 | data->raw_data = p; | 492 | data->raw_data = (void *) pdata; |
437 | } | 493 | } |
438 | 494 | ||
439 | return 0; | 495 | return 0; |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 7e9366e4490b..b1d15e6f7ae3 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -61,6 +61,7 @@ struct perf_evsel { | |||
61 | off_t id_offset; | 61 | off_t id_offset; |
62 | }; | 62 | }; |
63 | struct cgroup_sel *cgrp; | 63 | struct cgroup_sel *cgrp; |
64 | bool supported; | ||
64 | }; | 65 | }; |
65 | 66 | ||
66 | struct cpu_map; | 67 | struct cpu_map; |
@@ -81,11 +82,15 @@ void perf_evsel__free_id(struct perf_evsel *evsel); | |||
81 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 82 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
82 | 83 | ||
83 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | 84 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
84 | struct cpu_map *cpus, bool group); | 85 | struct cpu_map *cpus, bool group, |
86 | struct xyarray *group_fds); | ||
85 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, | 87 | int perf_evsel__open_per_thread(struct perf_evsel *evsel, |
86 | struct thread_map *threads, bool group); | 88 | struct thread_map *threads, bool group, |
89 | struct xyarray *group_fds); | ||
87 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 90 | int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
88 | struct thread_map *threads, bool group); | 91 | struct thread_map *threads, bool group, |
92 | struct xyarray *group_fds); | ||
93 | void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
89 | 94 | ||
90 | #define perf_evsel__match(evsel, t, c) \ | 95 | #define perf_evsel__match(evsel, t, c) \ |
91 | (evsel->attr.type == PERF_TYPE_##t && \ | 96 | (evsel->attr.type == PERF_TYPE_##t && \ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index afb0849fe530..bcd05d05b4f0 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #define _FILE_OFFSET_BITS 64 | 1 | #define _FILE_OFFSET_BITS 64 |
2 | 2 | ||
3 | #include "util.h" | ||
3 | #include <sys/types.h> | 4 | #include <sys/types.h> |
4 | #include <byteswap.h> | 5 | #include <byteswap.h> |
5 | #include <unistd.h> | 6 | #include <unistd.h> |
@@ -7,22 +8,29 @@ | |||
7 | #include <stdlib.h> | 8 | #include <stdlib.h> |
8 | #include <linux/list.h> | 9 | #include <linux/list.h> |
9 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
11 | #include <sys/utsname.h> | ||
10 | 12 | ||
11 | #include "evlist.h" | 13 | #include "evlist.h" |
12 | #include "evsel.h" | 14 | #include "evsel.h" |
13 | #include "util.h" | ||
14 | #include "header.h" | 15 | #include "header.h" |
15 | #include "../perf.h" | 16 | #include "../perf.h" |
16 | #include "trace-event.h" | 17 | #include "trace-event.h" |
17 | #include "session.h" | 18 | #include "session.h" |
18 | #include "symbol.h" | 19 | #include "symbol.h" |
19 | #include "debug.h" | 20 | #include "debug.h" |
21 | #include "cpumap.h" | ||
20 | 22 | ||
21 | static bool no_buildid_cache = false; | 23 | static bool no_buildid_cache = false; |
22 | 24 | ||
23 | static int event_count; | 25 | static int event_count; |
24 | static struct perf_trace_event_type *events; | 26 | static struct perf_trace_event_type *events; |
25 | 27 | ||
28 | static u32 header_argc; | ||
29 | static const char **header_argv; | ||
30 | |||
31 | static int dsos__write_buildid_table(struct perf_header *header, int fd); | ||
32 | static int perf_session__cache_build_ids(struct perf_session *session); | ||
33 | |||
26 | int perf_header__push_event(u64 id, const char *name) | 34 | int perf_header__push_event(u64 id, const char *name) |
27 | { | 35 | { |
28 | if (strlen(name) > MAX_EVENT_NAME) | 36 | if (strlen(name) > MAX_EVENT_NAME) |
@@ -110,6 +118,1020 @@ static int write_padded(int fd, const void *bf, size_t count, | |||
110 | return err; | 118 | return err; |
111 | } | 119 | } |
112 | 120 | ||
121 | static int do_write_string(int fd, const char *str) | ||
122 | { | ||
123 | u32 len, olen; | ||
124 | int ret; | ||
125 | |||
126 | olen = strlen(str) + 1; | ||
127 | len = ALIGN(olen, NAME_ALIGN); | ||
128 | |||
129 | /* write len, incl. \0 */ | ||
130 | ret = do_write(fd, &len, sizeof(len)); | ||
131 | if (ret < 0) | ||
132 | return ret; | ||
133 | |||
134 | return write_padded(fd, str, olen, len); | ||
135 | } | ||
136 | |||
137 | static char *do_read_string(int fd, struct perf_header *ph) | ||
138 | { | ||
139 | ssize_t sz, ret; | ||
140 | u32 len; | ||
141 | char *buf; | ||
142 | |||
143 | sz = read(fd, &len, sizeof(len)); | ||
144 | if (sz < (ssize_t)sizeof(len)) | ||
145 | return NULL; | ||
146 | |||
147 | if (ph->needs_swap) | ||
148 | len = bswap_32(len); | ||
149 | |||
150 | buf = malloc(len); | ||
151 | if (!buf) | ||
152 | return NULL; | ||
153 | |||
154 | ret = read(fd, buf, len); | ||
155 | if (ret == (ssize_t)len) { | ||
156 | /* | ||
157 | * strings are padded by zeroes | ||
158 | * thus the actual strlen of buf | ||
159 | * may be less than len | ||
160 | */ | ||
161 | return buf; | ||
162 | } | ||
163 | |||
164 | free(buf); | ||
165 | return NULL; | ||
166 | } | ||
167 | |||
168 | int | ||
169 | perf_header__set_cmdline(int argc, const char **argv) | ||
170 | { | ||
171 | int i; | ||
172 | |||
173 | header_argc = (u32)argc; | ||
174 | |||
175 | /* do not include NULL termination */ | ||
176 | header_argv = calloc(argc, sizeof(char *)); | ||
177 | if (!header_argv) | ||
178 | return -ENOMEM; | ||
179 | |||
180 | /* | ||
181 | * must copy argv contents because it gets moved | ||
182 | * around during option parsing | ||
183 | */ | ||
184 | for (i = 0; i < argc ; i++) | ||
185 | header_argv[i] = argv[i]; | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int write_trace_info(int fd, struct perf_header *h __used, | ||
191 | struct perf_evlist *evlist) | ||
192 | { | ||
193 | return read_tracing_data(fd, &evlist->entries); | ||
194 | } | ||
195 | |||
196 | |||
197 | static int write_build_id(int fd, struct perf_header *h, | ||
198 | struct perf_evlist *evlist __used) | ||
199 | { | ||
200 | struct perf_session *session; | ||
201 | int err; | ||
202 | |||
203 | session = container_of(h, struct perf_session, header); | ||
204 | |||
205 | err = dsos__write_buildid_table(h, fd); | ||
206 | if (err < 0) { | ||
207 | pr_debug("failed to write buildid table\n"); | ||
208 | return err; | ||
209 | } | ||
210 | if (!no_buildid_cache) | ||
211 | perf_session__cache_build_ids(session); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int write_hostname(int fd, struct perf_header *h __used, | ||
217 | struct perf_evlist *evlist __used) | ||
218 | { | ||
219 | struct utsname uts; | ||
220 | int ret; | ||
221 | |||
222 | ret = uname(&uts); | ||
223 | if (ret < 0) | ||
224 | return -1; | ||
225 | |||
226 | return do_write_string(fd, uts.nodename); | ||
227 | } | ||
228 | |||
229 | static int write_osrelease(int fd, struct perf_header *h __used, | ||
230 | struct perf_evlist *evlist __used) | ||
231 | { | ||
232 | struct utsname uts; | ||
233 | int ret; | ||
234 | |||
235 | ret = uname(&uts); | ||
236 | if (ret < 0) | ||
237 | return -1; | ||
238 | |||
239 | return do_write_string(fd, uts.release); | ||
240 | } | ||
241 | |||
242 | static int write_arch(int fd, struct perf_header *h __used, | ||
243 | struct perf_evlist *evlist __used) | ||
244 | { | ||
245 | struct utsname uts; | ||
246 | int ret; | ||
247 | |||
248 | ret = uname(&uts); | ||
249 | if (ret < 0) | ||
250 | return -1; | ||
251 | |||
252 | return do_write_string(fd, uts.machine); | ||
253 | } | ||
254 | |||
255 | static int write_version(int fd, struct perf_header *h __used, | ||
256 | struct perf_evlist *evlist __used) | ||
257 | { | ||
258 | return do_write_string(fd, perf_version_string); | ||
259 | } | ||
260 | |||
261 | static int write_cpudesc(int fd, struct perf_header *h __used, | ||
262 | struct perf_evlist *evlist __used) | ||
263 | { | ||
264 | #ifndef CPUINFO_PROC | ||
265 | #define CPUINFO_PROC NULL | ||
266 | #endif | ||
267 | FILE *file; | ||
268 | char *buf = NULL; | ||
269 | char *s, *p; | ||
270 | const char *search = CPUINFO_PROC; | ||
271 | size_t len = 0; | ||
272 | int ret = -1; | ||
273 | |||
274 | if (!search) | ||
275 | return -1; | ||
276 | |||
277 | file = fopen("/proc/cpuinfo", "r"); | ||
278 | if (!file) | ||
279 | return -1; | ||
280 | |||
281 | while (getline(&buf, &len, file) > 0) { | ||
282 | ret = strncmp(buf, search, strlen(search)); | ||
283 | if (!ret) | ||
284 | break; | ||
285 | } | ||
286 | |||
287 | if (ret) | ||
288 | goto done; | ||
289 | |||
290 | s = buf; | ||
291 | |||
292 | p = strchr(buf, ':'); | ||
293 | if (p && *(p+1) == ' ' && *(p+2)) | ||
294 | s = p + 2; | ||
295 | p = strchr(s, '\n'); | ||
296 | if (p) | ||
297 | *p = '\0'; | ||
298 | |||
299 | /* squash extra space characters (branding string) */ | ||
300 | p = s; | ||
301 | while (*p) { | ||
302 | if (isspace(*p)) { | ||
303 | char *r = p + 1; | ||
304 | char *q = r; | ||
305 | *p = ' '; | ||
306 | while (*q && isspace(*q)) | ||
307 | q++; | ||
308 | if (q != (p+1)) | ||
309 | while ((*r++ = *q++)); | ||
310 | } | ||
311 | p++; | ||
312 | } | ||
313 | ret = do_write_string(fd, s); | ||
314 | done: | ||
315 | free(buf); | ||
316 | fclose(file); | ||
317 | return ret; | ||
318 | } | ||
319 | |||
320 | static int write_nrcpus(int fd, struct perf_header *h __used, | ||
321 | struct perf_evlist *evlist __used) | ||
322 | { | ||
323 | long nr; | ||
324 | u32 nrc, nra; | ||
325 | int ret; | ||
326 | |||
327 | nr = sysconf(_SC_NPROCESSORS_CONF); | ||
328 | if (nr < 0) | ||
329 | return -1; | ||
330 | |||
331 | nrc = (u32)(nr & UINT_MAX); | ||
332 | |||
333 | nr = sysconf(_SC_NPROCESSORS_ONLN); | ||
334 | if (nr < 0) | ||
335 | return -1; | ||
336 | |||
337 | nra = (u32)(nr & UINT_MAX); | ||
338 | |||
339 | ret = do_write(fd, &nrc, sizeof(nrc)); | ||
340 | if (ret < 0) | ||
341 | return ret; | ||
342 | |||
343 | return do_write(fd, &nra, sizeof(nra)); | ||
344 | } | ||
345 | |||
346 | static int write_event_desc(int fd, struct perf_header *h __used, | ||
347 | struct perf_evlist *evlist) | ||
348 | { | ||
349 | struct perf_evsel *attr; | ||
350 | u32 nre = 0, nri, sz; | ||
351 | int ret; | ||
352 | |||
353 | list_for_each_entry(attr, &evlist->entries, node) | ||
354 | nre++; | ||
355 | |||
356 | /* | ||
357 | * write number of events | ||
358 | */ | ||
359 | ret = do_write(fd, &nre, sizeof(nre)); | ||
360 | if (ret < 0) | ||
361 | return ret; | ||
362 | |||
363 | /* | ||
364 | * size of perf_event_attr struct | ||
365 | */ | ||
366 | sz = (u32)sizeof(attr->attr); | ||
367 | ret = do_write(fd, &sz, sizeof(sz)); | ||
368 | if (ret < 0) | ||
369 | return ret; | ||
370 | |||
371 | list_for_each_entry(attr, &evlist->entries, node) { | ||
372 | |||
373 | ret = do_write(fd, &attr->attr, sz); | ||
374 | if (ret < 0) | ||
375 | return ret; | ||
376 | /* | ||
377 | * write number of unique id per event | ||
378 | * there is one id per instance of an event | ||
379 | * | ||
380 | * copy into an nri to be independent of the | ||
381 | * type of ids, | ||
382 | */ | ||
383 | nri = attr->ids; | ||
384 | ret = do_write(fd, &nri, sizeof(nri)); | ||
385 | if (ret < 0) | ||
386 | return ret; | ||
387 | |||
388 | /* | ||
389 | * write event string as passed on cmdline | ||
390 | */ | ||
391 | ret = do_write_string(fd, attr->name); | ||
392 | if (ret < 0) | ||
393 | return ret; | ||
394 | /* | ||
395 | * write unique ids for this event | ||
396 | */ | ||
397 | ret = do_write(fd, attr->id, attr->ids * sizeof(u64)); | ||
398 | if (ret < 0) | ||
399 | return ret; | ||
400 | } | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int write_cmdline(int fd, struct perf_header *h __used, | ||
405 | struct perf_evlist *evlist __used) | ||
406 | { | ||
407 | char buf[MAXPATHLEN]; | ||
408 | char proc[32]; | ||
409 | u32 i, n; | ||
410 | int ret; | ||
411 | |||
412 | /* | ||
413 | * actual atual path to perf binary | ||
414 | */ | ||
415 | sprintf(proc, "/proc/%d/exe", getpid()); | ||
416 | ret = readlink(proc, buf, sizeof(buf)); | ||
417 | if (ret <= 0) | ||
418 | return -1; | ||
419 | |||
420 | /* readlink() does not add null termination */ | ||
421 | buf[ret] = '\0'; | ||
422 | |||
423 | /* account for binary path */ | ||
424 | n = header_argc + 1; | ||
425 | |||
426 | ret = do_write(fd, &n, sizeof(n)); | ||
427 | if (ret < 0) | ||
428 | return ret; | ||
429 | |||
430 | ret = do_write_string(fd, buf); | ||
431 | if (ret < 0) | ||
432 | return ret; | ||
433 | |||
434 | for (i = 0 ; i < header_argc; i++) { | ||
435 | ret = do_write_string(fd, header_argv[i]); | ||
436 | if (ret < 0) | ||
437 | return ret; | ||
438 | } | ||
439 | return 0; | ||
440 | } | ||
441 | |||
442 | #define CORE_SIB_FMT \ | ||
443 | "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list" | ||
444 | #define THRD_SIB_FMT \ | ||
445 | "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list" | ||
446 | |||
447 | struct cpu_topo { | ||
448 | u32 core_sib; | ||
449 | u32 thread_sib; | ||
450 | char **core_siblings; | ||
451 | char **thread_siblings; | ||
452 | }; | ||
453 | |||
454 | static int build_cpu_topo(struct cpu_topo *tp, int cpu) | ||
455 | { | ||
456 | FILE *fp; | ||
457 | char filename[MAXPATHLEN]; | ||
458 | char *buf = NULL, *p; | ||
459 | size_t len = 0; | ||
460 | u32 i = 0; | ||
461 | int ret = -1; | ||
462 | |||
463 | sprintf(filename, CORE_SIB_FMT, cpu); | ||
464 | fp = fopen(filename, "r"); | ||
465 | if (!fp) | ||
466 | return -1; | ||
467 | |||
468 | if (getline(&buf, &len, fp) <= 0) | ||
469 | goto done; | ||
470 | |||
471 | fclose(fp); | ||
472 | |||
473 | p = strchr(buf, '\n'); | ||
474 | if (p) | ||
475 | *p = '\0'; | ||
476 | |||
477 | for (i = 0; i < tp->core_sib; i++) { | ||
478 | if (!strcmp(buf, tp->core_siblings[i])) | ||
479 | break; | ||
480 | } | ||
481 | if (i == tp->core_sib) { | ||
482 | tp->core_siblings[i] = buf; | ||
483 | tp->core_sib++; | ||
484 | buf = NULL; | ||
485 | len = 0; | ||
486 | } | ||
487 | |||
488 | sprintf(filename, THRD_SIB_FMT, cpu); | ||
489 | fp = fopen(filename, "r"); | ||
490 | if (!fp) | ||
491 | goto done; | ||
492 | |||
493 | if (getline(&buf, &len, fp) <= 0) | ||
494 | goto done; | ||
495 | |||
496 | p = strchr(buf, '\n'); | ||
497 | if (p) | ||
498 | *p = '\0'; | ||
499 | |||
500 | for (i = 0; i < tp->thread_sib; i++) { | ||
501 | if (!strcmp(buf, tp->thread_siblings[i])) | ||
502 | break; | ||
503 | } | ||
504 | if (i == tp->thread_sib) { | ||
505 | tp->thread_siblings[i] = buf; | ||
506 | tp->thread_sib++; | ||
507 | buf = NULL; | ||
508 | } | ||
509 | ret = 0; | ||
510 | done: | ||
511 | if(fp) | ||
512 | fclose(fp); | ||
513 | free(buf); | ||
514 | return ret; | ||
515 | } | ||
516 | |||
517 | static void free_cpu_topo(struct cpu_topo *tp) | ||
518 | { | ||
519 | u32 i; | ||
520 | |||
521 | if (!tp) | ||
522 | return; | ||
523 | |||
524 | for (i = 0 ; i < tp->core_sib; i++) | ||
525 | free(tp->core_siblings[i]); | ||
526 | |||
527 | for (i = 0 ; i < tp->thread_sib; i++) | ||
528 | free(tp->thread_siblings[i]); | ||
529 | |||
530 | free(tp); | ||
531 | } | ||
532 | |||
533 | static struct cpu_topo *build_cpu_topology(void) | ||
534 | { | ||
535 | struct cpu_topo *tp; | ||
536 | void *addr; | ||
537 | u32 nr, i; | ||
538 | size_t sz; | ||
539 | long ncpus; | ||
540 | int ret = -1; | ||
541 | |||
542 | ncpus = sysconf(_SC_NPROCESSORS_CONF); | ||
543 | if (ncpus < 0) | ||
544 | return NULL; | ||
545 | |||
546 | nr = (u32)(ncpus & UINT_MAX); | ||
547 | |||
548 | sz = nr * sizeof(char *); | ||
549 | |||
550 | addr = calloc(1, sizeof(*tp) + 2 * sz); | ||
551 | if (!addr) | ||
552 | return NULL; | ||
553 | |||
554 | tp = addr; | ||
555 | |||
556 | addr += sizeof(*tp); | ||
557 | tp->core_siblings = addr; | ||
558 | addr += sz; | ||
559 | tp->thread_siblings = addr; | ||
560 | |||
561 | for (i = 0; i < nr; i++) { | ||
562 | ret = build_cpu_topo(tp, i); | ||
563 | if (ret < 0) | ||
564 | break; | ||
565 | } | ||
566 | if (ret) { | ||
567 | free_cpu_topo(tp); | ||
568 | tp = NULL; | ||
569 | } | ||
570 | return tp; | ||
571 | } | ||
572 | |||
573 | static int write_cpu_topology(int fd, struct perf_header *h __used, | ||
574 | struct perf_evlist *evlist __used) | ||
575 | { | ||
576 | struct cpu_topo *tp; | ||
577 | u32 i; | ||
578 | int ret; | ||
579 | |||
580 | tp = build_cpu_topology(); | ||
581 | if (!tp) | ||
582 | return -1; | ||
583 | |||
584 | ret = do_write(fd, &tp->core_sib, sizeof(tp->core_sib)); | ||
585 | if (ret < 0) | ||
586 | goto done; | ||
587 | |||
588 | for (i = 0; i < tp->core_sib; i++) { | ||
589 | ret = do_write_string(fd, tp->core_siblings[i]); | ||
590 | if (ret < 0) | ||
591 | goto done; | ||
592 | } | ||
593 | ret = do_write(fd, &tp->thread_sib, sizeof(tp->thread_sib)); | ||
594 | if (ret < 0) | ||
595 | goto done; | ||
596 | |||
597 | for (i = 0; i < tp->thread_sib; i++) { | ||
598 | ret = do_write_string(fd, tp->thread_siblings[i]); | ||
599 | if (ret < 0) | ||
600 | break; | ||
601 | } | ||
602 | done: | ||
603 | free_cpu_topo(tp); | ||
604 | return ret; | ||
605 | } | ||
606 | |||
607 | |||
608 | |||
609 | static int write_total_mem(int fd, struct perf_header *h __used, | ||
610 | struct perf_evlist *evlist __used) | ||
611 | { | ||
612 | char *buf = NULL; | ||
613 | FILE *fp; | ||
614 | size_t len = 0; | ||
615 | int ret = -1, n; | ||
616 | uint64_t mem; | ||
617 | |||
618 | fp = fopen("/proc/meminfo", "r"); | ||
619 | if (!fp) | ||
620 | return -1; | ||
621 | |||
622 | while (getline(&buf, &len, fp) > 0) { | ||
623 | ret = strncmp(buf, "MemTotal:", 9); | ||
624 | if (!ret) | ||
625 | break; | ||
626 | } | ||
627 | if (!ret) { | ||
628 | n = sscanf(buf, "%*s %"PRIu64, &mem); | ||
629 | if (n == 1) | ||
630 | ret = do_write(fd, &mem, sizeof(mem)); | ||
631 | } | ||
632 | free(buf); | ||
633 | fclose(fp); | ||
634 | return ret; | ||
635 | } | ||
636 | |||
637 | static int write_topo_node(int fd, int node) | ||
638 | { | ||
639 | char str[MAXPATHLEN]; | ||
640 | char field[32]; | ||
641 | char *buf = NULL, *p; | ||
642 | size_t len = 0; | ||
643 | FILE *fp; | ||
644 | u64 mem_total, mem_free, mem; | ||
645 | int ret = -1; | ||
646 | |||
647 | sprintf(str, "/sys/devices/system/node/node%d/meminfo", node); | ||
648 | fp = fopen(str, "r"); | ||
649 | if (!fp) | ||
650 | return -1; | ||
651 | |||
652 | while (getline(&buf, &len, fp) > 0) { | ||
653 | /* skip over invalid lines */ | ||
654 | if (!strchr(buf, ':')) | ||
655 | continue; | ||
656 | if (sscanf(buf, "%*s %*d %s %"PRIu64, field, &mem) != 2) | ||
657 | goto done; | ||
658 | if (!strcmp(field, "MemTotal:")) | ||
659 | mem_total = mem; | ||
660 | if (!strcmp(field, "MemFree:")) | ||
661 | mem_free = mem; | ||
662 | } | ||
663 | |||
664 | fclose(fp); | ||
665 | |||
666 | ret = do_write(fd, &mem_total, sizeof(u64)); | ||
667 | if (ret) | ||
668 | goto done; | ||
669 | |||
670 | ret = do_write(fd, &mem_free, sizeof(u64)); | ||
671 | if (ret) | ||
672 | goto done; | ||
673 | |||
674 | ret = -1; | ||
675 | sprintf(str, "/sys/devices/system/node/node%d/cpulist", node); | ||
676 | |||
677 | fp = fopen(str, "r"); | ||
678 | if (!fp) | ||
679 | goto done; | ||
680 | |||
681 | if (getline(&buf, &len, fp) <= 0) | ||
682 | goto done; | ||
683 | |||
684 | p = strchr(buf, '\n'); | ||
685 | if (p) | ||
686 | *p = '\0'; | ||
687 | |||
688 | ret = do_write_string(fd, buf); | ||
689 | done: | ||
690 | free(buf); | ||
691 | fclose(fp); | ||
692 | return ret; | ||
693 | } | ||
694 | |||
695 | static int write_numa_topology(int fd, struct perf_header *h __used, | ||
696 | struct perf_evlist *evlist __used) | ||
697 | { | ||
698 | char *buf = NULL; | ||
699 | size_t len = 0; | ||
700 | FILE *fp; | ||
701 | struct cpu_map *node_map = NULL; | ||
702 | char *c; | ||
703 | u32 nr, i, j; | ||
704 | int ret = -1; | ||
705 | |||
706 | fp = fopen("/sys/devices/system/node/online", "r"); | ||
707 | if (!fp) | ||
708 | return -1; | ||
709 | |||
710 | if (getline(&buf, &len, fp) <= 0) | ||
711 | goto done; | ||
712 | |||
713 | c = strchr(buf, '\n'); | ||
714 | if (c) | ||
715 | *c = '\0'; | ||
716 | |||
717 | node_map = cpu_map__new(buf); | ||
718 | if (!node_map) | ||
719 | goto done; | ||
720 | |||
721 | nr = (u32)node_map->nr; | ||
722 | |||
723 | ret = do_write(fd, &nr, sizeof(nr)); | ||
724 | if (ret < 0) | ||
725 | goto done; | ||
726 | |||
727 | for (i = 0; i < nr; i++) { | ||
728 | j = (u32)node_map->map[i]; | ||
729 | ret = do_write(fd, &j, sizeof(j)); | ||
730 | if (ret < 0) | ||
731 | break; | ||
732 | |||
733 | ret = write_topo_node(fd, i); | ||
734 | if (ret < 0) | ||
735 | break; | ||
736 | } | ||
737 | done: | ||
738 | free(buf); | ||
739 | fclose(fp); | ||
740 | free(node_map); | ||
741 | return ret; | ||
742 | } | ||
743 | |||
744 | /* | ||
745 | * default get_cpuid(): nothing gets recorded | ||
746 | * actual implementation must be in arch/$(ARCH)/util/header.c | ||
747 | */ | ||
748 | int __attribute__((weak)) get_cpuid(char *buffer __used, size_t sz __used) | ||
749 | { | ||
750 | return -1; | ||
751 | } | ||
752 | |||
753 | static int write_cpuid(int fd, struct perf_header *h __used, | ||
754 | struct perf_evlist *evlist __used) | ||
755 | { | ||
756 | char buffer[64]; | ||
757 | int ret; | ||
758 | |||
759 | ret = get_cpuid(buffer, sizeof(buffer)); | ||
760 | if (!ret) | ||
761 | goto write_it; | ||
762 | |||
763 | return -1; | ||
764 | write_it: | ||
765 | return do_write_string(fd, buffer); | ||
766 | } | ||
767 | |||
768 | static void print_hostname(struct perf_header *ph, int fd, FILE *fp) | ||
769 | { | ||
770 | char *str = do_read_string(fd, ph); | ||
771 | fprintf(fp, "# hostname : %s\n", str); | ||
772 | free(str); | ||
773 | } | ||
774 | |||
775 | static void print_osrelease(struct perf_header *ph, int fd, FILE *fp) | ||
776 | { | ||
777 | char *str = do_read_string(fd, ph); | ||
778 | fprintf(fp, "# os release : %s\n", str); | ||
779 | free(str); | ||
780 | } | ||
781 | |||
782 | static void print_arch(struct perf_header *ph, int fd, FILE *fp) | ||
783 | { | ||
784 | char *str = do_read_string(fd, ph); | ||
785 | fprintf(fp, "# arch : %s\n", str); | ||
786 | free(str); | ||
787 | } | ||
788 | |||
789 | static void print_cpudesc(struct perf_header *ph, int fd, FILE *fp) | ||
790 | { | ||
791 | char *str = do_read_string(fd, ph); | ||
792 | fprintf(fp, "# cpudesc : %s\n", str); | ||
793 | free(str); | ||
794 | } | ||
795 | |||
796 | static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp) | ||
797 | { | ||
798 | ssize_t ret; | ||
799 | u32 nr; | ||
800 | |||
801 | ret = read(fd, &nr, sizeof(nr)); | ||
802 | if (ret != (ssize_t)sizeof(nr)) | ||
803 | nr = -1; /* interpreted as error */ | ||
804 | |||
805 | if (ph->needs_swap) | ||
806 | nr = bswap_32(nr); | ||
807 | |||
808 | fprintf(fp, "# nrcpus online : %u\n", nr); | ||
809 | |||
810 | ret = read(fd, &nr, sizeof(nr)); | ||
811 | if (ret != (ssize_t)sizeof(nr)) | ||
812 | nr = -1; /* interpreted as error */ | ||
813 | |||
814 | if (ph->needs_swap) | ||
815 | nr = bswap_32(nr); | ||
816 | |||
817 | fprintf(fp, "# nrcpus avail : %u\n", nr); | ||
818 | } | ||
819 | |||
820 | static void print_version(struct perf_header *ph, int fd, FILE *fp) | ||
821 | { | ||
822 | char *str = do_read_string(fd, ph); | ||
823 | fprintf(fp, "# perf version : %s\n", str); | ||
824 | free(str); | ||
825 | } | ||
826 | |||
827 | static void print_cmdline(struct perf_header *ph, int fd, FILE *fp) | ||
828 | { | ||
829 | ssize_t ret; | ||
830 | char *str; | ||
831 | u32 nr, i; | ||
832 | |||
833 | ret = read(fd, &nr, sizeof(nr)); | ||
834 | if (ret != (ssize_t)sizeof(nr)) | ||
835 | return; | ||
836 | |||
837 | if (ph->needs_swap) | ||
838 | nr = bswap_32(nr); | ||
839 | |||
840 | fprintf(fp, "# cmdline : "); | ||
841 | |||
842 | for (i = 0; i < nr; i++) { | ||
843 | str = do_read_string(fd, ph); | ||
844 | fprintf(fp, "%s ", str); | ||
845 | free(str); | ||
846 | } | ||
847 | fputc('\n', fp); | ||
848 | } | ||
849 | |||
850 | static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp) | ||
851 | { | ||
852 | ssize_t ret; | ||
853 | u32 nr, i; | ||
854 | char *str; | ||
855 | |||
856 | ret = read(fd, &nr, sizeof(nr)); | ||
857 | if (ret != (ssize_t)sizeof(nr)) | ||
858 | return; | ||
859 | |||
860 | if (ph->needs_swap) | ||
861 | nr = bswap_32(nr); | ||
862 | |||
863 | for (i = 0; i < nr; i++) { | ||
864 | str = do_read_string(fd, ph); | ||
865 | fprintf(fp, "# sibling cores : %s\n", str); | ||
866 | free(str); | ||
867 | } | ||
868 | |||
869 | ret = read(fd, &nr, sizeof(nr)); | ||
870 | if (ret != (ssize_t)sizeof(nr)) | ||
871 | return; | ||
872 | |||
873 | if (ph->needs_swap) | ||
874 | nr = bswap_32(nr); | ||
875 | |||
876 | for (i = 0; i < nr; i++) { | ||
877 | str = do_read_string(fd, ph); | ||
878 | fprintf(fp, "# sibling threads : %s\n", str); | ||
879 | free(str); | ||
880 | } | ||
881 | } | ||
882 | |||
883 | static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | ||
884 | { | ||
885 | struct perf_event_attr attr; | ||
886 | uint64_t id; | ||
887 | void *buf = NULL; | ||
888 | char *str; | ||
889 | u32 nre, sz, nr, i, j, msz; | ||
890 | int ret; | ||
891 | |||
892 | /* number of events */ | ||
893 | ret = read(fd, &nre, sizeof(nre)); | ||
894 | if (ret != (ssize_t)sizeof(nre)) | ||
895 | goto error; | ||
896 | |||
897 | if (ph->needs_swap) | ||
898 | nre = bswap_32(nre); | ||
899 | |||
900 | ret = read(fd, &sz, sizeof(sz)); | ||
901 | if (ret != (ssize_t)sizeof(sz)) | ||
902 | goto error; | ||
903 | |||
904 | if (ph->needs_swap) | ||
905 | sz = bswap_32(sz); | ||
906 | |||
907 | /* | ||
908 | * ensure it is at least to our ABI rev | ||
909 | */ | ||
910 | if (sz < (u32)sizeof(attr)) | ||
911 | goto error; | ||
912 | |||
913 | memset(&attr, 0, sizeof(attr)); | ||
914 | |||
915 | /* read entire region to sync up to next field */ | ||
916 | buf = malloc(sz); | ||
917 | if (!buf) | ||
918 | goto error; | ||
919 | |||
920 | msz = sizeof(attr); | ||
921 | if (sz < msz) | ||
922 | msz = sz; | ||
923 | |||
924 | for (i = 0 ; i < nre; i++) { | ||
925 | |||
926 | ret = read(fd, buf, sz); | ||
927 | if (ret != (ssize_t)sz) | ||
928 | goto error; | ||
929 | |||
930 | if (ph->needs_swap) | ||
931 | perf_event__attr_swap(buf); | ||
932 | |||
933 | memcpy(&attr, buf, msz); | ||
934 | |||
935 | ret = read(fd, &nr, sizeof(nr)); | ||
936 | if (ret != (ssize_t)sizeof(nr)) | ||
937 | goto error; | ||
938 | |||
939 | if (ph->needs_swap) | ||
940 | nr = bswap_32(nr); | ||
941 | |||
942 | str = do_read_string(fd, ph); | ||
943 | fprintf(fp, "# event : name = %s, ", str); | ||
944 | free(str); | ||
945 | |||
946 | fprintf(fp, "type = %d, config = 0x%"PRIx64 | ||
947 | ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64, | ||
948 | attr.type, | ||
949 | (u64)attr.config, | ||
950 | (u64)attr.config1, | ||
951 | (u64)attr.config2); | ||
952 | |||
953 | fprintf(fp, ", excl_usr = %d, excl_kern = %d", | ||
954 | attr.exclude_user, | ||
955 | attr.exclude_kernel); | ||
956 | |||
957 | if (nr) | ||
958 | fprintf(fp, ", id = {"); | ||
959 | |||
960 | for (j = 0 ; j < nr; j++) { | ||
961 | ret = read(fd, &id, sizeof(id)); | ||
962 | if (ret != (ssize_t)sizeof(id)) | ||
963 | goto error; | ||
964 | |||
965 | if (ph->needs_swap) | ||
966 | id = bswap_64(id); | ||
967 | |||
968 | if (j) | ||
969 | fputc(',', fp); | ||
970 | |||
971 | fprintf(fp, " %"PRIu64, id); | ||
972 | } | ||
973 | if (nr && j == nr) | ||
974 | fprintf(fp, " }"); | ||
975 | fputc('\n', fp); | ||
976 | } | ||
977 | free(buf); | ||
978 | return; | ||
979 | error: | ||
980 | fprintf(fp, "# event desc: not available or unable to read\n"); | ||
981 | } | ||
982 | |||
983 | static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp) | ||
984 | { | ||
985 | uint64_t mem; | ||
986 | ssize_t ret; | ||
987 | |||
988 | ret = read(fd, &mem, sizeof(mem)); | ||
989 | if (ret != sizeof(mem)) | ||
990 | goto error; | ||
991 | |||
992 | if (h->needs_swap) | ||
993 | mem = bswap_64(mem); | ||
994 | |||
995 | fprintf(fp, "# total memory : %"PRIu64" kB\n", mem); | ||
996 | return; | ||
997 | error: | ||
998 | fprintf(fp, "# total memory : unknown\n"); | ||
999 | } | ||
1000 | |||
1001 | static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp) | ||
1002 | { | ||
1003 | ssize_t ret; | ||
1004 | u32 nr, c, i; | ||
1005 | char *str; | ||
1006 | uint64_t mem_total, mem_free; | ||
1007 | |||
1008 | /* nr nodes */ | ||
1009 | ret = read(fd, &nr, sizeof(nr)); | ||
1010 | if (ret != (ssize_t)sizeof(nr)) | ||
1011 | goto error; | ||
1012 | |||
1013 | if (h->needs_swap) | ||
1014 | nr = bswap_32(nr); | ||
1015 | |||
1016 | for (i = 0; i < nr; i++) { | ||
1017 | |||
1018 | /* node number */ | ||
1019 | ret = read(fd, &c, sizeof(c)); | ||
1020 | if (ret != (ssize_t)sizeof(c)) | ||
1021 | goto error; | ||
1022 | |||
1023 | if (h->needs_swap) | ||
1024 | c = bswap_32(c); | ||
1025 | |||
1026 | ret = read(fd, &mem_total, sizeof(u64)); | ||
1027 | if (ret != sizeof(u64)) | ||
1028 | goto error; | ||
1029 | |||
1030 | ret = read(fd, &mem_free, sizeof(u64)); | ||
1031 | if (ret != sizeof(u64)) | ||
1032 | goto error; | ||
1033 | |||
1034 | if (h->needs_swap) { | ||
1035 | mem_total = bswap_64(mem_total); | ||
1036 | mem_free = bswap_64(mem_free); | ||
1037 | } | ||
1038 | |||
1039 | fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB," | ||
1040 | " free = %"PRIu64" kB\n", | ||
1041 | c, | ||
1042 | mem_total, | ||
1043 | mem_free); | ||
1044 | |||
1045 | str = do_read_string(fd, h); | ||
1046 | fprintf(fp, "# node%u cpu list : %s\n", c, str); | ||
1047 | free(str); | ||
1048 | } | ||
1049 | return; | ||
1050 | error: | ||
1051 | fprintf(fp, "# numa topology : not available\n"); | ||
1052 | } | ||
1053 | |||
1054 | static void print_cpuid(struct perf_header *ph, int fd, FILE *fp) | ||
1055 | { | ||
1056 | char *str = do_read_string(fd, ph); | ||
1057 | fprintf(fp, "# cpuid : %s\n", str); | ||
1058 | free(str); | ||
1059 | } | ||
1060 | |||
1061 | struct feature_ops { | ||
1062 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | ||
1063 | void (*print)(struct perf_header *h, int fd, FILE *fp); | ||
1064 | const char *name; | ||
1065 | bool full_only; | ||
1066 | }; | ||
1067 | |||
1068 | #define FEAT_OPA(n, w, p) \ | ||
1069 | [n] = { .name = #n, .write = w, .print = p } | ||
1070 | #define FEAT_OPF(n, w, p) \ | ||
1071 | [n] = { .name = #n, .write = w, .print = p, .full_only = true } | ||
1072 | |||
1073 | static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | ||
1074 | FEAT_OPA(HEADER_TRACE_INFO, write_trace_info, NULL), | ||
1075 | FEAT_OPA(HEADER_BUILD_ID, write_build_id, NULL), | ||
1076 | FEAT_OPA(HEADER_HOSTNAME, write_hostname, print_hostname), | ||
1077 | FEAT_OPA(HEADER_OSRELEASE, write_osrelease, print_osrelease), | ||
1078 | FEAT_OPA(HEADER_VERSION, write_version, print_version), | ||
1079 | FEAT_OPA(HEADER_ARCH, write_arch, print_arch), | ||
1080 | FEAT_OPA(HEADER_NRCPUS, write_nrcpus, print_nrcpus), | ||
1081 | FEAT_OPA(HEADER_CPUDESC, write_cpudesc, print_cpudesc), | ||
1082 | FEAT_OPA(HEADER_CPUID, write_cpuid, print_cpuid), | ||
1083 | FEAT_OPA(HEADER_TOTAL_MEM, write_total_mem, print_total_mem), | ||
1084 | FEAT_OPA(HEADER_EVENT_DESC, write_event_desc, print_event_desc), | ||
1085 | FEAT_OPA(HEADER_CMDLINE, write_cmdline, print_cmdline), | ||
1086 | FEAT_OPF(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology), | ||
1087 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology), | ||
1088 | }; | ||
1089 | |||
1090 | struct header_print_data { | ||
1091 | FILE *fp; | ||
1092 | bool full; /* extended list of headers */ | ||
1093 | }; | ||
1094 | |||
1095 | static int perf_file_section__fprintf_info(struct perf_file_section *section, | ||
1096 | struct perf_header *ph, | ||
1097 | int feat, int fd, void *data) | ||
1098 | { | ||
1099 | struct header_print_data *hd = data; | ||
1100 | |||
1101 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { | ||
1102 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | ||
1103 | "%d, continuing...\n", section->offset, feat); | ||
1104 | return 0; | ||
1105 | } | ||
1106 | if (feat < HEADER_TRACE_INFO || feat >= HEADER_LAST_FEATURE) { | ||
1107 | pr_warning("unknown feature %d\n", feat); | ||
1108 | return -1; | ||
1109 | } | ||
1110 | if (!feat_ops[feat].print) | ||
1111 | return 0; | ||
1112 | |||
1113 | if (!feat_ops[feat].full_only || hd->full) | ||
1114 | feat_ops[feat].print(ph, fd, hd->fp); | ||
1115 | else | ||
1116 | fprintf(hd->fp, "# %s info available, use -I to display\n", | ||
1117 | feat_ops[feat].name); | ||
1118 | |||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1122 | int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) | ||
1123 | { | ||
1124 | struct header_print_data hd; | ||
1125 | struct perf_header *header = &session->header; | ||
1126 | int fd = session->fd; | ||
1127 | hd.fp = fp; | ||
1128 | hd.full = full; | ||
1129 | |||
1130 | perf_header__process_sections(header, fd, &hd, | ||
1131 | perf_file_section__fprintf_info); | ||
1132 | return 0; | ||
1133 | } | ||
1134 | |||
113 | #define dsos__for_each_with_build_id(pos, head) \ | 1135 | #define dsos__for_each_with_build_id(pos, head) \ |
114 | list_for_each_entry(pos, head, node) \ | 1136 | list_for_each_entry(pos, head, node) \ |
115 | if (!pos->has_build_id) \ | 1137 | if (!pos->has_build_id) \ |
@@ -189,8 +1211,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
189 | const char *name, bool is_kallsyms) | 1211 | const char *name, bool is_kallsyms) |
190 | { | 1212 | { |
191 | const size_t size = PATH_MAX; | 1213 | const size_t size = PATH_MAX; |
192 | char *realname, *filename = malloc(size), | 1214 | char *realname, *filename = zalloc(size), |
193 | *linkname = malloc(size), *targetname; | 1215 | *linkname = zalloc(size), *targetname; |
194 | int len, err = -1; | 1216 | int len, err = -1; |
195 | 1217 | ||
196 | if (is_kallsyms) { | 1218 | if (is_kallsyms) { |
@@ -254,8 +1276,8 @@ static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size, | |||
254 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | 1276 | int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) |
255 | { | 1277 | { |
256 | const size_t size = PATH_MAX; | 1278 | const size_t size = PATH_MAX; |
257 | char *filename = malloc(size), | 1279 | char *filename = zalloc(size), |
258 | *linkname = malloc(size); | 1280 | *linkname = zalloc(size); |
259 | int err = -1; | 1281 | int err = -1; |
260 | 1282 | ||
261 | if (filename == NULL || linkname == NULL) | 1283 | if (filename == NULL || linkname == NULL) |
@@ -267,7 +1289,7 @@ int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir) | |||
267 | if (access(linkname, F_OK)) | 1289 | if (access(linkname, F_OK)) |
268 | goto out_free; | 1290 | goto out_free; |
269 | 1291 | ||
270 | if (readlink(linkname, filename, size) < 0) | 1292 | if (readlink(linkname, filename, size - 1) < 0) |
271 | goto out_free; | 1293 | goto out_free; |
272 | 1294 | ||
273 | if (unlink(linkname)) | 1295 | if (unlink(linkname)) |
@@ -356,15 +1378,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with | |||
356 | return ret; | 1378 | return ret; |
357 | } | 1379 | } |
358 | 1380 | ||
1381 | static int do_write_feat(int fd, struct perf_header *h, int type, | ||
1382 | struct perf_file_section **p, | ||
1383 | struct perf_evlist *evlist) | ||
1384 | { | ||
1385 | int err; | ||
1386 | int ret = 0; | ||
1387 | |||
1388 | if (perf_header__has_feat(h, type)) { | ||
1389 | |||
1390 | (*p)->offset = lseek(fd, 0, SEEK_CUR); | ||
1391 | |||
1392 | err = feat_ops[type].write(fd, h, evlist); | ||
1393 | if (err < 0) { | ||
1394 | pr_debug("failed to write feature %d\n", type); | ||
1395 | |||
1396 | /* undo anything written */ | ||
1397 | lseek(fd, (*p)->offset, SEEK_SET); | ||
1398 | |||
1399 | return -1; | ||
1400 | } | ||
1401 | (*p)->size = lseek(fd, 0, SEEK_CUR) - (*p)->offset; | ||
1402 | (*p)++; | ||
1403 | } | ||
1404 | return ret; | ||
1405 | } | ||
1406 | |||
359 | static int perf_header__adds_write(struct perf_header *header, | 1407 | static int perf_header__adds_write(struct perf_header *header, |
360 | struct perf_evlist *evlist, int fd) | 1408 | struct perf_evlist *evlist, int fd) |
361 | { | 1409 | { |
362 | int nr_sections; | 1410 | int nr_sections; |
363 | struct perf_session *session; | 1411 | struct perf_session *session; |
364 | struct perf_file_section *feat_sec; | 1412 | struct perf_file_section *feat_sec, *p; |
365 | int sec_size; | 1413 | int sec_size; |
366 | u64 sec_start; | 1414 | u64 sec_start; |
367 | int idx = 0, err; | 1415 | int err; |
368 | 1416 | ||
369 | session = container_of(header, struct perf_session, header); | 1417 | session = container_of(header, struct perf_session, header); |
370 | 1418 | ||
@@ -376,7 +1424,7 @@ static int perf_header__adds_write(struct perf_header *header, | |||
376 | if (!nr_sections) | 1424 | if (!nr_sections) |
377 | return 0; | 1425 | return 0; |
378 | 1426 | ||
379 | feat_sec = calloc(sizeof(*feat_sec), nr_sections); | 1427 | feat_sec = p = calloc(sizeof(*feat_sec), nr_sections); |
380 | if (feat_sec == NULL) | 1428 | if (feat_sec == NULL) |
381 | return -ENOMEM; | 1429 | return -ENOMEM; |
382 | 1430 | ||
@@ -385,36 +1433,69 @@ static int perf_header__adds_write(struct perf_header *header, | |||
385 | sec_start = header->data_offset + header->data_size; | 1433 | sec_start = header->data_offset + header->data_size; |
386 | lseek(fd, sec_start + sec_size, SEEK_SET); | 1434 | lseek(fd, sec_start + sec_size, SEEK_SET); |
387 | 1435 | ||
388 | if (perf_header__has_feat(header, HEADER_TRACE_INFO)) { | 1436 | err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist); |
389 | struct perf_file_section *trace_sec; | 1437 | if (err) |
390 | 1438 | goto out_free; | |
391 | trace_sec = &feat_sec[idx++]; | ||
392 | 1439 | ||
393 | /* Write trace info */ | 1440 | err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist); |
394 | trace_sec->offset = lseek(fd, 0, SEEK_CUR); | 1441 | if (err) { |
395 | read_tracing_data(fd, &evlist->entries); | 1442 | perf_header__clear_feat(header, HEADER_BUILD_ID); |
396 | trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; | 1443 | goto out_free; |
397 | } | 1444 | } |
398 | 1445 | ||
399 | if (perf_header__has_feat(header, HEADER_BUILD_ID)) { | 1446 | err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist); |
400 | struct perf_file_section *buildid_sec; | 1447 | if (err) |
1448 | perf_header__clear_feat(header, HEADER_HOSTNAME); | ||
401 | 1449 | ||
402 | buildid_sec = &feat_sec[idx++]; | 1450 | err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist); |
1451 | if (err) | ||
1452 | perf_header__clear_feat(header, HEADER_OSRELEASE); | ||
403 | 1453 | ||
404 | /* Write build-ids */ | 1454 | err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist); |
405 | buildid_sec->offset = lseek(fd, 0, SEEK_CUR); | 1455 | if (err) |
406 | err = dsos__write_buildid_table(header, fd); | 1456 | perf_header__clear_feat(header, HEADER_VERSION); |
407 | if (err < 0) { | 1457 | |
408 | pr_debug("failed to write buildid table\n"); | 1458 | err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist); |
409 | goto out_free; | 1459 | if (err) |
410 | } | 1460 | perf_header__clear_feat(header, HEADER_ARCH); |
411 | buildid_sec->size = lseek(fd, 0, SEEK_CUR) - | 1461 | |
412 | buildid_sec->offset; | 1462 | err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist); |
413 | if (!no_buildid_cache) | 1463 | if (err) |
414 | perf_session__cache_build_ids(session); | 1464 | perf_header__clear_feat(header, HEADER_NRCPUS); |
415 | } | 1465 | |
1466 | err = do_write_feat(fd, header, HEADER_CPUDESC, &p, evlist); | ||
1467 | if (err) | ||
1468 | perf_header__clear_feat(header, HEADER_CPUDESC); | ||
1469 | |||
1470 | err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist); | ||
1471 | if (err) | ||
1472 | perf_header__clear_feat(header, HEADER_CPUID); | ||
1473 | |||
1474 | err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist); | ||
1475 | if (err) | ||
1476 | perf_header__clear_feat(header, HEADER_TOTAL_MEM); | ||
1477 | |||
1478 | err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist); | ||
1479 | if (err) | ||
1480 | perf_header__clear_feat(header, HEADER_CMDLINE); | ||
1481 | |||
1482 | err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist); | ||
1483 | if (err) | ||
1484 | perf_header__clear_feat(header, HEADER_EVENT_DESC); | ||
1485 | |||
1486 | err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist); | ||
1487 | if (err) | ||
1488 | perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY); | ||
1489 | |||
1490 | err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist); | ||
1491 | if (err) | ||
1492 | perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY); | ||
416 | 1493 | ||
417 | lseek(fd, sec_start, SEEK_SET); | 1494 | lseek(fd, sec_start, SEEK_SET); |
1495 | /* | ||
1496 | * may write more than needed due to dropped feature, but | ||
1497 | * this is okay, reader will skip the mising entries | ||
1498 | */ | ||
418 | err = do_write(fd, feat_sec, sec_size); | 1499 | err = do_write(fd, feat_sec, sec_size); |
419 | if (err < 0) | 1500 | if (err < 0) |
420 | pr_debug("failed to write feature section\n"); | 1501 | pr_debug("failed to write feature section\n"); |
@@ -554,9 +1635,10 @@ static int perf_header__getbuffer64(struct perf_header *header, | |||
554 | } | 1635 | } |
555 | 1636 | ||
556 | int perf_header__process_sections(struct perf_header *header, int fd, | 1637 | int perf_header__process_sections(struct perf_header *header, int fd, |
1638 | void *data, | ||
557 | int (*process)(struct perf_file_section *section, | 1639 | int (*process)(struct perf_file_section *section, |
558 | struct perf_header *ph, | 1640 | struct perf_header *ph, |
559 | int feat, int fd)) | 1641 | int feat, int fd, void *data)) |
560 | { | 1642 | { |
561 | struct perf_file_section *feat_sec; | 1643 | struct perf_file_section *feat_sec; |
562 | int nr_sections; | 1644 | int nr_sections; |
@@ -584,7 +1666,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
584 | if (perf_header__has_feat(header, feat)) { | 1666 | if (perf_header__has_feat(header, feat)) { |
585 | struct perf_file_section *sec = &feat_sec[idx++]; | 1667 | struct perf_file_section *sec = &feat_sec[idx++]; |
586 | 1668 | ||
587 | err = process(sec, header, feat, fd); | 1669 | err = process(sec, header, feat, fd, data); |
588 | if (err < 0) | 1670 | if (err < 0) |
589 | break; | 1671 | break; |
590 | } | 1672 | } |
@@ -621,21 +1703,41 @@ int perf_file_header__read(struct perf_file_header *header, | |||
621 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); | 1703 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); |
622 | else | 1704 | else |
623 | return -1; | 1705 | return -1; |
1706 | } else if (ph->needs_swap) { | ||
1707 | unsigned int i; | ||
1708 | /* | ||
1709 | * feature bitmap is declared as an array of unsigned longs -- | ||
1710 | * not good since its size can differ between the host that | ||
1711 | * generated the data file and the host analyzing the file. | ||
1712 | * | ||
1713 | * We need to handle endianness, but we don't know the size of | ||
1714 | * the unsigned long where the file was generated. Take a best | ||
1715 | * guess at determining it: try 64-bit swap first (ie., file | ||
1716 | * created on a 64-bit host), and check if the hostname feature | ||
1717 | * bit is set (this feature bit is forced on as of fbe96f2). | ||
1718 | * If the bit is not, undo the 64-bit swap and try a 32-bit | ||
1719 | * swap. If the hostname bit is still not set (e.g., older data | ||
1720 | * file), punt and fallback to the original behavior -- | ||
1721 | * clearing all feature bits and setting buildid. | ||
1722 | */ | ||
1723 | for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) | ||
1724 | header->adds_features[i] = bswap_64(header->adds_features[i]); | ||
1725 | |||
1726 | if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { | ||
1727 | for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) { | ||
1728 | header->adds_features[i] = bswap_64(header->adds_features[i]); | ||
1729 | header->adds_features[i] = bswap_32(header->adds_features[i]); | ||
1730 | } | ||
1731 | } | ||
1732 | |||
1733 | if (!test_bit(HEADER_HOSTNAME, header->adds_features)) { | ||
1734 | bitmap_zero(header->adds_features, HEADER_FEAT_BITS); | ||
1735 | set_bit(HEADER_BUILD_ID, header->adds_features); | ||
1736 | } | ||
624 | } | 1737 | } |
625 | 1738 | ||
626 | memcpy(&ph->adds_features, &header->adds_features, | 1739 | memcpy(&ph->adds_features, &header->adds_features, |
627 | sizeof(ph->adds_features)); | 1740 | sizeof(ph->adds_features)); |
628 | /* | ||
629 | * FIXME: hack that assumes that if we need swap the perf.data file | ||
630 | * may be coming from an arch with a different word-size, ergo different | ||
631 | * DEFINE_BITMAP format, investigate more later, but for now its mostly | ||
632 | * safe to assume that we have a build-id section. Trace files probably | ||
633 | * have several other issues in this realm anyway... | ||
634 | */ | ||
635 | if (ph->needs_swap) { | ||
636 | memset(&ph->adds_features, 0, sizeof(ph->adds_features)); | ||
637 | perf_header__set_feat(ph, HEADER_BUILD_ID); | ||
638 | } | ||
639 | 1741 | ||
640 | ph->event_offset = header->event_types.offset; | 1742 | ph->event_offset = header->event_types.offset; |
641 | ph->event_size = header->event_types.size; | 1743 | ph->event_size = header->event_types.size; |
@@ -726,7 +1828,16 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, | |||
726 | return -1; | 1828 | return -1; |
727 | 1829 | ||
728 | bev.header = old_bev.header; | 1830 | bev.header = old_bev.header; |
729 | bev.pid = 0; | 1831 | |
1832 | /* | ||
1833 | * As the pid is the missing value, we need to fill | ||
1834 | * it properly. The header.misc value give us nice hint. | ||
1835 | */ | ||
1836 | bev.pid = HOST_KERNEL_ID; | ||
1837 | if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || | ||
1838 | bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) | ||
1839 | bev.pid = DEFAULT_GUEST_KERNEL_ID; | ||
1840 | |||
730 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); | 1841 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); |
731 | __event_process_build_id(&bev, filename, session); | 1842 | __event_process_build_id(&bev, filename, session); |
732 | 1843 | ||
@@ -787,7 +1898,7 @@ out: | |||
787 | 1898 | ||
788 | static int perf_file_section__process(struct perf_file_section *section, | 1899 | static int perf_file_section__process(struct perf_file_section *section, |
789 | struct perf_header *ph, | 1900 | struct perf_header *ph, |
790 | int feat, int fd) | 1901 | int feat, int fd, void *data __used) |
791 | { | 1902 | { |
792 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { | 1903 | if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { |
793 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " | 1904 | pr_debug("Failed to lseek to %" PRIu64 " offset for feature " |
@@ -804,6 +1915,21 @@ static int perf_file_section__process(struct perf_file_section *section, | |||
804 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) | 1915 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) |
805 | pr_debug("Failed to read buildids, continuing...\n"); | 1916 | pr_debug("Failed to read buildids, continuing...\n"); |
806 | break; | 1917 | break; |
1918 | |||
1919 | case HEADER_HOSTNAME: | ||
1920 | case HEADER_OSRELEASE: | ||
1921 | case HEADER_VERSION: | ||
1922 | case HEADER_ARCH: | ||
1923 | case HEADER_NRCPUS: | ||
1924 | case HEADER_CPUDESC: | ||
1925 | case HEADER_CPUID: | ||
1926 | case HEADER_TOTAL_MEM: | ||
1927 | case HEADER_CMDLINE: | ||
1928 | case HEADER_EVENT_DESC: | ||
1929 | case HEADER_CPU_TOPOLOGY: | ||
1930 | case HEADER_NUMA_TOPOLOGY: | ||
1931 | break; | ||
1932 | |||
807 | default: | 1933 | default: |
808 | pr_debug("unknown feature %d, continuing...\n", feat); | 1934 | pr_debug("unknown feature %d, continuing...\n", feat); |
809 | } | 1935 | } |
@@ -877,9 +2003,12 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
877 | struct perf_evsel *evsel; | 2003 | struct perf_evsel *evsel; |
878 | off_t tmp; | 2004 | off_t tmp; |
879 | 2005 | ||
880 | if (perf_header__getbuffer64(header, fd, &f_attr, sizeof(f_attr))) | 2006 | if (readn(fd, &f_attr, sizeof(f_attr)) <= 0) |
881 | goto out_errno; | 2007 | goto out_errno; |
882 | 2008 | ||
2009 | if (header->needs_swap) | ||
2010 | perf_event__attr_swap(&f_attr.attr); | ||
2011 | |||
883 | tmp = lseek(fd, 0, SEEK_CUR); | 2012 | tmp = lseek(fd, 0, SEEK_CUR); |
884 | evsel = perf_evsel__new(&f_attr.attr, i); | 2013 | evsel = perf_evsel__new(&f_attr.attr, i); |
885 | 2014 | ||
@@ -923,7 +2052,8 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
923 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | 2052 | event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); |
924 | } | 2053 | } |
925 | 2054 | ||
926 | perf_header__process_sections(header, fd, perf_file_section__process); | 2055 | perf_header__process_sections(header, fd, NULL, |
2056 | perf_file_section__process); | ||
927 | 2057 | ||
928 | lseek(fd, header->data_offset, SEEK_SET); | 2058 | lseek(fd, header->data_offset, SEEK_SET); |
929 | 2059 | ||
@@ -1088,15 +2218,29 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, | |||
1088 | struct perf_session *session __unused) | 2218 | struct perf_session *session __unused) |
1089 | { | 2219 | { |
1090 | union perf_event ev; | 2220 | union perf_event ev; |
2221 | struct tracing_data *tdata; | ||
1091 | ssize_t size = 0, aligned_size = 0, padding; | 2222 | ssize_t size = 0, aligned_size = 0, padding; |
1092 | int err __used = 0; | 2223 | int err __used = 0; |
1093 | 2224 | ||
2225 | /* | ||
2226 | * We are going to store the size of the data followed | ||
2227 | * by the data contents. Since the fd descriptor is a pipe, | ||
2228 | * we cannot seek back to store the size of the data once | ||
2229 | * we know it. Instead we: | ||
2230 | * | ||
2231 | * - write the tracing data to the temp file | ||
2232 | * - get/write the data size to pipe | ||
2233 | * - write the tracing data from the temp file | ||
2234 | * to the pipe | ||
2235 | */ | ||
2236 | tdata = tracing_data_get(&evlist->entries, fd, true); | ||
2237 | if (!tdata) | ||
2238 | return -1; | ||
2239 | |||
1094 | memset(&ev, 0, sizeof(ev)); | 2240 | memset(&ev, 0, sizeof(ev)); |
1095 | 2241 | ||
1096 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; | 2242 | ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; |
1097 | size = read_tracing_data_size(fd, &evlist->entries); | 2243 | size = tdata->size; |
1098 | if (size <= 0) | ||
1099 | return size; | ||
1100 | aligned_size = ALIGN(size, sizeof(u64)); | 2244 | aligned_size = ALIGN(size, sizeof(u64)); |
1101 | padding = aligned_size - size; | 2245 | padding = aligned_size - size; |
1102 | ev.tracing_data.header.size = sizeof(ev.tracing_data); | 2246 | ev.tracing_data.header.size = sizeof(ev.tracing_data); |
@@ -1104,7 +2248,12 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist, | |||
1104 | 2248 | ||
1105 | process(&ev, NULL, session); | 2249 | process(&ev, NULL, session); |
1106 | 2250 | ||
1107 | err = read_tracing_data(fd, &evlist->entries); | 2251 | /* |
2252 | * The put function will copy all the tracing data | ||
2253 | * stored in temp file to the pipe. | ||
2254 | */ | ||
2255 | tracing_data_put(tdata); | ||
2256 | |||
1108 | write_padded(fd, NULL, 0, padding); | 2257 | write_padded(fd, NULL, 0, padding); |
1109 | 2258 | ||
1110 | return aligned_size; | 2259 | return aligned_size; |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 1886256768a1..3d5a742f4a2a 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -12,6 +12,20 @@ | |||
12 | enum { | 12 | enum { |
13 | HEADER_TRACE_INFO = 1, | 13 | HEADER_TRACE_INFO = 1, |
14 | HEADER_BUILD_ID, | 14 | HEADER_BUILD_ID, |
15 | |||
16 | HEADER_HOSTNAME, | ||
17 | HEADER_OSRELEASE, | ||
18 | HEADER_VERSION, | ||
19 | HEADER_ARCH, | ||
20 | HEADER_NRCPUS, | ||
21 | HEADER_CPUDESC, | ||
22 | HEADER_CPUID, | ||
23 | HEADER_TOTAL_MEM, | ||
24 | HEADER_CMDLINE, | ||
25 | HEADER_EVENT_DESC, | ||
26 | HEADER_CPU_TOPOLOGY, | ||
27 | HEADER_NUMA_TOPOLOGY, | ||
28 | |||
15 | HEADER_LAST_FEATURE, | 29 | HEADER_LAST_FEATURE, |
16 | }; | 30 | }; |
17 | 31 | ||
@@ -68,10 +82,15 @@ void perf_header__set_feat(struct perf_header *header, int feat); | |||
68 | void perf_header__clear_feat(struct perf_header *header, int feat); | 82 | void perf_header__clear_feat(struct perf_header *header, int feat); |
69 | bool perf_header__has_feat(const struct perf_header *header, int feat); | 83 | bool perf_header__has_feat(const struct perf_header *header, int feat); |
70 | 84 | ||
85 | int perf_header__set_cmdline(int argc, const char **argv); | ||
86 | |||
71 | int perf_header__process_sections(struct perf_header *header, int fd, | 87 | int perf_header__process_sections(struct perf_header *header, int fd, |
88 | void *data, | ||
72 | int (*process)(struct perf_file_section *section, | 89 | int (*process)(struct perf_file_section *section, |
73 | struct perf_header *ph, | 90 | struct perf_header *ph, |
74 | int feat, int fd)); | 91 | int feat, int fd, void *data)); |
92 | |||
93 | int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); | ||
75 | 94 | ||
76 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | 95 | int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, |
77 | const char *name, bool is_kallsyms); | 96 | const char *name, bool is_kallsyms); |
@@ -104,4 +123,10 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc, | |||
104 | struct perf_session *session); | 123 | struct perf_session *session); |
105 | int perf_event__process_build_id(union perf_event *event, | 124 | int perf_event__process_build_id(union perf_event *event, |
106 | struct perf_session *session); | 125 | struct perf_session *session); |
126 | |||
127 | /* | ||
128 | * arch specific callback | ||
129 | */ | ||
130 | int get_cpuid(char *buffer, size_t sz); | ||
131 | |||
107 | #endif /* __PERF_HEADER_H */ | 132 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 627a02e03c57..a36a3fa81ffb 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -6,6 +6,11 @@ | |||
6 | #include "sort.h" | 6 | #include "sort.h" |
7 | #include <math.h> | 7 | #include <math.h> |
8 | 8 | ||
9 | static bool hists__filter_entry_by_dso(struct hists *hists, | ||
10 | struct hist_entry *he); | ||
11 | static bool hists__filter_entry_by_thread(struct hists *hists, | ||
12 | struct hist_entry *he); | ||
13 | |||
9 | enum hist_filter { | 14 | enum hist_filter { |
10 | HIST_FILTER__DSO, | 15 | HIST_FILTER__DSO, |
11 | HIST_FILTER__THREAD, | 16 | HIST_FILTER__THREAD, |
@@ -14,59 +19,60 @@ enum hist_filter { | |||
14 | 19 | ||
15 | struct callchain_param callchain_param = { | 20 | struct callchain_param callchain_param = { |
16 | .mode = CHAIN_GRAPH_REL, | 21 | .mode = CHAIN_GRAPH_REL, |
17 | .min_percent = 0.5 | 22 | .min_percent = 0.5, |
23 | .order = ORDER_CALLEE | ||
18 | }; | 24 | }; |
19 | 25 | ||
20 | u16 hists__col_len(struct hists *self, enum hist_column col) | 26 | u16 hists__col_len(struct hists *hists, enum hist_column col) |
21 | { | 27 | { |
22 | return self->col_len[col]; | 28 | return hists->col_len[col]; |
23 | } | 29 | } |
24 | 30 | ||
25 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) | 31 | void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len) |
26 | { | 32 | { |
27 | self->col_len[col] = len; | 33 | hists->col_len[col] = len; |
28 | } | 34 | } |
29 | 35 | ||
30 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) | 36 | bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len) |
31 | { | 37 | { |
32 | if (len > hists__col_len(self, col)) { | 38 | if (len > hists__col_len(hists, col)) { |
33 | hists__set_col_len(self, col, len); | 39 | hists__set_col_len(hists, col, len); |
34 | return true; | 40 | return true; |
35 | } | 41 | } |
36 | return false; | 42 | return false; |
37 | } | 43 | } |
38 | 44 | ||
39 | static void hists__reset_col_len(struct hists *self) | 45 | static void hists__reset_col_len(struct hists *hists) |
40 | { | 46 | { |
41 | enum hist_column col; | 47 | enum hist_column col; |
42 | 48 | ||
43 | for (col = 0; col < HISTC_NR_COLS; ++col) | 49 | for (col = 0; col < HISTC_NR_COLS; ++col) |
44 | hists__set_col_len(self, col, 0); | 50 | hists__set_col_len(hists, col, 0); |
45 | } | 51 | } |
46 | 52 | ||
47 | static void hists__calc_col_len(struct hists *self, struct hist_entry *h) | 53 | static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) |
48 | { | 54 | { |
49 | u16 len; | 55 | u16 len; |
50 | 56 | ||
51 | if (h->ms.sym) | 57 | if (h->ms.sym) |
52 | hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); | 58 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen); |
53 | else { | 59 | else { |
54 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 60 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; |
55 | 61 | ||
56 | if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && | 62 | if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width && |
57 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 63 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
58 | !symbol_conf.dso_list) | 64 | !symbol_conf.dso_list) |
59 | hists__set_col_len(self, HISTC_DSO, | 65 | hists__set_col_len(hists, HISTC_DSO, |
60 | unresolved_col_width); | 66 | unresolved_col_width); |
61 | } | 67 | } |
62 | 68 | ||
63 | len = thread__comm_len(h->thread); | 69 | len = thread__comm_len(h->thread); |
64 | if (hists__new_col_len(self, HISTC_COMM, len)) | 70 | if (hists__new_col_len(hists, HISTC_COMM, len)) |
65 | hists__set_col_len(self, HISTC_THREAD, len + 6); | 71 | hists__set_col_len(hists, HISTC_THREAD, len + 6); |
66 | 72 | ||
67 | if (h->ms.map) { | 73 | if (h->ms.map) { |
68 | len = dso__name_len(h->ms.map->dso); | 74 | len = dso__name_len(h->ms.map->dso); |
69 | hists__new_col_len(self, HISTC_DSO, len); | 75 | hists__new_col_len(hists, HISTC_DSO, len); |
70 | } | 76 | } |
71 | } | 77 | } |
72 | 78 | ||
@@ -91,6 +97,67 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self, | |||
91 | } | 97 | } |
92 | } | 98 | } |
93 | 99 | ||
100 | static void hist_entry__decay(struct hist_entry *he) | ||
101 | { | ||
102 | he->period = (he->period * 7) / 8; | ||
103 | he->nr_events = (he->nr_events * 7) / 8; | ||
104 | } | ||
105 | |||
106 | static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) | ||
107 | { | ||
108 | u64 prev_period = he->period; | ||
109 | |||
110 | if (prev_period == 0) | ||
111 | return true; | ||
112 | |||
113 | hist_entry__decay(he); | ||
114 | |||
115 | if (!he->filtered) | ||
116 | hists->stats.total_period -= prev_period - he->period; | ||
117 | |||
118 | return he->period == 0; | ||
119 | } | ||
120 | |||
121 | static void __hists__decay_entries(struct hists *hists, bool zap_user, | ||
122 | bool zap_kernel, bool threaded) | ||
123 | { | ||
124 | struct rb_node *next = rb_first(&hists->entries); | ||
125 | struct hist_entry *n; | ||
126 | |||
127 | while (next) { | ||
128 | n = rb_entry(next, struct hist_entry, rb_node); | ||
129 | next = rb_next(&n->rb_node); | ||
130 | /* | ||
131 | * We may be annotating this, for instance, so keep it here in | ||
132 | * case some it gets new samples, we'll eventually free it when | ||
133 | * the user stops browsing and it agains gets fully decayed. | ||
134 | */ | ||
135 | if (((zap_user && n->level == '.') || | ||
136 | (zap_kernel && n->level != '.') || | ||
137 | hists__decay_entry(hists, n)) && | ||
138 | !n->used) { | ||
139 | rb_erase(&n->rb_node, &hists->entries); | ||
140 | |||
141 | if (sort__need_collapse || threaded) | ||
142 | rb_erase(&n->rb_node_in, &hists->entries_collapsed); | ||
143 | |||
144 | hist_entry__free(n); | ||
145 | --hists->nr_entries; | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | |||
150 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel) | ||
151 | { | ||
152 | return __hists__decay_entries(hists, zap_user, zap_kernel, false); | ||
153 | } | ||
154 | |||
155 | void hists__decay_entries_threaded(struct hists *hists, | ||
156 | bool zap_user, bool zap_kernel) | ||
157 | { | ||
158 | return __hists__decay_entries(hists, zap_user, zap_kernel, true); | ||
159 | } | ||
160 | |||
94 | /* | 161 | /* |
95 | * histogram, sorted on item, collects periods | 162 | * histogram, sorted on item, collects periods |
96 | */ | 163 | */ |
@@ -112,11 +179,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
112 | return self; | 179 | return self; |
113 | } | 180 | } |
114 | 181 | ||
115 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) | 182 | static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) |
116 | { | 183 | { |
117 | if (!h->filtered) { | 184 | if (!h->filtered) { |
118 | hists__calc_col_len(self, h); | 185 | hists__calc_col_len(hists, h); |
119 | ++self->nr_entries; | 186 | ++hists->nr_entries; |
187 | hists->stats.total_period += h->period; | ||
120 | } | 188 | } |
121 | } | 189 | } |
122 | 190 | ||
@@ -127,11 +195,11 @@ static u8 symbol__parent_filter(const struct symbol *parent) | |||
127 | return 0; | 195 | return 0; |
128 | } | 196 | } |
129 | 197 | ||
130 | struct hist_entry *__hists__add_entry(struct hists *self, | 198 | struct hist_entry *__hists__add_entry(struct hists *hists, |
131 | struct addr_location *al, | 199 | struct addr_location *al, |
132 | struct symbol *sym_parent, u64 period) | 200 | struct symbol *sym_parent, u64 period) |
133 | { | 201 | { |
134 | struct rb_node **p = &self->entries.rb_node; | 202 | struct rb_node **p; |
135 | struct rb_node *parent = NULL; | 203 | struct rb_node *parent = NULL; |
136 | struct hist_entry *he; | 204 | struct hist_entry *he; |
137 | struct hist_entry entry = { | 205 | struct hist_entry entry = { |
@@ -149,9 +217,13 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
149 | }; | 217 | }; |
150 | int cmp; | 218 | int cmp; |
151 | 219 | ||
220 | pthread_mutex_lock(&hists->lock); | ||
221 | |||
222 | p = &hists->entries_in->rb_node; | ||
223 | |||
152 | while (*p != NULL) { | 224 | while (*p != NULL) { |
153 | parent = *p; | 225 | parent = *p; |
154 | he = rb_entry(parent, struct hist_entry, rb_node); | 226 | he = rb_entry(parent, struct hist_entry, rb_node_in); |
155 | 227 | ||
156 | cmp = hist_entry__cmp(&entry, he); | 228 | cmp = hist_entry__cmp(&entry, he); |
157 | 229 | ||
@@ -169,12 +241,14 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
169 | 241 | ||
170 | he = hist_entry__new(&entry); | 242 | he = hist_entry__new(&entry); |
171 | if (!he) | 243 | if (!he) |
172 | return NULL; | 244 | goto out_unlock; |
173 | rb_link_node(&he->rb_node, parent, p); | 245 | |
174 | rb_insert_color(&he->rb_node, &self->entries); | 246 | rb_link_node(&he->rb_node_in, parent, p); |
175 | hists__inc_nr_entries(self, he); | 247 | rb_insert_color(&he->rb_node_in, hists->entries_in); |
176 | out: | 248 | out: |
177 | hist_entry__add_cpumode_period(he, al->cpumode, period); | 249 | hist_entry__add_cpumode_period(he, al->cpumode, period); |
250 | out_unlock: | ||
251 | pthread_mutex_unlock(&hists->lock); | ||
178 | return he; | 252 | return he; |
179 | } | 253 | } |
180 | 254 | ||
@@ -221,7 +295,7 @@ void hist_entry__free(struct hist_entry *he) | |||
221 | * collapse the histogram | 295 | * collapse the histogram |
222 | */ | 296 | */ |
223 | 297 | ||
224 | static bool hists__collapse_insert_entry(struct hists *self, | 298 | static bool hists__collapse_insert_entry(struct hists *hists, |
225 | struct rb_root *root, | 299 | struct rb_root *root, |
226 | struct hist_entry *he) | 300 | struct hist_entry *he) |
227 | { | 301 | { |
@@ -232,15 +306,16 @@ static bool hists__collapse_insert_entry(struct hists *self, | |||
232 | 306 | ||
233 | while (*p != NULL) { | 307 | while (*p != NULL) { |
234 | parent = *p; | 308 | parent = *p; |
235 | iter = rb_entry(parent, struct hist_entry, rb_node); | 309 | iter = rb_entry(parent, struct hist_entry, rb_node_in); |
236 | 310 | ||
237 | cmp = hist_entry__collapse(iter, he); | 311 | cmp = hist_entry__collapse(iter, he); |
238 | 312 | ||
239 | if (!cmp) { | 313 | if (!cmp) { |
240 | iter->period += he->period; | 314 | iter->period += he->period; |
315 | iter->nr_events += he->nr_events; | ||
241 | if (symbol_conf.use_callchain) { | 316 | if (symbol_conf.use_callchain) { |
242 | callchain_cursor_reset(&self->callchain_cursor); | 317 | callchain_cursor_reset(&hists->callchain_cursor); |
243 | callchain_merge(&self->callchain_cursor, iter->callchain, | 318 | callchain_merge(&hists->callchain_cursor, iter->callchain, |
244 | he->callchain); | 319 | he->callchain); |
245 | } | 320 | } |
246 | hist_entry__free(he); | 321 | hist_entry__free(he); |
@@ -253,35 +328,68 @@ static bool hists__collapse_insert_entry(struct hists *self, | |||
253 | p = &(*p)->rb_right; | 328 | p = &(*p)->rb_right; |
254 | } | 329 | } |
255 | 330 | ||
256 | rb_link_node(&he->rb_node, parent, p); | 331 | rb_link_node(&he->rb_node_in, parent, p); |
257 | rb_insert_color(&he->rb_node, root); | 332 | rb_insert_color(&he->rb_node_in, root); |
258 | return true; | 333 | return true; |
259 | } | 334 | } |
260 | 335 | ||
261 | void hists__collapse_resort(struct hists *self) | 336 | static struct rb_root *hists__get_rotate_entries_in(struct hists *hists) |
337 | { | ||
338 | struct rb_root *root; | ||
339 | |||
340 | pthread_mutex_lock(&hists->lock); | ||
341 | |||
342 | root = hists->entries_in; | ||
343 | if (++hists->entries_in > &hists->entries_in_array[1]) | ||
344 | hists->entries_in = &hists->entries_in_array[0]; | ||
345 | |||
346 | pthread_mutex_unlock(&hists->lock); | ||
347 | |||
348 | return root; | ||
349 | } | ||
350 | |||
351 | static void hists__apply_filters(struct hists *hists, struct hist_entry *he) | ||
352 | { | ||
353 | hists__filter_entry_by_dso(hists, he); | ||
354 | hists__filter_entry_by_thread(hists, he); | ||
355 | } | ||
356 | |||
357 | static void __hists__collapse_resort(struct hists *hists, bool threaded) | ||
262 | { | 358 | { |
263 | struct rb_root tmp; | 359 | struct rb_root *root; |
264 | struct rb_node *next; | 360 | struct rb_node *next; |
265 | struct hist_entry *n; | 361 | struct hist_entry *n; |
266 | 362 | ||
267 | if (!sort__need_collapse) | 363 | if (!sort__need_collapse && !threaded) |
268 | return; | 364 | return; |
269 | 365 | ||
270 | tmp = RB_ROOT; | 366 | root = hists__get_rotate_entries_in(hists); |
271 | next = rb_first(&self->entries); | 367 | next = rb_first(root); |
272 | self->nr_entries = 0; | ||
273 | hists__reset_col_len(self); | ||
274 | 368 | ||
275 | while (next) { | 369 | while (next) { |
276 | n = rb_entry(next, struct hist_entry, rb_node); | 370 | n = rb_entry(next, struct hist_entry, rb_node_in); |
277 | next = rb_next(&n->rb_node); | 371 | next = rb_next(&n->rb_node_in); |
278 | 372 | ||
279 | rb_erase(&n->rb_node, &self->entries); | 373 | rb_erase(&n->rb_node_in, root); |
280 | if (hists__collapse_insert_entry(self, &tmp, n)) | 374 | if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) { |
281 | hists__inc_nr_entries(self, n); | 375 | /* |
376 | * If it wasn't combined with one of the entries already | ||
377 | * collapsed, we need to apply the filters that may have | ||
378 | * been set by, say, the hist_browser. | ||
379 | */ | ||
380 | hists__apply_filters(hists, n); | ||
381 | } | ||
282 | } | 382 | } |
383 | } | ||
384 | |||
385 | void hists__collapse_resort(struct hists *hists) | ||
386 | { | ||
387 | return __hists__collapse_resort(hists, false); | ||
388 | } | ||
283 | 389 | ||
284 | self->entries = tmp; | 390 | void hists__collapse_resort_threaded(struct hists *hists) |
391 | { | ||
392 | return __hists__collapse_resort(hists, true); | ||
285 | } | 393 | } |
286 | 394 | ||
287 | /* | 395 | /* |
@@ -314,31 +422,44 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
314 | rb_insert_color(&he->rb_node, entries); | 422 | rb_insert_color(&he->rb_node, entries); |
315 | } | 423 | } |
316 | 424 | ||
317 | void hists__output_resort(struct hists *self) | 425 | static void __hists__output_resort(struct hists *hists, bool threaded) |
318 | { | 426 | { |
319 | struct rb_root tmp; | 427 | struct rb_root *root; |
320 | struct rb_node *next; | 428 | struct rb_node *next; |
321 | struct hist_entry *n; | 429 | struct hist_entry *n; |
322 | u64 min_callchain_hits; | 430 | u64 min_callchain_hits; |
323 | 431 | ||
324 | min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); | 432 | min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100); |
433 | |||
434 | if (sort__need_collapse || threaded) | ||
435 | root = &hists->entries_collapsed; | ||
436 | else | ||
437 | root = hists->entries_in; | ||
325 | 438 | ||
326 | tmp = RB_ROOT; | 439 | next = rb_first(root); |
327 | next = rb_first(&self->entries); | 440 | hists->entries = RB_ROOT; |
328 | 441 | ||
329 | self->nr_entries = 0; | 442 | hists->nr_entries = 0; |
330 | hists__reset_col_len(self); | 443 | hists->stats.total_period = 0; |
444 | hists__reset_col_len(hists); | ||
331 | 445 | ||
332 | while (next) { | 446 | while (next) { |
333 | n = rb_entry(next, struct hist_entry, rb_node); | 447 | n = rb_entry(next, struct hist_entry, rb_node_in); |
334 | next = rb_next(&n->rb_node); | 448 | next = rb_next(&n->rb_node_in); |
335 | 449 | ||
336 | rb_erase(&n->rb_node, &self->entries); | 450 | __hists__insert_output_entry(&hists->entries, n, min_callchain_hits); |
337 | __hists__insert_output_entry(&tmp, n, min_callchain_hits); | 451 | hists__inc_nr_entries(hists, n); |
338 | hists__inc_nr_entries(self, n); | ||
339 | } | 452 | } |
453 | } | ||
340 | 454 | ||
341 | self->entries = tmp; | 455 | void hists__output_resort(struct hists *hists) |
456 | { | ||
457 | return __hists__output_resort(hists, false); | ||
458 | } | ||
459 | |||
460 | void hists__output_resort_threaded(struct hists *hists) | ||
461 | { | ||
462 | return __hists__output_resort(hists, true); | ||
342 | } | 463 | } |
343 | 464 | ||
344 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | 465 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) |
@@ -593,12 +714,27 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
593 | return ret; | 714 | return ret; |
594 | } | 715 | } |
595 | 716 | ||
596 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | 717 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
597 | struct hists *hists, struct hists *pair_hists, | 718 | { |
598 | bool show_displacement, long displacement, | 719 | struct rb_node *next = rb_first(&hists->entries); |
599 | bool color, u64 session_total) | 720 | struct hist_entry *n; |
721 | int row = 0; | ||
722 | |||
723 | hists__reset_col_len(hists); | ||
724 | |||
725 | while (next && row++ < max_rows) { | ||
726 | n = rb_entry(next, struct hist_entry, rb_node); | ||
727 | if (!n->filtered) | ||
728 | hists__calc_col_len(hists, n); | ||
729 | next = rb_next(&n->rb_node); | ||
730 | } | ||
731 | } | ||
732 | |||
733 | static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s, | ||
734 | size_t size, struct hists *pair_hists, | ||
735 | bool show_displacement, long displacement, | ||
736 | bool color, u64 session_total) | ||
600 | { | 737 | { |
601 | struct sort_entry *se; | ||
602 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; | 738 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; |
603 | u64 nr_events; | 739 | u64 nr_events; |
604 | const char *sep = symbol_conf.field_sep; | 740 | const char *sep = symbol_conf.field_sep; |
@@ -663,6 +799,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
663 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); | 799 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); |
664 | } | 800 | } |
665 | 801 | ||
802 | if (symbol_conf.show_total_period) { | ||
803 | if (sep) | ||
804 | ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); | ||
805 | else | ||
806 | ret += snprintf(s + ret, size - ret, " %12" PRIu64, period); | ||
807 | } | ||
808 | |||
666 | if (pair_hists) { | 809 | if (pair_hists) { |
667 | char bf[32]; | 810 | char bf[32]; |
668 | double old_percent = 0, new_percent = 0, diff; | 811 | double old_percent = 0, new_percent = 0, diff; |
@@ -697,26 +840,42 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
697 | } | 840 | } |
698 | } | 841 | } |
699 | 842 | ||
843 | return ret; | ||
844 | } | ||
845 | |||
846 | int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size, | ||
847 | struct hists *hists) | ||
848 | { | ||
849 | const char *sep = symbol_conf.field_sep; | ||
850 | struct sort_entry *se; | ||
851 | int ret = 0; | ||
852 | |||
700 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 853 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
701 | if (se->elide) | 854 | if (se->elide) |
702 | continue; | 855 | continue; |
703 | 856 | ||
704 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); | 857 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); |
705 | ret += se->se_snprintf(self, s + ret, size - ret, | 858 | ret += se->se_snprintf(he, s + ret, size - ret, |
706 | hists__col_len(hists, se->se_width_idx)); | 859 | hists__col_len(hists, se->se_width_idx)); |
707 | } | 860 | } |
708 | 861 | ||
709 | return ret; | 862 | return ret; |
710 | } | 863 | } |
711 | 864 | ||
712 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, | 865 | int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, |
713 | struct hists *pair_hists, bool show_displacement, | 866 | struct hists *pair_hists, bool show_displacement, |
714 | long displacement, FILE *fp, u64 session_total) | 867 | long displacement, FILE *fp, u64 session_total) |
715 | { | 868 | { |
716 | char bf[512]; | 869 | char bf[512]; |
717 | hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, | 870 | int ret; |
718 | show_displacement, displacement, | 871 | |
719 | true, session_total); | 872 | if (size == 0 || size > sizeof(bf)) |
873 | size = sizeof(bf); | ||
874 | |||
875 | ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists, | ||
876 | show_displacement, displacement, | ||
877 | true, session_total); | ||
878 | hist_entry__snprintf(he, bf + ret, size - ret, hists); | ||
720 | return fprintf(fp, "%s\n", bf); | 879 | return fprintf(fp, "%s\n", bf); |
721 | } | 880 | } |
722 | 881 | ||
@@ -737,8 +896,9 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, | |||
737 | left_margin); | 896 | left_margin); |
738 | } | 897 | } |
739 | 898 | ||
740 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 899 | size_t hists__fprintf(struct hists *hists, struct hists *pair, |
741 | bool show_displacement, FILE *fp) | 900 | bool show_displacement, bool show_header, int max_rows, |
901 | int max_cols, FILE *fp) | ||
742 | { | 902 | { |
743 | struct sort_entry *se; | 903 | struct sort_entry *se; |
744 | struct rb_node *nd; | 904 | struct rb_node *nd; |
@@ -748,9 +908,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
748 | unsigned int width; | 908 | unsigned int width; |
749 | const char *sep = symbol_conf.field_sep; | 909 | const char *sep = symbol_conf.field_sep; |
750 | const char *col_width = symbol_conf.col_width_list_str; | 910 | const char *col_width = symbol_conf.col_width_list_str; |
911 | int nr_rows = 0; | ||
751 | 912 | ||
752 | init_rem_hits(); | 913 | init_rem_hits(); |
753 | 914 | ||
915 | if (!show_header) | ||
916 | goto print_entries; | ||
917 | |||
754 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); | 918 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); |
755 | 919 | ||
756 | if (symbol_conf.show_nr_samples) { | 920 | if (symbol_conf.show_nr_samples) { |
@@ -760,6 +924,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
760 | fputs(" Samples ", fp); | 924 | fputs(" Samples ", fp); |
761 | } | 925 | } |
762 | 926 | ||
927 | if (symbol_conf.show_total_period) { | ||
928 | if (sep) | ||
929 | ret += fprintf(fp, "%cPeriod", *sep); | ||
930 | else | ||
931 | ret += fprintf(fp, " Period "); | ||
932 | } | ||
933 | |||
763 | if (symbol_conf.show_cpu_utilization) { | 934 | if (symbol_conf.show_cpu_utilization) { |
764 | if (sep) { | 935 | if (sep) { |
765 | ret += fprintf(fp, "%csys", *sep); | 936 | ret += fprintf(fp, "%csys", *sep); |
@@ -802,18 +973,21 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
802 | width = strlen(se->se_header); | 973 | width = strlen(se->se_header); |
803 | if (symbol_conf.col_width_list_str) { | 974 | if (symbol_conf.col_width_list_str) { |
804 | if (col_width) { | 975 | if (col_width) { |
805 | hists__set_col_len(self, se->se_width_idx, | 976 | hists__set_col_len(hists, se->se_width_idx, |
806 | atoi(col_width)); | 977 | atoi(col_width)); |
807 | col_width = strchr(col_width, ','); | 978 | col_width = strchr(col_width, ','); |
808 | if (col_width) | 979 | if (col_width) |
809 | ++col_width; | 980 | ++col_width; |
810 | } | 981 | } |
811 | } | 982 | } |
812 | if (!hists__new_col_len(self, se->se_width_idx, width)) | 983 | if (!hists__new_col_len(hists, se->se_width_idx, width)) |
813 | width = hists__col_len(self, se->se_width_idx); | 984 | width = hists__col_len(hists, se->se_width_idx); |
814 | fprintf(fp, " %*s", width, se->se_header); | 985 | fprintf(fp, " %*s", width, se->se_header); |
815 | } | 986 | } |
987 | |||
816 | fprintf(fp, "\n"); | 988 | fprintf(fp, "\n"); |
989 | if (max_rows && ++nr_rows >= max_rows) | ||
990 | goto out; | ||
817 | 991 | ||
818 | if (sep) | 992 | if (sep) |
819 | goto print_entries; | 993 | goto print_entries; |
@@ -821,6 +995,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
821 | fprintf(fp, "# ........"); | 995 | fprintf(fp, "# ........"); |
822 | if (symbol_conf.show_nr_samples) | 996 | if (symbol_conf.show_nr_samples) |
823 | fprintf(fp, " .........."); | 997 | fprintf(fp, " .........."); |
998 | if (symbol_conf.show_total_period) | ||
999 | fprintf(fp, " ............"); | ||
824 | if (pair) { | 1000 | if (pair) { |
825 | fprintf(fp, " .........."); | 1001 | fprintf(fp, " .........."); |
826 | if (show_displacement) | 1002 | if (show_displacement) |
@@ -833,19 +1009,28 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
833 | continue; | 1009 | continue; |
834 | 1010 | ||
835 | fprintf(fp, " "); | 1011 | fprintf(fp, " "); |
836 | width = hists__col_len(self, se->se_width_idx); | 1012 | width = hists__col_len(hists, se->se_width_idx); |
837 | if (width == 0) | 1013 | if (width == 0) |
838 | width = strlen(se->se_header); | 1014 | width = strlen(se->se_header); |
839 | for (i = 0; i < width; i++) | 1015 | for (i = 0; i < width; i++) |
840 | fprintf(fp, "."); | 1016 | fprintf(fp, "."); |
841 | } | 1017 | } |
842 | 1018 | ||
843 | fprintf(fp, "\n#\n"); | 1019 | fprintf(fp, "\n"); |
1020 | if (max_rows && ++nr_rows >= max_rows) | ||
1021 | goto out; | ||
1022 | |||
1023 | fprintf(fp, "#\n"); | ||
1024 | if (max_rows && ++nr_rows >= max_rows) | ||
1025 | goto out; | ||
844 | 1026 | ||
845 | print_entries: | 1027 | print_entries: |
846 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1028 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
847 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1029 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
848 | 1030 | ||
1031 | if (h->filtered) | ||
1032 | continue; | ||
1033 | |||
849 | if (show_displacement) { | 1034 | if (show_displacement) { |
850 | if (h->pair != NULL) | 1035 | if (h->pair != NULL) |
851 | displacement = ((long)h->pair->position - | 1036 | displacement = ((long)h->pair->position - |
@@ -854,19 +1039,22 @@ print_entries: | |||
854 | displacement = 0; | 1039 | displacement = 0; |
855 | ++position; | 1040 | ++position; |
856 | } | 1041 | } |
857 | ret += hist_entry__fprintf(h, self, pair, show_displacement, | 1042 | ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement, |
858 | displacement, fp, self->stats.total_period); | 1043 | displacement, fp, hists->stats.total_period); |
859 | 1044 | ||
860 | if (symbol_conf.use_callchain) | 1045 | if (symbol_conf.use_callchain) |
861 | ret += hist_entry__fprintf_callchain(h, self, fp, | 1046 | ret += hist_entry__fprintf_callchain(h, hists, fp, |
862 | self->stats.total_period); | 1047 | hists->stats.total_period); |
1048 | if (max_rows && ++nr_rows >= max_rows) | ||
1049 | goto out; | ||
1050 | |||
863 | if (h->ms.map == NULL && verbose > 1) { | 1051 | if (h->ms.map == NULL && verbose > 1) { |
864 | __map_groups__fprintf_maps(&h->thread->mg, | 1052 | __map_groups__fprintf_maps(&h->thread->mg, |
865 | MAP__FUNCTION, verbose, fp); | 1053 | MAP__FUNCTION, verbose, fp); |
866 | fprintf(fp, "%.10s end\n", graph_dotted_line); | 1054 | fprintf(fp, "%.10s end\n", graph_dotted_line); |
867 | } | 1055 | } |
868 | } | 1056 | } |
869 | 1057 | out: | |
870 | free(rem_sq_bracket); | 1058 | free(rem_sq_bracket); |
871 | 1059 | ||
872 | return ret; | 1060 | return ret; |
@@ -875,7 +1063,7 @@ print_entries: | |||
875 | /* | 1063 | /* |
876 | * See hists__fprintf to match the column widths | 1064 | * See hists__fprintf to match the column widths |
877 | */ | 1065 | */ |
878 | unsigned int hists__sort_list_width(struct hists *self) | 1066 | unsigned int hists__sort_list_width(struct hists *hists) |
879 | { | 1067 | { |
880 | struct sort_entry *se; | 1068 | struct sort_entry *se; |
881 | int ret = 9; /* total % */ | 1069 | int ret = 9; /* total % */ |
@@ -892,9 +1080,12 @@ unsigned int hists__sort_list_width(struct hists *self) | |||
892 | if (symbol_conf.show_nr_samples) | 1080 | if (symbol_conf.show_nr_samples) |
893 | ret += 11; | 1081 | ret += 11; |
894 | 1082 | ||
1083 | if (symbol_conf.show_total_period) | ||
1084 | ret += 13; | ||
1085 | |||
895 | list_for_each_entry(se, &hist_entry__sort_list, list) | 1086 | list_for_each_entry(se, &hist_entry__sort_list, list) |
896 | if (!se->elide) | 1087 | if (!se->elide) |
897 | ret += 2 + hists__col_len(self, se->se_width_idx); | 1088 | ret += 2 + hists__col_len(hists, se->se_width_idx); |
898 | 1089 | ||
899 | if (verbose) /* Addr + origin */ | 1090 | if (verbose) /* Addr + origin */ |
900 | ret += 3 + BITS_PER_LONG / 4; | 1091 | ret += 3 + BITS_PER_LONG / 4; |
@@ -902,63 +1093,84 @@ unsigned int hists__sort_list_width(struct hists *self) | |||
902 | return ret; | 1093 | return ret; |
903 | } | 1094 | } |
904 | 1095 | ||
905 | static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, | 1096 | static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h, |
906 | enum hist_filter filter) | 1097 | enum hist_filter filter) |
907 | { | 1098 | { |
908 | h->filtered &= ~(1 << filter); | 1099 | h->filtered &= ~(1 << filter); |
909 | if (h->filtered) | 1100 | if (h->filtered) |
910 | return; | 1101 | return; |
911 | 1102 | ||
912 | ++self->nr_entries; | 1103 | ++hists->nr_entries; |
913 | if (h->ms.unfolded) | 1104 | if (h->ms.unfolded) |
914 | self->nr_entries += h->nr_rows; | 1105 | hists->nr_entries += h->nr_rows; |
915 | h->row_offset = 0; | 1106 | h->row_offset = 0; |
916 | self->stats.total_period += h->period; | 1107 | hists->stats.total_period += h->period; |
917 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | 1108 | hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; |
918 | 1109 | ||
919 | hists__calc_col_len(self, h); | 1110 | hists__calc_col_len(hists, h); |
920 | } | 1111 | } |
921 | 1112 | ||
922 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | 1113 | |
1114 | static bool hists__filter_entry_by_dso(struct hists *hists, | ||
1115 | struct hist_entry *he) | ||
1116 | { | ||
1117 | if (hists->dso_filter != NULL && | ||
1118 | (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) { | ||
1119 | he->filtered |= (1 << HIST_FILTER__DSO); | ||
1120 | return true; | ||
1121 | } | ||
1122 | |||
1123 | return false; | ||
1124 | } | ||
1125 | |||
1126 | void hists__filter_by_dso(struct hists *hists) | ||
923 | { | 1127 | { |
924 | struct rb_node *nd; | 1128 | struct rb_node *nd; |
925 | 1129 | ||
926 | self->nr_entries = self->stats.total_period = 0; | 1130 | hists->nr_entries = hists->stats.total_period = 0; |
927 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 1131 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
928 | hists__reset_col_len(self); | 1132 | hists__reset_col_len(hists); |
929 | 1133 | ||
930 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1134 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
931 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1135 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
932 | 1136 | ||
933 | if (symbol_conf.exclude_other && !h->parent) | 1137 | if (symbol_conf.exclude_other && !h->parent) |
934 | continue; | 1138 | continue; |
935 | 1139 | ||
936 | if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { | 1140 | if (hists__filter_entry_by_dso(hists, h)) |
937 | h->filtered |= (1 << HIST_FILTER__DSO); | ||
938 | continue; | 1141 | continue; |
939 | } | ||
940 | 1142 | ||
941 | hists__remove_entry_filter(self, h, HIST_FILTER__DSO); | 1143 | hists__remove_entry_filter(hists, h, HIST_FILTER__DSO); |
942 | } | 1144 | } |
943 | } | 1145 | } |
944 | 1146 | ||
945 | void hists__filter_by_thread(struct hists *self, const struct thread *thread) | 1147 | static bool hists__filter_entry_by_thread(struct hists *hists, |
1148 | struct hist_entry *he) | ||
1149 | { | ||
1150 | if (hists->thread_filter != NULL && | ||
1151 | he->thread != hists->thread_filter) { | ||
1152 | he->filtered |= (1 << HIST_FILTER__THREAD); | ||
1153 | return true; | ||
1154 | } | ||
1155 | |||
1156 | return false; | ||
1157 | } | ||
1158 | |||
1159 | void hists__filter_by_thread(struct hists *hists) | ||
946 | { | 1160 | { |
947 | struct rb_node *nd; | 1161 | struct rb_node *nd; |
948 | 1162 | ||
949 | self->nr_entries = self->stats.total_period = 0; | 1163 | hists->nr_entries = hists->stats.total_period = 0; |
950 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 1164 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
951 | hists__reset_col_len(self); | 1165 | hists__reset_col_len(hists); |
952 | 1166 | ||
953 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 1167 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
954 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1168 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
955 | 1169 | ||
956 | if (thread != NULL && h->thread != thread) { | 1170 | if (hists__filter_entry_by_thread(hists, h)) |
957 | h->filtered |= (1 << HIST_FILTER__THREAD); | ||
958 | continue; | 1171 | continue; |
959 | } | ||
960 | 1172 | ||
961 | hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); | 1173 | hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD); |
962 | } | 1174 | } |
963 | } | 1175 | } |
964 | 1176 | ||
@@ -972,13 +1184,13 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize) | |||
972 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); | 1184 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
973 | } | 1185 | } |
974 | 1186 | ||
975 | void hists__inc_nr_events(struct hists *self, u32 type) | 1187 | void hists__inc_nr_events(struct hists *hists, u32 type) |
976 | { | 1188 | { |
977 | ++self->stats.nr_events[0]; | 1189 | ++hists->stats.nr_events[0]; |
978 | ++self->stats.nr_events[type]; | 1190 | ++hists->stats.nr_events[type]; |
979 | } | 1191 | } |
980 | 1192 | ||
981 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | 1193 | size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) |
982 | { | 1194 | { |
983 | int i; | 1195 | int i; |
984 | size_t ret = 0; | 1196 | size_t ret = 0; |
@@ -986,7 +1198,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
986 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 1198 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
987 | const char *name; | 1199 | const char *name; |
988 | 1200 | ||
989 | if (self->stats.nr_events[i] == 0) | 1201 | if (hists->stats.nr_events[i] == 0) |
990 | continue; | 1202 | continue; |
991 | 1203 | ||
992 | name = perf_event__name(i); | 1204 | name = perf_event__name(i); |
@@ -994,8 +1206,18 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) | |||
994 | continue; | 1206 | continue; |
995 | 1207 | ||
996 | ret += fprintf(fp, "%16s events: %10d\n", name, | 1208 | ret += fprintf(fp, "%16s events: %10d\n", name, |
997 | self->stats.nr_events[i]); | 1209 | hists->stats.nr_events[i]); |
998 | } | 1210 | } |
999 | 1211 | ||
1000 | return ret; | 1212 | return ret; |
1001 | } | 1213 | } |
1214 | |||
1215 | void hists__init(struct hists *hists) | ||
1216 | { | ||
1217 | memset(hists, 0, sizeof(*hists)); | ||
1218 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | ||
1219 | hists->entries_in = &hists->entries_in_array[0]; | ||
1220 | hists->entries_collapsed = RB_ROOT; | ||
1221 | hists->entries = RB_ROOT; | ||
1222 | pthread_mutex_init(&hists->lock, NULL); | ||
1223 | } | ||
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 3beb97c4d822..c86c1d27bd1e 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define __PERF_HIST_H | 2 | #define __PERF_HIST_H |
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <pthread.h> | ||
5 | #include "callchain.h" | 6 | #include "callchain.h" |
6 | 7 | ||
7 | extern struct callchain_param callchain_param; | 8 | extern struct callchain_param callchain_param; |
@@ -27,6 +28,7 @@ struct events_stats { | |||
27 | u64 total_lost; | 28 | u64 total_lost; |
28 | u64 total_invalid_chains; | 29 | u64 total_invalid_chains; |
29 | u32 nr_events[PERF_RECORD_HEADER_MAX]; | 30 | u32 nr_events[PERF_RECORD_HEADER_MAX]; |
31 | u32 nr_lost_warned; | ||
30 | u32 nr_unknown_events; | 32 | u32 nr_unknown_events; |
31 | u32 nr_invalid_chains; | 33 | u32 nr_invalid_chains; |
32 | u32 nr_unknown_id; | 34 | u32 nr_unknown_id; |
@@ -42,9 +44,18 @@ enum hist_column { | |||
42 | HISTC_NR_COLS, /* Last entry */ | 44 | HISTC_NR_COLS, /* Last entry */ |
43 | }; | 45 | }; |
44 | 46 | ||
47 | struct thread; | ||
48 | struct dso; | ||
49 | |||
45 | struct hists { | 50 | struct hists { |
51 | struct rb_root entries_in_array[2]; | ||
52 | struct rb_root *entries_in; | ||
46 | struct rb_root entries; | 53 | struct rb_root entries; |
54 | struct rb_root entries_collapsed; | ||
47 | u64 nr_entries; | 55 | u64 nr_entries; |
56 | const struct thread *thread_filter; | ||
57 | const struct dso *dso_filter; | ||
58 | pthread_mutex_t lock; | ||
48 | struct events_stats stats; | 59 | struct events_stats stats; |
49 | u64 event_stream; | 60 | u64 event_stream; |
50 | u16 col_len[HISTC_NR_COLS]; | 61 | u16 col_len[HISTC_NR_COLS]; |
@@ -52,34 +63,42 @@ struct hists { | |||
52 | struct callchain_cursor callchain_cursor; | 63 | struct callchain_cursor callchain_cursor; |
53 | }; | 64 | }; |
54 | 65 | ||
66 | void hists__init(struct hists *hists); | ||
67 | |||
55 | struct hist_entry *__hists__add_entry(struct hists *self, | 68 | struct hist_entry *__hists__add_entry(struct hists *self, |
56 | struct addr_location *al, | 69 | struct addr_location *al, |
57 | struct symbol *parent, u64 period); | 70 | struct symbol *parent, u64 period); |
58 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | 71 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); |
59 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | 72 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); |
60 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, | 73 | int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, |
61 | struct hists *pair_hists, bool show_displacement, | 74 | struct hists *pair_hists, bool show_displacement, |
62 | long displacement, FILE *fp, u64 total); | 75 | long displacement, FILE *fp, u64 session_total); |
63 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, | 76 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, |
64 | struct hists *hists, struct hists *pair_hists, | 77 | struct hists *hists); |
65 | bool show_displacement, long displacement, | ||
66 | bool color, u64 total); | ||
67 | void hist_entry__free(struct hist_entry *); | 78 | void hist_entry__free(struct hist_entry *); |
68 | 79 | ||
69 | void hists__output_resort(struct hists *self); | 80 | void hists__output_resort(struct hists *self); |
81 | void hists__output_resort_threaded(struct hists *hists); | ||
70 | void hists__collapse_resort(struct hists *self); | 82 | void hists__collapse_resort(struct hists *self); |
83 | void hists__collapse_resort_threaded(struct hists *hists); | ||
84 | |||
85 | void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel); | ||
86 | void hists__decay_entries_threaded(struct hists *hists, bool zap_user, | ||
87 | bool zap_kernel); | ||
88 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | ||
71 | 89 | ||
72 | void hists__inc_nr_events(struct hists *self, u32 type); | 90 | void hists__inc_nr_events(struct hists *self, u32 type); |
73 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); | 91 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); |
74 | 92 | ||
75 | size_t hists__fprintf(struct hists *self, struct hists *pair, | 93 | size_t hists__fprintf(struct hists *self, struct hists *pair, |
76 | bool show_displacement, FILE *fp); | 94 | bool show_displacement, bool show_header, |
95 | int max_rows, int max_cols, FILE *fp); | ||
77 | 96 | ||
78 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); | 97 | int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); |
79 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); | 98 | int hist_entry__annotate(struct hist_entry *self, size_t privsize); |
80 | 99 | ||
81 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); | 100 | void hists__filter_by_dso(struct hists *hists); |
82 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); | 101 | void hists__filter_by_thread(struct hists *hists); |
83 | 102 | ||
84 | u16 hists__col_len(struct hists *self, enum hist_column col); | 103 | u16 hists__col_len(struct hists *self, enum hist_column col); |
85 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 104 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
@@ -90,26 +109,33 @@ struct perf_evlist; | |||
90 | #ifdef NO_NEWT_SUPPORT | 109 | #ifdef NO_NEWT_SUPPORT |
91 | static inline | 110 | static inline |
92 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, | 111 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, |
93 | const char *help __used) | 112 | const char *help __used, |
113 | void(*timer)(void *arg) __used, | ||
114 | void *arg __used, | ||
115 | int refresh __used) | ||
94 | { | 116 | { |
95 | return 0; | 117 | return 0; |
96 | } | 118 | } |
97 | 119 | ||
98 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, | 120 | static inline int hist_entry__tui_annotate(struct hist_entry *self __used, |
99 | int evidx __used) | 121 | int evidx __used, |
122 | int nr_events __used, | ||
123 | void(*timer)(void *arg) __used, | ||
124 | void *arg __used, | ||
125 | int delay_secs __used) | ||
100 | { | 126 | { |
101 | return 0; | 127 | return 0; |
102 | } | 128 | } |
103 | #define KEY_LEFT -1 | 129 | #define K_LEFT -1 |
104 | #define KEY_RIGHT -2 | 130 | #define K_RIGHT -2 |
105 | #else | 131 | #else |
106 | #include <newt.h> | 132 | #include "ui/keysyms.h" |
107 | int hist_entry__tui_annotate(struct hist_entry *self, int evidx); | 133 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, |
108 | 134 | void(*timer)(void *arg), void *arg, int delay_secs); | |
109 | #define KEY_LEFT NEWT_KEY_LEFT | ||
110 | #define KEY_RIGHT NEWT_KEY_RIGHT | ||
111 | 135 | ||
112 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help); | 136 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
137 | void(*timer)(void *arg), void *arg, | ||
138 | int refresh); | ||
113 | #endif | 139 | #endif |
114 | 140 | ||
115 | unsigned int hists__sort_list_width(struct hists *self); | 141 | unsigned int hists__sort_list_width(struct hists *self); |
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index 791f9dd27ebf..547628e97f3d 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h | |||
@@ -5,7 +5,9 @@ | |||
5 | #define __always_inline inline | 5 | #define __always_inline inline |
6 | #endif | 6 | #endif |
7 | #define __user | 7 | #define __user |
8 | #ifndef __attribute_const__ | ||
8 | #define __attribute_const__ | 9 | #define __attribute_const__ |
10 | #endif | ||
9 | 11 | ||
10 | #define __used __attribute__((__unused__)) | 12 | #define __used __attribute__((__unused__)) |
11 | 13 | ||
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index a16ecab5229d..78284b13e808 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -18,6 +18,13 @@ static inline int is_anon_memory(const char *filename) | |||
18 | return strcmp(filename, "//anon") == 0; | 18 | return strcmp(filename, "//anon") == 0; |
19 | } | 19 | } |
20 | 20 | ||
21 | static inline int is_no_dso_memory(const char *filename) | ||
22 | { | ||
23 | return !strcmp(filename, "[stack]") || | ||
24 | !strcmp(filename, "[vdso]") || | ||
25 | !strcmp(filename, "[heap]"); | ||
26 | } | ||
27 | |||
21 | void map__init(struct map *self, enum map_type type, | 28 | void map__init(struct map *self, enum map_type type, |
22 | u64 start, u64 end, u64 pgoff, struct dso *dso) | 29 | u64 start, u64 end, u64 pgoff, struct dso *dso) |
23 | { | 30 | { |
@@ -42,9 +49,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
42 | if (self != NULL) { | 49 | if (self != NULL) { |
43 | char newfilename[PATH_MAX]; | 50 | char newfilename[PATH_MAX]; |
44 | struct dso *dso; | 51 | struct dso *dso; |
45 | int anon; | 52 | int anon, no_dso; |
46 | 53 | ||
47 | anon = is_anon_memory(filename); | 54 | anon = is_anon_memory(filename); |
55 | no_dso = is_no_dso_memory(filename); | ||
48 | 56 | ||
49 | if (anon) { | 57 | if (anon) { |
50 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); | 58 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); |
@@ -57,12 +65,16 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
57 | 65 | ||
58 | map__init(self, type, start, start + len, pgoff, dso); | 66 | map__init(self, type, start, start + len, pgoff, dso); |
59 | 67 | ||
60 | if (anon) { | 68 | if (anon || no_dso) { |
61 | set_identity: | ||
62 | self->map_ip = self->unmap_ip = identity__map_ip; | 69 | self->map_ip = self->unmap_ip = identity__map_ip; |
63 | } else if (strcmp(filename, "[vdso]") == 0) { | 70 | |
64 | dso__set_loaded(dso, self->type); | 71 | /* |
65 | goto set_identity; | 72 | * Set memory without DSO as loaded. All map__find_* |
73 | * functions still return NULL, and we avoid the | ||
74 | * unnecessary map__load warning. | ||
75 | */ | ||
76 | if (no_dso) | ||
77 | dso__set_loaded(dso, self->type); | ||
66 | } | 78 | } |
67 | } | 79 | } |
68 | return self; | 80 | return self; |
@@ -127,8 +139,8 @@ int map__load(struct map *self, symbol_filter_t filter) | |||
127 | 139 | ||
128 | if (len > sizeof(DSO__DELETED) && | 140 | if (len > sizeof(DSO__DELETED) && |
129 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { | 141 | strcmp(name + real_len + 1, DSO__DELETED) == 0) { |
130 | pr_warning("%.*s was updated, restart the long " | 142 | pr_warning("%.*s was updated (is prelink enabled?). " |
131 | "running apps that use it!\n", | 143 | "Restart the long running apps that use it!\n", |
132 | (int)real_len, name); | 144 | (int)real_len, name); |
133 | } else { | 145 | } else { |
134 | pr_warning("no symbols found in %s, maybe install " | 146 | pr_warning("no symbols found in %s, maybe install " |
@@ -220,55 +232,55 @@ u64 map__objdump_2ip(struct map *map, u64 addr) | |||
220 | return ip; | 232 | return ip; |
221 | } | 233 | } |
222 | 234 | ||
223 | void map_groups__init(struct map_groups *self) | 235 | void map_groups__init(struct map_groups *mg) |
224 | { | 236 | { |
225 | int i; | 237 | int i; |
226 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 238 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
227 | self->maps[i] = RB_ROOT; | 239 | mg->maps[i] = RB_ROOT; |
228 | INIT_LIST_HEAD(&self->removed_maps[i]); | 240 | INIT_LIST_HEAD(&mg->removed_maps[i]); |
229 | } | 241 | } |
230 | self->machine = NULL; | 242 | mg->machine = NULL; |
231 | } | 243 | } |
232 | 244 | ||
233 | static void maps__delete(struct rb_root *self) | 245 | static void maps__delete(struct rb_root *maps) |
234 | { | 246 | { |
235 | struct rb_node *next = rb_first(self); | 247 | struct rb_node *next = rb_first(maps); |
236 | 248 | ||
237 | while (next) { | 249 | while (next) { |
238 | struct map *pos = rb_entry(next, struct map, rb_node); | 250 | struct map *pos = rb_entry(next, struct map, rb_node); |
239 | 251 | ||
240 | next = rb_next(&pos->rb_node); | 252 | next = rb_next(&pos->rb_node); |
241 | rb_erase(&pos->rb_node, self); | 253 | rb_erase(&pos->rb_node, maps); |
242 | map__delete(pos); | 254 | map__delete(pos); |
243 | } | 255 | } |
244 | } | 256 | } |
245 | 257 | ||
246 | static void maps__delete_removed(struct list_head *self) | 258 | static void maps__delete_removed(struct list_head *maps) |
247 | { | 259 | { |
248 | struct map *pos, *n; | 260 | struct map *pos, *n; |
249 | 261 | ||
250 | list_for_each_entry_safe(pos, n, self, node) { | 262 | list_for_each_entry_safe(pos, n, maps, node) { |
251 | list_del(&pos->node); | 263 | list_del(&pos->node); |
252 | map__delete(pos); | 264 | map__delete(pos); |
253 | } | 265 | } |
254 | } | 266 | } |
255 | 267 | ||
256 | void map_groups__exit(struct map_groups *self) | 268 | void map_groups__exit(struct map_groups *mg) |
257 | { | 269 | { |
258 | int i; | 270 | int i; |
259 | 271 | ||
260 | for (i = 0; i < MAP__NR_TYPES; ++i) { | 272 | for (i = 0; i < MAP__NR_TYPES; ++i) { |
261 | maps__delete(&self->maps[i]); | 273 | maps__delete(&mg->maps[i]); |
262 | maps__delete_removed(&self->removed_maps[i]); | 274 | maps__delete_removed(&mg->removed_maps[i]); |
263 | } | 275 | } |
264 | } | 276 | } |
265 | 277 | ||
266 | void map_groups__flush(struct map_groups *self) | 278 | void map_groups__flush(struct map_groups *mg) |
267 | { | 279 | { |
268 | int type; | 280 | int type; |
269 | 281 | ||
270 | for (type = 0; type < MAP__NR_TYPES; type++) { | 282 | for (type = 0; type < MAP__NR_TYPES; type++) { |
271 | struct rb_root *root = &self->maps[type]; | 283 | struct rb_root *root = &mg->maps[type]; |
272 | struct rb_node *next = rb_first(root); | 284 | struct rb_node *next = rb_first(root); |
273 | 285 | ||
274 | while (next) { | 286 | while (next) { |
@@ -280,17 +292,17 @@ void map_groups__flush(struct map_groups *self) | |||
280 | * instance in some hist_entry instances, so | 292 | * instance in some hist_entry instances, so |
281 | * just move them to a separate list. | 293 | * just move them to a separate list. |
282 | */ | 294 | */ |
283 | list_add_tail(&pos->node, &self->removed_maps[pos->type]); | 295 | list_add_tail(&pos->node, &mg->removed_maps[pos->type]); |
284 | } | 296 | } |
285 | } | 297 | } |
286 | } | 298 | } |
287 | 299 | ||
288 | struct symbol *map_groups__find_symbol(struct map_groups *self, | 300 | struct symbol *map_groups__find_symbol(struct map_groups *mg, |
289 | enum map_type type, u64 addr, | 301 | enum map_type type, u64 addr, |
290 | struct map **mapp, | 302 | struct map **mapp, |
291 | symbol_filter_t filter) | 303 | symbol_filter_t filter) |
292 | { | 304 | { |
293 | struct map *map = map_groups__find(self, type, addr); | 305 | struct map *map = map_groups__find(mg, type, addr); |
294 | 306 | ||
295 | if (map != NULL) { | 307 | if (map != NULL) { |
296 | if (mapp != NULL) | 308 | if (mapp != NULL) |
@@ -301,7 +313,7 @@ struct symbol *map_groups__find_symbol(struct map_groups *self, | |||
301 | return NULL; | 313 | return NULL; |
302 | } | 314 | } |
303 | 315 | ||
304 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | 316 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, |
305 | enum map_type type, | 317 | enum map_type type, |
306 | const char *name, | 318 | const char *name, |
307 | struct map **mapp, | 319 | struct map **mapp, |
@@ -309,7 +321,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | |||
309 | { | 321 | { |
310 | struct rb_node *nd; | 322 | struct rb_node *nd; |
311 | 323 | ||
312 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | 324 | for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { |
313 | struct map *pos = rb_entry(nd, struct map, rb_node); | 325 | struct map *pos = rb_entry(nd, struct map, rb_node); |
314 | struct symbol *sym = map__find_symbol_by_name(pos, name, filter); | 326 | struct symbol *sym = map__find_symbol_by_name(pos, name, filter); |
315 | 327 | ||
@@ -323,13 +335,13 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | |||
323 | return NULL; | 335 | return NULL; |
324 | } | 336 | } |
325 | 337 | ||
326 | size_t __map_groups__fprintf_maps(struct map_groups *self, | 338 | size_t __map_groups__fprintf_maps(struct map_groups *mg, |
327 | enum map_type type, int verbose, FILE *fp) | 339 | enum map_type type, int verbose, FILE *fp) |
328 | { | 340 | { |
329 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); | 341 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); |
330 | struct rb_node *nd; | 342 | struct rb_node *nd; |
331 | 343 | ||
332 | for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { | 344 | for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { |
333 | struct map *pos = rb_entry(nd, struct map, rb_node); | 345 | struct map *pos = rb_entry(nd, struct map, rb_node); |
334 | printed += fprintf(fp, "Map:"); | 346 | printed += fprintf(fp, "Map:"); |
335 | printed += map__fprintf(pos, fp); | 347 | printed += map__fprintf(pos, fp); |
@@ -342,22 +354,22 @@ size_t __map_groups__fprintf_maps(struct map_groups *self, | |||
342 | return printed; | 354 | return printed; |
343 | } | 355 | } |
344 | 356 | ||
345 | size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp) | 357 | size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp) |
346 | { | 358 | { |
347 | size_t printed = 0, i; | 359 | size_t printed = 0, i; |
348 | for (i = 0; i < MAP__NR_TYPES; ++i) | 360 | for (i = 0; i < MAP__NR_TYPES; ++i) |
349 | printed += __map_groups__fprintf_maps(self, i, verbose, fp); | 361 | printed += __map_groups__fprintf_maps(mg, i, verbose, fp); |
350 | return printed; | 362 | return printed; |
351 | } | 363 | } |
352 | 364 | ||
353 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, | 365 | static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg, |
354 | enum map_type type, | 366 | enum map_type type, |
355 | int verbose, FILE *fp) | 367 | int verbose, FILE *fp) |
356 | { | 368 | { |
357 | struct map *pos; | 369 | struct map *pos; |
358 | size_t printed = 0; | 370 | size_t printed = 0; |
359 | 371 | ||
360 | list_for_each_entry(pos, &self->removed_maps[type], node) { | 372 | list_for_each_entry(pos, &mg->removed_maps[type], node) { |
361 | printed += fprintf(fp, "Map:"); | 373 | printed += fprintf(fp, "Map:"); |
362 | printed += map__fprintf(pos, fp); | 374 | printed += map__fprintf(pos, fp); |
363 | if (verbose > 1) { | 375 | if (verbose > 1) { |
@@ -368,26 +380,26 @@ static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, | |||
368 | return printed; | 380 | return printed; |
369 | } | 381 | } |
370 | 382 | ||
371 | static size_t map_groups__fprintf_removed_maps(struct map_groups *self, | 383 | static size_t map_groups__fprintf_removed_maps(struct map_groups *mg, |
372 | int verbose, FILE *fp) | 384 | int verbose, FILE *fp) |
373 | { | 385 | { |
374 | size_t printed = 0, i; | 386 | size_t printed = 0, i; |
375 | for (i = 0; i < MAP__NR_TYPES; ++i) | 387 | for (i = 0; i < MAP__NR_TYPES; ++i) |
376 | printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp); | 388 | printed += __map_groups__fprintf_removed_maps(mg, i, verbose, fp); |
377 | return printed; | 389 | return printed; |
378 | } | 390 | } |
379 | 391 | ||
380 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp) | 392 | size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp) |
381 | { | 393 | { |
382 | size_t printed = map_groups__fprintf_maps(self, verbose, fp); | 394 | size_t printed = map_groups__fprintf_maps(mg, verbose, fp); |
383 | printed += fprintf(fp, "Removed maps:\n"); | 395 | printed += fprintf(fp, "Removed maps:\n"); |
384 | return printed + map_groups__fprintf_removed_maps(self, verbose, fp); | 396 | return printed + map_groups__fprintf_removed_maps(mg, verbose, fp); |
385 | } | 397 | } |
386 | 398 | ||
387 | int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | 399 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, |
388 | int verbose, FILE *fp) | 400 | int verbose, FILE *fp) |
389 | { | 401 | { |
390 | struct rb_root *root = &self->maps[map->type]; | 402 | struct rb_root *root = &mg->maps[map->type]; |
391 | struct rb_node *next = rb_first(root); | 403 | struct rb_node *next = rb_first(root); |
392 | int err = 0; | 404 | int err = 0; |
393 | 405 | ||
@@ -418,7 +430,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | |||
418 | } | 430 | } |
419 | 431 | ||
420 | before->end = map->start - 1; | 432 | before->end = map->start - 1; |
421 | map_groups__insert(self, before); | 433 | map_groups__insert(mg, before); |
422 | if (verbose >= 2) | 434 | if (verbose >= 2) |
423 | map__fprintf(before, fp); | 435 | map__fprintf(before, fp); |
424 | } | 436 | } |
@@ -432,7 +444,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | |||
432 | } | 444 | } |
433 | 445 | ||
434 | after->start = map->end + 1; | 446 | after->start = map->end + 1; |
435 | map_groups__insert(self, after); | 447 | map_groups__insert(mg, after); |
436 | if (verbose >= 2) | 448 | if (verbose >= 2) |
437 | map__fprintf(after, fp); | 449 | map__fprintf(after, fp); |
438 | } | 450 | } |
@@ -441,7 +453,7 @@ move_map: | |||
441 | * If we have references, just move them to a separate list. | 453 | * If we have references, just move them to a separate list. |
442 | */ | 454 | */ |
443 | if (pos->referenced) | 455 | if (pos->referenced) |
444 | list_add_tail(&pos->node, &self->removed_maps[map->type]); | 456 | list_add_tail(&pos->node, &mg->removed_maps[map->type]); |
445 | else | 457 | else |
446 | map__delete(pos); | 458 | map__delete(pos); |
447 | 459 | ||
@@ -455,7 +467,7 @@ move_map: | |||
455 | /* | 467 | /* |
456 | * XXX This should not really _copy_ te maps, but refcount them. | 468 | * XXX This should not really _copy_ te maps, but refcount them. |
457 | */ | 469 | */ |
458 | int map_groups__clone(struct map_groups *self, | 470 | int map_groups__clone(struct map_groups *mg, |
459 | struct map_groups *parent, enum map_type type) | 471 | struct map_groups *parent, enum map_type type) |
460 | { | 472 | { |
461 | struct rb_node *nd; | 473 | struct rb_node *nd; |
@@ -464,7 +476,7 @@ int map_groups__clone(struct map_groups *self, | |||
464 | struct map *new = map__clone(map); | 476 | struct map *new = map__clone(map); |
465 | if (new == NULL) | 477 | if (new == NULL) |
466 | return -ENOMEM; | 478 | return -ENOMEM; |
467 | map_groups__insert(self, new); | 479 | map_groups__insert(mg, new); |
468 | } | 480 | } |
469 | return 0; | 481 | return 0; |
470 | } | 482 | } |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index b397c0383728..890d85545d0f 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -123,17 +123,17 @@ void map__fixup_end(struct map *self); | |||
123 | 123 | ||
124 | void map__reloc_vmlinux(struct map *self); | 124 | void map__reloc_vmlinux(struct map *self); |
125 | 125 | ||
126 | size_t __map_groups__fprintf_maps(struct map_groups *self, | 126 | size_t __map_groups__fprintf_maps(struct map_groups *mg, |
127 | enum map_type type, int verbose, FILE *fp); | 127 | enum map_type type, int verbose, FILE *fp); |
128 | void maps__insert(struct rb_root *maps, struct map *map); | 128 | void maps__insert(struct rb_root *maps, struct map *map); |
129 | void maps__remove(struct rb_root *self, struct map *map); | 129 | void maps__remove(struct rb_root *maps, struct map *map); |
130 | struct map *maps__find(struct rb_root *maps, u64 addr); | 130 | struct map *maps__find(struct rb_root *maps, u64 addr); |
131 | void map_groups__init(struct map_groups *self); | 131 | void map_groups__init(struct map_groups *mg); |
132 | void map_groups__exit(struct map_groups *self); | 132 | void map_groups__exit(struct map_groups *mg); |
133 | int map_groups__clone(struct map_groups *self, | 133 | int map_groups__clone(struct map_groups *mg, |
134 | struct map_groups *parent, enum map_type type); | 134 | struct map_groups *parent, enum map_type type); |
135 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); | 135 | size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp); |
136 | size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); | 136 | size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp); |
137 | 137 | ||
138 | typedef void (*machine__process_t)(struct machine *self, void *data); | 138 | typedef void (*machine__process_t)(struct machine *self, void *data); |
139 | 139 | ||
@@ -162,29 +162,29 @@ static inline bool machine__is_host(struct machine *self) | |||
162 | return self ? self->pid == HOST_KERNEL_ID : false; | 162 | return self ? self->pid == HOST_KERNEL_ID : false; |
163 | } | 163 | } |
164 | 164 | ||
165 | static inline void map_groups__insert(struct map_groups *self, struct map *map) | 165 | static inline void map_groups__insert(struct map_groups *mg, struct map *map) |
166 | { | 166 | { |
167 | maps__insert(&self->maps[map->type], map); | 167 | maps__insert(&mg->maps[map->type], map); |
168 | map->groups = self; | 168 | map->groups = mg; |
169 | } | 169 | } |
170 | 170 | ||
171 | static inline void map_groups__remove(struct map_groups *self, struct map *map) | 171 | static inline void map_groups__remove(struct map_groups *mg, struct map *map) |
172 | { | 172 | { |
173 | maps__remove(&self->maps[map->type], map); | 173 | maps__remove(&mg->maps[map->type], map); |
174 | } | 174 | } |
175 | 175 | ||
176 | static inline struct map *map_groups__find(struct map_groups *self, | 176 | static inline struct map *map_groups__find(struct map_groups *mg, |
177 | enum map_type type, u64 addr) | 177 | enum map_type type, u64 addr) |
178 | { | 178 | { |
179 | return maps__find(&self->maps[type], addr); | 179 | return maps__find(&mg->maps[type], addr); |
180 | } | 180 | } |
181 | 181 | ||
182 | struct symbol *map_groups__find_symbol(struct map_groups *self, | 182 | struct symbol *map_groups__find_symbol(struct map_groups *mg, |
183 | enum map_type type, u64 addr, | 183 | enum map_type type, u64 addr, |
184 | struct map **mapp, | 184 | struct map **mapp, |
185 | symbol_filter_t filter); | 185 | symbol_filter_t filter); |
186 | 186 | ||
187 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, | 187 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, |
188 | enum map_type type, | 188 | enum map_type type, |
189 | const char *name, | 189 | const char *name, |
190 | struct map **mapp, | 190 | struct map **mapp, |
@@ -208,11 +208,11 @@ struct symbol *machine__find_kernel_function(struct machine *self, u64 addr, | |||
208 | } | 208 | } |
209 | 209 | ||
210 | static inline | 210 | static inline |
211 | struct symbol *map_groups__find_function_by_name(struct map_groups *self, | 211 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, |
212 | const char *name, struct map **mapp, | 212 | const char *name, struct map **mapp, |
213 | symbol_filter_t filter) | 213 | symbol_filter_t filter) |
214 | { | 214 | { |
215 | return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); | 215 | return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); |
216 | } | 216 | } |
217 | 217 | ||
218 | static inline | 218 | static inline |
@@ -225,13 +225,13 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *self, | |||
225 | filter); | 225 | filter); |
226 | } | 226 | } |
227 | 227 | ||
228 | int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, | 228 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, |
229 | int verbose, FILE *fp); | 229 | int verbose, FILE *fp); |
230 | 230 | ||
231 | struct map *map_groups__find_by_name(struct map_groups *self, | 231 | struct map *map_groups__find_by_name(struct map_groups *mg, |
232 | enum map_type type, const char *name); | 232 | enum map_type type, const char *name); |
233 | struct map *machine__new_module(struct machine *self, u64 start, const char *filename); | 233 | struct map *machine__new_module(struct machine *self, u64 start, const char *filename); |
234 | 234 | ||
235 | void map_groups__flush(struct map_groups *self); | 235 | void map_groups__flush(struct map_groups *mg); |
236 | 236 | ||
237 | #endif /* __PERF_MAP_H */ | 237 | #endif /* __PERF_MAP_H */ |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 41982c373faf..928918b796b2 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -86,22 +86,24 @@ static const char *sw_event_names[PERF_COUNT_SW_MAX] = { | |||
86 | 86 | ||
87 | #define MAX_ALIASES 8 | 87 | #define MAX_ALIASES 8 |
88 | 88 | ||
89 | static const char *hw_cache[][MAX_ALIASES] = { | 89 | static const char *hw_cache[PERF_COUNT_HW_CACHE_MAX][MAX_ALIASES] = { |
90 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, | 90 | { "L1-dcache", "l1-d", "l1d", "L1-data", }, |
91 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, | 91 | { "L1-icache", "l1-i", "l1i", "L1-instruction", }, |
92 | { "LLC", "L2" }, | 92 | { "LLC", "L2", }, |
93 | { "dTLB", "d-tlb", "Data-TLB", }, | 93 | { "dTLB", "d-tlb", "Data-TLB", }, |
94 | { "iTLB", "i-tlb", "Instruction-TLB", }, | 94 | { "iTLB", "i-tlb", "Instruction-TLB", }, |
95 | { "branch", "branches", "bpu", "btb", "bpc", }, | 95 | { "branch", "branches", "bpu", "btb", "bpc", }, |
96 | { "node", }, | ||
96 | }; | 97 | }; |
97 | 98 | ||
98 | static const char *hw_cache_op[][MAX_ALIASES] = { | 99 | static const char *hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX][MAX_ALIASES] = { |
99 | { "load", "loads", "read", }, | 100 | { "load", "loads", "read", }, |
100 | { "store", "stores", "write", }, | 101 | { "store", "stores", "write", }, |
101 | { "prefetch", "prefetches", "speculative-read", "speculative-load", }, | 102 | { "prefetch", "prefetches", "speculative-read", "speculative-load", }, |
102 | }; | 103 | }; |
103 | 104 | ||
104 | static const char *hw_cache_result[][MAX_ALIASES] = { | 105 | static const char *hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX] |
106 | [MAX_ALIASES] = { | ||
105 | { "refs", "Reference", "ops", "access", }, | 107 | { "refs", "Reference", "ops", "access", }, |
106 | { "misses", "miss", }, | 108 | { "misses", "miss", }, |
107 | }; | 109 | }; |
@@ -124,6 +126,7 @@ static unsigned long hw_cache_stat[C(MAX)] = { | |||
124 | [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | 126 | [C(DTLB)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), |
125 | [C(ITLB)] = (CACHE_READ), | 127 | [C(ITLB)] = (CACHE_READ), |
126 | [C(BPU)] = (CACHE_READ), | 128 | [C(BPU)] = (CACHE_READ), |
129 | [C(NODE)] = (CACHE_READ | CACHE_WRITE | CACHE_PREFETCH), | ||
127 | }; | 130 | }; |
128 | 131 | ||
129 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ | 132 | #define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ |
@@ -393,7 +396,7 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | |||
393 | PERF_COUNT_HW_CACHE_OP_MAX); | 396 | PERF_COUNT_HW_CACHE_OP_MAX); |
394 | if (cache_op >= 0) { | 397 | if (cache_op >= 0) { |
395 | if (!is_cache_op_valid(cache_type, cache_op)) | 398 | if (!is_cache_op_valid(cache_type, cache_op)) |
396 | return 0; | 399 | return EVT_FAILED; |
397 | continue; | 400 | continue; |
398 | } | 401 | } |
399 | } | 402 | } |
@@ -475,7 +478,7 @@ parse_single_tracepoint_event(char *sys_name, | |||
475 | /* sys + ':' + event + ':' + flags*/ | 478 | /* sys + ':' + event + ':' + flags*/ |
476 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 479 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) |
477 | static enum event_result | 480 | static enum event_result |
478 | parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, | 481 | parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name, |
479 | const char *evt_exp, char *flags) | 482 | const char *evt_exp, char *flags) |
480 | { | 483 | { |
481 | char evt_path[MAXPATHLEN]; | 484 | char evt_path[MAXPATHLEN]; |
@@ -509,7 +512,7 @@ parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, | |||
509 | if (len < 0) | 512 | if (len < 0) |
510 | return EVT_FAILED; | 513 | return EVT_FAILED; |
511 | 514 | ||
512 | if (parse_events(opt, event_opt, 0)) | 515 | if (parse_events(evlist, event_opt, 0)) |
513 | return EVT_FAILED; | 516 | return EVT_FAILED; |
514 | } | 517 | } |
515 | 518 | ||
@@ -517,7 +520,7 @@ parse_multiple_tracepoint_event(const struct option *opt, char *sys_name, | |||
517 | } | 520 | } |
518 | 521 | ||
519 | static enum event_result | 522 | static enum event_result |
520 | parse_tracepoint_event(const struct option *opt, const char **strp, | 523 | parse_tracepoint_event(struct perf_evlist *evlist, const char **strp, |
521 | struct perf_event_attr *attr) | 524 | struct perf_event_attr *attr) |
522 | { | 525 | { |
523 | const char *evt_name; | 526 | const char *evt_name; |
@@ -557,8 +560,8 @@ parse_tracepoint_event(const struct option *opt, const char **strp, | |||
557 | return EVT_FAILED; | 560 | return EVT_FAILED; |
558 | if (strpbrk(evt_name, "*?")) { | 561 | if (strpbrk(evt_name, "*?")) { |
559 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | 562 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ |
560 | return parse_multiple_tracepoint_event(opt, sys_name, evt_name, | 563 | return parse_multiple_tracepoint_event(evlist, sys_name, |
561 | flags); | 564 | evt_name, flags); |
562 | } else { | 565 | } else { |
563 | return parse_single_tracepoint_event(sys_name, evt_name, | 566 | return parse_single_tracepoint_event(sys_name, evt_name, |
564 | evt_length, attr, strp); | 567 | evt_length, attr, strp); |
@@ -694,7 +697,11 @@ parse_raw_event(const char **strp, struct perf_event_attr *attr) | |||
694 | return EVT_FAILED; | 697 | return EVT_FAILED; |
695 | n = hex2u64(str + 1, &config); | 698 | n = hex2u64(str + 1, &config); |
696 | if (n > 0) { | 699 | if (n > 0) { |
697 | *strp = str + n + 1; | 700 | const char *end = str + n + 1; |
701 | if (*end != '\0' && *end != ',' && *end != ':') | ||
702 | return EVT_FAILED; | ||
703 | |||
704 | *strp = end; | ||
698 | attr->type = PERF_TYPE_RAW; | 705 | attr->type = PERF_TYPE_RAW; |
699 | attr->config = config; | 706 | attr->config = config; |
700 | return EVT_HANDLED; | 707 | return EVT_HANDLED; |
@@ -778,12 +785,12 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
778 | * Symbolic names are (almost) exactly matched. | 785 | * Symbolic names are (almost) exactly matched. |
779 | */ | 786 | */ |
780 | static enum event_result | 787 | static enum event_result |
781 | parse_event_symbols(const struct option *opt, const char **str, | 788 | parse_event_symbols(struct perf_evlist *evlist, const char **str, |
782 | struct perf_event_attr *attr) | 789 | struct perf_event_attr *attr) |
783 | { | 790 | { |
784 | enum event_result ret; | 791 | enum event_result ret; |
785 | 792 | ||
786 | ret = parse_tracepoint_event(opt, str, attr); | 793 | ret = parse_tracepoint_event(evlist, str, attr); |
787 | if (ret != EVT_FAILED) | 794 | if (ret != EVT_FAILED) |
788 | goto modifier; | 795 | goto modifier; |
789 | 796 | ||
@@ -822,9 +829,8 @@ modifier: | |||
822 | return ret; | 829 | return ret; |
823 | } | 830 | } |
824 | 831 | ||
825 | int parse_events(const struct option *opt, const char *str, int unset __used) | 832 | int parse_events(struct perf_evlist *evlist , const char *str, int unset __used) |
826 | { | 833 | { |
827 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
828 | struct perf_event_attr attr; | 834 | struct perf_event_attr attr; |
829 | enum event_result ret; | 835 | enum event_result ret; |
830 | const char *ostr; | 836 | const char *ostr; |
@@ -832,7 +838,7 @@ int parse_events(const struct option *opt, const char *str, int unset __used) | |||
832 | for (;;) { | 838 | for (;;) { |
833 | ostr = str; | 839 | ostr = str; |
834 | memset(&attr, 0, sizeof(attr)); | 840 | memset(&attr, 0, sizeof(attr)); |
835 | ret = parse_event_symbols(opt, &str, &attr); | 841 | ret = parse_event_symbols(evlist, &str, &attr); |
836 | if (ret == EVT_FAILED) | 842 | if (ret == EVT_FAILED) |
837 | return -1; | 843 | return -1; |
838 | 844 | ||
@@ -863,6 +869,13 @@ int parse_events(const struct option *opt, const char *str, int unset __used) | |||
863 | return 0; | 869 | return 0; |
864 | } | 870 | } |
865 | 871 | ||
872 | int parse_events_option(const struct option *opt, const char *str, | ||
873 | int unset __used) | ||
874 | { | ||
875 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | ||
876 | return parse_events(evlist, str, unset); | ||
877 | } | ||
878 | |||
866 | int parse_filter(const struct option *opt, const char *str, | 879 | int parse_filter(const struct option *opt, const char *str, |
867 | int unset __used) | 880 | int unset __used) |
868 | { | 881 | { |
@@ -1088,6 +1101,4 @@ void print_events(const char *event_glob) | |||
1088 | printf("\n"); | 1101 | printf("\n"); |
1089 | 1102 | ||
1090 | print_tracepoint_events(NULL, NULL); | 1103 | print_tracepoint_events(NULL, NULL); |
1091 | |||
1092 | exit(129); | ||
1093 | } | 1104 | } |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 746d3fcbfc2a..2f8e375e038d 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -8,6 +8,7 @@ | |||
8 | 8 | ||
9 | struct list_head; | 9 | struct list_head; |
10 | struct perf_evsel; | 10 | struct perf_evsel; |
11 | struct perf_evlist; | ||
11 | 12 | ||
12 | struct option; | 13 | struct option; |
13 | 14 | ||
@@ -24,7 +25,10 @@ const char *event_type(int type); | |||
24 | const char *event_name(struct perf_evsel *event); | 25 | const char *event_name(struct perf_evsel *event); |
25 | extern const char *__event_name(int type, u64 config); | 26 | extern const char *__event_name(int type, u64 config); |
26 | 27 | ||
27 | extern int parse_events(const struct option *opt, const char *str, int unset); | 28 | extern int parse_events_option(const struct option *opt, const char *str, |
29 | int unset); | ||
30 | extern int parse_events(struct perf_evlist *evlist, const char *str, | ||
31 | int unset); | ||
28 | extern int parse_filter(const struct option *opt, const char *str, int unset); | 32 | extern int parse_filter(const struct option *opt, const char *str, int unset); |
29 | 33 | ||
30 | #define EVENTS_HELP_MAX (128*1024) | 34 | #define EVENTS_HELP_MAX (128*1024) |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index f0223166e761..eb25900e2211 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -117,6 +117,10 @@ static struct map *kernel_get_module_map(const char *module) | |||
117 | struct rb_node *nd; | 117 | struct rb_node *nd; |
118 | struct map_groups *grp = &machine.kmaps; | 118 | struct map_groups *grp = &machine.kmaps; |
119 | 119 | ||
120 | /* A file path -- this is an offline module */ | ||
121 | if (module && strchr(module, '/')) | ||
122 | return machine__new_module(&machine, 0, module); | ||
123 | |||
120 | if (!module) | 124 | if (!module) |
121 | module = "kernel"; | 125 | module = "kernel"; |
122 | 126 | ||
@@ -170,16 +174,24 @@ const char *kernel_get_module_path(const char *module) | |||
170 | } | 174 | } |
171 | 175 | ||
172 | #ifdef DWARF_SUPPORT | 176 | #ifdef DWARF_SUPPORT |
173 | static int open_vmlinux(const char *module) | 177 | /* Open new debuginfo of given module */ |
178 | static struct debuginfo *open_debuginfo(const char *module) | ||
174 | { | 179 | { |
175 | const char *path = kernel_get_module_path(module); | 180 | const char *path; |
176 | if (!path) { | 181 | |
177 | pr_err("Failed to find path of %s module.\n", | 182 | /* A file path -- this is an offline module */ |
178 | module ?: "kernel"); | 183 | if (module && strchr(module, '/')) |
179 | return -ENOENT; | 184 | path = module; |
185 | else { | ||
186 | path = kernel_get_module_path(module); | ||
187 | |||
188 | if (!path) { | ||
189 | pr_err("Failed to find path of %s module.\n", | ||
190 | module ?: "kernel"); | ||
191 | return NULL; | ||
192 | } | ||
180 | } | 193 | } |
181 | pr_debug("Try to open %s\n", path); | 194 | return debuginfo__new(path); |
182 | return open(path, O_RDONLY); | ||
183 | } | 195 | } |
184 | 196 | ||
185 | /* | 197 | /* |
@@ -193,13 +205,24 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
193 | struct map *map; | 205 | struct map *map; |
194 | u64 addr; | 206 | u64 addr; |
195 | int ret = -ENOENT; | 207 | int ret = -ENOENT; |
208 | struct debuginfo *dinfo; | ||
196 | 209 | ||
197 | sym = __find_kernel_function_by_name(tp->symbol, &map); | 210 | sym = __find_kernel_function_by_name(tp->symbol, &map); |
198 | if (sym) { | 211 | if (sym) { |
199 | addr = map->unmap_ip(map, sym->start + tp->offset); | 212 | addr = map->unmap_ip(map, sym->start + tp->offset); |
200 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, | 213 | pr_debug("try to find %s+%ld@%" PRIx64 "\n", tp->symbol, |
201 | tp->offset, addr); | 214 | tp->offset, addr); |
202 | ret = find_perf_probe_point((unsigned long)addr, pp); | 215 | |
216 | dinfo = debuginfo__new_online_kernel(addr); | ||
217 | if (dinfo) { | ||
218 | ret = debuginfo__find_probe_point(dinfo, | ||
219 | (unsigned long)addr, pp); | ||
220 | debuginfo__delete(dinfo); | ||
221 | } else { | ||
222 | pr_debug("Failed to open debuginfo at 0x%" PRIx64 "\n", | ||
223 | addr); | ||
224 | ret = -ENOENT; | ||
225 | } | ||
203 | } | 226 | } |
204 | if (ret <= 0) { | 227 | if (ret <= 0) { |
205 | pr_debug("Failed to find corresponding probes from " | 228 | pr_debug("Failed to find corresponding probes from " |
@@ -214,30 +237,70 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, | |||
214 | return 0; | 237 | return 0; |
215 | } | 238 | } |
216 | 239 | ||
240 | static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, | ||
241 | int ntevs, const char *module) | ||
242 | { | ||
243 | int i, ret = 0; | ||
244 | char *tmp; | ||
245 | |||
246 | if (!module) | ||
247 | return 0; | ||
248 | |||
249 | tmp = strrchr(module, '/'); | ||
250 | if (tmp) { | ||
251 | /* This is a module path -- get the module name */ | ||
252 | module = strdup(tmp + 1); | ||
253 | if (!module) | ||
254 | return -ENOMEM; | ||
255 | tmp = strchr(module, '.'); | ||
256 | if (tmp) | ||
257 | *tmp = '\0'; | ||
258 | tmp = (char *)module; /* For free() */ | ||
259 | } | ||
260 | |||
261 | for (i = 0; i < ntevs; i++) { | ||
262 | tevs[i].point.module = strdup(module); | ||
263 | if (!tevs[i].point.module) { | ||
264 | ret = -ENOMEM; | ||
265 | break; | ||
266 | } | ||
267 | } | ||
268 | |||
269 | if (tmp) | ||
270 | free(tmp); | ||
271 | |||
272 | return ret; | ||
273 | } | ||
274 | |||
217 | /* Try to find perf_probe_event with debuginfo */ | 275 | /* Try to find perf_probe_event with debuginfo */ |
218 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 276 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
219 | struct probe_trace_event **tevs, | 277 | struct probe_trace_event **tevs, |
220 | int max_tevs, const char *module) | 278 | int max_tevs, const char *module) |
221 | { | 279 | { |
222 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 280 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
223 | int fd, ntevs; | 281 | struct debuginfo *dinfo = open_debuginfo(module); |
282 | int ntevs, ret = 0; | ||
224 | 283 | ||
225 | fd = open_vmlinux(module); | 284 | if (!dinfo) { |
226 | if (fd < 0) { | ||
227 | if (need_dwarf) { | 285 | if (need_dwarf) { |
228 | pr_warning("Failed to open debuginfo file.\n"); | 286 | pr_warning("Failed to open debuginfo file.\n"); |
229 | return fd; | 287 | return -ENOENT; |
230 | } | 288 | } |
231 | pr_debug("Could not open vmlinux. Try to use symbols.\n"); | 289 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); |
232 | return 0; | 290 | return 0; |
233 | } | 291 | } |
234 | 292 | ||
235 | /* Searching trace events corresponding to probe event */ | 293 | /* Searching trace events corresponding to a probe event */ |
236 | ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs); | 294 | ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); |
295 | |||
296 | debuginfo__delete(dinfo); | ||
237 | 297 | ||
238 | if (ntevs > 0) { /* Succeeded to find trace events */ | 298 | if (ntevs > 0) { /* Succeeded to find trace events */ |
239 | pr_debug("find %d probe_trace_events.\n", ntevs); | 299 | pr_debug("find %d probe_trace_events.\n", ntevs); |
240 | return ntevs; | 300 | if (module) |
301 | ret = add_module_to_probe_trace_events(*tevs, ntevs, | ||
302 | module); | ||
303 | return ret < 0 ? ret : ntevs; | ||
241 | } | 304 | } |
242 | 305 | ||
243 | if (ntevs == 0) { /* No error but failed to find probe point. */ | 306 | if (ntevs == 0) { /* No error but failed to find probe point. */ |
@@ -371,8 +434,9 @@ int show_line_range(struct line_range *lr, const char *module) | |||
371 | { | 434 | { |
372 | int l = 1; | 435 | int l = 1; |
373 | struct line_node *ln; | 436 | struct line_node *ln; |
437 | struct debuginfo *dinfo; | ||
374 | FILE *fp; | 438 | FILE *fp; |
375 | int fd, ret; | 439 | int ret; |
376 | char *tmp; | 440 | char *tmp; |
377 | 441 | ||
378 | /* Search a line range */ | 442 | /* Search a line range */ |
@@ -380,13 +444,14 @@ int show_line_range(struct line_range *lr, const char *module) | |||
380 | if (ret < 0) | 444 | if (ret < 0) |
381 | return ret; | 445 | return ret; |
382 | 446 | ||
383 | fd = open_vmlinux(module); | 447 | dinfo = open_debuginfo(module); |
384 | if (fd < 0) { | 448 | if (!dinfo) { |
385 | pr_warning("Failed to open debuginfo file.\n"); | 449 | pr_warning("Failed to open debuginfo file.\n"); |
386 | return fd; | 450 | return -ENOENT; |
387 | } | 451 | } |
388 | 452 | ||
389 | ret = find_line_range(fd, lr); | 453 | ret = debuginfo__find_line_range(dinfo, lr); |
454 | debuginfo__delete(dinfo); | ||
390 | if (ret == 0) { | 455 | if (ret == 0) { |
391 | pr_warning("Specified source line is not found.\n"); | 456 | pr_warning("Specified source line is not found.\n"); |
392 | return -ENOENT; | 457 | return -ENOENT; |
@@ -448,7 +513,8 @@ end: | |||
448 | return ret; | 513 | return ret; |
449 | } | 514 | } |
450 | 515 | ||
451 | static int show_available_vars_at(int fd, struct perf_probe_event *pev, | 516 | static int show_available_vars_at(struct debuginfo *dinfo, |
517 | struct perf_probe_event *pev, | ||
452 | int max_vls, struct strfilter *_filter, | 518 | int max_vls, struct strfilter *_filter, |
453 | bool externs) | 519 | bool externs) |
454 | { | 520 | { |
@@ -463,7 +529,8 @@ static int show_available_vars_at(int fd, struct perf_probe_event *pev, | |||
463 | return -EINVAL; | 529 | return -EINVAL; |
464 | pr_debug("Searching variables at %s\n", buf); | 530 | pr_debug("Searching variables at %s\n", buf); |
465 | 531 | ||
466 | ret = find_available_vars_at(fd, pev, &vls, max_vls, externs); | 532 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, |
533 | max_vls, externs); | ||
467 | if (ret <= 0) { | 534 | if (ret <= 0) { |
468 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); | 535 | pr_err("Failed to find variables at %s (%d)\n", buf, ret); |
469 | goto end; | 536 | goto end; |
@@ -504,24 +571,26 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs, | |||
504 | int max_vls, const char *module, | 571 | int max_vls, const char *module, |
505 | struct strfilter *_filter, bool externs) | 572 | struct strfilter *_filter, bool externs) |
506 | { | 573 | { |
507 | int i, fd, ret = 0; | 574 | int i, ret = 0; |
575 | struct debuginfo *dinfo; | ||
508 | 576 | ||
509 | ret = init_vmlinux(); | 577 | ret = init_vmlinux(); |
510 | if (ret < 0) | 578 | if (ret < 0) |
511 | return ret; | 579 | return ret; |
512 | 580 | ||
581 | dinfo = open_debuginfo(module); | ||
582 | if (!dinfo) { | ||
583 | pr_warning("Failed to open debuginfo file.\n"); | ||
584 | return -ENOENT; | ||
585 | } | ||
586 | |||
513 | setup_pager(); | 587 | setup_pager(); |
514 | 588 | ||
515 | for (i = 0; i < npevs && ret >= 0; i++) { | 589 | for (i = 0; i < npevs && ret >= 0; i++) |
516 | fd = open_vmlinux(module); | 590 | ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, |
517 | if (fd < 0) { | ||
518 | pr_warning("Failed to open debug information file.\n"); | ||
519 | ret = fd; | ||
520 | break; | ||
521 | } | ||
522 | ret = show_available_vars_at(fd, &pevs[i], max_vls, _filter, | ||
523 | externs); | 591 | externs); |
524 | } | 592 | |
593 | debuginfo__delete(dinfo); | ||
525 | return ret; | 594 | return ret; |
526 | } | 595 | } |
527 | 596 | ||
@@ -990,7 +1059,7 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) | |||
990 | 1059 | ||
991 | /* Parse probe_events event into struct probe_point */ | 1060 | /* Parse probe_events event into struct probe_point */ |
992 | static int parse_probe_trace_command(const char *cmd, | 1061 | static int parse_probe_trace_command(const char *cmd, |
993 | struct probe_trace_event *tev) | 1062 | struct probe_trace_event *tev) |
994 | { | 1063 | { |
995 | struct probe_trace_point *tp = &tev->point; | 1064 | struct probe_trace_point *tp = &tev->point; |
996 | char pr; | 1065 | char pr; |
@@ -1023,8 +1092,14 @@ static int parse_probe_trace_command(const char *cmd, | |||
1023 | 1092 | ||
1024 | tp->retprobe = (pr == 'r'); | 1093 | tp->retprobe = (pr == 'r'); |
1025 | 1094 | ||
1026 | /* Scan function name and offset */ | 1095 | /* Scan module name(if there), function name and offset */ |
1027 | ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol, | 1096 | p = strchr(argv[1], ':'); |
1097 | if (p) { | ||
1098 | tp->module = strndup(argv[1], p - argv[1]); | ||
1099 | p++; | ||
1100 | } else | ||
1101 | p = argv[1]; | ||
1102 | ret = sscanf(p, "%a[^+]+%lu", (float *)(void *)&tp->symbol, | ||
1028 | &tp->offset); | 1103 | &tp->offset); |
1029 | if (ret == 1) | 1104 | if (ret == 1) |
1030 | tp->offset = 0; | 1105 | tp->offset = 0; |
@@ -1269,9 +1344,10 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev) | |||
1269 | if (buf == NULL) | 1344 | if (buf == NULL) |
1270 | return NULL; | 1345 | return NULL; |
1271 | 1346 | ||
1272 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", | 1347 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s%s%s+%lu", |
1273 | tp->retprobe ? 'r' : 'p', | 1348 | tp->retprobe ? 'r' : 'p', |
1274 | tev->group, tev->event, | 1349 | tev->group, tev->event, |
1350 | tp->module ?: "", tp->module ? ":" : "", | ||
1275 | tp->symbol, tp->offset); | 1351 | tp->symbol, tp->offset); |
1276 | if (len <= 0) | 1352 | if (len <= 0) |
1277 | goto error; | 1353 | goto error; |
@@ -1378,6 +1454,8 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) | |||
1378 | free(tev->group); | 1454 | free(tev->group); |
1379 | if (tev->point.symbol) | 1455 | if (tev->point.symbol) |
1380 | free(tev->point.symbol); | 1456 | free(tev->point.symbol); |
1457 | if (tev->point.module) | ||
1458 | free(tev->point.module); | ||
1381 | for (i = 0; i < tev->nargs; i++) { | 1459 | for (i = 0; i < tev->nargs; i++) { |
1382 | if (tev->args[i].name) | 1460 | if (tev->args[i].name) |
1383 | free(tev->args[i].name); | 1461 | free(tev->args[i].name); |
@@ -1729,7 +1807,7 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
1729 | /* Convert perf_probe_event with debuginfo */ | 1807 | /* Convert perf_probe_event with debuginfo */ |
1730 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); | 1808 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); |
1731 | if (ret != 0) | 1809 | if (ret != 0) |
1732 | return ret; | 1810 | return ret; /* Found in debuginfo or got an error */ |
1733 | 1811 | ||
1734 | /* Allocate trace event buffer */ | 1812 | /* Allocate trace event buffer */ |
1735 | tev = *tevs = zalloc(sizeof(struct probe_trace_event)); | 1813 | tev = *tevs = zalloc(sizeof(struct probe_trace_event)); |
@@ -1742,6 +1820,15 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
1742 | ret = -ENOMEM; | 1820 | ret = -ENOMEM; |
1743 | goto error; | 1821 | goto error; |
1744 | } | 1822 | } |
1823 | |||
1824 | if (module) { | ||
1825 | tev->point.module = strdup(module); | ||
1826 | if (tev->point.module == NULL) { | ||
1827 | ret = -ENOMEM; | ||
1828 | goto error; | ||
1829 | } | ||
1830 | } | ||
1831 | |||
1745 | tev->point.offset = pev->point.offset; | 1832 | tev->point.offset = pev->point.offset; |
1746 | tev->point.retprobe = pev->point.retprobe; | 1833 | tev->point.retprobe = pev->point.retprobe; |
1747 | tev->nargs = pev->nargs; | 1834 | tev->nargs = pev->nargs; |
@@ -1869,8 +1956,10 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) | |||
1869 | 1956 | ||
1870 | pr_debug("Writing event: %s\n", buf); | 1957 | pr_debug("Writing event: %s\n", buf); |
1871 | ret = write(fd, buf, strlen(buf)); | 1958 | ret = write(fd, buf, strlen(buf)); |
1872 | if (ret < 0) | 1959 | if (ret < 0) { |
1960 | ret = -errno; | ||
1873 | goto error; | 1961 | goto error; |
1962 | } | ||
1874 | 1963 | ||
1875 | printf("Remove event: %s\n", ent->s); | 1964 | printf("Remove event: %s\n", ent->s); |
1876 | return 0; | 1965 | return 0; |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 3434fc9d79d5..a7dee835f49c 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -10,6 +10,7 @@ extern bool probe_event_dry_run; | |||
10 | /* kprobe-tracer tracing point */ | 10 | /* kprobe-tracer tracing point */ |
11 | struct probe_trace_point { | 11 | struct probe_trace_point { |
12 | char *symbol; /* Base symbol */ | 12 | char *symbol; /* Base symbol */ |
13 | char *module; /* Module name */ | ||
13 | unsigned long offset; /* Offset from symbol */ | 14 | unsigned long offset; /* Offset from symbol */ |
14 | bool retprobe; /* Return probe flag */ | 15 | bool retprobe; /* Return probe flag */ |
15 | }; | 16 | }; |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 3b9d0b800d5c..5d732621a462 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -43,21 +43,6 @@ | |||
43 | /* Kprobe tracer basic type is up to u64 */ | 43 | /* Kprobe tracer basic type is up to u64 */ |
44 | #define MAX_BASIC_TYPE_BITS 64 | 44 | #define MAX_BASIC_TYPE_BITS 64 |
45 | 45 | ||
46 | /* | ||
47 | * Compare the tail of two strings. | ||
48 | * Return 0 if whole of either string is same as another's tail part. | ||
49 | */ | ||
50 | static int strtailcmp(const char *s1, const char *s2) | ||
51 | { | ||
52 | int i1 = strlen(s1); | ||
53 | int i2 = strlen(s2); | ||
54 | while (--i1 >= 0 && --i2 >= 0) { | ||
55 | if (s1[i1] != s2[i2]) | ||
56 | return s1[i1] - s2[i2]; | ||
57 | } | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | /* Line number list operations */ | 46 | /* Line number list operations */ |
62 | 47 | ||
63 | /* Add a line to line number list */ | 48 | /* Add a line to line number list */ |
@@ -131,29 +116,37 @@ static const Dwfl_Callbacks offline_callbacks = { | |||
131 | }; | 116 | }; |
132 | 117 | ||
133 | /* Get a Dwarf from offline image */ | 118 | /* Get a Dwarf from offline image */ |
134 | static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias) | 119 | static int debuginfo__init_offline_dwarf(struct debuginfo *self, |
120 | const char *path) | ||
135 | { | 121 | { |
136 | Dwfl_Module *mod; | 122 | Dwfl_Module *mod; |
137 | Dwarf *dbg = NULL; | 123 | int fd; |
138 | 124 | ||
139 | if (!dwflp) | 125 | fd = open(path, O_RDONLY); |
140 | return NULL; | 126 | if (fd < 0) |
127 | return fd; | ||
141 | 128 | ||
142 | *dwflp = dwfl_begin(&offline_callbacks); | 129 | self->dwfl = dwfl_begin(&offline_callbacks); |
143 | if (!*dwflp) | 130 | if (!self->dwfl) |
144 | return NULL; | 131 | goto error; |
145 | 132 | ||
146 | mod = dwfl_report_offline(*dwflp, "", "", fd); | 133 | mod = dwfl_report_offline(self->dwfl, "", "", fd); |
147 | if (!mod) | 134 | if (!mod) |
148 | goto error; | 135 | goto error; |
149 | 136 | ||
150 | dbg = dwfl_module_getdwarf(mod, bias); | 137 | self->dbg = dwfl_module_getdwarf(mod, &self->bias); |
151 | if (!dbg) { | 138 | if (!self->dbg) |
139 | goto error; | ||
140 | |||
141 | return 0; | ||
152 | error: | 142 | error: |
153 | dwfl_end(*dwflp); | 143 | if (self->dwfl) |
154 | *dwflp = NULL; | 144 | dwfl_end(self->dwfl); |
155 | } | 145 | else |
156 | return dbg; | 146 | close(fd); |
147 | memset(self, 0, sizeof(*self)); | ||
148 | |||
149 | return -ENOENT; | ||
157 | } | 150 | } |
158 | 151 | ||
159 | #if _ELFUTILS_PREREQ(0, 148) | 152 | #if _ELFUTILS_PREREQ(0, 148) |
@@ -189,597 +182,81 @@ static const Dwfl_Callbacks kernel_callbacks = { | |||
189 | }; | 182 | }; |
190 | 183 | ||
191 | /* Get a Dwarf from live kernel image */ | 184 | /* Get a Dwarf from live kernel image */ |
192 | static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp, | 185 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, |
193 | Dwarf_Addr *bias) | 186 | Dwarf_Addr addr) |
194 | { | 187 | { |
195 | Dwarf *dbg; | 188 | self->dwfl = dwfl_begin(&kernel_callbacks); |
196 | 189 | if (!self->dwfl) | |
197 | if (!dwflp) | 190 | return -EINVAL; |
198 | return NULL; | ||
199 | |||
200 | *dwflp = dwfl_begin(&kernel_callbacks); | ||
201 | if (!*dwflp) | ||
202 | return NULL; | ||
203 | 191 | ||
204 | /* Load the kernel dwarves: Don't care the result here */ | 192 | /* Load the kernel dwarves: Don't care the result here */ |
205 | dwfl_linux_kernel_report_kernel(*dwflp); | 193 | dwfl_linux_kernel_report_kernel(self->dwfl); |
206 | dwfl_linux_kernel_report_modules(*dwflp); | 194 | dwfl_linux_kernel_report_modules(self->dwfl); |
207 | 195 | ||
208 | dbg = dwfl_addrdwarf(*dwflp, addr, bias); | 196 | self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); |
209 | /* Here, check whether we could get a real dwarf */ | 197 | /* Here, check whether we could get a real dwarf */ |
210 | if (!dbg) { | 198 | if (!self->dbg) { |
211 | pr_debug("Failed to find kernel dwarf at %lx\n", | 199 | pr_debug("Failed to find kernel dwarf at %lx\n", |
212 | (unsigned long)addr); | 200 | (unsigned long)addr); |
213 | dwfl_end(*dwflp); | 201 | dwfl_end(self->dwfl); |
214 | *dwflp = NULL; | 202 | memset(self, 0, sizeof(*self)); |
203 | return -ENOENT; | ||
215 | } | 204 | } |
216 | return dbg; | 205 | |
206 | return 0; | ||
217 | } | 207 | } |
218 | #else | 208 | #else |
219 | /* With older elfutils, this just support kernel module... */ | 209 | /* With older elfutils, this just support kernel module... */ |
220 | static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp, | 210 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, |
221 | Dwarf_Addr *bias) | 211 | Dwarf_Addr addr __used) |
222 | { | 212 | { |
223 | int fd; | ||
224 | const char *path = kernel_get_module_path("kernel"); | 213 | const char *path = kernel_get_module_path("kernel"); |
225 | 214 | ||
226 | if (!path) { | 215 | if (!path) { |
227 | pr_err("Failed to find vmlinux path\n"); | 216 | pr_err("Failed to find vmlinux path\n"); |
228 | return NULL; | 217 | return -ENOENT; |
229 | } | 218 | } |
230 | 219 | ||
231 | pr_debug2("Use file %s for debuginfo\n", path); | 220 | pr_debug2("Use file %s for debuginfo\n", path); |
232 | fd = open(path, O_RDONLY); | 221 | return debuginfo__init_offline_dwarf(self, path); |
233 | if (fd < 0) | ||
234 | return NULL; | ||
235 | |||
236 | return dwfl_init_offline_dwarf(fd, dwflp, bias); | ||
237 | } | 222 | } |
238 | #endif | 223 | #endif |
239 | 224 | ||
240 | /* Dwarf wrappers */ | 225 | struct debuginfo *debuginfo__new(const char *path) |
241 | |||
242 | /* Find the realpath of the target file. */ | ||
243 | static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
244 | { | 226 | { |
245 | Dwarf_Files *files; | 227 | struct debuginfo *self = zalloc(sizeof(struct debuginfo)); |
246 | size_t nfiles, i; | 228 | if (!self) |
247 | const char *src = NULL; | ||
248 | int ret; | ||
249 | |||
250 | if (!fname) | ||
251 | return NULL; | 229 | return NULL; |
252 | 230 | ||
253 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); | 231 | if (debuginfo__init_offline_dwarf(self, path) < 0) { |
254 | if (ret != 0) | 232 | free(self); |
255 | return NULL; | 233 | self = NULL; |
256 | |||
257 | for (i = 0; i < nfiles; i++) { | ||
258 | src = dwarf_filesrc(files, i, NULL, NULL); | ||
259 | if (strtailcmp(src, fname) == 0) | ||
260 | break; | ||
261 | } | 234 | } |
262 | if (i == nfiles) | ||
263 | return NULL; | ||
264 | return src; | ||
265 | } | ||
266 | 235 | ||
267 | /* Get DW_AT_comp_dir (should be NULL with older gcc) */ | 236 | return self; |
268 | static const char *cu_get_comp_dir(Dwarf_Die *cu_die) | ||
269 | { | ||
270 | Dwarf_Attribute attr; | ||
271 | if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) | ||
272 | return NULL; | ||
273 | return dwarf_formstring(&attr); | ||
274 | } | 237 | } |
275 | 238 | ||
276 | /* Get a line number and file name for given address */ | 239 | struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) |
277 | static int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | ||
278 | const char **fname, int *lineno) | ||
279 | { | 240 | { |
280 | Dwarf_Line *line; | 241 | struct debuginfo *self = zalloc(sizeof(struct debuginfo)); |
281 | Dwarf_Addr laddr; | 242 | if (!self) |
282 | |||
283 | line = dwarf_getsrc_die(cudie, (Dwarf_Addr)addr); | ||
284 | if (line && dwarf_lineaddr(line, &laddr) == 0 && | ||
285 | addr == (unsigned long)laddr && dwarf_lineno(line, lineno) == 0) { | ||
286 | *fname = dwarf_linesrc(line, NULL, NULL); | ||
287 | if (!*fname) | ||
288 | /* line number is useless without filename */ | ||
289 | *lineno = 0; | ||
290 | } | ||
291 | |||
292 | return *lineno ?: -ENOENT; | ||
293 | } | ||
294 | |||
295 | /* Compare diename and tname */ | ||
296 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
297 | { | ||
298 | const char *name; | ||
299 | name = dwarf_diename(dw_die); | ||
300 | return name ? (strcmp(tname, name) == 0) : false; | ||
301 | } | ||
302 | |||
303 | /* Get callsite line number of inline-function instance */ | ||
304 | static int die_get_call_lineno(Dwarf_Die *in_die) | ||
305 | { | ||
306 | Dwarf_Attribute attr; | ||
307 | Dwarf_Word ret; | ||
308 | |||
309 | if (!dwarf_attr(in_die, DW_AT_call_line, &attr)) | ||
310 | return -ENOENT; | ||
311 | |||
312 | dwarf_formudata(&attr, &ret); | ||
313 | return (int)ret; | ||
314 | } | ||
315 | |||
316 | /* Get type die */ | ||
317 | static Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
318 | { | ||
319 | Dwarf_Attribute attr; | ||
320 | |||
321 | if (dwarf_attr_integrate(vr_die, DW_AT_type, &attr) && | ||
322 | dwarf_formref_die(&attr, die_mem)) | ||
323 | return die_mem; | ||
324 | else | ||
325 | return NULL; | 243 | return NULL; |
326 | } | ||
327 | |||
328 | /* Get a type die, but skip qualifiers */ | ||
329 | static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
330 | { | ||
331 | int tag; | ||
332 | |||
333 | do { | ||
334 | vr_die = die_get_type(vr_die, die_mem); | ||
335 | if (!vr_die) | ||
336 | break; | ||
337 | tag = dwarf_tag(vr_die); | ||
338 | } while (tag == DW_TAG_const_type || | ||
339 | tag == DW_TAG_restrict_type || | ||
340 | tag == DW_TAG_volatile_type || | ||
341 | tag == DW_TAG_shared_type); | ||
342 | |||
343 | return vr_die; | ||
344 | } | ||
345 | |||
346 | /* Get a type die, but skip qualifiers and typedef */ | ||
347 | static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
348 | { | ||
349 | do { | ||
350 | vr_die = __die_get_real_type(vr_die, die_mem); | ||
351 | } while (vr_die && dwarf_tag(vr_die) == DW_TAG_typedef); | ||
352 | |||
353 | return vr_die; | ||
354 | } | ||
355 | |||
356 | static int die_get_attr_udata(Dwarf_Die *tp_die, unsigned int attr_name, | ||
357 | Dwarf_Word *result) | ||
358 | { | ||
359 | Dwarf_Attribute attr; | ||
360 | |||
361 | if (dwarf_attr(tp_die, attr_name, &attr) == NULL || | ||
362 | dwarf_formudata(&attr, result) != 0) | ||
363 | return -ENOENT; | ||
364 | |||
365 | return 0; | ||
366 | } | ||
367 | 244 | ||
368 | static bool die_is_signed_type(Dwarf_Die *tp_die) | 245 | if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) { |
369 | { | 246 | free(self); |
370 | Dwarf_Word ret; | 247 | self = NULL; |
371 | |||
372 | if (die_get_attr_udata(tp_die, DW_AT_encoding, &ret)) | ||
373 | return false; | ||
374 | |||
375 | return (ret == DW_ATE_signed_char || ret == DW_ATE_signed || | ||
376 | ret == DW_ATE_signed_fixed); | ||
377 | } | ||
378 | |||
379 | static int die_get_byte_size(Dwarf_Die *tp_die) | ||
380 | { | ||
381 | Dwarf_Word ret; | ||
382 | |||
383 | if (die_get_attr_udata(tp_die, DW_AT_byte_size, &ret)) | ||
384 | return 0; | ||
385 | |||
386 | return (int)ret; | ||
387 | } | ||
388 | |||
389 | static int die_get_bit_size(Dwarf_Die *tp_die) | ||
390 | { | ||
391 | Dwarf_Word ret; | ||
392 | |||
393 | if (die_get_attr_udata(tp_die, DW_AT_bit_size, &ret)) | ||
394 | return 0; | ||
395 | |||
396 | return (int)ret; | ||
397 | } | ||
398 | |||
399 | static int die_get_bit_offset(Dwarf_Die *tp_die) | ||
400 | { | ||
401 | Dwarf_Word ret; | ||
402 | |||
403 | if (die_get_attr_udata(tp_die, DW_AT_bit_offset, &ret)) | ||
404 | return 0; | ||
405 | |||
406 | return (int)ret; | ||
407 | } | ||
408 | |||
409 | /* Get data_member_location offset */ | ||
410 | static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs) | ||
411 | { | ||
412 | Dwarf_Attribute attr; | ||
413 | Dwarf_Op *expr; | ||
414 | size_t nexpr; | ||
415 | int ret; | ||
416 | |||
417 | if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL) | ||
418 | return -ENOENT; | ||
419 | |||
420 | if (dwarf_formudata(&attr, offs) != 0) { | ||
421 | /* DW_AT_data_member_location should be DW_OP_plus_uconst */ | ||
422 | ret = dwarf_getlocation(&attr, &expr, &nexpr); | ||
423 | if (ret < 0 || nexpr == 0) | ||
424 | return -ENOENT; | ||
425 | |||
426 | if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) { | ||
427 | pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n", | ||
428 | expr[0].atom, nexpr); | ||
429 | return -ENOTSUP; | ||
430 | } | ||
431 | *offs = (Dwarf_Word)expr[0].number; | ||
432 | } | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /* Return values for die_find callbacks */ | ||
437 | enum { | ||
438 | DIE_FIND_CB_FOUND = 0, /* End of Search */ | ||
439 | DIE_FIND_CB_CHILD = 1, /* Search only children */ | ||
440 | DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ | ||
441 | DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ | ||
442 | }; | ||
443 | |||
444 | /* Search a child die */ | ||
445 | static Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
446 | int (*callback)(Dwarf_Die *, void *), | ||
447 | void *data, Dwarf_Die *die_mem) | ||
448 | { | ||
449 | Dwarf_Die child_die; | ||
450 | int ret; | ||
451 | |||
452 | ret = dwarf_child(rt_die, die_mem); | ||
453 | if (ret != 0) | ||
454 | return NULL; | ||
455 | |||
456 | do { | ||
457 | ret = callback(die_mem, data); | ||
458 | if (ret == DIE_FIND_CB_FOUND) | ||
459 | return die_mem; | ||
460 | |||
461 | if ((ret & DIE_FIND_CB_CHILD) && | ||
462 | die_find_child(die_mem, callback, data, &child_die)) { | ||
463 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
464 | return die_mem; | ||
465 | } | ||
466 | } while ((ret & DIE_FIND_CB_SIBLING) && | ||
467 | dwarf_siblingof(die_mem, die_mem) == 0); | ||
468 | |||
469 | return NULL; | ||
470 | } | ||
471 | |||
472 | struct __addr_die_search_param { | ||
473 | Dwarf_Addr addr; | ||
474 | Dwarf_Die *die_mem; | ||
475 | }; | ||
476 | |||
477 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
478 | { | ||
479 | struct __addr_die_search_param *ad = data; | ||
480 | |||
481 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | ||
482 | dwarf_haspc(fn_die, ad->addr)) { | ||
483 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
484 | return DWARF_CB_ABORT; | ||
485 | } | 248 | } |
486 | return DWARF_CB_OK; | ||
487 | } | ||
488 | 249 | ||
489 | /* Search a real subprogram including this line, */ | 250 | return self; |
490 | static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, | ||
491 | Dwarf_Die *die_mem) | ||
492 | { | ||
493 | struct __addr_die_search_param ad; | ||
494 | ad.addr = addr; | ||
495 | ad.die_mem = die_mem; | ||
496 | /* dwarf_getscopes can't find subprogram. */ | ||
497 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) | ||
498 | return NULL; | ||
499 | else | ||
500 | return die_mem; | ||
501 | } | 251 | } |
502 | 252 | ||
503 | /* die_find callback for inline function search */ | 253 | void debuginfo__delete(struct debuginfo *self) |
504 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) | ||
505 | { | 254 | { |
506 | Dwarf_Addr *addr = data; | 255 | if (self) { |
507 | 256 | if (self->dwfl) | |
508 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | 257 | dwfl_end(self->dwfl); |
509 | dwarf_haspc(die_mem, *addr)) | 258 | free(self); |
510 | return DIE_FIND_CB_FOUND; | ||
511 | |||
512 | return DIE_FIND_CB_CONTINUE; | ||
513 | } | ||
514 | |||
515 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ | ||
516 | static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
517 | Dwarf_Die *die_mem) | ||
518 | { | ||
519 | Dwarf_Die tmp_die; | ||
520 | |||
521 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, &tmp_die); | ||
522 | if (!sp_die) | ||
523 | return NULL; | ||
524 | |||
525 | /* Inlined function could be recursive. Trace it until fail */ | ||
526 | while (sp_die) { | ||
527 | memcpy(die_mem, sp_die, sizeof(Dwarf_Die)); | ||
528 | sp_die = die_find_child(sp_die, __die_find_inline_cb, &addr, | ||
529 | &tmp_die); | ||
530 | } | ||
531 | |||
532 | return die_mem; | ||
533 | } | ||
534 | |||
535 | /* Walker on lines (Note: line number will not be sorted) */ | ||
536 | typedef int (* line_walk_handler_t) (const char *fname, int lineno, | ||
537 | Dwarf_Addr addr, void *data); | ||
538 | |||
539 | struct __line_walk_param { | ||
540 | const char *fname; | ||
541 | line_walk_handler_t handler; | ||
542 | void *data; | ||
543 | int retval; | ||
544 | }; | ||
545 | |||
546 | static int __die_walk_funclines_cb(Dwarf_Die *in_die, void *data) | ||
547 | { | ||
548 | struct __line_walk_param *lw = data; | ||
549 | Dwarf_Addr addr; | ||
550 | int lineno; | ||
551 | |||
552 | if (dwarf_tag(in_die) == DW_TAG_inlined_subroutine) { | ||
553 | lineno = die_get_call_lineno(in_die); | ||
554 | if (lineno > 0 && dwarf_entrypc(in_die, &addr) == 0) { | ||
555 | lw->retval = lw->handler(lw->fname, lineno, addr, | ||
556 | lw->data); | ||
557 | if (lw->retval != 0) | ||
558 | return DIE_FIND_CB_FOUND; | ||
559 | } | ||
560 | } | 259 | } |
561 | return DIE_FIND_CB_SIBLING; | ||
562 | } | ||
563 | |||
564 | /* Walk on lines of blocks included in given DIE */ | ||
565 | static int __die_walk_funclines(Dwarf_Die *sp_die, | ||
566 | line_walk_handler_t handler, void *data) | ||
567 | { | ||
568 | struct __line_walk_param lw = { | ||
569 | .handler = handler, | ||
570 | .data = data, | ||
571 | .retval = 0, | ||
572 | }; | ||
573 | Dwarf_Die die_mem; | ||
574 | Dwarf_Addr addr; | ||
575 | int lineno; | ||
576 | |||
577 | /* Handle function declaration line */ | ||
578 | lw.fname = dwarf_decl_file(sp_die); | ||
579 | if (lw.fname && dwarf_decl_line(sp_die, &lineno) == 0 && | ||
580 | dwarf_entrypc(sp_die, &addr) == 0) { | ||
581 | lw.retval = handler(lw.fname, lineno, addr, data); | ||
582 | if (lw.retval != 0) | ||
583 | goto done; | ||
584 | } | ||
585 | die_find_child(sp_die, __die_walk_funclines_cb, &lw, &die_mem); | ||
586 | done: | ||
587 | return lw.retval; | ||
588 | } | ||
589 | |||
590 | static int __die_walk_culines_cb(Dwarf_Die *sp_die, void *data) | ||
591 | { | ||
592 | struct __line_walk_param *lw = data; | ||
593 | |||
594 | lw->retval = __die_walk_funclines(sp_die, lw->handler, lw->data); | ||
595 | if (lw->retval != 0) | ||
596 | return DWARF_CB_ABORT; | ||
597 | |||
598 | return DWARF_CB_OK; | ||
599 | } | ||
600 | |||
601 | /* | ||
602 | * Walk on lines inside given PDIE. If the PDIE is subprogram, walk only on | ||
603 | * the lines inside the subprogram, otherwise PDIE must be a CU DIE. | ||
604 | */ | ||
605 | static int die_walk_lines(Dwarf_Die *pdie, line_walk_handler_t handler, | ||
606 | void *data) | ||
607 | { | ||
608 | Dwarf_Lines *lines; | ||
609 | Dwarf_Line *line; | ||
610 | Dwarf_Addr addr; | ||
611 | const char *fname; | ||
612 | int lineno, ret = 0; | ||
613 | Dwarf_Die die_mem, *cu_die; | ||
614 | size_t nlines, i; | ||
615 | |||
616 | /* Get the CU die */ | ||
617 | if (dwarf_tag(pdie) == DW_TAG_subprogram) | ||
618 | cu_die = dwarf_diecu(pdie, &die_mem, NULL, NULL); | ||
619 | else | ||
620 | cu_die = pdie; | ||
621 | if (!cu_die) { | ||
622 | pr_debug2("Failed to get CU from subprogram\n"); | ||
623 | return -EINVAL; | ||
624 | } | ||
625 | |||
626 | /* Get lines list in the CU */ | ||
627 | if (dwarf_getsrclines(cu_die, &lines, &nlines) != 0) { | ||
628 | pr_debug2("Failed to get source lines on this CU.\n"); | ||
629 | return -ENOENT; | ||
630 | } | ||
631 | pr_debug2("Get %zd lines from this CU\n", nlines); | ||
632 | |||
633 | /* Walk on the lines on lines list */ | ||
634 | for (i = 0; i < nlines; i++) { | ||
635 | line = dwarf_onesrcline(lines, i); | ||
636 | if (line == NULL || | ||
637 | dwarf_lineno(line, &lineno) != 0 || | ||
638 | dwarf_lineaddr(line, &addr) != 0) { | ||
639 | pr_debug2("Failed to get line info. " | ||
640 | "Possible error in debuginfo.\n"); | ||
641 | continue; | ||
642 | } | ||
643 | /* Filter lines based on address */ | ||
644 | if (pdie != cu_die) | ||
645 | /* | ||
646 | * Address filtering | ||
647 | * The line is included in given function, and | ||
648 | * no inline block includes it. | ||
649 | */ | ||
650 | if (!dwarf_haspc(pdie, addr) || | ||
651 | die_find_inlinefunc(pdie, addr, &die_mem)) | ||
652 | continue; | ||
653 | /* Get source line */ | ||
654 | fname = dwarf_linesrc(line, NULL, NULL); | ||
655 | |||
656 | ret = handler(fname, lineno, addr, data); | ||
657 | if (ret != 0) | ||
658 | return ret; | ||
659 | } | ||
660 | |||
661 | /* | ||
662 | * Dwarf lines doesn't include function declarations and inlined | ||
663 | * subroutines. We have to check functions list or given function. | ||
664 | */ | ||
665 | if (pdie != cu_die) | ||
666 | ret = __die_walk_funclines(pdie, handler, data); | ||
667 | else { | ||
668 | struct __line_walk_param param = { | ||
669 | .handler = handler, | ||
670 | .data = data, | ||
671 | .retval = 0, | ||
672 | }; | ||
673 | dwarf_getfuncs(cu_die, __die_walk_culines_cb, ¶m, 0); | ||
674 | ret = param.retval; | ||
675 | } | ||
676 | |||
677 | return ret; | ||
678 | } | ||
679 | |||
680 | struct __find_variable_param { | ||
681 | const char *name; | ||
682 | Dwarf_Addr addr; | ||
683 | }; | ||
684 | |||
685 | static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) | ||
686 | { | ||
687 | struct __find_variable_param *fvp = data; | ||
688 | int tag; | ||
689 | |||
690 | tag = dwarf_tag(die_mem); | ||
691 | if ((tag == DW_TAG_formal_parameter || | ||
692 | tag == DW_TAG_variable) && | ||
693 | die_compare_name(die_mem, fvp->name)) | ||
694 | return DIE_FIND_CB_FOUND; | ||
695 | |||
696 | if (dwarf_haspc(die_mem, fvp->addr)) | ||
697 | return DIE_FIND_CB_CONTINUE; | ||
698 | else | ||
699 | return DIE_FIND_CB_SIBLING; | ||
700 | } | ||
701 | |||
702 | /* Find a variable called 'name' at given address */ | ||
703 | static Dwarf_Die *die_find_variable_at(Dwarf_Die *sp_die, const char *name, | ||
704 | Dwarf_Addr addr, Dwarf_Die *die_mem) | ||
705 | { | ||
706 | struct __find_variable_param fvp = { .name = name, .addr = addr}; | ||
707 | |||
708 | return die_find_child(sp_die, __die_find_variable_cb, (void *)&fvp, | ||
709 | die_mem); | ||
710 | } | ||
711 | |||
712 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) | ||
713 | { | ||
714 | const char *name = data; | ||
715 | |||
716 | if ((dwarf_tag(die_mem) == DW_TAG_member) && | ||
717 | die_compare_name(die_mem, name)) | ||
718 | return DIE_FIND_CB_FOUND; | ||
719 | |||
720 | return DIE_FIND_CB_SIBLING; | ||
721 | } | ||
722 | |||
723 | /* Find a member called 'name' */ | ||
724 | static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
725 | Dwarf_Die *die_mem) | ||
726 | { | ||
727 | return die_find_child(st_die, __die_find_member_cb, (void *)name, | ||
728 | die_mem); | ||
729 | } | ||
730 | |||
731 | /* Get the name of given variable DIE */ | ||
732 | static int die_get_typename(Dwarf_Die *vr_die, char *buf, int len) | ||
733 | { | ||
734 | Dwarf_Die type; | ||
735 | int tag, ret, ret2; | ||
736 | const char *tmp = ""; | ||
737 | |||
738 | if (__die_get_real_type(vr_die, &type) == NULL) | ||
739 | return -ENOENT; | ||
740 | |||
741 | tag = dwarf_tag(&type); | ||
742 | if (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type) | ||
743 | tmp = "*"; | ||
744 | else if (tag == DW_TAG_subroutine_type) { | ||
745 | /* Function pointer */ | ||
746 | ret = snprintf(buf, len, "(function_type)"); | ||
747 | return (ret >= len) ? -E2BIG : ret; | ||
748 | } else { | ||
749 | if (!dwarf_diename(&type)) | ||
750 | return -ENOENT; | ||
751 | if (tag == DW_TAG_union_type) | ||
752 | tmp = "union "; | ||
753 | else if (tag == DW_TAG_structure_type) | ||
754 | tmp = "struct "; | ||
755 | /* Write a base name */ | ||
756 | ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type)); | ||
757 | return (ret >= len) ? -E2BIG : ret; | ||
758 | } | ||
759 | ret = die_get_typename(&type, buf, len); | ||
760 | if (ret > 0) { | ||
761 | ret2 = snprintf(buf + ret, len - ret, "%s", tmp); | ||
762 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
763 | } | ||
764 | return ret; | ||
765 | } | ||
766 | |||
767 | /* Get the name and type of given variable DIE, stored as "type\tname" */ | ||
768 | static int die_get_varname(Dwarf_Die *vr_die, char *buf, int len) | ||
769 | { | ||
770 | int ret, ret2; | ||
771 | |||
772 | ret = die_get_typename(vr_die, buf, len); | ||
773 | if (ret < 0) { | ||
774 | pr_debug("Failed to get type, make it unknown.\n"); | ||
775 | ret = snprintf(buf, len, "(unknown_type)"); | ||
776 | } | ||
777 | if (ret > 0) { | ||
778 | ret2 = snprintf(buf + ret, len - ret, "\t%s", | ||
779 | dwarf_diename(vr_die)); | ||
780 | ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret; | ||
781 | } | ||
782 | return ret; | ||
783 | } | 260 | } |
784 | 261 | ||
785 | /* | 262 | /* |
@@ -897,6 +374,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
897 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; | 374 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; |
898 | Dwarf_Die type; | 375 | Dwarf_Die type; |
899 | char buf[16]; | 376 | char buf[16]; |
377 | int bsize, boffs, total; | ||
900 | int ret; | 378 | int ret; |
901 | 379 | ||
902 | /* TODO: check all types */ | 380 | /* TODO: check all types */ |
@@ -906,11 +384,15 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
906 | return (tvar->type == NULL) ? -ENOMEM : 0; | 384 | return (tvar->type == NULL) ? -ENOMEM : 0; |
907 | } | 385 | } |
908 | 386 | ||
909 | if (die_get_bit_size(vr_die) != 0) { | 387 | bsize = dwarf_bitsize(vr_die); |
388 | if (bsize > 0) { | ||
910 | /* This is a bitfield */ | 389 | /* This is a bitfield */ |
911 | ret = snprintf(buf, 16, "b%d@%d/%zd", die_get_bit_size(vr_die), | 390 | boffs = dwarf_bitoffset(vr_die); |
912 | die_get_bit_offset(vr_die), | 391 | total = dwarf_bytesize(vr_die); |
913 | BYTES_TO_BITS(die_get_byte_size(vr_die))); | 392 | if (boffs < 0 || total < 0) |
393 | return -ENOENT; | ||
394 | ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs, | ||
395 | BYTES_TO_BITS(total)); | ||
914 | goto formatted; | 396 | goto formatted; |
915 | } | 397 | } |
916 | 398 | ||
@@ -958,10 +440,11 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
958 | return (tvar->type == NULL) ? -ENOMEM : 0; | 440 | return (tvar->type == NULL) ? -ENOMEM : 0; |
959 | } | 441 | } |
960 | 442 | ||
961 | ret = BYTES_TO_BITS(die_get_byte_size(&type)); | 443 | ret = dwarf_bytesize(&type); |
962 | if (!ret) | 444 | if (ret <= 0) |
963 | /* No size ... try to use default type */ | 445 | /* No size ... try to use default type */ |
964 | return 0; | 446 | return 0; |
447 | ret = BYTES_TO_BITS(ret); | ||
965 | 448 | ||
966 | /* Check the bitwidth */ | 449 | /* Check the bitwidth */ |
967 | if (ret > MAX_BASIC_TYPE_BITS) { | 450 | if (ret > MAX_BASIC_TYPE_BITS) { |
@@ -1025,7 +508,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
1025 | else | 508 | else |
1026 | *ref_ptr = ref; | 509 | *ref_ptr = ref; |
1027 | } | 510 | } |
1028 | ref->offset += die_get_byte_size(&type) * field->index; | 511 | ref->offset += dwarf_bytesize(&type) * field->index; |
1029 | if (!field->next) | 512 | if (!field->next) |
1030 | /* Save vr_die for converting types */ | 513 | /* Save vr_die for converting types */ |
1031 | memcpy(die_mem, vr_die, sizeof(*die_mem)); | 514 | memcpy(die_mem, vr_die, sizeof(*die_mem)); |
@@ -1129,12 +612,12 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
1129 | return ret; | 612 | return ret; |
1130 | } | 613 | } |
1131 | 614 | ||
1132 | /* Find a variable in a subprogram die */ | 615 | /* Find a variable in a scope DIE */ |
1133 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 616 | static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) |
1134 | { | 617 | { |
1135 | Dwarf_Die vr_die, *scopes; | 618 | Dwarf_Die vr_die; |
1136 | char buf[32], *ptr; | 619 | char buf[32], *ptr; |
1137 | int ret, nscopes; | 620 | int ret = 0; |
1138 | 621 | ||
1139 | if (!is_c_varname(pf->pvar->var)) { | 622 | if (!is_c_varname(pf->pvar->var)) { |
1140 | /* Copy raw parameters */ | 623 | /* Copy raw parameters */ |
@@ -1169,30 +652,16 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1169 | if (pf->tvar->name == NULL) | 652 | if (pf->tvar->name == NULL) |
1170 | return -ENOMEM; | 653 | return -ENOMEM; |
1171 | 654 | ||
1172 | pr_debug("Searching '%s' variable in context.\n", | 655 | pr_debug("Searching '%s' variable in context.\n", pf->pvar->var); |
1173 | pf->pvar->var); | ||
1174 | /* Search child die for local variables and parameters. */ | 656 | /* Search child die for local variables and parameters. */ |
1175 | if (die_find_variable_at(sp_die, pf->pvar->var, pf->addr, &vr_die)) | 657 | if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) { |
1176 | ret = convert_variable(&vr_die, pf); | 658 | /* Search again in global variables */ |
1177 | else { | 659 | if (!die_find_variable_at(&pf->cu_die, pf->pvar->var, 0, &vr_die)) |
1178 | /* Search upper class */ | 660 | ret = -ENOENT; |
1179 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | ||
1180 | while (nscopes-- > 1) { | ||
1181 | pr_debug("Searching variables in %s\n", | ||
1182 | dwarf_diename(&scopes[nscopes])); | ||
1183 | /* We should check this scope, so give dummy address */ | ||
1184 | if (die_find_variable_at(&scopes[nscopes], | ||
1185 | pf->pvar->var, 0, | ||
1186 | &vr_die)) { | ||
1187 | ret = convert_variable(&vr_die, pf); | ||
1188 | goto found; | ||
1189 | } | ||
1190 | } | ||
1191 | if (scopes) | ||
1192 | free(scopes); | ||
1193 | ret = -ENOENT; | ||
1194 | } | 661 | } |
1195 | found: | 662 | if (ret >= 0) |
663 | ret = convert_variable(&vr_die, pf); | ||
664 | |||
1196 | if (ret < 0) | 665 | if (ret < 0) |
1197 | pr_warning("Failed to find '%s' in this function.\n", | 666 | pr_warning("Failed to find '%s' in this function.\n", |
1198 | pf->pvar->var); | 667 | pf->pvar->var); |
@@ -1235,27 +704,30 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, | |||
1235 | return 0; | 704 | return 0; |
1236 | } | 705 | } |
1237 | 706 | ||
1238 | /* Call probe_finder callback with real subprogram DIE */ | 707 | /* Call probe_finder callback with scope DIE */ |
1239 | static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | 708 | static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) |
1240 | { | 709 | { |
1241 | Dwarf_Die die_mem; | ||
1242 | Dwarf_Attribute fb_attr; | 710 | Dwarf_Attribute fb_attr; |
1243 | size_t nops; | 711 | size_t nops; |
1244 | int ret; | 712 | int ret; |
1245 | 713 | ||
1246 | /* If no real subprogram, find a real one */ | 714 | if (!sc_die) { |
1247 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | 715 | pr_err("Caller must pass a scope DIE. Program error.\n"); |
1248 | sp_die = die_find_real_subprogram(&pf->cu_die, | 716 | return -EINVAL; |
1249 | pf->addr, &die_mem); | 717 | } |
1250 | if (!sp_die) { | 718 | |
719 | /* If not a real subprogram, find a real one */ | ||
720 | if (dwarf_tag(sc_die) != DW_TAG_subprogram) { | ||
721 | if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { | ||
1251 | pr_warning("Failed to find probe point in any " | 722 | pr_warning("Failed to find probe point in any " |
1252 | "functions.\n"); | 723 | "functions.\n"); |
1253 | return -ENOENT; | 724 | return -ENOENT; |
1254 | } | 725 | } |
1255 | } | 726 | } else |
727 | memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die)); | ||
1256 | 728 | ||
1257 | /* Get the frame base attribute/ops */ | 729 | /* Get the frame base attribute/ops from subprogram */ |
1258 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | 730 | dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr); |
1259 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); | 731 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); |
1260 | if (ret <= 0 || nops == 0) { | 732 | if (ret <= 0 || nops == 0) { |
1261 | pf->fb_ops = NULL; | 733 | pf->fb_ops = NULL; |
@@ -1273,7 +745,7 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1273 | } | 745 | } |
1274 | 746 | ||
1275 | /* Call finder's callback handler */ | 747 | /* Call finder's callback handler */ |
1276 | ret = pf->callback(sp_die, pf); | 748 | ret = pf->callback(sc_die, pf); |
1277 | 749 | ||
1278 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | 750 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ |
1279 | pf->fb_ops = NULL; | 751 | pf->fb_ops = NULL; |
@@ -1281,17 +753,82 @@ static int call_probe_finder(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1281 | return ret; | 753 | return ret; |
1282 | } | 754 | } |
1283 | 755 | ||
756 | struct find_scope_param { | ||
757 | const char *function; | ||
758 | const char *file; | ||
759 | int line; | ||
760 | int diff; | ||
761 | Dwarf_Die *die_mem; | ||
762 | bool found; | ||
763 | }; | ||
764 | |||
765 | static int find_best_scope_cb(Dwarf_Die *fn_die, void *data) | ||
766 | { | ||
767 | struct find_scope_param *fsp = data; | ||
768 | const char *file; | ||
769 | int lno; | ||
770 | |||
771 | /* Skip if declared file name does not match */ | ||
772 | if (fsp->file) { | ||
773 | file = dwarf_decl_file(fn_die); | ||
774 | if (!file || strcmp(fsp->file, file) != 0) | ||
775 | return 0; | ||
776 | } | ||
777 | /* If the function name is given, that's what user expects */ | ||
778 | if (fsp->function) { | ||
779 | if (die_compare_name(fn_die, fsp->function)) { | ||
780 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
781 | fsp->found = true; | ||
782 | return 1; | ||
783 | } | ||
784 | } else { | ||
785 | /* With the line number, find the nearest declared DIE */ | ||
786 | dwarf_decl_line(fn_die, &lno); | ||
787 | if (lno < fsp->line && fsp->diff > fsp->line - lno) { | ||
788 | /* Keep a candidate and continue */ | ||
789 | fsp->diff = fsp->line - lno; | ||
790 | memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die)); | ||
791 | fsp->found = true; | ||
792 | } | ||
793 | } | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | /* Find an appropriate scope fits to given conditions */ | ||
798 | static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem) | ||
799 | { | ||
800 | struct find_scope_param fsp = { | ||
801 | .function = pf->pev->point.function, | ||
802 | .file = pf->fname, | ||
803 | .line = pf->lno, | ||
804 | .diff = INT_MAX, | ||
805 | .die_mem = die_mem, | ||
806 | .found = false, | ||
807 | }; | ||
808 | |||
809 | cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp); | ||
810 | |||
811 | return fsp.found ? die_mem : NULL; | ||
812 | } | ||
813 | |||
1284 | static int probe_point_line_walker(const char *fname, int lineno, | 814 | static int probe_point_line_walker(const char *fname, int lineno, |
1285 | Dwarf_Addr addr, void *data) | 815 | Dwarf_Addr addr, void *data) |
1286 | { | 816 | { |
1287 | struct probe_finder *pf = data; | 817 | struct probe_finder *pf = data; |
818 | Dwarf_Die *sc_die, die_mem; | ||
1288 | int ret; | 819 | int ret; |
1289 | 820 | ||
1290 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) | 821 | if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0) |
1291 | return 0; | 822 | return 0; |
1292 | 823 | ||
1293 | pf->addr = addr; | 824 | pf->addr = addr; |
1294 | ret = call_probe_finder(NULL, pf); | 825 | sc_die = find_best_scope(pf, &die_mem); |
826 | if (!sc_die) { | ||
827 | pr_warning("Failed to find scope of probe point.\n"); | ||
828 | return -ENOENT; | ||
829 | } | ||
830 | |||
831 | ret = call_probe_finder(sc_die, pf); | ||
1295 | 832 | ||
1296 | /* Continue if no error, because the line will be in inline function */ | 833 | /* Continue if no error, because the line will be in inline function */ |
1297 | return ret < 0 ? ret : 0; | 834 | return ret < 0 ? ret : 0; |
@@ -1345,6 +882,7 @@ static int probe_point_lazy_walker(const char *fname, int lineno, | |||
1345 | Dwarf_Addr addr, void *data) | 882 | Dwarf_Addr addr, void *data) |
1346 | { | 883 | { |
1347 | struct probe_finder *pf = data; | 884 | struct probe_finder *pf = data; |
885 | Dwarf_Die *sc_die, die_mem; | ||
1348 | int ret; | 886 | int ret; |
1349 | 887 | ||
1350 | if (!line_list__has_line(&pf->lcache, lineno) || | 888 | if (!line_list__has_line(&pf->lcache, lineno) || |
@@ -1354,7 +892,14 @@ static int probe_point_lazy_walker(const char *fname, int lineno, | |||
1354 | pr_debug("Probe line found: line:%d addr:0x%llx\n", | 892 | pr_debug("Probe line found: line:%d addr:0x%llx\n", |
1355 | lineno, (unsigned long long)addr); | 893 | lineno, (unsigned long long)addr); |
1356 | pf->addr = addr; | 894 | pf->addr = addr; |
1357 | ret = call_probe_finder(NULL, pf); | 895 | pf->lno = lineno; |
896 | sc_die = find_best_scope(pf, &die_mem); | ||
897 | if (!sc_die) { | ||
898 | pr_warning("Failed to find scope of probe point.\n"); | ||
899 | return -ENOENT; | ||
900 | } | ||
901 | |||
902 | ret = call_probe_finder(sc_die, pf); | ||
1358 | 903 | ||
1359 | /* | 904 | /* |
1360 | * Continue if no error, because the lazy pattern will match | 905 | * Continue if no error, because the lazy pattern will match |
@@ -1379,42 +924,39 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1379 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); | 924 | return die_walk_lines(sp_die, probe_point_lazy_walker, pf); |
1380 | } | 925 | } |
1381 | 926 | ||
1382 | /* Callback parameter with return value */ | ||
1383 | struct dwarf_callback_param { | ||
1384 | void *data; | ||
1385 | int retval; | ||
1386 | }; | ||
1387 | |||
1388 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | 927 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) |
1389 | { | 928 | { |
1390 | struct dwarf_callback_param *param = data; | 929 | struct probe_finder *pf = data; |
1391 | struct probe_finder *pf = param->data; | ||
1392 | struct perf_probe_point *pp = &pf->pev->point; | 930 | struct perf_probe_point *pp = &pf->pev->point; |
1393 | Dwarf_Addr addr; | 931 | Dwarf_Addr addr; |
932 | int ret; | ||
1394 | 933 | ||
1395 | if (pp->lazy_line) | 934 | if (pp->lazy_line) |
1396 | param->retval = find_probe_point_lazy(in_die, pf); | 935 | ret = find_probe_point_lazy(in_die, pf); |
1397 | else { | 936 | else { |
1398 | /* Get probe address */ | 937 | /* Get probe address */ |
1399 | if (dwarf_entrypc(in_die, &addr) != 0) { | 938 | if (dwarf_entrypc(in_die, &addr) != 0) { |
1400 | pr_warning("Failed to get entry address of %s.\n", | 939 | pr_warning("Failed to get entry address of %s.\n", |
1401 | dwarf_diename(in_die)); | 940 | dwarf_diename(in_die)); |
1402 | param->retval = -ENOENT; | 941 | return -ENOENT; |
1403 | return DWARF_CB_ABORT; | ||
1404 | } | 942 | } |
1405 | pf->addr = addr; | 943 | pf->addr = addr; |
1406 | pf->addr += pp->offset; | 944 | pf->addr += pp->offset; |
1407 | pr_debug("found inline addr: 0x%jx\n", | 945 | pr_debug("found inline addr: 0x%jx\n", |
1408 | (uintmax_t)pf->addr); | 946 | (uintmax_t)pf->addr); |
1409 | 947 | ||
1410 | param->retval = call_probe_finder(in_die, pf); | 948 | ret = call_probe_finder(in_die, pf); |
1411 | if (param->retval < 0) | ||
1412 | return DWARF_CB_ABORT; | ||
1413 | } | 949 | } |
1414 | 950 | ||
1415 | return DWARF_CB_OK; | 951 | return ret; |
1416 | } | 952 | } |
1417 | 953 | ||
954 | /* Callback parameter with return value for libdw */ | ||
955 | struct dwarf_callback_param { | ||
956 | void *data; | ||
957 | int retval; | ||
958 | }; | ||
959 | |||
1418 | /* Search function from function name */ | 960 | /* Search function from function name */ |
1419 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | 961 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) |
1420 | { | 962 | { |
@@ -1451,14 +993,10 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
1451 | /* TODO: Check the address in this function */ | 993 | /* TODO: Check the address in this function */ |
1452 | param->retval = call_probe_finder(sp_die, pf); | 994 | param->retval = call_probe_finder(sp_die, pf); |
1453 | } | 995 | } |
1454 | } else { | 996 | } else |
1455 | struct dwarf_callback_param _param = {.data = (void *)pf, | ||
1456 | .retval = 0}; | ||
1457 | /* Inlined function: search instances */ | 997 | /* Inlined function: search instances */ |
1458 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, | 998 | param->retval = die_walk_instances(sp_die, |
1459 | &_param); | 999 | probe_point_inline_cb, (void *)pf); |
1460 | param->retval = _param.retval; | ||
1461 | } | ||
1462 | 1000 | ||
1463 | return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ | 1001 | return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */ |
1464 | } | 1002 | } |
@@ -1504,28 +1042,18 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) | |||
1504 | } | 1042 | } |
1505 | 1043 | ||
1506 | /* Find probe points from debuginfo */ | 1044 | /* Find probe points from debuginfo */ |
1507 | static int find_probes(int fd, struct probe_finder *pf) | 1045 | static int debuginfo__find_probes(struct debuginfo *self, |
1046 | struct probe_finder *pf) | ||
1508 | { | 1047 | { |
1509 | struct perf_probe_point *pp = &pf->pev->point; | 1048 | struct perf_probe_point *pp = &pf->pev->point; |
1510 | Dwarf_Off off, noff; | 1049 | Dwarf_Off off, noff; |
1511 | size_t cuhl; | 1050 | size_t cuhl; |
1512 | Dwarf_Die *diep; | 1051 | Dwarf_Die *diep; |
1513 | Dwarf *dbg = NULL; | ||
1514 | Dwfl *dwfl; | ||
1515 | Dwarf_Addr bias; /* Currently ignored */ | ||
1516 | int ret = 0; | 1052 | int ret = 0; |
1517 | 1053 | ||
1518 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | ||
1519 | if (!dbg) { | ||
1520 | pr_warning("No debug information found in the vmlinux - " | ||
1521 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
1522 | close(fd); /* Without dwfl_end(), fd isn't closed. */ | ||
1523 | return -EBADF; | ||
1524 | } | ||
1525 | |||
1526 | #if _ELFUTILS_PREREQ(0, 142) | 1054 | #if _ELFUTILS_PREREQ(0, 142) |
1527 | /* Get the call frame information from this dwarf */ | 1055 | /* Get the call frame information from this dwarf */ |
1528 | pf->cfi = dwarf_getcfi(dbg); | 1056 | pf->cfi = dwarf_getcfi(self->dbg); |
1529 | #endif | 1057 | #endif |
1530 | 1058 | ||
1531 | off = 0; | 1059 | off = 0; |
@@ -1544,7 +1072,8 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1544 | .data = pf, | 1072 | .data = pf, |
1545 | }; | 1073 | }; |
1546 | 1074 | ||
1547 | dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); | 1075 | dwarf_getpubnames(self->dbg, pubname_search_cb, |
1076 | &pubname_param, 0); | ||
1548 | if (pubname_param.found) { | 1077 | if (pubname_param.found) { |
1549 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); | 1078 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); |
1550 | if (ret) | 1079 | if (ret) |
@@ -1553,9 +1082,9 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1553 | } | 1082 | } |
1554 | 1083 | ||
1555 | /* Loop on CUs (Compilation Unit) */ | 1084 | /* Loop on CUs (Compilation Unit) */ |
1556 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | 1085 | while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
1557 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1086 | /* Get the DIE(Debugging Information Entry) of this CU */ |
1558 | diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die); | 1087 | diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); |
1559 | if (!diep) | 1088 | if (!diep) |
1560 | continue; | 1089 | continue; |
1561 | 1090 | ||
@@ -1582,14 +1111,12 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1582 | 1111 | ||
1583 | found: | 1112 | found: |
1584 | line_list__free(&pf->lcache); | 1113 | line_list__free(&pf->lcache); |
1585 | if (dwfl) | ||
1586 | dwfl_end(dwfl); | ||
1587 | 1114 | ||
1588 | return ret; | 1115 | return ret; |
1589 | } | 1116 | } |
1590 | 1117 | ||
1591 | /* Add a found probe point into trace event list */ | 1118 | /* Add a found probe point into trace event list */ |
1592 | static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | 1119 | static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) |
1593 | { | 1120 | { |
1594 | struct trace_event_finder *tf = | 1121 | struct trace_event_finder *tf = |
1595 | container_of(pf, struct trace_event_finder, pf); | 1122 | container_of(pf, struct trace_event_finder, pf); |
@@ -1604,8 +1131,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1604 | } | 1131 | } |
1605 | tev = &tf->tevs[tf->ntevs++]; | 1132 | tev = &tf->tevs[tf->ntevs++]; |
1606 | 1133 | ||
1607 | ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe, | 1134 | /* Trace point should be converted from subprogram DIE */ |
1608 | &tev->point); | 1135 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, |
1136 | pf->pev->point.retprobe, &tev->point); | ||
1609 | if (ret < 0) | 1137 | if (ret < 0) |
1610 | return ret; | 1138 | return ret; |
1611 | 1139 | ||
@@ -1620,7 +1148,8 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1620 | for (i = 0; i < pf->pev->nargs; i++) { | 1148 | for (i = 0; i < pf->pev->nargs; i++) { |
1621 | pf->pvar = &pf->pev->args[i]; | 1149 | pf->pvar = &pf->pev->args[i]; |
1622 | pf->tvar = &tev->args[i]; | 1150 | pf->tvar = &tev->args[i]; |
1623 | ret = find_variable(sp_die, pf); | 1151 | /* Variable should be found from scope DIE */ |
1152 | ret = find_variable(sc_die, pf); | ||
1624 | if (ret != 0) | 1153 | if (ret != 0) |
1625 | return ret; | 1154 | return ret; |
1626 | } | 1155 | } |
@@ -1629,8 +1158,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1629 | } | 1158 | } |
1630 | 1159 | ||
1631 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 1160 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
1632 | int find_probe_trace_events(int fd, struct perf_probe_event *pev, | 1161 | int debuginfo__find_trace_events(struct debuginfo *self, |
1633 | struct probe_trace_event **tevs, int max_tevs) | 1162 | struct perf_probe_event *pev, |
1163 | struct probe_trace_event **tevs, int max_tevs) | ||
1634 | { | 1164 | { |
1635 | struct trace_event_finder tf = { | 1165 | struct trace_event_finder tf = { |
1636 | .pf = {.pev = pev, .callback = add_probe_trace_event}, | 1166 | .pf = {.pev = pev, .callback = add_probe_trace_event}, |
@@ -1645,7 +1175,7 @@ int find_probe_trace_events(int fd, struct perf_probe_event *pev, | |||
1645 | tf.tevs = *tevs; | 1175 | tf.tevs = *tevs; |
1646 | tf.ntevs = 0; | 1176 | tf.ntevs = 0; |
1647 | 1177 | ||
1648 | ret = find_probes(fd, &tf.pf); | 1178 | ret = debuginfo__find_probes(self, &tf.pf); |
1649 | if (ret < 0) { | 1179 | if (ret < 0) { |
1650 | free(*tevs); | 1180 | free(*tevs); |
1651 | *tevs = NULL; | 1181 | *tevs = NULL; |
@@ -1687,13 +1217,13 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data) | |||
1687 | } | 1217 | } |
1688 | 1218 | ||
1689 | /* Add a found vars into available variables list */ | 1219 | /* Add a found vars into available variables list */ |
1690 | static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | 1220 | static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) |
1691 | { | 1221 | { |
1692 | struct available_var_finder *af = | 1222 | struct available_var_finder *af = |
1693 | container_of(pf, struct available_var_finder, pf); | 1223 | container_of(pf, struct available_var_finder, pf); |
1694 | struct variable_list *vl; | 1224 | struct variable_list *vl; |
1695 | Dwarf_Die die_mem, *scopes = NULL; | 1225 | Dwarf_Die die_mem; |
1696 | int ret, nscopes; | 1226 | int ret; |
1697 | 1227 | ||
1698 | /* Check number of tevs */ | 1228 | /* Check number of tevs */ |
1699 | if (af->nvls == af->max_vls) { | 1229 | if (af->nvls == af->max_vls) { |
@@ -1702,8 +1232,9 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1702 | } | 1232 | } |
1703 | vl = &af->vls[af->nvls++]; | 1233 | vl = &af->vls[af->nvls++]; |
1704 | 1234 | ||
1705 | ret = convert_to_trace_point(sp_die, pf->addr, pf->pev->point.retprobe, | 1235 | /* Trace point should be converted from subprogram DIE */ |
1706 | &vl->point); | 1236 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, |
1237 | pf->pev->point.retprobe, &vl->point); | ||
1707 | if (ret < 0) | 1238 | if (ret < 0) |
1708 | return ret; | 1239 | return ret; |
1709 | 1240 | ||
@@ -1715,19 +1246,14 @@ static int add_available_vars(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1715 | if (vl->vars == NULL) | 1246 | if (vl->vars == NULL) |
1716 | return -ENOMEM; | 1247 | return -ENOMEM; |
1717 | af->child = true; | 1248 | af->child = true; |
1718 | die_find_child(sp_die, collect_variables_cb, (void *)af, &die_mem); | 1249 | die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem); |
1719 | 1250 | ||
1720 | /* Find external variables */ | 1251 | /* Find external variables */ |
1721 | if (!af->externs) | 1252 | if (!af->externs) |
1722 | goto out; | 1253 | goto out; |
1723 | /* Don't need to search child DIE for externs. */ | 1254 | /* Don't need to search child DIE for externs. */ |
1724 | af->child = false; | 1255 | af->child = false; |
1725 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | 1256 | die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem); |
1726 | while (nscopes-- > 1) | ||
1727 | die_find_child(&scopes[nscopes], collect_variables_cb, | ||
1728 | (void *)af, &die_mem); | ||
1729 | if (scopes) | ||
1730 | free(scopes); | ||
1731 | 1257 | ||
1732 | out: | 1258 | out: |
1733 | if (strlist__empty(vl->vars)) { | 1259 | if (strlist__empty(vl->vars)) { |
@@ -1739,9 +1265,10 @@ out: | |||
1739 | } | 1265 | } |
1740 | 1266 | ||
1741 | /* Find available variables at given probe point */ | 1267 | /* Find available variables at given probe point */ |
1742 | int find_available_vars_at(int fd, struct perf_probe_event *pev, | 1268 | int debuginfo__find_available_vars_at(struct debuginfo *self, |
1743 | struct variable_list **vls, int max_vls, | 1269 | struct perf_probe_event *pev, |
1744 | bool externs) | 1270 | struct variable_list **vls, |
1271 | int max_vls, bool externs) | ||
1745 | { | 1272 | { |
1746 | struct available_var_finder af = { | 1273 | struct available_var_finder af = { |
1747 | .pf = {.pev = pev, .callback = add_available_vars}, | 1274 | .pf = {.pev = pev, .callback = add_available_vars}, |
@@ -1756,7 +1283,7 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
1756 | af.vls = *vls; | 1283 | af.vls = *vls; |
1757 | af.nvls = 0; | 1284 | af.nvls = 0; |
1758 | 1285 | ||
1759 | ret = find_probes(fd, &af.pf); | 1286 | ret = debuginfo__find_probes(self, &af.pf); |
1760 | if (ret < 0) { | 1287 | if (ret < 0) { |
1761 | /* Free vlist for error */ | 1288 | /* Free vlist for error */ |
1762 | while (af.nvls--) { | 1289 | while (af.nvls--) { |
@@ -1774,28 +1301,19 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
1774 | } | 1301 | } |
1775 | 1302 | ||
1776 | /* Reverse search */ | 1303 | /* Reverse search */ |
1777 | int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | 1304 | int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, |
1305 | struct perf_probe_point *ppt) | ||
1778 | { | 1306 | { |
1779 | Dwarf_Die cudie, spdie, indie; | 1307 | Dwarf_Die cudie, spdie, indie; |
1780 | Dwarf *dbg = NULL; | 1308 | Dwarf_Addr _addr, baseaddr; |
1781 | Dwfl *dwfl = NULL; | ||
1782 | Dwarf_Addr _addr, baseaddr, bias = 0; | ||
1783 | const char *fname = NULL, *func = NULL, *tmp; | 1309 | const char *fname = NULL, *func = NULL, *tmp; |
1784 | int baseline = 0, lineno = 0, ret = 0; | 1310 | int baseline = 0, lineno = 0, ret = 0; |
1785 | 1311 | ||
1786 | /* Open the live linux kernel */ | ||
1787 | dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); | ||
1788 | if (!dbg) { | ||
1789 | pr_warning("No debug information found in the vmlinux - " | ||
1790 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
1791 | ret = -EINVAL; | ||
1792 | goto end; | ||
1793 | } | ||
1794 | |||
1795 | /* Adjust address with bias */ | 1312 | /* Adjust address with bias */ |
1796 | addr += bias; | 1313 | addr += self->bias; |
1314 | |||
1797 | /* Find cu die */ | 1315 | /* Find cu die */ |
1798 | if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) { | 1316 | if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) { |
1799 | pr_warning("Failed to find debug information for address %lx\n", | 1317 | pr_warning("Failed to find debug information for address %lx\n", |
1800 | addr); | 1318 | addr); |
1801 | ret = -EINVAL; | 1319 | ret = -EINVAL; |
@@ -1807,7 +1325,7 @@ int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | |||
1807 | /* Don't care whether it failed or not */ | 1325 | /* Don't care whether it failed or not */ |
1808 | 1326 | ||
1809 | /* Find a corresponding function (name, baseline and baseaddr) */ | 1327 | /* Find a corresponding function (name, baseline and baseaddr) */ |
1810 | if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { | 1328 | if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { |
1811 | /* Get function entry information */ | 1329 | /* Get function entry information */ |
1812 | tmp = dwarf_diename(&spdie); | 1330 | tmp = dwarf_diename(&spdie); |
1813 | if (!tmp || | 1331 | if (!tmp || |
@@ -1871,8 +1389,6 @@ post: | |||
1871 | } | 1389 | } |
1872 | } | 1390 | } |
1873 | end: | 1391 | end: |
1874 | if (dwfl) | ||
1875 | dwfl_end(dwfl); | ||
1876 | if (ret == 0 && (fname || func)) | 1392 | if (ret == 0 && (fname || func)) |
1877 | ret = 1; /* Found a point */ | 1393 | ret = 1; /* Found a point */ |
1878 | return ret; | 1394 | return ret; |
@@ -1929,10 +1445,14 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | |||
1929 | 1445 | ||
1930 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | 1446 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) |
1931 | { | 1447 | { |
1932 | struct dwarf_callback_param *param = data; | 1448 | find_line_range_by_line(in_die, data); |
1933 | 1449 | ||
1934 | param->retval = find_line_range_by_line(in_die, param->data); | 1450 | /* |
1935 | return DWARF_CB_ABORT; /* No need to find other instances */ | 1451 | * We have to check all instances of inlined function, because |
1452 | * some execution paths can be optimized out depends on the | ||
1453 | * function argument of instances | ||
1454 | */ | ||
1455 | return 0; | ||
1936 | } | 1456 | } |
1937 | 1457 | ||
1938 | /* Search function from function name */ | 1458 | /* Search function from function name */ |
@@ -1960,15 +1480,10 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
1960 | pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); | 1480 | pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e); |
1961 | lr->start = lf->lno_s; | 1481 | lr->start = lf->lno_s; |
1962 | lr->end = lf->lno_e; | 1482 | lr->end = lf->lno_e; |
1963 | if (dwarf_func_inline(sp_die)) { | 1483 | if (dwarf_func_inline(sp_die)) |
1964 | struct dwarf_callback_param _param; | 1484 | param->retval = die_walk_instances(sp_die, |
1965 | _param.data = (void *)lf; | 1485 | line_range_inline_cb, lf); |
1966 | _param.retval = 0; | 1486 | else |
1967 | dwarf_func_inline_instances(sp_die, | ||
1968 | line_range_inline_cb, | ||
1969 | &_param); | ||
1970 | param->retval = _param.retval; | ||
1971 | } else | ||
1972 | param->retval = find_line_range_by_line(sp_die, lf); | 1487 | param->retval = find_line_range_by_line(sp_die, lf); |
1973 | return DWARF_CB_ABORT; | 1488 | return DWARF_CB_ABORT; |
1974 | } | 1489 | } |
@@ -1982,26 +1497,15 @@ static int find_line_range_by_func(struct line_finder *lf) | |||
1982 | return param.retval; | 1497 | return param.retval; |
1983 | } | 1498 | } |
1984 | 1499 | ||
1985 | int find_line_range(int fd, struct line_range *lr) | 1500 | int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) |
1986 | { | 1501 | { |
1987 | struct line_finder lf = {.lr = lr, .found = 0}; | 1502 | struct line_finder lf = {.lr = lr, .found = 0}; |
1988 | int ret = 0; | 1503 | int ret = 0; |
1989 | Dwarf_Off off = 0, noff; | 1504 | Dwarf_Off off = 0, noff; |
1990 | size_t cuhl; | 1505 | size_t cuhl; |
1991 | Dwarf_Die *diep; | 1506 | Dwarf_Die *diep; |
1992 | Dwarf *dbg = NULL; | ||
1993 | Dwfl *dwfl; | ||
1994 | Dwarf_Addr bias; /* Currently ignored */ | ||
1995 | const char *comp_dir; | 1507 | const char *comp_dir; |
1996 | 1508 | ||
1997 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | ||
1998 | if (!dbg) { | ||
1999 | pr_warning("No debug information found in the vmlinux - " | ||
2000 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
2001 | close(fd); /* Without dwfl_end(), fd isn't closed. */ | ||
2002 | return -EBADF; | ||
2003 | } | ||
2004 | |||
2005 | /* Fastpath: lookup by function name from .debug_pubnames section */ | 1509 | /* Fastpath: lookup by function name from .debug_pubnames section */ |
2006 | if (lr->function) { | 1510 | if (lr->function) { |
2007 | struct pubname_callback_param pubname_param = { | 1511 | struct pubname_callback_param pubname_param = { |
@@ -2010,7 +1514,8 @@ int find_line_range(int fd, struct line_range *lr) | |||
2010 | struct dwarf_callback_param line_range_param = { | 1514 | struct dwarf_callback_param line_range_param = { |
2011 | .data = (void *)&lf, .retval = 0}; | 1515 | .data = (void *)&lf, .retval = 0}; |
2012 | 1516 | ||
2013 | dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); | 1517 | dwarf_getpubnames(self->dbg, pubname_search_cb, |
1518 | &pubname_param, 0); | ||
2014 | if (pubname_param.found) { | 1519 | if (pubname_param.found) { |
2015 | line_range_search_cb(&lf.sp_die, &line_range_param); | 1520 | line_range_search_cb(&lf.sp_die, &line_range_param); |
2016 | if (lf.found) | 1521 | if (lf.found) |
@@ -2020,11 +1525,12 @@ int find_line_range(int fd, struct line_range *lr) | |||
2020 | 1525 | ||
2021 | /* Loop on CUs (Compilation Unit) */ | 1526 | /* Loop on CUs (Compilation Unit) */ |
2022 | while (!lf.found && ret >= 0) { | 1527 | while (!lf.found && ret >= 0) { |
2023 | if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) | 1528 | if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, |
1529 | NULL, NULL, NULL) != 0) | ||
2024 | break; | 1530 | break; |
2025 | 1531 | ||
2026 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1532 | /* Get the DIE(Debugging Information Entry) of this CU */ |
2027 | diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); | 1533 | diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); |
2028 | if (!diep) | 1534 | if (!diep) |
2029 | continue; | 1535 | continue; |
2030 | 1536 | ||
@@ -2058,7 +1564,6 @@ found: | |||
2058 | } | 1564 | } |
2059 | 1565 | ||
2060 | pr_debug("path: %s\n", lr->path); | 1566 | pr_debug("path: %s\n", lr->path); |
2061 | dwfl_end(dwfl); | ||
2062 | return (ret < 0) ? ret : lf.found; | 1567 | return (ret < 0) ? ret : lf.found; |
2063 | } | 1568 | } |
2064 | 1569 | ||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 605730a366db..1132c8f0ce89 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -16,33 +16,48 @@ static inline int is_c_varname(const char *name) | |||
16 | } | 16 | } |
17 | 17 | ||
18 | #ifdef DWARF_SUPPORT | 18 | #ifdef DWARF_SUPPORT |
19 | |||
20 | #include "dwarf-aux.h" | ||
21 | |||
22 | /* TODO: export debuginfo data structure even if no dwarf support */ | ||
23 | |||
24 | /* debug information structure */ | ||
25 | struct debuginfo { | ||
26 | Dwarf *dbg; | ||
27 | Dwfl *dwfl; | ||
28 | Dwarf_Addr bias; | ||
29 | }; | ||
30 | |||
31 | extern struct debuginfo *debuginfo__new(const char *path); | ||
32 | extern struct debuginfo *debuginfo__new_online_kernel(unsigned long addr); | ||
33 | extern void debuginfo__delete(struct debuginfo *self); | ||
34 | |||
19 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 35 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
20 | extern int find_probe_trace_events(int fd, struct perf_probe_event *pev, | 36 | extern int debuginfo__find_trace_events(struct debuginfo *self, |
21 | struct probe_trace_event **tevs, | 37 | struct perf_probe_event *pev, |
22 | int max_tevs); | 38 | struct probe_trace_event **tevs, |
39 | int max_tevs); | ||
23 | 40 | ||
24 | /* Find a perf_probe_point from debuginfo */ | 41 | /* Find a perf_probe_point from debuginfo */ |
25 | extern int find_perf_probe_point(unsigned long addr, | 42 | extern int debuginfo__find_probe_point(struct debuginfo *self, |
26 | struct perf_probe_point *ppt); | 43 | unsigned long addr, |
44 | struct perf_probe_point *ppt); | ||
27 | 45 | ||
28 | /* Find a line range */ | 46 | /* Find a line range */ |
29 | extern int find_line_range(int fd, struct line_range *lr); | 47 | extern int debuginfo__find_line_range(struct debuginfo *self, |
48 | struct line_range *lr); | ||
30 | 49 | ||
31 | /* Find available variables */ | 50 | /* Find available variables */ |
32 | extern int find_available_vars_at(int fd, struct perf_probe_event *pev, | 51 | extern int debuginfo__find_available_vars_at(struct debuginfo *self, |
33 | struct variable_list **vls, int max_points, | 52 | struct perf_probe_event *pev, |
34 | bool externs); | 53 | struct variable_list **vls, |
35 | 54 | int max_points, bool externs); | |
36 | #include <dwarf.h> | ||
37 | #include <elfutils/libdw.h> | ||
38 | #include <elfutils/libdwfl.h> | ||
39 | #include <elfutils/version.h> | ||
40 | 55 | ||
41 | struct probe_finder { | 56 | struct probe_finder { |
42 | struct perf_probe_event *pev; /* Target probe event */ | 57 | struct perf_probe_event *pev; /* Target probe event */ |
43 | 58 | ||
44 | /* Callback when a probe point is found */ | 59 | /* Callback when a probe point is found */ |
45 | int (*callback)(Dwarf_Die *sp_die, struct probe_finder *pf); | 60 | int (*callback)(Dwarf_Die *sc_die, struct probe_finder *pf); |
46 | 61 | ||
47 | /* For function searching */ | 62 | /* For function searching */ |
48 | int lno; /* Line number */ | 63 | int lno; /* Line number */ |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index a9ac0504aabd..9dd47a4f2596 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -187,16 +187,119 @@ static PyTypeObject pyrf_throttle_event__type = { | |||
187 | .tp_repr = (reprfunc)pyrf_throttle_event__repr, | 187 | .tp_repr = (reprfunc)pyrf_throttle_event__repr, |
188 | }; | 188 | }; |
189 | 189 | ||
190 | static char pyrf_lost_event__doc[] = PyDoc_STR("perf lost event object."); | ||
191 | |||
192 | static PyMemberDef pyrf_lost_event__members[] = { | ||
193 | sample_members | ||
194 | member_def(lost_event, id, T_ULONGLONG, "event id"), | ||
195 | member_def(lost_event, lost, T_ULONGLONG, "number of lost events"), | ||
196 | { .name = NULL, }, | ||
197 | }; | ||
198 | |||
199 | static PyObject *pyrf_lost_event__repr(struct pyrf_event *pevent) | ||
200 | { | ||
201 | PyObject *ret; | ||
202 | char *s; | ||
203 | |||
204 | if (asprintf(&s, "{ type: lost, id: %#" PRIx64 ", " | ||
205 | "lost: %#" PRIx64 " }", | ||
206 | pevent->event.lost.id, pevent->event.lost.lost) < 0) { | ||
207 | ret = PyErr_NoMemory(); | ||
208 | } else { | ||
209 | ret = PyString_FromString(s); | ||
210 | free(s); | ||
211 | } | ||
212 | return ret; | ||
213 | } | ||
214 | |||
215 | static PyTypeObject pyrf_lost_event__type = { | ||
216 | PyVarObject_HEAD_INIT(NULL, 0) | ||
217 | .tp_name = "perf.lost_event", | ||
218 | .tp_basicsize = sizeof(struct pyrf_event), | ||
219 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
220 | .tp_doc = pyrf_lost_event__doc, | ||
221 | .tp_members = pyrf_lost_event__members, | ||
222 | .tp_repr = (reprfunc)pyrf_lost_event__repr, | ||
223 | }; | ||
224 | |||
225 | static char pyrf_read_event__doc[] = PyDoc_STR("perf read event object."); | ||
226 | |||
227 | static PyMemberDef pyrf_read_event__members[] = { | ||
228 | sample_members | ||
229 | member_def(read_event, pid, T_UINT, "event pid"), | ||
230 | member_def(read_event, tid, T_UINT, "event tid"), | ||
231 | { .name = NULL, }, | ||
232 | }; | ||
233 | |||
234 | static PyObject *pyrf_read_event__repr(struct pyrf_event *pevent) | ||
235 | { | ||
236 | return PyString_FromFormat("{ type: read, pid: %u, tid: %u }", | ||
237 | pevent->event.read.pid, | ||
238 | pevent->event.read.tid); | ||
239 | /* | ||
240 | * FIXME: return the array of read values, | ||
241 | * making this method useful ;-) | ||
242 | */ | ||
243 | } | ||
244 | |||
245 | static PyTypeObject pyrf_read_event__type = { | ||
246 | PyVarObject_HEAD_INIT(NULL, 0) | ||
247 | .tp_name = "perf.read_event", | ||
248 | .tp_basicsize = sizeof(struct pyrf_event), | ||
249 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
250 | .tp_doc = pyrf_read_event__doc, | ||
251 | .tp_members = pyrf_read_event__members, | ||
252 | .tp_repr = (reprfunc)pyrf_read_event__repr, | ||
253 | }; | ||
254 | |||
255 | static char pyrf_sample_event__doc[] = PyDoc_STR("perf sample event object."); | ||
256 | |||
257 | static PyMemberDef pyrf_sample_event__members[] = { | ||
258 | sample_members | ||
259 | member_def(perf_event_header, type, T_UINT, "event type"), | ||
260 | { .name = NULL, }, | ||
261 | }; | ||
262 | |||
263 | static PyObject *pyrf_sample_event__repr(struct pyrf_event *pevent) | ||
264 | { | ||
265 | PyObject *ret; | ||
266 | char *s; | ||
267 | |||
268 | if (asprintf(&s, "{ type: sample }") < 0) { | ||
269 | ret = PyErr_NoMemory(); | ||
270 | } else { | ||
271 | ret = PyString_FromString(s); | ||
272 | free(s); | ||
273 | } | ||
274 | return ret; | ||
275 | } | ||
276 | |||
277 | static PyTypeObject pyrf_sample_event__type = { | ||
278 | PyVarObject_HEAD_INIT(NULL, 0) | ||
279 | .tp_name = "perf.sample_event", | ||
280 | .tp_basicsize = sizeof(struct pyrf_event), | ||
281 | .tp_flags = Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, | ||
282 | .tp_doc = pyrf_sample_event__doc, | ||
283 | .tp_members = pyrf_sample_event__members, | ||
284 | .tp_repr = (reprfunc)pyrf_sample_event__repr, | ||
285 | }; | ||
286 | |||
190 | static int pyrf_event__setup_types(void) | 287 | static int pyrf_event__setup_types(void) |
191 | { | 288 | { |
192 | int err; | 289 | int err; |
193 | pyrf_mmap_event__type.tp_new = | 290 | pyrf_mmap_event__type.tp_new = |
194 | pyrf_task_event__type.tp_new = | 291 | pyrf_task_event__type.tp_new = |
195 | pyrf_comm_event__type.tp_new = | 292 | pyrf_comm_event__type.tp_new = |
293 | pyrf_lost_event__type.tp_new = | ||
294 | pyrf_read_event__type.tp_new = | ||
295 | pyrf_sample_event__type.tp_new = | ||
196 | pyrf_throttle_event__type.tp_new = PyType_GenericNew; | 296 | pyrf_throttle_event__type.tp_new = PyType_GenericNew; |
197 | err = PyType_Ready(&pyrf_mmap_event__type); | 297 | err = PyType_Ready(&pyrf_mmap_event__type); |
198 | if (err < 0) | 298 | if (err < 0) |
199 | goto out; | 299 | goto out; |
300 | err = PyType_Ready(&pyrf_lost_event__type); | ||
301 | if (err < 0) | ||
302 | goto out; | ||
200 | err = PyType_Ready(&pyrf_task_event__type); | 303 | err = PyType_Ready(&pyrf_task_event__type); |
201 | if (err < 0) | 304 | if (err < 0) |
202 | goto out; | 305 | goto out; |
@@ -206,20 +309,26 @@ static int pyrf_event__setup_types(void) | |||
206 | err = PyType_Ready(&pyrf_throttle_event__type); | 309 | err = PyType_Ready(&pyrf_throttle_event__type); |
207 | if (err < 0) | 310 | if (err < 0) |
208 | goto out; | 311 | goto out; |
312 | err = PyType_Ready(&pyrf_read_event__type); | ||
313 | if (err < 0) | ||
314 | goto out; | ||
315 | err = PyType_Ready(&pyrf_sample_event__type); | ||
316 | if (err < 0) | ||
317 | goto out; | ||
209 | out: | 318 | out: |
210 | return err; | 319 | return err; |
211 | } | 320 | } |
212 | 321 | ||
213 | static PyTypeObject *pyrf_event__type[] = { | 322 | static PyTypeObject *pyrf_event__type[] = { |
214 | [PERF_RECORD_MMAP] = &pyrf_mmap_event__type, | 323 | [PERF_RECORD_MMAP] = &pyrf_mmap_event__type, |
215 | [PERF_RECORD_LOST] = &pyrf_mmap_event__type, | 324 | [PERF_RECORD_LOST] = &pyrf_lost_event__type, |
216 | [PERF_RECORD_COMM] = &pyrf_comm_event__type, | 325 | [PERF_RECORD_COMM] = &pyrf_comm_event__type, |
217 | [PERF_RECORD_EXIT] = &pyrf_task_event__type, | 326 | [PERF_RECORD_EXIT] = &pyrf_task_event__type, |
218 | [PERF_RECORD_THROTTLE] = &pyrf_throttle_event__type, | 327 | [PERF_RECORD_THROTTLE] = &pyrf_throttle_event__type, |
219 | [PERF_RECORD_UNTHROTTLE] = &pyrf_throttle_event__type, | 328 | [PERF_RECORD_UNTHROTTLE] = &pyrf_throttle_event__type, |
220 | [PERF_RECORD_FORK] = &pyrf_task_event__type, | 329 | [PERF_RECORD_FORK] = &pyrf_task_event__type, |
221 | [PERF_RECORD_READ] = &pyrf_mmap_event__type, | 330 | [PERF_RECORD_READ] = &pyrf_read_event__type, |
222 | [PERF_RECORD_SAMPLE] = &pyrf_mmap_event__type, | 331 | [PERF_RECORD_SAMPLE] = &pyrf_sample_event__type, |
223 | }; | 332 | }; |
224 | 333 | ||
225 | static PyObject *pyrf_event__new(union perf_event *event) | 334 | static PyObject *pyrf_event__new(union perf_event *event) |
@@ -247,7 +356,7 @@ struct pyrf_cpu_map { | |||
247 | static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, | 356 | static int pyrf_cpu_map__init(struct pyrf_cpu_map *pcpus, |
248 | PyObject *args, PyObject *kwargs) | 357 | PyObject *args, PyObject *kwargs) |
249 | { | 358 | { |
250 | static char *kwlist[] = { "cpustr", NULL, NULL, }; | 359 | static char *kwlist[] = { "cpustr", NULL }; |
251 | char *cpustr = NULL; | 360 | char *cpustr = NULL; |
252 | 361 | ||
253 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", | 362 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|s", |
@@ -316,7 +425,7 @@ struct pyrf_thread_map { | |||
316 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, | 425 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, |
317 | PyObject *args, PyObject *kwargs) | 426 | PyObject *args, PyObject *kwargs) |
318 | { | 427 | { |
319 | static char *kwlist[] = { "pid", "tid", NULL, NULL, }; | 428 | static char *kwlist[] = { "pid", "tid", NULL }; |
320 | int pid = -1, tid = -1; | 429 | int pid = -1, tid = -1; |
321 | 430 | ||
322 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", | 431 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", |
@@ -418,7 +527,9 @@ static int pyrf_evsel__init(struct pyrf_evsel *pevsel, | |||
418 | "wakeup_events", | 527 | "wakeup_events", |
419 | "bp_type", | 528 | "bp_type", |
420 | "bp_addr", | 529 | "bp_addr", |
421 | "bp_len", NULL, NULL, }; | 530 | "bp_len", |
531 | NULL | ||
532 | }; | ||
422 | u64 sample_period = 0; | 533 | u64 sample_period = 0; |
423 | u32 disabled = 0, | 534 | u32 disabled = 0, |
424 | inherit = 0, | 535 | inherit = 0, |
@@ -499,7 +610,7 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, | |||
499 | struct thread_map *threads = NULL; | 610 | struct thread_map *threads = NULL; |
500 | PyObject *pcpus = NULL, *pthreads = NULL; | 611 | PyObject *pcpus = NULL, *pthreads = NULL; |
501 | int group = 0, inherit = 0; | 612 | int group = 0, inherit = 0; |
502 | static char *kwlist[] = {"cpus", "threads", "group", "inherit", NULL, NULL}; | 613 | static char *kwlist[] = { "cpus", "threads", "group", "inherit", NULL }; |
503 | 614 | ||
504 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, | 615 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, |
505 | &pcpus, &pthreads, &group, &inherit)) | 616 | &pcpus, &pthreads, &group, &inherit)) |
@@ -512,7 +623,11 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel, | |||
512 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; | 623 | cpus = ((struct pyrf_cpu_map *)pcpus)->cpus; |
513 | 624 | ||
514 | evsel->attr.inherit = inherit; | 625 | evsel->attr.inherit = inherit; |
515 | if (perf_evsel__open(evsel, cpus, threads, group) < 0) { | 626 | /* |
627 | * This will group just the fds for this single evsel, to group | ||
628 | * multiple events, use evlist.open(). | ||
629 | */ | ||
630 | if (perf_evsel__open(evsel, cpus, threads, group, NULL) < 0) { | ||
516 | PyErr_SetFromErrno(PyExc_OSError); | 631 | PyErr_SetFromErrno(PyExc_OSError); |
517 | return NULL; | 632 | return NULL; |
518 | } | 633 | } |
@@ -582,8 +697,7 @@ static PyObject *pyrf_evlist__mmap(struct pyrf_evlist *pevlist, | |||
582 | PyObject *args, PyObject *kwargs) | 697 | PyObject *args, PyObject *kwargs) |
583 | { | 698 | { |
584 | struct perf_evlist *evlist = &pevlist->evlist; | 699 | struct perf_evlist *evlist = &pevlist->evlist; |
585 | static char *kwlist[] = {"pages", "overwrite", | 700 | static char *kwlist[] = { "pages", "overwrite", NULL }; |
586 | NULL, NULL}; | ||
587 | int pages = 128, overwrite = false; | 701 | int pages = 128, overwrite = false; |
588 | 702 | ||
589 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, | 703 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", kwlist, |
@@ -603,7 +717,7 @@ static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist, | |||
603 | PyObject *args, PyObject *kwargs) | 717 | PyObject *args, PyObject *kwargs) |
604 | { | 718 | { |
605 | struct perf_evlist *evlist = &pevlist->evlist; | 719 | struct perf_evlist *evlist = &pevlist->evlist; |
606 | static char *kwlist[] = {"timeout", NULL, NULL}; | 720 | static char *kwlist[] = { "timeout", NULL }; |
607 | int timeout = -1, n; | 721 | int timeout = -1, n; |
608 | 722 | ||
609 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) | 723 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", kwlist, &timeout)) |
@@ -674,7 +788,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
674 | struct perf_evlist *evlist = &pevlist->evlist; | 788 | struct perf_evlist *evlist = &pevlist->evlist; |
675 | union perf_event *event; | 789 | union perf_event *event; |
676 | int sample_id_all = 1, cpu; | 790 | int sample_id_all = 1, cpu; |
677 | static char *kwlist[] = {"cpu", "sample_id_all", NULL, NULL}; | 791 | static char *kwlist[] = { "cpu", "sample_id_all", NULL }; |
678 | int err; | 792 | int err; |
679 | 793 | ||
680 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, | 794 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "i|i", kwlist, |
@@ -693,7 +807,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
693 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | 807 | first = list_entry(evlist->entries.next, struct perf_evsel, node); |
694 | err = perf_event__parse_sample(event, first->attr.sample_type, | 808 | err = perf_event__parse_sample(event, first->attr.sample_type, |
695 | perf_evsel__sample_size(first), | 809 | perf_evsel__sample_size(first), |
696 | sample_id_all, &pevent->sample); | 810 | sample_id_all, &pevent->sample, false); |
697 | if (err) | 811 | if (err) |
698 | return PyErr_Format(PyExc_OSError, | 812 | return PyErr_Format(PyExc_OSError, |
699 | "perf: can't parse sample, err=%d", err); | 813 | "perf: can't parse sample, err=%d", err); |
@@ -704,6 +818,25 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
704 | return Py_None; | 818 | return Py_None; |
705 | } | 819 | } |
706 | 820 | ||
821 | static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist, | ||
822 | PyObject *args, PyObject *kwargs) | ||
823 | { | ||
824 | struct perf_evlist *evlist = &pevlist->evlist; | ||
825 | int group = 0; | ||
826 | static char *kwlist[] = { "group", NULL }; | ||
827 | |||
828 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, &group)) | ||
829 | return NULL; | ||
830 | |||
831 | if (perf_evlist__open(evlist, group) < 0) { | ||
832 | PyErr_SetFromErrno(PyExc_OSError); | ||
833 | return NULL; | ||
834 | } | ||
835 | |||
836 | Py_INCREF(Py_None); | ||
837 | return Py_None; | ||
838 | } | ||
839 | |||
707 | static PyMethodDef pyrf_evlist__methods[] = { | 840 | static PyMethodDef pyrf_evlist__methods[] = { |
708 | { | 841 | { |
709 | .ml_name = "mmap", | 842 | .ml_name = "mmap", |
@@ -712,6 +845,12 @@ static PyMethodDef pyrf_evlist__methods[] = { | |||
712 | .ml_doc = PyDoc_STR("mmap the file descriptor table.") | 845 | .ml_doc = PyDoc_STR("mmap the file descriptor table.") |
713 | }, | 846 | }, |
714 | { | 847 | { |
848 | .ml_name = "open", | ||
849 | .ml_meth = (PyCFunction)pyrf_evlist__open, | ||
850 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | ||
851 | .ml_doc = PyDoc_STR("open the file descriptors.") | ||
852 | }, | ||
853 | { | ||
715 | .ml_name = "poll", | 854 | .ml_name = "poll", |
716 | .ml_meth = (PyCFunction)pyrf_evlist__poll, | 855 | .ml_meth = (PyCFunction)pyrf_evlist__poll, |
717 | .ml_flags = METH_VARARGS | METH_KEYWORDS, | 856 | .ml_flags = METH_VARARGS | METH_KEYWORDS, |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f5a8fbdd3f76..85c1e6b76f0a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include "session.h" | 12 | #include "session.h" |
13 | #include "sort.h" | 13 | #include "sort.h" |
14 | #include "util.h" | 14 | #include "util.h" |
15 | #include "cpumap.h" | ||
15 | 16 | ||
16 | static int perf_session__open(struct perf_session *self, bool force) | 17 | static int perf_session__open(struct perf_session *self, bool force) |
17 | { | 18 | { |
@@ -247,9 +248,14 @@ int perf_session__resolve_callchain(struct perf_session *self, | |||
247 | callchain_cursor_reset(&self->callchain_cursor); | 248 | callchain_cursor_reset(&self->callchain_cursor); |
248 | 249 | ||
249 | for (i = 0; i < chain->nr; i++) { | 250 | for (i = 0; i < chain->nr; i++) { |
250 | u64 ip = chain->ips[i]; | 251 | u64 ip; |
251 | struct addr_location al; | 252 | struct addr_location al; |
252 | 253 | ||
254 | if (callchain_param.order == ORDER_CALLEE) | ||
255 | ip = chain->ips[i]; | ||
256 | else | ||
257 | ip = chain->ips[chain->nr - i - 1]; | ||
258 | |||
253 | if (ip >= PERF_CONTEXT_MAX) { | 259 | if (ip >= PERF_CONTEXT_MAX) { |
254 | switch (ip) { | 260 | switch (ip) { |
255 | case PERF_CONTEXT_HV: | 261 | case PERF_CONTEXT_HV: |
@@ -407,20 +413,26 @@ static void perf_event__read_swap(union perf_event *event) | |||
407 | event->read.id = bswap_64(event->read.id); | 413 | event->read.id = bswap_64(event->read.id); |
408 | } | 414 | } |
409 | 415 | ||
410 | static void perf_event__attr_swap(union perf_event *event) | 416 | /* exported for swapping attributes in file header */ |
417 | void perf_event__attr_swap(struct perf_event_attr *attr) | ||
418 | { | ||
419 | attr->type = bswap_32(attr->type); | ||
420 | attr->size = bswap_32(attr->size); | ||
421 | attr->config = bswap_64(attr->config); | ||
422 | attr->sample_period = bswap_64(attr->sample_period); | ||
423 | attr->sample_type = bswap_64(attr->sample_type); | ||
424 | attr->read_format = bswap_64(attr->read_format); | ||
425 | attr->wakeup_events = bswap_32(attr->wakeup_events); | ||
426 | attr->bp_type = bswap_32(attr->bp_type); | ||
427 | attr->bp_addr = bswap_64(attr->bp_addr); | ||
428 | attr->bp_len = bswap_64(attr->bp_len); | ||
429 | } | ||
430 | |||
431 | static void perf_event__hdr_attr_swap(union perf_event *event) | ||
411 | { | 432 | { |
412 | size_t size; | 433 | size_t size; |
413 | 434 | ||
414 | event->attr.attr.type = bswap_32(event->attr.attr.type); | 435 | perf_event__attr_swap(&event->attr.attr); |
415 | event->attr.attr.size = bswap_32(event->attr.attr.size); | ||
416 | event->attr.attr.config = bswap_64(event->attr.attr.config); | ||
417 | event->attr.attr.sample_period = bswap_64(event->attr.attr.sample_period); | ||
418 | event->attr.attr.sample_type = bswap_64(event->attr.attr.sample_type); | ||
419 | event->attr.attr.read_format = bswap_64(event->attr.attr.read_format); | ||
420 | event->attr.attr.wakeup_events = bswap_32(event->attr.attr.wakeup_events); | ||
421 | event->attr.attr.bp_type = bswap_32(event->attr.attr.bp_type); | ||
422 | event->attr.attr.bp_addr = bswap_64(event->attr.attr.bp_addr); | ||
423 | event->attr.attr.bp_len = bswap_64(event->attr.attr.bp_len); | ||
424 | 436 | ||
425 | size = event->header.size; | 437 | size = event->header.size; |
426 | size -= (void *)&event->attr.id - (void *)event; | 438 | size -= (void *)&event->attr.id - (void *)event; |
@@ -448,7 +460,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
448 | [PERF_RECORD_LOST] = perf_event__all64_swap, | 460 | [PERF_RECORD_LOST] = perf_event__all64_swap, |
449 | [PERF_RECORD_READ] = perf_event__read_swap, | 461 | [PERF_RECORD_READ] = perf_event__read_swap, |
450 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, | 462 | [PERF_RECORD_SAMPLE] = perf_event__all64_swap, |
451 | [PERF_RECORD_HEADER_ATTR] = perf_event__attr_swap, | 463 | [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, |
452 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, | 464 | [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, |
453 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, | 465 | [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, |
454 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, | 466 | [PERF_RECORD_HEADER_BUILD_ID] = NULL, |
@@ -490,6 +502,7 @@ static void flush_sample_queue(struct perf_session *s, | |||
490 | struct perf_sample sample; | 502 | struct perf_sample sample; |
491 | u64 limit = os->next_flush; | 503 | u64 limit = os->next_flush; |
492 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | 504 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; |
505 | unsigned idx = 0, progress_next = os->nr_samples / 16; | ||
493 | int ret; | 506 | int ret; |
494 | 507 | ||
495 | if (!ops->ordered_samples || !limit) | 508 | if (!ops->ordered_samples || !limit) |
@@ -509,6 +522,11 @@ static void flush_sample_queue(struct perf_session *s, | |||
509 | os->last_flush = iter->timestamp; | 522 | os->last_flush = iter->timestamp; |
510 | list_del(&iter->list); | 523 | list_del(&iter->list); |
511 | list_add(&iter->list, &os->sample_cache); | 524 | list_add(&iter->list, &os->sample_cache); |
525 | if (++idx >= progress_next) { | ||
526 | progress_next += os->nr_samples / 16; | ||
527 | ui_progress__update(idx, os->nr_samples, | ||
528 | "Processing time ordered events..."); | ||
529 | } | ||
512 | } | 530 | } |
513 | 531 | ||
514 | if (list_empty(head)) { | 532 | if (list_empty(head)) { |
@@ -517,6 +535,8 @@ static void flush_sample_queue(struct perf_session *s, | |||
517 | os->last_sample = | 535 | os->last_sample = |
518 | list_entry(head->prev, struct sample_queue, list); | 536 | list_entry(head->prev, struct sample_queue, list); |
519 | } | 537 | } |
538 | |||
539 | os->nr_samples = 0; | ||
520 | } | 540 | } |
521 | 541 | ||
522 | /* | 542 | /* |
@@ -576,6 +596,7 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s) | |||
576 | u64 timestamp = new->timestamp; | 596 | u64 timestamp = new->timestamp; |
577 | struct list_head *p; | 597 | struct list_head *p; |
578 | 598 | ||
599 | ++os->nr_samples; | ||
579 | os->last_sample = new; | 600 | os->last_sample = new; |
580 | 601 | ||
581 | if (!sample) { | 602 | if (!sample) { |
@@ -708,9 +729,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event, | |||
708 | if (!dump_trace) | 729 | if (!dump_trace) |
709 | return; | 730 | return; |
710 | 731 | ||
711 | printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 "\n", | 732 | printf("(IP, %d): %d/%d: %#" PRIx64 " period: %" PRIu64 " addr: %#" PRIx64 "\n", |
712 | event->header.misc, sample->pid, sample->tid, sample->ip, | 733 | event->header.misc, sample->pid, sample->tid, sample->ip, |
713 | sample->period); | 734 | sample->period, sample->addr); |
714 | 735 | ||
715 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) | 736 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) |
716 | callchain__printf(sample); | 737 | callchain__printf(sample); |
@@ -726,10 +747,27 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
726 | 747 | ||
727 | dump_event(session, event, file_offset, sample); | 748 | dump_event(session, event, file_offset, sample); |
728 | 749 | ||
750 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); | ||
751 | if (evsel != NULL && event->header.type != PERF_RECORD_SAMPLE) { | ||
752 | /* | ||
753 | * XXX We're leaving PERF_RECORD_SAMPLE unnacounted here | ||
754 | * because the tools right now may apply filters, discarding | ||
755 | * some of the samples. For consistency, in the future we | ||
756 | * should have something like nr_filtered_samples and remove | ||
757 | * the sample->period from total_sample_period, etc, KISS for | ||
758 | * now tho. | ||
759 | * | ||
760 | * Also testing against NULL allows us to handle files without | ||
761 | * attr.sample_id_all and/or without PERF_SAMPLE_ID. In the | ||
762 | * future probably it'll be a good idea to restrict event | ||
763 | * processing via perf_session to files with both set. | ||
764 | */ | ||
765 | hists__inc_nr_events(&evsel->hists, event->header.type); | ||
766 | } | ||
767 | |||
729 | switch (event->header.type) { | 768 | switch (event->header.type) { |
730 | case PERF_RECORD_SAMPLE: | 769 | case PERF_RECORD_SAMPLE: |
731 | dump_sample(session, event, sample); | 770 | dump_sample(session, event, sample); |
732 | evsel = perf_evlist__id2evsel(session->evlist, sample->id); | ||
733 | if (evsel == NULL) { | 771 | if (evsel == NULL) { |
734 | ++session->hists.stats.nr_unknown_id; | 772 | ++session->hists.stats.nr_unknown_id; |
735 | return -1; | 773 | return -1; |
@@ -862,11 +900,11 @@ static void perf_session__warn_about_errors(const struct perf_session *session, | |||
862 | const struct perf_event_ops *ops) | 900 | const struct perf_event_ops *ops) |
863 | { | 901 | { |
864 | if (ops->lost == perf_event__process_lost && | 902 | if (ops->lost == perf_event__process_lost && |
865 | session->hists.stats.total_lost != 0) { | 903 | session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) { |
866 | ui__warning("Processed %" PRIu64 " events and LOST %" PRIu64 | 904 | ui__warning("Processed %d events and lost %d chunks!\n\n" |
867 | "!\n\nCheck IO/CPU overload!\n\n", | 905 | "Check IO/CPU overload!\n\n", |
868 | session->hists.stats.total_period, | 906 | session->hists.stats.nr_events[0], |
869 | session->hists.stats.total_lost); | 907 | session->hists.stats.nr_events[PERF_RECORD_LOST]); |
870 | } | 908 | } |
871 | 909 | ||
872 | if (session->hists.stats.nr_unknown_events != 0) { | 910 | if (session->hists.stats.nr_unknown_events != 0) { |
@@ -1000,7 +1038,6 @@ int __perf_session__process_events(struct perf_session *session, | |||
1000 | { | 1038 | { |
1001 | u64 head, page_offset, file_offset, file_pos, progress_next; | 1039 | u64 head, page_offset, file_offset, file_pos, progress_next; |
1002 | int err, mmap_prot, mmap_flags, map_idx = 0; | 1040 | int err, mmap_prot, mmap_flags, map_idx = 0; |
1003 | struct ui_progress *progress; | ||
1004 | size_t page_size, mmap_size; | 1041 | size_t page_size, mmap_size; |
1005 | char *buf, *mmaps[8]; | 1042 | char *buf, *mmaps[8]; |
1006 | union perf_event *event; | 1043 | union perf_event *event; |
@@ -1018,9 +1055,6 @@ int __perf_session__process_events(struct perf_session *session, | |||
1018 | file_size = data_offset + data_size; | 1055 | file_size = data_offset + data_size; |
1019 | 1056 | ||
1020 | progress_next = file_size / 16; | 1057 | progress_next = file_size / 16; |
1021 | progress = ui_progress__new("Processing events...", file_size); | ||
1022 | if (progress == NULL) | ||
1023 | return -1; | ||
1024 | 1058 | ||
1025 | mmap_size = session->mmap_window; | 1059 | mmap_size = session->mmap_window; |
1026 | if (mmap_size > file_size) | 1060 | if (mmap_size > file_size) |
@@ -1083,7 +1117,8 @@ more: | |||
1083 | 1117 | ||
1084 | if (file_pos >= progress_next) { | 1118 | if (file_pos >= progress_next) { |
1085 | progress_next += file_size / 16; | 1119 | progress_next += file_size / 16; |
1086 | ui_progress__update(progress, file_pos); | 1120 | ui_progress__update(file_pos, file_size, |
1121 | "Processing events..."); | ||
1087 | } | 1122 | } |
1088 | 1123 | ||
1089 | if (file_pos < file_size) | 1124 | if (file_pos < file_size) |
@@ -1094,7 +1129,6 @@ more: | |||
1094 | session->ordered_samples.next_flush = ULLONG_MAX; | 1129 | session->ordered_samples.next_flush = ULLONG_MAX; |
1095 | flush_sample_queue(session, ops); | 1130 | flush_sample_queue(session, ops); |
1096 | out_err: | 1131 | out_err: |
1097 | ui_progress__delete(progress); | ||
1098 | perf_session__warn_about_errors(session, ops); | 1132 | perf_session__warn_about_errors(session, ops); |
1099 | perf_session_free_sample_buffers(session); | 1133 | perf_session_free_sample_buffers(session); |
1100 | return err; | 1134 | return err; |
@@ -1202,9 +1236,10 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | |||
1202 | return NULL; | 1236 | return NULL; |
1203 | } | 1237 | } |
1204 | 1238 | ||
1205 | void perf_session__print_symbols(union perf_event *event, | 1239 | void perf_session__print_ip(union perf_event *event, |
1206 | struct perf_sample *sample, | 1240 | struct perf_sample *sample, |
1207 | struct perf_session *session) | 1241 | struct perf_session *session, |
1242 | int print_sym, int print_dso) | ||
1208 | { | 1243 | { |
1209 | struct addr_location al; | 1244 | struct addr_location al; |
1210 | const char *symname, *dsoname; | 1245 | const char *symname, *dsoname; |
@@ -1233,32 +1268,102 @@ void perf_session__print_symbols(union perf_event *event, | |||
1233 | if (!node) | 1268 | if (!node) |
1234 | break; | 1269 | break; |
1235 | 1270 | ||
1236 | if (node->sym && node->sym->name) | 1271 | printf("\t%16" PRIx64, node->ip); |
1237 | symname = node->sym->name; | 1272 | if (print_sym) { |
1273 | if (node->sym && node->sym->name) | ||
1274 | symname = node->sym->name; | ||
1275 | else | ||
1276 | symname = ""; | ||
1277 | |||
1278 | printf(" %s", symname); | ||
1279 | } | ||
1280 | if (print_dso) { | ||
1281 | if (node->map && node->map->dso && node->map->dso->name) | ||
1282 | dsoname = node->map->dso->name; | ||
1283 | else | ||
1284 | dsoname = ""; | ||
1285 | |||
1286 | printf(" (%s)", dsoname); | ||
1287 | } | ||
1288 | printf("\n"); | ||
1289 | |||
1290 | callchain_cursor_advance(cursor); | ||
1291 | } | ||
1292 | |||
1293 | } else { | ||
1294 | printf("%16" PRIx64, sample->ip); | ||
1295 | if (print_sym) { | ||
1296 | if (al.sym && al.sym->name) | ||
1297 | symname = al.sym->name; | ||
1238 | else | 1298 | else |
1239 | symname = ""; | 1299 | symname = ""; |
1240 | 1300 | ||
1241 | if (node->map && node->map->dso && node->map->dso->name) | 1301 | printf(" %s", symname); |
1242 | dsoname = node->map->dso->name; | 1302 | } |
1303 | |||
1304 | if (print_dso) { | ||
1305 | if (al.map && al.map->dso && al.map->dso->name) | ||
1306 | dsoname = al.map->dso->name; | ||
1243 | else | 1307 | else |
1244 | dsoname = ""; | 1308 | dsoname = ""; |
1245 | 1309 | ||
1246 | printf("\t%16" PRIx64 " %s (%s)\n", node->ip, symname, dsoname); | 1310 | printf(" (%s)", dsoname); |
1311 | } | ||
1312 | } | ||
1313 | } | ||
1247 | 1314 | ||
1248 | callchain_cursor_advance(cursor); | 1315 | int perf_session__cpu_bitmap(struct perf_session *session, |
1316 | const char *cpu_list, unsigned long *cpu_bitmap) | ||
1317 | { | ||
1318 | int i; | ||
1319 | struct cpu_map *map; | ||
1320 | |||
1321 | for (i = 0; i < PERF_TYPE_MAX; ++i) { | ||
1322 | struct perf_evsel *evsel; | ||
1323 | |||
1324 | evsel = perf_session__find_first_evtype(session, i); | ||
1325 | if (!evsel) | ||
1326 | continue; | ||
1327 | |||
1328 | if (!(evsel->attr.sample_type & PERF_SAMPLE_CPU)) { | ||
1329 | pr_err("File does not contain CPU events. " | ||
1330 | "Remove -c option to proceed.\n"); | ||
1331 | return -1; | ||
1249 | } | 1332 | } |
1333 | } | ||
1250 | 1334 | ||
1251 | } else { | 1335 | map = cpu_map__new(cpu_list); |
1252 | if (al.sym && al.sym->name) | ||
1253 | symname = al.sym->name; | ||
1254 | else | ||
1255 | symname = ""; | ||
1256 | 1336 | ||
1257 | if (al.map && al.map->dso && al.map->dso->name) | 1337 | for (i = 0; i < map->nr; i++) { |
1258 | dsoname = al.map->dso->name; | 1338 | int cpu = map->map[i]; |
1259 | else | ||
1260 | dsoname = ""; | ||
1261 | 1339 | ||
1262 | printf("%16" PRIx64 " %s (%s)", al.addr, symname, dsoname); | 1340 | if (cpu >= MAX_NR_CPUS) { |
1341 | pr_err("Requested CPU %d too large. " | ||
1342 | "Consider raising MAX_NR_CPUS\n", cpu); | ||
1343 | return -1; | ||
1344 | } | ||
1345 | |||
1346 | set_bit(cpu, cpu_bitmap); | ||
1263 | } | 1347 | } |
1348 | |||
1349 | return 0; | ||
1350 | } | ||
1351 | |||
1352 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, | ||
1353 | bool full) | ||
1354 | { | ||
1355 | struct stat st; | ||
1356 | int ret; | ||
1357 | |||
1358 | if (session == NULL || fp == NULL) | ||
1359 | return; | ||
1360 | |||
1361 | ret = fstat(session->fd, &st); | ||
1362 | if (ret == -1) | ||
1363 | return; | ||
1364 | |||
1365 | fprintf(fp, "# ========\n"); | ||
1366 | fprintf(fp, "# captured on: %s", ctime(&st.st_ctime)); | ||
1367 | perf_header__fprintf_info(session, fp, full); | ||
1368 | fprintf(fp, "# ========\n#\n"); | ||
1264 | } | 1369 | } |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 66d4e1490879..6e393c98eb34 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -23,6 +23,7 @@ struct ordered_samples { | |||
23 | struct sample_queue *sample_buffer; | 23 | struct sample_queue *sample_buffer; |
24 | struct sample_queue *last_sample; | 24 | struct sample_queue *last_sample; |
25 | int sample_buffer_idx; | 25 | int sample_buffer_idx; |
26 | unsigned int nr_samples; | ||
26 | }; | 27 | }; |
27 | 28 | ||
28 | struct perf_session { | 29 | struct perf_session { |
@@ -112,6 +113,7 @@ int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, | |||
112 | u64 addr); | 113 | u64 addr); |
113 | 114 | ||
114 | void mem_bswap_64(void *src, int byte_size); | 115 | void mem_bswap_64(void *src, int byte_size); |
116 | void perf_event__attr_swap(struct perf_event_attr *attr); | ||
115 | 117 | ||
116 | int perf_session__create_kernel_maps(struct perf_session *self); | 118 | int perf_session__create_kernel_maps(struct perf_session *self); |
117 | 119 | ||
@@ -161,14 +163,20 @@ static inline int perf_session__parse_sample(struct perf_session *session, | |||
161 | { | 163 | { |
162 | return perf_event__parse_sample(event, session->sample_type, | 164 | return perf_event__parse_sample(event, session->sample_type, |
163 | session->sample_size, | 165 | session->sample_size, |
164 | session->sample_id_all, sample); | 166 | session->sample_id_all, sample, |
167 | session->header.needs_swap); | ||
165 | } | 168 | } |
166 | 169 | ||
167 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | 170 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, |
168 | unsigned int type); | 171 | unsigned int type); |
169 | 172 | ||
170 | void perf_session__print_symbols(union perf_event *event, | 173 | void perf_session__print_ip(union perf_event *event, |
171 | struct perf_sample *sample, | 174 | struct perf_sample *sample, |
172 | struct perf_session *session); | 175 | struct perf_session *session, |
176 | int print_sym, int print_dso); | ||
173 | 177 | ||
178 | int perf_session__cpu_bitmap(struct perf_session *session, | ||
179 | const char *cpu_list, unsigned long *cpu_bitmap); | ||
180 | |||
181 | void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full); | ||
174 | #endif /* __PERF_SESSION_H */ | 182 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index bbc982f5dd8b..95d370074928 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py | |||
@@ -3,9 +3,27 @@ | |||
3 | from distutils.core import setup, Extension | 3 | from distutils.core import setup, Extension |
4 | from os import getenv | 4 | from os import getenv |
5 | 5 | ||
6 | from distutils.command.build_ext import build_ext as _build_ext | ||
7 | from distutils.command.install_lib import install_lib as _install_lib | ||
8 | |||
9 | class build_ext(_build_ext): | ||
10 | def finalize_options(self): | ||
11 | _build_ext.finalize_options(self) | ||
12 | self.build_lib = build_lib | ||
13 | self.build_temp = build_tmp | ||
14 | |||
15 | class install_lib(_install_lib): | ||
16 | def finalize_options(self): | ||
17 | _install_lib.finalize_options(self) | ||
18 | self.build_dir = build_lib | ||
19 | |||
20 | |||
6 | cflags = ['-fno-strict-aliasing', '-Wno-write-strings'] | 21 | cflags = ['-fno-strict-aliasing', '-Wno-write-strings'] |
7 | cflags += getenv('CFLAGS', '').split() | 22 | cflags += getenv('CFLAGS', '').split() |
8 | 23 | ||
24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') | ||
25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') | ||
26 | |||
9 | perf = Extension('perf', | 27 | perf = Extension('perf', |
10 | sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', | 28 | sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', |
11 | 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', | 29 | 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', |
@@ -21,4 +39,5 @@ setup(name='perf', | |||
21 | author_email='acme@redhat.com', | 39 | author_email='acme@redhat.com', |
22 | license='GPLv2', | 40 | license='GPLv2', |
23 | url='http://perf.wiki.kernel.org', | 41 | url='http://perf.wiki.kernel.org', |
24 | ext_modules=[perf]) | 42 | ext_modules=[perf], |
43 | cmdclass={'build_ext': build_ext, 'install_lib': install_lib}) | ||
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index f44fa541d56e..16da30d8d765 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -15,95 +15,6 @@ char * field_sep; | |||
15 | 15 | ||
16 | LIST_HEAD(hist_entry__sort_list); | 16 | LIST_HEAD(hist_entry__sort_list); |
17 | 17 | ||
18 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | ||
19 | size_t size, unsigned int width); | ||
20 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | ||
21 | size_t size, unsigned int width); | ||
22 | static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | ||
23 | size_t size, unsigned int width); | ||
24 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | ||
25 | size_t size, unsigned int width); | ||
26 | static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | ||
27 | size_t size, unsigned int width); | ||
28 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | ||
29 | size_t size, unsigned int width); | ||
30 | |||
31 | struct sort_entry sort_thread = { | ||
32 | .se_header = "Command: Pid", | ||
33 | .se_cmp = sort__thread_cmp, | ||
34 | .se_snprintf = hist_entry__thread_snprintf, | ||
35 | .se_width_idx = HISTC_THREAD, | ||
36 | }; | ||
37 | |||
38 | struct sort_entry sort_comm = { | ||
39 | .se_header = "Command", | ||
40 | .se_cmp = sort__comm_cmp, | ||
41 | .se_collapse = sort__comm_collapse, | ||
42 | .se_snprintf = hist_entry__comm_snprintf, | ||
43 | .se_width_idx = HISTC_COMM, | ||
44 | }; | ||
45 | |||
46 | struct sort_entry sort_dso = { | ||
47 | .se_header = "Shared Object", | ||
48 | .se_cmp = sort__dso_cmp, | ||
49 | .se_snprintf = hist_entry__dso_snprintf, | ||
50 | .se_width_idx = HISTC_DSO, | ||
51 | }; | ||
52 | |||
53 | struct sort_entry sort_sym = { | ||
54 | .se_header = "Symbol", | ||
55 | .se_cmp = sort__sym_cmp, | ||
56 | .se_snprintf = hist_entry__sym_snprintf, | ||
57 | .se_width_idx = HISTC_SYMBOL, | ||
58 | }; | ||
59 | |||
60 | struct sort_entry sort_parent = { | ||
61 | .se_header = "Parent symbol", | ||
62 | .se_cmp = sort__parent_cmp, | ||
63 | .se_snprintf = hist_entry__parent_snprintf, | ||
64 | .se_width_idx = HISTC_PARENT, | ||
65 | }; | ||
66 | |||
67 | struct sort_entry sort_cpu = { | ||
68 | .se_header = "CPU", | ||
69 | .se_cmp = sort__cpu_cmp, | ||
70 | .se_snprintf = hist_entry__cpu_snprintf, | ||
71 | .se_width_idx = HISTC_CPU, | ||
72 | }; | ||
73 | |||
74 | struct sort_dimension { | ||
75 | const char *name; | ||
76 | struct sort_entry *entry; | ||
77 | int taken; | ||
78 | }; | ||
79 | |||
80 | static struct sort_dimension sort_dimensions[] = { | ||
81 | { .name = "pid", .entry = &sort_thread, }, | ||
82 | { .name = "comm", .entry = &sort_comm, }, | ||
83 | { .name = "dso", .entry = &sort_dso, }, | ||
84 | { .name = "symbol", .entry = &sort_sym, }, | ||
85 | { .name = "parent", .entry = &sort_parent, }, | ||
86 | { .name = "cpu", .entry = &sort_cpu, }, | ||
87 | }; | ||
88 | |||
89 | int64_t cmp_null(void *l, void *r) | ||
90 | { | ||
91 | if (!l && !r) | ||
92 | return 0; | ||
93 | else if (!l) | ||
94 | return -1; | ||
95 | else | ||
96 | return 1; | ||
97 | } | ||
98 | |||
99 | /* --sort pid */ | ||
100 | |||
101 | int64_t | ||
102 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
103 | { | ||
104 | return right->thread->pid - left->thread->pid; | ||
105 | } | ||
106 | |||
107 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | 18 | static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) |
108 | { | 19 | { |
109 | int n; | 20 | int n; |
@@ -125,6 +36,24 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | |||
125 | return n; | 36 | return n; |
126 | } | 37 | } |
127 | 38 | ||
39 | static int64_t cmp_null(void *l, void *r) | ||
40 | { | ||
41 | if (!l && !r) | ||
42 | return 0; | ||
43 | else if (!l) | ||
44 | return -1; | ||
45 | else | ||
46 | return 1; | ||
47 | } | ||
48 | |||
49 | /* --sort pid */ | ||
50 | |||
51 | static int64_t | ||
52 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | ||
53 | { | ||
54 | return right->thread->pid - left->thread->pid; | ||
55 | } | ||
56 | |||
128 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | 57 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, |
129 | size_t size, unsigned int width) | 58 | size_t size, unsigned int width) |
130 | { | 59 | { |
@@ -132,15 +61,50 @@ static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | |||
132 | self->thread->comm ?: "", self->thread->pid); | 61 | self->thread->comm ?: "", self->thread->pid); |
133 | } | 62 | } |
134 | 63 | ||
64 | struct sort_entry sort_thread = { | ||
65 | .se_header = "Command: Pid", | ||
66 | .se_cmp = sort__thread_cmp, | ||
67 | .se_snprintf = hist_entry__thread_snprintf, | ||
68 | .se_width_idx = HISTC_THREAD, | ||
69 | }; | ||
70 | |||
71 | /* --sort comm */ | ||
72 | |||
73 | static int64_t | ||
74 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | ||
75 | { | ||
76 | return right->thread->pid - left->thread->pid; | ||
77 | } | ||
78 | |||
79 | static int64_t | ||
80 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
81 | { | ||
82 | char *comm_l = left->thread->comm; | ||
83 | char *comm_r = right->thread->comm; | ||
84 | |||
85 | if (!comm_l || !comm_r) | ||
86 | return cmp_null(comm_l, comm_r); | ||
87 | |||
88 | return strcmp(comm_l, comm_r); | ||
89 | } | ||
90 | |||
135 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | 91 | static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, |
136 | size_t size, unsigned int width) | 92 | size_t size, unsigned int width) |
137 | { | 93 | { |
138 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); | 94 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); |
139 | } | 95 | } |
140 | 96 | ||
97 | struct sort_entry sort_comm = { | ||
98 | .se_header = "Command", | ||
99 | .se_cmp = sort__comm_cmp, | ||
100 | .se_collapse = sort__comm_collapse, | ||
101 | .se_snprintf = hist_entry__comm_snprintf, | ||
102 | .se_width_idx = HISTC_COMM, | ||
103 | }; | ||
104 | |||
141 | /* --sort dso */ | 105 | /* --sort dso */ |
142 | 106 | ||
143 | int64_t | 107 | static int64_t |
144 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | 108 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
145 | { | 109 | { |
146 | struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; | 110 | struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; |
@@ -173,18 +137,31 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | |||
173 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); | 137 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); |
174 | } | 138 | } |
175 | 139 | ||
140 | struct sort_entry sort_dso = { | ||
141 | .se_header = "Shared Object", | ||
142 | .se_cmp = sort__dso_cmp, | ||
143 | .se_snprintf = hist_entry__dso_snprintf, | ||
144 | .se_width_idx = HISTC_DSO, | ||
145 | }; | ||
146 | |||
176 | /* --sort symbol */ | 147 | /* --sort symbol */ |
177 | 148 | ||
178 | int64_t | 149 | static int64_t |
179 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 150 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
180 | { | 151 | { |
181 | u64 ip_l, ip_r; | 152 | u64 ip_l, ip_r; |
182 | 153 | ||
154 | if (!left->ms.sym && !right->ms.sym) | ||
155 | return right->level - left->level; | ||
156 | |||
157 | if (!left->ms.sym || !right->ms.sym) | ||
158 | return cmp_null(left->ms.sym, right->ms.sym); | ||
159 | |||
183 | if (left->ms.sym == right->ms.sym) | 160 | if (left->ms.sym == right->ms.sym) |
184 | return 0; | 161 | return 0; |
185 | 162 | ||
186 | ip_l = left->ms.sym ? left->ms.sym->start : left->ip; | 163 | ip_l = left->ms.sym->start; |
187 | ip_r = right->ms.sym ? right->ms.sym->start : right->ip; | 164 | ip_r = right->ms.sym->start; |
188 | 165 | ||
189 | return (int64_t)(ip_r - ip_l); | 166 | return (int64_t)(ip_r - ip_l); |
190 | } | 167 | } |
@@ -200,7 +177,9 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |||
200 | BITS_PER_LONG / 4, self->ip, o); | 177 | BITS_PER_LONG / 4, self->ip, o); |
201 | } | 178 | } |
202 | 179 | ||
203 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); | 180 | if (!sort_dso.elide) |
181 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); | ||
182 | |||
204 | if (self->ms.sym) | 183 | if (self->ms.sym) |
205 | ret += repsep_snprintf(bf + ret, size - ret, "%s", | 184 | ret += repsep_snprintf(bf + ret, size - ret, "%s", |
206 | self->ms.sym->name); | 185 | self->ms.sym->name); |
@@ -211,29 +190,16 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | |||
211 | return ret; | 190 | return ret; |
212 | } | 191 | } |
213 | 192 | ||
214 | /* --sort comm */ | 193 | struct sort_entry sort_sym = { |
215 | 194 | .se_header = "Symbol", | |
216 | int64_t | 195 | .se_cmp = sort__sym_cmp, |
217 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | 196 | .se_snprintf = hist_entry__sym_snprintf, |
218 | { | 197 | .se_width_idx = HISTC_SYMBOL, |
219 | return right->thread->pid - left->thread->pid; | 198 | }; |
220 | } | ||
221 | |||
222 | int64_t | ||
223 | sort__comm_collapse(struct hist_entry *left, struct hist_entry *right) | ||
224 | { | ||
225 | char *comm_l = left->thread->comm; | ||
226 | char *comm_r = right->thread->comm; | ||
227 | |||
228 | if (!comm_l || !comm_r) | ||
229 | return cmp_null(comm_l, comm_r); | ||
230 | |||
231 | return strcmp(comm_l, comm_r); | ||
232 | } | ||
233 | 199 | ||
234 | /* --sort parent */ | 200 | /* --sort parent */ |
235 | 201 | ||
236 | int64_t | 202 | static int64_t |
237 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) | 203 | sort__parent_cmp(struct hist_entry *left, struct hist_entry *right) |
238 | { | 204 | { |
239 | struct symbol *sym_l = left->parent; | 205 | struct symbol *sym_l = left->parent; |
@@ -252,9 +218,16 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | |||
252 | self->parent ? self->parent->name : "[other]"); | 218 | self->parent ? self->parent->name : "[other]"); |
253 | } | 219 | } |
254 | 220 | ||
221 | struct sort_entry sort_parent = { | ||
222 | .se_header = "Parent symbol", | ||
223 | .se_cmp = sort__parent_cmp, | ||
224 | .se_snprintf = hist_entry__parent_snprintf, | ||
225 | .se_width_idx = HISTC_PARENT, | ||
226 | }; | ||
227 | |||
255 | /* --sort cpu */ | 228 | /* --sort cpu */ |
256 | 229 | ||
257 | int64_t | 230 | static int64_t |
258 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | 231 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) |
259 | { | 232 | { |
260 | return right->cpu - left->cpu; | 233 | return right->cpu - left->cpu; |
@@ -266,6 +239,28 @@ static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | |||
266 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); | 239 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); |
267 | } | 240 | } |
268 | 241 | ||
242 | struct sort_entry sort_cpu = { | ||
243 | .se_header = "CPU", | ||
244 | .se_cmp = sort__cpu_cmp, | ||
245 | .se_snprintf = hist_entry__cpu_snprintf, | ||
246 | .se_width_idx = HISTC_CPU, | ||
247 | }; | ||
248 | |||
249 | struct sort_dimension { | ||
250 | const char *name; | ||
251 | struct sort_entry *entry; | ||
252 | int taken; | ||
253 | }; | ||
254 | |||
255 | static struct sort_dimension sort_dimensions[] = { | ||
256 | { .name = "pid", .entry = &sort_thread, }, | ||
257 | { .name = "comm", .entry = &sort_comm, }, | ||
258 | { .name = "dso", .entry = &sort_dso, }, | ||
259 | { .name = "symbol", .entry = &sort_sym, }, | ||
260 | { .name = "parent", .entry = &sort_parent, }, | ||
261 | { .name = "cpu", .entry = &sort_cpu, }, | ||
262 | }; | ||
263 | |||
269 | int sort_dimension__add(const char *tok) | 264 | int sort_dimension__add(const char *tok) |
270 | { | 265 | { |
271 | unsigned int i; | 266 | unsigned int i; |
@@ -273,15 +268,9 @@ int sort_dimension__add(const char *tok) | |||
273 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | 268 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { |
274 | struct sort_dimension *sd = &sort_dimensions[i]; | 269 | struct sort_dimension *sd = &sort_dimensions[i]; |
275 | 270 | ||
276 | if (sd->taken) | ||
277 | continue; | ||
278 | |||
279 | if (strncasecmp(tok, sd->name, strlen(tok))) | 271 | if (strncasecmp(tok, sd->name, strlen(tok))) |
280 | continue; | 272 | continue; |
281 | 273 | ||
282 | if (sd->entry->se_collapse) | ||
283 | sort__need_collapse = 1; | ||
284 | |||
285 | if (sd->entry == &sort_parent) { | 274 | if (sd->entry == &sort_parent) { |
286 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | 275 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); |
287 | if (ret) { | 276 | if (ret) { |
@@ -294,6 +283,12 @@ int sort_dimension__add(const char *tok) | |||
294 | sort__has_parent = 1; | 283 | sort__has_parent = 1; |
295 | } | 284 | } |
296 | 285 | ||
286 | if (sd->taken) | ||
287 | return 0; | ||
288 | |||
289 | if (sd->entry->se_collapse) | ||
290 | sort__need_collapse = 1; | ||
291 | |||
297 | if (list_empty(&hist_entry__sort_list)) { | 292 | if (list_empty(&hist_entry__sort_list)) { |
298 | if (!strcmp(sd->name, "pid")) | 293 | if (!strcmp(sd->name, "pid")) |
299 | sort__first_dimension = SORT_PID; | 294 | sort__first_dimension = SORT_PID; |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 0b91053a7d11..3f67ae395752 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -45,6 +45,7 @@ extern enum sort_type sort__first_dimension; | |||
45 | * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding | 45 | * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding |
46 | */ | 46 | */ |
47 | struct hist_entry { | 47 | struct hist_entry { |
48 | struct rb_node rb_node_in; | ||
48 | struct rb_node rb_node; | 49 | struct rb_node rb_node; |
49 | u64 period; | 50 | u64 period; |
50 | u64 period_sys; | 51 | u64 period_sys; |
@@ -63,6 +64,7 @@ struct hist_entry { | |||
63 | 64 | ||
64 | bool init_have_children; | 65 | bool init_have_children; |
65 | char level; | 66 | char level; |
67 | bool used; | ||
66 | u8 filtered; | 68 | u8 filtered; |
67 | struct symbol *parent; | 69 | struct symbol *parent; |
68 | union { | 70 | union { |
@@ -103,20 +105,6 @@ extern struct sort_entry sort_thread; | |||
103 | extern struct list_head hist_entry__sort_list; | 105 | extern struct list_head hist_entry__sort_list; |
104 | 106 | ||
105 | void setup_sorting(const char * const usagestr[], const struct option *opts); | 107 | void setup_sorting(const char * const usagestr[], const struct option *opts); |
106 | |||
107 | extern size_t sort__thread_print(FILE *, struct hist_entry *, unsigned int); | ||
108 | extern size_t sort__comm_print(FILE *, struct hist_entry *, unsigned int); | ||
109 | extern size_t sort__dso_print(FILE *, struct hist_entry *, unsigned int); | ||
110 | extern size_t sort__sym_print(FILE *, struct hist_entry *, unsigned int __used); | ||
111 | extern int64_t cmp_null(void *, void *); | ||
112 | extern int64_t sort__thread_cmp(struct hist_entry *, struct hist_entry *); | ||
113 | extern int64_t sort__comm_cmp(struct hist_entry *, struct hist_entry *); | ||
114 | extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *); | ||
115 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); | ||
116 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | ||
117 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | ||
118 | int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right); | ||
119 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | ||
120 | extern int sort_dimension__add(const char *); | 108 | extern int sort_dimension__add(const char *); |
121 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 109 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, |
122 | const char *list_name, FILE *fp); | 110 | const char *list_name, FILE *fp); |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index b9a985dadd08..d5836382ff2c 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -294,3 +294,22 @@ bool strlazymatch(const char *str, const char *pat) | |||
294 | { | 294 | { |
295 | return __match_glob(str, pat, true); | 295 | return __match_glob(str, pat, true); |
296 | } | 296 | } |
297 | |||
298 | /** | ||
299 | * strtailcmp - Compare the tail of two strings | ||
300 | * @s1: 1st string to be compared | ||
301 | * @s2: 2nd string to be compared | ||
302 | * | ||
303 | * Return 0 if whole of either string is same as another's tail part. | ||
304 | */ | ||
305 | int strtailcmp(const char *s1, const char *s2) | ||
306 | { | ||
307 | int i1 = strlen(s1); | ||
308 | int i2 = strlen(s2); | ||
309 | while (--i1 >= 0 && --i2 >= 0) { | ||
310 | if (s1[i1] != s2[i2]) | ||
311 | return s1[i1] - s2[i2]; | ||
312 | } | ||
313 | return 0; | ||
314 | } | ||
315 | |||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index eec196329fd9..632b50c7bc26 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -24,7 +24,7 @@ | |||
24 | #include <sys/utsname.h> | 24 | #include <sys/utsname.h> |
25 | 25 | ||
26 | #ifndef KSYM_NAME_LEN | 26 | #ifndef KSYM_NAME_LEN |
27 | #define KSYM_NAME_LEN 128 | 27 | #define KSYM_NAME_LEN 256 |
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | #ifndef NT_GNU_BUILD_ID | 30 | #ifndef NT_GNU_BUILD_ID |
@@ -46,6 +46,7 @@ struct symbol_conf symbol_conf = { | |||
46 | .exclude_other = true, | 46 | .exclude_other = true, |
47 | .use_modules = true, | 47 | .use_modules = true, |
48 | .try_vmlinux_path = true, | 48 | .try_vmlinux_path = true, |
49 | .annotate_src = true, | ||
49 | .symfs = "", | 50 | .symfs = "", |
50 | }; | 51 | }; |
51 | 52 | ||
@@ -74,16 +75,104 @@ static void dso__set_sorted_by_name(struct dso *dso, enum map_type type) | |||
74 | 75 | ||
75 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) | 76 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) |
76 | { | 77 | { |
78 | symbol_type = toupper(symbol_type); | ||
79 | |||
77 | switch (map_type) { | 80 | switch (map_type) { |
78 | case MAP__FUNCTION: | 81 | case MAP__FUNCTION: |
79 | return symbol_type == 'T' || symbol_type == 'W'; | 82 | return symbol_type == 'T' || symbol_type == 'W'; |
80 | case MAP__VARIABLE: | 83 | case MAP__VARIABLE: |
81 | return symbol_type == 'D' || symbol_type == 'd'; | 84 | return symbol_type == 'D'; |
82 | default: | 85 | default: |
83 | return false; | 86 | return false; |
84 | } | 87 | } |
85 | } | 88 | } |
86 | 89 | ||
90 | static int prefix_underscores_count(const char *str) | ||
91 | { | ||
92 | const char *tail = str; | ||
93 | |||
94 | while (*tail == '_') | ||
95 | tail++; | ||
96 | |||
97 | return tail - str; | ||
98 | } | ||
99 | |||
100 | #define SYMBOL_A 0 | ||
101 | #define SYMBOL_B 1 | ||
102 | |||
103 | static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | ||
104 | { | ||
105 | s64 a; | ||
106 | s64 b; | ||
107 | |||
108 | /* Prefer a symbol with non zero length */ | ||
109 | a = syma->end - syma->start; | ||
110 | b = symb->end - symb->start; | ||
111 | if ((b == 0) && (a > 0)) | ||
112 | return SYMBOL_A; | ||
113 | else if ((a == 0) && (b > 0)) | ||
114 | return SYMBOL_B; | ||
115 | |||
116 | /* Prefer a non weak symbol over a weak one */ | ||
117 | a = syma->binding == STB_WEAK; | ||
118 | b = symb->binding == STB_WEAK; | ||
119 | if (b && !a) | ||
120 | return SYMBOL_A; | ||
121 | if (a && !b) | ||
122 | return SYMBOL_B; | ||
123 | |||
124 | /* Prefer a global symbol over a non global one */ | ||
125 | a = syma->binding == STB_GLOBAL; | ||
126 | b = symb->binding == STB_GLOBAL; | ||
127 | if (a && !b) | ||
128 | return SYMBOL_A; | ||
129 | if (b && !a) | ||
130 | return SYMBOL_B; | ||
131 | |||
132 | /* Prefer a symbol with less underscores */ | ||
133 | a = prefix_underscores_count(syma->name); | ||
134 | b = prefix_underscores_count(symb->name); | ||
135 | if (b > a) | ||
136 | return SYMBOL_A; | ||
137 | else if (a > b) | ||
138 | return SYMBOL_B; | ||
139 | |||
140 | /* If all else fails, choose the symbol with the longest name */ | ||
141 | if (strlen(syma->name) >= strlen(symb->name)) | ||
142 | return SYMBOL_A; | ||
143 | else | ||
144 | return SYMBOL_B; | ||
145 | } | ||
146 | |||
147 | static void symbols__fixup_duplicate(struct rb_root *symbols) | ||
148 | { | ||
149 | struct rb_node *nd; | ||
150 | struct symbol *curr, *next; | ||
151 | |||
152 | nd = rb_first(symbols); | ||
153 | |||
154 | while (nd) { | ||
155 | curr = rb_entry(nd, struct symbol, rb_node); | ||
156 | again: | ||
157 | nd = rb_next(&curr->rb_node); | ||
158 | next = rb_entry(nd, struct symbol, rb_node); | ||
159 | |||
160 | if (!nd) | ||
161 | break; | ||
162 | |||
163 | if (curr->start != next->start) | ||
164 | continue; | ||
165 | |||
166 | if (choose_best_symbol(curr, next) == SYMBOL_A) { | ||
167 | rb_erase(&next->rb_node, symbols); | ||
168 | goto again; | ||
169 | } else { | ||
170 | nd = rb_next(&curr->rb_node); | ||
171 | rb_erase(&curr->rb_node, symbols); | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
87 | static void symbols__fixup_end(struct rb_root *symbols) | 176 | static void symbols__fixup_end(struct rb_root *symbols) |
88 | { | 177 | { |
89 | struct rb_node *nd, *prevnd = rb_first(symbols); | 178 | struct rb_node *nd, *prevnd = rb_first(symbols); |
@@ -438,18 +527,11 @@ int kallsyms__parse(const char *filename, void *arg, | |||
438 | char *line = NULL; | 527 | char *line = NULL; |
439 | size_t n; | 528 | size_t n; |
440 | int err = -1; | 529 | int err = -1; |
441 | u64 prev_start = 0; | ||
442 | char prev_symbol_type = 0; | ||
443 | char *prev_symbol_name; | ||
444 | FILE *file = fopen(filename, "r"); | 530 | FILE *file = fopen(filename, "r"); |
445 | 531 | ||
446 | if (file == NULL) | 532 | if (file == NULL) |
447 | goto out_failure; | 533 | goto out_failure; |
448 | 534 | ||
449 | prev_symbol_name = malloc(KSYM_NAME_LEN); | ||
450 | if (prev_symbol_name == NULL) | ||
451 | goto out_close; | ||
452 | |||
453 | err = 0; | 535 | err = 0; |
454 | 536 | ||
455 | while (!feof(file)) { | 537 | while (!feof(file)) { |
@@ -470,7 +552,7 @@ int kallsyms__parse(const char *filename, void *arg, | |||
470 | if (len + 2 >= line_len) | 552 | if (len + 2 >= line_len) |
471 | continue; | 553 | continue; |
472 | 554 | ||
473 | symbol_type = toupper(line[len]); | 555 | symbol_type = line[len]; |
474 | len += 2; | 556 | len += 2; |
475 | symbol_name = line + len; | 557 | symbol_name = line + len; |
476 | len = line_len - len; | 558 | len = line_len - len; |
@@ -480,24 +562,18 @@ int kallsyms__parse(const char *filename, void *arg, | |||
480 | break; | 562 | break; |
481 | } | 563 | } |
482 | 564 | ||
483 | if (prev_symbol_type) { | 565 | /* |
484 | u64 end = start; | 566 | * module symbols are not sorted so we add all |
485 | if (end != prev_start) | 567 | * symbols with zero length and rely on |
486 | --end; | 568 | * symbols__fixup_end() to fix it up. |
487 | err = process_symbol(arg, prev_symbol_name, | 569 | */ |
488 | prev_symbol_type, prev_start, end); | 570 | err = process_symbol(arg, symbol_name, |
489 | if (err) | 571 | symbol_type, start, start); |
490 | break; | 572 | if (err) |
491 | } | 573 | break; |
492 | |||
493 | memcpy(prev_symbol_name, symbol_name, len + 1); | ||
494 | prev_symbol_type = symbol_type; | ||
495 | prev_start = start; | ||
496 | } | 574 | } |
497 | 575 | ||
498 | free(prev_symbol_name); | ||
499 | free(line); | 576 | free(line); |
500 | out_close: | ||
501 | fclose(file); | 577 | fclose(file); |
502 | return err; | 578 | return err; |
503 | 579 | ||
@@ -703,6 +779,9 @@ int dso__load_kallsyms(struct dso *dso, const char *filename, | |||
703 | if (dso__load_all_kallsyms(dso, filename, map) < 0) | 779 | if (dso__load_all_kallsyms(dso, filename, map) < 0) |
704 | return -1; | 780 | return -1; |
705 | 781 | ||
782 | symbols__fixup_duplicate(&dso->symbols[map->type]); | ||
783 | symbols__fixup_end(&dso->symbols[map->type]); | ||
784 | |||
706 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 785 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
707 | dso->symtab_type = SYMTAB__GUEST_KALLSYMS; | 786 | dso->symtab_type = SYMTAB__GUEST_KALLSYMS; |
708 | else | 787 | else |
@@ -1092,8 +1171,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name, | |||
1092 | if (dso->has_build_id) { | 1171 | if (dso->has_build_id) { |
1093 | u8 build_id[BUILD_ID_SIZE]; | 1172 | u8 build_id[BUILD_ID_SIZE]; |
1094 | 1173 | ||
1095 | if (elf_read_build_id(elf, build_id, | 1174 | if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) |
1096 | BUILD_ID_SIZE) != BUILD_ID_SIZE) | ||
1097 | goto out_elf_end; | 1175 | goto out_elf_end; |
1098 | 1176 | ||
1099 | if (!dso__build_id_equal(dso, build_id)) | 1177 | if (!dso__build_id_equal(dso, build_id)) |
@@ -1111,6 +1189,8 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name, | |||
1111 | } | 1189 | } |
1112 | 1190 | ||
1113 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); | 1191 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); |
1192 | if (opdshdr.sh_type != SHT_PROGBITS) | ||
1193 | opdsec = NULL; | ||
1114 | if (opdsec) | 1194 | if (opdsec) |
1115 | opddata = elf_rawdata(opdsec, NULL); | 1195 | opddata = elf_rawdata(opdsec, NULL); |
1116 | 1196 | ||
@@ -1276,6 +1356,7 @@ new_symbol: | |||
1276 | * For misannotated, zeroed, ASM function sizes. | 1356 | * For misannotated, zeroed, ASM function sizes. |
1277 | */ | 1357 | */ |
1278 | if (nr > 0) { | 1358 | if (nr > 0) { |
1359 | symbols__fixup_duplicate(&dso->symbols[map->type]); | ||
1279 | symbols__fixup_end(&dso->symbols[map->type]); | 1360 | symbols__fixup_end(&dso->symbols[map->type]); |
1280 | if (kmap) { | 1361 | if (kmap) { |
1281 | /* | 1362 | /* |
@@ -1362,8 +1443,8 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) | |||
1362 | ptr = data->d_buf; | 1443 | ptr = data->d_buf; |
1363 | while (ptr < (data->d_buf + data->d_size)) { | 1444 | while (ptr < (data->d_buf + data->d_size)) { |
1364 | GElf_Nhdr *nhdr = ptr; | 1445 | GElf_Nhdr *nhdr = ptr; |
1365 | int namesz = NOTE_ALIGN(nhdr->n_namesz), | 1446 | size_t namesz = NOTE_ALIGN(nhdr->n_namesz), |
1366 | descsz = NOTE_ALIGN(nhdr->n_descsz); | 1447 | descsz = NOTE_ALIGN(nhdr->n_descsz); |
1367 | const char *name; | 1448 | const char *name; |
1368 | 1449 | ||
1369 | ptr += sizeof(*nhdr); | 1450 | ptr += sizeof(*nhdr); |
@@ -1372,8 +1453,10 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) | |||
1372 | if (nhdr->n_type == NT_GNU_BUILD_ID && | 1453 | if (nhdr->n_type == NT_GNU_BUILD_ID && |
1373 | nhdr->n_namesz == sizeof("GNU")) { | 1454 | nhdr->n_namesz == sizeof("GNU")) { |
1374 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { | 1455 | if (memcmp(name, "GNU", sizeof("GNU")) == 0) { |
1375 | memcpy(bf, ptr, BUILD_ID_SIZE); | 1456 | size_t sz = min(size, descsz); |
1376 | err = BUILD_ID_SIZE; | 1457 | memcpy(bf, ptr, sz); |
1458 | memset(bf + sz, 0, size - sz); | ||
1459 | err = descsz; | ||
1377 | break; | 1460 | break; |
1378 | } | 1461 | } |
1379 | } | 1462 | } |
@@ -1425,7 +1508,7 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | |||
1425 | while (1) { | 1508 | while (1) { |
1426 | char bf[BUFSIZ]; | 1509 | char bf[BUFSIZ]; |
1427 | GElf_Nhdr nhdr; | 1510 | GElf_Nhdr nhdr; |
1428 | int namesz, descsz; | 1511 | size_t namesz, descsz; |
1429 | 1512 | ||
1430 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) | 1513 | if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) |
1431 | break; | 1514 | break; |
@@ -1434,15 +1517,16 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) | |||
1434 | descsz = NOTE_ALIGN(nhdr.n_descsz); | 1517 | descsz = NOTE_ALIGN(nhdr.n_descsz); |
1435 | if (nhdr.n_type == NT_GNU_BUILD_ID && | 1518 | if (nhdr.n_type == NT_GNU_BUILD_ID && |
1436 | nhdr.n_namesz == sizeof("GNU")) { | 1519 | nhdr.n_namesz == sizeof("GNU")) { |
1437 | if (read(fd, bf, namesz) != namesz) | 1520 | if (read(fd, bf, namesz) != (ssize_t)namesz) |
1438 | break; | 1521 | break; |
1439 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { | 1522 | if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { |
1440 | if (read(fd, build_id, | 1523 | size_t sz = min(descsz, size); |
1441 | BUILD_ID_SIZE) == BUILD_ID_SIZE) { | 1524 | if (read(fd, build_id, sz) == (ssize_t)sz) { |
1525 | memset(build_id + sz, 0, size - sz); | ||
1442 | err = 0; | 1526 | err = 0; |
1443 | break; | 1527 | break; |
1444 | } | 1528 | } |
1445 | } else if (read(fd, bf, descsz) != descsz) | 1529 | } else if (read(fd, bf, descsz) != (ssize_t)descsz) |
1446 | break; | 1530 | break; |
1447 | } else { | 1531 | } else { |
1448 | int n = namesz + descsz; | 1532 | int n = namesz + descsz; |
@@ -1504,6 +1588,17 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1504 | dso->adjust_symbols = 0; | 1588 | dso->adjust_symbols = 0; |
1505 | 1589 | ||
1506 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { | 1590 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { |
1591 | struct stat st; | ||
1592 | |||
1593 | if (lstat(dso->name, &st) < 0) | ||
1594 | return -1; | ||
1595 | |||
1596 | if (st.st_uid && (st.st_uid != geteuid())) { | ||
1597 | pr_warning("File %s not owned by current user or root, " | ||
1598 | "ignoring it.\n", dso->name); | ||
1599 | return -1; | ||
1600 | } | ||
1601 | |||
1507 | ret = dso__load_perf_map(dso, map, filter); | 1602 | ret = dso__load_perf_map(dso, map, filter); |
1508 | dso->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT : | 1603 | dso->symtab_type = ret > 0 ? SYMTAB__JAVA_JIT : |
1509 | SYMTAB__NOT_FOUND; | 1604 | SYMTAB__NOT_FOUND; |
@@ -2170,27 +2265,22 @@ size_t machines__fprintf_dsos_buildid(struct rb_root *machines, | |||
2170 | return ret; | 2265 | return ret; |
2171 | } | 2266 | } |
2172 | 2267 | ||
2173 | struct dso *dso__new_kernel(const char *name) | 2268 | static struct dso* |
2269 | dso__kernel_findnew(struct machine *machine, const char *name, | ||
2270 | const char *short_name, int dso_type) | ||
2174 | { | 2271 | { |
2175 | struct dso *dso = dso__new(name ?: "[kernel.kallsyms]"); | 2272 | /* |
2176 | 2273 | * The kernel dso could be created by build_id processing. | |
2177 | if (dso != NULL) { | 2274 | */ |
2178 | dso__set_short_name(dso, "[kernel]"); | 2275 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name); |
2179 | dso->kernel = DSO_TYPE_KERNEL; | ||
2180 | } | ||
2181 | |||
2182 | return dso; | ||
2183 | } | ||
2184 | 2276 | ||
2185 | static struct dso *dso__new_guest_kernel(struct machine *machine, | 2277 | /* |
2186 | const char *name) | 2278 | * We need to run this in all cases, since during the build_id |
2187 | { | 2279 | * processing we had no idea this was the kernel dso. |
2188 | char bf[PATH_MAX]; | 2280 | */ |
2189 | struct dso *dso = dso__new(name ?: machine__mmap_name(machine, bf, | ||
2190 | sizeof(bf))); | ||
2191 | if (dso != NULL) { | 2281 | if (dso != NULL) { |
2192 | dso__set_short_name(dso, "[guest.kernel]"); | 2282 | dso__set_short_name(dso, short_name); |
2193 | dso->kernel = DSO_TYPE_GUEST_KERNEL; | 2283 | dso->kernel = dso_type; |
2194 | } | 2284 | } |
2195 | 2285 | ||
2196 | return dso; | 2286 | return dso; |
@@ -2208,24 +2298,36 @@ void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) | |||
2208 | dso->has_build_id = true; | 2298 | dso->has_build_id = true; |
2209 | } | 2299 | } |
2210 | 2300 | ||
2211 | static struct dso *machine__create_kernel(struct machine *machine) | 2301 | static struct dso *machine__get_kernel(struct machine *machine) |
2212 | { | 2302 | { |
2213 | const char *vmlinux_name = NULL; | 2303 | const char *vmlinux_name = NULL; |
2214 | struct dso *kernel; | 2304 | struct dso *kernel; |
2215 | 2305 | ||
2216 | if (machine__is_host(machine)) { | 2306 | if (machine__is_host(machine)) { |
2217 | vmlinux_name = symbol_conf.vmlinux_name; | 2307 | vmlinux_name = symbol_conf.vmlinux_name; |
2218 | kernel = dso__new_kernel(vmlinux_name); | 2308 | if (!vmlinux_name) |
2309 | vmlinux_name = "[kernel.kallsyms]"; | ||
2310 | |||
2311 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
2312 | "[kernel]", | ||
2313 | DSO_TYPE_KERNEL); | ||
2219 | } else { | 2314 | } else { |
2315 | char bf[PATH_MAX]; | ||
2316 | |||
2220 | if (machine__is_default_guest(machine)) | 2317 | if (machine__is_default_guest(machine)) |
2221 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; | 2318 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; |
2222 | kernel = dso__new_guest_kernel(machine, vmlinux_name); | 2319 | if (!vmlinux_name) |
2320 | vmlinux_name = machine__mmap_name(machine, bf, | ||
2321 | sizeof(bf)); | ||
2322 | |||
2323 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
2324 | "[guest.kernel]", | ||
2325 | DSO_TYPE_GUEST_KERNEL); | ||
2223 | } | 2326 | } |
2224 | 2327 | ||
2225 | if (kernel != NULL) { | 2328 | if (kernel != NULL && (!kernel->has_build_id)) |
2226 | dso__read_running_kernel_build_id(kernel, machine); | 2329 | dso__read_running_kernel_build_id(kernel, machine); |
2227 | dsos__add(&machine->kernel_dsos, kernel); | 2330 | |
2228 | } | ||
2229 | return kernel; | 2331 | return kernel; |
2230 | } | 2332 | } |
2231 | 2333 | ||
@@ -2329,7 +2431,7 @@ void machine__destroy_kernel_maps(struct machine *machine) | |||
2329 | 2431 | ||
2330 | int machine__create_kernel_maps(struct machine *machine) | 2432 | int machine__create_kernel_maps(struct machine *machine) |
2331 | { | 2433 | { |
2332 | struct dso *kernel = machine__create_kernel(machine); | 2434 | struct dso *kernel = machine__get_kernel(machine); |
2333 | 2435 | ||
2334 | if (kernel == NULL || | 2436 | if (kernel == NULL || |
2335 | __machine__create_kernel_maps(machine, kernel) < 0) | 2437 | __machine__create_kernel_maps(machine, kernel) < 0) |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 325ee36a9d29..29f8d742e92f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -72,11 +72,14 @@ struct symbol_conf { | |||
72 | use_modules, | 72 | use_modules, |
73 | sort_by_name, | 73 | sort_by_name, |
74 | show_nr_samples, | 74 | show_nr_samples, |
75 | show_total_period, | ||
75 | use_callchain, | 76 | use_callchain, |
76 | exclude_other, | 77 | exclude_other, |
77 | show_cpu_utilization, | 78 | show_cpu_utilization, |
78 | initialized, | 79 | initialized, |
79 | kptr_restrict; | 80 | kptr_restrict, |
81 | annotate_asm_raw, | ||
82 | annotate_src; | ||
80 | const char *vmlinux_name, | 83 | const char *vmlinux_name, |
81 | *kallsyms_name, | 84 | *kallsyms_name, |
82 | *source_prefix, | 85 | *source_prefix, |
@@ -155,7 +158,6 @@ struct dso { | |||
155 | }; | 158 | }; |
156 | 159 | ||
157 | struct dso *dso__new(const char *name); | 160 | struct dso *dso__new(const char *name); |
158 | struct dso *dso__new_kernel(const char *name); | ||
159 | void dso__delete(struct dso *dso); | 161 | void dso__delete(struct dso *dso); |
160 | 162 | ||
161 | int dso__name_len(const struct dso *dso); | 163 | int dso__name_len(const struct dso *dso); |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index a11f60735a18..500471dffa4f 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -15,52 +15,6 @@ | |||
15 | #include "top.h" | 15 | #include "top.h" |
16 | #include <inttypes.h> | 16 | #include <inttypes.h> |
17 | 17 | ||
18 | /* | ||
19 | * Ordering weight: count-1 * count-2 * ... / count-n | ||
20 | */ | ||
21 | static double sym_weight(const struct sym_entry *sym, struct perf_top *top) | ||
22 | { | ||
23 | double weight = sym->snap_count; | ||
24 | int counter; | ||
25 | |||
26 | if (!top->display_weighted) | ||
27 | return weight; | ||
28 | |||
29 | for (counter = 1; counter < top->evlist->nr_entries - 1; counter++) | ||
30 | weight *= sym->count[counter]; | ||
31 | |||
32 | weight /= (sym->count[counter] + 1); | ||
33 | |||
34 | return weight; | ||
35 | } | ||
36 | |||
37 | static void perf_top__remove_active_sym(struct perf_top *top, struct sym_entry *syme) | ||
38 | { | ||
39 | pthread_mutex_lock(&top->active_symbols_lock); | ||
40 | list_del_init(&syme->node); | ||
41 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
42 | } | ||
43 | |||
44 | static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | ||
45 | { | ||
46 | struct rb_node **p = &tree->rb_node; | ||
47 | struct rb_node *parent = NULL; | ||
48 | struct sym_entry *iter; | ||
49 | |||
50 | while (*p != NULL) { | ||
51 | parent = *p; | ||
52 | iter = rb_entry(parent, struct sym_entry, rb_node); | ||
53 | |||
54 | if (se->weight > iter->weight) | ||
55 | p = &(*p)->rb_left; | ||
56 | else | ||
57 | p = &(*p)->rb_right; | ||
58 | } | ||
59 | |||
60 | rb_link_node(&se->rb_node, parent, p); | ||
61 | rb_insert_color(&se->rb_node, tree); | ||
62 | } | ||
63 | |||
64 | #define SNPRINTF(buf, size, fmt, args...) \ | 18 | #define SNPRINTF(buf, size, fmt, args...) \ |
65 | ({ \ | 19 | ({ \ |
66 | size_t r = snprintf(buf, size, fmt, ## args); \ | 20 | size_t r = snprintf(buf, size, fmt, ## args); \ |
@@ -69,7 +23,6 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se) | |||
69 | 23 | ||
70 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | 24 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) |
71 | { | 25 | { |
72 | struct perf_evsel *counter; | ||
73 | float samples_per_sec = top->samples / top->delay_secs; | 26 | float samples_per_sec = top->samples / top->delay_secs; |
74 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; | 27 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; |
75 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; | 28 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; |
@@ -104,7 +57,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
104 | esamples_percent); | 57 | esamples_percent); |
105 | } | 58 | } |
106 | 59 | ||
107 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | 60 | if (top->evlist->nr_entries == 1) { |
108 | struct perf_evsel *first; | 61 | struct perf_evsel *first; |
109 | first = list_entry(top->evlist->entries.next, struct perf_evsel, node); | 62 | first = list_entry(top->evlist->entries.next, struct perf_evsel, node); |
110 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", | 63 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", |
@@ -112,27 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
112 | top->freq ? "Hz" : ""); | 65 | top->freq ? "Hz" : ""); |
113 | } | 66 | } |
114 | 67 | ||
115 | if (!top->display_weighted) { | 68 | ret += SNPRINTF(bf + ret, size - ret, "%s", event_name(top->sym_evsel)); |
116 | ret += SNPRINTF(bf + ret, size - ret, "%s", | ||
117 | event_name(top->sym_evsel)); | ||
118 | } else { | ||
119 | /* | ||
120 | * Don't let events eat all the space. Leaving 30 bytes | ||
121 | * for the rest should be enough. | ||
122 | */ | ||
123 | size_t last_pos = size - 30; | ||
124 | |||
125 | list_for_each_entry(counter, &top->evlist->entries, node) { | ||
126 | ret += SNPRINTF(bf + ret, size - ret, "%s%s", | ||
127 | counter->idx ? "/" : "", | ||
128 | event_name(counter)); | ||
129 | if (ret > last_pos) { | ||
130 | sprintf(bf + last_pos - 3, ".."); | ||
131 | ret = last_pos - 1; | ||
132 | break; | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | 69 | ||
137 | ret += SNPRINTF(bf + ret, size - ret, "], "); | 70 | ret += SNPRINTF(bf + ret, size - ret, "], "); |
138 | 71 | ||
@@ -166,73 +99,3 @@ void perf_top__reset_sample_counters(struct perf_top *top) | |||
166 | top->exact_samples = top->guest_kernel_samples = | 99 | top->exact_samples = top->guest_kernel_samples = |
167 | top->guest_us_samples = 0; | 100 | top->guest_us_samples = 0; |
168 | } | 101 | } |
169 | |||
170 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root) | ||
171 | { | ||
172 | struct sym_entry *syme, *n; | ||
173 | float sum_ksamples = 0.0; | ||
174 | int snap = !top->display_weighted ? top->sym_evsel->idx : 0, j; | ||
175 | |||
176 | /* Sort the active symbols */ | ||
177 | pthread_mutex_lock(&top->active_symbols_lock); | ||
178 | syme = list_entry(top->active_symbols.next, struct sym_entry, node); | ||
179 | pthread_mutex_unlock(&top->active_symbols_lock); | ||
180 | |||
181 | top->rb_entries = 0; | ||
182 | list_for_each_entry_safe_from(syme, n, &top->active_symbols, node) { | ||
183 | syme->snap_count = syme->count[snap]; | ||
184 | if (syme->snap_count != 0) { | ||
185 | |||
186 | if ((top->hide_user_symbols && | ||
187 | syme->map->dso->kernel == DSO_TYPE_USER) || | ||
188 | (top->hide_kernel_symbols && | ||
189 | syme->map->dso->kernel == DSO_TYPE_KERNEL)) { | ||
190 | perf_top__remove_active_sym(top, syme); | ||
191 | continue; | ||
192 | } | ||
193 | syme->weight = sym_weight(syme, top); | ||
194 | |||
195 | if ((int)syme->snap_count >= top->count_filter) { | ||
196 | rb_insert_active_sym(root, syme); | ||
197 | ++top->rb_entries; | ||
198 | } | ||
199 | sum_ksamples += syme->snap_count; | ||
200 | |||
201 | for (j = 0; j < top->evlist->nr_entries; j++) | ||
202 | syme->count[j] = top->zero ? 0 : syme->count[j] * 7 / 8; | ||
203 | } else | ||
204 | perf_top__remove_active_sym(top, syme); | ||
205 | } | ||
206 | |||
207 | return sum_ksamples; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Find the longest symbol name that will be displayed | ||
212 | */ | ||
213 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
214 | int *dso_width, int *dso_short_width, int *sym_width) | ||
215 | { | ||
216 | struct rb_node *nd; | ||
217 | int printed = 0; | ||
218 | |||
219 | *sym_width = *dso_width = *dso_short_width = 0; | ||
220 | |||
221 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
222 | struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node); | ||
223 | struct symbol *sym = sym_entry__symbol(syme); | ||
224 | |||
225 | if (++printed > top->print_entries || | ||
226 | (int)syme->snap_count < top->count_filter) | ||
227 | continue; | ||
228 | |||
229 | if (syme->map->dso->long_name_len > *dso_width) | ||
230 | *dso_width = syme->map->dso->long_name_len; | ||
231 | |||
232 | if (syme->map->dso->short_name_len > *dso_short_width) | ||
233 | *dso_short_width = syme->map->dso->short_name_len; | ||
234 | |||
235 | if (sym->namelen > *sym_width) | ||
236 | *sym_width = sym->namelen; | ||
237 | } | ||
238 | } | ||
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index bfbf95bcc603..399650967958 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -4,26 +4,10 @@ | |||
4 | #include "types.h" | 4 | #include "types.h" |
5 | #include "../perf.h" | 5 | #include "../perf.h" |
6 | #include <stddef.h> | 6 | #include <stddef.h> |
7 | #include <pthread.h> | ||
8 | #include <linux/list.h> | ||
9 | #include <linux/rbtree.h> | ||
10 | 7 | ||
11 | struct perf_evlist; | 8 | struct perf_evlist; |
12 | struct perf_evsel; | 9 | struct perf_evsel; |
13 | 10 | struct perf_session; | |
14 | struct sym_entry { | ||
15 | struct rb_node rb_node; | ||
16 | struct list_head node; | ||
17 | unsigned long snap_count; | ||
18 | double weight; | ||
19 | struct map *map; | ||
20 | unsigned long count[0]; | ||
21 | }; | ||
22 | |||
23 | static inline struct symbol *sym_entry__symbol(struct sym_entry *self) | ||
24 | { | ||
25 | return ((void *)self) + symbol_conf.priv_size; | ||
26 | } | ||
27 | 11 | ||
28 | struct perf_top { | 12 | struct perf_top { |
29 | struct perf_evlist *evlist; | 13 | struct perf_evlist *evlist; |
@@ -31,34 +15,20 @@ struct perf_top { | |||
31 | * Symbols will be added here in perf_event__process_sample and will | 15 | * Symbols will be added here in perf_event__process_sample and will |
32 | * get out after decayed. | 16 | * get out after decayed. |
33 | */ | 17 | */ |
34 | struct list_head active_symbols; | ||
35 | pthread_mutex_t active_symbols_lock; | ||
36 | pthread_cond_t active_symbols_cond; | ||
37 | u64 samples; | 18 | u64 samples; |
38 | u64 kernel_samples, us_samples; | 19 | u64 kernel_samples, us_samples; |
39 | u64 exact_samples; | 20 | u64 exact_samples; |
40 | u64 guest_us_samples, guest_kernel_samples; | 21 | u64 guest_us_samples, guest_kernel_samples; |
41 | int print_entries, count_filter, delay_secs; | 22 | int print_entries, count_filter, delay_secs; |
42 | int display_weighted, freq, rb_entries; | 23 | int freq; |
43 | pid_t target_pid, target_tid; | 24 | pid_t target_pid, target_tid; |
44 | bool hide_kernel_symbols, hide_user_symbols, zero; | 25 | bool hide_kernel_symbols, hide_user_symbols, zero; |
45 | const char *cpu_list; | 26 | const char *cpu_list; |
46 | struct sym_entry *sym_filter_entry; | 27 | struct hist_entry *sym_filter_entry; |
47 | struct perf_evsel *sym_evsel; | 28 | struct perf_evsel *sym_evsel; |
29 | struct perf_session *session; | ||
48 | }; | 30 | }; |
49 | 31 | ||
50 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | 32 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); |
51 | void perf_top__reset_sample_counters(struct perf_top *top); | 33 | void perf_top__reset_sample_counters(struct perf_top *top); |
52 | float perf_top__decay_samples(struct perf_top *top, struct rb_root *root); | ||
53 | void perf_top__find_widths(struct perf_top *top, struct rb_root *root, | ||
54 | int *dso_width, int *dso_short_width, int *sym_width); | ||
55 | |||
56 | #ifdef NO_NEWT_SUPPORT | ||
57 | static inline int perf_top__tui_browser(struct perf_top *top __used) | ||
58 | { | ||
59 | return 0; | ||
60 | } | ||
61 | #else | ||
62 | int perf_top__tui_browser(struct perf_top *top); | ||
63 | #endif | ||
64 | #endif /* __PERF_TOP_H */ | 34 | #endif /* __PERF_TOP_H */ |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 35729f4c40cb..d2655f08bcc0 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -80,7 +80,7 @@ static void die(const char *fmt, ...) | |||
80 | int ret = errno; | 80 | int ret = errno; |
81 | 81 | ||
82 | if (errno) | 82 | if (errno) |
83 | perror("trace-cmd"); | 83 | perror("perf"); |
84 | else | 84 | else |
85 | ret = -1; | 85 | ret = -1; |
86 | 86 | ||
@@ -183,106 +183,60 @@ int bigendian(void) | |||
183 | return *ptr == 0x01020304; | 183 | return *ptr == 0x01020304; |
184 | } | 184 | } |
185 | 185 | ||
186 | static unsigned long long copy_file_fd(int fd) | 186 | /* unfortunately, you can not stat debugfs or proc files for size */ |
187 | static void record_file(const char *file, size_t hdr_sz) | ||
187 | { | 188 | { |
188 | unsigned long long size = 0; | 189 | unsigned long long size = 0; |
189 | char buf[BUFSIZ]; | 190 | char buf[BUFSIZ], *sizep; |
190 | int r; | 191 | off_t hdr_pos = lseek(output_fd, 0, SEEK_CUR); |
191 | 192 | int r, fd; | |
192 | do { | ||
193 | r = read(fd, buf, BUFSIZ); | ||
194 | if (r > 0) { | ||
195 | size += r; | ||
196 | write_or_die(buf, r); | ||
197 | } | ||
198 | } while (r > 0); | ||
199 | |||
200 | return size; | ||
201 | } | ||
202 | |||
203 | static unsigned long long copy_file(const char *file) | ||
204 | { | ||
205 | unsigned long long size = 0; | ||
206 | int fd; | ||
207 | 193 | ||
208 | fd = open(file, O_RDONLY); | 194 | fd = open(file, O_RDONLY); |
209 | if (fd < 0) | 195 | if (fd < 0) |
210 | die("Can't read '%s'", file); | 196 | die("Can't read '%s'", file); |
211 | size = copy_file_fd(fd); | ||
212 | close(fd); | ||
213 | |||
214 | return size; | ||
215 | } | ||
216 | 197 | ||
217 | static unsigned long get_size_fd(int fd) | 198 | /* put in zeros for file size, then fill true size later */ |
218 | { | 199 | if (hdr_sz) |
219 | unsigned long long size = 0; | 200 | write_or_die(&size, hdr_sz); |
220 | char buf[BUFSIZ]; | ||
221 | int r; | ||
222 | 201 | ||
223 | do { | 202 | do { |
224 | r = read(fd, buf, BUFSIZ); | 203 | r = read(fd, buf, BUFSIZ); |
225 | if (r > 0) | 204 | if (r > 0) { |
226 | size += r; | 205 | size += r; |
206 | write_or_die(buf, r); | ||
207 | } | ||
227 | } while (r > 0); | 208 | } while (r > 0); |
228 | |||
229 | lseek(fd, 0, SEEK_SET); | ||
230 | |||
231 | return size; | ||
232 | } | ||
233 | |||
234 | static unsigned long get_size(const char *file) | ||
235 | { | ||
236 | unsigned long long size = 0; | ||
237 | int fd; | ||
238 | |||
239 | fd = open(file, O_RDONLY); | ||
240 | if (fd < 0) | ||
241 | die("Can't read '%s'", file); | ||
242 | size = get_size_fd(fd); | ||
243 | close(fd); | 209 | close(fd); |
244 | 210 | ||
245 | return size; | 211 | /* ugh, handle big-endian hdr_size == 4 */ |
212 | sizep = (char*)&size; | ||
213 | if (bigendian()) | ||
214 | sizep += sizeof(u64) - hdr_sz; | ||
215 | |||
216 | if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) | ||
217 | die("writing to %s", output_file); | ||
246 | } | 218 | } |
247 | 219 | ||
248 | static void read_header_files(void) | 220 | static void read_header_files(void) |
249 | { | 221 | { |
250 | unsigned long long size, check_size; | ||
251 | char *path; | 222 | char *path; |
252 | int fd; | 223 | struct stat st; |
253 | 224 | ||
254 | path = get_tracing_file("events/header_page"); | 225 | path = get_tracing_file("events/header_page"); |
255 | fd = open(path, O_RDONLY); | 226 | if (stat(path, &st) < 0) |
256 | if (fd < 0) | ||
257 | die("can't read '%s'", path); | 227 | die("can't read '%s'", path); |
258 | 228 | ||
259 | /* unfortunately, you can not stat debugfs files for size */ | ||
260 | size = get_size_fd(fd); | ||
261 | |||
262 | write_or_die("header_page", 12); | 229 | write_or_die("header_page", 12); |
263 | write_or_die(&size, 8); | 230 | record_file(path, 8); |
264 | check_size = copy_file_fd(fd); | ||
265 | close(fd); | ||
266 | |||
267 | if (size != check_size) | ||
268 | die("wrong size for '%s' size=%lld read=%lld", | ||
269 | path, size, check_size); | ||
270 | put_tracing_file(path); | 231 | put_tracing_file(path); |
271 | 232 | ||
272 | path = get_tracing_file("events/header_event"); | 233 | path = get_tracing_file("events/header_event"); |
273 | fd = open(path, O_RDONLY); | 234 | if (stat(path, &st) < 0) |
274 | if (fd < 0) | ||
275 | die("can't read '%s'", path); | 235 | die("can't read '%s'", path); |
276 | 236 | ||
277 | size = get_size_fd(fd); | ||
278 | |||
279 | write_or_die("header_event", 13); | 237 | write_or_die("header_event", 13); |
280 | write_or_die(&size, 8); | 238 | record_file(path, 8); |
281 | check_size = copy_file_fd(fd); | ||
282 | if (size != check_size) | ||
283 | die("wrong size for '%s'", path); | ||
284 | put_tracing_file(path); | 239 | put_tracing_file(path); |
285 | close(fd); | ||
286 | } | 240 | } |
287 | 241 | ||
288 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | 242 | static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) |
@@ -298,7 +252,6 @@ static bool name_in_tp_list(char *sys, struct tracepoint_path *tps) | |||
298 | 252 | ||
299 | static void copy_event_system(const char *sys, struct tracepoint_path *tps) | 253 | static void copy_event_system(const char *sys, struct tracepoint_path *tps) |
300 | { | 254 | { |
301 | unsigned long long size, check_size; | ||
302 | struct dirent *dent; | 255 | struct dirent *dent; |
303 | struct stat st; | 256 | struct stat st; |
304 | char *format; | 257 | char *format; |
@@ -338,14 +291,8 @@ static void copy_event_system(const char *sys, struct tracepoint_path *tps) | |||
338 | sprintf(format, "%s/%s/format", sys, dent->d_name); | 291 | sprintf(format, "%s/%s/format", sys, dent->d_name); |
339 | ret = stat(format, &st); | 292 | ret = stat(format, &st); |
340 | 293 | ||
341 | if (ret >= 0) { | 294 | if (ret >= 0) |
342 | /* unfortunately, you can not stat debugfs files for size */ | 295 | record_file(format, 8); |
343 | size = get_size(format); | ||
344 | write_or_die(&size, 8); | ||
345 | check_size = copy_file(format); | ||
346 | if (size != check_size) | ||
347 | die("error in size of file '%s'", format); | ||
348 | } | ||
349 | 296 | ||
350 | free(format); | 297 | free(format); |
351 | } | 298 | } |
@@ -426,7 +373,7 @@ static void read_event_files(struct tracepoint_path *tps) | |||
426 | 373 | ||
427 | static void read_proc_kallsyms(void) | 374 | static void read_proc_kallsyms(void) |
428 | { | 375 | { |
429 | unsigned int size, check_size; | 376 | unsigned int size; |
430 | const char *path = "/proc/kallsyms"; | 377 | const char *path = "/proc/kallsyms"; |
431 | struct stat st; | 378 | struct stat st; |
432 | int ret; | 379 | int ret; |
@@ -438,17 +385,12 @@ static void read_proc_kallsyms(void) | |||
438 | write_or_die(&size, 4); | 385 | write_or_die(&size, 4); |
439 | return; | 386 | return; |
440 | } | 387 | } |
441 | size = get_size(path); | 388 | record_file(path, 4); |
442 | write_or_die(&size, 4); | ||
443 | check_size = copy_file(path); | ||
444 | if (size != check_size) | ||
445 | die("error in size of file '%s'", path); | ||
446 | |||
447 | } | 389 | } |
448 | 390 | ||
449 | static void read_ftrace_printk(void) | 391 | static void read_ftrace_printk(void) |
450 | { | 392 | { |
451 | unsigned int size, check_size; | 393 | unsigned int size; |
452 | char *path; | 394 | char *path; |
453 | struct stat st; | 395 | struct stat st; |
454 | int ret; | 396 | int ret; |
@@ -461,11 +403,8 @@ static void read_ftrace_printk(void) | |||
461 | write_or_die(&size, 4); | 403 | write_or_die(&size, 4); |
462 | goto out; | 404 | goto out; |
463 | } | 405 | } |
464 | size = get_size(path); | 406 | record_file(path, 4); |
465 | write_or_die(&size, 4); | 407 | |
466 | check_size = copy_file(path); | ||
467 | if (size != check_size) | ||
468 | die("error in size of file '%s'", path); | ||
469 | out: | 408 | out: |
470 | put_tracing_file(path); | 409 | put_tracing_file(path); |
471 | } | 410 | } |
@@ -490,6 +429,19 @@ get_tracepoints_path(struct list_head *pattrs) | |||
490 | return nr_tracepoints > 0 ? path.next : NULL; | 429 | return nr_tracepoints > 0 ? path.next : NULL; |
491 | } | 430 | } |
492 | 431 | ||
432 | static void | ||
433 | put_tracepoints_path(struct tracepoint_path *tps) | ||
434 | { | ||
435 | while (tps) { | ||
436 | struct tracepoint_path *t = tps; | ||
437 | |||
438 | tps = tps->next; | ||
439 | free(t->name); | ||
440 | free(t->system); | ||
441 | free(t); | ||
442 | } | ||
443 | } | ||
444 | |||
493 | bool have_tracepoints(struct list_head *pattrs) | 445 | bool have_tracepoints(struct list_head *pattrs) |
494 | { | 446 | { |
495 | struct perf_evsel *pos; | 447 | struct perf_evsel *pos; |
@@ -501,19 +453,11 @@ bool have_tracepoints(struct list_head *pattrs) | |||
501 | return false; | 453 | return false; |
502 | } | 454 | } |
503 | 455 | ||
504 | int read_tracing_data(int fd, struct list_head *pattrs) | 456 | static void tracing_data_header(void) |
505 | { | 457 | { |
506 | char buf[BUFSIZ]; | 458 | char buf[20]; |
507 | struct tracepoint_path *tps = get_tracepoints_path(pattrs); | ||
508 | |||
509 | /* | ||
510 | * What? No tracepoints? No sense writing anything here, bail out. | ||
511 | */ | ||
512 | if (tps == NULL) | ||
513 | return -1; | ||
514 | |||
515 | output_fd = fd; | ||
516 | 459 | ||
460 | /* just guessing this is someone's birthday.. ;) */ | ||
517 | buf[0] = 23; | 461 | buf[0] = 23; |
518 | buf[1] = 8; | 462 | buf[1] = 8; |
519 | buf[2] = 68; | 463 | buf[2] = 68; |
@@ -538,28 +482,86 @@ int read_tracing_data(int fd, struct list_head *pattrs) | |||
538 | /* save page_size */ | 482 | /* save page_size */ |
539 | page_size = sysconf(_SC_PAGESIZE); | 483 | page_size = sysconf(_SC_PAGESIZE); |
540 | write_or_die(&page_size, 4); | 484 | write_or_die(&page_size, 4); |
485 | } | ||
486 | |||
487 | struct tracing_data *tracing_data_get(struct list_head *pattrs, | ||
488 | int fd, bool temp) | ||
489 | { | ||
490 | struct tracepoint_path *tps; | ||
491 | struct tracing_data *tdata; | ||
492 | |||
493 | output_fd = fd; | ||
541 | 494 | ||
495 | tps = get_tracepoints_path(pattrs); | ||
496 | if (!tps) | ||
497 | return NULL; | ||
498 | |||
499 | tdata = malloc_or_die(sizeof(*tdata)); | ||
500 | tdata->temp = temp; | ||
501 | tdata->size = 0; | ||
502 | |||
503 | if (temp) { | ||
504 | int temp_fd; | ||
505 | |||
506 | snprintf(tdata->temp_file, sizeof(tdata->temp_file), | ||
507 | "/tmp/perf-XXXXXX"); | ||
508 | if (!mkstemp(tdata->temp_file)) | ||
509 | die("Can't make temp file"); | ||
510 | |||
511 | temp_fd = open(tdata->temp_file, O_RDWR); | ||
512 | if (temp_fd < 0) | ||
513 | die("Can't read '%s'", tdata->temp_file); | ||
514 | |||
515 | /* | ||
516 | * Set the temp file the default output, so all the | ||
517 | * tracing data are stored into it. | ||
518 | */ | ||
519 | output_fd = temp_fd; | ||
520 | } | ||
521 | |||
522 | tracing_data_header(); | ||
542 | read_header_files(); | 523 | read_header_files(); |
543 | read_ftrace_files(tps); | 524 | read_ftrace_files(tps); |
544 | read_event_files(tps); | 525 | read_event_files(tps); |
545 | read_proc_kallsyms(); | 526 | read_proc_kallsyms(); |
546 | read_ftrace_printk(); | 527 | read_ftrace_printk(); |
547 | 528 | ||
548 | return 0; | 529 | /* |
530 | * All tracing data are stored by now, we can restore | ||
531 | * the default output file in case we used temp file. | ||
532 | */ | ||
533 | if (temp) { | ||
534 | tdata->size = lseek(output_fd, 0, SEEK_CUR); | ||
535 | close(output_fd); | ||
536 | output_fd = fd; | ||
537 | } | ||
538 | |||
539 | put_tracepoints_path(tps); | ||
540 | return tdata; | ||
549 | } | 541 | } |
550 | 542 | ||
551 | ssize_t read_tracing_data_size(int fd, struct list_head *pattrs) | 543 | void tracing_data_put(struct tracing_data *tdata) |
552 | { | 544 | { |
553 | ssize_t size; | 545 | if (tdata->temp) { |
554 | int err = 0; | 546 | record_file(tdata->temp_file, 0); |
547 | unlink(tdata->temp_file); | ||
548 | } | ||
549 | |||
550 | free(tdata); | ||
551 | } | ||
555 | 552 | ||
556 | calc_data_size = 1; | 553 | int read_tracing_data(int fd, struct list_head *pattrs) |
557 | err = read_tracing_data(fd, pattrs); | 554 | { |
558 | size = calc_data_size - 1; | 555 | struct tracing_data *tdata; |
559 | calc_data_size = 0; | ||
560 | 556 | ||
561 | if (err < 0) | 557 | /* |
562 | return err; | 558 | * We work over the real file, so we can write data |
559 | * directly, no temp file is needed. | ||
560 | */ | ||
561 | tdata = tracing_data_get(pattrs, fd, false); | ||
562 | if (!tdata) | ||
563 | return -ENOMEM; | ||
563 | 564 | ||
564 | return size; | 565 | tracing_data_put(tdata); |
566 | return 0; | ||
565 | } | 567 | } |
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index f674dda3363b..a84100817649 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -263,7 +263,18 @@ void *raw_field_ptr(struct event *event, const char *name, void *data); | |||
263 | unsigned long long eval_flag(const char *flag); | 263 | unsigned long long eval_flag(const char *flag); |
264 | 264 | ||
265 | int read_tracing_data(int fd, struct list_head *pattrs); | 265 | int read_tracing_data(int fd, struct list_head *pattrs); |
266 | ssize_t read_tracing_data_size(int fd, struct list_head *pattrs); | 266 | |
267 | struct tracing_data { | ||
268 | /* size is only valid if temp is 'true' */ | ||
269 | ssize_t size; | ||
270 | bool temp; | ||
271 | char temp_file[50]; | ||
272 | }; | ||
273 | |||
274 | struct tracing_data *tracing_data_get(struct list_head *pattrs, | ||
275 | int fd, bool temp); | ||
276 | void tracing_data_put(struct tracing_data *tdata); | ||
277 | |||
267 | 278 | ||
268 | /* taken from kernel/trace/trace.h */ | 279 | /* taken from kernel/trace/trace.h */ |
269 | enum trace_flag_type { | 280 | enum trace_flag_type { |
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c index 611219f80680..556829124b02 100644 --- a/tools/perf/util/ui/browser.c +++ b/tools/perf/util/ui/browser.c | |||
@@ -1,5 +1,10 @@ | |||
1 | #include "../util.h" | ||
2 | #include "../cache.h" | ||
3 | #include "../../perf.h" | ||
1 | #include "libslang.h" | 4 | #include "libslang.h" |
5 | #include <newt.h> | ||
2 | #include "ui.h" | 6 | #include "ui.h" |
7 | #include "util.h" | ||
3 | #include <linux/compiler.h> | 8 | #include <linux/compiler.h> |
4 | #include <linux/list.h> | 9 | #include <linux/list.h> |
5 | #include <linux/rbtree.h> | 10 | #include <linux/rbtree.h> |
@@ -7,13 +12,13 @@ | |||
7 | #include <sys/ttydefaults.h> | 12 | #include <sys/ttydefaults.h> |
8 | #include "browser.h" | 13 | #include "browser.h" |
9 | #include "helpline.h" | 14 | #include "helpline.h" |
15 | #include "keysyms.h" | ||
10 | #include "../color.h" | 16 | #include "../color.h" |
11 | #include "../util.h" | ||
12 | #include <stdio.h> | ||
13 | 17 | ||
14 | static int ui_browser__percent_color(double percent, bool current) | 18 | static int ui_browser__percent_color(struct ui_browser *browser, |
19 | double percent, bool current) | ||
15 | { | 20 | { |
16 | if (current) | 21 | if (current && (!browser->use_navkeypressed || browser->navkeypressed)) |
17 | return HE_COLORSET_SELECTED; | 22 | return HE_COLORSET_SELECTED; |
18 | if (percent >= MIN_RED) | 23 | if (percent >= MIN_RED) |
19 | return HE_COLORSET_TOP; | 24 | return HE_COLORSET_TOP; |
@@ -30,7 +35,7 @@ void ui_browser__set_color(struct ui_browser *self __used, int color) | |||
30 | void ui_browser__set_percent_color(struct ui_browser *self, | 35 | void ui_browser__set_percent_color(struct ui_browser *self, |
31 | double percent, bool current) | 36 | double percent, bool current) |
32 | { | 37 | { |
33 | int color = ui_browser__percent_color(percent, current); | 38 | int color = ui_browser__percent_color(self, percent, current); |
34 | ui_browser__set_color(self, color); | 39 | ui_browser__set_color(self, color); |
35 | } | 40 | } |
36 | 41 | ||
@@ -39,31 +44,62 @@ void ui_browser__gotorc(struct ui_browser *self, int y, int x) | |||
39 | SLsmg_gotorc(self->y + y, self->x + x); | 44 | SLsmg_gotorc(self->y + y, self->x + x); |
40 | } | 45 | } |
41 | 46 | ||
47 | static struct list_head * | ||
48 | ui_browser__list_head_filter_entries(struct ui_browser *browser, | ||
49 | struct list_head *pos) | ||
50 | { | ||
51 | do { | ||
52 | if (!browser->filter || !browser->filter(browser, pos)) | ||
53 | return pos; | ||
54 | pos = pos->next; | ||
55 | } while (pos != browser->entries); | ||
56 | |||
57 | return NULL; | ||
58 | } | ||
59 | |||
60 | static struct list_head * | ||
61 | ui_browser__list_head_filter_prev_entries(struct ui_browser *browser, | ||
62 | struct list_head *pos) | ||
63 | { | ||
64 | do { | ||
65 | if (!browser->filter || !browser->filter(browser, pos)) | ||
66 | return pos; | ||
67 | pos = pos->prev; | ||
68 | } while (pos != browser->entries); | ||
69 | |||
70 | return NULL; | ||
71 | } | ||
72 | |||
42 | void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) | 73 | void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) |
43 | { | 74 | { |
44 | struct list_head *head = self->entries; | 75 | struct list_head *head = self->entries; |
45 | struct list_head *pos; | 76 | struct list_head *pos; |
46 | 77 | ||
78 | if (self->nr_entries == 0) | ||
79 | return; | ||
80 | |||
47 | switch (whence) { | 81 | switch (whence) { |
48 | case SEEK_SET: | 82 | case SEEK_SET: |
49 | pos = head->next; | 83 | pos = ui_browser__list_head_filter_entries(self, head->next); |
50 | break; | 84 | break; |
51 | case SEEK_CUR: | 85 | case SEEK_CUR: |
52 | pos = self->top; | 86 | pos = self->top; |
53 | break; | 87 | break; |
54 | case SEEK_END: | 88 | case SEEK_END: |
55 | pos = head->prev; | 89 | pos = ui_browser__list_head_filter_prev_entries(self, head->prev); |
56 | break; | 90 | break; |
57 | default: | 91 | default: |
58 | return; | 92 | return; |
59 | } | 93 | } |
60 | 94 | ||
95 | assert(pos != NULL); | ||
96 | |||
61 | if (offset > 0) { | 97 | if (offset > 0) { |
62 | while (offset-- != 0) | 98 | while (offset-- != 0) |
63 | pos = pos->next; | 99 | pos = ui_browser__list_head_filter_entries(self, pos->next); |
64 | } else { | 100 | } else { |
65 | while (offset++ != 0) | 101 | while (offset++ != 0) |
66 | pos = pos->prev; | 102 | pos = ui_browser__list_head_filter_prev_entries(self, pos->prev); |
67 | } | 103 | } |
68 | 104 | ||
69 | self->top = pos; | 105 | self->top = pos; |
@@ -127,41 +163,76 @@ bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) | |||
127 | 163 | ||
128 | void ui_browser__refresh_dimensions(struct ui_browser *self) | 164 | void ui_browser__refresh_dimensions(struct ui_browser *self) |
129 | { | 165 | { |
130 | int cols, rows; | 166 | self->width = SLtt_Screen_Cols - 1; |
131 | newtGetScreenSize(&cols, &rows); | 167 | self->height = SLtt_Screen_Rows - 2; |
132 | |||
133 | self->width = cols - 1; | ||
134 | self->height = rows - 2; | ||
135 | self->y = 1; | 168 | self->y = 1; |
136 | self->x = 0; | 169 | self->x = 0; |
137 | } | 170 | } |
138 | 171 | ||
139 | void ui_browser__reset_index(struct ui_browser *self) | 172 | void ui_browser__handle_resize(struct ui_browser *browser) |
140 | { | 173 | { |
141 | self->index = self->top_idx = 0; | 174 | ui__refresh_dimensions(false); |
142 | self->seek(self, 0, SEEK_SET); | 175 | ui_browser__show(browser, browser->title, ui_helpline__current); |
176 | ui_browser__refresh(browser); | ||
143 | } | 177 | } |
144 | 178 | ||
145 | void ui_browser__add_exit_key(struct ui_browser *self, int key) | 179 | int ui_browser__warning(struct ui_browser *browser, int timeout, |
180 | const char *format, ...) | ||
146 | { | 181 | { |
147 | newtFormAddHotKey(self->form, key); | 182 | va_list args; |
183 | char *text; | ||
184 | int key = 0, err; | ||
185 | |||
186 | va_start(args, format); | ||
187 | err = vasprintf(&text, format, args); | ||
188 | va_end(args); | ||
189 | |||
190 | if (err < 0) { | ||
191 | va_start(args, format); | ||
192 | ui_helpline__vpush(format, args); | ||
193 | va_end(args); | ||
194 | } else { | ||
195 | while ((key == ui__question_window("Warning!", text, | ||
196 | "Press any key...", | ||
197 | timeout)) == K_RESIZE) | ||
198 | ui_browser__handle_resize(browser); | ||
199 | free(text); | ||
200 | } | ||
201 | |||
202 | return key; | ||
148 | } | 203 | } |
149 | 204 | ||
150 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]) | 205 | int ui_browser__help_window(struct ui_browser *browser, const char *text) |
151 | { | 206 | { |
152 | int i = 0; | 207 | int key; |
153 | 208 | ||
154 | while (keys[i] && i < 64) { | 209 | while ((key = ui__help_window(text)) == K_RESIZE) |
155 | ui_browser__add_exit_key(self, keys[i]); | 210 | ui_browser__handle_resize(browser); |
156 | ++i; | 211 | |
157 | } | 212 | return key; |
213 | } | ||
214 | |||
215 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text) | ||
216 | { | ||
217 | int key; | ||
218 | |||
219 | while ((key = ui__dialog_yesno(text)) == K_RESIZE) | ||
220 | ui_browser__handle_resize(browser); | ||
221 | |||
222 | return key == K_ENTER || toupper(key) == 'Y'; | ||
223 | } | ||
224 | |||
225 | void ui_browser__reset_index(struct ui_browser *self) | ||
226 | { | ||
227 | self->index = self->top_idx = 0; | ||
228 | self->seek(self, 0, SEEK_SET); | ||
158 | } | 229 | } |
159 | 230 | ||
160 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) | 231 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) |
161 | { | 232 | { |
162 | SLsmg_gotorc(0, 0); | 233 | SLsmg_gotorc(0, 0); |
163 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); | 234 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); |
164 | slsmg_write_nstring(title, browser->width); | 235 | slsmg_write_nstring(title, browser->width + 1); |
165 | } | 236 | } |
166 | 237 | ||
167 | void ui_browser__show_title(struct ui_browser *browser, const char *title) | 238 | void ui_browser__show_title(struct ui_browser *browser, const char *title) |
@@ -174,78 +245,145 @@ void ui_browser__show_title(struct ui_browser *browser, const char *title) | |||
174 | int ui_browser__show(struct ui_browser *self, const char *title, | 245 | int ui_browser__show(struct ui_browser *self, const char *title, |
175 | const char *helpline, ...) | 246 | const char *helpline, ...) |
176 | { | 247 | { |
248 | int err; | ||
177 | va_list ap; | 249 | va_list ap; |
178 | int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP, | ||
179 | NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ', | ||
180 | NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 }; | ||
181 | |||
182 | if (self->form != NULL) | ||
183 | newtFormDestroy(self->form); | ||
184 | 250 | ||
185 | ui_browser__refresh_dimensions(self); | 251 | ui_browser__refresh_dimensions(self); |
186 | self->form = newtForm(NULL, NULL, 0); | ||
187 | if (self->form == NULL) | ||
188 | return -1; | ||
189 | |||
190 | self->sb = newtVerticalScrollbar(self->width, 1, self->height, | ||
191 | HE_COLORSET_NORMAL, | ||
192 | HE_COLORSET_SELECTED); | ||
193 | if (self->sb == NULL) | ||
194 | return -1; | ||
195 | 252 | ||
196 | pthread_mutex_lock(&ui__lock); | 253 | pthread_mutex_lock(&ui__lock); |
197 | __ui_browser__show_title(self, title); | 254 | __ui_browser__show_title(self, title); |
198 | 255 | ||
199 | ui_browser__add_exit_keys(self, keys); | 256 | self->title = title; |
200 | newtFormAddComponent(self->form, self->sb); | 257 | free(self->helpline); |
258 | self->helpline = NULL; | ||
201 | 259 | ||
202 | va_start(ap, helpline); | 260 | va_start(ap, helpline); |
203 | ui_helpline__vpush(helpline, ap); | 261 | err = vasprintf(&self->helpline, helpline, ap); |
204 | va_end(ap); | 262 | va_end(ap); |
263 | if (err > 0) | ||
264 | ui_helpline__push(self->helpline); | ||
205 | pthread_mutex_unlock(&ui__lock); | 265 | pthread_mutex_unlock(&ui__lock); |
206 | return 0; | 266 | return err ? 0 : -1; |
207 | } | 267 | } |
208 | 268 | ||
209 | void ui_browser__hide(struct ui_browser *self) | 269 | void ui_browser__hide(struct ui_browser *browser __used) |
210 | { | 270 | { |
211 | pthread_mutex_lock(&ui__lock); | 271 | pthread_mutex_lock(&ui__lock); |
212 | newtFormDestroy(self->form); | ||
213 | self->form = NULL; | ||
214 | ui_helpline__pop(); | 272 | ui_helpline__pop(); |
215 | pthread_mutex_unlock(&ui__lock); | 273 | pthread_mutex_unlock(&ui__lock); |
216 | } | 274 | } |
217 | 275 | ||
218 | int ui_browser__refresh(struct ui_browser *self) | 276 | static void ui_browser__scrollbar_set(struct ui_browser *browser) |
277 | { | ||
278 | int height = browser->height, h = 0, pct = 0, | ||
279 | col = browser->width, | ||
280 | row = browser->y - 1; | ||
281 | |||
282 | if (browser->nr_entries > 1) { | ||
283 | pct = ((browser->index * (browser->height - 1)) / | ||
284 | (browser->nr_entries - 1)); | ||
285 | } | ||
286 | |||
287 | SLsmg_set_char_set(1); | ||
288 | |||
289 | while (h < height) { | ||
290 | ui_browser__gotorc(browser, row++, col); | ||
291 | SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_CKBRD_CHAR); | ||
292 | ++h; | ||
293 | } | ||
294 | |||
295 | SLsmg_set_char_set(0); | ||
296 | } | ||
297 | |||
298 | static int __ui_browser__refresh(struct ui_browser *browser) | ||
219 | { | 299 | { |
220 | int row; | 300 | int row; |
301 | int width = browser->width; | ||
302 | |||
303 | row = browser->refresh(browser); | ||
304 | ui_browser__set_color(browser, HE_COLORSET_NORMAL); | ||
305 | |||
306 | if (!browser->use_navkeypressed || browser->navkeypressed) | ||
307 | ui_browser__scrollbar_set(browser); | ||
308 | else | ||
309 | width += 1; | ||
310 | |||
311 | SLsmg_fill_region(browser->y + row, browser->x, | ||
312 | browser->height - row, width, ' '); | ||
313 | |||
314 | return 0; | ||
315 | } | ||
221 | 316 | ||
317 | int ui_browser__refresh(struct ui_browser *browser) | ||
318 | { | ||
222 | pthread_mutex_lock(&ui__lock); | 319 | pthread_mutex_lock(&ui__lock); |
223 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); | 320 | __ui_browser__refresh(browser); |
224 | row = self->refresh(self); | ||
225 | ui_browser__set_color(self, HE_COLORSET_NORMAL); | ||
226 | SLsmg_fill_region(self->y + row, self->x, | ||
227 | self->height - row, self->width, ' '); | ||
228 | pthread_mutex_unlock(&ui__lock); | 321 | pthread_mutex_unlock(&ui__lock); |
229 | 322 | ||
230 | return 0; | 323 | return 0; |
231 | } | 324 | } |
232 | 325 | ||
233 | int ui_browser__run(struct ui_browser *self) | 326 | /* |
327 | * Here we're updating nr_entries _after_ we started browsing, i.e. we have to | ||
328 | * forget about any reference to any entry in the underlying data structure, | ||
329 | * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser | ||
330 | * after an output_resort and hist decay. | ||
331 | */ | ||
332 | void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries) | ||
234 | { | 333 | { |
235 | struct newtExitStruct es; | 334 | off_t offset = nr_entries - browser->nr_entries; |
335 | |||
336 | browser->nr_entries = nr_entries; | ||
236 | 337 | ||
237 | if (ui_browser__refresh(self) < 0) | 338 | if (offset < 0) { |
238 | return -1; | 339 | if (browser->top_idx < (u64)-offset) |
340 | offset = -browser->top_idx; | ||
341 | |||
342 | browser->index += offset; | ||
343 | browser->top_idx += offset; | ||
344 | } | ||
345 | |||
346 | browser->top = NULL; | ||
347 | browser->seek(browser, browser->top_idx, SEEK_SET); | ||
348 | } | ||
349 | |||
350 | int ui_browser__run(struct ui_browser *self, int delay_secs) | ||
351 | { | ||
352 | int err, key; | ||
239 | 353 | ||
240 | while (1) { | 354 | while (1) { |
241 | off_t offset; | 355 | off_t offset; |
242 | 356 | ||
243 | newtFormRun(self->form, &es); | 357 | pthread_mutex_lock(&ui__lock); |
244 | 358 | err = __ui_browser__refresh(self); | |
245 | if (es.reason != NEWT_EXIT_HOTKEY) | 359 | SLsmg_refresh(); |
360 | pthread_mutex_unlock(&ui__lock); | ||
361 | if (err < 0) | ||
246 | break; | 362 | break; |
247 | switch (es.u.key) { | 363 | |
248 | case NEWT_KEY_DOWN: | 364 | key = ui__getch(delay_secs); |
365 | |||
366 | if (key == K_RESIZE) { | ||
367 | ui__refresh_dimensions(false); | ||
368 | ui_browser__refresh_dimensions(self); | ||
369 | __ui_browser__show_title(self, self->title); | ||
370 | ui_helpline__puts(self->helpline); | ||
371 | continue; | ||
372 | } | ||
373 | |||
374 | if (self->use_navkeypressed && !self->navkeypressed) { | ||
375 | if (key == K_DOWN || key == K_UP || | ||
376 | key == K_PGDN || key == K_PGUP || | ||
377 | key == K_HOME || key == K_END || | ||
378 | key == ' ') { | ||
379 | self->navkeypressed = true; | ||
380 | continue; | ||
381 | } else | ||
382 | return key; | ||
383 | } | ||
384 | |||
385 | switch (key) { | ||
386 | case K_DOWN: | ||
249 | if (self->index == self->nr_entries - 1) | 387 | if (self->index == self->nr_entries - 1) |
250 | break; | 388 | break; |
251 | ++self->index; | 389 | ++self->index; |
@@ -254,7 +392,7 @@ int ui_browser__run(struct ui_browser *self) | |||
254 | self->seek(self, +1, SEEK_CUR); | 392 | self->seek(self, +1, SEEK_CUR); |
255 | } | 393 | } |
256 | break; | 394 | break; |
257 | case NEWT_KEY_UP: | 395 | case K_UP: |
258 | if (self->index == 0) | 396 | if (self->index == 0) |
259 | break; | 397 | break; |
260 | --self->index; | 398 | --self->index; |
@@ -263,7 +401,7 @@ int ui_browser__run(struct ui_browser *self) | |||
263 | self->seek(self, -1, SEEK_CUR); | 401 | self->seek(self, -1, SEEK_CUR); |
264 | } | 402 | } |
265 | break; | 403 | break; |
266 | case NEWT_KEY_PGDN: | 404 | case K_PGDN: |
267 | case ' ': | 405 | case ' ': |
268 | if (self->top_idx + self->height > self->nr_entries - 1) | 406 | if (self->top_idx + self->height > self->nr_entries - 1) |
269 | break; | 407 | break; |
@@ -275,7 +413,7 @@ int ui_browser__run(struct ui_browser *self) | |||
275 | self->top_idx += offset; | 413 | self->top_idx += offset; |
276 | self->seek(self, +offset, SEEK_CUR); | 414 | self->seek(self, +offset, SEEK_CUR); |
277 | break; | 415 | break; |
278 | case NEWT_KEY_PGUP: | 416 | case K_PGUP: |
279 | if (self->top_idx == 0) | 417 | if (self->top_idx == 0) |
280 | break; | 418 | break; |
281 | 419 | ||
@@ -288,10 +426,10 @@ int ui_browser__run(struct ui_browser *self) | |||
288 | self->top_idx -= offset; | 426 | self->top_idx -= offset; |
289 | self->seek(self, -offset, SEEK_CUR); | 427 | self->seek(self, -offset, SEEK_CUR); |
290 | break; | 428 | break; |
291 | case NEWT_KEY_HOME: | 429 | case K_HOME: |
292 | ui_browser__reset_index(self); | 430 | ui_browser__reset_index(self); |
293 | break; | 431 | break; |
294 | case NEWT_KEY_END: | 432 | case K_END: |
295 | offset = self->height - 1; | 433 | offset = self->height - 1; |
296 | if (offset >= self->nr_entries) | 434 | if (offset >= self->nr_entries) |
297 | offset = self->nr_entries - 1; | 435 | offset = self->nr_entries - 1; |
@@ -301,10 +439,8 @@ int ui_browser__run(struct ui_browser *self) | |||
301 | self->seek(self, -offset, SEEK_END); | 439 | self->seek(self, -offset, SEEK_END); |
302 | break; | 440 | break; |
303 | default: | 441 | default: |
304 | return es.u.key; | 442 | return key; |
305 | } | 443 | } |
306 | if (ui_browser__refresh(self) < 0) | ||
307 | return -1; | ||
308 | } | 444 | } |
309 | return -1; | 445 | return -1; |
310 | } | 446 | } |
@@ -316,41 +452,146 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self) | |||
316 | int row = 0; | 452 | int row = 0; |
317 | 453 | ||
318 | if (self->top == NULL || self->top == self->entries) | 454 | if (self->top == NULL || self->top == self->entries) |
319 | self->top = head->next; | 455 | self->top = ui_browser__list_head_filter_entries(self, head->next); |
320 | 456 | ||
321 | pos = self->top; | 457 | pos = self->top; |
322 | 458 | ||
323 | list_for_each_from(pos, head) { | 459 | list_for_each_from(pos, head) { |
324 | ui_browser__gotorc(self, row, 0); | 460 | if (!self->filter || !self->filter(self, pos)) { |
325 | self->write(self, pos, row); | 461 | ui_browser__gotorc(self, row, 0); |
326 | if (++row == self->height) | 462 | self->write(self, pos, row); |
327 | break; | 463 | if (++row == self->height) |
464 | break; | ||
465 | } | ||
328 | } | 466 | } |
329 | 467 | ||
330 | return row; | 468 | return row; |
331 | } | 469 | } |
332 | 470 | ||
333 | static struct newtPercentTreeColors { | 471 | static struct ui_browser__colorset { |
334 | const char *topColorFg, *topColorBg; | 472 | const char *name, *fg, *bg; |
335 | const char *mediumColorFg, *mediumColorBg; | 473 | int colorset; |
336 | const char *normalColorFg, *normalColorBg; | 474 | } ui_browser__colorsets[] = { |
337 | const char *selColorFg, *selColorBg; | 475 | { |
338 | const char *codeColorFg, *codeColorBg; | 476 | .colorset = HE_COLORSET_TOP, |
339 | } defaultPercentTreeColors = { | 477 | .name = "top", |
340 | "red", "lightgray", | 478 | .fg = "red", |
341 | "green", "lightgray", | 479 | .bg = "default", |
342 | "black", "lightgray", | 480 | }, |
343 | "lightgray", "magenta", | 481 | { |
344 | "blue", "lightgray", | 482 | .colorset = HE_COLORSET_MEDIUM, |
483 | .name = "medium", | ||
484 | .fg = "green", | ||
485 | .bg = "default", | ||
486 | }, | ||
487 | { | ||
488 | .colorset = HE_COLORSET_NORMAL, | ||
489 | .name = "normal", | ||
490 | .fg = "default", | ||
491 | .bg = "default", | ||
492 | }, | ||
493 | { | ||
494 | .colorset = HE_COLORSET_SELECTED, | ||
495 | .name = "selected", | ||
496 | .fg = "black", | ||
497 | .bg = "lightgray", | ||
498 | }, | ||
499 | { | ||
500 | .colorset = HE_COLORSET_CODE, | ||
501 | .name = "code", | ||
502 | .fg = "blue", | ||
503 | .bg = "default", | ||
504 | }, | ||
505 | { | ||
506 | .name = NULL, | ||
507 | } | ||
345 | }; | 508 | }; |
346 | 509 | ||
510 | |||
511 | static int ui_browser__color_config(const char *var, const char *value, | ||
512 | void *data __used) | ||
513 | { | ||
514 | char *fg = NULL, *bg; | ||
515 | int i; | ||
516 | |||
517 | /* same dir for all commands */ | ||
518 | if (prefixcmp(var, "colors.") != 0) | ||
519 | return 0; | ||
520 | |||
521 | for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) { | ||
522 | const char *name = var + 7; | ||
523 | |||
524 | if (strcmp(ui_browser__colorsets[i].name, name) != 0) | ||
525 | continue; | ||
526 | |||
527 | fg = strdup(value); | ||
528 | if (fg == NULL) | ||
529 | break; | ||
530 | |||
531 | bg = strchr(fg, ','); | ||
532 | if (bg == NULL) | ||
533 | break; | ||
534 | |||
535 | *bg = '\0'; | ||
536 | while (isspace(*++bg)); | ||
537 | ui_browser__colorsets[i].bg = bg; | ||
538 | ui_browser__colorsets[i].fg = fg; | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | free(fg); | ||
543 | return -1; | ||
544 | } | ||
545 | |||
546 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence) | ||
547 | { | ||
548 | switch (whence) { | ||
549 | case SEEK_SET: | ||
550 | browser->top = browser->entries; | ||
551 | break; | ||
552 | case SEEK_CUR: | ||
553 | browser->top = browser->top + browser->top_idx + offset; | ||
554 | break; | ||
555 | case SEEK_END: | ||
556 | browser->top = browser->top + browser->nr_entries + offset; | ||
557 | break; | ||
558 | default: | ||
559 | return; | ||
560 | } | ||
561 | } | ||
562 | |||
563 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser) | ||
564 | { | ||
565 | unsigned int row = 0, idx = browser->top_idx; | ||
566 | char **pos; | ||
567 | |||
568 | if (browser->top == NULL) | ||
569 | browser->top = browser->entries; | ||
570 | |||
571 | pos = (char **)browser->top; | ||
572 | while (idx < browser->nr_entries) { | ||
573 | if (!browser->filter || !browser->filter(browser, *pos)) { | ||
574 | ui_browser__gotorc(browser, row, 0); | ||
575 | browser->write(browser, pos, row); | ||
576 | if (++row == browser->height) | ||
577 | break; | ||
578 | } | ||
579 | |||
580 | ++idx; | ||
581 | ++pos; | ||
582 | } | ||
583 | |||
584 | return row; | ||
585 | } | ||
586 | |||
347 | void ui_browser__init(void) | 587 | void ui_browser__init(void) |
348 | { | 588 | { |
349 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | 589 | int i = 0; |
590 | |||
591 | perf_config(ui_browser__color_config, NULL); | ||
350 | 592 | ||
351 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | 593 | while (ui_browser__colorsets[i].name) { |
352 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | 594 | struct ui_browser__colorset *c = &ui_browser__colorsets[i++]; |
353 | sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); | 595 | sltt_set_color(c->colorset, c->name, c->fg, c->bg); |
354 | sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); | 596 | } |
355 | sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); | ||
356 | } | 597 | } |
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index fc63dda10910..84d761b730c1 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h | |||
@@ -2,7 +2,6 @@ | |||
2 | #define _PERF_UI_BROWSER_H_ 1 | 2 | #define _PERF_UI_BROWSER_H_ 1 |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include <newt.h> | ||
6 | #include <sys/types.h> | 5 | #include <sys/types.h> |
7 | #include "../types.h" | 6 | #include "../types.h" |
8 | 7 | ||
@@ -13,15 +12,19 @@ | |||
13 | #define HE_COLORSET_CODE 54 | 12 | #define HE_COLORSET_CODE 54 |
14 | 13 | ||
15 | struct ui_browser { | 14 | struct ui_browser { |
16 | newtComponent form, sb; | ||
17 | u64 index, top_idx; | 15 | u64 index, top_idx; |
18 | void *top, *entries; | 16 | void *top, *entries; |
19 | u16 y, x, width, height; | 17 | u16 y, x, width, height; |
20 | void *priv; | 18 | void *priv; |
19 | const char *title; | ||
20 | char *helpline; | ||
21 | unsigned int (*refresh)(struct ui_browser *self); | 21 | unsigned int (*refresh)(struct ui_browser *self); |
22 | void (*write)(struct ui_browser *self, void *entry, int row); | 22 | void (*write)(struct ui_browser *self, void *entry, int row); |
23 | void (*seek)(struct ui_browser *self, off_t offset, int whence); | 23 | void (*seek)(struct ui_browser *self, off_t offset, int whence); |
24 | bool (*filter)(struct ui_browser *self, void *entry); | ||
24 | u32 nr_entries; | 25 | u32 nr_entries; |
26 | bool navkeypressed; | ||
27 | bool use_navkeypressed; | ||
25 | }; | 28 | }; |
26 | 29 | ||
27 | void ui_browser__set_color(struct ui_browser *self, int color); | 30 | void ui_browser__set_color(struct ui_browser *self, int color); |
@@ -32,15 +35,23 @@ void ui_browser__refresh_dimensions(struct ui_browser *self); | |||
32 | void ui_browser__reset_index(struct ui_browser *self); | 35 | void ui_browser__reset_index(struct ui_browser *self); |
33 | 36 | ||
34 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); | 37 | void ui_browser__gotorc(struct ui_browser *self, int y, int x); |
35 | void ui_browser__add_exit_key(struct ui_browser *self, int key); | ||
36 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]); | ||
37 | void __ui_browser__show_title(struct ui_browser *browser, const char *title); | 38 | void __ui_browser__show_title(struct ui_browser *browser, const char *title); |
38 | void ui_browser__show_title(struct ui_browser *browser, const char *title); | 39 | void ui_browser__show_title(struct ui_browser *browser, const char *title); |
39 | int ui_browser__show(struct ui_browser *self, const char *title, | 40 | int ui_browser__show(struct ui_browser *self, const char *title, |
40 | const char *helpline, ...); | 41 | const char *helpline, ...); |
41 | void ui_browser__hide(struct ui_browser *self); | 42 | void ui_browser__hide(struct ui_browser *self); |
42 | int ui_browser__refresh(struct ui_browser *self); | 43 | int ui_browser__refresh(struct ui_browser *self); |
43 | int ui_browser__run(struct ui_browser *self); | 44 | int ui_browser__run(struct ui_browser *browser, int delay_secs); |
45 | void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries); | ||
46 | void ui_browser__handle_resize(struct ui_browser *browser); | ||
47 | |||
48 | int ui_browser__warning(struct ui_browser *browser, int timeout, | ||
49 | const char *format, ...); | ||
50 | int ui_browser__help_window(struct ui_browser *browser, const char *text); | ||
51 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); | ||
52 | |||
53 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); | ||
54 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser); | ||
44 | 55 | ||
45 | void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); | 56 | void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); |
46 | unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); | 57 | unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self); |
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 0229723aceb3..0575905d1205 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c | |||
@@ -1,31 +1,31 @@ | |||
1 | #include "../../util.h" | ||
1 | #include "../browser.h" | 2 | #include "../browser.h" |
2 | #include "../helpline.h" | 3 | #include "../helpline.h" |
3 | #include "../libslang.h" | 4 | #include "../libslang.h" |
5 | #include "../ui.h" | ||
6 | #include "../util.h" | ||
4 | #include "../../annotate.h" | 7 | #include "../../annotate.h" |
5 | #include "../../hist.h" | 8 | #include "../../hist.h" |
6 | #include "../../sort.h" | 9 | #include "../../sort.h" |
7 | #include "../../symbol.h" | 10 | #include "../../symbol.h" |
8 | #include <pthread.h> | 11 | #include <pthread.h> |
9 | 12 | #include <newt.h> | |
10 | static void ui__error_window(const char *fmt, ...) | ||
11 | { | ||
12 | va_list ap; | ||
13 | |||
14 | va_start(ap, fmt); | ||
15 | newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap); | ||
16 | va_end(ap); | ||
17 | } | ||
18 | 13 | ||
19 | struct annotate_browser { | 14 | struct annotate_browser { |
20 | struct ui_browser b; | 15 | struct ui_browser b; |
21 | struct rb_root entries; | 16 | struct rb_root entries; |
22 | struct rb_node *curr_hot; | 17 | struct rb_node *curr_hot; |
18 | struct objdump_line *selection; | ||
19 | int nr_asm_entries; | ||
20 | int nr_entries; | ||
21 | bool hide_src_code; | ||
23 | }; | 22 | }; |
24 | 23 | ||
25 | struct objdump_line_rb_node { | 24 | struct objdump_line_rb_node { |
26 | struct rb_node rb_node; | 25 | struct rb_node rb_node; |
27 | double percent; | 26 | double percent; |
28 | u32 idx; | 27 | u32 idx; |
28 | int idx_asm; | ||
29 | }; | 29 | }; |
30 | 30 | ||
31 | static inline | 31 | static inline |
@@ -34,9 +34,22 @@ struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self) | |||
34 | return (struct objdump_line_rb_node *)(self + 1); | 34 | return (struct objdump_line_rb_node *)(self + 1); |
35 | } | 35 | } |
36 | 36 | ||
37 | static bool objdump_line__filter(struct ui_browser *browser, void *entry) | ||
38 | { | ||
39 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); | ||
40 | |||
41 | if (ab->hide_src_code) { | ||
42 | struct objdump_line *ol = list_entry(entry, struct objdump_line, node); | ||
43 | return ol->offset == -1; | ||
44 | } | ||
45 | |||
46 | return false; | ||
47 | } | ||
48 | |||
37 | static void annotate_browser__write(struct ui_browser *self, void *entry, int row) | 49 | static void annotate_browser__write(struct ui_browser *self, void *entry, int row) |
38 | { | 50 | { |
39 | struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); | 51 | struct annotate_browser *ab = container_of(self, struct annotate_browser, b); |
52 | struct objdump_line *ol = list_entry(entry, struct objdump_line, node); | ||
40 | bool current_entry = ui_browser__is_current_entry(self, row); | 53 | bool current_entry = ui_browser__is_current_entry(self, row); |
41 | int width = self->width; | 54 | int width = self->width; |
42 | 55 | ||
@@ -51,6 +64,11 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
51 | 64 | ||
52 | SLsmg_write_char(':'); | 65 | SLsmg_write_char(':'); |
53 | slsmg_write_nstring(" ", 8); | 66 | slsmg_write_nstring(" ", 8); |
67 | |||
68 | /* The scroll bar isn't being used */ | ||
69 | if (!self->navkeypressed) | ||
70 | width += 1; | ||
71 | |||
54 | if (!*ol->line) | 72 | if (!*ol->line) |
55 | slsmg_write_nstring(" ", width - 18); | 73 | slsmg_write_nstring(" ", width - 18); |
56 | else | 74 | else |
@@ -58,6 +76,8 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
58 | 76 | ||
59 | if (!current_entry) | 77 | if (!current_entry) |
60 | ui_browser__set_color(self, HE_COLORSET_CODE); | 78 | ui_browser__set_color(self, HE_COLORSET_CODE); |
79 | else | ||
80 | ab->selection = ol; | ||
61 | } | 81 | } |
62 | 82 | ||
63 | static double objdump_line__calc_percent(struct objdump_line *self, | 83 | static double objdump_line__calc_percent(struct objdump_line *self, |
@@ -141,7 +161,8 @@ static void annotate_browser__set_top(struct annotate_browser *self, | |||
141 | static void annotate_browser__calc_percent(struct annotate_browser *browser, | 161 | static void annotate_browser__calc_percent(struct annotate_browser *browser, |
142 | int evidx) | 162 | int evidx) |
143 | { | 163 | { |
144 | struct symbol *sym = browser->b.priv; | 164 | struct map_symbol *ms = browser->b.priv; |
165 | struct symbol *sym = ms->sym; | ||
145 | struct annotation *notes = symbol__annotation(sym); | 166 | struct annotation *notes = symbol__annotation(sym); |
146 | struct objdump_line *pos; | 167 | struct objdump_line *pos; |
147 | 168 | ||
@@ -163,25 +184,60 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser, | |||
163 | browser->curr_hot = rb_last(&browser->entries); | 184 | browser->curr_hot = rb_last(&browser->entries); |
164 | } | 185 | } |
165 | 186 | ||
187 | static bool annotate_browser__toggle_source(struct annotate_browser *browser) | ||
188 | { | ||
189 | struct objdump_line *ol; | ||
190 | struct objdump_line_rb_node *olrb; | ||
191 | off_t offset = browser->b.index - browser->b.top_idx; | ||
192 | |||
193 | browser->b.seek(&browser->b, offset, SEEK_CUR); | ||
194 | ol = list_entry(browser->b.top, struct objdump_line, node); | ||
195 | olrb = objdump_line__rb(ol); | ||
196 | |||
197 | if (browser->hide_src_code) { | ||
198 | if (olrb->idx_asm < offset) | ||
199 | offset = olrb->idx; | ||
200 | |||
201 | browser->b.nr_entries = browser->nr_entries; | ||
202 | browser->hide_src_code = false; | ||
203 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
204 | browser->b.top_idx = olrb->idx - offset; | ||
205 | browser->b.index = olrb->idx; | ||
206 | } else { | ||
207 | if (olrb->idx_asm < 0) { | ||
208 | ui_helpline__puts("Only available for assembly lines."); | ||
209 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
210 | return false; | ||
211 | } | ||
212 | |||
213 | if (olrb->idx_asm < offset) | ||
214 | offset = olrb->idx_asm; | ||
215 | |||
216 | browser->b.nr_entries = browser->nr_asm_entries; | ||
217 | browser->hide_src_code = true; | ||
218 | browser->b.seek(&browser->b, -offset, SEEK_CUR); | ||
219 | browser->b.top_idx = olrb->idx_asm - offset; | ||
220 | browser->b.index = olrb->idx_asm; | ||
221 | } | ||
222 | |||
223 | return true; | ||
224 | } | ||
225 | |||
166 | static int annotate_browser__run(struct annotate_browser *self, int evidx, | 226 | static int annotate_browser__run(struct annotate_browser *self, int evidx, |
167 | int refresh) | 227 | int nr_events, void(*timer)(void *arg), |
228 | void *arg, int delay_secs) | ||
168 | { | 229 | { |
169 | struct rb_node *nd = NULL; | 230 | struct rb_node *nd = NULL; |
170 | struct symbol *sym = self->b.priv; | 231 | struct map_symbol *ms = self->b.priv; |
171 | /* | 232 | struct symbol *sym = ms->sym; |
172 | * RIGHT To allow builtin-annotate to cycle thru multiple symbols by | 233 | const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, " |
173 | * examining the exit key for this function. | 234 | "H: Hottest, -> Line action, S -> Toggle source " |
174 | */ | 235 | "code view"; |
175 | int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB, | ||
176 | NEWT_KEY_RIGHT, 0 }; | ||
177 | int key; | 236 | int key; |
178 | 237 | ||
179 | if (ui_browser__show(&self->b, sym->name, | 238 | if (ui_browser__show(&self->b, sym->name, help) < 0) |
180 | "<-, -> or ESC: exit, TAB/shift+TAB: " | ||
181 | "cycle hottest lines, H: Hottest") < 0) | ||
182 | return -1; | 239 | return -1; |
183 | 240 | ||
184 | ui_browser__add_exit_keys(&self->b, exit_keys); | ||
185 | annotate_browser__calc_percent(self, evidx); | 241 | annotate_browser__calc_percent(self, evidx); |
186 | 242 | ||
187 | if (self->curr_hot) | 243 | if (self->curr_hot) |
@@ -189,13 +245,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
189 | 245 | ||
190 | nd = self->curr_hot; | 246 | nd = self->curr_hot; |
191 | 247 | ||
192 | if (refresh != 0) | ||
193 | newtFormSetTimer(self->b.form, refresh); | ||
194 | |||
195 | while (1) { | 248 | while (1) { |
196 | key = ui_browser__run(&self->b); | 249 | key = ui_browser__run(&self->b, delay_secs); |
197 | 250 | ||
198 | if (refresh != 0) { | 251 | if (delay_secs != 0) { |
199 | annotate_browser__calc_percent(self, evidx); | 252 | annotate_browser__calc_percent(self, evidx); |
200 | /* | 253 | /* |
201 | * Current line focus got out of the list of most active | 254 | * Current line focus got out of the list of most active |
@@ -207,15 +260,14 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
207 | } | 260 | } |
208 | 261 | ||
209 | switch (key) { | 262 | switch (key) { |
210 | case -1: | 263 | case K_TIMER: |
211 | /* | 264 | if (timer != NULL) |
212 | * FIXME we need to check if it was | 265 | timer(arg); |
213 | * es.reason == NEWT_EXIT_TIMER | 266 | |
214 | */ | 267 | if (delay_secs != 0) |
215 | if (refresh != 0) | ||
216 | symbol__annotate_decay_histogram(sym, evidx); | 268 | symbol__annotate_decay_histogram(sym, evidx); |
217 | continue; | 269 | continue; |
218 | case NEWT_KEY_TAB: | 270 | case K_TAB: |
219 | if (nd != NULL) { | 271 | if (nd != NULL) { |
220 | nd = rb_prev(nd); | 272 | nd = rb_prev(nd); |
221 | if (nd == NULL) | 273 | if (nd == NULL) |
@@ -223,7 +275,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
223 | } else | 275 | } else |
224 | nd = self->curr_hot; | 276 | nd = self->curr_hot; |
225 | break; | 277 | break; |
226 | case NEWT_KEY_UNTAB: | 278 | case K_UNTAB: |
227 | if (nd != NULL) | 279 | if (nd != NULL) |
228 | nd = rb_next(nd); | 280 | nd = rb_next(nd); |
229 | if (nd == NULL) | 281 | if (nd == NULL) |
@@ -234,8 +286,68 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
234 | case 'H': | 286 | case 'H': |
235 | nd = self->curr_hot; | 287 | nd = self->curr_hot; |
236 | break; | 288 | break; |
237 | default: | 289 | case 'S': |
290 | if (annotate_browser__toggle_source(self)) | ||
291 | ui_helpline__puts(help); | ||
292 | continue; | ||
293 | case K_ENTER: | ||
294 | case K_RIGHT: | ||
295 | if (self->selection == NULL) { | ||
296 | ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); | ||
297 | continue; | ||
298 | } | ||
299 | |||
300 | if (self->selection->offset == -1) { | ||
301 | ui_helpline__puts("Actions are only available for assembly lines."); | ||
302 | continue; | ||
303 | } else { | ||
304 | char *s = strstr(self->selection->line, "callq "); | ||
305 | struct annotation *notes; | ||
306 | struct symbol *target; | ||
307 | u64 ip; | ||
308 | |||
309 | if (s == NULL) { | ||
310 | ui_helpline__puts("Actions are only available for the 'callq' instruction."); | ||
311 | continue; | ||
312 | } | ||
313 | |||
314 | s = strchr(s, ' '); | ||
315 | if (s++ == NULL) { | ||
316 | ui_helpline__puts("Invallid callq instruction."); | ||
317 | continue; | ||
318 | } | ||
319 | |||
320 | ip = strtoull(s, NULL, 16); | ||
321 | ip = ms->map->map_ip(ms->map, ip); | ||
322 | target = map__find_symbol(ms->map, ip, NULL); | ||
323 | if (target == NULL) { | ||
324 | ui_helpline__puts("The called function was not found."); | ||
325 | continue; | ||
326 | } | ||
327 | |||
328 | notes = symbol__annotation(target); | ||
329 | pthread_mutex_lock(¬es->lock); | ||
330 | |||
331 | if (notes->src == NULL && | ||
332 | symbol__alloc_hist(target, nr_events) < 0) { | ||
333 | pthread_mutex_unlock(¬es->lock); | ||
334 | ui__warning("Not enough memory for annotating '%s' symbol!\n", | ||
335 | target->name); | ||
336 | continue; | ||
337 | } | ||
338 | |||
339 | pthread_mutex_unlock(¬es->lock); | ||
340 | symbol__tui_annotate(target, ms->map, evidx, nr_events, | ||
341 | timer, arg, delay_secs); | ||
342 | } | ||
343 | continue; | ||
344 | case K_LEFT: | ||
345 | case K_ESC: | ||
346 | case 'q': | ||
347 | case CTRL('c'): | ||
238 | goto out; | 348 | goto out; |
349 | default: | ||
350 | continue; | ||
239 | } | 351 | } |
240 | 352 | ||
241 | if (nd != NULL) | 353 | if (nd != NULL) |
@@ -246,22 +358,31 @@ out: | |||
246 | return key; | 358 | return key; |
247 | } | 359 | } |
248 | 360 | ||
249 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx) | 361 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events, |
362 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
250 | { | 363 | { |
251 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); | 364 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, nr_events, |
365 | timer, arg, delay_secs); | ||
252 | } | 366 | } |
253 | 367 | ||
254 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 368 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
255 | int refresh) | 369 | int nr_events, void(*timer)(void *arg), void *arg, |
370 | int delay_secs) | ||
256 | { | 371 | { |
257 | struct objdump_line *pos, *n; | 372 | struct objdump_line *pos, *n; |
258 | struct annotation *notes; | 373 | struct annotation *notes; |
374 | struct map_symbol ms = { | ||
375 | .map = map, | ||
376 | .sym = sym, | ||
377 | }; | ||
259 | struct annotate_browser browser = { | 378 | struct annotate_browser browser = { |
260 | .b = { | 379 | .b = { |
261 | .refresh = ui_browser__list_head_refresh, | 380 | .refresh = ui_browser__list_head_refresh, |
262 | .seek = ui_browser__list_head_seek, | 381 | .seek = ui_browser__list_head_seek, |
263 | .write = annotate_browser__write, | 382 | .write = annotate_browser__write, |
264 | .priv = sym, | 383 | .filter = objdump_line__filter, |
384 | .priv = &ms, | ||
385 | .use_navkeypressed = true, | ||
265 | }, | 386 | }, |
266 | }; | 387 | }; |
267 | int ret; | 388 | int ret; |
@@ -273,7 +394,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
273 | return -1; | 394 | return -1; |
274 | 395 | ||
275 | if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { | 396 | if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) { |
276 | ui__error_window(ui_helpline__last_msg); | 397 | ui__error("%s", ui_helpline__last_msg); |
277 | return -1; | 398 | return -1; |
278 | } | 399 | } |
279 | 400 | ||
@@ -288,12 +409,18 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
288 | if (browser.b.width < line_len) | 409 | if (browser.b.width < line_len) |
289 | browser.b.width = line_len; | 410 | browser.b.width = line_len; |
290 | rbpos = objdump_line__rb(pos); | 411 | rbpos = objdump_line__rb(pos); |
291 | rbpos->idx = browser.b.nr_entries++; | 412 | rbpos->idx = browser.nr_entries++; |
413 | if (pos->offset != -1) | ||
414 | rbpos->idx_asm = browser.nr_asm_entries++; | ||
415 | else | ||
416 | rbpos->idx_asm = -1; | ||
292 | } | 417 | } |
293 | 418 | ||
419 | browser.b.nr_entries = browser.nr_entries; | ||
294 | browser.b.entries = ¬es->src->source, | 420 | browser.b.entries = ¬es->src->source, |
295 | browser.b.width += 18; /* Percentage */ | 421 | browser.b.width += 18; /* Percentage */ |
296 | ret = annotate_browser__run(&browser, evidx, refresh); | 422 | ret = annotate_browser__run(&browser, evidx, nr_events, |
423 | timer, arg, delay_secs); | ||
297 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | 424 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
298 | list_del(&pos->node); | 425 | list_del(&pos->node); |
299 | objdump_line__free(pos); | 426 | objdump_line__free(pos); |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 5d767c622dfc..d0c94b459685 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "../browser.h" | 17 | #include "../browser.h" |
18 | #include "../helpline.h" | 18 | #include "../helpline.h" |
19 | #include "../util.h" | 19 | #include "../util.h" |
20 | #include "../ui.h" | ||
20 | #include "map.h" | 21 | #include "map.h" |
21 | 22 | ||
22 | struct hist_browser { | 23 | struct hist_browser { |
@@ -24,8 +25,12 @@ struct hist_browser { | |||
24 | struct hists *hists; | 25 | struct hists *hists; |
25 | struct hist_entry *he_selection; | 26 | struct hist_entry *he_selection; |
26 | struct map_symbol *selection; | 27 | struct map_symbol *selection; |
28 | bool has_symbols; | ||
27 | }; | 29 | }; |
28 | 30 | ||
31 | static int hists__browser_title(struct hists *self, char *bf, size_t size, | ||
32 | const char *ev_name); | ||
33 | |||
29 | static void hist_browser__refresh_dimensions(struct hist_browser *self) | 34 | static void hist_browser__refresh_dimensions(struct hist_browser *self) |
30 | { | 35 | { |
31 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ | 36 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ |
@@ -290,28 +295,49 @@ static void hist_browser__set_folding(struct hist_browser *self, bool unfold) | |||
290 | ui_browser__reset_index(&self->b); | 295 | ui_browser__reset_index(&self->b); |
291 | } | 296 | } |
292 | 297 | ||
293 | static int hist_browser__run(struct hist_browser *self, const char *title) | 298 | static void ui_browser__warn_lost_events(struct ui_browser *browser) |
299 | { | ||
300 | ui_browser__warning(browser, 4, | ||
301 | "Events are being lost, check IO/CPU overload!\n\n" | ||
302 | "You may want to run 'perf' using a RT scheduler policy:\n\n" | ||
303 | " perf top -r 80\n\n" | ||
304 | "Or reduce the sampling frequency."); | ||
305 | } | ||
306 | |||
307 | static int hist_browser__run(struct hist_browser *self, const char *ev_name, | ||
308 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
294 | { | 309 | { |
295 | int key; | 310 | int key; |
296 | int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', | 311 | char title[160]; |
297 | NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT, | ||
298 | NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, }; | ||
299 | 312 | ||
300 | self->b.entries = &self->hists->entries; | 313 | self->b.entries = &self->hists->entries; |
301 | self->b.nr_entries = self->hists->nr_entries; | 314 | self->b.nr_entries = self->hists->nr_entries; |
302 | 315 | ||
303 | hist_browser__refresh_dimensions(self); | 316 | hist_browser__refresh_dimensions(self); |
317 | hists__browser_title(self->hists, title, sizeof(title), ev_name); | ||
304 | 318 | ||
305 | if (ui_browser__show(&self->b, title, | 319 | if (ui_browser__show(&self->b, title, |
306 | "Press '?' for help on key bindings") < 0) | 320 | "Press '?' for help on key bindings") < 0) |
307 | return -1; | 321 | return -1; |
308 | 322 | ||
309 | ui_browser__add_exit_keys(&self->b, exit_keys); | ||
310 | |||
311 | while (1) { | 323 | while (1) { |
312 | key = ui_browser__run(&self->b); | 324 | key = ui_browser__run(&self->b, delay_secs); |
313 | 325 | ||
314 | switch (key) { | 326 | switch (key) { |
327 | case K_TIMER: | ||
328 | timer(arg); | ||
329 | ui_browser__update_nr_entries(&self->b, self->hists->nr_entries); | ||
330 | |||
331 | if (self->hists->stats.nr_lost_warned != | ||
332 | self->hists->stats.nr_events[PERF_RECORD_LOST]) { | ||
333 | self->hists->stats.nr_lost_warned = | ||
334 | self->hists->stats.nr_events[PERF_RECORD_LOST]; | ||
335 | ui_browser__warn_lost_events(&self->b); | ||
336 | } | ||
337 | |||
338 | hists__browser_title(self->hists, title, sizeof(title), ev_name); | ||
339 | ui_browser__show_title(&self->b, title); | ||
340 | continue; | ||
315 | case 'D': { /* Debug */ | 341 | case 'D': { /* Debug */ |
316 | static int seq; | 342 | static int seq; |
317 | struct hist_entry *h = rb_entry(self->b.top, | 343 | struct hist_entry *h = rb_entry(self->b.top, |
@@ -334,7 +360,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title) | |||
334 | /* Expand the whole world. */ | 360 | /* Expand the whole world. */ |
335 | hist_browser__set_folding(self, true); | 361 | hist_browser__set_folding(self, true); |
336 | break; | 362 | break; |
337 | case NEWT_KEY_ENTER: | 363 | case K_ENTER: |
338 | if (hist_browser__toggle_fold(self)) | 364 | if (hist_browser__toggle_fold(self)) |
339 | break; | 365 | break; |
340 | /* fall thru */ | 366 | /* fall thru */ |
@@ -532,7 +558,7 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
532 | char s[256]; | 558 | char s[256]; |
533 | double percent; | 559 | double percent; |
534 | int printed = 0; | 560 | int printed = 0; |
535 | int color, width = self->b.width; | 561 | int width = self->b.width - 6; /* The percentage */ |
536 | char folded_sign = ' '; | 562 | char folded_sign = ' '; |
537 | bool current_entry = ui_browser__is_current_entry(&self->b, row); | 563 | bool current_entry = ui_browser__is_current_entry(&self->b, row); |
538 | off_t row_offset = entry->row_offset; | 564 | off_t row_offset = entry->row_offset; |
@@ -548,26 +574,35 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
548 | } | 574 | } |
549 | 575 | ||
550 | if (row_offset == 0) { | 576 | if (row_offset == 0) { |
551 | hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, | 577 | hist_entry__snprintf(entry, s, sizeof(s), self->hists); |
552 | 0, false, self->hists->stats.total_period); | ||
553 | percent = (entry->period * 100.0) / self->hists->stats.total_period; | 578 | percent = (entry->period * 100.0) / self->hists->stats.total_period; |
554 | 579 | ||
555 | color = HE_COLORSET_SELECTED; | 580 | ui_browser__set_percent_color(&self->b, percent, current_entry); |
556 | if (!current_entry) { | ||
557 | if (percent >= MIN_RED) | ||
558 | color = HE_COLORSET_TOP; | ||
559 | else if (percent >= MIN_GREEN) | ||
560 | color = HE_COLORSET_MEDIUM; | ||
561 | else | ||
562 | color = HE_COLORSET_NORMAL; | ||
563 | } | ||
564 | |||
565 | ui_browser__set_color(&self->b, color); | ||
566 | ui_browser__gotorc(&self->b, row, 0); | 581 | ui_browser__gotorc(&self->b, row, 0); |
567 | if (symbol_conf.use_callchain) { | 582 | if (symbol_conf.use_callchain) { |
568 | slsmg_printf("%c ", folded_sign); | 583 | slsmg_printf("%c ", folded_sign); |
569 | width -= 2; | 584 | width -= 2; |
570 | } | 585 | } |
586 | |||
587 | slsmg_printf(" %5.2f%%", percent); | ||
588 | |||
589 | /* The scroll bar isn't being used */ | ||
590 | if (!self->b.navkeypressed) | ||
591 | width += 1; | ||
592 | |||
593 | if (!current_entry || !self->b.navkeypressed) | ||
594 | ui_browser__set_color(&self->b, HE_COLORSET_NORMAL); | ||
595 | |||
596 | if (symbol_conf.show_nr_samples) { | ||
597 | slsmg_printf(" %11u", entry->nr_events); | ||
598 | width -= 12; | ||
599 | } | ||
600 | |||
601 | if (symbol_conf.show_total_period) { | ||
602 | slsmg_printf(" %12" PRIu64, entry->period); | ||
603 | width -= 13; | ||
604 | } | ||
605 | |||
571 | slsmg_write_nstring(s, width); | 606 | slsmg_write_nstring(s, width); |
572 | ++row; | 607 | ++row; |
573 | ++printed; | 608 | ++printed; |
@@ -585,14 +620,23 @@ static int hist_browser__show_entry(struct hist_browser *self, | |||
585 | return printed; | 620 | return printed; |
586 | } | 621 | } |
587 | 622 | ||
623 | static void ui_browser__hists_init_top(struct ui_browser *browser) | ||
624 | { | ||
625 | if (browser->top == NULL) { | ||
626 | struct hist_browser *hb; | ||
627 | |||
628 | hb = container_of(browser, struct hist_browser, b); | ||
629 | browser->top = rb_first(&hb->hists->entries); | ||
630 | } | ||
631 | } | ||
632 | |||
588 | static unsigned int hist_browser__refresh(struct ui_browser *self) | 633 | static unsigned int hist_browser__refresh(struct ui_browser *self) |
589 | { | 634 | { |
590 | unsigned row = 0; | 635 | unsigned row = 0; |
591 | struct rb_node *nd; | 636 | struct rb_node *nd; |
592 | struct hist_browser *hb = container_of(self, struct hist_browser, b); | 637 | struct hist_browser *hb = container_of(self, struct hist_browser, b); |
593 | 638 | ||
594 | if (self->top == NULL) | 639 | ui_browser__hists_init_top(self); |
595 | self->top = rb_first(&hb->hists->entries); | ||
596 | 640 | ||
597 | for (nd = self->top; nd; nd = rb_next(nd)) { | 641 | for (nd = self->top; nd; nd = rb_next(nd)) { |
598 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 642 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
@@ -644,6 +688,8 @@ static void ui_browser__hists_seek(struct ui_browser *self, | |||
644 | if (self->nr_entries == 0) | 688 | if (self->nr_entries == 0) |
645 | return; | 689 | return; |
646 | 690 | ||
691 | ui_browser__hists_init_top(self); | ||
692 | |||
647 | switch (whence) { | 693 | switch (whence) { |
648 | case SEEK_SET: | 694 | case SEEK_SET: |
649 | nd = hists__filter_entries(rb_first(self->entries)); | 695 | nd = hists__filter_entries(rb_first(self->entries)); |
@@ -761,6 +807,8 @@ static struct hist_browser *hist_browser__new(struct hists *hists) | |||
761 | self->hists = hists; | 807 | self->hists = hists; |
762 | self->b.refresh = hist_browser__refresh; | 808 | self->b.refresh = hist_browser__refresh; |
763 | self->b.seek = ui_browser__hists_seek; | 809 | self->b.seek = ui_browser__hists_seek; |
810 | self->b.use_navkeypressed = true, | ||
811 | self->has_symbols = sort_sym.list.next != NULL; | ||
764 | } | 812 | } |
765 | 813 | ||
766 | return self; | 814 | return self; |
@@ -782,11 +830,12 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self) | |||
782 | } | 830 | } |
783 | 831 | ||
784 | static int hists__browser_title(struct hists *self, char *bf, size_t size, | 832 | static int hists__browser_title(struct hists *self, char *bf, size_t size, |
785 | const char *ev_name, const struct dso *dso, | 833 | const char *ev_name) |
786 | const struct thread *thread) | ||
787 | { | 834 | { |
788 | char unit; | 835 | char unit; |
789 | int printed; | 836 | int printed; |
837 | const struct dso *dso = self->dso_filter; | ||
838 | const struct thread *thread = self->thread_filter; | ||
790 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; | 839 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; |
791 | 840 | ||
792 | nr_events = convert_unit(nr_events, &unit); | 841 | nr_events = convert_unit(nr_events, &unit); |
@@ -803,16 +852,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | |||
803 | return printed; | 852 | return printed; |
804 | } | 853 | } |
805 | 854 | ||
806 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, | 855 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
807 | const char *helpline, const char *ev_name, | 856 | const char *helpline, const char *ev_name, |
808 | bool left_exits) | 857 | bool left_exits, |
858 | void(*timer)(void *arg), void *arg, | ||
859 | int delay_secs) | ||
809 | { | 860 | { |
810 | struct hists *self = &evsel->hists; | 861 | struct hists *self = &evsel->hists; |
811 | struct hist_browser *browser = hist_browser__new(self); | 862 | struct hist_browser *browser = hist_browser__new(self); |
812 | struct pstack *fstack; | 863 | struct pstack *fstack; |
813 | const struct thread *thread_filter = NULL; | ||
814 | const struct dso *dso_filter = NULL; | ||
815 | char msg[160]; | ||
816 | int key = -1; | 864 | int key = -1; |
817 | 865 | ||
818 | if (browser == NULL) | 866 | if (browser == NULL) |
@@ -824,8 +872,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
824 | 872 | ||
825 | ui_helpline__push(helpline); | 873 | ui_helpline__push(helpline); |
826 | 874 | ||
827 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
828 | dso_filter, thread_filter); | ||
829 | while (1) { | 875 | while (1) { |
830 | const struct thread *thread = NULL; | 876 | const struct thread *thread = NULL; |
831 | const struct dso *dso = NULL; | 877 | const struct dso *dso = NULL; |
@@ -834,7 +880,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
834 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 880 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
835 | browse_map = -2; | 881 | browse_map = -2; |
836 | 882 | ||
837 | key = hist_browser__run(browser, msg); | 883 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); |
838 | 884 | ||
839 | if (browser->he_selection != NULL) { | 885 | if (browser->he_selection != NULL) { |
840 | thread = hist_browser__selected_thread(browser); | 886 | thread = hist_browser__selected_thread(browser); |
@@ -842,14 +888,23 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
842 | } | 888 | } |
843 | 889 | ||
844 | switch (key) { | 890 | switch (key) { |
845 | case NEWT_KEY_TAB: | 891 | case K_TAB: |
846 | case NEWT_KEY_UNTAB: | 892 | case K_UNTAB: |
893 | if (nr_events == 1) | ||
894 | continue; | ||
847 | /* | 895 | /* |
848 | * Exit the browser, let hists__browser_tree | 896 | * Exit the browser, let hists__browser_tree |
849 | * go to the next or previous | 897 | * go to the next or previous |
850 | */ | 898 | */ |
851 | goto out_free_stack; | 899 | goto out_free_stack; |
852 | case 'a': | 900 | case 'a': |
901 | if (!browser->has_symbols) { | ||
902 | ui_browser__warning(&browser->b, delay_secs * 2, | ||
903 | "Annotation is only available for symbolic views, " | ||
904 | "include \"sym\" in --sort to use it."); | ||
905 | continue; | ||
906 | } | ||
907 | |||
853 | if (browser->selection == NULL || | 908 | if (browser->selection == NULL || |
854 | browser->selection->sym == NULL || | 909 | browser->selection->sym == NULL || |
855 | browser->selection->map->dso->annotate_warned) | 910 | browser->selection->map->dso->annotate_warned) |
@@ -859,25 +914,30 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
859 | goto zoom_dso; | 914 | goto zoom_dso; |
860 | case 't': | 915 | case 't': |
861 | goto zoom_thread; | 916 | goto zoom_thread; |
862 | case NEWT_KEY_F1: | 917 | case K_F1: |
863 | case 'h': | 918 | case 'h': |
864 | case '?': | 919 | case '?': |
865 | ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" | 920 | ui_browser__help_window(&browser->b, |
866 | "<- Zoom out\n" | 921 | "h/?/F1 Show this window\n" |
867 | "a Annotate current symbol\n" | 922 | "UP/DOWN/PGUP\n" |
868 | "h/?/F1 Show this window\n" | 923 | "PGDN/SPACE Navigate\n" |
869 | "C Collapse all callchains\n" | 924 | "q/ESC/CTRL+C Exit browser\n\n" |
870 | "E Expand all callchains\n" | 925 | "For multiple event sessions:\n\n" |
871 | "d Zoom into current DSO\n" | 926 | "TAB/UNTAB Switch events\n\n" |
872 | "t Zoom into current Thread\n" | 927 | "For symbolic views (--sort has sym):\n\n" |
873 | "TAB/UNTAB Switch events\n" | 928 | "-> Zoom into DSO/Threads & Annotate current symbol\n" |
874 | "q/CTRL+C Exit browser"); | 929 | "<- Zoom out\n" |
930 | "a Annotate current symbol\n" | ||
931 | "C Collapse all callchains\n" | ||
932 | "E Expand all callchains\n" | ||
933 | "d Zoom into current DSO\n" | ||
934 | "t Zoom into current Thread"); | ||
875 | continue; | 935 | continue; |
876 | case NEWT_KEY_ENTER: | 936 | case K_ENTER: |
877 | case NEWT_KEY_RIGHT: | 937 | case K_RIGHT: |
878 | /* menu */ | 938 | /* menu */ |
879 | break; | 939 | break; |
880 | case NEWT_KEY_LEFT: { | 940 | case K_LEFT: { |
881 | const void *top; | 941 | const void *top; |
882 | 942 | ||
883 | if (pstack__empty(fstack)) { | 943 | if (pstack__empty(fstack)) { |
@@ -889,21 +949,28 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
889 | continue; | 949 | continue; |
890 | } | 950 | } |
891 | top = pstack__pop(fstack); | 951 | top = pstack__pop(fstack); |
892 | if (top == &dso_filter) | 952 | if (top == &browser->hists->dso_filter) |
893 | goto zoom_out_dso; | 953 | goto zoom_out_dso; |
894 | if (top == &thread_filter) | 954 | if (top == &browser->hists->thread_filter) |
895 | goto zoom_out_thread; | 955 | goto zoom_out_thread; |
896 | continue; | 956 | continue; |
897 | } | 957 | } |
898 | case NEWT_KEY_ESCAPE: | 958 | case K_ESC: |
899 | if (!left_exits && | 959 | if (!left_exits && |
900 | !ui__dialog_yesno("Do you really want to exit?")) | 960 | !ui_browser__dialog_yesno(&browser->b, |
961 | "Do you really want to exit?")) | ||
901 | continue; | 962 | continue; |
902 | /* Fall thru */ | 963 | /* Fall thru */ |
903 | default: | 964 | case 'q': |
965 | case CTRL('c'): | ||
904 | goto out_free_stack; | 966 | goto out_free_stack; |
967 | default: | ||
968 | continue; | ||
905 | } | 969 | } |
906 | 970 | ||
971 | if (!browser->has_symbols) | ||
972 | goto add_exit_option; | ||
973 | |||
907 | if (browser->selection != NULL && | 974 | if (browser->selection != NULL && |
908 | browser->selection->sym != NULL && | 975 | browser->selection->sym != NULL && |
909 | !browser->selection->map->dso->annotate_warned && | 976 | !browser->selection->map->dso->annotate_warned && |
@@ -913,14 +980,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
913 | 980 | ||
914 | if (thread != NULL && | 981 | if (thread != NULL && |
915 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", | 982 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", |
916 | (thread_filter ? "out of" : "into"), | 983 | (browser->hists->thread_filter ? "out of" : "into"), |
917 | (thread->comm_set ? thread->comm : ""), | 984 | (thread->comm_set ? thread->comm : ""), |
918 | thread->pid) > 0) | 985 | thread->pid) > 0) |
919 | zoom_thread = nr_options++; | 986 | zoom_thread = nr_options++; |
920 | 987 | ||
921 | if (dso != NULL && | 988 | if (dso != NULL && |
922 | asprintf(&options[nr_options], "Zoom %s %s DSO", | 989 | asprintf(&options[nr_options], "Zoom %s %s DSO", |
923 | (dso_filter ? "out of" : "into"), | 990 | (browser->hists->dso_filter ? "out of" : "into"), |
924 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) | 991 | (dso->kernel ? "the Kernel" : dso->short_name)) > 0) |
925 | zoom_dso = nr_options++; | 992 | zoom_dso = nr_options++; |
926 | 993 | ||
@@ -928,7 +995,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
928 | browser->selection->map != NULL && | 995 | browser->selection->map != NULL && |
929 | asprintf(&options[nr_options], "Browse map details") > 0) | 996 | asprintf(&options[nr_options], "Browse map details") > 0) |
930 | browse_map = nr_options++; | 997 | browse_map = nr_options++; |
931 | 998 | add_exit_option: | |
932 | options[nr_options++] = (char *)"Exit"; | 999 | options[nr_options++] = (char *)"Exit"; |
933 | 1000 | ||
934 | choice = ui__popup_menu(nr_options, options); | 1001 | choice = ui__popup_menu(nr_options, options); |
@@ -944,50 +1011,59 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, | |||
944 | 1011 | ||
945 | if (choice == annotate) { | 1012 | if (choice == annotate) { |
946 | struct hist_entry *he; | 1013 | struct hist_entry *he; |
1014 | int err; | ||
947 | do_annotate: | 1015 | do_annotate: |
948 | he = hist_browser__selected_entry(browser); | 1016 | he = hist_browser__selected_entry(browser); |
949 | if (he == NULL) | 1017 | if (he == NULL) |
950 | continue; | 1018 | continue; |
951 | 1019 | /* | |
952 | hist_entry__tui_annotate(he, evsel->idx); | 1020 | * Don't let this be freed, say, by hists__decay_entry. |
1021 | */ | ||
1022 | he->used = true; | ||
1023 | err = hist_entry__tui_annotate(he, evsel->idx, nr_events, | ||
1024 | timer, arg, delay_secs); | ||
1025 | he->used = false; | ||
1026 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | ||
1027 | if (err) | ||
1028 | ui_browser__handle_resize(&browser->b); | ||
953 | } else if (choice == browse_map) | 1029 | } else if (choice == browse_map) |
954 | map__browse(browser->selection->map); | 1030 | map__browse(browser->selection->map); |
955 | else if (choice == zoom_dso) { | 1031 | else if (choice == zoom_dso) { |
956 | zoom_dso: | 1032 | zoom_dso: |
957 | if (dso_filter) { | 1033 | if (browser->hists->dso_filter) { |
958 | pstack__remove(fstack, &dso_filter); | 1034 | pstack__remove(fstack, &browser->hists->dso_filter); |
959 | zoom_out_dso: | 1035 | zoom_out_dso: |
960 | ui_helpline__pop(); | 1036 | ui_helpline__pop(); |
961 | dso_filter = NULL; | 1037 | browser->hists->dso_filter = NULL; |
1038 | sort_dso.elide = false; | ||
962 | } else { | 1039 | } else { |
963 | if (dso == NULL) | 1040 | if (dso == NULL) |
964 | continue; | 1041 | continue; |
965 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", | 1042 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", |
966 | dso->kernel ? "the Kernel" : dso->short_name); | 1043 | dso->kernel ? "the Kernel" : dso->short_name); |
967 | dso_filter = dso; | 1044 | browser->hists->dso_filter = dso; |
968 | pstack__push(fstack, &dso_filter); | 1045 | sort_dso.elide = true; |
1046 | pstack__push(fstack, &browser->hists->dso_filter); | ||
969 | } | 1047 | } |
970 | hists__filter_by_dso(self, dso_filter); | 1048 | hists__filter_by_dso(self); |
971 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
972 | dso_filter, thread_filter); | ||
973 | hist_browser__reset(browser); | 1049 | hist_browser__reset(browser); |
974 | } else if (choice == zoom_thread) { | 1050 | } else if (choice == zoom_thread) { |
975 | zoom_thread: | 1051 | zoom_thread: |
976 | if (thread_filter) { | 1052 | if (browser->hists->thread_filter) { |
977 | pstack__remove(fstack, &thread_filter); | 1053 | pstack__remove(fstack, &browser->hists->thread_filter); |
978 | zoom_out_thread: | 1054 | zoom_out_thread: |
979 | ui_helpline__pop(); | 1055 | ui_helpline__pop(); |
980 | thread_filter = NULL; | 1056 | browser->hists->thread_filter = NULL; |
1057 | sort_thread.elide = false; | ||
981 | } else { | 1058 | } else { |
982 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", | 1059 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", |
983 | thread->comm_set ? thread->comm : "", | 1060 | thread->comm_set ? thread->comm : "", |
984 | thread->pid); | 1061 | thread->pid); |
985 | thread_filter = thread; | 1062 | browser->hists->thread_filter = thread; |
986 | pstack__push(fstack, &thread_filter); | 1063 | sort_thread.elide = true; |
1064 | pstack__push(fstack, &browser->hists->thread_filter); | ||
987 | } | 1065 | } |
988 | hists__filter_by_thread(self, thread_filter); | 1066 | hists__filter_by_thread(self); |
989 | hists__browser_title(self, msg, sizeof(msg), ev_name, | ||
990 | dso_filter, thread_filter); | ||
991 | hist_browser__reset(browser); | 1067 | hist_browser__reset(browser); |
992 | } | 1068 | } |
993 | } | 1069 | } |
@@ -1001,6 +1077,7 @@ out: | |||
1001 | struct perf_evsel_menu { | 1077 | struct perf_evsel_menu { |
1002 | struct ui_browser b; | 1078 | struct ui_browser b; |
1003 | struct perf_evsel *selection; | 1079 | struct perf_evsel *selection; |
1080 | bool lost_events, lost_events_warned; | ||
1004 | }; | 1081 | }; |
1005 | 1082 | ||
1006 | static void perf_evsel_menu__write(struct ui_browser *browser, | 1083 | static void perf_evsel_menu__write(struct ui_browser *browser, |
@@ -1013,22 +1090,38 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1013 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | 1090 | unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE]; |
1014 | const char *ev_name = event_name(evsel); | 1091 | const char *ev_name = event_name(evsel); |
1015 | char bf[256], unit; | 1092 | char bf[256], unit; |
1093 | const char *warn = " "; | ||
1094 | size_t printed; | ||
1016 | 1095 | ||
1017 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | 1096 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
1018 | HE_COLORSET_NORMAL); | 1097 | HE_COLORSET_NORMAL); |
1019 | 1098 | ||
1020 | nr_events = convert_unit(nr_events, &unit); | 1099 | nr_events = convert_unit(nr_events, &unit); |
1021 | snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, | 1100 | printed = snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, |
1022 | unit, unit == ' ' ? "" : " ", ev_name); | 1101 | unit, unit == ' ' ? "" : " ", ev_name); |
1023 | slsmg_write_nstring(bf, browser->width); | 1102 | slsmg_printf("%s", bf); |
1103 | |||
1104 | nr_events = evsel->hists.stats.nr_events[PERF_RECORD_LOST]; | ||
1105 | if (nr_events != 0) { | ||
1106 | menu->lost_events = true; | ||
1107 | if (!current_entry) | ||
1108 | ui_browser__set_color(browser, HE_COLORSET_TOP); | ||
1109 | nr_events = convert_unit(nr_events, &unit); | ||
1110 | snprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", nr_events, | ||
1111 | unit, unit == ' ' ? "" : " "); | ||
1112 | warn = bf; | ||
1113 | } | ||
1114 | |||
1115 | slsmg_write_nstring(warn, browser->width - printed); | ||
1024 | 1116 | ||
1025 | if (current_entry) | 1117 | if (current_entry) |
1026 | menu->selection = evsel; | 1118 | menu->selection = evsel; |
1027 | } | 1119 | } |
1028 | 1120 | ||
1029 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | 1121 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, |
1122 | int nr_events, const char *help, | ||
1123 | void(*timer)(void *arg), void *arg, int delay_secs) | ||
1030 | { | 1124 | { |
1031 | int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
1032 | struct perf_evlist *evlist = menu->b.priv; | 1125 | struct perf_evlist *evlist = menu->b.priv; |
1033 | struct perf_evsel *pos; | 1126 | struct perf_evsel *pos; |
1034 | const char *ev_name, *title = "Available samples"; | 1127 | const char *ev_name, *title = "Available samples"; |
@@ -1038,50 +1131,72 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) | |||
1038 | "ESC: exit, ENTER|->: Browse histograms") < 0) | 1131 | "ESC: exit, ENTER|->: Browse histograms") < 0) |
1039 | return -1; | 1132 | return -1; |
1040 | 1133 | ||
1041 | ui_browser__add_exit_keys(&menu->b, exit_keys); | ||
1042 | |||
1043 | while (1) { | 1134 | while (1) { |
1044 | key = ui_browser__run(&menu->b); | 1135 | key = ui_browser__run(&menu->b, delay_secs); |
1045 | 1136 | ||
1046 | switch (key) { | 1137 | switch (key) { |
1047 | case NEWT_KEY_RIGHT: | 1138 | case K_TIMER: |
1048 | case NEWT_KEY_ENTER: | 1139 | timer(arg); |
1140 | |||
1141 | if (!menu->lost_events_warned && menu->lost_events) { | ||
1142 | ui_browser__warn_lost_events(&menu->b); | ||
1143 | menu->lost_events_warned = true; | ||
1144 | } | ||
1145 | continue; | ||
1146 | case K_RIGHT: | ||
1147 | case K_ENTER: | ||
1049 | if (!menu->selection) | 1148 | if (!menu->selection) |
1050 | continue; | 1149 | continue; |
1051 | pos = menu->selection; | 1150 | pos = menu->selection; |
1052 | browse_hists: | 1151 | browse_hists: |
1152 | perf_evlist__set_selected(evlist, pos); | ||
1153 | /* | ||
1154 | * Give the calling tool a chance to populate the non | ||
1155 | * default evsel resorted hists tree. | ||
1156 | */ | ||
1157 | if (timer) | ||
1158 | timer(arg); | ||
1053 | ev_name = event_name(pos); | 1159 | ev_name = event_name(pos); |
1054 | key = perf_evsel__hists_browse(pos, help, ev_name, true); | 1160 | key = perf_evsel__hists_browse(pos, nr_events, help, |
1161 | ev_name, true, timer, | ||
1162 | arg, delay_secs); | ||
1055 | ui_browser__show_title(&menu->b, title); | 1163 | ui_browser__show_title(&menu->b, title); |
1056 | break; | 1164 | switch (key) { |
1057 | case NEWT_KEY_LEFT: | 1165 | case K_TAB: |
1166 | if (pos->node.next == &evlist->entries) | ||
1167 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
1168 | else | ||
1169 | pos = list_entry(pos->node.next, struct perf_evsel, node); | ||
1170 | goto browse_hists; | ||
1171 | case K_UNTAB: | ||
1172 | if (pos->node.prev == &evlist->entries) | ||
1173 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
1174 | else | ||
1175 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
1176 | goto browse_hists; | ||
1177 | case K_ESC: | ||
1178 | if (!ui_browser__dialog_yesno(&menu->b, | ||
1179 | "Do you really want to exit?")) | ||
1180 | continue; | ||
1181 | /* Fall thru */ | ||
1182 | case 'q': | ||
1183 | case CTRL('c'): | ||
1184 | goto out; | ||
1185 | default: | ||
1186 | continue; | ||
1187 | } | ||
1188 | case K_LEFT: | ||
1058 | continue; | 1189 | continue; |
1059 | case NEWT_KEY_ESCAPE: | 1190 | case K_ESC: |
1060 | if (!ui__dialog_yesno("Do you really want to exit?")) | 1191 | if (!ui_browser__dialog_yesno(&menu->b, |
1192 | "Do you really want to exit?")) | ||
1061 | continue; | 1193 | continue; |
1062 | /* Fall thru */ | 1194 | /* Fall thru */ |
1063 | default: | ||
1064 | goto out; | ||
1065 | } | ||
1066 | |||
1067 | switch (key) { | ||
1068 | case NEWT_KEY_TAB: | ||
1069 | if (pos->node.next == &evlist->entries) | ||
1070 | pos = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
1071 | else | ||
1072 | pos = list_entry(pos->node.next, struct perf_evsel, node); | ||
1073 | goto browse_hists; | ||
1074 | case NEWT_KEY_UNTAB: | ||
1075 | if (pos->node.prev == &evlist->entries) | ||
1076 | pos = list_entry(evlist->entries.prev, struct perf_evsel, node); | ||
1077 | else | ||
1078 | pos = list_entry(pos->node.prev, struct perf_evsel, node); | ||
1079 | goto browse_hists; | ||
1080 | case 'q': | 1195 | case 'q': |
1081 | case CTRL('c'): | 1196 | case CTRL('c'): |
1082 | goto out; | 1197 | goto out; |
1083 | default: | 1198 | default: |
1084 | break; | 1199 | continue; |
1085 | } | 1200 | } |
1086 | } | 1201 | } |
1087 | 1202 | ||
@@ -1091,7 +1206,9 @@ out: | |||
1091 | } | 1206 | } |
1092 | 1207 | ||
1093 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | 1208 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
1094 | const char *help) | 1209 | const char *help, |
1210 | void(*timer)(void *arg), void *arg, | ||
1211 | int delay_secs) | ||
1095 | { | 1212 | { |
1096 | struct perf_evsel *pos; | 1213 | struct perf_evsel *pos; |
1097 | struct perf_evsel_menu menu = { | 1214 | struct perf_evsel_menu menu = { |
@@ -1121,18 +1238,24 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1121 | pos->name = strdup(ev_name); | 1238 | pos->name = strdup(ev_name); |
1122 | } | 1239 | } |
1123 | 1240 | ||
1124 | return perf_evsel_menu__run(&menu, help); | 1241 | return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, |
1242 | arg, delay_secs); | ||
1125 | } | 1243 | } |
1126 | 1244 | ||
1127 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) | 1245 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
1246 | void(*timer)(void *arg), void *arg, | ||
1247 | int delay_secs) | ||
1128 | { | 1248 | { |
1129 | 1249 | ||
1130 | if (evlist->nr_entries == 1) { | 1250 | if (evlist->nr_entries == 1) { |
1131 | struct perf_evsel *first = list_entry(evlist->entries.next, | 1251 | struct perf_evsel *first = list_entry(evlist->entries.next, |
1132 | struct perf_evsel, node); | 1252 | struct perf_evsel, node); |
1133 | const char *ev_name = event_name(first); | 1253 | const char *ev_name = event_name(first); |
1134 | return perf_evsel__hists_browse(first, help, ev_name, false); | 1254 | return perf_evsel__hists_browse(first, evlist->nr_entries, help, |
1255 | ev_name, false, timer, arg, | ||
1256 | delay_secs); | ||
1135 | } | 1257 | } |
1136 | 1258 | ||
1137 | return __perf_evlist__tui_browse_hists(evlist, help); | 1259 | return __perf_evlist__tui_browse_hists(evlist, help, |
1260 | timer, arg, delay_secs); | ||
1138 | } | 1261 | } |
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index 8462bffe20bc..6905bcc8be2d 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c | |||
@@ -1,5 +1,6 @@ | |||
1 | #include "../libslang.h" | 1 | #include "../libslang.h" |
2 | #include <elf.h> | 2 | #include <elf.h> |
3 | #include <newt.h> | ||
3 | #include <inttypes.h> | 4 | #include <inttypes.h> |
4 | #include <sys/ttydefaults.h> | 5 | #include <sys/ttydefaults.h> |
5 | #include <ctype.h> | 6 | #include <ctype.h> |
@@ -108,11 +109,8 @@ static int map_browser__run(struct map_browser *self) | |||
108 | verbose ? "" : "restart with -v to use") < 0) | 109 | verbose ? "" : "restart with -v to use") < 0) |
109 | return -1; | 110 | return -1; |
110 | 111 | ||
111 | if (verbose) | ||
112 | ui_browser__add_exit_key(&self->b, '/'); | ||
113 | |||
114 | while (1) { | 112 | while (1) { |
115 | key = ui_browser__run(&self->b); | 113 | key = ui_browser__run(&self->b, 0); |
116 | 114 | ||
117 | if (verbose && key == '/') | 115 | if (verbose && key == '/') |
118 | map_browser__search(self); | 116 | map_browser__search(self); |
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c deleted file mode 100644 index 5a06538532af..000000000000 --- a/tools/perf/util/ui/browsers/top.c +++ /dev/null | |||
@@ -1,213 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | ||
3 | * | ||
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | ||
5 | * copyright notes. | ||
6 | * | ||
7 | * Released under the GPL v2. (and only v2, not any later version) | ||
8 | */ | ||
9 | #include "../browser.h" | ||
10 | #include "../../annotate.h" | ||
11 | #include "../helpline.h" | ||
12 | #include "../libslang.h" | ||
13 | #include "../util.h" | ||
14 | #include "../../evlist.h" | ||
15 | #include "../../hist.h" | ||
16 | #include "../../sort.h" | ||
17 | #include "../../symbol.h" | ||
18 | #include "../../top.h" | ||
19 | |||
20 | struct perf_top_browser { | ||
21 | struct ui_browser b; | ||
22 | struct rb_root root; | ||
23 | struct sym_entry *selection; | ||
24 | float sum_ksamples; | ||
25 | int dso_width; | ||
26 | int dso_short_width; | ||
27 | int sym_width; | ||
28 | }; | ||
29 | |||
30 | static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row) | ||
31 | { | ||
32 | struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b); | ||
33 | struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node); | ||
34 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
35 | struct symbol *symbol = sym_entry__symbol(syme); | ||
36 | struct perf_top *top = browser->priv; | ||
37 | int width = browser->width; | ||
38 | double pcnt; | ||
39 | |||
40 | pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) / | ||
41 | top_browser->sum_ksamples)); | ||
42 | ui_browser__set_percent_color(browser, pcnt, current_entry); | ||
43 | |||
44 | if (top->evlist->nr_entries == 1 || !top->display_weighted) { | ||
45 | slsmg_printf("%20.2f ", syme->weight); | ||
46 | width -= 24; | ||
47 | } else { | ||
48 | slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count); | ||
49 | width -= 23; | ||
50 | } | ||
51 | |||
52 | slsmg_printf("%4.1f%%", pcnt); | ||
53 | width -= 7; | ||
54 | |||
55 | if (verbose) { | ||
56 | slsmg_printf(" %016" PRIx64, symbol->start); | ||
57 | width -= 17; | ||
58 | } | ||
59 | |||
60 | slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width, | ||
61 | symbol->name); | ||
62 | width -= top_browser->sym_width; | ||
63 | slsmg_write_nstring(width >= syme->map->dso->long_name_len ? | ||
64 | syme->map->dso->long_name : | ||
65 | syme->map->dso->short_name, width); | ||
66 | |||
67 | if (current_entry) | ||
68 | top_browser->selection = syme; | ||
69 | } | ||
70 | |||
71 | static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser) | ||
72 | { | ||
73 | struct perf_top *top = browser->b.priv; | ||
74 | u64 top_idx = browser->b.top_idx; | ||
75 | |||
76 | browser->root = RB_ROOT; | ||
77 | browser->b.top = NULL; | ||
78 | browser->sum_ksamples = perf_top__decay_samples(top, &browser->root); | ||
79 | /* | ||
80 | * No active symbols | ||
81 | */ | ||
82 | if (top->rb_entries == 0) | ||
83 | return; | ||
84 | |||
85 | perf_top__find_widths(top, &browser->root, &browser->dso_width, | ||
86 | &browser->dso_short_width, | ||
87 | &browser->sym_width); | ||
88 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) { | ||
89 | browser->dso_width = browser->dso_short_width; | ||
90 | if (browser->sym_width + browser->dso_width > browser->b.width - 29) | ||
91 | browser->sym_width = browser->b.width - browser->dso_width - 29; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Adjust the ui_browser indexes since the entries in the browser->root | ||
96 | * rb_tree may have changed, then seek it from start, so that we get a | ||
97 | * possible new top of the screen. | ||
98 | */ | ||
99 | browser->b.nr_entries = top->rb_entries; | ||
100 | |||
101 | if (top_idx >= browser->b.nr_entries) { | ||
102 | if (browser->b.height >= browser->b.nr_entries) | ||
103 | top_idx = browser->b.nr_entries - browser->b.height; | ||
104 | else | ||
105 | top_idx = 0; | ||
106 | } | ||
107 | |||
108 | if (browser->b.index >= top_idx + browser->b.height) | ||
109 | browser->b.index = top_idx + browser->b.index - browser->b.top_idx; | ||
110 | |||
111 | if (browser->b.index >= browser->b.nr_entries) | ||
112 | browser->b.index = browser->b.nr_entries - 1; | ||
113 | |||
114 | browser->b.top_idx = top_idx; | ||
115 | browser->b.seek(&browser->b, top_idx, SEEK_SET); | ||
116 | } | ||
117 | |||
118 | static void perf_top_browser__annotate(struct perf_top_browser *browser) | ||
119 | { | ||
120 | struct sym_entry *syme = browser->selection; | ||
121 | struct symbol *sym = sym_entry__symbol(syme); | ||
122 | struct annotation *notes = symbol__annotation(sym); | ||
123 | struct perf_top *top = browser->b.priv; | ||
124 | |||
125 | if (notes->src != NULL) | ||
126 | goto do_annotation; | ||
127 | |||
128 | pthread_mutex_lock(¬es->lock); | ||
129 | |||
130 | top->sym_filter_entry = NULL; | ||
131 | |||
132 | if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) { | ||
133 | pr_err("Not enough memory for annotating '%s' symbol!\n", | ||
134 | sym->name); | ||
135 | pthread_mutex_unlock(¬es->lock); | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | top->sym_filter_entry = syme; | ||
140 | |||
141 | pthread_mutex_unlock(¬es->lock); | ||
142 | do_annotation: | ||
143 | symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000); | ||
144 | } | ||
145 | |||
146 | static int perf_top_browser__run(struct perf_top_browser *browser) | ||
147 | { | ||
148 | int key; | ||
149 | char title[160]; | ||
150 | struct perf_top *top = browser->b.priv; | ||
151 | int delay_msecs = top->delay_secs * 1000; | ||
152 | int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, }; | ||
153 | |||
154 | perf_top_browser__update_rb_tree(browser); | ||
155 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
156 | perf_top__reset_sample_counters(top); | ||
157 | |||
158 | if (ui_browser__show(&browser->b, title, | ||
159 | "ESC: exit, ENTER|->|a: Live Annotate") < 0) | ||
160 | return -1; | ||
161 | |||
162 | newtFormSetTimer(browser->b.form, delay_msecs); | ||
163 | ui_browser__add_exit_keys(&browser->b, exit_keys); | ||
164 | |||
165 | while (1) { | ||
166 | key = ui_browser__run(&browser->b); | ||
167 | |||
168 | switch (key) { | ||
169 | case -1: | ||
170 | /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */ | ||
171 | perf_top_browser__update_rb_tree(browser); | ||
172 | perf_top__header_snprintf(top, title, sizeof(title)); | ||
173 | perf_top__reset_sample_counters(top); | ||
174 | ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT); | ||
175 | SLsmg_gotorc(0, 0); | ||
176 | slsmg_write_nstring(title, browser->b.width); | ||
177 | break; | ||
178 | case 'a': | ||
179 | case NEWT_KEY_RIGHT: | ||
180 | case NEWT_KEY_ENTER: | ||
181 | if (browser->selection) | ||
182 | perf_top_browser__annotate(browser); | ||
183 | break; | ||
184 | case NEWT_KEY_LEFT: | ||
185 | continue; | ||
186 | case NEWT_KEY_ESCAPE: | ||
187 | if (!ui__dialog_yesno("Do you really want to exit?")) | ||
188 | continue; | ||
189 | /* Fall thru */ | ||
190 | default: | ||
191 | goto out; | ||
192 | } | ||
193 | } | ||
194 | out: | ||
195 | ui_browser__hide(&browser->b); | ||
196 | return key; | ||
197 | } | ||
198 | |||
199 | int perf_top__tui_browser(struct perf_top *top) | ||
200 | { | ||
201 | struct perf_top_browser browser = { | ||
202 | .b = { | ||
203 | .entries = &browser.root, | ||
204 | .refresh = ui_browser__rb_tree_refresh, | ||
205 | .seek = ui_browser__rb_tree_seek, | ||
206 | .write = perf_top_browser__write, | ||
207 | .priv = top, | ||
208 | }, | ||
209 | }; | ||
210 | |||
211 | ui_helpline__push("Press <- or ESC to exit"); | ||
212 | return perf_top_browser__run(&browser); | ||
213 | } | ||
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index f36d2ff509ed..6ef3c5691762 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c | |||
@@ -1,20 +1,28 @@ | |||
1 | #define _GNU_SOURCE | 1 | #define _GNU_SOURCE |
2 | #include <stdio.h> | 2 | #include <stdio.h> |
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <newt.h> | 4 | #include <string.h> |
5 | 5 | ||
6 | #include "../debug.h" | 6 | #include "../debug.h" |
7 | #include "helpline.h" | 7 | #include "helpline.h" |
8 | #include "ui.h" | 8 | #include "ui.h" |
9 | #include "libslang.h" | ||
9 | 10 | ||
10 | void ui_helpline__pop(void) | 11 | void ui_helpline__pop(void) |
11 | { | 12 | { |
12 | newtPopHelpLine(); | ||
13 | } | 13 | } |
14 | 14 | ||
15 | char ui_helpline__current[512]; | ||
16 | |||
15 | void ui_helpline__push(const char *msg) | 17 | void ui_helpline__push(const char *msg) |
16 | { | 18 | { |
17 | newtPushHelpLine(msg); | 19 | const size_t sz = sizeof(ui_helpline__current); |
20 | |||
21 | SLsmg_gotorc(SLtt_Screen_Rows - 1, 0); | ||
22 | SLsmg_set_color(0); | ||
23 | SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols); | ||
24 | SLsmg_refresh(); | ||
25 | strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0'; | ||
18 | } | 26 | } |
19 | 27 | ||
20 | void ui_helpline__vpush(const char *fmt, va_list ap) | 28 | void ui_helpline__vpush(const char *fmt, va_list ap) |
@@ -63,7 +71,7 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
63 | 71 | ||
64 | if (ui_helpline__last_msg[backlog - 1] == '\n') { | 72 | if (ui_helpline__last_msg[backlog - 1] == '\n') { |
65 | ui_helpline__puts(ui_helpline__last_msg); | 73 | ui_helpline__puts(ui_helpline__last_msg); |
66 | newtRefresh(); | 74 | SLsmg_refresh(); |
67 | backlog = 0; | 75 | backlog = 0; |
68 | } | 76 | } |
69 | pthread_mutex_unlock(&ui__lock); | 77 | pthread_mutex_unlock(&ui__lock); |
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h index ab6028d0c401..7bab6b34e35e 100644 --- a/tools/perf/util/ui/helpline.h +++ b/tools/perf/util/ui/helpline.h | |||
@@ -1,6 +1,9 @@ | |||
1 | #ifndef _PERF_UI_HELPLINE_H_ | 1 | #ifndef _PERF_UI_HELPLINE_H_ |
2 | #define _PERF_UI_HELPLINE_H_ 1 | 2 | #define _PERF_UI_HELPLINE_H_ 1 |
3 | 3 | ||
4 | #include <stdio.h> | ||
5 | #include <stdarg.h> | ||
6 | |||
4 | void ui_helpline__init(void); | 7 | void ui_helpline__init(void); |
5 | void ui_helpline__pop(void); | 8 | void ui_helpline__pop(void); |
6 | void ui_helpline__push(const char *msg); | 9 | void ui_helpline__push(const char *msg); |
@@ -8,4 +11,6 @@ void ui_helpline__vpush(const char *fmt, va_list ap); | |||
8 | void ui_helpline__fpush(const char *fmt, ...); | 11 | void ui_helpline__fpush(const char *fmt, ...); |
9 | void ui_helpline__puts(const char *msg); | 12 | void ui_helpline__puts(const char *msg); |
10 | 13 | ||
14 | extern char ui_helpline__current[]; | ||
15 | |||
11 | #endif /* _PERF_UI_HELPLINE_H_ */ | 16 | #endif /* _PERF_UI_HELPLINE_H_ */ |
diff --git a/tools/perf/util/ui/keysyms.h b/tools/perf/util/ui/keysyms.h new file mode 100644 index 000000000000..3458b1985761 --- /dev/null +++ b/tools/perf/util/ui/keysyms.h | |||
@@ -0,0 +1,25 @@ | |||
1 | #ifndef _PERF_KEYSYMS_H_ | ||
2 | #define _PERF_KEYSYMS_H_ 1 | ||
3 | |||
4 | #include "libslang.h" | ||
5 | |||
6 | #define K_DOWN SL_KEY_DOWN | ||
7 | #define K_END SL_KEY_END | ||
8 | #define K_ENTER '\r' | ||
9 | #define K_ESC 033 | ||
10 | #define K_F1 SL_KEY_F(1) | ||
11 | #define K_HOME SL_KEY_HOME | ||
12 | #define K_LEFT SL_KEY_LEFT | ||
13 | #define K_PGDN SL_KEY_NPAGE | ||
14 | #define K_PGUP SL_KEY_PPAGE | ||
15 | #define K_RIGHT SL_KEY_RIGHT | ||
16 | #define K_TAB '\t' | ||
17 | #define K_UNTAB SL_KEY_UNTAB | ||
18 | #define K_UP SL_KEY_UP | ||
19 | |||
20 | /* Not really keys */ | ||
21 | #define K_TIMER -1 | ||
22 | #define K_ERROR -2 | ||
23 | #define K_RESIZE -3 | ||
24 | |||
25 | #endif /* _PERF_KEYSYMS_H_ */ | ||
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h index 2b63e1c9b181..4d54b6450f5b 100644 --- a/tools/perf/util/ui/libslang.h +++ b/tools/perf/util/ui/libslang.h | |||
@@ -24,4 +24,6 @@ | |||
24 | #define sltt_set_color SLtt_set_color | 24 | #define sltt_set_color SLtt_set_color |
25 | #endif | 25 | #endif |
26 | 26 | ||
27 | #define SL_KEY_UNTAB 0x1000 | ||
28 | |||
27 | #endif /* _PERF_UI_SLANG_H_ */ | 29 | #endif /* _PERF_UI_SLANG_H_ */ |
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c index d7fc399d36b3..295e366b6311 100644 --- a/tools/perf/util/ui/progress.c +++ b/tools/perf/util/ui/progress.c | |||
@@ -1,60 +1,29 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <newt.h> | ||
3 | #include "../cache.h" | 1 | #include "../cache.h" |
4 | #include "progress.h" | 2 | #include "progress.h" |
3 | #include "libslang.h" | ||
4 | #include "ui.h" | ||
5 | #include "browser.h" | ||
5 | 6 | ||
6 | struct ui_progress { | 7 | void ui_progress__update(u64 curr, u64 total, const char *title) |
7 | newtComponent form, scale; | ||
8 | }; | ||
9 | |||
10 | struct ui_progress *ui_progress__new(const char *title, u64 total) | ||
11 | { | ||
12 | struct ui_progress *self = malloc(sizeof(*self)); | ||
13 | |||
14 | if (self != NULL) { | ||
15 | int cols; | ||
16 | |||
17 | if (use_browser <= 0) | ||
18 | return self; | ||
19 | newtGetScreenSize(&cols, NULL); | ||
20 | cols -= 4; | ||
21 | newtCenteredWindow(cols, 1, title); | ||
22 | self->form = newtForm(NULL, NULL, 0); | ||
23 | if (self->form == NULL) | ||
24 | goto out_free_self; | ||
25 | self->scale = newtScale(0, 0, cols, total); | ||
26 | if (self->scale == NULL) | ||
27 | goto out_free_form; | ||
28 | newtFormAddComponent(self->form, self->scale); | ||
29 | newtRefresh(); | ||
30 | } | ||
31 | |||
32 | return self; | ||
33 | |||
34 | out_free_form: | ||
35 | newtFormDestroy(self->form); | ||
36 | out_free_self: | ||
37 | free(self); | ||
38 | return NULL; | ||
39 | } | ||
40 | |||
41 | void ui_progress__update(struct ui_progress *self, u64 curr) | ||
42 | { | 8 | { |
9 | int bar, y; | ||
43 | /* | 10 | /* |
44 | * FIXME: We should have a per UI backend way of showing progress, | 11 | * FIXME: We should have a per UI backend way of showing progress, |
45 | * stdio will just show a percentage as NN%, etc. | 12 | * stdio will just show a percentage as NN%, etc. |
46 | */ | 13 | */ |
47 | if (use_browser <= 0) | 14 | if (use_browser <= 0) |
48 | return; | 15 | return; |
49 | newtScaleSet(self->scale, curr); | ||
50 | newtRefresh(); | ||
51 | } | ||
52 | 16 | ||
53 | void ui_progress__delete(struct ui_progress *self) | 17 | ui__refresh_dimensions(true); |
54 | { | 18 | pthread_mutex_lock(&ui__lock); |
55 | if (use_browser > 0) { | 19 | y = SLtt_Screen_Rows / 2 - 2; |
56 | newtFormDestroy(self->form); | 20 | SLsmg_set_color(0); |
57 | newtPopWindow(); | 21 | SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); |
58 | } | 22 | SLsmg_gotorc(y++, 1); |
59 | free(self); | 23 | SLsmg_write_string((char *)title); |
24 | SLsmg_set_color(HE_COLORSET_SELECTED); | ||
25 | bar = ((SLtt_Screen_Cols - 2) * curr) / total; | ||
26 | SLsmg_fill_region(y, 1, 1, bar, ' '); | ||
27 | SLsmg_refresh(); | ||
28 | pthread_mutex_unlock(&ui__lock); | ||
60 | } | 29 | } |
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h index a3820a0beb5b..d9c205b59aa1 100644 --- a/tools/perf/util/ui/progress.h +++ b/tools/perf/util/ui/progress.h | |||
@@ -1,11 +1,8 @@ | |||
1 | #ifndef _PERF_UI_PROGRESS_H_ | 1 | #ifndef _PERF_UI_PROGRESS_H_ |
2 | #define _PERF_UI_PROGRESS_H_ 1 | 2 | #define _PERF_UI_PROGRESS_H_ 1 |
3 | 3 | ||
4 | struct ui_progress; | 4 | #include <../types.h> |
5 | 5 | ||
6 | struct ui_progress *ui_progress__new(const char *title, u64 total); | 6 | void ui_progress__update(u64 curr, u64 total, const char *title); |
7 | void ui_progress__delete(struct ui_progress *self); | ||
8 | |||
9 | void ui_progress__update(struct ui_progress *self, u64 curr); | ||
10 | 7 | ||
11 | #endif | 8 | #endif |
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c index ee46d671db59..85a69faa09aa 100644 --- a/tools/perf/util/ui/setup.c +++ b/tools/perf/util/ui/setup.c | |||
@@ -7,9 +7,85 @@ | |||
7 | #include "browser.h" | 7 | #include "browser.h" |
8 | #include "helpline.h" | 8 | #include "helpline.h" |
9 | #include "ui.h" | 9 | #include "ui.h" |
10 | #include "util.h" | ||
11 | #include "libslang.h" | ||
12 | #include "keysyms.h" | ||
10 | 13 | ||
11 | pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; | 14 | pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; |
12 | 15 | ||
16 | static volatile int ui__need_resize; | ||
17 | |||
18 | void ui__refresh_dimensions(bool force) | ||
19 | { | ||
20 | if (force || ui__need_resize) { | ||
21 | ui__need_resize = 0; | ||
22 | pthread_mutex_lock(&ui__lock); | ||
23 | SLtt_get_screen_size(); | ||
24 | SLsmg_reinit_smg(); | ||
25 | pthread_mutex_unlock(&ui__lock); | ||
26 | } | ||
27 | } | ||
28 | |||
29 | static void ui__sigwinch(int sig __used) | ||
30 | { | ||
31 | ui__need_resize = 1; | ||
32 | } | ||
33 | |||
34 | static void ui__setup_sigwinch(void) | ||
35 | { | ||
36 | static bool done; | ||
37 | |||
38 | if (done) | ||
39 | return; | ||
40 | |||
41 | done = true; | ||
42 | pthread__unblock_sigwinch(); | ||
43 | signal(SIGWINCH, ui__sigwinch); | ||
44 | } | ||
45 | |||
46 | int ui__getch(int delay_secs) | ||
47 | { | ||
48 | struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL; | ||
49 | fd_set read_set; | ||
50 | int err, key; | ||
51 | |||
52 | ui__setup_sigwinch(); | ||
53 | |||
54 | FD_ZERO(&read_set); | ||
55 | FD_SET(0, &read_set); | ||
56 | |||
57 | if (delay_secs) { | ||
58 | timeout.tv_sec = delay_secs; | ||
59 | timeout.tv_usec = 0; | ||
60 | } | ||
61 | |||
62 | err = select(1, &read_set, NULL, NULL, ptimeout); | ||
63 | |||
64 | if (err == 0) | ||
65 | return K_TIMER; | ||
66 | |||
67 | if (err == -1) { | ||
68 | if (errno == EINTR) | ||
69 | return K_RESIZE; | ||
70 | return K_ERROR; | ||
71 | } | ||
72 | |||
73 | key = SLang_getkey(); | ||
74 | if (key != K_ESC) | ||
75 | return key; | ||
76 | |||
77 | FD_ZERO(&read_set); | ||
78 | FD_SET(0, &read_set); | ||
79 | timeout.tv_sec = 0; | ||
80 | timeout.tv_usec = 20; | ||
81 | err = select(1, &read_set, NULL, NULL, &timeout); | ||
82 | if (err == 0) | ||
83 | return K_ESC; | ||
84 | |||
85 | SLang_ungetkey(key); | ||
86 | return SLkp_getkey(); | ||
87 | } | ||
88 | |||
13 | static void newt_suspend(void *d __used) | 89 | static void newt_suspend(void *d __used) |
14 | { | 90 | { |
15 | newtSuspend(); | 91 | newtSuspend(); |
@@ -17,6 +93,33 @@ static void newt_suspend(void *d __used) | |||
17 | newtResume(); | 93 | newtResume(); |
18 | } | 94 | } |
19 | 95 | ||
96 | static int ui__init(void) | ||
97 | { | ||
98 | int err = SLkp_init(); | ||
99 | |||
100 | if (err < 0) | ||
101 | goto out; | ||
102 | |||
103 | SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB); | ||
104 | out: | ||
105 | return err; | ||
106 | } | ||
107 | |||
108 | static void ui__exit(void) | ||
109 | { | ||
110 | SLtt_set_cursor_visibility(1); | ||
111 | SLsmg_refresh(); | ||
112 | SLsmg_reset_smg(); | ||
113 | SLang_reset_tty(); | ||
114 | } | ||
115 | |||
116 | static void ui__signal(int sig) | ||
117 | { | ||
118 | ui__exit(); | ||
119 | psignal(sig, "perf"); | ||
120 | exit(0); | ||
121 | } | ||
122 | |||
20 | void setup_browser(bool fallback_to_pager) | 123 | void setup_browser(bool fallback_to_pager) |
21 | { | 124 | { |
22 | if (!isatty(1) || !use_browser || dump_trace) { | 125 | if (!isatty(1) || !use_browser || dump_trace) { |
@@ -28,19 +131,25 @@ void setup_browser(bool fallback_to_pager) | |||
28 | 131 | ||
29 | use_browser = 1; | 132 | use_browser = 1; |
30 | newtInit(); | 133 | newtInit(); |
31 | newtCls(); | 134 | ui__init(); |
32 | newtSetSuspendCallback(newt_suspend, NULL); | 135 | newtSetSuspendCallback(newt_suspend, NULL); |
33 | ui_helpline__init(); | 136 | ui_helpline__init(); |
34 | ui_browser__init(); | 137 | ui_browser__init(); |
138 | |||
139 | signal(SIGSEGV, ui__signal); | ||
140 | signal(SIGFPE, ui__signal); | ||
141 | signal(SIGINT, ui__signal); | ||
142 | signal(SIGQUIT, ui__signal); | ||
143 | signal(SIGTERM, ui__signal); | ||
35 | } | 144 | } |
36 | 145 | ||
37 | void exit_browser(bool wait_for_ok) | 146 | void exit_browser(bool wait_for_ok) |
38 | { | 147 | { |
39 | if (use_browser > 0) { | 148 | if (use_browser > 0) { |
40 | if (wait_for_ok) { | 149 | if (wait_for_ok) |
41 | char title[] = "Fatal Error", ok[] = "Ok"; | 150 | ui__question_window("Fatal Error", |
42 | newtWinMessage(title, ok, ui_helpline__last_msg); | 151 | ui_helpline__last_msg, |
43 | } | 152 | "Press any key...", 0); |
44 | newtFinished(); | 153 | ui__exit(); |
45 | } | 154 | } |
46 | } | 155 | } |
diff --git a/tools/perf/util/ui/ui.h b/tools/perf/util/ui/ui.h index d264e059c829..7b67045479f6 100644 --- a/tools/perf/util/ui/ui.h +++ b/tools/perf/util/ui/ui.h | |||
@@ -2,7 +2,10 @@ | |||
2 | #define _PERF_UI_H_ 1 | 2 | #define _PERF_UI_H_ 1 |
3 | 3 | ||
4 | #include <pthread.h> | 4 | #include <pthread.h> |
5 | #include <stdbool.h> | ||
5 | 6 | ||
6 | extern pthread_mutex_t ui__lock; | 7 | extern pthread_mutex_t ui__lock; |
7 | 8 | ||
9 | void ui__refresh_dimensions(bool force); | ||
10 | |||
8 | #endif /* _PERF_UI_H_ */ | 11 | #endif /* _PERF_UI_H_ */ |
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index fdf1fc8f08bc..45daa7c41dad 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include <newt.h> | 1 | #include "../util.h" |
2 | #include <signal.h> | 2 | #include <signal.h> |
3 | #include <stdio.h> | ||
4 | #include <stdbool.h> | 3 | #include <stdbool.h> |
5 | #include <string.h> | 4 | #include <string.h> |
6 | #include <sys/ttydefaults.h> | 5 | #include <sys/ttydefaults.h> |
@@ -8,72 +7,75 @@ | |||
8 | #include "../cache.h" | 7 | #include "../cache.h" |
9 | #include "../debug.h" | 8 | #include "../debug.h" |
10 | #include "browser.h" | 9 | #include "browser.h" |
10 | #include "keysyms.h" | ||
11 | #include "helpline.h" | 11 | #include "helpline.h" |
12 | #include "ui.h" | 12 | #include "ui.h" |
13 | #include "util.h" | 13 | #include "util.h" |
14 | #include "libslang.h" | ||
14 | 15 | ||
15 | static void newt_form__set_exit_keys(newtComponent self) | 16 | static void ui_browser__argv_write(struct ui_browser *browser, |
17 | void *entry, int row) | ||
16 | { | 18 | { |
17 | newtFormAddHotKey(self, NEWT_KEY_LEFT); | 19 | char **arg = entry; |
18 | newtFormAddHotKey(self, NEWT_KEY_ESCAPE); | 20 | bool current_entry = ui_browser__is_current_entry(browser, row); |
19 | newtFormAddHotKey(self, 'Q'); | ||
20 | newtFormAddHotKey(self, 'q'); | ||
21 | newtFormAddHotKey(self, CTRL('c')); | ||
22 | } | ||
23 | 21 | ||
24 | static newtComponent newt_form__new(void) | 22 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
25 | { | 23 | HE_COLORSET_NORMAL); |
26 | newtComponent self = newtForm(NULL, NULL, 0); | 24 | slsmg_write_nstring(*arg, browser->width); |
27 | if (self) | ||
28 | newt_form__set_exit_keys(self); | ||
29 | return self; | ||
30 | } | 25 | } |
31 | 26 | ||
32 | int ui__popup_menu(int argc, char * const argv[]) | 27 | static int popup_menu__run(struct ui_browser *menu) |
33 | { | 28 | { |
34 | struct newtExitStruct es; | 29 | int key; |
35 | int i, rc = -1, max_len = 5; | ||
36 | newtComponent listbox, form = newt_form__new(); | ||
37 | 30 | ||
38 | if (form == NULL) | 31 | if (ui_browser__show(menu, " ", "ESC: exit, ENTER|->: Select option") < 0) |
39 | return -1; | 32 | return -1; |
40 | 33 | ||
41 | listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); | 34 | while (1) { |
42 | if (listbox == NULL) | 35 | key = ui_browser__run(menu, 0); |
43 | goto out_destroy_form; | ||
44 | 36 | ||
45 | newtFormAddComponent(form, listbox); | 37 | switch (key) { |
38 | case K_RIGHT: | ||
39 | case K_ENTER: | ||
40 | key = menu->index; | ||
41 | break; | ||
42 | case K_LEFT: | ||
43 | case K_ESC: | ||
44 | case 'q': | ||
45 | case CTRL('c'): | ||
46 | key = -1; | ||
47 | break; | ||
48 | default: | ||
49 | continue; | ||
50 | } | ||
46 | 51 | ||
47 | for (i = 0; i < argc; ++i) { | 52 | break; |
48 | int len = strlen(argv[i]); | ||
49 | if (len > max_len) | ||
50 | max_len = len; | ||
51 | if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) | ||
52 | goto out_destroy_form; | ||
53 | } | 53 | } |
54 | 54 | ||
55 | newtCenteredWindow(max_len, argc, NULL); | 55 | ui_browser__hide(menu); |
56 | newtFormRun(form, &es); | 56 | return key; |
57 | rc = newtListboxGetCurrent(listbox) - NULL; | ||
58 | if (es.reason == NEWT_EXIT_HOTKEY) | ||
59 | rc = -1; | ||
60 | newtPopWindow(); | ||
61 | out_destroy_form: | ||
62 | newtFormDestroy(form); | ||
63 | return rc; | ||
64 | } | 57 | } |
65 | 58 | ||
66 | int ui__help_window(const char *text) | 59 | int ui__popup_menu(int argc, char * const argv[]) |
67 | { | 60 | { |
68 | struct newtExitStruct es; | 61 | struct ui_browser menu = { |
69 | newtComponent tb, form = newt_form__new(); | 62 | .entries = (void *)argv, |
70 | int rc = -1; | 63 | .refresh = ui_browser__argv_refresh, |
64 | .seek = ui_browser__argv_seek, | ||
65 | .write = ui_browser__argv_write, | ||
66 | .nr_entries = argc, | ||
67 | }; | ||
68 | |||
69 | return popup_menu__run(&menu); | ||
70 | } | ||
71 | |||
72 | int ui__question_window(const char *title, const char *text, | ||
73 | const char *exit_msg, int delay_secs) | ||
74 | { | ||
75 | int x, y; | ||
71 | int max_len = 0, nr_lines = 0; | 76 | int max_len = 0, nr_lines = 0; |
72 | const char *t; | 77 | const char *t; |
73 | 78 | ||
74 | if (form == NULL) | ||
75 | return -1; | ||
76 | |||
77 | t = text; | 79 | t = text; |
78 | while (1) { | 80 | while (1) { |
79 | const char *sep = strchr(t, '\n'); | 81 | const char *sep = strchr(t, '\n'); |
@@ -90,41 +92,77 @@ int ui__help_window(const char *text) | |||
90 | t = sep + 1; | 92 | t = sep + 1; |
91 | } | 93 | } |
92 | 94 | ||
93 | tb = newtTextbox(0, 0, max_len, nr_lines, 0); | 95 | max_len += 2; |
94 | if (tb == NULL) | 96 | nr_lines += 4; |
95 | goto out_destroy_form; | 97 | y = SLtt_Screen_Rows / 2 - nr_lines / 2, |
96 | 98 | x = SLtt_Screen_Cols / 2 - max_len / 2; | |
97 | newtTextboxSetText(tb, text); | 99 | |
98 | newtFormAddComponent(form, tb); | 100 | SLsmg_set_color(0); |
99 | newtCenteredWindow(max_len, nr_lines, NULL); | 101 | SLsmg_draw_box(y, x++, nr_lines, max_len); |
100 | newtFormRun(form, &es); | 102 | if (title) { |
101 | newtPopWindow(); | 103 | SLsmg_gotorc(y, x + 1); |
102 | rc = 0; | 104 | SLsmg_write_string((char *)title); |
103 | out_destroy_form: | 105 | } |
104 | newtFormDestroy(form); | 106 | SLsmg_gotorc(++y, x); |
105 | return rc; | 107 | nr_lines -= 2; |
108 | max_len -= 2; | ||
109 | SLsmg_write_wrapped_string((unsigned char *)text, y, x, | ||
110 | nr_lines, max_len, 1); | ||
111 | SLsmg_gotorc(y + nr_lines - 2, x); | ||
112 | SLsmg_write_nstring((char *)" ", max_len); | ||
113 | SLsmg_gotorc(y + nr_lines - 1, x); | ||
114 | SLsmg_write_nstring((char *)exit_msg, max_len); | ||
115 | SLsmg_refresh(); | ||
116 | return ui__getch(delay_secs); | ||
106 | } | 117 | } |
107 | 118 | ||
108 | static const char yes[] = "Yes", no[] = "No", | 119 | int ui__help_window(const char *text) |
109 | warning_str[] = "Warning!", ok[] = "Ok"; | 120 | { |
121 | return ui__question_window("Help", text, "Press any key...", 0); | ||
122 | } | ||
110 | 123 | ||
111 | bool ui__dialog_yesno(const char *msg) | 124 | int ui__dialog_yesno(const char *msg) |
112 | { | 125 | { |
113 | /* newtWinChoice should really be accepting const char pointers... */ | 126 | return ui__question_window(NULL, msg, "Enter: Yes, ESC: No", 0); |
114 | return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1; | ||
115 | } | 127 | } |
116 | 128 | ||
117 | void ui__warning(const char *format, ...) | 129 | int __ui__warning(const char *title, const char *format, va_list args) |
118 | { | 130 | { |
119 | va_list args; | 131 | char *s; |
132 | |||
133 | if (use_browser > 0 && vasprintf(&s, format, args) > 0) { | ||
134 | int key; | ||
120 | 135 | ||
121 | va_start(args, format); | ||
122 | if (use_browser > 0) { | ||
123 | pthread_mutex_lock(&ui__lock); | 136 | pthread_mutex_lock(&ui__lock); |
124 | newtWinMessagev((char *)warning_str, (char *)ok, | 137 | key = ui__question_window(title, s, "Press any key...", 0); |
125 | (char *)format, args); | ||
126 | pthread_mutex_unlock(&ui__lock); | 138 | pthread_mutex_unlock(&ui__lock); |
127 | } else | 139 | free(s); |
128 | vfprintf(stderr, format, args); | 140 | return key; |
141 | } | ||
142 | |||
143 | fprintf(stderr, "%s:\n", title); | ||
144 | vfprintf(stderr, format, args); | ||
145 | return K_ESC; | ||
146 | } | ||
147 | |||
148 | int ui__warning(const char *format, ...) | ||
149 | { | ||
150 | int key; | ||
151 | va_list args; | ||
152 | |||
153 | va_start(args, format); | ||
154 | key = __ui__warning("Warning", format, args); | ||
155 | va_end(args); | ||
156 | return key; | ||
157 | } | ||
158 | |||
159 | int ui__error(const char *format, ...) | ||
160 | { | ||
161 | int key; | ||
162 | va_list args; | ||
163 | |||
164 | va_start(args, format); | ||
165 | key = __ui__warning("Error", format, args); | ||
129 | va_end(args); | 166 | va_end(args); |
167 | return key; | ||
130 | } | 168 | } |
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h index afcbc1d99531..2d1738bd71c8 100644 --- a/tools/perf/util/ui/util.h +++ b/tools/perf/util/ui/util.h | |||
@@ -1,10 +1,14 @@ | |||
1 | #ifndef _PERF_UI_UTIL_H_ | 1 | #ifndef _PERF_UI_UTIL_H_ |
2 | #define _PERF_UI_UTIL_H_ 1 | 2 | #define _PERF_UI_UTIL_H_ 1 |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdarg.h> |
5 | 5 | ||
6 | int ui__getch(int delay_secs); | ||
6 | int ui__popup_menu(int argc, char * const argv[]); | 7 | int ui__popup_menu(int argc, char * const argv[]); |
7 | int ui__help_window(const char *text); | 8 | int ui__help_window(const char *text); |
8 | bool ui__dialog_yesno(const char *msg); | 9 | int ui__dialog_yesno(const char *msg); |
10 | int ui__question_window(const char *title, const char *text, | ||
11 | const char *exit_msg, int delay_secs); | ||
12 | int __ui__warning(const char *title, const char *format, va_list args); | ||
9 | 13 | ||
10 | #endif /* _PERF_UI_UTIL_H_ */ | 14 | #endif /* _PERF_UI_UTIL_H_ */ |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index fc784284ac8b..0128906bac88 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -238,6 +238,7 @@ char **argv_split(const char *str, int *argcp); | |||
238 | void argv_free(char **argv); | 238 | void argv_free(char **argv); |
239 | bool strglobmatch(const char *str, const char *pat); | 239 | bool strglobmatch(const char *str, const char *pat); |
240 | bool strlazymatch(const char *str, const char *pat); | 240 | bool strlazymatch(const char *str, const char *pat); |
241 | int strtailcmp(const char *s1, const char *s2); | ||
241 | unsigned long convert_unit(unsigned long value, char *unit); | 242 | unsigned long convert_unit(unsigned long value, char *unit); |
242 | int readn(int fd, void *buf, size_t size); | 243 | int readn(int fd, void *buf, size_t size); |
243 | 244 | ||