Commit e742f3dc0886a92403d578e8ac771e5e33d06d08
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
Merge tag 'perf-urgent-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/g…
…it/acme/linux into perf/urgent Pull perf/urgent fixes from Arnaldo Carvalho de Melo: " User visible fixes: - Fix probing at function return (Namhyumg Kim) Developer visible fixes: - Symbol processing changes necessary for fixing support for kretprobes in 'perf probe' (Namhyung Kim, Arnaldo Carvalho de Melo) - Annotation memory leaks and instruction parsing fixes (Rabin Vincent) - Fix perl build on ARM64 (Wang Nam) " Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Showing 7 changed files Inline Diff
tools/perf/scripts/perl/Perf-Trace-Util/Context.c
1 | /* | 1 | /* |
2 | * This file was generated automatically by ExtUtils::ParseXS version 2.18_02 from the | 2 | * This file was generated automatically by ExtUtils::ParseXS version 2.18_02 from the |
3 | * contents of Context.xs. Do not edit this file, edit Context.xs instead. | 3 | * contents of Context.xs. Do not edit this file, edit Context.xs instead. |
4 | * | 4 | * |
5 | * ANY CHANGES MADE HERE WILL BE LOST! | 5 | * ANY CHANGES MADE HERE WILL BE LOST! |
6 | * | 6 | * |
7 | */ | 7 | */ |
8 | 8 | #include <stdbool.h> | |
9 | #ifndef HAS_BOOL | ||
10 | # define HAS_BOOL 1 | ||
11 | #endif | ||
9 | #line 1 "Context.xs" | 12 | #line 1 "Context.xs" |
10 | /* | 13 | /* |
11 | * Context.xs. XS interfaces for perf script. | 14 | * Context.xs. XS interfaces for perf script. |
12 | * | 15 | * |
13 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> | 16 | * Copyright (C) 2009 Tom Zanussi <tzanussi@gmail.com> |
14 | * | 17 | * |
15 | * This program is free software; you can redistribute it and/or modify | 18 | * This program is free software; you can redistribute it and/or modify |
16 | * it under the terms of the GNU General Public License as published by | 19 | * it under the terms of the GNU General Public License as published by |
17 | * the Free Software Foundation; either version 2 of the License, or | 20 | * the Free Software Foundation; either version 2 of the License, or |
18 | * (at your option) any later version. | 21 | * (at your option) any later version. |
19 | * | 22 | * |
20 | * This program is distributed in the hope that it will be useful, | 23 | * This program is distributed in the hope that it will be useful, |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
23 | * GNU General Public License for more details. | 26 | * GNU General Public License for more details. |
24 | * | 27 | * |
25 | * You should have received a copy of the GNU General Public License | 28 | * You should have received a copy of the GNU General Public License |
26 | * along with this program; if not, write to the Free Software | 29 | * along with this program; if not, write to the Free Software |
27 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 30 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
28 | * | 31 | * |
29 | */ | 32 | */ |
30 | 33 | ||
31 | #include "EXTERN.h" | 34 | #include "EXTERN.h" |
32 | #include "perl.h" | 35 | #include "perl.h" |
33 | #include "XSUB.h" | 36 | #include "XSUB.h" |
34 | #include "../../../perf.h" | 37 | #include "../../../perf.h" |
35 | #include "../../../util/trace-event.h" | 38 | #include "../../../util/trace-event.h" |
36 | 39 | ||
37 | #ifndef PERL_UNUSED_VAR | 40 | #ifndef PERL_UNUSED_VAR |
38 | # define PERL_UNUSED_VAR(var) if (0) var = var | 41 | # define PERL_UNUSED_VAR(var) if (0) var = var |
39 | #endif | 42 | #endif |
40 | 43 | ||
41 | #line 42 "Context.c" | 44 | #line 42 "Context.c" |
42 | 45 | ||
43 | XS(XS_Perf__Trace__Context_common_pc); /* prototype to pass -Wmissing-prototypes */ | 46 | XS(XS_Perf__Trace__Context_common_pc); /* prototype to pass -Wmissing-prototypes */ |
44 | XS(XS_Perf__Trace__Context_common_pc) | 47 | XS(XS_Perf__Trace__Context_common_pc) |
45 | { | 48 | { |
46 | #ifdef dVAR | 49 | #ifdef dVAR |
47 | dVAR; dXSARGS; | 50 | dVAR; dXSARGS; |
48 | #else | 51 | #else |
49 | dXSARGS; | 52 | dXSARGS; |
50 | #endif | 53 | #endif |
51 | if (items != 1) | 54 | if (items != 1) |
52 | Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_pc", "context"); | 55 | Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_pc", "context"); |
53 | PERL_UNUSED_VAR(cv); /* -W */ | 56 | PERL_UNUSED_VAR(cv); /* -W */ |
54 | { | 57 | { |
55 | struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0))); | 58 | struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0))); |
56 | int RETVAL; | 59 | int RETVAL; |
57 | dXSTARG; | 60 | dXSTARG; |
58 | 61 | ||
59 | RETVAL = common_pc(context); | 62 | RETVAL = common_pc(context); |
60 | XSprePUSH; PUSHi((IV)RETVAL); | 63 | XSprePUSH; PUSHi((IV)RETVAL); |
61 | } | 64 | } |
62 | XSRETURN(1); | 65 | XSRETURN(1); |
63 | } | 66 | } |
64 | 67 | ||
65 | 68 | ||
66 | XS(XS_Perf__Trace__Context_common_flags); /* prototype to pass -Wmissing-prototypes */ | 69 | XS(XS_Perf__Trace__Context_common_flags); /* prototype to pass -Wmissing-prototypes */ |
67 | XS(XS_Perf__Trace__Context_common_flags) | 70 | XS(XS_Perf__Trace__Context_common_flags) |
68 | { | 71 | { |
69 | #ifdef dVAR | 72 | #ifdef dVAR |
70 | dVAR; dXSARGS; | 73 | dVAR; dXSARGS; |
71 | #else | 74 | #else |
72 | dXSARGS; | 75 | dXSARGS; |
73 | #endif | 76 | #endif |
74 | if (items != 1) | 77 | if (items != 1) |
75 | Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_flags", "context"); | 78 | Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_flags", "context"); |
76 | PERL_UNUSED_VAR(cv); /* -W */ | 79 | PERL_UNUSED_VAR(cv); /* -W */ |
77 | { | 80 | { |
78 | struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0))); | 81 | struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0))); |
79 | int RETVAL; | 82 | int RETVAL; |
80 | dXSTARG; | 83 | dXSTARG; |
81 | 84 | ||
82 | RETVAL = common_flags(context); | 85 | RETVAL = common_flags(context); |
83 | XSprePUSH; PUSHi((IV)RETVAL); | 86 | XSprePUSH; PUSHi((IV)RETVAL); |
84 | } | 87 | } |
85 | XSRETURN(1); | 88 | XSRETURN(1); |
86 | } | 89 | } |
87 | 90 | ||
88 | 91 | ||
89 | XS(XS_Perf__Trace__Context_common_lock_depth); /* prototype to pass -Wmissing-prototypes */ | 92 | XS(XS_Perf__Trace__Context_common_lock_depth); /* prototype to pass -Wmissing-prototypes */ |
90 | XS(XS_Perf__Trace__Context_common_lock_depth) | 93 | XS(XS_Perf__Trace__Context_common_lock_depth) |
91 | { | 94 | { |
92 | #ifdef dVAR | 95 | #ifdef dVAR |
93 | dVAR; dXSARGS; | 96 | dVAR; dXSARGS; |
94 | #else | 97 | #else |
95 | dXSARGS; | 98 | dXSARGS; |
96 | #endif | 99 | #endif |
97 | if (items != 1) | 100 | if (items != 1) |
98 | Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_lock_depth", "context"); | 101 | Perl_croak(aTHX_ "Usage: %s(%s)", "Perf::Trace::Context::common_lock_depth", "context"); |
99 | PERL_UNUSED_VAR(cv); /* -W */ | 102 | PERL_UNUSED_VAR(cv); /* -W */ |
100 | { | 103 | { |
101 | struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0))); | 104 | struct scripting_context * context = INT2PTR(struct scripting_context *,SvIV(ST(0))); |
102 | int RETVAL; | 105 | int RETVAL; |
103 | dXSTARG; | 106 | dXSTARG; |
104 | 107 | ||
105 | RETVAL = common_lock_depth(context); | 108 | RETVAL = common_lock_depth(context); |
106 | XSprePUSH; PUSHi((IV)RETVAL); | 109 | XSprePUSH; PUSHi((IV)RETVAL); |
107 | } | 110 | } |
108 | XSRETURN(1); | 111 | XSRETURN(1); |
109 | } | 112 | } |
110 | 113 | ||
111 | #ifdef __cplusplus | 114 | #ifdef __cplusplus |
112 | extern "C" | 115 | extern "C" |
113 | #endif | 116 | #endif |
114 | XS(boot_Perf__Trace__Context); /* prototype to pass -Wmissing-prototypes */ | 117 | XS(boot_Perf__Trace__Context); /* prototype to pass -Wmissing-prototypes */ |
115 | XS(boot_Perf__Trace__Context) | 118 | XS(boot_Perf__Trace__Context) |
116 | { | 119 | { |
117 | #ifdef dVAR | 120 | #ifdef dVAR |
118 | dVAR; dXSARGS; | 121 | dVAR; dXSARGS; |
119 | #else | 122 | #else |
120 | dXSARGS; | 123 | dXSARGS; |
121 | #endif | 124 | #endif |
122 | const char* file = __FILE__; | 125 | const char* file = __FILE__; |
123 | 126 | ||
124 | PERL_UNUSED_VAR(cv); /* -W */ | 127 | PERL_UNUSED_VAR(cv); /* -W */ |
125 | PERL_UNUSED_VAR(items); /* -W */ | 128 | PERL_UNUSED_VAR(items); /* -W */ |
126 | XS_VERSION_BOOTCHECK ; | 129 | XS_VERSION_BOOTCHECK ; |
127 | 130 | ||
128 | newXSproto("Perf::Trace::Context::common_pc", XS_Perf__Trace__Context_common_pc, file, "$"); | 131 | newXSproto("Perf::Trace::Context::common_pc", XS_Perf__Trace__Context_common_pc, file, "$"); |
129 | newXSproto("Perf::Trace::Context::common_flags", XS_Perf__Trace__Context_common_flags, file, "$"); | 132 | newXSproto("Perf::Trace::Context::common_flags", XS_Perf__Trace__Context_common_flags, file, "$"); |
130 | newXSproto("Perf::Trace::Context::common_lock_depth", XS_Perf__Trace__Context_common_lock_depth, file, "$"); | 133 | newXSproto("Perf::Trace::Context::common_lock_depth", XS_Perf__Trace__Context_common_lock_depth, file, "$"); |
131 | if (PL_unitcheckav) | 134 | if (PL_unitcheckav) |
132 | call_list(PL_scopestack_ix, PL_unitcheckav); | 135 | call_list(PL_scopestack_ix, PL_unitcheckav); |
133 | XSRETURN_YES; | 136 | XSRETURN_YES; |
134 | } | 137 | } |
135 | 138 | ||
136 | 139 |
tools/perf/util/annotate.c
1 | /* | 1 | /* |
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | 2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
3 | * | 3 | * |
4 | * Parts came from builtin-annotate.c, see those files for further | 4 | * Parts came from builtin-annotate.c, see those files for further |
5 | * copyright notes. | 5 | * copyright notes. |
6 | * | 6 | * |
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 "util.h" | 10 | #include "util.h" |
11 | #include "ui/ui.h" | 11 | #include "ui/ui.h" |
12 | #include "sort.h" | 12 | #include "sort.h" |
13 | #include "build-id.h" | 13 | #include "build-id.h" |
14 | #include "color.h" | 14 | #include "color.h" |
15 | #include "cache.h" | 15 | #include "cache.h" |
16 | #include "symbol.h" | 16 | #include "symbol.h" |
17 | #include "debug.h" | 17 | #include "debug.h" |
18 | #include "annotate.h" | 18 | #include "annotate.h" |
19 | #include "evsel.h" | 19 | #include "evsel.h" |
20 | #include <regex.h> | 20 | #include <regex.h> |
21 | #include <pthread.h> | 21 | #include <pthread.h> |
22 | #include <linux/bitops.h> | 22 | #include <linux/bitops.h> |
23 | 23 | ||
24 | const char *disassembler_style; | 24 | const char *disassembler_style; |
25 | const char *objdump_path; | 25 | const char *objdump_path; |
26 | static regex_t file_lineno; | 26 | static regex_t file_lineno; |
27 | 27 | ||
28 | static struct ins *ins__find(const char *name); | 28 | static struct ins *ins__find(const char *name); |
29 | static int disasm_line__parse(char *line, char **namep, char **rawp); | 29 | static int disasm_line__parse(char *line, char **namep, char **rawp); |
30 | 30 | ||
31 | static void ins__delete(struct ins_operands *ops) | 31 | static void ins__delete(struct ins_operands *ops) |
32 | { | 32 | { |
33 | zfree(&ops->source.raw); | 33 | zfree(&ops->source.raw); |
34 | zfree(&ops->source.name); | 34 | zfree(&ops->source.name); |
35 | zfree(&ops->target.raw); | 35 | zfree(&ops->target.raw); |
36 | zfree(&ops->target.name); | 36 | zfree(&ops->target.name); |
37 | } | 37 | } |
38 | 38 | ||
39 | static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, | 39 | static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, |
40 | struct ins_operands *ops) | 40 | struct ins_operands *ops) |
41 | { | 41 | { |
42 | return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw); | 42 | return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->raw); |
43 | } | 43 | } |
44 | 44 | ||
45 | int ins__scnprintf(struct ins *ins, char *bf, size_t size, | 45 | int ins__scnprintf(struct ins *ins, char *bf, size_t size, |
46 | struct ins_operands *ops) | 46 | struct ins_operands *ops) |
47 | { | 47 | { |
48 | if (ins->ops->scnprintf) | 48 | if (ins->ops->scnprintf) |
49 | return ins->ops->scnprintf(ins, bf, size, ops); | 49 | return ins->ops->scnprintf(ins, bf, size, ops); |
50 | 50 | ||
51 | return ins__raw_scnprintf(ins, bf, size, ops); | 51 | return ins__raw_scnprintf(ins, bf, size, ops); |
52 | } | 52 | } |
53 | 53 | ||
54 | static int call__parse(struct ins_operands *ops) | 54 | static int call__parse(struct ins_operands *ops) |
55 | { | 55 | { |
56 | char *endptr, *tok, *name; | 56 | char *endptr, *tok, *name; |
57 | 57 | ||
58 | ops->target.addr = strtoull(ops->raw, &endptr, 16); | 58 | ops->target.addr = strtoull(ops->raw, &endptr, 16); |
59 | 59 | ||
60 | name = strchr(endptr, '<'); | 60 | name = strchr(endptr, '<'); |
61 | if (name == NULL) | 61 | if (name == NULL) |
62 | goto indirect_call; | 62 | goto indirect_call; |
63 | 63 | ||
64 | name++; | 64 | name++; |
65 | 65 | ||
66 | tok = strchr(name, '>'); | 66 | tok = strchr(name, '>'); |
67 | if (tok == NULL) | 67 | if (tok == NULL) |
68 | return -1; | 68 | return -1; |
69 | 69 | ||
70 | *tok = '\0'; | 70 | *tok = '\0'; |
71 | ops->target.name = strdup(name); | 71 | ops->target.name = strdup(name); |
72 | *tok = '>'; | 72 | *tok = '>'; |
73 | 73 | ||
74 | return ops->target.name == NULL ? -1 : 0; | 74 | return ops->target.name == NULL ? -1 : 0; |
75 | 75 | ||
76 | indirect_call: | 76 | indirect_call: |
77 | tok = strchr(endptr, '('); | 77 | tok = strchr(endptr, '('); |
78 | if (tok != NULL) { | 78 | if (tok != NULL) { |
79 | ops->target.addr = 0; | 79 | ops->target.addr = 0; |
80 | return 0; | 80 | return 0; |
81 | } | 81 | } |
82 | 82 | ||
83 | tok = strchr(endptr, '*'); | 83 | tok = strchr(endptr, '*'); |
84 | if (tok == NULL) | 84 | if (tok == NULL) |
85 | return -1; | 85 | return -1; |
86 | 86 | ||
87 | ops->target.addr = strtoull(tok + 1, NULL, 16); | 87 | ops->target.addr = strtoull(tok + 1, NULL, 16); |
88 | return 0; | 88 | return 0; |
89 | } | 89 | } |
90 | 90 | ||
91 | static int call__scnprintf(struct ins *ins, char *bf, size_t size, | 91 | static int call__scnprintf(struct ins *ins, char *bf, size_t size, |
92 | struct ins_operands *ops) | 92 | struct ins_operands *ops) |
93 | { | 93 | { |
94 | if (ops->target.name) | 94 | if (ops->target.name) |
95 | return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name); | 95 | return scnprintf(bf, size, "%-6.6s %s", ins->name, ops->target.name); |
96 | 96 | ||
97 | if (ops->target.addr == 0) | 97 | if (ops->target.addr == 0) |
98 | return ins__raw_scnprintf(ins, bf, size, ops); | 98 | return ins__raw_scnprintf(ins, bf, size, ops); |
99 | 99 | ||
100 | return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr); | 100 | return scnprintf(bf, size, "%-6.6s *%" PRIx64, ins->name, ops->target.addr); |
101 | } | 101 | } |
102 | 102 | ||
103 | static struct ins_ops call_ops = { | 103 | static struct ins_ops call_ops = { |
104 | .parse = call__parse, | 104 | .parse = call__parse, |
105 | .scnprintf = call__scnprintf, | 105 | .scnprintf = call__scnprintf, |
106 | }; | 106 | }; |
107 | 107 | ||
108 | bool ins__is_call(const struct ins *ins) | 108 | bool ins__is_call(const struct ins *ins) |
109 | { | 109 | { |
110 | return ins->ops == &call_ops; | 110 | return ins->ops == &call_ops; |
111 | } | 111 | } |
112 | 112 | ||
113 | static int jump__parse(struct ins_operands *ops) | 113 | static int jump__parse(struct ins_operands *ops) |
114 | { | 114 | { |
115 | const char *s = strchr(ops->raw, '+'); | 115 | const char *s = strchr(ops->raw, '+'); |
116 | 116 | ||
117 | ops->target.addr = strtoull(ops->raw, NULL, 16); | 117 | ops->target.addr = strtoull(ops->raw, NULL, 16); |
118 | 118 | ||
119 | if (s++ != NULL) | 119 | if (s++ != NULL) |
120 | ops->target.offset = strtoull(s, NULL, 16); | 120 | ops->target.offset = strtoull(s, NULL, 16); |
121 | else | 121 | else |
122 | ops->target.offset = UINT64_MAX; | 122 | ops->target.offset = UINT64_MAX; |
123 | 123 | ||
124 | return 0; | 124 | return 0; |
125 | } | 125 | } |
126 | 126 | ||
127 | static int jump__scnprintf(struct ins *ins, char *bf, size_t size, | 127 | static int jump__scnprintf(struct ins *ins, char *bf, size_t size, |
128 | struct ins_operands *ops) | 128 | struct ins_operands *ops) |
129 | { | 129 | { |
130 | return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset); | 130 | return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset); |
131 | } | 131 | } |
132 | 132 | ||
133 | static struct ins_ops jump_ops = { | 133 | static struct ins_ops jump_ops = { |
134 | .parse = jump__parse, | 134 | .parse = jump__parse, |
135 | .scnprintf = jump__scnprintf, | 135 | .scnprintf = jump__scnprintf, |
136 | }; | 136 | }; |
137 | 137 | ||
138 | bool ins__is_jump(const struct ins *ins) | 138 | bool ins__is_jump(const struct ins *ins) |
139 | { | 139 | { |
140 | return ins->ops == &jump_ops; | 140 | return ins->ops == &jump_ops; |
141 | } | 141 | } |
142 | 142 | ||
143 | static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) | 143 | static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) |
144 | { | 144 | { |
145 | char *endptr, *name, *t; | 145 | char *endptr, *name, *t; |
146 | 146 | ||
147 | if (strstr(raw, "(%rip)") == NULL) | 147 | if (strstr(raw, "(%rip)") == NULL) |
148 | return 0; | 148 | return 0; |
149 | 149 | ||
150 | *addrp = strtoull(comment, &endptr, 16); | 150 | *addrp = strtoull(comment, &endptr, 16); |
151 | name = strchr(endptr, '<'); | 151 | name = strchr(endptr, '<'); |
152 | if (name == NULL) | 152 | if (name == NULL) |
153 | return -1; | 153 | return -1; |
154 | 154 | ||
155 | name++; | 155 | name++; |
156 | 156 | ||
157 | t = strchr(name, '>'); | 157 | t = strchr(name, '>'); |
158 | if (t == NULL) | 158 | if (t == NULL) |
159 | return 0; | 159 | return 0; |
160 | 160 | ||
161 | *t = '\0'; | 161 | *t = '\0'; |
162 | *namep = strdup(name); | 162 | *namep = strdup(name); |
163 | *t = '>'; | 163 | *t = '>'; |
164 | 164 | ||
165 | return 0; | 165 | return 0; |
166 | } | 166 | } |
167 | 167 | ||
168 | static int lock__parse(struct ins_operands *ops) | 168 | static int lock__parse(struct ins_operands *ops) |
169 | { | 169 | { |
170 | char *name; | 170 | char *name; |
171 | 171 | ||
172 | ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); | 172 | ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); |
173 | if (ops->locked.ops == NULL) | 173 | if (ops->locked.ops == NULL) |
174 | return 0; | 174 | return 0; |
175 | 175 | ||
176 | if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) | 176 | if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) |
177 | goto out_free_ops; | 177 | goto out_free_ops; |
178 | 178 | ||
179 | ops->locked.ins = ins__find(name); | 179 | ops->locked.ins = ins__find(name); |
180 | free(name); | ||
181 | |||
180 | if (ops->locked.ins == NULL) | 182 | if (ops->locked.ins == NULL) |
181 | goto out_free_ops; | 183 | goto out_free_ops; |
182 | 184 | ||
183 | if (!ops->locked.ins->ops) | 185 | if (!ops->locked.ins->ops) |
184 | return 0; | 186 | return 0; |
185 | 187 | ||
186 | if (ops->locked.ins->ops->parse) | 188 | if (ops->locked.ins->ops->parse && |
187 | ops->locked.ins->ops->parse(ops->locked.ops); | 189 | ops->locked.ins->ops->parse(ops->locked.ops) < 0) |
190 | goto out_free_ops; | ||
188 | 191 | ||
189 | return 0; | 192 | return 0; |
190 | 193 | ||
191 | out_free_ops: | 194 | out_free_ops: |
192 | zfree(&ops->locked.ops); | 195 | zfree(&ops->locked.ops); |
193 | return 0; | 196 | return 0; |
194 | } | 197 | } |
195 | 198 | ||
196 | static int lock__scnprintf(struct ins *ins, char *bf, size_t size, | 199 | static int lock__scnprintf(struct ins *ins, char *bf, size_t size, |
197 | struct ins_operands *ops) | 200 | struct ins_operands *ops) |
198 | { | 201 | { |
199 | int printed; | 202 | int printed; |
200 | 203 | ||
201 | if (ops->locked.ins == NULL) | 204 | if (ops->locked.ins == NULL) |
202 | return ins__raw_scnprintf(ins, bf, size, ops); | 205 | return ins__raw_scnprintf(ins, bf, size, ops); |
203 | 206 | ||
204 | printed = scnprintf(bf, size, "%-6.6s ", ins->name); | 207 | printed = scnprintf(bf, size, "%-6.6s ", ins->name); |
205 | return printed + ins__scnprintf(ops->locked.ins, bf + printed, | 208 | return printed + ins__scnprintf(ops->locked.ins, bf + printed, |
206 | size - printed, ops->locked.ops); | 209 | size - printed, ops->locked.ops); |
207 | } | 210 | } |
208 | 211 | ||
209 | static void lock__delete(struct ins_operands *ops) | 212 | static void lock__delete(struct ins_operands *ops) |
210 | { | 213 | { |
214 | struct ins *ins = ops->locked.ins; | ||
215 | |||
216 | if (ins && ins->ops->free) | ||
217 | ins->ops->free(ops->locked.ops); | ||
218 | else | ||
219 | ins__delete(ops->locked.ops); | ||
220 | |||
211 | zfree(&ops->locked.ops); | 221 | zfree(&ops->locked.ops); |
212 | zfree(&ops->target.raw); | 222 | zfree(&ops->target.raw); |
213 | zfree(&ops->target.name); | 223 | zfree(&ops->target.name); |
214 | } | 224 | } |
215 | 225 | ||
216 | static struct ins_ops lock_ops = { | 226 | static struct ins_ops lock_ops = { |
217 | .free = lock__delete, | 227 | .free = lock__delete, |
218 | .parse = lock__parse, | 228 | .parse = lock__parse, |
219 | .scnprintf = lock__scnprintf, | 229 | .scnprintf = lock__scnprintf, |
220 | }; | 230 | }; |
221 | 231 | ||
222 | static int mov__parse(struct ins_operands *ops) | 232 | static int mov__parse(struct ins_operands *ops) |
223 | { | 233 | { |
224 | char *s = strchr(ops->raw, ','), *target, *comment, prev; | 234 | char *s = strchr(ops->raw, ','), *target, *comment, prev; |
225 | 235 | ||
226 | if (s == NULL) | 236 | if (s == NULL) |
227 | return -1; | 237 | return -1; |
228 | 238 | ||
229 | *s = '\0'; | 239 | *s = '\0'; |
230 | ops->source.raw = strdup(ops->raw); | 240 | ops->source.raw = strdup(ops->raw); |
231 | *s = ','; | 241 | *s = ','; |
232 | 242 | ||
233 | if (ops->source.raw == NULL) | 243 | if (ops->source.raw == NULL) |
234 | return -1; | 244 | return -1; |
235 | 245 | ||
236 | target = ++s; | 246 | target = ++s; |
237 | comment = strchr(s, '#'); | 247 | comment = strchr(s, '#'); |
238 | 248 | ||
239 | if (comment != NULL) | 249 | if (comment != NULL) |
240 | s = comment - 1; | 250 | s = comment - 1; |
241 | else | 251 | else |
242 | s = strchr(s, '\0') - 1; | 252 | s = strchr(s, '\0') - 1; |
243 | 253 | ||
244 | while (s > target && isspace(s[0])) | 254 | while (s > target && isspace(s[0])) |
245 | --s; | 255 | --s; |
246 | s++; | 256 | s++; |
247 | prev = *s; | 257 | prev = *s; |
248 | *s = '\0'; | 258 | *s = '\0'; |
249 | 259 | ||
250 | ops->target.raw = strdup(target); | 260 | ops->target.raw = strdup(target); |
251 | *s = prev; | 261 | *s = prev; |
252 | 262 | ||
253 | if (ops->target.raw == NULL) | 263 | if (ops->target.raw == NULL) |
254 | goto out_free_source; | 264 | goto out_free_source; |
255 | 265 | ||
256 | if (comment == NULL) | 266 | if (comment == NULL) |
257 | return 0; | 267 | return 0; |
258 | 268 | ||
259 | while (comment[0] != '\0' && isspace(comment[0])) | 269 | while (comment[0] != '\0' && isspace(comment[0])) |
260 | ++comment; | 270 | ++comment; |
261 | 271 | ||
262 | comment__symbol(ops->source.raw, comment, &ops->source.addr, &ops->source.name); | 272 | comment__symbol(ops->source.raw, comment, &ops->source.addr, &ops->source.name); |
263 | comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name); | 273 | comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name); |
264 | 274 | ||
265 | return 0; | 275 | return 0; |
266 | 276 | ||
267 | out_free_source: | 277 | out_free_source: |
268 | zfree(&ops->source.raw); | 278 | zfree(&ops->source.raw); |
269 | return -1; | 279 | return -1; |
270 | } | 280 | } |
271 | 281 | ||
272 | static int mov__scnprintf(struct ins *ins, char *bf, size_t size, | 282 | static int mov__scnprintf(struct ins *ins, char *bf, size_t size, |
273 | struct ins_operands *ops) | 283 | struct ins_operands *ops) |
274 | { | 284 | { |
275 | return scnprintf(bf, size, "%-6.6s %s,%s", ins->name, | 285 | return scnprintf(bf, size, "%-6.6s %s,%s", ins->name, |
276 | ops->source.name ?: ops->source.raw, | 286 | ops->source.name ?: ops->source.raw, |
277 | ops->target.name ?: ops->target.raw); | 287 | ops->target.name ?: ops->target.raw); |
278 | } | 288 | } |
279 | 289 | ||
280 | static struct ins_ops mov_ops = { | 290 | static struct ins_ops mov_ops = { |
281 | .parse = mov__parse, | 291 | .parse = mov__parse, |
282 | .scnprintf = mov__scnprintf, | 292 | .scnprintf = mov__scnprintf, |
283 | }; | 293 | }; |
284 | 294 | ||
285 | static int dec__parse(struct ins_operands *ops) | 295 | static int dec__parse(struct ins_operands *ops) |
286 | { | 296 | { |
287 | char *target, *comment, *s, prev; | 297 | char *target, *comment, *s, prev; |
288 | 298 | ||
289 | target = s = ops->raw; | 299 | target = s = ops->raw; |
290 | 300 | ||
291 | while (s[0] != '\0' && !isspace(s[0])) | 301 | while (s[0] != '\0' && !isspace(s[0])) |
292 | ++s; | 302 | ++s; |
293 | prev = *s; | 303 | prev = *s; |
294 | *s = '\0'; | 304 | *s = '\0'; |
295 | 305 | ||
296 | ops->target.raw = strdup(target); | 306 | ops->target.raw = strdup(target); |
297 | *s = prev; | 307 | *s = prev; |
298 | 308 | ||
299 | if (ops->target.raw == NULL) | 309 | if (ops->target.raw == NULL) |
300 | return -1; | 310 | return -1; |
301 | 311 | ||
302 | comment = strchr(s, '#'); | 312 | comment = strchr(s, '#'); |
303 | if (comment == NULL) | 313 | if (comment == NULL) |
304 | return 0; | 314 | return 0; |
305 | 315 | ||
306 | while (comment[0] != '\0' && isspace(comment[0])) | 316 | while (comment[0] != '\0' && isspace(comment[0])) |
307 | ++comment; | 317 | ++comment; |
308 | 318 | ||
309 | comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name); | 319 | comment__symbol(ops->target.raw, comment, &ops->target.addr, &ops->target.name); |
310 | 320 | ||
311 | return 0; | 321 | return 0; |
312 | } | 322 | } |
313 | 323 | ||
314 | static int dec__scnprintf(struct ins *ins, char *bf, size_t size, | 324 | static int dec__scnprintf(struct ins *ins, char *bf, size_t size, |
315 | struct ins_operands *ops) | 325 | struct ins_operands *ops) |
316 | { | 326 | { |
317 | return scnprintf(bf, size, "%-6.6s %s", ins->name, | 327 | return scnprintf(bf, size, "%-6.6s %s", ins->name, |
318 | ops->target.name ?: ops->target.raw); | 328 | ops->target.name ?: ops->target.raw); |
319 | } | 329 | } |
320 | 330 | ||
321 | static struct ins_ops dec_ops = { | 331 | static struct ins_ops dec_ops = { |
322 | .parse = dec__parse, | 332 | .parse = dec__parse, |
323 | .scnprintf = dec__scnprintf, | 333 | .scnprintf = dec__scnprintf, |
324 | }; | 334 | }; |
325 | 335 | ||
326 | static int nop__scnprintf(struct ins *ins __maybe_unused, char *bf, size_t size, | 336 | static int nop__scnprintf(struct ins *ins __maybe_unused, char *bf, size_t size, |
327 | struct ins_operands *ops __maybe_unused) | 337 | struct ins_operands *ops __maybe_unused) |
328 | { | 338 | { |
329 | return scnprintf(bf, size, "%-6.6s", "nop"); | 339 | return scnprintf(bf, size, "%-6.6s", "nop"); |
330 | } | 340 | } |
331 | 341 | ||
332 | static struct ins_ops nop_ops = { | 342 | static struct ins_ops nop_ops = { |
333 | .scnprintf = nop__scnprintf, | 343 | .scnprintf = nop__scnprintf, |
334 | }; | 344 | }; |
335 | 345 | ||
336 | /* | 346 | /* |
337 | * Must be sorted by name! | 347 | * Must be sorted by name! |
338 | */ | 348 | */ |
339 | static struct ins instructions[] = { | 349 | static struct ins instructions[] = { |
340 | { .name = "add", .ops = &mov_ops, }, | 350 | { .name = "add", .ops = &mov_ops, }, |
341 | { .name = "addl", .ops = &mov_ops, }, | 351 | { .name = "addl", .ops = &mov_ops, }, |
342 | { .name = "addq", .ops = &mov_ops, }, | 352 | { .name = "addq", .ops = &mov_ops, }, |
343 | { .name = "addw", .ops = &mov_ops, }, | 353 | { .name = "addw", .ops = &mov_ops, }, |
344 | { .name = "and", .ops = &mov_ops, }, | 354 | { .name = "and", .ops = &mov_ops, }, |
345 | { .name = "bts", .ops = &mov_ops, }, | 355 | { .name = "bts", .ops = &mov_ops, }, |
346 | { .name = "call", .ops = &call_ops, }, | 356 | { .name = "call", .ops = &call_ops, }, |
347 | { .name = "callq", .ops = &call_ops, }, | 357 | { .name = "callq", .ops = &call_ops, }, |
348 | { .name = "cmp", .ops = &mov_ops, }, | 358 | { .name = "cmp", .ops = &mov_ops, }, |
349 | { .name = "cmpb", .ops = &mov_ops, }, | 359 | { .name = "cmpb", .ops = &mov_ops, }, |
350 | { .name = "cmpl", .ops = &mov_ops, }, | 360 | { .name = "cmpl", .ops = &mov_ops, }, |
351 | { .name = "cmpq", .ops = &mov_ops, }, | 361 | { .name = "cmpq", .ops = &mov_ops, }, |
352 | { .name = "cmpw", .ops = &mov_ops, }, | 362 | { .name = "cmpw", .ops = &mov_ops, }, |
353 | { .name = "cmpxch", .ops = &mov_ops, }, | 363 | { .name = "cmpxch", .ops = &mov_ops, }, |
354 | { .name = "dec", .ops = &dec_ops, }, | 364 | { .name = "dec", .ops = &dec_ops, }, |
355 | { .name = "decl", .ops = &dec_ops, }, | 365 | { .name = "decl", .ops = &dec_ops, }, |
356 | { .name = "imul", .ops = &mov_ops, }, | 366 | { .name = "imul", .ops = &mov_ops, }, |
357 | { .name = "inc", .ops = &dec_ops, }, | 367 | { .name = "inc", .ops = &dec_ops, }, |
358 | { .name = "incl", .ops = &dec_ops, }, | 368 | { .name = "incl", .ops = &dec_ops, }, |
359 | { .name = "ja", .ops = &jump_ops, }, | 369 | { .name = "ja", .ops = &jump_ops, }, |
360 | { .name = "jae", .ops = &jump_ops, }, | 370 | { .name = "jae", .ops = &jump_ops, }, |
361 | { .name = "jb", .ops = &jump_ops, }, | 371 | { .name = "jb", .ops = &jump_ops, }, |
362 | { .name = "jbe", .ops = &jump_ops, }, | 372 | { .name = "jbe", .ops = &jump_ops, }, |
363 | { .name = "jc", .ops = &jump_ops, }, | 373 | { .name = "jc", .ops = &jump_ops, }, |
364 | { .name = "jcxz", .ops = &jump_ops, }, | 374 | { .name = "jcxz", .ops = &jump_ops, }, |
365 | { .name = "je", .ops = &jump_ops, }, | 375 | { .name = "je", .ops = &jump_ops, }, |
366 | { .name = "jecxz", .ops = &jump_ops, }, | 376 | { .name = "jecxz", .ops = &jump_ops, }, |
367 | { .name = "jg", .ops = &jump_ops, }, | 377 | { .name = "jg", .ops = &jump_ops, }, |
368 | { .name = "jge", .ops = &jump_ops, }, | 378 | { .name = "jge", .ops = &jump_ops, }, |
369 | { .name = "jl", .ops = &jump_ops, }, | 379 | { .name = "jl", .ops = &jump_ops, }, |
370 | { .name = "jle", .ops = &jump_ops, }, | 380 | { .name = "jle", .ops = &jump_ops, }, |
371 | { .name = "jmp", .ops = &jump_ops, }, | 381 | { .name = "jmp", .ops = &jump_ops, }, |
372 | { .name = "jmpq", .ops = &jump_ops, }, | 382 | { .name = "jmpq", .ops = &jump_ops, }, |
373 | { .name = "jna", .ops = &jump_ops, }, | 383 | { .name = "jna", .ops = &jump_ops, }, |
374 | { .name = "jnae", .ops = &jump_ops, }, | 384 | { .name = "jnae", .ops = &jump_ops, }, |
375 | { .name = "jnb", .ops = &jump_ops, }, | 385 | { .name = "jnb", .ops = &jump_ops, }, |
376 | { .name = "jnbe", .ops = &jump_ops, }, | 386 | { .name = "jnbe", .ops = &jump_ops, }, |
377 | { .name = "jnc", .ops = &jump_ops, }, | 387 | { .name = "jnc", .ops = &jump_ops, }, |
378 | { .name = "jne", .ops = &jump_ops, }, | 388 | { .name = "jne", .ops = &jump_ops, }, |
379 | { .name = "jng", .ops = &jump_ops, }, | 389 | { .name = "jng", .ops = &jump_ops, }, |
380 | { .name = "jnge", .ops = &jump_ops, }, | 390 | { .name = "jnge", .ops = &jump_ops, }, |
381 | { .name = "jnl", .ops = &jump_ops, }, | 391 | { .name = "jnl", .ops = &jump_ops, }, |
382 | { .name = "jnle", .ops = &jump_ops, }, | 392 | { .name = "jnle", .ops = &jump_ops, }, |
383 | { .name = "jno", .ops = &jump_ops, }, | 393 | { .name = "jno", .ops = &jump_ops, }, |
384 | { .name = "jnp", .ops = &jump_ops, }, | 394 | { .name = "jnp", .ops = &jump_ops, }, |
385 | { .name = "jns", .ops = &jump_ops, }, | 395 | { .name = "jns", .ops = &jump_ops, }, |
386 | { .name = "jnz", .ops = &jump_ops, }, | 396 | { .name = "jnz", .ops = &jump_ops, }, |
387 | { .name = "jo", .ops = &jump_ops, }, | 397 | { .name = "jo", .ops = &jump_ops, }, |
388 | { .name = "jp", .ops = &jump_ops, }, | 398 | { .name = "jp", .ops = &jump_ops, }, |
389 | { .name = "jpe", .ops = &jump_ops, }, | 399 | { .name = "jpe", .ops = &jump_ops, }, |
390 | { .name = "jpo", .ops = &jump_ops, }, | 400 | { .name = "jpo", .ops = &jump_ops, }, |
391 | { .name = "jrcxz", .ops = &jump_ops, }, | 401 | { .name = "jrcxz", .ops = &jump_ops, }, |
392 | { .name = "js", .ops = &jump_ops, }, | 402 | { .name = "js", .ops = &jump_ops, }, |
393 | { .name = "jz", .ops = &jump_ops, }, | 403 | { .name = "jz", .ops = &jump_ops, }, |
394 | { .name = "lea", .ops = &mov_ops, }, | 404 | { .name = "lea", .ops = &mov_ops, }, |
395 | { .name = "lock", .ops = &lock_ops, }, | 405 | { .name = "lock", .ops = &lock_ops, }, |
396 | { .name = "mov", .ops = &mov_ops, }, | 406 | { .name = "mov", .ops = &mov_ops, }, |
397 | { .name = "movb", .ops = &mov_ops, }, | 407 | { .name = "movb", .ops = &mov_ops, }, |
398 | { .name = "movdqa",.ops = &mov_ops, }, | 408 | { .name = "movdqa",.ops = &mov_ops, }, |
399 | { .name = "movl", .ops = &mov_ops, }, | 409 | { .name = "movl", .ops = &mov_ops, }, |
400 | { .name = "movq", .ops = &mov_ops, }, | 410 | { .name = "movq", .ops = &mov_ops, }, |
401 | { .name = "movslq", .ops = &mov_ops, }, | 411 | { .name = "movslq", .ops = &mov_ops, }, |
402 | { .name = "movzbl", .ops = &mov_ops, }, | 412 | { .name = "movzbl", .ops = &mov_ops, }, |
403 | { .name = "movzwl", .ops = &mov_ops, }, | 413 | { .name = "movzwl", .ops = &mov_ops, }, |
404 | { .name = "nop", .ops = &nop_ops, }, | 414 | { .name = "nop", .ops = &nop_ops, }, |
405 | { .name = "nopl", .ops = &nop_ops, }, | 415 | { .name = "nopl", .ops = &nop_ops, }, |
406 | { .name = "nopw", .ops = &nop_ops, }, | 416 | { .name = "nopw", .ops = &nop_ops, }, |
407 | { .name = "or", .ops = &mov_ops, }, | 417 | { .name = "or", .ops = &mov_ops, }, |
408 | { .name = "orl", .ops = &mov_ops, }, | 418 | { .name = "orl", .ops = &mov_ops, }, |
409 | { .name = "test", .ops = &mov_ops, }, | 419 | { .name = "test", .ops = &mov_ops, }, |
410 | { .name = "testb", .ops = &mov_ops, }, | 420 | { .name = "testb", .ops = &mov_ops, }, |
411 | { .name = "testl", .ops = &mov_ops, }, | 421 | { .name = "testl", .ops = &mov_ops, }, |
412 | { .name = "xadd", .ops = &mov_ops, }, | 422 | { .name = "xadd", .ops = &mov_ops, }, |
413 | { .name = "xbeginl", .ops = &jump_ops, }, | 423 | { .name = "xbeginl", .ops = &jump_ops, }, |
414 | { .name = "xbeginq", .ops = &jump_ops, }, | 424 | { .name = "xbeginq", .ops = &jump_ops, }, |
415 | }; | 425 | }; |
416 | 426 | ||
417 | static int ins__cmp(const void *name, const void *insp) | 427 | static int ins__cmp(const void *name, const void *insp) |
418 | { | 428 | { |
419 | const struct ins *ins = insp; | 429 | const struct ins *ins = insp; |
420 | 430 | ||
421 | return strcmp(name, ins->name); | 431 | return strcmp(name, ins->name); |
422 | } | 432 | } |
423 | 433 | ||
424 | static struct ins *ins__find(const char *name) | 434 | static struct ins *ins__find(const char *name) |
425 | { | 435 | { |
426 | const int nmemb = ARRAY_SIZE(instructions); | 436 | const int nmemb = ARRAY_SIZE(instructions); |
427 | 437 | ||
428 | return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp); | 438 | return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp); |
429 | } | 439 | } |
430 | 440 | ||
431 | int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym) | 441 | int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym) |
432 | { | 442 | { |
433 | struct annotation *notes = symbol__annotation(sym); | 443 | struct annotation *notes = symbol__annotation(sym); |
434 | pthread_mutex_init(¬es->lock, NULL); | 444 | pthread_mutex_init(¬es->lock, NULL); |
435 | return 0; | 445 | return 0; |
436 | } | 446 | } |
437 | 447 | ||
438 | int symbol__alloc_hist(struct symbol *sym) | 448 | int symbol__alloc_hist(struct symbol *sym) |
439 | { | 449 | { |
440 | struct annotation *notes = symbol__annotation(sym); | 450 | struct annotation *notes = symbol__annotation(sym); |
441 | const size_t size = symbol__size(sym); | 451 | const size_t size = symbol__size(sym); |
442 | size_t sizeof_sym_hist; | 452 | size_t sizeof_sym_hist; |
443 | 453 | ||
444 | /* Check for overflow when calculating sizeof_sym_hist */ | 454 | /* Check for overflow when calculating sizeof_sym_hist */ |
445 | if (size > (SIZE_MAX - sizeof(struct sym_hist)) / sizeof(u64)) | 455 | if (size > (SIZE_MAX - sizeof(struct sym_hist)) / sizeof(u64)) |
446 | return -1; | 456 | return -1; |
447 | 457 | ||
448 | sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64)); | 458 | sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64)); |
449 | 459 | ||
450 | /* Check for overflow in zalloc argument */ | 460 | /* Check for overflow in zalloc argument */ |
451 | if (sizeof_sym_hist > (SIZE_MAX - sizeof(*notes->src)) | 461 | if (sizeof_sym_hist > (SIZE_MAX - sizeof(*notes->src)) |
452 | / symbol_conf.nr_events) | 462 | / symbol_conf.nr_events) |
453 | return -1; | 463 | return -1; |
454 | 464 | ||
455 | notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); | 465 | notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); |
456 | if (notes->src == NULL) | 466 | if (notes->src == NULL) |
457 | return -1; | 467 | return -1; |
458 | notes->src->sizeof_sym_hist = sizeof_sym_hist; | 468 | notes->src->sizeof_sym_hist = sizeof_sym_hist; |
459 | notes->src->nr_histograms = symbol_conf.nr_events; | 469 | notes->src->nr_histograms = symbol_conf.nr_events; |
460 | INIT_LIST_HEAD(¬es->src->source); | 470 | INIT_LIST_HEAD(¬es->src->source); |
461 | return 0; | 471 | return 0; |
462 | } | 472 | } |
463 | 473 | ||
464 | void symbol__annotate_zero_histograms(struct symbol *sym) | 474 | void symbol__annotate_zero_histograms(struct symbol *sym) |
465 | { | 475 | { |
466 | struct annotation *notes = symbol__annotation(sym); | 476 | struct annotation *notes = symbol__annotation(sym); |
467 | 477 | ||
468 | pthread_mutex_lock(¬es->lock); | 478 | pthread_mutex_lock(¬es->lock); |
469 | if (notes->src != NULL) | 479 | if (notes->src != NULL) |
470 | memset(notes->src->histograms, 0, | 480 | memset(notes->src->histograms, 0, |
471 | notes->src->nr_histograms * notes->src->sizeof_sym_hist); | 481 | notes->src->nr_histograms * notes->src->sizeof_sym_hist); |
472 | pthread_mutex_unlock(¬es->lock); | 482 | pthread_mutex_unlock(¬es->lock); |
473 | } | 483 | } |
474 | 484 | ||
475 | static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, | 485 | static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, |
476 | struct annotation *notes, int evidx, u64 addr) | 486 | struct annotation *notes, int evidx, u64 addr) |
477 | { | 487 | { |
478 | unsigned offset; | 488 | unsigned offset; |
479 | struct sym_hist *h; | 489 | struct sym_hist *h; |
480 | 490 | ||
481 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); | 491 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); |
482 | 492 | ||
483 | if (addr < sym->start || addr >= sym->end) | 493 | if (addr < sym->start || addr >= sym->end) |
484 | return -ERANGE; | 494 | return -ERANGE; |
485 | 495 | ||
486 | offset = addr - sym->start; | 496 | offset = addr - sym->start; |
487 | h = annotation__histogram(notes, evidx); | 497 | h = annotation__histogram(notes, evidx); |
488 | h->sum++; | 498 | h->sum++; |
489 | h->addr[offset]++; | 499 | h->addr[offset]++; |
490 | 500 | ||
491 | pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64 | 501 | pr_debug3("%#" PRIx64 " %s: period++ [addr: %#" PRIx64 ", %#" PRIx64 |
492 | ", evidx=%d] => %" PRIu64 "\n", sym->start, sym->name, | 502 | ", evidx=%d] => %" PRIu64 "\n", sym->start, sym->name, |
493 | addr, addr - sym->start, evidx, h->addr[offset]); | 503 | addr, addr - sym->start, evidx, h->addr[offset]); |
494 | return 0; | 504 | return 0; |
495 | } | 505 | } |
496 | 506 | ||
497 | static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | 507 | static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, |
498 | int evidx, u64 addr) | 508 | int evidx, u64 addr) |
499 | { | 509 | { |
500 | struct annotation *notes; | 510 | struct annotation *notes; |
501 | 511 | ||
502 | if (sym == NULL) | 512 | if (sym == NULL) |
503 | return 0; | 513 | return 0; |
504 | 514 | ||
505 | notes = symbol__annotation(sym); | 515 | notes = symbol__annotation(sym); |
506 | if (notes->src == NULL) { | 516 | if (notes->src == NULL) { |
507 | if (symbol__alloc_hist(sym) < 0) | 517 | if (symbol__alloc_hist(sym) < 0) |
508 | return -ENOMEM; | 518 | return -ENOMEM; |
509 | } | 519 | } |
510 | 520 | ||
511 | return __symbol__inc_addr_samples(sym, map, notes, evidx, addr); | 521 | return __symbol__inc_addr_samples(sym, map, notes, evidx, addr); |
512 | } | 522 | } |
513 | 523 | ||
514 | int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx) | 524 | int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx) |
515 | { | 525 | { |
516 | return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr); | 526 | return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr); |
517 | } | 527 | } |
518 | 528 | ||
519 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) | 529 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) |
520 | { | 530 | { |
521 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); | 531 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); |
522 | } | 532 | } |
523 | 533 | ||
524 | static void disasm_line__init_ins(struct disasm_line *dl) | 534 | static void disasm_line__init_ins(struct disasm_line *dl) |
525 | { | 535 | { |
526 | dl->ins = ins__find(dl->name); | 536 | dl->ins = ins__find(dl->name); |
527 | 537 | ||
528 | if (dl->ins == NULL) | 538 | if (dl->ins == NULL) |
529 | return; | 539 | return; |
530 | 540 | ||
531 | if (!dl->ins->ops) | 541 | if (!dl->ins->ops) |
532 | return; | 542 | return; |
533 | 543 | ||
534 | if (dl->ins->ops->parse) | 544 | if (dl->ins->ops->parse && dl->ins->ops->parse(&dl->ops) < 0) |
535 | dl->ins->ops->parse(&dl->ops); | 545 | dl->ins = NULL; |
536 | } | 546 | } |
537 | 547 | ||
538 | static int disasm_line__parse(char *line, char **namep, char **rawp) | 548 | static int disasm_line__parse(char *line, char **namep, char **rawp) |
539 | { | 549 | { |
540 | char *name = line, tmp; | 550 | char *name = line, tmp; |
541 | 551 | ||
542 | while (isspace(name[0])) | 552 | while (isspace(name[0])) |
543 | ++name; | 553 | ++name; |
544 | 554 | ||
545 | if (name[0] == '\0') | 555 | if (name[0] == '\0') |
546 | return -1; | 556 | return -1; |
547 | 557 | ||
548 | *rawp = name + 1; | 558 | *rawp = name + 1; |
549 | 559 | ||
550 | while ((*rawp)[0] != '\0' && !isspace((*rawp)[0])) | 560 | while ((*rawp)[0] != '\0' && !isspace((*rawp)[0])) |
551 | ++*rawp; | 561 | ++*rawp; |
552 | 562 | ||
553 | tmp = (*rawp)[0]; | 563 | tmp = (*rawp)[0]; |
554 | (*rawp)[0] = '\0'; | 564 | (*rawp)[0] = '\0'; |
555 | *namep = strdup(name); | 565 | *namep = strdup(name); |
556 | 566 | ||
557 | if (*namep == NULL) | 567 | if (*namep == NULL) |
558 | goto out_free_name; | 568 | goto out_free_name; |
559 | 569 | ||
560 | (*rawp)[0] = tmp; | 570 | (*rawp)[0] = tmp; |
561 | 571 | ||
562 | if ((*rawp)[0] != '\0') { | 572 | if ((*rawp)[0] != '\0') { |
563 | (*rawp)++; | 573 | (*rawp)++; |
564 | while (isspace((*rawp)[0])) | 574 | while (isspace((*rawp)[0])) |
565 | ++(*rawp); | 575 | ++(*rawp); |
566 | } | 576 | } |
567 | 577 | ||
568 | return 0; | 578 | return 0; |
569 | 579 | ||
570 | out_free_name: | 580 | out_free_name: |
571 | zfree(namep); | 581 | zfree(namep); |
572 | return -1; | 582 | return -1; |
573 | } | 583 | } |
574 | 584 | ||
575 | static struct disasm_line *disasm_line__new(s64 offset, char *line, | 585 | static struct disasm_line *disasm_line__new(s64 offset, char *line, |
576 | size_t privsize, int line_nr) | 586 | size_t privsize, int line_nr) |
577 | { | 587 | { |
578 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); | 588 | struct disasm_line *dl = zalloc(sizeof(*dl) + privsize); |
579 | 589 | ||
580 | if (dl != NULL) { | 590 | if (dl != NULL) { |
581 | dl->offset = offset; | 591 | dl->offset = offset; |
582 | dl->line = strdup(line); | 592 | dl->line = strdup(line); |
583 | dl->line_nr = line_nr; | 593 | dl->line_nr = line_nr; |
584 | if (dl->line == NULL) | 594 | if (dl->line == NULL) |
585 | goto out_delete; | 595 | goto out_delete; |
586 | 596 | ||
587 | if (offset != -1) { | 597 | if (offset != -1) { |
588 | if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0) | 598 | if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0) |
589 | goto out_free_line; | 599 | goto out_free_line; |
590 | 600 | ||
591 | disasm_line__init_ins(dl); | 601 | disasm_line__init_ins(dl); |
592 | } | 602 | } |
593 | } | 603 | } |
594 | 604 | ||
595 | return dl; | 605 | return dl; |
596 | 606 | ||
597 | out_free_line: | 607 | out_free_line: |
598 | zfree(&dl->line); | 608 | zfree(&dl->line); |
599 | out_delete: | 609 | out_delete: |
600 | free(dl); | 610 | free(dl); |
601 | return NULL; | 611 | return NULL; |
602 | } | 612 | } |
603 | 613 | ||
604 | void disasm_line__free(struct disasm_line *dl) | 614 | void disasm_line__free(struct disasm_line *dl) |
605 | { | 615 | { |
606 | zfree(&dl->line); | 616 | zfree(&dl->line); |
607 | zfree(&dl->name); | 617 | zfree(&dl->name); |
608 | if (dl->ins && dl->ins->ops->free) | 618 | if (dl->ins && dl->ins->ops->free) |
609 | dl->ins->ops->free(&dl->ops); | 619 | dl->ins->ops->free(&dl->ops); |
610 | else | 620 | else |
611 | ins__delete(&dl->ops); | 621 | ins__delete(&dl->ops); |
612 | free(dl); | 622 | free(dl); |
613 | } | 623 | } |
614 | 624 | ||
615 | int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw) | 625 | int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw) |
616 | { | 626 | { |
617 | if (raw || !dl->ins) | 627 | if (raw || !dl->ins) |
618 | return scnprintf(bf, size, "%-6.6s %s", dl->name, dl->ops.raw); | 628 | return scnprintf(bf, size, "%-6.6s %s", dl->name, dl->ops.raw); |
619 | 629 | ||
620 | return ins__scnprintf(dl->ins, bf, size, &dl->ops); | 630 | return ins__scnprintf(dl->ins, bf, size, &dl->ops); |
621 | } | 631 | } |
622 | 632 | ||
623 | static void disasm__add(struct list_head *head, struct disasm_line *line) | 633 | static void disasm__add(struct list_head *head, struct disasm_line *line) |
624 | { | 634 | { |
625 | list_add_tail(&line->node, head); | 635 | list_add_tail(&line->node, head); |
626 | } | 636 | } |
627 | 637 | ||
628 | struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos) | 638 | struct disasm_line *disasm__get_next_ip_line(struct list_head *head, struct disasm_line *pos) |
629 | { | 639 | { |
630 | list_for_each_entry_continue(pos, head, node) | 640 | list_for_each_entry_continue(pos, head, node) |
631 | if (pos->offset >= 0) | 641 | if (pos->offset >= 0) |
632 | return pos; | 642 | return pos; |
633 | 643 | ||
634 | return NULL; | 644 | return NULL; |
635 | } | 645 | } |
636 | 646 | ||
637 | double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, | 647 | double disasm__calc_percent(struct annotation *notes, int evidx, s64 offset, |
638 | s64 end, const char **path) | 648 | s64 end, const char **path) |
639 | { | 649 | { |
640 | struct source_line *src_line = notes->src->lines; | 650 | struct source_line *src_line = notes->src->lines; |
641 | double percent = 0.0; | 651 | double percent = 0.0; |
642 | 652 | ||
643 | if (src_line) { | 653 | if (src_line) { |
644 | size_t sizeof_src_line = sizeof(*src_line) + | 654 | size_t sizeof_src_line = sizeof(*src_line) + |
645 | sizeof(src_line->p) * (src_line->nr_pcnt - 1); | 655 | sizeof(src_line->p) * (src_line->nr_pcnt - 1); |
646 | 656 | ||
647 | while (offset < end) { | 657 | while (offset < end) { |
648 | src_line = (void *)notes->src->lines + | 658 | src_line = (void *)notes->src->lines + |
649 | (sizeof_src_line * offset); | 659 | (sizeof_src_line * offset); |
650 | 660 | ||
651 | if (*path == NULL) | 661 | if (*path == NULL) |
652 | *path = src_line->path; | 662 | *path = src_line->path; |
653 | 663 | ||
654 | percent += src_line->p[evidx].percent; | 664 | percent += src_line->p[evidx].percent; |
655 | offset++; | 665 | offset++; |
656 | } | 666 | } |
657 | } else { | 667 | } else { |
658 | struct sym_hist *h = annotation__histogram(notes, evidx); | 668 | struct sym_hist *h = annotation__histogram(notes, evidx); |
659 | unsigned int hits = 0; | 669 | unsigned int hits = 0; |
660 | 670 | ||
661 | while (offset < end) | 671 | while (offset < end) |
662 | hits += h->addr[offset++]; | 672 | hits += h->addr[offset++]; |
663 | 673 | ||
664 | if (h->sum) | 674 | if (h->sum) |
665 | percent = 100.0 * hits / h->sum; | 675 | percent = 100.0 * hits / h->sum; |
666 | } | 676 | } |
667 | 677 | ||
668 | return percent; | 678 | return percent; |
669 | } | 679 | } |
670 | 680 | ||
671 | static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, | 681 | static int disasm_line__print(struct disasm_line *dl, struct symbol *sym, u64 start, |
672 | struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, | 682 | struct perf_evsel *evsel, u64 len, int min_pcnt, int printed, |
673 | int max_lines, struct disasm_line *queue) | 683 | int max_lines, struct disasm_line *queue) |
674 | { | 684 | { |
675 | static const char *prev_line; | 685 | static const char *prev_line; |
676 | static const char *prev_color; | 686 | static const char *prev_color; |
677 | 687 | ||
678 | if (dl->offset != -1) { | 688 | if (dl->offset != -1) { |
679 | const char *path = NULL; | 689 | const char *path = NULL; |
680 | double percent, max_percent = 0.0; | 690 | double percent, max_percent = 0.0; |
681 | double *ppercents = &percent; | 691 | double *ppercents = &percent; |
682 | int i, nr_percent = 1; | 692 | int i, nr_percent = 1; |
683 | const char *color; | 693 | const char *color; |
684 | struct annotation *notes = symbol__annotation(sym); | 694 | struct annotation *notes = symbol__annotation(sym); |
685 | s64 offset = dl->offset; | 695 | s64 offset = dl->offset; |
686 | const u64 addr = start + offset; | 696 | const u64 addr = start + offset; |
687 | struct disasm_line *next; | 697 | struct disasm_line *next; |
688 | 698 | ||
689 | next = disasm__get_next_ip_line(¬es->src->source, dl); | 699 | next = disasm__get_next_ip_line(¬es->src->source, dl); |
690 | 700 | ||
691 | if (perf_evsel__is_group_event(evsel)) { | 701 | if (perf_evsel__is_group_event(evsel)) { |
692 | nr_percent = evsel->nr_members; | 702 | nr_percent = evsel->nr_members; |
693 | ppercents = calloc(nr_percent, sizeof(double)); | 703 | ppercents = calloc(nr_percent, sizeof(double)); |
694 | if (ppercents == NULL) | 704 | if (ppercents == NULL) |
695 | return -1; | 705 | return -1; |
696 | } | 706 | } |
697 | 707 | ||
698 | for (i = 0; i < nr_percent; i++) { | 708 | for (i = 0; i < nr_percent; i++) { |
699 | percent = disasm__calc_percent(notes, | 709 | percent = disasm__calc_percent(notes, |
700 | notes->src->lines ? i : evsel->idx + i, | 710 | notes->src->lines ? i : evsel->idx + i, |
701 | offset, | 711 | offset, |
702 | next ? next->offset : (s64) len, | 712 | next ? next->offset : (s64) len, |
703 | &path); | 713 | &path); |
704 | 714 | ||
705 | ppercents[i] = percent; | 715 | ppercents[i] = percent; |
706 | if (percent > max_percent) | 716 | if (percent > max_percent) |
707 | max_percent = percent; | 717 | max_percent = percent; |
708 | } | 718 | } |
709 | 719 | ||
710 | if (max_percent < min_pcnt) | 720 | if (max_percent < min_pcnt) |
711 | return -1; | 721 | return -1; |
712 | 722 | ||
713 | if (max_lines && printed >= max_lines) | 723 | if (max_lines && printed >= max_lines) |
714 | return 1; | 724 | return 1; |
715 | 725 | ||
716 | if (queue != NULL) { | 726 | if (queue != NULL) { |
717 | list_for_each_entry_from(queue, ¬es->src->source, node) { | 727 | list_for_each_entry_from(queue, ¬es->src->source, node) { |
718 | if (queue == dl) | 728 | if (queue == dl) |
719 | break; | 729 | break; |
720 | disasm_line__print(queue, sym, start, evsel, len, | 730 | disasm_line__print(queue, sym, start, evsel, len, |
721 | 0, 0, 1, NULL); | 731 | 0, 0, 1, NULL); |
722 | } | 732 | } |
723 | } | 733 | } |
724 | 734 | ||
725 | color = get_percent_color(max_percent); | 735 | color = get_percent_color(max_percent); |
726 | 736 | ||
727 | /* | 737 | /* |
728 | * Also color the filename and line if needed, with | 738 | * Also color the filename and line if needed, with |
729 | * the same color than the percentage. Don't print it | 739 | * the same color than the percentage. Don't print it |
730 | * twice for close colored addr with the same filename:line | 740 | * twice for close colored addr with the same filename:line |
731 | */ | 741 | */ |
732 | if (path) { | 742 | if (path) { |
733 | if (!prev_line || strcmp(prev_line, path) | 743 | if (!prev_line || strcmp(prev_line, path) |
734 | || color != prev_color) { | 744 | || color != prev_color) { |
735 | color_fprintf(stdout, color, " %s", path); | 745 | color_fprintf(stdout, color, " %s", path); |
736 | prev_line = path; | 746 | prev_line = path; |
737 | prev_color = color; | 747 | prev_color = color; |
738 | } | 748 | } |
739 | } | 749 | } |
740 | 750 | ||
741 | for (i = 0; i < nr_percent; i++) { | 751 | for (i = 0; i < nr_percent; i++) { |
742 | percent = ppercents[i]; | 752 | percent = ppercents[i]; |
743 | color = get_percent_color(percent); | 753 | color = get_percent_color(percent); |
744 | color_fprintf(stdout, color, " %7.2f", percent); | 754 | color_fprintf(stdout, color, " %7.2f", percent); |
745 | } | 755 | } |
746 | 756 | ||
747 | printf(" : "); | 757 | printf(" : "); |
748 | color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); | 758 | color_fprintf(stdout, PERF_COLOR_MAGENTA, " %" PRIx64 ":", addr); |
749 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); | 759 | color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", dl->line); |
750 | 760 | ||
751 | if (ppercents != &percent) | 761 | if (ppercents != &percent) |
752 | free(ppercents); | 762 | free(ppercents); |
753 | 763 | ||
754 | } else if (max_lines && printed >= max_lines) | 764 | } else if (max_lines && printed >= max_lines) |
755 | return 1; | 765 | return 1; |
756 | else { | 766 | else { |
757 | int width = 8; | 767 | int width = 8; |
758 | 768 | ||
759 | if (queue) | 769 | if (queue) |
760 | return -1; | 770 | return -1; |
761 | 771 | ||
762 | if (perf_evsel__is_group_event(evsel)) | 772 | if (perf_evsel__is_group_event(evsel)) |
763 | width *= evsel->nr_members; | 773 | width *= evsel->nr_members; |
764 | 774 | ||
765 | if (!*dl->line) | 775 | if (!*dl->line) |
766 | printf(" %*s:\n", width, " "); | 776 | printf(" %*s:\n", width, " "); |
767 | else | 777 | else |
768 | printf(" %*s: %s\n", width, " ", dl->line); | 778 | printf(" %*s: %s\n", width, " ", dl->line); |
769 | } | 779 | } |
770 | 780 | ||
771 | return 0; | 781 | return 0; |
772 | } | 782 | } |
773 | 783 | ||
774 | /* | 784 | /* |
775 | * symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw) | 785 | * symbol__parse_objdump_line() parses objdump output (with -d --no-show-raw) |
776 | * which looks like following | 786 | * which looks like following |
777 | * | 787 | * |
778 | * 0000000000415500 <_init>: | 788 | * 0000000000415500 <_init>: |
779 | * 415500: sub $0x8,%rsp | 789 | * 415500: sub $0x8,%rsp |
780 | * 415504: mov 0x2f5ad5(%rip),%rax # 70afe0 <_DYNAMIC+0x2f8> | 790 | * 415504: mov 0x2f5ad5(%rip),%rax # 70afe0 <_DYNAMIC+0x2f8> |
781 | * 41550b: test %rax,%rax | 791 | * 41550b: test %rax,%rax |
782 | * 41550e: je 415515 <_init+0x15> | 792 | * 41550e: je 415515 <_init+0x15> |
783 | * 415510: callq 416e70 <__gmon_start__@plt> | 793 | * 415510: callq 416e70 <__gmon_start__@plt> |
784 | * 415515: add $0x8,%rsp | 794 | * 415515: add $0x8,%rsp |
785 | * 415519: retq | 795 | * 415519: retq |
786 | * | 796 | * |
787 | * it will be parsed and saved into struct disasm_line as | 797 | * it will be parsed and saved into struct disasm_line as |
788 | * <offset> <name> <ops.raw> | 798 | * <offset> <name> <ops.raw> |
789 | * | 799 | * |
790 | * The offset will be a relative offset from the start of the symbol and -1 | 800 | * The offset will be a relative offset from the start of the symbol and -1 |
791 | * means that it's not a disassembly line so should be treated differently. | 801 | * means that it's not a disassembly line so should be treated differently. |
792 | * The ops.raw part will be parsed further according to type of the instruction. | 802 | * The ops.raw part will be parsed further according to type of the instruction. |
793 | */ | 803 | */ |
794 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | 804 | static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, |
795 | FILE *file, size_t privsize, | 805 | FILE *file, size_t privsize, |
796 | int *line_nr) | 806 | int *line_nr) |
797 | { | 807 | { |
798 | struct annotation *notes = symbol__annotation(sym); | 808 | struct annotation *notes = symbol__annotation(sym); |
799 | struct disasm_line *dl; | 809 | struct disasm_line *dl; |
800 | char *line = NULL, *parsed_line, *tmp, *tmp2, *c; | 810 | char *line = NULL, *parsed_line, *tmp, *tmp2, *c; |
801 | size_t line_len; | 811 | size_t line_len; |
802 | s64 line_ip, offset = -1; | 812 | s64 line_ip, offset = -1; |
803 | regmatch_t match[2]; | 813 | regmatch_t match[2]; |
804 | 814 | ||
805 | if (getline(&line, &line_len, file) < 0) | 815 | if (getline(&line, &line_len, file) < 0) |
806 | return -1; | 816 | return -1; |
807 | 817 | ||
808 | if (!line) | 818 | if (!line) |
809 | return -1; | 819 | return -1; |
810 | 820 | ||
811 | while (line_len != 0 && isspace(line[line_len - 1])) | 821 | while (line_len != 0 && isspace(line[line_len - 1])) |
812 | line[--line_len] = '\0'; | 822 | line[--line_len] = '\0'; |
813 | 823 | ||
814 | c = strchr(line, '\n'); | 824 | c = strchr(line, '\n'); |
815 | if (c) | 825 | if (c) |
816 | *c = 0; | 826 | *c = 0; |
817 | 827 | ||
818 | line_ip = -1; | 828 | line_ip = -1; |
819 | parsed_line = line; | 829 | parsed_line = line; |
820 | 830 | ||
821 | /* /filename:linenr ? Save line number and ignore. */ | 831 | /* /filename:linenr ? Save line number and ignore. */ |
822 | if (regexec(&file_lineno, line, 2, match, 0) == 0) { | 832 | if (regexec(&file_lineno, line, 2, match, 0) == 0) { |
823 | *line_nr = atoi(line + match[1].rm_so); | 833 | *line_nr = atoi(line + match[1].rm_so); |
824 | return 0; | 834 | return 0; |
825 | } | 835 | } |
826 | 836 | ||
827 | /* | 837 | /* |
828 | * Strip leading spaces: | 838 | * Strip leading spaces: |
829 | */ | 839 | */ |
830 | tmp = line; | 840 | tmp = line; |
831 | while (*tmp) { | 841 | while (*tmp) { |
832 | if (*tmp != ' ') | 842 | if (*tmp != ' ') |
833 | break; | 843 | break; |
834 | tmp++; | 844 | tmp++; |
835 | } | 845 | } |
836 | 846 | ||
837 | if (*tmp) { | 847 | if (*tmp) { |
838 | /* | 848 | /* |
839 | * Parse hexa addresses followed by ':' | 849 | * Parse hexa addresses followed by ':' |
840 | */ | 850 | */ |
841 | line_ip = strtoull(tmp, &tmp2, 16); | 851 | line_ip = strtoull(tmp, &tmp2, 16); |
842 | if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') | 852 | if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0') |
843 | line_ip = -1; | 853 | line_ip = -1; |
844 | } | 854 | } |
845 | 855 | ||
846 | if (line_ip != -1) { | 856 | if (line_ip != -1) { |
847 | u64 start = map__rip_2objdump(map, sym->start), | 857 | u64 start = map__rip_2objdump(map, sym->start), |
848 | end = map__rip_2objdump(map, sym->end); | 858 | end = map__rip_2objdump(map, sym->end); |
849 | 859 | ||
850 | offset = line_ip - start; | 860 | offset = line_ip - start; |
851 | if ((u64)line_ip < start || (u64)line_ip >= end) | 861 | if ((u64)line_ip < start || (u64)line_ip >= end) |
852 | offset = -1; | 862 | offset = -1; |
853 | else | 863 | else |
854 | parsed_line = tmp2 + 1; | 864 | parsed_line = tmp2 + 1; |
855 | } | 865 | } |
856 | 866 | ||
857 | dl = disasm_line__new(offset, parsed_line, privsize, *line_nr); | 867 | dl = disasm_line__new(offset, parsed_line, privsize, *line_nr); |
858 | free(line); | 868 | free(line); |
859 | (*line_nr)++; | 869 | (*line_nr)++; |
860 | 870 | ||
861 | if (dl == NULL) | 871 | if (dl == NULL) |
862 | return -1; | 872 | return -1; |
863 | 873 | ||
864 | if (dl->ops.target.offset == UINT64_MAX) | 874 | if (dl->ops.target.offset == UINT64_MAX) |
865 | dl->ops.target.offset = dl->ops.target.addr - | 875 | dl->ops.target.offset = dl->ops.target.addr - |
866 | map__rip_2objdump(map, sym->start); | 876 | map__rip_2objdump(map, sym->start); |
867 | 877 | ||
868 | /* kcore has no symbols, so add the call target name */ | 878 | /* kcore has no symbols, so add the call target name */ |
869 | if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { | 879 | if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { |
870 | struct addr_map_symbol target = { | 880 | struct addr_map_symbol target = { |
871 | .map = map, | 881 | .map = map, |
872 | .addr = dl->ops.target.addr, | 882 | .addr = dl->ops.target.addr, |
873 | }; | 883 | }; |
874 | 884 | ||
875 | if (!map_groups__find_ams(&target, NULL) && | 885 | if (!map_groups__find_ams(&target, NULL) && |
876 | target.sym->start == target.al_addr) | 886 | target.sym->start == target.al_addr) |
877 | dl->ops.target.name = strdup(target.sym->name); | 887 | dl->ops.target.name = strdup(target.sym->name); |
878 | } | 888 | } |
879 | 889 | ||
880 | disasm__add(¬es->src->source, dl); | 890 | disasm__add(¬es->src->source, dl); |
881 | 891 | ||
882 | return 0; | 892 | return 0; |
883 | } | 893 | } |
884 | 894 | ||
885 | static __attribute__((constructor)) void symbol__init_regexpr(void) | 895 | static __attribute__((constructor)) void symbol__init_regexpr(void) |
886 | { | 896 | { |
887 | regcomp(&file_lineno, "^/[^:]+:([0-9]+)", REG_EXTENDED); | 897 | regcomp(&file_lineno, "^/[^:]+:([0-9]+)", REG_EXTENDED); |
888 | } | 898 | } |
889 | 899 | ||
890 | static void delete_last_nop(struct symbol *sym) | 900 | static void delete_last_nop(struct symbol *sym) |
891 | { | 901 | { |
892 | struct annotation *notes = symbol__annotation(sym); | 902 | struct annotation *notes = symbol__annotation(sym); |
893 | struct list_head *list = ¬es->src->source; | 903 | struct list_head *list = ¬es->src->source; |
894 | struct disasm_line *dl; | 904 | struct disasm_line *dl; |
895 | 905 | ||
896 | while (!list_empty(list)) { | 906 | while (!list_empty(list)) { |
897 | dl = list_entry(list->prev, struct disasm_line, node); | 907 | dl = list_entry(list->prev, struct disasm_line, node); |
898 | 908 | ||
899 | if (dl->ins && dl->ins->ops) { | 909 | if (dl->ins && dl->ins->ops) { |
900 | if (dl->ins->ops != &nop_ops) | 910 | if (dl->ins->ops != &nop_ops) |
901 | return; | 911 | return; |
902 | } else { | 912 | } else { |
903 | if (!strstr(dl->line, " nop ") && | 913 | if (!strstr(dl->line, " nop ") && |
904 | !strstr(dl->line, " nopl ") && | 914 | !strstr(dl->line, " nopl ") && |
905 | !strstr(dl->line, " nopw ")) | 915 | !strstr(dl->line, " nopw ")) |
906 | return; | 916 | return; |
907 | } | 917 | } |
908 | 918 | ||
909 | list_del(&dl->node); | 919 | list_del(&dl->node); |
910 | disasm_line__free(dl); | 920 | disasm_line__free(dl); |
911 | } | 921 | } |
912 | } | 922 | } |
913 | 923 | ||
914 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | 924 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) |
915 | { | 925 | { |
916 | struct dso *dso = map->dso; | 926 | struct dso *dso = map->dso; |
917 | char *filename = dso__build_id_filename(dso, NULL, 0); | 927 | char *filename = dso__build_id_filename(dso, NULL, 0); |
918 | bool free_filename = true; | 928 | bool free_filename = true; |
919 | char command[PATH_MAX * 2]; | 929 | char command[PATH_MAX * 2]; |
920 | FILE *file; | 930 | FILE *file; |
921 | int err = 0; | 931 | int err = 0; |
922 | char symfs_filename[PATH_MAX]; | 932 | char symfs_filename[PATH_MAX]; |
923 | struct kcore_extract kce; | 933 | struct kcore_extract kce; |
924 | bool delete_extract = false; | 934 | bool delete_extract = false; |
925 | int lineno = 0; | 935 | int lineno = 0; |
926 | 936 | ||
927 | if (filename) | 937 | if (filename) |
928 | symbol__join_symfs(symfs_filename, filename); | 938 | symbol__join_symfs(symfs_filename, filename); |
929 | 939 | ||
930 | if (filename == NULL) { | 940 | if (filename == NULL) { |
931 | if (dso->has_build_id) { | 941 | if (dso->has_build_id) { |
932 | pr_err("Can't annotate %s: not enough memory\n", | 942 | pr_err("Can't annotate %s: not enough memory\n", |
933 | sym->name); | 943 | sym->name); |
934 | return -ENOMEM; | 944 | return -ENOMEM; |
935 | } | 945 | } |
936 | goto fallback; | 946 | goto fallback; |
937 | } else if (dso__is_kcore(dso)) { | 947 | } else if (dso__is_kcore(dso)) { |
938 | goto fallback; | 948 | goto fallback; |
939 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || | 949 | } else if (readlink(symfs_filename, command, sizeof(command)) < 0 || |
940 | strstr(command, "[kernel.kallsyms]") || | 950 | strstr(command, "[kernel.kallsyms]") || |
941 | access(symfs_filename, R_OK)) { | 951 | access(symfs_filename, R_OK)) { |
942 | free(filename); | 952 | free(filename); |
943 | fallback: | 953 | fallback: |
944 | /* | 954 | /* |
945 | * If we don't have build-ids or the build-id file isn't in the | 955 | * If we don't have build-ids or the build-id file isn't in the |
946 | * cache, or is just a kallsyms file, well, lets hope that this | 956 | * cache, or is just a kallsyms file, well, lets hope that this |
947 | * DSO is the same as when 'perf record' ran. | 957 | * DSO is the same as when 'perf record' ran. |
948 | */ | 958 | */ |
949 | filename = (char *)dso->long_name; | 959 | filename = (char *)dso->long_name; |
950 | symbol__join_symfs(symfs_filename, filename); | 960 | symbol__join_symfs(symfs_filename, filename); |
951 | free_filename = false; | 961 | free_filename = false; |
952 | } | 962 | } |
953 | 963 | ||
954 | if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && | 964 | if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && |
955 | !dso__is_kcore(dso)) { | 965 | !dso__is_kcore(dso)) { |
956 | char bf[BUILD_ID_SIZE * 2 + 16] = " with build id "; | 966 | char bf[BUILD_ID_SIZE * 2 + 16] = " with build id "; |
957 | char *build_id_msg = NULL; | 967 | char *build_id_msg = NULL; |
958 | 968 | ||
959 | if (dso->annotate_warned) | 969 | if (dso->annotate_warned) |
960 | goto out_free_filename; | 970 | goto out_free_filename; |
961 | 971 | ||
962 | if (dso->has_build_id) { | 972 | if (dso->has_build_id) { |
963 | build_id__sprintf(dso->build_id, | 973 | build_id__sprintf(dso->build_id, |
964 | sizeof(dso->build_id), bf + 15); | 974 | sizeof(dso->build_id), bf + 15); |
965 | build_id_msg = bf; | 975 | build_id_msg = bf; |
966 | } | 976 | } |
967 | err = -ENOENT; | 977 | err = -ENOENT; |
968 | dso->annotate_warned = 1; | 978 | dso->annotate_warned = 1; |
969 | pr_err("Can't annotate %s:\n\n" | 979 | pr_err("Can't annotate %s:\n\n" |
970 | "No vmlinux file%s\nwas found in the path.\n\n" | 980 | "No vmlinux file%s\nwas found in the path.\n\n" |
971 | "Please use:\n\n" | 981 | "Please use:\n\n" |
972 | " perf buildid-cache -vu vmlinux\n\n" | 982 | " perf buildid-cache -vu vmlinux\n\n" |
973 | "or:\n\n" | 983 | "or:\n\n" |
974 | " --vmlinux vmlinux\n", | 984 | " --vmlinux vmlinux\n", |
975 | sym->name, build_id_msg ?: ""); | 985 | sym->name, build_id_msg ?: ""); |
976 | goto out_free_filename; | 986 | goto out_free_filename; |
977 | } | 987 | } |
978 | 988 | ||
979 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, | 989 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, |
980 | filename, sym->name, map->unmap_ip(map, sym->start), | 990 | filename, sym->name, map->unmap_ip(map, sym->start), |
981 | map->unmap_ip(map, sym->end)); | 991 | map->unmap_ip(map, sym->end)); |
982 | 992 | ||
983 | pr_debug("annotating [%p] %30s : [%p] %30s\n", | 993 | pr_debug("annotating [%p] %30s : [%p] %30s\n", |
984 | dso, dso->long_name, sym, sym->name); | 994 | dso, dso->long_name, sym, sym->name); |
985 | 995 | ||
986 | if (dso__is_kcore(dso)) { | 996 | if (dso__is_kcore(dso)) { |
987 | kce.kcore_filename = symfs_filename; | 997 | kce.kcore_filename = symfs_filename; |
988 | kce.addr = map__rip_2objdump(map, sym->start); | 998 | kce.addr = map__rip_2objdump(map, sym->start); |
989 | kce.offs = sym->start; | 999 | kce.offs = sym->start; |
990 | kce.len = sym->end - sym->start; | 1000 | kce.len = sym->end - sym->start; |
991 | if (!kcore_extract__create(&kce)) { | 1001 | if (!kcore_extract__create(&kce)) { |
992 | delete_extract = true; | 1002 | delete_extract = true; |
993 | strlcpy(symfs_filename, kce.extract_filename, | 1003 | strlcpy(symfs_filename, kce.extract_filename, |
994 | sizeof(symfs_filename)); | 1004 | sizeof(symfs_filename)); |
995 | if (free_filename) { | 1005 | if (free_filename) { |
996 | free(filename); | 1006 | free(filename); |
997 | free_filename = false; | 1007 | free_filename = false; |
998 | } | 1008 | } |
999 | filename = symfs_filename; | 1009 | filename = symfs_filename; |
1000 | } | 1010 | } |
1001 | } | 1011 | } |
1002 | 1012 | ||
1003 | snprintf(command, sizeof(command), | 1013 | snprintf(command, sizeof(command), |
1004 | "%s %s%s --start-address=0x%016" PRIx64 | 1014 | "%s %s%s --start-address=0x%016" PRIx64 |
1005 | " --stop-address=0x%016" PRIx64 | 1015 | " --stop-address=0x%016" PRIx64 |
1006 | " -l -d %s %s -C %s 2>/dev/null|grep -v %s|expand", | 1016 | " -l -d %s %s -C %s 2>/dev/null|grep -v %s|expand", |
1007 | objdump_path ? objdump_path : "objdump", | 1017 | objdump_path ? objdump_path : "objdump", |
1008 | disassembler_style ? "-M " : "", | 1018 | disassembler_style ? "-M " : "", |
1009 | disassembler_style ? disassembler_style : "", | 1019 | disassembler_style ? disassembler_style : "", |
1010 | map__rip_2objdump(map, sym->start), | 1020 | map__rip_2objdump(map, sym->start), |
1011 | map__rip_2objdump(map, sym->end), | 1021 | map__rip_2objdump(map, sym->end), |
1012 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", | 1022 | symbol_conf.annotate_asm_raw ? "" : "--no-show-raw", |
1013 | symbol_conf.annotate_src ? "-S" : "", | 1023 | symbol_conf.annotate_src ? "-S" : "", |
1014 | symfs_filename, filename); | 1024 | symfs_filename, filename); |
1015 | 1025 | ||
1016 | pr_debug("Executing: %s\n", command); | 1026 | pr_debug("Executing: %s\n", command); |
1017 | 1027 | ||
1018 | file = popen(command, "r"); | 1028 | file = popen(command, "r"); |
1019 | if (!file) | 1029 | if (!file) |
1020 | goto out_free_filename; | 1030 | goto out_free_filename; |
1021 | 1031 | ||
1022 | while (!feof(file)) | 1032 | while (!feof(file)) |
1023 | if (symbol__parse_objdump_line(sym, map, file, privsize, | 1033 | if (symbol__parse_objdump_line(sym, map, file, privsize, |
1024 | &lineno) < 0) | 1034 | &lineno) < 0) |
1025 | break; | 1035 | break; |
1026 | 1036 | ||
1027 | /* | 1037 | /* |
1028 | * kallsyms does not have symbol sizes so there may a nop at the end. | 1038 | * kallsyms does not have symbol sizes so there may a nop at the end. |
1029 | * Remove it. | 1039 | * Remove it. |
1030 | */ | 1040 | */ |
1031 | if (dso__is_kcore(dso)) | 1041 | if (dso__is_kcore(dso)) |
1032 | delete_last_nop(sym); | 1042 | delete_last_nop(sym); |
1033 | 1043 | ||
1034 | pclose(file); | 1044 | pclose(file); |
1035 | out_free_filename: | 1045 | out_free_filename: |
1036 | if (delete_extract) | 1046 | if (delete_extract) |
1037 | kcore_extract__delete(&kce); | 1047 | kcore_extract__delete(&kce); |
1038 | if (free_filename) | 1048 | if (free_filename) |
1039 | free(filename); | 1049 | free(filename); |
1040 | return err; | 1050 | return err; |
1041 | } | 1051 | } |
1042 | 1052 | ||
1043 | static void insert_source_line(struct rb_root *root, struct source_line *src_line) | 1053 | static void insert_source_line(struct rb_root *root, struct source_line *src_line) |
1044 | { | 1054 | { |
1045 | struct source_line *iter; | 1055 | struct source_line *iter; |
1046 | struct rb_node **p = &root->rb_node; | 1056 | struct rb_node **p = &root->rb_node; |
1047 | struct rb_node *parent = NULL; | 1057 | struct rb_node *parent = NULL; |
1048 | int i, ret; | 1058 | int i, ret; |
1049 | 1059 | ||
1050 | while (*p != NULL) { | 1060 | while (*p != NULL) { |
1051 | parent = *p; | 1061 | parent = *p; |
1052 | iter = rb_entry(parent, struct source_line, node); | 1062 | iter = rb_entry(parent, struct source_line, node); |
1053 | 1063 | ||
1054 | ret = strcmp(iter->path, src_line->path); | 1064 | ret = strcmp(iter->path, src_line->path); |
1055 | if (ret == 0) { | 1065 | if (ret == 0) { |
1056 | for (i = 0; i < src_line->nr_pcnt; i++) | 1066 | for (i = 0; i < src_line->nr_pcnt; i++) |
1057 | iter->p[i].percent_sum += src_line->p[i].percent; | 1067 | iter->p[i].percent_sum += src_line->p[i].percent; |
1058 | return; | 1068 | return; |
1059 | } | 1069 | } |
1060 | 1070 | ||
1061 | if (ret < 0) | 1071 | if (ret < 0) |
1062 | p = &(*p)->rb_left; | 1072 | p = &(*p)->rb_left; |
1063 | else | 1073 | else |
1064 | p = &(*p)->rb_right; | 1074 | p = &(*p)->rb_right; |
1065 | } | 1075 | } |
1066 | 1076 | ||
1067 | for (i = 0; i < src_line->nr_pcnt; i++) | 1077 | for (i = 0; i < src_line->nr_pcnt; i++) |
1068 | src_line->p[i].percent_sum = src_line->p[i].percent; | 1078 | src_line->p[i].percent_sum = src_line->p[i].percent; |
1069 | 1079 | ||
1070 | rb_link_node(&src_line->node, parent, p); | 1080 | rb_link_node(&src_line->node, parent, p); |
1071 | rb_insert_color(&src_line->node, root); | 1081 | rb_insert_color(&src_line->node, root); |
1072 | } | 1082 | } |
1073 | 1083 | ||
1074 | static int cmp_source_line(struct source_line *a, struct source_line *b) | 1084 | static int cmp_source_line(struct source_line *a, struct source_line *b) |
1075 | { | 1085 | { |
1076 | int i; | 1086 | int i; |
1077 | 1087 | ||
1078 | for (i = 0; i < a->nr_pcnt; i++) { | 1088 | for (i = 0; i < a->nr_pcnt; i++) { |
1079 | if (a->p[i].percent_sum == b->p[i].percent_sum) | 1089 | if (a->p[i].percent_sum == b->p[i].percent_sum) |
1080 | continue; | 1090 | continue; |
1081 | return a->p[i].percent_sum > b->p[i].percent_sum; | 1091 | return a->p[i].percent_sum > b->p[i].percent_sum; |
1082 | } | 1092 | } |
1083 | 1093 | ||
1084 | return 0; | 1094 | return 0; |
1085 | } | 1095 | } |
1086 | 1096 | ||
1087 | static void __resort_source_line(struct rb_root *root, struct source_line *src_line) | 1097 | static void __resort_source_line(struct rb_root *root, struct source_line *src_line) |
1088 | { | 1098 | { |
1089 | struct source_line *iter; | 1099 | struct source_line *iter; |
1090 | struct rb_node **p = &root->rb_node; | 1100 | struct rb_node **p = &root->rb_node; |
1091 | struct rb_node *parent = NULL; | 1101 | struct rb_node *parent = NULL; |
1092 | 1102 | ||
1093 | while (*p != NULL) { | 1103 | while (*p != NULL) { |
1094 | parent = *p; | 1104 | parent = *p; |
1095 | iter = rb_entry(parent, struct source_line, node); | 1105 | iter = rb_entry(parent, struct source_line, node); |
1096 | 1106 | ||
1097 | if (cmp_source_line(src_line, iter)) | 1107 | if (cmp_source_line(src_line, iter)) |
1098 | p = &(*p)->rb_left; | 1108 | p = &(*p)->rb_left; |
1099 | else | 1109 | else |
1100 | p = &(*p)->rb_right; | 1110 | p = &(*p)->rb_right; |
1101 | } | 1111 | } |
1102 | 1112 | ||
1103 | rb_link_node(&src_line->node, parent, p); | 1113 | rb_link_node(&src_line->node, parent, p); |
1104 | rb_insert_color(&src_line->node, root); | 1114 | rb_insert_color(&src_line->node, root); |
1105 | } | 1115 | } |
1106 | 1116 | ||
1107 | static void resort_source_line(struct rb_root *dest_root, struct rb_root *src_root) | 1117 | static void resort_source_line(struct rb_root *dest_root, struct rb_root *src_root) |
1108 | { | 1118 | { |
1109 | struct source_line *src_line; | 1119 | struct source_line *src_line; |
1110 | struct rb_node *node; | 1120 | struct rb_node *node; |
1111 | 1121 | ||
1112 | node = rb_first(src_root); | 1122 | node = rb_first(src_root); |
1113 | while (node) { | 1123 | while (node) { |
1114 | struct rb_node *next; | 1124 | struct rb_node *next; |
1115 | 1125 | ||
1116 | src_line = rb_entry(node, struct source_line, node); | 1126 | src_line = rb_entry(node, struct source_line, node); |
1117 | next = rb_next(node); | 1127 | next = rb_next(node); |
1118 | rb_erase(node, src_root); | 1128 | rb_erase(node, src_root); |
1119 | 1129 | ||
1120 | __resort_source_line(dest_root, src_line); | 1130 | __resort_source_line(dest_root, src_line); |
1121 | node = next; | 1131 | node = next; |
1122 | } | 1132 | } |
1123 | } | 1133 | } |
1124 | 1134 | ||
1125 | static void symbol__free_source_line(struct symbol *sym, int len) | 1135 | static void symbol__free_source_line(struct symbol *sym, int len) |
1126 | { | 1136 | { |
1127 | struct annotation *notes = symbol__annotation(sym); | 1137 | struct annotation *notes = symbol__annotation(sym); |
1128 | struct source_line *src_line = notes->src->lines; | 1138 | struct source_line *src_line = notes->src->lines; |
1129 | size_t sizeof_src_line; | 1139 | size_t sizeof_src_line; |
1130 | int i; | 1140 | int i; |
1131 | 1141 | ||
1132 | sizeof_src_line = sizeof(*src_line) + | 1142 | sizeof_src_line = sizeof(*src_line) + |
1133 | (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); | 1143 | (sizeof(src_line->p) * (src_line->nr_pcnt - 1)); |
1134 | 1144 | ||
1135 | for (i = 0; i < len; i++) { | 1145 | for (i = 0; i < len; i++) { |
1136 | free_srcline(src_line->path); | 1146 | free_srcline(src_line->path); |
1137 | src_line = (void *)src_line + sizeof_src_line; | 1147 | src_line = (void *)src_line + sizeof_src_line; |
1138 | } | 1148 | } |
1139 | 1149 | ||
1140 | zfree(¬es->src->lines); | 1150 | zfree(¬es->src->lines); |
1141 | } | 1151 | } |
1142 | 1152 | ||
1143 | /* Get the filename:line for the colored entries */ | 1153 | /* Get the filename:line for the colored entries */ |
1144 | static int symbol__get_source_line(struct symbol *sym, struct map *map, | 1154 | static int symbol__get_source_line(struct symbol *sym, struct map *map, |
1145 | struct perf_evsel *evsel, | 1155 | struct perf_evsel *evsel, |
1146 | struct rb_root *root, int len) | 1156 | struct rb_root *root, int len) |
1147 | { | 1157 | { |
1148 | u64 start; | 1158 | u64 start; |
1149 | int i, k; | 1159 | int i, k; |
1150 | int evidx = evsel->idx; | 1160 | int evidx = evsel->idx; |
1151 | struct source_line *src_line; | 1161 | struct source_line *src_line; |
1152 | struct annotation *notes = symbol__annotation(sym); | 1162 | struct annotation *notes = symbol__annotation(sym); |
1153 | struct sym_hist *h = annotation__histogram(notes, evidx); | 1163 | struct sym_hist *h = annotation__histogram(notes, evidx); |
1154 | struct rb_root tmp_root = RB_ROOT; | 1164 | struct rb_root tmp_root = RB_ROOT; |
1155 | int nr_pcnt = 1; | 1165 | int nr_pcnt = 1; |
1156 | u64 h_sum = h->sum; | 1166 | u64 h_sum = h->sum; |
1157 | size_t sizeof_src_line = sizeof(struct source_line); | 1167 | size_t sizeof_src_line = sizeof(struct source_line); |
1158 | 1168 | ||
1159 | if (perf_evsel__is_group_event(evsel)) { | 1169 | if (perf_evsel__is_group_event(evsel)) { |
1160 | for (i = 1; i < evsel->nr_members; i++) { | 1170 | for (i = 1; i < evsel->nr_members; i++) { |
1161 | h = annotation__histogram(notes, evidx + i); | 1171 | h = annotation__histogram(notes, evidx + i); |
1162 | h_sum += h->sum; | 1172 | h_sum += h->sum; |
1163 | } | 1173 | } |
1164 | nr_pcnt = evsel->nr_members; | 1174 | nr_pcnt = evsel->nr_members; |
1165 | sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->p); | 1175 | sizeof_src_line += (nr_pcnt - 1) * sizeof(src_line->p); |
1166 | } | 1176 | } |
1167 | 1177 | ||
1168 | if (!h_sum) | 1178 | if (!h_sum) |
1169 | return 0; | 1179 | return 0; |
1170 | 1180 | ||
1171 | src_line = notes->src->lines = calloc(len, sizeof_src_line); | 1181 | src_line = notes->src->lines = calloc(len, sizeof_src_line); |
1172 | if (!notes->src->lines) | 1182 | if (!notes->src->lines) |
1173 | return -1; | 1183 | return -1; |
1174 | 1184 | ||
1175 | start = map__rip_2objdump(map, sym->start); | 1185 | start = map__rip_2objdump(map, sym->start); |
1176 | 1186 | ||
1177 | for (i = 0; i < len; i++) { | 1187 | for (i = 0; i < len; i++) { |
1178 | u64 offset; | 1188 | u64 offset; |
1179 | double percent_max = 0.0; | 1189 | double percent_max = 0.0; |
1180 | 1190 | ||
1181 | src_line->nr_pcnt = nr_pcnt; | 1191 | src_line->nr_pcnt = nr_pcnt; |
1182 | 1192 | ||
1183 | for (k = 0; k < nr_pcnt; k++) { | 1193 | for (k = 0; k < nr_pcnt; k++) { |
1184 | h = annotation__histogram(notes, evidx + k); | 1194 | h = annotation__histogram(notes, evidx + k); |
1185 | src_line->p[k].percent = 100.0 * h->addr[i] / h->sum; | 1195 | src_line->p[k].percent = 100.0 * h->addr[i] / h->sum; |
1186 | 1196 | ||
1187 | if (src_line->p[k].percent > percent_max) | 1197 | if (src_line->p[k].percent > percent_max) |
1188 | percent_max = src_line->p[k].percent; | 1198 | percent_max = src_line->p[k].percent; |
1189 | } | 1199 | } |
1190 | 1200 | ||
1191 | if (percent_max <= 0.5) | 1201 | if (percent_max <= 0.5) |
1192 | goto next; | 1202 | goto next; |
1193 | 1203 | ||
1194 | offset = start + i; | 1204 | offset = start + i; |
1195 | src_line->path = get_srcline(map->dso, offset, NULL, false); | 1205 | src_line->path = get_srcline(map->dso, offset, NULL, false); |
1196 | insert_source_line(&tmp_root, src_line); | 1206 | insert_source_line(&tmp_root, src_line); |
1197 | 1207 | ||
1198 | next: | 1208 | next: |
1199 | src_line = (void *)src_line + sizeof_src_line; | 1209 | src_line = (void *)src_line + sizeof_src_line; |
1200 | } | 1210 | } |
1201 | 1211 | ||
1202 | resort_source_line(root, &tmp_root); | 1212 | resort_source_line(root, &tmp_root); |
1203 | return 0; | 1213 | return 0; |
1204 | } | 1214 | } |
1205 | 1215 | ||
1206 | static void print_summary(struct rb_root *root, const char *filename) | 1216 | static void print_summary(struct rb_root *root, const char *filename) |
1207 | { | 1217 | { |
1208 | struct source_line *src_line; | 1218 | struct source_line *src_line; |
1209 | struct rb_node *node; | 1219 | struct rb_node *node; |
1210 | 1220 | ||
1211 | printf("\nSorted summary for file %s\n", filename); | 1221 | printf("\nSorted summary for file %s\n", filename); |
1212 | printf("----------------------------------------------\n\n"); | 1222 | printf("----------------------------------------------\n\n"); |
1213 | 1223 | ||
1214 | if (RB_EMPTY_ROOT(root)) { | 1224 | if (RB_EMPTY_ROOT(root)) { |
1215 | printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); | 1225 | printf(" Nothing higher than %1.1f%%\n", MIN_GREEN); |
1216 | return; | 1226 | return; |
1217 | } | 1227 | } |
1218 | 1228 | ||
1219 | node = rb_first(root); | 1229 | node = rb_first(root); |
1220 | while (node) { | 1230 | while (node) { |
1221 | double percent, percent_max = 0.0; | 1231 | double percent, percent_max = 0.0; |
1222 | const char *color; | 1232 | const char *color; |
1223 | char *path; | 1233 | char *path; |
1224 | int i; | 1234 | int i; |
1225 | 1235 | ||
1226 | src_line = rb_entry(node, struct source_line, node); | 1236 | src_line = rb_entry(node, struct source_line, node); |
1227 | for (i = 0; i < src_line->nr_pcnt; i++) { | 1237 | for (i = 0; i < src_line->nr_pcnt; i++) { |
1228 | percent = src_line->p[i].percent_sum; | 1238 | percent = src_line->p[i].percent_sum; |
1229 | color = get_percent_color(percent); | 1239 | color = get_percent_color(percent); |
1230 | color_fprintf(stdout, color, " %7.2f", percent); | 1240 | color_fprintf(stdout, color, " %7.2f", percent); |
1231 | 1241 | ||
1232 | if (percent > percent_max) | 1242 | if (percent > percent_max) |
1233 | percent_max = percent; | 1243 | percent_max = percent; |
1234 | } | 1244 | } |
1235 | 1245 | ||
1236 | path = src_line->path; | 1246 | path = src_line->path; |
1237 | color = get_percent_color(percent_max); | 1247 | color = get_percent_color(percent_max); |
1238 | color_fprintf(stdout, color, " %s\n", path); | 1248 | color_fprintf(stdout, color, " %s\n", path); |
1239 | 1249 | ||
1240 | node = rb_next(node); | 1250 | node = rb_next(node); |
1241 | } | 1251 | } |
1242 | } | 1252 | } |
1243 | 1253 | ||
1244 | static void symbol__annotate_hits(struct symbol *sym, struct perf_evsel *evsel) | 1254 | static void symbol__annotate_hits(struct symbol *sym, struct perf_evsel *evsel) |
1245 | { | 1255 | { |
1246 | struct annotation *notes = symbol__annotation(sym); | 1256 | struct annotation *notes = symbol__annotation(sym); |
1247 | struct sym_hist *h = annotation__histogram(notes, evsel->idx); | 1257 | struct sym_hist *h = annotation__histogram(notes, evsel->idx); |
1248 | u64 len = symbol__size(sym), offset; | 1258 | u64 len = symbol__size(sym), offset; |
1249 | 1259 | ||
1250 | for (offset = 0; offset < len; ++offset) | 1260 | for (offset = 0; offset < len; ++offset) |
1251 | if (h->addr[offset] != 0) | 1261 | if (h->addr[offset] != 0) |
1252 | printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, | 1262 | printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2, |
1253 | sym->start + offset, h->addr[offset]); | 1263 | sym->start + offset, h->addr[offset]); |
1254 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); | 1264 | printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum); |
1255 | } | 1265 | } |
1256 | 1266 | ||
1257 | int symbol__annotate_printf(struct symbol *sym, struct map *map, | 1267 | int symbol__annotate_printf(struct symbol *sym, struct map *map, |
1258 | struct perf_evsel *evsel, bool full_paths, | 1268 | struct perf_evsel *evsel, bool full_paths, |
1259 | int min_pcnt, int max_lines, int context) | 1269 | int min_pcnt, int max_lines, int context) |
1260 | { | 1270 | { |
1261 | struct dso *dso = map->dso; | 1271 | struct dso *dso = map->dso; |
1262 | char *filename; | 1272 | char *filename; |
1263 | const char *d_filename; | 1273 | const char *d_filename; |
1264 | const char *evsel_name = perf_evsel__name(evsel); | 1274 | const char *evsel_name = perf_evsel__name(evsel); |
1265 | struct annotation *notes = symbol__annotation(sym); | 1275 | struct annotation *notes = symbol__annotation(sym); |
1266 | struct disasm_line *pos, *queue = NULL; | 1276 | struct disasm_line *pos, *queue = NULL; |
1267 | u64 start = map__rip_2objdump(map, sym->start); | 1277 | u64 start = map__rip_2objdump(map, sym->start); |
1268 | int printed = 2, queue_len = 0; | 1278 | int printed = 2, queue_len = 0; |
1269 | int more = 0; | 1279 | int more = 0; |
1270 | u64 len; | 1280 | u64 len; |
1271 | int width = 8; | 1281 | int width = 8; |
1272 | int namelen, evsel_name_len, graph_dotted_len; | 1282 | int namelen, evsel_name_len, graph_dotted_len; |
1273 | 1283 | ||
1274 | filename = strdup(dso->long_name); | 1284 | filename = strdup(dso->long_name); |
1275 | if (!filename) | 1285 | if (!filename) |
1276 | return -ENOMEM; | 1286 | return -ENOMEM; |
1277 | 1287 | ||
1278 | if (full_paths) | 1288 | if (full_paths) |
1279 | d_filename = filename; | 1289 | d_filename = filename; |
1280 | else | 1290 | else |
1281 | d_filename = basename(filename); | 1291 | d_filename = basename(filename); |
1282 | 1292 | ||
1283 | len = symbol__size(sym); | 1293 | len = symbol__size(sym); |
1284 | namelen = strlen(d_filename); | 1294 | namelen = strlen(d_filename); |
1285 | evsel_name_len = strlen(evsel_name); | 1295 | evsel_name_len = strlen(evsel_name); |
1286 | 1296 | ||
1287 | if (perf_evsel__is_group_event(evsel)) | 1297 | if (perf_evsel__is_group_event(evsel)) |
1288 | width *= evsel->nr_members; | 1298 | width *= evsel->nr_members; |
1289 | 1299 | ||
1290 | printf(" %-*.*s| Source code & Disassembly of %s for %s\n", | 1300 | printf(" %-*.*s| Source code & Disassembly of %s for %s\n", |
1291 | width, width, "Percent", d_filename, evsel_name); | 1301 | width, width, "Percent", d_filename, evsel_name); |
1292 | 1302 | ||
1293 | graph_dotted_len = width + namelen + evsel_name_len; | 1303 | graph_dotted_len = width + namelen + evsel_name_len; |
1294 | printf("-%-*.*s-----------------------------------------\n", | 1304 | printf("-%-*.*s-----------------------------------------\n", |
1295 | graph_dotted_len, graph_dotted_len, graph_dotted_line); | 1305 | graph_dotted_len, graph_dotted_len, graph_dotted_line); |
1296 | 1306 | ||
1297 | if (verbose) | 1307 | if (verbose) |
1298 | symbol__annotate_hits(sym, evsel); | 1308 | symbol__annotate_hits(sym, evsel); |
1299 | 1309 | ||
1300 | list_for_each_entry(pos, ¬es->src->source, node) { | 1310 | list_for_each_entry(pos, ¬es->src->source, node) { |
1301 | if (context && queue == NULL) { | 1311 | if (context && queue == NULL) { |
1302 | queue = pos; | 1312 | queue = pos; |
1303 | queue_len = 0; | 1313 | queue_len = 0; |
1304 | } | 1314 | } |
1305 | 1315 | ||
1306 | switch (disasm_line__print(pos, sym, start, evsel, len, | 1316 | switch (disasm_line__print(pos, sym, start, evsel, len, |
1307 | min_pcnt, printed, max_lines, | 1317 | min_pcnt, printed, max_lines, |
1308 | queue)) { | 1318 | queue)) { |
1309 | case 0: | 1319 | case 0: |
1310 | ++printed; | 1320 | ++printed; |
1311 | if (context) { | 1321 | if (context) { |
1312 | printed += queue_len; | 1322 | printed += queue_len; |
1313 | queue = NULL; | 1323 | queue = NULL; |
1314 | queue_len = 0; | 1324 | queue_len = 0; |
1315 | } | 1325 | } |
1316 | break; | 1326 | break; |
1317 | case 1: | 1327 | case 1: |
1318 | /* filtered by max_lines */ | 1328 | /* filtered by max_lines */ |
1319 | ++more; | 1329 | ++more; |
1320 | break; | 1330 | break; |
1321 | case -1: | 1331 | case -1: |
1322 | default: | 1332 | default: |
1323 | /* | 1333 | /* |
1324 | * Filtered by min_pcnt or non IP lines when | 1334 | * Filtered by min_pcnt or non IP lines when |
1325 | * context != 0 | 1335 | * context != 0 |
1326 | */ | 1336 | */ |
1327 | if (!context) | 1337 | if (!context) |
1328 | break; | 1338 | break; |
1329 | if (queue_len == context) | 1339 | if (queue_len == context) |
1330 | queue = list_entry(queue->node.next, typeof(*queue), node); | 1340 | queue = list_entry(queue->node.next, typeof(*queue), node); |
1331 | else | 1341 | else |
1332 | ++queue_len; | 1342 | ++queue_len; |
1333 | break; | 1343 | break; |
1334 | } | 1344 | } |
1335 | } | 1345 | } |
1336 | 1346 | ||
1337 | free(filename); | 1347 | free(filename); |
1338 | 1348 | ||
1339 | return more; | 1349 | return more; |
1340 | } | 1350 | } |
1341 | 1351 | ||
1342 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx) | 1352 | void symbol__annotate_zero_histogram(struct symbol *sym, int evidx) |
1343 | { | 1353 | { |
1344 | struct annotation *notes = symbol__annotation(sym); | 1354 | struct annotation *notes = symbol__annotation(sym); |
1345 | struct sym_hist *h = annotation__histogram(notes, evidx); | 1355 | struct sym_hist *h = annotation__histogram(notes, evidx); |
1346 | 1356 | ||
1347 | memset(h, 0, notes->src->sizeof_sym_hist); | 1357 | memset(h, 0, notes->src->sizeof_sym_hist); |
1348 | } | 1358 | } |
1349 | 1359 | ||
1350 | void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) | 1360 | void symbol__annotate_decay_histogram(struct symbol *sym, int evidx) |
1351 | { | 1361 | { |
1352 | struct annotation *notes = symbol__annotation(sym); | 1362 | struct annotation *notes = symbol__annotation(sym); |
1353 | struct sym_hist *h = annotation__histogram(notes, evidx); | 1363 | struct sym_hist *h = annotation__histogram(notes, evidx); |
1354 | int len = symbol__size(sym), offset; | 1364 | int len = symbol__size(sym), offset; |
1355 | 1365 | ||
1356 | h->sum = 0; | 1366 | h->sum = 0; |
1357 | for (offset = 0; offset < len; ++offset) { | 1367 | for (offset = 0; offset < len; ++offset) { |
1358 | h->addr[offset] = h->addr[offset] * 7 / 8; | 1368 | h->addr[offset] = h->addr[offset] * 7 / 8; |
1359 | h->sum += h->addr[offset]; | 1369 | h->sum += h->addr[offset]; |
1360 | } | 1370 | } |
1361 | } | 1371 | } |
1362 | 1372 | ||
1363 | void disasm__purge(struct list_head *head) | 1373 | void disasm__purge(struct list_head *head) |
1364 | { | 1374 | { |
1365 | struct disasm_line *pos, *n; | 1375 | struct disasm_line *pos, *n; |
1366 | 1376 | ||
1367 | list_for_each_entry_safe(pos, n, head, node) { | 1377 | list_for_each_entry_safe(pos, n, head, node) { |
1368 | list_del(&pos->node); | 1378 | list_del(&pos->node); |
1369 | disasm_line__free(pos); | 1379 | disasm_line__free(pos); |
1370 | } | 1380 | } |
1371 | } | 1381 | } |
1372 | 1382 | ||
1373 | static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp) | 1383 | static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp) |
1374 | { | 1384 | { |
1375 | size_t printed; | 1385 | size_t printed; |
1376 | 1386 | ||
1377 | if (dl->offset == -1) | 1387 | if (dl->offset == -1) |
1378 | return fprintf(fp, "%s\n", dl->line); | 1388 | return fprintf(fp, "%s\n", dl->line); |
1379 | 1389 | ||
1380 | printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name); | 1390 | printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name); |
1381 | 1391 | ||
1382 | if (dl->ops.raw[0] != '\0') { | 1392 | if (dl->ops.raw[0] != '\0') { |
1383 | printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ", | 1393 | printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ", |
1384 | dl->ops.raw); | 1394 | dl->ops.raw); |
1385 | } | 1395 | } |
1386 | 1396 | ||
1387 | return printed + fprintf(fp, "\n"); | 1397 | return printed + fprintf(fp, "\n"); |
1388 | } | 1398 | } |
1389 | 1399 | ||
1390 | size_t disasm__fprintf(struct list_head *head, FILE *fp) | 1400 | size_t disasm__fprintf(struct list_head *head, FILE *fp) |
1391 | { | 1401 | { |
1392 | struct disasm_line *pos; | 1402 | struct disasm_line *pos; |
1393 | size_t printed = 0; | 1403 | size_t printed = 0; |
1394 | 1404 | ||
1395 | list_for_each_entry(pos, head, node) | 1405 | list_for_each_entry(pos, head, node) |
1396 | printed += disasm_line__fprintf(pos, fp); | 1406 | printed += disasm_line__fprintf(pos, fp); |
1397 | 1407 | ||
1398 | return printed; | 1408 | return printed; |
1399 | } | 1409 | } |
1400 | 1410 | ||
1401 | int symbol__tty_annotate(struct symbol *sym, struct map *map, | 1411 | int symbol__tty_annotate(struct symbol *sym, struct map *map, |
1402 | struct perf_evsel *evsel, bool print_lines, | 1412 | struct perf_evsel *evsel, bool print_lines, |
1403 | bool full_paths, int min_pcnt, int max_lines) | 1413 | bool full_paths, int min_pcnt, int max_lines) |
1404 | { | 1414 | { |
1405 | struct dso *dso = map->dso; | 1415 | struct dso *dso = map->dso; |
1406 | struct rb_root source_line = RB_ROOT; | 1416 | struct rb_root source_line = RB_ROOT; |
1407 | u64 len; | 1417 | u64 len; |
1408 | 1418 | ||
1409 | if (symbol__annotate(sym, map, 0) < 0) | 1419 | if (symbol__annotate(sym, map, 0) < 0) |
1410 | return -1; | 1420 | return -1; |
1411 | 1421 | ||
1412 | len = symbol__size(sym); | 1422 | len = symbol__size(sym); |
1413 | 1423 | ||
1414 | if (print_lines) { | 1424 | if (print_lines) { |
1415 | symbol__get_source_line(sym, map, evsel, &source_line, len); | 1425 | symbol__get_source_line(sym, map, evsel, &source_line, len); |
1416 | print_summary(&source_line, dso->long_name); | 1426 | print_summary(&source_line, dso->long_name); |
1417 | } | 1427 | } |
1418 | 1428 | ||
1419 | symbol__annotate_printf(sym, map, evsel, full_paths, | 1429 | symbol__annotate_printf(sym, map, evsel, full_paths, |
1420 | min_pcnt, max_lines, 0); | 1430 | min_pcnt, max_lines, 0); |
1421 | if (print_lines) | 1431 | if (print_lines) |
1422 | symbol__free_source_line(sym, len); | 1432 | symbol__free_source_line(sym, len); |
1423 | 1433 | ||
1424 | disasm__purge(&symbol__annotation(sym)->src->source); | 1434 | disasm__purge(&symbol__annotation(sym)->src->source); |
1425 | 1435 | ||
1426 | return 0; | 1436 | return 0; |
1427 | } | 1437 | } |
1428 | 1438 | ||
1429 | int hist_entry__annotate(struct hist_entry *he, size_t privsize) | 1439 | int hist_entry__annotate(struct hist_entry *he, size_t privsize) |
1430 | { | 1440 | { |
1431 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); | 1441 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
1432 | } | 1442 | } |
1433 | 1443 | ||
1434 | bool ui__has_annotation(void) | 1444 | bool ui__has_annotation(void) |
1435 | { | 1445 | { |
1436 | return use_browser == 1 && sort__has_sym; | 1446 | return use_browser == 1 && sort__has_sym; |
1437 | } | 1447 | } |
1438 | 1448 |
tools/perf/util/evlist.c
1 | /* | 1 | /* |
2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> | 2 | * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
3 | * | 3 | * |
4 | * Parts came from builtin-{top,stat,record}.c, see those files for further | 4 | * Parts came from builtin-{top,stat,record}.c, see those files for further |
5 | * copyright notes. | 5 | * copyright notes. |
6 | * | 6 | * |
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 | #include "util.h" | 9 | #include "util.h" |
10 | #include <api/fs/debugfs.h> | 10 | #include <api/fs/debugfs.h> |
11 | #include <api/fs/fs.h> | 11 | #include <api/fs/fs.h> |
12 | #include <poll.h> | 12 | #include <poll.h> |
13 | #include "cpumap.h" | 13 | #include "cpumap.h" |
14 | #include "thread_map.h" | 14 | #include "thread_map.h" |
15 | #include "target.h" | 15 | #include "target.h" |
16 | #include "evlist.h" | 16 | #include "evlist.h" |
17 | #include "evsel.h" | 17 | #include "evsel.h" |
18 | #include "debug.h" | 18 | #include "debug.h" |
19 | #include <unistd.h> | 19 | #include <unistd.h> |
20 | 20 | ||
21 | #include "parse-events.h" | 21 | #include "parse-events.h" |
22 | #include "parse-options.h" | 22 | #include "parse-options.h" |
23 | 23 | ||
24 | #include <sys/mman.h> | 24 | #include <sys/mman.h> |
25 | 25 | ||
26 | #include <linux/bitops.h> | 26 | #include <linux/bitops.h> |
27 | #include <linux/hash.h> | 27 | #include <linux/hash.h> |
28 | #include <linux/log2.h> | 28 | #include <linux/log2.h> |
29 | 29 | ||
30 | static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx); | 30 | static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx); |
31 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx); | 31 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx); |
32 | 32 | ||
33 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 33 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
34 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) | 34 | #define SID(e, x, y) xyarray__entry(e->sample_id, x, y) |
35 | 35 | ||
36 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, | 36 | void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus, |
37 | struct thread_map *threads) | 37 | struct thread_map *threads) |
38 | { | 38 | { |
39 | int i; | 39 | int i; |
40 | 40 | ||
41 | for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) | 41 | for (i = 0; i < PERF_EVLIST__HLIST_SIZE; ++i) |
42 | INIT_HLIST_HEAD(&evlist->heads[i]); | 42 | INIT_HLIST_HEAD(&evlist->heads[i]); |
43 | INIT_LIST_HEAD(&evlist->entries); | 43 | INIT_LIST_HEAD(&evlist->entries); |
44 | perf_evlist__set_maps(evlist, cpus, threads); | 44 | perf_evlist__set_maps(evlist, cpus, threads); |
45 | fdarray__init(&evlist->pollfd, 64); | 45 | fdarray__init(&evlist->pollfd, 64); |
46 | evlist->workload.pid = -1; | 46 | evlist->workload.pid = -1; |
47 | } | 47 | } |
48 | 48 | ||
49 | struct perf_evlist *perf_evlist__new(void) | 49 | struct perf_evlist *perf_evlist__new(void) |
50 | { | 50 | { |
51 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); | 51 | struct perf_evlist *evlist = zalloc(sizeof(*evlist)); |
52 | 52 | ||
53 | if (evlist != NULL) | 53 | if (evlist != NULL) |
54 | perf_evlist__init(evlist, NULL, NULL); | 54 | perf_evlist__init(evlist, NULL, NULL); |
55 | 55 | ||
56 | return evlist; | 56 | return evlist; |
57 | } | 57 | } |
58 | 58 | ||
59 | struct perf_evlist *perf_evlist__new_default(void) | 59 | struct perf_evlist *perf_evlist__new_default(void) |
60 | { | 60 | { |
61 | struct perf_evlist *evlist = perf_evlist__new(); | 61 | struct perf_evlist *evlist = perf_evlist__new(); |
62 | 62 | ||
63 | if (evlist && perf_evlist__add_default(evlist)) { | 63 | if (evlist && perf_evlist__add_default(evlist)) { |
64 | perf_evlist__delete(evlist); | 64 | perf_evlist__delete(evlist); |
65 | evlist = NULL; | 65 | evlist = NULL; |
66 | } | 66 | } |
67 | 67 | ||
68 | return evlist; | 68 | return evlist; |
69 | } | 69 | } |
70 | 70 | ||
71 | /** | 71 | /** |
72 | * perf_evlist__set_id_pos - set the positions of event ids. | 72 | * perf_evlist__set_id_pos - set the positions of event ids. |
73 | * @evlist: selected event list | 73 | * @evlist: selected event list |
74 | * | 74 | * |
75 | * Events with compatible sample types all have the same id_pos | 75 | * Events with compatible sample types all have the same id_pos |
76 | * and is_pos. For convenience, put a copy on evlist. | 76 | * and is_pos. For convenience, put a copy on evlist. |
77 | */ | 77 | */ |
78 | void perf_evlist__set_id_pos(struct perf_evlist *evlist) | 78 | void perf_evlist__set_id_pos(struct perf_evlist *evlist) |
79 | { | 79 | { |
80 | struct perf_evsel *first = perf_evlist__first(evlist); | 80 | struct perf_evsel *first = perf_evlist__first(evlist); |
81 | 81 | ||
82 | evlist->id_pos = first->id_pos; | 82 | evlist->id_pos = first->id_pos; |
83 | evlist->is_pos = first->is_pos; | 83 | evlist->is_pos = first->is_pos; |
84 | } | 84 | } |
85 | 85 | ||
86 | static void perf_evlist__update_id_pos(struct perf_evlist *evlist) | 86 | static void perf_evlist__update_id_pos(struct perf_evlist *evlist) |
87 | { | 87 | { |
88 | struct perf_evsel *evsel; | 88 | struct perf_evsel *evsel; |
89 | 89 | ||
90 | evlist__for_each(evlist, evsel) | 90 | evlist__for_each(evlist, evsel) |
91 | perf_evsel__calc_id_pos(evsel); | 91 | perf_evsel__calc_id_pos(evsel); |
92 | 92 | ||
93 | perf_evlist__set_id_pos(evlist); | 93 | perf_evlist__set_id_pos(evlist); |
94 | } | 94 | } |
95 | 95 | ||
96 | static void perf_evlist__purge(struct perf_evlist *evlist) | 96 | static void perf_evlist__purge(struct perf_evlist *evlist) |
97 | { | 97 | { |
98 | struct perf_evsel *pos, *n; | 98 | struct perf_evsel *pos, *n; |
99 | 99 | ||
100 | evlist__for_each_safe(evlist, n, pos) { | 100 | evlist__for_each_safe(evlist, n, pos) { |
101 | list_del_init(&pos->node); | 101 | list_del_init(&pos->node); |
102 | perf_evsel__delete(pos); | 102 | perf_evsel__delete(pos); |
103 | } | 103 | } |
104 | 104 | ||
105 | evlist->nr_entries = 0; | 105 | evlist->nr_entries = 0; |
106 | } | 106 | } |
107 | 107 | ||
108 | void perf_evlist__exit(struct perf_evlist *evlist) | 108 | void perf_evlist__exit(struct perf_evlist *evlist) |
109 | { | 109 | { |
110 | zfree(&evlist->mmap); | 110 | zfree(&evlist->mmap); |
111 | fdarray__exit(&evlist->pollfd); | 111 | fdarray__exit(&evlist->pollfd); |
112 | } | 112 | } |
113 | 113 | ||
114 | void perf_evlist__delete(struct perf_evlist *evlist) | 114 | void perf_evlist__delete(struct perf_evlist *evlist) |
115 | { | 115 | { |
116 | perf_evlist__munmap(evlist); | 116 | perf_evlist__munmap(evlist); |
117 | perf_evlist__close(evlist); | 117 | perf_evlist__close(evlist); |
118 | cpu_map__delete(evlist->cpus); | 118 | cpu_map__delete(evlist->cpus); |
119 | thread_map__delete(evlist->threads); | 119 | thread_map__delete(evlist->threads); |
120 | evlist->cpus = NULL; | 120 | evlist->cpus = NULL; |
121 | evlist->threads = NULL; | 121 | evlist->threads = NULL; |
122 | perf_evlist__purge(evlist); | 122 | perf_evlist__purge(evlist); |
123 | perf_evlist__exit(evlist); | 123 | perf_evlist__exit(evlist); |
124 | free(evlist); | 124 | free(evlist); |
125 | } | 125 | } |
126 | 126 | ||
127 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | 127 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) |
128 | { | 128 | { |
129 | list_add_tail(&entry->node, &evlist->entries); | 129 | list_add_tail(&entry->node, &evlist->entries); |
130 | entry->idx = evlist->nr_entries; | 130 | entry->idx = evlist->nr_entries; |
131 | entry->tracking = !entry->idx; | 131 | entry->tracking = !entry->idx; |
132 | 132 | ||
133 | if (!evlist->nr_entries++) | 133 | if (!evlist->nr_entries++) |
134 | perf_evlist__set_id_pos(evlist); | 134 | perf_evlist__set_id_pos(evlist); |
135 | } | 135 | } |
136 | 136 | ||
137 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | 137 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, |
138 | struct list_head *list, | 138 | struct list_head *list, |
139 | int nr_entries) | 139 | int nr_entries) |
140 | { | 140 | { |
141 | bool set_id_pos = !evlist->nr_entries; | 141 | bool set_id_pos = !evlist->nr_entries; |
142 | 142 | ||
143 | list_splice_tail(list, &evlist->entries); | 143 | list_splice_tail(list, &evlist->entries); |
144 | evlist->nr_entries += nr_entries; | 144 | evlist->nr_entries += nr_entries; |
145 | if (set_id_pos) | 145 | if (set_id_pos) |
146 | perf_evlist__set_id_pos(evlist); | 146 | perf_evlist__set_id_pos(evlist); |
147 | } | 147 | } |
148 | 148 | ||
149 | void __perf_evlist__set_leader(struct list_head *list) | 149 | void __perf_evlist__set_leader(struct list_head *list) |
150 | { | 150 | { |
151 | struct perf_evsel *evsel, *leader; | 151 | struct perf_evsel *evsel, *leader; |
152 | 152 | ||
153 | leader = list_entry(list->next, struct perf_evsel, node); | 153 | leader = list_entry(list->next, struct perf_evsel, node); |
154 | evsel = list_entry(list->prev, struct perf_evsel, node); | 154 | evsel = list_entry(list->prev, struct perf_evsel, node); |
155 | 155 | ||
156 | leader->nr_members = evsel->idx - leader->idx + 1; | 156 | leader->nr_members = evsel->idx - leader->idx + 1; |
157 | 157 | ||
158 | __evlist__for_each(list, evsel) { | 158 | __evlist__for_each(list, evsel) { |
159 | evsel->leader = leader; | 159 | evsel->leader = leader; |
160 | } | 160 | } |
161 | } | 161 | } |
162 | 162 | ||
163 | void perf_evlist__set_leader(struct perf_evlist *evlist) | 163 | void perf_evlist__set_leader(struct perf_evlist *evlist) |
164 | { | 164 | { |
165 | if (evlist->nr_entries) { | 165 | if (evlist->nr_entries) { |
166 | evlist->nr_groups = evlist->nr_entries > 1 ? 1 : 0; | 166 | evlist->nr_groups = evlist->nr_entries > 1 ? 1 : 0; |
167 | __perf_evlist__set_leader(&evlist->entries); | 167 | __perf_evlist__set_leader(&evlist->entries); |
168 | } | 168 | } |
169 | } | 169 | } |
170 | 170 | ||
171 | int perf_evlist__add_default(struct perf_evlist *evlist) | 171 | int perf_evlist__add_default(struct perf_evlist *evlist) |
172 | { | 172 | { |
173 | struct perf_event_attr attr = { | 173 | struct perf_event_attr attr = { |
174 | .type = PERF_TYPE_HARDWARE, | 174 | .type = PERF_TYPE_HARDWARE, |
175 | .config = PERF_COUNT_HW_CPU_CYCLES, | 175 | .config = PERF_COUNT_HW_CPU_CYCLES, |
176 | }; | 176 | }; |
177 | struct perf_evsel *evsel; | 177 | struct perf_evsel *evsel; |
178 | 178 | ||
179 | event_attr_init(&attr); | 179 | event_attr_init(&attr); |
180 | 180 | ||
181 | evsel = perf_evsel__new(&attr); | 181 | evsel = perf_evsel__new(&attr); |
182 | if (evsel == NULL) | 182 | if (evsel == NULL) |
183 | goto error; | 183 | goto error; |
184 | 184 | ||
185 | /* use strdup() because free(evsel) assumes name is allocated */ | 185 | /* use strdup() because free(evsel) assumes name is allocated */ |
186 | evsel->name = strdup("cycles"); | 186 | evsel->name = strdup("cycles"); |
187 | if (!evsel->name) | 187 | if (!evsel->name) |
188 | goto error_free; | 188 | goto error_free; |
189 | 189 | ||
190 | perf_evlist__add(evlist, evsel); | 190 | perf_evlist__add(evlist, evsel); |
191 | return 0; | 191 | return 0; |
192 | error_free: | 192 | error_free: |
193 | perf_evsel__delete(evsel); | 193 | perf_evsel__delete(evsel); |
194 | error: | 194 | error: |
195 | return -ENOMEM; | 195 | return -ENOMEM; |
196 | } | 196 | } |
197 | 197 | ||
198 | static int perf_evlist__add_attrs(struct perf_evlist *evlist, | 198 | static int perf_evlist__add_attrs(struct perf_evlist *evlist, |
199 | struct perf_event_attr *attrs, size_t nr_attrs) | 199 | struct perf_event_attr *attrs, size_t nr_attrs) |
200 | { | 200 | { |
201 | struct perf_evsel *evsel, *n; | 201 | struct perf_evsel *evsel, *n; |
202 | LIST_HEAD(head); | 202 | LIST_HEAD(head); |
203 | size_t i; | 203 | size_t i; |
204 | 204 | ||
205 | for (i = 0; i < nr_attrs; i++) { | 205 | for (i = 0; i < nr_attrs; i++) { |
206 | evsel = perf_evsel__new_idx(attrs + i, evlist->nr_entries + i); | 206 | evsel = perf_evsel__new_idx(attrs + i, evlist->nr_entries + i); |
207 | if (evsel == NULL) | 207 | if (evsel == NULL) |
208 | goto out_delete_partial_list; | 208 | goto out_delete_partial_list; |
209 | list_add_tail(&evsel->node, &head); | 209 | list_add_tail(&evsel->node, &head); |
210 | } | 210 | } |
211 | 211 | ||
212 | perf_evlist__splice_list_tail(evlist, &head, nr_attrs); | 212 | perf_evlist__splice_list_tail(evlist, &head, nr_attrs); |
213 | 213 | ||
214 | return 0; | 214 | return 0; |
215 | 215 | ||
216 | out_delete_partial_list: | 216 | out_delete_partial_list: |
217 | __evlist__for_each_safe(&head, n, evsel) | 217 | __evlist__for_each_safe(&head, n, evsel) |
218 | perf_evsel__delete(evsel); | 218 | perf_evsel__delete(evsel); |
219 | return -1; | 219 | return -1; |
220 | } | 220 | } |
221 | 221 | ||
222 | int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, | 222 | int __perf_evlist__add_default_attrs(struct perf_evlist *evlist, |
223 | struct perf_event_attr *attrs, size_t nr_attrs) | 223 | struct perf_event_attr *attrs, size_t nr_attrs) |
224 | { | 224 | { |
225 | size_t i; | 225 | size_t i; |
226 | 226 | ||
227 | for (i = 0; i < nr_attrs; i++) | 227 | for (i = 0; i < nr_attrs; i++) |
228 | event_attr_init(attrs + i); | 228 | event_attr_init(attrs + i); |
229 | 229 | ||
230 | return perf_evlist__add_attrs(evlist, attrs, nr_attrs); | 230 | return perf_evlist__add_attrs(evlist, attrs, nr_attrs); |
231 | } | 231 | } |
232 | 232 | ||
233 | struct perf_evsel * | 233 | struct perf_evsel * |
234 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) | 234 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) |
235 | { | 235 | { |
236 | struct perf_evsel *evsel; | 236 | struct perf_evsel *evsel; |
237 | 237 | ||
238 | evlist__for_each(evlist, evsel) { | 238 | evlist__for_each(evlist, evsel) { |
239 | if (evsel->attr.type == PERF_TYPE_TRACEPOINT && | 239 | if (evsel->attr.type == PERF_TYPE_TRACEPOINT && |
240 | (int)evsel->attr.config == id) | 240 | (int)evsel->attr.config == id) |
241 | return evsel; | 241 | return evsel; |
242 | } | 242 | } |
243 | 243 | ||
244 | return NULL; | 244 | return NULL; |
245 | } | 245 | } |
246 | 246 | ||
247 | struct perf_evsel * | 247 | struct perf_evsel * |
248 | perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, | 248 | perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, |
249 | const char *name) | 249 | const char *name) |
250 | { | 250 | { |
251 | struct perf_evsel *evsel; | 251 | struct perf_evsel *evsel; |
252 | 252 | ||
253 | evlist__for_each(evlist, evsel) { | 253 | evlist__for_each(evlist, evsel) { |
254 | if ((evsel->attr.type == PERF_TYPE_TRACEPOINT) && | 254 | if ((evsel->attr.type == PERF_TYPE_TRACEPOINT) && |
255 | (strcmp(evsel->name, name) == 0)) | 255 | (strcmp(evsel->name, name) == 0)) |
256 | return evsel; | 256 | return evsel; |
257 | } | 257 | } |
258 | 258 | ||
259 | return NULL; | 259 | return NULL; |
260 | } | 260 | } |
261 | 261 | ||
262 | int perf_evlist__add_newtp(struct perf_evlist *evlist, | 262 | int perf_evlist__add_newtp(struct perf_evlist *evlist, |
263 | const char *sys, const char *name, void *handler) | 263 | const char *sys, const char *name, void *handler) |
264 | { | 264 | { |
265 | struct perf_evsel *evsel = perf_evsel__newtp(sys, name); | 265 | struct perf_evsel *evsel = perf_evsel__newtp(sys, name); |
266 | 266 | ||
267 | if (evsel == NULL) | 267 | if (evsel == NULL) |
268 | return -1; | 268 | return -1; |
269 | 269 | ||
270 | evsel->handler = handler; | 270 | evsel->handler = handler; |
271 | perf_evlist__add(evlist, evsel); | 271 | perf_evlist__add(evlist, evsel); |
272 | return 0; | 272 | return 0; |
273 | } | 273 | } |
274 | 274 | ||
275 | static int perf_evlist__nr_threads(struct perf_evlist *evlist, | 275 | static int perf_evlist__nr_threads(struct perf_evlist *evlist, |
276 | struct perf_evsel *evsel) | 276 | struct perf_evsel *evsel) |
277 | { | 277 | { |
278 | if (evsel->system_wide) | 278 | if (evsel->system_wide) |
279 | return 1; | 279 | return 1; |
280 | else | 280 | else |
281 | return thread_map__nr(evlist->threads); | 281 | return thread_map__nr(evlist->threads); |
282 | } | 282 | } |
283 | 283 | ||
284 | void perf_evlist__disable(struct perf_evlist *evlist) | 284 | void perf_evlist__disable(struct perf_evlist *evlist) |
285 | { | 285 | { |
286 | int cpu, thread; | 286 | int cpu, thread; |
287 | struct perf_evsel *pos; | 287 | struct perf_evsel *pos; |
288 | int nr_cpus = cpu_map__nr(evlist->cpus); | 288 | int nr_cpus = cpu_map__nr(evlist->cpus); |
289 | int nr_threads; | 289 | int nr_threads; |
290 | 290 | ||
291 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 291 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
292 | evlist__for_each(evlist, pos) { | 292 | evlist__for_each(evlist, pos) { |
293 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) | 293 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) |
294 | continue; | 294 | continue; |
295 | nr_threads = perf_evlist__nr_threads(evlist, pos); | 295 | nr_threads = perf_evlist__nr_threads(evlist, pos); |
296 | for (thread = 0; thread < nr_threads; thread++) | 296 | for (thread = 0; thread < nr_threads; thread++) |
297 | ioctl(FD(pos, cpu, thread), | 297 | ioctl(FD(pos, cpu, thread), |
298 | PERF_EVENT_IOC_DISABLE, 0); | 298 | PERF_EVENT_IOC_DISABLE, 0); |
299 | } | 299 | } |
300 | } | 300 | } |
301 | } | 301 | } |
302 | 302 | ||
303 | void perf_evlist__enable(struct perf_evlist *evlist) | 303 | void perf_evlist__enable(struct perf_evlist *evlist) |
304 | { | 304 | { |
305 | int cpu, thread; | 305 | int cpu, thread; |
306 | struct perf_evsel *pos; | 306 | struct perf_evsel *pos; |
307 | int nr_cpus = cpu_map__nr(evlist->cpus); | 307 | int nr_cpus = cpu_map__nr(evlist->cpus); |
308 | int nr_threads; | 308 | int nr_threads; |
309 | 309 | ||
310 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 310 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
311 | evlist__for_each(evlist, pos) { | 311 | evlist__for_each(evlist, pos) { |
312 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) | 312 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) |
313 | continue; | 313 | continue; |
314 | nr_threads = perf_evlist__nr_threads(evlist, pos); | 314 | nr_threads = perf_evlist__nr_threads(evlist, pos); |
315 | for (thread = 0; thread < nr_threads; thread++) | 315 | for (thread = 0; thread < nr_threads; thread++) |
316 | ioctl(FD(pos, cpu, thread), | 316 | ioctl(FD(pos, cpu, thread), |
317 | PERF_EVENT_IOC_ENABLE, 0); | 317 | PERF_EVENT_IOC_ENABLE, 0); |
318 | } | 318 | } |
319 | } | 319 | } |
320 | } | 320 | } |
321 | 321 | ||
322 | int perf_evlist__disable_event(struct perf_evlist *evlist, | 322 | int perf_evlist__disable_event(struct perf_evlist *evlist, |
323 | struct perf_evsel *evsel) | 323 | struct perf_evsel *evsel) |
324 | { | 324 | { |
325 | int cpu, thread, err; | 325 | int cpu, thread, err; |
326 | int nr_cpus = cpu_map__nr(evlist->cpus); | 326 | int nr_cpus = cpu_map__nr(evlist->cpus); |
327 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | 327 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); |
328 | 328 | ||
329 | if (!evsel->fd) | 329 | if (!evsel->fd) |
330 | return 0; | 330 | return 0; |
331 | 331 | ||
332 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 332 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
333 | for (thread = 0; thread < nr_threads; thread++) { | 333 | for (thread = 0; thread < nr_threads; thread++) { |
334 | err = ioctl(FD(evsel, cpu, thread), | 334 | err = ioctl(FD(evsel, cpu, thread), |
335 | PERF_EVENT_IOC_DISABLE, 0); | 335 | PERF_EVENT_IOC_DISABLE, 0); |
336 | if (err) | 336 | if (err) |
337 | return err; | 337 | return err; |
338 | } | 338 | } |
339 | } | 339 | } |
340 | return 0; | 340 | return 0; |
341 | } | 341 | } |
342 | 342 | ||
343 | int perf_evlist__enable_event(struct perf_evlist *evlist, | 343 | int perf_evlist__enable_event(struct perf_evlist *evlist, |
344 | struct perf_evsel *evsel) | 344 | struct perf_evsel *evsel) |
345 | { | 345 | { |
346 | int cpu, thread, err; | 346 | int cpu, thread, err; |
347 | int nr_cpus = cpu_map__nr(evlist->cpus); | 347 | int nr_cpus = cpu_map__nr(evlist->cpus); |
348 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | 348 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); |
349 | 349 | ||
350 | if (!evsel->fd) | 350 | if (!evsel->fd) |
351 | return -EINVAL; | 351 | return -EINVAL; |
352 | 352 | ||
353 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 353 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
354 | for (thread = 0; thread < nr_threads; thread++) { | 354 | for (thread = 0; thread < nr_threads; thread++) { |
355 | err = ioctl(FD(evsel, cpu, thread), | 355 | err = ioctl(FD(evsel, cpu, thread), |
356 | PERF_EVENT_IOC_ENABLE, 0); | 356 | PERF_EVENT_IOC_ENABLE, 0); |
357 | if (err) | 357 | if (err) |
358 | return err; | 358 | return err; |
359 | } | 359 | } |
360 | } | 360 | } |
361 | return 0; | 361 | return 0; |
362 | } | 362 | } |
363 | 363 | ||
364 | static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, | 364 | static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, |
365 | struct perf_evsel *evsel, int cpu) | 365 | struct perf_evsel *evsel, int cpu) |
366 | { | 366 | { |
367 | int thread, err; | 367 | int thread, err; |
368 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | 368 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); |
369 | 369 | ||
370 | if (!evsel->fd) | 370 | if (!evsel->fd) |
371 | return -EINVAL; | 371 | return -EINVAL; |
372 | 372 | ||
373 | for (thread = 0; thread < nr_threads; thread++) { | 373 | for (thread = 0; thread < nr_threads; thread++) { |
374 | err = ioctl(FD(evsel, cpu, thread), | 374 | err = ioctl(FD(evsel, cpu, thread), |
375 | PERF_EVENT_IOC_ENABLE, 0); | 375 | PERF_EVENT_IOC_ENABLE, 0); |
376 | if (err) | 376 | if (err) |
377 | return err; | 377 | return err; |
378 | } | 378 | } |
379 | return 0; | 379 | return 0; |
380 | } | 380 | } |
381 | 381 | ||
382 | static int perf_evlist__enable_event_thread(struct perf_evlist *evlist, | 382 | static int perf_evlist__enable_event_thread(struct perf_evlist *evlist, |
383 | struct perf_evsel *evsel, | 383 | struct perf_evsel *evsel, |
384 | int thread) | 384 | int thread) |
385 | { | 385 | { |
386 | int cpu, err; | 386 | int cpu, err; |
387 | int nr_cpus = cpu_map__nr(evlist->cpus); | 387 | int nr_cpus = cpu_map__nr(evlist->cpus); |
388 | 388 | ||
389 | if (!evsel->fd) | 389 | if (!evsel->fd) |
390 | return -EINVAL; | 390 | return -EINVAL; |
391 | 391 | ||
392 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 392 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
393 | err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); | 393 | err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); |
394 | if (err) | 394 | if (err) |
395 | return err; | 395 | return err; |
396 | } | 396 | } |
397 | return 0; | 397 | return 0; |
398 | } | 398 | } |
399 | 399 | ||
400 | int perf_evlist__enable_event_idx(struct perf_evlist *evlist, | 400 | int perf_evlist__enable_event_idx(struct perf_evlist *evlist, |
401 | struct perf_evsel *evsel, int idx) | 401 | struct perf_evsel *evsel, int idx) |
402 | { | 402 | { |
403 | bool per_cpu_mmaps = !cpu_map__empty(evlist->cpus); | 403 | bool per_cpu_mmaps = !cpu_map__empty(evlist->cpus); |
404 | 404 | ||
405 | if (per_cpu_mmaps) | 405 | if (per_cpu_mmaps) |
406 | return perf_evlist__enable_event_cpu(evlist, evsel, idx); | 406 | return perf_evlist__enable_event_cpu(evlist, evsel, idx); |
407 | else | 407 | else |
408 | return perf_evlist__enable_event_thread(evlist, evsel, idx); | 408 | return perf_evlist__enable_event_thread(evlist, evsel, idx); |
409 | } | 409 | } |
410 | 410 | ||
411 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 411 | int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
412 | { | 412 | { |
413 | int nr_cpus = cpu_map__nr(evlist->cpus); | 413 | int nr_cpus = cpu_map__nr(evlist->cpus); |
414 | int nr_threads = thread_map__nr(evlist->threads); | 414 | int nr_threads = thread_map__nr(evlist->threads); |
415 | int nfds = 0; | 415 | int nfds = 0; |
416 | struct perf_evsel *evsel; | 416 | struct perf_evsel *evsel; |
417 | 417 | ||
418 | evlist__for_each(evlist, evsel) { | 418 | evlist__for_each(evlist, evsel) { |
419 | if (evsel->system_wide) | 419 | if (evsel->system_wide) |
420 | nfds += nr_cpus; | 420 | nfds += nr_cpus; |
421 | else | 421 | else |
422 | nfds += nr_cpus * nr_threads; | 422 | nfds += nr_cpus * nr_threads; |
423 | } | 423 | } |
424 | 424 | ||
425 | if (fdarray__available_entries(&evlist->pollfd) < nfds && | 425 | if (fdarray__available_entries(&evlist->pollfd) < nfds && |
426 | fdarray__grow(&evlist->pollfd, nfds) < 0) | 426 | fdarray__grow(&evlist->pollfd, nfds) < 0) |
427 | return -ENOMEM; | 427 | return -ENOMEM; |
428 | 428 | ||
429 | return 0; | 429 | return 0; |
430 | } | 430 | } |
431 | 431 | ||
432 | static int __perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, int idx) | 432 | static int __perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd, int idx) |
433 | { | 433 | { |
434 | int pos = fdarray__add(&evlist->pollfd, fd, POLLIN | POLLERR | POLLHUP); | 434 | int pos = fdarray__add(&evlist->pollfd, fd, POLLIN | POLLERR | POLLHUP); |
435 | /* | 435 | /* |
436 | * Save the idx so that when we filter out fds POLLHUP'ed we can | 436 | * Save the idx so that when we filter out fds POLLHUP'ed we can |
437 | * close the associated evlist->mmap[] entry. | 437 | * close the associated evlist->mmap[] entry. |
438 | */ | 438 | */ |
439 | if (pos >= 0) { | 439 | if (pos >= 0) { |
440 | evlist->pollfd.priv[pos].idx = idx; | 440 | evlist->pollfd.priv[pos].idx = idx; |
441 | 441 | ||
442 | fcntl(fd, F_SETFL, O_NONBLOCK); | 442 | fcntl(fd, F_SETFL, O_NONBLOCK); |
443 | } | 443 | } |
444 | 444 | ||
445 | return pos; | 445 | return pos; |
446 | } | 446 | } |
447 | 447 | ||
448 | int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) | 448 | int perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd) |
449 | { | 449 | { |
450 | return __perf_evlist__add_pollfd(evlist, fd, -1); | 450 | return __perf_evlist__add_pollfd(evlist, fd, -1); |
451 | } | 451 | } |
452 | 452 | ||
453 | static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd) | 453 | static void perf_evlist__munmap_filtered(struct fdarray *fda, int fd) |
454 | { | 454 | { |
455 | struct perf_evlist *evlist = container_of(fda, struct perf_evlist, pollfd); | 455 | struct perf_evlist *evlist = container_of(fda, struct perf_evlist, pollfd); |
456 | 456 | ||
457 | perf_evlist__mmap_put(evlist, fda->priv[fd].idx); | 457 | perf_evlist__mmap_put(evlist, fda->priv[fd].idx); |
458 | } | 458 | } |
459 | 459 | ||
460 | int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask) | 460 | int perf_evlist__filter_pollfd(struct perf_evlist *evlist, short revents_and_mask) |
461 | { | 461 | { |
462 | return fdarray__filter(&evlist->pollfd, revents_and_mask, | 462 | return fdarray__filter(&evlist->pollfd, revents_and_mask, |
463 | perf_evlist__munmap_filtered); | 463 | perf_evlist__munmap_filtered); |
464 | } | 464 | } |
465 | 465 | ||
466 | int perf_evlist__poll(struct perf_evlist *evlist, int timeout) | 466 | int perf_evlist__poll(struct perf_evlist *evlist, int timeout) |
467 | { | 467 | { |
468 | return fdarray__poll(&evlist->pollfd, timeout); | 468 | return fdarray__poll(&evlist->pollfd, timeout); |
469 | } | 469 | } |
470 | 470 | ||
471 | static void perf_evlist__id_hash(struct perf_evlist *evlist, | 471 | static void perf_evlist__id_hash(struct perf_evlist *evlist, |
472 | struct perf_evsel *evsel, | 472 | struct perf_evsel *evsel, |
473 | int cpu, int thread, u64 id) | 473 | int cpu, int thread, u64 id) |
474 | { | 474 | { |
475 | int hash; | 475 | int hash; |
476 | struct perf_sample_id *sid = SID(evsel, cpu, thread); | 476 | struct perf_sample_id *sid = SID(evsel, cpu, thread); |
477 | 477 | ||
478 | sid->id = id; | 478 | sid->id = id; |
479 | sid->evsel = evsel; | 479 | sid->evsel = evsel; |
480 | hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); | 480 | hash = hash_64(sid->id, PERF_EVLIST__HLIST_BITS); |
481 | hlist_add_head(&sid->node, &evlist->heads[hash]); | 481 | hlist_add_head(&sid->node, &evlist->heads[hash]); |
482 | } | 482 | } |
483 | 483 | ||
484 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | 484 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, |
485 | int cpu, int thread, u64 id) | 485 | int cpu, int thread, u64 id) |
486 | { | 486 | { |
487 | perf_evlist__id_hash(evlist, evsel, cpu, thread, id); | 487 | perf_evlist__id_hash(evlist, evsel, cpu, thread, id); |
488 | evsel->id[evsel->ids++] = id; | 488 | evsel->id[evsel->ids++] = id; |
489 | } | 489 | } |
490 | 490 | ||
491 | static int perf_evlist__id_add_fd(struct perf_evlist *evlist, | 491 | static int perf_evlist__id_add_fd(struct perf_evlist *evlist, |
492 | struct perf_evsel *evsel, | 492 | struct perf_evsel *evsel, |
493 | int cpu, int thread, int fd) | 493 | int cpu, int thread, int fd) |
494 | { | 494 | { |
495 | u64 read_data[4] = { 0, }; | 495 | u64 read_data[4] = { 0, }; |
496 | int id_idx = 1; /* The first entry is the counter value */ | 496 | int id_idx = 1; /* The first entry is the counter value */ |
497 | u64 id; | 497 | u64 id; |
498 | int ret; | 498 | int ret; |
499 | 499 | ||
500 | ret = ioctl(fd, PERF_EVENT_IOC_ID, &id); | 500 | ret = ioctl(fd, PERF_EVENT_IOC_ID, &id); |
501 | if (!ret) | 501 | if (!ret) |
502 | goto add; | 502 | goto add; |
503 | 503 | ||
504 | if (errno != ENOTTY) | 504 | if (errno != ENOTTY) |
505 | return -1; | 505 | return -1; |
506 | 506 | ||
507 | /* Legacy way to get event id.. All hail to old kernels! */ | 507 | /* Legacy way to get event id.. All hail to old kernels! */ |
508 | 508 | ||
509 | /* | 509 | /* |
510 | * This way does not work with group format read, so bail | 510 | * This way does not work with group format read, so bail |
511 | * out in that case. | 511 | * out in that case. |
512 | */ | 512 | */ |
513 | if (perf_evlist__read_format(evlist) & PERF_FORMAT_GROUP) | 513 | if (perf_evlist__read_format(evlist) & PERF_FORMAT_GROUP) |
514 | return -1; | 514 | return -1; |
515 | 515 | ||
516 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || | 516 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || |
517 | read(fd, &read_data, sizeof(read_data)) == -1) | 517 | read(fd, &read_data, sizeof(read_data)) == -1) |
518 | return -1; | 518 | return -1; |
519 | 519 | ||
520 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | 520 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) |
521 | ++id_idx; | 521 | ++id_idx; |
522 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | 522 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) |
523 | ++id_idx; | 523 | ++id_idx; |
524 | 524 | ||
525 | id = read_data[id_idx]; | 525 | id = read_data[id_idx]; |
526 | 526 | ||
527 | add: | 527 | add: |
528 | perf_evlist__id_add(evlist, evsel, cpu, thread, id); | 528 | perf_evlist__id_add(evlist, evsel, cpu, thread, id); |
529 | return 0; | 529 | return 0; |
530 | } | 530 | } |
531 | 531 | ||
532 | static void perf_evlist__set_sid_idx(struct perf_evlist *evlist, | 532 | static void perf_evlist__set_sid_idx(struct perf_evlist *evlist, |
533 | struct perf_evsel *evsel, int idx, int cpu, | 533 | struct perf_evsel *evsel, int idx, int cpu, |
534 | int thread) | 534 | int thread) |
535 | { | 535 | { |
536 | struct perf_sample_id *sid = SID(evsel, cpu, thread); | 536 | struct perf_sample_id *sid = SID(evsel, cpu, thread); |
537 | sid->idx = idx; | 537 | sid->idx = idx; |
538 | if (evlist->cpus && cpu >= 0) | 538 | if (evlist->cpus && cpu >= 0) |
539 | sid->cpu = evlist->cpus->map[cpu]; | 539 | sid->cpu = evlist->cpus->map[cpu]; |
540 | else | 540 | else |
541 | sid->cpu = -1; | 541 | sid->cpu = -1; |
542 | if (!evsel->system_wide && evlist->threads && thread >= 0) | 542 | if (!evsel->system_wide && evlist->threads && thread >= 0) |
543 | sid->tid = evlist->threads->map[thread]; | 543 | sid->tid = evlist->threads->map[thread]; |
544 | else | 544 | else |
545 | sid->tid = -1; | 545 | sid->tid = -1; |
546 | } | 546 | } |
547 | 547 | ||
548 | struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id) | 548 | struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id) |
549 | { | 549 | { |
550 | struct hlist_head *head; | 550 | struct hlist_head *head; |
551 | struct perf_sample_id *sid; | 551 | struct perf_sample_id *sid; |
552 | int hash; | 552 | int hash; |
553 | 553 | ||
554 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | 554 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); |
555 | head = &evlist->heads[hash]; | 555 | head = &evlist->heads[hash]; |
556 | 556 | ||
557 | hlist_for_each_entry(sid, head, node) | 557 | hlist_for_each_entry(sid, head, node) |
558 | if (sid->id == id) | 558 | if (sid->id == id) |
559 | return sid; | 559 | return sid; |
560 | 560 | ||
561 | return NULL; | 561 | return NULL; |
562 | } | 562 | } |
563 | 563 | ||
564 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | 564 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) |
565 | { | 565 | { |
566 | struct perf_sample_id *sid; | 566 | struct perf_sample_id *sid; |
567 | 567 | ||
568 | if (evlist->nr_entries == 1) | 568 | if (evlist->nr_entries == 1) |
569 | return perf_evlist__first(evlist); | 569 | return perf_evlist__first(evlist); |
570 | 570 | ||
571 | sid = perf_evlist__id2sid(evlist, id); | 571 | sid = perf_evlist__id2sid(evlist, id); |
572 | if (sid) | 572 | if (sid) |
573 | return sid->evsel; | 573 | return sid->evsel; |
574 | 574 | ||
575 | if (!perf_evlist__sample_id_all(evlist)) | 575 | if (!perf_evlist__sample_id_all(evlist)) |
576 | return perf_evlist__first(evlist); | 576 | return perf_evlist__first(evlist); |
577 | 577 | ||
578 | return NULL; | 578 | return NULL; |
579 | } | 579 | } |
580 | 580 | ||
581 | static int perf_evlist__event2id(struct perf_evlist *evlist, | 581 | static int perf_evlist__event2id(struct perf_evlist *evlist, |
582 | union perf_event *event, u64 *id) | 582 | union perf_event *event, u64 *id) |
583 | { | 583 | { |
584 | const u64 *array = event->sample.array; | 584 | const u64 *array = event->sample.array; |
585 | ssize_t n; | 585 | ssize_t n; |
586 | 586 | ||
587 | n = (event->header.size - sizeof(event->header)) >> 3; | 587 | n = (event->header.size - sizeof(event->header)) >> 3; |
588 | 588 | ||
589 | if (event->header.type == PERF_RECORD_SAMPLE) { | 589 | if (event->header.type == PERF_RECORD_SAMPLE) { |
590 | if (evlist->id_pos >= n) | 590 | if (evlist->id_pos >= n) |
591 | return -1; | 591 | return -1; |
592 | *id = array[evlist->id_pos]; | 592 | *id = array[evlist->id_pos]; |
593 | } else { | 593 | } else { |
594 | if (evlist->is_pos > n) | 594 | if (evlist->is_pos > n) |
595 | return -1; | 595 | return -1; |
596 | n -= evlist->is_pos; | 596 | n -= evlist->is_pos; |
597 | *id = array[n]; | 597 | *id = array[n]; |
598 | } | 598 | } |
599 | return 0; | 599 | return 0; |
600 | } | 600 | } |
601 | 601 | ||
602 | static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist, | 602 | static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist, |
603 | union perf_event *event) | 603 | union perf_event *event) |
604 | { | 604 | { |
605 | struct perf_evsel *first = perf_evlist__first(evlist); | 605 | struct perf_evsel *first = perf_evlist__first(evlist); |
606 | struct hlist_head *head; | 606 | struct hlist_head *head; |
607 | struct perf_sample_id *sid; | 607 | struct perf_sample_id *sid; |
608 | int hash; | 608 | int hash; |
609 | u64 id; | 609 | u64 id; |
610 | 610 | ||
611 | if (evlist->nr_entries == 1) | 611 | if (evlist->nr_entries == 1) |
612 | return first; | 612 | return first; |
613 | 613 | ||
614 | if (!first->attr.sample_id_all && | 614 | if (!first->attr.sample_id_all && |
615 | event->header.type != PERF_RECORD_SAMPLE) | 615 | event->header.type != PERF_RECORD_SAMPLE) |
616 | return first; | 616 | return first; |
617 | 617 | ||
618 | if (perf_evlist__event2id(evlist, event, &id)) | 618 | if (perf_evlist__event2id(evlist, event, &id)) |
619 | return NULL; | 619 | return NULL; |
620 | 620 | ||
621 | /* Synthesized events have an id of zero */ | 621 | /* Synthesized events have an id of zero */ |
622 | if (!id) | 622 | if (!id) |
623 | return first; | 623 | return first; |
624 | 624 | ||
625 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | 625 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); |
626 | head = &evlist->heads[hash]; | 626 | head = &evlist->heads[hash]; |
627 | 627 | ||
628 | hlist_for_each_entry(sid, head, node) { | 628 | hlist_for_each_entry(sid, head, node) { |
629 | if (sid->id == id) | 629 | if (sid->id == id) |
630 | return sid->evsel; | 630 | return sid->evsel; |
631 | } | 631 | } |
632 | return NULL; | 632 | return NULL; |
633 | } | 633 | } |
634 | 634 | ||
635 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | 635 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) |
636 | { | 636 | { |
637 | struct perf_mmap *md = &evlist->mmap[idx]; | 637 | struct perf_mmap *md = &evlist->mmap[idx]; |
638 | unsigned int head = perf_mmap__read_head(md); | 638 | unsigned int head = perf_mmap__read_head(md); |
639 | unsigned int old = md->prev; | 639 | unsigned int old = md->prev; |
640 | unsigned char *data = md->base + page_size; | 640 | unsigned char *data = md->base + page_size; |
641 | union perf_event *event = NULL; | 641 | union perf_event *event = NULL; |
642 | 642 | ||
643 | if (evlist->overwrite) { | 643 | if (evlist->overwrite) { |
644 | /* | 644 | /* |
645 | * If we're further behind than half the buffer, there's a chance | 645 | * If we're further behind than half the buffer, there's a chance |
646 | * the writer will bite our tail and mess up the samples under us. | 646 | * the writer will bite our tail and mess up the samples under us. |
647 | * | 647 | * |
648 | * If we somehow ended up ahead of the head, we got messed up. | 648 | * If we somehow ended up ahead of the head, we got messed up. |
649 | * | 649 | * |
650 | * In either case, truncate and restart at head. | 650 | * In either case, truncate and restart at head. |
651 | */ | 651 | */ |
652 | int diff = head - old; | 652 | int diff = head - old; |
653 | if (diff > md->mask / 2 || diff < 0) { | 653 | if (diff > md->mask / 2 || diff < 0) { |
654 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); | 654 | fprintf(stderr, "WARNING: failed to keep up with mmap data.\n"); |
655 | 655 | ||
656 | /* | 656 | /* |
657 | * head points to a known good entry, start there. | 657 | * head points to a known good entry, start there. |
658 | */ | 658 | */ |
659 | old = head; | 659 | old = head; |
660 | } | 660 | } |
661 | } | 661 | } |
662 | 662 | ||
663 | if (old != head) { | 663 | if (old != head) { |
664 | size_t size; | 664 | size_t size; |
665 | 665 | ||
666 | event = (union perf_event *)&data[old & md->mask]; | 666 | event = (union perf_event *)&data[old & md->mask]; |
667 | size = event->header.size; | 667 | size = event->header.size; |
668 | 668 | ||
669 | /* | 669 | /* |
670 | * Event straddles the mmap boundary -- header should always | 670 | * Event straddles the mmap boundary -- header should always |
671 | * be inside due to u64 alignment of output. | 671 | * be inside due to u64 alignment of output. |
672 | */ | 672 | */ |
673 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | 673 | if ((old & md->mask) + size != ((old + size) & md->mask)) { |
674 | unsigned int offset = old; | 674 | unsigned int offset = old; |
675 | unsigned int len = min(sizeof(*event), size), cpy; | 675 | unsigned int len = min(sizeof(*event), size), cpy; |
676 | void *dst = md->event_copy; | 676 | void *dst = md->event_copy; |
677 | 677 | ||
678 | do { | 678 | do { |
679 | cpy = min(md->mask + 1 - (offset & md->mask), len); | 679 | cpy = min(md->mask + 1 - (offset & md->mask), len); |
680 | memcpy(dst, &data[offset & md->mask], cpy); | 680 | memcpy(dst, &data[offset & md->mask], cpy); |
681 | offset += cpy; | 681 | offset += cpy; |
682 | dst += cpy; | 682 | dst += cpy; |
683 | len -= cpy; | 683 | len -= cpy; |
684 | } while (len); | 684 | } while (len); |
685 | 685 | ||
686 | event = (union perf_event *) md->event_copy; | 686 | event = (union perf_event *) md->event_copy; |
687 | } | 687 | } |
688 | 688 | ||
689 | old += size; | 689 | old += size; |
690 | } | 690 | } |
691 | 691 | ||
692 | md->prev = old; | 692 | md->prev = old; |
693 | 693 | ||
694 | return event; | 694 | return event; |
695 | } | 695 | } |
696 | 696 | ||
697 | static bool perf_mmap__empty(struct perf_mmap *md) | 697 | static bool perf_mmap__empty(struct perf_mmap *md) |
698 | { | 698 | { |
699 | return perf_mmap__read_head(md) != md->prev; | 699 | return perf_mmap__read_head(md) != md->prev; |
700 | } | 700 | } |
701 | 701 | ||
702 | static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx) | 702 | static void perf_evlist__mmap_get(struct perf_evlist *evlist, int idx) |
703 | { | 703 | { |
704 | ++evlist->mmap[idx].refcnt; | 704 | ++evlist->mmap[idx].refcnt; |
705 | } | 705 | } |
706 | 706 | ||
707 | static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx) | 707 | static void perf_evlist__mmap_put(struct perf_evlist *evlist, int idx) |
708 | { | 708 | { |
709 | BUG_ON(evlist->mmap[idx].refcnt == 0); | 709 | BUG_ON(evlist->mmap[idx].refcnt == 0); |
710 | 710 | ||
711 | if (--evlist->mmap[idx].refcnt == 0) | 711 | if (--evlist->mmap[idx].refcnt == 0) |
712 | __perf_evlist__munmap(evlist, idx); | 712 | __perf_evlist__munmap(evlist, idx); |
713 | } | 713 | } |
714 | 714 | ||
715 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) | 715 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) |
716 | { | 716 | { |
717 | struct perf_mmap *md = &evlist->mmap[idx]; | 717 | struct perf_mmap *md = &evlist->mmap[idx]; |
718 | 718 | ||
719 | if (!evlist->overwrite) { | 719 | if (!evlist->overwrite) { |
720 | unsigned int old = md->prev; | 720 | unsigned int old = md->prev; |
721 | 721 | ||
722 | perf_mmap__write_tail(md, old); | 722 | perf_mmap__write_tail(md, old); |
723 | } | 723 | } |
724 | 724 | ||
725 | if (md->refcnt == 1 && perf_mmap__empty(md)) | 725 | if (md->refcnt == 1 && perf_mmap__empty(md)) |
726 | perf_evlist__mmap_put(evlist, idx); | 726 | perf_evlist__mmap_put(evlist, idx); |
727 | } | 727 | } |
728 | 728 | ||
729 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) | 729 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) |
730 | { | 730 | { |
731 | if (evlist->mmap[idx].base != NULL) { | 731 | if (evlist->mmap[idx].base != NULL) { |
732 | munmap(evlist->mmap[idx].base, evlist->mmap_len); | 732 | munmap(evlist->mmap[idx].base, evlist->mmap_len); |
733 | evlist->mmap[idx].base = NULL; | 733 | evlist->mmap[idx].base = NULL; |
734 | evlist->mmap[idx].refcnt = 0; | 734 | evlist->mmap[idx].refcnt = 0; |
735 | } | 735 | } |
736 | } | 736 | } |
737 | 737 | ||
738 | void perf_evlist__munmap(struct perf_evlist *evlist) | 738 | void perf_evlist__munmap(struct perf_evlist *evlist) |
739 | { | 739 | { |
740 | int i; | 740 | int i; |
741 | 741 | ||
742 | if (evlist->mmap == NULL) | 742 | if (evlist->mmap == NULL) |
743 | return; | 743 | return; |
744 | 744 | ||
745 | for (i = 0; i < evlist->nr_mmaps; i++) | 745 | for (i = 0; i < evlist->nr_mmaps; i++) |
746 | __perf_evlist__munmap(evlist, i); | 746 | __perf_evlist__munmap(evlist, i); |
747 | 747 | ||
748 | zfree(&evlist->mmap); | 748 | zfree(&evlist->mmap); |
749 | } | 749 | } |
750 | 750 | ||
751 | static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | 751 | static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) |
752 | { | 752 | { |
753 | evlist->nr_mmaps = cpu_map__nr(evlist->cpus); | 753 | evlist->nr_mmaps = cpu_map__nr(evlist->cpus); |
754 | if (cpu_map__empty(evlist->cpus)) | 754 | if (cpu_map__empty(evlist->cpus)) |
755 | evlist->nr_mmaps = thread_map__nr(evlist->threads); | 755 | evlist->nr_mmaps = thread_map__nr(evlist->threads); |
756 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); | 756 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); |
757 | return evlist->mmap != NULL ? 0 : -ENOMEM; | 757 | return evlist->mmap != NULL ? 0 : -ENOMEM; |
758 | } | 758 | } |
759 | 759 | ||
760 | struct mmap_params { | 760 | struct mmap_params { |
761 | int prot; | 761 | int prot; |
762 | int mask; | 762 | int mask; |
763 | }; | 763 | }; |
764 | 764 | ||
765 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, | 765 | static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, |
766 | struct mmap_params *mp, int fd) | 766 | struct mmap_params *mp, int fd) |
767 | { | 767 | { |
768 | /* | 768 | /* |
769 | * The last one will be done at perf_evlist__mmap_consume(), so that we | 769 | * The last one will be done at perf_evlist__mmap_consume(), so that we |
770 | * make sure we don't prevent tools from consuming every last event in | 770 | * make sure we don't prevent tools from consuming every last event in |
771 | * the ring buffer. | 771 | * the ring buffer. |
772 | * | 772 | * |
773 | * I.e. we can get the POLLHUP meaning that the fd doesn't exist | 773 | * I.e. we can get the POLLHUP meaning that the fd doesn't exist |
774 | * anymore, but the last events for it are still in the ring buffer, | 774 | * anymore, but the last events for it are still in the ring buffer, |
775 | * waiting to be consumed. | 775 | * waiting to be consumed. |
776 | * | 776 | * |
777 | * Tools can chose to ignore this at their own discretion, but the | 777 | * Tools can chose to ignore this at their own discretion, but the |
778 | * evlist layer can't just drop it when filtering events in | 778 | * evlist layer can't just drop it when filtering events in |
779 | * perf_evlist__filter_pollfd(). | 779 | * perf_evlist__filter_pollfd(). |
780 | */ | 780 | */ |
781 | evlist->mmap[idx].refcnt = 2; | 781 | evlist->mmap[idx].refcnt = 2; |
782 | evlist->mmap[idx].prev = 0; | 782 | evlist->mmap[idx].prev = 0; |
783 | evlist->mmap[idx].mask = mp->mask; | 783 | evlist->mmap[idx].mask = mp->mask; |
784 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot, | 784 | evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot, |
785 | MAP_SHARED, fd, 0); | 785 | MAP_SHARED, fd, 0); |
786 | if (evlist->mmap[idx].base == MAP_FAILED) { | 786 | if (evlist->mmap[idx].base == MAP_FAILED) { |
787 | pr_debug2("failed to mmap perf event ring buffer, error %d\n", | 787 | pr_debug2("failed to mmap perf event ring buffer, error %d\n", |
788 | errno); | 788 | errno); |
789 | evlist->mmap[idx].base = NULL; | 789 | evlist->mmap[idx].base = NULL; |
790 | return -1; | 790 | return -1; |
791 | } | 791 | } |
792 | 792 | ||
793 | return 0; | 793 | return 0; |
794 | } | 794 | } |
795 | 795 | ||
796 | static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, | 796 | static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, |
797 | struct mmap_params *mp, int cpu, | 797 | struct mmap_params *mp, int cpu, |
798 | int thread, int *output) | 798 | int thread, int *output) |
799 | { | 799 | { |
800 | struct perf_evsel *evsel; | 800 | struct perf_evsel *evsel; |
801 | 801 | ||
802 | evlist__for_each(evlist, evsel) { | 802 | evlist__for_each(evlist, evsel) { |
803 | int fd; | 803 | int fd; |
804 | 804 | ||
805 | if (evsel->system_wide && thread) | 805 | if (evsel->system_wide && thread) |
806 | continue; | 806 | continue; |
807 | 807 | ||
808 | fd = FD(evsel, cpu, thread); | 808 | fd = FD(evsel, cpu, thread); |
809 | 809 | ||
810 | if (*output == -1) { | 810 | if (*output == -1) { |
811 | *output = fd; | 811 | *output = fd; |
812 | if (__perf_evlist__mmap(evlist, idx, mp, *output) < 0) | 812 | if (__perf_evlist__mmap(evlist, idx, mp, *output) < 0) |
813 | return -1; | 813 | return -1; |
814 | } else { | 814 | } else { |
815 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) | 815 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) |
816 | return -1; | 816 | return -1; |
817 | 817 | ||
818 | perf_evlist__mmap_get(evlist, idx); | 818 | perf_evlist__mmap_get(evlist, idx); |
819 | } | 819 | } |
820 | 820 | ||
821 | /* | 821 | /* |
822 | * The system_wide flag causes a selected event to be opened | 822 | * The system_wide flag causes a selected event to be opened |
823 | * always without a pid. Consequently it will never get a | 823 | * always without a pid. Consequently it will never get a |
824 | * POLLHUP, but it is used for tracking in combination with | 824 | * POLLHUP, but it is used for tracking in combination with |
825 | * other events, so it should not need to be polled anyway. | 825 | * other events, so it should not need to be polled anyway. |
826 | * Therefore don't add it for polling. | 826 | * Therefore don't add it for polling. |
827 | */ | 827 | */ |
828 | if (!evsel->system_wide && | 828 | if (!evsel->system_wide && |
829 | __perf_evlist__add_pollfd(evlist, fd, idx) < 0) { | 829 | __perf_evlist__add_pollfd(evlist, fd, idx) < 0) { |
830 | perf_evlist__mmap_put(evlist, idx); | 830 | perf_evlist__mmap_put(evlist, idx); |
831 | return -1; | 831 | return -1; |
832 | } | 832 | } |
833 | 833 | ||
834 | if (evsel->attr.read_format & PERF_FORMAT_ID) { | 834 | if (evsel->attr.read_format & PERF_FORMAT_ID) { |
835 | if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread, | 835 | if (perf_evlist__id_add_fd(evlist, evsel, cpu, thread, |
836 | fd) < 0) | 836 | fd) < 0) |
837 | return -1; | 837 | return -1; |
838 | perf_evlist__set_sid_idx(evlist, evsel, idx, cpu, | 838 | perf_evlist__set_sid_idx(evlist, evsel, idx, cpu, |
839 | thread); | 839 | thread); |
840 | } | 840 | } |
841 | } | 841 | } |
842 | 842 | ||
843 | return 0; | 843 | return 0; |
844 | } | 844 | } |
845 | 845 | ||
846 | static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, | 846 | static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, |
847 | struct mmap_params *mp) | 847 | struct mmap_params *mp) |
848 | { | 848 | { |
849 | int cpu, thread; | 849 | int cpu, thread; |
850 | int nr_cpus = cpu_map__nr(evlist->cpus); | 850 | int nr_cpus = cpu_map__nr(evlist->cpus); |
851 | int nr_threads = thread_map__nr(evlist->threads); | 851 | int nr_threads = thread_map__nr(evlist->threads); |
852 | 852 | ||
853 | pr_debug2("perf event ring buffer mmapped per cpu\n"); | 853 | pr_debug2("perf event ring buffer mmapped per cpu\n"); |
854 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 854 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
855 | int output = -1; | 855 | int output = -1; |
856 | 856 | ||
857 | for (thread = 0; thread < nr_threads; thread++) { | 857 | for (thread = 0; thread < nr_threads; thread++) { |
858 | if (perf_evlist__mmap_per_evsel(evlist, cpu, mp, cpu, | 858 | if (perf_evlist__mmap_per_evsel(evlist, cpu, mp, cpu, |
859 | thread, &output)) | 859 | thread, &output)) |
860 | goto out_unmap; | 860 | goto out_unmap; |
861 | } | 861 | } |
862 | } | 862 | } |
863 | 863 | ||
864 | return 0; | 864 | return 0; |
865 | 865 | ||
866 | out_unmap: | 866 | out_unmap: |
867 | for (cpu = 0; cpu < nr_cpus; cpu++) | 867 | for (cpu = 0; cpu < nr_cpus; cpu++) |
868 | __perf_evlist__munmap(evlist, cpu); | 868 | __perf_evlist__munmap(evlist, cpu); |
869 | return -1; | 869 | return -1; |
870 | } | 870 | } |
871 | 871 | ||
872 | static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, | 872 | static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, |
873 | struct mmap_params *mp) | 873 | struct mmap_params *mp) |
874 | { | 874 | { |
875 | int thread; | 875 | int thread; |
876 | int nr_threads = thread_map__nr(evlist->threads); | 876 | int nr_threads = thread_map__nr(evlist->threads); |
877 | 877 | ||
878 | pr_debug2("perf event ring buffer mmapped per thread\n"); | 878 | pr_debug2("perf event ring buffer mmapped per thread\n"); |
879 | for (thread = 0; thread < nr_threads; thread++) { | 879 | for (thread = 0; thread < nr_threads; thread++) { |
880 | int output = -1; | 880 | int output = -1; |
881 | 881 | ||
882 | if (perf_evlist__mmap_per_evsel(evlist, thread, mp, 0, thread, | 882 | if (perf_evlist__mmap_per_evsel(evlist, thread, mp, 0, thread, |
883 | &output)) | 883 | &output)) |
884 | goto out_unmap; | 884 | goto out_unmap; |
885 | } | 885 | } |
886 | 886 | ||
887 | return 0; | 887 | return 0; |
888 | 888 | ||
889 | out_unmap: | 889 | out_unmap: |
890 | for (thread = 0; thread < nr_threads; thread++) | 890 | for (thread = 0; thread < nr_threads; thread++) |
891 | __perf_evlist__munmap(evlist, thread); | 891 | __perf_evlist__munmap(evlist, thread); |
892 | return -1; | 892 | return -1; |
893 | } | 893 | } |
894 | 894 | ||
895 | static size_t perf_evlist__mmap_size(unsigned long pages) | 895 | static size_t perf_evlist__mmap_size(unsigned long pages) |
896 | { | 896 | { |
897 | if (pages == UINT_MAX) { | 897 | if (pages == UINT_MAX) { |
898 | int max; | 898 | int max; |
899 | 899 | ||
900 | if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) { | 900 | if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) { |
901 | /* | 901 | /* |
902 | * Pick a once upon a time good value, i.e. things look | 902 | * Pick a once upon a time good value, i.e. things look |
903 | * strange since we can't read a sysctl value, but lets not | 903 | * strange since we can't read a sysctl value, but lets not |
904 | * die yet... | 904 | * die yet... |
905 | */ | 905 | */ |
906 | max = 512; | 906 | max = 512; |
907 | } else { | 907 | } else { |
908 | max -= (page_size / 1024); | 908 | max -= (page_size / 1024); |
909 | } | 909 | } |
910 | 910 | ||
911 | pages = (max * 1024) / page_size; | 911 | pages = (max * 1024) / page_size; |
912 | if (!is_power_of_2(pages)) | 912 | if (!is_power_of_2(pages)) |
913 | pages = rounddown_pow_of_two(pages); | 913 | pages = rounddown_pow_of_two(pages); |
914 | } else if (!is_power_of_2(pages)) | 914 | } else if (!is_power_of_2(pages)) |
915 | return 0; | 915 | return 0; |
916 | 916 | ||
917 | return (pages + 1) * page_size; | 917 | return (pages + 1) * page_size; |
918 | } | 918 | } |
919 | 919 | ||
920 | static long parse_pages_arg(const char *str, unsigned long min, | 920 | static long parse_pages_arg(const char *str, unsigned long min, |
921 | unsigned long max) | 921 | unsigned long max) |
922 | { | 922 | { |
923 | unsigned long pages, val; | 923 | unsigned long pages, val; |
924 | static struct parse_tag tags[] = { | 924 | static struct parse_tag tags[] = { |
925 | { .tag = 'B', .mult = 1 }, | 925 | { .tag = 'B', .mult = 1 }, |
926 | { .tag = 'K', .mult = 1 << 10 }, | 926 | { .tag = 'K', .mult = 1 << 10 }, |
927 | { .tag = 'M', .mult = 1 << 20 }, | 927 | { .tag = 'M', .mult = 1 << 20 }, |
928 | { .tag = 'G', .mult = 1 << 30 }, | 928 | { .tag = 'G', .mult = 1 << 30 }, |
929 | { .tag = 0 }, | 929 | { .tag = 0 }, |
930 | }; | 930 | }; |
931 | 931 | ||
932 | if (str == NULL) | 932 | if (str == NULL) |
933 | return -EINVAL; | 933 | return -EINVAL; |
934 | 934 | ||
935 | val = parse_tag_value(str, tags); | 935 | val = parse_tag_value(str, tags); |
936 | if (val != (unsigned long) -1) { | 936 | if (val != (unsigned long) -1) { |
937 | /* we got file size value */ | 937 | /* we got file size value */ |
938 | pages = PERF_ALIGN(val, page_size) / page_size; | 938 | pages = PERF_ALIGN(val, page_size) / page_size; |
939 | } else { | 939 | } else { |
940 | /* we got pages count value */ | 940 | /* we got pages count value */ |
941 | char *eptr; | 941 | char *eptr; |
942 | pages = strtoul(str, &eptr, 10); | 942 | pages = strtoul(str, &eptr, 10); |
943 | if (*eptr != '\0') | 943 | if (*eptr != '\0') |
944 | return -EINVAL; | 944 | return -EINVAL; |
945 | } | 945 | } |
946 | 946 | ||
947 | if (pages == 0 && min == 0) { | 947 | if (pages == 0 && min == 0) { |
948 | /* leave number of pages at 0 */ | 948 | /* leave number of pages at 0 */ |
949 | } else if (!is_power_of_2(pages)) { | 949 | } else if (!is_power_of_2(pages)) { |
950 | /* round pages up to next power of 2 */ | 950 | /* round pages up to next power of 2 */ |
951 | pages = roundup_pow_of_two(pages); | 951 | pages = roundup_pow_of_two(pages); |
952 | if (!pages) | 952 | if (!pages) |
953 | return -EINVAL; | 953 | return -EINVAL; |
954 | pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", | 954 | pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", |
955 | pages * page_size, pages); | 955 | pages * page_size, pages); |
956 | } | 956 | } |
957 | 957 | ||
958 | if (pages > max) | 958 | if (pages > max) |
959 | return -EINVAL; | 959 | return -EINVAL; |
960 | 960 | ||
961 | return pages; | 961 | return pages; |
962 | } | 962 | } |
963 | 963 | ||
964 | int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, | 964 | int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, |
965 | int unset __maybe_unused) | 965 | int unset __maybe_unused) |
966 | { | 966 | { |
967 | unsigned int *mmap_pages = opt->value; | 967 | unsigned int *mmap_pages = opt->value; |
968 | unsigned long max = UINT_MAX; | 968 | unsigned long max = UINT_MAX; |
969 | long pages; | 969 | long pages; |
970 | 970 | ||
971 | if (max > SIZE_MAX / page_size) | 971 | if (max > SIZE_MAX / page_size) |
972 | max = SIZE_MAX / page_size; | 972 | max = SIZE_MAX / page_size; |
973 | 973 | ||
974 | pages = parse_pages_arg(str, 1, max); | 974 | pages = parse_pages_arg(str, 1, max); |
975 | if (pages < 0) { | 975 | if (pages < 0) { |
976 | pr_err("Invalid argument for --mmap_pages/-m\n"); | 976 | pr_err("Invalid argument for --mmap_pages/-m\n"); |
977 | return -1; | 977 | return -1; |
978 | } | 978 | } |
979 | 979 | ||
980 | *mmap_pages = pages; | 980 | *mmap_pages = pages; |
981 | return 0; | 981 | return 0; |
982 | } | 982 | } |
983 | 983 | ||
984 | /** | 984 | /** |
985 | * perf_evlist__mmap - Create mmaps to receive events. | 985 | * perf_evlist__mmap - Create mmaps to receive events. |
986 | * @evlist: list of events | 986 | * @evlist: list of events |
987 | * @pages: map length in pages | 987 | * @pages: map length in pages |
988 | * @overwrite: overwrite older events? | 988 | * @overwrite: overwrite older events? |
989 | * | 989 | * |
990 | * If @overwrite is %false the user needs to signal event consumption using | 990 | * If @overwrite is %false the user needs to signal event consumption using |
991 | * perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this | 991 | * perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this |
992 | * automatically. | 992 | * automatically. |
993 | * | 993 | * |
994 | * Return: %0 on success, negative error code otherwise. | 994 | * Return: %0 on success, negative error code otherwise. |
995 | */ | 995 | */ |
996 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | 996 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, |
997 | bool overwrite) | 997 | bool overwrite) |
998 | { | 998 | { |
999 | struct perf_evsel *evsel; | 999 | struct perf_evsel *evsel; |
1000 | const struct cpu_map *cpus = evlist->cpus; | 1000 | const struct cpu_map *cpus = evlist->cpus; |
1001 | const struct thread_map *threads = evlist->threads; | 1001 | const struct thread_map *threads = evlist->threads; |
1002 | struct mmap_params mp = { | 1002 | struct mmap_params mp = { |
1003 | .prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), | 1003 | .prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), |
1004 | }; | 1004 | }; |
1005 | 1005 | ||
1006 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) | 1006 | if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) |
1007 | return -ENOMEM; | 1007 | return -ENOMEM; |
1008 | 1008 | ||
1009 | if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0) | 1009 | if (evlist->pollfd.entries == NULL && perf_evlist__alloc_pollfd(evlist) < 0) |
1010 | return -ENOMEM; | 1010 | return -ENOMEM; |
1011 | 1011 | ||
1012 | evlist->overwrite = overwrite; | 1012 | evlist->overwrite = overwrite; |
1013 | evlist->mmap_len = perf_evlist__mmap_size(pages); | 1013 | evlist->mmap_len = perf_evlist__mmap_size(pages); |
1014 | pr_debug("mmap size %zuB\n", evlist->mmap_len); | 1014 | pr_debug("mmap size %zuB\n", evlist->mmap_len); |
1015 | mp.mask = evlist->mmap_len - page_size - 1; | 1015 | mp.mask = evlist->mmap_len - page_size - 1; |
1016 | 1016 | ||
1017 | evlist__for_each(evlist, evsel) { | 1017 | evlist__for_each(evlist, evsel) { |
1018 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | 1018 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && |
1019 | evsel->sample_id == NULL && | 1019 | evsel->sample_id == NULL && |
1020 | perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0) | 1020 | perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0) |
1021 | return -ENOMEM; | 1021 | return -ENOMEM; |
1022 | } | 1022 | } |
1023 | 1023 | ||
1024 | if (cpu_map__empty(cpus)) | 1024 | if (cpu_map__empty(cpus)) |
1025 | return perf_evlist__mmap_per_thread(evlist, &mp); | 1025 | return perf_evlist__mmap_per_thread(evlist, &mp); |
1026 | 1026 | ||
1027 | return perf_evlist__mmap_per_cpu(evlist, &mp); | 1027 | return perf_evlist__mmap_per_cpu(evlist, &mp); |
1028 | } | 1028 | } |
1029 | 1029 | ||
1030 | int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) | 1030 | int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) |
1031 | { | 1031 | { |
1032 | evlist->threads = thread_map__new_str(target->pid, target->tid, | 1032 | evlist->threads = thread_map__new_str(target->pid, target->tid, |
1033 | target->uid); | 1033 | target->uid); |
1034 | 1034 | ||
1035 | if (evlist->threads == NULL) | 1035 | if (evlist->threads == NULL) |
1036 | return -1; | 1036 | return -1; |
1037 | 1037 | ||
1038 | if (target__uses_dummy_map(target)) | 1038 | if (target__uses_dummy_map(target)) |
1039 | evlist->cpus = cpu_map__dummy_new(); | 1039 | evlist->cpus = cpu_map__dummy_new(); |
1040 | else | 1040 | else |
1041 | evlist->cpus = cpu_map__new(target->cpu_list); | 1041 | evlist->cpus = cpu_map__new(target->cpu_list); |
1042 | 1042 | ||
1043 | if (evlist->cpus == NULL) | 1043 | if (evlist->cpus == NULL) |
1044 | goto out_delete_threads; | 1044 | goto out_delete_threads; |
1045 | 1045 | ||
1046 | return 0; | 1046 | return 0; |
1047 | 1047 | ||
1048 | out_delete_threads: | 1048 | out_delete_threads: |
1049 | thread_map__delete(evlist->threads); | 1049 | thread_map__delete(evlist->threads); |
1050 | evlist->threads = NULL; | 1050 | evlist->threads = NULL; |
1051 | return -1; | 1051 | return -1; |
1052 | } | 1052 | } |
1053 | 1053 | ||
1054 | int perf_evlist__apply_filters(struct perf_evlist *evlist) | 1054 | int perf_evlist__apply_filters(struct perf_evlist *evlist) |
1055 | { | 1055 | { |
1056 | struct perf_evsel *evsel; | 1056 | struct perf_evsel *evsel; |
1057 | int err = 0; | 1057 | int err = 0; |
1058 | const int ncpus = cpu_map__nr(evlist->cpus), | 1058 | const int ncpus = cpu_map__nr(evlist->cpus), |
1059 | nthreads = thread_map__nr(evlist->threads); | 1059 | nthreads = thread_map__nr(evlist->threads); |
1060 | 1060 | ||
1061 | evlist__for_each(evlist, evsel) { | 1061 | evlist__for_each(evlist, evsel) { |
1062 | if (evsel->filter == NULL) | 1062 | if (evsel->filter == NULL) |
1063 | continue; | 1063 | continue; |
1064 | 1064 | ||
1065 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter); | 1065 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter); |
1066 | if (err) | 1066 | if (err) |
1067 | break; | 1067 | break; |
1068 | } | 1068 | } |
1069 | 1069 | ||
1070 | return err; | 1070 | return err; |
1071 | } | 1071 | } |
1072 | 1072 | ||
1073 | int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) | 1073 | int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) |
1074 | { | 1074 | { |
1075 | struct perf_evsel *evsel; | 1075 | struct perf_evsel *evsel; |
1076 | int err = 0; | 1076 | int err = 0; |
1077 | const int ncpus = cpu_map__nr(evlist->cpus), | 1077 | const int ncpus = cpu_map__nr(evlist->cpus), |
1078 | nthreads = thread_map__nr(evlist->threads); | 1078 | nthreads = thread_map__nr(evlist->threads); |
1079 | 1079 | ||
1080 | evlist__for_each(evlist, evsel) { | 1080 | evlist__for_each(evlist, evsel) { |
1081 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); | 1081 | err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); |
1082 | if (err) | 1082 | if (err) |
1083 | break; | 1083 | break; |
1084 | } | 1084 | } |
1085 | 1085 | ||
1086 | return err; | 1086 | return err; |
1087 | } | 1087 | } |
1088 | 1088 | ||
1089 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) | 1089 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) |
1090 | { | 1090 | { |
1091 | struct perf_evsel *pos; | 1091 | struct perf_evsel *pos; |
1092 | 1092 | ||
1093 | if (evlist->nr_entries == 1) | 1093 | if (evlist->nr_entries == 1) |
1094 | return true; | 1094 | return true; |
1095 | 1095 | ||
1096 | if (evlist->id_pos < 0 || evlist->is_pos < 0) | 1096 | if (evlist->id_pos < 0 || evlist->is_pos < 0) |
1097 | return false; | 1097 | return false; |
1098 | 1098 | ||
1099 | evlist__for_each(evlist, pos) { | 1099 | evlist__for_each(evlist, pos) { |
1100 | if (pos->id_pos != evlist->id_pos || | 1100 | if (pos->id_pos != evlist->id_pos || |
1101 | pos->is_pos != evlist->is_pos) | 1101 | pos->is_pos != evlist->is_pos) |
1102 | return false; | 1102 | return false; |
1103 | } | 1103 | } |
1104 | 1104 | ||
1105 | return true; | 1105 | return true; |
1106 | } | 1106 | } |
1107 | 1107 | ||
1108 | u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist) | 1108 | u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist) |
1109 | { | 1109 | { |
1110 | struct perf_evsel *evsel; | 1110 | struct perf_evsel *evsel; |
1111 | 1111 | ||
1112 | if (evlist->combined_sample_type) | 1112 | if (evlist->combined_sample_type) |
1113 | return evlist->combined_sample_type; | 1113 | return evlist->combined_sample_type; |
1114 | 1114 | ||
1115 | evlist__for_each(evlist, evsel) | 1115 | evlist__for_each(evlist, evsel) |
1116 | evlist->combined_sample_type |= evsel->attr.sample_type; | 1116 | evlist->combined_sample_type |= evsel->attr.sample_type; |
1117 | 1117 | ||
1118 | return evlist->combined_sample_type; | 1118 | return evlist->combined_sample_type; |
1119 | } | 1119 | } |
1120 | 1120 | ||
1121 | u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist) | 1121 | u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist) |
1122 | { | 1122 | { |
1123 | evlist->combined_sample_type = 0; | 1123 | evlist->combined_sample_type = 0; |
1124 | return __perf_evlist__combined_sample_type(evlist); | 1124 | return __perf_evlist__combined_sample_type(evlist); |
1125 | } | 1125 | } |
1126 | 1126 | ||
1127 | bool perf_evlist__valid_read_format(struct perf_evlist *evlist) | 1127 | bool perf_evlist__valid_read_format(struct perf_evlist *evlist) |
1128 | { | 1128 | { |
1129 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; | 1129 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; |
1130 | u64 read_format = first->attr.read_format; | 1130 | u64 read_format = first->attr.read_format; |
1131 | u64 sample_type = first->attr.sample_type; | 1131 | u64 sample_type = first->attr.sample_type; |
1132 | 1132 | ||
1133 | evlist__for_each(evlist, pos) { | 1133 | evlist__for_each(evlist, pos) { |
1134 | if (read_format != pos->attr.read_format) | 1134 | if (read_format != pos->attr.read_format) |
1135 | return false; | 1135 | return false; |
1136 | } | 1136 | } |
1137 | 1137 | ||
1138 | /* PERF_SAMPLE_READ imples PERF_FORMAT_ID. */ | 1138 | /* PERF_SAMPLE_READ imples PERF_FORMAT_ID. */ |
1139 | if ((sample_type & PERF_SAMPLE_READ) && | 1139 | if ((sample_type & PERF_SAMPLE_READ) && |
1140 | !(read_format & PERF_FORMAT_ID)) { | 1140 | !(read_format & PERF_FORMAT_ID)) { |
1141 | return false; | 1141 | return false; |
1142 | } | 1142 | } |
1143 | 1143 | ||
1144 | return true; | 1144 | return true; |
1145 | } | 1145 | } |
1146 | 1146 | ||
1147 | u64 perf_evlist__read_format(struct perf_evlist *evlist) | 1147 | u64 perf_evlist__read_format(struct perf_evlist *evlist) |
1148 | { | 1148 | { |
1149 | struct perf_evsel *first = perf_evlist__first(evlist); | 1149 | struct perf_evsel *first = perf_evlist__first(evlist); |
1150 | return first->attr.read_format; | 1150 | return first->attr.read_format; |
1151 | } | 1151 | } |
1152 | 1152 | ||
1153 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist) | 1153 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist) |
1154 | { | 1154 | { |
1155 | struct perf_evsel *first = perf_evlist__first(evlist); | 1155 | struct perf_evsel *first = perf_evlist__first(evlist); |
1156 | struct perf_sample *data; | 1156 | struct perf_sample *data; |
1157 | u64 sample_type; | 1157 | u64 sample_type; |
1158 | u16 size = 0; | 1158 | u16 size = 0; |
1159 | 1159 | ||
1160 | if (!first->attr.sample_id_all) | 1160 | if (!first->attr.sample_id_all) |
1161 | goto out; | 1161 | goto out; |
1162 | 1162 | ||
1163 | sample_type = first->attr.sample_type; | 1163 | sample_type = first->attr.sample_type; |
1164 | 1164 | ||
1165 | if (sample_type & PERF_SAMPLE_TID) | 1165 | if (sample_type & PERF_SAMPLE_TID) |
1166 | size += sizeof(data->tid) * 2; | 1166 | size += sizeof(data->tid) * 2; |
1167 | 1167 | ||
1168 | if (sample_type & PERF_SAMPLE_TIME) | 1168 | if (sample_type & PERF_SAMPLE_TIME) |
1169 | size += sizeof(data->time); | 1169 | size += sizeof(data->time); |
1170 | 1170 | ||
1171 | if (sample_type & PERF_SAMPLE_ID) | 1171 | if (sample_type & PERF_SAMPLE_ID) |
1172 | size += sizeof(data->id); | 1172 | size += sizeof(data->id); |
1173 | 1173 | ||
1174 | if (sample_type & PERF_SAMPLE_STREAM_ID) | 1174 | if (sample_type & PERF_SAMPLE_STREAM_ID) |
1175 | size += sizeof(data->stream_id); | 1175 | size += sizeof(data->stream_id); |
1176 | 1176 | ||
1177 | if (sample_type & PERF_SAMPLE_CPU) | 1177 | if (sample_type & PERF_SAMPLE_CPU) |
1178 | size += sizeof(data->cpu) * 2; | 1178 | size += sizeof(data->cpu) * 2; |
1179 | 1179 | ||
1180 | if (sample_type & PERF_SAMPLE_IDENTIFIER) | 1180 | if (sample_type & PERF_SAMPLE_IDENTIFIER) |
1181 | size += sizeof(data->id); | 1181 | size += sizeof(data->id); |
1182 | out: | 1182 | out: |
1183 | return size; | 1183 | return size; |
1184 | } | 1184 | } |
1185 | 1185 | ||
1186 | bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist) | 1186 | bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist) |
1187 | { | 1187 | { |
1188 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; | 1188 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; |
1189 | 1189 | ||
1190 | evlist__for_each_continue(evlist, pos) { | 1190 | evlist__for_each_continue(evlist, pos) { |
1191 | if (first->attr.sample_id_all != pos->attr.sample_id_all) | 1191 | if (first->attr.sample_id_all != pos->attr.sample_id_all) |
1192 | return false; | 1192 | return false; |
1193 | } | 1193 | } |
1194 | 1194 | ||
1195 | return true; | 1195 | return true; |
1196 | } | 1196 | } |
1197 | 1197 | ||
1198 | bool perf_evlist__sample_id_all(struct perf_evlist *evlist) | 1198 | bool perf_evlist__sample_id_all(struct perf_evlist *evlist) |
1199 | { | 1199 | { |
1200 | struct perf_evsel *first = perf_evlist__first(evlist); | 1200 | struct perf_evsel *first = perf_evlist__first(evlist); |
1201 | return first->attr.sample_id_all; | 1201 | return first->attr.sample_id_all; |
1202 | } | 1202 | } |
1203 | 1203 | ||
1204 | void perf_evlist__set_selected(struct perf_evlist *evlist, | 1204 | void perf_evlist__set_selected(struct perf_evlist *evlist, |
1205 | struct perf_evsel *evsel) | 1205 | struct perf_evsel *evsel) |
1206 | { | 1206 | { |
1207 | evlist->selected = evsel; | 1207 | evlist->selected = evsel; |
1208 | } | 1208 | } |
1209 | 1209 | ||
1210 | void perf_evlist__close(struct perf_evlist *evlist) | 1210 | void perf_evlist__close(struct perf_evlist *evlist) |
1211 | { | 1211 | { |
1212 | struct perf_evsel *evsel; | 1212 | struct perf_evsel *evsel; |
1213 | int ncpus = cpu_map__nr(evlist->cpus); | 1213 | int ncpus = cpu_map__nr(evlist->cpus); |
1214 | int nthreads = thread_map__nr(evlist->threads); | 1214 | int nthreads = thread_map__nr(evlist->threads); |
1215 | int n; | 1215 | int n; |
1216 | 1216 | ||
1217 | evlist__for_each_reverse(evlist, evsel) { | 1217 | evlist__for_each_reverse(evlist, evsel) { |
1218 | n = evsel->cpus ? evsel->cpus->nr : ncpus; | 1218 | n = evsel->cpus ? evsel->cpus->nr : ncpus; |
1219 | perf_evsel__close(evsel, n, nthreads); | 1219 | perf_evsel__close(evsel, n, nthreads); |
1220 | } | 1220 | } |
1221 | } | 1221 | } |
1222 | 1222 | ||
1223 | static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist) | 1223 | static int perf_evlist__create_syswide_maps(struct perf_evlist *evlist) |
1224 | { | 1224 | { |
1225 | int err = -ENOMEM; | 1225 | int err = -ENOMEM; |
1226 | 1226 | ||
1227 | /* | 1227 | /* |
1228 | * Try reading /sys/devices/system/cpu/online to get | 1228 | * Try reading /sys/devices/system/cpu/online to get |
1229 | * an all cpus map. | 1229 | * an all cpus map. |
1230 | * | 1230 | * |
1231 | * FIXME: -ENOMEM is the best we can do here, the cpu_map | 1231 | * FIXME: -ENOMEM is the best we can do here, the cpu_map |
1232 | * code needs an overhaul to properly forward the | 1232 | * code needs an overhaul to properly forward the |
1233 | * error, and we may not want to do that fallback to a | 1233 | * error, and we may not want to do that fallback to a |
1234 | * default cpu identity map :-\ | 1234 | * default cpu identity map :-\ |
1235 | */ | 1235 | */ |
1236 | evlist->cpus = cpu_map__new(NULL); | 1236 | evlist->cpus = cpu_map__new(NULL); |
1237 | if (evlist->cpus == NULL) | 1237 | if (evlist->cpus == NULL) |
1238 | goto out; | 1238 | goto out; |
1239 | 1239 | ||
1240 | evlist->threads = thread_map__new_dummy(); | 1240 | evlist->threads = thread_map__new_dummy(); |
1241 | if (evlist->threads == NULL) | 1241 | if (evlist->threads == NULL) |
1242 | goto out_free_cpus; | 1242 | goto out_free_cpus; |
1243 | 1243 | ||
1244 | err = 0; | 1244 | err = 0; |
1245 | out: | 1245 | out: |
1246 | return err; | 1246 | return err; |
1247 | out_free_cpus: | 1247 | out_free_cpus: |
1248 | cpu_map__delete(evlist->cpus); | 1248 | cpu_map__delete(evlist->cpus); |
1249 | evlist->cpus = NULL; | 1249 | evlist->cpus = NULL; |
1250 | goto out; | 1250 | goto out; |
1251 | } | 1251 | } |
1252 | 1252 | ||
1253 | int perf_evlist__open(struct perf_evlist *evlist) | 1253 | int perf_evlist__open(struct perf_evlist *evlist) |
1254 | { | 1254 | { |
1255 | struct perf_evsel *evsel; | 1255 | struct perf_evsel *evsel; |
1256 | int err; | 1256 | int err; |
1257 | 1257 | ||
1258 | /* | 1258 | /* |
1259 | * Default: one fd per CPU, all threads, aka systemwide | 1259 | * Default: one fd per CPU, all threads, aka systemwide |
1260 | * as sys_perf_event_open(cpu = -1, thread = -1) is EINVAL | 1260 | * as sys_perf_event_open(cpu = -1, thread = -1) is EINVAL |
1261 | */ | 1261 | */ |
1262 | if (evlist->threads == NULL && evlist->cpus == NULL) { | 1262 | if (evlist->threads == NULL && evlist->cpus == NULL) { |
1263 | err = perf_evlist__create_syswide_maps(evlist); | 1263 | err = perf_evlist__create_syswide_maps(evlist); |
1264 | if (err < 0) | 1264 | if (err < 0) |
1265 | goto out_err; | 1265 | goto out_err; |
1266 | } | 1266 | } |
1267 | 1267 | ||
1268 | perf_evlist__update_id_pos(evlist); | 1268 | perf_evlist__update_id_pos(evlist); |
1269 | 1269 | ||
1270 | evlist__for_each(evlist, evsel) { | 1270 | evlist__for_each(evlist, evsel) { |
1271 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); | 1271 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); |
1272 | if (err < 0) | 1272 | if (err < 0) |
1273 | goto out_err; | 1273 | goto out_err; |
1274 | } | 1274 | } |
1275 | 1275 | ||
1276 | return 0; | 1276 | return 0; |
1277 | out_err: | 1277 | out_err: |
1278 | perf_evlist__close(evlist); | 1278 | perf_evlist__close(evlist); |
1279 | errno = -err; | 1279 | errno = -err; |
1280 | return err; | 1280 | return err; |
1281 | } | 1281 | } |
1282 | 1282 | ||
1283 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target, | 1283 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target, |
1284 | const char *argv[], bool pipe_output, | 1284 | const char *argv[], bool pipe_output, |
1285 | void (*exec_error)(int signo, siginfo_t *info, void *ucontext)) | 1285 | void (*exec_error)(int signo, siginfo_t *info, void *ucontext)) |
1286 | { | 1286 | { |
1287 | int child_ready_pipe[2], go_pipe[2]; | 1287 | int child_ready_pipe[2], go_pipe[2]; |
1288 | char bf; | 1288 | char bf; |
1289 | 1289 | ||
1290 | if (pipe(child_ready_pipe) < 0) { | 1290 | if (pipe(child_ready_pipe) < 0) { |
1291 | perror("failed to create 'ready' pipe"); | 1291 | perror("failed to create 'ready' pipe"); |
1292 | return -1; | 1292 | return -1; |
1293 | } | 1293 | } |
1294 | 1294 | ||
1295 | if (pipe(go_pipe) < 0) { | 1295 | if (pipe(go_pipe) < 0) { |
1296 | perror("failed to create 'go' pipe"); | 1296 | perror("failed to create 'go' pipe"); |
1297 | goto out_close_ready_pipe; | 1297 | goto out_close_ready_pipe; |
1298 | } | 1298 | } |
1299 | 1299 | ||
1300 | evlist->workload.pid = fork(); | 1300 | evlist->workload.pid = fork(); |
1301 | if (evlist->workload.pid < 0) { | 1301 | if (evlist->workload.pid < 0) { |
1302 | perror("failed to fork"); | 1302 | perror("failed to fork"); |
1303 | goto out_close_pipes; | 1303 | goto out_close_pipes; |
1304 | } | 1304 | } |
1305 | 1305 | ||
1306 | if (!evlist->workload.pid) { | 1306 | if (!evlist->workload.pid) { |
1307 | int ret; | 1307 | int ret; |
1308 | 1308 | ||
1309 | if (pipe_output) | 1309 | if (pipe_output) |
1310 | dup2(2, 1); | 1310 | dup2(2, 1); |
1311 | 1311 | ||
1312 | signal(SIGTERM, SIG_DFL); | 1312 | signal(SIGTERM, SIG_DFL); |
1313 | 1313 | ||
1314 | close(child_ready_pipe[0]); | 1314 | close(child_ready_pipe[0]); |
1315 | close(go_pipe[1]); | 1315 | close(go_pipe[1]); |
1316 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | 1316 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); |
1317 | 1317 | ||
1318 | /* | 1318 | /* |
1319 | * Tell the parent we're ready to go | 1319 | * Tell the parent we're ready to go |
1320 | */ | 1320 | */ |
1321 | close(child_ready_pipe[1]); | 1321 | close(child_ready_pipe[1]); |
1322 | 1322 | ||
1323 | /* | 1323 | /* |
1324 | * Wait until the parent tells us to go. | 1324 | * Wait until the parent tells us to go. |
1325 | */ | 1325 | */ |
1326 | ret = read(go_pipe[0], &bf, 1); | 1326 | ret = read(go_pipe[0], &bf, 1); |
1327 | /* | 1327 | /* |
1328 | * The parent will ask for the execvp() to be performed by | 1328 | * The parent will ask for the execvp() to be performed by |
1329 | * writing exactly one byte, in workload.cork_fd, usually via | 1329 | * writing exactly one byte, in workload.cork_fd, usually via |
1330 | * perf_evlist__start_workload(). | 1330 | * perf_evlist__start_workload(). |
1331 | * | 1331 | * |
1332 | * For cancelling the workload without actuallin running it, | 1332 | * For cancelling the workload without actuallin running it, |
1333 | * the parent will just close workload.cork_fd, without writing | 1333 | * the parent will just close workload.cork_fd, without writing |
1334 | * anything, i.e. read will return zero and we just exit() | 1334 | * anything, i.e. read will return zero and we just exit() |
1335 | * here. | 1335 | * here. |
1336 | */ | 1336 | */ |
1337 | if (ret != 1) { | 1337 | if (ret != 1) { |
1338 | if (ret == -1) | 1338 | if (ret == -1) |
1339 | perror("unable to read pipe"); | 1339 | perror("unable to read pipe"); |
1340 | exit(ret); | 1340 | exit(ret); |
1341 | } | 1341 | } |
1342 | 1342 | ||
1343 | execvp(argv[0], (char **)argv); | 1343 | execvp(argv[0], (char **)argv); |
1344 | 1344 | ||
1345 | if (exec_error) { | 1345 | if (exec_error) { |
1346 | union sigval val; | 1346 | union sigval val; |
1347 | 1347 | ||
1348 | val.sival_int = errno; | 1348 | val.sival_int = errno; |
1349 | if (sigqueue(getppid(), SIGUSR1, val)) | 1349 | if (sigqueue(getppid(), SIGUSR1, val)) |
1350 | perror(argv[0]); | 1350 | perror(argv[0]); |
1351 | } else | 1351 | } else |
1352 | perror(argv[0]); | 1352 | perror(argv[0]); |
1353 | exit(-1); | 1353 | exit(-1); |
1354 | } | 1354 | } |
1355 | 1355 | ||
1356 | if (exec_error) { | 1356 | if (exec_error) { |
1357 | struct sigaction act = { | 1357 | struct sigaction act = { |
1358 | .sa_flags = SA_SIGINFO, | 1358 | .sa_flags = SA_SIGINFO, |
1359 | .sa_sigaction = exec_error, | 1359 | .sa_sigaction = exec_error, |
1360 | }; | 1360 | }; |
1361 | sigaction(SIGUSR1, &act, NULL); | 1361 | sigaction(SIGUSR1, &act, NULL); |
1362 | } | 1362 | } |
1363 | 1363 | ||
1364 | if (target__none(target)) { | 1364 | if (target__none(target)) { |
1365 | if (evlist->threads == NULL) { | 1365 | if (evlist->threads == NULL) { |
1366 | fprintf(stderr, "FATAL: evlist->threads need to be set at this point (%s:%d).\n", | 1366 | fprintf(stderr, "FATAL: evlist->threads need to be set at this point (%s:%d).\n", |
1367 | __func__, __LINE__); | 1367 | __func__, __LINE__); |
1368 | goto out_close_pipes; | 1368 | goto out_close_pipes; |
1369 | } | 1369 | } |
1370 | evlist->threads->map[0] = evlist->workload.pid; | 1370 | evlist->threads->map[0] = evlist->workload.pid; |
1371 | } | 1371 | } |
1372 | 1372 | ||
1373 | close(child_ready_pipe[1]); | 1373 | close(child_ready_pipe[1]); |
1374 | close(go_pipe[0]); | 1374 | close(go_pipe[0]); |
1375 | /* | 1375 | /* |
1376 | * wait for child to settle | 1376 | * wait for child to settle |
1377 | */ | 1377 | */ |
1378 | if (read(child_ready_pipe[0], &bf, 1) == -1) { | 1378 | if (read(child_ready_pipe[0], &bf, 1) == -1) { |
1379 | perror("unable to read pipe"); | 1379 | perror("unable to read pipe"); |
1380 | goto out_close_pipes; | 1380 | goto out_close_pipes; |
1381 | } | 1381 | } |
1382 | 1382 | ||
1383 | fcntl(go_pipe[1], F_SETFD, FD_CLOEXEC); | 1383 | fcntl(go_pipe[1], F_SETFD, FD_CLOEXEC); |
1384 | evlist->workload.cork_fd = go_pipe[1]; | 1384 | evlist->workload.cork_fd = go_pipe[1]; |
1385 | close(child_ready_pipe[0]); | 1385 | close(child_ready_pipe[0]); |
1386 | return 0; | 1386 | return 0; |
1387 | 1387 | ||
1388 | out_close_pipes: | 1388 | out_close_pipes: |
1389 | close(go_pipe[0]); | 1389 | close(go_pipe[0]); |
1390 | close(go_pipe[1]); | 1390 | close(go_pipe[1]); |
1391 | out_close_ready_pipe: | 1391 | out_close_ready_pipe: |
1392 | close(child_ready_pipe[0]); | 1392 | close(child_ready_pipe[0]); |
1393 | close(child_ready_pipe[1]); | 1393 | close(child_ready_pipe[1]); |
1394 | return -1; | 1394 | return -1; |
1395 | } | 1395 | } |
1396 | 1396 | ||
1397 | int perf_evlist__start_workload(struct perf_evlist *evlist) | 1397 | int perf_evlist__start_workload(struct perf_evlist *evlist) |
1398 | { | 1398 | { |
1399 | if (evlist->workload.cork_fd > 0) { | 1399 | if (evlist->workload.cork_fd > 0) { |
1400 | char bf = 0; | 1400 | char bf = 0; |
1401 | int ret; | 1401 | int ret; |
1402 | /* | 1402 | /* |
1403 | * Remove the cork, let it rip! | 1403 | * Remove the cork, let it rip! |
1404 | */ | 1404 | */ |
1405 | ret = write(evlist->workload.cork_fd, &bf, 1); | 1405 | ret = write(evlist->workload.cork_fd, &bf, 1); |
1406 | if (ret < 0) | 1406 | if (ret < 0) |
1407 | perror("enable to write to pipe"); | 1407 | perror("enable to write to pipe"); |
1408 | 1408 | ||
1409 | close(evlist->workload.cork_fd); | 1409 | close(evlist->workload.cork_fd); |
1410 | return ret; | 1410 | return ret; |
1411 | } | 1411 | } |
1412 | 1412 | ||
1413 | return 0; | 1413 | return 0; |
1414 | } | 1414 | } |
1415 | 1415 | ||
1416 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, | 1416 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, |
1417 | struct perf_sample *sample) | 1417 | struct perf_sample *sample) |
1418 | { | 1418 | { |
1419 | struct perf_evsel *evsel = perf_evlist__event2evsel(evlist, event); | 1419 | struct perf_evsel *evsel = perf_evlist__event2evsel(evlist, event); |
1420 | 1420 | ||
1421 | if (!evsel) | 1421 | if (!evsel) |
1422 | return -EFAULT; | 1422 | return -EFAULT; |
1423 | return perf_evsel__parse_sample(evsel, event, sample); | 1423 | return perf_evsel__parse_sample(evsel, event, sample); |
1424 | } | 1424 | } |
1425 | 1425 | ||
1426 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) | 1426 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) |
1427 | { | 1427 | { |
1428 | struct perf_evsel *evsel; | 1428 | struct perf_evsel *evsel; |
1429 | size_t printed = 0; | 1429 | size_t printed = 0; |
1430 | 1430 | ||
1431 | evlist__for_each(evlist, evsel) { | 1431 | evlist__for_each(evlist, evsel) { |
1432 | printed += fprintf(fp, "%s%s", evsel->idx ? ", " : "", | 1432 | printed += fprintf(fp, "%s%s", evsel->idx ? ", " : "", |
1433 | perf_evsel__name(evsel)); | 1433 | perf_evsel__name(evsel)); |
1434 | } | 1434 | } |
1435 | 1435 | ||
1436 | return printed + fprintf(fp, "\n"); | 1436 | return printed + fprintf(fp, "\n"); |
1437 | } | 1437 | } |
1438 | 1438 | ||
1439 | int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, | 1439 | int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, |
1440 | int err, char *buf, size_t size) | 1440 | int err, char *buf, size_t size) |
1441 | { | 1441 | { |
1442 | char sbuf[128]; | 1442 | char sbuf[128]; |
1443 | 1443 | ||
1444 | switch (err) { | 1444 | switch (err) { |
1445 | case ENOENT: | 1445 | case ENOENT: |
1446 | scnprintf(buf, size, "%s", | 1446 | scnprintf(buf, size, "%s", |
1447 | "Error:\tUnable to find debugfs\n" | 1447 | "Error:\tUnable to find debugfs\n" |
1448 | "Hint:\tWas your kernel was compiled with debugfs support?\n" | 1448 | "Hint:\tWas your kernel compiled with debugfs support?\n" |
1449 | "Hint:\tIs the debugfs filesystem mounted?\n" | 1449 | "Hint:\tIs the debugfs filesystem mounted?\n" |
1450 | "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); | 1450 | "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); |
1451 | break; | 1451 | break; |
1452 | case EACCES: | 1452 | case EACCES: |
1453 | scnprintf(buf, size, | 1453 | scnprintf(buf, size, |
1454 | "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" | 1454 | "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" |
1455 | "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", | 1455 | "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", |
1456 | debugfs_mountpoint, debugfs_mountpoint); | 1456 | debugfs_mountpoint, debugfs_mountpoint); |
1457 | break; | 1457 | break; |
1458 | default: | 1458 | default: |
1459 | scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); | 1459 | scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); |
1460 | break; | 1460 | break; |
1461 | } | 1461 | } |
1462 | 1462 | ||
1463 | return 0; | 1463 | return 0; |
1464 | } | 1464 | } |
1465 | 1465 | ||
1466 | int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | 1466 | int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, |
1467 | int err, char *buf, size_t size) | 1467 | int err, char *buf, size_t size) |
1468 | { | 1468 | { |
1469 | int printed, value; | 1469 | int printed, value; |
1470 | char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); | 1470 | char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); |
1471 | 1471 | ||
1472 | switch (err) { | 1472 | switch (err) { |
1473 | case EACCES: | 1473 | case EACCES: |
1474 | case EPERM: | 1474 | case EPERM: |
1475 | printed = scnprintf(buf, size, | 1475 | printed = scnprintf(buf, size, |
1476 | "Error:\t%s.\n" | 1476 | "Error:\t%s.\n" |
1477 | "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); | 1477 | "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); |
1478 | 1478 | ||
1479 | value = perf_event_paranoid(); | 1479 | value = perf_event_paranoid(); |
1480 | 1480 | ||
1481 | printed += scnprintf(buf + printed, size - printed, "\nHint:\t"); | 1481 | printed += scnprintf(buf + printed, size - printed, "\nHint:\t"); |
1482 | 1482 | ||
1483 | if (value >= 2) { | 1483 | if (value >= 2) { |
1484 | printed += scnprintf(buf + printed, size - printed, | 1484 | printed += scnprintf(buf + printed, size - printed, |
1485 | "For your workloads it needs to be <= 1\nHint:\t"); | 1485 | "For your workloads it needs to be <= 1\nHint:\t"); |
1486 | } | 1486 | } |
1487 | printed += scnprintf(buf + printed, size - printed, | 1487 | printed += scnprintf(buf + printed, size - printed, |
1488 | "For system wide tracing it needs to be set to -1.\n"); | 1488 | "For system wide tracing it needs to be set to -1.\n"); |
1489 | 1489 | ||
1490 | printed += scnprintf(buf + printed, size - printed, | 1490 | printed += scnprintf(buf + printed, size - printed, |
1491 | "Hint:\tTry: 'sudo sh -c \"echo -1 > /proc/sys/kernel/perf_event_paranoid\"'\n" | 1491 | "Hint:\tTry: 'sudo sh -c \"echo -1 > /proc/sys/kernel/perf_event_paranoid\"'\n" |
1492 | "Hint:\tThe current value is %d.", value); | 1492 | "Hint:\tThe current value is %d.", value); |
1493 | break; | 1493 | break; |
1494 | default: | 1494 | default: |
1495 | scnprintf(buf, size, "%s", emsg); | 1495 | scnprintf(buf, size, "%s", emsg); |
1496 | break; | 1496 | break; |
1497 | } | 1497 | } |
1498 | 1498 | ||
1499 | return 0; | 1499 | return 0; |
1500 | } | 1500 | } |
1501 | 1501 | ||
1502 | int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size) | 1502 | int perf_evlist__strerror_mmap(struct perf_evlist *evlist, int err, char *buf, size_t size) |
1503 | { | 1503 | { |
1504 | char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); | 1504 | char sbuf[STRERR_BUFSIZE], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); |
1505 | int pages_attempted = evlist->mmap_len / 1024, pages_max_per_user, printed = 0; | 1505 | int pages_attempted = evlist->mmap_len / 1024, pages_max_per_user, printed = 0; |
1506 | 1506 | ||
1507 | switch (err) { | 1507 | switch (err) { |
1508 | case EPERM: | 1508 | case EPERM: |
1509 | sysctl__read_int("kernel/perf_event_mlock_kb", &pages_max_per_user); | 1509 | sysctl__read_int("kernel/perf_event_mlock_kb", &pages_max_per_user); |
1510 | printed += scnprintf(buf + printed, size - printed, | 1510 | printed += scnprintf(buf + printed, size - printed, |
1511 | "Error:\t%s.\n" | 1511 | "Error:\t%s.\n" |
1512 | "Hint:\tCheck /proc/sys/kernel/perf_event_mlock_kb (%d kB) setting.\n" | 1512 | "Hint:\tCheck /proc/sys/kernel/perf_event_mlock_kb (%d kB) setting.\n" |
1513 | "Hint:\tTried using %zd kB.\n", | 1513 | "Hint:\tTried using %zd kB.\n", |
1514 | emsg, pages_max_per_user, pages_attempted); | 1514 | emsg, pages_max_per_user, pages_attempted); |
1515 | 1515 | ||
1516 | if (pages_attempted >= pages_max_per_user) { | 1516 | if (pages_attempted >= pages_max_per_user) { |
1517 | printed += scnprintf(buf + printed, size - printed, | 1517 | printed += scnprintf(buf + printed, size - printed, |
1518 | "Hint:\tTry 'sudo sh -c \"echo %d > /proc/sys/kernel/perf_event_mlock_kb\"', or\n", | 1518 | "Hint:\tTry 'sudo sh -c \"echo %d > /proc/sys/kernel/perf_event_mlock_kb\"', or\n", |
1519 | pages_max_per_user + pages_attempted); | 1519 | pages_max_per_user + pages_attempted); |
1520 | } | 1520 | } |
1521 | 1521 | ||
1522 | printed += scnprintf(buf + printed, size - printed, | 1522 | printed += scnprintf(buf + printed, size - printed, |
1523 | "Hint:\tTry using a smaller -m/--mmap-pages value."); | 1523 | "Hint:\tTry using a smaller -m/--mmap-pages value."); |
1524 | break; | 1524 | break; |
1525 | default: | 1525 | default: |
1526 | scnprintf(buf, size, "%s", emsg); | 1526 | scnprintf(buf, size, "%s", emsg); |
1527 | break; | 1527 | break; |
1528 | } | 1528 | } |
1529 | 1529 | ||
1530 | return 0; | 1530 | return 0; |
1531 | } | 1531 | } |
1532 | 1532 | ||
1533 | void perf_evlist__to_front(struct perf_evlist *evlist, | 1533 | void perf_evlist__to_front(struct perf_evlist *evlist, |
1534 | struct perf_evsel *move_evsel) | 1534 | struct perf_evsel *move_evsel) |
1535 | { | 1535 | { |
1536 | struct perf_evsel *evsel, *n; | 1536 | struct perf_evsel *evsel, *n; |
1537 | LIST_HEAD(move); | 1537 | LIST_HEAD(move); |
1538 | 1538 | ||
1539 | if (move_evsel == perf_evlist__first(evlist)) | 1539 | if (move_evsel == perf_evlist__first(evlist)) |
1540 | return; | 1540 | return; |
1541 | 1541 | ||
1542 | evlist__for_each_safe(evlist, n, evsel) { | 1542 | evlist__for_each_safe(evlist, n, evsel) { |
1543 | if (evsel->leader == move_evsel->leader) | 1543 | if (evsel->leader == move_evsel->leader) |
1544 | list_move_tail(&evsel->node, &move); | 1544 | list_move_tail(&evsel->node, &move); |
1545 | } | 1545 | } |
1546 | 1546 | ||
1547 | list_splice(&move, &evlist->entries); | 1547 | list_splice(&move, &evlist->entries); |
1548 | } | 1548 | } |
1549 | 1549 | ||
1550 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, | 1550 | void perf_evlist__set_tracking_event(struct perf_evlist *evlist, |
1551 | struct perf_evsel *tracking_evsel) | 1551 | struct perf_evsel *tracking_evsel) |
1552 | { | 1552 | { |
1553 | struct perf_evsel *evsel; | 1553 | struct perf_evsel *evsel; |
1554 | 1554 | ||
1555 | if (tracking_evsel->tracking) | 1555 | if (tracking_evsel->tracking) |
1556 | return; | 1556 | return; |
1557 | 1557 | ||
1558 | evlist__for_each(evlist, evsel) { | 1558 | evlist__for_each(evlist, evsel) { |
1559 | if (evsel != tracking_evsel) | 1559 | if (evsel != tracking_evsel) |
1560 | evsel->tracking = false; | 1560 | evsel->tracking = false; |
1561 | } | 1561 | } |
1562 | 1562 | ||
1563 | tracking_evsel->tracking = true; | 1563 | tracking_evsel->tracking = true; |
1564 | } | 1564 | } |
1565 | 1565 |
tools/perf/util/map.h
1 | #ifndef __PERF_MAP_H | 1 | #ifndef __PERF_MAP_H |
2 | #define __PERF_MAP_H | 2 | #define __PERF_MAP_H |
3 | 3 | ||
4 | #include <linux/compiler.h> | 4 | #include <linux/compiler.h> |
5 | #include <linux/list.h> | 5 | #include <linux/list.h> |
6 | #include <linux/rbtree.h> | 6 | #include <linux/rbtree.h> |
7 | #include <stdio.h> | 7 | #include <stdio.h> |
8 | #include <stdbool.h> | 8 | #include <stdbool.h> |
9 | #include <linux/types.h> | 9 | #include <linux/types.h> |
10 | 10 | ||
11 | enum map_type { | 11 | enum map_type { |
12 | MAP__FUNCTION = 0, | 12 | MAP__FUNCTION = 0, |
13 | MAP__VARIABLE, | 13 | MAP__VARIABLE, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | #define MAP__NR_TYPES (MAP__VARIABLE + 1) | 16 | #define MAP__NR_TYPES (MAP__VARIABLE + 1) |
17 | 17 | ||
18 | extern const char *map_type__name[MAP__NR_TYPES]; | 18 | extern const char *map_type__name[MAP__NR_TYPES]; |
19 | 19 | ||
20 | struct dso; | 20 | struct dso; |
21 | struct ip_callchain; | 21 | struct ip_callchain; |
22 | struct ref_reloc_sym; | 22 | struct ref_reloc_sym; |
23 | struct map_groups; | 23 | struct map_groups; |
24 | struct machine; | 24 | struct machine; |
25 | struct perf_evsel; | 25 | struct perf_evsel; |
26 | 26 | ||
27 | struct map { | 27 | struct map { |
28 | union { | 28 | union { |
29 | struct rb_node rb_node; | 29 | struct rb_node rb_node; |
30 | struct list_head node; | 30 | struct list_head node; |
31 | }; | 31 | }; |
32 | u64 start; | 32 | u64 start; |
33 | u64 end; | 33 | u64 end; |
34 | u8 /* enum map_type */ type; | 34 | u8 /* enum map_type */ type; |
35 | bool referenced; | 35 | bool referenced; |
36 | bool erange_warned; | 36 | bool erange_warned; |
37 | u32 priv; | 37 | u32 priv; |
38 | u32 prot; | 38 | u32 prot; |
39 | u32 flags; | 39 | u32 flags; |
40 | u64 pgoff; | 40 | u64 pgoff; |
41 | u64 reloc; | 41 | u64 reloc; |
42 | u32 maj, min; /* only valid for MMAP2 record */ | 42 | u32 maj, min; /* only valid for MMAP2 record */ |
43 | u64 ino; /* only valid for MMAP2 record */ | 43 | u64 ino; /* only valid for MMAP2 record */ |
44 | u64 ino_generation;/* only valid for MMAP2 record */ | 44 | u64 ino_generation;/* only valid for MMAP2 record */ |
45 | 45 | ||
46 | /* ip -> dso rip */ | 46 | /* ip -> dso rip */ |
47 | u64 (*map_ip)(struct map *, u64); | 47 | u64 (*map_ip)(struct map *, u64); |
48 | /* dso rip -> ip */ | 48 | /* dso rip -> ip */ |
49 | u64 (*unmap_ip)(struct map *, u64); | 49 | u64 (*unmap_ip)(struct map *, u64); |
50 | 50 | ||
51 | struct dso *dso; | 51 | struct dso *dso; |
52 | struct map_groups *groups; | 52 | struct map_groups *groups; |
53 | }; | 53 | }; |
54 | 54 | ||
55 | struct kmap { | 55 | struct kmap { |
56 | struct ref_reloc_sym *ref_reloc_sym; | 56 | struct ref_reloc_sym *ref_reloc_sym; |
57 | struct map_groups *kmaps; | 57 | struct map_groups *kmaps; |
58 | }; | 58 | }; |
59 | 59 | ||
60 | struct map_groups { | 60 | struct map_groups { |
61 | struct rb_root maps[MAP__NR_TYPES]; | 61 | struct rb_root maps[MAP__NR_TYPES]; |
62 | struct list_head removed_maps[MAP__NR_TYPES]; | 62 | struct list_head removed_maps[MAP__NR_TYPES]; |
63 | struct machine *machine; | 63 | struct machine *machine; |
64 | int refcnt; | 64 | int refcnt; |
65 | }; | 65 | }; |
66 | 66 | ||
67 | struct map_groups *map_groups__new(struct machine *machine); | 67 | struct map_groups *map_groups__new(struct machine *machine); |
68 | void map_groups__delete(struct map_groups *mg); | 68 | void map_groups__delete(struct map_groups *mg); |
69 | bool map_groups__empty(struct map_groups *mg); | 69 | bool map_groups__empty(struct map_groups *mg); |
70 | 70 | ||
71 | static inline struct map_groups *map_groups__get(struct map_groups *mg) | 71 | static inline struct map_groups *map_groups__get(struct map_groups *mg) |
72 | { | 72 | { |
73 | ++mg->refcnt; | 73 | ++mg->refcnt; |
74 | return mg; | 74 | return mg; |
75 | } | 75 | } |
76 | 76 | ||
77 | void map_groups__put(struct map_groups *mg); | 77 | void map_groups__put(struct map_groups *mg); |
78 | 78 | ||
79 | static inline struct kmap *map__kmap(struct map *map) | 79 | static inline struct kmap *map__kmap(struct map *map) |
80 | { | 80 | { |
81 | return (struct kmap *)(map + 1); | 81 | return (struct kmap *)(map + 1); |
82 | } | 82 | } |
83 | 83 | ||
84 | static inline u64 map__map_ip(struct map *map, u64 ip) | 84 | static inline u64 map__map_ip(struct map *map, u64 ip) |
85 | { | 85 | { |
86 | return ip - map->start + map->pgoff; | 86 | return ip - map->start + map->pgoff; |
87 | } | 87 | } |
88 | 88 | ||
89 | static inline u64 map__unmap_ip(struct map *map, u64 ip) | 89 | static inline u64 map__unmap_ip(struct map *map, u64 ip) |
90 | { | 90 | { |
91 | return ip + map->start - map->pgoff; | 91 | return ip + map->start - map->pgoff; |
92 | } | 92 | } |
93 | 93 | ||
94 | static inline u64 identity__map_ip(struct map *map __maybe_unused, u64 ip) | 94 | static inline u64 identity__map_ip(struct map *map __maybe_unused, u64 ip) |
95 | { | 95 | { |
96 | return ip; | 96 | return ip; |
97 | } | 97 | } |
98 | 98 | ||
99 | 99 | ||
100 | /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ | 100 | /* rip/ip <-> addr suitable for passing to `objdump --start-address=` */ |
101 | u64 map__rip_2objdump(struct map *map, u64 rip); | 101 | u64 map__rip_2objdump(struct map *map, u64 rip); |
102 | 102 | ||
103 | /* objdump address -> memory address */ | 103 | /* objdump address -> memory address */ |
104 | u64 map__objdump_2mem(struct map *map, u64 ip); | 104 | u64 map__objdump_2mem(struct map *map, u64 ip); |
105 | 105 | ||
106 | struct symbol; | 106 | struct symbol; |
107 | struct thread; | 107 | struct thread; |
108 | 108 | ||
109 | /* map__for_each_symbol - iterate over the symbols in the given map | 109 | /* map__for_each_symbol - iterate over the symbols in the given map |
110 | * | 110 | * |
111 | * @map: the 'struct map *' in which symbols itereated | 111 | * @map: the 'struct map *' in which symbols itereated |
112 | * @pos: the 'struct symbol *' to use as a loop cursor | 112 | * @pos: the 'struct symbol *' to use as a loop cursor |
113 | * @n: the 'struct rb_node *' to use as a temporary storage | 113 | * @n: the 'struct rb_node *' to use as a temporary storage |
114 | * Note: caller must ensure map->dso is not NULL (map is loaded). | 114 | * Note: caller must ensure map->dso is not NULL (map is loaded). |
115 | */ | 115 | */ |
116 | #define map__for_each_symbol(map, pos, n) \ | 116 | #define map__for_each_symbol(map, pos, n) \ |
117 | dso__for_each_symbol(map->dso, pos, n, map->type) | 117 | dso__for_each_symbol(map->dso, pos, n, map->type) |
118 | 118 | ||
119 | /* map__for_each_symbol_with_name - iterate over the symbols in the given map | ||
120 | * that have the given name | ||
121 | * | ||
122 | * @map: the 'struct map *' in which symbols itereated | ||
123 | * @sym_name: the symbol name | ||
124 | * @pos: the 'struct symbol *' to use as a loop cursor | ||
125 | * @filter: to use when loading the DSO | ||
126 | */ | ||
127 | #define __map__for_each_symbol_by_name(map, sym_name, pos, filter) \ | ||
128 | for (pos = map__find_symbol_by_name(map, sym_name, filter); \ | ||
129 | pos && strcmp(pos->name, sym_name) == 0; \ | ||
130 | pos = symbol__next_by_name(pos)) | ||
131 | |||
132 | #define map__for_each_symbol_by_name(map, sym_name, pos) \ | ||
133 | __map__for_each_symbol_by_name(map, sym_name, (pos), NULL) | ||
134 | |||
119 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | 135 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); |
120 | 136 | ||
121 | void map__init(struct map *map, enum map_type type, | 137 | void map__init(struct map *map, enum map_type type, |
122 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 138 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
123 | struct map *map__new(struct machine *machine, u64 start, u64 len, | 139 | struct map *map__new(struct machine *machine, u64 start, u64 len, |
124 | u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, | 140 | u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, |
125 | u64 ino_gen, u32 prot, u32 flags, | 141 | u64 ino_gen, u32 prot, u32 flags, |
126 | char *filename, enum map_type type, struct thread *thread); | 142 | char *filename, enum map_type type, struct thread *thread); |
127 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 143 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
128 | void map__delete(struct map *map); | 144 | void map__delete(struct map *map); |
129 | struct map *map__clone(struct map *map); | 145 | struct map *map__clone(struct map *map); |
130 | int map__overlap(struct map *l, struct map *r); | 146 | int map__overlap(struct map *l, struct map *r); |
131 | size_t map__fprintf(struct map *map, FILE *fp); | 147 | size_t map__fprintf(struct map *map, FILE *fp); |
132 | size_t map__fprintf_dsoname(struct map *map, FILE *fp); | 148 | size_t map__fprintf_dsoname(struct map *map, FILE *fp); |
133 | int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, | 149 | int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, |
134 | FILE *fp); | 150 | FILE *fp); |
135 | 151 | ||
136 | int map__load(struct map *map, symbol_filter_t filter); | 152 | int map__load(struct map *map, symbol_filter_t filter); |
137 | struct symbol *map__find_symbol(struct map *map, | 153 | struct symbol *map__find_symbol(struct map *map, |
138 | u64 addr, symbol_filter_t filter); | 154 | u64 addr, symbol_filter_t filter); |
139 | struct symbol *map__find_symbol_by_name(struct map *map, const char *name, | 155 | struct symbol *map__find_symbol_by_name(struct map *map, const char *name, |
140 | symbol_filter_t filter); | 156 | symbol_filter_t filter); |
141 | void map__fixup_start(struct map *map); | 157 | void map__fixup_start(struct map *map); |
142 | void map__fixup_end(struct map *map); | 158 | void map__fixup_end(struct map *map); |
143 | 159 | ||
144 | void map__reloc_vmlinux(struct map *map); | 160 | void map__reloc_vmlinux(struct map *map); |
145 | 161 | ||
146 | size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, | 162 | size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, |
147 | FILE *fp); | 163 | FILE *fp); |
148 | void maps__insert(struct rb_root *maps, struct map *map); | 164 | void maps__insert(struct rb_root *maps, struct map *map); |
149 | void maps__remove(struct rb_root *maps, struct map *map); | 165 | void maps__remove(struct rb_root *maps, struct map *map); |
150 | struct map *maps__find(struct rb_root *maps, u64 addr); | 166 | struct map *maps__find(struct rb_root *maps, u64 addr); |
151 | struct map *maps__first(struct rb_root *maps); | 167 | struct map *maps__first(struct rb_root *maps); |
152 | struct map *maps__next(struct map *map); | 168 | struct map *maps__next(struct map *map); |
153 | void map_groups__init(struct map_groups *mg, struct machine *machine); | 169 | void map_groups__init(struct map_groups *mg, struct machine *machine); |
154 | void map_groups__exit(struct map_groups *mg); | 170 | void map_groups__exit(struct map_groups *mg); |
155 | int map_groups__clone(struct map_groups *mg, | 171 | int map_groups__clone(struct map_groups *mg, |
156 | struct map_groups *parent, enum map_type type); | 172 | struct map_groups *parent, enum map_type type); |
157 | size_t map_groups__fprintf(struct map_groups *mg, FILE *fp); | 173 | size_t map_groups__fprintf(struct map_groups *mg, FILE *fp); |
158 | 174 | ||
159 | int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, | 175 | int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, |
160 | u64 addr); | 176 | u64 addr); |
161 | 177 | ||
162 | static inline void map_groups__insert(struct map_groups *mg, struct map *map) | 178 | static inline void map_groups__insert(struct map_groups *mg, struct map *map) |
163 | { | 179 | { |
164 | maps__insert(&mg->maps[map->type], map); | 180 | maps__insert(&mg->maps[map->type], map); |
165 | map->groups = mg; | 181 | map->groups = mg; |
166 | } | 182 | } |
167 | 183 | ||
168 | static inline void map_groups__remove(struct map_groups *mg, struct map *map) | 184 | static inline void map_groups__remove(struct map_groups *mg, struct map *map) |
169 | { | 185 | { |
170 | maps__remove(&mg->maps[map->type], map); | 186 | maps__remove(&mg->maps[map->type], map); |
171 | } | 187 | } |
172 | 188 | ||
173 | static inline struct map *map_groups__find(struct map_groups *mg, | 189 | static inline struct map *map_groups__find(struct map_groups *mg, |
174 | enum map_type type, u64 addr) | 190 | enum map_type type, u64 addr) |
175 | { | 191 | { |
176 | return maps__find(&mg->maps[type], addr); | 192 | return maps__find(&mg->maps[type], addr); |
177 | } | 193 | } |
178 | 194 | ||
179 | static inline struct map *map_groups__first(struct map_groups *mg, | 195 | static inline struct map *map_groups__first(struct map_groups *mg, |
180 | enum map_type type) | 196 | enum map_type type) |
181 | { | 197 | { |
182 | return maps__first(&mg->maps[type]); | 198 | return maps__first(&mg->maps[type]); |
183 | } | 199 | } |
184 | 200 | ||
185 | static inline struct map *map_groups__next(struct map *map) | 201 | static inline struct map *map_groups__next(struct map *map) |
186 | { | 202 | { |
187 | return maps__next(map); | 203 | return maps__next(map); |
188 | } | 204 | } |
189 | 205 | ||
190 | struct symbol *map_groups__find_symbol(struct map_groups *mg, | 206 | struct symbol *map_groups__find_symbol(struct map_groups *mg, |
191 | enum map_type type, u64 addr, | 207 | enum map_type type, u64 addr, |
192 | struct map **mapp, | 208 | struct map **mapp, |
193 | symbol_filter_t filter); | 209 | symbol_filter_t filter); |
194 | 210 | ||
195 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, | 211 | struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, |
196 | enum map_type type, | 212 | enum map_type type, |
197 | const char *name, | 213 | const char *name, |
198 | struct map **mapp, | 214 | struct map **mapp, |
199 | symbol_filter_t filter); | 215 | symbol_filter_t filter); |
200 | 216 | ||
201 | struct addr_map_symbol; | 217 | struct addr_map_symbol; |
202 | 218 | ||
203 | int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter); | 219 | int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter); |
204 | 220 | ||
205 | static inline | 221 | static inline |
206 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, | 222 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, |
207 | const char *name, struct map **mapp, | 223 | const char *name, struct map **mapp, |
208 | symbol_filter_t filter) | 224 | symbol_filter_t filter) |
209 | { | 225 | { |
210 | return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); | 226 | return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); |
211 | } | 227 | } |
212 | 228 | ||
213 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, | 229 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, |
214 | FILE *fp); | 230 | FILE *fp); |
215 | 231 | ||
216 | struct map *map_groups__find_by_name(struct map_groups *mg, | 232 | struct map *map_groups__find_by_name(struct map_groups *mg, |
217 | enum map_type type, const char *name); | 233 | enum map_type type, const char *name); |
218 | 234 | ||
219 | void map_groups__flush(struct map_groups *mg); | 235 | void map_groups__flush(struct map_groups *mg); |
220 | 236 | ||
221 | #endif /* __PERF_MAP_H */ | 237 | #endif /* __PERF_MAP_H */ |
222 | 238 |
tools/perf/util/probe-event.c
1 | /* | 1 | /* |
2 | * probe-event.c : perf-probe definition to probe_events format converter | 2 | * probe-event.c : perf-probe definition to probe_events format converter |
3 | * | 3 | * |
4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> | 4 | * Written by Masami Hiramatsu <mhiramat@redhat.com> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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 | 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 | 8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. | 9 | * (at your option) any later version. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, | 11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License | 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 | 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. | 18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
19 | * | 19 | * |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <sys/utsname.h> | 22 | #include <sys/utsname.h> |
23 | #include <sys/types.h> | 23 | #include <sys/types.h> |
24 | #include <sys/stat.h> | 24 | #include <sys/stat.h> |
25 | #include <fcntl.h> | 25 | #include <fcntl.h> |
26 | #include <errno.h> | 26 | #include <errno.h> |
27 | #include <stdio.h> | 27 | #include <stdio.h> |
28 | #include <unistd.h> | 28 | #include <unistd.h> |
29 | #include <stdlib.h> | 29 | #include <stdlib.h> |
30 | #include <string.h> | 30 | #include <string.h> |
31 | #include <stdarg.h> | 31 | #include <stdarg.h> |
32 | #include <limits.h> | 32 | #include <limits.h> |
33 | #include <elf.h> | 33 | #include <elf.h> |
34 | 34 | ||
35 | #include "util.h" | 35 | #include "util.h" |
36 | #include "event.h" | 36 | #include "event.h" |
37 | #include "strlist.h" | 37 | #include "strlist.h" |
38 | #include "debug.h" | 38 | #include "debug.h" |
39 | #include "cache.h" | 39 | #include "cache.h" |
40 | #include "color.h" | 40 | #include "color.h" |
41 | #include "symbol.h" | 41 | #include "symbol.h" |
42 | #include "thread.h" | 42 | #include "thread.h" |
43 | #include <api/fs/debugfs.h> | 43 | #include <api/fs/debugfs.h> |
44 | #include "trace-event.h" /* For __maybe_unused */ | 44 | #include "trace-event.h" /* For __maybe_unused */ |
45 | #include "probe-event.h" | 45 | #include "probe-event.h" |
46 | #include "probe-finder.h" | 46 | #include "probe-finder.h" |
47 | #include "session.h" | 47 | #include "session.h" |
48 | 48 | ||
49 | #define MAX_CMDLEN 256 | 49 | #define MAX_CMDLEN 256 |
50 | #define PERFPROBE_GROUP "probe" | 50 | #define PERFPROBE_GROUP "probe" |
51 | 51 | ||
52 | bool probe_event_dry_run; /* Dry run flag */ | 52 | bool probe_event_dry_run; /* Dry run flag */ |
53 | 53 | ||
54 | #define semantic_error(msg ...) pr_err("Semantic error :" msg) | 54 | #define semantic_error(msg ...) pr_err("Semantic error :" msg) |
55 | 55 | ||
56 | /* If there is no space to write, returns -E2BIG. */ | 56 | /* If there is no space to write, returns -E2BIG. */ |
57 | static int e_snprintf(char *str, size_t size, const char *format, ...) | 57 | static int e_snprintf(char *str, size_t size, const char *format, ...) |
58 | __attribute__((format(printf, 3, 4))); | 58 | __attribute__((format(printf, 3, 4))); |
59 | 59 | ||
60 | static int e_snprintf(char *str, size_t size, const char *format, ...) | 60 | static int e_snprintf(char *str, size_t size, const char *format, ...) |
61 | { | 61 | { |
62 | int ret; | 62 | int ret; |
63 | va_list ap; | 63 | va_list ap; |
64 | va_start(ap, format); | 64 | va_start(ap, format); |
65 | ret = vsnprintf(str, size, format, ap); | 65 | ret = vsnprintf(str, size, format, ap); |
66 | va_end(ap); | 66 | va_end(ap); |
67 | if (ret >= (int)size) | 67 | if (ret >= (int)size) |
68 | ret = -E2BIG; | 68 | ret = -E2BIG; |
69 | return ret; | 69 | return ret; |
70 | } | 70 | } |
71 | 71 | ||
72 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp); | 72 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp); |
73 | static void clear_probe_trace_event(struct probe_trace_event *tev); | 73 | static void clear_probe_trace_event(struct probe_trace_event *tev); |
74 | static struct machine *host_machine; | 74 | static struct machine *host_machine; |
75 | 75 | ||
76 | /* Initialize symbol maps and path of vmlinux/modules */ | 76 | /* Initialize symbol maps and path of vmlinux/modules */ |
77 | static int init_symbol_maps(bool user_only) | 77 | static int init_symbol_maps(bool user_only) |
78 | { | 78 | { |
79 | int ret; | 79 | int ret; |
80 | 80 | ||
81 | symbol_conf.sort_by_name = true; | 81 | symbol_conf.sort_by_name = true; |
82 | ret = symbol__init(NULL); | 82 | ret = symbol__init(NULL); |
83 | if (ret < 0) { | 83 | if (ret < 0) { |
84 | pr_debug("Failed to init symbol map.\n"); | 84 | pr_debug("Failed to init symbol map.\n"); |
85 | goto out; | 85 | goto out; |
86 | } | 86 | } |
87 | 87 | ||
88 | if (host_machine || user_only) /* already initialized */ | 88 | if (host_machine || user_only) /* already initialized */ |
89 | return 0; | 89 | return 0; |
90 | 90 | ||
91 | if (symbol_conf.vmlinux_name) | 91 | if (symbol_conf.vmlinux_name) |
92 | pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); | 92 | pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); |
93 | 93 | ||
94 | host_machine = machine__new_host(); | 94 | host_machine = machine__new_host(); |
95 | if (!host_machine) { | 95 | if (!host_machine) { |
96 | pr_debug("machine__new_host() failed.\n"); | 96 | pr_debug("machine__new_host() failed.\n"); |
97 | symbol__exit(); | 97 | symbol__exit(); |
98 | ret = -1; | 98 | ret = -1; |
99 | } | 99 | } |
100 | out: | 100 | out: |
101 | if (ret < 0) | 101 | if (ret < 0) |
102 | pr_warning("Failed to init vmlinux path.\n"); | 102 | pr_warning("Failed to init vmlinux path.\n"); |
103 | return ret; | 103 | return ret; |
104 | } | 104 | } |
105 | 105 | ||
106 | static void exit_symbol_maps(void) | 106 | static void exit_symbol_maps(void) |
107 | { | 107 | { |
108 | if (host_machine) { | 108 | if (host_machine) { |
109 | machine__delete(host_machine); | 109 | machine__delete(host_machine); |
110 | host_machine = NULL; | 110 | host_machine = NULL; |
111 | } | 111 | } |
112 | symbol__exit(); | 112 | symbol__exit(); |
113 | } | 113 | } |
114 | 114 | ||
115 | static struct symbol *__find_kernel_function_by_name(const char *name, | 115 | static struct symbol *__find_kernel_function_by_name(const char *name, |
116 | struct map **mapp) | 116 | struct map **mapp) |
117 | { | 117 | { |
118 | return machine__find_kernel_function_by_name(host_machine, name, mapp, | 118 | return machine__find_kernel_function_by_name(host_machine, name, mapp, |
119 | NULL); | 119 | NULL); |
120 | } | 120 | } |
121 | 121 | ||
122 | static struct symbol *__find_kernel_function(u64 addr, struct map **mapp) | 122 | static struct symbol *__find_kernel_function(u64 addr, struct map **mapp) |
123 | { | 123 | { |
124 | return machine__find_kernel_function(host_machine, addr, mapp, NULL); | 124 | return machine__find_kernel_function(host_machine, addr, mapp, NULL); |
125 | } | 125 | } |
126 | 126 | ||
127 | static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) | 127 | static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) |
128 | { | 128 | { |
129 | /* kmap->ref_reloc_sym should be set if host_machine is initialized */ | 129 | /* kmap->ref_reloc_sym should be set if host_machine is initialized */ |
130 | struct kmap *kmap; | 130 | struct kmap *kmap; |
131 | 131 | ||
132 | if (map__load(host_machine->vmlinux_maps[MAP__FUNCTION], NULL) < 0) | 132 | if (map__load(host_machine->vmlinux_maps[MAP__FUNCTION], NULL) < 0) |
133 | return NULL; | 133 | return NULL; |
134 | 134 | ||
135 | kmap = map__kmap(host_machine->vmlinux_maps[MAP__FUNCTION]); | 135 | kmap = map__kmap(host_machine->vmlinux_maps[MAP__FUNCTION]); |
136 | return kmap->ref_reloc_sym; | 136 | return kmap->ref_reloc_sym; |
137 | } | 137 | } |
138 | 138 | ||
139 | static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) | 139 | static u64 kernel_get_symbol_address_by_name(const char *name, bool reloc) |
140 | { | 140 | { |
141 | struct ref_reloc_sym *reloc_sym; | 141 | struct ref_reloc_sym *reloc_sym; |
142 | struct symbol *sym; | 142 | struct symbol *sym; |
143 | struct map *map; | 143 | struct map *map; |
144 | 144 | ||
145 | /* ref_reloc_sym is just a label. Need a special fix*/ | 145 | /* ref_reloc_sym is just a label. Need a special fix*/ |
146 | reloc_sym = kernel_get_ref_reloc_sym(); | 146 | reloc_sym = kernel_get_ref_reloc_sym(); |
147 | if (reloc_sym && strcmp(name, reloc_sym->name) == 0) | 147 | if (reloc_sym && strcmp(name, reloc_sym->name) == 0) |
148 | return (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; | 148 | return (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; |
149 | else { | 149 | else { |
150 | sym = __find_kernel_function_by_name(name, &map); | 150 | sym = __find_kernel_function_by_name(name, &map); |
151 | if (sym) | 151 | if (sym) |
152 | return map->unmap_ip(map, sym->start) - | 152 | return map->unmap_ip(map, sym->start) - |
153 | (reloc) ? 0 : map->reloc; | 153 | (reloc) ? 0 : map->reloc; |
154 | } | 154 | } |
155 | return 0; | 155 | return 0; |
156 | } | 156 | } |
157 | 157 | ||
158 | static struct map *kernel_get_module_map(const char *module) | 158 | static struct map *kernel_get_module_map(const char *module) |
159 | { | 159 | { |
160 | struct rb_node *nd; | 160 | struct rb_node *nd; |
161 | struct map_groups *grp = &host_machine->kmaps; | 161 | struct map_groups *grp = &host_machine->kmaps; |
162 | 162 | ||
163 | /* A file path -- this is an offline module */ | 163 | /* A file path -- this is an offline module */ |
164 | if (module && strchr(module, '/')) | 164 | if (module && strchr(module, '/')) |
165 | return machine__new_module(host_machine, 0, module); | 165 | return machine__new_module(host_machine, 0, module); |
166 | 166 | ||
167 | if (!module) | 167 | if (!module) |
168 | module = "kernel"; | 168 | module = "kernel"; |
169 | 169 | ||
170 | for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { | 170 | for (nd = rb_first(&grp->maps[MAP__FUNCTION]); nd; nd = rb_next(nd)) { |
171 | struct map *pos = rb_entry(nd, struct map, rb_node); | 171 | struct map *pos = rb_entry(nd, struct map, rb_node); |
172 | if (strncmp(pos->dso->short_name + 1, module, | 172 | if (strncmp(pos->dso->short_name + 1, module, |
173 | pos->dso->short_name_len - 2) == 0) { | 173 | pos->dso->short_name_len - 2) == 0) { |
174 | return pos; | 174 | return pos; |
175 | } | 175 | } |
176 | } | 176 | } |
177 | return NULL; | 177 | return NULL; |
178 | } | 178 | } |
179 | 179 | ||
180 | static struct dso *kernel_get_module_dso(const char *module) | 180 | static struct dso *kernel_get_module_dso(const char *module) |
181 | { | 181 | { |
182 | struct dso *dso; | 182 | struct dso *dso; |
183 | struct map *map; | 183 | struct map *map; |
184 | const char *vmlinux_name; | 184 | const char *vmlinux_name; |
185 | 185 | ||
186 | if (module) { | 186 | if (module) { |
187 | list_for_each_entry(dso, &host_machine->kernel_dsos.head, | 187 | list_for_each_entry(dso, &host_machine->kernel_dsos.head, |
188 | node) { | 188 | node) { |
189 | if (strncmp(dso->short_name + 1, module, | 189 | if (strncmp(dso->short_name + 1, module, |
190 | dso->short_name_len - 2) == 0) | 190 | dso->short_name_len - 2) == 0) |
191 | goto found; | 191 | goto found; |
192 | } | 192 | } |
193 | pr_debug("Failed to find module %s.\n", module); | 193 | pr_debug("Failed to find module %s.\n", module); |
194 | return NULL; | 194 | return NULL; |
195 | } | 195 | } |
196 | 196 | ||
197 | map = host_machine->vmlinux_maps[MAP__FUNCTION]; | 197 | map = host_machine->vmlinux_maps[MAP__FUNCTION]; |
198 | dso = map->dso; | 198 | dso = map->dso; |
199 | 199 | ||
200 | vmlinux_name = symbol_conf.vmlinux_name; | 200 | vmlinux_name = symbol_conf.vmlinux_name; |
201 | if (vmlinux_name) { | 201 | if (vmlinux_name) { |
202 | if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0) | 202 | if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0) |
203 | return NULL; | 203 | return NULL; |
204 | } else { | 204 | } else { |
205 | if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { | 205 | if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { |
206 | pr_debug("Failed to load kernel map.\n"); | 206 | pr_debug("Failed to load kernel map.\n"); |
207 | return NULL; | 207 | return NULL; |
208 | } | 208 | } |
209 | } | 209 | } |
210 | found: | 210 | found: |
211 | return dso; | 211 | return dso; |
212 | } | 212 | } |
213 | 213 | ||
214 | const char *kernel_get_module_path(const char *module) | 214 | const char *kernel_get_module_path(const char *module) |
215 | { | 215 | { |
216 | struct dso *dso = kernel_get_module_dso(module); | 216 | struct dso *dso = kernel_get_module_dso(module); |
217 | return (dso) ? dso->long_name : NULL; | 217 | return (dso) ? dso->long_name : NULL; |
218 | } | 218 | } |
219 | 219 | ||
220 | static int convert_exec_to_group(const char *exec, char **result) | 220 | static int convert_exec_to_group(const char *exec, char **result) |
221 | { | 221 | { |
222 | char *ptr1, *ptr2, *exec_copy; | 222 | char *ptr1, *ptr2, *exec_copy; |
223 | char buf[64]; | 223 | char buf[64]; |
224 | int ret; | 224 | int ret; |
225 | 225 | ||
226 | exec_copy = strdup(exec); | 226 | exec_copy = strdup(exec); |
227 | if (!exec_copy) | 227 | if (!exec_copy) |
228 | return -ENOMEM; | 228 | return -ENOMEM; |
229 | 229 | ||
230 | ptr1 = basename(exec_copy); | 230 | ptr1 = basename(exec_copy); |
231 | if (!ptr1) { | 231 | if (!ptr1) { |
232 | ret = -EINVAL; | 232 | ret = -EINVAL; |
233 | goto out; | 233 | goto out; |
234 | } | 234 | } |
235 | 235 | ||
236 | ptr2 = strpbrk(ptr1, "-._"); | 236 | ptr2 = strpbrk(ptr1, "-._"); |
237 | if (ptr2) | 237 | if (ptr2) |
238 | *ptr2 = '\0'; | 238 | *ptr2 = '\0'; |
239 | ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1); | 239 | ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1); |
240 | if (ret < 0) | 240 | if (ret < 0) |
241 | goto out; | 241 | goto out; |
242 | 242 | ||
243 | *result = strdup(buf); | 243 | *result = strdup(buf); |
244 | ret = *result ? 0 : -ENOMEM; | 244 | ret = *result ? 0 : -ENOMEM; |
245 | 245 | ||
246 | out: | 246 | out: |
247 | free(exec_copy); | 247 | free(exec_copy); |
248 | return ret; | 248 | return ret; |
249 | } | 249 | } |
250 | 250 | ||
251 | static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) | 251 | static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) |
252 | { | 252 | { |
253 | int i; | 253 | int i; |
254 | 254 | ||
255 | for (i = 0; i < ntevs; i++) | 255 | for (i = 0; i < ntevs; i++) |
256 | clear_probe_trace_event(tevs + i); | 256 | clear_probe_trace_event(tevs + i); |
257 | } | 257 | } |
258 | 258 | ||
259 | #ifdef HAVE_DWARF_SUPPORT | 259 | #ifdef HAVE_DWARF_SUPPORT |
260 | 260 | ||
261 | /* Open new debuginfo of given module */ | 261 | /* Open new debuginfo of given module */ |
262 | static struct debuginfo *open_debuginfo(const char *module, bool silent) | 262 | static struct debuginfo *open_debuginfo(const char *module, bool silent) |
263 | { | 263 | { |
264 | const char *path = module; | 264 | const char *path = module; |
265 | struct debuginfo *ret; | 265 | struct debuginfo *ret; |
266 | 266 | ||
267 | if (!module || !strchr(module, '/')) { | 267 | if (!module || !strchr(module, '/')) { |
268 | path = kernel_get_module_path(module); | 268 | path = kernel_get_module_path(module); |
269 | if (!path) { | 269 | if (!path) { |
270 | if (!silent) | 270 | if (!silent) |
271 | pr_err("Failed to find path of %s module.\n", | 271 | pr_err("Failed to find path of %s module.\n", |
272 | module ?: "kernel"); | 272 | module ?: "kernel"); |
273 | return NULL; | 273 | return NULL; |
274 | } | 274 | } |
275 | } | 275 | } |
276 | ret = debuginfo__new(path); | 276 | ret = debuginfo__new(path); |
277 | if (!ret && !silent) { | 277 | if (!ret && !silent) { |
278 | pr_warning("The %s file has no debug information.\n", path); | 278 | pr_warning("The %s file has no debug information.\n", path); |
279 | if (!module || !strtailcmp(path, ".ko")) | 279 | if (!module || !strtailcmp(path, ".ko")) |
280 | pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, "); | 280 | pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, "); |
281 | else | 281 | else |
282 | pr_warning("Rebuild with -g, "); | 282 | pr_warning("Rebuild with -g, "); |
283 | pr_warning("or install an appropriate debuginfo package.\n"); | 283 | pr_warning("or install an appropriate debuginfo package.\n"); |
284 | } | 284 | } |
285 | return ret; | 285 | return ret; |
286 | } | 286 | } |
287 | 287 | ||
288 | 288 | ||
289 | static int get_text_start_address(const char *exec, unsigned long *address) | 289 | static int get_text_start_address(const char *exec, unsigned long *address) |
290 | { | 290 | { |
291 | Elf *elf; | 291 | Elf *elf; |
292 | GElf_Ehdr ehdr; | 292 | GElf_Ehdr ehdr; |
293 | GElf_Shdr shdr; | 293 | GElf_Shdr shdr; |
294 | int fd, ret = -ENOENT; | 294 | int fd, ret = -ENOENT; |
295 | 295 | ||
296 | fd = open(exec, O_RDONLY); | 296 | fd = open(exec, O_RDONLY); |
297 | if (fd < 0) | 297 | if (fd < 0) |
298 | return -errno; | 298 | return -errno; |
299 | 299 | ||
300 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 300 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
301 | if (elf == NULL) | 301 | if (elf == NULL) |
302 | return -EINVAL; | 302 | return -EINVAL; |
303 | 303 | ||
304 | if (gelf_getehdr(elf, &ehdr) == NULL) | 304 | if (gelf_getehdr(elf, &ehdr) == NULL) |
305 | goto out; | 305 | goto out; |
306 | 306 | ||
307 | if (!elf_section_by_name(elf, &ehdr, &shdr, ".text", NULL)) | 307 | if (!elf_section_by_name(elf, &ehdr, &shdr, ".text", NULL)) |
308 | goto out; | 308 | goto out; |
309 | 309 | ||
310 | *address = shdr.sh_addr - shdr.sh_offset; | 310 | *address = shdr.sh_addr - shdr.sh_offset; |
311 | ret = 0; | 311 | ret = 0; |
312 | out: | 312 | out: |
313 | elf_end(elf); | 313 | elf_end(elf); |
314 | return ret; | 314 | return ret; |
315 | } | 315 | } |
316 | 316 | ||
317 | /* | 317 | /* |
318 | * Convert trace point to probe point with debuginfo | 318 | * Convert trace point to probe point with debuginfo |
319 | */ | 319 | */ |
320 | static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, | 320 | static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, |
321 | struct perf_probe_point *pp, | 321 | struct perf_probe_point *pp, |
322 | bool is_kprobe) | 322 | bool is_kprobe) |
323 | { | 323 | { |
324 | struct debuginfo *dinfo = NULL; | 324 | struct debuginfo *dinfo = NULL; |
325 | unsigned long stext = 0; | 325 | unsigned long stext = 0; |
326 | u64 addr = tp->address; | 326 | u64 addr = tp->address; |
327 | int ret = -ENOENT; | 327 | int ret = -ENOENT; |
328 | 328 | ||
329 | /* convert the address to dwarf address */ | 329 | /* convert the address to dwarf address */ |
330 | if (!is_kprobe) { | 330 | if (!is_kprobe) { |
331 | if (!addr) { | 331 | if (!addr) { |
332 | ret = -EINVAL; | 332 | ret = -EINVAL; |
333 | goto error; | 333 | goto error; |
334 | } | 334 | } |
335 | ret = get_text_start_address(tp->module, &stext); | 335 | ret = get_text_start_address(tp->module, &stext); |
336 | if (ret < 0) | 336 | if (ret < 0) |
337 | goto error; | 337 | goto error; |
338 | addr += stext; | 338 | addr += stext; |
339 | } else { | 339 | } else { |
340 | addr = kernel_get_symbol_address_by_name(tp->symbol, false); | 340 | addr = kernel_get_symbol_address_by_name(tp->symbol, false); |
341 | if (addr == 0) | 341 | if (addr == 0) |
342 | goto error; | 342 | goto error; |
343 | addr += tp->offset; | 343 | addr += tp->offset; |
344 | } | 344 | } |
345 | 345 | ||
346 | pr_debug("try to find information at %" PRIx64 " in %s\n", addr, | 346 | pr_debug("try to find information at %" PRIx64 " in %s\n", addr, |
347 | tp->module ? : "kernel"); | 347 | tp->module ? : "kernel"); |
348 | 348 | ||
349 | dinfo = open_debuginfo(tp->module, verbose == 0); | 349 | dinfo = open_debuginfo(tp->module, verbose == 0); |
350 | if (dinfo) { | 350 | if (dinfo) { |
351 | ret = debuginfo__find_probe_point(dinfo, | 351 | ret = debuginfo__find_probe_point(dinfo, |
352 | (unsigned long)addr, pp); | 352 | (unsigned long)addr, pp); |
353 | debuginfo__delete(dinfo); | 353 | debuginfo__delete(dinfo); |
354 | } else | 354 | } else |
355 | ret = -ENOENT; | 355 | ret = -ENOENT; |
356 | 356 | ||
357 | if (ret > 0) { | 357 | if (ret > 0) { |
358 | pp->retprobe = tp->retprobe; | 358 | pp->retprobe = tp->retprobe; |
359 | return 0; | 359 | return 0; |
360 | } | 360 | } |
361 | error: | 361 | error: |
362 | pr_debug("Failed to find corresponding probes from debuginfo.\n"); | 362 | pr_debug("Failed to find corresponding probes from debuginfo.\n"); |
363 | return ret ? : -ENOENT; | 363 | return ret ? : -ENOENT; |
364 | } | 364 | } |
365 | 365 | ||
366 | static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs, | 366 | static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs, |
367 | int ntevs, const char *exec) | 367 | int ntevs, const char *exec) |
368 | { | 368 | { |
369 | int i, ret = 0; | 369 | int i, ret = 0; |
370 | unsigned long stext = 0; | 370 | unsigned long stext = 0; |
371 | 371 | ||
372 | if (!exec) | 372 | if (!exec) |
373 | return 0; | 373 | return 0; |
374 | 374 | ||
375 | ret = get_text_start_address(exec, &stext); | 375 | ret = get_text_start_address(exec, &stext); |
376 | if (ret < 0) | 376 | if (ret < 0) |
377 | return ret; | 377 | return ret; |
378 | 378 | ||
379 | for (i = 0; i < ntevs && ret >= 0; i++) { | 379 | for (i = 0; i < ntevs && ret >= 0; i++) { |
380 | /* point.address is the addres of point.symbol + point.offset */ | 380 | /* point.address is the addres of point.symbol + point.offset */ |
381 | tevs[i].point.address -= stext; | 381 | tevs[i].point.address -= stext; |
382 | tevs[i].point.module = strdup(exec); | 382 | tevs[i].point.module = strdup(exec); |
383 | if (!tevs[i].point.module) { | 383 | if (!tevs[i].point.module) { |
384 | ret = -ENOMEM; | 384 | ret = -ENOMEM; |
385 | break; | 385 | break; |
386 | } | 386 | } |
387 | tevs[i].uprobes = true; | 387 | tevs[i].uprobes = true; |
388 | } | 388 | } |
389 | 389 | ||
390 | return ret; | 390 | return ret; |
391 | } | 391 | } |
392 | 392 | ||
393 | static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, | 393 | static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, |
394 | int ntevs, const char *module) | 394 | int ntevs, const char *module) |
395 | { | 395 | { |
396 | int i, ret = 0; | 396 | int i, ret = 0; |
397 | char *tmp; | 397 | char *tmp; |
398 | 398 | ||
399 | if (!module) | 399 | if (!module) |
400 | return 0; | 400 | return 0; |
401 | 401 | ||
402 | tmp = strrchr(module, '/'); | 402 | tmp = strrchr(module, '/'); |
403 | if (tmp) { | 403 | if (tmp) { |
404 | /* This is a module path -- get the module name */ | 404 | /* This is a module path -- get the module name */ |
405 | module = strdup(tmp + 1); | 405 | module = strdup(tmp + 1); |
406 | if (!module) | 406 | if (!module) |
407 | return -ENOMEM; | 407 | return -ENOMEM; |
408 | tmp = strchr(module, '.'); | 408 | tmp = strchr(module, '.'); |
409 | if (tmp) | 409 | if (tmp) |
410 | *tmp = '\0'; | 410 | *tmp = '\0'; |
411 | tmp = (char *)module; /* For free() */ | 411 | tmp = (char *)module; /* For free() */ |
412 | } | 412 | } |
413 | 413 | ||
414 | for (i = 0; i < ntevs; i++) { | 414 | for (i = 0; i < ntevs; i++) { |
415 | tevs[i].point.module = strdup(module); | 415 | tevs[i].point.module = strdup(module); |
416 | if (!tevs[i].point.module) { | 416 | if (!tevs[i].point.module) { |
417 | ret = -ENOMEM; | 417 | ret = -ENOMEM; |
418 | break; | 418 | break; |
419 | } | 419 | } |
420 | } | 420 | } |
421 | 421 | ||
422 | free(tmp); | 422 | free(tmp); |
423 | return ret; | 423 | return ret; |
424 | } | 424 | } |
425 | 425 | ||
426 | /* Post processing the probe events */ | 426 | /* Post processing the probe events */ |
427 | static int post_process_probe_trace_events(struct probe_trace_event *tevs, | 427 | static int post_process_probe_trace_events(struct probe_trace_event *tevs, |
428 | int ntevs, const char *module, | 428 | int ntevs, const char *module, |
429 | bool uprobe) | 429 | bool uprobe) |
430 | { | 430 | { |
431 | struct ref_reloc_sym *reloc_sym; | 431 | struct ref_reloc_sym *reloc_sym; |
432 | char *tmp; | 432 | char *tmp; |
433 | int i; | 433 | int i; |
434 | 434 | ||
435 | if (uprobe) | 435 | if (uprobe) |
436 | return add_exec_to_probe_trace_events(tevs, ntevs, module); | 436 | return add_exec_to_probe_trace_events(tevs, ntevs, module); |
437 | 437 | ||
438 | /* Note that currently ref_reloc_sym based probe is not for drivers */ | 438 | /* Note that currently ref_reloc_sym based probe is not for drivers */ |
439 | if (module) | 439 | if (module) |
440 | return add_module_to_probe_trace_events(tevs, ntevs, module); | 440 | return add_module_to_probe_trace_events(tevs, ntevs, module); |
441 | 441 | ||
442 | reloc_sym = kernel_get_ref_reloc_sym(); | 442 | reloc_sym = kernel_get_ref_reloc_sym(); |
443 | if (!reloc_sym) { | 443 | if (!reloc_sym) { |
444 | pr_warning("Relocated base symbol is not found!\n"); | 444 | pr_warning("Relocated base symbol is not found!\n"); |
445 | return -EINVAL; | 445 | return -EINVAL; |
446 | } | 446 | } |
447 | 447 | ||
448 | for (i = 0; i < ntevs; i++) { | 448 | for (i = 0; i < ntevs; i++) { |
449 | if (tevs[i].point.address) { | 449 | if (tevs[i].point.address && !tevs[i].point.retprobe) { |
450 | tmp = strdup(reloc_sym->name); | 450 | tmp = strdup(reloc_sym->name); |
451 | if (!tmp) | 451 | if (!tmp) |
452 | return -ENOMEM; | 452 | return -ENOMEM; |
453 | free(tevs[i].point.symbol); | 453 | free(tevs[i].point.symbol); |
454 | tevs[i].point.symbol = tmp; | 454 | tevs[i].point.symbol = tmp; |
455 | tevs[i].point.offset = tevs[i].point.address - | 455 | tevs[i].point.offset = tevs[i].point.address - |
456 | reloc_sym->unrelocated_addr; | 456 | reloc_sym->unrelocated_addr; |
457 | } | 457 | } |
458 | } | 458 | } |
459 | return 0; | 459 | return 0; |
460 | } | 460 | } |
461 | 461 | ||
462 | /* Try to find perf_probe_event with debuginfo */ | 462 | /* Try to find perf_probe_event with debuginfo */ |
463 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 463 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
464 | struct probe_trace_event **tevs, | 464 | struct probe_trace_event **tevs, |
465 | int max_tevs, const char *target) | 465 | int max_tevs, const char *target) |
466 | { | 466 | { |
467 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 467 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
468 | struct debuginfo *dinfo; | 468 | struct debuginfo *dinfo; |
469 | int ntevs, ret = 0; | 469 | int ntevs, ret = 0; |
470 | 470 | ||
471 | dinfo = open_debuginfo(target, !need_dwarf); | 471 | dinfo = open_debuginfo(target, !need_dwarf); |
472 | 472 | ||
473 | if (!dinfo) { | 473 | if (!dinfo) { |
474 | if (need_dwarf) | 474 | if (need_dwarf) |
475 | return -ENOENT; | 475 | return -ENOENT; |
476 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); | 476 | pr_debug("Could not open debuginfo. Try to use symbols.\n"); |
477 | return 0; | 477 | return 0; |
478 | } | 478 | } |
479 | 479 | ||
480 | pr_debug("Try to find probe point from debuginfo.\n"); | 480 | pr_debug("Try to find probe point from debuginfo.\n"); |
481 | /* Searching trace events corresponding to a probe event */ | 481 | /* Searching trace events corresponding to a probe event */ |
482 | ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); | 482 | ntevs = debuginfo__find_trace_events(dinfo, pev, tevs, max_tevs); |
483 | 483 | ||
484 | debuginfo__delete(dinfo); | 484 | debuginfo__delete(dinfo); |
485 | 485 | ||
486 | if (ntevs > 0) { /* Succeeded to find trace events */ | 486 | if (ntevs > 0) { /* Succeeded to find trace events */ |
487 | pr_debug("Found %d probe_trace_events.\n", ntevs); | 487 | pr_debug("Found %d probe_trace_events.\n", ntevs); |
488 | ret = post_process_probe_trace_events(*tevs, ntevs, | 488 | ret = post_process_probe_trace_events(*tevs, ntevs, |
489 | target, pev->uprobes); | 489 | target, pev->uprobes); |
490 | if (ret < 0) { | 490 | if (ret < 0) { |
491 | clear_probe_trace_events(*tevs, ntevs); | 491 | clear_probe_trace_events(*tevs, ntevs); |
492 | zfree(tevs); | 492 | zfree(tevs); |
493 | } | 493 | } |
494 | return ret < 0 ? ret : ntevs; | 494 | return ret < 0 ? ret : ntevs; |
495 | } | 495 | } |
496 | 496 | ||
497 | if (ntevs == 0) { /* No error but failed to find probe point. */ | 497 | if (ntevs == 0) { /* No error but failed to find probe point. */ |
498 | pr_warning("Probe point '%s' not found in debuginfo.\n", | 498 | pr_warning("Probe point '%s' not found in debuginfo.\n", |
499 | synthesize_perf_probe_point(&pev->point)); | 499 | synthesize_perf_probe_point(&pev->point)); |
500 | if (need_dwarf) | 500 | if (need_dwarf) |
501 | return -ENOENT; | 501 | return -ENOENT; |
502 | return 0; | 502 | return 0; |
503 | } | 503 | } |
504 | /* Error path : ntevs < 0 */ | 504 | /* Error path : ntevs < 0 */ |
505 | pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); | 505 | pr_debug("An error occurred in debuginfo analysis (%d).\n", ntevs); |
506 | if (ntevs == -EBADF) { | 506 | if (ntevs == -EBADF) { |
507 | pr_warning("Warning: No dwarf info found in the vmlinux - " | 507 | pr_warning("Warning: No dwarf info found in the vmlinux - " |
508 | "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); | 508 | "please rebuild kernel with CONFIG_DEBUG_INFO=y.\n"); |
509 | if (!need_dwarf) { | 509 | if (!need_dwarf) { |
510 | pr_debug("Trying to use symbols.\n"); | 510 | pr_debug("Trying to use symbols.\n"); |
511 | return 0; | 511 | return 0; |
512 | } | 512 | } |
513 | } | 513 | } |
514 | return ntevs; | 514 | return ntevs; |
515 | } | 515 | } |
516 | 516 | ||
517 | /* | 517 | /* |
518 | * Find a src file from a DWARF tag path. Prepend optional source path prefix | 518 | * Find a src file from a DWARF tag path. Prepend optional source path prefix |
519 | * and chop off leading directories that do not exist. Result is passed back as | 519 | * and chop off leading directories that do not exist. Result is passed back as |
520 | * a newly allocated path on success. | 520 | * a newly allocated path on success. |
521 | * Return 0 if file was found and readable, -errno otherwise. | 521 | * Return 0 if file was found and readable, -errno otherwise. |
522 | */ | 522 | */ |
523 | static int get_real_path(const char *raw_path, const char *comp_dir, | 523 | static int get_real_path(const char *raw_path, const char *comp_dir, |
524 | char **new_path) | 524 | char **new_path) |
525 | { | 525 | { |
526 | const char *prefix = symbol_conf.source_prefix; | 526 | const char *prefix = symbol_conf.source_prefix; |
527 | 527 | ||
528 | if (!prefix) { | 528 | if (!prefix) { |
529 | if (raw_path[0] != '/' && comp_dir) | 529 | if (raw_path[0] != '/' && comp_dir) |
530 | /* If not an absolute path, try to use comp_dir */ | 530 | /* If not an absolute path, try to use comp_dir */ |
531 | prefix = comp_dir; | 531 | prefix = comp_dir; |
532 | else { | 532 | else { |
533 | if (access(raw_path, R_OK) == 0) { | 533 | if (access(raw_path, R_OK) == 0) { |
534 | *new_path = strdup(raw_path); | 534 | *new_path = strdup(raw_path); |
535 | return 0; | 535 | return 0; |
536 | } else | 536 | } else |
537 | return -errno; | 537 | return -errno; |
538 | } | 538 | } |
539 | } | 539 | } |
540 | 540 | ||
541 | *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2)); | 541 | *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2)); |
542 | if (!*new_path) | 542 | if (!*new_path) |
543 | return -ENOMEM; | 543 | return -ENOMEM; |
544 | 544 | ||
545 | for (;;) { | 545 | for (;;) { |
546 | sprintf(*new_path, "%s/%s", prefix, raw_path); | 546 | sprintf(*new_path, "%s/%s", prefix, raw_path); |
547 | 547 | ||
548 | if (access(*new_path, R_OK) == 0) | 548 | if (access(*new_path, R_OK) == 0) |
549 | return 0; | 549 | return 0; |
550 | 550 | ||
551 | if (!symbol_conf.source_prefix) | 551 | if (!symbol_conf.source_prefix) |
552 | /* In case of searching comp_dir, don't retry */ | 552 | /* In case of searching comp_dir, don't retry */ |
553 | return -errno; | 553 | return -errno; |
554 | 554 | ||
555 | switch (errno) { | 555 | switch (errno) { |
556 | case ENAMETOOLONG: | 556 | case ENAMETOOLONG: |
557 | case ENOENT: | 557 | case ENOENT: |
558 | case EROFS: | 558 | case EROFS: |
559 | case EFAULT: | 559 | case EFAULT: |
560 | raw_path = strchr(++raw_path, '/'); | 560 | raw_path = strchr(++raw_path, '/'); |
561 | if (!raw_path) { | 561 | if (!raw_path) { |
562 | zfree(new_path); | 562 | zfree(new_path); |
563 | return -ENOENT; | 563 | return -ENOENT; |
564 | } | 564 | } |
565 | continue; | 565 | continue; |
566 | 566 | ||
567 | default: | 567 | default: |
568 | zfree(new_path); | 568 | zfree(new_path); |
569 | return -errno; | 569 | return -errno; |
570 | } | 570 | } |
571 | } | 571 | } |
572 | } | 572 | } |
573 | 573 | ||
574 | #define LINEBUF_SIZE 256 | 574 | #define LINEBUF_SIZE 256 |
575 | #define NR_ADDITIONAL_LINES 2 | 575 | #define NR_ADDITIONAL_LINES 2 |
576 | 576 | ||
577 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) | 577 | static int __show_one_line(FILE *fp, int l, bool skip, bool show_num) |
578 | { | 578 | { |
579 | char buf[LINEBUF_SIZE], sbuf[STRERR_BUFSIZE]; | 579 | char buf[LINEBUF_SIZE], sbuf[STRERR_BUFSIZE]; |
580 | const char *color = show_num ? "" : PERF_COLOR_BLUE; | 580 | const char *color = show_num ? "" : PERF_COLOR_BLUE; |
581 | const char *prefix = NULL; | 581 | const char *prefix = NULL; |
582 | 582 | ||
583 | do { | 583 | do { |
584 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | 584 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) |
585 | goto error; | 585 | goto error; |
586 | if (skip) | 586 | if (skip) |
587 | continue; | 587 | continue; |
588 | if (!prefix) { | 588 | if (!prefix) { |
589 | prefix = show_num ? "%7d " : " "; | 589 | prefix = show_num ? "%7d " : " "; |
590 | color_fprintf(stdout, color, prefix, l); | 590 | color_fprintf(stdout, color, prefix, l); |
591 | } | 591 | } |
592 | color_fprintf(stdout, color, "%s", buf); | 592 | color_fprintf(stdout, color, "%s", buf); |
593 | 593 | ||
594 | } while (strchr(buf, '\n') == NULL); | 594 | } while (strchr(buf, '\n') == NULL); |
595 | 595 | ||
596 | return 1; | 596 | return 1; |
597 | error: | 597 | error: |
598 | if (ferror(fp)) { | 598 | if (ferror(fp)) { |
599 | pr_warning("File read error: %s\n", | 599 | pr_warning("File read error: %s\n", |
600 | strerror_r(errno, sbuf, sizeof(sbuf))); | 600 | strerror_r(errno, sbuf, sizeof(sbuf))); |
601 | return -1; | 601 | return -1; |
602 | } | 602 | } |
603 | return 0; | 603 | return 0; |
604 | } | 604 | } |
605 | 605 | ||
606 | static int _show_one_line(FILE *fp, int l, bool skip, bool show_num) | 606 | static int _show_one_line(FILE *fp, int l, bool skip, bool show_num) |
607 | { | 607 | { |
608 | int rv = __show_one_line(fp, l, skip, show_num); | 608 | int rv = __show_one_line(fp, l, skip, show_num); |
609 | if (rv == 0) { | 609 | if (rv == 0) { |
610 | pr_warning("Source file is shorter than expected.\n"); | 610 | pr_warning("Source file is shorter than expected.\n"); |
611 | rv = -1; | 611 | rv = -1; |
612 | } | 612 | } |
613 | return rv; | 613 | return rv; |
614 | } | 614 | } |
615 | 615 | ||
616 | #define show_one_line_with_num(f,l) _show_one_line(f,l,false,true) | 616 | #define show_one_line_with_num(f,l) _show_one_line(f,l,false,true) |
617 | #define show_one_line(f,l) _show_one_line(f,l,false,false) | 617 | #define show_one_line(f,l) _show_one_line(f,l,false,false) |
618 | #define skip_one_line(f,l) _show_one_line(f,l,true,false) | 618 | #define skip_one_line(f,l) _show_one_line(f,l,true,false) |
619 | #define show_one_line_or_eof(f,l) __show_one_line(f,l,false,false) | 619 | #define show_one_line_or_eof(f,l) __show_one_line(f,l,false,false) |
620 | 620 | ||
621 | /* | 621 | /* |
622 | * Show line-range always requires debuginfo to find source file and | 622 | * Show line-range always requires debuginfo to find source file and |
623 | * line number. | 623 | * line number. |
624 | */ | 624 | */ |
625 | static int __show_line_range(struct line_range *lr, const char *module) | 625 | static int __show_line_range(struct line_range *lr, const char *module) |
626 | { | 626 | { |
627 | int l = 1; | 627 | int l = 1; |
628 | struct int_node *ln; | 628 | struct int_node *ln; |
629 | struct debuginfo *dinfo; | 629 | struct debuginfo *dinfo; |
630 | FILE *fp; | 630 | FILE *fp; |
631 | int ret; | 631 | int ret; |
632 | char *tmp; | 632 | char *tmp; |
633 | char sbuf[STRERR_BUFSIZE]; | 633 | char sbuf[STRERR_BUFSIZE]; |
634 | 634 | ||
635 | /* Search a line range */ | 635 | /* Search a line range */ |
636 | dinfo = open_debuginfo(module, false); | 636 | dinfo = open_debuginfo(module, false); |
637 | if (!dinfo) | 637 | if (!dinfo) |
638 | return -ENOENT; | 638 | return -ENOENT; |
639 | 639 | ||
640 | ret = debuginfo__find_line_range(dinfo, lr); | 640 | ret = debuginfo__find_line_range(dinfo, lr); |
641 | debuginfo__delete(dinfo); | 641 | debuginfo__delete(dinfo); |
642 | if (ret == 0 || ret == -ENOENT) { | 642 | if (ret == 0 || ret == -ENOENT) { |
643 | pr_warning("Specified source line is not found.\n"); | 643 | pr_warning("Specified source line is not found.\n"); |
644 | return -ENOENT; | 644 | return -ENOENT; |
645 | } else if (ret < 0) { | 645 | } else if (ret < 0) { |
646 | pr_warning("Debuginfo analysis failed.\n"); | 646 | pr_warning("Debuginfo analysis failed.\n"); |
647 | return ret; | 647 | return ret; |
648 | } | 648 | } |
649 | 649 | ||
650 | /* Convert source file path */ | 650 | /* Convert source file path */ |
651 | tmp = lr->path; | 651 | tmp = lr->path; |
652 | ret = get_real_path(tmp, lr->comp_dir, &lr->path); | 652 | ret = get_real_path(tmp, lr->comp_dir, &lr->path); |
653 | free(tmp); /* Free old path */ | 653 | free(tmp); /* Free old path */ |
654 | if (ret < 0) { | 654 | if (ret < 0) { |
655 | pr_warning("Failed to find source file path.\n"); | 655 | pr_warning("Failed to find source file path.\n"); |
656 | return ret; | 656 | return ret; |
657 | } | 657 | } |
658 | 658 | ||
659 | setup_pager(); | 659 | setup_pager(); |
660 | 660 | ||
661 | if (lr->function) | 661 | if (lr->function) |
662 | fprintf(stdout, "<%s@%s:%d>\n", lr->function, lr->path, | 662 | fprintf(stdout, "<%s@%s:%d>\n", lr->function, lr->path, |
663 | lr->start - lr->offset); | 663 | lr->start - lr->offset); |
664 | else | 664 | else |
665 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); | 665 | fprintf(stdout, "<%s:%d>\n", lr->path, lr->start); |
666 | 666 | ||
667 | fp = fopen(lr->path, "r"); | 667 | fp = fopen(lr->path, "r"); |
668 | if (fp == NULL) { | 668 | if (fp == NULL) { |
669 | pr_warning("Failed to open %s: %s\n", lr->path, | 669 | pr_warning("Failed to open %s: %s\n", lr->path, |
670 | strerror_r(errno, sbuf, sizeof(sbuf))); | 670 | strerror_r(errno, sbuf, sizeof(sbuf))); |
671 | return -errno; | 671 | return -errno; |
672 | } | 672 | } |
673 | /* Skip to starting line number */ | 673 | /* Skip to starting line number */ |
674 | while (l < lr->start) { | 674 | while (l < lr->start) { |
675 | ret = skip_one_line(fp, l++); | 675 | ret = skip_one_line(fp, l++); |
676 | if (ret < 0) | 676 | if (ret < 0) |
677 | goto end; | 677 | goto end; |
678 | } | 678 | } |
679 | 679 | ||
680 | intlist__for_each(ln, lr->line_list) { | 680 | intlist__for_each(ln, lr->line_list) { |
681 | for (; ln->i > l; l++) { | 681 | for (; ln->i > l; l++) { |
682 | ret = show_one_line(fp, l - lr->offset); | 682 | ret = show_one_line(fp, l - lr->offset); |
683 | if (ret < 0) | 683 | if (ret < 0) |
684 | goto end; | 684 | goto end; |
685 | } | 685 | } |
686 | ret = show_one_line_with_num(fp, l++ - lr->offset); | 686 | ret = show_one_line_with_num(fp, l++ - lr->offset); |
687 | if (ret < 0) | 687 | if (ret < 0) |
688 | goto end; | 688 | goto end; |
689 | } | 689 | } |
690 | 690 | ||
691 | if (lr->end == INT_MAX) | 691 | if (lr->end == INT_MAX) |
692 | lr->end = l + NR_ADDITIONAL_LINES; | 692 | lr->end = l + NR_ADDITIONAL_LINES; |
693 | while (l <= lr->end) { | 693 | while (l <= lr->end) { |
694 | ret = show_one_line_or_eof(fp, l++ - lr->offset); | 694 | ret = show_one_line_or_eof(fp, l++ - lr->offset); |
695 | if (ret <= 0) | 695 | if (ret <= 0) |
696 | break; | 696 | break; |
697 | } | 697 | } |
698 | end: | 698 | end: |
699 | fclose(fp); | 699 | fclose(fp); |
700 | return ret; | 700 | return ret; |
701 | } | 701 | } |
702 | 702 | ||
703 | int show_line_range(struct line_range *lr, const char *module, bool user) | 703 | int show_line_range(struct line_range *lr, const char *module, bool user) |
704 | { | 704 | { |
705 | int ret; | 705 | int ret; |
706 | 706 | ||
707 | ret = init_symbol_maps(user); | 707 | ret = init_symbol_maps(user); |
708 | if (ret < 0) | 708 | if (ret < 0) |
709 | return ret; | 709 | return ret; |
710 | ret = __show_line_range(lr, module); | 710 | ret = __show_line_range(lr, module); |
711 | exit_symbol_maps(); | 711 | exit_symbol_maps(); |
712 | 712 | ||
713 | return ret; | 713 | return ret; |
714 | } | 714 | } |
715 | 715 | ||
716 | static int show_available_vars_at(struct debuginfo *dinfo, | 716 | static int show_available_vars_at(struct debuginfo *dinfo, |
717 | struct perf_probe_event *pev, | 717 | struct perf_probe_event *pev, |
718 | int max_vls, struct strfilter *_filter, | 718 | int max_vls, struct strfilter *_filter, |
719 | bool externs) | 719 | bool externs) |
720 | { | 720 | { |
721 | char *buf; | 721 | char *buf; |
722 | int ret, i, nvars; | 722 | int ret, i, nvars; |
723 | struct str_node *node; | 723 | struct str_node *node; |
724 | struct variable_list *vls = NULL, *vl; | 724 | struct variable_list *vls = NULL, *vl; |
725 | const char *var; | 725 | const char *var; |
726 | 726 | ||
727 | buf = synthesize_perf_probe_point(&pev->point); | 727 | buf = synthesize_perf_probe_point(&pev->point); |
728 | if (!buf) | 728 | if (!buf) |
729 | return -EINVAL; | 729 | return -EINVAL; |
730 | pr_debug("Searching variables at %s\n", buf); | 730 | pr_debug("Searching variables at %s\n", buf); |
731 | 731 | ||
732 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, | 732 | ret = debuginfo__find_available_vars_at(dinfo, pev, &vls, |
733 | max_vls, externs); | 733 | max_vls, externs); |
734 | if (ret <= 0) { | 734 | if (ret <= 0) { |
735 | if (ret == 0 || ret == -ENOENT) { | 735 | if (ret == 0 || ret == -ENOENT) { |
736 | pr_err("Failed to find the address of %s\n", buf); | 736 | pr_err("Failed to find the address of %s\n", buf); |
737 | ret = -ENOENT; | 737 | ret = -ENOENT; |
738 | } else | 738 | } else |
739 | pr_warning("Debuginfo analysis failed.\n"); | 739 | pr_warning("Debuginfo analysis failed.\n"); |
740 | goto end; | 740 | goto end; |
741 | } | 741 | } |
742 | 742 | ||
743 | /* Some variables are found */ | 743 | /* Some variables are found */ |
744 | fprintf(stdout, "Available variables at %s\n", buf); | 744 | fprintf(stdout, "Available variables at %s\n", buf); |
745 | for (i = 0; i < ret; i++) { | 745 | for (i = 0; i < ret; i++) { |
746 | vl = &vls[i]; | 746 | vl = &vls[i]; |
747 | /* | 747 | /* |
748 | * A probe point might be converted to | 748 | * A probe point might be converted to |
749 | * several trace points. | 749 | * several trace points. |
750 | */ | 750 | */ |
751 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, | 751 | fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, |
752 | vl->point.offset); | 752 | vl->point.offset); |
753 | zfree(&vl->point.symbol); | 753 | zfree(&vl->point.symbol); |
754 | nvars = 0; | 754 | nvars = 0; |
755 | if (vl->vars) { | 755 | if (vl->vars) { |
756 | strlist__for_each(node, vl->vars) { | 756 | strlist__for_each(node, vl->vars) { |
757 | var = strchr(node->s, '\t') + 1; | 757 | var = strchr(node->s, '\t') + 1; |
758 | if (strfilter__compare(_filter, var)) { | 758 | if (strfilter__compare(_filter, var)) { |
759 | fprintf(stdout, "\t\t%s\n", node->s); | 759 | fprintf(stdout, "\t\t%s\n", node->s); |
760 | nvars++; | 760 | nvars++; |
761 | } | 761 | } |
762 | } | 762 | } |
763 | strlist__delete(vl->vars); | 763 | strlist__delete(vl->vars); |
764 | } | 764 | } |
765 | if (nvars == 0) | 765 | if (nvars == 0) |
766 | fprintf(stdout, "\t\t(No matched variables)\n"); | 766 | fprintf(stdout, "\t\t(No matched variables)\n"); |
767 | } | 767 | } |
768 | free(vls); | 768 | free(vls); |
769 | end: | 769 | end: |
770 | free(buf); | 770 | free(buf); |
771 | return ret; | 771 | return ret; |
772 | } | 772 | } |
773 | 773 | ||
774 | /* Show available variables on given probe point */ | 774 | /* Show available variables on given probe point */ |
775 | int show_available_vars(struct perf_probe_event *pevs, int npevs, | 775 | int show_available_vars(struct perf_probe_event *pevs, int npevs, |
776 | int max_vls, const char *module, | 776 | int max_vls, const char *module, |
777 | struct strfilter *_filter, bool externs) | 777 | struct strfilter *_filter, bool externs) |
778 | { | 778 | { |
779 | int i, ret = 0; | 779 | int i, ret = 0; |
780 | struct debuginfo *dinfo; | 780 | struct debuginfo *dinfo; |
781 | 781 | ||
782 | ret = init_symbol_maps(pevs->uprobes); | 782 | ret = init_symbol_maps(pevs->uprobes); |
783 | if (ret < 0) | 783 | if (ret < 0) |
784 | return ret; | 784 | return ret; |
785 | 785 | ||
786 | dinfo = open_debuginfo(module, false); | 786 | dinfo = open_debuginfo(module, false); |
787 | if (!dinfo) { | 787 | if (!dinfo) { |
788 | ret = -ENOENT; | 788 | ret = -ENOENT; |
789 | goto out; | 789 | goto out; |
790 | } | 790 | } |
791 | 791 | ||
792 | setup_pager(); | 792 | setup_pager(); |
793 | 793 | ||
794 | for (i = 0; i < npevs && ret >= 0; i++) | 794 | for (i = 0; i < npevs && ret >= 0; i++) |
795 | ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, | 795 | ret = show_available_vars_at(dinfo, &pevs[i], max_vls, _filter, |
796 | externs); | 796 | externs); |
797 | 797 | ||
798 | debuginfo__delete(dinfo); | 798 | debuginfo__delete(dinfo); |
799 | out: | 799 | out: |
800 | exit_symbol_maps(); | 800 | exit_symbol_maps(); |
801 | return ret; | 801 | return ret; |
802 | } | 802 | } |
803 | 803 | ||
804 | #else /* !HAVE_DWARF_SUPPORT */ | 804 | #else /* !HAVE_DWARF_SUPPORT */ |
805 | 805 | ||
806 | static int | 806 | static int |
807 | find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused, | 807 | find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused, |
808 | struct perf_probe_point *pp __maybe_unused, | 808 | struct perf_probe_point *pp __maybe_unused, |
809 | bool is_kprobe __maybe_unused) | 809 | bool is_kprobe __maybe_unused) |
810 | { | 810 | { |
811 | return -ENOSYS; | 811 | return -ENOSYS; |
812 | } | 812 | } |
813 | 813 | ||
814 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 814 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
815 | struct probe_trace_event **tevs __maybe_unused, | 815 | struct probe_trace_event **tevs __maybe_unused, |
816 | int max_tevs __maybe_unused, | 816 | int max_tevs __maybe_unused, |
817 | const char *target __maybe_unused) | 817 | const char *target __maybe_unused) |
818 | { | 818 | { |
819 | if (perf_probe_event_need_dwarf(pev)) { | 819 | if (perf_probe_event_need_dwarf(pev)) { |
820 | pr_warning("Debuginfo-analysis is not supported.\n"); | 820 | pr_warning("Debuginfo-analysis is not supported.\n"); |
821 | return -ENOSYS; | 821 | return -ENOSYS; |
822 | } | 822 | } |
823 | 823 | ||
824 | return 0; | 824 | return 0; |
825 | } | 825 | } |
826 | 826 | ||
827 | int show_line_range(struct line_range *lr __maybe_unused, | 827 | int show_line_range(struct line_range *lr __maybe_unused, |
828 | const char *module __maybe_unused, | 828 | const char *module __maybe_unused, |
829 | bool user __maybe_unused) | 829 | bool user __maybe_unused) |
830 | { | 830 | { |
831 | pr_warning("Debuginfo-analysis is not supported.\n"); | 831 | pr_warning("Debuginfo-analysis is not supported.\n"); |
832 | return -ENOSYS; | 832 | return -ENOSYS; |
833 | } | 833 | } |
834 | 834 | ||
835 | int show_available_vars(struct perf_probe_event *pevs __maybe_unused, | 835 | int show_available_vars(struct perf_probe_event *pevs __maybe_unused, |
836 | int npevs __maybe_unused, int max_vls __maybe_unused, | 836 | int npevs __maybe_unused, int max_vls __maybe_unused, |
837 | const char *module __maybe_unused, | 837 | const char *module __maybe_unused, |
838 | struct strfilter *filter __maybe_unused, | 838 | struct strfilter *filter __maybe_unused, |
839 | bool externs __maybe_unused) | 839 | bool externs __maybe_unused) |
840 | { | 840 | { |
841 | pr_warning("Debuginfo-analysis is not supported.\n"); | 841 | pr_warning("Debuginfo-analysis is not supported.\n"); |
842 | return -ENOSYS; | 842 | return -ENOSYS; |
843 | } | 843 | } |
844 | #endif | 844 | #endif |
845 | 845 | ||
846 | void line_range__clear(struct line_range *lr) | 846 | void line_range__clear(struct line_range *lr) |
847 | { | 847 | { |
848 | free(lr->function); | 848 | free(lr->function); |
849 | free(lr->file); | 849 | free(lr->file); |
850 | free(lr->path); | 850 | free(lr->path); |
851 | free(lr->comp_dir); | 851 | free(lr->comp_dir); |
852 | intlist__delete(lr->line_list); | 852 | intlist__delete(lr->line_list); |
853 | memset(lr, 0, sizeof(*lr)); | 853 | memset(lr, 0, sizeof(*lr)); |
854 | } | 854 | } |
855 | 855 | ||
856 | int line_range__init(struct line_range *lr) | 856 | int line_range__init(struct line_range *lr) |
857 | { | 857 | { |
858 | memset(lr, 0, sizeof(*lr)); | 858 | memset(lr, 0, sizeof(*lr)); |
859 | lr->line_list = intlist__new(NULL); | 859 | lr->line_list = intlist__new(NULL); |
860 | if (!lr->line_list) | 860 | if (!lr->line_list) |
861 | return -ENOMEM; | 861 | return -ENOMEM; |
862 | else | 862 | else |
863 | return 0; | 863 | return 0; |
864 | } | 864 | } |
865 | 865 | ||
866 | static int parse_line_num(char **ptr, int *val, const char *what) | 866 | static int parse_line_num(char **ptr, int *val, const char *what) |
867 | { | 867 | { |
868 | const char *start = *ptr; | 868 | const char *start = *ptr; |
869 | 869 | ||
870 | errno = 0; | 870 | errno = 0; |
871 | *val = strtol(*ptr, ptr, 0); | 871 | *val = strtol(*ptr, ptr, 0); |
872 | if (errno || *ptr == start) { | 872 | if (errno || *ptr == start) { |
873 | semantic_error("'%s' is not a valid number.\n", what); | 873 | semantic_error("'%s' is not a valid number.\n", what); |
874 | return -EINVAL; | 874 | return -EINVAL; |
875 | } | 875 | } |
876 | return 0; | 876 | return 0; |
877 | } | 877 | } |
878 | 878 | ||
879 | /* | 879 | /* |
880 | * Stuff 'lr' according to the line range described by 'arg'. | 880 | * Stuff 'lr' according to the line range described by 'arg'. |
881 | * The line range syntax is described by: | 881 | * The line range syntax is described by: |
882 | * | 882 | * |
883 | * SRC[:SLN[+NUM|-ELN]] | 883 | * SRC[:SLN[+NUM|-ELN]] |
884 | * FNC[@SRC][:SLN[+NUM|-ELN]] | 884 | * FNC[@SRC][:SLN[+NUM|-ELN]] |
885 | */ | 885 | */ |
886 | int parse_line_range_desc(const char *arg, struct line_range *lr) | 886 | int parse_line_range_desc(const char *arg, struct line_range *lr) |
887 | { | 887 | { |
888 | char *range, *file, *name = strdup(arg); | 888 | char *range, *file, *name = strdup(arg); |
889 | int err; | 889 | int err; |
890 | 890 | ||
891 | if (!name) | 891 | if (!name) |
892 | return -ENOMEM; | 892 | return -ENOMEM; |
893 | 893 | ||
894 | lr->start = 0; | 894 | lr->start = 0; |
895 | lr->end = INT_MAX; | 895 | lr->end = INT_MAX; |
896 | 896 | ||
897 | range = strchr(name, ':'); | 897 | range = strchr(name, ':'); |
898 | if (range) { | 898 | if (range) { |
899 | *range++ = '\0'; | 899 | *range++ = '\0'; |
900 | 900 | ||
901 | err = parse_line_num(&range, &lr->start, "start line"); | 901 | err = parse_line_num(&range, &lr->start, "start line"); |
902 | if (err) | 902 | if (err) |
903 | goto err; | 903 | goto err; |
904 | 904 | ||
905 | if (*range == '+' || *range == '-') { | 905 | if (*range == '+' || *range == '-') { |
906 | const char c = *range++; | 906 | const char c = *range++; |
907 | 907 | ||
908 | err = parse_line_num(&range, &lr->end, "end line"); | 908 | err = parse_line_num(&range, &lr->end, "end line"); |
909 | if (err) | 909 | if (err) |
910 | goto err; | 910 | goto err; |
911 | 911 | ||
912 | if (c == '+') { | 912 | if (c == '+') { |
913 | lr->end += lr->start; | 913 | lr->end += lr->start; |
914 | /* | 914 | /* |
915 | * Adjust the number of lines here. | 915 | * Adjust the number of lines here. |
916 | * If the number of lines == 1, the | 916 | * If the number of lines == 1, the |
917 | * the end of line should be equal to | 917 | * the end of line should be equal to |
918 | * the start of line. | 918 | * the start of line. |
919 | */ | 919 | */ |
920 | lr->end--; | 920 | lr->end--; |
921 | } | 921 | } |
922 | } | 922 | } |
923 | 923 | ||
924 | pr_debug("Line range is %d to %d\n", lr->start, lr->end); | 924 | pr_debug("Line range is %d to %d\n", lr->start, lr->end); |
925 | 925 | ||
926 | err = -EINVAL; | 926 | err = -EINVAL; |
927 | if (lr->start > lr->end) { | 927 | if (lr->start > lr->end) { |
928 | semantic_error("Start line must be smaller" | 928 | semantic_error("Start line must be smaller" |
929 | " than end line.\n"); | 929 | " than end line.\n"); |
930 | goto err; | 930 | goto err; |
931 | } | 931 | } |
932 | if (*range != '\0') { | 932 | if (*range != '\0') { |
933 | semantic_error("Tailing with invalid str '%s'.\n", range); | 933 | semantic_error("Tailing with invalid str '%s'.\n", range); |
934 | goto err; | 934 | goto err; |
935 | } | 935 | } |
936 | } | 936 | } |
937 | 937 | ||
938 | file = strchr(name, '@'); | 938 | file = strchr(name, '@'); |
939 | if (file) { | 939 | if (file) { |
940 | *file = '\0'; | 940 | *file = '\0'; |
941 | lr->file = strdup(++file); | 941 | lr->file = strdup(++file); |
942 | if (lr->file == NULL) { | 942 | if (lr->file == NULL) { |
943 | err = -ENOMEM; | 943 | err = -ENOMEM; |
944 | goto err; | 944 | goto err; |
945 | } | 945 | } |
946 | lr->function = name; | 946 | lr->function = name; |
947 | } else if (strchr(name, '.')) | 947 | } else if (strchr(name, '.')) |
948 | lr->file = name; | 948 | lr->file = name; |
949 | else | 949 | else |
950 | lr->function = name; | 950 | lr->function = name; |
951 | 951 | ||
952 | return 0; | 952 | return 0; |
953 | err: | 953 | err: |
954 | free(name); | 954 | free(name); |
955 | return err; | 955 | return err; |
956 | } | 956 | } |
957 | 957 | ||
958 | /* Check the name is good for event/group */ | 958 | /* Check the name is good for event/group */ |
959 | static bool check_event_name(const char *name) | 959 | static bool check_event_name(const char *name) |
960 | { | 960 | { |
961 | if (!isalpha(*name) && *name != '_') | 961 | if (!isalpha(*name) && *name != '_') |
962 | return false; | 962 | return false; |
963 | while (*++name != '\0') { | 963 | while (*++name != '\0') { |
964 | if (!isalpha(*name) && !isdigit(*name) && *name != '_') | 964 | if (!isalpha(*name) && !isdigit(*name) && *name != '_') |
965 | return false; | 965 | return false; |
966 | } | 966 | } |
967 | return true; | 967 | return true; |
968 | } | 968 | } |
969 | 969 | ||
970 | /* Parse probepoint definition. */ | 970 | /* Parse probepoint definition. */ |
971 | static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) | 971 | static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev) |
972 | { | 972 | { |
973 | struct perf_probe_point *pp = &pev->point; | 973 | struct perf_probe_point *pp = &pev->point; |
974 | char *ptr, *tmp; | 974 | char *ptr, *tmp; |
975 | char c, nc = 0; | 975 | char c, nc = 0; |
976 | /* | 976 | /* |
977 | * <Syntax> | 977 | * <Syntax> |
978 | * perf probe [EVENT=]SRC[:LN|;PTN] | 978 | * perf probe [EVENT=]SRC[:LN|;PTN] |
979 | * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT] | 979 | * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT] |
980 | * | 980 | * |
981 | * TODO:Group name support | 981 | * TODO:Group name support |
982 | */ | 982 | */ |
983 | 983 | ||
984 | ptr = strpbrk(arg, ";=@+%"); | 984 | ptr = strpbrk(arg, ";=@+%"); |
985 | if (ptr && *ptr == '=') { /* Event name */ | 985 | if (ptr && *ptr == '=') { /* Event name */ |
986 | *ptr = '\0'; | 986 | *ptr = '\0'; |
987 | tmp = ptr + 1; | 987 | tmp = ptr + 1; |
988 | if (strchr(arg, ':')) { | 988 | if (strchr(arg, ':')) { |
989 | semantic_error("Group name is not supported yet.\n"); | 989 | semantic_error("Group name is not supported yet.\n"); |
990 | return -ENOTSUP; | 990 | return -ENOTSUP; |
991 | } | 991 | } |
992 | if (!check_event_name(arg)) { | 992 | if (!check_event_name(arg)) { |
993 | semantic_error("%s is bad for event name -it must " | 993 | semantic_error("%s is bad for event name -it must " |
994 | "follow C symbol-naming rule.\n", arg); | 994 | "follow C symbol-naming rule.\n", arg); |
995 | return -EINVAL; | 995 | return -EINVAL; |
996 | } | 996 | } |
997 | pev->event = strdup(arg); | 997 | pev->event = strdup(arg); |
998 | if (pev->event == NULL) | 998 | if (pev->event == NULL) |
999 | return -ENOMEM; | 999 | return -ENOMEM; |
1000 | pev->group = NULL; | 1000 | pev->group = NULL; |
1001 | arg = tmp; | 1001 | arg = tmp; |
1002 | } | 1002 | } |
1003 | 1003 | ||
1004 | ptr = strpbrk(arg, ";:+@%"); | 1004 | ptr = strpbrk(arg, ";:+@%"); |
1005 | if (ptr) { | 1005 | if (ptr) { |
1006 | nc = *ptr; | 1006 | nc = *ptr; |
1007 | *ptr++ = '\0'; | 1007 | *ptr++ = '\0'; |
1008 | } | 1008 | } |
1009 | 1009 | ||
1010 | tmp = strdup(arg); | 1010 | tmp = strdup(arg); |
1011 | if (tmp == NULL) | 1011 | if (tmp == NULL) |
1012 | return -ENOMEM; | 1012 | return -ENOMEM; |
1013 | 1013 | ||
1014 | /* Check arg is function or file and copy it */ | 1014 | /* Check arg is function or file and copy it */ |
1015 | if (strchr(tmp, '.')) /* File */ | 1015 | if (strchr(tmp, '.')) /* File */ |
1016 | pp->file = tmp; | 1016 | pp->file = tmp; |
1017 | else /* Function */ | 1017 | else /* Function */ |
1018 | pp->function = tmp; | 1018 | pp->function = tmp; |
1019 | 1019 | ||
1020 | /* Parse other options */ | 1020 | /* Parse other options */ |
1021 | while (ptr) { | 1021 | while (ptr) { |
1022 | arg = ptr; | 1022 | arg = ptr; |
1023 | c = nc; | 1023 | c = nc; |
1024 | if (c == ';') { /* Lazy pattern must be the last part */ | 1024 | if (c == ';') { /* Lazy pattern must be the last part */ |
1025 | pp->lazy_line = strdup(arg); | 1025 | pp->lazy_line = strdup(arg); |
1026 | if (pp->lazy_line == NULL) | 1026 | if (pp->lazy_line == NULL) |
1027 | return -ENOMEM; | 1027 | return -ENOMEM; |
1028 | break; | 1028 | break; |
1029 | } | 1029 | } |
1030 | ptr = strpbrk(arg, ";:+@%"); | 1030 | ptr = strpbrk(arg, ";:+@%"); |
1031 | if (ptr) { | 1031 | if (ptr) { |
1032 | nc = *ptr; | 1032 | nc = *ptr; |
1033 | *ptr++ = '\0'; | 1033 | *ptr++ = '\0'; |
1034 | } | 1034 | } |
1035 | switch (c) { | 1035 | switch (c) { |
1036 | case ':': /* Line number */ | 1036 | case ':': /* Line number */ |
1037 | pp->line = strtoul(arg, &tmp, 0); | 1037 | pp->line = strtoul(arg, &tmp, 0); |
1038 | if (*tmp != '\0') { | 1038 | if (*tmp != '\0') { |
1039 | semantic_error("There is non-digit char" | 1039 | semantic_error("There is non-digit char" |
1040 | " in line number.\n"); | 1040 | " in line number.\n"); |
1041 | return -EINVAL; | 1041 | return -EINVAL; |
1042 | } | 1042 | } |
1043 | break; | 1043 | break; |
1044 | case '+': /* Byte offset from a symbol */ | 1044 | case '+': /* Byte offset from a symbol */ |
1045 | pp->offset = strtoul(arg, &tmp, 0); | 1045 | pp->offset = strtoul(arg, &tmp, 0); |
1046 | if (*tmp != '\0') { | 1046 | if (*tmp != '\0') { |
1047 | semantic_error("There is non-digit character" | 1047 | semantic_error("There is non-digit character" |
1048 | " in offset.\n"); | 1048 | " in offset.\n"); |
1049 | return -EINVAL; | 1049 | return -EINVAL; |
1050 | } | 1050 | } |
1051 | break; | 1051 | break; |
1052 | case '@': /* File name */ | 1052 | case '@': /* File name */ |
1053 | if (pp->file) { | 1053 | if (pp->file) { |
1054 | semantic_error("SRC@SRC is not allowed.\n"); | 1054 | semantic_error("SRC@SRC is not allowed.\n"); |
1055 | return -EINVAL; | 1055 | return -EINVAL; |
1056 | } | 1056 | } |
1057 | pp->file = strdup(arg); | 1057 | pp->file = strdup(arg); |
1058 | if (pp->file == NULL) | 1058 | if (pp->file == NULL) |
1059 | return -ENOMEM; | 1059 | return -ENOMEM; |
1060 | break; | 1060 | break; |
1061 | case '%': /* Probe places */ | 1061 | case '%': /* Probe places */ |
1062 | if (strcmp(arg, "return") == 0) { | 1062 | if (strcmp(arg, "return") == 0) { |
1063 | pp->retprobe = 1; | 1063 | pp->retprobe = 1; |
1064 | } else { /* Others not supported yet */ | 1064 | } else { /* Others not supported yet */ |
1065 | semantic_error("%%%s is not supported.\n", arg); | 1065 | semantic_error("%%%s is not supported.\n", arg); |
1066 | return -ENOTSUP; | 1066 | return -ENOTSUP; |
1067 | } | 1067 | } |
1068 | break; | 1068 | break; |
1069 | default: /* Buggy case */ | 1069 | default: /* Buggy case */ |
1070 | pr_err("This program has a bug at %s:%d.\n", | 1070 | pr_err("This program has a bug at %s:%d.\n", |
1071 | __FILE__, __LINE__); | 1071 | __FILE__, __LINE__); |
1072 | return -ENOTSUP; | 1072 | return -ENOTSUP; |
1073 | break; | 1073 | break; |
1074 | } | 1074 | } |
1075 | } | 1075 | } |
1076 | 1076 | ||
1077 | /* Exclusion check */ | 1077 | /* Exclusion check */ |
1078 | if (pp->lazy_line && pp->line) { | 1078 | if (pp->lazy_line && pp->line) { |
1079 | semantic_error("Lazy pattern can't be used with" | 1079 | semantic_error("Lazy pattern can't be used with" |
1080 | " line number.\n"); | 1080 | " line number.\n"); |
1081 | return -EINVAL; | 1081 | return -EINVAL; |
1082 | } | 1082 | } |
1083 | 1083 | ||
1084 | if (pp->lazy_line && pp->offset) { | 1084 | if (pp->lazy_line && pp->offset) { |
1085 | semantic_error("Lazy pattern can't be used with offset.\n"); | 1085 | semantic_error("Lazy pattern can't be used with offset.\n"); |
1086 | return -EINVAL; | 1086 | return -EINVAL; |
1087 | } | 1087 | } |
1088 | 1088 | ||
1089 | if (pp->line && pp->offset) { | 1089 | if (pp->line && pp->offset) { |
1090 | semantic_error("Offset can't be used with line number.\n"); | 1090 | semantic_error("Offset can't be used with line number.\n"); |
1091 | return -EINVAL; | 1091 | return -EINVAL; |
1092 | } | 1092 | } |
1093 | 1093 | ||
1094 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { | 1094 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) { |
1095 | semantic_error("File always requires line number or " | 1095 | semantic_error("File always requires line number or " |
1096 | "lazy pattern.\n"); | 1096 | "lazy pattern.\n"); |
1097 | return -EINVAL; | 1097 | return -EINVAL; |
1098 | } | 1098 | } |
1099 | 1099 | ||
1100 | if (pp->offset && !pp->function) { | 1100 | if (pp->offset && !pp->function) { |
1101 | semantic_error("Offset requires an entry function.\n"); | 1101 | semantic_error("Offset requires an entry function.\n"); |
1102 | return -EINVAL; | 1102 | return -EINVAL; |
1103 | } | 1103 | } |
1104 | 1104 | ||
1105 | if (pp->retprobe && !pp->function) { | 1105 | if (pp->retprobe && !pp->function) { |
1106 | semantic_error("Return probe requires an entry function.\n"); | 1106 | semantic_error("Return probe requires an entry function.\n"); |
1107 | return -EINVAL; | 1107 | return -EINVAL; |
1108 | } | 1108 | } |
1109 | 1109 | ||
1110 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { | 1110 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) { |
1111 | semantic_error("Offset/Line/Lazy pattern can't be used with " | 1111 | semantic_error("Offset/Line/Lazy pattern can't be used with " |
1112 | "return probe.\n"); | 1112 | "return probe.\n"); |
1113 | return -EINVAL; | 1113 | return -EINVAL; |
1114 | } | 1114 | } |
1115 | 1115 | ||
1116 | pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", | 1116 | pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", |
1117 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, | 1117 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, |
1118 | pp->lazy_line); | 1118 | pp->lazy_line); |
1119 | return 0; | 1119 | return 0; |
1120 | } | 1120 | } |
1121 | 1121 | ||
1122 | /* Parse perf-probe event argument */ | 1122 | /* Parse perf-probe event argument */ |
1123 | static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | 1123 | static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) |
1124 | { | 1124 | { |
1125 | char *tmp, *goodname; | 1125 | char *tmp, *goodname; |
1126 | struct perf_probe_arg_field **fieldp; | 1126 | struct perf_probe_arg_field **fieldp; |
1127 | 1127 | ||
1128 | pr_debug("parsing arg: %s into ", str); | 1128 | pr_debug("parsing arg: %s into ", str); |
1129 | 1129 | ||
1130 | tmp = strchr(str, '='); | 1130 | tmp = strchr(str, '='); |
1131 | if (tmp) { | 1131 | if (tmp) { |
1132 | arg->name = strndup(str, tmp - str); | 1132 | arg->name = strndup(str, tmp - str); |
1133 | if (arg->name == NULL) | 1133 | if (arg->name == NULL) |
1134 | return -ENOMEM; | 1134 | return -ENOMEM; |
1135 | pr_debug("name:%s ", arg->name); | 1135 | pr_debug("name:%s ", arg->name); |
1136 | str = tmp + 1; | 1136 | str = tmp + 1; |
1137 | } | 1137 | } |
1138 | 1138 | ||
1139 | tmp = strchr(str, ':'); | 1139 | tmp = strchr(str, ':'); |
1140 | if (tmp) { /* Type setting */ | 1140 | if (tmp) { /* Type setting */ |
1141 | *tmp = '\0'; | 1141 | *tmp = '\0'; |
1142 | arg->type = strdup(tmp + 1); | 1142 | arg->type = strdup(tmp + 1); |
1143 | if (arg->type == NULL) | 1143 | if (arg->type == NULL) |
1144 | return -ENOMEM; | 1144 | return -ENOMEM; |
1145 | pr_debug("type:%s ", arg->type); | 1145 | pr_debug("type:%s ", arg->type); |
1146 | } | 1146 | } |
1147 | 1147 | ||
1148 | tmp = strpbrk(str, "-.["); | 1148 | tmp = strpbrk(str, "-.["); |
1149 | if (!is_c_varname(str) || !tmp) { | 1149 | if (!is_c_varname(str) || !tmp) { |
1150 | /* A variable, register, symbol or special value */ | 1150 | /* A variable, register, symbol or special value */ |
1151 | arg->var = strdup(str); | 1151 | arg->var = strdup(str); |
1152 | if (arg->var == NULL) | 1152 | if (arg->var == NULL) |
1153 | return -ENOMEM; | 1153 | return -ENOMEM; |
1154 | pr_debug("%s\n", arg->var); | 1154 | pr_debug("%s\n", arg->var); |
1155 | return 0; | 1155 | return 0; |
1156 | } | 1156 | } |
1157 | 1157 | ||
1158 | /* Structure fields or array element */ | 1158 | /* Structure fields or array element */ |
1159 | arg->var = strndup(str, tmp - str); | 1159 | arg->var = strndup(str, tmp - str); |
1160 | if (arg->var == NULL) | 1160 | if (arg->var == NULL) |
1161 | return -ENOMEM; | 1161 | return -ENOMEM; |
1162 | goodname = arg->var; | 1162 | goodname = arg->var; |
1163 | pr_debug("%s, ", arg->var); | 1163 | pr_debug("%s, ", arg->var); |
1164 | fieldp = &arg->field; | 1164 | fieldp = &arg->field; |
1165 | 1165 | ||
1166 | do { | 1166 | do { |
1167 | *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); | 1167 | *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); |
1168 | if (*fieldp == NULL) | 1168 | if (*fieldp == NULL) |
1169 | return -ENOMEM; | 1169 | return -ENOMEM; |
1170 | if (*tmp == '[') { /* Array */ | 1170 | if (*tmp == '[') { /* Array */ |
1171 | str = tmp; | 1171 | str = tmp; |
1172 | (*fieldp)->index = strtol(str + 1, &tmp, 0); | 1172 | (*fieldp)->index = strtol(str + 1, &tmp, 0); |
1173 | (*fieldp)->ref = true; | 1173 | (*fieldp)->ref = true; |
1174 | if (*tmp != ']' || tmp == str + 1) { | 1174 | if (*tmp != ']' || tmp == str + 1) { |
1175 | semantic_error("Array index must be a" | 1175 | semantic_error("Array index must be a" |
1176 | " number.\n"); | 1176 | " number.\n"); |
1177 | return -EINVAL; | 1177 | return -EINVAL; |
1178 | } | 1178 | } |
1179 | tmp++; | 1179 | tmp++; |
1180 | if (*tmp == '\0') | 1180 | if (*tmp == '\0') |
1181 | tmp = NULL; | 1181 | tmp = NULL; |
1182 | } else { /* Structure */ | 1182 | } else { /* Structure */ |
1183 | if (*tmp == '.') { | 1183 | if (*tmp == '.') { |
1184 | str = tmp + 1; | 1184 | str = tmp + 1; |
1185 | (*fieldp)->ref = false; | 1185 | (*fieldp)->ref = false; |
1186 | } else if (tmp[1] == '>') { | 1186 | } else if (tmp[1] == '>') { |
1187 | str = tmp + 2; | 1187 | str = tmp + 2; |
1188 | (*fieldp)->ref = true; | 1188 | (*fieldp)->ref = true; |
1189 | } else { | 1189 | } else { |
1190 | semantic_error("Argument parse error: %s\n", | 1190 | semantic_error("Argument parse error: %s\n", |
1191 | str); | 1191 | str); |
1192 | return -EINVAL; | 1192 | return -EINVAL; |
1193 | } | 1193 | } |
1194 | tmp = strpbrk(str, "-.["); | 1194 | tmp = strpbrk(str, "-.["); |
1195 | } | 1195 | } |
1196 | if (tmp) { | 1196 | if (tmp) { |
1197 | (*fieldp)->name = strndup(str, tmp - str); | 1197 | (*fieldp)->name = strndup(str, tmp - str); |
1198 | if ((*fieldp)->name == NULL) | 1198 | if ((*fieldp)->name == NULL) |
1199 | return -ENOMEM; | 1199 | return -ENOMEM; |
1200 | if (*str != '[') | 1200 | if (*str != '[') |
1201 | goodname = (*fieldp)->name; | 1201 | goodname = (*fieldp)->name; |
1202 | pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); | 1202 | pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); |
1203 | fieldp = &(*fieldp)->next; | 1203 | fieldp = &(*fieldp)->next; |
1204 | } | 1204 | } |
1205 | } while (tmp); | 1205 | } while (tmp); |
1206 | (*fieldp)->name = strdup(str); | 1206 | (*fieldp)->name = strdup(str); |
1207 | if ((*fieldp)->name == NULL) | 1207 | if ((*fieldp)->name == NULL) |
1208 | return -ENOMEM; | 1208 | return -ENOMEM; |
1209 | if (*str != '[') | 1209 | if (*str != '[') |
1210 | goodname = (*fieldp)->name; | 1210 | goodname = (*fieldp)->name; |
1211 | pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); | 1211 | pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); |
1212 | 1212 | ||
1213 | /* If no name is specified, set the last field name (not array index)*/ | 1213 | /* If no name is specified, set the last field name (not array index)*/ |
1214 | if (!arg->name) { | 1214 | if (!arg->name) { |
1215 | arg->name = strdup(goodname); | 1215 | arg->name = strdup(goodname); |
1216 | if (arg->name == NULL) | 1216 | if (arg->name == NULL) |
1217 | return -ENOMEM; | 1217 | return -ENOMEM; |
1218 | } | 1218 | } |
1219 | return 0; | 1219 | return 0; |
1220 | } | 1220 | } |
1221 | 1221 | ||
1222 | /* Parse perf-probe event command */ | 1222 | /* Parse perf-probe event command */ |
1223 | int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) | 1223 | int parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) |
1224 | { | 1224 | { |
1225 | char **argv; | 1225 | char **argv; |
1226 | int argc, i, ret = 0; | 1226 | int argc, i, ret = 0; |
1227 | 1227 | ||
1228 | argv = argv_split(cmd, &argc); | 1228 | argv = argv_split(cmd, &argc); |
1229 | if (!argv) { | 1229 | if (!argv) { |
1230 | pr_debug("Failed to split arguments.\n"); | 1230 | pr_debug("Failed to split arguments.\n"); |
1231 | return -ENOMEM; | 1231 | return -ENOMEM; |
1232 | } | 1232 | } |
1233 | if (argc - 1 > MAX_PROBE_ARGS) { | 1233 | if (argc - 1 > MAX_PROBE_ARGS) { |
1234 | semantic_error("Too many probe arguments (%d).\n", argc - 1); | 1234 | semantic_error("Too many probe arguments (%d).\n", argc - 1); |
1235 | ret = -ERANGE; | 1235 | ret = -ERANGE; |
1236 | goto out; | 1236 | goto out; |
1237 | } | 1237 | } |
1238 | /* Parse probe point */ | 1238 | /* Parse probe point */ |
1239 | ret = parse_perf_probe_point(argv[0], pev); | 1239 | ret = parse_perf_probe_point(argv[0], pev); |
1240 | if (ret < 0) | 1240 | if (ret < 0) |
1241 | goto out; | 1241 | goto out; |
1242 | 1242 | ||
1243 | /* Copy arguments and ensure return probe has no C argument */ | 1243 | /* Copy arguments and ensure return probe has no C argument */ |
1244 | pev->nargs = argc - 1; | 1244 | pev->nargs = argc - 1; |
1245 | pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); | 1245 | pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); |
1246 | if (pev->args == NULL) { | 1246 | if (pev->args == NULL) { |
1247 | ret = -ENOMEM; | 1247 | ret = -ENOMEM; |
1248 | goto out; | 1248 | goto out; |
1249 | } | 1249 | } |
1250 | for (i = 0; i < pev->nargs && ret >= 0; i++) { | 1250 | for (i = 0; i < pev->nargs && ret >= 0; i++) { |
1251 | ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]); | 1251 | ret = parse_perf_probe_arg(argv[i + 1], &pev->args[i]); |
1252 | if (ret >= 0 && | 1252 | if (ret >= 0 && |
1253 | is_c_varname(pev->args[i].var) && pev->point.retprobe) { | 1253 | is_c_varname(pev->args[i].var) && pev->point.retprobe) { |
1254 | semantic_error("You can't specify local variable for" | 1254 | semantic_error("You can't specify local variable for" |
1255 | " kretprobe.\n"); | 1255 | " kretprobe.\n"); |
1256 | ret = -EINVAL; | 1256 | ret = -EINVAL; |
1257 | } | 1257 | } |
1258 | } | 1258 | } |
1259 | out: | 1259 | out: |
1260 | argv_free(argv); | 1260 | argv_free(argv); |
1261 | 1261 | ||
1262 | return ret; | 1262 | return ret; |
1263 | } | 1263 | } |
1264 | 1264 | ||
1265 | /* Return true if this perf_probe_event requires debuginfo */ | 1265 | /* Return true if this perf_probe_event requires debuginfo */ |
1266 | bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) | 1266 | bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) |
1267 | { | 1267 | { |
1268 | int i; | 1268 | int i; |
1269 | 1269 | ||
1270 | if (pev->point.file || pev->point.line || pev->point.lazy_line) | 1270 | if (pev->point.file || pev->point.line || pev->point.lazy_line) |
1271 | return true; | 1271 | return true; |
1272 | 1272 | ||
1273 | for (i = 0; i < pev->nargs; i++) | 1273 | for (i = 0; i < pev->nargs; i++) |
1274 | if (is_c_varname(pev->args[i].var)) | 1274 | if (is_c_varname(pev->args[i].var)) |
1275 | return true; | 1275 | return true; |
1276 | 1276 | ||
1277 | return false; | 1277 | return false; |
1278 | } | 1278 | } |
1279 | 1279 | ||
1280 | /* Parse probe_events event into struct probe_point */ | 1280 | /* Parse probe_events event into struct probe_point */ |
1281 | static int parse_probe_trace_command(const char *cmd, | 1281 | static int parse_probe_trace_command(const char *cmd, |
1282 | struct probe_trace_event *tev) | 1282 | struct probe_trace_event *tev) |
1283 | { | 1283 | { |
1284 | struct probe_trace_point *tp = &tev->point; | 1284 | struct probe_trace_point *tp = &tev->point; |
1285 | char pr; | 1285 | char pr; |
1286 | char *p; | 1286 | char *p; |
1287 | char *argv0_str = NULL, *fmt, *fmt1_str, *fmt2_str, *fmt3_str; | 1287 | char *argv0_str = NULL, *fmt, *fmt1_str, *fmt2_str, *fmt3_str; |
1288 | int ret, i, argc; | 1288 | int ret, i, argc; |
1289 | char **argv; | 1289 | char **argv; |
1290 | 1290 | ||
1291 | pr_debug("Parsing probe_events: %s\n", cmd); | 1291 | pr_debug("Parsing probe_events: %s\n", cmd); |
1292 | argv = argv_split(cmd, &argc); | 1292 | argv = argv_split(cmd, &argc); |
1293 | if (!argv) { | 1293 | if (!argv) { |
1294 | pr_debug("Failed to split arguments.\n"); | 1294 | pr_debug("Failed to split arguments.\n"); |
1295 | return -ENOMEM; | 1295 | return -ENOMEM; |
1296 | } | 1296 | } |
1297 | if (argc < 2) { | 1297 | if (argc < 2) { |
1298 | semantic_error("Too few probe arguments.\n"); | 1298 | semantic_error("Too few probe arguments.\n"); |
1299 | ret = -ERANGE; | 1299 | ret = -ERANGE; |
1300 | goto out; | 1300 | goto out; |
1301 | } | 1301 | } |
1302 | 1302 | ||
1303 | /* Scan event and group name. */ | 1303 | /* Scan event and group name. */ |
1304 | argv0_str = strdup(argv[0]); | 1304 | argv0_str = strdup(argv[0]); |
1305 | if (argv0_str == NULL) { | 1305 | if (argv0_str == NULL) { |
1306 | ret = -ENOMEM; | 1306 | ret = -ENOMEM; |
1307 | goto out; | 1307 | goto out; |
1308 | } | 1308 | } |
1309 | fmt1_str = strtok_r(argv0_str, ":", &fmt); | 1309 | fmt1_str = strtok_r(argv0_str, ":", &fmt); |
1310 | fmt2_str = strtok_r(NULL, "/", &fmt); | 1310 | fmt2_str = strtok_r(NULL, "/", &fmt); |
1311 | fmt3_str = strtok_r(NULL, " \t", &fmt); | 1311 | fmt3_str = strtok_r(NULL, " \t", &fmt); |
1312 | if (fmt1_str == NULL || strlen(fmt1_str) != 1 || fmt2_str == NULL | 1312 | if (fmt1_str == NULL || strlen(fmt1_str) != 1 || fmt2_str == NULL |
1313 | || fmt3_str == NULL) { | 1313 | || fmt3_str == NULL) { |
1314 | semantic_error("Failed to parse event name: %s\n", argv[0]); | 1314 | semantic_error("Failed to parse event name: %s\n", argv[0]); |
1315 | ret = -EINVAL; | 1315 | ret = -EINVAL; |
1316 | goto out; | 1316 | goto out; |
1317 | } | 1317 | } |
1318 | pr = fmt1_str[0]; | 1318 | pr = fmt1_str[0]; |
1319 | tev->group = strdup(fmt2_str); | 1319 | tev->group = strdup(fmt2_str); |
1320 | tev->event = strdup(fmt3_str); | 1320 | tev->event = strdup(fmt3_str); |
1321 | if (tev->group == NULL || tev->event == NULL) { | 1321 | if (tev->group == NULL || tev->event == NULL) { |
1322 | ret = -ENOMEM; | 1322 | ret = -ENOMEM; |
1323 | goto out; | 1323 | goto out; |
1324 | } | 1324 | } |
1325 | pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); | 1325 | pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); |
1326 | 1326 | ||
1327 | tp->retprobe = (pr == 'r'); | 1327 | tp->retprobe = (pr == 'r'); |
1328 | 1328 | ||
1329 | /* Scan module name(if there), function name and offset */ | 1329 | /* Scan module name(if there), function name and offset */ |
1330 | p = strchr(argv[1], ':'); | 1330 | p = strchr(argv[1], ':'); |
1331 | if (p) { | 1331 | if (p) { |
1332 | tp->module = strndup(argv[1], p - argv[1]); | 1332 | tp->module = strndup(argv[1], p - argv[1]); |
1333 | p++; | 1333 | p++; |
1334 | } else | 1334 | } else |
1335 | p = argv[1]; | 1335 | p = argv[1]; |
1336 | fmt1_str = strtok_r(p, "+", &fmt); | 1336 | fmt1_str = strtok_r(p, "+", &fmt); |
1337 | if (fmt1_str[0] == '0') /* only the address started with 0x */ | 1337 | if (fmt1_str[0] == '0') /* only the address started with 0x */ |
1338 | tp->address = strtoul(fmt1_str, NULL, 0); | 1338 | tp->address = strtoul(fmt1_str, NULL, 0); |
1339 | else { | 1339 | else { |
1340 | /* Only the symbol-based probe has offset */ | 1340 | /* Only the symbol-based probe has offset */ |
1341 | tp->symbol = strdup(fmt1_str); | 1341 | tp->symbol = strdup(fmt1_str); |
1342 | if (tp->symbol == NULL) { | 1342 | if (tp->symbol == NULL) { |
1343 | ret = -ENOMEM; | 1343 | ret = -ENOMEM; |
1344 | goto out; | 1344 | goto out; |
1345 | } | 1345 | } |
1346 | fmt2_str = strtok_r(NULL, "", &fmt); | 1346 | fmt2_str = strtok_r(NULL, "", &fmt); |
1347 | if (fmt2_str == NULL) | 1347 | if (fmt2_str == NULL) |
1348 | tp->offset = 0; | 1348 | tp->offset = 0; |
1349 | else | 1349 | else |
1350 | tp->offset = strtoul(fmt2_str, NULL, 10); | 1350 | tp->offset = strtoul(fmt2_str, NULL, 10); |
1351 | } | 1351 | } |
1352 | 1352 | ||
1353 | tev->nargs = argc - 2; | 1353 | tev->nargs = argc - 2; |
1354 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); | 1354 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
1355 | if (tev->args == NULL) { | 1355 | if (tev->args == NULL) { |
1356 | ret = -ENOMEM; | 1356 | ret = -ENOMEM; |
1357 | goto out; | 1357 | goto out; |
1358 | } | 1358 | } |
1359 | for (i = 0; i < tev->nargs; i++) { | 1359 | for (i = 0; i < tev->nargs; i++) { |
1360 | p = strchr(argv[i + 2], '='); | 1360 | p = strchr(argv[i + 2], '='); |
1361 | if (p) /* We don't need which register is assigned. */ | 1361 | if (p) /* We don't need which register is assigned. */ |
1362 | *p++ = '\0'; | 1362 | *p++ = '\0'; |
1363 | else | 1363 | else |
1364 | p = argv[i + 2]; | 1364 | p = argv[i + 2]; |
1365 | tev->args[i].name = strdup(argv[i + 2]); | 1365 | tev->args[i].name = strdup(argv[i + 2]); |
1366 | /* TODO: parse regs and offset */ | 1366 | /* TODO: parse regs and offset */ |
1367 | tev->args[i].value = strdup(p); | 1367 | tev->args[i].value = strdup(p); |
1368 | if (tev->args[i].name == NULL || tev->args[i].value == NULL) { | 1368 | if (tev->args[i].name == NULL || tev->args[i].value == NULL) { |
1369 | ret = -ENOMEM; | 1369 | ret = -ENOMEM; |
1370 | goto out; | 1370 | goto out; |
1371 | } | 1371 | } |
1372 | } | 1372 | } |
1373 | ret = 0; | 1373 | ret = 0; |
1374 | out: | 1374 | out: |
1375 | free(argv0_str); | 1375 | free(argv0_str); |
1376 | argv_free(argv); | 1376 | argv_free(argv); |
1377 | return ret; | 1377 | return ret; |
1378 | } | 1378 | } |
1379 | 1379 | ||
1380 | /* Compose only probe arg */ | 1380 | /* Compose only probe arg */ |
1381 | int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) | 1381 | int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) |
1382 | { | 1382 | { |
1383 | struct perf_probe_arg_field *field = pa->field; | 1383 | struct perf_probe_arg_field *field = pa->field; |
1384 | int ret; | 1384 | int ret; |
1385 | char *tmp = buf; | 1385 | char *tmp = buf; |
1386 | 1386 | ||
1387 | if (pa->name && pa->var) | 1387 | if (pa->name && pa->var) |
1388 | ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var); | 1388 | ret = e_snprintf(tmp, len, "%s=%s", pa->name, pa->var); |
1389 | else | 1389 | else |
1390 | ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var); | 1390 | ret = e_snprintf(tmp, len, "%s", pa->name ? pa->name : pa->var); |
1391 | if (ret <= 0) | 1391 | if (ret <= 0) |
1392 | goto error; | 1392 | goto error; |
1393 | tmp += ret; | 1393 | tmp += ret; |
1394 | len -= ret; | 1394 | len -= ret; |
1395 | 1395 | ||
1396 | while (field) { | 1396 | while (field) { |
1397 | if (field->name[0] == '[') | 1397 | if (field->name[0] == '[') |
1398 | ret = e_snprintf(tmp, len, "%s", field->name); | 1398 | ret = e_snprintf(tmp, len, "%s", field->name); |
1399 | else | 1399 | else |
1400 | ret = e_snprintf(tmp, len, "%s%s", | 1400 | ret = e_snprintf(tmp, len, "%s%s", |
1401 | field->ref ? "->" : ".", field->name); | 1401 | field->ref ? "->" : ".", field->name); |
1402 | if (ret <= 0) | 1402 | if (ret <= 0) |
1403 | goto error; | 1403 | goto error; |
1404 | tmp += ret; | 1404 | tmp += ret; |
1405 | len -= ret; | 1405 | len -= ret; |
1406 | field = field->next; | 1406 | field = field->next; |
1407 | } | 1407 | } |
1408 | 1408 | ||
1409 | if (pa->type) { | 1409 | if (pa->type) { |
1410 | ret = e_snprintf(tmp, len, ":%s", pa->type); | 1410 | ret = e_snprintf(tmp, len, ":%s", pa->type); |
1411 | if (ret <= 0) | 1411 | if (ret <= 0) |
1412 | goto error; | 1412 | goto error; |
1413 | tmp += ret; | 1413 | tmp += ret; |
1414 | len -= ret; | 1414 | len -= ret; |
1415 | } | 1415 | } |
1416 | 1416 | ||
1417 | return tmp - buf; | 1417 | return tmp - buf; |
1418 | error: | 1418 | error: |
1419 | pr_debug("Failed to synthesize perf probe argument: %d\n", ret); | 1419 | pr_debug("Failed to synthesize perf probe argument: %d\n", ret); |
1420 | return ret; | 1420 | return ret; |
1421 | } | 1421 | } |
1422 | 1422 | ||
1423 | /* Compose only probe point (not argument) */ | 1423 | /* Compose only probe point (not argument) */ |
1424 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | 1424 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp) |
1425 | { | 1425 | { |
1426 | char *buf, *tmp; | 1426 | char *buf, *tmp; |
1427 | char offs[32] = "", line[32] = "", file[32] = ""; | 1427 | char offs[32] = "", line[32] = "", file[32] = ""; |
1428 | int ret, len; | 1428 | int ret, len; |
1429 | 1429 | ||
1430 | buf = zalloc(MAX_CMDLEN); | 1430 | buf = zalloc(MAX_CMDLEN); |
1431 | if (buf == NULL) { | 1431 | if (buf == NULL) { |
1432 | ret = -ENOMEM; | 1432 | ret = -ENOMEM; |
1433 | goto error; | 1433 | goto error; |
1434 | } | 1434 | } |
1435 | if (pp->offset) { | 1435 | if (pp->offset) { |
1436 | ret = e_snprintf(offs, 32, "+%lu", pp->offset); | 1436 | ret = e_snprintf(offs, 32, "+%lu", pp->offset); |
1437 | if (ret <= 0) | 1437 | if (ret <= 0) |
1438 | goto error; | 1438 | goto error; |
1439 | } | 1439 | } |
1440 | if (pp->line) { | 1440 | if (pp->line) { |
1441 | ret = e_snprintf(line, 32, ":%d", pp->line); | 1441 | ret = e_snprintf(line, 32, ":%d", pp->line); |
1442 | if (ret <= 0) | 1442 | if (ret <= 0) |
1443 | goto error; | 1443 | goto error; |
1444 | } | 1444 | } |
1445 | if (pp->file) { | 1445 | if (pp->file) { |
1446 | tmp = pp->file; | 1446 | tmp = pp->file; |
1447 | len = strlen(tmp); | 1447 | len = strlen(tmp); |
1448 | if (len > 30) { | 1448 | if (len > 30) { |
1449 | tmp = strchr(pp->file + len - 30, '/'); | 1449 | tmp = strchr(pp->file + len - 30, '/'); |
1450 | tmp = tmp ? tmp + 1 : pp->file + len - 30; | 1450 | tmp = tmp ? tmp + 1 : pp->file + len - 30; |
1451 | } | 1451 | } |
1452 | ret = e_snprintf(file, 32, "@%s", tmp); | 1452 | ret = e_snprintf(file, 32, "@%s", tmp); |
1453 | if (ret <= 0) | 1453 | if (ret <= 0) |
1454 | goto error; | 1454 | goto error; |
1455 | } | 1455 | } |
1456 | 1456 | ||
1457 | if (pp->function) | 1457 | if (pp->function) |
1458 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function, | 1458 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function, |
1459 | offs, pp->retprobe ? "%return" : "", line, | 1459 | offs, pp->retprobe ? "%return" : "", line, |
1460 | file); | 1460 | file); |
1461 | else | 1461 | else |
1462 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line); | 1462 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line); |
1463 | if (ret <= 0) | 1463 | if (ret <= 0) |
1464 | goto error; | 1464 | goto error; |
1465 | 1465 | ||
1466 | return buf; | 1466 | return buf; |
1467 | error: | 1467 | error: |
1468 | pr_debug("Failed to synthesize perf probe point: %d\n", ret); | 1468 | pr_debug("Failed to synthesize perf probe point: %d\n", ret); |
1469 | free(buf); | 1469 | free(buf); |
1470 | return NULL; | 1470 | return NULL; |
1471 | } | 1471 | } |
1472 | 1472 | ||
1473 | #if 0 | 1473 | #if 0 |
1474 | char *synthesize_perf_probe_command(struct perf_probe_event *pev) | 1474 | char *synthesize_perf_probe_command(struct perf_probe_event *pev) |
1475 | { | 1475 | { |
1476 | char *buf; | 1476 | char *buf; |
1477 | int i, len, ret; | 1477 | int i, len, ret; |
1478 | 1478 | ||
1479 | buf = synthesize_perf_probe_point(&pev->point); | 1479 | buf = synthesize_perf_probe_point(&pev->point); |
1480 | if (!buf) | 1480 | if (!buf) |
1481 | return NULL; | 1481 | return NULL; |
1482 | 1482 | ||
1483 | len = strlen(buf); | 1483 | len = strlen(buf); |
1484 | for (i = 0; i < pev->nargs; i++) { | 1484 | for (i = 0; i < pev->nargs; i++) { |
1485 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | 1485 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", |
1486 | pev->args[i].name); | 1486 | pev->args[i].name); |
1487 | if (ret <= 0) { | 1487 | if (ret <= 0) { |
1488 | free(buf); | 1488 | free(buf); |
1489 | return NULL; | 1489 | return NULL; |
1490 | } | 1490 | } |
1491 | len += ret; | 1491 | len += ret; |
1492 | } | 1492 | } |
1493 | 1493 | ||
1494 | return buf; | 1494 | return buf; |
1495 | } | 1495 | } |
1496 | #endif | 1496 | #endif |
1497 | 1497 | ||
1498 | static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref, | 1498 | static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref, |
1499 | char **buf, size_t *buflen, | 1499 | char **buf, size_t *buflen, |
1500 | int depth) | 1500 | int depth) |
1501 | { | 1501 | { |
1502 | int ret; | 1502 | int ret; |
1503 | if (ref->next) { | 1503 | if (ref->next) { |
1504 | depth = __synthesize_probe_trace_arg_ref(ref->next, buf, | 1504 | depth = __synthesize_probe_trace_arg_ref(ref->next, buf, |
1505 | buflen, depth + 1); | 1505 | buflen, depth + 1); |
1506 | if (depth < 0) | 1506 | if (depth < 0) |
1507 | goto out; | 1507 | goto out; |
1508 | } | 1508 | } |
1509 | 1509 | ||
1510 | ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset); | 1510 | ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset); |
1511 | if (ret < 0) | 1511 | if (ret < 0) |
1512 | depth = ret; | 1512 | depth = ret; |
1513 | else { | 1513 | else { |
1514 | *buf += ret; | 1514 | *buf += ret; |
1515 | *buflen -= ret; | 1515 | *buflen -= ret; |
1516 | } | 1516 | } |
1517 | out: | 1517 | out: |
1518 | return depth; | 1518 | return depth; |
1519 | 1519 | ||
1520 | } | 1520 | } |
1521 | 1521 | ||
1522 | static int synthesize_probe_trace_arg(struct probe_trace_arg *arg, | 1522 | static int synthesize_probe_trace_arg(struct probe_trace_arg *arg, |
1523 | char *buf, size_t buflen) | 1523 | char *buf, size_t buflen) |
1524 | { | 1524 | { |
1525 | struct probe_trace_arg_ref *ref = arg->ref; | 1525 | struct probe_trace_arg_ref *ref = arg->ref; |
1526 | int ret, depth = 0; | 1526 | int ret, depth = 0; |
1527 | char *tmp = buf; | 1527 | char *tmp = buf; |
1528 | 1528 | ||
1529 | /* Argument name or separator */ | 1529 | /* Argument name or separator */ |
1530 | if (arg->name) | 1530 | if (arg->name) |
1531 | ret = e_snprintf(buf, buflen, " %s=", arg->name); | 1531 | ret = e_snprintf(buf, buflen, " %s=", arg->name); |
1532 | else | 1532 | else |
1533 | ret = e_snprintf(buf, buflen, " "); | 1533 | ret = e_snprintf(buf, buflen, " "); |
1534 | if (ret < 0) | 1534 | if (ret < 0) |
1535 | return ret; | 1535 | return ret; |
1536 | buf += ret; | 1536 | buf += ret; |
1537 | buflen -= ret; | 1537 | buflen -= ret; |
1538 | 1538 | ||
1539 | /* Special case: @XXX */ | 1539 | /* Special case: @XXX */ |
1540 | if (arg->value[0] == '@' && arg->ref) | 1540 | if (arg->value[0] == '@' && arg->ref) |
1541 | ref = ref->next; | 1541 | ref = ref->next; |
1542 | 1542 | ||
1543 | /* Dereferencing arguments */ | 1543 | /* Dereferencing arguments */ |
1544 | if (ref) { | 1544 | if (ref) { |
1545 | depth = __synthesize_probe_trace_arg_ref(ref, &buf, | 1545 | depth = __synthesize_probe_trace_arg_ref(ref, &buf, |
1546 | &buflen, 1); | 1546 | &buflen, 1); |
1547 | if (depth < 0) | 1547 | if (depth < 0) |
1548 | return depth; | 1548 | return depth; |
1549 | } | 1549 | } |
1550 | 1550 | ||
1551 | /* Print argument value */ | 1551 | /* Print argument value */ |
1552 | if (arg->value[0] == '@' && arg->ref) | 1552 | if (arg->value[0] == '@' && arg->ref) |
1553 | ret = e_snprintf(buf, buflen, "%s%+ld", arg->value, | 1553 | ret = e_snprintf(buf, buflen, "%s%+ld", arg->value, |
1554 | arg->ref->offset); | 1554 | arg->ref->offset); |
1555 | else | 1555 | else |
1556 | ret = e_snprintf(buf, buflen, "%s", arg->value); | 1556 | ret = e_snprintf(buf, buflen, "%s", arg->value); |
1557 | if (ret < 0) | 1557 | if (ret < 0) |
1558 | return ret; | 1558 | return ret; |
1559 | buf += ret; | 1559 | buf += ret; |
1560 | buflen -= ret; | 1560 | buflen -= ret; |
1561 | 1561 | ||
1562 | /* Closing */ | 1562 | /* Closing */ |
1563 | while (depth--) { | 1563 | while (depth--) { |
1564 | ret = e_snprintf(buf, buflen, ")"); | 1564 | ret = e_snprintf(buf, buflen, ")"); |
1565 | if (ret < 0) | 1565 | if (ret < 0) |
1566 | return ret; | 1566 | return ret; |
1567 | buf += ret; | 1567 | buf += ret; |
1568 | buflen -= ret; | 1568 | buflen -= ret; |
1569 | } | 1569 | } |
1570 | /* Print argument type */ | 1570 | /* Print argument type */ |
1571 | if (arg->type) { | 1571 | if (arg->type) { |
1572 | ret = e_snprintf(buf, buflen, ":%s", arg->type); | 1572 | ret = e_snprintf(buf, buflen, ":%s", arg->type); |
1573 | if (ret <= 0) | 1573 | if (ret <= 0) |
1574 | return ret; | 1574 | return ret; |
1575 | buf += ret; | 1575 | buf += ret; |
1576 | } | 1576 | } |
1577 | 1577 | ||
1578 | return buf - tmp; | 1578 | return buf - tmp; |
1579 | } | 1579 | } |
1580 | 1580 | ||
1581 | char *synthesize_probe_trace_command(struct probe_trace_event *tev) | 1581 | char *synthesize_probe_trace_command(struct probe_trace_event *tev) |
1582 | { | 1582 | { |
1583 | struct probe_trace_point *tp = &tev->point; | 1583 | struct probe_trace_point *tp = &tev->point; |
1584 | char *buf; | 1584 | char *buf; |
1585 | int i, len, ret; | 1585 | int i, len, ret; |
1586 | 1586 | ||
1587 | buf = zalloc(MAX_CMDLEN); | 1587 | buf = zalloc(MAX_CMDLEN); |
1588 | if (buf == NULL) | 1588 | if (buf == NULL) |
1589 | return NULL; | 1589 | return NULL; |
1590 | 1590 | ||
1591 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s ", tp->retprobe ? 'r' : 'p', | 1591 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s ", tp->retprobe ? 'r' : 'p', |
1592 | tev->group, tev->event); | 1592 | tev->group, tev->event); |
1593 | if (len <= 0) | 1593 | if (len <= 0) |
1594 | goto error; | 1594 | goto error; |
1595 | 1595 | ||
1596 | /* Uprobes must have tp->address and tp->module */ | 1596 | /* Uprobes must have tp->address and tp->module */ |
1597 | if (tev->uprobes && (!tp->address || !tp->module)) | 1597 | if (tev->uprobes && (!tp->address || !tp->module)) |
1598 | goto error; | 1598 | goto error; |
1599 | 1599 | ||
1600 | /* Use the tp->address for uprobes */ | 1600 | /* Use the tp->address for uprobes */ |
1601 | if (tev->uprobes) | 1601 | if (tev->uprobes) |
1602 | ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx", | 1602 | ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s:0x%lx", |
1603 | tp->module, tp->address); | 1603 | tp->module, tp->address); |
1604 | else | 1604 | else |
1605 | ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu", | 1605 | ret = e_snprintf(buf + len, MAX_CMDLEN - len, "%s%s%s+%lu", |
1606 | tp->module ?: "", tp->module ? ":" : "", | 1606 | tp->module ?: "", tp->module ? ":" : "", |
1607 | tp->symbol, tp->offset); | 1607 | tp->symbol, tp->offset); |
1608 | 1608 | ||
1609 | if (ret <= 0) | 1609 | if (ret <= 0) |
1610 | goto error; | 1610 | goto error; |
1611 | len += ret; | 1611 | len += ret; |
1612 | 1612 | ||
1613 | for (i = 0; i < tev->nargs; i++) { | 1613 | for (i = 0; i < tev->nargs; i++) { |
1614 | ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, | 1614 | ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, |
1615 | MAX_CMDLEN - len); | 1615 | MAX_CMDLEN - len); |
1616 | if (ret <= 0) | 1616 | if (ret <= 0) |
1617 | goto error; | 1617 | goto error; |
1618 | len += ret; | 1618 | len += ret; |
1619 | } | 1619 | } |
1620 | 1620 | ||
1621 | return buf; | 1621 | return buf; |
1622 | error: | 1622 | error: |
1623 | free(buf); | 1623 | free(buf); |
1624 | return NULL; | 1624 | return NULL; |
1625 | } | 1625 | } |
1626 | 1626 | ||
1627 | static int find_perf_probe_point_from_map(struct probe_trace_point *tp, | 1627 | static int find_perf_probe_point_from_map(struct probe_trace_point *tp, |
1628 | struct perf_probe_point *pp, | 1628 | struct perf_probe_point *pp, |
1629 | bool is_kprobe) | 1629 | bool is_kprobe) |
1630 | { | 1630 | { |
1631 | struct symbol *sym = NULL; | 1631 | struct symbol *sym = NULL; |
1632 | struct map *map; | 1632 | struct map *map; |
1633 | u64 addr; | 1633 | u64 addr; |
1634 | int ret = -ENOENT; | 1634 | int ret = -ENOENT; |
1635 | 1635 | ||
1636 | if (!is_kprobe) { | 1636 | if (!is_kprobe) { |
1637 | map = dso__new_map(tp->module); | 1637 | map = dso__new_map(tp->module); |
1638 | if (!map) | 1638 | if (!map) |
1639 | goto out; | 1639 | goto out; |
1640 | addr = tp->address; | 1640 | addr = tp->address; |
1641 | sym = map__find_symbol(map, addr, NULL); | 1641 | sym = map__find_symbol(map, addr, NULL); |
1642 | } else { | 1642 | } else { |
1643 | addr = kernel_get_symbol_address_by_name(tp->symbol, true); | 1643 | addr = kernel_get_symbol_address_by_name(tp->symbol, true); |
1644 | if (addr) { | 1644 | if (addr) { |
1645 | addr += tp->offset; | 1645 | addr += tp->offset; |
1646 | sym = __find_kernel_function(addr, &map); | 1646 | sym = __find_kernel_function(addr, &map); |
1647 | } | 1647 | } |
1648 | } | 1648 | } |
1649 | if (!sym) | 1649 | if (!sym) |
1650 | goto out; | 1650 | goto out; |
1651 | 1651 | ||
1652 | pp->retprobe = tp->retprobe; | 1652 | pp->retprobe = tp->retprobe; |
1653 | pp->offset = addr - map->unmap_ip(map, sym->start); | 1653 | pp->offset = addr - map->unmap_ip(map, sym->start); |
1654 | pp->function = strdup(sym->name); | 1654 | pp->function = strdup(sym->name); |
1655 | ret = pp->function ? 0 : -ENOMEM; | 1655 | ret = pp->function ? 0 : -ENOMEM; |
1656 | 1656 | ||
1657 | out: | 1657 | out: |
1658 | if (map && !is_kprobe) { | 1658 | if (map && !is_kprobe) { |
1659 | dso__delete(map->dso); | 1659 | dso__delete(map->dso); |
1660 | map__delete(map); | 1660 | map__delete(map); |
1661 | } | 1661 | } |
1662 | 1662 | ||
1663 | return ret; | 1663 | return ret; |
1664 | } | 1664 | } |
1665 | 1665 | ||
1666 | static int convert_to_perf_probe_point(struct probe_trace_point *tp, | 1666 | static int convert_to_perf_probe_point(struct probe_trace_point *tp, |
1667 | struct perf_probe_point *pp, | 1667 | struct perf_probe_point *pp, |
1668 | bool is_kprobe) | 1668 | bool is_kprobe) |
1669 | { | 1669 | { |
1670 | char buf[128]; | 1670 | char buf[128]; |
1671 | int ret; | 1671 | int ret; |
1672 | 1672 | ||
1673 | ret = find_perf_probe_point_from_dwarf(tp, pp, is_kprobe); | 1673 | ret = find_perf_probe_point_from_dwarf(tp, pp, is_kprobe); |
1674 | if (!ret) | 1674 | if (!ret) |
1675 | return 0; | 1675 | return 0; |
1676 | ret = find_perf_probe_point_from_map(tp, pp, is_kprobe); | 1676 | ret = find_perf_probe_point_from_map(tp, pp, is_kprobe); |
1677 | if (!ret) | 1677 | if (!ret) |
1678 | return 0; | 1678 | return 0; |
1679 | 1679 | ||
1680 | pr_debug("Failed to find probe point from both of dwarf and map.\n"); | 1680 | pr_debug("Failed to find probe point from both of dwarf and map.\n"); |
1681 | 1681 | ||
1682 | if (tp->symbol) { | 1682 | if (tp->symbol) { |
1683 | pp->function = strdup(tp->symbol); | 1683 | pp->function = strdup(tp->symbol); |
1684 | pp->offset = tp->offset; | 1684 | pp->offset = tp->offset; |
1685 | } else if (!tp->module && !is_kprobe) { | 1685 | } else if (!tp->module && !is_kprobe) { |
1686 | ret = e_snprintf(buf, 128, "0x%" PRIx64, (u64)tp->address); | 1686 | ret = e_snprintf(buf, 128, "0x%" PRIx64, (u64)tp->address); |
1687 | if (ret < 0) | 1687 | if (ret < 0) |
1688 | return ret; | 1688 | return ret; |
1689 | pp->function = strdup(buf); | 1689 | pp->function = strdup(buf); |
1690 | pp->offset = 0; | 1690 | pp->offset = 0; |
1691 | } | 1691 | } |
1692 | if (pp->function == NULL) | 1692 | if (pp->function == NULL) |
1693 | return -ENOMEM; | 1693 | return -ENOMEM; |
1694 | 1694 | ||
1695 | pp->retprobe = tp->retprobe; | 1695 | pp->retprobe = tp->retprobe; |
1696 | 1696 | ||
1697 | return 0; | 1697 | return 0; |
1698 | } | 1698 | } |
1699 | 1699 | ||
1700 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, | 1700 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, |
1701 | struct perf_probe_event *pev, bool is_kprobe) | 1701 | struct perf_probe_event *pev, bool is_kprobe) |
1702 | { | 1702 | { |
1703 | char buf[64] = ""; | 1703 | char buf[64] = ""; |
1704 | int i, ret; | 1704 | int i, ret; |
1705 | 1705 | ||
1706 | /* Convert event/group name */ | 1706 | /* Convert event/group name */ |
1707 | pev->event = strdup(tev->event); | 1707 | pev->event = strdup(tev->event); |
1708 | pev->group = strdup(tev->group); | 1708 | pev->group = strdup(tev->group); |
1709 | if (pev->event == NULL || pev->group == NULL) | 1709 | if (pev->event == NULL || pev->group == NULL) |
1710 | return -ENOMEM; | 1710 | return -ENOMEM; |
1711 | 1711 | ||
1712 | /* Convert trace_point to probe_point */ | 1712 | /* Convert trace_point to probe_point */ |
1713 | ret = convert_to_perf_probe_point(&tev->point, &pev->point, is_kprobe); | 1713 | ret = convert_to_perf_probe_point(&tev->point, &pev->point, is_kprobe); |
1714 | if (ret < 0) | 1714 | if (ret < 0) |
1715 | return ret; | 1715 | return ret; |
1716 | 1716 | ||
1717 | /* Convert trace_arg to probe_arg */ | 1717 | /* Convert trace_arg to probe_arg */ |
1718 | pev->nargs = tev->nargs; | 1718 | pev->nargs = tev->nargs; |
1719 | pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); | 1719 | pev->args = zalloc(sizeof(struct perf_probe_arg) * pev->nargs); |
1720 | if (pev->args == NULL) | 1720 | if (pev->args == NULL) |
1721 | return -ENOMEM; | 1721 | return -ENOMEM; |
1722 | for (i = 0; i < tev->nargs && ret >= 0; i++) { | 1722 | for (i = 0; i < tev->nargs && ret >= 0; i++) { |
1723 | if (tev->args[i].name) | 1723 | if (tev->args[i].name) |
1724 | pev->args[i].name = strdup(tev->args[i].name); | 1724 | pev->args[i].name = strdup(tev->args[i].name); |
1725 | else { | 1725 | else { |
1726 | ret = synthesize_probe_trace_arg(&tev->args[i], | 1726 | ret = synthesize_probe_trace_arg(&tev->args[i], |
1727 | buf, 64); | 1727 | buf, 64); |
1728 | pev->args[i].name = strdup(buf); | 1728 | pev->args[i].name = strdup(buf); |
1729 | } | 1729 | } |
1730 | if (pev->args[i].name == NULL && ret >= 0) | 1730 | if (pev->args[i].name == NULL && ret >= 0) |
1731 | ret = -ENOMEM; | 1731 | ret = -ENOMEM; |
1732 | } | 1732 | } |
1733 | 1733 | ||
1734 | if (ret < 0) | 1734 | if (ret < 0) |
1735 | clear_perf_probe_event(pev); | 1735 | clear_perf_probe_event(pev); |
1736 | 1736 | ||
1737 | return ret; | 1737 | return ret; |
1738 | } | 1738 | } |
1739 | 1739 | ||
1740 | void clear_perf_probe_event(struct perf_probe_event *pev) | 1740 | void clear_perf_probe_event(struct perf_probe_event *pev) |
1741 | { | 1741 | { |
1742 | struct perf_probe_point *pp = &pev->point; | 1742 | struct perf_probe_point *pp = &pev->point; |
1743 | struct perf_probe_arg_field *field, *next; | 1743 | struct perf_probe_arg_field *field, *next; |
1744 | int i; | 1744 | int i; |
1745 | 1745 | ||
1746 | free(pev->event); | 1746 | free(pev->event); |
1747 | free(pev->group); | 1747 | free(pev->group); |
1748 | free(pp->file); | 1748 | free(pp->file); |
1749 | free(pp->function); | 1749 | free(pp->function); |
1750 | free(pp->lazy_line); | 1750 | free(pp->lazy_line); |
1751 | 1751 | ||
1752 | for (i = 0; i < pev->nargs; i++) { | 1752 | for (i = 0; i < pev->nargs; i++) { |
1753 | free(pev->args[i].name); | 1753 | free(pev->args[i].name); |
1754 | free(pev->args[i].var); | 1754 | free(pev->args[i].var); |
1755 | free(pev->args[i].type); | 1755 | free(pev->args[i].type); |
1756 | field = pev->args[i].field; | 1756 | field = pev->args[i].field; |
1757 | while (field) { | 1757 | while (field) { |
1758 | next = field->next; | 1758 | next = field->next; |
1759 | zfree(&field->name); | 1759 | zfree(&field->name); |
1760 | free(field); | 1760 | free(field); |
1761 | field = next; | 1761 | field = next; |
1762 | } | 1762 | } |
1763 | } | 1763 | } |
1764 | free(pev->args); | 1764 | free(pev->args); |
1765 | memset(pev, 0, sizeof(*pev)); | 1765 | memset(pev, 0, sizeof(*pev)); |
1766 | } | 1766 | } |
1767 | 1767 | ||
1768 | static void clear_probe_trace_event(struct probe_trace_event *tev) | 1768 | static void clear_probe_trace_event(struct probe_trace_event *tev) |
1769 | { | 1769 | { |
1770 | struct probe_trace_arg_ref *ref, *next; | 1770 | struct probe_trace_arg_ref *ref, *next; |
1771 | int i; | 1771 | int i; |
1772 | 1772 | ||
1773 | free(tev->event); | 1773 | free(tev->event); |
1774 | free(tev->group); | 1774 | free(tev->group); |
1775 | free(tev->point.symbol); | 1775 | free(tev->point.symbol); |
1776 | free(tev->point.module); | 1776 | free(tev->point.module); |
1777 | for (i = 0; i < tev->nargs; i++) { | 1777 | for (i = 0; i < tev->nargs; i++) { |
1778 | free(tev->args[i].name); | 1778 | free(tev->args[i].name); |
1779 | free(tev->args[i].value); | 1779 | free(tev->args[i].value); |
1780 | free(tev->args[i].type); | 1780 | free(tev->args[i].type); |
1781 | ref = tev->args[i].ref; | 1781 | ref = tev->args[i].ref; |
1782 | while (ref) { | 1782 | while (ref) { |
1783 | next = ref->next; | 1783 | next = ref->next; |
1784 | free(ref); | 1784 | free(ref); |
1785 | ref = next; | 1785 | ref = next; |
1786 | } | 1786 | } |
1787 | } | 1787 | } |
1788 | free(tev->args); | 1788 | free(tev->args); |
1789 | memset(tev, 0, sizeof(*tev)); | 1789 | memset(tev, 0, sizeof(*tev)); |
1790 | } | 1790 | } |
1791 | 1791 | ||
1792 | static void print_open_warning(int err, bool is_kprobe) | 1792 | static void print_open_warning(int err, bool is_kprobe) |
1793 | { | 1793 | { |
1794 | char sbuf[STRERR_BUFSIZE]; | 1794 | char sbuf[STRERR_BUFSIZE]; |
1795 | 1795 | ||
1796 | if (err == -ENOENT) { | 1796 | if (err == -ENOENT) { |
1797 | const char *config; | 1797 | const char *config; |
1798 | 1798 | ||
1799 | if (!is_kprobe) | 1799 | if (!is_kprobe) |
1800 | config = "CONFIG_UPROBE_EVENTS"; | 1800 | config = "CONFIG_UPROBE_EVENTS"; |
1801 | else | 1801 | else |
1802 | config = "CONFIG_KPROBE_EVENTS"; | 1802 | config = "CONFIG_KPROBE_EVENTS"; |
1803 | 1803 | ||
1804 | pr_warning("%cprobe_events file does not exist" | 1804 | pr_warning("%cprobe_events file does not exist" |
1805 | " - please rebuild kernel with %s.\n", | 1805 | " - please rebuild kernel with %s.\n", |
1806 | is_kprobe ? 'k' : 'u', config); | 1806 | is_kprobe ? 'k' : 'u', config); |
1807 | } else if (err == -ENOTSUP) | 1807 | } else if (err == -ENOTSUP) |
1808 | pr_warning("Debugfs is not mounted.\n"); | 1808 | pr_warning("Debugfs is not mounted.\n"); |
1809 | else | 1809 | else |
1810 | pr_warning("Failed to open %cprobe_events: %s\n", | 1810 | pr_warning("Failed to open %cprobe_events: %s\n", |
1811 | is_kprobe ? 'k' : 'u', | 1811 | is_kprobe ? 'k' : 'u', |
1812 | strerror_r(-err, sbuf, sizeof(sbuf))); | 1812 | strerror_r(-err, sbuf, sizeof(sbuf))); |
1813 | } | 1813 | } |
1814 | 1814 | ||
1815 | static void print_both_open_warning(int kerr, int uerr) | 1815 | static void print_both_open_warning(int kerr, int uerr) |
1816 | { | 1816 | { |
1817 | /* Both kprobes and uprobes are disabled, warn it. */ | 1817 | /* Both kprobes and uprobes are disabled, warn it. */ |
1818 | if (kerr == -ENOTSUP && uerr == -ENOTSUP) | 1818 | if (kerr == -ENOTSUP && uerr == -ENOTSUP) |
1819 | pr_warning("Debugfs is not mounted.\n"); | 1819 | pr_warning("Debugfs is not mounted.\n"); |
1820 | else if (kerr == -ENOENT && uerr == -ENOENT) | 1820 | else if (kerr == -ENOENT && uerr == -ENOENT) |
1821 | pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS " | 1821 | pr_warning("Please rebuild kernel with CONFIG_KPROBE_EVENTS " |
1822 | "or/and CONFIG_UPROBE_EVENTS.\n"); | 1822 | "or/and CONFIG_UPROBE_EVENTS.\n"); |
1823 | else { | 1823 | else { |
1824 | char sbuf[STRERR_BUFSIZE]; | 1824 | char sbuf[STRERR_BUFSIZE]; |
1825 | pr_warning("Failed to open kprobe events: %s.\n", | 1825 | pr_warning("Failed to open kprobe events: %s.\n", |
1826 | strerror_r(-kerr, sbuf, sizeof(sbuf))); | 1826 | strerror_r(-kerr, sbuf, sizeof(sbuf))); |
1827 | pr_warning("Failed to open uprobe events: %s.\n", | 1827 | pr_warning("Failed to open uprobe events: %s.\n", |
1828 | strerror_r(-uerr, sbuf, sizeof(sbuf))); | 1828 | strerror_r(-uerr, sbuf, sizeof(sbuf))); |
1829 | } | 1829 | } |
1830 | } | 1830 | } |
1831 | 1831 | ||
1832 | static int open_probe_events(const char *trace_file, bool readwrite) | 1832 | static int open_probe_events(const char *trace_file, bool readwrite) |
1833 | { | 1833 | { |
1834 | char buf[PATH_MAX]; | 1834 | char buf[PATH_MAX]; |
1835 | const char *__debugfs; | 1835 | const char *__debugfs; |
1836 | int ret; | 1836 | int ret; |
1837 | 1837 | ||
1838 | __debugfs = debugfs_find_mountpoint(); | 1838 | __debugfs = debugfs_find_mountpoint(); |
1839 | if (__debugfs == NULL) | 1839 | if (__debugfs == NULL) |
1840 | return -ENOTSUP; | 1840 | return -ENOTSUP; |
1841 | 1841 | ||
1842 | ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file); | 1842 | ret = e_snprintf(buf, PATH_MAX, "%s/%s", __debugfs, trace_file); |
1843 | if (ret >= 0) { | 1843 | if (ret >= 0) { |
1844 | pr_debug("Opening %s write=%d\n", buf, readwrite); | 1844 | pr_debug("Opening %s write=%d\n", buf, readwrite); |
1845 | if (readwrite && !probe_event_dry_run) | 1845 | if (readwrite && !probe_event_dry_run) |
1846 | ret = open(buf, O_RDWR, O_APPEND); | 1846 | ret = open(buf, O_RDWR, O_APPEND); |
1847 | else | 1847 | else |
1848 | ret = open(buf, O_RDONLY, 0); | 1848 | ret = open(buf, O_RDONLY, 0); |
1849 | 1849 | ||
1850 | if (ret < 0) | 1850 | if (ret < 0) |
1851 | ret = -errno; | 1851 | ret = -errno; |
1852 | } | 1852 | } |
1853 | return ret; | 1853 | return ret; |
1854 | } | 1854 | } |
1855 | 1855 | ||
1856 | static int open_kprobe_events(bool readwrite) | 1856 | static int open_kprobe_events(bool readwrite) |
1857 | { | 1857 | { |
1858 | return open_probe_events("tracing/kprobe_events", readwrite); | 1858 | return open_probe_events("tracing/kprobe_events", readwrite); |
1859 | } | 1859 | } |
1860 | 1860 | ||
1861 | static int open_uprobe_events(bool readwrite) | 1861 | static int open_uprobe_events(bool readwrite) |
1862 | { | 1862 | { |
1863 | return open_probe_events("tracing/uprobe_events", readwrite); | 1863 | return open_probe_events("tracing/uprobe_events", readwrite); |
1864 | } | 1864 | } |
1865 | 1865 | ||
1866 | /* Get raw string list of current kprobe_events or uprobe_events */ | 1866 | /* Get raw string list of current kprobe_events or uprobe_events */ |
1867 | static struct strlist *get_probe_trace_command_rawlist(int fd) | 1867 | static struct strlist *get_probe_trace_command_rawlist(int fd) |
1868 | { | 1868 | { |
1869 | int ret, idx; | 1869 | int ret, idx; |
1870 | FILE *fp; | 1870 | FILE *fp; |
1871 | char buf[MAX_CMDLEN]; | 1871 | char buf[MAX_CMDLEN]; |
1872 | char *p; | 1872 | char *p; |
1873 | struct strlist *sl; | 1873 | struct strlist *sl; |
1874 | 1874 | ||
1875 | sl = strlist__new(true, NULL); | 1875 | sl = strlist__new(true, NULL); |
1876 | 1876 | ||
1877 | fp = fdopen(dup(fd), "r"); | 1877 | fp = fdopen(dup(fd), "r"); |
1878 | while (!feof(fp)) { | 1878 | while (!feof(fp)) { |
1879 | p = fgets(buf, MAX_CMDLEN, fp); | 1879 | p = fgets(buf, MAX_CMDLEN, fp); |
1880 | if (!p) | 1880 | if (!p) |
1881 | break; | 1881 | break; |
1882 | 1882 | ||
1883 | idx = strlen(p) - 1; | 1883 | idx = strlen(p) - 1; |
1884 | if (p[idx] == '\n') | 1884 | if (p[idx] == '\n') |
1885 | p[idx] = '\0'; | 1885 | p[idx] = '\0'; |
1886 | ret = strlist__add(sl, buf); | 1886 | ret = strlist__add(sl, buf); |
1887 | if (ret < 0) { | 1887 | if (ret < 0) { |
1888 | pr_debug("strlist__add failed (%d)\n", ret); | 1888 | pr_debug("strlist__add failed (%d)\n", ret); |
1889 | strlist__delete(sl); | 1889 | strlist__delete(sl); |
1890 | return NULL; | 1890 | return NULL; |
1891 | } | 1891 | } |
1892 | } | 1892 | } |
1893 | fclose(fp); | 1893 | fclose(fp); |
1894 | 1894 | ||
1895 | return sl; | 1895 | return sl; |
1896 | } | 1896 | } |
1897 | 1897 | ||
1898 | /* Show an event */ | 1898 | /* Show an event */ |
1899 | static int show_perf_probe_event(struct perf_probe_event *pev, | 1899 | static int show_perf_probe_event(struct perf_probe_event *pev, |
1900 | const char *module) | 1900 | const char *module) |
1901 | { | 1901 | { |
1902 | int i, ret; | 1902 | int i, ret; |
1903 | char buf[128]; | 1903 | char buf[128]; |
1904 | char *place; | 1904 | char *place; |
1905 | 1905 | ||
1906 | /* Synthesize only event probe point */ | 1906 | /* Synthesize only event probe point */ |
1907 | place = synthesize_perf_probe_point(&pev->point); | 1907 | place = synthesize_perf_probe_point(&pev->point); |
1908 | if (!place) | 1908 | if (!place) |
1909 | return -EINVAL; | 1909 | return -EINVAL; |
1910 | 1910 | ||
1911 | ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); | 1911 | ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); |
1912 | if (ret < 0) | 1912 | if (ret < 0) |
1913 | return ret; | 1913 | return ret; |
1914 | 1914 | ||
1915 | pr_info(" %-20s (on %s", buf, place); | 1915 | pr_info(" %-20s (on %s", buf, place); |
1916 | if (module) | 1916 | if (module) |
1917 | pr_info(" in %s", module); | 1917 | pr_info(" in %s", module); |
1918 | 1918 | ||
1919 | if (pev->nargs > 0) { | 1919 | if (pev->nargs > 0) { |
1920 | pr_info(" with"); | 1920 | pr_info(" with"); |
1921 | for (i = 0; i < pev->nargs; i++) { | 1921 | for (i = 0; i < pev->nargs; i++) { |
1922 | ret = synthesize_perf_probe_arg(&pev->args[i], | 1922 | ret = synthesize_perf_probe_arg(&pev->args[i], |
1923 | buf, 128); | 1923 | buf, 128); |
1924 | if (ret < 0) | 1924 | if (ret < 0) |
1925 | break; | 1925 | break; |
1926 | pr_info(" %s", buf); | 1926 | pr_info(" %s", buf); |
1927 | } | 1927 | } |
1928 | } | 1928 | } |
1929 | pr_info(")\n"); | 1929 | pr_info(")\n"); |
1930 | free(place); | 1930 | free(place); |
1931 | return ret; | 1931 | return ret; |
1932 | } | 1932 | } |
1933 | 1933 | ||
1934 | static int __show_perf_probe_events(int fd, bool is_kprobe) | 1934 | static int __show_perf_probe_events(int fd, bool is_kprobe) |
1935 | { | 1935 | { |
1936 | int ret = 0; | 1936 | int ret = 0; |
1937 | struct probe_trace_event tev; | 1937 | struct probe_trace_event tev; |
1938 | struct perf_probe_event pev; | 1938 | struct perf_probe_event pev; |
1939 | struct strlist *rawlist; | 1939 | struct strlist *rawlist; |
1940 | struct str_node *ent; | 1940 | struct str_node *ent; |
1941 | 1941 | ||
1942 | memset(&tev, 0, sizeof(tev)); | 1942 | memset(&tev, 0, sizeof(tev)); |
1943 | memset(&pev, 0, sizeof(pev)); | 1943 | memset(&pev, 0, sizeof(pev)); |
1944 | 1944 | ||
1945 | rawlist = get_probe_trace_command_rawlist(fd); | 1945 | rawlist = get_probe_trace_command_rawlist(fd); |
1946 | if (!rawlist) | 1946 | if (!rawlist) |
1947 | return -ENOMEM; | 1947 | return -ENOMEM; |
1948 | 1948 | ||
1949 | strlist__for_each(ent, rawlist) { | 1949 | strlist__for_each(ent, rawlist) { |
1950 | ret = parse_probe_trace_command(ent->s, &tev); | 1950 | ret = parse_probe_trace_command(ent->s, &tev); |
1951 | if (ret >= 0) { | 1951 | if (ret >= 0) { |
1952 | ret = convert_to_perf_probe_event(&tev, &pev, | 1952 | ret = convert_to_perf_probe_event(&tev, &pev, |
1953 | is_kprobe); | 1953 | is_kprobe); |
1954 | if (ret >= 0) | 1954 | if (ret >= 0) |
1955 | ret = show_perf_probe_event(&pev, | 1955 | ret = show_perf_probe_event(&pev, |
1956 | tev.point.module); | 1956 | tev.point.module); |
1957 | } | 1957 | } |
1958 | clear_perf_probe_event(&pev); | 1958 | clear_perf_probe_event(&pev); |
1959 | clear_probe_trace_event(&tev); | 1959 | clear_probe_trace_event(&tev); |
1960 | if (ret < 0) | 1960 | if (ret < 0) |
1961 | break; | 1961 | break; |
1962 | } | 1962 | } |
1963 | strlist__delete(rawlist); | 1963 | strlist__delete(rawlist); |
1964 | 1964 | ||
1965 | return ret; | 1965 | return ret; |
1966 | } | 1966 | } |
1967 | 1967 | ||
1968 | /* List up current perf-probe events */ | 1968 | /* List up current perf-probe events */ |
1969 | int show_perf_probe_events(void) | 1969 | int show_perf_probe_events(void) |
1970 | { | 1970 | { |
1971 | int kp_fd, up_fd, ret; | 1971 | int kp_fd, up_fd, ret; |
1972 | 1972 | ||
1973 | setup_pager(); | 1973 | setup_pager(); |
1974 | 1974 | ||
1975 | ret = init_symbol_maps(false); | 1975 | ret = init_symbol_maps(false); |
1976 | if (ret < 0) | 1976 | if (ret < 0) |
1977 | return ret; | 1977 | return ret; |
1978 | 1978 | ||
1979 | kp_fd = open_kprobe_events(false); | 1979 | kp_fd = open_kprobe_events(false); |
1980 | if (kp_fd >= 0) { | 1980 | if (kp_fd >= 0) { |
1981 | ret = __show_perf_probe_events(kp_fd, true); | 1981 | ret = __show_perf_probe_events(kp_fd, true); |
1982 | close(kp_fd); | 1982 | close(kp_fd); |
1983 | if (ret < 0) | 1983 | if (ret < 0) |
1984 | goto out; | 1984 | goto out; |
1985 | } | 1985 | } |
1986 | 1986 | ||
1987 | up_fd = open_uprobe_events(false); | 1987 | up_fd = open_uprobe_events(false); |
1988 | if (kp_fd < 0 && up_fd < 0) { | 1988 | if (kp_fd < 0 && up_fd < 0) { |
1989 | print_both_open_warning(kp_fd, up_fd); | 1989 | print_both_open_warning(kp_fd, up_fd); |
1990 | ret = kp_fd; | 1990 | ret = kp_fd; |
1991 | goto out; | 1991 | goto out; |
1992 | } | 1992 | } |
1993 | 1993 | ||
1994 | if (up_fd >= 0) { | 1994 | if (up_fd >= 0) { |
1995 | ret = __show_perf_probe_events(up_fd, false); | 1995 | ret = __show_perf_probe_events(up_fd, false); |
1996 | close(up_fd); | 1996 | close(up_fd); |
1997 | } | 1997 | } |
1998 | out: | 1998 | out: |
1999 | exit_symbol_maps(); | 1999 | exit_symbol_maps(); |
2000 | return ret; | 2000 | return ret; |
2001 | } | 2001 | } |
2002 | 2002 | ||
2003 | /* Get current perf-probe event names */ | 2003 | /* Get current perf-probe event names */ |
2004 | static struct strlist *get_probe_trace_event_names(int fd, bool include_group) | 2004 | static struct strlist *get_probe_trace_event_names(int fd, bool include_group) |
2005 | { | 2005 | { |
2006 | char buf[128]; | 2006 | char buf[128]; |
2007 | struct strlist *sl, *rawlist; | 2007 | struct strlist *sl, *rawlist; |
2008 | struct str_node *ent; | 2008 | struct str_node *ent; |
2009 | struct probe_trace_event tev; | 2009 | struct probe_trace_event tev; |
2010 | int ret = 0; | 2010 | int ret = 0; |
2011 | 2011 | ||
2012 | memset(&tev, 0, sizeof(tev)); | 2012 | memset(&tev, 0, sizeof(tev)); |
2013 | rawlist = get_probe_trace_command_rawlist(fd); | 2013 | rawlist = get_probe_trace_command_rawlist(fd); |
2014 | if (!rawlist) | 2014 | if (!rawlist) |
2015 | return NULL; | 2015 | return NULL; |
2016 | sl = strlist__new(true, NULL); | 2016 | sl = strlist__new(true, NULL); |
2017 | strlist__for_each(ent, rawlist) { | 2017 | strlist__for_each(ent, rawlist) { |
2018 | ret = parse_probe_trace_command(ent->s, &tev); | 2018 | ret = parse_probe_trace_command(ent->s, &tev); |
2019 | if (ret < 0) | 2019 | if (ret < 0) |
2020 | break; | 2020 | break; |
2021 | if (include_group) { | 2021 | if (include_group) { |
2022 | ret = e_snprintf(buf, 128, "%s:%s", tev.group, | 2022 | ret = e_snprintf(buf, 128, "%s:%s", tev.group, |
2023 | tev.event); | 2023 | tev.event); |
2024 | if (ret >= 0) | 2024 | if (ret >= 0) |
2025 | ret = strlist__add(sl, buf); | 2025 | ret = strlist__add(sl, buf); |
2026 | } else | 2026 | } else |
2027 | ret = strlist__add(sl, tev.event); | 2027 | ret = strlist__add(sl, tev.event); |
2028 | clear_probe_trace_event(&tev); | 2028 | clear_probe_trace_event(&tev); |
2029 | if (ret < 0) | 2029 | if (ret < 0) |
2030 | break; | 2030 | break; |
2031 | } | 2031 | } |
2032 | strlist__delete(rawlist); | 2032 | strlist__delete(rawlist); |
2033 | 2033 | ||
2034 | if (ret < 0) { | 2034 | if (ret < 0) { |
2035 | strlist__delete(sl); | 2035 | strlist__delete(sl); |
2036 | return NULL; | 2036 | return NULL; |
2037 | } | 2037 | } |
2038 | return sl; | 2038 | return sl; |
2039 | } | 2039 | } |
2040 | 2040 | ||
2041 | static int write_probe_trace_event(int fd, struct probe_trace_event *tev) | 2041 | static int write_probe_trace_event(int fd, struct probe_trace_event *tev) |
2042 | { | 2042 | { |
2043 | int ret = 0; | 2043 | int ret = 0; |
2044 | char *buf = synthesize_probe_trace_command(tev); | 2044 | char *buf = synthesize_probe_trace_command(tev); |
2045 | char sbuf[STRERR_BUFSIZE]; | 2045 | char sbuf[STRERR_BUFSIZE]; |
2046 | 2046 | ||
2047 | if (!buf) { | 2047 | if (!buf) { |
2048 | pr_debug("Failed to synthesize probe trace event.\n"); | 2048 | pr_debug("Failed to synthesize probe trace event.\n"); |
2049 | return -EINVAL; | 2049 | return -EINVAL; |
2050 | } | 2050 | } |
2051 | 2051 | ||
2052 | pr_debug("Writing event: %s\n", buf); | 2052 | pr_debug("Writing event: %s\n", buf); |
2053 | if (!probe_event_dry_run) { | 2053 | if (!probe_event_dry_run) { |
2054 | ret = write(fd, buf, strlen(buf)); | 2054 | ret = write(fd, buf, strlen(buf)); |
2055 | if (ret <= 0) { | 2055 | if (ret <= 0) { |
2056 | ret = -errno; | 2056 | ret = -errno; |
2057 | pr_warning("Failed to write event: %s\n", | 2057 | pr_warning("Failed to write event: %s\n", |
2058 | strerror_r(errno, sbuf, sizeof(sbuf))); | 2058 | strerror_r(errno, sbuf, sizeof(sbuf))); |
2059 | } | 2059 | } |
2060 | } | 2060 | } |
2061 | free(buf); | 2061 | free(buf); |
2062 | return ret; | 2062 | return ret; |
2063 | } | 2063 | } |
2064 | 2064 | ||
2065 | static int get_new_event_name(char *buf, size_t len, const char *base, | 2065 | static int get_new_event_name(char *buf, size_t len, const char *base, |
2066 | struct strlist *namelist, bool allow_suffix) | 2066 | struct strlist *namelist, bool allow_suffix) |
2067 | { | 2067 | { |
2068 | int i, ret; | 2068 | int i, ret; |
2069 | 2069 | ||
2070 | /* Try no suffix */ | 2070 | /* Try no suffix */ |
2071 | ret = e_snprintf(buf, len, "%s", base); | 2071 | ret = e_snprintf(buf, len, "%s", base); |
2072 | if (ret < 0) { | 2072 | if (ret < 0) { |
2073 | pr_debug("snprintf() failed: %d\n", ret); | 2073 | pr_debug("snprintf() failed: %d\n", ret); |
2074 | return ret; | 2074 | return ret; |
2075 | } | 2075 | } |
2076 | if (!strlist__has_entry(namelist, buf)) | 2076 | if (!strlist__has_entry(namelist, buf)) |
2077 | return 0; | 2077 | return 0; |
2078 | 2078 | ||
2079 | if (!allow_suffix) { | 2079 | if (!allow_suffix) { |
2080 | pr_warning("Error: event \"%s\" already exists. " | 2080 | pr_warning("Error: event \"%s\" already exists. " |
2081 | "(Use -f to force duplicates.)\n", base); | 2081 | "(Use -f to force duplicates.)\n", base); |
2082 | return -EEXIST; | 2082 | return -EEXIST; |
2083 | } | 2083 | } |
2084 | 2084 | ||
2085 | /* Try to add suffix */ | 2085 | /* Try to add suffix */ |
2086 | for (i = 1; i < MAX_EVENT_INDEX; i++) { | 2086 | for (i = 1; i < MAX_EVENT_INDEX; i++) { |
2087 | ret = e_snprintf(buf, len, "%s_%d", base, i); | 2087 | ret = e_snprintf(buf, len, "%s_%d", base, i); |
2088 | if (ret < 0) { | 2088 | if (ret < 0) { |
2089 | pr_debug("snprintf() failed: %d\n", ret); | 2089 | pr_debug("snprintf() failed: %d\n", ret); |
2090 | return ret; | 2090 | return ret; |
2091 | } | 2091 | } |
2092 | if (!strlist__has_entry(namelist, buf)) | 2092 | if (!strlist__has_entry(namelist, buf)) |
2093 | break; | 2093 | break; |
2094 | } | 2094 | } |
2095 | if (i == MAX_EVENT_INDEX) { | 2095 | if (i == MAX_EVENT_INDEX) { |
2096 | pr_warning("Too many events are on the same function.\n"); | 2096 | pr_warning("Too many events are on the same function.\n"); |
2097 | ret = -ERANGE; | 2097 | ret = -ERANGE; |
2098 | } | 2098 | } |
2099 | 2099 | ||
2100 | return ret; | 2100 | return ret; |
2101 | } | 2101 | } |
2102 | 2102 | ||
2103 | static int __add_probe_trace_events(struct perf_probe_event *pev, | 2103 | static int __add_probe_trace_events(struct perf_probe_event *pev, |
2104 | struct probe_trace_event *tevs, | 2104 | struct probe_trace_event *tevs, |
2105 | int ntevs, bool allow_suffix) | 2105 | int ntevs, bool allow_suffix) |
2106 | { | 2106 | { |
2107 | int i, fd, ret; | 2107 | int i, fd, ret; |
2108 | struct probe_trace_event *tev = NULL; | 2108 | struct probe_trace_event *tev = NULL; |
2109 | char buf[64]; | 2109 | char buf[64]; |
2110 | const char *event, *group; | 2110 | const char *event, *group; |
2111 | struct strlist *namelist; | 2111 | struct strlist *namelist; |
2112 | 2112 | ||
2113 | if (pev->uprobes) | 2113 | if (pev->uprobes) |
2114 | fd = open_uprobe_events(true); | 2114 | fd = open_uprobe_events(true); |
2115 | else | 2115 | else |
2116 | fd = open_kprobe_events(true); | 2116 | fd = open_kprobe_events(true); |
2117 | 2117 | ||
2118 | if (fd < 0) { | 2118 | if (fd < 0) { |
2119 | print_open_warning(fd, !pev->uprobes); | 2119 | print_open_warning(fd, !pev->uprobes); |
2120 | return fd; | 2120 | return fd; |
2121 | } | 2121 | } |
2122 | 2122 | ||
2123 | /* Get current event names */ | 2123 | /* Get current event names */ |
2124 | namelist = get_probe_trace_event_names(fd, false); | 2124 | namelist = get_probe_trace_event_names(fd, false); |
2125 | if (!namelist) { | 2125 | if (!namelist) { |
2126 | pr_debug("Failed to get current event list.\n"); | 2126 | pr_debug("Failed to get current event list.\n"); |
2127 | return -EIO; | 2127 | return -EIO; |
2128 | } | 2128 | } |
2129 | 2129 | ||
2130 | ret = 0; | 2130 | ret = 0; |
2131 | pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); | 2131 | pr_info("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); |
2132 | for (i = 0; i < ntevs; i++) { | 2132 | for (i = 0; i < ntevs; i++) { |
2133 | tev = &tevs[i]; | 2133 | tev = &tevs[i]; |
2134 | if (pev->event) | 2134 | if (pev->event) |
2135 | event = pev->event; | 2135 | event = pev->event; |
2136 | else | 2136 | else |
2137 | if (pev->point.function) | 2137 | if (pev->point.function) |
2138 | event = pev->point.function; | 2138 | event = pev->point.function; |
2139 | else | 2139 | else |
2140 | event = tev->point.symbol; | 2140 | event = tev->point.symbol; |
2141 | if (pev->group) | 2141 | if (pev->group) |
2142 | group = pev->group; | 2142 | group = pev->group; |
2143 | else | 2143 | else |
2144 | group = PERFPROBE_GROUP; | 2144 | group = PERFPROBE_GROUP; |
2145 | 2145 | ||
2146 | /* Get an unused new event name */ | 2146 | /* Get an unused new event name */ |
2147 | ret = get_new_event_name(buf, 64, event, | 2147 | ret = get_new_event_name(buf, 64, event, |
2148 | namelist, allow_suffix); | 2148 | namelist, allow_suffix); |
2149 | if (ret < 0) | 2149 | if (ret < 0) |
2150 | break; | 2150 | break; |
2151 | event = buf; | 2151 | event = buf; |
2152 | 2152 | ||
2153 | tev->event = strdup(event); | 2153 | tev->event = strdup(event); |
2154 | tev->group = strdup(group); | 2154 | tev->group = strdup(group); |
2155 | if (tev->event == NULL || tev->group == NULL) { | 2155 | if (tev->event == NULL || tev->group == NULL) { |
2156 | ret = -ENOMEM; | 2156 | ret = -ENOMEM; |
2157 | break; | 2157 | break; |
2158 | } | 2158 | } |
2159 | ret = write_probe_trace_event(fd, tev); | 2159 | ret = write_probe_trace_event(fd, tev); |
2160 | if (ret < 0) | 2160 | if (ret < 0) |
2161 | break; | 2161 | break; |
2162 | /* Add added event name to namelist */ | 2162 | /* Add added event name to namelist */ |
2163 | strlist__add(namelist, event); | 2163 | strlist__add(namelist, event); |
2164 | 2164 | ||
2165 | /* Trick here - save current event/group */ | 2165 | /* Trick here - save current event/group */ |
2166 | event = pev->event; | 2166 | event = pev->event; |
2167 | group = pev->group; | 2167 | group = pev->group; |
2168 | pev->event = tev->event; | 2168 | pev->event = tev->event; |
2169 | pev->group = tev->group; | 2169 | pev->group = tev->group; |
2170 | show_perf_probe_event(pev, tev->point.module); | 2170 | show_perf_probe_event(pev, tev->point.module); |
2171 | /* Trick here - restore current event/group */ | 2171 | /* Trick here - restore current event/group */ |
2172 | pev->event = (char *)event; | 2172 | pev->event = (char *)event; |
2173 | pev->group = (char *)group; | 2173 | pev->group = (char *)group; |
2174 | 2174 | ||
2175 | /* | 2175 | /* |
2176 | * Probes after the first probe which comes from same | 2176 | * Probes after the first probe which comes from same |
2177 | * user input are always allowed to add suffix, because | 2177 | * user input are always allowed to add suffix, because |
2178 | * there might be several addresses corresponding to | 2178 | * there might be several addresses corresponding to |
2179 | * one code line. | 2179 | * one code line. |
2180 | */ | 2180 | */ |
2181 | allow_suffix = true; | 2181 | allow_suffix = true; |
2182 | } | 2182 | } |
2183 | 2183 | ||
2184 | if (ret >= 0) { | 2184 | if (ret >= 0) { |
2185 | /* Show how to use the event. */ | 2185 | /* Show how to use the event. */ |
2186 | pr_info("\nYou can now use it in all perf tools, such as:\n\n"); | 2186 | pr_info("\nYou can now use it in all perf tools, such as:\n\n"); |
2187 | pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, | 2187 | pr_info("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, |
2188 | tev->event); | 2188 | tev->event); |
2189 | } | 2189 | } |
2190 | 2190 | ||
2191 | strlist__delete(namelist); | 2191 | strlist__delete(namelist); |
2192 | close(fd); | 2192 | close(fd); |
2193 | return ret; | 2193 | return ret; |
2194 | } | 2194 | } |
2195 | 2195 | ||
2196 | static char *looking_function_name; | 2196 | static int find_probe_functions(struct map *map, char *name) |
2197 | static int num_matched_functions; | ||
2198 | |||
2199 | static int probe_function_filter(struct map *map __maybe_unused, | ||
2200 | struct symbol *sym) | ||
2201 | { | 2197 | { |
2202 | if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) && | 2198 | int found = 0; |
2203 | strcmp(looking_function_name, sym->name) == 0) { | 2199 | struct symbol *sym; |
2204 | num_matched_functions++; | 2200 | |
2205 | return 0; | 2201 | map__for_each_symbol_by_name(map, name, sym) { |
2202 | if (sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) | ||
2203 | found++; | ||
2206 | } | 2204 | } |
2207 | return 1; | 2205 | |
2206 | return found; | ||
2208 | } | 2207 | } |
2209 | 2208 | ||
2210 | #define strdup_or_goto(str, label) \ | 2209 | #define strdup_or_goto(str, label) \ |
2211 | ({ char *__p = strdup(str); if (!__p) goto label; __p; }) | 2210 | ({ char *__p = strdup(str); if (!__p) goto label; __p; }) |
2212 | 2211 | ||
2213 | /* | 2212 | /* |
2214 | * Find probe function addresses from map. | 2213 | * Find probe function addresses from map. |
2215 | * Return an error or the number of found probe_trace_event | 2214 | * Return an error or the number of found probe_trace_event |
2216 | */ | 2215 | */ |
2217 | static int find_probe_trace_events_from_map(struct perf_probe_event *pev, | 2216 | static int find_probe_trace_events_from_map(struct perf_probe_event *pev, |
2218 | struct probe_trace_event **tevs, | 2217 | struct probe_trace_event **tevs, |
2219 | int max_tevs, const char *target) | 2218 | int max_tevs, const char *target) |
2220 | { | 2219 | { |
2221 | struct map *map = NULL; | 2220 | struct map *map = NULL; |
2222 | struct kmap *kmap = NULL; | 2221 | struct kmap *kmap = NULL; |
2223 | struct ref_reloc_sym *reloc_sym = NULL; | 2222 | struct ref_reloc_sym *reloc_sym = NULL; |
2224 | struct symbol *sym; | 2223 | struct symbol *sym; |
2225 | struct rb_node *nd; | ||
2226 | struct probe_trace_event *tev; | 2224 | struct probe_trace_event *tev; |
2227 | struct perf_probe_point *pp = &pev->point; | 2225 | struct perf_probe_point *pp = &pev->point; |
2228 | struct probe_trace_point *tp; | 2226 | struct probe_trace_point *tp; |
2227 | int num_matched_functions; | ||
2229 | int ret, i; | 2228 | int ret, i; |
2230 | 2229 | ||
2231 | /* Init maps of given executable or kernel */ | 2230 | /* Init maps of given executable or kernel */ |
2232 | if (pev->uprobes) | 2231 | if (pev->uprobes) |
2233 | map = dso__new_map(target); | 2232 | map = dso__new_map(target); |
2234 | else | 2233 | else |
2235 | map = kernel_get_module_map(target); | 2234 | map = kernel_get_module_map(target); |
2236 | if (!map) { | 2235 | if (!map) { |
2237 | ret = -EINVAL; | 2236 | ret = -EINVAL; |
2238 | goto out; | 2237 | goto out; |
2239 | } | 2238 | } |
2240 | 2239 | ||
2241 | /* | 2240 | /* |
2242 | * Load matched symbols: Since the different local symbols may have | 2241 | * Load matched symbols: Since the different local symbols may have |
2243 | * same name but different addresses, this lists all the symbols. | 2242 | * same name but different addresses, this lists all the symbols. |
2244 | */ | 2243 | */ |
2245 | num_matched_functions = 0; | 2244 | num_matched_functions = find_probe_functions(map, pp->function); |
2246 | looking_function_name = pp->function; | 2245 | if (num_matched_functions == 0) { |
2247 | ret = map__load(map, probe_function_filter); | ||
2248 | if (ret || num_matched_functions == 0) { | ||
2249 | pr_err("Failed to find symbol %s in %s\n", pp->function, | 2246 | pr_err("Failed to find symbol %s in %s\n", pp->function, |
2250 | target ? : "kernel"); | 2247 | target ? : "kernel"); |
2251 | ret = -ENOENT; | 2248 | ret = -ENOENT; |
2252 | goto out; | 2249 | goto out; |
2253 | } else if (num_matched_functions > max_tevs) { | 2250 | } else if (num_matched_functions > max_tevs) { |
2254 | pr_err("Too many functions matched in %s\n", | 2251 | pr_err("Too many functions matched in %s\n", |
2255 | target ? : "kernel"); | 2252 | target ? : "kernel"); |
2256 | ret = -E2BIG; | 2253 | ret = -E2BIG; |
2257 | goto out; | 2254 | goto out; |
2258 | } | 2255 | } |
2259 | 2256 | ||
2260 | if (!pev->uprobes) { | 2257 | if (!pev->uprobes && !pp->retprobe) { |
2261 | kmap = map__kmap(map); | 2258 | kmap = map__kmap(map); |
2262 | reloc_sym = kmap->ref_reloc_sym; | 2259 | reloc_sym = kmap->ref_reloc_sym; |
2263 | if (!reloc_sym) { | 2260 | if (!reloc_sym) { |
2264 | pr_warning("Relocated base symbol is not found!\n"); | 2261 | pr_warning("Relocated base symbol is not found!\n"); |
2265 | ret = -EINVAL; | 2262 | ret = -EINVAL; |
2266 | goto out; | 2263 | goto out; |
2267 | } | 2264 | } |
2268 | } | 2265 | } |
2269 | 2266 | ||
2270 | /* Setup result trace-probe-events */ | 2267 | /* Setup result trace-probe-events */ |
2271 | *tevs = zalloc(sizeof(*tev) * num_matched_functions); | 2268 | *tevs = zalloc(sizeof(*tev) * num_matched_functions); |
2272 | if (!*tevs) { | 2269 | if (!*tevs) { |
2273 | ret = -ENOMEM; | 2270 | ret = -ENOMEM; |
2274 | goto out; | 2271 | goto out; |
2275 | } | 2272 | } |
2276 | 2273 | ||
2277 | ret = 0; | 2274 | ret = 0; |
2278 | map__for_each_symbol(map, sym, nd) { | 2275 | |
2276 | map__for_each_symbol_by_name(map, pp->function, sym) { | ||
2279 | tev = (*tevs) + ret; | 2277 | tev = (*tevs) + ret; |
2280 | tp = &tev->point; | 2278 | tp = &tev->point; |
2281 | if (ret == num_matched_functions) { | 2279 | if (ret == num_matched_functions) { |
2282 | pr_warning("Too many symbols are listed. Skip it.\n"); | 2280 | pr_warning("Too many symbols are listed. Skip it.\n"); |
2283 | break; | 2281 | break; |
2284 | } | 2282 | } |
2285 | ret++; | 2283 | ret++; |
2286 | 2284 | ||
2287 | if (pp->offset > sym->end - sym->start) { | 2285 | if (pp->offset > sym->end - sym->start) { |
2288 | pr_warning("Offset %ld is bigger than the size of %s\n", | 2286 | pr_warning("Offset %ld is bigger than the size of %s\n", |
2289 | pp->offset, sym->name); | 2287 | pp->offset, sym->name); |
2290 | ret = -ENOENT; | 2288 | ret = -ENOENT; |
2291 | goto err_out; | 2289 | goto err_out; |
2292 | } | 2290 | } |
2293 | /* Add one probe point */ | 2291 | /* Add one probe point */ |
2294 | tp->address = map->unmap_ip(map, sym->start) + pp->offset; | 2292 | tp->address = map->unmap_ip(map, sym->start) + pp->offset; |
2295 | if (reloc_sym) { | 2293 | if (reloc_sym) { |
2296 | tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out); | 2294 | tp->symbol = strdup_or_goto(reloc_sym->name, nomem_out); |
2297 | tp->offset = tp->address - reloc_sym->addr; | 2295 | tp->offset = tp->address - reloc_sym->addr; |
2298 | } else { | 2296 | } else { |
2299 | tp->symbol = strdup_or_goto(sym->name, nomem_out); | 2297 | tp->symbol = strdup_or_goto(sym->name, nomem_out); |
2300 | tp->offset = pp->offset; | 2298 | tp->offset = pp->offset; |
2301 | } | 2299 | } |
2302 | tp->retprobe = pp->retprobe; | 2300 | tp->retprobe = pp->retprobe; |
2303 | if (target) | 2301 | if (target) |
2304 | tev->point.module = strdup_or_goto(target, nomem_out); | 2302 | tev->point.module = strdup_or_goto(target, nomem_out); |
2305 | tev->uprobes = pev->uprobes; | 2303 | tev->uprobes = pev->uprobes; |
2306 | tev->nargs = pev->nargs; | 2304 | tev->nargs = pev->nargs; |
2307 | if (tev->nargs) { | 2305 | if (tev->nargs) { |
2308 | tev->args = zalloc(sizeof(struct probe_trace_arg) * | 2306 | tev->args = zalloc(sizeof(struct probe_trace_arg) * |
2309 | tev->nargs); | 2307 | tev->nargs); |
2310 | if (tev->args == NULL) | 2308 | if (tev->args == NULL) |
2311 | goto nomem_out; | 2309 | goto nomem_out; |
2312 | } | 2310 | } |
2313 | for (i = 0; i < tev->nargs; i++) { | 2311 | for (i = 0; i < tev->nargs; i++) { |
2314 | if (pev->args[i].name) | 2312 | if (pev->args[i].name) |
2315 | tev->args[i].name = | 2313 | tev->args[i].name = |
2316 | strdup_or_goto(pev->args[i].name, | 2314 | strdup_or_goto(pev->args[i].name, |
2317 | nomem_out); | 2315 | nomem_out); |
2318 | 2316 | ||
2319 | tev->args[i].value = strdup_or_goto(pev->args[i].var, | 2317 | tev->args[i].value = strdup_or_goto(pev->args[i].var, |
2320 | nomem_out); | 2318 | nomem_out); |
2321 | if (pev->args[i].type) | 2319 | if (pev->args[i].type) |
2322 | tev->args[i].type = | 2320 | tev->args[i].type = |
2323 | strdup_or_goto(pev->args[i].type, | 2321 | strdup_or_goto(pev->args[i].type, |
2324 | nomem_out); | 2322 | nomem_out); |
2325 | } | 2323 | } |
2326 | } | 2324 | } |
2327 | 2325 | ||
2328 | out: | 2326 | out: |
2329 | if (map && pev->uprobes) { | 2327 | if (map && pev->uprobes) { |
2330 | /* Only when using uprobe(exec) map needs to be released */ | 2328 | /* Only when using uprobe(exec) map needs to be released */ |
2331 | dso__delete(map->dso); | 2329 | dso__delete(map->dso); |
2332 | map__delete(map); | 2330 | map__delete(map); |
2333 | } | 2331 | } |
2334 | return ret; | 2332 | return ret; |
2335 | 2333 | ||
2336 | nomem_out: | 2334 | nomem_out: |
2337 | ret = -ENOMEM; | 2335 | ret = -ENOMEM; |
2338 | err_out: | 2336 | err_out: |
2339 | clear_probe_trace_events(*tevs, num_matched_functions); | 2337 | clear_probe_trace_events(*tevs, num_matched_functions); |
2340 | zfree(tevs); | 2338 | zfree(tevs); |
2341 | goto out; | 2339 | goto out; |
2342 | } | 2340 | } |
2343 | 2341 | ||
2344 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, | 2342 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, |
2345 | struct probe_trace_event **tevs, | 2343 | struct probe_trace_event **tevs, |
2346 | int max_tevs, const char *target) | 2344 | int max_tevs, const char *target) |
2347 | { | 2345 | { |
2348 | int ret; | 2346 | int ret; |
2349 | 2347 | ||
2350 | if (pev->uprobes && !pev->group) { | 2348 | if (pev->uprobes && !pev->group) { |
2351 | /* Replace group name if not given */ | 2349 | /* Replace group name if not given */ |
2352 | ret = convert_exec_to_group(target, &pev->group); | 2350 | ret = convert_exec_to_group(target, &pev->group); |
2353 | if (ret != 0) { | 2351 | if (ret != 0) { |
2354 | pr_warning("Failed to make a group name.\n"); | 2352 | pr_warning("Failed to make a group name.\n"); |
2355 | return ret; | 2353 | return ret; |
2356 | } | 2354 | } |
2357 | } | 2355 | } |
2358 | 2356 | ||
2359 | /* Convert perf_probe_event with debuginfo */ | 2357 | /* Convert perf_probe_event with debuginfo */ |
2360 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); | 2358 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); |
2361 | if (ret != 0) | 2359 | if (ret != 0) |
2362 | return ret; /* Found in debuginfo or got an error */ | 2360 | return ret; /* Found in debuginfo or got an error */ |
2363 | 2361 | ||
2364 | return find_probe_trace_events_from_map(pev, tevs, max_tevs, target); | 2362 | return find_probe_trace_events_from_map(pev, tevs, max_tevs, target); |
2365 | } | 2363 | } |
2366 | 2364 | ||
2367 | struct __event_package { | 2365 | struct __event_package { |
2368 | struct perf_probe_event *pev; | 2366 | struct perf_probe_event *pev; |
2369 | struct probe_trace_event *tevs; | 2367 | struct probe_trace_event *tevs; |
2370 | int ntevs; | 2368 | int ntevs; |
2371 | }; | 2369 | }; |
2372 | 2370 | ||
2373 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | 2371 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, |
2374 | int max_tevs, const char *target, bool force_add) | 2372 | int max_tevs, const char *target, bool force_add) |
2375 | { | 2373 | { |
2376 | int i, j, ret; | 2374 | int i, j, ret; |
2377 | struct __event_package *pkgs; | 2375 | struct __event_package *pkgs; |
2378 | 2376 | ||
2379 | ret = 0; | 2377 | ret = 0; |
2380 | pkgs = zalloc(sizeof(struct __event_package) * npevs); | 2378 | pkgs = zalloc(sizeof(struct __event_package) * npevs); |
2381 | 2379 | ||
2382 | if (pkgs == NULL) | 2380 | if (pkgs == NULL) |
2383 | return -ENOMEM; | 2381 | return -ENOMEM; |
2384 | 2382 | ||
2385 | ret = init_symbol_maps(pevs->uprobes); | 2383 | ret = init_symbol_maps(pevs->uprobes); |
2386 | if (ret < 0) { | 2384 | if (ret < 0) { |
2387 | free(pkgs); | 2385 | free(pkgs); |
2388 | return ret; | 2386 | return ret; |
2389 | } | 2387 | } |
2390 | 2388 | ||
2391 | /* Loop 1: convert all events */ | 2389 | /* Loop 1: convert all events */ |
2392 | for (i = 0; i < npevs; i++) { | 2390 | for (i = 0; i < npevs; i++) { |
2393 | pkgs[i].pev = &pevs[i]; | 2391 | pkgs[i].pev = &pevs[i]; |
2394 | /* Convert with or without debuginfo */ | 2392 | /* Convert with or without debuginfo */ |
2395 | ret = convert_to_probe_trace_events(pkgs[i].pev, | 2393 | ret = convert_to_probe_trace_events(pkgs[i].pev, |
2396 | &pkgs[i].tevs, | 2394 | &pkgs[i].tevs, |
2397 | max_tevs, | 2395 | max_tevs, |
2398 | target); | 2396 | target); |
2399 | if (ret < 0) | 2397 | if (ret < 0) |
2400 | goto end; | 2398 | goto end; |
2401 | pkgs[i].ntevs = ret; | 2399 | pkgs[i].ntevs = ret; |
2402 | } | 2400 | } |
2403 | 2401 | ||
2404 | /* Loop 2: add all events */ | 2402 | /* Loop 2: add all events */ |
2405 | for (i = 0; i < npevs; i++) { | 2403 | for (i = 0; i < npevs; i++) { |
2406 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, | 2404 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, |
2407 | pkgs[i].ntevs, force_add); | 2405 | pkgs[i].ntevs, force_add); |
2408 | if (ret < 0) | 2406 | if (ret < 0) |
2409 | break; | 2407 | break; |
2410 | } | 2408 | } |
2411 | end: | 2409 | end: |
2412 | /* Loop 3: cleanup and free trace events */ | 2410 | /* Loop 3: cleanup and free trace events */ |
2413 | for (i = 0; i < npevs; i++) { | 2411 | for (i = 0; i < npevs; i++) { |
2414 | for (j = 0; j < pkgs[i].ntevs; j++) | 2412 | for (j = 0; j < pkgs[i].ntevs; j++) |
2415 | clear_probe_trace_event(&pkgs[i].tevs[j]); | 2413 | clear_probe_trace_event(&pkgs[i].tevs[j]); |
2416 | zfree(&pkgs[i].tevs); | 2414 | zfree(&pkgs[i].tevs); |
2417 | } | 2415 | } |
2418 | free(pkgs); | 2416 | free(pkgs); |
2419 | exit_symbol_maps(); | 2417 | exit_symbol_maps(); |
2420 | 2418 | ||
2421 | return ret; | 2419 | return ret; |
2422 | } | 2420 | } |
2423 | 2421 | ||
2424 | static int __del_trace_probe_event(int fd, struct str_node *ent) | 2422 | static int __del_trace_probe_event(int fd, struct str_node *ent) |
2425 | { | 2423 | { |
2426 | char *p; | 2424 | char *p; |
2427 | char buf[128]; | 2425 | char buf[128]; |
2428 | int ret; | 2426 | int ret; |
2429 | 2427 | ||
2430 | /* Convert from perf-probe event to trace-probe event */ | 2428 | /* Convert from perf-probe event to trace-probe event */ |
2431 | ret = e_snprintf(buf, 128, "-:%s", ent->s); | 2429 | ret = e_snprintf(buf, 128, "-:%s", ent->s); |
2432 | if (ret < 0) | 2430 | if (ret < 0) |
2433 | goto error; | 2431 | goto error; |
2434 | 2432 | ||
2435 | p = strchr(buf + 2, ':'); | 2433 | p = strchr(buf + 2, ':'); |
2436 | if (!p) { | 2434 | if (!p) { |
2437 | pr_debug("Internal error: %s should have ':' but not.\n", | 2435 | pr_debug("Internal error: %s should have ':' but not.\n", |
2438 | ent->s); | 2436 | ent->s); |
2439 | ret = -ENOTSUP; | 2437 | ret = -ENOTSUP; |
2440 | goto error; | 2438 | goto error; |
2441 | } | 2439 | } |
2442 | *p = '/'; | 2440 | *p = '/'; |
2443 | 2441 | ||
2444 | pr_debug("Writing event: %s\n", buf); | 2442 | pr_debug("Writing event: %s\n", buf); |
2445 | ret = write(fd, buf, strlen(buf)); | 2443 | ret = write(fd, buf, strlen(buf)); |
2446 | if (ret < 0) { | 2444 | if (ret < 0) { |
2447 | ret = -errno; | 2445 | ret = -errno; |
2448 | goto error; | 2446 | goto error; |
2449 | } | 2447 | } |
2450 | 2448 | ||
2451 | pr_info("Removed event: %s\n", ent->s); | 2449 | pr_info("Removed event: %s\n", ent->s); |
2452 | return 0; | 2450 | return 0; |
2453 | error: | 2451 | error: |
2454 | pr_warning("Failed to delete event: %s\n", | 2452 | pr_warning("Failed to delete event: %s\n", |
2455 | strerror_r(-ret, buf, sizeof(buf))); | 2453 | strerror_r(-ret, buf, sizeof(buf))); |
2456 | return ret; | 2454 | return ret; |
2457 | } | 2455 | } |
2458 | 2456 | ||
2459 | static int del_trace_probe_event(int fd, const char *buf, | 2457 | static int del_trace_probe_event(int fd, const char *buf, |
2460 | struct strlist *namelist) | 2458 | struct strlist *namelist) |
2461 | { | 2459 | { |
2462 | struct str_node *ent, *n; | 2460 | struct str_node *ent, *n; |
2463 | int ret = -1; | 2461 | int ret = -1; |
2464 | 2462 | ||
2465 | if (strpbrk(buf, "*?")) { /* Glob-exp */ | 2463 | if (strpbrk(buf, "*?")) { /* Glob-exp */ |
2466 | strlist__for_each_safe(ent, n, namelist) | 2464 | strlist__for_each_safe(ent, n, namelist) |
2467 | if (strglobmatch(ent->s, buf)) { | 2465 | if (strglobmatch(ent->s, buf)) { |
2468 | ret = __del_trace_probe_event(fd, ent); | 2466 | ret = __del_trace_probe_event(fd, ent); |
2469 | if (ret < 0) | 2467 | if (ret < 0) |
2470 | break; | 2468 | break; |
2471 | strlist__remove(namelist, ent); | 2469 | strlist__remove(namelist, ent); |
2472 | } | 2470 | } |
2473 | } else { | 2471 | } else { |
2474 | ent = strlist__find(namelist, buf); | 2472 | ent = strlist__find(namelist, buf); |
2475 | if (ent) { | 2473 | if (ent) { |
2476 | ret = __del_trace_probe_event(fd, ent); | 2474 | ret = __del_trace_probe_event(fd, ent); |
2477 | if (ret >= 0) | 2475 | if (ret >= 0) |
2478 | strlist__remove(namelist, ent); | 2476 | strlist__remove(namelist, ent); |
2479 | } | 2477 | } |
2480 | } | 2478 | } |
2481 | 2479 | ||
2482 | return ret; | 2480 | return ret; |
2483 | } | 2481 | } |
2484 | 2482 | ||
2485 | int del_perf_probe_events(struct strlist *dellist) | 2483 | int del_perf_probe_events(struct strlist *dellist) |
2486 | { | 2484 | { |
2487 | int ret = -1, ufd = -1, kfd = -1; | 2485 | int ret = -1, ufd = -1, kfd = -1; |
2488 | char buf[128]; | 2486 | char buf[128]; |
2489 | const char *group, *event; | 2487 | const char *group, *event; |
2490 | char *p, *str; | 2488 | char *p, *str; |
2491 | struct str_node *ent; | 2489 | struct str_node *ent; |
2492 | struct strlist *namelist = NULL, *unamelist = NULL; | 2490 | struct strlist *namelist = NULL, *unamelist = NULL; |
2493 | 2491 | ||
2494 | /* Get current event names */ | 2492 | /* Get current event names */ |
2495 | kfd = open_kprobe_events(true); | 2493 | kfd = open_kprobe_events(true); |
2496 | if (kfd >= 0) | 2494 | if (kfd >= 0) |
2497 | namelist = get_probe_trace_event_names(kfd, true); | 2495 | namelist = get_probe_trace_event_names(kfd, true); |
2498 | 2496 | ||
2499 | ufd = open_uprobe_events(true); | 2497 | ufd = open_uprobe_events(true); |
2500 | if (ufd >= 0) | 2498 | if (ufd >= 0) |
2501 | unamelist = get_probe_trace_event_names(ufd, true); | 2499 | unamelist = get_probe_trace_event_names(ufd, true); |
2502 | 2500 | ||
2503 | if (kfd < 0 && ufd < 0) { | 2501 | if (kfd < 0 && ufd < 0) { |
2504 | print_both_open_warning(kfd, ufd); | 2502 | print_both_open_warning(kfd, ufd); |
2505 | goto error; | 2503 | goto error; |
2506 | } | 2504 | } |
2507 | 2505 | ||
2508 | if (namelist == NULL && unamelist == NULL) | 2506 | if (namelist == NULL && unamelist == NULL) |
2509 | goto error; | 2507 | goto error; |
2510 | 2508 | ||
2511 | strlist__for_each(ent, dellist) { | 2509 | strlist__for_each(ent, dellist) { |
2512 | str = strdup(ent->s); | 2510 | str = strdup(ent->s); |
2513 | if (str == NULL) { | 2511 | if (str == NULL) { |
2514 | ret = -ENOMEM; | 2512 | ret = -ENOMEM; |
2515 | goto error; | 2513 | goto error; |
2516 | } | 2514 | } |
2517 | pr_debug("Parsing: %s\n", str); | 2515 | pr_debug("Parsing: %s\n", str); |
2518 | p = strchr(str, ':'); | 2516 | p = strchr(str, ':'); |
2519 | if (p) { | 2517 | if (p) { |
2520 | group = str; | 2518 | group = str; |
2521 | *p = '\0'; | 2519 | *p = '\0'; |
2522 | event = p + 1; | 2520 | event = p + 1; |
2523 | } else { | 2521 | } else { |
2524 | group = "*"; | 2522 | group = "*"; |
2525 | event = str; | 2523 | event = str; |
2526 | } | 2524 | } |
2527 | 2525 | ||
2528 | ret = e_snprintf(buf, 128, "%s:%s", group, event); | 2526 | ret = e_snprintf(buf, 128, "%s:%s", group, event); |
2529 | if (ret < 0) { | 2527 | if (ret < 0) { |
2530 | pr_err("Failed to copy event."); | 2528 | pr_err("Failed to copy event."); |
2531 | free(str); | 2529 | free(str); |
2532 | goto error; | 2530 | goto error; |
2533 | } | 2531 | } |
2534 | 2532 | ||
2535 | pr_debug("Group: %s, Event: %s\n", group, event); | 2533 | pr_debug("Group: %s, Event: %s\n", group, event); |
2536 | 2534 | ||
2537 | if (namelist) | 2535 | if (namelist) |
2538 | ret = del_trace_probe_event(kfd, buf, namelist); | 2536 | ret = del_trace_probe_event(kfd, buf, namelist); |
2539 | 2537 | ||
2540 | if (unamelist && ret != 0) | 2538 | if (unamelist && ret != 0) |
2541 | ret = del_trace_probe_event(ufd, buf, unamelist); | 2539 | ret = del_trace_probe_event(ufd, buf, unamelist); |
2542 | 2540 | ||
2543 | if (ret != 0) | 2541 | if (ret != 0) |
2544 | pr_info("Info: Event \"%s\" does not exist.\n", buf); | 2542 | pr_info("Info: Event \"%s\" does not exist.\n", buf); |
2545 | 2543 | ||
2546 | free(str); | 2544 | free(str); |
2547 | } | 2545 | } |
2548 | 2546 | ||
2549 | error: | 2547 | error: |
2550 | if (kfd >= 0) { | 2548 | if (kfd >= 0) { |
2551 | strlist__delete(namelist); | 2549 | strlist__delete(namelist); |
2552 | close(kfd); | 2550 | close(kfd); |
2553 | } | 2551 | } |
2554 | 2552 | ||
2555 | if (ufd >= 0) { | 2553 | if (ufd >= 0) { |
2556 | strlist__delete(unamelist); | 2554 | strlist__delete(unamelist); |
2557 | close(ufd); | 2555 | close(ufd); |
2558 | } | 2556 | } |
2559 | 2557 | ||
2560 | return ret; | 2558 | return ret; |
2561 | } | 2559 | } |
2562 | 2560 | ||
2563 | /* TODO: don't use a global variable for filter ... */ | 2561 | /* TODO: don't use a global variable for filter ... */ |
2564 | static struct strfilter *available_func_filter; | 2562 | static struct strfilter *available_func_filter; |
2565 | 2563 | ||
2566 | /* | 2564 | /* |
2567 | * If a symbol corresponds to a function with global binding and | 2565 | * If a symbol corresponds to a function with global binding and |
2568 | * matches filter return 0. For all others return 1. | 2566 | * matches filter return 0. For all others return 1. |
2569 | */ | 2567 | */ |
2570 | static int filter_available_functions(struct map *map __maybe_unused, | 2568 | static int filter_available_functions(struct map *map __maybe_unused, |
2571 | struct symbol *sym) | 2569 | struct symbol *sym) |
2572 | { | 2570 | { |
2573 | if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) && | 2571 | if ((sym->binding == STB_GLOBAL || sym->binding == STB_LOCAL) && |
2574 | strfilter__compare(available_func_filter, sym->name)) | 2572 | strfilter__compare(available_func_filter, sym->name)) |
2575 | return 0; | 2573 | return 0; |
2576 | return 1; | 2574 | return 1; |
2577 | } | 2575 | } |
2578 | 2576 | ||
2579 | int show_available_funcs(const char *target, struct strfilter *_filter, | 2577 | int show_available_funcs(const char *target, struct strfilter *_filter, |
2580 | bool user) | 2578 | bool user) |
2581 | { | 2579 | { |
2582 | struct map *map; | 2580 | struct map *map; |
2583 | int ret; | 2581 | int ret; |
2584 | 2582 | ||
2585 | ret = init_symbol_maps(user); | 2583 | ret = init_symbol_maps(user); |
2586 | if (ret < 0) | 2584 | if (ret < 0) |
2587 | return ret; | 2585 | return ret; |
2588 | 2586 | ||
2589 | /* Get a symbol map */ | 2587 | /* Get a symbol map */ |
2590 | if (user) | 2588 | if (user) |
2591 | map = dso__new_map(target); | 2589 | map = dso__new_map(target); |
2592 | else | 2590 | else |
2593 | map = kernel_get_module_map(target); | 2591 | map = kernel_get_module_map(target); |
2594 | if (!map) { | 2592 | if (!map) { |
2595 | pr_err("Failed to get a map for %s\n", (target) ? : "kernel"); | 2593 | pr_err("Failed to get a map for %s\n", (target) ? : "kernel"); |
2596 | return -EINVAL; | 2594 | return -EINVAL; |
2597 | } | 2595 | } |
2598 | 2596 | ||
2599 | /* Load symbols with given filter */ | 2597 | /* Load symbols with given filter */ |
2600 | available_func_filter = _filter; | 2598 | available_func_filter = _filter; |
2601 | if (map__load(map, filter_available_functions)) { | 2599 | if (map__load(map, filter_available_functions)) { |
2602 | pr_err("Failed to load symbols in %s\n", (target) ? : "kernel"); | 2600 | pr_err("Failed to load symbols in %s\n", (target) ? : "kernel"); |
2603 | goto end; | 2601 | goto end; |
2604 | } | 2602 | } |
2605 | if (!dso__sorted_by_name(map->dso, map->type)) | 2603 | if (!dso__sorted_by_name(map->dso, map->type)) |
2606 | dso__sort_by_name(map->dso, map->type); | 2604 | dso__sort_by_name(map->dso, map->type); |
2607 | 2605 | ||
2608 | /* Show all (filtered) symbols */ | 2606 | /* Show all (filtered) symbols */ |
2609 | setup_pager(); | 2607 | setup_pager(); |
2610 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); | 2608 | dso__fprintf_symbols_by_name(map->dso, map->type, stdout); |
2611 | end: | 2609 | end: |
2612 | if (user) { | 2610 | if (user) { |
2613 | dso__delete(map->dso); | 2611 | dso__delete(map->dso); |
2614 | map__delete(map); | 2612 | map__delete(map); |
2615 | } | 2613 | } |
2616 | exit_symbol_maps(); | 2614 | exit_symbol_maps(); |
tools/perf/util/symbol.c
1 | #include <dirent.h> | 1 | #include <dirent.h> |
2 | #include <errno.h> | 2 | #include <errno.h> |
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <stdio.h> | 4 | #include <stdio.h> |
5 | #include <string.h> | 5 | #include <string.h> |
6 | #include <sys/types.h> | 6 | #include <sys/types.h> |
7 | #include <sys/stat.h> | 7 | #include <sys/stat.h> |
8 | #include <sys/param.h> | 8 | #include <sys/param.h> |
9 | #include <fcntl.h> | 9 | #include <fcntl.h> |
10 | #include <unistd.h> | 10 | #include <unistd.h> |
11 | #include <inttypes.h> | 11 | #include <inttypes.h> |
12 | #include "build-id.h" | 12 | #include "build-id.h" |
13 | #include "util.h" | 13 | #include "util.h" |
14 | #include "debug.h" | 14 | #include "debug.h" |
15 | #include "machine.h" | 15 | #include "machine.h" |
16 | #include "symbol.h" | 16 | #include "symbol.h" |
17 | #include "strlist.h" | 17 | #include "strlist.h" |
18 | #include "header.h" | 18 | #include "header.h" |
19 | 19 | ||
20 | #include <elf.h> | 20 | #include <elf.h> |
21 | #include <limits.h> | 21 | #include <limits.h> |
22 | #include <symbol/kallsyms.h> | 22 | #include <symbol/kallsyms.h> |
23 | #include <sys/utsname.h> | 23 | #include <sys/utsname.h> |
24 | 24 | ||
25 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, | 25 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, |
26 | symbol_filter_t filter); | 26 | symbol_filter_t filter); |
27 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | 27 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, |
28 | symbol_filter_t filter); | 28 | symbol_filter_t filter); |
29 | int vmlinux_path__nr_entries; | 29 | int vmlinux_path__nr_entries; |
30 | char **vmlinux_path; | 30 | char **vmlinux_path; |
31 | 31 | ||
32 | struct symbol_conf symbol_conf = { | 32 | struct symbol_conf symbol_conf = { |
33 | .use_modules = true, | 33 | .use_modules = true, |
34 | .try_vmlinux_path = true, | 34 | .try_vmlinux_path = true, |
35 | .annotate_src = true, | 35 | .annotate_src = true, |
36 | .demangle = true, | 36 | .demangle = true, |
37 | .demangle_kernel = false, | 37 | .demangle_kernel = false, |
38 | .cumulate_callchain = true, | 38 | .cumulate_callchain = true, |
39 | .show_hist_headers = true, | 39 | .show_hist_headers = true, |
40 | .symfs = "", | 40 | .symfs = "", |
41 | }; | 41 | }; |
42 | 42 | ||
43 | static enum dso_binary_type binary_type_symtab[] = { | 43 | static enum dso_binary_type binary_type_symtab[] = { |
44 | DSO_BINARY_TYPE__KALLSYMS, | 44 | DSO_BINARY_TYPE__KALLSYMS, |
45 | DSO_BINARY_TYPE__GUEST_KALLSYMS, | 45 | DSO_BINARY_TYPE__GUEST_KALLSYMS, |
46 | DSO_BINARY_TYPE__JAVA_JIT, | 46 | DSO_BINARY_TYPE__JAVA_JIT, |
47 | DSO_BINARY_TYPE__DEBUGLINK, | 47 | DSO_BINARY_TYPE__DEBUGLINK, |
48 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | 48 | DSO_BINARY_TYPE__BUILD_ID_CACHE, |
49 | DSO_BINARY_TYPE__FEDORA_DEBUGINFO, | 49 | DSO_BINARY_TYPE__FEDORA_DEBUGINFO, |
50 | DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, | 50 | DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, |
51 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | 51 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, |
52 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | 52 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, |
53 | DSO_BINARY_TYPE__GUEST_KMODULE, | 53 | DSO_BINARY_TYPE__GUEST_KMODULE, |
54 | DSO_BINARY_TYPE__GUEST_KMODULE_COMP, | 54 | DSO_BINARY_TYPE__GUEST_KMODULE_COMP, |
55 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 55 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
56 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP, | 56 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP, |
57 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, | 57 | DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO, |
58 | DSO_BINARY_TYPE__NOT_FOUND, | 58 | DSO_BINARY_TYPE__NOT_FOUND, |
59 | }; | 59 | }; |
60 | 60 | ||
61 | #define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab) | 61 | #define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab) |
62 | 62 | ||
63 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) | 63 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) |
64 | { | 64 | { |
65 | symbol_type = toupper(symbol_type); | 65 | symbol_type = toupper(symbol_type); |
66 | 66 | ||
67 | switch (map_type) { | 67 | switch (map_type) { |
68 | case MAP__FUNCTION: | 68 | case MAP__FUNCTION: |
69 | return symbol_type == 'T' || symbol_type == 'W'; | 69 | return symbol_type == 'T' || symbol_type == 'W'; |
70 | case MAP__VARIABLE: | 70 | case MAP__VARIABLE: |
71 | return symbol_type == 'D'; | 71 | return symbol_type == 'D'; |
72 | default: | 72 | default: |
73 | return false; | 73 | return false; |
74 | } | 74 | } |
75 | } | 75 | } |
76 | 76 | ||
77 | static int prefix_underscores_count(const char *str) | 77 | static int prefix_underscores_count(const char *str) |
78 | { | 78 | { |
79 | const char *tail = str; | 79 | const char *tail = str; |
80 | 80 | ||
81 | while (*tail == '_') | 81 | while (*tail == '_') |
82 | tail++; | 82 | tail++; |
83 | 83 | ||
84 | return tail - str; | 84 | return tail - str; |
85 | } | 85 | } |
86 | 86 | ||
87 | #define SYMBOL_A 0 | 87 | #define SYMBOL_A 0 |
88 | #define SYMBOL_B 1 | 88 | #define SYMBOL_B 1 |
89 | 89 | ||
90 | static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | 90 | static int choose_best_symbol(struct symbol *syma, struct symbol *symb) |
91 | { | 91 | { |
92 | s64 a; | 92 | s64 a; |
93 | s64 b; | 93 | s64 b; |
94 | size_t na, nb; | 94 | size_t na, nb; |
95 | 95 | ||
96 | /* Prefer a symbol with non zero length */ | 96 | /* Prefer a symbol with non zero length */ |
97 | a = syma->end - syma->start; | 97 | a = syma->end - syma->start; |
98 | b = symb->end - symb->start; | 98 | b = symb->end - symb->start; |
99 | if ((b == 0) && (a > 0)) | 99 | if ((b == 0) && (a > 0)) |
100 | return SYMBOL_A; | 100 | return SYMBOL_A; |
101 | else if ((a == 0) && (b > 0)) | 101 | else if ((a == 0) && (b > 0)) |
102 | return SYMBOL_B; | 102 | return SYMBOL_B; |
103 | 103 | ||
104 | /* Prefer a non weak symbol over a weak one */ | 104 | /* Prefer a non weak symbol over a weak one */ |
105 | a = syma->binding == STB_WEAK; | 105 | a = syma->binding == STB_WEAK; |
106 | b = symb->binding == STB_WEAK; | 106 | b = symb->binding == STB_WEAK; |
107 | if (b && !a) | 107 | if (b && !a) |
108 | return SYMBOL_A; | 108 | return SYMBOL_A; |
109 | if (a && !b) | 109 | if (a && !b) |
110 | return SYMBOL_B; | 110 | return SYMBOL_B; |
111 | 111 | ||
112 | /* Prefer a global symbol over a non global one */ | 112 | /* Prefer a global symbol over a non global one */ |
113 | a = syma->binding == STB_GLOBAL; | 113 | a = syma->binding == STB_GLOBAL; |
114 | b = symb->binding == STB_GLOBAL; | 114 | b = symb->binding == STB_GLOBAL; |
115 | if (a && !b) | 115 | if (a && !b) |
116 | return SYMBOL_A; | 116 | return SYMBOL_A; |
117 | if (b && !a) | 117 | if (b && !a) |
118 | return SYMBOL_B; | 118 | return SYMBOL_B; |
119 | 119 | ||
120 | /* Prefer a symbol with less underscores */ | 120 | /* Prefer a symbol with less underscores */ |
121 | a = prefix_underscores_count(syma->name); | 121 | a = prefix_underscores_count(syma->name); |
122 | b = prefix_underscores_count(symb->name); | 122 | b = prefix_underscores_count(symb->name); |
123 | if (b > a) | 123 | if (b > a) |
124 | return SYMBOL_A; | 124 | return SYMBOL_A; |
125 | else if (a > b) | 125 | else if (a > b) |
126 | return SYMBOL_B; | 126 | return SYMBOL_B; |
127 | 127 | ||
128 | /* Choose the symbol with the longest name */ | 128 | /* Choose the symbol with the longest name */ |
129 | na = strlen(syma->name); | 129 | na = strlen(syma->name); |
130 | nb = strlen(symb->name); | 130 | nb = strlen(symb->name); |
131 | if (na > nb) | 131 | if (na > nb) |
132 | return SYMBOL_A; | 132 | return SYMBOL_A; |
133 | else if (na < nb) | 133 | else if (na < nb) |
134 | return SYMBOL_B; | 134 | return SYMBOL_B; |
135 | 135 | ||
136 | /* Avoid "SyS" kernel syscall aliases */ | 136 | /* Avoid "SyS" kernel syscall aliases */ |
137 | if (na >= 3 && !strncmp(syma->name, "SyS", 3)) | 137 | if (na >= 3 && !strncmp(syma->name, "SyS", 3)) |
138 | return SYMBOL_B; | 138 | return SYMBOL_B; |
139 | if (na >= 10 && !strncmp(syma->name, "compat_SyS", 10)) | 139 | if (na >= 10 && !strncmp(syma->name, "compat_SyS", 10)) |
140 | return SYMBOL_B; | 140 | return SYMBOL_B; |
141 | 141 | ||
142 | return SYMBOL_A; | 142 | return SYMBOL_A; |
143 | } | 143 | } |
144 | 144 | ||
145 | void symbols__fixup_duplicate(struct rb_root *symbols) | 145 | void symbols__fixup_duplicate(struct rb_root *symbols) |
146 | { | 146 | { |
147 | struct rb_node *nd; | 147 | struct rb_node *nd; |
148 | struct symbol *curr, *next; | 148 | struct symbol *curr, *next; |
149 | 149 | ||
150 | nd = rb_first(symbols); | 150 | nd = rb_first(symbols); |
151 | 151 | ||
152 | while (nd) { | 152 | while (nd) { |
153 | curr = rb_entry(nd, struct symbol, rb_node); | 153 | curr = rb_entry(nd, struct symbol, rb_node); |
154 | again: | 154 | again: |
155 | nd = rb_next(&curr->rb_node); | 155 | nd = rb_next(&curr->rb_node); |
156 | next = rb_entry(nd, struct symbol, rb_node); | 156 | next = rb_entry(nd, struct symbol, rb_node); |
157 | 157 | ||
158 | if (!nd) | 158 | if (!nd) |
159 | break; | 159 | break; |
160 | 160 | ||
161 | if (curr->start != next->start) | 161 | if (curr->start != next->start) |
162 | continue; | 162 | continue; |
163 | 163 | ||
164 | if (choose_best_symbol(curr, next) == SYMBOL_A) { | 164 | if (choose_best_symbol(curr, next) == SYMBOL_A) { |
165 | rb_erase(&next->rb_node, symbols); | 165 | rb_erase(&next->rb_node, symbols); |
166 | symbol__delete(next); | 166 | symbol__delete(next); |
167 | goto again; | 167 | goto again; |
168 | } else { | 168 | } else { |
169 | nd = rb_next(&curr->rb_node); | 169 | nd = rb_next(&curr->rb_node); |
170 | rb_erase(&curr->rb_node, symbols); | 170 | rb_erase(&curr->rb_node, symbols); |
171 | symbol__delete(curr); | 171 | symbol__delete(curr); |
172 | } | 172 | } |
173 | } | 173 | } |
174 | } | 174 | } |
175 | 175 | ||
176 | void symbols__fixup_end(struct rb_root *symbols) | 176 | void symbols__fixup_end(struct rb_root *symbols) |
177 | { | 177 | { |
178 | struct rb_node *nd, *prevnd = rb_first(symbols); | 178 | struct rb_node *nd, *prevnd = rb_first(symbols); |
179 | struct symbol *curr, *prev; | 179 | struct symbol *curr, *prev; |
180 | 180 | ||
181 | if (prevnd == NULL) | 181 | if (prevnd == NULL) |
182 | return; | 182 | return; |
183 | 183 | ||
184 | curr = rb_entry(prevnd, struct symbol, rb_node); | 184 | curr = rb_entry(prevnd, struct symbol, rb_node); |
185 | 185 | ||
186 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | 186 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { |
187 | prev = curr; | 187 | prev = curr; |
188 | curr = rb_entry(nd, struct symbol, rb_node); | 188 | curr = rb_entry(nd, struct symbol, rb_node); |
189 | 189 | ||
190 | if (prev->end == prev->start && prev->end != curr->start) | 190 | if (prev->end == prev->start && prev->end != curr->start) |
191 | prev->end = curr->start; | 191 | prev->end = curr->start; |
192 | } | 192 | } |
193 | 193 | ||
194 | /* Last entry */ | 194 | /* Last entry */ |
195 | if (curr->end == curr->start) | 195 | if (curr->end == curr->start) |
196 | curr->end = roundup(curr->start, 4096); | 196 | curr->end = roundup(curr->start, 4096); |
197 | } | 197 | } |
198 | 198 | ||
199 | void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) | 199 | void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) |
200 | { | 200 | { |
201 | struct map *prev, *curr; | 201 | struct map *prev, *curr; |
202 | struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]); | 202 | struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]); |
203 | 203 | ||
204 | if (prevnd == NULL) | 204 | if (prevnd == NULL) |
205 | return; | 205 | return; |
206 | 206 | ||
207 | curr = rb_entry(prevnd, struct map, rb_node); | 207 | curr = rb_entry(prevnd, struct map, rb_node); |
208 | 208 | ||
209 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { | 209 | for (nd = rb_next(prevnd); nd; nd = rb_next(nd)) { |
210 | prev = curr; | 210 | prev = curr; |
211 | curr = rb_entry(nd, struct map, rb_node); | 211 | curr = rb_entry(nd, struct map, rb_node); |
212 | prev->end = curr->start; | 212 | prev->end = curr->start; |
213 | } | 213 | } |
214 | 214 | ||
215 | /* | 215 | /* |
216 | * We still haven't the actual symbols, so guess the | 216 | * We still haven't the actual symbols, so guess the |
217 | * last map final address. | 217 | * last map final address. |
218 | */ | 218 | */ |
219 | curr->end = ~0ULL; | 219 | curr->end = ~0ULL; |
220 | } | 220 | } |
221 | 221 | ||
222 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) | 222 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) |
223 | { | 223 | { |
224 | size_t namelen = strlen(name) + 1; | 224 | size_t namelen = strlen(name) + 1; |
225 | struct symbol *sym = calloc(1, (symbol_conf.priv_size + | 225 | struct symbol *sym = calloc(1, (symbol_conf.priv_size + |
226 | sizeof(*sym) + namelen)); | 226 | sizeof(*sym) + namelen)); |
227 | if (sym == NULL) | 227 | if (sym == NULL) |
228 | return NULL; | 228 | return NULL; |
229 | 229 | ||
230 | if (symbol_conf.priv_size) | 230 | if (symbol_conf.priv_size) |
231 | sym = ((void *)sym) + symbol_conf.priv_size; | 231 | sym = ((void *)sym) + symbol_conf.priv_size; |
232 | 232 | ||
233 | sym->start = start; | 233 | sym->start = start; |
234 | sym->end = len ? start + len : start; | 234 | sym->end = len ? start + len : start; |
235 | sym->binding = binding; | 235 | sym->binding = binding; |
236 | sym->namelen = namelen - 1; | 236 | sym->namelen = namelen - 1; |
237 | 237 | ||
238 | pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n", | 238 | pr_debug4("%s: %s %#" PRIx64 "-%#" PRIx64 "\n", |
239 | __func__, name, start, sym->end); | 239 | __func__, name, start, sym->end); |
240 | memcpy(sym->name, name, namelen); | 240 | memcpy(sym->name, name, namelen); |
241 | 241 | ||
242 | return sym; | 242 | return sym; |
243 | } | 243 | } |
244 | 244 | ||
245 | void symbol__delete(struct symbol *sym) | 245 | void symbol__delete(struct symbol *sym) |
246 | { | 246 | { |
247 | free(((void *)sym) - symbol_conf.priv_size); | 247 | free(((void *)sym) - symbol_conf.priv_size); |
248 | } | 248 | } |
249 | 249 | ||
250 | size_t symbol__fprintf(struct symbol *sym, FILE *fp) | 250 | size_t symbol__fprintf(struct symbol *sym, FILE *fp) |
251 | { | 251 | { |
252 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", | 252 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", |
253 | sym->start, sym->end, | 253 | sym->start, sym->end, |
254 | sym->binding == STB_GLOBAL ? 'g' : | 254 | sym->binding == STB_GLOBAL ? 'g' : |
255 | sym->binding == STB_LOCAL ? 'l' : 'w', | 255 | sym->binding == STB_LOCAL ? 'l' : 'w', |
256 | sym->name); | 256 | sym->name); |
257 | } | 257 | } |
258 | 258 | ||
259 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | 259 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, |
260 | const struct addr_location *al, FILE *fp) | 260 | const struct addr_location *al, FILE *fp) |
261 | { | 261 | { |
262 | unsigned long offset; | 262 | unsigned long offset; |
263 | size_t length; | 263 | size_t length; |
264 | 264 | ||
265 | if (sym && sym->name) { | 265 | if (sym && sym->name) { |
266 | length = fprintf(fp, "%s", sym->name); | 266 | length = fprintf(fp, "%s", sym->name); |
267 | if (al) { | 267 | if (al) { |
268 | if (al->addr < sym->end) | 268 | if (al->addr < sym->end) |
269 | offset = al->addr - sym->start; | 269 | offset = al->addr - sym->start; |
270 | else | 270 | else |
271 | offset = al->addr - al->map->start - sym->start; | 271 | offset = al->addr - al->map->start - sym->start; |
272 | length += fprintf(fp, "+0x%lx", offset); | 272 | length += fprintf(fp, "+0x%lx", offset); |
273 | } | 273 | } |
274 | return length; | 274 | return length; |
275 | } else | 275 | } else |
276 | return fprintf(fp, "[unknown]"); | 276 | return fprintf(fp, "[unknown]"); |
277 | } | 277 | } |
278 | 278 | ||
279 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) | 279 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) |
280 | { | 280 | { |
281 | return symbol__fprintf_symname_offs(sym, NULL, fp); | 281 | return symbol__fprintf_symname_offs(sym, NULL, fp); |
282 | } | 282 | } |
283 | 283 | ||
284 | void symbols__delete(struct rb_root *symbols) | 284 | void symbols__delete(struct rb_root *symbols) |
285 | { | 285 | { |
286 | struct symbol *pos; | 286 | struct symbol *pos; |
287 | struct rb_node *next = rb_first(symbols); | 287 | struct rb_node *next = rb_first(symbols); |
288 | 288 | ||
289 | while (next) { | 289 | while (next) { |
290 | pos = rb_entry(next, struct symbol, rb_node); | 290 | pos = rb_entry(next, struct symbol, rb_node); |
291 | next = rb_next(&pos->rb_node); | 291 | next = rb_next(&pos->rb_node); |
292 | rb_erase(&pos->rb_node, symbols); | 292 | rb_erase(&pos->rb_node, symbols); |
293 | symbol__delete(pos); | 293 | symbol__delete(pos); |
294 | } | 294 | } |
295 | } | 295 | } |
296 | 296 | ||
297 | void symbols__insert(struct rb_root *symbols, struct symbol *sym) | 297 | void symbols__insert(struct rb_root *symbols, struct symbol *sym) |
298 | { | 298 | { |
299 | struct rb_node **p = &symbols->rb_node; | 299 | struct rb_node **p = &symbols->rb_node; |
300 | struct rb_node *parent = NULL; | 300 | struct rb_node *parent = NULL; |
301 | const u64 ip = sym->start; | 301 | const u64 ip = sym->start; |
302 | struct symbol *s; | 302 | struct symbol *s; |
303 | 303 | ||
304 | while (*p != NULL) { | 304 | while (*p != NULL) { |
305 | parent = *p; | 305 | parent = *p; |
306 | s = rb_entry(parent, struct symbol, rb_node); | 306 | s = rb_entry(parent, struct symbol, rb_node); |
307 | if (ip < s->start) | 307 | if (ip < s->start) |
308 | p = &(*p)->rb_left; | 308 | p = &(*p)->rb_left; |
309 | else | 309 | else |
310 | p = &(*p)->rb_right; | 310 | p = &(*p)->rb_right; |
311 | } | 311 | } |
312 | rb_link_node(&sym->rb_node, parent, p); | 312 | rb_link_node(&sym->rb_node, parent, p); |
313 | rb_insert_color(&sym->rb_node, symbols); | 313 | rb_insert_color(&sym->rb_node, symbols); |
314 | } | 314 | } |
315 | 315 | ||
316 | static struct symbol *symbols__find(struct rb_root *symbols, u64 ip) | 316 | static struct symbol *symbols__find(struct rb_root *symbols, u64 ip) |
317 | { | 317 | { |
318 | struct rb_node *n; | 318 | struct rb_node *n; |
319 | 319 | ||
320 | if (symbols == NULL) | 320 | if (symbols == NULL) |
321 | return NULL; | 321 | return NULL; |
322 | 322 | ||
323 | n = symbols->rb_node; | 323 | n = symbols->rb_node; |
324 | 324 | ||
325 | while (n) { | 325 | while (n) { |
326 | struct symbol *s = rb_entry(n, struct symbol, rb_node); | 326 | struct symbol *s = rb_entry(n, struct symbol, rb_node); |
327 | 327 | ||
328 | if (ip < s->start) | 328 | if (ip < s->start) |
329 | n = n->rb_left; | 329 | n = n->rb_left; |
330 | else if (ip >= s->end) | 330 | else if (ip >= s->end) |
331 | n = n->rb_right; | 331 | n = n->rb_right; |
332 | else | 332 | else |
333 | return s; | 333 | return s; |
334 | } | 334 | } |
335 | 335 | ||
336 | return NULL; | 336 | return NULL; |
337 | } | 337 | } |
338 | 338 | ||
339 | static struct symbol *symbols__first(struct rb_root *symbols) | 339 | static struct symbol *symbols__first(struct rb_root *symbols) |
340 | { | 340 | { |
341 | struct rb_node *n = rb_first(symbols); | 341 | struct rb_node *n = rb_first(symbols); |
342 | 342 | ||
343 | if (n) | 343 | if (n) |
344 | return rb_entry(n, struct symbol, rb_node); | 344 | return rb_entry(n, struct symbol, rb_node); |
345 | 345 | ||
346 | return NULL; | 346 | return NULL; |
347 | } | 347 | } |
348 | 348 | ||
349 | static struct symbol *symbols__next(struct symbol *sym) | 349 | static struct symbol *symbols__next(struct symbol *sym) |
350 | { | 350 | { |
351 | struct rb_node *n = rb_next(&sym->rb_node); | 351 | struct rb_node *n = rb_next(&sym->rb_node); |
352 | 352 | ||
353 | if (n) | 353 | if (n) |
354 | return rb_entry(n, struct symbol, rb_node); | 354 | return rb_entry(n, struct symbol, rb_node); |
355 | 355 | ||
356 | return NULL; | 356 | return NULL; |
357 | } | 357 | } |
358 | 358 | ||
359 | struct symbol_name_rb_node { | 359 | struct symbol_name_rb_node { |
360 | struct rb_node rb_node; | 360 | struct rb_node rb_node; |
361 | struct symbol sym; | 361 | struct symbol sym; |
362 | }; | 362 | }; |
363 | 363 | ||
364 | static void symbols__insert_by_name(struct rb_root *symbols, struct symbol *sym) | 364 | static void symbols__insert_by_name(struct rb_root *symbols, struct symbol *sym) |
365 | { | 365 | { |
366 | struct rb_node **p = &symbols->rb_node; | 366 | struct rb_node **p = &symbols->rb_node; |
367 | struct rb_node *parent = NULL; | 367 | struct rb_node *parent = NULL; |
368 | struct symbol_name_rb_node *symn, *s; | 368 | struct symbol_name_rb_node *symn, *s; |
369 | 369 | ||
370 | symn = container_of(sym, struct symbol_name_rb_node, sym); | 370 | symn = container_of(sym, struct symbol_name_rb_node, sym); |
371 | 371 | ||
372 | while (*p != NULL) { | 372 | while (*p != NULL) { |
373 | parent = *p; | 373 | parent = *p; |
374 | s = rb_entry(parent, struct symbol_name_rb_node, rb_node); | 374 | s = rb_entry(parent, struct symbol_name_rb_node, rb_node); |
375 | if (strcmp(sym->name, s->sym.name) < 0) | 375 | if (strcmp(sym->name, s->sym.name) < 0) |
376 | p = &(*p)->rb_left; | 376 | p = &(*p)->rb_left; |
377 | else | 377 | else |
378 | p = &(*p)->rb_right; | 378 | p = &(*p)->rb_right; |
379 | } | 379 | } |
380 | rb_link_node(&symn->rb_node, parent, p); | 380 | rb_link_node(&symn->rb_node, parent, p); |
381 | rb_insert_color(&symn->rb_node, symbols); | 381 | rb_insert_color(&symn->rb_node, symbols); |
382 | } | 382 | } |
383 | 383 | ||
384 | static void symbols__sort_by_name(struct rb_root *symbols, | 384 | static void symbols__sort_by_name(struct rb_root *symbols, |
385 | struct rb_root *source) | 385 | struct rb_root *source) |
386 | { | 386 | { |
387 | struct rb_node *nd; | 387 | struct rb_node *nd; |
388 | 388 | ||
389 | for (nd = rb_first(source); nd; nd = rb_next(nd)) { | 389 | for (nd = rb_first(source); nd; nd = rb_next(nd)) { |
390 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | 390 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); |
391 | symbols__insert_by_name(symbols, pos); | 391 | symbols__insert_by_name(symbols, pos); |
392 | } | 392 | } |
393 | } | 393 | } |
394 | 394 | ||
395 | static struct symbol *symbols__find_by_name(struct rb_root *symbols, | 395 | static struct symbol *symbols__find_by_name(struct rb_root *symbols, |
396 | const char *name) | 396 | const char *name) |
397 | { | 397 | { |
398 | struct rb_node *n; | 398 | struct rb_node *n; |
399 | struct symbol_name_rb_node *s; | ||
399 | 400 | ||
400 | if (symbols == NULL) | 401 | if (symbols == NULL) |
401 | return NULL; | 402 | return NULL; |
402 | 403 | ||
403 | n = symbols->rb_node; | 404 | n = symbols->rb_node; |
404 | 405 | ||
405 | while (n) { | 406 | while (n) { |
406 | struct symbol_name_rb_node *s; | ||
407 | int cmp; | 407 | int cmp; |
408 | 408 | ||
409 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); | 409 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); |
410 | cmp = strcmp(name, s->sym.name); | 410 | cmp = strcmp(name, s->sym.name); |
411 | 411 | ||
412 | if (cmp < 0) | 412 | if (cmp < 0) |
413 | n = n->rb_left; | 413 | n = n->rb_left; |
414 | else if (cmp > 0) | 414 | else if (cmp > 0) |
415 | n = n->rb_right; | 415 | n = n->rb_right; |
416 | else | 416 | else |
417 | return &s->sym; | 417 | break; |
418 | } | 418 | } |
419 | 419 | ||
420 | return NULL; | 420 | if (n == NULL) |
421 | return NULL; | ||
422 | |||
423 | /* return first symbol that has same name (if any) */ | ||
424 | for (n = rb_prev(n); n; n = rb_prev(n)) { | ||
425 | struct symbol_name_rb_node *tmp; | ||
426 | |||
427 | tmp = rb_entry(n, struct symbol_name_rb_node, rb_node); | ||
428 | if (strcmp(tmp->sym.name, s->sym.name)) | ||
429 | break; | ||
430 | |||
431 | s = tmp; | ||
432 | } | ||
433 | |||
434 | return &s->sym; | ||
421 | } | 435 | } |
422 | 436 | ||
423 | struct symbol *dso__find_symbol(struct dso *dso, | 437 | struct symbol *dso__find_symbol(struct dso *dso, |
424 | enum map_type type, u64 addr) | 438 | enum map_type type, u64 addr) |
425 | { | 439 | { |
426 | return symbols__find(&dso->symbols[type], addr); | 440 | return symbols__find(&dso->symbols[type], addr); |
427 | } | 441 | } |
428 | 442 | ||
429 | struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) | 443 | struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) |
430 | { | 444 | { |
431 | return symbols__first(&dso->symbols[type]); | 445 | return symbols__first(&dso->symbols[type]); |
432 | } | 446 | } |
433 | 447 | ||
434 | struct symbol *dso__next_symbol(struct symbol *sym) | 448 | struct symbol *dso__next_symbol(struct symbol *sym) |
435 | { | 449 | { |
436 | return symbols__next(sym); | 450 | return symbols__next(sym); |
437 | } | 451 | } |
438 | 452 | ||
453 | struct symbol *symbol__next_by_name(struct symbol *sym) | ||
454 | { | ||
455 | struct symbol_name_rb_node *s = container_of(sym, struct symbol_name_rb_node, sym); | ||
456 | struct rb_node *n = rb_next(&s->rb_node); | ||
457 | |||
458 | return n ? &rb_entry(n, struct symbol_name_rb_node, rb_node)->sym : NULL; | ||
459 | } | ||
460 | |||
461 | /* | ||
462 | * Teturns first symbol that matched with @name. | ||
463 | */ | ||
439 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | 464 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, |
440 | const char *name) | 465 | const char *name) |
441 | { | 466 | { |
442 | return symbols__find_by_name(&dso->symbol_names[type], name); | 467 | return symbols__find_by_name(&dso->symbol_names[type], name); |
443 | } | 468 | } |
444 | 469 | ||
445 | void dso__sort_by_name(struct dso *dso, enum map_type type) | 470 | void dso__sort_by_name(struct dso *dso, enum map_type type) |
446 | { | 471 | { |
447 | dso__set_sorted_by_name(dso, type); | 472 | dso__set_sorted_by_name(dso, type); |
448 | return symbols__sort_by_name(&dso->symbol_names[type], | 473 | return symbols__sort_by_name(&dso->symbol_names[type], |
449 | &dso->symbols[type]); | 474 | &dso->symbols[type]); |
450 | } | 475 | } |
451 | 476 | ||
452 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | 477 | size_t dso__fprintf_symbols_by_name(struct dso *dso, |
453 | enum map_type type, FILE *fp) | 478 | enum map_type type, FILE *fp) |
454 | { | 479 | { |
455 | size_t ret = 0; | 480 | size_t ret = 0; |
456 | struct rb_node *nd; | 481 | struct rb_node *nd; |
457 | struct symbol_name_rb_node *pos; | 482 | struct symbol_name_rb_node *pos; |
458 | 483 | ||
459 | for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) { | 484 | for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) { |
460 | pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); | 485 | pos = rb_entry(nd, struct symbol_name_rb_node, rb_node); |
461 | fprintf(fp, "%s\n", pos->sym.name); | 486 | fprintf(fp, "%s\n", pos->sym.name); |
462 | } | 487 | } |
463 | 488 | ||
464 | return ret; | 489 | return ret; |
465 | } | 490 | } |
466 | 491 | ||
467 | int modules__parse(const char *filename, void *arg, | 492 | int modules__parse(const char *filename, void *arg, |
468 | int (*process_module)(void *arg, const char *name, | 493 | int (*process_module)(void *arg, const char *name, |
469 | u64 start)) | 494 | u64 start)) |
470 | { | 495 | { |
471 | char *line = NULL; | 496 | char *line = NULL; |
472 | size_t n; | 497 | size_t n; |
473 | FILE *file; | 498 | FILE *file; |
474 | int err = 0; | 499 | int err = 0; |
475 | 500 | ||
476 | file = fopen(filename, "r"); | 501 | file = fopen(filename, "r"); |
477 | if (file == NULL) | 502 | if (file == NULL) |
478 | return -1; | 503 | return -1; |
479 | 504 | ||
480 | while (1) { | 505 | while (1) { |
481 | char name[PATH_MAX]; | 506 | char name[PATH_MAX]; |
482 | u64 start; | 507 | u64 start; |
483 | char *sep; | 508 | char *sep; |
484 | ssize_t line_len; | 509 | ssize_t line_len; |
485 | 510 | ||
486 | line_len = getline(&line, &n, file); | 511 | line_len = getline(&line, &n, file); |
487 | if (line_len < 0) { | 512 | if (line_len < 0) { |
488 | if (feof(file)) | 513 | if (feof(file)) |
489 | break; | 514 | break; |
490 | err = -1; | 515 | err = -1; |
491 | goto out; | 516 | goto out; |
492 | } | 517 | } |
493 | 518 | ||
494 | if (!line) { | 519 | if (!line) { |
495 | err = -1; | 520 | err = -1; |
496 | goto out; | 521 | goto out; |
497 | } | 522 | } |
498 | 523 | ||
499 | line[--line_len] = '\0'; /* \n */ | 524 | line[--line_len] = '\0'; /* \n */ |
500 | 525 | ||
501 | sep = strrchr(line, 'x'); | 526 | sep = strrchr(line, 'x'); |
502 | if (sep == NULL) | 527 | if (sep == NULL) |
503 | continue; | 528 | continue; |
504 | 529 | ||
505 | hex2u64(sep + 1, &start); | 530 | hex2u64(sep + 1, &start); |
506 | 531 | ||
507 | sep = strchr(line, ' '); | 532 | sep = strchr(line, ' '); |
508 | if (sep == NULL) | 533 | if (sep == NULL) |
509 | continue; | 534 | continue; |
510 | 535 | ||
511 | *sep = '\0'; | 536 | *sep = '\0'; |
512 | 537 | ||
513 | scnprintf(name, sizeof(name), "[%s]", line); | 538 | scnprintf(name, sizeof(name), "[%s]", line); |
514 | 539 | ||
515 | err = process_module(arg, name, start); | 540 | err = process_module(arg, name, start); |
516 | if (err) | 541 | if (err) |
517 | break; | 542 | break; |
518 | } | 543 | } |
519 | out: | 544 | out: |
520 | free(line); | 545 | free(line); |
521 | fclose(file); | 546 | fclose(file); |
522 | return err; | 547 | return err; |
523 | } | 548 | } |
524 | 549 | ||
525 | struct process_kallsyms_args { | 550 | struct process_kallsyms_args { |
526 | struct map *map; | 551 | struct map *map; |
527 | struct dso *dso; | 552 | struct dso *dso; |
528 | }; | 553 | }; |
529 | 554 | ||
530 | /* | 555 | /* |
531 | * These are symbols in the kernel image, so make sure that | 556 | * These are symbols in the kernel image, so make sure that |
532 | * sym is from a kernel DSO. | 557 | * sym is from a kernel DSO. |
533 | */ | 558 | */ |
534 | bool symbol__is_idle(struct symbol *sym) | 559 | bool symbol__is_idle(struct symbol *sym) |
535 | { | 560 | { |
536 | const char * const idle_symbols[] = { | 561 | const char * const idle_symbols[] = { |
537 | "cpu_idle", | 562 | "cpu_idle", |
538 | "cpu_startup_entry", | 563 | "cpu_startup_entry", |
539 | "intel_idle", | 564 | "intel_idle", |
540 | "default_idle", | 565 | "default_idle", |
541 | "native_safe_halt", | 566 | "native_safe_halt", |
542 | "enter_idle", | 567 | "enter_idle", |
543 | "exit_idle", | 568 | "exit_idle", |
544 | "mwait_idle", | 569 | "mwait_idle", |
545 | "mwait_idle_with_hints", | 570 | "mwait_idle_with_hints", |
546 | "poll_idle", | 571 | "poll_idle", |
547 | "ppc64_runlatch_off", | 572 | "ppc64_runlatch_off", |
548 | "pseries_dedicated_idle_sleep", | 573 | "pseries_dedicated_idle_sleep", |
549 | NULL | 574 | NULL |
550 | }; | 575 | }; |
551 | 576 | ||
552 | int i; | 577 | int i; |
553 | 578 | ||
554 | if (!sym) | 579 | if (!sym) |
555 | return false; | 580 | return false; |
556 | 581 | ||
557 | for (i = 0; idle_symbols[i]; i++) { | 582 | for (i = 0; idle_symbols[i]; i++) { |
558 | if (!strcmp(idle_symbols[i], sym->name)) | 583 | if (!strcmp(idle_symbols[i], sym->name)) |
559 | return true; | 584 | return true; |
560 | } | 585 | } |
561 | 586 | ||
562 | return false; | 587 | return false; |
563 | } | 588 | } |
564 | 589 | ||
565 | static int map__process_kallsym_symbol(void *arg, const char *name, | 590 | static int map__process_kallsym_symbol(void *arg, const char *name, |
566 | char type, u64 start) | 591 | char type, u64 start) |
567 | { | 592 | { |
568 | struct symbol *sym; | 593 | struct symbol *sym; |
569 | struct process_kallsyms_args *a = arg; | 594 | struct process_kallsyms_args *a = arg; |
570 | struct rb_root *root = &a->dso->symbols[a->map->type]; | 595 | struct rb_root *root = &a->dso->symbols[a->map->type]; |
571 | 596 | ||
572 | if (!symbol_type__is_a(type, a->map->type)) | 597 | if (!symbol_type__is_a(type, a->map->type)) |
573 | return 0; | 598 | return 0; |
574 | 599 | ||
575 | /* | 600 | /* |
576 | * module symbols are not sorted so we add all | 601 | * module symbols are not sorted so we add all |
577 | * symbols, setting length to 0, and rely on | 602 | * symbols, setting length to 0, and rely on |
578 | * symbols__fixup_end() to fix it up. | 603 | * symbols__fixup_end() to fix it up. |
579 | */ | 604 | */ |
580 | sym = symbol__new(start, 0, kallsyms2elf_type(type), name); | 605 | sym = symbol__new(start, 0, kallsyms2elf_type(type), name); |
581 | if (sym == NULL) | 606 | if (sym == NULL) |
582 | return -ENOMEM; | 607 | return -ENOMEM; |
583 | /* | 608 | /* |
584 | * We will pass the symbols to the filter later, in | 609 | * We will pass the symbols to the filter later, in |
585 | * map__split_kallsyms, when we have split the maps per module | 610 | * map__split_kallsyms, when we have split the maps per module |
586 | */ | 611 | */ |
587 | symbols__insert(root, sym); | 612 | symbols__insert(root, sym); |
588 | 613 | ||
589 | return 0; | 614 | return 0; |
590 | } | 615 | } |
591 | 616 | ||
592 | /* | 617 | /* |
593 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, | 618 | * Loads the function entries in /proc/kallsyms into kernel_map->dso, |
594 | * so that we can in the next step set the symbol ->end address and then | 619 | * so that we can in the next step set the symbol ->end address and then |
595 | * call kernel_maps__split_kallsyms. | 620 | * call kernel_maps__split_kallsyms. |
596 | */ | 621 | */ |
597 | static int dso__load_all_kallsyms(struct dso *dso, const char *filename, | 622 | static int dso__load_all_kallsyms(struct dso *dso, const char *filename, |
598 | struct map *map) | 623 | struct map *map) |
599 | { | 624 | { |
600 | struct process_kallsyms_args args = { .map = map, .dso = dso, }; | 625 | struct process_kallsyms_args args = { .map = map, .dso = dso, }; |
601 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); | 626 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); |
602 | } | 627 | } |
603 | 628 | ||
604 | static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map, | 629 | static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map, |
605 | symbol_filter_t filter) | 630 | symbol_filter_t filter) |
606 | { | 631 | { |
607 | struct map_groups *kmaps = map__kmap(map)->kmaps; | 632 | struct map_groups *kmaps = map__kmap(map)->kmaps; |
608 | struct map *curr_map; | 633 | struct map *curr_map; |
609 | struct symbol *pos; | 634 | struct symbol *pos; |
610 | int count = 0, moved = 0; | 635 | int count = 0, moved = 0; |
611 | struct rb_root *root = &dso->symbols[map->type]; | 636 | struct rb_root *root = &dso->symbols[map->type]; |
612 | struct rb_node *next = rb_first(root); | 637 | struct rb_node *next = rb_first(root); |
613 | 638 | ||
614 | while (next) { | 639 | while (next) { |
615 | char *module; | 640 | char *module; |
616 | 641 | ||
617 | pos = rb_entry(next, struct symbol, rb_node); | 642 | pos = rb_entry(next, struct symbol, rb_node); |
618 | next = rb_next(&pos->rb_node); | 643 | next = rb_next(&pos->rb_node); |
619 | 644 | ||
620 | module = strchr(pos->name, '\t'); | 645 | module = strchr(pos->name, '\t'); |
621 | if (module) | 646 | if (module) |
622 | *module = '\0'; | 647 | *module = '\0'; |
623 | 648 | ||
624 | curr_map = map_groups__find(kmaps, map->type, pos->start); | 649 | curr_map = map_groups__find(kmaps, map->type, pos->start); |
625 | 650 | ||
626 | if (!curr_map || (filter && filter(curr_map, pos))) { | 651 | if (!curr_map || (filter && filter(curr_map, pos))) { |
627 | rb_erase(&pos->rb_node, root); | 652 | rb_erase(&pos->rb_node, root); |
628 | symbol__delete(pos); | 653 | symbol__delete(pos); |
629 | } else { | 654 | } else { |
630 | pos->start -= curr_map->start - curr_map->pgoff; | 655 | pos->start -= curr_map->start - curr_map->pgoff; |
631 | if (pos->end) | 656 | if (pos->end) |
632 | pos->end -= curr_map->start - curr_map->pgoff; | 657 | pos->end -= curr_map->start - curr_map->pgoff; |
633 | if (curr_map != map) { | 658 | if (curr_map != map) { |
634 | rb_erase(&pos->rb_node, root); | 659 | rb_erase(&pos->rb_node, root); |
635 | symbols__insert( | 660 | symbols__insert( |
636 | &curr_map->dso->symbols[curr_map->type], | 661 | &curr_map->dso->symbols[curr_map->type], |
637 | pos); | 662 | pos); |
638 | ++moved; | 663 | ++moved; |
639 | } else { | 664 | } else { |
640 | ++count; | 665 | ++count; |
641 | } | 666 | } |
642 | } | 667 | } |
643 | } | 668 | } |
644 | 669 | ||
645 | /* Symbols have been adjusted */ | 670 | /* Symbols have been adjusted */ |
646 | dso->adjust_symbols = 1; | 671 | dso->adjust_symbols = 1; |
647 | 672 | ||
648 | return count + moved; | 673 | return count + moved; |
649 | } | 674 | } |
650 | 675 | ||
651 | /* | 676 | /* |
652 | * Split the symbols into maps, making sure there are no overlaps, i.e. the | 677 | * Split the symbols into maps, making sure there are no overlaps, i.e. the |
653 | * kernel range is broken in several maps, named [kernel].N, as we don't have | 678 | * kernel range is broken in several maps, named [kernel].N, as we don't have |
654 | * the original ELF section names vmlinux have. | 679 | * the original ELF section names vmlinux have. |
655 | */ | 680 | */ |
656 | static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta, | 681 | static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta, |
657 | symbol_filter_t filter) | 682 | symbol_filter_t filter) |
658 | { | 683 | { |
659 | struct map_groups *kmaps = map__kmap(map)->kmaps; | 684 | struct map_groups *kmaps = map__kmap(map)->kmaps; |
660 | struct machine *machine = kmaps->machine; | 685 | struct machine *machine = kmaps->machine; |
661 | struct map *curr_map = map; | 686 | struct map *curr_map = map; |
662 | struct symbol *pos; | 687 | struct symbol *pos; |
663 | int count = 0, moved = 0; | 688 | int count = 0, moved = 0; |
664 | struct rb_root *root = &dso->symbols[map->type]; | 689 | struct rb_root *root = &dso->symbols[map->type]; |
665 | struct rb_node *next = rb_first(root); | 690 | struct rb_node *next = rb_first(root); |
666 | int kernel_range = 0; | 691 | int kernel_range = 0; |
667 | 692 | ||
668 | while (next) { | 693 | while (next) { |
669 | char *module; | 694 | char *module; |
670 | 695 | ||
671 | pos = rb_entry(next, struct symbol, rb_node); | 696 | pos = rb_entry(next, struct symbol, rb_node); |
672 | next = rb_next(&pos->rb_node); | 697 | next = rb_next(&pos->rb_node); |
673 | 698 | ||
674 | module = strchr(pos->name, '\t'); | 699 | module = strchr(pos->name, '\t'); |
675 | if (module) { | 700 | if (module) { |
676 | if (!symbol_conf.use_modules) | 701 | if (!symbol_conf.use_modules) |
677 | goto discard_symbol; | 702 | goto discard_symbol; |
678 | 703 | ||
679 | *module++ = '\0'; | 704 | *module++ = '\0'; |
680 | 705 | ||
681 | if (strcmp(curr_map->dso->short_name, module)) { | 706 | if (strcmp(curr_map->dso->short_name, module)) { |
682 | if (curr_map != map && | 707 | if (curr_map != map && |
683 | dso->kernel == DSO_TYPE_GUEST_KERNEL && | 708 | dso->kernel == DSO_TYPE_GUEST_KERNEL && |
684 | machine__is_default_guest(machine)) { | 709 | machine__is_default_guest(machine)) { |
685 | /* | 710 | /* |
686 | * We assume all symbols of a module are | 711 | * We assume all symbols of a module are |
687 | * continuous in * kallsyms, so curr_map | 712 | * continuous in * kallsyms, so curr_map |
688 | * points to a module and all its | 713 | * points to a module and all its |
689 | * symbols are in its kmap. Mark it as | 714 | * symbols are in its kmap. Mark it as |
690 | * loaded. | 715 | * loaded. |
691 | */ | 716 | */ |
692 | dso__set_loaded(curr_map->dso, | 717 | dso__set_loaded(curr_map->dso, |
693 | curr_map->type); | 718 | curr_map->type); |
694 | } | 719 | } |
695 | 720 | ||
696 | curr_map = map_groups__find_by_name(kmaps, | 721 | curr_map = map_groups__find_by_name(kmaps, |
697 | map->type, module); | 722 | map->type, module); |
698 | if (curr_map == NULL) { | 723 | if (curr_map == NULL) { |
699 | pr_debug("%s/proc/{kallsyms,modules} " | 724 | pr_debug("%s/proc/{kallsyms,modules} " |
700 | "inconsistency while looking " | 725 | "inconsistency while looking " |
701 | "for \"%s\" module!\n", | 726 | "for \"%s\" module!\n", |
702 | machine->root_dir, module); | 727 | machine->root_dir, module); |
703 | curr_map = map; | 728 | curr_map = map; |
704 | goto discard_symbol; | 729 | goto discard_symbol; |
705 | } | 730 | } |
706 | 731 | ||
707 | if (curr_map->dso->loaded && | 732 | if (curr_map->dso->loaded && |
708 | !machine__is_default_guest(machine)) | 733 | !machine__is_default_guest(machine)) |
709 | goto discard_symbol; | 734 | goto discard_symbol; |
710 | } | 735 | } |
711 | /* | 736 | /* |
712 | * So that we look just like we get from .ko files, | 737 | * So that we look just like we get from .ko files, |
713 | * i.e. not prelinked, relative to map->start. | 738 | * i.e. not prelinked, relative to map->start. |
714 | */ | 739 | */ |
715 | pos->start = curr_map->map_ip(curr_map, pos->start); | 740 | pos->start = curr_map->map_ip(curr_map, pos->start); |
716 | pos->end = curr_map->map_ip(curr_map, pos->end); | 741 | pos->end = curr_map->map_ip(curr_map, pos->end); |
717 | } else if (curr_map != map) { | 742 | } else if (curr_map != map) { |
718 | char dso_name[PATH_MAX]; | 743 | char dso_name[PATH_MAX]; |
719 | struct dso *ndso; | 744 | struct dso *ndso; |
720 | 745 | ||
721 | if (delta) { | 746 | if (delta) { |
722 | /* Kernel was relocated at boot time */ | 747 | /* Kernel was relocated at boot time */ |
723 | pos->start -= delta; | 748 | pos->start -= delta; |
724 | pos->end -= delta; | 749 | pos->end -= delta; |
725 | } | 750 | } |
726 | 751 | ||
727 | if (count == 0) { | 752 | if (count == 0) { |
728 | curr_map = map; | 753 | curr_map = map; |
729 | goto filter_symbol; | 754 | goto filter_symbol; |
730 | } | 755 | } |
731 | 756 | ||
732 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 757 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
733 | snprintf(dso_name, sizeof(dso_name), | 758 | snprintf(dso_name, sizeof(dso_name), |
734 | "[guest.kernel].%d", | 759 | "[guest.kernel].%d", |
735 | kernel_range++); | 760 | kernel_range++); |
736 | else | 761 | else |
737 | snprintf(dso_name, sizeof(dso_name), | 762 | snprintf(dso_name, sizeof(dso_name), |
738 | "[kernel].%d", | 763 | "[kernel].%d", |
739 | kernel_range++); | 764 | kernel_range++); |
740 | 765 | ||
741 | ndso = dso__new(dso_name); | 766 | ndso = dso__new(dso_name); |
742 | if (ndso == NULL) | 767 | if (ndso == NULL) |
743 | return -1; | 768 | return -1; |
744 | 769 | ||
745 | ndso->kernel = dso->kernel; | 770 | ndso->kernel = dso->kernel; |
746 | 771 | ||
747 | curr_map = map__new2(pos->start, ndso, map->type); | 772 | curr_map = map__new2(pos->start, ndso, map->type); |
748 | if (curr_map == NULL) { | 773 | if (curr_map == NULL) { |
749 | dso__delete(ndso); | 774 | dso__delete(ndso); |
750 | return -1; | 775 | return -1; |
751 | } | 776 | } |
752 | 777 | ||
753 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; | 778 | curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; |
754 | map_groups__insert(kmaps, curr_map); | 779 | map_groups__insert(kmaps, curr_map); |
755 | ++kernel_range; | 780 | ++kernel_range; |
756 | } else if (delta) { | 781 | } else if (delta) { |
757 | /* Kernel was relocated at boot time */ | 782 | /* Kernel was relocated at boot time */ |
758 | pos->start -= delta; | 783 | pos->start -= delta; |
759 | pos->end -= delta; | 784 | pos->end -= delta; |
760 | } | 785 | } |
761 | filter_symbol: | 786 | filter_symbol: |
762 | if (filter && filter(curr_map, pos)) { | 787 | if (filter && filter(curr_map, pos)) { |
763 | discard_symbol: rb_erase(&pos->rb_node, root); | 788 | discard_symbol: rb_erase(&pos->rb_node, root); |
764 | symbol__delete(pos); | 789 | symbol__delete(pos); |
765 | } else { | 790 | } else { |
766 | if (curr_map != map) { | 791 | if (curr_map != map) { |
767 | rb_erase(&pos->rb_node, root); | 792 | rb_erase(&pos->rb_node, root); |
768 | symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); | 793 | symbols__insert(&curr_map->dso->symbols[curr_map->type], pos); |
769 | ++moved; | 794 | ++moved; |
770 | } else | 795 | } else |
771 | ++count; | 796 | ++count; |
772 | } | 797 | } |
773 | } | 798 | } |
774 | 799 | ||
775 | if (curr_map != map && | 800 | if (curr_map != map && |
776 | dso->kernel == DSO_TYPE_GUEST_KERNEL && | 801 | dso->kernel == DSO_TYPE_GUEST_KERNEL && |
777 | machine__is_default_guest(kmaps->machine)) { | 802 | machine__is_default_guest(kmaps->machine)) { |
778 | dso__set_loaded(curr_map->dso, curr_map->type); | 803 | dso__set_loaded(curr_map->dso, curr_map->type); |
779 | } | 804 | } |
780 | 805 | ||
781 | return count + moved; | 806 | return count + moved; |
782 | } | 807 | } |
783 | 808 | ||
784 | bool symbol__restricted_filename(const char *filename, | 809 | bool symbol__restricted_filename(const char *filename, |
785 | const char *restricted_filename) | 810 | const char *restricted_filename) |
786 | { | 811 | { |
787 | bool restricted = false; | 812 | bool restricted = false; |
788 | 813 | ||
789 | if (symbol_conf.kptr_restrict) { | 814 | if (symbol_conf.kptr_restrict) { |
790 | char *r = realpath(filename, NULL); | 815 | char *r = realpath(filename, NULL); |
791 | 816 | ||
792 | if (r != NULL) { | 817 | if (r != NULL) { |
793 | restricted = strcmp(r, restricted_filename) == 0; | 818 | restricted = strcmp(r, restricted_filename) == 0; |
794 | free(r); | 819 | free(r); |
795 | return restricted; | 820 | return restricted; |
796 | } | 821 | } |
797 | } | 822 | } |
798 | 823 | ||
799 | return restricted; | 824 | return restricted; |
800 | } | 825 | } |
801 | 826 | ||
802 | struct module_info { | 827 | struct module_info { |
803 | struct rb_node rb_node; | 828 | struct rb_node rb_node; |
804 | char *name; | 829 | char *name; |
805 | u64 start; | 830 | u64 start; |
806 | }; | 831 | }; |
807 | 832 | ||
808 | static void add_module(struct module_info *mi, struct rb_root *modules) | 833 | static void add_module(struct module_info *mi, struct rb_root *modules) |
809 | { | 834 | { |
810 | struct rb_node **p = &modules->rb_node; | 835 | struct rb_node **p = &modules->rb_node; |
811 | struct rb_node *parent = NULL; | 836 | struct rb_node *parent = NULL; |
812 | struct module_info *m; | 837 | struct module_info *m; |
813 | 838 | ||
814 | while (*p != NULL) { | 839 | while (*p != NULL) { |
815 | parent = *p; | 840 | parent = *p; |
816 | m = rb_entry(parent, struct module_info, rb_node); | 841 | m = rb_entry(parent, struct module_info, rb_node); |
817 | if (strcmp(mi->name, m->name) < 0) | 842 | if (strcmp(mi->name, m->name) < 0) |
818 | p = &(*p)->rb_left; | 843 | p = &(*p)->rb_left; |
819 | else | 844 | else |
820 | p = &(*p)->rb_right; | 845 | p = &(*p)->rb_right; |
821 | } | 846 | } |
822 | rb_link_node(&mi->rb_node, parent, p); | 847 | rb_link_node(&mi->rb_node, parent, p); |
823 | rb_insert_color(&mi->rb_node, modules); | 848 | rb_insert_color(&mi->rb_node, modules); |
824 | } | 849 | } |
825 | 850 | ||
826 | static void delete_modules(struct rb_root *modules) | 851 | static void delete_modules(struct rb_root *modules) |
827 | { | 852 | { |
828 | struct module_info *mi; | 853 | struct module_info *mi; |
829 | struct rb_node *next = rb_first(modules); | 854 | struct rb_node *next = rb_first(modules); |
830 | 855 | ||
831 | while (next) { | 856 | while (next) { |
832 | mi = rb_entry(next, struct module_info, rb_node); | 857 | mi = rb_entry(next, struct module_info, rb_node); |
833 | next = rb_next(&mi->rb_node); | 858 | next = rb_next(&mi->rb_node); |
834 | rb_erase(&mi->rb_node, modules); | 859 | rb_erase(&mi->rb_node, modules); |
835 | zfree(&mi->name); | 860 | zfree(&mi->name); |
836 | free(mi); | 861 | free(mi); |
837 | } | 862 | } |
838 | } | 863 | } |
839 | 864 | ||
840 | static struct module_info *find_module(const char *name, | 865 | static struct module_info *find_module(const char *name, |
841 | struct rb_root *modules) | 866 | struct rb_root *modules) |
842 | { | 867 | { |
843 | struct rb_node *n = modules->rb_node; | 868 | struct rb_node *n = modules->rb_node; |
844 | 869 | ||
845 | while (n) { | 870 | while (n) { |
846 | struct module_info *m; | 871 | struct module_info *m; |
847 | int cmp; | 872 | int cmp; |
848 | 873 | ||
849 | m = rb_entry(n, struct module_info, rb_node); | 874 | m = rb_entry(n, struct module_info, rb_node); |
850 | cmp = strcmp(name, m->name); | 875 | cmp = strcmp(name, m->name); |
851 | if (cmp < 0) | 876 | if (cmp < 0) |
852 | n = n->rb_left; | 877 | n = n->rb_left; |
853 | else if (cmp > 0) | 878 | else if (cmp > 0) |
854 | n = n->rb_right; | 879 | n = n->rb_right; |
855 | else | 880 | else |
856 | return m; | 881 | return m; |
857 | } | 882 | } |
858 | 883 | ||
859 | return NULL; | 884 | return NULL; |
860 | } | 885 | } |
861 | 886 | ||
862 | static int __read_proc_modules(void *arg, const char *name, u64 start) | 887 | static int __read_proc_modules(void *arg, const char *name, u64 start) |
863 | { | 888 | { |
864 | struct rb_root *modules = arg; | 889 | struct rb_root *modules = arg; |
865 | struct module_info *mi; | 890 | struct module_info *mi; |
866 | 891 | ||
867 | mi = zalloc(sizeof(struct module_info)); | 892 | mi = zalloc(sizeof(struct module_info)); |
868 | if (!mi) | 893 | if (!mi) |
869 | return -ENOMEM; | 894 | return -ENOMEM; |
870 | 895 | ||
871 | mi->name = strdup(name); | 896 | mi->name = strdup(name); |
872 | mi->start = start; | 897 | mi->start = start; |
873 | 898 | ||
874 | if (!mi->name) { | 899 | if (!mi->name) { |
875 | free(mi); | 900 | free(mi); |
876 | return -ENOMEM; | 901 | return -ENOMEM; |
877 | } | 902 | } |
878 | 903 | ||
879 | add_module(mi, modules); | 904 | add_module(mi, modules); |
880 | 905 | ||
881 | return 0; | 906 | return 0; |
882 | } | 907 | } |
883 | 908 | ||
884 | static int read_proc_modules(const char *filename, struct rb_root *modules) | 909 | static int read_proc_modules(const char *filename, struct rb_root *modules) |
885 | { | 910 | { |
886 | if (symbol__restricted_filename(filename, "/proc/modules")) | 911 | if (symbol__restricted_filename(filename, "/proc/modules")) |
887 | return -1; | 912 | return -1; |
888 | 913 | ||
889 | if (modules__parse(filename, modules, __read_proc_modules)) { | 914 | if (modules__parse(filename, modules, __read_proc_modules)) { |
890 | delete_modules(modules); | 915 | delete_modules(modules); |
891 | return -1; | 916 | return -1; |
892 | } | 917 | } |
893 | 918 | ||
894 | return 0; | 919 | return 0; |
895 | } | 920 | } |
896 | 921 | ||
897 | int compare_proc_modules(const char *from, const char *to) | 922 | int compare_proc_modules(const char *from, const char *to) |
898 | { | 923 | { |
899 | struct rb_root from_modules = RB_ROOT; | 924 | struct rb_root from_modules = RB_ROOT; |
900 | struct rb_root to_modules = RB_ROOT; | 925 | struct rb_root to_modules = RB_ROOT; |
901 | struct rb_node *from_node, *to_node; | 926 | struct rb_node *from_node, *to_node; |
902 | struct module_info *from_m, *to_m; | 927 | struct module_info *from_m, *to_m; |
903 | int ret = -1; | 928 | int ret = -1; |
904 | 929 | ||
905 | if (read_proc_modules(from, &from_modules)) | 930 | if (read_proc_modules(from, &from_modules)) |
906 | return -1; | 931 | return -1; |
907 | 932 | ||
908 | if (read_proc_modules(to, &to_modules)) | 933 | if (read_proc_modules(to, &to_modules)) |
909 | goto out_delete_from; | 934 | goto out_delete_from; |
910 | 935 | ||
911 | from_node = rb_first(&from_modules); | 936 | from_node = rb_first(&from_modules); |
912 | to_node = rb_first(&to_modules); | 937 | to_node = rb_first(&to_modules); |
913 | while (from_node) { | 938 | while (from_node) { |
914 | if (!to_node) | 939 | if (!to_node) |
915 | break; | 940 | break; |
916 | 941 | ||
917 | from_m = rb_entry(from_node, struct module_info, rb_node); | 942 | from_m = rb_entry(from_node, struct module_info, rb_node); |
918 | to_m = rb_entry(to_node, struct module_info, rb_node); | 943 | to_m = rb_entry(to_node, struct module_info, rb_node); |
919 | 944 | ||
920 | if (from_m->start != to_m->start || | 945 | if (from_m->start != to_m->start || |
921 | strcmp(from_m->name, to_m->name)) | 946 | strcmp(from_m->name, to_m->name)) |
922 | break; | 947 | break; |
923 | 948 | ||
924 | from_node = rb_next(from_node); | 949 | from_node = rb_next(from_node); |
925 | to_node = rb_next(to_node); | 950 | to_node = rb_next(to_node); |
926 | } | 951 | } |
927 | 952 | ||
928 | if (!from_node && !to_node) | 953 | if (!from_node && !to_node) |
929 | ret = 0; | 954 | ret = 0; |
930 | 955 | ||
931 | delete_modules(&to_modules); | 956 | delete_modules(&to_modules); |
932 | out_delete_from: | 957 | out_delete_from: |
933 | delete_modules(&from_modules); | 958 | delete_modules(&from_modules); |
934 | 959 | ||
935 | return ret; | 960 | return ret; |
936 | } | 961 | } |
937 | 962 | ||
938 | static int do_validate_kcore_modules(const char *filename, struct map *map, | 963 | static int do_validate_kcore_modules(const char *filename, struct map *map, |
939 | struct map_groups *kmaps) | 964 | struct map_groups *kmaps) |
940 | { | 965 | { |
941 | struct rb_root modules = RB_ROOT; | 966 | struct rb_root modules = RB_ROOT; |
942 | struct map *old_map; | 967 | struct map *old_map; |
943 | int err; | 968 | int err; |
944 | 969 | ||
945 | err = read_proc_modules(filename, &modules); | 970 | err = read_proc_modules(filename, &modules); |
946 | if (err) | 971 | if (err) |
947 | return err; | 972 | return err; |
948 | 973 | ||
949 | old_map = map_groups__first(kmaps, map->type); | 974 | old_map = map_groups__first(kmaps, map->type); |
950 | while (old_map) { | 975 | while (old_map) { |
951 | struct map *next = map_groups__next(old_map); | 976 | struct map *next = map_groups__next(old_map); |
952 | struct module_info *mi; | 977 | struct module_info *mi; |
953 | 978 | ||
954 | if (old_map == map || old_map->start == map->start) { | 979 | if (old_map == map || old_map->start == map->start) { |
955 | /* The kernel map */ | 980 | /* The kernel map */ |
956 | old_map = next; | 981 | old_map = next; |
957 | continue; | 982 | continue; |
958 | } | 983 | } |
959 | 984 | ||
960 | /* Module must be in memory at the same address */ | 985 | /* Module must be in memory at the same address */ |
961 | mi = find_module(old_map->dso->short_name, &modules); | 986 | mi = find_module(old_map->dso->short_name, &modules); |
962 | if (!mi || mi->start != old_map->start) { | 987 | if (!mi || mi->start != old_map->start) { |
963 | err = -EINVAL; | 988 | err = -EINVAL; |
964 | goto out; | 989 | goto out; |
965 | } | 990 | } |
966 | 991 | ||
967 | old_map = next; | 992 | old_map = next; |
968 | } | 993 | } |
969 | out: | 994 | out: |
970 | delete_modules(&modules); | 995 | delete_modules(&modules); |
971 | return err; | 996 | return err; |
972 | } | 997 | } |
973 | 998 | ||
974 | /* | 999 | /* |
975 | * If kallsyms is referenced by name then we look for filename in the same | 1000 | * If kallsyms is referenced by name then we look for filename in the same |
976 | * directory. | 1001 | * directory. |
977 | */ | 1002 | */ |
978 | static bool filename_from_kallsyms_filename(char *filename, | 1003 | static bool filename_from_kallsyms_filename(char *filename, |
979 | const char *base_name, | 1004 | const char *base_name, |
980 | const char *kallsyms_filename) | 1005 | const char *kallsyms_filename) |
981 | { | 1006 | { |
982 | char *name; | 1007 | char *name; |
983 | 1008 | ||
984 | strcpy(filename, kallsyms_filename); | 1009 | strcpy(filename, kallsyms_filename); |
985 | name = strrchr(filename, '/'); | 1010 | name = strrchr(filename, '/'); |
986 | if (!name) | 1011 | if (!name) |
987 | return false; | 1012 | return false; |
988 | 1013 | ||
989 | name += 1; | 1014 | name += 1; |
990 | 1015 | ||
991 | if (!strcmp(name, "kallsyms")) { | 1016 | if (!strcmp(name, "kallsyms")) { |
992 | strcpy(name, base_name); | 1017 | strcpy(name, base_name); |
993 | return true; | 1018 | return true; |
994 | } | 1019 | } |
995 | 1020 | ||
996 | return false; | 1021 | return false; |
997 | } | 1022 | } |
998 | 1023 | ||
999 | static int validate_kcore_modules(const char *kallsyms_filename, | 1024 | static int validate_kcore_modules(const char *kallsyms_filename, |
1000 | struct map *map) | 1025 | struct map *map) |
1001 | { | 1026 | { |
1002 | struct map_groups *kmaps = map__kmap(map)->kmaps; | 1027 | struct map_groups *kmaps = map__kmap(map)->kmaps; |
1003 | char modules_filename[PATH_MAX]; | 1028 | char modules_filename[PATH_MAX]; |
1004 | 1029 | ||
1005 | if (!filename_from_kallsyms_filename(modules_filename, "modules", | 1030 | if (!filename_from_kallsyms_filename(modules_filename, "modules", |
1006 | kallsyms_filename)) | 1031 | kallsyms_filename)) |
1007 | return -EINVAL; | 1032 | return -EINVAL; |
1008 | 1033 | ||
1009 | if (do_validate_kcore_modules(modules_filename, map, kmaps)) | 1034 | if (do_validate_kcore_modules(modules_filename, map, kmaps)) |
1010 | return -EINVAL; | 1035 | return -EINVAL; |
1011 | 1036 | ||
1012 | return 0; | 1037 | return 0; |
1013 | } | 1038 | } |
1014 | 1039 | ||
1015 | static int validate_kcore_addresses(const char *kallsyms_filename, | 1040 | static int validate_kcore_addresses(const char *kallsyms_filename, |
1016 | struct map *map) | 1041 | struct map *map) |
1017 | { | 1042 | { |
1018 | struct kmap *kmap = map__kmap(map); | 1043 | struct kmap *kmap = map__kmap(map); |
1019 | 1044 | ||
1020 | if (kmap->ref_reloc_sym && kmap->ref_reloc_sym->name) { | 1045 | if (kmap->ref_reloc_sym && kmap->ref_reloc_sym->name) { |
1021 | u64 start; | 1046 | u64 start; |
1022 | 1047 | ||
1023 | start = kallsyms__get_function_start(kallsyms_filename, | 1048 | start = kallsyms__get_function_start(kallsyms_filename, |
1024 | kmap->ref_reloc_sym->name); | 1049 | kmap->ref_reloc_sym->name); |
1025 | if (start != kmap->ref_reloc_sym->addr) | 1050 | if (start != kmap->ref_reloc_sym->addr) |
1026 | return -EINVAL; | 1051 | return -EINVAL; |
1027 | } | 1052 | } |
1028 | 1053 | ||
1029 | return validate_kcore_modules(kallsyms_filename, map); | 1054 | return validate_kcore_modules(kallsyms_filename, map); |
1030 | } | 1055 | } |
1031 | 1056 | ||
1032 | struct kcore_mapfn_data { | 1057 | struct kcore_mapfn_data { |
1033 | struct dso *dso; | 1058 | struct dso *dso; |
1034 | enum map_type type; | 1059 | enum map_type type; |
1035 | struct list_head maps; | 1060 | struct list_head maps; |
1036 | }; | 1061 | }; |
1037 | 1062 | ||
1038 | static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) | 1063 | static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) |
1039 | { | 1064 | { |
1040 | struct kcore_mapfn_data *md = data; | 1065 | struct kcore_mapfn_data *md = data; |
1041 | struct map *map; | 1066 | struct map *map; |
1042 | 1067 | ||
1043 | map = map__new2(start, md->dso, md->type); | 1068 | map = map__new2(start, md->dso, md->type); |
1044 | if (map == NULL) | 1069 | if (map == NULL) |
1045 | return -ENOMEM; | 1070 | return -ENOMEM; |
1046 | 1071 | ||
1047 | map->end = map->start + len; | 1072 | map->end = map->start + len; |
1048 | map->pgoff = pgoff; | 1073 | map->pgoff = pgoff; |
1049 | 1074 | ||
1050 | list_add(&map->node, &md->maps); | 1075 | list_add(&map->node, &md->maps); |
1051 | 1076 | ||
1052 | return 0; | 1077 | return 0; |
1053 | } | 1078 | } |
1054 | 1079 | ||
1055 | static int dso__load_kcore(struct dso *dso, struct map *map, | 1080 | static int dso__load_kcore(struct dso *dso, struct map *map, |
1056 | const char *kallsyms_filename) | 1081 | const char *kallsyms_filename) |
1057 | { | 1082 | { |
1058 | struct map_groups *kmaps = map__kmap(map)->kmaps; | 1083 | struct map_groups *kmaps = map__kmap(map)->kmaps; |
1059 | struct machine *machine = kmaps->machine; | 1084 | struct machine *machine = kmaps->machine; |
1060 | struct kcore_mapfn_data md; | 1085 | struct kcore_mapfn_data md; |
1061 | struct map *old_map, *new_map, *replacement_map = NULL; | 1086 | struct map *old_map, *new_map, *replacement_map = NULL; |
1062 | bool is_64_bit; | 1087 | bool is_64_bit; |
1063 | int err, fd; | 1088 | int err, fd; |
1064 | char kcore_filename[PATH_MAX]; | 1089 | char kcore_filename[PATH_MAX]; |
1065 | struct symbol *sym; | 1090 | struct symbol *sym; |
1066 | 1091 | ||
1067 | /* This function requires that the map is the kernel map */ | 1092 | /* This function requires that the map is the kernel map */ |
1068 | if (map != machine->vmlinux_maps[map->type]) | 1093 | if (map != machine->vmlinux_maps[map->type]) |
1069 | return -EINVAL; | 1094 | return -EINVAL; |
1070 | 1095 | ||
1071 | if (!filename_from_kallsyms_filename(kcore_filename, "kcore", | 1096 | if (!filename_from_kallsyms_filename(kcore_filename, "kcore", |
1072 | kallsyms_filename)) | 1097 | kallsyms_filename)) |
1073 | return -EINVAL; | 1098 | return -EINVAL; |
1074 | 1099 | ||
1075 | /* Modules and kernel must be present at their original addresses */ | 1100 | /* Modules and kernel must be present at their original addresses */ |
1076 | if (validate_kcore_addresses(kallsyms_filename, map)) | 1101 | if (validate_kcore_addresses(kallsyms_filename, map)) |
1077 | return -EINVAL; | 1102 | return -EINVAL; |
1078 | 1103 | ||
1079 | md.dso = dso; | 1104 | md.dso = dso; |
1080 | md.type = map->type; | 1105 | md.type = map->type; |
1081 | INIT_LIST_HEAD(&md.maps); | 1106 | INIT_LIST_HEAD(&md.maps); |
1082 | 1107 | ||
1083 | fd = open(kcore_filename, O_RDONLY); | 1108 | fd = open(kcore_filename, O_RDONLY); |
1084 | if (fd < 0) | 1109 | if (fd < 0) |
1085 | return -EINVAL; | 1110 | return -EINVAL; |
1086 | 1111 | ||
1087 | /* Read new maps into temporary lists */ | 1112 | /* Read new maps into temporary lists */ |
1088 | err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md, | 1113 | err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md, |
1089 | &is_64_bit); | 1114 | &is_64_bit); |
1090 | if (err) | 1115 | if (err) |
1091 | goto out_err; | 1116 | goto out_err; |
1092 | dso->is_64_bit = is_64_bit; | 1117 | dso->is_64_bit = is_64_bit; |
1093 | 1118 | ||
1094 | if (list_empty(&md.maps)) { | 1119 | if (list_empty(&md.maps)) { |
1095 | err = -EINVAL; | 1120 | err = -EINVAL; |
1096 | goto out_err; | 1121 | goto out_err; |
1097 | } | 1122 | } |
1098 | 1123 | ||
1099 | /* Remove old maps */ | 1124 | /* Remove old maps */ |
1100 | old_map = map_groups__first(kmaps, map->type); | 1125 | old_map = map_groups__first(kmaps, map->type); |
1101 | while (old_map) { | 1126 | while (old_map) { |
1102 | struct map *next = map_groups__next(old_map); | 1127 | struct map *next = map_groups__next(old_map); |
1103 | 1128 | ||
1104 | if (old_map != map) | 1129 | if (old_map != map) |
1105 | map_groups__remove(kmaps, old_map); | 1130 | map_groups__remove(kmaps, old_map); |
1106 | old_map = next; | 1131 | old_map = next; |
1107 | } | 1132 | } |
1108 | 1133 | ||
1109 | /* Find the kernel map using the first symbol */ | 1134 | /* Find the kernel map using the first symbol */ |
1110 | sym = dso__first_symbol(dso, map->type); | 1135 | sym = dso__first_symbol(dso, map->type); |
1111 | list_for_each_entry(new_map, &md.maps, node) { | 1136 | list_for_each_entry(new_map, &md.maps, node) { |
1112 | if (sym && sym->start >= new_map->start && | 1137 | if (sym && sym->start >= new_map->start && |
1113 | sym->start < new_map->end) { | 1138 | sym->start < new_map->end) { |
1114 | replacement_map = new_map; | 1139 | replacement_map = new_map; |
1115 | break; | 1140 | break; |
1116 | } | 1141 | } |
1117 | } | 1142 | } |
1118 | 1143 | ||
1119 | if (!replacement_map) | 1144 | if (!replacement_map) |
1120 | replacement_map = list_entry(md.maps.next, struct map, node); | 1145 | replacement_map = list_entry(md.maps.next, struct map, node); |
1121 | 1146 | ||
1122 | /* Add new maps */ | 1147 | /* Add new maps */ |
1123 | while (!list_empty(&md.maps)) { | 1148 | while (!list_empty(&md.maps)) { |
1124 | new_map = list_entry(md.maps.next, struct map, node); | 1149 | new_map = list_entry(md.maps.next, struct map, node); |
1125 | list_del(&new_map->node); | 1150 | list_del(&new_map->node); |
1126 | if (new_map == replacement_map) { | 1151 | if (new_map == replacement_map) { |
1127 | map->start = new_map->start; | 1152 | map->start = new_map->start; |
1128 | map->end = new_map->end; | 1153 | map->end = new_map->end; |
1129 | map->pgoff = new_map->pgoff; | 1154 | map->pgoff = new_map->pgoff; |
1130 | map->map_ip = new_map->map_ip; | 1155 | map->map_ip = new_map->map_ip; |
1131 | map->unmap_ip = new_map->unmap_ip; | 1156 | map->unmap_ip = new_map->unmap_ip; |
1132 | map__delete(new_map); | 1157 | map__delete(new_map); |
1133 | /* Ensure maps are correctly ordered */ | 1158 | /* Ensure maps are correctly ordered */ |
1134 | map_groups__remove(kmaps, map); | 1159 | map_groups__remove(kmaps, map); |
1135 | map_groups__insert(kmaps, map); | 1160 | map_groups__insert(kmaps, map); |
1136 | } else { | 1161 | } else { |
1137 | map_groups__insert(kmaps, new_map); | 1162 | map_groups__insert(kmaps, new_map); |
1138 | } | 1163 | } |
1139 | } | 1164 | } |
1140 | 1165 | ||
1141 | /* | 1166 | /* |
1142 | * Set the data type and long name so that kcore can be read via | 1167 | * Set the data type and long name so that kcore can be read via |
1143 | * dso__data_read_addr(). | 1168 | * dso__data_read_addr(). |
1144 | */ | 1169 | */ |
1145 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1170 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
1146 | dso->binary_type = DSO_BINARY_TYPE__GUEST_KCORE; | 1171 | dso->binary_type = DSO_BINARY_TYPE__GUEST_KCORE; |
1147 | else | 1172 | else |
1148 | dso->binary_type = DSO_BINARY_TYPE__KCORE; | 1173 | dso->binary_type = DSO_BINARY_TYPE__KCORE; |
1149 | dso__set_long_name(dso, strdup(kcore_filename), true); | 1174 | dso__set_long_name(dso, strdup(kcore_filename), true); |
1150 | 1175 | ||
1151 | close(fd); | 1176 | close(fd); |
1152 | 1177 | ||
1153 | if (map->type == MAP__FUNCTION) | 1178 | if (map->type == MAP__FUNCTION) |
1154 | pr_debug("Using %s for kernel object code\n", kcore_filename); | 1179 | pr_debug("Using %s for kernel object code\n", kcore_filename); |
1155 | else | 1180 | else |
1156 | pr_debug("Using %s for kernel data\n", kcore_filename); | 1181 | pr_debug("Using %s for kernel data\n", kcore_filename); |
1157 | 1182 | ||
1158 | return 0; | 1183 | return 0; |
1159 | 1184 | ||
1160 | out_err: | 1185 | out_err: |
1161 | while (!list_empty(&md.maps)) { | 1186 | while (!list_empty(&md.maps)) { |
1162 | map = list_entry(md.maps.next, struct map, node); | 1187 | map = list_entry(md.maps.next, struct map, node); |
1163 | list_del(&map->node); | 1188 | list_del(&map->node); |
1164 | map__delete(map); | 1189 | map__delete(map); |
1165 | } | 1190 | } |
1166 | close(fd); | 1191 | close(fd); |
1167 | return -EINVAL; | 1192 | return -EINVAL; |
1168 | } | 1193 | } |
1169 | 1194 | ||
1170 | /* | 1195 | /* |
1171 | * If the kernel is relocated at boot time, kallsyms won't match. Compute the | 1196 | * If the kernel is relocated at boot time, kallsyms won't match. Compute the |
1172 | * delta based on the relocation reference symbol. | 1197 | * delta based on the relocation reference symbol. |
1173 | */ | 1198 | */ |
1174 | static int kallsyms__delta(struct map *map, const char *filename, u64 *delta) | 1199 | static int kallsyms__delta(struct map *map, const char *filename, u64 *delta) |
1175 | { | 1200 | { |
1176 | struct kmap *kmap = map__kmap(map); | 1201 | struct kmap *kmap = map__kmap(map); |
1177 | u64 addr; | 1202 | u64 addr; |
1178 | 1203 | ||
1179 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->name) | 1204 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->name) |
1180 | return 0; | 1205 | return 0; |
1181 | 1206 | ||
1182 | addr = kallsyms__get_function_start(filename, | 1207 | addr = kallsyms__get_function_start(filename, |
1183 | kmap->ref_reloc_sym->name); | 1208 | kmap->ref_reloc_sym->name); |
1184 | if (!addr) | 1209 | if (!addr) |
1185 | return -1; | 1210 | return -1; |
1186 | 1211 | ||
1187 | *delta = addr - kmap->ref_reloc_sym->addr; | 1212 | *delta = addr - kmap->ref_reloc_sym->addr; |
1188 | return 0; | 1213 | return 0; |
1189 | } | 1214 | } |
1190 | 1215 | ||
1191 | int dso__load_kallsyms(struct dso *dso, const char *filename, | 1216 | int dso__load_kallsyms(struct dso *dso, const char *filename, |
1192 | struct map *map, symbol_filter_t filter) | 1217 | struct map *map, symbol_filter_t filter) |
1193 | { | 1218 | { |
1194 | u64 delta = 0; | 1219 | u64 delta = 0; |
1195 | 1220 | ||
1196 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) | 1221 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) |
1197 | return -1; | 1222 | return -1; |
1198 | 1223 | ||
1199 | if (dso__load_all_kallsyms(dso, filename, map) < 0) | 1224 | if (dso__load_all_kallsyms(dso, filename, map) < 0) |
1200 | return -1; | 1225 | return -1; |
1201 | 1226 | ||
1202 | if (kallsyms__delta(map, filename, &delta)) | 1227 | if (kallsyms__delta(map, filename, &delta)) |
1203 | return -1; | 1228 | return -1; |
1204 | 1229 | ||
1205 | symbols__fixup_duplicate(&dso->symbols[map->type]); | 1230 | symbols__fixup_duplicate(&dso->symbols[map->type]); |
1206 | symbols__fixup_end(&dso->symbols[map->type]); | 1231 | symbols__fixup_end(&dso->symbols[map->type]); |
1207 | 1232 | ||
1208 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1233 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
1209 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KALLSYMS; | 1234 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KALLSYMS; |
1210 | else | 1235 | else |
1211 | dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS; | 1236 | dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS; |
1212 | 1237 | ||
1213 | if (!dso__load_kcore(dso, map, filename)) | 1238 | if (!dso__load_kcore(dso, map, filename)) |
1214 | return dso__split_kallsyms_for_kcore(dso, map, filter); | 1239 | return dso__split_kallsyms_for_kcore(dso, map, filter); |
1215 | else | 1240 | else |
1216 | return dso__split_kallsyms(dso, map, delta, filter); | 1241 | return dso__split_kallsyms(dso, map, delta, filter); |
1217 | } | 1242 | } |
1218 | 1243 | ||
1219 | static int dso__load_perf_map(struct dso *dso, struct map *map, | 1244 | static int dso__load_perf_map(struct dso *dso, struct map *map, |
1220 | symbol_filter_t filter) | 1245 | symbol_filter_t filter) |
1221 | { | 1246 | { |
1222 | char *line = NULL; | 1247 | char *line = NULL; |
1223 | size_t n; | 1248 | size_t n; |
1224 | FILE *file; | 1249 | FILE *file; |
1225 | int nr_syms = 0; | 1250 | int nr_syms = 0; |
1226 | 1251 | ||
1227 | file = fopen(dso->long_name, "r"); | 1252 | file = fopen(dso->long_name, "r"); |
1228 | if (file == NULL) | 1253 | if (file == NULL) |
1229 | goto out_failure; | 1254 | goto out_failure; |
1230 | 1255 | ||
1231 | while (!feof(file)) { | 1256 | while (!feof(file)) { |
1232 | u64 start, size; | 1257 | u64 start, size; |
1233 | struct symbol *sym; | 1258 | struct symbol *sym; |
1234 | int line_len, len; | 1259 | int line_len, len; |
1235 | 1260 | ||
1236 | line_len = getline(&line, &n, file); | 1261 | line_len = getline(&line, &n, file); |
1237 | if (line_len < 0) | 1262 | if (line_len < 0) |
1238 | break; | 1263 | break; |
1239 | 1264 | ||
1240 | if (!line) | 1265 | if (!line) |
1241 | goto out_failure; | 1266 | goto out_failure; |
1242 | 1267 | ||
1243 | line[--line_len] = '\0'; /* \n */ | 1268 | line[--line_len] = '\0'; /* \n */ |
1244 | 1269 | ||
1245 | len = hex2u64(line, &start); | 1270 | len = hex2u64(line, &start); |
1246 | 1271 | ||
1247 | len++; | 1272 | len++; |
1248 | if (len + 2 >= line_len) | 1273 | if (len + 2 >= line_len) |
1249 | continue; | 1274 | continue; |
1250 | 1275 | ||
1251 | len += hex2u64(line + len, &size); | 1276 | len += hex2u64(line + len, &size); |
1252 | 1277 | ||
1253 | len++; | 1278 | len++; |
1254 | if (len + 2 >= line_len) | 1279 | if (len + 2 >= line_len) |
1255 | continue; | 1280 | continue; |
1256 | 1281 | ||
1257 | sym = symbol__new(start, size, STB_GLOBAL, line + len); | 1282 | sym = symbol__new(start, size, STB_GLOBAL, line + len); |
1258 | 1283 | ||
1259 | if (sym == NULL) | 1284 | if (sym == NULL) |
1260 | goto out_delete_line; | 1285 | goto out_delete_line; |
1261 | 1286 | ||
1262 | if (filter && filter(map, sym)) | 1287 | if (filter && filter(map, sym)) |
1263 | symbol__delete(sym); | 1288 | symbol__delete(sym); |
1264 | else { | 1289 | else { |
1265 | symbols__insert(&dso->symbols[map->type], sym); | 1290 | symbols__insert(&dso->symbols[map->type], sym); |
1266 | nr_syms++; | 1291 | nr_syms++; |
1267 | } | 1292 | } |
1268 | } | 1293 | } |
1269 | 1294 | ||
1270 | free(line); | 1295 | free(line); |
1271 | fclose(file); | 1296 | fclose(file); |
1272 | 1297 | ||
1273 | return nr_syms; | 1298 | return nr_syms; |
1274 | 1299 | ||
1275 | out_delete_line: | 1300 | out_delete_line: |
1276 | free(line); | 1301 | free(line); |
1277 | out_failure: | 1302 | out_failure: |
1278 | return -1; | 1303 | return -1; |
1279 | } | 1304 | } |
1280 | 1305 | ||
1281 | static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, | 1306 | static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod, |
1282 | enum dso_binary_type type) | 1307 | enum dso_binary_type type) |
1283 | { | 1308 | { |
1284 | switch (type) { | 1309 | switch (type) { |
1285 | case DSO_BINARY_TYPE__JAVA_JIT: | 1310 | case DSO_BINARY_TYPE__JAVA_JIT: |
1286 | case DSO_BINARY_TYPE__DEBUGLINK: | 1311 | case DSO_BINARY_TYPE__DEBUGLINK: |
1287 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: | 1312 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: |
1288 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: | 1313 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: |
1289 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: | 1314 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: |
1290 | case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: | 1315 | case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: |
1291 | case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: | 1316 | case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: |
1292 | return !kmod && dso->kernel == DSO_TYPE_USER; | 1317 | return !kmod && dso->kernel == DSO_TYPE_USER; |
1293 | 1318 | ||
1294 | case DSO_BINARY_TYPE__KALLSYMS: | 1319 | case DSO_BINARY_TYPE__KALLSYMS: |
1295 | case DSO_BINARY_TYPE__VMLINUX: | 1320 | case DSO_BINARY_TYPE__VMLINUX: |
1296 | case DSO_BINARY_TYPE__KCORE: | 1321 | case DSO_BINARY_TYPE__KCORE: |
1297 | return dso->kernel == DSO_TYPE_KERNEL; | 1322 | return dso->kernel == DSO_TYPE_KERNEL; |
1298 | 1323 | ||
1299 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: | 1324 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: |
1300 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | 1325 | case DSO_BINARY_TYPE__GUEST_VMLINUX: |
1301 | case DSO_BINARY_TYPE__GUEST_KCORE: | 1326 | case DSO_BINARY_TYPE__GUEST_KCORE: |
1302 | return dso->kernel == DSO_TYPE_GUEST_KERNEL; | 1327 | return dso->kernel == DSO_TYPE_GUEST_KERNEL; |
1303 | 1328 | ||
1304 | case DSO_BINARY_TYPE__GUEST_KMODULE: | 1329 | case DSO_BINARY_TYPE__GUEST_KMODULE: |
1305 | case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: | 1330 | case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: |
1306 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | 1331 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: |
1307 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: | 1332 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: |
1308 | /* | 1333 | /* |
1309 | * kernel modules know their symtab type - it's set when | 1334 | * kernel modules know their symtab type - it's set when |
1310 | * creating a module dso in machine__new_module(). | 1335 | * creating a module dso in machine__new_module(). |
1311 | */ | 1336 | */ |
1312 | return kmod && dso->symtab_type == type; | 1337 | return kmod && dso->symtab_type == type; |
1313 | 1338 | ||
1314 | case DSO_BINARY_TYPE__BUILD_ID_CACHE: | 1339 | case DSO_BINARY_TYPE__BUILD_ID_CACHE: |
1315 | return true; | 1340 | return true; |
1316 | 1341 | ||
1317 | case DSO_BINARY_TYPE__NOT_FOUND: | 1342 | case DSO_BINARY_TYPE__NOT_FOUND: |
1318 | default: | 1343 | default: |
1319 | return false; | 1344 | return false; |
1320 | } | 1345 | } |
1321 | } | 1346 | } |
1322 | 1347 | ||
1323 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | 1348 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) |
1324 | { | 1349 | { |
1325 | char *name; | 1350 | char *name; |
1326 | int ret = -1; | 1351 | int ret = -1; |
1327 | u_int i; | 1352 | u_int i; |
1328 | struct machine *machine; | 1353 | struct machine *machine; |
1329 | char *root_dir = (char *) ""; | 1354 | char *root_dir = (char *) ""; |
1330 | int ss_pos = 0; | 1355 | int ss_pos = 0; |
1331 | struct symsrc ss_[2]; | 1356 | struct symsrc ss_[2]; |
1332 | struct symsrc *syms_ss = NULL, *runtime_ss = NULL; | 1357 | struct symsrc *syms_ss = NULL, *runtime_ss = NULL; |
1333 | bool kmod; | 1358 | bool kmod; |
1334 | 1359 | ||
1335 | dso__set_loaded(dso, map->type); | 1360 | dso__set_loaded(dso, map->type); |
1336 | 1361 | ||
1337 | if (dso->kernel == DSO_TYPE_KERNEL) | 1362 | if (dso->kernel == DSO_TYPE_KERNEL) |
1338 | return dso__load_kernel_sym(dso, map, filter); | 1363 | return dso__load_kernel_sym(dso, map, filter); |
1339 | else if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1364 | else if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
1340 | return dso__load_guest_kernel_sym(dso, map, filter); | 1365 | return dso__load_guest_kernel_sym(dso, map, filter); |
1341 | 1366 | ||
1342 | if (map->groups && map->groups->machine) | 1367 | if (map->groups && map->groups->machine) |
1343 | machine = map->groups->machine; | 1368 | machine = map->groups->machine; |
1344 | else | 1369 | else |
1345 | machine = NULL; | 1370 | machine = NULL; |
1346 | 1371 | ||
1347 | dso->adjust_symbols = 0; | 1372 | dso->adjust_symbols = 0; |
1348 | 1373 | ||
1349 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { | 1374 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { |
1350 | struct stat st; | 1375 | struct stat st; |
1351 | 1376 | ||
1352 | if (lstat(dso->name, &st) < 0) | 1377 | if (lstat(dso->name, &st) < 0) |
1353 | return -1; | 1378 | return -1; |
1354 | 1379 | ||
1355 | if (st.st_uid && (st.st_uid != geteuid())) { | 1380 | if (st.st_uid && (st.st_uid != geteuid())) { |
1356 | pr_warning("File %s not owned by current user or root, " | 1381 | pr_warning("File %s not owned by current user or root, " |
1357 | "ignoring it.\n", dso->name); | 1382 | "ignoring it.\n", dso->name); |
1358 | return -1; | 1383 | return -1; |
1359 | } | 1384 | } |
1360 | 1385 | ||
1361 | ret = dso__load_perf_map(dso, map, filter); | 1386 | ret = dso__load_perf_map(dso, map, filter); |
1362 | dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT : | 1387 | dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT : |
1363 | DSO_BINARY_TYPE__NOT_FOUND; | 1388 | DSO_BINARY_TYPE__NOT_FOUND; |
1364 | return ret; | 1389 | return ret; |
1365 | } | 1390 | } |
1366 | 1391 | ||
1367 | if (machine) | 1392 | if (machine) |
1368 | root_dir = machine->root_dir; | 1393 | root_dir = machine->root_dir; |
1369 | 1394 | ||
1370 | name = malloc(PATH_MAX); | 1395 | name = malloc(PATH_MAX); |
1371 | if (!name) | 1396 | if (!name) |
1372 | return -1; | 1397 | return -1; |
1373 | 1398 | ||
1374 | kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || | 1399 | kmod = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || |
1375 | dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || | 1400 | dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || |
1376 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE || | 1401 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE || |
1377 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; | 1402 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; |
1378 | 1403 | ||
1379 | /* | 1404 | /* |
1380 | * Iterate over candidate debug images. | 1405 | * Iterate over candidate debug images. |
1381 | * Keep track of "interesting" ones (those which have a symtab, dynsym, | 1406 | * Keep track of "interesting" ones (those which have a symtab, dynsym, |
1382 | * and/or opd section) for processing. | 1407 | * and/or opd section) for processing. |
1383 | */ | 1408 | */ |
1384 | for (i = 0; i < DSO_BINARY_TYPE__SYMTAB_CNT; i++) { | 1409 | for (i = 0; i < DSO_BINARY_TYPE__SYMTAB_CNT; i++) { |
1385 | struct symsrc *ss = &ss_[ss_pos]; | 1410 | struct symsrc *ss = &ss_[ss_pos]; |
1386 | bool next_slot = false; | 1411 | bool next_slot = false; |
1387 | 1412 | ||
1388 | enum dso_binary_type symtab_type = binary_type_symtab[i]; | 1413 | enum dso_binary_type symtab_type = binary_type_symtab[i]; |
1389 | 1414 | ||
1390 | if (!dso__is_compatible_symtab_type(dso, kmod, symtab_type)) | 1415 | if (!dso__is_compatible_symtab_type(dso, kmod, symtab_type)) |
1391 | continue; | 1416 | continue; |
1392 | 1417 | ||
1393 | if (dso__read_binary_type_filename(dso, symtab_type, | 1418 | if (dso__read_binary_type_filename(dso, symtab_type, |
1394 | root_dir, name, PATH_MAX)) | 1419 | root_dir, name, PATH_MAX)) |
1395 | continue; | 1420 | continue; |
1396 | 1421 | ||
1397 | /* Name is now the name of the next image to try */ | 1422 | /* Name is now the name of the next image to try */ |
1398 | if (symsrc__init(ss, dso, name, symtab_type) < 0) | 1423 | if (symsrc__init(ss, dso, name, symtab_type) < 0) |
1399 | continue; | 1424 | continue; |
1400 | 1425 | ||
1401 | if (!syms_ss && symsrc__has_symtab(ss)) { | 1426 | if (!syms_ss && symsrc__has_symtab(ss)) { |
1402 | syms_ss = ss; | 1427 | syms_ss = ss; |
1403 | next_slot = true; | 1428 | next_slot = true; |
1404 | if (!dso->symsrc_filename) | 1429 | if (!dso->symsrc_filename) |
1405 | dso->symsrc_filename = strdup(name); | 1430 | dso->symsrc_filename = strdup(name); |
1406 | } | 1431 | } |
1407 | 1432 | ||
1408 | if (!runtime_ss && symsrc__possibly_runtime(ss)) { | 1433 | if (!runtime_ss && symsrc__possibly_runtime(ss)) { |
1409 | runtime_ss = ss; | 1434 | runtime_ss = ss; |
1410 | next_slot = true; | 1435 | next_slot = true; |
1411 | } | 1436 | } |
1412 | 1437 | ||
1413 | if (next_slot) { | 1438 | if (next_slot) { |
1414 | ss_pos++; | 1439 | ss_pos++; |
1415 | 1440 | ||
1416 | if (syms_ss && runtime_ss) | 1441 | if (syms_ss && runtime_ss) |
1417 | break; | 1442 | break; |
1418 | } else { | 1443 | } else { |
1419 | symsrc__destroy(ss); | 1444 | symsrc__destroy(ss); |
1420 | } | 1445 | } |
1421 | 1446 | ||
1422 | } | 1447 | } |
1423 | 1448 | ||
1424 | if (!runtime_ss && !syms_ss) | 1449 | if (!runtime_ss && !syms_ss) |
1425 | goto out_free; | 1450 | goto out_free; |
1426 | 1451 | ||
1427 | if (runtime_ss && !syms_ss) { | 1452 | if (runtime_ss && !syms_ss) { |
1428 | syms_ss = runtime_ss; | 1453 | syms_ss = runtime_ss; |
1429 | } | 1454 | } |
1430 | 1455 | ||
1431 | /* We'll have to hope for the best */ | 1456 | /* We'll have to hope for the best */ |
1432 | if (!runtime_ss && syms_ss) | 1457 | if (!runtime_ss && syms_ss) |
1433 | runtime_ss = syms_ss; | 1458 | runtime_ss = syms_ss; |
1434 | 1459 | ||
1435 | if (syms_ss) | 1460 | if (syms_ss) |
1436 | ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, kmod); | 1461 | ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, kmod); |
1437 | else | 1462 | else |
1438 | ret = -1; | 1463 | ret = -1; |
1439 | 1464 | ||
1440 | if (ret > 0) { | 1465 | if (ret > 0) { |
1441 | int nr_plt; | 1466 | int nr_plt; |
1442 | 1467 | ||
1443 | nr_plt = dso__synthesize_plt_symbols(dso, runtime_ss, map, filter); | 1468 | nr_plt = dso__synthesize_plt_symbols(dso, runtime_ss, map, filter); |
1444 | if (nr_plt > 0) | 1469 | if (nr_plt > 0) |
1445 | ret += nr_plt; | 1470 | ret += nr_plt; |
1446 | } | 1471 | } |
1447 | 1472 | ||
1448 | for (; ss_pos > 0; ss_pos--) | 1473 | for (; ss_pos > 0; ss_pos--) |
1449 | symsrc__destroy(&ss_[ss_pos - 1]); | 1474 | symsrc__destroy(&ss_[ss_pos - 1]); |
1450 | out_free: | 1475 | out_free: |
1451 | free(name); | 1476 | free(name); |
1452 | if (ret < 0 && strstr(dso->name, " (deleted)") != NULL) | 1477 | if (ret < 0 && strstr(dso->name, " (deleted)") != NULL) |
1453 | return 0; | 1478 | return 0; |
1454 | return ret; | 1479 | return ret; |
1455 | } | 1480 | } |
1456 | 1481 | ||
1457 | struct map *map_groups__find_by_name(struct map_groups *mg, | 1482 | struct map *map_groups__find_by_name(struct map_groups *mg, |
1458 | enum map_type type, const char *name) | 1483 | enum map_type type, const char *name) |
1459 | { | 1484 | { |
1460 | struct rb_node *nd; | 1485 | struct rb_node *nd; |
1461 | 1486 | ||
1462 | for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { | 1487 | for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) { |
1463 | struct map *map = rb_entry(nd, struct map, rb_node); | 1488 | struct map *map = rb_entry(nd, struct map, rb_node); |
1464 | 1489 | ||
1465 | if (map->dso && strcmp(map->dso->short_name, name) == 0) | 1490 | if (map->dso && strcmp(map->dso->short_name, name) == 0) |
1466 | return map; | 1491 | return map; |
1467 | } | 1492 | } |
1468 | 1493 | ||
1469 | return NULL; | 1494 | return NULL; |
1470 | } | 1495 | } |
1471 | 1496 | ||
1472 | int dso__load_vmlinux(struct dso *dso, struct map *map, | 1497 | int dso__load_vmlinux(struct dso *dso, struct map *map, |
1473 | const char *vmlinux, bool vmlinux_allocated, | 1498 | const char *vmlinux, bool vmlinux_allocated, |
1474 | symbol_filter_t filter) | 1499 | symbol_filter_t filter) |
1475 | { | 1500 | { |
1476 | int err = -1; | 1501 | int err = -1; |
1477 | struct symsrc ss; | 1502 | struct symsrc ss; |
1478 | char symfs_vmlinux[PATH_MAX]; | 1503 | char symfs_vmlinux[PATH_MAX]; |
1479 | enum dso_binary_type symtab_type; | 1504 | enum dso_binary_type symtab_type; |
1480 | 1505 | ||
1481 | if (vmlinux[0] == '/') | 1506 | if (vmlinux[0] == '/') |
1482 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux); | 1507 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux); |
1483 | else | 1508 | else |
1484 | symbol__join_symfs(symfs_vmlinux, vmlinux); | 1509 | symbol__join_symfs(symfs_vmlinux, vmlinux); |
1485 | 1510 | ||
1486 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1511 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
1487 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; | 1512 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; |
1488 | else | 1513 | else |
1489 | symtab_type = DSO_BINARY_TYPE__VMLINUX; | 1514 | symtab_type = DSO_BINARY_TYPE__VMLINUX; |
1490 | 1515 | ||
1491 | if (symsrc__init(&ss, dso, symfs_vmlinux, symtab_type)) | 1516 | if (symsrc__init(&ss, dso, symfs_vmlinux, symtab_type)) |
1492 | return -1; | 1517 | return -1; |
1493 | 1518 | ||
1494 | err = dso__load_sym(dso, map, &ss, &ss, filter, 0); | 1519 | err = dso__load_sym(dso, map, &ss, &ss, filter, 0); |
1495 | symsrc__destroy(&ss); | 1520 | symsrc__destroy(&ss); |
1496 | 1521 | ||
1497 | if (err > 0) { | 1522 | if (err > 0) { |
1498 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1523 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
1499 | dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX; | 1524 | dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX; |
1500 | else | 1525 | else |
1501 | dso->binary_type = DSO_BINARY_TYPE__VMLINUX; | 1526 | dso->binary_type = DSO_BINARY_TYPE__VMLINUX; |
1502 | dso__set_long_name(dso, vmlinux, vmlinux_allocated); | 1527 | dso__set_long_name(dso, vmlinux, vmlinux_allocated); |
1503 | dso__set_loaded(dso, map->type); | 1528 | dso__set_loaded(dso, map->type); |
1504 | pr_debug("Using %s for symbols\n", symfs_vmlinux); | 1529 | pr_debug("Using %s for symbols\n", symfs_vmlinux); |
1505 | } | 1530 | } |
1506 | 1531 | ||
1507 | return err; | 1532 | return err; |
1508 | } | 1533 | } |
1509 | 1534 | ||
1510 | int dso__load_vmlinux_path(struct dso *dso, struct map *map, | 1535 | int dso__load_vmlinux_path(struct dso *dso, struct map *map, |
1511 | symbol_filter_t filter) | 1536 | symbol_filter_t filter) |
1512 | { | 1537 | { |
1513 | int i, err = 0; | 1538 | int i, err = 0; |
1514 | char *filename = NULL; | 1539 | char *filename = NULL; |
1515 | 1540 | ||
1516 | if (!symbol_conf.ignore_vmlinux_buildid) | 1541 | if (!symbol_conf.ignore_vmlinux_buildid) |
1517 | filename = dso__build_id_filename(dso, NULL, 0); | 1542 | filename = dso__build_id_filename(dso, NULL, 0); |
1518 | if (filename != NULL) { | 1543 | if (filename != NULL) { |
1519 | err = dso__load_vmlinux(dso, map, filename, true, filter); | 1544 | err = dso__load_vmlinux(dso, map, filename, true, filter); |
1520 | if (err > 0) | 1545 | if (err > 0) |
1521 | goto out; | 1546 | goto out; |
1522 | free(filename); | 1547 | free(filename); |
1523 | } | 1548 | } |
1524 | 1549 | ||
1525 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", | 1550 | pr_debug("Looking at the vmlinux_path (%d entries long)\n", |
1526 | vmlinux_path__nr_entries + 1); | 1551 | vmlinux_path__nr_entries + 1); |
1527 | 1552 | ||
1528 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { | 1553 | for (i = 0; i < vmlinux_path__nr_entries; ++i) { |
1529 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); | 1554 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); |
1530 | if (err > 0) | 1555 | if (err > 0) |
1531 | break; | 1556 | break; |
1532 | } | 1557 | } |
1533 | out: | 1558 | out: |
1534 | return err; | 1559 | return err; |
1535 | } | 1560 | } |
1536 | 1561 | ||
1537 | static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz) | 1562 | static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz) |
1538 | { | 1563 | { |
1539 | char kallsyms_filename[PATH_MAX]; | 1564 | char kallsyms_filename[PATH_MAX]; |
1540 | struct dirent *dent; | 1565 | struct dirent *dent; |
1541 | int ret = -1; | 1566 | int ret = -1; |
1542 | DIR *d; | 1567 | DIR *d; |
1543 | 1568 | ||
1544 | d = opendir(dir); | 1569 | d = opendir(dir); |
1545 | if (!d) | 1570 | if (!d) |
1546 | return -1; | 1571 | return -1; |
1547 | 1572 | ||
1548 | while (1) { | 1573 | while (1) { |
1549 | dent = readdir(d); | 1574 | dent = readdir(d); |
1550 | if (!dent) | 1575 | if (!dent) |
1551 | break; | 1576 | break; |
1552 | if (dent->d_type != DT_DIR) | 1577 | if (dent->d_type != DT_DIR) |
1553 | continue; | 1578 | continue; |
1554 | scnprintf(kallsyms_filename, sizeof(kallsyms_filename), | 1579 | scnprintf(kallsyms_filename, sizeof(kallsyms_filename), |
1555 | "%s/%s/kallsyms", dir, dent->d_name); | 1580 | "%s/%s/kallsyms", dir, dent->d_name); |
1556 | if (!validate_kcore_addresses(kallsyms_filename, map)) { | 1581 | if (!validate_kcore_addresses(kallsyms_filename, map)) { |
1557 | strlcpy(dir, kallsyms_filename, dir_sz); | 1582 | strlcpy(dir, kallsyms_filename, dir_sz); |
1558 | ret = 0; | 1583 | ret = 0; |
1559 | break; | 1584 | break; |
1560 | } | 1585 | } |
1561 | } | 1586 | } |
1562 | 1587 | ||
1563 | closedir(d); | 1588 | closedir(d); |
1564 | 1589 | ||
1565 | return ret; | 1590 | return ret; |
1566 | } | 1591 | } |
1567 | 1592 | ||
1568 | static char *dso__find_kallsyms(struct dso *dso, struct map *map) | 1593 | static char *dso__find_kallsyms(struct dso *dso, struct map *map) |
1569 | { | 1594 | { |
1570 | u8 host_build_id[BUILD_ID_SIZE]; | 1595 | u8 host_build_id[BUILD_ID_SIZE]; |
1571 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 1596 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
1572 | bool is_host = false; | 1597 | bool is_host = false; |
1573 | char path[PATH_MAX]; | 1598 | char path[PATH_MAX]; |
1574 | 1599 | ||
1575 | if (!dso->has_build_id) { | 1600 | if (!dso->has_build_id) { |
1576 | /* | 1601 | /* |
1577 | * Last resort, if we don't have a build-id and couldn't find | 1602 | * Last resort, if we don't have a build-id and couldn't find |
1578 | * any vmlinux file, try the running kernel kallsyms table. | 1603 | * any vmlinux file, try the running kernel kallsyms table. |
1579 | */ | 1604 | */ |
1580 | goto proc_kallsyms; | 1605 | goto proc_kallsyms; |
1581 | } | 1606 | } |
1582 | 1607 | ||
1583 | if (sysfs__read_build_id("/sys/kernel/notes", host_build_id, | 1608 | if (sysfs__read_build_id("/sys/kernel/notes", host_build_id, |
1584 | sizeof(host_build_id)) == 0) | 1609 | sizeof(host_build_id)) == 0) |
1585 | is_host = dso__build_id_equal(dso, host_build_id); | 1610 | is_host = dso__build_id_equal(dso, host_build_id); |
1586 | 1611 | ||
1587 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | 1612 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); |
1588 | 1613 | ||
1589 | scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", buildid_dir, | 1614 | scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", buildid_dir, |
1590 | sbuild_id); | 1615 | sbuild_id); |
1591 | 1616 | ||
1592 | /* Use /proc/kallsyms if possible */ | 1617 | /* Use /proc/kallsyms if possible */ |
1593 | if (is_host) { | 1618 | if (is_host) { |
1594 | DIR *d; | 1619 | DIR *d; |
1595 | int fd; | 1620 | int fd; |
1596 | 1621 | ||
1597 | /* If no cached kcore go with /proc/kallsyms */ | 1622 | /* If no cached kcore go with /proc/kallsyms */ |
1598 | d = opendir(path); | 1623 | d = opendir(path); |
1599 | if (!d) | 1624 | if (!d) |
1600 | goto proc_kallsyms; | 1625 | goto proc_kallsyms; |
1601 | closedir(d); | 1626 | closedir(d); |
1602 | 1627 | ||
1603 | /* | 1628 | /* |
1604 | * Do not check the build-id cache, until we know we cannot use | 1629 | * Do not check the build-id cache, until we know we cannot use |
1605 | * /proc/kcore. | 1630 | * /proc/kcore. |
1606 | */ | 1631 | */ |
1607 | fd = open("/proc/kcore", O_RDONLY); | 1632 | fd = open("/proc/kcore", O_RDONLY); |
1608 | if (fd != -1) { | 1633 | if (fd != -1) { |
1609 | close(fd); | 1634 | close(fd); |
1610 | /* If module maps match go with /proc/kallsyms */ | 1635 | /* If module maps match go with /proc/kallsyms */ |
1611 | if (!validate_kcore_addresses("/proc/kallsyms", map)) | 1636 | if (!validate_kcore_addresses("/proc/kallsyms", map)) |
1612 | goto proc_kallsyms; | 1637 | goto proc_kallsyms; |
1613 | } | 1638 | } |
1614 | 1639 | ||
1615 | /* Find kallsyms in build-id cache with kcore */ | 1640 | /* Find kallsyms in build-id cache with kcore */ |
1616 | if (!find_matching_kcore(map, path, sizeof(path))) | 1641 | if (!find_matching_kcore(map, path, sizeof(path))) |
1617 | return strdup(path); | 1642 | return strdup(path); |
1618 | 1643 | ||
1619 | goto proc_kallsyms; | 1644 | goto proc_kallsyms; |
1620 | } | 1645 | } |
1621 | 1646 | ||
1622 | /* Find kallsyms in build-id cache with kcore */ | 1647 | /* Find kallsyms in build-id cache with kcore */ |
1623 | if (!find_matching_kcore(map, path, sizeof(path))) | 1648 | if (!find_matching_kcore(map, path, sizeof(path))) |
1624 | return strdup(path); | 1649 | return strdup(path); |
1625 | 1650 | ||
1626 | scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s", | 1651 | scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s", |
1627 | buildid_dir, sbuild_id); | 1652 | buildid_dir, sbuild_id); |
1628 | 1653 | ||
1629 | if (access(path, F_OK)) { | 1654 | if (access(path, F_OK)) { |
1630 | pr_err("No kallsyms or vmlinux with build-id %s was found\n", | 1655 | pr_err("No kallsyms or vmlinux with build-id %s was found\n", |
1631 | sbuild_id); | 1656 | sbuild_id); |
1632 | return NULL; | 1657 | return NULL; |
1633 | } | 1658 | } |
1634 | 1659 | ||
1635 | return strdup(path); | 1660 | return strdup(path); |
1636 | 1661 | ||
1637 | proc_kallsyms: | 1662 | proc_kallsyms: |
1638 | return strdup("/proc/kallsyms"); | 1663 | return strdup("/proc/kallsyms"); |
1639 | } | 1664 | } |
1640 | 1665 | ||
1641 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, | 1666 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, |
1642 | symbol_filter_t filter) | 1667 | symbol_filter_t filter) |
1643 | { | 1668 | { |
1644 | int err; | 1669 | int err; |
1645 | const char *kallsyms_filename = NULL; | 1670 | const char *kallsyms_filename = NULL; |
1646 | char *kallsyms_allocated_filename = NULL; | 1671 | char *kallsyms_allocated_filename = NULL; |
1647 | /* | 1672 | /* |
1648 | * Step 1: if the user specified a kallsyms or vmlinux filename, use | 1673 | * Step 1: if the user specified a kallsyms or vmlinux filename, use |
1649 | * it and only it, reporting errors to the user if it cannot be used. | 1674 | * it and only it, reporting errors to the user if it cannot be used. |
1650 | * | 1675 | * |
1651 | * For instance, try to analyse an ARM perf.data file _without_ a | 1676 | * For instance, try to analyse an ARM perf.data file _without_ a |
1652 | * build-id, or if the user specifies the wrong path to the right | 1677 | * build-id, or if the user specifies the wrong path to the right |
1653 | * vmlinux file, obviously we can't fallback to another vmlinux (a | 1678 | * vmlinux file, obviously we can't fallback to another vmlinux (a |
1654 | * x86_86 one, on the machine where analysis is being performed, say), | 1679 | * x86_86 one, on the machine where analysis is being performed, say), |
1655 | * or worse, /proc/kallsyms. | 1680 | * or worse, /proc/kallsyms. |
1656 | * | 1681 | * |
1657 | * If the specified file _has_ a build-id and there is a build-id | 1682 | * If the specified file _has_ a build-id and there is a build-id |
1658 | * section in the perf.data file, we will still do the expected | 1683 | * section in the perf.data file, we will still do the expected |
1659 | * validation in dso__load_vmlinux and will bail out if they don't | 1684 | * validation in dso__load_vmlinux and will bail out if they don't |
1660 | * match. | 1685 | * match. |
1661 | */ | 1686 | */ |
1662 | if (symbol_conf.kallsyms_name != NULL) { | 1687 | if (symbol_conf.kallsyms_name != NULL) { |
1663 | kallsyms_filename = symbol_conf.kallsyms_name; | 1688 | kallsyms_filename = symbol_conf.kallsyms_name; |
1664 | goto do_kallsyms; | 1689 | goto do_kallsyms; |
1665 | } | 1690 | } |
1666 | 1691 | ||
1667 | if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) { | 1692 | if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) { |
1668 | return dso__load_vmlinux(dso, map, symbol_conf.vmlinux_name, | 1693 | return dso__load_vmlinux(dso, map, symbol_conf.vmlinux_name, |
1669 | false, filter); | 1694 | false, filter); |
1670 | } | 1695 | } |
1671 | 1696 | ||
1672 | if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) { | 1697 | if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) { |
1673 | err = dso__load_vmlinux_path(dso, map, filter); | 1698 | err = dso__load_vmlinux_path(dso, map, filter); |
1674 | if (err > 0) | 1699 | if (err > 0) |
1675 | return err; | 1700 | return err; |
1676 | } | 1701 | } |
1677 | 1702 | ||
1678 | /* do not try local files if a symfs was given */ | 1703 | /* do not try local files if a symfs was given */ |
1679 | if (symbol_conf.symfs[0] != 0) | 1704 | if (symbol_conf.symfs[0] != 0) |
1680 | return -1; | 1705 | return -1; |
1681 | 1706 | ||
1682 | kallsyms_allocated_filename = dso__find_kallsyms(dso, map); | 1707 | kallsyms_allocated_filename = dso__find_kallsyms(dso, map); |
1683 | if (!kallsyms_allocated_filename) | 1708 | if (!kallsyms_allocated_filename) |
1684 | return -1; | 1709 | return -1; |
1685 | 1710 | ||
1686 | kallsyms_filename = kallsyms_allocated_filename; | 1711 | kallsyms_filename = kallsyms_allocated_filename; |
1687 | 1712 | ||
1688 | do_kallsyms: | 1713 | do_kallsyms: |
1689 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); | 1714 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); |
1690 | if (err > 0) | 1715 | if (err > 0) |
1691 | pr_debug("Using %s for symbols\n", kallsyms_filename); | 1716 | pr_debug("Using %s for symbols\n", kallsyms_filename); |
1692 | free(kallsyms_allocated_filename); | 1717 | free(kallsyms_allocated_filename); |
1693 | 1718 | ||
1694 | if (err > 0 && !dso__is_kcore(dso)) { | 1719 | if (err > 0 && !dso__is_kcore(dso)) { |
1695 | dso->binary_type = DSO_BINARY_TYPE__KALLSYMS; | 1720 | dso->binary_type = DSO_BINARY_TYPE__KALLSYMS; |
1696 | dso__set_long_name(dso, "[kernel.kallsyms]", false); | 1721 | dso__set_long_name(dso, "[kernel.kallsyms]", false); |
1697 | map__fixup_start(map); | 1722 | map__fixup_start(map); |
1698 | map__fixup_end(map); | 1723 | map__fixup_end(map); |
1699 | } | 1724 | } |
1700 | 1725 | ||
1701 | return err; | 1726 | return err; |
1702 | } | 1727 | } |
1703 | 1728 | ||
1704 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | 1729 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, |
1705 | symbol_filter_t filter) | 1730 | symbol_filter_t filter) |
1706 | { | 1731 | { |
1707 | int err; | 1732 | int err; |
1708 | const char *kallsyms_filename = NULL; | 1733 | const char *kallsyms_filename = NULL; |
1709 | struct machine *machine; | 1734 | struct machine *machine; |
1710 | char path[PATH_MAX]; | 1735 | char path[PATH_MAX]; |
1711 | 1736 | ||
1712 | if (!map->groups) { | 1737 | if (!map->groups) { |
1713 | pr_debug("Guest kernel map hasn't the point to groups\n"); | 1738 | pr_debug("Guest kernel map hasn't the point to groups\n"); |
1714 | return -1; | 1739 | return -1; |
1715 | } | 1740 | } |
1716 | machine = map->groups->machine; | 1741 | machine = map->groups->machine; |
1717 | 1742 | ||
1718 | if (machine__is_default_guest(machine)) { | 1743 | if (machine__is_default_guest(machine)) { |
1719 | /* | 1744 | /* |
1720 | * if the user specified a vmlinux filename, use it and only | 1745 | * if the user specified a vmlinux filename, use it and only |
1721 | * it, reporting errors to the user if it cannot be used. | 1746 | * it, reporting errors to the user if it cannot be used. |
1722 | * Or use file guest_kallsyms inputted by user on commandline | 1747 | * Or use file guest_kallsyms inputted by user on commandline |
1723 | */ | 1748 | */ |
1724 | if (symbol_conf.default_guest_vmlinux_name != NULL) { | 1749 | if (symbol_conf.default_guest_vmlinux_name != NULL) { |
1725 | err = dso__load_vmlinux(dso, map, | 1750 | err = dso__load_vmlinux(dso, map, |
1726 | symbol_conf.default_guest_vmlinux_name, | 1751 | symbol_conf.default_guest_vmlinux_name, |
1727 | false, filter); | 1752 | false, filter); |
1728 | return err; | 1753 | return err; |
1729 | } | 1754 | } |
1730 | 1755 | ||
1731 | kallsyms_filename = symbol_conf.default_guest_kallsyms; | 1756 | kallsyms_filename = symbol_conf.default_guest_kallsyms; |
1732 | if (!kallsyms_filename) | 1757 | if (!kallsyms_filename) |
1733 | return -1; | 1758 | return -1; |
1734 | } else { | 1759 | } else { |
1735 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); | 1760 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); |
1736 | kallsyms_filename = path; | 1761 | kallsyms_filename = path; |
1737 | } | 1762 | } |
1738 | 1763 | ||
1739 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); | 1764 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); |
1740 | if (err > 0) | 1765 | if (err > 0) |
1741 | pr_debug("Using %s for symbols\n", kallsyms_filename); | 1766 | pr_debug("Using %s for symbols\n", kallsyms_filename); |
1742 | if (err > 0 && !dso__is_kcore(dso)) { | 1767 | if (err > 0 && !dso__is_kcore(dso)) { |
1743 | dso->binary_type = DSO_BINARY_TYPE__GUEST_KALLSYMS; | 1768 | dso->binary_type = DSO_BINARY_TYPE__GUEST_KALLSYMS; |
1744 | machine__mmap_name(machine, path, sizeof(path)); | 1769 | machine__mmap_name(machine, path, sizeof(path)); |
1745 | dso__set_long_name(dso, strdup(path), true); | 1770 | dso__set_long_name(dso, strdup(path), true); |
1746 | map__fixup_start(map); | 1771 | map__fixup_start(map); |
1747 | map__fixup_end(map); | 1772 | map__fixup_end(map); |
1748 | } | 1773 | } |
1749 | 1774 | ||
1750 | return err; | 1775 | return err; |
1751 | } | 1776 | } |
1752 | 1777 | ||
1753 | static void vmlinux_path__exit(void) | 1778 | static void vmlinux_path__exit(void) |
1754 | { | 1779 | { |
1755 | while (--vmlinux_path__nr_entries >= 0) | 1780 | while (--vmlinux_path__nr_entries >= 0) |
1756 | zfree(&vmlinux_path[vmlinux_path__nr_entries]); | 1781 | zfree(&vmlinux_path[vmlinux_path__nr_entries]); |
1757 | 1782 | ||
1758 | zfree(&vmlinux_path); | 1783 | zfree(&vmlinux_path); |
1759 | } | 1784 | } |
1760 | 1785 | ||
1761 | static int vmlinux_path__init(struct perf_session_env *env) | 1786 | static int vmlinux_path__init(struct perf_session_env *env) |
1762 | { | 1787 | { |
1763 | struct utsname uts; | 1788 | struct utsname uts; |
1764 | char bf[PATH_MAX]; | 1789 | char bf[PATH_MAX]; |
1765 | char *kernel_version; | 1790 | char *kernel_version; |
1766 | 1791 | ||
1767 | vmlinux_path = malloc(sizeof(char *) * 6); | 1792 | vmlinux_path = malloc(sizeof(char *) * 6); |
1768 | if (vmlinux_path == NULL) | 1793 | if (vmlinux_path == NULL) |
1769 | return -1; | 1794 | return -1; |
1770 | 1795 | ||
1771 | vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux"); | 1796 | vmlinux_path[vmlinux_path__nr_entries] = strdup("vmlinux"); |
1772 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1797 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1773 | goto out_fail; | 1798 | goto out_fail; |
1774 | ++vmlinux_path__nr_entries; | 1799 | ++vmlinux_path__nr_entries; |
1775 | vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux"); | 1800 | vmlinux_path[vmlinux_path__nr_entries] = strdup("/boot/vmlinux"); |
1776 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1801 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1777 | goto out_fail; | 1802 | goto out_fail; |
1778 | ++vmlinux_path__nr_entries; | 1803 | ++vmlinux_path__nr_entries; |
1779 | 1804 | ||
1780 | /* only try kernel version if no symfs was given */ | 1805 | /* only try kernel version if no symfs was given */ |
1781 | if (symbol_conf.symfs[0] != 0) | 1806 | if (symbol_conf.symfs[0] != 0) |
1782 | return 0; | 1807 | return 0; |
1783 | 1808 | ||
1784 | if (env) { | 1809 | if (env) { |
1785 | kernel_version = env->os_release; | 1810 | kernel_version = env->os_release; |
1786 | } else { | 1811 | } else { |
1787 | if (uname(&uts) < 0) | 1812 | if (uname(&uts) < 0) |
1788 | goto out_fail; | 1813 | goto out_fail; |
1789 | 1814 | ||
1790 | kernel_version = uts.release; | 1815 | kernel_version = uts.release; |
1791 | } | 1816 | } |
1792 | 1817 | ||
1793 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", kernel_version); | 1818 | snprintf(bf, sizeof(bf), "/boot/vmlinux-%s", kernel_version); |
1794 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1819 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
1795 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1820 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1796 | goto out_fail; | 1821 | goto out_fail; |
1797 | ++vmlinux_path__nr_entries; | 1822 | ++vmlinux_path__nr_entries; |
1798 | snprintf(bf, sizeof(bf), "/usr/lib/debug/boot/vmlinux-%s", | 1823 | snprintf(bf, sizeof(bf), "/usr/lib/debug/boot/vmlinux-%s", |
1799 | kernel_version); | 1824 | kernel_version); |
1800 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1825 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
1801 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1826 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1802 | goto out_fail; | 1827 | goto out_fail; |
1803 | ++vmlinux_path__nr_entries; | 1828 | ++vmlinux_path__nr_entries; |
1804 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", kernel_version); | 1829 | snprintf(bf, sizeof(bf), "/lib/modules/%s/build/vmlinux", kernel_version); |
1805 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1830 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
1806 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1831 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1807 | goto out_fail; | 1832 | goto out_fail; |
1808 | ++vmlinux_path__nr_entries; | 1833 | ++vmlinux_path__nr_entries; |
1809 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", | 1834 | snprintf(bf, sizeof(bf), "/usr/lib/debug/lib/modules/%s/vmlinux", |
1810 | kernel_version); | 1835 | kernel_version); |
1811 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); | 1836 | vmlinux_path[vmlinux_path__nr_entries] = strdup(bf); |
1812 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) | 1837 | if (vmlinux_path[vmlinux_path__nr_entries] == NULL) |
1813 | goto out_fail; | 1838 | goto out_fail; |
1814 | ++vmlinux_path__nr_entries; | 1839 | ++vmlinux_path__nr_entries; |
1815 | 1840 | ||
1816 | return 0; | 1841 | return 0; |
1817 | 1842 | ||
1818 | out_fail: | 1843 | out_fail: |
1819 | vmlinux_path__exit(); | 1844 | vmlinux_path__exit(); |
1820 | return -1; | 1845 | return -1; |
1821 | } | 1846 | } |
1822 | 1847 | ||
1823 | int setup_list(struct strlist **list, const char *list_str, | 1848 | int setup_list(struct strlist **list, const char *list_str, |
1824 | const char *list_name) | 1849 | const char *list_name) |
1825 | { | 1850 | { |
1826 | if (list_str == NULL) | 1851 | if (list_str == NULL) |
1827 | return 0; | 1852 | return 0; |
1828 | 1853 | ||
1829 | *list = strlist__new(true, list_str); | 1854 | *list = strlist__new(true, list_str); |
1830 | if (!*list) { | 1855 | if (!*list) { |
1831 | pr_err("problems parsing %s list\n", list_name); | 1856 | pr_err("problems parsing %s list\n", list_name); |
1832 | return -1; | 1857 | return -1; |
1833 | } | 1858 | } |
1834 | return 0; | 1859 | return 0; |
1835 | } | 1860 | } |
1836 | 1861 | ||
1837 | static bool symbol__read_kptr_restrict(void) | 1862 | static bool symbol__read_kptr_restrict(void) |
1838 | { | 1863 | { |
1839 | bool value = false; | 1864 | bool value = false; |
1840 | 1865 | ||
1841 | if (geteuid() != 0) { | 1866 | if (geteuid() != 0) { |
1842 | FILE *fp = fopen("/proc/sys/kernel/kptr_restrict", "r"); | 1867 | FILE *fp = fopen("/proc/sys/kernel/kptr_restrict", "r"); |
1843 | if (fp != NULL) { | 1868 | if (fp != NULL) { |
1844 | char line[8]; | 1869 | char line[8]; |
1845 | 1870 | ||
1846 | if (fgets(line, sizeof(line), fp) != NULL) | 1871 | if (fgets(line, sizeof(line), fp) != NULL) |
1847 | value = atoi(line) != 0; | 1872 | value = atoi(line) != 0; |
1848 | 1873 | ||
1849 | fclose(fp); | 1874 | fclose(fp); |
1850 | } | 1875 | } |
1851 | } | 1876 | } |
1852 | 1877 | ||
1853 | return value; | 1878 | return value; |
1854 | } | 1879 | } |
1855 | 1880 | ||
1856 | int symbol__init(struct perf_session_env *env) | 1881 | int symbol__init(struct perf_session_env *env) |
1857 | { | 1882 | { |
1858 | const char *symfs; | 1883 | const char *symfs; |
1859 | 1884 | ||
1860 | if (symbol_conf.initialized) | 1885 | if (symbol_conf.initialized) |
1861 | return 0; | 1886 | return 0; |
1862 | 1887 | ||
1863 | symbol_conf.priv_size = PERF_ALIGN(symbol_conf.priv_size, sizeof(u64)); | 1888 | symbol_conf.priv_size = PERF_ALIGN(symbol_conf.priv_size, sizeof(u64)); |
1864 | 1889 | ||
1865 | symbol__elf_init(); | 1890 | symbol__elf_init(); |
1866 | 1891 | ||
1867 | if (symbol_conf.sort_by_name) | 1892 | if (symbol_conf.sort_by_name) |
1868 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - | 1893 | symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) - |
1869 | sizeof(struct symbol)); | 1894 | sizeof(struct symbol)); |
1870 | 1895 | ||
1871 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0) | 1896 | if (symbol_conf.try_vmlinux_path && vmlinux_path__init(env) < 0) |
1872 | return -1; | 1897 | return -1; |
1873 | 1898 | ||
1874 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { | 1899 | if (symbol_conf.field_sep && *symbol_conf.field_sep == '.') { |
1875 | pr_err("'.' is the only non valid --field-separator argument\n"); | 1900 | pr_err("'.' is the only non valid --field-separator argument\n"); |
1876 | return -1; | 1901 | return -1; |
1877 | } | 1902 | } |
1878 | 1903 | ||
1879 | if (setup_list(&symbol_conf.dso_list, | 1904 | if (setup_list(&symbol_conf.dso_list, |
1880 | symbol_conf.dso_list_str, "dso") < 0) | 1905 | symbol_conf.dso_list_str, "dso") < 0) |
1881 | return -1; | 1906 | return -1; |
1882 | 1907 | ||
1883 | if (setup_list(&symbol_conf.comm_list, | 1908 | if (setup_list(&symbol_conf.comm_list, |
1884 | symbol_conf.comm_list_str, "comm") < 0) | 1909 | symbol_conf.comm_list_str, "comm") < 0) |
1885 | goto out_free_dso_list; | 1910 | goto out_free_dso_list; |
1886 | 1911 | ||
1887 | if (setup_list(&symbol_conf.sym_list, | 1912 | if (setup_list(&symbol_conf.sym_list, |
1888 | symbol_conf.sym_list_str, "symbol") < 0) | 1913 | symbol_conf.sym_list_str, "symbol") < 0) |
1889 | goto out_free_comm_list; | 1914 | goto out_free_comm_list; |
1890 | 1915 | ||
1891 | /* | 1916 | /* |
1892 | * A path to symbols of "/" is identical to "" | 1917 | * A path to symbols of "/" is identical to "" |
1893 | * reset here for simplicity. | 1918 | * reset here for simplicity. |
1894 | */ | 1919 | */ |
1895 | symfs = realpath(symbol_conf.symfs, NULL); | 1920 | symfs = realpath(symbol_conf.symfs, NULL); |
1896 | if (symfs == NULL) | 1921 | if (symfs == NULL) |
1897 | symfs = symbol_conf.symfs; | 1922 | symfs = symbol_conf.symfs; |
1898 | if (strcmp(symfs, "/") == 0) | 1923 | if (strcmp(symfs, "/") == 0) |
1899 | symbol_conf.symfs = ""; | 1924 | symbol_conf.symfs = ""; |
1900 | if (symfs != symbol_conf.symfs) | 1925 | if (symfs != symbol_conf.symfs) |
1901 | free((void *)symfs); | 1926 | free((void *)symfs); |
1902 | 1927 | ||
1903 | symbol_conf.kptr_restrict = symbol__read_kptr_restrict(); | 1928 | symbol_conf.kptr_restrict = symbol__read_kptr_restrict(); |
1904 | 1929 | ||
1905 | symbol_conf.initialized = true; | 1930 | symbol_conf.initialized = true; |
1906 | return 0; | 1931 | return 0; |
1907 | 1932 | ||
1908 | out_free_comm_list: | 1933 | out_free_comm_list: |
1909 | strlist__delete(symbol_conf.comm_list); | 1934 | strlist__delete(symbol_conf.comm_list); |
1910 | out_free_dso_list: | 1935 | out_free_dso_list: |
1911 | strlist__delete(symbol_conf.dso_list); | 1936 | strlist__delete(symbol_conf.dso_list); |
1912 | return -1; | 1937 | return -1; |
1913 | } | 1938 | } |
1914 | 1939 | ||
1915 | void symbol__exit(void) | 1940 | void symbol__exit(void) |
1916 | { | 1941 | { |
1917 | if (!symbol_conf.initialized) | 1942 | if (!symbol_conf.initialized) |
1918 | return; | 1943 | return; |
1919 | strlist__delete(symbol_conf.sym_list); | 1944 | strlist__delete(symbol_conf.sym_list); |
1920 | strlist__delete(symbol_conf.dso_list); | 1945 | strlist__delete(symbol_conf.dso_list); |
1921 | strlist__delete(symbol_conf.comm_list); | 1946 | strlist__delete(symbol_conf.comm_list); |
1922 | vmlinux_path__exit(); | 1947 | vmlinux_path__exit(); |
1923 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | 1948 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; |
1924 | symbol_conf.initialized = false; | 1949 | symbol_conf.initialized = false; |
1925 | } | 1950 | } |
tools/perf/util/symbol.h
1 | #ifndef __PERF_SYMBOL | 1 | #ifndef __PERF_SYMBOL |
2 | #define __PERF_SYMBOL 1 | 2 | #define __PERF_SYMBOL 1 |
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <stdint.h> | 6 | #include <stdint.h> |
7 | #include "map.h" | 7 | #include "map.h" |
8 | #include "../perf.h" | 8 | #include "../perf.h" |
9 | #include <linux/list.h> | 9 | #include <linux/list.h> |
10 | #include <linux/rbtree.h> | 10 | #include <linux/rbtree.h> |
11 | #include <stdio.h> | 11 | #include <stdio.h> |
12 | #include <byteswap.h> | 12 | #include <byteswap.h> |
13 | #include <libgen.h> | 13 | #include <libgen.h> |
14 | #include "build-id.h" | 14 | #include "build-id.h" |
15 | #include "event.h" | 15 | #include "event.h" |
16 | #include "util.h" | 16 | #include "util.h" |
17 | 17 | ||
18 | #ifdef HAVE_LIBELF_SUPPORT | 18 | #ifdef HAVE_LIBELF_SUPPORT |
19 | #include <libelf.h> | 19 | #include <libelf.h> |
20 | #include <gelf.h> | 20 | #include <gelf.h> |
21 | #endif | 21 | #endif |
22 | #include <elf.h> | 22 | #include <elf.h> |
23 | 23 | ||
24 | #include "dso.h" | 24 | #include "dso.h" |
25 | 25 | ||
26 | /* | 26 | /* |
27 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; | 27 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; |
28 | * for newer versions we can use mmap to reduce memory usage: | 28 | * for newer versions we can use mmap to reduce memory usage: |
29 | */ | 29 | */ |
30 | #ifdef HAVE_LIBELF_MMAP_SUPPORT | 30 | #ifdef HAVE_LIBELF_MMAP_SUPPORT |
31 | # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP | 31 | # define PERF_ELF_C_READ_MMAP ELF_C_READ_MMAP |
32 | #else | 32 | #else |
33 | # define PERF_ELF_C_READ_MMAP ELF_C_READ | 33 | # define PERF_ELF_C_READ_MMAP ELF_C_READ |
34 | #endif | 34 | #endif |
35 | 35 | ||
36 | #ifdef HAVE_LIBELF_SUPPORT | 36 | #ifdef HAVE_LIBELF_SUPPORT |
37 | extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, | 37 | extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, |
38 | GElf_Shdr *shp, const char *name, size_t *idx); | 38 | GElf_Shdr *shp, const char *name, size_t *idx); |
39 | #endif | 39 | #endif |
40 | 40 | ||
41 | #ifndef DMGL_PARAMS | 41 | #ifndef DMGL_PARAMS |
42 | #define DMGL_NO_OPTS 0 /* For readability... */ | 42 | #define DMGL_NO_OPTS 0 /* For readability... */ |
43 | #define DMGL_PARAMS (1 << 0) /* Include function args */ | 43 | #define DMGL_PARAMS (1 << 0) /* Include function args */ |
44 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | 44 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ |
45 | #endif | 45 | #endif |
46 | 46 | ||
47 | /** struct symbol - symtab entry | 47 | /** struct symbol - symtab entry |
48 | * | 48 | * |
49 | * @ignore - resolvable but tools ignore it (e.g. idle routines) | 49 | * @ignore - resolvable but tools ignore it (e.g. idle routines) |
50 | */ | 50 | */ |
51 | struct symbol { | 51 | struct symbol { |
52 | struct rb_node rb_node; | 52 | struct rb_node rb_node; |
53 | u64 start; | 53 | u64 start; |
54 | u64 end; | 54 | u64 end; |
55 | u16 namelen; | 55 | u16 namelen; |
56 | u8 binding; | 56 | u8 binding; |
57 | bool ignore; | 57 | bool ignore; |
58 | char name[0]; | 58 | char name[0]; |
59 | }; | 59 | }; |
60 | 60 | ||
61 | void symbol__delete(struct symbol *sym); | 61 | void symbol__delete(struct symbol *sym); |
62 | void symbols__delete(struct rb_root *symbols); | 62 | void symbols__delete(struct rb_root *symbols); |
63 | 63 | ||
64 | /* symbols__for_each_entry - iterate over symbols (rb_root) | 64 | /* symbols__for_each_entry - iterate over symbols (rb_root) |
65 | * | 65 | * |
66 | * @symbols: the rb_root of symbols | 66 | * @symbols: the rb_root of symbols |
67 | * @pos: the 'struct symbol *' to use as a loop cursor | 67 | * @pos: the 'struct symbol *' to use as a loop cursor |
68 | * @nd: the 'struct rb_node *' to use as a temporary storage | 68 | * @nd: the 'struct rb_node *' to use as a temporary storage |
69 | */ | 69 | */ |
70 | #define symbols__for_each_entry(symbols, pos, nd) \ | 70 | #define symbols__for_each_entry(symbols, pos, nd) \ |
71 | for (nd = rb_first(symbols); \ | 71 | for (nd = rb_first(symbols); \ |
72 | nd && (pos = rb_entry(nd, struct symbol, rb_node)); \ | 72 | nd && (pos = rb_entry(nd, struct symbol, rb_node)); \ |
73 | nd = rb_next(nd)) | 73 | nd = rb_next(nd)) |
74 | 74 | ||
75 | static inline size_t symbol__size(const struct symbol *sym) | 75 | static inline size_t symbol__size(const struct symbol *sym) |
76 | { | 76 | { |
77 | return sym->end - sym->start; | 77 | return sym->end - sym->start; |
78 | } | 78 | } |
79 | 79 | ||
80 | struct strlist; | 80 | struct strlist; |
81 | 81 | ||
82 | struct symbol_conf { | 82 | struct symbol_conf { |
83 | unsigned short priv_size; | 83 | unsigned short priv_size; |
84 | unsigned short nr_events; | 84 | unsigned short nr_events; |
85 | bool try_vmlinux_path, | 85 | bool try_vmlinux_path, |
86 | ignore_vmlinux, | 86 | ignore_vmlinux, |
87 | ignore_vmlinux_buildid, | 87 | ignore_vmlinux_buildid, |
88 | show_kernel_path, | 88 | show_kernel_path, |
89 | use_modules, | 89 | use_modules, |
90 | sort_by_name, | 90 | sort_by_name, |
91 | show_nr_samples, | 91 | show_nr_samples, |
92 | show_total_period, | 92 | show_total_period, |
93 | use_callchain, | 93 | use_callchain, |
94 | cumulate_callchain, | 94 | cumulate_callchain, |
95 | exclude_other, | 95 | exclude_other, |
96 | show_cpu_utilization, | 96 | show_cpu_utilization, |
97 | initialized, | 97 | initialized, |
98 | kptr_restrict, | 98 | kptr_restrict, |
99 | annotate_asm_raw, | 99 | annotate_asm_raw, |
100 | annotate_src, | 100 | annotate_src, |
101 | event_group, | 101 | event_group, |
102 | demangle, | 102 | demangle, |
103 | demangle_kernel, | 103 | demangle_kernel, |
104 | filter_relative, | 104 | filter_relative, |
105 | show_hist_headers, | 105 | show_hist_headers, |
106 | branch_callstack; | 106 | branch_callstack; |
107 | const char *vmlinux_name, | 107 | const char *vmlinux_name, |
108 | *kallsyms_name, | 108 | *kallsyms_name, |
109 | *source_prefix, | 109 | *source_prefix, |
110 | *field_sep; | 110 | *field_sep; |
111 | const char *default_guest_vmlinux_name, | 111 | const char *default_guest_vmlinux_name, |
112 | *default_guest_kallsyms, | 112 | *default_guest_kallsyms, |
113 | *default_guest_modules; | 113 | *default_guest_modules; |
114 | const char *guestmount; | 114 | const char *guestmount; |
115 | const char *dso_list_str, | 115 | const char *dso_list_str, |
116 | *comm_list_str, | 116 | *comm_list_str, |
117 | *sym_list_str, | 117 | *sym_list_str, |
118 | *col_width_list_str; | 118 | *col_width_list_str; |
119 | struct strlist *dso_list, | 119 | struct strlist *dso_list, |
120 | *comm_list, | 120 | *comm_list, |
121 | *sym_list, | 121 | *sym_list, |
122 | *dso_from_list, | 122 | *dso_from_list, |
123 | *dso_to_list, | 123 | *dso_to_list, |
124 | *sym_from_list, | 124 | *sym_from_list, |
125 | *sym_to_list; | 125 | *sym_to_list; |
126 | const char *symfs; | 126 | const char *symfs; |
127 | }; | 127 | }; |
128 | 128 | ||
129 | extern struct symbol_conf symbol_conf; | 129 | extern struct symbol_conf symbol_conf; |
130 | 130 | ||
131 | static inline int __symbol__join_symfs(char *bf, size_t size, const char *path) | 131 | static inline int __symbol__join_symfs(char *bf, size_t size, const char *path) |
132 | { | 132 | { |
133 | return path__join(bf, size, symbol_conf.symfs, path); | 133 | return path__join(bf, size, symbol_conf.symfs, path); |
134 | } | 134 | } |
135 | 135 | ||
136 | #define symbol__join_symfs(bf, path) __symbol__join_symfs(bf, sizeof(bf), path) | 136 | #define symbol__join_symfs(bf, path) __symbol__join_symfs(bf, sizeof(bf), path) |
137 | 137 | ||
138 | extern int vmlinux_path__nr_entries; | 138 | extern int vmlinux_path__nr_entries; |
139 | extern char **vmlinux_path; | 139 | extern char **vmlinux_path; |
140 | 140 | ||
141 | static inline void *symbol__priv(struct symbol *sym) | 141 | static inline void *symbol__priv(struct symbol *sym) |
142 | { | 142 | { |
143 | return ((void *)sym) - symbol_conf.priv_size; | 143 | return ((void *)sym) - symbol_conf.priv_size; |
144 | } | 144 | } |
145 | 145 | ||
146 | struct ref_reloc_sym { | 146 | struct ref_reloc_sym { |
147 | const char *name; | 147 | const char *name; |
148 | u64 addr; | 148 | u64 addr; |
149 | u64 unrelocated_addr; | 149 | u64 unrelocated_addr; |
150 | }; | 150 | }; |
151 | 151 | ||
152 | struct map_symbol { | 152 | struct map_symbol { |
153 | struct map *map; | 153 | struct map *map; |
154 | struct symbol *sym; | 154 | struct symbol *sym; |
155 | bool unfolded; | 155 | bool unfolded; |
156 | bool has_children; | 156 | bool has_children; |
157 | }; | 157 | }; |
158 | 158 | ||
159 | struct addr_map_symbol { | 159 | struct addr_map_symbol { |
160 | struct map *map; | 160 | struct map *map; |
161 | struct symbol *sym; | 161 | struct symbol *sym; |
162 | u64 addr; | 162 | u64 addr; |
163 | u64 al_addr; | 163 | u64 al_addr; |
164 | }; | 164 | }; |
165 | 165 | ||
166 | struct branch_info { | 166 | struct branch_info { |
167 | struct addr_map_symbol from; | 167 | struct addr_map_symbol from; |
168 | struct addr_map_symbol to; | 168 | struct addr_map_symbol to; |
169 | struct branch_flags flags; | 169 | struct branch_flags flags; |
170 | }; | 170 | }; |
171 | 171 | ||
172 | struct mem_info { | 172 | struct mem_info { |
173 | struct addr_map_symbol iaddr; | 173 | struct addr_map_symbol iaddr; |
174 | struct addr_map_symbol daddr; | 174 | struct addr_map_symbol daddr; |
175 | union perf_mem_data_src data_src; | 175 | union perf_mem_data_src data_src; |
176 | }; | 176 | }; |
177 | 177 | ||
178 | struct addr_location { | 178 | struct addr_location { |
179 | struct machine *machine; | 179 | struct machine *machine; |
180 | struct thread *thread; | 180 | struct thread *thread; |
181 | struct map *map; | 181 | struct map *map; |
182 | struct symbol *sym; | 182 | struct symbol *sym; |
183 | u64 addr; | 183 | u64 addr; |
184 | char level; | 184 | char level; |
185 | u8 filtered; | 185 | u8 filtered; |
186 | u8 cpumode; | 186 | u8 cpumode; |
187 | s32 cpu; | 187 | s32 cpu; |
188 | }; | 188 | }; |
189 | 189 | ||
190 | struct symsrc { | 190 | struct symsrc { |
191 | char *name; | 191 | char *name; |
192 | int fd; | 192 | int fd; |
193 | enum dso_binary_type type; | 193 | enum dso_binary_type type; |
194 | 194 | ||
195 | #ifdef HAVE_LIBELF_SUPPORT | 195 | #ifdef HAVE_LIBELF_SUPPORT |
196 | Elf *elf; | 196 | Elf *elf; |
197 | GElf_Ehdr ehdr; | 197 | GElf_Ehdr ehdr; |
198 | 198 | ||
199 | Elf_Scn *opdsec; | 199 | Elf_Scn *opdsec; |
200 | size_t opdidx; | 200 | size_t opdidx; |
201 | GElf_Shdr opdshdr; | 201 | GElf_Shdr opdshdr; |
202 | 202 | ||
203 | Elf_Scn *symtab; | 203 | Elf_Scn *symtab; |
204 | GElf_Shdr symshdr; | 204 | GElf_Shdr symshdr; |
205 | 205 | ||
206 | Elf_Scn *dynsym; | 206 | Elf_Scn *dynsym; |
207 | size_t dynsym_idx; | 207 | size_t dynsym_idx; |
208 | GElf_Shdr dynshdr; | 208 | GElf_Shdr dynshdr; |
209 | 209 | ||
210 | bool adjust_symbols; | 210 | bool adjust_symbols; |
211 | bool is_64_bit; | 211 | bool is_64_bit; |
212 | #endif | 212 | #endif |
213 | }; | 213 | }; |
214 | 214 | ||
215 | void symsrc__destroy(struct symsrc *ss); | 215 | void symsrc__destroy(struct symsrc *ss); |
216 | int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | 216 | int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, |
217 | enum dso_binary_type type); | 217 | enum dso_binary_type type); |
218 | bool symsrc__has_symtab(struct symsrc *ss); | 218 | bool symsrc__has_symtab(struct symsrc *ss); |
219 | bool symsrc__possibly_runtime(struct symsrc *ss); | 219 | bool symsrc__possibly_runtime(struct symsrc *ss); |
220 | 220 | ||
221 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter); | 221 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter); |
222 | int dso__load_vmlinux(struct dso *dso, struct map *map, | 222 | int dso__load_vmlinux(struct dso *dso, struct map *map, |
223 | const char *vmlinux, bool vmlinux_allocated, | 223 | const char *vmlinux, bool vmlinux_allocated, |
224 | symbol_filter_t filter); | 224 | symbol_filter_t filter); |
225 | int dso__load_vmlinux_path(struct dso *dso, struct map *map, | 225 | int dso__load_vmlinux_path(struct dso *dso, struct map *map, |
226 | symbol_filter_t filter); | 226 | symbol_filter_t filter); |
227 | int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, | 227 | int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, |
228 | symbol_filter_t filter); | 228 | symbol_filter_t filter); |
229 | 229 | ||
230 | struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, | 230 | struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, |
231 | u64 addr); | 231 | u64 addr); |
232 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | 232 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, |
233 | const char *name); | 233 | const char *name); |
234 | struct symbol *symbol__next_by_name(struct symbol *sym); | ||
234 | 235 | ||
235 | struct symbol *dso__first_symbol(struct dso *dso, enum map_type type); | 236 | struct symbol *dso__first_symbol(struct dso *dso, enum map_type type); |
236 | struct symbol *dso__next_symbol(struct symbol *sym); | 237 | struct symbol *dso__next_symbol(struct symbol *sym); |
237 | 238 | ||
238 | enum dso_type dso__type_fd(int fd); | 239 | enum dso_type dso__type_fd(int fd); |
239 | 240 | ||
240 | int filename__read_build_id(const char *filename, void *bf, size_t size); | 241 | int filename__read_build_id(const char *filename, void *bf, size_t size); |
241 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | 242 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); |
242 | int modules__parse(const char *filename, void *arg, | 243 | int modules__parse(const char *filename, void *arg, |
243 | int (*process_module)(void *arg, const char *name, | 244 | int (*process_module)(void *arg, const char *name, |
244 | u64 start)); | 245 | u64 start)); |
245 | int filename__read_debuglink(const char *filename, char *debuglink, | 246 | int filename__read_debuglink(const char *filename, char *debuglink, |
246 | size_t size); | 247 | size_t size); |
247 | 248 | ||
248 | struct perf_session_env; | 249 | struct perf_session_env; |
249 | int symbol__init(struct perf_session_env *env); | 250 | int symbol__init(struct perf_session_env *env); |
250 | void symbol__exit(void); | 251 | void symbol__exit(void); |
251 | void symbol__elf_init(void); | 252 | void symbol__elf_init(void); |
252 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); | 253 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); |
253 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | 254 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, |
254 | const struct addr_location *al, FILE *fp); | 255 | const struct addr_location *al, FILE *fp); |
255 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); | 256 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); |
256 | size_t symbol__fprintf(struct symbol *sym, FILE *fp); | 257 | size_t symbol__fprintf(struct symbol *sym, FILE *fp); |
257 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 258 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
258 | bool symbol__restricted_filename(const char *filename, | 259 | bool symbol__restricted_filename(const char *filename, |
259 | const char *restricted_filename); | 260 | const char *restricted_filename); |
260 | bool symbol__is_idle(struct symbol *sym); | 261 | bool symbol__is_idle(struct symbol *sym); |
261 | 262 | ||
262 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, | 263 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, |
263 | struct symsrc *runtime_ss, symbol_filter_t filter, | 264 | struct symsrc *runtime_ss, symbol_filter_t filter, |
264 | int kmodule); | 265 | int kmodule); |
265 | int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, | 266 | int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, |
266 | struct map *map, symbol_filter_t filter); | 267 | struct map *map, symbol_filter_t filter); |
267 | 268 | ||
268 | void symbols__insert(struct rb_root *symbols, struct symbol *sym); | 269 | void symbols__insert(struct rb_root *symbols, struct symbol *sym); |
269 | void symbols__fixup_duplicate(struct rb_root *symbols); | 270 | void symbols__fixup_duplicate(struct rb_root *symbols); |
270 | void symbols__fixup_end(struct rb_root *symbols); | 271 | void symbols__fixup_end(struct rb_root *symbols); |
271 | void __map_groups__fixup_end(struct map_groups *mg, enum map_type type); | 272 | void __map_groups__fixup_end(struct map_groups *mg, enum map_type type); |
272 | 273 | ||
273 | typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data); | 274 | typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data); |
274 | int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, | 275 | int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, |
275 | bool *is_64_bit); | 276 | bool *is_64_bit); |
276 | 277 | ||
277 | #define PERF_KCORE_EXTRACT "/tmp/perf-kcore-XXXXXX" | 278 | #define PERF_KCORE_EXTRACT "/tmp/perf-kcore-XXXXXX" |
278 | 279 | ||
279 | struct kcore_extract { | 280 | struct kcore_extract { |
280 | char *kcore_filename; | 281 | char *kcore_filename; |
281 | u64 addr; | 282 | u64 addr; |
282 | u64 offs; | 283 | u64 offs; |
283 | u64 len; | 284 | u64 len; |
284 | char extract_filename[sizeof(PERF_KCORE_EXTRACT)]; | 285 | char extract_filename[sizeof(PERF_KCORE_EXTRACT)]; |
285 | int fd; | 286 | int fd; |
286 | }; | 287 | }; |
287 | 288 | ||
288 | int kcore_extract__create(struct kcore_extract *kce); | 289 | int kcore_extract__create(struct kcore_extract *kce); |
289 | void kcore_extract__delete(struct kcore_extract *kce); | 290 | void kcore_extract__delete(struct kcore_extract *kce); |
290 | 291 | ||
291 | int kcore_copy(const char *from_dir, const char *to_dir); | 292 | int kcore_copy(const char *from_dir, const char *to_dir); |
292 | int compare_proc_modules(const char *from, const char *to); | 293 | int compare_proc_modules(const char *from, const char *to); |
293 | 294 | ||
294 | int setup_list(struct strlist **list, const char *list_str, | 295 | int setup_list(struct strlist **list, const char *list_str, |
295 | const char *list_name); | 296 | const char *list_name); |
296 | 297 | ||
297 | #endif /* __PERF_SYMBOL */ | 298 | #endif /* __PERF_SYMBOL */ |
298 | 299 |