diff options
Diffstat (limited to 'tools')
248 files changed, 16993 insertions, 7198 deletions
diff --git a/tools/Makefile b/tools/Makefile index 3ae43947a171..fa36565b209d 100644 --- a/tools/Makefile +++ b/tools/Makefile | |||
@@ -3,6 +3,7 @@ include scripts/Makefile.include | |||
3 | help: | 3 | help: |
4 | @echo 'Possible targets:' | 4 | @echo 'Possible targets:' |
5 | @echo '' | 5 | @echo '' |
6 | @echo ' cgroup - cgroup tools' | ||
6 | @echo ' cpupower - a tool for all things x86 CPU power' | 7 | @echo ' cpupower - a tool for all things x86 CPU power' |
7 | @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' | 8 | @echo ' firewire - the userspace part of nosy, an IEEE-1394 traffic sniffer' |
8 | @echo ' lguest - a minimal 32-bit x86 hypervisor' | 9 | @echo ' lguest - a minimal 32-bit x86 hypervisor' |
@@ -15,7 +16,7 @@ help: | |||
15 | @echo ' x86_energy_perf_policy - Intel energy policy tool' | 16 | @echo ' x86_energy_perf_policy - Intel energy policy tool' |
16 | @echo '' | 17 | @echo '' |
17 | @echo 'You can do:' | 18 | @echo 'You can do:' |
18 | @echo ' $$ make -C tools/<tool>_install' | 19 | @echo ' $$ make -C tools/ <tool>_install' |
19 | @echo '' | 20 | @echo '' |
20 | @echo ' from the kernel command line to build and install one of' | 21 | @echo ' from the kernel command line to build and install one of' |
21 | @echo ' the tools above' | 22 | @echo ' the tools above' |
@@ -31,47 +32,47 @@ help: | |||
31 | @echo ' clean: a summary clean target to clean _all_ folders' | 32 | @echo ' clean: a summary clean target to clean _all_ folders' |
32 | 33 | ||
33 | cpupower: FORCE | 34 | cpupower: FORCE |
34 | $(QUIET_SUBDIR0)power/$@/ $(QUIET_SUBDIR1) | 35 | $(call descend,power/$@) |
35 | 36 | ||
36 | firewire lguest perf usb virtio vm: FORCE | 37 | cgroup firewire lguest perf usb virtio vm: FORCE |
37 | $(QUIET_SUBDIR0)$@/ $(QUIET_SUBDIR1) | 38 | $(call descend,$@) |
38 | 39 | ||
39 | selftests: FORCE | 40 | selftests: FORCE |
40 | $(QUIET_SUBDIR0)testing/$@/ $(QUIET_SUBDIR1) | 41 | $(call descend,testing/$@) |
41 | 42 | ||
42 | turbostat x86_energy_perf_policy: FORCE | 43 | turbostat x86_energy_perf_policy: FORCE |
43 | $(QUIET_SUBDIR0)power/x86/$@/ $(QUIET_SUBDIR1) | 44 | $(call descend,power/x86/$@) |
44 | 45 | ||
45 | cpupower_install: | 46 | cpupower_install: |
46 | $(QUIET_SUBDIR0)power/$(@:_install=)/ $(QUIET_SUBDIR1) install | 47 | $(call descend,power/$(@:_install=),install) |
47 | 48 | ||
48 | firewire_install lguest_install perf_install usb_install virtio_install vm_install: | 49 | cgroup_install firewire_install lguest_install perf_install usb_install virtio_install vm_install: |
49 | $(QUIET_SUBDIR0)$(@:_install=)/ $(QUIET_SUBDIR1) install | 50 | $(call descend,$(@:_install=),install) |
50 | 51 | ||
51 | selftests_install: | 52 | selftests_install: |
52 | $(QUIET_SUBDIR0)testing/$(@:_clean=)/ $(QUIET_SUBDIR1) install | 53 | $(call descend,testing/$(@:_clean=),install) |
53 | 54 | ||
54 | turbostat_install x86_energy_perf_policy_install: | 55 | turbostat_install x86_energy_perf_policy_install: |
55 | $(QUIET_SUBDIR0)power/x86/$(@:_install=)/ $(QUIET_SUBDIR1) install | 56 | $(call descend,power/x86/$(@:_install=),install) |
56 | 57 | ||
57 | install: cpupower_install firewire_install lguest_install perf_install \ | 58 | install: cgroup_install cpupower_install firewire_install lguest_install \ |
58 | selftests_install turbostat_install usb_install virtio_install \ | 59 | perf_install selftests_install turbostat_install usb_install \ |
59 | vm_install x86_energy_perf_policy_install | 60 | virtio_install vm_install x86_energy_perf_policy_install |
60 | 61 | ||
61 | cpupower_clean: | 62 | cpupower_clean: |
62 | $(QUIET_SUBDIR0)power/cpupower/ $(QUIET_SUBDIR1) clean | 63 | $(call descend,power/cpupower,clean) |
63 | 64 | ||
64 | firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean: | 65 | cgroup_clean firewire_clean lguest_clean perf_clean usb_clean virtio_clean vm_clean: |
65 | $(QUIET_SUBDIR0)$(@:_clean=)/ $(QUIET_SUBDIR1) clean | 66 | $(call descend,$(@:_clean=),clean) |
66 | 67 | ||
67 | selftests_clean: | 68 | selftests_clean: |
68 | $(QUIET_SUBDIR0)testing/$(@:_clean=)/ $(QUIET_SUBDIR1) clean | 69 | $(call descend,testing/$(@:_clean=),clean) |
69 | 70 | ||
70 | turbostat_clean x86_energy_perf_policy_clean: | 71 | turbostat_clean x86_energy_perf_policy_clean: |
71 | $(QUIET_SUBDIR0)power/x86/$(@:_clean=)/ $(QUIET_SUBDIR1) clean | 72 | $(call descend,power/x86/$(@:_clean=),clean) |
72 | 73 | ||
73 | clean: cpupower_clean firewire_clean lguest_clean perf_clean selftests_clean \ | 74 | clean: cgroup_clean cpupower_clean firewire_clean lguest_clean perf_clean \ |
74 | turbostat_clean usb_clean virtio_clean vm_clean \ | 75 | selftests_clean turbostat_clean usb_clean virtio_clean \ |
75 | x86_energy_perf_policy_clean | 76 | vm_clean x86_energy_perf_policy_clean |
76 | 77 | ||
77 | .PHONY: FORCE | 78 | .PHONY: FORCE |
diff --git a/tools/cgroup/.gitignore b/tools/cgroup/.gitignore new file mode 100644 index 000000000000..633cd9b874f9 --- /dev/null +++ b/tools/cgroup/.gitignore | |||
@@ -0,0 +1 @@ | |||
cgroup_event_listener | |||
diff --git a/tools/cgroup/Makefile b/tools/cgroup/Makefile new file mode 100644 index 000000000000..b4286196b763 --- /dev/null +++ b/tools/cgroup/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # Makefile for cgroup tools | ||
2 | |||
3 | CC = $(CROSS_COMPILE)gcc | ||
4 | CFLAGS = -Wall -Wextra | ||
5 | |||
6 | all: cgroup_event_listener | ||
7 | %: %.c | ||
8 | $(CC) $(CFLAGS) -o $@ $^ | ||
9 | |||
10 | clean: | ||
11 | $(RM) cgroup_event_listener | ||
diff --git a/tools/cgroup/cgroup_event_listener.c b/tools/cgroup/cgroup_event_listener.c new file mode 100644 index 000000000000..4eb5507205c9 --- /dev/null +++ b/tools/cgroup/cgroup_event_listener.c | |||
@@ -0,0 +1,82 @@ | |||
1 | /* | ||
2 | * cgroup_event_listener.c - Simple listener of cgroup events | ||
3 | * | ||
4 | * Copyright (C) Kirill A. Shutemov <kirill@shutemov.name> | ||
5 | */ | ||
6 | |||
7 | #include <assert.h> | ||
8 | #include <err.h> | ||
9 | #include <errno.h> | ||
10 | #include <fcntl.h> | ||
11 | #include <libgen.h> | ||
12 | #include <limits.h> | ||
13 | #include <stdio.h> | ||
14 | #include <string.h> | ||
15 | #include <unistd.h> | ||
16 | |||
17 | #include <sys/eventfd.h> | ||
18 | |||
19 | #define USAGE_STR "Usage: cgroup_event_listener <path-to-control-file> <args>" | ||
20 | |||
21 | int main(int argc, char **argv) | ||
22 | { | ||
23 | int efd = -1; | ||
24 | int cfd = -1; | ||
25 | int event_control = -1; | ||
26 | char event_control_path[PATH_MAX]; | ||
27 | char line[LINE_MAX]; | ||
28 | int ret; | ||
29 | |||
30 | if (argc != 3) | ||
31 | errx(1, "%s", USAGE_STR); | ||
32 | |||
33 | cfd = open(argv[1], O_RDONLY); | ||
34 | if (cfd == -1) | ||
35 | err(1, "Cannot open %s", argv[1]); | ||
36 | |||
37 | ret = snprintf(event_control_path, PATH_MAX, "%s/cgroup.event_control", | ||
38 | dirname(argv[1])); | ||
39 | if (ret >= PATH_MAX) | ||
40 | errx(1, "Path to cgroup.event_control is too long"); | ||
41 | |||
42 | event_control = open(event_control_path, O_WRONLY); | ||
43 | if (event_control == -1) | ||
44 | err(1, "Cannot open %s", event_control_path); | ||
45 | |||
46 | efd = eventfd(0, 0); | ||
47 | if (efd == -1) | ||
48 | err(1, "eventfd() failed"); | ||
49 | |||
50 | ret = snprintf(line, LINE_MAX, "%d %d %s", efd, cfd, argv[2]); | ||
51 | if (ret >= LINE_MAX) | ||
52 | errx(1, "Arguments string is too long"); | ||
53 | |||
54 | ret = write(event_control, line, strlen(line) + 1); | ||
55 | if (ret == -1) | ||
56 | err(1, "Cannot write to cgroup.event_control"); | ||
57 | |||
58 | while (1) { | ||
59 | uint64_t result; | ||
60 | |||
61 | ret = read(efd, &result, sizeof(result)); | ||
62 | if (ret == -1) { | ||
63 | if (errno == EINTR) | ||
64 | continue; | ||
65 | err(1, "Cannot read from eventfd"); | ||
66 | } | ||
67 | assert(ret == sizeof(result)); | ||
68 | |||
69 | ret = access(event_control_path, W_OK); | ||
70 | if ((ret == -1) && (errno == ENOENT)) { | ||
71 | puts("The cgroup seems to have removed."); | ||
72 | break; | ||
73 | } | ||
74 | |||
75 | if (ret == -1) | ||
76 | err(1, "cgroup.event_control is not accessible any more"); | ||
77 | |||
78 | printf("%s %s: crossed\n", argv[1], argv[2]); | ||
79 | } | ||
80 | |||
81 | return 0; | ||
82 | } | ||
diff --git a/tools/firewire/nosy-dump.c b/tools/firewire/nosy-dump.c index f93b776370b6..3179c711bd65 100644 --- a/tools/firewire/nosy-dump.c +++ b/tools/firewire/nosy-dump.c | |||
@@ -150,6 +150,8 @@ subaction_create(uint32_t *data, size_t length) | |||
150 | 150 | ||
151 | /* we put the ack in the subaction struct for easy access. */ | 151 | /* we put the ack in the subaction struct for easy access. */ |
152 | sa = malloc(sizeof *sa - sizeof sa->packet + length); | 152 | sa = malloc(sizeof *sa - sizeof sa->packet + length); |
153 | if (!sa) | ||
154 | exit(EXIT_FAILURE); | ||
153 | sa->ack = data[length / 4 - 1]; | 155 | sa->ack = data[length / 4 - 1]; |
154 | sa->length = length; | 156 | sa->length = length; |
155 | memcpy(&sa->packet, data, length); | 157 | memcpy(&sa->packet, data, length); |
@@ -180,6 +182,8 @@ link_transaction_lookup(int request_node, int response_node, int tlabel) | |||
180 | } | 182 | } |
181 | 183 | ||
182 | t = malloc(sizeof *t); | 184 | t = malloc(sizeof *t); |
185 | if (!t) | ||
186 | exit(EXIT_FAILURE); | ||
183 | t->request_node = request_node; | 187 | t->request_node = request_node; |
184 | t->response_node = response_node; | 188 | t->response_node = response_node; |
185 | t->tlabel = tlabel; | 189 | t->tlabel = tlabel; |
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 5959affd8820..c800ea4c8bf9 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c | |||
@@ -43,6 +43,7 @@ | |||
43 | #include <sys/stat.h> | 43 | #include <sys/stat.h> |
44 | #include <fcntl.h> | 44 | #include <fcntl.h> |
45 | #include <dirent.h> | 45 | #include <dirent.h> |
46 | #include <net/if.h> | ||
46 | 47 | ||
47 | /* | 48 | /* |
48 | * KVP protocol: The user mode component first registers with the | 49 | * KVP protocol: The user mode component first registers with the |
@@ -88,6 +89,7 @@ static char *os_major = ""; | |||
88 | static char *os_minor = ""; | 89 | static char *os_minor = ""; |
89 | static char *processor_arch; | 90 | static char *processor_arch; |
90 | static char *os_build; | 91 | static char *os_build; |
92 | static char *os_version; | ||
91 | static char *lic_version = "Unknown version"; | 93 | static char *lic_version = "Unknown version"; |
92 | static struct utsname uts_buf; | 94 | static struct utsname uts_buf; |
93 | 95 | ||
@@ -95,7 +97,7 @@ static struct utsname uts_buf; | |||
95 | * The location of the interface configuration file. | 97 | * The location of the interface configuration file. |
96 | */ | 98 | */ |
97 | 99 | ||
98 | #define KVP_CONFIG_LOC "/var/opt/" | 100 | #define KVP_CONFIG_LOC "/var/lib/hyperv" |
99 | 101 | ||
100 | #define MAX_FILE_NAME 100 | 102 | #define MAX_FILE_NAME 100 |
101 | #define ENTRIES_PER_BLOCK 50 | 103 | #define ENTRIES_PER_BLOCK 50 |
@@ -149,7 +151,7 @@ static void kvp_update_file(int pool) | |||
149 | */ | 151 | */ |
150 | kvp_acquire_lock(pool); | 152 | kvp_acquire_lock(pool); |
151 | 153 | ||
152 | filep = fopen(kvp_file_info[pool].fname, "w"); | 154 | filep = fopen(kvp_file_info[pool].fname, "we"); |
153 | if (!filep) { | 155 | if (!filep) { |
154 | kvp_release_lock(pool); | 156 | kvp_release_lock(pool); |
155 | syslog(LOG_ERR, "Failed to open file, pool: %d", pool); | 157 | syslog(LOG_ERR, "Failed to open file, pool: %d", pool); |
@@ -180,7 +182,7 @@ static void kvp_update_mem_state(int pool) | |||
180 | 182 | ||
181 | kvp_acquire_lock(pool); | 183 | kvp_acquire_lock(pool); |
182 | 184 | ||
183 | filep = fopen(kvp_file_info[pool].fname, "r"); | 185 | filep = fopen(kvp_file_info[pool].fname, "re"); |
184 | if (!filep) { | 186 | if (!filep) { |
185 | kvp_release_lock(pool); | 187 | kvp_release_lock(pool); |
186 | syslog(LOG_ERR, "Failed to open file, pool: %d", pool); | 188 | syslog(LOG_ERR, "Failed to open file, pool: %d", pool); |
@@ -232,9 +234,9 @@ static int kvp_file_init(void) | |||
232 | int i; | 234 | int i; |
233 | int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; | 235 | int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; |
234 | 236 | ||
235 | if (access("/var/opt/hyperv", F_OK)) { | 237 | if (access(KVP_CONFIG_LOC, F_OK)) { |
236 | if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) { | 238 | if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) { |
237 | syslog(LOG_ERR, " Failed to create /var/opt/hyperv"); | 239 | syslog(LOG_ERR, " Failed to create %s", KVP_CONFIG_LOC); |
238 | exit(EXIT_FAILURE); | 240 | exit(EXIT_FAILURE); |
239 | } | 241 | } |
240 | } | 242 | } |
@@ -243,14 +245,14 @@ static int kvp_file_init(void) | |||
243 | fname = kvp_file_info[i].fname; | 245 | fname = kvp_file_info[i].fname; |
244 | records_read = 0; | 246 | records_read = 0; |
245 | num_blocks = 1; | 247 | num_blocks = 1; |
246 | sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i); | 248 | sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i); |
247 | fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH); | 249 | fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */); |
248 | 250 | ||
249 | if (fd == -1) | 251 | if (fd == -1) |
250 | return 1; | 252 | return 1; |
251 | 253 | ||
252 | 254 | ||
253 | filep = fopen(fname, "r"); | 255 | filep = fopen(fname, "re"); |
254 | if (!filep) | 256 | if (!filep) |
255 | return 1; | 257 | return 1; |
256 | 258 | ||
@@ -297,7 +299,7 @@ static int kvp_file_init(void) | |||
297 | return 0; | 299 | return 0; |
298 | } | 300 | } |
299 | 301 | ||
300 | static int kvp_key_delete(int pool, __u8 *key, int key_size) | 302 | static int kvp_key_delete(int pool, const char *key, int key_size) |
301 | { | 303 | { |
302 | int i; | 304 | int i; |
303 | int j, k; | 305 | int j, k; |
@@ -340,7 +342,7 @@ static int kvp_key_delete(int pool, __u8 *key, int key_size) | |||
340 | return 1; | 342 | return 1; |
341 | } | 343 | } |
342 | 344 | ||
343 | static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value, | 345 | static int kvp_key_add_or_modify(int pool, const char *key, int key_size, const char *value, |
344 | int value_size) | 346 | int value_size) |
345 | { | 347 | { |
346 | int i; | 348 | int i; |
@@ -394,7 +396,7 @@ static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value, | |||
394 | return 0; | 396 | return 0; |
395 | } | 397 | } |
396 | 398 | ||
397 | static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, | 399 | static int kvp_get_value(int pool, const char *key, int key_size, char *value, |
398 | int value_size) | 400 | int value_size) |
399 | { | 401 | { |
400 | int i; | 402 | int i; |
@@ -426,8 +428,8 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, | |||
426 | return 1; | 428 | return 1; |
427 | } | 429 | } |
428 | 430 | ||
429 | static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, | 431 | static int kvp_pool_enumerate(int pool, int index, char *key, int key_size, |
430 | __u8 *value, int value_size) | 432 | char *value, int value_size) |
431 | { | 433 | { |
432 | struct kvp_record *record; | 434 | struct kvp_record *record; |
433 | 435 | ||
@@ -453,7 +455,9 @@ void kvp_get_os_info(void) | |||
453 | char *p, buf[512]; | 455 | char *p, buf[512]; |
454 | 456 | ||
455 | uname(&uts_buf); | 457 | uname(&uts_buf); |
456 | os_build = uts_buf.release; | 458 | os_version = uts_buf.release; |
459 | os_build = strdup(uts_buf.release); | ||
460 | |||
457 | os_name = uts_buf.sysname; | 461 | os_name = uts_buf.sysname; |
458 | processor_arch = uts_buf.machine; | 462 | processor_arch = uts_buf.machine; |
459 | 463 | ||
@@ -462,7 +466,7 @@ void kvp_get_os_info(void) | |||
462 | * string to be of the form: x.y.z | 466 | * string to be of the form: x.y.z |
463 | * Strip additional information we may have. | 467 | * Strip additional information we may have. |
464 | */ | 468 | */ |
465 | p = strchr(os_build, '-'); | 469 | p = strchr(os_version, '-'); |
466 | if (p) | 470 | if (p) |
467 | *p = '\0'; | 471 | *p = '\0'; |
468 | 472 | ||
@@ -879,7 +883,7 @@ static int kvp_process_ip_address(void *addrp, | |||
879 | addr_length = INET6_ADDRSTRLEN; | 883 | addr_length = INET6_ADDRSTRLEN; |
880 | } | 884 | } |
881 | 885 | ||
882 | if ((length - *offset) < addr_length + 1) | 886 | if ((length - *offset) < addr_length + 2) |
883 | return HV_E_FAIL; | 887 | return HV_E_FAIL; |
884 | if (str == NULL) { | 888 | if (str == NULL) { |
885 | strcpy(buffer, "inet_ntop failed\n"); | 889 | strcpy(buffer, "inet_ntop failed\n"); |
@@ -887,11 +891,13 @@ static int kvp_process_ip_address(void *addrp, | |||
887 | } | 891 | } |
888 | if (*offset == 0) | 892 | if (*offset == 0) |
889 | strcpy(buffer, tmp); | 893 | strcpy(buffer, tmp); |
890 | else | 894 | else { |
895 | strcat(buffer, ";"); | ||
891 | strcat(buffer, tmp); | 896 | strcat(buffer, tmp); |
892 | strcat(buffer, ";"); | 897 | } |
893 | 898 | ||
894 | *offset += strlen(str) + 1; | 899 | *offset += strlen(str) + 1; |
900 | |||
895 | return 0; | 901 | return 0; |
896 | } | 902 | } |
897 | 903 | ||
@@ -953,7 +959,9 @@ kvp_get_ip_info(int family, char *if_name, int op, | |||
953 | * supported address families; if not we gather info on | 959 | * supported address families; if not we gather info on |
954 | * the specified address family. | 960 | * the specified address family. |
955 | */ | 961 | */ |
956 | if ((family != 0) && (curp->ifa_addr->sa_family != family)) { | 962 | if ((((family != 0) && |
963 | (curp->ifa_addr->sa_family != family))) || | ||
964 | (curp->ifa_flags & IFF_LOOPBACK)) { | ||
957 | curp = curp->ifa_next; | 965 | curp = curp->ifa_next; |
958 | continue; | 966 | continue; |
959 | } | 967 | } |
@@ -1154,16 +1162,13 @@ static int process_ip_string(FILE *f, char *ip_string, int type) | |||
1154 | snprintf(str, sizeof(str), "%s", "DNS"); | 1162 | snprintf(str, sizeof(str), "%s", "DNS"); |
1155 | break; | 1163 | break; |
1156 | } | 1164 | } |
1157 | if (i != 0) { | 1165 | |
1158 | if (type != DNS) { | 1166 | if (type == DNS) { |
1159 | snprintf(sub_str, sizeof(sub_str), | ||
1160 | "_%d", i++); | ||
1161 | } else { | ||
1162 | snprintf(sub_str, sizeof(sub_str), | ||
1163 | "%d", ++i); | ||
1164 | } | ||
1165 | } else if (type == DNS) { | ||
1166 | snprintf(sub_str, sizeof(sub_str), "%d", ++i); | 1167 | snprintf(sub_str, sizeof(sub_str), "%d", ++i); |
1168 | } else if (type == GATEWAY && i == 0) { | ||
1169 | ++i; | ||
1170 | } else { | ||
1171 | snprintf(sub_str, sizeof(sub_str), "%d", i++); | ||
1167 | } | 1172 | } |
1168 | 1173 | ||
1169 | 1174 | ||
@@ -1183,17 +1188,13 @@ static int process_ip_string(FILE *f, char *ip_string, int type) | |||
1183 | snprintf(str, sizeof(str), "%s", "DNS"); | 1188 | snprintf(str, sizeof(str), "%s", "DNS"); |
1184 | break; | 1189 | break; |
1185 | } | 1190 | } |
1186 | if ((j != 0) || (type == DNS)) { | 1191 | |
1187 | if (type != DNS) { | 1192 | if (type == DNS) { |
1188 | snprintf(sub_str, sizeof(sub_str), | 1193 | snprintf(sub_str, sizeof(sub_str), "%d", ++i); |
1189 | "_%d", j++); | 1194 | } else if (j == 0) { |
1190 | } else { | 1195 | ++j; |
1191 | snprintf(sub_str, sizeof(sub_str), | 1196 | } else { |
1192 | "%d", ++i); | 1197 | snprintf(sub_str, sizeof(sub_str), "_%d", j++); |
1193 | } | ||
1194 | } else if (type == DNS) { | ||
1195 | snprintf(sub_str, sizeof(sub_str), | ||
1196 | "%d", ++i); | ||
1197 | } | 1198 | } |
1198 | } else { | 1199 | } else { |
1199 | return HV_INVALIDARG; | 1200 | return HV_INVALIDARG; |
@@ -1236,18 +1237,19 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) | |||
1236 | * Here is the format of the ip configuration file: | 1237 | * Here is the format of the ip configuration file: |
1237 | * | 1238 | * |
1238 | * HWADDR=macaddr | 1239 | * HWADDR=macaddr |
1239 | * IF_NAME=interface name | 1240 | * DEVICE=interface name |
1240 | * DHCP=yes (This is optional; if yes, DHCP is configured) | 1241 | * BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured |
1242 | * or "none" if no boot-time protocol should be used) | ||
1241 | * | 1243 | * |
1242 | * IPADDR=ipaddr1 | 1244 | * IPADDR0=ipaddr1 |
1243 | * IPADDR_1=ipaddr2 | 1245 | * IPADDR1=ipaddr2 |
1244 | * IPADDR_x=ipaddry (where y = x + 1) | 1246 | * IPADDRx=ipaddry (where y = x + 1) |
1245 | * | 1247 | * |
1246 | * NETMASK=netmask1 | 1248 | * NETMASK0=netmask1 |
1247 | * NETMASK_x=netmasky (where y = x + 1) | 1249 | * NETMASKx=netmasky (where y = x + 1) |
1248 | * | 1250 | * |
1249 | * GATEWAY=ipaddr1 | 1251 | * GATEWAY=ipaddr1 |
1250 | * GATEWAY_x=ipaddry (where y = x + 1) | 1252 | * GATEWAYx=ipaddry (where y = x + 1) |
1251 | * | 1253 | * |
1252 | * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) | 1254 | * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) |
1253 | * | 1255 | * |
@@ -1263,7 +1265,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) | |||
1263 | */ | 1265 | */ |
1264 | 1266 | ||
1265 | snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, | 1267 | snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, |
1266 | "hyperv/ifcfg-", if_name); | 1268 | "/ifcfg-", if_name); |
1267 | 1269 | ||
1268 | file = fopen(if_file, "w"); | 1270 | file = fopen(if_file, "w"); |
1269 | 1271 | ||
@@ -1286,12 +1288,12 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) | |||
1286 | if (error) | 1288 | if (error) |
1287 | goto setval_error; | 1289 | goto setval_error; |
1288 | 1290 | ||
1289 | error = kvp_write_file(file, "IF_NAME", "", if_name); | 1291 | error = kvp_write_file(file, "DEVICE", "", if_name); |
1290 | if (error) | 1292 | if (error) |
1291 | goto setval_error; | 1293 | goto setval_error; |
1292 | 1294 | ||
1293 | if (new_val->dhcp_enabled) { | 1295 | if (new_val->dhcp_enabled) { |
1294 | error = kvp_write_file(file, "DHCP", "", "yes"); | 1296 | error = kvp_write_file(file, "BOOTPROTO", "", "dhcp"); |
1295 | if (error) | 1297 | if (error) |
1296 | goto setval_error; | 1298 | goto setval_error; |
1297 | 1299 | ||
@@ -1299,6 +1301,11 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) | |||
1299 | * We are done!. | 1301 | * We are done!. |
1300 | */ | 1302 | */ |
1301 | goto setval_done; | 1303 | goto setval_done; |
1304 | |||
1305 | } else { | ||
1306 | error = kvp_write_file(file, "BOOTPROTO", "", "none"); | ||
1307 | if (error) | ||
1308 | goto setval_error; | ||
1302 | } | 1309 | } |
1303 | 1310 | ||
1304 | /* | 1311 | /* |
@@ -1478,13 +1485,19 @@ int main(void) | |||
1478 | len = recvfrom(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0, | 1485 | len = recvfrom(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0, |
1479 | addr_p, &addr_l); | 1486 | addr_p, &addr_l); |
1480 | 1487 | ||
1481 | if (len < 0 || addr.nl_pid) { | 1488 | if (len < 0) { |
1482 | syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", | 1489 | syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", |
1483 | addr.nl_pid, errno, strerror(errno)); | 1490 | addr.nl_pid, errno, strerror(errno)); |
1484 | close(fd); | 1491 | close(fd); |
1485 | return -1; | 1492 | return -1; |
1486 | } | 1493 | } |
1487 | 1494 | ||
1495 | if (addr.nl_pid) { | ||
1496 | syslog(LOG_WARNING, "Received packet from untrusted pid:%u", | ||
1497 | addr.nl_pid); | ||
1498 | continue; | ||
1499 | } | ||
1500 | |||
1488 | incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; | 1501 | incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; |
1489 | incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); | 1502 | incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); |
1490 | hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; | 1503 | hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; |
@@ -1649,7 +1662,7 @@ int main(void) | |||
1649 | strcpy(key_name, "OSMinorVersion"); | 1662 | strcpy(key_name, "OSMinorVersion"); |
1650 | break; | 1663 | break; |
1651 | case OSVersion: | 1664 | case OSVersion: |
1652 | strcpy(key_value, os_build); | 1665 | strcpy(key_value, os_version); |
1653 | strcpy(key_name, "OSVersion"); | 1666 | strcpy(key_name, "OSVersion"); |
1654 | break; | 1667 | break; |
1655 | case ProcessorArchitecture: | 1668 | case ProcessorArchitecture: |
diff --git a/tools/hv/hv_set_ifconfig.sh b/tools/hv/hv_set_ifconfig.sh index 3e9427e08d80..735aafd64a3f 100755 --- a/tools/hv/hv_set_ifconfig.sh +++ b/tools/hv/hv_set_ifconfig.sh | |||
@@ -20,18 +20,19 @@ | |||
20 | # Here is the format of the ip configuration file: | 20 | # Here is the format of the ip configuration file: |
21 | # | 21 | # |
22 | # HWADDR=macaddr | 22 | # HWADDR=macaddr |
23 | # IF_NAME=interface name | 23 | # DEVICE=interface name |
24 | # DHCP=yes (This is optional; if yes, DHCP is configured) | 24 | # BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured |
25 | # or "none" if no boot-time protocol should be used) | ||
25 | # | 26 | # |
26 | # IPADDR=ipaddr1 | 27 | # IPADDR0=ipaddr1 |
27 | # IPADDR_1=ipaddr2 | 28 | # IPADDR1=ipaddr2 |
28 | # IPADDR_x=ipaddry (where y = x + 1) | 29 | # IPADDRx=ipaddry (where y = x + 1) |
29 | # | 30 | # |
30 | # NETMASK=netmask1 | 31 | # NETMASK0=netmask1 |
31 | # NETMASK_x=netmasky (where y = x + 1) | 32 | # NETMASKx=netmasky (where y = x + 1) |
32 | # | 33 | # |
33 | # GATEWAY=ipaddr1 | 34 | # GATEWAY=ipaddr1 |
34 | # GATEWAY_x=ipaddry (where y = x + 1) | 35 | # GATEWAYx=ipaddry (where y = x + 1) |
35 | # | 36 | # |
36 | # DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) | 37 | # DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) |
37 | # | 38 | # |
@@ -53,11 +54,6 @@ echo "NM_CONTROLLED=no" >> $1 | |||
53 | echo "PEERDNS=yes" >> $1 | 54 | echo "PEERDNS=yes" >> $1 |
54 | echo "ONBOOT=yes" >> $1 | 55 | echo "ONBOOT=yes" >> $1 |
55 | 56 | ||
56 | dhcp=$(grep "DHCP" $1 2>/dev/null) | ||
57 | if [ "$dhcp" != "" ]; | ||
58 | then | ||
59 | echo "BOOTPROTO=dhcp" >> $1; | ||
60 | fi | ||
61 | 57 | ||
62 | cp $1 /etc/sysconfig/network-scripts/ | 58 | cp $1 /etc/sysconfig/network-scripts/ |
63 | 59 | ||
@@ -65,4 +61,4 @@ cp $1 /etc/sysconfig/network-scripts/ | |||
65 | interface=$(echo $1 | awk -F - '{ print $2 }') | 61 | interface=$(echo $1 | awk -F - '{ print $2 }') |
66 | 62 | ||
67 | /sbin/ifdown $interface 2>/dev/null | 63 | /sbin/ifdown $interface 2>/dev/null |
68 | /sbin/ifup $interfac 2>/dev/null | 64 | /sbin/ifup $interface 2>/dev/null |
diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index fd2f9221b241..07a03452c227 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c | |||
@@ -179,29 +179,6 @@ static struct termios orig_term; | |||
179 | #define wmb() __asm__ __volatile__("" : : : "memory") | 179 | #define wmb() __asm__ __volatile__("" : : : "memory") |
180 | #define mb() __asm__ __volatile__("" : : : "memory") | 180 | #define mb() __asm__ __volatile__("" : : : "memory") |
181 | 181 | ||
182 | /* | ||
183 | * Convert an iovec element to the given type. | ||
184 | * | ||
185 | * This is a fairly ugly trick: we need to know the size of the type and | ||
186 | * alignment requirement to check the pointer is kosher. It's also nice to | ||
187 | * have the name of the type in case we report failure. | ||
188 | * | ||
189 | * Typing those three things all the time is cumbersome and error prone, so we | ||
190 | * have a macro which sets them all up and passes to the real function. | ||
191 | */ | ||
192 | #define convert(iov, type) \ | ||
193 | ((type *)_convert((iov), sizeof(type), __alignof__(type), #type)) | ||
194 | |||
195 | static void *_convert(struct iovec *iov, size_t size, size_t align, | ||
196 | const char *name) | ||
197 | { | ||
198 | if (iov->iov_len != size) | ||
199 | errx(1, "Bad iovec size %zu for %s", iov->iov_len, name); | ||
200 | if ((unsigned long)iov->iov_base % align != 0) | ||
201 | errx(1, "Bad alignment %p for %s", iov->iov_base, name); | ||
202 | return iov->iov_base; | ||
203 | } | ||
204 | |||
205 | /* Wrapper for the last available index. Makes it easier to change. */ | 182 | /* Wrapper for the last available index. Makes it easier to change. */ |
206 | #define lg_last_avail(vq) ((vq)->last_avail_idx) | 183 | #define lg_last_avail(vq) ((vq)->last_avail_idx) |
207 | 184 | ||
@@ -228,7 +205,8 @@ static bool iov_empty(const struct iovec iov[], unsigned int num_iov) | |||
228 | } | 205 | } |
229 | 206 | ||
230 | /* Take len bytes from the front of this iovec. */ | 207 | /* Take len bytes from the front of this iovec. */ |
231 | static void iov_consume(struct iovec iov[], unsigned num_iov, unsigned len) | 208 | static void iov_consume(struct iovec iov[], unsigned num_iov, |
209 | void *dest, unsigned len) | ||
232 | { | 210 | { |
233 | unsigned int i; | 211 | unsigned int i; |
234 | 212 | ||
@@ -236,11 +214,16 @@ static void iov_consume(struct iovec iov[], unsigned num_iov, unsigned len) | |||
236 | unsigned int used; | 214 | unsigned int used; |
237 | 215 | ||
238 | used = iov[i].iov_len < len ? iov[i].iov_len : len; | 216 | used = iov[i].iov_len < len ? iov[i].iov_len : len; |
217 | if (dest) { | ||
218 | memcpy(dest, iov[i].iov_base, used); | ||
219 | dest += used; | ||
220 | } | ||
239 | iov[i].iov_base += used; | 221 | iov[i].iov_base += used; |
240 | iov[i].iov_len -= used; | 222 | iov[i].iov_len -= used; |
241 | len -= used; | 223 | len -= used; |
242 | } | 224 | } |
243 | assert(len == 0); | 225 | if (len != 0) |
226 | errx(1, "iovec too short!"); | ||
244 | } | 227 | } |
245 | 228 | ||
246 | /* The device virtqueue descriptors are followed by feature bitmasks. */ | 229 | /* The device virtqueue descriptors are followed by feature bitmasks. */ |
@@ -864,7 +847,7 @@ static void console_output(struct virtqueue *vq) | |||
864 | warn("Write to stdout gave %i (%d)", len, errno); | 847 | warn("Write to stdout gave %i (%d)", len, errno); |
865 | break; | 848 | break; |
866 | } | 849 | } |
867 | iov_consume(iov, out, len); | 850 | iov_consume(iov, out, NULL, len); |
868 | } | 851 | } |
869 | 852 | ||
870 | /* | 853 | /* |
@@ -1591,9 +1574,9 @@ static void blk_request(struct virtqueue *vq) | |||
1591 | { | 1574 | { |
1592 | struct vblk_info *vblk = vq->dev->priv; | 1575 | struct vblk_info *vblk = vq->dev->priv; |
1593 | unsigned int head, out_num, in_num, wlen; | 1576 | unsigned int head, out_num, in_num, wlen; |
1594 | int ret; | 1577 | int ret, i; |
1595 | u8 *in; | 1578 | u8 *in; |
1596 | struct virtio_blk_outhdr *out; | 1579 | struct virtio_blk_outhdr out; |
1597 | struct iovec iov[vq->vring.num]; | 1580 | struct iovec iov[vq->vring.num]; |
1598 | off64_t off; | 1581 | off64_t off; |
1599 | 1582 | ||
@@ -1603,32 +1586,36 @@ static void blk_request(struct virtqueue *vq) | |||
1603 | */ | 1586 | */ |
1604 | head = wait_for_vq_desc(vq, iov, &out_num, &in_num); | 1587 | head = wait_for_vq_desc(vq, iov, &out_num, &in_num); |
1605 | 1588 | ||
1606 | /* | 1589 | /* Copy the output header from the front of the iov (adjusts iov) */ |
1607 | * Every block request should contain at least one output buffer | 1590 | iov_consume(iov, out_num, &out, sizeof(out)); |
1608 | * (detailing the location on disk and the type of request) and one | 1591 | |
1609 | * input buffer (to hold the result). | 1592 | /* Find and trim end of iov input array, for our status byte. */ |
1610 | */ | 1593 | in = NULL; |
1611 | if (out_num == 0 || in_num == 0) | 1594 | for (i = out_num + in_num - 1; i >= out_num; i--) { |
1612 | errx(1, "Bad virtblk cmd %u out=%u in=%u", | 1595 | if (iov[i].iov_len > 0) { |
1613 | head, out_num, in_num); | 1596 | in = iov[i].iov_base + iov[i].iov_len - 1; |
1597 | iov[i].iov_len--; | ||
1598 | break; | ||
1599 | } | ||
1600 | } | ||
1601 | if (!in) | ||
1602 | errx(1, "Bad virtblk cmd with no room for status"); | ||
1614 | 1603 | ||
1615 | out = convert(&iov[0], struct virtio_blk_outhdr); | ||
1616 | in = convert(&iov[out_num+in_num-1], u8); | ||
1617 | /* | 1604 | /* |
1618 | * For historical reasons, block operations are expressed in 512 byte | 1605 | * For historical reasons, block operations are expressed in 512 byte |
1619 | * "sectors". | 1606 | * "sectors". |
1620 | */ | 1607 | */ |
1621 | off = out->sector * 512; | 1608 | off = out.sector * 512; |
1622 | 1609 | ||
1623 | /* | 1610 | /* |
1624 | * In general the virtio block driver is allowed to try SCSI commands. | 1611 | * In general the virtio block driver is allowed to try SCSI commands. |
1625 | * It'd be nice if we supported eject, for example, but we don't. | 1612 | * It'd be nice if we supported eject, for example, but we don't. |
1626 | */ | 1613 | */ |
1627 | if (out->type & VIRTIO_BLK_T_SCSI_CMD) { | 1614 | if (out.type & VIRTIO_BLK_T_SCSI_CMD) { |
1628 | fprintf(stderr, "Scsi commands unsupported\n"); | 1615 | fprintf(stderr, "Scsi commands unsupported\n"); |
1629 | *in = VIRTIO_BLK_S_UNSUPP; | 1616 | *in = VIRTIO_BLK_S_UNSUPP; |
1630 | wlen = sizeof(*in); | 1617 | wlen = sizeof(*in); |
1631 | } else if (out->type & VIRTIO_BLK_T_OUT) { | 1618 | } else if (out.type & VIRTIO_BLK_T_OUT) { |
1632 | /* | 1619 | /* |
1633 | * Write | 1620 | * Write |
1634 | * | 1621 | * |
@@ -1636,10 +1623,10 @@ static void blk_request(struct virtqueue *vq) | |||
1636 | * if they try to write past end. | 1623 | * if they try to write past end. |
1637 | */ | 1624 | */ |
1638 | if (lseek64(vblk->fd, off, SEEK_SET) != off) | 1625 | if (lseek64(vblk->fd, off, SEEK_SET) != off) |
1639 | err(1, "Bad seek to sector %llu", out->sector); | 1626 | err(1, "Bad seek to sector %llu", out.sector); |
1640 | 1627 | ||
1641 | ret = writev(vblk->fd, iov+1, out_num-1); | 1628 | ret = writev(vblk->fd, iov, out_num); |
1642 | verbose("WRITE to sector %llu: %i\n", out->sector, ret); | 1629 | verbose("WRITE to sector %llu: %i\n", out.sector, ret); |
1643 | 1630 | ||
1644 | /* | 1631 | /* |
1645 | * Grr... Now we know how long the descriptor they sent was, we | 1632 | * Grr... Now we know how long the descriptor they sent was, we |
@@ -1655,7 +1642,7 @@ static void blk_request(struct virtqueue *vq) | |||
1655 | 1642 | ||
1656 | wlen = sizeof(*in); | 1643 | wlen = sizeof(*in); |
1657 | *in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR); | 1644 | *in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR); |
1658 | } else if (out->type & VIRTIO_BLK_T_FLUSH) { | 1645 | } else if (out.type & VIRTIO_BLK_T_FLUSH) { |
1659 | /* Flush */ | 1646 | /* Flush */ |
1660 | ret = fdatasync(vblk->fd); | 1647 | ret = fdatasync(vblk->fd); |
1661 | verbose("FLUSH fdatasync: %i\n", ret); | 1648 | verbose("FLUSH fdatasync: %i\n", ret); |
@@ -1669,10 +1656,9 @@ static void blk_request(struct virtqueue *vq) | |||
1669 | * if they try to read past end. | 1656 | * if they try to read past end. |
1670 | */ | 1657 | */ |
1671 | if (lseek64(vblk->fd, off, SEEK_SET) != off) | 1658 | if (lseek64(vblk->fd, off, SEEK_SET) != off) |
1672 | err(1, "Bad seek to sector %llu", out->sector); | 1659 | err(1, "Bad seek to sector %llu", out.sector); |
1673 | 1660 | ||
1674 | ret = readv(vblk->fd, iov+1, in_num-1); | 1661 | ret = readv(vblk->fd, iov + out_num, in_num); |
1675 | verbose("READ from sector %llu: %i\n", out->sector, ret); | ||
1676 | if (ret >= 0) { | 1662 | if (ret >= 0) { |
1677 | wlen = sizeof(*in) + ret; | 1663 | wlen = sizeof(*in) + ret; |
1678 | *in = VIRTIO_BLK_S_OK; | 1664 | *in = VIRTIO_BLK_S_OK; |
@@ -1758,7 +1744,7 @@ static void rng_input(struct virtqueue *vq) | |||
1758 | len = readv(rng_info->rfd, iov, in_num); | 1744 | len = readv(rng_info->rfd, iov, in_num); |
1759 | if (len <= 0) | 1745 | if (len <= 0) |
1760 | err(1, "Read from /dev/random gave %i", len); | 1746 | err(1, "Read from /dev/random gave %i", len); |
1761 | iov_consume(iov, in_num, len); | 1747 | iov_consume(iov, in_num, NULL, len); |
1762 | totlen += len; | 1748 | totlen += len; |
1763 | } | 1749 | } |
1764 | 1750 | ||
diff --git a/tools/lguest/lguest.txt b/tools/lguest/lguest.txt index bff0c554485d..7203ace65e83 100644 --- a/tools/lguest/lguest.txt +++ b/tools/lguest/lguest.txt | |||
@@ -29,10 +29,6 @@ Running Lguest: | |||
29 | 29 | ||
30 | You will need to configure your kernel with the following options: | 30 | You will need to configure your kernel with the following options: |
31 | 31 | ||
32 | "General setup": | ||
33 | "Prompt for development and/or incomplete code/drivers" = Y | ||
34 | (CONFIG_EXPERIMENTAL=y) | ||
35 | |||
36 | "Processor type and features": | 32 | "Processor type and features": |
37 | "Paravirtualized guest support" = Y | 33 | "Paravirtualized guest support" = Y |
38 | "Lguest guest support" = Y | 34 | "Lguest guest support" = Y |
@@ -43,10 +39,10 @@ Running Lguest: | |||
43 | 39 | ||
44 | "Device Drivers": | 40 | "Device Drivers": |
45 | "Block devices" | 41 | "Block devices" |
46 | "Virtio block driver (EXPERIMENTAL)" = M/Y | 42 | "Virtio block driver" = M/Y |
47 | "Network device support" | 43 | "Network device support" |
48 | "Universal TUN/TAP device driver support" = M/Y | 44 | "Universal TUN/TAP device driver support" = M/Y |
49 | "Virtio network driver (EXPERIMENTAL)" = M/Y | 45 | "Virtio network driver" = M/Y |
50 | (CONFIG_VIRTIO_BLK=m, CONFIG_VIRTIO_NET=m and CONFIG_TUN=m) | 46 | (CONFIG_VIRTIO_BLK=m, CONFIG_VIRTIO_NET=m and CONFIG_TUN=m) |
51 | 47 | ||
52 | "Virtualization" | 48 | "Virtualization" |
diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index 04d959fa0226..a20e32033431 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile | |||
@@ -253,7 +253,7 @@ all_deps := $(all_objs:%.o=.%.d) | |||
253 | # let .d file also depends on the source and header files | 253 | # let .d file also depends on the source and header files |
254 | define check_deps | 254 | define check_deps |
255 | @set -e; $(RM) $@; \ | 255 | @set -e; $(RM) $@; \ |
256 | $(CC) -M $(CFLAGS) $< > $@.$$$$; \ | 256 | $(CC) -MM $(CFLAGS) $< > $@.$$$$; \ |
257 | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ | 257 | sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \ |
258 | $(RM) $@.$$$$ | 258 | $(RM) $@.$$$$ |
259 | endef | 259 | endef |
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index f2989c525e48..82b0606dcb8a 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
@@ -13,8 +13,7 @@ | |||
13 | * GNU Lesser General Public License for more details. | 13 | * GNU Lesser General Public License for more details. |
14 | * | 14 | * |
15 | * You should have received a copy of the GNU Lesser General Public | 15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this program; if not, write to the Free Software | 16 | * License along with this program; if not, see <http://www.gnu.org/licenses> |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | 17 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | * | 19 | * |
@@ -174,7 +173,7 @@ static int cmdline_init(struct pevent *pevent) | |||
174 | return 0; | 173 | return 0; |
175 | } | 174 | } |
176 | 175 | ||
177 | static char *find_cmdline(struct pevent *pevent, int pid) | 176 | static const char *find_cmdline(struct pevent *pevent, int pid) |
178 | { | 177 | { |
179 | const struct cmdline *comm; | 178 | const struct cmdline *comm; |
180 | struct cmdline key; | 179 | struct cmdline key; |
@@ -1224,6 +1223,34 @@ static int field_is_long(struct format_field *field) | |||
1224 | return 0; | 1223 | return 0; |
1225 | } | 1224 | } |
1226 | 1225 | ||
1226 | static unsigned int type_size(const char *name) | ||
1227 | { | ||
1228 | /* This covers all FIELD_IS_STRING types. */ | ||
1229 | static struct { | ||
1230 | const char *type; | ||
1231 | unsigned int size; | ||
1232 | } table[] = { | ||
1233 | { "u8", 1 }, | ||
1234 | { "u16", 2 }, | ||
1235 | { "u32", 4 }, | ||
1236 | { "u64", 8 }, | ||
1237 | { "s8", 1 }, | ||
1238 | { "s16", 2 }, | ||
1239 | { "s32", 4 }, | ||
1240 | { "s64", 8 }, | ||
1241 | { "char", 1 }, | ||
1242 | { }, | ||
1243 | }; | ||
1244 | int i; | ||
1245 | |||
1246 | for (i = 0; table[i].type; i++) { | ||
1247 | if (!strcmp(table[i].type, name)) | ||
1248 | return table[i].size; | ||
1249 | } | ||
1250 | |||
1251 | return 0; | ||
1252 | } | ||
1253 | |||
1227 | static int event_read_fields(struct event_format *event, struct format_field **fields) | 1254 | static int event_read_fields(struct event_format *event, struct format_field **fields) |
1228 | { | 1255 | { |
1229 | struct format_field *field = NULL; | 1256 | struct format_field *field = NULL; |
@@ -1233,6 +1260,8 @@ static int event_read_fields(struct event_format *event, struct format_field **f | |||
1233 | int count = 0; | 1260 | int count = 0; |
1234 | 1261 | ||
1235 | do { | 1262 | do { |
1263 | unsigned int size_dynamic = 0; | ||
1264 | |||
1236 | type = read_token(&token); | 1265 | type = read_token(&token); |
1237 | if (type == EVENT_NEWLINE) { | 1266 | if (type == EVENT_NEWLINE) { |
1238 | free_token(token); | 1267 | free_token(token); |
@@ -1391,6 +1420,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f | |||
1391 | field->type = new_type; | 1420 | field->type = new_type; |
1392 | strcat(field->type, " "); | 1421 | strcat(field->type, " "); |
1393 | strcat(field->type, field->name); | 1422 | strcat(field->type, field->name); |
1423 | size_dynamic = type_size(field->name); | ||
1394 | free_token(field->name); | 1424 | free_token(field->name); |
1395 | strcat(field->type, brackets); | 1425 | strcat(field->type, brackets); |
1396 | field->name = token; | 1426 | field->name = token; |
@@ -1463,7 +1493,8 @@ static int event_read_fields(struct event_format *event, struct format_field **f | |||
1463 | if (read_expect_type(EVENT_ITEM, &token)) | 1493 | if (read_expect_type(EVENT_ITEM, &token)) |
1464 | goto fail; | 1494 | goto fail; |
1465 | 1495 | ||
1466 | /* add signed type */ | 1496 | if (strtoul(token, NULL, 0)) |
1497 | field->flags |= FIELD_IS_SIGNED; | ||
1467 | 1498 | ||
1468 | free_token(token); | 1499 | free_token(token); |
1469 | if (read_expected(EVENT_OP, ";") < 0) | 1500 | if (read_expected(EVENT_OP, ";") < 0) |
@@ -1478,10 +1509,14 @@ static int event_read_fields(struct event_format *event, struct format_field **f | |||
1478 | if (field->flags & FIELD_IS_ARRAY) { | 1509 | if (field->flags & FIELD_IS_ARRAY) { |
1479 | if (field->arraylen) | 1510 | if (field->arraylen) |
1480 | field->elementsize = field->size / field->arraylen; | 1511 | field->elementsize = field->size / field->arraylen; |
1512 | else if (field->flags & FIELD_IS_DYNAMIC) | ||
1513 | field->elementsize = size_dynamic; | ||
1481 | else if (field->flags & FIELD_IS_STRING) | 1514 | else if (field->flags & FIELD_IS_STRING) |
1482 | field->elementsize = 1; | 1515 | field->elementsize = 1; |
1483 | else | 1516 | else if (field->flags & FIELD_IS_LONG) |
1484 | field->elementsize = event->pevent->long_size; | 1517 | field->elementsize = event->pevent ? |
1518 | event->pevent->long_size : | ||
1519 | sizeof(long); | ||
1485 | } else | 1520 | } else |
1486 | field->elementsize = field->size; | 1521 | field->elementsize = field->size; |
1487 | 1522 | ||
@@ -1785,6 +1820,8 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok) | |||
1785 | strcmp(token, "/") == 0 || | 1820 | strcmp(token, "/") == 0 || |
1786 | strcmp(token, "<") == 0 || | 1821 | strcmp(token, "<") == 0 || |
1787 | strcmp(token, ">") == 0 || | 1822 | strcmp(token, ">") == 0 || |
1823 | strcmp(token, "<=") == 0 || | ||
1824 | strcmp(token, ">=") == 0 || | ||
1788 | strcmp(token, "==") == 0 || | 1825 | strcmp(token, "==") == 0 || |
1789 | strcmp(token, "!=") == 0) { | 1826 | strcmp(token, "!=") == 0) { |
1790 | 1827 | ||
@@ -2481,7 +2518,7 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char ** | |||
2481 | 2518 | ||
2482 | free_token(token); | 2519 | free_token(token); |
2483 | arg = alloc_arg(); | 2520 | arg = alloc_arg(); |
2484 | if (!field) { | 2521 | if (!arg) { |
2485 | do_warning("%s: not enough memory!", __func__); | 2522 | do_warning("%s: not enough memory!", __func__); |
2486 | *tok = NULL; | 2523 | *tok = NULL; |
2487 | return EVENT_ERROR; | 2524 | return EVENT_ERROR; |
@@ -2637,7 +2674,7 @@ process_func_handler(struct event_format *event, struct pevent_function_handler | |||
2637 | struct print_arg *farg; | 2674 | struct print_arg *farg; |
2638 | enum event_type type; | 2675 | enum event_type type; |
2639 | char *token; | 2676 | char *token; |
2640 | char *test; | 2677 | const char *test; |
2641 | int i; | 2678 | int i; |
2642 | 2679 | ||
2643 | arg->type = PRINT_FUNC; | 2680 | arg->type = PRINT_FUNC; |
@@ -3889,7 +3926,7 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size, | |||
3889 | struct event_format *event, struct print_arg *arg) | 3926 | struct event_format *event, struct print_arg *arg) |
3890 | { | 3927 | { |
3891 | unsigned char *buf; | 3928 | unsigned char *buf; |
3892 | char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; | 3929 | const char *fmt = "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x"; |
3893 | 3930 | ||
3894 | if (arg->type == PRINT_FUNC) { | 3931 | if (arg->type == PRINT_FUNC) { |
3895 | process_defined_func(s, data, size, event, arg); | 3932 | process_defined_func(s, data, size, event, arg); |
@@ -3931,7 +3968,8 @@ static int is_printable_array(char *p, unsigned int len) | |||
3931 | return 1; | 3968 | return 1; |
3932 | } | 3969 | } |
3933 | 3970 | ||
3934 | static void print_event_fields(struct trace_seq *s, void *data, int size, | 3971 | static void print_event_fields(struct trace_seq *s, void *data, |
3972 | int size __maybe_unused, | ||
3935 | struct event_format *event) | 3973 | struct event_format *event) |
3936 | { | 3974 | { |
3937 | struct format_field *field; | 3975 | struct format_field *field; |
@@ -4408,7 +4446,7 @@ void pevent_event_info(struct trace_seq *s, struct event_format *event, | |||
4408 | void pevent_print_event(struct pevent *pevent, struct trace_seq *s, | 4446 | void pevent_print_event(struct pevent *pevent, struct trace_seq *s, |
4409 | struct pevent_record *record) | 4447 | struct pevent_record *record) |
4410 | { | 4448 | { |
4411 | static char *spaces = " "; /* 20 spaces */ | 4449 | static const char *spaces = " "; /* 20 spaces */ |
4412 | struct event_format *event; | 4450 | struct event_format *event; |
4413 | unsigned long secs; | 4451 | unsigned long secs; |
4414 | unsigned long usecs; | 4452 | unsigned long usecs; |
@@ -5070,8 +5108,8 @@ static const char * const pevent_error_str[] = { | |||
5070 | }; | 5108 | }; |
5071 | #undef _PE | 5109 | #undef _PE |
5072 | 5110 | ||
5073 | int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, | 5111 | int pevent_strerror(struct pevent *pevent __maybe_unused, |
5074 | char *buf, size_t buflen) | 5112 | enum pevent_errno errnum, char *buf, size_t buflen) |
5075 | { | 5113 | { |
5076 | int idx; | 5114 | int idx; |
5077 | const char *msg; | 5115 | const char *msg; |
@@ -5100,6 +5138,7 @@ int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum, | |||
5100 | case PEVENT_ERRNO__READ_FORMAT_FAILED: | 5138 | case PEVENT_ERRNO__READ_FORMAT_FAILED: |
5101 | case PEVENT_ERRNO__READ_PRINT_FAILED: | 5139 | case PEVENT_ERRNO__READ_PRINT_FAILED: |
5102 | case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED: | 5140 | case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED: |
5141 | case PEVENT_ERRNO__INVALID_ARG_TYPE: | ||
5103 | snprintf(buf, buflen, "%s", msg); | 5142 | snprintf(buf, buflen, "%s", msg); |
5104 | break; | 5143 | break; |
5105 | 5144 | ||
@@ -5362,7 +5401,7 @@ int pevent_register_print_function(struct pevent *pevent, | |||
5362 | if (type == PEVENT_FUNC_ARG_VOID) | 5401 | if (type == PEVENT_FUNC_ARG_VOID) |
5363 | break; | 5402 | break; |
5364 | 5403 | ||
5365 | if (type < 0 || type >= PEVENT_FUNC_ARG_MAX_TYPES) { | 5404 | if (type >= PEVENT_FUNC_ARG_MAX_TYPES) { |
5366 | do_warning("Invalid argument type %d", type); | 5405 | do_warning("Invalid argument type %d", type); |
5367 | ret = PEVENT_ERRNO__INVALID_ARG_TYPE; | 5406 | ret = PEVENT_ERRNO__INVALID_ARG_TYPE; |
5368 | goto out_free; | 5407 | goto out_free; |
@@ -5560,7 +5599,7 @@ void pevent_free(struct pevent *pevent) | |||
5560 | } | 5599 | } |
5561 | 5600 | ||
5562 | if (pevent->func_map) { | 5601 | if (pevent->func_map) { |
5563 | for (i = 0; i < pevent->func_count; i++) { | 5602 | for (i = 0; i < (int)pevent->func_count; i++) { |
5564 | free(pevent->func_map[i].func); | 5603 | free(pevent->func_map[i].func); |
5565 | free(pevent->func_map[i].mod); | 5604 | free(pevent->func_map[i].mod); |
5566 | } | 5605 | } |
@@ -5582,7 +5621,7 @@ void pevent_free(struct pevent *pevent) | |||
5582 | } | 5621 | } |
5583 | 5622 | ||
5584 | if (pevent->printk_map) { | 5623 | if (pevent->printk_map) { |
5585 | for (i = 0; i < pevent->printk_count; i++) | 5624 | for (i = 0; i < (int)pevent->printk_count; i++) |
5586 | free(pevent->printk_map[i].printk); | 5625 | free(pevent->printk_map[i].printk); |
5587 | free(pevent->printk_map); | 5626 | free(pevent->printk_map); |
5588 | } | 5627 | } |
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 24a4bbabc5d5..7be7e89533e4 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h | |||
@@ -13,8 +13,7 @@ | |||
13 | * GNU Lesser General Public License for more details. | 13 | * GNU Lesser General Public License for more details. |
14 | * | 14 | * |
15 | * You should have received a copy of the GNU Lesser General Public | 15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this program; if not, write to the Free Software | 16 | * License along with this program; if not, see <http://www.gnu.org/licenses> |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | 17 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 19 | */ |
diff --git a/tools/lib/traceevent/event-utils.h b/tools/lib/traceevent/event-utils.h index bc075006966e..e76c9acb92cd 100644 --- a/tools/lib/traceevent/event-utils.h +++ b/tools/lib/traceevent/event-utils.h | |||
@@ -13,8 +13,7 @@ | |||
13 | * GNU Lesser General Public License for more details. | 13 | * GNU Lesser General Public License for more details. |
14 | * | 14 | * |
15 | * You should have received a copy of the GNU Lesser General Public | 15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this program; if not, write to the Free Software | 16 | * License along with this program; if not, see <http://www.gnu.org/licenses> |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | 17 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 19 | */ |
diff --git a/tools/lib/traceevent/parse-filter.c b/tools/lib/traceevent/parse-filter.c index 5ea4326ad11f..2500e75583fc 100644 --- a/tools/lib/traceevent/parse-filter.c +++ b/tools/lib/traceevent/parse-filter.c | |||
@@ -13,8 +13,7 @@ | |||
13 | * GNU Lesser General Public License for more details. | 13 | * GNU Lesser General Public License for more details. |
14 | * | 14 | * |
15 | * You should have received a copy of the GNU Lesser General Public | 15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this program; if not, write to the Free Software | 16 | * License along with this program; if not, see <http://www.gnu.org/licenses> |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | 17 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 19 | */ |
diff --git a/tools/lib/traceevent/parse-utils.c b/tools/lib/traceevent/parse-utils.c index f023a133abb6..bba701cf10e6 100644 --- a/tools/lib/traceevent/parse-utils.c +++ b/tools/lib/traceevent/parse-utils.c | |||
@@ -1,3 +1,22 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
3 | * | ||
4 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Lesser General Public | ||
7 | * License as published by the Free Software Foundation; | ||
8 | * version 2.1 of the License (not later!) | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU Lesser General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU Lesser General Public | ||
16 | * License along with this program; if not, see <http://www.gnu.org/licenses> | ||
17 | * | ||
18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
19 | */ | ||
1 | #include <stdio.h> | 20 | #include <stdio.h> |
2 | #include <stdlib.h> | 21 | #include <stdlib.h> |
3 | #include <string.h> | 22 | #include <string.h> |
diff --git a/tools/lib/traceevent/trace-seq.c b/tools/lib/traceevent/trace-seq.c index b1ccc923e8a5..a57db805136a 100644 --- a/tools/lib/traceevent/trace-seq.c +++ b/tools/lib/traceevent/trace-seq.c | |||
@@ -13,8 +13,7 @@ | |||
13 | * GNU Lesser General Public License for more details. | 13 | * GNU Lesser General Public License for more details. |
14 | * | 14 | * |
15 | * You should have received a copy of the GNU Lesser General Public | 15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this program; if not, write to the Free Software | 16 | * License along with this program; if not, see <http://www.gnu.org/licenses> |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | 17 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 18 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 19 | */ |
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index 9f2e44f2b17a..eb30044a922a 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile | |||
@@ -1,3 +1,5 @@ | |||
1 | include ../config/utilities.mak | ||
2 | |||
1 | OUTPUT := ./ | 3 | OUTPUT := ./ |
2 | ifeq ("$(origin O)", "command line") | 4 | ifeq ("$(origin O)", "command line") |
3 | ifneq ($(O),) | 5 | ifneq ($(O),) |
@@ -64,6 +66,7 @@ MAKEINFO=makeinfo | |||
64 | INSTALL_INFO=install-info | 66 | INSTALL_INFO=install-info |
65 | DOCBOOK2X_TEXI=docbook2x-texi | 67 | DOCBOOK2X_TEXI=docbook2x-texi |
66 | DBLATEX=dblatex | 68 | DBLATEX=dblatex |
69 | XMLTO=xmlto | ||
67 | ifndef PERL_PATH | 70 | ifndef PERL_PATH |
68 | PERL_PATH = /usr/bin/perl | 71 | PERL_PATH = /usr/bin/perl |
69 | endif | 72 | endif |
@@ -71,6 +74,16 @@ endif | |||
71 | -include ../config.mak.autogen | 74 | -include ../config.mak.autogen |
72 | -include ../config.mak | 75 | -include ../config.mak |
73 | 76 | ||
77 | _tmp_tool_path := $(call get-executable,$(ASCIIDOC)) | ||
78 | ifeq ($(_tmp_tool_path),) | ||
79 | missing_tools = $(ASCIIDOC) | ||
80 | endif | ||
81 | |||
82 | _tmp_tool_path := $(call get-executable,$(XMLTO)) | ||
83 | ifeq ($(_tmp_tool_path),) | ||
84 | missing_tools += $(XMLTO) | ||
85 | endif | ||
86 | |||
74 | # | 87 | # |
75 | # For asciidoc ... | 88 | # For asciidoc ... |
76 | # -7.1.2, no extra settings are needed. | 89 | # -7.1.2, no extra settings are needed. |
@@ -170,7 +183,12 @@ pdf: $(OUTPUT)user-manual.pdf | |||
170 | 183 | ||
171 | install: install-man | 184 | install: install-man |
172 | 185 | ||
173 | install-man: man | 186 | check-man-tools: |
187 | ifdef missing_tools | ||
188 | $(error "You need to install $(missing_tools) for man pages") | ||
189 | endif | ||
190 | |||
191 | do-install-man: man | ||
174 | $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) | 192 | $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir) |
175 | # $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir) | 193 | # $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir) |
176 | # $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir) | 194 | # $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir) |
@@ -178,6 +196,15 @@ install-man: man | |||
178 | # $(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir) | 196 | # $(INSTALL) -m 644 $(DOC_MAN5) $(DESTDIR)$(man5dir) |
179 | # $(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir) | 197 | # $(INSTALL) -m 644 $(DOC_MAN7) $(DESTDIR)$(man7dir) |
180 | 198 | ||
199 | install-man: check-man-tools man | ||
200 | |||
201 | try-install-man: | ||
202 | ifdef missing_tools | ||
203 | $(warning Please install $(missing_tools) to have the man pages installed) | ||
204 | else | ||
205 | $(MAKE) do-install-man | ||
206 | endif | ||
207 | |||
181 | install-info: info | 208 | install-info: info |
182 | $(INSTALL) -d -m 755 $(DESTDIR)$(infodir) | 209 | $(INSTALL) -d -m 755 $(DESTDIR)$(infodir) |
183 | $(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir) | 210 | $(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir) |
@@ -195,10 +222,14 @@ install-pdf: pdf | |||
195 | #install-html: html | 222 | #install-html: html |
196 | # '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir) | 223 | # '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir) |
197 | 224 | ||
225 | ifneq ($(MAKECMDGOALS),clean) | ||
226 | ifneq ($(MAKECMDGOALS),tags) | ||
198 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | 227 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE |
199 | $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) $(OUTPUT)PERF-VERSION-FILE | 228 | $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) $(OUTPUT)PERF-VERSION-FILE |
200 | 229 | ||
201 | -include $(OUTPUT)PERF-VERSION-FILE | 230 | -include $(OUTPUT)PERF-VERSION-FILE |
231 | endif | ||
232 | endif | ||
202 | 233 | ||
203 | # | 234 | # |
204 | # Determine "include::" file references in asciidoc files. | 235 | # Determine "include::" file references in asciidoc files. |
@@ -246,7 +277,7 @@ $(MAN_HTML): $(OUTPUT)%.html : %.txt | |||
246 | 277 | ||
247 | $(OUTPUT)%.1 $(OUTPUT)%.5 $(OUTPUT)%.7 : $(OUTPUT)%.xml | 278 | $(OUTPUT)%.1 $(OUTPUT)%.5 $(OUTPUT)%.7 : $(OUTPUT)%.xml |
248 | $(QUIET_XMLTO)$(RM) $@ && \ | 279 | $(QUIET_XMLTO)$(RM) $@ && \ |
249 | xmlto -o $(OUTPUT) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< | 280 | $(XMLTO) -o $(OUTPUT) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< |
250 | 281 | ||
251 | $(OUTPUT)%.xml : %.txt | 282 | $(OUTPUT)%.xml : %.txt |
252 | $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ | 283 | $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ |
diff --git a/tools/perf/Documentation/android.txt b/tools/perf/Documentation/android.txt new file mode 100644 index 000000000000..8484c3a04a6a --- /dev/null +++ b/tools/perf/Documentation/android.txt | |||
@@ -0,0 +1,78 @@ | |||
1 | How to compile perf for Android | ||
2 | ========================================= | ||
3 | |||
4 | I. Set the Android NDK environment | ||
5 | ------------------------------------------------ | ||
6 | |||
7 | (a). Use the Android NDK | ||
8 | ------------------------------------------------ | ||
9 | 1. You need to download and install the Android Native Development Kit (NDK). | ||
10 | Set the NDK variable to point to the path where you installed the NDK: | ||
11 | export NDK=/path/to/android-ndk | ||
12 | |||
13 | 2. Set cross-compiling environment variables for NDK toolchain and sysroot. | ||
14 | For arm: | ||
15 | export NDK_TOOLCHAIN=${NDK}/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi- | ||
16 | export NDK_SYSROOT=${NDK}/platforms/android-9/arch-arm | ||
17 | For x86: | ||
18 | export NDK_TOOLCHAIN=${NDK}/toolchains/x86-4.6/prebuilt/linux-x86/bin/i686-linux-android- | ||
19 | export NDK_SYSROOT=${NDK}/platforms/android-9/arch-x86 | ||
20 | |||
21 | This method is not working for Android NDK versions up to Revision 8b. | ||
22 | perf uses some bionic enhancements that are not included in these NDK versions. | ||
23 | You can use method (b) described below instead. | ||
24 | |||
25 | (b). Use the Android source tree | ||
26 | ----------------------------------------------- | ||
27 | 1. Download the master branch of the Android source tree. | ||
28 | Set the environment for the target you want using: | ||
29 | source build/envsetup.sh | ||
30 | lunch | ||
31 | |||
32 | 2. Build your own NDK sysroot to contain latest bionic changes and set the | ||
33 | NDK sysroot environment variable. | ||
34 | cd ${ANDROID_BUILD_TOP}/ndk | ||
35 | For arm: | ||
36 | ./build/tools/build-ndk-sysroot.sh --abi=arm | ||
37 | export NDK_SYSROOT=${ANDROID_BUILD_TOP}/ndk/build/platforms/android-3/arch-arm | ||
38 | For x86: | ||
39 | ./build/tools/build-ndk-sysroot.sh --abi=x86 | ||
40 | export NDK_SYSROOT=${ANDROID_BUILD_TOP}/ndk/build/platforms/android-3/arch-x86 | ||
41 | |||
42 | 3. Set the NDK toolchain environment variable. | ||
43 | For arm: | ||
44 | export NDK_TOOLCHAIN=${ANDROID_TOOLCHAIN}/arm-linux-androideabi- | ||
45 | For x86: | ||
46 | export NDK_TOOLCHAIN=${ANDROID_TOOLCHAIN}/i686-linux-android- | ||
47 | |||
48 | II. Compile perf for Android | ||
49 | ------------------------------------------------ | ||
50 | You need to run make with the NDK toolchain and sysroot defined above: | ||
51 | For arm: | ||
52 | make ARCH=arm CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}" | ||
53 | For x86: | ||
54 | make ARCH=x86 CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}" | ||
55 | |||
56 | III. Install perf | ||
57 | ----------------------------------------------- | ||
58 | You need to connect to your Android device/emulator using adb. | ||
59 | Install perf using: | ||
60 | adb push perf /data/perf | ||
61 | |||
62 | If you also want to use perf-archive you need busybox tools for Android. | ||
63 | For installing perf-archive, you first need to replace #!/bin/bash with #!/system/bin/sh: | ||
64 | sed 's/#!\/bin\/bash/#!\/system\/bin\/sh/g' perf-archive >> /tmp/perf-archive | ||
65 | chmod +x /tmp/perf-archive | ||
66 | adb push /tmp/perf-archive /data/perf-archive | ||
67 | |||
68 | IV. Environment settings for running perf | ||
69 | ------------------------------------------------ | ||
70 | Some perf features need environment variables to run properly. | ||
71 | You need to set these before running perf on the target: | ||
72 | adb shell | ||
73 | # PERF_PAGER=cat | ||
74 | |||
75 | IV. Run perf | ||
76 | ------------------------------------------------ | ||
77 | Run perf on your device/emulator to which you previously connected using adb: | ||
78 | # ./data/perf | ||
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index c8ffd9fd5c6a..5ad07ef417f0 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt | |||
@@ -61,11 +61,13 @@ OPTIONS | |||
61 | 61 | ||
62 | --stdio:: Use the stdio interface. | 62 | --stdio:: Use the stdio interface. |
63 | 63 | ||
64 | --tui:: Use the TUI interface Use of --tui requires a tty, if one is not | 64 | --tui:: Use the TUI interface. Use of --tui requires a tty, if one is not |
65 | present, as when piping to other commands, the stdio interface is | 65 | present, as when piping to other commands, the stdio interface is |
66 | used. This interfaces starts by centering on the line with more | 66 | used. This interfaces starts by centering on the line with more |
67 | samples, TAB/UNTAB cycles through the lines with more samples. | 67 | samples, TAB/UNTAB cycles through the lines with more samples. |
68 | 68 | ||
69 | --gtk:: Use the GTK interface. | ||
70 | |||
69 | -C:: | 71 | -C:: |
70 | --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can | 72 | --cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can |
71 | be provided as a comma-separated list with no space: 0,1. Ranges of | 73 | be provided as a comma-separated list with no space: 0,1. Ranges of |
@@ -88,6 +90,9 @@ OPTIONS | |||
88 | --objdump=<path>:: | 90 | --objdump=<path>:: |
89 | Path to objdump binary. | 91 | Path to objdump binary. |
90 | 92 | ||
93 | --skip-missing:: | ||
94 | Skip symbols that cannot be annotated. | ||
95 | |||
91 | SEE ALSO | 96 | SEE ALSO |
92 | -------- | 97 | -------- |
93 | linkperf:perf-record[1], linkperf:perf-report[1] | 98 | linkperf:perf-record[1], linkperf:perf-report[1] |
diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index c1057701a7dc..e9a8349a7172 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt | |||
@@ -24,6 +24,13 @@ OPTIONS | |||
24 | -r:: | 24 | -r:: |
25 | --remove=:: | 25 | --remove=:: |
26 | Remove specified file from the cache. | 26 | Remove specified file from the cache. |
27 | -M:: | ||
28 | --missing=:: | ||
29 | List missing build ids in the cache for the specified file. | ||
30 | -u:: | ||
31 | --update:: | ||
32 | Update specified file of the cache. It can be used to update kallsyms | ||
33 | kernel dso to vmlinux in order to support annotation. | ||
27 | -v:: | 34 | -v:: |
28 | --verbose:: | 35 | --verbose:: |
29 | Be more verbose. | 36 | Be more verbose. |
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index ab7f667de1b1..5b3123d5721f 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
@@ -22,10 +22,6 @@ specified perf.data files. | |||
22 | 22 | ||
23 | OPTIONS | 23 | OPTIONS |
24 | ------- | 24 | ------- |
25 | -M:: | ||
26 | --displacement:: | ||
27 | Show position displacement relative to baseline. | ||
28 | |||
29 | -D:: | 25 | -D:: |
30 | --dump-raw-trace:: | 26 | --dump-raw-trace:: |
31 | Dump raw trace in ASCII. | 27 | Dump raw trace in ASCII. |
@@ -72,6 +68,66 @@ OPTIONS | |||
72 | --symfs=<directory>:: | 68 | --symfs=<directory>:: |
73 | Look for files with symbols relative to this directory. | 69 | Look for files with symbols relative to this directory. |
74 | 70 | ||
71 | -b:: | ||
72 | --baseline-only:: | ||
73 | Show only items with match in baseline. | ||
74 | |||
75 | -c:: | ||
76 | --compute:: | ||
77 | Differential computation selection - delta,ratio,wdiff (default is delta). | ||
78 | If '+' is specified as a first character, the output is sorted based | ||
79 | on the computation results. | ||
80 | See COMPARISON METHODS section for more info. | ||
81 | |||
82 | -p:: | ||
83 | --period:: | ||
84 | Show period values for both compared hist entries. | ||
85 | |||
86 | -F:: | ||
87 | --formula:: | ||
88 | Show formula for given computation. | ||
89 | |||
90 | COMPARISON METHODS | ||
91 | ------------------ | ||
92 | delta | ||
93 | ~~~~~ | ||
94 | If specified the 'Delta' column is displayed with value 'd' computed as: | ||
95 | |||
96 | d = A->period_percent - B->period_percent | ||
97 | |||
98 | with: | ||
99 | - A/B being matching hist entry from first/second file specified | ||
100 | (or perf.data/perf.data.old) respectively. | ||
101 | |||
102 | - period_percent being the % of the hist entry period value within | ||
103 | single data file | ||
104 | |||
105 | ratio | ||
106 | ~~~~~ | ||
107 | If specified the 'Ratio' column is displayed with value 'r' computed as: | ||
108 | |||
109 | r = A->period / B->period | ||
110 | |||
111 | with: | ||
112 | - A/B being matching hist entry from first/second file specified | ||
113 | (or perf.data/perf.data.old) respectively. | ||
114 | |||
115 | - period being the hist entry period value | ||
116 | |||
117 | wdiff | ||
118 | ~~~~~ | ||
119 | If specified the 'Weighted diff' column is displayed with value 'd' computed as: | ||
120 | |||
121 | d = B->period * WEIGHT-A - A->period * WEIGHT-B | ||
122 | |||
123 | - A/B being matching hist entry from first/second file specified | ||
124 | (or perf.data/perf.data.old) respectively. | ||
125 | |||
126 | - period being the hist entry period value | ||
127 | |||
128 | - WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option | ||
129 | behind ':' separator like '-c wdiff:1,2'. | ||
130 | |||
75 | SEE ALSO | 131 | SEE ALSO |
76 | -------- | 132 | -------- |
77 | linkperf:perf-record[1] | 133 | linkperf:perf-record[1] |
diff --git a/tools/perf/Documentation/perf-evlist.txt b/tools/perf/Documentation/perf-evlist.txt index 15217345c2fa..1ceb3700ffbb 100644 --- a/tools/perf/Documentation/perf-evlist.txt +++ b/tools/perf/Documentation/perf-evlist.txt | |||
@@ -28,6 +28,10 @@ OPTIONS | |||
28 | --verbose=:: | 28 | --verbose=:: |
29 | Show all fields. | 29 | Show all fields. |
30 | 30 | ||
31 | -g:: | ||
32 | --group:: | ||
33 | Show event group information. | ||
34 | |||
31 | SEE ALSO | 35 | SEE ALSO |
32 | -------- | 36 | -------- |
33 | linkperf:perf-record[1], linkperf:perf-list[1], | 37 | linkperf:perf-record[1], linkperf:perf-list[1], |
diff --git a/tools/perf/Documentation/perf-inject.txt b/tools/perf/Documentation/perf-inject.txt index 025630d43cd2..a00a34276c54 100644 --- a/tools/perf/Documentation/perf-inject.txt +++ b/tools/perf/Documentation/perf-inject.txt | |||
@@ -29,6 +29,17 @@ OPTIONS | |||
29 | -v:: | 29 | -v:: |
30 | --verbose:: | 30 | --verbose:: |
31 | Be more verbose. | 31 | Be more verbose. |
32 | -i:: | ||
33 | --input=:: | ||
34 | Input file name. (default: stdin) | ||
35 | -o:: | ||
36 | --output=:: | ||
37 | Output file name. (default: stdout) | ||
38 | -s:: | ||
39 | --sched-stat:: | ||
40 | Merge sched_stat and sched_switch for getting events where and how long | ||
41 | tasks slept. sched_switch contains a callchain where a task slept and | ||
42 | sched_stat contains a timeslice how long a task slept. | ||
32 | 43 | ||
33 | SEE ALSO | 44 | SEE ALSO |
34 | -------- | 45 | -------- |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index b38a1f9ad460..938e8904f64d 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -175,7 +175,7 @@ following filters are defined: | |||
175 | 175 | ||
176 | + | 176 | + |
177 | The option requires at least one branch type among any, any_call, any_ret, ind_call. | 177 | The option requires at least one branch type among any, any_call, any_ret, ind_call. |
178 | The privilege levels may be ommitted, in which case, the privilege levels of the associated | 178 | The privilege levels may be omitted, in which case, the privilege levels of the associated |
179 | event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege | 179 | event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege |
180 | levels are subject to permissions. When sampling on multiple events, branch stack sampling | 180 | levels are subject to permissions. When sampling on multiple events, branch stack sampling |
181 | is enabled for all the sampling events. The sampled branch type is the same for all events. | 181 | is enabled for all the sampling events. The sampled branch type is the same for all events. |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index f4d91bebd59d..02284a0067f0 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -57,11 +57,44 @@ OPTIONS | |||
57 | 57 | ||
58 | -s:: | 58 | -s:: |
59 | --sort=:: | 59 | --sort=:: |
60 | Sort by key(s): pid, comm, dso, symbol, parent, srcline. | 60 | Sort histogram entries by given key(s) - multiple keys can be specified |
61 | in CSV format. Following sort keys are available: | ||
62 | pid, comm, dso, symbol, parent, cpu, srcline. | ||
63 | |||
64 | Each key has following meaning: | ||
65 | |||
66 | - comm: command (name) of the task which can be read via /proc/<pid>/comm | ||
67 | - pid: command and tid of the task | ||
68 | - dso: name of library or module executed at the time of sample | ||
69 | - symbol: name of function executed at the time of sample | ||
70 | - parent: name of function matched to the parent regex filter. Unmatched | ||
71 | entries are displayed as "[other]". | ||
72 | - cpu: cpu number the task ran at the time of sample | ||
73 | - srcline: filename and line number executed at the time of sample. The | ||
74 | DWARF debuggin info must be provided. | ||
75 | |||
76 | By default, comm, dso and symbol keys are used. | ||
77 | (i.e. --sort comm,dso,symbol) | ||
78 | |||
79 | If --branch-stack option is used, following sort keys are also | ||
80 | available: | ||
81 | dso_from, dso_to, symbol_from, symbol_to, mispredict. | ||
82 | |||
83 | - dso_from: name of library or module branched from | ||
84 | - dso_to: name of library or module branched to | ||
85 | - symbol_from: name of function branched from | ||
86 | - symbol_to: name of function branched to | ||
87 | - mispredict: "N" for predicted branch, "Y" for mispredicted branch | ||
88 | |||
89 | And default sort keys are changed to comm, dso_from, symbol_from, dso_to | ||
90 | and symbol_to, see '--branch-stack'. | ||
61 | 91 | ||
62 | -p:: | 92 | -p:: |
63 | --parent=<regex>:: | 93 | --parent=<regex>:: |
64 | regex filter to identify parent, see: '--sort parent' | 94 | A regex filter to identify parent. The parent is a caller of this |
95 | function and searched through the callchain, thus it requires callchain | ||
96 | information recorded. The pattern is in the exteneded regex format and | ||
97 | defaults to "\^sys_|^do_page_fault", see '--sort parent'. | ||
65 | 98 | ||
66 | -x:: | 99 | -x:: |
67 | --exclude-other:: | 100 | --exclude-other:: |
@@ -74,7 +107,6 @@ OPTIONS | |||
74 | 107 | ||
75 | -t:: | 108 | -t:: |
76 | --field-separator=:: | 109 | --field-separator=:: |
77 | |||
78 | Use a special separator character and don't pad with spaces, replacing | 110 | Use a special separator character and don't pad with spaces, replacing |
79 | all occurrences of this separator in symbol names (and other output) | 111 | all occurrences of this separator in symbol names (and other output) |
80 | with a '.' character, that thus it's the only non valid separator. | 112 | with a '.' character, that thus it's the only non valid separator. |
@@ -171,6 +203,9 @@ OPTIONS | |||
171 | --objdump=<path>:: | 203 | --objdump=<path>:: |
172 | Path to objdump binary. | 204 | Path to objdump binary. |
173 | 205 | ||
206 | --group:: | ||
207 | Show event group information together. | ||
208 | |||
174 | SEE ALSO | 209 | SEE ALSO |
175 | -------- | 210 | -------- |
176 | linkperf:perf-stat[1], linkperf:perf-annotate[1] | 211 | linkperf:perf-stat[1], linkperf:perf-annotate[1] |
diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt index a4027f221a53..9f1f054b8432 100644 --- a/tools/perf/Documentation/perf-script-python.txt +++ b/tools/perf/Documentation/perf-script-python.txt | |||
@@ -336,7 +336,6 @@ scripts listed by the 'perf script -l' command e.g.: | |||
336 | ---- | 336 | ---- |
337 | root@tropicana:~# perf script -l | 337 | root@tropicana:~# perf script -l |
338 | List of available trace scripts: | 338 | List of available trace scripts: |
339 | workqueue-stats workqueue stats (ins/exe/create/destroy) | ||
340 | wakeup-latency system-wide min/max/avg wakeup latency | 339 | wakeup-latency system-wide min/max/avg wakeup latency |
341 | rw-by-file <comm> r/w activity for a program, by file | 340 | rw-by-file <comm> r/w activity for a program, by file |
342 | rw-by-pid system-wide r/w activity | 341 | rw-by-pid system-wide r/w activity |
@@ -402,7 +401,6 @@ should show a new entry for your script: | |||
402 | ---- | 401 | ---- |
403 | root@tropicana:~# perf script -l | 402 | root@tropicana:~# perf script -l |
404 | List of available trace scripts: | 403 | List of available trace scripts: |
405 | workqueue-stats workqueue stats (ins/exe/create/destroy) | ||
406 | wakeup-latency system-wide min/max/avg wakeup latency | 404 | wakeup-latency system-wide min/max/avg wakeup latency |
407 | rw-by-file <comm> r/w activity for a program, by file | 405 | rw-by-file <comm> r/w activity for a program, by file |
408 | rw-by-pid system-wide r/w activity | 406 | rw-by-pid system-wide r/w activity |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 2fa173b51970..faf4f4feebcc 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -108,7 +108,23 @@ with it. --append may be used here. Examples: | |||
108 | 3>results perf stat --log-fd 3 -- $cmd | 108 | 3>results perf stat --log-fd 3 -- $cmd |
109 | 3>>results perf stat --log-fd 3 --append -- $cmd | 109 | 3>>results perf stat --log-fd 3 --append -- $cmd |
110 | 110 | ||
111 | 111 | --pre:: | |
112 | --post:: | ||
113 | Pre and post measurement hooks, e.g.: | ||
114 | |||
115 | perf stat --repeat 10 --null --sync --pre 'make -s O=defconfig-build/clean' -- make -s -j64 O=defconfig-build/ bzImage | ||
116 | |||
117 | -I msecs:: | ||
118 | --interval-print msecs:: | ||
119 | Print count deltas every N milliseconds (minimum: 100ms) | ||
120 | example: perf stat -I 1000 -e cycles -a sleep 5 | ||
121 | |||
122 | --aggr-socket:: | ||
123 | Aggregate counts per processor socket for system-wide mode measurements. This | ||
124 | is a useful mode to detect imbalance between sockets. To enable this mode, | ||
125 | use --aggr-socket in addition to -a. (system-wide). The output includes the | ||
126 | socket number and the number of online processors on that socket. This is | ||
127 | useful to gauge the amount of aggregation. | ||
112 | 128 | ||
113 | EXAMPLES | 129 | EXAMPLES |
114 | -------- | 130 | -------- |
diff --git a/tools/perf/Documentation/perf-test.txt b/tools/perf/Documentation/perf-test.txt index b24ac40fcd58..d1d3e5121f89 100644 --- a/tools/perf/Documentation/perf-test.txt +++ b/tools/perf/Documentation/perf-test.txt | |||
@@ -23,6 +23,10 @@ from 'perf test list'. | |||
23 | 23 | ||
24 | OPTIONS | 24 | OPTIONS |
25 | ------- | 25 | ------- |
26 | -s:: | ||
27 | --skip:: | ||
28 | Tests to skip (comma separater numeric list). | ||
29 | |||
26 | -v:: | 30 | -v:: |
27 | --verbose:: | 31 | --verbose:: |
28 | Be more verbose. | 32 | Be more verbose. |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 5b80d84d6b4a..a414bc95fd52 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -60,7 +60,7 @@ Default is to monitor all CPUS. | |||
60 | 60 | ||
61 | -i:: | 61 | -i:: |
62 | --inherit:: | 62 | --inherit:: |
63 | Child tasks inherit counters, only makes sens with -p option. | 63 | Child tasks do not inherit counters. |
64 | 64 | ||
65 | -k <path>:: | 65 | -k <path>:: |
66 | --vmlinux=<path>:: | 66 | --vmlinux=<path>:: |
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 3a2ae37310a9..68718ccdd178 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -48,6 +48,12 @@ comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0- | |||
48 | In per-thread mode with inheritance mode on (default), Events are captured only when | 48 | In per-thread mode with inheritance mode on (default), Events are captured only when |
49 | the thread executes on the designated CPUs. Default is to monitor all CPUs. | 49 | the thread executes on the designated CPUs. Default is to monitor all CPUs. |
50 | 50 | ||
51 | --duration: | ||
52 | Show only events that had a duration greater than N.M ms. | ||
53 | |||
54 | --sched: | ||
55 | Accrue thread runtime and provide a summary at the end of the session. | ||
56 | |||
51 | SEE ALSO | 57 | SEE ALSO |
52 | -------- | 58 | -------- |
53 | linkperf:perf-record[1], linkperf:perf-script[1] | 59 | linkperf:perf-record[1], linkperf:perf-script[1] |
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 80db3f4bcf7a..39d41068484f 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST | |||
@@ -11,11 +11,21 @@ lib/rbtree.c | |||
11 | include/linux/swab.h | 11 | include/linux/swab.h |
12 | arch/*/include/asm/unistd*.h | 12 | arch/*/include/asm/unistd*.h |
13 | arch/*/include/asm/perf_regs.h | 13 | arch/*/include/asm/perf_regs.h |
14 | arch/*/include/uapi/asm/unistd*.h | ||
15 | arch/*/include/uapi/asm/perf_regs.h | ||
14 | arch/*/lib/memcpy*.S | 16 | arch/*/lib/memcpy*.S |
15 | arch/*/lib/memset*.S | 17 | arch/*/lib/memset*.S |
16 | include/linux/poison.h | 18 | include/linux/poison.h |
17 | include/linux/magic.h | 19 | include/linux/magic.h |
18 | include/linux/hw_breakpoint.h | 20 | include/linux/hw_breakpoint.h |
21 | include/linux/rbtree_augmented.h | ||
22 | include/uapi/linux/perf_event.h | ||
23 | include/uapi/linux/const.h | ||
24 | include/uapi/linux/swab.h | ||
25 | include/uapi/linux/hw_breakpoint.h | ||
19 | arch/x86/include/asm/svm.h | 26 | arch/x86/include/asm/svm.h |
20 | arch/x86/include/asm/vmx.h | 27 | arch/x86/include/asm/vmx.h |
21 | arch/x86/include/asm/kvm_host.h | 28 | arch/x86/include/asm/kvm_host.h |
29 | arch/x86/include/uapi/asm/svm.h | ||
30 | arch/x86/include/uapi/asm/vmx.h | ||
31 | arch/x86/include/uapi/asm/kvm.h | ||
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 00deed4d6159..a2108ca1cc17 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -47,10 +47,11 @@ include config/utilities.mak | |||
47 | # backtrace post unwind. | 47 | # backtrace post unwind. |
48 | # | 48 | # |
49 | # Define NO_BACKTRACE if you do not want stack backtrace debug feature | 49 | # Define NO_BACKTRACE if you do not want stack backtrace debug feature |
50 | # | ||
51 | # Define NO_LIBNUMA if you do not want numa perf benchmark | ||
50 | 52 | ||
51 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | 53 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE |
52 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) | 54 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) |
53 | -include $(OUTPUT)PERF-VERSION-FILE | ||
54 | 55 | ||
55 | uname_M := $(shell uname -m 2>/dev/null || echo not) | 56 | uname_M := $(shell uname -m 2>/dev/null || echo not) |
56 | 57 | ||
@@ -58,7 +59,7 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ | |||
58 | -e s/arm.*/arm/ -e s/sa110/arm/ \ | 59 | -e s/arm.*/arm/ -e s/sa110/arm/ \ |
59 | -e s/s390x/s390/ -e s/parisc64/parisc/ \ | 60 | -e s/s390x/s390/ -e s/parisc64/parisc/ \ |
60 | -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ | 61 | -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \ |
61 | -e s/sh[234].*/sh/ ) | 62 | -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ ) |
62 | NO_PERF_REGS := 1 | 63 | NO_PERF_REGS := 1 |
63 | 64 | ||
64 | CC = $(CROSS_COMPILE)gcc | 65 | CC = $(CROSS_COMPILE)gcc |
@@ -148,30 +149,79 @@ RM = rm -f | |||
148 | MKDIR = mkdir | 149 | MKDIR = mkdir |
149 | FIND = find | 150 | FIND = find |
150 | INSTALL = install | 151 | INSTALL = install |
152 | FLEX = flex | ||
153 | BISON= bison | ||
151 | 154 | ||
152 | # sparse is architecture-neutral, which means that we need to tell it | 155 | # sparse is architecture-neutral, which means that we need to tell it |
153 | # explicitly what architecture to check for. Fix this up for yours.. | 156 | # explicitly what architecture to check for. Fix this up for yours.. |
154 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ | 157 | SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__ |
155 | 158 | ||
159 | ifneq ($(MAKECMDGOALS),clean) | ||
160 | ifneq ($(MAKECMDGOALS),tags) | ||
156 | -include config/feature-tests.mak | 161 | -include config/feature-tests.mak |
157 | 162 | ||
158 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -fstack-protector-all),y) | 163 | ifeq ($(call get-executable,$(FLEX)),) |
164 | dummy := $(error Error: $(FLEX) is missing on this system, please install it) | ||
165 | endif | ||
166 | |||
167 | ifeq ($(call get-executable,$(BISON)),) | ||
168 | dummy := $(error Error: $(BISON) is missing on this system, please install it) | ||
169 | endif | ||
170 | |||
171 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y) | ||
159 | CFLAGS := $(CFLAGS) -fstack-protector-all | 172 | CFLAGS := $(CFLAGS) -fstack-protector-all |
160 | endif | 173 | endif |
161 | 174 | ||
162 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wstack-protector),y) | 175 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wstack-protector,-Wstack-protector),y) |
163 | CFLAGS := $(CFLAGS) -Wstack-protector | 176 | CFLAGS := $(CFLAGS) -Wstack-protector |
164 | endif | 177 | endif |
165 | 178 | ||
166 | ifeq ($(call try-cc,$(SOURCE_HELLO),-Werror -Wvolatile-register-var),y) | 179 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -Wvolatile-register-var,-Wvolatile-register-var),y) |
167 | CFLAGS := $(CFLAGS) -Wvolatile-register-var | 180 | CFLAGS := $(CFLAGS) -Wvolatile-register-var |
168 | endif | 181 | endif |
169 | 182 | ||
170 | ### --- END CONFIGURATION SECTION --- | 183 | ### --- END CONFIGURATION SECTION --- |
171 | 184 | ||
172 | BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)util -I$(TRACE_EVENT_DIR) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | 185 | ifeq ($(srctree),) |
186 | srctree := $(patsubst %/,%,$(dir $(shell pwd))) | ||
187 | srctree := $(patsubst %/,%,$(dir $(srctree))) | ||
188 | #$(info Determined 'srctree' to be $(srctree)) | ||
189 | endif | ||
190 | |||
191 | ifneq ($(objtree),) | ||
192 | #$(info Determined 'objtree' to be $(objtree)) | ||
193 | endif | ||
194 | |||
195 | ifneq ($(OUTPUT),) | ||
196 | #$(info Determined 'OUTPUT' to be $(OUTPUT)) | ||
197 | endif | ||
198 | |||
199 | BASIC_CFLAGS = \ | ||
200 | -Iutil/include \ | ||
201 | -Iarch/$(ARCH)/include \ | ||
202 | $(if $(objtree),-I$(objtree)/arch/$(ARCH)/include/generated/uapi) \ | ||
203 | -I$(srctree)/arch/$(ARCH)/include/uapi \ | ||
204 | -I$(srctree)/arch/$(ARCH)/include \ | ||
205 | $(if $(objtree),-I$(objtree)/include/generated/uapi) \ | ||
206 | -I$(srctree)/include/uapi \ | ||
207 | -I$(srctree)/include \ | ||
208 | -I$(OUTPUT)util \ | ||
209 | -Iutil \ | ||
210 | -I. \ | ||
211 | -I$(TRACE_EVENT_DIR) \ | ||
212 | -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | ||
213 | |||
173 | BASIC_LDFLAGS = | 214 | BASIC_LDFLAGS = |
174 | 215 | ||
216 | ifeq ($(call try-cc,$(SOURCE_BIONIC),$(CFLAGS),bionic),y) | ||
217 | BIONIC := 1 | ||
218 | EXTLIBS := $(filter-out -lrt,$(EXTLIBS)) | ||
219 | EXTLIBS := $(filter-out -lpthread,$(EXTLIBS)) | ||
220 | BASIC_CFLAGS += -I. | ||
221 | endif | ||
222 | endif # MAKECMDGOALS != tags | ||
223 | endif # MAKECMDGOALS != clean | ||
224 | |||
175 | # Guard against environment variables | 225 | # Guard against environment variables |
176 | BUILTIN_OBJS = | 226 | BUILTIN_OBJS = |
177 | LIB_H = | 227 | LIB_H = |
@@ -195,11 +245,19 @@ endif | |||
195 | LIBTRACEEVENT = $(TE_PATH)libtraceevent.a | 245 | LIBTRACEEVENT = $(TE_PATH)libtraceevent.a |
196 | TE_LIB := -L$(TE_PATH) -ltraceevent | 246 | TE_LIB := -L$(TE_PATH) -ltraceevent |
197 | 247 | ||
248 | export LIBTRACEEVENT | ||
249 | |||
250 | # python extension build directories | ||
251 | PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ | ||
252 | PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ | ||
253 | PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/ | ||
254 | export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP | ||
255 | |||
256 | python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so | ||
257 | |||
198 | PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) | 258 | PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) |
199 | PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py | 259 | PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py |
200 | 260 | ||
201 | export LIBTRACEEVENT | ||
202 | |||
203 | $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) | 261 | $(OUTPUT)python/perf.so: $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) |
204 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ | 262 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ |
205 | --quiet build_ext; \ | 263 | --quiet build_ext; \ |
@@ -234,20 +292,17 @@ endif | |||
234 | 292 | ||
235 | export PERL_PATH | 293 | export PERL_PATH |
236 | 294 | ||
237 | FLEX = flex | ||
238 | BISON= bison | ||
239 | |||
240 | $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c | 295 | $(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c |
241 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c | 296 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c |
242 | 297 | ||
243 | $(OUTPUT)util/parse-events-bison.c: util/parse-events.y | 298 | $(OUTPUT)util/parse-events-bison.c: util/parse-events.y |
244 | $(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c | 299 | $(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c -p parse_events_ |
245 | 300 | ||
246 | $(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c | 301 | $(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c |
247 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c | 302 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c |
248 | 303 | ||
249 | $(OUTPUT)util/pmu-bison.c: util/pmu.y | 304 | $(OUTPUT)util/pmu-bison.c: util/pmu.y |
250 | $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c | 305 | $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c -p perf_pmu_ |
251 | 306 | ||
252 | $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c | 307 | $(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c |
253 | $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c | 308 | $(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c |
@@ -303,6 +358,7 @@ LIB_H += util/evlist.h | |||
303 | LIB_H += util/exec_cmd.h | 358 | LIB_H += util/exec_cmd.h |
304 | LIB_H += util/types.h | 359 | LIB_H += util/types.h |
305 | LIB_H += util/levenshtein.h | 360 | LIB_H += util/levenshtein.h |
361 | LIB_H += util/machine.h | ||
306 | LIB_H += util/map.h | 362 | LIB_H += util/map.h |
307 | LIB_H += util/parse-options.h | 363 | LIB_H += util/parse-options.h |
308 | LIB_H += util/parse-events.h | 364 | LIB_H += util/parse-events.h |
@@ -319,6 +375,7 @@ LIB_H += util/svghelper.h | |||
319 | LIB_H += util/tool.h | 375 | LIB_H += util/tool.h |
320 | LIB_H += util/run-command.h | 376 | LIB_H += util/run-command.h |
321 | LIB_H += util/sigchain.h | 377 | LIB_H += util/sigchain.h |
378 | LIB_H += util/dso.h | ||
322 | LIB_H += util/symbol.h | 379 | LIB_H += util/symbol.h |
323 | LIB_H += util/color.h | 380 | LIB_H += util/color.h |
324 | LIB_H += util/values.h | 381 | LIB_H += util/values.h |
@@ -341,8 +398,11 @@ LIB_H += util/rblist.h | |||
341 | LIB_H += util/intlist.h | 398 | LIB_H += util/intlist.h |
342 | LIB_H += util/perf_regs.h | 399 | LIB_H += util/perf_regs.h |
343 | LIB_H += util/unwind.h | 400 | LIB_H += util/unwind.h |
344 | LIB_H += ui/helpline.h | ||
345 | LIB_H += util/vdso.h | 401 | LIB_H += util/vdso.h |
402 | LIB_H += ui/helpline.h | ||
403 | LIB_H += ui/progress.h | ||
404 | LIB_H += ui/util.h | ||
405 | LIB_H += ui/ui.h | ||
346 | 406 | ||
347 | LIB_OBJS += $(OUTPUT)util/abspath.o | 407 | LIB_OBJS += $(OUTPUT)util/abspath.o |
348 | LIB_OBJS += $(OUTPUT)util/alias.o | 408 | LIB_OBJS += $(OUTPUT)util/alias.o |
@@ -362,7 +422,6 @@ LIB_OBJS += $(OUTPUT)util/help.o | |||
362 | LIB_OBJS += $(OUTPUT)util/levenshtein.o | 422 | LIB_OBJS += $(OUTPUT)util/levenshtein.o |
363 | LIB_OBJS += $(OUTPUT)util/parse-options.o | 423 | LIB_OBJS += $(OUTPUT)util/parse-options.o |
364 | LIB_OBJS += $(OUTPUT)util/parse-events.o | 424 | LIB_OBJS += $(OUTPUT)util/parse-events.o |
365 | LIB_OBJS += $(OUTPUT)util/parse-events-test.o | ||
366 | LIB_OBJS += $(OUTPUT)util/path.o | 425 | LIB_OBJS += $(OUTPUT)util/path.o |
367 | LIB_OBJS += $(OUTPUT)util/rbtree.o | 426 | LIB_OBJS += $(OUTPUT)util/rbtree.o |
368 | LIB_OBJS += $(OUTPUT)util/bitmap.o | 427 | LIB_OBJS += $(OUTPUT)util/bitmap.o |
@@ -377,15 +436,16 @@ LIB_OBJS += $(OUTPUT)util/top.o | |||
377 | LIB_OBJS += $(OUTPUT)util/usage.o | 436 | LIB_OBJS += $(OUTPUT)util/usage.o |
378 | LIB_OBJS += $(OUTPUT)util/wrapper.o | 437 | LIB_OBJS += $(OUTPUT)util/wrapper.o |
379 | LIB_OBJS += $(OUTPUT)util/sigchain.o | 438 | LIB_OBJS += $(OUTPUT)util/sigchain.o |
439 | LIB_OBJS += $(OUTPUT)util/dso.o | ||
380 | LIB_OBJS += $(OUTPUT)util/symbol.o | 440 | LIB_OBJS += $(OUTPUT)util/symbol.o |
381 | LIB_OBJS += $(OUTPUT)util/symbol-elf.o | 441 | LIB_OBJS += $(OUTPUT)util/symbol-elf.o |
382 | LIB_OBJS += $(OUTPUT)util/dso-test-data.o | ||
383 | LIB_OBJS += $(OUTPUT)util/color.o | 442 | LIB_OBJS += $(OUTPUT)util/color.o |
384 | LIB_OBJS += $(OUTPUT)util/pager.o | 443 | LIB_OBJS += $(OUTPUT)util/pager.o |
385 | LIB_OBJS += $(OUTPUT)util/header.o | 444 | LIB_OBJS += $(OUTPUT)util/header.o |
386 | LIB_OBJS += $(OUTPUT)util/callchain.o | 445 | LIB_OBJS += $(OUTPUT)util/callchain.o |
387 | LIB_OBJS += $(OUTPUT)util/values.o | 446 | LIB_OBJS += $(OUTPUT)util/values.o |
388 | LIB_OBJS += $(OUTPUT)util/debug.o | 447 | LIB_OBJS += $(OUTPUT)util/debug.o |
448 | LIB_OBJS += $(OUTPUT)util/machine.o | ||
389 | LIB_OBJS += $(OUTPUT)util/map.o | 449 | LIB_OBJS += $(OUTPUT)util/map.o |
390 | LIB_OBJS += $(OUTPUT)util/pstack.o | 450 | LIB_OBJS += $(OUTPUT)util/pstack.o |
391 | LIB_OBJS += $(OUTPUT)util/session.o | 451 | LIB_OBJS += $(OUTPUT)util/session.o |
@@ -413,10 +473,31 @@ LIB_OBJS += $(OUTPUT)util/intlist.o | |||
413 | LIB_OBJS += $(OUTPUT)util/vdso.o | 473 | LIB_OBJS += $(OUTPUT)util/vdso.o |
414 | LIB_OBJS += $(OUTPUT)util/stat.o | 474 | LIB_OBJS += $(OUTPUT)util/stat.o |
415 | 475 | ||
476 | LIB_OBJS += $(OUTPUT)ui/setup.o | ||
416 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 477 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
478 | LIB_OBJS += $(OUTPUT)ui/progress.o | ||
479 | LIB_OBJS += $(OUTPUT)ui/util.o | ||
417 | LIB_OBJS += $(OUTPUT)ui/hist.o | 480 | LIB_OBJS += $(OUTPUT)ui/hist.o |
418 | LIB_OBJS += $(OUTPUT)ui/stdio/hist.o | 481 | LIB_OBJS += $(OUTPUT)ui/stdio/hist.o |
419 | 482 | ||
483 | LIB_OBJS += $(OUTPUT)arch/common.o | ||
484 | |||
485 | LIB_OBJS += $(OUTPUT)tests/parse-events.o | ||
486 | LIB_OBJS += $(OUTPUT)tests/dso-data.o | ||
487 | LIB_OBJS += $(OUTPUT)tests/attr.o | ||
488 | LIB_OBJS += $(OUTPUT)tests/vmlinux-kallsyms.o | ||
489 | LIB_OBJS += $(OUTPUT)tests/open-syscall.o | ||
490 | LIB_OBJS += $(OUTPUT)tests/open-syscall-all-cpus.o | ||
491 | LIB_OBJS += $(OUTPUT)tests/open-syscall-tp-fields.o | ||
492 | LIB_OBJS += $(OUTPUT)tests/mmap-basic.o | ||
493 | LIB_OBJS += $(OUTPUT)tests/perf-record.o | ||
494 | LIB_OBJS += $(OUTPUT)tests/rdpmc.o | ||
495 | LIB_OBJS += $(OUTPUT)tests/evsel-roundtrip-name.o | ||
496 | LIB_OBJS += $(OUTPUT)tests/evsel-tp-sched.o | ||
497 | LIB_OBJS += $(OUTPUT)tests/pmu.o | ||
498 | LIB_OBJS += $(OUTPUT)tests/hists_link.o | ||
499 | LIB_OBJS += $(OUTPUT)tests/python-use.o | ||
500 | |||
420 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 501 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
421 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 502 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
422 | # Benchmark modules | 503 | # Benchmark modules |
@@ -446,46 +527,65 @@ BUILTIN_OBJS += $(OUTPUT)builtin-probe.o | |||
446 | BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o | 527 | BUILTIN_OBJS += $(OUTPUT)builtin-kmem.o |
447 | BUILTIN_OBJS += $(OUTPUT)builtin-lock.o | 528 | BUILTIN_OBJS += $(OUTPUT)builtin-lock.o |
448 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o | 529 | BUILTIN_OBJS += $(OUTPUT)builtin-kvm.o |
449 | BUILTIN_OBJS += $(OUTPUT)builtin-test.o | ||
450 | BUILTIN_OBJS += $(OUTPUT)builtin-inject.o | 530 | BUILTIN_OBJS += $(OUTPUT)builtin-inject.o |
531 | BUILTIN_OBJS += $(OUTPUT)tests/builtin-test.o | ||
451 | 532 | ||
452 | PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT) | 533 | PERFLIBS = $(LIB_FILE) $(LIBTRACEEVENT) |
453 | 534 | ||
454 | # | 535 | # |
455 | # Platform specific tweaks | 536 | # Platform specific tweaks |
456 | # | 537 | # |
538 | ifneq ($(MAKECMDGOALS),clean) | ||
539 | ifneq ($(MAKECMDGOALS),tags) | ||
457 | 540 | ||
458 | # We choose to avoid "if .. else if .. else .. endif endif" | 541 | # We choose to avoid "if .. else if .. else .. endif endif" |
459 | # because maintaining the nesting to match is a pain. If | 542 | # because maintaining the nesting to match is a pain. If |
460 | # we had "elif" things would have been much nicer... | 543 | # we had "elif" things would have been much nicer... |
461 | 544 | ||
462 | -include config.mak.autogen | ||
463 | -include config.mak | ||
464 | |||
465 | ifdef NO_LIBELF | 545 | ifdef NO_LIBELF |
466 | NO_DWARF := 1 | 546 | NO_DWARF := 1 |
467 | NO_DEMANGLE := 1 | 547 | NO_DEMANGLE := 1 |
468 | NO_LIBUNWIND := 1 | 548 | NO_LIBUNWIND := 1 |
469 | else | 549 | else |
470 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) | 550 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) |
471 | ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y) | 551 | ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF),libelf),y) |
472 | FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS) | 552 | FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS) |
473 | ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y) | 553 | ifeq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC),glibc),y) |
474 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | 554 | LIBC_SUPPORT := 1 |
475 | else | 555 | endif |
556 | ifeq ($(BIONIC),1) | ||
557 | LIBC_SUPPORT := 1 | ||
558 | endif | ||
559 | ifeq ($(LIBC_SUPPORT),1) | ||
560 | msg := $(warning No libelf found, disables 'probe' tool, please install elfutils-libelf-devel/libelf-dev); | ||
561 | |||
476 | NO_LIBELF := 1 | 562 | NO_LIBELF := 1 |
477 | NO_DWARF := 1 | 563 | NO_DWARF := 1 |
478 | NO_DEMANGLE := 1 | 564 | NO_DEMANGLE := 1 |
565 | else | ||
566 | msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static); | ||
479 | endif | 567 | endif |
480 | else | 568 | else |
481 | FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS) | 569 | # for linking with debug library, run like: |
482 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y) | 570 | # make DEBUG=1 LIBDW_DIR=/opt/libdw/ |
571 | ifdef LIBDW_DIR | ||
572 | LIBDW_CFLAGS := -I$(LIBDW_DIR)/include | ||
573 | LIBDW_LDFLAGS := -L$(LIBDW_DIR)/lib | ||
574 | endif | ||
575 | |||
576 | FLAGS_DWARF=$(ALL_CFLAGS) $(LIBDW_CFLAGS) -ldw -lelf $(LIBDW_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) | ||
577 | ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF),libdw),y) | ||
483 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); | 578 | msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev); |
484 | NO_DWARF := 1 | 579 | NO_DWARF := 1 |
485 | endif # Dwarf support | 580 | endif # Dwarf support |
486 | endif # SOURCE_LIBELF | 581 | endif # SOURCE_LIBELF |
487 | endif # NO_LIBELF | 582 | endif # NO_LIBELF |
488 | 583 | ||
584 | # There's only x86 (both 32 and 64) support for CFI unwind so far | ||
585 | ifneq ($(ARCH),x86) | ||
586 | NO_LIBUNWIND := 1 | ||
587 | endif | ||
588 | |||
489 | ifndef NO_LIBUNWIND | 589 | ifndef NO_LIBUNWIND |
490 | # for linking with debug library, run like: | 590 | # for linking with debug library, run like: |
491 | # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ | 591 | # make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/ |
@@ -495,7 +595,7 @@ ifdef LIBUNWIND_DIR | |||
495 | endif | 595 | endif |
496 | 596 | ||
497 | FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) | 597 | FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS) |
498 | ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND)),y) | 598 | ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND),libunwind),y) |
499 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); | 599 | msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99); |
500 | NO_LIBUNWIND := 1 | 600 | NO_LIBUNWIND := 1 |
501 | endif # Libunwind support | 601 | endif # Libunwind support |
@@ -524,7 +624,8 @@ LIB_OBJS += $(OUTPUT)util/symbol-minimal.o | |||
524 | else # NO_LIBELF | 624 | else # NO_LIBELF |
525 | BASIC_CFLAGS += -DLIBELF_SUPPORT | 625 | BASIC_CFLAGS += -DLIBELF_SUPPORT |
526 | 626 | ||
527 | ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y) | 627 | FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) |
628 | ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) | ||
528 | BASIC_CFLAGS += -DLIBELF_MMAP | 629 | BASIC_CFLAGS += -DLIBELF_MMAP |
529 | endif | 630 | endif |
530 | 631 | ||
@@ -532,7 +633,8 @@ ifndef NO_DWARF | |||
532 | ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) | 633 | ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined) |
533 | msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); | 634 | msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled); |
534 | else | 635 | else |
535 | BASIC_CFLAGS += -DDWARF_SUPPORT | 636 | BASIC_CFLAGS := -DDWARF_SUPPORT $(LIBDW_CFLAGS) $(BASIC_CFLAGS) |
637 | BASIC_LDFLAGS := $(LIBDW_LDFLAGS) $(BASIC_LDFLAGS) | ||
536 | EXTLIBS += -lelf -ldw | 638 | EXTLIBS += -lelf -ldw |
537 | LIB_OBJS += $(OUTPUT)util/probe-finder.o | 639 | LIB_OBJS += $(OUTPUT)util/probe-finder.o |
538 | LIB_OBJS += $(OUTPUT)util/dwarf-aux.o | 640 | LIB_OBJS += $(OUTPUT)util/dwarf-aux.o |
@@ -550,7 +652,7 @@ endif | |||
550 | 652 | ||
551 | ifndef NO_LIBAUDIT | 653 | ifndef NO_LIBAUDIT |
552 | FLAGS_LIBAUDIT = $(ALL_CFLAGS) $(ALL_LDFLAGS) -laudit | 654 | FLAGS_LIBAUDIT = $(ALL_CFLAGS) $(ALL_LDFLAGS) -laudit |
553 | ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT)),y) | 655 | ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT),libaudit),y) |
554 | msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev); | 656 | msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev); |
555 | else | 657 | else |
556 | BASIC_CFLAGS += -DLIBAUDIT_SUPPORT | 658 | BASIC_CFLAGS += -DLIBAUDIT_SUPPORT |
@@ -561,53 +663,47 @@ endif | |||
561 | 663 | ||
562 | ifndef NO_NEWT | 664 | ifndef NO_NEWT |
563 | FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt | 665 | FLAGS_NEWT=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lnewt |
564 | ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT)),y) | 666 | ifneq ($(call try-cc,$(SOURCE_NEWT),$(FLAGS_NEWT),libnewt),y) |
565 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); | 667 | msg := $(warning newt not found, disables TUI support. Please install newt-devel or libnewt-dev); |
566 | else | 668 | else |
567 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h | 669 | # Fedora has /usr/include/slang/slang.h, but ubuntu /usr/include/slang.h |
568 | BASIC_CFLAGS += -I/usr/include/slang | 670 | BASIC_CFLAGS += -I/usr/include/slang |
569 | BASIC_CFLAGS += -DNEWT_SUPPORT | 671 | BASIC_CFLAGS += -DNEWT_SUPPORT |
570 | EXTLIBS += -lnewt -lslang | 672 | EXTLIBS += -lnewt -lslang |
571 | LIB_OBJS += $(OUTPUT)ui/setup.o | ||
572 | LIB_OBJS += $(OUTPUT)ui/browser.o | 673 | LIB_OBJS += $(OUTPUT)ui/browser.o |
573 | LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o | 674 | LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o |
574 | LIB_OBJS += $(OUTPUT)ui/browsers/hists.o | 675 | LIB_OBJS += $(OUTPUT)ui/browsers/hists.o |
575 | LIB_OBJS += $(OUTPUT)ui/browsers/map.o | 676 | LIB_OBJS += $(OUTPUT)ui/browsers/map.o |
576 | LIB_OBJS += $(OUTPUT)ui/progress.o | 677 | LIB_OBJS += $(OUTPUT)ui/browsers/scripts.o |
577 | LIB_OBJS += $(OUTPUT)ui/util.o | ||
578 | LIB_OBJS += $(OUTPUT)ui/tui/setup.o | 678 | LIB_OBJS += $(OUTPUT)ui/tui/setup.o |
579 | LIB_OBJS += $(OUTPUT)ui/tui/util.o | 679 | LIB_OBJS += $(OUTPUT)ui/tui/util.o |
580 | LIB_OBJS += $(OUTPUT)ui/tui/helpline.o | 680 | LIB_OBJS += $(OUTPUT)ui/tui/helpline.o |
681 | LIB_OBJS += $(OUTPUT)ui/tui/progress.o | ||
581 | LIB_H += ui/browser.h | 682 | LIB_H += ui/browser.h |
582 | LIB_H += ui/browsers/map.h | 683 | LIB_H += ui/browsers/map.h |
583 | LIB_H += ui/keysyms.h | 684 | LIB_H += ui/keysyms.h |
584 | LIB_H += ui/libslang.h | 685 | LIB_H += ui/libslang.h |
585 | LIB_H += ui/progress.h | ||
586 | LIB_H += ui/util.h | ||
587 | LIB_H += ui/ui.h | ||
588 | endif | 686 | endif |
589 | endif | 687 | endif |
590 | 688 | ||
591 | ifndef NO_GTK2 | 689 | ifndef NO_GTK2 |
592 | FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) | 690 | FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null) |
593 | ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y) | 691 | ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2),gtk2),y) |
594 | msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); | 692 | msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); |
595 | else | 693 | else |
596 | ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2)),y) | 694 | ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2),-DHAVE_GTK_INFO_BAR),y) |
597 | BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR | 695 | BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR |
598 | endif | 696 | endif |
599 | BASIC_CFLAGS += -DGTK2_SUPPORT | 697 | BASIC_CFLAGS += -DGTK2_SUPPORT |
600 | BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) | 698 | BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null) |
601 | EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null) | 699 | EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null) |
602 | LIB_OBJS += $(OUTPUT)ui/gtk/browser.o | 700 | LIB_OBJS += $(OUTPUT)ui/gtk/browser.o |
701 | LIB_OBJS += $(OUTPUT)ui/gtk/hists.o | ||
603 | LIB_OBJS += $(OUTPUT)ui/gtk/setup.o | 702 | LIB_OBJS += $(OUTPUT)ui/gtk/setup.o |
604 | LIB_OBJS += $(OUTPUT)ui/gtk/util.o | 703 | LIB_OBJS += $(OUTPUT)ui/gtk/util.o |
605 | LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o | 704 | LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o |
606 | # Make sure that it'd be included only once. | 705 | LIB_OBJS += $(OUTPUT)ui/gtk/progress.o |
607 | ifeq ($(findstring -DNEWT_SUPPORT,$(BASIC_CFLAGS)),) | 706 | LIB_OBJS += $(OUTPUT)ui/gtk/annotate.o |
608 | LIB_OBJS += $(OUTPUT)ui/setup.o | ||
609 | LIB_OBJS += $(OUTPUT)ui/util.o | ||
610 | endif | ||
611 | endif | 707 | endif |
612 | endif | 708 | endif |
613 | 709 | ||
@@ -620,7 +716,7 @@ else | |||
620 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` | 716 | PERL_EMBED_CCOPTS = `perl -MExtUtils::Embed -e ccopts 2>/dev/null` |
621 | FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) | 717 | FLAGS_PERL_EMBED=$(PERL_EMBED_CCOPTS) $(PERL_EMBED_LDOPTS) |
622 | 718 | ||
623 | ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED)),y) | 719 | ifneq ($(call try-cc,$(SOURCE_PERL_EMBED),$(FLAGS_PERL_EMBED),perl),y) |
624 | BASIC_CFLAGS += -DNO_LIBPERL | 720 | BASIC_CFLAGS += -DNO_LIBPERL |
625 | else | 721 | else |
626 | ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS) | 722 | ALL_LDFLAGS += $(PERL_EMBED_LDFLAGS) |
@@ -634,7 +730,7 @@ disable-python = $(eval $(disable-python_code)) | |||
634 | define disable-python_code | 730 | define disable-python_code |
635 | BASIC_CFLAGS += -DNO_LIBPYTHON | 731 | BASIC_CFLAGS += -DNO_LIBPYTHON |
636 | $(if $(1),$(warning No $(1) was found)) | 732 | $(if $(1),$(warning No $(1) was found)) |
637 | $(warning Python support won't be built) | 733 | $(warning Python support will not be built) |
638 | endef | 734 | endef |
639 | 735 | ||
640 | override PYTHON := \ | 736 | override PYTHON := \ |
@@ -642,19 +738,10 @@ override PYTHON := \ | |||
642 | 738 | ||
643 | ifndef PYTHON | 739 | ifndef PYTHON |
644 | $(call disable-python,python interpreter) | 740 | $(call disable-python,python interpreter) |
645 | python-clean := | ||
646 | else | 741 | else |
647 | 742 | ||
648 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) | 743 | PYTHON_WORD := $(call shell-wordify,$(PYTHON)) |
649 | 744 | ||
650 | # python extension build directories | ||
651 | PYTHON_EXTBUILD := $(OUTPUT)python_ext_build/ | ||
652 | PYTHON_EXTBUILD_LIB := $(PYTHON_EXTBUILD)lib/ | ||
653 | PYTHON_EXTBUILD_TMP := $(PYTHON_EXTBUILD)tmp/ | ||
654 | export PYTHON_EXTBUILD_LIB PYTHON_EXTBUILD_TMP | ||
655 | |||
656 | python-clean := rm -rf $(PYTHON_EXTBUILD) $(OUTPUT)python/perf.so | ||
657 | |||
658 | ifdef NO_LIBPYTHON | 745 | ifdef NO_LIBPYTHON |
659 | $(call disable-python) | 746 | $(call disable-python) |
660 | else | 747 | else |
@@ -674,11 +761,11 @@ else | |||
674 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) | 761 | PYTHON_EMBED_CCOPTS := $(shell $(PYTHON_CONFIG_SQ) --cflags 2>/dev/null) |
675 | FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) | 762 | FLAGS_PYTHON_EMBED := $(PYTHON_EMBED_CCOPTS) $(PYTHON_EMBED_LDOPTS) |
676 | 763 | ||
677 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED)),y) | 764 | ifneq ($(call try-cc,$(SOURCE_PYTHON_EMBED),$(FLAGS_PYTHON_EMBED),python),y) |
678 | $(call disable-python,Python.h (for Python 2.x)) | 765 | $(call disable-python,Python.h (for Python 2.x)) |
679 | else | 766 | else |
680 | 767 | ||
681 | ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED)),y) | 768 | ifneq ($(call try-cc,$(SOURCE_PYTHON_VERSION),$(FLAGS_PYTHON_EMBED),python version),y) |
682 | $(warning Python 3 is not yet supported; please set) | 769 | $(warning Python 3 is not yet supported; please set) |
683 | $(warning PYTHON and/or PYTHON_CONFIG appropriately.) | 770 | $(warning PYTHON and/or PYTHON_CONFIG appropriately.) |
684 | $(warning If you also have Python 2 installed, then) | 771 | $(warning If you also have Python 2 installed, then) |
@@ -712,22 +799,22 @@ else | |||
712 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | 799 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE |
713 | else | 800 | else |
714 | FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd | 801 | FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd |
715 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD)) | 802 | has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD),libbfd) |
716 | ifeq ($(has_bfd),y) | 803 | ifeq ($(has_bfd),y) |
717 | EXTLIBS += -lbfd | 804 | EXTLIBS += -lbfd |
718 | else | 805 | else |
719 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty | 806 | FLAGS_BFD_IBERTY=$(FLAGS_BFD) -liberty |
720 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY)) | 807 | has_bfd_iberty := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY),liberty) |
721 | ifeq ($(has_bfd_iberty),y) | 808 | ifeq ($(has_bfd_iberty),y) |
722 | EXTLIBS += -lbfd -liberty | 809 | EXTLIBS += -lbfd -liberty |
723 | else | 810 | else |
724 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz | 811 | FLAGS_BFD_IBERTY_Z=$(FLAGS_BFD_IBERTY) -lz |
725 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z)) | 812 | has_bfd_iberty_z := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD_IBERTY_Z),libz) |
726 | ifeq ($(has_bfd_iberty_z),y) | 813 | ifeq ($(has_bfd_iberty_z),y) |
727 | EXTLIBS += -lbfd -liberty -lz | 814 | EXTLIBS += -lbfd -liberty -lz |
728 | else | 815 | else |
729 | FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty | 816 | FLAGS_CPLUS_DEMANGLE=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -liberty |
730 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE)) | 817 | has_cplus_demangle := $(call try-cc,$(SOURCE_CPLUS_DEMANGLE),$(FLAGS_CPLUS_DEMANGLE),demangle) |
731 | ifeq ($(has_cplus_demangle),y) | 818 | ifeq ($(has_cplus_demangle),y) |
732 | EXTLIBS += -liberty | 819 | EXTLIBS += -liberty |
733 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE | 820 | BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE |
@@ -749,21 +836,41 @@ ifeq ($(NO_PERF_REGS),0) | |||
749 | endif | 836 | endif |
750 | 837 | ||
751 | ifndef NO_STRLCPY | 838 | ifndef NO_STRLCPY |
752 | ifeq ($(call try-cc,$(SOURCE_STRLCPY),),y) | 839 | ifeq ($(call try-cc,$(SOURCE_STRLCPY),,-DHAVE_STRLCPY),y) |
753 | BASIC_CFLAGS += -DHAVE_STRLCPY | 840 | BASIC_CFLAGS += -DHAVE_STRLCPY |
754 | endif | 841 | endif |
755 | endif | 842 | endif |
756 | 843 | ||
844 | ifndef NO_ON_EXIT | ||
845 | ifeq ($(call try-cc,$(SOURCE_ON_EXIT),,-DHAVE_ON_EXIT),y) | ||
846 | BASIC_CFLAGS += -DHAVE_ON_EXIT | ||
847 | endif | ||
848 | endif | ||
849 | |||
757 | ifndef NO_BACKTRACE | 850 | ifndef NO_BACKTRACE |
758 | ifeq ($(call try-cc,$(SOURCE_BACKTRACE),),y) | 851 | ifeq ($(call try-cc,$(SOURCE_BACKTRACE),,-DBACKTRACE_SUPPORT),y) |
759 | BASIC_CFLAGS += -DBACKTRACE_SUPPORT | 852 | BASIC_CFLAGS += -DBACKTRACE_SUPPORT |
760 | endif | 853 | endif |
761 | endif | 854 | endif |
762 | 855 | ||
856 | ifndef NO_LIBNUMA | ||
857 | FLAGS_LIBNUMA = $(ALL_CFLAGS) $(ALL_LDFLAGS) -lnuma | ||
858 | ifneq ($(call try-cc,$(SOURCE_LIBNUMA),$(FLAGS_LIBNUMA),libnuma),y) | ||
859 | msg := $(warning No numa.h found, disables 'perf bench numa mem' benchmark, please install numa-libs-devel or libnuma-dev); | ||
860 | else | ||
861 | BASIC_CFLAGS += -DLIBNUMA_SUPPORT | ||
862 | BUILTIN_OBJS += $(OUTPUT)bench/numa.o | ||
863 | EXTLIBS += -lnuma | ||
864 | endif | ||
865 | endif | ||
866 | |||
763 | ifdef ASCIIDOC8 | 867 | ifdef ASCIIDOC8 |
764 | export ASCIIDOC8 | 868 | export ASCIIDOC8 |
765 | endif | 869 | endif |
766 | 870 | ||
871 | endif # MAKECMDGOALS != tags | ||
872 | endif # MAKECMDGOALS != clean | ||
873 | |||
767 | # Shell quote (do not use $(call) to accommodate ancient setups); | 874 | # Shell quote (do not use $(call) to accommodate ancient setups); |
768 | 875 | ||
769 | ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG)) | 876 | ETC_PERFCONFIG_SQ = $(subst ','\'',$(ETC_PERFCONFIG)) |
@@ -805,7 +912,7 @@ strip: $(PROGRAMS) $(OUTPUT)perf | |||
805 | $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf | 912 | $(STRIP) $(STRIP_OPTS) $(PROGRAMS) $(OUTPUT)perf |
806 | 913 | ||
807 | $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS | 914 | $(OUTPUT)perf.o: perf.c $(OUTPUT)common-cmds.h $(OUTPUT)PERF-CFLAGS |
808 | $(QUIET_CC)$(CC) -DPERF_VERSION='"$(PERF_VERSION)"' \ | 915 | $(QUIET_CC)$(CC) -include $(OUTPUT)PERF-VERSION-FILE \ |
809 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ | 916 | '-DPERF_HTML_PATH="$(htmldir_SQ)"' \ |
810 | $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ | 917 | $(ALL_CFLAGS) -c $(filter %.c,$^) -o $@ |
811 | 918 | ||
@@ -864,10 +971,20 @@ $(OUTPUT)%.s: %.S | |||
864 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS | 971 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS |
865 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 972 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ |
866 | '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ | 973 | '-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \ |
867 | '-DBINDIR="$(bindir_relative_SQ)"' \ | ||
868 | '-DPREFIX="$(prefix_SQ)"' \ | 974 | '-DPREFIX="$(prefix_SQ)"' \ |
869 | $< | 975 | $< |
870 | 976 | ||
977 | $(OUTPUT)tests/attr.o: tests/attr.c $(OUTPUT)PERF-CFLAGS | ||
978 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | ||
979 | '-DBINDIR="$(bindir_SQ)"' -DPYTHON='"$(PYTHON_WORD)"' \ | ||
980 | $< | ||
981 | |||
982 | $(OUTPUT)tests/python-use.o: tests/python-use.c $(OUTPUT)PERF-CFLAGS | ||
983 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | ||
984 | -DPYTHONPATH='"$(OUTPUT)python"' \ | ||
985 | -DPYTHON='"$(PYTHON_WORD)"' \ | ||
986 | $< | ||
987 | |||
871 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS | 988 | $(OUTPUT)util/config.o: util/config.c $(OUTPUT)PERF-CFLAGS |
872 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 989 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
873 | 990 | ||
@@ -883,6 +1000,9 @@ $(OUTPUT)ui/browsers/hists.o: ui/browsers/hists.c $(OUTPUT)PERF-CFLAGS | |||
883 | $(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS | 1000 | $(OUTPUT)ui/browsers/map.o: ui/browsers/map.c $(OUTPUT)PERF-CFLAGS |
884 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | 1001 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< |
885 | 1002 | ||
1003 | $(OUTPUT)ui/browsers/scripts.o: ui/browsers/scripts.c $(OUTPUT)PERF-CFLAGS | ||
1004 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DENABLE_SLFUTURE_CONST $< | ||
1005 | |||
886 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS | 1006 | $(OUTPUT)util/rbtree.o: ../../lib/rbtree.c $(OUTPUT)PERF-CFLAGS |
887 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< | 1007 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Wno-unused-parameter -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $< |
888 | 1008 | ||
@@ -954,20 +1074,15 @@ help: | |||
954 | @echo 'Perf maintainer targets:' | 1074 | @echo 'Perf maintainer targets:' |
955 | @echo ' clean - clean all binary objects and build output' | 1075 | @echo ' clean - clean all binary objects and build output' |
956 | 1076 | ||
957 | doc: | ||
958 | $(MAKE) -C Documentation all | ||
959 | |||
960 | man: | ||
961 | $(MAKE) -C Documentation man | ||
962 | 1077 | ||
963 | html: | 1078 | DOC_TARGETS := doc man html info pdf |
964 | $(MAKE) -C Documentation html | ||
965 | 1079 | ||
966 | info: | 1080 | INSTALL_DOC_TARGETS := $(patsubst %,install-%,$(DOC_TARGETS)) try-install-man |
967 | $(MAKE) -C Documentation info | 1081 | INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html |
968 | 1082 | ||
969 | pdf: | 1083 | # 'make doc' should call 'make -C Documentation all' |
970 | $(MAKE) -C Documentation pdf | 1084 | $(DOC_TARGETS): |
1085 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all) | ||
971 | 1086 | ||
972 | TAGS: | 1087 | TAGS: |
973 | $(RM) TAGS | 1088 | $(RM) TAGS |
@@ -1018,7 +1133,7 @@ perfexec_instdir = $(prefix)/$(perfexecdir) | |||
1018 | endif | 1133 | endif |
1019 | perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) | 1134 | perfexec_instdir_SQ = $(subst ','\'',$(perfexec_instdir)) |
1020 | 1135 | ||
1021 | install: all | 1136 | install-bin: all |
1022 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' | 1137 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' |
1023 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' | 1138 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' |
1024 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 1139 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
@@ -1034,33 +1149,19 @@ install: all | |||
1034 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | 1149 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' |
1035 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' | 1150 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' |
1036 | $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' | 1151 | $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' |
1152 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' | ||
1153 | $(INSTALL) tests/attr.py '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' | ||
1154 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' | ||
1155 | $(INSTALL) tests/attr/* '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/attr' | ||
1156 | |||
1157 | install: install-bin try-install-man | ||
1037 | 1158 | ||
1038 | install-python_ext: | 1159 | install-python_ext: |
1039 | $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' | 1160 | $(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)' |
1040 | 1161 | ||
1041 | install-doc: | 1162 | # 'make install-doc' should call 'make -C Documentation install' |
1042 | $(MAKE) -C Documentation install | 1163 | $(INSTALL_DOC_TARGETS): |
1043 | 1164 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:-doc=) | |
1044 | install-man: | ||
1045 | $(MAKE) -C Documentation install-man | ||
1046 | |||
1047 | install-html: | ||
1048 | $(MAKE) -C Documentation install-html | ||
1049 | |||
1050 | install-info: | ||
1051 | $(MAKE) -C Documentation install-info | ||
1052 | |||
1053 | install-pdf: | ||
1054 | $(MAKE) -C Documentation install-pdf | ||
1055 | |||
1056 | quick-install-doc: | ||
1057 | $(MAKE) -C Documentation quick-install | ||
1058 | |||
1059 | quick-install-man: | ||
1060 | $(MAKE) -C Documentation quick-install-man | ||
1061 | |||
1062 | quick-install-html: | ||
1063 | $(MAKE) -C Documentation quick-install-html | ||
1064 | 1165 | ||
1065 | ### Cleaning rules | 1166 | ### Cleaning rules |
1066 | 1167 | ||
@@ -1068,7 +1169,7 @@ clean: $(LIBTRACEEVENT)-clean | |||
1068 | $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) | 1169 | $(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS) |
1069 | $(RM) $(ALL_PROGRAMS) perf | 1170 | $(RM) $(ALL_PROGRAMS) perf |
1070 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* | 1171 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* |
1071 | $(MAKE) -C Documentation/ clean | 1172 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean |
1072 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS | 1173 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS |
1073 | $(RM) $(OUTPUT)util/*-bison* | 1174 | $(RM) $(OUTPUT)util/*-bison* |
1074 | $(RM) $(OUTPUT)util/*-flex* | 1175 | $(RM) $(OUTPUT)util/*-flex* |
diff --git a/tools/perf/arch/common.c b/tools/perf/arch/common.c new file mode 100644 index 000000000000..aacef07ebf31 --- /dev/null +++ b/tools/perf/arch/common.c | |||
@@ -0,0 +1,212 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <sys/utsname.h> | ||
3 | #include "common.h" | ||
4 | #include "../util/debug.h" | ||
5 | |||
6 | const char *const arm_triplets[] = { | ||
7 | "arm-eabi-", | ||
8 | "arm-linux-androideabi-", | ||
9 | "arm-unknown-linux-", | ||
10 | "arm-unknown-linux-gnu-", | ||
11 | "arm-unknown-linux-gnueabi-", | ||
12 | NULL | ||
13 | }; | ||
14 | |||
15 | const char *const powerpc_triplets[] = { | ||
16 | "powerpc-unknown-linux-gnu-", | ||
17 | "powerpc64-unknown-linux-gnu-", | ||
18 | NULL | ||
19 | }; | ||
20 | |||
21 | const char *const s390_triplets[] = { | ||
22 | "s390-ibm-linux-", | ||
23 | NULL | ||
24 | }; | ||
25 | |||
26 | const char *const sh_triplets[] = { | ||
27 | "sh-unknown-linux-gnu-", | ||
28 | "sh64-unknown-linux-gnu-", | ||
29 | NULL | ||
30 | }; | ||
31 | |||
32 | const char *const sparc_triplets[] = { | ||
33 | "sparc-unknown-linux-gnu-", | ||
34 | "sparc64-unknown-linux-gnu-", | ||
35 | NULL | ||
36 | }; | ||
37 | |||
38 | const char *const x86_triplets[] = { | ||
39 | "x86_64-pc-linux-gnu-", | ||
40 | "x86_64-unknown-linux-gnu-", | ||
41 | "i686-pc-linux-gnu-", | ||
42 | "i586-pc-linux-gnu-", | ||
43 | "i486-pc-linux-gnu-", | ||
44 | "i386-pc-linux-gnu-", | ||
45 | "i686-linux-android-", | ||
46 | "i686-android-linux-", | ||
47 | NULL | ||
48 | }; | ||
49 | |||
50 | const char *const mips_triplets[] = { | ||
51 | "mips-unknown-linux-gnu-", | ||
52 | "mipsel-linux-android-", | ||
53 | NULL | ||
54 | }; | ||
55 | |||
56 | static bool lookup_path(char *name) | ||
57 | { | ||
58 | bool found = false; | ||
59 | char *path, *tmp; | ||
60 | char buf[PATH_MAX]; | ||
61 | char *env = getenv("PATH"); | ||
62 | |||
63 | if (!env) | ||
64 | return false; | ||
65 | |||
66 | env = strdup(env); | ||
67 | if (!env) | ||
68 | return false; | ||
69 | |||
70 | path = strtok_r(env, ":", &tmp); | ||
71 | while (path) { | ||
72 | scnprintf(buf, sizeof(buf), "%s/%s", path, name); | ||
73 | if (access(buf, F_OK) == 0) { | ||
74 | found = true; | ||
75 | break; | ||
76 | } | ||
77 | path = strtok_r(NULL, ":", &tmp); | ||
78 | } | ||
79 | free(env); | ||
80 | return found; | ||
81 | } | ||
82 | |||
83 | static int lookup_triplets(const char *const *triplets, const char *name) | ||
84 | { | ||
85 | int i; | ||
86 | char buf[PATH_MAX]; | ||
87 | |||
88 | for (i = 0; triplets[i] != NULL; i++) { | ||
89 | scnprintf(buf, sizeof(buf), "%s%s", triplets[i], name); | ||
90 | if (lookup_path(buf)) | ||
91 | return i; | ||
92 | } | ||
93 | return -1; | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * Return architecture name in a normalized form. | ||
98 | * The conversion logic comes from the Makefile. | ||
99 | */ | ||
100 | static const char *normalize_arch(char *arch) | ||
101 | { | ||
102 | if (!strcmp(arch, "x86_64")) | ||
103 | return "x86"; | ||
104 | if (arch[0] == 'i' && arch[2] == '8' && arch[3] == '6') | ||
105 | return "x86"; | ||
106 | if (!strcmp(arch, "sun4u") || !strncmp(arch, "sparc", 5)) | ||
107 | return "sparc"; | ||
108 | if (!strncmp(arch, "arm", 3) || !strcmp(arch, "sa110")) | ||
109 | return "arm"; | ||
110 | if (!strncmp(arch, "s390", 4)) | ||
111 | return "s390"; | ||
112 | if (!strncmp(arch, "parisc", 6)) | ||
113 | return "parisc"; | ||
114 | if (!strncmp(arch, "powerpc", 7) || !strncmp(arch, "ppc", 3)) | ||
115 | return "powerpc"; | ||
116 | if (!strncmp(arch, "mips", 4)) | ||
117 | return "mips"; | ||
118 | if (!strncmp(arch, "sh", 2) && isdigit(arch[2])) | ||
119 | return "sh"; | ||
120 | |||
121 | return arch; | ||
122 | } | ||
123 | |||
124 | static int perf_session_env__lookup_binutils_path(struct perf_session_env *env, | ||
125 | const char *name, | ||
126 | const char **path) | ||
127 | { | ||
128 | int idx; | ||
129 | const char *arch, *cross_env; | ||
130 | struct utsname uts; | ||
131 | const char *const *path_list; | ||
132 | char *buf = NULL; | ||
133 | |||
134 | arch = normalize_arch(env->arch); | ||
135 | |||
136 | if (uname(&uts) < 0) | ||
137 | goto out; | ||
138 | |||
139 | /* | ||
140 | * We don't need to try to find objdump path for native system. | ||
141 | * Just use default binutils path (e.g.: "objdump"). | ||
142 | */ | ||
143 | if (!strcmp(normalize_arch(uts.machine), arch)) | ||
144 | goto out; | ||
145 | |||
146 | cross_env = getenv("CROSS_COMPILE"); | ||
147 | if (cross_env) { | ||
148 | if (asprintf(&buf, "%s%s", cross_env, name) < 0) | ||
149 | goto out_error; | ||
150 | if (buf[0] == '/') { | ||
151 | if (access(buf, F_OK) == 0) | ||
152 | goto out; | ||
153 | goto out_error; | ||
154 | } | ||
155 | if (lookup_path(buf)) | ||
156 | goto out; | ||
157 | free(buf); | ||
158 | buf = NULL; | ||
159 | } | ||
160 | |||
161 | if (!strcmp(arch, "arm")) | ||
162 | path_list = arm_triplets; | ||
163 | else if (!strcmp(arch, "powerpc")) | ||
164 | path_list = powerpc_triplets; | ||
165 | else if (!strcmp(arch, "sh")) | ||
166 | path_list = sh_triplets; | ||
167 | else if (!strcmp(arch, "s390")) | ||
168 | path_list = s390_triplets; | ||
169 | else if (!strcmp(arch, "sparc")) | ||
170 | path_list = sparc_triplets; | ||
171 | else if (!strcmp(arch, "x86")) | ||
172 | path_list = x86_triplets; | ||
173 | else if (!strcmp(arch, "mips")) | ||
174 | path_list = mips_triplets; | ||
175 | else { | ||
176 | ui__error("binutils for %s not supported.\n", arch); | ||
177 | goto out_error; | ||
178 | } | ||
179 | |||
180 | idx = lookup_triplets(path_list, name); | ||
181 | if (idx < 0) { | ||
182 | ui__error("Please install %s for %s.\n" | ||
183 | "You can add it to PATH, set CROSS_COMPILE or " | ||
184 | "override the default using --%s.\n", | ||
185 | name, arch, name); | ||
186 | goto out_error; | ||
187 | } | ||
188 | |||
189 | if (asprintf(&buf, "%s%s", path_list[idx], name) < 0) | ||
190 | goto out_error; | ||
191 | |||
192 | out: | ||
193 | *path = buf; | ||
194 | return 0; | ||
195 | out_error: | ||
196 | free(buf); | ||
197 | *path = NULL; | ||
198 | return -1; | ||
199 | } | ||
200 | |||
201 | int perf_session_env__lookup_objdump(struct perf_session_env *env) | ||
202 | { | ||
203 | /* | ||
204 | * For live mode, env->arch will be NULL and we can use | ||
205 | * the native objdump tool. | ||
206 | */ | ||
207 | if (env->arch == NULL) | ||
208 | return 0; | ||
209 | |||
210 | return perf_session_env__lookup_binutils_path(env, "objdump", | ||
211 | &objdump_path); | ||
212 | } | ||
diff --git a/tools/perf/arch/common.h b/tools/perf/arch/common.h new file mode 100644 index 000000000000..ede246eda9be --- /dev/null +++ b/tools/perf/arch/common.h | |||
@@ -0,0 +1,10 @@ | |||
1 | #ifndef ARCH_PERF_COMMON_H | ||
2 | #define ARCH_PERF_COMMON_H | ||
3 | |||
4 | #include "../util/session.h" | ||
5 | |||
6 | extern const char *objdump_path; | ||
7 | |||
8 | int perf_session_env__lookup_objdump(struct perf_session_env *env); | ||
9 | |||
10 | #endif /* ARCH_PERF_COMMON_H */ | ||
diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h index 46fc9f15c6b3..7fcdcdbee917 100644 --- a/tools/perf/arch/x86/include/perf_regs.h +++ b/tools/perf/arch/x86/include/perf_regs.h | |||
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
5 | #include "../../util/types.h" | 5 | #include "../../util/types.h" |
6 | #include "../../../../../arch/x86/include/asm/perf_regs.h" | 6 | #include <asm/perf_regs.h> |
7 | 7 | ||
8 | #ifndef ARCH_X86_64 | 8 | #ifndef ARCH_X86_64 |
9 | #define PERF_REGS_MASK ((1ULL << PERF_REG_X86_32_MAX) - 1) | 9 | #define PERF_REGS_MASK ((1ULL << PERF_REG_X86_32_MAX) - 1) |
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h index 8f89998eeaf4..a5223e6a7b43 100644 --- a/tools/perf/bench/bench.h +++ b/tools/perf/bench/bench.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #ifndef BENCH_H | 1 | #ifndef BENCH_H |
2 | #define BENCH_H | 2 | #define BENCH_H |
3 | 3 | ||
4 | extern int bench_numa(int argc, const char **argv, const char *prefix); | ||
4 | extern int bench_sched_messaging(int argc, const char **argv, const char *prefix); | 5 | extern int bench_sched_messaging(int argc, const char **argv, const char *prefix); |
5 | extern int bench_sched_pipe(int argc, const char **argv, const char *prefix); | 6 | extern int bench_sched_pipe(int argc, const char **argv, const char *prefix); |
6 | extern int bench_mem_memcpy(int argc, const char **argv, | 7 | extern int bench_mem_memcpy(int argc, const char **argv, |
diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c new file mode 100644 index 000000000000..30d1c3225b46 --- /dev/null +++ b/tools/perf/bench/numa.c | |||
@@ -0,0 +1,1731 @@ | |||
1 | /* | ||
2 | * numa.c | ||
3 | * | ||
4 | * numa: Simulate NUMA-sensitive workload and measure their NUMA performance | ||
5 | */ | ||
6 | |||
7 | #include "../perf.h" | ||
8 | #include "../builtin.h" | ||
9 | #include "../util/util.h" | ||
10 | #include "../util/parse-options.h" | ||
11 | |||
12 | #include "bench.h" | ||
13 | |||
14 | #include <errno.h> | ||
15 | #include <sched.h> | ||
16 | #include <stdio.h> | ||
17 | #include <assert.h> | ||
18 | #include <malloc.h> | ||
19 | #include <signal.h> | ||
20 | #include <stdlib.h> | ||
21 | #include <string.h> | ||
22 | #include <unistd.h> | ||
23 | #include <pthread.h> | ||
24 | #include <sys/mman.h> | ||
25 | #include <sys/time.h> | ||
26 | #include <sys/wait.h> | ||
27 | #include <sys/prctl.h> | ||
28 | #include <sys/types.h> | ||
29 | |||
30 | #include <numa.h> | ||
31 | #include <numaif.h> | ||
32 | |||
33 | /* | ||
34 | * Regular printout to the terminal, supressed if -q is specified: | ||
35 | */ | ||
36 | #define tprintf(x...) do { if (g && g->p.show_details >= 0) printf(x); } while (0) | ||
37 | |||
38 | /* | ||
39 | * Debug printf: | ||
40 | */ | ||
41 | #define dprintf(x...) do { if (g && g->p.show_details >= 1) printf(x); } while (0) | ||
42 | |||
43 | struct thread_data { | ||
44 | int curr_cpu; | ||
45 | cpu_set_t bind_cpumask; | ||
46 | int bind_node; | ||
47 | u8 *process_data; | ||
48 | int process_nr; | ||
49 | int thread_nr; | ||
50 | int task_nr; | ||
51 | unsigned int loops_done; | ||
52 | u64 val; | ||
53 | u64 runtime_ns; | ||
54 | pthread_mutex_t *process_lock; | ||
55 | }; | ||
56 | |||
57 | /* Parameters set by options: */ | ||
58 | |||
59 | struct params { | ||
60 | /* Startup synchronization: */ | ||
61 | bool serialize_startup; | ||
62 | |||
63 | /* Task hierarchy: */ | ||
64 | int nr_proc; | ||
65 | int nr_threads; | ||
66 | |||
67 | /* Working set sizes: */ | ||
68 | const char *mb_global_str; | ||
69 | const char *mb_proc_str; | ||
70 | const char *mb_proc_locked_str; | ||
71 | const char *mb_thread_str; | ||
72 | |||
73 | double mb_global; | ||
74 | double mb_proc; | ||
75 | double mb_proc_locked; | ||
76 | double mb_thread; | ||
77 | |||
78 | /* Access patterns to the working set: */ | ||
79 | bool data_reads; | ||
80 | bool data_writes; | ||
81 | bool data_backwards; | ||
82 | bool data_zero_memset; | ||
83 | bool data_rand_walk; | ||
84 | u32 nr_loops; | ||
85 | u32 nr_secs; | ||
86 | u32 sleep_usecs; | ||
87 | |||
88 | /* Working set initialization: */ | ||
89 | bool init_zero; | ||
90 | bool init_random; | ||
91 | bool init_cpu0; | ||
92 | |||
93 | /* Misc options: */ | ||
94 | int show_details; | ||
95 | int run_all; | ||
96 | int thp; | ||
97 | |||
98 | long bytes_global; | ||
99 | long bytes_process; | ||
100 | long bytes_process_locked; | ||
101 | long bytes_thread; | ||
102 | |||
103 | int nr_tasks; | ||
104 | bool show_quiet; | ||
105 | |||
106 | bool show_convergence; | ||
107 | bool measure_convergence; | ||
108 | |||
109 | int perturb_secs; | ||
110 | int nr_cpus; | ||
111 | int nr_nodes; | ||
112 | |||
113 | /* Affinity options -C and -N: */ | ||
114 | char *cpu_list_str; | ||
115 | char *node_list_str; | ||
116 | }; | ||
117 | |||
118 | |||
119 | /* Global, read-writable area, accessible to all processes and threads: */ | ||
120 | |||
121 | struct global_info { | ||
122 | u8 *data; | ||
123 | |||
124 | pthread_mutex_t startup_mutex; | ||
125 | int nr_tasks_started; | ||
126 | |||
127 | pthread_mutex_t startup_done_mutex; | ||
128 | |||
129 | pthread_mutex_t start_work_mutex; | ||
130 | int nr_tasks_working; | ||
131 | |||
132 | pthread_mutex_t stop_work_mutex; | ||
133 | u64 bytes_done; | ||
134 | |||
135 | struct thread_data *threads; | ||
136 | |||
137 | /* Convergence latency measurement: */ | ||
138 | bool all_converged; | ||
139 | bool stop_work; | ||
140 | |||
141 | int print_once; | ||
142 | |||
143 | struct params p; | ||
144 | }; | ||
145 | |||
146 | static struct global_info *g = NULL; | ||
147 | |||
148 | static int parse_cpus_opt(const struct option *opt, const char *arg, int unset); | ||
149 | static int parse_nodes_opt(const struct option *opt, const char *arg, int unset); | ||
150 | |||
151 | struct params p0; | ||
152 | |||
153 | static const struct option options[] = { | ||
154 | OPT_INTEGER('p', "nr_proc" , &p0.nr_proc, "number of processes"), | ||
155 | OPT_INTEGER('t', "nr_threads" , &p0.nr_threads, "number of threads per process"), | ||
156 | |||
157 | OPT_STRING('G', "mb_global" , &p0.mb_global_str, "MB", "global memory (MBs)"), | ||
158 | OPT_STRING('P', "mb_proc" , &p0.mb_proc_str, "MB", "process memory (MBs)"), | ||
159 | OPT_STRING('L', "mb_proc_locked", &p0.mb_proc_locked_str,"MB", "process serialized/locked memory access (MBs), <= process_memory"), | ||
160 | OPT_STRING('T', "mb_thread" , &p0.mb_thread_str, "MB", "thread memory (MBs)"), | ||
161 | |||
162 | OPT_UINTEGER('l', "nr_loops" , &p0.nr_loops, "max number of loops to run"), | ||
163 | OPT_UINTEGER('s', "nr_secs" , &p0.nr_secs, "max number of seconds to run"), | ||
164 | OPT_UINTEGER('u', "usleep" , &p0.sleep_usecs, "usecs to sleep per loop iteration"), | ||
165 | |||
166 | OPT_BOOLEAN('R', "data_reads" , &p0.data_reads, "access the data via writes (can be mixed with -W)"), | ||
167 | OPT_BOOLEAN('W', "data_writes" , &p0.data_writes, "access the data via writes (can be mixed with -R)"), | ||
168 | OPT_BOOLEAN('B', "data_backwards", &p0.data_backwards, "access the data backwards as well"), | ||
169 | OPT_BOOLEAN('Z', "data_zero_memset", &p0.data_zero_memset,"access the data via glibc bzero only"), | ||
170 | OPT_BOOLEAN('r', "data_rand_walk", &p0.data_rand_walk, "access the data with random (32bit LFSR) walk"), | ||
171 | |||
172 | |||
173 | OPT_BOOLEAN('z', "init_zero" , &p0.init_zero, "bzero the initial allocations"), | ||
174 | OPT_BOOLEAN('I', "init_random" , &p0.init_random, "randomize the contents of the initial allocations"), | ||
175 | OPT_BOOLEAN('0', "init_cpu0" , &p0.init_cpu0, "do the initial allocations on CPU#0"), | ||
176 | OPT_INTEGER('x', "perturb_secs", &p0.perturb_secs, "perturb thread 0/0 every X secs, to test convergence stability"), | ||
177 | |||
178 | OPT_INCR ('d', "show_details" , &p0.show_details, "Show details"), | ||
179 | OPT_INCR ('a', "all" , &p0.run_all, "Run all tests in the suite"), | ||
180 | OPT_INTEGER('H', "thp" , &p0.thp, "MADV_NOHUGEPAGE < 0 < MADV_HUGEPAGE"), | ||
181 | OPT_BOOLEAN('c', "show_convergence", &p0.show_convergence, "show convergence details"), | ||
182 | OPT_BOOLEAN('m', "measure_convergence", &p0.measure_convergence, "measure convergence latency"), | ||
183 | OPT_BOOLEAN('q', "quiet" , &p0.show_quiet, "bzero the initial allocations"), | ||
184 | OPT_BOOLEAN('S', "serialize-startup", &p0.serialize_startup,"serialize thread startup"), | ||
185 | |||
186 | /* Special option string parsing callbacks: */ | ||
187 | OPT_CALLBACK('C', "cpus", NULL, "cpu[,cpu2,...cpuN]", | ||
188 | "bind the first N tasks to these specific cpus (the rest is unbound)", | ||
189 | parse_cpus_opt), | ||
190 | OPT_CALLBACK('M', "memnodes", NULL, "node[,node2,...nodeN]", | ||
191 | "bind the first N tasks to these specific memory nodes (the rest is unbound)", | ||
192 | parse_nodes_opt), | ||
193 | OPT_END() | ||
194 | }; | ||
195 | |||
196 | static const char * const bench_numa_usage[] = { | ||
197 | "perf bench numa <options>", | ||
198 | NULL | ||
199 | }; | ||
200 | |||
201 | static const char * const numa_usage[] = { | ||
202 | "perf bench numa mem [<options>]", | ||
203 | NULL | ||
204 | }; | ||
205 | |||
206 | static cpu_set_t bind_to_cpu(int target_cpu) | ||
207 | { | ||
208 | cpu_set_t orig_mask, mask; | ||
209 | int ret; | ||
210 | |||
211 | ret = sched_getaffinity(0, sizeof(orig_mask), &orig_mask); | ||
212 | BUG_ON(ret); | ||
213 | |||
214 | CPU_ZERO(&mask); | ||
215 | |||
216 | if (target_cpu == -1) { | ||
217 | int cpu; | ||
218 | |||
219 | for (cpu = 0; cpu < g->p.nr_cpus; cpu++) | ||
220 | CPU_SET(cpu, &mask); | ||
221 | } else { | ||
222 | BUG_ON(target_cpu < 0 || target_cpu >= g->p.nr_cpus); | ||
223 | CPU_SET(target_cpu, &mask); | ||
224 | } | ||
225 | |||
226 | ret = sched_setaffinity(0, sizeof(mask), &mask); | ||
227 | BUG_ON(ret); | ||
228 | |||
229 | return orig_mask; | ||
230 | } | ||
231 | |||
232 | static cpu_set_t bind_to_node(int target_node) | ||
233 | { | ||
234 | int cpus_per_node = g->p.nr_cpus/g->p.nr_nodes; | ||
235 | cpu_set_t orig_mask, mask; | ||
236 | int cpu; | ||
237 | int ret; | ||
238 | |||
239 | BUG_ON(cpus_per_node*g->p.nr_nodes != g->p.nr_cpus); | ||
240 | BUG_ON(!cpus_per_node); | ||
241 | |||
242 | ret = sched_getaffinity(0, sizeof(orig_mask), &orig_mask); | ||
243 | BUG_ON(ret); | ||
244 | |||
245 | CPU_ZERO(&mask); | ||
246 | |||
247 | if (target_node == -1) { | ||
248 | for (cpu = 0; cpu < g->p.nr_cpus; cpu++) | ||
249 | CPU_SET(cpu, &mask); | ||
250 | } else { | ||
251 | int cpu_start = (target_node + 0) * cpus_per_node; | ||
252 | int cpu_stop = (target_node + 1) * cpus_per_node; | ||
253 | |||
254 | BUG_ON(cpu_stop > g->p.nr_cpus); | ||
255 | |||
256 | for (cpu = cpu_start; cpu < cpu_stop; cpu++) | ||
257 | CPU_SET(cpu, &mask); | ||
258 | } | ||
259 | |||
260 | ret = sched_setaffinity(0, sizeof(mask), &mask); | ||
261 | BUG_ON(ret); | ||
262 | |||
263 | return orig_mask; | ||
264 | } | ||
265 | |||
266 | static void bind_to_cpumask(cpu_set_t mask) | ||
267 | { | ||
268 | int ret; | ||
269 | |||
270 | ret = sched_setaffinity(0, sizeof(mask), &mask); | ||
271 | BUG_ON(ret); | ||
272 | } | ||
273 | |||
274 | static void mempol_restore(void) | ||
275 | { | ||
276 | int ret; | ||
277 | |||
278 | ret = set_mempolicy(MPOL_DEFAULT, NULL, g->p.nr_nodes-1); | ||
279 | |||
280 | BUG_ON(ret); | ||
281 | } | ||
282 | |||
283 | static void bind_to_memnode(int node) | ||
284 | { | ||
285 | unsigned long nodemask; | ||
286 | int ret; | ||
287 | |||
288 | if (node == -1) | ||
289 | return; | ||
290 | |||
291 | BUG_ON(g->p.nr_nodes > (int)sizeof(nodemask)); | ||
292 | nodemask = 1L << node; | ||
293 | |||
294 | ret = set_mempolicy(MPOL_BIND, &nodemask, sizeof(nodemask)*8); | ||
295 | dprintf("binding to node %d, mask: %016lx => %d\n", node, nodemask, ret); | ||
296 | |||
297 | BUG_ON(ret); | ||
298 | } | ||
299 | |||
300 | #define HPSIZE (2*1024*1024) | ||
301 | |||
302 | #define set_taskname(fmt...) \ | ||
303 | do { \ | ||
304 | char name[20]; \ | ||
305 | \ | ||
306 | snprintf(name, 20, fmt); \ | ||
307 | prctl(PR_SET_NAME, name); \ | ||
308 | } while (0) | ||
309 | |||
310 | static u8 *alloc_data(ssize_t bytes0, int map_flags, | ||
311 | int init_zero, int init_cpu0, int thp, int init_random) | ||
312 | { | ||
313 | cpu_set_t orig_mask; | ||
314 | ssize_t bytes; | ||
315 | u8 *buf; | ||
316 | int ret; | ||
317 | |||
318 | if (!bytes0) | ||
319 | return NULL; | ||
320 | |||
321 | /* Allocate and initialize all memory on CPU#0: */ | ||
322 | if (init_cpu0) { | ||
323 | orig_mask = bind_to_node(0); | ||
324 | bind_to_memnode(0); | ||
325 | } | ||
326 | |||
327 | bytes = bytes0 + HPSIZE; | ||
328 | |||
329 | buf = (void *)mmap(0, bytes, PROT_READ|PROT_WRITE, MAP_ANON|map_flags, -1, 0); | ||
330 | BUG_ON(buf == (void *)-1); | ||
331 | |||
332 | if (map_flags == MAP_PRIVATE) { | ||
333 | if (thp > 0) { | ||
334 | ret = madvise(buf, bytes, MADV_HUGEPAGE); | ||
335 | if (ret && !g->print_once) { | ||
336 | g->print_once = 1; | ||
337 | printf("WARNING: Could not enable THP - do: 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled'\n"); | ||
338 | } | ||
339 | } | ||
340 | if (thp < 0) { | ||
341 | ret = madvise(buf, bytes, MADV_NOHUGEPAGE); | ||
342 | if (ret && !g->print_once) { | ||
343 | g->print_once = 1; | ||
344 | printf("WARNING: Could not disable THP: run a CONFIG_TRANSPARENT_HUGEPAGE kernel?\n"); | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | |||
349 | if (init_zero) { | ||
350 | bzero(buf, bytes); | ||
351 | } else { | ||
352 | /* Initialize random contents, different in each word: */ | ||
353 | if (init_random) { | ||
354 | u64 *wbuf = (void *)buf; | ||
355 | long off = rand(); | ||
356 | long i; | ||
357 | |||
358 | for (i = 0; i < bytes/8; i++) | ||
359 | wbuf[i] = i + off; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /* Align to 2MB boundary: */ | ||
364 | buf = (void *)(((unsigned long)buf + HPSIZE-1) & ~(HPSIZE-1)); | ||
365 | |||
366 | /* Restore affinity: */ | ||
367 | if (init_cpu0) { | ||
368 | bind_to_cpumask(orig_mask); | ||
369 | mempol_restore(); | ||
370 | } | ||
371 | |||
372 | return buf; | ||
373 | } | ||
374 | |||
375 | static void free_data(void *data, ssize_t bytes) | ||
376 | { | ||
377 | int ret; | ||
378 | |||
379 | if (!data) | ||
380 | return; | ||
381 | |||
382 | ret = munmap(data, bytes); | ||
383 | BUG_ON(ret); | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * Create a shared memory buffer that can be shared between processes, zeroed: | ||
388 | */ | ||
389 | static void * zalloc_shared_data(ssize_t bytes) | ||
390 | { | ||
391 | return alloc_data(bytes, MAP_SHARED, 1, g->p.init_cpu0, g->p.thp, g->p.init_random); | ||
392 | } | ||
393 | |||
394 | /* | ||
395 | * Create a shared memory buffer that can be shared between processes: | ||
396 | */ | ||
397 | static void * setup_shared_data(ssize_t bytes) | ||
398 | { | ||
399 | return alloc_data(bytes, MAP_SHARED, 0, g->p.init_cpu0, g->p.thp, g->p.init_random); | ||
400 | } | ||
401 | |||
402 | /* | ||
403 | * Allocate process-local memory - this will either be shared between | ||
404 | * threads of this process, or only be accessed by this thread: | ||
405 | */ | ||
406 | static void * setup_private_data(ssize_t bytes) | ||
407 | { | ||
408 | return alloc_data(bytes, MAP_PRIVATE, 0, g->p.init_cpu0, g->p.thp, g->p.init_random); | ||
409 | } | ||
410 | |||
411 | /* | ||
412 | * Return a process-shared (global) mutex: | ||
413 | */ | ||
414 | static void init_global_mutex(pthread_mutex_t *mutex) | ||
415 | { | ||
416 | pthread_mutexattr_t attr; | ||
417 | |||
418 | pthread_mutexattr_init(&attr); | ||
419 | pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); | ||
420 | pthread_mutex_init(mutex, &attr); | ||
421 | } | ||
422 | |||
423 | static int parse_cpu_list(const char *arg) | ||
424 | { | ||
425 | p0.cpu_list_str = strdup(arg); | ||
426 | |||
427 | dprintf("got CPU list: {%s}\n", p0.cpu_list_str); | ||
428 | |||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static void parse_setup_cpu_list(void) | ||
433 | { | ||
434 | struct thread_data *td; | ||
435 | char *str0, *str; | ||
436 | int t; | ||
437 | |||
438 | if (!g->p.cpu_list_str) | ||
439 | return; | ||
440 | |||
441 | dprintf("g->p.nr_tasks: %d\n", g->p.nr_tasks); | ||
442 | |||
443 | str0 = str = strdup(g->p.cpu_list_str); | ||
444 | t = 0; | ||
445 | |||
446 | BUG_ON(!str); | ||
447 | |||
448 | tprintf("# binding tasks to CPUs:\n"); | ||
449 | tprintf("# "); | ||
450 | |||
451 | while (true) { | ||
452 | int bind_cpu, bind_cpu_0, bind_cpu_1; | ||
453 | char *tok, *tok_end, *tok_step, *tok_len, *tok_mul; | ||
454 | int bind_len; | ||
455 | int step; | ||
456 | int mul; | ||
457 | |||
458 | tok = strsep(&str, ","); | ||
459 | if (!tok) | ||
460 | break; | ||
461 | |||
462 | tok_end = strstr(tok, "-"); | ||
463 | |||
464 | dprintf("\ntoken: {%s}, end: {%s}\n", tok, tok_end); | ||
465 | if (!tok_end) { | ||
466 | /* Single CPU specified: */ | ||
467 | bind_cpu_0 = bind_cpu_1 = atol(tok); | ||
468 | } else { | ||
469 | /* CPU range specified (for example: "5-11"): */ | ||
470 | bind_cpu_0 = atol(tok); | ||
471 | bind_cpu_1 = atol(tok_end + 1); | ||
472 | } | ||
473 | |||
474 | step = 1; | ||
475 | tok_step = strstr(tok, "#"); | ||
476 | if (tok_step) { | ||
477 | step = atol(tok_step + 1); | ||
478 | BUG_ON(step <= 0 || step >= g->p.nr_cpus); | ||
479 | } | ||
480 | |||
481 | /* | ||
482 | * Mask length. | ||
483 | * Eg: "--cpus 8_4-16#4" means: '--cpus 8_4,12_4,16_4', | ||
484 | * where the _4 means the next 4 CPUs are allowed. | ||
485 | */ | ||
486 | bind_len = 1; | ||
487 | tok_len = strstr(tok, "_"); | ||
488 | if (tok_len) { | ||
489 | bind_len = atol(tok_len + 1); | ||
490 | BUG_ON(bind_len <= 0 || bind_len > g->p.nr_cpus); | ||
491 | } | ||
492 | |||
493 | /* Multiplicator shortcut, "0x8" is a shortcut for: "0,0,0,0,0,0,0,0" */ | ||
494 | mul = 1; | ||
495 | tok_mul = strstr(tok, "x"); | ||
496 | if (tok_mul) { | ||
497 | mul = atol(tok_mul + 1); | ||
498 | BUG_ON(mul <= 0); | ||
499 | } | ||
500 | |||
501 | dprintf("CPUs: %d_%d-%d#%dx%d\n", bind_cpu_0, bind_len, bind_cpu_1, step, mul); | ||
502 | |||
503 | BUG_ON(bind_cpu_0 < 0 || bind_cpu_0 >= g->p.nr_cpus); | ||
504 | BUG_ON(bind_cpu_1 < 0 || bind_cpu_1 >= g->p.nr_cpus); | ||
505 | BUG_ON(bind_cpu_0 > bind_cpu_1); | ||
506 | |||
507 | for (bind_cpu = bind_cpu_0; bind_cpu <= bind_cpu_1; bind_cpu += step) { | ||
508 | int i; | ||
509 | |||
510 | for (i = 0; i < mul; i++) { | ||
511 | int cpu; | ||
512 | |||
513 | if (t >= g->p.nr_tasks) { | ||
514 | printf("\n# NOTE: ignoring bind CPUs starting at CPU#%d\n #", bind_cpu); | ||
515 | goto out; | ||
516 | } | ||
517 | td = g->threads + t; | ||
518 | |||
519 | if (t) | ||
520 | tprintf(","); | ||
521 | if (bind_len > 1) { | ||
522 | tprintf("%2d/%d", bind_cpu, bind_len); | ||
523 | } else { | ||
524 | tprintf("%2d", bind_cpu); | ||
525 | } | ||
526 | |||
527 | CPU_ZERO(&td->bind_cpumask); | ||
528 | for (cpu = bind_cpu; cpu < bind_cpu+bind_len; cpu++) { | ||
529 | BUG_ON(cpu < 0 || cpu >= g->p.nr_cpus); | ||
530 | CPU_SET(cpu, &td->bind_cpumask); | ||
531 | } | ||
532 | t++; | ||
533 | } | ||
534 | } | ||
535 | } | ||
536 | out: | ||
537 | |||
538 | tprintf("\n"); | ||
539 | |||
540 | if (t < g->p.nr_tasks) | ||
541 | printf("# NOTE: %d tasks bound, %d tasks unbound\n", t, g->p.nr_tasks - t); | ||
542 | |||
543 | free(str0); | ||
544 | } | ||
545 | |||
546 | static int parse_cpus_opt(const struct option *opt __maybe_unused, | ||
547 | const char *arg, int unset __maybe_unused) | ||
548 | { | ||
549 | if (!arg) | ||
550 | return -1; | ||
551 | |||
552 | return parse_cpu_list(arg); | ||
553 | } | ||
554 | |||
555 | static int parse_node_list(const char *arg) | ||
556 | { | ||
557 | p0.node_list_str = strdup(arg); | ||
558 | |||
559 | dprintf("got NODE list: {%s}\n", p0.node_list_str); | ||
560 | |||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | static void parse_setup_node_list(void) | ||
565 | { | ||
566 | struct thread_data *td; | ||
567 | char *str0, *str; | ||
568 | int t; | ||
569 | |||
570 | if (!g->p.node_list_str) | ||
571 | return; | ||
572 | |||
573 | dprintf("g->p.nr_tasks: %d\n", g->p.nr_tasks); | ||
574 | |||
575 | str0 = str = strdup(g->p.node_list_str); | ||
576 | t = 0; | ||
577 | |||
578 | BUG_ON(!str); | ||
579 | |||
580 | tprintf("# binding tasks to NODEs:\n"); | ||
581 | tprintf("# "); | ||
582 | |||
583 | while (true) { | ||
584 | int bind_node, bind_node_0, bind_node_1; | ||
585 | char *tok, *tok_end, *tok_step, *tok_mul; | ||
586 | int step; | ||
587 | int mul; | ||
588 | |||
589 | tok = strsep(&str, ","); | ||
590 | if (!tok) | ||
591 | break; | ||
592 | |||
593 | tok_end = strstr(tok, "-"); | ||
594 | |||
595 | dprintf("\ntoken: {%s}, end: {%s}\n", tok, tok_end); | ||
596 | if (!tok_end) { | ||
597 | /* Single NODE specified: */ | ||
598 | bind_node_0 = bind_node_1 = atol(tok); | ||
599 | } else { | ||
600 | /* NODE range specified (for example: "5-11"): */ | ||
601 | bind_node_0 = atol(tok); | ||
602 | bind_node_1 = atol(tok_end + 1); | ||
603 | } | ||
604 | |||
605 | step = 1; | ||
606 | tok_step = strstr(tok, "#"); | ||
607 | if (tok_step) { | ||
608 | step = atol(tok_step + 1); | ||
609 | BUG_ON(step <= 0 || step >= g->p.nr_nodes); | ||
610 | } | ||
611 | |||
612 | /* Multiplicator shortcut, "0x8" is a shortcut for: "0,0,0,0,0,0,0,0" */ | ||
613 | mul = 1; | ||
614 | tok_mul = strstr(tok, "x"); | ||
615 | if (tok_mul) { | ||
616 | mul = atol(tok_mul + 1); | ||
617 | BUG_ON(mul <= 0); | ||
618 | } | ||
619 | |||
620 | dprintf("NODEs: %d-%d #%d\n", bind_node_0, bind_node_1, step); | ||
621 | |||
622 | BUG_ON(bind_node_0 < 0 || bind_node_0 >= g->p.nr_nodes); | ||
623 | BUG_ON(bind_node_1 < 0 || bind_node_1 >= g->p.nr_nodes); | ||
624 | BUG_ON(bind_node_0 > bind_node_1); | ||
625 | |||
626 | for (bind_node = bind_node_0; bind_node <= bind_node_1; bind_node += step) { | ||
627 | int i; | ||
628 | |||
629 | for (i = 0; i < mul; i++) { | ||
630 | if (t >= g->p.nr_tasks) { | ||
631 | printf("\n# NOTE: ignoring bind NODEs starting at NODE#%d\n", bind_node); | ||
632 | goto out; | ||
633 | } | ||
634 | td = g->threads + t; | ||
635 | |||
636 | if (!t) | ||
637 | tprintf(" %2d", bind_node); | ||
638 | else | ||
639 | tprintf(",%2d", bind_node); | ||
640 | |||
641 | td->bind_node = bind_node; | ||
642 | t++; | ||
643 | } | ||
644 | } | ||
645 | } | ||
646 | out: | ||
647 | |||
648 | tprintf("\n"); | ||
649 | |||
650 | if (t < g->p.nr_tasks) | ||
651 | printf("# NOTE: %d tasks mem-bound, %d tasks unbound\n", t, g->p.nr_tasks - t); | ||
652 | |||
653 | free(str0); | ||
654 | } | ||
655 | |||
656 | static int parse_nodes_opt(const struct option *opt __maybe_unused, | ||
657 | const char *arg, int unset __maybe_unused) | ||
658 | { | ||
659 | if (!arg) | ||
660 | return -1; | ||
661 | |||
662 | return parse_node_list(arg); | ||
663 | |||
664 | return 0; | ||
665 | } | ||
666 | |||
667 | #define BIT(x) (1ul << x) | ||
668 | |||
669 | static inline uint32_t lfsr_32(uint32_t lfsr) | ||
670 | { | ||
671 | const uint32_t taps = BIT(1) | BIT(5) | BIT(6) | BIT(31); | ||
672 | return (lfsr>>1) ^ ((0x0u - (lfsr & 0x1u)) & taps); | ||
673 | } | ||
674 | |||
675 | /* | ||
676 | * Make sure there's real data dependency to RAM (when read | ||
677 | * accesses are enabled), so the compiler, the CPU and the | ||
678 | * kernel (KSM, zero page, etc.) cannot optimize away RAM | ||
679 | * accesses: | ||
680 | */ | ||
681 | static inline u64 access_data(u64 *data __attribute__((unused)), u64 val) | ||
682 | { | ||
683 | if (g->p.data_reads) | ||
684 | val += *data; | ||
685 | if (g->p.data_writes) | ||
686 | *data = val + 1; | ||
687 | return val; | ||
688 | } | ||
689 | |||
690 | /* | ||
691 | * The worker process does two types of work, a forwards going | ||
692 | * loop and a backwards going loop. | ||
693 | * | ||
694 | * We do this so that on multiprocessor systems we do not create | ||
695 | * a 'train' of processing, with highly synchronized processes, | ||
696 | * skewing the whole benchmark. | ||
697 | */ | ||
698 | static u64 do_work(u8 *__data, long bytes, int nr, int nr_max, int loop, u64 val) | ||
699 | { | ||
700 | long words = bytes/sizeof(u64); | ||
701 | u64 *data = (void *)__data; | ||
702 | long chunk_0, chunk_1; | ||
703 | u64 *d0, *d, *d1; | ||
704 | long off; | ||
705 | long i; | ||
706 | |||
707 | BUG_ON(!data && words); | ||
708 | BUG_ON(data && !words); | ||
709 | |||
710 | if (!data) | ||
711 | return val; | ||
712 | |||
713 | /* Very simple memset() work variant: */ | ||
714 | if (g->p.data_zero_memset && !g->p.data_rand_walk) { | ||
715 | bzero(data, bytes); | ||
716 | return val; | ||
717 | } | ||
718 | |||
719 | /* Spread out by PID/TID nr and by loop nr: */ | ||
720 | chunk_0 = words/nr_max; | ||
721 | chunk_1 = words/g->p.nr_loops; | ||
722 | off = nr*chunk_0 + loop*chunk_1; | ||
723 | |||
724 | while (off >= words) | ||
725 | off -= words; | ||
726 | |||
727 | if (g->p.data_rand_walk) { | ||
728 | u32 lfsr = nr + loop + val; | ||
729 | int j; | ||
730 | |||
731 | for (i = 0; i < words/1024; i++) { | ||
732 | long start, end; | ||
733 | |||
734 | lfsr = lfsr_32(lfsr); | ||
735 | |||
736 | start = lfsr % words; | ||
737 | end = min(start + 1024, words-1); | ||
738 | |||
739 | if (g->p.data_zero_memset) { | ||
740 | bzero(data + start, (end-start) * sizeof(u64)); | ||
741 | } else { | ||
742 | for (j = start; j < end; j++) | ||
743 | val = access_data(data + j, val); | ||
744 | } | ||
745 | } | ||
746 | } else if (!g->p.data_backwards || (nr + loop) & 1) { | ||
747 | |||
748 | d0 = data + off; | ||
749 | d = data + off + 1; | ||
750 | d1 = data + words; | ||
751 | |||
752 | /* Process data forwards: */ | ||
753 | for (;;) { | ||
754 | if (unlikely(d >= d1)) | ||
755 | d = data; | ||
756 | if (unlikely(d == d0)) | ||
757 | break; | ||
758 | |||
759 | val = access_data(d, val); | ||
760 | |||
761 | d++; | ||
762 | } | ||
763 | } else { | ||
764 | /* Process data backwards: */ | ||
765 | |||
766 | d0 = data + off; | ||
767 | d = data + off - 1; | ||
768 | d1 = data + words; | ||
769 | |||
770 | /* Process data forwards: */ | ||
771 | for (;;) { | ||
772 | if (unlikely(d < data)) | ||
773 | d = data + words-1; | ||
774 | if (unlikely(d == d0)) | ||
775 | break; | ||
776 | |||
777 | val = access_data(d, val); | ||
778 | |||
779 | d--; | ||
780 | } | ||
781 | } | ||
782 | |||
783 | return val; | ||
784 | } | ||
785 | |||
786 | static void update_curr_cpu(int task_nr, unsigned long bytes_worked) | ||
787 | { | ||
788 | unsigned int cpu; | ||
789 | |||
790 | cpu = sched_getcpu(); | ||
791 | |||
792 | g->threads[task_nr].curr_cpu = cpu; | ||
793 | prctl(0, bytes_worked); | ||
794 | } | ||
795 | |||
796 | #define MAX_NR_NODES 64 | ||
797 | |||
798 | /* | ||
799 | * Count the number of nodes a process's threads | ||
800 | * are spread out on. | ||
801 | * | ||
802 | * A count of 1 means that the process is compressed | ||
803 | * to a single node. A count of g->p.nr_nodes means it's | ||
804 | * spread out on the whole system. | ||
805 | */ | ||
806 | static int count_process_nodes(int process_nr) | ||
807 | { | ||
808 | char node_present[MAX_NR_NODES] = { 0, }; | ||
809 | int nodes; | ||
810 | int n, t; | ||
811 | |||
812 | for (t = 0; t < g->p.nr_threads; t++) { | ||
813 | struct thread_data *td; | ||
814 | int task_nr; | ||
815 | int node; | ||
816 | |||
817 | task_nr = process_nr*g->p.nr_threads + t; | ||
818 | td = g->threads + task_nr; | ||
819 | |||
820 | node = numa_node_of_cpu(td->curr_cpu); | ||
821 | node_present[node] = 1; | ||
822 | } | ||
823 | |||
824 | nodes = 0; | ||
825 | |||
826 | for (n = 0; n < MAX_NR_NODES; n++) | ||
827 | nodes += node_present[n]; | ||
828 | |||
829 | return nodes; | ||
830 | } | ||
831 | |||
832 | /* | ||
833 | * Count the number of distinct process-threads a node contains. | ||
834 | * | ||
835 | * A count of 1 means that the node contains only a single | ||
836 | * process. If all nodes on the system contain at most one | ||
837 | * process then we are well-converged. | ||
838 | */ | ||
839 | static int count_node_processes(int node) | ||
840 | { | ||
841 | int processes = 0; | ||
842 | int t, p; | ||
843 | |||
844 | for (p = 0; p < g->p.nr_proc; p++) { | ||
845 | for (t = 0; t < g->p.nr_threads; t++) { | ||
846 | struct thread_data *td; | ||
847 | int task_nr; | ||
848 | int n; | ||
849 | |||
850 | task_nr = p*g->p.nr_threads + t; | ||
851 | td = g->threads + task_nr; | ||
852 | |||
853 | n = numa_node_of_cpu(td->curr_cpu); | ||
854 | if (n == node) { | ||
855 | processes++; | ||
856 | break; | ||
857 | } | ||
858 | } | ||
859 | } | ||
860 | |||
861 | return processes; | ||
862 | } | ||
863 | |||
864 | static void calc_convergence_compression(int *strong) | ||
865 | { | ||
866 | unsigned int nodes_min, nodes_max; | ||
867 | int p; | ||
868 | |||
869 | nodes_min = -1; | ||
870 | nodes_max = 0; | ||
871 | |||
872 | for (p = 0; p < g->p.nr_proc; p++) { | ||
873 | unsigned int nodes = count_process_nodes(p); | ||
874 | |||
875 | nodes_min = min(nodes, nodes_min); | ||
876 | nodes_max = max(nodes, nodes_max); | ||
877 | } | ||
878 | |||
879 | /* Strong convergence: all threads compress on a single node: */ | ||
880 | if (nodes_min == 1 && nodes_max == 1) { | ||
881 | *strong = 1; | ||
882 | } else { | ||
883 | *strong = 0; | ||
884 | tprintf(" {%d-%d}", nodes_min, nodes_max); | ||
885 | } | ||
886 | } | ||
887 | |||
888 | static void calc_convergence(double runtime_ns_max, double *convergence) | ||
889 | { | ||
890 | unsigned int loops_done_min, loops_done_max; | ||
891 | int process_groups; | ||
892 | int nodes[MAX_NR_NODES]; | ||
893 | int distance; | ||
894 | int nr_min; | ||
895 | int nr_max; | ||
896 | int strong; | ||
897 | int sum; | ||
898 | int nr; | ||
899 | int node; | ||
900 | int cpu; | ||
901 | int t; | ||
902 | |||
903 | if (!g->p.show_convergence && !g->p.measure_convergence) | ||
904 | return; | ||
905 | |||
906 | for (node = 0; node < g->p.nr_nodes; node++) | ||
907 | nodes[node] = 0; | ||
908 | |||
909 | loops_done_min = -1; | ||
910 | loops_done_max = 0; | ||
911 | |||
912 | for (t = 0; t < g->p.nr_tasks; t++) { | ||
913 | struct thread_data *td = g->threads + t; | ||
914 | unsigned int loops_done; | ||
915 | |||
916 | cpu = td->curr_cpu; | ||
917 | |||
918 | /* Not all threads have written it yet: */ | ||
919 | if (cpu < 0) | ||
920 | continue; | ||
921 | |||
922 | node = numa_node_of_cpu(cpu); | ||
923 | |||
924 | nodes[node]++; | ||
925 | |||
926 | loops_done = td->loops_done; | ||
927 | loops_done_min = min(loops_done, loops_done_min); | ||
928 | loops_done_max = max(loops_done, loops_done_max); | ||
929 | } | ||
930 | |||
931 | nr_max = 0; | ||
932 | nr_min = g->p.nr_tasks; | ||
933 | sum = 0; | ||
934 | |||
935 | for (node = 0; node < g->p.nr_nodes; node++) { | ||
936 | nr = nodes[node]; | ||
937 | nr_min = min(nr, nr_min); | ||
938 | nr_max = max(nr, nr_max); | ||
939 | sum += nr; | ||
940 | } | ||
941 | BUG_ON(nr_min > nr_max); | ||
942 | |||
943 | BUG_ON(sum > g->p.nr_tasks); | ||
944 | |||
945 | if (0 && (sum < g->p.nr_tasks)) | ||
946 | return; | ||
947 | |||
948 | /* | ||
949 | * Count the number of distinct process groups present | ||
950 | * on nodes - when we are converged this will decrease | ||
951 | * to g->p.nr_proc: | ||
952 | */ | ||
953 | process_groups = 0; | ||
954 | |||
955 | for (node = 0; node < g->p.nr_nodes; node++) { | ||
956 | int processes = count_node_processes(node); | ||
957 | |||
958 | nr = nodes[node]; | ||
959 | tprintf(" %2d/%-2d", nr, processes); | ||
960 | |||
961 | process_groups += processes; | ||
962 | } | ||
963 | |||
964 | distance = nr_max - nr_min; | ||
965 | |||
966 | tprintf(" [%2d/%-2d]", distance, process_groups); | ||
967 | |||
968 | tprintf(" l:%3d-%-3d (%3d)", | ||
969 | loops_done_min, loops_done_max, loops_done_max-loops_done_min); | ||
970 | |||
971 | if (loops_done_min && loops_done_max) { | ||
972 | double skew = 1.0 - (double)loops_done_min/loops_done_max; | ||
973 | |||
974 | tprintf(" [%4.1f%%]", skew * 100.0); | ||
975 | } | ||
976 | |||
977 | calc_convergence_compression(&strong); | ||
978 | |||
979 | if (strong && process_groups == g->p.nr_proc) { | ||
980 | if (!*convergence) { | ||
981 | *convergence = runtime_ns_max; | ||
982 | tprintf(" (%6.1fs converged)\n", *convergence/1e9); | ||
983 | if (g->p.measure_convergence) { | ||
984 | g->all_converged = true; | ||
985 | g->stop_work = true; | ||
986 | } | ||
987 | } | ||
988 | } else { | ||
989 | if (*convergence) { | ||
990 | tprintf(" (%6.1fs de-converged)", runtime_ns_max/1e9); | ||
991 | *convergence = 0; | ||
992 | } | ||
993 | tprintf("\n"); | ||
994 | } | ||
995 | } | ||
996 | |||
997 | static void show_summary(double runtime_ns_max, int l, double *convergence) | ||
998 | { | ||
999 | tprintf("\r # %5.1f%% [%.1f mins]", | ||
1000 | (double)(l+1)/g->p.nr_loops*100.0, runtime_ns_max/1e9 / 60.0); | ||
1001 | |||
1002 | calc_convergence(runtime_ns_max, convergence); | ||
1003 | |||
1004 | if (g->p.show_details >= 0) | ||
1005 | fflush(stdout); | ||
1006 | } | ||
1007 | |||
1008 | static void *worker_thread(void *__tdata) | ||
1009 | { | ||
1010 | struct thread_data *td = __tdata; | ||
1011 | struct timeval start0, start, stop, diff; | ||
1012 | int process_nr = td->process_nr; | ||
1013 | int thread_nr = td->thread_nr; | ||
1014 | unsigned long last_perturbance; | ||
1015 | int task_nr = td->task_nr; | ||
1016 | int details = g->p.show_details; | ||
1017 | int first_task, last_task; | ||
1018 | double convergence = 0; | ||
1019 | u64 val = td->val; | ||
1020 | double runtime_ns_max; | ||
1021 | u8 *global_data; | ||
1022 | u8 *process_data; | ||
1023 | u8 *thread_data; | ||
1024 | u64 bytes_done; | ||
1025 | long work_done; | ||
1026 | u32 l; | ||
1027 | |||
1028 | bind_to_cpumask(td->bind_cpumask); | ||
1029 | bind_to_memnode(td->bind_node); | ||
1030 | |||
1031 | set_taskname("thread %d/%d", process_nr, thread_nr); | ||
1032 | |||
1033 | global_data = g->data; | ||
1034 | process_data = td->process_data; | ||
1035 | thread_data = setup_private_data(g->p.bytes_thread); | ||
1036 | |||
1037 | bytes_done = 0; | ||
1038 | |||
1039 | last_task = 0; | ||
1040 | if (process_nr == g->p.nr_proc-1 && thread_nr == g->p.nr_threads-1) | ||
1041 | last_task = 1; | ||
1042 | |||
1043 | first_task = 0; | ||
1044 | if (process_nr == 0 && thread_nr == 0) | ||
1045 | first_task = 1; | ||
1046 | |||
1047 | if (details >= 2) { | ||
1048 | printf("# thread %2d / %2d global mem: %p, process mem: %p, thread mem: %p\n", | ||
1049 | process_nr, thread_nr, global_data, process_data, thread_data); | ||
1050 | } | ||
1051 | |||
1052 | if (g->p.serialize_startup) { | ||
1053 | pthread_mutex_lock(&g->startup_mutex); | ||
1054 | g->nr_tasks_started++; | ||
1055 | pthread_mutex_unlock(&g->startup_mutex); | ||
1056 | |||
1057 | /* Here we will wait for the main process to start us all at once: */ | ||
1058 | pthread_mutex_lock(&g->start_work_mutex); | ||
1059 | g->nr_tasks_working++; | ||
1060 | |||
1061 | /* Last one wake the main process: */ | ||
1062 | if (g->nr_tasks_working == g->p.nr_tasks) | ||
1063 | pthread_mutex_unlock(&g->startup_done_mutex); | ||
1064 | |||
1065 | pthread_mutex_unlock(&g->start_work_mutex); | ||
1066 | } | ||
1067 | |||
1068 | gettimeofday(&start0, NULL); | ||
1069 | |||
1070 | start = stop = start0; | ||
1071 | last_perturbance = start.tv_sec; | ||
1072 | |||
1073 | for (l = 0; l < g->p.nr_loops; l++) { | ||
1074 | start = stop; | ||
1075 | |||
1076 | if (g->stop_work) | ||
1077 | break; | ||
1078 | |||
1079 | val += do_work(global_data, g->p.bytes_global, process_nr, g->p.nr_proc, l, val); | ||
1080 | val += do_work(process_data, g->p.bytes_process, thread_nr, g->p.nr_threads, l, val); | ||
1081 | val += do_work(thread_data, g->p.bytes_thread, 0, 1, l, val); | ||
1082 | |||
1083 | if (g->p.sleep_usecs) { | ||
1084 | pthread_mutex_lock(td->process_lock); | ||
1085 | usleep(g->p.sleep_usecs); | ||
1086 | pthread_mutex_unlock(td->process_lock); | ||
1087 | } | ||
1088 | /* | ||
1089 | * Amount of work to be done under a process-global lock: | ||
1090 | */ | ||
1091 | if (g->p.bytes_process_locked) { | ||
1092 | pthread_mutex_lock(td->process_lock); | ||
1093 | val += do_work(process_data, g->p.bytes_process_locked, thread_nr, g->p.nr_threads, l, val); | ||
1094 | pthread_mutex_unlock(td->process_lock); | ||
1095 | } | ||
1096 | |||
1097 | work_done = g->p.bytes_global + g->p.bytes_process + | ||
1098 | g->p.bytes_process_locked + g->p.bytes_thread; | ||
1099 | |||
1100 | update_curr_cpu(task_nr, work_done); | ||
1101 | bytes_done += work_done; | ||
1102 | |||
1103 | if (details < 0 && !g->p.perturb_secs && !g->p.measure_convergence && !g->p.nr_secs) | ||
1104 | continue; | ||
1105 | |||
1106 | td->loops_done = l; | ||
1107 | |||
1108 | gettimeofday(&stop, NULL); | ||
1109 | |||
1110 | /* Check whether our max runtime timed out: */ | ||
1111 | if (g->p.nr_secs) { | ||
1112 | timersub(&stop, &start0, &diff); | ||
1113 | if (diff.tv_sec >= g->p.nr_secs) { | ||
1114 | g->stop_work = true; | ||
1115 | break; | ||
1116 | } | ||
1117 | } | ||
1118 | |||
1119 | /* Update the summary at most once per second: */ | ||
1120 | if (start.tv_sec == stop.tv_sec) | ||
1121 | continue; | ||
1122 | |||
1123 | /* | ||
1124 | * Perturb the first task's equilibrium every g->p.perturb_secs seconds, | ||
1125 | * by migrating to CPU#0: | ||
1126 | */ | ||
1127 | if (first_task && g->p.perturb_secs && (int)(stop.tv_sec - last_perturbance) >= g->p.perturb_secs) { | ||
1128 | cpu_set_t orig_mask; | ||
1129 | int target_cpu; | ||
1130 | int this_cpu; | ||
1131 | |||
1132 | last_perturbance = stop.tv_sec; | ||
1133 | |||
1134 | /* | ||
1135 | * Depending on where we are running, move into | ||
1136 | * the other half of the system, to create some | ||
1137 | * real disturbance: | ||
1138 | */ | ||
1139 | this_cpu = g->threads[task_nr].curr_cpu; | ||
1140 | if (this_cpu < g->p.nr_cpus/2) | ||
1141 | target_cpu = g->p.nr_cpus-1; | ||
1142 | else | ||
1143 | target_cpu = 0; | ||
1144 | |||
1145 | orig_mask = bind_to_cpu(target_cpu); | ||
1146 | |||
1147 | /* Here we are running on the target CPU already */ | ||
1148 | if (details >= 1) | ||
1149 | printf(" (injecting perturbalance, moved to CPU#%d)\n", target_cpu); | ||
1150 | |||
1151 | bind_to_cpumask(orig_mask); | ||
1152 | } | ||
1153 | |||
1154 | if (details >= 3) { | ||
1155 | timersub(&stop, &start, &diff); | ||
1156 | runtime_ns_max = diff.tv_sec * 1000000000; | ||
1157 | runtime_ns_max += diff.tv_usec * 1000; | ||
1158 | |||
1159 | if (details >= 0) { | ||
1160 | printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016lx]\n", | ||
1161 | process_nr, thread_nr, runtime_ns_max / bytes_done, val); | ||
1162 | } | ||
1163 | fflush(stdout); | ||
1164 | } | ||
1165 | if (!last_task) | ||
1166 | continue; | ||
1167 | |||
1168 | timersub(&stop, &start0, &diff); | ||
1169 | runtime_ns_max = diff.tv_sec * 1000000000ULL; | ||
1170 | runtime_ns_max += diff.tv_usec * 1000ULL; | ||
1171 | |||
1172 | show_summary(runtime_ns_max, l, &convergence); | ||
1173 | } | ||
1174 | |||
1175 | gettimeofday(&stop, NULL); | ||
1176 | timersub(&stop, &start0, &diff); | ||
1177 | td->runtime_ns = diff.tv_sec * 1000000000ULL; | ||
1178 | td->runtime_ns += diff.tv_usec * 1000ULL; | ||
1179 | |||
1180 | free_data(thread_data, g->p.bytes_thread); | ||
1181 | |||
1182 | pthread_mutex_lock(&g->stop_work_mutex); | ||
1183 | g->bytes_done += bytes_done; | ||
1184 | pthread_mutex_unlock(&g->stop_work_mutex); | ||
1185 | |||
1186 | return NULL; | ||
1187 | } | ||
1188 | |||
1189 | /* | ||
1190 | * A worker process starts a couple of threads: | ||
1191 | */ | ||
1192 | static void worker_process(int process_nr) | ||
1193 | { | ||
1194 | pthread_mutex_t process_lock; | ||
1195 | struct thread_data *td; | ||
1196 | pthread_t *pthreads; | ||
1197 | u8 *process_data; | ||
1198 | int task_nr; | ||
1199 | int ret; | ||
1200 | int t; | ||
1201 | |||
1202 | pthread_mutex_init(&process_lock, NULL); | ||
1203 | set_taskname("process %d", process_nr); | ||
1204 | |||
1205 | /* | ||
1206 | * Pick up the memory policy and the CPU binding of our first thread, | ||
1207 | * so that we initialize memory accordingly: | ||
1208 | */ | ||
1209 | task_nr = process_nr*g->p.nr_threads; | ||
1210 | td = g->threads + task_nr; | ||
1211 | |||
1212 | bind_to_memnode(td->bind_node); | ||
1213 | bind_to_cpumask(td->bind_cpumask); | ||
1214 | |||
1215 | pthreads = zalloc(g->p.nr_threads * sizeof(pthread_t)); | ||
1216 | process_data = setup_private_data(g->p.bytes_process); | ||
1217 | |||
1218 | if (g->p.show_details >= 3) { | ||
1219 | printf(" # process %2d global mem: %p, process mem: %p\n", | ||
1220 | process_nr, g->data, process_data); | ||
1221 | } | ||
1222 | |||
1223 | for (t = 0; t < g->p.nr_threads; t++) { | ||
1224 | task_nr = process_nr*g->p.nr_threads + t; | ||
1225 | td = g->threads + task_nr; | ||
1226 | |||
1227 | td->process_data = process_data; | ||
1228 | td->process_nr = process_nr; | ||
1229 | td->thread_nr = t; | ||
1230 | td->task_nr = task_nr; | ||
1231 | td->val = rand(); | ||
1232 | td->curr_cpu = -1; | ||
1233 | td->process_lock = &process_lock; | ||
1234 | |||
1235 | ret = pthread_create(pthreads + t, NULL, worker_thread, td); | ||
1236 | BUG_ON(ret); | ||
1237 | } | ||
1238 | |||
1239 | for (t = 0; t < g->p.nr_threads; t++) { | ||
1240 | ret = pthread_join(pthreads[t], NULL); | ||
1241 | BUG_ON(ret); | ||
1242 | } | ||
1243 | |||
1244 | free_data(process_data, g->p.bytes_process); | ||
1245 | free(pthreads); | ||
1246 | } | ||
1247 | |||
1248 | static void print_summary(void) | ||
1249 | { | ||
1250 | if (g->p.show_details < 0) | ||
1251 | return; | ||
1252 | |||
1253 | printf("\n ###\n"); | ||
1254 | printf(" # %d %s will execute (on %d nodes, %d CPUs):\n", | ||
1255 | g->p.nr_tasks, g->p.nr_tasks == 1 ? "task" : "tasks", g->p.nr_nodes, g->p.nr_cpus); | ||
1256 | printf(" # %5dx %5ldMB global shared mem operations\n", | ||
1257 | g->p.nr_loops, g->p.bytes_global/1024/1024); | ||
1258 | printf(" # %5dx %5ldMB process shared mem operations\n", | ||
1259 | g->p.nr_loops, g->p.bytes_process/1024/1024); | ||
1260 | printf(" # %5dx %5ldMB thread local mem operations\n", | ||
1261 | g->p.nr_loops, g->p.bytes_thread/1024/1024); | ||
1262 | |||
1263 | printf(" ###\n"); | ||
1264 | |||
1265 | printf("\n ###\n"); fflush(stdout); | ||
1266 | } | ||
1267 | |||
1268 | static void init_thread_data(void) | ||
1269 | { | ||
1270 | ssize_t size = sizeof(*g->threads)*g->p.nr_tasks; | ||
1271 | int t; | ||
1272 | |||
1273 | g->threads = zalloc_shared_data(size); | ||
1274 | |||
1275 | for (t = 0; t < g->p.nr_tasks; t++) { | ||
1276 | struct thread_data *td = g->threads + t; | ||
1277 | int cpu; | ||
1278 | |||
1279 | /* Allow all nodes by default: */ | ||
1280 | td->bind_node = -1; | ||
1281 | |||
1282 | /* Allow all CPUs by default: */ | ||
1283 | CPU_ZERO(&td->bind_cpumask); | ||
1284 | for (cpu = 0; cpu < g->p.nr_cpus; cpu++) | ||
1285 | CPU_SET(cpu, &td->bind_cpumask); | ||
1286 | } | ||
1287 | } | ||
1288 | |||
1289 | static void deinit_thread_data(void) | ||
1290 | { | ||
1291 | ssize_t size = sizeof(*g->threads)*g->p.nr_tasks; | ||
1292 | |||
1293 | free_data(g->threads, size); | ||
1294 | } | ||
1295 | |||
1296 | static int init(void) | ||
1297 | { | ||
1298 | g = (void *)alloc_data(sizeof(*g), MAP_SHARED, 1, 0, 0 /* THP */, 0); | ||
1299 | |||
1300 | /* Copy over options: */ | ||
1301 | g->p = p0; | ||
1302 | |||
1303 | g->p.nr_cpus = numa_num_configured_cpus(); | ||
1304 | |||
1305 | g->p.nr_nodes = numa_max_node() + 1; | ||
1306 | |||
1307 | /* char array in count_process_nodes(): */ | ||
1308 | BUG_ON(g->p.nr_nodes > MAX_NR_NODES || g->p.nr_nodes < 0); | ||
1309 | |||
1310 | if (g->p.show_quiet && !g->p.show_details) | ||
1311 | g->p.show_details = -1; | ||
1312 | |||
1313 | /* Some memory should be specified: */ | ||
1314 | if (!g->p.mb_global_str && !g->p.mb_proc_str && !g->p.mb_thread_str) | ||
1315 | return -1; | ||
1316 | |||
1317 | if (g->p.mb_global_str) { | ||
1318 | g->p.mb_global = atof(g->p.mb_global_str); | ||
1319 | BUG_ON(g->p.mb_global < 0); | ||
1320 | } | ||
1321 | |||
1322 | if (g->p.mb_proc_str) { | ||
1323 | g->p.mb_proc = atof(g->p.mb_proc_str); | ||
1324 | BUG_ON(g->p.mb_proc < 0); | ||
1325 | } | ||
1326 | |||
1327 | if (g->p.mb_proc_locked_str) { | ||
1328 | g->p.mb_proc_locked = atof(g->p.mb_proc_locked_str); | ||
1329 | BUG_ON(g->p.mb_proc_locked < 0); | ||
1330 | BUG_ON(g->p.mb_proc_locked > g->p.mb_proc); | ||
1331 | } | ||
1332 | |||
1333 | if (g->p.mb_thread_str) { | ||
1334 | g->p.mb_thread = atof(g->p.mb_thread_str); | ||
1335 | BUG_ON(g->p.mb_thread < 0); | ||
1336 | } | ||
1337 | |||
1338 | BUG_ON(g->p.nr_threads <= 0); | ||
1339 | BUG_ON(g->p.nr_proc <= 0); | ||
1340 | |||
1341 | g->p.nr_tasks = g->p.nr_proc*g->p.nr_threads; | ||
1342 | |||
1343 | g->p.bytes_global = g->p.mb_global *1024L*1024L; | ||
1344 | g->p.bytes_process = g->p.mb_proc *1024L*1024L; | ||
1345 | g->p.bytes_process_locked = g->p.mb_proc_locked *1024L*1024L; | ||
1346 | g->p.bytes_thread = g->p.mb_thread *1024L*1024L; | ||
1347 | |||
1348 | g->data = setup_shared_data(g->p.bytes_global); | ||
1349 | |||
1350 | /* Startup serialization: */ | ||
1351 | init_global_mutex(&g->start_work_mutex); | ||
1352 | init_global_mutex(&g->startup_mutex); | ||
1353 | init_global_mutex(&g->startup_done_mutex); | ||
1354 | init_global_mutex(&g->stop_work_mutex); | ||
1355 | |||
1356 | init_thread_data(); | ||
1357 | |||
1358 | tprintf("#\n"); | ||
1359 | parse_setup_cpu_list(); | ||
1360 | parse_setup_node_list(); | ||
1361 | tprintf("#\n"); | ||
1362 | |||
1363 | print_summary(); | ||
1364 | |||
1365 | return 0; | ||
1366 | } | ||
1367 | |||
1368 | static void deinit(void) | ||
1369 | { | ||
1370 | free_data(g->data, g->p.bytes_global); | ||
1371 | g->data = NULL; | ||
1372 | |||
1373 | deinit_thread_data(); | ||
1374 | |||
1375 | free_data(g, sizeof(*g)); | ||
1376 | g = NULL; | ||
1377 | } | ||
1378 | |||
1379 | /* | ||
1380 | * Print a short or long result, depending on the verbosity setting: | ||
1381 | */ | ||
1382 | static void print_res(const char *name, double val, | ||
1383 | const char *txt_unit, const char *txt_short, const char *txt_long) | ||
1384 | { | ||
1385 | if (!name) | ||
1386 | name = "main,"; | ||
1387 | |||
1388 | if (g->p.show_quiet) | ||
1389 | printf(" %-30s %15.3f, %-15s %s\n", name, val, txt_unit, txt_short); | ||
1390 | else | ||
1391 | printf(" %14.3f %s\n", val, txt_long); | ||
1392 | } | ||
1393 | |||
1394 | static int __bench_numa(const char *name) | ||
1395 | { | ||
1396 | struct timeval start, stop, diff; | ||
1397 | u64 runtime_ns_min, runtime_ns_sum; | ||
1398 | pid_t *pids, pid, wpid; | ||
1399 | double delta_runtime; | ||
1400 | double runtime_avg; | ||
1401 | double runtime_sec_max; | ||
1402 | double runtime_sec_min; | ||
1403 | int wait_stat; | ||
1404 | double bytes; | ||
1405 | int i, t; | ||
1406 | |||
1407 | if (init()) | ||
1408 | return -1; | ||
1409 | |||
1410 | pids = zalloc(g->p.nr_proc * sizeof(*pids)); | ||
1411 | pid = -1; | ||
1412 | |||
1413 | /* All threads try to acquire it, this way we can wait for them to start up: */ | ||
1414 | pthread_mutex_lock(&g->start_work_mutex); | ||
1415 | |||
1416 | if (g->p.serialize_startup) { | ||
1417 | tprintf(" #\n"); | ||
1418 | tprintf(" # Startup synchronization: ..."); fflush(stdout); | ||
1419 | } | ||
1420 | |||
1421 | gettimeofday(&start, NULL); | ||
1422 | |||
1423 | for (i = 0; i < g->p.nr_proc; i++) { | ||
1424 | pid = fork(); | ||
1425 | dprintf(" # process %2d: PID %d\n", i, pid); | ||
1426 | |||
1427 | BUG_ON(pid < 0); | ||
1428 | if (!pid) { | ||
1429 | /* Child process: */ | ||
1430 | worker_process(i); | ||
1431 | |||
1432 | exit(0); | ||
1433 | } | ||
1434 | pids[i] = pid; | ||
1435 | |||
1436 | } | ||
1437 | /* Wait for all the threads to start up: */ | ||
1438 | while (g->nr_tasks_started != g->p.nr_tasks) | ||
1439 | usleep(1000); | ||
1440 | |||
1441 | BUG_ON(g->nr_tasks_started != g->p.nr_tasks); | ||
1442 | |||
1443 | if (g->p.serialize_startup) { | ||
1444 | double startup_sec; | ||
1445 | |||
1446 | pthread_mutex_lock(&g->startup_done_mutex); | ||
1447 | |||
1448 | /* This will start all threads: */ | ||
1449 | pthread_mutex_unlock(&g->start_work_mutex); | ||
1450 | |||
1451 | /* This mutex is locked - the last started thread will wake us: */ | ||
1452 | pthread_mutex_lock(&g->startup_done_mutex); | ||
1453 | |||
1454 | gettimeofday(&stop, NULL); | ||
1455 | |||
1456 | timersub(&stop, &start, &diff); | ||
1457 | |||
1458 | startup_sec = diff.tv_sec * 1000000000.0; | ||
1459 | startup_sec += diff.tv_usec * 1000.0; | ||
1460 | startup_sec /= 1e9; | ||
1461 | |||
1462 | tprintf(" threads initialized in %.6f seconds.\n", startup_sec); | ||
1463 | tprintf(" #\n"); | ||
1464 | |||
1465 | start = stop; | ||
1466 | pthread_mutex_unlock(&g->startup_done_mutex); | ||
1467 | } else { | ||
1468 | gettimeofday(&start, NULL); | ||
1469 | } | ||
1470 | |||
1471 | /* Parent process: */ | ||
1472 | |||
1473 | |||
1474 | for (i = 0; i < g->p.nr_proc; i++) { | ||
1475 | wpid = waitpid(pids[i], &wait_stat, 0); | ||
1476 | BUG_ON(wpid < 0); | ||
1477 | BUG_ON(!WIFEXITED(wait_stat)); | ||
1478 | |||
1479 | } | ||
1480 | |||
1481 | runtime_ns_sum = 0; | ||
1482 | runtime_ns_min = -1LL; | ||
1483 | |||
1484 | for (t = 0; t < g->p.nr_tasks; t++) { | ||
1485 | u64 thread_runtime_ns = g->threads[t].runtime_ns; | ||
1486 | |||
1487 | runtime_ns_sum += thread_runtime_ns; | ||
1488 | runtime_ns_min = min(thread_runtime_ns, runtime_ns_min); | ||
1489 | } | ||
1490 | |||
1491 | gettimeofday(&stop, NULL); | ||
1492 | timersub(&stop, &start, &diff); | ||
1493 | |||
1494 | BUG_ON(bench_format != BENCH_FORMAT_DEFAULT); | ||
1495 | |||
1496 | tprintf("\n ###\n"); | ||
1497 | tprintf("\n"); | ||
1498 | |||
1499 | runtime_sec_max = diff.tv_sec * 1000000000.0; | ||
1500 | runtime_sec_max += diff.tv_usec * 1000.0; | ||
1501 | runtime_sec_max /= 1e9; | ||
1502 | |||
1503 | runtime_sec_min = runtime_ns_min/1e9; | ||
1504 | |||
1505 | bytes = g->bytes_done; | ||
1506 | runtime_avg = (double)runtime_ns_sum / g->p.nr_tasks / 1e9; | ||
1507 | |||
1508 | if (g->p.measure_convergence) { | ||
1509 | print_res(name, runtime_sec_max, | ||
1510 | "secs,", "NUMA-convergence-latency", "secs latency to NUMA-converge"); | ||
1511 | } | ||
1512 | |||
1513 | print_res(name, runtime_sec_max, | ||
1514 | "secs,", "runtime-max/thread", "secs slowest (max) thread-runtime"); | ||
1515 | |||
1516 | print_res(name, runtime_sec_min, | ||
1517 | "secs,", "runtime-min/thread", "secs fastest (min) thread-runtime"); | ||
1518 | |||
1519 | print_res(name, runtime_avg, | ||
1520 | "secs,", "runtime-avg/thread", "secs average thread-runtime"); | ||
1521 | |||
1522 | delta_runtime = (runtime_sec_max - runtime_sec_min)/2.0; | ||
1523 | print_res(name, delta_runtime / runtime_sec_max * 100.0, | ||
1524 | "%,", "spread-runtime/thread", "% difference between max/avg runtime"); | ||
1525 | |||
1526 | print_res(name, bytes / g->p.nr_tasks / 1e9, | ||
1527 | "GB,", "data/thread", "GB data processed, per thread"); | ||
1528 | |||
1529 | print_res(name, bytes / 1e9, | ||
1530 | "GB,", "data-total", "GB data processed, total"); | ||
1531 | |||
1532 | print_res(name, runtime_sec_max * 1e9 / (bytes / g->p.nr_tasks), | ||
1533 | "nsecs,", "runtime/byte/thread","nsecs/byte/thread runtime"); | ||
1534 | |||
1535 | print_res(name, bytes / g->p.nr_tasks / 1e9 / runtime_sec_max, | ||
1536 | "GB/sec,", "thread-speed", "GB/sec/thread speed"); | ||
1537 | |||
1538 | print_res(name, bytes / runtime_sec_max / 1e9, | ||
1539 | "GB/sec,", "total-speed", "GB/sec total speed"); | ||
1540 | |||
1541 | free(pids); | ||
1542 | |||
1543 | deinit(); | ||
1544 | |||
1545 | return 0; | ||
1546 | } | ||
1547 | |||
1548 | #define MAX_ARGS 50 | ||
1549 | |||
1550 | static int command_size(const char **argv) | ||
1551 | { | ||
1552 | int size = 0; | ||
1553 | |||
1554 | while (*argv) { | ||
1555 | size++; | ||
1556 | argv++; | ||
1557 | } | ||
1558 | |||
1559 | BUG_ON(size >= MAX_ARGS); | ||
1560 | |||
1561 | return size; | ||
1562 | } | ||
1563 | |||
1564 | static void init_params(struct params *p, const char *name, int argc, const char **argv) | ||
1565 | { | ||
1566 | int i; | ||
1567 | |||
1568 | printf("\n # Running %s \"perf bench numa", name); | ||
1569 | |||
1570 | for (i = 0; i < argc; i++) | ||
1571 | printf(" %s", argv[i]); | ||
1572 | |||
1573 | printf("\"\n"); | ||
1574 | |||
1575 | memset(p, 0, sizeof(*p)); | ||
1576 | |||
1577 | /* Initialize nonzero defaults: */ | ||
1578 | |||
1579 | p->serialize_startup = 1; | ||
1580 | p->data_reads = true; | ||
1581 | p->data_writes = true; | ||
1582 | p->data_backwards = true; | ||
1583 | p->data_rand_walk = true; | ||
1584 | p->nr_loops = -1; | ||
1585 | p->init_random = true; | ||
1586 | } | ||
1587 | |||
1588 | static int run_bench_numa(const char *name, const char **argv) | ||
1589 | { | ||
1590 | int argc = command_size(argv); | ||
1591 | |||
1592 | init_params(&p0, name, argc, argv); | ||
1593 | argc = parse_options(argc, argv, options, bench_numa_usage, 0); | ||
1594 | if (argc) | ||
1595 | goto err; | ||
1596 | |||
1597 | if (__bench_numa(name)) | ||
1598 | goto err; | ||
1599 | |||
1600 | return 0; | ||
1601 | |||
1602 | err: | ||
1603 | usage_with_options(numa_usage, options); | ||
1604 | return -1; | ||
1605 | } | ||
1606 | |||
1607 | #define OPT_BW_RAM "-s", "20", "-zZq", "--thp", " 1", "--no-data_rand_walk" | ||
1608 | #define OPT_BW_RAM_NOTHP OPT_BW_RAM, "--thp", "-1" | ||
1609 | |||
1610 | #define OPT_CONV "-s", "100", "-zZ0qcm", "--thp", " 1" | ||
1611 | #define OPT_CONV_NOTHP OPT_CONV, "--thp", "-1" | ||
1612 | |||
1613 | #define OPT_BW "-s", "20", "-zZ0q", "--thp", " 1" | ||
1614 | #define OPT_BW_NOTHP OPT_BW, "--thp", "-1" | ||
1615 | |||
1616 | /* | ||
1617 | * The built-in test-suite executed by "perf bench numa -a". | ||
1618 | * | ||
1619 | * (A minimum of 4 nodes and 16 GB of RAM is recommended.) | ||
1620 | */ | ||
1621 | static const char *tests[][MAX_ARGS] = { | ||
1622 | /* Basic single-stream NUMA bandwidth measurements: */ | ||
1623 | { "RAM-bw-local,", "mem", "-p", "1", "-t", "1", "-P", "1024", | ||
1624 | "-C" , "0", "-M", "0", OPT_BW_RAM }, | ||
1625 | { "RAM-bw-local-NOTHP,", | ||
1626 | "mem", "-p", "1", "-t", "1", "-P", "1024", | ||
1627 | "-C" , "0", "-M", "0", OPT_BW_RAM_NOTHP }, | ||
1628 | { "RAM-bw-remote,", "mem", "-p", "1", "-t", "1", "-P", "1024", | ||
1629 | "-C" , "0", "-M", "1", OPT_BW_RAM }, | ||
1630 | |||
1631 | /* 2-stream NUMA bandwidth measurements: */ | ||
1632 | { "RAM-bw-local-2x,", "mem", "-p", "2", "-t", "1", "-P", "1024", | ||
1633 | "-C", "0,2", "-M", "0x2", OPT_BW_RAM }, | ||
1634 | { "RAM-bw-remote-2x,", "mem", "-p", "2", "-t", "1", "-P", "1024", | ||
1635 | "-C", "0,2", "-M", "1x2", OPT_BW_RAM }, | ||
1636 | |||
1637 | /* Cross-stream NUMA bandwidth measurement: */ | ||
1638 | { "RAM-bw-cross,", "mem", "-p", "2", "-t", "1", "-P", "1024", | ||
1639 | "-C", "0,8", "-M", "1,0", OPT_BW_RAM }, | ||
1640 | |||
1641 | /* Convergence latency measurements: */ | ||
1642 | { " 1x3-convergence,", "mem", "-p", "1", "-t", "3", "-P", "512", OPT_CONV }, | ||
1643 | { " 1x4-convergence,", "mem", "-p", "1", "-t", "4", "-P", "512", OPT_CONV }, | ||
1644 | { " 1x6-convergence,", "mem", "-p", "1", "-t", "6", "-P", "1020", OPT_CONV }, | ||
1645 | { " 2x3-convergence,", "mem", "-p", "3", "-t", "3", "-P", "1020", OPT_CONV }, | ||
1646 | { " 3x3-convergence,", "mem", "-p", "3", "-t", "3", "-P", "1020", OPT_CONV }, | ||
1647 | { " 4x4-convergence,", "mem", "-p", "4", "-t", "4", "-P", "512", OPT_CONV }, | ||
1648 | { " 4x4-convergence-NOTHP,", | ||
1649 | "mem", "-p", "4", "-t", "4", "-P", "512", OPT_CONV_NOTHP }, | ||
1650 | { " 4x6-convergence,", "mem", "-p", "4", "-t", "6", "-P", "1020", OPT_CONV }, | ||
1651 | { " 4x8-convergence,", "mem", "-p", "4", "-t", "8", "-P", "512", OPT_CONV }, | ||
1652 | { " 8x4-convergence,", "mem", "-p", "8", "-t", "4", "-P", "512", OPT_CONV }, | ||
1653 | { " 8x4-convergence-NOTHP,", | ||
1654 | "mem", "-p", "8", "-t", "4", "-P", "512", OPT_CONV_NOTHP }, | ||
1655 | { " 3x1-convergence,", "mem", "-p", "3", "-t", "1", "-P", "512", OPT_CONV }, | ||
1656 | { " 4x1-convergence,", "mem", "-p", "4", "-t", "1", "-P", "512", OPT_CONV }, | ||
1657 | { " 8x1-convergence,", "mem", "-p", "8", "-t", "1", "-P", "512", OPT_CONV }, | ||
1658 | { "16x1-convergence,", "mem", "-p", "16", "-t", "1", "-P", "256", OPT_CONV }, | ||
1659 | { "32x1-convergence,", "mem", "-p", "32", "-t", "1", "-P", "128", OPT_CONV }, | ||
1660 | |||
1661 | /* Various NUMA process/thread layout bandwidth measurements: */ | ||
1662 | { " 2x1-bw-process,", "mem", "-p", "2", "-t", "1", "-P", "1024", OPT_BW }, | ||
1663 | { " 3x1-bw-process,", "mem", "-p", "3", "-t", "1", "-P", "1024", OPT_BW }, | ||
1664 | { " 4x1-bw-process,", "mem", "-p", "4", "-t", "1", "-P", "1024", OPT_BW }, | ||
1665 | { " 8x1-bw-process,", "mem", "-p", "8", "-t", "1", "-P", " 512", OPT_BW }, | ||
1666 | { " 8x1-bw-process-NOTHP,", | ||
1667 | "mem", "-p", "8", "-t", "1", "-P", " 512", OPT_BW_NOTHP }, | ||
1668 | { "16x1-bw-process,", "mem", "-p", "16", "-t", "1", "-P", "256", OPT_BW }, | ||
1669 | |||
1670 | { " 4x1-bw-thread,", "mem", "-p", "1", "-t", "4", "-T", "256", OPT_BW }, | ||
1671 | { " 8x1-bw-thread,", "mem", "-p", "1", "-t", "8", "-T", "256", OPT_BW }, | ||
1672 | { "16x1-bw-thread,", "mem", "-p", "1", "-t", "16", "-T", "128", OPT_BW }, | ||
1673 | { "32x1-bw-thread,", "mem", "-p", "1", "-t", "32", "-T", "64", OPT_BW }, | ||
1674 | |||
1675 | { " 2x3-bw-thread,", "mem", "-p", "2", "-t", "3", "-P", "512", OPT_BW }, | ||
1676 | { " 4x4-bw-thread,", "mem", "-p", "4", "-t", "4", "-P", "512", OPT_BW }, | ||
1677 | { " 4x6-bw-thread,", "mem", "-p", "4", "-t", "6", "-P", "512", OPT_BW }, | ||
1678 | { " 4x8-bw-thread,", "mem", "-p", "4", "-t", "8", "-P", "512", OPT_BW }, | ||
1679 | { " 4x8-bw-thread-NOTHP,", | ||
1680 | "mem", "-p", "4", "-t", "8", "-P", "512", OPT_BW_NOTHP }, | ||
1681 | { " 3x3-bw-thread,", "mem", "-p", "3", "-t", "3", "-P", "512", OPT_BW }, | ||
1682 | { " 5x5-bw-thread,", "mem", "-p", "5", "-t", "5", "-P", "512", OPT_BW }, | ||
1683 | |||
1684 | { "2x16-bw-thread,", "mem", "-p", "2", "-t", "16", "-P", "512", OPT_BW }, | ||
1685 | { "1x32-bw-thread,", "mem", "-p", "1", "-t", "32", "-P", "2048", OPT_BW }, | ||
1686 | |||
1687 | { "numa02-bw,", "mem", "-p", "1", "-t", "32", "-T", "32", OPT_BW }, | ||
1688 | { "numa02-bw-NOTHP,", "mem", "-p", "1", "-t", "32", "-T", "32", OPT_BW_NOTHP }, | ||
1689 | { "numa01-bw-thread,", "mem", "-p", "2", "-t", "16", "-T", "192", OPT_BW }, | ||
1690 | { "numa01-bw-thread-NOTHP,", | ||
1691 | "mem", "-p", "2", "-t", "16", "-T", "192", OPT_BW_NOTHP }, | ||
1692 | }; | ||
1693 | |||
1694 | static int bench_all(void) | ||
1695 | { | ||
1696 | int nr = ARRAY_SIZE(tests); | ||
1697 | int ret; | ||
1698 | int i; | ||
1699 | |||
1700 | ret = system("echo ' #'; echo ' # Running test on: '$(uname -a); echo ' #'"); | ||
1701 | BUG_ON(ret < 0); | ||
1702 | |||
1703 | for (i = 0; i < nr; i++) { | ||
1704 | if (run_bench_numa(tests[i][0], tests[i] + 1)) | ||
1705 | return -1; | ||
1706 | } | ||
1707 | |||
1708 | printf("\n"); | ||
1709 | |||
1710 | return 0; | ||
1711 | } | ||
1712 | |||
1713 | int bench_numa(int argc, const char **argv, const char *prefix __maybe_unused) | ||
1714 | { | ||
1715 | init_params(&p0, "main,", argc, argv); | ||
1716 | argc = parse_options(argc, argv, options, bench_numa_usage, 0); | ||
1717 | if (argc) | ||
1718 | goto err; | ||
1719 | |||
1720 | if (p0.run_all) | ||
1721 | return bench_all(); | ||
1722 | |||
1723 | if (__bench_numa(NULL)) | ||
1724 | goto err; | ||
1725 | |||
1726 | return 0; | ||
1727 | |||
1728 | err: | ||
1729 | usage_with_options(numa_usage, options); | ||
1730 | return -1; | ||
1731 | } | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 9ea38540b873..2e6961ea3184 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -28,15 +28,16 @@ | |||
28 | #include "util/hist.h" | 28 | #include "util/hist.h" |
29 | #include "util/session.h" | 29 | #include "util/session.h" |
30 | #include "util/tool.h" | 30 | #include "util/tool.h" |
31 | #include "arch/common.h" | ||
31 | 32 | ||
32 | #include <linux/bitmap.h> | 33 | #include <linux/bitmap.h> |
33 | 34 | ||
34 | struct perf_annotate { | 35 | struct perf_annotate { |
35 | struct perf_tool tool; | 36 | struct perf_tool tool; |
36 | char const *input_name; | 37 | bool force, use_tui, use_stdio, use_gtk; |
37 | bool force, use_tui, use_stdio; | ||
38 | bool full_paths; | 38 | bool full_paths; |
39 | bool print_line; | 39 | bool print_line; |
40 | bool skip_missing; | ||
40 | const char *sym_hist_filter; | 41 | const char *sym_hist_filter; |
41 | const char *cpu_list; | 42 | const char *cpu_list; |
42 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 43 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
@@ -138,9 +139,22 @@ find_next: | |||
138 | continue; | 139 | continue; |
139 | } | 140 | } |
140 | 141 | ||
141 | if (use_browser > 0) { | 142 | if (use_browser == 2) { |
142 | key = hist_entry__tui_annotate(he, evidx, NULL, NULL, 0); | 143 | int ret; |
144 | |||
145 | ret = hist_entry__gtk_annotate(he, evidx, NULL); | ||
146 | if (!ret || !ann->skip_missing) | ||
147 | return; | ||
148 | |||
149 | /* skip missing symbols */ | ||
150 | nd = rb_next(nd); | ||
151 | } else if (use_browser == 1) { | ||
152 | key = hist_entry__tui_annotate(he, evidx, NULL); | ||
143 | switch (key) { | 153 | switch (key) { |
154 | case -1: | ||
155 | if (!ann->skip_missing) | ||
156 | return; | ||
157 | /* fall through */ | ||
144 | case K_RIGHT: | 158 | case K_RIGHT: |
145 | next = rb_next(nd); | 159 | next = rb_next(nd); |
146 | break; | 160 | break; |
@@ -174,7 +188,7 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
174 | struct perf_evsel *pos; | 188 | struct perf_evsel *pos; |
175 | u64 total_nr_samples; | 189 | u64 total_nr_samples; |
176 | 190 | ||
177 | session = perf_session__new(ann->input_name, O_RDONLY, | 191 | session = perf_session__new(input_name, O_RDONLY, |
178 | ann->force, false, &ann->tool); | 192 | ann->force, false, &ann->tool); |
179 | if (session == NULL) | 193 | if (session == NULL) |
180 | return -ENOMEM; | 194 | return -ENOMEM; |
@@ -186,6 +200,12 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
186 | goto out_delete; | 200 | goto out_delete; |
187 | } | 201 | } |
188 | 202 | ||
203 | if (!objdump_path) { | ||
204 | ret = perf_session_env__lookup_objdump(&session->header.env); | ||
205 | if (ret) | ||
206 | goto out_delete; | ||
207 | } | ||
208 | |||
189 | ret = perf_session__process_events(session, &ann->tool); | 209 | ret = perf_session__process_events(session, &ann->tool); |
190 | if (ret) | 210 | if (ret) |
191 | goto out_delete; | 211 | goto out_delete; |
@@ -218,6 +238,10 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
218 | ui__error("The %s file has no samples!\n", session->filename); | 238 | ui__error("The %s file has no samples!\n", session->filename); |
219 | goto out_delete; | 239 | goto out_delete; |
220 | } | 240 | } |
241 | |||
242 | if (use_browser == 2) | ||
243 | perf_gtk__show_annotations(); | ||
244 | |||
221 | out_delete: | 245 | out_delete: |
222 | /* | 246 | /* |
223 | * Speed up the exit process, for large files this can | 247 | * Speed up the exit process, for large files this can |
@@ -246,13 +270,14 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
246 | .sample = process_sample_event, | 270 | .sample = process_sample_event, |
247 | .mmap = perf_event__process_mmap, | 271 | .mmap = perf_event__process_mmap, |
248 | .comm = perf_event__process_comm, | 272 | .comm = perf_event__process_comm, |
249 | .fork = perf_event__process_task, | 273 | .exit = perf_event__process_exit, |
274 | .fork = perf_event__process_fork, | ||
250 | .ordered_samples = true, | 275 | .ordered_samples = true, |
251 | .ordering_requires_timestamps = true, | 276 | .ordering_requires_timestamps = true, |
252 | }, | 277 | }, |
253 | }; | 278 | }; |
254 | const struct option options[] = { | 279 | const struct option options[] = { |
255 | OPT_STRING('i', "input", &annotate.input_name, "file", | 280 | OPT_STRING('i', "input", &input_name, "file", |
256 | "input file name"), | 281 | "input file name"), |
257 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 282 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
258 | "only consider symbols in these dsos"), | 283 | "only consider symbols in these dsos"), |
@@ -263,6 +288,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
263 | "be more verbose (show symbol address, etc)"), | 288 | "be more verbose (show symbol address, etc)"), |
264 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 289 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
265 | "dump raw trace in ASCII"), | 290 | "dump raw trace in ASCII"), |
291 | OPT_BOOLEAN(0, "gtk", &annotate.use_gtk, "Use the GTK interface"), | ||
266 | OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"), | 292 | OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"), |
267 | OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"), | 293 | OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"), |
268 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | 294 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
@@ -273,6 +299,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
273 | "print matching source lines (may be slow)"), | 299 | "print matching source lines (may be slow)"), |
274 | OPT_BOOLEAN('P', "full-paths", &annotate.full_paths, | 300 | OPT_BOOLEAN('P', "full-paths", &annotate.full_paths, |
275 | "Don't shorten the displayed pathnames"), | 301 | "Don't shorten the displayed pathnames"), |
302 | OPT_BOOLEAN(0, "skip-missing", &annotate.skip_missing, | ||
303 | "Skip symbols that cannot be annotated"), | ||
276 | OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"), | 304 | OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"), |
277 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 305 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
278 | "Look for files with symbols relative to this directory"), | 306 | "Look for files with symbols relative to this directory"), |
@@ -293,6 +321,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
293 | use_browser = 0; | 321 | use_browser = 0; |
294 | else if (annotate.use_tui) | 322 | else if (annotate.use_tui) |
295 | use_browser = 1; | 323 | use_browser = 1; |
324 | else if (annotate.use_gtk) | ||
325 | use_browser = 2; | ||
296 | 326 | ||
297 | setup_browser(true); | 327 | setup_browser(true); |
298 | 328 | ||
@@ -302,7 +332,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
302 | if (symbol__init() < 0) | 332 | if (symbol__init() < 0) |
303 | return -1; | 333 | return -1; |
304 | 334 | ||
305 | setup_sorting(annotate_usage, options); | 335 | if (setup_sorting() < 0) |
336 | usage_with_options(annotate_usage, options); | ||
306 | 337 | ||
307 | if (argc) { | 338 | if (argc) { |
308 | /* | 339 | /* |
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index cae9a5fd2ecf..77298bf892b8 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c | |||
@@ -35,6 +35,18 @@ struct bench_suite { | |||
35 | /* sentinel: easy for help */ | 35 | /* sentinel: easy for help */ |
36 | #define suite_all { "all", "Test all benchmark suites", NULL } | 36 | #define suite_all { "all", "Test all benchmark suites", NULL } |
37 | 37 | ||
38 | #ifdef LIBNUMA_SUPPORT | ||
39 | static struct bench_suite numa_suites[] = { | ||
40 | { "mem", | ||
41 | "Benchmark for NUMA workloads", | ||
42 | bench_numa }, | ||
43 | suite_all, | ||
44 | { NULL, | ||
45 | NULL, | ||
46 | NULL } | ||
47 | }; | ||
48 | #endif | ||
49 | |||
38 | static struct bench_suite sched_suites[] = { | 50 | static struct bench_suite sched_suites[] = { |
39 | { "messaging", | 51 | { "messaging", |
40 | "Benchmark for scheduler and IPC mechanisms", | 52 | "Benchmark for scheduler and IPC mechanisms", |
@@ -68,6 +80,11 @@ struct bench_subsys { | |||
68 | }; | 80 | }; |
69 | 81 | ||
70 | static struct bench_subsys subsystems[] = { | 82 | static struct bench_subsys subsystems[] = { |
83 | #ifdef LIBNUMA_SUPPORT | ||
84 | { "numa", | ||
85 | "NUMA scheduling and MM behavior", | ||
86 | numa_suites }, | ||
87 | #endif | ||
71 | { "sched", | 88 | { "sched", |
72 | "scheduler and IPC mechanism", | 89 | "scheduler and IPC mechanism", |
73 | sched_suites }, | 90 | sched_suites }, |
@@ -159,6 +176,7 @@ static void all_suite(struct bench_subsys *subsys) /* FROM HERE */ | |||
159 | printf("# Running %s/%s benchmark...\n", | 176 | printf("# Running %s/%s benchmark...\n", |
160 | subsys->name, | 177 | subsys->name, |
161 | suites[i].name); | 178 | suites[i].name); |
179 | fflush(stdout); | ||
162 | 180 | ||
163 | argv[1] = suites[i].name; | 181 | argv[1] = suites[i].name; |
164 | suites[i].fn(1, argv, NULL); | 182 | suites[i].fn(1, argv, NULL); |
@@ -225,6 +243,7 @@ int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused) | |||
225 | printf("# Running %s/%s benchmark...\n", | 243 | printf("# Running %s/%s benchmark...\n", |
226 | subsystems[i].name, | 244 | subsystems[i].name, |
227 | subsystems[i].suites[j].name); | 245 | subsystems[i].suites[j].name); |
246 | fflush(stdout); | ||
228 | status = subsystems[i].suites[j].fn(argc - 1, | 247 | status = subsystems[i].suites[j].fn(argc - 1, |
229 | argv + 1, prefix); | 248 | argv + 1, prefix); |
230 | goto end; | 249 | goto end; |
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index d37e077f4b14..c96c8fa38243 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c | |||
@@ -13,6 +13,8 @@ | |||
13 | #include "util/header.h" | 13 | #include "util/header.h" |
14 | #include "util/parse-options.h" | 14 | #include "util/parse-options.h" |
15 | #include "util/strlist.h" | 15 | #include "util/strlist.h" |
16 | #include "util/build-id.h" | ||
17 | #include "util/session.h" | ||
16 | #include "util/symbol.h" | 18 | #include "util/symbol.h" |
17 | 19 | ||
18 | static int build_id_cache__add_file(const char *filename, const char *debugdir) | 20 | static int build_id_cache__add_file(const char *filename, const char *debugdir) |
@@ -57,19 +59,89 @@ static int build_id_cache__remove_file(const char *filename, | |||
57 | return err; | 59 | return err; |
58 | } | 60 | } |
59 | 61 | ||
62 | static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) | ||
63 | { | ||
64 | char filename[PATH_MAX]; | ||
65 | u8 build_id[BUILD_ID_SIZE]; | ||
66 | |||
67 | if (dso__build_id_filename(dso, filename, sizeof(filename)) && | ||
68 | filename__read_build_id(filename, build_id, | ||
69 | sizeof(build_id)) != sizeof(build_id)) { | ||
70 | if (errno == ENOENT) | ||
71 | return false; | ||
72 | |||
73 | pr_warning("Problems with %s file, consider removing it from the cache\n", | ||
74 | filename); | ||
75 | } else if (memcmp(dso->build_id, build_id, sizeof(dso->build_id))) { | ||
76 | pr_warning("Problems with %s file, consider removing it from the cache\n", | ||
77 | filename); | ||
78 | } | ||
79 | |||
80 | return true; | ||
81 | } | ||
82 | |||
83 | static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp) | ||
84 | { | ||
85 | struct perf_session *session = perf_session__new(filename, O_RDONLY, | ||
86 | force, false, NULL); | ||
87 | if (session == NULL) | ||
88 | return -1; | ||
89 | |||
90 | perf_session__fprintf_dsos_buildid(session, fp, dso__missing_buildid_cache, 0); | ||
91 | perf_session__delete(session); | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static int build_id_cache__update_file(const char *filename, | ||
97 | const char *debugdir) | ||
98 | { | ||
99 | u8 build_id[BUILD_ID_SIZE]; | ||
100 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
101 | |||
102 | int err; | ||
103 | |||
104 | if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) { | ||
105 | pr_debug("Couldn't read a build-id in %s\n", filename); | ||
106 | return -1; | ||
107 | } | ||
108 | |||
109 | build_id__sprintf(build_id, sizeof(build_id), sbuild_id); | ||
110 | err = build_id_cache__remove_s(sbuild_id, debugdir); | ||
111 | if (!err) { | ||
112 | err = build_id_cache__add_s(sbuild_id, debugdir, filename, | ||
113 | false, false); | ||
114 | } | ||
115 | if (verbose) | ||
116 | pr_info("Updating %s %s: %s\n", sbuild_id, filename, | ||
117 | err ? "FAIL" : "Ok"); | ||
118 | |||
119 | return err; | ||
120 | } | ||
121 | |||
60 | int cmd_buildid_cache(int argc, const char **argv, | 122 | int cmd_buildid_cache(int argc, const char **argv, |
61 | const char *prefix __maybe_unused) | 123 | const char *prefix __maybe_unused) |
62 | { | 124 | { |
63 | struct strlist *list; | 125 | struct strlist *list; |
64 | struct str_node *pos; | 126 | struct str_node *pos; |
127 | int ret = 0; | ||
128 | bool force = false; | ||
65 | char debugdir[PATH_MAX]; | 129 | char debugdir[PATH_MAX]; |
66 | char const *add_name_list_str = NULL, | 130 | char const *add_name_list_str = NULL, |
67 | *remove_name_list_str = NULL; | 131 | *remove_name_list_str = NULL, |
132 | *missing_filename = NULL, | ||
133 | *update_name_list_str = NULL; | ||
134 | |||
68 | const struct option buildid_cache_options[] = { | 135 | const struct option buildid_cache_options[] = { |
69 | OPT_STRING('a', "add", &add_name_list_str, | 136 | OPT_STRING('a', "add", &add_name_list_str, |
70 | "file list", "file(s) to add"), | 137 | "file list", "file(s) to add"), |
71 | OPT_STRING('r', "remove", &remove_name_list_str, "file list", | 138 | OPT_STRING('r', "remove", &remove_name_list_str, "file list", |
72 | "file(s) to remove"), | 139 | "file(s) to remove"), |
140 | OPT_STRING('M', "missing", &missing_filename, "file", | ||
141 | "to find missing build ids in the cache"), | ||
142 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | ||
143 | OPT_STRING('u', "update", &update_name_list_str, "file list", | ||
144 | "file(s) to update"), | ||
73 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), | 145 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), |
74 | OPT_END() | 146 | OPT_END() |
75 | }; | 147 | }; |
@@ -124,5 +196,26 @@ int cmd_buildid_cache(int argc, const char **argv, | |||
124 | } | 196 | } |
125 | } | 197 | } |
126 | 198 | ||
127 | return 0; | 199 | if (missing_filename) |
200 | ret = build_id_cache__fprintf_missing(missing_filename, force, stdout); | ||
201 | |||
202 | if (update_name_list_str) { | ||
203 | list = strlist__new(true, update_name_list_str); | ||
204 | if (list) { | ||
205 | strlist__for_each(pos, list) | ||
206 | if (build_id_cache__update_file(pos->s, debugdir)) { | ||
207 | if (errno == ENOENT) { | ||
208 | pr_debug("%s wasn't in the cache\n", | ||
209 | pos->s); | ||
210 | continue; | ||
211 | } | ||
212 | pr_warning("Couldn't update %s: %s\n", | ||
213 | pos->s, strerror(errno)); | ||
214 | } | ||
215 | |||
216 | strlist__delete(list); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | return ret; | ||
128 | } | 221 | } |
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index a0e94fffa03e..e74366a13218 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -44,24 +44,26 @@ static int filename__fprintf_build_id(const char *name, FILE *fp) | |||
44 | return fprintf(fp, "%s\n", sbuild_id); | 44 | return fprintf(fp, "%s\n", sbuild_id); |
45 | } | 45 | } |
46 | 46 | ||
47 | static int perf_session__list_build_ids(const char *input_name, | 47 | static bool dso__skip_buildid(struct dso *dso, int with_hits) |
48 | bool force, bool with_hits) | 48 | { |
49 | return with_hits && !dso->hit; | ||
50 | } | ||
51 | |||
52 | static int perf_session__list_build_ids(bool force, bool with_hits) | ||
49 | { | 53 | { |
50 | struct perf_session *session; | 54 | struct perf_session *session; |
51 | 55 | ||
52 | symbol__elf_init(); | 56 | symbol__elf_init(); |
53 | |||
54 | session = perf_session__new(input_name, O_RDONLY, force, false, | ||
55 | &build_id__mark_dso_hit_ops); | ||
56 | if (session == NULL) | ||
57 | return -1; | ||
58 | |||
59 | /* | 57 | /* |
60 | * See if this is an ELF file first: | 58 | * See if this is an ELF file first: |
61 | */ | 59 | */ |
62 | if (filename__fprintf_build_id(session->filename, stdout)) | 60 | if (filename__fprintf_build_id(input_name, stdout)) |
63 | goto out; | 61 | goto out; |
64 | 62 | ||
63 | session = perf_session__new(input_name, O_RDONLY, force, false, | ||
64 | &build_id__mark_dso_hit_ops); | ||
65 | if (session == NULL) | ||
66 | return -1; | ||
65 | /* | 67 | /* |
66 | * in pipe-mode, the only way to get the buildids is to parse | 68 | * in pipe-mode, the only way to get the buildids is to parse |
67 | * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID | 69 | * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID |
@@ -69,9 +71,9 @@ static int perf_session__list_build_ids(const char *input_name, | |||
69 | if (with_hits || session->fd_pipe) | 71 | if (with_hits || session->fd_pipe) |
70 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); | 72 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); |
71 | 73 | ||
72 | perf_session__fprintf_dsos_buildid(session, stdout, with_hits); | 74 | perf_session__fprintf_dsos_buildid(session, stdout, dso__skip_buildid, with_hits); |
73 | out: | ||
74 | perf_session__delete(session); | 75 | perf_session__delete(session); |
76 | out: | ||
75 | return 0; | 77 | return 0; |
76 | } | 78 | } |
77 | 79 | ||
@@ -81,7 +83,6 @@ int cmd_buildid_list(int argc, const char **argv, | |||
81 | bool show_kernel = false; | 83 | bool show_kernel = false; |
82 | bool with_hits = false; | 84 | bool with_hits = false; |
83 | bool force = false; | 85 | bool force = false; |
84 | const char *input_name = NULL; | ||
85 | const struct option options[] = { | 86 | const struct option options[] = { |
86 | OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"), | 87 | OPT_BOOLEAN('H', "with-hits", &with_hits, "Show only DSOs with hits"), |
87 | OPT_STRING('i', "input", &input_name, "file", "input file name"), | 88 | OPT_STRING('i', "input", &input_name, "file", "input file name"), |
@@ -101,5 +102,5 @@ int cmd_buildid_list(int argc, const char **argv, | |||
101 | if (show_kernel) | 102 | if (show_kernel) |
102 | return sysfs__fprintf_build_id(stdout); | 103 | return sysfs__fprintf_build_id(stdout); |
103 | 104 | ||
104 | return perf_session__list_build_ids(input_name, force, with_hits); | 105 | return perf_session__list_build_ids(force, with_hits); |
105 | } | 106 | } |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index a0b531c14b97..d207a97a2db1 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -23,7 +23,212 @@ static char const *input_old = "perf.data.old", | |||
23 | *input_new = "perf.data"; | 23 | *input_new = "perf.data"; |
24 | static char diff__default_sort_order[] = "dso,symbol"; | 24 | static char diff__default_sort_order[] = "dso,symbol"; |
25 | static bool force; | 25 | static bool force; |
26 | static bool show_displacement; | 26 | static bool show_period; |
27 | static bool show_formula; | ||
28 | static bool show_baseline_only; | ||
29 | static bool sort_compute; | ||
30 | |||
31 | static s64 compute_wdiff_w1; | ||
32 | static s64 compute_wdiff_w2; | ||
33 | |||
34 | enum { | ||
35 | COMPUTE_DELTA, | ||
36 | COMPUTE_RATIO, | ||
37 | COMPUTE_WEIGHTED_DIFF, | ||
38 | COMPUTE_MAX, | ||
39 | }; | ||
40 | |||
41 | const char *compute_names[COMPUTE_MAX] = { | ||
42 | [COMPUTE_DELTA] = "delta", | ||
43 | [COMPUTE_RATIO] = "ratio", | ||
44 | [COMPUTE_WEIGHTED_DIFF] = "wdiff", | ||
45 | }; | ||
46 | |||
47 | static int compute; | ||
48 | |||
49 | static int setup_compute_opt_wdiff(char *opt) | ||
50 | { | ||
51 | char *w1_str = opt; | ||
52 | char *w2_str; | ||
53 | |||
54 | int ret = -EINVAL; | ||
55 | |||
56 | if (!opt) | ||
57 | goto out; | ||
58 | |||
59 | w2_str = strchr(opt, ','); | ||
60 | if (!w2_str) | ||
61 | goto out; | ||
62 | |||
63 | *w2_str++ = 0x0; | ||
64 | if (!*w2_str) | ||
65 | goto out; | ||
66 | |||
67 | compute_wdiff_w1 = strtol(w1_str, NULL, 10); | ||
68 | compute_wdiff_w2 = strtol(w2_str, NULL, 10); | ||
69 | |||
70 | if (!compute_wdiff_w1 || !compute_wdiff_w2) | ||
71 | goto out; | ||
72 | |||
73 | pr_debug("compute wdiff w1(%" PRId64 ") w2(%" PRId64 ")\n", | ||
74 | compute_wdiff_w1, compute_wdiff_w2); | ||
75 | |||
76 | ret = 0; | ||
77 | |||
78 | out: | ||
79 | if (ret) | ||
80 | pr_err("Failed: wrong weight data, use 'wdiff:w1,w2'\n"); | ||
81 | |||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | static int setup_compute_opt(char *opt) | ||
86 | { | ||
87 | if (compute == COMPUTE_WEIGHTED_DIFF) | ||
88 | return setup_compute_opt_wdiff(opt); | ||
89 | |||
90 | if (opt) { | ||
91 | pr_err("Failed: extra option specified '%s'", opt); | ||
92 | return -EINVAL; | ||
93 | } | ||
94 | |||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static int setup_compute(const struct option *opt, const char *str, | ||
99 | int unset __maybe_unused) | ||
100 | { | ||
101 | int *cp = (int *) opt->value; | ||
102 | char *cstr = (char *) str; | ||
103 | char buf[50]; | ||
104 | unsigned i; | ||
105 | char *option; | ||
106 | |||
107 | if (!str) { | ||
108 | *cp = COMPUTE_DELTA; | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | if (*str == '+') { | ||
113 | sort_compute = true; | ||
114 | cstr = (char *) ++str; | ||
115 | if (!*str) | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | option = strchr(str, ':'); | ||
120 | if (option) { | ||
121 | unsigned len = option++ - str; | ||
122 | |||
123 | /* | ||
124 | * The str data are not writeable, so we need | ||
125 | * to use another buffer. | ||
126 | */ | ||
127 | |||
128 | /* No option value is longer. */ | ||
129 | if (len >= sizeof(buf)) | ||
130 | return -EINVAL; | ||
131 | |||
132 | strncpy(buf, str, len); | ||
133 | buf[len] = 0x0; | ||
134 | cstr = buf; | ||
135 | } | ||
136 | |||
137 | for (i = 0; i < COMPUTE_MAX; i++) | ||
138 | if (!strcmp(cstr, compute_names[i])) { | ||
139 | *cp = i; | ||
140 | return setup_compute_opt(option); | ||
141 | } | ||
142 | |||
143 | pr_err("Failed: '%s' is not computation method " | ||
144 | "(use 'delta','ratio' or 'wdiff')\n", str); | ||
145 | return -EINVAL; | ||
146 | } | ||
147 | |||
148 | double perf_diff__period_percent(struct hist_entry *he, u64 period) | ||
149 | { | ||
150 | u64 total = he->hists->stats.total_period; | ||
151 | return (period * 100.0) / total; | ||
152 | } | ||
153 | |||
154 | double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair) | ||
155 | { | ||
156 | double new_percent = perf_diff__period_percent(he, he->stat.period); | ||
157 | double old_percent = perf_diff__period_percent(pair, pair->stat.period); | ||
158 | |||
159 | he->diff.period_ratio_delta = new_percent - old_percent; | ||
160 | he->diff.computed = true; | ||
161 | return he->diff.period_ratio_delta; | ||
162 | } | ||
163 | |||
164 | double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair) | ||
165 | { | ||
166 | double new_period = he->stat.period; | ||
167 | double old_period = pair->stat.period; | ||
168 | |||
169 | he->diff.computed = true; | ||
170 | he->diff.period_ratio = new_period / old_period; | ||
171 | return he->diff.period_ratio; | ||
172 | } | ||
173 | |||
174 | s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair) | ||
175 | { | ||
176 | u64 new_period = he->stat.period; | ||
177 | u64 old_period = pair->stat.period; | ||
178 | |||
179 | he->diff.computed = true; | ||
180 | he->diff.wdiff = new_period * compute_wdiff_w2 - | ||
181 | old_period * compute_wdiff_w1; | ||
182 | |||
183 | return he->diff.wdiff; | ||
184 | } | ||
185 | |||
186 | static int formula_delta(struct hist_entry *he, struct hist_entry *pair, | ||
187 | char *buf, size_t size) | ||
188 | { | ||
189 | return scnprintf(buf, size, | ||
190 | "(%" PRIu64 " * 100 / %" PRIu64 ") - " | ||
191 | "(%" PRIu64 " * 100 / %" PRIu64 ")", | ||
192 | he->stat.period, he->hists->stats.total_period, | ||
193 | pair->stat.period, pair->hists->stats.total_period); | ||
194 | } | ||
195 | |||
196 | static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, | ||
197 | char *buf, size_t size) | ||
198 | { | ||
199 | double new_period = he->stat.period; | ||
200 | double old_period = pair->stat.period; | ||
201 | |||
202 | return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); | ||
203 | } | ||
204 | |||
205 | static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair, | ||
206 | char *buf, size_t size) | ||
207 | { | ||
208 | u64 new_period = he->stat.period; | ||
209 | u64 old_period = pair->stat.period; | ||
210 | |||
211 | return scnprintf(buf, size, | ||
212 | "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", | ||
213 | new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); | ||
214 | } | ||
215 | |||
216 | int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, | ||
217 | char *buf, size_t size) | ||
218 | { | ||
219 | switch (compute) { | ||
220 | case COMPUTE_DELTA: | ||
221 | return formula_delta(he, pair, buf, size); | ||
222 | case COMPUTE_RATIO: | ||
223 | return formula_ratio(he, pair, buf, size); | ||
224 | case COMPUTE_WEIGHTED_DIFF: | ||
225 | return formula_wdiff(he, pair, buf, size); | ||
226 | default: | ||
227 | BUG_ON(1); | ||
228 | } | ||
229 | |||
230 | return -1; | ||
231 | } | ||
27 | 232 | ||
28 | static int hists__add_entry(struct hists *self, | 233 | static int hists__add_entry(struct hists *self, |
29 | struct addr_location *al, u64 period) | 234 | struct addr_location *al, u64 period) |
@@ -47,7 +252,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
47 | return -1; | 252 | return -1; |
48 | } | 253 | } |
49 | 254 | ||
50 | if (al.filtered || al.sym == NULL) | 255 | if (al.filtered) |
51 | return 0; | 256 | return 0; |
52 | 257 | ||
53 | if (hists__add_entry(&evsel->hists, &al, sample->period)) { | 258 | if (hists__add_entry(&evsel->hists, &al, sample->period)) { |
@@ -63,115 +268,197 @@ static struct perf_tool tool = { | |||
63 | .sample = diff__process_sample_event, | 268 | .sample = diff__process_sample_event, |
64 | .mmap = perf_event__process_mmap, | 269 | .mmap = perf_event__process_mmap, |
65 | .comm = perf_event__process_comm, | 270 | .comm = perf_event__process_comm, |
66 | .exit = perf_event__process_task, | 271 | .exit = perf_event__process_exit, |
67 | .fork = perf_event__process_task, | 272 | .fork = perf_event__process_fork, |
68 | .lost = perf_event__process_lost, | 273 | .lost = perf_event__process_lost, |
69 | .ordered_samples = true, | 274 | .ordered_samples = true, |
70 | .ordering_requires_timestamps = true, | 275 | .ordering_requires_timestamps = true, |
71 | }; | 276 | }; |
72 | 277 | ||
73 | static void insert_hist_entry_by_name(struct rb_root *root, | 278 | static struct perf_evsel *evsel_match(struct perf_evsel *evsel, |
74 | struct hist_entry *he) | 279 | struct perf_evlist *evlist) |
75 | { | 280 | { |
76 | struct rb_node **p = &root->rb_node; | 281 | struct perf_evsel *e; |
77 | struct rb_node *parent = NULL; | ||
78 | struct hist_entry *iter; | ||
79 | 282 | ||
80 | while (*p != NULL) { | 283 | list_for_each_entry(e, &evlist->entries, node) |
81 | parent = *p; | 284 | if (perf_evsel__match2(evsel, e)) |
82 | iter = rb_entry(parent, struct hist_entry, rb_node); | 285 | return e; |
83 | if (hist_entry__cmp(he, iter) < 0) | 286 | |
84 | p = &(*p)->rb_left; | 287 | return NULL; |
85 | else | 288 | } |
86 | p = &(*p)->rb_right; | 289 | |
290 | static void perf_evlist__collapse_resort(struct perf_evlist *evlist) | ||
291 | { | ||
292 | struct perf_evsel *evsel; | ||
293 | |||
294 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
295 | struct hists *hists = &evsel->hists; | ||
296 | |||
297 | hists__collapse_resort(hists); | ||
87 | } | 298 | } |
299 | } | ||
88 | 300 | ||
89 | rb_link_node(&he->rb_node, parent, p); | 301 | static void hists__baseline_only(struct hists *hists) |
90 | rb_insert_color(&he->rb_node, root); | 302 | { |
303 | struct rb_root *root; | ||
304 | struct rb_node *next; | ||
305 | |||
306 | if (sort__need_collapse) | ||
307 | root = &hists->entries_collapsed; | ||
308 | else | ||
309 | root = hists->entries_in; | ||
310 | |||
311 | next = rb_first(root); | ||
312 | while (next != NULL) { | ||
313 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in); | ||
314 | |||
315 | next = rb_next(&he->rb_node_in); | ||
316 | if (!hist_entry__next_pair(he)) { | ||
317 | rb_erase(&he->rb_node_in, root); | ||
318 | hist_entry__free(he); | ||
319 | } | ||
320 | } | ||
91 | } | 321 | } |
92 | 322 | ||
93 | static void hists__name_resort(struct hists *self, bool sort) | 323 | static void hists__precompute(struct hists *hists) |
94 | { | 324 | { |
95 | unsigned long position = 1; | 325 | struct rb_node *next = rb_first(&hists->entries); |
96 | struct rb_root tmp = RB_ROOT; | ||
97 | struct rb_node *next = rb_first(&self->entries); | ||
98 | 326 | ||
99 | while (next != NULL) { | 327 | while (next != NULL) { |
100 | struct hist_entry *n = rb_entry(next, struct hist_entry, rb_node); | 328 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node); |
329 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
101 | 330 | ||
102 | next = rb_next(&n->rb_node); | 331 | next = rb_next(&he->rb_node); |
103 | n->position = position++; | 332 | if (!pair) |
333 | continue; | ||
104 | 334 | ||
105 | if (sort) { | 335 | switch (compute) { |
106 | rb_erase(&n->rb_node, &self->entries); | 336 | case COMPUTE_DELTA: |
107 | insert_hist_entry_by_name(&tmp, n); | 337 | perf_diff__compute_delta(he, pair); |
338 | break; | ||
339 | case COMPUTE_RATIO: | ||
340 | perf_diff__compute_ratio(he, pair); | ||
341 | break; | ||
342 | case COMPUTE_WEIGHTED_DIFF: | ||
343 | perf_diff__compute_wdiff(he, pair); | ||
344 | break; | ||
345 | default: | ||
346 | BUG_ON(1); | ||
108 | } | 347 | } |
109 | } | 348 | } |
349 | } | ||
110 | 350 | ||
111 | if (sort) | 351 | static int64_t cmp_doubles(double l, double r) |
112 | self->entries = tmp; | 352 | { |
353 | if (l > r) | ||
354 | return -1; | ||
355 | else if (l < r) | ||
356 | return 1; | ||
357 | else | ||
358 | return 0; | ||
113 | } | 359 | } |
114 | 360 | ||
115 | static struct hist_entry *hists__find_entry(struct hists *self, | 361 | static int64_t |
116 | struct hist_entry *he) | 362 | hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, |
363 | int c) | ||
117 | { | 364 | { |
118 | struct rb_node *n = self->entries.rb_node; | 365 | switch (c) { |
366 | case COMPUTE_DELTA: | ||
367 | { | ||
368 | double l = left->diff.period_ratio_delta; | ||
369 | double r = right->diff.period_ratio_delta; | ||
119 | 370 | ||
120 | while (n) { | 371 | return cmp_doubles(l, r); |
121 | struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node); | 372 | } |
122 | int64_t cmp = hist_entry__cmp(he, iter); | 373 | case COMPUTE_RATIO: |
374 | { | ||
375 | double l = left->diff.period_ratio; | ||
376 | double r = right->diff.period_ratio; | ||
123 | 377 | ||
124 | if (cmp < 0) | 378 | return cmp_doubles(l, r); |
125 | n = n->rb_left; | ||
126 | else if (cmp > 0) | ||
127 | n = n->rb_right; | ||
128 | else | ||
129 | return iter; | ||
130 | } | 379 | } |
380 | case COMPUTE_WEIGHTED_DIFF: | ||
381 | { | ||
382 | s64 l = left->diff.wdiff; | ||
383 | s64 r = right->diff.wdiff; | ||
131 | 384 | ||
132 | return NULL; | 385 | return r - l; |
386 | } | ||
387 | default: | ||
388 | BUG_ON(1); | ||
389 | } | ||
390 | |||
391 | return 0; | ||
133 | } | 392 | } |
134 | 393 | ||
135 | static void hists__match(struct hists *older, struct hists *newer) | 394 | static void insert_hist_entry_by_compute(struct rb_root *root, |
395 | struct hist_entry *he, | ||
396 | int c) | ||
136 | { | 397 | { |
137 | struct rb_node *nd; | 398 | struct rb_node **p = &root->rb_node; |
399 | struct rb_node *parent = NULL; | ||
400 | struct hist_entry *iter; | ||
138 | 401 | ||
139 | for (nd = rb_first(&newer->entries); nd; nd = rb_next(nd)) { | 402 | while (*p != NULL) { |
140 | struct hist_entry *pos = rb_entry(nd, struct hist_entry, rb_node); | 403 | parent = *p; |
141 | pos->pair = hists__find_entry(older, pos); | 404 | iter = rb_entry(parent, struct hist_entry, rb_node); |
405 | if (hist_entry__cmp_compute(he, iter, c) < 0) | ||
406 | p = &(*p)->rb_left; | ||
407 | else | ||
408 | p = &(*p)->rb_right; | ||
142 | } | 409 | } |
410 | |||
411 | rb_link_node(&he->rb_node, parent, p); | ||
412 | rb_insert_color(&he->rb_node, root); | ||
143 | } | 413 | } |
144 | 414 | ||
145 | static struct perf_evsel *evsel_match(struct perf_evsel *evsel, | 415 | static void hists__compute_resort(struct hists *hists) |
146 | struct perf_evlist *evlist) | ||
147 | { | 416 | { |
148 | struct perf_evsel *e; | 417 | struct rb_root *root; |
418 | struct rb_node *next; | ||
149 | 419 | ||
150 | list_for_each_entry(e, &evlist->entries, node) | 420 | if (sort__need_collapse) |
151 | if (perf_evsel__match2(evsel, e)) | 421 | root = &hists->entries_collapsed; |
152 | return e; | 422 | else |
423 | root = hists->entries_in; | ||
153 | 424 | ||
154 | return NULL; | 425 | hists->entries = RB_ROOT; |
155 | } | 426 | next = rb_first(root); |
156 | 427 | ||
157 | static void perf_evlist__resort_hists(struct perf_evlist *evlist, bool name) | 428 | hists->nr_entries = 0; |
158 | { | 429 | hists->stats.total_period = 0; |
159 | struct perf_evsel *evsel; | 430 | hists__reset_col_len(hists); |
160 | 431 | ||
161 | list_for_each_entry(evsel, &evlist->entries, node) { | 432 | while (next != NULL) { |
162 | struct hists *hists = &evsel->hists; | 433 | struct hist_entry *he; |
163 | 434 | ||
164 | hists__output_resort(hists); | 435 | he = rb_entry(next, struct hist_entry, rb_node_in); |
436 | next = rb_next(&he->rb_node_in); | ||
165 | 437 | ||
166 | /* | 438 | insert_hist_entry_by_compute(&hists->entries, he, compute); |
167 | * The hists__name_resort only sets possition | 439 | hists__inc_nr_entries(hists, he); |
168 | * if name is false. | ||
169 | */ | ||
170 | if (name || ((!name) && show_displacement)) | ||
171 | hists__name_resort(hists, name); | ||
172 | } | 440 | } |
173 | } | 441 | } |
174 | 442 | ||
443 | static void hists__process(struct hists *old, struct hists *new) | ||
444 | { | ||
445 | hists__match(new, old); | ||
446 | |||
447 | if (show_baseline_only) | ||
448 | hists__baseline_only(new); | ||
449 | else | ||
450 | hists__link(new, old); | ||
451 | |||
452 | if (sort_compute) { | ||
453 | hists__precompute(new); | ||
454 | hists__compute_resort(new); | ||
455 | } else { | ||
456 | hists__output_resort(new); | ||
457 | } | ||
458 | |||
459 | hists__fprintf(new, true, 0, 0, stdout); | ||
460 | } | ||
461 | |||
175 | static int __cmd_diff(void) | 462 | static int __cmd_diff(void) |
176 | { | 463 | { |
177 | int ret, i; | 464 | int ret, i; |
@@ -198,8 +485,8 @@ static int __cmd_diff(void) | |||
198 | evlist_old = older->evlist; | 485 | evlist_old = older->evlist; |
199 | evlist_new = newer->evlist; | 486 | evlist_new = newer->evlist; |
200 | 487 | ||
201 | perf_evlist__resort_hists(evlist_old, true); | 488 | perf_evlist__collapse_resort(evlist_old); |
202 | perf_evlist__resort_hists(evlist_new, false); | 489 | perf_evlist__collapse_resort(evlist_new); |
203 | 490 | ||
204 | list_for_each_entry(evsel, &evlist_new->entries, node) { | 491 | list_for_each_entry(evsel, &evlist_new->entries, node) { |
205 | struct perf_evsel *evsel_old; | 492 | struct perf_evsel *evsel_old; |
@@ -213,8 +500,7 @@ static int __cmd_diff(void) | |||
213 | 500 | ||
214 | first = false; | 501 | first = false; |
215 | 502 | ||
216 | hists__match(&evsel_old->hists, &evsel->hists); | 503 | hists__process(&evsel_old->hists, &evsel->hists); |
217 | hists__fprintf(&evsel->hists, true, 0, 0, stdout); | ||
218 | } | 504 | } |
219 | 505 | ||
220 | out_delete: | 506 | out_delete: |
@@ -233,8 +519,16 @@ static const char * const diff_usage[] = { | |||
233 | static const struct option options[] = { | 519 | static const struct option options[] = { |
234 | OPT_INCR('v', "verbose", &verbose, | 520 | OPT_INCR('v', "verbose", &verbose, |
235 | "be more verbose (show symbol address, etc)"), | 521 | "be more verbose (show symbol address, etc)"), |
236 | OPT_BOOLEAN('M', "displacement", &show_displacement, | 522 | OPT_BOOLEAN('b', "baseline-only", &show_baseline_only, |
237 | "Show position displacement relative to baseline"), | 523 | "Show only items with match in baseline"), |
524 | OPT_CALLBACK('c', "compute", &compute, | ||
525 | "delta,ratio,wdiff:w1,w2 (default delta)", | ||
526 | "Entries differential computation selection", | ||
527 | setup_compute), | ||
528 | OPT_BOOLEAN('p', "period", &show_period, | ||
529 | "Show period values."), | ||
530 | OPT_BOOLEAN('F', "formula", &show_formula, | ||
531 | "Show formula."), | ||
238 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | 532 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, |
239 | "dump raw trace in ASCII"), | 533 | "dump raw trace in ASCII"), |
240 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 534 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
@@ -258,17 +552,33 @@ static const struct option options[] = { | |||
258 | 552 | ||
259 | static void ui_init(void) | 553 | static void ui_init(void) |
260 | { | 554 | { |
261 | perf_hpp__init(); | 555 | /* |
262 | 556 | * Display baseline/delta/ratio | |
263 | /* No overhead column. */ | 557 | * formula/periods columns. |
264 | perf_hpp__column_enable(PERF_HPP__OVERHEAD, false); | 558 | */ |
265 | 559 | perf_hpp__column_enable(PERF_HPP__BASELINE); | |
266 | /* Display baseline/delta/displacement columns. */ | 560 | |
267 | perf_hpp__column_enable(PERF_HPP__BASELINE, true); | 561 | switch (compute) { |
268 | perf_hpp__column_enable(PERF_HPP__DELTA, true); | 562 | case COMPUTE_DELTA: |
269 | 563 | perf_hpp__column_enable(PERF_HPP__DELTA); | |
270 | if (show_displacement) | 564 | break; |
271 | perf_hpp__column_enable(PERF_HPP__DISPL, true); | 565 | case COMPUTE_RATIO: |
566 | perf_hpp__column_enable(PERF_HPP__RATIO); | ||
567 | break; | ||
568 | case COMPUTE_WEIGHTED_DIFF: | ||
569 | perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF); | ||
570 | break; | ||
571 | default: | ||
572 | BUG_ON(1); | ||
573 | }; | ||
574 | |||
575 | if (show_formula) | ||
576 | perf_hpp__column_enable(PERF_HPP__FORMULA); | ||
577 | |||
578 | if (show_period) { | ||
579 | perf_hpp__column_enable(PERF_HPP__PERIOD); | ||
580 | perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE); | ||
581 | } | ||
272 | } | 582 | } |
273 | 583 | ||
274 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | 584 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) |
@@ -295,7 +605,9 @@ int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | |||
295 | 605 | ||
296 | ui_init(); | 606 | ui_init(); |
297 | 607 | ||
298 | setup_sorting(diff_usage, options); | 608 | if (setup_sorting() < 0) |
609 | usage_with_options(diff_usage, options); | ||
610 | |||
299 | setup_pager(); | 611 | setup_pager(); |
300 | 612 | ||
301 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL); | 613 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", NULL); |
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 997afb82691b..05bd9dfe875c 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c | |||
@@ -15,94 +15,17 @@ | |||
15 | #include "util/parse-options.h" | 15 | #include "util/parse-options.h" |
16 | #include "util/session.h" | 16 | #include "util/session.h" |
17 | 17 | ||
18 | struct perf_attr_details { | 18 | static int __cmd_evlist(const char *file_name, struct perf_attr_details *details) |
19 | bool freq; | ||
20 | bool verbose; | ||
21 | }; | ||
22 | |||
23 | static int comma_printf(bool *first, const char *fmt, ...) | ||
24 | { | ||
25 | va_list args; | ||
26 | int ret = 0; | ||
27 | |||
28 | if (!*first) { | ||
29 | ret += printf(","); | ||
30 | } else { | ||
31 | ret += printf(":"); | ||
32 | *first = false; | ||
33 | } | ||
34 | |||
35 | va_start(args, fmt); | ||
36 | ret += vprintf(fmt, args); | ||
37 | va_end(args); | ||
38 | return ret; | ||
39 | } | ||
40 | |||
41 | static int __if_print(bool *first, const char *field, u64 value) | ||
42 | { | ||
43 | if (value == 0) | ||
44 | return 0; | ||
45 | |||
46 | return comma_printf(first, " %s: %" PRIu64, field, value); | ||
47 | } | ||
48 | |||
49 | #define if_print(field) __if_print(&first, #field, pos->attr.field) | ||
50 | |||
51 | static int __cmd_evlist(const char *input_name, struct perf_attr_details *details) | ||
52 | { | 19 | { |
53 | struct perf_session *session; | 20 | struct perf_session *session; |
54 | struct perf_evsel *pos; | 21 | struct perf_evsel *pos; |
55 | 22 | ||
56 | session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); | 23 | session = perf_session__new(file_name, O_RDONLY, 0, false, NULL); |
57 | if (session == NULL) | 24 | if (session == NULL) |
58 | return -ENOMEM; | 25 | return -ENOMEM; |
59 | 26 | ||
60 | list_for_each_entry(pos, &session->evlist->entries, node) { | 27 | list_for_each_entry(pos, &session->evlist->entries, node) |
61 | bool first = true; | 28 | perf_evsel__fprintf(pos, details, stdout); |
62 | |||
63 | printf("%s", perf_evsel__name(pos)); | ||
64 | |||
65 | if (details->verbose || details->freq) { | ||
66 | comma_printf(&first, " sample_freq=%" PRIu64, | ||
67 | (u64)pos->attr.sample_freq); | ||
68 | } | ||
69 | |||
70 | if (details->verbose) { | ||
71 | if_print(type); | ||
72 | if_print(config); | ||
73 | if_print(config1); | ||
74 | if_print(config2); | ||
75 | if_print(size); | ||
76 | if_print(sample_type); | ||
77 | if_print(read_format); | ||
78 | if_print(disabled); | ||
79 | if_print(inherit); | ||
80 | if_print(pinned); | ||
81 | if_print(exclusive); | ||
82 | if_print(exclude_user); | ||
83 | if_print(exclude_kernel); | ||
84 | if_print(exclude_hv); | ||
85 | if_print(exclude_idle); | ||
86 | if_print(mmap); | ||
87 | if_print(comm); | ||
88 | if_print(freq); | ||
89 | if_print(inherit_stat); | ||
90 | if_print(enable_on_exec); | ||
91 | if_print(task); | ||
92 | if_print(watermark); | ||
93 | if_print(precise_ip); | ||
94 | if_print(mmap_data); | ||
95 | if_print(sample_id_all); | ||
96 | if_print(exclude_host); | ||
97 | if_print(exclude_guest); | ||
98 | if_print(__reserved_1); | ||
99 | if_print(wakeup_events); | ||
100 | if_print(bp_type); | ||
101 | if_print(branch_sample_type); | ||
102 | } | ||
103 | |||
104 | putchar('\n'); | ||
105 | } | ||
106 | 29 | ||
107 | perf_session__delete(session); | 30 | perf_session__delete(session); |
108 | return 0; | 31 | return 0; |
@@ -111,12 +34,13 @@ static int __cmd_evlist(const char *input_name, struct perf_attr_details *detail | |||
111 | int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) | 34 | int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) |
112 | { | 35 | { |
113 | struct perf_attr_details details = { .verbose = false, }; | 36 | struct perf_attr_details details = { .verbose = false, }; |
114 | const char *input_name = NULL; | ||
115 | const struct option options[] = { | 37 | const struct option options[] = { |
116 | OPT_STRING('i', "input", &input_name, "file", "Input file name"), | 38 | OPT_STRING('i', "input", &input_name, "file", "Input file name"), |
117 | OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"), | 39 | OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"), |
118 | OPT_BOOLEAN('v', "verbose", &details.verbose, | 40 | OPT_BOOLEAN('v', "verbose", &details.verbose, |
119 | "Show all event attr details"), | 41 | "Show all event attr details"), |
42 | OPT_BOOLEAN('g', "group", &details.event_group, | ||
43 | "Show event group information"), | ||
120 | OPT_END() | 44 | OPT_END() |
121 | }; | 45 | }; |
122 | const char * const evlist_usage[] = { | 46 | const char * const evlist_usage[] = { |
@@ -128,5 +52,10 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) | |||
128 | if (argc) | 52 | if (argc) |
129 | usage_with_options(evlist_usage, options); | 53 | usage_with_options(evlist_usage, options); |
130 | 54 | ||
55 | if (details.event_group && (details.verbose || details.freq)) { | ||
56 | pr_err("--group option is not compatible with other options\n"); | ||
57 | usage_with_options(evlist_usage, options); | ||
58 | } | ||
59 | |||
131 | return __cmd_evlist(input_name, &details); | 60 | return __cmd_evlist(input_name, &details); |
132 | } | 61 | } |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 4688bea95c12..84ad6abe4258 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -8,33 +8,53 @@ | |||
8 | #include "builtin.h" | 8 | #include "builtin.h" |
9 | 9 | ||
10 | #include "perf.h" | 10 | #include "perf.h" |
11 | #include "util/color.h" | ||
12 | #include "util/evlist.h" | ||
13 | #include "util/evsel.h" | ||
11 | #include "util/session.h" | 14 | #include "util/session.h" |
12 | #include "util/tool.h" | 15 | #include "util/tool.h" |
13 | #include "util/debug.h" | 16 | #include "util/debug.h" |
17 | #include "util/build-id.h" | ||
14 | 18 | ||
15 | #include "util/parse-options.h" | 19 | #include "util/parse-options.h" |
16 | 20 | ||
21 | #include <linux/list.h> | ||
22 | |||
17 | struct perf_inject { | 23 | struct perf_inject { |
18 | struct perf_tool tool; | 24 | struct perf_tool tool; |
19 | bool build_ids; | 25 | bool build_ids; |
26 | bool sched_stat; | ||
27 | const char *input_name; | ||
28 | int pipe_output, | ||
29 | output; | ||
30 | u64 bytes_written; | ||
31 | struct list_head samples; | ||
32 | }; | ||
33 | |||
34 | struct event_entry { | ||
35 | struct list_head node; | ||
36 | u32 tid; | ||
37 | union perf_event event[0]; | ||
20 | }; | 38 | }; |
21 | 39 | ||
22 | static int perf_event__repipe_synth(struct perf_tool *tool __maybe_unused, | 40 | static int perf_event__repipe_synth(struct perf_tool *tool, |
23 | union perf_event *event, | 41 | union perf_event *event, |
24 | struct machine *machine __maybe_unused) | 42 | struct machine *machine __maybe_unused) |
25 | { | 43 | { |
44 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
26 | uint32_t size; | 45 | uint32_t size; |
27 | void *buf = event; | 46 | void *buf = event; |
28 | 47 | ||
29 | size = event->header.size; | 48 | size = event->header.size; |
30 | 49 | ||
31 | while (size) { | 50 | while (size) { |
32 | int ret = write(STDOUT_FILENO, buf, size); | 51 | int ret = write(inject->output, buf, size); |
33 | if (ret < 0) | 52 | if (ret < 0) |
34 | return -errno; | 53 | return -errno; |
35 | 54 | ||
36 | size -= ret; | 55 | size -= ret; |
37 | buf += ret; | 56 | buf += ret; |
57 | inject->bytes_written += ret; | ||
38 | } | 58 | } |
39 | 59 | ||
40 | return 0; | 60 | return 0; |
@@ -80,12 +100,25 @@ static int perf_event__repipe(struct perf_tool *tool, | |||
80 | return perf_event__repipe_synth(tool, event, machine); | 100 | return perf_event__repipe_synth(tool, event, machine); |
81 | } | 101 | } |
82 | 102 | ||
103 | typedef int (*inject_handler)(struct perf_tool *tool, | ||
104 | union perf_event *event, | ||
105 | struct perf_sample *sample, | ||
106 | struct perf_evsel *evsel, | ||
107 | struct machine *machine); | ||
108 | |||
83 | static int perf_event__repipe_sample(struct perf_tool *tool, | 109 | static int perf_event__repipe_sample(struct perf_tool *tool, |
84 | union perf_event *event, | 110 | union perf_event *event, |
85 | struct perf_sample *sample __maybe_unused, | 111 | struct perf_sample *sample, |
86 | struct perf_evsel *evsel __maybe_unused, | 112 | struct perf_evsel *evsel, |
87 | struct machine *machine) | 113 | struct machine *machine) |
88 | { | 114 | { |
115 | if (evsel->handler.func) { | ||
116 | inject_handler f = evsel->handler.func; | ||
117 | return f(tool, event, sample, evsel, machine); | ||
118 | } | ||
119 | |||
120 | build_id__mark_dso_hit(tool, event, sample, evsel, machine); | ||
121 | |||
89 | return perf_event__repipe_synth(tool, event, machine); | 122 | return perf_event__repipe_synth(tool, event, machine); |
90 | } | 123 | } |
91 | 124 | ||
@@ -102,14 +135,14 @@ static int perf_event__repipe_mmap(struct perf_tool *tool, | |||
102 | return err; | 135 | return err; |
103 | } | 136 | } |
104 | 137 | ||
105 | static int perf_event__repipe_task(struct perf_tool *tool, | 138 | static int perf_event__repipe_fork(struct perf_tool *tool, |
106 | union perf_event *event, | 139 | union perf_event *event, |
107 | struct perf_sample *sample, | 140 | struct perf_sample *sample, |
108 | struct machine *machine) | 141 | struct machine *machine) |
109 | { | 142 | { |
110 | int err; | 143 | int err; |
111 | 144 | ||
112 | err = perf_event__process_task(tool, event, sample, machine); | 145 | err = perf_event__process_fork(tool, event, sample, machine); |
113 | perf_event__repipe(tool, event, sample, machine); | 146 | perf_event__repipe(tool, event, sample, machine); |
114 | 147 | ||
115 | return err; | 148 | return err; |
@@ -210,6 +243,80 @@ repipe: | |||
210 | return 0; | 243 | return 0; |
211 | } | 244 | } |
212 | 245 | ||
246 | static int perf_inject__sched_process_exit(struct perf_tool *tool, | ||
247 | union perf_event *event __maybe_unused, | ||
248 | struct perf_sample *sample, | ||
249 | struct perf_evsel *evsel __maybe_unused, | ||
250 | struct machine *machine __maybe_unused) | ||
251 | { | ||
252 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
253 | struct event_entry *ent; | ||
254 | |||
255 | list_for_each_entry(ent, &inject->samples, node) { | ||
256 | if (sample->tid == ent->tid) { | ||
257 | list_del_init(&ent->node); | ||
258 | free(ent); | ||
259 | break; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | static int perf_inject__sched_switch(struct perf_tool *tool, | ||
267 | union perf_event *event, | ||
268 | struct perf_sample *sample, | ||
269 | struct perf_evsel *evsel, | ||
270 | struct machine *machine) | ||
271 | { | ||
272 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
273 | struct event_entry *ent; | ||
274 | |||
275 | perf_inject__sched_process_exit(tool, event, sample, evsel, machine); | ||
276 | |||
277 | ent = malloc(event->header.size + sizeof(struct event_entry)); | ||
278 | if (ent == NULL) { | ||
279 | color_fprintf(stderr, PERF_COLOR_RED, | ||
280 | "Not enough memory to process sched switch event!"); | ||
281 | return -1; | ||
282 | } | ||
283 | |||
284 | ent->tid = sample->tid; | ||
285 | memcpy(&ent->event, event, event->header.size); | ||
286 | list_add(&ent->node, &inject->samples); | ||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static int perf_inject__sched_stat(struct perf_tool *tool, | ||
291 | union perf_event *event __maybe_unused, | ||
292 | struct perf_sample *sample, | ||
293 | struct perf_evsel *evsel, | ||
294 | struct machine *machine) | ||
295 | { | ||
296 | struct event_entry *ent; | ||
297 | union perf_event *event_sw; | ||
298 | struct perf_sample sample_sw; | ||
299 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | ||
300 | u32 pid = perf_evsel__intval(evsel, sample, "pid"); | ||
301 | |||
302 | list_for_each_entry(ent, &inject->samples, node) { | ||
303 | if (pid == ent->tid) | ||
304 | goto found; | ||
305 | } | ||
306 | |||
307 | return 0; | ||
308 | found: | ||
309 | event_sw = &ent->event[0]; | ||
310 | perf_evsel__parse_sample(evsel, event_sw, &sample_sw); | ||
311 | |||
312 | sample_sw.period = sample->period; | ||
313 | sample_sw.time = sample->time; | ||
314 | perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, | ||
315 | &sample_sw, false); | ||
316 | build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); | ||
317 | return perf_event__repipe(tool, event_sw, &sample_sw, machine); | ||
318 | } | ||
319 | |||
213 | extern volatile int session_done; | 320 | extern volatile int session_done; |
214 | 321 | ||
215 | static void sig_handler(int sig __maybe_unused) | 322 | static void sig_handler(int sig __maybe_unused) |
@@ -217,6 +324,21 @@ static void sig_handler(int sig __maybe_unused) | |||
217 | session_done = 1; | 324 | session_done = 1; |
218 | } | 325 | } |
219 | 326 | ||
327 | static int perf_evsel__check_stype(struct perf_evsel *evsel, | ||
328 | u64 sample_type, const char *sample_msg) | ||
329 | { | ||
330 | struct perf_event_attr *attr = &evsel->attr; | ||
331 | const char *name = perf_evsel__name(evsel); | ||
332 | |||
333 | if (!(attr->sample_type & sample_type)) { | ||
334 | pr_err("Samples for %s event do not have %s attribute set.", | ||
335 | name, sample_msg); | ||
336 | return -EINVAL; | ||
337 | } | ||
338 | |||
339 | return 0; | ||
340 | } | ||
341 | |||
220 | static int __cmd_inject(struct perf_inject *inject) | 342 | static int __cmd_inject(struct perf_inject *inject) |
221 | { | 343 | { |
222 | struct perf_session *session; | 344 | struct perf_session *session; |
@@ -224,19 +346,48 @@ static int __cmd_inject(struct perf_inject *inject) | |||
224 | 346 | ||
225 | signal(SIGINT, sig_handler); | 347 | signal(SIGINT, sig_handler); |
226 | 348 | ||
227 | if (inject->build_ids) { | 349 | if (inject->build_ids || inject->sched_stat) { |
228 | inject->tool.sample = perf_event__inject_buildid; | ||
229 | inject->tool.mmap = perf_event__repipe_mmap; | 350 | inject->tool.mmap = perf_event__repipe_mmap; |
230 | inject->tool.fork = perf_event__repipe_task; | 351 | inject->tool.fork = perf_event__repipe_fork; |
231 | inject->tool.tracing_data = perf_event__repipe_tracing_data; | 352 | inject->tool.tracing_data = perf_event__repipe_tracing_data; |
232 | } | 353 | } |
233 | 354 | ||
234 | session = perf_session__new("-", O_RDONLY, false, true, &inject->tool); | 355 | session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool); |
235 | if (session == NULL) | 356 | if (session == NULL) |
236 | return -ENOMEM; | 357 | return -ENOMEM; |
237 | 358 | ||
359 | if (inject->build_ids) { | ||
360 | inject->tool.sample = perf_event__inject_buildid; | ||
361 | } else if (inject->sched_stat) { | ||
362 | struct perf_evsel *evsel; | ||
363 | |||
364 | inject->tool.ordered_samples = true; | ||
365 | |||
366 | list_for_each_entry(evsel, &session->evlist->entries, node) { | ||
367 | const char *name = perf_evsel__name(evsel); | ||
368 | |||
369 | if (!strcmp(name, "sched:sched_switch")) { | ||
370 | if (perf_evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID")) | ||
371 | return -EINVAL; | ||
372 | |||
373 | evsel->handler.func = perf_inject__sched_switch; | ||
374 | } else if (!strcmp(name, "sched:sched_process_exit")) | ||
375 | evsel->handler.func = perf_inject__sched_process_exit; | ||
376 | else if (!strncmp(name, "sched:sched_stat_", 17)) | ||
377 | evsel->handler.func = perf_inject__sched_stat; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | if (!inject->pipe_output) | ||
382 | lseek(inject->output, session->header.data_offset, SEEK_SET); | ||
383 | |||
238 | ret = perf_session__process_events(session, &inject->tool); | 384 | ret = perf_session__process_events(session, &inject->tool); |
239 | 385 | ||
386 | if (!inject->pipe_output) { | ||
387 | session->header.data_size = inject->bytes_written; | ||
388 | perf_session__write_header(session, session->evlist, inject->output, true); | ||
389 | } | ||
390 | |||
240 | perf_session__delete(session); | 391 | perf_session__delete(session); |
241 | 392 | ||
242 | return ret; | 393 | return ret; |
@@ -260,10 +411,20 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
260 | .tracing_data = perf_event__repipe_tracing_data_synth, | 411 | .tracing_data = perf_event__repipe_tracing_data_synth, |
261 | .build_id = perf_event__repipe_op2_synth, | 412 | .build_id = perf_event__repipe_op2_synth, |
262 | }, | 413 | }, |
414 | .input_name = "-", | ||
415 | .samples = LIST_HEAD_INIT(inject.samples), | ||
263 | }; | 416 | }; |
417 | const char *output_name = "-"; | ||
264 | const struct option options[] = { | 418 | const struct option options[] = { |
265 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, | 419 | OPT_BOOLEAN('b', "build-ids", &inject.build_ids, |
266 | "Inject build-ids into the output stream"), | 420 | "Inject build-ids into the output stream"), |
421 | OPT_STRING('i', "input", &inject.input_name, "file", | ||
422 | "input file name"), | ||
423 | OPT_STRING('o', "output", &output_name, "file", | ||
424 | "output file name"), | ||
425 | OPT_BOOLEAN('s', "sched-stat", &inject.sched_stat, | ||
426 | "Merge sched-stat and sched-switch for getting events " | ||
427 | "where and how long tasks slept"), | ||
267 | OPT_INCR('v', "verbose", &verbose, | 428 | OPT_INCR('v', "verbose", &verbose, |
268 | "be more verbose (show build ids, etc)"), | 429 | "be more verbose (show build ids, etc)"), |
269 | OPT_END() | 430 | OPT_END() |
@@ -281,6 +442,18 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
281 | if (argc) | 442 | if (argc) |
282 | usage_with_options(inject_usage, options); | 443 | usage_with_options(inject_usage, options); |
283 | 444 | ||
445 | if (!strcmp(output_name, "-")) { | ||
446 | inject.pipe_output = 1; | ||
447 | inject.output = STDOUT_FILENO; | ||
448 | } else { | ||
449 | inject.output = open(output_name, O_CREAT | O_WRONLY | O_TRUNC, | ||
450 | S_IRUSR | S_IWUSR); | ||
451 | if (inject.output < 0) { | ||
452 | perror("failed to create output file"); | ||
453 | return -1; | ||
454 | } | ||
455 | } | ||
456 | |||
284 | if (symbol__init() < 0) | 457 | if (symbol__init() < 0) |
285 | return -1; | 458 | return -1; |
286 | 459 | ||
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 14bf82f63659..46878daca5cc 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "util/debug.h" | 17 | #include "util/debug.h" |
18 | 18 | ||
19 | #include <linux/rbtree.h> | 19 | #include <linux/rbtree.h> |
20 | #include <linux/string.h> | ||
20 | 21 | ||
21 | struct alloc_stat; | 22 | struct alloc_stat; |
22 | typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); | 23 | typedef int (*sort_fn_t)(struct alloc_stat *, struct alloc_stat *); |
@@ -340,7 +341,7 @@ static void __print_result(struct rb_root *root, struct perf_session *session, | |||
340 | int n_lines, int is_caller) | 341 | int n_lines, int is_caller) |
341 | { | 342 | { |
342 | struct rb_node *next; | 343 | struct rb_node *next; |
343 | struct machine *machine; | 344 | struct machine *machine = &session->machines.host; |
344 | 345 | ||
345 | printf("%.102s\n", graph_dotted_line); | 346 | printf("%.102s\n", graph_dotted_line); |
346 | printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); | 347 | printf(" %-34s |", is_caller ? "Callsite": "Alloc Ptr"); |
@@ -349,11 +350,6 @@ static void __print_result(struct rb_root *root, struct perf_session *session, | |||
349 | 350 | ||
350 | next = rb_first(root); | 351 | next = rb_first(root); |
351 | 352 | ||
352 | machine = perf_session__find_host_machine(session); | ||
353 | if (!machine) { | ||
354 | pr_err("__print_result: couldn't find kernel information\n"); | ||
355 | return; | ||
356 | } | ||
357 | while (next && n_lines--) { | 353 | while (next && n_lines--) { |
358 | struct alloc_stat *data = rb_entry(next, struct alloc_stat, | 354 | struct alloc_stat *data = rb_entry(next, struct alloc_stat, |
359 | node); | 355 | node); |
@@ -477,7 +473,7 @@ static void sort_result(void) | |||
477 | __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); | 473 | __sort_result(&root_caller_stat, &root_caller_sorted, &caller_sort); |
478 | } | 474 | } |
479 | 475 | ||
480 | static int __cmd_kmem(const char *input_name) | 476 | static int __cmd_kmem(void) |
481 | { | 477 | { |
482 | int err = -EINVAL; | 478 | int err = -EINVAL; |
483 | struct perf_session *session; | 479 | struct perf_session *session; |
@@ -614,8 +610,7 @@ static struct sort_dimension *avail_sorts[] = { | |||
614 | &pingpong_sort_dimension, | 610 | &pingpong_sort_dimension, |
615 | }; | 611 | }; |
616 | 612 | ||
617 | #define NUM_AVAIL_SORTS \ | 613 | #define NUM_AVAIL_SORTS ((int)ARRAY_SIZE(avail_sorts)) |
618 | (int)(sizeof(avail_sorts) / sizeof(struct sort_dimension *)) | ||
619 | 614 | ||
620 | static int sort_dimension__add(const char *tok, struct list_head *list) | 615 | static int sort_dimension__add(const char *tok, struct list_head *list) |
621 | { | 616 | { |
@@ -624,12 +619,11 @@ static int sort_dimension__add(const char *tok, struct list_head *list) | |||
624 | 619 | ||
625 | for (i = 0; i < NUM_AVAIL_SORTS; i++) { | 620 | for (i = 0; i < NUM_AVAIL_SORTS; i++) { |
626 | if (!strcmp(avail_sorts[i]->name, tok)) { | 621 | if (!strcmp(avail_sorts[i]->name, tok)) { |
627 | sort = malloc(sizeof(*sort)); | 622 | sort = memdup(avail_sorts[i], sizeof(*avail_sorts[i])); |
628 | if (!sort) { | 623 | if (!sort) { |
629 | pr_err("%s: malloc failed\n", __func__); | 624 | pr_err("%s: memdup failed\n", __func__); |
630 | return -1; | 625 | return -1; |
631 | } | 626 | } |
632 | memcpy(sort, avail_sorts[i], sizeof(*sort)); | ||
633 | list_add_tail(&sort->list, list); | 627 | list_add_tail(&sort->list, list); |
634 | return 0; | 628 | return 0; |
635 | } | 629 | } |
@@ -743,7 +737,6 @@ static int __cmd_record(int argc, const char **argv) | |||
743 | int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | 737 | int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) |
744 | { | 738 | { |
745 | const char * const default_sort_order = "frag,hit,bytes"; | 739 | const char * const default_sort_order = "frag,hit,bytes"; |
746 | const char *input_name = NULL; | ||
747 | const struct option kmem_options[] = { | 740 | const struct option kmem_options[] = { |
748 | OPT_STRING('i', "input", &input_name, "file", "input file name"), | 741 | OPT_STRING('i', "input", &input_name, "file", "input file name"), |
749 | OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL, | 742 | OPT_CALLBACK_NOOPT(0, "caller", NULL, NULL, |
@@ -779,7 +772,7 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
779 | if (list_empty(&alloc_sort)) | 772 | if (list_empty(&alloc_sort)) |
780 | setup_sorting(&alloc_sort, default_sort_order); | 773 | setup_sorting(&alloc_sort, default_sort_order); |
781 | 774 | ||
782 | return __cmd_kmem(input_name); | 775 | return __cmd_kmem(); |
783 | } else | 776 | } else |
784 | usage_with_options(kmem_usage, kmem_options); | 777 | usage_with_options(kmem_usage, kmem_options); |
785 | 778 | ||
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 260abc535b5b..37a769d7f9fe 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -22,9 +22,10 @@ | |||
22 | #include <pthread.h> | 22 | #include <pthread.h> |
23 | #include <math.h> | 23 | #include <math.h> |
24 | 24 | ||
25 | #include "../../arch/x86/include/asm/svm.h" | 25 | #if defined(__i386__) || defined(__x86_64__) |
26 | #include "../../arch/x86/include/asm/vmx.h" | 26 | #include <asm/svm.h> |
27 | #include "../../arch/x86/include/asm/kvm.h" | 27 | #include <asm/vmx.h> |
28 | #include <asm/kvm.h> | ||
28 | 29 | ||
29 | struct event_key { | 30 | struct event_key { |
30 | #define INVALID_KEY (~0ULL) | 31 | #define INVALID_KEY (~0ULL) |
@@ -58,7 +59,7 @@ struct kvm_event_key { | |||
58 | }; | 59 | }; |
59 | 60 | ||
60 | 61 | ||
61 | struct perf_kvm; | 62 | struct perf_kvm_stat; |
62 | 63 | ||
63 | struct kvm_events_ops { | 64 | struct kvm_events_ops { |
64 | bool (*is_begin_event)(struct perf_evsel *evsel, | 65 | bool (*is_begin_event)(struct perf_evsel *evsel, |
@@ -66,7 +67,7 @@ struct kvm_events_ops { | |||
66 | struct event_key *key); | 67 | struct event_key *key); |
67 | bool (*is_end_event)(struct perf_evsel *evsel, | 68 | bool (*is_end_event)(struct perf_evsel *evsel, |
68 | struct perf_sample *sample, struct event_key *key); | 69 | struct perf_sample *sample, struct event_key *key); |
69 | void (*decode_key)(struct perf_kvm *kvm, struct event_key *key, | 70 | void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key, |
70 | char decode[20]); | 71 | char decode[20]); |
71 | const char *name; | 72 | const char *name; |
72 | }; | 73 | }; |
@@ -79,7 +80,7 @@ struct exit_reasons_table { | |||
79 | #define EVENTS_BITS 12 | 80 | #define EVENTS_BITS 12 |
80 | #define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS) | 81 | #define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS) |
81 | 82 | ||
82 | struct perf_kvm { | 83 | struct perf_kvm_stat { |
83 | struct perf_tool tool; | 84 | struct perf_tool tool; |
84 | struct perf_session *session; | 85 | struct perf_session *session; |
85 | 86 | ||
@@ -146,7 +147,7 @@ static struct exit_reasons_table svm_exit_reasons[] = { | |||
146 | SVM_EXIT_REASONS | 147 | SVM_EXIT_REASONS |
147 | }; | 148 | }; |
148 | 149 | ||
149 | static const char *get_exit_reason(struct perf_kvm *kvm, u64 exit_code) | 150 | static const char *get_exit_reason(struct perf_kvm_stat *kvm, u64 exit_code) |
150 | { | 151 | { |
151 | int i = kvm->exit_reasons_size; | 152 | int i = kvm->exit_reasons_size; |
152 | struct exit_reasons_table *tbl = kvm->exit_reasons; | 153 | struct exit_reasons_table *tbl = kvm->exit_reasons; |
@@ -162,7 +163,7 @@ static const char *get_exit_reason(struct perf_kvm *kvm, u64 exit_code) | |||
162 | return "UNKNOWN"; | 163 | return "UNKNOWN"; |
163 | } | 164 | } |
164 | 165 | ||
165 | static void exit_event_decode_key(struct perf_kvm *kvm, | 166 | static void exit_event_decode_key(struct perf_kvm_stat *kvm, |
166 | struct event_key *key, | 167 | struct event_key *key, |
167 | char decode[20]) | 168 | char decode[20]) |
168 | { | 169 | { |
@@ -228,7 +229,7 @@ static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample, | |||
228 | return false; | 229 | return false; |
229 | } | 230 | } |
230 | 231 | ||
231 | static void mmio_event_decode_key(struct perf_kvm *kvm __maybe_unused, | 232 | static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, |
232 | struct event_key *key, | 233 | struct event_key *key, |
233 | char decode[20]) | 234 | char decode[20]) |
234 | { | 235 | { |
@@ -271,7 +272,7 @@ static bool ioport_event_end(struct perf_evsel *evsel, | |||
271 | return kvm_entry_event(evsel); | 272 | return kvm_entry_event(evsel); |
272 | } | 273 | } |
273 | 274 | ||
274 | static void ioport_event_decode_key(struct perf_kvm *kvm __maybe_unused, | 275 | static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, |
275 | struct event_key *key, | 276 | struct event_key *key, |
276 | char decode[20]) | 277 | char decode[20]) |
277 | { | 278 | { |
@@ -286,7 +287,7 @@ static struct kvm_events_ops ioport_events = { | |||
286 | .name = "IO Port Access" | 287 | .name = "IO Port Access" |
287 | }; | 288 | }; |
288 | 289 | ||
289 | static bool register_kvm_events_ops(struct perf_kvm *kvm) | 290 | static bool register_kvm_events_ops(struct perf_kvm_stat *kvm) |
290 | { | 291 | { |
291 | bool ret = true; | 292 | bool ret = true; |
292 | 293 | ||
@@ -311,11 +312,11 @@ struct vcpu_event_record { | |||
311 | }; | 312 | }; |
312 | 313 | ||
313 | 314 | ||
314 | static void init_kvm_event_record(struct perf_kvm *kvm) | 315 | static void init_kvm_event_record(struct perf_kvm_stat *kvm) |
315 | { | 316 | { |
316 | int i; | 317 | unsigned int i; |
317 | 318 | ||
318 | for (i = 0; i < (int)EVENTS_CACHE_SIZE; i++) | 319 | for (i = 0; i < EVENTS_CACHE_SIZE; i++) |
319 | INIT_LIST_HEAD(&kvm->kvm_events_cache[i]); | 320 | INIT_LIST_HEAD(&kvm->kvm_events_cache[i]); |
320 | } | 321 | } |
321 | 322 | ||
@@ -360,7 +361,7 @@ static struct kvm_event *kvm_alloc_init_event(struct event_key *key) | |||
360 | return event; | 361 | return event; |
361 | } | 362 | } |
362 | 363 | ||
363 | static struct kvm_event *find_create_kvm_event(struct perf_kvm *kvm, | 364 | static struct kvm_event *find_create_kvm_event(struct perf_kvm_stat *kvm, |
364 | struct event_key *key) | 365 | struct event_key *key) |
365 | { | 366 | { |
366 | struct kvm_event *event; | 367 | struct kvm_event *event; |
@@ -369,9 +370,10 @@ static struct kvm_event *find_create_kvm_event(struct perf_kvm *kvm, | |||
369 | BUG_ON(key->key == INVALID_KEY); | 370 | BUG_ON(key->key == INVALID_KEY); |
370 | 371 | ||
371 | head = &kvm->kvm_events_cache[kvm_events_hash_fn(key->key)]; | 372 | head = &kvm->kvm_events_cache[kvm_events_hash_fn(key->key)]; |
372 | list_for_each_entry(event, head, hash_entry) | 373 | list_for_each_entry(event, head, hash_entry) { |
373 | if (event->key.key == key->key && event->key.info == key->info) | 374 | if (event->key.key == key->key && event->key.info == key->info) |
374 | return event; | 375 | return event; |
376 | } | ||
375 | 377 | ||
376 | event = kvm_alloc_init_event(key); | 378 | event = kvm_alloc_init_event(key); |
377 | if (!event) | 379 | if (!event) |
@@ -381,7 +383,7 @@ static struct kvm_event *find_create_kvm_event(struct perf_kvm *kvm, | |||
381 | return event; | 383 | return event; |
382 | } | 384 | } |
383 | 385 | ||
384 | static bool handle_begin_event(struct perf_kvm *kvm, | 386 | static bool handle_begin_event(struct perf_kvm_stat *kvm, |
385 | struct vcpu_event_record *vcpu_record, | 387 | struct vcpu_event_record *vcpu_record, |
386 | struct event_key *key, u64 timestamp) | 388 | struct event_key *key, u64 timestamp) |
387 | { | 389 | { |
@@ -416,7 +418,10 @@ static double kvm_event_rel_stddev(int vcpu_id, struct kvm_event *event) | |||
416 | static bool update_kvm_event(struct kvm_event *event, int vcpu_id, | 418 | static bool update_kvm_event(struct kvm_event *event, int vcpu_id, |
417 | u64 time_diff) | 419 | u64 time_diff) |
418 | { | 420 | { |
419 | kvm_update_event_stats(&event->total, time_diff); | 421 | if (vcpu_id == -1) { |
422 | kvm_update_event_stats(&event->total, time_diff); | ||
423 | return true; | ||
424 | } | ||
420 | 425 | ||
421 | if (!kvm_event_expand(event, vcpu_id)) | 426 | if (!kvm_event_expand(event, vcpu_id)) |
422 | return false; | 427 | return false; |
@@ -425,13 +430,19 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id, | |||
425 | return true; | 430 | return true; |
426 | } | 431 | } |
427 | 432 | ||
428 | static bool handle_end_event(struct perf_kvm *kvm, | 433 | static bool handle_end_event(struct perf_kvm_stat *kvm, |
429 | struct vcpu_event_record *vcpu_record, | 434 | struct vcpu_event_record *vcpu_record, |
430 | struct event_key *key, | 435 | struct event_key *key, |
431 | u64 timestamp) | 436 | u64 timestamp) |
432 | { | 437 | { |
433 | struct kvm_event *event; | 438 | struct kvm_event *event; |
434 | u64 time_begin, time_diff; | 439 | u64 time_begin, time_diff; |
440 | int vcpu; | ||
441 | |||
442 | if (kvm->trace_vcpu == -1) | ||
443 | vcpu = -1; | ||
444 | else | ||
445 | vcpu = vcpu_record->vcpu_id; | ||
435 | 446 | ||
436 | event = vcpu_record->last_event; | 447 | event = vcpu_record->last_event; |
437 | time_begin = vcpu_record->start_time; | 448 | time_begin = vcpu_record->start_time; |
@@ -461,7 +472,7 @@ static bool handle_end_event(struct perf_kvm *kvm, | |||
461 | BUG_ON(timestamp < time_begin); | 472 | BUG_ON(timestamp < time_begin); |
462 | 473 | ||
463 | time_diff = timestamp - time_begin; | 474 | time_diff = timestamp - time_begin; |
464 | return update_kvm_event(event, vcpu_record->vcpu_id, time_diff); | 475 | return update_kvm_event(event, vcpu, time_diff); |
465 | } | 476 | } |
466 | 477 | ||
467 | static | 478 | static |
@@ -486,7 +497,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, | |||
486 | return thread->priv; | 497 | return thread->priv; |
487 | } | 498 | } |
488 | 499 | ||
489 | static bool handle_kvm_event(struct perf_kvm *kvm, | 500 | static bool handle_kvm_event(struct perf_kvm_stat *kvm, |
490 | struct thread *thread, | 501 | struct thread *thread, |
491 | struct perf_evsel *evsel, | 502 | struct perf_evsel *evsel, |
492 | struct perf_sample *sample) | 503 | struct perf_sample *sample) |
@@ -498,6 +509,11 @@ static bool handle_kvm_event(struct perf_kvm *kvm, | |||
498 | if (!vcpu_record) | 509 | if (!vcpu_record) |
499 | return true; | 510 | return true; |
500 | 511 | ||
512 | /* only process events for vcpus user cares about */ | ||
513 | if ((kvm->trace_vcpu != -1) && | ||
514 | (kvm->trace_vcpu != vcpu_record->vcpu_id)) | ||
515 | return true; | ||
516 | |||
501 | if (kvm->events_ops->is_begin_event(evsel, sample, &key)) | 517 | if (kvm->events_ops->is_begin_event(evsel, sample, &key)) |
502 | return handle_begin_event(kvm, vcpu_record, &key, sample->time); | 518 | return handle_begin_event(kvm, vcpu_record, &key, sample->time); |
503 | 519 | ||
@@ -541,7 +557,7 @@ static struct kvm_event_key keys[] = { | |||
541 | { NULL, NULL } | 557 | { NULL, NULL } |
542 | }; | 558 | }; |
543 | 559 | ||
544 | static bool select_key(struct perf_kvm *kvm) | 560 | static bool select_key(struct perf_kvm_stat *kvm) |
545 | { | 561 | { |
546 | int i; | 562 | int i; |
547 | 563 | ||
@@ -577,7 +593,8 @@ static void insert_to_result(struct rb_root *result, struct kvm_event *event, | |||
577 | rb_insert_color(&event->rb, result); | 593 | rb_insert_color(&event->rb, result); |
578 | } | 594 | } |
579 | 595 | ||
580 | static void update_total_count(struct perf_kvm *kvm, struct kvm_event *event) | 596 | static void |
597 | update_total_count(struct perf_kvm_stat *kvm, struct kvm_event *event) | ||
581 | { | 598 | { |
582 | int vcpu = kvm->trace_vcpu; | 599 | int vcpu = kvm->trace_vcpu; |
583 | 600 | ||
@@ -590,19 +607,21 @@ static bool event_is_valid(struct kvm_event *event, int vcpu) | |||
590 | return !!get_event_count(event, vcpu); | 607 | return !!get_event_count(event, vcpu); |
591 | } | 608 | } |
592 | 609 | ||
593 | static void sort_result(struct perf_kvm *kvm) | 610 | static void sort_result(struct perf_kvm_stat *kvm) |
594 | { | 611 | { |
595 | unsigned int i; | 612 | unsigned int i; |
596 | int vcpu = kvm->trace_vcpu; | 613 | int vcpu = kvm->trace_vcpu; |
597 | struct kvm_event *event; | 614 | struct kvm_event *event; |
598 | 615 | ||
599 | for (i = 0; i < EVENTS_CACHE_SIZE; i++) | 616 | for (i = 0; i < EVENTS_CACHE_SIZE; i++) { |
600 | list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry) | 617 | list_for_each_entry(event, &kvm->kvm_events_cache[i], hash_entry) { |
601 | if (event_is_valid(event, vcpu)) { | 618 | if (event_is_valid(event, vcpu)) { |
602 | update_total_count(kvm, event); | 619 | update_total_count(kvm, event); |
603 | insert_to_result(&kvm->result, event, | 620 | insert_to_result(&kvm->result, event, |
604 | kvm->compare, vcpu); | 621 | kvm->compare, vcpu); |
605 | } | 622 | } |
623 | } | ||
624 | } | ||
606 | } | 625 | } |
607 | 626 | ||
608 | /* returns left most element of result, and erase it */ | 627 | /* returns left most element of result, and erase it */ |
@@ -627,7 +646,7 @@ static void print_vcpu_info(int vcpu) | |||
627 | pr_info("VCPU %d:\n\n", vcpu); | 646 | pr_info("VCPU %d:\n\n", vcpu); |
628 | } | 647 | } |
629 | 648 | ||
630 | static void print_result(struct perf_kvm *kvm) | 649 | static void print_result(struct perf_kvm_stat *kvm) |
631 | { | 650 | { |
632 | char decode[20]; | 651 | char decode[20]; |
633 | struct kvm_event *event; | 652 | struct kvm_event *event; |
@@ -659,8 +678,8 @@ static void print_result(struct perf_kvm *kvm) | |||
659 | pr_info("\n"); | 678 | pr_info("\n"); |
660 | } | 679 | } |
661 | 680 | ||
662 | pr_info("\nTotal Samples:%lld, Total events handled time:%.2fus.\n\n", | 681 | pr_info("\nTotal Samples:%" PRIu64 ", Total events handled time:%.2fus.\n\n", |
663 | (unsigned long long)kvm->total_count, kvm->total_time / 1e3); | 682 | kvm->total_count, kvm->total_time / 1e3); |
664 | } | 683 | } |
665 | 684 | ||
666 | static int process_sample_event(struct perf_tool *tool, | 685 | static int process_sample_event(struct perf_tool *tool, |
@@ -670,7 +689,8 @@ static int process_sample_event(struct perf_tool *tool, | |||
670 | struct machine *machine) | 689 | struct machine *machine) |
671 | { | 690 | { |
672 | struct thread *thread = machine__findnew_thread(machine, sample->tid); | 691 | struct thread *thread = machine__findnew_thread(machine, sample->tid); |
673 | struct perf_kvm *kvm = container_of(tool, struct perf_kvm, tool); | 692 | struct perf_kvm_stat *kvm = container_of(tool, struct perf_kvm_stat, |
693 | tool); | ||
674 | 694 | ||
675 | if (thread == NULL) { | 695 | if (thread == NULL) { |
676 | pr_debug("problem processing %d event, skipping it.\n", | 696 | pr_debug("problem processing %d event, skipping it.\n", |
@@ -701,7 +721,7 @@ static int get_cpu_isa(struct perf_session *session) | |||
701 | return isa; | 721 | return isa; |
702 | } | 722 | } |
703 | 723 | ||
704 | static int read_events(struct perf_kvm *kvm) | 724 | static int read_events(struct perf_kvm_stat *kvm) |
705 | { | 725 | { |
706 | int ret; | 726 | int ret; |
707 | 727 | ||
@@ -750,7 +770,7 @@ static bool verify_vcpu(int vcpu) | |||
750 | return true; | 770 | return true; |
751 | } | 771 | } |
752 | 772 | ||
753 | static int kvm_events_report_vcpu(struct perf_kvm *kvm) | 773 | static int kvm_events_report_vcpu(struct perf_kvm_stat *kvm) |
754 | { | 774 | { |
755 | int ret = -EINVAL; | 775 | int ret = -EINVAL; |
756 | int vcpu = kvm->trace_vcpu; | 776 | int vcpu = kvm->trace_vcpu; |
@@ -798,7 +818,8 @@ static const char * const record_args[] = { | |||
798 | _p; \ | 818 | _p; \ |
799 | }) | 819 | }) |
800 | 820 | ||
801 | static int kvm_events_record(struct perf_kvm *kvm, int argc, const char **argv) | 821 | static int |
822 | kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | ||
802 | { | 823 | { |
803 | unsigned int rec_argc, i, j; | 824 | unsigned int rec_argc, i, j; |
804 | const char **rec_argv; | 825 | const char **rec_argv; |
@@ -821,7 +842,8 @@ static int kvm_events_record(struct perf_kvm *kvm, int argc, const char **argv) | |||
821 | return cmd_record(i, rec_argv, NULL); | 842 | return cmd_record(i, rec_argv, NULL); |
822 | } | 843 | } |
823 | 844 | ||
824 | static int kvm_events_report(struct perf_kvm *kvm, int argc, const char **argv) | 845 | static int |
846 | kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | ||
825 | { | 847 | { |
826 | const struct option kvm_events_report_options[] = { | 848 | const struct option kvm_events_report_options[] = { |
827 | OPT_STRING(0, "event", &kvm->report_event, "report event", | 849 | OPT_STRING(0, "event", &kvm->report_event, "report event", |
@@ -864,24 +886,37 @@ static void print_kvm_stat_usage(void) | |||
864 | printf("\nOtherwise, it is the alias of 'perf stat':\n"); | 886 | printf("\nOtherwise, it is the alias of 'perf stat':\n"); |
865 | } | 887 | } |
866 | 888 | ||
867 | static int kvm_cmd_stat(struct perf_kvm *kvm, int argc, const char **argv) | 889 | static int kvm_cmd_stat(const char *file_name, int argc, const char **argv) |
868 | { | 890 | { |
891 | struct perf_kvm_stat kvm = { | ||
892 | .file_name = file_name, | ||
893 | |||
894 | .trace_vcpu = -1, | ||
895 | .report_event = "vmexit", | ||
896 | .sort_key = "sample", | ||
897 | |||
898 | .exit_reasons = svm_exit_reasons, | ||
899 | .exit_reasons_size = ARRAY_SIZE(svm_exit_reasons), | ||
900 | .exit_reasons_isa = "SVM", | ||
901 | }; | ||
902 | |||
869 | if (argc == 1) { | 903 | if (argc == 1) { |
870 | print_kvm_stat_usage(); | 904 | print_kvm_stat_usage(); |
871 | goto perf_stat; | 905 | goto perf_stat; |
872 | } | 906 | } |
873 | 907 | ||
874 | if (!strncmp(argv[1], "rec", 3)) | 908 | if (!strncmp(argv[1], "rec", 3)) |
875 | return kvm_events_record(kvm, argc - 1, argv + 1); | 909 | return kvm_events_record(&kvm, argc - 1, argv + 1); |
876 | 910 | ||
877 | if (!strncmp(argv[1], "rep", 3)) | 911 | if (!strncmp(argv[1], "rep", 3)) |
878 | return kvm_events_report(kvm, argc - 1 , argv + 1); | 912 | return kvm_events_report(&kvm, argc - 1 , argv + 1); |
879 | 913 | ||
880 | perf_stat: | 914 | perf_stat: |
881 | return cmd_stat(argc, argv, NULL); | 915 | return cmd_stat(argc, argv, NULL); |
882 | } | 916 | } |
917 | #endif | ||
883 | 918 | ||
884 | static int __cmd_record(struct perf_kvm *kvm, int argc, const char **argv) | 919 | static int __cmd_record(const char *file_name, int argc, const char **argv) |
885 | { | 920 | { |
886 | int rec_argc, i = 0, j; | 921 | int rec_argc, i = 0, j; |
887 | const char **rec_argv; | 922 | const char **rec_argv; |
@@ -890,7 +925,7 @@ static int __cmd_record(struct perf_kvm *kvm, int argc, const char **argv) | |||
890 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 925 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
891 | rec_argv[i++] = strdup("record"); | 926 | rec_argv[i++] = strdup("record"); |
892 | rec_argv[i++] = strdup("-o"); | 927 | rec_argv[i++] = strdup("-o"); |
893 | rec_argv[i++] = strdup(kvm->file_name); | 928 | rec_argv[i++] = strdup(file_name); |
894 | for (j = 1; j < argc; j++, i++) | 929 | for (j = 1; j < argc; j++, i++) |
895 | rec_argv[i] = argv[j]; | 930 | rec_argv[i] = argv[j]; |
896 | 931 | ||
@@ -899,7 +934,7 @@ static int __cmd_record(struct perf_kvm *kvm, int argc, const char **argv) | |||
899 | return cmd_record(i, rec_argv, NULL); | 934 | return cmd_record(i, rec_argv, NULL); |
900 | } | 935 | } |
901 | 936 | ||
902 | static int __cmd_report(struct perf_kvm *kvm, int argc, const char **argv) | 937 | static int __cmd_report(const char *file_name, int argc, const char **argv) |
903 | { | 938 | { |
904 | int rec_argc, i = 0, j; | 939 | int rec_argc, i = 0, j; |
905 | const char **rec_argv; | 940 | const char **rec_argv; |
@@ -908,7 +943,7 @@ static int __cmd_report(struct perf_kvm *kvm, int argc, const char **argv) | |||
908 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 943 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
909 | rec_argv[i++] = strdup("report"); | 944 | rec_argv[i++] = strdup("report"); |
910 | rec_argv[i++] = strdup("-i"); | 945 | rec_argv[i++] = strdup("-i"); |
911 | rec_argv[i++] = strdup(kvm->file_name); | 946 | rec_argv[i++] = strdup(file_name); |
912 | for (j = 1; j < argc; j++, i++) | 947 | for (j = 1; j < argc; j++, i++) |
913 | rec_argv[i] = argv[j]; | 948 | rec_argv[i] = argv[j]; |
914 | 949 | ||
@@ -917,7 +952,8 @@ static int __cmd_report(struct perf_kvm *kvm, int argc, const char **argv) | |||
917 | return cmd_report(i, rec_argv, NULL); | 952 | return cmd_report(i, rec_argv, NULL); |
918 | } | 953 | } |
919 | 954 | ||
920 | static int __cmd_buildid_list(struct perf_kvm *kvm, int argc, const char **argv) | 955 | static int |
956 | __cmd_buildid_list(const char *file_name, int argc, const char **argv) | ||
921 | { | 957 | { |
922 | int rec_argc, i = 0, j; | 958 | int rec_argc, i = 0, j; |
923 | const char **rec_argv; | 959 | const char **rec_argv; |
@@ -926,7 +962,7 @@ static int __cmd_buildid_list(struct perf_kvm *kvm, int argc, const char **argv) | |||
926 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 962 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
927 | rec_argv[i++] = strdup("buildid-list"); | 963 | rec_argv[i++] = strdup("buildid-list"); |
928 | rec_argv[i++] = strdup("-i"); | 964 | rec_argv[i++] = strdup("-i"); |
929 | rec_argv[i++] = strdup(kvm->file_name); | 965 | rec_argv[i++] = strdup(file_name); |
930 | for (j = 1; j < argc; j++, i++) | 966 | for (j = 1; j < argc; j++, i++) |
931 | rec_argv[i] = argv[j]; | 967 | rec_argv[i] = argv[j]; |
932 | 968 | ||
@@ -937,20 +973,11 @@ static int __cmd_buildid_list(struct perf_kvm *kvm, int argc, const char **argv) | |||
937 | 973 | ||
938 | int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) | 974 | int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) |
939 | { | 975 | { |
940 | struct perf_kvm kvm = { | 976 | const char *file_name = NULL; |
941 | .trace_vcpu = -1, | ||
942 | .report_event = "vmexit", | ||
943 | .sort_key = "sample", | ||
944 | |||
945 | .exit_reasons = svm_exit_reasons, | ||
946 | .exit_reasons_size = ARRAY_SIZE(svm_exit_reasons), | ||
947 | .exit_reasons_isa = "SVM", | ||
948 | }; | ||
949 | |||
950 | const struct option kvm_options[] = { | 977 | const struct option kvm_options[] = { |
951 | OPT_STRING('i', "input", &kvm.file_name, "file", | 978 | OPT_STRING('i', "input", &file_name, "file", |
952 | "Input file name"), | 979 | "Input file name"), |
953 | OPT_STRING('o', "output", &kvm.file_name, "file", | 980 | OPT_STRING('o', "output", &file_name, "file", |
954 | "Output file name"), | 981 | "Output file name"), |
955 | OPT_BOOLEAN(0, "guest", &perf_guest, | 982 | OPT_BOOLEAN(0, "guest", &perf_guest, |
956 | "Collect guest os data"), | 983 | "Collect guest os data"), |
@@ -985,32 +1012,34 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused) | |||
985 | if (!perf_host) | 1012 | if (!perf_host) |
986 | perf_guest = 1; | 1013 | perf_guest = 1; |
987 | 1014 | ||
988 | if (!kvm.file_name) { | 1015 | if (!file_name) { |
989 | if (perf_host && !perf_guest) | 1016 | if (perf_host && !perf_guest) |
990 | kvm.file_name = strdup("perf.data.host"); | 1017 | file_name = strdup("perf.data.host"); |
991 | else if (!perf_host && perf_guest) | 1018 | else if (!perf_host && perf_guest) |
992 | kvm.file_name = strdup("perf.data.guest"); | 1019 | file_name = strdup("perf.data.guest"); |
993 | else | 1020 | else |
994 | kvm.file_name = strdup("perf.data.kvm"); | 1021 | file_name = strdup("perf.data.kvm"); |
995 | 1022 | ||
996 | if (!kvm.file_name) { | 1023 | if (!file_name) { |
997 | pr_err("Failed to allocate memory for filename\n"); | 1024 | pr_err("Failed to allocate memory for filename\n"); |
998 | return -ENOMEM; | 1025 | return -ENOMEM; |
999 | } | 1026 | } |
1000 | } | 1027 | } |
1001 | 1028 | ||
1002 | if (!strncmp(argv[0], "rec", 3)) | 1029 | if (!strncmp(argv[0], "rec", 3)) |
1003 | return __cmd_record(&kvm, argc, argv); | 1030 | return __cmd_record(file_name, argc, argv); |
1004 | else if (!strncmp(argv[0], "rep", 3)) | 1031 | else if (!strncmp(argv[0], "rep", 3)) |
1005 | return __cmd_report(&kvm, argc, argv); | 1032 | return __cmd_report(file_name, argc, argv); |
1006 | else if (!strncmp(argv[0], "diff", 4)) | 1033 | else if (!strncmp(argv[0], "diff", 4)) |
1007 | return cmd_diff(argc, argv, NULL); | 1034 | return cmd_diff(argc, argv, NULL); |
1008 | else if (!strncmp(argv[0], "top", 3)) | 1035 | else if (!strncmp(argv[0], "top", 3)) |
1009 | return cmd_top(argc, argv, NULL); | 1036 | return cmd_top(argc, argv, NULL); |
1010 | else if (!strncmp(argv[0], "buildid-list", 12)) | 1037 | else if (!strncmp(argv[0], "buildid-list", 12)) |
1011 | return __cmd_buildid_list(&kvm, argc, argv); | 1038 | return __cmd_buildid_list(file_name, argc, argv); |
1039 | #if defined(__i386__) || defined(__x86_64__) | ||
1012 | else if (!strncmp(argv[0], "stat", 4)) | 1040 | else if (!strncmp(argv[0], "stat", 4)) |
1013 | return kvm_cmd_stat(&kvm, argc, argv); | 1041 | return kvm_cmd_stat(file_name, argc, argv); |
1042 | #endif | ||
1014 | else | 1043 | else |
1015 | usage_with_options(kvm_usage, kvm_options); | 1044 | usage_with_options(kvm_usage, kvm_options); |
1016 | 1045 | ||
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 6f5f328157aa..425830069749 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
@@ -335,8 +335,6 @@ alloc_failed: | |||
335 | return NULL; | 335 | return NULL; |
336 | } | 336 | } |
337 | 337 | ||
338 | static const char *input_name; | ||
339 | |||
340 | struct trace_lock_handler { | 338 | struct trace_lock_handler { |
341 | int (*acquire_event)(struct perf_evsel *evsel, | 339 | int (*acquire_event)(struct perf_evsel *evsel, |
342 | struct perf_sample *sample); | 340 | struct perf_sample *sample); |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index e9231659754d..774c90713a53 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -31,6 +31,38 @@ | |||
31 | #include <sched.h> | 31 | #include <sched.h> |
32 | #include <sys/mman.h> | 32 | #include <sys/mman.h> |
33 | 33 | ||
34 | #ifndef HAVE_ON_EXIT | ||
35 | #ifndef ATEXIT_MAX | ||
36 | #define ATEXIT_MAX 32 | ||
37 | #endif | ||
38 | static int __on_exit_count = 0; | ||
39 | typedef void (*on_exit_func_t) (int, void *); | ||
40 | static on_exit_func_t __on_exit_funcs[ATEXIT_MAX]; | ||
41 | static void *__on_exit_args[ATEXIT_MAX]; | ||
42 | static int __exitcode = 0; | ||
43 | static void __handle_on_exit_funcs(void); | ||
44 | static int on_exit(on_exit_func_t function, void *arg); | ||
45 | #define exit(x) (exit)(__exitcode = (x)) | ||
46 | |||
47 | static int on_exit(on_exit_func_t function, void *arg) | ||
48 | { | ||
49 | if (__on_exit_count == ATEXIT_MAX) | ||
50 | return -ENOMEM; | ||
51 | else if (__on_exit_count == 0) | ||
52 | atexit(__handle_on_exit_funcs); | ||
53 | __on_exit_funcs[__on_exit_count] = function; | ||
54 | __on_exit_args[__on_exit_count++] = arg; | ||
55 | return 0; | ||
56 | } | ||
57 | |||
58 | static void __handle_on_exit_funcs(void) | ||
59 | { | ||
60 | int i; | ||
61 | for (i = 0; i < __on_exit_count; i++) | ||
62 | __on_exit_funcs[i] (__exitcode, __on_exit_args[i]); | ||
63 | } | ||
64 | #endif | ||
65 | |||
34 | enum write_mode_t { | 66 | enum write_mode_t { |
35 | WRITE_FORCE, | 67 | WRITE_FORCE, |
36 | WRITE_APPEND | 68 | WRITE_APPEND |
@@ -192,121 +224,28 @@ static bool perf_evlist__equal(struct perf_evlist *evlist, | |||
192 | 224 | ||
193 | static int perf_record__open(struct perf_record *rec) | 225 | static int perf_record__open(struct perf_record *rec) |
194 | { | 226 | { |
227 | char msg[512]; | ||
195 | struct perf_evsel *pos; | 228 | struct perf_evsel *pos; |
196 | struct perf_evlist *evlist = rec->evlist; | 229 | struct perf_evlist *evlist = rec->evlist; |
197 | struct perf_session *session = rec->session; | 230 | struct perf_session *session = rec->session; |
198 | struct perf_record_opts *opts = &rec->opts; | 231 | struct perf_record_opts *opts = &rec->opts; |
199 | int rc = 0; | 232 | int rc = 0; |
200 | 233 | ||
201 | perf_evlist__config_attrs(evlist, opts); | 234 | perf_evlist__config(evlist, opts); |
202 | |||
203 | if (opts->group) | ||
204 | perf_evlist__set_leader(evlist); | ||
205 | 235 | ||
206 | list_for_each_entry(pos, &evlist->entries, node) { | 236 | list_for_each_entry(pos, &evlist->entries, node) { |
207 | struct perf_event_attr *attr = &pos->attr; | ||
208 | /* | ||
209 | * Check if parse_single_tracepoint_event has already asked for | ||
210 | * PERF_SAMPLE_TIME. | ||
211 | * | ||
212 | * XXX this is kludgy but short term fix for problems introduced by | ||
213 | * eac23d1c that broke 'perf script' by having different sample_types | ||
214 | * when using multiple tracepoint events when we use a perf binary | ||
215 | * that tries to use sample_id_all on an older kernel. | ||
216 | * | ||
217 | * We need to move counter creation to perf_session, support | ||
218 | * different sample_types, etc. | ||
219 | */ | ||
220 | bool time_needed = attr->sample_type & PERF_SAMPLE_TIME; | ||
221 | |||
222 | fallback_missing_features: | ||
223 | if (opts->exclude_guest_missing) | ||
224 | attr->exclude_guest = attr->exclude_host = 0; | ||
225 | retry_sample_id: | ||
226 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; | ||
227 | try_again: | 237 | try_again: |
228 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) { | 238 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) { |
229 | int err = errno; | 239 | if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) { |
230 | |||
231 | if (err == EPERM || err == EACCES) { | ||
232 | ui__error_paranoid(); | ||
233 | rc = -err; | ||
234 | goto out; | ||
235 | } else if (err == ENODEV && opts->target.cpu_list) { | ||
236 | pr_err("No such device - did you specify" | ||
237 | " an out-of-range profile CPU?\n"); | ||
238 | rc = -err; | ||
239 | goto out; | ||
240 | } else if (err == EINVAL) { | ||
241 | if (!opts->exclude_guest_missing && | ||
242 | (attr->exclude_guest || attr->exclude_host)) { | ||
243 | pr_debug("Old kernel, cannot exclude " | ||
244 | "guest or host samples.\n"); | ||
245 | opts->exclude_guest_missing = true; | ||
246 | goto fallback_missing_features; | ||
247 | } else if (!opts->sample_id_all_missing) { | ||
248 | /* | ||
249 | * Old kernel, no attr->sample_id_type_all field | ||
250 | */ | ||
251 | opts->sample_id_all_missing = true; | ||
252 | if (!opts->sample_time && !opts->raw_samples && !time_needed) | ||
253 | attr->sample_type &= ~PERF_SAMPLE_TIME; | ||
254 | |||
255 | goto retry_sample_id; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * If it's cycles then fall back to hrtimer | ||
261 | * based cpu-clock-tick sw counter, which | ||
262 | * is always available even if no PMU support. | ||
263 | * | ||
264 | * PPC returns ENXIO until 2.6.37 (behavior changed | ||
265 | * with commit b0a873e). | ||
266 | */ | ||
267 | if ((err == ENOENT || err == ENXIO) | ||
268 | && attr->type == PERF_TYPE_HARDWARE | ||
269 | && attr->config == PERF_COUNT_HW_CPU_CYCLES) { | ||
270 | |||
271 | if (verbose) | 240 | if (verbose) |
272 | ui__warning("The cycles event is not supported, " | 241 | ui__warning("%s\n", msg); |
273 | "trying to fall back to cpu-clock-ticks\n"); | ||
274 | attr->type = PERF_TYPE_SOFTWARE; | ||
275 | attr->config = PERF_COUNT_SW_CPU_CLOCK; | ||
276 | if (pos->name) { | ||
277 | free(pos->name); | ||
278 | pos->name = NULL; | ||
279 | } | ||
280 | goto try_again; | 242 | goto try_again; |
281 | } | 243 | } |
282 | 244 | ||
283 | if (err == ENOENT) { | 245 | rc = -errno; |
284 | ui__error("The %s event is not supported.\n", | 246 | perf_evsel__open_strerror(pos, &opts->target, |
285 | perf_evsel__name(pos)); | 247 | errno, msg, sizeof(msg)); |
286 | rc = -err; | 248 | ui__error("%s\n", msg); |
287 | goto out; | ||
288 | } | ||
289 | |||
290 | printf("\n"); | ||
291 | error("sys_perf_event_open() syscall returned with %d " | ||
292 | "(%s) for event %s. /bin/dmesg may provide " | ||
293 | "additional information.\n", | ||
294 | err, strerror(err), perf_evsel__name(pos)); | ||
295 | |||
296 | #if defined(__i386__) || defined(__x86_64__) | ||
297 | if (attr->type == PERF_TYPE_HARDWARE && | ||
298 | err == EOPNOTSUPP) { | ||
299 | pr_err("No hardware sampling interrupt available." | ||
300 | " No APIC? If so then you can boot the kernel" | ||
301 | " with the \"lapic\" boot parameter to" | ||
302 | " force-enable it.\n"); | ||
303 | rc = -err; | ||
304 | goto out; | ||
305 | } | ||
306 | #endif | ||
307 | |||
308 | pr_err("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | ||
309 | rc = -err; | ||
310 | goto out; | 249 | goto out; |
311 | } | 250 | } |
312 | } | 251 | } |
@@ -326,7 +265,8 @@ try_again: | |||
326 | "or try again with a smaller value of -m/--mmap_pages.\n" | 265 | "or try again with a smaller value of -m/--mmap_pages.\n" |
327 | "(current value: %d)\n", opts->mmap_pages); | 266 | "(current value: %d)\n", opts->mmap_pages); |
328 | rc = -errno; | 267 | rc = -errno; |
329 | } else if (!is_power_of_2(opts->mmap_pages)) { | 268 | } else if (!is_power_of_2(opts->mmap_pages) && |
269 | (opts->mmap_pages != UINT_MAX)) { | ||
330 | pr_err("--mmap_pages/-m value must be a power of two."); | 270 | pr_err("--mmap_pages/-m value must be a power of two."); |
331 | rc = -EINVAL; | 271 | rc = -EINVAL; |
332 | } else { | 272 | } else { |
@@ -388,10 +328,6 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data) | |||
388 | { | 328 | { |
389 | int err; | 329 | int err; |
390 | struct perf_tool *tool = data; | 330 | struct perf_tool *tool = data; |
391 | |||
392 | if (machine__is_host(machine)) | ||
393 | return; | ||
394 | |||
395 | /* | 331 | /* |
396 | *As for guest kernel when processing subcommand record&report, | 332 | *As for guest kernel when processing subcommand record&report, |
397 | *we arrange module mmap prior to guest kernel mmap and trigger | 333 | *we arrange module mmap prior to guest kernel mmap and trigger |
@@ -460,6 +396,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
460 | struct perf_evlist *evsel_list = rec->evlist; | 396 | struct perf_evlist *evsel_list = rec->evlist; |
461 | const char *output_name = rec->output_name; | 397 | const char *output_name = rec->output_name; |
462 | struct perf_session *session; | 398 | struct perf_session *session; |
399 | bool disabled = false; | ||
463 | 400 | ||
464 | rec->progname = argv[0]; | 401 | rec->progname = argv[0]; |
465 | 402 | ||
@@ -549,6 +486,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
549 | goto out_delete_session; | 486 | goto out_delete_session; |
550 | } | 487 | } |
551 | 488 | ||
489 | if (!evsel_list->nr_groups) | ||
490 | perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); | ||
491 | |||
552 | /* | 492 | /* |
553 | * perf_session__delete(session) will be called at perf_record__exit() | 493 | * perf_session__delete(session) will be called at perf_record__exit() |
554 | */ | 494 | */ |
@@ -575,12 +515,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
575 | 515 | ||
576 | rec->post_processing_offset = lseek(output, 0, SEEK_CUR); | 516 | rec->post_processing_offset = lseek(output, 0, SEEK_CUR); |
577 | 517 | ||
578 | machine = perf_session__find_host_machine(session); | 518 | machine = &session->machines.host; |
579 | if (!machine) { | ||
580 | pr_err("Couldn't find native kernel information.\n"); | ||
581 | err = -1; | ||
582 | goto out_delete_session; | ||
583 | } | ||
584 | 519 | ||
585 | if (opts->pipe_output) { | 520 | if (opts->pipe_output) { |
586 | err = perf_event__synthesize_attrs(tool, session, | 521 | err = perf_event__synthesize_attrs(tool, session, |
@@ -633,9 +568,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
633 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" | 568 | "Symbol resolution may be skewed if relocation was used (e.g. kexec).\n" |
634 | "Check /proc/modules permission or run as root.\n"); | 569 | "Check /proc/modules permission or run as root.\n"); |
635 | 570 | ||
636 | if (perf_guest) | 571 | if (perf_guest) { |
637 | perf_session__process_machines(session, tool, | 572 | machines__process_guests(&session->machines, |
638 | perf_event__synthesize_guest_os); | 573 | perf_event__synthesize_guest_os, tool); |
574 | } | ||
639 | 575 | ||
640 | if (!opts->target.system_wide) | 576 | if (!opts->target.system_wide) |
641 | err = perf_event__synthesize_thread_map(tool, evsel_list->threads, | 577 | err = perf_event__synthesize_thread_map(tool, evsel_list->threads, |
@@ -659,7 +595,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
659 | } | 595 | } |
660 | } | 596 | } |
661 | 597 | ||
662 | perf_evlist__enable(evsel_list); | 598 | /* |
599 | * When perf is starting the traced process, all the events | ||
600 | * (apart from group members) have enable_on_exec=1 set, | ||
601 | * so don't spoil it by prematurely enabling them. | ||
602 | */ | ||
603 | if (!perf_target__none(&opts->target)) | ||
604 | perf_evlist__enable(evsel_list); | ||
663 | 605 | ||
664 | /* | 606 | /* |
665 | * Let the child rip | 607 | * Let the child rip |
@@ -682,8 +624,15 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
682 | waking++; | 624 | waking++; |
683 | } | 625 | } |
684 | 626 | ||
685 | if (done) | 627 | /* |
628 | * When perf is starting the traced process, at the end events | ||
629 | * die with the process and we wait for that. Thus no need to | ||
630 | * disable events in this case. | ||
631 | */ | ||
632 | if (done && !disabled && !perf_target__none(&opts->target)) { | ||
686 | perf_evlist__disable(evsel_list); | 633 | perf_evlist__disable(evsel_list); |
634 | disabled = true; | ||
635 | } | ||
687 | } | 636 | } |
688 | 637 | ||
689 | if (quiet || signr == SIGUSR1) | 638 | if (quiet || signr == SIGUSR1) |
@@ -819,11 +768,10 @@ static int get_stack_size(char *str, unsigned long *_size) | |||
819 | } | 768 | } |
820 | #endif /* LIBUNWIND_SUPPORT */ | 769 | #endif /* LIBUNWIND_SUPPORT */ |
821 | 770 | ||
822 | static int | 771 | int record_parse_callchain_opt(const struct option *opt, |
823 | parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, | 772 | const char *arg, int unset) |
824 | int unset) | ||
825 | { | 773 | { |
826 | struct perf_record *rec = (struct perf_record *)opt->value; | 774 | struct perf_record_opts *opts = opt->value; |
827 | char *tok, *name, *saveptr = NULL; | 775 | char *tok, *name, *saveptr = NULL; |
828 | char *buf; | 776 | char *buf; |
829 | int ret = -1; | 777 | int ret = -1; |
@@ -849,7 +797,7 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, | |||
849 | /* Framepointer style */ | 797 | /* Framepointer style */ |
850 | if (!strncmp(name, "fp", sizeof("fp"))) { | 798 | if (!strncmp(name, "fp", sizeof("fp"))) { |
851 | if (!strtok_r(NULL, ",", &saveptr)) { | 799 | if (!strtok_r(NULL, ",", &saveptr)) { |
852 | rec->opts.call_graph = CALLCHAIN_FP; | 800 | opts->call_graph = CALLCHAIN_FP; |
853 | ret = 0; | 801 | ret = 0; |
854 | } else | 802 | } else |
855 | pr_err("callchain: No more arguments " | 803 | pr_err("callchain: No more arguments " |
@@ -862,20 +810,20 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, | |||
862 | const unsigned long default_stack_dump_size = 8192; | 810 | const unsigned long default_stack_dump_size = 8192; |
863 | 811 | ||
864 | ret = 0; | 812 | ret = 0; |
865 | rec->opts.call_graph = CALLCHAIN_DWARF; | 813 | opts->call_graph = CALLCHAIN_DWARF; |
866 | rec->opts.stack_dump_size = default_stack_dump_size; | 814 | opts->stack_dump_size = default_stack_dump_size; |
867 | 815 | ||
868 | tok = strtok_r(NULL, ",", &saveptr); | 816 | tok = strtok_r(NULL, ",", &saveptr); |
869 | if (tok) { | 817 | if (tok) { |
870 | unsigned long size = 0; | 818 | unsigned long size = 0; |
871 | 819 | ||
872 | ret = get_stack_size(tok, &size); | 820 | ret = get_stack_size(tok, &size); |
873 | rec->opts.stack_dump_size = size; | 821 | opts->stack_dump_size = size; |
874 | } | 822 | } |
875 | 823 | ||
876 | if (!ret) | 824 | if (!ret) |
877 | pr_debug("callchain: stack dump size %d\n", | 825 | pr_debug("callchain: stack dump size %d\n", |
878 | rec->opts.stack_dump_size); | 826 | opts->stack_dump_size); |
879 | #endif /* LIBUNWIND_SUPPORT */ | 827 | #endif /* LIBUNWIND_SUPPORT */ |
880 | } else { | 828 | } else { |
881 | pr_err("callchain: Unknown -g option " | 829 | pr_err("callchain: Unknown -g option " |
@@ -888,7 +836,7 @@ parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg, | |||
888 | free(buf); | 836 | free(buf); |
889 | 837 | ||
890 | if (!ret) | 838 | if (!ret) |
891 | pr_debug("callchain: type %d\n", rec->opts.call_graph); | 839 | pr_debug("callchain: type %d\n", opts->call_graph); |
892 | 840 | ||
893 | return ret; | 841 | return ret; |
894 | } | 842 | } |
@@ -926,9 +874,9 @@ static struct perf_record record = { | |||
926 | #define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " | 874 | #define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " |
927 | 875 | ||
928 | #ifdef LIBUNWIND_SUPPORT | 876 | #ifdef LIBUNWIND_SUPPORT |
929 | static const char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; | 877 | const char record_callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; |
930 | #else | 878 | #else |
931 | static const char callchain_help[] = CALLCHAIN_HELP "[fp]"; | 879 | const char record_callchain_help[] = CALLCHAIN_HELP "[fp]"; |
932 | #endif | 880 | #endif |
933 | 881 | ||
934 | /* | 882 | /* |
@@ -972,9 +920,9 @@ const struct option record_options[] = { | |||
972 | "number of mmap data pages"), | 920 | "number of mmap data pages"), |
973 | OPT_BOOLEAN(0, "group", &record.opts.group, | 921 | OPT_BOOLEAN(0, "group", &record.opts.group, |
974 | "put the counters into a counter group"), | 922 | "put the counters into a counter group"), |
975 | OPT_CALLBACK_DEFAULT('g', "call-graph", &record, "mode[,dump_size]", | 923 | OPT_CALLBACK_DEFAULT('g', "call-graph", &record.opts, |
976 | callchain_help, &parse_callchain_opt, | 924 | "mode[,dump_size]", record_callchain_help, |
977 | "fp"), | 925 | &record_parse_callchain_opt, "fp"), |
978 | OPT_INCR('v', "verbose", &verbose, | 926 | OPT_INCR('v', "verbose", &verbose, |
979 | "be more verbose (show counter open errors, etc)"), | 927 | "be more verbose (show counter open errors, etc)"), |
980 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), | 928 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index a61725d89d3e..96b5a7fee4bb 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include "builtin.h" | 8 | #include "builtin.h" |
9 | 9 | ||
10 | #include "util/util.h" | 10 | #include "util/util.h" |
11 | #include "util/cache.h" | ||
11 | 12 | ||
12 | #include "util/annotate.h" | 13 | #include "util/annotate.h" |
13 | #include "util/color.h" | 14 | #include "util/color.h" |
@@ -33,13 +34,13 @@ | |||
33 | #include "util/thread.h" | 34 | #include "util/thread.h" |
34 | #include "util/sort.h" | 35 | #include "util/sort.h" |
35 | #include "util/hist.h" | 36 | #include "util/hist.h" |
37 | #include "arch/common.h" | ||
36 | 38 | ||
37 | #include <linux/bitmap.h> | 39 | #include <linux/bitmap.h> |
38 | 40 | ||
39 | struct perf_report { | 41 | struct perf_report { |
40 | struct perf_tool tool; | 42 | struct perf_tool tool; |
41 | struct perf_session *session; | 43 | struct perf_session *session; |
42 | char const *input_name; | ||
43 | bool force, use_tui, use_gtk, use_stdio; | 44 | bool force, use_tui, use_gtk, use_stdio; |
44 | bool hide_unresolved; | 45 | bool hide_unresolved; |
45 | bool dont_use_callchains; | 46 | bool dont_use_callchains; |
@@ -54,6 +55,16 @@ struct perf_report { | |||
54 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 55 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
55 | }; | 56 | }; |
56 | 57 | ||
58 | static int perf_report_config(const char *var, const char *value, void *cb) | ||
59 | { | ||
60 | if (!strcmp(var, "report.group")) { | ||
61 | symbol_conf.event_group = perf_config_bool(var, value); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | return perf_default_config(var, value, cb); | ||
66 | } | ||
67 | |||
57 | static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | 68 | static int perf_report__add_branch_hist_entry(struct perf_tool *tool, |
58 | struct addr_location *al, | 69 | struct addr_location *al, |
59 | struct perf_sample *sample, | 70 | struct perf_sample *sample, |
@@ -299,6 +310,21 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, | |||
299 | char unit; | 310 | char unit; |
300 | unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; | 311 | unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; |
301 | u64 nr_events = self->stats.total_period; | 312 | u64 nr_events = self->stats.total_period; |
313 | struct perf_evsel *evsel = hists_to_evsel(self); | ||
314 | char buf[512]; | ||
315 | size_t size = sizeof(buf); | ||
316 | |||
317 | if (symbol_conf.event_group && evsel->nr_members > 1) { | ||
318 | struct perf_evsel *pos; | ||
319 | |||
320 | perf_evsel__group_desc(evsel, buf, size); | ||
321 | evname = buf; | ||
322 | |||
323 | for_each_group_member(pos, evsel) { | ||
324 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
325 | nr_events += pos->hists.stats.total_period; | ||
326 | } | ||
327 | } | ||
302 | 328 | ||
303 | nr_samples = convert_unit(nr_samples, &unit); | 329 | nr_samples = convert_unit(nr_samples, &unit); |
304 | ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit); | 330 | ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit); |
@@ -319,6 +345,10 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist, | |||
319 | struct hists *hists = &pos->hists; | 345 | struct hists *hists = &pos->hists; |
320 | const char *evname = perf_evsel__name(pos); | 346 | const char *evname = perf_evsel__name(pos); |
321 | 347 | ||
348 | if (symbol_conf.event_group && | ||
349 | !perf_evsel__is_group_leader(pos)) | ||
350 | continue; | ||
351 | |||
322 | hists__fprintf_nr_sample_events(hists, evname, stdout); | 352 | hists__fprintf_nr_sample_events(hists, evname, stdout); |
323 | hists__fprintf(hists, true, 0, 0, stdout); | 353 | hists__fprintf(hists, true, 0, 0, stdout); |
324 | fprintf(stdout, "\n\n"); | 354 | fprintf(stdout, "\n\n"); |
@@ -372,7 +402,7 @@ static int __cmd_report(struct perf_report *rep) | |||
372 | if (ret) | 402 | if (ret) |
373 | goto out_delete; | 403 | goto out_delete; |
374 | 404 | ||
375 | kernel_map = session->host_machine.vmlinux_maps[MAP__FUNCTION]; | 405 | kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION]; |
376 | kernel_kmap = map__kmap(kernel_map); | 406 | kernel_kmap = map__kmap(kernel_map); |
377 | if (kernel_map == NULL || | 407 | if (kernel_map == NULL || |
378 | (kernel_map->dso->hit && | 408 | (kernel_map->dso->hit && |
@@ -416,8 +446,16 @@ static int __cmd_report(struct perf_report *rep) | |||
416 | hists->symbol_filter_str = rep->symbol_filter_str; | 446 | hists->symbol_filter_str = rep->symbol_filter_str; |
417 | 447 | ||
418 | hists__collapse_resort(hists); | 448 | hists__collapse_resort(hists); |
419 | hists__output_resort(hists); | ||
420 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 449 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
450 | |||
451 | /* Non-group events are considered as leader */ | ||
452 | if (symbol_conf.event_group && | ||
453 | !perf_evsel__is_group_leader(pos)) { | ||
454 | struct hists *leader_hists = &pos->leader->hists; | ||
455 | |||
456 | hists__match(leader_hists, hists); | ||
457 | hists__link(leader_hists, hists); | ||
458 | } | ||
421 | } | 459 | } |
422 | 460 | ||
423 | if (nr_samples == 0) { | 461 | if (nr_samples == 0) { |
@@ -425,13 +463,25 @@ static int __cmd_report(struct perf_report *rep) | |||
425 | goto out_delete; | 463 | goto out_delete; |
426 | } | 464 | } |
427 | 465 | ||
466 | list_for_each_entry(pos, &session->evlist->entries, node) | ||
467 | hists__output_resort(&pos->hists); | ||
468 | |||
428 | if (use_browser > 0) { | 469 | if (use_browser > 0) { |
429 | if (use_browser == 1) { | 470 | if (use_browser == 1) { |
430 | perf_evlist__tui_browse_hists(session->evlist, help, | 471 | ret = perf_evlist__tui_browse_hists(session->evlist, |
431 | NULL, NULL, 0); | 472 | help, |
473 | NULL, | ||
474 | &session->header.env); | ||
475 | /* | ||
476 | * Usually "ret" is the last pressed key, and we only | ||
477 | * care if the key notifies us to switch data file. | ||
478 | */ | ||
479 | if (ret != K_SWITCH_INPUT_DATA) | ||
480 | ret = 0; | ||
481 | |||
432 | } else if (use_browser == 2) { | 482 | } else if (use_browser == 2) { |
433 | perf_evlist__gtk_browse_hists(session->evlist, help, | 483 | perf_evlist__gtk_browse_hists(session->evlist, help, |
434 | NULL, NULL, 0); | 484 | NULL); |
435 | } | 485 | } |
436 | } else | 486 | } else |
437 | perf_evlist__tty_browse_hists(session->evlist, rep, help); | 487 | perf_evlist__tty_browse_hists(session->evlist, rep, help); |
@@ -556,8 +606,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
556 | .sample = process_sample_event, | 606 | .sample = process_sample_event, |
557 | .mmap = perf_event__process_mmap, | 607 | .mmap = perf_event__process_mmap, |
558 | .comm = perf_event__process_comm, | 608 | .comm = perf_event__process_comm, |
559 | .exit = perf_event__process_task, | 609 | .exit = perf_event__process_exit, |
560 | .fork = perf_event__process_task, | 610 | .fork = perf_event__process_fork, |
561 | .lost = perf_event__process_lost, | 611 | .lost = perf_event__process_lost, |
562 | .read = process_read_event, | 612 | .read = process_read_event, |
563 | .attr = perf_event__process_attr, | 613 | .attr = perf_event__process_attr, |
@@ -570,7 +620,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
570 | .pretty_printing_style = "normal", | 620 | .pretty_printing_style = "normal", |
571 | }; | 621 | }; |
572 | const struct option options[] = { | 622 | const struct option options[] = { |
573 | OPT_STRING('i', "input", &report.input_name, "file", | 623 | OPT_STRING('i', "input", &input_name, "file", |
574 | "input file name"), | 624 | "input file name"), |
575 | OPT_INCR('v', "verbose", &verbose, | 625 | OPT_INCR('v', "verbose", &verbose, |
576 | "be more verbose (show symbol address, etc)"), | 626 | "be more verbose (show symbol address, etc)"), |
@@ -594,8 +644,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
594 | OPT_BOOLEAN(0, "stdio", &report.use_stdio, | 644 | OPT_BOOLEAN(0, "stdio", &report.use_stdio, |
595 | "Use the stdio interface"), | 645 | "Use the stdio interface"), |
596 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 646 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
597 | "sort by key(s): pid, comm, dso, symbol, parent, dso_to," | 647 | "sort by key(s): pid, comm, dso, symbol, parent, cpu, srcline," |
598 | " dso_from, symbol_to, symbol_from, mispredict"), | 648 | " dso_to, dso_from, symbol_to, symbol_from, mispredict"), |
599 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, | 649 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, |
600 | "Show sample percentage for different cpu modes"), | 650 | "Show sample percentage for different cpu modes"), |
601 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 651 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
@@ -637,6 +687,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
637 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 687 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
638 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | 688 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, |
639 | "Show a column with the sum of periods"), | 689 | "Show a column with the sum of periods"), |
690 | OPT_BOOLEAN(0, "group", &symbol_conf.event_group, | ||
691 | "Show event group information together"), | ||
640 | OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "", | 692 | OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "", |
641 | "use branch records for histogram filling", parse_branch_mode), | 693 | "use branch records for histogram filling", parse_branch_mode), |
642 | OPT_STRING(0, "objdump", &objdump_path, "path", | 694 | OPT_STRING(0, "objdump", &objdump_path, "path", |
@@ -644,6 +696,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
644 | OPT_END() | 696 | OPT_END() |
645 | }; | 697 | }; |
646 | 698 | ||
699 | perf_config(perf_report_config, NULL); | ||
700 | |||
647 | argc = parse_options(argc, argv, options, report_usage, 0); | 701 | argc = parse_options(argc, argv, options, report_usage, 0); |
648 | 702 | ||
649 | if (report.use_stdio) | 703 | if (report.use_stdio) |
@@ -656,13 +710,23 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
656 | if (report.inverted_callchain) | 710 | if (report.inverted_callchain) |
657 | callchain_param.order = ORDER_CALLER; | 711 | callchain_param.order = ORDER_CALLER; |
658 | 712 | ||
659 | if (!report.input_name || !strlen(report.input_name)) { | 713 | if (!input_name || !strlen(input_name)) { |
660 | if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) | 714 | if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) |
661 | report.input_name = "-"; | 715 | input_name = "-"; |
662 | else | 716 | else |
663 | report.input_name = "perf.data"; | 717 | input_name = "perf.data"; |
718 | } | ||
719 | |||
720 | if (strcmp(input_name, "-") != 0) | ||
721 | setup_browser(true); | ||
722 | else { | ||
723 | use_browser = 0; | ||
724 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
725 | perf_hpp__init(); | ||
664 | } | 726 | } |
665 | session = perf_session__new(report.input_name, O_RDONLY, | 727 | |
728 | repeat: | ||
729 | session = perf_session__new(input_name, O_RDONLY, | ||
666 | report.force, false, &report.tool); | 730 | report.force, false, &report.tool); |
667 | if (session == NULL) | 731 | if (session == NULL) |
668 | return -ENOMEM; | 732 | return -ENOMEM; |
@@ -687,14 +751,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
687 | 751 | ||
688 | } | 752 | } |
689 | 753 | ||
690 | if (strcmp(report.input_name, "-") != 0) | 754 | if (setup_sorting() < 0) |
691 | setup_browser(true); | 755 | usage_with_options(report_usage, options); |
692 | else { | ||
693 | use_browser = 0; | ||
694 | perf_hpp__init(); | ||
695 | } | ||
696 | |||
697 | setup_sorting(report_usage, options); | ||
698 | 756 | ||
699 | /* | 757 | /* |
700 | * Only in the newt browser we are doing integrated annotation, | 758 | * Only in the newt browser we are doing integrated annotation, |
@@ -762,6 +820,12 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
762 | } | 820 | } |
763 | 821 | ||
764 | ret = __cmd_report(&report); | 822 | ret = __cmd_report(&report); |
823 | if (ret == K_SWITCH_INPUT_DATA) { | ||
824 | perf_session__delete(session); | ||
825 | goto repeat; | ||
826 | } else | ||
827 | ret = 0; | ||
828 | |||
765 | error: | 829 | error: |
766 | perf_session__delete(session); | 830 | perf_session__delete(session); |
767 | return ret; | 831 | return ret; |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 3488ead3b60c..138229439a93 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -120,7 +120,6 @@ struct trace_sched_handler { | |||
120 | 120 | ||
121 | struct perf_sched { | 121 | struct perf_sched { |
122 | struct perf_tool tool; | 122 | struct perf_tool tool; |
123 | const char *input_name; | ||
124 | const char *sort_order; | 123 | const char *sort_order; |
125 | unsigned long nr_tasks; | 124 | unsigned long nr_tasks; |
126 | struct task_desc *pid_to_task[MAX_PID]; | 125 | struct task_desc *pid_to_task[MAX_PID]; |
@@ -1460,7 +1459,7 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy, | |||
1460 | }; | 1459 | }; |
1461 | struct perf_session *session; | 1460 | struct perf_session *session; |
1462 | 1461 | ||
1463 | session = perf_session__new(sched->input_name, O_RDONLY, 0, false, &sched->tool); | 1462 | session = perf_session__new(input_name, O_RDONLY, 0, false, &sched->tool); |
1464 | if (session == NULL) { | 1463 | if (session == NULL) { |
1465 | pr_debug("No Memory for session\n"); | 1464 | pr_debug("No Memory for session\n"); |
1466 | return -1; | 1465 | return -1; |
@@ -1476,9 +1475,9 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy, | |||
1476 | goto out_delete; | 1475 | goto out_delete; |
1477 | } | 1476 | } |
1478 | 1477 | ||
1479 | sched->nr_events = session->hists.stats.nr_events[0]; | 1478 | sched->nr_events = session->stats.nr_events[0]; |
1480 | sched->nr_lost_events = session->hists.stats.total_lost; | 1479 | sched->nr_lost_events = session->stats.total_lost; |
1481 | sched->nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST]; | 1480 | sched->nr_lost_chunks = session->stats.nr_events[PERF_RECORD_LOST]; |
1482 | } | 1481 | } |
1483 | 1482 | ||
1484 | if (destroy) | 1483 | if (destroy) |
@@ -1672,7 +1671,8 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1672 | .sample = perf_sched__process_tracepoint_sample, | 1671 | .sample = perf_sched__process_tracepoint_sample, |
1673 | .comm = perf_event__process_comm, | 1672 | .comm = perf_event__process_comm, |
1674 | .lost = perf_event__process_lost, | 1673 | .lost = perf_event__process_lost, |
1675 | .fork = perf_event__process_task, | 1674 | .exit = perf_event__process_exit, |
1675 | .fork = perf_event__process_fork, | ||
1676 | .ordered_samples = true, | 1676 | .ordered_samples = true, |
1677 | }, | 1677 | }, |
1678 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), | 1678 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), |
@@ -1707,7 +1707,7 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1707 | OPT_END() | 1707 | OPT_END() |
1708 | }; | 1708 | }; |
1709 | const struct option sched_options[] = { | 1709 | const struct option sched_options[] = { |
1710 | OPT_STRING('i', "input", &sched.input_name, "file", | 1710 | OPT_STRING('i', "input", &input_name, "file", |
1711 | "input file name"), | 1711 | "input file name"), |
1712 | OPT_INCR('v', "verbose", &verbose, | 1712 | OPT_INCR('v', "verbose", &verbose, |
1713 | "be more verbose (show symbol address, etc)"), | 1713 | "be more verbose (show symbol address, etc)"), |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index fb9625083a2e..92d4658f56fb 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -520,8 +520,8 @@ static struct perf_tool perf_script = { | |||
520 | .sample = process_sample_event, | 520 | .sample = process_sample_event, |
521 | .mmap = perf_event__process_mmap, | 521 | .mmap = perf_event__process_mmap, |
522 | .comm = perf_event__process_comm, | 522 | .comm = perf_event__process_comm, |
523 | .exit = perf_event__process_task, | 523 | .exit = perf_event__process_exit, |
524 | .fork = perf_event__process_task, | 524 | .fork = perf_event__process_fork, |
525 | .attr = perf_event__process_attr, | 525 | .attr = perf_event__process_attr, |
526 | .event_type = perf_event__process_event_type, | 526 | .event_type = perf_event__process_event_type, |
527 | .tracing_data = perf_event__process_tracing_data, | 527 | .tracing_data = perf_event__process_tracing_data, |
@@ -692,7 +692,7 @@ static int parse_output_fields(const struct option *opt __maybe_unused, | |||
692 | const char *arg, int unset __maybe_unused) | 692 | const char *arg, int unset __maybe_unused) |
693 | { | 693 | { |
694 | char *tok; | 694 | char *tok; |
695 | int i, imax = sizeof(all_output_options) / sizeof(struct output_option); | 695 | int i, imax = ARRAY_SIZE(all_output_options); |
696 | int j; | 696 | int j; |
697 | int rc = 0; | 697 | int rc = 0; |
698 | char *str = strdup(arg); | 698 | char *str = strdup(arg); |
@@ -909,18 +909,6 @@ static const char *ends_with(const char *str, const char *suffix) | |||
909 | return NULL; | 909 | return NULL; |
910 | } | 910 | } |
911 | 911 | ||
912 | static char *ltrim(char *str) | ||
913 | { | ||
914 | int len = strlen(str); | ||
915 | |||
916 | while (len && isspace(*str)) { | ||
917 | len--; | ||
918 | str++; | ||
919 | } | ||
920 | |||
921 | return str; | ||
922 | } | ||
923 | |||
924 | static int read_script_info(struct script_desc *desc, const char *filename) | 912 | static int read_script_info(struct script_desc *desc, const char *filename) |
925 | { | 913 | { |
926 | char line[BUFSIZ], *p; | 914 | char line[BUFSIZ], *p; |
@@ -1030,6 +1018,68 @@ static int list_available_scripts(const struct option *opt __maybe_unused, | |||
1030 | } | 1018 | } |
1031 | 1019 | ||
1032 | /* | 1020 | /* |
1021 | * Some scripts specify the required events in their "xxx-record" file, | ||
1022 | * this function will check if the events in perf.data match those | ||
1023 | * mentioned in the "xxx-record". | ||
1024 | * | ||
1025 | * Fixme: All existing "xxx-record" are all in good formats "-e event ", | ||
1026 | * which is covered well now. And new parsing code should be added to | ||
1027 | * cover the future complexing formats like event groups etc. | ||
1028 | */ | ||
1029 | static int check_ev_match(char *dir_name, char *scriptname, | ||
1030 | struct perf_session *session) | ||
1031 | { | ||
1032 | char filename[MAXPATHLEN], evname[128]; | ||
1033 | char line[BUFSIZ], *p; | ||
1034 | struct perf_evsel *pos; | ||
1035 | int match, len; | ||
1036 | FILE *fp; | ||
1037 | |||
1038 | sprintf(filename, "%s/bin/%s-record", dir_name, scriptname); | ||
1039 | |||
1040 | fp = fopen(filename, "r"); | ||
1041 | if (!fp) | ||
1042 | return -1; | ||
1043 | |||
1044 | while (fgets(line, sizeof(line), fp)) { | ||
1045 | p = ltrim(line); | ||
1046 | if (*p == '#') | ||
1047 | continue; | ||
1048 | |||
1049 | while (strlen(p)) { | ||
1050 | p = strstr(p, "-e"); | ||
1051 | if (!p) | ||
1052 | break; | ||
1053 | |||
1054 | p += 2; | ||
1055 | p = ltrim(p); | ||
1056 | len = strcspn(p, " \t"); | ||
1057 | if (!len) | ||
1058 | break; | ||
1059 | |||
1060 | snprintf(evname, len + 1, "%s", p); | ||
1061 | |||
1062 | match = 0; | ||
1063 | list_for_each_entry(pos, | ||
1064 | &session->evlist->entries, node) { | ||
1065 | if (!strcmp(perf_evsel__name(pos), evname)) { | ||
1066 | match = 1; | ||
1067 | break; | ||
1068 | } | ||
1069 | } | ||
1070 | |||
1071 | if (!match) { | ||
1072 | fclose(fp); | ||
1073 | return -1; | ||
1074 | } | ||
1075 | } | ||
1076 | } | ||
1077 | |||
1078 | fclose(fp); | ||
1079 | return 0; | ||
1080 | } | ||
1081 | |||
1082 | /* | ||
1033 | * Return -1 if none is found, otherwise the actual scripts number. | 1083 | * Return -1 if none is found, otherwise the actual scripts number. |
1034 | * | 1084 | * |
1035 | * Currently the only user of this function is the script browser, which | 1085 | * Currently the only user of this function is the script browser, which |
@@ -1039,17 +1089,23 @@ static int list_available_scripts(const struct option *opt __maybe_unused, | |||
1039 | int find_scripts(char **scripts_array, char **scripts_path_array) | 1089 | int find_scripts(char **scripts_array, char **scripts_path_array) |
1040 | { | 1090 | { |
1041 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; | 1091 | struct dirent *script_next, *lang_next, script_dirent, lang_dirent; |
1042 | char scripts_path[MAXPATHLEN]; | 1092 | char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN]; |
1043 | DIR *scripts_dir, *lang_dir; | 1093 | DIR *scripts_dir, *lang_dir; |
1044 | char lang_path[MAXPATHLEN]; | 1094 | struct perf_session *session; |
1045 | char *temp; | 1095 | char *temp; |
1046 | int i = 0; | 1096 | int i = 0; |
1047 | 1097 | ||
1098 | session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); | ||
1099 | if (!session) | ||
1100 | return -1; | ||
1101 | |||
1048 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); | 1102 | snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path()); |
1049 | 1103 | ||
1050 | scripts_dir = opendir(scripts_path); | 1104 | scripts_dir = opendir(scripts_path); |
1051 | if (!scripts_dir) | 1105 | if (!scripts_dir) { |
1106 | perf_session__delete(session); | ||
1052 | return -1; | 1107 | return -1; |
1108 | } | ||
1053 | 1109 | ||
1054 | for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { | 1110 | for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { |
1055 | snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path, | 1111 | snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path, |
@@ -1077,10 +1133,18 @@ int find_scripts(char **scripts_array, char **scripts_path_array) | |||
1077 | snprintf(scripts_array[i], | 1133 | snprintf(scripts_array[i], |
1078 | (temp - script_dirent.d_name) + 1, | 1134 | (temp - script_dirent.d_name) + 1, |
1079 | "%s", script_dirent.d_name); | 1135 | "%s", script_dirent.d_name); |
1136 | |||
1137 | if (check_ev_match(lang_path, | ||
1138 | scripts_array[i], session)) | ||
1139 | continue; | ||
1140 | |||
1080 | i++; | 1141 | i++; |
1081 | } | 1142 | } |
1143 | closedir(lang_dir); | ||
1082 | } | 1144 | } |
1083 | 1145 | ||
1146 | closedir(scripts_dir); | ||
1147 | perf_session__delete(session); | ||
1084 | return i; | 1148 | return i; |
1085 | } | 1149 | } |
1086 | 1150 | ||
@@ -1175,7 +1239,6 @@ static int have_cmd(int argc, const char **argv) | |||
1175 | int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | 1239 | int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) |
1176 | { | 1240 | { |
1177 | bool show_full_info = false; | 1241 | bool show_full_info = false; |
1178 | const char *input_name = NULL; | ||
1179 | char *rec_script_path = NULL; | 1242 | char *rec_script_path = NULL; |
1180 | char *rep_script_path = NULL; | 1243 | char *rep_script_path = NULL; |
1181 | struct perf_session *session; | 1244 | struct perf_session *session; |
@@ -1412,7 +1475,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1412 | return -1; | 1475 | return -1; |
1413 | } | 1476 | } |
1414 | 1477 | ||
1415 | perf_session__fprintf_info(session, stdout, show_full_info); | 1478 | if (!script_name && !generate_script_lang) |
1479 | perf_session__fprintf_info(session, stdout, show_full_info); | ||
1416 | 1480 | ||
1417 | if (!no_callchain) | 1481 | if (!no_callchain) |
1418 | symbol_conf.use_callchain = true; | 1482 | symbol_conf.use_callchain = true; |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 93b9011fa3e2..99848761f573 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -57,6 +57,7 @@ | |||
57 | #include "util/thread.h" | 57 | #include "util/thread.h" |
58 | #include "util/thread_map.h" | 58 | #include "util/thread_map.h" |
59 | 59 | ||
60 | #include <stdlib.h> | ||
60 | #include <sys/prctl.h> | 61 | #include <sys/prctl.h> |
61 | #include <locale.h> | 62 | #include <locale.h> |
62 | 63 | ||
@@ -64,6 +65,11 @@ | |||
64 | #define CNTR_NOT_SUPPORTED "<not supported>" | 65 | #define CNTR_NOT_SUPPORTED "<not supported>" |
65 | #define CNTR_NOT_COUNTED "<not counted>" | 66 | #define CNTR_NOT_COUNTED "<not counted>" |
66 | 67 | ||
68 | static void print_stat(int argc, const char **argv); | ||
69 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix); | ||
70 | static void print_counter(struct perf_evsel *counter, char *prefix); | ||
71 | static void print_aggr_socket(char *prefix); | ||
72 | |||
67 | static struct perf_evlist *evsel_list; | 73 | static struct perf_evlist *evsel_list; |
68 | 74 | ||
69 | static struct perf_target target = { | 75 | static struct perf_target target = { |
@@ -74,6 +80,7 @@ static int run_count = 1; | |||
74 | static bool no_inherit = false; | 80 | static bool no_inherit = false; |
75 | static bool scale = true; | 81 | static bool scale = true; |
76 | static bool no_aggr = false; | 82 | static bool no_aggr = false; |
83 | static bool aggr_socket = false; | ||
77 | static pid_t child_pid = -1; | 84 | static pid_t child_pid = -1; |
78 | static bool null_run = false; | 85 | static bool null_run = false; |
79 | static int detailed_run = 0; | 86 | static int detailed_run = 0; |
@@ -83,6 +90,12 @@ static const char *csv_sep = NULL; | |||
83 | static bool csv_output = false; | 90 | static bool csv_output = false; |
84 | static bool group = false; | 91 | static bool group = false; |
85 | static FILE *output = NULL; | 92 | static FILE *output = NULL; |
93 | static const char *pre_cmd = NULL; | ||
94 | static const char *post_cmd = NULL; | ||
95 | static bool sync_run = false; | ||
96 | static unsigned int interval = 0; | ||
97 | static struct timespec ref_time; | ||
98 | static struct cpu_map *sock_map; | ||
86 | 99 | ||
87 | static volatile int done = 0; | 100 | static volatile int done = 0; |
88 | 101 | ||
@@ -90,6 +103,28 @@ struct perf_stat { | |||
90 | struct stats res_stats[3]; | 103 | struct stats res_stats[3]; |
91 | }; | 104 | }; |
92 | 105 | ||
106 | static inline void diff_timespec(struct timespec *r, struct timespec *a, | ||
107 | struct timespec *b) | ||
108 | { | ||
109 | r->tv_sec = a->tv_sec - b->tv_sec; | ||
110 | if (a->tv_nsec < b->tv_nsec) { | ||
111 | r->tv_nsec = a->tv_nsec + 1000000000L - b->tv_nsec; | ||
112 | r->tv_sec--; | ||
113 | } else { | ||
114 | r->tv_nsec = a->tv_nsec - b->tv_nsec ; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) | ||
119 | { | ||
120 | return (evsel->cpus && !target.cpu_list) ? evsel->cpus : evsel_list->cpus; | ||
121 | } | ||
122 | |||
123 | static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel) | ||
124 | { | ||
125 | return perf_evsel__cpus(evsel)->nr; | ||
126 | } | ||
127 | |||
93 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) | 128 | static int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel) |
94 | { | 129 | { |
95 | evsel->priv = zalloc(sizeof(struct perf_stat)); | 130 | evsel->priv = zalloc(sizeof(struct perf_stat)); |
@@ -102,14 +137,27 @@ static void perf_evsel__free_stat_priv(struct perf_evsel *evsel) | |||
102 | evsel->priv = NULL; | 137 | evsel->priv = NULL; |
103 | } | 138 | } |
104 | 139 | ||
105 | static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel) | 140 | static int perf_evsel__alloc_prev_raw_counts(struct perf_evsel *evsel) |
106 | { | 141 | { |
107 | return (evsel->cpus && !target.cpu_list) ? evsel->cpus : evsel_list->cpus; | 142 | void *addr; |
143 | size_t sz; | ||
144 | |||
145 | sz = sizeof(*evsel->counts) + | ||
146 | (perf_evsel__nr_cpus(evsel) * sizeof(struct perf_counts_values)); | ||
147 | |||
148 | addr = zalloc(sz); | ||
149 | if (!addr) | ||
150 | return -ENOMEM; | ||
151 | |||
152 | evsel->prev_raw_counts = addr; | ||
153 | |||
154 | return 0; | ||
108 | } | 155 | } |
109 | 156 | ||
110 | static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel) | 157 | static void perf_evsel__free_prev_raw_counts(struct perf_evsel *evsel) |
111 | { | 158 | { |
112 | return perf_evsel__cpus(evsel)->nr; | 159 | free(evsel->prev_raw_counts); |
160 | evsel->prev_raw_counts = NULL; | ||
113 | } | 161 | } |
114 | 162 | ||
115 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; | 163 | static struct stats runtime_nsecs_stats[MAX_NR_CPUS]; |
@@ -125,12 +173,9 @@ static struct stats runtime_itlb_cache_stats[MAX_NR_CPUS]; | |||
125 | static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; | 173 | static struct stats runtime_dtlb_cache_stats[MAX_NR_CPUS]; |
126 | static struct stats walltime_nsecs_stats; | 174 | static struct stats walltime_nsecs_stats; |
127 | 175 | ||
128 | static int create_perf_stat_counter(struct perf_evsel *evsel, | 176 | static int create_perf_stat_counter(struct perf_evsel *evsel) |
129 | struct perf_evsel *first) | ||
130 | { | 177 | { |
131 | struct perf_event_attr *attr = &evsel->attr; | 178 | struct perf_event_attr *attr = &evsel->attr; |
132 | bool exclude_guest_missing = false; | ||
133 | int ret; | ||
134 | 179 | ||
135 | if (scale) | 180 | if (scale) |
136 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 181 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
@@ -138,37 +183,16 @@ static int create_perf_stat_counter(struct perf_evsel *evsel, | |||
138 | 183 | ||
139 | attr->inherit = !no_inherit; | 184 | attr->inherit = !no_inherit; |
140 | 185 | ||
141 | retry: | 186 | if (perf_target__has_cpu(&target)) |
142 | if (exclude_guest_missing) | 187 | return perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); |
143 | evsel->attr.exclude_guest = evsel->attr.exclude_host = 0; | ||
144 | |||
145 | if (perf_target__has_cpu(&target)) { | ||
146 | ret = perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel)); | ||
147 | if (ret) | ||
148 | goto check_ret; | ||
149 | return 0; | ||
150 | } | ||
151 | 188 | ||
152 | if (!perf_target__has_task(&target) && (!group || evsel == first)) { | 189 | if (!perf_target__has_task(&target) && |
190 | perf_evsel__is_group_leader(evsel)) { | ||
153 | attr->disabled = 1; | 191 | attr->disabled = 1; |
154 | attr->enable_on_exec = 1; | 192 | attr->enable_on_exec = 1; |
155 | } | 193 | } |
156 | 194 | ||
157 | ret = perf_evsel__open_per_thread(evsel, evsel_list->threads); | 195 | return perf_evsel__open_per_thread(evsel, evsel_list->threads); |
158 | if (!ret) | ||
159 | return 0; | ||
160 | /* fall through */ | ||
161 | check_ret: | ||
162 | if (ret && errno == EINVAL) { | ||
163 | if (!exclude_guest_missing && | ||
164 | (evsel->attr.exclude_guest || evsel->attr.exclude_host)) { | ||
165 | pr_debug("Old kernel, cannot exclude " | ||
166 | "guest or host samples.\n"); | ||
167 | exclude_guest_missing = true; | ||
168 | goto retry; | ||
169 | } | ||
170 | } | ||
171 | return ret; | ||
172 | } | 196 | } |
173 | 197 | ||
174 | /* | 198 | /* |
@@ -265,15 +289,79 @@ static int read_counter(struct perf_evsel *counter) | |||
265 | return 0; | 289 | return 0; |
266 | } | 290 | } |
267 | 291 | ||
268 | static int run_perf_stat(int argc __maybe_unused, const char **argv) | 292 | static void print_interval(void) |
293 | { | ||
294 | static int num_print_interval; | ||
295 | struct perf_evsel *counter; | ||
296 | struct perf_stat *ps; | ||
297 | struct timespec ts, rs; | ||
298 | char prefix[64]; | ||
299 | |||
300 | if (no_aggr) { | ||
301 | list_for_each_entry(counter, &evsel_list->entries, node) { | ||
302 | ps = counter->priv; | ||
303 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); | ||
304 | read_counter(counter); | ||
305 | } | ||
306 | } else { | ||
307 | list_for_each_entry(counter, &evsel_list->entries, node) { | ||
308 | ps = counter->priv; | ||
309 | memset(ps->res_stats, 0, sizeof(ps->res_stats)); | ||
310 | read_counter_aggr(counter); | ||
311 | } | ||
312 | } | ||
313 | clock_gettime(CLOCK_MONOTONIC, &ts); | ||
314 | diff_timespec(&rs, &ts, &ref_time); | ||
315 | sprintf(prefix, "%6lu.%09lu%s", rs.tv_sec, rs.tv_nsec, csv_sep); | ||
316 | |||
317 | if (num_print_interval == 0 && !csv_output) { | ||
318 | if (aggr_socket) | ||
319 | fprintf(output, "# time socket cpus counts events\n"); | ||
320 | else if (no_aggr) | ||
321 | fprintf(output, "# time CPU counts events\n"); | ||
322 | else | ||
323 | fprintf(output, "# time counts events\n"); | ||
324 | } | ||
325 | |||
326 | if (++num_print_interval == 25) | ||
327 | num_print_interval = 0; | ||
328 | |||
329 | if (aggr_socket) | ||
330 | print_aggr_socket(prefix); | ||
331 | else if (no_aggr) { | ||
332 | list_for_each_entry(counter, &evsel_list->entries, node) | ||
333 | print_counter(counter, prefix); | ||
334 | } else { | ||
335 | list_for_each_entry(counter, &evsel_list->entries, node) | ||
336 | print_counter_aggr(counter, prefix); | ||
337 | } | ||
338 | } | ||
339 | |||
340 | static int __run_perf_stat(int argc __maybe_unused, const char **argv) | ||
269 | { | 341 | { |
342 | char msg[512]; | ||
270 | unsigned long long t0, t1; | 343 | unsigned long long t0, t1; |
271 | struct perf_evsel *counter, *first; | 344 | struct perf_evsel *counter; |
345 | struct timespec ts; | ||
272 | int status = 0; | 346 | int status = 0; |
273 | int child_ready_pipe[2], go_pipe[2]; | 347 | int child_ready_pipe[2], go_pipe[2]; |
274 | const bool forks = (argc > 0); | 348 | const bool forks = (argc > 0); |
275 | char buf; | 349 | char buf; |
276 | 350 | ||
351 | if (interval) { | ||
352 | ts.tv_sec = interval / 1000; | ||
353 | ts.tv_nsec = (interval % 1000) * 1000000; | ||
354 | } else { | ||
355 | ts.tv_sec = 1; | ||
356 | ts.tv_nsec = 0; | ||
357 | } | ||
358 | |||
359 | if (aggr_socket | ||
360 | && cpu_map__build_socket_map(evsel_list->cpus, &sock_map)) { | ||
361 | perror("cannot build socket map"); | ||
362 | return -1; | ||
363 | } | ||
364 | |||
277 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { | 365 | if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) { |
278 | perror("failed to create pipes"); | 366 | perror("failed to create pipes"); |
279 | return -1; | 367 | return -1; |
@@ -328,10 +416,8 @@ static int run_perf_stat(int argc __maybe_unused, const char **argv) | |||
328 | if (group) | 416 | if (group) |
329 | perf_evlist__set_leader(evsel_list); | 417 | perf_evlist__set_leader(evsel_list); |
330 | 418 | ||
331 | first = perf_evlist__first(evsel_list); | ||
332 | |||
333 | list_for_each_entry(counter, &evsel_list->entries, node) { | 419 | list_for_each_entry(counter, &evsel_list->entries, node) { |
334 | if (create_perf_stat_counter(counter, first) < 0) { | 420 | if (create_perf_stat_counter(counter) < 0) { |
335 | /* | 421 | /* |
336 | * PPC returns ENXIO for HW counters until 2.6.37 | 422 | * PPC returns ENXIO for HW counters until 2.6.37 |
337 | * (behavior changed with commit b0a873e). | 423 | * (behavior changed with commit b0a873e). |
@@ -346,20 +432,13 @@ static int run_perf_stat(int argc __maybe_unused, const char **argv) | |||
346 | continue; | 432 | continue; |
347 | } | 433 | } |
348 | 434 | ||
349 | if (errno == EPERM || errno == EACCES) { | 435 | perf_evsel__open_strerror(counter, &target, |
350 | error("You may not have permission to collect %sstats.\n" | 436 | errno, msg, sizeof(msg)); |
351 | "\t Consider tweaking" | 437 | ui__error("%s\n", msg); |
352 | " /proc/sys/kernel/perf_event_paranoid or running as root.", | 438 | |
353 | target.system_wide ? "system-wide " : ""); | ||
354 | } else { | ||
355 | error("open_counter returned with %d (%s). " | ||
356 | "/bin/dmesg may provide additional information.\n", | ||
357 | errno, strerror(errno)); | ||
358 | } | ||
359 | if (child_pid != -1) | 439 | if (child_pid != -1) |
360 | kill(child_pid, SIGTERM); | 440 | kill(child_pid, SIGTERM); |
361 | 441 | ||
362 | pr_err("Not all events could be opened.\n"); | ||
363 | return -1; | 442 | return -1; |
364 | } | 443 | } |
365 | counter->supported = true; | 444 | counter->supported = true; |
@@ -375,14 +454,25 @@ static int run_perf_stat(int argc __maybe_unused, const char **argv) | |||
375 | * Enable counters and exec the command: | 454 | * Enable counters and exec the command: |
376 | */ | 455 | */ |
377 | t0 = rdclock(); | 456 | t0 = rdclock(); |
457 | clock_gettime(CLOCK_MONOTONIC, &ref_time); | ||
378 | 458 | ||
379 | if (forks) { | 459 | if (forks) { |
380 | close(go_pipe[1]); | 460 | close(go_pipe[1]); |
461 | if (interval) { | ||
462 | while (!waitpid(child_pid, &status, WNOHANG)) { | ||
463 | nanosleep(&ts, NULL); | ||
464 | print_interval(); | ||
465 | } | ||
466 | } | ||
381 | wait(&status); | 467 | wait(&status); |
382 | if (WIFSIGNALED(status)) | 468 | if (WIFSIGNALED(status)) |
383 | psignal(WTERMSIG(status), argv[0]); | 469 | psignal(WTERMSIG(status), argv[0]); |
384 | } else { | 470 | } else { |
385 | while(!done) sleep(1); | 471 | while (!done) { |
472 | nanosleep(&ts, NULL); | ||
473 | if (interval) | ||
474 | print_interval(); | ||
475 | } | ||
386 | } | 476 | } |
387 | 477 | ||
388 | t1 = rdclock(); | 478 | t1 = rdclock(); |
@@ -405,6 +495,32 @@ static int run_perf_stat(int argc __maybe_unused, const char **argv) | |||
405 | return WEXITSTATUS(status); | 495 | return WEXITSTATUS(status); |
406 | } | 496 | } |
407 | 497 | ||
498 | static int run_perf_stat(int argc __maybe_unused, const char **argv) | ||
499 | { | ||
500 | int ret; | ||
501 | |||
502 | if (pre_cmd) { | ||
503 | ret = system(pre_cmd); | ||
504 | if (ret) | ||
505 | return ret; | ||
506 | } | ||
507 | |||
508 | if (sync_run) | ||
509 | sync(); | ||
510 | |||
511 | ret = __run_perf_stat(argc, argv); | ||
512 | if (ret) | ||
513 | return ret; | ||
514 | |||
515 | if (post_cmd) { | ||
516 | ret = system(post_cmd); | ||
517 | if (ret) | ||
518 | return ret; | ||
519 | } | ||
520 | |||
521 | return ret; | ||
522 | } | ||
523 | |||
408 | static void print_noise_pct(double total, double avg) | 524 | static void print_noise_pct(double total, double avg) |
409 | { | 525 | { |
410 | double pct = rel_stddev_stats(total, avg); | 526 | double pct = rel_stddev_stats(total, avg); |
@@ -426,13 +542,21 @@ static void print_noise(struct perf_evsel *evsel, double avg) | |||
426 | print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); | 542 | print_noise_pct(stddev_stats(&ps->res_stats[0]), avg); |
427 | } | 543 | } |
428 | 544 | ||
429 | static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) | 545 | static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) |
430 | { | 546 | { |
431 | double msecs = avg / 1e6; | 547 | double msecs = avg / 1e6; |
432 | char cpustr[16] = { '\0', }; | 548 | char cpustr[16] = { '\0', }; |
433 | const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s"; | 549 | const char *fmt = csv_output ? "%s%.6f%s%s" : "%s%18.6f%s%-25s"; |
434 | 550 | ||
435 | if (no_aggr) | 551 | if (aggr_socket) |
552 | sprintf(cpustr, "S%*d%s%*d%s", | ||
553 | csv_output ? 0 : -5, | ||
554 | cpu, | ||
555 | csv_sep, | ||
556 | csv_output ? 0 : 4, | ||
557 | nr, | ||
558 | csv_sep); | ||
559 | else if (no_aggr) | ||
436 | sprintf(cpustr, "CPU%*d%s", | 560 | sprintf(cpustr, "CPU%*d%s", |
437 | csv_output ? 0 : -4, | 561 | csv_output ? 0 : -4, |
438 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); | 562 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); |
@@ -442,7 +566,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
442 | if (evsel->cgrp) | 566 | if (evsel->cgrp) |
443 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 567 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
444 | 568 | ||
445 | if (csv_output) | 569 | if (csv_output || interval) |
446 | return; | 570 | return; |
447 | 571 | ||
448 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) | 572 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) |
@@ -631,7 +755,7 @@ static void print_ll_cache_misses(int cpu, | |||
631 | fprintf(output, " of all LL-cache hits "); | 755 | fprintf(output, " of all LL-cache hits "); |
632 | } | 756 | } |
633 | 757 | ||
634 | static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | 758 | static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) |
635 | { | 759 | { |
636 | double total, ratio = 0.0; | 760 | double total, ratio = 0.0; |
637 | char cpustr[16] = { '\0', }; | 761 | char cpustr[16] = { '\0', }; |
@@ -644,7 +768,15 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
644 | else | 768 | else |
645 | fmt = "%s%18.0f%s%-25s"; | 769 | fmt = "%s%18.0f%s%-25s"; |
646 | 770 | ||
647 | if (no_aggr) | 771 | if (aggr_socket) |
772 | sprintf(cpustr, "S%*d%s%*d%s", | ||
773 | csv_output ? 0 : -5, | ||
774 | cpu, | ||
775 | csv_sep, | ||
776 | csv_output ? 0 : 4, | ||
777 | nr, | ||
778 | csv_sep); | ||
779 | else if (no_aggr) | ||
648 | sprintf(cpustr, "CPU%*d%s", | 780 | sprintf(cpustr, "CPU%*d%s", |
649 | csv_output ? 0 : -4, | 781 | csv_output ? 0 : -4, |
650 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); | 782 | perf_evsel__cpus(evsel)->map[cpu], csv_sep); |
@@ -656,12 +788,11 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
656 | if (evsel->cgrp) | 788 | if (evsel->cgrp) |
657 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 789 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
658 | 790 | ||
659 | if (csv_output) | 791 | if (csv_output || interval) |
660 | return; | 792 | return; |
661 | 793 | ||
662 | if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { | 794 | if (perf_evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS)) { |
663 | total = avg_stats(&runtime_cycles_stats[cpu]); | 795 | total = avg_stats(&runtime_cycles_stats[cpu]); |
664 | |||
665 | if (total) | 796 | if (total) |
666 | ratio = avg / total; | 797 | ratio = avg / total; |
667 | 798 | ||
@@ -751,16 +882,83 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
751 | } | 882 | } |
752 | } | 883 | } |
753 | 884 | ||
885 | static void print_aggr_socket(char *prefix) | ||
886 | { | ||
887 | struct perf_evsel *counter; | ||
888 | u64 ena, run, val; | ||
889 | int cpu, s, s2, sock, nr; | ||
890 | |||
891 | if (!sock_map) | ||
892 | return; | ||
893 | |||
894 | for (s = 0; s < sock_map->nr; s++) { | ||
895 | sock = cpu_map__socket(sock_map, s); | ||
896 | list_for_each_entry(counter, &evsel_list->entries, node) { | ||
897 | val = ena = run = 0; | ||
898 | nr = 0; | ||
899 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
900 | s2 = cpu_map__get_socket(evsel_list->cpus, cpu); | ||
901 | if (s2 != sock) | ||
902 | continue; | ||
903 | val += counter->counts->cpu[cpu].val; | ||
904 | ena += counter->counts->cpu[cpu].ena; | ||
905 | run += counter->counts->cpu[cpu].run; | ||
906 | nr++; | ||
907 | } | ||
908 | if (prefix) | ||
909 | fprintf(output, "%s", prefix); | ||
910 | |||
911 | if (run == 0 || ena == 0) { | ||
912 | fprintf(output, "S%*d%s%*d%s%*s%s%*s", | ||
913 | csv_output ? 0 : -5, | ||
914 | s, | ||
915 | csv_sep, | ||
916 | csv_output ? 0 : 4, | ||
917 | nr, | ||
918 | csv_sep, | ||
919 | csv_output ? 0 : 18, | ||
920 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
921 | csv_sep, | ||
922 | csv_output ? 0 : -24, | ||
923 | perf_evsel__name(counter)); | ||
924 | if (counter->cgrp) | ||
925 | fprintf(output, "%s%s", | ||
926 | csv_sep, counter->cgrp->name); | ||
927 | |||
928 | fputc('\n', output); | ||
929 | continue; | ||
930 | } | ||
931 | |||
932 | if (nsec_counter(counter)) | ||
933 | nsec_printout(sock, nr, counter, val); | ||
934 | else | ||
935 | abs_printout(sock, nr, counter, val); | ||
936 | |||
937 | if (!csv_output) { | ||
938 | print_noise(counter, 1.0); | ||
939 | |||
940 | if (run != ena) | ||
941 | fprintf(output, " (%.2f%%)", | ||
942 | 100.0 * run / ena); | ||
943 | } | ||
944 | fputc('\n', output); | ||
945 | } | ||
946 | } | ||
947 | } | ||
948 | |||
754 | /* | 949 | /* |
755 | * Print out the results of a single counter: | 950 | * Print out the results of a single counter: |
756 | * aggregated counts in system-wide mode | 951 | * aggregated counts in system-wide mode |
757 | */ | 952 | */ |
758 | static void print_counter_aggr(struct perf_evsel *counter) | 953 | static void print_counter_aggr(struct perf_evsel *counter, char *prefix) |
759 | { | 954 | { |
760 | struct perf_stat *ps = counter->priv; | 955 | struct perf_stat *ps = counter->priv; |
761 | double avg = avg_stats(&ps->res_stats[0]); | 956 | double avg = avg_stats(&ps->res_stats[0]); |
762 | int scaled = counter->counts->scaled; | 957 | int scaled = counter->counts->scaled; |
763 | 958 | ||
959 | if (prefix) | ||
960 | fprintf(output, "%s", prefix); | ||
961 | |||
764 | if (scaled == -1) { | 962 | if (scaled == -1) { |
765 | fprintf(output, "%*s%s%*s", | 963 | fprintf(output, "%*s%s%*s", |
766 | csv_output ? 0 : 18, | 964 | csv_output ? 0 : 18, |
@@ -777,9 +975,9 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
777 | } | 975 | } |
778 | 976 | ||
779 | if (nsec_counter(counter)) | 977 | if (nsec_counter(counter)) |
780 | nsec_printout(-1, counter, avg); | 978 | nsec_printout(-1, 0, counter, avg); |
781 | else | 979 | else |
782 | abs_printout(-1, counter, avg); | 980 | abs_printout(-1, 0, counter, avg); |
783 | 981 | ||
784 | print_noise(counter, avg); | 982 | print_noise(counter, avg); |
785 | 983 | ||
@@ -803,7 +1001,7 @@ static void print_counter_aggr(struct perf_evsel *counter) | |||
803 | * Print out the results of a single counter: | 1001 | * Print out the results of a single counter: |
804 | * does not use aggregated count in system-wide | 1002 | * does not use aggregated count in system-wide |
805 | */ | 1003 | */ |
806 | static void print_counter(struct perf_evsel *counter) | 1004 | static void print_counter(struct perf_evsel *counter, char *prefix) |
807 | { | 1005 | { |
808 | u64 ena, run, val; | 1006 | u64 ena, run, val; |
809 | int cpu; | 1007 | int cpu; |
@@ -812,6 +1010,10 @@ static void print_counter(struct perf_evsel *counter) | |||
812 | val = counter->counts->cpu[cpu].val; | 1010 | val = counter->counts->cpu[cpu].val; |
813 | ena = counter->counts->cpu[cpu].ena; | 1011 | ena = counter->counts->cpu[cpu].ena; |
814 | run = counter->counts->cpu[cpu].run; | 1012 | run = counter->counts->cpu[cpu].run; |
1013 | |||
1014 | if (prefix) | ||
1015 | fprintf(output, "%s", prefix); | ||
1016 | |||
815 | if (run == 0 || ena == 0) { | 1017 | if (run == 0 || ena == 0) { |
816 | fprintf(output, "CPU%*d%s%*s%s%*s", | 1018 | fprintf(output, "CPU%*d%s%*s%s%*s", |
817 | csv_output ? 0 : -4, | 1019 | csv_output ? 0 : -4, |
@@ -831,9 +1033,9 @@ static void print_counter(struct perf_evsel *counter) | |||
831 | } | 1033 | } |
832 | 1034 | ||
833 | if (nsec_counter(counter)) | 1035 | if (nsec_counter(counter)) |
834 | nsec_printout(cpu, counter, val); | 1036 | nsec_printout(cpu, 0, counter, val); |
835 | else | 1037 | else |
836 | abs_printout(cpu, counter, val); | 1038 | abs_printout(cpu, 0, counter, val); |
837 | 1039 | ||
838 | if (!csv_output) { | 1040 | if (!csv_output) { |
839 | print_noise(counter, 1.0); | 1041 | print_noise(counter, 1.0); |
@@ -871,12 +1073,14 @@ static void print_stat(int argc, const char **argv) | |||
871 | fprintf(output, ":\n\n"); | 1073 | fprintf(output, ":\n\n"); |
872 | } | 1074 | } |
873 | 1075 | ||
874 | if (no_aggr) { | 1076 | if (aggr_socket) |
1077 | print_aggr_socket(NULL); | ||
1078 | else if (no_aggr) { | ||
875 | list_for_each_entry(counter, &evsel_list->entries, node) | 1079 | list_for_each_entry(counter, &evsel_list->entries, node) |
876 | print_counter(counter); | 1080 | print_counter(counter, NULL); |
877 | } else { | 1081 | } else { |
878 | list_for_each_entry(counter, &evsel_list->entries, node) | 1082 | list_for_each_entry(counter, &evsel_list->entries, node) |
879 | print_counter_aggr(counter); | 1083 | print_counter_aggr(counter, NULL); |
880 | } | 1084 | } |
881 | 1085 | ||
882 | if (!csv_output) { | 1086 | if (!csv_output) { |
@@ -897,7 +1101,7 @@ static volatile int signr = -1; | |||
897 | 1101 | ||
898 | static void skip_signal(int signo) | 1102 | static void skip_signal(int signo) |
899 | { | 1103 | { |
900 | if(child_pid == -1) | 1104 | if ((child_pid == -1) || interval) |
901 | done = 1; | 1105 | done = 1; |
902 | 1106 | ||
903 | signr = signo; | 1107 | signr = signo; |
@@ -1069,8 +1273,7 @@ static int add_default_attributes(void) | |||
1069 | 1273 | ||
1070 | int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | 1274 | int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) |
1071 | { | 1275 | { |
1072 | bool append_file = false, | 1276 | bool append_file = false; |
1073 | sync_run = false; | ||
1074 | int output_fd = 0; | 1277 | int output_fd = 0; |
1075 | const char *output_name = NULL; | 1278 | const char *output_name = NULL; |
1076 | const struct option options[] = { | 1279 | const struct option options[] = { |
@@ -1114,6 +1317,13 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1114 | OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), | 1317 | OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), |
1115 | OPT_INTEGER(0, "log-fd", &output_fd, | 1318 | OPT_INTEGER(0, "log-fd", &output_fd, |
1116 | "log output to fd, instead of stderr"), | 1319 | "log output to fd, instead of stderr"), |
1320 | OPT_STRING(0, "pre", &pre_cmd, "command", | ||
1321 | "command to run prior to the measured command"), | ||
1322 | OPT_STRING(0, "post", &post_cmd, "command", | ||
1323 | "command to run after to the measured command"), | ||
1324 | OPT_UINTEGER('I', "interval-print", &interval, | ||
1325 | "print counts at regular interval in ms (>= 100)"), | ||
1326 | OPT_BOOLEAN(0, "aggr-socket", &aggr_socket, "aggregate counts per processor socket"), | ||
1117 | OPT_END() | 1327 | OPT_END() |
1118 | }; | 1328 | }; |
1119 | const char * const stat_usage[] = { | 1329 | const char * const stat_usage[] = { |
@@ -1200,6 +1410,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1200 | usage_with_options(stat_usage, options); | 1410 | usage_with_options(stat_usage, options); |
1201 | } | 1411 | } |
1202 | 1412 | ||
1413 | if (aggr_socket) { | ||
1414 | if (!perf_target__has_cpu(&target)) { | ||
1415 | fprintf(stderr, "--aggr-socket only available in system-wide mode (-a)\n"); | ||
1416 | usage_with_options(stat_usage, options); | ||
1417 | } | ||
1418 | no_aggr = true; | ||
1419 | } | ||
1420 | |||
1203 | if (add_default_attributes()) | 1421 | if (add_default_attributes()) |
1204 | goto out; | 1422 | goto out; |
1205 | 1423 | ||
@@ -1214,12 +1432,23 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1214 | usage_with_options(stat_usage, options); | 1432 | usage_with_options(stat_usage, options); |
1215 | return -1; | 1433 | return -1; |
1216 | } | 1434 | } |
1435 | if (interval && interval < 100) { | ||
1436 | pr_err("print interval must be >= 100ms\n"); | ||
1437 | usage_with_options(stat_usage, options); | ||
1438 | return -1; | ||
1439 | } | ||
1217 | 1440 | ||
1218 | list_for_each_entry(pos, &evsel_list->entries, node) { | 1441 | list_for_each_entry(pos, &evsel_list->entries, node) { |
1219 | if (perf_evsel__alloc_stat_priv(pos) < 0 || | 1442 | if (perf_evsel__alloc_stat_priv(pos) < 0 || |
1220 | perf_evsel__alloc_counts(pos, perf_evsel__nr_cpus(pos)) < 0) | 1443 | perf_evsel__alloc_counts(pos, perf_evsel__nr_cpus(pos)) < 0) |
1221 | goto out_free_fd; | 1444 | goto out_free_fd; |
1222 | } | 1445 | } |
1446 | if (interval) { | ||
1447 | list_for_each_entry(pos, &evsel_list->entries, node) { | ||
1448 | if (perf_evsel__alloc_prev_raw_counts(pos) < 0) | ||
1449 | goto out_free_fd; | ||
1450 | } | ||
1451 | } | ||
1223 | 1452 | ||
1224 | /* | 1453 | /* |
1225 | * We dont want to block the signals - that would cause | 1454 | * We dont want to block the signals - that would cause |
@@ -1229,6 +1458,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1229 | */ | 1458 | */ |
1230 | atexit(sig_atexit); | 1459 | atexit(sig_atexit); |
1231 | signal(SIGINT, skip_signal); | 1460 | signal(SIGINT, skip_signal); |
1461 | signal(SIGCHLD, skip_signal); | ||
1232 | signal(SIGALRM, skip_signal); | 1462 | signal(SIGALRM, skip_signal); |
1233 | signal(SIGABRT, skip_signal); | 1463 | signal(SIGABRT, skip_signal); |
1234 | 1464 | ||
@@ -1238,17 +1468,17 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1238 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", | 1468 | fprintf(output, "[ perf stat: executing run #%d ... ]\n", |
1239 | run_idx + 1); | 1469 | run_idx + 1); |
1240 | 1470 | ||
1241 | if (sync_run) | ||
1242 | sync(); | ||
1243 | |||
1244 | status = run_perf_stat(argc, argv); | 1471 | status = run_perf_stat(argc, argv); |
1245 | } | 1472 | } |
1246 | 1473 | ||
1247 | if (status != -1) | 1474 | if (status != -1 && !interval) |
1248 | print_stat(argc, argv); | 1475 | print_stat(argc, argv); |
1249 | out_free_fd: | 1476 | out_free_fd: |
1250 | list_for_each_entry(pos, &evsel_list->entries, node) | 1477 | list_for_each_entry(pos, &evsel_list->entries, node) { |
1251 | perf_evsel__free_stat_priv(pos); | 1478 | perf_evsel__free_stat_priv(pos); |
1479 | perf_evsel__free_counts(pos); | ||
1480 | perf_evsel__free_prev_raw_counts(pos); | ||
1481 | } | ||
1252 | perf_evlist__delete_maps(evsel_list); | 1482 | perf_evlist__delete_maps(evsel_list); |
1253 | out: | 1483 | out: |
1254 | perf_evlist__delete(evsel_list); | 1484 | perf_evlist__delete(evsel_list); |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c deleted file mode 100644 index 484f26cc0c00..000000000000 --- a/tools/perf/builtin-test.c +++ /dev/null | |||
@@ -1,1547 +0,0 @@ | |||
1 | /* | ||
2 | * builtin-test.c | ||
3 | * | ||
4 | * Builtin regression testing command: ever growing number of sanity tests | ||
5 | */ | ||
6 | #include "builtin.h" | ||
7 | |||
8 | #include "util/cache.h" | ||
9 | #include "util/debug.h" | ||
10 | #include "util/debugfs.h" | ||
11 | #include "util/evlist.h" | ||
12 | #include "util/parse-options.h" | ||
13 | #include "util/parse-events.h" | ||
14 | #include "util/symbol.h" | ||
15 | #include "util/thread_map.h" | ||
16 | #include "util/pmu.h" | ||
17 | #include "event-parse.h" | ||
18 | #include "../../include/linux/hw_breakpoint.h" | ||
19 | |||
20 | #include <sys/mman.h> | ||
21 | |||
22 | static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused, | ||
23 | struct symbol *sym) | ||
24 | { | ||
25 | bool *visited = symbol__priv(sym); | ||
26 | *visited = true; | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | static int test__vmlinux_matches_kallsyms(void) | ||
31 | { | ||
32 | int err = -1; | ||
33 | struct rb_node *nd; | ||
34 | struct symbol *sym; | ||
35 | struct map *kallsyms_map, *vmlinux_map; | ||
36 | struct machine kallsyms, vmlinux; | ||
37 | enum map_type type = MAP__FUNCTION; | ||
38 | long page_size = sysconf(_SC_PAGE_SIZE); | ||
39 | struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; | ||
40 | |||
41 | /* | ||
42 | * Step 1: | ||
43 | * | ||
44 | * Init the machines that will hold kernel, modules obtained from | ||
45 | * both vmlinux + .ko files and from /proc/kallsyms split by modules. | ||
46 | */ | ||
47 | machine__init(&kallsyms, "", HOST_KERNEL_ID); | ||
48 | machine__init(&vmlinux, "", HOST_KERNEL_ID); | ||
49 | |||
50 | /* | ||
51 | * Step 2: | ||
52 | * | ||
53 | * Create the kernel maps for kallsyms and the DSO where we will then | ||
54 | * load /proc/kallsyms. Also create the modules maps from /proc/modules | ||
55 | * and find the .ko files that match them in /lib/modules/`uname -r`/. | ||
56 | */ | ||
57 | if (machine__create_kernel_maps(&kallsyms) < 0) { | ||
58 | pr_debug("machine__create_kernel_maps "); | ||
59 | return -1; | ||
60 | } | ||
61 | |||
62 | /* | ||
63 | * Step 3: | ||
64 | * | ||
65 | * Load and split /proc/kallsyms into multiple maps, one per module. | ||
66 | */ | ||
67 | if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) { | ||
68 | pr_debug("dso__load_kallsyms "); | ||
69 | goto out; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * Step 4: | ||
74 | * | ||
75 | * kallsyms will be internally on demand sorted by name so that we can | ||
76 | * find the reference relocation * symbol, i.e. the symbol we will use | ||
77 | * to see if the running kernel was relocated by checking if it has the | ||
78 | * same value in the vmlinux file we load. | ||
79 | */ | ||
80 | kallsyms_map = machine__kernel_map(&kallsyms, type); | ||
81 | |||
82 | sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL); | ||
83 | if (sym == NULL) { | ||
84 | pr_debug("dso__find_symbol_by_name "); | ||
85 | goto out; | ||
86 | } | ||
87 | |||
88 | ref_reloc_sym.addr = sym->start; | ||
89 | |||
90 | /* | ||
91 | * Step 5: | ||
92 | * | ||
93 | * Now repeat step 2, this time for the vmlinux file we'll auto-locate. | ||
94 | */ | ||
95 | if (machine__create_kernel_maps(&vmlinux) < 0) { | ||
96 | pr_debug("machine__create_kernel_maps "); | ||
97 | goto out; | ||
98 | } | ||
99 | |||
100 | vmlinux_map = machine__kernel_map(&vmlinux, type); | ||
101 | map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym; | ||
102 | |||
103 | /* | ||
104 | * Step 6: | ||
105 | * | ||
106 | * Locate a vmlinux file in the vmlinux path that has a buildid that | ||
107 | * matches the one of the running kernel. | ||
108 | * | ||
109 | * While doing that look if we find the ref reloc symbol, if we find it | ||
110 | * we'll have its ref_reloc_symbol.unrelocated_addr and then | ||
111 | * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines | ||
112 | * to fixup the symbols. | ||
113 | */ | ||
114 | if (machine__load_vmlinux_path(&vmlinux, type, | ||
115 | vmlinux_matches_kallsyms_filter) <= 0) { | ||
116 | pr_debug("machine__load_vmlinux_path "); | ||
117 | goto out; | ||
118 | } | ||
119 | |||
120 | err = 0; | ||
121 | /* | ||
122 | * Step 7: | ||
123 | * | ||
124 | * Now look at the symbols in the vmlinux DSO and check if we find all of them | ||
125 | * in the kallsyms dso. For the ones that are in both, check its names and | ||
126 | * end addresses too. | ||
127 | */ | ||
128 | for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { | ||
129 | struct symbol *pair, *first_pair; | ||
130 | bool backwards = true; | ||
131 | |||
132 | sym = rb_entry(nd, struct symbol, rb_node); | ||
133 | |||
134 | if (sym->start == sym->end) | ||
135 | continue; | ||
136 | |||
137 | first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL); | ||
138 | pair = first_pair; | ||
139 | |||
140 | if (pair && pair->start == sym->start) { | ||
141 | next_pair: | ||
142 | if (strcmp(sym->name, pair->name) == 0) { | ||
143 | /* | ||
144 | * kallsyms don't have the symbol end, so we | ||
145 | * set that by using the next symbol start - 1, | ||
146 | * in some cases we get this up to a page | ||
147 | * wrong, trace_kmalloc when I was developing | ||
148 | * this code was one such example, 2106 bytes | ||
149 | * off the real size. More than that and we | ||
150 | * _really_ have a problem. | ||
151 | */ | ||
152 | s64 skew = sym->end - pair->end; | ||
153 | if (llabs(skew) < page_size) | ||
154 | continue; | ||
155 | |||
156 | pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n", | ||
157 | sym->start, sym->name, sym->end, pair->end); | ||
158 | } else { | ||
159 | struct rb_node *nnd; | ||
160 | detour: | ||
161 | nnd = backwards ? rb_prev(&pair->rb_node) : | ||
162 | rb_next(&pair->rb_node); | ||
163 | if (nnd) { | ||
164 | struct symbol *next = rb_entry(nnd, struct symbol, rb_node); | ||
165 | |||
166 | if (next->start == sym->start) { | ||
167 | pair = next; | ||
168 | goto next_pair; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | if (backwards) { | ||
173 | backwards = false; | ||
174 | pair = first_pair; | ||
175 | goto detour; | ||
176 | } | ||
177 | |||
178 | pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", | ||
179 | sym->start, sym->name, pair->name); | ||
180 | } | ||
181 | } else | ||
182 | pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name); | ||
183 | |||
184 | err = -1; | ||
185 | } | ||
186 | |||
187 | if (!verbose) | ||
188 | goto out; | ||
189 | |||
190 | pr_info("Maps only in vmlinux:\n"); | ||
191 | |||
192 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | ||
193 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; | ||
194 | /* | ||
195 | * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while | ||
196 | * the kernel will have the path for the vmlinux file being used, | ||
197 | * so use the short name, less descriptive but the same ("[kernel]" in | ||
198 | * both cases. | ||
199 | */ | ||
200 | pair = map_groups__find_by_name(&kallsyms.kmaps, type, | ||
201 | (pos->dso->kernel ? | ||
202 | pos->dso->short_name : | ||
203 | pos->dso->name)); | ||
204 | if (pair) | ||
205 | pair->priv = 1; | ||
206 | else | ||
207 | map__fprintf(pos, stderr); | ||
208 | } | ||
209 | |||
210 | pr_info("Maps in vmlinux with a different name in kallsyms:\n"); | ||
211 | |||
212 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | ||
213 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; | ||
214 | |||
215 | pair = map_groups__find(&kallsyms.kmaps, type, pos->start); | ||
216 | if (pair == NULL || pair->priv) | ||
217 | continue; | ||
218 | |||
219 | if (pair->start == pos->start) { | ||
220 | pair->priv = 1; | ||
221 | pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", | ||
222 | pos->start, pos->end, pos->pgoff, pos->dso->name); | ||
223 | if (pos->pgoff != pair->pgoff || pos->end != pair->end) | ||
224 | pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "", | ||
225 | pair->start, pair->end, pair->pgoff); | ||
226 | pr_info(" %s\n", pair->dso->name); | ||
227 | pair->priv = 1; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | pr_info("Maps only in kallsyms:\n"); | ||
232 | |||
233 | for (nd = rb_first(&kallsyms.kmaps.maps[type]); | ||
234 | nd; nd = rb_next(nd)) { | ||
235 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
236 | |||
237 | if (!pos->priv) | ||
238 | map__fprintf(pos, stderr); | ||
239 | } | ||
240 | out: | ||
241 | return err; | ||
242 | } | ||
243 | |||
244 | #include "util/cpumap.h" | ||
245 | #include "util/evsel.h" | ||
246 | #include <sys/types.h> | ||
247 | |||
248 | static int trace_event__id(const char *evname) | ||
249 | { | ||
250 | char *filename; | ||
251 | int err = -1, fd; | ||
252 | |||
253 | if (asprintf(&filename, | ||
254 | "%s/syscalls/%s/id", | ||
255 | tracing_events_path, evname) < 0) | ||
256 | return -1; | ||
257 | |||
258 | fd = open(filename, O_RDONLY); | ||
259 | if (fd >= 0) { | ||
260 | char id[16]; | ||
261 | if (read(fd, id, sizeof(id)) > 0) | ||
262 | err = atoi(id); | ||
263 | close(fd); | ||
264 | } | ||
265 | |||
266 | free(filename); | ||
267 | return err; | ||
268 | } | ||
269 | |||
270 | static int test__open_syscall_event(void) | ||
271 | { | ||
272 | int err = -1, fd; | ||
273 | struct thread_map *threads; | ||
274 | struct perf_evsel *evsel; | ||
275 | struct perf_event_attr attr; | ||
276 | unsigned int nr_open_calls = 111, i; | ||
277 | int id = trace_event__id("sys_enter_open"); | ||
278 | |||
279 | if (id < 0) { | ||
280 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
281 | return -1; | ||
282 | } | ||
283 | |||
284 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
285 | if (threads == NULL) { | ||
286 | pr_debug("thread_map__new\n"); | ||
287 | return -1; | ||
288 | } | ||
289 | |||
290 | memset(&attr, 0, sizeof(attr)); | ||
291 | attr.type = PERF_TYPE_TRACEPOINT; | ||
292 | attr.config = id; | ||
293 | evsel = perf_evsel__new(&attr, 0); | ||
294 | if (evsel == NULL) { | ||
295 | pr_debug("perf_evsel__new\n"); | ||
296 | goto out_thread_map_delete; | ||
297 | } | ||
298 | |||
299 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { | ||
300 | pr_debug("failed to open counter: %s, " | ||
301 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
302 | strerror(errno)); | ||
303 | goto out_evsel_delete; | ||
304 | } | ||
305 | |||
306 | for (i = 0; i < nr_open_calls; ++i) { | ||
307 | fd = open("/etc/passwd", O_RDONLY); | ||
308 | close(fd); | ||
309 | } | ||
310 | |||
311 | if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) { | ||
312 | pr_debug("perf_evsel__read_on_cpu\n"); | ||
313 | goto out_close_fd; | ||
314 | } | ||
315 | |||
316 | if (evsel->counts->cpu[0].val != nr_open_calls) { | ||
317 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n", | ||
318 | nr_open_calls, evsel->counts->cpu[0].val); | ||
319 | goto out_close_fd; | ||
320 | } | ||
321 | |||
322 | err = 0; | ||
323 | out_close_fd: | ||
324 | perf_evsel__close_fd(evsel, 1, threads->nr); | ||
325 | out_evsel_delete: | ||
326 | perf_evsel__delete(evsel); | ||
327 | out_thread_map_delete: | ||
328 | thread_map__delete(threads); | ||
329 | return err; | ||
330 | } | ||
331 | |||
332 | #include <sched.h> | ||
333 | |||
334 | static int test__open_syscall_event_on_all_cpus(void) | ||
335 | { | ||
336 | int err = -1, fd, cpu; | ||
337 | struct thread_map *threads; | ||
338 | struct cpu_map *cpus; | ||
339 | struct perf_evsel *evsel; | ||
340 | struct perf_event_attr attr; | ||
341 | unsigned int nr_open_calls = 111, i; | ||
342 | cpu_set_t cpu_set; | ||
343 | int id = trace_event__id("sys_enter_open"); | ||
344 | |||
345 | if (id < 0) { | ||
346 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
347 | return -1; | ||
348 | } | ||
349 | |||
350 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
351 | if (threads == NULL) { | ||
352 | pr_debug("thread_map__new\n"); | ||
353 | return -1; | ||
354 | } | ||
355 | |||
356 | cpus = cpu_map__new(NULL); | ||
357 | if (cpus == NULL) { | ||
358 | pr_debug("cpu_map__new\n"); | ||
359 | goto out_thread_map_delete; | ||
360 | } | ||
361 | |||
362 | |||
363 | CPU_ZERO(&cpu_set); | ||
364 | |||
365 | memset(&attr, 0, sizeof(attr)); | ||
366 | attr.type = PERF_TYPE_TRACEPOINT; | ||
367 | attr.config = id; | ||
368 | evsel = perf_evsel__new(&attr, 0); | ||
369 | if (evsel == NULL) { | ||
370 | pr_debug("perf_evsel__new\n"); | ||
371 | goto out_thread_map_delete; | ||
372 | } | ||
373 | |||
374 | if (perf_evsel__open(evsel, cpus, threads) < 0) { | ||
375 | pr_debug("failed to open counter: %s, " | ||
376 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
377 | strerror(errno)); | ||
378 | goto out_evsel_delete; | ||
379 | } | ||
380 | |||
381 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | ||
382 | unsigned int ncalls = nr_open_calls + cpu; | ||
383 | /* | ||
384 | * XXX eventually lift this restriction in a way that | ||
385 | * keeps perf building on older glibc installations | ||
386 | * without CPU_ALLOC. 1024 cpus in 2010 still seems | ||
387 | * a reasonable upper limit tho :-) | ||
388 | */ | ||
389 | if (cpus->map[cpu] >= CPU_SETSIZE) { | ||
390 | pr_debug("Ignoring CPU %d\n", cpus->map[cpu]); | ||
391 | continue; | ||
392 | } | ||
393 | |||
394 | CPU_SET(cpus->map[cpu], &cpu_set); | ||
395 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
396 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
397 | cpus->map[cpu], | ||
398 | strerror(errno)); | ||
399 | goto out_close_fd; | ||
400 | } | ||
401 | for (i = 0; i < ncalls; ++i) { | ||
402 | fd = open("/etc/passwd", O_RDONLY); | ||
403 | close(fd); | ||
404 | } | ||
405 | CPU_CLR(cpus->map[cpu], &cpu_set); | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | * Here we need to explicitely preallocate the counts, as if | ||
410 | * we use the auto allocation it will allocate just for 1 cpu, | ||
411 | * as we start by cpu 0. | ||
412 | */ | ||
413 | if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) { | ||
414 | pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr); | ||
415 | goto out_close_fd; | ||
416 | } | ||
417 | |||
418 | err = 0; | ||
419 | |||
420 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | ||
421 | unsigned int expected; | ||
422 | |||
423 | if (cpus->map[cpu] >= CPU_SETSIZE) | ||
424 | continue; | ||
425 | |||
426 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { | ||
427 | pr_debug("perf_evsel__read_on_cpu\n"); | ||
428 | err = -1; | ||
429 | break; | ||
430 | } | ||
431 | |||
432 | expected = nr_open_calls + cpu; | ||
433 | if (evsel->counts->cpu[cpu].val != expected) { | ||
434 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", | ||
435 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); | ||
436 | err = -1; | ||
437 | } | ||
438 | } | ||
439 | |||
440 | out_close_fd: | ||
441 | perf_evsel__close_fd(evsel, 1, threads->nr); | ||
442 | out_evsel_delete: | ||
443 | perf_evsel__delete(evsel); | ||
444 | out_thread_map_delete: | ||
445 | thread_map__delete(threads); | ||
446 | return err; | ||
447 | } | ||
448 | |||
449 | /* | ||
450 | * This test will generate random numbers of calls to some getpid syscalls, | ||
451 | * then establish an mmap for a group of events that are created to monitor | ||
452 | * the syscalls. | ||
453 | * | ||
454 | * It will receive the events, using mmap, use its PERF_SAMPLE_ID generated | ||
455 | * sample.id field to map back to its respective perf_evsel instance. | ||
456 | * | ||
457 | * Then it checks if the number of syscalls reported as perf events by | ||
458 | * the kernel corresponds to the number of syscalls made. | ||
459 | */ | ||
460 | static int test__basic_mmap(void) | ||
461 | { | ||
462 | int err = -1; | ||
463 | union perf_event *event; | ||
464 | struct thread_map *threads; | ||
465 | struct cpu_map *cpus; | ||
466 | struct perf_evlist *evlist; | ||
467 | struct perf_event_attr attr = { | ||
468 | .type = PERF_TYPE_TRACEPOINT, | ||
469 | .read_format = PERF_FORMAT_ID, | ||
470 | .sample_type = PERF_SAMPLE_ID, | ||
471 | .watermark = 0, | ||
472 | }; | ||
473 | cpu_set_t cpu_set; | ||
474 | const char *syscall_names[] = { "getsid", "getppid", "getpgrp", | ||
475 | "getpgid", }; | ||
476 | pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp, | ||
477 | (void*)getpgid }; | ||
478 | #define nsyscalls ARRAY_SIZE(syscall_names) | ||
479 | int ids[nsyscalls]; | ||
480 | unsigned int nr_events[nsyscalls], | ||
481 | expected_nr_events[nsyscalls], i, j; | ||
482 | struct perf_evsel *evsels[nsyscalls], *evsel; | ||
483 | |||
484 | for (i = 0; i < nsyscalls; ++i) { | ||
485 | char name[64]; | ||
486 | |||
487 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); | ||
488 | ids[i] = trace_event__id(name); | ||
489 | if (ids[i] < 0) { | ||
490 | pr_debug("Is debugfs mounted on /sys/kernel/debug?\n"); | ||
491 | return -1; | ||
492 | } | ||
493 | nr_events[i] = 0; | ||
494 | expected_nr_events[i] = random() % 257; | ||
495 | } | ||
496 | |||
497 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
498 | if (threads == NULL) { | ||
499 | pr_debug("thread_map__new\n"); | ||
500 | return -1; | ||
501 | } | ||
502 | |||
503 | cpus = cpu_map__new(NULL); | ||
504 | if (cpus == NULL) { | ||
505 | pr_debug("cpu_map__new\n"); | ||
506 | goto out_free_threads; | ||
507 | } | ||
508 | |||
509 | CPU_ZERO(&cpu_set); | ||
510 | CPU_SET(cpus->map[0], &cpu_set); | ||
511 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); | ||
512 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
513 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
514 | cpus->map[0], strerror(errno)); | ||
515 | goto out_free_cpus; | ||
516 | } | ||
517 | |||
518 | evlist = perf_evlist__new(cpus, threads); | ||
519 | if (evlist == NULL) { | ||
520 | pr_debug("perf_evlist__new\n"); | ||
521 | goto out_free_cpus; | ||
522 | } | ||
523 | |||
524 | /* anonymous union fields, can't be initialized above */ | ||
525 | attr.wakeup_events = 1; | ||
526 | attr.sample_period = 1; | ||
527 | |||
528 | for (i = 0; i < nsyscalls; ++i) { | ||
529 | attr.config = ids[i]; | ||
530 | evsels[i] = perf_evsel__new(&attr, i); | ||
531 | if (evsels[i] == NULL) { | ||
532 | pr_debug("perf_evsel__new\n"); | ||
533 | goto out_free_evlist; | ||
534 | } | ||
535 | |||
536 | perf_evlist__add(evlist, evsels[i]); | ||
537 | |||
538 | if (perf_evsel__open(evsels[i], cpus, threads) < 0) { | ||
539 | pr_debug("failed to open counter: %s, " | ||
540 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
541 | strerror(errno)); | ||
542 | goto out_close_fd; | ||
543 | } | ||
544 | } | ||
545 | |||
546 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | ||
547 | pr_debug("failed to mmap events: %d (%s)\n", errno, | ||
548 | strerror(errno)); | ||
549 | goto out_close_fd; | ||
550 | } | ||
551 | |||
552 | for (i = 0; i < nsyscalls; ++i) | ||
553 | for (j = 0; j < expected_nr_events[i]; ++j) { | ||
554 | int foo = syscalls[i](); | ||
555 | ++foo; | ||
556 | } | ||
557 | |||
558 | while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { | ||
559 | struct perf_sample sample; | ||
560 | |||
561 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
562 | pr_debug("unexpected %s event\n", | ||
563 | perf_event__name(event->header.type)); | ||
564 | goto out_munmap; | ||
565 | } | ||
566 | |||
567 | err = perf_evlist__parse_sample(evlist, event, &sample); | ||
568 | if (err) { | ||
569 | pr_err("Can't parse sample, err = %d\n", err); | ||
570 | goto out_munmap; | ||
571 | } | ||
572 | |||
573 | evsel = perf_evlist__id2evsel(evlist, sample.id); | ||
574 | if (evsel == NULL) { | ||
575 | pr_debug("event with id %" PRIu64 | ||
576 | " doesn't map to an evsel\n", sample.id); | ||
577 | goto out_munmap; | ||
578 | } | ||
579 | nr_events[evsel->idx]++; | ||
580 | } | ||
581 | |||
582 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
583 | if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { | ||
584 | pr_debug("expected %d %s events, got %d\n", | ||
585 | expected_nr_events[evsel->idx], | ||
586 | perf_evsel__name(evsel), nr_events[evsel->idx]); | ||
587 | goto out_munmap; | ||
588 | } | ||
589 | } | ||
590 | |||
591 | err = 0; | ||
592 | out_munmap: | ||
593 | perf_evlist__munmap(evlist); | ||
594 | out_close_fd: | ||
595 | for (i = 0; i < nsyscalls; ++i) | ||
596 | perf_evsel__close_fd(evsels[i], 1, threads->nr); | ||
597 | out_free_evlist: | ||
598 | perf_evlist__delete(evlist); | ||
599 | out_free_cpus: | ||
600 | cpu_map__delete(cpus); | ||
601 | out_free_threads: | ||
602 | thread_map__delete(threads); | ||
603 | return err; | ||
604 | #undef nsyscalls | ||
605 | } | ||
606 | |||
607 | static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t **maskp, | ||
608 | size_t *sizep) | ||
609 | { | ||
610 | cpu_set_t *mask; | ||
611 | size_t size; | ||
612 | int i, cpu = -1, nrcpus = 1024; | ||
613 | realloc: | ||
614 | mask = CPU_ALLOC(nrcpus); | ||
615 | size = CPU_ALLOC_SIZE(nrcpus); | ||
616 | CPU_ZERO_S(size, mask); | ||
617 | |||
618 | if (sched_getaffinity(pid, size, mask) == -1) { | ||
619 | CPU_FREE(mask); | ||
620 | if (errno == EINVAL && nrcpus < (1024 << 8)) { | ||
621 | nrcpus = nrcpus << 2; | ||
622 | goto realloc; | ||
623 | } | ||
624 | perror("sched_getaffinity"); | ||
625 | return -1; | ||
626 | } | ||
627 | |||
628 | for (i = 0; i < nrcpus; i++) { | ||
629 | if (CPU_ISSET_S(i, size, mask)) { | ||
630 | if (cpu == -1) { | ||
631 | cpu = i; | ||
632 | *maskp = mask; | ||
633 | *sizep = size; | ||
634 | } else | ||
635 | CPU_CLR_S(i, size, mask); | ||
636 | } | ||
637 | } | ||
638 | |||
639 | if (cpu == -1) | ||
640 | CPU_FREE(mask); | ||
641 | |||
642 | return cpu; | ||
643 | } | ||
644 | |||
645 | static int test__PERF_RECORD(void) | ||
646 | { | ||
647 | struct perf_record_opts opts = { | ||
648 | .target = { | ||
649 | .uid = UINT_MAX, | ||
650 | .uses_mmap = true, | ||
651 | }, | ||
652 | .no_delay = true, | ||
653 | .freq = 10, | ||
654 | .mmap_pages = 256, | ||
655 | }; | ||
656 | cpu_set_t *cpu_mask = NULL; | ||
657 | size_t cpu_mask_size = 0; | ||
658 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
659 | struct perf_evsel *evsel; | ||
660 | struct perf_sample sample; | ||
661 | const char *cmd = "sleep"; | ||
662 | const char *argv[] = { cmd, "1", NULL, }; | ||
663 | char *bname; | ||
664 | u64 prev_time = 0; | ||
665 | bool found_cmd_mmap = false, | ||
666 | found_libc_mmap = false, | ||
667 | found_vdso_mmap = false, | ||
668 | found_ld_mmap = false; | ||
669 | int err = -1, errs = 0, i, wakeups = 0; | ||
670 | u32 cpu; | ||
671 | int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; | ||
672 | |||
673 | if (evlist == NULL || argv == NULL) { | ||
674 | pr_debug("Not enough memory to create evlist\n"); | ||
675 | goto out; | ||
676 | } | ||
677 | |||
678 | /* | ||
679 | * We need at least one evsel in the evlist, use the default | ||
680 | * one: "cycles". | ||
681 | */ | ||
682 | err = perf_evlist__add_default(evlist); | ||
683 | if (err < 0) { | ||
684 | pr_debug("Not enough memory to create evsel\n"); | ||
685 | goto out_delete_evlist; | ||
686 | } | ||
687 | |||
688 | /* | ||
689 | * Create maps of threads and cpus to monitor. In this case | ||
690 | * we start with all threads and cpus (-1, -1) but then in | ||
691 | * perf_evlist__prepare_workload we'll fill in the only thread | ||
692 | * we're monitoring, the one forked there. | ||
693 | */ | ||
694 | err = perf_evlist__create_maps(evlist, &opts.target); | ||
695 | if (err < 0) { | ||
696 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
697 | goto out_delete_evlist; | ||
698 | } | ||
699 | |||
700 | /* | ||
701 | * Prepare the workload in argv[] to run, it'll fork it, and then wait | ||
702 | * for perf_evlist__start_workload() to exec it. This is done this way | ||
703 | * so that we have time to open the evlist (calling sys_perf_event_open | ||
704 | * on all the fds) and then mmap them. | ||
705 | */ | ||
706 | err = perf_evlist__prepare_workload(evlist, &opts, argv); | ||
707 | if (err < 0) { | ||
708 | pr_debug("Couldn't run the workload!\n"); | ||
709 | goto out_delete_evlist; | ||
710 | } | ||
711 | |||
712 | /* | ||
713 | * Config the evsels, setting attr->comm on the first one, etc. | ||
714 | */ | ||
715 | evsel = perf_evlist__first(evlist); | ||
716 | evsel->attr.sample_type |= PERF_SAMPLE_CPU; | ||
717 | evsel->attr.sample_type |= PERF_SAMPLE_TID; | ||
718 | evsel->attr.sample_type |= PERF_SAMPLE_TIME; | ||
719 | perf_evlist__config_attrs(evlist, &opts); | ||
720 | |||
721 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask, | ||
722 | &cpu_mask_size); | ||
723 | if (err < 0) { | ||
724 | pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); | ||
725 | goto out_delete_evlist; | ||
726 | } | ||
727 | |||
728 | cpu = err; | ||
729 | |||
730 | /* | ||
731 | * So that we can check perf_sample.cpu on all the samples. | ||
732 | */ | ||
733 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, cpu_mask) < 0) { | ||
734 | pr_debug("sched_setaffinity: %s\n", strerror(errno)); | ||
735 | goto out_free_cpu_mask; | ||
736 | } | ||
737 | |||
738 | /* | ||
739 | * Call sys_perf_event_open on all the fds on all the evsels, | ||
740 | * grouping them if asked to. | ||
741 | */ | ||
742 | err = perf_evlist__open(evlist); | ||
743 | if (err < 0) { | ||
744 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | ||
745 | goto out_delete_evlist; | ||
746 | } | ||
747 | |||
748 | /* | ||
749 | * mmap the first fd on a given CPU and ask for events for the other | ||
750 | * fds in the same CPU to be injected in the same mmap ring buffer | ||
751 | * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)). | ||
752 | */ | ||
753 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | ||
754 | if (err < 0) { | ||
755 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | ||
756 | goto out_delete_evlist; | ||
757 | } | ||
758 | |||
759 | /* | ||
760 | * Now that all is properly set up, enable the events, they will | ||
761 | * count just on workload.pid, which will start... | ||
762 | */ | ||
763 | perf_evlist__enable(evlist); | ||
764 | |||
765 | /* | ||
766 | * Now! | ||
767 | */ | ||
768 | perf_evlist__start_workload(evlist); | ||
769 | |||
770 | while (1) { | ||
771 | int before = total_events; | ||
772 | |||
773 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
774 | union perf_event *event; | ||
775 | |||
776 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
777 | const u32 type = event->header.type; | ||
778 | const char *name = perf_event__name(type); | ||
779 | |||
780 | ++total_events; | ||
781 | if (type < PERF_RECORD_MAX) | ||
782 | nr_events[type]++; | ||
783 | |||
784 | err = perf_evlist__parse_sample(evlist, event, &sample); | ||
785 | if (err < 0) { | ||
786 | if (verbose) | ||
787 | perf_event__fprintf(event, stderr); | ||
788 | pr_debug("Couldn't parse sample\n"); | ||
789 | goto out_err; | ||
790 | } | ||
791 | |||
792 | if (verbose) { | ||
793 | pr_info("%" PRIu64" %d ", sample.time, sample.cpu); | ||
794 | perf_event__fprintf(event, stderr); | ||
795 | } | ||
796 | |||
797 | if (prev_time > sample.time) { | ||
798 | pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n", | ||
799 | name, prev_time, sample.time); | ||
800 | ++errs; | ||
801 | } | ||
802 | |||
803 | prev_time = sample.time; | ||
804 | |||
805 | if (sample.cpu != cpu) { | ||
806 | pr_debug("%s with unexpected cpu, expected %d, got %d\n", | ||
807 | name, cpu, sample.cpu); | ||
808 | ++errs; | ||
809 | } | ||
810 | |||
811 | if ((pid_t)sample.pid != evlist->workload.pid) { | ||
812 | pr_debug("%s with unexpected pid, expected %d, got %d\n", | ||
813 | name, evlist->workload.pid, sample.pid); | ||
814 | ++errs; | ||
815 | } | ||
816 | |||
817 | if ((pid_t)sample.tid != evlist->workload.pid) { | ||
818 | pr_debug("%s with unexpected tid, expected %d, got %d\n", | ||
819 | name, evlist->workload.pid, sample.tid); | ||
820 | ++errs; | ||
821 | } | ||
822 | |||
823 | if ((type == PERF_RECORD_COMM || | ||
824 | type == PERF_RECORD_MMAP || | ||
825 | type == PERF_RECORD_FORK || | ||
826 | type == PERF_RECORD_EXIT) && | ||
827 | (pid_t)event->comm.pid != evlist->workload.pid) { | ||
828 | pr_debug("%s with unexpected pid/tid\n", name); | ||
829 | ++errs; | ||
830 | } | ||
831 | |||
832 | if ((type == PERF_RECORD_COMM || | ||
833 | type == PERF_RECORD_MMAP) && | ||
834 | event->comm.pid != event->comm.tid) { | ||
835 | pr_debug("%s with different pid/tid!\n", name); | ||
836 | ++errs; | ||
837 | } | ||
838 | |||
839 | switch (type) { | ||
840 | case PERF_RECORD_COMM: | ||
841 | if (strcmp(event->comm.comm, cmd)) { | ||
842 | pr_debug("%s with unexpected comm!\n", name); | ||
843 | ++errs; | ||
844 | } | ||
845 | break; | ||
846 | case PERF_RECORD_EXIT: | ||
847 | goto found_exit; | ||
848 | case PERF_RECORD_MMAP: | ||
849 | bname = strrchr(event->mmap.filename, '/'); | ||
850 | if (bname != NULL) { | ||
851 | if (!found_cmd_mmap) | ||
852 | found_cmd_mmap = !strcmp(bname + 1, cmd); | ||
853 | if (!found_libc_mmap) | ||
854 | found_libc_mmap = !strncmp(bname + 1, "libc", 4); | ||
855 | if (!found_ld_mmap) | ||
856 | found_ld_mmap = !strncmp(bname + 1, "ld", 2); | ||
857 | } else if (!found_vdso_mmap) | ||
858 | found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]"); | ||
859 | break; | ||
860 | |||
861 | case PERF_RECORD_SAMPLE: | ||
862 | /* Just ignore samples for now */ | ||
863 | break; | ||
864 | default: | ||
865 | pr_debug("Unexpected perf_event->header.type %d!\n", | ||
866 | type); | ||
867 | ++errs; | ||
868 | } | ||
869 | } | ||
870 | } | ||
871 | |||
872 | /* | ||
873 | * We don't use poll here because at least at 3.1 times the | ||
874 | * PERF_RECORD_{!SAMPLE} events don't honour | ||
875 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. | ||
876 | */ | ||
877 | if (total_events == before && false) | ||
878 | poll(evlist->pollfd, evlist->nr_fds, -1); | ||
879 | |||
880 | sleep(1); | ||
881 | if (++wakeups > 5) { | ||
882 | pr_debug("No PERF_RECORD_EXIT event!\n"); | ||
883 | break; | ||
884 | } | ||
885 | } | ||
886 | |||
887 | found_exit: | ||
888 | if (nr_events[PERF_RECORD_COMM] > 1) { | ||
889 | pr_debug("Excessive number of PERF_RECORD_COMM events!\n"); | ||
890 | ++errs; | ||
891 | } | ||
892 | |||
893 | if (nr_events[PERF_RECORD_COMM] == 0) { | ||
894 | pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd); | ||
895 | ++errs; | ||
896 | } | ||
897 | |||
898 | if (!found_cmd_mmap) { | ||
899 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd); | ||
900 | ++errs; | ||
901 | } | ||
902 | |||
903 | if (!found_libc_mmap) { | ||
904 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc"); | ||
905 | ++errs; | ||
906 | } | ||
907 | |||
908 | if (!found_ld_mmap) { | ||
909 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld"); | ||
910 | ++errs; | ||
911 | } | ||
912 | |||
913 | if (!found_vdso_mmap) { | ||
914 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]"); | ||
915 | ++errs; | ||
916 | } | ||
917 | out_err: | ||
918 | perf_evlist__munmap(evlist); | ||
919 | out_free_cpu_mask: | ||
920 | CPU_FREE(cpu_mask); | ||
921 | out_delete_evlist: | ||
922 | perf_evlist__delete(evlist); | ||
923 | out: | ||
924 | return (err < 0 || errs > 0) ? -1 : 0; | ||
925 | } | ||
926 | |||
927 | |||
928 | #if defined(__x86_64__) || defined(__i386__) | ||
929 | |||
930 | #define barrier() asm volatile("" ::: "memory") | ||
931 | |||
932 | static u64 rdpmc(unsigned int counter) | ||
933 | { | ||
934 | unsigned int low, high; | ||
935 | |||
936 | asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); | ||
937 | |||
938 | return low | ((u64)high) << 32; | ||
939 | } | ||
940 | |||
941 | static u64 rdtsc(void) | ||
942 | { | ||
943 | unsigned int low, high; | ||
944 | |||
945 | asm volatile("rdtsc" : "=a" (low), "=d" (high)); | ||
946 | |||
947 | return low | ((u64)high) << 32; | ||
948 | } | ||
949 | |||
950 | static u64 mmap_read_self(void *addr) | ||
951 | { | ||
952 | struct perf_event_mmap_page *pc = addr; | ||
953 | u32 seq, idx, time_mult = 0, time_shift = 0; | ||
954 | u64 count, cyc = 0, time_offset = 0, enabled, running, delta; | ||
955 | |||
956 | do { | ||
957 | seq = pc->lock; | ||
958 | barrier(); | ||
959 | |||
960 | enabled = pc->time_enabled; | ||
961 | running = pc->time_running; | ||
962 | |||
963 | if (enabled != running) { | ||
964 | cyc = rdtsc(); | ||
965 | time_mult = pc->time_mult; | ||
966 | time_shift = pc->time_shift; | ||
967 | time_offset = pc->time_offset; | ||
968 | } | ||
969 | |||
970 | idx = pc->index; | ||
971 | count = pc->offset; | ||
972 | if (idx) | ||
973 | count += rdpmc(idx - 1); | ||
974 | |||
975 | barrier(); | ||
976 | } while (pc->lock != seq); | ||
977 | |||
978 | if (enabled != running) { | ||
979 | u64 quot, rem; | ||
980 | |||
981 | quot = (cyc >> time_shift); | ||
982 | rem = cyc & ((1 << time_shift) - 1); | ||
983 | delta = time_offset + quot * time_mult + | ||
984 | ((rem * time_mult) >> time_shift); | ||
985 | |||
986 | enabled += delta; | ||
987 | if (idx) | ||
988 | running += delta; | ||
989 | |||
990 | quot = count / running; | ||
991 | rem = count % running; | ||
992 | count = quot * enabled + (rem * enabled) / running; | ||
993 | } | ||
994 | |||
995 | return count; | ||
996 | } | ||
997 | |||
998 | /* | ||
999 | * If the RDPMC instruction faults then signal this back to the test parent task: | ||
1000 | */ | ||
1001 | static void segfault_handler(int sig __maybe_unused, | ||
1002 | siginfo_t *info __maybe_unused, | ||
1003 | void *uc __maybe_unused) | ||
1004 | { | ||
1005 | exit(-1); | ||
1006 | } | ||
1007 | |||
1008 | static int __test__rdpmc(void) | ||
1009 | { | ||
1010 | long page_size = sysconf(_SC_PAGE_SIZE); | ||
1011 | volatile int tmp = 0; | ||
1012 | u64 i, loops = 1000; | ||
1013 | int n; | ||
1014 | int fd; | ||
1015 | void *addr; | ||
1016 | struct perf_event_attr attr = { | ||
1017 | .type = PERF_TYPE_HARDWARE, | ||
1018 | .config = PERF_COUNT_HW_INSTRUCTIONS, | ||
1019 | .exclude_kernel = 1, | ||
1020 | }; | ||
1021 | u64 delta_sum = 0; | ||
1022 | struct sigaction sa; | ||
1023 | |||
1024 | sigfillset(&sa.sa_mask); | ||
1025 | sa.sa_sigaction = segfault_handler; | ||
1026 | sigaction(SIGSEGV, &sa, NULL); | ||
1027 | |||
1028 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | ||
1029 | if (fd < 0) { | ||
1030 | pr_err("Error: sys_perf_event_open() syscall returned " | ||
1031 | "with %d (%s)\n", fd, strerror(errno)); | ||
1032 | return -1; | ||
1033 | } | ||
1034 | |||
1035 | addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); | ||
1036 | if (addr == (void *)(-1)) { | ||
1037 | pr_err("Error: mmap() syscall returned with (%s)\n", | ||
1038 | strerror(errno)); | ||
1039 | goto out_close; | ||
1040 | } | ||
1041 | |||
1042 | for (n = 0; n < 6; n++) { | ||
1043 | u64 stamp, now, delta; | ||
1044 | |||
1045 | stamp = mmap_read_self(addr); | ||
1046 | |||
1047 | for (i = 0; i < loops; i++) | ||
1048 | tmp++; | ||
1049 | |||
1050 | now = mmap_read_self(addr); | ||
1051 | loops *= 10; | ||
1052 | |||
1053 | delta = now - stamp; | ||
1054 | pr_debug("%14d: %14Lu\n", n, (long long)delta); | ||
1055 | |||
1056 | delta_sum += delta; | ||
1057 | } | ||
1058 | |||
1059 | munmap(addr, page_size); | ||
1060 | pr_debug(" "); | ||
1061 | out_close: | ||
1062 | close(fd); | ||
1063 | |||
1064 | if (!delta_sum) | ||
1065 | return -1; | ||
1066 | |||
1067 | return 0; | ||
1068 | } | ||
1069 | |||
1070 | static int test__rdpmc(void) | ||
1071 | { | ||
1072 | int status = 0; | ||
1073 | int wret = 0; | ||
1074 | int ret; | ||
1075 | int pid; | ||
1076 | |||
1077 | pid = fork(); | ||
1078 | if (pid < 0) | ||
1079 | return -1; | ||
1080 | |||
1081 | if (!pid) { | ||
1082 | ret = __test__rdpmc(); | ||
1083 | |||
1084 | exit(ret); | ||
1085 | } | ||
1086 | |||
1087 | wret = waitpid(pid, &status, 0); | ||
1088 | if (wret < 0 || status) | ||
1089 | return -1; | ||
1090 | |||
1091 | return 0; | ||
1092 | } | ||
1093 | |||
1094 | #endif | ||
1095 | |||
1096 | static int test__perf_pmu(void) | ||
1097 | { | ||
1098 | return perf_pmu__test(); | ||
1099 | } | ||
1100 | |||
1101 | static int perf_evsel__roundtrip_cache_name_test(void) | ||
1102 | { | ||
1103 | char name[128]; | ||
1104 | int type, op, err = 0, ret = 0, i, idx; | ||
1105 | struct perf_evsel *evsel; | ||
1106 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
1107 | |||
1108 | if (evlist == NULL) | ||
1109 | return -ENOMEM; | ||
1110 | |||
1111 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
1112 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
1113 | /* skip invalid cache type */ | ||
1114 | if (!perf_evsel__is_cache_op_valid(type, op)) | ||
1115 | continue; | ||
1116 | |||
1117 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
1118 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | ||
1119 | name, sizeof(name)); | ||
1120 | err = parse_events(evlist, name, 0); | ||
1121 | if (err) | ||
1122 | ret = err; | ||
1123 | } | ||
1124 | } | ||
1125 | } | ||
1126 | |||
1127 | idx = 0; | ||
1128 | evsel = perf_evlist__first(evlist); | ||
1129 | |||
1130 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
1131 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
1132 | /* skip invalid cache type */ | ||
1133 | if (!perf_evsel__is_cache_op_valid(type, op)) | ||
1134 | continue; | ||
1135 | |||
1136 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
1137 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | ||
1138 | name, sizeof(name)); | ||
1139 | if (evsel->idx != idx) | ||
1140 | continue; | ||
1141 | |||
1142 | ++idx; | ||
1143 | |||
1144 | if (strcmp(perf_evsel__name(evsel), name)) { | ||
1145 | pr_debug("%s != %s\n", perf_evsel__name(evsel), name); | ||
1146 | ret = -1; | ||
1147 | } | ||
1148 | |||
1149 | evsel = perf_evsel__next(evsel); | ||
1150 | } | ||
1151 | } | ||
1152 | } | ||
1153 | |||
1154 | perf_evlist__delete(evlist); | ||
1155 | return ret; | ||
1156 | } | ||
1157 | |||
1158 | static int __perf_evsel__name_array_test(const char *names[], int nr_names) | ||
1159 | { | ||
1160 | int i, err; | ||
1161 | struct perf_evsel *evsel; | ||
1162 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
1163 | |||
1164 | if (evlist == NULL) | ||
1165 | return -ENOMEM; | ||
1166 | |||
1167 | for (i = 0; i < nr_names; ++i) { | ||
1168 | err = parse_events(evlist, names[i], 0); | ||
1169 | if (err) { | ||
1170 | pr_debug("failed to parse event '%s', err %d\n", | ||
1171 | names[i], err); | ||
1172 | goto out_delete_evlist; | ||
1173 | } | ||
1174 | } | ||
1175 | |||
1176 | err = 0; | ||
1177 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
1178 | if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) { | ||
1179 | --err; | ||
1180 | pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]); | ||
1181 | } | ||
1182 | } | ||
1183 | |||
1184 | out_delete_evlist: | ||
1185 | perf_evlist__delete(evlist); | ||
1186 | return err; | ||
1187 | } | ||
1188 | |||
1189 | #define perf_evsel__name_array_test(names) \ | ||
1190 | __perf_evsel__name_array_test(names, ARRAY_SIZE(names)) | ||
1191 | |||
1192 | static int perf_evsel__roundtrip_name_test(void) | ||
1193 | { | ||
1194 | int err = 0, ret = 0; | ||
1195 | |||
1196 | err = perf_evsel__name_array_test(perf_evsel__hw_names); | ||
1197 | if (err) | ||
1198 | ret = err; | ||
1199 | |||
1200 | err = perf_evsel__name_array_test(perf_evsel__sw_names); | ||
1201 | if (err) | ||
1202 | ret = err; | ||
1203 | |||
1204 | err = perf_evsel__roundtrip_cache_name_test(); | ||
1205 | if (err) | ||
1206 | ret = err; | ||
1207 | |||
1208 | return ret; | ||
1209 | } | ||
1210 | |||
1211 | static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, | ||
1212 | int size, bool should_be_signed) | ||
1213 | { | ||
1214 | struct format_field *field = perf_evsel__field(evsel, name); | ||
1215 | int is_signed; | ||
1216 | int ret = 0; | ||
1217 | |||
1218 | if (field == NULL) { | ||
1219 | pr_debug("%s: \"%s\" field not found!\n", evsel->name, name); | ||
1220 | return -1; | ||
1221 | } | ||
1222 | |||
1223 | is_signed = !!(field->flags | FIELD_IS_SIGNED); | ||
1224 | if (should_be_signed && !is_signed) { | ||
1225 | pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n", | ||
1226 | evsel->name, name, is_signed, should_be_signed); | ||
1227 | ret = -1; | ||
1228 | } | ||
1229 | |||
1230 | if (field->size != size) { | ||
1231 | pr_debug("%s: \"%s\" size (%d) should be %d!\n", | ||
1232 | evsel->name, name, field->size, size); | ||
1233 | ret = -1; | ||
1234 | } | ||
1235 | |||
1236 | return ret; | ||
1237 | } | ||
1238 | |||
1239 | static int perf_evsel__tp_sched_test(void) | ||
1240 | { | ||
1241 | struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch", 0); | ||
1242 | int ret = 0; | ||
1243 | |||
1244 | if (evsel == NULL) { | ||
1245 | pr_debug("perf_evsel__new\n"); | ||
1246 | return -1; | ||
1247 | } | ||
1248 | |||
1249 | if (perf_evsel__test_field(evsel, "prev_comm", 16, true)) | ||
1250 | ret = -1; | ||
1251 | |||
1252 | if (perf_evsel__test_field(evsel, "prev_pid", 4, true)) | ||
1253 | ret = -1; | ||
1254 | |||
1255 | if (perf_evsel__test_field(evsel, "prev_prio", 4, true)) | ||
1256 | ret = -1; | ||
1257 | |||
1258 | if (perf_evsel__test_field(evsel, "prev_state", 8, true)) | ||
1259 | ret = -1; | ||
1260 | |||
1261 | if (perf_evsel__test_field(evsel, "next_comm", 16, true)) | ||
1262 | ret = -1; | ||
1263 | |||
1264 | if (perf_evsel__test_field(evsel, "next_pid", 4, true)) | ||
1265 | ret = -1; | ||
1266 | |||
1267 | if (perf_evsel__test_field(evsel, "next_prio", 4, true)) | ||
1268 | ret = -1; | ||
1269 | |||
1270 | perf_evsel__delete(evsel); | ||
1271 | |||
1272 | evsel = perf_evsel__newtp("sched", "sched_wakeup", 0); | ||
1273 | |||
1274 | if (perf_evsel__test_field(evsel, "comm", 16, true)) | ||
1275 | ret = -1; | ||
1276 | |||
1277 | if (perf_evsel__test_field(evsel, "pid", 4, true)) | ||
1278 | ret = -1; | ||
1279 | |||
1280 | if (perf_evsel__test_field(evsel, "prio", 4, true)) | ||
1281 | ret = -1; | ||
1282 | |||
1283 | if (perf_evsel__test_field(evsel, "success", 4, true)) | ||
1284 | ret = -1; | ||
1285 | |||
1286 | if (perf_evsel__test_field(evsel, "target_cpu", 4, true)) | ||
1287 | ret = -1; | ||
1288 | |||
1289 | return ret; | ||
1290 | } | ||
1291 | |||
1292 | static int test__syscall_open_tp_fields(void) | ||
1293 | { | ||
1294 | struct perf_record_opts opts = { | ||
1295 | .target = { | ||
1296 | .uid = UINT_MAX, | ||
1297 | .uses_mmap = true, | ||
1298 | }, | ||
1299 | .no_delay = true, | ||
1300 | .freq = 1, | ||
1301 | .mmap_pages = 256, | ||
1302 | .raw_samples = true, | ||
1303 | }; | ||
1304 | const char *filename = "/etc/passwd"; | ||
1305 | int flags = O_RDONLY | O_DIRECTORY; | ||
1306 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
1307 | struct perf_evsel *evsel; | ||
1308 | int err = -1, i, nr_events = 0, nr_polls = 0; | ||
1309 | |||
1310 | if (evlist == NULL) { | ||
1311 | pr_debug("%s: perf_evlist__new\n", __func__); | ||
1312 | goto out; | ||
1313 | } | ||
1314 | |||
1315 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); | ||
1316 | if (evsel == NULL) { | ||
1317 | pr_debug("%s: perf_evsel__newtp\n", __func__); | ||
1318 | goto out_delete_evlist; | ||
1319 | } | ||
1320 | |||
1321 | perf_evlist__add(evlist, evsel); | ||
1322 | |||
1323 | err = perf_evlist__create_maps(evlist, &opts.target); | ||
1324 | if (err < 0) { | ||
1325 | pr_debug("%s: perf_evlist__create_maps\n", __func__); | ||
1326 | goto out_delete_evlist; | ||
1327 | } | ||
1328 | |||
1329 | perf_evsel__config(evsel, &opts, evsel); | ||
1330 | |||
1331 | evlist->threads->map[0] = getpid(); | ||
1332 | |||
1333 | err = perf_evlist__open(evlist); | ||
1334 | if (err < 0) { | ||
1335 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | ||
1336 | goto out_delete_evlist; | ||
1337 | } | ||
1338 | |||
1339 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | ||
1340 | if (err < 0) { | ||
1341 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | ||
1342 | goto out_delete_evlist; | ||
1343 | } | ||
1344 | |||
1345 | perf_evlist__enable(evlist); | ||
1346 | |||
1347 | /* | ||
1348 | * Generate the event: | ||
1349 | */ | ||
1350 | open(filename, flags); | ||
1351 | |||
1352 | while (1) { | ||
1353 | int before = nr_events; | ||
1354 | |||
1355 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
1356 | union perf_event *event; | ||
1357 | |||
1358 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
1359 | const u32 type = event->header.type; | ||
1360 | int tp_flags; | ||
1361 | struct perf_sample sample; | ||
1362 | |||
1363 | ++nr_events; | ||
1364 | |||
1365 | if (type != PERF_RECORD_SAMPLE) | ||
1366 | continue; | ||
1367 | |||
1368 | err = perf_evsel__parse_sample(evsel, event, &sample); | ||
1369 | if (err) { | ||
1370 | pr_err("Can't parse sample, err = %d\n", err); | ||
1371 | goto out_munmap; | ||
1372 | } | ||
1373 | |||
1374 | tp_flags = perf_evsel__intval(evsel, &sample, "flags"); | ||
1375 | |||
1376 | if (flags != tp_flags) { | ||
1377 | pr_debug("%s: Expected flags=%#x, got %#x\n", | ||
1378 | __func__, flags, tp_flags); | ||
1379 | goto out_munmap; | ||
1380 | } | ||
1381 | |||
1382 | goto out_ok; | ||
1383 | } | ||
1384 | } | ||
1385 | |||
1386 | if (nr_events == before) | ||
1387 | poll(evlist->pollfd, evlist->nr_fds, 10); | ||
1388 | |||
1389 | if (++nr_polls > 5) { | ||
1390 | pr_debug("%s: no events!\n", __func__); | ||
1391 | goto out_munmap; | ||
1392 | } | ||
1393 | } | ||
1394 | out_ok: | ||
1395 | err = 0; | ||
1396 | out_munmap: | ||
1397 | perf_evlist__munmap(evlist); | ||
1398 | out_delete_evlist: | ||
1399 | perf_evlist__delete(evlist); | ||
1400 | out: | ||
1401 | return err; | ||
1402 | } | ||
1403 | |||
1404 | static struct test { | ||
1405 | const char *desc; | ||
1406 | int (*func)(void); | ||
1407 | } tests[] = { | ||
1408 | { | ||
1409 | .desc = "vmlinux symtab matches kallsyms", | ||
1410 | .func = test__vmlinux_matches_kallsyms, | ||
1411 | }, | ||
1412 | { | ||
1413 | .desc = "detect open syscall event", | ||
1414 | .func = test__open_syscall_event, | ||
1415 | }, | ||
1416 | { | ||
1417 | .desc = "detect open syscall event on all cpus", | ||
1418 | .func = test__open_syscall_event_on_all_cpus, | ||
1419 | }, | ||
1420 | { | ||
1421 | .desc = "read samples using the mmap interface", | ||
1422 | .func = test__basic_mmap, | ||
1423 | }, | ||
1424 | { | ||
1425 | .desc = "parse events tests", | ||
1426 | .func = parse_events__test, | ||
1427 | }, | ||
1428 | #if defined(__x86_64__) || defined(__i386__) | ||
1429 | { | ||
1430 | .desc = "x86 rdpmc test", | ||
1431 | .func = test__rdpmc, | ||
1432 | }, | ||
1433 | #endif | ||
1434 | { | ||
1435 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", | ||
1436 | .func = test__PERF_RECORD, | ||
1437 | }, | ||
1438 | { | ||
1439 | .desc = "Test perf pmu format parsing", | ||
1440 | .func = test__perf_pmu, | ||
1441 | }, | ||
1442 | { | ||
1443 | .desc = "Test dso data interface", | ||
1444 | .func = dso__test_data, | ||
1445 | }, | ||
1446 | { | ||
1447 | .desc = "roundtrip evsel->name check", | ||
1448 | .func = perf_evsel__roundtrip_name_test, | ||
1449 | }, | ||
1450 | { | ||
1451 | .desc = "Check parsing of sched tracepoints fields", | ||
1452 | .func = perf_evsel__tp_sched_test, | ||
1453 | }, | ||
1454 | { | ||
1455 | .desc = "Generate and check syscalls:sys_enter_open event fields", | ||
1456 | .func = test__syscall_open_tp_fields, | ||
1457 | }, | ||
1458 | { | ||
1459 | .func = NULL, | ||
1460 | }, | ||
1461 | }; | ||
1462 | |||
1463 | static bool perf_test__matches(int curr, int argc, const char *argv[]) | ||
1464 | { | ||
1465 | int i; | ||
1466 | |||
1467 | if (argc == 0) | ||
1468 | return true; | ||
1469 | |||
1470 | for (i = 0; i < argc; ++i) { | ||
1471 | char *end; | ||
1472 | long nr = strtoul(argv[i], &end, 10); | ||
1473 | |||
1474 | if (*end == '\0') { | ||
1475 | if (nr == curr + 1) | ||
1476 | return true; | ||
1477 | continue; | ||
1478 | } | ||
1479 | |||
1480 | if (strstr(tests[curr].desc, argv[i])) | ||
1481 | return true; | ||
1482 | } | ||
1483 | |||
1484 | return false; | ||
1485 | } | ||
1486 | |||
1487 | static int __cmd_test(int argc, const char *argv[]) | ||
1488 | { | ||
1489 | int i = 0; | ||
1490 | |||
1491 | while (tests[i].func) { | ||
1492 | int curr = i++, err; | ||
1493 | |||
1494 | if (!perf_test__matches(curr, argc, argv)) | ||
1495 | continue; | ||
1496 | |||
1497 | pr_info("%2d: %s:", i, tests[curr].desc); | ||
1498 | pr_debug("\n--- start ---\n"); | ||
1499 | err = tests[curr].func(); | ||
1500 | pr_debug("---- end ----\n%s:", tests[curr].desc); | ||
1501 | pr_info(" %s\n", err ? "FAILED!\n" : "Ok"); | ||
1502 | } | ||
1503 | |||
1504 | return 0; | ||
1505 | } | ||
1506 | |||
1507 | static int perf_test__list(int argc, const char **argv) | ||
1508 | { | ||
1509 | int i = 0; | ||
1510 | |||
1511 | while (tests[i].func) { | ||
1512 | int curr = i++; | ||
1513 | |||
1514 | if (argc > 1 && !strstr(tests[curr].desc, argv[1])) | ||
1515 | continue; | ||
1516 | |||
1517 | pr_info("%2d: %s\n", i, tests[curr].desc); | ||
1518 | } | ||
1519 | |||
1520 | return 0; | ||
1521 | } | ||
1522 | |||
1523 | int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | ||
1524 | { | ||
1525 | const char * const test_usage[] = { | ||
1526 | "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]", | ||
1527 | NULL, | ||
1528 | }; | ||
1529 | const struct option test_options[] = { | ||
1530 | OPT_INCR('v', "verbose", &verbose, | ||
1531 | "be more verbose (show symbol address, etc)"), | ||
1532 | OPT_END() | ||
1533 | }; | ||
1534 | |||
1535 | argc = parse_options(argc, argv, test_options, test_usage, 0); | ||
1536 | if (argc >= 1 && !strcmp(argv[0], "list")) | ||
1537 | return perf_test__list(argc, argv); | ||
1538 | |||
1539 | symbol_conf.priv_size = sizeof(int); | ||
1540 | symbol_conf.sort_by_name = true; | ||
1541 | symbol_conf.try_vmlinux_path = true; | ||
1542 | |||
1543 | if (symbol__init() < 0) | ||
1544 | return -1; | ||
1545 | |||
1546 | return __cmd_test(argc, argv); | ||
1547 | } | ||
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index f251b613b2f3..ab4cf232b852 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -965,7 +965,7 @@ static void write_svg_file(const char *filename) | |||
965 | svg_close(); | 965 | svg_close(); |
966 | } | 966 | } |
967 | 967 | ||
968 | static int __cmd_timechart(const char *input_name, const char *output_name) | 968 | static int __cmd_timechart(const char *output_name) |
969 | { | 969 | { |
970 | struct perf_tool perf_timechart = { | 970 | struct perf_tool perf_timechart = { |
971 | .comm = process_comm_event, | 971 | .comm = process_comm_event, |
@@ -1061,7 +1061,6 @@ parse_process(const struct option *opt __maybe_unused, const char *arg, | |||
1061 | int cmd_timechart(int argc, const char **argv, | 1061 | int cmd_timechart(int argc, const char **argv, |
1062 | const char *prefix __maybe_unused) | 1062 | const char *prefix __maybe_unused) |
1063 | { | 1063 | { |
1064 | const char *input_name; | ||
1065 | const char *output_name = "output.svg"; | 1064 | const char *output_name = "output.svg"; |
1066 | const struct option options[] = { | 1065 | const struct option options[] = { |
1067 | OPT_STRING('i', "input", &input_name, "file", "input file name"), | 1066 | OPT_STRING('i', "input", &input_name, "file", "input file name"), |
@@ -1092,5 +1091,5 @@ int cmd_timechart(int argc, const char **argv, | |||
1092 | 1091 | ||
1093 | setup_pager(); | 1092 | setup_pager(); |
1094 | 1093 | ||
1095 | return __cmd_timechart(input_name, output_name); | 1094 | return __cmd_timechart(output_name); |
1096 | } | 1095 | } |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index ff6db8086805..72f6eb7b4173 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include "util/color.h" | 26 | #include "util/color.h" |
27 | #include "util/evlist.h" | 27 | #include "util/evlist.h" |
28 | #include "util/evsel.h" | 28 | #include "util/evsel.h" |
29 | #include "util/machine.h" | ||
29 | #include "util/session.h" | 30 | #include "util/session.h" |
30 | #include "util/symbol.h" | 31 | #include "util/symbol.h" |
31 | #include "util/thread.h" | 32 | #include "util/thread.h" |
@@ -67,27 +68,7 @@ | |||
67 | #include <linux/unistd.h> | 68 | #include <linux/unistd.h> |
68 | #include <linux/types.h> | 69 | #include <linux/types.h> |
69 | 70 | ||
70 | void get_term_dimensions(struct winsize *ws) | 71 | static volatile int done; |
71 | { | ||
72 | char *s = getenv("LINES"); | ||
73 | |||
74 | if (s != NULL) { | ||
75 | ws->ws_row = atoi(s); | ||
76 | s = getenv("COLUMNS"); | ||
77 | if (s != NULL) { | ||
78 | ws->ws_col = atoi(s); | ||
79 | if (ws->ws_row && ws->ws_col) | ||
80 | return; | ||
81 | } | ||
82 | } | ||
83 | #ifdef TIOCGWINSZ | ||
84 | if (ioctl(1, TIOCGWINSZ, ws) == 0 && | ||
85 | ws->ws_row && ws->ws_col) | ||
86 | return; | ||
87 | #endif | ||
88 | ws->ws_row = 25; | ||
89 | ws->ws_col = 80; | ||
90 | } | ||
91 | 72 | ||
92 | static void perf_top__update_print_entries(struct perf_top *top) | 73 | static void perf_top__update_print_entries(struct perf_top *top) |
93 | { | 74 | { |
@@ -452,8 +433,10 @@ static int perf_top__key_mapped(struct perf_top *top, int c) | |||
452 | return 0; | 433 | return 0; |
453 | } | 434 | } |
454 | 435 | ||
455 | static void perf_top__handle_keypress(struct perf_top *top, int c) | 436 | static bool perf_top__handle_keypress(struct perf_top *top, int c) |
456 | { | 437 | { |
438 | bool ret = true; | ||
439 | |||
457 | if (!perf_top__key_mapped(top, c)) { | 440 | if (!perf_top__key_mapped(top, c)) { |
458 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; | 441 | struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; |
459 | struct termios tc, save; | 442 | struct termios tc, save; |
@@ -474,7 +457,7 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) | |||
474 | 457 | ||
475 | tcsetattr(0, TCSAFLUSH, &save); | 458 | tcsetattr(0, TCSAFLUSH, &save); |
476 | if (!perf_top__key_mapped(top, c)) | 459 | if (!perf_top__key_mapped(top, c)) |
477 | return; | 460 | return ret; |
478 | } | 461 | } |
479 | 462 | ||
480 | switch (c) { | 463 | switch (c) { |
@@ -536,7 +519,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) | |||
536 | printf("exiting.\n"); | 519 | printf("exiting.\n"); |
537 | if (top->dump_symtab) | 520 | if (top->dump_symtab) |
538 | perf_session__fprintf_dsos(top->session, stderr); | 521 | perf_session__fprintf_dsos(top->session, stderr); |
539 | exit(0); | 522 | ret = false; |
523 | break; | ||
540 | case 's': | 524 | case 's': |
541 | perf_top__prompt_symbol(top, "Enter details symbol"); | 525 | perf_top__prompt_symbol(top, "Enter details symbol"); |
542 | break; | 526 | break; |
@@ -559,6 +543,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) | |||
559 | default: | 543 | default: |
560 | break; | 544 | break; |
561 | } | 545 | } |
546 | |||
547 | return ret; | ||
562 | } | 548 | } |
563 | 549 | ||
564 | static void perf_top__sort_new_samples(void *arg) | 550 | static void perf_top__sort_new_samples(void *arg) |
@@ -581,6 +567,11 @@ static void *display_thread_tui(void *arg) | |||
581 | struct perf_evsel *pos; | 567 | struct perf_evsel *pos; |
582 | struct perf_top *top = arg; | 568 | struct perf_top *top = arg; |
583 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; | 569 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; |
570 | struct hist_browser_timer hbt = { | ||
571 | .timer = perf_top__sort_new_samples, | ||
572 | .arg = top, | ||
573 | .refresh = top->delay_secs, | ||
574 | }; | ||
584 | 575 | ||
585 | perf_top__sort_new_samples(top); | 576 | perf_top__sort_new_samples(top); |
586 | 577 | ||
@@ -590,14 +581,12 @@ static void *display_thread_tui(void *arg) | |||
590 | * via --uid. | 581 | * via --uid. |
591 | */ | 582 | */ |
592 | list_for_each_entry(pos, &top->evlist->entries, node) | 583 | list_for_each_entry(pos, &top->evlist->entries, node) |
593 | pos->hists.uid_filter_str = top->target.uid_str; | 584 | pos->hists.uid_filter_str = top->record_opts.target.uid_str; |
594 | 585 | ||
595 | perf_evlist__tui_browse_hists(top->evlist, help, | 586 | perf_evlist__tui_browse_hists(top->evlist, help, &hbt, |
596 | perf_top__sort_new_samples, | 587 | &top->session->header.env); |
597 | top, top->delay_secs); | ||
598 | 588 | ||
599 | exit_browser(0); | 589 | done = 1; |
600 | exit(0); | ||
601 | return NULL; | 590 | return NULL; |
602 | } | 591 | } |
603 | 592 | ||
@@ -621,7 +610,7 @@ repeat: | |||
621 | /* trash return*/ | 610 | /* trash return*/ |
622 | getc(stdin); | 611 | getc(stdin); |
623 | 612 | ||
624 | while (1) { | 613 | while (!done) { |
625 | perf_top__print_sym_table(top); | 614 | perf_top__print_sym_table(top); |
626 | /* | 615 | /* |
627 | * Either timeout expired or we got an EINTR due to SIGWINCH, | 616 | * Either timeout expired or we got an EINTR due to SIGWINCH, |
@@ -635,15 +624,14 @@ repeat: | |||
635 | continue; | 624 | continue; |
636 | /* Fall trhu */ | 625 | /* Fall trhu */ |
637 | default: | 626 | default: |
638 | goto process_hotkey; | 627 | c = getc(stdin); |
628 | tcsetattr(0, TCSAFLUSH, &save); | ||
629 | |||
630 | if (perf_top__handle_keypress(top, c)) | ||
631 | goto repeat; | ||
632 | done = 1; | ||
639 | } | 633 | } |
640 | } | 634 | } |
641 | process_hotkey: | ||
642 | c = getc(stdin); | ||
643 | tcsetattr(0, TCSAFLUSH, &save); | ||
644 | |||
645 | perf_top__handle_keypress(top, c); | ||
646 | goto repeat; | ||
647 | 635 | ||
648 | return NULL; | 636 | return NULL; |
649 | } | 637 | } |
@@ -711,7 +699,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
711 | static struct intlist *seen; | 699 | static struct intlist *seen; |
712 | 700 | ||
713 | if (!seen) | 701 | if (!seen) |
714 | seen = intlist__new(); | 702 | seen = intlist__new(NULL); |
715 | 703 | ||
716 | if (!intlist__has_entry(seen, event->ip.pid)) { | 704 | if (!intlist__has_entry(seen, event->ip.pid)) { |
717 | pr_err("Can't find guest [%d]'s kernel information\n", | 705 | pr_err("Can't find guest [%d]'s kernel information\n", |
@@ -722,8 +710,8 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
722 | } | 710 | } |
723 | 711 | ||
724 | if (!machine) { | 712 | if (!machine) { |
725 | pr_err("%u unprocessable samples recorded.", | 713 | pr_err("%u unprocessable samples recorded.\r", |
726 | top->session->hists.stats.nr_unprocessable_samples++); | 714 | top->session->stats.nr_unprocessable_samples++); |
727 | return; | 715 | return; |
728 | } | 716 | } |
729 | 717 | ||
@@ -842,13 +830,13 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
842 | ++top->us_samples; | 830 | ++top->us_samples; |
843 | if (top->hide_user_symbols) | 831 | if (top->hide_user_symbols) |
844 | continue; | 832 | continue; |
845 | machine = perf_session__find_host_machine(session); | 833 | machine = &session->machines.host; |
846 | break; | 834 | break; |
847 | case PERF_RECORD_MISC_KERNEL: | 835 | case PERF_RECORD_MISC_KERNEL: |
848 | ++top->kernel_samples; | 836 | ++top->kernel_samples; |
849 | if (top->hide_kernel_symbols) | 837 | if (top->hide_kernel_symbols) |
850 | continue; | 838 | continue; |
851 | machine = perf_session__find_host_machine(session); | 839 | machine = &session->machines.host; |
852 | break; | 840 | break; |
853 | case PERF_RECORD_MISC_GUEST_KERNEL: | 841 | case PERF_RECORD_MISC_GUEST_KERNEL: |
854 | ++top->guest_kernel_samples; | 842 | ++top->guest_kernel_samples; |
@@ -871,9 +859,9 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
871 | &sample, machine); | 859 | &sample, machine); |
872 | } else if (event->header.type < PERF_RECORD_MAX) { | 860 | } else if (event->header.type < PERF_RECORD_MAX) { |
873 | hists__inc_nr_events(&evsel->hists, event->header.type); | 861 | hists__inc_nr_events(&evsel->hists, event->header.type); |
874 | perf_event__process(&top->tool, event, &sample, machine); | 862 | machine__process_event(machine, event); |
875 | } else | 863 | } else |
876 | ++session->hists.stats.nr_unknown_events; | 864 | ++session->stats.nr_unknown_events; |
877 | } | 865 | } |
878 | } | 866 | } |
879 | 867 | ||
@@ -885,119 +873,42 @@ static void perf_top__mmap_read(struct perf_top *top) | |||
885 | perf_top__mmap_read_idx(top, i); | 873 | perf_top__mmap_read_idx(top, i); |
886 | } | 874 | } |
887 | 875 | ||
888 | static void perf_top__start_counters(struct perf_top *top) | 876 | static int perf_top__start_counters(struct perf_top *top) |
889 | { | 877 | { |
878 | char msg[512]; | ||
890 | struct perf_evsel *counter; | 879 | struct perf_evsel *counter; |
891 | struct perf_evlist *evlist = top->evlist; | 880 | struct perf_evlist *evlist = top->evlist; |
881 | struct perf_record_opts *opts = &top->record_opts; | ||
892 | 882 | ||
893 | if (top->group) | 883 | perf_evlist__config(evlist, opts); |
894 | perf_evlist__set_leader(evlist); | ||
895 | 884 | ||
896 | list_for_each_entry(counter, &evlist->entries, node) { | 885 | list_for_each_entry(counter, &evlist->entries, node) { |
897 | struct perf_event_attr *attr = &counter->attr; | ||
898 | |||
899 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | ||
900 | |||
901 | if (top->freq) { | ||
902 | attr->sample_type |= PERF_SAMPLE_PERIOD; | ||
903 | attr->freq = 1; | ||
904 | attr->sample_freq = top->freq; | ||
905 | } | ||
906 | |||
907 | if (evlist->nr_entries > 1) { | ||
908 | attr->sample_type |= PERF_SAMPLE_ID; | ||
909 | attr->read_format |= PERF_FORMAT_ID; | ||
910 | } | ||
911 | |||
912 | if (perf_target__has_cpu(&top->target)) | ||
913 | attr->sample_type |= PERF_SAMPLE_CPU; | ||
914 | |||
915 | if (symbol_conf.use_callchain) | ||
916 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | ||
917 | |||
918 | attr->mmap = 1; | ||
919 | attr->comm = 1; | ||
920 | attr->inherit = top->inherit; | ||
921 | fallback_missing_features: | ||
922 | if (top->exclude_guest_missing) | ||
923 | attr->exclude_guest = attr->exclude_host = 0; | ||
924 | retry_sample_id: | ||
925 | attr->sample_id_all = top->sample_id_all_missing ? 0 : 1; | ||
926 | try_again: | 886 | try_again: |
927 | if (perf_evsel__open(counter, top->evlist->cpus, | 887 | if (perf_evsel__open(counter, top->evlist->cpus, |
928 | top->evlist->threads) < 0) { | 888 | top->evlist->threads) < 0) { |
929 | int err = errno; | 889 | if (perf_evsel__fallback(counter, errno, msg, sizeof(msg))) { |
930 | |||
931 | if (err == EPERM || err == EACCES) { | ||
932 | ui__error_paranoid(); | ||
933 | goto out_err; | ||
934 | } else if (err == EINVAL) { | ||
935 | if (!top->exclude_guest_missing && | ||
936 | (attr->exclude_guest || attr->exclude_host)) { | ||
937 | pr_debug("Old kernel, cannot exclude " | ||
938 | "guest or host samples.\n"); | ||
939 | top->exclude_guest_missing = true; | ||
940 | goto fallback_missing_features; | ||
941 | } else if (!top->sample_id_all_missing) { | ||
942 | /* | ||
943 | * Old kernel, no attr->sample_id_type_all field | ||
944 | */ | ||
945 | top->sample_id_all_missing = true; | ||
946 | goto retry_sample_id; | ||
947 | } | ||
948 | } | ||
949 | /* | ||
950 | * If it's cycles then fall back to hrtimer | ||
951 | * based cpu-clock-tick sw counter, which | ||
952 | * is always available even if no PMU support: | ||
953 | */ | ||
954 | if ((err == ENOENT || err == ENXIO) && | ||
955 | (attr->type == PERF_TYPE_HARDWARE) && | ||
956 | (attr->config == PERF_COUNT_HW_CPU_CYCLES)) { | ||
957 | |||
958 | if (verbose) | 890 | if (verbose) |
959 | ui__warning("Cycles event not supported,\n" | 891 | ui__warning("%s\n", msg); |
960 | "trying to fall back to cpu-clock-ticks\n"); | ||
961 | |||
962 | attr->type = PERF_TYPE_SOFTWARE; | ||
963 | attr->config = PERF_COUNT_SW_CPU_CLOCK; | ||
964 | if (counter->name) { | ||
965 | free(counter->name); | ||
966 | counter->name = NULL; | ||
967 | } | ||
968 | goto try_again; | 892 | goto try_again; |
969 | } | 893 | } |
970 | 894 | ||
971 | if (err == ENOENT) { | 895 | perf_evsel__open_strerror(counter, &opts->target, |
972 | ui__error("The %s event is not supported.\n", | 896 | errno, msg, sizeof(msg)); |
973 | perf_evsel__name(counter)); | 897 | ui__error("%s\n", msg); |
974 | goto out_err; | ||
975 | } else if (err == EMFILE) { | ||
976 | ui__error("Too many events are opened.\n" | ||
977 | "Try again after reducing the number of events\n"); | ||
978 | goto out_err; | ||
979 | } | ||
980 | |||
981 | ui__error("The sys_perf_event_open() syscall " | ||
982 | "returned with %d (%s). /bin/dmesg " | ||
983 | "may provide additional information.\n" | ||
984 | "No CONFIG_PERF_EVENTS=y kernel support " | ||
985 | "configured?\n", err, strerror(err)); | ||
986 | goto out_err; | 898 | goto out_err; |
987 | } | 899 | } |
988 | } | 900 | } |
989 | 901 | ||
990 | if (perf_evlist__mmap(evlist, top->mmap_pages, false) < 0) { | 902 | if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { |
991 | ui__error("Failed to mmap with %d (%s)\n", | 903 | ui__error("Failed to mmap with %d (%s)\n", |
992 | errno, strerror(errno)); | 904 | errno, strerror(errno)); |
993 | goto out_err; | 905 | goto out_err; |
994 | } | 906 | } |
995 | 907 | ||
996 | return; | 908 | return 0; |
997 | 909 | ||
998 | out_err: | 910 | out_err: |
999 | exit_browser(0); | 911 | return -1; |
1000 | exit(0); | ||
1001 | } | 912 | } |
1002 | 913 | ||
1003 | static int perf_top__setup_sample_type(struct perf_top *top) | 914 | static int perf_top__setup_sample_type(struct perf_top *top) |
@@ -1007,7 +918,7 @@ static int perf_top__setup_sample_type(struct perf_top *top) | |||
1007 | ui__error("Selected -g but \"sym\" not present in --sort/-s."); | 918 | ui__error("Selected -g but \"sym\" not present in --sort/-s."); |
1008 | return -EINVAL; | 919 | return -EINVAL; |
1009 | } | 920 | } |
1010 | } else if (!top->dont_use_callchains && callchain_param.mode != CHAIN_NONE) { | 921 | } else if (callchain_param.mode != CHAIN_NONE) { |
1011 | if (callchain_register_param(&callchain_param) < 0) { | 922 | if (callchain_register_param(&callchain_param) < 0) { |
1012 | ui__error("Can't register callchain params.\n"); | 923 | ui__error("Can't register callchain params.\n"); |
1013 | return -EINVAL; | 924 | return -EINVAL; |
@@ -1019,6 +930,7 @@ static int perf_top__setup_sample_type(struct perf_top *top) | |||
1019 | 930 | ||
1020 | static int __cmd_top(struct perf_top *top) | 931 | static int __cmd_top(struct perf_top *top) |
1021 | { | 932 | { |
933 | struct perf_record_opts *opts = &top->record_opts; | ||
1022 | pthread_t thread; | 934 | pthread_t thread; |
1023 | int ret; | 935 | int ret; |
1024 | /* | 936 | /* |
@@ -1033,26 +945,42 @@ static int __cmd_top(struct perf_top *top) | |||
1033 | if (ret) | 945 | if (ret) |
1034 | goto out_delete; | 946 | goto out_delete; |
1035 | 947 | ||
1036 | if (perf_target__has_task(&top->target)) | 948 | if (perf_target__has_task(&opts->target)) |
1037 | perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, | 949 | perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, |
1038 | perf_event__process, | 950 | perf_event__process, |
1039 | &top->session->host_machine); | 951 | &top->session->machines.host); |
1040 | else | 952 | else |
1041 | perf_event__synthesize_threads(&top->tool, perf_event__process, | 953 | perf_event__synthesize_threads(&top->tool, perf_event__process, |
1042 | &top->session->host_machine); | 954 | &top->session->machines.host); |
1043 | perf_top__start_counters(top); | 955 | |
956 | ret = perf_top__start_counters(top); | ||
957 | if (ret) | ||
958 | goto out_delete; | ||
959 | |||
1044 | top->session->evlist = top->evlist; | 960 | top->session->evlist = top->evlist; |
1045 | perf_session__set_id_hdr_size(top->session); | 961 | perf_session__set_id_hdr_size(top->session); |
1046 | 962 | ||
963 | /* | ||
964 | * When perf is starting the traced process, all the events (apart from | ||
965 | * group members) have enable_on_exec=1 set, so don't spoil it by | ||
966 | * prematurely enabling them. | ||
967 | * | ||
968 | * XXX 'top' still doesn't start workloads like record, trace, but should, | ||
969 | * so leave the check here. | ||
970 | */ | ||
971 | if (!perf_target__none(&opts->target)) | ||
972 | perf_evlist__enable(top->evlist); | ||
973 | |||
1047 | /* Wait for a minimal set of events before starting the snapshot */ | 974 | /* Wait for a minimal set of events before starting the snapshot */ |
1048 | poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 975 | poll(top->evlist->pollfd, top->evlist->nr_fds, 100); |
1049 | 976 | ||
1050 | perf_top__mmap_read(top); | 977 | perf_top__mmap_read(top); |
1051 | 978 | ||
979 | ret = -1; | ||
1052 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : | 980 | if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : |
1053 | display_thread), top)) { | 981 | display_thread), top)) { |
1054 | ui__error("Could not create display thread.\n"); | 982 | ui__error("Could not create display thread.\n"); |
1055 | exit(-1); | 983 | goto out_delete; |
1056 | } | 984 | } |
1057 | 985 | ||
1058 | if (top->realtime_prio) { | 986 | if (top->realtime_prio) { |
@@ -1061,11 +989,11 @@ static int __cmd_top(struct perf_top *top) | |||
1061 | param.sched_priority = top->realtime_prio; | 989 | param.sched_priority = top->realtime_prio; |
1062 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { | 990 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
1063 | ui__error("Could not set realtime priority.\n"); | 991 | ui__error("Could not set realtime priority.\n"); |
1064 | exit(-1); | 992 | goto out_delete; |
1065 | } | 993 | } |
1066 | } | 994 | } |
1067 | 995 | ||
1068 | while (1) { | 996 | while (!done) { |
1069 | u64 hits = top->samples; | 997 | u64 hits = top->samples; |
1070 | 998 | ||
1071 | perf_top__mmap_read(top); | 999 | perf_top__mmap_read(top); |
@@ -1074,126 +1002,67 @@ static int __cmd_top(struct perf_top *top) | |||
1074 | ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); | 1002 | ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); |
1075 | } | 1003 | } |
1076 | 1004 | ||
1005 | ret = 0; | ||
1077 | out_delete: | 1006 | out_delete: |
1078 | perf_session__delete(top->session); | 1007 | perf_session__delete(top->session); |
1079 | top->session = NULL; | 1008 | top->session = NULL; |
1080 | 1009 | ||
1081 | return 0; | 1010 | return ret; |
1082 | } | 1011 | } |
1083 | 1012 | ||
1084 | static int | 1013 | static int |
1085 | parse_callchain_opt(const struct option *opt, const char *arg, int unset) | 1014 | parse_callchain_opt(const struct option *opt, const char *arg, int unset) |
1086 | { | 1015 | { |
1087 | struct perf_top *top = (struct perf_top *)opt->value; | ||
1088 | char *tok, *tok2; | ||
1089 | char *endptr; | ||
1090 | |||
1091 | /* | 1016 | /* |
1092 | * --no-call-graph | 1017 | * --no-call-graph |
1093 | */ | 1018 | */ |
1094 | if (unset) { | 1019 | if (unset) |
1095 | top->dont_use_callchains = true; | ||
1096 | return 0; | 1020 | return 0; |
1097 | } | ||
1098 | 1021 | ||
1099 | symbol_conf.use_callchain = true; | 1022 | symbol_conf.use_callchain = true; |
1100 | 1023 | ||
1101 | if (!arg) | 1024 | return record_parse_callchain_opt(opt, arg, unset); |
1102 | return 0; | ||
1103 | |||
1104 | tok = strtok((char *)arg, ","); | ||
1105 | if (!tok) | ||
1106 | return -1; | ||
1107 | |||
1108 | /* get the output mode */ | ||
1109 | if (!strncmp(tok, "graph", strlen(arg))) | ||
1110 | callchain_param.mode = CHAIN_GRAPH_ABS; | ||
1111 | |||
1112 | else if (!strncmp(tok, "flat", strlen(arg))) | ||
1113 | callchain_param.mode = CHAIN_FLAT; | ||
1114 | |||
1115 | else if (!strncmp(tok, "fractal", strlen(arg))) | ||
1116 | callchain_param.mode = CHAIN_GRAPH_REL; | ||
1117 | |||
1118 | else if (!strncmp(tok, "none", strlen(arg))) { | ||
1119 | callchain_param.mode = CHAIN_NONE; | ||
1120 | symbol_conf.use_callchain = false; | ||
1121 | |||
1122 | return 0; | ||
1123 | } else | ||
1124 | return -1; | ||
1125 | |||
1126 | /* get the min percentage */ | ||
1127 | tok = strtok(NULL, ","); | ||
1128 | if (!tok) | ||
1129 | goto setup; | ||
1130 | |||
1131 | callchain_param.min_percent = strtod(tok, &endptr); | ||
1132 | if (tok == endptr) | ||
1133 | return -1; | ||
1134 | |||
1135 | /* get the print limit */ | ||
1136 | tok2 = strtok(NULL, ","); | ||
1137 | if (!tok2) | ||
1138 | goto setup; | ||
1139 | |||
1140 | if (tok2[0] != 'c') { | ||
1141 | callchain_param.print_limit = strtod(tok2, &endptr); | ||
1142 | tok2 = strtok(NULL, ","); | ||
1143 | if (!tok2) | ||
1144 | goto setup; | ||
1145 | } | ||
1146 | |||
1147 | /* get the call chain order */ | ||
1148 | if (!strcmp(tok2, "caller")) | ||
1149 | callchain_param.order = ORDER_CALLER; | ||
1150 | else if (!strcmp(tok2, "callee")) | ||
1151 | callchain_param.order = ORDER_CALLEE; | ||
1152 | else | ||
1153 | return -1; | ||
1154 | setup: | ||
1155 | if (callchain_register_param(&callchain_param) < 0) { | ||
1156 | fprintf(stderr, "Can't register callchain params\n"); | ||
1157 | return -1; | ||
1158 | } | ||
1159 | return 0; | ||
1160 | } | 1025 | } |
1161 | 1026 | ||
1162 | int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | 1027 | int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) |
1163 | { | 1028 | { |
1164 | struct perf_evsel *pos; | ||
1165 | int status; | 1029 | int status; |
1166 | char errbuf[BUFSIZ]; | 1030 | char errbuf[BUFSIZ]; |
1167 | struct perf_top top = { | 1031 | struct perf_top top = { |
1168 | .count_filter = 5, | 1032 | .count_filter = 5, |
1169 | .delay_secs = 2, | 1033 | .delay_secs = 2, |
1170 | .freq = 4000, /* 4 KHz */ | 1034 | .record_opts = { |
1171 | .mmap_pages = 128, | 1035 | .mmap_pages = UINT_MAX, |
1172 | .sym_pcnt_filter = 5, | 1036 | .user_freq = UINT_MAX, |
1173 | .target = { | 1037 | .user_interval = ULLONG_MAX, |
1174 | .uses_mmap = true, | 1038 | .freq = 4000, /* 4 KHz */ |
1039 | .target = { | ||
1040 | .uses_mmap = true, | ||
1041 | }, | ||
1175 | }, | 1042 | }, |
1043 | .sym_pcnt_filter = 5, | ||
1176 | }; | 1044 | }; |
1177 | char callchain_default_opt[] = "fractal,0.5,callee"; | 1045 | struct perf_record_opts *opts = &top.record_opts; |
1046 | struct perf_target *target = &opts->target; | ||
1178 | const struct option options[] = { | 1047 | const struct option options[] = { |
1179 | OPT_CALLBACK('e', "event", &top.evlist, "event", | 1048 | OPT_CALLBACK('e', "event", &top.evlist, "event", |
1180 | "event selector. use 'perf list' to list available events", | 1049 | "event selector. use 'perf list' to list available events", |
1181 | parse_events_option), | 1050 | parse_events_option), |
1182 | OPT_INTEGER('c', "count", &top.default_interval, | 1051 | OPT_U64('c', "count", &opts->user_interval, "event period to sample"), |
1183 | "event period to sample"), | 1052 | OPT_STRING('p', "pid", &target->pid, "pid", |
1184 | OPT_STRING('p', "pid", &top.target.pid, "pid", | ||
1185 | "profile events on existing process id"), | 1053 | "profile events on existing process id"), |
1186 | OPT_STRING('t', "tid", &top.target.tid, "tid", | 1054 | OPT_STRING('t', "tid", &target->tid, "tid", |
1187 | "profile events on existing thread id"), | 1055 | "profile events on existing thread id"), |
1188 | OPT_BOOLEAN('a', "all-cpus", &top.target.system_wide, | 1056 | OPT_BOOLEAN('a', "all-cpus", &target->system_wide, |
1189 | "system-wide collection from all CPUs"), | 1057 | "system-wide collection from all CPUs"), |
1190 | OPT_STRING('C', "cpu", &top.target.cpu_list, "cpu", | 1058 | OPT_STRING('C', "cpu", &target->cpu_list, "cpu", |
1191 | "list of cpus to monitor"), | 1059 | "list of cpus to monitor"), |
1192 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, | 1060 | OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, |
1193 | "file", "vmlinux pathname"), | 1061 | "file", "vmlinux pathname"), |
1194 | OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, | 1062 | OPT_BOOLEAN('K', "hide_kernel_symbols", &top.hide_kernel_symbols, |
1195 | "hide kernel symbols"), | 1063 | "hide kernel symbols"), |
1196 | OPT_UINTEGER('m', "mmap-pages", &top.mmap_pages, "number of mmap data pages"), | 1064 | OPT_UINTEGER('m', "mmap-pages", &opts->mmap_pages, |
1065 | "number of mmap data pages"), | ||
1197 | OPT_INTEGER('r', "realtime", &top.realtime_prio, | 1066 | OPT_INTEGER('r', "realtime", &top.realtime_prio, |
1198 | "collect data with this RT SCHED_FIFO priority"), | 1067 | "collect data with this RT SCHED_FIFO priority"), |
1199 | OPT_INTEGER('d', "delay", &top.delay_secs, | 1068 | OPT_INTEGER('d', "delay", &top.delay_secs, |
@@ -1202,16 +1071,14 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1202 | "dump the symbol table used for profiling"), | 1071 | "dump the symbol table used for profiling"), |
1203 | OPT_INTEGER('f', "count-filter", &top.count_filter, | 1072 | OPT_INTEGER('f', "count-filter", &top.count_filter, |
1204 | "only display functions with more events than this"), | 1073 | "only display functions with more events than this"), |
1205 | OPT_BOOLEAN('g', "group", &top.group, | 1074 | OPT_BOOLEAN('g', "group", &opts->group, |
1206 | "put the counters into a counter group"), | 1075 | "put the counters into a counter group"), |
1207 | OPT_BOOLEAN('i', "inherit", &top.inherit, | 1076 | OPT_BOOLEAN('i', "no-inherit", &opts->no_inherit, |
1208 | "child tasks inherit counters"), | 1077 | "child tasks do not inherit counters"), |
1209 | OPT_STRING(0, "sym-annotate", &top.sym_filter, "symbol name", | 1078 | OPT_STRING(0, "sym-annotate", &top.sym_filter, "symbol name", |
1210 | "symbol to annotate"), | 1079 | "symbol to annotate"), |
1211 | OPT_BOOLEAN('z', "zero", &top.zero, | 1080 | OPT_BOOLEAN('z', "zero", &top.zero, "zero history across updates"), |
1212 | "zero history across updates"), | 1081 | OPT_UINTEGER('F', "freq", &opts->user_freq, "profile at this frequency"), |
1213 | OPT_INTEGER('F', "freq", &top.freq, | ||
1214 | "profile at this frequency"), | ||
1215 | OPT_INTEGER('E', "entries", &top.print_entries, | 1082 | OPT_INTEGER('E', "entries", &top.print_entries, |
1216 | "display this many functions"), | 1083 | "display this many functions"), |
1217 | OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols, | 1084 | OPT_BOOLEAN('U', "hide_user_symbols", &top.hide_user_symbols, |
@@ -1224,10 +1091,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1224 | "sort by key(s): pid, comm, dso, symbol, parent"), | 1091 | "sort by key(s): pid, comm, dso, symbol, parent"), |
1225 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | 1092 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, |
1226 | "Show a column with the number of samples"), | 1093 | "Show a column with the number of samples"), |
1227 | OPT_CALLBACK_DEFAULT('G', "call-graph", &top, "output_type,min_percent, call_order", | 1094 | OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, |
1228 | "Display callchains using output_type (graph, flat, fractal, or none), min percent threshold and callchain order. " | 1095 | "mode[,dump_size]", record_callchain_help, |
1229 | "Default: fractal,0.5,callee", &parse_callchain_opt, | 1096 | &parse_callchain_opt, "fp"), |
1230 | callchain_default_opt), | ||
1231 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | 1097 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, |
1232 | "Show a column with the sum of periods"), | 1098 | "Show a column with the sum of periods"), |
1233 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 1099 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
@@ -1242,7 +1108,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1242 | "Display raw encoding of assembly instructions (default)"), | 1108 | "Display raw encoding of assembly instructions (default)"), |
1243 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 1109 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
1244 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 1110 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
1245 | OPT_STRING('u', "uid", &top.target.uid_str, "user", "user to profile"), | 1111 | OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), |
1246 | OPT_END() | 1112 | OPT_END() |
1247 | }; | 1113 | }; |
1248 | const char * const top_usage[] = { | 1114 | const char * const top_usage[] = { |
@@ -1263,7 +1129,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1263 | if (sort_order == default_sort_order) | 1129 | if (sort_order == default_sort_order) |
1264 | sort_order = "dso,symbol"; | 1130 | sort_order = "dso,symbol"; |
1265 | 1131 | ||
1266 | setup_sorting(top_usage, options); | 1132 | if (setup_sorting() < 0) |
1133 | usage_with_options(top_usage, options); | ||
1267 | 1134 | ||
1268 | if (top.use_stdio) | 1135 | if (top.use_stdio) |
1269 | use_browser = 0; | 1136 | use_browser = 0; |
@@ -1272,33 +1139,33 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1272 | 1139 | ||
1273 | setup_browser(false); | 1140 | setup_browser(false); |
1274 | 1141 | ||
1275 | status = perf_target__validate(&top.target); | 1142 | status = perf_target__validate(target); |
1276 | if (status) { | 1143 | if (status) { |
1277 | perf_target__strerror(&top.target, status, errbuf, BUFSIZ); | 1144 | perf_target__strerror(target, status, errbuf, BUFSIZ); |
1278 | ui__warning("%s", errbuf); | 1145 | ui__warning("%s", errbuf); |
1279 | } | 1146 | } |
1280 | 1147 | ||
1281 | status = perf_target__parse_uid(&top.target); | 1148 | status = perf_target__parse_uid(target); |
1282 | if (status) { | 1149 | if (status) { |
1283 | int saved_errno = errno; | 1150 | int saved_errno = errno; |
1284 | 1151 | ||
1285 | perf_target__strerror(&top.target, status, errbuf, BUFSIZ); | 1152 | perf_target__strerror(target, status, errbuf, BUFSIZ); |
1286 | ui__error("%s", errbuf); | 1153 | ui__error("%s", errbuf); |
1287 | 1154 | ||
1288 | status = -saved_errno; | 1155 | status = -saved_errno; |
1289 | goto out_delete_evlist; | 1156 | goto out_delete_evlist; |
1290 | } | 1157 | } |
1291 | 1158 | ||
1292 | if (perf_target__none(&top.target)) | 1159 | if (perf_target__none(target)) |
1293 | top.target.system_wide = true; | 1160 | target->system_wide = true; |
1294 | 1161 | ||
1295 | if (perf_evlist__create_maps(top.evlist, &top.target) < 0) | 1162 | if (perf_evlist__create_maps(top.evlist, target) < 0) |
1296 | usage_with_options(top_usage, options); | 1163 | usage_with_options(top_usage, options); |
1297 | 1164 | ||
1298 | if (!top.evlist->nr_entries && | 1165 | if (!top.evlist->nr_entries && |
1299 | perf_evlist__add_default(top.evlist) < 0) { | 1166 | perf_evlist__add_default(top.evlist) < 0) { |
1300 | ui__error("Not enough memory for event selector list\n"); | 1167 | ui__error("Not enough memory for event selector list\n"); |
1301 | return -ENOMEM; | 1168 | goto out_delete_maps; |
1302 | } | 1169 | } |
1303 | 1170 | ||
1304 | symbol_conf.nr_events = top.evlist->nr_entries; | 1171 | symbol_conf.nr_events = top.evlist->nr_entries; |
@@ -1306,24 +1173,22 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1306 | if (top.delay_secs < 1) | 1173 | if (top.delay_secs < 1) |
1307 | top.delay_secs = 1; | 1174 | top.delay_secs = 1; |
1308 | 1175 | ||
1176 | if (opts->user_interval != ULLONG_MAX) | ||
1177 | opts->default_interval = opts->user_interval; | ||
1178 | if (opts->user_freq != UINT_MAX) | ||
1179 | opts->freq = opts->user_freq; | ||
1180 | |||
1309 | /* | 1181 | /* |
1310 | * User specified count overrides default frequency. | 1182 | * User specified count overrides default frequency. |
1311 | */ | 1183 | */ |
1312 | if (top.default_interval) | 1184 | if (opts->default_interval) |
1313 | top.freq = 0; | 1185 | opts->freq = 0; |
1314 | else if (top.freq) { | 1186 | else if (opts->freq) { |
1315 | top.default_interval = top.freq; | 1187 | opts->default_interval = opts->freq; |
1316 | } else { | 1188 | } else { |
1317 | ui__error("frequency and count are zero, aborting\n"); | 1189 | ui__error("frequency and count are zero, aborting\n"); |
1318 | exit(EXIT_FAILURE); | 1190 | status = -EINVAL; |
1319 | } | 1191 | goto out_delete_maps; |
1320 | |||
1321 | list_for_each_entry(pos, &top.evlist->entries, node) { | ||
1322 | /* | ||
1323 | * Fill in the ones not specifically initialized via -c: | ||
1324 | */ | ||
1325 | if (!pos->attr.sample_period) | ||
1326 | pos->attr.sample_period = top.default_interval; | ||
1327 | } | 1192 | } |
1328 | 1193 | ||
1329 | top.sym_evsel = perf_evlist__first(top.evlist); | 1194 | top.sym_evsel = perf_evlist__first(top.evlist); |
@@ -1356,6 +1221,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1356 | 1221 | ||
1357 | status = __cmd_top(&top); | 1222 | status = __cmd_top(&top); |
1358 | 1223 | ||
1224 | out_delete_maps: | ||
1225 | perf_evlist__delete_maps(top.evlist); | ||
1359 | out_delete_evlist: | 1226 | out_delete_evlist: |
1360 | perf_evlist__delete(top.evlist); | 1227 | perf_evlist__delete(top.evlist); |
1361 | 1228 | ||
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7aaee39f6774..d222d7fc7e96 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -1,5 +1,8 @@ | |||
1 | #include "builtin.h" | 1 | #include "builtin.h" |
2 | #include "util/color.h" | ||
2 | #include "util/evlist.h" | 3 | #include "util/evlist.h" |
4 | #include "util/machine.h" | ||
5 | #include "util/thread.h" | ||
3 | #include "util/parse-options.h" | 6 | #include "util/parse-options.h" |
4 | #include "util/thread_map.h" | 7 | #include "util/thread_map.h" |
5 | #include "event-parse.h" | 8 | #include "event-parse.h" |
@@ -13,15 +16,18 @@ static struct syscall_fmt { | |||
13 | bool errmsg; | 16 | bool errmsg; |
14 | bool timeout; | 17 | bool timeout; |
15 | } syscall_fmts[] = { | 18 | } syscall_fmts[] = { |
19 | { .name = "access", .errmsg = true, }, | ||
16 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, | 20 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, |
17 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, | 21 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, |
18 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, | 22 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, |
19 | { .name = "futex", .errmsg = true, }, | 23 | { .name = "futex", .errmsg = true, }, |
24 | { .name = "open", .errmsg = true, }, | ||
20 | { .name = "poll", .errmsg = true, .timeout = true, }, | 25 | { .name = "poll", .errmsg = true, .timeout = true, }, |
21 | { .name = "ppoll", .errmsg = true, .timeout = true, }, | 26 | { .name = "ppoll", .errmsg = true, .timeout = true, }, |
22 | { .name = "read", .errmsg = true, }, | 27 | { .name = "read", .errmsg = true, }, |
23 | { .name = "recvfrom", .errmsg = true, }, | 28 | { .name = "recvfrom", .errmsg = true, }, |
24 | { .name = "select", .errmsg = true, .timeout = true, }, | 29 | { .name = "select", .errmsg = true, .timeout = true, }, |
30 | { .name = "socket", .errmsg = true, }, | ||
25 | { .name = "stat", .errmsg = true, .alias = "newstat", }, | 31 | { .name = "stat", .errmsg = true, .alias = "newstat", }, |
26 | }; | 32 | }; |
27 | 33 | ||
@@ -43,6 +49,57 @@ struct syscall { | |||
43 | struct syscall_fmt *fmt; | 49 | struct syscall_fmt *fmt; |
44 | }; | 50 | }; |
45 | 51 | ||
52 | static size_t fprintf_duration(unsigned long t, FILE *fp) | ||
53 | { | ||
54 | double duration = (double)t / NSEC_PER_MSEC; | ||
55 | size_t printed = fprintf(fp, "("); | ||
56 | |||
57 | if (duration >= 1.0) | ||
58 | printed += color_fprintf(fp, PERF_COLOR_RED, "%6.3f ms", duration); | ||
59 | else if (duration >= 0.01) | ||
60 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); | ||
61 | else | ||
62 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); | ||
63 | return printed + fprintf(stdout, "): "); | ||
64 | } | ||
65 | |||
66 | struct thread_trace { | ||
67 | u64 entry_time; | ||
68 | u64 exit_time; | ||
69 | bool entry_pending; | ||
70 | unsigned long nr_events; | ||
71 | char *entry_str; | ||
72 | double runtime_ms; | ||
73 | }; | ||
74 | |||
75 | static struct thread_trace *thread_trace__new(void) | ||
76 | { | ||
77 | return zalloc(sizeof(struct thread_trace)); | ||
78 | } | ||
79 | |||
80 | static struct thread_trace *thread__trace(struct thread *thread) | ||
81 | { | ||
82 | struct thread_trace *ttrace; | ||
83 | |||
84 | if (thread == NULL) | ||
85 | goto fail; | ||
86 | |||
87 | if (thread->priv == NULL) | ||
88 | thread->priv = thread_trace__new(); | ||
89 | |||
90 | if (thread->priv == NULL) | ||
91 | goto fail; | ||
92 | |||
93 | ttrace = thread->priv; | ||
94 | ++ttrace->nr_events; | ||
95 | |||
96 | return ttrace; | ||
97 | fail: | ||
98 | color_fprintf(stdout, PERF_COLOR_RED, | ||
99 | "WARNING: not enough memory, dropping samples!\n"); | ||
100 | return NULL; | ||
101 | } | ||
102 | |||
46 | struct trace { | 103 | struct trace { |
47 | int audit_machine; | 104 | int audit_machine; |
48 | struct { | 105 | struct { |
@@ -50,8 +107,96 @@ struct trace { | |||
50 | struct syscall *table; | 107 | struct syscall *table; |
51 | } syscalls; | 108 | } syscalls; |
52 | struct perf_record_opts opts; | 109 | struct perf_record_opts opts; |
110 | struct machine host; | ||
111 | u64 base_time; | ||
112 | unsigned long nr_events; | ||
113 | bool sched; | ||
114 | bool multiple_threads; | ||
115 | double duration_filter; | ||
116 | double runtime_ms; | ||
53 | }; | 117 | }; |
54 | 118 | ||
119 | static bool trace__filter_duration(struct trace *trace, double t) | ||
120 | { | ||
121 | return t < (trace->duration_filter * NSEC_PER_MSEC); | ||
122 | } | ||
123 | |||
124 | static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) | ||
125 | { | ||
126 | double ts = (double)(tstamp - trace->base_time) / NSEC_PER_MSEC; | ||
127 | |||
128 | return fprintf(fp, "%10.3f ", ts); | ||
129 | } | ||
130 | |||
131 | static bool done = false; | ||
132 | |||
133 | static void sig_handler(int sig __maybe_unused) | ||
134 | { | ||
135 | done = true; | ||
136 | } | ||
137 | |||
138 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, | ||
139 | u64 duration, u64 tstamp, FILE *fp) | ||
140 | { | ||
141 | size_t printed = trace__fprintf_tstamp(trace, tstamp, fp); | ||
142 | printed += fprintf_duration(duration, fp); | ||
143 | |||
144 | if (trace->multiple_threads) | ||
145 | printed += fprintf(fp, "%d ", thread->pid); | ||
146 | |||
147 | return printed; | ||
148 | } | ||
149 | |||
150 | static int trace__process_event(struct machine *machine, union perf_event *event) | ||
151 | { | ||
152 | int ret = 0; | ||
153 | |||
154 | switch (event->header.type) { | ||
155 | case PERF_RECORD_LOST: | ||
156 | color_fprintf(stdout, PERF_COLOR_RED, | ||
157 | "LOST %" PRIu64 " events!\n", event->lost.lost); | ||
158 | ret = machine__process_lost_event(machine, event); | ||
159 | default: | ||
160 | ret = machine__process_event(machine, event); | ||
161 | break; | ||
162 | } | ||
163 | |||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | static int trace__tool_process(struct perf_tool *tool __maybe_unused, | ||
168 | union perf_event *event, | ||
169 | struct perf_sample *sample __maybe_unused, | ||
170 | struct machine *machine) | ||
171 | { | ||
172 | return trace__process_event(machine, event); | ||
173 | } | ||
174 | |||
175 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | ||
176 | { | ||
177 | int err = symbol__init(); | ||
178 | |||
179 | if (err) | ||
180 | return err; | ||
181 | |||
182 | machine__init(&trace->host, "", HOST_KERNEL_ID); | ||
183 | machine__create_kernel_maps(&trace->host); | ||
184 | |||
185 | if (perf_target__has_task(&trace->opts.target)) { | ||
186 | err = perf_event__synthesize_thread_map(NULL, evlist->threads, | ||
187 | trace__tool_process, | ||
188 | &trace->host); | ||
189 | } else { | ||
190 | err = perf_event__synthesize_threads(NULL, trace__tool_process, | ||
191 | &trace->host); | ||
192 | } | ||
193 | |||
194 | if (err) | ||
195 | symbol__exit(); | ||
196 | |||
197 | return err; | ||
198 | } | ||
199 | |||
55 | static int trace__read_syscall_info(struct trace *trace, int id) | 200 | static int trace__read_syscall_info(struct trace *trace, int id) |
56 | { | 201 | { |
57 | char tp_name[128]; | 202 | char tp_name[128]; |
@@ -93,7 +238,8 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
93 | return sc->tp_format != NULL ? 0 : -1; | 238 | return sc->tp_format != NULL ? 0 : -1; |
94 | } | 239 | } |
95 | 240 | ||
96 | static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp) | 241 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, |
242 | unsigned long *args) | ||
97 | { | 243 | { |
98 | int i = 0; | 244 | int i = 0; |
99 | size_t printed = 0; | 245 | size_t printed = 0; |
@@ -102,12 +248,15 @@ static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FIL | |||
102 | struct format_field *field; | 248 | struct format_field *field; |
103 | 249 | ||
104 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | 250 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { |
105 | printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "", | 251 | printed += scnprintf(bf + printed, size - printed, |
106 | field->name, args[i++]); | 252 | "%s%s: %ld", printed ? ", " : "", |
253 | field->name, args[i++]); | ||
107 | } | 254 | } |
108 | } else { | 255 | } else { |
109 | while (i < 6) { | 256 | while (i < 6) { |
110 | printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]); | 257 | printed += scnprintf(bf + printed, size - printed, |
258 | "%sarg%d: %ld", | ||
259 | printed ? ", " : "", i, args[i]); | ||
111 | ++i; | 260 | ++i; |
112 | } | 261 | } |
113 | } | 262 | } |
@@ -139,17 +288,24 @@ static struct syscall *trace__syscall_info(struct trace *trace, | |||
139 | return &trace->syscalls.table[id]; | 288 | return &trace->syscalls.table[id]; |
140 | 289 | ||
141 | out_cant_read: | 290 | out_cant_read: |
142 | printf("Problems reading syscall %d information\n", id); | 291 | printf("Problems reading syscall %d", id); |
292 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) | ||
293 | printf("(%s)", trace->syscalls.table[id].name); | ||
294 | puts(" information"); | ||
143 | return NULL; | 295 | return NULL; |
144 | } | 296 | } |
145 | 297 | ||
146 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | 298 | static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, |
147 | struct perf_sample *sample) | 299 | struct perf_sample *sample) |
148 | { | 300 | { |
301 | char *msg; | ||
149 | void *args; | 302 | void *args; |
303 | size_t printed = 0; | ||
304 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
150 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 305 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
306 | struct thread_trace *ttrace = thread__trace(thread); | ||
151 | 307 | ||
152 | if (sc == NULL) | 308 | if (ttrace == NULL || sc == NULL) |
153 | return -1; | 309 | return -1; |
154 | 310 | ||
155 | args = perf_evsel__rawptr(evsel, sample, "args"); | 311 | args = perf_evsel__rawptr(evsel, sample, "args"); |
@@ -158,8 +314,27 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
158 | return -1; | 314 | return -1; |
159 | } | 315 | } |
160 | 316 | ||
161 | printf("%s(", sc->name); | 317 | ttrace = thread->priv; |
162 | syscall__fprintf_args(sc, args, stdout); | 318 | |
319 | if (ttrace->entry_str == NULL) { | ||
320 | ttrace->entry_str = malloc(1024); | ||
321 | if (!ttrace->entry_str) | ||
322 | return -1; | ||
323 | } | ||
324 | |||
325 | ttrace->entry_time = sample->time; | ||
326 | msg = ttrace->entry_str; | ||
327 | printed += scnprintf(msg + printed, 1024 - printed, "%s(", sc->name); | ||
328 | |||
329 | printed += syscall__scnprintf_args(sc, msg + printed, 1024 - printed, args); | ||
330 | |||
331 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | ||
332 | if (!trace->duration_filter) { | ||
333 | trace__fprintf_entry_head(trace, thread, 1, sample->time, stdout); | ||
334 | printf("%-70s\n", ttrace->entry_str); | ||
335 | } | ||
336 | } else | ||
337 | ttrace->entry_pending = true; | ||
163 | 338 | ||
164 | return 0; | 339 | return 0; |
165 | } | 340 | } |
@@ -168,13 +343,37 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
168 | struct perf_sample *sample) | 343 | struct perf_sample *sample) |
169 | { | 344 | { |
170 | int ret; | 345 | int ret; |
346 | u64 duration = 0; | ||
347 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
348 | struct thread_trace *ttrace = thread__trace(thread); | ||
171 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 349 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
172 | 350 | ||
173 | if (sc == NULL) | 351 | if (ttrace == NULL || sc == NULL) |
174 | return -1; | 352 | return -1; |
175 | 353 | ||
176 | ret = perf_evsel__intval(evsel, sample, "ret"); | 354 | ret = perf_evsel__intval(evsel, sample, "ret"); |
177 | 355 | ||
356 | ttrace = thread->priv; | ||
357 | |||
358 | ttrace->exit_time = sample->time; | ||
359 | |||
360 | if (ttrace->entry_time) { | ||
361 | duration = sample->time - ttrace->entry_time; | ||
362 | if (trace__filter_duration(trace, duration)) | ||
363 | goto out; | ||
364 | } else if (trace->duration_filter) | ||
365 | goto out; | ||
366 | |||
367 | trace__fprintf_entry_head(trace, thread, duration, sample->time, stdout); | ||
368 | |||
369 | if (ttrace->entry_pending) { | ||
370 | printf("%-70s", ttrace->entry_str); | ||
371 | } else { | ||
372 | printf(" ... ["); | ||
373 | color_fprintf(stdout, PERF_COLOR_YELLOW, "continued"); | ||
374 | printf("]: %s()", sc->name); | ||
375 | } | ||
376 | |||
178 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { | 377 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { |
179 | char bf[256]; | 378 | char bf[256]; |
180 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), | 379 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), |
@@ -187,14 +386,44 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
187 | printf(") = %d", ret); | 386 | printf(") = %d", ret); |
188 | 387 | ||
189 | putchar('\n'); | 388 | putchar('\n'); |
389 | out: | ||
390 | ttrace->entry_pending = false; | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, | ||
396 | struct perf_sample *sample) | ||
397 | { | ||
398 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | ||
399 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; | ||
400 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | ||
401 | struct thread_trace *ttrace = thread__trace(thread); | ||
402 | |||
403 | if (ttrace == NULL) | ||
404 | goto out_dump; | ||
405 | |||
406 | ttrace->runtime_ms += runtime_ms; | ||
407 | trace->runtime_ms += runtime_ms; | ||
408 | return 0; | ||
409 | |||
410 | out_dump: | ||
411 | printf("%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", | ||
412 | evsel->name, | ||
413 | perf_evsel__strval(evsel, sample, "comm"), | ||
414 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), | ||
415 | runtime, | ||
416 | perf_evsel__intval(evsel, sample, "vruntime")); | ||
190 | return 0; | 417 | return 0; |
191 | } | 418 | } |
192 | 419 | ||
193 | static int trace__run(struct trace *trace) | 420 | static int trace__run(struct trace *trace, int argc, const char **argv) |
194 | { | 421 | { |
195 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | 422 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); |
196 | struct perf_evsel *evsel; | 423 | struct perf_evsel *evsel; |
197 | int err = -1, i, nr_events = 0, before; | 424 | int err = -1, i; |
425 | unsigned long before; | ||
426 | const bool forks = argc > 0; | ||
198 | 427 | ||
199 | if (evlist == NULL) { | 428 | if (evlist == NULL) { |
200 | printf("Not enough memory to run!\n"); | 429 | printf("Not enough memory to run!\n"); |
@@ -207,13 +436,37 @@ static int trace__run(struct trace *trace) | |||
207 | goto out_delete_evlist; | 436 | goto out_delete_evlist; |
208 | } | 437 | } |
209 | 438 | ||
439 | if (trace->sched && | ||
440 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", | ||
441 | trace__sched_stat_runtime)) { | ||
442 | printf("Couldn't read the sched_stat_runtime tracepoint information!\n"); | ||
443 | goto out_delete_evlist; | ||
444 | } | ||
445 | |||
210 | err = perf_evlist__create_maps(evlist, &trace->opts.target); | 446 | err = perf_evlist__create_maps(evlist, &trace->opts.target); |
211 | if (err < 0) { | 447 | if (err < 0) { |
212 | printf("Problems parsing the target to trace, check your options!\n"); | 448 | printf("Problems parsing the target to trace, check your options!\n"); |
213 | goto out_delete_evlist; | 449 | goto out_delete_evlist; |
214 | } | 450 | } |
215 | 451 | ||
216 | perf_evlist__config_attrs(evlist, &trace->opts); | 452 | err = trace__symbols_init(trace, evlist); |
453 | if (err < 0) { | ||
454 | printf("Problems initializing symbol libraries!\n"); | ||
455 | goto out_delete_evlist; | ||
456 | } | ||
457 | |||
458 | perf_evlist__config(evlist, &trace->opts); | ||
459 | |||
460 | signal(SIGCHLD, sig_handler); | ||
461 | signal(SIGINT, sig_handler); | ||
462 | |||
463 | if (forks) { | ||
464 | err = perf_evlist__prepare_workload(evlist, &trace->opts, argv); | ||
465 | if (err < 0) { | ||
466 | printf("Couldn't run the workload!\n"); | ||
467 | goto out_delete_evlist; | ||
468 | } | ||
469 | } | ||
217 | 470 | ||
218 | err = perf_evlist__open(evlist); | 471 | err = perf_evlist__open(evlist); |
219 | if (err < 0) { | 472 | if (err < 0) { |
@@ -228,8 +481,13 @@ static int trace__run(struct trace *trace) | |||
228 | } | 481 | } |
229 | 482 | ||
230 | perf_evlist__enable(evlist); | 483 | perf_evlist__enable(evlist); |
484 | |||
485 | if (forks) | ||
486 | perf_evlist__start_workload(evlist); | ||
487 | |||
488 | trace->multiple_threads = evlist->threads->map[0] == -1 || evlist->threads->nr > 1; | ||
231 | again: | 489 | again: |
232 | before = nr_events; | 490 | before = trace->nr_events; |
233 | 491 | ||
234 | for (i = 0; i < evlist->nr_mmaps; i++) { | 492 | for (i = 0; i < evlist->nr_mmaps; i++) { |
235 | union perf_event *event; | 493 | union perf_event *event; |
@@ -239,19 +497,7 @@ again: | |||
239 | tracepoint_handler handler; | 497 | tracepoint_handler handler; |
240 | struct perf_sample sample; | 498 | struct perf_sample sample; |
241 | 499 | ||
242 | ++nr_events; | 500 | ++trace->nr_events; |
243 | |||
244 | switch (type) { | ||
245 | case PERF_RECORD_SAMPLE: | ||
246 | break; | ||
247 | case PERF_RECORD_LOST: | ||
248 | printf("LOST %" PRIu64 " events!\n", event->lost.lost); | ||
249 | continue; | ||
250 | default: | ||
251 | printf("Unexpected %s event, skipping...\n", | ||
252 | perf_event__name(type)); | ||
253 | continue; | ||
254 | } | ||
255 | 501 | ||
256 | err = perf_evlist__parse_sample(evlist, event, &sample); | 502 | err = perf_evlist__parse_sample(evlist, event, &sample); |
257 | if (err) { | 503 | if (err) { |
@@ -259,14 +505,26 @@ again: | |||
259 | continue; | 505 | continue; |
260 | } | 506 | } |
261 | 507 | ||
508 | if (trace->base_time == 0) | ||
509 | trace->base_time = sample.time; | ||
510 | |||
511 | if (type != PERF_RECORD_SAMPLE) { | ||
512 | trace__process_event(&trace->host, event); | ||
513 | continue; | ||
514 | } | ||
515 | |||
262 | evsel = perf_evlist__id2evsel(evlist, sample.id); | 516 | evsel = perf_evlist__id2evsel(evlist, sample.id); |
263 | if (evsel == NULL) { | 517 | if (evsel == NULL) { |
264 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); | 518 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); |
265 | continue; | 519 | continue; |
266 | } | 520 | } |
267 | 521 | ||
268 | if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1) | 522 | if (sample.raw_data == NULL) { |
269 | printf("%d ", sample.tid); | 523 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
524 | perf_evsel__name(evsel), sample.tid, | ||
525 | sample.cpu, sample.raw_size); | ||
526 | continue; | ||
527 | } | ||
270 | 528 | ||
271 | if (sample.raw_data == NULL) { | 529 | if (sample.raw_data == NULL) { |
272 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | 530 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
@@ -280,8 +538,15 @@ again: | |||
280 | } | 538 | } |
281 | } | 539 | } |
282 | 540 | ||
283 | if (nr_events == before) | 541 | if (trace->nr_events == before) { |
542 | if (done) | ||
543 | goto out_delete_evlist; | ||
544 | |||
284 | poll(evlist->pollfd, evlist->nr_fds, -1); | 545 | poll(evlist->pollfd, evlist->nr_fds, -1); |
546 | } | ||
547 | |||
548 | if (done) | ||
549 | perf_evlist__disable(evlist); | ||
285 | 550 | ||
286 | goto again; | 551 | goto again; |
287 | 552 | ||
@@ -291,10 +556,65 @@ out: | |||
291 | return err; | 556 | return err; |
292 | } | 557 | } |
293 | 558 | ||
559 | static size_t trace__fprintf_threads_header(FILE *fp) | ||
560 | { | ||
561 | size_t printed; | ||
562 | |||
563 | printed = fprintf(fp, "\n _____________________________________________________________________\n"); | ||
564 | printed += fprintf(fp," __) Summary of events (__\n\n"); | ||
565 | printed += fprintf(fp," [ task - pid ] [ events ] [ ratio ] [ runtime ]\n"); | ||
566 | printed += fprintf(fp," _____________________________________________________________________\n\n"); | ||
567 | |||
568 | return printed; | ||
569 | } | ||
570 | |||
571 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) | ||
572 | { | ||
573 | size_t printed = trace__fprintf_threads_header(fp); | ||
574 | struct rb_node *nd; | ||
575 | |||
576 | for (nd = rb_first(&trace->host.threads); nd; nd = rb_next(nd)) { | ||
577 | struct thread *thread = rb_entry(nd, struct thread, rb_node); | ||
578 | struct thread_trace *ttrace = thread->priv; | ||
579 | const char *color; | ||
580 | double ratio; | ||
581 | |||
582 | if (ttrace == NULL) | ||
583 | continue; | ||
584 | |||
585 | ratio = (double)ttrace->nr_events / trace->nr_events * 100.0; | ||
586 | |||
587 | color = PERF_COLOR_NORMAL; | ||
588 | if (ratio > 50.0) | ||
589 | color = PERF_COLOR_RED; | ||
590 | else if (ratio > 25.0) | ||
591 | color = PERF_COLOR_GREEN; | ||
592 | else if (ratio > 5.0) | ||
593 | color = PERF_COLOR_YELLOW; | ||
594 | |||
595 | printed += color_fprintf(fp, color, "%20s", thread->comm); | ||
596 | printed += fprintf(fp, " - %-5d :%11lu [", thread->pid, ttrace->nr_events); | ||
597 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); | ||
598 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); | ||
599 | } | ||
600 | |||
601 | return printed; | ||
602 | } | ||
603 | |||
604 | static int trace__set_duration(const struct option *opt, const char *str, | ||
605 | int unset __maybe_unused) | ||
606 | { | ||
607 | struct trace *trace = opt->value; | ||
608 | |||
609 | trace->duration_filter = atof(str); | ||
610 | return 0; | ||
611 | } | ||
612 | |||
294 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | 613 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) |
295 | { | 614 | { |
296 | const char * const trace_usage[] = { | 615 | const char * const trace_usage[] = { |
297 | "perf trace [<options>]", | 616 | "perf trace [<options>] [<command>]", |
617 | "perf trace [<options>] -- <command> [<options>]", | ||
298 | NULL | 618 | NULL |
299 | }; | 619 | }; |
300 | struct trace trace = { | 620 | struct trace trace = { |
@@ -328,21 +648,38 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
328 | "number of mmap data pages"), | 648 | "number of mmap data pages"), |
329 | OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user", | 649 | OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user", |
330 | "user to profile"), | 650 | "user to profile"), |
651 | OPT_CALLBACK(0, "duration", &trace, "float", | ||
652 | "show only events with duration > N.M ms", | ||
653 | trace__set_duration), | ||
654 | OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), | ||
331 | OPT_END() | 655 | OPT_END() |
332 | }; | 656 | }; |
333 | int err; | 657 | int err; |
658 | char bf[BUFSIZ]; | ||
334 | 659 | ||
335 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); | 660 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); |
336 | if (argc) | 661 | |
337 | usage_with_options(trace_usage, trace_options); | 662 | err = perf_target__validate(&trace.opts.target); |
663 | if (err) { | ||
664 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | ||
665 | printf("%s", bf); | ||
666 | return err; | ||
667 | } | ||
338 | 668 | ||
339 | err = perf_target__parse_uid(&trace.opts.target); | 669 | err = perf_target__parse_uid(&trace.opts.target); |
340 | if (err) { | 670 | if (err) { |
341 | char bf[BUFSIZ]; | ||
342 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 671 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
343 | printf("%s", bf); | 672 | printf("%s", bf); |
344 | return err; | 673 | return err; |
345 | } | 674 | } |
346 | 675 | ||
347 | return trace__run(&trace); | 676 | if (!argc && perf_target__none(&trace.opts.target)) |
677 | trace.opts.target.system_wide = true; | ||
678 | |||
679 | err = trace__run(&trace, argc, argv); | ||
680 | |||
681 | if (trace.sched && !err) | ||
682 | trace__fprintf_thread_summary(&trace, stdout); | ||
683 | |||
684 | return err; | ||
348 | } | 685 | } |
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index 4add41bb0c7e..b4eabb44e381 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak | |||
@@ -43,6 +43,15 @@ int main(void) | |||
43 | } | 43 | } |
44 | endef | 44 | endef |
45 | 45 | ||
46 | define SOURCE_BIONIC | ||
47 | #include <android/api-level.h> | ||
48 | |||
49 | int main(void) | ||
50 | { | ||
51 | return __ANDROID_API__; | ||
52 | } | ||
53 | endef | ||
54 | |||
46 | define SOURCE_ELF_MMAP | 55 | define SOURCE_ELF_MMAP |
47 | #include <libelf.h> | 56 | #include <libelf.h> |
48 | int main(void) | 57 | int main(void) |
@@ -112,7 +121,10 @@ define SOURCE_PYTHON_VERSION | |||
112 | #if PY_VERSION_HEX >= 0x03000000 | 121 | #if PY_VERSION_HEX >= 0x03000000 |
113 | #error | 122 | #error |
114 | #endif | 123 | #endif |
115 | int main(void){} | 124 | int main(void) |
125 | { | ||
126 | return 0; | ||
127 | } | ||
116 | endef | 128 | endef |
117 | define SOURCE_PYTHON_EMBED | 129 | define SOURCE_PYTHON_EMBED |
118 | #include <Python.h> | 130 | #include <Python.h> |
@@ -203,4 +215,24 @@ int main(void) | |||
203 | return audit_open(); | 215 | return audit_open(); |
204 | } | 216 | } |
205 | endef | 217 | endef |
206 | endif \ No newline at end of file | 218 | endif |
219 | |||
220 | define SOURCE_ON_EXIT | ||
221 | #include <stdio.h> | ||
222 | |||
223 | int main(void) | ||
224 | { | ||
225 | return on_exit(NULL, NULL); | ||
226 | } | ||
227 | endef | ||
228 | |||
229 | define SOURCE_LIBNUMA | ||
230 | #include <numa.h> | ||
231 | #include <numaif.h> | ||
232 | |||
233 | int main(void) | ||
234 | { | ||
235 | numa_available(); | ||
236 | return 0; | ||
237 | } | ||
238 | endef \ No newline at end of file | ||
diff --git a/tools/perf/config/utilities.mak b/tools/perf/config/utilities.mak index 8046182a19eb..8ef3bd30a549 100644 --- a/tools/perf/config/utilities.mak +++ b/tools/perf/config/utilities.mak | |||
@@ -13,7 +13,7 @@ newline := $(newline) | |||
13 | # what should replace a newline when escaping | 13 | # what should replace a newline when escaping |
14 | # newlines; the default is a bizarre string. | 14 | # newlines; the default is a bizarre string. |
15 | # | 15 | # |
16 | nl-escape = $(or $(1),m822df3020w6a44id34bt574ctac44eb9f4n) | 16 | nl-escape = $(if $(1),$(1),m822df3020w6a44id34bt574ctac44eb9f4n) |
17 | 17 | ||
18 | # escape-nl | 18 | # escape-nl |
19 | # | 19 | # |
@@ -173,16 +173,22 @@ _ge-abspath = $(if $(is-executable),$(1)) | |||
173 | # Usage: absolute-executable-path-or-empty = $(call get-executable-or-default,variable,default) | 173 | # Usage: absolute-executable-path-or-empty = $(call get-executable-or-default,variable,default) |
174 | # | 174 | # |
175 | define get-executable-or-default | 175 | define get-executable-or-default |
176 | $(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2))) | 176 | $(if $($(1)),$(call _ge_attempt,$($(1)),$(1)),$(call _ge_attempt,$(2),$(1))) |
177 | endef | 177 | endef |
178 | _ge_attempt = $(or $(get-executable),$(_gea_warn),$(call _gea_err,$(2))) | 178 | _ge_attempt = $(if $(get-executable),$(get-executable),$(_gea_warn)$(call _gea_err,$(2))) |
179 | _gea_warn = $(warning The path '$(1)' is not executable.) | 179 | _gea_warn = $(warning The path '$(1)' is not executable.) |
180 | _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) | 180 | _gea_err = $(if $(1),$(error Please set '$(1)' appropriately)) |
181 | 181 | ||
182 | # try-cc | 182 | # try-cc |
183 | # Usage: option = $(call try-cc, source-to-build, cc-options) | 183 | # Usage: option = $(call try-cc, source-to-build, cc-options, msg) |
184 | ifndef V | ||
185 | TRY_CC_OUTPUT= > /dev/null 2>&1 | ||
186 | endif | ||
187 | TRY_CC_MSG=echo " CHK $(3)" 1>&2; | ||
188 | |||
184 | try-cc = $(shell sh -c \ | 189 | try-cc = $(shell sh -c \ |
185 | 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \ | 190 | 'TMP="$(OUTPUT)$(TMPOUT).$$$$"; \ |
191 | $(TRY_CC_MSG) \ | ||
186 | echo "$(1)" | \ | 192 | echo "$(1)" | \ |
187 | $(CC) -x c - $(2) -o "$$TMP" > /dev/null 2>&1 && echo y; \ | 193 | $(CC) -x c - $(2) -o "$$TMP" $(TRY_CC_OUTPUT) && echo y; \ |
188 | rm -f "$$TMP"') | 194 | rm -f "$$TMP"') |
diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 6d50eb0b4251..095b88207cd3 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c | |||
@@ -24,6 +24,7 @@ const char perf_more_info_string[] = | |||
24 | 24 | ||
25 | int use_browser = -1; | 25 | int use_browser = -1; |
26 | static int use_pager = -1; | 26 | static int use_pager = -1; |
27 | const char *input_name; | ||
27 | 28 | ||
28 | struct cmd_struct { | 29 | struct cmd_struct { |
29 | const char *cmd; | 30 | const char *cmd; |
@@ -84,21 +85,26 @@ int check_pager_config(const char *cmd) | |||
84 | return c.val; | 85 | return c.val; |
85 | } | 86 | } |
86 | 87 | ||
87 | static int tui_command_config(const char *var, const char *value, void *data) | 88 | static int browser_command_config(const char *var, const char *value, void *data) |
88 | { | 89 | { |
89 | struct pager_config *c = data; | 90 | struct pager_config *c = data; |
90 | if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd)) | 91 | if (!prefixcmp(var, "tui.") && !strcmp(var + 4, c->cmd)) |
91 | c->val = perf_config_bool(var, value); | 92 | c->val = perf_config_bool(var, value); |
93 | if (!prefixcmp(var, "gtk.") && !strcmp(var + 4, c->cmd)) | ||
94 | c->val = perf_config_bool(var, value) ? 2 : 0; | ||
92 | return 0; | 95 | return 0; |
93 | } | 96 | } |
94 | 97 | ||
95 | /* returns 0 for "no tui", 1 for "use tui", and -1 for "not specified" */ | 98 | /* |
96 | static int check_tui_config(const char *cmd) | 99 | * returns 0 for "no tui", 1 for "use tui", 2 for "use gtk", |
100 | * and -1 for "not specified" | ||
101 | */ | ||
102 | static int check_browser_config(const char *cmd) | ||
97 | { | 103 | { |
98 | struct pager_config c; | 104 | struct pager_config c; |
99 | c.cmd = cmd; | 105 | c.cmd = cmd; |
100 | c.val = -1; | 106 | c.val = -1; |
101 | perf_config(tui_command_config, &c); | 107 | perf_config(browser_command_config, &c); |
102 | return c.val; | 108 | return c.val; |
103 | } | 109 | } |
104 | 110 | ||
@@ -301,7 +307,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
301 | prefix = NULL; /* setup_perf_directory(); */ | 307 | prefix = NULL; /* setup_perf_directory(); */ |
302 | 308 | ||
303 | if (use_browser == -1) | 309 | if (use_browser == -1) |
304 | use_browser = check_tui_config(p->cmd); | 310 | use_browser = check_browser_config(p->cmd); |
305 | 311 | ||
306 | if (use_pager == -1 && p->option & RUN_SETUP) | 312 | if (use_pager == -1 && p->option & RUN_SETUP) |
307 | use_pager = check_pager_config(p->cmd); | 313 | use_pager = check_pager_config(p->cmd); |
@@ -322,14 +328,23 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) | |||
322 | if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) | 328 | if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) |
323 | return 0; | 329 | return 0; |
324 | 330 | ||
331 | status = 1; | ||
325 | /* Check for ENOSPC and EIO errors.. */ | 332 | /* Check for ENOSPC and EIO errors.. */ |
326 | if (fflush(stdout)) | 333 | if (fflush(stdout)) { |
327 | die("write failure on standard output: %s", strerror(errno)); | 334 | fprintf(stderr, "write failure on standard output: %s", strerror(errno)); |
328 | if (ferror(stdout)) | 335 | goto out; |
329 | die("unknown write failure on standard output"); | 336 | } |
330 | if (fclose(stdout)) | 337 | if (ferror(stdout)) { |
331 | die("close failed on standard output: %s", strerror(errno)); | 338 | fprintf(stderr, "unknown write failure on standard output"); |
332 | return 0; | 339 | goto out; |
340 | } | ||
341 | if (fclose(stdout)) { | ||
342 | fprintf(stderr, "close failed on standard output: %s", strerror(errno)); | ||
343 | goto out; | ||
344 | } | ||
345 | status = 0; | ||
346 | out: | ||
347 | return status; | ||
333 | } | 348 | } |
334 | 349 | ||
335 | static void handle_internal_command(int argc, const char **argv) | 350 | static void handle_internal_command(int argc, const char **argv) |
@@ -440,6 +455,8 @@ int main(int argc, const char **argv) | |||
440 | { | 455 | { |
441 | const char *cmd; | 456 | const char *cmd; |
442 | 457 | ||
458 | page_size = sysconf(_SC_PAGE_SIZE); | ||
459 | |||
443 | cmd = perf_extract_argv0_path(argv[0]); | 460 | cmd = perf_extract_argv0_path(argv[0]); |
444 | if (!cmd) | 461 | if (!cmd) |
445 | cmd = "perf-help"; | 462 | cmd = "perf-help"; |
@@ -459,7 +476,8 @@ int main(int argc, const char **argv) | |||
459 | cmd += 5; | 476 | cmd += 5; |
460 | argv[0] = cmd; | 477 | argv[0] = cmd; |
461 | handle_internal_command(argc, argv); | 478 | handle_internal_command(argc, argv); |
462 | die("cannot handle %s internally", cmd); | 479 | fprintf(stderr, "cannot handle %s internally", cmd); |
480 | goto out; | ||
463 | } | 481 | } |
464 | 482 | ||
465 | /* Look for flags.. */ | 483 | /* Look for flags.. */ |
@@ -477,10 +495,12 @@ int main(int argc, const char **argv) | |||
477 | printf("\n usage: %s\n\n", perf_usage_string); | 495 | printf("\n usage: %s\n\n", perf_usage_string); |
478 | list_common_cmds_help(); | 496 | list_common_cmds_help(); |
479 | printf("\n %s\n\n", perf_more_info_string); | 497 | printf("\n %s\n\n", perf_more_info_string); |
480 | exit(1); | 498 | goto out; |
481 | } | 499 | } |
482 | cmd = argv[0]; | 500 | cmd = argv[0]; |
483 | 501 | ||
502 | test_attr__init(); | ||
503 | |||
484 | /* | 504 | /* |
485 | * We use PATH to find perf commands, but we prepend some higher | 505 | * We use PATH to find perf commands, but we prepend some higher |
486 | * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH | 506 | * precedence paths: the "--exec-path" option, the PERF_EXEC_PATH |
@@ -507,7 +527,7 @@ int main(int argc, const char **argv) | |||
507 | fprintf(stderr, "Expansion of alias '%s' failed; " | 527 | fprintf(stderr, "Expansion of alias '%s' failed; " |
508 | "'%s' is not a perf-command\n", | 528 | "'%s' is not a perf-command\n", |
509 | cmd, argv[0]); | 529 | cmd, argv[0]); |
510 | exit(1); | 530 | goto out; |
511 | } | 531 | } |
512 | if (!done_help) { | 532 | if (!done_help) { |
513 | cmd = argv[0] = help_unknown_cmd(cmd); | 533 | cmd = argv[0] = help_unknown_cmd(cmd); |
@@ -518,6 +538,6 @@ int main(int argc, const char **argv) | |||
518 | 538 | ||
519 | fprintf(stderr, "Failed to run command '%s': %s\n", | 539 | fprintf(stderr, "Failed to run command '%s': %s\n", |
520 | cmd, strerror(errno)); | 540 | cmd, strerror(errno)); |
521 | 541 | out: | |
522 | return 1; | 542 | return 1; |
523 | } | 543 | } |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index c50985eaec41..74659ecf93e0 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -1,12 +1,9 @@ | |||
1 | #ifndef _PERF_PERF_H | 1 | #ifndef _PERF_PERF_H |
2 | #define _PERF_PERF_H | 2 | #define _PERF_PERF_H |
3 | 3 | ||
4 | struct winsize; | 4 | #include <asm/unistd.h> |
5 | |||
6 | void get_term_dimensions(struct winsize *ws); | ||
7 | 5 | ||
8 | #if defined(__i386__) | 6 | #if defined(__i386__) |
9 | #include "../../arch/x86/include/asm/unistd.h" | ||
10 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | 7 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") |
11 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 8 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
12 | #define CPUINFO_PROC "model name" | 9 | #define CPUINFO_PROC "model name" |
@@ -16,7 +13,6 @@ void get_term_dimensions(struct winsize *ws); | |||
16 | #endif | 13 | #endif |
17 | 14 | ||
18 | #if defined(__x86_64__) | 15 | #if defined(__x86_64__) |
19 | #include "../../arch/x86/include/asm/unistd.h" | ||
20 | #define rmb() asm volatile("lfence" ::: "memory") | 16 | #define rmb() asm volatile("lfence" ::: "memory") |
21 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 17 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
22 | #define CPUINFO_PROC "model name" | 18 | #define CPUINFO_PROC "model name" |
@@ -26,20 +22,18 @@ void get_term_dimensions(struct winsize *ws); | |||
26 | #endif | 22 | #endif |
27 | 23 | ||
28 | #ifdef __powerpc__ | 24 | #ifdef __powerpc__ |
29 | #include "../../arch/powerpc/include/asm/unistd.h" | 25 | #include "../../arch/powerpc/include/uapi/asm/unistd.h" |
30 | #define rmb() asm volatile ("sync" ::: "memory") | 26 | #define rmb() asm volatile ("sync" ::: "memory") |
31 | #define cpu_relax() asm volatile ("" ::: "memory"); | 27 | #define cpu_relax() asm volatile ("" ::: "memory"); |
32 | #define CPUINFO_PROC "cpu" | 28 | #define CPUINFO_PROC "cpu" |
33 | #endif | 29 | #endif |
34 | 30 | ||
35 | #ifdef __s390__ | 31 | #ifdef __s390__ |
36 | #include "../../arch/s390/include/asm/unistd.h" | ||
37 | #define rmb() asm volatile("bcr 15,0" ::: "memory") | 32 | #define rmb() asm volatile("bcr 15,0" ::: "memory") |
38 | #define cpu_relax() asm volatile("" ::: "memory"); | 33 | #define cpu_relax() asm volatile("" ::: "memory"); |
39 | #endif | 34 | #endif |
40 | 35 | ||
41 | #ifdef __sh__ | 36 | #ifdef __sh__ |
42 | #include "../../arch/sh/include/asm/unistd.h" | ||
43 | #if defined(__SH4A__) || defined(__SH5__) | 37 | #if defined(__SH4A__) || defined(__SH5__) |
44 | # define rmb() asm volatile("synco" ::: "memory") | 38 | # define rmb() asm volatile("synco" ::: "memory") |
45 | #else | 39 | #else |
@@ -50,35 +44,30 @@ void get_term_dimensions(struct winsize *ws); | |||
50 | #endif | 44 | #endif |
51 | 45 | ||
52 | #ifdef __hppa__ | 46 | #ifdef __hppa__ |
53 | #include "../../arch/parisc/include/asm/unistd.h" | ||
54 | #define rmb() asm volatile("" ::: "memory") | 47 | #define rmb() asm volatile("" ::: "memory") |
55 | #define cpu_relax() asm volatile("" ::: "memory"); | 48 | #define cpu_relax() asm volatile("" ::: "memory"); |
56 | #define CPUINFO_PROC "cpu" | 49 | #define CPUINFO_PROC "cpu" |
57 | #endif | 50 | #endif |
58 | 51 | ||
59 | #ifdef __sparc__ | 52 | #ifdef __sparc__ |
60 | #include "../../arch/sparc/include/uapi/asm/unistd.h" | ||
61 | #define rmb() asm volatile("":::"memory") | 53 | #define rmb() asm volatile("":::"memory") |
62 | #define cpu_relax() asm volatile("":::"memory") | 54 | #define cpu_relax() asm volatile("":::"memory") |
63 | #define CPUINFO_PROC "cpu" | 55 | #define CPUINFO_PROC "cpu" |
64 | #endif | 56 | #endif |
65 | 57 | ||
66 | #ifdef __alpha__ | 58 | #ifdef __alpha__ |
67 | #include "../../arch/alpha/include/asm/unistd.h" | ||
68 | #define rmb() asm volatile("mb" ::: "memory") | 59 | #define rmb() asm volatile("mb" ::: "memory") |
69 | #define cpu_relax() asm volatile("" ::: "memory") | 60 | #define cpu_relax() asm volatile("" ::: "memory") |
70 | #define CPUINFO_PROC "cpu model" | 61 | #define CPUINFO_PROC "cpu model" |
71 | #endif | 62 | #endif |
72 | 63 | ||
73 | #ifdef __ia64__ | 64 | #ifdef __ia64__ |
74 | #include "../../arch/ia64/include/asm/unistd.h" | ||
75 | #define rmb() asm volatile ("mf" ::: "memory") | 65 | #define rmb() asm volatile ("mf" ::: "memory") |
76 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") | 66 | #define cpu_relax() asm volatile ("hint @pause" ::: "memory") |
77 | #define CPUINFO_PROC "model name" | 67 | #define CPUINFO_PROC "model name" |
78 | #endif | 68 | #endif |
79 | 69 | ||
80 | #ifdef __arm__ | 70 | #ifdef __arm__ |
81 | #include "../../arch/arm/include/asm/unistd.h" | ||
82 | /* | 71 | /* |
83 | * Use the __kuser_memory_barrier helper in the CPU helper page. See | 72 | * Use the __kuser_memory_barrier helper in the CPU helper page. See |
84 | * arch/arm/kernel/entry-armv.S in the kernel source for details. | 73 | * arch/arm/kernel/entry-armv.S in the kernel source for details. |
@@ -89,13 +78,11 @@ void get_term_dimensions(struct winsize *ws); | |||
89 | #endif | 78 | #endif |
90 | 79 | ||
91 | #ifdef __aarch64__ | 80 | #ifdef __aarch64__ |
92 | #include "../../arch/arm64/include/asm/unistd.h" | ||
93 | #define rmb() asm volatile("dmb ld" ::: "memory") | 81 | #define rmb() asm volatile("dmb ld" ::: "memory") |
94 | #define cpu_relax() asm volatile("yield" ::: "memory") | 82 | #define cpu_relax() asm volatile("yield" ::: "memory") |
95 | #endif | 83 | #endif |
96 | 84 | ||
97 | #ifdef __mips__ | 85 | #ifdef __mips__ |
98 | #include "../../arch/mips/include/asm/unistd.h" | ||
99 | #define rmb() asm volatile( \ | 86 | #define rmb() asm volatile( \ |
100 | ".set mips2\n\t" \ | 87 | ".set mips2\n\t" \ |
101 | "sync\n\t" \ | 88 | "sync\n\t" \ |
@@ -107,41 +94,27 @@ void get_term_dimensions(struct winsize *ws); | |||
107 | #define CPUINFO_PROC "cpu model" | 94 | #define CPUINFO_PROC "cpu model" |
108 | #endif | 95 | #endif |
109 | 96 | ||
97 | #ifdef __arc__ | ||
98 | #define rmb() asm volatile("" ::: "memory") | ||
99 | #define cpu_relax() rmb() | ||
100 | #define CPUINFO_PROC "Processor" | ||
101 | #endif | ||
102 | |||
103 | #ifdef __metag__ | ||
104 | #define rmb() asm volatile("" ::: "memory") | ||
105 | #define cpu_relax() asm volatile("" ::: "memory") | ||
106 | #define CPUINFO_PROC "CPU" | ||
107 | #endif | ||
108 | |||
110 | #include <time.h> | 109 | #include <time.h> |
111 | #include <unistd.h> | 110 | #include <unistd.h> |
112 | #include <sys/types.h> | 111 | #include <sys/types.h> |
113 | #include <sys/syscall.h> | 112 | #include <sys/syscall.h> |
114 | 113 | ||
115 | #include "../../include/uapi/linux/perf_event.h" | 114 | #include <linux/perf_event.h> |
116 | #include "util/types.h" | 115 | #include "util/types.h" |
117 | #include <stdbool.h> | 116 | #include <stdbool.h> |
118 | 117 | ||
119 | struct perf_mmap { | ||
120 | void *base; | ||
121 | int mask; | ||
122 | unsigned int prev; | ||
123 | }; | ||
124 | |||
125 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) | ||
126 | { | ||
127 | struct perf_event_mmap_page *pc = mm->base; | ||
128 | int head = pc->data_head; | ||
129 | rmb(); | ||
130 | return head; | ||
131 | } | ||
132 | |||
133 | static inline void perf_mmap__write_tail(struct perf_mmap *md, | ||
134 | unsigned long tail) | ||
135 | { | ||
136 | struct perf_event_mmap_page *pc = md->base; | ||
137 | |||
138 | /* | ||
139 | * ensure all reads are done before we write the tail out. | ||
140 | */ | ||
141 | /* mb(); */ | ||
142 | pc->data_tail = tail; | ||
143 | } | ||
144 | |||
145 | /* | 118 | /* |
146 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all | 119 | * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all |
147 | * counters in the current task. | 120 | * counters in the current task. |
@@ -174,13 +147,25 @@ static inline unsigned long long rdclock(void) | |||
174 | (void) (&_min1 == &_min2); \ | 147 | (void) (&_min1 == &_min2); \ |
175 | _min1 < _min2 ? _min1 : _min2; }) | 148 | _min1 < _min2 ? _min1 : _min2; }) |
176 | 149 | ||
150 | extern bool test_attr__enabled; | ||
151 | void test_attr__init(void); | ||
152 | void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, | ||
153 | int fd, int group_fd, unsigned long flags); | ||
154 | |||
177 | static inline int | 155 | static inline int |
178 | sys_perf_event_open(struct perf_event_attr *attr, | 156 | sys_perf_event_open(struct perf_event_attr *attr, |
179 | pid_t pid, int cpu, int group_fd, | 157 | pid_t pid, int cpu, int group_fd, |
180 | unsigned long flags) | 158 | unsigned long flags) |
181 | { | 159 | { |
182 | return syscall(__NR_perf_event_open, attr, pid, cpu, | 160 | int fd; |
183 | group_fd, flags); | 161 | |
162 | fd = syscall(__NR_perf_event_open, attr, pid, cpu, | ||
163 | group_fd, flags); | ||
164 | |||
165 | if (unlikely(test_attr__enabled)) | ||
166 | test_attr__open(attr, pid, cpu, fd, group_fd, flags); | ||
167 | |||
168 | return fd; | ||
184 | } | 169 | } |
185 | 170 | ||
186 | #define MAX_COUNTERS 256 | 171 | #define MAX_COUNTERS 256 |
@@ -208,6 +193,7 @@ struct branch_stack { | |||
208 | struct branch_entry entries[0]; | 193 | struct branch_entry entries[0]; |
209 | }; | 194 | }; |
210 | 195 | ||
196 | extern const char *input_name; | ||
211 | extern bool perf_host, perf_guest; | 197 | extern bool perf_host, perf_guest; |
212 | extern const char perf_version_string[]; | 198 | extern const char perf_version_string[]; |
213 | 199 | ||
@@ -233,8 +219,6 @@ struct perf_record_opts { | |||
233 | bool raw_samples; | 219 | bool raw_samples; |
234 | bool sample_address; | 220 | bool sample_address; |
235 | bool sample_time; | 221 | bool sample_time; |
236 | bool sample_id_all_missing; | ||
237 | bool exclude_guest_missing; | ||
238 | bool period; | 222 | bool period; |
239 | unsigned int freq; | 223 | unsigned int freq; |
240 | unsigned int mmap_pages; | 224 | unsigned int mmap_pages; |
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-record b/tools/perf/scripts/perl/bin/workqueue-stats-record deleted file mode 100644 index 8edda9078d5d..000000000000 --- a/tools/perf/scripts/perl/bin/workqueue-stats-record +++ /dev/null | |||
@@ -1,2 +0,0 @@ | |||
1 | #!/bin/bash | ||
2 | perf record -e workqueue:workqueue_creation -e workqueue:workqueue_destruction -e workqueue:workqueue_execution -e workqueue:workqueue_insertion $@ | ||
diff --git a/tools/perf/scripts/perl/bin/workqueue-stats-report b/tools/perf/scripts/perl/bin/workqueue-stats-report deleted file mode 100644 index 6d91411d248c..000000000000 --- a/tools/perf/scripts/perl/bin/workqueue-stats-report +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | #!/bin/bash | ||
2 | # description: workqueue stats (ins/exe/create/destroy) | ||
3 | perf script $@ -s "$PERF_EXEC_PATH"/scripts/perl/workqueue-stats.pl | ||
diff --git a/tools/perf/scripts/perl/rwtop.pl b/tools/perf/scripts/perl/rwtop.pl index 4bb3ecd33472..8b20787021c1 100644 --- a/tools/perf/scripts/perl/rwtop.pl +++ b/tools/perf/scripts/perl/rwtop.pl | |||
@@ -17,6 +17,7 @@ use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | |||
17 | use lib "./Perf-Trace-Util/lib"; | 17 | use lib "./Perf-Trace-Util/lib"; |
18 | use Perf::Trace::Core; | 18 | use Perf::Trace::Core; |
19 | use Perf::Trace::Util; | 19 | use Perf::Trace::Util; |
20 | use POSIX qw/SIGALRM SA_RESTART/; | ||
20 | 21 | ||
21 | my $default_interval = 3; | 22 | my $default_interval = 3; |
22 | my $nlines = 20; | 23 | my $nlines = 20; |
@@ -90,7 +91,10 @@ sub syscalls::sys_enter_write | |||
90 | 91 | ||
91 | sub trace_begin | 92 | sub trace_begin |
92 | { | 93 | { |
93 | $SIG{ALRM} = \&set_print_pending; | 94 | my $sa = POSIX::SigAction->new(\&set_print_pending); |
95 | $sa->flags(SA_RESTART); | ||
96 | $sa->safe(1); | ||
97 | POSIX::sigaction(SIGALRM, $sa) or die "Can't set SIGALRM handler: $!\n"; | ||
94 | alarm 1; | 98 | alarm 1; |
95 | } | 99 | } |
96 | 100 | ||
diff --git a/tools/perf/scripts/perl/workqueue-stats.pl b/tools/perf/scripts/perl/workqueue-stats.pl deleted file mode 100644 index a8eaff5119e0..000000000000 --- a/tools/perf/scripts/perl/workqueue-stats.pl +++ /dev/null | |||
@@ -1,129 +0,0 @@ | |||
1 | #!/usr/bin/perl -w | ||
2 | # (c) 2009, Tom Zanussi <tzanussi@gmail.com> | ||
3 | # Licensed under the terms of the GNU GPL License version 2 | ||
4 | |||
5 | # Displays workqueue stats | ||
6 | # | ||
7 | # Usage: | ||
8 | # | ||
9 | # perf record -c 1 -f -a -R -e workqueue:workqueue_creation -e | ||
10 | # workqueue:workqueue_destruction -e workqueue:workqueue_execution | ||
11 | # -e workqueue:workqueue_insertion | ||
12 | # | ||
13 | # perf script -p -s tools/perf/scripts/perl/workqueue-stats.pl | ||
14 | |||
15 | use 5.010000; | ||
16 | use strict; | ||
17 | use warnings; | ||
18 | |||
19 | use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib"; | ||
20 | use lib "./Perf-Trace-Util/lib"; | ||
21 | use Perf::Trace::Core; | ||
22 | use Perf::Trace::Util; | ||
23 | |||
24 | my @cpus; | ||
25 | |||
26 | sub workqueue::workqueue_destruction | ||
27 | { | ||
28 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
29 | $common_pid, $common_comm, | ||
30 | $thread_comm, $thread_pid) = @_; | ||
31 | |||
32 | $cpus[$common_cpu]{$thread_pid}{destroyed}++; | ||
33 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
34 | } | ||
35 | |||
36 | sub workqueue::workqueue_creation | ||
37 | { | ||
38 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
39 | $common_pid, $common_comm, | ||
40 | $thread_comm, $thread_pid, $cpu) = @_; | ||
41 | |||
42 | $cpus[$common_cpu]{$thread_pid}{created}++; | ||
43 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
44 | } | ||
45 | |||
46 | sub workqueue::workqueue_execution | ||
47 | { | ||
48 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
49 | $common_pid, $common_comm, | ||
50 | $thread_comm, $thread_pid, $func) = @_; | ||
51 | |||
52 | $cpus[$common_cpu]{$thread_pid}{executed}++; | ||
53 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
54 | } | ||
55 | |||
56 | sub workqueue::workqueue_insertion | ||
57 | { | ||
58 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
59 | $common_pid, $common_comm, | ||
60 | $thread_comm, $thread_pid, $func) = @_; | ||
61 | |||
62 | $cpus[$common_cpu]{$thread_pid}{inserted}++; | ||
63 | $cpus[$common_cpu]{$thread_pid}{comm} = $thread_comm; | ||
64 | } | ||
65 | |||
66 | sub trace_end | ||
67 | { | ||
68 | print "workqueue work stats:\n\n"; | ||
69 | my $cpu = 0; | ||
70 | printf("%3s %6s %6s\t%-20s\n", "cpu", "ins", "exec", "name"); | ||
71 | printf("%3s %6s %6s\t%-20s\n", "---", "---", "----", "----"); | ||
72 | foreach my $pidhash (@cpus) { | ||
73 | while ((my $pid, my $wqhash) = each %$pidhash) { | ||
74 | my $ins = $$wqhash{'inserted'} || 0; | ||
75 | my $exe = $$wqhash{'executed'} || 0; | ||
76 | my $comm = $$wqhash{'comm'} || ""; | ||
77 | if ($ins || $exe) { | ||
78 | printf("%3u %6u %6u\t%-20s\n", $cpu, $ins, $exe, $comm); | ||
79 | } | ||
80 | } | ||
81 | $cpu++; | ||
82 | } | ||
83 | |||
84 | $cpu = 0; | ||
85 | print "\nworkqueue lifecycle stats:\n\n"; | ||
86 | printf("%3s %6s %6s\t%-20s\n", "cpu", "created", "destroyed", "name"); | ||
87 | printf("%3s %6s %6s\t%-20s\n", "---", "-------", "---------", "----"); | ||
88 | foreach my $pidhash (@cpus) { | ||
89 | while ((my $pid, my $wqhash) = each %$pidhash) { | ||
90 | my $created = $$wqhash{'created'} || 0; | ||
91 | my $destroyed = $$wqhash{'destroyed'} || 0; | ||
92 | my $comm = $$wqhash{'comm'} || ""; | ||
93 | if ($created || $destroyed) { | ||
94 | printf("%3u %6u %6u\t%-20s\n", $cpu, $created, $destroyed, | ||
95 | $comm); | ||
96 | } | ||
97 | } | ||
98 | $cpu++; | ||
99 | } | ||
100 | |||
101 | print_unhandled(); | ||
102 | } | ||
103 | |||
104 | my %unhandled; | ||
105 | |||
106 | sub print_unhandled | ||
107 | { | ||
108 | if ((scalar keys %unhandled) == 0) { | ||
109 | return; | ||
110 | } | ||
111 | |||
112 | print "\nunhandled events:\n\n"; | ||
113 | |||
114 | printf("%-40s %10s\n", "event", "count"); | ||
115 | printf("%-40s %10s\n", "----------------------------------------", | ||
116 | "-----------"); | ||
117 | |||
118 | foreach my $event_name (keys %unhandled) { | ||
119 | printf("%-40s %10d\n", $event_name, $unhandled{$event_name}); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | sub trace_unhandled | ||
124 | { | ||
125 | my ($event_name, $context, $common_cpu, $common_secs, $common_nsecs, | ||
126 | $common_pid, $common_comm) = @_; | ||
127 | |||
128 | $unhandled{$event_name}++; | ||
129 | } | ||
diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c new file mode 100644 index 000000000000..bdcceb886f77 --- /dev/null +++ b/tools/perf/tests/attr.c | |||
@@ -0,0 +1,178 @@ | |||
1 | |||
2 | /* | ||
3 | * The struct perf_event_attr test support. | ||
4 | * | ||
5 | * This test is embedded inside into perf directly and is governed | ||
6 | * by the PERF_TEST_ATTR environment variable and hook inside | ||
7 | * sys_perf_event_open function. | ||
8 | * | ||
9 | * The general idea is to store 'struct perf_event_attr' details for | ||
10 | * each event created within single perf command. Each event details | ||
11 | * are stored into separate text file. Once perf command is finished | ||
12 | * these files can be checked for values we expect for command. | ||
13 | * | ||
14 | * Besides 'struct perf_event_attr' values we also store 'fd' and | ||
15 | * 'group_fd' values to allow checking for groups created. | ||
16 | * | ||
17 | * This all is triggered by setting PERF_TEST_ATTR environment variable. | ||
18 | * It must contain name of existing directory with access and write | ||
19 | * permissions. All the event text files are stored there. | ||
20 | */ | ||
21 | |||
22 | /* | ||
23 | * Powerpc needs __SANE_USERSPACE_TYPES__ before <linux/types.h> to select | ||
24 | * 'int-ll64.h' and avoid compile warnings when printing __u64 with %llu. | ||
25 | */ | ||
26 | #define __SANE_USERSPACE_TYPES__ | ||
27 | #include <stdlib.h> | ||
28 | #include <stdio.h> | ||
29 | #include <inttypes.h> | ||
30 | #include <linux/types.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include "../perf.h" | ||
33 | #include "util.h" | ||
34 | #include "exec_cmd.h" | ||
35 | #include "tests.h" | ||
36 | |||
37 | #define ENV "PERF_TEST_ATTR" | ||
38 | |||
39 | extern int verbose; | ||
40 | |||
41 | static char *dir; | ||
42 | |||
43 | void test_attr__init(void) | ||
44 | { | ||
45 | dir = getenv(ENV); | ||
46 | test_attr__enabled = (dir != NULL); | ||
47 | } | ||
48 | |||
49 | #define BUFSIZE 1024 | ||
50 | |||
51 | #define __WRITE_ASS(str, fmt, data) \ | ||
52 | do { \ | ||
53 | char buf[BUFSIZE]; \ | ||
54 | size_t size; \ | ||
55 | \ | ||
56 | size = snprintf(buf, BUFSIZE, #str "=%"fmt "\n", data); \ | ||
57 | if (1 != fwrite(buf, size, 1, file)) { \ | ||
58 | perror("test attr - failed to write event file"); \ | ||
59 | fclose(file); \ | ||
60 | return -1; \ | ||
61 | } \ | ||
62 | \ | ||
63 | } while (0) | ||
64 | |||
65 | #define WRITE_ASS(field, fmt) __WRITE_ASS(field, fmt, attr->field) | ||
66 | |||
67 | static int store_event(struct perf_event_attr *attr, pid_t pid, int cpu, | ||
68 | int fd, int group_fd, unsigned long flags) | ||
69 | { | ||
70 | FILE *file; | ||
71 | char path[PATH_MAX]; | ||
72 | |||
73 | snprintf(path, PATH_MAX, "%s/event-%d-%llu-%d", dir, | ||
74 | attr->type, attr->config, fd); | ||
75 | |||
76 | file = fopen(path, "w+"); | ||
77 | if (!file) { | ||
78 | perror("test attr - failed to open event file"); | ||
79 | return -1; | ||
80 | } | ||
81 | |||
82 | if (fprintf(file, "[event-%d-%llu-%d]\n", | ||
83 | attr->type, attr->config, fd) < 0) { | ||
84 | perror("test attr - failed to write event file"); | ||
85 | fclose(file); | ||
86 | return -1; | ||
87 | } | ||
88 | |||
89 | /* syscall arguments */ | ||
90 | __WRITE_ASS(fd, "d", fd); | ||
91 | __WRITE_ASS(group_fd, "d", group_fd); | ||
92 | __WRITE_ASS(cpu, "d", cpu); | ||
93 | __WRITE_ASS(pid, "d", pid); | ||
94 | __WRITE_ASS(flags, "lu", flags); | ||
95 | |||
96 | /* struct perf_event_attr */ | ||
97 | WRITE_ASS(type, PRIu32); | ||
98 | WRITE_ASS(size, PRIu32); | ||
99 | WRITE_ASS(config, "llu"); | ||
100 | WRITE_ASS(sample_period, "llu"); | ||
101 | WRITE_ASS(sample_type, "llu"); | ||
102 | WRITE_ASS(read_format, "llu"); | ||
103 | WRITE_ASS(disabled, "d"); | ||
104 | WRITE_ASS(inherit, "d"); | ||
105 | WRITE_ASS(pinned, "d"); | ||
106 | WRITE_ASS(exclusive, "d"); | ||
107 | WRITE_ASS(exclude_user, "d"); | ||
108 | WRITE_ASS(exclude_kernel, "d"); | ||
109 | WRITE_ASS(exclude_hv, "d"); | ||
110 | WRITE_ASS(exclude_idle, "d"); | ||
111 | WRITE_ASS(mmap, "d"); | ||
112 | WRITE_ASS(comm, "d"); | ||
113 | WRITE_ASS(freq, "d"); | ||
114 | WRITE_ASS(inherit_stat, "d"); | ||
115 | WRITE_ASS(enable_on_exec, "d"); | ||
116 | WRITE_ASS(task, "d"); | ||
117 | WRITE_ASS(watermark, "d"); | ||
118 | WRITE_ASS(precise_ip, "d"); | ||
119 | WRITE_ASS(mmap_data, "d"); | ||
120 | WRITE_ASS(sample_id_all, "d"); | ||
121 | WRITE_ASS(exclude_host, "d"); | ||
122 | WRITE_ASS(exclude_guest, "d"); | ||
123 | WRITE_ASS(exclude_callchain_kernel, "d"); | ||
124 | WRITE_ASS(exclude_callchain_user, "d"); | ||
125 | WRITE_ASS(wakeup_events, PRIu32); | ||
126 | WRITE_ASS(bp_type, PRIu32); | ||
127 | WRITE_ASS(config1, "llu"); | ||
128 | WRITE_ASS(config2, "llu"); | ||
129 | WRITE_ASS(branch_sample_type, "llu"); | ||
130 | WRITE_ASS(sample_regs_user, "llu"); | ||
131 | WRITE_ASS(sample_stack_user, PRIu32); | ||
132 | |||
133 | fclose(file); | ||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, | ||
138 | int fd, int group_fd, unsigned long flags) | ||
139 | { | ||
140 | int errno_saved = errno; | ||
141 | |||
142 | if (store_event(attr, pid, cpu, fd, group_fd, flags)) | ||
143 | die("test attr FAILED"); | ||
144 | |||
145 | errno = errno_saved; | ||
146 | } | ||
147 | |||
148 | static int run_dir(const char *d, const char *perf) | ||
149 | { | ||
150 | char cmd[3*PATH_MAX]; | ||
151 | |||
152 | snprintf(cmd, 3*PATH_MAX, PYTHON " %s/attr.py -d %s/attr/ -p %s %s", | ||
153 | d, d, perf, verbose ? "-v" : ""); | ||
154 | |||
155 | return system(cmd); | ||
156 | } | ||
157 | |||
158 | int test__attr(void) | ||
159 | { | ||
160 | struct stat st; | ||
161 | char path_perf[PATH_MAX]; | ||
162 | char path_dir[PATH_MAX]; | ||
163 | |||
164 | /* First try developement tree tests. */ | ||
165 | if (!lstat("./tests", &st)) | ||
166 | return run_dir("./tests", "./perf"); | ||
167 | |||
168 | /* Then installed path. */ | ||
169 | snprintf(path_dir, PATH_MAX, "%s/tests", perf_exec_path()); | ||
170 | snprintf(path_perf, PATH_MAX, "%s/perf", BINDIR); | ||
171 | |||
172 | if (!lstat(path_dir, &st) && | ||
173 | !lstat(path_perf, &st)) | ||
174 | return run_dir(path_dir, path_perf); | ||
175 | |||
176 | fprintf(stderr, " (ommitted)"); | ||
177 | return 0; | ||
178 | } | ||
diff --git a/tools/perf/tests/attr.py b/tools/perf/tests/attr.py new file mode 100644 index 000000000000..2f629ca485bc --- /dev/null +++ b/tools/perf/tests/attr.py | |||
@@ -0,0 +1,331 @@ | |||
1 | #! /usr/bin/python | ||
2 | |||
3 | import os | ||
4 | import sys | ||
5 | import glob | ||
6 | import optparse | ||
7 | import tempfile | ||
8 | import logging | ||
9 | import shutil | ||
10 | import ConfigParser | ||
11 | |||
12 | class Fail(Exception): | ||
13 | def __init__(self, test, msg): | ||
14 | self.msg = msg | ||
15 | self.test = test | ||
16 | def getMsg(self): | ||
17 | return '\'%s\' - %s' % (self.test.path, self.msg) | ||
18 | |||
19 | class Unsup(Exception): | ||
20 | def __init__(self, test): | ||
21 | self.test = test | ||
22 | def getMsg(self): | ||
23 | return '\'%s\'' % self.test.path | ||
24 | |||
25 | class Event(dict): | ||
26 | terms = [ | ||
27 | 'flags', | ||
28 | 'type', | ||
29 | 'size', | ||
30 | 'config', | ||
31 | 'sample_period', | ||
32 | 'sample_type', | ||
33 | 'read_format', | ||
34 | 'disabled', | ||
35 | 'inherit', | ||
36 | 'pinned', | ||
37 | 'exclusive', | ||
38 | 'exclude_user', | ||
39 | 'exclude_kernel', | ||
40 | 'exclude_hv', | ||
41 | 'exclude_idle', | ||
42 | 'mmap', | ||
43 | 'comm', | ||
44 | 'freq', | ||
45 | 'inherit_stat', | ||
46 | 'enable_on_exec', | ||
47 | 'task', | ||
48 | 'watermark', | ||
49 | 'precise_ip', | ||
50 | 'mmap_data', | ||
51 | 'sample_id_all', | ||
52 | 'exclude_host', | ||
53 | 'exclude_guest', | ||
54 | 'exclude_callchain_kernel', | ||
55 | 'exclude_callchain_user', | ||
56 | 'wakeup_events', | ||
57 | 'bp_type', | ||
58 | 'config1', | ||
59 | 'config2', | ||
60 | 'branch_sample_type', | ||
61 | 'sample_regs_user', | ||
62 | 'sample_stack_user', | ||
63 | ] | ||
64 | |||
65 | def add(self, data): | ||
66 | for key, val in data: | ||
67 | log.debug(" %s = %s" % (key, val)) | ||
68 | self[key] = val | ||
69 | |||
70 | def __init__(self, name, data, base): | ||
71 | log.debug(" Event %s" % name); | ||
72 | self.name = name; | ||
73 | self.group = '' | ||
74 | self.add(base) | ||
75 | self.add(data) | ||
76 | |||
77 | def compare_data(self, a, b): | ||
78 | # Allow multiple values in assignment separated by '|' | ||
79 | a_list = a.split('|') | ||
80 | b_list = b.split('|') | ||
81 | |||
82 | for a_item in a_list: | ||
83 | for b_item in b_list: | ||
84 | if (a_item == b_item): | ||
85 | return True | ||
86 | elif (a_item == '*') or (b_item == '*'): | ||
87 | return True | ||
88 | |||
89 | return False | ||
90 | |||
91 | def equal(self, other): | ||
92 | for t in Event.terms: | ||
93 | log.debug(" [%s] %s %s" % (t, self[t], other[t])); | ||
94 | if not self.has_key(t) or not other.has_key(t): | ||
95 | return False | ||
96 | if not self.compare_data(self[t], other[t]): | ||
97 | return False | ||
98 | return True | ||
99 | |||
100 | def diff(self, other): | ||
101 | for t in Event.terms: | ||
102 | if not self.has_key(t) or not other.has_key(t): | ||
103 | continue | ||
104 | if not self.compare_data(self[t], other[t]): | ||
105 | log.warning("expected %s=%s, got %s" % (t, self[t], other[t])) | ||
106 | |||
107 | |||
108 | # Test file description needs to have following sections: | ||
109 | # [config] | ||
110 | # - just single instance in file | ||
111 | # - needs to specify: | ||
112 | # 'command' - perf command name | ||
113 | # 'args' - special command arguments | ||
114 | # 'ret' - expected command return value (0 by default) | ||
115 | # | ||
116 | # [eventX:base] | ||
117 | # - one or multiple instances in file | ||
118 | # - expected values assignments | ||
119 | class Test(object): | ||
120 | def __init__(self, path, options): | ||
121 | parser = ConfigParser.SafeConfigParser() | ||
122 | parser.read(path) | ||
123 | |||
124 | log.debug("running '%s'" % path) | ||
125 | |||
126 | self.path = path | ||
127 | self.test_dir = options.test_dir | ||
128 | self.perf = options.perf | ||
129 | self.command = parser.get('config', 'command') | ||
130 | self.args = parser.get('config', 'args') | ||
131 | |||
132 | try: | ||
133 | self.ret = parser.get('config', 'ret') | ||
134 | except: | ||
135 | self.ret = 0 | ||
136 | |||
137 | self.expect = {} | ||
138 | self.result = {} | ||
139 | log.debug(" loading expected events"); | ||
140 | self.load_events(path, self.expect) | ||
141 | |||
142 | def is_event(self, name): | ||
143 | if name.find("event") == -1: | ||
144 | return False | ||
145 | else: | ||
146 | return True | ||
147 | |||
148 | def load_events(self, path, events): | ||
149 | parser_event = ConfigParser.SafeConfigParser() | ||
150 | parser_event.read(path) | ||
151 | |||
152 | # The event record section header contains 'event' word, | ||
153 | # optionaly followed by ':' allowing to load 'parent | ||
154 | # event' first as a base | ||
155 | for section in filter(self.is_event, parser_event.sections()): | ||
156 | |||
157 | parser_items = parser_event.items(section); | ||
158 | base_items = {} | ||
159 | |||
160 | # Read parent event if there's any | ||
161 | if (':' in section): | ||
162 | base = section[section.index(':') + 1:] | ||
163 | parser_base = ConfigParser.SafeConfigParser() | ||
164 | parser_base.read(self.test_dir + '/' + base) | ||
165 | base_items = parser_base.items('event') | ||
166 | |||
167 | e = Event(section, parser_items, base_items) | ||
168 | events[section] = e | ||
169 | |||
170 | def run_cmd(self, tempdir): | ||
171 | cmd = "PERF_TEST_ATTR=%s %s %s -o %s/perf.data %s" % (tempdir, | ||
172 | self.perf, self.command, tempdir, self.args) | ||
173 | ret = os.WEXITSTATUS(os.system(cmd)) | ||
174 | |||
175 | log.warning(" running '%s' ret %d " % (cmd, ret)) | ||
176 | |||
177 | if ret != int(self.ret): | ||
178 | raise Unsup(self) | ||
179 | |||
180 | def compare(self, expect, result): | ||
181 | match = {} | ||
182 | |||
183 | log.debug(" compare"); | ||
184 | |||
185 | # For each expected event find all matching | ||
186 | # events in result. Fail if there's not any. | ||
187 | for exp_name, exp_event in expect.items(): | ||
188 | exp_list = [] | ||
189 | log.debug(" matching [%s]" % exp_name) | ||
190 | for res_name, res_event in result.items(): | ||
191 | log.debug(" to [%s]" % res_name) | ||
192 | if (exp_event.equal(res_event)): | ||
193 | exp_list.append(res_name) | ||
194 | log.debug(" ->OK") | ||
195 | else: | ||
196 | log.debug(" ->FAIL"); | ||
197 | |||
198 | log.debug(" match: [%s] matches %s" % (exp_name, str(exp_list))) | ||
199 | |||
200 | # we did not any matching event - fail | ||
201 | if (not exp_list): | ||
202 | exp_event.diff(res_event) | ||
203 | raise Fail(self, 'match failure'); | ||
204 | |||
205 | match[exp_name] = exp_list | ||
206 | |||
207 | # For each defined group in the expected events | ||
208 | # check we match the same group in the result. | ||
209 | for exp_name, exp_event in expect.items(): | ||
210 | group = exp_event.group | ||
211 | |||
212 | if (group == ''): | ||
213 | continue | ||
214 | |||
215 | for res_name in match[exp_name]: | ||
216 | res_group = result[res_name].group | ||
217 | if res_group not in match[group]: | ||
218 | raise Fail(self, 'group failure') | ||
219 | |||
220 | log.debug(" group: [%s] matches group leader %s" % | ||
221 | (exp_name, str(match[group]))) | ||
222 | |||
223 | log.debug(" matched") | ||
224 | |||
225 | def resolve_groups(self, events): | ||
226 | for name, event in events.items(): | ||
227 | group_fd = event['group_fd']; | ||
228 | if group_fd == '-1': | ||
229 | continue; | ||
230 | |||
231 | for iname, ievent in events.items(): | ||
232 | if (ievent['fd'] == group_fd): | ||
233 | event.group = iname | ||
234 | log.debug('[%s] has group leader [%s]' % (name, iname)) | ||
235 | break; | ||
236 | |||
237 | def run(self): | ||
238 | tempdir = tempfile.mkdtemp(); | ||
239 | |||
240 | try: | ||
241 | # run the test script | ||
242 | self.run_cmd(tempdir); | ||
243 | |||
244 | # load events expectation for the test | ||
245 | log.debug(" loading result events"); | ||
246 | for f in glob.glob(tempdir + '/event*'): | ||
247 | self.load_events(f, self.result); | ||
248 | |||
249 | # resolve group_fd to event names | ||
250 | self.resolve_groups(self.expect); | ||
251 | self.resolve_groups(self.result); | ||
252 | |||
253 | # do the expectation - results matching - both ways | ||
254 | self.compare(self.expect, self.result) | ||
255 | self.compare(self.result, self.expect) | ||
256 | |||
257 | finally: | ||
258 | # cleanup | ||
259 | shutil.rmtree(tempdir) | ||
260 | |||
261 | |||
262 | def run_tests(options): | ||
263 | for f in glob.glob(options.test_dir + '/' + options.test): | ||
264 | try: | ||
265 | Test(f, options).run() | ||
266 | except Unsup, obj: | ||
267 | log.warning("unsupp %s" % obj.getMsg()) | ||
268 | |||
269 | def setup_log(verbose): | ||
270 | global log | ||
271 | level = logging.CRITICAL | ||
272 | |||
273 | if verbose == 1: | ||
274 | level = logging.WARNING | ||
275 | if verbose == 2: | ||
276 | level = logging.INFO | ||
277 | if verbose >= 3: | ||
278 | level = logging.DEBUG | ||
279 | |||
280 | log = logging.getLogger('test') | ||
281 | log.setLevel(level) | ||
282 | ch = logging.StreamHandler() | ||
283 | ch.setLevel(level) | ||
284 | formatter = logging.Formatter('%(message)s') | ||
285 | ch.setFormatter(formatter) | ||
286 | log.addHandler(ch) | ||
287 | |||
288 | USAGE = '''%s [OPTIONS] | ||
289 | -d dir # tests dir | ||
290 | -p path # perf binary | ||
291 | -t test # single test | ||
292 | -v # verbose level | ||
293 | ''' % sys.argv[0] | ||
294 | |||
295 | def main(): | ||
296 | parser = optparse.OptionParser(usage=USAGE) | ||
297 | |||
298 | parser.add_option("-t", "--test", | ||
299 | action="store", type="string", dest="test") | ||
300 | parser.add_option("-d", "--test-dir", | ||
301 | action="store", type="string", dest="test_dir") | ||
302 | parser.add_option("-p", "--perf", | ||
303 | action="store", type="string", dest="perf") | ||
304 | parser.add_option("-v", "--verbose", | ||
305 | action="count", dest="verbose") | ||
306 | |||
307 | options, args = parser.parse_args() | ||
308 | if args: | ||
309 | parser.error('FAILED wrong arguments %s' % ' '.join(args)) | ||
310 | return -1 | ||
311 | |||
312 | setup_log(options.verbose) | ||
313 | |||
314 | if not options.test_dir: | ||
315 | print 'FAILED no -d option specified' | ||
316 | sys.exit(-1) | ||
317 | |||
318 | if not options.test: | ||
319 | options.test = 'test*' | ||
320 | |||
321 | try: | ||
322 | run_tests(options) | ||
323 | |||
324 | except Fail, obj: | ||
325 | print "FAILED %s" % obj.getMsg(); | ||
326 | sys.exit(-1) | ||
327 | |||
328 | sys.exit(0) | ||
329 | |||
330 | if __name__ == '__main__': | ||
331 | main() | ||
diff --git a/tools/perf/tests/attr/README b/tools/perf/tests/attr/README new file mode 100644 index 000000000000..d102957cd59a --- /dev/null +++ b/tools/perf/tests/attr/README | |||
@@ -0,0 +1,64 @@ | |||
1 | The struct perf_event_attr test (attr tests) support | ||
2 | ==================================================== | ||
3 | This testing support is embedded into perf directly and is governed | ||
4 | by the PERF_TEST_ATTR environment variable and hook inside the | ||
5 | sys_perf_event_open function. | ||
6 | |||
7 | The general idea is to store 'struct perf_event_attr' details for | ||
8 | each event created within single perf command. Each event details | ||
9 | are stored into separate text file. Once perf command is finished | ||
10 | these files are checked for values we expect for command. | ||
11 | |||
12 | The attr tests consist of following parts: | ||
13 | |||
14 | tests/attr.c | ||
15 | ------------ | ||
16 | This is the sys_perf_event_open hook implementation. The hook | ||
17 | is triggered when the PERF_TEST_ATTR environment variable is | ||
18 | defined. It must contain name of existing directory with access | ||
19 | and write permissions. | ||
20 | |||
21 | For each sys_perf_event_open call event details are stored in | ||
22 | separate file. Besides 'struct perf_event_attr' values we also | ||
23 | store 'fd' and 'group_fd' values to allow checking for groups. | ||
24 | |||
25 | tests/attr.py | ||
26 | ------------- | ||
27 | This is the python script that does all the hard work. It reads | ||
28 | the test definition, executes it and checks results. | ||
29 | |||
30 | tests/attr/ | ||
31 | ----------- | ||
32 | Directory containing all attr test definitions. | ||
33 | Following tests are defined (with perf commands): | ||
34 | |||
35 | perf record kill (test-record-basic) | ||
36 | perf record -b kill (test-record-branch-any) | ||
37 | perf record -j any kill (test-record-branch-filter-any) | ||
38 | perf record -j any_call kill (test-record-branch-filter-any_call) | ||
39 | perf record -j any_ret kill (test-record-branch-filter-any_ret) | ||
40 | perf record -j hv kill (test-record-branch-filter-hv) | ||
41 | perf record -j ind_call kill (test-record-branch-filter-ind_call) | ||
42 | perf record -j k kill (test-record-branch-filter-k) | ||
43 | perf record -j u kill (test-record-branch-filter-u) | ||
44 | perf record -c 123 kill (test-record-count) | ||
45 | perf record -d kill (test-record-data) | ||
46 | perf record -F 100 kill (test-record-freq) | ||
47 | perf record -g -- kill (test-record-graph-default) | ||
48 | perf record -g dwarf -- kill (test-record-graph-dwarf) | ||
49 | perf record -g fp kill (test-record-graph-fp) | ||
50 | perf record --group -e cycles,instructions kill (test-record-group) | ||
51 | perf record -e '{cycles,instructions}' kill (test-record-group1) | ||
52 | perf record -D kill (test-record-no-delay) | ||
53 | perf record -i kill (test-record-no-inherit) | ||
54 | perf record -n kill (test-record-no-samples) | ||
55 | perf record -c 100 -P kill (test-record-period) | ||
56 | perf record -R kill (test-record-raw) | ||
57 | perf stat -e cycles kill (test-stat-basic) | ||
58 | perf stat kill (test-stat-default) | ||
59 | perf stat -d kill (test-stat-detailed-1) | ||
60 | perf stat -dd kill (test-stat-detailed-2) | ||
61 | perf stat -ddd kill (test-stat-detailed-3) | ||
62 | perf stat --group -e cycles,instructions kill (test-stat-group) | ||
63 | perf stat -e '{cycles,instructions}' kill (test-stat-group1) | ||
64 | perf stat -i -e cycles kill (test-stat-no-inherit) | ||
diff --git a/tools/perf/tests/attr/base-record b/tools/perf/tests/attr/base-record new file mode 100644 index 000000000000..5bc3880f7be5 --- /dev/null +++ b/tools/perf/tests/attr/base-record | |||
@@ -0,0 +1,39 @@ | |||
1 | [event] | ||
2 | fd=1 | ||
3 | group_fd=-1 | ||
4 | flags=0 | ||
5 | type=0|1 | ||
6 | size=96 | ||
7 | config=0 | ||
8 | sample_period=4000 | ||
9 | sample_type=263 | ||
10 | read_format=0 | ||
11 | disabled=1 | ||
12 | inherit=1 | ||
13 | pinned=0 | ||
14 | exclusive=0 | ||
15 | exclude_user=0 | ||
16 | exclude_kernel=0 | ||
17 | exclude_hv=0 | ||
18 | exclude_idle=0 | ||
19 | mmap=1 | ||
20 | comm=1 | ||
21 | freq=1 | ||
22 | inherit_stat=0 | ||
23 | enable_on_exec=1 | ||
24 | task=0 | ||
25 | watermark=0 | ||
26 | precise_ip=0 | ||
27 | mmap_data=0 | ||
28 | sample_id_all=1 | ||
29 | exclude_host=0 | ||
30 | exclude_guest=1 | ||
31 | exclude_callchain_kernel=0 | ||
32 | exclude_callchain_user=0 | ||
33 | wakeup_events=0 | ||
34 | bp_type=0 | ||
35 | config1=0 | ||
36 | config2=0 | ||
37 | branch_sample_type=0 | ||
38 | sample_regs_user=0 | ||
39 | sample_stack_user=0 | ||
diff --git a/tools/perf/tests/attr/base-stat b/tools/perf/tests/attr/base-stat new file mode 100644 index 000000000000..4bd79a82784f --- /dev/null +++ b/tools/perf/tests/attr/base-stat | |||
@@ -0,0 +1,39 @@ | |||
1 | [event] | ||
2 | fd=1 | ||
3 | group_fd=-1 | ||
4 | flags=0 | ||
5 | type=0 | ||
6 | size=96 | ||
7 | config=0 | ||
8 | sample_period=0 | ||
9 | sample_type=0 | ||
10 | read_format=3 | ||
11 | disabled=1 | ||
12 | inherit=1 | ||
13 | pinned=0 | ||
14 | exclusive=0 | ||
15 | exclude_user=0 | ||
16 | exclude_kernel=0 | ||
17 | exclude_hv=0 | ||
18 | exclude_idle=0 | ||
19 | mmap=0 | ||
20 | comm=0 | ||
21 | freq=0 | ||
22 | inherit_stat=0 | ||
23 | enable_on_exec=1 | ||
24 | task=0 | ||
25 | watermark=0 | ||
26 | precise_ip=0 | ||
27 | mmap_data=0 | ||
28 | sample_id_all=0 | ||
29 | exclude_host=0 | ||
30 | exclude_guest=1 | ||
31 | exclude_callchain_kernel=0 | ||
32 | exclude_callchain_user=0 | ||
33 | wakeup_events=0 | ||
34 | bp_type=0 | ||
35 | config1=0 | ||
36 | config2=0 | ||
37 | branch_sample_type=0 | ||
38 | sample_regs_user=0 | ||
39 | sample_stack_user=0 | ||
diff --git a/tools/perf/tests/attr/test-record-basic b/tools/perf/tests/attr/test-record-basic new file mode 100644 index 000000000000..55c0428370ca --- /dev/null +++ b/tools/perf/tests/attr/test-record-basic | |||
@@ -0,0 +1,5 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
diff --git a/tools/perf/tests/attr/test-record-branch-any b/tools/perf/tests/attr/test-record-branch-any new file mode 100644 index 000000000000..1421960ed4e9 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-any | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -b kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=8 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-any b/tools/perf/tests/attr/test-record-branch-filter-any new file mode 100644 index 000000000000..915c4df0e0c2 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-any | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j any kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=8 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-any_call b/tools/perf/tests/attr/test-record-branch-filter-any_call new file mode 100644 index 000000000000..8708dbd4f373 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-any_call | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j any_call kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=16 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-any_ret b/tools/perf/tests/attr/test-record-branch-filter-any_ret new file mode 100644 index 000000000000..0d3607a6dcbe --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-any_ret | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j any_ret kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=32 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-hv b/tools/perf/tests/attr/test-record-branch-filter-hv new file mode 100644 index 000000000000..f25526740cec --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-hv | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j hv kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=8 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-ind_call b/tools/perf/tests/attr/test-record-branch-filter-ind_call new file mode 100644 index 000000000000..e862dd179128 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-ind_call | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j ind_call kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=64 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-k b/tools/perf/tests/attr/test-record-branch-filter-k new file mode 100644 index 000000000000..182971e898f5 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-k | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j k kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=8 | ||
diff --git a/tools/perf/tests/attr/test-record-branch-filter-u b/tools/perf/tests/attr/test-record-branch-filter-u new file mode 100644 index 000000000000..83449ef9e687 --- /dev/null +++ b/tools/perf/tests/attr/test-record-branch-filter-u | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -j u kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=2311 | ||
8 | branch_sample_type=8 | ||
diff --git a/tools/perf/tests/attr/test-record-count b/tools/perf/tests/attr/test-record-count new file mode 100644 index 000000000000..2f841de56f6b --- /dev/null +++ b/tools/perf/tests/attr/test-record-count | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -c 123 kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=123 | ||
7 | sample_type=7 | ||
8 | freq=0 | ||
diff --git a/tools/perf/tests/attr/test-record-data b/tools/perf/tests/attr/test-record-data new file mode 100644 index 000000000000..6627c3e7534a --- /dev/null +++ b/tools/perf/tests/attr/test-record-data | |||
@@ -0,0 +1,8 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -d kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=271 | ||
8 | mmap_data=1 | ||
diff --git a/tools/perf/tests/attr/test-record-freq b/tools/perf/tests/attr/test-record-freq new file mode 100644 index 000000000000..600d0f8f2583 --- /dev/null +++ b/tools/perf/tests/attr/test-record-freq | |||
@@ -0,0 +1,6 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -F 100 kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=100 | ||
diff --git a/tools/perf/tests/attr/test-record-graph-default b/tools/perf/tests/attr/test-record-graph-default new file mode 100644 index 000000000000..833d1849d767 --- /dev/null +++ b/tools/perf/tests/attr/test-record-graph-default | |||
@@ -0,0 +1,6 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -g -- kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_type=295 | ||
diff --git a/tools/perf/tests/attr/test-record-graph-dwarf b/tools/perf/tests/attr/test-record-graph-dwarf new file mode 100644 index 000000000000..e93e082f5208 --- /dev/null +++ b/tools/perf/tests/attr/test-record-graph-dwarf | |||
@@ -0,0 +1,10 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -g dwarf -- kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_type=12583 | ||
7 | exclude_callchain_user=1 | ||
8 | sample_stack_user=8192 | ||
9 | # TODO different for each arch, no support for that now | ||
10 | sample_regs_user=* | ||
diff --git a/tools/perf/tests/attr/test-record-graph-fp b/tools/perf/tests/attr/test-record-graph-fp new file mode 100644 index 000000000000..7cef3743f03f --- /dev/null +++ b/tools/perf/tests/attr/test-record-graph-fp | |||
@@ -0,0 +1,6 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -g fp kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_type=295 | ||
diff --git a/tools/perf/tests/attr/test-record-group b/tools/perf/tests/attr/test-record-group new file mode 100644 index 000000000000..57739cacdb2a --- /dev/null +++ b/tools/perf/tests/attr/test-record-group | |||
@@ -0,0 +1,20 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = --group -e cycles,instructions kill >/dev/null 2>&1 | ||
4 | |||
5 | [event-1:base-record] | ||
6 | fd=1 | ||
7 | group_fd=-1 | ||
8 | sample_type=327 | ||
9 | read_format=4 | ||
10 | |||
11 | [event-2:base-record] | ||
12 | fd=2 | ||
13 | group_fd=1 | ||
14 | config=1 | ||
15 | sample_type=327 | ||
16 | read_format=4 | ||
17 | mmap=0 | ||
18 | comm=0 | ||
19 | enable_on_exec=0 | ||
20 | disabled=0 | ||
diff --git a/tools/perf/tests/attr/test-record-group1 b/tools/perf/tests/attr/test-record-group1 new file mode 100644 index 000000000000..c5548d054aff --- /dev/null +++ b/tools/perf/tests/attr/test-record-group1 | |||
@@ -0,0 +1,21 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -e '{cycles,instructions}' kill >/dev/null 2>&1 | ||
4 | |||
5 | [event-1:base-record] | ||
6 | fd=1 | ||
7 | group_fd=-1 | ||
8 | sample_type=327 | ||
9 | read_format=4 | ||
10 | |||
11 | [event-2:base-record] | ||
12 | fd=2 | ||
13 | group_fd=1 | ||
14 | type=0 | ||
15 | config=1 | ||
16 | sample_type=327 | ||
17 | read_format=4 | ||
18 | mmap=0 | ||
19 | comm=0 | ||
20 | enable_on_exec=0 | ||
21 | disabled=0 | ||
diff --git a/tools/perf/tests/attr/test-record-no-delay b/tools/perf/tests/attr/test-record-no-delay new file mode 100644 index 000000000000..f253b78cdbf2 --- /dev/null +++ b/tools/perf/tests/attr/test-record-no-delay | |||
@@ -0,0 +1,9 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -D kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=263 | ||
8 | watermark=0 | ||
9 | wakeup_events=1 | ||
diff --git a/tools/perf/tests/attr/test-record-no-inherit b/tools/perf/tests/attr/test-record-no-inherit new file mode 100644 index 000000000000..9079a25cd643 --- /dev/null +++ b/tools/perf/tests/attr/test-record-no-inherit | |||
@@ -0,0 +1,7 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -i kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_type=259 | ||
7 | inherit=0 | ||
diff --git a/tools/perf/tests/attr/test-record-no-samples b/tools/perf/tests/attr/test-record-no-samples new file mode 100644 index 000000000000..d0141b2418b5 --- /dev/null +++ b/tools/perf/tests/attr/test-record-no-samples | |||
@@ -0,0 +1,6 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -n kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=0 | ||
diff --git a/tools/perf/tests/attr/test-record-period b/tools/perf/tests/attr/test-record-period new file mode 100644 index 000000000000..8abc5314fc52 --- /dev/null +++ b/tools/perf/tests/attr/test-record-period | |||
@@ -0,0 +1,7 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -c 100 -P kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=100 | ||
7 | freq=0 | ||
diff --git a/tools/perf/tests/attr/test-record-raw b/tools/perf/tests/attr/test-record-raw new file mode 100644 index 000000000000..4a8ef25b5f49 --- /dev/null +++ b/tools/perf/tests/attr/test-record-raw | |||
@@ -0,0 +1,7 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -R kill >/dev/null 2>&1 | ||
4 | |||
5 | [event:base-record] | ||
6 | sample_period=4000 | ||
7 | sample_type=1415 | ||
diff --git a/tools/perf/tests/attr/test-stat-basic b/tools/perf/tests/attr/test-stat-basic new file mode 100644 index 000000000000..74e17881f2ba --- /dev/null +++ b/tools/perf/tests/attr/test-stat-basic | |||
@@ -0,0 +1,6 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -e cycles kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | [event:base-stat] | ||
diff --git a/tools/perf/tests/attr/test-stat-default b/tools/perf/tests/attr/test-stat-default new file mode 100644 index 000000000000..19270f54c96e --- /dev/null +++ b/tools/perf/tests/attr/test-stat-default | |||
@@ -0,0 +1,64 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK | ||
7 | [event1:base-stat] | ||
8 | fd=1 | ||
9 | type=1 | ||
10 | config=1 | ||
11 | |||
12 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES | ||
13 | [event2:base-stat] | ||
14 | fd=2 | ||
15 | type=1 | ||
16 | config=3 | ||
17 | |||
18 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS | ||
19 | [event3:base-stat] | ||
20 | fd=3 | ||
21 | type=1 | ||
22 | config=4 | ||
23 | |||
24 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS | ||
25 | [event4:base-stat] | ||
26 | fd=4 | ||
27 | type=1 | ||
28 | config=2 | ||
29 | |||
30 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES | ||
31 | [event5:base-stat] | ||
32 | fd=5 | ||
33 | type=0 | ||
34 | config=0 | ||
35 | |||
36 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND | ||
37 | [event6:base-stat] | ||
38 | fd=6 | ||
39 | type=0 | ||
40 | config=7 | ||
41 | |||
42 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND | ||
43 | [event7:base-stat] | ||
44 | fd=7 | ||
45 | type=0 | ||
46 | config=8 | ||
47 | |||
48 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS | ||
49 | [event8:base-stat] | ||
50 | fd=8 | ||
51 | type=0 | ||
52 | config=1 | ||
53 | |||
54 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS | ||
55 | [event9:base-stat] | ||
56 | fd=9 | ||
57 | type=0 | ||
58 | config=4 | ||
59 | |||
60 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES | ||
61 | [event10:base-stat] | ||
62 | fd=10 | ||
63 | type=0 | ||
64 | config=5 | ||
diff --git a/tools/perf/tests/attr/test-stat-detailed-1 b/tools/perf/tests/attr/test-stat-detailed-1 new file mode 100644 index 000000000000..51426b87153b --- /dev/null +++ b/tools/perf/tests/attr/test-stat-detailed-1 | |||
@@ -0,0 +1,101 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -d kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | |||
7 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK | ||
8 | [event1:base-stat] | ||
9 | fd=1 | ||
10 | type=1 | ||
11 | config=1 | ||
12 | |||
13 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES | ||
14 | [event2:base-stat] | ||
15 | fd=2 | ||
16 | type=1 | ||
17 | config=3 | ||
18 | |||
19 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS | ||
20 | [event3:base-stat] | ||
21 | fd=3 | ||
22 | type=1 | ||
23 | config=4 | ||
24 | |||
25 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS | ||
26 | [event4:base-stat] | ||
27 | fd=4 | ||
28 | type=1 | ||
29 | config=2 | ||
30 | |||
31 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES | ||
32 | [event5:base-stat] | ||
33 | fd=5 | ||
34 | type=0 | ||
35 | config=0 | ||
36 | |||
37 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND | ||
38 | [event6:base-stat] | ||
39 | fd=6 | ||
40 | type=0 | ||
41 | config=7 | ||
42 | |||
43 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND | ||
44 | [event7:base-stat] | ||
45 | fd=7 | ||
46 | type=0 | ||
47 | config=8 | ||
48 | |||
49 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS | ||
50 | [event8:base-stat] | ||
51 | fd=8 | ||
52 | type=0 | ||
53 | config=1 | ||
54 | |||
55 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS | ||
56 | [event9:base-stat] | ||
57 | fd=9 | ||
58 | type=0 | ||
59 | config=4 | ||
60 | |||
61 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES | ||
62 | [event10:base-stat] | ||
63 | fd=10 | ||
64 | type=0 | ||
65 | config=5 | ||
66 | |||
67 | # PERF_TYPE_HW_CACHE / | ||
68 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
69 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
70 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
71 | [event11:base-stat] | ||
72 | fd=11 | ||
73 | type=3 | ||
74 | config=0 | ||
75 | |||
76 | # PERF_TYPE_HW_CACHE / | ||
77 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
78 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
79 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
80 | [event12:base-stat] | ||
81 | fd=12 | ||
82 | type=3 | ||
83 | config=65536 | ||
84 | |||
85 | # PERF_TYPE_HW_CACHE / | ||
86 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
87 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
88 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
89 | [event13:base-stat] | ||
90 | fd=13 | ||
91 | type=3 | ||
92 | config=2 | ||
93 | |||
94 | # PERF_TYPE_HW_CACHE, | ||
95 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
96 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
97 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
98 | [event14:base-stat] | ||
99 | fd=14 | ||
100 | type=3 | ||
101 | config=65538 | ||
diff --git a/tools/perf/tests/attr/test-stat-detailed-2 b/tools/perf/tests/attr/test-stat-detailed-2 new file mode 100644 index 000000000000..8de5acc31c27 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-detailed-2 | |||
@@ -0,0 +1,155 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -dd kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | |||
7 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK | ||
8 | [event1:base-stat] | ||
9 | fd=1 | ||
10 | type=1 | ||
11 | config=1 | ||
12 | |||
13 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES | ||
14 | [event2:base-stat] | ||
15 | fd=2 | ||
16 | type=1 | ||
17 | config=3 | ||
18 | |||
19 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS | ||
20 | [event3:base-stat] | ||
21 | fd=3 | ||
22 | type=1 | ||
23 | config=4 | ||
24 | |||
25 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS | ||
26 | [event4:base-stat] | ||
27 | fd=4 | ||
28 | type=1 | ||
29 | config=2 | ||
30 | |||
31 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES | ||
32 | [event5:base-stat] | ||
33 | fd=5 | ||
34 | type=0 | ||
35 | config=0 | ||
36 | |||
37 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND | ||
38 | [event6:base-stat] | ||
39 | fd=6 | ||
40 | type=0 | ||
41 | config=7 | ||
42 | |||
43 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND | ||
44 | [event7:base-stat] | ||
45 | fd=7 | ||
46 | type=0 | ||
47 | config=8 | ||
48 | |||
49 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS | ||
50 | [event8:base-stat] | ||
51 | fd=8 | ||
52 | type=0 | ||
53 | config=1 | ||
54 | |||
55 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS | ||
56 | [event9:base-stat] | ||
57 | fd=9 | ||
58 | type=0 | ||
59 | config=4 | ||
60 | |||
61 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES | ||
62 | [event10:base-stat] | ||
63 | fd=10 | ||
64 | type=0 | ||
65 | config=5 | ||
66 | |||
67 | # PERF_TYPE_HW_CACHE / | ||
68 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
69 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
70 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
71 | [event11:base-stat] | ||
72 | fd=11 | ||
73 | type=3 | ||
74 | config=0 | ||
75 | |||
76 | # PERF_TYPE_HW_CACHE / | ||
77 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
78 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
79 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
80 | [event12:base-stat] | ||
81 | fd=12 | ||
82 | type=3 | ||
83 | config=65536 | ||
84 | |||
85 | # PERF_TYPE_HW_CACHE / | ||
86 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
87 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
88 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
89 | [event13:base-stat] | ||
90 | fd=13 | ||
91 | type=3 | ||
92 | config=2 | ||
93 | |||
94 | # PERF_TYPE_HW_CACHE, | ||
95 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
96 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
97 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
98 | [event14:base-stat] | ||
99 | fd=14 | ||
100 | type=3 | ||
101 | config=65538 | ||
102 | |||
103 | # PERF_TYPE_HW_CACHE, | ||
104 | # PERF_COUNT_HW_CACHE_L1I << 0 | | ||
105 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
106 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
107 | [event15:base-stat] | ||
108 | fd=15 | ||
109 | type=3 | ||
110 | config=1 | ||
111 | |||
112 | # PERF_TYPE_HW_CACHE, | ||
113 | # PERF_COUNT_HW_CACHE_L1I << 0 | | ||
114 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
115 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
116 | [event16:base-stat] | ||
117 | fd=16 | ||
118 | type=3 | ||
119 | config=65537 | ||
120 | |||
121 | # PERF_TYPE_HW_CACHE, | ||
122 | # PERF_COUNT_HW_CACHE_DTLB << 0 | | ||
123 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
124 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
125 | [event17:base-stat] | ||
126 | fd=17 | ||
127 | type=3 | ||
128 | config=3 | ||
129 | |||
130 | # PERF_TYPE_HW_CACHE, | ||
131 | # PERF_COUNT_HW_CACHE_DTLB << 0 | | ||
132 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
133 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
134 | [event18:base-stat] | ||
135 | fd=18 | ||
136 | type=3 | ||
137 | config=65539 | ||
138 | |||
139 | # PERF_TYPE_HW_CACHE, | ||
140 | # PERF_COUNT_HW_CACHE_ITLB << 0 | | ||
141 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
142 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
143 | [event19:base-stat] | ||
144 | fd=19 | ||
145 | type=3 | ||
146 | config=4 | ||
147 | |||
148 | # PERF_TYPE_HW_CACHE, | ||
149 | # PERF_COUNT_HW_CACHE_ITLB << 0 | | ||
150 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
151 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
152 | [event20:base-stat] | ||
153 | fd=20 | ||
154 | type=3 | ||
155 | config=65540 | ||
diff --git a/tools/perf/tests/attr/test-stat-detailed-3 b/tools/perf/tests/attr/test-stat-detailed-3 new file mode 100644 index 000000000000..0a1f45bf7d79 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-detailed-3 | |||
@@ -0,0 +1,173 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -ddd kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | |||
7 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_TASK_CLOCK | ||
8 | [event1:base-stat] | ||
9 | fd=1 | ||
10 | type=1 | ||
11 | config=1 | ||
12 | |||
13 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CONTEXT_SWITCHES | ||
14 | [event2:base-stat] | ||
15 | fd=2 | ||
16 | type=1 | ||
17 | config=3 | ||
18 | |||
19 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_CPU_MIGRATIONS | ||
20 | [event3:base-stat] | ||
21 | fd=3 | ||
22 | type=1 | ||
23 | config=4 | ||
24 | |||
25 | # PERF_TYPE_SOFTWARE / PERF_COUNT_SW_PAGE_FAULTS | ||
26 | [event4:base-stat] | ||
27 | fd=4 | ||
28 | type=1 | ||
29 | config=2 | ||
30 | |||
31 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_CPU_CYCLES | ||
32 | [event5:base-stat] | ||
33 | fd=5 | ||
34 | type=0 | ||
35 | config=0 | ||
36 | |||
37 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_FRONTEND | ||
38 | [event6:base-stat] | ||
39 | fd=6 | ||
40 | type=0 | ||
41 | config=7 | ||
42 | |||
43 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_STALLED_CYCLES_BACKEND | ||
44 | [event7:base-stat] | ||
45 | fd=7 | ||
46 | type=0 | ||
47 | config=8 | ||
48 | |||
49 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_INSTRUCTIONS | ||
50 | [event8:base-stat] | ||
51 | fd=8 | ||
52 | type=0 | ||
53 | config=1 | ||
54 | |||
55 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_INSTRUCTIONS | ||
56 | [event9:base-stat] | ||
57 | fd=9 | ||
58 | type=0 | ||
59 | config=4 | ||
60 | |||
61 | # PERF_TYPE_HARDWARE / PERF_COUNT_HW_BRANCH_MISSES | ||
62 | [event10:base-stat] | ||
63 | fd=10 | ||
64 | type=0 | ||
65 | config=5 | ||
66 | |||
67 | # PERF_TYPE_HW_CACHE / | ||
68 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
69 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
70 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
71 | [event11:base-stat] | ||
72 | fd=11 | ||
73 | type=3 | ||
74 | config=0 | ||
75 | |||
76 | # PERF_TYPE_HW_CACHE / | ||
77 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
78 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
79 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
80 | [event12:base-stat] | ||
81 | fd=12 | ||
82 | type=3 | ||
83 | config=65536 | ||
84 | |||
85 | # PERF_TYPE_HW_CACHE / | ||
86 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
87 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
88 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
89 | [event13:base-stat] | ||
90 | fd=13 | ||
91 | type=3 | ||
92 | config=2 | ||
93 | |||
94 | # PERF_TYPE_HW_CACHE, | ||
95 | # PERF_COUNT_HW_CACHE_LL << 0 | | ||
96 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
97 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
98 | [event14:base-stat] | ||
99 | fd=14 | ||
100 | type=3 | ||
101 | config=65538 | ||
102 | |||
103 | # PERF_TYPE_HW_CACHE, | ||
104 | # PERF_COUNT_HW_CACHE_L1I << 0 | | ||
105 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
106 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
107 | [event15:base-stat] | ||
108 | fd=15 | ||
109 | type=3 | ||
110 | config=1 | ||
111 | |||
112 | # PERF_TYPE_HW_CACHE, | ||
113 | # PERF_COUNT_HW_CACHE_L1I << 0 | | ||
114 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
115 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
116 | [event16:base-stat] | ||
117 | fd=16 | ||
118 | type=3 | ||
119 | config=65537 | ||
120 | |||
121 | # PERF_TYPE_HW_CACHE, | ||
122 | # PERF_COUNT_HW_CACHE_DTLB << 0 | | ||
123 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
124 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
125 | [event17:base-stat] | ||
126 | fd=17 | ||
127 | type=3 | ||
128 | config=3 | ||
129 | |||
130 | # PERF_TYPE_HW_CACHE, | ||
131 | # PERF_COUNT_HW_CACHE_DTLB << 0 | | ||
132 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
133 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
134 | [event18:base-stat] | ||
135 | fd=18 | ||
136 | type=3 | ||
137 | config=65539 | ||
138 | |||
139 | # PERF_TYPE_HW_CACHE, | ||
140 | # PERF_COUNT_HW_CACHE_ITLB << 0 | | ||
141 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
142 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
143 | [event19:base-stat] | ||
144 | fd=19 | ||
145 | type=3 | ||
146 | config=4 | ||
147 | |||
148 | # PERF_TYPE_HW_CACHE, | ||
149 | # PERF_COUNT_HW_CACHE_ITLB << 0 | | ||
150 | # (PERF_COUNT_HW_CACHE_OP_READ << 8) | | ||
151 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
152 | [event20:base-stat] | ||
153 | fd=20 | ||
154 | type=3 | ||
155 | config=65540 | ||
156 | |||
157 | # PERF_TYPE_HW_CACHE, | ||
158 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
159 | # (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | | ||
160 | # (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16) | ||
161 | [event21:base-stat] | ||
162 | fd=21 | ||
163 | type=3 | ||
164 | config=512 | ||
165 | |||
166 | # PERF_TYPE_HW_CACHE, | ||
167 | # PERF_COUNT_HW_CACHE_L1D << 0 | | ||
168 | # (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | | ||
169 | # (PERF_COUNT_HW_CACHE_RESULT_MISS << 16) | ||
170 | [event22:base-stat] | ||
171 | fd=22 | ||
172 | type=3 | ||
173 | config=66048 | ||
diff --git a/tools/perf/tests/attr/test-stat-group b/tools/perf/tests/attr/test-stat-group new file mode 100644 index 000000000000..fdc1596a8862 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-group | |||
@@ -0,0 +1,15 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = --group -e cycles,instructions kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | [event-1:base-stat] | ||
7 | fd=1 | ||
8 | group_fd=-1 | ||
9 | |||
10 | [event-2:base-stat] | ||
11 | fd=2 | ||
12 | group_fd=1 | ||
13 | config=1 | ||
14 | disabled=0 | ||
15 | enable_on_exec=0 | ||
diff --git a/tools/perf/tests/attr/test-stat-group1 b/tools/perf/tests/attr/test-stat-group1 new file mode 100644 index 000000000000..2a1f86e4a904 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-group1 | |||
@@ -0,0 +1,15 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -e '{cycles,instructions}' kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | [event-1:base-stat] | ||
7 | fd=1 | ||
8 | group_fd=-1 | ||
9 | |||
10 | [event-2:base-stat] | ||
11 | fd=2 | ||
12 | group_fd=1 | ||
13 | config=1 | ||
14 | disabled=0 | ||
15 | enable_on_exec=0 | ||
diff --git a/tools/perf/tests/attr/test-stat-no-inherit b/tools/perf/tests/attr/test-stat-no-inherit new file mode 100644 index 000000000000..d54b2a1e3e28 --- /dev/null +++ b/tools/perf/tests/attr/test-stat-no-inherit | |||
@@ -0,0 +1,7 @@ | |||
1 | [config] | ||
2 | command = stat | ||
3 | args = -i -e cycles kill >/dev/null 2>&1 | ||
4 | ret = 1 | ||
5 | |||
6 | [event:base-stat] | ||
7 | inherit=0 | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c new file mode 100644 index 000000000000..acb98e0e39f2 --- /dev/null +++ b/tools/perf/tests/builtin-test.c | |||
@@ -0,0 +1,203 @@ | |||
1 | /* | ||
2 | * builtin-test.c | ||
3 | * | ||
4 | * Builtin regression testing command: ever growing number of sanity tests | ||
5 | */ | ||
6 | #include "builtin.h" | ||
7 | #include "intlist.h" | ||
8 | #include "tests.h" | ||
9 | #include "debug.h" | ||
10 | #include "color.h" | ||
11 | #include "parse-options.h" | ||
12 | #include "symbol.h" | ||
13 | |||
14 | static struct test { | ||
15 | const char *desc; | ||
16 | int (*func)(void); | ||
17 | } tests[] = { | ||
18 | { | ||
19 | .desc = "vmlinux symtab matches kallsyms", | ||
20 | .func = test__vmlinux_matches_kallsyms, | ||
21 | }, | ||
22 | { | ||
23 | .desc = "detect open syscall event", | ||
24 | .func = test__open_syscall_event, | ||
25 | }, | ||
26 | { | ||
27 | .desc = "detect open syscall event on all cpus", | ||
28 | .func = test__open_syscall_event_on_all_cpus, | ||
29 | }, | ||
30 | { | ||
31 | .desc = "read samples using the mmap interface", | ||
32 | .func = test__basic_mmap, | ||
33 | }, | ||
34 | { | ||
35 | .desc = "parse events tests", | ||
36 | .func = test__parse_events, | ||
37 | }, | ||
38 | #if defined(__x86_64__) || defined(__i386__) | ||
39 | { | ||
40 | .desc = "x86 rdpmc test", | ||
41 | .func = test__rdpmc, | ||
42 | }, | ||
43 | #endif | ||
44 | { | ||
45 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", | ||
46 | .func = test__PERF_RECORD, | ||
47 | }, | ||
48 | { | ||
49 | .desc = "Test perf pmu format parsing", | ||
50 | .func = test__pmu, | ||
51 | }, | ||
52 | { | ||
53 | .desc = "Test dso data interface", | ||
54 | .func = test__dso_data, | ||
55 | }, | ||
56 | { | ||
57 | .desc = "roundtrip evsel->name check", | ||
58 | .func = test__perf_evsel__roundtrip_name_test, | ||
59 | }, | ||
60 | { | ||
61 | .desc = "Check parsing of sched tracepoints fields", | ||
62 | .func = test__perf_evsel__tp_sched_test, | ||
63 | }, | ||
64 | { | ||
65 | .desc = "Generate and check syscalls:sys_enter_open event fields", | ||
66 | .func = test__syscall_open_tp_fields, | ||
67 | }, | ||
68 | { | ||
69 | .desc = "struct perf_event_attr setup", | ||
70 | .func = test__attr, | ||
71 | }, | ||
72 | { | ||
73 | .desc = "Test matching and linking mutliple hists", | ||
74 | .func = test__hists_link, | ||
75 | }, | ||
76 | { | ||
77 | .desc = "Try 'use perf' in python, checking link problems", | ||
78 | .func = test__python_use, | ||
79 | }, | ||
80 | { | ||
81 | .func = NULL, | ||
82 | }, | ||
83 | }; | ||
84 | |||
85 | static bool perf_test__matches(int curr, int argc, const char *argv[]) | ||
86 | { | ||
87 | int i; | ||
88 | |||
89 | if (argc == 0) | ||
90 | return true; | ||
91 | |||
92 | for (i = 0; i < argc; ++i) { | ||
93 | char *end; | ||
94 | long nr = strtoul(argv[i], &end, 10); | ||
95 | |||
96 | if (*end == '\0') { | ||
97 | if (nr == curr + 1) | ||
98 | return true; | ||
99 | continue; | ||
100 | } | ||
101 | |||
102 | if (strstr(tests[curr].desc, argv[i])) | ||
103 | return true; | ||
104 | } | ||
105 | |||
106 | return false; | ||
107 | } | ||
108 | |||
109 | static int __cmd_test(int argc, const char *argv[], struct intlist *skiplist) | ||
110 | { | ||
111 | int i = 0; | ||
112 | int width = 0; | ||
113 | |||
114 | while (tests[i].func) { | ||
115 | int len = strlen(tests[i].desc); | ||
116 | |||
117 | if (width < len) | ||
118 | width = len; | ||
119 | ++i; | ||
120 | } | ||
121 | |||
122 | i = 0; | ||
123 | while (tests[i].func) { | ||
124 | int curr = i++, err; | ||
125 | |||
126 | if (!perf_test__matches(curr, argc, argv)) | ||
127 | continue; | ||
128 | |||
129 | pr_info("%2d: %-*s:", i, width, tests[curr].desc); | ||
130 | |||
131 | if (intlist__find(skiplist, i)) { | ||
132 | color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip (user override)\n"); | ||
133 | continue; | ||
134 | } | ||
135 | |||
136 | pr_debug("\n--- start ---\n"); | ||
137 | err = tests[curr].func(); | ||
138 | pr_debug("---- end ----\n%s:", tests[curr].desc); | ||
139 | |||
140 | switch (err) { | ||
141 | case TEST_OK: | ||
142 | pr_info(" Ok\n"); | ||
143 | break; | ||
144 | case TEST_SKIP: | ||
145 | color_fprintf(stderr, PERF_COLOR_YELLOW, " Skip\n"); | ||
146 | break; | ||
147 | case TEST_FAIL: | ||
148 | default: | ||
149 | color_fprintf(stderr, PERF_COLOR_RED, " FAILED!\n"); | ||
150 | break; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static int perf_test__list(int argc, const char **argv) | ||
158 | { | ||
159 | int i = 0; | ||
160 | |||
161 | while (tests[i].func) { | ||
162 | int curr = i++; | ||
163 | |||
164 | if (argc > 1 && !strstr(tests[curr].desc, argv[1])) | ||
165 | continue; | ||
166 | |||
167 | pr_info("%2d: %s\n", i, tests[curr].desc); | ||
168 | } | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused) | ||
174 | { | ||
175 | const char * const test_usage[] = { | ||
176 | "perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]", | ||
177 | NULL, | ||
178 | }; | ||
179 | const char *skip = NULL; | ||
180 | const struct option test_options[] = { | ||
181 | OPT_STRING('s', "skip", &skip, "tests", "tests to skip"), | ||
182 | OPT_INCR('v', "verbose", &verbose, | ||
183 | "be more verbose (show symbol address, etc)"), | ||
184 | OPT_END() | ||
185 | }; | ||
186 | struct intlist *skiplist = NULL; | ||
187 | |||
188 | argc = parse_options(argc, argv, test_options, test_usage, 0); | ||
189 | if (argc >= 1 && !strcmp(argv[0], "list")) | ||
190 | return perf_test__list(argc, argv); | ||
191 | |||
192 | symbol_conf.priv_size = sizeof(int); | ||
193 | symbol_conf.sort_by_name = true; | ||
194 | symbol_conf.try_vmlinux_path = true; | ||
195 | |||
196 | if (symbol__init() < 0) | ||
197 | return -1; | ||
198 | |||
199 | if (skip != NULL) | ||
200 | skiplist = intlist__new(skip); | ||
201 | |||
202 | return __cmd_test(argc, argv, skiplist); | ||
203 | } | ||
diff --git a/tools/perf/util/dso-test-data.c b/tools/perf/tests/dso-data.c index c6caedeb1d6b..5eaffa2de9c5 100644 --- a/tools/perf/util/dso-test-data.c +++ b/tools/perf/tests/dso-data.c | |||
@@ -6,7 +6,9 @@ | |||
6 | #include <fcntl.h> | 6 | #include <fcntl.h> |
7 | #include <string.h> | 7 | #include <string.h> |
8 | 8 | ||
9 | #include "machine.h" | ||
9 | #include "symbol.h" | 10 | #include "symbol.h" |
11 | #include "tests.h" | ||
10 | 12 | ||
11 | #define TEST_ASSERT_VAL(text, cond) \ | 13 | #define TEST_ASSERT_VAL(text, cond) \ |
12 | do { \ | 14 | do { \ |
@@ -24,6 +26,10 @@ static char *test_file(int size) | |||
24 | unsigned char *buf; | 26 | unsigned char *buf; |
25 | 27 | ||
26 | fd = mkstemp(templ); | 28 | fd = mkstemp(templ); |
29 | if (fd < 0) { | ||
30 | perror("mkstemp failed"); | ||
31 | return NULL; | ||
32 | } | ||
27 | 33 | ||
28 | buf = malloc(size); | 34 | buf = malloc(size); |
29 | if (!buf) { | 35 | if (!buf) { |
@@ -94,7 +100,7 @@ struct test_data_offset offsets[] = { | |||
94 | }, | 100 | }, |
95 | }; | 101 | }; |
96 | 102 | ||
97 | int dso__test_data(void) | 103 | int test__dso_data(void) |
98 | { | 104 | { |
99 | struct machine machine; | 105 | struct machine machine; |
100 | struct dso *dso; | 106 | struct dso *dso; |
diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c new file mode 100644 index 000000000000..0fd99a9adb91 --- /dev/null +++ b/tools/perf/tests/evsel-roundtrip-name.c | |||
@@ -0,0 +1,114 @@ | |||
1 | #include "evlist.h" | ||
2 | #include "evsel.h" | ||
3 | #include "parse-events.h" | ||
4 | #include "tests.h" | ||
5 | |||
6 | static int perf_evsel__roundtrip_cache_name_test(void) | ||
7 | { | ||
8 | char name[128]; | ||
9 | int type, op, err = 0, ret = 0, i, idx; | ||
10 | struct perf_evsel *evsel; | ||
11 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
12 | |||
13 | if (evlist == NULL) | ||
14 | return -ENOMEM; | ||
15 | |||
16 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
17 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
18 | /* skip invalid cache type */ | ||
19 | if (!perf_evsel__is_cache_op_valid(type, op)) | ||
20 | continue; | ||
21 | |||
22 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
23 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | ||
24 | name, sizeof(name)); | ||
25 | err = parse_events(evlist, name); | ||
26 | if (err) | ||
27 | ret = err; | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | |||
32 | idx = 0; | ||
33 | evsel = perf_evlist__first(evlist); | ||
34 | |||
35 | for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) { | ||
36 | for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) { | ||
37 | /* skip invalid cache type */ | ||
38 | if (!perf_evsel__is_cache_op_valid(type, op)) | ||
39 | continue; | ||
40 | |||
41 | for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) { | ||
42 | __perf_evsel__hw_cache_type_op_res_name(type, op, i, | ||
43 | name, sizeof(name)); | ||
44 | if (evsel->idx != idx) | ||
45 | continue; | ||
46 | |||
47 | ++idx; | ||
48 | |||
49 | if (strcmp(perf_evsel__name(evsel), name)) { | ||
50 | pr_debug("%s != %s\n", perf_evsel__name(evsel), name); | ||
51 | ret = -1; | ||
52 | } | ||
53 | |||
54 | evsel = perf_evsel__next(evsel); | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | perf_evlist__delete(evlist); | ||
60 | return ret; | ||
61 | } | ||
62 | |||
63 | static int __perf_evsel__name_array_test(const char *names[], int nr_names) | ||
64 | { | ||
65 | int i, err; | ||
66 | struct perf_evsel *evsel; | ||
67 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
68 | |||
69 | if (evlist == NULL) | ||
70 | return -ENOMEM; | ||
71 | |||
72 | for (i = 0; i < nr_names; ++i) { | ||
73 | err = parse_events(evlist, names[i]); | ||
74 | if (err) { | ||
75 | pr_debug("failed to parse event '%s', err %d\n", | ||
76 | names[i], err); | ||
77 | goto out_delete_evlist; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | err = 0; | ||
82 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
83 | if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) { | ||
84 | --err; | ||
85 | pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | out_delete_evlist: | ||
90 | perf_evlist__delete(evlist); | ||
91 | return err; | ||
92 | } | ||
93 | |||
94 | #define perf_evsel__name_array_test(names) \ | ||
95 | __perf_evsel__name_array_test(names, ARRAY_SIZE(names)) | ||
96 | |||
97 | int test__perf_evsel__roundtrip_name_test(void) | ||
98 | { | ||
99 | int err = 0, ret = 0; | ||
100 | |||
101 | err = perf_evsel__name_array_test(perf_evsel__hw_names); | ||
102 | if (err) | ||
103 | ret = err; | ||
104 | |||
105 | err = perf_evsel__name_array_test(perf_evsel__sw_names); | ||
106 | if (err) | ||
107 | ret = err; | ||
108 | |||
109 | err = perf_evsel__roundtrip_cache_name_test(); | ||
110 | if (err) | ||
111 | ret = err; | ||
112 | |||
113 | return ret; | ||
114 | } | ||
diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c new file mode 100644 index 000000000000..a5d2fcc5ae35 --- /dev/null +++ b/tools/perf/tests/evsel-tp-sched.c | |||
@@ -0,0 +1,84 @@ | |||
1 | #include "evsel.h" | ||
2 | #include "tests.h" | ||
3 | #include "event-parse.h" | ||
4 | |||
5 | static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, | ||
6 | int size, bool should_be_signed) | ||
7 | { | ||
8 | struct format_field *field = perf_evsel__field(evsel, name); | ||
9 | int is_signed; | ||
10 | int ret = 0; | ||
11 | |||
12 | if (field == NULL) { | ||
13 | pr_debug("%s: \"%s\" field not found!\n", evsel->name, name); | ||
14 | return -1; | ||
15 | } | ||
16 | |||
17 | is_signed = !!(field->flags | FIELD_IS_SIGNED); | ||
18 | if (should_be_signed && !is_signed) { | ||
19 | pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n", | ||
20 | evsel->name, name, is_signed, should_be_signed); | ||
21 | ret = -1; | ||
22 | } | ||
23 | |||
24 | if (field->size != size) { | ||
25 | pr_debug("%s: \"%s\" size (%d) should be %d!\n", | ||
26 | evsel->name, name, field->size, size); | ||
27 | ret = -1; | ||
28 | } | ||
29 | |||
30 | return ret; | ||
31 | } | ||
32 | |||
33 | int test__perf_evsel__tp_sched_test(void) | ||
34 | { | ||
35 | struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch", 0); | ||
36 | int ret = 0; | ||
37 | |||
38 | if (evsel == NULL) { | ||
39 | pr_debug("perf_evsel__new\n"); | ||
40 | return -1; | ||
41 | } | ||
42 | |||
43 | if (perf_evsel__test_field(evsel, "prev_comm", 16, true)) | ||
44 | ret = -1; | ||
45 | |||
46 | if (perf_evsel__test_field(evsel, "prev_pid", 4, true)) | ||
47 | ret = -1; | ||
48 | |||
49 | if (perf_evsel__test_field(evsel, "prev_prio", 4, true)) | ||
50 | ret = -1; | ||
51 | |||
52 | if (perf_evsel__test_field(evsel, "prev_state", 8, true)) | ||
53 | ret = -1; | ||
54 | |||
55 | if (perf_evsel__test_field(evsel, "next_comm", 16, true)) | ||
56 | ret = -1; | ||
57 | |||
58 | if (perf_evsel__test_field(evsel, "next_pid", 4, true)) | ||
59 | ret = -1; | ||
60 | |||
61 | if (perf_evsel__test_field(evsel, "next_prio", 4, true)) | ||
62 | ret = -1; | ||
63 | |||
64 | perf_evsel__delete(evsel); | ||
65 | |||
66 | evsel = perf_evsel__newtp("sched", "sched_wakeup", 0); | ||
67 | |||
68 | if (perf_evsel__test_field(evsel, "comm", 16, true)) | ||
69 | ret = -1; | ||
70 | |||
71 | if (perf_evsel__test_field(evsel, "pid", 4, true)) | ||
72 | ret = -1; | ||
73 | |||
74 | if (perf_evsel__test_field(evsel, "prio", 4, true)) | ||
75 | ret = -1; | ||
76 | |||
77 | if (perf_evsel__test_field(evsel, "success", 4, true)) | ||
78 | ret = -1; | ||
79 | |||
80 | if (perf_evsel__test_field(evsel, "target_cpu", 4, true)) | ||
81 | ret = -1; | ||
82 | |||
83 | return ret; | ||
84 | } | ||
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c new file mode 100644 index 000000000000..1be64a6c5daf --- /dev/null +++ b/tools/perf/tests/hists_link.c | |||
@@ -0,0 +1,500 @@ | |||
1 | #include "perf.h" | ||
2 | #include "tests.h" | ||
3 | #include "debug.h" | ||
4 | #include "symbol.h" | ||
5 | #include "sort.h" | ||
6 | #include "evsel.h" | ||
7 | #include "evlist.h" | ||
8 | #include "machine.h" | ||
9 | #include "thread.h" | ||
10 | #include "parse-events.h" | ||
11 | |||
12 | static struct { | ||
13 | u32 pid; | ||
14 | const char *comm; | ||
15 | } fake_threads[] = { | ||
16 | { 100, "perf" }, | ||
17 | { 200, "perf" }, | ||
18 | { 300, "bash" }, | ||
19 | }; | ||
20 | |||
21 | static struct { | ||
22 | u32 pid; | ||
23 | u64 start; | ||
24 | const char *filename; | ||
25 | } fake_mmap_info[] = { | ||
26 | { 100, 0x40000, "perf" }, | ||
27 | { 100, 0x50000, "libc" }, | ||
28 | { 100, 0xf0000, "[kernel]" }, | ||
29 | { 200, 0x40000, "perf" }, | ||
30 | { 200, 0x50000, "libc" }, | ||
31 | { 200, 0xf0000, "[kernel]" }, | ||
32 | { 300, 0x40000, "bash" }, | ||
33 | { 300, 0x50000, "libc" }, | ||
34 | { 300, 0xf0000, "[kernel]" }, | ||
35 | }; | ||
36 | |||
37 | struct fake_sym { | ||
38 | u64 start; | ||
39 | u64 length; | ||
40 | const char *name; | ||
41 | }; | ||
42 | |||
43 | static struct fake_sym perf_syms[] = { | ||
44 | { 700, 100, "main" }, | ||
45 | { 800, 100, "run_command" }, | ||
46 | { 900, 100, "cmd_record" }, | ||
47 | }; | ||
48 | |||
49 | static struct fake_sym bash_syms[] = { | ||
50 | { 700, 100, "main" }, | ||
51 | { 800, 100, "xmalloc" }, | ||
52 | { 900, 100, "xfree" }, | ||
53 | }; | ||
54 | |||
55 | static struct fake_sym libc_syms[] = { | ||
56 | { 700, 100, "malloc" }, | ||
57 | { 800, 100, "free" }, | ||
58 | { 900, 100, "realloc" }, | ||
59 | }; | ||
60 | |||
61 | static struct fake_sym kernel_syms[] = { | ||
62 | { 700, 100, "schedule" }, | ||
63 | { 800, 100, "page_fault" }, | ||
64 | { 900, 100, "sys_perf_event_open" }, | ||
65 | }; | ||
66 | |||
67 | static struct { | ||
68 | const char *dso_name; | ||
69 | struct fake_sym *syms; | ||
70 | size_t nr_syms; | ||
71 | } fake_symbols[] = { | ||
72 | { "perf", perf_syms, ARRAY_SIZE(perf_syms) }, | ||
73 | { "bash", bash_syms, ARRAY_SIZE(bash_syms) }, | ||
74 | { "libc", libc_syms, ARRAY_SIZE(libc_syms) }, | ||
75 | { "[kernel]", kernel_syms, ARRAY_SIZE(kernel_syms) }, | ||
76 | }; | ||
77 | |||
78 | static struct machine *setup_fake_machine(struct machines *machines) | ||
79 | { | ||
80 | struct machine *machine = machines__find(machines, HOST_KERNEL_ID); | ||
81 | size_t i; | ||
82 | |||
83 | if (machine == NULL) { | ||
84 | pr_debug("Not enough memory for machine setup\n"); | ||
85 | return NULL; | ||
86 | } | ||
87 | |||
88 | for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { | ||
89 | struct thread *thread; | ||
90 | |||
91 | thread = machine__findnew_thread(machine, fake_threads[i].pid); | ||
92 | if (thread == NULL) | ||
93 | goto out; | ||
94 | |||
95 | thread__set_comm(thread, fake_threads[i].comm); | ||
96 | } | ||
97 | |||
98 | for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { | ||
99 | union perf_event fake_mmap_event = { | ||
100 | .mmap = { | ||
101 | .header = { .misc = PERF_RECORD_MISC_USER, }, | ||
102 | .pid = fake_mmap_info[i].pid, | ||
103 | .start = fake_mmap_info[i].start, | ||
104 | .len = 0x1000ULL, | ||
105 | .pgoff = 0ULL, | ||
106 | }, | ||
107 | }; | ||
108 | |||
109 | strcpy(fake_mmap_event.mmap.filename, | ||
110 | fake_mmap_info[i].filename); | ||
111 | |||
112 | machine__process_mmap_event(machine, &fake_mmap_event); | ||
113 | } | ||
114 | |||
115 | for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { | ||
116 | size_t k; | ||
117 | struct dso *dso; | ||
118 | |||
119 | dso = __dsos__findnew(&machine->user_dsos, | ||
120 | fake_symbols[i].dso_name); | ||
121 | if (dso == NULL) | ||
122 | goto out; | ||
123 | |||
124 | /* emulate dso__load() */ | ||
125 | dso__set_loaded(dso, MAP__FUNCTION); | ||
126 | |||
127 | for (k = 0; k < fake_symbols[i].nr_syms; k++) { | ||
128 | struct symbol *sym; | ||
129 | struct fake_sym *fsym = &fake_symbols[i].syms[k]; | ||
130 | |||
131 | sym = symbol__new(fsym->start, fsym->length, | ||
132 | STB_GLOBAL, fsym->name); | ||
133 | if (sym == NULL) | ||
134 | goto out; | ||
135 | |||
136 | symbols__insert(&dso->symbols[MAP__FUNCTION], sym); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | return machine; | ||
141 | |||
142 | out: | ||
143 | pr_debug("Not enough memory for machine setup\n"); | ||
144 | machine__delete_threads(machine); | ||
145 | machine__delete(machine); | ||
146 | return NULL; | ||
147 | } | ||
148 | |||
149 | struct sample { | ||
150 | u32 pid; | ||
151 | u64 ip; | ||
152 | struct thread *thread; | ||
153 | struct map *map; | ||
154 | struct symbol *sym; | ||
155 | }; | ||
156 | |||
157 | static struct sample fake_common_samples[] = { | ||
158 | /* perf [kernel] schedule() */ | ||
159 | { .pid = 100, .ip = 0xf0000 + 700, }, | ||
160 | /* perf [perf] main() */ | ||
161 | { .pid = 200, .ip = 0x40000 + 700, }, | ||
162 | /* perf [perf] cmd_record() */ | ||
163 | { .pid = 200, .ip = 0x40000 + 900, }, | ||
164 | /* bash [bash] xmalloc() */ | ||
165 | { .pid = 300, .ip = 0x40000 + 800, }, | ||
166 | /* bash [libc] malloc() */ | ||
167 | { .pid = 300, .ip = 0x50000 + 700, }, | ||
168 | }; | ||
169 | |||
170 | static struct sample fake_samples[][5] = { | ||
171 | { | ||
172 | /* perf [perf] run_command() */ | ||
173 | { .pid = 100, .ip = 0x40000 + 800, }, | ||
174 | /* perf [libc] malloc() */ | ||
175 | { .pid = 100, .ip = 0x50000 + 700, }, | ||
176 | /* perf [kernel] page_fault() */ | ||
177 | { .pid = 100, .ip = 0xf0000 + 800, }, | ||
178 | /* perf [kernel] sys_perf_event_open() */ | ||
179 | { .pid = 200, .ip = 0xf0000 + 900, }, | ||
180 | /* bash [libc] free() */ | ||
181 | { .pid = 300, .ip = 0x50000 + 800, }, | ||
182 | }, | ||
183 | { | ||
184 | /* perf [libc] free() */ | ||
185 | { .pid = 200, .ip = 0x50000 + 800, }, | ||
186 | /* bash [libc] malloc() */ | ||
187 | { .pid = 300, .ip = 0x50000 + 700, }, /* will be merged */ | ||
188 | /* bash [bash] xfee() */ | ||
189 | { .pid = 300, .ip = 0x40000 + 900, }, | ||
190 | /* bash [libc] realloc() */ | ||
191 | { .pid = 300, .ip = 0x50000 + 900, }, | ||
192 | /* bash [kernel] page_fault() */ | ||
193 | { .pid = 300, .ip = 0xf0000 + 800, }, | ||
194 | }, | ||
195 | }; | ||
196 | |||
197 | static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | ||
198 | { | ||
199 | struct perf_evsel *evsel; | ||
200 | struct addr_location al; | ||
201 | struct hist_entry *he; | ||
202 | struct perf_sample sample = { .cpu = 0, }; | ||
203 | size_t i = 0, k; | ||
204 | |||
205 | /* | ||
206 | * each evsel will have 10 samples - 5 common and 5 distinct. | ||
207 | * However the second evsel also has a collapsed entry for | ||
208 | * "bash [libc] malloc" so total 9 entries will be in the tree. | ||
209 | */ | ||
210 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
211 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { | ||
212 | const union perf_event event = { | ||
213 | .ip = { | ||
214 | .header = { | ||
215 | .misc = PERF_RECORD_MISC_USER, | ||
216 | }, | ||
217 | .pid = fake_common_samples[k].pid, | ||
218 | .ip = fake_common_samples[k].ip, | ||
219 | }, | ||
220 | }; | ||
221 | |||
222 | if (perf_event__preprocess_sample(&event, machine, &al, | ||
223 | &sample, 0) < 0) | ||
224 | goto out; | ||
225 | |||
226 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1); | ||
227 | if (he == NULL) | ||
228 | goto out; | ||
229 | |||
230 | fake_common_samples[k].thread = al.thread; | ||
231 | fake_common_samples[k].map = al.map; | ||
232 | fake_common_samples[k].sym = al.sym; | ||
233 | } | ||
234 | |||
235 | for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) { | ||
236 | const union perf_event event = { | ||
237 | .ip = { | ||
238 | .header = { | ||
239 | .misc = PERF_RECORD_MISC_USER, | ||
240 | }, | ||
241 | .pid = fake_samples[i][k].pid, | ||
242 | .ip = fake_samples[i][k].ip, | ||
243 | }, | ||
244 | }; | ||
245 | |||
246 | if (perf_event__preprocess_sample(&event, machine, &al, | ||
247 | &sample, 0) < 0) | ||
248 | goto out; | ||
249 | |||
250 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1); | ||
251 | if (he == NULL) | ||
252 | goto out; | ||
253 | |||
254 | fake_samples[i][k].thread = al.thread; | ||
255 | fake_samples[i][k].map = al.map; | ||
256 | fake_samples[i][k].sym = al.sym; | ||
257 | } | ||
258 | i++; | ||
259 | } | ||
260 | |||
261 | return 0; | ||
262 | |||
263 | out: | ||
264 | pr_debug("Not enough memory for adding a hist entry\n"); | ||
265 | return -1; | ||
266 | } | ||
267 | |||
268 | static int find_sample(struct sample *samples, size_t nr_samples, | ||
269 | struct thread *t, struct map *m, struct symbol *s) | ||
270 | { | ||
271 | while (nr_samples--) { | ||
272 | if (samples->thread == t && samples->map == m && | ||
273 | samples->sym == s) | ||
274 | return 1; | ||
275 | samples++; | ||
276 | } | ||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int __validate_match(struct hists *hists) | ||
281 | { | ||
282 | size_t count = 0; | ||
283 | struct rb_root *root; | ||
284 | struct rb_node *node; | ||
285 | |||
286 | /* | ||
287 | * Only entries from fake_common_samples should have a pair. | ||
288 | */ | ||
289 | if (sort__need_collapse) | ||
290 | root = &hists->entries_collapsed; | ||
291 | else | ||
292 | root = hists->entries_in; | ||
293 | |||
294 | node = rb_first(root); | ||
295 | while (node) { | ||
296 | struct hist_entry *he; | ||
297 | |||
298 | he = rb_entry(node, struct hist_entry, rb_node_in); | ||
299 | |||
300 | if (hist_entry__has_pairs(he)) { | ||
301 | if (find_sample(fake_common_samples, | ||
302 | ARRAY_SIZE(fake_common_samples), | ||
303 | he->thread, he->ms.map, he->ms.sym)) { | ||
304 | count++; | ||
305 | } else { | ||
306 | pr_debug("Can't find the matched entry\n"); | ||
307 | return -1; | ||
308 | } | ||
309 | } | ||
310 | |||
311 | node = rb_next(node); | ||
312 | } | ||
313 | |||
314 | if (count != ARRAY_SIZE(fake_common_samples)) { | ||
315 | pr_debug("Invalid count for matched entries: %zd of %zd\n", | ||
316 | count, ARRAY_SIZE(fake_common_samples)); | ||
317 | return -1; | ||
318 | } | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static int validate_match(struct hists *leader, struct hists *other) | ||
324 | { | ||
325 | return __validate_match(leader) || __validate_match(other); | ||
326 | } | ||
327 | |||
328 | static int __validate_link(struct hists *hists, int idx) | ||
329 | { | ||
330 | size_t count = 0; | ||
331 | size_t count_pair = 0; | ||
332 | size_t count_dummy = 0; | ||
333 | struct rb_root *root; | ||
334 | struct rb_node *node; | ||
335 | |||
336 | /* | ||
337 | * Leader hists (idx = 0) will have dummy entries from other, | ||
338 | * and some entries will have no pair. However every entry | ||
339 | * in other hists should have (dummy) pair. | ||
340 | */ | ||
341 | if (sort__need_collapse) | ||
342 | root = &hists->entries_collapsed; | ||
343 | else | ||
344 | root = hists->entries_in; | ||
345 | |||
346 | node = rb_first(root); | ||
347 | while (node) { | ||
348 | struct hist_entry *he; | ||
349 | |||
350 | he = rb_entry(node, struct hist_entry, rb_node_in); | ||
351 | |||
352 | if (hist_entry__has_pairs(he)) { | ||
353 | if (!find_sample(fake_common_samples, | ||
354 | ARRAY_SIZE(fake_common_samples), | ||
355 | he->thread, he->ms.map, he->ms.sym) && | ||
356 | !find_sample(fake_samples[idx], | ||
357 | ARRAY_SIZE(fake_samples[idx]), | ||
358 | he->thread, he->ms.map, he->ms.sym)) { | ||
359 | count_dummy++; | ||
360 | } | ||
361 | count_pair++; | ||
362 | } else if (idx) { | ||
363 | pr_debug("A entry from the other hists should have pair\n"); | ||
364 | return -1; | ||
365 | } | ||
366 | |||
367 | count++; | ||
368 | node = rb_next(node); | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * Note that we have a entry collapsed in the other (idx = 1) hists. | ||
373 | */ | ||
374 | if (idx == 0) { | ||
375 | if (count_dummy != ARRAY_SIZE(fake_samples[1]) - 1) { | ||
376 | pr_debug("Invalid count of dummy entries: %zd of %zd\n", | ||
377 | count_dummy, ARRAY_SIZE(fake_samples[1]) - 1); | ||
378 | return -1; | ||
379 | } | ||
380 | if (count != count_pair + ARRAY_SIZE(fake_samples[0])) { | ||
381 | pr_debug("Invalid count of total leader entries: %zd of %zd\n", | ||
382 | count, count_pair + ARRAY_SIZE(fake_samples[0])); | ||
383 | return -1; | ||
384 | } | ||
385 | } else { | ||
386 | if (count != count_pair) { | ||
387 | pr_debug("Invalid count of total other entries: %zd of %zd\n", | ||
388 | count, count_pair); | ||
389 | return -1; | ||
390 | } | ||
391 | if (count_dummy > 0) { | ||
392 | pr_debug("Other hists should not have dummy entries: %zd\n", | ||
393 | count_dummy); | ||
394 | return -1; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static int validate_link(struct hists *leader, struct hists *other) | ||
402 | { | ||
403 | return __validate_link(leader, 0) || __validate_link(other, 1); | ||
404 | } | ||
405 | |||
406 | static void print_hists(struct hists *hists) | ||
407 | { | ||
408 | int i = 0; | ||
409 | struct rb_root *root; | ||
410 | struct rb_node *node; | ||
411 | |||
412 | if (sort__need_collapse) | ||
413 | root = &hists->entries_collapsed; | ||
414 | else | ||
415 | root = hists->entries_in; | ||
416 | |||
417 | pr_info("----- %s --------\n", __func__); | ||
418 | node = rb_first(root); | ||
419 | while (node) { | ||
420 | struct hist_entry *he; | ||
421 | |||
422 | he = rb_entry(node, struct hist_entry, rb_node_in); | ||
423 | |||
424 | pr_info("%2d: entry: %-8s [%-8s] %20s: period = %"PRIu64"\n", | ||
425 | i, he->thread->comm, he->ms.map->dso->short_name, | ||
426 | he->ms.sym->name, he->stat.period); | ||
427 | |||
428 | i++; | ||
429 | node = rb_next(node); | ||
430 | } | ||
431 | } | ||
432 | |||
433 | int test__hists_link(void) | ||
434 | { | ||
435 | int err = -1; | ||
436 | struct machines machines; | ||
437 | struct machine *machine = NULL; | ||
438 | struct perf_evsel *evsel, *first; | ||
439 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
440 | |||
441 | if (evlist == NULL) | ||
442 | return -ENOMEM; | ||
443 | |||
444 | err = parse_events(evlist, "cpu-clock"); | ||
445 | if (err) | ||
446 | goto out; | ||
447 | err = parse_events(evlist, "task-clock"); | ||
448 | if (err) | ||
449 | goto out; | ||
450 | |||
451 | /* default sort order (comm,dso,sym) will be used */ | ||
452 | if (setup_sorting() < 0) | ||
453 | goto out; | ||
454 | |||
455 | machines__init(&machines); | ||
456 | |||
457 | /* setup threads/dso/map/symbols also */ | ||
458 | machine = setup_fake_machine(&machines); | ||
459 | if (!machine) | ||
460 | goto out; | ||
461 | |||
462 | if (verbose > 1) | ||
463 | machine__fprintf(machine, stderr); | ||
464 | |||
465 | /* process sample events */ | ||
466 | err = add_hist_entries(evlist, machine); | ||
467 | if (err < 0) | ||
468 | goto out; | ||
469 | |||
470 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
471 | hists__collapse_resort(&evsel->hists); | ||
472 | |||
473 | if (verbose > 2) | ||
474 | print_hists(&evsel->hists); | ||
475 | } | ||
476 | |||
477 | first = perf_evlist__first(evlist); | ||
478 | evsel = perf_evlist__last(evlist); | ||
479 | |||
480 | /* match common entries */ | ||
481 | hists__match(&first->hists, &evsel->hists); | ||
482 | err = validate_match(&first->hists, &evsel->hists); | ||
483 | if (err) | ||
484 | goto out; | ||
485 | |||
486 | /* link common and/or dummy entries */ | ||
487 | hists__link(&first->hists, &evsel->hists); | ||
488 | err = validate_link(&first->hists, &evsel->hists); | ||
489 | if (err) | ||
490 | goto out; | ||
491 | |||
492 | err = 0; | ||
493 | |||
494 | out: | ||
495 | /* tear down everything */ | ||
496 | perf_evlist__delete(evlist); | ||
497 | machines__exit(&machines); | ||
498 | |||
499 | return err; | ||
500 | } | ||
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c new file mode 100644 index 000000000000..cdd50755af51 --- /dev/null +++ b/tools/perf/tests/mmap-basic.c | |||
@@ -0,0 +1,148 @@ | |||
1 | #include "evlist.h" | ||
2 | #include "evsel.h" | ||
3 | #include "thread_map.h" | ||
4 | #include "cpumap.h" | ||
5 | #include "tests.h" | ||
6 | |||
7 | /* | ||
8 | * This test will generate random numbers of calls to some getpid syscalls, | ||
9 | * then establish an mmap for a group of events that are created to monitor | ||
10 | * the syscalls. | ||
11 | * | ||
12 | * It will receive the events, using mmap, use its PERF_SAMPLE_ID generated | ||
13 | * sample.id field to map back to its respective perf_evsel instance. | ||
14 | * | ||
15 | * Then it checks if the number of syscalls reported as perf events by | ||
16 | * the kernel corresponds to the number of syscalls made. | ||
17 | */ | ||
18 | int test__basic_mmap(void) | ||
19 | { | ||
20 | int err = -1; | ||
21 | union perf_event *event; | ||
22 | struct thread_map *threads; | ||
23 | struct cpu_map *cpus; | ||
24 | struct perf_evlist *evlist; | ||
25 | cpu_set_t cpu_set; | ||
26 | const char *syscall_names[] = { "getsid", "getppid", "getpgrp", | ||
27 | "getpgid", }; | ||
28 | pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp, | ||
29 | (void*)getpgid }; | ||
30 | #define nsyscalls ARRAY_SIZE(syscall_names) | ||
31 | unsigned int nr_events[nsyscalls], | ||
32 | expected_nr_events[nsyscalls], i, j; | ||
33 | struct perf_evsel *evsels[nsyscalls], *evsel; | ||
34 | |||
35 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
36 | if (threads == NULL) { | ||
37 | pr_debug("thread_map__new\n"); | ||
38 | return -1; | ||
39 | } | ||
40 | |||
41 | cpus = cpu_map__new(NULL); | ||
42 | if (cpus == NULL) { | ||
43 | pr_debug("cpu_map__new\n"); | ||
44 | goto out_free_threads; | ||
45 | } | ||
46 | |||
47 | CPU_ZERO(&cpu_set); | ||
48 | CPU_SET(cpus->map[0], &cpu_set); | ||
49 | sched_setaffinity(0, sizeof(cpu_set), &cpu_set); | ||
50 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
51 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
52 | cpus->map[0], strerror(errno)); | ||
53 | goto out_free_cpus; | ||
54 | } | ||
55 | |||
56 | evlist = perf_evlist__new(cpus, threads); | ||
57 | if (evlist == NULL) { | ||
58 | pr_debug("perf_evlist__new\n"); | ||
59 | goto out_free_cpus; | ||
60 | } | ||
61 | |||
62 | for (i = 0; i < nsyscalls; ++i) { | ||
63 | char name[64]; | ||
64 | |||
65 | snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]); | ||
66 | evsels[i] = perf_evsel__newtp("syscalls", name, i); | ||
67 | if (evsels[i] == NULL) { | ||
68 | pr_debug("perf_evsel__new\n"); | ||
69 | goto out_free_evlist; | ||
70 | } | ||
71 | |||
72 | evsels[i]->attr.wakeup_events = 1; | ||
73 | perf_evsel__set_sample_id(evsels[i]); | ||
74 | |||
75 | perf_evlist__add(evlist, evsels[i]); | ||
76 | |||
77 | if (perf_evsel__open(evsels[i], cpus, threads) < 0) { | ||
78 | pr_debug("failed to open counter: %s, " | ||
79 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
80 | strerror(errno)); | ||
81 | goto out_close_fd; | ||
82 | } | ||
83 | |||
84 | nr_events[i] = 0; | ||
85 | expected_nr_events[i] = 1 + rand() % 127; | ||
86 | } | ||
87 | |||
88 | if (perf_evlist__mmap(evlist, 128, true) < 0) { | ||
89 | pr_debug("failed to mmap events: %d (%s)\n", errno, | ||
90 | strerror(errno)); | ||
91 | goto out_close_fd; | ||
92 | } | ||
93 | |||
94 | for (i = 0; i < nsyscalls; ++i) | ||
95 | for (j = 0; j < expected_nr_events[i]; ++j) { | ||
96 | int foo = syscalls[i](); | ||
97 | ++foo; | ||
98 | } | ||
99 | |||
100 | while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { | ||
101 | struct perf_sample sample; | ||
102 | |||
103 | if (event->header.type != PERF_RECORD_SAMPLE) { | ||
104 | pr_debug("unexpected %s event\n", | ||
105 | perf_event__name(event->header.type)); | ||
106 | goto out_munmap; | ||
107 | } | ||
108 | |||
109 | err = perf_evlist__parse_sample(evlist, event, &sample); | ||
110 | if (err) { | ||
111 | pr_err("Can't parse sample, err = %d\n", err); | ||
112 | goto out_munmap; | ||
113 | } | ||
114 | |||
115 | err = -1; | ||
116 | evsel = perf_evlist__id2evsel(evlist, sample.id); | ||
117 | if (evsel == NULL) { | ||
118 | pr_debug("event with id %" PRIu64 | ||
119 | " doesn't map to an evsel\n", sample.id); | ||
120 | goto out_munmap; | ||
121 | } | ||
122 | nr_events[evsel->idx]++; | ||
123 | } | ||
124 | |||
125 | err = 0; | ||
126 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
127 | if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) { | ||
128 | pr_debug("expected %d %s events, got %d\n", | ||
129 | expected_nr_events[evsel->idx], | ||
130 | perf_evsel__name(evsel), nr_events[evsel->idx]); | ||
131 | err = -1; | ||
132 | goto out_munmap; | ||
133 | } | ||
134 | } | ||
135 | |||
136 | out_munmap: | ||
137 | perf_evlist__munmap(evlist); | ||
138 | out_close_fd: | ||
139 | for (i = 0; i < nsyscalls; ++i) | ||
140 | perf_evsel__close_fd(evsels[i], 1, threads->nr); | ||
141 | out_free_evlist: | ||
142 | perf_evlist__delete(evlist); | ||
143 | out_free_cpus: | ||
144 | cpu_map__delete(cpus); | ||
145 | out_free_threads: | ||
146 | thread_map__delete(threads); | ||
147 | return err; | ||
148 | } | ||
diff --git a/tools/perf/tests/open-syscall-all-cpus.c b/tools/perf/tests/open-syscall-all-cpus.c new file mode 100644 index 000000000000..b0657a9ccda6 --- /dev/null +++ b/tools/perf/tests/open-syscall-all-cpus.c | |||
@@ -0,0 +1,109 @@ | |||
1 | #include "evsel.h" | ||
2 | #include "tests.h" | ||
3 | #include "thread_map.h" | ||
4 | #include "cpumap.h" | ||
5 | #include "debug.h" | ||
6 | |||
7 | int test__open_syscall_event_on_all_cpus(void) | ||
8 | { | ||
9 | int err = -1, fd, cpu; | ||
10 | struct cpu_map *cpus; | ||
11 | struct perf_evsel *evsel; | ||
12 | unsigned int nr_open_calls = 111, i; | ||
13 | cpu_set_t cpu_set; | ||
14 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
15 | |||
16 | if (threads == NULL) { | ||
17 | pr_debug("thread_map__new\n"); | ||
18 | return -1; | ||
19 | } | ||
20 | |||
21 | cpus = cpu_map__new(NULL); | ||
22 | if (cpus == NULL) { | ||
23 | pr_debug("cpu_map__new\n"); | ||
24 | goto out_thread_map_delete; | ||
25 | } | ||
26 | |||
27 | CPU_ZERO(&cpu_set); | ||
28 | |||
29 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); | ||
30 | if (evsel == NULL) { | ||
31 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
32 | goto out_thread_map_delete; | ||
33 | } | ||
34 | |||
35 | if (perf_evsel__open(evsel, cpus, threads) < 0) { | ||
36 | pr_debug("failed to open counter: %s, " | ||
37 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
38 | strerror(errno)); | ||
39 | goto out_evsel_delete; | ||
40 | } | ||
41 | |||
42 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | ||
43 | unsigned int ncalls = nr_open_calls + cpu; | ||
44 | /* | ||
45 | * XXX eventually lift this restriction in a way that | ||
46 | * keeps perf building on older glibc installations | ||
47 | * without CPU_ALLOC. 1024 cpus in 2010 still seems | ||
48 | * a reasonable upper limit tho :-) | ||
49 | */ | ||
50 | if (cpus->map[cpu] >= CPU_SETSIZE) { | ||
51 | pr_debug("Ignoring CPU %d\n", cpus->map[cpu]); | ||
52 | continue; | ||
53 | } | ||
54 | |||
55 | CPU_SET(cpus->map[cpu], &cpu_set); | ||
56 | if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) { | ||
57 | pr_debug("sched_setaffinity() failed on CPU %d: %s ", | ||
58 | cpus->map[cpu], | ||
59 | strerror(errno)); | ||
60 | goto out_close_fd; | ||
61 | } | ||
62 | for (i = 0; i < ncalls; ++i) { | ||
63 | fd = open("/etc/passwd", O_RDONLY); | ||
64 | close(fd); | ||
65 | } | ||
66 | CPU_CLR(cpus->map[cpu], &cpu_set); | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * Here we need to explicitely preallocate the counts, as if | ||
71 | * we use the auto allocation it will allocate just for 1 cpu, | ||
72 | * as we start by cpu 0. | ||
73 | */ | ||
74 | if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) { | ||
75 | pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr); | ||
76 | goto out_close_fd; | ||
77 | } | ||
78 | |||
79 | err = 0; | ||
80 | |||
81 | for (cpu = 0; cpu < cpus->nr; ++cpu) { | ||
82 | unsigned int expected; | ||
83 | |||
84 | if (cpus->map[cpu] >= CPU_SETSIZE) | ||
85 | continue; | ||
86 | |||
87 | if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) { | ||
88 | pr_debug("perf_evsel__read_on_cpu\n"); | ||
89 | err = -1; | ||
90 | break; | ||
91 | } | ||
92 | |||
93 | expected = nr_open_calls + cpu; | ||
94 | if (evsel->counts->cpu[cpu].val != expected) { | ||
95 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n", | ||
96 | expected, cpus->map[cpu], evsel->counts->cpu[cpu].val); | ||
97 | err = -1; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | perf_evsel__free_counts(evsel); | ||
102 | out_close_fd: | ||
103 | perf_evsel__close_fd(evsel, 1, threads->nr); | ||
104 | out_evsel_delete: | ||
105 | perf_evsel__delete(evsel); | ||
106 | out_thread_map_delete: | ||
107 | thread_map__delete(threads); | ||
108 | return err; | ||
109 | } | ||
diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c new file mode 100644 index 000000000000..1c52fdc1164e --- /dev/null +++ b/tools/perf/tests/open-syscall-tp-fields.c | |||
@@ -0,0 +1,117 @@ | |||
1 | #include "perf.h" | ||
2 | #include "evlist.h" | ||
3 | #include "evsel.h" | ||
4 | #include "thread_map.h" | ||
5 | #include "tests.h" | ||
6 | |||
7 | int test__syscall_open_tp_fields(void) | ||
8 | { | ||
9 | struct perf_record_opts opts = { | ||
10 | .target = { | ||
11 | .uid = UINT_MAX, | ||
12 | .uses_mmap = true, | ||
13 | }, | ||
14 | .no_delay = true, | ||
15 | .freq = 1, | ||
16 | .mmap_pages = 256, | ||
17 | .raw_samples = true, | ||
18 | }; | ||
19 | const char *filename = "/etc/passwd"; | ||
20 | int flags = O_RDONLY | O_DIRECTORY; | ||
21 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
22 | struct perf_evsel *evsel; | ||
23 | int err = -1, i, nr_events = 0, nr_polls = 0; | ||
24 | |||
25 | if (evlist == NULL) { | ||
26 | pr_debug("%s: perf_evlist__new\n", __func__); | ||
27 | goto out; | ||
28 | } | ||
29 | |||
30 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); | ||
31 | if (evsel == NULL) { | ||
32 | pr_debug("%s: perf_evsel__newtp\n", __func__); | ||
33 | goto out_delete_evlist; | ||
34 | } | ||
35 | |||
36 | perf_evlist__add(evlist, evsel); | ||
37 | |||
38 | err = perf_evlist__create_maps(evlist, &opts.target); | ||
39 | if (err < 0) { | ||
40 | pr_debug("%s: perf_evlist__create_maps\n", __func__); | ||
41 | goto out_delete_evlist; | ||
42 | } | ||
43 | |||
44 | perf_evsel__config(evsel, &opts); | ||
45 | |||
46 | evlist->threads->map[0] = getpid(); | ||
47 | |||
48 | err = perf_evlist__open(evlist); | ||
49 | if (err < 0) { | ||
50 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | ||
51 | goto out_delete_evlist; | ||
52 | } | ||
53 | |||
54 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | ||
55 | if (err < 0) { | ||
56 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | ||
57 | goto out_delete_evlist; | ||
58 | } | ||
59 | |||
60 | perf_evlist__enable(evlist); | ||
61 | |||
62 | /* | ||
63 | * Generate the event: | ||
64 | */ | ||
65 | open(filename, flags); | ||
66 | |||
67 | while (1) { | ||
68 | int before = nr_events; | ||
69 | |||
70 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
71 | union perf_event *event; | ||
72 | |||
73 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
74 | const u32 type = event->header.type; | ||
75 | int tp_flags; | ||
76 | struct perf_sample sample; | ||
77 | |||
78 | ++nr_events; | ||
79 | |||
80 | if (type != PERF_RECORD_SAMPLE) | ||
81 | continue; | ||
82 | |||
83 | err = perf_evsel__parse_sample(evsel, event, &sample); | ||
84 | if (err) { | ||
85 | pr_err("Can't parse sample, err = %d\n", err); | ||
86 | goto out_munmap; | ||
87 | } | ||
88 | |||
89 | tp_flags = perf_evsel__intval(evsel, &sample, "flags"); | ||
90 | |||
91 | if (flags != tp_flags) { | ||
92 | pr_debug("%s: Expected flags=%#x, got %#x\n", | ||
93 | __func__, flags, tp_flags); | ||
94 | goto out_munmap; | ||
95 | } | ||
96 | |||
97 | goto out_ok; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | if (nr_events == before) | ||
102 | poll(evlist->pollfd, evlist->nr_fds, 10); | ||
103 | |||
104 | if (++nr_polls > 5) { | ||
105 | pr_debug("%s: no events!\n", __func__); | ||
106 | goto out_munmap; | ||
107 | } | ||
108 | } | ||
109 | out_ok: | ||
110 | err = 0; | ||
111 | out_munmap: | ||
112 | perf_evlist__munmap(evlist); | ||
113 | out_delete_evlist: | ||
114 | perf_evlist__delete(evlist); | ||
115 | out: | ||
116 | return err; | ||
117 | } | ||
diff --git a/tools/perf/tests/open-syscall.c b/tools/perf/tests/open-syscall.c new file mode 100644 index 000000000000..befc0671f95d --- /dev/null +++ b/tools/perf/tests/open-syscall.c | |||
@@ -0,0 +1,55 @@ | |||
1 | #include "thread_map.h" | ||
2 | #include "evsel.h" | ||
3 | #include "debug.h" | ||
4 | #include "tests.h" | ||
5 | |||
6 | int test__open_syscall_event(void) | ||
7 | { | ||
8 | int err = -1, fd; | ||
9 | struct perf_evsel *evsel; | ||
10 | unsigned int nr_open_calls = 111, i; | ||
11 | struct thread_map *threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
12 | |||
13 | if (threads == NULL) { | ||
14 | pr_debug("thread_map__new\n"); | ||
15 | return -1; | ||
16 | } | ||
17 | |||
18 | evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0); | ||
19 | if (evsel == NULL) { | ||
20 | pr_debug("is debugfs mounted on /sys/kernel/debug?\n"); | ||
21 | goto out_thread_map_delete; | ||
22 | } | ||
23 | |||
24 | if (perf_evsel__open_per_thread(evsel, threads) < 0) { | ||
25 | pr_debug("failed to open counter: %s, " | ||
26 | "tweak /proc/sys/kernel/perf_event_paranoid?\n", | ||
27 | strerror(errno)); | ||
28 | goto out_evsel_delete; | ||
29 | } | ||
30 | |||
31 | for (i = 0; i < nr_open_calls; ++i) { | ||
32 | fd = open("/etc/passwd", O_RDONLY); | ||
33 | close(fd); | ||
34 | } | ||
35 | |||
36 | if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) { | ||
37 | pr_debug("perf_evsel__read_on_cpu\n"); | ||
38 | goto out_close_fd; | ||
39 | } | ||
40 | |||
41 | if (evsel->counts->cpu[0].val != nr_open_calls) { | ||
42 | pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n", | ||
43 | nr_open_calls, evsel->counts->cpu[0].val); | ||
44 | goto out_close_fd; | ||
45 | } | ||
46 | |||
47 | err = 0; | ||
48 | out_close_fd: | ||
49 | perf_evsel__close_fd(evsel, 1, threads->nr); | ||
50 | out_evsel_delete: | ||
51 | perf_evsel__delete(evsel); | ||
52 | out_thread_map_delete: | ||
53 | thread_map__delete(threads); | ||
54 | return err; | ||
55 | } | ||
diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/tests/parse-events.c index 516ecd9ddd6e..c5636f36fe31 100644 --- a/tools/perf/util/parse-events-test.c +++ b/tools/perf/tests/parse-events.c | |||
@@ -3,7 +3,9 @@ | |||
3 | #include "evsel.h" | 3 | #include "evsel.h" |
4 | #include "evlist.h" | 4 | #include "evlist.h" |
5 | #include "sysfs.h" | 5 | #include "sysfs.h" |
6 | #include "../../../include/linux/hw_breakpoint.h" | 6 | #include "debugfs.h" |
7 | #include "tests.h" | ||
8 | #include <linux/hw_breakpoint.h> | ||
7 | 9 | ||
8 | #define TEST_ASSERT_VAL(text, cond) \ | 10 | #define TEST_ASSERT_VAL(text, cond) \ |
9 | do { \ | 11 | do { \ |
@@ -21,6 +23,7 @@ static int test__checkevent_tracepoint(struct perf_evlist *evlist) | |||
21 | struct perf_evsel *evsel = perf_evlist__first(evlist); | 23 | struct perf_evsel *evsel = perf_evlist__first(evlist); |
22 | 24 | ||
23 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 25 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
26 | TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups); | ||
24 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); | 27 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); |
25 | TEST_ASSERT_VAL("wrong sample_type", | 28 | TEST_ASSERT_VAL("wrong sample_type", |
26 | PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type); | 29 | PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type); |
@@ -33,6 +36,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist) | |||
33 | struct perf_evsel *evsel; | 36 | struct perf_evsel *evsel; |
34 | 37 | ||
35 | TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); | 38 | TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); |
39 | TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups); | ||
36 | 40 | ||
37 | list_for_each_entry(evsel, &evlist->entries, node) { | 41 | list_for_each_entry(evsel, &evlist->entries, node) { |
38 | TEST_ASSERT_VAL("wrong type", | 42 | TEST_ASSERT_VAL("wrong type", |
@@ -443,12 +447,29 @@ static int test__checkevent_pmu_name(struct perf_evlist *evlist) | |||
443 | return 0; | 447 | return 0; |
444 | } | 448 | } |
445 | 449 | ||
450 | static int test__checkevent_pmu_events(struct perf_evlist *evlist) | ||
451 | { | ||
452 | struct perf_evsel *evsel; | ||
453 | |||
454 | evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
455 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
456 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | ||
457 | TEST_ASSERT_VAL("wrong exclude_user", | ||
458 | !evsel->attr.exclude_user); | ||
459 | TEST_ASSERT_VAL("wrong exclude_kernel", | ||
460 | evsel->attr.exclude_kernel); | ||
461 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
462 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
463 | |||
464 | return 0; | ||
465 | } | ||
466 | |||
446 | static int test__checkterms_simple(struct list_head *terms) | 467 | static int test__checkterms_simple(struct list_head *terms) |
447 | { | 468 | { |
448 | struct parse_events__term *term; | 469 | struct parse_events_term *term; |
449 | 470 | ||
450 | /* config=10 */ | 471 | /* config=10 */ |
451 | term = list_entry(terms->next, struct parse_events__term, list); | 472 | term = list_entry(terms->next, struct parse_events_term, list); |
452 | TEST_ASSERT_VAL("wrong type term", | 473 | TEST_ASSERT_VAL("wrong type term", |
453 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG); | 474 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG); |
454 | TEST_ASSERT_VAL("wrong type val", | 475 | TEST_ASSERT_VAL("wrong type val", |
@@ -457,7 +478,7 @@ static int test__checkterms_simple(struct list_head *terms) | |||
457 | TEST_ASSERT_VAL("wrong config", !term->config); | 478 | TEST_ASSERT_VAL("wrong config", !term->config); |
458 | 479 | ||
459 | /* config1 */ | 480 | /* config1 */ |
460 | term = list_entry(term->list.next, struct parse_events__term, list); | 481 | term = list_entry(term->list.next, struct parse_events_term, list); |
461 | TEST_ASSERT_VAL("wrong type term", | 482 | TEST_ASSERT_VAL("wrong type term", |
462 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG1); | 483 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG1); |
463 | TEST_ASSERT_VAL("wrong type val", | 484 | TEST_ASSERT_VAL("wrong type val", |
@@ -466,7 +487,7 @@ static int test__checkterms_simple(struct list_head *terms) | |||
466 | TEST_ASSERT_VAL("wrong config", !term->config); | 487 | TEST_ASSERT_VAL("wrong config", !term->config); |
467 | 488 | ||
468 | /* config2=3 */ | 489 | /* config2=3 */ |
469 | term = list_entry(term->list.next, struct parse_events__term, list); | 490 | term = list_entry(term->list.next, struct parse_events_term, list); |
470 | TEST_ASSERT_VAL("wrong type term", | 491 | TEST_ASSERT_VAL("wrong type term", |
471 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG2); | 492 | term->type_term == PARSE_EVENTS__TERM_TYPE_CONFIG2); |
472 | TEST_ASSERT_VAL("wrong type val", | 493 | TEST_ASSERT_VAL("wrong type val", |
@@ -475,7 +496,7 @@ static int test__checkterms_simple(struct list_head *terms) | |||
475 | TEST_ASSERT_VAL("wrong config", !term->config); | 496 | TEST_ASSERT_VAL("wrong config", !term->config); |
476 | 497 | ||
477 | /* umask=1*/ | 498 | /* umask=1*/ |
478 | term = list_entry(term->list.next, struct parse_events__term, list); | 499 | term = list_entry(term->list.next, struct parse_events_term, list); |
479 | TEST_ASSERT_VAL("wrong type term", | 500 | TEST_ASSERT_VAL("wrong type term", |
480 | term->type_term == PARSE_EVENTS__TERM_TYPE_USER); | 501 | term->type_term == PARSE_EVENTS__TERM_TYPE_USER); |
481 | TEST_ASSERT_VAL("wrong type val", | 502 | TEST_ASSERT_VAL("wrong type val", |
@@ -491,6 +512,7 @@ static int test__group1(struct perf_evlist *evlist) | |||
491 | struct perf_evsel *evsel, *leader; | 512 | struct perf_evsel *evsel, *leader; |
492 | 513 | ||
493 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | 514 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); |
515 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
494 | 516 | ||
495 | /* instructions:k */ | 517 | /* instructions:k */ |
496 | evsel = leader = perf_evlist__first(evlist); | 518 | evsel = leader = perf_evlist__first(evlist); |
@@ -503,7 +525,9 @@ static int test__group1(struct perf_evlist *evlist) | |||
503 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 525 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
504 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 526 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
505 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 527 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
506 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 528 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
529 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
530 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
507 | 531 | ||
508 | /* cycles:upp */ | 532 | /* cycles:upp */ |
509 | evsel = perf_evsel__next(evsel); | 533 | evsel = perf_evsel__next(evsel); |
@@ -518,6 +542,7 @@ static int test__group1(struct perf_evlist *evlist) | |||
518 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 542 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
519 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); | 543 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); |
520 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 544 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
545 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
521 | 546 | ||
522 | return 0; | 547 | return 0; |
523 | } | 548 | } |
@@ -527,6 +552,7 @@ static int test__group2(struct perf_evlist *evlist) | |||
527 | struct perf_evsel *evsel, *leader; | 552 | struct perf_evsel *evsel, *leader; |
528 | 553 | ||
529 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); | 554 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); |
555 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
530 | 556 | ||
531 | /* faults + :ku modifier */ | 557 | /* faults + :ku modifier */ |
532 | evsel = leader = perf_evlist__first(evlist); | 558 | evsel = leader = perf_evlist__first(evlist); |
@@ -539,7 +565,9 @@ static int test__group2(struct perf_evlist *evlist) | |||
539 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 565 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
540 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 566 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
541 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 567 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
542 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 568 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
569 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
570 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
543 | 571 | ||
544 | /* cache-references + :u modifier */ | 572 | /* cache-references + :u modifier */ |
545 | evsel = perf_evsel__next(evsel); | 573 | evsel = perf_evsel__next(evsel); |
@@ -549,10 +577,11 @@ static int test__group2(struct perf_evlist *evlist) | |||
549 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | 577 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); |
550 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | 578 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); |
551 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 579 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
552 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 580 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); |
553 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 581 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
554 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 582 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
555 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 583 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
584 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
556 | 585 | ||
557 | /* cycles:k */ | 586 | /* cycles:k */ |
558 | evsel = perf_evsel__next(evsel); | 587 | evsel = perf_evsel__next(evsel); |
@@ -565,7 +594,7 @@ static int test__group2(struct perf_evlist *evlist) | |||
565 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 594 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
566 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 595 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
567 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 596 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
568 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 597 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
569 | 598 | ||
570 | return 0; | 599 | return 0; |
571 | } | 600 | } |
@@ -575,6 +604,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
575 | struct perf_evsel *evsel, *leader; | 604 | struct perf_evsel *evsel, *leader; |
576 | 605 | ||
577 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); | 606 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); |
607 | TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups); | ||
578 | 608 | ||
579 | /* group1 syscalls:sys_enter_open:H */ | 609 | /* group1 syscalls:sys_enter_open:H */ |
580 | evsel = leader = perf_evlist__first(evlist); | 610 | evsel = leader = perf_evlist__first(evlist); |
@@ -588,9 +618,11 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
588 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | 618 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); |
589 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 619 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
590 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 620 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
591 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 621 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
592 | TEST_ASSERT_VAL("wrong group name", | 622 | TEST_ASSERT_VAL("wrong group name", |
593 | !strcmp(leader->group_name, "group1")); | 623 | !strcmp(leader->group_name, "group1")); |
624 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
625 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
594 | 626 | ||
595 | /* group1 cycles:kppp */ | 627 | /* group1 cycles:kppp */ |
596 | evsel = perf_evsel__next(evsel); | 628 | evsel = perf_evsel__next(evsel); |
@@ -606,6 +638,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
606 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3); | 638 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3); |
607 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 639 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
608 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 640 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
641 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
609 | 642 | ||
610 | /* group2 cycles + G modifier */ | 643 | /* group2 cycles + G modifier */ |
611 | evsel = leader = perf_evsel__next(evsel); | 644 | evsel = leader = perf_evsel__next(evsel); |
@@ -618,9 +651,11 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
618 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 651 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
619 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 652 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
620 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 653 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
621 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 654 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
622 | TEST_ASSERT_VAL("wrong group name", | 655 | TEST_ASSERT_VAL("wrong group name", |
623 | !strcmp(leader->group_name, "group2")); | 656 | !strcmp(leader->group_name, "group2")); |
657 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
658 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
624 | 659 | ||
625 | /* group2 1:3 + G modifier */ | 660 | /* group2 1:3 + G modifier */ |
626 | evsel = perf_evsel__next(evsel); | 661 | evsel = perf_evsel__next(evsel); |
@@ -633,6 +668,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
633 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 668 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
634 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 669 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
635 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 670 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
671 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
636 | 672 | ||
637 | /* instructions:u */ | 673 | /* instructions:u */ |
638 | evsel = perf_evsel__next(evsel); | 674 | evsel = perf_evsel__next(evsel); |
@@ -645,7 +681,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
645 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | 681 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); |
646 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 682 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
647 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 683 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
648 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 684 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
649 | 685 | ||
650 | return 0; | 686 | return 0; |
651 | } | 687 | } |
@@ -655,6 +691,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) | |||
655 | struct perf_evsel *evsel, *leader; | 691 | struct perf_evsel *evsel, *leader; |
656 | 692 | ||
657 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | 693 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); |
694 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
658 | 695 | ||
659 | /* cycles:u + p */ | 696 | /* cycles:u + p */ |
660 | evsel = leader = perf_evlist__first(evlist); | 697 | evsel = leader = perf_evlist__first(evlist); |
@@ -669,7 +706,9 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) | |||
669 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 706 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
670 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); | 707 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); |
671 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 708 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
672 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 709 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
710 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
711 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
673 | 712 | ||
674 | /* instructions:kp + p */ | 713 | /* instructions:kp + p */ |
675 | evsel = perf_evsel__next(evsel); | 714 | evsel = perf_evsel__next(evsel); |
@@ -684,6 +723,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) | |||
684 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 723 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
685 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); | 724 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); |
686 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 725 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
726 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
687 | 727 | ||
688 | return 0; | 728 | return 0; |
689 | } | 729 | } |
@@ -693,6 +733,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
693 | struct perf_evsel *evsel, *leader; | 733 | struct perf_evsel *evsel, *leader; |
694 | 734 | ||
695 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); | 735 | TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); |
736 | TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups); | ||
696 | 737 | ||
697 | /* cycles + G */ | 738 | /* cycles + G */ |
698 | evsel = leader = perf_evlist__first(evlist); | 739 | evsel = leader = perf_evlist__first(evlist); |
@@ -706,7 +747,9 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
706 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 747 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
707 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 748 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
708 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 749 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
709 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 750 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
751 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
752 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
710 | 753 | ||
711 | /* instructions + G */ | 754 | /* instructions + G */ |
712 | evsel = perf_evsel__next(evsel); | 755 | evsel = perf_evsel__next(evsel); |
@@ -720,6 +763,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
720 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 763 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
721 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 764 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
722 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 765 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
766 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
723 | 767 | ||
724 | /* cycles:G */ | 768 | /* cycles:G */ |
725 | evsel = leader = perf_evsel__next(evsel); | 769 | evsel = leader = perf_evsel__next(evsel); |
@@ -733,7 +777,9 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
733 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 777 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
734 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 778 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
735 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 779 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
736 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 780 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
781 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
782 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
737 | 783 | ||
738 | /* instructions:G */ | 784 | /* instructions:G */ |
739 | evsel = perf_evsel__next(evsel); | 785 | evsel = perf_evsel__next(evsel); |
@@ -747,6 +793,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
747 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | 793 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); |
748 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 794 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
749 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 795 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
796 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
750 | 797 | ||
751 | /* cycles */ | 798 | /* cycles */ |
752 | evsel = perf_evsel__next(evsel); | 799 | evsel = perf_evsel__next(evsel); |
@@ -759,18 +806,235 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
759 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | 806 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); |
760 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 807 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
761 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 808 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
762 | TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL); | 809 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
810 | |||
811 | return 0; | ||
812 | } | ||
813 | |||
814 | static int test__group_gh1(struct perf_evlist *evlist) | ||
815 | { | ||
816 | struct perf_evsel *evsel, *leader; | ||
817 | |||
818 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
819 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
820 | |||
821 | /* cycles + :H group modifier */ | ||
822 | evsel = leader = perf_evlist__first(evlist); | ||
823 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
824 | TEST_ASSERT_VAL("wrong config", | ||
825 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
826 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
827 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
828 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
829 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
830 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
831 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
832 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
833 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | ||
834 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
835 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
836 | |||
837 | /* cache-misses:G + :H group modifier */ | ||
838 | evsel = perf_evsel__next(evsel); | ||
839 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
840 | TEST_ASSERT_VAL("wrong config", | ||
841 | PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config); | ||
842 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
843 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
844 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
845 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
846 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
847 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
848 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
849 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
850 | |||
851 | return 0; | ||
852 | } | ||
853 | |||
854 | static int test__group_gh2(struct perf_evlist *evlist) | ||
855 | { | ||
856 | struct perf_evsel *evsel, *leader; | ||
857 | |||
858 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
859 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
860 | |||
861 | /* cycles + :G group modifier */ | ||
862 | evsel = leader = perf_evlist__first(evlist); | ||
863 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
864 | TEST_ASSERT_VAL("wrong config", | ||
865 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
866 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
867 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
868 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
869 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
870 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
871 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
872 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
873 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | ||
874 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
875 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
876 | |||
877 | /* cache-misses:H + :G group modifier */ | ||
878 | evsel = perf_evsel__next(evsel); | ||
879 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
880 | TEST_ASSERT_VAL("wrong config", | ||
881 | PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config); | ||
882 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
883 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
884 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
885 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
886 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
887 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
888 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
889 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
763 | 890 | ||
764 | return 0; | 891 | return 0; |
765 | } | 892 | } |
766 | 893 | ||
767 | struct test__event_st { | 894 | static int test__group_gh3(struct perf_evlist *evlist) |
895 | { | ||
896 | struct perf_evsel *evsel, *leader; | ||
897 | |||
898 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
899 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
900 | |||
901 | /* cycles:G + :u group modifier */ | ||
902 | evsel = leader = perf_evlist__first(evlist); | ||
903 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
904 | TEST_ASSERT_VAL("wrong config", | ||
905 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
906 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
907 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
908 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
909 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
910 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
911 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
912 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
913 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | ||
914 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
915 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
916 | |||
917 | /* cache-misses:H + :u group modifier */ | ||
918 | evsel = perf_evsel__next(evsel); | ||
919 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
920 | TEST_ASSERT_VAL("wrong config", | ||
921 | PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config); | ||
922 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
923 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
924 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
925 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
926 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
927 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
928 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
929 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
930 | |||
931 | return 0; | ||
932 | } | ||
933 | |||
934 | static int test__group_gh4(struct perf_evlist *evlist) | ||
935 | { | ||
936 | struct perf_evsel *evsel, *leader; | ||
937 | |||
938 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
939 | TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups); | ||
940 | |||
941 | /* cycles:G + :uG group modifier */ | ||
942 | evsel = leader = perf_evlist__first(evlist); | ||
943 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
944 | TEST_ASSERT_VAL("wrong config", | ||
945 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
946 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
947 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
948 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
949 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
950 | TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); | ||
951 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
952 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
953 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | ||
954 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | ||
955 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | ||
956 | |||
957 | /* cache-misses:H + :uG group modifier */ | ||
958 | evsel = perf_evsel__next(evsel); | ||
959 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
960 | TEST_ASSERT_VAL("wrong config", | ||
961 | PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config); | ||
962 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
963 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
964 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
965 | TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest); | ||
966 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
967 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
968 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
969 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | ||
970 | |||
971 | return 0; | ||
972 | } | ||
973 | |||
974 | static int count_tracepoints(void) | ||
975 | { | ||
976 | char events_path[PATH_MAX]; | ||
977 | struct dirent *events_ent; | ||
978 | DIR *events_dir; | ||
979 | int cnt = 0; | ||
980 | |||
981 | scnprintf(events_path, PATH_MAX, "%s/tracing/events", | ||
982 | debugfs_find_mountpoint()); | ||
983 | |||
984 | events_dir = opendir(events_path); | ||
985 | |||
986 | TEST_ASSERT_VAL("Can't open events dir", events_dir); | ||
987 | |||
988 | while ((events_ent = readdir(events_dir))) { | ||
989 | char sys_path[PATH_MAX]; | ||
990 | struct dirent *sys_ent; | ||
991 | DIR *sys_dir; | ||
992 | |||
993 | if (!strcmp(events_ent->d_name, ".") | ||
994 | || !strcmp(events_ent->d_name, "..") | ||
995 | || !strcmp(events_ent->d_name, "enable") | ||
996 | || !strcmp(events_ent->d_name, "header_event") | ||
997 | || !strcmp(events_ent->d_name, "header_page")) | ||
998 | continue; | ||
999 | |||
1000 | scnprintf(sys_path, PATH_MAX, "%s/%s", | ||
1001 | events_path, events_ent->d_name); | ||
1002 | |||
1003 | sys_dir = opendir(sys_path); | ||
1004 | TEST_ASSERT_VAL("Can't open sys dir", sys_dir); | ||
1005 | |||
1006 | while ((sys_ent = readdir(sys_dir))) { | ||
1007 | if (!strcmp(sys_ent->d_name, ".") | ||
1008 | || !strcmp(sys_ent->d_name, "..") | ||
1009 | || !strcmp(sys_ent->d_name, "enable") | ||
1010 | || !strcmp(sys_ent->d_name, "filter")) | ||
1011 | continue; | ||
1012 | |||
1013 | cnt++; | ||
1014 | } | ||
1015 | |||
1016 | closedir(sys_dir); | ||
1017 | } | ||
1018 | |||
1019 | closedir(events_dir); | ||
1020 | return cnt; | ||
1021 | } | ||
1022 | |||
1023 | static int test__all_tracepoints(struct perf_evlist *evlist) | ||
1024 | { | ||
1025 | TEST_ASSERT_VAL("wrong events count", | ||
1026 | count_tracepoints() == evlist->nr_entries); | ||
1027 | |||
1028 | return test__checkevent_tracepoint_multi(evlist); | ||
1029 | } | ||
1030 | |||
1031 | struct evlist_test { | ||
768 | const char *name; | 1032 | const char *name; |
769 | __u32 type; | 1033 | __u32 type; |
770 | int (*check)(struct perf_evlist *evlist); | 1034 | int (*check)(struct perf_evlist *evlist); |
771 | }; | 1035 | }; |
772 | 1036 | ||
773 | static struct test__event_st test__events[] = { | 1037 | static struct evlist_test test__events[] = { |
774 | [0] = { | 1038 | [0] = { |
775 | .name = "syscalls:sys_enter_open", | 1039 | .name = "syscalls:sys_enter_open", |
776 | .check = test__checkevent_tracepoint, | 1040 | .check = test__checkevent_tracepoint, |
@@ -903,9 +1167,29 @@ static struct test__event_st test__events[] = { | |||
903 | .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles", | 1167 | .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles", |
904 | .check = test__group5, | 1168 | .check = test__group5, |
905 | }, | 1169 | }, |
1170 | [33] = { | ||
1171 | .name = "*:*", | ||
1172 | .check = test__all_tracepoints, | ||
1173 | }, | ||
1174 | [34] = { | ||
1175 | .name = "{cycles,cache-misses:G}:H", | ||
1176 | .check = test__group_gh1, | ||
1177 | }, | ||
1178 | [35] = { | ||
1179 | .name = "{cycles,cache-misses:H}:G", | ||
1180 | .check = test__group_gh2, | ||
1181 | }, | ||
1182 | [36] = { | ||
1183 | .name = "{cycles:G,cache-misses:H}:u", | ||
1184 | .check = test__group_gh3, | ||
1185 | }, | ||
1186 | [37] = { | ||
1187 | .name = "{cycles:G,cache-misses:H}:uG", | ||
1188 | .check = test__group_gh4, | ||
1189 | }, | ||
906 | }; | 1190 | }; |
907 | 1191 | ||
908 | static struct test__event_st test__events_pmu[] = { | 1192 | static struct evlist_test test__events_pmu[] = { |
909 | [0] = { | 1193 | [0] = { |
910 | .name = "cpu/config=10,config1,config2=3,period=1000/u", | 1194 | .name = "cpu/config=10,config1,config2=3,period=1000/u", |
911 | .check = test__checkevent_pmu, | 1195 | .check = test__checkevent_pmu, |
@@ -916,20 +1200,20 @@ static struct test__event_st test__events_pmu[] = { | |||
916 | }, | 1200 | }, |
917 | }; | 1201 | }; |
918 | 1202 | ||
919 | struct test__term { | 1203 | struct terms_test { |
920 | const char *str; | 1204 | const char *str; |
921 | __u32 type; | 1205 | __u32 type; |
922 | int (*check)(struct list_head *terms); | 1206 | int (*check)(struct list_head *terms); |
923 | }; | 1207 | }; |
924 | 1208 | ||
925 | static struct test__term test__terms[] = { | 1209 | static struct terms_test test__terms[] = { |
926 | [0] = { | 1210 | [0] = { |
927 | .str = "config=10,config1,config2=3,umask=1", | 1211 | .str = "config=10,config1,config2=3,umask=1", |
928 | .check = test__checkterms_simple, | 1212 | .check = test__checkterms_simple, |
929 | }, | 1213 | }, |
930 | }; | 1214 | }; |
931 | 1215 | ||
932 | static int test_event(struct test__event_st *e) | 1216 | static int test_event(struct evlist_test *e) |
933 | { | 1217 | { |
934 | struct perf_evlist *evlist; | 1218 | struct perf_evlist *evlist; |
935 | int ret; | 1219 | int ret; |
@@ -938,7 +1222,7 @@ static int test_event(struct test__event_st *e) | |||
938 | if (evlist == NULL) | 1222 | if (evlist == NULL) |
939 | return -ENOMEM; | 1223 | return -ENOMEM; |
940 | 1224 | ||
941 | ret = parse_events(evlist, e->name, 0); | 1225 | ret = parse_events(evlist, e->name); |
942 | if (ret) { | 1226 | if (ret) { |
943 | pr_debug("failed to parse event '%s', err %d\n", | 1227 | pr_debug("failed to parse event '%s', err %d\n", |
944 | e->name, ret); | 1228 | e->name, ret); |
@@ -951,13 +1235,13 @@ static int test_event(struct test__event_st *e) | |||
951 | return ret; | 1235 | return ret; |
952 | } | 1236 | } |
953 | 1237 | ||
954 | static int test_events(struct test__event_st *events, unsigned cnt) | 1238 | static int test_events(struct evlist_test *events, unsigned cnt) |
955 | { | 1239 | { |
956 | int ret1, ret2 = 0; | 1240 | int ret1, ret2 = 0; |
957 | unsigned i; | 1241 | unsigned i; |
958 | 1242 | ||
959 | for (i = 0; i < cnt; i++) { | 1243 | for (i = 0; i < cnt; i++) { |
960 | struct test__event_st *e = &events[i]; | 1244 | struct evlist_test *e = &events[i]; |
961 | 1245 | ||
962 | pr_debug("running test %d '%s'\n", i, e->name); | 1246 | pr_debug("running test %d '%s'\n", i, e->name); |
963 | ret1 = test_event(e); | 1247 | ret1 = test_event(e); |
@@ -968,7 +1252,7 @@ static int test_events(struct test__event_st *events, unsigned cnt) | |||
968 | return ret2; | 1252 | return ret2; |
969 | } | 1253 | } |
970 | 1254 | ||
971 | static int test_term(struct test__term *t) | 1255 | static int test_term(struct terms_test *t) |
972 | { | 1256 | { |
973 | struct list_head *terms; | 1257 | struct list_head *terms; |
974 | int ret; | 1258 | int ret; |
@@ -992,13 +1276,13 @@ static int test_term(struct test__term *t) | |||
992 | return ret; | 1276 | return ret; |
993 | } | 1277 | } |
994 | 1278 | ||
995 | static int test_terms(struct test__term *terms, unsigned cnt) | 1279 | static int test_terms(struct terms_test *terms, unsigned cnt) |
996 | { | 1280 | { |
997 | int ret = 0; | 1281 | int ret = 0; |
998 | unsigned i; | 1282 | unsigned i; |
999 | 1283 | ||
1000 | for (i = 0; i < cnt; i++) { | 1284 | for (i = 0; i < cnt; i++) { |
1001 | struct test__term *t = &terms[i]; | 1285 | struct terms_test *t = &terms[i]; |
1002 | 1286 | ||
1003 | pr_debug("running test %d '%s'\n", i, t->str); | 1287 | pr_debug("running test %d '%s'\n", i, t->str); |
1004 | ret = test_term(t); | 1288 | ret = test_term(t); |
@@ -1024,7 +1308,52 @@ static int test_pmu(void) | |||
1024 | return !ret; | 1308 | return !ret; |
1025 | } | 1309 | } |
1026 | 1310 | ||
1027 | int parse_events__test(void) | 1311 | static int test_pmu_events(void) |
1312 | { | ||
1313 | struct stat st; | ||
1314 | char path[PATH_MAX]; | ||
1315 | struct dirent *ent; | ||
1316 | DIR *dir; | ||
1317 | int ret; | ||
1318 | |||
1319 | snprintf(path, PATH_MAX, "%s/bus/event_source/devices/cpu/events/", | ||
1320 | sysfs_find_mountpoint()); | ||
1321 | |||
1322 | ret = stat(path, &st); | ||
1323 | if (ret) { | ||
1324 | pr_debug("ommiting PMU cpu events tests\n"); | ||
1325 | return 0; | ||
1326 | } | ||
1327 | |||
1328 | dir = opendir(path); | ||
1329 | if (!dir) { | ||
1330 | pr_debug("can't open pmu event dir"); | ||
1331 | return -1; | ||
1332 | } | ||
1333 | |||
1334 | while (!ret && (ent = readdir(dir))) { | ||
1335 | #define MAX_NAME 100 | ||
1336 | struct evlist_test e; | ||
1337 | char name[MAX_NAME]; | ||
1338 | |||
1339 | if (!strcmp(ent->d_name, ".") || | ||
1340 | !strcmp(ent->d_name, "..")) | ||
1341 | continue; | ||
1342 | |||
1343 | snprintf(name, MAX_NAME, "cpu/event=%s/u", ent->d_name); | ||
1344 | |||
1345 | e.name = name; | ||
1346 | e.check = test__checkevent_pmu_events; | ||
1347 | |||
1348 | ret = test_event(&e); | ||
1349 | #undef MAX_NAME | ||
1350 | } | ||
1351 | |||
1352 | closedir(dir); | ||
1353 | return ret; | ||
1354 | } | ||
1355 | |||
1356 | int test__parse_events(void) | ||
1028 | { | 1357 | { |
1029 | int ret1, ret2 = 0; | 1358 | int ret1, ret2 = 0; |
1030 | 1359 | ||
@@ -1040,6 +1369,12 @@ do { \ | |||
1040 | if (test_pmu()) | 1369 | if (test_pmu()) |
1041 | TEST_EVENTS(test__events_pmu); | 1370 | TEST_EVENTS(test__events_pmu); |
1042 | 1371 | ||
1372 | if (test_pmu()) { | ||
1373 | int ret = test_pmu_events(); | ||
1374 | if (ret) | ||
1375 | return ret; | ||
1376 | } | ||
1377 | |||
1043 | ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms)); | 1378 | ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms)); |
1044 | if (!ret2) | 1379 | if (!ret2) |
1045 | ret2 = ret1; | 1380 | ret2 = ret1; |
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c new file mode 100644 index 000000000000..1e8e5128d0da --- /dev/null +++ b/tools/perf/tests/perf-record.c | |||
@@ -0,0 +1,314 @@ | |||
1 | #include <sched.h> | ||
2 | #include "evlist.h" | ||
3 | #include "evsel.h" | ||
4 | #include "perf.h" | ||
5 | #include "debug.h" | ||
6 | #include "tests.h" | ||
7 | |||
8 | static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t *maskp) | ||
9 | { | ||
10 | int i, cpu = -1, nrcpus = 1024; | ||
11 | realloc: | ||
12 | CPU_ZERO(maskp); | ||
13 | |||
14 | if (sched_getaffinity(pid, sizeof(*maskp), maskp) == -1) { | ||
15 | if (errno == EINVAL && nrcpus < (1024 << 8)) { | ||
16 | nrcpus = nrcpus << 2; | ||
17 | goto realloc; | ||
18 | } | ||
19 | perror("sched_getaffinity"); | ||
20 | return -1; | ||
21 | } | ||
22 | |||
23 | for (i = 0; i < nrcpus; i++) { | ||
24 | if (CPU_ISSET(i, maskp)) { | ||
25 | if (cpu == -1) | ||
26 | cpu = i; | ||
27 | else | ||
28 | CPU_CLR(i, maskp); | ||
29 | } | ||
30 | } | ||
31 | |||
32 | return cpu; | ||
33 | } | ||
34 | |||
35 | int test__PERF_RECORD(void) | ||
36 | { | ||
37 | struct perf_record_opts opts = { | ||
38 | .target = { | ||
39 | .uid = UINT_MAX, | ||
40 | .uses_mmap = true, | ||
41 | }, | ||
42 | .no_delay = true, | ||
43 | .freq = 10, | ||
44 | .mmap_pages = 256, | ||
45 | }; | ||
46 | cpu_set_t cpu_mask; | ||
47 | size_t cpu_mask_size = sizeof(cpu_mask); | ||
48 | struct perf_evlist *evlist = perf_evlist__new(NULL, NULL); | ||
49 | struct perf_evsel *evsel; | ||
50 | struct perf_sample sample; | ||
51 | const char *cmd = "sleep"; | ||
52 | const char *argv[] = { cmd, "1", NULL, }; | ||
53 | char *bname; | ||
54 | u64 prev_time = 0; | ||
55 | bool found_cmd_mmap = false, | ||
56 | found_libc_mmap = false, | ||
57 | found_vdso_mmap = false, | ||
58 | found_ld_mmap = false; | ||
59 | int err = -1, errs = 0, i, wakeups = 0; | ||
60 | u32 cpu; | ||
61 | int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, }; | ||
62 | |||
63 | if (evlist == NULL || argv == NULL) { | ||
64 | pr_debug("Not enough memory to create evlist\n"); | ||
65 | goto out; | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * We need at least one evsel in the evlist, use the default | ||
70 | * one: "cycles". | ||
71 | */ | ||
72 | err = perf_evlist__add_default(evlist); | ||
73 | if (err < 0) { | ||
74 | pr_debug("Not enough memory to create evsel\n"); | ||
75 | goto out_delete_evlist; | ||
76 | } | ||
77 | |||
78 | /* | ||
79 | * Create maps of threads and cpus to monitor. In this case | ||
80 | * we start with all threads and cpus (-1, -1) but then in | ||
81 | * perf_evlist__prepare_workload we'll fill in the only thread | ||
82 | * we're monitoring, the one forked there. | ||
83 | */ | ||
84 | err = perf_evlist__create_maps(evlist, &opts.target); | ||
85 | if (err < 0) { | ||
86 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
87 | goto out_delete_evlist; | ||
88 | } | ||
89 | |||
90 | /* | ||
91 | * Prepare the workload in argv[] to run, it'll fork it, and then wait | ||
92 | * for perf_evlist__start_workload() to exec it. This is done this way | ||
93 | * so that we have time to open the evlist (calling sys_perf_event_open | ||
94 | * on all the fds) and then mmap them. | ||
95 | */ | ||
96 | err = perf_evlist__prepare_workload(evlist, &opts, argv); | ||
97 | if (err < 0) { | ||
98 | pr_debug("Couldn't run the workload!\n"); | ||
99 | goto out_delete_maps; | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * Config the evsels, setting attr->comm on the first one, etc. | ||
104 | */ | ||
105 | evsel = perf_evlist__first(evlist); | ||
106 | perf_evsel__set_sample_bit(evsel, CPU); | ||
107 | perf_evsel__set_sample_bit(evsel, TID); | ||
108 | perf_evsel__set_sample_bit(evsel, TIME); | ||
109 | perf_evlist__config(evlist, &opts); | ||
110 | |||
111 | err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask); | ||
112 | if (err < 0) { | ||
113 | pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno)); | ||
114 | goto out_delete_maps; | ||
115 | } | ||
116 | |||
117 | cpu = err; | ||
118 | |||
119 | /* | ||
120 | * So that we can check perf_sample.cpu on all the samples. | ||
121 | */ | ||
122 | if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, &cpu_mask) < 0) { | ||
123 | pr_debug("sched_setaffinity: %s\n", strerror(errno)); | ||
124 | goto out_delete_maps; | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Call sys_perf_event_open on all the fds on all the evsels, | ||
129 | * grouping them if asked to. | ||
130 | */ | ||
131 | err = perf_evlist__open(evlist); | ||
132 | if (err < 0) { | ||
133 | pr_debug("perf_evlist__open: %s\n", strerror(errno)); | ||
134 | goto out_delete_maps; | ||
135 | } | ||
136 | |||
137 | /* | ||
138 | * mmap the first fd on a given CPU and ask for events for the other | ||
139 | * fds in the same CPU to be injected in the same mmap ring buffer | ||
140 | * (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)). | ||
141 | */ | ||
142 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | ||
143 | if (err < 0) { | ||
144 | pr_debug("perf_evlist__mmap: %s\n", strerror(errno)); | ||
145 | goto out_delete_maps; | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * Now that all is properly set up, enable the events, they will | ||
150 | * count just on workload.pid, which will start... | ||
151 | */ | ||
152 | perf_evlist__enable(evlist); | ||
153 | |||
154 | /* | ||
155 | * Now! | ||
156 | */ | ||
157 | perf_evlist__start_workload(evlist); | ||
158 | |||
159 | while (1) { | ||
160 | int before = total_events; | ||
161 | |||
162 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
163 | union perf_event *event; | ||
164 | |||
165 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
166 | const u32 type = event->header.type; | ||
167 | const char *name = perf_event__name(type); | ||
168 | |||
169 | ++total_events; | ||
170 | if (type < PERF_RECORD_MAX) | ||
171 | nr_events[type]++; | ||
172 | |||
173 | err = perf_evlist__parse_sample(evlist, event, &sample); | ||
174 | if (err < 0) { | ||
175 | if (verbose) | ||
176 | perf_event__fprintf(event, stderr); | ||
177 | pr_debug("Couldn't parse sample\n"); | ||
178 | goto out_err; | ||
179 | } | ||
180 | |||
181 | if (verbose) { | ||
182 | pr_info("%" PRIu64" %d ", sample.time, sample.cpu); | ||
183 | perf_event__fprintf(event, stderr); | ||
184 | } | ||
185 | |||
186 | if (prev_time > sample.time) { | ||
187 | pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n", | ||
188 | name, prev_time, sample.time); | ||
189 | ++errs; | ||
190 | } | ||
191 | |||
192 | prev_time = sample.time; | ||
193 | |||
194 | if (sample.cpu != cpu) { | ||
195 | pr_debug("%s with unexpected cpu, expected %d, got %d\n", | ||
196 | name, cpu, sample.cpu); | ||
197 | ++errs; | ||
198 | } | ||
199 | |||
200 | if ((pid_t)sample.pid != evlist->workload.pid) { | ||
201 | pr_debug("%s with unexpected pid, expected %d, got %d\n", | ||
202 | name, evlist->workload.pid, sample.pid); | ||
203 | ++errs; | ||
204 | } | ||
205 | |||
206 | if ((pid_t)sample.tid != evlist->workload.pid) { | ||
207 | pr_debug("%s with unexpected tid, expected %d, got %d\n", | ||
208 | name, evlist->workload.pid, sample.tid); | ||
209 | ++errs; | ||
210 | } | ||
211 | |||
212 | if ((type == PERF_RECORD_COMM || | ||
213 | type == PERF_RECORD_MMAP || | ||
214 | type == PERF_RECORD_FORK || | ||
215 | type == PERF_RECORD_EXIT) && | ||
216 | (pid_t)event->comm.pid != evlist->workload.pid) { | ||
217 | pr_debug("%s with unexpected pid/tid\n", name); | ||
218 | ++errs; | ||
219 | } | ||
220 | |||
221 | if ((type == PERF_RECORD_COMM || | ||
222 | type == PERF_RECORD_MMAP) && | ||
223 | event->comm.pid != event->comm.tid) { | ||
224 | pr_debug("%s with different pid/tid!\n", name); | ||
225 | ++errs; | ||
226 | } | ||
227 | |||
228 | switch (type) { | ||
229 | case PERF_RECORD_COMM: | ||
230 | if (strcmp(event->comm.comm, cmd)) { | ||
231 | pr_debug("%s with unexpected comm!\n", name); | ||
232 | ++errs; | ||
233 | } | ||
234 | break; | ||
235 | case PERF_RECORD_EXIT: | ||
236 | goto found_exit; | ||
237 | case PERF_RECORD_MMAP: | ||
238 | bname = strrchr(event->mmap.filename, '/'); | ||
239 | if (bname != NULL) { | ||
240 | if (!found_cmd_mmap) | ||
241 | found_cmd_mmap = !strcmp(bname + 1, cmd); | ||
242 | if (!found_libc_mmap) | ||
243 | found_libc_mmap = !strncmp(bname + 1, "libc", 4); | ||
244 | if (!found_ld_mmap) | ||
245 | found_ld_mmap = !strncmp(bname + 1, "ld", 2); | ||
246 | } else if (!found_vdso_mmap) | ||
247 | found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]"); | ||
248 | break; | ||
249 | |||
250 | case PERF_RECORD_SAMPLE: | ||
251 | /* Just ignore samples for now */ | ||
252 | break; | ||
253 | default: | ||
254 | pr_debug("Unexpected perf_event->header.type %d!\n", | ||
255 | type); | ||
256 | ++errs; | ||
257 | } | ||
258 | } | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * We don't use poll here because at least at 3.1 times the | ||
263 | * PERF_RECORD_{!SAMPLE} events don't honour | ||
264 | * perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does. | ||
265 | */ | ||
266 | if (total_events == before && false) | ||
267 | poll(evlist->pollfd, evlist->nr_fds, -1); | ||
268 | |||
269 | sleep(1); | ||
270 | if (++wakeups > 5) { | ||
271 | pr_debug("No PERF_RECORD_EXIT event!\n"); | ||
272 | break; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | found_exit: | ||
277 | if (nr_events[PERF_RECORD_COMM] > 1) { | ||
278 | pr_debug("Excessive number of PERF_RECORD_COMM events!\n"); | ||
279 | ++errs; | ||
280 | } | ||
281 | |||
282 | if (nr_events[PERF_RECORD_COMM] == 0) { | ||
283 | pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd); | ||
284 | ++errs; | ||
285 | } | ||
286 | |||
287 | if (!found_cmd_mmap) { | ||
288 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd); | ||
289 | ++errs; | ||
290 | } | ||
291 | |||
292 | if (!found_libc_mmap) { | ||
293 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc"); | ||
294 | ++errs; | ||
295 | } | ||
296 | |||
297 | if (!found_ld_mmap) { | ||
298 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld"); | ||
299 | ++errs; | ||
300 | } | ||
301 | |||
302 | if (!found_vdso_mmap) { | ||
303 | pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]"); | ||
304 | ++errs; | ||
305 | } | ||
306 | out_err: | ||
307 | perf_evlist__munmap(evlist); | ||
308 | out_delete_maps: | ||
309 | perf_evlist__delete_maps(evlist); | ||
310 | out_delete_evlist: | ||
311 | perf_evlist__delete(evlist); | ||
312 | out: | ||
313 | return (err < 0 || errs > 0) ? -1 : 0; | ||
314 | } | ||
diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c new file mode 100644 index 000000000000..12b322fa3475 --- /dev/null +++ b/tools/perf/tests/pmu.c | |||
@@ -0,0 +1,173 @@ | |||
1 | #include "parse-events.h" | ||
2 | #include "pmu.h" | ||
3 | #include "util.h" | ||
4 | #include "tests.h" | ||
5 | |||
6 | /* Simulated format definitions. */ | ||
7 | static struct test_format { | ||
8 | const char *name; | ||
9 | const char *value; | ||
10 | } test_formats[] = { | ||
11 | { "krava01", "config:0-1,62-63\n", }, | ||
12 | { "krava02", "config:10-17\n", }, | ||
13 | { "krava03", "config:5\n", }, | ||
14 | { "krava11", "config1:0,2,4,6,8,20-28\n", }, | ||
15 | { "krava12", "config1:63\n", }, | ||
16 | { "krava13", "config1:45-47\n", }, | ||
17 | { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", }, | ||
18 | { "krava22", "config2:8,18,48,58\n", }, | ||
19 | { "krava23", "config2:28-29,38\n", }, | ||
20 | }; | ||
21 | |||
22 | /* Simulated users input. */ | ||
23 | static struct parse_events_term test_terms[] = { | ||
24 | { | ||
25 | .config = (char *) "krava01", | ||
26 | .val.num = 15, | ||
27 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
28 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
29 | }, | ||
30 | { | ||
31 | .config = (char *) "krava02", | ||
32 | .val.num = 170, | ||
33 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
34 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
35 | }, | ||
36 | { | ||
37 | .config = (char *) "krava03", | ||
38 | .val.num = 1, | ||
39 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
40 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
41 | }, | ||
42 | { | ||
43 | .config = (char *) "krava11", | ||
44 | .val.num = 27, | ||
45 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
46 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
47 | }, | ||
48 | { | ||
49 | .config = (char *) "krava12", | ||
50 | .val.num = 1, | ||
51 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
52 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
53 | }, | ||
54 | { | ||
55 | .config = (char *) "krava13", | ||
56 | .val.num = 2, | ||
57 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
58 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
59 | }, | ||
60 | { | ||
61 | .config = (char *) "krava21", | ||
62 | .val.num = 119, | ||
63 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
64 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
65 | }, | ||
66 | { | ||
67 | .config = (char *) "krava22", | ||
68 | .val.num = 11, | ||
69 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
70 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
71 | }, | ||
72 | { | ||
73 | .config = (char *) "krava23", | ||
74 | .val.num = 2, | ||
75 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
76 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
77 | }, | ||
78 | }; | ||
79 | |||
80 | /* | ||
81 | * Prepare format directory data, exported by kernel | ||
82 | * at /sys/bus/event_source/devices/<dev>/format. | ||
83 | */ | ||
84 | static char *test_format_dir_get(void) | ||
85 | { | ||
86 | static char dir[PATH_MAX]; | ||
87 | unsigned int i; | ||
88 | |||
89 | snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX"); | ||
90 | if (!mkdtemp(dir)) | ||
91 | return NULL; | ||
92 | |||
93 | for (i = 0; i < ARRAY_SIZE(test_formats); i++) { | ||
94 | static char name[PATH_MAX]; | ||
95 | struct test_format *format = &test_formats[i]; | ||
96 | FILE *file; | ||
97 | |||
98 | snprintf(name, PATH_MAX, "%s/%s", dir, format->name); | ||
99 | |||
100 | file = fopen(name, "w"); | ||
101 | if (!file) | ||
102 | return NULL; | ||
103 | |||
104 | if (1 != fwrite(format->value, strlen(format->value), 1, file)) | ||
105 | break; | ||
106 | |||
107 | fclose(file); | ||
108 | } | ||
109 | |||
110 | return dir; | ||
111 | } | ||
112 | |||
113 | /* Cleanup format directory. */ | ||
114 | static int test_format_dir_put(char *dir) | ||
115 | { | ||
116 | char buf[PATH_MAX]; | ||
117 | snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir); | ||
118 | if (system(buf)) | ||
119 | return -1; | ||
120 | |||
121 | snprintf(buf, PATH_MAX, "rmdir %s\n", dir); | ||
122 | return system(buf); | ||
123 | } | ||
124 | |||
125 | static struct list_head *test_terms_list(void) | ||
126 | { | ||
127 | static LIST_HEAD(terms); | ||
128 | unsigned int i; | ||
129 | |||
130 | for (i = 0; i < ARRAY_SIZE(test_terms); i++) | ||
131 | list_add_tail(&test_terms[i].list, &terms); | ||
132 | |||
133 | return &terms; | ||
134 | } | ||
135 | |||
136 | int test__pmu(void) | ||
137 | { | ||
138 | char *format = test_format_dir_get(); | ||
139 | LIST_HEAD(formats); | ||
140 | struct list_head *terms = test_terms_list(); | ||
141 | int ret; | ||
142 | |||
143 | if (!format) | ||
144 | return -EINVAL; | ||
145 | |||
146 | do { | ||
147 | struct perf_event_attr attr; | ||
148 | |||
149 | memset(&attr, 0, sizeof(attr)); | ||
150 | |||
151 | ret = perf_pmu__format_parse(format, &formats); | ||
152 | if (ret) | ||
153 | break; | ||
154 | |||
155 | ret = perf_pmu__config_terms(&formats, &attr, terms); | ||
156 | if (ret) | ||
157 | break; | ||
158 | |||
159 | ret = -EINVAL; | ||
160 | |||
161 | if (attr.config != 0xc00000000002a823) | ||
162 | break; | ||
163 | if (attr.config1 != 0x8000400000000145) | ||
164 | break; | ||
165 | if (attr.config2 != 0x0400000020041d07) | ||
166 | break; | ||
167 | |||
168 | ret = 0; | ||
169 | } while (0); | ||
170 | |||
171 | test_format_dir_put(format); | ||
172 | return ret; | ||
173 | } | ||
diff --git a/tools/perf/tests/python-use.c b/tools/perf/tests/python-use.c new file mode 100644 index 000000000000..7760277c6def --- /dev/null +++ b/tools/perf/tests/python-use.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * Just test if we can load the python binding. | ||
3 | */ | ||
4 | |||
5 | #include <stdio.h> | ||
6 | #include <stdlib.h> | ||
7 | #include "tests.h" | ||
8 | |||
9 | extern int verbose; | ||
10 | |||
11 | int test__python_use(void) | ||
12 | { | ||
13 | char *cmd; | ||
14 | int ret; | ||
15 | |||
16 | if (asprintf(&cmd, "echo \"import sys ; sys.path.append('%s'); import perf\" | %s %s", | ||
17 | PYTHONPATH, PYTHON, verbose ? "" : "2> /dev/null") < 0) | ||
18 | return -1; | ||
19 | |||
20 | ret = system(cmd) ? -1 : 0; | ||
21 | free(cmd); | ||
22 | return ret; | ||
23 | } | ||
diff --git a/tools/perf/tests/rdpmc.c b/tools/perf/tests/rdpmc.c new file mode 100644 index 000000000000..ff94886aad99 --- /dev/null +++ b/tools/perf/tests/rdpmc.c | |||
@@ -0,0 +1,175 @@ | |||
1 | #include <unistd.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <signal.h> | ||
4 | #include <sys/mman.h> | ||
5 | #include "types.h" | ||
6 | #include "perf.h" | ||
7 | #include "debug.h" | ||
8 | #include "tests.h" | ||
9 | |||
10 | #if defined(__x86_64__) || defined(__i386__) | ||
11 | |||
12 | #define barrier() asm volatile("" ::: "memory") | ||
13 | |||
14 | static u64 rdpmc(unsigned int counter) | ||
15 | { | ||
16 | unsigned int low, high; | ||
17 | |||
18 | asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); | ||
19 | |||
20 | return low | ((u64)high) << 32; | ||
21 | } | ||
22 | |||
23 | static u64 rdtsc(void) | ||
24 | { | ||
25 | unsigned int low, high; | ||
26 | |||
27 | asm volatile("rdtsc" : "=a" (low), "=d" (high)); | ||
28 | |||
29 | return low | ((u64)high) << 32; | ||
30 | } | ||
31 | |||
32 | static u64 mmap_read_self(void *addr) | ||
33 | { | ||
34 | struct perf_event_mmap_page *pc = addr; | ||
35 | u32 seq, idx, time_mult = 0, time_shift = 0; | ||
36 | u64 count, cyc = 0, time_offset = 0, enabled, running, delta; | ||
37 | |||
38 | do { | ||
39 | seq = pc->lock; | ||
40 | barrier(); | ||
41 | |||
42 | enabled = pc->time_enabled; | ||
43 | running = pc->time_running; | ||
44 | |||
45 | if (enabled != running) { | ||
46 | cyc = rdtsc(); | ||
47 | time_mult = pc->time_mult; | ||
48 | time_shift = pc->time_shift; | ||
49 | time_offset = pc->time_offset; | ||
50 | } | ||
51 | |||
52 | idx = pc->index; | ||
53 | count = pc->offset; | ||
54 | if (idx) | ||
55 | count += rdpmc(idx - 1); | ||
56 | |||
57 | barrier(); | ||
58 | } while (pc->lock != seq); | ||
59 | |||
60 | if (enabled != running) { | ||
61 | u64 quot, rem; | ||
62 | |||
63 | quot = (cyc >> time_shift); | ||
64 | rem = cyc & ((1 << time_shift) - 1); | ||
65 | delta = time_offset + quot * time_mult + | ||
66 | ((rem * time_mult) >> time_shift); | ||
67 | |||
68 | enabled += delta; | ||
69 | if (idx) | ||
70 | running += delta; | ||
71 | |||
72 | quot = count / running; | ||
73 | rem = count % running; | ||
74 | count = quot * enabled + (rem * enabled) / running; | ||
75 | } | ||
76 | |||
77 | return count; | ||
78 | } | ||
79 | |||
80 | /* | ||
81 | * If the RDPMC instruction faults then signal this back to the test parent task: | ||
82 | */ | ||
83 | static void segfault_handler(int sig __maybe_unused, | ||
84 | siginfo_t *info __maybe_unused, | ||
85 | void *uc __maybe_unused) | ||
86 | { | ||
87 | exit(-1); | ||
88 | } | ||
89 | |||
90 | static int __test__rdpmc(void) | ||
91 | { | ||
92 | volatile int tmp = 0; | ||
93 | u64 i, loops = 1000; | ||
94 | int n; | ||
95 | int fd; | ||
96 | void *addr; | ||
97 | struct perf_event_attr attr = { | ||
98 | .type = PERF_TYPE_HARDWARE, | ||
99 | .config = PERF_COUNT_HW_INSTRUCTIONS, | ||
100 | .exclude_kernel = 1, | ||
101 | }; | ||
102 | u64 delta_sum = 0; | ||
103 | struct sigaction sa; | ||
104 | |||
105 | sigfillset(&sa.sa_mask); | ||
106 | sa.sa_sigaction = segfault_handler; | ||
107 | sigaction(SIGSEGV, &sa, NULL); | ||
108 | |||
109 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | ||
110 | if (fd < 0) { | ||
111 | pr_err("Error: sys_perf_event_open() syscall returned " | ||
112 | "with %d (%s)\n", fd, strerror(errno)); | ||
113 | return -1; | ||
114 | } | ||
115 | |||
116 | addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); | ||
117 | if (addr == (void *)(-1)) { | ||
118 | pr_err("Error: mmap() syscall returned with (%s)\n", | ||
119 | strerror(errno)); | ||
120 | goto out_close; | ||
121 | } | ||
122 | |||
123 | for (n = 0; n < 6; n++) { | ||
124 | u64 stamp, now, delta; | ||
125 | |||
126 | stamp = mmap_read_self(addr); | ||
127 | |||
128 | for (i = 0; i < loops; i++) | ||
129 | tmp++; | ||
130 | |||
131 | now = mmap_read_self(addr); | ||
132 | loops *= 10; | ||
133 | |||
134 | delta = now - stamp; | ||
135 | pr_debug("%14d: %14Lu\n", n, (long long)delta); | ||
136 | |||
137 | delta_sum += delta; | ||
138 | } | ||
139 | |||
140 | munmap(addr, page_size); | ||
141 | pr_debug(" "); | ||
142 | out_close: | ||
143 | close(fd); | ||
144 | |||
145 | if (!delta_sum) | ||
146 | return -1; | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | int test__rdpmc(void) | ||
152 | { | ||
153 | int status = 0; | ||
154 | int wret = 0; | ||
155 | int ret; | ||
156 | int pid; | ||
157 | |||
158 | pid = fork(); | ||
159 | if (pid < 0) | ||
160 | return -1; | ||
161 | |||
162 | if (!pid) { | ||
163 | ret = __test__rdpmc(); | ||
164 | |||
165 | exit(ret); | ||
166 | } | ||
167 | |||
168 | wret = waitpid(pid, &status, 0); | ||
169 | if (wret < 0 || status) | ||
170 | return -1; | ||
171 | |||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | #endif | ||
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h new file mode 100644 index 000000000000..5de0be1ff4b6 --- /dev/null +++ b/tools/perf/tests/tests.h | |||
@@ -0,0 +1,27 @@ | |||
1 | #ifndef TESTS_H | ||
2 | #define TESTS_H | ||
3 | |||
4 | enum { | ||
5 | TEST_OK = 0, | ||
6 | TEST_FAIL = -1, | ||
7 | TEST_SKIP = -2, | ||
8 | }; | ||
9 | |||
10 | /* Tests */ | ||
11 | int test__vmlinux_matches_kallsyms(void); | ||
12 | int test__open_syscall_event(void); | ||
13 | int test__open_syscall_event_on_all_cpus(void); | ||
14 | int test__basic_mmap(void); | ||
15 | int test__PERF_RECORD(void); | ||
16 | int test__rdpmc(void); | ||
17 | int test__perf_evsel__roundtrip_name_test(void); | ||
18 | int test__perf_evsel__tp_sched_test(void); | ||
19 | int test__syscall_open_tp_fields(void); | ||
20 | int test__pmu(void); | ||
21 | int test__attr(void); | ||
22 | int test__dso_data(void); | ||
23 | int test__parse_events(void); | ||
24 | int test__hists_link(void); | ||
25 | int test__python_use(void); | ||
26 | |||
27 | #endif /* TESTS_H */ | ||
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c new file mode 100644 index 000000000000..7b4c4d26d1ba --- /dev/null +++ b/tools/perf/tests/vmlinux-kallsyms.c | |||
@@ -0,0 +1,233 @@ | |||
1 | #include <linux/compiler.h> | ||
2 | #include <linux/rbtree.h> | ||
3 | #include <string.h> | ||
4 | #include "map.h" | ||
5 | #include "symbol.h" | ||
6 | #include "util.h" | ||
7 | #include "tests.h" | ||
8 | #include "debug.h" | ||
9 | #include "machine.h" | ||
10 | |||
11 | static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused, | ||
12 | struct symbol *sym) | ||
13 | { | ||
14 | bool *visited = symbol__priv(sym); | ||
15 | *visited = true; | ||
16 | return 0; | ||
17 | } | ||
18 | |||
19 | int test__vmlinux_matches_kallsyms(void) | ||
20 | { | ||
21 | int err = -1; | ||
22 | struct rb_node *nd; | ||
23 | struct symbol *sym; | ||
24 | struct map *kallsyms_map, *vmlinux_map; | ||
25 | struct machine kallsyms, vmlinux; | ||
26 | enum map_type type = MAP__FUNCTION; | ||
27 | struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; | ||
28 | |||
29 | /* | ||
30 | * Step 1: | ||
31 | * | ||
32 | * Init the machines that will hold kernel, modules obtained from | ||
33 | * both vmlinux + .ko files and from /proc/kallsyms split by modules. | ||
34 | */ | ||
35 | machine__init(&kallsyms, "", HOST_KERNEL_ID); | ||
36 | machine__init(&vmlinux, "", HOST_KERNEL_ID); | ||
37 | |||
38 | /* | ||
39 | * Step 2: | ||
40 | * | ||
41 | * Create the kernel maps for kallsyms and the DSO where we will then | ||
42 | * load /proc/kallsyms. Also create the modules maps from /proc/modules | ||
43 | * and find the .ko files that match them in /lib/modules/`uname -r`/. | ||
44 | */ | ||
45 | if (machine__create_kernel_maps(&kallsyms) < 0) { | ||
46 | pr_debug("machine__create_kernel_maps "); | ||
47 | goto out; | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * Step 3: | ||
52 | * | ||
53 | * Load and split /proc/kallsyms into multiple maps, one per module. | ||
54 | */ | ||
55 | if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) { | ||
56 | pr_debug("dso__load_kallsyms "); | ||
57 | goto out; | ||
58 | } | ||
59 | |||
60 | /* | ||
61 | * Step 4: | ||
62 | * | ||
63 | * kallsyms will be internally on demand sorted by name so that we can | ||
64 | * find the reference relocation * symbol, i.e. the symbol we will use | ||
65 | * to see if the running kernel was relocated by checking if it has the | ||
66 | * same value in the vmlinux file we load. | ||
67 | */ | ||
68 | kallsyms_map = machine__kernel_map(&kallsyms, type); | ||
69 | |||
70 | sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL); | ||
71 | if (sym == NULL) { | ||
72 | pr_debug("dso__find_symbol_by_name "); | ||
73 | goto out; | ||
74 | } | ||
75 | |||
76 | ref_reloc_sym.addr = sym->start; | ||
77 | |||
78 | /* | ||
79 | * Step 5: | ||
80 | * | ||
81 | * Now repeat step 2, this time for the vmlinux file we'll auto-locate. | ||
82 | */ | ||
83 | if (machine__create_kernel_maps(&vmlinux) < 0) { | ||
84 | pr_debug("machine__create_kernel_maps "); | ||
85 | goto out; | ||
86 | } | ||
87 | |||
88 | vmlinux_map = machine__kernel_map(&vmlinux, type); | ||
89 | map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym; | ||
90 | |||
91 | /* | ||
92 | * Step 6: | ||
93 | * | ||
94 | * Locate a vmlinux file in the vmlinux path that has a buildid that | ||
95 | * matches the one of the running kernel. | ||
96 | * | ||
97 | * While doing that look if we find the ref reloc symbol, if we find it | ||
98 | * we'll have its ref_reloc_symbol.unrelocated_addr and then | ||
99 | * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines | ||
100 | * to fixup the symbols. | ||
101 | */ | ||
102 | if (machine__load_vmlinux_path(&vmlinux, type, | ||
103 | vmlinux_matches_kallsyms_filter) <= 0) { | ||
104 | pr_debug("Couldn't find a vmlinux that matches the kernel running on this machine, skipping test\n"); | ||
105 | err = TEST_SKIP; | ||
106 | goto out; | ||
107 | } | ||
108 | |||
109 | err = 0; | ||
110 | /* | ||
111 | * Step 7: | ||
112 | * | ||
113 | * Now look at the symbols in the vmlinux DSO and check if we find all of them | ||
114 | * in the kallsyms dso. For the ones that are in both, check its names and | ||
115 | * end addresses too. | ||
116 | */ | ||
117 | for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) { | ||
118 | struct symbol *pair, *first_pair; | ||
119 | bool backwards = true; | ||
120 | |||
121 | sym = rb_entry(nd, struct symbol, rb_node); | ||
122 | |||
123 | if (sym->start == sym->end) | ||
124 | continue; | ||
125 | |||
126 | first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL); | ||
127 | pair = first_pair; | ||
128 | |||
129 | if (pair && pair->start == sym->start) { | ||
130 | next_pair: | ||
131 | if (strcmp(sym->name, pair->name) == 0) { | ||
132 | /* | ||
133 | * kallsyms don't have the symbol end, so we | ||
134 | * set that by using the next symbol start - 1, | ||
135 | * in some cases we get this up to a page | ||
136 | * wrong, trace_kmalloc when I was developing | ||
137 | * this code was one such example, 2106 bytes | ||
138 | * off the real size. More than that and we | ||
139 | * _really_ have a problem. | ||
140 | */ | ||
141 | s64 skew = sym->end - pair->end; | ||
142 | if (llabs(skew) < page_size) | ||
143 | continue; | ||
144 | |||
145 | pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n", | ||
146 | sym->start, sym->name, sym->end, pair->end); | ||
147 | } else { | ||
148 | struct rb_node *nnd; | ||
149 | detour: | ||
150 | nnd = backwards ? rb_prev(&pair->rb_node) : | ||
151 | rb_next(&pair->rb_node); | ||
152 | if (nnd) { | ||
153 | struct symbol *next = rb_entry(nnd, struct symbol, rb_node); | ||
154 | |||
155 | if (next->start == sym->start) { | ||
156 | pair = next; | ||
157 | goto next_pair; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | if (backwards) { | ||
162 | backwards = false; | ||
163 | pair = first_pair; | ||
164 | goto detour; | ||
165 | } | ||
166 | |||
167 | pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", | ||
168 | sym->start, sym->name, pair->name); | ||
169 | } | ||
170 | } else | ||
171 | pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name); | ||
172 | |||
173 | err = -1; | ||
174 | } | ||
175 | |||
176 | if (!verbose) | ||
177 | goto out; | ||
178 | |||
179 | pr_info("Maps only in vmlinux:\n"); | ||
180 | |||
181 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | ||
182 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; | ||
183 | /* | ||
184 | * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while | ||
185 | * the kernel will have the path for the vmlinux file being used, | ||
186 | * so use the short name, less descriptive but the same ("[kernel]" in | ||
187 | * both cases. | ||
188 | */ | ||
189 | pair = map_groups__find_by_name(&kallsyms.kmaps, type, | ||
190 | (pos->dso->kernel ? | ||
191 | pos->dso->short_name : | ||
192 | pos->dso->name)); | ||
193 | if (pair) | ||
194 | pair->priv = 1; | ||
195 | else | ||
196 | map__fprintf(pos, stderr); | ||
197 | } | ||
198 | |||
199 | pr_info("Maps in vmlinux with a different name in kallsyms:\n"); | ||
200 | |||
201 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | ||
202 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; | ||
203 | |||
204 | pair = map_groups__find(&kallsyms.kmaps, type, pos->start); | ||
205 | if (pair == NULL || pair->priv) | ||
206 | continue; | ||
207 | |||
208 | if (pair->start == pos->start) { | ||
209 | pair->priv = 1; | ||
210 | pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", | ||
211 | pos->start, pos->end, pos->pgoff, pos->dso->name); | ||
212 | if (pos->pgoff != pair->pgoff || pos->end != pair->end) | ||
213 | pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "", | ||
214 | pair->start, pair->end, pair->pgoff); | ||
215 | pr_info(" %s\n", pair->dso->name); | ||
216 | pair->priv = 1; | ||
217 | } | ||
218 | } | ||
219 | |||
220 | pr_info("Maps only in kallsyms:\n"); | ||
221 | |||
222 | for (nd = rb_first(&kallsyms.kmaps.maps[type]); | ||
223 | nd; nd = rb_next(nd)) { | ||
224 | struct map *pos = rb_entry(nd, struct map, rb_node); | ||
225 | |||
226 | if (!pos->priv) | ||
227 | map__fprintf(pos, stderr); | ||
228 | } | ||
229 | out: | ||
230 | machine__exit(&kallsyms); | ||
231 | machine__exit(&vmlinux); | ||
232 | return err; | ||
233 | } | ||
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c index 4aeb7d5df939..809ea4632a34 100644 --- a/tools/perf/ui/browser.c +++ b/tools/perf/ui/browser.c | |||
@@ -273,6 +273,8 @@ void ui_browser__hide(struct ui_browser *browser __maybe_unused) | |||
273 | { | 273 | { |
274 | pthread_mutex_lock(&ui__lock); | 274 | pthread_mutex_lock(&ui__lock); |
275 | ui_helpline__pop(); | 275 | ui_helpline__pop(); |
276 | free(browser->helpline); | ||
277 | browser->helpline = NULL; | ||
276 | pthread_mutex_unlock(&ui__lock); | 278 | pthread_mutex_unlock(&ui__lock); |
277 | } | 279 | } |
278 | 280 | ||
@@ -471,7 +473,7 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *browser) | |||
471 | return row; | 473 | return row; |
472 | } | 474 | } |
473 | 475 | ||
474 | static struct ui_browser__colorset { | 476 | static struct ui_browser_colorset { |
475 | const char *name, *fg, *bg; | 477 | const char *name, *fg, *bg; |
476 | int colorset; | 478 | int colorset; |
477 | } ui_browser__colorsets[] = { | 479 | } ui_browser__colorsets[] = { |
@@ -706,7 +708,7 @@ void ui_browser__init(void) | |||
706 | perf_config(ui_browser__color_config, NULL); | 708 | perf_config(ui_browser__color_config, NULL); |
707 | 709 | ||
708 | while (ui_browser__colorsets[i].name) { | 710 | while (ui_browser__colorsets[i].name) { |
709 | struct ui_browser__colorset *c = &ui_browser__colorsets[i++]; | 711 | struct ui_browser_colorset *c = &ui_browser__colorsets[i++]; |
710 | sltt_set_color(c->colorset, c->name, c->fg, c->bg); | 712 | sltt_set_color(c->colorset, c->name, c->fg, c->bg); |
711 | } | 713 | } |
712 | 714 | ||
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index 8f8cd2d73b3b..7dca1555c610 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
@@ -182,15 +182,30 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
182 | ab->selection = dl; | 182 | ab->selection = dl; |
183 | } | 183 | } |
184 | 184 | ||
185 | static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym) | ||
186 | { | ||
187 | if (!dl || !dl->ins || !ins__is_jump(dl->ins) | ||
188 | || !disasm_line__has_offset(dl) | ||
189 | || dl->ops.target.offset >= symbol__size(sym)) | ||
190 | return false; | ||
191 | |||
192 | return true; | ||
193 | } | ||
194 | |||
185 | static void annotate_browser__draw_current_jump(struct ui_browser *browser) | 195 | static void annotate_browser__draw_current_jump(struct ui_browser *browser) |
186 | { | 196 | { |
187 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); | 197 | struct annotate_browser *ab = container_of(browser, struct annotate_browser, b); |
188 | struct disasm_line *cursor = ab->selection, *target; | 198 | struct disasm_line *cursor = ab->selection, *target; |
189 | struct browser_disasm_line *btarget, *bcursor; | 199 | struct browser_disasm_line *btarget, *bcursor; |
190 | unsigned int from, to; | 200 | unsigned int from, to; |
201 | struct map_symbol *ms = ab->b.priv; | ||
202 | struct symbol *sym = ms->sym; | ||
191 | 203 | ||
192 | if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) || | 204 | /* PLT symbols contain external offsets */ |
193 | !disasm_line__has_offset(cursor)) | 205 | if (strstr(sym->name, "@plt")) |
206 | return; | ||
207 | |||
208 | if (!disasm_line__is_valid_jump(cursor, sym)) | ||
194 | return; | 209 | return; |
195 | 210 | ||
196 | target = ab->offsets[cursor->ops.target.offset]; | 211 | target = ab->offsets[cursor->ops.target.offset]; |
@@ -386,9 +401,8 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser) | |||
386 | browser->b.nr_entries = browser->nr_asm_entries; | 401 | browser->b.nr_entries = browser->nr_asm_entries; |
387 | } | 402 | } |
388 | 403 | ||
389 | static bool annotate_browser__callq(struct annotate_browser *browser, | 404 | static bool annotate_browser__callq(struct annotate_browser *browser, int evidx, |
390 | int evidx, void (*timer)(void *arg), | 405 | struct hist_browser_timer *hbt) |
391 | void *arg, int delay_secs) | ||
392 | { | 406 | { |
393 | struct map_symbol *ms = browser->b.priv; | 407 | struct map_symbol *ms = browser->b.priv; |
394 | struct disasm_line *dl = browser->selection; | 408 | struct disasm_line *dl = browser->selection; |
@@ -418,7 +432,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, | |||
418 | } | 432 | } |
419 | 433 | ||
420 | pthread_mutex_unlock(¬es->lock); | 434 | pthread_mutex_unlock(¬es->lock); |
421 | symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs); | 435 | symbol__tui_annotate(target, ms->map, evidx, hbt); |
422 | ui_browser__show_title(&browser->b, sym->name); | 436 | ui_browser__show_title(&browser->b, sym->name); |
423 | return true; | 437 | return true; |
424 | } | 438 | } |
@@ -602,13 +616,13 @@ static void annotate_browser__update_addr_width(struct annotate_browser *browser | |||
602 | } | 616 | } |
603 | 617 | ||
604 | static int annotate_browser__run(struct annotate_browser *browser, int evidx, | 618 | static int annotate_browser__run(struct annotate_browser *browser, int evidx, |
605 | void(*timer)(void *arg), | 619 | struct hist_browser_timer *hbt) |
606 | void *arg, int delay_secs) | ||
607 | { | 620 | { |
608 | struct rb_node *nd = NULL; | 621 | struct rb_node *nd = NULL; |
609 | struct map_symbol *ms = browser->b.priv; | 622 | struct map_symbol *ms = browser->b.priv; |
610 | struct symbol *sym = ms->sym; | 623 | struct symbol *sym = ms->sym; |
611 | const char *help = "Press 'h' for help on key bindings"; | 624 | const char *help = "Press 'h' for help on key bindings"; |
625 | int delay_secs = hbt ? hbt->refresh : 0; | ||
612 | int key; | 626 | int key; |
613 | 627 | ||
614 | if (ui_browser__show(&browser->b, sym->name, help) < 0) | 628 | if (ui_browser__show(&browser->b, sym->name, help) < 0) |
@@ -639,8 +653,8 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, | |||
639 | 653 | ||
640 | switch (key) { | 654 | switch (key) { |
641 | case K_TIMER: | 655 | case K_TIMER: |
642 | if (timer != NULL) | 656 | if (hbt) |
643 | timer(arg); | 657 | hbt->timer(hbt->arg); |
644 | 658 | ||
645 | if (delay_secs != 0) | 659 | if (delay_secs != 0) |
646 | symbol__annotate_decay_histogram(sym, evidx); | 660 | symbol__annotate_decay_histogram(sym, evidx); |
@@ -676,8 +690,14 @@ static int annotate_browser__run(struct annotate_browser *browser, int evidx, | |||
676 | "o Toggle disassembler output/simplified view\n" | 690 | "o Toggle disassembler output/simplified view\n" |
677 | "s Toggle source code view\n" | 691 | "s Toggle source code view\n" |
678 | "/ Search string\n" | 692 | "/ Search string\n" |
693 | "r Run available scripts\n" | ||
679 | "? Search previous string\n"); | 694 | "? Search previous string\n"); |
680 | continue; | 695 | continue; |
696 | case 'r': | ||
697 | { | ||
698 | script_browse(NULL); | ||
699 | continue; | ||
700 | } | ||
681 | case 'H': | 701 | case 'H': |
682 | nd = browser->curr_hot; | 702 | nd = browser->curr_hot; |
683 | break; | 703 | break; |
@@ -734,7 +754,7 @@ show_help: | |||
734 | goto show_sup_ins; | 754 | goto show_sup_ins; |
735 | goto out; | 755 | goto out; |
736 | } else if (!(annotate_browser__jump(browser) || | 756 | } else if (!(annotate_browser__jump(browser) || |
737 | annotate_browser__callq(browser, evidx, timer, arg, delay_secs))) { | 757 | annotate_browser__callq(browser, evidx, hbt))) { |
738 | show_sup_ins: | 758 | show_sup_ins: |
739 | ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); | 759 | ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions."); |
740 | } | 760 | } |
@@ -757,32 +777,29 @@ out: | |||
757 | } | 777 | } |
758 | 778 | ||
759 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, | 779 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, |
760 | void(*timer)(void *arg), void *arg, int delay_secs) | 780 | struct hist_browser_timer *hbt) |
761 | { | 781 | { |
762 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, | 782 | return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, hbt); |
763 | timer, arg, delay_secs); | ||
764 | } | 783 | } |
765 | 784 | ||
766 | static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, | 785 | static void annotate_browser__mark_jump_targets(struct annotate_browser *browser, |
767 | size_t size) | 786 | size_t size) |
768 | { | 787 | { |
769 | u64 offset; | 788 | u64 offset; |
789 | struct map_symbol *ms = browser->b.priv; | ||
790 | struct symbol *sym = ms->sym; | ||
791 | |||
792 | /* PLT symbols contain external offsets */ | ||
793 | if (strstr(sym->name, "@plt")) | ||
794 | return; | ||
770 | 795 | ||
771 | for (offset = 0; offset < size; ++offset) { | 796 | for (offset = 0; offset < size; ++offset) { |
772 | struct disasm_line *dl = browser->offsets[offset], *dlt; | 797 | struct disasm_line *dl = browser->offsets[offset], *dlt; |
773 | struct browser_disasm_line *bdlt; | 798 | struct browser_disasm_line *bdlt; |
774 | 799 | ||
775 | if (!dl || !dl->ins || !ins__is_jump(dl->ins) || | 800 | if (!disasm_line__is_valid_jump(dl, sym)) |
776 | !disasm_line__has_offset(dl)) | ||
777 | continue; | 801 | continue; |
778 | 802 | ||
779 | if (dl->ops.target.offset >= size) { | ||
780 | ui__error("jump to after symbol!\n" | ||
781 | "size: %zx, jump target: %" PRIx64, | ||
782 | size, dl->ops.target.offset); | ||
783 | continue; | ||
784 | } | ||
785 | |||
786 | dlt = browser->offsets[dl->ops.target.offset]; | 803 | dlt = browser->offsets[dl->ops.target.offset]; |
787 | /* | 804 | /* |
788 | * FIXME: Oops, no jump target? Buggy disassembler? Or do we | 805 | * FIXME: Oops, no jump target? Buggy disassembler? Or do we |
@@ -810,8 +827,7 @@ static inline int width_jumps(int n) | |||
810 | } | 827 | } |
811 | 828 | ||
812 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 829 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
813 | void(*timer)(void *arg), void *arg, | 830 | struct hist_browser_timer *hbt) |
814 | int delay_secs) | ||
815 | { | 831 | { |
816 | struct disasm_line *pos, *n; | 832 | struct disasm_line *pos, *n; |
817 | struct annotation *notes; | 833 | struct annotation *notes; |
@@ -893,7 +909,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | |||
893 | 909 | ||
894 | annotate_browser__update_addr_width(&browser); | 910 | annotate_browser__update_addr_width(&browser); |
895 | 911 | ||
896 | ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs); | 912 | ret = annotate_browser__run(&browser, evidx, hbt); |
897 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | 913 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { |
898 | list_del(&pos->node); | 914 | list_del(&pos->node); |
899 | disasm_line__free(pos); | 915 | disasm_line__free(pos); |
@@ -906,11 +922,11 @@ out_free_offsets: | |||
906 | 922 | ||
907 | #define ANNOTATE_CFG(n) \ | 923 | #define ANNOTATE_CFG(n) \ |
908 | { .name = #n, .value = &annotate_browser__opts.n, } | 924 | { .name = #n, .value = &annotate_browser__opts.n, } |
909 | 925 | ||
910 | /* | 926 | /* |
911 | * Keep the entries sorted, they are bsearch'ed | 927 | * Keep the entries sorted, they are bsearch'ed |
912 | */ | 928 | */ |
913 | static struct annotate__config { | 929 | static struct annotate_config { |
914 | const char *name; | 930 | const char *name; |
915 | bool *value; | 931 | bool *value; |
916 | } annotate__configs[] = { | 932 | } annotate__configs[] = { |
@@ -924,7 +940,7 @@ static struct annotate__config { | |||
924 | 940 | ||
925 | static int annotate_config__cmp(const void *name, const void *cfgp) | 941 | static int annotate_config__cmp(const void *name, const void *cfgp) |
926 | { | 942 | { |
927 | const struct annotate__config *cfg = cfgp; | 943 | const struct annotate_config *cfg = cfgp; |
928 | 944 | ||
929 | return strcmp(name, cfg->name); | 945 | return strcmp(name, cfg->name); |
930 | } | 946 | } |
@@ -932,7 +948,7 @@ static int annotate_config__cmp(const void *name, const void *cfgp) | |||
932 | static int annotate__config(const char *var, const char *value, | 948 | static int annotate__config(const char *var, const char *value, |
933 | void *data __maybe_unused) | 949 | void *data __maybe_unused) |
934 | { | 950 | { |
935 | struct annotate__config *cfg; | 951 | struct annotate_config *cfg; |
936 | const char *name; | 952 | const char *name; |
937 | 953 | ||
938 | if (prefixcmp(var, "annotate.") != 0) | 954 | if (prefixcmp(var, "annotate.") != 0) |
@@ -940,7 +956,7 @@ static int annotate__config(const char *var, const char *value, | |||
940 | 956 | ||
941 | name = var + 9; | 957 | name = var + 9; |
942 | cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), | 958 | cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs), |
943 | sizeof(struct annotate__config), annotate_config__cmp); | 959 | sizeof(struct annotate_config), annotate_config__cmp); |
944 | 960 | ||
945 | if (cfg == NULL) | 961 | if (cfg == NULL) |
946 | return -1; | 962 | return -1; |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index ef2f93ca7496..aa22704047d6 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include "../../util/pstack.h" | 11 | #include "../../util/pstack.h" |
12 | #include "../../util/sort.h" | 12 | #include "../../util/sort.h" |
13 | #include "../../util/util.h" | 13 | #include "../../util/util.h" |
14 | #include "../../arch/common.h" | ||
14 | 15 | ||
15 | #include "../browser.h" | 16 | #include "../browser.h" |
16 | #include "../helpline.h" | 17 | #include "../helpline.h" |
@@ -310,10 +311,11 @@ static void ui_browser__warn_lost_events(struct ui_browser *browser) | |||
310 | } | 311 | } |
311 | 312 | ||
312 | static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | 313 | static int hist_browser__run(struct hist_browser *browser, const char *ev_name, |
313 | void(*timer)(void *arg), void *arg, int delay_secs) | 314 | struct hist_browser_timer *hbt) |
314 | { | 315 | { |
315 | int key; | 316 | int key; |
316 | char title[160]; | 317 | char title[160]; |
318 | int delay_secs = hbt ? hbt->refresh : 0; | ||
317 | 319 | ||
318 | browser->b.entries = &browser->hists->entries; | 320 | browser->b.entries = &browser->hists->entries; |
319 | browser->b.nr_entries = browser->hists->nr_entries; | 321 | browser->b.nr_entries = browser->hists->nr_entries; |
@@ -330,7 +332,7 @@ static int hist_browser__run(struct hist_browser *browser, const char *ev_name, | |||
330 | 332 | ||
331 | switch (key) { | 333 | switch (key) { |
332 | case K_TIMER: | 334 | case K_TIMER: |
333 | timer(arg); | 335 | hbt->timer(hbt->arg); |
334 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | 336 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); |
335 | 337 | ||
336 | if (browser->hists->stats.nr_lost_warned != | 338 | if (browser->hists->stats.nr_lost_warned != |
@@ -565,26 +567,128 @@ static int hist_browser__show_callchain(struct hist_browser *browser, | |||
565 | return row - first_row; | 567 | return row - first_row; |
566 | } | 568 | } |
567 | 569 | ||
568 | #define HPP__COLOR_FN(_name, _field) \ | 570 | struct hpp_arg { |
569 | static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \ | 571 | struct ui_browser *b; |
570 | struct hist_entry *he) \ | 572 | char folded_sign; |
573 | bool current_entry; | ||
574 | }; | ||
575 | |||
576 | static int __hpp__color_callchain(struct hpp_arg *arg) | ||
577 | { | ||
578 | if (!symbol_conf.use_callchain) | ||
579 | return 0; | ||
580 | |||
581 | slsmg_printf("%c ", arg->folded_sign); | ||
582 | return 2; | ||
583 | } | ||
584 | |||
585 | static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, | ||
586 | u64 (*get_field)(struct hist_entry *), | ||
587 | int (*callchain_cb)(struct hpp_arg *)) | ||
588 | { | ||
589 | int ret = 0; | ||
590 | double percent = 0.0; | ||
591 | struct hists *hists = he->hists; | ||
592 | struct hpp_arg *arg = hpp->ptr; | ||
593 | |||
594 | if (hists->stats.total_period) | ||
595 | percent = 100.0 * get_field(he) / hists->stats.total_period; | ||
596 | |||
597 | ui_browser__set_percent_color(arg->b, percent, arg->current_entry); | ||
598 | |||
599 | if (callchain_cb) | ||
600 | ret += callchain_cb(arg); | ||
601 | |||
602 | ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); | ||
603 | slsmg_printf("%s", hpp->buf); | ||
604 | |||
605 | if (symbol_conf.event_group) { | ||
606 | int prev_idx, idx_delta; | ||
607 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
608 | struct hist_entry *pair; | ||
609 | int nr_members = evsel->nr_members; | ||
610 | |||
611 | if (nr_members <= 1) | ||
612 | goto out; | ||
613 | |||
614 | prev_idx = perf_evsel__group_idx(evsel); | ||
615 | |||
616 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { | ||
617 | u64 period = get_field(pair); | ||
618 | u64 total = pair->hists->stats.total_period; | ||
619 | |||
620 | if (!total) | ||
621 | continue; | ||
622 | |||
623 | evsel = hists_to_evsel(pair->hists); | ||
624 | idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; | ||
625 | |||
626 | while (idx_delta--) { | ||
627 | /* | ||
628 | * zero-fill group members in the middle which | ||
629 | * have no sample | ||
630 | */ | ||
631 | ui_browser__set_percent_color(arg->b, 0.0, | ||
632 | arg->current_entry); | ||
633 | ret += scnprintf(hpp->buf, hpp->size, | ||
634 | " %6.2f%%", 0.0); | ||
635 | slsmg_printf("%s", hpp->buf); | ||
636 | } | ||
637 | |||
638 | percent = 100.0 * period / total; | ||
639 | ui_browser__set_percent_color(arg->b, percent, | ||
640 | arg->current_entry); | ||
641 | ret += scnprintf(hpp->buf, hpp->size, | ||
642 | " %6.2f%%", percent); | ||
643 | slsmg_printf("%s", hpp->buf); | ||
644 | |||
645 | prev_idx = perf_evsel__group_idx(evsel); | ||
646 | } | ||
647 | |||
648 | idx_delta = nr_members - prev_idx - 1; | ||
649 | |||
650 | while (idx_delta--) { | ||
651 | /* | ||
652 | * zero-fill group members at last which have no sample | ||
653 | */ | ||
654 | ui_browser__set_percent_color(arg->b, 0.0, | ||
655 | arg->current_entry); | ||
656 | ret += scnprintf(hpp->buf, hpp->size, | ||
657 | " %6.2f%%", 0.0); | ||
658 | slsmg_printf("%s", hpp->buf); | ||
659 | } | ||
660 | } | ||
661 | out: | ||
662 | if (!arg->current_entry || !arg->b->navkeypressed) | ||
663 | ui_browser__set_color(arg->b, HE_COLORSET_NORMAL); | ||
664 | |||
665 | return ret; | ||
666 | } | ||
667 | |||
668 | #define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \ | ||
669 | static u64 __hpp_get_##_field(struct hist_entry *he) \ | ||
571 | { \ | 670 | { \ |
572 | struct hists *hists = he->hists; \ | 671 | return he->stat._field; \ |
573 | double percent = 100.0 * he->stat._field / hists->stats.total_period; \ | 672 | } \ |
574 | *(double *)hpp->ptr = percent; \ | 673 | \ |
575 | return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \ | 674 | static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \ |
675 | struct hist_entry *he) \ | ||
676 | { \ | ||
677 | return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \ | ||
576 | } | 678 | } |
577 | 679 | ||
578 | HPP__COLOR_FN(overhead, period) | 680 | __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain) |
579 | HPP__COLOR_FN(overhead_sys, period_sys) | 681 | __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL) |
580 | HPP__COLOR_FN(overhead_us, period_us) | 682 | __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL) |
581 | HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) | 683 | __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL) |
582 | HPP__COLOR_FN(overhead_guest_us, period_guest_us) | 684 | __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL) |
583 | 685 | ||
584 | #undef HPP__COLOR_FN | 686 | #undef __HPP_COLOR_PERCENT_FN |
585 | 687 | ||
586 | void hist_browser__init_hpp(void) | 688 | void hist_browser__init_hpp(void) |
587 | { | 689 | { |
690 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
691 | |||
588 | perf_hpp__init(); | 692 | perf_hpp__init(); |
589 | 693 | ||
590 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | 694 | perf_hpp__format[PERF_HPP__OVERHEAD].color = |
@@ -604,13 +708,13 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
604 | unsigned short row) | 708 | unsigned short row) |
605 | { | 709 | { |
606 | char s[256]; | 710 | char s[256]; |
607 | double percent; | 711 | int printed = 0; |
608 | int i, printed = 0; | ||
609 | int width = browser->b.width; | 712 | int width = browser->b.width; |
610 | char folded_sign = ' '; | 713 | char folded_sign = ' '; |
611 | bool current_entry = ui_browser__is_current_entry(&browser->b, row); | 714 | bool current_entry = ui_browser__is_current_entry(&browser->b, row); |
612 | off_t row_offset = entry->row_offset; | 715 | off_t row_offset = entry->row_offset; |
613 | bool first = true; | 716 | bool first = true; |
717 | struct perf_hpp_fmt *fmt; | ||
614 | 718 | ||
615 | if (current_entry) { | 719 | if (current_entry) { |
616 | browser->he_selection = entry; | 720 | browser->he_selection = entry; |
@@ -623,41 +727,30 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
623 | } | 727 | } |
624 | 728 | ||
625 | if (row_offset == 0) { | 729 | if (row_offset == 0) { |
730 | struct hpp_arg arg = { | ||
731 | .b = &browser->b, | ||
732 | .folded_sign = folded_sign, | ||
733 | .current_entry = current_entry, | ||
734 | }; | ||
626 | struct perf_hpp hpp = { | 735 | struct perf_hpp hpp = { |
627 | .buf = s, | 736 | .buf = s, |
628 | .size = sizeof(s), | 737 | .size = sizeof(s), |
738 | .ptr = &arg, | ||
629 | }; | 739 | }; |
630 | 740 | ||
631 | ui_browser__gotorc(&browser->b, row, 0); | 741 | ui_browser__gotorc(&browser->b, row, 0); |
632 | 742 | ||
633 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | 743 | perf_hpp__for_each_format(fmt) { |
634 | if (!perf_hpp__format[i].cond) | ||
635 | continue; | ||
636 | |||
637 | if (!first) { | 744 | if (!first) { |
638 | slsmg_printf(" "); | 745 | slsmg_printf(" "); |
639 | width -= 2; | 746 | width -= 2; |
640 | } | 747 | } |
641 | first = false; | 748 | first = false; |
642 | 749 | ||
643 | if (perf_hpp__format[i].color) { | 750 | if (fmt->color) { |
644 | hpp.ptr = &percent; | 751 | width -= fmt->color(&hpp, entry); |
645 | /* It will set percent for us. See HPP__COLOR_FN above. */ | ||
646 | width -= perf_hpp__format[i].color(&hpp, entry); | ||
647 | |||
648 | ui_browser__set_percent_color(&browser->b, percent, current_entry); | ||
649 | |||
650 | if (i == PERF_HPP__OVERHEAD && symbol_conf.use_callchain) { | ||
651 | slsmg_printf("%c ", folded_sign); | ||
652 | width -= 2; | ||
653 | } | ||
654 | |||
655 | slsmg_printf("%s", s); | ||
656 | |||
657 | if (!current_entry || !browser->b.navkeypressed) | ||
658 | ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL); | ||
659 | } else { | 752 | } else { |
660 | width -= perf_hpp__format[i].entry(&hpp, entry); | 753 | width -= fmt->entry(&hpp, entry); |
661 | slsmg_printf("%s", s); | 754 | slsmg_printf("%s", s); |
662 | } | 755 | } |
663 | } | 756 | } |
@@ -1096,6 +1189,21 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, | |||
1096 | const struct thread *thread = hists->thread_filter; | 1189 | const struct thread *thread = hists->thread_filter; |
1097 | unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 1190 | unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
1098 | u64 nr_events = hists->stats.total_period; | 1191 | u64 nr_events = hists->stats.total_period; |
1192 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
1193 | char buf[512]; | ||
1194 | size_t buflen = sizeof(buf); | ||
1195 | |||
1196 | if (symbol_conf.event_group && evsel->nr_members > 1) { | ||
1197 | struct perf_evsel *pos; | ||
1198 | |||
1199 | perf_evsel__group_desc(evsel, buf, buflen); | ||
1200 | ev_name = buf; | ||
1201 | |||
1202 | for_each_group_member(pos, evsel) { | ||
1203 | nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1204 | nr_events += pos->hists.stats.total_period; | ||
1205 | } | ||
1206 | } | ||
1099 | 1207 | ||
1100 | nr_samples = convert_unit(nr_samples, &unit); | 1208 | nr_samples = convert_unit(nr_samples, &unit); |
1101 | printed = scnprintf(bf, size, | 1209 | printed = scnprintf(bf, size, |
@@ -1127,11 +1235,107 @@ static inline void free_popup_options(char **options, int n) | |||
1127 | } | 1235 | } |
1128 | } | 1236 | } |
1129 | 1237 | ||
1238 | /* Check whether the browser is for 'top' or 'report' */ | ||
1239 | static inline bool is_report_browser(void *timer) | ||
1240 | { | ||
1241 | return timer == NULL; | ||
1242 | } | ||
1243 | |||
1244 | /* | ||
1245 | * Only runtime switching of perf data file will make "input_name" point | ||
1246 | * to a malloced buffer. So add "is_input_name_malloced" flag to decide | ||
1247 | * whether we need to call free() for current "input_name" during the switch. | ||
1248 | */ | ||
1249 | static bool is_input_name_malloced = false; | ||
1250 | |||
1251 | static int switch_data_file(void) | ||
1252 | { | ||
1253 | char *pwd, *options[32], *abs_path[32], *tmp; | ||
1254 | DIR *pwd_dir; | ||
1255 | int nr_options = 0, choice = -1, ret = -1; | ||
1256 | struct dirent *dent; | ||
1257 | |||
1258 | pwd = getenv("PWD"); | ||
1259 | if (!pwd) | ||
1260 | return ret; | ||
1261 | |||
1262 | pwd_dir = opendir(pwd); | ||
1263 | if (!pwd_dir) | ||
1264 | return ret; | ||
1265 | |||
1266 | memset(options, 0, sizeof(options)); | ||
1267 | memset(options, 0, sizeof(abs_path)); | ||
1268 | |||
1269 | while ((dent = readdir(pwd_dir))) { | ||
1270 | char path[PATH_MAX]; | ||
1271 | u64 magic; | ||
1272 | char *name = dent->d_name; | ||
1273 | FILE *file; | ||
1274 | |||
1275 | if (!(dent->d_type == DT_REG)) | ||
1276 | continue; | ||
1277 | |||
1278 | snprintf(path, sizeof(path), "%s/%s", pwd, name); | ||
1279 | |||
1280 | file = fopen(path, "r"); | ||
1281 | if (!file) | ||
1282 | continue; | ||
1283 | |||
1284 | if (fread(&magic, 1, 8, file) < 8) | ||
1285 | goto close_file_and_continue; | ||
1286 | |||
1287 | if (is_perf_magic(magic)) { | ||
1288 | options[nr_options] = strdup(name); | ||
1289 | if (!options[nr_options]) | ||
1290 | goto close_file_and_continue; | ||
1291 | |||
1292 | abs_path[nr_options] = strdup(path); | ||
1293 | if (!abs_path[nr_options]) { | ||
1294 | free(options[nr_options]); | ||
1295 | ui__warning("Can't search all data files due to memory shortage.\n"); | ||
1296 | fclose(file); | ||
1297 | break; | ||
1298 | } | ||
1299 | |||
1300 | nr_options++; | ||
1301 | } | ||
1302 | |||
1303 | close_file_and_continue: | ||
1304 | fclose(file); | ||
1305 | if (nr_options >= 32) { | ||
1306 | ui__warning("Too many perf data files in PWD!\n" | ||
1307 | "Only the first 32 files will be listed.\n"); | ||
1308 | break; | ||
1309 | } | ||
1310 | } | ||
1311 | closedir(pwd_dir); | ||
1312 | |||
1313 | if (nr_options) { | ||
1314 | choice = ui__popup_menu(nr_options, options); | ||
1315 | if (choice < nr_options && choice >= 0) { | ||
1316 | tmp = strdup(abs_path[choice]); | ||
1317 | if (tmp) { | ||
1318 | if (is_input_name_malloced) | ||
1319 | free((void *)input_name); | ||
1320 | input_name = tmp; | ||
1321 | is_input_name_malloced = true; | ||
1322 | ret = 0; | ||
1323 | } else | ||
1324 | ui__warning("Data switch failed due to memory shortage!\n"); | ||
1325 | } | ||
1326 | } | ||
1327 | |||
1328 | free_popup_options(options, nr_options); | ||
1329 | free_popup_options(abs_path, nr_options); | ||
1330 | return ret; | ||
1331 | } | ||
1332 | |||
1333 | |||
1130 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | 1334 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
1131 | const char *helpline, const char *ev_name, | 1335 | const char *helpline, const char *ev_name, |
1132 | bool left_exits, | 1336 | bool left_exits, |
1133 | void(*timer)(void *arg), void *arg, | 1337 | struct hist_browser_timer *hbt, |
1134 | int delay_secs) | 1338 | struct perf_session_env *env) |
1135 | { | 1339 | { |
1136 | struct hists *hists = &evsel->hists; | 1340 | struct hists *hists = &evsel->hists; |
1137 | struct hist_browser *browser = hist_browser__new(hists); | 1341 | struct hist_browser *browser = hist_browser__new(hists); |
@@ -1141,6 +1345,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1141 | int nr_options = 0; | 1345 | int nr_options = 0; |
1142 | int key = -1; | 1346 | int key = -1; |
1143 | char buf[64]; | 1347 | char buf[64]; |
1348 | char script_opt[64]; | ||
1349 | int delay_secs = hbt ? hbt->refresh : 0; | ||
1144 | 1350 | ||
1145 | if (browser == NULL) | 1351 | if (browser == NULL) |
1146 | return -1; | 1352 | return -1; |
@@ -1159,10 +1365,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1159 | int choice = 0, | 1365 | int choice = 0, |
1160 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 1366 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
1161 | annotate_f = -2, annotate_t = -2, browse_map = -2; | 1367 | annotate_f = -2, annotate_t = -2, browse_map = -2; |
1368 | int scripts_comm = -2, scripts_symbol = -2, | ||
1369 | scripts_all = -2, switch_data = -2; | ||
1162 | 1370 | ||
1163 | nr_options = 0; | 1371 | nr_options = 0; |
1164 | 1372 | ||
1165 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); | 1373 | key = hist_browser__run(browser, ev_name, hbt); |
1166 | 1374 | ||
1167 | if (browser->he_selection != NULL) { | 1375 | if (browser->he_selection != NULL) { |
1168 | thread = hist_browser__selected_thread(browser); | 1376 | thread = hist_browser__selected_thread(browser); |
@@ -1211,6 +1419,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1211 | hist_browser__reset(browser); | 1419 | hist_browser__reset(browser); |
1212 | } | 1420 | } |
1213 | continue; | 1421 | continue; |
1422 | case 'r': | ||
1423 | if (is_report_browser(hbt)) | ||
1424 | goto do_scripts; | ||
1425 | continue; | ||
1426 | case 's': | ||
1427 | if (is_report_browser(hbt)) | ||
1428 | goto do_data_switch; | ||
1429 | continue; | ||
1214 | case K_F1: | 1430 | case K_F1: |
1215 | case 'h': | 1431 | case 'h': |
1216 | case '?': | 1432 | case '?': |
@@ -1229,6 +1445,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1229 | "E Expand all callchains\n" | 1445 | "E Expand all callchains\n" |
1230 | "d Zoom into current DSO\n" | 1446 | "d Zoom into current DSO\n" |
1231 | "t Zoom into current Thread\n" | 1447 | "t Zoom into current Thread\n" |
1448 | "r Run available scripts('perf report' only)\n" | ||
1449 | "s Switch to another data file in PWD ('perf report' only)\n" | ||
1232 | "P Print histograms to perf.hist.N\n" | 1450 | "P Print histograms to perf.hist.N\n" |
1233 | "V Verbose (DSO names in callchains, etc)\n" | 1451 | "V Verbose (DSO names in callchains, etc)\n" |
1234 | "/ Filter symbol by name"); | 1452 | "/ Filter symbol by name"); |
@@ -1317,6 +1535,28 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1317 | browser->selection->map != NULL && | 1535 | browser->selection->map != NULL && |
1318 | asprintf(&options[nr_options], "Browse map details") > 0) | 1536 | asprintf(&options[nr_options], "Browse map details") > 0) |
1319 | browse_map = nr_options++; | 1537 | browse_map = nr_options++; |
1538 | |||
1539 | /* perf script support */ | ||
1540 | if (browser->he_selection) { | ||
1541 | struct symbol *sym; | ||
1542 | |||
1543 | if (asprintf(&options[nr_options], "Run scripts for samples of thread [%s]", | ||
1544 | browser->he_selection->thread->comm) > 0) | ||
1545 | scripts_comm = nr_options++; | ||
1546 | |||
1547 | sym = browser->he_selection->ms.sym; | ||
1548 | if (sym && sym->namelen && | ||
1549 | asprintf(&options[nr_options], "Run scripts for samples of symbol [%s]", | ||
1550 | sym->name) > 0) | ||
1551 | scripts_symbol = nr_options++; | ||
1552 | } | ||
1553 | |||
1554 | if (asprintf(&options[nr_options], "Run scripts for all samples") > 0) | ||
1555 | scripts_all = nr_options++; | ||
1556 | |||
1557 | if (is_report_browser(hbt) && asprintf(&options[nr_options], | ||
1558 | "Switch to another data file in PWD") > 0) | ||
1559 | switch_data = nr_options++; | ||
1320 | add_exit_option: | 1560 | add_exit_option: |
1321 | options[nr_options++] = (char *)"Exit"; | 1561 | options[nr_options++] = (char *)"Exit"; |
1322 | retry_popup_menu: | 1562 | retry_popup_menu: |
@@ -1334,6 +1574,9 @@ retry_popup_menu: | |||
1334 | struct hist_entry *he; | 1574 | struct hist_entry *he; |
1335 | int err; | 1575 | int err; |
1336 | do_annotate: | 1576 | do_annotate: |
1577 | if (!objdump_path && perf_session_env__lookup_objdump(env)) | ||
1578 | continue; | ||
1579 | |||
1337 | he = hist_browser__selected_entry(browser); | 1580 | he = hist_browser__selected_entry(browser); |
1338 | if (he == NULL) | 1581 | if (he == NULL) |
1339 | continue; | 1582 | continue; |
@@ -1356,8 +1599,7 @@ do_annotate: | |||
1356 | * Don't let this be freed, say, by hists__decay_entry. | 1599 | * Don't let this be freed, say, by hists__decay_entry. |
1357 | */ | 1600 | */ |
1358 | he->used = true; | 1601 | he->used = true; |
1359 | err = hist_entry__tui_annotate(he, evsel->idx, | 1602 | err = hist_entry__tui_annotate(he, evsel->idx, hbt); |
1360 | timer, arg, delay_secs); | ||
1361 | he->used = false; | 1603 | he->used = false; |
1362 | /* | 1604 | /* |
1363 | * offer option to annotate the other branch source or target | 1605 | * offer option to annotate the other branch source or target |
@@ -1411,6 +1653,30 @@ zoom_out_thread: | |||
1411 | hists__filter_by_thread(hists); | 1653 | hists__filter_by_thread(hists); |
1412 | hist_browser__reset(browser); | 1654 | hist_browser__reset(browser); |
1413 | } | 1655 | } |
1656 | /* perf scripts support */ | ||
1657 | else if (choice == scripts_all || choice == scripts_comm || | ||
1658 | choice == scripts_symbol) { | ||
1659 | do_scripts: | ||
1660 | memset(script_opt, 0, 64); | ||
1661 | |||
1662 | if (choice == scripts_comm) | ||
1663 | sprintf(script_opt, " -c %s ", browser->he_selection->thread->comm); | ||
1664 | |||
1665 | if (choice == scripts_symbol) | ||
1666 | sprintf(script_opt, " -S %s ", browser->he_selection->ms.sym->name); | ||
1667 | |||
1668 | script_browse(script_opt); | ||
1669 | } | ||
1670 | /* Switch to another data file */ | ||
1671 | else if (choice == switch_data) { | ||
1672 | do_data_switch: | ||
1673 | if (!switch_data_file()) { | ||
1674 | key = K_SWITCH_INPUT_DATA; | ||
1675 | break; | ||
1676 | } else | ||
1677 | ui__warning("Won't switch the data files due to\n" | ||
1678 | "no valid data file get selected!\n"); | ||
1679 | } | ||
1414 | } | 1680 | } |
1415 | out_free_stack: | 1681 | out_free_stack: |
1416 | pstack__delete(fstack); | 1682 | pstack__delete(fstack); |
@@ -1424,6 +1690,7 @@ struct perf_evsel_menu { | |||
1424 | struct ui_browser b; | 1690 | struct ui_browser b; |
1425 | struct perf_evsel *selection; | 1691 | struct perf_evsel *selection; |
1426 | bool lost_events, lost_events_warned; | 1692 | bool lost_events, lost_events_warned; |
1693 | struct perf_session_env *env; | ||
1427 | }; | 1694 | }; |
1428 | 1695 | ||
1429 | static void perf_evsel_menu__write(struct ui_browser *browser, | 1696 | static void perf_evsel_menu__write(struct ui_browser *browser, |
@@ -1442,6 +1709,16 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1442 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | 1709 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : |
1443 | HE_COLORSET_NORMAL); | 1710 | HE_COLORSET_NORMAL); |
1444 | 1711 | ||
1712 | if (symbol_conf.event_group && evsel->nr_members > 1) { | ||
1713 | struct perf_evsel *pos; | ||
1714 | |||
1715 | ev_name = perf_evsel__group_name(evsel); | ||
1716 | |||
1717 | for_each_group_member(pos, evsel) { | ||
1718 | nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1719 | } | ||
1720 | } | ||
1721 | |||
1445 | nr_events = convert_unit(nr_events, &unit); | 1722 | nr_events = convert_unit(nr_events, &unit); |
1446 | printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, | 1723 | printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, |
1447 | unit, unit == ' ' ? "" : " ", ev_name); | 1724 | unit, unit == ' ' ? "" : " ", ev_name); |
@@ -1466,11 +1743,12 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1466 | 1743 | ||
1467 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, | 1744 | static int perf_evsel_menu__run(struct perf_evsel_menu *menu, |
1468 | int nr_events, const char *help, | 1745 | int nr_events, const char *help, |
1469 | void(*timer)(void *arg), void *arg, int delay_secs) | 1746 | struct hist_browser_timer *hbt) |
1470 | { | 1747 | { |
1471 | struct perf_evlist *evlist = menu->b.priv; | 1748 | struct perf_evlist *evlist = menu->b.priv; |
1472 | struct perf_evsel *pos; | 1749 | struct perf_evsel *pos; |
1473 | const char *ev_name, *title = "Available samples"; | 1750 | const char *ev_name, *title = "Available samples"; |
1751 | int delay_secs = hbt ? hbt->refresh : 0; | ||
1474 | int key; | 1752 | int key; |
1475 | 1753 | ||
1476 | if (ui_browser__show(&menu->b, title, | 1754 | if (ui_browser__show(&menu->b, title, |
@@ -1482,7 +1760,7 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, | |||
1482 | 1760 | ||
1483 | switch (key) { | 1761 | switch (key) { |
1484 | case K_TIMER: | 1762 | case K_TIMER: |
1485 | timer(arg); | 1763 | hbt->timer(hbt->arg); |
1486 | 1764 | ||
1487 | if (!menu->lost_events_warned && menu->lost_events) { | 1765 | if (!menu->lost_events_warned && menu->lost_events) { |
1488 | ui_browser__warn_lost_events(&menu->b); | 1766 | ui_browser__warn_lost_events(&menu->b); |
@@ -1500,12 +1778,12 @@ browse_hists: | |||
1500 | * Give the calling tool a chance to populate the non | 1778 | * Give the calling tool a chance to populate the non |
1501 | * default evsel resorted hists tree. | 1779 | * default evsel resorted hists tree. |
1502 | */ | 1780 | */ |
1503 | if (timer) | 1781 | if (hbt) |
1504 | timer(arg); | 1782 | hbt->timer(hbt->arg); |
1505 | ev_name = perf_evsel__name(pos); | 1783 | ev_name = perf_evsel__name(pos); |
1506 | key = perf_evsel__hists_browse(pos, nr_events, help, | 1784 | key = perf_evsel__hists_browse(pos, nr_events, help, |
1507 | ev_name, true, timer, | 1785 | ev_name, true, hbt, |
1508 | arg, delay_secs); | 1786 | menu->env); |
1509 | ui_browser__show_title(&menu->b, title); | 1787 | ui_browser__show_title(&menu->b, title); |
1510 | switch (key) { | 1788 | switch (key) { |
1511 | case K_TAB: | 1789 | case K_TAB: |
@@ -1525,6 +1803,7 @@ browse_hists: | |||
1525 | "Do you really want to exit?")) | 1803 | "Do you really want to exit?")) |
1526 | continue; | 1804 | continue; |
1527 | /* Fall thru */ | 1805 | /* Fall thru */ |
1806 | case K_SWITCH_INPUT_DATA: | ||
1528 | case 'q': | 1807 | case 'q': |
1529 | case CTRL('c'): | 1808 | case CTRL('c'): |
1530 | goto out; | 1809 | goto out; |
@@ -1551,10 +1830,21 @@ out: | |||
1551 | return key; | 1830 | return key; |
1552 | } | 1831 | } |
1553 | 1832 | ||
1833 | static bool filter_group_entries(struct ui_browser *self __maybe_unused, | ||
1834 | void *entry) | ||
1835 | { | ||
1836 | struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node); | ||
1837 | |||
1838 | if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel)) | ||
1839 | return true; | ||
1840 | |||
1841 | return false; | ||
1842 | } | ||
1843 | |||
1554 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | 1844 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, |
1555 | const char *help, | 1845 | int nr_entries, const char *help, |
1556 | void(*timer)(void *arg), void *arg, | 1846 | struct hist_browser_timer *hbt, |
1557 | int delay_secs) | 1847 | struct perf_session_env *env) |
1558 | { | 1848 | { |
1559 | struct perf_evsel *pos; | 1849 | struct perf_evsel *pos; |
1560 | struct perf_evsel_menu menu = { | 1850 | struct perf_evsel_menu menu = { |
@@ -1563,9 +1853,11 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1563 | .refresh = ui_browser__list_head_refresh, | 1853 | .refresh = ui_browser__list_head_refresh, |
1564 | .seek = ui_browser__list_head_seek, | 1854 | .seek = ui_browser__list_head_seek, |
1565 | .write = perf_evsel_menu__write, | 1855 | .write = perf_evsel_menu__write, |
1566 | .nr_entries = evlist->nr_entries, | 1856 | .filter = filter_group_entries, |
1857 | .nr_entries = nr_entries, | ||
1567 | .priv = evlist, | 1858 | .priv = evlist, |
1568 | }, | 1859 | }, |
1860 | .env = env, | ||
1569 | }; | 1861 | }; |
1570 | 1862 | ||
1571 | ui_helpline__push("Press ESC to exit"); | 1863 | ui_helpline__push("Press ESC to exit"); |
@@ -1578,23 +1870,37 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | |||
1578 | menu.b.width = line_len; | 1870 | menu.b.width = line_len; |
1579 | } | 1871 | } |
1580 | 1872 | ||
1581 | return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer, | 1873 | return perf_evsel_menu__run(&menu, nr_entries, help, hbt); |
1582 | arg, delay_secs); | ||
1583 | } | 1874 | } |
1584 | 1875 | ||
1585 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | 1876 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
1586 | void(*timer)(void *arg), void *arg, | 1877 | struct hist_browser_timer *hbt, |
1587 | int delay_secs) | 1878 | struct perf_session_env *env) |
1588 | { | 1879 | { |
1589 | if (evlist->nr_entries == 1) { | 1880 | int nr_entries = evlist->nr_entries; |
1881 | |||
1882 | single_entry: | ||
1883 | if (nr_entries == 1) { | ||
1590 | struct perf_evsel *first = list_entry(evlist->entries.next, | 1884 | struct perf_evsel *first = list_entry(evlist->entries.next, |
1591 | struct perf_evsel, node); | 1885 | struct perf_evsel, node); |
1592 | const char *ev_name = perf_evsel__name(first); | 1886 | const char *ev_name = perf_evsel__name(first); |
1593 | return perf_evsel__hists_browse(first, evlist->nr_entries, help, | 1887 | |
1594 | ev_name, false, timer, arg, | 1888 | return perf_evsel__hists_browse(first, nr_entries, help, |
1595 | delay_secs); | 1889 | ev_name, false, hbt, env); |
1890 | } | ||
1891 | |||
1892 | if (symbol_conf.event_group) { | ||
1893 | struct perf_evsel *pos; | ||
1894 | |||
1895 | nr_entries = 0; | ||
1896 | list_for_each_entry(pos, &evlist->entries, node) | ||
1897 | if (perf_evsel__is_group_leader(pos)) | ||
1898 | nr_entries++; | ||
1899 | |||
1900 | if (nr_entries == 1) | ||
1901 | goto single_entry; | ||
1596 | } | 1902 | } |
1597 | 1903 | ||
1598 | return __perf_evlist__tui_browse_hists(evlist, help, | 1904 | return __perf_evlist__tui_browse_hists(evlist, nr_entries, help, |
1599 | timer, arg, delay_secs); | 1905 | hbt, env); |
1600 | } | 1906 | } |
diff --git a/tools/perf/ui/browsers/scripts.c b/tools/perf/ui/browsers/scripts.c new file mode 100644 index 000000000000..cbbd44b0d93e --- /dev/null +++ b/tools/perf/ui/browsers/scripts.c | |||
@@ -0,0 +1,189 @@ | |||
1 | #include <elf.h> | ||
2 | #include <newt.h> | ||
3 | #include <inttypes.h> | ||
4 | #include <sys/ttydefaults.h> | ||
5 | #include <string.h> | ||
6 | #include "../../util/sort.h" | ||
7 | #include "../../util/util.h" | ||
8 | #include "../../util/hist.h" | ||
9 | #include "../../util/debug.h" | ||
10 | #include "../../util/symbol.h" | ||
11 | #include "../browser.h" | ||
12 | #include "../helpline.h" | ||
13 | #include "../libslang.h" | ||
14 | |||
15 | /* 2048 lines should be enough for a script output */ | ||
16 | #define MAX_LINES 2048 | ||
17 | |||
18 | /* 160 bytes for one output line */ | ||
19 | #define AVERAGE_LINE_LEN 160 | ||
20 | |||
21 | struct script_line { | ||
22 | struct list_head node; | ||
23 | char line[AVERAGE_LINE_LEN]; | ||
24 | }; | ||
25 | |||
26 | struct perf_script_browser { | ||
27 | struct ui_browser b; | ||
28 | struct list_head entries; | ||
29 | const char *script_name; | ||
30 | int nr_lines; | ||
31 | }; | ||
32 | |||
33 | #define SCRIPT_NAMELEN 128 | ||
34 | #define SCRIPT_MAX_NO 64 | ||
35 | /* | ||
36 | * Usually the full path for a script is: | ||
37 | * /home/username/libexec/perf-core/scripts/python/xxx.py | ||
38 | * /home/username/libexec/perf-core/scripts/perl/xxx.pl | ||
39 | * So 256 should be long enough to contain the full path. | ||
40 | */ | ||
41 | #define SCRIPT_FULLPATH_LEN 256 | ||
42 | |||
43 | /* | ||
44 | * When success, will copy the full path of the selected script | ||
45 | * into the buffer pointed by script_name, and return 0. | ||
46 | * Return -1 on failure. | ||
47 | */ | ||
48 | static int list_scripts(char *script_name) | ||
49 | { | ||
50 | char *buf, *names[SCRIPT_MAX_NO], *paths[SCRIPT_MAX_NO]; | ||
51 | int i, num, choice, ret = -1; | ||
52 | |||
53 | /* Preset the script name to SCRIPT_NAMELEN */ | ||
54 | buf = malloc(SCRIPT_MAX_NO * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN)); | ||
55 | if (!buf) | ||
56 | return ret; | ||
57 | |||
58 | for (i = 0; i < SCRIPT_MAX_NO; i++) { | ||
59 | names[i] = buf + i * (SCRIPT_NAMELEN + SCRIPT_FULLPATH_LEN); | ||
60 | paths[i] = names[i] + SCRIPT_NAMELEN; | ||
61 | } | ||
62 | |||
63 | num = find_scripts(names, paths); | ||
64 | if (num > 0) { | ||
65 | choice = ui__popup_menu(num, names); | ||
66 | if (choice < num && choice >= 0) { | ||
67 | strcpy(script_name, paths[choice]); | ||
68 | ret = 0; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | free(buf); | ||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | static void script_browser__write(struct ui_browser *browser, | ||
77 | void *entry, int row) | ||
78 | { | ||
79 | struct script_line *sline = list_entry(entry, struct script_line, node); | ||
80 | bool current_entry = ui_browser__is_current_entry(browser, row); | ||
81 | |||
82 | ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : | ||
83 | HE_COLORSET_NORMAL); | ||
84 | |||
85 | slsmg_write_nstring(sline->line, browser->width); | ||
86 | } | ||
87 | |||
88 | static int script_browser__run(struct perf_script_browser *self) | ||
89 | { | ||
90 | int key; | ||
91 | |||
92 | if (ui_browser__show(&self->b, self->script_name, | ||
93 | "Press <- or ESC to exit") < 0) | ||
94 | return -1; | ||
95 | |||
96 | while (1) { | ||
97 | key = ui_browser__run(&self->b, 0); | ||
98 | |||
99 | /* We can add some special key handling here if needed */ | ||
100 | break; | ||
101 | } | ||
102 | |||
103 | ui_browser__hide(&self->b); | ||
104 | return key; | ||
105 | } | ||
106 | |||
107 | |||
108 | int script_browse(const char *script_opt) | ||
109 | { | ||
110 | char cmd[SCRIPT_FULLPATH_LEN*2], script_name[SCRIPT_FULLPATH_LEN]; | ||
111 | char *line = NULL; | ||
112 | size_t len = 0; | ||
113 | ssize_t retlen; | ||
114 | int ret = -1, nr_entries = 0; | ||
115 | FILE *fp; | ||
116 | void *buf; | ||
117 | struct script_line *sline; | ||
118 | |||
119 | struct perf_script_browser script = { | ||
120 | .b = { | ||
121 | .refresh = ui_browser__list_head_refresh, | ||
122 | .seek = ui_browser__list_head_seek, | ||
123 | .write = script_browser__write, | ||
124 | }, | ||
125 | .script_name = script_name, | ||
126 | }; | ||
127 | |||
128 | INIT_LIST_HEAD(&script.entries); | ||
129 | |||
130 | /* Save each line of the output in one struct script_line object. */ | ||
131 | buf = zalloc((sizeof(*sline)) * MAX_LINES); | ||
132 | if (!buf) | ||
133 | return -1; | ||
134 | sline = buf; | ||
135 | |||
136 | memset(script_name, 0, SCRIPT_FULLPATH_LEN); | ||
137 | if (list_scripts(script_name)) | ||
138 | goto exit; | ||
139 | |||
140 | sprintf(cmd, "perf script -s %s ", script_name); | ||
141 | |||
142 | if (script_opt) | ||
143 | strcat(cmd, script_opt); | ||
144 | |||
145 | if (input_name) { | ||
146 | strcat(cmd, " -i "); | ||
147 | strcat(cmd, input_name); | ||
148 | } | ||
149 | |||
150 | strcat(cmd, " 2>&1"); | ||
151 | |||
152 | fp = popen(cmd, "r"); | ||
153 | if (!fp) | ||
154 | goto exit; | ||
155 | |||
156 | while ((retlen = getline(&line, &len, fp)) != -1) { | ||
157 | strncpy(sline->line, line, AVERAGE_LINE_LEN); | ||
158 | |||
159 | /* If one output line is very large, just cut it short */ | ||
160 | if (retlen >= AVERAGE_LINE_LEN) { | ||
161 | sline->line[AVERAGE_LINE_LEN - 1] = '\0'; | ||
162 | sline->line[AVERAGE_LINE_LEN - 2] = '\n'; | ||
163 | } | ||
164 | list_add_tail(&sline->node, &script.entries); | ||
165 | |||
166 | if (script.b.width < retlen) | ||
167 | script.b.width = retlen; | ||
168 | |||
169 | if (nr_entries++ >= MAX_LINES - 1) | ||
170 | break; | ||
171 | sline++; | ||
172 | } | ||
173 | |||
174 | if (script.b.width > AVERAGE_LINE_LEN) | ||
175 | script.b.width = AVERAGE_LINE_LEN; | ||
176 | |||
177 | if (line) | ||
178 | free(line); | ||
179 | pclose(fp); | ||
180 | |||
181 | script.nr_lines = nr_entries; | ||
182 | script.b.nr_entries = nr_entries; | ||
183 | script.b.entries = &script.entries; | ||
184 | |||
185 | ret = script_browser__run(&script); | ||
186 | exit: | ||
187 | free(buf); | ||
188 | return ret; | ||
189 | } | ||
diff --git a/tools/perf/ui/gtk/annotate.c b/tools/perf/ui/gtk/annotate.c new file mode 100644 index 000000000000..7d8dc581a545 --- /dev/null +++ b/tools/perf/ui/gtk/annotate.c | |||
@@ -0,0 +1,229 @@ | |||
1 | #include "gtk.h" | ||
2 | #include "util/debug.h" | ||
3 | #include "util/annotate.h" | ||
4 | #include "ui/helpline.h" | ||
5 | |||
6 | |||
7 | enum { | ||
8 | ANN_COL__PERCENT, | ||
9 | ANN_COL__OFFSET, | ||
10 | ANN_COL__LINE, | ||
11 | |||
12 | MAX_ANN_COLS | ||
13 | }; | ||
14 | |||
15 | static const char *const col_names[] = { | ||
16 | "Overhead", | ||
17 | "Offset", | ||
18 | "Line" | ||
19 | }; | ||
20 | |||
21 | static int perf_gtk__get_percent(char *buf, size_t size, struct symbol *sym, | ||
22 | struct disasm_line *dl, int evidx) | ||
23 | { | ||
24 | struct sym_hist *symhist; | ||
25 | double percent = 0.0; | ||
26 | const char *markup; | ||
27 | int ret = 0; | ||
28 | |||
29 | strcpy(buf, ""); | ||
30 | |||
31 | if (dl->offset == (s64) -1) | ||
32 | return 0; | ||
33 | |||
34 | symhist = annotation__histogram(symbol__annotation(sym), evidx); | ||
35 | if (!symhist->addr[dl->offset]) | ||
36 | return 0; | ||
37 | |||
38 | percent = 100.0 * symhist->addr[dl->offset] / symhist->sum; | ||
39 | |||
40 | markup = perf_gtk__get_percent_color(percent); | ||
41 | if (markup) | ||
42 | ret += scnprintf(buf, size, "%s", markup); | ||
43 | ret += scnprintf(buf + ret, size - ret, "%6.2f%%", percent); | ||
44 | if (markup) | ||
45 | ret += scnprintf(buf + ret, size - ret, "</span>"); | ||
46 | |||
47 | return ret; | ||
48 | } | ||
49 | |||
50 | static int perf_gtk__get_offset(char *buf, size_t size, struct symbol *sym, | ||
51 | struct map *map, struct disasm_line *dl) | ||
52 | { | ||
53 | u64 start = map__rip_2objdump(map, sym->start); | ||
54 | |||
55 | strcpy(buf, ""); | ||
56 | |||
57 | if (dl->offset == (s64) -1) | ||
58 | return 0; | ||
59 | |||
60 | return scnprintf(buf, size, "%"PRIx64, start + dl->offset); | ||
61 | } | ||
62 | |||
63 | static int perf_gtk__get_line(char *buf, size_t size, struct disasm_line *dl) | ||
64 | { | ||
65 | int ret = 0; | ||
66 | char *line = g_markup_escape_text(dl->line, -1); | ||
67 | const char *markup = "<span fgcolor='gray'>"; | ||
68 | |||
69 | strcpy(buf, ""); | ||
70 | |||
71 | if (!line) | ||
72 | return 0; | ||
73 | |||
74 | if (dl->offset != (s64) -1) | ||
75 | markup = NULL; | ||
76 | |||
77 | if (markup) | ||
78 | ret += scnprintf(buf, size, "%s", markup); | ||
79 | ret += scnprintf(buf + ret, size - ret, "%s", line); | ||
80 | if (markup) | ||
81 | ret += scnprintf(buf + ret, size - ret, "</span>"); | ||
82 | |||
83 | g_free(line); | ||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | static int perf_gtk__annotate_symbol(GtkWidget *window, struct symbol *sym, | ||
88 | struct map *map, int evidx, | ||
89 | struct hist_browser_timer *hbt __maybe_unused) | ||
90 | { | ||
91 | struct disasm_line *pos, *n; | ||
92 | struct annotation *notes; | ||
93 | GType col_types[MAX_ANN_COLS]; | ||
94 | GtkCellRenderer *renderer; | ||
95 | GtkListStore *store; | ||
96 | GtkWidget *view; | ||
97 | int i; | ||
98 | char s[512]; | ||
99 | |||
100 | notes = symbol__annotation(sym); | ||
101 | |||
102 | for (i = 0; i < MAX_ANN_COLS; i++) { | ||
103 | col_types[i] = G_TYPE_STRING; | ||
104 | } | ||
105 | store = gtk_list_store_newv(MAX_ANN_COLS, col_types); | ||
106 | |||
107 | view = gtk_tree_view_new(); | ||
108 | renderer = gtk_cell_renderer_text_new(); | ||
109 | |||
110 | for (i = 0; i < MAX_ANN_COLS; i++) { | ||
111 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
112 | -1, col_names[i], renderer, "markup", | ||
113 | i, NULL); | ||
114 | } | ||
115 | |||
116 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | ||
117 | g_object_unref(GTK_TREE_MODEL(store)); | ||
118 | |||
119 | list_for_each_entry(pos, ¬es->src->source, node) { | ||
120 | GtkTreeIter iter; | ||
121 | |||
122 | gtk_list_store_append(store, &iter); | ||
123 | |||
124 | if (perf_gtk__get_percent(s, sizeof(s), sym, pos, evidx)) | ||
125 | gtk_list_store_set(store, &iter, ANN_COL__PERCENT, s, -1); | ||
126 | if (perf_gtk__get_offset(s, sizeof(s), sym, map, pos)) | ||
127 | gtk_list_store_set(store, &iter, ANN_COL__OFFSET, s, -1); | ||
128 | if (perf_gtk__get_line(s, sizeof(s), pos)) | ||
129 | gtk_list_store_set(store, &iter, ANN_COL__LINE, s, -1); | ||
130 | } | ||
131 | |||
132 | gtk_container_add(GTK_CONTAINER(window), view); | ||
133 | |||
134 | list_for_each_entry_safe(pos, n, ¬es->src->source, node) { | ||
135 | list_del(&pos->node); | ||
136 | disasm_line__free(pos); | ||
137 | } | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx, | ||
143 | struct hist_browser_timer *hbt) | ||
144 | { | ||
145 | GtkWidget *window; | ||
146 | GtkWidget *notebook; | ||
147 | GtkWidget *scrolled_window; | ||
148 | GtkWidget *tab_label; | ||
149 | |||
150 | if (map->dso->annotate_warned) | ||
151 | return -1; | ||
152 | |||
153 | if (symbol__annotate(sym, map, 0) < 0) { | ||
154 | ui__error("%s", ui_helpline__current); | ||
155 | return -1; | ||
156 | } | ||
157 | |||
158 | if (perf_gtk__is_active_context(pgctx)) { | ||
159 | window = pgctx->main_window; | ||
160 | notebook = pgctx->notebook; | ||
161 | } else { | ||
162 | GtkWidget *vbox; | ||
163 | GtkWidget *infobar; | ||
164 | GtkWidget *statbar; | ||
165 | |||
166 | signal(SIGSEGV, perf_gtk__signal); | ||
167 | signal(SIGFPE, perf_gtk__signal); | ||
168 | signal(SIGINT, perf_gtk__signal); | ||
169 | signal(SIGQUIT, perf_gtk__signal); | ||
170 | signal(SIGTERM, perf_gtk__signal); | ||
171 | |||
172 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
173 | gtk_window_set_title(GTK_WINDOW(window), "perf annotate"); | ||
174 | |||
175 | g_signal_connect(window, "delete_event", gtk_main_quit, NULL); | ||
176 | |||
177 | pgctx = perf_gtk__activate_context(window); | ||
178 | if (!pgctx) | ||
179 | return -1; | ||
180 | |||
181 | vbox = gtk_vbox_new(FALSE, 0); | ||
182 | notebook = gtk_notebook_new(); | ||
183 | pgctx->notebook = notebook; | ||
184 | |||
185 | gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); | ||
186 | |||
187 | infobar = perf_gtk__setup_info_bar(); | ||
188 | if (infobar) { | ||
189 | gtk_box_pack_start(GTK_BOX(vbox), infobar, | ||
190 | FALSE, FALSE, 0); | ||
191 | } | ||
192 | |||
193 | statbar = perf_gtk__setup_statusbar(); | ||
194 | gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); | ||
195 | |||
196 | gtk_container_add(GTK_CONTAINER(window), vbox); | ||
197 | } | ||
198 | |||
199 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); | ||
200 | tab_label = gtk_label_new(sym->name); | ||
201 | |||
202 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | ||
203 | GTK_POLICY_AUTOMATIC, | ||
204 | GTK_POLICY_AUTOMATIC); | ||
205 | |||
206 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, | ||
207 | tab_label); | ||
208 | |||
209 | perf_gtk__annotate_symbol(scrolled_window, sym, map, evidx, hbt); | ||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | void perf_gtk__show_annotations(void) | ||
214 | { | ||
215 | GtkWidget *window; | ||
216 | |||
217 | if (!perf_gtk__is_active_context(pgctx)) | ||
218 | return; | ||
219 | |||
220 | window = pgctx->main_window; | ||
221 | gtk_widget_show_all(window); | ||
222 | |||
223 | perf_gtk__resize_window(window); | ||
224 | gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); | ||
225 | |||
226 | gtk_main(); | ||
227 | |||
228 | perf_gtk__deactivate_context(&pgctx); | ||
229 | } | ||
diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c index 4125c6284114..c95012cdb438 100644 --- a/tools/perf/ui/gtk/browser.c +++ b/tools/perf/ui/gtk/browser.c | |||
@@ -8,15 +8,13 @@ | |||
8 | 8 | ||
9 | #include <signal.h> | 9 | #include <signal.h> |
10 | 10 | ||
11 | #define MAX_COLUMNS 32 | 11 | void perf_gtk__signal(int sig) |
12 | |||
13 | static void perf_gtk__signal(int sig) | ||
14 | { | 12 | { |
15 | perf_gtk__exit(false); | 13 | perf_gtk__exit(false); |
16 | psignal(sig, "perf"); | 14 | psignal(sig, "perf"); |
17 | } | 15 | } |
18 | 16 | ||
19 | static void perf_gtk__resize_window(GtkWidget *window) | 17 | void perf_gtk__resize_window(GtkWidget *window) |
20 | { | 18 | { |
21 | GdkRectangle rect; | 19 | GdkRectangle rect; |
22 | GdkScreen *screen; | 20 | GdkScreen *screen; |
@@ -36,7 +34,7 @@ static void perf_gtk__resize_window(GtkWidget *window) | |||
36 | gtk_window_resize(GTK_WINDOW(window), width, height); | 34 | gtk_window_resize(GTK_WINDOW(window), width, height); |
37 | } | 35 | } |
38 | 36 | ||
39 | static const char *perf_gtk__get_percent_color(double percent) | 37 | const char *perf_gtk__get_percent_color(double percent) |
40 | { | 38 | { |
41 | if (percent >= MIN_RED) | 39 | if (percent >= MIN_RED) |
42 | return "<span fgcolor='red'>"; | 40 | return "<span fgcolor='red'>"; |
@@ -45,155 +43,8 @@ static const char *perf_gtk__get_percent_color(double percent) | |||
45 | return NULL; | 43 | return NULL; |
46 | } | 44 | } |
47 | 45 | ||
48 | #define HPP__COLOR_FN(_name, _field) \ | ||
49 | static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp, \ | ||
50 | struct hist_entry *he) \ | ||
51 | { \ | ||
52 | struct hists *hists = he->hists; \ | ||
53 | double percent = 100.0 * he->stat._field / hists->stats.total_period; \ | ||
54 | const char *markup; \ | ||
55 | int ret = 0; \ | ||
56 | \ | ||
57 | markup = perf_gtk__get_percent_color(percent); \ | ||
58 | if (markup) \ | ||
59 | ret += scnprintf(hpp->buf, hpp->size, "%s", markup); \ | ||
60 | ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%6.2f%%", percent); \ | ||
61 | if (markup) \ | ||
62 | ret += scnprintf(hpp->buf + ret, hpp->size - ret, "</span>"); \ | ||
63 | \ | ||
64 | return ret; \ | ||
65 | } | ||
66 | |||
67 | HPP__COLOR_FN(overhead, period) | ||
68 | HPP__COLOR_FN(overhead_sys, period_sys) | ||
69 | HPP__COLOR_FN(overhead_us, period_us) | ||
70 | HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) | ||
71 | HPP__COLOR_FN(overhead_guest_us, period_guest_us) | ||
72 | |||
73 | #undef HPP__COLOR_FN | ||
74 | |||
75 | void perf_gtk__init_hpp(void) | ||
76 | { | ||
77 | perf_hpp__init(); | ||
78 | |||
79 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | ||
80 | perf_gtk__hpp_color_overhead; | ||
81 | perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = | ||
82 | perf_gtk__hpp_color_overhead_sys; | ||
83 | perf_hpp__format[PERF_HPP__OVERHEAD_US].color = | ||
84 | perf_gtk__hpp_color_overhead_us; | ||
85 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = | ||
86 | perf_gtk__hpp_color_overhead_guest_sys; | ||
87 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = | ||
88 | perf_gtk__hpp_color_overhead_guest_us; | ||
89 | } | ||
90 | |||
91 | static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) | ||
92 | { | ||
93 | GType col_types[MAX_COLUMNS]; | ||
94 | GtkCellRenderer *renderer; | ||
95 | struct sort_entry *se; | ||
96 | GtkListStore *store; | ||
97 | struct rb_node *nd; | ||
98 | GtkWidget *view; | ||
99 | int i, col_idx; | ||
100 | int nr_cols; | ||
101 | char s[512]; | ||
102 | |||
103 | struct perf_hpp hpp = { | ||
104 | .buf = s, | ||
105 | .size = sizeof(s), | ||
106 | }; | ||
107 | |||
108 | nr_cols = 0; | ||
109 | |||
110 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | ||
111 | if (!perf_hpp__format[i].cond) | ||
112 | continue; | ||
113 | |||
114 | col_types[nr_cols++] = G_TYPE_STRING; | ||
115 | } | ||
116 | |||
117 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
118 | if (se->elide) | ||
119 | continue; | ||
120 | |||
121 | col_types[nr_cols++] = G_TYPE_STRING; | ||
122 | } | ||
123 | |||
124 | store = gtk_list_store_newv(nr_cols, col_types); | ||
125 | |||
126 | view = gtk_tree_view_new(); | ||
127 | |||
128 | renderer = gtk_cell_renderer_text_new(); | ||
129 | |||
130 | col_idx = 0; | ||
131 | |||
132 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | ||
133 | if (!perf_hpp__format[i].cond) | ||
134 | continue; | ||
135 | |||
136 | perf_hpp__format[i].header(&hpp); | ||
137 | |||
138 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
139 | -1, s, | ||
140 | renderer, "markup", | ||
141 | col_idx++, NULL); | ||
142 | } | ||
143 | |||
144 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
145 | if (se->elide) | ||
146 | continue; | ||
147 | |||
148 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
149 | -1, se->se_header, | ||
150 | renderer, "text", | ||
151 | col_idx++, NULL); | ||
152 | } | ||
153 | |||
154 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | ||
155 | |||
156 | g_object_unref(GTK_TREE_MODEL(store)); | ||
157 | |||
158 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
159 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
160 | GtkTreeIter iter; | ||
161 | |||
162 | if (h->filtered) | ||
163 | continue; | ||
164 | |||
165 | gtk_list_store_append(store, &iter); | ||
166 | |||
167 | col_idx = 0; | ||
168 | |||
169 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | ||
170 | if (!perf_hpp__format[i].cond) | ||
171 | continue; | ||
172 | |||
173 | if (perf_hpp__format[i].color) | ||
174 | perf_hpp__format[i].color(&hpp, h); | ||
175 | else | ||
176 | perf_hpp__format[i].entry(&hpp, h); | ||
177 | |||
178 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
179 | } | ||
180 | |||
181 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
182 | if (se->elide) | ||
183 | continue; | ||
184 | |||
185 | se->se_snprintf(h, s, ARRAY_SIZE(s), | ||
186 | hists__col_len(hists, se->se_width_idx)); | ||
187 | |||
188 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
189 | } | ||
190 | } | ||
191 | |||
192 | gtk_container_add(GTK_CONTAINER(window), view); | ||
193 | } | ||
194 | |||
195 | #ifdef HAVE_GTK_INFO_BAR | 46 | #ifdef HAVE_GTK_INFO_BAR |
196 | static GtkWidget *perf_gtk__setup_info_bar(void) | 47 | GtkWidget *perf_gtk__setup_info_bar(void) |
197 | { | 48 | { |
198 | GtkWidget *info_bar; | 49 | GtkWidget *info_bar; |
199 | GtkWidget *label; | 50 | GtkWidget *label; |
@@ -220,7 +71,7 @@ static GtkWidget *perf_gtk__setup_info_bar(void) | |||
220 | } | 71 | } |
221 | #endif | 72 | #endif |
222 | 73 | ||
223 | static GtkWidget *perf_gtk__setup_statusbar(void) | 74 | GtkWidget *perf_gtk__setup_statusbar(void) |
224 | { | 75 | { |
225 | GtkWidget *stbar; | 76 | GtkWidget *stbar; |
226 | unsigned ctxid; | 77 | unsigned ctxid; |
@@ -234,81 +85,3 @@ static GtkWidget *perf_gtk__setup_statusbar(void) | |||
234 | 85 | ||
235 | return stbar; | 86 | return stbar; |
236 | } | 87 | } |
237 | |||
238 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | ||
239 | const char *help, | ||
240 | void (*timer) (void *arg)__maybe_unused, | ||
241 | void *arg __maybe_unused, | ||
242 | int delay_secs __maybe_unused) | ||
243 | { | ||
244 | struct perf_evsel *pos; | ||
245 | GtkWidget *vbox; | ||
246 | GtkWidget *notebook; | ||
247 | GtkWidget *info_bar; | ||
248 | GtkWidget *statbar; | ||
249 | GtkWidget *window; | ||
250 | |||
251 | signal(SIGSEGV, perf_gtk__signal); | ||
252 | signal(SIGFPE, perf_gtk__signal); | ||
253 | signal(SIGINT, perf_gtk__signal); | ||
254 | signal(SIGQUIT, perf_gtk__signal); | ||
255 | signal(SIGTERM, perf_gtk__signal); | ||
256 | |||
257 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
258 | |||
259 | gtk_window_set_title(GTK_WINDOW(window), "perf report"); | ||
260 | |||
261 | g_signal_connect(window, "delete_event", gtk_main_quit, NULL); | ||
262 | |||
263 | pgctx = perf_gtk__activate_context(window); | ||
264 | if (!pgctx) | ||
265 | return -1; | ||
266 | |||
267 | vbox = gtk_vbox_new(FALSE, 0); | ||
268 | |||
269 | notebook = gtk_notebook_new(); | ||
270 | |||
271 | list_for_each_entry(pos, &evlist->entries, node) { | ||
272 | struct hists *hists = &pos->hists; | ||
273 | const char *evname = perf_evsel__name(pos); | ||
274 | GtkWidget *scrolled_window; | ||
275 | GtkWidget *tab_label; | ||
276 | |||
277 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); | ||
278 | |||
279 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | ||
280 | GTK_POLICY_AUTOMATIC, | ||
281 | GTK_POLICY_AUTOMATIC); | ||
282 | |||
283 | perf_gtk__show_hists(scrolled_window, hists); | ||
284 | |||
285 | tab_label = gtk_label_new(evname); | ||
286 | |||
287 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); | ||
288 | } | ||
289 | |||
290 | gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); | ||
291 | |||
292 | info_bar = perf_gtk__setup_info_bar(); | ||
293 | if (info_bar) | ||
294 | gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0); | ||
295 | |||
296 | statbar = perf_gtk__setup_statusbar(); | ||
297 | gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); | ||
298 | |||
299 | gtk_container_add(GTK_CONTAINER(window), vbox); | ||
300 | |||
301 | gtk_widget_show_all(window); | ||
302 | |||
303 | perf_gtk__resize_window(window); | ||
304 | |||
305 | gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); | ||
306 | |||
307 | ui_helpline__push(help); | ||
308 | |||
309 | gtk_main(); | ||
310 | |||
311 | perf_gtk__deactivate_context(&pgctx); | ||
312 | |||
313 | return 0; | ||
314 | } | ||
diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h index 687af0bba187..3d96785ef155 100644 --- a/tools/perf/ui/gtk/gtk.h +++ b/tools/perf/ui/gtk/gtk.h | |||
@@ -10,6 +10,7 @@ | |||
10 | 10 | ||
11 | struct perf_gtk_context { | 11 | struct perf_gtk_context { |
12 | GtkWidget *main_window; | 12 | GtkWidget *main_window; |
13 | GtkWidget *notebook; | ||
13 | 14 | ||
14 | #ifdef HAVE_GTK_INFO_BAR | 15 | #ifdef HAVE_GTK_INFO_BAR |
15 | GtkWidget *info_bar; | 16 | GtkWidget *info_bar; |
@@ -30,9 +31,17 @@ struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window); | |||
30 | int perf_gtk__deactivate_context(struct perf_gtk_context **ctx); | 31 | int perf_gtk__deactivate_context(struct perf_gtk_context **ctx); |
31 | 32 | ||
32 | void perf_gtk__init_helpline(void); | 33 | void perf_gtk__init_helpline(void); |
34 | void perf_gtk__init_progress(void); | ||
33 | void perf_gtk__init_hpp(void); | 35 | void perf_gtk__init_hpp(void); |
34 | 36 | ||
35 | #ifndef HAVE_GTK_INFO_BAR | 37 | void perf_gtk__signal(int sig); |
38 | void perf_gtk__resize_window(GtkWidget *window); | ||
39 | const char *perf_gtk__get_percent_color(double percent); | ||
40 | GtkWidget *perf_gtk__setup_statusbar(void); | ||
41 | |||
42 | #ifdef HAVE_GTK_INFO_BAR | ||
43 | GtkWidget *perf_gtk__setup_info_bar(void); | ||
44 | #else | ||
36 | static inline GtkWidget *perf_gtk__setup_info_bar(void) | 45 | static inline GtkWidget *perf_gtk__setup_info_bar(void) |
37 | { | 46 | { |
38 | return NULL; | 47 | return NULL; |
diff --git a/tools/perf/ui/gtk/helpline.c b/tools/perf/ui/gtk/helpline.c index 5db4432ff12a..3388cbd12186 100644 --- a/tools/perf/ui/gtk/helpline.c +++ b/tools/perf/ui/gtk/helpline.c | |||
@@ -24,17 +24,7 @@ static void gtk_helpline_push(const char *msg) | |||
24 | pgctx->statbar_ctx_id, msg); | 24 | pgctx->statbar_ctx_id, msg); |
25 | } | 25 | } |
26 | 26 | ||
27 | static struct ui_helpline gtk_helpline_fns = { | 27 | static int gtk_helpline_show(const char *fmt, va_list ap) |
28 | .pop = gtk_helpline_pop, | ||
29 | .push = gtk_helpline_push, | ||
30 | }; | ||
31 | |||
32 | void perf_gtk__init_helpline(void) | ||
33 | { | ||
34 | helpline_fns = >k_helpline_fns; | ||
35 | } | ||
36 | |||
37 | int perf_gtk__show_helpline(const char *fmt, va_list ap) | ||
38 | { | 28 | { |
39 | int ret; | 29 | int ret; |
40 | char *ptr; | 30 | char *ptr; |
@@ -54,3 +44,14 @@ int perf_gtk__show_helpline(const char *fmt, va_list ap) | |||
54 | 44 | ||
55 | return ret; | 45 | return ret; |
56 | } | 46 | } |
47 | |||
48 | static struct ui_helpline gtk_helpline_fns = { | ||
49 | .pop = gtk_helpline_pop, | ||
50 | .push = gtk_helpline_push, | ||
51 | .show = gtk_helpline_show, | ||
52 | }; | ||
53 | |||
54 | void perf_gtk__init_helpline(void) | ||
55 | { | ||
56 | helpline_fns = >k_helpline_fns; | ||
57 | } | ||
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c new file mode 100644 index 000000000000..1e764a8ad259 --- /dev/null +++ b/tools/perf/ui/gtk/hists.c | |||
@@ -0,0 +1,312 @@ | |||
1 | #include "../evlist.h" | ||
2 | #include "../cache.h" | ||
3 | #include "../evsel.h" | ||
4 | #include "../sort.h" | ||
5 | #include "../hist.h" | ||
6 | #include "../helpline.h" | ||
7 | #include "gtk.h" | ||
8 | |||
9 | #define MAX_COLUMNS 32 | ||
10 | |||
11 | static int __percent_color_snprintf(char *buf, size_t size, double percent) | ||
12 | { | ||
13 | int ret = 0; | ||
14 | const char *markup; | ||
15 | |||
16 | markup = perf_gtk__get_percent_color(percent); | ||
17 | if (markup) | ||
18 | ret += scnprintf(buf, size, markup); | ||
19 | |||
20 | ret += scnprintf(buf + ret, size - ret, " %6.2f%%", percent); | ||
21 | |||
22 | if (markup) | ||
23 | ret += scnprintf(buf + ret, size - ret, "</span>"); | ||
24 | |||
25 | return ret; | ||
26 | } | ||
27 | |||
28 | |||
29 | static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he, | ||
30 | u64 (*get_field)(struct hist_entry *)) | ||
31 | { | ||
32 | int ret; | ||
33 | double percent = 0.0; | ||
34 | struct hists *hists = he->hists; | ||
35 | |||
36 | if (hists->stats.total_period) | ||
37 | percent = 100.0 * get_field(he) / hists->stats.total_period; | ||
38 | |||
39 | ret = __percent_color_snprintf(hpp->buf, hpp->size, percent); | ||
40 | |||
41 | if (symbol_conf.event_group) { | ||
42 | int prev_idx, idx_delta; | ||
43 | struct perf_evsel *evsel = hists_to_evsel(hists); | ||
44 | struct hist_entry *pair; | ||
45 | int nr_members = evsel->nr_members; | ||
46 | |||
47 | if (nr_members <= 1) | ||
48 | return ret; | ||
49 | |||
50 | prev_idx = perf_evsel__group_idx(evsel); | ||
51 | |||
52 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { | ||
53 | u64 period = get_field(pair); | ||
54 | u64 total = pair->hists->stats.total_period; | ||
55 | |||
56 | evsel = hists_to_evsel(pair->hists); | ||
57 | idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; | ||
58 | |||
59 | while (idx_delta--) { | ||
60 | /* | ||
61 | * zero-fill group members in the middle which | ||
62 | * have no sample | ||
63 | */ | ||
64 | ret += __percent_color_snprintf(hpp->buf + ret, | ||
65 | hpp->size - ret, | ||
66 | 0.0); | ||
67 | } | ||
68 | |||
69 | percent = 100.0 * period / total; | ||
70 | ret += __percent_color_snprintf(hpp->buf + ret, | ||
71 | hpp->size - ret, | ||
72 | percent); | ||
73 | |||
74 | prev_idx = perf_evsel__group_idx(evsel); | ||
75 | } | ||
76 | |||
77 | idx_delta = nr_members - prev_idx - 1; | ||
78 | |||
79 | while (idx_delta--) { | ||
80 | /* | ||
81 | * zero-fill group members at last which have no sample | ||
82 | */ | ||
83 | ret += __percent_color_snprintf(hpp->buf + ret, | ||
84 | hpp->size - ret, | ||
85 | 0.0); | ||
86 | } | ||
87 | } | ||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | #define __HPP_COLOR_PERCENT_FN(_type, _field) \ | ||
92 | static u64 he_get_##_field(struct hist_entry *he) \ | ||
93 | { \ | ||
94 | return he->stat._field; \ | ||
95 | } \ | ||
96 | \ | ||
97 | static int perf_gtk__hpp_color_##_type(struct perf_hpp *hpp, \ | ||
98 | struct hist_entry *he) \ | ||
99 | { \ | ||
100 | return __hpp__color_fmt(hpp, he, he_get_##_field); \ | ||
101 | } | ||
102 | |||
103 | __HPP_COLOR_PERCENT_FN(overhead, period) | ||
104 | __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) | ||
105 | __HPP_COLOR_PERCENT_FN(overhead_us, period_us) | ||
106 | __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) | ||
107 | __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) | ||
108 | |||
109 | #undef __HPP_COLOR_PERCENT_FN | ||
110 | |||
111 | |||
112 | void perf_gtk__init_hpp(void) | ||
113 | { | ||
114 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
115 | |||
116 | perf_hpp__init(); | ||
117 | |||
118 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | ||
119 | perf_gtk__hpp_color_overhead; | ||
120 | perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color = | ||
121 | perf_gtk__hpp_color_overhead_sys; | ||
122 | perf_hpp__format[PERF_HPP__OVERHEAD_US].color = | ||
123 | perf_gtk__hpp_color_overhead_us; | ||
124 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color = | ||
125 | perf_gtk__hpp_color_overhead_guest_sys; | ||
126 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color = | ||
127 | perf_gtk__hpp_color_overhead_guest_us; | ||
128 | } | ||
129 | |||
130 | static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) | ||
131 | { | ||
132 | struct perf_hpp_fmt *fmt; | ||
133 | GType col_types[MAX_COLUMNS]; | ||
134 | GtkCellRenderer *renderer; | ||
135 | struct sort_entry *se; | ||
136 | GtkListStore *store; | ||
137 | struct rb_node *nd; | ||
138 | GtkWidget *view; | ||
139 | int col_idx; | ||
140 | int nr_cols; | ||
141 | char s[512]; | ||
142 | |||
143 | struct perf_hpp hpp = { | ||
144 | .buf = s, | ||
145 | .size = sizeof(s), | ||
146 | .ptr = hists_to_evsel(hists), | ||
147 | }; | ||
148 | |||
149 | nr_cols = 0; | ||
150 | |||
151 | perf_hpp__for_each_format(fmt) | ||
152 | col_types[nr_cols++] = G_TYPE_STRING; | ||
153 | |||
154 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
155 | if (se->elide) | ||
156 | continue; | ||
157 | |||
158 | col_types[nr_cols++] = G_TYPE_STRING; | ||
159 | } | ||
160 | |||
161 | store = gtk_list_store_newv(nr_cols, col_types); | ||
162 | |||
163 | view = gtk_tree_view_new(); | ||
164 | |||
165 | renderer = gtk_cell_renderer_text_new(); | ||
166 | |||
167 | col_idx = 0; | ||
168 | |||
169 | perf_hpp__for_each_format(fmt) { | ||
170 | fmt->header(&hpp); | ||
171 | |||
172 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
173 | -1, ltrim(s), | ||
174 | renderer, "markup", | ||
175 | col_idx++, NULL); | ||
176 | } | ||
177 | |||
178 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
179 | if (se->elide) | ||
180 | continue; | ||
181 | |||
182 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
183 | -1, se->se_header, | ||
184 | renderer, "text", | ||
185 | col_idx++, NULL); | ||
186 | } | ||
187 | |||
188 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | ||
189 | |||
190 | g_object_unref(GTK_TREE_MODEL(store)); | ||
191 | |||
192 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
193 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
194 | GtkTreeIter iter; | ||
195 | |||
196 | if (h->filtered) | ||
197 | continue; | ||
198 | |||
199 | gtk_list_store_append(store, &iter); | ||
200 | |||
201 | col_idx = 0; | ||
202 | |||
203 | perf_hpp__for_each_format(fmt) { | ||
204 | if (fmt->color) | ||
205 | fmt->color(&hpp, h); | ||
206 | else | ||
207 | fmt->entry(&hpp, h); | ||
208 | |||
209 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
210 | } | ||
211 | |||
212 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
213 | if (se->elide) | ||
214 | continue; | ||
215 | |||
216 | se->se_snprintf(h, s, ARRAY_SIZE(s), | ||
217 | hists__col_len(hists, se->se_width_idx)); | ||
218 | |||
219 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | gtk_container_add(GTK_CONTAINER(window), view); | ||
224 | } | ||
225 | |||
226 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | ||
227 | const char *help, | ||
228 | struct hist_browser_timer *hbt __maybe_unused) | ||
229 | { | ||
230 | struct perf_evsel *pos; | ||
231 | GtkWidget *vbox; | ||
232 | GtkWidget *notebook; | ||
233 | GtkWidget *info_bar; | ||
234 | GtkWidget *statbar; | ||
235 | GtkWidget *window; | ||
236 | |||
237 | signal(SIGSEGV, perf_gtk__signal); | ||
238 | signal(SIGFPE, perf_gtk__signal); | ||
239 | signal(SIGINT, perf_gtk__signal); | ||
240 | signal(SIGQUIT, perf_gtk__signal); | ||
241 | signal(SIGTERM, perf_gtk__signal); | ||
242 | |||
243 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
244 | |||
245 | gtk_window_set_title(GTK_WINDOW(window), "perf report"); | ||
246 | |||
247 | g_signal_connect(window, "delete_event", gtk_main_quit, NULL); | ||
248 | |||
249 | pgctx = perf_gtk__activate_context(window); | ||
250 | if (!pgctx) | ||
251 | return -1; | ||
252 | |||
253 | vbox = gtk_vbox_new(FALSE, 0); | ||
254 | |||
255 | notebook = gtk_notebook_new(); | ||
256 | |||
257 | gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0); | ||
258 | |||
259 | info_bar = perf_gtk__setup_info_bar(); | ||
260 | if (info_bar) | ||
261 | gtk_box_pack_start(GTK_BOX(vbox), info_bar, FALSE, FALSE, 0); | ||
262 | |||
263 | statbar = perf_gtk__setup_statusbar(); | ||
264 | gtk_box_pack_start(GTK_BOX(vbox), statbar, FALSE, FALSE, 0); | ||
265 | |||
266 | gtk_container_add(GTK_CONTAINER(window), vbox); | ||
267 | |||
268 | list_for_each_entry(pos, &evlist->entries, node) { | ||
269 | struct hists *hists = &pos->hists; | ||
270 | const char *evname = perf_evsel__name(pos); | ||
271 | GtkWidget *scrolled_window; | ||
272 | GtkWidget *tab_label; | ||
273 | char buf[512]; | ||
274 | size_t size = sizeof(buf); | ||
275 | |||
276 | if (symbol_conf.event_group) { | ||
277 | if (!perf_evsel__is_group_leader(pos)) | ||
278 | continue; | ||
279 | |||
280 | if (pos->nr_members > 1) { | ||
281 | perf_evsel__group_desc(pos, buf, size); | ||
282 | evname = buf; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); | ||
287 | |||
288 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | ||
289 | GTK_POLICY_AUTOMATIC, | ||
290 | GTK_POLICY_AUTOMATIC); | ||
291 | |||
292 | perf_gtk__show_hists(scrolled_window, hists); | ||
293 | |||
294 | tab_label = gtk_label_new(evname); | ||
295 | |||
296 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); | ||
297 | } | ||
298 | |||
299 | gtk_widget_show_all(window); | ||
300 | |||
301 | perf_gtk__resize_window(window); | ||
302 | |||
303 | gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); | ||
304 | |||
305 | ui_helpline__push(help); | ||
306 | |||
307 | gtk_main(); | ||
308 | |||
309 | perf_gtk__deactivate_context(&pgctx); | ||
310 | |||
311 | return 0; | ||
312 | } | ||
diff --git a/tools/perf/ui/gtk/progress.c b/tools/perf/ui/gtk/progress.c new file mode 100644 index 000000000000..482bcf3df9b7 --- /dev/null +++ b/tools/perf/ui/gtk/progress.c | |||
@@ -0,0 +1,59 @@ | |||
1 | #include <inttypes.h> | ||
2 | |||
3 | #include "gtk.h" | ||
4 | #include "../progress.h" | ||
5 | #include "util.h" | ||
6 | |||
7 | static GtkWidget *dialog; | ||
8 | static GtkWidget *progress; | ||
9 | |||
10 | static void gtk_progress_update(u64 curr, u64 total, const char *title) | ||
11 | { | ||
12 | double fraction = total ? 1.0 * curr / total : 0.0; | ||
13 | char buf[1024]; | ||
14 | |||
15 | if (dialog == NULL) { | ||
16 | GtkWidget *vbox = gtk_vbox_new(TRUE, 5); | ||
17 | GtkWidget *label = gtk_label_new(title); | ||
18 | |||
19 | dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
20 | progress = gtk_progress_bar_new(); | ||
21 | |||
22 | gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 3); | ||
23 | gtk_box_pack_start(GTK_BOX(vbox), progress, TRUE, TRUE, 3); | ||
24 | |||
25 | gtk_container_add(GTK_CONTAINER(dialog), vbox); | ||
26 | |||
27 | gtk_window_set_title(GTK_WINDOW(dialog), "perf"); | ||
28 | gtk_window_resize(GTK_WINDOW(dialog), 300, 80); | ||
29 | gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); | ||
30 | |||
31 | gtk_widget_show_all(dialog); | ||
32 | } | ||
33 | |||
34 | gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress), fraction); | ||
35 | snprintf(buf, sizeof(buf), "%"PRIu64" / %"PRIu64, curr, total); | ||
36 | gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), buf); | ||
37 | |||
38 | /* we didn't call gtk_main yet, so do it manually */ | ||
39 | while (gtk_events_pending()) | ||
40 | gtk_main_iteration(); | ||
41 | } | ||
42 | |||
43 | static void gtk_progress_finish(void) | ||
44 | { | ||
45 | /* this will also destroy all of its children */ | ||
46 | gtk_widget_destroy(dialog); | ||
47 | |||
48 | dialog = NULL; | ||
49 | } | ||
50 | |||
51 | static struct ui_progress gtk_progress_fns = { | ||
52 | .update = gtk_progress_update, | ||
53 | .finish = gtk_progress_finish, | ||
54 | }; | ||
55 | |||
56 | void perf_gtk__init_progress(void) | ||
57 | { | ||
58 | progress_fns = >k_progress_fns; | ||
59 | } | ||
diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c index 3c4c6ef78283..6c2dd2e423f3 100644 --- a/tools/perf/ui/gtk/setup.c +++ b/tools/perf/ui/gtk/setup.c | |||
@@ -8,7 +8,9 @@ int perf_gtk__init(void) | |||
8 | { | 8 | { |
9 | perf_error__register(&perf_gtk_eops); | 9 | perf_error__register(&perf_gtk_eops); |
10 | perf_gtk__init_helpline(); | 10 | perf_gtk__init_helpline(); |
11 | perf_gtk__init_progress(); | ||
11 | perf_gtk__init_hpp(); | 12 | perf_gtk__init_hpp(); |
13 | |||
12 | return gtk_init_check(NULL, NULL) ? 0 : -1; | 14 | return gtk_init_check(NULL, NULL) ? 0 : -1; |
13 | } | 15 | } |
14 | 16 | ||
diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c index ccb046aac98b..c06942a41c78 100644 --- a/tools/perf/ui/gtk/util.c +++ b/tools/perf/ui/gtk/util.c | |||
@@ -111,14 +111,3 @@ struct perf_error_ops perf_gtk_eops = { | |||
111 | .warning = perf_gtk__warning_statusbar, | 111 | .warning = perf_gtk__warning_statusbar, |
112 | #endif | 112 | #endif |
113 | }; | 113 | }; |
114 | |||
115 | /* | ||
116 | * FIXME: Functions below should be implemented properly. | ||
117 | * For now, just add stubs for NO_NEWT=1 build. | ||
118 | */ | ||
119 | #ifndef NEWT_SUPPORT | ||
120 | void ui_progress__update(u64 curr __maybe_unused, u64 total __maybe_unused, | ||
121 | const char *title __maybe_unused) | ||
122 | { | ||
123 | } | ||
124 | #endif | ||
diff --git a/tools/perf/ui/helpline.c b/tools/perf/ui/helpline.c index a49bcf3c190b..700fb3cfa1c7 100644 --- a/tools/perf/ui/helpline.c +++ b/tools/perf/ui/helpline.c | |||
@@ -16,9 +16,16 @@ static void nop_helpline__push(const char *msg __maybe_unused) | |||
16 | { | 16 | { |
17 | } | 17 | } |
18 | 18 | ||
19 | static int nop_helpline__show(const char *fmt __maybe_unused, | ||
20 | va_list ap __maybe_unused) | ||
21 | { | ||
22 | return 0; | ||
23 | } | ||
24 | |||
19 | static struct ui_helpline default_helpline_fns = { | 25 | static struct ui_helpline default_helpline_fns = { |
20 | .pop = nop_helpline__pop, | 26 | .pop = nop_helpline__pop, |
21 | .push = nop_helpline__push, | 27 | .push = nop_helpline__push, |
28 | .show = nop_helpline__show, | ||
22 | }; | 29 | }; |
23 | 30 | ||
24 | struct ui_helpline *helpline_fns = &default_helpline_fns; | 31 | struct ui_helpline *helpline_fns = &default_helpline_fns; |
@@ -59,3 +66,8 @@ void ui_helpline__puts(const char *msg) | |||
59 | ui_helpline__pop(); | 66 | ui_helpline__pop(); |
60 | ui_helpline__push(msg); | 67 | ui_helpline__push(msg); |
61 | } | 68 | } |
69 | |||
70 | int ui_helpline__vshow(const char *fmt, va_list ap) | ||
71 | { | ||
72 | return helpline_fns->show(fmt, ap); | ||
73 | } | ||
diff --git a/tools/perf/ui/helpline.h b/tools/perf/ui/helpline.h index baa28a4d16b9..46181f4fc07e 100644 --- a/tools/perf/ui/helpline.h +++ b/tools/perf/ui/helpline.h | |||
@@ -9,6 +9,7 @@ | |||
9 | struct ui_helpline { | 9 | struct ui_helpline { |
10 | void (*pop)(void); | 10 | void (*pop)(void); |
11 | void (*push)(const char *msg); | 11 | void (*push)(const char *msg); |
12 | int (*show)(const char *fmt, va_list ap); | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | extern struct ui_helpline *helpline_fns; | 15 | extern struct ui_helpline *helpline_fns; |
@@ -20,28 +21,9 @@ void ui_helpline__push(const char *msg); | |||
20 | void ui_helpline__vpush(const char *fmt, va_list ap); | 21 | void ui_helpline__vpush(const char *fmt, va_list ap); |
21 | void ui_helpline__fpush(const char *fmt, ...); | 22 | void ui_helpline__fpush(const char *fmt, ...); |
22 | void ui_helpline__puts(const char *msg); | 23 | void ui_helpline__puts(const char *msg); |
24 | int ui_helpline__vshow(const char *fmt, va_list ap); | ||
23 | 25 | ||
24 | extern char ui_helpline__current[512]; | 26 | extern char ui_helpline__current[512]; |
25 | |||
26 | #ifdef NEWT_SUPPORT | ||
27 | extern char ui_helpline__last_msg[]; | 27 | extern char ui_helpline__last_msg[]; |
28 | int ui_helpline__show_help(const char *format, va_list ap); | ||
29 | #else | ||
30 | static inline int ui_helpline__show_help(const char *format __maybe_unused, | ||
31 | va_list ap __maybe_unused) | ||
32 | { | ||
33 | return 0; | ||
34 | } | ||
35 | #endif /* NEWT_SUPPORT */ | ||
36 | |||
37 | #ifdef GTK2_SUPPORT | ||
38 | int perf_gtk__show_helpline(const char *format, va_list ap); | ||
39 | #else | ||
40 | static inline int perf_gtk__show_helpline(const char *format __maybe_unused, | ||
41 | va_list ap __maybe_unused) | ||
42 | { | ||
43 | return 0; | ||
44 | } | ||
45 | #endif /* GTK2_SUPPORT */ | ||
46 | 28 | ||
47 | #endif /* _PERF_UI_HELPLINE_H_ */ | 29 | #endif /* _PERF_UI_HELPLINE_H_ */ |
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index f5a1e4f65263..d671e63aa351 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -3,151 +3,163 @@ | |||
3 | #include "../util/hist.h" | 3 | #include "../util/hist.h" |
4 | #include "../util/util.h" | 4 | #include "../util/util.h" |
5 | #include "../util/sort.h" | 5 | #include "../util/sort.h" |
6 | 6 | #include "../util/evsel.h" | |
7 | 7 | ||
8 | /* hist period print (hpp) functions */ | 8 | /* hist period print (hpp) functions */ |
9 | static int hpp__header_overhead(struct perf_hpp *hpp) | ||
10 | { | ||
11 | return scnprintf(hpp->buf, hpp->size, "Overhead"); | ||
12 | } | ||
13 | 9 | ||
14 | static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused) | 10 | typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...); |
15 | { | ||
16 | return 8; | ||
17 | } | ||
18 | 11 | ||
19 | static int hpp__color_overhead(struct perf_hpp *hpp, struct hist_entry *he) | 12 | static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, |
13 | u64 (*get_field)(struct hist_entry *), | ||
14 | const char *fmt, hpp_snprint_fn print_fn, | ||
15 | bool fmt_percent) | ||
20 | { | 16 | { |
17 | int ret; | ||
21 | struct hists *hists = he->hists; | 18 | struct hists *hists = he->hists; |
22 | double percent = 100.0 * he->stat.period / hists->stats.total_period; | ||
23 | 19 | ||
24 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); | 20 | if (fmt_percent) { |
25 | } | 21 | double percent = 0.0; |
26 | 22 | ||
27 | static int hpp__entry_overhead(struct perf_hpp *hpp, struct hist_entry *he) | 23 | if (hists->stats.total_period) |
28 | { | 24 | percent = 100.0 * get_field(he) / |
29 | struct hists *hists = he->hists; | 25 | hists->stats.total_period; |
30 | double percent = 100.0 * he->stat.period / hists->stats.total_period; | ||
31 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; | ||
32 | 26 | ||
33 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | 27 | ret = print_fn(hpp->buf, hpp->size, fmt, percent); |
34 | } | 28 | } else |
29 | ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he)); | ||
35 | 30 | ||
36 | static int hpp__header_overhead_sys(struct perf_hpp *hpp) | 31 | if (symbol_conf.event_group) { |
37 | { | 32 | int prev_idx, idx_delta; |
38 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; | 33 | struct perf_evsel *evsel = hists_to_evsel(hists); |
34 | struct hist_entry *pair; | ||
35 | int nr_members = evsel->nr_members; | ||
39 | 36 | ||
40 | return scnprintf(hpp->buf, hpp->size, fmt, "sys"); | 37 | if (nr_members <= 1) |
41 | } | 38 | return ret; |
42 | 39 | ||
43 | static int hpp__width_overhead_sys(struct perf_hpp *hpp __maybe_unused) | 40 | prev_idx = perf_evsel__group_idx(evsel); |
44 | { | ||
45 | return 7; | ||
46 | } | ||
47 | 41 | ||
48 | static int hpp__color_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) | 42 | list_for_each_entry(pair, &he->pairs.head, pairs.node) { |
49 | { | 43 | u64 period = get_field(pair); |
50 | struct hists *hists = he->hists; | 44 | u64 total = pair->hists->stats.total_period; |
51 | double percent = 100.0 * he->stat.period_sys / hists->stats.total_period; | ||
52 | 45 | ||
53 | return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent); | 46 | if (!total) |
54 | } | 47 | continue; |
55 | 48 | ||
56 | static int hpp__entry_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) | 49 | evsel = hists_to_evsel(pair->hists); |
57 | { | 50 | idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; |
58 | struct hists *hists = he->hists; | ||
59 | double percent = 100.0 * he->stat.period_sys / hists->stats.total_period; | ||
60 | const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; | ||
61 | 51 | ||
62 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | 52 | while (idx_delta--) { |
63 | } | 53 | /* |
54 | * zero-fill group members in the middle which | ||
55 | * have no sample | ||
56 | */ | ||
57 | ret += print_fn(hpp->buf + ret, hpp->size - ret, | ||
58 | fmt, 0); | ||
59 | } | ||
64 | 60 | ||
65 | static int hpp__header_overhead_us(struct perf_hpp *hpp) | 61 | if (fmt_percent) |
66 | { | 62 | ret += print_fn(hpp->buf + ret, hpp->size - ret, |
67 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; | 63 | fmt, 100.0 * period / total); |
68 | 64 | else | |
69 | return scnprintf(hpp->buf, hpp->size, fmt, "user"); | 65 | ret += print_fn(hpp->buf + ret, hpp->size - ret, |
70 | } | 66 | fmt, period); |
71 | 67 | ||
72 | static int hpp__width_overhead_us(struct perf_hpp *hpp __maybe_unused) | 68 | prev_idx = perf_evsel__group_idx(evsel); |
73 | { | 69 | } |
74 | return 7; | ||
75 | } | ||
76 | |||
77 | static int hpp__color_overhead_us(struct perf_hpp *hpp, struct hist_entry *he) | ||
78 | { | ||
79 | struct hists *hists = he->hists; | ||
80 | double percent = 100.0 * he->stat.period_us / hists->stats.total_period; | ||
81 | |||
82 | return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent); | ||
83 | } | ||
84 | |||
85 | static int hpp__entry_overhead_us(struct perf_hpp *hpp, struct hist_entry *he) | ||
86 | { | ||
87 | struct hists *hists = he->hists; | ||
88 | double percent = 100.0 * he->stat.period_us / hists->stats.total_period; | ||
89 | const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; | ||
90 | |||
91 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | ||
92 | } | ||
93 | |||
94 | static int hpp__header_overhead_guest_sys(struct perf_hpp *hpp) | ||
95 | { | ||
96 | return scnprintf(hpp->buf, hpp->size, "guest sys"); | ||
97 | } | ||
98 | |||
99 | static int hpp__width_overhead_guest_sys(struct perf_hpp *hpp __maybe_unused) | ||
100 | { | ||
101 | return 9; | ||
102 | } | ||
103 | |||
104 | static int hpp__color_overhead_guest_sys(struct perf_hpp *hpp, | ||
105 | struct hist_entry *he) | ||
106 | { | ||
107 | struct hists *hists = he->hists; | ||
108 | double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period; | ||
109 | |||
110 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); | ||
111 | } | ||
112 | |||
113 | static int hpp__entry_overhead_guest_sys(struct perf_hpp *hpp, | ||
114 | struct hist_entry *he) | ||
115 | { | ||
116 | struct hists *hists = he->hists; | ||
117 | double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period; | ||
118 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% "; | ||
119 | 70 | ||
120 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | 71 | idx_delta = nr_members - prev_idx - 1; |
121 | } | ||
122 | 72 | ||
123 | static int hpp__header_overhead_guest_us(struct perf_hpp *hpp) | 73 | while (idx_delta--) { |
124 | { | 74 | /* |
125 | return scnprintf(hpp->buf, hpp->size, "guest usr"); | 75 | * zero-fill group members at last which have no sample |
126 | } | 76 | */ |
127 | 77 | ret += print_fn(hpp->buf + ret, hpp->size - ret, | |
128 | static int hpp__width_overhead_guest_us(struct perf_hpp *hpp __maybe_unused) | 78 | fmt, 0); |
129 | { | 79 | } |
130 | return 9; | 80 | } |
81 | return ret; | ||
131 | } | 82 | } |
132 | 83 | ||
133 | static int hpp__color_overhead_guest_us(struct perf_hpp *hpp, | 84 | #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ |
134 | struct hist_entry *he) | 85 | static int hpp__header_##_type(struct perf_hpp *hpp) \ |
135 | { | 86 | { \ |
136 | struct hists *hists = he->hists; | 87 | int len = _min_width; \ |
137 | double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period; | 88 | \ |
138 | 89 | if (symbol_conf.event_group) { \ | |
139 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); | 90 | struct perf_evsel *evsel = hpp->ptr; \ |
140 | } | 91 | \ |
92 | len = max(len, evsel->nr_members * _unit_width); \ | ||
93 | } \ | ||
94 | return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \ | ||
95 | } | ||
96 | |||
97 | #define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
98 | static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused) \ | ||
99 | { \ | ||
100 | int len = _min_width; \ | ||
101 | \ | ||
102 | if (symbol_conf.event_group) { \ | ||
103 | struct perf_evsel *evsel = hpp->ptr; \ | ||
104 | \ | ||
105 | len = max(len, evsel->nr_members * _unit_width); \ | ||
106 | } \ | ||
107 | return len; \ | ||
108 | } | ||
109 | |||
110 | #define __HPP_COLOR_PERCENT_FN(_type, _field) \ | ||
111 | static u64 he_get_##_field(struct hist_entry *he) \ | ||
112 | { \ | ||
113 | return he->stat._field; \ | ||
114 | } \ | ||
115 | \ | ||
116 | static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ | ||
117 | { \ | ||
118 | return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ | ||
119 | (hpp_snprint_fn)percent_color_snprintf, true); \ | ||
120 | } | ||
121 | |||
122 | #define __HPP_ENTRY_PERCENT_FN(_type, _field) \ | ||
123 | static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ | ||
124 | { \ | ||
125 | const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ | ||
126 | return __hpp__fmt(hpp, he, he_get_##_field, fmt, \ | ||
127 | scnprintf, true); \ | ||
128 | } | ||
129 | |||
130 | #define __HPP_ENTRY_RAW_FN(_type, _field) \ | ||
131 | static u64 he_get_raw_##_field(struct hist_entry *he) \ | ||
132 | { \ | ||
133 | return he->stat._field; \ | ||
134 | } \ | ||
135 | \ | ||
136 | static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ | ||
137 | { \ | ||
138 | const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ | ||
139 | return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \ | ||
140 | } | ||
141 | |||
142 | #define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ | ||
143 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | ||
144 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
145 | __HPP_COLOR_PERCENT_FN(_type, _field) \ | ||
146 | __HPP_ENTRY_PERCENT_FN(_type, _field) | ||
147 | |||
148 | #define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ | ||
149 | __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | ||
150 | __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | ||
151 | __HPP_ENTRY_RAW_FN(_type, _field) | ||
152 | |||
153 | |||
154 | HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) | ||
155 | HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8) | ||
156 | HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8) | ||
157 | HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8) | ||
158 | HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) | ||
159 | |||
160 | HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) | ||
161 | HPP_RAW_FNS(period, "Period", period, 12, 12) | ||
141 | 162 | ||
142 | static int hpp__entry_overhead_guest_us(struct perf_hpp *hpp, | ||
143 | struct hist_entry *he) | ||
144 | { | ||
145 | struct hists *hists = he->hists; | ||
146 | double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period; | ||
147 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% "; | ||
148 | |||
149 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | ||
150 | } | ||
151 | 163 | ||
152 | static int hpp__header_baseline(struct perf_hpp *hpp) | 164 | static int hpp__header_baseline(struct perf_hpp *hpp) |
153 | { | 165 | { |
@@ -161,7 +173,7 @@ static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused) | |||
161 | 173 | ||
162 | static double baseline_percent(struct hist_entry *he) | 174 | static double baseline_percent(struct hist_entry *he) |
163 | { | 175 | { |
164 | struct hist_entry *pair = he->pair; | 176 | struct hist_entry *pair = hist_entry__next_pair(he); |
165 | struct hists *pair_hists = pair ? pair->hists : NULL; | 177 | struct hists *pair_hists = pair ? pair->hists : NULL; |
166 | double percent = 0.0; | 178 | double percent = 0.0; |
167 | 179 | ||
@@ -179,7 +191,10 @@ static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he) | |||
179 | { | 191 | { |
180 | double percent = baseline_percent(he); | 192 | double percent = baseline_percent(he); |
181 | 193 | ||
182 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); | 194 | if (hist_entry__has_pairs(he) || symbol_conf.field_sep) |
195 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); | ||
196 | else | ||
197 | return scnprintf(hpp->buf, hpp->size, " "); | ||
183 | } | 198 | } |
184 | 199 | ||
185 | static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) | 200 | static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) |
@@ -187,45 +202,31 @@ static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) | |||
187 | double percent = baseline_percent(he); | 202 | double percent = baseline_percent(he); |
188 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; | 203 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; |
189 | 204 | ||
190 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | 205 | if (hist_entry__has_pairs(he) || symbol_conf.field_sep) |
191 | } | 206 | return scnprintf(hpp->buf, hpp->size, fmt, percent); |
192 | 207 | else | |
193 | static int hpp__header_samples(struct perf_hpp *hpp) | 208 | return scnprintf(hpp->buf, hpp->size, " "); |
194 | { | ||
195 | const char *fmt = symbol_conf.field_sep ? "%s" : "%11s"; | ||
196 | |||
197 | return scnprintf(hpp->buf, hpp->size, fmt, "Samples"); | ||
198 | } | ||
199 | |||
200 | static int hpp__width_samples(struct perf_hpp *hpp __maybe_unused) | ||
201 | { | ||
202 | return 11; | ||
203 | } | 209 | } |
204 | 210 | ||
205 | static int hpp__entry_samples(struct perf_hpp *hpp, struct hist_entry *he) | 211 | static int hpp__header_period_baseline(struct perf_hpp *hpp) |
206 | { | ||
207 | const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%11" PRIu64; | ||
208 | |||
209 | return scnprintf(hpp->buf, hpp->size, fmt, he->stat.nr_events); | ||
210 | } | ||
211 | |||
212 | static int hpp__header_period(struct perf_hpp *hpp) | ||
213 | { | 212 | { |
214 | const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; | 213 | const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; |
215 | 214 | ||
216 | return scnprintf(hpp->buf, hpp->size, fmt, "Period"); | 215 | return scnprintf(hpp->buf, hpp->size, fmt, "Period Base"); |
217 | } | 216 | } |
218 | 217 | ||
219 | static int hpp__width_period(struct perf_hpp *hpp __maybe_unused) | 218 | static int hpp__width_period_baseline(struct perf_hpp *hpp __maybe_unused) |
220 | { | 219 | { |
221 | return 12; | 220 | return 12; |
222 | } | 221 | } |
223 | 222 | ||
224 | static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he) | 223 | static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *he) |
225 | { | 224 | { |
225 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
226 | u64 period = pair ? pair->stat.period : 0; | ||
226 | const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64; | 227 | const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64; |
227 | 228 | ||
228 | return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period); | 229 | return scnprintf(hpp->buf, hpp->size, fmt, period); |
229 | } | 230 | } |
230 | 231 | ||
231 | static int hpp__header_delta(struct perf_hpp *hpp) | 232 | static int hpp__header_delta(struct perf_hpp *hpp) |
@@ -242,104 +243,188 @@ static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused) | |||
242 | 243 | ||
243 | static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) | 244 | static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) |
244 | { | 245 | { |
245 | struct hist_entry *pair = he->pair; | 246 | struct hist_entry *pair = hist_entry__next_pair(he); |
246 | struct hists *pair_hists = pair ? pair->hists : NULL; | ||
247 | struct hists *hists = he->hists; | ||
248 | u64 old_total, new_total; | ||
249 | double old_percent = 0, new_percent = 0; | ||
250 | double diff; | ||
251 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; | 247 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; |
252 | char buf[32] = " "; | 248 | char buf[32] = " "; |
249 | double diff = 0.0; | ||
253 | 250 | ||
254 | old_total = pair_hists ? pair_hists->stats.total_period : 0; | 251 | if (pair) { |
255 | if (old_total > 0 && pair) | 252 | if (he->diff.computed) |
256 | old_percent = 100.0 * pair->stat.period / old_total; | 253 | diff = he->diff.period_ratio_delta; |
257 | 254 | else | |
258 | new_total = hists->stats.total_period; | 255 | diff = perf_diff__compute_delta(he, pair); |
259 | if (new_total > 0) | 256 | } else |
260 | new_percent = 100.0 * he->stat.period / new_total; | 257 | diff = perf_diff__period_percent(he, he->stat.period); |
261 | 258 | ||
262 | diff = new_percent - old_percent; | ||
263 | if (fabs(diff) >= 0.01) | 259 | if (fabs(diff) >= 0.01) |
264 | scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); | 260 | scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); |
265 | 261 | ||
266 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | 262 | return scnprintf(hpp->buf, hpp->size, fmt, buf); |
267 | } | 263 | } |
268 | 264 | ||
269 | static int hpp__header_displ(struct perf_hpp *hpp) | 265 | static int hpp__header_ratio(struct perf_hpp *hpp) |
266 | { | ||
267 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | ||
268 | |||
269 | return scnprintf(hpp->buf, hpp->size, fmt, "Ratio"); | ||
270 | } | ||
271 | |||
272 | static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused) | ||
273 | { | ||
274 | return 14; | ||
275 | } | ||
276 | |||
277 | static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he) | ||
278 | { | ||
279 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
280 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | ||
281 | char buf[32] = " "; | ||
282 | double ratio = 0.0; | ||
283 | |||
284 | if (pair) { | ||
285 | if (he->diff.computed) | ||
286 | ratio = he->diff.period_ratio; | ||
287 | else | ||
288 | ratio = perf_diff__compute_ratio(he, pair); | ||
289 | } | ||
290 | |||
291 | if (ratio > 0.0) | ||
292 | scnprintf(buf, sizeof(buf), "%+14.6F", ratio); | ||
293 | |||
294 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | ||
295 | } | ||
296 | |||
297 | static int hpp__header_wdiff(struct perf_hpp *hpp) | ||
270 | { | 298 | { |
271 | return scnprintf(hpp->buf, hpp->size, "Displ."); | 299 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; |
300 | |||
301 | return scnprintf(hpp->buf, hpp->size, fmt, "Weighted diff"); | ||
272 | } | 302 | } |
273 | 303 | ||
274 | static int hpp__width_displ(struct perf_hpp *hpp __maybe_unused) | 304 | static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused) |
275 | { | 305 | { |
276 | return 6; | 306 | return 14; |
277 | } | 307 | } |
278 | 308 | ||
279 | static int hpp__entry_displ(struct perf_hpp *hpp, | 309 | static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he) |
280 | struct hist_entry *he) | ||
281 | { | 310 | { |
282 | struct hist_entry *pair = he->pair; | 311 | struct hist_entry *pair = hist_entry__next_pair(he); |
283 | long displacement = pair ? pair->position - he->position : 0; | 312 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; |
284 | const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s"; | ||
285 | char buf[32] = " "; | 313 | char buf[32] = " "; |
314 | s64 wdiff = 0; | ||
315 | |||
316 | if (pair) { | ||
317 | if (he->diff.computed) | ||
318 | wdiff = he->diff.wdiff; | ||
319 | else | ||
320 | wdiff = perf_diff__compute_wdiff(he, pair); | ||
321 | } | ||
322 | |||
323 | if (wdiff != 0) | ||
324 | scnprintf(buf, sizeof(buf), "%14ld", wdiff); | ||
325 | |||
326 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | ||
327 | } | ||
328 | |||
329 | static int hpp__header_formula(struct perf_hpp *hpp) | ||
330 | { | ||
331 | const char *fmt = symbol_conf.field_sep ? "%s" : "%70s"; | ||
332 | |||
333 | return scnprintf(hpp->buf, hpp->size, fmt, "Formula"); | ||
334 | } | ||
335 | |||
336 | static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused) | ||
337 | { | ||
338 | return 70; | ||
339 | } | ||
340 | |||
341 | static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he) | ||
342 | { | ||
343 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
344 | const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s"; | ||
345 | char buf[96] = " "; | ||
286 | 346 | ||
287 | if (displacement) | 347 | if (pair) |
288 | scnprintf(buf, sizeof(buf), "%+4ld", displacement); | 348 | perf_diff__formula(he, pair, buf, sizeof(buf)); |
289 | 349 | ||
290 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | 350 | return scnprintf(hpp->buf, hpp->size, fmt, buf); |
291 | } | 351 | } |
292 | 352 | ||
293 | #define HPP__COLOR_PRINT_FNS(_name) \ | 353 | #define HPP__COLOR_PRINT_FNS(_name) \ |
294 | .header = hpp__header_ ## _name, \ | 354 | { \ |
295 | .width = hpp__width_ ## _name, \ | 355 | .header = hpp__header_ ## _name, \ |
296 | .color = hpp__color_ ## _name, \ | 356 | .width = hpp__width_ ## _name, \ |
297 | .entry = hpp__entry_ ## _name | 357 | .color = hpp__color_ ## _name, \ |
358 | .entry = hpp__entry_ ## _name \ | ||
359 | } | ||
298 | 360 | ||
299 | #define HPP__PRINT_FNS(_name) \ | 361 | #define HPP__PRINT_FNS(_name) \ |
300 | .header = hpp__header_ ## _name, \ | 362 | { \ |
301 | .width = hpp__width_ ## _name, \ | 363 | .header = hpp__header_ ## _name, \ |
302 | .entry = hpp__entry_ ## _name | 364 | .width = hpp__width_ ## _name, \ |
365 | .entry = hpp__entry_ ## _name \ | ||
366 | } | ||
303 | 367 | ||
304 | struct perf_hpp_fmt perf_hpp__format[] = { | 368 | struct perf_hpp_fmt perf_hpp__format[] = { |
305 | { .cond = false, HPP__COLOR_PRINT_FNS(baseline) }, | 369 | HPP__COLOR_PRINT_FNS(baseline), |
306 | { .cond = true, HPP__COLOR_PRINT_FNS(overhead) }, | 370 | HPP__COLOR_PRINT_FNS(overhead), |
307 | { .cond = false, HPP__COLOR_PRINT_FNS(overhead_sys) }, | 371 | HPP__COLOR_PRINT_FNS(overhead_sys), |
308 | { .cond = false, HPP__COLOR_PRINT_FNS(overhead_us) }, | 372 | HPP__COLOR_PRINT_FNS(overhead_us), |
309 | { .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_sys) }, | 373 | HPP__COLOR_PRINT_FNS(overhead_guest_sys), |
310 | { .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_us) }, | 374 | HPP__COLOR_PRINT_FNS(overhead_guest_us), |
311 | { .cond = false, HPP__PRINT_FNS(samples) }, | 375 | HPP__PRINT_FNS(samples), |
312 | { .cond = false, HPP__PRINT_FNS(period) }, | 376 | HPP__PRINT_FNS(period), |
313 | { .cond = false, HPP__PRINT_FNS(delta) }, | 377 | HPP__PRINT_FNS(period_baseline), |
314 | { .cond = false, HPP__PRINT_FNS(displ) } | 378 | HPP__PRINT_FNS(delta), |
379 | HPP__PRINT_FNS(ratio), | ||
380 | HPP__PRINT_FNS(wdiff), | ||
381 | HPP__PRINT_FNS(formula) | ||
315 | }; | 382 | }; |
316 | 383 | ||
384 | LIST_HEAD(perf_hpp__list); | ||
385 | |||
386 | |||
317 | #undef HPP__COLOR_PRINT_FNS | 387 | #undef HPP__COLOR_PRINT_FNS |
318 | #undef HPP__PRINT_FNS | 388 | #undef HPP__PRINT_FNS |
319 | 389 | ||
390 | #undef HPP_PERCENT_FNS | ||
391 | #undef HPP_RAW_FNS | ||
392 | |||
393 | #undef __HPP_HEADER_FN | ||
394 | #undef __HPP_WIDTH_FN | ||
395 | #undef __HPP_COLOR_PERCENT_FN | ||
396 | #undef __HPP_ENTRY_PERCENT_FN | ||
397 | #undef __HPP_ENTRY_RAW_FN | ||
398 | |||
399 | |||
320 | void perf_hpp__init(void) | 400 | void perf_hpp__init(void) |
321 | { | 401 | { |
322 | if (symbol_conf.show_cpu_utilization) { | 402 | if (symbol_conf.show_cpu_utilization) { |
323 | perf_hpp__format[PERF_HPP__OVERHEAD_SYS].cond = true; | 403 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS); |
324 | perf_hpp__format[PERF_HPP__OVERHEAD_US].cond = true; | 404 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_US); |
325 | 405 | ||
326 | if (perf_guest) { | 406 | if (perf_guest) { |
327 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].cond = true; | 407 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS); |
328 | perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].cond = true; | 408 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US); |
329 | } | 409 | } |
330 | } | 410 | } |
331 | 411 | ||
332 | if (symbol_conf.show_nr_samples) | 412 | if (symbol_conf.show_nr_samples) |
333 | perf_hpp__format[PERF_HPP__SAMPLES].cond = true; | 413 | perf_hpp__column_enable(PERF_HPP__SAMPLES); |
334 | 414 | ||
335 | if (symbol_conf.show_total_period) | 415 | if (symbol_conf.show_total_period) |
336 | perf_hpp__format[PERF_HPP__PERIOD].cond = true; | 416 | perf_hpp__column_enable(PERF_HPP__PERIOD); |
417 | } | ||
418 | |||
419 | void perf_hpp__column_register(struct perf_hpp_fmt *format) | ||
420 | { | ||
421 | list_add_tail(&format->list, &perf_hpp__list); | ||
337 | } | 422 | } |
338 | 423 | ||
339 | void perf_hpp__column_enable(unsigned col, bool enable) | 424 | void perf_hpp__column_enable(unsigned col) |
340 | { | 425 | { |
341 | BUG_ON(col >= PERF_HPP__MAX_INDEX); | 426 | BUG_ON(col >= PERF_HPP__MAX_INDEX); |
342 | perf_hpp__format[col].cond = enable; | 427 | perf_hpp__column_register(&perf_hpp__format[col]); |
343 | } | 428 | } |
344 | 429 | ||
345 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) | 430 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) |
@@ -352,27 +437,29 @@ int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, | |||
352 | bool color) | 437 | bool color) |
353 | { | 438 | { |
354 | const char *sep = symbol_conf.field_sep; | 439 | const char *sep = symbol_conf.field_sep; |
440 | struct perf_hpp_fmt *fmt; | ||
355 | char *start = hpp->buf; | 441 | char *start = hpp->buf; |
356 | int i, ret; | 442 | int ret; |
357 | bool first = true; | 443 | bool first = true; |
358 | 444 | ||
359 | if (symbol_conf.exclude_other && !he->parent) | 445 | if (symbol_conf.exclude_other && !he->parent) |
360 | return 0; | 446 | return 0; |
361 | 447 | ||
362 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | 448 | perf_hpp__for_each_format(fmt) { |
363 | if (!perf_hpp__format[i].cond) | 449 | /* |
364 | continue; | 450 | * If there's no field_sep, we still need |
365 | 451 | * to display initial ' '. | |
452 | */ | ||
366 | if (!sep || !first) { | 453 | if (!sep || !first) { |
367 | ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); | 454 | ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); |
368 | advance_hpp(hpp, ret); | 455 | advance_hpp(hpp, ret); |
456 | } else | ||
369 | first = false; | 457 | first = false; |
370 | } | ||
371 | 458 | ||
372 | if (color && perf_hpp__format[i].color) | 459 | if (color && fmt->color) |
373 | ret = perf_hpp__format[i].color(hpp, he); | 460 | ret = fmt->color(hpp, he); |
374 | else | 461 | else |
375 | ret = perf_hpp__format[i].entry(hpp, he); | 462 | ret = fmt->entry(hpp, he); |
376 | 463 | ||
377 | advance_hpp(hpp, ret); | 464 | advance_hpp(hpp, ret); |
378 | } | 465 | } |
@@ -404,16 +491,18 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, | |||
404 | */ | 491 | */ |
405 | unsigned int hists__sort_list_width(struct hists *hists) | 492 | unsigned int hists__sort_list_width(struct hists *hists) |
406 | { | 493 | { |
494 | struct perf_hpp_fmt *fmt; | ||
407 | struct sort_entry *se; | 495 | struct sort_entry *se; |
408 | int i, ret = 0; | 496 | int i = 0, ret = 0; |
497 | struct perf_hpp dummy_hpp = { | ||
498 | .ptr = hists_to_evsel(hists), | ||
499 | }; | ||
409 | 500 | ||
410 | for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { | 501 | perf_hpp__for_each_format(fmt) { |
411 | if (!perf_hpp__format[i].cond) | ||
412 | continue; | ||
413 | if (i) | 502 | if (i) |
414 | ret += 2; | 503 | ret += 2; |
415 | 504 | ||
416 | ret += perf_hpp__format[i].width(NULL); | 505 | ret += fmt->width(&dummy_hpp); |
417 | } | 506 | } |
418 | 507 | ||
419 | list_for_each_entry(se, &hist_entry__sort_list, list) | 508 | list_for_each_entry(se, &hist_entry__sort_list, list) |
diff --git a/tools/perf/ui/keysyms.h b/tools/perf/ui/keysyms.h index 809eca5707fa..65092d576b4e 100644 --- a/tools/perf/ui/keysyms.h +++ b/tools/perf/ui/keysyms.h | |||
@@ -23,5 +23,6 @@ | |||
23 | #define K_TIMER -1 | 23 | #define K_TIMER -1 |
24 | #define K_ERROR -2 | 24 | #define K_ERROR -2 |
25 | #define K_RESIZE -3 | 25 | #define K_RESIZE -3 |
26 | #define K_SWITCH_INPUT_DATA -4 | ||
26 | 27 | ||
27 | #endif /* _PERF_KEYSYMS_H_ */ | 28 | #endif /* _PERF_KEYSYMS_H_ */ |
diff --git a/tools/perf/ui/progress.c b/tools/perf/ui/progress.c index 13aa64e50e11..3ec695607a4d 100644 --- a/tools/perf/ui/progress.c +++ b/tools/perf/ui/progress.c | |||
@@ -1,32 +1,26 @@ | |||
1 | #include "../cache.h" | 1 | #include "../cache.h" |
2 | #include "progress.h" | 2 | #include "progress.h" |
3 | #include "libslang.h" | ||
4 | #include "ui.h" | ||
5 | #include "browser.h" | ||
6 | 3 | ||
7 | void ui_progress__update(u64 curr, u64 total, const char *title) | 4 | static void nop_progress_update(u64 curr __maybe_unused, |
5 | u64 total __maybe_unused, | ||
6 | const char *title __maybe_unused) | ||
8 | { | 7 | { |
9 | int bar, y; | 8 | } |
10 | /* | ||
11 | * FIXME: We should have a per UI backend way of showing progress, | ||
12 | * stdio will just show a percentage as NN%, etc. | ||
13 | */ | ||
14 | if (use_browser <= 0) | ||
15 | return; | ||
16 | 9 | ||
17 | if (total == 0) | 10 | static struct ui_progress default_progress_fns = |
18 | return; | 11 | { |
12 | .update = nop_progress_update, | ||
13 | }; | ||
19 | 14 | ||
20 | ui__refresh_dimensions(true); | 15 | struct ui_progress *progress_fns = &default_progress_fns; |
21 | pthread_mutex_lock(&ui__lock); | 16 | |
22 | y = SLtt_Screen_Rows / 2 - 2; | 17 | void ui_progress__update(u64 curr, u64 total, const char *title) |
23 | SLsmg_set_color(0); | 18 | { |
24 | SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); | 19 | return progress_fns->update(curr, total, title); |
25 | SLsmg_gotorc(y++, 1); | 20 | } |
26 | SLsmg_write_string((char *)title); | 21 | |
27 | SLsmg_set_color(HE_COLORSET_SELECTED); | 22 | void ui_progress__finish(void) |
28 | bar = ((SLtt_Screen_Cols - 2) * curr) / total; | 23 | { |
29 | SLsmg_fill_region(y, 1, 1, bar, ' '); | 24 | if (progress_fns->finish) |
30 | SLsmg_refresh(); | 25 | progress_fns->finish(); |
31 | pthread_mutex_unlock(&ui__lock); | ||
32 | } | 26 | } |
diff --git a/tools/perf/ui/progress.h b/tools/perf/ui/progress.h index d9c205b59aa1..257cc224f9cf 100644 --- a/tools/perf/ui/progress.h +++ b/tools/perf/ui/progress.h | |||
@@ -3,6 +3,16 @@ | |||
3 | 3 | ||
4 | #include <../types.h> | 4 | #include <../types.h> |
5 | 5 | ||
6 | struct ui_progress { | ||
7 | void (*update)(u64, u64, const char *); | ||
8 | void (*finish)(void); | ||
9 | }; | ||
10 | |||
11 | extern struct ui_progress *progress_fns; | ||
12 | |||
13 | void ui_progress__init(void); | ||
14 | |||
6 | void ui_progress__update(u64 curr, u64 total, const char *title); | 15 | void ui_progress__update(u64 curr, u64 total, const char *title); |
16 | void ui_progress__finish(void); | ||
7 | 17 | ||
8 | #endif | 18 | #endif |
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index ebb4cc107876..ae6a789cb0f6 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c | |||
@@ -8,7 +8,7 @@ pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; | |||
8 | 8 | ||
9 | void setup_browser(bool fallback_to_pager) | 9 | void setup_browser(bool fallback_to_pager) |
10 | { | 10 | { |
11 | if (!isatty(1) || dump_trace) | 11 | if (use_browser < 2 && (!isatty(1) || dump_trace)) |
12 | use_browser = 0; | 12 | use_browser = 0; |
13 | 13 | ||
14 | /* default to TUI */ | 14 | /* default to TUI */ |
@@ -30,6 +30,7 @@ void setup_browser(bool fallback_to_pager) | |||
30 | if (fallback_to_pager) | 30 | if (fallback_to_pager) |
31 | setup_pager(); | 31 | setup_pager(); |
32 | 32 | ||
33 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
33 | perf_hpp__init(); | 34 | perf_hpp__init(); |
34 | break; | 35 | break; |
35 | } | 36 | } |
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index fbd4e32d0743..ff1f60cf442e 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include "../../util/util.h" | 3 | #include "../../util/util.h" |
4 | #include "../../util/hist.h" | 4 | #include "../../util/hist.h" |
5 | #include "../../util/sort.h" | 5 | #include "../../util/sort.h" |
6 | #include "../../util/evsel.h" | ||
6 | 7 | ||
7 | 8 | ||
8 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | 9 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) |
@@ -335,17 +336,19 @@ static int hist_entry__fprintf(struct hist_entry *he, size_t size, | |||
335 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | 336 | size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, |
336 | int max_cols, FILE *fp) | 337 | int max_cols, FILE *fp) |
337 | { | 338 | { |
339 | struct perf_hpp_fmt *fmt; | ||
338 | struct sort_entry *se; | 340 | struct sort_entry *se; |
339 | struct rb_node *nd; | 341 | struct rb_node *nd; |
340 | size_t ret = 0; | 342 | size_t ret = 0; |
341 | unsigned int width; | 343 | unsigned int width; |
342 | const char *sep = symbol_conf.field_sep; | 344 | const char *sep = symbol_conf.field_sep; |
343 | const char *col_width = symbol_conf.col_width_list_str; | 345 | const char *col_width = symbol_conf.col_width_list_str; |
344 | int idx, nr_rows = 0; | 346 | int nr_rows = 0; |
345 | char bf[64]; | 347 | char bf[96]; |
346 | struct perf_hpp dummy_hpp = { | 348 | struct perf_hpp dummy_hpp = { |
347 | .buf = bf, | 349 | .buf = bf, |
348 | .size = sizeof(bf), | 350 | .size = sizeof(bf), |
351 | .ptr = hists_to_evsel(hists), | ||
349 | }; | 352 | }; |
350 | bool first = true; | 353 | bool first = true; |
351 | 354 | ||
@@ -355,16 +358,14 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
355 | goto print_entries; | 358 | goto print_entries; |
356 | 359 | ||
357 | fprintf(fp, "# "); | 360 | fprintf(fp, "# "); |
358 | for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { | ||
359 | if (!perf_hpp__format[idx].cond) | ||
360 | continue; | ||
361 | 361 | ||
362 | perf_hpp__for_each_format(fmt) { | ||
362 | if (!first) | 363 | if (!first) |
363 | fprintf(fp, "%s", sep ?: " "); | 364 | fprintf(fp, "%s", sep ?: " "); |
364 | else | 365 | else |
365 | first = false; | 366 | first = false; |
366 | 367 | ||
367 | perf_hpp__format[idx].header(&dummy_hpp); | 368 | fmt->header(&dummy_hpp); |
368 | fprintf(fp, "%s", bf); | 369 | fprintf(fp, "%s", bf); |
369 | } | 370 | } |
370 | 371 | ||
@@ -400,18 +401,16 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
400 | first = true; | 401 | first = true; |
401 | 402 | ||
402 | fprintf(fp, "# "); | 403 | fprintf(fp, "# "); |
403 | for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) { | ||
404 | unsigned int i; | ||
405 | 404 | ||
406 | if (!perf_hpp__format[idx].cond) | 405 | perf_hpp__for_each_format(fmt) { |
407 | continue; | 406 | unsigned int i; |
408 | 407 | ||
409 | if (!first) | 408 | if (!first) |
410 | fprintf(fp, "%s", sep ?: " "); | 409 | fprintf(fp, "%s", sep ?: " "); |
411 | else | 410 | else |
412 | first = false; | 411 | first = false; |
413 | 412 | ||
414 | width = perf_hpp__format[idx].width(&dummy_hpp); | 413 | width = fmt->width(&dummy_hpp); |
415 | for (i = 0; i < width; i++) | 414 | for (i = 0; i < width; i++) |
416 | fprintf(fp, "."); | 415 | fprintf(fp, "."); |
417 | } | 416 | } |
@@ -462,7 +461,7 @@ out: | |||
462 | return ret; | 461 | return ret; |
463 | } | 462 | } |
464 | 463 | ||
465 | size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) | 464 | size_t events_stats__fprintf(struct events_stats *stats, FILE *fp) |
466 | { | 465 | { |
467 | int i; | 466 | int i; |
468 | size_t ret = 0; | 467 | size_t ret = 0; |
@@ -470,7 +469,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) | |||
470 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { | 469 | for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { |
471 | const char *name; | 470 | const char *name; |
472 | 471 | ||
473 | if (hists->stats.nr_events[i] == 0) | 472 | if (stats->nr_events[i] == 0) |
474 | continue; | 473 | continue; |
475 | 474 | ||
476 | name = perf_event__name(i); | 475 | name = perf_event__name(i); |
@@ -478,7 +477,7 @@ size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp) | |||
478 | continue; | 477 | continue; |
479 | 478 | ||
480 | ret += fprintf(fp, "%16s events: %10d\n", name, | 479 | ret += fprintf(fp, "%16s events: %10d\n", name, |
481 | hists->stats.nr_events[i]); | 480 | stats->nr_events[i]); |
482 | } | 481 | } |
483 | 482 | ||
484 | return ret; | 483 | return ret; |
diff --git a/tools/perf/ui/tui/helpline.c b/tools/perf/ui/tui/helpline.c index 2884d2f41e33..1c8b9afd5d6e 100644 --- a/tools/perf/ui/tui/helpline.c +++ b/tools/perf/ui/tui/helpline.c | |||
@@ -8,6 +8,8 @@ | |||
8 | #include "../ui.h" | 8 | #include "../ui.h" |
9 | #include "../libslang.h" | 9 | #include "../libslang.h" |
10 | 10 | ||
11 | char ui_helpline__last_msg[1024]; | ||
12 | |||
11 | static void tui_helpline__pop(void) | 13 | static void tui_helpline__pop(void) |
12 | { | 14 | { |
13 | } | 15 | } |
@@ -23,20 +25,7 @@ static void tui_helpline__push(const char *msg) | |||
23 | strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0'; | 25 | strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0'; |
24 | } | 26 | } |
25 | 27 | ||
26 | struct ui_helpline tui_helpline_fns = { | 28 | static int tui_helpline__show(const char *format, va_list ap) |
27 | .pop = tui_helpline__pop, | ||
28 | .push = tui_helpline__push, | ||
29 | }; | ||
30 | |||
31 | void ui_helpline__init(void) | ||
32 | { | ||
33 | helpline_fns = &tui_helpline_fns; | ||
34 | ui_helpline__puts(" "); | ||
35 | } | ||
36 | |||
37 | char ui_helpline__last_msg[1024]; | ||
38 | |||
39 | int ui_helpline__show_help(const char *format, va_list ap) | ||
40 | { | 29 | { |
41 | int ret; | 30 | int ret; |
42 | static int backlog; | 31 | static int backlog; |
@@ -55,3 +44,15 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
55 | 44 | ||
56 | return ret; | 45 | return ret; |
57 | } | 46 | } |
47 | |||
48 | struct ui_helpline tui_helpline_fns = { | ||
49 | .pop = tui_helpline__pop, | ||
50 | .push = tui_helpline__push, | ||
51 | .show = tui_helpline__show, | ||
52 | }; | ||
53 | |||
54 | void ui_helpline__init(void) | ||
55 | { | ||
56 | helpline_fns = &tui_helpline_fns; | ||
57 | ui_helpline__puts(" "); | ||
58 | } | ||
diff --git a/tools/perf/ui/tui/progress.c b/tools/perf/ui/tui/progress.c new file mode 100644 index 000000000000..6c2184d53cbf --- /dev/null +++ b/tools/perf/ui/tui/progress.c | |||
@@ -0,0 +1,42 @@ | |||
1 | #include "../cache.h" | ||
2 | #include "../progress.h" | ||
3 | #include "../libslang.h" | ||
4 | #include "../ui.h" | ||
5 | #include "../browser.h" | ||
6 | |||
7 | static void tui_progress__update(u64 curr, u64 total, const char *title) | ||
8 | { | ||
9 | int bar, y; | ||
10 | /* | ||
11 | * FIXME: We should have a per UI backend way of showing progress, | ||
12 | * stdio will just show a percentage as NN%, etc. | ||
13 | */ | ||
14 | if (use_browser <= 0) | ||
15 | return; | ||
16 | |||
17 | if (total == 0) | ||
18 | return; | ||
19 | |||
20 | ui__refresh_dimensions(true); | ||
21 | pthread_mutex_lock(&ui__lock); | ||
22 | y = SLtt_Screen_Rows / 2 - 2; | ||
23 | SLsmg_set_color(0); | ||
24 | SLsmg_draw_box(y, 0, 3, SLtt_Screen_Cols); | ||
25 | SLsmg_gotorc(y++, 1); | ||
26 | SLsmg_write_string((char *)title); | ||
27 | SLsmg_set_color(HE_COLORSET_SELECTED); | ||
28 | bar = ((SLtt_Screen_Cols - 2) * curr) / total; | ||
29 | SLsmg_fill_region(y, 1, 1, bar, ' '); | ||
30 | SLsmg_refresh(); | ||
31 | pthread_mutex_unlock(&ui__lock); | ||
32 | } | ||
33 | |||
34 | static struct ui_progress tui_progress_fns = | ||
35 | { | ||
36 | .update = tui_progress__update, | ||
37 | }; | ||
38 | |||
39 | void ui_progress__init(void) | ||
40 | { | ||
41 | progress_fns = &tui_progress_fns; | ||
42 | } | ||
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c index 60debb81537a..81efa192e86c 100644 --- a/tools/perf/ui/tui/setup.c +++ b/tools/perf/ui/tui/setup.c | |||
@@ -118,6 +118,7 @@ int ui__init(void) | |||
118 | newtSetSuspendCallback(newt_suspend, NULL); | 118 | newtSetSuspendCallback(newt_suspend, NULL); |
119 | ui_helpline__init(); | 119 | ui_helpline__init(); |
120 | ui_browser__init(); | 120 | ui_browser__init(); |
121 | ui_progress__init(); | ||
121 | 122 | ||
122 | signal(SIGSEGV, ui__signal); | 123 | signal(SIGSEGV, ui__signal); |
123 | signal(SIGFPE, ui__signal); | 124 | signal(SIGFPE, ui__signal); |
diff --git a/tools/perf/ui/ui.h b/tools/perf/ui/ui.h index 7b67045479f6..d86359c99907 100644 --- a/tools/perf/ui/ui.h +++ b/tools/perf/ui/ui.h | |||
@@ -3,9 +3,37 @@ | |||
3 | 3 | ||
4 | #include <pthread.h> | 4 | #include <pthread.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <linux/compiler.h> | ||
6 | 7 | ||
7 | extern pthread_mutex_t ui__lock; | 8 | extern pthread_mutex_t ui__lock; |
8 | 9 | ||
10 | extern int use_browser; | ||
11 | |||
12 | void setup_browser(bool fallback_to_pager); | ||
13 | void exit_browser(bool wait_for_ok); | ||
14 | |||
15 | #ifdef NEWT_SUPPORT | ||
16 | int ui__init(void); | ||
17 | void ui__exit(bool wait_for_ok); | ||
18 | #else | ||
19 | static inline int ui__init(void) | ||
20 | { | ||
21 | return -1; | ||
22 | } | ||
23 | static inline void ui__exit(bool wait_for_ok __maybe_unused) {} | ||
24 | #endif | ||
25 | |||
26 | #ifdef GTK2_SUPPORT | ||
27 | int perf_gtk__init(void); | ||
28 | void perf_gtk__exit(bool wait_for_ok); | ||
29 | #else | ||
30 | static inline int perf_gtk__init(void) | ||
31 | { | ||
32 | return -1; | ||
33 | } | ||
34 | static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} | ||
35 | #endif | ||
36 | |||
9 | void ui__refresh_dimensions(bool force); | 37 | void ui__refresh_dimensions(bool force); |
10 | 38 | ||
11 | #endif /* _PERF_UI_H_ */ | 39 | #endif /* _PERF_UI_H_ */ |
diff --git a/tools/perf/ui/util.c b/tools/perf/ui/util.c index 4f989774c8c6..e3e0a963d03a 100644 --- a/tools/perf/ui/util.c +++ b/tools/perf/ui/util.c | |||
@@ -52,7 +52,6 @@ int ui__warning(const char *format, ...) | |||
52 | return ret; | 52 | return ret; |
53 | } | 53 | } |
54 | 54 | ||
55 | |||
56 | /** | 55 | /** |
57 | * perf_error__register - Register error logging functions | 56 | * perf_error__register - Register error logging functions |
58 | * @eops: The pointer to error logging function struct | 57 | * @eops: The pointer to error logging function struct |
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN index 95264f304179..055fef34b6f6 100755 --- a/tools/perf/util/PERF-VERSION-GEN +++ b/tools/perf/util/PERF-VERSION-GEN | |||
@@ -9,18 +9,14 @@ GVF=${OUTPUT}PERF-VERSION-FILE | |||
9 | LF=' | 9 | LF=' |
10 | ' | 10 | ' |
11 | 11 | ||
12 | # | ||
12 | # First check if there is a .git to get the version from git describe | 13 | # First check if there is a .git to get the version from git describe |
13 | # otherwise try to get the version from the kernel makefile | 14 | # otherwise try to get the version from the kernel Makefile |
15 | # | ||
14 | if test -d ../../.git -o -f ../../.git && | 16 | if test -d ../../.git -o -f ../../.git && |
15 | VN=$(git describe --match 'v[0-9].[0-9]*' --abbrev=4 HEAD 2>/dev/null) && | 17 | VN=$(git tag 2>/dev/null | tail -1 | grep -E "v[0-9].[0-9]*") |
16 | case "$VN" in | ||
17 | *$LF*) (exit 1) ;; | ||
18 | v[0-9]*) | ||
19 | git update-index -q --refresh | ||
20 | test -z "$(git diff-index --name-only HEAD --)" || | ||
21 | VN="$VN-dirty" ;; | ||
22 | esac | ||
23 | then | 18 | then |
19 | VN=$(echo $VN"-g"$(git log -1 --abbrev=4 --pretty=format:"%h" HEAD)) | ||
24 | VN=$(echo "$VN" | sed -e 's/-/./g'); | 20 | VN=$(echo "$VN" | sed -e 's/-/./g'); |
25 | else | 21 | else |
26 | VN=$(MAKEFLAGS= make -sC ../.. kernelversion) | 22 | VN=$(MAKEFLAGS= make -sC ../.. kernelversion) |
@@ -30,13 +26,13 @@ VN=$(expr "$VN" : v*'\(.*\)') | |||
30 | 26 | ||
31 | if test -r $GVF | 27 | if test -r $GVF |
32 | then | 28 | then |
33 | VC=$(sed -e 's/^PERF_VERSION = //' <$GVF) | 29 | VC=$(sed -e 's/^#define PERF_VERSION "\(.*\)"/\1/' <$GVF) |
34 | else | 30 | else |
35 | VC=unset | 31 | VC=unset |
36 | fi | 32 | fi |
37 | test "$VN" = "$VC" || { | 33 | test "$VN" = "$VC" || { |
38 | echo >&2 "PERF_VERSION = $VN" | 34 | echo >&2 "PERF_VERSION = $VN" |
39 | echo "PERF_VERSION = $VN" >$GVF | 35 | echo "#define PERF_VERSION \"$VN\"" >$GVF |
40 | } | 36 | } |
41 | 37 | ||
42 | 38 | ||
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index f0a910371377..d33fe937e6f1 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include "debug.h" | 15 | #include "debug.h" |
16 | #include "annotate.h" | 16 | #include "annotate.h" |
17 | #include <pthread.h> | 17 | #include <pthread.h> |
18 | #include <linux/bitops.h> | ||
18 | 19 | ||
19 | const char *disassembler_style; | 20 | const char *disassembler_style; |
20 | const char *objdump_path; | 21 | const char *objdump_path; |
@@ -170,15 +171,15 @@ static int lock__parse(struct ins_operands *ops) | |||
170 | if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) | 171 | if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) |
171 | goto out_free_ops; | 172 | goto out_free_ops; |
172 | 173 | ||
173 | ops->locked.ins = ins__find(name); | 174 | ops->locked.ins = ins__find(name); |
174 | if (ops->locked.ins == NULL) | 175 | if (ops->locked.ins == NULL) |
175 | goto out_free_ops; | 176 | goto out_free_ops; |
176 | 177 | ||
177 | if (!ops->locked.ins->ops) | 178 | if (!ops->locked.ins->ops) |
178 | return 0; | 179 | return 0; |
179 | 180 | ||
180 | if (ops->locked.ins->ops->parse) | 181 | if (ops->locked.ins->ops->parse) |
181 | ops->locked.ins->ops->parse(ops->locked.ops); | 182 | ops->locked.ins->ops->parse(ops->locked.ops); |
182 | 183 | ||
183 | return 0; | 184 | return 0; |
184 | 185 | ||
@@ -400,6 +401,8 @@ static struct ins instructions[] = { | |||
400 | { .name = "testb", .ops = &mov_ops, }, | 401 | { .name = "testb", .ops = &mov_ops, }, |
401 | { .name = "testl", .ops = &mov_ops, }, | 402 | { .name = "testl", .ops = &mov_ops, }, |
402 | { .name = "xadd", .ops = &mov_ops, }, | 403 | { .name = "xadd", .ops = &mov_ops, }, |
404 | { .name = "xbeginl", .ops = &jump_ops, }, | ||
405 | { .name = "xbeginq", .ops = &jump_ops, }, | ||
403 | }; | 406 | }; |
404 | 407 | ||
405 | static int ins__cmp(const void *name, const void *insp) | 408 | static int ins__cmp(const void *name, const void *insp) |
@@ -806,7 +809,7 @@ fallback: | |||
806 | pr_err("Can't annotate %s:\n\n" | 809 | pr_err("Can't annotate %s:\n\n" |
807 | "No vmlinux file%s\nwas found in the path.\n\n" | 810 | "No vmlinux file%s\nwas found in the path.\n\n" |
808 | "Please use:\n\n" | 811 | "Please use:\n\n" |
809 | " perf buildid-cache -av vmlinux\n\n" | 812 | " perf buildid-cache -vu vmlinux\n\n" |
810 | "or:\n\n" | 813 | "or:\n\n" |
811 | " --vmlinux vmlinux\n", | 814 | " --vmlinux vmlinux\n", |
812 | sym->name, build_id_msg ?: ""); | 815 | sym->name, build_id_msg ?: ""); |
@@ -855,21 +858,68 @@ static void insert_source_line(struct rb_root *root, struct source_line *src_lin | |||
855 | struct source_line *iter; | 858 | struct source_line *iter; |
856 | struct rb_node **p = &root->rb_node; | 859 | struct rb_node **p = &root->rb_node; |
857 | struct rb_node *parent = NULL; | 860 | struct rb_node *parent = NULL; |
861 | int ret; | ||
858 | 862 | ||
859 | while (*p != NULL) { | 863 | while (*p != NULL) { |
860 | parent = *p; | 864 | parent = *p; |
861 | iter = rb_entry(parent, struct source_line, node); | 865 | iter = rb_entry(parent, struct source_line, node); |
862 | 866 | ||
863 | if (src_line->percent > iter->percent) | 867 | ret = strcmp(iter->path, src_line->path); |
868 | if (ret == 0) { | ||
869 | iter->percent_sum += src_line->percent; | ||
870 | return; | ||
871 | } | ||
872 | |||
873 | if (ret < 0) | ||
864 | p = &(*p)->rb_left; | 874 | p = &(*p)->rb_left; |
865 | else | 875 | else |
866 | p = &(*p)->rb_right; | 876 | p = &(*p)->rb_right; |
867 | } | 877 | } |
868 | 878 | ||
879 | src_line->percent_sum = src_line->percent; | ||
880 | |||
869 | rb_link_node(&src_line->node, parent, p); | 881 | rb_link_node(&src_line->node, parent, p); |
870 | rb_insert_color(&src_line->node, root); | 882 | rb_insert_color(&src_line->node, root); |
871 | } | 883 | } |
872 | 884 | ||
885 | static void __resort_source_line(struct rb_root *root, struct source_line *src_line) | ||
886 | { | ||
887 | struct source_line *iter; | ||
888 | struct rb_node **p = &root->rb_node; | ||
889 | struct rb_node *parent = NULL; | ||
890 | |||
891 | while (*p != NULL) { | ||
892 | parent = *p; | ||
893 | iter = rb_entry(parent, struct source_line, node); | ||
894 | |||
895 | if (src_line->percent_sum > iter->percent_sum) | ||
896 | p = &(*p)->rb_left; | ||
897 | else | ||
898 | p = &(*p)->rb_right; | ||
899 | } | ||
900 | |||
901 | rb_link_node(&src_line->node, parent, p); | ||
902 | rb_insert_color(&src_line->node, root); | ||
903 | } | ||
904 | |||
905 | static void resort_source_line(struct rb_root *dest_root, struct rb_root *src_root) | ||
906 | { | ||
907 | struct source_line *src_line; | ||
908 | struct rb_node *node; | ||
909 | |||
910 | node = rb_first(src_root); | ||
911 | while (node) { | ||
912 | struct rb_node *next; | ||
913 | |||
914 | src_line = rb_entry(node, struct source_line, node); | ||
915 | next = rb_next(node); | ||
916 | rb_erase(node, src_root); | ||
917 | |||
918 | __resort_source_line(dest_root, src_line); | ||
919 | node = next; | ||
920 | } | ||
921 | } | ||
922 | |||
873 | static void symbol__free_source_line(struct symbol *sym, int len) | 923 | static void symbol__free_source_line(struct symbol *sym, int len) |
874 | { | 924 | { |
875 | struct annotation *notes = symbol__annotation(sym); | 925 | struct annotation *notes = symbol__annotation(sym); |
@@ -894,6 +944,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
894 | struct source_line *src_line; | 944 | struct source_line *src_line; |
895 | struct annotation *notes = symbol__annotation(sym); | 945 | struct annotation *notes = symbol__annotation(sym); |
896 | struct sym_hist *h = annotation__histogram(notes, evidx); | 946 | struct sym_hist *h = annotation__histogram(notes, evidx); |
947 | struct rb_root tmp_root = RB_ROOT; | ||
897 | 948 | ||
898 | if (!h->sum) | 949 | if (!h->sum) |
899 | return 0; | 950 | return 0; |
@@ -928,12 +979,13 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
928 | goto next; | 979 | goto next; |
929 | 980 | ||
930 | strcpy(src_line[i].path, path); | 981 | strcpy(src_line[i].path, path); |
931 | insert_source_line(root, &src_line[i]); | 982 | insert_source_line(&tmp_root, &src_line[i]); |
932 | 983 | ||
933 | next: | 984 | next: |
934 | pclose(fp); | 985 | pclose(fp); |
935 | } | 986 | } |
936 | 987 | ||
988 | resort_source_line(root, &tmp_root); | ||
937 | return 0; | 989 | return 0; |
938 | } | 990 | } |
939 | 991 | ||
@@ -957,7 +1009,7 @@ static void print_summary(struct rb_root *root, const char *filename) | |||
957 | char *path; | 1009 | char *path; |
958 | 1010 | ||
959 | src_line = rb_entry(node, struct source_line, node); | 1011 | src_line = rb_entry(node, struct source_line, node); |
960 | percent = src_line->percent; | 1012 | percent = src_line->percent_sum; |
961 | color = get_percent_color(percent); | 1013 | color = get_percent_color(percent); |
962 | path = src_line->path; | 1014 | path = src_line->path; |
963 | 1015 | ||
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 39242dcee8f2..c422440fe611 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <stdint.h> | 5 | #include <stdint.h> |
6 | #include "types.h" | 6 | #include "types.h" |
7 | #include "symbol.h" | 7 | #include "symbol.h" |
8 | #include "hist.h" | ||
9 | #include "sort.h" | ||
8 | #include <linux/list.h> | 10 | #include <linux/list.h> |
9 | #include <linux/rbtree.h> | 11 | #include <linux/rbtree.h> |
10 | #include <pthread.h> | 12 | #include <pthread.h> |
@@ -75,6 +77,7 @@ struct sym_hist { | |||
75 | struct source_line { | 77 | struct source_line { |
76 | struct rb_node node; | 78 | struct rb_node node; |
77 | double percent; | 79 | double percent; |
80 | double percent_sum; | ||
78 | char *path; | 81 | char *path; |
79 | }; | 82 | }; |
80 | 83 | ||
@@ -140,20 +143,41 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx, | |||
140 | 143 | ||
141 | #ifdef NEWT_SUPPORT | 144 | #ifdef NEWT_SUPPORT |
142 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, | 145 | int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, |
143 | void(*timer)(void *arg), void *arg, int delay_secs); | 146 | struct hist_browser_timer *hbt); |
144 | #else | 147 | #else |
145 | static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, | 148 | static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused, |
146 | struct map *map __maybe_unused, | 149 | struct map *map __maybe_unused, |
147 | int evidx __maybe_unused, | 150 | int evidx __maybe_unused, |
148 | void(*timer)(void *arg) __maybe_unused, | 151 | struct hist_browser_timer *hbt |
149 | void *arg __maybe_unused, | 152 | __maybe_unused) |
150 | int delay_secs __maybe_unused) | ||
151 | { | 153 | { |
152 | return 0; | 154 | return 0; |
153 | } | 155 | } |
154 | #endif | 156 | #endif |
155 | 157 | ||
158 | #ifdef GTK2_SUPPORT | ||
159 | int symbol__gtk_annotate(struct symbol *sym, struct map *map, int evidx, | ||
160 | struct hist_browser_timer *hbt); | ||
161 | |||
162 | static inline int hist_entry__gtk_annotate(struct hist_entry *he, int evidx, | ||
163 | struct hist_browser_timer *hbt) | ||
164 | { | ||
165 | return symbol__gtk_annotate(he->ms.sym, he->ms.map, evidx, hbt); | ||
166 | } | ||
167 | |||
168 | void perf_gtk__show_annotations(void); | ||
169 | #else | ||
170 | static inline int hist_entry__gtk_annotate(struct hist_entry *he __maybe_unused, | ||
171 | int evidx __maybe_unused, | ||
172 | struct hist_browser_timer *hbt | ||
173 | __maybe_unused) | ||
174 | { | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static inline void perf_gtk__show_annotations(void) {} | ||
179 | #endif | ||
180 | |||
156 | extern const char *disassembler_style; | 181 | extern const char *disassembler_style; |
157 | extern const char *objdump_path; | ||
158 | 182 | ||
159 | #endif /* __PERF_ANNOTATE_H */ | 183 | #endif /* __PERF_ANNOTATE_H */ |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 8e3a740ddbd4..5295625c0c00 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -16,11 +16,11 @@ | |||
16 | #include "session.h" | 16 | #include "session.h" |
17 | #include "tool.h" | 17 | #include "tool.h" |
18 | 18 | ||
19 | static int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | 19 | int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, |
20 | union perf_event *event, | 20 | union perf_event *event, |
21 | struct perf_sample *sample __maybe_unused, | 21 | struct perf_sample *sample __maybe_unused, |
22 | struct perf_evsel *evsel __maybe_unused, | 22 | struct perf_evsel *evsel __maybe_unused, |
23 | struct machine *machine) | 23 | struct machine *machine) |
24 | { | 24 | { |
25 | struct addr_location al; | 25 | struct addr_location al; |
26 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 26 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
@@ -64,12 +64,27 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, | |||
64 | struct perf_tool build_id__mark_dso_hit_ops = { | 64 | struct perf_tool build_id__mark_dso_hit_ops = { |
65 | .sample = build_id__mark_dso_hit, | 65 | .sample = build_id__mark_dso_hit, |
66 | .mmap = perf_event__process_mmap, | 66 | .mmap = perf_event__process_mmap, |
67 | .fork = perf_event__process_task, | 67 | .fork = perf_event__process_fork, |
68 | .exit = perf_event__exit_del_thread, | 68 | .exit = perf_event__exit_del_thread, |
69 | .attr = perf_event__process_attr, | 69 | .attr = perf_event__process_attr, |
70 | .build_id = perf_event__process_build_id, | 70 | .build_id = perf_event__process_build_id, |
71 | }; | 71 | }; |
72 | 72 | ||
73 | int build_id__sprintf(const u8 *build_id, int len, char *bf) | ||
74 | { | ||
75 | char *bid = bf; | ||
76 | const u8 *raw = build_id; | ||
77 | int i; | ||
78 | |||
79 | for (i = 0; i < len; ++i) { | ||
80 | sprintf(bid, "%02x", *raw); | ||
81 | ++raw; | ||
82 | bid += 2; | ||
83 | } | ||
84 | |||
85 | return raw - build_id; | ||
86 | } | ||
87 | |||
73 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) | 88 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) |
74 | { | 89 | { |
75 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 90 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index a993ba87d996..a811f5c62e18 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h | |||
@@ -1,10 +1,19 @@ | |||
1 | #ifndef PERF_BUILD_ID_H_ | 1 | #ifndef PERF_BUILD_ID_H_ |
2 | #define PERF_BUILD_ID_H_ 1 | 2 | #define PERF_BUILD_ID_H_ 1 |
3 | 3 | ||
4 | #include "session.h" | 4 | #define BUILD_ID_SIZE 20 |
5 | |||
6 | #include "tool.h" | ||
7 | #include "types.h" | ||
5 | 8 | ||
6 | extern struct perf_tool build_id__mark_dso_hit_ops; | 9 | extern struct perf_tool build_id__mark_dso_hit_ops; |
10 | struct dso; | ||
7 | 11 | ||
12 | int build_id__sprintf(const u8 *build_id, int len, char *bf); | ||
8 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size); | 13 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size); |
9 | 14 | ||
15 | int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, | ||
16 | struct perf_sample *sample, struct perf_evsel *evsel, | ||
17 | struct machine *machine); | ||
18 | |||
10 | #endif | 19 | #endif |
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 2bd51370ad28..26e367239873 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "util.h" | 5 | #include "util.h" |
6 | #include "strbuf.h" | 6 | #include "strbuf.h" |
7 | #include "../perf.h" | 7 | #include "../perf.h" |
8 | #include "../ui/ui.h" | ||
8 | 9 | ||
9 | #define CMD_EXEC_PATH "--exec-path" | 10 | #define CMD_EXEC_PATH "--exec-path" |
10 | #define CMD_PERF_DIR "--perf-dir=" | 11 | #define CMD_PERF_DIR "--perf-dir=" |
@@ -31,44 +32,6 @@ extern const char *pager_program; | |||
31 | extern int pager_in_use(void); | 32 | extern int pager_in_use(void); |
32 | extern int pager_use_color; | 33 | extern int pager_use_color; |
33 | 34 | ||
34 | extern int use_browser; | ||
35 | |||
36 | #if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT) | ||
37 | void setup_browser(bool fallback_to_pager); | ||
38 | void exit_browser(bool wait_for_ok); | ||
39 | |||
40 | #ifdef NEWT_SUPPORT | ||
41 | int ui__init(void); | ||
42 | void ui__exit(bool wait_for_ok); | ||
43 | #else | ||
44 | static inline int ui__init(void) | ||
45 | { | ||
46 | return -1; | ||
47 | } | ||
48 | static inline void ui__exit(bool wait_for_ok __maybe_unused) {} | ||
49 | #endif | ||
50 | |||
51 | #ifdef GTK2_SUPPORT | ||
52 | int perf_gtk__init(void); | ||
53 | void perf_gtk__exit(bool wait_for_ok); | ||
54 | #else | ||
55 | static inline int perf_gtk__init(void) | ||
56 | { | ||
57 | return -1; | ||
58 | } | ||
59 | static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {} | ||
60 | #endif | ||
61 | |||
62 | #else /* NEWT_SUPPORT || GTK2_SUPPORT */ | ||
63 | |||
64 | static inline void setup_browser(bool fallback_to_pager) | ||
65 | { | ||
66 | if (fallback_to_pager) | ||
67 | setup_pager(); | ||
68 | } | ||
69 | static inline void exit_browser(bool wait_for_ok __maybe_unused) {} | ||
70 | #endif /* NEWT_SUPPORT || GTK2_SUPPORT */ | ||
71 | |||
72 | char *alias_lookup(const char *alias); | 35 | char *alias_lookup(const char *alias); |
73 | int split_cmdline(char *cmdline, const char ***argv); | 36 | int split_cmdline(char *cmdline, const char ***argv); |
74 | 37 | ||
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index d3b3f5d82137..42b6a632fe7b 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -444,7 +444,7 @@ int callchain_cursor_append(struct callchain_cursor *cursor, | |||
444 | struct callchain_cursor_node *node = *cursor->last; | 444 | struct callchain_cursor_node *node = *cursor->last; |
445 | 445 | ||
446 | if (!node) { | 446 | if (!node) { |
447 | node = calloc(sizeof(*node), 1); | 447 | node = calloc(1, sizeof(*node)); |
448 | if (!node) | 448 | if (!node) |
449 | return -ENOMEM; | 449 | return -ENOMEM; |
450 | 450 | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index eb340571e7d6..3ee9f67d5af0 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -143,4 +143,9 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | |||
143 | cursor->curr = cursor->curr->next; | 143 | cursor->curr = cursor->curr->next; |
144 | cursor->pos++; | 144 | cursor->pos++; |
145 | } | 145 | } |
146 | |||
147 | struct option; | ||
148 | |||
149 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); | ||
150 | extern const char record_callchain_help[]; | ||
146 | #endif /* __PERF_CALLCHAIN_H */ | 151 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 2b32ffa9ebdb..f817046e22b1 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include "util.h" | 1 | #include "util.h" |
2 | #include "sysfs.h" | ||
2 | #include "../perf.h" | 3 | #include "../perf.h" |
3 | #include "cpumap.h" | 4 | #include "cpumap.h" |
4 | #include <assert.h> | 5 | #include <assert.h> |
@@ -201,3 +202,56 @@ void cpu_map__delete(struct cpu_map *map) | |||
201 | { | 202 | { |
202 | free(map); | 203 | free(map); |
203 | } | 204 | } |
205 | |||
206 | int cpu_map__get_socket(struct cpu_map *map, int idx) | ||
207 | { | ||
208 | FILE *fp; | ||
209 | const char *mnt; | ||
210 | char path[PATH_MAX]; | ||
211 | int cpu, ret; | ||
212 | |||
213 | if (idx > map->nr) | ||
214 | return -1; | ||
215 | |||
216 | cpu = map->map[idx]; | ||
217 | |||
218 | mnt = sysfs_find_mountpoint(); | ||
219 | if (!mnt) | ||
220 | return -1; | ||
221 | |||
222 | sprintf(path, | ||
223 | "%s/devices/system/cpu/cpu%d/topology/physical_package_id", | ||
224 | mnt, cpu); | ||
225 | |||
226 | fp = fopen(path, "r"); | ||
227 | if (!fp) | ||
228 | return -1; | ||
229 | ret = fscanf(fp, "%d", &cpu); | ||
230 | fclose(fp); | ||
231 | return ret == 1 ? cpu : -1; | ||
232 | } | ||
233 | |||
234 | int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp) | ||
235 | { | ||
236 | struct cpu_map *sock; | ||
237 | int nr = cpus->nr; | ||
238 | int cpu, s1, s2; | ||
239 | |||
240 | sock = calloc(1, sizeof(*sock) + nr * sizeof(int)); | ||
241 | if (!sock) | ||
242 | return -1; | ||
243 | |||
244 | for (cpu = 0; cpu < nr; cpu++) { | ||
245 | s1 = cpu_map__get_socket(cpus, cpu); | ||
246 | for (s2 = 0; s2 < sock->nr; s2++) { | ||
247 | if (s1 == sock->map[s2]) | ||
248 | break; | ||
249 | } | ||
250 | if (s2 == sock->nr) { | ||
251 | sock->map[sock->nr] = s1; | ||
252 | sock->nr++; | ||
253 | } | ||
254 | } | ||
255 | *sockp = sock; | ||
256 | return 0; | ||
257 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 2f68a3b8c285..161b00756a12 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -14,6 +14,15 @@ struct cpu_map *cpu_map__dummy_new(void); | |||
14 | void cpu_map__delete(struct cpu_map *map); | 14 | void cpu_map__delete(struct cpu_map *map); |
15 | struct cpu_map *cpu_map__read(FILE *file); | 15 | struct cpu_map *cpu_map__read(FILE *file); |
16 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); | 16 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); |
17 | int cpu_map__get_socket(struct cpu_map *map, int idx); | ||
18 | int cpu_map__build_socket_map(struct cpu_map *cpus, struct cpu_map **sockp); | ||
19 | |||
20 | static inline int cpu_map__socket(struct cpu_map *sock, int s) | ||
21 | { | ||
22 | if (!sock || s > sock->nr || s < 0) | ||
23 | return 0; | ||
24 | return sock->map[s]; | ||
25 | } | ||
17 | 26 | ||
18 | static inline int cpu_map__nr(const struct cpu_map *map) | 27 | static inline int cpu_map__nr(const struct cpu_map *map) |
19 | { | 28 | { |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 03f830b48148..399e74c34c1a 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -23,10 +23,8 @@ int eprintf(int level, const char *fmt, ...) | |||
23 | 23 | ||
24 | if (verbose >= level) { | 24 | if (verbose >= level) { |
25 | va_start(args, fmt); | 25 | va_start(args, fmt); |
26 | if (use_browser == 1) | 26 | if (use_browser >= 1) |
27 | ret = ui_helpline__show_help(fmt, args); | 27 | ui_helpline__vshow(fmt, args); |
28 | else if (use_browser == 2) | ||
29 | ret = perf_gtk__show_helpline(fmt, args); | ||
30 | else | 28 | else |
31 | ret = vfprintf(stderr, fmt, args); | 29 | ret = vfprintf(stderr, fmt, args); |
32 | va_end(args); | 30 | va_end(args); |
@@ -49,28 +47,6 @@ int dump_printf(const char *fmt, ...) | |||
49 | return ret; | 47 | return ret; |
50 | } | 48 | } |
51 | 49 | ||
52 | #if !defined(NEWT_SUPPORT) && !defined(GTK2_SUPPORT) | ||
53 | int ui__warning(const char *format, ...) | ||
54 | { | ||
55 | va_list args; | ||
56 | |||
57 | va_start(args, format); | ||
58 | vfprintf(stderr, format, args); | ||
59 | va_end(args); | ||
60 | return 0; | ||
61 | } | ||
62 | #endif | ||
63 | |||
64 | int ui__error_paranoid(void) | ||
65 | { | ||
66 | return ui__error("Permission error - are you root?\n" | ||
67 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" | ||
68 | " -1 - Not paranoid at all\n" | ||
69 | " 0 - Disallow raw tracepoint access for unpriv\n" | ||
70 | " 1 - Disallow cpu events for unpriv\n" | ||
71 | " 2 - Disallow kernel profiling for unpriv\n"); | ||
72 | } | ||
73 | |||
74 | void trace_event(union perf_event *event) | 50 | void trace_event(union perf_event *event) |
75 | { | 51 | { |
76 | unsigned char *raw_event = (void *)event; | 52 | unsigned char *raw_event = (void *)event; |
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index dec98750b484..efbd98805ad0 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include "event.h" | 6 | #include "event.h" |
7 | #include "../ui/helpline.h" | 7 | #include "../ui/helpline.h" |
8 | #include "../ui/progress.h" | ||
9 | #include "../ui/util.h" | ||
8 | 10 | ||
9 | extern int verbose; | 11 | extern int verbose; |
10 | extern bool quiet, dump_trace; | 12 | extern bool quiet, dump_trace; |
@@ -12,38 +14,7 @@ extern bool quiet, dump_trace; | |||
12 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 14 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
13 | void trace_event(union perf_event *event); | 15 | void trace_event(union perf_event *event); |
14 | 16 | ||
15 | struct ui_progress; | ||
16 | struct perf_error_ops; | ||
17 | |||
18 | #if defined(NEWT_SUPPORT) || defined(GTK2_SUPPORT) | ||
19 | |||
20 | #include "../ui/progress.h" | ||
21 | int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); | 17 | int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); |
22 | #include "../ui/util.h" | ||
23 | |||
24 | #else | ||
25 | |||
26 | static inline void ui_progress__update(u64 curr __maybe_unused, | ||
27 | u64 total __maybe_unused, | ||
28 | const char *title __maybe_unused) {} | ||
29 | |||
30 | #define ui__error(format, arg...) ui__warning(format, ##arg) | ||
31 | |||
32 | static inline int | ||
33 | perf_error__register(struct perf_error_ops *eops __maybe_unused) | ||
34 | { | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | static inline int | ||
39 | perf_error__unregister(struct perf_error_ops *eops __maybe_unused) | ||
40 | { | ||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | #endif /* NEWT_SUPPORT || GTK2_SUPPORT */ | ||
45 | |||
46 | int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); | 18 | int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); |
47 | int ui__error_paranoid(void); | ||
48 | 19 | ||
49 | #endif /* __PERF_DEBUG_H */ | 20 | #endif /* __PERF_DEBUG_H */ |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c new file mode 100644 index 000000000000..6f7d5a9d6b05 --- /dev/null +++ b/tools/perf/util/dso.c | |||
@@ -0,0 +1,595 @@ | |||
1 | #include "symbol.h" | ||
2 | #include "dso.h" | ||
3 | #include "machine.h" | ||
4 | #include "util.h" | ||
5 | #include "debug.h" | ||
6 | |||
7 | char dso__symtab_origin(const struct dso *dso) | ||
8 | { | ||
9 | static const char origin[] = { | ||
10 | [DSO_BINARY_TYPE__KALLSYMS] = 'k', | ||
11 | [DSO_BINARY_TYPE__VMLINUX] = 'v', | ||
12 | [DSO_BINARY_TYPE__JAVA_JIT] = 'j', | ||
13 | [DSO_BINARY_TYPE__DEBUGLINK] = 'l', | ||
14 | [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', | ||
15 | [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', | ||
16 | [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', | ||
17 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', | ||
18 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', | ||
19 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', | ||
20 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', | ||
21 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', | ||
22 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | ||
23 | }; | ||
24 | |||
25 | if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) | ||
26 | return '!'; | ||
27 | return origin[dso->symtab_type]; | ||
28 | } | ||
29 | |||
30 | int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | ||
31 | char *root_dir, char *file, size_t size) | ||
32 | { | ||
33 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
34 | int ret = 0; | ||
35 | |||
36 | switch (type) { | ||
37 | case DSO_BINARY_TYPE__DEBUGLINK: { | ||
38 | char *debuglink; | ||
39 | |||
40 | strncpy(file, dso->long_name, size); | ||
41 | debuglink = file + dso->long_name_len; | ||
42 | while (debuglink != file && *debuglink != '/') | ||
43 | debuglink--; | ||
44 | if (*debuglink == '/') | ||
45 | debuglink++; | ||
46 | filename__read_debuglink(dso->long_name, debuglink, | ||
47 | size - (debuglink - file)); | ||
48 | } | ||
49 | break; | ||
50 | case DSO_BINARY_TYPE__BUILD_ID_CACHE: | ||
51 | /* skip the locally configured cache if a symfs is given */ | ||
52 | if (symbol_conf.symfs[0] || | ||
53 | (dso__build_id_filename(dso, file, size) == NULL)) | ||
54 | ret = -1; | ||
55 | break; | ||
56 | |||
57 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: | ||
58 | snprintf(file, size, "%s/usr/lib/debug%s.debug", | ||
59 | symbol_conf.symfs, dso->long_name); | ||
60 | break; | ||
61 | |||
62 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: | ||
63 | snprintf(file, size, "%s/usr/lib/debug%s", | ||
64 | symbol_conf.symfs, dso->long_name); | ||
65 | break; | ||
66 | |||
67 | case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: | ||
68 | if (!dso->has_build_id) { | ||
69 | ret = -1; | ||
70 | break; | ||
71 | } | ||
72 | |||
73 | build_id__sprintf(dso->build_id, | ||
74 | sizeof(dso->build_id), | ||
75 | build_id_hex); | ||
76 | snprintf(file, size, | ||
77 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", | ||
78 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | ||
79 | break; | ||
80 | |||
81 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: | ||
82 | snprintf(file, size, "%s%s", | ||
83 | symbol_conf.symfs, dso->long_name); | ||
84 | break; | ||
85 | |||
86 | case DSO_BINARY_TYPE__GUEST_KMODULE: | ||
87 | snprintf(file, size, "%s%s%s", symbol_conf.symfs, | ||
88 | root_dir, dso->long_name); | ||
89 | break; | ||
90 | |||
91 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | ||
92 | snprintf(file, size, "%s%s", symbol_conf.symfs, | ||
93 | dso->long_name); | ||
94 | break; | ||
95 | |||
96 | default: | ||
97 | case DSO_BINARY_TYPE__KALLSYMS: | ||
98 | case DSO_BINARY_TYPE__VMLINUX: | ||
99 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: | ||
100 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | ||
101 | case DSO_BINARY_TYPE__JAVA_JIT: | ||
102 | case DSO_BINARY_TYPE__NOT_FOUND: | ||
103 | ret = -1; | ||
104 | break; | ||
105 | } | ||
106 | |||
107 | return ret; | ||
108 | } | ||
109 | |||
110 | static int open_dso(struct dso *dso, struct machine *machine) | ||
111 | { | ||
112 | char *root_dir = (char *) ""; | ||
113 | char *name; | ||
114 | int fd; | ||
115 | |||
116 | name = malloc(PATH_MAX); | ||
117 | if (!name) | ||
118 | return -ENOMEM; | ||
119 | |||
120 | if (machine) | ||
121 | root_dir = machine->root_dir; | ||
122 | |||
123 | if (dso__binary_type_file(dso, dso->data_type, | ||
124 | root_dir, name, PATH_MAX)) { | ||
125 | free(name); | ||
126 | return -EINVAL; | ||
127 | } | ||
128 | |||
129 | fd = open(name, O_RDONLY); | ||
130 | free(name); | ||
131 | return fd; | ||
132 | } | ||
133 | |||
134 | int dso__data_fd(struct dso *dso, struct machine *machine) | ||
135 | { | ||
136 | static enum dso_binary_type binary_type_data[] = { | ||
137 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | ||
138 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | ||
139 | DSO_BINARY_TYPE__NOT_FOUND, | ||
140 | }; | ||
141 | int i = 0; | ||
142 | |||
143 | if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) | ||
144 | return open_dso(dso, machine); | ||
145 | |||
146 | do { | ||
147 | int fd; | ||
148 | |||
149 | dso->data_type = binary_type_data[i++]; | ||
150 | |||
151 | fd = open_dso(dso, machine); | ||
152 | if (fd >= 0) | ||
153 | return fd; | ||
154 | |||
155 | } while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); | ||
156 | |||
157 | return -EINVAL; | ||
158 | } | ||
159 | |||
160 | static void | ||
161 | dso_cache__free(struct rb_root *root) | ||
162 | { | ||
163 | struct rb_node *next = rb_first(root); | ||
164 | |||
165 | while (next) { | ||
166 | struct dso_cache *cache; | ||
167 | |||
168 | cache = rb_entry(next, struct dso_cache, rb_node); | ||
169 | next = rb_next(&cache->rb_node); | ||
170 | rb_erase(&cache->rb_node, root); | ||
171 | free(cache); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | static struct dso_cache* | ||
176 | dso_cache__find(struct rb_root *root, u64 offset) | ||
177 | { | ||
178 | struct rb_node **p = &root->rb_node; | ||
179 | struct rb_node *parent = NULL; | ||
180 | struct dso_cache *cache; | ||
181 | |||
182 | while (*p != NULL) { | ||
183 | u64 end; | ||
184 | |||
185 | parent = *p; | ||
186 | cache = rb_entry(parent, struct dso_cache, rb_node); | ||
187 | end = cache->offset + DSO__DATA_CACHE_SIZE; | ||
188 | |||
189 | if (offset < cache->offset) | ||
190 | p = &(*p)->rb_left; | ||
191 | else if (offset >= end) | ||
192 | p = &(*p)->rb_right; | ||
193 | else | ||
194 | return cache; | ||
195 | } | ||
196 | return NULL; | ||
197 | } | ||
198 | |||
199 | static void | ||
200 | dso_cache__insert(struct rb_root *root, struct dso_cache *new) | ||
201 | { | ||
202 | struct rb_node **p = &root->rb_node; | ||
203 | struct rb_node *parent = NULL; | ||
204 | struct dso_cache *cache; | ||
205 | u64 offset = new->offset; | ||
206 | |||
207 | while (*p != NULL) { | ||
208 | u64 end; | ||
209 | |||
210 | parent = *p; | ||
211 | cache = rb_entry(parent, struct dso_cache, rb_node); | ||
212 | end = cache->offset + DSO__DATA_CACHE_SIZE; | ||
213 | |||
214 | if (offset < cache->offset) | ||
215 | p = &(*p)->rb_left; | ||
216 | else if (offset >= end) | ||
217 | p = &(*p)->rb_right; | ||
218 | } | ||
219 | |||
220 | rb_link_node(&new->rb_node, parent, p); | ||
221 | rb_insert_color(&new->rb_node, root); | ||
222 | } | ||
223 | |||
224 | static ssize_t | ||
225 | dso_cache__memcpy(struct dso_cache *cache, u64 offset, | ||
226 | u8 *data, u64 size) | ||
227 | { | ||
228 | u64 cache_offset = offset - cache->offset; | ||
229 | u64 cache_size = min(cache->size - cache_offset, size); | ||
230 | |||
231 | memcpy(data, cache->data + cache_offset, cache_size); | ||
232 | return cache_size; | ||
233 | } | ||
234 | |||
235 | static ssize_t | ||
236 | dso_cache__read(struct dso *dso, struct machine *machine, | ||
237 | u64 offset, u8 *data, ssize_t size) | ||
238 | { | ||
239 | struct dso_cache *cache; | ||
240 | ssize_t ret; | ||
241 | int fd; | ||
242 | |||
243 | fd = dso__data_fd(dso, machine); | ||
244 | if (fd < 0) | ||
245 | return -1; | ||
246 | |||
247 | do { | ||
248 | u64 cache_offset; | ||
249 | |||
250 | ret = -ENOMEM; | ||
251 | |||
252 | cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); | ||
253 | if (!cache) | ||
254 | break; | ||
255 | |||
256 | cache_offset = offset & DSO__DATA_CACHE_MASK; | ||
257 | ret = -EINVAL; | ||
258 | |||
259 | if (-1 == lseek(fd, cache_offset, SEEK_SET)) | ||
260 | break; | ||
261 | |||
262 | ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); | ||
263 | if (ret <= 0) | ||
264 | break; | ||
265 | |||
266 | cache->offset = cache_offset; | ||
267 | cache->size = ret; | ||
268 | dso_cache__insert(&dso->cache, cache); | ||
269 | |||
270 | ret = dso_cache__memcpy(cache, offset, data, size); | ||
271 | |||
272 | } while (0); | ||
273 | |||
274 | if (ret <= 0) | ||
275 | free(cache); | ||
276 | |||
277 | close(fd); | ||
278 | return ret; | ||
279 | } | ||
280 | |||
281 | static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, | ||
282 | u64 offset, u8 *data, ssize_t size) | ||
283 | { | ||
284 | struct dso_cache *cache; | ||
285 | |||
286 | cache = dso_cache__find(&dso->cache, offset); | ||
287 | if (cache) | ||
288 | return dso_cache__memcpy(cache, offset, data, size); | ||
289 | else | ||
290 | return dso_cache__read(dso, machine, offset, data, size); | ||
291 | } | ||
292 | |||
293 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
294 | u64 offset, u8 *data, ssize_t size) | ||
295 | { | ||
296 | ssize_t r = 0; | ||
297 | u8 *p = data; | ||
298 | |||
299 | do { | ||
300 | ssize_t ret; | ||
301 | |||
302 | ret = dso_cache_read(dso, machine, offset, p, size); | ||
303 | if (ret < 0) | ||
304 | return ret; | ||
305 | |||
306 | /* Reached EOF, return what we have. */ | ||
307 | if (!ret) | ||
308 | break; | ||
309 | |||
310 | BUG_ON(ret > size); | ||
311 | |||
312 | r += ret; | ||
313 | p += ret; | ||
314 | offset += ret; | ||
315 | size -= ret; | ||
316 | |||
317 | } while (size); | ||
318 | |||
319 | return r; | ||
320 | } | ||
321 | |||
322 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | ||
323 | struct machine *machine, u64 addr, | ||
324 | u8 *data, ssize_t size) | ||
325 | { | ||
326 | u64 offset = map->map_ip(map, addr); | ||
327 | return dso__data_read_offset(dso, machine, offset, data, size); | ||
328 | } | ||
329 | |||
330 | struct map *dso__new_map(const char *name) | ||
331 | { | ||
332 | struct map *map = NULL; | ||
333 | struct dso *dso = dso__new(name); | ||
334 | |||
335 | if (dso) | ||
336 | map = map__new2(0, dso, MAP__FUNCTION); | ||
337 | |||
338 | return map; | ||
339 | } | ||
340 | |||
341 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | ||
342 | const char *short_name, int dso_type) | ||
343 | { | ||
344 | /* | ||
345 | * The kernel dso could be created by build_id processing. | ||
346 | */ | ||
347 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name); | ||
348 | |||
349 | /* | ||
350 | * We need to run this in all cases, since during the build_id | ||
351 | * processing we had no idea this was the kernel dso. | ||
352 | */ | ||
353 | if (dso != NULL) { | ||
354 | dso__set_short_name(dso, short_name); | ||
355 | dso->kernel = dso_type; | ||
356 | } | ||
357 | |||
358 | return dso; | ||
359 | } | ||
360 | |||
361 | void dso__set_long_name(struct dso *dso, char *name) | ||
362 | { | ||
363 | if (name == NULL) | ||
364 | return; | ||
365 | dso->long_name = name; | ||
366 | dso->long_name_len = strlen(name); | ||
367 | } | ||
368 | |||
369 | void dso__set_short_name(struct dso *dso, const char *name) | ||
370 | { | ||
371 | if (name == NULL) | ||
372 | return; | ||
373 | dso->short_name = name; | ||
374 | dso->short_name_len = strlen(name); | ||
375 | } | ||
376 | |||
377 | static void dso__set_basename(struct dso *dso) | ||
378 | { | ||
379 | dso__set_short_name(dso, basename(dso->long_name)); | ||
380 | } | ||
381 | |||
382 | int dso__name_len(const struct dso *dso) | ||
383 | { | ||
384 | if (!dso) | ||
385 | return strlen("[unknown]"); | ||
386 | if (verbose) | ||
387 | return dso->long_name_len; | ||
388 | |||
389 | return dso->short_name_len; | ||
390 | } | ||
391 | |||
392 | bool dso__loaded(const struct dso *dso, enum map_type type) | ||
393 | { | ||
394 | return dso->loaded & (1 << type); | ||
395 | } | ||
396 | |||
397 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type) | ||
398 | { | ||
399 | return dso->sorted_by_name & (1 << type); | ||
400 | } | ||
401 | |||
402 | void dso__set_sorted_by_name(struct dso *dso, enum map_type type) | ||
403 | { | ||
404 | dso->sorted_by_name |= (1 << type); | ||
405 | } | ||
406 | |||
407 | struct dso *dso__new(const char *name) | ||
408 | { | ||
409 | struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1); | ||
410 | |||
411 | if (dso != NULL) { | ||
412 | int i; | ||
413 | strcpy(dso->name, name); | ||
414 | dso__set_long_name(dso, dso->name); | ||
415 | dso__set_short_name(dso, dso->name); | ||
416 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
417 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; | ||
418 | dso->cache = RB_ROOT; | ||
419 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; | ||
420 | dso->data_type = DSO_BINARY_TYPE__NOT_FOUND; | ||
421 | dso->loaded = 0; | ||
422 | dso->sorted_by_name = 0; | ||
423 | dso->has_build_id = 0; | ||
424 | dso->kernel = DSO_TYPE_USER; | ||
425 | dso->needs_swap = DSO_SWAP__UNSET; | ||
426 | INIT_LIST_HEAD(&dso->node); | ||
427 | } | ||
428 | |||
429 | return dso; | ||
430 | } | ||
431 | |||
432 | void dso__delete(struct dso *dso) | ||
433 | { | ||
434 | int i; | ||
435 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
436 | symbols__delete(&dso->symbols[i]); | ||
437 | if (dso->sname_alloc) | ||
438 | free((char *)dso->short_name); | ||
439 | if (dso->lname_alloc) | ||
440 | free(dso->long_name); | ||
441 | dso_cache__free(&dso->cache); | ||
442 | free(dso); | ||
443 | } | ||
444 | |||
445 | void dso__set_build_id(struct dso *dso, void *build_id) | ||
446 | { | ||
447 | memcpy(dso->build_id, build_id, sizeof(dso->build_id)); | ||
448 | dso->has_build_id = 1; | ||
449 | } | ||
450 | |||
451 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id) | ||
452 | { | ||
453 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; | ||
454 | } | ||
455 | |||
456 | void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) | ||
457 | { | ||
458 | char path[PATH_MAX]; | ||
459 | |||
460 | if (machine__is_default_guest(machine)) | ||
461 | return; | ||
462 | sprintf(path, "%s/sys/kernel/notes", machine->root_dir); | ||
463 | if (sysfs__read_build_id(path, dso->build_id, | ||
464 | sizeof(dso->build_id)) == 0) | ||
465 | dso->has_build_id = true; | ||
466 | } | ||
467 | |||
468 | int dso__kernel_module_get_build_id(struct dso *dso, | ||
469 | const char *root_dir) | ||
470 | { | ||
471 | char filename[PATH_MAX]; | ||
472 | /* | ||
473 | * kernel module short names are of the form "[module]" and | ||
474 | * we need just "module" here. | ||
475 | */ | ||
476 | const char *name = dso->short_name + 1; | ||
477 | |||
478 | snprintf(filename, sizeof(filename), | ||
479 | "%s/sys/module/%.*s/notes/.note.gnu.build-id", | ||
480 | root_dir, (int)strlen(name) - 1, name); | ||
481 | |||
482 | if (sysfs__read_build_id(filename, dso->build_id, | ||
483 | sizeof(dso->build_id)) == 0) | ||
484 | dso->has_build_id = true; | ||
485 | |||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | ||
490 | { | ||
491 | bool have_build_id = false; | ||
492 | struct dso *pos; | ||
493 | |||
494 | list_for_each_entry(pos, head, node) { | ||
495 | if (with_hits && !pos->hit) | ||
496 | continue; | ||
497 | if (pos->has_build_id) { | ||
498 | have_build_id = true; | ||
499 | continue; | ||
500 | } | ||
501 | if (filename__read_build_id(pos->long_name, pos->build_id, | ||
502 | sizeof(pos->build_id)) > 0) { | ||
503 | have_build_id = true; | ||
504 | pos->has_build_id = true; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | return have_build_id; | ||
509 | } | ||
510 | |||
511 | void dsos__add(struct list_head *head, struct dso *dso) | ||
512 | { | ||
513 | list_add_tail(&dso->node, head); | ||
514 | } | ||
515 | |||
516 | struct dso *dsos__find(struct list_head *head, const char *name) | ||
517 | { | ||
518 | struct dso *pos; | ||
519 | |||
520 | list_for_each_entry(pos, head, node) | ||
521 | if (strcmp(pos->long_name, name) == 0) | ||
522 | return pos; | ||
523 | return NULL; | ||
524 | } | ||
525 | |||
526 | struct dso *__dsos__findnew(struct list_head *head, const char *name) | ||
527 | { | ||
528 | struct dso *dso = dsos__find(head, name); | ||
529 | |||
530 | if (!dso) { | ||
531 | dso = dso__new(name); | ||
532 | if (dso != NULL) { | ||
533 | dsos__add(head, dso); | ||
534 | dso__set_basename(dso); | ||
535 | } | ||
536 | } | ||
537 | |||
538 | return dso; | ||
539 | } | ||
540 | |||
541 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | ||
542 | bool (skip)(struct dso *dso, int parm), int parm) | ||
543 | { | ||
544 | struct dso *pos; | ||
545 | size_t ret = 0; | ||
546 | |||
547 | list_for_each_entry(pos, head, node) { | ||
548 | if (skip && skip(pos, parm)) | ||
549 | continue; | ||
550 | ret += dso__fprintf_buildid(pos, fp); | ||
551 | ret += fprintf(fp, " %s\n", pos->long_name); | ||
552 | } | ||
553 | return ret; | ||
554 | } | ||
555 | |||
556 | size_t __dsos__fprintf(struct list_head *head, FILE *fp) | ||
557 | { | ||
558 | struct dso *pos; | ||
559 | size_t ret = 0; | ||
560 | |||
561 | list_for_each_entry(pos, head, node) { | ||
562 | int i; | ||
563 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
564 | ret += dso__fprintf(pos, i, fp); | ||
565 | } | ||
566 | |||
567 | return ret; | ||
568 | } | ||
569 | |||
570 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) | ||
571 | { | ||
572 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
573 | |||
574 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | ||
575 | return fprintf(fp, "%s", sbuild_id); | ||
576 | } | ||
577 | |||
578 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp) | ||
579 | { | ||
580 | struct rb_node *nd; | ||
581 | size_t ret = fprintf(fp, "dso: %s (", dso->short_name); | ||
582 | |||
583 | if (dso->short_name != dso->long_name) | ||
584 | ret += fprintf(fp, "%s, ", dso->long_name); | ||
585 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], | ||
586 | dso__loaded(dso, type) ? "" : "NOT "); | ||
587 | ret += dso__fprintf_buildid(dso, fp); | ||
588 | ret += fprintf(fp, ")\n"); | ||
589 | for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) { | ||
590 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
591 | ret += symbol__fprintf(pos, fp); | ||
592 | } | ||
593 | |||
594 | return ret; | ||
595 | } | ||
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h new file mode 100644 index 000000000000..450199ab51b5 --- /dev/null +++ b/tools/perf/util/dso.h | |||
@@ -0,0 +1,148 @@ | |||
1 | #ifndef __PERF_DSO | ||
2 | #define __PERF_DSO | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | #include <linux/rbtree.h> | ||
6 | #include "types.h" | ||
7 | #include "map.h" | ||
8 | |||
9 | enum dso_binary_type { | ||
10 | DSO_BINARY_TYPE__KALLSYMS = 0, | ||
11 | DSO_BINARY_TYPE__GUEST_KALLSYMS, | ||
12 | DSO_BINARY_TYPE__VMLINUX, | ||
13 | DSO_BINARY_TYPE__GUEST_VMLINUX, | ||
14 | DSO_BINARY_TYPE__JAVA_JIT, | ||
15 | DSO_BINARY_TYPE__DEBUGLINK, | ||
16 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | ||
17 | DSO_BINARY_TYPE__FEDORA_DEBUGINFO, | ||
18 | DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, | ||
19 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | ||
20 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | ||
21 | DSO_BINARY_TYPE__GUEST_KMODULE, | ||
22 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | ||
23 | DSO_BINARY_TYPE__NOT_FOUND, | ||
24 | }; | ||
25 | |||
26 | enum dso_kernel_type { | ||
27 | DSO_TYPE_USER = 0, | ||
28 | DSO_TYPE_KERNEL, | ||
29 | DSO_TYPE_GUEST_KERNEL | ||
30 | }; | ||
31 | |||
32 | enum dso_swap_type { | ||
33 | DSO_SWAP__UNSET, | ||
34 | DSO_SWAP__NO, | ||
35 | DSO_SWAP__YES, | ||
36 | }; | ||
37 | |||
38 | #define DSO__SWAP(dso, type, val) \ | ||
39 | ({ \ | ||
40 | type ____r = val; \ | ||
41 | BUG_ON(dso->needs_swap == DSO_SWAP__UNSET); \ | ||
42 | if (dso->needs_swap == DSO_SWAP__YES) { \ | ||
43 | switch (sizeof(____r)) { \ | ||
44 | case 2: \ | ||
45 | ____r = bswap_16(val); \ | ||
46 | break; \ | ||
47 | case 4: \ | ||
48 | ____r = bswap_32(val); \ | ||
49 | break; \ | ||
50 | case 8: \ | ||
51 | ____r = bswap_64(val); \ | ||
52 | break; \ | ||
53 | default: \ | ||
54 | BUG_ON(1); \ | ||
55 | } \ | ||
56 | } \ | ||
57 | ____r; \ | ||
58 | }) | ||
59 | |||
60 | #define DSO__DATA_CACHE_SIZE 4096 | ||
61 | #define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1) | ||
62 | |||
63 | struct dso_cache { | ||
64 | struct rb_node rb_node; | ||
65 | u64 offset; | ||
66 | u64 size; | ||
67 | char data[0]; | ||
68 | }; | ||
69 | |||
70 | struct dso { | ||
71 | struct list_head node; | ||
72 | struct rb_root symbols[MAP__NR_TYPES]; | ||
73 | struct rb_root symbol_names[MAP__NR_TYPES]; | ||
74 | struct rb_root cache; | ||
75 | enum dso_kernel_type kernel; | ||
76 | enum dso_swap_type needs_swap; | ||
77 | enum dso_binary_type symtab_type; | ||
78 | enum dso_binary_type data_type; | ||
79 | u8 adjust_symbols:1; | ||
80 | u8 has_build_id:1; | ||
81 | u8 hit:1; | ||
82 | u8 annotate_warned:1; | ||
83 | u8 sname_alloc:1; | ||
84 | u8 lname_alloc:1; | ||
85 | u8 sorted_by_name; | ||
86 | u8 loaded; | ||
87 | u8 build_id[BUILD_ID_SIZE]; | ||
88 | const char *short_name; | ||
89 | char *long_name; | ||
90 | u16 long_name_len; | ||
91 | u16 short_name_len; | ||
92 | char name[0]; | ||
93 | }; | ||
94 | |||
95 | static inline void dso__set_loaded(struct dso *dso, enum map_type type) | ||
96 | { | ||
97 | dso->loaded |= (1 << type); | ||
98 | } | ||
99 | |||
100 | struct dso *dso__new(const char *name); | ||
101 | void dso__delete(struct dso *dso); | ||
102 | |||
103 | void dso__set_short_name(struct dso *dso, const char *name); | ||
104 | void dso__set_long_name(struct dso *dso, char *name); | ||
105 | |||
106 | int dso__name_len(const struct dso *dso); | ||
107 | |||
108 | bool dso__loaded(const struct dso *dso, enum map_type type); | ||
109 | |||
110 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type); | ||
111 | void dso__set_sorted_by_name(struct dso *dso, enum map_type type); | ||
112 | void dso__sort_by_name(struct dso *dso, enum map_type type); | ||
113 | |||
114 | void dso__set_build_id(struct dso *dso, void *build_id); | ||
115 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id); | ||
116 | void dso__read_running_kernel_build_id(struct dso *dso, | ||
117 | struct machine *machine); | ||
118 | int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir); | ||
119 | |||
120 | char dso__symtab_origin(const struct dso *dso); | ||
121 | int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | ||
122 | char *root_dir, char *file, size_t size); | ||
123 | |||
124 | int dso__data_fd(struct dso *dso, struct machine *machine); | ||
125 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
126 | u64 offset, u8 *data, ssize_t size); | ||
127 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | ||
128 | struct machine *machine, u64 addr, | ||
129 | u8 *data, ssize_t size); | ||
130 | |||
131 | struct map *dso__new_map(const char *name); | ||
132 | struct dso *dso__kernel_findnew(struct machine *machine, const char *name, | ||
133 | const char *short_name, int dso_type); | ||
134 | |||
135 | void dsos__add(struct list_head *head, struct dso *dso); | ||
136 | struct dso *dsos__find(struct list_head *head, const char *name); | ||
137 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | ||
138 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); | ||
139 | |||
140 | size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | ||
141 | bool (skip)(struct dso *dso, int parm), int parm); | ||
142 | size_t __dsos__fprintf(struct list_head *head, FILE *fp); | ||
143 | |||
144 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp); | ||
145 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | ||
146 | enum map_type type, FILE *fp); | ||
147 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); | ||
148 | #endif /* __PERF_DSO */ | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 6715b1938725..5cd13d768cec 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -1,6 +1,7 @@ | |||
1 | #include <linux/types.h> | 1 | #include <linux/types.h> |
2 | #include "event.h" | 2 | #include "event.h" |
3 | #include "debug.h" | 3 | #include "debug.h" |
4 | #include "machine.h" | ||
4 | #include "sort.h" | 5 | #include "sort.h" |
5 | #include "string.h" | 6 | #include "string.h" |
6 | #include "strlist.h" | 7 | #include "strlist.h" |
@@ -192,55 +193,43 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
192 | event->header.misc = PERF_RECORD_MISC_USER; | 193 | event->header.misc = PERF_RECORD_MISC_USER; |
193 | 194 | ||
194 | while (1) { | 195 | while (1) { |
195 | char bf[BUFSIZ], *pbf = bf; | 196 | char bf[BUFSIZ]; |
196 | int n; | 197 | char prot[5]; |
198 | char execname[PATH_MAX]; | ||
199 | char anonstr[] = "//anon"; | ||
197 | size_t size; | 200 | size_t size; |
201 | |||
198 | if (fgets(bf, sizeof(bf), fp) == NULL) | 202 | if (fgets(bf, sizeof(bf), fp) == NULL) |
199 | break; | 203 | break; |
200 | 204 | ||
205 | /* ensure null termination since stack will be reused. */ | ||
206 | strcpy(execname, ""); | ||
207 | |||
201 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | 208 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ |
202 | n = hex2u64(pbf, &event->mmap.start); | 209 | sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n", |
203 | if (n < 0) | 210 | &event->mmap.start, &event->mmap.len, prot, |
204 | continue; | 211 | &event->mmap.pgoff, execname); |
205 | pbf += n + 1; | 212 | |
206 | n = hex2u64(pbf, &event->mmap.len); | 213 | if (prot[2] != 'x') |
207 | if (n < 0) | ||
208 | continue; | 214 | continue; |
209 | pbf += n + 3; | 215 | |
210 | if (*pbf == 'x') { /* vm_exec */ | 216 | if (!strcmp(execname, "")) |
211 | char anonstr[] = "//anon\n"; | 217 | strcpy(execname, anonstr); |
212 | char *execname = strchr(bf, '/'); | 218 | |
213 | 219 | size = strlen(execname) + 1; | |
214 | /* Catch VDSO */ | 220 | memcpy(event->mmap.filename, execname, size); |
215 | if (execname == NULL) | 221 | size = PERF_ALIGN(size, sizeof(u64)); |
216 | execname = strstr(bf, "[vdso]"); | 222 | event->mmap.len -= event->mmap.start; |
217 | 223 | event->mmap.header.size = (sizeof(event->mmap) - | |
218 | /* Catch anonymous mmaps */ | 224 | (sizeof(event->mmap.filename) - size)); |
219 | if ((execname == NULL) && !strstr(bf, "[")) | 225 | memset(event->mmap.filename + size, 0, machine->id_hdr_size); |
220 | execname = anonstr; | 226 | event->mmap.header.size += machine->id_hdr_size; |
221 | 227 | event->mmap.pid = tgid; | |
222 | if (execname == NULL) | 228 | event->mmap.tid = pid; |
223 | continue; | 229 | |
224 | 230 | if (process(tool, event, &synth_sample, machine) != 0) { | |
225 | pbf += 3; | 231 | rc = -1; |
226 | n = hex2u64(pbf, &event->mmap.pgoff); | 232 | break; |
227 | |||
228 | size = strlen(execname); | ||
229 | execname[size - 1] = '\0'; /* Remove \n */ | ||
230 | memcpy(event->mmap.filename, execname, size); | ||
231 | size = PERF_ALIGN(size, sizeof(u64)); | ||
232 | event->mmap.len -= event->mmap.start; | ||
233 | event->mmap.header.size = (sizeof(event->mmap) - | ||
234 | (sizeof(event->mmap.filename) - size)); | ||
235 | memset(event->mmap.filename + size, 0, machine->id_hdr_size); | ||
236 | event->mmap.header.size += machine->id_hdr_size; | ||
237 | event->mmap.pid = tgid; | ||
238 | event->mmap.tid = pid; | ||
239 | |||
240 | if (process(tool, event, &synth_sample, machine) != 0) { | ||
241 | rc = -1; | ||
242 | break; | ||
243 | } | ||
244 | } | 233 | } |
245 | } | 234 | } |
246 | 235 | ||
@@ -404,16 +393,15 @@ int perf_event__synthesize_threads(struct perf_tool *tool, | |||
404 | 393 | ||
405 | if (*end) /* only interested in proper numerical dirents */ | 394 | if (*end) /* only interested in proper numerical dirents */ |
406 | continue; | 395 | continue; |
407 | 396 | /* | |
408 | if (__event__synthesize_thread(comm_event, mmap_event, pid, 1, | 397 | * We may race with exiting thread, so don't stop just because |
409 | process, tool, machine) != 0) { | 398 | * one thread couldn't be synthesized. |
410 | err = -1; | 399 | */ |
411 | goto out_closedir; | 400 | __event__synthesize_thread(comm_event, mmap_event, pid, 1, |
412 | } | 401 | process, tool, machine); |
413 | } | 402 | } |
414 | 403 | ||
415 | err = 0; | 404 | err = 0; |
416 | out_closedir: | ||
417 | closedir(proc); | 405 | closedir(proc); |
418 | out_free_mmap: | 406 | out_free_mmap: |
419 | free(mmap_event); | 407 | free(mmap_event); |
@@ -488,8 +476,10 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, | |||
488 | } | 476 | } |
489 | } | 477 | } |
490 | 478 | ||
491 | if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) | 479 | if (kallsyms__parse(filename, &args, find_symbol_cb) <= 0) { |
480 | free(event); | ||
492 | return -ENOENT; | 481 | return -ENOENT; |
482 | } | ||
493 | 483 | ||
494 | map = machine->vmlinux_maps[MAP__FUNCTION]; | 484 | map = machine->vmlinux_maps[MAP__FUNCTION]; |
495 | size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), | 485 | size = snprintf(event->mmap.filename, sizeof(event->mmap.filename), |
@@ -519,134 +509,15 @@ int perf_event__process_comm(struct perf_tool *tool __maybe_unused, | |||
519 | struct perf_sample *sample __maybe_unused, | 509 | struct perf_sample *sample __maybe_unused, |
520 | struct machine *machine) | 510 | struct machine *machine) |
521 | { | 511 | { |
522 | struct thread *thread = machine__findnew_thread(machine, event->comm.tid); | 512 | return machine__process_comm_event(machine, event); |
523 | |||
524 | if (dump_trace) | ||
525 | perf_event__fprintf_comm(event, stdout); | ||
526 | |||
527 | if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { | ||
528 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
529 | return -1; | ||
530 | } | ||
531 | |||
532 | return 0; | ||
533 | } | 513 | } |
534 | 514 | ||
535 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, | 515 | int perf_event__process_lost(struct perf_tool *tool __maybe_unused, |
536 | union perf_event *event, | 516 | union perf_event *event, |
537 | struct perf_sample *sample __maybe_unused, | 517 | struct perf_sample *sample __maybe_unused, |
538 | struct machine *machine __maybe_unused) | 518 | struct machine *machine) |
539 | { | ||
540 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", | ||
541 | event->lost.id, event->lost.lost); | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | static void perf_event__set_kernel_mmap_len(union perf_event *event, | ||
546 | struct map **maps) | ||
547 | { | 519 | { |
548 | maps[MAP__FUNCTION]->start = event->mmap.start; | 520 | return machine__process_lost_event(machine, event); |
549 | maps[MAP__FUNCTION]->end = event->mmap.start + event->mmap.len; | ||
550 | /* | ||
551 | * Be a bit paranoid here, some perf.data file came with | ||
552 | * a zero sized synthesized MMAP event for the kernel. | ||
553 | */ | ||
554 | if (maps[MAP__FUNCTION]->end == 0) | ||
555 | maps[MAP__FUNCTION]->end = ~0ULL; | ||
556 | } | ||
557 | |||
558 | static int perf_event__process_kernel_mmap(struct perf_tool *tool | ||
559 | __maybe_unused, | ||
560 | union perf_event *event, | ||
561 | struct machine *machine) | ||
562 | { | ||
563 | struct map *map; | ||
564 | char kmmap_prefix[PATH_MAX]; | ||
565 | enum dso_kernel_type kernel_type; | ||
566 | bool is_kernel_mmap; | ||
567 | |||
568 | machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); | ||
569 | if (machine__is_host(machine)) | ||
570 | kernel_type = DSO_TYPE_KERNEL; | ||
571 | else | ||
572 | kernel_type = DSO_TYPE_GUEST_KERNEL; | ||
573 | |||
574 | is_kernel_mmap = memcmp(event->mmap.filename, | ||
575 | kmmap_prefix, | ||
576 | strlen(kmmap_prefix) - 1) == 0; | ||
577 | if (event->mmap.filename[0] == '/' || | ||
578 | (!is_kernel_mmap && event->mmap.filename[0] == '[')) { | ||
579 | |||
580 | char short_module_name[1024]; | ||
581 | char *name, *dot; | ||
582 | |||
583 | if (event->mmap.filename[0] == '/') { | ||
584 | name = strrchr(event->mmap.filename, '/'); | ||
585 | if (name == NULL) | ||
586 | goto out_problem; | ||
587 | |||
588 | ++name; /* skip / */ | ||
589 | dot = strrchr(name, '.'); | ||
590 | if (dot == NULL) | ||
591 | goto out_problem; | ||
592 | snprintf(short_module_name, sizeof(short_module_name), | ||
593 | "[%.*s]", (int)(dot - name), name); | ||
594 | strxfrchar(short_module_name, '-', '_'); | ||
595 | } else | ||
596 | strcpy(short_module_name, event->mmap.filename); | ||
597 | |||
598 | map = machine__new_module(machine, event->mmap.start, | ||
599 | event->mmap.filename); | ||
600 | if (map == NULL) | ||
601 | goto out_problem; | ||
602 | |||
603 | name = strdup(short_module_name); | ||
604 | if (name == NULL) | ||
605 | goto out_problem; | ||
606 | |||
607 | map->dso->short_name = name; | ||
608 | map->dso->sname_alloc = 1; | ||
609 | map->end = map->start + event->mmap.len; | ||
610 | } else if (is_kernel_mmap) { | ||
611 | const char *symbol_name = (event->mmap.filename + | ||
612 | strlen(kmmap_prefix)); | ||
613 | /* | ||
614 | * Should be there already, from the build-id table in | ||
615 | * the header. | ||
616 | */ | ||
617 | struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, | ||
618 | kmmap_prefix); | ||
619 | if (kernel == NULL) | ||
620 | goto out_problem; | ||
621 | |||
622 | kernel->kernel = kernel_type; | ||
623 | if (__machine__create_kernel_maps(machine, kernel) < 0) | ||
624 | goto out_problem; | ||
625 | |||
626 | perf_event__set_kernel_mmap_len(event, machine->vmlinux_maps); | ||
627 | |||
628 | /* | ||
629 | * Avoid using a zero address (kptr_restrict) for the ref reloc | ||
630 | * symbol. Effectively having zero here means that at record | ||
631 | * time /proc/sys/kernel/kptr_restrict was non zero. | ||
632 | */ | ||
633 | if (event->mmap.pgoff != 0) { | ||
634 | maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, | ||
635 | symbol_name, | ||
636 | event->mmap.pgoff); | ||
637 | } | ||
638 | |||
639 | if (machine__is_default_guest(machine)) { | ||
640 | /* | ||
641 | * preload dso of guest kernel and modules | ||
642 | */ | ||
643 | dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], | ||
644 | NULL); | ||
645 | } | ||
646 | } | ||
647 | return 0; | ||
648 | out_problem: | ||
649 | return -1; | ||
650 | } | 521 | } |
651 | 522 | ||
652 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | 523 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) |
@@ -656,43 +527,12 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | |||
656 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); | 527 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); |
657 | } | 528 | } |
658 | 529 | ||
659 | int perf_event__process_mmap(struct perf_tool *tool, | 530 | int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, |
660 | union perf_event *event, | 531 | union perf_event *event, |
661 | struct perf_sample *sample __maybe_unused, | 532 | struct perf_sample *sample __maybe_unused, |
662 | struct machine *machine) | 533 | struct machine *machine) |
663 | { | 534 | { |
664 | struct thread *thread; | 535 | return machine__process_mmap_event(machine, event); |
665 | struct map *map; | ||
666 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
667 | int ret = 0; | ||
668 | |||
669 | if (dump_trace) | ||
670 | perf_event__fprintf_mmap(event, stdout); | ||
671 | |||
672 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | ||
673 | cpumode == PERF_RECORD_MISC_KERNEL) { | ||
674 | ret = perf_event__process_kernel_mmap(tool, event, machine); | ||
675 | if (ret < 0) | ||
676 | goto out_problem; | ||
677 | return 0; | ||
678 | } | ||
679 | |||
680 | thread = machine__findnew_thread(machine, event->mmap.pid); | ||
681 | if (thread == NULL) | ||
682 | goto out_problem; | ||
683 | map = map__new(&machine->user_dsos, event->mmap.start, | ||
684 | event->mmap.len, event->mmap.pgoff, | ||
685 | event->mmap.pid, event->mmap.filename, | ||
686 | MAP__FUNCTION); | ||
687 | if (map == NULL) | ||
688 | goto out_problem; | ||
689 | |||
690 | thread__insert_map(thread, map); | ||
691 | return 0; | ||
692 | |||
693 | out_problem: | ||
694 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
695 | return 0; | ||
696 | } | 536 | } |
697 | 537 | ||
698 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) | 538 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) |
@@ -702,29 +542,20 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) | |||
702 | event->fork.ppid, event->fork.ptid); | 542 | event->fork.ppid, event->fork.ptid); |
703 | } | 543 | } |
704 | 544 | ||
705 | int perf_event__process_task(struct perf_tool *tool __maybe_unused, | 545 | int perf_event__process_fork(struct perf_tool *tool __maybe_unused, |
706 | union perf_event *event, | 546 | union perf_event *event, |
707 | struct perf_sample *sample __maybe_unused, | 547 | struct perf_sample *sample __maybe_unused, |
708 | struct machine *machine) | 548 | struct machine *machine) |
709 | { | 549 | { |
710 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | 550 | return machine__process_fork_event(machine, event); |
711 | struct thread *parent = machine__findnew_thread(machine, event->fork.ptid); | 551 | } |
712 | |||
713 | if (dump_trace) | ||
714 | perf_event__fprintf_task(event, stdout); | ||
715 | |||
716 | if (event->header.type == PERF_RECORD_EXIT) { | ||
717 | machine__remove_thread(machine, thread); | ||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | if (thread == NULL || parent == NULL || | ||
722 | thread__fork(thread, parent) < 0) { | ||
723 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
724 | return -1; | ||
725 | } | ||
726 | 552 | ||
727 | return 0; | 553 | int perf_event__process_exit(struct perf_tool *tool __maybe_unused, |
554 | union perf_event *event, | ||
555 | struct perf_sample *sample __maybe_unused, | ||
556 | struct machine *machine) | ||
557 | { | ||
558 | return machine__process_exit_event(machine, event); | ||
728 | } | 559 | } |
729 | 560 | ||
730 | size_t perf_event__fprintf(union perf_event *event, FILE *fp) | 561 | size_t perf_event__fprintf(union perf_event *event, FILE *fp) |
@@ -750,27 +581,12 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) | |||
750 | return ret; | 581 | return ret; |
751 | } | 582 | } |
752 | 583 | ||
753 | int perf_event__process(struct perf_tool *tool, union perf_event *event, | 584 | int perf_event__process(struct perf_tool *tool __maybe_unused, |
754 | struct perf_sample *sample, struct machine *machine) | 585 | union perf_event *event, |
586 | struct perf_sample *sample __maybe_unused, | ||
587 | struct machine *machine) | ||
755 | { | 588 | { |
756 | switch (event->header.type) { | 589 | return machine__process_event(machine, event); |
757 | case PERF_RECORD_COMM: | ||
758 | perf_event__process_comm(tool, event, sample, machine); | ||
759 | break; | ||
760 | case PERF_RECORD_MMAP: | ||
761 | perf_event__process_mmap(tool, event, sample, machine); | ||
762 | break; | ||
763 | case PERF_RECORD_FORK: | ||
764 | case PERF_RECORD_EXIT: | ||
765 | perf_event__process_task(tool, event, sample, machine); | ||
766 | break; | ||
767 | case PERF_RECORD_LOST: | ||
768 | perf_event__process_lost(tool, event, sample, machine); | ||
769 | default: | ||
770 | break; | ||
771 | } | ||
772 | |||
773 | return 0; | ||
774 | } | 590 | } |
775 | 591 | ||
776 | void thread__find_addr_map(struct thread *self, | 592 | void thread__find_addr_map(struct thread *self, |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 21b99e741a87..0d573ff4771a 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -6,6 +6,7 @@ | |||
6 | 6 | ||
7 | #include "../perf.h" | 7 | #include "../perf.h" |
8 | #include "map.h" | 8 | #include "map.h" |
9 | #include "build-id.h" | ||
9 | 10 | ||
10 | /* | 11 | /* |
11 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * | 12 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * |
@@ -96,8 +97,6 @@ struct perf_sample { | |||
96 | struct stack_dump user_stack; | 97 | struct stack_dump user_stack; |
97 | }; | 98 | }; |
98 | 99 | ||
99 | #define BUILD_ID_SIZE 20 | ||
100 | |||
101 | struct build_id_event { | 100 | struct build_id_event { |
102 | struct perf_event_header header; | 101 | struct perf_event_header header; |
103 | pid_t pid; | 102 | pid_t pid; |
@@ -191,7 +190,11 @@ int perf_event__process_mmap(struct perf_tool *tool, | |||
191 | union perf_event *event, | 190 | union perf_event *event, |
192 | struct perf_sample *sample, | 191 | struct perf_sample *sample, |
193 | struct machine *machine); | 192 | struct machine *machine); |
194 | int perf_event__process_task(struct perf_tool *tool, | 193 | int perf_event__process_fork(struct perf_tool *tool, |
194 | union perf_event *event, | ||
195 | struct perf_sample *sample, | ||
196 | struct machine *machine); | ||
197 | int perf_event__process_exit(struct perf_tool *tool, | ||
195 | union perf_event *event, | 198 | union perf_event *event, |
196 | struct perf_sample *sample, | 199 | struct perf_sample *sample, |
197 | struct machine *machine); | 200 | struct machine *machine); |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 186b87730396..c8be0fbc5145 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -49,21 +49,25 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | |||
49 | return evlist; | 49 | return evlist; |
50 | } | 50 | } |
51 | 51 | ||
52 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 52 | void perf_evlist__config(struct perf_evlist *evlist, |
53 | struct perf_record_opts *opts) | 53 | struct perf_record_opts *opts) |
54 | { | 54 | { |
55 | struct perf_evsel *evsel, *first; | 55 | struct perf_evsel *evsel; |
56 | /* | ||
57 | * Set the evsel leader links before we configure attributes, | ||
58 | * since some might depend on this info. | ||
59 | */ | ||
60 | if (opts->group) | ||
61 | perf_evlist__set_leader(evlist); | ||
56 | 62 | ||
57 | if (evlist->cpus->map[0] < 0) | 63 | if (evlist->cpus->map[0] < 0) |
58 | opts->no_inherit = true; | 64 | opts->no_inherit = true; |
59 | 65 | ||
60 | first = perf_evlist__first(evlist); | ||
61 | |||
62 | list_for_each_entry(evsel, &evlist->entries, node) { | 66 | list_for_each_entry(evsel, &evlist->entries, node) { |
63 | perf_evsel__config(evsel, opts, first); | 67 | perf_evsel__config(evsel, opts); |
64 | 68 | ||
65 | if (evlist->nr_entries > 1) | 69 | if (evlist->nr_entries > 1) |
66 | evsel->attr.sample_type |= PERF_SAMPLE_ID; | 70 | perf_evsel__set_sample_id(evsel); |
67 | } | 71 | } |
68 | } | 72 | } |
69 | 73 | ||
@@ -113,18 +117,21 @@ void __perf_evlist__set_leader(struct list_head *list) | |||
113 | struct perf_evsel *evsel, *leader; | 117 | struct perf_evsel *evsel, *leader; |
114 | 118 | ||
115 | leader = list_entry(list->next, struct perf_evsel, node); | 119 | leader = list_entry(list->next, struct perf_evsel, node); |
116 | leader->leader = NULL; | 120 | evsel = list_entry(list->prev, struct perf_evsel, node); |
121 | |||
122 | leader->nr_members = evsel->idx - leader->idx + 1; | ||
117 | 123 | ||
118 | list_for_each_entry(evsel, list, node) { | 124 | list_for_each_entry(evsel, list, node) { |
119 | if (evsel != leader) | 125 | evsel->leader = leader; |
120 | evsel->leader = leader; | ||
121 | } | 126 | } |
122 | } | 127 | } |
123 | 128 | ||
124 | void perf_evlist__set_leader(struct perf_evlist *evlist) | 129 | void perf_evlist__set_leader(struct perf_evlist *evlist) |
125 | { | 130 | { |
126 | if (evlist->nr_entries) | 131 | if (evlist->nr_entries) { |
132 | evlist->nr_groups = evlist->nr_entries > 1 ? 1 : 0; | ||
127 | __perf_evlist__set_leader(&evlist->entries); | 133 | __perf_evlist__set_leader(&evlist->entries); |
134 | } | ||
128 | } | 135 | } |
129 | 136 | ||
130 | int perf_evlist__add_default(struct perf_evlist *evlist) | 137 | int perf_evlist__add_default(struct perf_evlist *evlist) |
@@ -224,6 +231,8 @@ void perf_evlist__disable(struct perf_evlist *evlist) | |||
224 | 231 | ||
225 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | 232 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { |
226 | list_for_each_entry(pos, &evlist->entries, node) { | 233 | list_for_each_entry(pos, &evlist->entries, node) { |
234 | if (!perf_evsel__is_group_leader(pos)) | ||
235 | continue; | ||
227 | for (thread = 0; thread < evlist->threads->nr; thread++) | 236 | for (thread = 0; thread < evlist->threads->nr; thread++) |
228 | ioctl(FD(pos, cpu, thread), | 237 | ioctl(FD(pos, cpu, thread), |
229 | PERF_EVENT_IOC_DISABLE, 0); | 238 | PERF_EVENT_IOC_DISABLE, 0); |
@@ -238,6 +247,8 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
238 | 247 | ||
239 | for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { | 248 | for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) { |
240 | list_for_each_entry(pos, &evlist->entries, node) { | 249 | list_for_each_entry(pos, &evlist->entries, node) { |
250 | if (!perf_evsel__is_group_leader(pos)) | ||
251 | continue; | ||
241 | for (thread = 0; thread < evlist->threads->nr; thread++) | 252 | for (thread = 0; thread < evlist->threads->nr; thread++) |
242 | ioctl(FD(pos, cpu, thread), | 253 | ioctl(FD(pos, cpu, thread), |
243 | PERF_EVENT_IOC_ENABLE, 0); | 254 | PERF_EVENT_IOC_ENABLE, 0); |
@@ -303,7 +314,6 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist, | |||
303 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | 314 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) |
304 | { | 315 | { |
305 | struct hlist_head *head; | 316 | struct hlist_head *head; |
306 | struct hlist_node *pos; | ||
307 | struct perf_sample_id *sid; | 317 | struct perf_sample_id *sid; |
308 | int hash; | 318 | int hash; |
309 | 319 | ||
@@ -313,7 +323,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
313 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | 323 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); |
314 | head = &evlist->heads[hash]; | 324 | head = &evlist->heads[hash]; |
315 | 325 | ||
316 | hlist_for_each_entry(sid, pos, head, node) | 326 | hlist_for_each_entry(sid, head, node) |
317 | if (sid->id == id) | 327 | if (sid->id == id) |
318 | return sid->evsel; | 328 | return sid->evsel; |
319 | 329 | ||
@@ -325,8 +335,6 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
325 | 335 | ||
326 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | 336 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) |
327 | { | 337 | { |
328 | /* XXX Move this to perf.c, making it generally available */ | ||
329 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
330 | struct perf_mmap *md = &evlist->mmap[idx]; | 338 | struct perf_mmap *md = &evlist->mmap[idx]; |
331 | unsigned int head = perf_mmap__read_head(md); | 339 | unsigned int head = perf_mmap__read_head(md); |
332 | unsigned int old = md->prev; | 340 | unsigned int old = md->prev; |
@@ -366,7 +374,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
366 | if ((old & md->mask) + size != ((old + size) & md->mask)) { | 374 | if ((old & md->mask) + size != ((old + size) & md->mask)) { |
367 | unsigned int offset = old; | 375 | unsigned int offset = old; |
368 | unsigned int len = min(sizeof(*event), size), cpy; | 376 | unsigned int len = min(sizeof(*event), size), cpy; |
369 | void *dst = &evlist->event_copy; | 377 | void *dst = &md->event_copy; |
370 | 378 | ||
371 | do { | 379 | do { |
372 | cpy = min(md->mask + 1 - (offset & md->mask), len); | 380 | cpy = min(md->mask + 1 - (offset & md->mask), len); |
@@ -376,7 +384,7 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
376 | len -= cpy; | 384 | len -= cpy; |
377 | } while (len); | 385 | } while (len); |
378 | 386 | ||
379 | event = &evlist->event_copy; | 387 | event = &md->event_copy; |
380 | } | 388 | } |
381 | 389 | ||
382 | old += size; | 390 | old += size; |
@@ -528,7 +536,6 @@ out_unmap: | |||
528 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | 536 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, |
529 | bool overwrite) | 537 | bool overwrite) |
530 | { | 538 | { |
531 | unsigned int page_size = sysconf(_SC_PAGE_SIZE); | ||
532 | struct perf_evsel *evsel; | 539 | struct perf_evsel *evsel; |
533 | const struct cpu_map *cpus = evlist->cpus; | 540 | const struct cpu_map *cpus = evlist->cpus; |
534 | const struct thread_map *threads = evlist->threads; | 541 | const struct thread_map *threads = evlist->threads; |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 56003f779e60..2dd07bd60b4f 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -17,10 +17,18 @@ struct perf_record_opts; | |||
17 | #define PERF_EVLIST__HLIST_BITS 8 | 17 | #define PERF_EVLIST__HLIST_BITS 8 |
18 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) | 18 | #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) |
19 | 19 | ||
20 | struct perf_mmap { | ||
21 | void *base; | ||
22 | int mask; | ||
23 | unsigned int prev; | ||
24 | union perf_event event_copy; | ||
25 | }; | ||
26 | |||
20 | struct perf_evlist { | 27 | struct perf_evlist { |
21 | struct list_head entries; | 28 | struct list_head entries; |
22 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; | 29 | struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; |
23 | int nr_entries; | 30 | int nr_entries; |
31 | int nr_groups; | ||
24 | int nr_fds; | 32 | int nr_fds; |
25 | int nr_mmaps; | 33 | int nr_mmaps; |
26 | int mmap_len; | 34 | int mmap_len; |
@@ -29,7 +37,6 @@ struct perf_evlist { | |||
29 | pid_t pid; | 37 | pid_t pid; |
30 | } workload; | 38 | } workload; |
31 | bool overwrite; | 39 | bool overwrite; |
32 | union perf_event event_copy; | ||
33 | struct perf_mmap *mmap; | 40 | struct perf_mmap *mmap; |
34 | struct pollfd *pollfd; | 41 | struct pollfd *pollfd; |
35 | struct thread_map *threads; | 42 | struct thread_map *threads; |
@@ -76,8 +83,8 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | |||
76 | 83 | ||
77 | int perf_evlist__open(struct perf_evlist *evlist); | 84 | int perf_evlist__open(struct perf_evlist *evlist); |
78 | 85 | ||
79 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 86 | void perf_evlist__config(struct perf_evlist *evlist, |
80 | struct perf_record_opts *opts); | 87 | struct perf_record_opts *opts); |
81 | 88 | ||
82 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, | 89 | int perf_evlist__prepare_workload(struct perf_evlist *evlist, |
83 | struct perf_record_opts *opts, | 90 | struct perf_record_opts *opts, |
@@ -135,4 +142,25 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) | |||
135 | } | 142 | } |
136 | 143 | ||
137 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); | 144 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); |
145 | |||
146 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) | ||
147 | { | ||
148 | struct perf_event_mmap_page *pc = mm->base; | ||
149 | int head = pc->data_head; | ||
150 | rmb(); | ||
151 | return head; | ||
152 | } | ||
153 | |||
154 | static inline void perf_mmap__write_tail(struct perf_mmap *md, | ||
155 | unsigned long tail) | ||
156 | { | ||
157 | struct perf_event_mmap_page *pc = md->base; | ||
158 | |||
159 | /* | ||
160 | * ensure all reads are done before we write the tail out. | ||
161 | */ | ||
162 | /* mb(); */ | ||
163 | pc->data_tail = tail; | ||
164 | } | ||
165 | |||
138 | #endif /* __PERF_EVLIST_H */ | 166 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 618d41140abd..9c82f98f26de 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -18,10 +18,15 @@ | |||
18 | #include "cpumap.h" | 18 | #include "cpumap.h" |
19 | #include "thread_map.h" | 19 | #include "thread_map.h" |
20 | #include "target.h" | 20 | #include "target.h" |
21 | #include "../../../include/linux/hw_breakpoint.h" | 21 | #include <linux/hw_breakpoint.h> |
22 | #include "../../../include/uapi/linux/perf_event.h" | 22 | #include <linux/perf_event.h> |
23 | #include "perf_regs.h" | 23 | #include "perf_regs.h" |
24 | 24 | ||
25 | static struct { | ||
26 | bool sample_id_all; | ||
27 | bool exclude_guest; | ||
28 | } perf_missing_features; | ||
29 | |||
25 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 30 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
26 | 31 | ||
27 | static int __perf_evsel__sample_size(u64 sample_type) | 32 | static int __perf_evsel__sample_size(u64 sample_type) |
@@ -50,11 +55,36 @@ void hists__init(struct hists *hists) | |||
50 | pthread_mutex_init(&hists->lock, NULL); | 55 | pthread_mutex_init(&hists->lock, NULL); |
51 | } | 56 | } |
52 | 57 | ||
58 | void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, | ||
59 | enum perf_event_sample_format bit) | ||
60 | { | ||
61 | if (!(evsel->attr.sample_type & bit)) { | ||
62 | evsel->attr.sample_type |= bit; | ||
63 | evsel->sample_size += sizeof(u64); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel, | ||
68 | enum perf_event_sample_format bit) | ||
69 | { | ||
70 | if (evsel->attr.sample_type & bit) { | ||
71 | evsel->attr.sample_type &= ~bit; | ||
72 | evsel->sample_size -= sizeof(u64); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | void perf_evsel__set_sample_id(struct perf_evsel *evsel) | ||
77 | { | ||
78 | perf_evsel__set_sample_bit(evsel, ID); | ||
79 | evsel->attr.read_format |= PERF_FORMAT_ID; | ||
80 | } | ||
81 | |||
53 | void perf_evsel__init(struct perf_evsel *evsel, | 82 | void perf_evsel__init(struct perf_evsel *evsel, |
54 | struct perf_event_attr *attr, int idx) | 83 | struct perf_event_attr *attr, int idx) |
55 | { | 84 | { |
56 | evsel->idx = idx; | 85 | evsel->idx = idx; |
57 | evsel->attr = *attr; | 86 | evsel->attr = *attr; |
87 | evsel->leader = evsel; | ||
58 | INIT_LIST_HEAD(&evsel->node); | 88 | INIT_LIST_HEAD(&evsel->node); |
59 | hists__init(&evsel->hists); | 89 | hists__init(&evsel->hists); |
60 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); | 90 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); |
@@ -404,20 +434,70 @@ const char *perf_evsel__name(struct perf_evsel *evsel) | |||
404 | return evsel->name ?: "unknown"; | 434 | return evsel->name ?: "unknown"; |
405 | } | 435 | } |
406 | 436 | ||
407 | void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, | 437 | const char *perf_evsel__group_name(struct perf_evsel *evsel) |
408 | struct perf_evsel *first) | 438 | { |
439 | return evsel->group_name ?: "anon group"; | ||
440 | } | ||
441 | |||
442 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) | ||
443 | { | ||
444 | int ret; | ||
445 | struct perf_evsel *pos; | ||
446 | const char *group_name = perf_evsel__group_name(evsel); | ||
447 | |||
448 | ret = scnprintf(buf, size, "%s", group_name); | ||
449 | |||
450 | ret += scnprintf(buf + ret, size - ret, " { %s", | ||
451 | perf_evsel__name(evsel)); | ||
452 | |||
453 | for_each_group_member(pos, evsel) | ||
454 | ret += scnprintf(buf + ret, size - ret, ", %s", | ||
455 | perf_evsel__name(pos)); | ||
456 | |||
457 | ret += scnprintf(buf + ret, size - ret, " }"); | ||
458 | |||
459 | return ret; | ||
460 | } | ||
461 | |||
462 | /* | ||
463 | * The enable_on_exec/disabled value strategy: | ||
464 | * | ||
465 | * 1) For any type of traced program: | ||
466 | * - all independent events and group leaders are disabled | ||
467 | * - all group members are enabled | ||
468 | * | ||
469 | * Group members are ruled by group leaders. They need to | ||
470 | * be enabled, because the group scheduling relies on that. | ||
471 | * | ||
472 | * 2) For traced programs executed by perf: | ||
473 | * - all independent events and group leaders have | ||
474 | * enable_on_exec set | ||
475 | * - we don't specifically enable or disable any event during | ||
476 | * the record command | ||
477 | * | ||
478 | * Independent events and group leaders are initially disabled | ||
479 | * and get enabled by exec. Group members are ruled by group | ||
480 | * leaders as stated in 1). | ||
481 | * | ||
482 | * 3) For traced programs attached by perf (pid/tid): | ||
483 | * - we specifically enable or disable all events during | ||
484 | * the record command | ||
485 | * | ||
486 | * When attaching events to already running traced we | ||
487 | * enable/disable events specifically, as there's no | ||
488 | * initial traced exec call. | ||
489 | */ | ||
490 | void perf_evsel__config(struct perf_evsel *evsel, | ||
491 | struct perf_record_opts *opts) | ||
409 | { | 492 | { |
410 | struct perf_event_attr *attr = &evsel->attr; | 493 | struct perf_event_attr *attr = &evsel->attr; |
411 | int track = !evsel->idx; /* only the first counter needs these */ | 494 | int track = !evsel->idx; /* only the first counter needs these */ |
412 | 495 | ||
413 | attr->disabled = 1; | 496 | attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; |
414 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; | ||
415 | attr->inherit = !opts->no_inherit; | 497 | attr->inherit = !opts->no_inherit; |
416 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | ||
417 | PERF_FORMAT_TOTAL_TIME_RUNNING | | ||
418 | PERF_FORMAT_ID; | ||
419 | 498 | ||
420 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 499 | perf_evsel__set_sample_bit(evsel, IP); |
500 | perf_evsel__set_sample_bit(evsel, TID); | ||
421 | 501 | ||
422 | /* | 502 | /* |
423 | * We default some events to a 1 default interval. But keep | 503 | * We default some events to a 1 default interval. But keep |
@@ -426,7 +506,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, | |||
426 | if (!attr->sample_period || (opts->user_freq != UINT_MAX && | 506 | if (!attr->sample_period || (opts->user_freq != UINT_MAX && |
427 | opts->user_interval != ULLONG_MAX)) { | 507 | opts->user_interval != ULLONG_MAX)) { |
428 | if (opts->freq) { | 508 | if (opts->freq) { |
429 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 509 | perf_evsel__set_sample_bit(evsel, PERIOD); |
430 | attr->freq = 1; | 510 | attr->freq = 1; |
431 | attr->sample_freq = opts->freq; | 511 | attr->sample_freq = opts->freq; |
432 | } else { | 512 | } else { |
@@ -441,16 +521,16 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, | |||
441 | attr->inherit_stat = 1; | 521 | attr->inherit_stat = 1; |
442 | 522 | ||
443 | if (opts->sample_address) { | 523 | if (opts->sample_address) { |
444 | attr->sample_type |= PERF_SAMPLE_ADDR; | 524 | perf_evsel__set_sample_bit(evsel, ADDR); |
445 | attr->mmap_data = track; | 525 | attr->mmap_data = track; |
446 | } | 526 | } |
447 | 527 | ||
448 | if (opts->call_graph) { | 528 | if (opts->call_graph) { |
449 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 529 | perf_evsel__set_sample_bit(evsel, CALLCHAIN); |
450 | 530 | ||
451 | if (opts->call_graph == CALLCHAIN_DWARF) { | 531 | if (opts->call_graph == CALLCHAIN_DWARF) { |
452 | attr->sample_type |= PERF_SAMPLE_REGS_USER | | 532 | perf_evsel__set_sample_bit(evsel, REGS_USER); |
453 | PERF_SAMPLE_STACK_USER; | 533 | perf_evsel__set_sample_bit(evsel, STACK_USER); |
454 | attr->sample_regs_user = PERF_REGS_MASK; | 534 | attr->sample_regs_user = PERF_REGS_MASK; |
455 | attr->sample_stack_user = opts->stack_dump_size; | 535 | attr->sample_stack_user = opts->stack_dump_size; |
456 | attr->exclude_callchain_user = 1; | 536 | attr->exclude_callchain_user = 1; |
@@ -458,20 +538,20 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, | |||
458 | } | 538 | } |
459 | 539 | ||
460 | if (perf_target__has_cpu(&opts->target)) | 540 | if (perf_target__has_cpu(&opts->target)) |
461 | attr->sample_type |= PERF_SAMPLE_CPU; | 541 | perf_evsel__set_sample_bit(evsel, CPU); |
462 | 542 | ||
463 | if (opts->period) | 543 | if (opts->period) |
464 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 544 | perf_evsel__set_sample_bit(evsel, PERIOD); |
465 | 545 | ||
466 | if (!opts->sample_id_all_missing && | 546 | if (!perf_missing_features.sample_id_all && |
467 | (opts->sample_time || !opts->no_inherit || | 547 | (opts->sample_time || !opts->no_inherit || |
468 | perf_target__has_cpu(&opts->target))) | 548 | perf_target__has_cpu(&opts->target))) |
469 | attr->sample_type |= PERF_SAMPLE_TIME; | 549 | perf_evsel__set_sample_bit(evsel, TIME); |
470 | 550 | ||
471 | if (opts->raw_samples) { | 551 | if (opts->raw_samples) { |
472 | attr->sample_type |= PERF_SAMPLE_TIME; | 552 | perf_evsel__set_sample_bit(evsel, TIME); |
473 | attr->sample_type |= PERF_SAMPLE_RAW; | 553 | perf_evsel__set_sample_bit(evsel, RAW); |
474 | attr->sample_type |= PERF_SAMPLE_CPU; | 554 | perf_evsel__set_sample_bit(evsel, CPU); |
475 | } | 555 | } |
476 | 556 | ||
477 | if (opts->no_delay) { | 557 | if (opts->no_delay) { |
@@ -479,17 +559,28 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, | |||
479 | attr->wakeup_events = 1; | 559 | attr->wakeup_events = 1; |
480 | } | 560 | } |
481 | if (opts->branch_stack) { | 561 | if (opts->branch_stack) { |
482 | attr->sample_type |= PERF_SAMPLE_BRANCH_STACK; | 562 | perf_evsel__set_sample_bit(evsel, BRANCH_STACK); |
483 | attr->branch_sample_type = opts->branch_stack; | 563 | attr->branch_sample_type = opts->branch_stack; |
484 | } | 564 | } |
485 | 565 | ||
486 | attr->mmap = track; | 566 | attr->mmap = track; |
487 | attr->comm = track; | 567 | attr->comm = track; |
488 | 568 | ||
489 | if (perf_target__none(&opts->target) && | 569 | /* |
490 | (!opts->group || evsel == first)) { | 570 | * XXX see the function comment above |
571 | * | ||
572 | * Disabling only independent events or group leaders, | ||
573 | * keeping group members enabled. | ||
574 | */ | ||
575 | if (perf_evsel__is_group_leader(evsel)) | ||
576 | attr->disabled = 1; | ||
577 | |||
578 | /* | ||
579 | * Setting enable_on_exec for independent events and | ||
580 | * group leaders for traced executed by perf. | ||
581 | */ | ||
582 | if (perf_target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) | ||
491 | attr->enable_on_exec = 1; | 583 | attr->enable_on_exec = 1; |
492 | } | ||
493 | } | 584 | } |
494 | 585 | ||
495 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 586 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
@@ -574,6 +665,11 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
574 | } | 665 | } |
575 | } | 666 | } |
576 | 667 | ||
668 | void perf_evsel__free_counts(struct perf_evsel *evsel) | ||
669 | { | ||
670 | free(evsel->counts); | ||
671 | } | ||
672 | |||
577 | void perf_evsel__exit(struct perf_evsel *evsel) | 673 | void perf_evsel__exit(struct perf_evsel *evsel) |
578 | { | 674 | { |
579 | assert(list_empty(&evsel->node)); | 675 | assert(list_empty(&evsel->node)); |
@@ -593,6 +689,28 @@ void perf_evsel__delete(struct perf_evsel *evsel) | |||
593 | free(evsel); | 689 | free(evsel); |
594 | } | 690 | } |
595 | 691 | ||
692 | static inline void compute_deltas(struct perf_evsel *evsel, | ||
693 | int cpu, | ||
694 | struct perf_counts_values *count) | ||
695 | { | ||
696 | struct perf_counts_values tmp; | ||
697 | |||
698 | if (!evsel->prev_raw_counts) | ||
699 | return; | ||
700 | |||
701 | if (cpu == -1) { | ||
702 | tmp = evsel->prev_raw_counts->aggr; | ||
703 | evsel->prev_raw_counts->aggr = *count; | ||
704 | } else { | ||
705 | tmp = evsel->prev_raw_counts->cpu[cpu]; | ||
706 | evsel->prev_raw_counts->cpu[cpu] = *count; | ||
707 | } | ||
708 | |||
709 | count->val = count->val - tmp.val; | ||
710 | count->ena = count->ena - tmp.ena; | ||
711 | count->run = count->run - tmp.run; | ||
712 | } | ||
713 | |||
596 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | 714 | int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, |
597 | int cpu, int thread, bool scale) | 715 | int cpu, int thread, bool scale) |
598 | { | 716 | { |
@@ -608,6 +726,8 @@ int __perf_evsel__read_on_cpu(struct perf_evsel *evsel, | |||
608 | if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) | 726 | if (readn(FD(evsel, cpu, thread), &count, nv * sizeof(u64)) < 0) |
609 | return -errno; | 727 | return -errno; |
610 | 728 | ||
729 | compute_deltas(evsel, cpu, &count); | ||
730 | |||
611 | if (scale) { | 731 | if (scale) { |
612 | if (count.run == 0) | 732 | if (count.run == 0) |
613 | count.val = 0; | 733 | count.val = 0; |
@@ -646,6 +766,8 @@ int __perf_evsel__read(struct perf_evsel *evsel, | |||
646 | } | 766 | } |
647 | } | 767 | } |
648 | 768 | ||
769 | compute_deltas(evsel, -1, aggr); | ||
770 | |||
649 | evsel->counts->scaled = 0; | 771 | evsel->counts->scaled = 0; |
650 | if (scale) { | 772 | if (scale) { |
651 | if (aggr->run == 0) { | 773 | if (aggr->run == 0) { |
@@ -669,7 +791,7 @@ static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread) | |||
669 | struct perf_evsel *leader = evsel->leader; | 791 | struct perf_evsel *leader = evsel->leader; |
670 | int fd; | 792 | int fd; |
671 | 793 | ||
672 | if (!leader) | 794 | if (perf_evsel__is_group_leader(evsel)) |
673 | return -1; | 795 | return -1; |
674 | 796 | ||
675 | /* | 797 | /* |
@@ -700,6 +822,13 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
700 | pid = evsel->cgrp->fd; | 822 | pid = evsel->cgrp->fd; |
701 | } | 823 | } |
702 | 824 | ||
825 | fallback_missing_features: | ||
826 | if (perf_missing_features.exclude_guest) | ||
827 | evsel->attr.exclude_guest = evsel->attr.exclude_host = 0; | ||
828 | retry_sample_id: | ||
829 | if (perf_missing_features.sample_id_all) | ||
830 | evsel->attr.sample_id_all = 0; | ||
831 | |||
703 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 832 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
704 | 833 | ||
705 | for (thread = 0; thread < threads->nr; thread++) { | 834 | for (thread = 0; thread < threads->nr; thread++) { |
@@ -716,13 +845,26 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
716 | group_fd, flags); | 845 | group_fd, flags); |
717 | if (FD(evsel, cpu, thread) < 0) { | 846 | if (FD(evsel, cpu, thread) < 0) { |
718 | err = -errno; | 847 | err = -errno; |
719 | goto out_close; | 848 | goto try_fallback; |
720 | } | 849 | } |
721 | } | 850 | } |
722 | } | 851 | } |
723 | 852 | ||
724 | return 0; | 853 | return 0; |
725 | 854 | ||
855 | try_fallback: | ||
856 | if (err != -EINVAL || cpu > 0 || thread > 0) | ||
857 | goto out_close; | ||
858 | |||
859 | if (!perf_missing_features.exclude_guest && | ||
860 | (evsel->attr.exclude_guest || evsel->attr.exclude_host)) { | ||
861 | perf_missing_features.exclude_guest = true; | ||
862 | goto fallback_missing_features; | ||
863 | } else if (!perf_missing_features.sample_id_all) { | ||
864 | perf_missing_features.sample_id_all = true; | ||
865 | goto retry_sample_id; | ||
866 | } | ||
867 | |||
726 | out_close: | 868 | out_close: |
727 | do { | 869 | do { |
728 | while (--thread >= 0) { | 870 | while (--thread >= 0) { |
@@ -1167,3 +1309,225 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, | |||
1167 | 1309 | ||
1168 | return 0; | 1310 | return 0; |
1169 | } | 1311 | } |
1312 | |||
1313 | static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) | ||
1314 | { | ||
1315 | va_list args; | ||
1316 | int ret = 0; | ||
1317 | |||
1318 | if (!*first) { | ||
1319 | ret += fprintf(fp, ","); | ||
1320 | } else { | ||
1321 | ret += fprintf(fp, ":"); | ||
1322 | *first = false; | ||
1323 | } | ||
1324 | |||
1325 | va_start(args, fmt); | ||
1326 | ret += vfprintf(fp, fmt, args); | ||
1327 | va_end(args); | ||
1328 | return ret; | ||
1329 | } | ||
1330 | |||
1331 | static int __if_fprintf(FILE *fp, bool *first, const char *field, u64 value) | ||
1332 | { | ||
1333 | if (value == 0) | ||
1334 | return 0; | ||
1335 | |||
1336 | return comma_fprintf(fp, first, " %s: %" PRIu64, field, value); | ||
1337 | } | ||
1338 | |||
1339 | #define if_print(field) printed += __if_fprintf(fp, &first, #field, evsel->attr.field) | ||
1340 | |||
1341 | struct bit_names { | ||
1342 | int bit; | ||
1343 | const char *name; | ||
1344 | }; | ||
1345 | |||
1346 | static int bits__fprintf(FILE *fp, const char *field, u64 value, | ||
1347 | struct bit_names *bits, bool *first) | ||
1348 | { | ||
1349 | int i = 0, printed = comma_fprintf(fp, first, " %s: ", field); | ||
1350 | bool first_bit = true; | ||
1351 | |||
1352 | do { | ||
1353 | if (value & bits[i].bit) { | ||
1354 | printed += fprintf(fp, "%s%s", first_bit ? "" : "|", bits[i].name); | ||
1355 | first_bit = false; | ||
1356 | } | ||
1357 | } while (bits[++i].name != NULL); | ||
1358 | |||
1359 | return printed; | ||
1360 | } | ||
1361 | |||
1362 | static int sample_type__fprintf(FILE *fp, bool *first, u64 value) | ||
1363 | { | ||
1364 | #define bit_name(n) { PERF_SAMPLE_##n, #n } | ||
1365 | struct bit_names bits[] = { | ||
1366 | bit_name(IP), bit_name(TID), bit_name(TIME), bit_name(ADDR), | ||
1367 | bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), | ||
1368 | bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), | ||
1369 | bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), | ||
1370 | { .name = NULL, } | ||
1371 | }; | ||
1372 | #undef bit_name | ||
1373 | return bits__fprintf(fp, "sample_type", value, bits, first); | ||
1374 | } | ||
1375 | |||
1376 | static int read_format__fprintf(FILE *fp, bool *first, u64 value) | ||
1377 | { | ||
1378 | #define bit_name(n) { PERF_FORMAT_##n, #n } | ||
1379 | struct bit_names bits[] = { | ||
1380 | bit_name(TOTAL_TIME_ENABLED), bit_name(TOTAL_TIME_RUNNING), | ||
1381 | bit_name(ID), bit_name(GROUP), | ||
1382 | { .name = NULL, } | ||
1383 | }; | ||
1384 | #undef bit_name | ||
1385 | return bits__fprintf(fp, "read_format", value, bits, first); | ||
1386 | } | ||
1387 | |||
1388 | int perf_evsel__fprintf(struct perf_evsel *evsel, | ||
1389 | struct perf_attr_details *details, FILE *fp) | ||
1390 | { | ||
1391 | bool first = true; | ||
1392 | int printed = 0; | ||
1393 | |||
1394 | if (details->event_group) { | ||
1395 | struct perf_evsel *pos; | ||
1396 | |||
1397 | if (!perf_evsel__is_group_leader(evsel)) | ||
1398 | return 0; | ||
1399 | |||
1400 | if (evsel->nr_members > 1) | ||
1401 | printed += fprintf(fp, "%s{", evsel->group_name ?: ""); | ||
1402 | |||
1403 | printed += fprintf(fp, "%s", perf_evsel__name(evsel)); | ||
1404 | for_each_group_member(pos, evsel) | ||
1405 | printed += fprintf(fp, ",%s", perf_evsel__name(pos)); | ||
1406 | |||
1407 | if (evsel->nr_members > 1) | ||
1408 | printed += fprintf(fp, "}"); | ||
1409 | goto out; | ||
1410 | } | ||
1411 | |||
1412 | printed += fprintf(fp, "%s", perf_evsel__name(evsel)); | ||
1413 | |||
1414 | if (details->verbose || details->freq) { | ||
1415 | printed += comma_fprintf(fp, &first, " sample_freq=%" PRIu64, | ||
1416 | (u64)evsel->attr.sample_freq); | ||
1417 | } | ||
1418 | |||
1419 | if (details->verbose) { | ||
1420 | if_print(type); | ||
1421 | if_print(config); | ||
1422 | if_print(config1); | ||
1423 | if_print(config2); | ||
1424 | if_print(size); | ||
1425 | printed += sample_type__fprintf(fp, &first, evsel->attr.sample_type); | ||
1426 | if (evsel->attr.read_format) | ||
1427 | printed += read_format__fprintf(fp, &first, evsel->attr.read_format); | ||
1428 | if_print(disabled); | ||
1429 | if_print(inherit); | ||
1430 | if_print(pinned); | ||
1431 | if_print(exclusive); | ||
1432 | if_print(exclude_user); | ||
1433 | if_print(exclude_kernel); | ||
1434 | if_print(exclude_hv); | ||
1435 | if_print(exclude_idle); | ||
1436 | if_print(mmap); | ||
1437 | if_print(comm); | ||
1438 | if_print(freq); | ||
1439 | if_print(inherit_stat); | ||
1440 | if_print(enable_on_exec); | ||
1441 | if_print(task); | ||
1442 | if_print(watermark); | ||
1443 | if_print(precise_ip); | ||
1444 | if_print(mmap_data); | ||
1445 | if_print(sample_id_all); | ||
1446 | if_print(exclude_host); | ||
1447 | if_print(exclude_guest); | ||
1448 | if_print(__reserved_1); | ||
1449 | if_print(wakeup_events); | ||
1450 | if_print(bp_type); | ||
1451 | if_print(branch_sample_type); | ||
1452 | } | ||
1453 | out: | ||
1454 | fputc('\n', fp); | ||
1455 | return ++printed; | ||
1456 | } | ||
1457 | |||
1458 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | ||
1459 | char *msg, size_t msgsize) | ||
1460 | { | ||
1461 | if ((err == ENOENT || err == ENXIO) && | ||
1462 | evsel->attr.type == PERF_TYPE_HARDWARE && | ||
1463 | evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) { | ||
1464 | /* | ||
1465 | * If it's cycles then fall back to hrtimer based | ||
1466 | * cpu-clock-tick sw counter, which is always available even if | ||
1467 | * no PMU support. | ||
1468 | * | ||
1469 | * PPC returns ENXIO until 2.6.37 (behavior changed with commit | ||
1470 | * b0a873e). | ||
1471 | */ | ||
1472 | scnprintf(msg, msgsize, "%s", | ||
1473 | "The cycles event is not supported, trying to fall back to cpu-clock-ticks"); | ||
1474 | |||
1475 | evsel->attr.type = PERF_TYPE_SOFTWARE; | ||
1476 | evsel->attr.config = PERF_COUNT_SW_CPU_CLOCK; | ||
1477 | |||
1478 | free(evsel->name); | ||
1479 | evsel->name = NULL; | ||
1480 | return true; | ||
1481 | } | ||
1482 | |||
1483 | return false; | ||
1484 | } | ||
1485 | |||
1486 | int perf_evsel__open_strerror(struct perf_evsel *evsel, | ||
1487 | struct perf_target *target, | ||
1488 | int err, char *msg, size_t size) | ||
1489 | { | ||
1490 | switch (err) { | ||
1491 | case EPERM: | ||
1492 | case EACCES: | ||
1493 | return scnprintf(msg, size, "%s", | ||
1494 | "You may not have permission to collect %sstats.\n" | ||
1495 | "Consider tweaking /proc/sys/kernel/perf_event_paranoid:\n" | ||
1496 | " -1 - Not paranoid at all\n" | ||
1497 | " 0 - Disallow raw tracepoint access for unpriv\n" | ||
1498 | " 1 - Disallow cpu events for unpriv\n" | ||
1499 | " 2 - Disallow kernel profiling for unpriv", | ||
1500 | target->system_wide ? "system-wide " : ""); | ||
1501 | case ENOENT: | ||
1502 | return scnprintf(msg, size, "The %s event is not supported.", | ||
1503 | perf_evsel__name(evsel)); | ||
1504 | case EMFILE: | ||
1505 | return scnprintf(msg, size, "%s", | ||
1506 | "Too many events are opened.\n" | ||
1507 | "Try again after reducing the number of events."); | ||
1508 | case ENODEV: | ||
1509 | if (target->cpu_list) | ||
1510 | return scnprintf(msg, size, "%s", | ||
1511 | "No such device - did you specify an out-of-range profile CPU?\n"); | ||
1512 | break; | ||
1513 | case EOPNOTSUPP: | ||
1514 | if (evsel->attr.precise_ip) | ||
1515 | return scnprintf(msg, size, "%s", | ||
1516 | "\'precise\' request may not be supported. Try removing 'p' modifier."); | ||
1517 | #if defined(__i386__) || defined(__x86_64__) | ||
1518 | if (evsel->attr.type == PERF_TYPE_HARDWARE) | ||
1519 | return scnprintf(msg, size, "%s", | ||
1520 | "No hardware sampling interrupt available.\n" | ||
1521 | "No APIC? If so then you can boot the kernel with the \"lapic\" boot parameter to force-enable it."); | ||
1522 | #endif | ||
1523 | break; | ||
1524 | default: | ||
1525 | break; | ||
1526 | } | ||
1527 | |||
1528 | return scnprintf(msg, size, | ||
1529 | "The sys_perf_event_open() syscall returned with %d (%s) for event (%s). \n" | ||
1530 | "/bin/dmesg may provide additional information.\n" | ||
1531 | "No CONFIG_PERF_EVENTS=y kernel support configured?\n", | ||
1532 | err, strerror(err), perf_evsel__name(evsel)); | ||
1533 | } | ||
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 6f94d6dea00f..52021c3087df 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -3,7 +3,8 @@ | |||
3 | 3 | ||
4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include "../../../include/uapi/linux/perf_event.h" | 6 | #include <stddef.h> |
7 | #include <linux/perf_event.h> | ||
7 | #include "types.h" | 8 | #include "types.h" |
8 | #include "xyarray.h" | 9 | #include "xyarray.h" |
9 | #include "cgroup.h" | 10 | #include "cgroup.h" |
@@ -52,6 +53,7 @@ struct perf_evsel { | |||
52 | struct xyarray *sample_id; | 53 | struct xyarray *sample_id; |
53 | u64 *id; | 54 | u64 *id; |
54 | struct perf_counts *counts; | 55 | struct perf_counts *counts; |
56 | struct perf_counts *prev_raw_counts; | ||
55 | int idx; | 57 | int idx; |
56 | u32 ids; | 58 | u32 ids; |
57 | struct hists hists; | 59 | struct hists hists; |
@@ -72,10 +74,13 @@ struct perf_evsel { | |||
72 | bool needs_swap; | 74 | bool needs_swap; |
73 | /* parse modifier helper */ | 75 | /* parse modifier helper */ |
74 | int exclude_GH; | 76 | int exclude_GH; |
77 | int nr_members; | ||
75 | struct perf_evsel *leader; | 78 | struct perf_evsel *leader; |
76 | char *group_name; | 79 | char *group_name; |
77 | }; | 80 | }; |
78 | 81 | ||
82 | #define hists_to_evsel(h) container_of(h, struct perf_evsel, hists) | ||
83 | |||
79 | struct cpu_map; | 84 | struct cpu_map; |
80 | struct thread_map; | 85 | struct thread_map; |
81 | struct perf_evlist; | 86 | struct perf_evlist; |
@@ -92,8 +97,7 @@ void perf_evsel__exit(struct perf_evsel *evsel); | |||
92 | void perf_evsel__delete(struct perf_evsel *evsel); | 97 | void perf_evsel__delete(struct perf_evsel *evsel); |
93 | 98 | ||
94 | void perf_evsel__config(struct perf_evsel *evsel, | 99 | void perf_evsel__config(struct perf_evsel *evsel, |
95 | struct perf_record_opts *opts, | 100 | struct perf_record_opts *opts); |
96 | struct perf_evsel *first); | ||
97 | 101 | ||
98 | bool perf_evsel__is_cache_op_valid(u8 type, u8 op); | 102 | bool perf_evsel__is_cache_op_valid(u8 type, u8 op); |
99 | 103 | ||
@@ -110,14 +114,30 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX]; | |||
110 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, | 114 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, |
111 | char *bf, size_t size); | 115 | char *bf, size_t size); |
112 | const char *perf_evsel__name(struct perf_evsel *evsel); | 116 | const char *perf_evsel__name(struct perf_evsel *evsel); |
117 | const char *perf_evsel__group_name(struct perf_evsel *evsel); | ||
118 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); | ||
113 | 119 | ||
114 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 120 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
115 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | 121 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); |
116 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); | 122 | int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus); |
117 | void perf_evsel__free_fd(struct perf_evsel *evsel); | 123 | void perf_evsel__free_fd(struct perf_evsel *evsel); |
118 | void perf_evsel__free_id(struct perf_evsel *evsel); | 124 | void perf_evsel__free_id(struct perf_evsel *evsel); |
125 | void perf_evsel__free_counts(struct perf_evsel *evsel); | ||
119 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 126 | void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
120 | 127 | ||
128 | void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, | ||
129 | enum perf_event_sample_format bit); | ||
130 | void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel, | ||
131 | enum perf_event_sample_format bit); | ||
132 | |||
133 | #define perf_evsel__set_sample_bit(evsel, bit) \ | ||
134 | __perf_evsel__set_sample_bit(evsel, PERF_SAMPLE_##bit) | ||
135 | |||
136 | #define perf_evsel__reset_sample_bit(evsel, bit) \ | ||
137 | __perf_evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit) | ||
138 | |||
139 | void perf_evsel__set_sample_id(struct perf_evsel *evsel); | ||
140 | |||
121 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | 141 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, |
122 | const char *filter); | 142 | const char *filter); |
123 | 143 | ||
@@ -225,4 +245,35 @@ static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel) | |||
225 | { | 245 | { |
226 | return list_entry(evsel->node.next, struct perf_evsel, node); | 246 | return list_entry(evsel->node.next, struct perf_evsel, node); |
227 | } | 247 | } |
248 | |||
249 | static inline bool perf_evsel__is_group_leader(const struct perf_evsel *evsel) | ||
250 | { | ||
251 | return evsel->leader == evsel; | ||
252 | } | ||
253 | |||
254 | struct perf_attr_details { | ||
255 | bool freq; | ||
256 | bool verbose; | ||
257 | bool event_group; | ||
258 | }; | ||
259 | |||
260 | int perf_evsel__fprintf(struct perf_evsel *evsel, | ||
261 | struct perf_attr_details *details, FILE *fp); | ||
262 | |||
263 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | ||
264 | char *msg, size_t msgsize); | ||
265 | int perf_evsel__open_strerror(struct perf_evsel *evsel, | ||
266 | struct perf_target *target, | ||
267 | int err, char *msg, size_t size); | ||
268 | |||
269 | static inline int perf_evsel__group_idx(struct perf_evsel *evsel) | ||
270 | { | ||
271 | return evsel->idx - evsel->leader->idx; | ||
272 | } | ||
273 | |||
274 | #define for_each_group_member(_evsel, _leader) \ | ||
275 | for ((_evsel) = list_entry((_leader)->node.next, struct perf_evsel, node); \ | ||
276 | (_evsel) && (_evsel)->leader == (_leader); \ | ||
277 | (_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node)) | ||
278 | |||
228 | #endif /* __PERF_EVSEL_H */ | 279 | #endif /* __PERF_EVSEL_H */ |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 7daad237dea5..f4bfd79ef6a7 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include "pmu.h" | 23 | #include "pmu.h" |
24 | #include "vdso.h" | 24 | #include "vdso.h" |
25 | #include "strbuf.h" | 25 | #include "strbuf.h" |
26 | #include "build-id.h" | ||
26 | 27 | ||
27 | static bool no_buildid_cache = false; | 28 | static bool no_buildid_cache = false; |
28 | 29 | ||
@@ -147,7 +148,7 @@ static char *do_read_string(int fd, struct perf_header *ph) | |||
147 | u32 len; | 148 | u32 len; |
148 | char *buf; | 149 | char *buf; |
149 | 150 | ||
150 | sz = read(fd, &len, sizeof(len)); | 151 | sz = readn(fd, &len, sizeof(len)); |
151 | if (sz < (ssize_t)sizeof(len)) | 152 | if (sz < (ssize_t)sizeof(len)) |
152 | return NULL; | 153 | return NULL; |
153 | 154 | ||
@@ -158,7 +159,7 @@ static char *do_read_string(int fd, struct perf_header *ph) | |||
158 | if (!buf) | 159 | if (!buf) |
159 | return NULL; | 160 | return NULL; |
160 | 161 | ||
161 | ret = read(fd, buf, len); | 162 | ret = readn(fd, buf, len); |
162 | if (ret == (ssize_t)len) { | 163 | if (ret == (ssize_t)len) { |
163 | /* | 164 | /* |
164 | * strings are padded by zeroes | 165 | * strings are padded by zeroes |
@@ -286,12 +287,12 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd) | |||
286 | struct perf_session *session = container_of(header, | 287 | struct perf_session *session = container_of(header, |
287 | struct perf_session, header); | 288 | struct perf_session, header); |
288 | struct rb_node *nd; | 289 | struct rb_node *nd; |
289 | int err = machine__write_buildid_table(&session->host_machine, fd); | 290 | int err = machine__write_buildid_table(&session->machines.host, fd); |
290 | 291 | ||
291 | if (err) | 292 | if (err) |
292 | return err; | 293 | return err; |
293 | 294 | ||
294 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { | 295 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { |
295 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 296 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
296 | err = machine__write_buildid_table(pos, fd); | 297 | err = machine__write_buildid_table(pos, fd); |
297 | if (err) | 298 | if (err) |
@@ -312,7 +313,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
312 | if (is_kallsyms) { | 313 | if (is_kallsyms) { |
313 | if (symbol_conf.kptr_restrict) { | 314 | if (symbol_conf.kptr_restrict) { |
314 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); | 315 | pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n"); |
315 | return 0; | 316 | err = 0; |
317 | goto out_free; | ||
316 | } | 318 | } |
317 | realname = (char *) name; | 319 | realname = (char *) name; |
318 | } else | 320 | } else |
@@ -447,9 +449,9 @@ static int perf_session__cache_build_ids(struct perf_session *session) | |||
447 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) | 449 | if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) |
448 | return -1; | 450 | return -1; |
449 | 451 | ||
450 | ret = machine__cache_build_ids(&session->host_machine, debugdir); | 452 | ret = machine__cache_build_ids(&session->machines.host, debugdir); |
451 | 453 | ||
452 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { | 454 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { |
453 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 455 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
454 | ret |= machine__cache_build_ids(pos, debugdir); | 456 | ret |= machine__cache_build_ids(pos, debugdir); |
455 | } | 457 | } |
@@ -466,9 +468,9 @@ static bool machine__read_build_ids(struct machine *machine, bool with_hits) | |||
466 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) | 468 | static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits) |
467 | { | 469 | { |
468 | struct rb_node *nd; | 470 | struct rb_node *nd; |
469 | bool ret = machine__read_build_ids(&session->host_machine, with_hits); | 471 | bool ret = machine__read_build_ids(&session->machines.host, with_hits); |
470 | 472 | ||
471 | for (nd = rb_first(&session->machines); nd; nd = rb_next(nd)) { | 473 | for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) { |
472 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | 474 | struct machine *pos = rb_entry(nd, struct machine, rb_node); |
473 | ret |= machine__read_build_ids(pos, with_hits); | 475 | ret |= machine__read_build_ids(pos, with_hits); |
474 | } | 476 | } |
@@ -953,6 +955,7 @@ static int write_topo_node(int fd, int node) | |||
953 | } | 955 | } |
954 | 956 | ||
955 | fclose(fp); | 957 | fclose(fp); |
958 | fp = NULL; | ||
956 | 959 | ||
957 | ret = do_write(fd, &mem_total, sizeof(u64)); | 960 | ret = do_write(fd, &mem_total, sizeof(u64)); |
958 | if (ret) | 961 | if (ret) |
@@ -979,7 +982,8 @@ static int write_topo_node(int fd, int node) | |||
979 | ret = do_write_string(fd, buf); | 982 | ret = do_write_string(fd, buf); |
980 | done: | 983 | done: |
981 | free(buf); | 984 | free(buf); |
982 | fclose(fp); | 985 | if (fp) |
986 | fclose(fp); | ||
983 | return ret; | 987 | return ret; |
984 | } | 988 | } |
985 | 989 | ||
@@ -1050,16 +1054,25 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused, | |||
1050 | struct perf_pmu *pmu = NULL; | 1054 | struct perf_pmu *pmu = NULL; |
1051 | off_t offset = lseek(fd, 0, SEEK_CUR); | 1055 | off_t offset = lseek(fd, 0, SEEK_CUR); |
1052 | __u32 pmu_num = 0; | 1056 | __u32 pmu_num = 0; |
1057 | int ret; | ||
1053 | 1058 | ||
1054 | /* write real pmu_num later */ | 1059 | /* write real pmu_num later */ |
1055 | do_write(fd, &pmu_num, sizeof(pmu_num)); | 1060 | ret = do_write(fd, &pmu_num, sizeof(pmu_num)); |
1061 | if (ret < 0) | ||
1062 | return ret; | ||
1056 | 1063 | ||
1057 | while ((pmu = perf_pmu__scan(pmu))) { | 1064 | while ((pmu = perf_pmu__scan(pmu))) { |
1058 | if (!pmu->name) | 1065 | if (!pmu->name) |
1059 | continue; | 1066 | continue; |
1060 | pmu_num++; | 1067 | pmu_num++; |
1061 | do_write(fd, &pmu->type, sizeof(pmu->type)); | 1068 | |
1062 | do_write_string(fd, pmu->name); | 1069 | ret = do_write(fd, &pmu->type, sizeof(pmu->type)); |
1070 | if (ret < 0) | ||
1071 | return ret; | ||
1072 | |||
1073 | ret = do_write_string(fd, pmu->name); | ||
1074 | if (ret < 0) | ||
1075 | return ret; | ||
1063 | } | 1076 | } |
1064 | 1077 | ||
1065 | if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) { | 1078 | if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) { |
@@ -1072,6 +1085,52 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused, | |||
1072 | } | 1085 | } |
1073 | 1086 | ||
1074 | /* | 1087 | /* |
1088 | * File format: | ||
1089 | * | ||
1090 | * struct group_descs { | ||
1091 | * u32 nr_groups; | ||
1092 | * struct group_desc { | ||
1093 | * char name[]; | ||
1094 | * u32 leader_idx; | ||
1095 | * u32 nr_members; | ||
1096 | * }[nr_groups]; | ||
1097 | * }; | ||
1098 | */ | ||
1099 | static int write_group_desc(int fd, struct perf_header *h __maybe_unused, | ||
1100 | struct perf_evlist *evlist) | ||
1101 | { | ||
1102 | u32 nr_groups = evlist->nr_groups; | ||
1103 | struct perf_evsel *evsel; | ||
1104 | int ret; | ||
1105 | |||
1106 | ret = do_write(fd, &nr_groups, sizeof(nr_groups)); | ||
1107 | if (ret < 0) | ||
1108 | return ret; | ||
1109 | |||
1110 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
1111 | if (perf_evsel__is_group_leader(evsel) && | ||
1112 | evsel->nr_members > 1) { | ||
1113 | const char *name = evsel->group_name ?: "{anon_group}"; | ||
1114 | u32 leader_idx = evsel->idx; | ||
1115 | u32 nr_members = evsel->nr_members; | ||
1116 | |||
1117 | ret = do_write_string(fd, name); | ||
1118 | if (ret < 0) | ||
1119 | return ret; | ||
1120 | |||
1121 | ret = do_write(fd, &leader_idx, sizeof(leader_idx)); | ||
1122 | if (ret < 0) | ||
1123 | return ret; | ||
1124 | |||
1125 | ret = do_write(fd, &nr_members, sizeof(nr_members)); | ||
1126 | if (ret < 0) | ||
1127 | return ret; | ||
1128 | } | ||
1129 | } | ||
1130 | return 0; | ||
1131 | } | ||
1132 | |||
1133 | /* | ||
1075 | * default get_cpuid(): nothing gets recorded | 1134 | * default get_cpuid(): nothing gets recorded |
1076 | * actual implementation must be in arch/$(ARCH)/util/header.c | 1135 | * actual implementation must be in arch/$(ARCH)/util/header.c |
1077 | */ | 1136 | */ |
@@ -1208,14 +1267,14 @@ read_event_desc(struct perf_header *ph, int fd) | |||
1208 | size_t msz; | 1267 | size_t msz; |
1209 | 1268 | ||
1210 | /* number of events */ | 1269 | /* number of events */ |
1211 | ret = read(fd, &nre, sizeof(nre)); | 1270 | ret = readn(fd, &nre, sizeof(nre)); |
1212 | if (ret != (ssize_t)sizeof(nre)) | 1271 | if (ret != (ssize_t)sizeof(nre)) |
1213 | goto error; | 1272 | goto error; |
1214 | 1273 | ||
1215 | if (ph->needs_swap) | 1274 | if (ph->needs_swap) |
1216 | nre = bswap_32(nre); | 1275 | nre = bswap_32(nre); |
1217 | 1276 | ||
1218 | ret = read(fd, &sz, sizeof(sz)); | 1277 | ret = readn(fd, &sz, sizeof(sz)); |
1219 | if (ret != (ssize_t)sizeof(sz)) | 1278 | if (ret != (ssize_t)sizeof(sz)) |
1220 | goto error; | 1279 | goto error; |
1221 | 1280 | ||
@@ -1243,7 +1302,7 @@ read_event_desc(struct perf_header *ph, int fd) | |||
1243 | * must read entire on-file attr struct to | 1302 | * must read entire on-file attr struct to |
1244 | * sync up with layout. | 1303 | * sync up with layout. |
1245 | */ | 1304 | */ |
1246 | ret = read(fd, buf, sz); | 1305 | ret = readn(fd, buf, sz); |
1247 | if (ret != (ssize_t)sz) | 1306 | if (ret != (ssize_t)sz) |
1248 | goto error; | 1307 | goto error; |
1249 | 1308 | ||
@@ -1252,7 +1311,7 @@ read_event_desc(struct perf_header *ph, int fd) | |||
1252 | 1311 | ||
1253 | memcpy(&evsel->attr, buf, msz); | 1312 | memcpy(&evsel->attr, buf, msz); |
1254 | 1313 | ||
1255 | ret = read(fd, &nr, sizeof(nr)); | 1314 | ret = readn(fd, &nr, sizeof(nr)); |
1256 | if (ret != (ssize_t)sizeof(nr)) | 1315 | if (ret != (ssize_t)sizeof(nr)) |
1257 | goto error; | 1316 | goto error; |
1258 | 1317 | ||
@@ -1273,7 +1332,7 @@ read_event_desc(struct perf_header *ph, int fd) | |||
1273 | evsel->id = id; | 1332 | evsel->id = id; |
1274 | 1333 | ||
1275 | for (j = 0 ; j < nr; j++) { | 1334 | for (j = 0 ; j < nr; j++) { |
1276 | ret = read(fd, id, sizeof(*id)); | 1335 | ret = readn(fd, id, sizeof(*id)); |
1277 | if (ret != (ssize_t)sizeof(*id)) | 1336 | if (ret != (ssize_t)sizeof(*id)) |
1278 | goto error; | 1337 | goto error; |
1279 | if (ph->needs_swap) | 1338 | if (ph->needs_swap) |
@@ -1378,6 +1437,8 @@ static void print_numa_topology(struct perf_header *ph, int fd __maybe_unused, | |||
1378 | 1437 | ||
1379 | str = tmp + 1; | 1438 | str = tmp + 1; |
1380 | fprintf(fp, "# node%u cpu list : %s\n", c, str); | 1439 | fprintf(fp, "# node%u cpu list : %s\n", c, str); |
1440 | |||
1441 | str += strlen(str) + 1; | ||
1381 | } | 1442 | } |
1382 | return; | 1443 | return; |
1383 | error: | 1444 | error: |
@@ -1432,6 +1493,31 @@ error: | |||
1432 | fprintf(fp, "# pmu mappings: unable to read\n"); | 1493 | fprintf(fp, "# pmu mappings: unable to read\n"); |
1433 | } | 1494 | } |
1434 | 1495 | ||
1496 | static void print_group_desc(struct perf_header *ph, int fd __maybe_unused, | ||
1497 | FILE *fp) | ||
1498 | { | ||
1499 | struct perf_session *session; | ||
1500 | struct perf_evsel *evsel; | ||
1501 | u32 nr = 0; | ||
1502 | |||
1503 | session = container_of(ph, struct perf_session, header); | ||
1504 | |||
1505 | list_for_each_entry(evsel, &session->evlist->entries, node) { | ||
1506 | if (perf_evsel__is_group_leader(evsel) && | ||
1507 | evsel->nr_members > 1) { | ||
1508 | fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", | ||
1509 | perf_evsel__name(evsel)); | ||
1510 | |||
1511 | nr = evsel->nr_members - 1; | ||
1512 | } else if (nr) { | ||
1513 | fprintf(fp, ",%s", perf_evsel__name(evsel)); | ||
1514 | |||
1515 | if (--nr == 0) | ||
1516 | fprintf(fp, "}\n"); | ||
1517 | } | ||
1518 | } | ||
1519 | } | ||
1520 | |||
1435 | static int __event_process_build_id(struct build_id_event *bev, | 1521 | static int __event_process_build_id(struct build_id_event *bev, |
1436 | char *filename, | 1522 | char *filename, |
1437 | struct perf_session *session) | 1523 | struct perf_session *session) |
@@ -1503,14 +1589,14 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, | |||
1503 | while (offset < limit) { | 1589 | while (offset < limit) { |
1504 | ssize_t len; | 1590 | ssize_t len; |
1505 | 1591 | ||
1506 | if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) | 1592 | if (readn(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) |
1507 | return -1; | 1593 | return -1; |
1508 | 1594 | ||
1509 | if (header->needs_swap) | 1595 | if (header->needs_swap) |
1510 | perf_event_header__bswap(&old_bev.header); | 1596 | perf_event_header__bswap(&old_bev.header); |
1511 | 1597 | ||
1512 | len = old_bev.header.size - sizeof(old_bev); | 1598 | len = old_bev.header.size - sizeof(old_bev); |
1513 | if (read(input, filename, len) != len) | 1599 | if (readn(input, filename, len) != len) |
1514 | return -1; | 1600 | return -1; |
1515 | 1601 | ||
1516 | bev.header = old_bev.header; | 1602 | bev.header = old_bev.header; |
@@ -1545,14 +1631,14 @@ static int perf_header__read_build_ids(struct perf_header *header, | |||
1545 | while (offset < limit) { | 1631 | while (offset < limit) { |
1546 | ssize_t len; | 1632 | ssize_t len; |
1547 | 1633 | ||
1548 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | 1634 | if (readn(input, &bev, sizeof(bev)) != sizeof(bev)) |
1549 | goto out; | 1635 | goto out; |
1550 | 1636 | ||
1551 | if (header->needs_swap) | 1637 | if (header->needs_swap) |
1552 | perf_event_header__bswap(&bev.header); | 1638 | perf_event_header__bswap(&bev.header); |
1553 | 1639 | ||
1554 | len = bev.header.size - sizeof(bev); | 1640 | len = bev.header.size - sizeof(bev); |
1555 | if (read(input, filename, len) != len) | 1641 | if (readn(input, filename, len) != len) |
1556 | goto out; | 1642 | goto out; |
1557 | /* | 1643 | /* |
1558 | * The a1645ce1 changeset: | 1644 | * The a1645ce1 changeset: |
@@ -1638,7 +1724,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused, | |||
1638 | size_t ret; | 1724 | size_t ret; |
1639 | u32 nr; | 1725 | u32 nr; |
1640 | 1726 | ||
1641 | ret = read(fd, &nr, sizeof(nr)); | 1727 | ret = readn(fd, &nr, sizeof(nr)); |
1642 | if (ret != sizeof(nr)) | 1728 | if (ret != sizeof(nr)) |
1643 | return -1; | 1729 | return -1; |
1644 | 1730 | ||
@@ -1647,7 +1733,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused, | |||
1647 | 1733 | ||
1648 | ph->env.nr_cpus_online = nr; | 1734 | ph->env.nr_cpus_online = nr; |
1649 | 1735 | ||
1650 | ret = read(fd, &nr, sizeof(nr)); | 1736 | ret = readn(fd, &nr, sizeof(nr)); |
1651 | if (ret != sizeof(nr)) | 1737 | if (ret != sizeof(nr)) |
1652 | return -1; | 1738 | return -1; |
1653 | 1739 | ||
@@ -1681,7 +1767,7 @@ static int process_total_mem(struct perf_file_section *section __maybe_unused, | |||
1681 | uint64_t mem; | 1767 | uint64_t mem; |
1682 | size_t ret; | 1768 | size_t ret; |
1683 | 1769 | ||
1684 | ret = read(fd, &mem, sizeof(mem)); | 1770 | ret = readn(fd, &mem, sizeof(mem)); |
1685 | if (ret != sizeof(mem)) | 1771 | if (ret != sizeof(mem)) |
1686 | return -1; | 1772 | return -1; |
1687 | 1773 | ||
@@ -1753,7 +1839,7 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused, | |||
1753 | u32 nr, i; | 1839 | u32 nr, i; |
1754 | struct strbuf sb; | 1840 | struct strbuf sb; |
1755 | 1841 | ||
1756 | ret = read(fd, &nr, sizeof(nr)); | 1842 | ret = readn(fd, &nr, sizeof(nr)); |
1757 | if (ret != sizeof(nr)) | 1843 | if (ret != sizeof(nr)) |
1758 | return -1; | 1844 | return -1; |
1759 | 1845 | ||
@@ -1789,7 +1875,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused | |||
1789 | char *str; | 1875 | char *str; |
1790 | struct strbuf sb; | 1876 | struct strbuf sb; |
1791 | 1877 | ||
1792 | ret = read(fd, &nr, sizeof(nr)); | 1878 | ret = readn(fd, &nr, sizeof(nr)); |
1793 | if (ret != sizeof(nr)) | 1879 | if (ret != sizeof(nr)) |
1794 | return -1; | 1880 | return -1; |
1795 | 1881 | ||
@@ -1810,7 +1896,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused | |||
1810 | } | 1896 | } |
1811 | ph->env.sibling_cores = strbuf_detach(&sb, NULL); | 1897 | ph->env.sibling_cores = strbuf_detach(&sb, NULL); |
1812 | 1898 | ||
1813 | ret = read(fd, &nr, sizeof(nr)); | 1899 | ret = readn(fd, &nr, sizeof(nr)); |
1814 | if (ret != sizeof(nr)) | 1900 | if (ret != sizeof(nr)) |
1815 | return -1; | 1901 | return -1; |
1816 | 1902 | ||
@@ -1847,7 +1933,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse | |||
1847 | struct strbuf sb; | 1933 | struct strbuf sb; |
1848 | 1934 | ||
1849 | /* nr nodes */ | 1935 | /* nr nodes */ |
1850 | ret = read(fd, &nr, sizeof(nr)); | 1936 | ret = readn(fd, &nr, sizeof(nr)); |
1851 | if (ret != sizeof(nr)) | 1937 | if (ret != sizeof(nr)) |
1852 | goto error; | 1938 | goto error; |
1853 | 1939 | ||
@@ -1859,15 +1945,15 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse | |||
1859 | 1945 | ||
1860 | for (i = 0; i < nr; i++) { | 1946 | for (i = 0; i < nr; i++) { |
1861 | /* node number */ | 1947 | /* node number */ |
1862 | ret = read(fd, &node, sizeof(node)); | 1948 | ret = readn(fd, &node, sizeof(node)); |
1863 | if (ret != sizeof(node)) | 1949 | if (ret != sizeof(node)) |
1864 | goto error; | 1950 | goto error; |
1865 | 1951 | ||
1866 | ret = read(fd, &mem_total, sizeof(u64)); | 1952 | ret = readn(fd, &mem_total, sizeof(u64)); |
1867 | if (ret != sizeof(u64)) | 1953 | if (ret != sizeof(u64)) |
1868 | goto error; | 1954 | goto error; |
1869 | 1955 | ||
1870 | ret = read(fd, &mem_free, sizeof(u64)); | 1956 | ret = readn(fd, &mem_free, sizeof(u64)); |
1871 | if (ret != sizeof(u64)) | 1957 | if (ret != sizeof(u64)) |
1872 | goto error; | 1958 | goto error; |
1873 | 1959 | ||
@@ -1906,7 +1992,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused | |||
1906 | u32 type; | 1992 | u32 type; |
1907 | struct strbuf sb; | 1993 | struct strbuf sb; |
1908 | 1994 | ||
1909 | ret = read(fd, &pmu_num, sizeof(pmu_num)); | 1995 | ret = readn(fd, &pmu_num, sizeof(pmu_num)); |
1910 | if (ret != sizeof(pmu_num)) | 1996 | if (ret != sizeof(pmu_num)) |
1911 | return -1; | 1997 | return -1; |
1912 | 1998 | ||
@@ -1922,7 +2008,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused | |||
1922 | strbuf_init(&sb, 128); | 2008 | strbuf_init(&sb, 128); |
1923 | 2009 | ||
1924 | while (pmu_num) { | 2010 | while (pmu_num) { |
1925 | if (read(fd, &type, sizeof(type)) != sizeof(type)) | 2011 | if (readn(fd, &type, sizeof(type)) != sizeof(type)) |
1926 | goto error; | 2012 | goto error; |
1927 | if (ph->needs_swap) | 2013 | if (ph->needs_swap) |
1928 | type = bswap_32(type); | 2014 | type = bswap_32(type); |
@@ -1946,6 +2032,98 @@ error: | |||
1946 | return -1; | 2032 | return -1; |
1947 | } | 2033 | } |
1948 | 2034 | ||
2035 | static int process_group_desc(struct perf_file_section *section __maybe_unused, | ||
2036 | struct perf_header *ph, int fd, | ||
2037 | void *data __maybe_unused) | ||
2038 | { | ||
2039 | size_t ret = -1; | ||
2040 | u32 i, nr, nr_groups; | ||
2041 | struct perf_session *session; | ||
2042 | struct perf_evsel *evsel, *leader = NULL; | ||
2043 | struct group_desc { | ||
2044 | char *name; | ||
2045 | u32 leader_idx; | ||
2046 | u32 nr_members; | ||
2047 | } *desc; | ||
2048 | |||
2049 | if (readn(fd, &nr_groups, sizeof(nr_groups)) != sizeof(nr_groups)) | ||
2050 | return -1; | ||
2051 | |||
2052 | if (ph->needs_swap) | ||
2053 | nr_groups = bswap_32(nr_groups); | ||
2054 | |||
2055 | ph->env.nr_groups = nr_groups; | ||
2056 | if (!nr_groups) { | ||
2057 | pr_debug("group desc not available\n"); | ||
2058 | return 0; | ||
2059 | } | ||
2060 | |||
2061 | desc = calloc(nr_groups, sizeof(*desc)); | ||
2062 | if (!desc) | ||
2063 | return -1; | ||
2064 | |||
2065 | for (i = 0; i < nr_groups; i++) { | ||
2066 | desc[i].name = do_read_string(fd, ph); | ||
2067 | if (!desc[i].name) | ||
2068 | goto out_free; | ||
2069 | |||
2070 | if (readn(fd, &desc[i].leader_idx, sizeof(u32)) != sizeof(u32)) | ||
2071 | goto out_free; | ||
2072 | |||
2073 | if (readn(fd, &desc[i].nr_members, sizeof(u32)) != sizeof(u32)) | ||
2074 | goto out_free; | ||
2075 | |||
2076 | if (ph->needs_swap) { | ||
2077 | desc[i].leader_idx = bswap_32(desc[i].leader_idx); | ||
2078 | desc[i].nr_members = bswap_32(desc[i].nr_members); | ||
2079 | } | ||
2080 | } | ||
2081 | |||
2082 | /* | ||
2083 | * Rebuild group relationship based on the group_desc | ||
2084 | */ | ||
2085 | session = container_of(ph, struct perf_session, header); | ||
2086 | session->evlist->nr_groups = nr_groups; | ||
2087 | |||
2088 | i = nr = 0; | ||
2089 | list_for_each_entry(evsel, &session->evlist->entries, node) { | ||
2090 | if (evsel->idx == (int) desc[i].leader_idx) { | ||
2091 | evsel->leader = evsel; | ||
2092 | /* {anon_group} is a dummy name */ | ||
2093 | if (strcmp(desc[i].name, "{anon_group}")) | ||
2094 | evsel->group_name = desc[i].name; | ||
2095 | evsel->nr_members = desc[i].nr_members; | ||
2096 | |||
2097 | if (i >= nr_groups || nr > 0) { | ||
2098 | pr_debug("invalid group desc\n"); | ||
2099 | goto out_free; | ||
2100 | } | ||
2101 | |||
2102 | leader = evsel; | ||
2103 | nr = evsel->nr_members - 1; | ||
2104 | i++; | ||
2105 | } else if (nr) { | ||
2106 | /* This is a group member */ | ||
2107 | evsel->leader = leader; | ||
2108 | |||
2109 | nr--; | ||
2110 | } | ||
2111 | } | ||
2112 | |||
2113 | if (i != nr_groups || nr != 0) { | ||
2114 | pr_debug("invalid group desc\n"); | ||
2115 | goto out_free; | ||
2116 | } | ||
2117 | |||
2118 | ret = 0; | ||
2119 | out_free: | ||
2120 | while ((int) --i >= 0) | ||
2121 | free(desc[i].name); | ||
2122 | free(desc); | ||
2123 | |||
2124 | return ret; | ||
2125 | } | ||
2126 | |||
1949 | struct feature_ops { | 2127 | struct feature_ops { |
1950 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | 2128 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); |
1951 | void (*print)(struct perf_header *h, int fd, FILE *fp); | 2129 | void (*print)(struct perf_header *h, int fd, FILE *fp); |
@@ -1985,6 +2163,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
1985 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), | 2163 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), |
1986 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), | 2164 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), |
1987 | FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), | 2165 | FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), |
2166 | FEAT_OPP(HEADER_GROUP_DESC, group_desc), | ||
1988 | }; | 2167 | }; |
1989 | 2168 | ||
1990 | struct header_print_data { | 2169 | struct header_print_data { |
@@ -2074,7 +2253,7 @@ static int perf_header__adds_write(struct perf_header *header, | |||
2074 | if (!nr_sections) | 2253 | if (!nr_sections) |
2075 | return 0; | 2254 | return 0; |
2076 | 2255 | ||
2077 | feat_sec = p = calloc(sizeof(*feat_sec), nr_sections); | 2256 | feat_sec = p = calloc(nr_sections, sizeof(*feat_sec)); |
2078 | if (feat_sec == NULL) | 2257 | if (feat_sec == NULL) |
2079 | return -ENOMEM; | 2258 | return -ENOMEM; |
2080 | 2259 | ||
@@ -2246,7 +2425,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
2246 | if (!nr_sections) | 2425 | if (!nr_sections) |
2247 | return 0; | 2426 | return 0; |
2248 | 2427 | ||
2249 | feat_sec = sec = calloc(sizeof(*feat_sec), nr_sections); | 2428 | feat_sec = sec = calloc(nr_sections, sizeof(*feat_sec)); |
2250 | if (!feat_sec) | 2429 | if (!feat_sec) |
2251 | return -1; | 2430 | return -1; |
2252 | 2431 | ||
@@ -2340,6 +2519,16 @@ static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph) | |||
2340 | return -1; | 2519 | return -1; |
2341 | } | 2520 | } |
2342 | 2521 | ||
2522 | bool is_perf_magic(u64 magic) | ||
2523 | { | ||
2524 | if (!memcmp(&magic, __perf_magic1, sizeof(magic)) | ||
2525 | || magic == __perf_magic2 | ||
2526 | || magic == __perf_magic2_sw) | ||
2527 | return true; | ||
2528 | |||
2529 | return false; | ||
2530 | } | ||
2531 | |||
2343 | static int check_magic_endian(u64 magic, uint64_t hdr_sz, | 2532 | static int check_magic_endian(u64 magic, uint64_t hdr_sz, |
2344 | bool is_pipe, struct perf_header *ph) | 2533 | bool is_pipe, struct perf_header *ph) |
2345 | { | 2534 | { |
@@ -2899,16 +3088,22 @@ int perf_event__process_tracing_data(union perf_event *event, | |||
2899 | session->repipe); | 3088 | session->repipe); |
2900 | padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; | 3089 | padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; |
2901 | 3090 | ||
2902 | if (read(session->fd, buf, padding) < 0) | 3091 | if (readn(session->fd, buf, padding) < 0) { |
2903 | die("reading input file"); | 3092 | pr_err("%s: reading input file", __func__); |
3093 | return -1; | ||
3094 | } | ||
2904 | if (session->repipe) { | 3095 | if (session->repipe) { |
2905 | int retw = write(STDOUT_FILENO, buf, padding); | 3096 | int retw = write(STDOUT_FILENO, buf, padding); |
2906 | if (retw <= 0 || retw != padding) | 3097 | if (retw <= 0 || retw != padding) { |
2907 | die("repiping tracing data padding"); | 3098 | pr_err("%s: repiping tracing data padding", __func__); |
3099 | return -1; | ||
3100 | } | ||
2908 | } | 3101 | } |
2909 | 3102 | ||
2910 | if (size_read + padding != size) | 3103 | if (size_read + padding != size) { |
2911 | die("tracing data size mismatch"); | 3104 | pr_err("%s: tracing data size mismatch", __func__); |
3105 | return -1; | ||
3106 | } | ||
2912 | 3107 | ||
2913 | perf_evlist__prepare_tracepoint_events(session->evlist, | 3108 | perf_evlist__prepare_tracepoint_events(session->evlist, |
2914 | session->pevent); | 3109 | session->pevent); |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 879d215cdac9..c9fc55cada6d 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -1,7 +1,7 @@ | |||
1 | #ifndef __PERF_HEADER_H | 1 | #ifndef __PERF_HEADER_H |
2 | #define __PERF_HEADER_H | 2 | #define __PERF_HEADER_H |
3 | 3 | ||
4 | #include "../../../include/uapi/linux/perf_event.h" | 4 | #include <linux/perf_event.h> |
5 | #include <sys/types.h> | 5 | #include <sys/types.h> |
6 | #include <stdbool.h> | 6 | #include <stdbool.h> |
7 | #include "types.h" | 7 | #include "types.h" |
@@ -29,6 +29,7 @@ enum { | |||
29 | HEADER_NUMA_TOPOLOGY, | 29 | HEADER_NUMA_TOPOLOGY, |
30 | HEADER_BRANCH_STACK, | 30 | HEADER_BRANCH_STACK, |
31 | HEADER_PMU_MAPPINGS, | 31 | HEADER_PMU_MAPPINGS, |
32 | HEADER_GROUP_DESC, | ||
32 | HEADER_LAST_FEATURE, | 33 | HEADER_LAST_FEATURE, |
33 | HEADER_FEAT_BITS = 256, | 34 | HEADER_FEAT_BITS = 256, |
34 | }; | 35 | }; |
@@ -79,6 +80,7 @@ struct perf_session_env { | |||
79 | char *numa_nodes; | 80 | char *numa_nodes; |
80 | int nr_pmu_mappings; | 81 | int nr_pmu_mappings; |
81 | char *pmu_mappings; | 82 | char *pmu_mappings; |
83 | int nr_groups; | ||
82 | }; | 84 | }; |
83 | 85 | ||
84 | struct perf_header { | 86 | struct perf_header { |
@@ -154,6 +156,7 @@ int perf_event__synthesize_build_id(struct perf_tool *tool, | |||
154 | int perf_event__process_build_id(struct perf_tool *tool, | 156 | int perf_event__process_build_id(struct perf_tool *tool, |
155 | union perf_event *event, | 157 | union perf_event *event, |
156 | struct perf_session *session); | 158 | struct perf_session *session); |
159 | bool is_perf_magic(u64 magic); | ||
157 | 160 | ||
158 | /* | 161 | /* |
159 | * arch specific callback | 162 | * arch specific callback |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 277947a669b2..f855941bebea 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -4,6 +4,7 @@ | |||
4 | #include "hist.h" | 4 | #include "hist.h" |
5 | #include "session.h" | 5 | #include "session.h" |
6 | #include "sort.h" | 6 | #include "sort.h" |
7 | #include "evsel.h" | ||
7 | #include <math.h> | 8 | #include <math.h> |
8 | 9 | ||
9 | static bool hists__filter_entry_by_dso(struct hists *hists, | 10 | static bool hists__filter_entry_by_dso(struct hists *hists, |
@@ -82,6 +83,9 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
82 | hists__new_col_len(hists, HISTC_DSO, len); | 83 | hists__new_col_len(hists, HISTC_DSO, len); |
83 | } | 84 | } |
84 | 85 | ||
86 | if (h->parent) | ||
87 | hists__new_col_len(hists, HISTC_PARENT, h->parent->namelen); | ||
88 | |||
85 | if (h->branch_info) { | 89 | if (h->branch_info) { |
86 | int symlen; | 90 | int symlen; |
87 | /* | 91 | /* |
@@ -242,14 +246,24 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
242 | 246 | ||
243 | if (he->ms.map) | 247 | if (he->ms.map) |
244 | he->ms.map->referenced = true; | 248 | he->ms.map->referenced = true; |
249 | |||
250 | if (he->branch_info) { | ||
251 | if (he->branch_info->from.map) | ||
252 | he->branch_info->from.map->referenced = true; | ||
253 | if (he->branch_info->to.map) | ||
254 | he->branch_info->to.map->referenced = true; | ||
255 | } | ||
256 | |||
245 | if (symbol_conf.use_callchain) | 257 | if (symbol_conf.use_callchain) |
246 | callchain_init(he->callchain); | 258 | callchain_init(he->callchain); |
259 | |||
260 | INIT_LIST_HEAD(&he->pairs.node); | ||
247 | } | 261 | } |
248 | 262 | ||
249 | return he; | 263 | return he; |
250 | } | 264 | } |
251 | 265 | ||
252 | static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) | 266 | void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) |
253 | { | 267 | { |
254 | if (!h->filtered) { | 268 | if (!h->filtered) { |
255 | hists__calc_col_len(hists, h); | 269 | hists__calc_col_len(hists, h); |
@@ -283,7 +297,13 @@ static struct hist_entry *add_hist_entry(struct hists *hists, | |||
283 | parent = *p; | 297 | parent = *p; |
284 | he = rb_entry(parent, struct hist_entry, rb_node_in); | 298 | he = rb_entry(parent, struct hist_entry, rb_node_in); |
285 | 299 | ||
286 | cmp = hist_entry__cmp(entry, he); | 300 | /* |
301 | * Make sure that it receives arguments in a same order as | ||
302 | * hist_entry__collapse() so that we can use an appropriate | ||
303 | * function when searching an entry regardless which sort | ||
304 | * keys were used. | ||
305 | */ | ||
306 | cmp = hist_entry__cmp(he, entry); | ||
287 | 307 | ||
288 | if (!cmp) { | 308 | if (!cmp) { |
289 | he_stat__add_period(&he->stat, period); | 309 | he_stat__add_period(&he->stat, period); |
@@ -410,6 +430,7 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) | |||
410 | 430 | ||
411 | void hist_entry__free(struct hist_entry *he) | 431 | void hist_entry__free(struct hist_entry *he) |
412 | { | 432 | { |
433 | free(he->branch_info); | ||
413 | free(he); | 434 | free(he); |
414 | } | 435 | } |
415 | 436 | ||
@@ -520,6 +541,62 @@ void hists__collapse_resort_threaded(struct hists *hists) | |||
520 | * reverse the map, sort on period. | 541 | * reverse the map, sort on period. |
521 | */ | 542 | */ |
522 | 543 | ||
544 | static int period_cmp(u64 period_a, u64 period_b) | ||
545 | { | ||
546 | if (period_a > period_b) | ||
547 | return 1; | ||
548 | if (period_a < period_b) | ||
549 | return -1; | ||
550 | return 0; | ||
551 | } | ||
552 | |||
553 | static int hist_entry__sort_on_period(struct hist_entry *a, | ||
554 | struct hist_entry *b) | ||
555 | { | ||
556 | int ret; | ||
557 | int i, nr_members; | ||
558 | struct perf_evsel *evsel; | ||
559 | struct hist_entry *pair; | ||
560 | u64 *periods_a, *periods_b; | ||
561 | |||
562 | ret = period_cmp(a->stat.period, b->stat.period); | ||
563 | if (ret || !symbol_conf.event_group) | ||
564 | return ret; | ||
565 | |||
566 | evsel = hists_to_evsel(a->hists); | ||
567 | nr_members = evsel->nr_members; | ||
568 | if (nr_members <= 1) | ||
569 | return ret; | ||
570 | |||
571 | periods_a = zalloc(sizeof(periods_a) * nr_members); | ||
572 | periods_b = zalloc(sizeof(periods_b) * nr_members); | ||
573 | |||
574 | if (!periods_a || !periods_b) | ||
575 | goto out; | ||
576 | |||
577 | list_for_each_entry(pair, &a->pairs.head, pairs.node) { | ||
578 | evsel = hists_to_evsel(pair->hists); | ||
579 | periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period; | ||
580 | } | ||
581 | |||
582 | list_for_each_entry(pair, &b->pairs.head, pairs.node) { | ||
583 | evsel = hists_to_evsel(pair->hists); | ||
584 | periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period; | ||
585 | } | ||
586 | |||
587 | for (i = 1; i < nr_members; i++) { | ||
588 | ret = period_cmp(periods_a[i], periods_b[i]); | ||
589 | if (ret) | ||
590 | break; | ||
591 | } | ||
592 | |||
593 | out: | ||
594 | free(periods_a); | ||
595 | free(periods_b); | ||
596 | |||
597 | return ret; | ||
598 | } | ||
599 | |||
523 | static void __hists__insert_output_entry(struct rb_root *entries, | 600 | static void __hists__insert_output_entry(struct rb_root *entries, |
524 | struct hist_entry *he, | 601 | struct hist_entry *he, |
525 | u64 min_callchain_hits) | 602 | u64 min_callchain_hits) |
@@ -536,7 +613,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, | |||
536 | parent = *p; | 613 | parent = *p; |
537 | iter = rb_entry(parent, struct hist_entry, rb_node); | 614 | iter = rb_entry(parent, struct hist_entry, rb_node); |
538 | 615 | ||
539 | if (he->stat.period > iter->stat.period) | 616 | if (hist_entry__sort_on_period(he, iter) > 0) |
540 | p = &(*p)->rb_left; | 617 | p = &(*p)->rb_left; |
541 | else | 618 | else |
542 | p = &(*p)->rb_right; | 619 | p = &(*p)->rb_right; |
@@ -708,8 +785,134 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize) | |||
708 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); | 785 | return symbol__annotate(he->ms.sym, he->ms.map, privsize); |
709 | } | 786 | } |
710 | 787 | ||
788 | void events_stats__inc(struct events_stats *stats, u32 type) | ||
789 | { | ||
790 | ++stats->nr_events[0]; | ||
791 | ++stats->nr_events[type]; | ||
792 | } | ||
793 | |||
711 | void hists__inc_nr_events(struct hists *hists, u32 type) | 794 | void hists__inc_nr_events(struct hists *hists, u32 type) |
712 | { | 795 | { |
713 | ++hists->stats.nr_events[0]; | 796 | events_stats__inc(&hists->stats, type); |
714 | ++hists->stats.nr_events[type]; | 797 | } |
798 | |||
799 | static struct hist_entry *hists__add_dummy_entry(struct hists *hists, | ||
800 | struct hist_entry *pair) | ||
801 | { | ||
802 | struct rb_root *root; | ||
803 | struct rb_node **p; | ||
804 | struct rb_node *parent = NULL; | ||
805 | struct hist_entry *he; | ||
806 | int cmp; | ||
807 | |||
808 | if (sort__need_collapse) | ||
809 | root = &hists->entries_collapsed; | ||
810 | else | ||
811 | root = hists->entries_in; | ||
812 | |||
813 | p = &root->rb_node; | ||
814 | |||
815 | while (*p != NULL) { | ||
816 | parent = *p; | ||
817 | he = rb_entry(parent, struct hist_entry, rb_node_in); | ||
818 | |||
819 | cmp = hist_entry__collapse(he, pair); | ||
820 | |||
821 | if (!cmp) | ||
822 | goto out; | ||
823 | |||
824 | if (cmp < 0) | ||
825 | p = &(*p)->rb_left; | ||
826 | else | ||
827 | p = &(*p)->rb_right; | ||
828 | } | ||
829 | |||
830 | he = hist_entry__new(pair); | ||
831 | if (he) { | ||
832 | memset(&he->stat, 0, sizeof(he->stat)); | ||
833 | he->hists = hists; | ||
834 | rb_link_node(&he->rb_node_in, parent, p); | ||
835 | rb_insert_color(&he->rb_node_in, root); | ||
836 | hists__inc_nr_entries(hists, he); | ||
837 | } | ||
838 | out: | ||
839 | return he; | ||
840 | } | ||
841 | |||
842 | static struct hist_entry *hists__find_entry(struct hists *hists, | ||
843 | struct hist_entry *he) | ||
844 | { | ||
845 | struct rb_node *n; | ||
846 | |||
847 | if (sort__need_collapse) | ||
848 | n = hists->entries_collapsed.rb_node; | ||
849 | else | ||
850 | n = hists->entries_in->rb_node; | ||
851 | |||
852 | while (n) { | ||
853 | struct hist_entry *iter = rb_entry(n, struct hist_entry, rb_node_in); | ||
854 | int64_t cmp = hist_entry__collapse(iter, he); | ||
855 | |||
856 | if (cmp < 0) | ||
857 | n = n->rb_left; | ||
858 | else if (cmp > 0) | ||
859 | n = n->rb_right; | ||
860 | else | ||
861 | return iter; | ||
862 | } | ||
863 | |||
864 | return NULL; | ||
865 | } | ||
866 | |||
867 | /* | ||
868 | * Look for pairs to link to the leader buckets (hist_entries): | ||
869 | */ | ||
870 | void hists__match(struct hists *leader, struct hists *other) | ||
871 | { | ||
872 | struct rb_root *root; | ||
873 | struct rb_node *nd; | ||
874 | struct hist_entry *pos, *pair; | ||
875 | |||
876 | if (sort__need_collapse) | ||
877 | root = &leader->entries_collapsed; | ||
878 | else | ||
879 | root = leader->entries_in; | ||
880 | |||
881 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
882 | pos = rb_entry(nd, struct hist_entry, rb_node_in); | ||
883 | pair = hists__find_entry(other, pos); | ||
884 | |||
885 | if (pair) | ||
886 | hist_entry__add_pair(pair, pos); | ||
887 | } | ||
888 | } | ||
889 | |||
890 | /* | ||
891 | * Look for entries in the other hists that are not present in the leader, if | ||
892 | * we find them, just add a dummy entry on the leader hists, with period=0, | ||
893 | * nr_events=0, to serve as the list header. | ||
894 | */ | ||
895 | int hists__link(struct hists *leader, struct hists *other) | ||
896 | { | ||
897 | struct rb_root *root; | ||
898 | struct rb_node *nd; | ||
899 | struct hist_entry *pos, *pair; | ||
900 | |||
901 | if (sort__need_collapse) | ||
902 | root = &other->entries_collapsed; | ||
903 | else | ||
904 | root = other->entries_in; | ||
905 | |||
906 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
907 | pos = rb_entry(nd, struct hist_entry, rb_node_in); | ||
908 | |||
909 | if (!hist_entry__has_pairs(pos)) { | ||
910 | pair = hists__add_dummy_entry(leader, pos); | ||
911 | if (pair == NULL) | ||
912 | return -1; | ||
913 | hist_entry__add_pair(pos, pair); | ||
914 | } | ||
915 | } | ||
916 | |||
917 | return 0; | ||
715 | } | 918 | } |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 66cb31fe81d2..38624686ee9a 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -4,6 +4,7 @@ | |||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <pthread.h> | 5 | #include <pthread.h> |
6 | #include "callchain.h" | 6 | #include "callchain.h" |
7 | #include "header.h" | ||
7 | 8 | ||
8 | extern struct callchain_param callchain_param; | 9 | extern struct callchain_param callchain_param; |
9 | 10 | ||
@@ -95,8 +96,10 @@ void hists__decay_entries_threaded(struct hists *hists, bool zap_user, | |||
95 | bool zap_kernel); | 96 | bool zap_kernel); |
96 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); | 97 | void hists__output_recalc_col_len(struct hists *hists, int max_rows); |
97 | 98 | ||
99 | void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h); | ||
98 | void hists__inc_nr_events(struct hists *self, u32 type); | 100 | void hists__inc_nr_events(struct hists *self, u32 type); |
99 | size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); | 101 | void events_stats__inc(struct events_stats *stats, u32 type); |
102 | size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); | ||
100 | 103 | ||
101 | size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, | 104 | size_t hists__fprintf(struct hists *self, bool show_header, int max_rows, |
102 | int max_cols, FILE *fp); | 105 | int max_cols, FILE *fp); |
@@ -114,6 +117,9 @@ bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | |||
114 | void hists__reset_col_len(struct hists *hists); | 117 | void hists__reset_col_len(struct hists *hists); |
115 | void hists__calc_col_len(struct hists *hists, struct hist_entry *he); | 118 | void hists__calc_col_len(struct hists *hists, struct hist_entry *he); |
116 | 119 | ||
120 | void hists__match(struct hists *leader, struct hists *other); | ||
121 | int hists__link(struct hists *leader, struct hists *other); | ||
122 | |||
117 | struct perf_hpp { | 123 | struct perf_hpp { |
118 | char *buf; | 124 | char *buf; |
119 | size_t size; | 125 | size_t size; |
@@ -122,13 +128,19 @@ struct perf_hpp { | |||
122 | }; | 128 | }; |
123 | 129 | ||
124 | struct perf_hpp_fmt { | 130 | struct perf_hpp_fmt { |
125 | bool cond; | ||
126 | int (*header)(struct perf_hpp *hpp); | 131 | int (*header)(struct perf_hpp *hpp); |
127 | int (*width)(struct perf_hpp *hpp); | 132 | int (*width)(struct perf_hpp *hpp); |
128 | int (*color)(struct perf_hpp *hpp, struct hist_entry *he); | 133 | int (*color)(struct perf_hpp *hpp, struct hist_entry *he); |
129 | int (*entry)(struct perf_hpp *hpp, struct hist_entry *he); | 134 | int (*entry)(struct perf_hpp *hpp, struct hist_entry *he); |
135 | |||
136 | struct list_head list; | ||
130 | }; | 137 | }; |
131 | 138 | ||
139 | extern struct list_head perf_hpp__list; | ||
140 | |||
141 | #define perf_hpp__for_each_format(format) \ | ||
142 | list_for_each_entry(format, &perf_hpp__list, list) | ||
143 | |||
132 | extern struct perf_hpp_fmt perf_hpp__format[]; | 144 | extern struct perf_hpp_fmt perf_hpp__format[]; |
133 | 145 | ||
134 | enum { | 146 | enum { |
@@ -140,34 +152,44 @@ enum { | |||
140 | PERF_HPP__OVERHEAD_GUEST_US, | 152 | PERF_HPP__OVERHEAD_GUEST_US, |
141 | PERF_HPP__SAMPLES, | 153 | PERF_HPP__SAMPLES, |
142 | PERF_HPP__PERIOD, | 154 | PERF_HPP__PERIOD, |
155 | PERF_HPP__PERIOD_BASELINE, | ||
143 | PERF_HPP__DELTA, | 156 | PERF_HPP__DELTA, |
144 | PERF_HPP__DISPL, | 157 | PERF_HPP__RATIO, |
158 | PERF_HPP__WEIGHTED_DIFF, | ||
159 | PERF_HPP__FORMULA, | ||
145 | 160 | ||
146 | PERF_HPP__MAX_INDEX | 161 | PERF_HPP__MAX_INDEX |
147 | }; | 162 | }; |
148 | 163 | ||
149 | void perf_hpp__init(void); | 164 | void perf_hpp__init(void); |
150 | void perf_hpp__column_enable(unsigned col, bool enable); | 165 | void perf_hpp__column_register(struct perf_hpp_fmt *format); |
166 | void perf_hpp__column_enable(unsigned col); | ||
151 | int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, | 167 | int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, |
152 | bool color); | 168 | bool color); |
153 | 169 | ||
154 | struct perf_evlist; | 170 | struct perf_evlist; |
155 | 171 | ||
172 | struct hist_browser_timer { | ||
173 | void (*timer)(void *arg); | ||
174 | void *arg; | ||
175 | int refresh; | ||
176 | }; | ||
177 | |||
156 | #ifdef NEWT_SUPPORT | 178 | #ifdef NEWT_SUPPORT |
157 | #include "../ui/keysyms.h" | 179 | #include "../ui/keysyms.h" |
158 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, | 180 | int hist_entry__tui_annotate(struct hist_entry *he, int evidx, |
159 | void(*timer)(void *arg), void *arg, int delay_secs); | 181 | struct hist_browser_timer *hbt); |
160 | 182 | ||
161 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | 183 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, |
162 | void(*timer)(void *arg), void *arg, | 184 | struct hist_browser_timer *hbt, |
163 | int refresh); | 185 | struct perf_session_env *env); |
186 | int script_browse(const char *script_opt); | ||
164 | #else | 187 | #else |
165 | static inline | 188 | static inline |
166 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, | 189 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, |
167 | const char *help __maybe_unused, | 190 | const char *help __maybe_unused, |
168 | void(*timer)(void *arg) __maybe_unused, | 191 | struct hist_browser_timer *hbt __maybe_unused, |
169 | void *arg __maybe_unused, | 192 | struct perf_session_env *env __maybe_unused) |
170 | int refresh __maybe_unused) | ||
171 | { | 193 | { |
172 | return 0; | 194 | return 0; |
173 | } | 195 | } |
@@ -175,28 +197,29 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused, | |||
175 | static inline int hist_entry__tui_annotate(struct hist_entry *self | 197 | static inline int hist_entry__tui_annotate(struct hist_entry *self |
176 | __maybe_unused, | 198 | __maybe_unused, |
177 | int evidx __maybe_unused, | 199 | int evidx __maybe_unused, |
178 | void(*timer)(void *arg) | 200 | struct hist_browser_timer *hbt |
179 | __maybe_unused, | 201 | __maybe_unused) |
180 | void *arg __maybe_unused, | 202 | { |
181 | int delay_secs __maybe_unused) | 203 | return 0; |
204 | } | ||
205 | |||
206 | static inline int script_browse(const char *script_opt __maybe_unused) | ||
182 | { | 207 | { |
183 | return 0; | 208 | return 0; |
184 | } | 209 | } |
210 | |||
185 | #define K_LEFT -1 | 211 | #define K_LEFT -1 |
186 | #define K_RIGHT -2 | 212 | #define K_RIGHT -2 |
187 | #endif | 213 | #endif |
188 | 214 | ||
189 | #ifdef GTK2_SUPPORT | 215 | #ifdef GTK2_SUPPORT |
190 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, | 216 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, |
191 | void(*timer)(void *arg), void *arg, | 217 | struct hist_browser_timer *hbt __maybe_unused); |
192 | int refresh); | ||
193 | #else | 218 | #else |
194 | static inline | 219 | static inline |
195 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, | 220 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, |
196 | const char *help __maybe_unused, | 221 | const char *help __maybe_unused, |
197 | void(*timer)(void *arg) __maybe_unused, | 222 | struct hist_browser_timer *hbt __maybe_unused) |
198 | void *arg __maybe_unused, | ||
199 | int refresh __maybe_unused) | ||
200 | { | 223 | { |
201 | return 0; | 224 | return 0; |
202 | } | 225 | } |
@@ -204,4 +227,10 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, | |||
204 | 227 | ||
205 | unsigned int hists__sort_list_width(struct hists *self); | 228 | unsigned int hists__sort_list_width(struct hists *self); |
206 | 229 | ||
230 | double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair); | ||
231 | double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair); | ||
232 | s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair); | ||
233 | int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, | ||
234 | char *buf, size_t size); | ||
235 | double perf_diff__period_percent(struct hist_entry *he, u64 period); | ||
207 | #endif /* __PERF_HIST_H */ | 236 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index a55d8cf083c9..45cf10a562bd 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h | |||
@@ -14,6 +14,7 @@ | |||
14 | #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) | 14 | #define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long)) |
15 | #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) | 15 | #define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64)) |
16 | #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) | 16 | #define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32)) |
17 | #define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) | ||
17 | 18 | ||
18 | #define for_each_set_bit(bit, addr, size) \ | 19 | #define for_each_set_bit(bit, addr, size) \ |
19 | for ((bit) = find_first_bit((addr), (size)); \ | 20 | for ((bit) = find_first_bit((addr), (size)); \ |
diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c index 9d0740024ba8..11a8d86f7fea 100644 --- a/tools/perf/util/intlist.c +++ b/tools/perf/util/intlist.c | |||
@@ -59,16 +59,40 @@ void intlist__remove(struct intlist *ilist, struct int_node *node) | |||
59 | 59 | ||
60 | struct int_node *intlist__find(struct intlist *ilist, int i) | 60 | struct int_node *intlist__find(struct intlist *ilist, int i) |
61 | { | 61 | { |
62 | struct int_node *node = NULL; | 62 | struct int_node *node; |
63 | struct rb_node *rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); | 63 | struct rb_node *rb_node; |
64 | 64 | ||
65 | if (ilist == NULL) | ||
66 | return NULL; | ||
67 | |||
68 | node = NULL; | ||
69 | rb_node = rblist__find(&ilist->rblist, (void *)((long)i)); | ||
65 | if (rb_node) | 70 | if (rb_node) |
66 | node = container_of(rb_node, struct int_node, rb_node); | 71 | node = container_of(rb_node, struct int_node, rb_node); |
67 | 72 | ||
68 | return node; | 73 | return node; |
69 | } | 74 | } |
70 | 75 | ||
71 | struct intlist *intlist__new(void) | 76 | static int intlist__parse_list(struct intlist *ilist, const char *s) |
77 | { | ||
78 | char *sep; | ||
79 | int err; | ||
80 | |||
81 | do { | ||
82 | long value = strtol(s, &sep, 10); | ||
83 | err = -EINVAL; | ||
84 | if (*sep != ',' && *sep != '\0') | ||
85 | break; | ||
86 | err = intlist__add(ilist, value); | ||
87 | if (err) | ||
88 | break; | ||
89 | s = sep + 1; | ||
90 | } while (*sep != '\0'); | ||
91 | |||
92 | return err; | ||
93 | } | ||
94 | |||
95 | struct intlist *intlist__new(const char *slist) | ||
72 | { | 96 | { |
73 | struct intlist *ilist = malloc(sizeof(*ilist)); | 97 | struct intlist *ilist = malloc(sizeof(*ilist)); |
74 | 98 | ||
@@ -77,9 +101,15 @@ struct intlist *intlist__new(void) | |||
77 | ilist->rblist.node_cmp = intlist__node_cmp; | 101 | ilist->rblist.node_cmp = intlist__node_cmp; |
78 | ilist->rblist.node_new = intlist__node_new; | 102 | ilist->rblist.node_new = intlist__node_new; |
79 | ilist->rblist.node_delete = intlist__node_delete; | 103 | ilist->rblist.node_delete = intlist__node_delete; |
104 | |||
105 | if (slist && intlist__parse_list(ilist, slist)) | ||
106 | goto out_delete; | ||
80 | } | 107 | } |
81 | 108 | ||
82 | return ilist; | 109 | return ilist; |
110 | out_delete: | ||
111 | intlist__delete(ilist); | ||
112 | return NULL; | ||
83 | } | 113 | } |
84 | 114 | ||
85 | void intlist__delete(struct intlist *ilist) | 115 | void intlist__delete(struct intlist *ilist) |
diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h index 6d63ab90db50..62351dad848f 100644 --- a/tools/perf/util/intlist.h +++ b/tools/perf/util/intlist.h | |||
@@ -15,7 +15,7 @@ struct intlist { | |||
15 | struct rblist rblist; | 15 | struct rblist rblist; |
16 | }; | 16 | }; |
17 | 17 | ||
18 | struct intlist *intlist__new(void); | 18 | struct intlist *intlist__new(const char *slist); |
19 | void intlist__delete(struct intlist *ilist); | 19 | void intlist__delete(struct intlist *ilist); |
20 | 20 | ||
21 | void intlist__remove(struct intlist *ilist, struct int_node *in); | 21 | void intlist__remove(struct intlist *ilist, struct int_node *in); |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c new file mode 100644 index 000000000000..efdb38e65a92 --- /dev/null +++ b/tools/perf/util/machine.c | |||
@@ -0,0 +1,1226 @@ | |||
1 | #include "callchain.h" | ||
2 | #include "debug.h" | ||
3 | #include "event.h" | ||
4 | #include "evsel.h" | ||
5 | #include "hist.h" | ||
6 | #include "machine.h" | ||
7 | #include "map.h" | ||
8 | #include "sort.h" | ||
9 | #include "strlist.h" | ||
10 | #include "thread.h" | ||
11 | #include <stdbool.h> | ||
12 | #include "unwind.h" | ||
13 | |||
14 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | ||
15 | { | ||
16 | map_groups__init(&machine->kmaps); | ||
17 | RB_CLEAR_NODE(&machine->rb_node); | ||
18 | INIT_LIST_HEAD(&machine->user_dsos); | ||
19 | INIT_LIST_HEAD(&machine->kernel_dsos); | ||
20 | |||
21 | machine->threads = RB_ROOT; | ||
22 | INIT_LIST_HEAD(&machine->dead_threads); | ||
23 | machine->last_match = NULL; | ||
24 | |||
25 | machine->kmaps.machine = machine; | ||
26 | machine->pid = pid; | ||
27 | |||
28 | machine->root_dir = strdup(root_dir); | ||
29 | if (machine->root_dir == NULL) | ||
30 | return -ENOMEM; | ||
31 | |||
32 | if (pid != HOST_KERNEL_ID) { | ||
33 | struct thread *thread = machine__findnew_thread(machine, pid); | ||
34 | char comm[64]; | ||
35 | |||
36 | if (thread == NULL) | ||
37 | return -ENOMEM; | ||
38 | |||
39 | snprintf(comm, sizeof(comm), "[guest/%d]", pid); | ||
40 | thread__set_comm(thread, comm); | ||
41 | } | ||
42 | |||
43 | return 0; | ||
44 | } | ||
45 | |||
46 | static void dsos__delete(struct list_head *dsos) | ||
47 | { | ||
48 | struct dso *pos, *n; | ||
49 | |||
50 | list_for_each_entry_safe(pos, n, dsos, node) { | ||
51 | list_del(&pos->node); | ||
52 | dso__delete(pos); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | void machine__delete_dead_threads(struct machine *machine) | ||
57 | { | ||
58 | struct thread *n, *t; | ||
59 | |||
60 | list_for_each_entry_safe(t, n, &machine->dead_threads, node) { | ||
61 | list_del(&t->node); | ||
62 | thread__delete(t); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | void machine__delete_threads(struct machine *machine) | ||
67 | { | ||
68 | struct rb_node *nd = rb_first(&machine->threads); | ||
69 | |||
70 | while (nd) { | ||
71 | struct thread *t = rb_entry(nd, struct thread, rb_node); | ||
72 | |||
73 | rb_erase(&t->rb_node, &machine->threads); | ||
74 | nd = rb_next(nd); | ||
75 | thread__delete(t); | ||
76 | } | ||
77 | } | ||
78 | |||
79 | void machine__exit(struct machine *machine) | ||
80 | { | ||
81 | map_groups__exit(&machine->kmaps); | ||
82 | dsos__delete(&machine->user_dsos); | ||
83 | dsos__delete(&machine->kernel_dsos); | ||
84 | free(machine->root_dir); | ||
85 | machine->root_dir = NULL; | ||
86 | } | ||
87 | |||
88 | void machine__delete(struct machine *machine) | ||
89 | { | ||
90 | machine__exit(machine); | ||
91 | free(machine); | ||
92 | } | ||
93 | |||
94 | void machines__init(struct machines *machines) | ||
95 | { | ||
96 | machine__init(&machines->host, "", HOST_KERNEL_ID); | ||
97 | machines->guests = RB_ROOT; | ||
98 | } | ||
99 | |||
100 | void machines__exit(struct machines *machines) | ||
101 | { | ||
102 | machine__exit(&machines->host); | ||
103 | /* XXX exit guest */ | ||
104 | } | ||
105 | |||
106 | struct machine *machines__add(struct machines *machines, pid_t pid, | ||
107 | const char *root_dir) | ||
108 | { | ||
109 | struct rb_node **p = &machines->guests.rb_node; | ||
110 | struct rb_node *parent = NULL; | ||
111 | struct machine *pos, *machine = malloc(sizeof(*machine)); | ||
112 | |||
113 | if (machine == NULL) | ||
114 | return NULL; | ||
115 | |||
116 | if (machine__init(machine, root_dir, pid) != 0) { | ||
117 | free(machine); | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | while (*p != NULL) { | ||
122 | parent = *p; | ||
123 | pos = rb_entry(parent, struct machine, rb_node); | ||
124 | if (pid < pos->pid) | ||
125 | p = &(*p)->rb_left; | ||
126 | else | ||
127 | p = &(*p)->rb_right; | ||
128 | } | ||
129 | |||
130 | rb_link_node(&machine->rb_node, parent, p); | ||
131 | rb_insert_color(&machine->rb_node, &machines->guests); | ||
132 | |||
133 | return machine; | ||
134 | } | ||
135 | |||
136 | struct machine *machines__find(struct machines *machines, pid_t pid) | ||
137 | { | ||
138 | struct rb_node **p = &machines->guests.rb_node; | ||
139 | struct rb_node *parent = NULL; | ||
140 | struct machine *machine; | ||
141 | struct machine *default_machine = NULL; | ||
142 | |||
143 | if (pid == HOST_KERNEL_ID) | ||
144 | return &machines->host; | ||
145 | |||
146 | while (*p != NULL) { | ||
147 | parent = *p; | ||
148 | machine = rb_entry(parent, struct machine, rb_node); | ||
149 | if (pid < machine->pid) | ||
150 | p = &(*p)->rb_left; | ||
151 | else if (pid > machine->pid) | ||
152 | p = &(*p)->rb_right; | ||
153 | else | ||
154 | return machine; | ||
155 | if (!machine->pid) | ||
156 | default_machine = machine; | ||
157 | } | ||
158 | |||
159 | return default_machine; | ||
160 | } | ||
161 | |||
162 | struct machine *machines__findnew(struct machines *machines, pid_t pid) | ||
163 | { | ||
164 | char path[PATH_MAX]; | ||
165 | const char *root_dir = ""; | ||
166 | struct machine *machine = machines__find(machines, pid); | ||
167 | |||
168 | if (machine && (machine->pid == pid)) | ||
169 | goto out; | ||
170 | |||
171 | if ((pid != HOST_KERNEL_ID) && | ||
172 | (pid != DEFAULT_GUEST_KERNEL_ID) && | ||
173 | (symbol_conf.guestmount)) { | ||
174 | sprintf(path, "%s/%d", symbol_conf.guestmount, pid); | ||
175 | if (access(path, R_OK)) { | ||
176 | static struct strlist *seen; | ||
177 | |||
178 | if (!seen) | ||
179 | seen = strlist__new(true, NULL); | ||
180 | |||
181 | if (!strlist__has_entry(seen, path)) { | ||
182 | pr_err("Can't access file %s\n", path); | ||
183 | strlist__add(seen, path); | ||
184 | } | ||
185 | machine = NULL; | ||
186 | goto out; | ||
187 | } | ||
188 | root_dir = path; | ||
189 | } | ||
190 | |||
191 | machine = machines__add(machines, pid, root_dir); | ||
192 | out: | ||
193 | return machine; | ||
194 | } | ||
195 | |||
196 | void machines__process_guests(struct machines *machines, | ||
197 | machine__process_t process, void *data) | ||
198 | { | ||
199 | struct rb_node *nd; | ||
200 | |||
201 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | ||
202 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
203 | process(pos, data); | ||
204 | } | ||
205 | } | ||
206 | |||
207 | char *machine__mmap_name(struct machine *machine, char *bf, size_t size) | ||
208 | { | ||
209 | if (machine__is_host(machine)) | ||
210 | snprintf(bf, size, "[%s]", "kernel.kallsyms"); | ||
211 | else if (machine__is_default_guest(machine)) | ||
212 | snprintf(bf, size, "[%s]", "guest.kernel.kallsyms"); | ||
213 | else { | ||
214 | snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", | ||
215 | machine->pid); | ||
216 | } | ||
217 | |||
218 | return bf; | ||
219 | } | ||
220 | |||
221 | void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size) | ||
222 | { | ||
223 | struct rb_node *node; | ||
224 | struct machine *machine; | ||
225 | |||
226 | machines->host.id_hdr_size = id_hdr_size; | ||
227 | |||
228 | for (node = rb_first(&machines->guests); node; node = rb_next(node)) { | ||
229 | machine = rb_entry(node, struct machine, rb_node); | ||
230 | machine->id_hdr_size = id_hdr_size; | ||
231 | } | ||
232 | |||
233 | return; | ||
234 | } | ||
235 | |||
236 | static struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, | ||
237 | bool create) | ||
238 | { | ||
239 | struct rb_node **p = &machine->threads.rb_node; | ||
240 | struct rb_node *parent = NULL; | ||
241 | struct thread *th; | ||
242 | |||
243 | /* | ||
244 | * Font-end cache - PID lookups come in blocks, | ||
245 | * so most of the time we dont have to look up | ||
246 | * the full rbtree: | ||
247 | */ | ||
248 | if (machine->last_match && machine->last_match->pid == pid) | ||
249 | return machine->last_match; | ||
250 | |||
251 | while (*p != NULL) { | ||
252 | parent = *p; | ||
253 | th = rb_entry(parent, struct thread, rb_node); | ||
254 | |||
255 | if (th->pid == pid) { | ||
256 | machine->last_match = th; | ||
257 | return th; | ||
258 | } | ||
259 | |||
260 | if (pid < th->pid) | ||
261 | p = &(*p)->rb_left; | ||
262 | else | ||
263 | p = &(*p)->rb_right; | ||
264 | } | ||
265 | |||
266 | if (!create) | ||
267 | return NULL; | ||
268 | |||
269 | th = thread__new(pid); | ||
270 | if (th != NULL) { | ||
271 | rb_link_node(&th->rb_node, parent, p); | ||
272 | rb_insert_color(&th->rb_node, &machine->threads); | ||
273 | machine->last_match = th; | ||
274 | } | ||
275 | |||
276 | return th; | ||
277 | } | ||
278 | |||
279 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid) | ||
280 | { | ||
281 | return __machine__findnew_thread(machine, pid, true); | ||
282 | } | ||
283 | |||
284 | struct thread *machine__find_thread(struct machine *machine, pid_t pid) | ||
285 | { | ||
286 | return __machine__findnew_thread(machine, pid, false); | ||
287 | } | ||
288 | |||
289 | int machine__process_comm_event(struct machine *machine, union perf_event *event) | ||
290 | { | ||
291 | struct thread *thread = machine__findnew_thread(machine, event->comm.tid); | ||
292 | |||
293 | if (dump_trace) | ||
294 | perf_event__fprintf_comm(event, stdout); | ||
295 | |||
296 | if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { | ||
297 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | ||
298 | return -1; | ||
299 | } | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | int machine__process_lost_event(struct machine *machine __maybe_unused, | ||
305 | union perf_event *event) | ||
306 | { | ||
307 | dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n", | ||
308 | event->lost.id, event->lost.lost); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | struct map *machine__new_module(struct machine *machine, u64 start, | ||
313 | const char *filename) | ||
314 | { | ||
315 | struct map *map; | ||
316 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | ||
317 | |||
318 | if (dso == NULL) | ||
319 | return NULL; | ||
320 | |||
321 | map = map__new2(start, dso, MAP__FUNCTION); | ||
322 | if (map == NULL) | ||
323 | return NULL; | ||
324 | |||
325 | if (machine__is_host(machine)) | ||
326 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; | ||
327 | else | ||
328 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; | ||
329 | map_groups__insert(&machine->kmaps, map); | ||
330 | return map; | ||
331 | } | ||
332 | |||
333 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp) | ||
334 | { | ||
335 | struct rb_node *nd; | ||
336 | size_t ret = __dsos__fprintf(&machines->host.kernel_dsos, fp) + | ||
337 | __dsos__fprintf(&machines->host.user_dsos, fp); | ||
338 | |||
339 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | ||
340 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
341 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | ||
342 | ret += __dsos__fprintf(&pos->user_dsos, fp); | ||
343 | } | ||
344 | |||
345 | return ret; | ||
346 | } | ||
347 | |||
348 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | ||
349 | bool (skip)(struct dso *dso, int parm), int parm) | ||
350 | { | ||
351 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, skip, parm) + | ||
352 | __dsos__fprintf_buildid(&machine->user_dsos, fp, skip, parm); | ||
353 | } | ||
354 | |||
355 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, | ||
356 | bool (skip)(struct dso *dso, int parm), int parm) | ||
357 | { | ||
358 | struct rb_node *nd; | ||
359 | size_t ret = machine__fprintf_dsos_buildid(&machines->host, fp, skip, parm); | ||
360 | |||
361 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | ||
362 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
363 | ret += machine__fprintf_dsos_buildid(pos, fp, skip, parm); | ||
364 | } | ||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) | ||
369 | { | ||
370 | int i; | ||
371 | size_t printed = 0; | ||
372 | struct dso *kdso = machine->vmlinux_maps[MAP__FUNCTION]->dso; | ||
373 | |||
374 | if (kdso->has_build_id) { | ||
375 | char filename[PATH_MAX]; | ||
376 | if (dso__build_id_filename(kdso, filename, sizeof(filename))) | ||
377 | printed += fprintf(fp, "[0] %s\n", filename); | ||
378 | } | ||
379 | |||
380 | for (i = 0; i < vmlinux_path__nr_entries; ++i) | ||
381 | printed += fprintf(fp, "[%d] %s\n", | ||
382 | i + kdso->has_build_id, vmlinux_path[i]); | ||
383 | |||
384 | return printed; | ||
385 | } | ||
386 | |||
387 | size_t machine__fprintf(struct machine *machine, FILE *fp) | ||
388 | { | ||
389 | size_t ret = 0; | ||
390 | struct rb_node *nd; | ||
391 | |||
392 | for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { | ||
393 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | ||
394 | |||
395 | ret += thread__fprintf(pos, fp); | ||
396 | } | ||
397 | |||
398 | return ret; | ||
399 | } | ||
400 | |||
401 | static struct dso *machine__get_kernel(struct machine *machine) | ||
402 | { | ||
403 | const char *vmlinux_name = NULL; | ||
404 | struct dso *kernel; | ||
405 | |||
406 | if (machine__is_host(machine)) { | ||
407 | vmlinux_name = symbol_conf.vmlinux_name; | ||
408 | if (!vmlinux_name) | ||
409 | vmlinux_name = "[kernel.kallsyms]"; | ||
410 | |||
411 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
412 | "[kernel]", | ||
413 | DSO_TYPE_KERNEL); | ||
414 | } else { | ||
415 | char bf[PATH_MAX]; | ||
416 | |||
417 | if (machine__is_default_guest(machine)) | ||
418 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; | ||
419 | if (!vmlinux_name) | ||
420 | vmlinux_name = machine__mmap_name(machine, bf, | ||
421 | sizeof(bf)); | ||
422 | |||
423 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
424 | "[guest.kernel]", | ||
425 | DSO_TYPE_GUEST_KERNEL); | ||
426 | } | ||
427 | |||
428 | if (kernel != NULL && (!kernel->has_build_id)) | ||
429 | dso__read_running_kernel_build_id(kernel, machine); | ||
430 | |||
431 | return kernel; | ||
432 | } | ||
433 | |||
434 | struct process_args { | ||
435 | u64 start; | ||
436 | }; | ||
437 | |||
438 | static int symbol__in_kernel(void *arg, const char *name, | ||
439 | char type __maybe_unused, u64 start) | ||
440 | { | ||
441 | struct process_args *args = arg; | ||
442 | |||
443 | if (strchr(name, '[')) | ||
444 | return 0; | ||
445 | |||
446 | args->start = start; | ||
447 | return 1; | ||
448 | } | ||
449 | |||
450 | /* Figure out the start address of kernel map from /proc/kallsyms */ | ||
451 | static u64 machine__get_kernel_start_addr(struct machine *machine) | ||
452 | { | ||
453 | const char *filename; | ||
454 | char path[PATH_MAX]; | ||
455 | struct process_args args; | ||
456 | |||
457 | if (machine__is_host(machine)) { | ||
458 | filename = "/proc/kallsyms"; | ||
459 | } else { | ||
460 | if (machine__is_default_guest(machine)) | ||
461 | filename = (char *)symbol_conf.default_guest_kallsyms; | ||
462 | else { | ||
463 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); | ||
464 | filename = path; | ||
465 | } | ||
466 | } | ||
467 | |||
468 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) | ||
469 | return 0; | ||
470 | |||
471 | if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) | ||
472 | return 0; | ||
473 | |||
474 | return args.start; | ||
475 | } | ||
476 | |||
477 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | ||
478 | { | ||
479 | enum map_type type; | ||
480 | u64 start = machine__get_kernel_start_addr(machine); | ||
481 | |||
482 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
483 | struct kmap *kmap; | ||
484 | |||
485 | machine->vmlinux_maps[type] = map__new2(start, kernel, type); | ||
486 | if (machine->vmlinux_maps[type] == NULL) | ||
487 | return -1; | ||
488 | |||
489 | machine->vmlinux_maps[type]->map_ip = | ||
490 | machine->vmlinux_maps[type]->unmap_ip = | ||
491 | identity__map_ip; | ||
492 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
493 | kmap->kmaps = &machine->kmaps; | ||
494 | map_groups__insert(&machine->kmaps, | ||
495 | machine->vmlinux_maps[type]); | ||
496 | } | ||
497 | |||
498 | return 0; | ||
499 | } | ||
500 | |||
501 | void machine__destroy_kernel_maps(struct machine *machine) | ||
502 | { | ||
503 | enum map_type type; | ||
504 | |||
505 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
506 | struct kmap *kmap; | ||
507 | |||
508 | if (machine->vmlinux_maps[type] == NULL) | ||
509 | continue; | ||
510 | |||
511 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
512 | map_groups__remove(&machine->kmaps, | ||
513 | machine->vmlinux_maps[type]); | ||
514 | if (kmap->ref_reloc_sym) { | ||
515 | /* | ||
516 | * ref_reloc_sym is shared among all maps, so free just | ||
517 | * on one of them. | ||
518 | */ | ||
519 | if (type == MAP__FUNCTION) { | ||
520 | free((char *)kmap->ref_reloc_sym->name); | ||
521 | kmap->ref_reloc_sym->name = NULL; | ||
522 | free(kmap->ref_reloc_sym); | ||
523 | } | ||
524 | kmap->ref_reloc_sym = NULL; | ||
525 | } | ||
526 | |||
527 | map__delete(machine->vmlinux_maps[type]); | ||
528 | machine->vmlinux_maps[type] = NULL; | ||
529 | } | ||
530 | } | ||
531 | |||
532 | int machines__create_guest_kernel_maps(struct machines *machines) | ||
533 | { | ||
534 | int ret = 0; | ||
535 | struct dirent **namelist = NULL; | ||
536 | int i, items = 0; | ||
537 | char path[PATH_MAX]; | ||
538 | pid_t pid; | ||
539 | char *endp; | ||
540 | |||
541 | if (symbol_conf.default_guest_vmlinux_name || | ||
542 | symbol_conf.default_guest_modules || | ||
543 | symbol_conf.default_guest_kallsyms) { | ||
544 | machines__create_kernel_maps(machines, DEFAULT_GUEST_KERNEL_ID); | ||
545 | } | ||
546 | |||
547 | if (symbol_conf.guestmount) { | ||
548 | items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); | ||
549 | if (items <= 0) | ||
550 | return -ENOENT; | ||
551 | for (i = 0; i < items; i++) { | ||
552 | if (!isdigit(namelist[i]->d_name[0])) { | ||
553 | /* Filter out . and .. */ | ||
554 | continue; | ||
555 | } | ||
556 | pid = (pid_t)strtol(namelist[i]->d_name, &endp, 10); | ||
557 | if ((*endp != '\0') || | ||
558 | (endp == namelist[i]->d_name) || | ||
559 | (errno == ERANGE)) { | ||
560 | pr_debug("invalid directory (%s). Skipping.\n", | ||
561 | namelist[i]->d_name); | ||
562 | continue; | ||
563 | } | ||
564 | sprintf(path, "%s/%s/proc/kallsyms", | ||
565 | symbol_conf.guestmount, | ||
566 | namelist[i]->d_name); | ||
567 | ret = access(path, R_OK); | ||
568 | if (ret) { | ||
569 | pr_debug("Can't access file %s\n", path); | ||
570 | goto failure; | ||
571 | } | ||
572 | machines__create_kernel_maps(machines, pid); | ||
573 | } | ||
574 | failure: | ||
575 | free(namelist); | ||
576 | } | ||
577 | |||
578 | return ret; | ||
579 | } | ||
580 | |||
581 | void machines__destroy_kernel_maps(struct machines *machines) | ||
582 | { | ||
583 | struct rb_node *next = rb_first(&machines->guests); | ||
584 | |||
585 | machine__destroy_kernel_maps(&machines->host); | ||
586 | |||
587 | while (next) { | ||
588 | struct machine *pos = rb_entry(next, struct machine, rb_node); | ||
589 | |||
590 | next = rb_next(&pos->rb_node); | ||
591 | rb_erase(&pos->rb_node, &machines->guests); | ||
592 | machine__delete(pos); | ||
593 | } | ||
594 | } | ||
595 | |||
596 | int machines__create_kernel_maps(struct machines *machines, pid_t pid) | ||
597 | { | ||
598 | struct machine *machine = machines__findnew(machines, pid); | ||
599 | |||
600 | if (machine == NULL) | ||
601 | return -1; | ||
602 | |||
603 | return machine__create_kernel_maps(machine); | ||
604 | } | ||
605 | |||
606 | int machine__load_kallsyms(struct machine *machine, const char *filename, | ||
607 | enum map_type type, symbol_filter_t filter) | ||
608 | { | ||
609 | struct map *map = machine->vmlinux_maps[type]; | ||
610 | int ret = dso__load_kallsyms(map->dso, filename, map, filter); | ||
611 | |||
612 | if (ret > 0) { | ||
613 | dso__set_loaded(map->dso, type); | ||
614 | /* | ||
615 | * Since /proc/kallsyms will have multiple sessions for the | ||
616 | * kernel, with modules between them, fixup the end of all | ||
617 | * sections. | ||
618 | */ | ||
619 | __map_groups__fixup_end(&machine->kmaps, type); | ||
620 | } | ||
621 | |||
622 | return ret; | ||
623 | } | ||
624 | |||
625 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | ||
626 | symbol_filter_t filter) | ||
627 | { | ||
628 | struct map *map = machine->vmlinux_maps[type]; | ||
629 | int ret = dso__load_vmlinux_path(map->dso, map, filter); | ||
630 | |||
631 | if (ret > 0) { | ||
632 | dso__set_loaded(map->dso, type); | ||
633 | map__reloc_vmlinux(map); | ||
634 | } | ||
635 | |||
636 | return ret; | ||
637 | } | ||
638 | |||
639 | static void map_groups__fixup_end(struct map_groups *mg) | ||
640 | { | ||
641 | int i; | ||
642 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
643 | __map_groups__fixup_end(mg, i); | ||
644 | } | ||
645 | |||
646 | static char *get_kernel_version(const char *root_dir) | ||
647 | { | ||
648 | char version[PATH_MAX]; | ||
649 | FILE *file; | ||
650 | char *name, *tmp; | ||
651 | const char *prefix = "Linux version "; | ||
652 | |||
653 | sprintf(version, "%s/proc/version", root_dir); | ||
654 | file = fopen(version, "r"); | ||
655 | if (!file) | ||
656 | return NULL; | ||
657 | |||
658 | version[0] = '\0'; | ||
659 | tmp = fgets(version, sizeof(version), file); | ||
660 | fclose(file); | ||
661 | |||
662 | name = strstr(version, prefix); | ||
663 | if (!name) | ||
664 | return NULL; | ||
665 | name += strlen(prefix); | ||
666 | tmp = strchr(name, ' '); | ||
667 | if (tmp) | ||
668 | *tmp = '\0'; | ||
669 | |||
670 | return strdup(name); | ||
671 | } | ||
672 | |||
673 | static int map_groups__set_modules_path_dir(struct map_groups *mg, | ||
674 | const char *dir_name) | ||
675 | { | ||
676 | struct dirent *dent; | ||
677 | DIR *dir = opendir(dir_name); | ||
678 | int ret = 0; | ||
679 | |||
680 | if (!dir) { | ||
681 | pr_debug("%s: cannot open %s dir\n", __func__, dir_name); | ||
682 | return -1; | ||
683 | } | ||
684 | |||
685 | while ((dent = readdir(dir)) != NULL) { | ||
686 | char path[PATH_MAX]; | ||
687 | struct stat st; | ||
688 | |||
689 | /*sshfs might return bad dent->d_type, so we have to stat*/ | ||
690 | snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name); | ||
691 | if (stat(path, &st)) | ||
692 | continue; | ||
693 | |||
694 | if (S_ISDIR(st.st_mode)) { | ||
695 | if (!strcmp(dent->d_name, ".") || | ||
696 | !strcmp(dent->d_name, "..")) | ||
697 | continue; | ||
698 | |||
699 | ret = map_groups__set_modules_path_dir(mg, path); | ||
700 | if (ret < 0) | ||
701 | goto out; | ||
702 | } else { | ||
703 | char *dot = strrchr(dent->d_name, '.'), | ||
704 | dso_name[PATH_MAX]; | ||
705 | struct map *map; | ||
706 | char *long_name; | ||
707 | |||
708 | if (dot == NULL || strcmp(dot, ".ko")) | ||
709 | continue; | ||
710 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | ||
711 | (int)(dot - dent->d_name), dent->d_name); | ||
712 | |||
713 | strxfrchar(dso_name, '-', '_'); | ||
714 | map = map_groups__find_by_name(mg, MAP__FUNCTION, | ||
715 | dso_name); | ||
716 | if (map == NULL) | ||
717 | continue; | ||
718 | |||
719 | long_name = strdup(path); | ||
720 | if (long_name == NULL) { | ||
721 | ret = -1; | ||
722 | goto out; | ||
723 | } | ||
724 | dso__set_long_name(map->dso, long_name); | ||
725 | map->dso->lname_alloc = 1; | ||
726 | dso__kernel_module_get_build_id(map->dso, ""); | ||
727 | } | ||
728 | } | ||
729 | |||
730 | out: | ||
731 | closedir(dir); | ||
732 | return ret; | ||
733 | } | ||
734 | |||
735 | static int machine__set_modules_path(struct machine *machine) | ||
736 | { | ||
737 | char *version; | ||
738 | char modules_path[PATH_MAX]; | ||
739 | |||
740 | version = get_kernel_version(machine->root_dir); | ||
741 | if (!version) | ||
742 | return -1; | ||
743 | |||
744 | snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", | ||
745 | machine->root_dir, version); | ||
746 | free(version); | ||
747 | |||
748 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); | ||
749 | } | ||
750 | |||
751 | static int machine__create_modules(struct machine *machine) | ||
752 | { | ||
753 | char *line = NULL; | ||
754 | size_t n; | ||
755 | FILE *file; | ||
756 | struct map *map; | ||
757 | const char *modules; | ||
758 | char path[PATH_MAX]; | ||
759 | |||
760 | if (machine__is_default_guest(machine)) | ||
761 | modules = symbol_conf.default_guest_modules; | ||
762 | else { | ||
763 | sprintf(path, "%s/proc/modules", machine->root_dir); | ||
764 | modules = path; | ||
765 | } | ||
766 | |||
767 | if (symbol__restricted_filename(path, "/proc/modules")) | ||
768 | return -1; | ||
769 | |||
770 | file = fopen(modules, "r"); | ||
771 | if (file == NULL) | ||
772 | return -1; | ||
773 | |||
774 | while (!feof(file)) { | ||
775 | char name[PATH_MAX]; | ||
776 | u64 start; | ||
777 | char *sep; | ||
778 | int line_len; | ||
779 | |||
780 | line_len = getline(&line, &n, file); | ||
781 | if (line_len < 0) | ||
782 | break; | ||
783 | |||
784 | if (!line) | ||
785 | goto out_failure; | ||
786 | |||
787 | line[--line_len] = '\0'; /* \n */ | ||
788 | |||
789 | sep = strrchr(line, 'x'); | ||
790 | if (sep == NULL) | ||
791 | continue; | ||
792 | |||
793 | hex2u64(sep + 1, &start); | ||
794 | |||
795 | sep = strchr(line, ' '); | ||
796 | if (sep == NULL) | ||
797 | continue; | ||
798 | |||
799 | *sep = '\0'; | ||
800 | |||
801 | snprintf(name, sizeof(name), "[%s]", line); | ||
802 | map = machine__new_module(machine, start, name); | ||
803 | if (map == NULL) | ||
804 | goto out_delete_line; | ||
805 | dso__kernel_module_get_build_id(map->dso, machine->root_dir); | ||
806 | } | ||
807 | |||
808 | free(line); | ||
809 | fclose(file); | ||
810 | |||
811 | return machine__set_modules_path(machine); | ||
812 | |||
813 | out_delete_line: | ||
814 | free(line); | ||
815 | out_failure: | ||
816 | return -1; | ||
817 | } | ||
818 | |||
819 | int machine__create_kernel_maps(struct machine *machine) | ||
820 | { | ||
821 | struct dso *kernel = machine__get_kernel(machine); | ||
822 | |||
823 | if (kernel == NULL || | ||
824 | __machine__create_kernel_maps(machine, kernel) < 0) | ||
825 | return -1; | ||
826 | |||
827 | if (symbol_conf.use_modules && machine__create_modules(machine) < 0) { | ||
828 | if (machine__is_host(machine)) | ||
829 | pr_debug("Problems creating module maps, " | ||
830 | "continuing anyway...\n"); | ||
831 | else | ||
832 | pr_debug("Problems creating module maps for guest %d, " | ||
833 | "continuing anyway...\n", machine->pid); | ||
834 | } | ||
835 | |||
836 | /* | ||
837 | * Now that we have all the maps created, just set the ->end of them: | ||
838 | */ | ||
839 | map_groups__fixup_end(&machine->kmaps); | ||
840 | return 0; | ||
841 | } | ||
842 | |||
843 | static void machine__set_kernel_mmap_len(struct machine *machine, | ||
844 | union perf_event *event) | ||
845 | { | ||
846 | int i; | ||
847 | |||
848 | for (i = 0; i < MAP__NR_TYPES; i++) { | ||
849 | machine->vmlinux_maps[i]->start = event->mmap.start; | ||
850 | machine->vmlinux_maps[i]->end = (event->mmap.start + | ||
851 | event->mmap.len); | ||
852 | /* | ||
853 | * Be a bit paranoid here, some perf.data file came with | ||
854 | * a zero sized synthesized MMAP event for the kernel. | ||
855 | */ | ||
856 | if (machine->vmlinux_maps[i]->end == 0) | ||
857 | machine->vmlinux_maps[i]->end = ~0ULL; | ||
858 | } | ||
859 | } | ||
860 | |||
861 | static int machine__process_kernel_mmap_event(struct machine *machine, | ||
862 | union perf_event *event) | ||
863 | { | ||
864 | struct map *map; | ||
865 | char kmmap_prefix[PATH_MAX]; | ||
866 | enum dso_kernel_type kernel_type; | ||
867 | bool is_kernel_mmap; | ||
868 | |||
869 | machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); | ||
870 | if (machine__is_host(machine)) | ||
871 | kernel_type = DSO_TYPE_KERNEL; | ||
872 | else | ||
873 | kernel_type = DSO_TYPE_GUEST_KERNEL; | ||
874 | |||
875 | is_kernel_mmap = memcmp(event->mmap.filename, | ||
876 | kmmap_prefix, | ||
877 | strlen(kmmap_prefix) - 1) == 0; | ||
878 | if (event->mmap.filename[0] == '/' || | ||
879 | (!is_kernel_mmap && event->mmap.filename[0] == '[')) { | ||
880 | |||
881 | char short_module_name[1024]; | ||
882 | char *name, *dot; | ||
883 | |||
884 | if (event->mmap.filename[0] == '/') { | ||
885 | name = strrchr(event->mmap.filename, '/'); | ||
886 | if (name == NULL) | ||
887 | goto out_problem; | ||
888 | |||
889 | ++name; /* skip / */ | ||
890 | dot = strrchr(name, '.'); | ||
891 | if (dot == NULL) | ||
892 | goto out_problem; | ||
893 | snprintf(short_module_name, sizeof(short_module_name), | ||
894 | "[%.*s]", (int)(dot - name), name); | ||
895 | strxfrchar(short_module_name, '-', '_'); | ||
896 | } else | ||
897 | strcpy(short_module_name, event->mmap.filename); | ||
898 | |||
899 | map = machine__new_module(machine, event->mmap.start, | ||
900 | event->mmap.filename); | ||
901 | if (map == NULL) | ||
902 | goto out_problem; | ||
903 | |||
904 | name = strdup(short_module_name); | ||
905 | if (name == NULL) | ||
906 | goto out_problem; | ||
907 | |||
908 | map->dso->short_name = name; | ||
909 | map->dso->sname_alloc = 1; | ||
910 | map->end = map->start + event->mmap.len; | ||
911 | } else if (is_kernel_mmap) { | ||
912 | const char *symbol_name = (event->mmap.filename + | ||
913 | strlen(kmmap_prefix)); | ||
914 | /* | ||
915 | * Should be there already, from the build-id table in | ||
916 | * the header. | ||
917 | */ | ||
918 | struct dso *kernel = __dsos__findnew(&machine->kernel_dsos, | ||
919 | kmmap_prefix); | ||
920 | if (kernel == NULL) | ||
921 | goto out_problem; | ||
922 | |||
923 | kernel->kernel = kernel_type; | ||
924 | if (__machine__create_kernel_maps(machine, kernel) < 0) | ||
925 | goto out_problem; | ||
926 | |||
927 | machine__set_kernel_mmap_len(machine, event); | ||
928 | |||
929 | /* | ||
930 | * Avoid using a zero address (kptr_restrict) for the ref reloc | ||
931 | * symbol. Effectively having zero here means that at record | ||
932 | * time /proc/sys/kernel/kptr_restrict was non zero. | ||
933 | */ | ||
934 | if (event->mmap.pgoff != 0) { | ||
935 | maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps, | ||
936 | symbol_name, | ||
937 | event->mmap.pgoff); | ||
938 | } | ||
939 | |||
940 | if (machine__is_default_guest(machine)) { | ||
941 | /* | ||
942 | * preload dso of guest kernel and modules | ||
943 | */ | ||
944 | dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION], | ||
945 | NULL); | ||
946 | } | ||
947 | } | ||
948 | return 0; | ||
949 | out_problem: | ||
950 | return -1; | ||
951 | } | ||
952 | |||
953 | int machine__process_mmap_event(struct machine *machine, union perf_event *event) | ||
954 | { | ||
955 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
956 | struct thread *thread; | ||
957 | struct map *map; | ||
958 | int ret = 0; | ||
959 | |||
960 | if (dump_trace) | ||
961 | perf_event__fprintf_mmap(event, stdout); | ||
962 | |||
963 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | ||
964 | cpumode == PERF_RECORD_MISC_KERNEL) { | ||
965 | ret = machine__process_kernel_mmap_event(machine, event); | ||
966 | if (ret < 0) | ||
967 | goto out_problem; | ||
968 | return 0; | ||
969 | } | ||
970 | |||
971 | thread = machine__findnew_thread(machine, event->mmap.pid); | ||
972 | if (thread == NULL) | ||
973 | goto out_problem; | ||
974 | map = map__new(&machine->user_dsos, event->mmap.start, | ||
975 | event->mmap.len, event->mmap.pgoff, | ||
976 | event->mmap.pid, event->mmap.filename, | ||
977 | MAP__FUNCTION); | ||
978 | if (map == NULL) | ||
979 | goto out_problem; | ||
980 | |||
981 | thread__insert_map(thread, map); | ||
982 | return 0; | ||
983 | |||
984 | out_problem: | ||
985 | dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); | ||
986 | return 0; | ||
987 | } | ||
988 | |||
989 | int machine__process_fork_event(struct machine *machine, union perf_event *event) | ||
990 | { | ||
991 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | ||
992 | struct thread *parent = machine__findnew_thread(machine, event->fork.ptid); | ||
993 | |||
994 | if (dump_trace) | ||
995 | perf_event__fprintf_task(event, stdout); | ||
996 | |||
997 | if (thread == NULL || parent == NULL || | ||
998 | thread__fork(thread, parent) < 0) { | ||
999 | dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); | ||
1000 | return -1; | ||
1001 | } | ||
1002 | |||
1003 | return 0; | ||
1004 | } | ||
1005 | |||
1006 | int machine__process_exit_event(struct machine *machine, union perf_event *event) | ||
1007 | { | ||
1008 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | ||
1009 | |||
1010 | if (dump_trace) | ||
1011 | perf_event__fprintf_task(event, stdout); | ||
1012 | |||
1013 | if (thread != NULL) | ||
1014 | machine__remove_thread(machine, thread); | ||
1015 | |||
1016 | return 0; | ||
1017 | } | ||
1018 | |||
1019 | int machine__process_event(struct machine *machine, union perf_event *event) | ||
1020 | { | ||
1021 | int ret; | ||
1022 | |||
1023 | switch (event->header.type) { | ||
1024 | case PERF_RECORD_COMM: | ||
1025 | ret = machine__process_comm_event(machine, event); break; | ||
1026 | case PERF_RECORD_MMAP: | ||
1027 | ret = machine__process_mmap_event(machine, event); break; | ||
1028 | case PERF_RECORD_FORK: | ||
1029 | ret = machine__process_fork_event(machine, event); break; | ||
1030 | case PERF_RECORD_EXIT: | ||
1031 | ret = machine__process_exit_event(machine, event); break; | ||
1032 | case PERF_RECORD_LOST: | ||
1033 | ret = machine__process_lost_event(machine, event); break; | ||
1034 | default: | ||
1035 | ret = -1; | ||
1036 | break; | ||
1037 | } | ||
1038 | |||
1039 | return ret; | ||
1040 | } | ||
1041 | |||
1042 | void machine__remove_thread(struct machine *machine, struct thread *th) | ||
1043 | { | ||
1044 | machine->last_match = NULL; | ||
1045 | rb_erase(&th->rb_node, &machine->threads); | ||
1046 | /* | ||
1047 | * We may have references to this thread, for instance in some hist_entry | ||
1048 | * instances, so just move them to a separate list. | ||
1049 | */ | ||
1050 | list_add_tail(&th->node, &machine->dead_threads); | ||
1051 | } | ||
1052 | |||
1053 | static bool symbol__match_parent_regex(struct symbol *sym) | ||
1054 | { | ||
1055 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
1056 | return 1; | ||
1057 | |||
1058 | return 0; | ||
1059 | } | ||
1060 | |||
1061 | static const u8 cpumodes[] = { | ||
1062 | PERF_RECORD_MISC_USER, | ||
1063 | PERF_RECORD_MISC_KERNEL, | ||
1064 | PERF_RECORD_MISC_GUEST_USER, | ||
1065 | PERF_RECORD_MISC_GUEST_KERNEL | ||
1066 | }; | ||
1067 | #define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) | ||
1068 | |||
1069 | static void ip__resolve_ams(struct machine *machine, struct thread *thread, | ||
1070 | struct addr_map_symbol *ams, | ||
1071 | u64 ip) | ||
1072 | { | ||
1073 | struct addr_location al; | ||
1074 | size_t i; | ||
1075 | u8 m; | ||
1076 | |||
1077 | memset(&al, 0, sizeof(al)); | ||
1078 | |||
1079 | for (i = 0; i < NCPUMODES; i++) { | ||
1080 | m = cpumodes[i]; | ||
1081 | /* | ||
1082 | * We cannot use the header.misc hint to determine whether a | ||
1083 | * branch stack address is user, kernel, guest, hypervisor. | ||
1084 | * Branches may straddle the kernel/user/hypervisor boundaries. | ||
1085 | * Thus, we have to try consecutively until we find a match | ||
1086 | * or else, the symbol is unknown | ||
1087 | */ | ||
1088 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, | ||
1089 | ip, &al, NULL); | ||
1090 | if (al.sym) | ||
1091 | goto found; | ||
1092 | } | ||
1093 | found: | ||
1094 | ams->addr = ip; | ||
1095 | ams->al_addr = al.addr; | ||
1096 | ams->sym = al.sym; | ||
1097 | ams->map = al.map; | ||
1098 | } | ||
1099 | |||
1100 | struct branch_info *machine__resolve_bstack(struct machine *machine, | ||
1101 | struct thread *thr, | ||
1102 | struct branch_stack *bs) | ||
1103 | { | ||
1104 | struct branch_info *bi; | ||
1105 | unsigned int i; | ||
1106 | |||
1107 | bi = calloc(bs->nr, sizeof(struct branch_info)); | ||
1108 | if (!bi) | ||
1109 | return NULL; | ||
1110 | |||
1111 | for (i = 0; i < bs->nr; i++) { | ||
1112 | ip__resolve_ams(machine, thr, &bi[i].to, bs->entries[i].to); | ||
1113 | ip__resolve_ams(machine, thr, &bi[i].from, bs->entries[i].from); | ||
1114 | bi[i].flags = bs->entries[i].flags; | ||
1115 | } | ||
1116 | return bi; | ||
1117 | } | ||
1118 | |||
1119 | static int machine__resolve_callchain_sample(struct machine *machine, | ||
1120 | struct thread *thread, | ||
1121 | struct ip_callchain *chain, | ||
1122 | struct symbol **parent) | ||
1123 | |||
1124 | { | ||
1125 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
1126 | unsigned int i; | ||
1127 | int err; | ||
1128 | |||
1129 | callchain_cursor_reset(&callchain_cursor); | ||
1130 | |||
1131 | if (chain->nr > PERF_MAX_STACK_DEPTH) { | ||
1132 | pr_warning("corrupted callchain. skipping...\n"); | ||
1133 | return 0; | ||
1134 | } | ||
1135 | |||
1136 | for (i = 0; i < chain->nr; i++) { | ||
1137 | u64 ip; | ||
1138 | struct addr_location al; | ||
1139 | |||
1140 | if (callchain_param.order == ORDER_CALLEE) | ||
1141 | ip = chain->ips[i]; | ||
1142 | else | ||
1143 | ip = chain->ips[chain->nr - i - 1]; | ||
1144 | |||
1145 | if (ip >= PERF_CONTEXT_MAX) { | ||
1146 | switch (ip) { | ||
1147 | case PERF_CONTEXT_HV: | ||
1148 | cpumode = PERF_RECORD_MISC_HYPERVISOR; | ||
1149 | break; | ||
1150 | case PERF_CONTEXT_KERNEL: | ||
1151 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
1152 | break; | ||
1153 | case PERF_CONTEXT_USER: | ||
1154 | cpumode = PERF_RECORD_MISC_USER; | ||
1155 | break; | ||
1156 | default: | ||
1157 | pr_debug("invalid callchain context: " | ||
1158 | "%"PRId64"\n", (s64) ip); | ||
1159 | /* | ||
1160 | * It seems the callchain is corrupted. | ||
1161 | * Discard all. | ||
1162 | */ | ||
1163 | callchain_cursor_reset(&callchain_cursor); | ||
1164 | return 0; | ||
1165 | } | ||
1166 | continue; | ||
1167 | } | ||
1168 | |||
1169 | al.filtered = false; | ||
1170 | thread__find_addr_location(thread, machine, cpumode, | ||
1171 | MAP__FUNCTION, ip, &al, NULL); | ||
1172 | if (al.sym != NULL) { | ||
1173 | if (sort__has_parent && !*parent && | ||
1174 | symbol__match_parent_regex(al.sym)) | ||
1175 | *parent = al.sym; | ||
1176 | if (!symbol_conf.use_callchain) | ||
1177 | break; | ||
1178 | } | ||
1179 | |||
1180 | err = callchain_cursor_append(&callchain_cursor, | ||
1181 | ip, al.map, al.sym); | ||
1182 | if (err) | ||
1183 | return err; | ||
1184 | } | ||
1185 | |||
1186 | return 0; | ||
1187 | } | ||
1188 | |||
1189 | static int unwind_entry(struct unwind_entry *entry, void *arg) | ||
1190 | { | ||
1191 | struct callchain_cursor *cursor = arg; | ||
1192 | return callchain_cursor_append(cursor, entry->ip, | ||
1193 | entry->map, entry->sym); | ||
1194 | } | ||
1195 | |||
1196 | int machine__resolve_callchain(struct machine *machine, | ||
1197 | struct perf_evsel *evsel, | ||
1198 | struct thread *thread, | ||
1199 | struct perf_sample *sample, | ||
1200 | struct symbol **parent) | ||
1201 | |||
1202 | { | ||
1203 | int ret; | ||
1204 | |||
1205 | callchain_cursor_reset(&callchain_cursor); | ||
1206 | |||
1207 | ret = machine__resolve_callchain_sample(machine, thread, | ||
1208 | sample->callchain, parent); | ||
1209 | if (ret) | ||
1210 | return ret; | ||
1211 | |||
1212 | /* Can we do dwarf post unwind? */ | ||
1213 | if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) && | ||
1214 | (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER))) | ||
1215 | return 0; | ||
1216 | |||
1217 | /* Bail out if nothing was captured. */ | ||
1218 | if ((!sample->user_regs.regs) || | ||
1219 | (!sample->user_stack.size)) | ||
1220 | return 0; | ||
1221 | |||
1222 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | ||
1223 | thread, evsel->attr.sample_regs_user, | ||
1224 | sample); | ||
1225 | |||
1226 | } | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h new file mode 100644 index 000000000000..5ac5892f2326 --- /dev/null +++ b/tools/perf/util/machine.h | |||
@@ -0,0 +1,157 @@ | |||
1 | #ifndef __PERF_MACHINE_H | ||
2 | #define __PERF_MACHINE_H | ||
3 | |||
4 | #include <sys/types.h> | ||
5 | #include <linux/rbtree.h> | ||
6 | #include "map.h" | ||
7 | |||
8 | struct branch_stack; | ||
9 | struct perf_evsel; | ||
10 | struct perf_sample; | ||
11 | struct symbol; | ||
12 | struct thread; | ||
13 | union perf_event; | ||
14 | |||
15 | /* Native host kernel uses -1 as pid index in machine */ | ||
16 | #define HOST_KERNEL_ID (-1) | ||
17 | #define DEFAULT_GUEST_KERNEL_ID (0) | ||
18 | |||
19 | struct machine { | ||
20 | struct rb_node rb_node; | ||
21 | pid_t pid; | ||
22 | u16 id_hdr_size; | ||
23 | char *root_dir; | ||
24 | struct rb_root threads; | ||
25 | struct list_head dead_threads; | ||
26 | struct thread *last_match; | ||
27 | struct list_head user_dsos; | ||
28 | struct list_head kernel_dsos; | ||
29 | struct map_groups kmaps; | ||
30 | struct map *vmlinux_maps[MAP__NR_TYPES]; | ||
31 | }; | ||
32 | |||
33 | static inline | ||
34 | struct map *machine__kernel_map(struct machine *machine, enum map_type type) | ||
35 | { | ||
36 | return machine->vmlinux_maps[type]; | ||
37 | } | ||
38 | |||
39 | struct thread *machine__find_thread(struct machine *machine, pid_t pid); | ||
40 | |||
41 | int machine__process_comm_event(struct machine *machine, union perf_event *event); | ||
42 | int machine__process_exit_event(struct machine *machine, union perf_event *event); | ||
43 | int machine__process_fork_event(struct machine *machine, union perf_event *event); | ||
44 | int machine__process_lost_event(struct machine *machine, union perf_event *event); | ||
45 | int machine__process_mmap_event(struct machine *machine, union perf_event *event); | ||
46 | int machine__process_event(struct machine *machine, union perf_event *event); | ||
47 | |||
48 | typedef void (*machine__process_t)(struct machine *machine, void *data); | ||
49 | |||
50 | struct machines { | ||
51 | struct machine host; | ||
52 | struct rb_root guests; | ||
53 | }; | ||
54 | |||
55 | void machines__init(struct machines *machines); | ||
56 | void machines__exit(struct machines *machines); | ||
57 | |||
58 | void machines__process_guests(struct machines *machines, | ||
59 | machine__process_t process, void *data); | ||
60 | |||
61 | struct machine *machines__add(struct machines *machines, pid_t pid, | ||
62 | const char *root_dir); | ||
63 | struct machine *machines__find_host(struct machines *machines); | ||
64 | struct machine *machines__find(struct machines *machines, pid_t pid); | ||
65 | struct machine *machines__findnew(struct machines *machines, pid_t pid); | ||
66 | |||
67 | void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size); | ||
68 | char *machine__mmap_name(struct machine *machine, char *bf, size_t size); | ||
69 | |||
70 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); | ||
71 | void machine__exit(struct machine *machine); | ||
72 | void machine__delete_dead_threads(struct machine *machine); | ||
73 | void machine__delete_threads(struct machine *machine); | ||
74 | void machine__delete(struct machine *machine); | ||
75 | |||
76 | struct branch_info *machine__resolve_bstack(struct machine *machine, | ||
77 | struct thread *thread, | ||
78 | struct branch_stack *bs); | ||
79 | int machine__resolve_callchain(struct machine *machine, | ||
80 | struct perf_evsel *evsel, | ||
81 | struct thread *thread, | ||
82 | struct perf_sample *sample, | ||
83 | struct symbol **parent); | ||
84 | |||
85 | /* | ||
86 | * Default guest kernel is defined by parameter --guestkallsyms | ||
87 | * and --guestmodules | ||
88 | */ | ||
89 | static inline bool machine__is_default_guest(struct machine *machine) | ||
90 | { | ||
91 | return machine ? machine->pid == DEFAULT_GUEST_KERNEL_ID : false; | ||
92 | } | ||
93 | |||
94 | static inline bool machine__is_host(struct machine *machine) | ||
95 | { | ||
96 | return machine ? machine->pid == HOST_KERNEL_ID : false; | ||
97 | } | ||
98 | |||
99 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid); | ||
100 | void machine__remove_thread(struct machine *machine, struct thread *th); | ||
101 | |||
102 | size_t machine__fprintf(struct machine *machine, FILE *fp); | ||
103 | |||
104 | static inline | ||
105 | struct symbol *machine__find_kernel_symbol(struct machine *machine, | ||
106 | enum map_type type, u64 addr, | ||
107 | struct map **mapp, | ||
108 | symbol_filter_t filter) | ||
109 | { | ||
110 | return map_groups__find_symbol(&machine->kmaps, type, addr, | ||
111 | mapp, filter); | ||
112 | } | ||
113 | |||
114 | static inline | ||
115 | struct symbol *machine__find_kernel_function(struct machine *machine, u64 addr, | ||
116 | struct map **mapp, | ||
117 | symbol_filter_t filter) | ||
118 | { | ||
119 | return machine__find_kernel_symbol(machine, MAP__FUNCTION, addr, | ||
120 | mapp, filter); | ||
121 | } | ||
122 | |||
123 | static inline | ||
124 | struct symbol *machine__find_kernel_function_by_name(struct machine *machine, | ||
125 | const char *name, | ||
126 | struct map **mapp, | ||
127 | symbol_filter_t filter) | ||
128 | { | ||
129 | return map_groups__find_function_by_name(&machine->kmaps, name, mapp, | ||
130 | filter); | ||
131 | } | ||
132 | |||
133 | struct map *machine__new_module(struct machine *machine, u64 start, | ||
134 | const char *filename); | ||
135 | |||
136 | int machine__load_kallsyms(struct machine *machine, const char *filename, | ||
137 | enum map_type type, symbol_filter_t filter); | ||
138 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | ||
139 | symbol_filter_t filter); | ||
140 | |||
141 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | ||
142 | bool (skip)(struct dso *dso, int parm), int parm); | ||
143 | size_t machines__fprintf_dsos(struct machines *machines, FILE *fp); | ||
144 | size_t machines__fprintf_dsos_buildid(struct machines *machines, FILE *fp, | ||
145 | bool (skip)(struct dso *dso, int parm), int parm); | ||
146 | |||
147 | void machine__destroy_kernel_maps(struct machine *machine); | ||
148 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel); | ||
149 | int machine__create_kernel_maps(struct machine *machine); | ||
150 | |||
151 | int machines__create_kernel_maps(struct machines *machines, pid_t pid); | ||
152 | int machines__create_guest_kernel_maps(struct machines *machines); | ||
153 | void machines__destroy_kernel_maps(struct machines *machines); | ||
154 | |||
155 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); | ||
156 | |||
157 | #endif /* __PERF_MACHINE_H */ | ||
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 6109fa4d14cd..6fcb9de62340 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -10,6 +10,8 @@ | |||
10 | #include "thread.h" | 10 | #include "thread.h" |
11 | #include "strlist.h" | 11 | #include "strlist.h" |
12 | #include "vdso.h" | 12 | #include "vdso.h" |
13 | #include "build-id.h" | ||
14 | #include <linux/string.h> | ||
13 | 15 | ||
14 | const char *map_type__name[MAP__NR_TYPES] = { | 16 | const char *map_type__name[MAP__NR_TYPES] = { |
15 | [MAP__FUNCTION] = "Functions", | 17 | [MAP__FUNCTION] = "Functions", |
@@ -18,38 +20,39 @@ const char *map_type__name[MAP__NR_TYPES] = { | |||
18 | 20 | ||
19 | static inline int is_anon_memory(const char *filename) | 21 | static inline int is_anon_memory(const char *filename) |
20 | { | 22 | { |
21 | return strcmp(filename, "//anon") == 0; | 23 | return !strcmp(filename, "//anon") || |
24 | !strcmp(filename, "/anon_hugepage (deleted)"); | ||
22 | } | 25 | } |
23 | 26 | ||
24 | static inline int is_no_dso_memory(const char *filename) | 27 | static inline int is_no_dso_memory(const char *filename) |
25 | { | 28 | { |
26 | return !strcmp(filename, "[stack]") || | 29 | return !strncmp(filename, "[stack", 6) || |
27 | !strcmp(filename, "[heap]"); | 30 | !strcmp(filename, "[heap]"); |
28 | } | 31 | } |
29 | 32 | ||
30 | void map__init(struct map *self, enum map_type type, | 33 | void map__init(struct map *map, enum map_type type, |
31 | u64 start, u64 end, u64 pgoff, struct dso *dso) | 34 | u64 start, u64 end, u64 pgoff, struct dso *dso) |
32 | { | 35 | { |
33 | self->type = type; | 36 | map->type = type; |
34 | self->start = start; | 37 | map->start = start; |
35 | self->end = end; | 38 | map->end = end; |
36 | self->pgoff = pgoff; | 39 | map->pgoff = pgoff; |
37 | self->dso = dso; | 40 | map->dso = dso; |
38 | self->map_ip = map__map_ip; | 41 | map->map_ip = map__map_ip; |
39 | self->unmap_ip = map__unmap_ip; | 42 | map->unmap_ip = map__unmap_ip; |
40 | RB_CLEAR_NODE(&self->rb_node); | 43 | RB_CLEAR_NODE(&map->rb_node); |
41 | self->groups = NULL; | 44 | map->groups = NULL; |
42 | self->referenced = false; | 45 | map->referenced = false; |
43 | self->erange_warned = false; | 46 | map->erange_warned = false; |
44 | } | 47 | } |
45 | 48 | ||
46 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 49 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
47 | u64 pgoff, u32 pid, char *filename, | 50 | u64 pgoff, u32 pid, char *filename, |
48 | enum map_type type) | 51 | enum map_type type) |
49 | { | 52 | { |
50 | struct map *self = malloc(sizeof(*self)); | 53 | struct map *map = malloc(sizeof(*map)); |
51 | 54 | ||
52 | if (self != NULL) { | 55 | if (map != NULL) { |
53 | char newfilename[PATH_MAX]; | 56 | char newfilename[PATH_MAX]; |
54 | struct dso *dso; | 57 | struct dso *dso; |
55 | int anon, no_dso, vdso; | 58 | int anon, no_dso, vdso; |
@@ -72,10 +75,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
72 | if (dso == NULL) | 75 | if (dso == NULL) |
73 | goto out_delete; | 76 | goto out_delete; |
74 | 77 | ||
75 | map__init(self, type, start, start + len, pgoff, dso); | 78 | map__init(map, type, start, start + len, pgoff, dso); |
76 | 79 | ||
77 | if (anon || no_dso) { | 80 | if (anon || no_dso) { |
78 | self->map_ip = self->unmap_ip = identity__map_ip; | 81 | map->map_ip = map->unmap_ip = identity__map_ip; |
79 | 82 | ||
80 | /* | 83 | /* |
81 | * Set memory without DSO as loaded. All map__find_* | 84 | * Set memory without DSO as loaded. All map__find_* |
@@ -83,12 +86,12 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
83 | * unnecessary map__load warning. | 86 | * unnecessary map__load warning. |
84 | */ | 87 | */ |
85 | if (no_dso) | 88 | if (no_dso) |
86 | dso__set_loaded(dso, self->type); | 89 | dso__set_loaded(dso, map->type); |
87 | } | 90 | } |
88 | } | 91 | } |
89 | return self; | 92 | return map; |
90 | out_delete: | 93 | out_delete: |
91 | free(self); | 94 | free(map); |
92 | return NULL; | 95 | return NULL; |
93 | } | 96 | } |
94 | 97 | ||
@@ -111,48 +114,48 @@ struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | |||
111 | return map; | 114 | return map; |
112 | } | 115 | } |
113 | 116 | ||
114 | void map__delete(struct map *self) | 117 | void map__delete(struct map *map) |
115 | { | 118 | { |
116 | free(self); | 119 | free(map); |
117 | } | 120 | } |
118 | 121 | ||
119 | void map__fixup_start(struct map *self) | 122 | void map__fixup_start(struct map *map) |
120 | { | 123 | { |
121 | struct rb_root *symbols = &self->dso->symbols[self->type]; | 124 | struct rb_root *symbols = &map->dso->symbols[map->type]; |
122 | struct rb_node *nd = rb_first(symbols); | 125 | struct rb_node *nd = rb_first(symbols); |
123 | if (nd != NULL) { | 126 | if (nd != NULL) { |
124 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | 127 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); |
125 | self->start = sym->start; | 128 | map->start = sym->start; |
126 | } | 129 | } |
127 | } | 130 | } |
128 | 131 | ||
129 | void map__fixup_end(struct map *self) | 132 | void map__fixup_end(struct map *map) |
130 | { | 133 | { |
131 | struct rb_root *symbols = &self->dso->symbols[self->type]; | 134 | struct rb_root *symbols = &map->dso->symbols[map->type]; |
132 | struct rb_node *nd = rb_last(symbols); | 135 | struct rb_node *nd = rb_last(symbols); |
133 | if (nd != NULL) { | 136 | if (nd != NULL) { |
134 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); | 137 | struct symbol *sym = rb_entry(nd, struct symbol, rb_node); |
135 | self->end = sym->end; | 138 | map->end = sym->end; |
136 | } | 139 | } |
137 | } | 140 | } |
138 | 141 | ||
139 | #define DSO__DELETED "(deleted)" | 142 | #define DSO__DELETED "(deleted)" |
140 | 143 | ||
141 | int map__load(struct map *self, symbol_filter_t filter) | 144 | int map__load(struct map *map, symbol_filter_t filter) |
142 | { | 145 | { |
143 | const char *name = self->dso->long_name; | 146 | const char *name = map->dso->long_name; |
144 | int nr; | 147 | int nr; |
145 | 148 | ||
146 | if (dso__loaded(self->dso, self->type)) | 149 | if (dso__loaded(map->dso, map->type)) |
147 | return 0; | 150 | return 0; |
148 | 151 | ||
149 | nr = dso__load(self->dso, self, filter); | 152 | nr = dso__load(map->dso, map, filter); |
150 | if (nr < 0) { | 153 | if (nr < 0) { |
151 | if (self->dso->has_build_id) { | 154 | if (map->dso->has_build_id) { |
152 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | 155 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; |
153 | 156 | ||
154 | build_id__sprintf(self->dso->build_id, | 157 | build_id__sprintf(map->dso->build_id, |
155 | sizeof(self->dso->build_id), | 158 | sizeof(map->dso->build_id), |
156 | sbuild_id); | 159 | sbuild_id); |
157 | pr_warning("%s with build id %s not found", | 160 | pr_warning("%s with build id %s not found", |
158 | name, sbuild_id); | 161 | name, sbuild_id); |
@@ -182,43 +185,36 @@ int map__load(struct map *self, symbol_filter_t filter) | |||
182 | * Only applies to the kernel, as its symtabs aren't relative like the | 185 | * Only applies to the kernel, as its symtabs aren't relative like the |
183 | * module ones. | 186 | * module ones. |
184 | */ | 187 | */ |
185 | if (self->dso->kernel) | 188 | if (map->dso->kernel) |
186 | map__reloc_vmlinux(self); | 189 | map__reloc_vmlinux(map); |
187 | 190 | ||
188 | return 0; | 191 | return 0; |
189 | } | 192 | } |
190 | 193 | ||
191 | struct symbol *map__find_symbol(struct map *self, u64 addr, | 194 | struct symbol *map__find_symbol(struct map *map, u64 addr, |
192 | symbol_filter_t filter) | 195 | symbol_filter_t filter) |
193 | { | 196 | { |
194 | if (map__load(self, filter) < 0) | 197 | if (map__load(map, filter) < 0) |
195 | return NULL; | 198 | return NULL; |
196 | 199 | ||
197 | return dso__find_symbol(self->dso, self->type, addr); | 200 | return dso__find_symbol(map->dso, map->type, addr); |
198 | } | 201 | } |
199 | 202 | ||
200 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | 203 | struct symbol *map__find_symbol_by_name(struct map *map, const char *name, |
201 | symbol_filter_t filter) | 204 | symbol_filter_t filter) |
202 | { | 205 | { |
203 | if (map__load(self, filter) < 0) | 206 | if (map__load(map, filter) < 0) |
204 | return NULL; | 207 | return NULL; |
205 | 208 | ||
206 | if (!dso__sorted_by_name(self->dso, self->type)) | 209 | if (!dso__sorted_by_name(map->dso, map->type)) |
207 | dso__sort_by_name(self->dso, self->type); | 210 | dso__sort_by_name(map->dso, map->type); |
208 | 211 | ||
209 | return dso__find_symbol_by_name(self->dso, self->type, name); | 212 | return dso__find_symbol_by_name(map->dso, map->type, name); |
210 | } | 213 | } |
211 | 214 | ||
212 | struct map *map__clone(struct map *self) | 215 | struct map *map__clone(struct map *map) |
213 | { | 216 | { |
214 | struct map *map = malloc(sizeof(*self)); | 217 | return memdup(map, sizeof(*map)); |
215 | |||
216 | if (!map) | ||
217 | return NULL; | ||
218 | |||
219 | memcpy(map, self, sizeof(*self)); | ||
220 | |||
221 | return map; | ||
222 | } | 218 | } |
223 | 219 | ||
224 | int map__overlap(struct map *l, struct map *r) | 220 | int map__overlap(struct map *l, struct map *r) |
@@ -235,10 +231,10 @@ int map__overlap(struct map *l, struct map *r) | |||
235 | return 0; | 231 | return 0; |
236 | } | 232 | } |
237 | 233 | ||
238 | size_t map__fprintf(struct map *self, FILE *fp) | 234 | size_t map__fprintf(struct map *map, FILE *fp) |
239 | { | 235 | { |
240 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s\n", | 236 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s\n", |
241 | self->start, self->end, self->pgoff, self->dso->name); | 237 | map->start, map->end, map->pgoff, map->dso->name); |
242 | } | 238 | } |
243 | 239 | ||
244 | size_t map__fprintf_dsoname(struct map *map, FILE *fp) | 240 | size_t map__fprintf_dsoname(struct map *map, FILE *fp) |
@@ -526,9 +522,9 @@ static u64 map__reloc_unmap_ip(struct map *map, u64 ip) | |||
526 | return ip - (s64)map->pgoff; | 522 | return ip - (s64)map->pgoff; |
527 | } | 523 | } |
528 | 524 | ||
529 | void map__reloc_vmlinux(struct map *self) | 525 | void map__reloc_vmlinux(struct map *map) |
530 | { | 526 | { |
531 | struct kmap *kmap = map__kmap(self); | 527 | struct kmap *kmap = map__kmap(map); |
532 | s64 reloc; | 528 | s64 reloc; |
533 | 529 | ||
534 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) | 530 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) |
@@ -540,9 +536,9 @@ void map__reloc_vmlinux(struct map *self) | |||
540 | if (!reloc) | 536 | if (!reloc) |
541 | return; | 537 | return; |
542 | 538 | ||
543 | self->map_ip = map__reloc_map_ip; | 539 | map->map_ip = map__reloc_map_ip; |
544 | self->unmap_ip = map__reloc_unmap_ip; | 540 | map->unmap_ip = map__reloc_unmap_ip; |
545 | self->pgoff = reloc; | 541 | map->pgoff = reloc; |
546 | } | 542 | } |
547 | 543 | ||
548 | void maps__insert(struct rb_root *maps, struct map *map) | 544 | void maps__insert(struct rb_root *maps, struct map *map) |
@@ -565,9 +561,9 @@ void maps__insert(struct rb_root *maps, struct map *map) | |||
565 | rb_insert_color(&map->rb_node, maps); | 561 | rb_insert_color(&map->rb_node, maps); |
566 | } | 562 | } |
567 | 563 | ||
568 | void maps__remove(struct rb_root *self, struct map *map) | 564 | void maps__remove(struct rb_root *maps, struct map *map) |
569 | { | 565 | { |
570 | rb_erase(&map->rb_node, self); | 566 | rb_erase(&map->rb_node, maps); |
571 | } | 567 | } |
572 | 568 | ||
573 | struct map *maps__find(struct rb_root *maps, u64 ip) | 569 | struct map *maps__find(struct rb_root *maps, u64 ip) |
@@ -589,182 +585,3 @@ struct map *maps__find(struct rb_root *maps, u64 ip) | |||
589 | 585 | ||
590 | return NULL; | 586 | return NULL; |
591 | } | 587 | } |
592 | |||
593 | int machine__init(struct machine *self, const char *root_dir, pid_t pid) | ||
594 | { | ||
595 | map_groups__init(&self->kmaps); | ||
596 | RB_CLEAR_NODE(&self->rb_node); | ||
597 | INIT_LIST_HEAD(&self->user_dsos); | ||
598 | INIT_LIST_HEAD(&self->kernel_dsos); | ||
599 | |||
600 | self->threads = RB_ROOT; | ||
601 | INIT_LIST_HEAD(&self->dead_threads); | ||
602 | self->last_match = NULL; | ||
603 | |||
604 | self->kmaps.machine = self; | ||
605 | self->pid = pid; | ||
606 | self->root_dir = strdup(root_dir); | ||
607 | if (self->root_dir == NULL) | ||
608 | return -ENOMEM; | ||
609 | |||
610 | if (pid != HOST_KERNEL_ID) { | ||
611 | struct thread *thread = machine__findnew_thread(self, pid); | ||
612 | char comm[64]; | ||
613 | |||
614 | if (thread == NULL) | ||
615 | return -ENOMEM; | ||
616 | |||
617 | snprintf(comm, sizeof(comm), "[guest/%d]", pid); | ||
618 | thread__set_comm(thread, comm); | ||
619 | } | ||
620 | |||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | static void dsos__delete(struct list_head *self) | ||
625 | { | ||
626 | struct dso *pos, *n; | ||
627 | |||
628 | list_for_each_entry_safe(pos, n, self, node) { | ||
629 | list_del(&pos->node); | ||
630 | dso__delete(pos); | ||
631 | } | ||
632 | } | ||
633 | |||
634 | void machine__exit(struct machine *self) | ||
635 | { | ||
636 | map_groups__exit(&self->kmaps); | ||
637 | dsos__delete(&self->user_dsos); | ||
638 | dsos__delete(&self->kernel_dsos); | ||
639 | free(self->root_dir); | ||
640 | self->root_dir = NULL; | ||
641 | } | ||
642 | |||
643 | void machine__delete(struct machine *self) | ||
644 | { | ||
645 | machine__exit(self); | ||
646 | free(self); | ||
647 | } | ||
648 | |||
649 | struct machine *machines__add(struct rb_root *self, pid_t pid, | ||
650 | const char *root_dir) | ||
651 | { | ||
652 | struct rb_node **p = &self->rb_node; | ||
653 | struct rb_node *parent = NULL; | ||
654 | struct machine *pos, *machine = malloc(sizeof(*machine)); | ||
655 | |||
656 | if (!machine) | ||
657 | return NULL; | ||
658 | |||
659 | if (machine__init(machine, root_dir, pid) != 0) { | ||
660 | free(machine); | ||
661 | return NULL; | ||
662 | } | ||
663 | |||
664 | while (*p != NULL) { | ||
665 | parent = *p; | ||
666 | pos = rb_entry(parent, struct machine, rb_node); | ||
667 | if (pid < pos->pid) | ||
668 | p = &(*p)->rb_left; | ||
669 | else | ||
670 | p = &(*p)->rb_right; | ||
671 | } | ||
672 | |||
673 | rb_link_node(&machine->rb_node, parent, p); | ||
674 | rb_insert_color(&machine->rb_node, self); | ||
675 | |||
676 | return machine; | ||
677 | } | ||
678 | |||
679 | struct machine *machines__find(struct rb_root *self, pid_t pid) | ||
680 | { | ||
681 | struct rb_node **p = &self->rb_node; | ||
682 | struct rb_node *parent = NULL; | ||
683 | struct machine *machine; | ||
684 | struct machine *default_machine = NULL; | ||
685 | |||
686 | while (*p != NULL) { | ||
687 | parent = *p; | ||
688 | machine = rb_entry(parent, struct machine, rb_node); | ||
689 | if (pid < machine->pid) | ||
690 | p = &(*p)->rb_left; | ||
691 | else if (pid > machine->pid) | ||
692 | p = &(*p)->rb_right; | ||
693 | else | ||
694 | return machine; | ||
695 | if (!machine->pid) | ||
696 | default_machine = machine; | ||
697 | } | ||
698 | |||
699 | return default_machine; | ||
700 | } | ||
701 | |||
702 | struct machine *machines__findnew(struct rb_root *self, pid_t pid) | ||
703 | { | ||
704 | char path[PATH_MAX]; | ||
705 | const char *root_dir = ""; | ||
706 | struct machine *machine = machines__find(self, pid); | ||
707 | |||
708 | if (machine && (machine->pid == pid)) | ||
709 | goto out; | ||
710 | |||
711 | if ((pid != HOST_KERNEL_ID) && | ||
712 | (pid != DEFAULT_GUEST_KERNEL_ID) && | ||
713 | (symbol_conf.guestmount)) { | ||
714 | sprintf(path, "%s/%d", symbol_conf.guestmount, pid); | ||
715 | if (access(path, R_OK)) { | ||
716 | static struct strlist *seen; | ||
717 | |||
718 | if (!seen) | ||
719 | seen = strlist__new(true, NULL); | ||
720 | |||
721 | if (!strlist__has_entry(seen, path)) { | ||
722 | pr_err("Can't access file %s\n", path); | ||
723 | strlist__add(seen, path); | ||
724 | } | ||
725 | machine = NULL; | ||
726 | goto out; | ||
727 | } | ||
728 | root_dir = path; | ||
729 | } | ||
730 | |||
731 | machine = machines__add(self, pid, root_dir); | ||
732 | |||
733 | out: | ||
734 | return machine; | ||
735 | } | ||
736 | |||
737 | void machines__process(struct rb_root *self, machine__process_t process, void *data) | ||
738 | { | ||
739 | struct rb_node *nd; | ||
740 | |||
741 | for (nd = rb_first(self); nd; nd = rb_next(nd)) { | ||
742 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
743 | process(pos, data); | ||
744 | } | ||
745 | } | ||
746 | |||
747 | char *machine__mmap_name(struct machine *self, char *bf, size_t size) | ||
748 | { | ||
749 | if (machine__is_host(self)) | ||
750 | snprintf(bf, size, "[%s]", "kernel.kallsyms"); | ||
751 | else if (machine__is_default_guest(self)) | ||
752 | snprintf(bf, size, "[%s]", "guest.kernel.kallsyms"); | ||
753 | else | ||
754 | snprintf(bf, size, "[%s.%d]", "guest.kernel.kallsyms", self->pid); | ||
755 | |||
756 | return bf; | ||
757 | } | ||
758 | |||
759 | void machines__set_id_hdr_size(struct rb_root *machines, u16 id_hdr_size) | ||
760 | { | ||
761 | struct rb_node *node; | ||
762 | struct machine *machine; | ||
763 | |||
764 | for (node = rb_first(machines); node; node = rb_next(node)) { | ||
765 | machine = rb_entry(node, struct machine, rb_node); | ||
766 | machine->id_hdr_size = id_hdr_size; | ||
767 | } | ||
768 | |||
769 | return; | ||
770 | } | ||
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index d2250fc97e25..a887f2c9dfbb 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -57,33 +57,9 @@ struct map_groups { | |||
57 | struct machine *machine; | 57 | struct machine *machine; |
58 | }; | 58 | }; |
59 | 59 | ||
60 | /* Native host kernel uses -1 as pid index in machine */ | 60 | static inline struct kmap *map__kmap(struct map *map) |
61 | #define HOST_KERNEL_ID (-1) | ||
62 | #define DEFAULT_GUEST_KERNEL_ID (0) | ||
63 | |||
64 | struct machine { | ||
65 | struct rb_node rb_node; | ||
66 | pid_t pid; | ||
67 | u16 id_hdr_size; | ||
68 | char *root_dir; | ||
69 | struct rb_root threads; | ||
70 | struct list_head dead_threads; | ||
71 | struct thread *last_match; | ||
72 | struct list_head user_dsos; | ||
73 | struct list_head kernel_dsos; | ||
74 | struct map_groups kmaps; | ||
75 | struct map *vmlinux_maps[MAP__NR_TYPES]; | ||
76 | }; | ||
77 | |||
78 | static inline | ||
79 | struct map *machine__kernel_map(struct machine *self, enum map_type type) | ||
80 | { | ||
81 | return self->vmlinux_maps[type]; | ||
82 | } | ||
83 | |||
84 | static inline struct kmap *map__kmap(struct map *self) | ||
85 | { | 61 | { |
86 | return (struct kmap *)(self + 1); | 62 | return (struct kmap *)(map + 1); |
87 | } | 63 | } |
88 | 64 | ||
89 | static inline u64 map__map_ip(struct map *map, u64 ip) | 65 | static inline u64 map__map_ip(struct map *map, u64 ip) |
@@ -109,27 +85,27 @@ struct symbol; | |||
109 | 85 | ||
110 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | 86 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); |
111 | 87 | ||
112 | void map__init(struct map *self, enum map_type type, | 88 | void map__init(struct map *map, enum map_type type, |
113 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 89 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
114 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 90 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
115 | u64 pgoff, u32 pid, char *filename, | 91 | u64 pgoff, u32 pid, char *filename, |
116 | enum map_type type); | 92 | enum map_type type); |
117 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 93 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
118 | void map__delete(struct map *self); | 94 | void map__delete(struct map *map); |
119 | struct map *map__clone(struct map *self); | 95 | struct map *map__clone(struct map *map); |
120 | int map__overlap(struct map *l, struct map *r); | 96 | int map__overlap(struct map *l, struct map *r); |
121 | size_t map__fprintf(struct map *self, FILE *fp); | 97 | size_t map__fprintf(struct map *map, FILE *fp); |
122 | size_t map__fprintf_dsoname(struct map *map, FILE *fp); | 98 | size_t map__fprintf_dsoname(struct map *map, FILE *fp); |
123 | 99 | ||
124 | int map__load(struct map *self, symbol_filter_t filter); | 100 | int map__load(struct map *map, symbol_filter_t filter); |
125 | struct symbol *map__find_symbol(struct map *self, | 101 | struct symbol *map__find_symbol(struct map *map, |
126 | u64 addr, symbol_filter_t filter); | 102 | u64 addr, symbol_filter_t filter); |
127 | struct symbol *map__find_symbol_by_name(struct map *self, const char *name, | 103 | struct symbol *map__find_symbol_by_name(struct map *map, const char *name, |
128 | symbol_filter_t filter); | 104 | symbol_filter_t filter); |
129 | void map__fixup_start(struct map *self); | 105 | void map__fixup_start(struct map *map); |
130 | void map__fixup_end(struct map *self); | 106 | void map__fixup_end(struct map *map); |
131 | 107 | ||
132 | void map__reloc_vmlinux(struct map *self); | 108 | void map__reloc_vmlinux(struct map *map); |
133 | 109 | ||
134 | size_t __map_groups__fprintf_maps(struct map_groups *mg, | 110 | size_t __map_groups__fprintf_maps(struct map_groups *mg, |
135 | enum map_type type, int verbose, FILE *fp); | 111 | enum map_type type, int verbose, FILE *fp); |
@@ -143,44 +119,9 @@ int map_groups__clone(struct map_groups *mg, | |||
143 | size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp); | 119 | size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp); |
144 | size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp); | 120 | size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp); |
145 | 121 | ||
146 | typedef void (*machine__process_t)(struct machine *self, void *data); | ||
147 | |||
148 | void machines__process(struct rb_root *self, machine__process_t process, void *data); | ||
149 | struct machine *machines__add(struct rb_root *self, pid_t pid, | ||
150 | const char *root_dir); | ||
151 | struct machine *machines__find_host(struct rb_root *self); | ||
152 | struct machine *machines__find(struct rb_root *self, pid_t pid); | ||
153 | struct machine *machines__findnew(struct rb_root *self, pid_t pid); | ||
154 | void machines__set_id_hdr_size(struct rb_root *self, u16 id_hdr_size); | ||
155 | char *machine__mmap_name(struct machine *self, char *bf, size_t size); | ||
156 | int machine__init(struct machine *self, const char *root_dir, pid_t pid); | ||
157 | void machine__exit(struct machine *self); | ||
158 | void machine__delete(struct machine *self); | ||
159 | |||
160 | struct perf_evsel; | ||
161 | struct perf_sample; | ||
162 | int machine__resolve_callchain(struct machine *machine, | ||
163 | struct perf_evsel *evsel, | ||
164 | struct thread *thread, | ||
165 | struct perf_sample *sample, | ||
166 | struct symbol **parent); | ||
167 | int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, | 122 | int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, |
168 | u64 addr); | 123 | u64 addr); |
169 | 124 | ||
170 | /* | ||
171 | * Default guest kernel is defined by parameter --guestkallsyms | ||
172 | * and --guestmodules | ||
173 | */ | ||
174 | static inline bool machine__is_default_guest(struct machine *self) | ||
175 | { | ||
176 | return self ? self->pid == DEFAULT_GUEST_KERNEL_ID : false; | ||
177 | } | ||
178 | |||
179 | static inline bool machine__is_host(struct machine *self) | ||
180 | { | ||
181 | return self ? self->pid == HOST_KERNEL_ID : false; | ||
182 | } | ||
183 | |||
184 | static inline void map_groups__insert(struct map_groups *mg, struct map *map) | 125 | static inline void map_groups__insert(struct map_groups *mg, struct map *map) |
185 | { | 126 | { |
186 | maps__insert(&mg->maps[map->type], map); | 127 | maps__insert(&mg->maps[map->type], map); |
@@ -209,29 +150,6 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg, | |||
209 | struct map **mapp, | 150 | struct map **mapp, |
210 | symbol_filter_t filter); | 151 | symbol_filter_t filter); |
211 | 152 | ||
212 | |||
213 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid); | ||
214 | void machine__remove_thread(struct machine *machine, struct thread *th); | ||
215 | |||
216 | size_t machine__fprintf(struct machine *machine, FILE *fp); | ||
217 | |||
218 | static inline | ||
219 | struct symbol *machine__find_kernel_symbol(struct machine *self, | ||
220 | enum map_type type, u64 addr, | ||
221 | struct map **mapp, | ||
222 | symbol_filter_t filter) | ||
223 | { | ||
224 | return map_groups__find_symbol(&self->kmaps, type, addr, mapp, filter); | ||
225 | } | ||
226 | |||
227 | static inline | ||
228 | struct symbol *machine__find_kernel_function(struct machine *self, u64 addr, | ||
229 | struct map **mapp, | ||
230 | symbol_filter_t filter) | ||
231 | { | ||
232 | return machine__find_kernel_symbol(self, MAP__FUNCTION, addr, mapp, filter); | ||
233 | } | ||
234 | |||
235 | static inline | 153 | static inline |
236 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, | 154 | struct symbol *map_groups__find_function_by_name(struct map_groups *mg, |
237 | const char *name, struct map **mapp, | 155 | const char *name, struct map **mapp, |
@@ -240,22 +158,11 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *mg, | |||
240 | return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); | 158 | return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter); |
241 | } | 159 | } |
242 | 160 | ||
243 | static inline | ||
244 | struct symbol *machine__find_kernel_function_by_name(struct machine *self, | ||
245 | const char *name, | ||
246 | struct map **mapp, | ||
247 | symbol_filter_t filter) | ||
248 | { | ||
249 | return map_groups__find_function_by_name(&self->kmaps, name, mapp, | ||
250 | filter); | ||
251 | } | ||
252 | |||
253 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, | 161 | int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, |
254 | int verbose, FILE *fp); | 162 | int verbose, FILE *fp); |
255 | 163 | ||
256 | struct map *map_groups__find_by_name(struct map_groups *mg, | 164 | struct map *map_groups__find_by_name(struct map_groups *mg, |
257 | enum map_type type, const char *name); | 165 | enum map_type type, const char *name); |
258 | struct map *machine__new_module(struct machine *self, u64 start, const char *filename); | ||
259 | 166 | ||
260 | void map_groups__flush(struct map_groups *mg); | 167 | void map_groups__flush(struct map_groups *mg); |
261 | 168 | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 75c7b0fca6d9..c84f48cf9678 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -1,4 +1,4 @@ | |||
1 | #include "../../../include/linux/hw_breakpoint.h" | 1 | #include <linux/hw_breakpoint.h> |
2 | #include "util.h" | 2 | #include "util.h" |
3 | #include "../perf.h" | 3 | #include "../perf.h" |
4 | #include "evlist.h" | 4 | #include "evlist.h" |
@@ -380,8 +380,8 @@ static int add_tracepoint(struct list_head **listp, int *idx, | |||
380 | return 0; | 380 | return 0; |
381 | } | 381 | } |
382 | 382 | ||
383 | static int add_tracepoint_multi(struct list_head **list, int *idx, | 383 | static int add_tracepoint_multi_event(struct list_head **list, int *idx, |
384 | char *sys_name, char *evt_name) | 384 | char *sys_name, char *evt_name) |
385 | { | 385 | { |
386 | char evt_path[MAXPATHLEN]; | 386 | char evt_path[MAXPATHLEN]; |
387 | struct dirent *evt_ent; | 387 | struct dirent *evt_ent; |
@@ -408,6 +408,47 @@ static int add_tracepoint_multi(struct list_head **list, int *idx, | |||
408 | ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name); | 408 | ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name); |
409 | } | 409 | } |
410 | 410 | ||
411 | closedir(evt_dir); | ||
412 | return ret; | ||
413 | } | ||
414 | |||
415 | static int add_tracepoint_event(struct list_head **list, int *idx, | ||
416 | char *sys_name, char *evt_name) | ||
417 | { | ||
418 | return strpbrk(evt_name, "*?") ? | ||
419 | add_tracepoint_multi_event(list, idx, sys_name, evt_name) : | ||
420 | add_tracepoint(list, idx, sys_name, evt_name); | ||
421 | } | ||
422 | |||
423 | static int add_tracepoint_multi_sys(struct list_head **list, int *idx, | ||
424 | char *sys_name, char *evt_name) | ||
425 | { | ||
426 | struct dirent *events_ent; | ||
427 | DIR *events_dir; | ||
428 | int ret = 0; | ||
429 | |||
430 | events_dir = opendir(tracing_events_path); | ||
431 | if (!events_dir) { | ||
432 | perror("Can't open event dir"); | ||
433 | return -1; | ||
434 | } | ||
435 | |||
436 | while (!ret && (events_ent = readdir(events_dir))) { | ||
437 | if (!strcmp(events_ent->d_name, ".") | ||
438 | || !strcmp(events_ent->d_name, "..") | ||
439 | || !strcmp(events_ent->d_name, "enable") | ||
440 | || !strcmp(events_ent->d_name, "header_event") | ||
441 | || !strcmp(events_ent->d_name, "header_page")) | ||
442 | continue; | ||
443 | |||
444 | if (!strglobmatch(events_ent->d_name, sys_name)) | ||
445 | continue; | ||
446 | |||
447 | ret = add_tracepoint_event(list, idx, events_ent->d_name, | ||
448 | evt_name); | ||
449 | } | ||
450 | |||
451 | closedir(events_dir); | ||
411 | return ret; | 452 | return ret; |
412 | } | 453 | } |
413 | 454 | ||
@@ -420,9 +461,10 @@ int parse_events_add_tracepoint(struct list_head **list, int *idx, | |||
420 | if (ret) | 461 | if (ret) |
421 | return ret; | 462 | return ret; |
422 | 463 | ||
423 | return strpbrk(event, "*?") ? | 464 | if (strpbrk(sys, "*?")) |
424 | add_tracepoint_multi(list, idx, sys, event) : | 465 | return add_tracepoint_multi_sys(list, idx, sys, event); |
425 | add_tracepoint(list, idx, sys, event); | 466 | else |
467 | return add_tracepoint_event(list, idx, sys, event); | ||
426 | } | 468 | } |
427 | 469 | ||
428 | static int | 470 | static int |
@@ -492,7 +534,7 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx, | |||
492 | } | 534 | } |
493 | 535 | ||
494 | static int config_term(struct perf_event_attr *attr, | 536 | static int config_term(struct perf_event_attr *attr, |
495 | struct parse_events__term *term) | 537 | struct parse_events_term *term) |
496 | { | 538 | { |
497 | #define CHECK_TYPE_VAL(type) \ | 539 | #define CHECK_TYPE_VAL(type) \ |
498 | do { \ | 540 | do { \ |
@@ -537,7 +579,7 @@ do { \ | |||
537 | static int config_attr(struct perf_event_attr *attr, | 579 | static int config_attr(struct perf_event_attr *attr, |
538 | struct list_head *head, int fail) | 580 | struct list_head *head, int fail) |
539 | { | 581 | { |
540 | struct parse_events__term *term; | 582 | struct parse_events_term *term; |
541 | 583 | ||
542 | list_for_each_entry(term, head, list) | 584 | list_for_each_entry(term, head, list) |
543 | if (config_term(attr, term) && fail) | 585 | if (config_term(attr, term) && fail) |
@@ -563,14 +605,14 @@ int parse_events_add_numeric(struct list_head **list, int *idx, | |||
563 | return add_event(list, idx, &attr, NULL); | 605 | return add_event(list, idx, &attr, NULL); |
564 | } | 606 | } |
565 | 607 | ||
566 | static int parse_events__is_name_term(struct parse_events__term *term) | 608 | static int parse_events__is_name_term(struct parse_events_term *term) |
567 | { | 609 | { |
568 | return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; | 610 | return term->type_term == PARSE_EVENTS__TERM_TYPE_NAME; |
569 | } | 611 | } |
570 | 612 | ||
571 | static char *pmu_event_name(struct list_head *head_terms) | 613 | static char *pmu_event_name(struct list_head *head_terms) |
572 | { | 614 | { |
573 | struct parse_events__term *term; | 615 | struct parse_events_term *term; |
574 | 616 | ||
575 | list_for_each_entry(term, head_terms, list) | 617 | list_for_each_entry(term, head_terms, list) |
576 | if (parse_events__is_name_term(term)) | 618 | if (parse_events__is_name_term(term)) |
@@ -657,14 +699,6 @@ static int get_event_modifier(struct event_modifier *mod, char *str, | |||
657 | int exclude = eu | ek | eh; | 699 | int exclude = eu | ek | eh; |
658 | int exclude_GH = evsel ? evsel->exclude_GH : 0; | 700 | int exclude_GH = evsel ? evsel->exclude_GH : 0; |
659 | 701 | ||
660 | /* | ||
661 | * We are here for group and 'GH' was not set as event | ||
662 | * modifier and whatever event/group modifier override | ||
663 | * default 'GH' setup. | ||
664 | */ | ||
665 | if (evsel && !exclude_GH) | ||
666 | eH = eG = 0; | ||
667 | |||
668 | memset(mod, 0, sizeof(*mod)); | 702 | memset(mod, 0, sizeof(*mod)); |
669 | 703 | ||
670 | while (*str) { | 704 | while (*str) { |
@@ -722,6 +756,27 @@ static int get_event_modifier(struct event_modifier *mod, char *str, | |||
722 | return 0; | 756 | return 0; |
723 | } | 757 | } |
724 | 758 | ||
759 | /* | ||
760 | * Basic modifier sanity check to validate it contains only one | ||
761 | * instance of any modifier (apart from 'p') present. | ||
762 | */ | ||
763 | static int check_modifier(char *str) | ||
764 | { | ||
765 | char *p = str; | ||
766 | |||
767 | /* The sizeof includes 0 byte as well. */ | ||
768 | if (strlen(str) > (sizeof("ukhGHppp") - 1)) | ||
769 | return -1; | ||
770 | |||
771 | while (*p) { | ||
772 | if (*p != 'p' && strchr(p + 1, *p)) | ||
773 | return -1; | ||
774 | p++; | ||
775 | } | ||
776 | |||
777 | return 0; | ||
778 | } | ||
779 | |||
725 | int parse_events__modifier_event(struct list_head *list, char *str, bool add) | 780 | int parse_events__modifier_event(struct list_head *list, char *str, bool add) |
726 | { | 781 | { |
727 | struct perf_evsel *evsel; | 782 | struct perf_evsel *evsel; |
@@ -730,6 +785,9 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add) | |||
730 | if (str == NULL) | 785 | if (str == NULL) |
731 | return 0; | 786 | return 0; |
732 | 787 | ||
788 | if (check_modifier(str)) | ||
789 | return -EINVAL; | ||
790 | |||
733 | if (!add && get_event_modifier(&mod, str, NULL)) | 791 | if (!add && get_event_modifier(&mod, str, NULL)) |
734 | return -EINVAL; | 792 | return -EINVAL; |
735 | 793 | ||
@@ -790,7 +848,7 @@ static int parse_events__scanner(const char *str, void *data, int start_token) | |||
790 | */ | 848 | */ |
791 | int parse_events_terms(struct list_head *terms, const char *str) | 849 | int parse_events_terms(struct list_head *terms, const char *str) |
792 | { | 850 | { |
793 | struct parse_events_data__terms data = { | 851 | struct parse_events_terms data = { |
794 | .terms = NULL, | 852 | .terms = NULL, |
795 | }; | 853 | }; |
796 | int ret; | 854 | int ret; |
@@ -806,10 +864,9 @@ int parse_events_terms(struct list_head *terms, const char *str) | |||
806 | return ret; | 864 | return ret; |
807 | } | 865 | } |
808 | 866 | ||
809 | int parse_events(struct perf_evlist *evlist, const char *str, | 867 | int parse_events(struct perf_evlist *evlist, const char *str) |
810 | int unset __maybe_unused) | ||
811 | { | 868 | { |
812 | struct parse_events_data__events data = { | 869 | struct parse_events_evlist data = { |
813 | .list = LIST_HEAD_INIT(data.list), | 870 | .list = LIST_HEAD_INIT(data.list), |
814 | .idx = evlist->nr_entries, | 871 | .idx = evlist->nr_entries, |
815 | }; | 872 | }; |
@@ -819,6 +876,7 @@ int parse_events(struct perf_evlist *evlist, const char *str, | |||
819 | if (!ret) { | 876 | if (!ret) { |
820 | int entries = data.idx - evlist->nr_entries; | 877 | int entries = data.idx - evlist->nr_entries; |
821 | perf_evlist__splice_list_tail(evlist, &data.list, entries); | 878 | perf_evlist__splice_list_tail(evlist, &data.list, entries); |
879 | evlist->nr_groups += data.nr_groups; | ||
822 | return 0; | 880 | return 0; |
823 | } | 881 | } |
824 | 882 | ||
@@ -827,8 +885,6 @@ int parse_events(struct perf_evlist *evlist, const char *str, | |||
827 | * Both call perf_evlist__delete in case of error, so we dont | 885 | * Both call perf_evlist__delete in case of error, so we dont |
828 | * need to bother. | 886 | * need to bother. |
829 | */ | 887 | */ |
830 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); | ||
831 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
832 | return ret; | 888 | return ret; |
833 | } | 889 | } |
834 | 890 | ||
@@ -836,7 +892,13 @@ int parse_events_option(const struct option *opt, const char *str, | |||
836 | int unset __maybe_unused) | 892 | int unset __maybe_unused) |
837 | { | 893 | { |
838 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; | 894 | struct perf_evlist *evlist = *(struct perf_evlist **)opt->value; |
839 | return parse_events(evlist, str, unset); | 895 | int ret = parse_events(evlist, str); |
896 | |||
897 | if (ret) { | ||
898 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); | ||
899 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
900 | } | ||
901 | return ret; | ||
840 | } | 902 | } |
841 | 903 | ||
842 | int parse_filter(const struct option *opt, const char *str, | 904 | int parse_filter(const struct option *opt, const char *str, |
@@ -1081,7 +1143,7 @@ void print_events(const char *event_glob, bool name_only) | |||
1081 | printf(" %-50s [%s]\n", | 1143 | printf(" %-50s [%s]\n", |
1082 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", | 1144 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", |
1083 | event_type_descriptors[PERF_TYPE_RAW]); | 1145 | event_type_descriptors[PERF_TYPE_RAW]); |
1084 | printf(" (see 'perf list --help' on how to encode it)\n"); | 1146 | printf(" (see 'man perf-list' on how to encode it)\n"); |
1085 | printf("\n"); | 1147 | printf("\n"); |
1086 | 1148 | ||
1087 | printf(" %-50s [%s]\n", | 1149 | printf(" %-50s [%s]\n", |
@@ -1093,16 +1155,16 @@ void print_events(const char *event_glob, bool name_only) | |||
1093 | print_tracepoint_events(NULL, NULL, name_only); | 1155 | print_tracepoint_events(NULL, NULL, name_only); |
1094 | } | 1156 | } |
1095 | 1157 | ||
1096 | int parse_events__is_hardcoded_term(struct parse_events__term *term) | 1158 | int parse_events__is_hardcoded_term(struct parse_events_term *term) |
1097 | { | 1159 | { |
1098 | return term->type_term != PARSE_EVENTS__TERM_TYPE_USER; | 1160 | return term->type_term != PARSE_EVENTS__TERM_TYPE_USER; |
1099 | } | 1161 | } |
1100 | 1162 | ||
1101 | static int new_term(struct parse_events__term **_term, int type_val, | 1163 | static int new_term(struct parse_events_term **_term, int type_val, |
1102 | int type_term, char *config, | 1164 | int type_term, char *config, |
1103 | char *str, u64 num) | 1165 | char *str, u64 num) |
1104 | { | 1166 | { |
1105 | struct parse_events__term *term; | 1167 | struct parse_events_term *term; |
1106 | 1168 | ||
1107 | term = zalloc(sizeof(*term)); | 1169 | term = zalloc(sizeof(*term)); |
1108 | if (!term) | 1170 | if (!term) |
@@ -1128,22 +1190,40 @@ static int new_term(struct parse_events__term **_term, int type_val, | |||
1128 | return 0; | 1190 | return 0; |
1129 | } | 1191 | } |
1130 | 1192 | ||
1131 | int parse_events__term_num(struct parse_events__term **term, | 1193 | int parse_events_term__num(struct parse_events_term **term, |
1132 | int type_term, char *config, u64 num) | 1194 | int type_term, char *config, u64 num) |
1133 | { | 1195 | { |
1134 | return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, | 1196 | return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term, |
1135 | config, NULL, num); | 1197 | config, NULL, num); |
1136 | } | 1198 | } |
1137 | 1199 | ||
1138 | int parse_events__term_str(struct parse_events__term **term, | 1200 | int parse_events_term__str(struct parse_events_term **term, |
1139 | int type_term, char *config, char *str) | 1201 | int type_term, char *config, char *str) |
1140 | { | 1202 | { |
1141 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, | 1203 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, type_term, |
1142 | config, str, 0); | 1204 | config, str, 0); |
1143 | } | 1205 | } |
1144 | 1206 | ||
1145 | int parse_events__term_clone(struct parse_events__term **new, | 1207 | int parse_events_term__sym_hw(struct parse_events_term **term, |
1146 | struct parse_events__term *term) | 1208 | char *config, unsigned idx) |
1209 | { | ||
1210 | struct event_symbol *sym; | ||
1211 | |||
1212 | BUG_ON(idx >= PERF_COUNT_HW_MAX); | ||
1213 | sym = &event_symbols_hw[idx]; | ||
1214 | |||
1215 | if (config) | ||
1216 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, | ||
1217 | PARSE_EVENTS__TERM_TYPE_USER, config, | ||
1218 | (char *) sym->symbol, 0); | ||
1219 | else | ||
1220 | return new_term(term, PARSE_EVENTS__TERM_TYPE_STR, | ||
1221 | PARSE_EVENTS__TERM_TYPE_USER, | ||
1222 | (char *) "event", (char *) sym->symbol, 0); | ||
1223 | } | ||
1224 | |||
1225 | int parse_events_term__clone(struct parse_events_term **new, | ||
1226 | struct parse_events_term *term) | ||
1147 | { | 1227 | { |
1148 | return new_term(new, term->type_val, term->type_term, term->config, | 1228 | return new_term(new, term->type_val, term->type_term, term->config, |
1149 | term->val.str, term->val.num); | 1229 | term->val.str, term->val.num); |
@@ -1151,7 +1231,7 @@ int parse_events__term_clone(struct parse_events__term **new, | |||
1151 | 1231 | ||
1152 | void parse_events__free_terms(struct list_head *terms) | 1232 | void parse_events__free_terms(struct list_head *terms) |
1153 | { | 1233 | { |
1154 | struct parse_events__term *term, *h; | 1234 | struct parse_events_term *term, *h; |
1155 | 1235 | ||
1156 | list_for_each_entry_safe(term, h, terms, list) | 1236 | list_for_each_entry_safe(term, h, terms, list) |
1157 | free(term); | 1237 | free(term); |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 839230ceb18b..8a4859315fd9 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -7,7 +7,7 @@ | |||
7 | #include <linux/list.h> | 7 | #include <linux/list.h> |
8 | #include <stdbool.h> | 8 | #include <stdbool.h> |
9 | #include "types.h" | 9 | #include "types.h" |
10 | #include "../../../include/uapi/linux/perf_event.h" | 10 | #include <linux/perf_event.h> |
11 | #include "types.h" | 11 | #include "types.h" |
12 | 12 | ||
13 | struct list_head; | 13 | struct list_head; |
@@ -29,8 +29,7 @@ const char *event_type(int type); | |||
29 | 29 | ||
30 | extern int parse_events_option(const struct option *opt, const char *str, | 30 | extern int parse_events_option(const struct option *opt, const char *str, |
31 | int unset); | 31 | int unset); |
32 | extern int parse_events(struct perf_evlist *evlist, const char *str, | 32 | extern int parse_events(struct perf_evlist *evlist, const char *str); |
33 | int unset); | ||
34 | extern int parse_events_terms(struct list_head *terms, const char *str); | 33 | extern int parse_events_terms(struct list_head *terms, const char *str); |
35 | extern int parse_filter(const struct option *opt, const char *str, int unset); | 34 | extern int parse_filter(const struct option *opt, const char *str, int unset); |
36 | 35 | ||
@@ -51,7 +50,7 @@ enum { | |||
51 | PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, | 50 | PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, |
52 | }; | 51 | }; |
53 | 52 | ||
54 | struct parse_events__term { | 53 | struct parse_events_term { |
55 | char *config; | 54 | char *config; |
56 | union { | 55 | union { |
57 | char *str; | 56 | char *str; |
@@ -62,22 +61,25 @@ struct parse_events__term { | |||
62 | struct list_head list; | 61 | struct list_head list; |
63 | }; | 62 | }; |
64 | 63 | ||
65 | struct parse_events_data__events { | 64 | struct parse_events_evlist { |
66 | struct list_head list; | 65 | struct list_head list; |
67 | int idx; | 66 | int idx; |
67 | int nr_groups; | ||
68 | }; | 68 | }; |
69 | 69 | ||
70 | struct parse_events_data__terms { | 70 | struct parse_events_terms { |
71 | struct list_head *terms; | 71 | struct list_head *terms; |
72 | }; | 72 | }; |
73 | 73 | ||
74 | int parse_events__is_hardcoded_term(struct parse_events__term *term); | 74 | int parse_events__is_hardcoded_term(struct parse_events_term *term); |
75 | int parse_events__term_num(struct parse_events__term **_term, | 75 | int parse_events_term__num(struct parse_events_term **_term, |
76 | int type_term, char *config, u64 num); | 76 | int type_term, char *config, u64 num); |
77 | int parse_events__term_str(struct parse_events__term **_term, | 77 | int parse_events_term__str(struct parse_events_term **_term, |
78 | int type_term, char *config, char *str); | 78 | int type_term, char *config, char *str); |
79 | int parse_events__term_clone(struct parse_events__term **new, | 79 | int parse_events_term__sym_hw(struct parse_events_term **term, |
80 | struct parse_events__term *term); | 80 | char *config, unsigned idx); |
81 | int parse_events_term__clone(struct parse_events_term **new, | ||
82 | struct parse_events_term *term); | ||
81 | void parse_events__free_terms(struct list_head *terms); | 83 | void parse_events__free_terms(struct list_head *terms); |
82 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); | 84 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); |
83 | int parse_events__modifier_group(struct list_head *list, char *event_mod); | 85 | int parse_events__modifier_group(struct list_head *list, char *event_mod); |
@@ -97,7 +99,6 @@ void parse_events__set_leader(char *name, struct list_head *list); | |||
97 | void parse_events_update_lists(struct list_head *list_event, | 99 | void parse_events_update_lists(struct list_head *list_event, |
98 | struct list_head *list_all); | 100 | struct list_head *list_all); |
99 | void parse_events_error(void *data, void *scanner, char const *msg); | 101 | void parse_events_error(void *data, void *scanner, char const *msg); |
100 | int parse_events__test(void); | ||
101 | 102 | ||
102 | void print_events(const char *event_glob, bool name_only); | 103 | void print_events(const char *event_glob, bool name_only); |
103 | void print_events_type(u8 type); | 104 | void print_events_type(u8 type); |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index c87efc12579d..e9d1134c2c68 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
@@ -81,7 +81,8 @@ num_dec [0-9]+ | |||
81 | num_hex 0x[a-fA-F0-9]+ | 81 | num_hex 0x[a-fA-F0-9]+ |
82 | num_raw_hex [a-fA-F0-9]+ | 82 | num_raw_hex [a-fA-F0-9]+ |
83 | name [a-zA-Z_*?][a-zA-Z0-9_*?]* | 83 | name [a-zA-Z_*?][a-zA-Z0-9_*?]* |
84 | modifier_event [ukhpGH]{1,8} | 84 | name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?]* |
85 | modifier_event [ukhpGH]+ | ||
85 | modifier_bp [rwx]{1,3} | 86 | modifier_bp [rwx]{1,3} |
86 | 87 | ||
87 | %% | 88 | %% |
@@ -168,6 +169,7 @@ period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } | |||
168 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } | 169 | branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } |
169 | , { return ','; } | 170 | , { return ','; } |
170 | "/" { BEGIN(INITIAL); return '/'; } | 171 | "/" { BEGIN(INITIAL); return '/'; } |
172 | {name_minus} { return str(yyscanner, PE_NAME); } | ||
171 | } | 173 | } |
172 | 174 | ||
173 | mem: { BEGIN(mem); return PE_PREFIX_MEM; } | 175 | mem: { BEGIN(mem); return PE_PREFIX_MEM; } |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index cd88209e3c58..afc44c18dfe1 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -1,5 +1,4 @@ | |||
1 | %pure-parser | 1 | %pure-parser |
2 | %name-prefix "parse_events_" | ||
3 | %parse-param {void *_data} | 2 | %parse-param {void *_data} |
4 | %parse-param {void *scanner} | 3 | %parse-param {void *scanner} |
5 | %lex-param {void* scanner} | 4 | %lex-param {void* scanner} |
@@ -23,6 +22,14 @@ do { \ | |||
23 | YYABORT; \ | 22 | YYABORT; \ |
24 | } while (0) | 23 | } while (0) |
25 | 24 | ||
25 | static inc_group_count(struct list_head *list, | ||
26 | struct parse_events_evlist *data) | ||
27 | { | ||
28 | /* Count groups only have more than 1 members */ | ||
29 | if (!list_is_last(list->next, list)) | ||
30 | data->nr_groups++; | ||
31 | } | ||
32 | |||
26 | %} | 33 | %} |
27 | 34 | ||
28 | %token PE_START_EVENTS PE_START_TERMS | 35 | %token PE_START_EVENTS PE_START_TERMS |
@@ -68,7 +75,7 @@ do { \ | |||
68 | char *str; | 75 | char *str; |
69 | u64 num; | 76 | u64 num; |
70 | struct list_head *head; | 77 | struct list_head *head; |
71 | struct parse_events__term *term; | 78 | struct parse_events_term *term; |
72 | } | 79 | } |
73 | %% | 80 | %% |
74 | 81 | ||
@@ -79,7 +86,7 @@ PE_START_TERMS start_terms | |||
79 | 86 | ||
80 | start_events: groups | 87 | start_events: groups |
81 | { | 88 | { |
82 | struct parse_events_data__events *data = _data; | 89 | struct parse_events_evlist *data = _data; |
83 | 90 | ||
84 | parse_events_update_lists($1, &data->list); | 91 | parse_events_update_lists($1, &data->list); |
85 | } | 92 | } |
@@ -123,6 +130,7 @@ PE_NAME '{' events '}' | |||
123 | { | 130 | { |
124 | struct list_head *list = $3; | 131 | struct list_head *list = $3; |
125 | 132 | ||
133 | inc_group_count(list, _data); | ||
126 | parse_events__set_leader($1, list); | 134 | parse_events__set_leader($1, list); |
127 | $$ = list; | 135 | $$ = list; |
128 | } | 136 | } |
@@ -131,6 +139,7 @@ PE_NAME '{' events '}' | |||
131 | { | 139 | { |
132 | struct list_head *list = $2; | 140 | struct list_head *list = $2; |
133 | 141 | ||
142 | inc_group_count(list, _data); | ||
134 | parse_events__set_leader(NULL, list); | 143 | parse_events__set_leader(NULL, list); |
135 | $$ = list; | 144 | $$ = list; |
136 | } | 145 | } |
@@ -186,7 +195,7 @@ event_def: event_pmu | | |||
186 | event_pmu: | 195 | event_pmu: |
187 | PE_NAME '/' event_config '/' | 196 | PE_NAME '/' event_config '/' |
188 | { | 197 | { |
189 | struct parse_events_data__events *data = _data; | 198 | struct parse_events_evlist *data = _data; |
190 | struct list_head *list = NULL; | 199 | struct list_head *list = NULL; |
191 | 200 | ||
192 | ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3)); | 201 | ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3)); |
@@ -202,7 +211,7 @@ PE_VALUE_SYM_SW | |||
202 | event_legacy_symbol: | 211 | event_legacy_symbol: |
203 | value_sym '/' event_config '/' | 212 | value_sym '/' event_config '/' |
204 | { | 213 | { |
205 | struct parse_events_data__events *data = _data; | 214 | struct parse_events_evlist *data = _data; |
206 | struct list_head *list = NULL; | 215 | struct list_head *list = NULL; |
207 | int type = $1 >> 16; | 216 | int type = $1 >> 16; |
208 | int config = $1 & 255; | 217 | int config = $1 & 255; |
@@ -215,7 +224,7 @@ value_sym '/' event_config '/' | |||
215 | | | 224 | | |
216 | value_sym sep_slash_dc | 225 | value_sym sep_slash_dc |
217 | { | 226 | { |
218 | struct parse_events_data__events *data = _data; | 227 | struct parse_events_evlist *data = _data; |
219 | struct list_head *list = NULL; | 228 | struct list_head *list = NULL; |
220 | int type = $1 >> 16; | 229 | int type = $1 >> 16; |
221 | int config = $1 & 255; | 230 | int config = $1 & 255; |
@@ -228,7 +237,7 @@ value_sym sep_slash_dc | |||
228 | event_legacy_cache: | 237 | event_legacy_cache: |
229 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT | 238 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT |
230 | { | 239 | { |
231 | struct parse_events_data__events *data = _data; | 240 | struct parse_events_evlist *data = _data; |
232 | struct list_head *list = NULL; | 241 | struct list_head *list = NULL; |
233 | 242 | ||
234 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5)); | 243 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5)); |
@@ -237,7 +246,7 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT | |||
237 | | | 246 | | |
238 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT | 247 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT |
239 | { | 248 | { |
240 | struct parse_events_data__events *data = _data; | 249 | struct parse_events_evlist *data = _data; |
241 | struct list_head *list = NULL; | 250 | struct list_head *list = NULL; |
242 | 251 | ||
243 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, NULL)); | 252 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, NULL)); |
@@ -246,7 +255,7 @@ PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT | |||
246 | | | 255 | | |
247 | PE_NAME_CACHE_TYPE | 256 | PE_NAME_CACHE_TYPE |
248 | { | 257 | { |
249 | struct parse_events_data__events *data = _data; | 258 | struct parse_events_evlist *data = _data; |
250 | struct list_head *list = NULL; | 259 | struct list_head *list = NULL; |
251 | 260 | ||
252 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL)); | 261 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL)); |
@@ -256,7 +265,7 @@ PE_NAME_CACHE_TYPE | |||
256 | event_legacy_mem: | 265 | event_legacy_mem: |
257 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | 266 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc |
258 | { | 267 | { |
259 | struct parse_events_data__events *data = _data; | 268 | struct parse_events_evlist *data = _data; |
260 | struct list_head *list = NULL; | 269 | struct list_head *list = NULL; |
261 | 270 | ||
262 | ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, | 271 | ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, |
@@ -266,7 +275,7 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | |||
266 | | | 275 | | |
267 | PE_PREFIX_MEM PE_VALUE sep_dc | 276 | PE_PREFIX_MEM PE_VALUE sep_dc |
268 | { | 277 | { |
269 | struct parse_events_data__events *data = _data; | 278 | struct parse_events_evlist *data = _data; |
270 | struct list_head *list = NULL; | 279 | struct list_head *list = NULL; |
271 | 280 | ||
272 | ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, | 281 | ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, |
@@ -277,7 +286,7 @@ PE_PREFIX_MEM PE_VALUE sep_dc | |||
277 | event_legacy_tracepoint: | 286 | event_legacy_tracepoint: |
278 | PE_NAME ':' PE_NAME | 287 | PE_NAME ':' PE_NAME |
279 | { | 288 | { |
280 | struct parse_events_data__events *data = _data; | 289 | struct parse_events_evlist *data = _data; |
281 | struct list_head *list = NULL; | 290 | struct list_head *list = NULL; |
282 | 291 | ||
283 | ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3)); | 292 | ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3)); |
@@ -287,7 +296,7 @@ PE_NAME ':' PE_NAME | |||
287 | event_legacy_numeric: | 296 | event_legacy_numeric: |
288 | PE_VALUE ':' PE_VALUE | 297 | PE_VALUE ':' PE_VALUE |
289 | { | 298 | { |
290 | struct parse_events_data__events *data = _data; | 299 | struct parse_events_evlist *data = _data; |
291 | struct list_head *list = NULL; | 300 | struct list_head *list = NULL; |
292 | 301 | ||
293 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL)); | 302 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL)); |
@@ -297,7 +306,7 @@ PE_VALUE ':' PE_VALUE | |||
297 | event_legacy_raw: | 306 | event_legacy_raw: |
298 | PE_RAW | 307 | PE_RAW |
299 | { | 308 | { |
300 | struct parse_events_data__events *data = _data; | 309 | struct parse_events_evlist *data = _data; |
301 | struct list_head *list = NULL; | 310 | struct list_head *list = NULL; |
302 | 311 | ||
303 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, | 312 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, |
@@ -307,7 +316,7 @@ PE_RAW | |||
307 | 316 | ||
308 | start_terms: event_config | 317 | start_terms: event_config |
309 | { | 318 | { |
310 | struct parse_events_data__terms *data = _data; | 319 | struct parse_events_terms *data = _data; |
311 | data->terms = $1; | 320 | data->terms = $1; |
312 | } | 321 | } |
313 | 322 | ||
@@ -315,7 +324,7 @@ event_config: | |||
315 | event_config ',' event_term | 324 | event_config ',' event_term |
316 | { | 325 | { |
317 | struct list_head *head = $1; | 326 | struct list_head *head = $1; |
318 | struct parse_events__term *term = $3; | 327 | struct parse_events_term *term = $3; |
319 | 328 | ||
320 | ABORT_ON(!head); | 329 | ABORT_ON(!head); |
321 | list_add_tail(&term->list, head); | 330 | list_add_tail(&term->list, head); |
@@ -325,7 +334,7 @@ event_config ',' event_term | |||
325 | event_term | 334 | event_term |
326 | { | 335 | { |
327 | struct list_head *head = malloc(sizeof(*head)); | 336 | struct list_head *head = malloc(sizeof(*head)); |
328 | struct parse_events__term *term = $1; | 337 | struct parse_events_term *term = $1; |
329 | 338 | ||
330 | ABORT_ON(!head); | 339 | ABORT_ON(!head); |
331 | INIT_LIST_HEAD(head); | 340 | INIT_LIST_HEAD(head); |
@@ -336,52 +345,70 @@ event_term | |||
336 | event_term: | 345 | event_term: |
337 | PE_NAME '=' PE_NAME | 346 | PE_NAME '=' PE_NAME |
338 | { | 347 | { |
339 | struct parse_events__term *term; | 348 | struct parse_events_term *term; |
340 | 349 | ||
341 | ABORT_ON(parse_events__term_str(&term, PARSE_EVENTS__TERM_TYPE_USER, | 350 | ABORT_ON(parse_events_term__str(&term, PARSE_EVENTS__TERM_TYPE_USER, |
342 | $1, $3)); | 351 | $1, $3)); |
343 | $$ = term; | 352 | $$ = term; |
344 | } | 353 | } |
345 | | | 354 | | |
346 | PE_NAME '=' PE_VALUE | 355 | PE_NAME '=' PE_VALUE |
347 | { | 356 | { |
348 | struct parse_events__term *term; | 357 | struct parse_events_term *term; |
349 | 358 | ||
350 | ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER, | 359 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, |
351 | $1, $3)); | 360 | $1, $3)); |
352 | $$ = term; | 361 | $$ = term; |
353 | } | 362 | } |
354 | | | 363 | | |
364 | PE_NAME '=' PE_VALUE_SYM_HW | ||
365 | { | ||
366 | struct parse_events_term *term; | ||
367 | int config = $3 & 255; | ||
368 | |||
369 | ABORT_ON(parse_events_term__sym_hw(&term, $1, config)); | ||
370 | $$ = term; | ||
371 | } | ||
372 | | | ||
355 | PE_NAME | 373 | PE_NAME |
356 | { | 374 | { |
357 | struct parse_events__term *term; | 375 | struct parse_events_term *term; |
358 | 376 | ||
359 | ABORT_ON(parse_events__term_num(&term, PARSE_EVENTS__TERM_TYPE_USER, | 377 | ABORT_ON(parse_events_term__num(&term, PARSE_EVENTS__TERM_TYPE_USER, |
360 | $1, 1)); | 378 | $1, 1)); |
361 | $$ = term; | 379 | $$ = term; |
362 | } | 380 | } |
363 | | | 381 | | |
382 | PE_VALUE_SYM_HW | ||
383 | { | ||
384 | struct parse_events_term *term; | ||
385 | int config = $1 & 255; | ||
386 | |||
387 | ABORT_ON(parse_events_term__sym_hw(&term, NULL, config)); | ||
388 | $$ = term; | ||
389 | } | ||
390 | | | ||
364 | PE_TERM '=' PE_NAME | 391 | PE_TERM '=' PE_NAME |
365 | { | 392 | { |
366 | struct parse_events__term *term; | 393 | struct parse_events_term *term; |
367 | 394 | ||
368 | ABORT_ON(parse_events__term_str(&term, (int)$1, NULL, $3)); | 395 | ABORT_ON(parse_events_term__str(&term, (int)$1, NULL, $3)); |
369 | $$ = term; | 396 | $$ = term; |
370 | } | 397 | } |
371 | | | 398 | | |
372 | PE_TERM '=' PE_VALUE | 399 | PE_TERM '=' PE_VALUE |
373 | { | 400 | { |
374 | struct parse_events__term *term; | 401 | struct parse_events_term *term; |
375 | 402 | ||
376 | ABORT_ON(parse_events__term_num(&term, (int)$1, NULL, $3)); | 403 | ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, $3)); |
377 | $$ = term; | 404 | $$ = term; |
378 | } | 405 | } |
379 | | | 406 | | |
380 | PE_TERM | 407 | PE_TERM |
381 | { | 408 | { |
382 | struct parse_events__term *term; | 409 | struct parse_events_term *term; |
383 | 410 | ||
384 | ABORT_ON(parse_events__term_num(&term, (int)$1, NULL, 1)); | 411 | ABORT_ON(parse_events_term__num(&term, (int)$1, NULL, 1)); |
385 | $$ = term; | 412 | $$ = term; |
386 | } | 413 | } |
387 | 414 | ||
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 8a2229da594f..4c6f9c490a8d 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -1,4 +1,3 @@ | |||
1 | |||
2 | #include <linux/list.h> | 1 | #include <linux/list.h> |
3 | #include <sys/types.h> | 2 | #include <sys/types.h> |
4 | #include <sys/stat.h> | 3 | #include <sys/stat.h> |
@@ -11,6 +10,19 @@ | |||
11 | #include "parse-events.h" | 10 | #include "parse-events.h" |
12 | #include "cpumap.h" | 11 | #include "cpumap.h" |
13 | 12 | ||
13 | struct perf_pmu_alias { | ||
14 | char *name; | ||
15 | struct list_head terms; | ||
16 | struct list_head list; | ||
17 | }; | ||
18 | |||
19 | struct perf_pmu_format { | ||
20 | char *name; | ||
21 | int value; | ||
22 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | ||
23 | struct list_head list; | ||
24 | }; | ||
25 | |||
14 | #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" | 26 | #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" |
15 | 27 | ||
16 | int perf_pmu_parse(struct list_head *list, char *name); | 28 | int perf_pmu_parse(struct list_head *list, char *name); |
@@ -22,7 +34,7 @@ static LIST_HEAD(pmus); | |||
22 | * Parse & process all the sysfs attributes located under | 34 | * Parse & process all the sysfs attributes located under |
23 | * the directory specified in 'dir' parameter. | 35 | * the directory specified in 'dir' parameter. |
24 | */ | 36 | */ |
25 | static int pmu_format_parse(char *dir, struct list_head *head) | 37 | int perf_pmu__format_parse(char *dir, struct list_head *head) |
26 | { | 38 | { |
27 | struct dirent *evt_ent; | 39 | struct dirent *evt_ent; |
28 | DIR *format_dir; | 40 | DIR *format_dir; |
@@ -77,7 +89,7 @@ static int pmu_format(char *name, struct list_head *format) | |||
77 | if (stat(path, &st) < 0) | 89 | if (stat(path, &st) < 0) |
78 | return 0; /* no error if format does not exist */ | 90 | return 0; /* no error if format does not exist */ |
79 | 91 | ||
80 | if (pmu_format_parse(path, format)) | 92 | if (perf_pmu__format_parse(path, format)) |
81 | return -1; | 93 | return -1; |
82 | 94 | ||
83 | return 0; | 95 | return 0; |
@@ -85,7 +97,7 @@ static int pmu_format(char *name, struct list_head *format) | |||
85 | 97 | ||
86 | static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) | 98 | static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) |
87 | { | 99 | { |
88 | struct perf_pmu__alias *alias; | 100 | struct perf_pmu_alias *alias; |
89 | char buf[256]; | 101 | char buf[256]; |
90 | int ret; | 102 | int ret; |
91 | 103 | ||
@@ -164,7 +176,7 @@ static int pmu_aliases(char *name, struct list_head *head) | |||
164 | "%s/bus/event_source/devices/%s/events", sysfs, name); | 176 | "%s/bus/event_source/devices/%s/events", sysfs, name); |
165 | 177 | ||
166 | if (stat(path, &st) < 0) | 178 | if (stat(path, &st) < 0) |
167 | return -1; | 179 | return 0; /* no error if 'events' does not exist */ |
168 | 180 | ||
169 | if (pmu_aliases_parse(path, head)) | 181 | if (pmu_aliases_parse(path, head)) |
170 | return -1; | 182 | return -1; |
@@ -172,15 +184,15 @@ static int pmu_aliases(char *name, struct list_head *head) | |||
172 | return 0; | 184 | return 0; |
173 | } | 185 | } |
174 | 186 | ||
175 | static int pmu_alias_terms(struct perf_pmu__alias *alias, | 187 | static int pmu_alias_terms(struct perf_pmu_alias *alias, |
176 | struct list_head *terms) | 188 | struct list_head *terms) |
177 | { | 189 | { |
178 | struct parse_events__term *term, *clone; | 190 | struct parse_events_term *term, *clone; |
179 | LIST_HEAD(list); | 191 | LIST_HEAD(list); |
180 | int ret; | 192 | int ret; |
181 | 193 | ||
182 | list_for_each_entry(term, &alias->terms, list) { | 194 | list_for_each_entry(term, &alias->terms, list) { |
183 | ret = parse_events__term_clone(&clone, term); | 195 | ret = parse_events_term__clone(&clone, term); |
184 | if (ret) { | 196 | if (ret) { |
185 | parse_events__free_terms(&list); | 197 | parse_events__free_terms(&list); |
186 | return ret; | 198 | return ret; |
@@ -296,6 +308,9 @@ static struct perf_pmu *pmu_lookup(char *name) | |||
296 | if (pmu_format(name, &format)) | 308 | if (pmu_format(name, &format)) |
297 | return NULL; | 309 | return NULL; |
298 | 310 | ||
311 | if (pmu_aliases(name, &aliases)) | ||
312 | return NULL; | ||
313 | |||
299 | if (pmu_type(name, &type)) | 314 | if (pmu_type(name, &type)) |
300 | return NULL; | 315 | return NULL; |
301 | 316 | ||
@@ -305,8 +320,6 @@ static struct perf_pmu *pmu_lookup(char *name) | |||
305 | 320 | ||
306 | pmu->cpus = pmu_cpumask(name); | 321 | pmu->cpus = pmu_cpumask(name); |
307 | 322 | ||
308 | pmu_aliases(name, &aliases); | ||
309 | |||
310 | INIT_LIST_HEAD(&pmu->format); | 323 | INIT_LIST_HEAD(&pmu->format); |
311 | INIT_LIST_HEAD(&pmu->aliases); | 324 | INIT_LIST_HEAD(&pmu->aliases); |
312 | list_splice(&format, &pmu->format); | 325 | list_splice(&format, &pmu->format); |
@@ -359,10 +372,10 @@ struct perf_pmu *perf_pmu__find(char *name) | |||
359 | return pmu_lookup(name); | 372 | return pmu_lookup(name); |
360 | } | 373 | } |
361 | 374 | ||
362 | static struct perf_pmu__format* | 375 | static struct perf_pmu_format * |
363 | pmu_find_format(struct list_head *formats, char *name) | 376 | pmu_find_format(struct list_head *formats, char *name) |
364 | { | 377 | { |
365 | struct perf_pmu__format *format; | 378 | struct perf_pmu_format *format; |
366 | 379 | ||
367 | list_for_each_entry(format, formats, list) | 380 | list_for_each_entry(format, formats, list) |
368 | if (!strcmp(format->name, name)) | 381 | if (!strcmp(format->name, name)) |
@@ -402,9 +415,9 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value) | |||
402 | */ | 415 | */ |
403 | static int pmu_config_term(struct list_head *formats, | 416 | static int pmu_config_term(struct list_head *formats, |
404 | struct perf_event_attr *attr, | 417 | struct perf_event_attr *attr, |
405 | struct parse_events__term *term) | 418 | struct parse_events_term *term) |
406 | { | 419 | { |
407 | struct perf_pmu__format *format; | 420 | struct perf_pmu_format *format; |
408 | __u64 *vp; | 421 | __u64 *vp; |
409 | 422 | ||
410 | /* | 423 | /* |
@@ -445,10 +458,11 @@ static int pmu_config_term(struct list_head *formats, | |||
445 | return 0; | 458 | return 0; |
446 | } | 459 | } |
447 | 460 | ||
448 | static int pmu_config(struct list_head *formats, struct perf_event_attr *attr, | 461 | int perf_pmu__config_terms(struct list_head *formats, |
449 | struct list_head *head_terms) | 462 | struct perf_event_attr *attr, |
463 | struct list_head *head_terms) | ||
450 | { | 464 | { |
451 | struct parse_events__term *term; | 465 | struct parse_events_term *term; |
452 | 466 | ||
453 | list_for_each_entry(term, head_terms, list) | 467 | list_for_each_entry(term, head_terms, list) |
454 | if (pmu_config_term(formats, attr, term)) | 468 | if (pmu_config_term(formats, attr, term)) |
@@ -466,13 +480,13 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | |||
466 | struct list_head *head_terms) | 480 | struct list_head *head_terms) |
467 | { | 481 | { |
468 | attr->type = pmu->type; | 482 | attr->type = pmu->type; |
469 | return pmu_config(&pmu->format, attr, head_terms); | 483 | return perf_pmu__config_terms(&pmu->format, attr, head_terms); |
470 | } | 484 | } |
471 | 485 | ||
472 | static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, | 486 | static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, |
473 | struct parse_events__term *term) | 487 | struct parse_events_term *term) |
474 | { | 488 | { |
475 | struct perf_pmu__alias *alias; | 489 | struct perf_pmu_alias *alias; |
476 | char *name; | 490 | char *name; |
477 | 491 | ||
478 | if (parse_events__is_hardcoded_term(term)) | 492 | if (parse_events__is_hardcoded_term(term)) |
@@ -505,8 +519,8 @@ static struct perf_pmu__alias *pmu_find_alias(struct perf_pmu *pmu, | |||
505 | */ | 519 | */ |
506 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) | 520 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) |
507 | { | 521 | { |
508 | struct parse_events__term *term, *h; | 522 | struct parse_events_term *term, *h; |
509 | struct perf_pmu__alias *alias; | 523 | struct perf_pmu_alias *alias; |
510 | int ret; | 524 | int ret; |
511 | 525 | ||
512 | list_for_each_entry_safe(term, h, head_terms, list) { | 526 | list_for_each_entry_safe(term, h, head_terms, list) { |
@@ -525,7 +539,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) | |||
525 | int perf_pmu__new_format(struct list_head *list, char *name, | 539 | int perf_pmu__new_format(struct list_head *list, char *name, |
526 | int config, unsigned long *bits) | 540 | int config, unsigned long *bits) |
527 | { | 541 | { |
528 | struct perf_pmu__format *format; | 542 | struct perf_pmu_format *format; |
529 | 543 | ||
530 | format = zalloc(sizeof(*format)); | 544 | format = zalloc(sizeof(*format)); |
531 | if (!format) | 545 | if (!format) |
@@ -546,181 +560,7 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to) | |||
546 | if (!to) | 560 | if (!to) |
547 | to = from; | 561 | to = from; |
548 | 562 | ||
549 | memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS)); | 563 | memset(bits, 0, BITS_TO_BYTES(PERF_PMU_FORMAT_BITS)); |
550 | for (b = from; b <= to; b++) | 564 | for (b = from; b <= to; b++) |
551 | set_bit(b, bits); | 565 | set_bit(b, bits); |
552 | } | 566 | } |
553 | |||
554 | /* Simulated format definitions. */ | ||
555 | static struct test_format { | ||
556 | const char *name; | ||
557 | const char *value; | ||
558 | } test_formats[] = { | ||
559 | { "krava01", "config:0-1,62-63\n", }, | ||
560 | { "krava02", "config:10-17\n", }, | ||
561 | { "krava03", "config:5\n", }, | ||
562 | { "krava11", "config1:0,2,4,6,8,20-28\n", }, | ||
563 | { "krava12", "config1:63\n", }, | ||
564 | { "krava13", "config1:45-47\n", }, | ||
565 | { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", }, | ||
566 | { "krava22", "config2:8,18,48,58\n", }, | ||
567 | { "krava23", "config2:28-29,38\n", }, | ||
568 | }; | ||
569 | |||
570 | #define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format)) | ||
571 | |||
572 | /* Simulated users input. */ | ||
573 | static struct parse_events__term test_terms[] = { | ||
574 | { | ||
575 | .config = (char *) "krava01", | ||
576 | .val.num = 15, | ||
577 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
578 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
579 | }, | ||
580 | { | ||
581 | .config = (char *) "krava02", | ||
582 | .val.num = 170, | ||
583 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
584 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
585 | }, | ||
586 | { | ||
587 | .config = (char *) "krava03", | ||
588 | .val.num = 1, | ||
589 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
590 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
591 | }, | ||
592 | { | ||
593 | .config = (char *) "krava11", | ||
594 | .val.num = 27, | ||
595 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
596 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
597 | }, | ||
598 | { | ||
599 | .config = (char *) "krava12", | ||
600 | .val.num = 1, | ||
601 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
602 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
603 | }, | ||
604 | { | ||
605 | .config = (char *) "krava13", | ||
606 | .val.num = 2, | ||
607 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
608 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
609 | }, | ||
610 | { | ||
611 | .config = (char *) "krava21", | ||
612 | .val.num = 119, | ||
613 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
614 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
615 | }, | ||
616 | { | ||
617 | .config = (char *) "krava22", | ||
618 | .val.num = 11, | ||
619 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
620 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
621 | }, | ||
622 | { | ||
623 | .config = (char *) "krava23", | ||
624 | .val.num = 2, | ||
625 | .type_val = PARSE_EVENTS__TERM_TYPE_NUM, | ||
626 | .type_term = PARSE_EVENTS__TERM_TYPE_USER, | ||
627 | }, | ||
628 | }; | ||
629 | #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term)) | ||
630 | |||
631 | /* | ||
632 | * Prepare format directory data, exported by kernel | ||
633 | * at /sys/bus/event_source/devices/<dev>/format. | ||
634 | */ | ||
635 | static char *test_format_dir_get(void) | ||
636 | { | ||
637 | static char dir[PATH_MAX]; | ||
638 | unsigned int i; | ||
639 | |||
640 | snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX"); | ||
641 | if (!mkdtemp(dir)) | ||
642 | return NULL; | ||
643 | |||
644 | for (i = 0; i < TEST_FORMATS_CNT; i++) { | ||
645 | static char name[PATH_MAX]; | ||
646 | struct test_format *format = &test_formats[i]; | ||
647 | FILE *file; | ||
648 | |||
649 | snprintf(name, PATH_MAX, "%s/%s", dir, format->name); | ||
650 | |||
651 | file = fopen(name, "w"); | ||
652 | if (!file) | ||
653 | return NULL; | ||
654 | |||
655 | if (1 != fwrite(format->value, strlen(format->value), 1, file)) | ||
656 | break; | ||
657 | |||
658 | fclose(file); | ||
659 | } | ||
660 | |||
661 | return dir; | ||
662 | } | ||
663 | |||
664 | /* Cleanup format directory. */ | ||
665 | static int test_format_dir_put(char *dir) | ||
666 | { | ||
667 | char buf[PATH_MAX]; | ||
668 | snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir); | ||
669 | if (system(buf)) | ||
670 | return -1; | ||
671 | |||
672 | snprintf(buf, PATH_MAX, "rmdir %s\n", dir); | ||
673 | return system(buf); | ||
674 | } | ||
675 | |||
676 | static struct list_head *test_terms_list(void) | ||
677 | { | ||
678 | static LIST_HEAD(terms); | ||
679 | unsigned int i; | ||
680 | |||
681 | for (i = 0; i < TERMS_CNT; i++) | ||
682 | list_add_tail(&test_terms[i].list, &terms); | ||
683 | |||
684 | return &terms; | ||
685 | } | ||
686 | |||
687 | #undef TERMS_CNT | ||
688 | |||
689 | int perf_pmu__test(void) | ||
690 | { | ||
691 | char *format = test_format_dir_get(); | ||
692 | LIST_HEAD(formats); | ||
693 | struct list_head *terms = test_terms_list(); | ||
694 | int ret; | ||
695 | |||
696 | if (!format) | ||
697 | return -EINVAL; | ||
698 | |||
699 | do { | ||
700 | struct perf_event_attr attr; | ||
701 | |||
702 | memset(&attr, 0, sizeof(attr)); | ||
703 | |||
704 | ret = pmu_format_parse(format, &formats); | ||
705 | if (ret) | ||
706 | break; | ||
707 | |||
708 | ret = pmu_config(&formats, &attr, terms); | ||
709 | if (ret) | ||
710 | break; | ||
711 | |||
712 | ret = -EINVAL; | ||
713 | |||
714 | if (attr.config != 0xc00000000002a823) | ||
715 | break; | ||
716 | if (attr.config1 != 0x8000400000000145) | ||
717 | break; | ||
718 | if (attr.config2 != 0x0400000020041d07) | ||
719 | break; | ||
720 | |||
721 | ret = 0; | ||
722 | } while (0); | ||
723 | |||
724 | test_format_dir_put(format); | ||
725 | return ret; | ||
726 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 39f3abac7744..32fe55b659fa 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -2,7 +2,7 @@ | |||
2 | #define __PMU_H | 2 | #define __PMU_H |
3 | 3 | ||
4 | #include <linux/bitops.h> | 4 | #include <linux/bitops.h> |
5 | #include "../../../include/uapi/linux/perf_event.h" | 5 | #include <linux/perf_event.h> |
6 | 6 | ||
7 | enum { | 7 | enum { |
8 | PERF_PMU_FORMAT_VALUE_CONFIG, | 8 | PERF_PMU_FORMAT_VALUE_CONFIG, |
@@ -12,19 +12,6 @@ enum { | |||
12 | 12 | ||
13 | #define PERF_PMU_FORMAT_BITS 64 | 13 | #define PERF_PMU_FORMAT_BITS 64 |
14 | 14 | ||
15 | struct perf_pmu__format { | ||
16 | char *name; | ||
17 | int value; | ||
18 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | ||
19 | struct list_head list; | ||
20 | }; | ||
21 | |||
22 | struct perf_pmu__alias { | ||
23 | char *name; | ||
24 | struct list_head terms; | ||
25 | struct list_head list; | ||
26 | }; | ||
27 | |||
28 | struct perf_pmu { | 15 | struct perf_pmu { |
29 | char *name; | 16 | char *name; |
30 | __u32 type; | 17 | __u32 type; |
@@ -37,15 +24,19 @@ struct perf_pmu { | |||
37 | struct perf_pmu *perf_pmu__find(char *name); | 24 | struct perf_pmu *perf_pmu__find(char *name); |
38 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | 25 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
39 | struct list_head *head_terms); | 26 | struct list_head *head_terms); |
27 | int perf_pmu__config_terms(struct list_head *formats, | ||
28 | struct perf_event_attr *attr, | ||
29 | struct list_head *head_terms); | ||
40 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); | 30 | int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); |
41 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, | 31 | struct list_head *perf_pmu__alias(struct perf_pmu *pmu, |
42 | struct list_head *head_terms); | 32 | struct list_head *head_terms); |
43 | int perf_pmu_wrap(void); | 33 | int perf_pmu_wrap(void); |
44 | void perf_pmu_error(struct list_head *list, char *name, char const *msg); | 34 | void perf_pmu_error(struct list_head *list, char *name, char const *msg); |
45 | 35 | ||
46 | int perf_pmu__new_format(struct list_head *list, char *name, | 36 | int perf_pmu__new_format(struct list_head *list, char *name, |
47 | int config, unsigned long *bits); | 37 | int config, unsigned long *bits); |
48 | void perf_pmu__set_format(unsigned long *bits, long from, long to); | 38 | void perf_pmu__set_format(unsigned long *bits, long from, long to); |
39 | int perf_pmu__format_parse(char *dir, struct list_head *head); | ||
49 | 40 | ||
50 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | 41 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); |
51 | 42 | ||
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y index ec898047ebb9..bfd7e8509869 100644 --- a/tools/perf/util/pmu.y +++ b/tools/perf/util/pmu.y | |||
@@ -1,5 +1,4 @@ | |||
1 | 1 | ||
2 | %name-prefix "perf_pmu_" | ||
3 | %parse-param {struct list_head *format} | 2 | %parse-param {struct list_head *format} |
4 | %parse-param {char *name} | 3 | %parse-param {char *name} |
5 | 4 | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 1daf5c14e751..be0329394d56 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -413,12 +413,12 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
413 | dwarf_diename(vr_die), dwarf_diename(&type)); | 413 | dwarf_diename(vr_die), dwarf_diename(&type)); |
414 | return -EINVAL; | 414 | return -EINVAL; |
415 | } | 415 | } |
416 | if (die_get_real_type(&type, &type) == NULL) { | ||
417 | pr_warning("Failed to get a type" | ||
418 | " information.\n"); | ||
419 | return -ENOENT; | ||
420 | } | ||
416 | if (ret == DW_TAG_pointer_type) { | 421 | if (ret == DW_TAG_pointer_type) { |
417 | if (die_get_real_type(&type, &type) == NULL) { | ||
418 | pr_warning("Failed to get a type" | ||
419 | " information.\n"); | ||
420 | return -ENOENT; | ||
421 | } | ||
422 | while (*ref_ptr) | 422 | while (*ref_ptr) |
423 | ref_ptr = &(*ref_ptr)->next; | 423 | ref_ptr = &(*ref_ptr)->next; |
424 | /* Add new reference with offset +0 */ | 424 | /* Add new reference with offset +0 */ |
diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c index 13d36faf64eb..daa17aeb6c63 100644 --- a/tools/perf/util/pstack.c +++ b/tools/perf/util/pstack.c | |||
@@ -17,59 +17,59 @@ struct pstack { | |||
17 | 17 | ||
18 | struct pstack *pstack__new(unsigned short max_nr_entries) | 18 | struct pstack *pstack__new(unsigned short max_nr_entries) |
19 | { | 19 | { |
20 | struct pstack *self = zalloc((sizeof(*self) + | 20 | struct pstack *pstack = zalloc((sizeof(*pstack) + |
21 | max_nr_entries * sizeof(void *))); | 21 | max_nr_entries * sizeof(void *))); |
22 | if (self != NULL) | 22 | if (pstack != NULL) |
23 | self->max_nr_entries = max_nr_entries; | 23 | pstack->max_nr_entries = max_nr_entries; |
24 | return self; | 24 | return pstack; |
25 | } | 25 | } |
26 | 26 | ||
27 | void pstack__delete(struct pstack *self) | 27 | void pstack__delete(struct pstack *pstack) |
28 | { | 28 | { |
29 | free(self); | 29 | free(pstack); |
30 | } | 30 | } |
31 | 31 | ||
32 | bool pstack__empty(const struct pstack *self) | 32 | bool pstack__empty(const struct pstack *pstack) |
33 | { | 33 | { |
34 | return self->top == 0; | 34 | return pstack->top == 0; |
35 | } | 35 | } |
36 | 36 | ||
37 | void pstack__remove(struct pstack *self, void *key) | 37 | void pstack__remove(struct pstack *pstack, void *key) |
38 | { | 38 | { |
39 | unsigned short i = self->top, last_index = self->top - 1; | 39 | unsigned short i = pstack->top, last_index = pstack->top - 1; |
40 | 40 | ||
41 | while (i-- != 0) { | 41 | while (i-- != 0) { |
42 | if (self->entries[i] == key) { | 42 | if (pstack->entries[i] == key) { |
43 | if (i < last_index) | 43 | if (i < last_index) |
44 | memmove(self->entries + i, | 44 | memmove(pstack->entries + i, |
45 | self->entries + i + 1, | 45 | pstack->entries + i + 1, |
46 | (last_index - i) * sizeof(void *)); | 46 | (last_index - i) * sizeof(void *)); |
47 | --self->top; | 47 | --pstack->top; |
48 | return; | 48 | return; |
49 | } | 49 | } |
50 | } | 50 | } |
51 | pr_err("%s: %p not on the pstack!\n", __func__, key); | 51 | pr_err("%s: %p not on the pstack!\n", __func__, key); |
52 | } | 52 | } |
53 | 53 | ||
54 | void pstack__push(struct pstack *self, void *key) | 54 | void pstack__push(struct pstack *pstack, void *key) |
55 | { | 55 | { |
56 | if (self->top == self->max_nr_entries) { | 56 | if (pstack->top == pstack->max_nr_entries) { |
57 | pr_err("%s: top=%d, overflow!\n", __func__, self->top); | 57 | pr_err("%s: top=%d, overflow!\n", __func__, pstack->top); |
58 | return; | 58 | return; |
59 | } | 59 | } |
60 | self->entries[self->top++] = key; | 60 | pstack->entries[pstack->top++] = key; |
61 | } | 61 | } |
62 | 62 | ||
63 | void *pstack__pop(struct pstack *self) | 63 | void *pstack__pop(struct pstack *pstack) |
64 | { | 64 | { |
65 | void *ret; | 65 | void *ret; |
66 | 66 | ||
67 | if (self->top == 0) { | 67 | if (pstack->top == 0) { |
68 | pr_err("%s: underflow!\n", __func__); | 68 | pr_err("%s: underflow!\n", __func__); |
69 | return NULL; | 69 | return NULL; |
70 | } | 70 | } |
71 | 71 | ||
72 | ret = self->entries[--self->top]; | 72 | ret = pstack->entries[--pstack->top]; |
73 | self->entries[self->top] = NULL; | 73 | pstack->entries[pstack->top] = NULL; |
74 | return ret; | 74 | return ret; |
75 | } | 75 | } |
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index c40c2d33199e..64536a993f4a 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources | |||
@@ -18,4 +18,5 @@ util/cgroup.c | |||
18 | util/debugfs.c | 18 | util/debugfs.c |
19 | util/rblist.c | 19 | util/rblist.c |
20 | util/strlist.c | 20 | util/strlist.c |
21 | util/sysfs.c | ||
21 | ../../lib/rbtree.c | 22 | ../../lib/rbtree.c |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 9181bf212fb9..925e0c3e6d91 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -1015,6 +1015,8 @@ PyMODINIT_FUNC initperf(void) | |||
1015 | pyrf_cpu_map__setup_types() < 0) | 1015 | pyrf_cpu_map__setup_types() < 0) |
1016 | return; | 1016 | return; |
1017 | 1017 | ||
1018 | page_size = sysconf(_SC_PAGE_SIZE); | ||
1019 | |||
1018 | Py_INCREF(&pyrf_evlist__type); | 1020 | Py_INCREF(&pyrf_evlist__type); |
1019 | PyModule_AddObject(module, "evlist", (PyObject*)&pyrf_evlist__type); | 1021 | PyModule_AddObject(module, "evlist", (PyObject*)&pyrf_evlist__type); |
1020 | 1022 | ||
@@ -1043,3 +1045,12 @@ error: | |||
1043 | if (PyErr_Occurred()) | 1045 | if (PyErr_Occurred()) |
1044 | PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); | 1046 | PyErr_SetString(PyExc_ImportError, "perf: Init failed!"); |
1045 | } | 1047 | } |
1048 | |||
1049 | /* | ||
1050 | * Dummy, to avoid dragging all the test_attr infrastructure in the python | ||
1051 | * binding. | ||
1052 | */ | ||
1053 | void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu, | ||
1054 | int fd, int group_fd, unsigned long flags) | ||
1055 | { | ||
1056 | } | ||
diff --git a/tools/perf/util/rblist.c b/tools/perf/util/rblist.c index 0171fb611004..a16cdd2625ad 100644 --- a/tools/perf/util/rblist.c +++ b/tools/perf/util/rblist.c | |||
@@ -44,6 +44,7 @@ int rblist__add_node(struct rblist *rblist, const void *new_entry) | |||
44 | void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node) | 44 | void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node) |
45 | { | 45 | { |
46 | rb_erase(rb_node, &rblist->entries); | 46 | rb_erase(rb_node, &rblist->entries); |
47 | --rblist->nr_entries; | ||
47 | rblist->node_delete(rblist, rb_node); | 48 | rblist->node_delete(rblist, rb_node); |
48 | } | 49 | } |
49 | 50 | ||
@@ -87,8 +88,7 @@ void rblist__delete(struct rblist *rblist) | |||
87 | while (next) { | 88 | while (next) { |
88 | pos = next; | 89 | pos = next; |
89 | next = rb_next(pos); | 90 | next = rb_next(pos); |
90 | rb_erase(pos, &rblist->entries); | 91 | rblist__remove_node(rblist, pos); |
91 | rblist->node_delete(rblist, pos); | ||
92 | } | 92 | } |
93 | free(rblist); | 93 | free(rblist); |
94 | } | 94 | } |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index f80605eb1855..eacec859f299 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -292,6 +292,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, | |||
292 | ns = nsecs - s * NSECS_PER_SEC; | 292 | ns = nsecs - s * NSECS_PER_SEC; |
293 | 293 | ||
294 | scripting_context->event_data = data; | 294 | scripting_context->event_data = data; |
295 | scripting_context->pevent = evsel->tp_format->pevent; | ||
295 | 296 | ||
296 | ENTER; | 297 | ENTER; |
297 | SAVETMPS; | 298 | SAVETMPS; |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 730c6630cba5..e87aa5d9696b 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -32,7 +32,6 @@ | |||
32 | #include "../event.h" | 32 | #include "../event.h" |
33 | #include "../thread.h" | 33 | #include "../thread.h" |
34 | #include "../trace-event.h" | 34 | #include "../trace-event.h" |
35 | #include "../evsel.h" | ||
36 | 35 | ||
37 | PyMODINIT_FUNC initperf_trace_context(void); | 36 | PyMODINIT_FUNC initperf_trace_context(void); |
38 | 37 | ||
@@ -266,6 +265,7 @@ static void python_process_tracepoint(union perf_event *perf_event | |||
266 | ns = nsecs - s * NSECS_PER_SEC; | 265 | ns = nsecs - s * NSECS_PER_SEC; |
267 | 266 | ||
268 | scripting_context->event_data = data; | 267 | scripting_context->event_data = data; |
268 | scripting_context->pevent = evsel->tp_format->pevent; | ||
269 | 269 | ||
270 | context = PyCObject_FromVoidPtr(scripting_context, NULL); | 270 | context = PyCObject_FromVoidPtr(scripting_context, NULL); |
271 | 271 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 8cdd23239c90..bd85280bb6e8 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -16,7 +16,6 @@ | |||
16 | #include "cpumap.h" | 16 | #include "cpumap.h" |
17 | #include "event-parse.h" | 17 | #include "event-parse.h" |
18 | #include "perf_regs.h" | 18 | #include "perf_regs.h" |
19 | #include "unwind.h" | ||
20 | #include "vdso.h" | 19 | #include "vdso.h" |
21 | 20 | ||
22 | static int perf_session__open(struct perf_session *self, bool force) | 21 | static int perf_session__open(struct perf_session *self, bool force) |
@@ -87,13 +86,12 @@ void perf_session__set_id_hdr_size(struct perf_session *session) | |||
87 | { | 86 | { |
88 | u16 id_hdr_size = perf_evlist__id_hdr_size(session->evlist); | 87 | u16 id_hdr_size = perf_evlist__id_hdr_size(session->evlist); |
89 | 88 | ||
90 | session->host_machine.id_hdr_size = id_hdr_size; | ||
91 | machines__set_id_hdr_size(&session->machines, id_hdr_size); | 89 | machines__set_id_hdr_size(&session->machines, id_hdr_size); |
92 | } | 90 | } |
93 | 91 | ||
94 | int perf_session__create_kernel_maps(struct perf_session *self) | 92 | int perf_session__create_kernel_maps(struct perf_session *self) |
95 | { | 93 | { |
96 | int ret = machine__create_kernel_maps(&self->host_machine); | 94 | int ret = machine__create_kernel_maps(&self->machines.host); |
97 | 95 | ||
98 | if (ret >= 0) | 96 | if (ret >= 0) |
99 | ret = machines__create_guest_kernel_maps(&self->machines); | 97 | ret = machines__create_guest_kernel_maps(&self->machines); |
@@ -102,8 +100,7 @@ int perf_session__create_kernel_maps(struct perf_session *self) | |||
102 | 100 | ||
103 | static void perf_session__destroy_kernel_maps(struct perf_session *self) | 101 | static void perf_session__destroy_kernel_maps(struct perf_session *self) |
104 | { | 102 | { |
105 | machine__destroy_kernel_maps(&self->host_machine); | 103 | machines__destroy_kernel_maps(&self->machines); |
106 | machines__destroy_guest_kernel_maps(&self->machines); | ||
107 | } | 104 | } |
108 | 105 | ||
109 | struct perf_session *perf_session__new(const char *filename, int mode, | 106 | struct perf_session *perf_session__new(const char *filename, int mode, |
@@ -128,22 +125,11 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
128 | goto out; | 125 | goto out; |
129 | 126 | ||
130 | memcpy(self->filename, filename, len); | 127 | memcpy(self->filename, filename, len); |
131 | /* | ||
132 | * On 64bit we can mmap the data file in one go. No need for tiny mmap | ||
133 | * slices. On 32bit we use 32MB. | ||
134 | */ | ||
135 | #if BITS_PER_LONG == 64 | ||
136 | self->mmap_window = ULLONG_MAX; | ||
137 | #else | ||
138 | self->mmap_window = 32 * 1024 * 1024ULL; | ||
139 | #endif | ||
140 | self->machines = RB_ROOT; | ||
141 | self->repipe = repipe; | 128 | self->repipe = repipe; |
142 | INIT_LIST_HEAD(&self->ordered_samples.samples); | 129 | INIT_LIST_HEAD(&self->ordered_samples.samples); |
143 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); | 130 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); |
144 | INIT_LIST_HEAD(&self->ordered_samples.to_free); | 131 | INIT_LIST_HEAD(&self->ordered_samples.to_free); |
145 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); | 132 | machines__init(&self->machines); |
146 | hists__init(&self->hists); | ||
147 | 133 | ||
148 | if (mode == O_RDONLY) { | 134 | if (mode == O_RDONLY) { |
149 | if (perf_session__open(self, force) < 0) | 135 | if (perf_session__open(self, force) < 0) |
@@ -171,37 +157,30 @@ out_delete: | |||
171 | return NULL; | 157 | return NULL; |
172 | } | 158 | } |
173 | 159 | ||
174 | static void machine__delete_dead_threads(struct machine *machine) | ||
175 | { | ||
176 | struct thread *n, *t; | ||
177 | |||
178 | list_for_each_entry_safe(t, n, &machine->dead_threads, node) { | ||
179 | list_del(&t->node); | ||
180 | thread__delete(t); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | static void perf_session__delete_dead_threads(struct perf_session *session) | 160 | static void perf_session__delete_dead_threads(struct perf_session *session) |
185 | { | 161 | { |
186 | machine__delete_dead_threads(&session->host_machine); | 162 | machine__delete_dead_threads(&session->machines.host); |
187 | } | 163 | } |
188 | 164 | ||
189 | static void machine__delete_threads(struct machine *self) | 165 | static void perf_session__delete_threads(struct perf_session *session) |
190 | { | 166 | { |
191 | struct rb_node *nd = rb_first(&self->threads); | 167 | machine__delete_threads(&session->machines.host); |
192 | |||
193 | while (nd) { | ||
194 | struct thread *t = rb_entry(nd, struct thread, rb_node); | ||
195 | |||
196 | rb_erase(&t->rb_node, &self->threads); | ||
197 | nd = rb_next(nd); | ||
198 | thread__delete(t); | ||
199 | } | ||
200 | } | 168 | } |
201 | 169 | ||
202 | static void perf_session__delete_threads(struct perf_session *session) | 170 | static void perf_session_env__delete(struct perf_session_env *env) |
203 | { | 171 | { |
204 | machine__delete_threads(&session->host_machine); | 172 | free(env->hostname); |
173 | free(env->os_release); | ||
174 | free(env->version); | ||
175 | free(env->arch); | ||
176 | free(env->cpu_desc); | ||
177 | free(env->cpuid); | ||
178 | |||
179 | free(env->cmdline); | ||
180 | free(env->sibling_cores); | ||
181 | free(env->sibling_threads); | ||
182 | free(env->numa_nodes); | ||
183 | free(env->pmu_mappings); | ||
205 | } | 184 | } |
206 | 185 | ||
207 | void perf_session__delete(struct perf_session *self) | 186 | void perf_session__delete(struct perf_session *self) |
@@ -209,198 +188,13 @@ void perf_session__delete(struct perf_session *self) | |||
209 | perf_session__destroy_kernel_maps(self); | 188 | perf_session__destroy_kernel_maps(self); |
210 | perf_session__delete_dead_threads(self); | 189 | perf_session__delete_dead_threads(self); |
211 | perf_session__delete_threads(self); | 190 | perf_session__delete_threads(self); |
212 | machine__exit(&self->host_machine); | 191 | perf_session_env__delete(&self->header.env); |
192 | machines__exit(&self->machines); | ||
213 | close(self->fd); | 193 | close(self->fd); |
214 | free(self); | 194 | free(self); |
215 | vdso__exit(); | 195 | vdso__exit(); |
216 | } | 196 | } |
217 | 197 | ||
218 | void machine__remove_thread(struct machine *self, struct thread *th) | ||
219 | { | ||
220 | self->last_match = NULL; | ||
221 | rb_erase(&th->rb_node, &self->threads); | ||
222 | /* | ||
223 | * We may have references to this thread, for instance in some hist_entry | ||
224 | * instances, so just move them to a separate list. | ||
225 | */ | ||
226 | list_add_tail(&th->node, &self->dead_threads); | ||
227 | } | ||
228 | |||
229 | static bool symbol__match_parent_regex(struct symbol *sym) | ||
230 | { | ||
231 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | ||
232 | return 1; | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static const u8 cpumodes[] = { | ||
238 | PERF_RECORD_MISC_USER, | ||
239 | PERF_RECORD_MISC_KERNEL, | ||
240 | PERF_RECORD_MISC_GUEST_USER, | ||
241 | PERF_RECORD_MISC_GUEST_KERNEL | ||
242 | }; | ||
243 | #define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) | ||
244 | |||
245 | static void ip__resolve_ams(struct machine *self, struct thread *thread, | ||
246 | struct addr_map_symbol *ams, | ||
247 | u64 ip) | ||
248 | { | ||
249 | struct addr_location al; | ||
250 | size_t i; | ||
251 | u8 m; | ||
252 | |||
253 | memset(&al, 0, sizeof(al)); | ||
254 | |||
255 | for (i = 0; i < NCPUMODES; i++) { | ||
256 | m = cpumodes[i]; | ||
257 | /* | ||
258 | * We cannot use the header.misc hint to determine whether a | ||
259 | * branch stack address is user, kernel, guest, hypervisor. | ||
260 | * Branches may straddle the kernel/user/hypervisor boundaries. | ||
261 | * Thus, we have to try consecutively until we find a match | ||
262 | * or else, the symbol is unknown | ||
263 | */ | ||
264 | thread__find_addr_location(thread, self, m, MAP__FUNCTION, | ||
265 | ip, &al, NULL); | ||
266 | if (al.sym) | ||
267 | goto found; | ||
268 | } | ||
269 | found: | ||
270 | ams->addr = ip; | ||
271 | ams->al_addr = al.addr; | ||
272 | ams->sym = al.sym; | ||
273 | ams->map = al.map; | ||
274 | } | ||
275 | |||
276 | struct branch_info *machine__resolve_bstack(struct machine *self, | ||
277 | struct thread *thr, | ||
278 | struct branch_stack *bs) | ||
279 | { | ||
280 | struct branch_info *bi; | ||
281 | unsigned int i; | ||
282 | |||
283 | bi = calloc(bs->nr, sizeof(struct branch_info)); | ||
284 | if (!bi) | ||
285 | return NULL; | ||
286 | |||
287 | for (i = 0; i < bs->nr; i++) { | ||
288 | ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to); | ||
289 | ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from); | ||
290 | bi[i].flags = bs->entries[i].flags; | ||
291 | } | ||
292 | return bi; | ||
293 | } | ||
294 | |||
295 | static int machine__resolve_callchain_sample(struct machine *machine, | ||
296 | struct thread *thread, | ||
297 | struct ip_callchain *chain, | ||
298 | struct symbol **parent) | ||
299 | |||
300 | { | ||
301 | u8 cpumode = PERF_RECORD_MISC_USER; | ||
302 | unsigned int i; | ||
303 | int err; | ||
304 | |||
305 | callchain_cursor_reset(&callchain_cursor); | ||
306 | |||
307 | if (chain->nr > PERF_MAX_STACK_DEPTH) { | ||
308 | pr_warning("corrupted callchain. skipping...\n"); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | for (i = 0; i < chain->nr; i++) { | ||
313 | u64 ip; | ||
314 | struct addr_location al; | ||
315 | |||
316 | if (callchain_param.order == ORDER_CALLEE) | ||
317 | ip = chain->ips[i]; | ||
318 | else | ||
319 | ip = chain->ips[chain->nr - i - 1]; | ||
320 | |||
321 | if (ip >= PERF_CONTEXT_MAX) { | ||
322 | switch (ip) { | ||
323 | case PERF_CONTEXT_HV: | ||
324 | cpumode = PERF_RECORD_MISC_HYPERVISOR; | ||
325 | break; | ||
326 | case PERF_CONTEXT_KERNEL: | ||
327 | cpumode = PERF_RECORD_MISC_KERNEL; | ||
328 | break; | ||
329 | case PERF_CONTEXT_USER: | ||
330 | cpumode = PERF_RECORD_MISC_USER; | ||
331 | break; | ||
332 | default: | ||
333 | pr_debug("invalid callchain context: " | ||
334 | "%"PRId64"\n", (s64) ip); | ||
335 | /* | ||
336 | * It seems the callchain is corrupted. | ||
337 | * Discard all. | ||
338 | */ | ||
339 | callchain_cursor_reset(&callchain_cursor); | ||
340 | return 0; | ||
341 | } | ||
342 | continue; | ||
343 | } | ||
344 | |||
345 | al.filtered = false; | ||
346 | thread__find_addr_location(thread, machine, cpumode, | ||
347 | MAP__FUNCTION, ip, &al, NULL); | ||
348 | if (al.sym != NULL) { | ||
349 | if (sort__has_parent && !*parent && | ||
350 | symbol__match_parent_regex(al.sym)) | ||
351 | *parent = al.sym; | ||
352 | if (!symbol_conf.use_callchain) | ||
353 | break; | ||
354 | } | ||
355 | |||
356 | err = callchain_cursor_append(&callchain_cursor, | ||
357 | ip, al.map, al.sym); | ||
358 | if (err) | ||
359 | return err; | ||
360 | } | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int unwind_entry(struct unwind_entry *entry, void *arg) | ||
366 | { | ||
367 | struct callchain_cursor *cursor = arg; | ||
368 | return callchain_cursor_append(cursor, entry->ip, | ||
369 | entry->map, entry->sym); | ||
370 | } | ||
371 | |||
372 | int machine__resolve_callchain(struct machine *machine, | ||
373 | struct perf_evsel *evsel, | ||
374 | struct thread *thread, | ||
375 | struct perf_sample *sample, | ||
376 | struct symbol **parent) | ||
377 | |||
378 | { | ||
379 | int ret; | ||
380 | |||
381 | callchain_cursor_reset(&callchain_cursor); | ||
382 | |||
383 | ret = machine__resolve_callchain_sample(machine, thread, | ||
384 | sample->callchain, parent); | ||
385 | if (ret) | ||
386 | return ret; | ||
387 | |||
388 | /* Can we do dwarf post unwind? */ | ||
389 | if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) && | ||
390 | (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER))) | ||
391 | return 0; | ||
392 | |||
393 | /* Bail out if nothing was captured. */ | ||
394 | if ((!sample->user_regs.regs) || | ||
395 | (!sample->user_stack.size)) | ||
396 | return 0; | ||
397 | |||
398 | return unwind__get_entries(unwind_entry, &callchain_cursor, machine, | ||
399 | thread, evsel->attr.sample_regs_user, | ||
400 | sample); | ||
401 | |||
402 | } | ||
403 | |||
404 | static int process_event_synth_tracing_data_stub(union perf_event *event | 198 | static int process_event_synth_tracing_data_stub(union perf_event *event |
405 | __maybe_unused, | 199 | __maybe_unused, |
406 | struct perf_session *session | 200 | struct perf_session *session |
@@ -1027,7 +821,7 @@ static struct machine * | |||
1027 | return perf_session__findnew_machine(session, pid); | 821 | return perf_session__findnew_machine(session, pid); |
1028 | } | 822 | } |
1029 | 823 | ||
1030 | return perf_session__find_host_machine(session); | 824 | return &session->machines.host; |
1031 | } | 825 | } |
1032 | 826 | ||
1033 | static int perf_session_deliver_event(struct perf_session *session, | 827 | static int perf_session_deliver_event(struct perf_session *session, |
@@ -1065,11 +859,11 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
1065 | case PERF_RECORD_SAMPLE: | 859 | case PERF_RECORD_SAMPLE: |
1066 | dump_sample(evsel, event, sample); | 860 | dump_sample(evsel, event, sample); |
1067 | if (evsel == NULL) { | 861 | if (evsel == NULL) { |
1068 | ++session->hists.stats.nr_unknown_id; | 862 | ++session->stats.nr_unknown_id; |
1069 | return 0; | 863 | return 0; |
1070 | } | 864 | } |
1071 | if (machine == NULL) { | 865 | if (machine == NULL) { |
1072 | ++session->hists.stats.nr_unprocessable_samples; | 866 | ++session->stats.nr_unprocessable_samples; |
1073 | return 0; | 867 | return 0; |
1074 | } | 868 | } |
1075 | return tool->sample(tool, event, sample, evsel, machine); | 869 | return tool->sample(tool, event, sample, evsel, machine); |
@@ -1083,7 +877,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
1083 | return tool->exit(tool, event, sample, machine); | 877 | return tool->exit(tool, event, sample, machine); |
1084 | case PERF_RECORD_LOST: | 878 | case PERF_RECORD_LOST: |
1085 | if (tool->lost == perf_event__process_lost) | 879 | if (tool->lost == perf_event__process_lost) |
1086 | session->hists.stats.total_lost += event->lost.lost; | 880 | session->stats.total_lost += event->lost.lost; |
1087 | return tool->lost(tool, event, sample, machine); | 881 | return tool->lost(tool, event, sample, machine); |
1088 | case PERF_RECORD_READ: | 882 | case PERF_RECORD_READ: |
1089 | return tool->read(tool, event, sample, evsel, machine); | 883 | return tool->read(tool, event, sample, evsel, machine); |
@@ -1092,7 +886,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
1092 | case PERF_RECORD_UNTHROTTLE: | 886 | case PERF_RECORD_UNTHROTTLE: |
1093 | return tool->unthrottle(tool, event, sample, machine); | 887 | return tool->unthrottle(tool, event, sample, machine); |
1094 | default: | 888 | default: |
1095 | ++session->hists.stats.nr_unknown_events; | 889 | ++session->stats.nr_unknown_events; |
1096 | return -1; | 890 | return -1; |
1097 | } | 891 | } |
1098 | } | 892 | } |
@@ -1106,8 +900,8 @@ static int perf_session__preprocess_sample(struct perf_session *session, | |||
1106 | 900 | ||
1107 | if (!ip_callchain__valid(sample->callchain, event)) { | 901 | if (!ip_callchain__valid(sample->callchain, event)) { |
1108 | pr_debug("call-chain problem with event, skipping it.\n"); | 902 | pr_debug("call-chain problem with event, skipping it.\n"); |
1109 | ++session->hists.stats.nr_invalid_chains; | 903 | ++session->stats.nr_invalid_chains; |
1110 | session->hists.stats.total_invalid_chains += sample->period; | 904 | session->stats.total_invalid_chains += sample->period; |
1111 | return -EINVAL; | 905 | return -EINVAL; |
1112 | } | 906 | } |
1113 | return 0; | 907 | return 0; |
@@ -1165,7 +959,7 @@ static int perf_session__process_event(struct perf_session *session, | |||
1165 | if (event->header.type >= PERF_RECORD_HEADER_MAX) | 959 | if (event->header.type >= PERF_RECORD_HEADER_MAX) |
1166 | return -EINVAL; | 960 | return -EINVAL; |
1167 | 961 | ||
1168 | hists__inc_nr_events(&session->hists, event->header.type); | 962 | events_stats__inc(&session->stats, event->header.type); |
1169 | 963 | ||
1170 | if (event->header.type >= PERF_RECORD_USER_TYPE_START) | 964 | if (event->header.type >= PERF_RECORD_USER_TYPE_START) |
1171 | return perf_session__process_user_event(session, event, tool, file_offset); | 965 | return perf_session__process_user_event(session, event, tool, file_offset); |
@@ -1201,7 +995,7 @@ void perf_event_header__bswap(struct perf_event_header *self) | |||
1201 | 995 | ||
1202 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) | 996 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) |
1203 | { | 997 | { |
1204 | return machine__findnew_thread(&session->host_machine, pid); | 998 | return machine__findnew_thread(&session->machines.host, pid); |
1205 | } | 999 | } |
1206 | 1000 | ||
1207 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | 1001 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) |
@@ -1220,39 +1014,39 @@ static void perf_session__warn_about_errors(const struct perf_session *session, | |||
1220 | const struct perf_tool *tool) | 1014 | const struct perf_tool *tool) |
1221 | { | 1015 | { |
1222 | if (tool->lost == perf_event__process_lost && | 1016 | if (tool->lost == perf_event__process_lost && |
1223 | session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) { | 1017 | session->stats.nr_events[PERF_RECORD_LOST] != 0) { |
1224 | ui__warning("Processed %d events and lost %d chunks!\n\n" | 1018 | ui__warning("Processed %d events and lost %d chunks!\n\n" |
1225 | "Check IO/CPU overload!\n\n", | 1019 | "Check IO/CPU overload!\n\n", |
1226 | session->hists.stats.nr_events[0], | 1020 | session->stats.nr_events[0], |
1227 | session->hists.stats.nr_events[PERF_RECORD_LOST]); | 1021 | session->stats.nr_events[PERF_RECORD_LOST]); |
1228 | } | 1022 | } |
1229 | 1023 | ||
1230 | if (session->hists.stats.nr_unknown_events != 0) { | 1024 | if (session->stats.nr_unknown_events != 0) { |
1231 | ui__warning("Found %u unknown events!\n\n" | 1025 | ui__warning("Found %u unknown events!\n\n" |
1232 | "Is this an older tool processing a perf.data " | 1026 | "Is this an older tool processing a perf.data " |
1233 | "file generated by a more recent tool?\n\n" | 1027 | "file generated by a more recent tool?\n\n" |
1234 | "If that is not the case, consider " | 1028 | "If that is not the case, consider " |
1235 | "reporting to linux-kernel@vger.kernel.org.\n\n", | 1029 | "reporting to linux-kernel@vger.kernel.org.\n\n", |
1236 | session->hists.stats.nr_unknown_events); | 1030 | session->stats.nr_unknown_events); |
1237 | } | 1031 | } |
1238 | 1032 | ||
1239 | if (session->hists.stats.nr_unknown_id != 0) { | 1033 | if (session->stats.nr_unknown_id != 0) { |
1240 | ui__warning("%u samples with id not present in the header\n", | 1034 | ui__warning("%u samples with id not present in the header\n", |
1241 | session->hists.stats.nr_unknown_id); | 1035 | session->stats.nr_unknown_id); |
1242 | } | 1036 | } |
1243 | 1037 | ||
1244 | if (session->hists.stats.nr_invalid_chains != 0) { | 1038 | if (session->stats.nr_invalid_chains != 0) { |
1245 | ui__warning("Found invalid callchains!\n\n" | 1039 | ui__warning("Found invalid callchains!\n\n" |
1246 | "%u out of %u events were discarded for this reason.\n\n" | 1040 | "%u out of %u events were discarded for this reason.\n\n" |
1247 | "Consider reporting to linux-kernel@vger.kernel.org.\n\n", | 1041 | "Consider reporting to linux-kernel@vger.kernel.org.\n\n", |
1248 | session->hists.stats.nr_invalid_chains, | 1042 | session->stats.nr_invalid_chains, |
1249 | session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); | 1043 | session->stats.nr_events[PERF_RECORD_SAMPLE]); |
1250 | } | 1044 | } |
1251 | 1045 | ||
1252 | if (session->hists.stats.nr_unprocessable_samples != 0) { | 1046 | if (session->stats.nr_unprocessable_samples != 0) { |
1253 | ui__warning("%u unprocessable samples recorded.\n" | 1047 | ui__warning("%u unprocessable samples recorded.\n" |
1254 | "Do you have a KVM guest running and not using 'perf kvm'?\n", | 1048 | "Do you have a KVM guest running and not using 'perf kvm'?\n", |
1255 | session->hists.stats.nr_unprocessable_samples); | 1049 | session->stats.nr_unprocessable_samples); |
1256 | } | 1050 | } |
1257 | } | 1051 | } |
1258 | 1052 | ||
@@ -1369,21 +1163,31 @@ fetch_mmaped_event(struct perf_session *session, | |||
1369 | return event; | 1163 | return event; |
1370 | } | 1164 | } |
1371 | 1165 | ||
1166 | /* | ||
1167 | * On 64bit we can mmap the data file in one go. No need for tiny mmap | ||
1168 | * slices. On 32bit we use 32MB. | ||
1169 | */ | ||
1170 | #if BITS_PER_LONG == 64 | ||
1171 | #define MMAP_SIZE ULLONG_MAX | ||
1172 | #define NUM_MMAPS 1 | ||
1173 | #else | ||
1174 | #define MMAP_SIZE (32 * 1024 * 1024ULL) | ||
1175 | #define NUM_MMAPS 128 | ||
1176 | #endif | ||
1177 | |||
1372 | int __perf_session__process_events(struct perf_session *session, | 1178 | int __perf_session__process_events(struct perf_session *session, |
1373 | u64 data_offset, u64 data_size, | 1179 | u64 data_offset, u64 data_size, |
1374 | u64 file_size, struct perf_tool *tool) | 1180 | u64 file_size, struct perf_tool *tool) |
1375 | { | 1181 | { |
1376 | u64 head, page_offset, file_offset, file_pos, progress_next; | 1182 | u64 head, page_offset, file_offset, file_pos, progress_next; |
1377 | int err, mmap_prot, mmap_flags, map_idx = 0; | 1183 | int err, mmap_prot, mmap_flags, map_idx = 0; |
1378 | size_t page_size, mmap_size; | 1184 | size_t mmap_size; |
1379 | char *buf, *mmaps[8]; | 1185 | char *buf, *mmaps[NUM_MMAPS]; |
1380 | union perf_event *event; | 1186 | union perf_event *event; |
1381 | uint32_t size; | 1187 | uint32_t size; |
1382 | 1188 | ||
1383 | perf_tool__fill_defaults(tool); | 1189 | perf_tool__fill_defaults(tool); |
1384 | 1190 | ||
1385 | page_size = sysconf(_SC_PAGESIZE); | ||
1386 | |||
1387 | page_offset = page_size * (data_offset / page_size); | 1191 | page_offset = page_size * (data_offset / page_size); |
1388 | file_offset = page_offset; | 1192 | file_offset = page_offset; |
1389 | head = data_offset - page_offset; | 1193 | head = data_offset - page_offset; |
@@ -1393,7 +1197,7 @@ int __perf_session__process_events(struct perf_session *session, | |||
1393 | 1197 | ||
1394 | progress_next = file_size / 16; | 1198 | progress_next = file_size / 16; |
1395 | 1199 | ||
1396 | mmap_size = session->mmap_window; | 1200 | mmap_size = MMAP_SIZE; |
1397 | if (mmap_size > file_size) | 1201 | if (mmap_size > file_size) |
1398 | mmap_size = file_size; | 1202 | mmap_size = file_size; |
1399 | 1203 | ||
@@ -1460,6 +1264,7 @@ more: | |||
1460 | session->ordered_samples.next_flush = ULLONG_MAX; | 1264 | session->ordered_samples.next_flush = ULLONG_MAX; |
1461 | err = flush_sample_queue(session, tool); | 1265 | err = flush_sample_queue(session, tool); |
1462 | out_err: | 1266 | out_err: |
1267 | ui_progress__finish(); | ||
1463 | perf_session__warn_about_errors(session, tool); | 1268 | perf_session__warn_about_errors(session, tool); |
1464 | perf_session_free_sample_buffers(session); | 1269 | perf_session_free_sample_buffers(session); |
1465 | return err; | 1270 | return err; |
@@ -1527,16 +1332,13 @@ int maps__set_kallsyms_ref_reloc_sym(struct map **maps, | |||
1527 | 1332 | ||
1528 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) | 1333 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp) |
1529 | { | 1334 | { |
1530 | return __dsos__fprintf(&self->host_machine.kernel_dsos, fp) + | 1335 | return machines__fprintf_dsos(&self->machines, fp); |
1531 | __dsos__fprintf(&self->host_machine.user_dsos, fp) + | ||
1532 | machines__fprintf_dsos(&self->machines, fp); | ||
1533 | } | 1336 | } |
1534 | 1337 | ||
1535 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, | 1338 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, FILE *fp, |
1536 | bool with_hits) | 1339 | bool (skip)(struct dso *dso, int parm), int parm) |
1537 | { | 1340 | { |
1538 | size_t ret = machine__fprintf_dsos_buildid(&self->host_machine, fp, with_hits); | 1341 | return machines__fprintf_dsos_buildid(&self->machines, fp, skip, parm); |
1539 | return ret + machines__fprintf_dsos_buildid(&self->machines, fp, with_hits); | ||
1540 | } | 1342 | } |
1541 | 1343 | ||
1542 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | 1344 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) |
@@ -1544,11 +1346,11 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) | |||
1544 | struct perf_evsel *pos; | 1346 | struct perf_evsel *pos; |
1545 | size_t ret = fprintf(fp, "Aggregated stats:\n"); | 1347 | size_t ret = fprintf(fp, "Aggregated stats:\n"); |
1546 | 1348 | ||
1547 | ret += hists__fprintf_nr_events(&session->hists, fp); | 1349 | ret += events_stats__fprintf(&session->stats, fp); |
1548 | 1350 | ||
1549 | list_for_each_entry(pos, &session->evlist->entries, node) { | 1351 | list_for_each_entry(pos, &session->evlist->entries, node) { |
1550 | ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); | 1352 | ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); |
1551 | ret += hists__fprintf_nr_events(&pos->hists, fp); | 1353 | ret += events_stats__fprintf(&pos->hists.stats, fp); |
1552 | } | 1354 | } |
1553 | 1355 | ||
1554 | return ret; | 1356 | return ret; |
@@ -1560,7 +1362,7 @@ size_t perf_session__fprintf(struct perf_session *session, FILE *fp) | |||
1560 | * FIXME: Here we have to actually print all the machines in this | 1362 | * FIXME: Here we have to actually print all the machines in this |
1561 | * session, not just the host... | 1363 | * session, not just the host... |
1562 | */ | 1364 | */ |
1563 | return machine__fprintf(&session->host_machine, fp); | 1365 | return machine__fprintf(&session->machines.host, fp); |
1564 | } | 1366 | } |
1565 | 1367 | ||
1566 | void perf_session__remove_thread(struct perf_session *session, | 1368 | void perf_session__remove_thread(struct perf_session *session, |
@@ -1569,10 +1371,10 @@ void perf_session__remove_thread(struct perf_session *session, | |||
1569 | /* | 1371 | /* |
1570 | * FIXME: This one makes no sense, we need to remove the thread from | 1372 | * FIXME: This one makes no sense, we need to remove the thread from |
1571 | * the machine it belongs to, perf_session can have many machines, so | 1373 | * the machine it belongs to, perf_session can have many machines, so |
1572 | * doing it always on ->host_machine is wrong. Fix when auditing all | 1374 | * doing it always on ->machines.host is wrong. Fix when auditing all |
1573 | * the 'perf kvm' code. | 1375 | * the 'perf kvm' code. |
1574 | */ | 1376 | */ |
1575 | machine__remove_thread(&session->host_machine, th); | 1377 | machine__remove_thread(&session->machines.host, th); |
1576 | } | 1378 | } |
1577 | 1379 | ||
1578 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | 1380 | struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index dd6426163ba6..b5c0847edfa9 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -4,10 +4,11 @@ | |||
4 | #include "hist.h" | 4 | #include "hist.h" |
5 | #include "event.h" | 5 | #include "event.h" |
6 | #include "header.h" | 6 | #include "header.h" |
7 | #include "machine.h" | ||
7 | #include "symbol.h" | 8 | #include "symbol.h" |
8 | #include "thread.h" | 9 | #include "thread.h" |
9 | #include <linux/rbtree.h> | 10 | #include <linux/rbtree.h> |
10 | #include "../../../include/uapi/linux/perf_event.h" | 11 | #include <linux/perf_event.h> |
11 | 12 | ||
12 | struct sample_queue; | 13 | struct sample_queue; |
13 | struct ip_callchain; | 14 | struct ip_callchain; |
@@ -29,16 +30,10 @@ struct ordered_samples { | |||
29 | struct perf_session { | 30 | struct perf_session { |
30 | struct perf_header header; | 31 | struct perf_header header; |
31 | unsigned long size; | 32 | unsigned long size; |
32 | unsigned long mmap_window; | 33 | struct machines machines; |
33 | struct machine host_machine; | ||
34 | struct rb_root machines; | ||
35 | struct perf_evlist *evlist; | 34 | struct perf_evlist *evlist; |
36 | struct pevent *pevent; | 35 | struct pevent *pevent; |
37 | /* | 36 | struct events_stats stats; |
38 | * FIXME: Need to split this up further, we need global | ||
39 | * stats + per event stats. | ||
40 | */ | ||
41 | struct hists hists; | ||
42 | int fd; | 37 | int fd; |
43 | bool fd_pipe; | 38 | bool fd_pipe; |
44 | bool repipe; | 39 | bool repipe; |
@@ -53,7 +48,7 @@ struct perf_tool; | |||
53 | struct perf_session *perf_session__new(const char *filename, int mode, | 48 | struct perf_session *perf_session__new(const char *filename, int mode, |
54 | bool force, bool repipe, | 49 | bool force, bool repipe, |
55 | struct perf_tool *tool); | 50 | struct perf_tool *tool); |
56 | void perf_session__delete(struct perf_session *self); | 51 | void perf_session__delete(struct perf_session *session); |
57 | 52 | ||
58 | void perf_event_header__bswap(struct perf_event_header *self); | 53 | void perf_event_header__bswap(struct perf_event_header *self); |
59 | 54 | ||
@@ -68,10 +63,6 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel | |||
68 | struct ip_callchain *chain, | 63 | struct ip_callchain *chain, |
69 | struct symbol **parent); | 64 | struct symbol **parent); |
70 | 65 | ||
71 | struct branch_info *machine__resolve_bstack(struct machine *self, | ||
72 | struct thread *thread, | ||
73 | struct branch_stack *bs); | ||
74 | |||
75 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 66 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
76 | 67 | ||
77 | void mem_bswap_64(void *src, int byte_size); | 68 | void mem_bswap_64(void *src, int byte_size); |
@@ -84,43 +75,24 @@ void perf_session__set_id_hdr_size(struct perf_session *session); | |||
84 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); | 75 | void perf_session__remove_thread(struct perf_session *self, struct thread *th); |
85 | 76 | ||
86 | static inline | 77 | static inline |
87 | struct machine *perf_session__find_host_machine(struct perf_session *self) | ||
88 | { | ||
89 | return &self->host_machine; | ||
90 | } | ||
91 | |||
92 | static inline | ||
93 | struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) | 78 | struct machine *perf_session__find_machine(struct perf_session *self, pid_t pid) |
94 | { | 79 | { |
95 | if (pid == HOST_KERNEL_ID) | ||
96 | return &self->host_machine; | ||
97 | return machines__find(&self->machines, pid); | 80 | return machines__find(&self->machines, pid); |
98 | } | 81 | } |
99 | 82 | ||
100 | static inline | 83 | static inline |
101 | struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) | 84 | struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t pid) |
102 | { | 85 | { |
103 | if (pid == HOST_KERNEL_ID) | ||
104 | return &self->host_machine; | ||
105 | return machines__findnew(&self->machines, pid); | 86 | return machines__findnew(&self->machines, pid); |
106 | } | 87 | } |
107 | 88 | ||
108 | static inline | ||
109 | void perf_session__process_machines(struct perf_session *self, | ||
110 | struct perf_tool *tool, | ||
111 | machine__process_t process) | ||
112 | { | ||
113 | process(&self->host_machine, tool); | ||
114 | return machines__process(&self->machines, process, tool); | ||
115 | } | ||
116 | |||
117 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); | 89 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
118 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); | 90 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); |
119 | 91 | ||
120 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); | 92 | size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp); |
121 | 93 | ||
122 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *self, | 94 | size_t perf_session__fprintf_dsos_buildid(struct perf_session *session, FILE *fp, |
123 | FILE *fp, bool with_hits); | 95 | bool (fn)(struct dso *dso, int parm), int parm); |
124 | 96 | ||
125 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); | 97 | size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); |
126 | 98 | ||
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index cfd1c0feb32d..d41926cb9e3f 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -60,7 +60,7 @@ sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | |||
60 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | 60 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, |
61 | size_t size, unsigned int width) | 61 | size_t size, unsigned int width) |
62 | { | 62 | { |
63 | return repsep_snprintf(bf, size, "%*s:%5d", width, | 63 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, |
64 | self->thread->comm ?: "", self->thread->pid); | 64 | self->thread->comm ?: "", self->thread->pid); |
65 | } | 65 | } |
66 | 66 | ||
@@ -97,6 +97,16 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | |||
97 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); | 97 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); |
98 | } | 98 | } |
99 | 99 | ||
100 | struct sort_entry sort_comm = { | ||
101 | .se_header = "Command", | ||
102 | .se_cmp = sort__comm_cmp, | ||
103 | .se_collapse = sort__comm_collapse, | ||
104 | .se_snprintf = hist_entry__comm_snprintf, | ||
105 | .se_width_idx = HISTC_COMM, | ||
106 | }; | ||
107 | |||
108 | /* --sort dso */ | ||
109 | |||
100 | static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) | 110 | static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) |
101 | { | 111 | { |
102 | struct dso *dso_l = map_l ? map_l->dso : NULL; | 112 | struct dso *dso_l = map_l ? map_l->dso : NULL; |
@@ -117,40 +127,12 @@ static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) | |||
117 | return strcmp(dso_name_l, dso_name_r); | 127 | return strcmp(dso_name_l, dso_name_r); |
118 | } | 128 | } |
119 | 129 | ||
120 | struct sort_entry sort_comm = { | ||
121 | .se_header = "Command", | ||
122 | .se_cmp = sort__comm_cmp, | ||
123 | .se_collapse = sort__comm_collapse, | ||
124 | .se_snprintf = hist_entry__comm_snprintf, | ||
125 | .se_width_idx = HISTC_COMM, | ||
126 | }; | ||
127 | |||
128 | /* --sort dso */ | ||
129 | |||
130 | static int64_t | 130 | static int64_t |
131 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | 131 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
132 | { | 132 | { |
133 | return _sort__dso_cmp(left->ms.map, right->ms.map); | 133 | return _sort__dso_cmp(left->ms.map, right->ms.map); |
134 | } | 134 | } |
135 | 135 | ||
136 | |||
137 | static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r, | ||
138 | u64 ip_l, u64 ip_r) | ||
139 | { | ||
140 | if (!sym_l || !sym_r) | ||
141 | return cmp_null(sym_l, sym_r); | ||
142 | |||
143 | if (sym_l == sym_r) | ||
144 | return 0; | ||
145 | |||
146 | if (sym_l) | ||
147 | ip_l = sym_l->start; | ||
148 | if (sym_r) | ||
149 | ip_r = sym_r->start; | ||
150 | |||
151 | return (int64_t)(ip_r - ip_l); | ||
152 | } | ||
153 | |||
154 | static int _hist_entry__dso_snprintf(struct map *map, char *bf, | 136 | static int _hist_entry__dso_snprintf(struct map *map, char *bf, |
155 | size_t size, unsigned int width) | 137 | size_t size, unsigned int width) |
156 | { | 138 | { |
@@ -169,9 +151,43 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | |||
169 | return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); | 151 | return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); |
170 | } | 152 | } |
171 | 153 | ||
154 | struct sort_entry sort_dso = { | ||
155 | .se_header = "Shared Object", | ||
156 | .se_cmp = sort__dso_cmp, | ||
157 | .se_snprintf = hist_entry__dso_snprintf, | ||
158 | .se_width_idx = HISTC_DSO, | ||
159 | }; | ||
160 | |||
161 | /* --sort symbol */ | ||
162 | |||
163 | static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) | ||
164 | { | ||
165 | u64 ip_l, ip_r; | ||
166 | |||
167 | if (!sym_l || !sym_r) | ||
168 | return cmp_null(sym_l, sym_r); | ||
169 | |||
170 | if (sym_l == sym_r) | ||
171 | return 0; | ||
172 | |||
173 | ip_l = sym_l->start; | ||
174 | ip_r = sym_r->start; | ||
175 | |||
176 | return (int64_t)(ip_r - ip_l); | ||
177 | } | ||
178 | |||
179 | static int64_t | ||
180 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
181 | { | ||
182 | if (!left->ms.sym && !right->ms.sym) | ||
183 | return right->level - left->level; | ||
184 | |||
185 | return _sort__sym_cmp(left->ms.sym, right->ms.sym); | ||
186 | } | ||
187 | |||
172 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | 188 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, |
173 | u64 ip, char level, char *bf, size_t size, | 189 | u64 ip, char level, char *bf, size_t size, |
174 | unsigned int width __maybe_unused) | 190 | unsigned int width) |
175 | { | 191 | { |
176 | size_t ret = 0; | 192 | size_t ret = 0; |
177 | 193 | ||
@@ -197,43 +213,13 @@ static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, | |||
197 | return ret; | 213 | return ret; |
198 | } | 214 | } |
199 | 215 | ||
200 | |||
201 | struct sort_entry sort_dso = { | ||
202 | .se_header = "Shared Object", | ||
203 | .se_cmp = sort__dso_cmp, | ||
204 | .se_snprintf = hist_entry__dso_snprintf, | ||
205 | .se_width_idx = HISTC_DSO, | ||
206 | }; | ||
207 | |||
208 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | 216 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, |
209 | size_t size, | 217 | size_t size, unsigned int width) |
210 | unsigned int width __maybe_unused) | ||
211 | { | 218 | { |
212 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, | 219 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, |
213 | self->level, bf, size, width); | 220 | self->level, bf, size, width); |
214 | } | 221 | } |
215 | 222 | ||
216 | /* --sort symbol */ | ||
217 | static int64_t | ||
218 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | ||
219 | { | ||
220 | u64 ip_l, ip_r; | ||
221 | |||
222 | if (!left->ms.sym && !right->ms.sym) | ||
223 | return right->level - left->level; | ||
224 | |||
225 | if (!left->ms.sym || !right->ms.sym) | ||
226 | return cmp_null(left->ms.sym, right->ms.sym); | ||
227 | |||
228 | if (left->ms.sym == right->ms.sym) | ||
229 | return 0; | ||
230 | |||
231 | ip_l = left->ms.sym->start; | ||
232 | ip_r = right->ms.sym->start; | ||
233 | |||
234 | return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r); | ||
235 | } | ||
236 | |||
237 | struct sort_entry sort_sym = { | 223 | struct sort_entry sort_sym = { |
238 | .se_header = "Symbol", | 224 | .se_header = "Symbol", |
239 | .se_cmp = sort__sym_cmp, | 225 | .se_cmp = sort__sym_cmp, |
@@ -253,7 +239,7 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |||
253 | size_t size, | 239 | size_t size, |
254 | unsigned int width __maybe_unused) | 240 | unsigned int width __maybe_unused) |
255 | { | 241 | { |
256 | FILE *fp; | 242 | FILE *fp = NULL; |
257 | char cmd[PATH_MAX + 2], *path = self->srcline, *nl; | 243 | char cmd[PATH_MAX + 2], *path = self->srcline, *nl; |
258 | size_t line_len; | 244 | size_t line_len; |
259 | 245 | ||
@@ -274,7 +260,6 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |||
274 | 260 | ||
275 | if (getline(&path, &line_len, fp) < 0 || !line_len) | 261 | if (getline(&path, &line_len, fp) < 0 || !line_len) |
276 | goto out_ip; | 262 | goto out_ip; |
277 | fclose(fp); | ||
278 | self->srcline = strdup(path); | 263 | self->srcline = strdup(path); |
279 | if (self->srcline == NULL) | 264 | if (self->srcline == NULL) |
280 | goto out_ip; | 265 | goto out_ip; |
@@ -284,8 +269,12 @@ static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf, | |||
284 | *nl = '\0'; | 269 | *nl = '\0'; |
285 | path = self->srcline; | 270 | path = self->srcline; |
286 | out_path: | 271 | out_path: |
272 | if (fp) | ||
273 | pclose(fp); | ||
287 | return repsep_snprintf(bf, size, "%s", path); | 274 | return repsep_snprintf(bf, size, "%s", path); |
288 | out_ip: | 275 | out_ip: |
276 | if (fp) | ||
277 | pclose(fp); | ||
289 | return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); | 278 | return repsep_snprintf(bf, size, "%-#*llx", BITS_PER_LONG / 4, self->ip); |
290 | } | 279 | } |
291 | 280 | ||
@@ -335,7 +324,7 @@ sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | |||
335 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | 324 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, |
336 | size_t size, unsigned int width) | 325 | size_t size, unsigned int width) |
337 | { | 326 | { |
338 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); | 327 | return repsep_snprintf(bf, size, "%*d", width, self->cpu); |
339 | } | 328 | } |
340 | 329 | ||
341 | struct sort_entry sort_cpu = { | 330 | struct sort_entry sort_cpu = { |
@@ -345,6 +334,8 @@ struct sort_entry sort_cpu = { | |||
345 | .se_width_idx = HISTC_CPU, | 334 | .se_width_idx = HISTC_CPU, |
346 | }; | 335 | }; |
347 | 336 | ||
337 | /* sort keys for branch stacks */ | ||
338 | |||
348 | static int64_t | 339 | static int64_t |
349 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) | 340 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) |
350 | { | 341 | { |
@@ -359,13 +350,6 @@ static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, | |||
359 | bf, size, width); | 350 | bf, size, width); |
360 | } | 351 | } |
361 | 352 | ||
362 | struct sort_entry sort_dso_from = { | ||
363 | .se_header = "Source Shared Object", | ||
364 | .se_cmp = sort__dso_from_cmp, | ||
365 | .se_snprintf = hist_entry__dso_from_snprintf, | ||
366 | .se_width_idx = HISTC_DSO_FROM, | ||
367 | }; | ||
368 | |||
369 | static int64_t | 353 | static int64_t |
370 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | 354 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) |
371 | { | 355 | { |
@@ -389,8 +373,7 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) | |||
389 | if (!from_l->sym && !from_r->sym) | 373 | if (!from_l->sym && !from_r->sym) |
390 | return right->level - left->level; | 374 | return right->level - left->level; |
391 | 375 | ||
392 | return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr, | 376 | return _sort__sym_cmp(from_l->sym, from_r->sym); |
393 | from_r->addr); | ||
394 | } | 377 | } |
395 | 378 | ||
396 | static int64_t | 379 | static int64_t |
@@ -402,12 +385,11 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | |||
402 | if (!to_l->sym && !to_r->sym) | 385 | if (!to_l->sym && !to_r->sym) |
403 | return right->level - left->level; | 386 | return right->level - left->level; |
404 | 387 | ||
405 | return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr); | 388 | return _sort__sym_cmp(to_l->sym, to_r->sym); |
406 | } | 389 | } |
407 | 390 | ||
408 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | 391 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, |
409 | size_t size, | 392 | size_t size, unsigned int width) |
410 | unsigned int width __maybe_unused) | ||
411 | { | 393 | { |
412 | struct addr_map_symbol *from = &self->branch_info->from; | 394 | struct addr_map_symbol *from = &self->branch_info->from; |
413 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | 395 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, |
@@ -416,8 +398,7 @@ static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | |||
416 | } | 398 | } |
417 | 399 | ||
418 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | 400 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, |
419 | size_t size, | 401 | size_t size, unsigned int width) |
420 | unsigned int width __maybe_unused) | ||
421 | { | 402 | { |
422 | struct addr_map_symbol *to = &self->branch_info->to; | 403 | struct addr_map_symbol *to = &self->branch_info->to; |
423 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | 404 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, |
@@ -425,6 +406,13 @@ static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | |||
425 | 406 | ||
426 | } | 407 | } |
427 | 408 | ||
409 | struct sort_entry sort_dso_from = { | ||
410 | .se_header = "Source Shared Object", | ||
411 | .se_cmp = sort__dso_from_cmp, | ||
412 | .se_snprintf = hist_entry__dso_from_snprintf, | ||
413 | .se_width_idx = HISTC_DSO_FROM, | ||
414 | }; | ||
415 | |||
428 | struct sort_entry sort_dso_to = { | 416 | struct sort_entry sort_dso_to = { |
429 | .se_header = "Target Shared Object", | 417 | .se_header = "Target Shared Object", |
430 | .se_cmp = sort__dso_to_cmp, | 418 | .se_cmp = sort__dso_to_cmp, |
@@ -484,30 +472,40 @@ struct sort_dimension { | |||
484 | 472 | ||
485 | #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } | 473 | #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } |
486 | 474 | ||
487 | static struct sort_dimension sort_dimensions[] = { | 475 | static struct sort_dimension common_sort_dimensions[] = { |
488 | DIM(SORT_PID, "pid", sort_thread), | 476 | DIM(SORT_PID, "pid", sort_thread), |
489 | DIM(SORT_COMM, "comm", sort_comm), | 477 | DIM(SORT_COMM, "comm", sort_comm), |
490 | DIM(SORT_DSO, "dso", sort_dso), | 478 | DIM(SORT_DSO, "dso", sort_dso), |
491 | DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), | ||
492 | DIM(SORT_DSO_TO, "dso_to", sort_dso_to), | ||
493 | DIM(SORT_SYM, "symbol", sort_sym), | 479 | DIM(SORT_SYM, "symbol", sort_sym), |
494 | DIM(SORT_SYM_TO, "symbol_from", sort_sym_from), | ||
495 | DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to), | ||
496 | DIM(SORT_PARENT, "parent", sort_parent), | 480 | DIM(SORT_PARENT, "parent", sort_parent), |
497 | DIM(SORT_CPU, "cpu", sort_cpu), | 481 | DIM(SORT_CPU, "cpu", sort_cpu), |
498 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), | ||
499 | DIM(SORT_SRCLINE, "srcline", sort_srcline), | 482 | DIM(SORT_SRCLINE, "srcline", sort_srcline), |
500 | }; | 483 | }; |
501 | 484 | ||
485 | #undef DIM | ||
486 | |||
487 | #define DIM(d, n, func) [d - __SORT_BRANCH_STACK] = { .name = n, .entry = &(func) } | ||
488 | |||
489 | static struct sort_dimension bstack_sort_dimensions[] = { | ||
490 | DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), | ||
491 | DIM(SORT_DSO_TO, "dso_to", sort_dso_to), | ||
492 | DIM(SORT_SYM_FROM, "symbol_from", sort_sym_from), | ||
493 | DIM(SORT_SYM_TO, "symbol_to", sort_sym_to), | ||
494 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), | ||
495 | }; | ||
496 | |||
497 | #undef DIM | ||
498 | |||
502 | int sort_dimension__add(const char *tok) | 499 | int sort_dimension__add(const char *tok) |
503 | { | 500 | { |
504 | unsigned int i; | 501 | unsigned int i; |
505 | 502 | ||
506 | for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) { | 503 | for (i = 0; i < ARRAY_SIZE(common_sort_dimensions); i++) { |
507 | struct sort_dimension *sd = &sort_dimensions[i]; | 504 | struct sort_dimension *sd = &common_sort_dimensions[i]; |
508 | 505 | ||
509 | if (strncasecmp(tok, sd->name, strlen(tok))) | 506 | if (strncasecmp(tok, sd->name, strlen(tok))) |
510 | continue; | 507 | continue; |
508 | |||
511 | if (sd->entry == &sort_parent) { | 509 | if (sd->entry == &sort_parent) { |
512 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | 510 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); |
513 | if (ret) { | 511 | if (ret) { |
@@ -518,9 +516,7 @@ int sort_dimension__add(const char *tok) | |||
518 | return -EINVAL; | 516 | return -EINVAL; |
519 | } | 517 | } |
520 | sort__has_parent = 1; | 518 | sort__has_parent = 1; |
521 | } else if (sd->entry == &sort_sym || | 519 | } else if (sd->entry == &sort_sym) { |
522 | sd->entry == &sort_sym_from || | ||
523 | sd->entry == &sort_sym_to) { | ||
524 | sort__has_sym = 1; | 520 | sort__has_sym = 1; |
525 | } | 521 | } |
526 | 522 | ||
@@ -530,52 +526,69 @@ int sort_dimension__add(const char *tok) | |||
530 | if (sd->entry->se_collapse) | 526 | if (sd->entry->se_collapse) |
531 | sort__need_collapse = 1; | 527 | sort__need_collapse = 1; |
532 | 528 | ||
533 | if (list_empty(&hist_entry__sort_list)) { | 529 | if (list_empty(&hist_entry__sort_list)) |
534 | if (!strcmp(sd->name, "pid")) | 530 | sort__first_dimension = i; |
535 | sort__first_dimension = SORT_PID; | ||
536 | else if (!strcmp(sd->name, "comm")) | ||
537 | sort__first_dimension = SORT_COMM; | ||
538 | else if (!strcmp(sd->name, "dso")) | ||
539 | sort__first_dimension = SORT_DSO; | ||
540 | else if (!strcmp(sd->name, "symbol")) | ||
541 | sort__first_dimension = SORT_SYM; | ||
542 | else if (!strcmp(sd->name, "parent")) | ||
543 | sort__first_dimension = SORT_PARENT; | ||
544 | else if (!strcmp(sd->name, "cpu")) | ||
545 | sort__first_dimension = SORT_CPU; | ||
546 | else if (!strcmp(sd->name, "symbol_from")) | ||
547 | sort__first_dimension = SORT_SYM_FROM; | ||
548 | else if (!strcmp(sd->name, "symbol_to")) | ||
549 | sort__first_dimension = SORT_SYM_TO; | ||
550 | else if (!strcmp(sd->name, "dso_from")) | ||
551 | sort__first_dimension = SORT_DSO_FROM; | ||
552 | else if (!strcmp(sd->name, "dso_to")) | ||
553 | sort__first_dimension = SORT_DSO_TO; | ||
554 | else if (!strcmp(sd->name, "mispredict")) | ||
555 | sort__first_dimension = SORT_MISPREDICT; | ||
556 | } | ||
557 | 531 | ||
558 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | 532 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); |
559 | sd->taken = 1; | 533 | sd->taken = 1; |
560 | 534 | ||
561 | return 0; | 535 | return 0; |
562 | } | 536 | } |
537 | |||
538 | for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { | ||
539 | struct sort_dimension *sd = &bstack_sort_dimensions[i]; | ||
540 | |||
541 | if (strncasecmp(tok, sd->name, strlen(tok))) | ||
542 | continue; | ||
543 | |||
544 | if (sort__branch_mode != 1) | ||
545 | return -EINVAL; | ||
546 | |||
547 | if (sd->entry == &sort_sym_from || sd->entry == &sort_sym_to) | ||
548 | sort__has_sym = 1; | ||
549 | |||
550 | if (sd->taken) | ||
551 | return 0; | ||
552 | |||
553 | if (sd->entry->se_collapse) | ||
554 | sort__need_collapse = 1; | ||
555 | |||
556 | if (list_empty(&hist_entry__sort_list)) | ||
557 | sort__first_dimension = i + __SORT_BRANCH_STACK; | ||
558 | |||
559 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | ||
560 | sd->taken = 1; | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
563 | return -ESRCH; | 565 | return -ESRCH; |
564 | } | 566 | } |
565 | 567 | ||
566 | void setup_sorting(const char * const usagestr[], const struct option *opts) | 568 | int setup_sorting(void) |
567 | { | 569 | { |
568 | char *tmp, *tok, *str = strdup(sort_order); | 570 | char *tmp, *tok, *str = strdup(sort_order); |
571 | int ret = 0; | ||
572 | |||
573 | if (str == NULL) { | ||
574 | error("Not enough memory to setup sort keys"); | ||
575 | return -ENOMEM; | ||
576 | } | ||
569 | 577 | ||
570 | for (tok = strtok_r(str, ", ", &tmp); | 578 | for (tok = strtok_r(str, ", ", &tmp); |
571 | tok; tok = strtok_r(NULL, ", ", &tmp)) { | 579 | tok; tok = strtok_r(NULL, ", ", &tmp)) { |
572 | if (sort_dimension__add(tok) < 0) { | 580 | ret = sort_dimension__add(tok); |
581 | if (ret == -EINVAL) { | ||
582 | error("Invalid --sort key: `%s'", tok); | ||
583 | break; | ||
584 | } else if (ret == -ESRCH) { | ||
573 | error("Unknown --sort key: `%s'", tok); | 585 | error("Unknown --sort key: `%s'", tok); |
574 | usage_with_options(usagestr, opts); | 586 | break; |
575 | } | 587 | } |
576 | } | 588 | } |
577 | 589 | ||
578 | free(str); | 590 | free(str); |
591 | return ret; | ||
579 | } | 592 | } |
580 | 593 | ||
581 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 594 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 5786f323b597..b13e56f6ccbe 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -52,6 +52,19 @@ struct he_stat { | |||
52 | u32 nr_events; | 52 | u32 nr_events; |
53 | }; | 53 | }; |
54 | 54 | ||
55 | struct hist_entry_diff { | ||
56 | bool computed; | ||
57 | |||
58 | /* PERF_HPP__DELTA */ | ||
59 | double period_ratio_delta; | ||
60 | |||
61 | /* PERF_HPP__RATIO */ | ||
62 | double period_ratio; | ||
63 | |||
64 | /* HISTC_WEIGHTED_DIFF */ | ||
65 | s64 wdiff; | ||
66 | }; | ||
67 | |||
55 | /** | 68 | /** |
56 | * struct hist_entry - histogram entry | 69 | * struct hist_entry - histogram entry |
57 | * | 70 | * |
@@ -61,12 +74,18 @@ struct he_stat { | |||
61 | struct hist_entry { | 74 | struct hist_entry { |
62 | struct rb_node rb_node_in; | 75 | struct rb_node rb_node_in; |
63 | struct rb_node rb_node; | 76 | struct rb_node rb_node; |
77 | union { | ||
78 | struct list_head node; | ||
79 | struct list_head head; | ||
80 | } pairs; | ||
64 | struct he_stat stat; | 81 | struct he_stat stat; |
65 | struct map_symbol ms; | 82 | struct map_symbol ms; |
66 | struct thread *thread; | 83 | struct thread *thread; |
67 | u64 ip; | 84 | u64 ip; |
68 | s32 cpu; | 85 | s32 cpu; |
69 | 86 | ||
87 | struct hist_entry_diff diff; | ||
88 | |||
70 | /* XXX These two should move to some tree widget lib */ | 89 | /* XXX These two should move to some tree widget lib */ |
71 | u16 row_offset; | 90 | u16 row_offset; |
72 | u16 nr_rows; | 91 | u16 nr_rows; |
@@ -78,28 +97,47 @@ struct hist_entry { | |||
78 | char *srcline; | 97 | char *srcline; |
79 | struct symbol *parent; | 98 | struct symbol *parent; |
80 | unsigned long position; | 99 | unsigned long position; |
81 | union { | 100 | struct rb_root sorted_chain; |
82 | struct hist_entry *pair; | ||
83 | struct rb_root sorted_chain; | ||
84 | }; | ||
85 | struct branch_info *branch_info; | 101 | struct branch_info *branch_info; |
86 | struct hists *hists; | 102 | struct hists *hists; |
87 | struct callchain_root callchain[0]; | 103 | struct callchain_root callchain[0]; |
88 | }; | 104 | }; |
89 | 105 | ||
106 | static inline bool hist_entry__has_pairs(struct hist_entry *he) | ||
107 | { | ||
108 | return !list_empty(&he->pairs.node); | ||
109 | } | ||
110 | |||
111 | static inline struct hist_entry *hist_entry__next_pair(struct hist_entry *he) | ||
112 | { | ||
113 | if (hist_entry__has_pairs(he)) | ||
114 | return list_entry(he->pairs.node.next, struct hist_entry, pairs.node); | ||
115 | return NULL; | ||
116 | } | ||
117 | |||
118 | static inline void hist_entry__add_pair(struct hist_entry *he, | ||
119 | struct hist_entry *pair) | ||
120 | { | ||
121 | list_add_tail(&he->pairs.head, &pair->pairs.node); | ||
122 | } | ||
123 | |||
90 | enum sort_type { | 124 | enum sort_type { |
125 | /* common sort keys */ | ||
91 | SORT_PID, | 126 | SORT_PID, |
92 | SORT_COMM, | 127 | SORT_COMM, |
93 | SORT_DSO, | 128 | SORT_DSO, |
94 | SORT_SYM, | 129 | SORT_SYM, |
95 | SORT_PARENT, | 130 | SORT_PARENT, |
96 | SORT_CPU, | 131 | SORT_CPU, |
97 | SORT_DSO_FROM, | 132 | SORT_SRCLINE, |
133 | |||
134 | /* branch stack specific sort keys */ | ||
135 | __SORT_BRANCH_STACK, | ||
136 | SORT_DSO_FROM = __SORT_BRANCH_STACK, | ||
98 | SORT_DSO_TO, | 137 | SORT_DSO_TO, |
99 | SORT_SYM_FROM, | 138 | SORT_SYM_FROM, |
100 | SORT_SYM_TO, | 139 | SORT_SYM_TO, |
101 | SORT_MISPREDICT, | 140 | SORT_MISPREDICT, |
102 | SORT_SRCLINE, | ||
103 | }; | 141 | }; |
104 | 142 | ||
105 | /* | 143 | /* |
@@ -122,7 +160,7 @@ struct sort_entry { | |||
122 | extern struct sort_entry sort_thread; | 160 | extern struct sort_entry sort_thread; |
123 | extern struct list_head hist_entry__sort_list; | 161 | extern struct list_head hist_entry__sort_list; |
124 | 162 | ||
125 | void setup_sorting(const char * const usagestr[], const struct option *opts); | 163 | int setup_sorting(void); |
126 | extern int sort_dimension__add(const char *); | 164 | extern int sort_dimension__add(const char *); |
127 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 165 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, |
128 | const char *list_name, FILE *fp); | 166 | const char *list_name, FILE *fp); |
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index 2eeb51baf077..cfa906882e2c 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c | |||
@@ -90,17 +90,17 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) | |||
90 | if (!strbuf_avail(sb)) | 90 | if (!strbuf_avail(sb)) |
91 | strbuf_grow(sb, 64); | 91 | strbuf_grow(sb, 64); |
92 | va_start(ap, fmt); | 92 | va_start(ap, fmt); |
93 | len = vscnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); | 93 | len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); |
94 | va_end(ap); | 94 | va_end(ap); |
95 | if (len < 0) | 95 | if (len < 0) |
96 | die("your vscnprintf is broken"); | 96 | die("your vsnprintf is broken"); |
97 | if (len > strbuf_avail(sb)) { | 97 | if (len > strbuf_avail(sb)) { |
98 | strbuf_grow(sb, len); | 98 | strbuf_grow(sb, len); |
99 | va_start(ap, fmt); | 99 | va_start(ap, fmt); |
100 | len = vscnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); | 100 | len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); |
101 | va_end(ap); | 101 | va_end(ap); |
102 | if (len > strbuf_avail(sb)) { | 102 | if (len > strbuf_avail(sb)) { |
103 | die("this should not happen, your snprintf is broken"); | 103 | die("this should not happen, your vsnprintf is broken"); |
104 | } | 104 | } |
105 | } | 105 | } |
106 | strbuf_setlen(sb, sb->len + len); | 106 | strbuf_setlen(sb, sb->len + len); |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 32170590892d..29c7b2cb2521 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -314,6 +314,42 @@ int strtailcmp(const char *s1, const char *s2) | |||
314 | } | 314 | } |
315 | 315 | ||
316 | /** | 316 | /** |
317 | * strxfrchar - Locate and replace character in @s | ||
318 | * @s: The string to be searched/changed. | ||
319 | * @from: Source character to be replaced. | ||
320 | * @to: Destination character. | ||
321 | * | ||
322 | * Return pointer to the changed string. | ||
323 | */ | ||
324 | char *strxfrchar(char *s, char from, char to) | ||
325 | { | ||
326 | char *p = s; | ||
327 | |||
328 | while ((p = strchr(p, from)) != NULL) | ||
329 | *p++ = to; | ||
330 | |||
331 | return s; | ||
332 | } | ||
333 | |||
334 | /** | ||
335 | * ltrim - Removes leading whitespace from @s. | ||
336 | * @s: The string to be stripped. | ||
337 | * | ||
338 | * Return pointer to the first non-whitespace character in @s. | ||
339 | */ | ||
340 | char *ltrim(char *s) | ||
341 | { | ||
342 | int len = strlen(s); | ||
343 | |||
344 | while (len && isspace(*s)) { | ||
345 | len--; | ||
346 | s++; | ||
347 | } | ||
348 | |||
349 | return s; | ||
350 | } | ||
351 | |||
352 | /** | ||
317 | * rtrim - Removes trailing whitespace from @s. | 353 | * rtrim - Removes trailing whitespace from @s. |
318 | * @s: The string to be stripped. | 354 | * @s: The string to be stripped. |
319 | * | 355 | * |
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index 155d8b7078a7..55433aa42c8f 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c | |||
@@ -35,11 +35,11 @@ out_delete: | |||
35 | return NULL; | 35 | return NULL; |
36 | } | 36 | } |
37 | 37 | ||
38 | static void str_node__delete(struct str_node *self, bool dupstr) | 38 | static void str_node__delete(struct str_node *snode, bool dupstr) |
39 | { | 39 | { |
40 | if (dupstr) | 40 | if (dupstr) |
41 | free((void *)self->s); | 41 | free((void *)snode->s); |
42 | free(self); | 42 | free(snode); |
43 | } | 43 | } |
44 | 44 | ||
45 | static | 45 | static |
@@ -59,12 +59,12 @@ static int strlist__node_cmp(struct rb_node *rb_node, const void *entry) | |||
59 | return strcmp(snode->s, str); | 59 | return strcmp(snode->s, str); |
60 | } | 60 | } |
61 | 61 | ||
62 | int strlist__add(struct strlist *self, const char *new_entry) | 62 | int strlist__add(struct strlist *slist, const char *new_entry) |
63 | { | 63 | { |
64 | return rblist__add_node(&self->rblist, new_entry); | 64 | return rblist__add_node(&slist->rblist, new_entry); |
65 | } | 65 | } |
66 | 66 | ||
67 | int strlist__load(struct strlist *self, const char *filename) | 67 | int strlist__load(struct strlist *slist, const char *filename) |
68 | { | 68 | { |
69 | char entry[1024]; | 69 | char entry[1024]; |
70 | int err; | 70 | int err; |
@@ -80,7 +80,7 @@ int strlist__load(struct strlist *self, const char *filename) | |||
80 | continue; | 80 | continue; |
81 | entry[len - 1] = '\0'; | 81 | entry[len - 1] = '\0'; |
82 | 82 | ||
83 | err = strlist__add(self, entry); | 83 | err = strlist__add(slist, entry); |
84 | if (err != 0) | 84 | if (err != 0) |
85 | goto out; | 85 | goto out; |
86 | } | 86 | } |
@@ -107,56 +107,56 @@ struct str_node *strlist__find(struct strlist *slist, const char *entry) | |||
107 | return snode; | 107 | return snode; |
108 | } | 108 | } |
109 | 109 | ||
110 | static int strlist__parse_list_entry(struct strlist *self, const char *s) | 110 | static int strlist__parse_list_entry(struct strlist *slist, const char *s) |
111 | { | 111 | { |
112 | if (strncmp(s, "file://", 7) == 0) | 112 | if (strncmp(s, "file://", 7) == 0) |
113 | return strlist__load(self, s + 7); | 113 | return strlist__load(slist, s + 7); |
114 | 114 | ||
115 | return strlist__add(self, s); | 115 | return strlist__add(slist, s); |
116 | } | 116 | } |
117 | 117 | ||
118 | int strlist__parse_list(struct strlist *self, const char *s) | 118 | int strlist__parse_list(struct strlist *slist, const char *s) |
119 | { | 119 | { |
120 | char *sep; | 120 | char *sep; |
121 | int err; | 121 | int err; |
122 | 122 | ||
123 | while ((sep = strchr(s, ',')) != NULL) { | 123 | while ((sep = strchr(s, ',')) != NULL) { |
124 | *sep = '\0'; | 124 | *sep = '\0'; |
125 | err = strlist__parse_list_entry(self, s); | 125 | err = strlist__parse_list_entry(slist, s); |
126 | *sep = ','; | 126 | *sep = ','; |
127 | if (err != 0) | 127 | if (err != 0) |
128 | return err; | 128 | return err; |
129 | s = sep + 1; | 129 | s = sep + 1; |
130 | } | 130 | } |
131 | 131 | ||
132 | return *s ? strlist__parse_list_entry(self, s) : 0; | 132 | return *s ? strlist__parse_list_entry(slist, s) : 0; |
133 | } | 133 | } |
134 | 134 | ||
135 | struct strlist *strlist__new(bool dupstr, const char *slist) | 135 | struct strlist *strlist__new(bool dupstr, const char *list) |
136 | { | 136 | { |
137 | struct strlist *self = malloc(sizeof(*self)); | 137 | struct strlist *slist = malloc(sizeof(*slist)); |
138 | 138 | ||
139 | if (self != NULL) { | 139 | if (slist != NULL) { |
140 | rblist__init(&self->rblist); | 140 | rblist__init(&slist->rblist); |
141 | self->rblist.node_cmp = strlist__node_cmp; | 141 | slist->rblist.node_cmp = strlist__node_cmp; |
142 | self->rblist.node_new = strlist__node_new; | 142 | slist->rblist.node_new = strlist__node_new; |
143 | self->rblist.node_delete = strlist__node_delete; | 143 | slist->rblist.node_delete = strlist__node_delete; |
144 | 144 | ||
145 | self->dupstr = dupstr; | 145 | slist->dupstr = dupstr; |
146 | if (slist && strlist__parse_list(self, slist) != 0) | 146 | if (slist && strlist__parse_list(slist, list) != 0) |
147 | goto out_error; | 147 | goto out_error; |
148 | } | 148 | } |
149 | 149 | ||
150 | return self; | 150 | return slist; |
151 | out_error: | 151 | out_error: |
152 | free(self); | 152 | free(slist); |
153 | return NULL; | 153 | return NULL; |
154 | } | 154 | } |
155 | 155 | ||
156 | void strlist__delete(struct strlist *self) | 156 | void strlist__delete(struct strlist *slist) |
157 | { | 157 | { |
158 | if (self != NULL) | 158 | if (slist != NULL) |
159 | rblist__delete(&self->rblist); | 159 | rblist__delete(&slist->rblist); |
160 | } | 160 | } |
161 | 161 | ||
162 | struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx) | 162 | struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx) |
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h index dd9f922ec67c..5c7f87069d9c 100644 --- a/tools/perf/util/strlist.h +++ b/tools/perf/util/strlist.h | |||
@@ -17,34 +17,34 @@ struct strlist { | |||
17 | }; | 17 | }; |
18 | 18 | ||
19 | struct strlist *strlist__new(bool dupstr, const char *slist); | 19 | struct strlist *strlist__new(bool dupstr, const char *slist); |
20 | void strlist__delete(struct strlist *self); | 20 | void strlist__delete(struct strlist *slist); |
21 | 21 | ||
22 | void strlist__remove(struct strlist *self, struct str_node *sn); | 22 | void strlist__remove(struct strlist *slist, struct str_node *sn); |
23 | int strlist__load(struct strlist *self, const char *filename); | 23 | int strlist__load(struct strlist *slist, const char *filename); |
24 | int strlist__add(struct strlist *self, const char *str); | 24 | int strlist__add(struct strlist *slist, const char *str); |
25 | 25 | ||
26 | struct str_node *strlist__entry(const struct strlist *self, unsigned int idx); | 26 | struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx); |
27 | struct str_node *strlist__find(struct strlist *self, const char *entry); | 27 | struct str_node *strlist__find(struct strlist *slist, const char *entry); |
28 | 28 | ||
29 | static inline bool strlist__has_entry(struct strlist *self, const char *entry) | 29 | static inline bool strlist__has_entry(struct strlist *slist, const char *entry) |
30 | { | 30 | { |
31 | return strlist__find(self, entry) != NULL; | 31 | return strlist__find(slist, entry) != NULL; |
32 | } | 32 | } |
33 | 33 | ||
34 | static inline bool strlist__empty(const struct strlist *self) | 34 | static inline bool strlist__empty(const struct strlist *slist) |
35 | { | 35 | { |
36 | return rblist__empty(&self->rblist); | 36 | return rblist__empty(&slist->rblist); |
37 | } | 37 | } |
38 | 38 | ||
39 | static inline unsigned int strlist__nr_entries(const struct strlist *self) | 39 | static inline unsigned int strlist__nr_entries(const struct strlist *slist) |
40 | { | 40 | { |
41 | return rblist__nr_entries(&self->rblist); | 41 | return rblist__nr_entries(&slist->rblist); |
42 | } | 42 | } |
43 | 43 | ||
44 | /* For strlist iteration */ | 44 | /* For strlist iteration */ |
45 | static inline struct str_node *strlist__first(struct strlist *self) | 45 | static inline struct str_node *strlist__first(struct strlist *slist) |
46 | { | 46 | { |
47 | struct rb_node *rn = rb_first(&self->rblist.entries); | 47 | struct rb_node *rn = rb_first(&slist->rblist.entries); |
48 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; | 48 | return rn ? rb_entry(rn, struct str_node, rb_node) : NULL; |
49 | } | 49 | } |
50 | static inline struct str_node *strlist__next(struct str_node *sn) | 50 | static inline struct str_node *strlist__next(struct str_node *sn) |
@@ -59,21 +59,21 @@ static inline struct str_node *strlist__next(struct str_node *sn) | |||
59 | /** | 59 | /** |
60 | * strlist_for_each - iterate over a strlist | 60 | * strlist_for_each - iterate over a strlist |
61 | * @pos: the &struct str_node to use as a loop cursor. | 61 | * @pos: the &struct str_node to use as a loop cursor. |
62 | * @self: the &struct strlist for loop. | 62 | * @slist: the &struct strlist for loop. |
63 | */ | 63 | */ |
64 | #define strlist__for_each(pos, self) \ | 64 | #define strlist__for_each(pos, slist) \ |
65 | for (pos = strlist__first(self); pos; pos = strlist__next(pos)) | 65 | for (pos = strlist__first(slist); pos; pos = strlist__next(pos)) |
66 | 66 | ||
67 | /** | 67 | /** |
68 | * strlist_for_each_safe - iterate over a strlist safe against removal of | 68 | * strlist_for_each_safe - iterate over a strlist safe against removal of |
69 | * str_node | 69 | * str_node |
70 | * @pos: the &struct str_node to use as a loop cursor. | 70 | * @pos: the &struct str_node to use as a loop cursor. |
71 | * @n: another &struct str_node to use as temporary storage. | 71 | * @n: another &struct str_node to use as temporary storage. |
72 | * @self: the &struct strlist for loop. | 72 | * @slist: the &struct strlist for loop. |
73 | */ | 73 | */ |
74 | #define strlist__for_each_safe(pos, n, self) \ | 74 | #define strlist__for_each_safe(pos, n, slist) \ |
75 | for (pos = strlist__first(self), n = strlist__next(pos); pos;\ | 75 | for (pos = strlist__first(slist), n = strlist__next(pos); pos;\ |
76 | pos = n, n = strlist__next(n)) | 76 | pos = n, n = strlist__next(n)) |
77 | 77 | ||
78 | int strlist__parse_list(struct strlist *self, const char *s); | 78 | int strlist__parse_list(struct strlist *slist, const char *s); |
79 | #endif /* __PERF_STRLIST_H */ | 79 | #endif /* __PERF_STRLIST_H */ |
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index db0cc92cf2ea..54efcb5659ac 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -1,6 +1,3 @@ | |||
1 | #include <libelf.h> | ||
2 | #include <gelf.h> | ||
3 | #include <elf.h> | ||
4 | #include <fcntl.h> | 1 | #include <fcntl.h> |
5 | #include <stdio.h> | 2 | #include <stdio.h> |
6 | #include <errno.h> | 3 | #include <errno.h> |
@@ -718,6 +715,17 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
718 | sym.st_value); | 715 | sym.st_value); |
719 | used_opd = true; | 716 | used_opd = true; |
720 | } | 717 | } |
718 | /* | ||
719 | * When loading symbols in a data mapping, ABS symbols (which | ||
720 | * has a value of SHN_ABS in its st_shndx) failed at | ||
721 | * elf_getscn(). And it marks the loading as a failure so | ||
722 | * already loaded symbols cannot be fixed up. | ||
723 | * | ||
724 | * I'm not sure what should be done. Just ignore them for now. | ||
725 | * - Namhyung Kim | ||
726 | */ | ||
727 | if (sym.st_shndx == SHN_ABS) | ||
728 | continue; | ||
721 | 729 | ||
722 | sec = elf_getscn(runtime_ss->elf, sym.st_shndx); | 730 | sec = elf_getscn(runtime_ss->elf, sym.st_shndx); |
723 | if (!sec) | 731 | if (!sec) |
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 259f8f2ea9c9..a7390cde63bc 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c | |||
@@ -1,6 +1,5 @@ | |||
1 | #include "symbol.h" | 1 | #include "symbol.h" |
2 | 2 | ||
3 | #include <elf.h> | ||
4 | #include <stdio.h> | 3 | #include <stdio.h> |
5 | #include <fcntl.h> | 4 | #include <fcntl.h> |
6 | #include <string.h> | 5 | #include <string.h> |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index e2e8c697cffe..e6432d85b43d 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -12,6 +12,7 @@ | |||
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 "symbol.h" | 16 | #include "symbol.h" |
16 | #include "strlist.h" | 17 | #include "strlist.h" |
17 | 18 | ||
@@ -23,13 +24,12 @@ | |||
23 | #define KSYM_NAME_LEN 256 | 24 | #define KSYM_NAME_LEN 256 |
24 | #endif | 25 | #endif |
25 | 26 | ||
26 | static void dso_cache__free(struct rb_root *root); | ||
27 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, | 27 | static int dso__load_kernel_sym(struct dso *dso, struct map *map, |
28 | symbol_filter_t filter); | 28 | symbol_filter_t filter); |
29 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | 29 | static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, |
30 | symbol_filter_t filter); | 30 | symbol_filter_t filter); |
31 | static int vmlinux_path__nr_entries; | 31 | int vmlinux_path__nr_entries; |
32 | static char **vmlinux_path; | 32 | char **vmlinux_path; |
33 | 33 | ||
34 | struct symbol_conf symbol_conf = { | 34 | struct symbol_conf symbol_conf = { |
35 | .exclude_other = true, | 35 | .exclude_other = true, |
@@ -56,39 +56,6 @@ static enum dso_binary_type binary_type_symtab[] = { | |||
56 | 56 | ||
57 | #define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab) | 57 | #define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab) |
58 | 58 | ||
59 | static enum dso_binary_type binary_type_data[] = { | ||
60 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | ||
61 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | ||
62 | DSO_BINARY_TYPE__NOT_FOUND, | ||
63 | }; | ||
64 | |||
65 | #define DSO_BINARY_TYPE__DATA_CNT ARRAY_SIZE(binary_type_data) | ||
66 | |||
67 | int dso__name_len(const struct dso *dso) | ||
68 | { | ||
69 | if (!dso) | ||
70 | return strlen("[unknown]"); | ||
71 | if (verbose) | ||
72 | return dso->long_name_len; | ||
73 | |||
74 | return dso->short_name_len; | ||
75 | } | ||
76 | |||
77 | bool dso__loaded(const struct dso *dso, enum map_type type) | ||
78 | { | ||
79 | return dso->loaded & (1 << type); | ||
80 | } | ||
81 | |||
82 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type) | ||
83 | { | ||
84 | return dso->sorted_by_name & (1 << type); | ||
85 | } | ||
86 | |||
87 | static void dso__set_sorted_by_name(struct dso *dso, enum map_type type) | ||
88 | { | ||
89 | dso->sorted_by_name |= (1 << type); | ||
90 | } | ||
91 | |||
92 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) | 59 | bool symbol_type__is_a(char symbol_type, enum map_type map_type) |
93 | { | 60 | { |
94 | symbol_type = toupper(symbol_type); | 61 | symbol_type = toupper(symbol_type); |
@@ -235,13 +202,6 @@ void __map_groups__fixup_end(struct map_groups *mg, enum map_type type) | |||
235 | curr->end = ~0ULL; | 202 | curr->end = ~0ULL; |
236 | } | 203 | } |
237 | 204 | ||
238 | static void map_groups__fixup_end(struct map_groups *mg) | ||
239 | { | ||
240 | int i; | ||
241 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
242 | __map_groups__fixup_end(mg, i); | ||
243 | } | ||
244 | |||
245 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) | 205 | struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name) |
246 | { | 206 | { |
247 | size_t namelen = strlen(name) + 1; | 207 | size_t namelen = strlen(name) + 1; |
@@ -270,7 +230,7 @@ void symbol__delete(struct symbol *sym) | |||
270 | free(((void *)sym) - symbol_conf.priv_size); | 230 | free(((void *)sym) - symbol_conf.priv_size); |
271 | } | 231 | } |
272 | 232 | ||
273 | static size_t symbol__fprintf(struct symbol *sym, FILE *fp) | 233 | size_t symbol__fprintf(struct symbol *sym, FILE *fp) |
274 | { | 234 | { |
275 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", | 235 | return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n", |
276 | sym->start, sym->end, | 236 | sym->start, sym->end, |
@@ -301,53 +261,7 @@ size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) | |||
301 | return symbol__fprintf_symname_offs(sym, NULL, fp); | 261 | return symbol__fprintf_symname_offs(sym, NULL, fp); |
302 | } | 262 | } |
303 | 263 | ||
304 | void dso__set_long_name(struct dso *dso, char *name) | 264 | void symbols__delete(struct rb_root *symbols) |
305 | { | ||
306 | if (name == NULL) | ||
307 | return; | ||
308 | dso->long_name = name; | ||
309 | dso->long_name_len = strlen(name); | ||
310 | } | ||
311 | |||
312 | static void dso__set_short_name(struct dso *dso, const char *name) | ||
313 | { | ||
314 | if (name == NULL) | ||
315 | return; | ||
316 | dso->short_name = name; | ||
317 | dso->short_name_len = strlen(name); | ||
318 | } | ||
319 | |||
320 | static void dso__set_basename(struct dso *dso) | ||
321 | { | ||
322 | dso__set_short_name(dso, basename(dso->long_name)); | ||
323 | } | ||
324 | |||
325 | struct dso *dso__new(const char *name) | ||
326 | { | ||
327 | struct dso *dso = calloc(1, sizeof(*dso) + strlen(name) + 1); | ||
328 | |||
329 | if (dso != NULL) { | ||
330 | int i; | ||
331 | strcpy(dso->name, name); | ||
332 | dso__set_long_name(dso, dso->name); | ||
333 | dso__set_short_name(dso, dso->name); | ||
334 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
335 | dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; | ||
336 | dso->cache = RB_ROOT; | ||
337 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; | ||
338 | dso->data_type = DSO_BINARY_TYPE__NOT_FOUND; | ||
339 | dso->loaded = 0; | ||
340 | dso->sorted_by_name = 0; | ||
341 | dso->has_build_id = 0; | ||
342 | dso->kernel = DSO_TYPE_USER; | ||
343 | dso->needs_swap = DSO_SWAP__UNSET; | ||
344 | INIT_LIST_HEAD(&dso->node); | ||
345 | } | ||
346 | |||
347 | return dso; | ||
348 | } | ||
349 | |||
350 | static void symbols__delete(struct rb_root *symbols) | ||
351 | { | 265 | { |
352 | struct symbol *pos; | 266 | struct symbol *pos; |
353 | struct rb_node *next = rb_first(symbols); | 267 | struct rb_node *next = rb_first(symbols); |
@@ -360,25 +274,6 @@ static void symbols__delete(struct rb_root *symbols) | |||
360 | } | 274 | } |
361 | } | 275 | } |
362 | 276 | ||
363 | void dso__delete(struct dso *dso) | ||
364 | { | ||
365 | int i; | ||
366 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
367 | symbols__delete(&dso->symbols[i]); | ||
368 | if (dso->sname_alloc) | ||
369 | free((char *)dso->short_name); | ||
370 | if (dso->lname_alloc) | ||
371 | free(dso->long_name); | ||
372 | dso_cache__free(&dso->cache); | ||
373 | free(dso); | ||
374 | } | ||
375 | |||
376 | void dso__set_build_id(struct dso *dso, void *build_id) | ||
377 | { | ||
378 | memcpy(dso->build_id, build_id, sizeof(dso->build_id)); | ||
379 | dso->has_build_id = 1; | ||
380 | } | ||
381 | |||
382 | void symbols__insert(struct rb_root *symbols, struct symbol *sym) | 277 | void symbols__insert(struct rb_root *symbols, struct symbol *sym) |
383 | { | 278 | { |
384 | struct rb_node **p = &symbols->rb_node; | 279 | struct rb_node **p = &symbols->rb_node; |
@@ -504,29 +399,6 @@ void dso__sort_by_name(struct dso *dso, enum map_type type) | |||
504 | &dso->symbols[type]); | 399 | &dso->symbols[type]); |
505 | } | 400 | } |
506 | 401 | ||
507 | int build_id__sprintf(const u8 *build_id, int len, char *bf) | ||
508 | { | ||
509 | char *bid = bf; | ||
510 | const u8 *raw = build_id; | ||
511 | int i; | ||
512 | |||
513 | for (i = 0; i < len; ++i) { | ||
514 | sprintf(bid, "%02x", *raw); | ||
515 | ++raw; | ||
516 | bid += 2; | ||
517 | } | ||
518 | |||
519 | return raw - build_id; | ||
520 | } | ||
521 | |||
522 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp) | ||
523 | { | ||
524 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
525 | |||
526 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); | ||
527 | return fprintf(fp, "%s", sbuild_id); | ||
528 | } | ||
529 | |||
530 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | 402 | size_t dso__fprintf_symbols_by_name(struct dso *dso, |
531 | enum map_type type, FILE *fp) | 403 | enum map_type type, FILE *fp) |
532 | { | 404 | { |
@@ -542,25 +414,6 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso, | |||
542 | return ret; | 414 | return ret; |
543 | } | 415 | } |
544 | 416 | ||
545 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp) | ||
546 | { | ||
547 | struct rb_node *nd; | ||
548 | size_t ret = fprintf(fp, "dso: %s (", dso->short_name); | ||
549 | |||
550 | if (dso->short_name != dso->long_name) | ||
551 | ret += fprintf(fp, "%s, ", dso->long_name); | ||
552 | ret += fprintf(fp, "%s, %sloaded, ", map_type__name[type], | ||
553 | dso->loaded ? "" : "NOT "); | ||
554 | ret += dso__fprintf_buildid(dso, fp); | ||
555 | ret += fprintf(fp, ")\n"); | ||
556 | for (nd = rb_first(&dso->symbols[type]); nd; nd = rb_next(nd)) { | ||
557 | struct symbol *pos = rb_entry(nd, struct symbol, rb_node); | ||
558 | ret += symbol__fprintf(pos, fp); | ||
559 | } | ||
560 | |||
561 | return ret; | ||
562 | } | ||
563 | |||
564 | int kallsyms__parse(const char *filename, void *arg, | 417 | int kallsyms__parse(const char *filename, void *arg, |
565 | int (*process_symbol)(void *arg, const char *name, | 418 | int (*process_symbol)(void *arg, const char *name, |
566 | char type, u64 start)) | 419 | char type, u64 start)) |
@@ -792,8 +645,8 @@ discard_symbol: rb_erase(&pos->rb_node, root); | |||
792 | return count + moved; | 645 | return count + moved; |
793 | } | 646 | } |
794 | 647 | ||
795 | static bool symbol__restricted_filename(const char *filename, | 648 | bool symbol__restricted_filename(const char *filename, |
796 | const char *restricted_filename) | 649 | const char *restricted_filename) |
797 | { | 650 | { |
798 | bool restricted = false; | 651 | bool restricted = false; |
799 | 652 | ||
@@ -892,136 +745,6 @@ out_failure: | |||
892 | return -1; | 745 | return -1; |
893 | } | 746 | } |
894 | 747 | ||
895 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id) | ||
896 | { | ||
897 | return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0; | ||
898 | } | ||
899 | |||
900 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | ||
901 | { | ||
902 | bool have_build_id = false; | ||
903 | struct dso *pos; | ||
904 | |||
905 | list_for_each_entry(pos, head, node) { | ||
906 | if (with_hits && !pos->hit) | ||
907 | continue; | ||
908 | if (pos->has_build_id) { | ||
909 | have_build_id = true; | ||
910 | continue; | ||
911 | } | ||
912 | if (filename__read_build_id(pos->long_name, pos->build_id, | ||
913 | sizeof(pos->build_id)) > 0) { | ||
914 | have_build_id = true; | ||
915 | pos->has_build_id = true; | ||
916 | } | ||
917 | } | ||
918 | |||
919 | return have_build_id; | ||
920 | } | ||
921 | |||
922 | char dso__symtab_origin(const struct dso *dso) | ||
923 | { | ||
924 | static const char origin[] = { | ||
925 | [DSO_BINARY_TYPE__KALLSYMS] = 'k', | ||
926 | [DSO_BINARY_TYPE__VMLINUX] = 'v', | ||
927 | [DSO_BINARY_TYPE__JAVA_JIT] = 'j', | ||
928 | [DSO_BINARY_TYPE__DEBUGLINK] = 'l', | ||
929 | [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', | ||
930 | [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', | ||
931 | [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', | ||
932 | [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', | ||
933 | [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', | ||
934 | [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', | ||
935 | [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', | ||
936 | [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', | ||
937 | [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', | ||
938 | }; | ||
939 | |||
940 | if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) | ||
941 | return '!'; | ||
942 | return origin[dso->symtab_type]; | ||
943 | } | ||
944 | |||
945 | int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | ||
946 | char *root_dir, char *file, size_t size) | ||
947 | { | ||
948 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | ||
949 | int ret = 0; | ||
950 | |||
951 | switch (type) { | ||
952 | case DSO_BINARY_TYPE__DEBUGLINK: { | ||
953 | char *debuglink; | ||
954 | |||
955 | strncpy(file, dso->long_name, size); | ||
956 | debuglink = file + dso->long_name_len; | ||
957 | while (debuglink != file && *debuglink != '/') | ||
958 | debuglink--; | ||
959 | if (*debuglink == '/') | ||
960 | debuglink++; | ||
961 | filename__read_debuglink(dso->long_name, debuglink, | ||
962 | size - (debuglink - file)); | ||
963 | } | ||
964 | break; | ||
965 | case DSO_BINARY_TYPE__BUILD_ID_CACHE: | ||
966 | /* skip the locally configured cache if a symfs is given */ | ||
967 | if (symbol_conf.symfs[0] || | ||
968 | (dso__build_id_filename(dso, file, size) == NULL)) | ||
969 | ret = -1; | ||
970 | break; | ||
971 | |||
972 | case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: | ||
973 | snprintf(file, size, "%s/usr/lib/debug%s.debug", | ||
974 | symbol_conf.symfs, dso->long_name); | ||
975 | break; | ||
976 | |||
977 | case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: | ||
978 | snprintf(file, size, "%s/usr/lib/debug%s", | ||
979 | symbol_conf.symfs, dso->long_name); | ||
980 | break; | ||
981 | |||
982 | case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: | ||
983 | if (!dso->has_build_id) { | ||
984 | ret = -1; | ||
985 | break; | ||
986 | } | ||
987 | |||
988 | build_id__sprintf(dso->build_id, | ||
989 | sizeof(dso->build_id), | ||
990 | build_id_hex); | ||
991 | snprintf(file, size, | ||
992 | "%s/usr/lib/debug/.build-id/%.2s/%s.debug", | ||
993 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | ||
994 | break; | ||
995 | |||
996 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: | ||
997 | snprintf(file, size, "%s%s", | ||
998 | symbol_conf.symfs, dso->long_name); | ||
999 | break; | ||
1000 | |||
1001 | case DSO_BINARY_TYPE__GUEST_KMODULE: | ||
1002 | snprintf(file, size, "%s%s%s", symbol_conf.symfs, | ||
1003 | root_dir, dso->long_name); | ||
1004 | break; | ||
1005 | |||
1006 | case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: | ||
1007 | snprintf(file, size, "%s%s", symbol_conf.symfs, | ||
1008 | dso->long_name); | ||
1009 | break; | ||
1010 | |||
1011 | default: | ||
1012 | case DSO_BINARY_TYPE__KALLSYMS: | ||
1013 | case DSO_BINARY_TYPE__VMLINUX: | ||
1014 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: | ||
1015 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | ||
1016 | case DSO_BINARY_TYPE__JAVA_JIT: | ||
1017 | case DSO_BINARY_TYPE__NOT_FOUND: | ||
1018 | ret = -1; | ||
1019 | break; | ||
1020 | } | ||
1021 | |||
1022 | return ret; | ||
1023 | } | ||
1024 | |||
1025 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | 748 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) |
1026 | { | 749 | { |
1027 | char *name; | 750 | char *name; |
@@ -1045,10 +768,6 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1045 | else | 768 | else |
1046 | machine = NULL; | 769 | machine = NULL; |
1047 | 770 | ||
1048 | name = malloc(PATH_MAX); | ||
1049 | if (!name) | ||
1050 | return -1; | ||
1051 | |||
1052 | dso->adjust_symbols = 0; | 771 | dso->adjust_symbols = 0; |
1053 | 772 | ||
1054 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { | 773 | if (strncmp(dso->name, "/tmp/perf-", 10) == 0) { |
@@ -1072,6 +791,10 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
1072 | if (machine) | 791 | if (machine) |
1073 | root_dir = machine->root_dir; | 792 | root_dir = machine->root_dir; |
1074 | 793 | ||
794 | name = malloc(PATH_MAX); | ||
795 | if (!name) | ||
796 | return -1; | ||
797 | |||
1075 | /* Iterate over candidate debug images. | 798 | /* Iterate over candidate debug images. |
1076 | * Keep track of "interesting" ones (those which have a symtab, dynsym, | 799 | * Keep track of "interesting" ones (those which have a symtab, dynsym, |
1077 | * and/or opd section) for processing. | 800 | * and/or opd section) for processing. |
@@ -1157,221 +880,6 @@ struct map *map_groups__find_by_name(struct map_groups *mg, | |||
1157 | return NULL; | 880 | return NULL; |
1158 | } | 881 | } |
1159 | 882 | ||
1160 | static int dso__kernel_module_get_build_id(struct dso *dso, | ||
1161 | const char *root_dir) | ||
1162 | { | ||
1163 | char filename[PATH_MAX]; | ||
1164 | /* | ||
1165 | * kernel module short names are of the form "[module]" and | ||
1166 | * we need just "module" here. | ||
1167 | */ | ||
1168 | const char *name = dso->short_name + 1; | ||
1169 | |||
1170 | snprintf(filename, sizeof(filename), | ||
1171 | "%s/sys/module/%.*s/notes/.note.gnu.build-id", | ||
1172 | root_dir, (int)strlen(name) - 1, name); | ||
1173 | |||
1174 | if (sysfs__read_build_id(filename, dso->build_id, | ||
1175 | sizeof(dso->build_id)) == 0) | ||
1176 | dso->has_build_id = true; | ||
1177 | |||
1178 | return 0; | ||
1179 | } | ||
1180 | |||
1181 | static int map_groups__set_modules_path_dir(struct map_groups *mg, | ||
1182 | const char *dir_name) | ||
1183 | { | ||
1184 | struct dirent *dent; | ||
1185 | DIR *dir = opendir(dir_name); | ||
1186 | int ret = 0; | ||
1187 | |||
1188 | if (!dir) { | ||
1189 | pr_debug("%s: cannot open %s dir\n", __func__, dir_name); | ||
1190 | return -1; | ||
1191 | } | ||
1192 | |||
1193 | while ((dent = readdir(dir)) != NULL) { | ||
1194 | char path[PATH_MAX]; | ||
1195 | struct stat st; | ||
1196 | |||
1197 | /*sshfs might return bad dent->d_type, so we have to stat*/ | ||
1198 | snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name); | ||
1199 | if (stat(path, &st)) | ||
1200 | continue; | ||
1201 | |||
1202 | if (S_ISDIR(st.st_mode)) { | ||
1203 | if (!strcmp(dent->d_name, ".") || | ||
1204 | !strcmp(dent->d_name, "..")) | ||
1205 | continue; | ||
1206 | |||
1207 | ret = map_groups__set_modules_path_dir(mg, path); | ||
1208 | if (ret < 0) | ||
1209 | goto out; | ||
1210 | } else { | ||
1211 | char *dot = strrchr(dent->d_name, '.'), | ||
1212 | dso_name[PATH_MAX]; | ||
1213 | struct map *map; | ||
1214 | char *long_name; | ||
1215 | |||
1216 | if (dot == NULL || strcmp(dot, ".ko")) | ||
1217 | continue; | ||
1218 | snprintf(dso_name, sizeof(dso_name), "[%.*s]", | ||
1219 | (int)(dot - dent->d_name), dent->d_name); | ||
1220 | |||
1221 | strxfrchar(dso_name, '-', '_'); | ||
1222 | map = map_groups__find_by_name(mg, MAP__FUNCTION, | ||
1223 | dso_name); | ||
1224 | if (map == NULL) | ||
1225 | continue; | ||
1226 | |||
1227 | long_name = strdup(path); | ||
1228 | if (long_name == NULL) { | ||
1229 | ret = -1; | ||
1230 | goto out; | ||
1231 | } | ||
1232 | dso__set_long_name(map->dso, long_name); | ||
1233 | map->dso->lname_alloc = 1; | ||
1234 | dso__kernel_module_get_build_id(map->dso, ""); | ||
1235 | } | ||
1236 | } | ||
1237 | |||
1238 | out: | ||
1239 | closedir(dir); | ||
1240 | return ret; | ||
1241 | } | ||
1242 | |||
1243 | static char *get_kernel_version(const char *root_dir) | ||
1244 | { | ||
1245 | char version[PATH_MAX]; | ||
1246 | FILE *file; | ||
1247 | char *name, *tmp; | ||
1248 | const char *prefix = "Linux version "; | ||
1249 | |||
1250 | sprintf(version, "%s/proc/version", root_dir); | ||
1251 | file = fopen(version, "r"); | ||
1252 | if (!file) | ||
1253 | return NULL; | ||
1254 | |||
1255 | version[0] = '\0'; | ||
1256 | tmp = fgets(version, sizeof(version), file); | ||
1257 | fclose(file); | ||
1258 | |||
1259 | name = strstr(version, prefix); | ||
1260 | if (!name) | ||
1261 | return NULL; | ||
1262 | name += strlen(prefix); | ||
1263 | tmp = strchr(name, ' '); | ||
1264 | if (tmp) | ||
1265 | *tmp = '\0'; | ||
1266 | |||
1267 | return strdup(name); | ||
1268 | } | ||
1269 | |||
1270 | static int machine__set_modules_path(struct machine *machine) | ||
1271 | { | ||
1272 | char *version; | ||
1273 | char modules_path[PATH_MAX]; | ||
1274 | |||
1275 | version = get_kernel_version(machine->root_dir); | ||
1276 | if (!version) | ||
1277 | return -1; | ||
1278 | |||
1279 | snprintf(modules_path, sizeof(modules_path), "%s/lib/modules/%s/kernel", | ||
1280 | machine->root_dir, version); | ||
1281 | free(version); | ||
1282 | |||
1283 | return map_groups__set_modules_path_dir(&machine->kmaps, modules_path); | ||
1284 | } | ||
1285 | |||
1286 | struct map *machine__new_module(struct machine *machine, u64 start, | ||
1287 | const char *filename) | ||
1288 | { | ||
1289 | struct map *map; | ||
1290 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, filename); | ||
1291 | |||
1292 | if (dso == NULL) | ||
1293 | return NULL; | ||
1294 | |||
1295 | map = map__new2(start, dso, MAP__FUNCTION); | ||
1296 | if (map == NULL) | ||
1297 | return NULL; | ||
1298 | |||
1299 | if (machine__is_host(machine)) | ||
1300 | dso->symtab_type = DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE; | ||
1301 | else | ||
1302 | dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; | ||
1303 | map_groups__insert(&machine->kmaps, map); | ||
1304 | return map; | ||
1305 | } | ||
1306 | |||
1307 | static int machine__create_modules(struct machine *machine) | ||
1308 | { | ||
1309 | char *line = NULL; | ||
1310 | size_t n; | ||
1311 | FILE *file; | ||
1312 | struct map *map; | ||
1313 | const char *modules; | ||
1314 | char path[PATH_MAX]; | ||
1315 | |||
1316 | if (machine__is_default_guest(machine)) | ||
1317 | modules = symbol_conf.default_guest_modules; | ||
1318 | else { | ||
1319 | sprintf(path, "%s/proc/modules", machine->root_dir); | ||
1320 | modules = path; | ||
1321 | } | ||
1322 | |||
1323 | if (symbol__restricted_filename(path, "/proc/modules")) | ||
1324 | return -1; | ||
1325 | |||
1326 | file = fopen(modules, "r"); | ||
1327 | if (file == NULL) | ||
1328 | return -1; | ||
1329 | |||
1330 | while (!feof(file)) { | ||
1331 | char name[PATH_MAX]; | ||
1332 | u64 start; | ||
1333 | char *sep; | ||
1334 | int line_len; | ||
1335 | |||
1336 | line_len = getline(&line, &n, file); | ||
1337 | if (line_len < 0) | ||
1338 | break; | ||
1339 | |||
1340 | if (!line) | ||
1341 | goto out_failure; | ||
1342 | |||
1343 | line[--line_len] = '\0'; /* \n */ | ||
1344 | |||
1345 | sep = strrchr(line, 'x'); | ||
1346 | if (sep == NULL) | ||
1347 | continue; | ||
1348 | |||
1349 | hex2u64(sep + 1, &start); | ||
1350 | |||
1351 | sep = strchr(line, ' '); | ||
1352 | if (sep == NULL) | ||
1353 | continue; | ||
1354 | |||
1355 | *sep = '\0'; | ||
1356 | |||
1357 | snprintf(name, sizeof(name), "[%s]", line); | ||
1358 | map = machine__new_module(machine, start, name); | ||
1359 | if (map == NULL) | ||
1360 | goto out_delete_line; | ||
1361 | dso__kernel_module_get_build_id(map->dso, machine->root_dir); | ||
1362 | } | ||
1363 | |||
1364 | free(line); | ||
1365 | fclose(file); | ||
1366 | |||
1367 | return machine__set_modules_path(machine); | ||
1368 | |||
1369 | out_delete_line: | ||
1370 | free(line); | ||
1371 | out_failure: | ||
1372 | return -1; | ||
1373 | } | ||
1374 | |||
1375 | int dso__load_vmlinux(struct dso *dso, struct map *map, | 883 | int dso__load_vmlinux(struct dso *dso, struct map *map, |
1376 | const char *vmlinux, symbol_filter_t filter) | 884 | const char *vmlinux, symbol_filter_t filter) |
1377 | { | 885 | { |
@@ -1415,8 +923,10 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
1415 | filename = dso__build_id_filename(dso, NULL, 0); | 923 | filename = dso__build_id_filename(dso, NULL, 0); |
1416 | if (filename != NULL) { | 924 | if (filename != NULL) { |
1417 | err = dso__load_vmlinux(dso, map, filename, filter); | 925 | err = dso__load_vmlinux(dso, map, filename, filter); |
1418 | if (err > 0) | 926 | if (err > 0) { |
927 | dso->lname_alloc = 1; | ||
1419 | goto out; | 928 | goto out; |
929 | } | ||
1420 | free(filename); | 930 | free(filename); |
1421 | } | 931 | } |
1422 | 932 | ||
@@ -1424,6 +934,7 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
1424 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter); | 934 | err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter); |
1425 | if (err > 0) { | 935 | if (err > 0) { |
1426 | dso__set_long_name(dso, strdup(vmlinux_path[i])); | 936 | dso__set_long_name(dso, strdup(vmlinux_path[i])); |
937 | dso->lname_alloc = 1; | ||
1427 | break; | 938 | break; |
1428 | } | 939 | } |
1429 | } | 940 | } |
@@ -1463,6 +974,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
1463 | if (err > 0) { | 974 | if (err > 0) { |
1464 | dso__set_long_name(dso, | 975 | dso__set_long_name(dso, |
1465 | strdup(symbol_conf.vmlinux_name)); | 976 | strdup(symbol_conf.vmlinux_name)); |
977 | dso->lname_alloc = 1; | ||
1466 | goto out_fixup; | 978 | goto out_fixup; |
1467 | } | 979 | } |
1468 | return err; | 980 | return err; |
@@ -1591,287 +1103,6 @@ out_try_fixup: | |||
1591 | return err; | 1103 | return err; |
1592 | } | 1104 | } |
1593 | 1105 | ||
1594 | void dsos__add(struct list_head *head, struct dso *dso) | ||
1595 | { | ||
1596 | list_add_tail(&dso->node, head); | ||
1597 | } | ||
1598 | |||
1599 | struct dso *dsos__find(struct list_head *head, const char *name) | ||
1600 | { | ||
1601 | struct dso *pos; | ||
1602 | |||
1603 | list_for_each_entry(pos, head, node) | ||
1604 | if (strcmp(pos->long_name, name) == 0) | ||
1605 | return pos; | ||
1606 | return NULL; | ||
1607 | } | ||
1608 | |||
1609 | struct dso *__dsos__findnew(struct list_head *head, const char *name) | ||
1610 | { | ||
1611 | struct dso *dso = dsos__find(head, name); | ||
1612 | |||
1613 | if (!dso) { | ||
1614 | dso = dso__new(name); | ||
1615 | if (dso != NULL) { | ||
1616 | dsos__add(head, dso); | ||
1617 | dso__set_basename(dso); | ||
1618 | } | ||
1619 | } | ||
1620 | |||
1621 | return dso; | ||
1622 | } | ||
1623 | |||
1624 | size_t __dsos__fprintf(struct list_head *head, FILE *fp) | ||
1625 | { | ||
1626 | struct dso *pos; | ||
1627 | size_t ret = 0; | ||
1628 | |||
1629 | list_for_each_entry(pos, head, node) { | ||
1630 | int i; | ||
1631 | for (i = 0; i < MAP__NR_TYPES; ++i) | ||
1632 | ret += dso__fprintf(pos, i, fp); | ||
1633 | } | ||
1634 | |||
1635 | return ret; | ||
1636 | } | ||
1637 | |||
1638 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp) | ||
1639 | { | ||
1640 | struct rb_node *nd; | ||
1641 | size_t ret = 0; | ||
1642 | |||
1643 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | ||
1644 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
1645 | ret += __dsos__fprintf(&pos->kernel_dsos, fp); | ||
1646 | ret += __dsos__fprintf(&pos->user_dsos, fp); | ||
1647 | } | ||
1648 | |||
1649 | return ret; | ||
1650 | } | ||
1651 | |||
1652 | static size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp, | ||
1653 | bool with_hits) | ||
1654 | { | ||
1655 | struct dso *pos; | ||
1656 | size_t ret = 0; | ||
1657 | |||
1658 | list_for_each_entry(pos, head, node) { | ||
1659 | if (with_hits && !pos->hit) | ||
1660 | continue; | ||
1661 | ret += dso__fprintf_buildid(pos, fp); | ||
1662 | ret += fprintf(fp, " %s\n", pos->long_name); | ||
1663 | } | ||
1664 | return ret; | ||
1665 | } | ||
1666 | |||
1667 | size_t machine__fprintf_dsos_buildid(struct machine *machine, FILE *fp, | ||
1668 | bool with_hits) | ||
1669 | { | ||
1670 | return __dsos__fprintf_buildid(&machine->kernel_dsos, fp, with_hits) + | ||
1671 | __dsos__fprintf_buildid(&machine->user_dsos, fp, with_hits); | ||
1672 | } | ||
1673 | |||
1674 | size_t machines__fprintf_dsos_buildid(struct rb_root *machines, | ||
1675 | FILE *fp, bool with_hits) | ||
1676 | { | ||
1677 | struct rb_node *nd; | ||
1678 | size_t ret = 0; | ||
1679 | |||
1680 | for (nd = rb_first(machines); nd; nd = rb_next(nd)) { | ||
1681 | struct machine *pos = rb_entry(nd, struct machine, rb_node); | ||
1682 | ret += machine__fprintf_dsos_buildid(pos, fp, with_hits); | ||
1683 | } | ||
1684 | return ret; | ||
1685 | } | ||
1686 | |||
1687 | static struct dso* | ||
1688 | dso__kernel_findnew(struct machine *machine, const char *name, | ||
1689 | const char *short_name, int dso_type) | ||
1690 | { | ||
1691 | /* | ||
1692 | * The kernel dso could be created by build_id processing. | ||
1693 | */ | ||
1694 | struct dso *dso = __dsos__findnew(&machine->kernel_dsos, name); | ||
1695 | |||
1696 | /* | ||
1697 | * We need to run this in all cases, since during the build_id | ||
1698 | * processing we had no idea this was the kernel dso. | ||
1699 | */ | ||
1700 | if (dso != NULL) { | ||
1701 | dso__set_short_name(dso, short_name); | ||
1702 | dso->kernel = dso_type; | ||
1703 | } | ||
1704 | |||
1705 | return dso; | ||
1706 | } | ||
1707 | |||
1708 | void dso__read_running_kernel_build_id(struct dso *dso, struct machine *machine) | ||
1709 | { | ||
1710 | char path[PATH_MAX]; | ||
1711 | |||
1712 | if (machine__is_default_guest(machine)) | ||
1713 | return; | ||
1714 | sprintf(path, "%s/sys/kernel/notes", machine->root_dir); | ||
1715 | if (sysfs__read_build_id(path, dso->build_id, | ||
1716 | sizeof(dso->build_id)) == 0) | ||
1717 | dso->has_build_id = true; | ||
1718 | } | ||
1719 | |||
1720 | static struct dso *machine__get_kernel(struct machine *machine) | ||
1721 | { | ||
1722 | const char *vmlinux_name = NULL; | ||
1723 | struct dso *kernel; | ||
1724 | |||
1725 | if (machine__is_host(machine)) { | ||
1726 | vmlinux_name = symbol_conf.vmlinux_name; | ||
1727 | if (!vmlinux_name) | ||
1728 | vmlinux_name = "[kernel.kallsyms]"; | ||
1729 | |||
1730 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
1731 | "[kernel]", | ||
1732 | DSO_TYPE_KERNEL); | ||
1733 | } else { | ||
1734 | char bf[PATH_MAX]; | ||
1735 | |||
1736 | if (machine__is_default_guest(machine)) | ||
1737 | vmlinux_name = symbol_conf.default_guest_vmlinux_name; | ||
1738 | if (!vmlinux_name) | ||
1739 | vmlinux_name = machine__mmap_name(machine, bf, | ||
1740 | sizeof(bf)); | ||
1741 | |||
1742 | kernel = dso__kernel_findnew(machine, vmlinux_name, | ||
1743 | "[guest.kernel]", | ||
1744 | DSO_TYPE_GUEST_KERNEL); | ||
1745 | } | ||
1746 | |||
1747 | if (kernel != NULL && (!kernel->has_build_id)) | ||
1748 | dso__read_running_kernel_build_id(kernel, machine); | ||
1749 | |||
1750 | return kernel; | ||
1751 | } | ||
1752 | |||
1753 | struct process_args { | ||
1754 | u64 start; | ||
1755 | }; | ||
1756 | |||
1757 | static int symbol__in_kernel(void *arg, const char *name, | ||
1758 | char type __maybe_unused, u64 start) | ||
1759 | { | ||
1760 | struct process_args *args = arg; | ||
1761 | |||
1762 | if (strchr(name, '[')) | ||
1763 | return 0; | ||
1764 | |||
1765 | args->start = start; | ||
1766 | return 1; | ||
1767 | } | ||
1768 | |||
1769 | /* Figure out the start address of kernel map from /proc/kallsyms */ | ||
1770 | static u64 machine__get_kernel_start_addr(struct machine *machine) | ||
1771 | { | ||
1772 | const char *filename; | ||
1773 | char path[PATH_MAX]; | ||
1774 | struct process_args args; | ||
1775 | |||
1776 | if (machine__is_host(machine)) { | ||
1777 | filename = "/proc/kallsyms"; | ||
1778 | } else { | ||
1779 | if (machine__is_default_guest(machine)) | ||
1780 | filename = (char *)symbol_conf.default_guest_kallsyms; | ||
1781 | else { | ||
1782 | sprintf(path, "%s/proc/kallsyms", machine->root_dir); | ||
1783 | filename = path; | ||
1784 | } | ||
1785 | } | ||
1786 | |||
1787 | if (symbol__restricted_filename(filename, "/proc/kallsyms")) | ||
1788 | return 0; | ||
1789 | |||
1790 | if (kallsyms__parse(filename, &args, symbol__in_kernel) <= 0) | ||
1791 | return 0; | ||
1792 | |||
1793 | return args.start; | ||
1794 | } | ||
1795 | |||
1796 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel) | ||
1797 | { | ||
1798 | enum map_type type; | ||
1799 | u64 start = machine__get_kernel_start_addr(machine); | ||
1800 | |||
1801 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
1802 | struct kmap *kmap; | ||
1803 | |||
1804 | machine->vmlinux_maps[type] = map__new2(start, kernel, type); | ||
1805 | if (machine->vmlinux_maps[type] == NULL) | ||
1806 | return -1; | ||
1807 | |||
1808 | machine->vmlinux_maps[type]->map_ip = | ||
1809 | machine->vmlinux_maps[type]->unmap_ip = | ||
1810 | identity__map_ip; | ||
1811 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
1812 | kmap->kmaps = &machine->kmaps; | ||
1813 | map_groups__insert(&machine->kmaps, | ||
1814 | machine->vmlinux_maps[type]); | ||
1815 | } | ||
1816 | |||
1817 | return 0; | ||
1818 | } | ||
1819 | |||
1820 | void machine__destroy_kernel_maps(struct machine *machine) | ||
1821 | { | ||
1822 | enum map_type type; | ||
1823 | |||
1824 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
1825 | struct kmap *kmap; | ||
1826 | |||
1827 | if (machine->vmlinux_maps[type] == NULL) | ||
1828 | continue; | ||
1829 | |||
1830 | kmap = map__kmap(machine->vmlinux_maps[type]); | ||
1831 | map_groups__remove(&machine->kmaps, | ||
1832 | machine->vmlinux_maps[type]); | ||
1833 | if (kmap->ref_reloc_sym) { | ||
1834 | /* | ||
1835 | * ref_reloc_sym is shared among all maps, so free just | ||
1836 | * on one of them. | ||
1837 | */ | ||
1838 | if (type == MAP__FUNCTION) { | ||
1839 | free((char *)kmap->ref_reloc_sym->name); | ||
1840 | kmap->ref_reloc_sym->name = NULL; | ||
1841 | free(kmap->ref_reloc_sym); | ||
1842 | } | ||
1843 | kmap->ref_reloc_sym = NULL; | ||
1844 | } | ||
1845 | |||
1846 | map__delete(machine->vmlinux_maps[type]); | ||
1847 | machine->vmlinux_maps[type] = NULL; | ||
1848 | } | ||
1849 | } | ||
1850 | |||
1851 | int machine__create_kernel_maps(struct machine *machine) | ||
1852 | { | ||
1853 | struct dso *kernel = machine__get_kernel(machine); | ||
1854 | |||
1855 | if (kernel == NULL || | ||
1856 | __machine__create_kernel_maps(machine, kernel) < 0) | ||
1857 | return -1; | ||
1858 | |||
1859 | if (symbol_conf.use_modules && machine__create_modules(machine) < 0) { | ||
1860 | if (machine__is_host(machine)) | ||
1861 | pr_debug("Problems creating module maps, " | ||
1862 | "continuing anyway...\n"); | ||
1863 | else | ||
1864 | pr_debug("Problems creating module maps for guest %d, " | ||
1865 | "continuing anyway...\n", machine->pid); | ||
1866 | } | ||
1867 | |||
1868 | /* | ||
1869 | * Now that we have all the maps created, just set the ->end of them: | ||
1870 | */ | ||
1871 | map_groups__fixup_end(&machine->kmaps); | ||
1872 | return 0; | ||
1873 | } | ||
1874 | |||
1875 | static void vmlinux_path__exit(void) | 1106 | static void vmlinux_path__exit(void) |
1876 | { | 1107 | { |
1877 | while (--vmlinux_path__nr_entries >= 0) { | 1108 | while (--vmlinux_path__nr_entries >= 0) { |
@@ -1932,25 +1163,6 @@ out_fail: | |||
1932 | return -1; | 1163 | return -1; |
1933 | } | 1164 | } |
1934 | 1165 | ||
1935 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp) | ||
1936 | { | ||
1937 | int i; | ||
1938 | size_t printed = 0; | ||
1939 | struct dso *kdso = machine->vmlinux_maps[MAP__FUNCTION]->dso; | ||
1940 | |||
1941 | if (kdso->has_build_id) { | ||
1942 | char filename[PATH_MAX]; | ||
1943 | if (dso__build_id_filename(kdso, filename, sizeof(filename))) | ||
1944 | printed += fprintf(fp, "[0] %s\n", filename); | ||
1945 | } | ||
1946 | |||
1947 | for (i = 0; i < vmlinux_path__nr_entries; ++i) | ||
1948 | printed += fprintf(fp, "[%d] %s\n", | ||
1949 | i + kdso->has_build_id, vmlinux_path[i]); | ||
1950 | |||
1951 | return printed; | ||
1952 | } | ||
1953 | |||
1954 | static int setup_list(struct strlist **list, const char *list_str, | 1166 | static int setup_list(struct strlist **list, const char *list_str, |
1955 | const char *list_name) | 1167 | const char *list_name) |
1956 | { | 1168 | { |
@@ -2054,377 +1266,3 @@ void symbol__exit(void) | |||
2054 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | 1266 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; |
2055 | symbol_conf.initialized = false; | 1267 | symbol_conf.initialized = false; |
2056 | } | 1268 | } |
2057 | |||
2058 | int machines__create_kernel_maps(struct rb_root *machines, pid_t pid) | ||
2059 | { | ||
2060 | struct machine *machine = machines__findnew(machines, pid); | ||
2061 | |||
2062 | if (machine == NULL) | ||
2063 | return -1; | ||
2064 | |||
2065 | return machine__create_kernel_maps(machine); | ||
2066 | } | ||
2067 | |||
2068 | static int hex(char ch) | ||
2069 | { | ||
2070 | if ((ch >= '0') && (ch <= '9')) | ||
2071 | return ch - '0'; | ||
2072 | if ((ch >= 'a') && (ch <= 'f')) | ||
2073 | return ch - 'a' + 10; | ||
2074 | if ((ch >= 'A') && (ch <= 'F')) | ||
2075 | return ch - 'A' + 10; | ||
2076 | return -1; | ||
2077 | } | ||
2078 | |||
2079 | /* | ||
2080 | * While we find nice hex chars, build a long_val. | ||
2081 | * Return number of chars processed. | ||
2082 | */ | ||
2083 | int hex2u64(const char *ptr, u64 *long_val) | ||
2084 | { | ||
2085 | const char *p = ptr; | ||
2086 | *long_val = 0; | ||
2087 | |||
2088 | while (*p) { | ||
2089 | const int hex_val = hex(*p); | ||
2090 | |||
2091 | if (hex_val < 0) | ||
2092 | break; | ||
2093 | |||
2094 | *long_val = (*long_val << 4) | hex_val; | ||
2095 | p++; | ||
2096 | } | ||
2097 | |||
2098 | return p - ptr; | ||
2099 | } | ||
2100 | |||
2101 | char *strxfrchar(char *s, char from, char to) | ||
2102 | { | ||
2103 | char *p = s; | ||
2104 | |||
2105 | while ((p = strchr(p, from)) != NULL) | ||
2106 | *p++ = to; | ||
2107 | |||
2108 | return s; | ||
2109 | } | ||
2110 | |||
2111 | int machines__create_guest_kernel_maps(struct rb_root *machines) | ||
2112 | { | ||
2113 | int ret = 0; | ||
2114 | struct dirent **namelist = NULL; | ||
2115 | int i, items = 0; | ||
2116 | char path[PATH_MAX]; | ||
2117 | pid_t pid; | ||
2118 | char *endp; | ||
2119 | |||
2120 | if (symbol_conf.default_guest_vmlinux_name || | ||
2121 | symbol_conf.default_guest_modules || | ||
2122 | symbol_conf.default_guest_kallsyms) { | ||
2123 | machines__create_kernel_maps(machines, DEFAULT_GUEST_KERNEL_ID); | ||
2124 | } | ||
2125 | |||
2126 | if (symbol_conf.guestmount) { | ||
2127 | items = scandir(symbol_conf.guestmount, &namelist, NULL, NULL); | ||
2128 | if (items <= 0) | ||
2129 | return -ENOENT; | ||
2130 | for (i = 0; i < items; i++) { | ||
2131 | if (!isdigit(namelist[i]->d_name[0])) { | ||
2132 | /* Filter out . and .. */ | ||
2133 | continue; | ||
2134 | } | ||
2135 | pid = (pid_t)strtol(namelist[i]->d_name, &endp, 10); | ||
2136 | if ((*endp != '\0') || | ||
2137 | (endp == namelist[i]->d_name) || | ||
2138 | (errno == ERANGE)) { | ||
2139 | pr_debug("invalid directory (%s). Skipping.\n", | ||
2140 | namelist[i]->d_name); | ||
2141 | continue; | ||
2142 | } | ||
2143 | sprintf(path, "%s/%s/proc/kallsyms", | ||
2144 | symbol_conf.guestmount, | ||
2145 | namelist[i]->d_name); | ||
2146 | ret = access(path, R_OK); | ||
2147 | if (ret) { | ||
2148 | pr_debug("Can't access file %s\n", path); | ||
2149 | goto failure; | ||
2150 | } | ||
2151 | machines__create_kernel_maps(machines, pid); | ||
2152 | } | ||
2153 | failure: | ||
2154 | free(namelist); | ||
2155 | } | ||
2156 | |||
2157 | return ret; | ||
2158 | } | ||
2159 | |||
2160 | void machines__destroy_guest_kernel_maps(struct rb_root *machines) | ||
2161 | { | ||
2162 | struct rb_node *next = rb_first(machines); | ||
2163 | |||
2164 | while (next) { | ||
2165 | struct machine *pos = rb_entry(next, struct machine, rb_node); | ||
2166 | |||
2167 | next = rb_next(&pos->rb_node); | ||
2168 | rb_erase(&pos->rb_node, machines); | ||
2169 | machine__delete(pos); | ||
2170 | } | ||
2171 | } | ||
2172 | |||
2173 | int machine__load_kallsyms(struct machine *machine, const char *filename, | ||
2174 | enum map_type type, symbol_filter_t filter) | ||
2175 | { | ||
2176 | struct map *map = machine->vmlinux_maps[type]; | ||
2177 | int ret = dso__load_kallsyms(map->dso, filename, map, filter); | ||
2178 | |||
2179 | if (ret > 0) { | ||
2180 | dso__set_loaded(map->dso, type); | ||
2181 | /* | ||
2182 | * Since /proc/kallsyms will have multiple sessions for the | ||
2183 | * kernel, with modules between them, fixup the end of all | ||
2184 | * sections. | ||
2185 | */ | ||
2186 | __map_groups__fixup_end(&machine->kmaps, type); | ||
2187 | } | ||
2188 | |||
2189 | return ret; | ||
2190 | } | ||
2191 | |||
2192 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | ||
2193 | symbol_filter_t filter) | ||
2194 | { | ||
2195 | struct map *map = machine->vmlinux_maps[type]; | ||
2196 | int ret = dso__load_vmlinux_path(map->dso, map, filter); | ||
2197 | |||
2198 | if (ret > 0) { | ||
2199 | dso__set_loaded(map->dso, type); | ||
2200 | map__reloc_vmlinux(map); | ||
2201 | } | ||
2202 | |||
2203 | return ret; | ||
2204 | } | ||
2205 | |||
2206 | struct map *dso__new_map(const char *name) | ||
2207 | { | ||
2208 | struct map *map = NULL; | ||
2209 | struct dso *dso = dso__new(name); | ||
2210 | |||
2211 | if (dso) | ||
2212 | map = map__new2(0, dso, MAP__FUNCTION); | ||
2213 | |||
2214 | return map; | ||
2215 | } | ||
2216 | |||
2217 | static int open_dso(struct dso *dso, struct machine *machine) | ||
2218 | { | ||
2219 | char *root_dir = (char *) ""; | ||
2220 | char *name; | ||
2221 | int fd; | ||
2222 | |||
2223 | name = malloc(PATH_MAX); | ||
2224 | if (!name) | ||
2225 | return -ENOMEM; | ||
2226 | |||
2227 | if (machine) | ||
2228 | root_dir = machine->root_dir; | ||
2229 | |||
2230 | if (dso__binary_type_file(dso, dso->data_type, | ||
2231 | root_dir, name, PATH_MAX)) { | ||
2232 | free(name); | ||
2233 | return -EINVAL; | ||
2234 | } | ||
2235 | |||
2236 | fd = open(name, O_RDONLY); | ||
2237 | free(name); | ||
2238 | return fd; | ||
2239 | } | ||
2240 | |||
2241 | int dso__data_fd(struct dso *dso, struct machine *machine) | ||
2242 | { | ||
2243 | int i = 0; | ||
2244 | |||
2245 | if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) | ||
2246 | return open_dso(dso, machine); | ||
2247 | |||
2248 | do { | ||
2249 | int fd; | ||
2250 | |||
2251 | dso->data_type = binary_type_data[i++]; | ||
2252 | |||
2253 | fd = open_dso(dso, machine); | ||
2254 | if (fd >= 0) | ||
2255 | return fd; | ||
2256 | |||
2257 | } while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); | ||
2258 | |||
2259 | return -EINVAL; | ||
2260 | } | ||
2261 | |||
2262 | static void | ||
2263 | dso_cache__free(struct rb_root *root) | ||
2264 | { | ||
2265 | struct rb_node *next = rb_first(root); | ||
2266 | |||
2267 | while (next) { | ||
2268 | struct dso_cache *cache; | ||
2269 | |||
2270 | cache = rb_entry(next, struct dso_cache, rb_node); | ||
2271 | next = rb_next(&cache->rb_node); | ||
2272 | rb_erase(&cache->rb_node, root); | ||
2273 | free(cache); | ||
2274 | } | ||
2275 | } | ||
2276 | |||
2277 | static struct dso_cache* | ||
2278 | dso_cache__find(struct rb_root *root, u64 offset) | ||
2279 | { | ||
2280 | struct rb_node **p = &root->rb_node; | ||
2281 | struct rb_node *parent = NULL; | ||
2282 | struct dso_cache *cache; | ||
2283 | |||
2284 | while (*p != NULL) { | ||
2285 | u64 end; | ||
2286 | |||
2287 | parent = *p; | ||
2288 | cache = rb_entry(parent, struct dso_cache, rb_node); | ||
2289 | end = cache->offset + DSO__DATA_CACHE_SIZE; | ||
2290 | |||
2291 | if (offset < cache->offset) | ||
2292 | p = &(*p)->rb_left; | ||
2293 | else if (offset >= end) | ||
2294 | p = &(*p)->rb_right; | ||
2295 | else | ||
2296 | return cache; | ||
2297 | } | ||
2298 | return NULL; | ||
2299 | } | ||
2300 | |||
2301 | static void | ||
2302 | dso_cache__insert(struct rb_root *root, struct dso_cache *new) | ||
2303 | { | ||
2304 | struct rb_node **p = &root->rb_node; | ||
2305 | struct rb_node *parent = NULL; | ||
2306 | struct dso_cache *cache; | ||
2307 | u64 offset = new->offset; | ||
2308 | |||
2309 | while (*p != NULL) { | ||
2310 | u64 end; | ||
2311 | |||
2312 | parent = *p; | ||
2313 | cache = rb_entry(parent, struct dso_cache, rb_node); | ||
2314 | end = cache->offset + DSO__DATA_CACHE_SIZE; | ||
2315 | |||
2316 | if (offset < cache->offset) | ||
2317 | p = &(*p)->rb_left; | ||
2318 | else if (offset >= end) | ||
2319 | p = &(*p)->rb_right; | ||
2320 | } | ||
2321 | |||
2322 | rb_link_node(&new->rb_node, parent, p); | ||
2323 | rb_insert_color(&new->rb_node, root); | ||
2324 | } | ||
2325 | |||
2326 | static ssize_t | ||
2327 | dso_cache__memcpy(struct dso_cache *cache, u64 offset, | ||
2328 | u8 *data, u64 size) | ||
2329 | { | ||
2330 | u64 cache_offset = offset - cache->offset; | ||
2331 | u64 cache_size = min(cache->size - cache_offset, size); | ||
2332 | |||
2333 | memcpy(data, cache->data + cache_offset, cache_size); | ||
2334 | return cache_size; | ||
2335 | } | ||
2336 | |||
2337 | static ssize_t | ||
2338 | dso_cache__read(struct dso *dso, struct machine *machine, | ||
2339 | u64 offset, u8 *data, ssize_t size) | ||
2340 | { | ||
2341 | struct dso_cache *cache; | ||
2342 | ssize_t ret; | ||
2343 | int fd; | ||
2344 | |||
2345 | fd = dso__data_fd(dso, machine); | ||
2346 | if (fd < 0) | ||
2347 | return -1; | ||
2348 | |||
2349 | do { | ||
2350 | u64 cache_offset; | ||
2351 | |||
2352 | ret = -ENOMEM; | ||
2353 | |||
2354 | cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); | ||
2355 | if (!cache) | ||
2356 | break; | ||
2357 | |||
2358 | cache_offset = offset & DSO__DATA_CACHE_MASK; | ||
2359 | ret = -EINVAL; | ||
2360 | |||
2361 | if (-1 == lseek(fd, cache_offset, SEEK_SET)) | ||
2362 | break; | ||
2363 | |||
2364 | ret = read(fd, cache->data, DSO__DATA_CACHE_SIZE); | ||
2365 | if (ret <= 0) | ||
2366 | break; | ||
2367 | |||
2368 | cache->offset = cache_offset; | ||
2369 | cache->size = ret; | ||
2370 | dso_cache__insert(&dso->cache, cache); | ||
2371 | |||
2372 | ret = dso_cache__memcpy(cache, offset, data, size); | ||
2373 | |||
2374 | } while (0); | ||
2375 | |||
2376 | if (ret <= 0) | ||
2377 | free(cache); | ||
2378 | |||
2379 | close(fd); | ||
2380 | return ret; | ||
2381 | } | ||
2382 | |||
2383 | static ssize_t dso_cache_read(struct dso *dso, struct machine *machine, | ||
2384 | u64 offset, u8 *data, ssize_t size) | ||
2385 | { | ||
2386 | struct dso_cache *cache; | ||
2387 | |||
2388 | cache = dso_cache__find(&dso->cache, offset); | ||
2389 | if (cache) | ||
2390 | return dso_cache__memcpy(cache, offset, data, size); | ||
2391 | else | ||
2392 | return dso_cache__read(dso, machine, offset, data, size); | ||
2393 | } | ||
2394 | |||
2395 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
2396 | u64 offset, u8 *data, ssize_t size) | ||
2397 | { | ||
2398 | ssize_t r = 0; | ||
2399 | u8 *p = data; | ||
2400 | |||
2401 | do { | ||
2402 | ssize_t ret; | ||
2403 | |||
2404 | ret = dso_cache_read(dso, machine, offset, p, size); | ||
2405 | if (ret < 0) | ||
2406 | return ret; | ||
2407 | |||
2408 | /* Reached EOF, return what we have. */ | ||
2409 | if (!ret) | ||
2410 | break; | ||
2411 | |||
2412 | BUG_ON(ret > size); | ||
2413 | |||
2414 | r += ret; | ||
2415 | p += ret; | ||
2416 | offset += ret; | ||
2417 | size -= ret; | ||
2418 | |||
2419 | } while (size); | ||
2420 | |||
2421 | return r; | ||
2422 | } | ||
2423 | |||
2424 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | ||
2425 | struct machine *machine, u64 addr, | ||
2426 | u8 *data, ssize_t size) | ||
2427 | { | ||
2428 | u64 offset = map->map_ip(map, addr); | ||
2429 | return dso__data_read_offset(dso, machine, offset, data, size); | ||
2430 | } | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 8b6ef7fac745..b62ca37c4b77 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -11,12 +11,15 @@ | |||
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 | 15 | ||
15 | #ifdef LIBELF_SUPPORT | 16 | #ifdef LIBELF_SUPPORT |
16 | #include <libelf.h> | 17 | #include <libelf.h> |
17 | #include <gelf.h> | 18 | #include <gelf.h> |
18 | #include <elf.h> | ||
19 | #endif | 19 | #endif |
20 | #include <elf.h> | ||
21 | |||
22 | #include "dso.h" | ||
20 | 23 | ||
21 | #ifdef HAVE_CPLUS_DEMANGLE | 24 | #ifdef HAVE_CPLUS_DEMANGLE |
22 | extern char *cplus_demangle(const char *, int); | 25 | extern char *cplus_demangle(const char *, int); |
@@ -39,9 +42,6 @@ static inline char *bfd_demangle(void __maybe_unused *v, | |||
39 | #endif | 42 | #endif |
40 | #endif | 43 | #endif |
41 | 44 | ||
42 | int hex2u64(const char *ptr, u64 *val); | ||
43 | char *strxfrchar(char *s, char from, char to); | ||
44 | |||
45 | /* | 45 | /* |
46 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; | 46 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; |
47 | * for newer versions we can use mmap to reduce memory usage: | 47 | * for newer versions we can use mmap to reduce memory usage: |
@@ -57,8 +57,6 @@ char *strxfrchar(char *s, char from, char to); | |||
57 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | 57 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ |
58 | #endif | 58 | #endif |
59 | 59 | ||
60 | #define BUILD_ID_SIZE 20 | ||
61 | |||
62 | /** struct symbol - symtab entry | 60 | /** struct symbol - symtab entry |
63 | * | 61 | * |
64 | * @ignore - resolvable but tools ignore it (e.g. idle routines) | 62 | * @ignore - resolvable but tools ignore it (e.g. idle routines) |
@@ -74,6 +72,7 @@ struct symbol { | |||
74 | }; | 72 | }; |
75 | 73 | ||
76 | void symbol__delete(struct symbol *sym); | 74 | void symbol__delete(struct symbol *sym); |
75 | void symbols__delete(struct rb_root *symbols); | ||
77 | 76 | ||
78 | static inline size_t symbol__size(const struct symbol *sym) | 77 | static inline size_t symbol__size(const struct symbol *sym) |
79 | { | 78 | { |
@@ -97,7 +96,8 @@ struct symbol_conf { | |||
97 | initialized, | 96 | initialized, |
98 | kptr_restrict, | 97 | kptr_restrict, |
99 | annotate_asm_raw, | 98 | annotate_asm_raw, |
100 | annotate_src; | 99 | annotate_src, |
100 | event_group; | ||
101 | const char *vmlinux_name, | 101 | const char *vmlinux_name, |
102 | *kallsyms_name, | 102 | *kallsyms_name, |
103 | *source_prefix, | 103 | *source_prefix, |
@@ -121,6 +121,8 @@ struct symbol_conf { | |||
121 | }; | 121 | }; |
122 | 122 | ||
123 | extern struct symbol_conf symbol_conf; | 123 | extern struct symbol_conf symbol_conf; |
124 | extern int vmlinux_path__nr_entries; | ||
125 | extern char **vmlinux_path; | ||
124 | 126 | ||
125 | static inline void *symbol__priv(struct symbol *sym) | 127 | static inline void *symbol__priv(struct symbol *sym) |
126 | { | 128 | { |
@@ -164,70 +166,6 @@ struct addr_location { | |||
164 | s32 cpu; | 166 | s32 cpu; |
165 | }; | 167 | }; |
166 | 168 | ||
167 | enum dso_binary_type { | ||
168 | DSO_BINARY_TYPE__KALLSYMS = 0, | ||
169 | DSO_BINARY_TYPE__GUEST_KALLSYMS, | ||
170 | DSO_BINARY_TYPE__VMLINUX, | ||
171 | DSO_BINARY_TYPE__GUEST_VMLINUX, | ||
172 | DSO_BINARY_TYPE__JAVA_JIT, | ||
173 | DSO_BINARY_TYPE__DEBUGLINK, | ||
174 | DSO_BINARY_TYPE__BUILD_ID_CACHE, | ||
175 | DSO_BINARY_TYPE__FEDORA_DEBUGINFO, | ||
176 | DSO_BINARY_TYPE__UBUNTU_DEBUGINFO, | ||
177 | DSO_BINARY_TYPE__BUILDID_DEBUGINFO, | ||
178 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | ||
179 | DSO_BINARY_TYPE__GUEST_KMODULE, | ||
180 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | ||
181 | DSO_BINARY_TYPE__NOT_FOUND, | ||
182 | }; | ||
183 | |||
184 | enum dso_kernel_type { | ||
185 | DSO_TYPE_USER = 0, | ||
186 | DSO_TYPE_KERNEL, | ||
187 | DSO_TYPE_GUEST_KERNEL | ||
188 | }; | ||
189 | |||
190 | enum dso_swap_type { | ||
191 | DSO_SWAP__UNSET, | ||
192 | DSO_SWAP__NO, | ||
193 | DSO_SWAP__YES, | ||
194 | }; | ||
195 | |||
196 | #define DSO__DATA_CACHE_SIZE 4096 | ||
197 | #define DSO__DATA_CACHE_MASK ~(DSO__DATA_CACHE_SIZE - 1) | ||
198 | |||
199 | struct dso_cache { | ||
200 | struct rb_node rb_node; | ||
201 | u64 offset; | ||
202 | u64 size; | ||
203 | char data[0]; | ||
204 | }; | ||
205 | |||
206 | struct dso { | ||
207 | struct list_head node; | ||
208 | struct rb_root symbols[MAP__NR_TYPES]; | ||
209 | struct rb_root symbol_names[MAP__NR_TYPES]; | ||
210 | struct rb_root cache; | ||
211 | enum dso_kernel_type kernel; | ||
212 | enum dso_swap_type needs_swap; | ||
213 | enum dso_binary_type symtab_type; | ||
214 | enum dso_binary_type data_type; | ||
215 | u8 adjust_symbols:1; | ||
216 | u8 has_build_id:1; | ||
217 | u8 hit:1; | ||
218 | u8 annotate_warned:1; | ||
219 | u8 sname_alloc:1; | ||
220 | u8 lname_alloc:1; | ||
221 | u8 sorted_by_name; | ||
222 | u8 loaded; | ||
223 | u8 build_id[BUILD_ID_SIZE]; | ||
224 | const char *short_name; | ||
225 | char *long_name; | ||
226 | u16 long_name_len; | ||
227 | u16 short_name_len; | ||
228 | char name[0]; | ||
229 | }; | ||
230 | |||
231 | struct symsrc { | 169 | struct symsrc { |
232 | char *name; | 170 | char *name; |
233 | int fd; | 171 | int fd; |
@@ -258,47 +196,6 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
258 | bool symsrc__has_symtab(struct symsrc *ss); | 196 | bool symsrc__has_symtab(struct symsrc *ss); |
259 | bool symsrc__possibly_runtime(struct symsrc *ss); | 197 | bool symsrc__possibly_runtime(struct symsrc *ss); |
260 | 198 | ||
261 | #define DSO__SWAP(dso, type, val) \ | ||
262 | ({ \ | ||
263 | type ____r = val; \ | ||
264 | BUG_ON(dso->needs_swap == DSO_SWAP__UNSET); \ | ||
265 | if (dso->needs_swap == DSO_SWAP__YES) { \ | ||
266 | switch (sizeof(____r)) { \ | ||
267 | case 2: \ | ||
268 | ____r = bswap_16(val); \ | ||
269 | break; \ | ||
270 | case 4: \ | ||
271 | ____r = bswap_32(val); \ | ||
272 | break; \ | ||
273 | case 8: \ | ||
274 | ____r = bswap_64(val); \ | ||
275 | break; \ | ||
276 | default: \ | ||
277 | BUG_ON(1); \ | ||
278 | } \ | ||
279 | } \ | ||
280 | ____r; \ | ||
281 | }) | ||
282 | |||
283 | struct dso *dso__new(const char *name); | ||
284 | void dso__delete(struct dso *dso); | ||
285 | |||
286 | int dso__name_len(const struct dso *dso); | ||
287 | |||
288 | bool dso__loaded(const struct dso *dso, enum map_type type); | ||
289 | bool dso__sorted_by_name(const struct dso *dso, enum map_type type); | ||
290 | |||
291 | static inline void dso__set_loaded(struct dso *dso, enum map_type type) | ||
292 | { | ||
293 | dso->loaded |= (1 << type); | ||
294 | } | ||
295 | |||
296 | void dso__sort_by_name(struct dso *dso, enum map_type type); | ||
297 | |||
298 | void dsos__add(struct list_head *head, struct dso *dso); | ||
299 | struct dso *dsos__find(struct list_head *head, const char *name); | ||
300 | struct dso *__dsos__findnew(struct list_head *head, const char *name); | ||
301 | |||
302 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter); | 199 | int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter); |
303 | int dso__load_vmlinux(struct dso *dso, struct map *map, | 200 | int dso__load_vmlinux(struct dso *dso, struct map *map, |
304 | const char *vmlinux, symbol_filter_t filter); | 201 | const char *vmlinux, symbol_filter_t filter); |
@@ -306,30 +203,7 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, | |||
306 | symbol_filter_t filter); | 203 | symbol_filter_t filter); |
307 | int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, | 204 | int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, |
308 | symbol_filter_t filter); | 205 | symbol_filter_t filter); |
309 | int machine__load_kallsyms(struct machine *machine, const char *filename, | 206 | |
310 | enum map_type type, symbol_filter_t filter); | ||
311 | int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | ||
312 | symbol_filter_t filter); | ||
313 | |||
314 | size_t __dsos__fprintf(struct list_head *head, FILE *fp); | ||
315 | |||
316 | size_t machine__fprintf_dsos_buildid(struct machine *machine, | ||
317 | FILE *fp, bool with_hits); | ||
318 | size_t machines__fprintf_dsos(struct rb_root *machines, FILE *fp); | ||
319 | size_t machines__fprintf_dsos_buildid(struct rb_root *machines, | ||
320 | FILE *fp, bool with_hits); | ||
321 | size_t dso__fprintf_buildid(struct dso *dso, FILE *fp); | ||
322 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | ||
323 | enum map_type type, FILE *fp); | ||
324 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); | ||
325 | |||
326 | char dso__symtab_origin(const struct dso *dso); | ||
327 | void dso__set_long_name(struct dso *dso, char *name); | ||
328 | void dso__set_build_id(struct dso *dso, void *build_id); | ||
329 | bool dso__build_id_equal(const struct dso *dso, u8 *build_id); | ||
330 | void dso__read_running_kernel_build_id(struct dso *dso, | ||
331 | struct machine *machine); | ||
332 | struct map *dso__new_map(const char *name); | ||
333 | struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, | 207 | struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, |
334 | u64 addr); | 208 | u64 addr); |
335 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | 209 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, |
@@ -337,22 +211,12 @@ struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | |||
337 | 211 | ||
338 | int filename__read_build_id(const char *filename, void *bf, size_t size); | 212 | int filename__read_build_id(const char *filename, void *bf, size_t size); |
339 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | 213 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); |
340 | bool __dsos__read_build_ids(struct list_head *head, bool with_hits); | ||
341 | int build_id__sprintf(const u8 *build_id, int len, char *bf); | ||
342 | int kallsyms__parse(const char *filename, void *arg, | 214 | int kallsyms__parse(const char *filename, void *arg, |
343 | int (*process_symbol)(void *arg, const char *name, | 215 | int (*process_symbol)(void *arg, const char *name, |
344 | char type, u64 start)); | 216 | char type, u64 start)); |
345 | int filename__read_debuglink(const char *filename, char *debuglink, | 217 | int filename__read_debuglink(const char *filename, char *debuglink, |
346 | size_t size); | 218 | size_t size); |
347 | 219 | ||
348 | void machine__destroy_kernel_maps(struct machine *machine); | ||
349 | int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel); | ||
350 | int machine__create_kernel_maps(struct machine *machine); | ||
351 | |||
352 | int machines__create_kernel_maps(struct rb_root *machines, pid_t pid); | ||
353 | int machines__create_guest_kernel_maps(struct rb_root *machines); | ||
354 | void machines__destroy_guest_kernel_maps(struct rb_root *machines); | ||
355 | |||
356 | int symbol__init(void); | 220 | int symbol__init(void); |
357 | void symbol__exit(void); | 221 | void symbol__exit(void); |
358 | void symbol__elf_init(void); | 222 | void symbol__elf_init(void); |
@@ -360,20 +224,11 @@ struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); | |||
360 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | 224 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, |
361 | const struct addr_location *al, FILE *fp); | 225 | const struct addr_location *al, FILE *fp); |
362 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); | 226 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); |
227 | size_t symbol__fprintf(struct symbol *sym, FILE *fp); | ||
363 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 228 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
229 | bool symbol__restricted_filename(const char *filename, | ||
230 | const char *restricted_filename); | ||
364 | 231 | ||
365 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); | ||
366 | |||
367 | int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | ||
368 | char *root_dir, char *file, size_t size); | ||
369 | |||
370 | int dso__data_fd(struct dso *dso, struct machine *machine); | ||
371 | ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, | ||
372 | u64 offset, u8 *data, ssize_t size); | ||
373 | ssize_t dso__data_read_addr(struct dso *dso, struct map *map, | ||
374 | struct machine *machine, u64 addr, | ||
375 | u8 *data, ssize_t size); | ||
376 | int dso__test_data(void); | ||
377 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, | 232 | int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, |
378 | struct symsrc *runtime_ss, symbol_filter_t filter, | 233 | struct symsrc *runtime_ss, symbol_filter_t filter, |
379 | int kmodule); | 234 | int kmodule); |
diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c index 48c6902e749f..f71e9eafe15a 100644 --- a/tools/perf/util/sysfs.c +++ b/tools/perf/util/sysfs.c | |||
@@ -8,7 +8,7 @@ static const char * const sysfs_known_mountpoints[] = { | |||
8 | }; | 8 | }; |
9 | 9 | ||
10 | static int sysfs_found; | 10 | static int sysfs_found; |
11 | char sysfs_mountpoint[PATH_MAX]; | 11 | char sysfs_mountpoint[PATH_MAX + 1]; |
12 | 12 | ||
13 | static int sysfs_valid_mountpoint(const char *sysfs) | 13 | static int sysfs_valid_mountpoint(const char *sysfs) |
14 | { | 14 | { |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 8b3e5939afb6..632e40e5ceca 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -7,7 +7,7 @@ | |||
7 | #include "util.h" | 7 | #include "util.h" |
8 | #include "debug.h" | 8 | #include "debug.h" |
9 | 9 | ||
10 | static struct thread *thread__new(pid_t pid) | 10 | struct thread *thread__new(pid_t pid) |
11 | { | 11 | { |
12 | struct thread *self = zalloc(sizeof(*self)); | 12 | struct thread *self = zalloc(sizeof(*self)); |
13 | 13 | ||
@@ -54,49 +54,10 @@ int thread__comm_len(struct thread *self) | |||
54 | return self->comm_len; | 54 | return self->comm_len; |
55 | } | 55 | } |
56 | 56 | ||
57 | static size_t thread__fprintf(struct thread *self, FILE *fp) | 57 | size_t thread__fprintf(struct thread *thread, FILE *fp) |
58 | { | 58 | { |
59 | return fprintf(fp, "Thread %d %s\n", self->pid, self->comm) + | 59 | return fprintf(fp, "Thread %d %s\n", thread->pid, thread->comm) + |
60 | map_groups__fprintf(&self->mg, verbose, fp); | 60 | map_groups__fprintf(&thread->mg, verbose, fp); |
61 | } | ||
62 | |||
63 | struct thread *machine__findnew_thread(struct machine *self, pid_t pid) | ||
64 | { | ||
65 | struct rb_node **p = &self->threads.rb_node; | ||
66 | struct rb_node *parent = NULL; | ||
67 | struct thread *th; | ||
68 | |||
69 | /* | ||
70 | * Font-end cache - PID lookups come in blocks, | ||
71 | * so most of the time we dont have to look up | ||
72 | * the full rbtree: | ||
73 | */ | ||
74 | if (self->last_match && self->last_match->pid == pid) | ||
75 | return self->last_match; | ||
76 | |||
77 | while (*p != NULL) { | ||
78 | parent = *p; | ||
79 | th = rb_entry(parent, struct thread, rb_node); | ||
80 | |||
81 | if (th->pid == pid) { | ||
82 | self->last_match = th; | ||
83 | return th; | ||
84 | } | ||
85 | |||
86 | if (pid < th->pid) | ||
87 | p = &(*p)->rb_left; | ||
88 | else | ||
89 | p = &(*p)->rb_right; | ||
90 | } | ||
91 | |||
92 | th = thread__new(pid); | ||
93 | if (th != NULL) { | ||
94 | rb_link_node(&th->rb_node, parent, p); | ||
95 | rb_insert_color(&th->rb_node, &self->threads); | ||
96 | self->last_match = th; | ||
97 | } | ||
98 | |||
99 | return th; | ||
100 | } | 61 | } |
101 | 62 | ||
102 | void thread__insert_map(struct thread *self, struct map *map) | 63 | void thread__insert_map(struct thread *self, struct map *map) |
@@ -123,17 +84,3 @@ int thread__fork(struct thread *self, struct thread *parent) | |||
123 | return -ENOMEM; | 84 | return -ENOMEM; |
124 | return 0; | 85 | return 0; |
125 | } | 86 | } |
126 | |||
127 | size_t machine__fprintf(struct machine *machine, FILE *fp) | ||
128 | { | ||
129 | size_t ret = 0; | ||
130 | struct rb_node *nd; | ||
131 | |||
132 | for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) { | ||
133 | struct thread *pos = rb_entry(nd, struct thread, rb_node); | ||
134 | |||
135 | ret += thread__fprintf(pos, fp); | ||
136 | } | ||
137 | |||
138 | return ret; | ||
139 | } | ||
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index f66610b7bacf..5ad266403098 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <linux/rbtree.h> | 4 | #include <linux/rbtree.h> |
5 | #include <unistd.h> | 5 | #include <unistd.h> |
6 | #include <sys/types.h> | ||
6 | #include "symbol.h" | 7 | #include "symbol.h" |
7 | 8 | ||
8 | struct thread { | 9 | struct thread { |
@@ -22,12 +23,14 @@ struct thread { | |||
22 | 23 | ||
23 | struct machine; | 24 | struct machine; |
24 | 25 | ||
26 | struct thread *thread__new(pid_t pid); | ||
25 | void thread__delete(struct thread *self); | 27 | void thread__delete(struct thread *self); |
26 | 28 | ||
27 | int thread__set_comm(struct thread *self, const char *comm); | 29 | int thread__set_comm(struct thread *self, const char *comm); |
28 | int thread__comm_len(struct thread *self); | 30 | int thread__comm_len(struct thread *self); |
29 | void thread__insert_map(struct thread *self, struct map *map); | 31 | void thread__insert_map(struct thread *self, struct map *map); |
30 | int thread__fork(struct thread *self, struct thread *parent); | 32 | int thread__fork(struct thread *self, struct thread *parent); |
33 | size_t thread__fprintf(struct thread *thread, FILE *fp); | ||
31 | 34 | ||
32 | static inline struct map *thread__find_map(struct thread *self, | 35 | static inline struct map *thread__find_map(struct thread *self, |
33 | enum map_type type, u64 addr) | 36 | enum map_type type, u64 addr) |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 884dde9b9bc1..54d37a4753c5 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -26,6 +26,8 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
26 | float samples_per_sec = top->samples / top->delay_secs; | 26 | float samples_per_sec = top->samples / top->delay_secs; |
27 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; | 27 | float ksamples_per_sec = top->kernel_samples / top->delay_secs; |
28 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; | 28 | float esamples_percent = (100.0 * top->exact_samples) / top->samples; |
29 | struct perf_record_opts *opts = &top->record_opts; | ||
30 | struct perf_target *target = &opts->target; | ||
29 | size_t ret = 0; | 31 | size_t ret = 0; |
30 | 32 | ||
31 | if (!perf_guest) { | 33 | if (!perf_guest) { |
@@ -61,31 +63,31 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
61 | struct perf_evsel *first = perf_evlist__first(top->evlist); | 63 | struct perf_evsel *first = perf_evlist__first(top->evlist); |
62 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", | 64 | ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", |
63 | (uint64_t)first->attr.sample_period, | 65 | (uint64_t)first->attr.sample_period, |
64 | top->freq ? "Hz" : ""); | 66 | opts->freq ? "Hz" : ""); |
65 | } | 67 | } |
66 | 68 | ||
67 | ret += SNPRINTF(bf + ret, size - ret, "%s", perf_evsel__name(top->sym_evsel)); | 69 | ret += SNPRINTF(bf + ret, size - ret, "%s", perf_evsel__name(top->sym_evsel)); |
68 | 70 | ||
69 | ret += SNPRINTF(bf + ret, size - ret, "], "); | 71 | ret += SNPRINTF(bf + ret, size - ret, "], "); |
70 | 72 | ||
71 | if (top->target.pid) | 73 | if (target->pid) |
72 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s", | 74 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s", |
73 | top->target.pid); | 75 | target->pid); |
74 | else if (top->target.tid) | 76 | else if (target->tid) |
75 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", | 77 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", |
76 | top->target.tid); | 78 | target->tid); |
77 | else if (top->target.uid_str != NULL) | 79 | else if (target->uid_str != NULL) |
78 | ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", | 80 | ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", |
79 | top->target.uid_str); | 81 | target->uid_str); |
80 | else | 82 | else |
81 | ret += SNPRINTF(bf + ret, size - ret, " (all"); | 83 | ret += SNPRINTF(bf + ret, size - ret, " (all"); |
82 | 84 | ||
83 | if (top->target.cpu_list) | 85 | if (target->cpu_list) |
84 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", | 86 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", |
85 | top->evlist->cpus->nr > 1 ? "s" : "", | 87 | top->evlist->cpus->nr > 1 ? "s" : "", |
86 | top->target.cpu_list); | 88 | target->cpu_list); |
87 | else { | 89 | else { |
88 | if (top->target.tid) | 90 | if (target->tid) |
89 | ret += SNPRINTF(bf + ret, size - ret, ")"); | 91 | ret += SNPRINTF(bf + ret, size - ret, ")"); |
90 | else | 92 | else |
91 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", | 93 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", |
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index 86ff1b15059b..7ebf357dc9e1 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -14,7 +14,7 @@ struct perf_session; | |||
14 | struct perf_top { | 14 | struct perf_top { |
15 | struct perf_tool tool; | 15 | struct perf_tool tool; |
16 | struct perf_evlist *evlist; | 16 | struct perf_evlist *evlist; |
17 | struct perf_target target; | 17 | struct perf_record_opts record_opts; |
18 | /* | 18 | /* |
19 | * Symbols will be added here in perf_event__process_sample and will | 19 | * Symbols will be added here in perf_event__process_sample and will |
20 | * get out after decayed. | 20 | * get out after decayed. |
@@ -24,24 +24,16 @@ struct perf_top { | |||
24 | u64 exact_samples; | 24 | u64 exact_samples; |
25 | u64 guest_us_samples, guest_kernel_samples; | 25 | u64 guest_us_samples, guest_kernel_samples; |
26 | int print_entries, count_filter, delay_secs; | 26 | int print_entries, count_filter, delay_secs; |
27 | int freq; | ||
28 | bool hide_kernel_symbols, hide_user_symbols, zero; | 27 | bool hide_kernel_symbols, hide_user_symbols, zero; |
29 | bool use_tui, use_stdio; | 28 | bool use_tui, use_stdio; |
30 | bool sort_has_symbols; | 29 | bool sort_has_symbols; |
31 | bool dont_use_callchains; | ||
32 | bool kptr_restrict_warned; | 30 | bool kptr_restrict_warned; |
33 | bool vmlinux_warned; | 31 | bool vmlinux_warned; |
34 | bool inherit; | ||
35 | bool group; | ||
36 | bool sample_id_all_missing; | ||
37 | bool exclude_guest_missing; | ||
38 | bool dump_symtab; | 32 | bool dump_symtab; |
39 | struct hist_entry *sym_filter_entry; | 33 | struct hist_entry *sym_filter_entry; |
40 | struct perf_evsel *sym_evsel; | 34 | struct perf_evsel *sym_evsel; |
41 | struct perf_session *session; | 35 | struct perf_session *session; |
42 | struct winsize winsize; | 36 | struct winsize winsize; |
43 | unsigned int mmap_pages; | ||
44 | int default_interval; | ||
45 | int realtime_prio; | 37 | int realtime_prio; |
46 | int sym_pcnt_filter; | 38 | int sym_pcnt_filter; |
47 | const char *sym_filter; | 39 | const char *sym_filter; |
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index 719ed74a8565..3741572696af 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -47,8 +47,6 @@ int file_bigendian; | |||
47 | int host_bigendian; | 47 | int host_bigendian; |
48 | static int long_size; | 48 | static int long_size; |
49 | 49 | ||
50 | static unsigned long page_size; | ||
51 | |||
52 | static ssize_t calc_data_size; | 50 | static ssize_t calc_data_size; |
53 | static bool repipe; | 51 | static bool repipe; |
54 | 52 | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 99664598bc1a..805d1f52c5b4 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -10,6 +10,10 @@ | |||
10 | /* | 10 | /* |
11 | * XXX We need to find a better place for these things... | 11 | * XXX We need to find a better place for these things... |
12 | */ | 12 | */ |
13 | unsigned int page_size; | ||
14 | |||
15 | bool test_attr__enabled; | ||
16 | |||
13 | bool perf_host = true; | 17 | bool perf_host = true; |
14 | bool perf_guest = false; | 18 | bool perf_guest = false; |
15 | 19 | ||
@@ -164,6 +168,39 @@ size_t hex_width(u64 v) | |||
164 | return n; | 168 | return n; |
165 | } | 169 | } |
166 | 170 | ||
171 | static int hex(char ch) | ||
172 | { | ||
173 | if ((ch >= '0') && (ch <= '9')) | ||
174 | return ch - '0'; | ||
175 | if ((ch >= 'a') && (ch <= 'f')) | ||
176 | return ch - 'a' + 10; | ||
177 | if ((ch >= 'A') && (ch <= 'F')) | ||
178 | return ch - 'A' + 10; | ||
179 | return -1; | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * While we find nice hex chars, build a long_val. | ||
184 | * Return number of chars processed. | ||
185 | */ | ||
186 | int hex2u64(const char *ptr, u64 *long_val) | ||
187 | { | ||
188 | const char *p = ptr; | ||
189 | *long_val = 0; | ||
190 | |||
191 | while (*p) { | ||
192 | const int hex_val = hex(*p); | ||
193 | |||
194 | if (hex_val < 0) | ||
195 | break; | ||
196 | |||
197 | *long_val = (*long_val << 4) | hex_val; | ||
198 | p++; | ||
199 | } | ||
200 | |||
201 | return p - ptr; | ||
202 | } | ||
203 | |||
167 | /* Obtain a backtrace and print it to stdout. */ | 204 | /* Obtain a backtrace and print it to stdout. */ |
168 | #ifdef BACKTRACE_SUPPORT | 205 | #ifdef BACKTRACE_SUPPORT |
169 | void dump_stack(void) | 206 | void dump_stack(void) |
@@ -183,3 +220,25 @@ void dump_stack(void) | |||
183 | #else | 220 | #else |
184 | void dump_stack(void) {} | 221 | void dump_stack(void) {} |
185 | #endif | 222 | #endif |
223 | |||
224 | void get_term_dimensions(struct winsize *ws) | ||
225 | { | ||
226 | char *s = getenv("LINES"); | ||
227 | |||
228 | if (s != NULL) { | ||
229 | ws->ws_row = atoi(s); | ||
230 | s = getenv("COLUMNS"); | ||
231 | if (s != NULL) { | ||
232 | ws->ws_col = atoi(s); | ||
233 | if (ws->ws_row && ws->ws_col) | ||
234 | return; | ||
235 | } | ||
236 | } | ||
237 | #ifdef TIOCGWINSZ | ||
238 | if (ioctl(1, TIOCGWINSZ, ws) == 0 && | ||
239 | ws->ws_row && ws->ws_col) | ||
240 | return; | ||
241 | #endif | ||
242 | ws->ws_row = 25; | ||
243 | ws->ws_col = 80; | ||
244 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 70fa70b535b2..09b4c26b71aa 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -198,6 +198,10 @@ static inline int has_extension(const char *filename, const char *ext) | |||
198 | #undef tolower | 198 | #undef tolower |
199 | #undef toupper | 199 | #undef toupper |
200 | 200 | ||
201 | #ifndef NSEC_PER_MSEC | ||
202 | #define NSEC_PER_MSEC 1000000L | ||
203 | #endif | ||
204 | |||
201 | extern unsigned char sane_ctype[256]; | 205 | extern unsigned char sane_ctype[256]; |
202 | #define GIT_SPACE 0x01 | 206 | #define GIT_SPACE 0x01 |
203 | #define GIT_DIGIT 0x02 | 207 | #define GIT_DIGIT 0x02 |
@@ -236,6 +240,7 @@ void argv_free(char **argv); | |||
236 | bool strglobmatch(const char *str, const char *pat); | 240 | bool strglobmatch(const char *str, const char *pat); |
237 | bool strlazymatch(const char *str, const char *pat); | 241 | bool strlazymatch(const char *str, const char *pat); |
238 | int strtailcmp(const char *s1, const char *s2); | 242 | int strtailcmp(const char *s1, const char *s2); |
243 | char *strxfrchar(char *s, char from, char to); | ||
239 | unsigned long convert_unit(unsigned long value, char *unit); | 244 | unsigned long convert_unit(unsigned long value, char *unit); |
240 | int readn(int fd, void *buf, size_t size); | 245 | int readn(int fd, void *buf, size_t size); |
241 | 246 | ||
@@ -258,9 +263,16 @@ bool is_power_of_2(unsigned long n) | |||
258 | } | 263 | } |
259 | 264 | ||
260 | size_t hex_width(u64 v); | 265 | size_t hex_width(u64 v); |
266 | int hex2u64(const char *ptr, u64 *val); | ||
261 | 267 | ||
268 | char *ltrim(char *s); | ||
262 | char *rtrim(char *s); | 269 | char *rtrim(char *s); |
263 | 270 | ||
264 | void dump_stack(void); | 271 | void dump_stack(void); |
265 | 272 | ||
273 | extern unsigned int page_size; | ||
274 | |||
275 | struct winsize; | ||
276 | void get_term_dimensions(struct winsize *ws); | ||
277 | |||
266 | #endif | 278 | #endif |
diff --git a/tools/power/acpi/Makefile b/tools/power/acpi/Makefile index 6b9cf7a987c7..bafeb8d662a3 100644 --- a/tools/power/acpi/Makefile +++ b/tools/power/acpi/Makefile | |||
@@ -13,6 +13,6 @@ clean : | |||
13 | rm -f $(CLEANFILES) $(patsubst %.c,%.o, $(SRCS)) *~ | 13 | rm -f $(CLEANFILES) $(patsubst %.c,%.o, $(SRCS)) *~ |
14 | 14 | ||
15 | install : | 15 | install : |
16 | install acpidump /usr/bin/acpidump | 16 | install acpidump /usr/sbin/acpidump |
17 | install acpidump.8 /usr/share/man/man8 | 17 | install acpidump.8 /usr/share/man/man8 |
18 | 18 | ||
diff --git a/tools/power/cpupower/.gitignore b/tools/power/cpupower/.gitignore index 8a83dd2ffc11..d42073f12609 100644 --- a/tools/power/cpupower/.gitignore +++ b/tools/power/cpupower/.gitignore | |||
@@ -20,3 +20,10 @@ utils/cpufreq-set.o | |||
20 | utils/cpufreq-aperf.o | 20 | utils/cpufreq-aperf.o |
21 | cpupower | 21 | cpupower |
22 | bench/cpufreq-bench | 22 | bench/cpufreq-bench |
23 | debug/kernel/Module.symvers | ||
24 | debug/i386/centrino-decode | ||
25 | debug/i386/dump_psb | ||
26 | debug/i386/intel_gsic | ||
27 | debug/i386/powernow-k8-decode | ||
28 | debug/x86_64/centrino-decode | ||
29 | debug/x86_64/powernow-k8-decode | ||
diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index cf397bd26d0c..d875a74a3bdf 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile | |||
@@ -253,7 +253,8 @@ clean: | |||
253 | | xargs rm -f | 253 | | xargs rm -f |
254 | -rm -f $(OUTPUT)cpupower | 254 | -rm -f $(OUTPUT)cpupower |
255 | -rm -f $(OUTPUT)libcpupower.so* | 255 | -rm -f $(OUTPUT)libcpupower.so* |
256 | -rm -rf $(OUTPUT)po/*.{gmo,pot} | 256 | -rm -rf $(OUTPUT)po/*.gmo |
257 | -rm -rf $(OUTPUT)po/*.pot | ||
257 | $(MAKE) -C bench O=$(OUTPUT) clean | 258 | $(MAKE) -C bench O=$(OUTPUT) clean |
258 | 259 | ||
259 | 260 | ||
diff --git a/tools/power/cpupower/debug/i386/Makefile b/tools/power/cpupower/debug/i386/Makefile index 3ba158f0e287..c05cc0ac80c7 100644 --- a/tools/power/cpupower/debug/i386/Makefile +++ b/tools/power/cpupower/debug/i386/Makefile | |||
@@ -26,7 +26,10 @@ $(OUTPUT)powernow-k8-decode: powernow-k8-decode.c | |||
26 | all: $(OUTPUT)centrino-decode $(OUTPUT)dump_psb $(OUTPUT)intel_gsic $(OUTPUT)powernow-k8-decode | 26 | all: $(OUTPUT)centrino-decode $(OUTPUT)dump_psb $(OUTPUT)intel_gsic $(OUTPUT)powernow-k8-decode |
27 | 27 | ||
28 | clean: | 28 | clean: |
29 | rm -rf $(OUTPUT){centrino-decode,dump_psb,intel_gsic,powernow-k8-decode} | 29 | rm -rf $(OUTPUT)centrino-decode |
30 | rm -rf $(OUTPUT)dump_psb | ||
31 | rm -rf $(OUTPUT)intel_gsic | ||
32 | rm -rf $(OUTPUT)powernow-k8-decode | ||
30 | 33 | ||
31 | install: | 34 | install: |
32 | $(INSTALL) -d $(DESTDIR)${bindir} | 35 | $(INSTALL) -d $(DESTDIR)${bindir} |
diff --git a/tools/power/cpupower/man/cpupower-monitor.1 b/tools/power/cpupower/man/cpupower-monitor.1 index 1141c2073719..e01c35d13b6e 100644 --- a/tools/power/cpupower/man/cpupower-monitor.1 +++ b/tools/power/cpupower/man/cpupower-monitor.1 | |||
@@ -7,11 +7,11 @@ cpupower\-monitor \- Report processor frequency and idle statistics | |||
7 | .RB "\-l" | 7 | .RB "\-l" |
8 | 8 | ||
9 | .B cpupower monitor | 9 | .B cpupower monitor |
10 | .RB [ "\-m <mon1>," [ "<mon2>,..." ] ] | 10 | .RB [ -c ] [ "\-m <mon1>," [ "<mon2>,..." ] ] |
11 | .RB [ "\-i seconds" ] | 11 | .RB [ "\-i seconds" ] |
12 | .br | 12 | .br |
13 | .B cpupower monitor | 13 | .B cpupower monitor |
14 | .RB [ "\-m <mon1>," [ "<mon2>,..." ] ] | 14 | .RB [ -c ][ "\-m <mon1>," [ "<mon2>,..." ] ] |
15 | .RB command | 15 | .RB command |
16 | .br | 16 | .br |
17 | .SH DESCRIPTION | 17 | .SH DESCRIPTION |
@@ -64,6 +64,17 @@ Only display specific monitors. Use the monitor string(s) provided by \-l option | |||
64 | Measure intervall. | 64 | Measure intervall. |
65 | .RE | 65 | .RE |
66 | .PP | 66 | .PP |
67 | \-c | ||
68 | .RS 4 | ||
69 | Schedule the process on every core before starting and ending measuring. | ||
70 | This could be needed for the Idle_Stats monitor when no other MSR based | ||
71 | monitor (has to be run on the core that is measured) is run in parallel. | ||
72 | This is to wake up the processors from deeper sleep states and let the | ||
73 | kernel re | ||
74 | -account its cpuidle (C-state) information before reading the | ||
75 | cpuidle timings from sysfs. | ||
76 | .RE | ||
77 | .PP | ||
67 | command | 78 | command |
68 | .RS 4 | 79 | .RS 4 |
69 | Measure idle and frequency characteristics of an arbitrary command/workload. | 80 | Measure idle and frequency characteristics of an arbitrary command/workload. |
diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c index 906895d21cce..93b0aa74ca03 100644 --- a/tools/power/cpupower/utils/helpers/cpuid.c +++ b/tools/power/cpupower/utils/helpers/cpuid.c | |||
@@ -158,6 +158,8 @@ out: | |||
158 | cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; | 158 | cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; |
159 | case 0x2A: /* SNB */ | 159 | case 0x2A: /* SNB */ |
160 | case 0x2D: /* SNB Xeon */ | 160 | case 0x2D: /* SNB Xeon */ |
161 | case 0x3A: /* IVB */ | ||
162 | case 0x3E: /* IVB Xeon */ | ||
161 | cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; | 163 | cpu_info->caps |= CPUPOWER_CAP_HAS_TURBO_RATIO; |
162 | cpu_info->caps |= CPUPOWER_CAP_IS_SNB; | 164 | cpu_info->caps |= CPUPOWER_CAP_IS_SNB; |
163 | break; | 165 | break; |
diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 2eb584cf2f55..aa9e95486a2d 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h | |||
@@ -92,6 +92,14 @@ extern int get_cpu_info(unsigned int cpu, struct cpupower_cpu_info *cpu_info); | |||
92 | extern struct cpupower_cpu_info cpupower_cpu_info; | 92 | extern struct cpupower_cpu_info cpupower_cpu_info; |
93 | /* cpuid and cpuinfo helpers **************************/ | 93 | /* cpuid and cpuinfo helpers **************************/ |
94 | 94 | ||
95 | struct cpuid_core_info { | ||
96 | int pkg; | ||
97 | int core; | ||
98 | int cpu; | ||
99 | |||
100 | /* flags */ | ||
101 | unsigned int is_online:1; | ||
102 | }; | ||
95 | 103 | ||
96 | /* CPU topology/hierarchy parsing ******************/ | 104 | /* CPU topology/hierarchy parsing ******************/ |
97 | struct cpupower_topology { | 105 | struct cpupower_topology { |
@@ -101,18 +109,12 @@ struct cpupower_topology { | |||
101 | unsigned int threads; /* per core */ | 109 | unsigned int threads; /* per core */ |
102 | 110 | ||
103 | /* Array gets mallocated with cores entries, holding per core info */ | 111 | /* Array gets mallocated with cores entries, holding per core info */ |
104 | struct { | 112 | struct cpuid_core_info *core_info; |
105 | int pkg; | ||
106 | int core; | ||
107 | int cpu; | ||
108 | |||
109 | /* flags */ | ||
110 | unsigned int is_online:1; | ||
111 | } *core_info; | ||
112 | }; | 113 | }; |
113 | 114 | ||
114 | extern int get_cpu_topology(struct cpupower_topology *cpu_top); | 115 | extern int get_cpu_topology(struct cpupower_topology *cpu_top); |
115 | extern void cpu_topology_release(struct cpupower_topology cpu_top); | 116 | extern void cpu_topology_release(struct cpupower_topology cpu_top); |
117 | |||
116 | /* CPU topology/hierarchy parsing ******************/ | 118 | /* CPU topology/hierarchy parsing ******************/ |
117 | 119 | ||
118 | /* X86 ONLY ****************************************/ | 120 | /* X86 ONLY ****************************************/ |
diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c index 96e28c124b5c..38ab91629463 100644 --- a/tools/power/cpupower/utils/helpers/sysfs.c +++ b/tools/power/cpupower/utils/helpers/sysfs.c | |||
@@ -37,25 +37,6 @@ unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) | |||
37 | return (unsigned int) numread; | 37 | return (unsigned int) numread; |
38 | } | 38 | } |
39 | 39 | ||
40 | static unsigned int sysfs_write_file(const char *path, | ||
41 | const char *value, size_t len) | ||
42 | { | ||
43 | int fd; | ||
44 | ssize_t numwrite; | ||
45 | |||
46 | fd = open(path, O_WRONLY); | ||
47 | if (fd == -1) | ||
48 | return 0; | ||
49 | |||
50 | numwrite = write(fd, value, len); | ||
51 | if (numwrite < 1) { | ||
52 | close(fd); | ||
53 | return 0; | ||
54 | } | ||
55 | close(fd); | ||
56 | return (unsigned int) numwrite; | ||
57 | } | ||
58 | |||
59 | /* | 40 | /* |
60 | * Detect whether a CPU is online | 41 | * Detect whether a CPU is online |
61 | * | 42 | * |
diff --git a/tools/power/cpupower/utils/helpers/topology.c b/tools/power/cpupower/utils/helpers/topology.c index 4eae2c47ba48..c13120af519b 100644 --- a/tools/power/cpupower/utils/helpers/topology.c +++ b/tools/power/cpupower/utils/helpers/topology.c | |||
@@ -20,9 +20,8 @@ | |||
20 | #include <helpers/sysfs.h> | 20 | #include <helpers/sysfs.h> |
21 | 21 | ||
22 | /* returns -1 on failure, 0 on success */ | 22 | /* returns -1 on failure, 0 on success */ |
23 | int sysfs_topology_read_file(unsigned int cpu, const char *fname) | 23 | static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result) |
24 | { | 24 | { |
25 | unsigned long value; | ||
26 | char linebuf[MAX_LINE_LEN]; | 25 | char linebuf[MAX_LINE_LEN]; |
27 | char *endp; | 26 | char *endp; |
28 | char path[SYSFS_PATH_MAX]; | 27 | char path[SYSFS_PATH_MAX]; |
@@ -31,20 +30,12 @@ int sysfs_topology_read_file(unsigned int cpu, const char *fname) | |||
31 | cpu, fname); | 30 | cpu, fname); |
32 | if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0) | 31 | if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0) |
33 | return -1; | 32 | return -1; |
34 | value = strtoul(linebuf, &endp, 0); | 33 | *result = strtol(linebuf, &endp, 0); |
35 | if (endp == linebuf || errno == ERANGE) | 34 | if (endp == linebuf || errno == ERANGE) |
36 | return -1; | 35 | return -1; |
37 | return value; | 36 | return 0; |
38 | } | 37 | } |
39 | 38 | ||
40 | struct cpuid_core_info { | ||
41 | unsigned int pkg; | ||
42 | unsigned int thread; | ||
43 | unsigned int cpu; | ||
44 | /* flags */ | ||
45 | unsigned int is_online:1; | ||
46 | }; | ||
47 | |||
48 | static int __compare(const void *t1, const void *t2) | 39 | static int __compare(const void *t1, const void *t2) |
49 | { | 40 | { |
50 | struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1; | 41 | struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1; |
@@ -53,9 +44,9 @@ static int __compare(const void *t1, const void *t2) | |||
53 | return -1; | 44 | return -1; |
54 | else if (top1->pkg > top2->pkg) | 45 | else if (top1->pkg > top2->pkg) |
55 | return 1; | 46 | return 1; |
56 | else if (top1->thread < top2->thread) | 47 | else if (top1->core < top2->core) |
57 | return -1; | 48 | return -1; |
58 | else if (top1->thread > top2->thread) | 49 | else if (top1->core > top2->core) |
59 | return 1; | 50 | return 1; |
60 | else if (top1->cpu < top2->cpu) | 51 | else if (top1->cpu < top2->cpu) |
61 | return -1; | 52 | return -1; |
@@ -73,28 +64,42 @@ static int __compare(const void *t1, const void *t2) | |||
73 | */ | 64 | */ |
74 | int get_cpu_topology(struct cpupower_topology *cpu_top) | 65 | int get_cpu_topology(struct cpupower_topology *cpu_top) |
75 | { | 66 | { |
76 | int cpu, cpus = sysconf(_SC_NPROCESSORS_CONF); | 67 | int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF); |
77 | 68 | ||
78 | cpu_top->core_info = malloc(sizeof(struct cpupower_topology) * cpus); | 69 | cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus); |
79 | if (cpu_top->core_info == NULL) | 70 | if (cpu_top->core_info == NULL) |
80 | return -ENOMEM; | 71 | return -ENOMEM; |
81 | cpu_top->pkgs = cpu_top->cores = 0; | 72 | cpu_top->pkgs = cpu_top->cores = 0; |
82 | for (cpu = 0; cpu < cpus; cpu++) { | 73 | for (cpu = 0; cpu < cpus; cpu++) { |
83 | cpu_top->core_info[cpu].cpu = cpu; | 74 | cpu_top->core_info[cpu].cpu = cpu; |
84 | cpu_top->core_info[cpu].is_online = sysfs_is_cpu_online(cpu); | 75 | cpu_top->core_info[cpu].is_online = sysfs_is_cpu_online(cpu); |
85 | cpu_top->core_info[cpu].pkg = | 76 | if(sysfs_topology_read_file( |
86 | sysfs_topology_read_file(cpu, "physical_package_id"); | 77 | cpu, |
87 | if ((int)cpu_top->core_info[cpu].pkg != -1 && | 78 | "physical_package_id", |
88 | cpu_top->core_info[cpu].pkg > cpu_top->pkgs) | 79 | &(cpu_top->core_info[cpu].pkg)) < 0) |
89 | cpu_top->pkgs = cpu_top->core_info[cpu].pkg; | 80 | return -1; |
90 | cpu_top->core_info[cpu].core = | 81 | if(sysfs_topology_read_file( |
91 | sysfs_topology_read_file(cpu, "core_id"); | 82 | cpu, |
83 | "core_id", | ||
84 | &(cpu_top->core_info[cpu].core)) < 0) | ||
85 | return -1; | ||
92 | } | 86 | } |
93 | cpu_top->pkgs++; | ||
94 | 87 | ||
95 | qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), | 88 | qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), |
96 | __compare); | 89 | __compare); |
97 | 90 | ||
91 | /* Count the number of distinct pkgs values. This works | ||
92 | because the primary sort of the core_info struct was just | ||
93 | done by pkg value. */ | ||
94 | last_pkg = cpu_top->core_info[0].pkg; | ||
95 | for(cpu = 1; cpu < cpus; cpu++) { | ||
96 | if(cpu_top->core_info[cpu].pkg != last_pkg) { | ||
97 | last_pkg = cpu_top->core_info[cpu].pkg; | ||
98 | cpu_top->pkgs++; | ||
99 | } | ||
100 | } | ||
101 | cpu_top->pkgs++; | ||
102 | |||
98 | /* Intel's cores count is not consecutively numbered, there may | 103 | /* Intel's cores count is not consecutively numbered, there may |
99 | * be a core_id of 3, but none of 2. Assume there always is 0 | 104 | * be a core_id of 3, but none of 2. Assume there always is 0 |
100 | * Get amount of cores by counting duplicates in a package | 105 | * Get amount of cores by counting duplicates in a package |
diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c index 0d6571e418db..c4bae9203a69 100644 --- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c +++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.c | |||
@@ -39,6 +39,7 @@ static int mode; | |||
39 | static int interval = 1; | 39 | static int interval = 1; |
40 | static char *show_monitors_param; | 40 | static char *show_monitors_param; |
41 | static struct cpupower_topology cpu_top; | 41 | static struct cpupower_topology cpu_top; |
42 | static unsigned int wake_cpus; | ||
42 | 43 | ||
43 | /* ToDo: Document this in the manpage */ | 44 | /* ToDo: Document this in the manpage */ |
44 | static char range_abbr[RANGE_MAX] = { 'T', 'C', 'P', 'M', }; | 45 | static char range_abbr[RANGE_MAX] = { 'T', 'C', 'P', 'M', }; |
@@ -84,7 +85,7 @@ int fill_string_with_spaces(char *s, int n) | |||
84 | void print_header(int topology_depth) | 85 | void print_header(int topology_depth) |
85 | { | 86 | { |
86 | int unsigned mon; | 87 | int unsigned mon; |
87 | int state, need_len, pr_mon_len; | 88 | int state, need_len; |
88 | cstate_t s; | 89 | cstate_t s; |
89 | char buf[128] = ""; | 90 | char buf[128] = ""; |
90 | int percent_width = 4; | 91 | int percent_width = 4; |
@@ -93,7 +94,6 @@ void print_header(int topology_depth) | |||
93 | printf("%s|", buf); | 94 | printf("%s|", buf); |
94 | 95 | ||
95 | for (mon = 0; mon < avail_monitors; mon++) { | 96 | for (mon = 0; mon < avail_monitors; mon++) { |
96 | pr_mon_len = 0; | ||
97 | need_len = monitors[mon]->hw_states_num * (percent_width + 3) | 97 | need_len = monitors[mon]->hw_states_num * (percent_width + 3) |
98 | - 1; | 98 | - 1; |
99 | if (mon != 0) { | 99 | if (mon != 0) { |
@@ -315,16 +315,28 @@ int fork_it(char **argv) | |||
315 | int do_interval_measure(int i) | 315 | int do_interval_measure(int i) |
316 | { | 316 | { |
317 | unsigned int num; | 317 | unsigned int num; |
318 | int cpu; | ||
319 | |||
320 | if (wake_cpus) | ||
321 | for (cpu = 0; cpu < cpu_count; cpu++) | ||
322 | bind_cpu(cpu); | ||
318 | 323 | ||
319 | for (num = 0; num < avail_monitors; num++) { | 324 | for (num = 0; num < avail_monitors; num++) { |
320 | dprint("HW C-state residency monitor: %s - States: %d\n", | 325 | dprint("HW C-state residency monitor: %s - States: %d\n", |
321 | monitors[num]->name, monitors[num]->hw_states_num); | 326 | monitors[num]->name, monitors[num]->hw_states_num); |
322 | monitors[num]->start(); | 327 | monitors[num]->start(); |
323 | } | 328 | } |
329 | |||
324 | sleep(i); | 330 | sleep(i); |
331 | |||
332 | if (wake_cpus) | ||
333 | for (cpu = 0; cpu < cpu_count; cpu++) | ||
334 | bind_cpu(cpu); | ||
335 | |||
325 | for (num = 0; num < avail_monitors; num++) | 336 | for (num = 0; num < avail_monitors; num++) |
326 | monitors[num]->stop(); | 337 | monitors[num]->stop(); |
327 | 338 | ||
339 | |||
328 | return 0; | 340 | return 0; |
329 | } | 341 | } |
330 | 342 | ||
@@ -333,7 +345,7 @@ static void cmdline(int argc, char *argv[]) | |||
333 | int opt; | 345 | int opt; |
334 | progname = basename(argv[0]); | 346 | progname = basename(argv[0]); |
335 | 347 | ||
336 | while ((opt = getopt(argc, argv, "+li:m:")) != -1) { | 348 | while ((opt = getopt(argc, argv, "+lci:m:")) != -1) { |
337 | switch (opt) { | 349 | switch (opt) { |
338 | case 'l': | 350 | case 'l': |
339 | if (mode) | 351 | if (mode) |
@@ -352,6 +364,9 @@ static void cmdline(int argc, char *argv[]) | |||
352 | mode = show; | 364 | mode = show; |
353 | show_monitors_param = optarg; | 365 | show_monitors_param = optarg; |
354 | break; | 366 | break; |
367 | case 'c': | ||
368 | wake_cpus = 1; | ||
369 | break; | ||
355 | default: | 370 | default: |
356 | print_wrong_arg_exit(); | 371 | print_wrong_arg_exit(); |
357 | } | 372 | } |
diff --git a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h index 9312ee1f2dbc..9e43f3371fbc 100644 --- a/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h +++ b/tools/power/cpupower/utils/idle_monitor/cpupower-monitor.h | |||
@@ -65,4 +65,21 @@ extern long long timespec_diff_us(struct timespec start, struct timespec end); | |||
65 | "could be inaccurate\n"), mes, ov); \ | 65 | "could be inaccurate\n"), mes, ov); \ |
66 | } | 66 | } |
67 | 67 | ||
68 | |||
69 | /* Taken over from x86info project sources -> return 0 on success */ | ||
70 | #include <sched.h> | ||
71 | #include <sys/types.h> | ||
72 | #include <unistd.h> | ||
73 | static inline int bind_cpu(int cpu) | ||
74 | { | ||
75 | cpu_set_t set; | ||
76 | |||
77 | if (sched_getaffinity(getpid(), sizeof(set), &set) == 0) { | ||
78 | CPU_ZERO(&set); | ||
79 | CPU_SET(cpu, &set); | ||
80 | return sched_setaffinity(getpid(), sizeof(set), &set); | ||
81 | } | ||
82 | return 1; | ||
83 | } | ||
84 | |||
68 | #endif /* __CPUIDLE_INFO_HW__ */ | 85 | #endif /* __CPUIDLE_INFO_HW__ */ |
diff --git a/tools/power/cpupower/utils/idle_monitor/snb_idle.c b/tools/power/cpupower/utils/idle_monitor/snb_idle.c index a1bc07cd53e1..a99b43b97d6d 100644 --- a/tools/power/cpupower/utils/idle_monitor/snb_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/snb_idle.c | |||
@@ -150,9 +150,15 @@ static struct cpuidle_monitor *snb_register(void) | |||
150 | || cpupower_cpu_info.family != 6) | 150 | || cpupower_cpu_info.family != 6) |
151 | return NULL; | 151 | return NULL; |
152 | 152 | ||
153 | if (cpupower_cpu_info.model != 0x2A | 153 | switch (cpupower_cpu_info.model) { |
154 | && cpupower_cpu_info.model != 0x2D) | 154 | case 0x2A: /* SNB */ |
155 | case 0x2D: /* SNB Xeon */ | ||
156 | case 0x3A: /* IVB */ | ||
157 | case 0x3E: /* IVB Xeon */ | ||
158 | break; | ||
159 | default: | ||
155 | return NULL; | 160 | return NULL; |
161 | } | ||
156 | 162 | ||
157 | is_valid = calloc(cpu_count, sizeof(int)); | 163 | is_valid = calloc(cpu_count, sizeof(int)); |
158 | for (num = 0; num < SNB_CSTATE_COUNT; num++) { | 164 | for (num = 0; num < SNB_CSTATE_COUNT; num++) { |
diff --git a/tools/power/x86/turbostat/Makefile b/tools/power/x86/turbostat/Makefile index f85649554191..f09641da40d4 100644 --- a/tools/power/x86/turbostat/Makefile +++ b/tools/power/x86/turbostat/Makefile | |||
@@ -1,9 +1,22 @@ | |||
1 | CC = $(CROSS_COMPILE)gcc | ||
2 | BUILD_OUTPUT := $(PWD) | ||
3 | PREFIX := /usr | ||
4 | DESTDIR := | ||
5 | |||
1 | turbostat : turbostat.c | 6 | turbostat : turbostat.c |
2 | CFLAGS += -Wall | 7 | CFLAGS += -Wall |
8 | CFLAGS += -I../../../../arch/x86/include/uapi/ | ||
9 | |||
10 | %: %.c | ||
11 | @mkdir -p $(BUILD_OUTPUT) | ||
12 | $(CC) $(CFLAGS) $< -o $(BUILD_OUTPUT)/$@ | ||
3 | 13 | ||
14 | .PHONY : clean | ||
4 | clean : | 15 | clean : |
5 | rm -f turbostat | 16 | @rm -f $(BUILD_OUTPUT)/turbostat |
6 | 17 | ||
7 | install : | 18 | install : turbostat |
8 | install turbostat /usr/bin/turbostat | 19 | install -d $(DESTDIR)$(PREFIX)/bin |
9 | install turbostat.8 /usr/share/man/man8 | 20 | install $(BUILD_OUTPUT)/turbostat $(DESTDIR)$(PREFIX)/bin/turbostat |
21 | install -d $(DESTDIR)$(PREFIX)/share/man/man8 | ||
22 | install turbostat.8 $(DESTDIR)$(PREFIX)/share/man/man8 | ||
diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 index e4d0690cccf9..b4ddb748356c 100644 --- a/tools/power/x86/turbostat/turbostat.8 +++ b/tools/power/x86/turbostat/turbostat.8 | |||
@@ -11,16 +11,16 @@ turbostat \- Report processor frequency and idle statistics | |||
11 | .RB [ Options ] | 11 | .RB [ Options ] |
12 | .RB [ "\-i interval_sec" ] | 12 | .RB [ "\-i interval_sec" ] |
13 | .SH DESCRIPTION | 13 | .SH DESCRIPTION |
14 | \fBturbostat \fP reports processor topology, frequency | 14 | \fBturbostat \fP reports processor topology, frequency, |
15 | and idle power state statistics on modern X86 processors. | 15 | idle power-state statistics, temperature and power on modern X86 processors. |
16 | Either \fBcommand\fP is forked and statistics are printed | 16 | Either \fBcommand\fP is forked and statistics are printed |
17 | upon its completion, or statistics are printed periodically. | 17 | upon its completion, or statistics are printed periodically. |
18 | 18 | ||
19 | \fBturbostat \fP | 19 | \fBturbostat \fP |
20 | requires that the processor | 20 | must be run on root, and |
21 | minimally requires that the processor | ||
21 | supports an "invariant" TSC, plus the APERF and MPERF MSRs. | 22 | supports an "invariant" TSC, plus the APERF and MPERF MSRs. |
22 | \fBturbostat \fP will report idle cpu power state residency | 23 | Additional information is reported depending on hardware counter support. |
23 | on processors that additionally support C-state residency counters. | ||
24 | 24 | ||
25 | .SS Options | 25 | .SS Options |
26 | The \fB-p\fP option limits output to the 1st thread in 1st core of each package. | 26 | The \fB-p\fP option limits output to the 1st thread in 1st core of each package. |
@@ -31,8 +31,6 @@ The \fB-S\fP option limits output to a 1-line System Summary for each interval. | |||
31 | .PP | 31 | .PP |
32 | The \fB-v\fP option increases verbosity. | 32 | The \fB-v\fP option increases verbosity. |
33 | .PP | 33 | .PP |
34 | The \fB-s\fP option prints the SMI counter, equivalent to "-c 0x34" | ||
35 | .PP | ||
36 | The \fB-c MSR#\fP option includes the delta of the specified 32-bit MSR counter. | 34 | The \fB-c MSR#\fP option includes the delta of the specified 32-bit MSR counter. |
37 | .PP | 35 | .PP |
38 | The \fB-C MSR#\fP option includes the delta of the specified 64-bit MSR counter. | 36 | The \fB-C MSR#\fP option includes the delta of the specified 64-bit MSR counter. |
@@ -57,7 +55,15 @@ Note that multiple CPUs per core indicate support for Intel(R) Hyper-Threading T | |||
57 | \fBGHz\fP average clock rate while the CPU was in c0 state. | 55 | \fBGHz\fP average clock rate while the CPU was in c0 state. |
58 | \fBTSC\fP average GHz that the TSC ran during the entire interval. | 56 | \fBTSC\fP average GHz that the TSC ran during the entire interval. |
59 | \fB%c1, %c3, %c6, %c7\fP show the percentage residency in hardware core idle states. | 57 | \fB%c1, %c3, %c6, %c7\fP show the percentage residency in hardware core idle states. |
58 | \fBCTMP\fP Degrees Celsius reported by the per-core Digital Thermal Sensor. | ||
59 | \fBPTMP\fP Degrees Celsius reported by the per-package Package Thermal Monitor. | ||
60 | \fB%pc2, %pc3, %pc6, %pc7\fP percentage residency in hardware package idle states. | 60 | \fB%pc2, %pc3, %pc6, %pc7\fP percentage residency in hardware package idle states. |
61 | \fBPkg_W\fP Watts consumed by the whole package. | ||
62 | \fBCor_W\fP Watts consumed by the core part of the package. | ||
63 | \fBGFX_W\fP Watts consumed by the Graphics part of the package -- available only on client processors. | ||
64 | \fBRAM_W\fP Watts consumed by the DRAM DIMMS -- available only on server processors. | ||
65 | \fBPKG_%\fP percent of the interval that RAPL throttling was active on the Package. | ||
66 | \fBRAM_%\fP percent of the interval that RAPL throttling was active on DRAM. | ||
61 | .fi | 67 | .fi |
62 | .PP | 68 | .PP |
63 | .SH EXAMPLE | 69 | .SH EXAMPLE |
@@ -66,50 +72,73 @@ Without any parameters, turbostat prints out counters ever 5 seconds. | |||
66 | for turbostat to fork). | 72 | for turbostat to fork). |
67 | 73 | ||
68 | The first row of statistics is a summary for the entire system. | 74 | The first row of statistics is a summary for the entire system. |
69 | Note that the summary is a weighted average. | 75 | For residency % columns, the summary is a weighted average. |
76 | For Temperature columns, the summary is the column maximum. | ||
77 | For Watts columns, the summary is a system total. | ||
70 | Subsequent rows show per-CPU statistics. | 78 | Subsequent rows show per-CPU statistics. |
71 | 79 | ||
72 | .nf | 80 | .nf |
73 | [root@x980]# ./turbostat | 81 | [root@sandy]# ./turbostat |
74 | cor CPU %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6 | 82 | cor CPU %c0 GHz TSC %c1 %c3 %c6 %c7 CTMP PTMP %pc2 %pc3 %pc6 %pc7 Pkg_W Cor_W GFX_W |
75 | 0.09 1.62 3.38 1.83 0.32 97.76 1.26 83.61 | 83 | 0.06 0.80 2.29 0.11 0.00 0.00 99.83 47 40 0.26 0.01 0.44 98.78 3.49 0.12 0.14 |
76 | 0 0 0.15 1.62 3.38 10.23 0.05 89.56 1.26 83.61 | 84 | 0 0 0.07 0.80 2.29 0.07 0.00 0.00 99.86 40 40 0.26 0.01 0.44 98.78 3.49 0.12 0.14 |
77 | 0 6 0.05 1.62 3.38 10.34 | 85 | 0 4 0.03 0.80 2.29 0.12 |
78 | 1 2 0.03 1.62 3.38 0.07 0.05 99.86 | 86 | 1 1 0.04 0.80 2.29 0.25 0.01 0.00 99.71 40 |
79 | 1 8 0.03 1.62 3.38 0.06 | 87 | 1 5 0.16 0.80 2.29 0.13 |
80 | 2 4 0.21 1.62 3.38 0.10 1.49 98.21 | 88 | 2 2 0.05 0.80 2.29 0.06 0.01 0.00 99.88 40 |
81 | 2 10 0.02 1.62 3.38 0.29 | 89 | 2 6 0.03 0.80 2.29 0.08 |
82 | 8 1 0.04 1.62 3.38 0.04 0.08 99.84 | 90 | 3 3 0.05 0.80 2.29 0.08 0.00 0.00 99.87 47 |
83 | 8 7 0.01 1.62 3.38 0.06 | 91 | 3 7 0.04 0.84 2.29 0.09 |
84 | 9 3 0.53 1.62 3.38 0.10 0.20 99.17 | ||
85 | 9 9 0.02 1.62 3.38 0.60 | ||
86 | 10 5 0.01 1.62 3.38 0.02 0.04 99.92 | ||
87 | 10 11 0.02 1.62 3.38 0.02 | ||
88 | .fi | 92 | .fi |
89 | .SH SUMMARY EXAMPLE | 93 | .SH SUMMARY EXAMPLE |
90 | The "-s" option prints the column headers just once, | 94 | The "-s" option prints the column headers just once, |
91 | and then the one line system summary for each sample interval. | 95 | and then the one line system summary for each sample interval. |
92 | 96 | ||
93 | .nf | 97 | .nf |
94 | [root@x980]# ./turbostat -s | 98 | [root@wsm]# turbostat -S |
95 | %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6 | 99 | %c0 GHz TSC %c1 %c3 %c6 CTMP %pc3 %pc6 |
96 | 0.23 1.67 3.38 2.00 0.30 97.47 1.07 82.12 | 100 | 1.40 2.81 3.38 10.78 43.47 44.35 42 13.67 2.09 |
97 | 0.10 1.62 3.38 1.87 2.25 95.77 12.02 72.60 | 101 | 1.34 2.90 3.38 11.48 58.96 28.23 41 19.89 0.15 |
98 | 0.20 1.64 3.38 1.98 0.11 97.72 0.30 83.36 | 102 | 1.55 2.72 3.38 26.73 37.66 34.07 42 2.53 2.80 |
99 | 0.11 1.70 3.38 1.86 1.81 96.22 9.71 74.90 | 103 | 1.37 2.83 3.38 16.95 60.05 21.63 42 5.76 0.20 |
100 | .fi | 104 | .fi |
101 | .SH VERBOSE EXAMPLE | 105 | .SH VERBOSE EXAMPLE |
102 | The "-v" option adds verbosity to the output: | 106 | The "-v" option adds verbosity to the output: |
103 | 107 | ||
104 | .nf | 108 | .nf |
105 | GenuineIntel 11 CPUID levels; family:model:stepping 0x6:2c:2 (6:44:2) | 109 | [root@ivy]# turbostat -v |
106 | 12 * 133 = 1600 MHz max efficiency | 110 | turbostat v3.0 November 23, 2012 - Len Brown <lenb@kernel.org> |
107 | 25 * 133 = 3333 MHz TSC frequency | 111 | CPUID(0): GenuineIntel 13 CPUID levels; family:model:stepping 0x6:3a:9 (6:58:9) |
108 | 26 * 133 = 3467 MHz max turbo 4 active cores | 112 | CPUID(6): APERF, DTS, PTM, EPB |
109 | 26 * 133 = 3467 MHz max turbo 3 active cores | 113 | RAPL: 851 sec. Joule Counter Range |
110 | 27 * 133 = 3600 MHz max turbo 2 active cores | 114 | cpu0: MSR_NHM_PLATFORM_INFO: 0x81010f0012300 |
111 | 27 * 133 = 3600 MHz max turbo 1 active cores | 115 | 16 * 100 = 1600 MHz max efficiency |
112 | 116 | 35 * 100 = 3500 MHz TSC frequency | |
117 | cpu0: MSR_NHM_SNB_PKG_CST_CFG_CTL: 0x1e008402 (UNdemote-C3, UNdemote-C1, demote-C3, demote-C1, locked: pkg-cstate-limit=2: pc6-noret) | ||
118 | cpu0: MSR_NHM_TURBO_RATIO_LIMIT: 0x25262727 | ||
119 | 37 * 100 = 3700 MHz max turbo 4 active cores | ||
120 | 38 * 100 = 3800 MHz max turbo 3 active cores | ||
121 | 39 * 100 = 3900 MHz max turbo 2 active cores | ||
122 | 39 * 100 = 3900 MHz max turbo 1 active cores | ||
123 | cpu0: MSR_IA32_ENERGY_PERF_BIAS: 0x00000006 (balanced) | ||
124 | cpu0: MSR_RAPL_POWER_UNIT: 0x000a1003 (0.125000 Watts, 0.000015 Joules, 0.000977 sec.) | ||
125 | cpu0: MSR_PKG_POWER_INFO: 0x01e00268 (77 W TDP, RAPL 60 - 0 W, 0.000000 sec.) | ||
126 | cpu0: MSR_PKG_POWER_LIMIT: 0x830000148268 (UNlocked) | ||
127 | cpu0: PKG Limit #1: ENabled (77.000000 Watts, 1.000000 sec, clamp DISabled) | ||
128 | cpu0: PKG Limit #2: ENabled (96.000000 Watts, 0.000977* sec, clamp DISabled) | ||
129 | cpu0: MSR_PP0_POLICY: 0 | ||
130 | cpu0: MSR_PP0_POWER_LIMIT: 0x00000000 (UNlocked) | ||
131 | cpu0: Cores Limit: DISabled (0.000000 Watts, 0.000977 sec, clamp DISabled) | ||
132 | cpu0: MSR_PP1_POLICY: 0 | ||
133 | cpu0: MSR_PP1_POWER_LIMIT: 0x00000000 (UNlocked) | ||
134 | cpu0: GFX Limit: DISabled (0.000000 Watts, 0.000977 sec, clamp DISabled) | ||
135 | cpu0: MSR_IA32_TEMPERATURE_TARGET: 0x00691400 (105 C) | ||
136 | cpu0: MSR_IA32_PACKAGE_THERM_STATUS: 0x884e0000 (27 C) | ||
137 | cpu0: MSR_IA32_THERM_STATUS: 0x88560000 (19 C +/- 1) | ||
138 | cpu1: MSR_IA32_THERM_STATUS: 0x88560000 (19 C +/- 1) | ||
139 | cpu2: MSR_IA32_THERM_STATUS: 0x88540000 (21 C +/- 1) | ||
140 | cpu3: MSR_IA32_THERM_STATUS: 0x884e0000 (27 C +/- 1) | ||
141 | ... | ||
113 | .fi | 142 | .fi |
114 | The \fBmax efficiency\fP frequency, a.k.a. Low Frequency Mode, is the frequency | 143 | The \fBmax efficiency\fP frequency, a.k.a. Low Frequency Mode, is the frequency |
115 | available at the minimum package voltage. The \fBTSC frequency\fP is the nominal | 144 | available at the minimum package voltage. The \fBTSC frequency\fP is the nominal |
@@ -142,7 +171,7 @@ cor CPU %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6 | |||
142 | 10 5 1.42 3.43 3.38 2.14 30.99 65.44 | 171 | 10 5 1.42 3.43 3.38 2.14 30.99 65.44 |
143 | 10 11 0.16 2.88 3.38 3.40 | 172 | 10 11 0.16 2.88 3.38 3.40 |
144 | .fi | 173 | .fi |
145 | Above the cycle soaker drives cpu7 up its 3.6 Ghz turbo limit | 174 | Above the cycle soaker drives cpu7 up its 3.6 GHz turbo limit |
146 | while the other processors are generally in various states of idle. | 175 | while the other processors are generally in various states of idle. |
147 | 176 | ||
148 | Note that cpu1 and cpu7 are HT siblings within core8. | 177 | Note that cpu1 and cpu7 are HT siblings within core8. |
@@ -155,26 +184,24 @@ This is a weighted average, where the weight is %c0. ie. it is the total number | |||
155 | un-halted cycles elapsed per time divided by the number of CPUs. | 184 | un-halted cycles elapsed per time divided by the number of CPUs. |
156 | .SH SMI COUNTING EXAMPLE | 185 | .SH SMI COUNTING EXAMPLE |
157 | On Intel Nehalem and newer processors, MSR 0x34 is a System Management Mode Interrupt (SMI) counter. | 186 | On Intel Nehalem and newer processors, MSR 0x34 is a System Management Mode Interrupt (SMI) counter. |
158 | Using the -m option, you can display how many SMIs have fired since reset, or if there | 187 | This counter is shown by default under the "SMI" column. |
159 | are SMIs during the measurement interval, you can display the delta using the -d option. | ||
160 | .nf | 188 | .nf |
161 | [root@x980 ~]# turbostat -m 0x34 | 189 | [root@x980 ~]# turbostat |
162 | cor CPU %c0 GHz TSC MSR 0x034 %c1 %c3 %c6 %pc3 %pc6 | 190 | cor CPU %c0 GHz TSC SMI %c1 %c3 %c6 CTMP %pc3 %pc6 |
163 | 1.41 1.82 3.38 0x00000000 8.92 37.82 51.85 17.37 0.55 | 191 | 0.11 1.91 3.38 0 1.84 0.26 97.79 29 0.82 83.87 |
164 | 0 0 3.73 2.03 3.38 0x00000055 1.72 48.25 46.31 17.38 0.55 | 192 | 0 0 0.40 1.63 3.38 0 10.27 0.12 89.20 20 0.82 83.88 |
165 | 0 6 0.14 1.63 3.38 0x00000056 5.30 | 193 | 0 6 0.06 1.63 3.38 0 10.61 |
166 | 1 2 2.51 1.80 3.38 0x00000056 15.65 29.33 52.52 | 194 | 1 2 0.37 2.63 3.38 0 0.02 0.10 99.51 22 |
167 | 1 8 0.10 1.65 3.38 0x00000056 18.05 | 195 | 1 8 0.01 1.62 3.38 0 0.39 |
168 | 2 4 1.16 1.68 3.38 0x00000056 5.87 24.47 68.50 | 196 | 2 4 0.07 1.62 3.38 0 0.04 0.07 99.82 23 |
169 | 2 10 0.10 1.63 3.38 0x00000056 6.93 | 197 | 2 10 0.02 1.62 3.38 0 0.09 |
170 | 8 1 3.84 1.91 3.38 0x00000056 1.36 50.65 44.16 | 198 | 8 1 0.23 1.64 3.38 0 0.10 1.07 98.60 24 |
171 | 8 7 0.08 1.64 3.38 0x00000056 5.12 | 199 | 8 7 0.02 1.64 3.38 0 0.31 |
172 | 9 3 1.82 1.73 3.38 0x00000056 7.59 24.21 66.38 | 200 | 9 3 0.03 1.62 3.38 0 0.03 0.05 99.89 29 |
173 | 9 9 0.09 1.68 3.38 0x00000056 9.32 | 201 | 9 9 0.02 1.62 3.38 0 0.05 |
174 | 10 5 1.66 1.65 3.38 0x00000056 15.10 50.00 33.23 | 202 | 10 5 0.07 1.62 3.38 0 0.08 0.12 99.73 27 |
175 | 10 11 1.72 1.65 3.38 0x00000056 15.05 | 203 | 10 11 0.03 1.62 3.38 0 0.13 |
176 | ^C | 204 | ^C |
177 | [root@x980 ~]# | ||
178 | .fi | 205 | .fi |
179 | .SH NOTES | 206 | .SH NOTES |
180 | 207 | ||
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 2655ae9a3ad8..6f3214ed4444 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c | |||
@@ -20,6 +20,7 @@ | |||
20 | */ | 20 | */ |
21 | 21 | ||
22 | #define _GNU_SOURCE | 22 | #define _GNU_SOURCE |
23 | #include <asm/msr.h> | ||
23 | #include <stdio.h> | 24 | #include <stdio.h> |
24 | #include <unistd.h> | 25 | #include <unistd.h> |
25 | #include <sys/types.h> | 26 | #include <sys/types.h> |
@@ -35,28 +36,18 @@ | |||
35 | #include <ctype.h> | 36 | #include <ctype.h> |
36 | #include <sched.h> | 37 | #include <sched.h> |
37 | 38 | ||
38 | #define MSR_NEHALEM_PLATFORM_INFO 0xCE | ||
39 | #define MSR_NEHALEM_TURBO_RATIO_LIMIT 0x1AD | ||
40 | #define MSR_IVT_TURBO_RATIO_LIMIT 0x1AE | ||
41 | #define MSR_APERF 0xE8 | ||
42 | #define MSR_MPERF 0xE7 | ||
43 | #define MSR_PKG_C2_RESIDENCY 0x60D /* SNB only */ | ||
44 | #define MSR_PKG_C3_RESIDENCY 0x3F8 | ||
45 | #define MSR_PKG_C6_RESIDENCY 0x3F9 | ||
46 | #define MSR_PKG_C7_RESIDENCY 0x3FA /* SNB only */ | ||
47 | #define MSR_CORE_C3_RESIDENCY 0x3FC | ||
48 | #define MSR_CORE_C6_RESIDENCY 0x3FD | ||
49 | #define MSR_CORE_C7_RESIDENCY 0x3FE /* SNB only */ | ||
50 | |||
51 | char *proc_stat = "/proc/stat"; | 39 | char *proc_stat = "/proc/stat"; |
52 | unsigned int interval_sec = 5; /* set with -i interval_sec */ | 40 | unsigned int interval_sec = 5; /* set with -i interval_sec */ |
53 | unsigned int verbose; /* set with -v */ | 41 | unsigned int verbose; /* set with -v */ |
42 | unsigned int rapl_verbose; /* set with -R */ | ||
43 | unsigned int thermal_verbose; /* set with -T */ | ||
54 | unsigned int summary_only; /* set with -s */ | 44 | unsigned int summary_only; /* set with -s */ |
55 | unsigned int skip_c0; | 45 | unsigned int skip_c0; |
56 | unsigned int skip_c1; | 46 | unsigned int skip_c1; |
57 | unsigned int do_nhm_cstates; | 47 | unsigned int do_nhm_cstates; |
58 | unsigned int do_snb_cstates; | 48 | unsigned int do_snb_cstates; |
59 | unsigned int has_aperf; | 49 | unsigned int has_aperf; |
50 | unsigned int has_epb; | ||
60 | unsigned int units = 1000000000; /* Ghz etc */ | 51 | unsigned int units = 1000000000; /* Ghz etc */ |
61 | unsigned int genuine_intel; | 52 | unsigned int genuine_intel; |
62 | unsigned int has_invariant_tsc; | 53 | unsigned int has_invariant_tsc; |
@@ -67,6 +58,7 @@ unsigned int extra_msr_offset32; | |||
67 | unsigned int extra_msr_offset64; | 58 | unsigned int extra_msr_offset64; |
68 | unsigned int extra_delta_offset32; | 59 | unsigned int extra_delta_offset32; |
69 | unsigned int extra_delta_offset64; | 60 | unsigned int extra_delta_offset64; |
61 | int do_smi; | ||
70 | double bclk; | 62 | double bclk; |
71 | unsigned int show_pkg; | 63 | unsigned int show_pkg; |
72 | unsigned int show_core; | 64 | unsigned int show_core; |
@@ -74,6 +66,23 @@ unsigned int show_cpu; | |||
74 | unsigned int show_pkg_only; | 66 | unsigned int show_pkg_only; |
75 | unsigned int show_core_only; | 67 | unsigned int show_core_only; |
76 | char *output_buffer, *outp; | 68 | char *output_buffer, *outp; |
69 | unsigned int do_rapl; | ||
70 | unsigned int do_dts; | ||
71 | unsigned int do_ptm; | ||
72 | unsigned int tcc_activation_temp; | ||
73 | unsigned int tcc_activation_temp_override; | ||
74 | double rapl_power_units, rapl_energy_units, rapl_time_units; | ||
75 | double rapl_joule_counter_range; | ||
76 | |||
77 | #define RAPL_PKG (1 << 0) | ||
78 | #define RAPL_CORES (1 << 1) | ||
79 | #define RAPL_GFX (1 << 2) | ||
80 | #define RAPL_DRAM (1 << 3) | ||
81 | #define RAPL_PKG_PERF_STATUS (1 << 4) | ||
82 | #define RAPL_DRAM_PERF_STATUS (1 << 5) | ||
83 | #define TJMAX_DEFAULT 100 | ||
84 | |||
85 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) | ||
77 | 86 | ||
78 | int aperf_mperf_unstable; | 87 | int aperf_mperf_unstable; |
79 | int backwards_count; | 88 | int backwards_count; |
@@ -91,6 +100,7 @@ struct thread_data { | |||
91 | unsigned long long extra_delta64; | 100 | unsigned long long extra_delta64; |
92 | unsigned long long extra_msr32; | 101 | unsigned long long extra_msr32; |
93 | unsigned long long extra_delta32; | 102 | unsigned long long extra_delta32; |
103 | unsigned int smi_count; | ||
94 | unsigned int cpu_id; | 104 | unsigned int cpu_id; |
95 | unsigned int flags; | 105 | unsigned int flags; |
96 | #define CPU_IS_FIRST_THREAD_IN_CORE 0x2 | 106 | #define CPU_IS_FIRST_THREAD_IN_CORE 0x2 |
@@ -101,6 +111,7 @@ struct core_data { | |||
101 | unsigned long long c3; | 111 | unsigned long long c3; |
102 | unsigned long long c6; | 112 | unsigned long long c6; |
103 | unsigned long long c7; | 113 | unsigned long long c7; |
114 | unsigned int core_temp_c; | ||
104 | unsigned int core_id; | 115 | unsigned int core_id; |
105 | } *core_even, *core_odd; | 116 | } *core_even, *core_odd; |
106 | 117 | ||
@@ -110,6 +121,14 @@ struct pkg_data { | |||
110 | unsigned long long pc6; | 121 | unsigned long long pc6; |
111 | unsigned long long pc7; | 122 | unsigned long long pc7; |
112 | unsigned int package_id; | 123 | unsigned int package_id; |
124 | unsigned int energy_pkg; /* MSR_PKG_ENERGY_STATUS */ | ||
125 | unsigned int energy_dram; /* MSR_DRAM_ENERGY_STATUS */ | ||
126 | unsigned int energy_cores; /* MSR_PP0_ENERGY_STATUS */ | ||
127 | unsigned int energy_gfx; /* MSR_PP1_ENERGY_STATUS */ | ||
128 | unsigned int rapl_pkg_perf_status; /* MSR_PKG_PERF_STATUS */ | ||
129 | unsigned int rapl_dram_perf_status; /* MSR_DRAM_PERF_STATUS */ | ||
130 | unsigned int pkg_temp_c; | ||
131 | |||
113 | } *package_even, *package_odd; | 132 | } *package_even, *package_odd; |
114 | 133 | ||
115 | #define ODD_COUNTERS thread_odd, core_odd, package_odd | 134 | #define ODD_COUNTERS thread_odd, core_odd, package_odd |
@@ -206,8 +225,10 @@ int get_msr(int cpu, off_t offset, unsigned long long *msr) | |||
206 | retval = pread(fd, msr, sizeof *msr, offset); | 225 | retval = pread(fd, msr, sizeof *msr, offset); |
207 | close(fd); | 226 | close(fd); |
208 | 227 | ||
209 | if (retval != sizeof *msr) | 228 | if (retval != sizeof *msr) { |
229 | fprintf(stderr, "%s offset 0x%zx read failed\n", pathname, offset); | ||
210 | return -1; | 230 | return -1; |
231 | } | ||
211 | 232 | ||
212 | return 0; | 233 | return 0; |
213 | } | 234 | } |
@@ -229,6 +250,8 @@ void print_header(void) | |||
229 | if (has_aperf) | 250 | if (has_aperf) |
230 | outp += sprintf(outp, " GHz"); | 251 | outp += sprintf(outp, " GHz"); |
231 | outp += sprintf(outp, " TSC"); | 252 | outp += sprintf(outp, " TSC"); |
253 | if (do_smi) | ||
254 | outp += sprintf(outp, " SMI"); | ||
232 | if (extra_delta_offset32) | 255 | if (extra_delta_offset32) |
233 | outp += sprintf(outp, " count 0x%03X", extra_delta_offset32); | 256 | outp += sprintf(outp, " count 0x%03X", extra_delta_offset32); |
234 | if (extra_delta_offset64) | 257 | if (extra_delta_offset64) |
@@ -245,6 +268,12 @@ void print_header(void) | |||
245 | outp += sprintf(outp, " %%c6"); | 268 | outp += sprintf(outp, " %%c6"); |
246 | if (do_snb_cstates) | 269 | if (do_snb_cstates) |
247 | outp += sprintf(outp, " %%c7"); | 270 | outp += sprintf(outp, " %%c7"); |
271 | |||
272 | if (do_dts) | ||
273 | outp += sprintf(outp, " CTMP"); | ||
274 | if (do_ptm) | ||
275 | outp += sprintf(outp, " PTMP"); | ||
276 | |||
248 | if (do_snb_cstates) | 277 | if (do_snb_cstates) |
249 | outp += sprintf(outp, " %%pc2"); | 278 | outp += sprintf(outp, " %%pc2"); |
250 | if (do_nhm_cstates) | 279 | if (do_nhm_cstates) |
@@ -254,6 +283,19 @@ void print_header(void) | |||
254 | if (do_snb_cstates) | 283 | if (do_snb_cstates) |
255 | outp += sprintf(outp, " %%pc7"); | 284 | outp += sprintf(outp, " %%pc7"); |
256 | 285 | ||
286 | if (do_rapl & RAPL_PKG) | ||
287 | outp += sprintf(outp, " Pkg_W"); | ||
288 | if (do_rapl & RAPL_CORES) | ||
289 | outp += sprintf(outp, " Cor_W"); | ||
290 | if (do_rapl & RAPL_GFX) | ||
291 | outp += sprintf(outp, " GFX_W"); | ||
292 | if (do_rapl & RAPL_DRAM) | ||
293 | outp += sprintf(outp, " RAM_W"); | ||
294 | if (do_rapl & RAPL_PKG_PERF_STATUS) | ||
295 | outp += sprintf(outp, " PKG_%%"); | ||
296 | if (do_rapl & RAPL_DRAM_PERF_STATUS) | ||
297 | outp += sprintf(outp, " RAM_%%"); | ||
298 | |||
257 | outp += sprintf(outp, "\n"); | 299 | outp += sprintf(outp, "\n"); |
258 | } | 300 | } |
259 | 301 | ||
@@ -276,6 +318,8 @@ int dump_counters(struct thread_data *t, struct core_data *c, | |||
276 | extra_msr_offset32, t->extra_msr32); | 318 | extra_msr_offset32, t->extra_msr32); |
277 | fprintf(stderr, "msr0x%x: %016llX\n", | 319 | fprintf(stderr, "msr0x%x: %016llX\n", |
278 | extra_msr_offset64, t->extra_msr64); | 320 | extra_msr_offset64, t->extra_msr64); |
321 | if (do_smi) | ||
322 | fprintf(stderr, "SMI: %08X\n", t->smi_count); | ||
279 | } | 323 | } |
280 | 324 | ||
281 | if (c) { | 325 | if (c) { |
@@ -283,6 +327,7 @@ int dump_counters(struct thread_data *t, struct core_data *c, | |||
283 | fprintf(stderr, "c3: %016llX\n", c->c3); | 327 | fprintf(stderr, "c3: %016llX\n", c->c3); |
284 | fprintf(stderr, "c6: %016llX\n", c->c6); | 328 | fprintf(stderr, "c6: %016llX\n", c->c6); |
285 | fprintf(stderr, "c7: %016llX\n", c->c7); | 329 | fprintf(stderr, "c7: %016llX\n", c->c7); |
330 | fprintf(stderr, "DTS: %dC\n", c->core_temp_c); | ||
286 | } | 331 | } |
287 | 332 | ||
288 | if (p) { | 333 | if (p) { |
@@ -291,6 +336,13 @@ int dump_counters(struct thread_data *t, struct core_data *c, | |||
291 | fprintf(stderr, "pc3: %016llX\n", p->pc3); | 336 | fprintf(stderr, "pc3: %016llX\n", p->pc3); |
292 | fprintf(stderr, "pc6: %016llX\n", p->pc6); | 337 | fprintf(stderr, "pc6: %016llX\n", p->pc6); |
293 | fprintf(stderr, "pc7: %016llX\n", p->pc7); | 338 | fprintf(stderr, "pc7: %016llX\n", p->pc7); |
339 | fprintf(stderr, "Joules PKG: %0X\n", p->energy_pkg); | ||
340 | fprintf(stderr, "Joules COR: %0X\n", p->energy_cores); | ||
341 | fprintf(stderr, "Joules GFX: %0X\n", p->energy_gfx); | ||
342 | fprintf(stderr, "Joules RAM: %0X\n", p->energy_dram); | ||
343 | fprintf(stderr, "Throttle PKG: %0X\n", p->rapl_pkg_perf_status); | ||
344 | fprintf(stderr, "Throttle RAM: %0X\n", p->rapl_dram_perf_status); | ||
345 | fprintf(stderr, "PTM: %dC\n", p->pkg_temp_c); | ||
294 | } | 346 | } |
295 | return 0; | 347 | return 0; |
296 | } | 348 | } |
@@ -300,14 +352,22 @@ int dump_counters(struct thread_data *t, struct core_data *c, | |||
300 | * package: "pk" 2 columns %2d | 352 | * package: "pk" 2 columns %2d |
301 | * core: "cor" 3 columns %3d | 353 | * core: "cor" 3 columns %3d |
302 | * CPU: "CPU" 3 columns %3d | 354 | * CPU: "CPU" 3 columns %3d |
355 | * Pkg_W: %6.2 | ||
356 | * Cor_W: %6.2 | ||
357 | * GFX_W: %5.2 | ||
358 | * RAM_W: %5.2 | ||
303 | * GHz: "GHz" 3 columns %3.2 | 359 | * GHz: "GHz" 3 columns %3.2 |
304 | * TSC: "TSC" 3 columns %3.2 | 360 | * TSC: "TSC" 3 columns %3.2 |
361 | * SMI: "SMI" 4 columns %4d | ||
305 | * percentage " %pc3" %6.2 | 362 | * percentage " %pc3" %6.2 |
363 | * Perf Status percentage: %5.2 | ||
364 | * "CTMP" 4 columns %4d | ||
306 | */ | 365 | */ |
307 | int format_counters(struct thread_data *t, struct core_data *c, | 366 | int format_counters(struct thread_data *t, struct core_data *c, |
308 | struct pkg_data *p) | 367 | struct pkg_data *p) |
309 | { | 368 | { |
310 | double interval_float; | 369 | double interval_float; |
370 | char *fmt5, *fmt6; | ||
311 | 371 | ||
312 | /* if showing only 1st thread in core and this isn't one, bail out */ | 372 | /* if showing only 1st thread in core and this isn't one, bail out */ |
313 | if (show_core_only && !(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) | 373 | if (show_core_only && !(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) |
@@ -347,7 +407,6 @@ int format_counters(struct thread_data *t, struct core_data *c, | |||
347 | if (show_cpu) | 407 | if (show_cpu) |
348 | outp += sprintf(outp, " %3d", t->cpu_id); | 408 | outp += sprintf(outp, " %3d", t->cpu_id); |
349 | } | 409 | } |
350 | |||
351 | /* %c0 */ | 410 | /* %c0 */ |
352 | if (do_nhm_cstates) { | 411 | if (do_nhm_cstates) { |
353 | if (show_pkg || show_core || show_cpu) | 412 | if (show_pkg || show_core || show_cpu) |
@@ -379,6 +438,10 @@ int format_counters(struct thread_data *t, struct core_data *c, | |||
379 | /* TSC */ | 438 | /* TSC */ |
380 | outp += sprintf(outp, "%5.2f", 1.0 * t->tsc/units/interval_float); | 439 | outp += sprintf(outp, "%5.2f", 1.0 * t->tsc/units/interval_float); |
381 | 440 | ||
441 | /* SMI */ | ||
442 | if (do_smi) | ||
443 | outp += sprintf(outp, "%4d", t->smi_count); | ||
444 | |||
382 | /* delta */ | 445 | /* delta */ |
383 | if (extra_delta_offset32) | 446 | if (extra_delta_offset32) |
384 | outp += sprintf(outp, " %11llu", t->extra_delta32); | 447 | outp += sprintf(outp, " %11llu", t->extra_delta32); |
@@ -412,10 +475,16 @@ int format_counters(struct thread_data *t, struct core_data *c, | |||
412 | if (do_snb_cstates) | 475 | if (do_snb_cstates) |
413 | outp += sprintf(outp, " %6.2f", 100.0 * c->c7/t->tsc); | 476 | outp += sprintf(outp, " %6.2f", 100.0 * c->c7/t->tsc); |
414 | 477 | ||
478 | if (do_dts) | ||
479 | outp += sprintf(outp, " %4d", c->core_temp_c); | ||
480 | |||
415 | /* print per-package data only for 1st core in package */ | 481 | /* print per-package data only for 1st core in package */ |
416 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) | 482 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) |
417 | goto done; | 483 | goto done; |
418 | 484 | ||
485 | if (do_ptm) | ||
486 | outp += sprintf(outp, " %4d", p->pkg_temp_c); | ||
487 | |||
419 | if (do_snb_cstates) | 488 | if (do_snb_cstates) |
420 | outp += sprintf(outp, " %6.2f", 100.0 * p->pc2/t->tsc); | 489 | outp += sprintf(outp, " %6.2f", 100.0 * p->pc2/t->tsc); |
421 | if (do_nhm_cstates) | 490 | if (do_nhm_cstates) |
@@ -424,6 +493,32 @@ int format_counters(struct thread_data *t, struct core_data *c, | |||
424 | outp += sprintf(outp, " %6.2f", 100.0 * p->pc6/t->tsc); | 493 | outp += sprintf(outp, " %6.2f", 100.0 * p->pc6/t->tsc); |
425 | if (do_snb_cstates) | 494 | if (do_snb_cstates) |
426 | outp += sprintf(outp, " %6.2f", 100.0 * p->pc7/t->tsc); | 495 | outp += sprintf(outp, " %6.2f", 100.0 * p->pc7/t->tsc); |
496 | |||
497 | /* | ||
498 | * If measurement interval exceeds minimum RAPL Joule Counter range, | ||
499 | * indicate that results are suspect by printing "**" in fraction place. | ||
500 | */ | ||
501 | if (interval_float < rapl_joule_counter_range) { | ||
502 | fmt5 = " %5.2f"; | ||
503 | fmt6 = " %6.2f"; | ||
504 | } else { | ||
505 | fmt5 = " %3.0f**"; | ||
506 | fmt6 = " %4.0f**"; | ||
507 | } | ||
508 | |||
509 | if (do_rapl & RAPL_PKG) | ||
510 | outp += sprintf(outp, fmt6, p->energy_pkg * rapl_energy_units / interval_float); | ||
511 | if (do_rapl & RAPL_CORES) | ||
512 | outp += sprintf(outp, fmt6, p->energy_cores * rapl_energy_units / interval_float); | ||
513 | if (do_rapl & RAPL_GFX) | ||
514 | outp += sprintf(outp, fmt5, p->energy_gfx * rapl_energy_units / interval_float); | ||
515 | if (do_rapl & RAPL_DRAM) | ||
516 | outp += sprintf(outp, fmt5, p->energy_dram * rapl_energy_units / interval_float); | ||
517 | if (do_rapl & RAPL_PKG_PERF_STATUS ) | ||
518 | outp += sprintf(outp, fmt5, 100.0 * p->rapl_pkg_perf_status * rapl_time_units / interval_float); | ||
519 | if (do_rapl & RAPL_DRAM_PERF_STATUS ) | ||
520 | outp += sprintf(outp, fmt5, 100.0 * p->rapl_dram_perf_status * rapl_time_units / interval_float); | ||
521 | |||
427 | done: | 522 | done: |
428 | outp += sprintf(outp, "\n"); | 523 | outp += sprintf(outp, "\n"); |
429 | 524 | ||
@@ -433,6 +528,7 @@ done: | |||
433 | void flush_stdout() | 528 | void flush_stdout() |
434 | { | 529 | { |
435 | fputs(output_buffer, stdout); | 530 | fputs(output_buffer, stdout); |
531 | fflush(stdout); | ||
436 | outp = output_buffer; | 532 | outp = output_buffer; |
437 | } | 533 | } |
438 | void flush_stderr() | 534 | void flush_stderr() |
@@ -459,6 +555,13 @@ void format_all_counters(struct thread_data *t, struct core_data *c, struct pkg_ | |||
459 | for_all_cpus(format_counters, t, c, p); | 555 | for_all_cpus(format_counters, t, c, p); |
460 | } | 556 | } |
461 | 557 | ||
558 | #define DELTA_WRAP32(new, old) \ | ||
559 | if (new > old) { \ | ||
560 | old = new - old; \ | ||
561 | } else { \ | ||
562 | old = 0x100000000 + new - old; \ | ||
563 | } | ||
564 | |||
462 | void | 565 | void |
463 | delta_package(struct pkg_data *new, struct pkg_data *old) | 566 | delta_package(struct pkg_data *new, struct pkg_data *old) |
464 | { | 567 | { |
@@ -466,6 +569,14 @@ delta_package(struct pkg_data *new, struct pkg_data *old) | |||
466 | old->pc3 = new->pc3 - old->pc3; | 569 | old->pc3 = new->pc3 - old->pc3; |
467 | old->pc6 = new->pc6 - old->pc6; | 570 | old->pc6 = new->pc6 - old->pc6; |
468 | old->pc7 = new->pc7 - old->pc7; | 571 | old->pc7 = new->pc7 - old->pc7; |
572 | old->pkg_temp_c = new->pkg_temp_c; | ||
573 | |||
574 | DELTA_WRAP32(new->energy_pkg, old->energy_pkg); | ||
575 | DELTA_WRAP32(new->energy_cores, old->energy_cores); | ||
576 | DELTA_WRAP32(new->energy_gfx, old->energy_gfx); | ||
577 | DELTA_WRAP32(new->energy_dram, old->energy_dram); | ||
578 | DELTA_WRAP32(new->rapl_pkg_perf_status, old->rapl_pkg_perf_status); | ||
579 | DELTA_WRAP32(new->rapl_dram_perf_status, old->rapl_dram_perf_status); | ||
469 | } | 580 | } |
470 | 581 | ||
471 | void | 582 | void |
@@ -474,6 +585,7 @@ delta_core(struct core_data *new, struct core_data *old) | |||
474 | old->c3 = new->c3 - old->c3; | 585 | old->c3 = new->c3 - old->c3; |
475 | old->c6 = new->c6 - old->c6; | 586 | old->c6 = new->c6 - old->c6; |
476 | old->c7 = new->c7 - old->c7; | 587 | old->c7 = new->c7 - old->c7; |
588 | old->core_temp_c = new->core_temp_c; | ||
477 | } | 589 | } |
478 | 590 | ||
479 | /* | 591 | /* |
@@ -544,6 +656,9 @@ delta_thread(struct thread_data *new, struct thread_data *old, | |||
544 | */ | 656 | */ |
545 | old->extra_msr32 = new->extra_msr32; | 657 | old->extra_msr32 = new->extra_msr32; |
546 | old->extra_msr64 = new->extra_msr64; | 658 | old->extra_msr64 = new->extra_msr64; |
659 | |||
660 | if (do_smi) | ||
661 | old->smi_count = new->smi_count - old->smi_count; | ||
547 | } | 662 | } |
548 | 663 | ||
549 | int delta_cpu(struct thread_data *t, struct core_data *c, | 664 | int delta_cpu(struct thread_data *t, struct core_data *c, |
@@ -571,6 +686,7 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data | |||
571 | t->mperf = 0; | 686 | t->mperf = 0; |
572 | t->c1 = 0; | 687 | t->c1 = 0; |
573 | 688 | ||
689 | t->smi_count = 0; | ||
574 | t->extra_delta32 = 0; | 690 | t->extra_delta32 = 0; |
575 | t->extra_delta64 = 0; | 691 | t->extra_delta64 = 0; |
576 | 692 | ||
@@ -580,11 +696,20 @@ void clear_counters(struct thread_data *t, struct core_data *c, struct pkg_data | |||
580 | c->c3 = 0; | 696 | c->c3 = 0; |
581 | c->c6 = 0; | 697 | c->c6 = 0; |
582 | c->c7 = 0; | 698 | c->c7 = 0; |
699 | c->core_temp_c = 0; | ||
583 | 700 | ||
584 | p->pc2 = 0; | 701 | p->pc2 = 0; |
585 | p->pc3 = 0; | 702 | p->pc3 = 0; |
586 | p->pc6 = 0; | 703 | p->pc6 = 0; |
587 | p->pc7 = 0; | 704 | p->pc7 = 0; |
705 | |||
706 | p->energy_pkg = 0; | ||
707 | p->energy_dram = 0; | ||
708 | p->energy_cores = 0; | ||
709 | p->energy_gfx = 0; | ||
710 | p->rapl_pkg_perf_status = 0; | ||
711 | p->rapl_dram_perf_status = 0; | ||
712 | p->pkg_temp_c = 0; | ||
588 | } | 713 | } |
589 | int sum_counters(struct thread_data *t, struct core_data *c, | 714 | int sum_counters(struct thread_data *t, struct core_data *c, |
590 | struct pkg_data *p) | 715 | struct pkg_data *p) |
@@ -605,6 +730,8 @@ int sum_counters(struct thread_data *t, struct core_data *c, | |||
605 | average.cores.c6 += c->c6; | 730 | average.cores.c6 += c->c6; |
606 | average.cores.c7 += c->c7; | 731 | average.cores.c7 += c->c7; |
607 | 732 | ||
733 | average.cores.core_temp_c = MAX(average.cores.core_temp_c, c->core_temp_c); | ||
734 | |||
608 | /* sum per-pkg values only for 1st core in pkg */ | 735 | /* sum per-pkg values only for 1st core in pkg */ |
609 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) | 736 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) |
610 | return 0; | 737 | return 0; |
@@ -614,6 +741,15 @@ int sum_counters(struct thread_data *t, struct core_data *c, | |||
614 | average.packages.pc6 += p->pc6; | 741 | average.packages.pc6 += p->pc6; |
615 | average.packages.pc7 += p->pc7; | 742 | average.packages.pc7 += p->pc7; |
616 | 743 | ||
744 | average.packages.energy_pkg += p->energy_pkg; | ||
745 | average.packages.energy_dram += p->energy_dram; | ||
746 | average.packages.energy_cores += p->energy_cores; | ||
747 | average.packages.energy_gfx += p->energy_gfx; | ||
748 | |||
749 | average.packages.pkg_temp_c = MAX(average.packages.pkg_temp_c, p->pkg_temp_c); | ||
750 | |||
751 | average.packages.rapl_pkg_perf_status += p->rapl_pkg_perf_status; | ||
752 | average.packages.rapl_dram_perf_status += p->rapl_dram_perf_status; | ||
617 | return 0; | 753 | return 0; |
618 | } | 754 | } |
619 | /* | 755 | /* |
@@ -665,23 +801,31 @@ static unsigned long long rdtsc(void) | |||
665 | int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) | 801 | int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) |
666 | { | 802 | { |
667 | int cpu = t->cpu_id; | 803 | int cpu = t->cpu_id; |
804 | unsigned long long msr; | ||
668 | 805 | ||
669 | if (cpu_migrate(cpu)) | 806 | if (cpu_migrate(cpu)) { |
807 | fprintf(stderr, "Could not migrate to CPU %d\n", cpu); | ||
670 | return -1; | 808 | return -1; |
809 | } | ||
671 | 810 | ||
672 | t->tsc = rdtsc(); /* we are running on local CPU of interest */ | 811 | t->tsc = rdtsc(); /* we are running on local CPU of interest */ |
673 | 812 | ||
674 | if (has_aperf) { | 813 | if (has_aperf) { |
675 | if (get_msr(cpu, MSR_APERF, &t->aperf)) | 814 | if (get_msr(cpu, MSR_IA32_APERF, &t->aperf)) |
676 | return -3; | 815 | return -3; |
677 | if (get_msr(cpu, MSR_MPERF, &t->mperf)) | 816 | if (get_msr(cpu, MSR_IA32_MPERF, &t->mperf)) |
678 | return -4; | 817 | return -4; |
679 | } | 818 | } |
680 | 819 | ||
820 | if (do_smi) { | ||
821 | if (get_msr(cpu, MSR_SMI_COUNT, &msr)) | ||
822 | return -5; | ||
823 | t->smi_count = msr & 0xFFFFFFFF; | ||
824 | } | ||
681 | if (extra_delta_offset32) { | 825 | if (extra_delta_offset32) { |
682 | if (get_msr(cpu, extra_delta_offset32, &t->extra_delta32)) | 826 | if (get_msr(cpu, extra_delta_offset32, &msr)) |
683 | return -5; | 827 | return -5; |
684 | t->extra_delta32 &= 0xFFFFFFFF; | 828 | t->extra_delta32 = msr & 0xFFFFFFFF; |
685 | } | 829 | } |
686 | 830 | ||
687 | if (extra_delta_offset64) | 831 | if (extra_delta_offset64) |
@@ -689,9 +833,9 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) | |||
689 | return -5; | 833 | return -5; |
690 | 834 | ||
691 | if (extra_msr_offset32) { | 835 | if (extra_msr_offset32) { |
692 | if (get_msr(cpu, extra_msr_offset32, &t->extra_msr32)) | 836 | if (get_msr(cpu, extra_msr_offset32, &msr)) |
693 | return -5; | 837 | return -5; |
694 | t->extra_msr32 &= 0xFFFFFFFF; | 838 | t->extra_msr32 = msr & 0xFFFFFFFF; |
695 | } | 839 | } |
696 | 840 | ||
697 | if (extra_msr_offset64) | 841 | if (extra_msr_offset64) |
@@ -713,6 +857,13 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) | |||
713 | if (get_msr(cpu, MSR_CORE_C7_RESIDENCY, &c->c7)) | 857 | if (get_msr(cpu, MSR_CORE_C7_RESIDENCY, &c->c7)) |
714 | return -8; | 858 | return -8; |
715 | 859 | ||
860 | if (do_dts) { | ||
861 | if (get_msr(cpu, MSR_IA32_THERM_STATUS, &msr)) | ||
862 | return -9; | ||
863 | c->core_temp_c = tcc_activation_temp - ((msr >> 16) & 0x7F); | ||
864 | } | ||
865 | |||
866 | |||
716 | /* collect package counters only for 1st core in package */ | 867 | /* collect package counters only for 1st core in package */ |
717 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) | 868 | if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) |
718 | return 0; | 869 | return 0; |
@@ -729,6 +880,41 @@ int get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) | |||
729 | if (get_msr(cpu, MSR_PKG_C7_RESIDENCY, &p->pc7)) | 880 | if (get_msr(cpu, MSR_PKG_C7_RESIDENCY, &p->pc7)) |
730 | return -12; | 881 | return -12; |
731 | } | 882 | } |
883 | if (do_rapl & RAPL_PKG) { | ||
884 | if (get_msr(cpu, MSR_PKG_ENERGY_STATUS, &msr)) | ||
885 | return -13; | ||
886 | p->energy_pkg = msr & 0xFFFFFFFF; | ||
887 | } | ||
888 | if (do_rapl & RAPL_CORES) { | ||
889 | if (get_msr(cpu, MSR_PP0_ENERGY_STATUS, &msr)) | ||
890 | return -14; | ||
891 | p->energy_cores = msr & 0xFFFFFFFF; | ||
892 | } | ||
893 | if (do_rapl & RAPL_DRAM) { | ||
894 | if (get_msr(cpu, MSR_DRAM_ENERGY_STATUS, &msr)) | ||
895 | return -15; | ||
896 | p->energy_dram = msr & 0xFFFFFFFF; | ||
897 | } | ||
898 | if (do_rapl & RAPL_GFX) { | ||
899 | if (get_msr(cpu, MSR_PP1_ENERGY_STATUS, &msr)) | ||
900 | return -16; | ||
901 | p->energy_gfx = msr & 0xFFFFFFFF; | ||
902 | } | ||
903 | if (do_rapl & RAPL_PKG_PERF_STATUS) { | ||
904 | if (get_msr(cpu, MSR_PKG_PERF_STATUS, &msr)) | ||
905 | return -16; | ||
906 | p->rapl_pkg_perf_status = msr & 0xFFFFFFFF; | ||
907 | } | ||
908 | if (do_rapl & RAPL_DRAM_PERF_STATUS) { | ||
909 | if (get_msr(cpu, MSR_DRAM_PERF_STATUS, &msr)) | ||
910 | return -16; | ||
911 | p->rapl_dram_perf_status = msr & 0xFFFFFFFF; | ||
912 | } | ||
913 | if (do_ptm) { | ||
914 | if (get_msr(cpu, MSR_IA32_PACKAGE_THERM_STATUS, &msr)) | ||
915 | return -17; | ||
916 | p->pkg_temp_c = tcc_activation_temp - ((msr >> 16) & 0x7F); | ||
917 | } | ||
732 | return 0; | 918 | return 0; |
733 | } | 919 | } |
734 | 920 | ||
@@ -740,10 +926,9 @@ void print_verbose_header(void) | |||
740 | if (!do_nehalem_platform_info) | 926 | if (!do_nehalem_platform_info) |
741 | return; | 927 | return; |
742 | 928 | ||
743 | get_msr(0, MSR_NEHALEM_PLATFORM_INFO, &msr); | 929 | get_msr(0, MSR_NHM_PLATFORM_INFO, &msr); |
744 | 930 | ||
745 | if (verbose > 1) | 931 | fprintf(stderr, "cpu0: MSR_NHM_PLATFORM_INFO: 0x%08llx\n", msr); |
746 | fprintf(stderr, "MSR_NEHALEM_PLATFORM_INFO: 0x%llx\n", msr); | ||
747 | 932 | ||
748 | ratio = (msr >> 40) & 0xFF; | 933 | ratio = (msr >> 40) & 0xFF; |
749 | fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n", | 934 | fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n", |
@@ -753,13 +938,16 @@ void print_verbose_header(void) | |||
753 | fprintf(stderr, "%d * %.0f = %.0f MHz TSC frequency\n", | 938 | fprintf(stderr, "%d * %.0f = %.0f MHz TSC frequency\n", |
754 | ratio, bclk, ratio * bclk); | 939 | ratio, bclk, ratio * bclk); |
755 | 940 | ||
941 | get_msr(0, MSR_IA32_POWER_CTL, &msr); | ||
942 | fprintf(stderr, "cpu0: MSR_IA32_POWER_CTL: 0x%08llx (C1E: %sabled)\n", | ||
943 | msr, msr & 0x2 ? "EN" : "DIS"); | ||
944 | |||
756 | if (!do_ivt_turbo_ratio_limit) | 945 | if (!do_ivt_turbo_ratio_limit) |
757 | goto print_nhm_turbo_ratio_limits; | 946 | goto print_nhm_turbo_ratio_limits; |
758 | 947 | ||
759 | get_msr(0, MSR_IVT_TURBO_RATIO_LIMIT, &msr); | 948 | get_msr(0, MSR_IVT_TURBO_RATIO_LIMIT, &msr); |
760 | 949 | ||
761 | if (verbose > 1) | 950 | fprintf(stderr, "cpu0: MSR_IVT_TURBO_RATIO_LIMIT: 0x%08llx\n", msr); |
762 | fprintf(stderr, "MSR_IVT_TURBO_RATIO_LIMIT: 0x%llx\n", msr); | ||
763 | 951 | ||
764 | ratio = (msr >> 56) & 0xFF; | 952 | ratio = (msr >> 56) & 0xFF; |
765 | if (ratio) | 953 | if (ratio) |
@@ -802,14 +990,55 @@ void print_verbose_header(void) | |||
802 | ratio, bclk, ratio * bclk); | 990 | ratio, bclk, ratio * bclk); |
803 | 991 | ||
804 | print_nhm_turbo_ratio_limits: | 992 | print_nhm_turbo_ratio_limits: |
993 | get_msr(0, MSR_NHM_SNB_PKG_CST_CFG_CTL, &msr); | ||
994 | |||
995 | #define SNB_C1_AUTO_UNDEMOTE (1UL << 27) | ||
996 | #define SNB_C3_AUTO_UNDEMOTE (1UL << 28) | ||
997 | |||
998 | fprintf(stderr, "cpu0: MSR_NHM_SNB_PKG_CST_CFG_CTL: 0x%08llx", msr); | ||
999 | |||
1000 | fprintf(stderr, " (%s%s%s%s%slocked: pkg-cstate-limit=%d: ", | ||
1001 | (msr & SNB_C3_AUTO_UNDEMOTE) ? "UNdemote-C3, " : "", | ||
1002 | (msr & SNB_C1_AUTO_UNDEMOTE) ? "UNdemote-C1, " : "", | ||
1003 | (msr & NHM_C3_AUTO_DEMOTE) ? "demote-C3, " : "", | ||
1004 | (msr & NHM_C1_AUTO_DEMOTE) ? "demote-C1, " : "", | ||
1005 | (msr & (1 << 15)) ? "" : "UN", | ||
1006 | (unsigned int)msr & 7); | ||
1007 | |||
1008 | |||
1009 | switch(msr & 0x7) { | ||
1010 | case 0: | ||
1011 | fprintf(stderr, "pc0"); | ||
1012 | break; | ||
1013 | case 1: | ||
1014 | fprintf(stderr, do_snb_cstates ? "pc2" : "pc0"); | ||
1015 | break; | ||
1016 | case 2: | ||
1017 | fprintf(stderr, do_snb_cstates ? "pc6-noret" : "pc3"); | ||
1018 | break; | ||
1019 | case 3: | ||
1020 | fprintf(stderr, "pc6"); | ||
1021 | break; | ||
1022 | case 4: | ||
1023 | fprintf(stderr, "pc7"); | ||
1024 | break; | ||
1025 | case 5: | ||
1026 | fprintf(stderr, do_snb_cstates ? "pc7s" : "invalid"); | ||
1027 | break; | ||
1028 | case 7: | ||
1029 | fprintf(stderr, "unlimited"); | ||
1030 | break; | ||
1031 | default: | ||
1032 | fprintf(stderr, "invalid"); | ||
1033 | } | ||
1034 | fprintf(stderr, ")\n"); | ||
805 | 1035 | ||
806 | if (!do_nehalem_turbo_ratio_limit) | 1036 | if (!do_nehalem_turbo_ratio_limit) |
807 | return; | 1037 | return; |
808 | 1038 | ||
809 | get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT, &msr); | 1039 | get_msr(0, MSR_NHM_TURBO_RATIO_LIMIT, &msr); |
810 | 1040 | ||
811 | if (verbose > 1) | 1041 | fprintf(stderr, "cpu0: MSR_NHM_TURBO_RATIO_LIMIT: 0x%08llx\n", msr); |
812 | fprintf(stderr, "MSR_NEHALEM_TURBO_RATIO_LIMIT: 0x%llx\n", msr); | ||
813 | 1042 | ||
814 | ratio = (msr >> 56) & 0xFF; | 1043 | ratio = (msr >> 56) & 0xFF; |
815 | if (ratio) | 1044 | if (ratio) |
@@ -1098,13 +1327,22 @@ int mark_cpu_present(int cpu) | |||
1098 | void turbostat_loop() | 1327 | void turbostat_loop() |
1099 | { | 1328 | { |
1100 | int retval; | 1329 | int retval; |
1330 | int restarted = 0; | ||
1101 | 1331 | ||
1102 | restart: | 1332 | restart: |
1333 | restarted++; | ||
1334 | |||
1103 | retval = for_all_cpus(get_counters, EVEN_COUNTERS); | 1335 | retval = for_all_cpus(get_counters, EVEN_COUNTERS); |
1104 | if (retval) { | 1336 | if (retval < -1) { |
1337 | exit(retval); | ||
1338 | } else if (retval == -1) { | ||
1339 | if (restarted > 1) { | ||
1340 | exit(retval); | ||
1341 | } | ||
1105 | re_initialize(); | 1342 | re_initialize(); |
1106 | goto restart; | 1343 | goto restart; |
1107 | } | 1344 | } |
1345 | restarted = 0; | ||
1108 | gettimeofday(&tv_even, (struct timezone *)NULL); | 1346 | gettimeofday(&tv_even, (struct timezone *)NULL); |
1109 | 1347 | ||
1110 | while (1) { | 1348 | while (1) { |
@@ -1114,7 +1352,9 @@ restart: | |||
1114 | } | 1352 | } |
1115 | sleep(interval_sec); | 1353 | sleep(interval_sec); |
1116 | retval = for_all_cpus(get_counters, ODD_COUNTERS); | 1354 | retval = for_all_cpus(get_counters, ODD_COUNTERS); |
1117 | if (retval) { | 1355 | if (retval < -1) { |
1356 | exit(retval); | ||
1357 | } else if (retval == -1) { | ||
1118 | re_initialize(); | 1358 | re_initialize(); |
1119 | goto restart; | 1359 | goto restart; |
1120 | } | 1360 | } |
@@ -1126,7 +1366,9 @@ restart: | |||
1126 | flush_stdout(); | 1366 | flush_stdout(); |
1127 | sleep(interval_sec); | 1367 | sleep(interval_sec); |
1128 | retval = for_all_cpus(get_counters, EVEN_COUNTERS); | 1368 | retval = for_all_cpus(get_counters, EVEN_COUNTERS); |
1129 | if (retval) { | 1369 | if (retval < -1) { |
1370 | exit(retval); | ||
1371 | } else if (retval == -1) { | ||
1130 | re_initialize(); | 1372 | re_initialize(); |
1131 | goto restart; | 1373 | goto restart; |
1132 | } | 1374 | } |
@@ -1176,6 +1418,9 @@ int has_nehalem_turbo_ratio_limit(unsigned int family, unsigned int model) | |||
1176 | case 0x2D: /* SNB Xeon */ | 1418 | case 0x2D: /* SNB Xeon */ |
1177 | case 0x3A: /* IVB */ | 1419 | case 0x3A: /* IVB */ |
1178 | case 0x3E: /* IVB Xeon */ | 1420 | case 0x3E: /* IVB Xeon */ |
1421 | case 0x3C: /* HSW */ | ||
1422 | case 0x3F: /* HSW */ | ||
1423 | case 0x45: /* HSW */ | ||
1179 | return 1; | 1424 | return 1; |
1180 | case 0x2E: /* Nehalem-EX Xeon - Beckton */ | 1425 | case 0x2E: /* Nehalem-EX Xeon - Beckton */ |
1181 | case 0x2F: /* Westmere-EX Xeon - Eagleton */ | 1426 | case 0x2F: /* Westmere-EX Xeon - Eagleton */ |
@@ -1199,6 +1444,302 @@ int has_ivt_turbo_ratio_limit(unsigned int family, unsigned int model) | |||
1199 | } | 1444 | } |
1200 | } | 1445 | } |
1201 | 1446 | ||
1447 | /* | ||
1448 | * print_epb() | ||
1449 | * Decode the ENERGY_PERF_BIAS MSR | ||
1450 | */ | ||
1451 | int print_epb(struct thread_data *t, struct core_data *c, struct pkg_data *p) | ||
1452 | { | ||
1453 | unsigned long long msr; | ||
1454 | char *epb_string; | ||
1455 | int cpu; | ||
1456 | |||
1457 | if (!has_epb) | ||
1458 | return 0; | ||
1459 | |||
1460 | cpu = t->cpu_id; | ||
1461 | |||
1462 | /* EPB is per-package */ | ||
1463 | if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) | ||
1464 | return 0; | ||
1465 | |||
1466 | if (cpu_migrate(cpu)) { | ||
1467 | fprintf(stderr, "Could not migrate to CPU %d\n", cpu); | ||
1468 | return -1; | ||
1469 | } | ||
1470 | |||
1471 | if (get_msr(cpu, MSR_IA32_ENERGY_PERF_BIAS, &msr)) | ||
1472 | return 0; | ||
1473 | |||
1474 | switch (msr & 0x7) { | ||
1475 | case ENERGY_PERF_BIAS_PERFORMANCE: | ||
1476 | epb_string = "performance"; | ||
1477 | break; | ||
1478 | case ENERGY_PERF_BIAS_NORMAL: | ||
1479 | epb_string = "balanced"; | ||
1480 | break; | ||
1481 | case ENERGY_PERF_BIAS_POWERSAVE: | ||
1482 | epb_string = "powersave"; | ||
1483 | break; | ||
1484 | default: | ||
1485 | epb_string = "custom"; | ||
1486 | break; | ||
1487 | } | ||
1488 | fprintf(stderr, "cpu%d: MSR_IA32_ENERGY_PERF_BIAS: 0x%08llx (%s)\n", cpu, msr, epb_string); | ||
1489 | |||
1490 | return 0; | ||
1491 | } | ||
1492 | |||
1493 | #define RAPL_POWER_GRANULARITY 0x7FFF /* 15 bit power granularity */ | ||
1494 | #define RAPL_TIME_GRANULARITY 0x3F /* 6 bit time granularity */ | ||
1495 | |||
1496 | /* | ||
1497 | * rapl_probe() | ||
1498 | * | ||
1499 | * sets do_rapl | ||
1500 | */ | ||
1501 | void rapl_probe(unsigned int family, unsigned int model) | ||
1502 | { | ||
1503 | unsigned long long msr; | ||
1504 | double tdp; | ||
1505 | |||
1506 | if (!genuine_intel) | ||
1507 | return; | ||
1508 | |||
1509 | if (family != 6) | ||
1510 | return; | ||
1511 | |||
1512 | switch (model) { | ||
1513 | case 0x2A: | ||
1514 | case 0x3A: | ||
1515 | case 0x3C: /* HSW */ | ||
1516 | case 0x3F: /* HSW */ | ||
1517 | case 0x45: /* HSW */ | ||
1518 | do_rapl = RAPL_PKG | RAPL_CORES | RAPL_GFX; | ||
1519 | break; | ||
1520 | case 0x2D: | ||
1521 | case 0x3E: | ||
1522 | do_rapl = RAPL_PKG | RAPL_CORES | RAPL_DRAM | RAPL_PKG_PERF_STATUS | RAPL_DRAM_PERF_STATUS; | ||
1523 | break; | ||
1524 | default: | ||
1525 | return; | ||
1526 | } | ||
1527 | |||
1528 | /* units on package 0, verify later other packages match */ | ||
1529 | if (get_msr(0, MSR_RAPL_POWER_UNIT, &msr)) | ||
1530 | return; | ||
1531 | |||
1532 | rapl_power_units = 1.0 / (1 << (msr & 0xF)); | ||
1533 | rapl_energy_units = 1.0 / (1 << (msr >> 8 & 0x1F)); | ||
1534 | rapl_time_units = 1.0 / (1 << (msr >> 16 & 0xF)); | ||
1535 | |||
1536 | /* get TDP to determine energy counter range */ | ||
1537 | if (get_msr(0, MSR_PKG_POWER_INFO, &msr)) | ||
1538 | return; | ||
1539 | |||
1540 | tdp = ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units; | ||
1541 | |||
1542 | rapl_joule_counter_range = 0xFFFFFFFF * rapl_energy_units / tdp; | ||
1543 | |||
1544 | if (verbose) | ||
1545 | fprintf(stderr, "RAPL: %.0f sec. Joule Counter Range\n", rapl_joule_counter_range); | ||
1546 | |||
1547 | return; | ||
1548 | } | ||
1549 | |||
1550 | int print_thermal(struct thread_data *t, struct core_data *c, struct pkg_data *p) | ||
1551 | { | ||
1552 | unsigned long long msr; | ||
1553 | unsigned int dts; | ||
1554 | int cpu; | ||
1555 | |||
1556 | if (!(do_dts || do_ptm)) | ||
1557 | return 0; | ||
1558 | |||
1559 | cpu = t->cpu_id; | ||
1560 | |||
1561 | /* DTS is per-core, no need to print for each thread */ | ||
1562 | if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) | ||
1563 | return 0; | ||
1564 | |||
1565 | if (cpu_migrate(cpu)) { | ||
1566 | fprintf(stderr, "Could not migrate to CPU %d\n", cpu); | ||
1567 | return -1; | ||
1568 | } | ||
1569 | |||
1570 | if (do_ptm && (t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) { | ||
1571 | if (get_msr(cpu, MSR_IA32_PACKAGE_THERM_STATUS, &msr)) | ||
1572 | return 0; | ||
1573 | |||
1574 | dts = (msr >> 16) & 0x7F; | ||
1575 | fprintf(stderr, "cpu%d: MSR_IA32_PACKAGE_THERM_STATUS: 0x%08llx (%d C)\n", | ||
1576 | cpu, msr, tcc_activation_temp - dts); | ||
1577 | |||
1578 | #ifdef THERM_DEBUG | ||
1579 | if (get_msr(cpu, MSR_IA32_PACKAGE_THERM_INTERRUPT, &msr)) | ||
1580 | return 0; | ||
1581 | |||
1582 | dts = (msr >> 16) & 0x7F; | ||
1583 | dts2 = (msr >> 8) & 0x7F; | ||
1584 | fprintf(stderr, "cpu%d: MSR_IA32_PACKAGE_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", | ||
1585 | cpu, msr, tcc_activation_temp - dts, tcc_activation_temp - dts2); | ||
1586 | #endif | ||
1587 | } | ||
1588 | |||
1589 | |||
1590 | if (do_dts) { | ||
1591 | unsigned int resolution; | ||
1592 | |||
1593 | if (get_msr(cpu, MSR_IA32_THERM_STATUS, &msr)) | ||
1594 | return 0; | ||
1595 | |||
1596 | dts = (msr >> 16) & 0x7F; | ||
1597 | resolution = (msr >> 27) & 0xF; | ||
1598 | fprintf(stderr, "cpu%d: MSR_IA32_THERM_STATUS: 0x%08llx (%d C +/- %d)\n", | ||
1599 | cpu, msr, tcc_activation_temp - dts, resolution); | ||
1600 | |||
1601 | #ifdef THERM_DEBUG | ||
1602 | if (get_msr(cpu, MSR_IA32_THERM_INTERRUPT, &msr)) | ||
1603 | return 0; | ||
1604 | |||
1605 | dts = (msr >> 16) & 0x7F; | ||
1606 | dts2 = (msr >> 8) & 0x7F; | ||
1607 | fprintf(stderr, "cpu%d: MSR_IA32_THERM_INTERRUPT: 0x%08llx (%d C, %d C)\n", | ||
1608 | cpu, msr, tcc_activation_temp - dts, tcc_activation_temp - dts2); | ||
1609 | #endif | ||
1610 | } | ||
1611 | |||
1612 | return 0; | ||
1613 | } | ||
1614 | |||
1615 | void print_power_limit_msr(int cpu, unsigned long long msr, char *label) | ||
1616 | { | ||
1617 | fprintf(stderr, "cpu%d: %s: %sabled (%f Watts, %f sec, clamp %sabled)\n", | ||
1618 | cpu, label, | ||
1619 | ((msr >> 15) & 1) ? "EN" : "DIS", | ||
1620 | ((msr >> 0) & 0x7FFF) * rapl_power_units, | ||
1621 | (1.0 + (((msr >> 22) & 0x3)/4.0)) * (1 << ((msr >> 17) & 0x1F)) * rapl_time_units, | ||
1622 | (((msr >> 16) & 1) ? "EN" : "DIS")); | ||
1623 | |||
1624 | return; | ||
1625 | } | ||
1626 | |||
1627 | int print_rapl(struct thread_data *t, struct core_data *c, struct pkg_data *p) | ||
1628 | { | ||
1629 | unsigned long long msr; | ||
1630 | int cpu; | ||
1631 | double local_rapl_power_units, local_rapl_energy_units, local_rapl_time_units; | ||
1632 | |||
1633 | if (!do_rapl) | ||
1634 | return 0; | ||
1635 | |||
1636 | /* RAPL counters are per package, so print only for 1st thread/package */ | ||
1637 | if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) | ||
1638 | return 0; | ||
1639 | |||
1640 | cpu = t->cpu_id; | ||
1641 | if (cpu_migrate(cpu)) { | ||
1642 | fprintf(stderr, "Could not migrate to CPU %d\n", cpu); | ||
1643 | return -1; | ||
1644 | } | ||
1645 | |||
1646 | if (get_msr(cpu, MSR_RAPL_POWER_UNIT, &msr)) | ||
1647 | return -1; | ||
1648 | |||
1649 | local_rapl_power_units = 1.0 / (1 << (msr & 0xF)); | ||
1650 | local_rapl_energy_units = 1.0 / (1 << (msr >> 8 & 0x1F)); | ||
1651 | local_rapl_time_units = 1.0 / (1 << (msr >> 16 & 0xF)); | ||
1652 | |||
1653 | if (local_rapl_power_units != rapl_power_units) | ||
1654 | fprintf(stderr, "cpu%d, ERROR: Power units mis-match\n", cpu); | ||
1655 | if (local_rapl_energy_units != rapl_energy_units) | ||
1656 | fprintf(stderr, "cpu%d, ERROR: Energy units mis-match\n", cpu); | ||
1657 | if (local_rapl_time_units != rapl_time_units) | ||
1658 | fprintf(stderr, "cpu%d, ERROR: Time units mis-match\n", cpu); | ||
1659 | |||
1660 | if (verbose) { | ||
1661 | fprintf(stderr, "cpu%d: MSR_RAPL_POWER_UNIT: 0x%08llx " | ||
1662 | "(%f Watts, %f Joules, %f sec.)\n", cpu, msr, | ||
1663 | local_rapl_power_units, local_rapl_energy_units, local_rapl_time_units); | ||
1664 | } | ||
1665 | if (do_rapl & RAPL_PKG) { | ||
1666 | if (get_msr(cpu, MSR_PKG_POWER_INFO, &msr)) | ||
1667 | return -5; | ||
1668 | |||
1669 | |||
1670 | fprintf(stderr, "cpu%d: MSR_PKG_POWER_INFO: 0x%08llx (%.0f W TDP, RAPL %.0f - %.0f W, %f sec.)\n", | ||
1671 | cpu, msr, | ||
1672 | ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units, | ||
1673 | ((msr >> 16) & RAPL_POWER_GRANULARITY) * rapl_power_units, | ||
1674 | ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, | ||
1675 | ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); | ||
1676 | |||
1677 | if (get_msr(cpu, MSR_PKG_POWER_LIMIT, &msr)) | ||
1678 | return -9; | ||
1679 | |||
1680 | fprintf(stderr, "cpu%d: MSR_PKG_POWER_LIMIT: 0x%08llx (%slocked)\n", | ||
1681 | cpu, msr, (msr >> 63) & 1 ? "": "UN"); | ||
1682 | |||
1683 | print_power_limit_msr(cpu, msr, "PKG Limit #1"); | ||
1684 | fprintf(stderr, "cpu%d: PKG Limit #2: %sabled (%f Watts, %f* sec, clamp %sabled)\n", | ||
1685 | cpu, | ||
1686 | ((msr >> 47) & 1) ? "EN" : "DIS", | ||
1687 | ((msr >> 32) & 0x7FFF) * rapl_power_units, | ||
1688 | (1.0 + (((msr >> 54) & 0x3)/4.0)) * (1 << ((msr >> 49) & 0x1F)) * rapl_time_units, | ||
1689 | ((msr >> 48) & 1) ? "EN" : "DIS"); | ||
1690 | } | ||
1691 | |||
1692 | if (do_rapl & RAPL_DRAM) { | ||
1693 | if (get_msr(cpu, MSR_DRAM_POWER_INFO, &msr)) | ||
1694 | return -6; | ||
1695 | |||
1696 | |||
1697 | fprintf(stderr, "cpu%d: MSR_DRAM_POWER_INFO,: 0x%08llx (%.0f W TDP, RAPL %.0f - %.0f W, %f sec.)\n", | ||
1698 | cpu, msr, | ||
1699 | ((msr >> 0) & RAPL_POWER_GRANULARITY) * rapl_power_units, | ||
1700 | ((msr >> 16) & RAPL_POWER_GRANULARITY) * rapl_power_units, | ||
1701 | ((msr >> 32) & RAPL_POWER_GRANULARITY) * rapl_power_units, | ||
1702 | ((msr >> 48) & RAPL_TIME_GRANULARITY) * rapl_time_units); | ||
1703 | |||
1704 | |||
1705 | if (get_msr(cpu, MSR_DRAM_POWER_LIMIT, &msr)) | ||
1706 | return -9; | ||
1707 | fprintf(stderr, "cpu%d: MSR_DRAM_POWER_LIMIT: 0x%08llx (%slocked)\n", | ||
1708 | cpu, msr, (msr >> 31) & 1 ? "": "UN"); | ||
1709 | |||
1710 | print_power_limit_msr(cpu, msr, "DRAM Limit"); | ||
1711 | } | ||
1712 | if (do_rapl & RAPL_CORES) { | ||
1713 | if (verbose) { | ||
1714 | if (get_msr(cpu, MSR_PP0_POLICY, &msr)) | ||
1715 | return -7; | ||
1716 | |||
1717 | fprintf(stderr, "cpu%d: MSR_PP0_POLICY: %lld\n", cpu, msr & 0xF); | ||
1718 | |||
1719 | if (get_msr(cpu, MSR_PP0_POWER_LIMIT, &msr)) | ||
1720 | return -9; | ||
1721 | fprintf(stderr, "cpu%d: MSR_PP0_POWER_LIMIT: 0x%08llx (%slocked)\n", | ||
1722 | cpu, msr, (msr >> 31) & 1 ? "": "UN"); | ||
1723 | print_power_limit_msr(cpu, msr, "Cores Limit"); | ||
1724 | } | ||
1725 | } | ||
1726 | if (do_rapl & RAPL_GFX) { | ||
1727 | if (verbose) { | ||
1728 | if (get_msr(cpu, MSR_PP1_POLICY, &msr)) | ||
1729 | return -8; | ||
1730 | |||
1731 | fprintf(stderr, "cpu%d: MSR_PP1_POLICY: %lld\n", cpu, msr & 0xF); | ||
1732 | |||
1733 | if (get_msr(cpu, MSR_PP1_POWER_LIMIT, &msr)) | ||
1734 | return -9; | ||
1735 | fprintf(stderr, "cpu%d: MSR_PP1_POWER_LIMIT: 0x%08llx (%slocked)\n", | ||
1736 | cpu, msr, (msr >> 31) & 1 ? "": "UN"); | ||
1737 | print_power_limit_msr(cpu, msr, "GFX Limit"); | ||
1738 | } | ||
1739 | } | ||
1740 | return 0; | ||
1741 | } | ||
1742 | |||
1202 | 1743 | ||
1203 | int is_snb(unsigned int family, unsigned int model) | 1744 | int is_snb(unsigned int family, unsigned int model) |
1204 | { | 1745 | { |
@@ -1210,6 +1751,9 @@ int is_snb(unsigned int family, unsigned int model) | |||
1210 | case 0x2D: | 1751 | case 0x2D: |
1211 | case 0x3A: /* IVB */ | 1752 | case 0x3A: /* IVB */ |
1212 | case 0x3E: /* IVB Xeon */ | 1753 | case 0x3E: /* IVB Xeon */ |
1754 | case 0x3C: /* HSW */ | ||
1755 | case 0x3F: /* HSW */ | ||
1756 | case 0x45: /* HSW */ | ||
1213 | return 1; | 1757 | return 1; |
1214 | } | 1758 | } |
1215 | return 0; | 1759 | return 0; |
@@ -1223,6 +1767,72 @@ double discover_bclk(unsigned int family, unsigned int model) | |||
1223 | return 133.33; | 1767 | return 133.33; |
1224 | } | 1768 | } |
1225 | 1769 | ||
1770 | /* | ||
1771 | * MSR_IA32_TEMPERATURE_TARGET indicates the temperature where | ||
1772 | * the Thermal Control Circuit (TCC) activates. | ||
1773 | * This is usually equal to tjMax. | ||
1774 | * | ||
1775 | * Older processors do not have this MSR, so there we guess, | ||
1776 | * but also allow cmdline over-ride with -T. | ||
1777 | * | ||
1778 | * Several MSR temperature values are in units of degrees-C | ||
1779 | * below this value, including the Digital Thermal Sensor (DTS), | ||
1780 | * Package Thermal Management Sensor (PTM), and thermal event thresholds. | ||
1781 | */ | ||
1782 | int set_temperature_target(struct thread_data *t, struct core_data *c, struct pkg_data *p) | ||
1783 | { | ||
1784 | unsigned long long msr; | ||
1785 | unsigned int target_c_local; | ||
1786 | int cpu; | ||
1787 | |||
1788 | /* tcc_activation_temp is used only for dts or ptm */ | ||
1789 | if (!(do_dts || do_ptm)) | ||
1790 | return 0; | ||
1791 | |||
1792 | /* this is a per-package concept */ | ||
1793 | if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) | ||
1794 | return 0; | ||
1795 | |||
1796 | cpu = t->cpu_id; | ||
1797 | if (cpu_migrate(cpu)) { | ||
1798 | fprintf(stderr, "Could not migrate to CPU %d\n", cpu); | ||
1799 | return -1; | ||
1800 | } | ||
1801 | |||
1802 | if (tcc_activation_temp_override != 0) { | ||
1803 | tcc_activation_temp = tcc_activation_temp_override; | ||
1804 | fprintf(stderr, "cpu%d: Using cmdline TCC Target (%d C)\n", | ||
1805 | cpu, tcc_activation_temp); | ||
1806 | return 0; | ||
1807 | } | ||
1808 | |||
1809 | /* Temperature Target MSR is Nehalem and newer only */ | ||
1810 | if (!do_nehalem_platform_info) | ||
1811 | goto guess; | ||
1812 | |||
1813 | if (get_msr(0, MSR_IA32_TEMPERATURE_TARGET, &msr)) | ||
1814 | goto guess; | ||
1815 | |||
1816 | target_c_local = (msr >> 16) & 0x7F; | ||
1817 | |||
1818 | if (verbose) | ||
1819 | fprintf(stderr, "cpu%d: MSR_IA32_TEMPERATURE_TARGET: 0x%08llx (%d C)\n", | ||
1820 | cpu, msr, target_c_local); | ||
1821 | |||
1822 | if (target_c_local < 85 || target_c_local > 120) | ||
1823 | goto guess; | ||
1824 | |||
1825 | tcc_activation_temp = target_c_local; | ||
1826 | |||
1827 | return 0; | ||
1828 | |||
1829 | guess: | ||
1830 | tcc_activation_temp = TJMAX_DEFAULT; | ||
1831 | fprintf(stderr, "cpu%d: Guessing tjMax %d C, Please use -T to specify\n", | ||
1832 | cpu, tcc_activation_temp); | ||
1833 | |||
1834 | return 0; | ||
1835 | } | ||
1226 | void check_cpuid() | 1836 | void check_cpuid() |
1227 | { | 1837 | { |
1228 | unsigned int eax, ebx, ecx, edx, max_level; | 1838 | unsigned int eax, ebx, ecx, edx, max_level; |
@@ -1236,7 +1846,7 @@ void check_cpuid() | |||
1236 | genuine_intel = 1; | 1846 | genuine_intel = 1; |
1237 | 1847 | ||
1238 | if (verbose) | 1848 | if (verbose) |
1239 | fprintf(stderr, "%.4s%.4s%.4s ", | 1849 | fprintf(stderr, "CPUID(0): %.4s%.4s%.4s ", |
1240 | (char *)&ebx, (char *)&edx, (char *)&ecx); | 1850 | (char *)&ebx, (char *)&edx, (char *)&ecx); |
1241 | 1851 | ||
1242 | asm("cpuid" : "=a" (fms), "=c" (ecx), "=d" (edx) : "a" (1) : "ebx"); | 1852 | asm("cpuid" : "=a" (fms), "=c" (ecx), "=d" (edx) : "a" (1) : "ebx"); |
@@ -1287,24 +1897,37 @@ void check_cpuid() | |||
1287 | 1897 | ||
1288 | asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x6)); | 1898 | asm("cpuid" : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (0x6)); |
1289 | has_aperf = ecx & (1 << 0); | 1899 | has_aperf = ecx & (1 << 0); |
1290 | if (!has_aperf) { | 1900 | do_dts = eax & (1 << 0); |
1291 | fprintf(stderr, "No APERF MSR\n"); | 1901 | do_ptm = eax & (1 << 6); |
1292 | exit(1); | 1902 | has_epb = ecx & (1 << 3); |
1293 | } | 1903 | |
1904 | if (verbose) | ||
1905 | fprintf(stderr, "CPUID(6): %s%s%s%s\n", | ||
1906 | has_aperf ? "APERF" : "No APERF!", | ||
1907 | do_dts ? ", DTS" : "", | ||
1908 | do_ptm ? ", PTM": "", | ||
1909 | has_epb ? ", EPB": ""); | ||
1910 | |||
1911 | if (!has_aperf) | ||
1912 | exit(-1); | ||
1294 | 1913 | ||
1295 | do_nehalem_platform_info = genuine_intel && has_invariant_tsc; | 1914 | do_nehalem_platform_info = genuine_intel && has_invariant_tsc; |
1296 | do_nhm_cstates = genuine_intel; /* all Intel w/ non-stop TSC have NHM counters */ | 1915 | do_nhm_cstates = genuine_intel; /* all Intel w/ non-stop TSC have NHM counters */ |
1916 | do_smi = do_nhm_cstates; | ||
1297 | do_snb_cstates = is_snb(family, model); | 1917 | do_snb_cstates = is_snb(family, model); |
1298 | bclk = discover_bclk(family, model); | 1918 | bclk = discover_bclk(family, model); |
1299 | 1919 | ||
1300 | do_nehalem_turbo_ratio_limit = has_nehalem_turbo_ratio_limit(family, model); | 1920 | do_nehalem_turbo_ratio_limit = has_nehalem_turbo_ratio_limit(family, model); |
1301 | do_ivt_turbo_ratio_limit = has_ivt_turbo_ratio_limit(family, model); | 1921 | do_ivt_turbo_ratio_limit = has_ivt_turbo_ratio_limit(family, model); |
1922 | rapl_probe(family, model); | ||
1923 | |||
1924 | return; | ||
1302 | } | 1925 | } |
1303 | 1926 | ||
1304 | 1927 | ||
1305 | void usage() | 1928 | void usage() |
1306 | { | 1929 | { |
1307 | fprintf(stderr, "%s: [-v][-p|-P|-S][-c MSR# | -s]][-C MSR#][-m MSR#][-M MSR#][-i interval_sec | command ...]\n", | 1930 | fprintf(stderr, "%s: [-v][-R][-T][-p|-P|-S][-c MSR# | -s]][-C MSR#][-m MSR#][-M MSR#][-i interval_sec | command ...]\n", |
1308 | progname); | 1931 | progname); |
1309 | exit(1); | 1932 | exit(1); |
1310 | } | 1933 | } |
@@ -1540,13 +2163,27 @@ void turbostat_init() | |||
1540 | 2163 | ||
1541 | if (verbose) | 2164 | if (verbose) |
1542 | print_verbose_header(); | 2165 | print_verbose_header(); |
2166 | |||
2167 | if (verbose) | ||
2168 | for_all_cpus(print_epb, ODD_COUNTERS); | ||
2169 | |||
2170 | if (verbose) | ||
2171 | for_all_cpus(print_rapl, ODD_COUNTERS); | ||
2172 | |||
2173 | for_all_cpus(set_temperature_target, ODD_COUNTERS); | ||
2174 | |||
2175 | if (verbose) | ||
2176 | for_all_cpus(print_thermal, ODD_COUNTERS); | ||
1543 | } | 2177 | } |
1544 | 2178 | ||
1545 | int fork_it(char **argv) | 2179 | int fork_it(char **argv) |
1546 | { | 2180 | { |
1547 | pid_t child_pid; | 2181 | pid_t child_pid; |
2182 | int status; | ||
1548 | 2183 | ||
1549 | for_all_cpus(get_counters, EVEN_COUNTERS); | 2184 | status = for_all_cpus(get_counters, EVEN_COUNTERS); |
2185 | if (status) | ||
2186 | exit(status); | ||
1550 | /* clear affinity side-effect of get_counters() */ | 2187 | /* clear affinity side-effect of get_counters() */ |
1551 | sched_setaffinity(0, cpu_present_setsize, cpu_present_set); | 2188 | sched_setaffinity(0, cpu_present_setsize, cpu_present_set); |
1552 | gettimeofday(&tv_even, (struct timezone *)NULL); | 2189 | gettimeofday(&tv_even, (struct timezone *)NULL); |
@@ -1556,7 +2193,6 @@ int fork_it(char **argv) | |||
1556 | /* child */ | 2193 | /* child */ |
1557 | execvp(argv[0], argv); | 2194 | execvp(argv[0], argv); |
1558 | } else { | 2195 | } else { |
1559 | int status; | ||
1560 | 2196 | ||
1561 | /* parent */ | 2197 | /* parent */ |
1562 | if (child_pid == -1) { | 2198 | if (child_pid == -1) { |
@@ -1568,7 +2204,7 @@ int fork_it(char **argv) | |||
1568 | signal(SIGQUIT, SIG_IGN); | 2204 | signal(SIGQUIT, SIG_IGN); |
1569 | if (waitpid(child_pid, &status, 0) == -1) { | 2205 | if (waitpid(child_pid, &status, 0) == -1) { |
1570 | perror("wait"); | 2206 | perror("wait"); |
1571 | exit(1); | 2207 | exit(status); |
1572 | } | 2208 | } |
1573 | } | 2209 | } |
1574 | /* | 2210 | /* |
@@ -1585,7 +2221,7 @@ int fork_it(char **argv) | |||
1585 | 2221 | ||
1586 | fprintf(stderr, "%.6f sec\n", tv_delta.tv_sec + tv_delta.tv_usec/1000000.0); | 2222 | fprintf(stderr, "%.6f sec\n", tv_delta.tv_sec + tv_delta.tv_usec/1000000.0); |
1587 | 2223 | ||
1588 | return 0; | 2224 | return status; |
1589 | } | 2225 | } |
1590 | 2226 | ||
1591 | void cmdline(int argc, char **argv) | 2227 | void cmdline(int argc, char **argv) |
@@ -1594,7 +2230,7 @@ void cmdline(int argc, char **argv) | |||
1594 | 2230 | ||
1595 | progname = argv[0]; | 2231 | progname = argv[0]; |
1596 | 2232 | ||
1597 | while ((opt = getopt(argc, argv, "+pPSvisc:sC:m:M:")) != -1) { | 2233 | while ((opt = getopt(argc, argv, "+pPSvi:sc:sC:m:M:RT:")) != -1) { |
1598 | switch (opt) { | 2234 | switch (opt) { |
1599 | case 'p': | 2235 | case 'p': |
1600 | show_core_only++; | 2236 | show_core_only++; |
@@ -1614,9 +2250,6 @@ void cmdline(int argc, char **argv) | |||
1614 | case 'c': | 2250 | case 'c': |
1615 | sscanf(optarg, "%x", &extra_delta_offset32); | 2251 | sscanf(optarg, "%x", &extra_delta_offset32); |
1616 | break; | 2252 | break; |
1617 | case 's': | ||
1618 | extra_delta_offset32 = 0x34; /* SMI counter */ | ||
1619 | break; | ||
1620 | case 'C': | 2253 | case 'C': |
1621 | sscanf(optarg, "%x", &extra_delta_offset64); | 2254 | sscanf(optarg, "%x", &extra_delta_offset64); |
1622 | break; | 2255 | break; |
@@ -1626,6 +2259,12 @@ void cmdline(int argc, char **argv) | |||
1626 | case 'M': | 2259 | case 'M': |
1627 | sscanf(optarg, "%x", &extra_msr_offset64); | 2260 | sscanf(optarg, "%x", &extra_msr_offset64); |
1628 | break; | 2261 | break; |
2262 | case 'R': | ||
2263 | rapl_verbose++; | ||
2264 | break; | ||
2265 | case 'T': | ||
2266 | tcc_activation_temp_override = atoi(optarg); | ||
2267 | break; | ||
1629 | default: | 2268 | default: |
1630 | usage(); | 2269 | usage(); |
1631 | } | 2270 | } |
@@ -1636,8 +2275,8 @@ int main(int argc, char **argv) | |||
1636 | { | 2275 | { |
1637 | cmdline(argc, argv); | 2276 | cmdline(argc, argv); |
1638 | 2277 | ||
1639 | if (verbose > 1) | 2278 | if (verbose) |
1640 | fprintf(stderr, "turbostat v2.1 October 6, 2012" | 2279 | fprintf(stderr, "turbostat v3.2 February 11, 2013" |
1641 | " - Len Brown <lenb@kernel.org>\n"); | 2280 | " - Len Brown <lenb@kernel.org>\n"); |
1642 | 2281 | ||
1643 | turbostat_init(); | 2282 | turbostat_init(); |
diff --git a/tools/power/x86/x86_energy_perf_policy/Makefile b/tools/power/x86/x86_energy_perf_policy/Makefile index f458237fdd79..971c9ffdcb50 100644 --- a/tools/power/x86/x86_energy_perf_policy/Makefile +++ b/tools/power/x86/x86_energy_perf_policy/Makefile | |||
@@ -1,8 +1,10 @@ | |||
1 | DESTDIR ?= | ||
2 | |||
1 | x86_energy_perf_policy : x86_energy_perf_policy.c | 3 | x86_energy_perf_policy : x86_energy_perf_policy.c |
2 | 4 | ||
3 | clean : | 5 | clean : |
4 | rm -f x86_energy_perf_policy | 6 | rm -f x86_energy_perf_policy |
5 | 7 | ||
6 | install : | 8 | install : |
7 | install x86_energy_perf_policy /usr/bin/ | 9 | install x86_energy_perf_policy ${DESTDIR}/usr/bin/ |
8 | install x86_energy_perf_policy.8 /usr/share/man/man8/ | 10 | install x86_energy_perf_policy.8 ${DESTDIR}/usr/share/man/man8/ |
diff --git a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c index 33c5c7ee148f..40b3e5482f8a 100644 --- a/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c +++ b/tools/power/x86/x86_energy_perf_policy/x86_energy_perf_policy.c | |||
@@ -289,7 +289,7 @@ void for_every_cpu(void (func)(int)) | |||
289 | "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", | 289 | "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", |
290 | &cpu); | 290 | &cpu); |
291 | if (retval != 1) | 291 | if (retval != 1) |
292 | return; | 292 | break; |
293 | 293 | ||
294 | func(cpu); | 294 | func(cpu); |
295 | } | 295 | } |
diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include index 96ce80a3743b..2964b96aa55f 100644 --- a/tools/scripts/Makefile.include +++ b/tools/scripts/Makefile.include | |||
@@ -1,8 +1,11 @@ | |||
1 | ifeq ("$(origin O)", "command line") | 1 | ifeq ($(origin O), command line) |
2 | dummy := $(if $(shell test -d $(O) || echo $(O)),$(error O=$(O) does not exist),) | 2 | dummy := $(if $(shell test -d $(O) || echo $(O)),$(error O=$(O) does not exist),) |
3 | ABSOLUTE_O := $(shell cd $(O) ; pwd) | 3 | ABSOLUTE_O := $(shell cd $(O) ; pwd) |
4 | OUTPUT := $(ABSOLUTE_O)/ | 4 | OUTPUT := $(ABSOLUTE_O)/$(if $(subdir),$(subdir)/) |
5 | COMMAND_O := O=$(ABSOLUTE_O) | 5 | COMMAND_O := O=$(ABSOLUTE_O) |
6 | ifeq ($(objtree),) | ||
7 | objtree := $(O) | ||
8 | endif | ||
6 | endif | 9 | endif |
7 | 10 | ||
8 | ifneq ($(OUTPUT),) | 11 | ifneq ($(OUTPUT),) |
@@ -41,7 +44,16 @@ else | |||
41 | NO_SUBDIR = : | 44 | NO_SUBDIR = : |
42 | endif | 45 | endif |
43 | 46 | ||
44 | QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir | 47 | # |
48 | # Define a callable command for descending to a new directory | ||
49 | # | ||
50 | # Call by doing: $(call descend,directory[,target]) | ||
51 | # | ||
52 | descend = \ | ||
53 | +mkdir -p $(OUTPUT)$(1) && \ | ||
54 | $(MAKE) $(COMMAND_O) subdir=$(if $(subdir),$(subdir)/$(1),$(1)) $(PRINT_DIR) -C $(1) $(2) | ||
55 | |||
56 | QUIET_SUBDIR0 = +$(MAKE) $(COMMAND_O) -C # space to separate -C and subdir | ||
45 | QUIET_SUBDIR1 = | 57 | QUIET_SUBDIR1 = |
46 | 58 | ||
47 | ifneq ($(findstring $(MAKEFLAGS),s),s) | 59 | ifneq ($(findstring $(MAKEFLAGS),s),s) |
@@ -56,5 +68,10 @@ ifndef V | |||
56 | $(MAKE) $(PRINT_DIR) -C $$subdir | 68 | $(MAKE) $(PRINT_DIR) -C $$subdir |
57 | QUIET_FLEX = @echo ' ' FLEX $@; | 69 | QUIET_FLEX = @echo ' ' FLEX $@; |
58 | QUIET_BISON = @echo ' ' BISON $@; | 70 | QUIET_BISON = @echo ' ' BISON $@; |
71 | |||
72 | descend = \ | ||
73 | @echo ' ' DESCEND $(1); \ | ||
74 | mkdir -p $(OUTPUT)$(1) && \ | ||
75 | $(MAKE) $(COMMAND_O) subdir=$(if $(subdir),$(subdir)/$(1),$(1)) $(PRINT_DIR) -C $(1) $(2) | ||
59 | endif | 76 | endif |
60 | endif | 77 | endif |
diff --git a/tools/testing/ktest/examples/include/patchcheck.conf b/tools/testing/ktest/examples/include/patchcheck.conf index 339d3e1700ff..0eb0a5ac77da 100644 --- a/tools/testing/ktest/examples/include/patchcheck.conf +++ b/tools/testing/ktest/examples/include/patchcheck.conf | |||
@@ -14,6 +14,16 @@ | |||
14 | PATCH_START := HEAD~3 | 14 | PATCH_START := HEAD~3 |
15 | PATCH_END := HEAD | 15 | PATCH_END := HEAD |
16 | 16 | ||
17 | # Use the oldconfig if build_type wasn't defined | ||
18 | DEFAULTS IF NOT DEFINED BUILD_TYPE | ||
19 | DO_BUILD_TYPE := oldconfig | ||
20 | |||
21 | DEFAULTS ELSE | ||
22 | DO_BUILD_TYPE := ${BUILD_TYPE} | ||
23 | |||
24 | DEFAULTS | ||
25 | |||
26 | |||
17 | # Change PATCH_CHECKOUT to be the branch you want to test. The test will | 27 | # Change PATCH_CHECKOUT to be the branch you want to test. The test will |
18 | # do a git checkout of this branch before starting. Obviously both | 28 | # do a git checkout of this branch before starting. Obviously both |
19 | # PATCH_START and PATCH_END must be in this branch (and PATCH_START must | 29 | # PATCH_START and PATCH_END must be in this branch (and PATCH_START must |
@@ -43,6 +53,31 @@ PATCH_TEST_TYPE := boot | |||
43 | # (space delimited) | 53 | # (space delimited) |
44 | #IGNORE_WARNINGS = 39eaf7ef884dcc44f7ff1bac803ca2a1dcf43544 6edb2a8a385f0cdef51dae37ff23e74d76d8a6ce | 54 | #IGNORE_WARNINGS = 39eaf7ef884dcc44f7ff1bac803ca2a1dcf43544 6edb2a8a385f0cdef51dae37ff23e74d76d8a6ce |
45 | 55 | ||
56 | # Instead of just checking for warnings to files that are changed | ||
57 | # it can be advantageous to check for any new warnings. If a | ||
58 | # header file is changed, it could cause a warning in a file not | ||
59 | # touched by the commit. To detect these kinds of warnings, you | ||
60 | # can use the WARNINGS_FILE option. | ||
61 | # | ||
62 | # If the variable CREATE_WARNINGS_FILE is set, this config will | ||
63 | # enable the WARNINGS_FILE during the patchcheck test. Also, | ||
64 | # before running the patchcheck test, it will create the | ||
65 | # warnings file. | ||
66 | # | ||
67 | DEFAULTS IF DEFINED CREATE_WARNINGS_FILE | ||
68 | WARNINGS_FILE = ${OUTPUT_DIR}/warnings_file | ||
69 | |||
70 | TEST_START IF DEFINED CREATE_WARNINGS_FILE | ||
71 | # WARNINGS_FILE is already set by the DEFAULTS above | ||
72 | TEST_TYPE = make_warnings_file | ||
73 | # Checkout the commit before the patches to test, | ||
74 | # and record all the warnings that exist before the patches | ||
75 | # to test are added | ||
76 | CHECKOUT = ${PATCHCHECK_START}~1 | ||
77 | # Force a full build | ||
78 | BUILD_NOCLEAN = 0 | ||
79 | BUILD_TYPE = ${DO_BUILD_TYPE} | ||
80 | |||
46 | # If you are running a multi test, and the test failed on the first | 81 | # If you are running a multi test, and the test failed on the first |
47 | # test but on, say the 5th patch. If you want to restart on the | 82 | # test but on, say the 5th patch. If you want to restart on the |
48 | # fifth patch, set PATCH_START1. This will make the first test start | 83 | # fifth patch, set PATCH_START1. This will make the first test start |
@@ -61,6 +96,7 @@ PATCHCHECK_TYPE = ${PATCH_TEST_TYPE} | |||
61 | PATCHCHECK_START = ${PATCH_START1} | 96 | PATCHCHECK_START = ${PATCH_START1} |
62 | PATCHCHECK_END = ${PATCH_END} | 97 | PATCHCHECK_END = ${PATCH_END} |
63 | CHECKOUT = ${PATCH_CHECKOUT} | 98 | CHECKOUT = ${PATCH_CHECKOUT} |
99 | BUILD_TYPE = ${DO_BUILD_TYPE} | ||
64 | 100 | ||
65 | TEST_START IF ${TEST} == patchcheck && ${MULTI} | 101 | TEST_START IF ${TEST} == patchcheck && ${MULTI} |
66 | TEST_TYPE = patchcheck | 102 | TEST_TYPE = patchcheck |
@@ -72,3 +108,4 @@ PATCHCHECK_END = ${PATCH_END} | |||
72 | CHECKOUT = ${PATCH_CHECKOUT} | 108 | CHECKOUT = ${PATCH_CHECKOUT} |
73 | # Use multi to test different compilers? | 109 | # Use multi to test different compilers? |
74 | MAKE_CMD = CC=gcc-4.5.1 make | 110 | MAKE_CMD = CC=gcc-4.5.1 make |
111 | BUILD_TYPE = ${DO_BUILD_TYPE} | ||
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index c7ba7614061b..4e67d52eb3a2 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl | |||
@@ -53,6 +53,9 @@ my %default = ( | |||
53 | "STOP_AFTER_FAILURE" => 60, | 53 | "STOP_AFTER_FAILURE" => 60, |
54 | "STOP_TEST_AFTER" => 600, | 54 | "STOP_TEST_AFTER" => 600, |
55 | "MAX_MONITOR_WAIT" => 1800, | 55 | "MAX_MONITOR_WAIT" => 1800, |
56 | "GRUB_REBOOT" => "grub2-reboot", | ||
57 | "SYSLINUX" => "extlinux", | ||
58 | "SYSLINUX_PATH" => "/boot/extlinux", | ||
56 | 59 | ||
57 | # required, and we will ask users if they don't have them but we keep the default | 60 | # required, and we will ask users if they don't have them but we keep the default |
58 | # value something that is common. | 61 | # value something that is common. |
@@ -105,7 +108,12 @@ my $scp_to_target; | |||
105 | my $scp_to_target_install; | 108 | my $scp_to_target_install; |
106 | my $power_off; | 109 | my $power_off; |
107 | my $grub_menu; | 110 | my $grub_menu; |
111 | my $grub_file; | ||
108 | my $grub_number; | 112 | my $grub_number; |
113 | my $grub_reboot; | ||
114 | my $syslinux; | ||
115 | my $syslinux_path; | ||
116 | my $syslinux_label; | ||
109 | my $target; | 117 | my $target; |
110 | my $make; | 118 | my $make; |
111 | my $pre_install; | 119 | my $pre_install; |
@@ -118,6 +126,7 @@ my $start_minconfig_defined; | |||
118 | my $output_minconfig; | 126 | my $output_minconfig; |
119 | my $minconfig_type; | 127 | my $minconfig_type; |
120 | my $use_output_minconfig; | 128 | my $use_output_minconfig; |
129 | my $warnings_file; | ||
121 | my $ignore_config; | 130 | my $ignore_config; |
122 | my $ignore_errors; | 131 | my $ignore_errors; |
123 | my $addconfig; | 132 | my $addconfig; |
@@ -185,6 +194,9 @@ my $patchcheck_end; | |||
185 | # which would require more options. | 194 | # which would require more options. |
186 | my $buildonly = 1; | 195 | my $buildonly = 1; |
187 | 196 | ||
197 | # tell build not to worry about warnings, even when WARNINGS_FILE is set | ||
198 | my $warnings_ok = 0; | ||
199 | |||
188 | # set when creating a new config | 200 | # set when creating a new config |
189 | my $newconfig = 0; | 201 | my $newconfig = 0; |
190 | 202 | ||
@@ -227,11 +239,17 @@ my %option_map = ( | |||
227 | "START_MIN_CONFIG" => \$start_minconfig, | 239 | "START_MIN_CONFIG" => \$start_minconfig, |
228 | "MIN_CONFIG_TYPE" => \$minconfig_type, | 240 | "MIN_CONFIG_TYPE" => \$minconfig_type, |
229 | "USE_OUTPUT_MIN_CONFIG" => \$use_output_minconfig, | 241 | "USE_OUTPUT_MIN_CONFIG" => \$use_output_minconfig, |
242 | "WARNINGS_FILE" => \$warnings_file, | ||
230 | "IGNORE_CONFIG" => \$ignore_config, | 243 | "IGNORE_CONFIG" => \$ignore_config, |
231 | "TEST" => \$run_test, | 244 | "TEST" => \$run_test, |
232 | "ADD_CONFIG" => \$addconfig, | 245 | "ADD_CONFIG" => \$addconfig, |
233 | "REBOOT_TYPE" => \$reboot_type, | 246 | "REBOOT_TYPE" => \$reboot_type, |
234 | "GRUB_MENU" => \$grub_menu, | 247 | "GRUB_MENU" => \$grub_menu, |
248 | "GRUB_FILE" => \$grub_file, | ||
249 | "GRUB_REBOOT" => \$grub_reboot, | ||
250 | "SYSLINUX" => \$syslinux, | ||
251 | "SYSLINUX_PATH" => \$syslinux_path, | ||
252 | "SYSLINUX_LABEL" => \$syslinux_label, | ||
235 | "PRE_INSTALL" => \$pre_install, | 253 | "PRE_INSTALL" => \$pre_install, |
236 | "POST_INSTALL" => \$post_install, | 254 | "POST_INSTALL" => \$post_install, |
237 | "NO_INSTALL" => \$no_install, | 255 | "NO_INSTALL" => \$no_install, |
@@ -368,7 +386,7 @@ EOF | |||
368 | ; | 386 | ; |
369 | $config_help{"REBOOT_TYPE"} = << "EOF" | 387 | $config_help{"REBOOT_TYPE"} = << "EOF" |
370 | Way to reboot the box to the test kernel. | 388 | Way to reboot the box to the test kernel. |
371 | Only valid options so far are "grub" and "script". | 389 | Only valid options so far are "grub", "grub2", "syslinux", and "script". |
372 | 390 | ||
373 | If you specify grub, it will assume grub version 1 | 391 | If you specify grub, it will assume grub version 1 |
374 | and will search in /boot/grub/menu.lst for the title \$GRUB_MENU | 392 | and will search in /boot/grub/menu.lst for the title \$GRUB_MENU |
@@ -378,11 +396,19 @@ $config_help{"REBOOT_TYPE"} = << "EOF" | |||
378 | 396 | ||
379 | The entry in /boot/grub/menu.lst must be entered in manually. | 397 | The entry in /boot/grub/menu.lst must be entered in manually. |
380 | The test will not modify that file. | 398 | The test will not modify that file. |
399 | |||
400 | If you specify grub2, then you also need to specify both \$GRUB_MENU | ||
401 | and \$GRUB_FILE. | ||
402 | |||
403 | If you specify syslinux, then you may use SYSLINUX to define the syslinux | ||
404 | command (defaults to extlinux), and SYSLINUX_PATH to specify the path to | ||
405 | the syslinux install (defaults to /boot/extlinux). But you have to specify | ||
406 | SYSLINUX_LABEL to define the label to boot to for the test kernel. | ||
381 | EOF | 407 | EOF |
382 | ; | 408 | ; |
383 | $config_help{"GRUB_MENU"} = << "EOF" | 409 | $config_help{"GRUB_MENU"} = << "EOF" |
384 | The grub title name for the test kernel to boot | 410 | The grub title name for the test kernel to boot |
385 | (Only mandatory if REBOOT_TYPE = grub) | 411 | (Only mandatory if REBOOT_TYPE = grub or grub2) |
386 | 412 | ||
387 | Note, ktest.pl will not update the grub menu.lst, you need to | 413 | Note, ktest.pl will not update the grub menu.lst, you need to |
388 | manually add an option for the test. ktest.pl will search | 414 | manually add an option for the test. ktest.pl will search |
@@ -393,6 +419,22 @@ $config_help{"GRUB_MENU"} = << "EOF" | |||
393 | title Test Kernel | 419 | title Test Kernel |
394 | kernel vmlinuz-test | 420 | kernel vmlinuz-test |
395 | GRUB_MENU = Test Kernel | 421 | GRUB_MENU = Test Kernel |
422 | |||
423 | For grub2, a search of \$GRUB_FILE is performed for the lines | ||
424 | that begin with "menuentry". It will not detect submenus. The | ||
425 | menu must be a non-nested menu. Add the quotes used in the menu | ||
426 | to guarantee your selection, as the first menuentry with the content | ||
427 | of \$GRUB_MENU that is found will be used. | ||
428 | EOF | ||
429 | ; | ||
430 | $config_help{"GRUB_FILE"} = << "EOF" | ||
431 | If grub2 is used, the full path for the grub.cfg file is placed | ||
432 | here. Use something like /boot/grub2/grub.cfg to search. | ||
433 | EOF | ||
434 | ; | ||
435 | $config_help{"SYSLINUX_LABEL"} = << "EOF" | ||
436 | If syslinux is used, the label that boots the target kernel must | ||
437 | be specified with SYSLINUX_LABEL. | ||
396 | EOF | 438 | EOF |
397 | ; | 439 | ; |
398 | $config_help{"REBOOT_SCRIPT"} = << "EOF" | 440 | $config_help{"REBOOT_SCRIPT"} = << "EOF" |
@@ -521,6 +563,15 @@ sub get_ktest_configs { | |||
521 | if ($rtype eq "grub") { | 563 | if ($rtype eq "grub") { |
522 | get_ktest_config("GRUB_MENU"); | 564 | get_ktest_config("GRUB_MENU"); |
523 | } | 565 | } |
566 | |||
567 | if ($rtype eq "grub2") { | ||
568 | get_ktest_config("GRUB_MENU"); | ||
569 | get_ktest_config("GRUB_FILE"); | ||
570 | } | ||
571 | |||
572 | if ($rtype eq "syslinux") { | ||
573 | get_ktest_config("SYSLINUX_LABEL"); | ||
574 | } | ||
524 | } | 575 | } |
525 | 576 | ||
526 | sub process_variables { | 577 | sub process_variables { |
@@ -573,6 +624,18 @@ sub set_value { | |||
573 | # Note if a test is something other than build, then we | 624 | # Note if a test is something other than build, then we |
574 | # will need other manditory options. | 625 | # will need other manditory options. |
575 | if ($prvalue ne "install") { | 626 | if ($prvalue ne "install") { |
627 | # for bisect, we need to check BISECT_TYPE | ||
628 | if ($prvalue ne "bisect") { | ||
629 | $buildonly = 0; | ||
630 | } | ||
631 | } else { | ||
632 | # install still limits some manditory options. | ||
633 | $buildonly = 2; | ||
634 | } | ||
635 | } | ||
636 | |||
637 | if ($buildonly && $lvalue =~ /^BISECT_TYPE(\[.*\])?$/ && $prvalue ne "build") { | ||
638 | if ($prvalue ne "install") { | ||
576 | $buildonly = 0; | 639 | $buildonly = 0; |
577 | } else { | 640 | } else { |
578 | # install still limits some manditory options. | 641 | # install still limits some manditory options. |
@@ -1016,7 +1079,7 @@ sub read_config { | |||
1016 | } | 1079 | } |
1017 | 1080 | ||
1018 | sub __eval_option { | 1081 | sub __eval_option { |
1019 | my ($option, $i) = @_; | 1082 | my ($name, $option, $i) = @_; |
1020 | 1083 | ||
1021 | # Add space to evaluate the character before $ | 1084 | # Add space to evaluate the character before $ |
1022 | $option = " $option"; | 1085 | $option = " $option"; |
@@ -1048,7 +1111,11 @@ sub __eval_option { | |||
1048 | my $o = "$var\[$i\]"; | 1111 | my $o = "$var\[$i\]"; |
1049 | my $parento = "$var\[$parent\]"; | 1112 | my $parento = "$var\[$parent\]"; |
1050 | 1113 | ||
1051 | if (defined($opt{$o})) { | 1114 | # If a variable contains itself, use the default var |
1115 | if (($var eq $name) && defined($opt{$var})) { | ||
1116 | $o = $opt{$var}; | ||
1117 | $retval = "$retval$o"; | ||
1118 | } elsif (defined($opt{$o})) { | ||
1052 | $o = $opt{$o}; | 1119 | $o = $opt{$o}; |
1053 | $retval = "$retval$o"; | 1120 | $retval = "$retval$o"; |
1054 | } elsif ($repeated && defined($opt{$parento})) { | 1121 | } elsif ($repeated && defined($opt{$parento})) { |
@@ -1072,7 +1139,7 @@ sub __eval_option { | |||
1072 | } | 1139 | } |
1073 | 1140 | ||
1074 | sub eval_option { | 1141 | sub eval_option { |
1075 | my ($option, $i) = @_; | 1142 | my ($name, $option, $i) = @_; |
1076 | 1143 | ||
1077 | my $prev = ""; | 1144 | my $prev = ""; |
1078 | 1145 | ||
@@ -1088,7 +1155,7 @@ sub eval_option { | |||
1088 | "Check for recursive variables\n"; | 1155 | "Check for recursive variables\n"; |
1089 | } | 1156 | } |
1090 | $prev = $option; | 1157 | $prev = $option; |
1091 | $option = __eval_option($option, $i); | 1158 | $option = __eval_option($name, $option, $i); |
1092 | } | 1159 | } |
1093 | 1160 | ||
1094 | return $option; | 1161 | return $option; |
@@ -1123,6 +1190,9 @@ sub wait_for_monitor; | |||
1123 | sub reboot { | 1190 | sub reboot { |
1124 | my ($time) = @_; | 1191 | my ($time) = @_; |
1125 | 1192 | ||
1193 | # Make sure everything has been written to disk | ||
1194 | run_ssh("sync"); | ||
1195 | |||
1126 | if (defined($time)) { | 1196 | if (defined($time)) { |
1127 | start_monitor; | 1197 | start_monitor; |
1128 | # flush out current monitor | 1198 | # flush out current monitor |
@@ -1142,11 +1212,24 @@ sub reboot { | |||
1142 | } | 1212 | } |
1143 | 1213 | ||
1144 | if (defined($time)) { | 1214 | if (defined($time)) { |
1145 | if (wait_for_monitor($time, $reboot_success_line)) { | 1215 | |
1216 | # We only want to get to the new kernel, don't fail | ||
1217 | # if we stumble over a call trace. | ||
1218 | my $save_ignore_errors = $ignore_errors; | ||
1219 | $ignore_errors = 1; | ||
1220 | |||
1221 | # Look for the good kernel to boot | ||
1222 | if (wait_for_monitor($time, "Linux version")) { | ||
1146 | # reboot got stuck? | 1223 | # reboot got stuck? |
1147 | doprint "Reboot did not finish. Forcing power cycle\n"; | 1224 | doprint "Reboot did not finish. Forcing power cycle\n"; |
1148 | run_command "$power_cycle"; | 1225 | run_command "$power_cycle"; |
1149 | } | 1226 | } |
1227 | |||
1228 | $ignore_errors = $save_ignore_errors; | ||
1229 | |||
1230 | # Still need to wait for the reboot to finish | ||
1231 | wait_for_monitor($time, $reboot_success_line); | ||
1232 | |||
1150 | end_monitor; | 1233 | end_monitor; |
1151 | } | 1234 | } |
1152 | } | 1235 | } |
@@ -1230,6 +1313,7 @@ sub start_monitor { | |||
1230 | } | 1313 | } |
1231 | 1314 | ||
1232 | sub end_monitor { | 1315 | sub end_monitor { |
1316 | return if (!defined $console); | ||
1233 | if (--$monitor_cnt) { | 1317 | if (--$monitor_cnt) { |
1234 | return; | 1318 | return; |
1235 | } | 1319 | } |
@@ -1452,8 +1536,44 @@ sub run_scp_mod { | |||
1452 | return run_scp($src, $dst, $cp_scp); | 1536 | return run_scp($src, $dst, $cp_scp); |
1453 | } | 1537 | } |
1454 | 1538 | ||
1539 | sub get_grub2_index { | ||
1540 | |||
1541 | return if (defined($grub_number)); | ||
1542 | |||
1543 | doprint "Find grub2 menu ... "; | ||
1544 | $grub_number = -1; | ||
1545 | |||
1546 | my $ssh_grub = $ssh_exec; | ||
1547 | $ssh_grub =~ s,\$SSH_COMMAND,cat $grub_file,g; | ||
1548 | |||
1549 | open(IN, "$ssh_grub |") | ||
1550 | or die "unable to get $grub_file"; | ||
1551 | |||
1552 | my $found = 0; | ||
1553 | |||
1554 | while (<IN>) { | ||
1555 | if (/^menuentry.*$grub_menu/) { | ||
1556 | $grub_number++; | ||
1557 | $found = 1; | ||
1558 | last; | ||
1559 | } elsif (/^menuentry\s/) { | ||
1560 | $grub_number++; | ||
1561 | } | ||
1562 | } | ||
1563 | close(IN); | ||
1564 | |||
1565 | die "Could not find '$grub_menu' in $grub_file on $machine" | ||
1566 | if (!$found); | ||
1567 | doprint "$grub_number\n"; | ||
1568 | } | ||
1569 | |||
1455 | sub get_grub_index { | 1570 | sub get_grub_index { |
1456 | 1571 | ||
1572 | if ($reboot_type eq "grub2") { | ||
1573 | get_grub2_index; | ||
1574 | return; | ||
1575 | } | ||
1576 | |||
1457 | if ($reboot_type ne "grub") { | 1577 | if ($reboot_type ne "grub") { |
1458 | return; | 1578 | return; |
1459 | } | 1579 | } |
@@ -1500,7 +1620,7 @@ sub wait_for_input | |||
1500 | 1620 | ||
1501 | $rin = ''; | 1621 | $rin = ''; |
1502 | vec($rin, fileno($fp), 1) = 1; | 1622 | vec($rin, fileno($fp), 1) = 1; |
1503 | $ready = select($rin, undef, undef, $time); | 1623 | ($ready, $time) = select($rin, undef, undef, $time); |
1504 | 1624 | ||
1505 | $line = ""; | 1625 | $line = ""; |
1506 | 1626 | ||
@@ -1524,6 +1644,10 @@ sub reboot_to { | |||
1524 | 1644 | ||
1525 | if ($reboot_type eq "grub") { | 1645 | if ($reboot_type eq "grub") { |
1526 | run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch)'"; | 1646 | run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch)'"; |
1647 | } elsif ($reboot_type eq "grub2") { | ||
1648 | run_ssh "$grub_reboot $grub_number"; | ||
1649 | } elsif ($reboot_type eq "syslinux") { | ||
1650 | run_ssh "$syslinux --once \\\"$syslinux_label\\\" $syslinux_path"; | ||
1527 | } elsif (defined $reboot_script) { | 1651 | } elsif (defined $reboot_script) { |
1528 | run_command "$reboot_script"; | 1652 | run_command "$reboot_script"; |
1529 | } | 1653 | } |
@@ -1718,6 +1842,14 @@ sub do_post_install { | |||
1718 | dodie "Failed to run post install"; | 1842 | dodie "Failed to run post install"; |
1719 | } | 1843 | } |
1720 | 1844 | ||
1845 | # Sometimes the reboot fails, and will hang. We try to ssh to the box | ||
1846 | # and if we fail, we force another reboot, that should powercycle it. | ||
1847 | sub test_booted { | ||
1848 | if (!run_ssh "echo testing connection") { | ||
1849 | reboot $sleep_time; | ||
1850 | } | ||
1851 | } | ||
1852 | |||
1721 | sub install { | 1853 | sub install { |
1722 | 1854 | ||
1723 | return if ($no_install); | 1855 | return if ($no_install); |
@@ -1730,6 +1862,8 @@ sub install { | |||
1730 | 1862 | ||
1731 | my $cp_target = eval_kernel_version $target_image; | 1863 | my $cp_target = eval_kernel_version $target_image; |
1732 | 1864 | ||
1865 | test_booted; | ||
1866 | |||
1733 | run_scp_install "$outputdir/$build_target", "$cp_target" or | 1867 | run_scp_install "$outputdir/$build_target", "$cp_target" or |
1734 | dodie "failed to copy image"; | 1868 | dodie "failed to copy image"; |
1735 | 1869 | ||
@@ -1792,23 +1926,102 @@ sub get_version { | |||
1792 | 1926 | ||
1793 | sub start_monitor_and_boot { | 1927 | sub start_monitor_and_boot { |
1794 | # Make sure the stable kernel has finished booting | 1928 | # Make sure the stable kernel has finished booting |
1795 | start_monitor; | 1929 | |
1796 | wait_for_monitor 5; | 1930 | # Install bisects, don't need console |
1797 | end_monitor; | 1931 | if (defined $console) { |
1932 | start_monitor; | ||
1933 | wait_for_monitor 5; | ||
1934 | end_monitor; | ||
1935 | } | ||
1798 | 1936 | ||
1799 | get_grub_index; | 1937 | get_grub_index; |
1800 | get_version; | 1938 | get_version; |
1801 | install; | 1939 | install; |
1802 | 1940 | ||
1803 | start_monitor; | 1941 | start_monitor if (defined $console); |
1804 | return monitor; | 1942 | return monitor; |
1805 | } | 1943 | } |
1806 | 1944 | ||
1945 | my $check_build_re = ".*:.*(warning|error|Error):.*"; | ||
1946 | my $utf8_quote = "\\x{e2}\\x{80}(\\x{98}|\\x{99})"; | ||
1947 | |||
1948 | sub process_warning_line { | ||
1949 | my ($line) = @_; | ||
1950 | |||
1951 | chomp $line; | ||
1952 | |||
1953 | # for distcc heterogeneous systems, some compilers | ||
1954 | # do things differently causing warning lines | ||
1955 | # to be slightly different. This makes an attempt | ||
1956 | # to fixe those issues. | ||
1957 | |||
1958 | # chop off the index into the line | ||
1959 | # using distcc, some compilers give different indexes | ||
1960 | # depending on white space | ||
1961 | $line =~ s/^(\s*\S+:\d+:)\d+/$1/; | ||
1962 | |||
1963 | # Some compilers use UTF-8 extended for quotes and some don't. | ||
1964 | $line =~ s/$utf8_quote/'/g; | ||
1965 | |||
1966 | return $line; | ||
1967 | } | ||
1968 | |||
1969 | # Read buildlog and check against warnings file for any | ||
1970 | # new warnings. | ||
1971 | # | ||
1972 | # Returns 1 if OK | ||
1973 | # 0 otherwise | ||
1807 | sub check_buildlog { | 1974 | sub check_buildlog { |
1975 | return 1 if (!defined $warnings_file); | ||
1976 | |||
1977 | my %warnings_list; | ||
1978 | |||
1979 | # Failed builds should not reboot the target | ||
1980 | my $save_no_reboot = $no_reboot; | ||
1981 | $no_reboot = 1; | ||
1982 | |||
1983 | if (-f $warnings_file) { | ||
1984 | open(IN, $warnings_file) or | ||
1985 | dodie "Error opening $warnings_file"; | ||
1986 | |||
1987 | while (<IN>) { | ||
1988 | if (/$check_build_re/) { | ||
1989 | my $warning = process_warning_line $_; | ||
1990 | |||
1991 | $warnings_list{$warning} = 1; | ||
1992 | } | ||
1993 | } | ||
1994 | close(IN); | ||
1995 | } | ||
1996 | |||
1997 | # If warnings file didn't exist, and WARNINGS_FILE exist, | ||
1998 | # then we fail on any warning! | ||
1999 | |||
2000 | open(IN, $buildlog) or dodie "Can't open $buildlog"; | ||
2001 | while (<IN>) { | ||
2002 | if (/$check_build_re/) { | ||
2003 | my $warning = process_warning_line $_; | ||
2004 | |||
2005 | if (!defined $warnings_list{$warning}) { | ||
2006 | fail "New warning found (not in $warnings_file)\n$_\n"; | ||
2007 | $no_reboot = $save_no_reboot; | ||
2008 | return 0; | ||
2009 | } | ||
2010 | } | ||
2011 | } | ||
2012 | $no_reboot = $save_no_reboot; | ||
2013 | close(IN); | ||
2014 | } | ||
2015 | |||
2016 | sub check_patch_buildlog { | ||
1808 | my ($patch) = @_; | 2017 | my ($patch) = @_; |
1809 | 2018 | ||
1810 | my @files = `git show $patch | diffstat -l`; | 2019 | my @files = `git show $patch | diffstat -l`; |
1811 | 2020 | ||
2021 | foreach my $file (@files) { | ||
2022 | chomp $file; | ||
2023 | } | ||
2024 | |||
1812 | open(IN, "git show $patch |") or | 2025 | open(IN, "git show $patch |") or |
1813 | dodie "failed to show $patch"; | 2026 | dodie "failed to show $patch"; |
1814 | while (<IN>) { | 2027 | while (<IN>) { |
@@ -1877,10 +2090,14 @@ sub make_oldconfig { | |||
1877 | 2090 | ||
1878 | if (!run_command "$make olddefconfig") { | 2091 | if (!run_command "$make olddefconfig") { |
1879 | # Perhaps olddefconfig doesn't exist in this version of the kernel | 2092 | # Perhaps olddefconfig doesn't exist in this version of the kernel |
1880 | # try a yes '' | oldconfig | 2093 | # try oldnoconfig |
1881 | doprint "olddefconfig failed, trying yes '' | make oldconfig\n"; | 2094 | doprint "olddefconfig failed, trying make oldnoconfig\n"; |
1882 | run_command "yes '' | $make oldconfig" or | 2095 | if (!run_command "$make oldnoconfig") { |
1883 | dodie "failed make config oldconfig"; | 2096 | doprint "oldnoconfig failed, trying yes '' | make oldconfig\n"; |
2097 | # try a yes '' | oldconfig | ||
2098 | run_command "yes '' | $make oldconfig" or | ||
2099 | dodie "failed make config oldconfig"; | ||
2100 | } | ||
1884 | } | 2101 | } |
1885 | } | 2102 | } |
1886 | 2103 | ||
@@ -2952,11 +3169,13 @@ sub patchcheck { | |||
2952 | build "oldconfig" or return 0; | 3169 | build "oldconfig" or return 0; |
2953 | } | 3170 | } |
2954 | 3171 | ||
2955 | 3172 | # No need to do per patch checking if warnings file exists | |
2956 | if (!defined($ignored_warnings{$sha1})) { | 3173 | if (!defined($warnings_file) && !defined($ignored_warnings{$sha1})) { |
2957 | check_buildlog $sha1 or return 0; | 3174 | check_patch_buildlog $sha1 or return 0; |
2958 | } | 3175 | } |
2959 | 3176 | ||
3177 | check_buildlog or return 0; | ||
3178 | |||
2960 | next if ($type eq "build"); | 3179 | next if ($type eq "build"); |
2961 | 3180 | ||
2962 | my $failed = 0; | 3181 | my $failed = 0; |
@@ -3514,6 +3733,39 @@ sub make_min_config { | |||
3514 | return 1; | 3733 | return 1; |
3515 | } | 3734 | } |
3516 | 3735 | ||
3736 | sub make_warnings_file { | ||
3737 | my ($i) = @_; | ||
3738 | |||
3739 | if (!defined($warnings_file)) { | ||
3740 | dodie "Must define WARNINGS_FILE for make_warnings_file test"; | ||
3741 | } | ||
3742 | |||
3743 | if ($build_type eq "nobuild") { | ||
3744 | dodie "BUILD_TYPE can not be 'nobuild' for make_warnings_file test"; | ||
3745 | } | ||
3746 | |||
3747 | build $build_type or dodie "Failed to build"; | ||
3748 | |||
3749 | open(OUT, ">$warnings_file") or dodie "Can't create $warnings_file"; | ||
3750 | |||
3751 | open(IN, $buildlog) or dodie "Can't open $buildlog"; | ||
3752 | while (<IN>) { | ||
3753 | |||
3754 | # Some compilers use UTF-8 extended for quotes | ||
3755 | # for distcc heterogeneous systems, this causes issues | ||
3756 | s/$utf8_quote/'/g; | ||
3757 | |||
3758 | if (/$check_build_re/) { | ||
3759 | print OUT; | ||
3760 | } | ||
3761 | } | ||
3762 | close(IN); | ||
3763 | |||
3764 | close(OUT); | ||
3765 | |||
3766 | success $i; | ||
3767 | } | ||
3768 | |||
3517 | $#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; | 3769 | $#ARGV < 1 or die "ktest.pl version: $VERSION\n usage: ktest.pl config-file\n"; |
3518 | 3770 | ||
3519 | if ($#ARGV == 0) { | 3771 | if ($#ARGV == 0) { |
@@ -3559,7 +3811,7 @@ EOF | |||
3559 | read_config $ktest_config; | 3811 | read_config $ktest_config; |
3560 | 3812 | ||
3561 | if (defined($opt{"LOG_FILE"})) { | 3813 | if (defined($opt{"LOG_FILE"})) { |
3562 | $opt{"LOG_FILE"} = eval_option($opt{"LOG_FILE"}, -1); | 3814 | $opt{"LOG_FILE"} = eval_option("LOG_FILE", $opt{"LOG_FILE"}, -1); |
3563 | } | 3815 | } |
3564 | 3816 | ||
3565 | # Append any configs entered in manually to the config file. | 3817 | # Append any configs entered in manually to the config file. |
@@ -3636,7 +3888,7 @@ sub set_test_option { | |||
3636 | my $option = __set_test_option($name, $i); | 3888 | my $option = __set_test_option($name, $i); |
3637 | return $option if (!defined($option)); | 3889 | return $option if (!defined($option)); |
3638 | 3890 | ||
3639 | return eval_option($option, $i); | 3891 | return eval_option($name, $option, $i); |
3640 | } | 3892 | } |
3641 | 3893 | ||
3642 | # First we need to do is the builds | 3894 | # First we need to do is the builds |
@@ -3700,6 +3952,11 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
3700 | $target = "$ssh_user\@$machine"; | 3952 | $target = "$ssh_user\@$machine"; |
3701 | if ($reboot_type eq "grub") { | 3953 | if ($reboot_type eq "grub") { |
3702 | dodie "GRUB_MENU not defined" if (!defined($grub_menu)); | 3954 | dodie "GRUB_MENU not defined" if (!defined($grub_menu)); |
3955 | } elsif ($reboot_type eq "grub2") { | ||
3956 | dodie "GRUB_MENU not defined" if (!defined($grub_menu)); | ||
3957 | dodie "GRUB_FILE not defined" if (!defined($grub_file)); | ||
3958 | } elsif ($reboot_type eq "syslinux") { | ||
3959 | dodie "SYSLINUX_LABEL not defined" if (!defined($syslinux_label)); | ||
3703 | } | 3960 | } |
3704 | } | 3961 | } |
3705 | 3962 | ||
@@ -3710,9 +3967,9 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
3710 | $run_type = $bisect_type; | 3967 | $run_type = $bisect_type; |
3711 | } elsif ($test_type eq "config_bisect") { | 3968 | } elsif ($test_type eq "config_bisect") { |
3712 | $run_type = $config_bisect_type; | 3969 | $run_type = $config_bisect_type; |
3713 | } | 3970 | } elsif ($test_type eq "make_min_config") { |
3714 | 3971 | $run_type = ""; | |
3715 | if ($test_type eq "make_min_config") { | 3972 | } elsif ($test_type eq "make_warnings_file") { |
3716 | $run_type = ""; | 3973 | $run_type = ""; |
3717 | } | 3974 | } |
3718 | 3975 | ||
@@ -3769,10 +4026,15 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
3769 | } elsif ($test_type eq "make_min_config") { | 4026 | } elsif ($test_type eq "make_min_config") { |
3770 | make_min_config $i; | 4027 | make_min_config $i; |
3771 | next; | 4028 | next; |
4029 | } elsif ($test_type eq "make_warnings_file") { | ||
4030 | $no_reboot = 1; | ||
4031 | make_warnings_file $i; | ||
4032 | next; | ||
3772 | } | 4033 | } |
3773 | 4034 | ||
3774 | if ($build_type ne "nobuild") { | 4035 | if ($build_type ne "nobuild") { |
3775 | build $build_type or next; | 4036 | build $build_type or next; |
4037 | check_buildlog or next; | ||
3776 | } | 4038 | } |
3777 | 4039 | ||
3778 | if ($test_type eq "install") { | 4040 | if ($test_type eq "install") { |
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index de28a0a3b8fc..0a290fb4cd5e 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf | |||
@@ -332,8 +332,18 @@ | |||
332 | # from other linux builds on the system. | 332 | # from other linux builds on the system. |
333 | #LOCALVERSION = -test | 333 | #LOCALVERSION = -test |
334 | 334 | ||
335 | # For REBOOT_TYPE = grub2, you must specify where the grub.cfg | ||
336 | # file is. This is the file that is searched to find the menu | ||
337 | # option to boot to with GRUB_REBOOT | ||
338 | #GRUB_FILE = /boot/grub2/grub.cfg | ||
339 | |||
340 | # The tool for REBOOT_TYPE = grub2 to set the next reboot kernel | ||
341 | # to boot into (one shot mode). | ||
342 | # (default grub2_reboot) | ||
343 | #GRUB_REBOOT = grub2_reboot | ||
344 | |||
335 | # The grub title name for the test kernel to boot | 345 | # The grub title name for the test kernel to boot |
336 | # (Only mandatory if REBOOT_TYPE = grub) | 346 | # (Only mandatory if REBOOT_TYPE = grub or grub2) |
337 | # | 347 | # |
338 | # Note, ktest.pl will not update the grub menu.lst, you need to | 348 | # Note, ktest.pl will not update the grub menu.lst, you need to |
339 | # manually add an option for the test. ktest.pl will search | 349 | # manually add an option for the test. ktest.pl will search |
@@ -343,8 +353,33 @@ | |||
343 | # For example, if in the /boot/grub/menu.lst the test kernel title has: | 353 | # For example, if in the /boot/grub/menu.lst the test kernel title has: |
344 | # title Test Kernel | 354 | # title Test Kernel |
345 | # kernel vmlinuz-test | 355 | # kernel vmlinuz-test |
356 | # | ||
357 | # For grub2, a search of top level "menuentry"s are done. No | ||
358 | # submenu is searched. The menu is found by searching for the | ||
359 | # contents of GRUB_MENU in the line that starts with "menuentry". | ||
360 | # You may want to include the quotes around the option. For example: | ||
361 | # for: menuentry 'Test Kernel' | ||
362 | # do a: GRUB_MENU = 'Test Kernel' | ||
363 | # For customizing, add your entry in /etc/grub.d/40_custom. | ||
364 | # | ||
346 | #GRUB_MENU = Test Kernel | 365 | #GRUB_MENU = Test Kernel |
347 | 366 | ||
367 | # For REBOOT_TYPE = syslinux, the name of the syslinux executable | ||
368 | # (on the target) to use to set up the next reboot to boot the | ||
369 | # test kernel. | ||
370 | # (default extlinux) | ||
371 | #SYSLINUX = syslinux | ||
372 | |||
373 | # For REBOOT_TYPE = syslinux, the path that is passed to to the | ||
374 | # syslinux command where syslinux is installed. | ||
375 | # (default /boot/extlinux) | ||
376 | #SYSLINUX_PATH = /boot/syslinux | ||
377 | |||
378 | # For REBOOT_TYPE = syslinux, the syslinux label that references the | ||
379 | # test kernel in the syslinux config file. | ||
380 | # (default undefined) | ||
381 | #SYSLINUX_LABEL = "test-kernel" | ||
382 | |||
348 | # A script to reboot the target into the test kernel | 383 | # A script to reboot the target into the test kernel |
349 | # This and SWITCH_TO_TEST are about the same, except | 384 | # This and SWITCH_TO_TEST are about the same, except |
350 | # SWITCH_TO_TEST is run even for REBOOT_TYPE = grub. | 385 | # SWITCH_TO_TEST is run even for REBOOT_TYPE = grub. |
@@ -497,7 +532,7 @@ | |||
497 | #POST_BUILD_DIE = 1 | 532 | #POST_BUILD_DIE = 1 |
498 | 533 | ||
499 | # Way to reboot the box to the test kernel. | 534 | # Way to reboot the box to the test kernel. |
500 | # Only valid options so far are "grub" and "script" | 535 | # Only valid options so far are "grub", "grub2", "syslinux" and "script" |
501 | # (default grub) | 536 | # (default grub) |
502 | # If you specify grub, it will assume grub version 1 | 537 | # If you specify grub, it will assume grub version 1 |
503 | # and will search in /boot/grub/menu.lst for the title $GRUB_MENU | 538 | # and will search in /boot/grub/menu.lst for the title $GRUB_MENU |
@@ -505,6 +540,13 @@ | |||
505 | # your setup, then specify "script" and have a command or script | 540 | # your setup, then specify "script" and have a command or script |
506 | # specified in REBOOT_SCRIPT to boot to the target. | 541 | # specified in REBOOT_SCRIPT to boot to the target. |
507 | # | 542 | # |
543 | # For REBOOT_TYPE = grub2, you must define both GRUB_MENU and | ||
544 | # GRUB_FILE. | ||
545 | # | ||
546 | # For REBOOT_TYPE = syslinux, you must define SYSLINUX_LABEL, and | ||
547 | # perhaps modify SYSLINUX (default extlinux) and SYSLINUX_PATH | ||
548 | # (default /boot/extlinux) | ||
549 | # | ||
508 | # The entry in /boot/grub/menu.lst must be entered in manually. | 550 | # The entry in /boot/grub/menu.lst must be entered in manually. |
509 | # The test will not modify that file. | 551 | # The test will not modify that file. |
510 | #REBOOT_TYPE = grub | 552 | #REBOOT_TYPE = grub |
@@ -751,6 +793,20 @@ | |||
751 | # Example for a virtual guest call "Guest". | 793 | # Example for a virtual guest call "Guest". |
752 | #POWER_OFF = virsh destroy Guest | 794 | #POWER_OFF = virsh destroy Guest |
753 | 795 | ||
796 | # To have the build fail on "new" warnings, create a file that | ||
797 | # contains a list of all known warnings (they must match exactly | ||
798 | # to the line with 'warning:', 'error:' or 'Error:'. If the option | ||
799 | # WARNINGS_FILE is set, then that file will be read, and if the | ||
800 | # build detects a warning, it will examine this file and if the | ||
801 | # warning does not exist in it, it will fail the build. | ||
802 | # | ||
803 | # Note, if this option is defined to a file that does not exist | ||
804 | # then any warning will fail the build. | ||
805 | # (see make_warnings_file below) | ||
806 | # | ||
807 | # (optional, default undefined) | ||
808 | #WARNINGS_FILE = ${OUTPUT_DIR}/warnings_file | ||
809 | |||
754 | # The way to execute a command on the target | 810 | # The way to execute a command on the target |
755 | # (default ssh $SSH_USER@$MACHINE $SSH_COMMAND";) | 811 | # (default ssh $SSH_USER@$MACHINE $SSH_COMMAND";) |
756 | # The variables SSH_USER, MACHINE and SSH_COMMAND are defined | 812 | # The variables SSH_USER, MACHINE and SSH_COMMAND are defined |
@@ -1180,3 +1236,33 @@ | |||
1180 | # MIN_CONFIG_TYPE = test | 1236 | # MIN_CONFIG_TYPE = test |
1181 | # TEST = ssh ${USER}@${MACHINE} echo hi | 1237 | # TEST = ssh ${USER}@${MACHINE} echo hi |
1182 | # | 1238 | # |
1239 | # | ||
1240 | # | ||
1241 | # | ||
1242 | # For TEST_TYPE = make_warnings_file | ||
1243 | # | ||
1244 | # If you want the build to fail when a new warning is discovered | ||
1245 | # you set the WARNINGS_FILE to point to a file of known warnings. | ||
1246 | # | ||
1247 | # The test "make_warnings_file" will let you create a new warnings | ||
1248 | # file before you run other tests, like patchcheck. | ||
1249 | # | ||
1250 | # What this test does is to run just a build, you still need to | ||
1251 | # specify BUILD_TYPE to tell the test what type of config to use. | ||
1252 | # A BUILD_TYPE of nobuild will fail this test. | ||
1253 | # | ||
1254 | # The test will do the build and scan for all warnings. Any warning | ||
1255 | # it discovers will be saved in the WARNINGS_FILE (required) option. | ||
1256 | # | ||
1257 | # It is recommended (but not necessary) to make sure BUILD_NOCLEAN is | ||
1258 | # off, so that a full build is done (make mrproper is performed). | ||
1259 | # That way, all warnings will be captured. | ||
1260 | # | ||
1261 | # Example: | ||
1262 | # | ||
1263 | # TEST_TYPE = make_warnings_file | ||
1264 | # WARNINGS_FILE = ${OUTPUT_DIR} | ||
1265 | # BUILD_TYPE = useconfig:oldconfig | ||
1266 | # CHECKOUT = v3.8 | ||
1267 | # BUILD_NOCLEAN = 0 | ||
1268 | # | ||
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 43480149119e..3cc0ad7ae863 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
@@ -1,4 +1,10 @@ | |||
1 | TARGETS = breakpoints kcmp mqueue vm cpu-hotplug memory-hotplug epoll | 1 | TARGETS = breakpoints |
2 | TARGETS += kcmp | ||
3 | TARGETS += mqueue | ||
4 | TARGETS += vm | ||
5 | TARGETS += cpu-hotplug | ||
6 | TARGETS += memory-hotplug | ||
7 | TARGETS += efivarfs | ||
2 | 8 | ||
3 | all: | 9 | all: |
4 | for TARGET in $(TARGETS); do \ | 10 | for TARGET in $(TARGETS); do \ |
diff --git a/tools/testing/selftests/README.txt b/tools/testing/selftests/README.txt new file mode 100644 index 000000000000..5e2faf9c55d3 --- /dev/null +++ b/tools/testing/selftests/README.txt | |||
@@ -0,0 +1,42 @@ | |||
1 | Linux Kernel Selftests | ||
2 | |||
3 | The kernel contains a set of "self tests" under the tools/testing/selftests/ | ||
4 | directory. These are intended to be small unit tests to exercise individual | ||
5 | code paths in the kernel. | ||
6 | |||
7 | Running the selftests | ||
8 | ===================== | ||
9 | |||
10 | To build the tests: | ||
11 | |||
12 | $ make -C tools/testing/selftests | ||
13 | |||
14 | |||
15 | To run the tests: | ||
16 | |||
17 | $ make -C tools/testing/selftests run_tests | ||
18 | |||
19 | - note that some tests will require root privileges. | ||
20 | |||
21 | |||
22 | To run only tests targetted for a single subsystem: | ||
23 | |||
24 | $ make -C tools/testing/selftests TARGETS=cpu-hotplug run_tests | ||
25 | |||
26 | See the top-level tools/testing/selftests/Makefile for the list of all possible | ||
27 | targets. | ||
28 | |||
29 | |||
30 | Contributing new tests | ||
31 | ====================== | ||
32 | |||
33 | In general, the rules for for selftests are | ||
34 | |||
35 | * Do as much as you can if you're not root; | ||
36 | |||
37 | * Don't take too long; | ||
38 | |||
39 | * Don't break the build on any architecture, and | ||
40 | |||
41 | * Don't cause the top-level "make run_tests" to fail if your feature is | ||
42 | unconfigured. | ||
diff --git a/tools/testing/selftests/breakpoints/Makefile b/tools/testing/selftests/breakpoints/Makefile index 931278035f5c..e18b42b254af 100644 --- a/tools/testing/selftests/breakpoints/Makefile +++ b/tools/testing/selftests/breakpoints/Makefile | |||
@@ -17,7 +17,7 @@ else | |||
17 | endif | 17 | endif |
18 | 18 | ||
19 | run_tests: | 19 | run_tests: |
20 | ./breakpoint_test | 20 | @./breakpoint_test || echo "breakpoints selftests: [FAIL]" |
21 | 21 | ||
22 | clean: | 22 | clean: |
23 | rm -fr breakpoint_test | 23 | rm -fr breakpoint_test |
diff --git a/tools/testing/selftests/cpu-hotplug/Makefile b/tools/testing/selftests/cpu-hotplug/Makefile index 7c9c20ff578a..12657a5e4bf9 100644 --- a/tools/testing/selftests/cpu-hotplug/Makefile +++ b/tools/testing/selftests/cpu-hotplug/Makefile | |||
@@ -1,6 +1,6 @@ | |||
1 | all: | 1 | all: |
2 | 2 | ||
3 | run_tests: | 3 | run_tests: |
4 | ./on-off-test.sh | 4 | @./on-off-test.sh || echo "cpu-hotplug selftests: [FAIL]" |
5 | 5 | ||
6 | clean: | 6 | clean: |
diff --git a/tools/testing/selftests/efivarfs/Makefile b/tools/testing/selftests/efivarfs/Makefile new file mode 100644 index 000000000000..29e8c6bc81b0 --- /dev/null +++ b/tools/testing/selftests/efivarfs/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | CC = $(CROSS_COMPILE)gcc | ||
2 | CFLAGS = -Wall | ||
3 | |||
4 | test_objs = open-unlink create-read | ||
5 | |||
6 | all: $(test_objs) | ||
7 | |||
8 | run_tests: all | ||
9 | @/bin/bash ./efivarfs.sh || echo "efivarfs selftests: [FAIL]" | ||
10 | |||
11 | clean: | ||
12 | rm -f $(test_objs) | ||
diff --git a/tools/testing/selftests/efivarfs/create-read.c b/tools/testing/selftests/efivarfs/create-read.c new file mode 100644 index 000000000000..7feef1880968 --- /dev/null +++ b/tools/testing/selftests/efivarfs/create-read.c | |||
@@ -0,0 +1,38 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdint.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <unistd.h> | ||
5 | #include <sys/types.h> | ||
6 | #include <sys/stat.h> | ||
7 | #include <fcntl.h> | ||
8 | #include <errno.h> | ||
9 | #include <string.h> | ||
10 | |||
11 | int main(int argc, char **argv) | ||
12 | { | ||
13 | const char *path; | ||
14 | char buf[4]; | ||
15 | int fd, rc; | ||
16 | |||
17 | if (argc < 2) { | ||
18 | fprintf(stderr, "usage: %s <path>\n", argv[0]); | ||
19 | return EXIT_FAILURE; | ||
20 | } | ||
21 | |||
22 | path = argv[1]; | ||
23 | |||
24 | /* create a test variable */ | ||
25 | fd = open(path, O_RDWR | O_CREAT, 0600); | ||
26 | if (fd < 0) { | ||
27 | perror("open(O_WRONLY)"); | ||
28 | return EXIT_FAILURE; | ||
29 | } | ||
30 | |||
31 | rc = read(fd, buf, sizeof(buf)); | ||
32 | if (rc != 0) { | ||
33 | fprintf(stderr, "Reading a new var should return EOF\n"); | ||
34 | return EXIT_FAILURE; | ||
35 | } | ||
36 | |||
37 | return EXIT_SUCCESS; | ||
38 | } | ||
diff --git a/tools/testing/selftests/efivarfs/efivarfs.sh b/tools/testing/selftests/efivarfs/efivarfs.sh new file mode 100644 index 000000000000..77edcdcc016b --- /dev/null +++ b/tools/testing/selftests/efivarfs/efivarfs.sh | |||
@@ -0,0 +1,198 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | efivarfs_mount=/sys/firmware/efi/efivars | ||
4 | test_guid=210be57c-9849-4fc7-a635-e6382d1aec27 | ||
5 | |||
6 | check_prereqs() | ||
7 | { | ||
8 | local msg="skip all tests:" | ||
9 | |||
10 | if [ $UID != 0 ]; then | ||
11 | echo $msg must be run as root >&2 | ||
12 | exit 0 | ||
13 | fi | ||
14 | |||
15 | if ! grep -q "^\S\+ $efivarfs_mount efivarfs" /proc/mounts; then | ||
16 | echo $msg efivarfs is not mounted on $efivarfs_mount >&2 | ||
17 | exit 0 | ||
18 | fi | ||
19 | } | ||
20 | |||
21 | run_test() | ||
22 | { | ||
23 | local test="$1" | ||
24 | |||
25 | echo "--------------------" | ||
26 | echo "running $test" | ||
27 | echo "--------------------" | ||
28 | |||
29 | if [ "$(type -t $test)" = 'function' ]; then | ||
30 | ( $test ) | ||
31 | else | ||
32 | ( ./$test ) | ||
33 | fi | ||
34 | |||
35 | if [ $? -ne 0 ]; then | ||
36 | echo " [FAIL]" | ||
37 | rc=1 | ||
38 | else | ||
39 | echo " [PASS]" | ||
40 | fi | ||
41 | } | ||
42 | |||
43 | test_create() | ||
44 | { | ||
45 | local attrs='\x07\x00\x00\x00' | ||
46 | local file=$efivarfs_mount/$FUNCNAME-$test_guid | ||
47 | |||
48 | printf "$attrs\x00" > $file | ||
49 | |||
50 | if [ ! -e $file ]; then | ||
51 | echo "$file couldn't be created" >&2 | ||
52 | exit 1 | ||
53 | fi | ||
54 | |||
55 | if [ $(stat -c %s $file) -ne 5 ]; then | ||
56 | echo "$file has invalid size" >&2 | ||
57 | exit 1 | ||
58 | fi | ||
59 | } | ||
60 | |||
61 | test_create_empty() | ||
62 | { | ||
63 | local file=$efivarfs_mount/$FUNCNAME-$test_guid | ||
64 | |||
65 | : > $file | ||
66 | |||
67 | if [ ! -e $file ]; then | ||
68 | echo "$file can not be created without writing" >&2 | ||
69 | exit 1 | ||
70 | fi | ||
71 | } | ||
72 | |||
73 | test_create_read() | ||
74 | { | ||
75 | local file=$efivarfs_mount/$FUNCNAME-$test_guid | ||
76 | ./create-read $file | ||
77 | } | ||
78 | |||
79 | test_delete() | ||
80 | { | ||
81 | local attrs='\x07\x00\x00\x00' | ||
82 | local file=$efivarfs_mount/$FUNCNAME-$test_guid | ||
83 | |||
84 | printf "$attrs\x00" > $file | ||
85 | |||
86 | if [ ! -e $file ]; then | ||
87 | echo "$file couldn't be created" >&2 | ||
88 | exit 1 | ||
89 | fi | ||
90 | |||
91 | rm $file | ||
92 | |||
93 | if [ -e $file ]; then | ||
94 | echo "$file couldn't be deleted" >&2 | ||
95 | exit 1 | ||
96 | fi | ||
97 | |||
98 | } | ||
99 | |||
100 | # test that we can remove a variable by issuing a write with only | ||
101 | # attributes specified | ||
102 | test_zero_size_delete() | ||
103 | { | ||
104 | local attrs='\x07\x00\x00\x00' | ||
105 | local file=$efivarfs_mount/$FUNCNAME-$test_guid | ||
106 | |||
107 | printf "$attrs\x00" > $file | ||
108 | |||
109 | if [ ! -e $file ]; then | ||
110 | echo "$file does not exist" >&2 | ||
111 | exit 1 | ||
112 | fi | ||
113 | |||
114 | printf "$attrs" > $file | ||
115 | |||
116 | if [ -e $file ]; then | ||
117 | echo "$file should have been deleted" >&2 | ||
118 | exit 1 | ||
119 | fi | ||
120 | } | ||
121 | |||
122 | test_open_unlink() | ||
123 | { | ||
124 | local file=$efivarfs_mount/$FUNCNAME-$test_guid | ||
125 | ./open-unlink $file | ||
126 | } | ||
127 | |||
128 | # test that we can create a range of filenames | ||
129 | test_valid_filenames() | ||
130 | { | ||
131 | local attrs='\x07\x00\x00\x00' | ||
132 | local ret=0 | ||
133 | |||
134 | local file_list="abc dump-type0-11-1-1362436005 1234 -" | ||
135 | for f in $file_list; do | ||
136 | local file=$efivarfs_mount/$f-$test_guid | ||
137 | |||
138 | printf "$attrs\x00" > $file | ||
139 | |||
140 | if [ ! -e $file ]; then | ||
141 | echo "$file could not be created" >&2 | ||
142 | ret=1 | ||
143 | else | ||
144 | rm $file | ||
145 | fi | ||
146 | done | ||
147 | |||
148 | exit $ret | ||
149 | } | ||
150 | |||
151 | test_invalid_filenames() | ||
152 | { | ||
153 | local attrs='\x07\x00\x00\x00' | ||
154 | local ret=0 | ||
155 | |||
156 | local file_list=" | ||
157 | -1234-1234-1234-123456789abc | ||
158 | foo | ||
159 | foo-bar | ||
160 | -foo- | ||
161 | foo-barbazba-foob-foob-foob-foobarbazfoo | ||
162 | foo------------------------------------- | ||
163 | -12345678-1234-1234-1234-123456789abc | ||
164 | a-12345678=1234-1234-1234-123456789abc | ||
165 | a-12345678-1234=1234-1234-123456789abc | ||
166 | a-12345678-1234-1234=1234-123456789abc | ||
167 | a-12345678-1234-1234-1234=123456789abc | ||
168 | 1112345678-1234-1234-1234-123456789abc" | ||
169 | |||
170 | for f in $file_list; do | ||
171 | local file=$efivarfs_mount/$f | ||
172 | |||
173 | printf "$attrs\x00" 2>/dev/null > $file | ||
174 | |||
175 | if [ -e $file ]; then | ||
176 | echo "Creating $file should have failed" >&2 | ||
177 | rm $file | ||
178 | ret=1 | ||
179 | fi | ||
180 | done | ||
181 | |||
182 | exit $ret | ||
183 | } | ||
184 | |||
185 | check_prereqs | ||
186 | |||
187 | rc=0 | ||
188 | |||
189 | run_test test_create | ||
190 | run_test test_create_empty | ||
191 | run_test test_create_read | ||
192 | run_test test_delete | ||
193 | run_test test_zero_size_delete | ||
194 | run_test test_open_unlink | ||
195 | run_test test_valid_filenames | ||
196 | run_test test_invalid_filenames | ||
197 | |||
198 | exit $rc | ||
diff --git a/tools/testing/selftests/efivarfs/open-unlink.c b/tools/testing/selftests/efivarfs/open-unlink.c new file mode 100644 index 000000000000..8c0764407b3c --- /dev/null +++ b/tools/testing/selftests/efivarfs/open-unlink.c | |||
@@ -0,0 +1,63 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stdint.h> | ||
3 | #include <stdlib.h> | ||
4 | #include <unistd.h> | ||
5 | #include <sys/types.h> | ||
6 | #include <sys/stat.h> | ||
7 | #include <fcntl.h> | ||
8 | |||
9 | int main(int argc, char **argv) | ||
10 | { | ||
11 | const char *path; | ||
12 | char buf[5]; | ||
13 | int fd, rc; | ||
14 | |||
15 | if (argc < 2) { | ||
16 | fprintf(stderr, "usage: %s <path>\n", argv[0]); | ||
17 | return EXIT_FAILURE; | ||
18 | } | ||
19 | |||
20 | path = argv[1]; | ||
21 | |||
22 | /* attributes: EFI_VARIABLE_NON_VOLATILE | | ||
23 | * EFI_VARIABLE_BOOTSERVICE_ACCESS | | ||
24 | * EFI_VARIABLE_RUNTIME_ACCESS | ||
25 | */ | ||
26 | *(uint32_t *)buf = 0x7; | ||
27 | buf[4] = 0; | ||
28 | |||
29 | /* create a test variable */ | ||
30 | fd = open(path, O_WRONLY | O_CREAT); | ||
31 | if (fd < 0) { | ||
32 | perror("open(O_WRONLY)"); | ||
33 | return EXIT_FAILURE; | ||
34 | } | ||
35 | |||
36 | rc = write(fd, buf, sizeof(buf)); | ||
37 | if (rc != sizeof(buf)) { | ||
38 | perror("write"); | ||
39 | return EXIT_FAILURE; | ||
40 | } | ||
41 | |||
42 | close(fd); | ||
43 | |||
44 | fd = open(path, O_RDONLY); | ||
45 | if (fd < 0) { | ||
46 | perror("open"); | ||
47 | return EXIT_FAILURE; | ||
48 | } | ||
49 | |||
50 | if (unlink(path) < 0) { | ||
51 | perror("unlink"); | ||
52 | return EXIT_FAILURE; | ||
53 | } | ||
54 | |||
55 | rc = read(fd, buf, sizeof(buf)); | ||
56 | if (rc > 0) { | ||
57 | fprintf(stderr, "reading from an unlinked variable " | ||
58 | "shouldn't be possible\n"); | ||
59 | return EXIT_FAILURE; | ||
60 | } | ||
61 | |||
62 | return EXIT_SUCCESS; | ||
63 | } | ||
diff --git a/tools/testing/selftests/epoll/Makefile b/tools/testing/selftests/epoll/Makefile deleted file mode 100644 index 19806ed62f50..000000000000 --- a/tools/testing/selftests/epoll/Makefile +++ /dev/null | |||
@@ -1,11 +0,0 @@ | |||
1 | # Makefile for epoll selftests | ||
2 | |||
3 | all: test_epoll | ||
4 | %: %.c | ||
5 | gcc -pthread -g -o $@ $^ | ||
6 | |||
7 | run_tests: all | ||
8 | ./test_epoll | ||
9 | |||
10 | clean: | ||
11 | $(RM) test_epoll | ||
diff --git a/tools/testing/selftests/epoll/test_epoll.c b/tools/testing/selftests/epoll/test_epoll.c deleted file mode 100644 index f7525392ce84..000000000000 --- a/tools/testing/selftests/epoll/test_epoll.c +++ /dev/null | |||
@@ -1,344 +0,0 @@ | |||
1 | /* | ||
2 | * tools/testing/selftests/epoll/test_epoll.c | ||
3 | * | ||
4 | * Copyright 2012 Adobe Systems Incorporated | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * Paton J. Lewis <palewis@adobe.com> | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <errno.h> | ||
16 | #include <fcntl.h> | ||
17 | #include <pthread.h> | ||
18 | #include <stdio.h> | ||
19 | #include <stdlib.h> | ||
20 | #include <unistd.h> | ||
21 | #include <sys/epoll.h> | ||
22 | #include <sys/socket.h> | ||
23 | |||
24 | /* | ||
25 | * A pointer to an epoll_item_private structure will be stored in the epoll | ||
26 | * item's event structure so that we can get access to the epoll_item_private | ||
27 | * data after calling epoll_wait: | ||
28 | */ | ||
29 | struct epoll_item_private { | ||
30 | int index; /* Position of this struct within the epoll_items array. */ | ||
31 | int fd; | ||
32 | uint32_t events; | ||
33 | pthread_mutex_t mutex; /* Guards the following variables... */ | ||
34 | int stop; | ||
35 | int status; /* Stores any error encountered while handling item. */ | ||
36 | /* The following variable allows us to test whether we have encountered | ||
37 | a problem while attempting to cancel and delete the associated | ||
38 | event. When the test program exits, 'deleted' should be exactly | ||
39 | one. If it is greater than one, then the failed test reflects a real | ||
40 | world situation where we would have tried to access the epoll item's | ||
41 | private data after deleting it: */ | ||
42 | int deleted; | ||
43 | }; | ||
44 | |||
45 | struct epoll_item_private *epoll_items; | ||
46 | |||
47 | /* | ||
48 | * Delete the specified item from the epoll set. In a real-world secneario this | ||
49 | * is where we would free the associated data structure, but in this testing | ||
50 | * environment we retain the structure so that we can test for double-deletion: | ||
51 | */ | ||
52 | void delete_item(int index) | ||
53 | { | ||
54 | __sync_fetch_and_add(&epoll_items[index].deleted, 1); | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * A pointer to a read_thread_data structure will be passed as the argument to | ||
59 | * each read thread: | ||
60 | */ | ||
61 | struct read_thread_data { | ||
62 | int stop; | ||
63 | int status; /* Indicates any error encountered by the read thread. */ | ||
64 | int epoll_set; | ||
65 | }; | ||
66 | |||
67 | /* | ||
68 | * The function executed by the read threads: | ||
69 | */ | ||
70 | void *read_thread_function(void *function_data) | ||
71 | { | ||
72 | struct read_thread_data *thread_data = | ||
73 | (struct read_thread_data *)function_data; | ||
74 | struct epoll_event event_data; | ||
75 | struct epoll_item_private *item_data; | ||
76 | char socket_data; | ||
77 | |||
78 | /* Handle events until we encounter an error or this thread's 'stop' | ||
79 | condition is set: */ | ||
80 | while (1) { | ||
81 | int result = epoll_wait(thread_data->epoll_set, | ||
82 | &event_data, | ||
83 | 1, /* Number of desired events */ | ||
84 | 1000); /* Timeout in ms */ | ||
85 | if (result < 0) { | ||
86 | /* Breakpoints signal all threads. Ignore that while | ||
87 | debugging: */ | ||
88 | if (errno == EINTR) | ||
89 | continue; | ||
90 | thread_data->status = errno; | ||
91 | return 0; | ||
92 | } else if (thread_data->stop) | ||
93 | return 0; | ||
94 | else if (result == 0) /* Timeout */ | ||
95 | continue; | ||
96 | |||
97 | /* We need the mutex here because checking for the stop | ||
98 | condition and re-enabling the epoll item need to be done | ||
99 | together as one atomic operation when EPOLL_CTL_DISABLE is | ||
100 | available: */ | ||
101 | item_data = (struct epoll_item_private *)event_data.data.ptr; | ||
102 | pthread_mutex_lock(&item_data->mutex); | ||
103 | |||
104 | /* Remove the item from the epoll set if we want to stop | ||
105 | handling that event: */ | ||
106 | if (item_data->stop) | ||
107 | delete_item(item_data->index); | ||
108 | else { | ||
109 | /* Clear the data that was written to the other end of | ||
110 | our non-blocking socket: */ | ||
111 | do { | ||
112 | if (read(item_data->fd, &socket_data, 1) < 1) { | ||
113 | if ((errno == EAGAIN) || | ||
114 | (errno == EWOULDBLOCK)) | ||
115 | break; | ||
116 | else | ||
117 | goto error_unlock; | ||
118 | } | ||
119 | } while (item_data->events & EPOLLET); | ||
120 | |||
121 | /* The item was one-shot, so re-enable it: */ | ||
122 | event_data.events = item_data->events; | ||
123 | if (epoll_ctl(thread_data->epoll_set, | ||
124 | EPOLL_CTL_MOD, | ||
125 | item_data->fd, | ||
126 | &event_data) < 0) | ||
127 | goto error_unlock; | ||
128 | } | ||
129 | |||
130 | pthread_mutex_unlock(&item_data->mutex); | ||
131 | } | ||
132 | |||
133 | error_unlock: | ||
134 | thread_data->status = item_data->status = errno; | ||
135 | pthread_mutex_unlock(&item_data->mutex); | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * A pointer to a write_thread_data structure will be passed as the argument to | ||
141 | * the write thread: | ||
142 | */ | ||
143 | struct write_thread_data { | ||
144 | int stop; | ||
145 | int status; /* Indicates any error encountered by the write thread. */ | ||
146 | int n_fds; | ||
147 | int *fds; | ||
148 | }; | ||
149 | |||
150 | /* | ||
151 | * The function executed by the write thread. It writes a single byte to each | ||
152 | * socket in turn until the stop condition for this thread is set. If writing to | ||
153 | * a socket would block (i.e. errno was EAGAIN), we leave that socket alone for | ||
154 | * the moment and just move on to the next socket in the list. We don't care | ||
155 | * about the order in which we deliver events to the epoll set. In fact we don't | ||
156 | * care about the data we're writing to the pipes at all; we just want to | ||
157 | * trigger epoll events: | ||
158 | */ | ||
159 | void *write_thread_function(void *function_data) | ||
160 | { | ||
161 | const char data = 'X'; | ||
162 | int index; | ||
163 | struct write_thread_data *thread_data = | ||
164 | (struct write_thread_data *)function_data; | ||
165 | while (!thread_data->stop) | ||
166 | for (index = 0; | ||
167 | !thread_data->stop && (index < thread_data->n_fds); | ||
168 | ++index) | ||
169 | if ((write(thread_data->fds[index], &data, 1) < 1) && | ||
170 | (errno != EAGAIN) && | ||
171 | (errno != EWOULDBLOCK)) { | ||
172 | thread_data->status = errno; | ||
173 | return; | ||
174 | } | ||
175 | } | ||
176 | |||
177 | /* | ||
178 | * Arguments are currently ignored: | ||
179 | */ | ||
180 | int main(int argc, char **argv) | ||
181 | { | ||
182 | const int n_read_threads = 100; | ||
183 | const int n_epoll_items = 500; | ||
184 | int index; | ||
185 | int epoll_set = epoll_create1(0); | ||
186 | struct write_thread_data write_thread_data = { | ||
187 | 0, 0, n_epoll_items, malloc(n_epoll_items * sizeof(int)) | ||
188 | }; | ||
189 | struct read_thread_data *read_thread_data = | ||
190 | malloc(n_read_threads * sizeof(struct read_thread_data)); | ||
191 | pthread_t *read_threads = malloc(n_read_threads * sizeof(pthread_t)); | ||
192 | pthread_t write_thread; | ||
193 | |||
194 | printf("-----------------\n"); | ||
195 | printf("Runing test_epoll\n"); | ||
196 | printf("-----------------\n"); | ||
197 | |||
198 | epoll_items = malloc(n_epoll_items * sizeof(struct epoll_item_private)); | ||
199 | |||
200 | if (epoll_set < 0 || epoll_items == 0 || write_thread_data.fds == 0 || | ||
201 | read_thread_data == 0 || read_threads == 0) | ||
202 | goto error; | ||
203 | |||
204 | if (sysconf(_SC_NPROCESSORS_ONLN) < 2) { | ||
205 | printf("Error: please run this test on a multi-core system.\n"); | ||
206 | goto error; | ||
207 | } | ||
208 | |||
209 | /* Create the socket pairs and epoll items: */ | ||
210 | for (index = 0; index < n_epoll_items; ++index) { | ||
211 | int socket_pair[2]; | ||
212 | struct epoll_event event_data; | ||
213 | if (socketpair(AF_UNIX, | ||
214 | SOCK_STREAM | SOCK_NONBLOCK, | ||
215 | 0, | ||
216 | socket_pair) < 0) | ||
217 | goto error; | ||
218 | write_thread_data.fds[index] = socket_pair[0]; | ||
219 | epoll_items[index].index = index; | ||
220 | epoll_items[index].fd = socket_pair[1]; | ||
221 | if (pthread_mutex_init(&epoll_items[index].mutex, NULL) != 0) | ||
222 | goto error; | ||
223 | /* We always use EPOLLONESHOT because this test is currently | ||
224 | structured to demonstrate the need for EPOLL_CTL_DISABLE, | ||
225 | which only produces useful information in the EPOLLONESHOT | ||
226 | case (without EPOLLONESHOT, calling epoll_ctl with | ||
227 | EPOLL_CTL_DISABLE will never return EBUSY). If support for | ||
228 | testing events without EPOLLONESHOT is desired, it should | ||
229 | probably be implemented in a separate unit test. */ | ||
230 | epoll_items[index].events = EPOLLIN | EPOLLONESHOT; | ||
231 | if (index < n_epoll_items / 2) | ||
232 | epoll_items[index].events |= EPOLLET; | ||
233 | epoll_items[index].stop = 0; | ||
234 | epoll_items[index].status = 0; | ||
235 | epoll_items[index].deleted = 0; | ||
236 | event_data.events = epoll_items[index].events; | ||
237 | event_data.data.ptr = &epoll_items[index]; | ||
238 | if (epoll_ctl(epoll_set, | ||
239 | EPOLL_CTL_ADD, | ||
240 | epoll_items[index].fd, | ||
241 | &event_data) < 0) | ||
242 | goto error; | ||
243 | } | ||
244 | |||
245 | /* Create and start the read threads: */ | ||
246 | for (index = 0; index < n_read_threads; ++index) { | ||
247 | read_thread_data[index].stop = 0; | ||
248 | read_thread_data[index].status = 0; | ||
249 | read_thread_data[index].epoll_set = epoll_set; | ||
250 | if (pthread_create(&read_threads[index], | ||
251 | NULL, | ||
252 | read_thread_function, | ||
253 | &read_thread_data[index]) != 0) | ||
254 | goto error; | ||
255 | } | ||
256 | |||
257 | if (pthread_create(&write_thread, | ||
258 | NULL, | ||
259 | write_thread_function, | ||
260 | &write_thread_data) != 0) | ||
261 | goto error; | ||
262 | |||
263 | /* Cancel all event pollers: */ | ||
264 | #ifdef EPOLL_CTL_DISABLE | ||
265 | for (index = 0; index < n_epoll_items; ++index) { | ||
266 | pthread_mutex_lock(&epoll_items[index].mutex); | ||
267 | ++epoll_items[index].stop; | ||
268 | if (epoll_ctl(epoll_set, | ||
269 | EPOLL_CTL_DISABLE, | ||
270 | epoll_items[index].fd, | ||
271 | NULL) == 0) | ||
272 | delete_item(index); | ||
273 | else if (errno != EBUSY) { | ||
274 | pthread_mutex_unlock(&epoll_items[index].mutex); | ||
275 | goto error; | ||
276 | } | ||
277 | /* EBUSY means events were being handled; allow the other thread | ||
278 | to delete the item. */ | ||
279 | pthread_mutex_unlock(&epoll_items[index].mutex); | ||
280 | } | ||
281 | #else | ||
282 | for (index = 0; index < n_epoll_items; ++index) { | ||
283 | pthread_mutex_lock(&epoll_items[index].mutex); | ||
284 | ++epoll_items[index].stop; | ||
285 | pthread_mutex_unlock(&epoll_items[index].mutex); | ||
286 | /* Wait in case a thread running read_thread_function is | ||
287 | currently executing code between epoll_wait and | ||
288 | pthread_mutex_lock with this item. Note that a longer delay | ||
289 | would make double-deletion less likely (at the expense of | ||
290 | performance), but there is no guarantee that any delay would | ||
291 | ever be sufficient. Note also that we delete all event | ||
292 | pollers at once for testing purposes, but in a real-world | ||
293 | environment we are likely to want to be able to cancel event | ||
294 | pollers at arbitrary times. Therefore we can't improve this | ||
295 | situation by just splitting this loop into two loops | ||
296 | (i.e. signal 'stop' for all items, sleep, and then delete all | ||
297 | items). We also can't fix the problem via EPOLL_CTL_DEL | ||
298 | because that command can't prevent the case where some other | ||
299 | thread is executing read_thread_function within the region | ||
300 | mentioned above: */ | ||
301 | usleep(1); | ||
302 | pthread_mutex_lock(&epoll_items[index].mutex); | ||
303 | if (!epoll_items[index].deleted) | ||
304 | delete_item(index); | ||
305 | pthread_mutex_unlock(&epoll_items[index].mutex); | ||
306 | } | ||
307 | #endif | ||
308 | |||
309 | /* Shut down the read threads: */ | ||
310 | for (index = 0; index < n_read_threads; ++index) | ||
311 | __sync_fetch_and_add(&read_thread_data[index].stop, 1); | ||
312 | for (index = 0; index < n_read_threads; ++index) { | ||
313 | if (pthread_join(read_threads[index], NULL) != 0) | ||
314 | goto error; | ||
315 | if (read_thread_data[index].status) | ||
316 | goto error; | ||
317 | } | ||
318 | |||
319 | /* Shut down the write thread: */ | ||
320 | __sync_fetch_and_add(&write_thread_data.stop, 1); | ||
321 | if ((pthread_join(write_thread, NULL) != 0) || write_thread_data.status) | ||
322 | goto error; | ||
323 | |||
324 | /* Check for final error conditions: */ | ||
325 | for (index = 0; index < n_epoll_items; ++index) { | ||
326 | if (epoll_items[index].status != 0) | ||
327 | goto error; | ||
328 | if (pthread_mutex_destroy(&epoll_items[index].mutex) < 0) | ||
329 | goto error; | ||
330 | } | ||
331 | for (index = 0; index < n_epoll_items; ++index) | ||
332 | if (epoll_items[index].deleted != 1) { | ||
333 | printf("Error: item data deleted %1d times.\n", | ||
334 | epoll_items[index].deleted); | ||
335 | goto error; | ||
336 | } | ||
337 | |||
338 | printf("[PASS]\n"); | ||
339 | return 0; | ||
340 | |||
341 | error: | ||
342 | printf("[FAIL]\n"); | ||
343 | return errno; | ||
344 | } | ||
diff --git a/tools/testing/selftests/ipc/Makefile b/tools/testing/selftests/ipc/Makefile new file mode 100644 index 000000000000..5386fd7c43ae --- /dev/null +++ b/tools/testing/selftests/ipc/Makefile | |||
@@ -0,0 +1,25 @@ | |||
1 | uname_M := $(shell uname -m 2>/dev/null || echo not) | ||
2 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) | ||
3 | ifeq ($(ARCH),i386) | ||
4 | ARCH := X86 | ||
5 | CFLAGS := -DCONFIG_X86_32 -D__i386__ | ||
6 | endif | ||
7 | ifeq ($(ARCH),x86_64) | ||
8 | ARCH := X86 | ||
9 | CFLAGS := -DCONFIG_X86_64 -D__x86_64__ | ||
10 | endif | ||
11 | |||
12 | CFLAGS += -I../../../../usr/include/ | ||
13 | |||
14 | all: | ||
15 | ifeq ($(ARCH),X86) | ||
16 | gcc $(CFLAGS) msgque.c -o msgque_test | ||
17 | else | ||
18 | echo "Not an x86 target, can't build msgque selftest" | ||
19 | endif | ||
20 | |||
21 | run_tests: all | ||
22 | ./msgque_test | ||
23 | |||
24 | clean: | ||
25 | rm -fr ./msgque_test | ||
diff --git a/tools/testing/selftests/ipc/msgque.c b/tools/testing/selftests/ipc/msgque.c new file mode 100644 index 000000000000..d66418237d21 --- /dev/null +++ b/tools/testing/selftests/ipc/msgque.c | |||
@@ -0,0 +1,246 @@ | |||
1 | #include <stdlib.h> | ||
2 | #include <stdio.h> | ||
3 | #include <string.h> | ||
4 | #include <errno.h> | ||
5 | #include <linux/msg.h> | ||
6 | #include <fcntl.h> | ||
7 | |||
8 | #define MAX_MSG_SIZE 32 | ||
9 | |||
10 | struct msg1 { | ||
11 | int msize; | ||
12 | long mtype; | ||
13 | char mtext[MAX_MSG_SIZE]; | ||
14 | }; | ||
15 | |||
16 | #define TEST_STRING "Test sysv5 msg" | ||
17 | #define MSG_TYPE 1 | ||
18 | |||
19 | #define ANOTHER_TEST_STRING "Yet another test sysv5 msg" | ||
20 | #define ANOTHER_MSG_TYPE 26538 | ||
21 | |||
22 | struct msgque_data { | ||
23 | key_t key; | ||
24 | int msq_id; | ||
25 | int qbytes; | ||
26 | int qnum; | ||
27 | int mode; | ||
28 | struct msg1 *messages; | ||
29 | }; | ||
30 | |||
31 | int restore_queue(struct msgque_data *msgque) | ||
32 | { | ||
33 | int fd, ret, id, i; | ||
34 | char buf[32]; | ||
35 | |||
36 | fd = open("/proc/sys/kernel/msg_next_id", O_WRONLY); | ||
37 | if (fd == -1) { | ||
38 | printf("Failed to open /proc/sys/kernel/msg_next_id\n"); | ||
39 | return -errno; | ||
40 | } | ||
41 | sprintf(buf, "%d", msgque->msq_id); | ||
42 | |||
43 | ret = write(fd, buf, strlen(buf)); | ||
44 | if (ret != strlen(buf)) { | ||
45 | printf("Failed to write to /proc/sys/kernel/msg_next_id\n"); | ||
46 | return -errno; | ||
47 | } | ||
48 | |||
49 | id = msgget(msgque->key, msgque->mode | IPC_CREAT | IPC_EXCL); | ||
50 | if (id == -1) { | ||
51 | printf("Failed to create queue\n"); | ||
52 | return -errno; | ||
53 | } | ||
54 | |||
55 | if (id != msgque->msq_id) { | ||
56 | printf("Restored queue has wrong id (%d instead of %d)\n", | ||
57 | id, msgque->msq_id); | ||
58 | ret = -EFAULT; | ||
59 | goto destroy; | ||
60 | } | ||
61 | |||
62 | for (i = 0; i < msgque->qnum; i++) { | ||
63 | if (msgsnd(msgque->msq_id, &msgque->messages[i].mtype, | ||
64 | msgque->messages[i].msize, IPC_NOWAIT) != 0) { | ||
65 | printf("msgsnd failed (%m)\n"); | ||
66 | ret = -errno; | ||
67 | goto destroy; | ||
68 | }; | ||
69 | } | ||
70 | return 0; | ||
71 | |||
72 | destroy: | ||
73 | if (msgctl(id, IPC_RMID, 0)) | ||
74 | printf("Failed to destroy queue: %d\n", -errno); | ||
75 | return ret; | ||
76 | } | ||
77 | |||
78 | int check_and_destroy_queue(struct msgque_data *msgque) | ||
79 | { | ||
80 | struct msg1 message; | ||
81 | int cnt = 0, ret; | ||
82 | |||
83 | while (1) { | ||
84 | ret = msgrcv(msgque->msq_id, &message.mtype, MAX_MSG_SIZE, | ||
85 | 0, IPC_NOWAIT); | ||
86 | if (ret < 0) { | ||
87 | if (errno == ENOMSG) | ||
88 | break; | ||
89 | printf("Failed to read IPC message: %m\n"); | ||
90 | ret = -errno; | ||
91 | goto err; | ||
92 | } | ||
93 | if (ret != msgque->messages[cnt].msize) { | ||
94 | printf("Wrong message size: %d (expected %d)\n", ret, | ||
95 | msgque->messages[cnt].msize); | ||
96 | ret = -EINVAL; | ||
97 | goto err; | ||
98 | } | ||
99 | if (message.mtype != msgque->messages[cnt].mtype) { | ||
100 | printf("Wrong message type\n"); | ||
101 | ret = -EINVAL; | ||
102 | goto err; | ||
103 | } | ||
104 | if (memcmp(message.mtext, msgque->messages[cnt].mtext, ret)) { | ||
105 | printf("Wrong message content\n"); | ||
106 | ret = -EINVAL; | ||
107 | goto err; | ||
108 | } | ||
109 | cnt++; | ||
110 | } | ||
111 | |||
112 | if (cnt != msgque->qnum) { | ||
113 | printf("Wrong message number\n"); | ||
114 | ret = -EINVAL; | ||
115 | goto err; | ||
116 | } | ||
117 | |||
118 | ret = 0; | ||
119 | err: | ||
120 | if (msgctl(msgque->msq_id, IPC_RMID, 0)) { | ||
121 | printf("Failed to destroy queue: %d\n", -errno); | ||
122 | return -errno; | ||
123 | } | ||
124 | return ret; | ||
125 | } | ||
126 | |||
127 | int dump_queue(struct msgque_data *msgque) | ||
128 | { | ||
129 | struct msqid64_ds ds; | ||
130 | int kern_id; | ||
131 | int i, ret; | ||
132 | |||
133 | for (kern_id = 0; kern_id < 256; kern_id++) { | ||
134 | ret = msgctl(kern_id, MSG_STAT, &ds); | ||
135 | if (ret < 0) { | ||
136 | if (errno == -EINVAL) | ||
137 | continue; | ||
138 | printf("Failed to get stats for IPC queue with id %d\n", | ||
139 | kern_id); | ||
140 | return -errno; | ||
141 | } | ||
142 | |||
143 | if (ret == msgque->msq_id) | ||
144 | break; | ||
145 | } | ||
146 | |||
147 | msgque->messages = malloc(sizeof(struct msg1) * ds.msg_qnum); | ||
148 | if (msgque->messages == NULL) { | ||
149 | printf("Failed to get stats for IPC queue\n"); | ||
150 | return -ENOMEM; | ||
151 | } | ||
152 | |||
153 | msgque->qnum = ds.msg_qnum; | ||
154 | msgque->mode = ds.msg_perm.mode; | ||
155 | msgque->qbytes = ds.msg_qbytes; | ||
156 | |||
157 | for (i = 0; i < msgque->qnum; i++) { | ||
158 | ret = msgrcv(msgque->msq_id, &msgque->messages[i].mtype, | ||
159 | MAX_MSG_SIZE, i, IPC_NOWAIT | MSG_COPY); | ||
160 | if (ret < 0) { | ||
161 | printf("Failed to copy IPC message: %m (%d)\n", errno); | ||
162 | return -errno; | ||
163 | } | ||
164 | msgque->messages[i].msize = ret; | ||
165 | } | ||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | int fill_msgque(struct msgque_data *msgque) | ||
170 | { | ||
171 | struct msg1 msgbuf; | ||
172 | |||
173 | msgbuf.mtype = MSG_TYPE; | ||
174 | memcpy(msgbuf.mtext, TEST_STRING, sizeof(TEST_STRING)); | ||
175 | if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(TEST_STRING), | ||
176 | IPC_NOWAIT) != 0) { | ||
177 | printf("First message send failed (%m)\n"); | ||
178 | return -errno; | ||
179 | }; | ||
180 | |||
181 | msgbuf.mtype = ANOTHER_MSG_TYPE; | ||
182 | memcpy(msgbuf.mtext, ANOTHER_TEST_STRING, sizeof(ANOTHER_TEST_STRING)); | ||
183 | if (msgsnd(msgque->msq_id, &msgbuf.mtype, sizeof(ANOTHER_TEST_STRING), | ||
184 | IPC_NOWAIT) != 0) { | ||
185 | printf("Second message send failed (%m)\n"); | ||
186 | return -errno; | ||
187 | }; | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | int main(int argc, char **argv) | ||
192 | { | ||
193 | int msg, pid, err; | ||
194 | struct msgque_data msgque; | ||
195 | |||
196 | msgque.key = ftok(argv[0], 822155650); | ||
197 | if (msgque.key == -1) { | ||
198 | printf("Can't make key\n"); | ||
199 | return -errno; | ||
200 | } | ||
201 | |||
202 | msgque.msq_id = msgget(msgque.key, IPC_CREAT | IPC_EXCL | 0666); | ||
203 | if (msgque.msq_id == -1) { | ||
204 | printf("Can't create queue\n"); | ||
205 | goto err_out; | ||
206 | } | ||
207 | |||
208 | err = fill_msgque(&msgque); | ||
209 | if (err) { | ||
210 | printf("Failed to fill queue\n"); | ||
211 | goto err_destroy; | ||
212 | } | ||
213 | |||
214 | err = dump_queue(&msgque); | ||
215 | if (err) { | ||
216 | printf("Failed to dump queue\n"); | ||
217 | goto err_destroy; | ||
218 | } | ||
219 | |||
220 | err = check_and_destroy_queue(&msgque); | ||
221 | if (err) { | ||
222 | printf("Failed to check and destroy queue\n"); | ||
223 | goto err_out; | ||
224 | } | ||
225 | |||
226 | err = restore_queue(&msgque); | ||
227 | if (err) { | ||
228 | printf("Failed to restore queue\n"); | ||
229 | goto err_destroy; | ||
230 | } | ||
231 | |||
232 | err = check_and_destroy_queue(&msgque); | ||
233 | if (err) { | ||
234 | printf("Failed to test queue\n"); | ||
235 | goto err_out; | ||
236 | } | ||
237 | return 0; | ||
238 | |||
239 | err_destroy: | ||
240 | if (msgctl(msgque.msq_id, IPC_RMID, 0)) { | ||
241 | printf("Failed to destroy queue: %d\n", -errno); | ||
242 | return -errno; | ||
243 | } | ||
244 | err_out: | ||
245 | return err; | ||
246 | } | ||
diff --git a/tools/testing/selftests/kcmp/Makefile b/tools/testing/selftests/kcmp/Makefile index dc79b86ea65c..56eb5523dbb8 100644 --- a/tools/testing/selftests/kcmp/Makefile +++ b/tools/testing/selftests/kcmp/Makefile | |||
@@ -16,13 +16,13 @@ CFLAGS += -I../../../../arch/x86/include/ | |||
16 | 16 | ||
17 | all: | 17 | all: |
18 | ifeq ($(ARCH),X86) | 18 | ifeq ($(ARCH),X86) |
19 | gcc $(CFLAGS) kcmp_test.c -o run_test | 19 | gcc $(CFLAGS) kcmp_test.c -o kcmp_test |
20 | else | 20 | else |
21 | echo "Not an x86 target, can't build kcmp selftest" | 21 | echo "Not an x86 target, can't build kcmp selftest" |
22 | endif | 22 | endif |
23 | 23 | ||
24 | run-tests: all | 24 | run_tests: all |
25 | ./kcmp_test | 25 | @./kcmp_test || echo "kcmp_test: [FAIL]" |
26 | 26 | ||
27 | clean: | 27 | clean: |
28 | rm -fr ./run_test | 28 | rm -fr ./run_test |
diff --git a/tools/testing/selftests/kcmp/kcmp_test.c b/tools/testing/selftests/kcmp/kcmp_test.c index 358cc6bfa35d..fa4f1b37e045 100644 --- a/tools/testing/selftests/kcmp/kcmp_test.c +++ b/tools/testing/selftests/kcmp/kcmp_test.c | |||
@@ -72,7 +72,8 @@ int main(int argc, char **argv) | |||
72 | /* This one should return same fd */ | 72 | /* This one should return same fd */ |
73 | ret = sys_kcmp(pid1, pid2, KCMP_FILE, fd1, fd1); | 73 | ret = sys_kcmp(pid1, pid2, KCMP_FILE, fd1, fd1); |
74 | if (ret) { | 74 | if (ret) { |
75 | printf("FAIL: 0 expected but %d returned\n", ret); | 75 | printf("FAIL: 0 expected but %d returned (%s)\n", |
76 | ret, strerror(errno)); | ||
76 | ret = -1; | 77 | ret = -1; |
77 | } else | 78 | } else |
78 | printf("PASS: 0 returned as expected\n"); | 79 | printf("PASS: 0 returned as expected\n"); |
@@ -80,7 +81,8 @@ int main(int argc, char **argv) | |||
80 | /* Compare with self */ | 81 | /* Compare with self */ |
81 | ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0); | 82 | ret = sys_kcmp(pid1, pid1, KCMP_VM, 0, 0); |
82 | if (ret) { | 83 | if (ret) { |
83 | printf("FAIL: 0 expected but %li returned\n", ret); | 84 | printf("FAIL: 0 expected but %li returned (%s)\n", |
85 | ret, strerror(errno)); | ||
84 | ret = -1; | 86 | ret = -1; |
85 | } else | 87 | } else |
86 | printf("PASS: 0 returned as expected\n"); | 88 | printf("PASS: 0 returned as expected\n"); |
diff --git a/tools/testing/selftests/memory-hotplug/Makefile b/tools/testing/selftests/memory-hotplug/Makefile index 7c9c20ff578a..0f49c3f5f58d 100644 --- a/tools/testing/selftests/memory-hotplug/Makefile +++ b/tools/testing/selftests/memory-hotplug/Makefile | |||
@@ -1,6 +1,6 @@ | |||
1 | all: | 1 | all: |
2 | 2 | ||
3 | run_tests: | 3 | run_tests: |
4 | ./on-off-test.sh | 4 | @./on-off-test.sh || echo "memory-hotplug selftests: [FAIL]" |
5 | 5 | ||
6 | clean: | 6 | clean: |
diff --git a/tools/testing/selftests/mqueue/Makefile b/tools/testing/selftests/mqueue/Makefile index 54c0aad2b47c..218a122c7951 100644 --- a/tools/testing/selftests/mqueue/Makefile +++ b/tools/testing/selftests/mqueue/Makefile | |||
@@ -3,8 +3,8 @@ all: | |||
3 | gcc -O2 -lrt -lpthread -lpopt -o mq_perf_tests mq_perf_tests.c | 3 | gcc -O2 -lrt -lpthread -lpopt -o mq_perf_tests mq_perf_tests.c |
4 | 4 | ||
5 | run_tests: | 5 | run_tests: |
6 | ./mq_open_tests /test1 | 6 | @./mq_open_tests /test1 || echo "mq_open_tests: [FAIL]" |
7 | ./mq_perf_tests | 7 | @./mq_perf_tests || echo "mq_perf_tests: [FAIL]" |
8 | 8 | ||
9 | clean: | 9 | clean: |
10 | rm -f mq_open_tests mq_perf_tests | 10 | rm -f mq_open_tests mq_perf_tests |
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index b336b24aa6c0..436d2e81868b 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile | |||
@@ -1,14 +1,14 @@ | |||
1 | # Makefile for vm selftests | 1 | # Makefile for vm selftests |
2 | 2 | ||
3 | CC = $(CROSS_COMPILE)gcc | 3 | CC = $(CROSS_COMPILE)gcc |
4 | CFLAGS = -Wall -Wextra | 4 | CFLAGS = -Wall |
5 | 5 | ||
6 | all: hugepage-mmap hugepage-shm map_hugetlb | 6 | all: hugepage-mmap hugepage-shm map_hugetlb thuge-gen |
7 | %: %.c | 7 | %: %.c |
8 | $(CC) $(CFLAGS) -o $@ $^ | 8 | $(CC) $(CFLAGS) -o $@ $^ |
9 | 9 | ||
10 | run_tests: all | 10 | run_tests: all |
11 | /bin/sh ./run_vmtests | 11 | @/bin/sh ./run_vmtests || echo "vmtests: [FAIL]" |
12 | 12 | ||
13 | clean: | 13 | clean: |
14 | $(RM) hugepage-mmap hugepage-shm map_hugetlb | 14 | $(RM) hugepage-mmap hugepage-shm map_hugetlb |
diff --git a/tools/testing/selftests/vm/thuge-gen.c b/tools/testing/selftests/vm/thuge-gen.c new file mode 100644 index 000000000000..c87957295f74 --- /dev/null +++ b/tools/testing/selftests/vm/thuge-gen.c | |||
@@ -0,0 +1,254 @@ | |||
1 | /* Test selecting other page sizes for mmap/shmget. | ||
2 | |||
3 | Before running this huge pages for each huge page size must have been | ||
4 | reserved. | ||
5 | For large pages beyond MAX_ORDER (like 1GB on x86) boot options must be used. | ||
6 | Also shmmax must be increased. | ||
7 | And you need to run as root to work around some weird permissions in shm. | ||
8 | And nothing using huge pages should run in parallel. | ||
9 | When the program aborts you may need to clean up the shm segments with | ||
10 | ipcrm -m by hand, like this | ||
11 | sudo ipcs | awk '$1 == "0x00000000" {print $2}' | xargs -n1 sudo ipcrm -m | ||
12 | (warning this will remove all if someone else uses them) */ | ||
13 | |||
14 | #define _GNU_SOURCE 1 | ||
15 | #include <sys/mman.h> | ||
16 | #include <stdlib.h> | ||
17 | #include <stdio.h> | ||
18 | #include <sys/ipc.h> | ||
19 | #include <sys/shm.h> | ||
20 | #include <sys/stat.h> | ||
21 | #include <glob.h> | ||
22 | #include <assert.h> | ||
23 | #include <unistd.h> | ||
24 | #include <stdarg.h> | ||
25 | #include <string.h> | ||
26 | |||
27 | #define err(x) perror(x), exit(1) | ||
28 | |||
29 | #define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT) | ||
30 | #define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT) | ||
31 | #define MAP_HUGE_SHIFT 26 | ||
32 | #define MAP_HUGE_MASK 0x3f | ||
33 | #define MAP_HUGETLB 0x40000 | ||
34 | |||
35 | #define SHM_HUGETLB 04000 /* segment will use huge TLB pages */ | ||
36 | #define SHM_HUGE_SHIFT 26 | ||
37 | #define SHM_HUGE_MASK 0x3f | ||
38 | #define SHM_HUGE_2MB (21 << SHM_HUGE_SHIFT) | ||
39 | #define SHM_HUGE_1GB (30 << SHM_HUGE_SHIFT) | ||
40 | |||
41 | #define NUM_PAGESIZES 5 | ||
42 | |||
43 | #define NUM_PAGES 4 | ||
44 | |||
45 | #define Dprintf(fmt...) // printf(fmt) | ||
46 | |||
47 | unsigned long page_sizes[NUM_PAGESIZES]; | ||
48 | int num_page_sizes; | ||
49 | |||
50 | int ilog2(unsigned long v) | ||
51 | { | ||
52 | int l = 0; | ||
53 | while ((1UL << l) < v) | ||
54 | l++; | ||
55 | return l; | ||
56 | } | ||
57 | |||
58 | void find_pagesizes(void) | ||
59 | { | ||
60 | glob_t g; | ||
61 | int i; | ||
62 | glob("/sys/kernel/mm/hugepages/hugepages-*kB", 0, NULL, &g); | ||
63 | assert(g.gl_pathc <= NUM_PAGESIZES); | ||
64 | for (i = 0; i < g.gl_pathc; i++) { | ||
65 | sscanf(g.gl_pathv[i], "/sys/kernel/mm/hugepages/hugepages-%lukB", | ||
66 | &page_sizes[i]); | ||
67 | page_sizes[i] <<= 10; | ||
68 | printf("Found %luMB\n", page_sizes[i] >> 20); | ||
69 | } | ||
70 | num_page_sizes = g.gl_pathc; | ||
71 | globfree(&g); | ||
72 | } | ||
73 | |||
74 | unsigned long default_huge_page_size(void) | ||
75 | { | ||
76 | unsigned long hps = 0; | ||
77 | char *line = NULL; | ||
78 | size_t linelen = 0; | ||
79 | FILE *f = fopen("/proc/meminfo", "r"); | ||
80 | if (!f) | ||
81 | return 0; | ||
82 | while (getline(&line, &linelen, f) > 0) { | ||
83 | if (sscanf(line, "Hugepagesize: %lu kB", &hps) == 1) { | ||
84 | hps <<= 10; | ||
85 | break; | ||
86 | } | ||
87 | } | ||
88 | free(line); | ||
89 | return hps; | ||
90 | } | ||
91 | |||
92 | void show(unsigned long ps) | ||
93 | { | ||
94 | char buf[100]; | ||
95 | if (ps == getpagesize()) | ||
96 | return; | ||
97 | printf("%luMB: ", ps >> 20); | ||
98 | fflush(stdout); | ||
99 | snprintf(buf, sizeof buf, | ||
100 | "cat /sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", | ||
101 | ps >> 10); | ||
102 | system(buf); | ||
103 | } | ||
104 | |||
105 | unsigned long read_sysfs(int warn, char *fmt, ...) | ||
106 | { | ||
107 | char *line = NULL; | ||
108 | size_t linelen = 0; | ||
109 | char buf[100]; | ||
110 | FILE *f; | ||
111 | va_list ap; | ||
112 | unsigned long val = 0; | ||
113 | |||
114 | va_start(ap, fmt); | ||
115 | vsnprintf(buf, sizeof buf, fmt, ap); | ||
116 | va_end(ap); | ||
117 | |||
118 | f = fopen(buf, "r"); | ||
119 | if (!f) { | ||
120 | if (warn) | ||
121 | printf("missing %s\n", buf); | ||
122 | return 0; | ||
123 | } | ||
124 | if (getline(&line, &linelen, f) > 0) { | ||
125 | sscanf(line, "%lu", &val); | ||
126 | } | ||
127 | fclose(f); | ||
128 | free(line); | ||
129 | return val; | ||
130 | } | ||
131 | |||
132 | unsigned long read_free(unsigned long ps) | ||
133 | { | ||
134 | return read_sysfs(ps != getpagesize(), | ||
135 | "/sys/kernel/mm/hugepages/hugepages-%lukB/free_hugepages", | ||
136 | ps >> 10); | ||
137 | } | ||
138 | |||
139 | void test_mmap(unsigned long size, unsigned flags) | ||
140 | { | ||
141 | char *map; | ||
142 | unsigned long before, after; | ||
143 | int err; | ||
144 | |||
145 | before = read_free(size); | ||
146 | map = mmap(NULL, size*NUM_PAGES, PROT_READ|PROT_WRITE, | ||
147 | MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|flags, 0, 0); | ||
148 | |||
149 | if (map == (char *)-1) err("mmap"); | ||
150 | memset(map, 0xff, size*NUM_PAGES); | ||
151 | after = read_free(size); | ||
152 | Dprintf("before %lu after %lu diff %ld size %lu\n", | ||
153 | before, after, before - after, size); | ||
154 | assert(size == getpagesize() || (before - after) == NUM_PAGES); | ||
155 | show(size); | ||
156 | err = munmap(map, size); | ||
157 | assert(!err); | ||
158 | } | ||
159 | |||
160 | void test_shmget(unsigned long size, unsigned flags) | ||
161 | { | ||
162 | int id; | ||
163 | unsigned long before, after; | ||
164 | int err; | ||
165 | |||
166 | before = read_free(size); | ||
167 | id = shmget(IPC_PRIVATE, size * NUM_PAGES, IPC_CREAT|0600|flags); | ||
168 | if (id < 0) err("shmget"); | ||
169 | |||
170 | struct shm_info i; | ||
171 | if (shmctl(id, SHM_INFO, (void *)&i) < 0) err("shmctl"); | ||
172 | Dprintf("alloc %lu res %lu\n", i.shm_tot, i.shm_rss); | ||
173 | |||
174 | |||
175 | Dprintf("id %d\n", id); | ||
176 | char *map = shmat(id, NULL, 0600); | ||
177 | if (map == (char*)-1) err("shmat"); | ||
178 | |||
179 | shmctl(id, IPC_RMID, NULL); | ||
180 | |||
181 | memset(map, 0xff, size*NUM_PAGES); | ||
182 | after = read_free(size); | ||
183 | |||
184 | Dprintf("before %lu after %lu diff %ld size %lu\n", | ||
185 | before, after, before - after, size); | ||
186 | assert(size == getpagesize() || (before - after) == NUM_PAGES); | ||
187 | show(size); | ||
188 | err = shmdt(map); | ||
189 | assert(!err); | ||
190 | } | ||
191 | |||
192 | void sanity_checks(void) | ||
193 | { | ||
194 | int i; | ||
195 | unsigned long largest = getpagesize(); | ||
196 | |||
197 | for (i = 0; i < num_page_sizes; i++) { | ||
198 | if (page_sizes[i] > largest) | ||
199 | largest = page_sizes[i]; | ||
200 | |||
201 | if (read_free(page_sizes[i]) < NUM_PAGES) { | ||
202 | printf("Not enough huge pages for page size %lu MB, need %u\n", | ||
203 | page_sizes[i] >> 20, | ||
204 | NUM_PAGES); | ||
205 | exit(0); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | if (read_sysfs(0, "/proc/sys/kernel/shmmax") < NUM_PAGES * largest) { | ||
210 | printf("Please do echo %lu > /proc/sys/kernel/shmmax", largest * NUM_PAGES); | ||
211 | exit(0); | ||
212 | } | ||
213 | |||
214 | #if defined(__x86_64__) | ||
215 | if (largest != 1U<<30) { | ||
216 | printf("No GB pages available on x86-64\n" | ||
217 | "Please boot with hugepagesz=1G hugepages=%d\n", NUM_PAGES); | ||
218 | exit(0); | ||
219 | } | ||
220 | #endif | ||
221 | } | ||
222 | |||
223 | int main(void) | ||
224 | { | ||
225 | int i; | ||
226 | unsigned default_hps = default_huge_page_size(); | ||
227 | |||
228 | find_pagesizes(); | ||
229 | |||
230 | sanity_checks(); | ||
231 | |||
232 | for (i = 0; i < num_page_sizes; i++) { | ||
233 | unsigned long ps = page_sizes[i]; | ||
234 | int arg = ilog2(ps) << MAP_HUGE_SHIFT; | ||
235 | printf("Testing %luMB mmap with shift %x\n", ps >> 20, arg); | ||
236 | test_mmap(ps, MAP_HUGETLB | arg); | ||
237 | } | ||
238 | printf("Testing default huge mmap\n"); | ||
239 | test_mmap(default_hps, SHM_HUGETLB); | ||
240 | |||
241 | puts("Testing non-huge shmget"); | ||
242 | test_shmget(getpagesize(), 0); | ||
243 | |||
244 | for (i = 0; i < num_page_sizes; i++) { | ||
245 | unsigned long ps = page_sizes[i]; | ||
246 | int arg = ilog2(ps) << SHM_HUGE_SHIFT; | ||
247 | printf("Testing %luMB shmget with shift %x\n", ps >> 20, arg); | ||
248 | test_shmget(ps, SHM_HUGETLB | arg); | ||
249 | } | ||
250 | puts("default huge shmget"); | ||
251 | test_shmget(default_hps, SHM_HUGETLB); | ||
252 | |||
253 | return 0; | ||
254 | } | ||
diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c index 8674b9ec14f6..fe1e66b6ef40 100644 --- a/tools/usb/ffs-test.c +++ b/tools/usb/ffs-test.c | |||
@@ -38,7 +38,7 @@ | |||
38 | #include <unistd.h> | 38 | #include <unistd.h> |
39 | #include <tools/le_byteshift.h> | 39 | #include <tools/le_byteshift.h> |
40 | 40 | ||
41 | #include "../../include/linux/usb/functionfs.h" | 41 | #include "../../include/uapi/linux/usb/functionfs.h" |
42 | 42 | ||
43 | 43 | ||
44 | /******************** Little Endian Handling ********************************/ | 44 | /******************** Little Endian Handling ********************************/ |
diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c index 68d0734b2081..879f9870a6bc 100644 --- a/tools/usb/testusb.c +++ b/tools/usb/testusb.c | |||
@@ -279,8 +279,7 @@ nomem: | |||
279 | 279 | ||
280 | entry->ifnum = ifnum; | 280 | entry->ifnum = ifnum; |
281 | 281 | ||
282 | /* FIXME ask usbfs what speed; update USBDEVFS_CONNECTINFO so | 282 | /* FIXME update USBDEVFS_CONNECTINFO so it tells about high speed etc */ |
283 | * it tells about high speed etc */ | ||
284 | 283 | ||
285 | fprintf(stderr, "%s speed\t%s\t%u\n", | 284 | fprintf(stderr, "%s speed\t%s\t%u\n", |
286 | speed(entry->speed), entry->name, entry->ifnum); | 285 | speed(entry->speed), entry->name, entry->ifnum); |
@@ -351,7 +350,7 @@ restart: | |||
351 | return arg; | 350 | return arg; |
352 | } | 351 | } |
353 | 352 | ||
354 | static const char *usbfs_dir_find(void) | 353 | static const char *usb_dir_find(void) |
355 | { | 354 | { |
356 | static char udev_usb_path[] = "/dev/bus/usb"; | 355 | static char udev_usb_path[] = "/dev/bus/usb"; |
357 | 356 | ||
@@ -380,7 +379,7 @@ int main (int argc, char **argv) | |||
380 | int c; | 379 | int c; |
381 | struct testdev *entry; | 380 | struct testdev *entry; |
382 | char *device; | 381 | char *device; |
383 | const char *usbfs_dir = NULL; | 382 | const char *usb_dir = NULL; |
384 | int all = 0, forever = 0, not = 0; | 383 | int all = 0, forever = 0, not = 0; |
385 | int test = -1 /* all */; | 384 | int test = -1 /* all */; |
386 | struct usbtest_param param; | 385 | struct usbtest_param param; |
@@ -407,8 +406,8 @@ int main (int argc, char **argv) | |||
407 | case 'D': /* device, if only one */ | 406 | case 'D': /* device, if only one */ |
408 | device = optarg; | 407 | device = optarg; |
409 | continue; | 408 | continue; |
410 | case 'A': /* use all devices with specified usbfs dir */ | 409 | case 'A': /* use all devices with specified USB dir */ |
411 | usbfs_dir = optarg; | 410 | usb_dir = optarg; |
412 | /* FALL THROUGH */ | 411 | /* FALL THROUGH */ |
413 | case 'a': /* use all devices */ | 412 | case 'a': /* use all devices */ |
414 | device = NULL; | 413 | device = NULL; |
@@ -449,7 +448,7 @@ usage: | |||
449 | "usage: %s [options]\n" | 448 | "usage: %s [options]\n" |
450 | "Options:\n" | 449 | "Options:\n" |
451 | "\t-D dev only test specific device\n" | 450 | "\t-D dev only test specific device\n" |
452 | "\t-A usbfs-dir\n" | 451 | "\t-A usb-dir\n" |
453 | "\t-a test all recognized devices\n" | 452 | "\t-a test all recognized devices\n" |
454 | "\t-l loop forever(for stress test)\n" | 453 | "\t-l loop forever(for stress test)\n" |
455 | "\t-t testnum only run specified case\n" | 454 | "\t-t testnum only run specified case\n" |
@@ -470,18 +469,18 @@ usage: | |||
470 | goto usage; | 469 | goto usage; |
471 | } | 470 | } |
472 | 471 | ||
473 | /* Find usbfs mount point */ | 472 | /* Find usb device subdirectory */ |
474 | if (!usbfs_dir) { | 473 | if (!usb_dir) { |
475 | usbfs_dir = usbfs_dir_find(); | 474 | usb_dir = usb_dir_find(); |
476 | if (!usbfs_dir) { | 475 | if (!usb_dir) { |
477 | fputs ("usbfs files are missing\n", stderr); | 476 | fputs ("USB device files are missing\n", stderr); |
478 | return -1; | 477 | return -1; |
479 | } | 478 | } |
480 | } | 479 | } |
481 | 480 | ||
482 | /* collect and list the test devices */ | 481 | /* collect and list the test devices */ |
483 | if (ftw (usbfs_dir, find_testdev, 3) != 0) { | 482 | if (ftw (usb_dir, find_testdev, 3) != 0) { |
484 | fputs ("ftw failed; is usbfs missing?\n", stderr); | 483 | fputs ("ftw failed; are USB device files missing?\n", stderr); |
485 | return -1; | 484 | return -1; |
486 | } | 485 | } |
487 | 486 | ||
@@ -507,10 +506,8 @@ usage: | |||
507 | return handle_testdev (entry) != entry; | 506 | return handle_testdev (entry) != entry; |
508 | } | 507 | } |
509 | status = pthread_create (&entry->thread, 0, handle_testdev, entry); | 508 | status = pthread_create (&entry->thread, 0, handle_testdev, entry); |
510 | if (status) { | 509 | if (status) |
511 | perror ("pthread_create"); | 510 | perror ("pthread_create"); |
512 | continue; | ||
513 | } | ||
514 | } | 511 | } |
515 | if (device) { | 512 | if (device) { |
516 | struct testdev dev; | 513 | struct testdev dev; |
diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c index e626fa553c5a..fcc9aa25fd08 100644 --- a/tools/virtio/virtio_test.c +++ b/tools/virtio/virtio_test.c | |||
@@ -164,7 +164,7 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq, | |||
164 | r = virtqueue_add_buf(vq->vq, &sl, 1, 0, | 164 | r = virtqueue_add_buf(vq->vq, &sl, 1, 0, |
165 | dev->buf + started, | 165 | dev->buf + started, |
166 | GFP_ATOMIC); | 166 | GFP_ATOMIC); |
167 | if (likely(r >= 0)) { | 167 | if (likely(r == 0)) { |
168 | ++started; | 168 | ++started; |
169 | virtqueue_kick(vq->vq); | 169 | virtqueue_kick(vq->vq); |
170 | } | 170 | } |
@@ -177,7 +177,7 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq, | |||
177 | r = 0; | 177 | r = 0; |
178 | } | 178 | } |
179 | 179 | ||
180 | } while (r >= 0); | 180 | } while (r == 0); |
181 | if (completed == completed_before) | 181 | if (completed == completed_before) |
182 | ++spurious; | 182 | ++spurious; |
183 | assert(completed <= bufs); | 183 | assert(completed <= bufs); |
@@ -232,7 +232,7 @@ const struct option longopts[] = { | |||
232 | } | 232 | } |
233 | }; | 233 | }; |
234 | 234 | ||
235 | static void help() | 235 | static void help(void) |
236 | { | 236 | { |
237 | fprintf(stderr, "Usage: virtio_test [--help]" | 237 | fprintf(stderr, "Usage: virtio_test [--help]" |
238 | " [--no-indirect]" | 238 | " [--no-indirect]" |
diff --git a/tools/vm/.gitignore b/tools/vm/.gitignore new file mode 100644 index 000000000000..44f095fa2604 --- /dev/null +++ b/tools/vm/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | slabinfo | ||
2 | page-types | ||