diff options
Diffstat (limited to 'tools/perf/util/probe-finder.c')
-rw-r--r-- | tools/perf/util/probe-finder.c | 201 |
1 files changed, 104 insertions, 97 deletions
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 53d219bddb48..3e44a3e36519 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -116,29 +116,37 @@ static const Dwfl_Callbacks offline_callbacks = { | |||
116 | }; | 116 | }; |
117 | 117 | ||
118 | /* Get a Dwarf from offline image */ | 118 | /* Get a Dwarf from offline image */ |
119 | static Dwarf *dwfl_init_offline_dwarf(int fd, Dwfl **dwflp, Dwarf_Addr *bias) | 119 | static int debuginfo__init_offline_dwarf(struct debuginfo *self, |
120 | const char *path) | ||
120 | { | 121 | { |
121 | Dwfl_Module *mod; | 122 | Dwfl_Module *mod; |
122 | Dwarf *dbg = NULL; | 123 | int fd; |
123 | 124 | ||
124 | if (!dwflp) | 125 | fd = open(path, O_RDONLY); |
125 | return NULL; | 126 | if (fd < 0) |
127 | return fd; | ||
126 | 128 | ||
127 | *dwflp = dwfl_begin(&offline_callbacks); | 129 | self->dwfl = dwfl_begin(&offline_callbacks); |
128 | if (!*dwflp) | 130 | if (!self->dwfl) |
129 | return NULL; | 131 | goto error; |
130 | 132 | ||
131 | mod = dwfl_report_offline(*dwflp, "", "", fd); | 133 | mod = dwfl_report_offline(self->dwfl, "", "", fd); |
132 | if (!mod) | 134 | if (!mod) |
133 | goto error; | 135 | goto error; |
134 | 136 | ||
135 | dbg = dwfl_module_getdwarf(mod, bias); | 137 | self->dbg = dwfl_module_getdwarf(mod, &self->bias); |
136 | if (!dbg) { | 138 | if (!self->dbg) |
139 | goto error; | ||
140 | |||
141 | return 0; | ||
137 | error: | 142 | error: |
138 | dwfl_end(*dwflp); | 143 | if (self->dwfl) |
139 | *dwflp = NULL; | 144 | dwfl_end(self->dwfl); |
140 | } | 145 | else |
141 | return dbg; | 146 | close(fd); |
147 | memset(self, 0, sizeof(*self)); | ||
148 | |||
149 | return -ENOENT; | ||
142 | } | 150 | } |
143 | 151 | ||
144 | #if _ELFUTILS_PREREQ(0, 148) | 152 | #if _ELFUTILS_PREREQ(0, 148) |
@@ -174,53 +182,82 @@ static const Dwfl_Callbacks kernel_callbacks = { | |||
174 | }; | 182 | }; |
175 | 183 | ||
176 | /* Get a Dwarf from live kernel image */ | 184 | /* Get a Dwarf from live kernel image */ |
177 | static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr, Dwfl **dwflp, | 185 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, |
178 | Dwarf_Addr *bias) | 186 | Dwarf_Addr addr) |
179 | { | 187 | { |
180 | Dwarf *dbg; | 188 | self->dwfl = dwfl_begin(&kernel_callbacks); |
181 | 189 | if (!self->dwfl) | |
182 | if (!dwflp) | 190 | return -EINVAL; |
183 | return NULL; | ||
184 | |||
185 | *dwflp = dwfl_begin(&kernel_callbacks); | ||
186 | if (!*dwflp) | ||
187 | return NULL; | ||
188 | 191 | ||
189 | /* Load the kernel dwarves: Don't care the result here */ | 192 | /* Load the kernel dwarves: Don't care the result here */ |
190 | dwfl_linux_kernel_report_kernel(*dwflp); | 193 | dwfl_linux_kernel_report_kernel(self->dwfl); |
191 | dwfl_linux_kernel_report_modules(*dwflp); | 194 | dwfl_linux_kernel_report_modules(self->dwfl); |
192 | 195 | ||
193 | dbg = dwfl_addrdwarf(*dwflp, addr, bias); | 196 | self->dbg = dwfl_addrdwarf(self->dwfl, addr, &self->bias); |
194 | /* Here, check whether we could get a real dwarf */ | 197 | /* Here, check whether we could get a real dwarf */ |
195 | if (!dbg) { | 198 | if (!self->dbg) { |
196 | pr_debug("Failed to find kernel dwarf at %lx\n", | 199 | pr_debug("Failed to find kernel dwarf at %lx\n", |
197 | (unsigned long)addr); | 200 | (unsigned long)addr); |
198 | dwfl_end(*dwflp); | 201 | dwfl_end(self->dwfl); |
199 | *dwflp = NULL; | 202 | memset(self, 0, sizeof(*self)); |
203 | return -ENOENT; | ||
200 | } | 204 | } |
201 | return dbg; | 205 | |
206 | return 0; | ||
202 | } | 207 | } |
203 | #else | 208 | #else |
204 | /* With older elfutils, this just support kernel module... */ | 209 | /* With older elfutils, this just support kernel module... */ |
205 | static Dwarf *dwfl_init_live_kernel_dwarf(Dwarf_Addr addr __used, Dwfl **dwflp, | 210 | static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self, |
206 | Dwarf_Addr *bias) | 211 | Dwarf_Addr addr __used) |
207 | { | 212 | { |
208 | int fd; | ||
209 | const char *path = kernel_get_module_path("kernel"); | 213 | const char *path = kernel_get_module_path("kernel"); |
210 | 214 | ||
211 | if (!path) { | 215 | if (!path) { |
212 | pr_err("Failed to find vmlinux path\n"); | 216 | pr_err("Failed to find vmlinux path\n"); |
213 | return NULL; | 217 | return -ENOENT; |
214 | } | 218 | } |
215 | 219 | ||
216 | pr_debug2("Use file %s for debuginfo\n", path); | 220 | pr_debug2("Use file %s for debuginfo\n", path); |
217 | fd = open(path, O_RDONLY); | 221 | return debuginfo__init_offline_dwarf(self, path); |
218 | if (fd < 0) | 222 | } |
223 | #endif | ||
224 | |||
225 | struct debuginfo *debuginfo__new(const char *path) | ||
226 | { | ||
227 | struct debuginfo *self = zalloc(sizeof(struct debuginfo)); | ||
228 | if (!self) | ||
219 | return NULL; | 229 | return NULL; |
220 | 230 | ||
221 | return dwfl_init_offline_dwarf(fd, dwflp, bias); | 231 | if (debuginfo__init_offline_dwarf(self, path) < 0) { |
232 | free(self); | ||
233 | self = NULL; | ||
234 | } | ||
235 | |||
236 | return self; | ||
237 | } | ||
238 | |||
239 | struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) | ||
240 | { | ||
241 | struct debuginfo *self = zalloc(sizeof(struct debuginfo)); | ||
242 | if (!self) | ||
243 | return NULL; | ||
244 | |||
245 | if (debuginfo__init_online_kernel_dwarf(self, (Dwarf_Addr)addr) < 0) { | ||
246 | free(self); | ||
247 | self = NULL; | ||
248 | } | ||
249 | |||
250 | return self; | ||
251 | } | ||
252 | |||
253 | void debuginfo__delete(struct debuginfo *self) | ||
254 | { | ||
255 | if (self) { | ||
256 | if (self->dwfl) | ||
257 | dwfl_end(self->dwfl); | ||
258 | free(self); | ||
259 | } | ||
222 | } | 260 | } |
223 | #endif | ||
224 | 261 | ||
225 | /* | 262 | /* |
226 | * Probe finder related functions | 263 | * Probe finder related functions |
@@ -949,28 +986,18 @@ static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data) | |||
949 | } | 986 | } |
950 | 987 | ||
951 | /* Find probe points from debuginfo */ | 988 | /* Find probe points from debuginfo */ |
952 | static int find_probes(int fd, struct probe_finder *pf) | 989 | static int debuginfo__find_probes(struct debuginfo *self, |
990 | struct probe_finder *pf) | ||
953 | { | 991 | { |
954 | struct perf_probe_point *pp = &pf->pev->point; | 992 | struct perf_probe_point *pp = &pf->pev->point; |
955 | Dwarf_Off off, noff; | 993 | Dwarf_Off off, noff; |
956 | size_t cuhl; | 994 | size_t cuhl; |
957 | Dwarf_Die *diep; | 995 | Dwarf_Die *diep; |
958 | Dwarf *dbg = NULL; | ||
959 | Dwfl *dwfl; | ||
960 | Dwarf_Addr bias; /* Currently ignored */ | ||
961 | int ret = 0; | 996 | int ret = 0; |
962 | 997 | ||
963 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | ||
964 | if (!dbg) { | ||
965 | pr_warning("No debug information found in the vmlinux - " | ||
966 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
967 | close(fd); /* Without dwfl_end(), fd isn't closed. */ | ||
968 | return -EBADF; | ||
969 | } | ||
970 | |||
971 | #if _ELFUTILS_PREREQ(0, 142) | 998 | #if _ELFUTILS_PREREQ(0, 142) |
972 | /* Get the call frame information from this dwarf */ | 999 | /* Get the call frame information from this dwarf */ |
973 | pf->cfi = dwarf_getcfi(dbg); | 1000 | pf->cfi = dwarf_getcfi(self->dbg); |
974 | #endif | 1001 | #endif |
975 | 1002 | ||
976 | off = 0; | 1003 | off = 0; |
@@ -989,7 +1016,8 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
989 | .data = pf, | 1016 | .data = pf, |
990 | }; | 1017 | }; |
991 | 1018 | ||
992 | dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); | 1019 | dwarf_getpubnames(self->dbg, pubname_search_cb, |
1020 | &pubname_param, 0); | ||
993 | if (pubname_param.found) { | 1021 | if (pubname_param.found) { |
994 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); | 1022 | ret = probe_point_search_cb(&pf->sp_die, &probe_param); |
995 | if (ret) | 1023 | if (ret) |
@@ -998,9 +1026,9 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
998 | } | 1026 | } |
999 | 1027 | ||
1000 | /* Loop on CUs (Compilation Unit) */ | 1028 | /* Loop on CUs (Compilation Unit) */ |
1001 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { | 1029 | while (!dwarf_nextcu(self->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
1002 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1030 | /* Get the DIE(Debugging Information Entry) of this CU */ |
1003 | diep = dwarf_offdie(dbg, off + cuhl, &pf->cu_die); | 1031 | diep = dwarf_offdie(self->dbg, off + cuhl, &pf->cu_die); |
1004 | if (!diep) | 1032 | if (!diep) |
1005 | continue; | 1033 | continue; |
1006 | 1034 | ||
@@ -1027,8 +1055,6 @@ static int find_probes(int fd, struct probe_finder *pf) | |||
1027 | 1055 | ||
1028 | found: | 1056 | found: |
1029 | line_list__free(&pf->lcache); | 1057 | line_list__free(&pf->lcache); |
1030 | if (dwfl) | ||
1031 | dwfl_end(dwfl); | ||
1032 | 1058 | ||
1033 | return ret; | 1059 | return ret; |
1034 | } | 1060 | } |
@@ -1074,8 +1100,9 @@ static int add_probe_trace_event(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
1074 | } | 1100 | } |
1075 | 1101 | ||
1076 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ | 1102 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
1077 | int find_probe_trace_events(int fd, struct perf_probe_event *pev, | 1103 | int debuginfo__find_trace_events(struct debuginfo *self, |
1078 | struct probe_trace_event **tevs, int max_tevs) | 1104 | struct perf_probe_event *pev, |
1105 | struct probe_trace_event **tevs, int max_tevs) | ||
1079 | { | 1106 | { |
1080 | struct trace_event_finder tf = { | 1107 | struct trace_event_finder tf = { |
1081 | .pf = {.pev = pev, .callback = add_probe_trace_event}, | 1108 | .pf = {.pev = pev, .callback = add_probe_trace_event}, |
@@ -1090,7 +1117,7 @@ int find_probe_trace_events(int fd, struct perf_probe_event *pev, | |||
1090 | tf.tevs = *tevs; | 1117 | tf.tevs = *tevs; |
1091 | tf.ntevs = 0; | 1118 | tf.ntevs = 0; |
1092 | 1119 | ||
1093 | ret = find_probes(fd, &tf.pf); | 1120 | ret = debuginfo__find_probes(self, &tf.pf); |
1094 | if (ret < 0) { | 1121 | if (ret < 0) { |
1095 | free(*tevs); | 1122 | free(*tevs); |
1096 | *tevs = NULL; | 1123 | *tevs = NULL; |
@@ -1184,9 +1211,10 @@ out: | |||
1184 | } | 1211 | } |
1185 | 1212 | ||
1186 | /* Find available variables at given probe point */ | 1213 | /* Find available variables at given probe point */ |
1187 | int find_available_vars_at(int fd, struct perf_probe_event *pev, | 1214 | int debuginfo__find_available_vars_at(struct debuginfo *self, |
1188 | struct variable_list **vls, int max_vls, | 1215 | struct perf_probe_event *pev, |
1189 | bool externs) | 1216 | struct variable_list **vls, |
1217 | int max_vls, bool externs) | ||
1190 | { | 1218 | { |
1191 | struct available_var_finder af = { | 1219 | struct available_var_finder af = { |
1192 | .pf = {.pev = pev, .callback = add_available_vars}, | 1220 | .pf = {.pev = pev, .callback = add_available_vars}, |
@@ -1201,7 +1229,7 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
1201 | af.vls = *vls; | 1229 | af.vls = *vls; |
1202 | af.nvls = 0; | 1230 | af.nvls = 0; |
1203 | 1231 | ||
1204 | ret = find_probes(fd, &af.pf); | 1232 | ret = debuginfo__find_probes(self, &af.pf); |
1205 | if (ret < 0) { | 1233 | if (ret < 0) { |
1206 | /* Free vlist for error */ | 1234 | /* Free vlist for error */ |
1207 | while (af.nvls--) { | 1235 | while (af.nvls--) { |
@@ -1219,28 +1247,19 @@ int find_available_vars_at(int fd, struct perf_probe_event *pev, | |||
1219 | } | 1247 | } |
1220 | 1248 | ||
1221 | /* Reverse search */ | 1249 | /* Reverse search */ |
1222 | int find_perf_probe_point(unsigned long addr, struct perf_probe_point *ppt) | 1250 | int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, |
1251 | struct perf_probe_point *ppt) | ||
1223 | { | 1252 | { |
1224 | Dwarf_Die cudie, spdie, indie; | 1253 | Dwarf_Die cudie, spdie, indie; |
1225 | Dwarf *dbg = NULL; | 1254 | Dwarf_Addr _addr, baseaddr; |
1226 | Dwfl *dwfl = NULL; | ||
1227 | Dwarf_Addr _addr, baseaddr, bias = 0; | ||
1228 | const char *fname = NULL, *func = NULL, *tmp; | 1255 | const char *fname = NULL, *func = NULL, *tmp; |
1229 | int baseline = 0, lineno = 0, ret = 0; | 1256 | int baseline = 0, lineno = 0, ret = 0; |
1230 | 1257 | ||
1231 | /* Open the live linux kernel */ | ||
1232 | dbg = dwfl_init_live_kernel_dwarf(addr, &dwfl, &bias); | ||
1233 | if (!dbg) { | ||
1234 | pr_warning("No debug information found in the vmlinux - " | ||
1235 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
1236 | ret = -EINVAL; | ||
1237 | goto end; | ||
1238 | } | ||
1239 | |||
1240 | /* Adjust address with bias */ | 1258 | /* Adjust address with bias */ |
1241 | addr += bias; | 1259 | addr += self->bias; |
1260 | |||
1242 | /* Find cu die */ | 1261 | /* Find cu die */ |
1243 | if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr - bias, &cudie)) { | 1262 | if (!dwarf_addrdie(self->dbg, (Dwarf_Addr)addr - self->bias, &cudie)) { |
1244 | pr_warning("Failed to find debug information for address %lx\n", | 1263 | pr_warning("Failed to find debug information for address %lx\n", |
1245 | addr); | 1264 | addr); |
1246 | ret = -EINVAL; | 1265 | ret = -EINVAL; |
@@ -1316,8 +1335,6 @@ post: | |||
1316 | } | 1335 | } |
1317 | } | 1336 | } |
1318 | end: | 1337 | end: |
1319 | if (dwfl) | ||
1320 | dwfl_end(dwfl); | ||
1321 | if (ret == 0 && (fname || func)) | 1338 | if (ret == 0 && (fname || func)) |
1322 | ret = 1; /* Found a point */ | 1339 | ret = 1; /* Found a point */ |
1323 | return ret; | 1340 | return ret; |
@@ -1427,26 +1444,15 @@ static int find_line_range_by_func(struct line_finder *lf) | |||
1427 | return param.retval; | 1444 | return param.retval; |
1428 | } | 1445 | } |
1429 | 1446 | ||
1430 | int find_line_range(int fd, struct line_range *lr) | 1447 | int debuginfo__find_line_range(struct debuginfo *self, struct line_range *lr) |
1431 | { | 1448 | { |
1432 | struct line_finder lf = {.lr = lr, .found = 0}; | 1449 | struct line_finder lf = {.lr = lr, .found = 0}; |
1433 | int ret = 0; | 1450 | int ret = 0; |
1434 | Dwarf_Off off = 0, noff; | 1451 | Dwarf_Off off = 0, noff; |
1435 | size_t cuhl; | 1452 | size_t cuhl; |
1436 | Dwarf_Die *diep; | 1453 | Dwarf_Die *diep; |
1437 | Dwarf *dbg = NULL; | ||
1438 | Dwfl *dwfl; | ||
1439 | Dwarf_Addr bias; /* Currently ignored */ | ||
1440 | const char *comp_dir; | 1454 | const char *comp_dir; |
1441 | 1455 | ||
1442 | dbg = dwfl_init_offline_dwarf(fd, &dwfl, &bias); | ||
1443 | if (!dbg) { | ||
1444 | pr_warning("No debug information found in the vmlinux - " | ||
1445 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
1446 | close(fd); /* Without dwfl_end(), fd isn't closed. */ | ||
1447 | return -EBADF; | ||
1448 | } | ||
1449 | |||
1450 | /* Fastpath: lookup by function name from .debug_pubnames section */ | 1456 | /* Fastpath: lookup by function name from .debug_pubnames section */ |
1451 | if (lr->function) { | 1457 | if (lr->function) { |
1452 | struct pubname_callback_param pubname_param = { | 1458 | struct pubname_callback_param pubname_param = { |
@@ -1455,7 +1461,8 @@ int find_line_range(int fd, struct line_range *lr) | |||
1455 | struct dwarf_callback_param line_range_param = { | 1461 | struct dwarf_callback_param line_range_param = { |
1456 | .data = (void *)&lf, .retval = 0}; | 1462 | .data = (void *)&lf, .retval = 0}; |
1457 | 1463 | ||
1458 | dwarf_getpubnames(dbg, pubname_search_cb, &pubname_param, 0); | 1464 | dwarf_getpubnames(self->dbg, pubname_search_cb, |
1465 | &pubname_param, 0); | ||
1459 | if (pubname_param.found) { | 1466 | if (pubname_param.found) { |
1460 | line_range_search_cb(&lf.sp_die, &line_range_param); | 1467 | line_range_search_cb(&lf.sp_die, &line_range_param); |
1461 | if (lf.found) | 1468 | if (lf.found) |
@@ -1465,11 +1472,12 @@ int find_line_range(int fd, struct line_range *lr) | |||
1465 | 1472 | ||
1466 | /* Loop on CUs (Compilation Unit) */ | 1473 | /* Loop on CUs (Compilation Unit) */ |
1467 | while (!lf.found && ret >= 0) { | 1474 | while (!lf.found && ret >= 0) { |
1468 | if (dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL) != 0) | 1475 | if (dwarf_nextcu(self->dbg, off, &noff, &cuhl, |
1476 | NULL, NULL, NULL) != 0) | ||
1469 | break; | 1477 | break; |
1470 | 1478 | ||
1471 | /* Get the DIE(Debugging Information Entry) of this CU */ | 1479 | /* Get the DIE(Debugging Information Entry) of this CU */ |
1472 | diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); | 1480 | diep = dwarf_offdie(self->dbg, off + cuhl, &lf.cu_die); |
1473 | if (!diep) | 1481 | if (!diep) |
1474 | continue; | 1482 | continue; |
1475 | 1483 | ||
@@ -1503,7 +1511,6 @@ found: | |||
1503 | } | 1511 | } |
1504 | 1512 | ||
1505 | pr_debug("path: %s\n", lr->path); | 1513 | pr_debug("path: %s\n", lr->path); |
1506 | dwfl_end(dwfl); | ||
1507 | return (ret < 0) ? ret : lf.found; | 1514 | return (ret < 0) ? ret : lf.found; |
1508 | } | 1515 | } |
1509 | 1516 | ||