diff options
Diffstat (limited to 'tools/perf/util/machine.c')
| -rw-r--r-- | tools/perf/util/machine.c | 142 |
1 files changed, 89 insertions, 53 deletions
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 34fc7c8672e4..d97309c87bd6 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
| @@ -21,7 +21,7 @@ static void dsos__init(struct dsos *dsos) | |||
| 21 | 21 | ||
| 22 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | 22 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) |
| 23 | { | 23 | { |
| 24 | map_groups__init(&machine->kmaps); | 24 | map_groups__init(&machine->kmaps, machine); |
| 25 | RB_CLEAR_NODE(&machine->rb_node); | 25 | RB_CLEAR_NODE(&machine->rb_node); |
| 26 | dsos__init(&machine->user_dsos); | 26 | dsos__init(&machine->user_dsos); |
| 27 | dsos__init(&machine->kernel_dsos); | 27 | dsos__init(&machine->kernel_dsos); |
| @@ -32,7 +32,6 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
| 32 | 32 | ||
| 33 | machine->vdso_info = NULL; | 33 | machine->vdso_info = NULL; |
| 34 | 34 | ||
| 35 | machine->kmaps.machine = machine; | ||
| 36 | machine->pid = pid; | 35 | machine->pid = pid; |
| 37 | 36 | ||
| 38 | machine->symbol_filter = NULL; | 37 | machine->symbol_filter = NULL; |
| @@ -319,7 +318,7 @@ static void machine__update_thread_pid(struct machine *machine, | |||
| 319 | goto out_err; | 318 | goto out_err; |
| 320 | 319 | ||
| 321 | if (!leader->mg) | 320 | if (!leader->mg) |
| 322 | leader->mg = map_groups__new(); | 321 | leader->mg = map_groups__new(machine); |
| 323 | 322 | ||
| 324 | if (!leader->mg) | 323 | if (!leader->mg) |
| 325 | goto out_err; | 324 | goto out_err; |
| @@ -465,6 +464,7 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
| 465 | { | 464 | { |
| 466 | struct map *map; | 465 | struct map *map; |
| 467 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | 466 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); |
| 467 | bool compressed; | ||
| 468 | 468 | ||
| 469 | if (dso == NULL) | 469 | if (dso == NULL) |
| 470 | return NULL; | 470 | return NULL; |
| @@ -477,6 +477,11 @@ struct map *machine__new_module(struct machine *machine, u64 start, | |||
| 477 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; | 477 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; |
| 478 | else | 478 | else |
| 479 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; | 479 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; |
| 480 | |||
| 481 | /* _KMODULE_COMP should be next to _KMODULE */ | ||
| 482 | if (is_kernel_module(filename, &compressed) && compressed) | ||
| 483 | dso->symtab_type++; | ||
| 484 | |||
| 480 | map_groups__insert(&machine->kmaps, map); | 485 | map_groups__insert(&machine->kmaps, map); |
| 481 | return map; | 486 | return map; |
| 482 | } | 487 | } |
| @@ -862,8 +867,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg, | |||
| 862 | struct map *map; | 867 | struct map *map; |
| 863 | char *long_name; | 868 | char *long_name; |
| 864 | 869 | ||
| 865 | if (dot == NULL || strcmp(dot, ".ko")) | 870 | if (dot == NULL) |
| 866 | continue; | 871 | continue; |
| 872 | |||
| 873 | /* On some system, modules are compressed like .ko.gz */ | ||
| 874 | if (is_supported_compression(dot + 1) && | ||
| 875 | is_kmodule_extension(dot - 2)) | ||
| 876 | dot -= 3; | ||
| 877 | |||
| 867 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | 878 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", |
| 868 | (int)(dot - dent->d_name), dent->d_name); | 879 | (int)(dot - dent->d_name), dent->d_name); |
| 869 | 880 | ||
| @@ -1045,6 +1056,11 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
| 1045 | dot = strrchr(name, '.'); | 1056 | dot = strrchr(name, '.'); |
| 1046 | if (dot == NULL) | 1057 | if (dot == NULL) |
| 1047 | goto out_problem; | 1058 | goto out_problem; |
| 1059 | /* On some system, modules are compressed like .ko.gz */ | ||
| 1060 | if (is_supported_compression(dot + 1)) | ||
| 1061 | dot -= 3; | ||
| 1062 | if (!is_kmodule_extension(dot + 1)) | ||
| 1063 | goto out_problem; | ||
| 1048 | snprintf(short_module_name, sizeof(short_module_name), | 1064 | snprintf(short_module_name, sizeof(short_module_name), |
| 1049 | "[%.*s]", (int)(dot - name), name); | 1065 | "[%.*s]", (int)(dot - name), name); |
| 1050 | strxfrchar(short_module_name, '-', '_'); | 1066 | strxfrchar(short_module_name, '-', '_'); |
| @@ -1069,8 +1085,20 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
| 1069 | * Should be there already, from the build-id table in | 1085 | * Should be there already, from the build-id table in |
| 1070 | * the header. | 1086 | * the header. |
| 1071 | */ | 1087 | */ |
| 1072 | struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, | 1088 | struct dso *kernel = NULL; |
| 1073 | kmmap_prefix); | 1089 | struct dso *dso; |
| 1090 | |||
| 1091 | list_for_each_entry(dso, &machine->kernel_dsos.head, node) { | ||
| 1092 | if (is_kernel_module(dso->long_name, NULL)) | ||
| 1093 | continue; | ||
| 1094 | |||
| 1095 | kernel = dso; | ||
| 1096 | break; | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | if (kernel == NULL) | ||
| 1100 | kernel = __dsos__findnew(&machine->kernel_dsos, | ||
| 1101 | kmmap_prefix); | ||
| 1074 | if (kernel == NULL) | 1102 | if (kernel == NULL) |
| 1075 | goto out_problem; | 1103 | goto out_problem; |
| 1076 | 1104 | ||
| @@ -1078,6 +1106,9 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
| 1078 | if (__machine__create_kernel_maps(machine, kernel) < 0) | 1106 | if (__machine__create_kernel_maps(machine, kernel) < 0) |
| 1079 | goto out_problem; | 1107 | goto out_problem; |
| 1080 | 1108 | ||
| 1109 | if (strstr(dso->long_name, "vmlinux")) | ||
| 1110 | dso__set_short_name(dso, "[kernel.vmlinux]", false); | ||
| 1111 | |||
| 1081 | machine__set_kernel_mmap_len(machine, event); | 1112 | machine__set_kernel_mmap_len(machine, event); |
| 1082 | 1113 | ||
| 1083 | /* | 1114 | /* |
| @@ -1290,7 +1321,7 @@ static bool symbol__match_regex(struct symbol *sym, regex_t *regex) | |||
| 1290 | return 0; | 1321 | return 0; |
| 1291 | } | 1322 | } |
| 1292 | 1323 | ||
| 1293 | static void ip__resolve_ams(struct machine *machine, struct thread *thread, | 1324 | static void ip__resolve_ams(struct thread *thread, |
| 1294 | struct addr_map_symbol *ams, | 1325 | struct addr_map_symbol *ams, |
| 1295 | u64 ip) | 1326 | u64 ip) |
| 1296 | { | 1327 | { |
| @@ -1304,7 +1335,7 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread, | |||
| 1304 | * Thus, we have to try consecutively until we find a match | 1335 | * Thus, we have to try consecutively until we find a match |
| 1305 | * or else, the symbol is unknown | 1336 | * or else, the symbol is unknown |
| 1306 | */ | 1337 | */ |
| 1307 | thread__find_cpumode_addr_location(thread, machine, MAP__FUNCTION, ip, &al); | 1338 | thread__find_cpumode_addr_location(thread, MAP__FUNCTION, ip, &al); |
| 1308 | 1339 | ||
| 1309 | ams->addr = ip; | 1340 | ams->addr = ip; |
| 1310 | ams->al_addr = al.addr; | 1341 | ams->al_addr = al.addr; |
| @@ -1312,23 +1343,21 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread, | |||
| 1312 | ams->map = al.map; | 1343 | ams->map = al.map; |
| 1313 | } | 1344 | } |
| 1314 | 1345 | ||
| 1315 | static void ip__resolve_data(struct machine *machine, struct thread *thread, | 1346 | static void ip__resolve_data(struct thread *thread, |
| 1316 | u8 m, struct addr_map_symbol *ams, u64 addr) | 1347 | u8 m, struct addr_map_symbol *ams, u64 addr) |
| 1317 | { | 1348 | { |
| 1318 | struct addr_location al; | 1349 | struct addr_location al; |
| 1319 | 1350 | ||
| 1320 | memset(&al, 0, sizeof(al)); | 1351 | memset(&al, 0, sizeof(al)); |
| 1321 | 1352 | ||
| 1322 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, | 1353 | thread__find_addr_location(thread, m, MAP__VARIABLE, addr, &al); |
| 1323 | &al); | ||
| 1324 | if (al.map == NULL) { | 1354 | if (al.map == NULL) { |
| 1325 | /* | 1355 | /* |
| 1326 | * some shared data regions have execute bit set which puts | 1356 | * some shared data regions have execute bit set which puts |
| 1327 | * their mapping in the MAP__FUNCTION type array. | 1357 | * their mapping in the MAP__FUNCTION type array. |
| 1328 | * Check there as a fallback option before dropping the sample. | 1358 | * Check there as a fallback option before dropping the sample. |
| 1329 | */ | 1359 | */ |
| 1330 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, addr, | 1360 | thread__find_addr_location(thread, m, MAP__FUNCTION, addr, &al); |
| 1331 | &al); | ||
| 1332 | } | 1361 | } |
| 1333 | 1362 | ||
| 1334 | ams->addr = addr; | 1363 | ams->addr = addr; |
| @@ -1345,14 +1374,41 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample, | |||
| 1345 | if (!mi) | 1374 | if (!mi) |
| 1346 | return NULL; | 1375 | return NULL; |
| 1347 | 1376 | ||
| 1348 | ip__resolve_ams(al->machine, al->thread, &mi->iaddr, sample->ip); | 1377 | ip__resolve_ams(al->thread, &mi->iaddr, sample->ip); |
| 1349 | ip__resolve_data(al->machine, al->thread, al->cpumode, | 1378 | ip__resolve_data(al->thread, al->cpumode, &mi->daddr, sample->addr); |
| 1350 | &mi->daddr, sample->addr); | ||
| 1351 | mi->data_src.val = sample->data_src; | 1379 | mi->data_src.val = sample->data_src; |
| 1352 | 1380 | ||
| 1353 | return mi; | 1381 | return mi; |
| 1354 | } | 1382 | } |
| 1355 | 1383 | ||
| 1384 | static int add_callchain_ip(struct thread *thread, | ||
| 1385 | struct symbol **parent, | ||
| 1386 | struct addr_location *root_al, | ||
| 1387 | int cpumode, | ||
| 1388 | u64 ip) | ||
| 1389 | { | ||
| 1390 | struct addr_location al; | ||
| 1391 | |||
| 1392 | al.filtered = 0; | ||
| 1393 | al.sym = NULL; | ||
| 1394 | thread__find_addr_location(thread, cpumode, MAP__FUNCTION, | ||
| 1395 | ip, &al); | ||
| 1396 | if (al.sym != NULL) { | ||
| 1397 | if (sort__has_parent && !*parent && | ||
| 1398 | symbol__match_regex(al.sym, &parent_regex)) | ||
| 1399 | *parent = al.sym; | ||
| 1400 | else if (have_ignore_callees && root_al && | ||
| 1401 | symbol__match_regex(al.sym, &ignore_callees_regex)) { | ||
| 1402 | /* Treat this symbol as the root, | ||
| 1403 | forgetting its callees. */ | ||
| 1404 | *root_al = al; | ||
| 1405 | callchain_cursor_reset(&callchain_cursor); | ||
| 1406 | } | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | return callchain_cursor_append(&callchain_cursor, al.addr, al.map, al.sym); | ||
| 1410 | } | ||
| 1411 | |||
| 1356 | struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | 1412 | struct branch_info *sample__resolve_bstack(struct perf_sample *sample, |
| 1357 | struct addr_location *al) | 1413 | struct addr_location *al) |
| 1358 | { | 1414 | { |
| @@ -1364,15 +1420,14 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample, | |||
| 1364 | return NULL; | 1420 | return NULL; |
| 1365 | 1421 | ||
| 1366 | for (i = 0; i < bs->nr; i++) { | 1422 | for (i = 0; i < bs->nr; i++) { |
| 1367 | ip__resolve_ams(al->machine, al->thread, &bi[i].to, bs->entries[i].to); | 1423 | ip__resolve_ams(al->thread, &bi[i].to, bs->entries[i].to); |
| 1368 | ip__resolve_ams(al->machine, al->thread, &bi[i].from, bs->entries[i].from); | 1424 | ip__resolve_ams(al->thread, &bi[i].from, bs->entries[i].from); |
| 1369 | bi[i].flags = bs->entries[i].flags; | 1425 | bi[i].flags = bs->entries[i].flags; |
| 1370 | } | 1426 | } |
| 1371 | return bi; | 1427 | return bi; |
| 1372 | } | 1428 | } |
| 1373 | 1429 | ||
| 1374 | static int machine__resolve_callchain_sample(struct machine *machine, | 1430 | static int thread__resolve_callchain_sample(struct thread *thread, |
| 1375 | struct thread *thread, | ||
| 1376 | struct ip_callchain *chain, | 1431 | struct ip_callchain *chain, |
| 1377 | struct symbol **parent, | 1432 | struct symbol **parent, |
| 1378 | struct addr_location *root_al, | 1433 | struct addr_location *root_al, |
| @@ -1396,11 +1451,10 @@ static int machine__resolve_callchain_sample(struct machine *machine, | |||
| 1396 | * Based on DWARF debug information, some architectures skip | 1451 | * Based on DWARF debug information, some architectures skip |
| 1397 | * a callchain entry saved by the kernel. | 1452 | * a callchain entry saved by the kernel. |
| 1398 | */ | 1453 | */ |
| 1399 | skip_idx = arch_skip_callchain_idx(machine, thread, chain); | 1454 | skip_idx = arch_skip_callchain_idx(thread, chain); |
| 1400 | 1455 | ||
| 1401 | for (i = 0; i < chain_nr; i++) { | 1456 | for (i = 0; i < chain_nr; i++) { |
| 1402 | u64 ip; | 1457 | u64 ip; |
| 1403 | struct addr_location al; | ||
| 1404 | 1458 | ||
| 1405 | if (callchain_param.order == ORDER_CALLEE) | 1459 | if (callchain_param.order == ORDER_CALLEE) |
| 1406 | j = i; | 1460 | j = i; |
| @@ -1437,24 +1491,10 @@ static int machine__resolve_callchain_sample(struct machine *machine, | |||
| 1437 | continue; | 1491 | continue; |
| 1438 | } | 1492 | } |
| 1439 | 1493 | ||
| 1440 | al.filtered = 0; | 1494 | err = add_callchain_ip(thread, parent, root_al, |
| 1441 | thread__find_addr_location(thread, machine, cpumode, | 1495 | cpumode, ip); |
| 1442 | MAP__FUNCTION, ip, &al); | 1496 | if (err == -EINVAL) |
| 1443 | if (al.sym != NULL) { | 1497 | break; |
| 1444 | if (sort__has_parent && !*parent && | ||
| 1445 | symbol__match_regex(al.sym, &parent_regex)) | ||
| 1446 | *parent = al.sym; | ||
| 1447 | else if (have_ignore_callees && root_al && | ||
| 1448 | symbol__match_regex(al.sym, &ignore_callees_regex)) { | ||
| 1449 | /* Treat this symbol as the root, | ||
| 1450 | forgetting its callees. */ | ||
| 1451 | *root_al = al; | ||
| 1452 | callchain_cursor_reset(&callchain_cursor); | ||
| 1453 | } | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | err = callchain_cursor_append(&callchain_cursor, | ||
| 1457 | ip, al.map, al.sym); | ||
| 1458 | if (err) | 1498 | if (err) |
| 1459 | return err; | 1499 | return err; |
| 1460 | } | 1500 | } |
| @@ -1469,19 +1509,15 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) | |||
| 1469 | entry->map, entry->sym); | 1509 | entry->map, entry->sym); |
| 1470 | } | 1510 | } |
| 1471 | 1511 | ||
| 1472 | int machine__resolve_callchain(struct machine *machine, | 1512 | int thread__resolve_callchain(struct thread *thread, |
| 1473 | struct perf_evsel *evsel, | 1513 | struct perf_evsel *evsel, |
| 1474 | struct thread *thread, | 1514 | struct perf_sample *sample, |
| 1475 | struct perf_sample *sample, | 1515 | struct symbol **parent, |
| 1476 | struct symbol **parent, | 1516 | struct addr_location *root_al, |
| 1477 | struct addr_location *root_al, | 1517 | int max_stack) |
| 1478 | int max_stack) | ||
| 1479 | { | 1518 | { |
| 1480 | int ret; | 1519 | int ret = thread__resolve_callchain_sample(thread, sample->callchain, |
| 1481 | 1520 | parent, root_al, max_stack); | |
| 1482 | ret = machine__resolve_callchain_sample(machine, thread, | ||
| 1483 | sample->callchain, parent, | ||
| 1484 | root_al, max_stack); | ||
| 1485 | if (ret) | 1521 | if (ret) |
| 1486 | return ret; | 1522 | return ret; |
| 1487 | 1523 | ||
| @@ -1495,7 +1531,7 @@ int machine__resolve_callchain(struct machine *machine, | |||
| 1495 | (!sample->user_stack.size)) | 1531 | (!sample->user_stack.size)) |
| 1496 | return 0; | 1532 | return 0; |
| 1497 | 1533 | ||
| 1498 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | 1534 | return unwind__get_entries(unwind_entry, &callchain_cursor, |
| 1499 | thread, sample, max_stack); | 1535 | thread, sample, max_stack); |
| 1500 | 1536 | ||
| 1501 | } | 1537 | } |
