diff options
author | Paul Moore <pmoore@redhat.com> | 2013-11-08 13:56:38 -0500 |
---|---|---|
committer | Paul Moore <pmoore@redhat.com> | 2013-11-08 13:56:38 -0500 |
commit | 94851b18d4eb94f8bbf0d9176f7429bd8e371f62 (patch) | |
tree | c3c743ac6323e1caf9e987d6946cc4b2333a8256 /tools | |
parent | 42d64e1add3a1ce8a787116036163b8724362145 (diff) | |
parent | 5e01dc7b26d9f24f39abace5da98ccbd6a5ceb52 (diff) |
Merge tag 'v3.12'
Linux 3.12
Diffstat (limited to 'tools')
140 files changed, 8957 insertions, 1926 deletions
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 07819bfa7dba..8fd9ec66121c 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c | |||
@@ -79,8 +79,6 @@ enum { | |||
79 | DNS | 79 | DNS |
80 | }; | 80 | }; |
81 | 81 | ||
82 | static char kvp_send_buffer[4096]; | ||
83 | static char kvp_recv_buffer[4096 * 2]; | ||
84 | static struct sockaddr_nl addr; | 82 | static struct sockaddr_nl addr; |
85 | static int in_hand_shake = 1; | 83 | static int in_hand_shake = 1; |
86 | 84 | ||
@@ -1301,6 +1299,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) | |||
1301 | } | 1299 | } |
1302 | 1300 | ||
1303 | error = kvp_write_file(file, "HWADDR", "", mac_addr); | 1301 | error = kvp_write_file(file, "HWADDR", "", mac_addr); |
1302 | free(mac_addr); | ||
1304 | if (error) | 1303 | if (error) |
1305 | goto setval_error; | 1304 | goto setval_error; |
1306 | 1305 | ||
@@ -1346,7 +1345,6 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) | |||
1346 | goto setval_error; | 1345 | goto setval_error; |
1347 | 1346 | ||
1348 | setval_done: | 1347 | setval_done: |
1349 | free(mac_addr); | ||
1350 | fclose(file); | 1348 | fclose(file); |
1351 | 1349 | ||
1352 | /* | 1350 | /* |
@@ -1355,12 +1353,15 @@ setval_done: | |||
1355 | */ | 1353 | */ |
1356 | 1354 | ||
1357 | snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file); | 1355 | snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file); |
1358 | system(cmd); | 1356 | if (system(cmd)) { |
1357 | syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s", | ||
1358 | cmd, errno, strerror(errno)); | ||
1359 | return HV_E_FAIL; | ||
1360 | } | ||
1359 | return 0; | 1361 | return 0; |
1360 | 1362 | ||
1361 | setval_error: | 1363 | setval_error: |
1362 | syslog(LOG_ERR, "Failed to write config file"); | 1364 | syslog(LOG_ERR, "Failed to write config file"); |
1363 | free(mac_addr); | ||
1364 | fclose(file); | 1365 | fclose(file); |
1365 | return error; | 1366 | return error; |
1366 | } | 1367 | } |
@@ -1391,23 +1392,18 @@ kvp_get_domain_name(char *buffer, int length) | |||
1391 | static int | 1392 | static int |
1392 | netlink_send(int fd, struct cn_msg *msg) | 1393 | netlink_send(int fd, struct cn_msg *msg) |
1393 | { | 1394 | { |
1394 | struct nlmsghdr *nlh; | 1395 | struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE }; |
1395 | unsigned int size; | 1396 | unsigned int size; |
1396 | struct msghdr message; | 1397 | struct msghdr message; |
1397 | char buffer[64]; | ||
1398 | struct iovec iov[2]; | 1398 | struct iovec iov[2]; |
1399 | 1399 | ||
1400 | size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); | 1400 | size = sizeof(struct cn_msg) + msg->len; |
1401 | 1401 | ||
1402 | nlh = (struct nlmsghdr *)buffer; | 1402 | nlh.nlmsg_pid = getpid(); |
1403 | nlh->nlmsg_seq = 0; | 1403 | nlh.nlmsg_len = NLMSG_LENGTH(size); |
1404 | nlh->nlmsg_pid = getpid(); | ||
1405 | nlh->nlmsg_type = NLMSG_DONE; | ||
1406 | nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); | ||
1407 | nlh->nlmsg_flags = 0; | ||
1408 | 1404 | ||
1409 | iov[0].iov_base = nlh; | 1405 | iov[0].iov_base = &nlh; |
1410 | iov[0].iov_len = sizeof(*nlh); | 1406 | iov[0].iov_len = sizeof(nlh); |
1411 | 1407 | ||
1412 | iov[1].iov_base = msg; | 1408 | iov[1].iov_base = msg; |
1413 | iov[1].iov_len = size; | 1409 | iov[1].iov_len = size; |
@@ -1437,10 +1433,22 @@ int main(void) | |||
1437 | int pool; | 1433 | int pool; |
1438 | char *if_name; | 1434 | char *if_name; |
1439 | struct hv_kvp_ipaddr_value *kvp_ip_val; | 1435 | struct hv_kvp_ipaddr_value *kvp_ip_val; |
1436 | char *kvp_send_buffer; | ||
1437 | char *kvp_recv_buffer; | ||
1438 | size_t kvp_recv_buffer_len; | ||
1440 | 1439 | ||
1441 | daemon(1, 0); | 1440 | if (daemon(1, 0)) |
1441 | return 1; | ||
1442 | openlog("KVP", 0, LOG_USER); | 1442 | openlog("KVP", 0, LOG_USER); |
1443 | syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); | 1443 | syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); |
1444 | |||
1445 | kvp_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg); | ||
1446 | kvp_send_buffer = calloc(1, kvp_recv_buffer_len); | ||
1447 | kvp_recv_buffer = calloc(1, kvp_recv_buffer_len); | ||
1448 | if (!(kvp_send_buffer && kvp_recv_buffer)) { | ||
1449 | syslog(LOG_ERR, "Failed to allocate netlink buffers"); | ||
1450 | exit(EXIT_FAILURE); | ||
1451 | } | ||
1444 | /* | 1452 | /* |
1445 | * Retrieve OS release information. | 1453 | * Retrieve OS release information. |
1446 | */ | 1454 | */ |
@@ -1514,7 +1522,7 @@ int main(void) | |||
1514 | continue; | 1522 | continue; |
1515 | } | 1523 | } |
1516 | 1524 | ||
1517 | len = recvfrom(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0, | 1525 | len = recvfrom(fd, kvp_recv_buffer, kvp_recv_buffer_len, 0, |
1518 | addr_p, &addr_l); | 1526 | addr_p, &addr_l); |
1519 | 1527 | ||
1520 | if (len < 0) { | 1528 | if (len < 0) { |
diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index fea03a3edaf4..8611962c672c 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c | |||
@@ -38,8 +38,6 @@ | |||
38 | #include <linux/netlink.h> | 38 | #include <linux/netlink.h> |
39 | #include <syslog.h> | 39 | #include <syslog.h> |
40 | 40 | ||
41 | static char vss_recv_buffer[4096]; | ||
42 | static char vss_send_buffer[4096]; | ||
43 | static struct sockaddr_nl addr; | 41 | static struct sockaddr_nl addr; |
44 | 42 | ||
45 | #ifndef SOL_NETLINK | 43 | #ifndef SOL_NETLINK |
@@ -107,23 +105,18 @@ static int vss_operate(int operation) | |||
107 | 105 | ||
108 | static int netlink_send(int fd, struct cn_msg *msg) | 106 | static int netlink_send(int fd, struct cn_msg *msg) |
109 | { | 107 | { |
110 | struct nlmsghdr *nlh; | 108 | struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE }; |
111 | unsigned int size; | 109 | unsigned int size; |
112 | struct msghdr message; | 110 | struct msghdr message; |
113 | char buffer[64]; | ||
114 | struct iovec iov[2]; | 111 | struct iovec iov[2]; |
115 | 112 | ||
116 | size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len); | 113 | size = sizeof(struct cn_msg) + msg->len; |
117 | 114 | ||
118 | nlh = (struct nlmsghdr *)buffer; | 115 | nlh.nlmsg_pid = getpid(); |
119 | nlh->nlmsg_seq = 0; | 116 | nlh.nlmsg_len = NLMSG_LENGTH(size); |
120 | nlh->nlmsg_pid = getpid(); | ||
121 | nlh->nlmsg_type = NLMSG_DONE; | ||
122 | nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh)); | ||
123 | nlh->nlmsg_flags = 0; | ||
124 | 117 | ||
125 | iov[0].iov_base = nlh; | 118 | iov[0].iov_base = &nlh; |
126 | iov[0].iov_len = sizeof(*nlh); | 119 | iov[0].iov_len = sizeof(nlh); |
127 | 120 | ||
128 | iov[1].iov_base = msg; | 121 | iov[1].iov_base = msg; |
129 | iov[1].iov_len = size; | 122 | iov[1].iov_len = size; |
@@ -147,6 +140,9 @@ int main(void) | |||
147 | struct cn_msg *incoming_cn_msg; | 140 | struct cn_msg *incoming_cn_msg; |
148 | int op; | 141 | int op; |
149 | struct hv_vss_msg *vss_msg; | 142 | struct hv_vss_msg *vss_msg; |
143 | char *vss_send_buffer; | ||
144 | char *vss_recv_buffer; | ||
145 | size_t vss_recv_buffer_len; | ||
150 | 146 | ||
151 | if (daemon(1, 0)) | 147 | if (daemon(1, 0)) |
152 | return 1; | 148 | return 1; |
@@ -154,9 +150,18 @@ int main(void) | |||
154 | openlog("Hyper-V VSS", 0, LOG_USER); | 150 | openlog("Hyper-V VSS", 0, LOG_USER); |
155 | syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); | 151 | syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); |
156 | 152 | ||
153 | vss_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg); | ||
154 | vss_send_buffer = calloc(1, vss_recv_buffer_len); | ||
155 | vss_recv_buffer = calloc(1, vss_recv_buffer_len); | ||
156 | if (!(vss_send_buffer && vss_recv_buffer)) { | ||
157 | syslog(LOG_ERR, "Failed to allocate netlink buffers"); | ||
158 | exit(EXIT_FAILURE); | ||
159 | } | ||
160 | |||
157 | fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); | 161 | fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); |
158 | if (fd < 0) { | 162 | if (fd < 0) { |
159 | syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); | 163 | syslog(LOG_ERR, "netlink socket creation failed; error:%d %s", |
164 | errno, strerror(errno)); | ||
160 | exit(EXIT_FAILURE); | 165 | exit(EXIT_FAILURE); |
161 | } | 166 | } |
162 | addr.nl_family = AF_NETLINK; | 167 | addr.nl_family = AF_NETLINK; |
@@ -167,12 +172,16 @@ int main(void) | |||
167 | 172 | ||
168 | error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); | 173 | error = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); |
169 | if (error < 0) { | 174 | if (error < 0) { |
170 | syslog(LOG_ERR, "bind failed; error:%d", error); | 175 | syslog(LOG_ERR, "bind failed; error:%d %s", errno, strerror(errno)); |
171 | close(fd); | 176 | close(fd); |
172 | exit(EXIT_FAILURE); | 177 | exit(EXIT_FAILURE); |
173 | } | 178 | } |
174 | nl_group = CN_VSS_IDX; | 179 | nl_group = CN_VSS_IDX; |
175 | setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)); | 180 | if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) { |
181 | syslog(LOG_ERR, "setsockopt failed; error:%d %s", errno, strerror(errno)); | ||
182 | close(fd); | ||
183 | exit(EXIT_FAILURE); | ||
184 | } | ||
176 | /* | 185 | /* |
177 | * Register ourselves with the kernel. | 186 | * Register ourselves with the kernel. |
178 | */ | 187 | */ |
@@ -187,7 +196,7 @@ int main(void) | |||
187 | 196 | ||
188 | len = netlink_send(fd, message); | 197 | len = netlink_send(fd, message); |
189 | if (len < 0) { | 198 | if (len < 0) { |
190 | syslog(LOG_ERR, "netlink_send failed; error:%d", len); | 199 | syslog(LOG_ERR, "netlink_send failed; error:%d %s", errno, strerror(errno)); |
191 | close(fd); | 200 | close(fd); |
192 | exit(EXIT_FAILURE); | 201 | exit(EXIT_FAILURE); |
193 | } | 202 | } |
@@ -199,9 +208,18 @@ int main(void) | |||
199 | socklen_t addr_l = sizeof(addr); | 208 | socklen_t addr_l = sizeof(addr); |
200 | pfd.events = POLLIN; | 209 | pfd.events = POLLIN; |
201 | pfd.revents = 0; | 210 | pfd.revents = 0; |
202 | poll(&pfd, 1, -1); | ||
203 | 211 | ||
204 | len = recvfrom(fd, vss_recv_buffer, sizeof(vss_recv_buffer), 0, | 212 | if (poll(&pfd, 1, -1) < 0) { |
213 | syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno)); | ||
214 | if (errno == EINVAL) { | ||
215 | close(fd); | ||
216 | exit(EXIT_FAILURE); | ||
217 | } | ||
218 | else | ||
219 | continue; | ||
220 | } | ||
221 | |||
222 | len = recvfrom(fd, vss_recv_buffer, vss_recv_buffer_len, 0, | ||
205 | addr_p, &addr_l); | 223 | addr_p, &addr_l); |
206 | 224 | ||
207 | if (len < 0) { | 225 | if (len < 0) { |
@@ -241,7 +259,8 @@ int main(void) | |||
241 | vss_msg->error = error; | 259 | vss_msg->error = error; |
242 | len = netlink_send(fd, incoming_cn_msg); | 260 | len = netlink_send(fd, incoming_cn_msg); |
243 | if (len < 0) { | 261 | if (len < 0) { |
244 | syslog(LOG_ERR, "net_link send failed; error:%d", len); | 262 | syslog(LOG_ERR, "net_link send failed; error:%d %s", |
263 | errno, strerror(errno)); | ||
245 | exit(EXIT_FAILURE); | 264 | exit(EXIT_FAILURE); |
246 | } | 265 | } |
247 | } | 266 | } |
diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c index 68f67cf3d318..32cf2ce15d69 100644 --- a/tools/lguest/lguest.c +++ b/tools/lguest/lguest.c | |||
@@ -42,6 +42,10 @@ | |||
42 | #include <pwd.h> | 42 | #include <pwd.h> |
43 | #include <grp.h> | 43 | #include <grp.h> |
44 | 44 | ||
45 | #ifndef VIRTIO_F_ANY_LAYOUT | ||
46 | #define VIRTIO_F_ANY_LAYOUT 27 | ||
47 | #endif | ||
48 | |||
45 | /*L:110 | 49 | /*L:110 |
46 | * We can ignore the 43 include files we need for this program, but I do want | 50 | * We can ignore the 43 include files we need for this program, but I do want |
47 | * to draw attention to the use of kernel-style types. | 51 | * to draw attention to the use of kernel-style types. |
@@ -1544,6 +1548,8 @@ static void setup_tun_net(char *arg) | |||
1544 | add_feature(dev, VIRTIO_NET_F_HOST_ECN); | 1548 | add_feature(dev, VIRTIO_NET_F_HOST_ECN); |
1545 | /* We handle indirect ring entries */ | 1549 | /* We handle indirect ring entries */ |
1546 | add_feature(dev, VIRTIO_RING_F_INDIRECT_DESC); | 1550 | add_feature(dev, VIRTIO_RING_F_INDIRECT_DESC); |
1551 | /* We're compliant with the damn spec. */ | ||
1552 | add_feature(dev, VIRTIO_F_ANY_LAYOUT); | ||
1547 | set_config(dev, sizeof(conf), &conf); | 1553 | set_config(dev, sizeof(conf), &conf); |
1548 | 1554 | ||
1549 | /* We don't need the socket any more; setup is done. */ | 1555 | /* We don't need the socket any more; setup is done. */ |
diff --git a/tools/lib/lk/Makefile b/tools/lib/lk/Makefile index 280dd8205430..3dba0a4aebbf 100644 --- a/tools/lib/lk/Makefile +++ b/tools/lib/lk/Makefile | |||
@@ -3,21 +3,6 @@ include ../../scripts/Makefile.include | |||
3 | CC = $(CROSS_COMPILE)gcc | 3 | CC = $(CROSS_COMPILE)gcc |
4 | AR = $(CROSS_COMPILE)ar | 4 | AR = $(CROSS_COMPILE)ar |
5 | 5 | ||
6 | # Makefiles suck: This macro sets a default value of $(2) for the | ||
7 | # variable named by $(1), unless the variable has been set by | ||
8 | # environment or command line. This is necessary for CC and AR | ||
9 | # because make sets default values, so the simpler ?= approach | ||
10 | # won't work as expected. | ||
11 | define allow-override | ||
12 | $(if $(or $(findstring environment,$(origin $(1))),\ | ||
13 | $(findstring command line,$(origin $(1)))),,\ | ||
14 | $(eval $(1) = $(2))) | ||
15 | endef | ||
16 | |||
17 | # Allow setting CC and AR, or setting CROSS_COMPILE as a prefix. | ||
18 | $(call allow-override,CC,$(CROSS_COMPILE)gcc) | ||
19 | $(call allow-override,AR,$(CROSS_COMPILE)ar) | ||
20 | |||
21 | # guard against environment variables | 6 | # guard against environment variables |
22 | LIB_H= | 7 | LIB_H= |
23 | LIB_OBJS= | 8 | LIB_OBJS= |
diff --git a/tools/lib/lk/debugfs.c b/tools/lib/lk/debugfs.c index 099e7cd022e4..7c4347962353 100644 --- a/tools/lib/lk/debugfs.c +++ b/tools/lib/lk/debugfs.c | |||
@@ -5,7 +5,6 @@ | |||
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <sys/vfs.h> | 6 | #include <sys/vfs.h> |
7 | #include <sys/mount.h> | 7 | #include <sys/mount.h> |
8 | #include <linux/magic.h> | ||
9 | #include <linux/kernel.h> | 8 | #include <linux/kernel.h> |
10 | 9 | ||
11 | #include "debugfs.h" | 10 | #include "debugfs.h" |
diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile index 0b0a90787db6..ca6cb779876a 100644 --- a/tools/lib/traceevent/Makefile +++ b/tools/lib/traceevent/Makefile | |||
@@ -39,13 +39,8 @@ bindir_relative = bin | |||
39 | bindir = $(prefix)/$(bindir_relative) | 39 | bindir = $(prefix)/$(bindir_relative) |
40 | man_dir = $(prefix)/share/man | 40 | man_dir = $(prefix)/share/man |
41 | man_dir_SQ = '$(subst ','\'',$(man_dir))' | 41 | man_dir_SQ = '$(subst ','\'',$(man_dir))' |
42 | html_install = $(prefix)/share/kernelshark/html | ||
43 | html_install_SQ = '$(subst ','\'',$(html_install))' | ||
44 | img_install = $(prefix)/share/kernelshark/html/images | ||
45 | img_install_SQ = '$(subst ','\'',$(img_install))' | ||
46 | 42 | ||
47 | export man_dir man_dir_SQ html_install html_install_SQ INSTALL | 43 | export man_dir man_dir_SQ INSTALL |
48 | export img_install img_install_SQ | ||
49 | export DESTDIR DESTDIR_SQ | 44 | export DESTDIR DESTDIR_SQ |
50 | 45 | ||
51 | # copy a bit from Linux kbuild | 46 | # copy a bit from Linux kbuild |
@@ -65,7 +60,7 @@ ifeq ($(BUILD_SRC),) | |||
65 | ifneq ($(BUILD_OUTPUT),) | 60 | ifneq ($(BUILD_OUTPUT),) |
66 | 61 | ||
67 | define build_output | 62 | define build_output |
68 | $(if $(VERBOSE:1=),@)$(MAKE) -C $(BUILD_OUTPUT) \ | 63 | $(if $(VERBOSE:1=),@)+$(MAKE) -C $(BUILD_OUTPUT) \ |
69 | BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1 | 64 | BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1 |
70 | endef | 65 | endef |
71 | 66 | ||
@@ -76,10 +71,7 @@ $(if $(BUILD_OUTPUT),, \ | |||
76 | 71 | ||
77 | all: sub-make | 72 | all: sub-make |
78 | 73 | ||
79 | gui: force | 74 | $(MAKECMDGOALS): sub-make |
80 | $(call build_output, all_cmd) | ||
81 | |||
82 | $(filter-out gui,$(MAKECMDGOALS)): sub-make | ||
83 | 75 | ||
84 | sub-make: force | 76 | sub-make: force |
85 | $(call build_output, $(MAKECMDGOALS)) | 77 | $(call build_output, $(MAKECMDGOALS)) |
@@ -189,6 +181,7 @@ $(obj)/%.o: $(src)/%.c | |||
189 | $(Q)$(call do_compile) | 181 | $(Q)$(call do_compile) |
190 | 182 | ||
191 | PEVENT_LIB_OBJS = event-parse.o trace-seq.o parse-filter.o parse-utils.o | 183 | PEVENT_LIB_OBJS = event-parse.o trace-seq.o parse-filter.o parse-utils.o |
184 | PEVENT_LIB_OBJS += kbuffer-parse.o | ||
192 | 185 | ||
193 | ALL_OBJS = $(PEVENT_LIB_OBJS) | 186 | ALL_OBJS = $(PEVENT_LIB_OBJS) |
194 | 187 | ||
@@ -258,9 +251,6 @@ define check_deps | |||
258 | $(RM) $@.$$$$ | 251 | $(RM) $@.$$$$ |
259 | endef | 252 | endef |
260 | 253 | ||
261 | $(gui_deps): ks_version.h | ||
262 | $(non_gui_deps): tc_version.h | ||
263 | |||
264 | $(all_deps): .%.d: $(src)/%.c | 254 | $(all_deps): .%.d: $(src)/%.c |
265 | $(Q)$(call check_deps) | 255 | $(Q)$(call check_deps) |
266 | 256 | ||
@@ -300,7 +290,7 @@ define do_install | |||
300 | $(INSTALL) $1 '$(DESTDIR_SQ)$2' | 290 | $(INSTALL) $1 '$(DESTDIR_SQ)$2' |
301 | endef | 291 | endef |
302 | 292 | ||
303 | install_lib: all_cmd install_plugins install_python | 293 | install_lib: all_cmd |
304 | $(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ)) | 294 | $(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ)) |
305 | 295 | ||
306 | install: install_lib | 296 | install: install_lib |
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c index 82b0606dcb8a..d1c2a6a4cd32 100644 --- a/tools/lib/traceevent/event-parse.c +++ b/tools/lib/traceevent/event-parse.c | |||
@@ -5450,10 +5450,9 @@ int pevent_register_print_function(struct pevent *pevent, | |||
5450 | * If @id is >= 0, then it is used to find the event. | 5450 | * If @id is >= 0, then it is used to find the event. |
5451 | * else @sys_name and @event_name are used. | 5451 | * else @sys_name and @event_name are used. |
5452 | */ | 5452 | */ |
5453 | int pevent_register_event_handler(struct pevent *pevent, | 5453 | int pevent_register_event_handler(struct pevent *pevent, int id, |
5454 | int id, char *sys_name, char *event_name, | 5454 | const char *sys_name, const char *event_name, |
5455 | pevent_event_handler_func func, | 5455 | pevent_event_handler_func func, void *context) |
5456 | void *context) | ||
5457 | { | 5456 | { |
5458 | struct event_format *event; | 5457 | struct event_format *event; |
5459 | struct event_handler *handle; | 5458 | struct event_handler *handle; |
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h index 7be7e89533e4..c37b2026d04a 100644 --- a/tools/lib/traceevent/event-parse.h +++ b/tools/lib/traceevent/event-parse.h | |||
@@ -69,6 +69,7 @@ struct trace_seq { | |||
69 | }; | 69 | }; |
70 | 70 | ||
71 | void trace_seq_init(struct trace_seq *s); | 71 | void trace_seq_init(struct trace_seq *s); |
72 | void trace_seq_reset(struct trace_seq *s); | ||
72 | void trace_seq_destroy(struct trace_seq *s); | 73 | void trace_seq_destroy(struct trace_seq *s); |
73 | 74 | ||
74 | extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) | 75 | extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...) |
@@ -399,6 +400,7 @@ struct pevent { | |||
399 | 400 | ||
400 | int cpus; | 401 | int cpus; |
401 | int long_size; | 402 | int long_size; |
403 | int page_size; | ||
402 | 404 | ||
403 | struct cmdline *cmdlines; | 405 | struct cmdline *cmdlines; |
404 | struct cmdline_list *cmdlist; | 406 | struct cmdline_list *cmdlist; |
@@ -561,7 +563,8 @@ int pevent_print_num_field(struct trace_seq *s, const char *fmt, | |||
561 | struct event_format *event, const char *name, | 563 | struct event_format *event, const char *name, |
562 | struct pevent_record *record, int err); | 564 | struct pevent_record *record, int err); |
563 | 565 | ||
564 | int pevent_register_event_handler(struct pevent *pevent, int id, char *sys_name, char *event_name, | 566 | int pevent_register_event_handler(struct pevent *pevent, int id, |
567 | const char *sys_name, const char *event_name, | ||
565 | pevent_event_handler_func func, void *context); | 568 | pevent_event_handler_func func, void *context); |
566 | int pevent_register_print_function(struct pevent *pevent, | 569 | int pevent_register_print_function(struct pevent *pevent, |
567 | pevent_func_handler func, | 570 | pevent_func_handler func, |
@@ -619,6 +622,16 @@ static inline void pevent_set_long_size(struct pevent *pevent, int long_size) | |||
619 | pevent->long_size = long_size; | 622 | pevent->long_size = long_size; |
620 | } | 623 | } |
621 | 624 | ||
625 | static inline int pevent_get_page_size(struct pevent *pevent) | ||
626 | { | ||
627 | return pevent->page_size; | ||
628 | } | ||
629 | |||
630 | static inline void pevent_set_page_size(struct pevent *pevent, int _page_size) | ||
631 | { | ||
632 | pevent->page_size = _page_size; | ||
633 | } | ||
634 | |||
622 | static inline int pevent_is_file_bigendian(struct pevent *pevent) | 635 | static inline int pevent_is_file_bigendian(struct pevent *pevent) |
623 | { | 636 | { |
624 | return pevent->file_bigendian; | 637 | return pevent->file_bigendian; |
diff --git a/tools/lib/traceevent/kbuffer-parse.c b/tools/lib/traceevent/kbuffer-parse.c new file mode 100644 index 000000000000..dcc665228c71 --- /dev/null +++ b/tools/lib/traceevent/kbuffer-parse.c | |||
@@ -0,0 +1,732 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009, 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, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
20 | */ | ||
21 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | |||
25 | #include "kbuffer.h" | ||
26 | |||
27 | #define MISSING_EVENTS (1 << 31) | ||
28 | #define MISSING_STORED (1 << 30) | ||
29 | |||
30 | #define COMMIT_MASK ((1 << 27) - 1) | ||
31 | |||
32 | enum { | ||
33 | KBUFFER_FL_HOST_BIG_ENDIAN = (1<<0), | ||
34 | KBUFFER_FL_BIG_ENDIAN = (1<<1), | ||
35 | KBUFFER_FL_LONG_8 = (1<<2), | ||
36 | KBUFFER_FL_OLD_FORMAT = (1<<3), | ||
37 | }; | ||
38 | |||
39 | #define ENDIAN_MASK (KBUFFER_FL_HOST_BIG_ENDIAN | KBUFFER_FL_BIG_ENDIAN) | ||
40 | |||
41 | /** kbuffer | ||
42 | * @timestamp - timestamp of current event | ||
43 | * @lost_events - # of lost events between this subbuffer and previous | ||
44 | * @flags - special flags of the kbuffer | ||
45 | * @subbuffer - pointer to the sub-buffer page | ||
46 | * @data - pointer to the start of data on the sub-buffer page | ||
47 | * @index - index from @data to the @curr event data | ||
48 | * @curr - offset from @data to the start of current event | ||
49 | * (includes metadata) | ||
50 | * @next - offset from @data to the start of next event | ||
51 | * @size - The size of data on @data | ||
52 | * @start - The offset from @subbuffer where @data lives | ||
53 | * | ||
54 | * @read_4 - Function to read 4 raw bytes (may swap) | ||
55 | * @read_8 - Function to read 8 raw bytes (may swap) | ||
56 | * @read_long - Function to read a long word (4 or 8 bytes with needed swap) | ||
57 | */ | ||
58 | struct kbuffer { | ||
59 | unsigned long long timestamp; | ||
60 | long long lost_events; | ||
61 | unsigned long flags; | ||
62 | void *subbuffer; | ||
63 | void *data; | ||
64 | unsigned int index; | ||
65 | unsigned int curr; | ||
66 | unsigned int next; | ||
67 | unsigned int size; | ||
68 | unsigned int start; | ||
69 | |||
70 | unsigned int (*read_4)(void *ptr); | ||
71 | unsigned long long (*read_8)(void *ptr); | ||
72 | unsigned long long (*read_long)(struct kbuffer *kbuf, void *ptr); | ||
73 | int (*next_event)(struct kbuffer *kbuf); | ||
74 | }; | ||
75 | |||
76 | static void *zmalloc(size_t size) | ||
77 | { | ||
78 | return calloc(1, size); | ||
79 | } | ||
80 | |||
81 | static int host_is_bigendian(void) | ||
82 | { | ||
83 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; | ||
84 | unsigned int *ptr; | ||
85 | |||
86 | ptr = (unsigned int *)str; | ||
87 | return *ptr == 0x01020304; | ||
88 | } | ||
89 | |||
90 | static int do_swap(struct kbuffer *kbuf) | ||
91 | { | ||
92 | return ((kbuf->flags & KBUFFER_FL_HOST_BIG_ENDIAN) + kbuf->flags) & | ||
93 | ENDIAN_MASK; | ||
94 | } | ||
95 | |||
96 | static unsigned long long __read_8(void *ptr) | ||
97 | { | ||
98 | unsigned long long data = *(unsigned long long *)ptr; | ||
99 | |||
100 | return data; | ||
101 | } | ||
102 | |||
103 | static unsigned long long __read_8_sw(void *ptr) | ||
104 | { | ||
105 | unsigned long long data = *(unsigned long long *)ptr; | ||
106 | unsigned long long swap; | ||
107 | |||
108 | swap = ((data & 0xffULL) << 56) | | ||
109 | ((data & (0xffULL << 8)) << 40) | | ||
110 | ((data & (0xffULL << 16)) << 24) | | ||
111 | ((data & (0xffULL << 24)) << 8) | | ||
112 | ((data & (0xffULL << 32)) >> 8) | | ||
113 | ((data & (0xffULL << 40)) >> 24) | | ||
114 | ((data & (0xffULL << 48)) >> 40) | | ||
115 | ((data & (0xffULL << 56)) >> 56); | ||
116 | |||
117 | return swap; | ||
118 | } | ||
119 | |||
120 | static unsigned int __read_4(void *ptr) | ||
121 | { | ||
122 | unsigned int data = *(unsigned int *)ptr; | ||
123 | |||
124 | return data; | ||
125 | } | ||
126 | |||
127 | static unsigned int __read_4_sw(void *ptr) | ||
128 | { | ||
129 | unsigned int data = *(unsigned int *)ptr; | ||
130 | unsigned int swap; | ||
131 | |||
132 | swap = ((data & 0xffULL) << 24) | | ||
133 | ((data & (0xffULL << 8)) << 8) | | ||
134 | ((data & (0xffULL << 16)) >> 8) | | ||
135 | ((data & (0xffULL << 24)) >> 24); | ||
136 | |||
137 | return swap; | ||
138 | } | ||
139 | |||
140 | static unsigned long long read_8(struct kbuffer *kbuf, void *ptr) | ||
141 | { | ||
142 | return kbuf->read_8(ptr); | ||
143 | } | ||
144 | |||
145 | static unsigned int read_4(struct kbuffer *kbuf, void *ptr) | ||
146 | { | ||
147 | return kbuf->read_4(ptr); | ||
148 | } | ||
149 | |||
150 | static unsigned long long __read_long_8(struct kbuffer *kbuf, void *ptr) | ||
151 | { | ||
152 | return kbuf->read_8(ptr); | ||
153 | } | ||
154 | |||
155 | static unsigned long long __read_long_4(struct kbuffer *kbuf, void *ptr) | ||
156 | { | ||
157 | return kbuf->read_4(ptr); | ||
158 | } | ||
159 | |||
160 | static unsigned long long read_long(struct kbuffer *kbuf, void *ptr) | ||
161 | { | ||
162 | return kbuf->read_long(kbuf, ptr); | ||
163 | } | ||
164 | |||
165 | static int calc_index(struct kbuffer *kbuf, void *ptr) | ||
166 | { | ||
167 | return (unsigned long)ptr - (unsigned long)kbuf->data; | ||
168 | } | ||
169 | |||
170 | static int __next_event(struct kbuffer *kbuf); | ||
171 | |||
172 | /** | ||
173 | * kbuffer_alloc - allocat a new kbuffer | ||
174 | * @size; enum to denote size of word | ||
175 | * @endian: enum to denote endianness | ||
176 | * | ||
177 | * Allocates and returns a new kbuffer. | ||
178 | */ | ||
179 | struct kbuffer * | ||
180 | kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian) | ||
181 | { | ||
182 | struct kbuffer *kbuf; | ||
183 | int flags = 0; | ||
184 | |||
185 | switch (size) { | ||
186 | case KBUFFER_LSIZE_4: | ||
187 | break; | ||
188 | case KBUFFER_LSIZE_8: | ||
189 | flags |= KBUFFER_FL_LONG_8; | ||
190 | break; | ||
191 | default: | ||
192 | return NULL; | ||
193 | } | ||
194 | |||
195 | switch (endian) { | ||
196 | case KBUFFER_ENDIAN_LITTLE: | ||
197 | break; | ||
198 | case KBUFFER_ENDIAN_BIG: | ||
199 | flags |= KBUFFER_FL_BIG_ENDIAN; | ||
200 | break; | ||
201 | default: | ||
202 | return NULL; | ||
203 | } | ||
204 | |||
205 | kbuf = zmalloc(sizeof(*kbuf)); | ||
206 | if (!kbuf) | ||
207 | return NULL; | ||
208 | |||
209 | kbuf->flags = flags; | ||
210 | |||
211 | if (host_is_bigendian()) | ||
212 | kbuf->flags |= KBUFFER_FL_HOST_BIG_ENDIAN; | ||
213 | |||
214 | if (do_swap(kbuf)) { | ||
215 | kbuf->read_8 = __read_8_sw; | ||
216 | kbuf->read_4 = __read_4_sw; | ||
217 | } else { | ||
218 | kbuf->read_8 = __read_8; | ||
219 | kbuf->read_4 = __read_4; | ||
220 | } | ||
221 | |||
222 | if (kbuf->flags & KBUFFER_FL_LONG_8) | ||
223 | kbuf->read_long = __read_long_8; | ||
224 | else | ||
225 | kbuf->read_long = __read_long_4; | ||
226 | |||
227 | /* May be changed by kbuffer_set_old_format() */ | ||
228 | kbuf->next_event = __next_event; | ||
229 | |||
230 | return kbuf; | ||
231 | } | ||
232 | |||
233 | /** kbuffer_free - free an allocated kbuffer | ||
234 | * @kbuf: The kbuffer to free | ||
235 | * | ||
236 | * Can take NULL as a parameter. | ||
237 | */ | ||
238 | void kbuffer_free(struct kbuffer *kbuf) | ||
239 | { | ||
240 | free(kbuf); | ||
241 | } | ||
242 | |||
243 | static unsigned int type4host(struct kbuffer *kbuf, | ||
244 | unsigned int type_len_ts) | ||
245 | { | ||
246 | if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) | ||
247 | return (type_len_ts >> 29) & 3; | ||
248 | else | ||
249 | return type_len_ts & 3; | ||
250 | } | ||
251 | |||
252 | static unsigned int len4host(struct kbuffer *kbuf, | ||
253 | unsigned int type_len_ts) | ||
254 | { | ||
255 | if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) | ||
256 | return (type_len_ts >> 27) & 7; | ||
257 | else | ||
258 | return (type_len_ts >> 2) & 7; | ||
259 | } | ||
260 | |||
261 | static unsigned int type_len4host(struct kbuffer *kbuf, | ||
262 | unsigned int type_len_ts) | ||
263 | { | ||
264 | if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) | ||
265 | return (type_len_ts >> 27) & ((1 << 5) - 1); | ||
266 | else | ||
267 | return type_len_ts & ((1 << 5) - 1); | ||
268 | } | ||
269 | |||
270 | static unsigned int ts4host(struct kbuffer *kbuf, | ||
271 | unsigned int type_len_ts) | ||
272 | { | ||
273 | if (kbuf->flags & KBUFFER_FL_BIG_ENDIAN) | ||
274 | return type_len_ts & ((1 << 27) - 1); | ||
275 | else | ||
276 | return type_len_ts >> 5; | ||
277 | } | ||
278 | |||
279 | /* | ||
280 | * Linux 2.6.30 and earlier (not much ealier) had a different | ||
281 | * ring buffer format. It should be obsolete, but we handle it anyway. | ||
282 | */ | ||
283 | enum old_ring_buffer_type { | ||
284 | OLD_RINGBUF_TYPE_PADDING, | ||
285 | OLD_RINGBUF_TYPE_TIME_EXTEND, | ||
286 | OLD_RINGBUF_TYPE_TIME_STAMP, | ||
287 | OLD_RINGBUF_TYPE_DATA, | ||
288 | }; | ||
289 | |||
290 | static unsigned int old_update_pointers(struct kbuffer *kbuf) | ||
291 | { | ||
292 | unsigned long long extend; | ||
293 | unsigned int type_len_ts; | ||
294 | unsigned int type; | ||
295 | unsigned int len; | ||
296 | unsigned int delta; | ||
297 | unsigned int length; | ||
298 | void *ptr = kbuf->data + kbuf->curr; | ||
299 | |||
300 | type_len_ts = read_4(kbuf, ptr); | ||
301 | ptr += 4; | ||
302 | |||
303 | type = type4host(kbuf, type_len_ts); | ||
304 | len = len4host(kbuf, type_len_ts); | ||
305 | delta = ts4host(kbuf, type_len_ts); | ||
306 | |||
307 | switch (type) { | ||
308 | case OLD_RINGBUF_TYPE_PADDING: | ||
309 | kbuf->next = kbuf->size; | ||
310 | return 0; | ||
311 | |||
312 | case OLD_RINGBUF_TYPE_TIME_EXTEND: | ||
313 | extend = read_4(kbuf, ptr); | ||
314 | extend <<= TS_SHIFT; | ||
315 | extend += delta; | ||
316 | delta = extend; | ||
317 | ptr += 4; | ||
318 | break; | ||
319 | |||
320 | case OLD_RINGBUF_TYPE_TIME_STAMP: | ||
321 | /* should never happen! */ | ||
322 | kbuf->curr = kbuf->size; | ||
323 | kbuf->next = kbuf->size; | ||
324 | kbuf->index = kbuf->size; | ||
325 | return -1; | ||
326 | default: | ||
327 | if (len) | ||
328 | length = len * 4; | ||
329 | else { | ||
330 | length = read_4(kbuf, ptr); | ||
331 | length -= 4; | ||
332 | ptr += 4; | ||
333 | } | ||
334 | break; | ||
335 | } | ||
336 | |||
337 | kbuf->timestamp += delta; | ||
338 | kbuf->index = calc_index(kbuf, ptr); | ||
339 | kbuf->next = kbuf->index + length; | ||
340 | |||
341 | return type; | ||
342 | } | ||
343 | |||
344 | static int __old_next_event(struct kbuffer *kbuf) | ||
345 | { | ||
346 | int type; | ||
347 | |||
348 | do { | ||
349 | kbuf->curr = kbuf->next; | ||
350 | if (kbuf->next >= kbuf->size) | ||
351 | return -1; | ||
352 | type = old_update_pointers(kbuf); | ||
353 | } while (type == OLD_RINGBUF_TYPE_TIME_EXTEND || type == OLD_RINGBUF_TYPE_PADDING); | ||
354 | |||
355 | return 0; | ||
356 | } | ||
357 | |||
358 | static unsigned int | ||
359 | translate_data(struct kbuffer *kbuf, void *data, void **rptr, | ||
360 | unsigned long long *delta, int *length) | ||
361 | { | ||
362 | unsigned long long extend; | ||
363 | unsigned int type_len_ts; | ||
364 | unsigned int type_len; | ||
365 | |||
366 | type_len_ts = read_4(kbuf, data); | ||
367 | data += 4; | ||
368 | |||
369 | type_len = type_len4host(kbuf, type_len_ts); | ||
370 | *delta = ts4host(kbuf, type_len_ts); | ||
371 | |||
372 | switch (type_len) { | ||
373 | case KBUFFER_TYPE_PADDING: | ||
374 | *length = read_4(kbuf, data); | ||
375 | data += *length; | ||
376 | break; | ||
377 | |||
378 | case KBUFFER_TYPE_TIME_EXTEND: | ||
379 | extend = read_4(kbuf, data); | ||
380 | data += 4; | ||
381 | extend <<= TS_SHIFT; | ||
382 | extend += *delta; | ||
383 | *delta = extend; | ||
384 | *length = 0; | ||
385 | break; | ||
386 | |||
387 | case KBUFFER_TYPE_TIME_STAMP: | ||
388 | data += 12; | ||
389 | *length = 0; | ||
390 | break; | ||
391 | case 0: | ||
392 | *length = read_4(kbuf, data) - 4; | ||
393 | *length = (*length + 3) & ~3; | ||
394 | data += 4; | ||
395 | break; | ||
396 | default: | ||
397 | *length = type_len * 4; | ||
398 | break; | ||
399 | } | ||
400 | |||
401 | *rptr = data; | ||
402 | |||
403 | return type_len; | ||
404 | } | ||
405 | |||
406 | static unsigned int update_pointers(struct kbuffer *kbuf) | ||
407 | { | ||
408 | unsigned long long delta; | ||
409 | unsigned int type_len; | ||
410 | int length; | ||
411 | void *ptr = kbuf->data + kbuf->curr; | ||
412 | |||
413 | type_len = translate_data(kbuf, ptr, &ptr, &delta, &length); | ||
414 | |||
415 | kbuf->timestamp += delta; | ||
416 | kbuf->index = calc_index(kbuf, ptr); | ||
417 | kbuf->next = kbuf->index + length; | ||
418 | |||
419 | return type_len; | ||
420 | } | ||
421 | |||
422 | /** | ||
423 | * kbuffer_translate_data - read raw data to get a record | ||
424 | * @swap: Set to 1 if bytes in words need to be swapped when read | ||
425 | * @data: The raw data to read | ||
426 | * @size: Address to store the size of the event data. | ||
427 | * | ||
428 | * Returns a pointer to the event data. To determine the entire | ||
429 | * record size (record metadata + data) just add the difference between | ||
430 | * @data and the returned value to @size. | ||
431 | */ | ||
432 | void *kbuffer_translate_data(int swap, void *data, unsigned int *size) | ||
433 | { | ||
434 | unsigned long long delta; | ||
435 | struct kbuffer kbuf; | ||
436 | int type_len; | ||
437 | int length; | ||
438 | void *ptr; | ||
439 | |||
440 | if (swap) { | ||
441 | kbuf.read_8 = __read_8_sw; | ||
442 | kbuf.read_4 = __read_4_sw; | ||
443 | kbuf.flags = host_is_bigendian() ? 0 : KBUFFER_FL_BIG_ENDIAN; | ||
444 | } else { | ||
445 | kbuf.read_8 = __read_8; | ||
446 | kbuf.read_4 = __read_4; | ||
447 | kbuf.flags = host_is_bigendian() ? KBUFFER_FL_BIG_ENDIAN: 0; | ||
448 | } | ||
449 | |||
450 | type_len = translate_data(&kbuf, data, &ptr, &delta, &length); | ||
451 | switch (type_len) { | ||
452 | case KBUFFER_TYPE_PADDING: | ||
453 | case KBUFFER_TYPE_TIME_EXTEND: | ||
454 | case KBUFFER_TYPE_TIME_STAMP: | ||
455 | return NULL; | ||
456 | }; | ||
457 | |||
458 | *size = length; | ||
459 | |||
460 | return ptr; | ||
461 | } | ||
462 | |||
463 | static int __next_event(struct kbuffer *kbuf) | ||
464 | { | ||
465 | int type; | ||
466 | |||
467 | do { | ||
468 | kbuf->curr = kbuf->next; | ||
469 | if (kbuf->next >= kbuf->size) | ||
470 | return -1; | ||
471 | type = update_pointers(kbuf); | ||
472 | } while (type == KBUFFER_TYPE_TIME_EXTEND || type == KBUFFER_TYPE_PADDING); | ||
473 | |||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | static int next_event(struct kbuffer *kbuf) | ||
478 | { | ||
479 | return kbuf->next_event(kbuf); | ||
480 | } | ||
481 | |||
482 | /** | ||
483 | * kbuffer_next_event - increment the current pointer | ||
484 | * @kbuf: The kbuffer to read | ||
485 | * @ts: Address to store the next record's timestamp (may be NULL to ignore) | ||
486 | * | ||
487 | * Increments the pointers into the subbuffer of the kbuffer to point to the | ||
488 | * next event so that the next kbuffer_read_event() will return a | ||
489 | * new event. | ||
490 | * | ||
491 | * Returns the data of the next event if a new event exists on the subbuffer, | ||
492 | * NULL otherwise. | ||
493 | */ | ||
494 | void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts) | ||
495 | { | ||
496 | int ret; | ||
497 | |||
498 | if (!kbuf || !kbuf->subbuffer) | ||
499 | return NULL; | ||
500 | |||
501 | ret = next_event(kbuf); | ||
502 | if (ret < 0) | ||
503 | return NULL; | ||
504 | |||
505 | if (ts) | ||
506 | *ts = kbuf->timestamp; | ||
507 | |||
508 | return kbuf->data + kbuf->index; | ||
509 | } | ||
510 | |||
511 | /** | ||
512 | * kbuffer_load_subbuffer - load a new subbuffer into the kbuffer | ||
513 | * @kbuf: The kbuffer to load | ||
514 | * @subbuffer: The subbuffer to load into @kbuf. | ||
515 | * | ||
516 | * Load a new subbuffer (page) into @kbuf. This will reset all | ||
517 | * the pointers and update the @kbuf timestamp. The next read will | ||
518 | * return the first event on @subbuffer. | ||
519 | * | ||
520 | * Returns 0 on succes, -1 otherwise. | ||
521 | */ | ||
522 | int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer) | ||
523 | { | ||
524 | unsigned long long flags; | ||
525 | void *ptr = subbuffer; | ||
526 | |||
527 | if (!kbuf || !subbuffer) | ||
528 | return -1; | ||
529 | |||
530 | kbuf->subbuffer = subbuffer; | ||
531 | |||
532 | kbuf->timestamp = read_8(kbuf, ptr); | ||
533 | ptr += 8; | ||
534 | |||
535 | kbuf->curr = 0; | ||
536 | |||
537 | if (kbuf->flags & KBUFFER_FL_LONG_8) | ||
538 | kbuf->start = 16; | ||
539 | else | ||
540 | kbuf->start = 12; | ||
541 | |||
542 | kbuf->data = subbuffer + kbuf->start; | ||
543 | |||
544 | flags = read_long(kbuf, ptr); | ||
545 | kbuf->size = (unsigned int)flags & COMMIT_MASK; | ||
546 | |||
547 | if (flags & MISSING_EVENTS) { | ||
548 | if (flags & MISSING_STORED) { | ||
549 | ptr = kbuf->data + kbuf->size; | ||
550 | kbuf->lost_events = read_long(kbuf, ptr); | ||
551 | } else | ||
552 | kbuf->lost_events = -1; | ||
553 | } else | ||
554 | kbuf->lost_events = 0; | ||
555 | |||
556 | kbuf->index = 0; | ||
557 | kbuf->next = 0; | ||
558 | |||
559 | next_event(kbuf); | ||
560 | |||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | /** | ||
565 | * kbuffer_read_event - read the next event in the kbuffer subbuffer | ||
566 | * @kbuf: The kbuffer to read from | ||
567 | * @ts: The address to store the timestamp of the event (may be NULL to ignore) | ||
568 | * | ||
569 | * Returns a pointer to the data part of the current event. | ||
570 | * NULL if no event is left on the subbuffer. | ||
571 | */ | ||
572 | void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts) | ||
573 | { | ||
574 | if (!kbuf || !kbuf->subbuffer) | ||
575 | return NULL; | ||
576 | |||
577 | if (kbuf->curr >= kbuf->size) | ||
578 | return NULL; | ||
579 | |||
580 | if (ts) | ||
581 | *ts = kbuf->timestamp; | ||
582 | return kbuf->data + kbuf->index; | ||
583 | } | ||
584 | |||
585 | /** | ||
586 | * kbuffer_timestamp - Return the timestamp of the current event | ||
587 | * @kbuf: The kbuffer to read from | ||
588 | * | ||
589 | * Returns the timestamp of the current (next) event. | ||
590 | */ | ||
591 | unsigned long long kbuffer_timestamp(struct kbuffer *kbuf) | ||
592 | { | ||
593 | return kbuf->timestamp; | ||
594 | } | ||
595 | |||
596 | /** | ||
597 | * kbuffer_read_at_offset - read the event that is at offset | ||
598 | * @kbuf: The kbuffer to read from | ||
599 | * @offset: The offset into the subbuffer | ||
600 | * @ts: The address to store the timestamp of the event (may be NULL to ignore) | ||
601 | * | ||
602 | * The @offset must be an index from the @kbuf subbuffer beginning. | ||
603 | * If @offset is bigger than the stored subbuffer, NULL will be returned. | ||
604 | * | ||
605 | * Returns the data of the record that is at @offset. Note, @offset does | ||
606 | * not need to be the start of the record, the offset just needs to be | ||
607 | * in the record (or beginning of it). | ||
608 | * | ||
609 | * Note, the kbuf timestamp and pointers are updated to the | ||
610 | * returned record. That is, kbuffer_read_event() will return the same | ||
611 | * data and timestamp, and kbuffer_next_event() will increment from | ||
612 | * this record. | ||
613 | */ | ||
614 | void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, | ||
615 | unsigned long long *ts) | ||
616 | { | ||
617 | void *data; | ||
618 | |||
619 | if (offset < kbuf->start) | ||
620 | offset = 0; | ||
621 | else | ||
622 | offset -= kbuf->start; | ||
623 | |||
624 | /* Reset the buffer */ | ||
625 | kbuffer_load_subbuffer(kbuf, kbuf->subbuffer); | ||
626 | |||
627 | while (kbuf->curr < offset) { | ||
628 | data = kbuffer_next_event(kbuf, ts); | ||
629 | if (!data) | ||
630 | break; | ||
631 | } | ||
632 | |||
633 | return data; | ||
634 | } | ||
635 | |||
636 | /** | ||
637 | * kbuffer_subbuffer_size - the size of the loaded subbuffer | ||
638 | * @kbuf: The kbuffer to read from | ||
639 | * | ||
640 | * Returns the size of the subbuffer. Note, this size is | ||
641 | * where the last event resides. The stored subbuffer may actually be | ||
642 | * bigger due to padding and such. | ||
643 | */ | ||
644 | int kbuffer_subbuffer_size(struct kbuffer *kbuf) | ||
645 | { | ||
646 | return kbuf->size; | ||
647 | } | ||
648 | |||
649 | /** | ||
650 | * kbuffer_curr_index - Return the index of the record | ||
651 | * @kbuf: The kbuffer to read from | ||
652 | * | ||
653 | * Returns the index from the start of the data part of | ||
654 | * the subbuffer to the current location. Note this is not | ||
655 | * from the start of the subbuffer. An index of zero will | ||
656 | * point to the first record. Use kbuffer_curr_offset() for | ||
657 | * the actually offset (that can be used by kbuffer_read_at_offset()) | ||
658 | */ | ||
659 | int kbuffer_curr_index(struct kbuffer *kbuf) | ||
660 | { | ||
661 | return kbuf->curr; | ||
662 | } | ||
663 | |||
664 | /** | ||
665 | * kbuffer_curr_offset - Return the offset of the record | ||
666 | * @kbuf: The kbuffer to read from | ||
667 | * | ||
668 | * Returns the offset from the start of the subbuffer to the | ||
669 | * current location. | ||
670 | */ | ||
671 | int kbuffer_curr_offset(struct kbuffer *kbuf) | ||
672 | { | ||
673 | return kbuf->curr + kbuf->start; | ||
674 | } | ||
675 | |||
676 | /** | ||
677 | * kbuffer_event_size - return the size of the event data | ||
678 | * @kbuf: The kbuffer to read | ||
679 | * | ||
680 | * Returns the size of the event data (the payload not counting | ||
681 | * the meta data of the record) of the current event. | ||
682 | */ | ||
683 | int kbuffer_event_size(struct kbuffer *kbuf) | ||
684 | { | ||
685 | return kbuf->next - kbuf->index; | ||
686 | } | ||
687 | |||
688 | /** | ||
689 | * kbuffer_curr_size - return the size of the entire record | ||
690 | * @kbuf: The kbuffer to read | ||
691 | * | ||
692 | * Returns the size of the entire record (meta data and payload) | ||
693 | * of the current event. | ||
694 | */ | ||
695 | int kbuffer_curr_size(struct kbuffer *kbuf) | ||
696 | { | ||
697 | return kbuf->next - kbuf->curr; | ||
698 | } | ||
699 | |||
700 | /** | ||
701 | * kbuffer_missed_events - return the # of missed events from last event. | ||
702 | * @kbuf: The kbuffer to read from | ||
703 | * | ||
704 | * Returns the # of missed events (if recorded) before the current | ||
705 | * event. Note, only events on the beginning of a subbuffer can | ||
706 | * have missed events, all other events within the buffer will be | ||
707 | * zero. | ||
708 | */ | ||
709 | int kbuffer_missed_events(struct kbuffer *kbuf) | ||
710 | { | ||
711 | /* Only the first event can have missed events */ | ||
712 | if (kbuf->curr) | ||
713 | return 0; | ||
714 | |||
715 | return kbuf->lost_events; | ||
716 | } | ||
717 | |||
718 | /** | ||
719 | * kbuffer_set_old_forma - set the kbuffer to use the old format parsing | ||
720 | * @kbuf: The kbuffer to set | ||
721 | * | ||
722 | * This is obsolete (or should be). The first kernels to use the | ||
723 | * new ring buffer had a slightly different ring buffer format | ||
724 | * (2.6.30 and earlier). It is still somewhat supported by kbuffer, | ||
725 | * but should not be counted on in the future. | ||
726 | */ | ||
727 | void kbuffer_set_old_format(struct kbuffer *kbuf) | ||
728 | { | ||
729 | kbuf->flags |= KBUFFER_FL_OLD_FORMAT; | ||
730 | |||
731 | kbuf->next_event = __old_next_event; | ||
732 | } | ||
diff --git a/tools/lib/traceevent/kbuffer.h b/tools/lib/traceevent/kbuffer.h new file mode 100644 index 000000000000..c831f64b17a0 --- /dev/null +++ b/tools/lib/traceevent/kbuffer.h | |||
@@ -0,0 +1,67 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 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, write to the Free Software | ||
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
18 | * | ||
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
20 | */ | ||
21 | #ifndef _KBUFFER_H | ||
22 | #define _KBUFFER_H | ||
23 | |||
24 | #ifndef TS_SHIFT | ||
25 | #define TS_SHIFT 27 | ||
26 | #endif | ||
27 | |||
28 | enum kbuffer_endian { | ||
29 | KBUFFER_ENDIAN_BIG, | ||
30 | KBUFFER_ENDIAN_LITTLE, | ||
31 | }; | ||
32 | |||
33 | enum kbuffer_long_size { | ||
34 | KBUFFER_LSIZE_4, | ||
35 | KBUFFER_LSIZE_8, | ||
36 | }; | ||
37 | |||
38 | enum { | ||
39 | KBUFFER_TYPE_PADDING = 29, | ||
40 | KBUFFER_TYPE_TIME_EXTEND = 30, | ||
41 | KBUFFER_TYPE_TIME_STAMP = 31, | ||
42 | }; | ||
43 | |||
44 | struct kbuffer; | ||
45 | |||
46 | struct kbuffer *kbuffer_alloc(enum kbuffer_long_size size, enum kbuffer_endian endian); | ||
47 | void kbuffer_free(struct kbuffer *kbuf); | ||
48 | int kbuffer_load_subbuffer(struct kbuffer *kbuf, void *subbuffer); | ||
49 | void *kbuffer_read_event(struct kbuffer *kbuf, unsigned long long *ts); | ||
50 | void *kbuffer_next_event(struct kbuffer *kbuf, unsigned long long *ts); | ||
51 | unsigned long long kbuffer_timestamp(struct kbuffer *kbuf); | ||
52 | |||
53 | void *kbuffer_translate_data(int swap, void *data, unsigned int *size); | ||
54 | |||
55 | void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, unsigned long long *ts); | ||
56 | |||
57 | int kbuffer_curr_index(struct kbuffer *kbuf); | ||
58 | |||
59 | int kbuffer_curr_offset(struct kbuffer *kbuf); | ||
60 | int kbuffer_curr_size(struct kbuffer *kbuf); | ||
61 | int kbuffer_event_size(struct kbuffer *kbuf); | ||
62 | int kbuffer_missed_events(struct kbuffer *kbuf); | ||
63 | int kbuffer_subbuffer_size(struct kbuffer *kbuf); | ||
64 | |||
65 | void kbuffer_set_old_format(struct kbuffer *kbuf); | ||
66 | |||
67 | #endif /* _K_BUFFER_H */ | ||
diff --git a/tools/lib/traceevent/trace-seq.c b/tools/lib/traceevent/trace-seq.c index a57db805136a..d7f2e68bc5b9 100644 --- a/tools/lib/traceevent/trace-seq.c +++ b/tools/lib/traceevent/trace-seq.c | |||
@@ -49,6 +49,19 @@ void trace_seq_init(struct trace_seq *s) | |||
49 | } | 49 | } |
50 | 50 | ||
51 | /** | 51 | /** |
52 | * trace_seq_reset - re-initialize the trace_seq structure | ||
53 | * @s: a pointer to the trace_seq structure to reset | ||
54 | */ | ||
55 | void trace_seq_reset(struct trace_seq *s) | ||
56 | { | ||
57 | if (!s) | ||
58 | return; | ||
59 | TRACE_SEQ_CHECK(s); | ||
60 | s->len = 0; | ||
61 | s->readpos = 0; | ||
62 | } | ||
63 | |||
64 | /** | ||
52 | * trace_seq_destroy - free up memory of a trace_seq | 65 | * trace_seq_destroy - free up memory of a trace_seq |
53 | * @s: a pointer to the trace_seq to free the buffer | 66 | * @s: a pointer to the trace_seq to free the buffer |
54 | * | 67 | * |
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt index 5b3123d5721f..fdfceee0ffd0 100644 --- a/tools/perf/Documentation/perf-diff.txt +++ b/tools/perf/Documentation/perf-diff.txt | |||
@@ -3,17 +3,17 @@ perf-diff(1) | |||
3 | 3 | ||
4 | NAME | 4 | NAME |
5 | ---- | 5 | ---- |
6 | perf-diff - Read two perf.data files and display the differential profile | 6 | perf-diff - Read perf.data files and display the differential profile |
7 | 7 | ||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf diff' [oldfile] [newfile] | 11 | 'perf diff' [baseline file] [data file1] [[data file2] ... ] |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | This command displays the performance difference amongst two perf.data files | 15 | This command displays the performance difference amongst two or more perf.data |
16 | captured via perf record. | 16 | files captured via perf record. |
17 | 17 | ||
18 | If no parameters are passed it will assume perf.data.old and perf.data. | 18 | If no parameters are passed it will assume perf.data.old and perf.data. |
19 | 19 | ||
@@ -75,8 +75,6 @@ OPTIONS | |||
75 | -c:: | 75 | -c:: |
76 | --compute:: | 76 | --compute:: |
77 | Differential computation selection - delta,ratio,wdiff (default is delta). | 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. | 78 | See COMPARISON METHODS section for more info. |
81 | 79 | ||
82 | -p:: | 80 | -p:: |
@@ -87,6 +85,63 @@ OPTIONS | |||
87 | --formula:: | 85 | --formula:: |
88 | Show formula for given computation. | 86 | Show formula for given computation. |
89 | 87 | ||
88 | -o:: | ||
89 | --order:: | ||
90 | Specify compute sorting column number. | ||
91 | |||
92 | COMPARISON | ||
93 | ---------- | ||
94 | The comparison is governed by the baseline file. The baseline perf.data | ||
95 | file is iterated for samples. All other perf.data files specified on | ||
96 | the command line are searched for the baseline sample pair. If the pair | ||
97 | is found, specified computation is made and result is displayed. | ||
98 | |||
99 | All samples from non-baseline perf.data files, that do not match any | ||
100 | baseline entry, are displayed with empty space within baseline column | ||
101 | and possible computation results (delta) in their related column. | ||
102 | |||
103 | Example files samples: | ||
104 | - file A with samples f1, f2, f3, f4, f6 | ||
105 | - file B with samples f2, f4, f5 | ||
106 | - file C with samples f1, f2, f5 | ||
107 | |||
108 | Example output: | ||
109 | x - computation takes place for pair | ||
110 | b - baseline sample percentage | ||
111 | |||
112 | - perf diff A B C | ||
113 | |||
114 | baseline/A compute/B compute/C samples | ||
115 | --------------------------------------- | ||
116 | b x f1 | ||
117 | b x x f2 | ||
118 | b f3 | ||
119 | b x f4 | ||
120 | b f6 | ||
121 | x x f5 | ||
122 | |||
123 | - perf diff B A C | ||
124 | |||
125 | baseline/B compute/A compute/C samples | ||
126 | --------------------------------------- | ||
127 | b x x f2 | ||
128 | b x f4 | ||
129 | b x f5 | ||
130 | x x f1 | ||
131 | x f3 | ||
132 | x f6 | ||
133 | |||
134 | - perf diff C B A | ||
135 | |||
136 | baseline/C compute/B compute/A samples | ||
137 | --------------------------------------- | ||
138 | b x f1 | ||
139 | b x x f2 | ||
140 | b x f5 | ||
141 | x f3 | ||
142 | x x f4 | ||
143 | x f6 | ||
144 | |||
90 | COMPARISON METHODS | 145 | COMPARISON METHODS |
91 | ------------------ | 146 | ------------------ |
92 | delta | 147 | delta |
@@ -96,7 +151,7 @@ If specified the 'Delta' column is displayed with value 'd' computed as: | |||
96 | d = A->period_percent - B->period_percent | 151 | d = A->period_percent - B->period_percent |
97 | 152 | ||
98 | with: | 153 | with: |
99 | - A/B being matching hist entry from first/second file specified | 154 | - A/B being matching hist entry from data/baseline file specified |
100 | (or perf.data/perf.data.old) respectively. | 155 | (or perf.data/perf.data.old) respectively. |
101 | 156 | ||
102 | - period_percent being the % of the hist entry period value within | 157 | - period_percent being the % of the hist entry period value within |
@@ -109,24 +164,26 @@ If specified the 'Ratio' column is displayed with value 'r' computed as: | |||
109 | r = A->period / B->period | 164 | r = A->period / B->period |
110 | 165 | ||
111 | with: | 166 | with: |
112 | - A/B being matching hist entry from first/second file specified | 167 | - A/B being matching hist entry from data/baseline file specified |
113 | (or perf.data/perf.data.old) respectively. | 168 | (or perf.data/perf.data.old) respectively. |
114 | 169 | ||
115 | - period being the hist entry period value | 170 | - period being the hist entry period value |
116 | 171 | ||
117 | wdiff | 172 | wdiff:WEIGHT-B,WEIGHT-A |
118 | ~~~~~ | 173 | ~~~~~~~~~~~~~~~~~~~~~~~ |
119 | If specified the 'Weighted diff' column is displayed with value 'd' computed as: | 174 | If specified the 'Weighted diff' column is displayed with value 'd' computed as: |
120 | 175 | ||
121 | d = B->period * WEIGHT-A - A->period * WEIGHT-B | 176 | d = B->period * WEIGHT-A - A->period * WEIGHT-B |
122 | 177 | ||
123 | - A/B being matching hist entry from first/second file specified | 178 | - A/B being matching hist entry from data/baseline file specified |
124 | (or perf.data/perf.data.old) respectively. | 179 | (or perf.data/perf.data.old) respectively. |
125 | 180 | ||
126 | - period being the hist entry period value | 181 | - period being the hist entry period value |
127 | 182 | ||
128 | - WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option | 183 | - WEIGHT-A/WEIGHT-B being user suplied weights in the the '-c' option |
129 | behind ':' separator like '-c wdiff:1,2'. | 184 | behind ':' separator like '-c wdiff:1,2'. |
185 | - WIEGHT-A being the weight of the data file | ||
186 | - WIEGHT-B being the weight of the baseline data file | ||
130 | 187 | ||
131 | SEE ALSO | 188 | SEE ALSO |
132 | -------- | 189 | -------- |
diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index 326f2cb333cb..ac84db2d2334 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt | |||
@@ -13,6 +13,7 @@ SYNOPSIS | |||
13 | {top|record|report|diff|buildid-list} | 13 | {top|record|report|diff|buildid-list} |
14 | 'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path> | 14 | 'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path> |
15 | | --guestvmlinux=<path>] {top|record|report|diff|buildid-list|stat} | 15 | | --guestvmlinux=<path>] {top|record|report|diff|buildid-list|stat} |
16 | 'perf kvm stat [record|report|live] [<options>] | ||
16 | 17 | ||
17 | DESCRIPTION | 18 | DESCRIPTION |
18 | ----------- | 19 | ----------- |
@@ -50,6 +51,10 @@ There are a couple of variants of perf kvm: | |||
50 | 'perf kvm stat report' reports statistical data which includes events | 51 | 'perf kvm stat report' reports statistical data which includes events |
51 | handled time, samples, and so on. | 52 | handled time, samples, and so on. |
52 | 53 | ||
54 | 'perf kvm stat live' reports statistical data in a live mode (similar to | ||
55 | record + report but with statistical data updated live at a given display | ||
56 | rate). | ||
57 | |||
53 | OPTIONS | 58 | OPTIONS |
54 | ------- | 59 | ------- |
55 | -i:: | 60 | -i:: |
@@ -85,13 +90,50 @@ STAT REPORT OPTIONS | |||
85 | --vcpu=<value>:: | 90 | --vcpu=<value>:: |
86 | analyze events which occures on this vcpu. (default: all vcpus) | 91 | analyze events which occures on this vcpu. (default: all vcpus) |
87 | 92 | ||
88 | --events=<value>:: | 93 | --event=<value>:: |
89 | events to be analyzed. Possible values: vmexit, mmio, ioport. | 94 | event to be analyzed. Possible values: vmexit, mmio, ioport. |
90 | (default: vmexit) | 95 | (default: vmexit) |
91 | -k:: | 96 | -k:: |
92 | --key=<value>:: | 97 | --key=<value>:: |
93 | Sorting key. Possible values: sample (default, sort by samples | 98 | Sorting key. Possible values: sample (default, sort by samples |
94 | number), time (sort by average time). | 99 | number), time (sort by average time). |
100 | -p:: | ||
101 | --pid=:: | ||
102 | Analyze events only for given process ID(s) (comma separated list). | ||
103 | |||
104 | STAT LIVE OPTIONS | ||
105 | ----------------- | ||
106 | -d:: | ||
107 | --display:: | ||
108 | Time in seconds between display updates | ||
109 | |||
110 | -m:: | ||
111 | --mmap-pages=:: | ||
112 | Number of mmap data pages. Must be a power of two. | ||
113 | |||
114 | -a:: | ||
115 | --all-cpus:: | ||
116 | System-wide collection from all CPUs. | ||
117 | |||
118 | -p:: | ||
119 | --pid=:: | ||
120 | Analyze events only for given process ID(s) (comma separated list). | ||
121 | |||
122 | --vcpu=<value>:: | ||
123 | analyze events which occures on this vcpu. (default: all vcpus) | ||
124 | |||
125 | |||
126 | --event=<value>:: | ||
127 | event to be analyzed. Possible values: vmexit, mmio, ioport. | ||
128 | (default: vmexit) | ||
129 | |||
130 | -k:: | ||
131 | --key=<value>:: | ||
132 | Sorting key. Possible values: sample (default, sort by samples | ||
133 | number), time (sort by average time). | ||
134 | |||
135 | --duration=<value>:: | ||
136 | Show events other than HLT that take longer than duration usecs. | ||
95 | 137 | ||
96 | SEE ALSO | 138 | SEE ALSO |
97 | -------- | 139 | -------- |
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index d1e39dc8c810..6fce6a622206 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
@@ -8,7 +8,7 @@ perf-list - List all symbolic event types | |||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf list' [hw|sw|cache|tracepoint|event_glob] | 11 | 'perf list' [hw|sw|cache|tracepoint|pmu|event_glob] |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
@@ -29,6 +29,8 @@ counted. The following modifiers exist: | |||
29 | G - guest counting (in KVM guests) | 29 | G - guest counting (in KVM guests) |
30 | H - host counting (not in KVM guests) | 30 | H - host counting (not in KVM guests) |
31 | p - precise level | 31 | p - precise level |
32 | S - read sample value (PERF_SAMPLE_READ) | ||
33 | D - pin the event to the PMU | ||
32 | 34 | ||
33 | The 'p' modifier can be used for specifying how precise the instruction | 35 | The 'p' modifier can be used for specifying how precise the instruction |
34 | address should be. The 'p' modifier can be specified multiple times: | 36 | address should be. The 'p' modifier can be specified multiple times: |
@@ -104,6 +106,8 @@ To limit the list use: | |||
104 | 'subsys_glob:event_glob' to filter by tracepoint subsystems such as sched, | 106 | 'subsys_glob:event_glob' to filter by tracepoint subsystems such as sched, |
105 | block, etc. | 107 | block, etc. |
106 | 108 | ||
109 | . 'pmu' to print the kernel supplied PMU events. | ||
110 | |||
107 | . If none of the above is matched, it will apply the supplied glob to all | 111 | . If none of the above is matched, it will apply the supplied glob to all |
108 | events, printing the ones that match. | 112 | events, printing the ones that match. |
109 | 113 | ||
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index e297b74471b8..ca0d3d9f4bac 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -90,8 +90,20 @@ OPTIONS | |||
90 | Number of mmap data pages. Must be a power of two. | 90 | Number of mmap data pages. Must be a power of two. |
91 | 91 | ||
92 | -g:: | 92 | -g:: |
93 | Enables call-graph (stack chain/backtrace) recording. | ||
94 | |||
93 | --call-graph:: | 95 | --call-graph:: |
94 | Do call-graph (stack chain/backtrace) recording. | 96 | Setup and enable call-graph (stack chain/backtrace) recording, |
97 | implies -g. | ||
98 | |||
99 | Allows specifying "fp" (frame pointer) or "dwarf" | ||
100 | (DWARF's CFI - Call Frame Information) as the method to collect | ||
101 | the information used to show the call graphs. | ||
102 | |||
103 | In some systems, where binaries are build with gcc | ||
104 | --fomit-frame-pointer, using the "fp" method will produce bogus | ||
105 | call graphs, using "dwarf", if available (perf tools linked to | ||
106 | the libunwind library) should be used instead. | ||
95 | 107 | ||
96 | -q:: | 108 | -q:: |
97 | --quiet:: | 109 | --quiet:: |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 66dab7410c1d..2b8097ee39d8 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -115,7 +115,7 @@ OPTIONS | |||
115 | --dump-raw-trace:: | 115 | --dump-raw-trace:: |
116 | Dump raw trace in ASCII. | 116 | Dump raw trace in ASCII. |
117 | 117 | ||
118 | -g [type,min[,limit],order]:: | 118 | -g [type,min[,limit],order[,key]]:: |
119 | --call-graph:: | 119 | --call-graph:: |
120 | Display call chains using type, min percent threshold, optional print | 120 | Display call chains using type, min percent threshold, optional print |
121 | limit and order. | 121 | limit and order. |
@@ -129,12 +129,21 @@ OPTIONS | |||
129 | - callee: callee based call graph. | 129 | - callee: callee based call graph. |
130 | - caller: inverted caller based call graph. | 130 | - caller: inverted caller based call graph. |
131 | 131 | ||
132 | Default: fractal,0.5,callee. | 132 | key can be: |
133 | - function: compare on functions | ||
134 | - address: compare on individual code addresses | ||
135 | |||
136 | Default: fractal,0.5,callee,function. | ||
133 | 137 | ||
134 | -G:: | 138 | -G:: |
135 | --inverted:: | 139 | --inverted:: |
136 | alias for inverted caller based call graph. | 140 | alias for inverted caller based call graph. |
137 | 141 | ||
142 | --ignore-callees=<regex>:: | ||
143 | Ignore callees of the function(s) matching the given regex. | ||
144 | This has the effect of collecting the callers of each such | ||
145 | function into one place in the call-graph tree. | ||
146 | |||
138 | --pretty=<key>:: | 147 | --pretty=<key>:: |
139 | Pretty printing style. key: normal, raw | 148 | Pretty printing style. key: normal, raw |
140 | 149 | ||
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 2fe87fb558f0..73c9759005a3 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -132,6 +132,11 @@ is a useful mode to detect imbalance between physical cores. To enable this mod | |||
132 | use --per-core in addition to -a. (system-wide). The output includes the | 132 | use --per-core in addition to -a. (system-wide). The output includes the |
133 | core number and the number of online logical processors on that physical processor. | 133 | core number and the number of online logical processors on that physical processor. |
134 | 134 | ||
135 | -D msecs:: | ||
136 | --initial-delay msecs:: | ||
137 | After starting the program, wait msecs before measuring. This is useful to | ||
138 | filter out the startup phase of the program, which is often very different. | ||
139 | |||
135 | EXAMPLES | 140 | EXAMPLES |
136 | -------- | 141 | -------- |
137 | 142 | ||
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 7fdd1909e376..6a118e71d003 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -140,20 +140,17 @@ Default is to monitor all CPUS. | |||
140 | --asm-raw:: | 140 | --asm-raw:: |
141 | Show raw instruction encoding of assembly instructions. | 141 | Show raw instruction encoding of assembly instructions. |
142 | 142 | ||
143 | -G [type,min,order]:: | 143 | -G:: |
144 | Enables call-graph (stack chain/backtrace) recording. | ||
145 | |||
144 | --call-graph:: | 146 | --call-graph:: |
145 | Display call chains using type, min percent threshold and order. | 147 | Setup and enable call-graph (stack chain/backtrace) recording, |
146 | type can be either: | 148 | implies -G. |
147 | - flat: single column, linear exposure of call chains. | 149 | |
148 | - graph: use a graph tree, displaying absolute overhead rates. | 150 | --ignore-callees=<regex>:: |
149 | - fractal: like graph, but displays relative rates. Each branch of | 151 | Ignore callees of the function(s) matching the given regex. |
150 | the tree is considered as a new profiled object. | 152 | This has the effect of collecting the callers of each such |
151 | 153 | function into one place in the call-graph tree. | |
152 | order can be either: | ||
153 | - callee: callee based call graph. | ||
154 | - caller: inverted caller based call graph. | ||
155 | |||
156 | Default: fractal,0.5,callee. | ||
157 | 154 | ||
158 | --percent-limit:: | 155 | --percent-limit:: |
159 | Do not show entries which have an overhead under that percent. | 156 | Do not show entries which have an overhead under that percent. |
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 68718ccdd178..daccd2c0a48f 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
@@ -23,25 +23,45 @@ analysis phases. | |||
23 | OPTIONS | 23 | OPTIONS |
24 | ------- | 24 | ------- |
25 | 25 | ||
26 | -a:: | ||
26 | --all-cpus:: | 27 | --all-cpus:: |
27 | System-wide collection from all CPUs. | 28 | System-wide collection from all CPUs. |
28 | 29 | ||
30 | -e:: | ||
31 | --expr:: | ||
32 | List of events to show, currently only syscall names. | ||
33 | Prefixing with ! shows all syscalls but the ones specified. You may | ||
34 | need to escape it. | ||
35 | |||
36 | -o:: | ||
37 | --output=:: | ||
38 | Output file name. | ||
39 | |||
29 | -p:: | 40 | -p:: |
30 | --pid=:: | 41 | --pid=:: |
31 | Record events on existing process ID (comma separated list). | 42 | Record events on existing process ID (comma separated list). |
32 | 43 | ||
44 | -t:: | ||
33 | --tid=:: | 45 | --tid=:: |
34 | Record events on existing thread ID (comma separated list). | 46 | Record events on existing thread ID (comma separated list). |
35 | 47 | ||
48 | -u:: | ||
36 | --uid=:: | 49 | --uid=:: |
37 | Record events in threads owned by uid. Name or number. | 50 | Record events in threads owned by uid. Name or number. |
38 | 51 | ||
52 | -v:: | ||
53 | --verbose=:: | ||
54 | Verbosity level. | ||
55 | |||
56 | -i:: | ||
39 | --no-inherit:: | 57 | --no-inherit:: |
40 | Child tasks do not inherit counters. | 58 | Child tasks do not inherit counters. |
41 | 59 | ||
60 | -m:: | ||
42 | --mmap-pages=:: | 61 | --mmap-pages=:: |
43 | Number of mmap data pages. Must be a power of two. | 62 | Number of mmap data pages. Must be a power of two. |
44 | 63 | ||
64 | -C:: | ||
45 | --cpu:: | 65 | --cpu:: |
46 | Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a | 66 | Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a |
47 | comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. | 67 | comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. |
@@ -54,6 +74,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. | |||
54 | --sched: | 74 | --sched: |
55 | Accrue thread runtime and provide a summary at the end of the session. | 75 | Accrue thread runtime and provide a summary at the end of the session. |
56 | 76 | ||
77 | -i | ||
78 | --input | ||
79 | Process events from a given perf data file. | ||
80 | |||
57 | SEE ALSO | 81 | SEE ALSO |
58 | -------- | 82 | -------- |
59 | linkperf:perf-record[1], linkperf:perf-script[1] | 83 | linkperf:perf-record[1], linkperf:perf-script[1] |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 641fccddb249..64c043b7a438 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -124,7 +124,7 @@ strip-libs = $(filter-out -l%,$(1)) | |||
124 | ifneq ($(OUTPUT),) | 124 | ifneq ($(OUTPUT),) |
125 | TE_PATH=$(OUTPUT) | 125 | TE_PATH=$(OUTPUT) |
126 | ifneq ($(subdir),) | 126 | ifneq ($(subdir),) |
127 | LK_PATH=$(objtree)/lib/lk/ | 127 | LK_PATH=$(OUTPUT)/../lib/lk/ |
128 | else | 128 | else |
129 | LK_PATH=$(OUTPUT) | 129 | LK_PATH=$(OUTPUT) |
130 | endif | 130 | endif |
@@ -281,7 +281,7 @@ LIB_H += util/cpumap.h | |||
281 | LIB_H += util/top.h | 281 | LIB_H += util/top.h |
282 | LIB_H += $(ARCH_INCLUDE) | 282 | LIB_H += $(ARCH_INCLUDE) |
283 | LIB_H += util/cgroup.h | 283 | LIB_H += util/cgroup.h |
284 | LIB_H += $(TRACE_EVENT_DIR)event-parse.h | 284 | LIB_H += $(LIB_INCLUDE)traceevent/event-parse.h |
285 | LIB_H += util/target.h | 285 | LIB_H += util/target.h |
286 | LIB_H += util/rblist.h | 286 | LIB_H += util/rblist.h |
287 | LIB_H += util/intlist.h | 287 | LIB_H += util/intlist.h |
@@ -360,6 +360,7 @@ LIB_OBJS += $(OUTPUT)util/rblist.o | |||
360 | LIB_OBJS += $(OUTPUT)util/intlist.o | 360 | LIB_OBJS += $(OUTPUT)util/intlist.o |
361 | LIB_OBJS += $(OUTPUT)util/vdso.o | 361 | LIB_OBJS += $(OUTPUT)util/vdso.o |
362 | LIB_OBJS += $(OUTPUT)util/stat.o | 362 | LIB_OBJS += $(OUTPUT)util/stat.o |
363 | LIB_OBJS += $(OUTPUT)util/record.o | ||
363 | 364 | ||
364 | LIB_OBJS += $(OUTPUT)ui/setup.o | 365 | LIB_OBJS += $(OUTPUT)ui/setup.o |
365 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 366 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
@@ -389,6 +390,12 @@ LIB_OBJS += $(OUTPUT)tests/bp_signal.o | |||
389 | LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o | 390 | LIB_OBJS += $(OUTPUT)tests/bp_signal_overflow.o |
390 | LIB_OBJS += $(OUTPUT)tests/task-exit.o | 391 | LIB_OBJS += $(OUTPUT)tests/task-exit.o |
391 | LIB_OBJS += $(OUTPUT)tests/sw-clock.o | 392 | LIB_OBJS += $(OUTPUT)tests/sw-clock.o |
393 | ifeq ($(ARCH),x86) | ||
394 | LIB_OBJS += $(OUTPUT)tests/perf-time-to-tsc.o | ||
395 | endif | ||
396 | LIB_OBJS += $(OUTPUT)tests/code-reading.o | ||
397 | LIB_OBJS += $(OUTPUT)tests/sample-parsing.o | ||
398 | LIB_OBJS += $(OUTPUT)tests/parse-no-sample-id-all.o | ||
392 | 399 | ||
393 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o | 400 | BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o |
394 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o | 401 | BUILTIN_OBJS += $(OUTPUT)builtin-bench.o |
@@ -459,6 +466,7 @@ endif # NO_LIBELF | |||
459 | ifndef NO_LIBUNWIND | 466 | ifndef NO_LIBUNWIND |
460 | LIB_OBJS += $(OUTPUT)util/unwind.o | 467 | LIB_OBJS += $(OUTPUT)util/unwind.o |
461 | endif | 468 | endif |
469 | LIB_OBJS += $(OUTPUT)tests/keep-tracking.o | ||
462 | 470 | ||
463 | ifndef NO_LIBAUDIT | 471 | ifndef NO_LIBAUDIT |
464 | BUILTIN_OBJS += $(OUTPUT)builtin-trace.o | 472 | BUILTIN_OBJS += $(OUTPUT)builtin-trace.o |
@@ -631,10 +639,10 @@ $(OUTPUT)util/parse-events.o: util/parse-events.c $(OUTPUT)PERF-CFLAGS | |||
631 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-redundant-decls $< | 639 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) -Wno-redundant-decls $< |
632 | 640 | ||
633 | $(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS | 641 | $(OUTPUT)util/scripting-engines/trace-event-perl.o: util/scripting-engines/trace-event-perl.c $(OUTPUT)PERF-CFLAGS |
634 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< | 642 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow -Wno-undef -Wno-switch-default $< |
635 | 643 | ||
636 | $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS | 644 | $(OUTPUT)scripts/perl/Perf-Trace-Util/Context.o: scripts/perl/Perf-Trace-Util/Context.c $(OUTPUT)PERF-CFLAGS |
637 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs $< | 645 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PERL_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-nested-externs -Wno-undef -Wno-switch-default $< |
638 | 646 | ||
639 | $(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS | 647 | $(OUTPUT)util/scripting-engines/trace-event-python.o: util/scripting-engines/trace-event-python.c $(OUTPUT)PERF-CFLAGS |
640 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< | 648 | $(QUIET_CC)$(CC) -o $@ -c $(CFLAGS) $(PYTHON_EMBED_CCOPTS) -Wno-redundant-decls -Wno-strict-prototypes -Wno-unused-parameter -Wno-shadow $< |
@@ -762,17 +770,22 @@ check: $(OUTPUT)common-cmds.h | |||
762 | install-bin: all | 770 | install-bin: all |
763 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' | 771 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)' |
764 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' | 772 | $(INSTALL) $(OUTPUT)perf '$(DESTDIR_SQ)$(bindir_SQ)' |
773 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
774 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
775 | ifndef NO_LIBPERL | ||
765 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 776 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
766 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | 777 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' |
767 | $(INSTALL) $(OUTPUT)perf-archive -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)' | ||
768 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' | 778 | $(INSTALL) scripts/perl/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/Perf-Trace-Util/lib/Perf/Trace' |
769 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' | 779 | $(INSTALL) scripts/perl/*.pl -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl' |
770 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' | 780 | $(INSTALL) scripts/perl/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/perl/bin' |
781 | endif | ||
782 | ifndef NO_LIBPYTHON | ||
771 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' | 783 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' |
772 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | 784 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' |
773 | $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' | 785 | $(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace' |
774 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' | 786 | $(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python' |
775 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' | 787 | $(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin' |
788 | endif | ||
776 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' | 789 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d' |
777 | $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' | 790 | $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf' |
778 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' | 791 | $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests' |
diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile index 815841c04eb2..8801fe02f206 100644 --- a/tools/perf/arch/x86/Makefile +++ b/tools/perf/arch/x86/Makefile | |||
@@ -6,3 +6,5 @@ ifndef NO_LIBUNWIND | |||
6 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind.o | 6 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind.o |
7 | endif | 7 | endif |
8 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o | 8 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o |
9 | LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/tsc.o | ||
10 | LIB_H += arch/$(ARCH)/util/tsc.h | ||
diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c new file mode 100644 index 000000000000..b2519e49424f --- /dev/null +++ b/tools/perf/arch/x86/util/tsc.c | |||
@@ -0,0 +1,59 @@ | |||
1 | #include <stdbool.h> | ||
2 | #include <errno.h> | ||
3 | |||
4 | #include <linux/perf_event.h> | ||
5 | |||
6 | #include "../../perf.h" | ||
7 | #include "../../util/types.h" | ||
8 | #include "../../util/debug.h" | ||
9 | #include "tsc.h" | ||
10 | |||
11 | u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc) | ||
12 | { | ||
13 | u64 t, quot, rem; | ||
14 | |||
15 | t = ns - tc->time_zero; | ||
16 | quot = t / tc->time_mult; | ||
17 | rem = t % tc->time_mult; | ||
18 | return (quot << tc->time_shift) + | ||
19 | (rem << tc->time_shift) / tc->time_mult; | ||
20 | } | ||
21 | |||
22 | u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc) | ||
23 | { | ||
24 | u64 quot, rem; | ||
25 | |||
26 | quot = cyc >> tc->time_shift; | ||
27 | rem = cyc & ((1 << tc->time_shift) - 1); | ||
28 | return tc->time_zero + quot * tc->time_mult + | ||
29 | ((rem * tc->time_mult) >> tc->time_shift); | ||
30 | } | ||
31 | |||
32 | int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, | ||
33 | struct perf_tsc_conversion *tc) | ||
34 | { | ||
35 | bool cap_user_time_zero; | ||
36 | u32 seq; | ||
37 | int i = 0; | ||
38 | |||
39 | while (1) { | ||
40 | seq = pc->lock; | ||
41 | rmb(); | ||
42 | tc->time_mult = pc->time_mult; | ||
43 | tc->time_shift = pc->time_shift; | ||
44 | tc->time_zero = pc->time_zero; | ||
45 | cap_user_time_zero = pc->cap_user_time_zero; | ||
46 | rmb(); | ||
47 | if (pc->lock == seq && !(seq & 1)) | ||
48 | break; | ||
49 | if (++i > 10000) { | ||
50 | pr_debug("failed to get perf_event_mmap_page lock\n"); | ||
51 | return -EINVAL; | ||
52 | } | ||
53 | } | ||
54 | |||
55 | if (!cap_user_time_zero) | ||
56 | return -EOPNOTSUPP; | ||
57 | |||
58 | return 0; | ||
59 | } | ||
diff --git a/tools/perf/arch/x86/util/tsc.h b/tools/perf/arch/x86/util/tsc.h new file mode 100644 index 000000000000..a24dec81c795 --- /dev/null +++ b/tools/perf/arch/x86/util/tsc.h | |||
@@ -0,0 +1,20 @@ | |||
1 | #ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ | ||
2 | #define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ | ||
3 | |||
4 | #include "../../util/types.h" | ||
5 | |||
6 | struct perf_tsc_conversion { | ||
7 | u16 time_shift; | ||
8 | u32 time_mult; | ||
9 | u64 time_zero; | ||
10 | }; | ||
11 | |||
12 | struct perf_event_mmap_page; | ||
13 | |||
14 | int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, | ||
15 | struct perf_tsc_conversion *tc); | ||
16 | |||
17 | u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc); | ||
18 | u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc); | ||
19 | |||
20 | #endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */ | ||
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index 25fd3f1966f1..8cdca43016b2 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c | |||
@@ -117,6 +117,8 @@ static void alloc_mem(void **dst, void **src, size_t length) | |||
117 | *src = zalloc(length); | 117 | *src = zalloc(length); |
118 | if (!*src) | 118 | if (!*src) |
119 | die("memory allocation failed - maybe length is too large?\n"); | 119 | die("memory allocation failed - maybe length is too large?\n"); |
120 | /* Make sure to always replace the zero pages even if MMAP_THRESH is crossed */ | ||
121 | memset(*src, 0, length); | ||
120 | } | 122 | } |
121 | 123 | ||
122 | static u64 do_memcpy_cycle(memcpy_t fn, size_t len, bool prefault) | 124 | static u64 do_memcpy_cycle(memcpy_t fn, size_t len, bool prefault) |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index db491e9a812b..5ebd0c3b71b6 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -90,8 +90,7 @@ static int process_sample_event(struct perf_tool *tool, | |||
90 | struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool); | 90 | struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool); |
91 | struct addr_location al; | 91 | struct addr_location al; |
92 | 92 | ||
93 | if (perf_event__preprocess_sample(event, machine, &al, sample, | 93 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { |
94 | symbol__annotate_init) < 0) { | ||
95 | pr_warning("problem processing %d event, skipping it.\n", | 94 | pr_warning("problem processing %d event, skipping it.\n", |
96 | event->header.type); | 95 | event->header.type); |
97 | return -1; | 96 | return -1; |
@@ -195,6 +194,8 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
195 | if (session == NULL) | 194 | if (session == NULL) |
196 | return -ENOMEM; | 195 | return -ENOMEM; |
197 | 196 | ||
197 | machines__set_symbol_filter(&session->machines, symbol__annotate_init); | ||
198 | |||
198 | if (ann->cpu_list) { | 199 | if (ann->cpu_list) { |
199 | ret = perf_session__cpu_bitmap(session, ann->cpu_list, | 200 | ret = perf_session__cpu_bitmap(session, ann->cpu_list, |
200 | ann->cpu_bitmap); | 201 | ann->cpu_bitmap); |
@@ -276,6 +277,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused) | |||
276 | .tool = { | 277 | .tool = { |
277 | .sample = process_sample_event, | 278 | .sample = process_sample_event, |
278 | .mmap = perf_event__process_mmap, | 279 | .mmap = perf_event__process_mmap, |
280 | .mmap2 = perf_event__process_mmap2, | ||
279 | .comm = perf_event__process_comm, | 281 | .comm = perf_event__process_comm, |
280 | .exit = perf_event__process_exit, | 282 | .exit = perf_event__process_exit, |
281 | .fork = perf_event__process_fork, | 283 | .fork = perf_event__process_fork, |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 0aac5f3e594d..f28799e94f2a 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -18,15 +18,53 @@ | |||
18 | #include "util/util.h" | 18 | #include "util/util.h" |
19 | 19 | ||
20 | #include <stdlib.h> | 20 | #include <stdlib.h> |
21 | #include <math.h> | ||
21 | 22 | ||
22 | static char const *input_old = "perf.data.old", | 23 | /* Diff command specific HPP columns. */ |
23 | *input_new = "perf.data"; | 24 | enum { |
24 | static char diff__default_sort_order[] = "dso,symbol"; | 25 | PERF_HPP_DIFF__BASELINE, |
25 | static bool force; | 26 | PERF_HPP_DIFF__PERIOD, |
27 | PERF_HPP_DIFF__PERIOD_BASELINE, | ||
28 | PERF_HPP_DIFF__DELTA, | ||
29 | PERF_HPP_DIFF__RATIO, | ||
30 | PERF_HPP_DIFF__WEIGHTED_DIFF, | ||
31 | PERF_HPP_DIFF__FORMULA, | ||
32 | |||
33 | PERF_HPP_DIFF__MAX_INDEX | ||
34 | }; | ||
35 | |||
36 | struct diff_hpp_fmt { | ||
37 | struct perf_hpp_fmt fmt; | ||
38 | int idx; | ||
39 | char *header; | ||
40 | int header_width; | ||
41 | }; | ||
42 | |||
43 | struct data__file { | ||
44 | struct perf_session *session; | ||
45 | const char *file; | ||
46 | int idx; | ||
47 | struct hists *hists; | ||
48 | struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX]; | ||
49 | }; | ||
50 | |||
51 | static struct data__file *data__files; | ||
52 | static int data__files_cnt; | ||
53 | |||
54 | #define data__for_each_file_start(i, d, s) \ | ||
55 | for (i = s, d = &data__files[s]; \ | ||
56 | i < data__files_cnt; \ | ||
57 | i++, d = &data__files[i]) | ||
58 | |||
59 | #define data__for_each_file(i, d) data__for_each_file_start(i, d, 0) | ||
60 | #define data__for_each_file_new(i, d) data__for_each_file_start(i, d, 1) | ||
61 | |||
62 | static char diff__default_sort_order[] = "dso,symbol"; | ||
63 | static bool force; | ||
26 | static bool show_period; | 64 | static bool show_period; |
27 | static bool show_formula; | 65 | static bool show_formula; |
28 | static bool show_baseline_only; | 66 | static bool show_baseline_only; |
29 | static bool sort_compute; | 67 | static unsigned int sort_compute; |
30 | 68 | ||
31 | static s64 compute_wdiff_w1; | 69 | static s64 compute_wdiff_w1; |
32 | static s64 compute_wdiff_w2; | 70 | static s64 compute_wdiff_w2; |
@@ -46,6 +84,47 @@ const char *compute_names[COMPUTE_MAX] = { | |||
46 | 84 | ||
47 | static int compute; | 85 | static int compute; |
48 | 86 | ||
87 | static int compute_2_hpp[COMPUTE_MAX] = { | ||
88 | [COMPUTE_DELTA] = PERF_HPP_DIFF__DELTA, | ||
89 | [COMPUTE_RATIO] = PERF_HPP_DIFF__RATIO, | ||
90 | [COMPUTE_WEIGHTED_DIFF] = PERF_HPP_DIFF__WEIGHTED_DIFF, | ||
91 | }; | ||
92 | |||
93 | #define MAX_COL_WIDTH 70 | ||
94 | |||
95 | static struct header_column { | ||
96 | const char *name; | ||
97 | int width; | ||
98 | } columns[PERF_HPP_DIFF__MAX_INDEX] = { | ||
99 | [PERF_HPP_DIFF__BASELINE] = { | ||
100 | .name = "Baseline", | ||
101 | }, | ||
102 | [PERF_HPP_DIFF__PERIOD] = { | ||
103 | .name = "Period", | ||
104 | .width = 14, | ||
105 | }, | ||
106 | [PERF_HPP_DIFF__PERIOD_BASELINE] = { | ||
107 | .name = "Base period", | ||
108 | .width = 14, | ||
109 | }, | ||
110 | [PERF_HPP_DIFF__DELTA] = { | ||
111 | .name = "Delta", | ||
112 | .width = 7, | ||
113 | }, | ||
114 | [PERF_HPP_DIFF__RATIO] = { | ||
115 | .name = "Ratio", | ||
116 | .width = 14, | ||
117 | }, | ||
118 | [PERF_HPP_DIFF__WEIGHTED_DIFF] = { | ||
119 | .name = "Weighted diff", | ||
120 | .width = 14, | ||
121 | }, | ||
122 | [PERF_HPP_DIFF__FORMULA] = { | ||
123 | .name = "Formula", | ||
124 | .width = MAX_COL_WIDTH, | ||
125 | } | ||
126 | }; | ||
127 | |||
49 | static int setup_compute_opt_wdiff(char *opt) | 128 | static int setup_compute_opt_wdiff(char *opt) |
50 | { | 129 | { |
51 | char *w1_str = opt; | 130 | char *w1_str = opt; |
@@ -109,13 +188,6 @@ static int setup_compute(const struct option *opt, const char *str, | |||
109 | return 0; | 188 | return 0; |
110 | } | 189 | } |
111 | 190 | ||
112 | if (*str == '+') { | ||
113 | sort_compute = true; | ||
114 | cstr = (char *) ++str; | ||
115 | if (!*str) | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | option = strchr(str, ':'); | 191 | option = strchr(str, ':'); |
120 | if (option) { | 192 | if (option) { |
121 | unsigned len = option++ - str; | 193 | unsigned len = option++ - str; |
@@ -145,42 +217,42 @@ static int setup_compute(const struct option *opt, const char *str, | |||
145 | return -EINVAL; | 217 | return -EINVAL; |
146 | } | 218 | } |
147 | 219 | ||
148 | double perf_diff__period_percent(struct hist_entry *he, u64 period) | 220 | static double period_percent(struct hist_entry *he, u64 period) |
149 | { | 221 | { |
150 | u64 total = he->hists->stats.total_period; | 222 | u64 total = he->hists->stats.total_period; |
151 | return (period * 100.0) / total; | 223 | return (period * 100.0) / total; |
152 | } | 224 | } |
153 | 225 | ||
154 | double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair) | 226 | static double compute_delta(struct hist_entry *he, struct hist_entry *pair) |
155 | { | 227 | { |
156 | double new_percent = perf_diff__period_percent(he, he->stat.period); | 228 | double old_percent = period_percent(he, he->stat.period); |
157 | double old_percent = perf_diff__period_percent(pair, pair->stat.period); | 229 | double new_percent = period_percent(pair, pair->stat.period); |
158 | 230 | ||
159 | he->diff.period_ratio_delta = new_percent - old_percent; | 231 | pair->diff.period_ratio_delta = new_percent - old_percent; |
160 | he->diff.computed = true; | 232 | pair->diff.computed = true; |
161 | return he->diff.period_ratio_delta; | 233 | return pair->diff.period_ratio_delta; |
162 | } | 234 | } |
163 | 235 | ||
164 | double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair) | 236 | static double compute_ratio(struct hist_entry *he, struct hist_entry *pair) |
165 | { | 237 | { |
166 | double new_period = he->stat.period; | 238 | double old_period = he->stat.period ?: 1; |
167 | double old_period = pair->stat.period; | 239 | double new_period = pair->stat.period; |
168 | 240 | ||
169 | he->diff.computed = true; | 241 | pair->diff.computed = true; |
170 | he->diff.period_ratio = new_period / old_period; | 242 | pair->diff.period_ratio = new_period / old_period; |
171 | return he->diff.period_ratio; | 243 | return pair->diff.period_ratio; |
172 | } | 244 | } |
173 | 245 | ||
174 | s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair) | 246 | static s64 compute_wdiff(struct hist_entry *he, struct hist_entry *pair) |
175 | { | 247 | { |
176 | u64 new_period = he->stat.period; | 248 | u64 old_period = he->stat.period; |
177 | u64 old_period = pair->stat.period; | 249 | u64 new_period = pair->stat.period; |
178 | 250 | ||
179 | he->diff.computed = true; | 251 | pair->diff.computed = true; |
180 | he->diff.wdiff = new_period * compute_wdiff_w2 - | 252 | pair->diff.wdiff = new_period * compute_wdiff_w2 - |
181 | old_period * compute_wdiff_w1; | 253 | old_period * compute_wdiff_w1; |
182 | 254 | ||
183 | return he->diff.wdiff; | 255 | return pair->diff.wdiff; |
184 | } | 256 | } |
185 | 257 | ||
186 | static int formula_delta(struct hist_entry *he, struct hist_entry *pair, | 258 | static int formula_delta(struct hist_entry *he, struct hist_entry *pair, |
@@ -189,15 +261,15 @@ static int formula_delta(struct hist_entry *he, struct hist_entry *pair, | |||
189 | return scnprintf(buf, size, | 261 | return scnprintf(buf, size, |
190 | "(%" PRIu64 " * 100 / %" PRIu64 ") - " | 262 | "(%" PRIu64 " * 100 / %" PRIu64 ") - " |
191 | "(%" PRIu64 " * 100 / %" PRIu64 ")", | 263 | "(%" PRIu64 " * 100 / %" PRIu64 ")", |
192 | he->stat.period, he->hists->stats.total_period, | 264 | pair->stat.period, pair->hists->stats.total_period, |
193 | pair->stat.period, pair->hists->stats.total_period); | 265 | he->stat.period, he->hists->stats.total_period); |
194 | } | 266 | } |
195 | 267 | ||
196 | static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, | 268 | static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, |
197 | char *buf, size_t size) | 269 | char *buf, size_t size) |
198 | { | 270 | { |
199 | double new_period = he->stat.period; | 271 | double old_period = he->stat.period; |
200 | double old_period = pair->stat.period; | 272 | double new_period = pair->stat.period; |
201 | 273 | ||
202 | return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); | 274 | return scnprintf(buf, size, "%.0F / %.0F", new_period, old_period); |
203 | } | 275 | } |
@@ -205,16 +277,16 @@ static int formula_ratio(struct hist_entry *he, struct hist_entry *pair, | |||
205 | static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair, | 277 | static int formula_wdiff(struct hist_entry *he, struct hist_entry *pair, |
206 | char *buf, size_t size) | 278 | char *buf, size_t size) |
207 | { | 279 | { |
208 | u64 new_period = he->stat.period; | 280 | u64 old_period = he->stat.period; |
209 | u64 old_period = pair->stat.period; | 281 | u64 new_period = pair->stat.period; |
210 | 282 | ||
211 | return scnprintf(buf, size, | 283 | return scnprintf(buf, size, |
212 | "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", | 284 | "(%" PRIu64 " * " "%" PRId64 ") - (%" PRIu64 " * " "%" PRId64 ")", |
213 | new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); | 285 | new_period, compute_wdiff_w2, old_period, compute_wdiff_w1); |
214 | } | 286 | } |
215 | 287 | ||
216 | int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, | 288 | static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair, |
217 | char *buf, size_t size) | 289 | char *buf, size_t size) |
218 | { | 290 | { |
219 | switch (compute) { | 291 | switch (compute) { |
220 | case COMPUTE_DELTA: | 292 | case COMPUTE_DELTA: |
@@ -247,7 +319,7 @@ static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | |||
247 | { | 319 | { |
248 | struct addr_location al; | 320 | struct addr_location al; |
249 | 321 | ||
250 | if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) { | 322 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { |
251 | pr_warning("problem processing %d event, skipping it.\n", | 323 | pr_warning("problem processing %d event, skipping it.\n", |
252 | event->header.type); | 324 | event->header.type); |
253 | return -1; | 325 | return -1; |
@@ -299,6 +371,29 @@ static void perf_evlist__collapse_resort(struct perf_evlist *evlist) | |||
299 | } | 371 | } |
300 | } | 372 | } |
301 | 373 | ||
374 | static struct hist_entry* | ||
375 | get_pair_data(struct hist_entry *he, struct data__file *d) | ||
376 | { | ||
377 | if (hist_entry__has_pairs(he)) { | ||
378 | struct hist_entry *pair; | ||
379 | |||
380 | list_for_each_entry(pair, &he->pairs.head, pairs.node) | ||
381 | if (pair->hists == d->hists) | ||
382 | return pair; | ||
383 | } | ||
384 | |||
385 | return NULL; | ||
386 | } | ||
387 | |||
388 | static struct hist_entry* | ||
389 | get_pair_fmt(struct hist_entry *he, struct diff_hpp_fmt *dfmt) | ||
390 | { | ||
391 | void *ptr = dfmt - dfmt->idx; | ||
392 | struct data__file *d = container_of(ptr, struct data__file, fmt); | ||
393 | |||
394 | return get_pair_data(he, d); | ||
395 | } | ||
396 | |||
302 | static void hists__baseline_only(struct hists *hists) | 397 | static void hists__baseline_only(struct hists *hists) |
303 | { | 398 | { |
304 | struct rb_root *root; | 399 | struct rb_root *root; |
@@ -333,22 +428,24 @@ static void hists__precompute(struct hists *hists) | |||
333 | 428 | ||
334 | next = rb_first(root); | 429 | next = rb_first(root); |
335 | while (next != NULL) { | 430 | while (next != NULL) { |
336 | struct hist_entry *he = rb_entry(next, struct hist_entry, rb_node_in); | 431 | struct hist_entry *he, *pair; |
337 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
338 | 432 | ||
433 | he = rb_entry(next, struct hist_entry, rb_node_in); | ||
339 | next = rb_next(&he->rb_node_in); | 434 | next = rb_next(&he->rb_node_in); |
435 | |||
436 | pair = get_pair_data(he, &data__files[sort_compute]); | ||
340 | if (!pair) | 437 | if (!pair) |
341 | continue; | 438 | continue; |
342 | 439 | ||
343 | switch (compute) { | 440 | switch (compute) { |
344 | case COMPUTE_DELTA: | 441 | case COMPUTE_DELTA: |
345 | perf_diff__compute_delta(he, pair); | 442 | compute_delta(he, pair); |
346 | break; | 443 | break; |
347 | case COMPUTE_RATIO: | 444 | case COMPUTE_RATIO: |
348 | perf_diff__compute_ratio(he, pair); | 445 | compute_ratio(he, pair); |
349 | break; | 446 | break; |
350 | case COMPUTE_WEIGHTED_DIFF: | 447 | case COMPUTE_WEIGHTED_DIFF: |
351 | perf_diff__compute_wdiff(he, pair); | 448 | compute_wdiff(he, pair); |
352 | break; | 449 | break; |
353 | default: | 450 | default: |
354 | BUG_ON(1); | 451 | BUG_ON(1); |
@@ -367,7 +464,7 @@ static int64_t cmp_doubles(double l, double r) | |||
367 | } | 464 | } |
368 | 465 | ||
369 | static int64_t | 466 | static int64_t |
370 | hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | 467 | __hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, |
371 | int c) | 468 | int c) |
372 | { | 469 | { |
373 | switch (c) { | 470 | switch (c) { |
@@ -399,6 +496,36 @@ hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | |||
399 | return 0; | 496 | return 0; |
400 | } | 497 | } |
401 | 498 | ||
499 | static int64_t | ||
500 | hist_entry__cmp_compute(struct hist_entry *left, struct hist_entry *right, | ||
501 | int c) | ||
502 | { | ||
503 | bool pairs_left = hist_entry__has_pairs(left); | ||
504 | bool pairs_right = hist_entry__has_pairs(right); | ||
505 | struct hist_entry *p_right, *p_left; | ||
506 | |||
507 | if (!pairs_left && !pairs_right) | ||
508 | return 0; | ||
509 | |||
510 | if (!pairs_left || !pairs_right) | ||
511 | return pairs_left ? -1 : 1; | ||
512 | |||
513 | p_left = get_pair_data(left, &data__files[sort_compute]); | ||
514 | p_right = get_pair_data(right, &data__files[sort_compute]); | ||
515 | |||
516 | if (!p_left && !p_right) | ||
517 | return 0; | ||
518 | |||
519 | if (!p_left || !p_right) | ||
520 | return p_left ? -1 : 1; | ||
521 | |||
522 | /* | ||
523 | * We have 2 entries of same kind, let's | ||
524 | * make the data comparison. | ||
525 | */ | ||
526 | return __hist_entry__cmp_compute(p_left, p_right, c); | ||
527 | } | ||
528 | |||
402 | static void insert_hist_entry_by_compute(struct rb_root *root, | 529 | static void insert_hist_entry_by_compute(struct rb_root *root, |
403 | struct hist_entry *he, | 530 | struct hist_entry *he, |
404 | int c) | 531 | int c) |
@@ -448,75 +575,121 @@ static void hists__compute_resort(struct hists *hists) | |||
448 | } | 575 | } |
449 | } | 576 | } |
450 | 577 | ||
451 | static void hists__process(struct hists *old, struct hists *new) | 578 | static void hists__process(struct hists *hists) |
452 | { | 579 | { |
453 | hists__match(new, old); | ||
454 | |||
455 | if (show_baseline_only) | 580 | if (show_baseline_only) |
456 | hists__baseline_only(new); | 581 | hists__baseline_only(hists); |
457 | else | ||
458 | hists__link(new, old); | ||
459 | 582 | ||
460 | if (sort_compute) { | 583 | if (sort_compute) { |
461 | hists__precompute(new); | 584 | hists__precompute(hists); |
462 | hists__compute_resort(new); | 585 | hists__compute_resort(hists); |
463 | } else { | 586 | } else { |
464 | hists__output_resort(new); | 587 | hists__output_resort(hists); |
465 | } | 588 | } |
466 | 589 | ||
467 | hists__fprintf(new, true, 0, 0, 0, stdout); | 590 | hists__fprintf(hists, true, 0, 0, 0, stdout); |
468 | } | 591 | } |
469 | 592 | ||
470 | static int __cmd_diff(void) | 593 | static void data__fprintf(void) |
471 | { | 594 | { |
472 | int ret, i; | 595 | struct data__file *d; |
473 | #define older (session[0]) | 596 | int i; |
474 | #define newer (session[1]) | 597 | |
475 | struct perf_session *session[2]; | 598 | fprintf(stdout, "# Data files:\n"); |
476 | struct perf_evlist *evlist_new, *evlist_old; | 599 | |
477 | struct perf_evsel *evsel; | 600 | data__for_each_file(i, d) |
601 | fprintf(stdout, "# [%d] %s %s\n", | ||
602 | d->idx, d->file, | ||
603 | !d->idx ? "(Baseline)" : ""); | ||
604 | |||
605 | fprintf(stdout, "#\n"); | ||
606 | } | ||
607 | |||
608 | static void data_process(void) | ||
609 | { | ||
610 | struct perf_evlist *evlist_base = data__files[0].session->evlist; | ||
611 | struct perf_evsel *evsel_base; | ||
478 | bool first = true; | 612 | bool first = true; |
479 | 613 | ||
480 | older = perf_session__new(input_old, O_RDONLY, force, false, | 614 | list_for_each_entry(evsel_base, &evlist_base->entries, node) { |
481 | &tool); | 615 | struct data__file *d; |
482 | newer = perf_session__new(input_new, O_RDONLY, force, false, | 616 | int i; |
483 | &tool); | ||
484 | if (session[0] == NULL || session[1] == NULL) | ||
485 | return -ENOMEM; | ||
486 | 617 | ||
487 | for (i = 0; i < 2; ++i) { | 618 | data__for_each_file_new(i, d) { |
488 | ret = perf_session__process_events(session[i], &tool); | 619 | struct perf_evlist *evlist = d->session->evlist; |
489 | if (ret) | 620 | struct perf_evsel *evsel; |
490 | goto out_delete; | ||
491 | } | ||
492 | 621 | ||
493 | evlist_old = older->evlist; | 622 | evsel = evsel_match(evsel_base, evlist); |
494 | evlist_new = newer->evlist; | 623 | if (!evsel) |
624 | continue; | ||
495 | 625 | ||
496 | perf_evlist__collapse_resort(evlist_old); | 626 | d->hists = &evsel->hists; |
497 | perf_evlist__collapse_resort(evlist_new); | ||
498 | 627 | ||
499 | list_for_each_entry(evsel, &evlist_new->entries, node) { | 628 | hists__match(&evsel_base->hists, &evsel->hists); |
500 | struct perf_evsel *evsel_old; | ||
501 | 629 | ||
502 | evsel_old = evsel_match(evsel, evlist_old); | 630 | if (!show_baseline_only) |
503 | if (!evsel_old) | 631 | hists__link(&evsel_base->hists, |
504 | continue; | 632 | &evsel->hists); |
633 | } | ||
505 | 634 | ||
506 | fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", | 635 | fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n", |
507 | perf_evsel__name(evsel)); | 636 | perf_evsel__name(evsel_base)); |
508 | 637 | ||
509 | first = false; | 638 | first = false; |
510 | 639 | ||
511 | hists__process(&evsel_old->hists, &evsel->hists); | 640 | if (verbose || data__files_cnt > 2) |
641 | data__fprintf(); | ||
642 | |||
643 | hists__process(&evsel_base->hists); | ||
644 | } | ||
645 | } | ||
646 | |||
647 | static void data__free(struct data__file *d) | ||
648 | { | ||
649 | int col; | ||
650 | |||
651 | for (col = 0; col < PERF_HPP_DIFF__MAX_INDEX; col++) { | ||
652 | struct diff_hpp_fmt *fmt = &d->fmt[col]; | ||
653 | |||
654 | free(fmt->header); | ||
512 | } | 655 | } |
656 | } | ||
513 | 657 | ||
514 | out_delete: | 658 | static int __cmd_diff(void) |
515 | for (i = 0; i < 2; ++i) | 659 | { |
516 | perf_session__delete(session[i]); | 660 | struct data__file *d; |
661 | int ret = -EINVAL, i; | ||
662 | |||
663 | data__for_each_file(i, d) { | ||
664 | d->session = perf_session__new(d->file, O_RDONLY, force, | ||
665 | false, &tool); | ||
666 | if (!d->session) { | ||
667 | pr_err("Failed to open %s\n", d->file); | ||
668 | ret = -ENOMEM; | ||
669 | goto out_delete; | ||
670 | } | ||
671 | |||
672 | ret = perf_session__process_events(d->session, &tool); | ||
673 | if (ret) { | ||
674 | pr_err("Failed to process %s\n", d->file); | ||
675 | goto out_delete; | ||
676 | } | ||
677 | |||
678 | perf_evlist__collapse_resort(d->session->evlist); | ||
679 | } | ||
680 | |||
681 | data_process(); | ||
682 | |||
683 | out_delete: | ||
684 | data__for_each_file(i, d) { | ||
685 | if (d->session) | ||
686 | perf_session__delete(d->session); | ||
687 | |||
688 | data__free(d); | ||
689 | } | ||
690 | |||
691 | free(data__files); | ||
517 | return ret; | 692 | return ret; |
518 | #undef older | ||
519 | #undef newer | ||
520 | } | 693 | } |
521 | 694 | ||
522 | static const char * const diff_usage[] = { | 695 | static const char * const diff_usage[] = { |
@@ -555,61 +728,310 @@ static const struct option options[] = { | |||
555 | "columns '.' is reserved."), | 728 | "columns '.' is reserved."), |
556 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 729 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
557 | "Look for files with symbols relative to this directory"), | 730 | "Look for files with symbols relative to this directory"), |
731 | OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), | ||
558 | OPT_END() | 732 | OPT_END() |
559 | }; | 733 | }; |
560 | 734 | ||
561 | static void ui_init(void) | 735 | static double baseline_percent(struct hist_entry *he) |
562 | { | 736 | { |
563 | /* | 737 | struct hists *hists = he->hists; |
564 | * Display baseline/delta/ratio | 738 | return 100.0 * he->stat.period / hists->stats.total_period; |
565 | * formula/periods columns. | 739 | } |
566 | */ | ||
567 | perf_hpp__column_enable(PERF_HPP__BASELINE); | ||
568 | 740 | ||
569 | switch (compute) { | 741 | static int hpp__color_baseline(struct perf_hpp_fmt *fmt, |
570 | case COMPUTE_DELTA: | 742 | struct perf_hpp *hpp, struct hist_entry *he) |
571 | perf_hpp__column_enable(PERF_HPP__DELTA); | 743 | { |
744 | struct diff_hpp_fmt *dfmt = | ||
745 | container_of(fmt, struct diff_hpp_fmt, fmt); | ||
746 | double percent = baseline_percent(he); | ||
747 | char pfmt[20] = " "; | ||
748 | |||
749 | if (!he->dummy) { | ||
750 | scnprintf(pfmt, 20, "%%%d.2f%%%%", dfmt->header_width - 1); | ||
751 | return percent_color_snprintf(hpp->buf, hpp->size, | ||
752 | pfmt, percent); | ||
753 | } else | ||
754 | return scnprintf(hpp->buf, hpp->size, "%*s", | ||
755 | dfmt->header_width, pfmt); | ||
756 | } | ||
757 | |||
758 | static int hpp__entry_baseline(struct hist_entry *he, char *buf, size_t size) | ||
759 | { | ||
760 | double percent = baseline_percent(he); | ||
761 | const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%"; | ||
762 | int ret = 0; | ||
763 | |||
764 | if (!he->dummy) | ||
765 | ret = scnprintf(buf, size, fmt, percent); | ||
766 | |||
767 | return ret; | ||
768 | } | ||
769 | |||
770 | static void | ||
771 | hpp__entry_unpair(struct hist_entry *he, int idx, char *buf, size_t size) | ||
772 | { | ||
773 | switch (idx) { | ||
774 | case PERF_HPP_DIFF__PERIOD_BASELINE: | ||
775 | scnprintf(buf, size, "%" PRIu64, he->stat.period); | ||
572 | break; | 776 | break; |
573 | case COMPUTE_RATIO: | 777 | |
574 | perf_hpp__column_enable(PERF_HPP__RATIO); | 778 | default: |
575 | break; | 779 | break; |
576 | case COMPUTE_WEIGHTED_DIFF: | 780 | } |
577 | perf_hpp__column_enable(PERF_HPP__WEIGHTED_DIFF); | 781 | } |
782 | |||
783 | static void | ||
784 | hpp__entry_pair(struct hist_entry *he, struct hist_entry *pair, | ||
785 | int idx, char *buf, size_t size) | ||
786 | { | ||
787 | double diff; | ||
788 | double ratio; | ||
789 | s64 wdiff; | ||
790 | |||
791 | switch (idx) { | ||
792 | case PERF_HPP_DIFF__DELTA: | ||
793 | if (pair->diff.computed) | ||
794 | diff = pair->diff.period_ratio_delta; | ||
795 | else | ||
796 | diff = compute_delta(he, pair); | ||
797 | |||
798 | if (fabs(diff) >= 0.01) | ||
799 | scnprintf(buf, size, "%+4.2F%%", diff); | ||
800 | break; | ||
801 | |||
802 | case PERF_HPP_DIFF__RATIO: | ||
803 | /* No point for ratio number if we are dummy.. */ | ||
804 | if (he->dummy) | ||
805 | break; | ||
806 | |||
807 | if (pair->diff.computed) | ||
808 | ratio = pair->diff.period_ratio; | ||
809 | else | ||
810 | ratio = compute_ratio(he, pair); | ||
811 | |||
812 | if (ratio > 0.0) | ||
813 | scnprintf(buf, size, "%14.6F", ratio); | ||
814 | break; | ||
815 | |||
816 | case PERF_HPP_DIFF__WEIGHTED_DIFF: | ||
817 | /* No point for wdiff number if we are dummy.. */ | ||
818 | if (he->dummy) | ||
819 | break; | ||
820 | |||
821 | if (pair->diff.computed) | ||
822 | wdiff = pair->diff.wdiff; | ||
823 | else | ||
824 | wdiff = compute_wdiff(he, pair); | ||
825 | |||
826 | if (wdiff != 0) | ||
827 | scnprintf(buf, size, "%14ld", wdiff); | ||
828 | break; | ||
829 | |||
830 | case PERF_HPP_DIFF__FORMULA: | ||
831 | formula_fprintf(he, pair, buf, size); | ||
832 | break; | ||
833 | |||
834 | case PERF_HPP_DIFF__PERIOD: | ||
835 | scnprintf(buf, size, "%" PRIu64, pair->stat.period); | ||
578 | break; | 836 | break; |
837 | |||
579 | default: | 838 | default: |
580 | BUG_ON(1); | 839 | BUG_ON(1); |
581 | }; | 840 | }; |
841 | } | ||
842 | |||
843 | static void | ||
844 | __hpp__entry_global(struct hist_entry *he, struct diff_hpp_fmt *dfmt, | ||
845 | char *buf, size_t size) | ||
846 | { | ||
847 | struct hist_entry *pair = get_pair_fmt(he, dfmt); | ||
848 | int idx = dfmt->idx; | ||
849 | |||
850 | /* baseline is special */ | ||
851 | if (idx == PERF_HPP_DIFF__BASELINE) | ||
852 | hpp__entry_baseline(he, buf, size); | ||
853 | else { | ||
854 | if (pair) | ||
855 | hpp__entry_pair(he, pair, idx, buf, size); | ||
856 | else | ||
857 | hpp__entry_unpair(he, idx, buf, size); | ||
858 | } | ||
859 | } | ||
860 | |||
861 | static int hpp__entry_global(struct perf_hpp_fmt *_fmt, struct perf_hpp *hpp, | ||
862 | struct hist_entry *he) | ||
863 | { | ||
864 | struct diff_hpp_fmt *dfmt = | ||
865 | container_of(_fmt, struct diff_hpp_fmt, fmt); | ||
866 | char buf[MAX_COL_WIDTH] = " "; | ||
867 | |||
868 | __hpp__entry_global(he, dfmt, buf, MAX_COL_WIDTH); | ||
869 | |||
870 | if (symbol_conf.field_sep) | ||
871 | return scnprintf(hpp->buf, hpp->size, "%s", buf); | ||
872 | else | ||
873 | return scnprintf(hpp->buf, hpp->size, "%*s", | ||
874 | dfmt->header_width, buf); | ||
875 | } | ||
876 | |||
877 | static int hpp__header(struct perf_hpp_fmt *fmt, | ||
878 | struct perf_hpp *hpp) | ||
879 | { | ||
880 | struct diff_hpp_fmt *dfmt = | ||
881 | container_of(fmt, struct diff_hpp_fmt, fmt); | ||
582 | 882 | ||
583 | if (show_formula) | 883 | BUG_ON(!dfmt->header); |
584 | perf_hpp__column_enable(PERF_HPP__FORMULA); | 884 | return scnprintf(hpp->buf, hpp->size, dfmt->header); |
885 | } | ||
585 | 886 | ||
586 | if (show_period) { | 887 | static int hpp__width(struct perf_hpp_fmt *fmt, |
587 | perf_hpp__column_enable(PERF_HPP__PERIOD); | 888 | struct perf_hpp *hpp __maybe_unused) |
588 | perf_hpp__column_enable(PERF_HPP__PERIOD_BASELINE); | 889 | { |
890 | struct diff_hpp_fmt *dfmt = | ||
891 | container_of(fmt, struct diff_hpp_fmt, fmt); | ||
892 | |||
893 | BUG_ON(dfmt->header_width <= 0); | ||
894 | return dfmt->header_width; | ||
895 | } | ||
896 | |||
897 | static void init_header(struct data__file *d, struct diff_hpp_fmt *dfmt) | ||
898 | { | ||
899 | #define MAX_HEADER_NAME 100 | ||
900 | char buf_indent[MAX_HEADER_NAME]; | ||
901 | char buf[MAX_HEADER_NAME]; | ||
902 | const char *header = NULL; | ||
903 | int width = 0; | ||
904 | |||
905 | BUG_ON(dfmt->idx >= PERF_HPP_DIFF__MAX_INDEX); | ||
906 | header = columns[dfmt->idx].name; | ||
907 | width = columns[dfmt->idx].width; | ||
908 | |||
909 | /* Only our defined HPP fmts should appear here. */ | ||
910 | BUG_ON(!header); | ||
911 | |||
912 | if (data__files_cnt > 2) | ||
913 | scnprintf(buf, MAX_HEADER_NAME, "%s/%d", header, d->idx); | ||
914 | |||
915 | #define NAME (data__files_cnt > 2 ? buf : header) | ||
916 | dfmt->header_width = width; | ||
917 | width = (int) strlen(NAME); | ||
918 | if (dfmt->header_width < width) | ||
919 | dfmt->header_width = width; | ||
920 | |||
921 | scnprintf(buf_indent, MAX_HEADER_NAME, "%*s", | ||
922 | dfmt->header_width, NAME); | ||
923 | |||
924 | dfmt->header = strdup(buf_indent); | ||
925 | #undef MAX_HEADER_NAME | ||
926 | #undef NAME | ||
927 | } | ||
928 | |||
929 | static void data__hpp_register(struct data__file *d, int idx) | ||
930 | { | ||
931 | struct diff_hpp_fmt *dfmt = &d->fmt[idx]; | ||
932 | struct perf_hpp_fmt *fmt = &dfmt->fmt; | ||
933 | |||
934 | dfmt->idx = idx; | ||
935 | |||
936 | fmt->header = hpp__header; | ||
937 | fmt->width = hpp__width; | ||
938 | fmt->entry = hpp__entry_global; | ||
939 | |||
940 | /* TODO more colors */ | ||
941 | if (idx == PERF_HPP_DIFF__BASELINE) | ||
942 | fmt->color = hpp__color_baseline; | ||
943 | |||
944 | init_header(d, dfmt); | ||
945 | perf_hpp__column_register(fmt); | ||
946 | } | ||
947 | |||
948 | static void ui_init(void) | ||
949 | { | ||
950 | struct data__file *d; | ||
951 | int i; | ||
952 | |||
953 | data__for_each_file(i, d) { | ||
954 | |||
955 | /* | ||
956 | * Baseline or compute realted columns: | ||
957 | * | ||
958 | * PERF_HPP_DIFF__BASELINE | ||
959 | * PERF_HPP_DIFF__DELTA | ||
960 | * PERF_HPP_DIFF__RATIO | ||
961 | * PERF_HPP_DIFF__WEIGHTED_DIFF | ||
962 | */ | ||
963 | data__hpp_register(d, i ? compute_2_hpp[compute] : | ||
964 | PERF_HPP_DIFF__BASELINE); | ||
965 | |||
966 | /* | ||
967 | * And the rest: | ||
968 | * | ||
969 | * PERF_HPP_DIFF__FORMULA | ||
970 | * PERF_HPP_DIFF__PERIOD | ||
971 | * PERF_HPP_DIFF__PERIOD_BASELINE | ||
972 | */ | ||
973 | if (show_formula && i) | ||
974 | data__hpp_register(d, PERF_HPP_DIFF__FORMULA); | ||
975 | |||
976 | if (show_period) | ||
977 | data__hpp_register(d, i ? PERF_HPP_DIFF__PERIOD : | ||
978 | PERF_HPP_DIFF__PERIOD_BASELINE); | ||
589 | } | 979 | } |
590 | } | 980 | } |
591 | 981 | ||
592 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | 982 | static int data_init(int argc, const char **argv) |
593 | { | 983 | { |
594 | sort_order = diff__default_sort_order; | 984 | struct data__file *d; |
595 | argc = parse_options(argc, argv, options, diff_usage, 0); | 985 | static const char *defaults[] = { |
986 | "perf.data.old", | ||
987 | "perf.data", | ||
988 | }; | ||
989 | bool use_default = true; | ||
990 | int i; | ||
991 | |||
992 | data__files_cnt = 2; | ||
993 | |||
596 | if (argc) { | 994 | if (argc) { |
597 | if (argc > 2) | 995 | if (argc == 1) |
598 | usage_with_options(diff_usage, options); | 996 | defaults[1] = argv[0]; |
599 | if (argc == 2) { | 997 | else { |
600 | input_old = argv[0]; | 998 | data__files_cnt = argc; |
601 | input_new = argv[1]; | 999 | use_default = false; |
602 | } else | 1000 | } |
603 | input_new = argv[0]; | ||
604 | } else if (symbol_conf.default_guest_vmlinux_name || | 1001 | } else if (symbol_conf.default_guest_vmlinux_name || |
605 | symbol_conf.default_guest_kallsyms) { | 1002 | symbol_conf.default_guest_kallsyms) { |
606 | input_old = "perf.data.host"; | 1003 | defaults[0] = "perf.data.host"; |
607 | input_new = "perf.data.guest"; | 1004 | defaults[1] = "perf.data.guest"; |
608 | } | 1005 | } |
609 | 1006 | ||
1007 | if (sort_compute >= (unsigned int) data__files_cnt) { | ||
1008 | pr_err("Order option out of limit.\n"); | ||
1009 | return -EINVAL; | ||
1010 | } | ||
1011 | |||
1012 | data__files = zalloc(sizeof(*data__files) * data__files_cnt); | ||
1013 | if (!data__files) | ||
1014 | return -ENOMEM; | ||
1015 | |||
1016 | data__for_each_file(i, d) { | ||
1017 | d->file = use_default ? defaults[i] : argv[i]; | ||
1018 | d->idx = i; | ||
1019 | } | ||
1020 | |||
1021 | return 0; | ||
1022 | } | ||
1023 | |||
1024 | int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused) | ||
1025 | { | ||
1026 | sort_order = diff__default_sort_order; | ||
1027 | argc = parse_options(argc, argv, options, diff_usage, 0); | ||
1028 | |||
610 | if (symbol__init() < 0) | 1029 | if (symbol__init() < 0) |
611 | return -1; | 1030 | return -1; |
612 | 1031 | ||
1032 | if (data_init(argc, argv) < 0) | ||
1033 | return -1; | ||
1034 | |||
613 | ui_init(); | 1035 | ui_init(); |
614 | 1036 | ||
615 | if (setup_sorting() < 0) | 1037 | if (setup_sorting() < 0) |
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 84ad6abe4258..afe377b2884f 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -38,8 +38,7 @@ struct event_entry { | |||
38 | }; | 38 | }; |
39 | 39 | ||
40 | static int perf_event__repipe_synth(struct perf_tool *tool, | 40 | static int perf_event__repipe_synth(struct perf_tool *tool, |
41 | union perf_event *event, | 41 | union perf_event *event) |
42 | struct machine *machine __maybe_unused) | ||
43 | { | 42 | { |
44 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); | 43 | struct perf_inject *inject = container_of(tool, struct perf_inject, tool); |
45 | uint32_t size; | 44 | uint32_t size; |
@@ -65,39 +64,28 @@ static int perf_event__repipe_op2_synth(struct perf_tool *tool, | |||
65 | struct perf_session *session | 64 | struct perf_session *session |
66 | __maybe_unused) | 65 | __maybe_unused) |
67 | { | 66 | { |
68 | return perf_event__repipe_synth(tool, event, NULL); | 67 | return perf_event__repipe_synth(tool, event); |
69 | } | 68 | } |
70 | 69 | ||
71 | static int perf_event__repipe_event_type_synth(struct perf_tool *tool, | 70 | static int perf_event__repipe_attr(struct perf_tool *tool, |
72 | union perf_event *event) | 71 | union perf_event *event, |
73 | { | 72 | struct perf_evlist **pevlist) |
74 | return perf_event__repipe_synth(tool, event, NULL); | ||
75 | } | ||
76 | |||
77 | static int perf_event__repipe_tracing_data_synth(union perf_event *event, | ||
78 | struct perf_session *session | ||
79 | __maybe_unused) | ||
80 | { | ||
81 | return perf_event__repipe_synth(NULL, event, NULL); | ||
82 | } | ||
83 | |||
84 | static int perf_event__repipe_attr(union perf_event *event, | ||
85 | struct perf_evlist **pevlist __maybe_unused) | ||
86 | { | 73 | { |
87 | int ret; | 74 | int ret; |
88 | ret = perf_event__process_attr(event, pevlist); | 75 | |
76 | ret = perf_event__process_attr(tool, event, pevlist); | ||
89 | if (ret) | 77 | if (ret) |
90 | return ret; | 78 | return ret; |
91 | 79 | ||
92 | return perf_event__repipe_synth(NULL, event, NULL); | 80 | return perf_event__repipe_synth(tool, event); |
93 | } | 81 | } |
94 | 82 | ||
95 | static int perf_event__repipe(struct perf_tool *tool, | 83 | static int perf_event__repipe(struct perf_tool *tool, |
96 | union perf_event *event, | 84 | union perf_event *event, |
97 | struct perf_sample *sample __maybe_unused, | 85 | struct perf_sample *sample __maybe_unused, |
98 | struct machine *machine) | 86 | struct machine *machine __maybe_unused) |
99 | { | 87 | { |
100 | return perf_event__repipe_synth(tool, event, machine); | 88 | return perf_event__repipe_synth(tool, event); |
101 | } | 89 | } |
102 | 90 | ||
103 | typedef int (*inject_handler)(struct perf_tool *tool, | 91 | typedef int (*inject_handler)(struct perf_tool *tool, |
@@ -119,7 +107,7 @@ static int perf_event__repipe_sample(struct perf_tool *tool, | |||
119 | 107 | ||
120 | build_id__mark_dso_hit(tool, event, sample, evsel, machine); | 108 | build_id__mark_dso_hit(tool, event, sample, evsel, machine); |
121 | 109 | ||
122 | return perf_event__repipe_synth(tool, event, machine); | 110 | return perf_event__repipe_synth(tool, event); |
123 | } | 111 | } |
124 | 112 | ||
125 | static int perf_event__repipe_mmap(struct perf_tool *tool, | 113 | static int perf_event__repipe_mmap(struct perf_tool *tool, |
@@ -135,6 +123,19 @@ static int perf_event__repipe_mmap(struct perf_tool *tool, | |||
135 | return err; | 123 | return err; |
136 | } | 124 | } |
137 | 125 | ||
126 | static int perf_event__repipe_mmap2(struct perf_tool *tool, | ||
127 | union perf_event *event, | ||
128 | struct perf_sample *sample, | ||
129 | struct machine *machine) | ||
130 | { | ||
131 | int err; | ||
132 | |||
133 | err = perf_event__process_mmap2(tool, event, sample, machine); | ||
134 | perf_event__repipe(tool, event, sample, machine); | ||
135 | |||
136 | return err; | ||
137 | } | ||
138 | |||
138 | static int perf_event__repipe_fork(struct perf_tool *tool, | 139 | static int perf_event__repipe_fork(struct perf_tool *tool, |
139 | union perf_event *event, | 140 | union perf_event *event, |
140 | struct perf_sample *sample, | 141 | struct perf_sample *sample, |
@@ -148,13 +149,14 @@ static int perf_event__repipe_fork(struct perf_tool *tool, | |||
148 | return err; | 149 | return err; |
149 | } | 150 | } |
150 | 151 | ||
151 | static int perf_event__repipe_tracing_data(union perf_event *event, | 152 | static int perf_event__repipe_tracing_data(struct perf_tool *tool, |
153 | union perf_event *event, | ||
152 | struct perf_session *session) | 154 | struct perf_session *session) |
153 | { | 155 | { |
154 | int err; | 156 | int err; |
155 | 157 | ||
156 | perf_event__repipe_synth(NULL, event, NULL); | 158 | perf_event__repipe_synth(tool, event); |
157 | err = perf_event__process_tracing_data(event, session); | 159 | err = perf_event__process_tracing_data(tool, event, session); |
158 | 160 | ||
159 | return err; | 161 | return err; |
160 | } | 162 | } |
@@ -209,7 +211,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, | |||
209 | 211 | ||
210 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 212 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
211 | 213 | ||
212 | thread = machine__findnew_thread(machine, event->ip.pid); | 214 | thread = machine__findnew_thread(machine, sample->pid, sample->pid); |
213 | if (thread == NULL) { | 215 | if (thread == NULL) { |
214 | pr_err("problem processing %d event, skipping it.\n", | 216 | pr_err("problem processing %d event, skipping it.\n", |
215 | event->header.type); | 217 | event->header.type); |
@@ -217,7 +219,7 @@ static int perf_event__inject_buildid(struct perf_tool *tool, | |||
217 | } | 219 | } |
218 | 220 | ||
219 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 221 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, |
220 | event->ip.ip, &al); | 222 | sample->ip, &al); |
221 | 223 | ||
222 | if (al.map != NULL) { | 224 | if (al.map != NULL) { |
223 | if (!al.map->dso->hit) { | 225 | if (!al.map->dso->hit) { |
@@ -312,13 +314,13 @@ found: | |||
312 | sample_sw.period = sample->period; | 314 | sample_sw.period = sample->period; |
313 | sample_sw.time = sample->time; | 315 | sample_sw.time = sample->time; |
314 | perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, | 316 | perf_event__synthesize_sample(event_sw, evsel->attr.sample_type, |
315 | &sample_sw, false); | 317 | evsel->attr.sample_regs_user, |
318 | evsel->attr.read_format, &sample_sw, | ||
319 | false); | ||
316 | build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); | 320 | build_id__mark_dso_hit(tool, event_sw, &sample_sw, evsel, machine); |
317 | return perf_event__repipe(tool, event_sw, &sample_sw, machine); | 321 | return perf_event__repipe(tool, event_sw, &sample_sw, machine); |
318 | } | 322 | } |
319 | 323 | ||
320 | extern volatile int session_done; | ||
321 | |||
322 | static void sig_handler(int sig __maybe_unused) | 324 | static void sig_handler(int sig __maybe_unused) |
323 | { | 325 | { |
324 | session_done = 1; | 326 | session_done = 1; |
@@ -348,6 +350,7 @@ static int __cmd_inject(struct perf_inject *inject) | |||
348 | 350 | ||
349 | if (inject->build_ids || inject->sched_stat) { | 351 | if (inject->build_ids || inject->sched_stat) { |
350 | inject->tool.mmap = perf_event__repipe_mmap; | 352 | inject->tool.mmap = perf_event__repipe_mmap; |
353 | inject->tool.mmap2 = perf_event__repipe_mmap2; | ||
351 | inject->tool.fork = perf_event__repipe_fork; | 354 | inject->tool.fork = perf_event__repipe_fork; |
352 | inject->tool.tracing_data = perf_event__repipe_tracing_data; | 355 | inject->tool.tracing_data = perf_event__repipe_tracing_data; |
353 | } | 356 | } |
@@ -399,6 +402,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
399 | .tool = { | 402 | .tool = { |
400 | .sample = perf_event__repipe_sample, | 403 | .sample = perf_event__repipe_sample, |
401 | .mmap = perf_event__repipe, | 404 | .mmap = perf_event__repipe, |
405 | .mmap2 = perf_event__repipe, | ||
402 | .comm = perf_event__repipe, | 406 | .comm = perf_event__repipe, |
403 | .fork = perf_event__repipe, | 407 | .fork = perf_event__repipe, |
404 | .exit = perf_event__repipe, | 408 | .exit = perf_event__repipe, |
@@ -407,8 +411,8 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
407 | .throttle = perf_event__repipe, | 411 | .throttle = perf_event__repipe, |
408 | .unthrottle = perf_event__repipe, | 412 | .unthrottle = perf_event__repipe, |
409 | .attr = perf_event__repipe_attr, | 413 | .attr = perf_event__repipe_attr, |
410 | .event_type = perf_event__repipe_event_type_synth, | 414 | .tracing_data = perf_event__repipe_op2_synth, |
411 | .tracing_data = perf_event__repipe_tracing_data_synth, | 415 | .finished_round = perf_event__repipe_op2_synth, |
412 | .build_id = perf_event__repipe_op2_synth, | 416 | .build_id = perf_event__repipe_op2_synth, |
413 | }, | 417 | }, |
414 | .input_name = "-", | 418 | .input_name = "-", |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 0259502638b4..9b5f077fee5b 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -101,7 +101,7 @@ static int setup_cpunode_map(void) | |||
101 | 101 | ||
102 | dir1 = opendir(PATH_SYS_NODE); | 102 | dir1 = opendir(PATH_SYS_NODE); |
103 | if (!dir1) | 103 | if (!dir1) |
104 | return -1; | 104 | return 0; |
105 | 105 | ||
106 | while ((dent1 = readdir(dir1)) != NULL) { | 106 | while ((dent1 = readdir(dir1)) != NULL) { |
107 | if (dent1->d_type != DT_DIR || | 107 | if (dent1->d_type != DT_DIR || |
@@ -305,7 +305,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
305 | struct perf_evsel *evsel, | 305 | struct perf_evsel *evsel, |
306 | struct machine *machine) | 306 | struct machine *machine) |
307 | { | 307 | { |
308 | struct thread *thread = machine__findnew_thread(machine, event->ip.pid); | 308 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
309 | sample->pid); | ||
309 | 310 | ||
310 | if (thread == NULL) { | 311 | if (thread == NULL) { |
311 | pr_debug("problem processing %d event, skipping it.\n", | 312 | pr_debug("problem processing %d event, skipping it.\n", |
@@ -313,7 +314,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
313 | return -1; | 314 | return -1; |
314 | } | 315 | } |
315 | 316 | ||
316 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 317 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid); |
317 | 318 | ||
318 | if (evsel->handler.func != NULL) { | 319 | if (evsel->handler.func != NULL) { |
319 | tracepoint_handler f = evsel->handler.func; | 320 | tracepoint_handler f = evsel->handler.func; |
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 24b78aecc928..fbc2888d6495 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -2,22 +2,26 @@ | |||
2 | #include "perf.h" | 2 | #include "perf.h" |
3 | 3 | ||
4 | #include "util/evsel.h" | 4 | #include "util/evsel.h" |
5 | #include "util/evlist.h" | ||
5 | #include "util/util.h" | 6 | #include "util/util.h" |
6 | #include "util/cache.h" | 7 | #include "util/cache.h" |
7 | #include "util/symbol.h" | 8 | #include "util/symbol.h" |
8 | #include "util/thread.h" | 9 | #include "util/thread.h" |
9 | #include "util/header.h" | 10 | #include "util/header.h" |
10 | #include "util/session.h" | 11 | #include "util/session.h" |
11 | 12 | #include "util/intlist.h" | |
12 | #include "util/parse-options.h" | 13 | #include "util/parse-options.h" |
13 | #include "util/trace-event.h" | 14 | #include "util/trace-event.h" |
14 | #include "util/debug.h" | 15 | #include "util/debug.h" |
15 | #include <lk/debugfs.h> | 16 | #include <lk/debugfs.h> |
16 | #include "util/tool.h" | 17 | #include "util/tool.h" |
17 | #include "util/stat.h" | 18 | #include "util/stat.h" |
19 | #include "util/top.h" | ||
18 | 20 | ||
19 | #include <sys/prctl.h> | 21 | #include <sys/prctl.h> |
22 | #include <sys/timerfd.h> | ||
20 | 23 | ||
24 | #include <termios.h> | ||
21 | #include <semaphore.h> | 25 | #include <semaphore.h> |
22 | #include <pthread.h> | 26 | #include <pthread.h> |
23 | #include <math.h> | 27 | #include <math.h> |
@@ -82,6 +86,8 @@ struct exit_reasons_table { | |||
82 | 86 | ||
83 | struct perf_kvm_stat { | 87 | struct perf_kvm_stat { |
84 | struct perf_tool tool; | 88 | struct perf_tool tool; |
89 | struct perf_record_opts opts; | ||
90 | struct perf_evlist *evlist; | ||
85 | struct perf_session *session; | 91 | struct perf_session *session; |
86 | 92 | ||
87 | const char *file_name; | 93 | const char *file_name; |
@@ -96,10 +102,20 @@ struct perf_kvm_stat { | |||
96 | struct kvm_events_ops *events_ops; | 102 | struct kvm_events_ops *events_ops; |
97 | key_cmp_fun compare; | 103 | key_cmp_fun compare; |
98 | struct list_head kvm_events_cache[EVENTS_CACHE_SIZE]; | 104 | struct list_head kvm_events_cache[EVENTS_CACHE_SIZE]; |
105 | |||
99 | u64 total_time; | 106 | u64 total_time; |
100 | u64 total_count; | 107 | u64 total_count; |
108 | u64 lost_events; | ||
109 | u64 duration; | ||
110 | |||
111 | const char *pid_str; | ||
112 | struct intlist *pid_list; | ||
101 | 113 | ||
102 | struct rb_root result; | 114 | struct rb_root result; |
115 | |||
116 | int timerfd; | ||
117 | unsigned int display_time; | ||
118 | bool live; | ||
103 | }; | 119 | }; |
104 | 120 | ||
105 | 121 | ||
@@ -320,6 +336,28 @@ static void init_kvm_event_record(struct perf_kvm_stat *kvm) | |||
320 | INIT_LIST_HEAD(&kvm->kvm_events_cache[i]); | 336 | INIT_LIST_HEAD(&kvm->kvm_events_cache[i]); |
321 | } | 337 | } |
322 | 338 | ||
339 | static void clear_events_cache_stats(struct list_head *kvm_events_cache) | ||
340 | { | ||
341 | struct list_head *head; | ||
342 | struct kvm_event *event; | ||
343 | unsigned int i; | ||
344 | int j; | ||
345 | |||
346 | for (i = 0; i < EVENTS_CACHE_SIZE; i++) { | ||
347 | head = &kvm_events_cache[i]; | ||
348 | list_for_each_entry(event, head, hash_entry) { | ||
349 | /* reset stats for event */ | ||
350 | event->total.time = 0; | ||
351 | init_stats(&event->total.stats); | ||
352 | |||
353 | for (j = 0; j < event->max_vcpu; ++j) { | ||
354 | event->vcpu[j].time = 0; | ||
355 | init_stats(&event->vcpu[j].stats); | ||
356 | } | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | |||
323 | static int kvm_events_hash_fn(u64 key) | 361 | static int kvm_events_hash_fn(u64 key) |
324 | { | 362 | { |
325 | return key & (EVENTS_CACHE_SIZE - 1); | 363 | return key & (EVENTS_CACHE_SIZE - 1); |
@@ -436,7 +474,7 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id, | |||
436 | static bool handle_end_event(struct perf_kvm_stat *kvm, | 474 | static bool handle_end_event(struct perf_kvm_stat *kvm, |
437 | struct vcpu_event_record *vcpu_record, | 475 | struct vcpu_event_record *vcpu_record, |
438 | struct event_key *key, | 476 | struct event_key *key, |
439 | u64 timestamp) | 477 | struct perf_sample *sample) |
440 | { | 478 | { |
441 | struct kvm_event *event; | 479 | struct kvm_event *event; |
442 | u64 time_begin, time_diff; | 480 | u64 time_begin, time_diff; |
@@ -472,9 +510,25 @@ static bool handle_end_event(struct perf_kvm_stat *kvm, | |||
472 | vcpu_record->last_event = NULL; | 510 | vcpu_record->last_event = NULL; |
473 | vcpu_record->start_time = 0; | 511 | vcpu_record->start_time = 0; |
474 | 512 | ||
475 | BUG_ON(timestamp < time_begin); | 513 | /* seems to happen once in a while during live mode */ |
514 | if (sample->time < time_begin) { | ||
515 | pr_debug("End time before begin time; skipping event.\n"); | ||
516 | return true; | ||
517 | } | ||
518 | |||
519 | time_diff = sample->time - time_begin; | ||
520 | |||
521 | if (kvm->duration && time_diff > kvm->duration) { | ||
522 | char decode[32]; | ||
523 | |||
524 | kvm->events_ops->decode_key(kvm, &event->key, decode); | ||
525 | if (strcmp(decode, "HLT")) { | ||
526 | pr_info("%" PRIu64 " VM %d, vcpu %d: %s event took %" PRIu64 "usec\n", | ||
527 | sample->time, sample->pid, vcpu_record->vcpu_id, | ||
528 | decode, time_diff/1000); | ||
529 | } | ||
530 | } | ||
476 | 531 | ||
477 | time_diff = timestamp - time_begin; | ||
478 | return update_kvm_event(event, vcpu, time_diff); | 532 | return update_kvm_event(event, vcpu, time_diff); |
479 | } | 533 | } |
480 | 534 | ||
@@ -521,7 +575,7 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm, | |||
521 | return handle_begin_event(kvm, vcpu_record, &key, sample->time); | 575 | return handle_begin_event(kvm, vcpu_record, &key, sample->time); |
522 | 576 | ||
523 | if (kvm->events_ops->is_end_event(evsel, sample, &key)) | 577 | if (kvm->events_ops->is_end_event(evsel, sample, &key)) |
524 | return handle_end_event(kvm, vcpu_record, &key, sample->time); | 578 | return handle_end_event(kvm, vcpu_record, &key, sample); |
525 | 579 | ||
526 | return true; | 580 | return true; |
527 | } | 581 | } |
@@ -550,6 +604,8 @@ static int compare_kvm_event_ ## func(struct kvm_event *one, \ | |||
550 | GET_EVENT_KEY(time, time); | 604 | GET_EVENT_KEY(time, time); |
551 | COMPARE_EVENT_KEY(count, stats.n); | 605 | COMPARE_EVENT_KEY(count, stats.n); |
552 | COMPARE_EVENT_KEY(mean, stats.mean); | 606 | COMPARE_EVENT_KEY(mean, stats.mean); |
607 | GET_EVENT_KEY(max, stats.max); | ||
608 | GET_EVENT_KEY(min, stats.min); | ||
553 | 609 | ||
554 | #define DEF_SORT_NAME_KEY(name, compare_key) \ | 610 | #define DEF_SORT_NAME_KEY(name, compare_key) \ |
555 | { #name, compare_kvm_event_ ## compare_key } | 611 | { #name, compare_kvm_event_ ## compare_key } |
@@ -639,43 +695,81 @@ static struct kvm_event *pop_from_result(struct rb_root *result) | |||
639 | return container_of(node, struct kvm_event, rb); | 695 | return container_of(node, struct kvm_event, rb); |
640 | } | 696 | } |
641 | 697 | ||
642 | static void print_vcpu_info(int vcpu) | 698 | static void print_vcpu_info(struct perf_kvm_stat *kvm) |
643 | { | 699 | { |
700 | int vcpu = kvm->trace_vcpu; | ||
701 | |||
644 | pr_info("Analyze events for "); | 702 | pr_info("Analyze events for "); |
645 | 703 | ||
704 | if (kvm->live) { | ||
705 | if (kvm->opts.target.system_wide) | ||
706 | pr_info("all VMs, "); | ||
707 | else if (kvm->opts.target.pid) | ||
708 | pr_info("pid(s) %s, ", kvm->opts.target.pid); | ||
709 | else | ||
710 | pr_info("dazed and confused on what is monitored, "); | ||
711 | } | ||
712 | |||
646 | if (vcpu == -1) | 713 | if (vcpu == -1) |
647 | pr_info("all VCPUs:\n\n"); | 714 | pr_info("all VCPUs:\n\n"); |
648 | else | 715 | else |
649 | pr_info("VCPU %d:\n\n", vcpu); | 716 | pr_info("VCPU %d:\n\n", vcpu); |
650 | } | 717 | } |
651 | 718 | ||
719 | static void show_timeofday(void) | ||
720 | { | ||
721 | char date[64]; | ||
722 | struct timeval tv; | ||
723 | struct tm ltime; | ||
724 | |||
725 | gettimeofday(&tv, NULL); | ||
726 | if (localtime_r(&tv.tv_sec, <ime)) { | ||
727 | strftime(date, sizeof(date), "%H:%M:%S", <ime); | ||
728 | pr_info("%s.%06ld", date, tv.tv_usec); | ||
729 | } else | ||
730 | pr_info("00:00:00.000000"); | ||
731 | |||
732 | return; | ||
733 | } | ||
734 | |||
652 | static void print_result(struct perf_kvm_stat *kvm) | 735 | static void print_result(struct perf_kvm_stat *kvm) |
653 | { | 736 | { |
654 | char decode[20]; | 737 | char decode[20]; |
655 | struct kvm_event *event; | 738 | struct kvm_event *event; |
656 | int vcpu = kvm->trace_vcpu; | 739 | int vcpu = kvm->trace_vcpu; |
657 | 740 | ||
741 | if (kvm->live) { | ||
742 | puts(CONSOLE_CLEAR); | ||
743 | show_timeofday(); | ||
744 | } | ||
745 | |||
658 | pr_info("\n\n"); | 746 | pr_info("\n\n"); |
659 | print_vcpu_info(vcpu); | 747 | print_vcpu_info(kvm); |
660 | pr_info("%20s ", kvm->events_ops->name); | 748 | pr_info("%20s ", kvm->events_ops->name); |
661 | pr_info("%10s ", "Samples"); | 749 | pr_info("%10s ", "Samples"); |
662 | pr_info("%9s ", "Samples%"); | 750 | pr_info("%9s ", "Samples%"); |
663 | 751 | ||
664 | pr_info("%9s ", "Time%"); | 752 | pr_info("%9s ", "Time%"); |
753 | pr_info("%10s ", "Min Time"); | ||
754 | pr_info("%10s ", "Max Time"); | ||
665 | pr_info("%16s ", "Avg time"); | 755 | pr_info("%16s ", "Avg time"); |
666 | pr_info("\n\n"); | 756 | pr_info("\n\n"); |
667 | 757 | ||
668 | while ((event = pop_from_result(&kvm->result))) { | 758 | while ((event = pop_from_result(&kvm->result))) { |
669 | u64 ecount, etime; | 759 | u64 ecount, etime, max, min; |
670 | 760 | ||
671 | ecount = get_event_count(event, vcpu); | 761 | ecount = get_event_count(event, vcpu); |
672 | etime = get_event_time(event, vcpu); | 762 | etime = get_event_time(event, vcpu); |
763 | max = get_event_max(event, vcpu); | ||
764 | min = get_event_min(event, vcpu); | ||
673 | 765 | ||
674 | kvm->events_ops->decode_key(kvm, &event->key, decode); | 766 | kvm->events_ops->decode_key(kvm, &event->key, decode); |
675 | pr_info("%20s ", decode); | 767 | pr_info("%20s ", decode); |
676 | pr_info("%10llu ", (unsigned long long)ecount); | 768 | pr_info("%10llu ", (unsigned long long)ecount); |
677 | pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); | 769 | pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); |
678 | pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); | 770 | pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); |
771 | pr_info("%8" PRIu64 "us ", min / 1000); | ||
772 | pr_info("%8" PRIu64 "us ", max / 1000); | ||
679 | pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3, | 773 | pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3, |
680 | kvm_event_rel_stddev(vcpu, event)); | 774 | kvm_event_rel_stddev(vcpu, event)); |
681 | pr_info("\n"); | 775 | pr_info("\n"); |
@@ -683,6 +777,29 @@ static void print_result(struct perf_kvm_stat *kvm) | |||
683 | 777 | ||
684 | pr_info("\nTotal Samples:%" PRIu64 ", Total events handled time:%.2fus.\n\n", | 778 | pr_info("\nTotal Samples:%" PRIu64 ", Total events handled time:%.2fus.\n\n", |
685 | kvm->total_count, kvm->total_time / 1e3); | 779 | kvm->total_count, kvm->total_time / 1e3); |
780 | |||
781 | if (kvm->lost_events) | ||
782 | pr_info("\nLost events: %" PRIu64 "\n\n", kvm->lost_events); | ||
783 | } | ||
784 | |||
785 | static int process_lost_event(struct perf_tool *tool, | ||
786 | union perf_event *event __maybe_unused, | ||
787 | struct perf_sample *sample __maybe_unused, | ||
788 | struct machine *machine __maybe_unused) | ||
789 | { | ||
790 | struct perf_kvm_stat *kvm = container_of(tool, struct perf_kvm_stat, tool); | ||
791 | |||
792 | kvm->lost_events++; | ||
793 | return 0; | ||
794 | } | ||
795 | |||
796 | static bool skip_sample(struct perf_kvm_stat *kvm, | ||
797 | struct perf_sample *sample) | ||
798 | { | ||
799 | if (kvm->pid_list && intlist__find(kvm->pid_list, sample->pid) == NULL) | ||
800 | return true; | ||
801 | |||
802 | return false; | ||
686 | } | 803 | } |
687 | 804 | ||
688 | static int process_sample_event(struct perf_tool *tool, | 805 | static int process_sample_event(struct perf_tool *tool, |
@@ -691,10 +808,14 @@ static int process_sample_event(struct perf_tool *tool, | |||
691 | struct perf_evsel *evsel, | 808 | struct perf_evsel *evsel, |
692 | struct machine *machine) | 809 | struct machine *machine) |
693 | { | 810 | { |
694 | struct thread *thread = machine__findnew_thread(machine, sample->tid); | 811 | struct thread *thread; |
695 | struct perf_kvm_stat *kvm = container_of(tool, struct perf_kvm_stat, | 812 | struct perf_kvm_stat *kvm = container_of(tool, struct perf_kvm_stat, |
696 | tool); | 813 | tool); |
697 | 814 | ||
815 | if (skip_sample(kvm, sample)) | ||
816 | return 0; | ||
817 | |||
818 | thread = machine__findnew_thread(machine, sample->pid, sample->tid); | ||
698 | if (thread == NULL) { | 819 | if (thread == NULL) { |
699 | pr_debug("problem processing %d event, skipping it.\n", | 820 | pr_debug("problem processing %d event, skipping it.\n", |
700 | event->header.type); | 821 | event->header.type); |
@@ -707,10 +828,20 @@ static int process_sample_event(struct perf_tool *tool, | |||
707 | return 0; | 828 | return 0; |
708 | } | 829 | } |
709 | 830 | ||
710 | static int get_cpu_isa(struct perf_session *session) | 831 | static int cpu_isa_config(struct perf_kvm_stat *kvm) |
711 | { | 832 | { |
712 | char *cpuid = session->header.env.cpuid; | 833 | char buf[64], *cpuid; |
713 | int isa; | 834 | int err, isa; |
835 | |||
836 | if (kvm->live) { | ||
837 | err = get_cpuid(buf, sizeof(buf)); | ||
838 | if (err != 0) { | ||
839 | pr_err("Failed to look up CPU type (Intel or AMD)\n"); | ||
840 | return err; | ||
841 | } | ||
842 | cpuid = buf; | ||
843 | } else | ||
844 | cpuid = kvm->session->header.env.cpuid; | ||
714 | 845 | ||
715 | if (strstr(cpuid, "Intel")) | 846 | if (strstr(cpuid, "Intel")) |
716 | isa = 1; | 847 | isa = 1; |
@@ -718,10 +849,368 @@ static int get_cpu_isa(struct perf_session *session) | |||
718 | isa = 0; | 849 | isa = 0; |
719 | else { | 850 | else { |
720 | pr_err("CPU %s is not supported.\n", cpuid); | 851 | pr_err("CPU %s is not supported.\n", cpuid); |
721 | isa = -ENOTSUP; | 852 | return -ENOTSUP; |
722 | } | 853 | } |
723 | 854 | ||
724 | return isa; | 855 | if (isa == 1) { |
856 | kvm->exit_reasons = vmx_exit_reasons; | ||
857 | kvm->exit_reasons_size = ARRAY_SIZE(vmx_exit_reasons); | ||
858 | kvm->exit_reasons_isa = "VMX"; | ||
859 | } | ||
860 | |||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | static bool verify_vcpu(int vcpu) | ||
865 | { | ||
866 | if (vcpu != -1 && vcpu < 0) { | ||
867 | pr_err("Invalid vcpu:%d.\n", vcpu); | ||
868 | return false; | ||
869 | } | ||
870 | |||
871 | return true; | ||
872 | } | ||
873 | |||
874 | /* keeping the max events to a modest level to keep | ||
875 | * the processing of samples per mmap smooth. | ||
876 | */ | ||
877 | #define PERF_KVM__MAX_EVENTS_PER_MMAP 25 | ||
878 | |||
879 | static s64 perf_kvm__mmap_read_idx(struct perf_kvm_stat *kvm, int idx, | ||
880 | u64 *mmap_time) | ||
881 | { | ||
882 | union perf_event *event; | ||
883 | struct perf_sample sample; | ||
884 | s64 n = 0; | ||
885 | int err; | ||
886 | |||
887 | *mmap_time = ULLONG_MAX; | ||
888 | while ((event = perf_evlist__mmap_read(kvm->evlist, idx)) != NULL) { | ||
889 | err = perf_evlist__parse_sample(kvm->evlist, event, &sample); | ||
890 | if (err) { | ||
891 | perf_evlist__mmap_consume(kvm->evlist, idx); | ||
892 | pr_err("Failed to parse sample\n"); | ||
893 | return -1; | ||
894 | } | ||
895 | |||
896 | err = perf_session_queue_event(kvm->session, event, &sample, 0); | ||
897 | /* | ||
898 | * FIXME: Here we can't consume the event, as perf_session_queue_event will | ||
899 | * point to it, and it'll get possibly overwritten by the kernel. | ||
900 | */ | ||
901 | perf_evlist__mmap_consume(kvm->evlist, idx); | ||
902 | |||
903 | if (err) { | ||
904 | pr_err("Failed to enqueue sample: %d\n", err); | ||
905 | return -1; | ||
906 | } | ||
907 | |||
908 | /* save time stamp of our first sample for this mmap */ | ||
909 | if (n == 0) | ||
910 | *mmap_time = sample.time; | ||
911 | |||
912 | /* limit events per mmap handled all at once */ | ||
913 | n++; | ||
914 | if (n == PERF_KVM__MAX_EVENTS_PER_MMAP) | ||
915 | break; | ||
916 | } | ||
917 | |||
918 | return n; | ||
919 | } | ||
920 | |||
921 | static int perf_kvm__mmap_read(struct perf_kvm_stat *kvm) | ||
922 | { | ||
923 | int i, err, throttled = 0; | ||
924 | s64 n, ntotal = 0; | ||
925 | u64 flush_time = ULLONG_MAX, mmap_time; | ||
926 | |||
927 | for (i = 0; i < kvm->evlist->nr_mmaps; i++) { | ||
928 | n = perf_kvm__mmap_read_idx(kvm, i, &mmap_time); | ||
929 | if (n < 0) | ||
930 | return -1; | ||
931 | |||
932 | /* flush time is going to be the minimum of all the individual | ||
933 | * mmap times. Essentially, we flush all the samples queued up | ||
934 | * from the last pass under our minimal start time -- that leaves | ||
935 | * a very small race for samples to come in with a lower timestamp. | ||
936 | * The ioctl to return the perf_clock timestamp should close the | ||
937 | * race entirely. | ||
938 | */ | ||
939 | if (mmap_time < flush_time) | ||
940 | flush_time = mmap_time; | ||
941 | |||
942 | ntotal += n; | ||
943 | if (n == PERF_KVM__MAX_EVENTS_PER_MMAP) | ||
944 | throttled = 1; | ||
945 | } | ||
946 | |||
947 | /* flush queue after each round in which we processed events */ | ||
948 | if (ntotal) { | ||
949 | kvm->session->ordered_samples.next_flush = flush_time; | ||
950 | err = kvm->tool.finished_round(&kvm->tool, NULL, kvm->session); | ||
951 | if (err) { | ||
952 | if (kvm->lost_events) | ||
953 | pr_info("\nLost events: %" PRIu64 "\n\n", | ||
954 | kvm->lost_events); | ||
955 | return err; | ||
956 | } | ||
957 | } | ||
958 | |||
959 | return throttled; | ||
960 | } | ||
961 | |||
962 | static volatile int done; | ||
963 | |||
964 | static void sig_handler(int sig __maybe_unused) | ||
965 | { | ||
966 | done = 1; | ||
967 | } | ||
968 | |||
969 | static int perf_kvm__timerfd_create(struct perf_kvm_stat *kvm) | ||
970 | { | ||
971 | struct itimerspec new_value; | ||
972 | int rc = -1; | ||
973 | |||
974 | kvm->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); | ||
975 | if (kvm->timerfd < 0) { | ||
976 | pr_err("timerfd_create failed\n"); | ||
977 | goto out; | ||
978 | } | ||
979 | |||
980 | new_value.it_value.tv_sec = kvm->display_time; | ||
981 | new_value.it_value.tv_nsec = 0; | ||
982 | new_value.it_interval.tv_sec = kvm->display_time; | ||
983 | new_value.it_interval.tv_nsec = 0; | ||
984 | |||
985 | if (timerfd_settime(kvm->timerfd, 0, &new_value, NULL) != 0) { | ||
986 | pr_err("timerfd_settime failed: %d\n", errno); | ||
987 | close(kvm->timerfd); | ||
988 | goto out; | ||
989 | } | ||
990 | |||
991 | rc = 0; | ||
992 | out: | ||
993 | return rc; | ||
994 | } | ||
995 | |||
996 | static int perf_kvm__handle_timerfd(struct perf_kvm_stat *kvm) | ||
997 | { | ||
998 | uint64_t c; | ||
999 | int rc; | ||
1000 | |||
1001 | rc = read(kvm->timerfd, &c, sizeof(uint64_t)); | ||
1002 | if (rc < 0) { | ||
1003 | if (errno == EAGAIN) | ||
1004 | return 0; | ||
1005 | |||
1006 | pr_err("Failed to read timer fd: %d\n", errno); | ||
1007 | return -1; | ||
1008 | } | ||
1009 | |||
1010 | if (rc != sizeof(uint64_t)) { | ||
1011 | pr_err("Error reading timer fd - invalid size returned\n"); | ||
1012 | return -1; | ||
1013 | } | ||
1014 | |||
1015 | if (c != 1) | ||
1016 | pr_debug("Missed timer beats: %" PRIu64 "\n", c-1); | ||
1017 | |||
1018 | /* update display */ | ||
1019 | sort_result(kvm); | ||
1020 | print_result(kvm); | ||
1021 | |||
1022 | /* reset counts */ | ||
1023 | clear_events_cache_stats(kvm->kvm_events_cache); | ||
1024 | kvm->total_count = 0; | ||
1025 | kvm->total_time = 0; | ||
1026 | kvm->lost_events = 0; | ||
1027 | |||
1028 | return 0; | ||
1029 | } | ||
1030 | |||
1031 | static int fd_set_nonblock(int fd) | ||
1032 | { | ||
1033 | long arg = 0; | ||
1034 | |||
1035 | arg = fcntl(fd, F_GETFL); | ||
1036 | if (arg < 0) { | ||
1037 | pr_err("Failed to get current flags for fd %d\n", fd); | ||
1038 | return -1; | ||
1039 | } | ||
1040 | |||
1041 | if (fcntl(fd, F_SETFL, arg | O_NONBLOCK) < 0) { | ||
1042 | pr_err("Failed to set non-block option on fd %d\n", fd); | ||
1043 | return -1; | ||
1044 | } | ||
1045 | |||
1046 | return 0; | ||
1047 | } | ||
1048 | |||
1049 | static | ||
1050 | int perf_kvm__handle_stdin(struct termios *tc_now, struct termios *tc_save) | ||
1051 | { | ||
1052 | int c; | ||
1053 | |||
1054 | tcsetattr(0, TCSANOW, tc_now); | ||
1055 | c = getc(stdin); | ||
1056 | tcsetattr(0, TCSAFLUSH, tc_save); | ||
1057 | |||
1058 | if (c == 'q') | ||
1059 | return 1; | ||
1060 | |||
1061 | return 0; | ||
1062 | } | ||
1063 | |||
1064 | static int kvm_events_live_report(struct perf_kvm_stat *kvm) | ||
1065 | { | ||
1066 | struct pollfd *pollfds = NULL; | ||
1067 | int nr_fds, nr_stdin, ret, err = -EINVAL; | ||
1068 | struct termios tc, save; | ||
1069 | |||
1070 | /* live flag must be set first */ | ||
1071 | kvm->live = true; | ||
1072 | |||
1073 | ret = cpu_isa_config(kvm); | ||
1074 | if (ret < 0) | ||
1075 | return ret; | ||
1076 | |||
1077 | if (!verify_vcpu(kvm->trace_vcpu) || | ||
1078 | !select_key(kvm) || | ||
1079 | !register_kvm_events_ops(kvm)) { | ||
1080 | goto out; | ||
1081 | } | ||
1082 | |||
1083 | init_kvm_event_record(kvm); | ||
1084 | |||
1085 | tcgetattr(0, &save); | ||
1086 | tc = save; | ||
1087 | tc.c_lflag &= ~(ICANON | ECHO); | ||
1088 | tc.c_cc[VMIN] = 0; | ||
1089 | tc.c_cc[VTIME] = 0; | ||
1090 | |||
1091 | signal(SIGINT, sig_handler); | ||
1092 | signal(SIGTERM, sig_handler); | ||
1093 | |||
1094 | /* copy pollfds -- need to add timerfd and stdin */ | ||
1095 | nr_fds = kvm->evlist->nr_fds; | ||
1096 | pollfds = zalloc(sizeof(struct pollfd) * (nr_fds + 2)); | ||
1097 | if (!pollfds) { | ||
1098 | err = -ENOMEM; | ||
1099 | goto out; | ||
1100 | } | ||
1101 | memcpy(pollfds, kvm->evlist->pollfd, | ||
1102 | sizeof(struct pollfd) * kvm->evlist->nr_fds); | ||
1103 | |||
1104 | /* add timer fd */ | ||
1105 | if (perf_kvm__timerfd_create(kvm) < 0) { | ||
1106 | err = -1; | ||
1107 | goto out; | ||
1108 | } | ||
1109 | |||
1110 | pollfds[nr_fds].fd = kvm->timerfd; | ||
1111 | pollfds[nr_fds].events = POLLIN; | ||
1112 | nr_fds++; | ||
1113 | |||
1114 | pollfds[nr_fds].fd = fileno(stdin); | ||
1115 | pollfds[nr_fds].events = POLLIN; | ||
1116 | nr_stdin = nr_fds; | ||
1117 | nr_fds++; | ||
1118 | if (fd_set_nonblock(fileno(stdin)) != 0) | ||
1119 | goto out; | ||
1120 | |||
1121 | /* everything is good - enable the events and process */ | ||
1122 | perf_evlist__enable(kvm->evlist); | ||
1123 | |||
1124 | while (!done) { | ||
1125 | int rc; | ||
1126 | |||
1127 | rc = perf_kvm__mmap_read(kvm); | ||
1128 | if (rc < 0) | ||
1129 | break; | ||
1130 | |||
1131 | err = perf_kvm__handle_timerfd(kvm); | ||
1132 | if (err) | ||
1133 | goto out; | ||
1134 | |||
1135 | if (pollfds[nr_stdin].revents & POLLIN) | ||
1136 | done = perf_kvm__handle_stdin(&tc, &save); | ||
1137 | |||
1138 | if (!rc && !done) | ||
1139 | err = poll(pollfds, nr_fds, 100); | ||
1140 | } | ||
1141 | |||
1142 | perf_evlist__disable(kvm->evlist); | ||
1143 | |||
1144 | if (err == 0) { | ||
1145 | sort_result(kvm); | ||
1146 | print_result(kvm); | ||
1147 | } | ||
1148 | |||
1149 | out: | ||
1150 | if (kvm->timerfd >= 0) | ||
1151 | close(kvm->timerfd); | ||
1152 | |||
1153 | if (pollfds) | ||
1154 | free(pollfds); | ||
1155 | |||
1156 | return err; | ||
1157 | } | ||
1158 | |||
1159 | static int kvm_live_open_events(struct perf_kvm_stat *kvm) | ||
1160 | { | ||
1161 | int err, rc = -1; | ||
1162 | struct perf_evsel *pos; | ||
1163 | struct perf_evlist *evlist = kvm->evlist; | ||
1164 | |||
1165 | perf_evlist__config(evlist, &kvm->opts); | ||
1166 | |||
1167 | /* | ||
1168 | * Note: exclude_{guest,host} do not apply here. | ||
1169 | * This command processes KVM tracepoints from host only | ||
1170 | */ | ||
1171 | list_for_each_entry(pos, &evlist->entries, node) { | ||
1172 | struct perf_event_attr *attr = &pos->attr; | ||
1173 | |||
1174 | /* make sure these *are* set */ | ||
1175 | perf_evsel__set_sample_bit(pos, TID); | ||
1176 | perf_evsel__set_sample_bit(pos, TIME); | ||
1177 | perf_evsel__set_sample_bit(pos, CPU); | ||
1178 | perf_evsel__set_sample_bit(pos, RAW); | ||
1179 | /* make sure these are *not*; want as small a sample as possible */ | ||
1180 | perf_evsel__reset_sample_bit(pos, PERIOD); | ||
1181 | perf_evsel__reset_sample_bit(pos, IP); | ||
1182 | perf_evsel__reset_sample_bit(pos, CALLCHAIN); | ||
1183 | perf_evsel__reset_sample_bit(pos, ADDR); | ||
1184 | perf_evsel__reset_sample_bit(pos, READ); | ||
1185 | attr->mmap = 0; | ||
1186 | attr->comm = 0; | ||
1187 | attr->task = 0; | ||
1188 | |||
1189 | attr->sample_period = 1; | ||
1190 | |||
1191 | attr->watermark = 0; | ||
1192 | attr->wakeup_events = 1000; | ||
1193 | |||
1194 | /* will enable all once we are ready */ | ||
1195 | attr->disabled = 1; | ||
1196 | } | ||
1197 | |||
1198 | err = perf_evlist__open(evlist); | ||
1199 | if (err < 0) { | ||
1200 | printf("Couldn't create the events: %s\n", strerror(errno)); | ||
1201 | goto out; | ||
1202 | } | ||
1203 | |||
1204 | if (perf_evlist__mmap(evlist, kvm->opts.mmap_pages, false) < 0) { | ||
1205 | ui__error("Failed to mmap the events: %s\n", strerror(errno)); | ||
1206 | perf_evlist__close(evlist); | ||
1207 | goto out; | ||
1208 | } | ||
1209 | |||
1210 | rc = 0; | ||
1211 | |||
1212 | out: | ||
1213 | return rc; | ||
725 | } | 1214 | } |
726 | 1215 | ||
727 | static int read_events(struct perf_kvm_stat *kvm) | 1216 | static int read_events(struct perf_kvm_stat *kvm) |
@@ -749,28 +1238,24 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
749 | * Do not use 'isa' recorded in kvm_exit tracepoint since it is not | 1238 | * Do not use 'isa' recorded in kvm_exit tracepoint since it is not |
750 | * traced in the old kernel. | 1239 | * traced in the old kernel. |
751 | */ | 1240 | */ |
752 | ret = get_cpu_isa(kvm->session); | 1241 | ret = cpu_isa_config(kvm); |
753 | |||
754 | if (ret < 0) | 1242 | if (ret < 0) |
755 | return ret; | 1243 | return ret; |
756 | 1244 | ||
757 | if (ret == 1) { | ||
758 | kvm->exit_reasons = vmx_exit_reasons; | ||
759 | kvm->exit_reasons_size = ARRAY_SIZE(vmx_exit_reasons); | ||
760 | kvm->exit_reasons_isa = "VMX"; | ||
761 | } | ||
762 | |||
763 | return perf_session__process_events(kvm->session, &kvm->tool); | 1245 | return perf_session__process_events(kvm->session, &kvm->tool); |
764 | } | 1246 | } |
765 | 1247 | ||
766 | static bool verify_vcpu(int vcpu) | 1248 | static int parse_target_str(struct perf_kvm_stat *kvm) |
767 | { | 1249 | { |
768 | if (vcpu != -1 && vcpu < 0) { | 1250 | if (kvm->pid_str) { |
769 | pr_err("Invalid vcpu:%d.\n", vcpu); | 1251 | kvm->pid_list = intlist__new(kvm->pid_str); |
770 | return false; | 1252 | if (kvm->pid_list == NULL) { |
1253 | pr_err("Error parsing process id string\n"); | ||
1254 | return -EINVAL; | ||
1255 | } | ||
771 | } | 1256 | } |
772 | 1257 | ||
773 | return true; | 1258 | return 0; |
774 | } | 1259 | } |
775 | 1260 | ||
776 | static int kvm_events_report_vcpu(struct perf_kvm_stat *kvm) | 1261 | static int kvm_events_report_vcpu(struct perf_kvm_stat *kvm) |
@@ -778,6 +1263,9 @@ static int kvm_events_report_vcpu(struct perf_kvm_stat *kvm) | |||
778 | int ret = -EINVAL; | 1263 | int ret = -EINVAL; |
779 | int vcpu = kvm->trace_vcpu; | 1264 | int vcpu = kvm->trace_vcpu; |
780 | 1265 | ||
1266 | if (parse_target_str(kvm) != 0) | ||
1267 | goto exit; | ||
1268 | |||
781 | if (!verify_vcpu(vcpu)) | 1269 | if (!verify_vcpu(vcpu)) |
782 | goto exit; | 1270 | goto exit; |
783 | 1271 | ||
@@ -801,16 +1289,11 @@ exit: | |||
801 | return ret; | 1289 | return ret; |
802 | } | 1290 | } |
803 | 1291 | ||
804 | static const char * const record_args[] = { | 1292 | static const char * const kvm_events_tp[] = { |
805 | "record", | 1293 | "kvm:kvm_entry", |
806 | "-R", | 1294 | "kvm:kvm_exit", |
807 | "-f", | 1295 | "kvm:kvm_mmio", |
808 | "-m", "1024", | 1296 | "kvm:kvm_pio", |
809 | "-c", "1", | ||
810 | "-e", "kvm:kvm_entry", | ||
811 | "-e", "kvm:kvm_exit", | ||
812 | "-e", "kvm:kvm_mmio", | ||
813 | "-e", "kvm:kvm_pio", | ||
814 | }; | 1297 | }; |
815 | 1298 | ||
816 | #define STRDUP_FAIL_EXIT(s) \ | 1299 | #define STRDUP_FAIL_EXIT(s) \ |
@@ -826,8 +1309,15 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
826 | { | 1309 | { |
827 | unsigned int rec_argc, i, j; | 1310 | unsigned int rec_argc, i, j; |
828 | const char **rec_argv; | 1311 | const char **rec_argv; |
1312 | const char * const record_args[] = { | ||
1313 | "record", | ||
1314 | "-R", | ||
1315 | "-m", "1024", | ||
1316 | "-c", "1", | ||
1317 | }; | ||
829 | 1318 | ||
830 | rec_argc = ARRAY_SIZE(record_args) + argc + 2; | 1319 | rec_argc = ARRAY_SIZE(record_args) + argc + 2 + |
1320 | 2 * ARRAY_SIZE(kvm_events_tp); | ||
831 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | 1321 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); |
832 | 1322 | ||
833 | if (rec_argv == NULL) | 1323 | if (rec_argv == NULL) |
@@ -836,6 +1326,11 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
836 | for (i = 0; i < ARRAY_SIZE(record_args); i++) | 1326 | for (i = 0; i < ARRAY_SIZE(record_args); i++) |
837 | rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]); | 1327 | rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]); |
838 | 1328 | ||
1329 | for (j = 0; j < ARRAY_SIZE(kvm_events_tp); j++) { | ||
1330 | rec_argv[i++] = "-e"; | ||
1331 | rec_argv[i++] = STRDUP_FAIL_EXIT(kvm_events_tp[j]); | ||
1332 | } | ||
1333 | |||
839 | rec_argv[i++] = STRDUP_FAIL_EXIT("-o"); | 1334 | rec_argv[i++] = STRDUP_FAIL_EXIT("-o"); |
840 | rec_argv[i++] = STRDUP_FAIL_EXIT(kvm->file_name); | 1335 | rec_argv[i++] = STRDUP_FAIL_EXIT(kvm->file_name); |
841 | 1336 | ||
@@ -856,6 +1351,8 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
856 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", | 1351 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", |
857 | "key for sorting: sample(sort by samples number)" | 1352 | "key for sorting: sample(sort by samples number)" |
858 | " time (sort by avg time)"), | 1353 | " time (sort by avg time)"), |
1354 | OPT_STRING('p', "pid", &kvm->pid_str, "pid", | ||
1355 | "analyze events only for given process id(s)"), | ||
859 | OPT_END() | 1356 | OPT_END() |
860 | }; | 1357 | }; |
861 | 1358 | ||
@@ -878,6 +1375,190 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) | |||
878 | return kvm_events_report_vcpu(kvm); | 1375 | return kvm_events_report_vcpu(kvm); |
879 | } | 1376 | } |
880 | 1377 | ||
1378 | static struct perf_evlist *kvm_live_event_list(void) | ||
1379 | { | ||
1380 | struct perf_evlist *evlist; | ||
1381 | char *tp, *name, *sys; | ||
1382 | unsigned int j; | ||
1383 | int err = -1; | ||
1384 | |||
1385 | evlist = perf_evlist__new(); | ||
1386 | if (evlist == NULL) | ||
1387 | return NULL; | ||
1388 | |||
1389 | for (j = 0; j < ARRAY_SIZE(kvm_events_tp); j++) { | ||
1390 | |||
1391 | tp = strdup(kvm_events_tp[j]); | ||
1392 | if (tp == NULL) | ||
1393 | goto out; | ||
1394 | |||
1395 | /* split tracepoint into subsystem and name */ | ||
1396 | sys = tp; | ||
1397 | name = strchr(tp, ':'); | ||
1398 | if (name == NULL) { | ||
1399 | pr_err("Error parsing %s tracepoint: subsystem delimiter not found\n", | ||
1400 | kvm_events_tp[j]); | ||
1401 | free(tp); | ||
1402 | goto out; | ||
1403 | } | ||
1404 | *name = '\0'; | ||
1405 | name++; | ||
1406 | |||
1407 | if (perf_evlist__add_newtp(evlist, sys, name, NULL)) { | ||
1408 | pr_err("Failed to add %s tracepoint to the list\n", kvm_events_tp[j]); | ||
1409 | free(tp); | ||
1410 | goto out; | ||
1411 | } | ||
1412 | |||
1413 | free(tp); | ||
1414 | } | ||
1415 | |||
1416 | err = 0; | ||
1417 | |||
1418 | out: | ||
1419 | if (err) { | ||
1420 | perf_evlist__delete(evlist); | ||
1421 | evlist = NULL; | ||
1422 | } | ||
1423 | |||
1424 | return evlist; | ||
1425 | } | ||
1426 | |||
1427 | static int kvm_events_live(struct perf_kvm_stat *kvm, | ||
1428 | int argc, const char **argv) | ||
1429 | { | ||
1430 | char errbuf[BUFSIZ]; | ||
1431 | int err; | ||
1432 | |||
1433 | const struct option live_options[] = { | ||
1434 | OPT_STRING('p', "pid", &kvm->opts.target.pid, "pid", | ||
1435 | "record events on existing process id"), | ||
1436 | OPT_UINTEGER('m', "mmap-pages", &kvm->opts.mmap_pages, | ||
1437 | "number of mmap data pages"), | ||
1438 | OPT_INCR('v', "verbose", &verbose, | ||
1439 | "be more verbose (show counter open errors, etc)"), | ||
1440 | OPT_BOOLEAN('a', "all-cpus", &kvm->opts.target.system_wide, | ||
1441 | "system-wide collection from all CPUs"), | ||
1442 | OPT_UINTEGER('d', "display", &kvm->display_time, | ||
1443 | "time in seconds between display updates"), | ||
1444 | OPT_STRING(0, "event", &kvm->report_event, "report event", | ||
1445 | "event for reporting: vmexit, mmio, ioport"), | ||
1446 | OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu, | ||
1447 | "vcpu id to report"), | ||
1448 | OPT_STRING('k', "key", &kvm->sort_key, "sort-key", | ||
1449 | "key for sorting: sample(sort by samples number)" | ||
1450 | " time (sort by avg time)"), | ||
1451 | OPT_U64(0, "duration", &kvm->duration, | ||
1452 | "show events other than HALT that take longer than duration usecs"), | ||
1453 | OPT_END() | ||
1454 | }; | ||
1455 | const char * const live_usage[] = { | ||
1456 | "perf kvm stat live [<options>]", | ||
1457 | NULL | ||
1458 | }; | ||
1459 | |||
1460 | |||
1461 | /* event handling */ | ||
1462 | kvm->tool.sample = process_sample_event; | ||
1463 | kvm->tool.comm = perf_event__process_comm; | ||
1464 | kvm->tool.exit = perf_event__process_exit; | ||
1465 | kvm->tool.fork = perf_event__process_fork; | ||
1466 | kvm->tool.lost = process_lost_event; | ||
1467 | kvm->tool.ordered_samples = true; | ||
1468 | perf_tool__fill_defaults(&kvm->tool); | ||
1469 | |||
1470 | /* set defaults */ | ||
1471 | kvm->display_time = 1; | ||
1472 | kvm->opts.user_interval = 1; | ||
1473 | kvm->opts.mmap_pages = 512; | ||
1474 | kvm->opts.target.uses_mmap = false; | ||
1475 | kvm->opts.target.uid_str = NULL; | ||
1476 | kvm->opts.target.uid = UINT_MAX; | ||
1477 | |||
1478 | symbol__init(); | ||
1479 | disable_buildid_cache(); | ||
1480 | |||
1481 | use_browser = 0; | ||
1482 | setup_browser(false); | ||
1483 | |||
1484 | if (argc) { | ||
1485 | argc = parse_options(argc, argv, live_options, | ||
1486 | live_usage, 0); | ||
1487 | if (argc) | ||
1488 | usage_with_options(live_usage, live_options); | ||
1489 | } | ||
1490 | |||
1491 | kvm->duration *= NSEC_PER_USEC; /* convert usec to nsec */ | ||
1492 | |||
1493 | /* | ||
1494 | * target related setups | ||
1495 | */ | ||
1496 | err = perf_target__validate(&kvm->opts.target); | ||
1497 | if (err) { | ||
1498 | perf_target__strerror(&kvm->opts.target, err, errbuf, BUFSIZ); | ||
1499 | ui__warning("%s", errbuf); | ||
1500 | } | ||
1501 | |||
1502 | if (perf_target__none(&kvm->opts.target)) | ||
1503 | kvm->opts.target.system_wide = true; | ||
1504 | |||
1505 | |||
1506 | /* | ||
1507 | * generate the event list | ||
1508 | */ | ||
1509 | kvm->evlist = kvm_live_event_list(); | ||
1510 | if (kvm->evlist == NULL) { | ||
1511 | err = -1; | ||
1512 | goto out; | ||
1513 | } | ||
1514 | |||
1515 | symbol_conf.nr_events = kvm->evlist->nr_entries; | ||
1516 | |||
1517 | if (perf_evlist__create_maps(kvm->evlist, &kvm->opts.target) < 0) | ||
1518 | usage_with_options(live_usage, live_options); | ||
1519 | |||
1520 | /* | ||
1521 | * perf session | ||
1522 | */ | ||
1523 | kvm->session = perf_session__new(NULL, O_WRONLY, false, false, &kvm->tool); | ||
1524 | if (kvm->session == NULL) { | ||
1525 | err = -ENOMEM; | ||
1526 | goto out; | ||
1527 | } | ||
1528 | kvm->session->evlist = kvm->evlist; | ||
1529 | perf_session__set_id_hdr_size(kvm->session); | ||
1530 | |||
1531 | |||
1532 | if (perf_target__has_task(&kvm->opts.target)) | ||
1533 | perf_event__synthesize_thread_map(&kvm->tool, | ||
1534 | kvm->evlist->threads, | ||
1535 | perf_event__process, | ||
1536 | &kvm->session->machines.host); | ||
1537 | else | ||
1538 | perf_event__synthesize_threads(&kvm->tool, perf_event__process, | ||
1539 | &kvm->session->machines.host); | ||
1540 | |||
1541 | |||
1542 | err = kvm_live_open_events(kvm); | ||
1543 | if (err) | ||
1544 | goto out; | ||
1545 | |||
1546 | err = kvm_events_live_report(kvm); | ||
1547 | |||
1548 | out: | ||
1549 | exit_browser(0); | ||
1550 | |||
1551 | if (kvm->session) | ||
1552 | perf_session__delete(kvm->session); | ||
1553 | kvm->session = NULL; | ||
1554 | if (kvm->evlist) { | ||
1555 | perf_evlist__delete_maps(kvm->evlist); | ||
1556 | perf_evlist__delete(kvm->evlist); | ||
1557 | } | ||
1558 | |||
1559 | return err; | ||
1560 | } | ||
1561 | |||
881 | static void print_kvm_stat_usage(void) | 1562 | static void print_kvm_stat_usage(void) |
882 | { | 1563 | { |
883 | printf("Usage: perf kvm stat <command>\n\n"); | 1564 | printf("Usage: perf kvm stat <command>\n\n"); |
@@ -885,6 +1566,7 @@ static void print_kvm_stat_usage(void) | |||
885 | printf("# Available commands:\n"); | 1566 | printf("# Available commands:\n"); |
886 | printf("\trecord: record kvm events\n"); | 1567 | printf("\trecord: record kvm events\n"); |
887 | printf("\treport: report statistical data of kvm events\n"); | 1568 | printf("\treport: report statistical data of kvm events\n"); |
1569 | printf("\tlive: live reporting of statistical data of kvm events\n"); | ||
888 | 1570 | ||
889 | printf("\nOtherwise, it is the alias of 'perf stat':\n"); | 1571 | printf("\nOtherwise, it is the alias of 'perf stat':\n"); |
890 | } | 1572 | } |
@@ -914,6 +1596,9 @@ static int kvm_cmd_stat(const char *file_name, int argc, const char **argv) | |||
914 | if (!strncmp(argv[1], "rep", 3)) | 1596 | if (!strncmp(argv[1], "rep", 3)) |
915 | return kvm_events_report(&kvm, argc - 1 , argv + 1); | 1597 | return kvm_events_report(&kvm, argc - 1 , argv + 1); |
916 | 1598 | ||
1599 | if (!strncmp(argv[1], "live", 4)) | ||
1600 | return kvm_events_live(&kvm, argc - 1 , argv + 1); | ||
1601 | |||
917 | perf_stat: | 1602 | perf_stat: |
918 | return cmd_stat(argc, argv, NULL); | 1603 | return cmd_stat(argc, argv, NULL); |
919 | } | 1604 | } |
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 1948eceb517a..e79f423cc302 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c | |||
@@ -13,6 +13,7 @@ | |||
13 | 13 | ||
14 | #include "util/parse-events.h" | 14 | #include "util/parse-events.h" |
15 | #include "util/cache.h" | 15 | #include "util/cache.h" |
16 | #include "util/pmu.h" | ||
16 | 17 | ||
17 | int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | 18 | int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) |
18 | { | 19 | { |
@@ -37,6 +38,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
37 | else if (strcmp(argv[i], "cache") == 0 || | 38 | else if (strcmp(argv[i], "cache") == 0 || |
38 | strcmp(argv[i], "hwcache") == 0) | 39 | strcmp(argv[i], "hwcache") == 0) |
39 | print_hwcache_events(NULL, false); | 40 | print_hwcache_events(NULL, false); |
41 | else if (strcmp(argv[i], "pmu") == 0) | ||
42 | print_pmu_events(NULL, false); | ||
40 | else if (strcmp(argv[i], "--raw-dump") == 0) | 43 | else if (strcmp(argv[i], "--raw-dump") == 0) |
41 | print_events(NULL, true); | 44 | print_events(NULL, true); |
42 | else { | 45 | else { |
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 76543a4a7a30..ee33ba2f05dd 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
@@ -805,7 +805,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
805 | struct perf_evsel *evsel, | 805 | struct perf_evsel *evsel, |
806 | struct machine *machine) | 806 | struct machine *machine) |
807 | { | 807 | { |
808 | struct thread *thread = machine__findnew_thread(machine, sample->tid); | 808 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
809 | sample->tid); | ||
809 | 810 | ||
810 | if (thread == NULL) { | 811 | if (thread == NULL) { |
811 | pr_debug("problem processing %d event, skipping it.\n", | 812 | pr_debug("problem processing %d event, skipping it.\n", |
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index a8ff6d264e50..253133a6251d 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c | |||
@@ -14,7 +14,6 @@ static const char *mem_operation = MEM_OPERATION_LOAD; | |||
14 | struct perf_mem { | 14 | struct perf_mem { |
15 | struct perf_tool tool; | 15 | struct perf_tool tool; |
16 | char const *input_name; | 16 | char const *input_name; |
17 | symbol_filter_t annotate_init; | ||
18 | bool hide_unresolved; | 17 | bool hide_unresolved; |
19 | bool dump_raw; | 18 | bool dump_raw; |
20 | const char *cpu_list; | 19 | const char *cpu_list; |
@@ -69,8 +68,7 @@ dump_raw_samples(struct perf_tool *tool, | |||
69 | struct addr_location al; | 68 | struct addr_location al; |
70 | const char *fmt; | 69 | const char *fmt; |
71 | 70 | ||
72 | if (perf_event__preprocess_sample(event, machine, &al, sample, | 71 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { |
73 | mem->annotate_init) < 0) { | ||
74 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 72 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
75 | event->header.type); | 73 | event->header.type); |
76 | return -1; | 74 | return -1; |
@@ -96,7 +94,7 @@ dump_raw_samples(struct perf_tool *tool, | |||
96 | symbol_conf.field_sep, | 94 | symbol_conf.field_sep, |
97 | sample->tid, | 95 | sample->tid, |
98 | symbol_conf.field_sep, | 96 | symbol_conf.field_sep, |
99 | event->ip.ip, | 97 | sample->ip, |
100 | symbol_conf.field_sep, | 98 | symbol_conf.field_sep, |
101 | sample->addr, | 99 | sample->addr, |
102 | symbol_conf.field_sep, | 100 | symbol_conf.field_sep, |
@@ -192,6 +190,7 @@ int cmd_mem(int argc, const char **argv, const char *prefix __maybe_unused) | |||
192 | .tool = { | 190 | .tool = { |
193 | .sample = process_sample_event, | 191 | .sample = process_sample_event, |
194 | .mmap = perf_event__process_mmap, | 192 | .mmap = perf_event__process_mmap, |
193 | .mmap2 = perf_event__process_mmap2, | ||
195 | .comm = perf_event__process_comm, | 194 | .comm = perf_event__process_comm, |
196 | .lost = perf_event__process_lost, | 195 | .lost = perf_event__process_lost, |
197 | .fork = perf_event__process_fork, | 196 | .fork = perf_event__process_fork, |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index ecca62e27b28..d04651484640 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -474,13 +474,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
474 | goto out_delete_session; | 474 | goto out_delete_session; |
475 | } | 475 | } |
476 | 476 | ||
477 | err = perf_event__synthesize_event_types(tool, process_synthesized_event, | ||
478 | machine); | ||
479 | if (err < 0) { | ||
480 | pr_err("Couldn't synthesize event_types.\n"); | ||
481 | goto out_delete_session; | ||
482 | } | ||
483 | |||
484 | if (have_tracepoints(&evsel_list->entries)) { | 477 | if (have_tracepoints(&evsel_list->entries)) { |
485 | /* | 478 | /* |
486 | * FIXME err <= 0 here actually means that | 479 | * FIXME err <= 0 here actually means that |
@@ -719,21 +712,12 @@ static int get_stack_size(char *str, unsigned long *_size) | |||
719 | } | 712 | } |
720 | #endif /* LIBUNWIND_SUPPORT */ | 713 | #endif /* LIBUNWIND_SUPPORT */ |
721 | 714 | ||
722 | int record_parse_callchain_opt(const struct option *opt, | 715 | int record_parse_callchain(const char *arg, struct perf_record_opts *opts) |
723 | const char *arg, int unset) | ||
724 | { | 716 | { |
725 | struct perf_record_opts *opts = opt->value; | ||
726 | char *tok, *name, *saveptr = NULL; | 717 | char *tok, *name, *saveptr = NULL; |
727 | char *buf; | 718 | char *buf; |
728 | int ret = -1; | 719 | int ret = -1; |
729 | 720 | ||
730 | /* --no-call-graph */ | ||
731 | if (unset) | ||
732 | return 0; | ||
733 | |||
734 | /* We specified default option if none is provided. */ | ||
735 | BUG_ON(!arg); | ||
736 | |||
737 | /* We need buffer that we know we can write to. */ | 721 | /* We need buffer that we know we can write to. */ |
738 | buf = malloc(strlen(arg) + 1); | 722 | buf = malloc(strlen(arg) + 1); |
739 | if (!buf) | 723 | if (!buf) |
@@ -771,13 +755,9 @@ int record_parse_callchain_opt(const struct option *opt, | |||
771 | ret = get_stack_size(tok, &size); | 755 | ret = get_stack_size(tok, &size); |
772 | opts->stack_dump_size = size; | 756 | opts->stack_dump_size = size; |
773 | } | 757 | } |
774 | |||
775 | if (!ret) | ||
776 | pr_debug("callchain: stack dump size %d\n", | ||
777 | opts->stack_dump_size); | ||
778 | #endif /* LIBUNWIND_SUPPORT */ | 758 | #endif /* LIBUNWIND_SUPPORT */ |
779 | } else { | 759 | } else { |
780 | pr_err("callchain: Unknown -g option " | 760 | pr_err("callchain: Unknown --call-graph option " |
781 | "value: %s\n", arg); | 761 | "value: %s\n", arg); |
782 | break; | 762 | break; |
783 | } | 763 | } |
@@ -785,13 +765,52 @@ int record_parse_callchain_opt(const struct option *opt, | |||
785 | } while (0); | 765 | } while (0); |
786 | 766 | ||
787 | free(buf); | 767 | free(buf); |
768 | return ret; | ||
769 | } | ||
770 | |||
771 | static void callchain_debug(struct perf_record_opts *opts) | ||
772 | { | ||
773 | pr_debug("callchain: type %d\n", opts->call_graph); | ||
774 | |||
775 | if (opts->call_graph == CALLCHAIN_DWARF) | ||
776 | pr_debug("callchain: stack dump size %d\n", | ||
777 | opts->stack_dump_size); | ||
778 | } | ||
788 | 779 | ||
780 | int record_parse_callchain_opt(const struct option *opt, | ||
781 | const char *arg, | ||
782 | int unset) | ||
783 | { | ||
784 | struct perf_record_opts *opts = opt->value; | ||
785 | int ret; | ||
786 | |||
787 | /* --no-call-graph */ | ||
788 | if (unset) { | ||
789 | opts->call_graph = CALLCHAIN_NONE; | ||
790 | pr_debug("callchain: disabled\n"); | ||
791 | return 0; | ||
792 | } | ||
793 | |||
794 | ret = record_parse_callchain(arg, opts); | ||
789 | if (!ret) | 795 | if (!ret) |
790 | pr_debug("callchain: type %d\n", opts->call_graph); | 796 | callchain_debug(opts); |
791 | 797 | ||
792 | return ret; | 798 | return ret; |
793 | } | 799 | } |
794 | 800 | ||
801 | int record_callchain_opt(const struct option *opt, | ||
802 | const char *arg __maybe_unused, | ||
803 | int unset __maybe_unused) | ||
804 | { | ||
805 | struct perf_record_opts *opts = opt->value; | ||
806 | |||
807 | if (opts->call_graph == CALLCHAIN_NONE) | ||
808 | opts->call_graph = CALLCHAIN_FP; | ||
809 | |||
810 | callchain_debug(opts); | ||
811 | return 0; | ||
812 | } | ||
813 | |||
795 | static const char * const record_usage[] = { | 814 | static const char * const record_usage[] = { |
796 | "perf record [<options>] [<command>]", | 815 | "perf record [<options>] [<command>]", |
797 | "perf record [<options>] -- <command> [<options>]", | 816 | "perf record [<options>] -- <command> [<options>]", |
@@ -820,12 +839,12 @@ static struct perf_record record = { | |||
820 | }, | 839 | }, |
821 | }; | 840 | }; |
822 | 841 | ||
823 | #define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: " | 842 | #define CALLCHAIN_HELP "setup and enables call-graph (stack chain/backtrace) recording: " |
824 | 843 | ||
825 | #ifdef LIBUNWIND_SUPPORT | 844 | #ifdef LIBUNWIND_SUPPORT |
826 | const char record_callchain_help[] = CALLCHAIN_HELP "[fp] dwarf"; | 845 | const char record_callchain_help[] = CALLCHAIN_HELP "fp dwarf"; |
827 | #else | 846 | #else |
828 | const char record_callchain_help[] = CALLCHAIN_HELP "[fp]"; | 847 | const char record_callchain_help[] = CALLCHAIN_HELP "fp"; |
829 | #endif | 848 | #endif |
830 | 849 | ||
831 | /* | 850 | /* |
@@ -865,9 +884,12 @@ const struct option record_options[] = { | |||
865 | "number of mmap data pages"), | 884 | "number of mmap data pages"), |
866 | OPT_BOOLEAN(0, "group", &record.opts.group, | 885 | OPT_BOOLEAN(0, "group", &record.opts.group, |
867 | "put the counters into a counter group"), | 886 | "put the counters into a counter group"), |
868 | OPT_CALLBACK_DEFAULT('g', "call-graph", &record.opts, | 887 | OPT_CALLBACK_NOOPT('g', NULL, &record.opts, |
869 | "mode[,dump_size]", record_callchain_help, | 888 | NULL, "enables call-graph recording" , |
870 | &record_parse_callchain_opt, "fp"), | 889 | &record_callchain_opt), |
890 | OPT_CALLBACK(0, "call-graph", &record.opts, | ||
891 | "mode[,dump_size]", record_callchain_help, | ||
892 | &record_parse_callchain_opt), | ||
871 | OPT_INCR('v', "verbose", &verbose, | 893 | OPT_INCR('v', "verbose", &verbose, |
872 | "be more verbose (show counter open errors, etc)"), | 894 | "be more verbose (show counter open errors, etc)"), |
873 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), | 895 | OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"), |
@@ -904,7 +926,6 @@ const struct option record_options[] = { | |||
904 | int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | 926 | int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) |
905 | { | 927 | { |
906 | int err = -ENOMEM; | 928 | int err = -ENOMEM; |
907 | struct perf_evsel *pos; | ||
908 | struct perf_evlist *evsel_list; | 929 | struct perf_evlist *evsel_list; |
909 | struct perf_record *rec = &record; | 930 | struct perf_record *rec = &record; |
910 | char errbuf[BUFSIZ]; | 931 | char errbuf[BUFSIZ]; |
@@ -968,11 +989,6 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused) | |||
968 | if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) | 989 | if (perf_evlist__create_maps(evsel_list, &rec->opts.target) < 0) |
969 | usage_with_options(record_usage, record_options); | 990 | usage_with_options(record_usage, record_options); |
970 | 991 | ||
971 | list_for_each_entry(pos, &evsel_list->entries, node) { | ||
972 | if (perf_header__push_event(pos->attr.config, perf_evsel__name(pos))) | ||
973 | goto out_free_fd; | ||
974 | } | ||
975 | |||
976 | if (rec->opts.user_interval != ULLONG_MAX) | 992 | if (rec->opts.user_interval != ULLONG_MAX) |
977 | rec->opts.default_interval = rec->opts.user_interval; | 993 | rec->opts.default_interval = rec->opts.user_interval; |
978 | if (rec->opts.user_freq != UINT_MAX) | 994 | if (rec->opts.user_freq != UINT_MAX) |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 3662047cc6b1..72eae7498c09 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -49,7 +49,6 @@ struct perf_report { | |||
49 | bool mem_mode; | 49 | bool mem_mode; |
50 | struct perf_read_values show_threads_values; | 50 | struct perf_read_values show_threads_values; |
51 | const char *pretty_printing_style; | 51 | const char *pretty_printing_style; |
52 | symbol_filter_t annotate_init; | ||
53 | const char *cpu_list; | 52 | const char *cpu_list; |
54 | const char *symbol_filter_str; | 53 | const char *symbol_filter_str; |
55 | float min_percent; | 54 | float min_percent; |
@@ -89,7 +88,7 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, | |||
89 | if ((sort__has_parent || symbol_conf.use_callchain) && | 88 | if ((sort__has_parent || symbol_conf.use_callchain) && |
90 | sample->callchain) { | 89 | sample->callchain) { |
91 | err = machine__resolve_callchain(machine, evsel, al->thread, | 90 | err = machine__resolve_callchain(machine, evsel, al->thread, |
92 | sample, &parent); | 91 | sample, &parent, al); |
93 | if (err) | 92 | if (err) |
94 | return err; | 93 | return err; |
95 | } | 94 | } |
@@ -180,7 +179,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | |||
180 | if ((sort__has_parent || symbol_conf.use_callchain) | 179 | if ((sort__has_parent || symbol_conf.use_callchain) |
181 | && sample->callchain) { | 180 | && sample->callchain) { |
182 | err = machine__resolve_callchain(machine, evsel, al->thread, | 181 | err = machine__resolve_callchain(machine, evsel, al->thread, |
183 | sample, &parent); | 182 | sample, &parent, al); |
184 | if (err) | 183 | if (err) |
185 | return err; | 184 | return err; |
186 | } | 185 | } |
@@ -254,7 +253,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, | |||
254 | 253 | ||
255 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { | 254 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { |
256 | err = machine__resolve_callchain(machine, evsel, al->thread, | 255 | err = machine__resolve_callchain(machine, evsel, al->thread, |
257 | sample, &parent); | 256 | sample, &parent, al); |
258 | if (err) | 257 | if (err) |
259 | return err; | 258 | return err; |
260 | } | 259 | } |
@@ -305,8 +304,7 @@ static int process_sample_event(struct perf_tool *tool, | |||
305 | struct addr_location al; | 304 | struct addr_location al; |
306 | int ret; | 305 | int ret; |
307 | 306 | ||
308 | if (perf_event__preprocess_sample(event, machine, &al, sample, | 307 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { |
309 | rep->annotate_init) < 0) { | ||
310 | fprintf(stderr, "problem processing %d event, skipping it.\n", | 308 | fprintf(stderr, "problem processing %d event, skipping it.\n", |
311 | event->header.type); | 309 | event->header.type); |
312 | return -1; | 310 | return -1; |
@@ -367,7 +365,7 @@ static int process_read_event(struct perf_tool *tool, | |||
367 | static int perf_report__setup_sample_type(struct perf_report *rep) | 365 | static int perf_report__setup_sample_type(struct perf_report *rep) |
368 | { | 366 | { |
369 | struct perf_session *self = rep->session; | 367 | struct perf_session *self = rep->session; |
370 | u64 sample_type = perf_evlist__sample_type(self->evlist); | 368 | u64 sample_type = perf_evlist__combined_sample_type(self->evlist); |
371 | 369 | ||
372 | if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { | 370 | if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { |
373 | if (sort__has_parent) { | 371 | if (sort__has_parent) { |
@@ -403,8 +401,6 @@ static int perf_report__setup_sample_type(struct perf_report *rep) | |||
403 | return 0; | 401 | return 0; |
404 | } | 402 | } |
405 | 403 | ||
406 | extern volatile int session_done; | ||
407 | |||
408 | static void sig_handler(int sig __maybe_unused) | 404 | static void sig_handler(int sig __maybe_unused) |
409 | { | 405 | { |
410 | session_done = 1; | 406 | session_done = 1; |
@@ -497,7 +493,7 @@ static int __cmd_report(struct perf_report *rep) | |||
497 | ret = perf_session__cpu_bitmap(session, rep->cpu_list, | 493 | ret = perf_session__cpu_bitmap(session, rep->cpu_list, |
498 | rep->cpu_bitmap); | 494 | rep->cpu_bitmap); |
499 | if (ret) | 495 | if (ret) |
500 | goto out_delete; | 496 | return ret; |
501 | } | 497 | } |
502 | 498 | ||
503 | if (use_browser <= 0) | 499 | if (use_browser <= 0) |
@@ -508,11 +504,11 @@ static int __cmd_report(struct perf_report *rep) | |||
508 | 504 | ||
509 | ret = perf_report__setup_sample_type(rep); | 505 | ret = perf_report__setup_sample_type(rep); |
510 | if (ret) | 506 | if (ret) |
511 | goto out_delete; | 507 | return ret; |
512 | 508 | ||
513 | ret = perf_session__process_events(session, &rep->tool); | 509 | ret = perf_session__process_events(session, &rep->tool); |
514 | if (ret) | 510 | if (ret) |
515 | goto out_delete; | 511 | return ret; |
516 | 512 | ||
517 | kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION]; | 513 | kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION]; |
518 | kernel_kmap = map__kmap(kernel_map); | 514 | kernel_kmap = map__kmap(kernel_map); |
@@ -547,7 +543,7 @@ static int __cmd_report(struct perf_report *rep) | |||
547 | 543 | ||
548 | if (dump_trace) { | 544 | if (dump_trace) { |
549 | perf_session__fprintf_nr_events(session, stdout); | 545 | perf_session__fprintf_nr_events(session, stdout); |
550 | goto out_delete; | 546 | return 0; |
551 | } | 547 | } |
552 | 548 | ||
553 | nr_samples = 0; | 549 | nr_samples = 0; |
@@ -570,9 +566,12 @@ static int __cmd_report(struct perf_report *rep) | |||
570 | } | 566 | } |
571 | } | 567 | } |
572 | 568 | ||
569 | if (session_done()) | ||
570 | return 0; | ||
571 | |||
573 | if (nr_samples == 0) { | 572 | if (nr_samples == 0) { |
574 | ui__error("The %s file has no samples!\n", session->filename); | 573 | ui__error("The %s file has no samples!\n", session->filename); |
575 | goto out_delete; | 574 | return 0; |
576 | } | 575 | } |
577 | 576 | ||
578 | list_for_each_entry(pos, &session->evlist->entries, node) | 577 | list_for_each_entry(pos, &session->evlist->entries, node) |
@@ -598,19 +597,6 @@ static int __cmd_report(struct perf_report *rep) | |||
598 | } else | 597 | } else |
599 | perf_evlist__tty_browse_hists(session->evlist, rep, help); | 598 | perf_evlist__tty_browse_hists(session->evlist, rep, help); |
600 | 599 | ||
601 | out_delete: | ||
602 | /* | ||
603 | * Speed up the exit process, for large files this can | ||
604 | * take quite a while. | ||
605 | * | ||
606 | * XXX Enable this when using valgrind or if we ever | ||
607 | * librarize this command. | ||
608 | * | ||
609 | * Also experiment with obstacks to see how much speed | ||
610 | * up we'll get here. | ||
611 | * | ||
612 | * perf_session__delete(session); | ||
613 | */ | ||
614 | return ret; | 600 | return ret; |
615 | } | 601 | } |
616 | 602 | ||
@@ -680,12 +666,23 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset) | |||
680 | } | 666 | } |
681 | 667 | ||
682 | /* get the call chain order */ | 668 | /* get the call chain order */ |
683 | if (!strcmp(tok2, "caller")) | 669 | if (!strncmp(tok2, "caller", strlen("caller"))) |
684 | callchain_param.order = ORDER_CALLER; | 670 | callchain_param.order = ORDER_CALLER; |
685 | else if (!strcmp(tok2, "callee")) | 671 | else if (!strncmp(tok2, "callee", strlen("callee"))) |
686 | callchain_param.order = ORDER_CALLEE; | 672 | callchain_param.order = ORDER_CALLEE; |
687 | else | 673 | else |
688 | return -1; | 674 | return -1; |
675 | |||
676 | /* Get the sort key */ | ||
677 | tok2 = strtok(NULL, ","); | ||
678 | if (!tok2) | ||
679 | goto setup; | ||
680 | if (!strncmp(tok2, "function", strlen("function"))) | ||
681 | callchain_param.key = CCKEY_FUNCTION; | ||
682 | else if (!strncmp(tok2, "address", strlen("address"))) | ||
683 | callchain_param.key = CCKEY_ADDRESS; | ||
684 | else | ||
685 | return -1; | ||
689 | setup: | 686 | setup: |
690 | if (callchain_register_param(&callchain_param) < 0) { | 687 | if (callchain_register_param(&callchain_param) < 0) { |
691 | fprintf(stderr, "Can't register callchain params\n"); | 688 | fprintf(stderr, "Can't register callchain params\n"); |
@@ -694,6 +691,24 @@ setup: | |||
694 | return 0; | 691 | return 0; |
695 | } | 692 | } |
696 | 693 | ||
694 | int | ||
695 | report_parse_ignore_callees_opt(const struct option *opt __maybe_unused, | ||
696 | const char *arg, int unset __maybe_unused) | ||
697 | { | ||
698 | if (arg) { | ||
699 | int err = regcomp(&ignore_callees_regex, arg, REG_EXTENDED); | ||
700 | if (err) { | ||
701 | char buf[BUFSIZ]; | ||
702 | regerror(err, &ignore_callees_regex, buf, sizeof(buf)); | ||
703 | pr_err("Invalid --ignore-callees regex: %s\n%s", arg, buf); | ||
704 | return -1; | ||
705 | } | ||
706 | have_ignore_callees = 1; | ||
707 | } | ||
708 | |||
709 | return 0; | ||
710 | } | ||
711 | |||
697 | static int | 712 | static int |
698 | parse_branch_mode(const struct option *opt __maybe_unused, | 713 | parse_branch_mode(const struct option *opt __maybe_unused, |
699 | const char *str __maybe_unused, int unset) | 714 | const char *str __maybe_unused, int unset) |
@@ -730,13 +745,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
730 | .tool = { | 745 | .tool = { |
731 | .sample = process_sample_event, | 746 | .sample = process_sample_event, |
732 | .mmap = perf_event__process_mmap, | 747 | .mmap = perf_event__process_mmap, |
748 | .mmap2 = perf_event__process_mmap2, | ||
733 | .comm = perf_event__process_comm, | 749 | .comm = perf_event__process_comm, |
734 | .exit = perf_event__process_exit, | 750 | .exit = perf_event__process_exit, |
735 | .fork = perf_event__process_fork, | 751 | .fork = perf_event__process_fork, |
736 | .lost = perf_event__process_lost, | 752 | .lost = perf_event__process_lost, |
737 | .read = process_read_event, | 753 | .read = process_read_event, |
738 | .attr = perf_event__process_attr, | 754 | .attr = perf_event__process_attr, |
739 | .event_type = perf_event__process_event_type, | ||
740 | .tracing_data = perf_event__process_tracing_data, | 755 | .tracing_data = perf_event__process_tracing_data, |
741 | .build_id = perf_event__process_build_id, | 756 | .build_id = perf_event__process_build_id, |
742 | .ordered_samples = true, | 757 | .ordered_samples = true, |
@@ -780,10 +795,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
780 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, | 795 | OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other, |
781 | "Only display entries with parent-match"), | 796 | "Only display entries with parent-match"), |
782 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", | 797 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", |
783 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit and callchain order. " | 798 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " |
784 | "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt), | 799 | "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), |
785 | OPT_BOOLEAN('G', "inverted", &report.inverted_callchain, | 800 | OPT_BOOLEAN('G', "inverted", &report.inverted_callchain, |
786 | "alias for inverted call graph"), | 801 | "alias for inverted call graph"), |
802 | OPT_CALLBACK(0, "ignore-callees", NULL, "regex", | ||
803 | "ignore callees of these functions in call graphs", | ||
804 | report_parse_ignore_callees_opt), | ||
787 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 805 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
788 | "only consider symbols in these dsos"), | 806 | "only consider symbols in these dsos"), |
789 | OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | 807 | OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
@@ -853,7 +871,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
853 | setup_browser(true); | 871 | setup_browser(true); |
854 | else { | 872 | else { |
855 | use_browser = 0; | 873 | use_browser = 0; |
856 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
857 | perf_hpp__init(); | 874 | perf_hpp__init(); |
858 | } | 875 | } |
859 | 876 | ||
@@ -907,7 +924,8 @@ repeat: | |||
907 | */ | 924 | */ |
908 | if (use_browser == 1 && sort__has_sym) { | 925 | if (use_browser == 1 && sort__has_sym) { |
909 | symbol_conf.priv_size = sizeof(struct annotation); | 926 | symbol_conf.priv_size = sizeof(struct annotation); |
910 | report.annotate_init = symbol__annotate_init; | 927 | machines__set_symbol_filter(&session->machines, |
928 | symbol__annotate_init); | ||
911 | /* | 929 | /* |
912 | * For searching by name on the "Browse map details". | 930 | * For searching by name on the "Browse map details". |
913 | * providing it only in verbose mode not to bloat too | 931 | * providing it only in verbose mode not to bloat too |
@@ -931,14 +949,6 @@ repeat: | |||
931 | if (parent_pattern != default_parent_pattern) { | 949 | if (parent_pattern != default_parent_pattern) { |
932 | if (sort_dimension__add("parent") < 0) | 950 | if (sort_dimension__add("parent") < 0) |
933 | goto error; | 951 | goto error; |
934 | |||
935 | /* | ||
936 | * Only show the parent fields if we explicitly | ||
937 | * sort that way. If we only use parent machinery | ||
938 | * for filtering, we don't want it. | ||
939 | */ | ||
940 | if (!strstr(sort_order, "parent")) | ||
941 | sort_parent.elide = 1; | ||
942 | } | 952 | } |
943 | 953 | ||
944 | if (argc) { | 954 | if (argc) { |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index fed9ae432c16..d8c51b2f263f 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -109,8 +109,9 @@ struct trace_sched_handler { | |||
109 | int (*wakeup_event)(struct perf_sched *sched, struct perf_evsel *evsel, | 109 | int (*wakeup_event)(struct perf_sched *sched, struct perf_evsel *evsel, |
110 | struct perf_sample *sample, struct machine *machine); | 110 | struct perf_sample *sample, struct machine *machine); |
111 | 111 | ||
112 | int (*fork_event)(struct perf_sched *sched, struct perf_evsel *evsel, | 112 | /* PERF_RECORD_FORK event, not sched_process_fork tracepoint */ |
113 | struct perf_sample *sample); | 113 | int (*fork_event)(struct perf_sched *sched, union perf_event *event, |
114 | struct machine *machine); | ||
114 | 115 | ||
115 | int (*migrate_task_event)(struct perf_sched *sched, | 116 | int (*migrate_task_event)(struct perf_sched *sched, |
116 | struct perf_evsel *evsel, | 117 | struct perf_evsel *evsel, |
@@ -717,22 +718,31 @@ static int replay_switch_event(struct perf_sched *sched, | |||
717 | return 0; | 718 | return 0; |
718 | } | 719 | } |
719 | 720 | ||
720 | static int replay_fork_event(struct perf_sched *sched, struct perf_evsel *evsel, | 721 | static int replay_fork_event(struct perf_sched *sched, |
721 | struct perf_sample *sample) | 722 | union perf_event *event, |
723 | struct machine *machine) | ||
722 | { | 724 | { |
723 | const char *parent_comm = perf_evsel__strval(evsel, sample, "parent_comm"), | 725 | struct thread *child, *parent; |
724 | *child_comm = perf_evsel__strval(evsel, sample, "child_comm"); | 726 | |
725 | const u32 parent_pid = perf_evsel__intval(evsel, sample, "parent_pid"), | 727 | child = machine__findnew_thread(machine, event->fork.pid, |
726 | child_pid = perf_evsel__intval(evsel, sample, "child_pid"); | 728 | event->fork.tid); |
729 | parent = machine__findnew_thread(machine, event->fork.ppid, | ||
730 | event->fork.ptid); | ||
731 | |||
732 | if (child == NULL || parent == NULL) { | ||
733 | pr_debug("thread does not exist on fork event: child %p, parent %p\n", | ||
734 | child, parent); | ||
735 | return 0; | ||
736 | } | ||
727 | 737 | ||
728 | if (verbose) { | 738 | if (verbose) { |
729 | printf("sched_fork event %p\n", evsel); | 739 | printf("fork event\n"); |
730 | printf("... parent: %s/%d\n", parent_comm, parent_pid); | 740 | printf("... parent: %s/%d\n", parent->comm, parent->tid); |
731 | printf("... child: %s/%d\n", child_comm, child_pid); | 741 | printf("... child: %s/%d\n", child->comm, child->tid); |
732 | } | 742 | } |
733 | 743 | ||
734 | register_pid(sched, parent_pid, parent_comm); | 744 | register_pid(sched, parent->tid, parent->comm); |
735 | register_pid(sched, child_pid, child_comm); | 745 | register_pid(sched, child->tid, child->comm); |
736 | return 0; | 746 | return 0; |
737 | } | 747 | } |
738 | 748 | ||
@@ -824,14 +834,6 @@ static int thread_atoms_insert(struct perf_sched *sched, struct thread *thread) | |||
824 | return 0; | 834 | return 0; |
825 | } | 835 | } |
826 | 836 | ||
827 | static int latency_fork_event(struct perf_sched *sched __maybe_unused, | ||
828 | struct perf_evsel *evsel __maybe_unused, | ||
829 | struct perf_sample *sample __maybe_unused) | ||
830 | { | ||
831 | /* should insert the newcomer */ | ||
832 | return 0; | ||
833 | } | ||
834 | |||
835 | static char sched_out_state(u64 prev_state) | 837 | static char sched_out_state(u64 prev_state) |
836 | { | 838 | { |
837 | const char *str = TASK_STATE_TO_CHAR_STR; | 839 | const char *str = TASK_STATE_TO_CHAR_STR; |
@@ -934,8 +936,8 @@ static int latency_switch_event(struct perf_sched *sched, | |||
934 | return -1; | 936 | return -1; |
935 | } | 937 | } |
936 | 938 | ||
937 | sched_out = machine__findnew_thread(machine, prev_pid); | 939 | sched_out = machine__findnew_thread(machine, 0, prev_pid); |
938 | sched_in = machine__findnew_thread(machine, next_pid); | 940 | sched_in = machine__findnew_thread(machine, 0, next_pid); |
939 | 941 | ||
940 | out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid); | 942 | out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid); |
941 | if (!out_events) { | 943 | if (!out_events) { |
@@ -978,7 +980,7 @@ static int latency_runtime_event(struct perf_sched *sched, | |||
978 | { | 980 | { |
979 | const u32 pid = perf_evsel__intval(evsel, sample, "pid"); | 981 | const u32 pid = perf_evsel__intval(evsel, sample, "pid"); |
980 | const u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | 982 | const u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); |
981 | struct thread *thread = machine__findnew_thread(machine, pid); | 983 | struct thread *thread = machine__findnew_thread(machine, 0, pid); |
982 | struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); | 984 | struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); |
983 | u64 timestamp = sample->time; | 985 | u64 timestamp = sample->time; |
984 | int cpu = sample->cpu; | 986 | int cpu = sample->cpu; |
@@ -1016,7 +1018,7 @@ static int latency_wakeup_event(struct perf_sched *sched, | |||
1016 | if (!success) | 1018 | if (!success) |
1017 | return 0; | 1019 | return 0; |
1018 | 1020 | ||
1019 | wakee = machine__findnew_thread(machine, pid); | 1021 | wakee = machine__findnew_thread(machine, 0, pid); |
1020 | atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid); | 1022 | atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid); |
1021 | if (!atoms) { | 1023 | if (!atoms) { |
1022 | if (thread_atoms_insert(sched, wakee)) | 1024 | if (thread_atoms_insert(sched, wakee)) |
@@ -1070,12 +1072,12 @@ static int latency_migrate_task_event(struct perf_sched *sched, | |||
1070 | if (sched->profile_cpu == -1) | 1072 | if (sched->profile_cpu == -1) |
1071 | return 0; | 1073 | return 0; |
1072 | 1074 | ||
1073 | migrant = machine__findnew_thread(machine, pid); | 1075 | migrant = machine__findnew_thread(machine, 0, pid); |
1074 | atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); | 1076 | atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); |
1075 | if (!atoms) { | 1077 | if (!atoms) { |
1076 | if (thread_atoms_insert(sched, migrant)) | 1078 | if (thread_atoms_insert(sched, migrant)) |
1077 | return -1; | 1079 | return -1; |
1078 | register_pid(sched, migrant->pid, migrant->comm); | 1080 | register_pid(sched, migrant->tid, migrant->comm); |
1079 | atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); | 1081 | atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); |
1080 | if (!atoms) { | 1082 | if (!atoms) { |
1081 | pr_err("migration-event: Internal tree error"); | 1083 | pr_err("migration-event: Internal tree error"); |
@@ -1115,7 +1117,7 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_ | |||
1115 | sched->all_runtime += work_list->total_runtime; | 1117 | sched->all_runtime += work_list->total_runtime; |
1116 | sched->all_count += work_list->nb_atoms; | 1118 | sched->all_count += work_list->nb_atoms; |
1117 | 1119 | ||
1118 | ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->pid); | 1120 | ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->tid); |
1119 | 1121 | ||
1120 | for (i = 0; i < 24 - ret; i++) | 1122 | for (i = 0; i < 24 - ret; i++) |
1121 | printf(" "); | 1123 | printf(" "); |
@@ -1131,9 +1133,9 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_ | |||
1131 | 1133 | ||
1132 | static int pid_cmp(struct work_atoms *l, struct work_atoms *r) | 1134 | static int pid_cmp(struct work_atoms *l, struct work_atoms *r) |
1133 | { | 1135 | { |
1134 | if (l->thread->pid < r->thread->pid) | 1136 | if (l->thread->tid < r->thread->tid) |
1135 | return -1; | 1137 | return -1; |
1136 | if (l->thread->pid > r->thread->pid) | 1138 | if (l->thread->tid > r->thread->tid) |
1137 | return 1; | 1139 | return 1; |
1138 | 1140 | ||
1139 | return 0; | 1141 | return 0; |
@@ -1289,8 +1291,8 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, | |||
1289 | return -1; | 1291 | return -1; |
1290 | } | 1292 | } |
1291 | 1293 | ||
1292 | sched_out = machine__findnew_thread(machine, prev_pid); | 1294 | sched_out = machine__findnew_thread(machine, 0, prev_pid); |
1293 | sched_in = machine__findnew_thread(machine, next_pid); | 1295 | sched_in = machine__findnew_thread(machine, 0, next_pid); |
1294 | 1296 | ||
1295 | sched->curr_thread[this_cpu] = sched_in; | 1297 | sched->curr_thread[this_cpu] = sched_in; |
1296 | 1298 | ||
@@ -1321,7 +1323,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, | |||
1321 | printf("*"); | 1323 | printf("*"); |
1322 | 1324 | ||
1323 | if (sched->curr_thread[cpu]) { | 1325 | if (sched->curr_thread[cpu]) { |
1324 | if (sched->curr_thread[cpu]->pid) | 1326 | if (sched->curr_thread[cpu]->tid) |
1325 | printf("%2s ", sched->curr_thread[cpu]->shortname); | 1327 | printf("%2s ", sched->curr_thread[cpu]->shortname); |
1326 | else | 1328 | else |
1327 | printf(". "); | 1329 | printf(". "); |
@@ -1332,7 +1334,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, | |||
1332 | printf(" %12.6f secs ", (double)timestamp/1e9); | 1334 | printf(" %12.6f secs ", (double)timestamp/1e9); |
1333 | if (new_shortname) { | 1335 | if (new_shortname) { |
1334 | printf("%s => %s:%d\n", | 1336 | printf("%s => %s:%d\n", |
1335 | sched_in->shortname, sched_in->comm, sched_in->pid); | 1337 | sched_in->shortname, sched_in->comm, sched_in->tid); |
1336 | } else { | 1338 | } else { |
1337 | printf("\n"); | 1339 | printf("\n"); |
1338 | } | 1340 | } |
@@ -1379,25 +1381,20 @@ static int process_sched_runtime_event(struct perf_tool *tool, | |||
1379 | return 0; | 1381 | return 0; |
1380 | } | 1382 | } |
1381 | 1383 | ||
1382 | static int process_sched_fork_event(struct perf_tool *tool, | 1384 | static int perf_sched__process_fork_event(struct perf_tool *tool, |
1383 | struct perf_evsel *evsel, | 1385 | union perf_event *event, |
1384 | struct perf_sample *sample, | 1386 | struct perf_sample *sample, |
1385 | struct machine *machine __maybe_unused) | 1387 | struct machine *machine) |
1386 | { | 1388 | { |
1387 | struct perf_sched *sched = container_of(tool, struct perf_sched, tool); | 1389 | struct perf_sched *sched = container_of(tool, struct perf_sched, tool); |
1388 | 1390 | ||
1389 | if (sched->tp_handler->fork_event) | 1391 | /* run the fork event through the perf machineruy */ |
1390 | return sched->tp_handler->fork_event(sched, evsel, sample); | 1392 | perf_event__process_fork(tool, event, sample, machine); |
1391 | 1393 | ||
1392 | return 0; | 1394 | /* and then run additional processing needed for this command */ |
1393 | } | 1395 | if (sched->tp_handler->fork_event) |
1396 | return sched->tp_handler->fork_event(sched, event, machine); | ||
1394 | 1397 | ||
1395 | static int process_sched_exit_event(struct perf_tool *tool __maybe_unused, | ||
1396 | struct perf_evsel *evsel, | ||
1397 | struct perf_sample *sample __maybe_unused, | ||
1398 | struct machine *machine __maybe_unused) | ||
1399 | { | ||
1400 | pr_debug("sched_exit event %p\n", evsel); | ||
1401 | return 0; | 1398 | return 0; |
1402 | } | 1399 | } |
1403 | 1400 | ||
@@ -1425,15 +1422,8 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_ | |||
1425 | struct perf_evsel *evsel, | 1422 | struct perf_evsel *evsel, |
1426 | struct machine *machine) | 1423 | struct machine *machine) |
1427 | { | 1424 | { |
1428 | struct thread *thread = machine__findnew_thread(machine, sample->tid); | ||
1429 | int err = 0; | 1425 | int err = 0; |
1430 | 1426 | ||
1431 | if (thread == NULL) { | ||
1432 | pr_debug("problem processing %s event, skipping it.\n", | ||
1433 | perf_evsel__name(evsel)); | ||
1434 | return -1; | ||
1435 | } | ||
1436 | |||
1437 | evsel->hists.stats.total_period += sample->period; | 1427 | evsel->hists.stats.total_period += sample->period; |
1438 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 1428 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
1439 | 1429 | ||
@@ -1445,7 +1435,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_ | |||
1445 | return err; | 1435 | return err; |
1446 | } | 1436 | } |
1447 | 1437 | ||
1448 | static int perf_sched__read_events(struct perf_sched *sched, bool destroy, | 1438 | static int perf_sched__read_events(struct perf_sched *sched, |
1449 | struct perf_session **psession) | 1439 | struct perf_session **psession) |
1450 | { | 1440 | { |
1451 | const struct perf_evsel_str_handler handlers[] = { | 1441 | const struct perf_evsel_str_handler handlers[] = { |
@@ -1453,8 +1443,6 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy, | |||
1453 | { "sched:sched_stat_runtime", process_sched_runtime_event, }, | 1443 | { "sched:sched_stat_runtime", process_sched_runtime_event, }, |
1454 | { "sched:sched_wakeup", process_sched_wakeup_event, }, | 1444 | { "sched:sched_wakeup", process_sched_wakeup_event, }, |
1455 | { "sched:sched_wakeup_new", process_sched_wakeup_event, }, | 1445 | { "sched:sched_wakeup_new", process_sched_wakeup_event, }, |
1456 | { "sched:sched_process_fork", process_sched_fork_event, }, | ||
1457 | { "sched:sched_process_exit", process_sched_exit_event, }, | ||
1458 | { "sched:sched_migrate_task", process_sched_migrate_task_event, }, | 1446 | { "sched:sched_migrate_task", process_sched_migrate_task_event, }, |
1459 | }; | 1447 | }; |
1460 | struct perf_session *session; | 1448 | struct perf_session *session; |
@@ -1480,11 +1468,10 @@ static int perf_sched__read_events(struct perf_sched *sched, bool destroy, | |||
1480 | sched->nr_lost_chunks = session->stats.nr_events[PERF_RECORD_LOST]; | 1468 | sched->nr_lost_chunks = session->stats.nr_events[PERF_RECORD_LOST]; |
1481 | } | 1469 | } |
1482 | 1470 | ||
1483 | if (destroy) | ||
1484 | perf_session__delete(session); | ||
1485 | |||
1486 | if (psession) | 1471 | if (psession) |
1487 | *psession = session; | 1472 | *psession = session; |
1473 | else | ||
1474 | perf_session__delete(session); | ||
1488 | 1475 | ||
1489 | return 0; | 1476 | return 0; |
1490 | 1477 | ||
@@ -1529,8 +1516,11 @@ static int perf_sched__lat(struct perf_sched *sched) | |||
1529 | struct perf_session *session; | 1516 | struct perf_session *session; |
1530 | 1517 | ||
1531 | setup_pager(); | 1518 | setup_pager(); |
1532 | if (perf_sched__read_events(sched, false, &session)) | 1519 | |
1520 | /* save session -- references to threads are held in work_list */ | ||
1521 | if (perf_sched__read_events(sched, &session)) | ||
1533 | return -1; | 1522 | return -1; |
1523 | |||
1534 | perf_sched__sort_lat(sched); | 1524 | perf_sched__sort_lat(sched); |
1535 | 1525 | ||
1536 | printf("\n ---------------------------------------------------------------------------------------------------------------\n"); | 1526 | printf("\n ---------------------------------------------------------------------------------------------------------------\n"); |
@@ -1565,7 +1555,7 @@ static int perf_sched__map(struct perf_sched *sched) | |||
1565 | sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF); | 1555 | sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF); |
1566 | 1556 | ||
1567 | setup_pager(); | 1557 | setup_pager(); |
1568 | if (perf_sched__read_events(sched, true, NULL)) | 1558 | if (perf_sched__read_events(sched, NULL)) |
1569 | return -1; | 1559 | return -1; |
1570 | print_bad_events(sched); | 1560 | print_bad_events(sched); |
1571 | return 0; | 1561 | return 0; |
@@ -1580,7 +1570,7 @@ static int perf_sched__replay(struct perf_sched *sched) | |||
1580 | 1570 | ||
1581 | test_calibrations(sched); | 1571 | test_calibrations(sched); |
1582 | 1572 | ||
1583 | if (perf_sched__read_events(sched, true, NULL)) | 1573 | if (perf_sched__read_events(sched, NULL)) |
1584 | return -1; | 1574 | return -1; |
1585 | 1575 | ||
1586 | printf("nr_run_events: %ld\n", sched->nr_run_events); | 1576 | printf("nr_run_events: %ld\n", sched->nr_run_events); |
@@ -1639,7 +1629,6 @@ static int __cmd_record(int argc, const char **argv) | |||
1639 | "-e", "sched:sched_stat_sleep", | 1629 | "-e", "sched:sched_stat_sleep", |
1640 | "-e", "sched:sched_stat_iowait", | 1630 | "-e", "sched:sched_stat_iowait", |
1641 | "-e", "sched:sched_stat_runtime", | 1631 | "-e", "sched:sched_stat_runtime", |
1642 | "-e", "sched:sched_process_exit", | ||
1643 | "-e", "sched:sched_process_fork", | 1632 | "-e", "sched:sched_process_fork", |
1644 | "-e", "sched:sched_wakeup", | 1633 | "-e", "sched:sched_wakeup", |
1645 | "-e", "sched:sched_migrate_task", | 1634 | "-e", "sched:sched_migrate_task", |
@@ -1662,28 +1651,29 @@ static int __cmd_record(int argc, const char **argv) | |||
1662 | return cmd_record(i, rec_argv, NULL); | 1651 | return cmd_record(i, rec_argv, NULL); |
1663 | } | 1652 | } |
1664 | 1653 | ||
1654 | static const char default_sort_order[] = "avg, max, switch, runtime"; | ||
1655 | static struct perf_sched sched = { | ||
1656 | .tool = { | ||
1657 | .sample = perf_sched__process_tracepoint_sample, | ||
1658 | .comm = perf_event__process_comm, | ||
1659 | .lost = perf_event__process_lost, | ||
1660 | .fork = perf_sched__process_fork_event, | ||
1661 | .ordered_samples = true, | ||
1662 | }, | ||
1663 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), | ||
1664 | .sort_list = LIST_HEAD_INIT(sched.sort_list), | ||
1665 | .start_work_mutex = PTHREAD_MUTEX_INITIALIZER, | ||
1666 | .work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER, | ||
1667 | .curr_pid = { [0 ... MAX_CPUS - 1] = -1 }, | ||
1668 | .sort_order = default_sort_order, | ||
1669 | .replay_repeat = 10, | ||
1670 | .profile_cpu = -1, | ||
1671 | .next_shortname1 = 'A', | ||
1672 | .next_shortname2 = '0', | ||
1673 | }; | ||
1674 | |||
1665 | int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | 1675 | int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) |
1666 | { | 1676 | { |
1667 | const char default_sort_order[] = "avg, max, switch, runtime"; | ||
1668 | struct perf_sched sched = { | ||
1669 | .tool = { | ||
1670 | .sample = perf_sched__process_tracepoint_sample, | ||
1671 | .comm = perf_event__process_comm, | ||
1672 | .lost = perf_event__process_lost, | ||
1673 | .fork = perf_event__process_fork, | ||
1674 | .ordered_samples = true, | ||
1675 | }, | ||
1676 | .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid), | ||
1677 | .sort_list = LIST_HEAD_INIT(sched.sort_list), | ||
1678 | .start_work_mutex = PTHREAD_MUTEX_INITIALIZER, | ||
1679 | .work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER, | ||
1680 | .curr_pid = { [0 ... MAX_CPUS - 1] = -1 }, | ||
1681 | .sort_order = default_sort_order, | ||
1682 | .replay_repeat = 10, | ||
1683 | .profile_cpu = -1, | ||
1684 | .next_shortname1 = 'A', | ||
1685 | .next_shortname2 = '0', | ||
1686 | }; | ||
1687 | const struct option latency_options[] = { | 1677 | const struct option latency_options[] = { |
1688 | OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]", | 1678 | OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]", |
1689 | "sort by key(s): runtime, switch, avg, max"), | 1679 | "sort by key(s): runtime, switch, avg, max"), |
@@ -1729,7 +1719,6 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1729 | .wakeup_event = latency_wakeup_event, | 1719 | .wakeup_event = latency_wakeup_event, |
1730 | .switch_event = latency_switch_event, | 1720 | .switch_event = latency_switch_event, |
1731 | .runtime_event = latency_runtime_event, | 1721 | .runtime_event = latency_runtime_event, |
1732 | .fork_event = latency_fork_event, | ||
1733 | .migrate_task_event = latency_migrate_task_event, | 1722 | .migrate_task_event = latency_migrate_task_event, |
1734 | }; | 1723 | }; |
1735 | struct trace_sched_handler map_ops = { | 1724 | struct trace_sched_handler map_ops = { |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 92d4658f56fb..9c333ff3dfeb 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -24,6 +24,7 @@ static u64 last_timestamp; | |||
24 | static u64 nr_unordered; | 24 | static u64 nr_unordered; |
25 | extern const struct option record_options[]; | 25 | extern const struct option record_options[]; |
26 | static bool no_callchain; | 26 | static bool no_callchain; |
27 | static bool latency_format; | ||
27 | static bool system_wide; | 28 | static bool system_wide; |
28 | static const char *cpu_list; | 29 | static const char *cpu_list; |
29 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 30 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
@@ -65,6 +66,7 @@ struct output_option { | |||
65 | static struct { | 66 | static struct { |
66 | bool user_set; | 67 | bool user_set; |
67 | bool wildcard_set; | 68 | bool wildcard_set; |
69 | unsigned int print_ip_opts; | ||
68 | u64 fields; | 70 | u64 fields; |
69 | u64 invalid_fields; | 71 | u64 invalid_fields; |
70 | } output[PERF_TYPE_MAX] = { | 72 | } output[PERF_TYPE_MAX] = { |
@@ -234,6 +236,7 @@ static int perf_session__check_output_opt(struct perf_session *session) | |||
234 | { | 236 | { |
235 | int j; | 237 | int j; |
236 | struct perf_evsel *evsel; | 238 | struct perf_evsel *evsel; |
239 | struct perf_event_attr *attr; | ||
237 | 240 | ||
238 | for (j = 0; j < PERF_TYPE_MAX; ++j) { | 241 | for (j = 0; j < PERF_TYPE_MAX; ++j) { |
239 | evsel = perf_session__find_first_evtype(session, j); | 242 | evsel = perf_session__find_first_evtype(session, j); |
@@ -252,6 +255,24 @@ static int perf_session__check_output_opt(struct perf_session *session) | |||
252 | if (evsel && output[j].fields && | 255 | if (evsel && output[j].fields && |
253 | perf_evsel__check_attr(evsel, session)) | 256 | perf_evsel__check_attr(evsel, session)) |
254 | return -1; | 257 | return -1; |
258 | |||
259 | if (evsel == NULL) | ||
260 | continue; | ||
261 | |||
262 | attr = &evsel->attr; | ||
263 | |||
264 | output[j].print_ip_opts = 0; | ||
265 | if (PRINT_FIELD(IP)) | ||
266 | output[j].print_ip_opts |= PRINT_IP_OPT_IP; | ||
267 | |||
268 | if (PRINT_FIELD(SYM)) | ||
269 | output[j].print_ip_opts |= PRINT_IP_OPT_SYM; | ||
270 | |||
271 | if (PRINT_FIELD(DSO)) | ||
272 | output[j].print_ip_opts |= PRINT_IP_OPT_DSO; | ||
273 | |||
274 | if (PRINT_FIELD(SYMOFFSET)) | ||
275 | output[j].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET; | ||
255 | } | 276 | } |
256 | 277 | ||
257 | return 0; | 278 | return 0; |
@@ -381,8 +402,8 @@ static void print_sample_bts(union perf_event *event, | |||
381 | else | 402 | else |
382 | printf("\n"); | 403 | printf("\n"); |
383 | perf_evsel__print_ip(evsel, event, sample, machine, | 404 | perf_evsel__print_ip(evsel, event, sample, machine, |
384 | PRINT_FIELD(SYM), PRINT_FIELD(DSO), | 405 | output[attr->type].print_ip_opts, |
385 | PRINT_FIELD(SYMOFFSET)); | 406 | PERF_MAX_STACK_DEPTH); |
386 | } | 407 | } |
387 | 408 | ||
388 | printf(" => "); | 409 | printf(" => "); |
@@ -396,10 +417,10 @@ static void print_sample_bts(union perf_event *event, | |||
396 | 417 | ||
397 | static void process_event(union perf_event *event, struct perf_sample *sample, | 418 | static void process_event(union perf_event *event, struct perf_sample *sample, |
398 | struct perf_evsel *evsel, struct machine *machine, | 419 | struct perf_evsel *evsel, struct machine *machine, |
399 | struct addr_location *al) | 420 | struct thread *thread, |
421 | struct addr_location *al __maybe_unused) | ||
400 | { | 422 | { |
401 | struct perf_event_attr *attr = &evsel->attr; | 423 | struct perf_event_attr *attr = &evsel->attr; |
402 | struct thread *thread = al->thread; | ||
403 | 424 | ||
404 | if (output[attr->type].fields == 0) | 425 | if (output[attr->type].fields == 0) |
405 | return; | 426 | return; |
@@ -422,9 +443,10 @@ static void process_event(union perf_event *event, struct perf_sample *sample, | |||
422 | printf(" "); | 443 | printf(" "); |
423 | else | 444 | else |
424 | printf("\n"); | 445 | printf("\n"); |
446 | |||
425 | perf_evsel__print_ip(evsel, event, sample, machine, | 447 | perf_evsel__print_ip(evsel, event, sample, machine, |
426 | PRINT_FIELD(SYM), PRINT_FIELD(DSO), | 448 | output[attr->type].print_ip_opts, |
427 | PRINT_FIELD(SYMOFFSET)); | 449 | PERF_MAX_STACK_DEPTH); |
428 | } | 450 | } |
429 | 451 | ||
430 | printf("\n"); | 452 | printf("\n"); |
@@ -479,7 +501,8 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
479 | struct machine *machine) | 501 | struct machine *machine) |
480 | { | 502 | { |
481 | struct addr_location al; | 503 | struct addr_location al; |
482 | struct thread *thread = machine__findnew_thread(machine, event->ip.tid); | 504 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
505 | sample->tid); | ||
483 | 506 | ||
484 | if (thread == NULL) { | 507 | if (thread == NULL) { |
485 | pr_debug("problem processing %d event, skipping it.\n", | 508 | pr_debug("problem processing %d event, skipping it.\n", |
@@ -498,7 +521,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
498 | return 0; | 521 | return 0; |
499 | } | 522 | } |
500 | 523 | ||
501 | if (perf_event__preprocess_sample(event, machine, &al, sample, 0) < 0) { | 524 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { |
502 | pr_err("problem processing %d event, skipping it.\n", | 525 | pr_err("problem processing %d event, skipping it.\n", |
503 | event->header.type); | 526 | event->header.type); |
504 | return -1; | 527 | return -1; |
@@ -510,7 +533,7 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
510 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) | 533 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) |
511 | return 0; | 534 | return 0; |
512 | 535 | ||
513 | scripting_ops->process_event(event, sample, evsel, machine, &al); | 536 | scripting_ops->process_event(event, sample, evsel, machine, thread, &al); |
514 | 537 | ||
515 | evsel->hists.stats.total_period += sample->period; | 538 | evsel->hists.stats.total_period += sample->period; |
516 | return 0; | 539 | return 0; |
@@ -519,19 +542,17 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
519 | static struct perf_tool perf_script = { | 542 | static struct perf_tool perf_script = { |
520 | .sample = process_sample_event, | 543 | .sample = process_sample_event, |
521 | .mmap = perf_event__process_mmap, | 544 | .mmap = perf_event__process_mmap, |
545 | .mmap2 = perf_event__process_mmap2, | ||
522 | .comm = perf_event__process_comm, | 546 | .comm = perf_event__process_comm, |
523 | .exit = perf_event__process_exit, | 547 | .exit = perf_event__process_exit, |
524 | .fork = perf_event__process_fork, | 548 | .fork = perf_event__process_fork, |
525 | .attr = perf_event__process_attr, | 549 | .attr = perf_event__process_attr, |
526 | .event_type = perf_event__process_event_type, | ||
527 | .tracing_data = perf_event__process_tracing_data, | 550 | .tracing_data = perf_event__process_tracing_data, |
528 | .build_id = perf_event__process_build_id, | 551 | .build_id = perf_event__process_build_id, |
529 | .ordered_samples = true, | 552 | .ordered_samples = true, |
530 | .ordering_requires_timestamps = true, | 553 | .ordering_requires_timestamps = true, |
531 | }; | 554 | }; |
532 | 555 | ||
533 | extern volatile int session_done; | ||
534 | |||
535 | static void sig_handler(int sig __maybe_unused) | 556 | static void sig_handler(int sig __maybe_unused) |
536 | { | 557 | { |
537 | session_done = 1; | 558 | session_done = 1; |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 352fbd7ff4a1..5098f144b92d 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -100,6 +100,7 @@ static const char *pre_cmd = NULL; | |||
100 | static const char *post_cmd = NULL; | 100 | static const char *post_cmd = NULL; |
101 | static bool sync_run = false; | 101 | static bool sync_run = false; |
102 | static unsigned int interval = 0; | 102 | static unsigned int interval = 0; |
103 | static unsigned int initial_delay = 0; | ||
103 | static bool forever = false; | 104 | static bool forever = false; |
104 | static struct timespec ref_time; | 105 | static struct timespec ref_time; |
105 | static struct cpu_map *aggr_map; | 106 | static struct cpu_map *aggr_map; |
@@ -254,7 +255,8 @@ static int create_perf_stat_counter(struct perf_evsel *evsel) | |||
254 | if (!perf_target__has_task(&target) && | 255 | if (!perf_target__has_task(&target) && |
255 | perf_evsel__is_group_leader(evsel)) { | 256 | perf_evsel__is_group_leader(evsel)) { |
256 | attr->disabled = 1; | 257 | attr->disabled = 1; |
257 | attr->enable_on_exec = 1; | 258 | if (!initial_delay) |
259 | attr->enable_on_exec = 1; | ||
258 | } | 260 | } |
259 | 261 | ||
260 | return perf_evsel__open_per_thread(evsel, evsel_list->threads); | 262 | return perf_evsel__open_per_thread(evsel, evsel_list->threads); |
@@ -414,6 +416,22 @@ static void print_interval(void) | |||
414 | list_for_each_entry(counter, &evsel_list->entries, node) | 416 | list_for_each_entry(counter, &evsel_list->entries, node) |
415 | print_counter_aggr(counter, prefix); | 417 | print_counter_aggr(counter, prefix); |
416 | } | 418 | } |
419 | |||
420 | fflush(output); | ||
421 | } | ||
422 | |||
423 | static void handle_initial_delay(void) | ||
424 | { | ||
425 | struct perf_evsel *counter; | ||
426 | |||
427 | if (initial_delay) { | ||
428 | const int ncpus = cpu_map__nr(evsel_list->cpus), | ||
429 | nthreads = thread_map__nr(evsel_list->threads); | ||
430 | |||
431 | usleep(initial_delay * 1000); | ||
432 | list_for_each_entry(counter, &evsel_list->entries, node) | ||
433 | perf_evsel__enable(counter, ncpus, nthreads); | ||
434 | } | ||
417 | } | 435 | } |
418 | 436 | ||
419 | static int __run_perf_stat(int argc, const char **argv) | 437 | static int __run_perf_stat(int argc, const char **argv) |
@@ -439,6 +457,7 @@ static int __run_perf_stat(int argc, const char **argv) | |||
439 | perror("failed to prepare workload"); | 457 | perror("failed to prepare workload"); |
440 | return -1; | 458 | return -1; |
441 | } | 459 | } |
460 | child_pid = evsel_list->workload.pid; | ||
442 | } | 461 | } |
443 | 462 | ||
444 | if (group) | 463 | if (group) |
@@ -486,6 +505,7 @@ static int __run_perf_stat(int argc, const char **argv) | |||
486 | 505 | ||
487 | if (forks) { | 506 | if (forks) { |
488 | perf_evlist__start_workload(evsel_list); | 507 | perf_evlist__start_workload(evsel_list); |
508 | handle_initial_delay(); | ||
489 | 509 | ||
490 | if (interval) { | 510 | if (interval) { |
491 | while (!waitpid(child_pid, &status, WNOHANG)) { | 511 | while (!waitpid(child_pid, &status, WNOHANG)) { |
@@ -497,6 +517,7 @@ static int __run_perf_stat(int argc, const char **argv) | |||
497 | if (WIFSIGNALED(status)) | 517 | if (WIFSIGNALED(status)) |
498 | psignal(WTERMSIG(status), argv[0]); | 518 | psignal(WTERMSIG(status), argv[0]); |
499 | } else { | 519 | } else { |
520 | handle_initial_delay(); | ||
500 | while (!done) { | 521 | while (!done) { |
501 | nanosleep(&ts, NULL); | 522 | nanosleep(&ts, NULL); |
502 | if (interval) | 523 | if (interval) |
@@ -1419,6 +1440,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1419 | "aggregate counts per processor socket", AGGR_SOCKET), | 1440 | "aggregate counts per processor socket", AGGR_SOCKET), |
1420 | OPT_SET_UINT(0, "per-core", &aggr_mode, | 1441 | OPT_SET_UINT(0, "per-core", &aggr_mode, |
1421 | "aggregate counts per physical processor core", AGGR_CORE), | 1442 | "aggregate counts per physical processor core", AGGR_CORE), |
1443 | OPT_UINTEGER('D', "delay", &initial_delay, | ||
1444 | "ms to wait before starting measurement after program start"), | ||
1422 | OPT_END() | 1445 | OPT_END() |
1423 | }; | 1446 | }; |
1424 | const char * const stat_usage[] = { | 1447 | const char * const stat_usage[] = { |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 4536a92b18f3..c2e02319347a 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
@@ -12,6 +12,8 @@ | |||
12 | * of the License. | 12 | * of the License. |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <traceevent/event-parse.h> | ||
16 | |||
15 | #include "builtin.h" | 17 | #include "builtin.h" |
16 | 18 | ||
17 | #include "util/util.h" | 19 | #include "util/util.h" |
@@ -19,6 +21,7 @@ | |||
19 | #include "util/color.h" | 21 | #include "util/color.h" |
20 | #include <linux/list.h> | 22 | #include <linux/list.h> |
21 | #include "util/cache.h" | 23 | #include "util/cache.h" |
24 | #include "util/evlist.h" | ||
22 | #include "util/evsel.h" | 25 | #include "util/evsel.h" |
23 | #include <linux/rbtree.h> | 26 | #include <linux/rbtree.h> |
24 | #include "util/symbol.h" | 27 | #include "util/symbol.h" |
@@ -328,25 +331,6 @@ struct wakeup_entry { | |||
328 | int success; | 331 | int success; |
329 | }; | 332 | }; |
330 | 333 | ||
331 | /* | ||
332 | * trace_flag_type is an enumeration that holds different | ||
333 | * states when a trace occurs. These are: | ||
334 | * IRQS_OFF - interrupts were disabled | ||
335 | * IRQS_NOSUPPORT - arch does not support irqs_disabled_flags | ||
336 | * NEED_RESCED - reschedule is requested | ||
337 | * HARDIRQ - inside an interrupt handler | ||
338 | * SOFTIRQ - inside a softirq handler | ||
339 | */ | ||
340 | enum trace_flag_type { | ||
341 | TRACE_FLAG_IRQS_OFF = 0x01, | ||
342 | TRACE_FLAG_IRQS_NOSUPPORT = 0x02, | ||
343 | TRACE_FLAG_NEED_RESCHED = 0x04, | ||
344 | TRACE_FLAG_HARDIRQ = 0x08, | ||
345 | TRACE_FLAG_SOFTIRQ = 0x10, | ||
346 | }; | ||
347 | |||
348 | |||
349 | |||
350 | struct sched_switch { | 334 | struct sched_switch { |
351 | struct trace_entry te; | 335 | struct trace_entry te; |
352 | char prev_comm[TASK_COMM_LEN]; | 336 | char prev_comm[TASK_COMM_LEN]; |
@@ -479,6 +463,8 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te) | |||
479 | } | 463 | } |
480 | } | 464 | } |
481 | 465 | ||
466 | typedef int (*tracepoint_handler)(struct perf_evsel *evsel, | ||
467 | struct perf_sample *sample); | ||
482 | 468 | ||
483 | static int process_sample_event(struct perf_tool *tool __maybe_unused, | 469 | static int process_sample_event(struct perf_tool *tool __maybe_unused, |
484 | union perf_event *event __maybe_unused, | 470 | union perf_event *event __maybe_unused, |
@@ -486,8 +472,6 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
486 | struct perf_evsel *evsel, | 472 | struct perf_evsel *evsel, |
487 | struct machine *machine __maybe_unused) | 473 | struct machine *machine __maybe_unused) |
488 | { | 474 | { |
489 | struct trace_entry *te; | ||
490 | |||
491 | if (evsel->attr.sample_type & PERF_SAMPLE_TIME) { | 475 | if (evsel->attr.sample_type & PERF_SAMPLE_TIME) { |
492 | if (!first_time || first_time > sample->time) | 476 | if (!first_time || first_time > sample->time) |
493 | first_time = sample->time; | 477 | first_time = sample->time; |
@@ -495,69 +479,90 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused, | |||
495 | last_time = sample->time; | 479 | last_time = sample->time; |
496 | } | 480 | } |
497 | 481 | ||
498 | te = (void *)sample->raw_data; | 482 | if (sample->cpu > numcpus) |
499 | if ((evsel->attr.sample_type & PERF_SAMPLE_RAW) && sample->raw_size > 0) { | 483 | numcpus = sample->cpu; |
500 | char *event_str; | 484 | |
501 | #ifdef SUPPORT_OLD_POWER_EVENTS | 485 | if (evsel->handler.func != NULL) { |
502 | struct power_entry_old *peo; | 486 | tracepoint_handler f = evsel->handler.func; |
503 | peo = (void *)te; | 487 | return f(evsel, sample); |
504 | #endif | 488 | } |
505 | /* | 489 | |
506 | * FIXME: use evsel, its already mapped from id to perf_evsel, | 490 | return 0; |
507 | * remove perf_header__find_event infrastructure bits. | 491 | } |
508 | * Mapping all these "power:cpu_idle" strings to the tracepoint | 492 | |
509 | * ID and then just comparing against evsel->attr.config. | 493 | static int |
510 | * | 494 | process_sample_cpu_idle(struct perf_evsel *evsel __maybe_unused, |
511 | * e.g.: | 495 | struct perf_sample *sample) |
512 | * | 496 | { |
513 | * if (evsel->attr.config == power_cpu_idle_id) | 497 | struct power_processor_entry *ppe = sample->raw_data; |
514 | */ | 498 | |
515 | event_str = perf_header__find_event(te->type); | 499 | if (ppe->state == (u32) PWR_EVENT_EXIT) |
516 | 500 | c_state_end(ppe->cpu_id, sample->time); | |
517 | if (!event_str) | 501 | else |
518 | return 0; | 502 | c_state_start(ppe->cpu_id, sample->time, ppe->state); |
519 | 503 | return 0; | |
520 | if (sample->cpu > numcpus) | 504 | } |
521 | numcpus = sample->cpu; | 505 | |
522 | 506 | static int | |
523 | if (strcmp(event_str, "power:cpu_idle") == 0) { | 507 | process_sample_cpu_frequency(struct perf_evsel *evsel __maybe_unused, |
524 | struct power_processor_entry *ppe = (void *)te; | 508 | struct perf_sample *sample) |
525 | if (ppe->state == (u32)PWR_EVENT_EXIT) | 509 | { |
526 | c_state_end(ppe->cpu_id, sample->time); | 510 | struct power_processor_entry *ppe = sample->raw_data; |
527 | else | 511 | |
528 | c_state_start(ppe->cpu_id, sample->time, | 512 | p_state_change(ppe->cpu_id, sample->time, ppe->state); |
529 | ppe->state); | 513 | return 0; |
530 | } | 514 | } |
531 | else if (strcmp(event_str, "power:cpu_frequency") == 0) { | 515 | |
532 | struct power_processor_entry *ppe = (void *)te; | 516 | static int |
533 | p_state_change(ppe->cpu_id, sample->time, ppe->state); | 517 | process_sample_sched_wakeup(struct perf_evsel *evsel __maybe_unused, |
534 | } | 518 | struct perf_sample *sample) |
519 | { | ||
520 | struct trace_entry *te = sample->raw_data; | ||
521 | |||
522 | sched_wakeup(sample->cpu, sample->time, sample->pid, te); | ||
523 | return 0; | ||
524 | } | ||
535 | 525 | ||
536 | else if (strcmp(event_str, "sched:sched_wakeup") == 0) | 526 | static int |
537 | sched_wakeup(sample->cpu, sample->time, sample->pid, te); | 527 | process_sample_sched_switch(struct perf_evsel *evsel __maybe_unused, |
528 | struct perf_sample *sample) | ||
529 | { | ||
530 | struct trace_entry *te = sample->raw_data; | ||
538 | 531 | ||
539 | else if (strcmp(event_str, "sched:sched_switch") == 0) | 532 | sched_switch(sample->cpu, sample->time, te); |
540 | sched_switch(sample->cpu, sample->time, te); | 533 | return 0; |
534 | } | ||
541 | 535 | ||
542 | #ifdef SUPPORT_OLD_POWER_EVENTS | 536 | #ifdef SUPPORT_OLD_POWER_EVENTS |
543 | if (use_old_power_events) { | 537 | static int |
544 | if (strcmp(event_str, "power:power_start") == 0) | 538 | process_sample_power_start(struct perf_evsel *evsel __maybe_unused, |
545 | c_state_start(peo->cpu_id, sample->time, | 539 | struct perf_sample *sample) |
546 | peo->value); | 540 | { |
547 | 541 | struct power_entry_old *peo = sample->raw_data; | |
548 | else if (strcmp(event_str, "power:power_end") == 0) | 542 | |
549 | c_state_end(sample->cpu, sample->time); | 543 | c_state_start(peo->cpu_id, sample->time, peo->value); |
550 | |||
551 | else if (strcmp(event_str, | ||
552 | "power:power_frequency") == 0) | ||
553 | p_state_change(peo->cpu_id, sample->time, | ||
554 | peo->value); | ||
555 | } | ||
556 | #endif | ||
557 | } | ||
558 | return 0; | 544 | return 0; |
559 | } | 545 | } |
560 | 546 | ||
547 | static int | ||
548 | process_sample_power_end(struct perf_evsel *evsel __maybe_unused, | ||
549 | struct perf_sample *sample) | ||
550 | { | ||
551 | c_state_end(sample->cpu, sample->time); | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static int | ||
556 | process_sample_power_frequency(struct perf_evsel *evsel __maybe_unused, | ||
557 | struct perf_sample *sample) | ||
558 | { | ||
559 | struct power_entry_old *peo = sample->raw_data; | ||
560 | |||
561 | p_state_change(peo->cpu_id, sample->time, peo->value); | ||
562 | return 0; | ||
563 | } | ||
564 | #endif /* SUPPORT_OLD_POWER_EVENTS */ | ||
565 | |||
561 | /* | 566 | /* |
562 | * After the last sample we need to wrap up the current C/P state | 567 | * After the last sample we need to wrap up the current C/P state |
563 | * and close out each CPU for these. | 568 | * and close out each CPU for these. |
@@ -974,6 +979,17 @@ static int __cmd_timechart(const char *output_name) | |||
974 | .sample = process_sample_event, | 979 | .sample = process_sample_event, |
975 | .ordered_samples = true, | 980 | .ordered_samples = true, |
976 | }; | 981 | }; |
982 | const struct perf_evsel_str_handler power_tracepoints[] = { | ||
983 | { "power:cpu_idle", process_sample_cpu_idle }, | ||
984 | { "power:cpu_frequency", process_sample_cpu_frequency }, | ||
985 | { "sched:sched_wakeup", process_sample_sched_wakeup }, | ||
986 | { "sched:sched_switch", process_sample_sched_switch }, | ||
987 | #ifdef SUPPORT_OLD_POWER_EVENTS | ||
988 | { "power:power_start", process_sample_power_start }, | ||
989 | { "power:power_end", process_sample_power_end }, | ||
990 | { "power:power_frequency", process_sample_power_frequency }, | ||
991 | #endif | ||
992 | }; | ||
977 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, | 993 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, |
978 | 0, false, &perf_timechart); | 994 | 0, false, &perf_timechart); |
979 | int ret = -EINVAL; | 995 | int ret = -EINVAL; |
@@ -984,6 +1000,12 @@ static int __cmd_timechart(const char *output_name) | |||
984 | if (!perf_session__has_traces(session, "timechart record")) | 1000 | if (!perf_session__has_traces(session, "timechart record")) |
985 | goto out_delete; | 1001 | goto out_delete; |
986 | 1002 | ||
1003 | if (perf_session__set_tracepoints_handlers(session, | ||
1004 | power_tracepoints)) { | ||
1005 | pr_err("Initializing session tracepoint handlers failed\n"); | ||
1006 | goto out_delete; | ||
1007 | } | ||
1008 | |||
987 | ret = perf_session__process_events(session, &perf_timechart); | 1009 | ret = perf_session__process_events(session, &perf_timechart); |
988 | if (ret) | 1010 | if (ret) |
989 | goto out_delete; | 1011 | goto out_delete; |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index e06c4f869330..5a11f13e56f9 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include "util/xyarray.h" | 40 | #include "util/xyarray.h" |
41 | #include "util/sort.h" | 41 | #include "util/sort.h" |
42 | #include "util/intlist.h" | 42 | #include "util/intlist.h" |
43 | #include "arch/common.h" | ||
43 | 44 | ||
44 | #include "util/debug.h" | 45 | #include "util/debug.h" |
45 | 46 | ||
@@ -102,7 +103,8 @@ static int perf_top__parse_source(struct perf_top *top, struct hist_entry *he) | |||
102 | /* | 103 | /* |
103 | * We can't annotate with just /proc/kallsyms | 104 | * We can't annotate with just /proc/kallsyms |
104 | */ | 105 | */ |
105 | if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) { | 106 | if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && |
107 | !dso__is_kcore(map->dso)) { | ||
106 | pr_err("Can't annotate %s: No vmlinux file was found in the " | 108 | pr_err("Can't annotate %s: No vmlinux file was found in the " |
107 | "path\n", sym->name); | 109 | "path\n", sym->name); |
108 | sleep(1); | 110 | sleep(1); |
@@ -237,8 +239,6 @@ out_unlock: | |||
237 | pthread_mutex_unlock(¬es->lock); | 239 | pthread_mutex_unlock(¬es->lock); |
238 | } | 240 | } |
239 | 241 | ||
240 | static const char CONSOLE_CLEAR[] = "[H[2J"; | ||
241 | |||
242 | static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, | 242 | static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, |
243 | struct addr_location *al, | 243 | struct addr_location *al, |
244 | struct perf_sample *sample) | 244 | struct perf_sample *sample) |
@@ -689,7 +689,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
689 | { | 689 | { |
690 | struct perf_top *top = container_of(tool, struct perf_top, tool); | 690 | struct perf_top *top = container_of(tool, struct perf_top, tool); |
691 | struct symbol *parent = NULL; | 691 | struct symbol *parent = NULL; |
692 | u64 ip = event->ip.ip; | 692 | u64 ip = sample->ip; |
693 | struct addr_location al; | 693 | struct addr_location al; |
694 | int err; | 694 | int err; |
695 | 695 | ||
@@ -699,10 +699,10 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
699 | if (!seen) | 699 | if (!seen) |
700 | seen = intlist__new(NULL); | 700 | seen = intlist__new(NULL); |
701 | 701 | ||
702 | if (!intlist__has_entry(seen, event->ip.pid)) { | 702 | if (!intlist__has_entry(seen, sample->pid)) { |
703 | pr_err("Can't find guest [%d]'s kernel information\n", | 703 | pr_err("Can't find guest [%d]'s kernel information\n", |
704 | event->ip.pid); | 704 | sample->pid); |
705 | intlist__add(seen, event->ip.pid); | 705 | intlist__add(seen, sample->pid); |
706 | } | 706 | } |
707 | return; | 707 | return; |
708 | } | 708 | } |
@@ -716,8 +716,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
716 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) | 716 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) |
717 | top->exact_samples++; | 717 | top->exact_samples++; |
718 | 718 | ||
719 | if (perf_event__preprocess_sample(event, machine, &al, sample, | 719 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0 || |
720 | symbol_filter) < 0 || | ||
721 | al.filtered) | 720 | al.filtered) |
722 | return; | 721 | return; |
723 | 722 | ||
@@ -772,8 +771,7 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
772 | sample->callchain) { | 771 | sample->callchain) { |
773 | err = machine__resolve_callchain(machine, evsel, | 772 | err = machine__resolve_callchain(machine, evsel, |
774 | al.thread, sample, | 773 | al.thread, sample, |
775 | &parent); | 774 | &parent, &al); |
776 | |||
777 | if (err) | 775 | if (err) |
778 | return; | 776 | return; |
779 | } | 777 | } |
@@ -812,7 +810,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
812 | ret = perf_evlist__parse_sample(top->evlist, event, &sample); | 810 | ret = perf_evlist__parse_sample(top->evlist, event, &sample); |
813 | if (ret) { | 811 | if (ret) { |
814 | pr_err("Can't parse sample, err = %d\n", ret); | 812 | pr_err("Can't parse sample, err = %d\n", ret); |
815 | continue; | 813 | goto next_event; |
816 | } | 814 | } |
817 | 815 | ||
818 | evsel = perf_evlist__id2evsel(session->evlist, sample.id); | 816 | evsel = perf_evlist__id2evsel(session->evlist, sample.id); |
@@ -827,18 +825,19 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
827 | case PERF_RECORD_MISC_USER: | 825 | case PERF_RECORD_MISC_USER: |
828 | ++top->us_samples; | 826 | ++top->us_samples; |
829 | if (top->hide_user_symbols) | 827 | if (top->hide_user_symbols) |
830 | continue; | 828 | goto next_event; |
831 | machine = &session->machines.host; | 829 | machine = &session->machines.host; |
832 | break; | 830 | break; |
833 | case PERF_RECORD_MISC_KERNEL: | 831 | case PERF_RECORD_MISC_KERNEL: |
834 | ++top->kernel_samples; | 832 | ++top->kernel_samples; |
835 | if (top->hide_kernel_symbols) | 833 | if (top->hide_kernel_symbols) |
836 | continue; | 834 | goto next_event; |
837 | machine = &session->machines.host; | 835 | machine = &session->machines.host; |
838 | break; | 836 | break; |
839 | case PERF_RECORD_MISC_GUEST_KERNEL: | 837 | case PERF_RECORD_MISC_GUEST_KERNEL: |
840 | ++top->guest_kernel_samples; | 838 | ++top->guest_kernel_samples; |
841 | machine = perf_session__find_machine(session, event->ip.pid); | 839 | machine = perf_session__find_machine(session, |
840 | sample.pid); | ||
842 | break; | 841 | break; |
843 | case PERF_RECORD_MISC_GUEST_USER: | 842 | case PERF_RECORD_MISC_GUEST_USER: |
844 | ++top->guest_us_samples; | 843 | ++top->guest_us_samples; |
@@ -848,7 +847,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
848 | */ | 847 | */ |
849 | /* Fall thru */ | 848 | /* Fall thru */ |
850 | default: | 849 | default: |
851 | continue; | 850 | goto next_event; |
852 | } | 851 | } |
853 | 852 | ||
854 | 853 | ||
@@ -860,6 +859,8 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx) | |||
860 | machine__process_event(machine, event); | 859 | machine__process_event(machine, event); |
861 | } else | 860 | } else |
862 | ++session->stats.nr_unknown_events; | 861 | ++session->stats.nr_unknown_events; |
862 | next_event: | ||
863 | perf_evlist__mmap_consume(top->evlist, idx); | ||
863 | } | 864 | } |
864 | } | 865 | } |
865 | 866 | ||
@@ -939,6 +940,14 @@ static int __cmd_top(struct perf_top *top) | |||
939 | if (top->session == NULL) | 940 | if (top->session == NULL) |
940 | return -ENOMEM; | 941 | return -ENOMEM; |
941 | 942 | ||
943 | machines__set_symbol_filter(&top->session->machines, symbol_filter); | ||
944 | |||
945 | if (!objdump_path) { | ||
946 | ret = perf_session_env__lookup_objdump(&top->session->header.env); | ||
947 | if (ret) | ||
948 | goto out_delete; | ||
949 | } | ||
950 | |||
942 | ret = perf_top__setup_sample_type(top); | 951 | ret = perf_top__setup_sample_type(top); |
943 | if (ret) | 952 | if (ret) |
944 | goto out_delete; | 953 | goto out_delete; |
@@ -1009,16 +1018,16 @@ out_delete: | |||
1009 | } | 1018 | } |
1010 | 1019 | ||
1011 | static int | 1020 | static int |
1012 | parse_callchain_opt(const struct option *opt, const char *arg, int unset) | 1021 | callchain_opt(const struct option *opt, const char *arg, int unset) |
1013 | { | 1022 | { |
1014 | /* | ||
1015 | * --no-call-graph | ||
1016 | */ | ||
1017 | if (unset) | ||
1018 | return 0; | ||
1019 | |||
1020 | symbol_conf.use_callchain = true; | 1023 | symbol_conf.use_callchain = true; |
1024 | return record_callchain_opt(opt, arg, unset); | ||
1025 | } | ||
1021 | 1026 | ||
1027 | static int | ||
1028 | parse_callchain_opt(const struct option *opt, const char *arg, int unset) | ||
1029 | { | ||
1030 | symbol_conf.use_callchain = true; | ||
1022 | return record_parse_callchain_opt(opt, arg, unset); | 1031 | return record_parse_callchain_opt(opt, arg, unset); |
1023 | } | 1032 | } |
1024 | 1033 | ||
@@ -1099,9 +1108,15 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1099 | "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight"), | 1108 | "sort by key(s): pid, comm, dso, symbol, parent, weight, local_weight"), |
1100 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, | 1109 | OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples, |
1101 | "Show a column with the number of samples"), | 1110 | "Show a column with the number of samples"), |
1102 | OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, | 1111 | OPT_CALLBACK_NOOPT('G', NULL, &top.record_opts, |
1103 | "mode[,dump_size]", record_callchain_help, | 1112 | NULL, "enables call-graph recording", |
1104 | &parse_callchain_opt, "fp"), | 1113 | &callchain_opt), |
1114 | OPT_CALLBACK(0, "call-graph", &top.record_opts, | ||
1115 | "mode[,dump_size]", record_callchain_help, | ||
1116 | &parse_callchain_opt), | ||
1117 | OPT_CALLBACK(0, "ignore-callees", NULL, "regex", | ||
1118 | "ignore callees of these functions in call graphs", | ||
1119 | report_parse_ignore_callees_opt), | ||
1105 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | 1120 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, |
1106 | "Show a column with the sum of periods"), | 1121 | "Show a column with the sum of periods"), |
1107 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 1122 | OPT_STRING(0, "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
@@ -1114,6 +1129,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1114 | "Interleave source code with assembly code (default)"), | 1129 | "Interleave source code with assembly code (default)"), |
1115 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, | 1130 | OPT_BOOLEAN(0, "asm-raw", &symbol_conf.annotate_asm_raw, |
1116 | "Display raw encoding of assembly instructions (default)"), | 1131 | "Display raw encoding of assembly instructions (default)"), |
1132 | OPT_STRING(0, "objdump", &objdump_path, "path", | ||
1133 | "objdump binary to use for disassembly and annotations"), | ||
1117 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 1134 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
1118 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 1135 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
1119 | OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), | 1136 | OPT_STRING('u', "uid", &target->uid_str, "user", "user to profile"), |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index ab3ed4af1466..99c8d9ad6729 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -1,34 +1,350 @@ | |||
1 | #include <traceevent/event-parse.h> | ||
1 | #include "builtin.h" | 2 | #include "builtin.h" |
2 | #include "util/color.h" | 3 | #include "util/color.h" |
4 | #include "util/debug.h" | ||
3 | #include "util/evlist.h" | 5 | #include "util/evlist.h" |
4 | #include "util/machine.h" | 6 | #include "util/machine.h" |
7 | #include "util/session.h" | ||
5 | #include "util/thread.h" | 8 | #include "util/thread.h" |
6 | #include "util/parse-options.h" | 9 | #include "util/parse-options.h" |
10 | #include "util/strlist.h" | ||
11 | #include "util/intlist.h" | ||
7 | #include "util/thread_map.h" | 12 | #include "util/thread_map.h" |
8 | #include "event-parse.h" | ||
9 | 13 | ||
10 | #include <libaudit.h> | 14 | #include <libaudit.h> |
11 | #include <stdlib.h> | 15 | #include <stdlib.h> |
16 | #include <sys/mman.h> | ||
17 | #include <linux/futex.h> | ||
18 | |||
19 | /* For older distros: */ | ||
20 | #ifndef MAP_STACK | ||
21 | # define MAP_STACK 0x20000 | ||
22 | #endif | ||
23 | |||
24 | #ifndef MADV_HWPOISON | ||
25 | # define MADV_HWPOISON 100 | ||
26 | #endif | ||
27 | |||
28 | #ifndef MADV_MERGEABLE | ||
29 | # define MADV_MERGEABLE 12 | ||
30 | #endif | ||
31 | |||
32 | #ifndef MADV_UNMERGEABLE | ||
33 | # define MADV_UNMERGEABLE 13 | ||
34 | #endif | ||
35 | |||
36 | static size_t syscall_arg__scnprintf_hex(char *bf, size_t size, | ||
37 | unsigned long arg, | ||
38 | u8 arg_idx __maybe_unused, | ||
39 | u8 *arg_mask __maybe_unused) | ||
40 | { | ||
41 | return scnprintf(bf, size, "%#lx", arg); | ||
42 | } | ||
43 | |||
44 | #define SCA_HEX syscall_arg__scnprintf_hex | ||
45 | |||
46 | static size_t syscall_arg__scnprintf_whence(char *bf, size_t size, | ||
47 | unsigned long arg, | ||
48 | u8 arg_idx __maybe_unused, | ||
49 | u8 *arg_mask __maybe_unused) | ||
50 | { | ||
51 | int whence = arg; | ||
52 | |||
53 | switch (whence) { | ||
54 | #define P_WHENCE(n) case SEEK_##n: return scnprintf(bf, size, #n) | ||
55 | P_WHENCE(SET); | ||
56 | P_WHENCE(CUR); | ||
57 | P_WHENCE(END); | ||
58 | #ifdef SEEK_DATA | ||
59 | P_WHENCE(DATA); | ||
60 | #endif | ||
61 | #ifdef SEEK_HOLE | ||
62 | P_WHENCE(HOLE); | ||
63 | #endif | ||
64 | #undef P_WHENCE | ||
65 | default: break; | ||
66 | } | ||
67 | |||
68 | return scnprintf(bf, size, "%#x", whence); | ||
69 | } | ||
70 | |||
71 | #define SCA_WHENCE syscall_arg__scnprintf_whence | ||
72 | |||
73 | static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size, | ||
74 | unsigned long arg, | ||
75 | u8 arg_idx __maybe_unused, | ||
76 | u8 *arg_mask __maybe_unused) | ||
77 | { | ||
78 | int printed = 0, prot = arg; | ||
79 | |||
80 | if (prot == PROT_NONE) | ||
81 | return scnprintf(bf, size, "NONE"); | ||
82 | #define P_MMAP_PROT(n) \ | ||
83 | if (prot & PROT_##n) { \ | ||
84 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | ||
85 | prot &= ~PROT_##n; \ | ||
86 | } | ||
87 | |||
88 | P_MMAP_PROT(EXEC); | ||
89 | P_MMAP_PROT(READ); | ||
90 | P_MMAP_PROT(WRITE); | ||
91 | #ifdef PROT_SEM | ||
92 | P_MMAP_PROT(SEM); | ||
93 | #endif | ||
94 | P_MMAP_PROT(GROWSDOWN); | ||
95 | P_MMAP_PROT(GROWSUP); | ||
96 | #undef P_MMAP_PROT | ||
97 | |||
98 | if (prot) | ||
99 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", prot); | ||
100 | |||
101 | return printed; | ||
102 | } | ||
103 | |||
104 | #define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot | ||
105 | |||
106 | static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size, | ||
107 | unsigned long arg, u8 arg_idx __maybe_unused, | ||
108 | u8 *arg_mask __maybe_unused) | ||
109 | { | ||
110 | int printed = 0, flags = arg; | ||
111 | |||
112 | #define P_MMAP_FLAG(n) \ | ||
113 | if (flags & MAP_##n) { \ | ||
114 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | ||
115 | flags &= ~MAP_##n; \ | ||
116 | } | ||
117 | |||
118 | P_MMAP_FLAG(SHARED); | ||
119 | P_MMAP_FLAG(PRIVATE); | ||
120 | #ifdef MAP_32BIT | ||
121 | P_MMAP_FLAG(32BIT); | ||
122 | #endif | ||
123 | P_MMAP_FLAG(ANONYMOUS); | ||
124 | P_MMAP_FLAG(DENYWRITE); | ||
125 | P_MMAP_FLAG(EXECUTABLE); | ||
126 | P_MMAP_FLAG(FILE); | ||
127 | P_MMAP_FLAG(FIXED); | ||
128 | P_MMAP_FLAG(GROWSDOWN); | ||
129 | #ifdef MAP_HUGETLB | ||
130 | P_MMAP_FLAG(HUGETLB); | ||
131 | #endif | ||
132 | P_MMAP_FLAG(LOCKED); | ||
133 | P_MMAP_FLAG(NONBLOCK); | ||
134 | P_MMAP_FLAG(NORESERVE); | ||
135 | P_MMAP_FLAG(POPULATE); | ||
136 | P_MMAP_FLAG(STACK); | ||
137 | #ifdef MAP_UNINITIALIZED | ||
138 | P_MMAP_FLAG(UNINITIALIZED); | ||
139 | #endif | ||
140 | #undef P_MMAP_FLAG | ||
141 | |||
142 | if (flags) | ||
143 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); | ||
144 | |||
145 | return printed; | ||
146 | } | ||
147 | |||
148 | #define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags | ||
149 | |||
150 | static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size, | ||
151 | unsigned long arg, u8 arg_idx __maybe_unused, | ||
152 | u8 *arg_mask __maybe_unused) | ||
153 | { | ||
154 | int behavior = arg; | ||
155 | |||
156 | switch (behavior) { | ||
157 | #define P_MADV_BHV(n) case MADV_##n: return scnprintf(bf, size, #n) | ||
158 | P_MADV_BHV(NORMAL); | ||
159 | P_MADV_BHV(RANDOM); | ||
160 | P_MADV_BHV(SEQUENTIAL); | ||
161 | P_MADV_BHV(WILLNEED); | ||
162 | P_MADV_BHV(DONTNEED); | ||
163 | P_MADV_BHV(REMOVE); | ||
164 | P_MADV_BHV(DONTFORK); | ||
165 | P_MADV_BHV(DOFORK); | ||
166 | P_MADV_BHV(HWPOISON); | ||
167 | #ifdef MADV_SOFT_OFFLINE | ||
168 | P_MADV_BHV(SOFT_OFFLINE); | ||
169 | #endif | ||
170 | P_MADV_BHV(MERGEABLE); | ||
171 | P_MADV_BHV(UNMERGEABLE); | ||
172 | #ifdef MADV_HUGEPAGE | ||
173 | P_MADV_BHV(HUGEPAGE); | ||
174 | #endif | ||
175 | #ifdef MADV_NOHUGEPAGE | ||
176 | P_MADV_BHV(NOHUGEPAGE); | ||
177 | #endif | ||
178 | #ifdef MADV_DONTDUMP | ||
179 | P_MADV_BHV(DONTDUMP); | ||
180 | #endif | ||
181 | #ifdef MADV_DODUMP | ||
182 | P_MADV_BHV(DODUMP); | ||
183 | #endif | ||
184 | #undef P_MADV_PHV | ||
185 | default: break; | ||
186 | } | ||
187 | |||
188 | return scnprintf(bf, size, "%#x", behavior); | ||
189 | } | ||
190 | |||
191 | #define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior | ||
192 | |||
193 | static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, unsigned long arg, | ||
194 | u8 arg_idx __maybe_unused, u8 *arg_mask) | ||
195 | { | ||
196 | enum syscall_futex_args { | ||
197 | SCF_UADDR = (1 << 0), | ||
198 | SCF_OP = (1 << 1), | ||
199 | SCF_VAL = (1 << 2), | ||
200 | SCF_TIMEOUT = (1 << 3), | ||
201 | SCF_UADDR2 = (1 << 4), | ||
202 | SCF_VAL3 = (1 << 5), | ||
203 | }; | ||
204 | int op = arg; | ||
205 | int cmd = op & FUTEX_CMD_MASK; | ||
206 | size_t printed = 0; | ||
207 | |||
208 | switch (cmd) { | ||
209 | #define P_FUTEX_OP(n) case FUTEX_##n: printed = scnprintf(bf, size, #n); | ||
210 | P_FUTEX_OP(WAIT); *arg_mask |= SCF_VAL3|SCF_UADDR2; break; | ||
211 | P_FUTEX_OP(WAKE); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; | ||
212 | P_FUTEX_OP(FD); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; | ||
213 | P_FUTEX_OP(REQUEUE); *arg_mask |= SCF_VAL3|SCF_TIMEOUT; break; | ||
214 | P_FUTEX_OP(CMP_REQUEUE); *arg_mask |= SCF_TIMEOUT; break; | ||
215 | P_FUTEX_OP(CMP_REQUEUE_PI); *arg_mask |= SCF_TIMEOUT; break; | ||
216 | P_FUTEX_OP(WAKE_OP); break; | ||
217 | P_FUTEX_OP(LOCK_PI); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; | ||
218 | P_FUTEX_OP(UNLOCK_PI); *arg_mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break; | ||
219 | P_FUTEX_OP(TRYLOCK_PI); *arg_mask |= SCF_VAL3|SCF_UADDR2; break; | ||
220 | P_FUTEX_OP(WAIT_BITSET); *arg_mask |= SCF_UADDR2; break; | ||
221 | P_FUTEX_OP(WAKE_BITSET); *arg_mask |= SCF_UADDR2; break; | ||
222 | P_FUTEX_OP(WAIT_REQUEUE_PI); break; | ||
223 | default: printed = scnprintf(bf, size, "%#x", cmd); break; | ||
224 | } | ||
225 | |||
226 | if (op & FUTEX_PRIVATE_FLAG) | ||
227 | printed += scnprintf(bf + printed, size - printed, "|PRIV"); | ||
228 | |||
229 | if (op & FUTEX_CLOCK_REALTIME) | ||
230 | printed += scnprintf(bf + printed, size - printed, "|CLKRT"); | ||
231 | |||
232 | return printed; | ||
233 | } | ||
234 | |||
235 | #define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op | ||
236 | |||
237 | static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size, | ||
238 | unsigned long arg, | ||
239 | u8 arg_idx, u8 *arg_mask) | ||
240 | { | ||
241 | int printed = 0, flags = arg; | ||
242 | |||
243 | if (!(flags & O_CREAT)) | ||
244 | *arg_mask |= 1 << (arg_idx + 1); /* Mask the mode parm */ | ||
245 | |||
246 | if (flags == 0) | ||
247 | return scnprintf(bf, size, "RDONLY"); | ||
248 | #define P_FLAG(n) \ | ||
249 | if (flags & O_##n) { \ | ||
250 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | ||
251 | flags &= ~O_##n; \ | ||
252 | } | ||
253 | |||
254 | P_FLAG(APPEND); | ||
255 | P_FLAG(ASYNC); | ||
256 | P_FLAG(CLOEXEC); | ||
257 | P_FLAG(CREAT); | ||
258 | P_FLAG(DIRECT); | ||
259 | P_FLAG(DIRECTORY); | ||
260 | P_FLAG(EXCL); | ||
261 | P_FLAG(LARGEFILE); | ||
262 | P_FLAG(NOATIME); | ||
263 | P_FLAG(NOCTTY); | ||
264 | #ifdef O_NONBLOCK | ||
265 | P_FLAG(NONBLOCK); | ||
266 | #elif O_NDELAY | ||
267 | P_FLAG(NDELAY); | ||
268 | #endif | ||
269 | #ifdef O_PATH | ||
270 | P_FLAG(PATH); | ||
271 | #endif | ||
272 | P_FLAG(RDWR); | ||
273 | #ifdef O_DSYNC | ||
274 | if ((flags & O_SYNC) == O_SYNC) | ||
275 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", "SYNC"); | ||
276 | else { | ||
277 | P_FLAG(DSYNC); | ||
278 | } | ||
279 | #else | ||
280 | P_FLAG(SYNC); | ||
281 | #endif | ||
282 | P_FLAG(TRUNC); | ||
283 | P_FLAG(WRONLY); | ||
284 | #undef P_FLAG | ||
285 | |||
286 | if (flags) | ||
287 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); | ||
288 | |||
289 | return printed; | ||
290 | } | ||
291 | |||
292 | #define SCA_OPEN_FLAGS syscall_arg__scnprintf_open_flags | ||
12 | 293 | ||
13 | static struct syscall_fmt { | 294 | static struct syscall_fmt { |
14 | const char *name; | 295 | const char *name; |
15 | const char *alias; | 296 | const char *alias; |
297 | size_t (*arg_scnprintf[6])(char *bf, size_t size, unsigned long arg, u8 arg_idx, u8 *arg_mask); | ||
16 | bool errmsg; | 298 | bool errmsg; |
17 | bool timeout; | 299 | bool timeout; |
300 | bool hexret; | ||
18 | } syscall_fmts[] = { | 301 | } syscall_fmts[] = { |
19 | { .name = "access", .errmsg = true, }, | 302 | { .name = "access", .errmsg = true, }, |
20 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, | 303 | { .name = "arch_prctl", .errmsg = true, .alias = "prctl", }, |
304 | { .name = "brk", .hexret = true, | ||
305 | .arg_scnprintf = { [0] = SCA_HEX, /* brk */ }, }, | ||
306 | { .name = "mmap", .hexret = true, }, | ||
307 | { .name = "connect", .errmsg = true, }, | ||
21 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, | 308 | { .name = "fstat", .errmsg = true, .alias = "newfstat", }, |
22 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, | 309 | { .name = "fstatat", .errmsg = true, .alias = "newfstatat", }, |
23 | { .name = "futex", .errmsg = true, }, | 310 | { .name = "futex", .errmsg = true, |
24 | { .name = "open", .errmsg = true, }, | 311 | .arg_scnprintf = { [1] = SCA_FUTEX_OP, /* op */ }, }, |
312 | { .name = "ioctl", .errmsg = true, | ||
313 | .arg_scnprintf = { [2] = SCA_HEX, /* arg */ }, }, | ||
314 | { .name = "lseek", .errmsg = true, | ||
315 | .arg_scnprintf = { [2] = SCA_WHENCE, /* whence */ }, }, | ||
316 | { .name = "lstat", .errmsg = true, .alias = "newlstat", }, | ||
317 | { .name = "madvise", .errmsg = true, | ||
318 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ | ||
319 | [2] = SCA_MADV_BHV, /* behavior */ }, }, | ||
320 | { .name = "mmap", .hexret = true, | ||
321 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ | ||
322 | [2] = SCA_MMAP_PROT, /* prot */ | ||
323 | [3] = SCA_MMAP_FLAGS, /* flags */ }, }, | ||
324 | { .name = "mprotect", .errmsg = true, | ||
325 | .arg_scnprintf = { [0] = SCA_HEX, /* start */ | ||
326 | [2] = SCA_MMAP_PROT, /* prot */ }, }, | ||
327 | { .name = "mremap", .hexret = true, | ||
328 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ | ||
329 | [4] = SCA_HEX, /* new_addr */ }, }, | ||
330 | { .name = "munmap", .errmsg = true, | ||
331 | .arg_scnprintf = { [0] = SCA_HEX, /* addr */ }, }, | ||
332 | { .name = "open", .errmsg = true, | ||
333 | .arg_scnprintf = { [1] = SCA_OPEN_FLAGS, /* flags */ }, }, | ||
334 | { .name = "open_by_handle_at", .errmsg = true, | ||
335 | .arg_scnprintf = { [2] = SCA_OPEN_FLAGS, /* flags */ }, }, | ||
336 | { .name = "openat", .errmsg = true, | ||
337 | .arg_scnprintf = { [2] = SCA_OPEN_FLAGS, /* flags */ }, }, | ||
25 | { .name = "poll", .errmsg = true, .timeout = true, }, | 338 | { .name = "poll", .errmsg = true, .timeout = true, }, |
26 | { .name = "ppoll", .errmsg = true, .timeout = true, }, | 339 | { .name = "ppoll", .errmsg = true, .timeout = true, }, |
340 | { .name = "pread", .errmsg = true, .alias = "pread64", }, | ||
341 | { .name = "pwrite", .errmsg = true, .alias = "pwrite64", }, | ||
27 | { .name = "read", .errmsg = true, }, | 342 | { .name = "read", .errmsg = true, }, |
28 | { .name = "recvfrom", .errmsg = true, }, | 343 | { .name = "recvfrom", .errmsg = true, }, |
29 | { .name = "select", .errmsg = true, .timeout = true, }, | 344 | { .name = "select", .errmsg = true, .timeout = true, }, |
30 | { .name = "socket", .errmsg = true, }, | 345 | { .name = "socket", .errmsg = true, }, |
31 | { .name = "stat", .errmsg = true, .alias = "newstat", }, | 346 | { .name = "stat", .errmsg = true, .alias = "newstat", }, |
347 | { .name = "uname", .errmsg = true, .alias = "newuname", }, | ||
32 | }; | 348 | }; |
33 | 349 | ||
34 | static int syscall_fmt__cmp(const void *name, const void *fmtp) | 350 | static int syscall_fmt__cmp(const void *name, const void *fmtp) |
@@ -46,7 +362,10 @@ static struct syscall_fmt *syscall_fmt__find(const char *name) | |||
46 | struct syscall { | 362 | struct syscall { |
47 | struct event_format *tp_format; | 363 | struct event_format *tp_format; |
48 | const char *name; | 364 | const char *name; |
365 | bool filtered; | ||
49 | struct syscall_fmt *fmt; | 366 | struct syscall_fmt *fmt; |
367 | size_t (**arg_scnprintf)(char *bf, size_t size, | ||
368 | unsigned long arg, u8 arg_idx, u8 *args_mask); | ||
50 | }; | 369 | }; |
51 | 370 | ||
52 | static size_t fprintf_duration(unsigned long t, FILE *fp) | 371 | static size_t fprintf_duration(unsigned long t, FILE *fp) |
@@ -60,7 +379,7 @@ static size_t fprintf_duration(unsigned long t, FILE *fp) | |||
60 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); | 379 | printed += color_fprintf(fp, PERF_COLOR_YELLOW, "%6.3f ms", duration); |
61 | else | 380 | else |
62 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); | 381 | printed += color_fprintf(fp, PERF_COLOR_NORMAL, "%6.3f ms", duration); |
63 | return printed + fprintf(stdout, "): "); | 382 | return printed + fprintf(fp, "): "); |
64 | } | 383 | } |
65 | 384 | ||
66 | struct thread_trace { | 385 | struct thread_trace { |
@@ -77,7 +396,7 @@ static struct thread_trace *thread_trace__new(void) | |||
77 | return zalloc(sizeof(struct thread_trace)); | 396 | return zalloc(sizeof(struct thread_trace)); |
78 | } | 397 | } |
79 | 398 | ||
80 | static struct thread_trace *thread__trace(struct thread *thread) | 399 | static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) |
81 | { | 400 | { |
82 | struct thread_trace *ttrace; | 401 | struct thread_trace *ttrace; |
83 | 402 | ||
@@ -95,12 +414,13 @@ static struct thread_trace *thread__trace(struct thread *thread) | |||
95 | 414 | ||
96 | return ttrace; | 415 | return ttrace; |
97 | fail: | 416 | fail: |
98 | color_fprintf(stdout, PERF_COLOR_RED, | 417 | color_fprintf(fp, PERF_COLOR_RED, |
99 | "WARNING: not enough memory, dropping samples!\n"); | 418 | "WARNING: not enough memory, dropping samples!\n"); |
100 | return NULL; | 419 | return NULL; |
101 | } | 420 | } |
102 | 421 | ||
103 | struct trace { | 422 | struct trace { |
423 | struct perf_tool tool; | ||
104 | int audit_machine; | 424 | int audit_machine; |
105 | struct { | 425 | struct { |
106 | int max; | 426 | int max; |
@@ -109,7 +429,12 @@ struct trace { | |||
109 | struct perf_record_opts opts; | 429 | struct perf_record_opts opts; |
110 | struct machine host; | 430 | struct machine host; |
111 | u64 base_time; | 431 | u64 base_time; |
432 | FILE *output; | ||
112 | unsigned long nr_events; | 433 | unsigned long nr_events; |
434 | struct strlist *ev_qualifier; | ||
435 | bool not_ev_qualifier; | ||
436 | struct intlist *tid_list; | ||
437 | struct intlist *pid_list; | ||
113 | bool sched; | 438 | bool sched; |
114 | bool multiple_threads; | 439 | bool multiple_threads; |
115 | double duration_filter; | 440 | double duration_filter; |
@@ -142,18 +467,19 @@ static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thre | |||
142 | printed += fprintf_duration(duration, fp); | 467 | printed += fprintf_duration(duration, fp); |
143 | 468 | ||
144 | if (trace->multiple_threads) | 469 | if (trace->multiple_threads) |
145 | printed += fprintf(fp, "%d ", thread->pid); | 470 | printed += fprintf(fp, "%d ", thread->tid); |
146 | 471 | ||
147 | return printed; | 472 | return printed; |
148 | } | 473 | } |
149 | 474 | ||
150 | static int trace__process_event(struct machine *machine, union perf_event *event) | 475 | static int trace__process_event(struct trace *trace, struct machine *machine, |
476 | union perf_event *event) | ||
151 | { | 477 | { |
152 | int ret = 0; | 478 | int ret = 0; |
153 | 479 | ||
154 | switch (event->header.type) { | 480 | switch (event->header.type) { |
155 | case PERF_RECORD_LOST: | 481 | case PERF_RECORD_LOST: |
156 | color_fprintf(stdout, PERF_COLOR_RED, | 482 | color_fprintf(trace->output, PERF_COLOR_RED, |
157 | "LOST %" PRIu64 " events!\n", event->lost.lost); | 483 | "LOST %" PRIu64 " events!\n", event->lost.lost); |
158 | ret = machine__process_lost_event(machine, event); | 484 | ret = machine__process_lost_event(machine, event); |
159 | default: | 485 | default: |
@@ -164,12 +490,13 @@ static int trace__process_event(struct machine *machine, union perf_event *event | |||
164 | return ret; | 490 | return ret; |
165 | } | 491 | } |
166 | 492 | ||
167 | static int trace__tool_process(struct perf_tool *tool __maybe_unused, | 493 | static int trace__tool_process(struct perf_tool *tool, |
168 | union perf_event *event, | 494 | union perf_event *event, |
169 | struct perf_sample *sample __maybe_unused, | 495 | struct perf_sample *sample __maybe_unused, |
170 | struct machine *machine) | 496 | struct machine *machine) |
171 | { | 497 | { |
172 | return trace__process_event(machine, event); | 498 | struct trace *trace = container_of(tool, struct trace, tool); |
499 | return trace__process_event(trace, machine, event); | ||
173 | } | 500 | } |
174 | 501 | ||
175 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | 502 | static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) |
@@ -183,11 +510,11 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | |||
183 | machine__create_kernel_maps(&trace->host); | 510 | machine__create_kernel_maps(&trace->host); |
184 | 511 | ||
185 | if (perf_target__has_task(&trace->opts.target)) { | 512 | if (perf_target__has_task(&trace->opts.target)) { |
186 | err = perf_event__synthesize_thread_map(NULL, evlist->threads, | 513 | err = perf_event__synthesize_thread_map(&trace->tool, evlist->threads, |
187 | trace__tool_process, | 514 | trace__tool_process, |
188 | &trace->host); | 515 | &trace->host); |
189 | } else { | 516 | } else { |
190 | err = perf_event__synthesize_threads(NULL, trace__tool_process, | 517 | err = perf_event__synthesize_threads(&trace->tool, trace__tool_process, |
191 | &trace->host); | 518 | &trace->host); |
192 | } | 519 | } |
193 | 520 | ||
@@ -197,6 +524,26 @@ static int trace__symbols_init(struct trace *trace, struct perf_evlist *evlist) | |||
197 | return err; | 524 | return err; |
198 | } | 525 | } |
199 | 526 | ||
527 | static int syscall__set_arg_fmts(struct syscall *sc) | ||
528 | { | ||
529 | struct format_field *field; | ||
530 | int idx = 0; | ||
531 | |||
532 | sc->arg_scnprintf = calloc(sc->tp_format->format.nr_fields - 1, sizeof(void *)); | ||
533 | if (sc->arg_scnprintf == NULL) | ||
534 | return -1; | ||
535 | |||
536 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | ||
537 | if (sc->fmt && sc->fmt->arg_scnprintf[idx]) | ||
538 | sc->arg_scnprintf[idx] = sc->fmt->arg_scnprintf[idx]; | ||
539 | else if (field->flags & FIELD_IS_POINTER) | ||
540 | sc->arg_scnprintf[idx] = syscall_arg__scnprintf_hex; | ||
541 | ++idx; | ||
542 | } | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
200 | static int trace__read_syscall_info(struct trace *trace, int id) | 547 | static int trace__read_syscall_info(struct trace *trace, int id) |
201 | { | 548 | { |
202 | char tp_name[128]; | 549 | char tp_name[128]; |
@@ -225,6 +572,20 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
225 | 572 | ||
226 | sc = trace->syscalls.table + id; | 573 | sc = trace->syscalls.table + id; |
227 | sc->name = name; | 574 | sc->name = name; |
575 | |||
576 | if (trace->ev_qualifier) { | ||
577 | bool in = strlist__find(trace->ev_qualifier, name) != NULL; | ||
578 | |||
579 | if (!(in ^ trace->not_ev_qualifier)) { | ||
580 | sc->filtered = true; | ||
581 | /* | ||
582 | * No need to do read tracepoint information since this will be | ||
583 | * filtered out. | ||
584 | */ | ||
585 | return 0; | ||
586 | } | ||
587 | } | ||
588 | |||
228 | sc->fmt = syscall_fmt__find(sc->name); | 589 | sc->fmt = syscall_fmt__find(sc->name); |
229 | 590 | ||
230 | snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); | 591 | snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name); |
@@ -235,7 +596,10 @@ static int trace__read_syscall_info(struct trace *trace, int id) | |||
235 | sc->tp_format = event_format__new("syscalls", tp_name); | 596 | sc->tp_format = event_format__new("syscalls", tp_name); |
236 | } | 597 | } |
237 | 598 | ||
238 | return sc->tp_format != NULL ? 0 : -1; | 599 | if (sc->tp_format == NULL) |
600 | return -1; | ||
601 | |||
602 | return syscall__set_arg_fmts(sc); | ||
239 | } | 603 | } |
240 | 604 | ||
241 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | 605 | static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, |
@@ -246,11 +610,23 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, | |||
246 | 610 | ||
247 | if (sc->tp_format != NULL) { | 611 | if (sc->tp_format != NULL) { |
248 | struct format_field *field; | 612 | struct format_field *field; |
613 | u8 mask = 0, bit = 1; | ||
614 | |||
615 | for (field = sc->tp_format->format.fields->next; field; | ||
616 | field = field->next, ++i, bit <<= 1) { | ||
617 | if (mask & bit) | ||
618 | continue; | ||
249 | 619 | ||
250 | for (field = sc->tp_format->format.fields->next; field; field = field->next) { | ||
251 | printed += scnprintf(bf + printed, size - printed, | 620 | printed += scnprintf(bf + printed, size - printed, |
252 | "%s%s: %ld", printed ? ", " : "", | 621 | "%s%s: ", printed ? ", " : "", field->name); |
253 | field->name, args[i++]); | 622 | |
623 | if (sc->arg_scnprintf && sc->arg_scnprintf[i]) { | ||
624 | printed += sc->arg_scnprintf[i](bf + printed, size - printed, | ||
625 | args[i], i, &mask); | ||
626 | } else { | ||
627 | printed += scnprintf(bf + printed, size - printed, | ||
628 | "%ld", args[i]); | ||
629 | } | ||
254 | } | 630 | } |
255 | } else { | 631 | } else { |
256 | while (i < 6) { | 632 | while (i < 6) { |
@@ -274,7 +650,22 @@ static struct syscall *trace__syscall_info(struct trace *trace, | |||
274 | int id = perf_evsel__intval(evsel, sample, "id"); | 650 | int id = perf_evsel__intval(evsel, sample, "id"); |
275 | 651 | ||
276 | if (id < 0) { | 652 | if (id < 0) { |
277 | printf("Invalid syscall %d id, skipping...\n", id); | 653 | |
654 | /* | ||
655 | * XXX: Noticed on x86_64, reproduced as far back as 3.0.36, haven't tried | ||
656 | * before that, leaving at a higher verbosity level till that is | ||
657 | * explained. Reproduced with plain ftrace with: | ||
658 | * | ||
659 | * echo 1 > /t/events/raw_syscalls/sys_exit/enable | ||
660 | * grep "NR -1 " /t/trace_pipe | ||
661 | * | ||
662 | * After generating some load on the machine. | ||
663 | */ | ||
664 | if (verbose > 1) { | ||
665 | static u64 n; | ||
666 | fprintf(trace->output, "Invalid syscall %d id, skipping (%s, %" PRIu64 ") ...\n", | ||
667 | id, perf_evsel__name(evsel), ++n); | ||
668 | } | ||
278 | return NULL; | 669 | return NULL; |
279 | } | 670 | } |
280 | 671 | ||
@@ -288,10 +679,12 @@ static struct syscall *trace__syscall_info(struct trace *trace, | |||
288 | return &trace->syscalls.table[id]; | 679 | return &trace->syscalls.table[id]; |
289 | 680 | ||
290 | out_cant_read: | 681 | out_cant_read: |
291 | printf("Problems reading syscall %d", id); | 682 | if (verbose) { |
292 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) | 683 | fprintf(trace->output, "Problems reading syscall %d", id); |
293 | printf("(%s)", trace->syscalls.table[id].name); | 684 | if (id <= trace->syscalls.max && trace->syscalls.table[id].name != NULL) |
294 | puts(" information"); | 685 | fprintf(trace->output, "(%s)", trace->syscalls.table[id].name); |
686 | fputs(" information\n", trace->output); | ||
687 | } | ||
295 | return NULL; | 688 | return NULL; |
296 | } | 689 | } |
297 | 690 | ||
@@ -301,16 +694,25 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
301 | char *msg; | 694 | char *msg; |
302 | void *args; | 695 | void *args; |
303 | size_t printed = 0; | 696 | size_t printed = 0; |
304 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | 697 | struct thread *thread; |
305 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 698 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
306 | struct thread_trace *ttrace = thread__trace(thread); | 699 | struct thread_trace *ttrace; |
700 | |||
701 | if (sc == NULL) | ||
702 | return -1; | ||
703 | |||
704 | if (sc->filtered) | ||
705 | return 0; | ||
307 | 706 | ||
308 | if (ttrace == NULL || sc == NULL) | 707 | thread = machine__findnew_thread(&trace->host, sample->pid, |
708 | sample->tid); | ||
709 | ttrace = thread__trace(thread, trace->output); | ||
710 | if (ttrace == NULL) | ||
309 | return -1; | 711 | return -1; |
310 | 712 | ||
311 | args = perf_evsel__rawptr(evsel, sample, "args"); | 713 | args = perf_evsel__rawptr(evsel, sample, "args"); |
312 | if (args == NULL) { | 714 | if (args == NULL) { |
313 | printf("Problems reading syscall arguments\n"); | 715 | fprintf(trace->output, "Problems reading syscall arguments\n"); |
314 | return -1; | 716 | return -1; |
315 | } | 717 | } |
316 | 718 | ||
@@ -330,8 +732,8 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, | |||
330 | 732 | ||
331 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { | 733 | if (!strcmp(sc->name, "exit_group") || !strcmp(sc->name, "exit")) { |
332 | if (!trace->duration_filter) { | 734 | if (!trace->duration_filter) { |
333 | trace__fprintf_entry_head(trace, thread, 1, sample->time, stdout); | 735 | trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output); |
334 | printf("%-70s\n", ttrace->entry_str); | 736 | fprintf(trace->output, "%-70s\n", ttrace->entry_str); |
335 | } | 737 | } |
336 | } else | 738 | } else |
337 | ttrace->entry_pending = true; | 739 | ttrace->entry_pending = true; |
@@ -344,11 +746,20 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
344 | { | 746 | { |
345 | int ret; | 747 | int ret; |
346 | u64 duration = 0; | 748 | u64 duration = 0; |
347 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | 749 | struct thread *thread; |
348 | struct thread_trace *ttrace = thread__trace(thread); | ||
349 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); | 750 | struct syscall *sc = trace__syscall_info(trace, evsel, sample); |
751 | struct thread_trace *ttrace; | ||
752 | |||
753 | if (sc == NULL) | ||
754 | return -1; | ||
755 | |||
756 | if (sc->filtered) | ||
757 | return 0; | ||
350 | 758 | ||
351 | if (ttrace == NULL || sc == NULL) | 759 | thread = machine__findnew_thread(&trace->host, sample->pid, |
760 | sample->tid); | ||
761 | ttrace = thread__trace(thread, trace->output); | ||
762 | if (ttrace == NULL) | ||
352 | return -1; | 763 | return -1; |
353 | 764 | ||
354 | ret = perf_evsel__intval(evsel, sample, "ret"); | 765 | ret = perf_evsel__intval(evsel, sample, "ret"); |
@@ -364,28 +775,33 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
364 | } else if (trace->duration_filter) | 775 | } else if (trace->duration_filter) |
365 | goto out; | 776 | goto out; |
366 | 777 | ||
367 | trace__fprintf_entry_head(trace, thread, duration, sample->time, stdout); | 778 | trace__fprintf_entry_head(trace, thread, duration, sample->time, trace->output); |
368 | 779 | ||
369 | if (ttrace->entry_pending) { | 780 | if (ttrace->entry_pending) { |
370 | printf("%-70s", ttrace->entry_str); | 781 | fprintf(trace->output, "%-70s", ttrace->entry_str); |
371 | } else { | 782 | } else { |
372 | printf(" ... ["); | 783 | fprintf(trace->output, " ... ["); |
373 | color_fprintf(stdout, PERF_COLOR_YELLOW, "continued"); | 784 | color_fprintf(trace->output, PERF_COLOR_YELLOW, "continued"); |
374 | printf("]: %s()", sc->name); | 785 | fprintf(trace->output, "]: %s()", sc->name); |
375 | } | 786 | } |
376 | 787 | ||
377 | if (ret < 0 && sc->fmt && sc->fmt->errmsg) { | 788 | if (sc->fmt == NULL) { |
789 | signed_print: | ||
790 | fprintf(trace->output, ") = %d", ret); | ||
791 | } else if (ret < 0 && sc->fmt->errmsg) { | ||
378 | char bf[256]; | 792 | char bf[256]; |
379 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), | 793 | const char *emsg = strerror_r(-ret, bf, sizeof(bf)), |
380 | *e = audit_errno_to_name(-ret); | 794 | *e = audit_errno_to_name(-ret); |
381 | 795 | ||
382 | printf(") = -1 %s %s", e, emsg); | 796 | fprintf(trace->output, ") = -1 %s %s", e, emsg); |
383 | } else if (ret == 0 && sc->fmt && sc->fmt->timeout) | 797 | } else if (ret == 0 && sc->fmt->timeout) |
384 | printf(") = 0 Timeout"); | 798 | fprintf(trace->output, ") = 0 Timeout"); |
799 | else if (sc->fmt->hexret) | ||
800 | fprintf(trace->output, ") = %#x", ret); | ||
385 | else | 801 | else |
386 | printf(") = %d", ret); | 802 | goto signed_print; |
387 | 803 | ||
388 | putchar('\n'); | 804 | fputc('\n', trace->output); |
389 | out: | 805 | out: |
390 | ttrace->entry_pending = false; | 806 | ttrace->entry_pending = false; |
391 | 807 | ||
@@ -397,8 +813,10 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs | |||
397 | { | 813 | { |
398 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); | 814 | u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); |
399 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; | 815 | double runtime_ms = (double)runtime / NSEC_PER_MSEC; |
400 | struct thread *thread = machine__findnew_thread(&trace->host, sample->tid); | 816 | struct thread *thread = machine__findnew_thread(&trace->host, |
401 | struct thread_trace *ttrace = thread__trace(thread); | 817 | sample->pid, |
818 | sample->tid); | ||
819 | struct thread_trace *ttrace = thread__trace(thread, trace->output); | ||
402 | 820 | ||
403 | if (ttrace == NULL) | 821 | if (ttrace == NULL) |
404 | goto out_dump; | 822 | goto out_dump; |
@@ -408,7 +826,7 @@ static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evs | |||
408 | return 0; | 826 | return 0; |
409 | 827 | ||
410 | out_dump: | 828 | out_dump: |
411 | printf("%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", | 829 | fprintf(trace->output, "%s: comm=%s,pid=%u,runtime=%" PRIu64 ",vruntime=%" PRIu64 ")\n", |
412 | evsel->name, | 830 | evsel->name, |
413 | perf_evsel__strval(evsel, sample, "comm"), | 831 | perf_evsel__strval(evsel, sample, "comm"), |
414 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), | 832 | (pid_t)perf_evsel__intval(evsel, sample, "pid"), |
@@ -417,6 +835,72 @@ out_dump: | |||
417 | return 0; | 835 | return 0; |
418 | } | 836 | } |
419 | 837 | ||
838 | static bool skip_sample(struct trace *trace, struct perf_sample *sample) | ||
839 | { | ||
840 | if ((trace->pid_list && intlist__find(trace->pid_list, sample->pid)) || | ||
841 | (trace->tid_list && intlist__find(trace->tid_list, sample->tid))) | ||
842 | return false; | ||
843 | |||
844 | if (trace->pid_list || trace->tid_list) | ||
845 | return true; | ||
846 | |||
847 | return false; | ||
848 | } | ||
849 | |||
850 | static int trace__process_sample(struct perf_tool *tool, | ||
851 | union perf_event *event __maybe_unused, | ||
852 | struct perf_sample *sample, | ||
853 | struct perf_evsel *evsel, | ||
854 | struct machine *machine __maybe_unused) | ||
855 | { | ||
856 | struct trace *trace = container_of(tool, struct trace, tool); | ||
857 | int err = 0; | ||
858 | |||
859 | tracepoint_handler handler = evsel->handler.func; | ||
860 | |||
861 | if (skip_sample(trace, sample)) | ||
862 | return 0; | ||
863 | |||
864 | if (trace->base_time == 0) | ||
865 | trace->base_time = sample->time; | ||
866 | |||
867 | if (handler) | ||
868 | handler(trace, evsel, sample); | ||
869 | |||
870 | return err; | ||
871 | } | ||
872 | |||
873 | static bool | ||
874 | perf_session__has_tp(struct perf_session *session, const char *name) | ||
875 | { | ||
876 | struct perf_evsel *evsel; | ||
877 | |||
878 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, name); | ||
879 | |||
880 | return evsel != NULL; | ||
881 | } | ||
882 | |||
883 | static int parse_target_str(struct trace *trace) | ||
884 | { | ||
885 | if (trace->opts.target.pid) { | ||
886 | trace->pid_list = intlist__new(trace->opts.target.pid); | ||
887 | if (trace->pid_list == NULL) { | ||
888 | pr_err("Error parsing process id string\n"); | ||
889 | return -EINVAL; | ||
890 | } | ||
891 | } | ||
892 | |||
893 | if (trace->opts.target.tid) { | ||
894 | trace->tid_list = intlist__new(trace->opts.target.tid); | ||
895 | if (trace->tid_list == NULL) { | ||
896 | pr_err("Error parsing thread id string\n"); | ||
897 | return -EINVAL; | ||
898 | } | ||
899 | } | ||
900 | |||
901 | return 0; | ||
902 | } | ||
903 | |||
420 | static int trace__run(struct trace *trace, int argc, const char **argv) | 904 | static int trace__run(struct trace *trace, int argc, const char **argv) |
421 | { | 905 | { |
422 | struct perf_evlist *evlist = perf_evlist__new(); | 906 | struct perf_evlist *evlist = perf_evlist__new(); |
@@ -426,32 +910,32 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
426 | const bool forks = argc > 0; | 910 | const bool forks = argc > 0; |
427 | 911 | ||
428 | if (evlist == NULL) { | 912 | if (evlist == NULL) { |
429 | printf("Not enough memory to run!\n"); | 913 | fprintf(trace->output, "Not enough memory to run!\n"); |
430 | goto out; | 914 | goto out; |
431 | } | 915 | } |
432 | 916 | ||
433 | if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || | 917 | if (perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_enter", trace__sys_enter) || |
434 | perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { | 918 | perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) { |
435 | printf("Couldn't read the raw_syscalls tracepoints information!\n"); | 919 | fprintf(trace->output, "Couldn't read the raw_syscalls tracepoints information!\n"); |
436 | goto out_delete_evlist; | 920 | goto out_delete_evlist; |
437 | } | 921 | } |
438 | 922 | ||
439 | if (trace->sched && | 923 | if (trace->sched && |
440 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", | 924 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", |
441 | trace__sched_stat_runtime)) { | 925 | trace__sched_stat_runtime)) { |
442 | printf("Couldn't read the sched_stat_runtime tracepoint information!\n"); | 926 | fprintf(trace->output, "Couldn't read the sched_stat_runtime tracepoint information!\n"); |
443 | goto out_delete_evlist; | 927 | goto out_delete_evlist; |
444 | } | 928 | } |
445 | 929 | ||
446 | err = perf_evlist__create_maps(evlist, &trace->opts.target); | 930 | err = perf_evlist__create_maps(evlist, &trace->opts.target); |
447 | if (err < 0) { | 931 | if (err < 0) { |
448 | printf("Problems parsing the target to trace, check your options!\n"); | 932 | fprintf(trace->output, "Problems parsing the target to trace, check your options!\n"); |
449 | goto out_delete_evlist; | 933 | goto out_delete_evlist; |
450 | } | 934 | } |
451 | 935 | ||
452 | err = trace__symbols_init(trace, evlist); | 936 | err = trace__symbols_init(trace, evlist); |
453 | if (err < 0) { | 937 | if (err < 0) { |
454 | printf("Problems initializing symbol libraries!\n"); | 938 | fprintf(trace->output, "Problems initializing symbol libraries!\n"); |
455 | goto out_delete_maps; | 939 | goto out_delete_maps; |
456 | } | 940 | } |
457 | 941 | ||
@@ -464,20 +948,20 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
464 | err = perf_evlist__prepare_workload(evlist, &trace->opts.target, | 948 | err = perf_evlist__prepare_workload(evlist, &trace->opts.target, |
465 | argv, false, false); | 949 | argv, false, false); |
466 | if (err < 0) { | 950 | if (err < 0) { |
467 | printf("Couldn't run the workload!\n"); | 951 | fprintf(trace->output, "Couldn't run the workload!\n"); |
468 | goto out_delete_maps; | 952 | goto out_delete_maps; |
469 | } | 953 | } |
470 | } | 954 | } |
471 | 955 | ||
472 | err = perf_evlist__open(evlist); | 956 | err = perf_evlist__open(evlist); |
473 | if (err < 0) { | 957 | if (err < 0) { |
474 | printf("Couldn't create the events: %s\n", strerror(errno)); | 958 | fprintf(trace->output, "Couldn't create the events: %s\n", strerror(errno)); |
475 | goto out_delete_maps; | 959 | goto out_delete_maps; |
476 | } | 960 | } |
477 | 961 | ||
478 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 962 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
479 | if (err < 0) { | 963 | if (err < 0) { |
480 | printf("Couldn't mmap the events: %s\n", strerror(errno)); | 964 | fprintf(trace->output, "Couldn't mmap the events: %s\n", strerror(errno)); |
481 | goto out_close_evlist; | 965 | goto out_close_evlist; |
482 | } | 966 | } |
483 | 967 | ||
@@ -502,33 +986,38 @@ again: | |||
502 | 986 | ||
503 | err = perf_evlist__parse_sample(evlist, event, &sample); | 987 | err = perf_evlist__parse_sample(evlist, event, &sample); |
504 | if (err) { | 988 | if (err) { |
505 | printf("Can't parse sample, err = %d, skipping...\n", err); | 989 | fprintf(trace->output, "Can't parse sample, err = %d, skipping...\n", err); |
506 | continue; | 990 | goto next_event; |
507 | } | 991 | } |
508 | 992 | ||
509 | if (trace->base_time == 0) | 993 | if (trace->base_time == 0) |
510 | trace->base_time = sample.time; | 994 | trace->base_time = sample.time; |
511 | 995 | ||
512 | if (type != PERF_RECORD_SAMPLE) { | 996 | if (type != PERF_RECORD_SAMPLE) { |
513 | trace__process_event(&trace->host, event); | 997 | trace__process_event(trace, &trace->host, event); |
514 | continue; | 998 | continue; |
515 | } | 999 | } |
516 | 1000 | ||
517 | evsel = perf_evlist__id2evsel(evlist, sample.id); | 1001 | evsel = perf_evlist__id2evsel(evlist, sample.id); |
518 | if (evsel == NULL) { | 1002 | if (evsel == NULL) { |
519 | printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); | 1003 | fprintf(trace->output, "Unknown tp ID %" PRIu64 ", skipping...\n", sample.id); |
520 | continue; | 1004 | goto next_event; |
521 | } | 1005 | } |
522 | 1006 | ||
523 | if (sample.raw_data == NULL) { | 1007 | if (sample.raw_data == NULL) { |
524 | printf("%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | 1008 | fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
525 | perf_evsel__name(evsel), sample.tid, | 1009 | perf_evsel__name(evsel), sample.tid, |
526 | sample.cpu, sample.raw_size); | 1010 | sample.cpu, sample.raw_size); |
527 | continue; | 1011 | goto next_event; |
528 | } | 1012 | } |
529 | 1013 | ||
530 | handler = evsel->handler.func; | 1014 | handler = evsel->handler.func; |
531 | handler(trace, evsel, &sample); | 1015 | handler(trace, evsel, &sample); |
1016 | next_event: | ||
1017 | perf_evlist__mmap_consume(evlist, i); | ||
1018 | |||
1019 | if (done) | ||
1020 | goto out_unmap_evlist; | ||
532 | } | 1021 | } |
533 | } | 1022 | } |
534 | 1023 | ||
@@ -556,6 +1045,70 @@ out: | |||
556 | return err; | 1045 | return err; |
557 | } | 1046 | } |
558 | 1047 | ||
1048 | static int trace__replay(struct trace *trace) | ||
1049 | { | ||
1050 | const struct perf_evsel_str_handler handlers[] = { | ||
1051 | { "raw_syscalls:sys_enter", trace__sys_enter, }, | ||
1052 | { "raw_syscalls:sys_exit", trace__sys_exit, }, | ||
1053 | }; | ||
1054 | |||
1055 | struct perf_session *session; | ||
1056 | int err = -1; | ||
1057 | |||
1058 | trace->tool.sample = trace__process_sample; | ||
1059 | trace->tool.mmap = perf_event__process_mmap; | ||
1060 | trace->tool.mmap2 = perf_event__process_mmap2; | ||
1061 | trace->tool.comm = perf_event__process_comm; | ||
1062 | trace->tool.exit = perf_event__process_exit; | ||
1063 | trace->tool.fork = perf_event__process_fork; | ||
1064 | trace->tool.attr = perf_event__process_attr; | ||
1065 | trace->tool.tracing_data = perf_event__process_tracing_data; | ||
1066 | trace->tool.build_id = perf_event__process_build_id; | ||
1067 | |||
1068 | trace->tool.ordered_samples = true; | ||
1069 | trace->tool.ordering_requires_timestamps = true; | ||
1070 | |||
1071 | /* add tid to output */ | ||
1072 | trace->multiple_threads = true; | ||
1073 | |||
1074 | if (symbol__init() < 0) | ||
1075 | return -1; | ||
1076 | |||
1077 | session = perf_session__new(input_name, O_RDONLY, 0, false, | ||
1078 | &trace->tool); | ||
1079 | if (session == NULL) | ||
1080 | return -ENOMEM; | ||
1081 | |||
1082 | err = perf_session__set_tracepoints_handlers(session, handlers); | ||
1083 | if (err) | ||
1084 | goto out; | ||
1085 | |||
1086 | if (!perf_session__has_tp(session, "raw_syscalls:sys_enter")) { | ||
1087 | pr_err("Data file does not have raw_syscalls:sys_enter events\n"); | ||
1088 | goto out; | ||
1089 | } | ||
1090 | |||
1091 | if (!perf_session__has_tp(session, "raw_syscalls:sys_exit")) { | ||
1092 | pr_err("Data file does not have raw_syscalls:sys_exit events\n"); | ||
1093 | goto out; | ||
1094 | } | ||
1095 | |||
1096 | err = parse_target_str(trace); | ||
1097 | if (err != 0) | ||
1098 | goto out; | ||
1099 | |||
1100 | setup_pager(); | ||
1101 | |||
1102 | err = perf_session__process_events(session, &trace->tool); | ||
1103 | if (err) | ||
1104 | pr_err("Failed to process events, error %d", err); | ||
1105 | |||
1106 | out: | ||
1107 | perf_session__delete(session); | ||
1108 | |||
1109 | return err; | ||
1110 | } | ||
1111 | |||
559 | static size_t trace__fprintf_threads_header(FILE *fp) | 1112 | static size_t trace__fprintf_threads_header(FILE *fp) |
560 | { | 1113 | { |
561 | size_t printed; | 1114 | size_t printed; |
@@ -593,7 +1146,7 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) | |||
593 | color = PERF_COLOR_YELLOW; | 1146 | color = PERF_COLOR_YELLOW; |
594 | 1147 | ||
595 | printed += color_fprintf(fp, color, "%20s", thread->comm); | 1148 | printed += color_fprintf(fp, color, "%20s", thread->comm); |
596 | printed += fprintf(fp, " - %-5d :%11lu [", thread->pid, ttrace->nr_events); | 1149 | printed += fprintf(fp, " - %-5d :%11lu [", thread->tid, ttrace->nr_events); |
597 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); | 1150 | printed += color_fprintf(fp, color, "%5.1f%%", ratio); |
598 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); | 1151 | printed += fprintf(fp, " ] %10.3f ms\n", ttrace->runtime_ms); |
599 | } | 1152 | } |
@@ -610,6 +1163,23 @@ static int trace__set_duration(const struct option *opt, const char *str, | |||
610 | return 0; | 1163 | return 0; |
611 | } | 1164 | } |
612 | 1165 | ||
1166 | static int trace__open_output(struct trace *trace, const char *filename) | ||
1167 | { | ||
1168 | struct stat st; | ||
1169 | |||
1170 | if (!stat(filename, &st) && st.st_size) { | ||
1171 | char oldname[PATH_MAX]; | ||
1172 | |||
1173 | scnprintf(oldname, sizeof(oldname), "%s.old", filename); | ||
1174 | unlink(oldname); | ||
1175 | rename(filename, oldname); | ||
1176 | } | ||
1177 | |||
1178 | trace->output = fopen(filename, "w"); | ||
1179 | |||
1180 | return trace->output == NULL ? -errno : 0; | ||
1181 | } | ||
1182 | |||
613 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | 1183 | int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) |
614 | { | 1184 | { |
615 | const char * const trace_usage[] = { | 1185 | const char * const trace_usage[] = { |
@@ -632,26 +1202,34 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
632 | .no_delay = true, | 1202 | .no_delay = true, |
633 | .mmap_pages = 1024, | 1203 | .mmap_pages = 1024, |
634 | }, | 1204 | }, |
1205 | .output = stdout, | ||
635 | }; | 1206 | }; |
1207 | const char *output_name = NULL; | ||
1208 | const char *ev_qualifier_str = NULL; | ||
636 | const struct option trace_options[] = { | 1209 | const struct option trace_options[] = { |
1210 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", | ||
1211 | "list of events to trace"), | ||
1212 | OPT_STRING('o', "output", &output_name, "file", "output file name"), | ||
1213 | OPT_STRING('i', "input", &input_name, "file", "Analyze events in file"), | ||
637 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", | 1214 | OPT_STRING('p', "pid", &trace.opts.target.pid, "pid", |
638 | "trace events on existing process id"), | 1215 | "trace events on existing process id"), |
639 | OPT_STRING(0, "tid", &trace.opts.target.tid, "tid", | 1216 | OPT_STRING('t', "tid", &trace.opts.target.tid, "tid", |
640 | "trace events on existing thread id"), | 1217 | "trace events on existing thread id"), |
641 | OPT_BOOLEAN(0, "all-cpus", &trace.opts.target.system_wide, | 1218 | OPT_BOOLEAN('a', "all-cpus", &trace.opts.target.system_wide, |
642 | "system-wide collection from all CPUs"), | 1219 | "system-wide collection from all CPUs"), |
643 | OPT_STRING(0, "cpu", &trace.opts.target.cpu_list, "cpu", | 1220 | OPT_STRING('C', "cpu", &trace.opts.target.cpu_list, "cpu", |
644 | "list of cpus to monitor"), | 1221 | "list of cpus to monitor"), |
645 | OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit, | 1222 | OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit, |
646 | "child tasks do not inherit counters"), | 1223 | "child tasks do not inherit counters"), |
647 | OPT_UINTEGER(0, "mmap-pages", &trace.opts.mmap_pages, | 1224 | OPT_UINTEGER('m', "mmap-pages", &trace.opts.mmap_pages, |
648 | "number of mmap data pages"), | 1225 | "number of mmap data pages"), |
649 | OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user", | 1226 | OPT_STRING('u', "uid", &trace.opts.target.uid_str, "user", |
650 | "user to profile"), | 1227 | "user to profile"), |
651 | OPT_CALLBACK(0, "duration", &trace, "float", | 1228 | OPT_CALLBACK(0, "duration", &trace, "float", |
652 | "show only events with duration > N.M ms", | 1229 | "show only events with duration > N.M ms", |
653 | trace__set_duration), | 1230 | trace__set_duration), |
654 | OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), | 1231 | OPT_BOOLEAN(0, "sched", &trace.sched, "show blocking scheduler events"), |
1232 | OPT_INCR('v', "verbose", &verbose, "be more verbose"), | ||
655 | OPT_END() | 1233 | OPT_END() |
656 | }; | 1234 | }; |
657 | int err; | 1235 | int err; |
@@ -659,27 +1237,57 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
659 | 1237 | ||
660 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); | 1238 | argc = parse_options(argc, argv, trace_options, trace_usage, 0); |
661 | 1239 | ||
1240 | if (output_name != NULL) { | ||
1241 | err = trace__open_output(&trace, output_name); | ||
1242 | if (err < 0) { | ||
1243 | perror("failed to create output file"); | ||
1244 | goto out; | ||
1245 | } | ||
1246 | } | ||
1247 | |||
1248 | if (ev_qualifier_str != NULL) { | ||
1249 | const char *s = ev_qualifier_str; | ||
1250 | |||
1251 | trace.not_ev_qualifier = *s == '!'; | ||
1252 | if (trace.not_ev_qualifier) | ||
1253 | ++s; | ||
1254 | trace.ev_qualifier = strlist__new(true, s); | ||
1255 | if (trace.ev_qualifier == NULL) { | ||
1256 | fputs("Not enough memory to parse event qualifier", | ||
1257 | trace.output); | ||
1258 | err = -ENOMEM; | ||
1259 | goto out_close; | ||
1260 | } | ||
1261 | } | ||
1262 | |||
662 | err = perf_target__validate(&trace.opts.target); | 1263 | err = perf_target__validate(&trace.opts.target); |
663 | if (err) { | 1264 | if (err) { |
664 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 1265 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
665 | printf("%s", bf); | 1266 | fprintf(trace.output, "%s", bf); |
666 | return err; | 1267 | goto out_close; |
667 | } | 1268 | } |
668 | 1269 | ||
669 | err = perf_target__parse_uid(&trace.opts.target); | 1270 | err = perf_target__parse_uid(&trace.opts.target); |
670 | if (err) { | 1271 | if (err) { |
671 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); | 1272 | perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf)); |
672 | printf("%s", bf); | 1273 | fprintf(trace.output, "%s", bf); |
673 | return err; | 1274 | goto out_close; |
674 | } | 1275 | } |
675 | 1276 | ||
676 | if (!argc && perf_target__none(&trace.opts.target)) | 1277 | if (!argc && perf_target__none(&trace.opts.target)) |
677 | trace.opts.target.system_wide = true; | 1278 | trace.opts.target.system_wide = true; |
678 | 1279 | ||
679 | err = trace__run(&trace, argc, argv); | 1280 | if (input_name) |
1281 | err = trace__replay(&trace); | ||
1282 | else | ||
1283 | err = trace__run(&trace, argc, argv); | ||
680 | 1284 | ||
681 | if (trace.sched && !err) | 1285 | if (trace.sched && !err) |
682 | trace__fprintf_thread_summary(&trace, stdout); | 1286 | trace__fprintf_thread_summary(&trace, trace.output); |
683 | 1287 | ||
1288 | out_close: | ||
1289 | if (output_name != NULL) | ||
1290 | fclose(trace.output); | ||
1291 | out: | ||
684 | return err; | 1292 | return err; |
685 | } | 1293 | } |
diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index b5d9238cb181..5f6f9b3271bb 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile | |||
@@ -46,6 +46,8 @@ ifneq ($(obj-perf),) | |||
46 | obj-perf := $(abspath $(obj-perf))/ | 46 | obj-perf := $(abspath $(obj-perf))/ |
47 | endif | 47 | endif |
48 | 48 | ||
49 | LIB_INCLUDE := $(srctree)/tools/lib/ | ||
50 | |||
49 | # include ARCH specific config | 51 | # include ARCH specific config |
50 | -include $(src-perf)/arch/$(ARCH)/Makefile | 52 | -include $(src-perf)/arch/$(ARCH)/Makefile |
51 | 53 | ||
@@ -85,7 +87,7 @@ CFLAGS += -Wall | |||
85 | CFLAGS += -Wextra | 87 | CFLAGS += -Wextra |
86 | CFLAGS += -std=gnu99 | 88 | CFLAGS += -std=gnu99 |
87 | 89 | ||
88 | EXTLIBS = -lelf -lpthread -lrt -lm | 90 | EXTLIBS = -lelf -lpthread -lrt -lm -ldl |
89 | 91 | ||
90 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y) | 92 | ifeq ($(call try-cc,$(SOURCE_HELLO),$(CFLAGS) -Werror -fstack-protector-all,-fstack-protector-all),y) |
91 | CFLAGS += -fstack-protector-all | 93 | CFLAGS += -fstack-protector-all |
@@ -121,8 +123,7 @@ endif | |||
121 | 123 | ||
122 | CFLAGS += -I$(src-perf)/util | 124 | CFLAGS += -I$(src-perf)/util |
123 | CFLAGS += -I$(src-perf) | 125 | CFLAGS += -I$(src-perf) |
124 | CFLAGS += -I$(TRACE_EVENT_DIR) | 126 | CFLAGS += -I$(LIB_INCLUDE) |
125 | CFLAGS += -I$(srctree)/tools/lib/ | ||
126 | 127 | ||
127 | CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE | 128 | CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE |
128 | 129 | ||
@@ -179,6 +180,9 @@ FLAGS_LIBELF=$(CFLAGS) $(LDFLAGS) $(EXTLIBS) | |||
179 | ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) | 180 | ifeq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_LIBELF),-DLIBELF_MMAP),y) |
180 | CFLAGS += -DLIBELF_MMAP | 181 | CFLAGS += -DLIBELF_MMAP |
181 | endif | 182 | endif |
183 | ifeq ($(call try-cc,$(SOURCE_ELF_GETPHDRNUM),$(FLAGS_LIBELF),-DHAVE_ELF_GETPHDRNUM),y) | ||
184 | CFLAGS += -DHAVE_ELF_GETPHDRNUM | ||
185 | endif | ||
182 | 186 | ||
183 | # include ARCH specific config | 187 | # include ARCH specific config |
184 | -include $(src-perf)/arch/$(ARCH)/Makefile | 188 | -include $(src-perf)/arch/$(ARCH)/Makefile |
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index 708fb8e9822a..f79305739ecc 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak | |||
@@ -61,6 +61,15 @@ int main(void) | |||
61 | } | 61 | } |
62 | endef | 62 | endef |
63 | 63 | ||
64 | define SOURCE_ELF_GETPHDRNUM | ||
65 | #include <libelf.h> | ||
66 | int main(void) | ||
67 | { | ||
68 | size_t dst; | ||
69 | return elf_getphdrnum(0, &dst); | ||
70 | } | ||
71 | endef | ||
72 | |||
64 | ifndef NO_SLANG | 73 | ifndef NO_SLANG |
65 | define SOURCE_SLANG | 74 | define SOURCE_SLANG |
66 | #include <slang.h> | 75 | #include <slang.h> |
@@ -210,6 +219,7 @@ define SOURCE_LIBAUDIT | |||
210 | 219 | ||
211 | int main(void) | 220 | int main(void) |
212 | { | 221 | { |
222 | printf(\"error message: %s\", audit_errno_to_name(0)); | ||
213 | return audit_open(); | 223 | return audit_open(); |
214 | } | 224 | } |
215 | endef | 225 | endef |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 32bd102c32b6..cf20187eee0a 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -125,6 +125,9 @@ | |||
125 | #ifndef NSEC_PER_SEC | 125 | #ifndef NSEC_PER_SEC |
126 | # define NSEC_PER_SEC 1000000000ULL | 126 | # define NSEC_PER_SEC 1000000000ULL |
127 | #endif | 127 | #endif |
128 | #ifndef NSEC_PER_USEC | ||
129 | # define NSEC_PER_USEC 1000ULL | ||
130 | #endif | ||
128 | 131 | ||
129 | static inline unsigned long long rdclock(void) | 132 | static inline unsigned long long rdclock(void) |
130 | { | 133 | { |
diff --git a/tools/perf/python/twatch.py b/tools/perf/python/twatch.py index b11cca584238..2225162ee1fc 100755 --- a/tools/perf/python/twatch.py +++ b/tools/perf/python/twatch.py | |||
@@ -21,7 +21,7 @@ def main(): | |||
21 | evsel = perf.evsel(task = 1, comm = 1, mmap = 0, | 21 | evsel = perf.evsel(task = 1, comm = 1, mmap = 0, |
22 | wakeup_events = 1, watermark = 1, | 22 | wakeup_events = 1, watermark = 1, |
23 | sample_id_all = 1, | 23 | sample_id_all = 1, |
24 | sample_type = perf.SAMPLE_PERIOD | perf.SAMPLE_TID | perf.SAMPLE_CPU | perf.SAMPLE_TID) | 24 | sample_type = perf.SAMPLE_PERIOD | perf.SAMPLE_TID | perf.SAMPLE_CPU) |
25 | evsel.open(cpus = cpus, threads = threads); | 25 | evsel.open(cpus = cpus, threads = threads); |
26 | evlist = perf.evlist(cpus, threads) | 26 | evlist = perf.evlist(cpus, threads) |
27 | evlist.add(evsel) | 27 | evlist.add(evsel) |
diff --git a/tools/perf/tests/attr/test-record-group-sampling b/tools/perf/tests/attr/test-record-group-sampling new file mode 100644 index 000000000000..658f5d60c873 --- /dev/null +++ b/tools/perf/tests/attr/test-record-group-sampling | |||
@@ -0,0 +1,36 @@ | |||
1 | [config] | ||
2 | command = record | ||
3 | args = -e '{cycles,cache-misses}:S' kill >/dev/null 2>&1 | ||
4 | |||
5 | [event-1:base-record] | ||
6 | fd=1 | ||
7 | group_fd=-1 | ||
8 | sample_type=343 | ||
9 | read_format=12 | ||
10 | inherit=0 | ||
11 | |||
12 | [event-2:base-record] | ||
13 | fd=2 | ||
14 | group_fd=1 | ||
15 | |||
16 | # cache-misses | ||
17 | type=0 | ||
18 | config=3 | ||
19 | |||
20 | # default | PERF_SAMPLE_READ | ||
21 | sample_type=343 | ||
22 | |||
23 | # PERF_FORMAT_ID | PERF_FORMAT_GROUP | ||
24 | read_format=12 | ||
25 | |||
26 | mmap=0 | ||
27 | comm=0 | ||
28 | enable_on_exec=0 | ||
29 | disabled=0 | ||
30 | |||
31 | # inherit is disabled for group sampling | ||
32 | inherit=0 | ||
33 | |||
34 | # sampling disabled | ||
35 | sample_freq=0 | ||
36 | sample_period=0 | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 35b45f1466b5..1e67437fb4ca 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -93,6 +93,28 @@ static struct test { | |||
93 | .desc = "Test software clock events have valid period values", | 93 | .desc = "Test software clock events have valid period values", |
94 | .func = test__sw_clock_freq, | 94 | .func = test__sw_clock_freq, |
95 | }, | 95 | }, |
96 | #if defined(__x86_64__) || defined(__i386__) | ||
97 | { | ||
98 | .desc = "Test converting perf time to TSC", | ||
99 | .func = test__perf_time_to_tsc, | ||
100 | }, | ||
101 | #endif | ||
102 | { | ||
103 | .desc = "Test object code reading", | ||
104 | .func = test__code_reading, | ||
105 | }, | ||
106 | { | ||
107 | .desc = "Test sample parsing", | ||
108 | .func = test__sample_parsing, | ||
109 | }, | ||
110 | { | ||
111 | .desc = "Test using a dummy software event to keep tracking", | ||
112 | .func = test__keep_tracking, | ||
113 | }, | ||
114 | { | ||
115 | .desc = "Test parsing with no sample_id_all bit set", | ||
116 | .func = test__parse_no_sample_id_all, | ||
117 | }, | ||
96 | { | 118 | { |
97 | .func = NULL, | 119 | .func = NULL, |
98 | }, | 120 | }, |
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c new file mode 100644 index 000000000000..e3fedfa2906e --- /dev/null +++ b/tools/perf/tests/code-reading.c | |||
@@ -0,0 +1,573 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <stdlib.h> | ||
3 | #include <unistd.h> | ||
4 | #include <stdio.h> | ||
5 | #include <inttypes.h> | ||
6 | #include <ctype.h> | ||
7 | #include <string.h> | ||
8 | |||
9 | #include "parse-events.h" | ||
10 | #include "evlist.h" | ||
11 | #include "evsel.h" | ||
12 | #include "thread_map.h" | ||
13 | #include "cpumap.h" | ||
14 | #include "machine.h" | ||
15 | #include "event.h" | ||
16 | #include "thread.h" | ||
17 | |||
18 | #include "tests.h" | ||
19 | |||
20 | #define BUFSZ 1024 | ||
21 | #define READLEN 128 | ||
22 | |||
23 | struct state { | ||
24 | u64 done[1024]; | ||
25 | size_t done_cnt; | ||
26 | }; | ||
27 | |||
28 | static unsigned int hex(char c) | ||
29 | { | ||
30 | if (c >= '0' && c <= '9') | ||
31 | return c - '0'; | ||
32 | if (c >= 'a' && c <= 'f') | ||
33 | return c - 'a' + 10; | ||
34 | return c - 'A' + 10; | ||
35 | } | ||
36 | |||
37 | static void read_objdump_line(const char *line, size_t line_len, void **buf, | ||
38 | size_t *len) | ||
39 | { | ||
40 | const char *p; | ||
41 | size_t i; | ||
42 | |||
43 | /* Skip to a colon */ | ||
44 | p = strchr(line, ':'); | ||
45 | if (!p) | ||
46 | return; | ||
47 | i = p + 1 - line; | ||
48 | |||
49 | /* Read bytes */ | ||
50 | while (*len) { | ||
51 | char c1, c2; | ||
52 | |||
53 | /* Skip spaces */ | ||
54 | for (; i < line_len; i++) { | ||
55 | if (!isspace(line[i])) | ||
56 | break; | ||
57 | } | ||
58 | /* Get 2 hex digits */ | ||
59 | if (i >= line_len || !isxdigit(line[i])) | ||
60 | break; | ||
61 | c1 = line[i++]; | ||
62 | if (i >= line_len || !isxdigit(line[i])) | ||
63 | break; | ||
64 | c2 = line[i++]; | ||
65 | /* Followed by a space */ | ||
66 | if (i < line_len && line[i] && !isspace(line[i])) | ||
67 | break; | ||
68 | /* Store byte */ | ||
69 | *(unsigned char *)*buf = (hex(c1) << 4) | hex(c2); | ||
70 | *buf += 1; | ||
71 | *len -= 1; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static int read_objdump_output(FILE *f, void **buf, size_t *len) | ||
76 | { | ||
77 | char *line = NULL; | ||
78 | size_t line_len; | ||
79 | ssize_t ret; | ||
80 | int err = 0; | ||
81 | |||
82 | while (1) { | ||
83 | ret = getline(&line, &line_len, f); | ||
84 | if (feof(f)) | ||
85 | break; | ||
86 | if (ret < 0) { | ||
87 | pr_debug("getline failed\n"); | ||
88 | err = -1; | ||
89 | break; | ||
90 | } | ||
91 | read_objdump_line(line, ret, buf, len); | ||
92 | } | ||
93 | |||
94 | free(line); | ||
95 | |||
96 | return err; | ||
97 | } | ||
98 | |||
99 | static int read_via_objdump(const char *filename, u64 addr, void *buf, | ||
100 | size_t len) | ||
101 | { | ||
102 | char cmd[PATH_MAX * 2]; | ||
103 | const char *fmt; | ||
104 | FILE *f; | ||
105 | int ret; | ||
106 | |||
107 | fmt = "%s -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s"; | ||
108 | ret = snprintf(cmd, sizeof(cmd), fmt, "objdump", addr, addr + len, | ||
109 | filename); | ||
110 | if (ret <= 0 || (size_t)ret >= sizeof(cmd)) | ||
111 | return -1; | ||
112 | |||
113 | pr_debug("Objdump command is: %s\n", cmd); | ||
114 | |||
115 | /* Ignore objdump errors */ | ||
116 | strcat(cmd, " 2>/dev/null"); | ||
117 | |||
118 | f = popen(cmd, "r"); | ||
119 | if (!f) { | ||
120 | pr_debug("popen failed\n"); | ||
121 | return -1; | ||
122 | } | ||
123 | |||
124 | ret = read_objdump_output(f, &buf, &len); | ||
125 | if (len) { | ||
126 | pr_debug("objdump read too few bytes\n"); | ||
127 | if (!ret) | ||
128 | ret = len; | ||
129 | } | ||
130 | |||
131 | pclose(f); | ||
132 | |||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | static int read_object_code(u64 addr, size_t len, u8 cpumode, | ||
137 | struct thread *thread, struct machine *machine, | ||
138 | struct state *state) | ||
139 | { | ||
140 | struct addr_location al; | ||
141 | unsigned char buf1[BUFSZ]; | ||
142 | unsigned char buf2[BUFSZ]; | ||
143 | size_t ret_len; | ||
144 | u64 objdump_addr; | ||
145 | int ret; | ||
146 | |||
147 | pr_debug("Reading object code for memory address: %#"PRIx64"\n", addr); | ||
148 | |||
149 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, addr, | ||
150 | &al); | ||
151 | if (!al.map || !al.map->dso) { | ||
152 | pr_debug("thread__find_addr_map failed\n"); | ||
153 | return -1; | ||
154 | } | ||
155 | |||
156 | pr_debug("File is: %s\n", al.map->dso->long_name); | ||
157 | |||
158 | if (al.map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && | ||
159 | !dso__is_kcore(al.map->dso)) { | ||
160 | pr_debug("Unexpected kernel address - skipping\n"); | ||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | pr_debug("On file address is: %#"PRIx64"\n", al.addr); | ||
165 | |||
166 | if (len > BUFSZ) | ||
167 | len = BUFSZ; | ||
168 | |||
169 | /* Do not go off the map */ | ||
170 | if (addr + len > al.map->end) | ||
171 | len = al.map->end - addr; | ||
172 | |||
173 | /* Read the object code using perf */ | ||
174 | ret_len = dso__data_read_offset(al.map->dso, machine, al.addr, buf1, | ||
175 | len); | ||
176 | if (ret_len != len) { | ||
177 | pr_debug("dso__data_read_offset failed\n"); | ||
178 | return -1; | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * Converting addresses for use by objdump requires more information. | ||
183 | * map__load() does that. See map__rip_2objdump() for details. | ||
184 | */ | ||
185 | if (map__load(al.map, NULL)) | ||
186 | return -1; | ||
187 | |||
188 | /* objdump struggles with kcore - try each map only once */ | ||
189 | if (dso__is_kcore(al.map->dso)) { | ||
190 | size_t d; | ||
191 | |||
192 | for (d = 0; d < state->done_cnt; d++) { | ||
193 | if (state->done[d] == al.map->start) { | ||
194 | pr_debug("kcore map tested already"); | ||
195 | pr_debug(" - skipping\n"); | ||
196 | return 0; | ||
197 | } | ||
198 | } | ||
199 | if (state->done_cnt >= ARRAY_SIZE(state->done)) { | ||
200 | pr_debug("Too many kcore maps - skipping\n"); | ||
201 | return 0; | ||
202 | } | ||
203 | state->done[state->done_cnt++] = al.map->start; | ||
204 | } | ||
205 | |||
206 | /* Read the object code using objdump */ | ||
207 | objdump_addr = map__rip_2objdump(al.map, al.addr); | ||
208 | ret = read_via_objdump(al.map->dso->long_name, objdump_addr, buf2, len); | ||
209 | if (ret > 0) { | ||
210 | /* | ||
211 | * The kernel maps are inaccurate - assume objdump is right in | ||
212 | * that case. | ||
213 | */ | ||
214 | if (cpumode == PERF_RECORD_MISC_KERNEL || | ||
215 | cpumode == PERF_RECORD_MISC_GUEST_KERNEL) { | ||
216 | len -= ret; | ||
217 | if (len) { | ||
218 | pr_debug("Reducing len to %zu\n", len); | ||
219 | } else if (dso__is_kcore(al.map->dso)) { | ||
220 | /* | ||
221 | * objdump cannot handle very large segments | ||
222 | * that may be found in kcore. | ||
223 | */ | ||
224 | pr_debug("objdump failed for kcore"); | ||
225 | pr_debug(" - skipping\n"); | ||
226 | return 0; | ||
227 | } else { | ||
228 | return -1; | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | if (ret < 0) { | ||
233 | pr_debug("read_via_objdump failed\n"); | ||
234 | return -1; | ||
235 | } | ||
236 | |||
237 | /* The results should be identical */ | ||
238 | if (memcmp(buf1, buf2, len)) { | ||
239 | pr_debug("Bytes read differ from those read by objdump\n"); | ||
240 | return -1; | ||
241 | } | ||
242 | pr_debug("Bytes read match those read by objdump\n"); | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static int process_sample_event(struct machine *machine, | ||
248 | struct perf_evlist *evlist, | ||
249 | union perf_event *event, struct state *state) | ||
250 | { | ||
251 | struct perf_sample sample; | ||
252 | struct thread *thread; | ||
253 | u8 cpumode; | ||
254 | |||
255 | if (perf_evlist__parse_sample(evlist, event, &sample)) { | ||
256 | pr_debug("perf_evlist__parse_sample failed\n"); | ||
257 | return -1; | ||
258 | } | ||
259 | |||
260 | thread = machine__findnew_thread(machine, sample.pid, sample.pid); | ||
261 | if (!thread) { | ||
262 | pr_debug("machine__findnew_thread failed\n"); | ||
263 | return -1; | ||
264 | } | ||
265 | |||
266 | cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
267 | |||
268 | return read_object_code(sample.ip, READLEN, cpumode, thread, machine, | ||
269 | state); | ||
270 | } | ||
271 | |||
272 | static int process_event(struct machine *machine, struct perf_evlist *evlist, | ||
273 | union perf_event *event, struct state *state) | ||
274 | { | ||
275 | if (event->header.type == PERF_RECORD_SAMPLE) | ||
276 | return process_sample_event(machine, evlist, event, state); | ||
277 | |||
278 | if (event->header.type < PERF_RECORD_MAX) | ||
279 | return machine__process_event(machine, event); | ||
280 | |||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | static int process_events(struct machine *machine, struct perf_evlist *evlist, | ||
285 | struct state *state) | ||
286 | { | ||
287 | union perf_event *event; | ||
288 | int i, ret; | ||
289 | |||
290 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
291 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
292 | ret = process_event(machine, evlist, event, state); | ||
293 | perf_evlist__mmap_consume(evlist, i); | ||
294 | if (ret < 0) | ||
295 | return ret; | ||
296 | } | ||
297 | } | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | static int comp(const void *a, const void *b) | ||
302 | { | ||
303 | return *(int *)a - *(int *)b; | ||
304 | } | ||
305 | |||
306 | static void do_sort_something(void) | ||
307 | { | ||
308 | int buf[40960], i; | ||
309 | |||
310 | for (i = 0; i < (int)ARRAY_SIZE(buf); i++) | ||
311 | buf[i] = ARRAY_SIZE(buf) - i - 1; | ||
312 | |||
313 | qsort(buf, ARRAY_SIZE(buf), sizeof(int), comp); | ||
314 | |||
315 | for (i = 0; i < (int)ARRAY_SIZE(buf); i++) { | ||
316 | if (buf[i] != i) { | ||
317 | pr_debug("qsort failed\n"); | ||
318 | break; | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | |||
323 | static void sort_something(void) | ||
324 | { | ||
325 | int i; | ||
326 | |||
327 | for (i = 0; i < 10; i++) | ||
328 | do_sort_something(); | ||
329 | } | ||
330 | |||
331 | static void syscall_something(void) | ||
332 | { | ||
333 | int pipefd[2]; | ||
334 | int i; | ||
335 | |||
336 | for (i = 0; i < 1000; i++) { | ||
337 | if (pipe(pipefd) < 0) { | ||
338 | pr_debug("pipe failed\n"); | ||
339 | break; | ||
340 | } | ||
341 | close(pipefd[1]); | ||
342 | close(pipefd[0]); | ||
343 | } | ||
344 | } | ||
345 | |||
346 | static void fs_something(void) | ||
347 | { | ||
348 | const char *test_file_name = "temp-perf-code-reading-test-file--"; | ||
349 | FILE *f; | ||
350 | int i; | ||
351 | |||
352 | for (i = 0; i < 1000; i++) { | ||
353 | f = fopen(test_file_name, "w+"); | ||
354 | if (f) { | ||
355 | fclose(f); | ||
356 | unlink(test_file_name); | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | |||
361 | static void do_something(void) | ||
362 | { | ||
363 | fs_something(); | ||
364 | |||
365 | sort_something(); | ||
366 | |||
367 | syscall_something(); | ||
368 | } | ||
369 | |||
370 | enum { | ||
371 | TEST_CODE_READING_OK, | ||
372 | TEST_CODE_READING_NO_VMLINUX, | ||
373 | TEST_CODE_READING_NO_KCORE, | ||
374 | TEST_CODE_READING_NO_ACCESS, | ||
375 | TEST_CODE_READING_NO_KERNEL_OBJ, | ||
376 | }; | ||
377 | |||
378 | static int do_test_code_reading(bool try_kcore) | ||
379 | { | ||
380 | struct machines machines; | ||
381 | struct machine *machine; | ||
382 | struct thread *thread; | ||
383 | struct perf_record_opts opts = { | ||
384 | .mmap_pages = UINT_MAX, | ||
385 | .user_freq = UINT_MAX, | ||
386 | .user_interval = ULLONG_MAX, | ||
387 | .freq = 4000, | ||
388 | .target = { | ||
389 | .uses_mmap = true, | ||
390 | }, | ||
391 | }; | ||
392 | struct state state = { | ||
393 | .done_cnt = 0, | ||
394 | }; | ||
395 | struct thread_map *threads = NULL; | ||
396 | struct cpu_map *cpus = NULL; | ||
397 | struct perf_evlist *evlist = NULL; | ||
398 | struct perf_evsel *evsel = NULL; | ||
399 | int err = -1, ret; | ||
400 | pid_t pid; | ||
401 | struct map *map; | ||
402 | bool have_vmlinux, have_kcore, excl_kernel = false; | ||
403 | |||
404 | pid = getpid(); | ||
405 | |||
406 | machines__init(&machines); | ||
407 | machine = &machines.host; | ||
408 | |||
409 | ret = machine__create_kernel_maps(machine); | ||
410 | if (ret < 0) { | ||
411 | pr_debug("machine__create_kernel_maps failed\n"); | ||
412 | goto out_err; | ||
413 | } | ||
414 | |||
415 | /* Force the use of kallsyms instead of vmlinux to try kcore */ | ||
416 | if (try_kcore) | ||
417 | symbol_conf.kallsyms_name = "/proc/kallsyms"; | ||
418 | |||
419 | /* Load kernel map */ | ||
420 | map = machine->vmlinux_maps[MAP__FUNCTION]; | ||
421 | ret = map__load(map, NULL); | ||
422 | if (ret < 0) { | ||
423 | pr_debug("map__load failed\n"); | ||
424 | goto out_err; | ||
425 | } | ||
426 | have_vmlinux = dso__is_vmlinux(map->dso); | ||
427 | have_kcore = dso__is_kcore(map->dso); | ||
428 | |||
429 | /* 2nd time through we just try kcore */ | ||
430 | if (try_kcore && !have_kcore) | ||
431 | return TEST_CODE_READING_NO_KCORE; | ||
432 | |||
433 | /* No point getting kernel events if there is no kernel object */ | ||
434 | if (!have_vmlinux && !have_kcore) | ||
435 | excl_kernel = true; | ||
436 | |||
437 | threads = thread_map__new_by_tid(pid); | ||
438 | if (!threads) { | ||
439 | pr_debug("thread_map__new_by_tid failed\n"); | ||
440 | goto out_err; | ||
441 | } | ||
442 | |||
443 | ret = perf_event__synthesize_thread_map(NULL, threads, | ||
444 | perf_event__process, machine); | ||
445 | if (ret < 0) { | ||
446 | pr_debug("perf_event__synthesize_thread_map failed\n"); | ||
447 | goto out_err; | ||
448 | } | ||
449 | |||
450 | thread = machine__findnew_thread(machine, pid, pid); | ||
451 | if (!thread) { | ||
452 | pr_debug("machine__findnew_thread failed\n"); | ||
453 | goto out_err; | ||
454 | } | ||
455 | |||
456 | cpus = cpu_map__new(NULL); | ||
457 | if (!cpus) { | ||
458 | pr_debug("cpu_map__new failed\n"); | ||
459 | goto out_err; | ||
460 | } | ||
461 | |||
462 | while (1) { | ||
463 | const char *str; | ||
464 | |||
465 | evlist = perf_evlist__new(); | ||
466 | if (!evlist) { | ||
467 | pr_debug("perf_evlist__new failed\n"); | ||
468 | goto out_err; | ||
469 | } | ||
470 | |||
471 | perf_evlist__set_maps(evlist, cpus, threads); | ||
472 | |||
473 | if (excl_kernel) | ||
474 | str = "cycles:u"; | ||
475 | else | ||
476 | str = "cycles"; | ||
477 | pr_debug("Parsing event '%s'\n", str); | ||
478 | ret = parse_events(evlist, str); | ||
479 | if (ret < 0) { | ||
480 | pr_debug("parse_events failed\n"); | ||
481 | goto out_err; | ||
482 | } | ||
483 | |||
484 | perf_evlist__config(evlist, &opts); | ||
485 | |||
486 | evsel = perf_evlist__first(evlist); | ||
487 | |||
488 | evsel->attr.comm = 1; | ||
489 | evsel->attr.disabled = 1; | ||
490 | evsel->attr.enable_on_exec = 0; | ||
491 | |||
492 | ret = perf_evlist__open(evlist); | ||
493 | if (ret < 0) { | ||
494 | if (!excl_kernel) { | ||
495 | excl_kernel = true; | ||
496 | perf_evlist__delete(evlist); | ||
497 | evlist = NULL; | ||
498 | continue; | ||
499 | } | ||
500 | pr_debug("perf_evlist__open failed\n"); | ||
501 | goto out_err; | ||
502 | } | ||
503 | break; | ||
504 | } | ||
505 | |||
506 | ret = perf_evlist__mmap(evlist, UINT_MAX, false); | ||
507 | if (ret < 0) { | ||
508 | pr_debug("perf_evlist__mmap failed\n"); | ||
509 | goto out_err; | ||
510 | } | ||
511 | |||
512 | perf_evlist__enable(evlist); | ||
513 | |||
514 | do_something(); | ||
515 | |||
516 | perf_evlist__disable(evlist); | ||
517 | |||
518 | ret = process_events(machine, evlist, &state); | ||
519 | if (ret < 0) | ||
520 | goto out_err; | ||
521 | |||
522 | if (!have_vmlinux && !have_kcore && !try_kcore) | ||
523 | err = TEST_CODE_READING_NO_KERNEL_OBJ; | ||
524 | else if (!have_vmlinux && !try_kcore) | ||
525 | err = TEST_CODE_READING_NO_VMLINUX; | ||
526 | else if (excl_kernel) | ||
527 | err = TEST_CODE_READING_NO_ACCESS; | ||
528 | else | ||
529 | err = TEST_CODE_READING_OK; | ||
530 | out_err: | ||
531 | if (evlist) { | ||
532 | perf_evlist__munmap(evlist); | ||
533 | perf_evlist__close(evlist); | ||
534 | perf_evlist__delete(evlist); | ||
535 | } | ||
536 | if (cpus) | ||
537 | cpu_map__delete(cpus); | ||
538 | if (threads) | ||
539 | thread_map__delete(threads); | ||
540 | machines__destroy_kernel_maps(&machines); | ||
541 | machine__delete_threads(machine); | ||
542 | machines__exit(&machines); | ||
543 | |||
544 | return err; | ||
545 | } | ||
546 | |||
547 | int test__code_reading(void) | ||
548 | { | ||
549 | int ret; | ||
550 | |||
551 | ret = do_test_code_reading(false); | ||
552 | if (!ret) | ||
553 | ret = do_test_code_reading(true); | ||
554 | |||
555 | switch (ret) { | ||
556 | case TEST_CODE_READING_OK: | ||
557 | return 0; | ||
558 | case TEST_CODE_READING_NO_VMLINUX: | ||
559 | fprintf(stderr, " (no vmlinux)"); | ||
560 | return 0; | ||
561 | case TEST_CODE_READING_NO_KCORE: | ||
562 | fprintf(stderr, " (no kcore)"); | ||
563 | return 0; | ||
564 | case TEST_CODE_READING_NO_ACCESS: | ||
565 | fprintf(stderr, " (no access)"); | ||
566 | return 0; | ||
567 | case TEST_CODE_READING_NO_KERNEL_OBJ: | ||
568 | fprintf(stderr, " (no kernel obj)"); | ||
569 | return 0; | ||
570 | default: | ||
571 | return -1; | ||
572 | }; | ||
573 | } | ||
diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index 5eaffa2de9c5..dffe0551acaa 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c | |||
@@ -10,14 +10,6 @@ | |||
10 | #include "symbol.h" | 10 | #include "symbol.h" |
11 | #include "tests.h" | 11 | #include "tests.h" |
12 | 12 | ||
13 | #define TEST_ASSERT_VAL(text, cond) \ | ||
14 | do { \ | ||
15 | if (!(cond)) { \ | ||
16 | pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ | ||
17 | return -1; \ | ||
18 | } \ | ||
19 | } while (0) | ||
20 | |||
21 | static char *test_file(int size) | 13 | static char *test_file(int size) |
22 | { | 14 | { |
23 | static char buf_templ[] = "/tmp/test-XXXXXX"; | 15 | static char buf_templ[] = "/tmp/test-XXXXXX"; |
diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c index a5d2fcc5ae35..9b98c1554833 100644 --- a/tools/perf/tests/evsel-tp-sched.c +++ b/tools/perf/tests/evsel-tp-sched.c | |||
@@ -1,6 +1,6 @@ | |||
1 | #include <traceevent/event-parse.h> | ||
1 | #include "evsel.h" | 2 | #include "evsel.h" |
2 | #include "tests.h" | 3 | #include "tests.h" |
3 | #include "event-parse.h" | ||
4 | 4 | ||
5 | static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, | 5 | static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, |
6 | int size, bool should_be_signed) | 6 | int size, bool should_be_signed) |
@@ -49,7 +49,7 @@ int test__perf_evsel__tp_sched_test(void) | |||
49 | if (perf_evsel__test_field(evsel, "prev_prio", 4, true)) | 49 | if (perf_evsel__test_field(evsel, "prev_prio", 4, true)) |
50 | ret = -1; | 50 | ret = -1; |
51 | 51 | ||
52 | if (perf_evsel__test_field(evsel, "prev_state", 8, true)) | 52 | if (perf_evsel__test_field(evsel, "prev_state", sizeof(long), true)) |
53 | ret = -1; | 53 | ret = -1; |
54 | 54 | ||
55 | if (perf_evsel__test_field(evsel, "next_comm", 16, true)) | 55 | if (perf_evsel__test_field(evsel, "next_comm", 16, true)) |
diff --git a/tools/perf/tests/hists_link.c b/tools/perf/tests/hists_link.c index 89085a9615e2..4228ffc0d968 100644 --- a/tools/perf/tests/hists_link.c +++ b/tools/perf/tests/hists_link.c | |||
@@ -88,7 +88,8 @@ static struct machine *setup_fake_machine(struct machines *machines) | |||
88 | for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { | 88 | for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { |
89 | struct thread *thread; | 89 | struct thread *thread; |
90 | 90 | ||
91 | thread = machine__findnew_thread(machine, fake_threads[i].pid); | 91 | thread = machine__findnew_thread(machine, fake_threads[i].pid, |
92 | fake_threads[i].pid); | ||
92 | if (thread == NULL) | 93 | if (thread == NULL) |
93 | goto out; | 94 | goto out; |
94 | 95 | ||
@@ -210,17 +211,15 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
210 | list_for_each_entry(evsel, &evlist->entries, node) { | 211 | list_for_each_entry(evsel, &evlist->entries, node) { |
211 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { | 212 | for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) { |
212 | const union perf_event event = { | 213 | const union perf_event event = { |
213 | .ip = { | 214 | .header = { |
214 | .header = { | 215 | .misc = PERF_RECORD_MISC_USER, |
215 | .misc = PERF_RECORD_MISC_USER, | ||
216 | }, | ||
217 | .pid = fake_common_samples[k].pid, | ||
218 | .ip = fake_common_samples[k].ip, | ||
219 | }, | 216 | }, |
220 | }; | 217 | }; |
221 | 218 | ||
219 | sample.pid = fake_common_samples[k].pid; | ||
220 | sample.ip = fake_common_samples[k].ip; | ||
222 | if (perf_event__preprocess_sample(&event, machine, &al, | 221 | if (perf_event__preprocess_sample(&event, machine, &al, |
223 | &sample, 0) < 0) | 222 | &sample) < 0) |
224 | goto out; | 223 | goto out; |
225 | 224 | ||
226 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); | 225 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); |
@@ -234,17 +233,15 @@ static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine) | |||
234 | 233 | ||
235 | for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) { | 234 | for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) { |
236 | const union perf_event event = { | 235 | const union perf_event event = { |
237 | .ip = { | 236 | .header = { |
238 | .header = { | 237 | .misc = PERF_RECORD_MISC_USER, |
239 | .misc = PERF_RECORD_MISC_USER, | ||
240 | }, | ||
241 | .pid = fake_samples[i][k].pid, | ||
242 | .ip = fake_samples[i][k].ip, | ||
243 | }, | 238 | }, |
244 | }; | 239 | }; |
245 | 240 | ||
241 | sample.pid = fake_samples[i][k].pid; | ||
242 | sample.ip = fake_samples[i][k].ip; | ||
246 | if (perf_event__preprocess_sample(&event, machine, &al, | 243 | if (perf_event__preprocess_sample(&event, machine, &al, |
247 | &sample, 0) < 0) | 244 | &sample) < 0) |
248 | goto out; | 245 | goto out; |
249 | 246 | ||
250 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); | 247 | he = __hists__add_entry(&evsel->hists, &al, NULL, 1, 1); |
diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c new file mode 100644 index 000000000000..376c35608534 --- /dev/null +++ b/tools/perf/tests/keep-tracking.c | |||
@@ -0,0 +1,155 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <unistd.h> | ||
3 | #include <sys/prctl.h> | ||
4 | |||
5 | #include "parse-events.h" | ||
6 | #include "evlist.h" | ||
7 | #include "evsel.h" | ||
8 | #include "thread_map.h" | ||
9 | #include "cpumap.h" | ||
10 | #include "tests.h" | ||
11 | |||
12 | #define CHECK__(x) { \ | ||
13 | while ((x) < 0) { \ | ||
14 | pr_debug(#x " failed!\n"); \ | ||
15 | goto out_err; \ | ||
16 | } \ | ||
17 | } | ||
18 | |||
19 | #define CHECK_NOT_NULL__(x) { \ | ||
20 | while ((x) == NULL) { \ | ||
21 | pr_debug(#x " failed!\n"); \ | ||
22 | goto out_err; \ | ||
23 | } \ | ||
24 | } | ||
25 | |||
26 | static int find_comm(struct perf_evlist *evlist, const char *comm) | ||
27 | { | ||
28 | union perf_event *event; | ||
29 | int i, found; | ||
30 | |||
31 | found = 0; | ||
32 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
33 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
34 | if (event->header.type == PERF_RECORD_COMM && | ||
35 | (pid_t)event->comm.pid == getpid() && | ||
36 | (pid_t)event->comm.tid == getpid() && | ||
37 | strcmp(event->comm.comm, comm) == 0) | ||
38 | found += 1; | ||
39 | perf_evlist__mmap_consume(evlist, i); | ||
40 | } | ||
41 | } | ||
42 | return found; | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * test__keep_tracking - test using a dummy software event to keep tracking. | ||
47 | * | ||
48 | * This function implements a test that checks that tracking events continue | ||
49 | * when an event is disabled but a dummy software event is not disabled. If the | ||
50 | * test passes %0 is returned, otherwise %-1 is returned. | ||
51 | */ | ||
52 | int test__keep_tracking(void) | ||
53 | { | ||
54 | struct perf_record_opts opts = { | ||
55 | .mmap_pages = UINT_MAX, | ||
56 | .user_freq = UINT_MAX, | ||
57 | .user_interval = ULLONG_MAX, | ||
58 | .freq = 4000, | ||
59 | .target = { | ||
60 | .uses_mmap = true, | ||
61 | }, | ||
62 | }; | ||
63 | struct thread_map *threads = NULL; | ||
64 | struct cpu_map *cpus = NULL; | ||
65 | struct perf_evlist *evlist = NULL; | ||
66 | struct perf_evsel *evsel = NULL; | ||
67 | int found, err = -1; | ||
68 | const char *comm; | ||
69 | |||
70 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
71 | CHECK_NOT_NULL__(threads); | ||
72 | |||
73 | cpus = cpu_map__new(NULL); | ||
74 | CHECK_NOT_NULL__(cpus); | ||
75 | |||
76 | evlist = perf_evlist__new(); | ||
77 | CHECK_NOT_NULL__(evlist); | ||
78 | |||
79 | perf_evlist__set_maps(evlist, cpus, threads); | ||
80 | |||
81 | CHECK__(parse_events(evlist, "dummy:u")); | ||
82 | CHECK__(parse_events(evlist, "cycles:u")); | ||
83 | |||
84 | perf_evlist__config(evlist, &opts); | ||
85 | |||
86 | evsel = perf_evlist__first(evlist); | ||
87 | |||
88 | evsel->attr.comm = 1; | ||
89 | evsel->attr.disabled = 1; | ||
90 | evsel->attr.enable_on_exec = 0; | ||
91 | |||
92 | if (perf_evlist__open(evlist) < 0) { | ||
93 | fprintf(stderr, " (not supported)"); | ||
94 | err = 0; | ||
95 | goto out_err; | ||
96 | } | ||
97 | |||
98 | CHECK__(perf_evlist__mmap(evlist, UINT_MAX, false)); | ||
99 | |||
100 | /* | ||
101 | * First, test that a 'comm' event can be found when the event is | ||
102 | * enabled. | ||
103 | */ | ||
104 | |||
105 | perf_evlist__enable(evlist); | ||
106 | |||
107 | comm = "Test COMM 1"; | ||
108 | CHECK__(prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0)); | ||
109 | |||
110 | perf_evlist__disable(evlist); | ||
111 | |||
112 | found = find_comm(evlist, comm); | ||
113 | if (found != 1) { | ||
114 | pr_debug("First time, failed to find tracking event.\n"); | ||
115 | goto out_err; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Secondly, test that a 'comm' event can be found when the event is | ||
120 | * disabled with the dummy event still enabled. | ||
121 | */ | ||
122 | |||
123 | perf_evlist__enable(evlist); | ||
124 | |||
125 | evsel = perf_evlist__last(evlist); | ||
126 | |||
127 | CHECK__(perf_evlist__disable_event(evlist, evsel)); | ||
128 | |||
129 | comm = "Test COMM 2"; | ||
130 | CHECK__(prctl(PR_SET_NAME, (unsigned long)comm, 0, 0, 0)); | ||
131 | |||
132 | perf_evlist__disable(evlist); | ||
133 | |||
134 | found = find_comm(evlist, comm); | ||
135 | if (found != 1) { | ||
136 | pr_debug("Seconf time, failed to find tracking event.\n"); | ||
137 | goto out_err; | ||
138 | } | ||
139 | |||
140 | err = 0; | ||
141 | |||
142 | out_err: | ||
143 | if (evlist) { | ||
144 | perf_evlist__disable(evlist); | ||
145 | perf_evlist__munmap(evlist); | ||
146 | perf_evlist__close(evlist); | ||
147 | perf_evlist__delete(evlist); | ||
148 | } | ||
149 | if (cpus) | ||
150 | cpu_map__delete(cpus); | ||
151 | if (threads) | ||
152 | thread_map__delete(threads); | ||
153 | |||
154 | return err; | ||
155 | } | ||
diff --git a/tools/perf/tests/make b/tools/perf/tests/make index c441a2875128..2ca0abf1b2b6 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make | |||
@@ -1,6 +1,8 @@ | |||
1 | PERF := . | 1 | PERF := . |
2 | MK := Makefile | 2 | MK := Makefile |
3 | 3 | ||
4 | has = $(shell which $1 2>/dev/null) | ||
5 | |||
4 | # standard single make variable specified | 6 | # standard single make variable specified |
5 | make_clean_all := clean all | 7 | make_clean_all := clean all |
6 | make_python_perf_so := python/perf.so | 8 | make_python_perf_so := python/perf.so |
@@ -25,6 +27,13 @@ make_help := help | |||
25 | make_doc := doc | 27 | make_doc := doc |
26 | make_perf_o := perf.o | 28 | make_perf_o := perf.o |
27 | make_util_map_o := util/map.o | 29 | make_util_map_o := util/map.o |
30 | make_install := install | ||
31 | make_install_bin := install-bin | ||
32 | make_install_doc := install-doc | ||
33 | make_install_man := install-man | ||
34 | make_install_html := install-html | ||
35 | make_install_info := install-info | ||
36 | make_install_pdf := install-pdf | ||
28 | 37 | ||
29 | # all the NO_* variable combined | 38 | # all the NO_* variable combined |
30 | make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1 | 39 | make_minimal := NO_LIBPERL=1 NO_LIBPYTHON=1 NO_NEWT=1 NO_GTK2=1 |
@@ -50,14 +59,27 @@ run += make_no_backtrace | |||
50 | run += make_no_libnuma | 59 | run += make_no_libnuma |
51 | run += make_no_libaudit | 60 | run += make_no_libaudit |
52 | run += make_no_libbionic | 61 | run += make_no_libbionic |
53 | run += make_tags | ||
54 | run += make_cscope | ||
55 | run += make_help | 62 | run += make_help |
56 | run += make_doc | 63 | run += make_doc |
57 | run += make_perf_o | 64 | run += make_perf_o |
58 | run += make_util_map_o | 65 | run += make_util_map_o |
66 | run += make_install | ||
67 | run += make_install_bin | ||
68 | # FIXME 'install-*' commented out till they're fixed | ||
69 | # run += make_install_doc | ||
70 | # run += make_install_man | ||
71 | # run += make_install_html | ||
72 | # run += make_install_info | ||
73 | # run += make_install_pdf | ||
59 | run += make_minimal | 74 | run += make_minimal |
60 | 75 | ||
76 | ifneq ($(call has,ctags),) | ||
77 | run += make_tags | ||
78 | endif | ||
79 | ifneq ($(call has,cscope),) | ||
80 | run += make_cscope | ||
81 | endif | ||
82 | |||
61 | # $(run_O) contains same portion of $(run) tests with '_O' attached | 83 | # $(run_O) contains same portion of $(run) tests with '_O' attached |
62 | # to distinguish O=... tests | 84 | # to distinguish O=... tests |
63 | run_O := $(addsuffix _O,$(run)) | 85 | run_O := $(addsuffix _O,$(run)) |
@@ -84,6 +106,31 @@ test_make_python_perf_so := test -f $(PERF)/python/perf.so | |||
84 | test_make_perf_o := test -f $(PERF)/perf.o | 106 | test_make_perf_o := test -f $(PERF)/perf.o |
85 | test_make_util_map_o := test -f $(PERF)/util/map.o | 107 | test_make_util_map_o := test -f $(PERF)/util/map.o |
86 | 108 | ||
109 | test_make_install := test -x $$TMP_DEST/bin/perf | ||
110 | test_make_install_O := $(test_make_install) | ||
111 | test_make_install_bin := $(test_make_install) | ||
112 | test_make_install_bin_O := $(test_make_install) | ||
113 | |||
114 | # FIXME nothing gets installed | ||
115 | test_make_install_man := test -f $$TMP_DEST/share/man/man1/perf.1 | ||
116 | test_make_install_man_O := $(test_make_install_man) | ||
117 | |||
118 | # FIXME nothing gets installed | ||
119 | test_make_install_doc := $(test_ok) | ||
120 | test_make_install_doc_O := $(test_ok) | ||
121 | |||
122 | # FIXME nothing gets installed | ||
123 | test_make_install_html := $(test_ok) | ||
124 | test_make_install_html_O := $(test_ok) | ||
125 | |||
126 | # FIXME nothing gets installed | ||
127 | test_make_install_info := $(test_ok) | ||
128 | test_make_install_info_O := $(test_ok) | ||
129 | |||
130 | # FIXME nothing gets installed | ||
131 | test_make_install_pdf := $(test_ok) | ||
132 | test_make_install_pdf_O := $(test_ok) | ||
133 | |||
87 | # Kbuild tests only | 134 | # Kbuild tests only |
88 | #test_make_python_perf_so_O := test -f $$TMP/tools/perf/python/perf.so | 135 | #test_make_python_perf_so_O := test -f $$TMP/tools/perf/python/perf.so |
89 | #test_make_perf_o_O := test -f $$TMP/tools/perf/perf.o | 136 | #test_make_perf_o_O := test -f $$TMP/tools/perf/perf.o |
@@ -95,7 +142,7 @@ test_make_util_map_o_O := true | |||
95 | test_default = test -x $(PERF)/perf | 142 | test_default = test -x $(PERF)/perf |
96 | test = $(if $(test_$1),$(test_$1),$(test_default)) | 143 | test = $(if $(test_$1),$(test_$1),$(test_default)) |
97 | 144 | ||
98 | test_default_O = test -x $$TMP/perf | 145 | test_default_O = test -x $$TMP_O/perf |
99 | test_O = $(if $(test_$1),$(test_$1),$(test_default_O)) | 146 | test_O = $(if $(test_$1),$(test_$1),$(test_default_O)) |
100 | 147 | ||
101 | all: | 148 | all: |
@@ -111,23 +158,27 @@ clean := @(cd $(PERF); make -s -f $(MK) clean >/dev/null) | |||
111 | 158 | ||
112 | $(run): | 159 | $(run): |
113 | $(call clean) | 160 | $(call clean) |
114 | @cmd="cd $(PERF) && make -f $(MK) $($@)"; \ | 161 | @TMP_DEST=$$(mktemp -d); \ |
162 | cmd="cd $(PERF) && make -f $(MK) DESTDIR=$$TMP_DEST $($@)"; \ | ||
115 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ | 163 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ |
116 | ( eval $$cmd ) >> $@ 2>&1; \ | 164 | ( eval $$cmd ) >> $@ 2>&1; \ |
117 | echo " test: $(call test,$@)"; \ | 165 | echo " test: $(call test,$@)"; \ |
118 | $(call test,$@) && \ | 166 | $(call test,$@) && \ |
119 | rm -f $@ | 167 | rm -f $@ \ |
168 | rm -rf $$TMP_DEST | ||
120 | 169 | ||
121 | $(run_O): | 170 | $(run_O): |
122 | $(call clean) | 171 | $(call clean) |
123 | @TMP=$$(mktemp -d); \ | 172 | @TMP_O=$$(mktemp -d); \ |
124 | cmd="cd $(PERF) && make -f $(MK) $($(patsubst %_O,%,$@)) O=$$TMP"; \ | 173 | TMP_DEST=$$(mktemp -d); \ |
174 | cmd="cd $(PERF) && make -f $(MK) O=$$TMP_O DESTDIR=$$TMP_DEST $($(patsubst %_O,%,$@))"; \ | ||
125 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ | 175 | echo "- $@: $$cmd" && echo $$cmd > $@ && \ |
126 | ( eval $$cmd ) >> $@ 2>&1 && \ | 176 | ( eval $$cmd ) >> $@ 2>&1 && \ |
127 | echo " test: $(call test_O,$@)"; \ | 177 | echo " test: $(call test_O,$@)"; \ |
128 | $(call test_O,$@) && \ | 178 | $(call test_O,$@) && \ |
129 | rm -f $@ && \ | 179 | rm -f $@ && \ |
130 | rm -rf $$TMP | 180 | rm -rf $$TMP_O \ |
181 | rm -rf $$TMP_DEST | ||
131 | 182 | ||
132 | all: $(run) $(run_O) | 183 | all: $(run) $(run_O) |
133 | @echo OK | 184 | @echo OK |
diff --git a/tools/perf/tests/mmap-basic.c b/tools/perf/tests/mmap-basic.c index 5b1b5aba722b..a7232c204eb9 100644 --- a/tools/perf/tests/mmap-basic.c +++ b/tools/perf/tests/mmap-basic.c | |||
@@ -72,7 +72,7 @@ int test__basic_mmap(void) | |||
72 | } | 72 | } |
73 | 73 | ||
74 | evsels[i]->attr.wakeup_events = 1; | 74 | evsels[i]->attr.wakeup_events = 1; |
75 | perf_evsel__set_sample_id(evsels[i]); | 75 | perf_evsel__set_sample_id(evsels[i], false); |
76 | 76 | ||
77 | perf_evlist__add(evlist, evsels[i]); | 77 | perf_evlist__add(evlist, evsels[i]); |
78 | 78 | ||
@@ -122,6 +122,7 @@ int test__basic_mmap(void) | |||
122 | goto out_munmap; | 122 | goto out_munmap; |
123 | } | 123 | } |
124 | nr_events[evsel->idx]++; | 124 | nr_events[evsel->idx]++; |
125 | perf_evlist__mmap_consume(evlist, 0); | ||
125 | } | 126 | } |
126 | 127 | ||
127 | err = 0; | 128 | err = 0; |
diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index fc5b9fca8b47..524b221b829b 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c | |||
@@ -77,8 +77,10 @@ int test__syscall_open_tp_fields(void) | |||
77 | 77 | ||
78 | ++nr_events; | 78 | ++nr_events; |
79 | 79 | ||
80 | if (type != PERF_RECORD_SAMPLE) | 80 | if (type != PERF_RECORD_SAMPLE) { |
81 | perf_evlist__mmap_consume(evlist, i); | ||
81 | continue; | 82 | continue; |
83 | } | ||
82 | 84 | ||
83 | err = perf_evsel__parse_sample(evsel, event, &sample); | 85 | err = perf_evsel__parse_sample(evsel, event, &sample); |
84 | if (err) { | 86 | if (err) { |
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 0275bab4ea9e..48114d164e9f 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c | |||
@@ -7,14 +7,6 @@ | |||
7 | #include "tests.h" | 7 | #include "tests.h" |
8 | #include <linux/hw_breakpoint.h> | 8 | #include <linux/hw_breakpoint.h> |
9 | 9 | ||
10 | #define TEST_ASSERT_VAL(text, cond) \ | ||
11 | do { \ | ||
12 | if (!(cond)) { \ | ||
13 | pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ | ||
14 | return -1; \ | ||
15 | } \ | ||
16 | } while (0) | ||
17 | |||
18 | #define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \ | 10 | #define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \ |
19 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD) | 11 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD) |
20 | 12 | ||
@@ -460,6 +452,7 @@ static int test__checkevent_pmu_events(struct perf_evlist *evlist) | |||
460 | evsel->attr.exclude_kernel); | 452 | evsel->attr.exclude_kernel); |
461 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | 453 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); |
462 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 454 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
455 | TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned); | ||
463 | 456 | ||
464 | return 0; | 457 | return 0; |
465 | } | 458 | } |
@@ -528,6 +521,7 @@ static int test__group1(struct perf_evlist *evlist) | |||
528 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | 521 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
529 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | 522 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); |
530 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | 523 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); |
524 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
531 | 525 | ||
532 | /* cycles:upp */ | 526 | /* cycles:upp */ |
533 | evsel = perf_evsel__next(evsel); | 527 | evsel = perf_evsel__next(evsel); |
@@ -543,6 +537,7 @@ static int test__group1(struct perf_evlist *evlist) | |||
543 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); | 537 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); |
544 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 538 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
545 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | 539 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); |
540 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
546 | 541 | ||
547 | return 0; | 542 | return 0; |
548 | } | 543 | } |
@@ -568,6 +563,7 @@ static int test__group2(struct perf_evlist *evlist) | |||
568 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | 563 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
569 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | 564 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); |
570 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | 565 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); |
566 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
571 | 567 | ||
572 | /* cache-references + :u modifier */ | 568 | /* cache-references + :u modifier */ |
573 | evsel = perf_evsel__next(evsel); | 569 | evsel = perf_evsel__next(evsel); |
@@ -582,6 +578,7 @@ static int test__group2(struct perf_evlist *evlist) | |||
582 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 578 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
583 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 579 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
584 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | 580 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); |
581 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
585 | 582 | ||
586 | /* cycles:k */ | 583 | /* cycles:k */ |
587 | evsel = perf_evsel__next(evsel); | 584 | evsel = perf_evsel__next(evsel); |
@@ -595,6 +592,7 @@ static int test__group2(struct perf_evlist *evlist) | |||
595 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 592 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
596 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 593 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
597 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | 594 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
595 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
598 | 596 | ||
599 | return 0; | 597 | return 0; |
600 | } | 598 | } |
@@ -623,6 +621,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
623 | !strcmp(leader->group_name, "group1")); | 621 | !strcmp(leader->group_name, "group1")); |
624 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | 622 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); |
625 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | 623 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); |
624 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
626 | 625 | ||
627 | /* group1 cycles:kppp */ | 626 | /* group1 cycles:kppp */ |
628 | evsel = perf_evsel__next(evsel); | 627 | evsel = perf_evsel__next(evsel); |
@@ -639,6 +638,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
639 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 638 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
640 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | 639 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); |
641 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | 640 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); |
641 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
642 | 642 | ||
643 | /* group2 cycles + G modifier */ | 643 | /* group2 cycles + G modifier */ |
644 | evsel = leader = perf_evsel__next(evsel); | 644 | evsel = leader = perf_evsel__next(evsel); |
@@ -656,6 +656,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
656 | !strcmp(leader->group_name, "group2")); | 656 | !strcmp(leader->group_name, "group2")); |
657 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | 657 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); |
658 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | 658 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); |
659 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
659 | 660 | ||
660 | /* group2 1:3 + G modifier */ | 661 | /* group2 1:3 + G modifier */ |
661 | evsel = perf_evsel__next(evsel); | 662 | evsel = perf_evsel__next(evsel); |
@@ -669,6 +670,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
669 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 670 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
670 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 671 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
671 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | 672 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); |
673 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
672 | 674 | ||
673 | /* instructions:u */ | 675 | /* instructions:u */ |
674 | evsel = perf_evsel__next(evsel); | 676 | evsel = perf_evsel__next(evsel); |
@@ -682,6 +684,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) | |||
682 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | 684 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); |
683 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 685 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
684 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | 686 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
687 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
685 | 688 | ||
686 | return 0; | 689 | return 0; |
687 | } | 690 | } |
@@ -709,6 +712,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) | |||
709 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | 712 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
710 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | 713 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); |
711 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | 714 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); |
715 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
712 | 716 | ||
713 | /* instructions:kp + p */ | 717 | /* instructions:kp + p */ |
714 | evsel = perf_evsel__next(evsel); | 718 | evsel = perf_evsel__next(evsel); |
@@ -724,6 +728,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) | |||
724 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); | 728 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); |
725 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 729 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
726 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | 730 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); |
731 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
727 | 732 | ||
728 | return 0; | 733 | return 0; |
729 | } | 734 | } |
@@ -750,6 +755,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
750 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | 755 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
751 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | 756 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); |
752 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | 757 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); |
758 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
753 | 759 | ||
754 | /* instructions + G */ | 760 | /* instructions + G */ |
755 | evsel = perf_evsel__next(evsel); | 761 | evsel = perf_evsel__next(evsel); |
@@ -764,6 +770,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
764 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | 770 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); |
765 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | 771 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); |
766 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); | 772 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1); |
773 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
767 | 774 | ||
768 | /* cycles:G */ | 775 | /* cycles:G */ |
769 | evsel = leader = perf_evsel__next(evsel); | 776 | evsel = leader = perf_evsel__next(evsel); |
@@ -780,6 +787,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) | |||
780 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); | 787 | TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); |
781 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); | 788 | TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2); |
782 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); | 789 | TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0); |
790 | TEST_ASSERT_VAL("wrong sample_read", !evsel->sample_read); | ||
783 | 791 | ||
784 | /* instructions:G */ | 792 | /* instructions:G */ |
785 | evsel = perf_evsel__next(evsel); | 793 | evsel = perf_evsel__next(evsel); |
@@ -971,6 +979,142 @@ static int test__group_gh4(struct perf_evlist *evlist) | |||
971 | return 0; | 979 | return 0; |
972 | } | 980 | } |
973 | 981 | ||
982 | static int test__leader_sample1(struct perf_evlist *evlist) | ||
983 | { | ||
984 | struct perf_evsel *evsel, *leader; | ||
985 | |||
986 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); | ||
987 | |||
988 | /* cycles - sampling group leader */ | ||
989 | evsel = leader = perf_evlist__first(evlist); | ||
990 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
991 | TEST_ASSERT_VAL("wrong config", | ||
992 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
993 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
994 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
995 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
996 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
997 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
998 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
999 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
1000 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
1001 | TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); | ||
1002 | |||
1003 | /* cache-misses - not sampling */ | ||
1004 | evsel = perf_evsel__next(evsel); | ||
1005 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
1006 | TEST_ASSERT_VAL("wrong config", | ||
1007 | PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config); | ||
1008 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
1009 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
1010 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
1011 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
1012 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
1013 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
1014 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
1015 | TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); | ||
1016 | |||
1017 | /* branch-misses - not sampling */ | ||
1018 | evsel = perf_evsel__next(evsel); | ||
1019 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
1020 | TEST_ASSERT_VAL("wrong config", | ||
1021 | PERF_COUNT_HW_BRANCH_MISSES == evsel->attr.config); | ||
1022 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
1023 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
1024 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
1025 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
1026 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
1027 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
1028 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
1029 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
1030 | TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); | ||
1031 | |||
1032 | return 0; | ||
1033 | } | ||
1034 | |||
1035 | static int test__leader_sample2(struct perf_evlist *evlist __maybe_unused) | ||
1036 | { | ||
1037 | struct perf_evsel *evsel, *leader; | ||
1038 | |||
1039 | TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); | ||
1040 | |||
1041 | /* instructions - sampling group leader */ | ||
1042 | evsel = leader = perf_evlist__first(evlist); | ||
1043 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
1044 | TEST_ASSERT_VAL("wrong config", | ||
1045 | PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config); | ||
1046 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
1047 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
1048 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
1049 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
1050 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
1051 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
1052 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
1053 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
1054 | TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); | ||
1055 | |||
1056 | /* branch-misses - not sampling */ | ||
1057 | evsel = perf_evsel__next(evsel); | ||
1058 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
1059 | TEST_ASSERT_VAL("wrong config", | ||
1060 | PERF_COUNT_HW_BRANCH_MISSES == evsel->attr.config); | ||
1061 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
1062 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
1063 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
1064 | TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest); | ||
1065 | TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); | ||
1066 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
1067 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
1068 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
1069 | TEST_ASSERT_VAL("wrong sample_read", evsel->sample_read); | ||
1070 | |||
1071 | return 0; | ||
1072 | } | ||
1073 | |||
1074 | static int test__checkevent_pinned_modifier(struct perf_evlist *evlist) | ||
1075 | { | ||
1076 | struct perf_evsel *evsel = perf_evlist__first(evlist); | ||
1077 | |||
1078 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
1079 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
1080 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
1081 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | ||
1082 | TEST_ASSERT_VAL("wrong pinned", evsel->attr.pinned); | ||
1083 | |||
1084 | return test__checkevent_symbolic_name(evlist); | ||
1085 | } | ||
1086 | |||
1087 | static int test__pinned_group(struct perf_evlist *evlist) | ||
1088 | { | ||
1089 | struct perf_evsel *evsel, *leader; | ||
1090 | |||
1091 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); | ||
1092 | |||
1093 | /* cycles - group leader */ | ||
1094 | evsel = leader = perf_evlist__first(evlist); | ||
1095 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
1096 | TEST_ASSERT_VAL("wrong config", | ||
1097 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
1098 | TEST_ASSERT_VAL("wrong group name", !evsel->group_name); | ||
1099 | TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); | ||
1100 | TEST_ASSERT_VAL("wrong pinned", evsel->attr.pinned); | ||
1101 | |||
1102 | /* cache-misses - can not be pinned, but will go on with the leader */ | ||
1103 | evsel = perf_evsel__next(evsel); | ||
1104 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
1105 | TEST_ASSERT_VAL("wrong config", | ||
1106 | PERF_COUNT_HW_CACHE_MISSES == evsel->attr.config); | ||
1107 | TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned); | ||
1108 | |||
1109 | /* branch-misses - ditto */ | ||
1110 | evsel = perf_evsel__next(evsel); | ||
1111 | TEST_ASSERT_VAL("wrong config", | ||
1112 | PERF_COUNT_HW_BRANCH_MISSES == evsel->attr.config); | ||
1113 | TEST_ASSERT_VAL("wrong pinned", !evsel->attr.pinned); | ||
1114 | |||
1115 | return 0; | ||
1116 | } | ||
1117 | |||
974 | static int count_tracepoints(void) | 1118 | static int count_tracepoints(void) |
975 | { | 1119 | { |
976 | char events_path[PATH_MAX]; | 1120 | char events_path[PATH_MAX]; |
@@ -1187,6 +1331,22 @@ static struct evlist_test test__events[] = { | |||
1187 | .name = "{cycles:G,cache-misses:H}:uG", | 1331 | .name = "{cycles:G,cache-misses:H}:uG", |
1188 | .check = test__group_gh4, | 1332 | .check = test__group_gh4, |
1189 | }, | 1333 | }, |
1334 | [38] = { | ||
1335 | .name = "{cycles,cache-misses,branch-misses}:S", | ||
1336 | .check = test__leader_sample1, | ||
1337 | }, | ||
1338 | [39] = { | ||
1339 | .name = "{instructions,branch-misses}:Su", | ||
1340 | .check = test__leader_sample2, | ||
1341 | }, | ||
1342 | [40] = { | ||
1343 | .name = "instructions:uDp", | ||
1344 | .check = test__checkevent_pinned_modifier, | ||
1345 | }, | ||
1346 | [41] = { | ||
1347 | .name = "{cycles,cache-misses,branch-misses}:D", | ||
1348 | .check = test__pinned_group, | ||
1349 | }, | ||
1190 | }; | 1350 | }; |
1191 | 1351 | ||
1192 | static struct evlist_test test__events_pmu[] = { | 1352 | static struct evlist_test test__events_pmu[] = { |
@@ -1254,24 +1414,20 @@ static int test_events(struct evlist_test *events, unsigned cnt) | |||
1254 | 1414 | ||
1255 | static int test_term(struct terms_test *t) | 1415 | static int test_term(struct terms_test *t) |
1256 | { | 1416 | { |
1257 | struct list_head *terms; | 1417 | struct list_head terms; |
1258 | int ret; | 1418 | int ret; |
1259 | 1419 | ||
1260 | terms = malloc(sizeof(*terms)); | 1420 | INIT_LIST_HEAD(&terms); |
1261 | if (!terms) | ||
1262 | return -ENOMEM; | ||
1263 | |||
1264 | INIT_LIST_HEAD(terms); | ||
1265 | 1421 | ||
1266 | ret = parse_events_terms(terms, t->str); | 1422 | ret = parse_events_terms(&terms, t->str); |
1267 | if (ret) { | 1423 | if (ret) { |
1268 | pr_debug("failed to parse terms '%s', err %d\n", | 1424 | pr_debug("failed to parse terms '%s', err %d\n", |
1269 | t->str , ret); | 1425 | t->str , ret); |
1270 | return ret; | 1426 | return ret; |
1271 | } | 1427 | } |
1272 | 1428 | ||
1273 | ret = t->check(terms); | 1429 | ret = t->check(&terms); |
1274 | parse_events__free_terms(terms); | 1430 | parse_events__free_terms(&terms); |
1275 | 1431 | ||
1276 | return ret; | 1432 | return ret; |
1277 | } | 1433 | } |
diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c new file mode 100644 index 000000000000..e117b6c6a248 --- /dev/null +++ b/tools/perf/tests/parse-no-sample-id-all.c | |||
@@ -0,0 +1,108 @@ | |||
1 | #include <sys/types.h> | ||
2 | #include <stddef.h> | ||
3 | |||
4 | #include "tests.h" | ||
5 | |||
6 | #include "event.h" | ||
7 | #include "evlist.h" | ||
8 | #include "header.h" | ||
9 | #include "util.h" | ||
10 | |||
11 | static int process_event(struct perf_evlist **pevlist, union perf_event *event) | ||
12 | { | ||
13 | struct perf_sample sample; | ||
14 | |||
15 | if (event->header.type == PERF_RECORD_HEADER_ATTR) { | ||
16 | if (perf_event__process_attr(NULL, event, pevlist)) { | ||
17 | pr_debug("perf_event__process_attr failed\n"); | ||
18 | return -1; | ||
19 | } | ||
20 | return 0; | ||
21 | } | ||
22 | |||
23 | if (event->header.type >= PERF_RECORD_USER_TYPE_START) | ||
24 | return -1; | ||
25 | |||
26 | if (!*pevlist) | ||
27 | return -1; | ||
28 | |||
29 | if (perf_evlist__parse_sample(*pevlist, event, &sample)) { | ||
30 | pr_debug("perf_evlist__parse_sample failed\n"); | ||
31 | return -1; | ||
32 | } | ||
33 | |||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | static int process_events(union perf_event **events, size_t count) | ||
38 | { | ||
39 | struct perf_evlist *evlist = NULL; | ||
40 | int err = 0; | ||
41 | size_t i; | ||
42 | |||
43 | for (i = 0; i < count && !err; i++) | ||
44 | err = process_event(&evlist, events[i]); | ||
45 | |||
46 | if (evlist) | ||
47 | perf_evlist__delete(evlist); | ||
48 | |||
49 | return err; | ||
50 | } | ||
51 | |||
52 | struct test_attr_event { | ||
53 | struct attr_event attr; | ||
54 | u64 id; | ||
55 | }; | ||
56 | |||
57 | /** | ||
58 | * test__parse_no_sample_id_all - test parsing with no sample_id_all bit set. | ||
59 | * | ||
60 | * This function tests parsing data produced on kernel's that do not support the | ||
61 | * sample_id_all bit. Without the sample_id_all bit, non-sample events (such as | ||
62 | * mmap events) do not have an id sample appended, and consequently logic | ||
63 | * designed to determine the id will not work. That case happens when there is | ||
64 | * more than one selected event, so this test processes three events: 2 | ||
65 | * attributes representing the selected events and one mmap event. | ||
66 | * | ||
67 | * Return: %0 on success, %-1 if the test fails. | ||
68 | */ | ||
69 | int test__parse_no_sample_id_all(void) | ||
70 | { | ||
71 | int err; | ||
72 | |||
73 | struct test_attr_event event1 = { | ||
74 | .attr = { | ||
75 | .header = { | ||
76 | .type = PERF_RECORD_HEADER_ATTR, | ||
77 | .size = sizeof(struct test_attr_event), | ||
78 | }, | ||
79 | }, | ||
80 | .id = 1, | ||
81 | }; | ||
82 | struct test_attr_event event2 = { | ||
83 | .attr = { | ||
84 | .header = { | ||
85 | .type = PERF_RECORD_HEADER_ATTR, | ||
86 | .size = sizeof(struct test_attr_event), | ||
87 | }, | ||
88 | }, | ||
89 | .id = 2, | ||
90 | }; | ||
91 | struct mmap_event event3 = { | ||
92 | .header = { | ||
93 | .type = PERF_RECORD_MMAP, | ||
94 | .size = sizeof(struct mmap_event), | ||
95 | }, | ||
96 | }; | ||
97 | union perf_event *events[] = { | ||
98 | (union perf_event *)&event1, | ||
99 | (union perf_event *)&event2, | ||
100 | (union perf_event *)&event3, | ||
101 | }; | ||
102 | |||
103 | err = process_events(events, ARRAY_SIZE(events)); | ||
104 | if (err) | ||
105 | return -1; | ||
106 | |||
107 | return 0; | ||
108 | } | ||
diff --git a/tools/perf/tests/perf-record.c b/tools/perf/tests/perf-record.c index 72d8881873b0..7923b06ffc91 100644 --- a/tools/perf/tests/perf-record.c +++ b/tools/perf/tests/perf-record.c | |||
@@ -50,7 +50,7 @@ int test__PERF_RECORD(void) | |||
50 | struct perf_sample sample; | 50 | struct perf_sample sample; |
51 | const char *cmd = "sleep"; | 51 | const char *cmd = "sleep"; |
52 | const char *argv[] = { cmd, "1", NULL, }; | 52 | const char *argv[] = { cmd, "1", NULL, }; |
53 | char *bname; | 53 | char *bname, *mmap_filename; |
54 | u64 prev_time = 0; | 54 | u64 prev_time = 0; |
55 | bool found_cmd_mmap = false, | 55 | bool found_cmd_mmap = false, |
56 | found_libc_mmap = false, | 56 | found_libc_mmap = false, |
@@ -212,6 +212,7 @@ int test__PERF_RECORD(void) | |||
212 | 212 | ||
213 | if ((type == PERF_RECORD_COMM || | 213 | if ((type == PERF_RECORD_COMM || |
214 | type == PERF_RECORD_MMAP || | 214 | type == PERF_RECORD_MMAP || |
215 | type == PERF_RECORD_MMAP2 || | ||
215 | type == PERF_RECORD_FORK || | 216 | type == PERF_RECORD_FORK || |
216 | type == PERF_RECORD_EXIT) && | 217 | type == PERF_RECORD_EXIT) && |
217 | (pid_t)event->comm.pid != evlist->workload.pid) { | 218 | (pid_t)event->comm.pid != evlist->workload.pid) { |
@@ -220,7 +221,8 @@ int test__PERF_RECORD(void) | |||
220 | } | 221 | } |
221 | 222 | ||
222 | if ((type == PERF_RECORD_COMM || | 223 | if ((type == PERF_RECORD_COMM || |
223 | type == PERF_RECORD_MMAP) && | 224 | type == PERF_RECORD_MMAP || |
225 | type == PERF_RECORD_MMAP2) && | ||
224 | event->comm.pid != event->comm.tid) { | 226 | event->comm.pid != event->comm.tid) { |
225 | pr_debug("%s with different pid/tid!\n", name); | 227 | pr_debug("%s with different pid/tid!\n", name); |
226 | ++errs; | 228 | ++errs; |
@@ -236,7 +238,12 @@ int test__PERF_RECORD(void) | |||
236 | case PERF_RECORD_EXIT: | 238 | case PERF_RECORD_EXIT: |
237 | goto found_exit; | 239 | goto found_exit; |
238 | case PERF_RECORD_MMAP: | 240 | case PERF_RECORD_MMAP: |
239 | bname = strrchr(event->mmap.filename, '/'); | 241 | mmap_filename = event->mmap.filename; |
242 | goto check_bname; | ||
243 | case PERF_RECORD_MMAP2: | ||
244 | mmap_filename = event->mmap2.filename; | ||
245 | check_bname: | ||
246 | bname = strrchr(mmap_filename, '/'); | ||
240 | if (bname != NULL) { | 247 | if (bname != NULL) { |
241 | if (!found_cmd_mmap) | 248 | if (!found_cmd_mmap) |
242 | found_cmd_mmap = !strcmp(bname + 1, cmd); | 249 | found_cmd_mmap = !strcmp(bname + 1, cmd); |
@@ -245,7 +252,7 @@ int test__PERF_RECORD(void) | |||
245 | if (!found_ld_mmap) | 252 | if (!found_ld_mmap) |
246 | found_ld_mmap = !strncmp(bname + 1, "ld", 2); | 253 | found_ld_mmap = !strncmp(bname + 1, "ld", 2); |
247 | } else if (!found_vdso_mmap) | 254 | } else if (!found_vdso_mmap) |
248 | found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]"); | 255 | found_vdso_mmap = !strcmp(mmap_filename, "[vdso]"); |
249 | break; | 256 | break; |
250 | 257 | ||
251 | case PERF_RECORD_SAMPLE: | 258 | case PERF_RECORD_SAMPLE: |
@@ -256,6 +263,8 @@ int test__PERF_RECORD(void) | |||
256 | type); | 263 | type); |
257 | ++errs; | 264 | ++errs; |
258 | } | 265 | } |
266 | |||
267 | perf_evlist__mmap_consume(evlist, i); | ||
259 | } | 268 | } |
260 | } | 269 | } |
261 | 270 | ||
diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c new file mode 100644 index 000000000000..4ca1b938f6a6 --- /dev/null +++ b/tools/perf/tests/perf-time-to-tsc.c | |||
@@ -0,0 +1,179 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <sys/types.h> | ||
3 | #include <unistd.h> | ||
4 | #include <inttypes.h> | ||
5 | #include <sys/prctl.h> | ||
6 | |||
7 | #include "parse-events.h" | ||
8 | #include "evlist.h" | ||
9 | #include "evsel.h" | ||
10 | #include "thread_map.h" | ||
11 | #include "cpumap.h" | ||
12 | #include "tests.h" | ||
13 | |||
14 | #include "../arch/x86/util/tsc.h" | ||
15 | |||
16 | #define CHECK__(x) { \ | ||
17 | while ((x) < 0) { \ | ||
18 | pr_debug(#x " failed!\n"); \ | ||
19 | goto out_err; \ | ||
20 | } \ | ||
21 | } | ||
22 | |||
23 | #define CHECK_NOT_NULL__(x) { \ | ||
24 | while ((x) == NULL) { \ | ||
25 | pr_debug(#x " failed!\n"); \ | ||
26 | goto out_err; \ | ||
27 | } \ | ||
28 | } | ||
29 | |||
30 | static u64 rdtsc(void) | ||
31 | { | ||
32 | unsigned int low, high; | ||
33 | |||
34 | asm volatile("rdtsc" : "=a" (low), "=d" (high)); | ||
35 | |||
36 | return low | ((u64)high) << 32; | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * test__perf_time_to_tsc - test converting perf time to TSC. | ||
41 | * | ||
42 | * This function implements a test that checks that the conversion of perf time | ||
43 | * to and from TSC is consistent with the order of events. If the test passes | ||
44 | * %0 is returned, otherwise %-1 is returned. If TSC conversion is not | ||
45 | * supported then then the test passes but " (not supported)" is printed. | ||
46 | */ | ||
47 | int test__perf_time_to_tsc(void) | ||
48 | { | ||
49 | struct perf_record_opts opts = { | ||
50 | .mmap_pages = UINT_MAX, | ||
51 | .user_freq = UINT_MAX, | ||
52 | .user_interval = ULLONG_MAX, | ||
53 | .freq = 4000, | ||
54 | .target = { | ||
55 | .uses_mmap = true, | ||
56 | }, | ||
57 | .sample_time = true, | ||
58 | }; | ||
59 | struct thread_map *threads = NULL; | ||
60 | struct cpu_map *cpus = NULL; | ||
61 | struct perf_evlist *evlist = NULL; | ||
62 | struct perf_evsel *evsel = NULL; | ||
63 | int err = -1, ret, i; | ||
64 | const char *comm1, *comm2; | ||
65 | struct perf_tsc_conversion tc; | ||
66 | struct perf_event_mmap_page *pc; | ||
67 | union perf_event *event; | ||
68 | u64 test_tsc, comm1_tsc, comm2_tsc; | ||
69 | u64 test_time, comm1_time = 0, comm2_time = 0; | ||
70 | |||
71 | threads = thread_map__new(-1, getpid(), UINT_MAX); | ||
72 | CHECK_NOT_NULL__(threads); | ||
73 | |||
74 | cpus = cpu_map__new(NULL); | ||
75 | CHECK_NOT_NULL__(cpus); | ||
76 | |||
77 | evlist = perf_evlist__new(); | ||
78 | CHECK_NOT_NULL__(evlist); | ||
79 | |||
80 | perf_evlist__set_maps(evlist, cpus, threads); | ||
81 | |||
82 | CHECK__(parse_events(evlist, "cycles:u")); | ||
83 | |||
84 | perf_evlist__config(evlist, &opts); | ||
85 | |||
86 | evsel = perf_evlist__first(evlist); | ||
87 | |||
88 | evsel->attr.comm = 1; | ||
89 | evsel->attr.disabled = 1; | ||
90 | evsel->attr.enable_on_exec = 0; | ||
91 | |||
92 | CHECK__(perf_evlist__open(evlist)); | ||
93 | |||
94 | CHECK__(perf_evlist__mmap(evlist, UINT_MAX, false)); | ||
95 | |||
96 | pc = evlist->mmap[0].base; | ||
97 | ret = perf_read_tsc_conversion(pc, &tc); | ||
98 | if (ret) { | ||
99 | if (ret == -EOPNOTSUPP) { | ||
100 | fprintf(stderr, " (not supported)"); | ||
101 | return 0; | ||
102 | } | ||
103 | goto out_err; | ||
104 | } | ||
105 | |||
106 | perf_evlist__enable(evlist); | ||
107 | |||
108 | comm1 = "Test COMM 1"; | ||
109 | CHECK__(prctl(PR_SET_NAME, (unsigned long)comm1, 0, 0, 0)); | ||
110 | |||
111 | test_tsc = rdtsc(); | ||
112 | |||
113 | comm2 = "Test COMM 2"; | ||
114 | CHECK__(prctl(PR_SET_NAME, (unsigned long)comm2, 0, 0, 0)); | ||
115 | |||
116 | perf_evlist__disable(evlist); | ||
117 | |||
118 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
119 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
120 | struct perf_sample sample; | ||
121 | |||
122 | if (event->header.type != PERF_RECORD_COMM || | ||
123 | (pid_t)event->comm.pid != getpid() || | ||
124 | (pid_t)event->comm.tid != getpid()) | ||
125 | goto next_event; | ||
126 | |||
127 | if (strcmp(event->comm.comm, comm1) == 0) { | ||
128 | CHECK__(perf_evsel__parse_sample(evsel, event, | ||
129 | &sample)); | ||
130 | comm1_time = sample.time; | ||
131 | } | ||
132 | if (strcmp(event->comm.comm, comm2) == 0) { | ||
133 | CHECK__(perf_evsel__parse_sample(evsel, event, | ||
134 | &sample)); | ||
135 | comm2_time = sample.time; | ||
136 | } | ||
137 | next_event: | ||
138 | perf_evlist__mmap_consume(evlist, i); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | if (!comm1_time || !comm2_time) | ||
143 | goto out_err; | ||
144 | |||
145 | test_time = tsc_to_perf_time(test_tsc, &tc); | ||
146 | comm1_tsc = perf_time_to_tsc(comm1_time, &tc); | ||
147 | comm2_tsc = perf_time_to_tsc(comm2_time, &tc); | ||
148 | |||
149 | pr_debug("1st event perf time %"PRIu64" tsc %"PRIu64"\n", | ||
150 | comm1_time, comm1_tsc); | ||
151 | pr_debug("rdtsc time %"PRIu64" tsc %"PRIu64"\n", | ||
152 | test_time, test_tsc); | ||
153 | pr_debug("2nd event perf time %"PRIu64" tsc %"PRIu64"\n", | ||
154 | comm2_time, comm2_tsc); | ||
155 | |||
156 | if (test_time <= comm1_time || | ||
157 | test_time >= comm2_time) | ||
158 | goto out_err; | ||
159 | |||
160 | if (test_tsc <= comm1_tsc || | ||
161 | test_tsc >= comm2_tsc) | ||
162 | goto out_err; | ||
163 | |||
164 | err = 0; | ||
165 | |||
166 | out_err: | ||
167 | if (evlist) { | ||
168 | perf_evlist__disable(evlist); | ||
169 | perf_evlist__munmap(evlist); | ||
170 | perf_evlist__close(evlist); | ||
171 | perf_evlist__delete(evlist); | ||
172 | } | ||
173 | if (cpus) | ||
174 | cpu_map__delete(cpus); | ||
175 | if (threads) | ||
176 | thread_map__delete(threads); | ||
177 | |||
178 | return err; | ||
179 | } | ||
diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c new file mode 100644 index 000000000000..77f598dbd97a --- /dev/null +++ b/tools/perf/tests/sample-parsing.c | |||
@@ -0,0 +1,316 @@ | |||
1 | #include <stdbool.h> | ||
2 | #include <inttypes.h> | ||
3 | |||
4 | #include "util.h" | ||
5 | #include "event.h" | ||
6 | #include "evsel.h" | ||
7 | |||
8 | #include "tests.h" | ||
9 | |||
10 | #define COMP(m) do { \ | ||
11 | if (s1->m != s2->m) { \ | ||
12 | pr_debug("Samples differ at '"#m"'\n"); \ | ||
13 | return false; \ | ||
14 | } \ | ||
15 | } while (0) | ||
16 | |||
17 | #define MCOMP(m) do { \ | ||
18 | if (memcmp(&s1->m, &s2->m, sizeof(s1->m))) { \ | ||
19 | pr_debug("Samples differ at '"#m"'\n"); \ | ||
20 | return false; \ | ||
21 | } \ | ||
22 | } while (0) | ||
23 | |||
24 | static bool samples_same(const struct perf_sample *s1, | ||
25 | const struct perf_sample *s2, u64 type, u64 regs_user, | ||
26 | u64 read_format) | ||
27 | { | ||
28 | size_t i; | ||
29 | |||
30 | if (type & PERF_SAMPLE_IDENTIFIER) | ||
31 | COMP(id); | ||
32 | |||
33 | if (type & PERF_SAMPLE_IP) | ||
34 | COMP(ip); | ||
35 | |||
36 | if (type & PERF_SAMPLE_TID) { | ||
37 | COMP(pid); | ||
38 | COMP(tid); | ||
39 | } | ||
40 | |||
41 | if (type & PERF_SAMPLE_TIME) | ||
42 | COMP(time); | ||
43 | |||
44 | if (type & PERF_SAMPLE_ADDR) | ||
45 | COMP(addr); | ||
46 | |||
47 | if (type & PERF_SAMPLE_ID) | ||
48 | COMP(id); | ||
49 | |||
50 | if (type & PERF_SAMPLE_STREAM_ID) | ||
51 | COMP(stream_id); | ||
52 | |||
53 | if (type & PERF_SAMPLE_CPU) | ||
54 | COMP(cpu); | ||
55 | |||
56 | if (type & PERF_SAMPLE_PERIOD) | ||
57 | COMP(period); | ||
58 | |||
59 | if (type & PERF_SAMPLE_READ) { | ||
60 | if (read_format & PERF_FORMAT_GROUP) | ||
61 | COMP(read.group.nr); | ||
62 | else | ||
63 | COMP(read.one.value); | ||
64 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
65 | COMP(read.time_enabled); | ||
66 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
67 | COMP(read.time_running); | ||
68 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | ||
69 | if (read_format & PERF_FORMAT_GROUP) { | ||
70 | for (i = 0; i < s1->read.group.nr; i++) | ||
71 | MCOMP(read.group.values[i]); | ||
72 | } else { | ||
73 | COMP(read.one.id); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
78 | COMP(callchain->nr); | ||
79 | for (i = 0; i < s1->callchain->nr; i++) | ||
80 | COMP(callchain->ips[i]); | ||
81 | } | ||
82 | |||
83 | if (type & PERF_SAMPLE_RAW) { | ||
84 | COMP(raw_size); | ||
85 | if (memcmp(s1->raw_data, s2->raw_data, s1->raw_size)) { | ||
86 | pr_debug("Samples differ at 'raw_data'\n"); | ||
87 | return false; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
92 | COMP(branch_stack->nr); | ||
93 | for (i = 0; i < s1->branch_stack->nr; i++) | ||
94 | MCOMP(branch_stack->entries[i]); | ||
95 | } | ||
96 | |||
97 | if (type & PERF_SAMPLE_REGS_USER) { | ||
98 | size_t sz = hweight_long(regs_user) * sizeof(u64); | ||
99 | |||
100 | COMP(user_regs.abi); | ||
101 | if (s1->user_regs.abi && | ||
102 | (!s1->user_regs.regs || !s2->user_regs.regs || | ||
103 | memcmp(s1->user_regs.regs, s2->user_regs.regs, sz))) { | ||
104 | pr_debug("Samples differ at 'user_regs'\n"); | ||
105 | return false; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | if (type & PERF_SAMPLE_STACK_USER) { | ||
110 | COMP(user_stack.size); | ||
111 | if (memcmp(s1->user_stack.data, s1->user_stack.data, | ||
112 | s1->user_stack.size)) { | ||
113 | pr_debug("Samples differ at 'user_stack'\n"); | ||
114 | return false; | ||
115 | } | ||
116 | } | ||
117 | |||
118 | if (type & PERF_SAMPLE_WEIGHT) | ||
119 | COMP(weight); | ||
120 | |||
121 | if (type & PERF_SAMPLE_DATA_SRC) | ||
122 | COMP(data_src); | ||
123 | |||
124 | return true; | ||
125 | } | ||
126 | |||
127 | static int do_test(u64 sample_type, u64 sample_regs_user, u64 read_format) | ||
128 | { | ||
129 | struct perf_evsel evsel = { | ||
130 | .needs_swap = false, | ||
131 | .attr = { | ||
132 | .sample_type = sample_type, | ||
133 | .sample_regs_user = sample_regs_user, | ||
134 | .read_format = read_format, | ||
135 | }, | ||
136 | }; | ||
137 | union perf_event *event; | ||
138 | union { | ||
139 | struct ip_callchain callchain; | ||
140 | u64 data[64]; | ||
141 | } callchain = { | ||
142 | /* 3 ips */ | ||
143 | .data = {3, 201, 202, 203}, | ||
144 | }; | ||
145 | union { | ||
146 | struct branch_stack branch_stack; | ||
147 | u64 data[64]; | ||
148 | } branch_stack = { | ||
149 | /* 1 branch_entry */ | ||
150 | .data = {1, 211, 212, 213}, | ||
151 | }; | ||
152 | u64 user_regs[64]; | ||
153 | const u64 raw_data[] = {0x123456780a0b0c0dULL, 0x1102030405060708ULL}; | ||
154 | const u64 data[] = {0x2211443366558877ULL, 0, 0xaabbccddeeff4321ULL}; | ||
155 | struct perf_sample sample = { | ||
156 | .ip = 101, | ||
157 | .pid = 102, | ||
158 | .tid = 103, | ||
159 | .time = 104, | ||
160 | .addr = 105, | ||
161 | .id = 106, | ||
162 | .stream_id = 107, | ||
163 | .period = 108, | ||
164 | .weight = 109, | ||
165 | .cpu = 110, | ||
166 | .raw_size = sizeof(raw_data), | ||
167 | .data_src = 111, | ||
168 | .raw_data = (void *)raw_data, | ||
169 | .callchain = &callchain.callchain, | ||
170 | .branch_stack = &branch_stack.branch_stack, | ||
171 | .user_regs = { | ||
172 | .abi = PERF_SAMPLE_REGS_ABI_64, | ||
173 | .regs = user_regs, | ||
174 | }, | ||
175 | .user_stack = { | ||
176 | .size = sizeof(data), | ||
177 | .data = (void *)data, | ||
178 | }, | ||
179 | .read = { | ||
180 | .time_enabled = 0x030a59d664fca7deULL, | ||
181 | .time_running = 0x011b6ae553eb98edULL, | ||
182 | }, | ||
183 | }; | ||
184 | struct sample_read_value values[] = {{1, 5}, {9, 3}, {2, 7}, {6, 4},}; | ||
185 | struct perf_sample sample_out; | ||
186 | size_t i, sz, bufsz; | ||
187 | int err, ret = -1; | ||
188 | |||
189 | for (i = 0; i < sizeof(user_regs); i++) | ||
190 | *(i + (u8 *)user_regs) = i & 0xfe; | ||
191 | |||
192 | if (read_format & PERF_FORMAT_GROUP) { | ||
193 | sample.read.group.nr = 4; | ||
194 | sample.read.group.values = values; | ||
195 | } else { | ||
196 | sample.read.one.value = 0x08789faeb786aa87ULL; | ||
197 | sample.read.one.id = 99; | ||
198 | } | ||
199 | |||
200 | sz = perf_event__sample_event_size(&sample, sample_type, | ||
201 | sample_regs_user, read_format); | ||
202 | bufsz = sz + 4096; /* Add a bit for overrun checking */ | ||
203 | event = malloc(bufsz); | ||
204 | if (!event) { | ||
205 | pr_debug("malloc failed\n"); | ||
206 | return -1; | ||
207 | } | ||
208 | |||
209 | memset(event, 0xff, bufsz); | ||
210 | event->header.type = PERF_RECORD_SAMPLE; | ||
211 | event->header.misc = 0; | ||
212 | event->header.size = sz; | ||
213 | |||
214 | err = perf_event__synthesize_sample(event, sample_type, | ||
215 | sample_regs_user, read_format, | ||
216 | &sample, false); | ||
217 | if (err) { | ||
218 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", | ||
219 | "perf_event__synthesize_sample", sample_type, err); | ||
220 | goto out_free; | ||
221 | } | ||
222 | |||
223 | /* The data does not contain 0xff so we use that to check the size */ | ||
224 | for (i = bufsz; i > 0; i--) { | ||
225 | if (*(i - 1 + (u8 *)event) != 0xff) | ||
226 | break; | ||
227 | } | ||
228 | if (i != sz) { | ||
229 | pr_debug("Event size mismatch: actual %zu vs expected %zu\n", | ||
230 | i, sz); | ||
231 | goto out_free; | ||
232 | } | ||
233 | |||
234 | evsel.sample_size = __perf_evsel__sample_size(sample_type); | ||
235 | |||
236 | err = perf_evsel__parse_sample(&evsel, event, &sample_out); | ||
237 | if (err) { | ||
238 | pr_debug("%s failed for sample_type %#"PRIx64", error %d\n", | ||
239 | "perf_evsel__parse_sample", sample_type, err); | ||
240 | goto out_free; | ||
241 | } | ||
242 | |||
243 | if (!samples_same(&sample, &sample_out, sample_type, | ||
244 | sample_regs_user, read_format)) { | ||
245 | pr_debug("parsing failed for sample_type %#"PRIx64"\n", | ||
246 | sample_type); | ||
247 | goto out_free; | ||
248 | } | ||
249 | |||
250 | ret = 0; | ||
251 | out_free: | ||
252 | free(event); | ||
253 | if (ret && read_format) | ||
254 | pr_debug("read_format %#"PRIx64"\n", read_format); | ||
255 | return ret; | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * test__sample_parsing - test sample parsing. | ||
260 | * | ||
261 | * This function implements a test that synthesizes a sample event, parses it | ||
262 | * and then checks that the parsed sample matches the original sample. The test | ||
263 | * checks sample format bits separately and together. If the test passes %0 is | ||
264 | * returned, otherwise %-1 is returned. | ||
265 | */ | ||
266 | int test__sample_parsing(void) | ||
267 | { | ||
268 | const u64 rf[] = {4, 5, 6, 7, 12, 13, 14, 15}; | ||
269 | u64 sample_type; | ||
270 | u64 sample_regs_user; | ||
271 | size_t i; | ||
272 | int err; | ||
273 | |||
274 | /* | ||
275 | * Fail the test if it has not been updated when new sample format bits | ||
276 | * were added. | ||
277 | */ | ||
278 | if (PERF_SAMPLE_MAX > PERF_SAMPLE_IDENTIFIER << 1) { | ||
279 | pr_debug("sample format has changed - test needs updating\n"); | ||
280 | return -1; | ||
281 | } | ||
282 | |||
283 | /* Test each sample format bit separately */ | ||
284 | for (sample_type = 1; sample_type != PERF_SAMPLE_MAX; | ||
285 | sample_type <<= 1) { | ||
286 | /* Test read_format variations */ | ||
287 | if (sample_type == PERF_SAMPLE_READ) { | ||
288 | for (i = 0; i < ARRAY_SIZE(rf); i++) { | ||
289 | err = do_test(sample_type, 0, rf[i]); | ||
290 | if (err) | ||
291 | return err; | ||
292 | } | ||
293 | continue; | ||
294 | } | ||
295 | |||
296 | if (sample_type == PERF_SAMPLE_REGS_USER) | ||
297 | sample_regs_user = 0x3fff; | ||
298 | else | ||
299 | sample_regs_user = 0; | ||
300 | |||
301 | err = do_test(sample_type, sample_regs_user, 0); | ||
302 | if (err) | ||
303 | return err; | ||
304 | } | ||
305 | |||
306 | /* Test all sample format bits together */ | ||
307 | sample_type = PERF_SAMPLE_MAX - 1; | ||
308 | sample_regs_user = 0x3fff; | ||
309 | for (i = 0; i < ARRAY_SIZE(rf); i++) { | ||
310 | err = do_test(sample_type, sample_regs_user, rf[i]); | ||
311 | if (err) | ||
312 | return err; | ||
313 | } | ||
314 | |||
315 | return 0; | ||
316 | } | ||
diff --git a/tools/perf/tests/sw-clock.c b/tools/perf/tests/sw-clock.c index 2e41e2d32ccc..6e2b44ec0749 100644 --- a/tools/perf/tests/sw-clock.c +++ b/tools/perf/tests/sw-clock.c | |||
@@ -78,7 +78,7 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | |||
78 | struct perf_sample sample; | 78 | struct perf_sample sample; |
79 | 79 | ||
80 | if (event->header.type != PERF_RECORD_SAMPLE) | 80 | if (event->header.type != PERF_RECORD_SAMPLE) |
81 | continue; | 81 | goto next_event; |
82 | 82 | ||
83 | err = perf_evlist__parse_sample(evlist, event, &sample); | 83 | err = perf_evlist__parse_sample(evlist, event, &sample); |
84 | if (err < 0) { | 84 | if (err < 0) { |
@@ -88,6 +88,8 @@ static int __test__sw_clock_freq(enum perf_sw_ids clock_id) | |||
88 | 88 | ||
89 | total_periods += sample.period; | 89 | total_periods += sample.period; |
90 | nr_samples++; | 90 | nr_samples++; |
91 | next_event: | ||
92 | perf_evlist__mmap_consume(evlist, 0); | ||
91 | } | 93 | } |
92 | 94 | ||
93 | if ((u64) nr_samples == total_periods) { | 95 | if ((u64) nr_samples == total_periods) { |
diff --git a/tools/perf/tests/task-exit.c b/tools/perf/tests/task-exit.c index 28fe5894b061..a3e64876e940 100644 --- a/tools/perf/tests/task-exit.c +++ b/tools/perf/tests/task-exit.c | |||
@@ -96,10 +96,10 @@ int test__task_exit(void) | |||
96 | 96 | ||
97 | retry: | 97 | retry: |
98 | while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { | 98 | while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) { |
99 | if (event->header.type != PERF_RECORD_EXIT) | 99 | if (event->header.type == PERF_RECORD_EXIT) |
100 | continue; | 100 | nr_exit++; |
101 | 101 | ||
102 | nr_exit++; | 102 | perf_evlist__mmap_consume(evlist, 0); |
103 | } | 103 | } |
104 | 104 | ||
105 | if (!exited || !nr_exit) { | 105 | if (!exited || !nr_exit) { |
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index dd7feae2d37b..e0ac713857ba 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -1,6 +1,14 @@ | |||
1 | #ifndef TESTS_H | 1 | #ifndef TESTS_H |
2 | #define TESTS_H | 2 | #define TESTS_H |
3 | 3 | ||
4 | #define TEST_ASSERT_VAL(text, cond) \ | ||
5 | do { \ | ||
6 | if (!(cond)) { \ | ||
7 | pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \ | ||
8 | return -1; \ | ||
9 | } \ | ||
10 | } while (0) | ||
11 | |||
4 | enum { | 12 | enum { |
5 | TEST_OK = 0, | 13 | TEST_OK = 0, |
6 | TEST_FAIL = -1, | 14 | TEST_FAIL = -1, |
@@ -27,5 +35,10 @@ int test__bp_signal(void); | |||
27 | int test__bp_signal_overflow(void); | 35 | int test__bp_signal_overflow(void); |
28 | int test__task_exit(void); | 36 | int test__task_exit(void); |
29 | int test__sw_clock_freq(void); | 37 | int test__sw_clock_freq(void); |
38 | int test__perf_time_to_tsc(void); | ||
39 | int test__code_reading(void); | ||
40 | int test__sample_parsing(void); | ||
41 | int test__keep_tracking(void); | ||
42 | int test__parse_no_sample_id_all(void); | ||
30 | 43 | ||
31 | #endif /* TESTS_H */ | 44 | #endif /* TESTS_H */ |
diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index 7b4c4d26d1ba..2bd13edcbc17 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c | |||
@@ -16,6 +16,8 @@ static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused, | |||
16 | return 0; | 16 | return 0; |
17 | } | 17 | } |
18 | 18 | ||
19 | #define UM(x) kallsyms_map->unmap_ip(kallsyms_map, (x)) | ||
20 | |||
19 | int test__vmlinux_matches_kallsyms(void) | 21 | int test__vmlinux_matches_kallsyms(void) |
20 | { | 22 | { |
21 | int err = -1; | 23 | int err = -1; |
@@ -25,6 +27,7 @@ int test__vmlinux_matches_kallsyms(void) | |||
25 | struct machine kallsyms, vmlinux; | 27 | struct machine kallsyms, vmlinux; |
26 | enum map_type type = MAP__FUNCTION; | 28 | enum map_type type = MAP__FUNCTION; |
27 | struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; | 29 | struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", }; |
30 | u64 mem_start, mem_end; | ||
28 | 31 | ||
29 | /* | 32 | /* |
30 | * Step 1: | 33 | * Step 1: |
@@ -73,7 +76,7 @@ int test__vmlinux_matches_kallsyms(void) | |||
73 | goto out; | 76 | goto out; |
74 | } | 77 | } |
75 | 78 | ||
76 | ref_reloc_sym.addr = sym->start; | 79 | ref_reloc_sym.addr = UM(sym->start); |
77 | 80 | ||
78 | /* | 81 | /* |
79 | * Step 5: | 82 | * Step 5: |
@@ -123,10 +126,14 @@ int test__vmlinux_matches_kallsyms(void) | |||
123 | if (sym->start == sym->end) | 126 | if (sym->start == sym->end) |
124 | continue; | 127 | continue; |
125 | 128 | ||
126 | first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL); | 129 | mem_start = vmlinux_map->unmap_ip(vmlinux_map, sym->start); |
130 | mem_end = vmlinux_map->unmap_ip(vmlinux_map, sym->end); | ||
131 | |||
132 | first_pair = machine__find_kernel_symbol(&kallsyms, type, | ||
133 | mem_start, NULL, NULL); | ||
127 | pair = first_pair; | 134 | pair = first_pair; |
128 | 135 | ||
129 | if (pair && pair->start == sym->start) { | 136 | if (pair && UM(pair->start) == mem_start) { |
130 | next_pair: | 137 | next_pair: |
131 | if (strcmp(sym->name, pair->name) == 0) { | 138 | if (strcmp(sym->name, pair->name) == 0) { |
132 | /* | 139 | /* |
@@ -138,12 +145,20 @@ next_pair: | |||
138 | * off the real size. More than that and we | 145 | * off the real size. More than that and we |
139 | * _really_ have a problem. | 146 | * _really_ have a problem. |
140 | */ | 147 | */ |
141 | s64 skew = sym->end - pair->end; | 148 | s64 skew = mem_end - UM(pair->end); |
142 | if (llabs(skew) < page_size) | 149 | if (llabs(skew) >= page_size) |
143 | continue; | 150 | pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n", |
151 | mem_start, sym->name, mem_end, | ||
152 | UM(pair->end)); | ||
153 | |||
154 | /* | ||
155 | * Do not count this as a failure, because we | ||
156 | * could really find a case where it's not | ||
157 | * possible to get proper function end from | ||
158 | * kallsyms. | ||
159 | */ | ||
160 | continue; | ||
144 | 161 | ||
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 { | 162 | } else { |
148 | struct rb_node *nnd; | 163 | struct rb_node *nnd; |
149 | detour: | 164 | detour: |
@@ -152,7 +167,7 @@ detour: | |||
152 | if (nnd) { | 167 | if (nnd) { |
153 | struct symbol *next = rb_entry(nnd, struct symbol, rb_node); | 168 | struct symbol *next = rb_entry(nnd, struct symbol, rb_node); |
154 | 169 | ||
155 | if (next->start == sym->start) { | 170 | if (UM(next->start) == mem_start) { |
156 | pair = next; | 171 | pair = next; |
157 | goto next_pair; | 172 | goto next_pair; |
158 | } | 173 | } |
@@ -165,10 +180,11 @@ detour: | |||
165 | } | 180 | } |
166 | 181 | ||
167 | pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", | 182 | pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n", |
168 | sym->start, sym->name, pair->name); | 183 | mem_start, sym->name, pair->name); |
169 | } | 184 | } |
170 | } else | 185 | } else |
171 | pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name); | 186 | pr_debug("%#" PRIx64 ": %s not on kallsyms\n", |
187 | mem_start, sym->name); | ||
172 | 188 | ||
173 | err = -1; | 189 | err = -1; |
174 | } | 190 | } |
@@ -201,16 +217,19 @@ detour: | |||
201 | for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) { | 217 | 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; | 218 | struct map *pos = rb_entry(nd, struct map, rb_node), *pair; |
203 | 219 | ||
204 | pair = map_groups__find(&kallsyms.kmaps, type, pos->start); | 220 | mem_start = vmlinux_map->unmap_ip(vmlinux_map, pos->start); |
221 | mem_end = vmlinux_map->unmap_ip(vmlinux_map, pos->end); | ||
222 | |||
223 | pair = map_groups__find(&kallsyms.kmaps, type, mem_start); | ||
205 | if (pair == NULL || pair->priv) | 224 | if (pair == NULL || pair->priv) |
206 | continue; | 225 | continue; |
207 | 226 | ||
208 | if (pair->start == pos->start) { | 227 | if (pair->start == mem_start) { |
209 | pair->priv = 1; | 228 | pair->priv = 1; |
210 | pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", | 229 | pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as", |
211 | pos->start, pos->end, pos->pgoff, pos->dso->name); | 230 | pos->start, pos->end, pos->pgoff, pos->dso->name); |
212 | if (pos->pgoff != pair->pgoff || pos->end != pair->end) | 231 | if (mem_end != pair->end) |
213 | pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "", | 232 | pr_info(":\n*%" PRIx64 "-%" PRIx64 " %" PRIx64, |
214 | pair->start, pair->end, pair->pgoff); | 233 | pair->start, pair->end, pair->pgoff); |
215 | pr_info(" %s\n", pair->dso->name); | 234 | pr_info(" %s\n", pair->dso->name); |
216 | pair->priv = 1; | 235 | pair->priv = 1; |
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index cc64d3f7fc36..08545ae46992 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
@@ -428,6 +428,14 @@ static void annotate_browser__init_asm_mode(struct annotate_browser *browser) | |||
428 | browser->b.nr_entries = browser->nr_asm_entries; | 428 | browser->b.nr_entries = browser->nr_asm_entries; |
429 | } | 429 | } |
430 | 430 | ||
431 | #define SYM_TITLE_MAX_SIZE (PATH_MAX + 64) | ||
432 | |||
433 | static int sym_title(struct symbol *sym, struct map *map, char *title, | ||
434 | size_t sz) | ||
435 | { | ||
436 | return snprintf(title, sz, "%s %s", sym->name, map->dso->long_name); | ||
437 | } | ||
438 | |||
431 | static bool annotate_browser__callq(struct annotate_browser *browser, | 439 | static bool annotate_browser__callq(struct annotate_browser *browser, |
432 | struct perf_evsel *evsel, | 440 | struct perf_evsel *evsel, |
433 | struct hist_browser_timer *hbt) | 441 | struct hist_browser_timer *hbt) |
@@ -438,6 +446,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, | |||
438 | struct annotation *notes; | 446 | struct annotation *notes; |
439 | struct symbol *target; | 447 | struct symbol *target; |
440 | u64 ip; | 448 | u64 ip; |
449 | char title[SYM_TITLE_MAX_SIZE]; | ||
441 | 450 | ||
442 | if (!ins__is_call(dl->ins)) | 451 | if (!ins__is_call(dl->ins)) |
443 | return false; | 452 | return false; |
@@ -461,7 +470,8 @@ static bool annotate_browser__callq(struct annotate_browser *browser, | |||
461 | 470 | ||
462 | pthread_mutex_unlock(¬es->lock); | 471 | pthread_mutex_unlock(¬es->lock); |
463 | symbol__tui_annotate(target, ms->map, evsel, hbt); | 472 | symbol__tui_annotate(target, ms->map, evsel, hbt); |
464 | ui_browser__show_title(&browser->b, sym->name); | 473 | sym_title(sym, ms->map, title, sizeof(title)); |
474 | ui_browser__show_title(&browser->b, title); | ||
465 | return true; | 475 | return true; |
466 | } | 476 | } |
467 | 477 | ||
@@ -495,7 +505,7 @@ static bool annotate_browser__jump(struct annotate_browser *browser) | |||
495 | 505 | ||
496 | dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx); | 506 | dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx); |
497 | if (dl == NULL) { | 507 | if (dl == NULL) { |
498 | ui_helpline__puts("Invallid jump offset"); | 508 | ui_helpline__puts("Invalid jump offset"); |
499 | return true; | 509 | return true; |
500 | } | 510 | } |
501 | 511 | ||
@@ -653,8 +663,10 @@ static int annotate_browser__run(struct annotate_browser *browser, | |||
653 | const char *help = "Press 'h' for help on key bindings"; | 663 | const char *help = "Press 'h' for help on key bindings"; |
654 | int delay_secs = hbt ? hbt->refresh : 0; | 664 | int delay_secs = hbt ? hbt->refresh : 0; |
655 | int key; | 665 | int key; |
666 | char title[SYM_TITLE_MAX_SIZE]; | ||
656 | 667 | ||
657 | if (ui_browser__show(&browser->b, sym->name, help) < 0) | 668 | sym_title(sym, ms->map, title, sizeof(title)); |
669 | if (ui_browser__show(&browser->b, title, help) < 0) | ||
658 | return -1; | 670 | return -1; |
659 | 671 | ||
660 | annotate_browser__calc_percent(browser, evsel); | 672 | annotate_browser__calc_percent(browser, evsel); |
@@ -720,7 +732,7 @@ static int annotate_browser__run(struct annotate_browser *browser, | |||
720 | "s Toggle source code view\n" | 732 | "s Toggle source code view\n" |
721 | "/ Search string\n" | 733 | "/ Search string\n" |
722 | "r Run available scripts\n" | 734 | "r Run available scripts\n" |
723 | "? Search previous string\n"); | 735 | "? Search string backwards\n"); |
724 | continue; | 736 | continue; |
725 | case 'r': | 737 | case 'r': |
726 | { | 738 | { |
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index fc0bd3843d34..7ef36c360471 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c | |||
@@ -685,8 +685,10 @@ static u64 __hpp_get_##_field(struct hist_entry *he) \ | |||
685 | return he->stat._field; \ | 685 | return he->stat._field; \ |
686 | } \ | 686 | } \ |
687 | \ | 687 | \ |
688 | static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \ | 688 | static int \ |
689 | struct hist_entry *he) \ | 689 | hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused,\ |
690 | struct perf_hpp *hpp, \ | ||
691 | struct hist_entry *he) \ | ||
690 | { \ | 692 | { \ |
691 | return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \ | 693 | return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \ |
692 | } | 694 | } |
@@ -701,8 +703,6 @@ __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL) | |||
701 | 703 | ||
702 | void hist_browser__init_hpp(void) | 704 | void hist_browser__init_hpp(void) |
703 | { | 705 | { |
704 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
705 | |||
706 | perf_hpp__init(); | 706 | perf_hpp__init(); |
707 | 707 | ||
708 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | 708 | perf_hpp__format[PERF_HPP__OVERHEAD].color = |
@@ -762,9 +762,9 @@ static int hist_browser__show_entry(struct hist_browser *browser, | |||
762 | first = false; | 762 | first = false; |
763 | 763 | ||
764 | if (fmt->color) { | 764 | if (fmt->color) { |
765 | width -= fmt->color(&hpp, entry); | 765 | width -= fmt->color(fmt, &hpp, entry); |
766 | } else { | 766 | } else { |
767 | width -= fmt->entry(&hpp, entry); | 767 | width -= fmt->entry(fmt, &hpp, entry); |
768 | slsmg_printf("%s", s); | 768 | slsmg_printf("%s", s); |
769 | } | 769 | } |
770 | } | 770 | } |
@@ -1256,7 +1256,7 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size, | |||
1256 | printed += scnprintf(bf + printed, size - printed, | 1256 | printed += scnprintf(bf + printed, size - printed, |
1257 | ", Thread: %s(%d)", | 1257 | ", Thread: %s(%d)", |
1258 | (thread->comm_set ? thread->comm : ""), | 1258 | (thread->comm_set ? thread->comm : ""), |
1259 | thread->pid); | 1259 | thread->tid); |
1260 | if (dso) | 1260 | if (dso) |
1261 | printed += scnprintf(bf + printed, size - printed, | 1261 | printed += scnprintf(bf + printed, size - printed, |
1262 | ", DSO: %s", dso->short_name); | 1262 | ", DSO: %s", dso->short_name); |
@@ -1579,7 +1579,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
1579 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", | 1579 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", |
1580 | (browser->hists->thread_filter ? "out of" : "into"), | 1580 | (browser->hists->thread_filter ? "out of" : "into"), |
1581 | (thread->comm_set ? thread->comm : ""), | 1581 | (thread->comm_set ? thread->comm : ""), |
1582 | thread->pid) > 0) | 1582 | thread->tid) > 0) |
1583 | zoom_thread = nr_options++; | 1583 | zoom_thread = nr_options++; |
1584 | 1584 | ||
1585 | if (dso != NULL && | 1585 | if (dso != NULL && |
@@ -1702,7 +1702,7 @@ zoom_out_thread: | |||
1702 | } else { | 1702 | } else { |
1703 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", | 1703 | ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", |
1704 | thread->comm_set ? thread->comm : "", | 1704 | thread->comm_set ? thread->comm : "", |
1705 | thread->pid); | 1705 | thread->tid); |
1706 | browser->hists->thread_filter = thread; | 1706 | browser->hists->thread_filter = thread; |
1707 | sort_thread.elide = true; | 1707 | sort_thread.elide = true; |
1708 | pstack__push(fstack, &browser->hists->thread_filter); | 1708 | pstack__push(fstack, &browser->hists->thread_filter); |
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c index 9708dd5fb8f3..2ca66cc1160f 100644 --- a/tools/perf/ui/gtk/hists.c +++ b/tools/perf/ui/gtk/hists.c | |||
@@ -91,7 +91,8 @@ static u64 he_get_##_field(struct hist_entry *he) \ | |||
91 | return he->stat._field; \ | 91 | return he->stat._field; \ |
92 | } \ | 92 | } \ |
93 | \ | 93 | \ |
94 | static int perf_gtk__hpp_color_##_type(struct perf_hpp *hpp, \ | 94 | static int perf_gtk__hpp_color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ |
95 | struct perf_hpp *hpp, \ | ||
95 | struct hist_entry *he) \ | 96 | struct hist_entry *he) \ |
96 | { \ | 97 | { \ |
97 | return __hpp__color_fmt(hpp, he, he_get_##_field); \ | 98 | return __hpp__color_fmt(hpp, he, he_get_##_field); \ |
@@ -108,8 +109,6 @@ __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us) | |||
108 | 109 | ||
109 | void perf_gtk__init_hpp(void) | 110 | void perf_gtk__init_hpp(void) |
110 | { | 111 | { |
111 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
112 | |||
113 | perf_hpp__init(); | 112 | perf_hpp__init(); |
114 | 113 | ||
115 | perf_hpp__format[PERF_HPP__OVERHEAD].color = | 114 | perf_hpp__format[PERF_HPP__OVERHEAD].color = |
@@ -124,6 +123,81 @@ void perf_gtk__init_hpp(void) | |||
124 | perf_gtk__hpp_color_overhead_guest_us; | 123 | perf_gtk__hpp_color_overhead_guest_us; |
125 | } | 124 | } |
126 | 125 | ||
126 | static void callchain_list__sym_name(struct callchain_list *cl, | ||
127 | char *bf, size_t bfsize) | ||
128 | { | ||
129 | if (cl->ms.sym) | ||
130 | scnprintf(bf, bfsize, "%s", cl->ms.sym->name); | ||
131 | else | ||
132 | scnprintf(bf, bfsize, "%#" PRIx64, cl->ip); | ||
133 | } | ||
134 | |||
135 | static void perf_gtk__add_callchain(struct rb_root *root, GtkTreeStore *store, | ||
136 | GtkTreeIter *parent, int col, u64 total) | ||
137 | { | ||
138 | struct rb_node *nd; | ||
139 | bool has_single_node = (rb_first(root) == rb_last(root)); | ||
140 | |||
141 | for (nd = rb_first(root); nd; nd = rb_next(nd)) { | ||
142 | struct callchain_node *node; | ||
143 | struct callchain_list *chain; | ||
144 | GtkTreeIter iter, new_parent; | ||
145 | bool need_new_parent; | ||
146 | double percent; | ||
147 | u64 hits, child_total; | ||
148 | |||
149 | node = rb_entry(nd, struct callchain_node, rb_node); | ||
150 | |||
151 | hits = callchain_cumul_hits(node); | ||
152 | percent = 100.0 * hits / total; | ||
153 | |||
154 | new_parent = *parent; | ||
155 | need_new_parent = !has_single_node && (node->val_nr > 1); | ||
156 | |||
157 | list_for_each_entry(chain, &node->val, list) { | ||
158 | char buf[128]; | ||
159 | |||
160 | gtk_tree_store_append(store, &iter, &new_parent); | ||
161 | |||
162 | scnprintf(buf, sizeof(buf), "%5.2f%%", percent); | ||
163 | gtk_tree_store_set(store, &iter, 0, buf, -1); | ||
164 | |||
165 | callchain_list__sym_name(chain, buf, sizeof(buf)); | ||
166 | gtk_tree_store_set(store, &iter, col, buf, -1); | ||
167 | |||
168 | if (need_new_parent) { | ||
169 | /* | ||
170 | * Only show the top-most symbol in a callchain | ||
171 | * if it's not the only callchain. | ||
172 | */ | ||
173 | new_parent = iter; | ||
174 | need_new_parent = false; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
179 | child_total = node->children_hit; | ||
180 | else | ||
181 | child_total = total; | ||
182 | |||
183 | /* Now 'iter' contains info of the last callchain_list */ | ||
184 | perf_gtk__add_callchain(&node->rb_root, store, &iter, col, | ||
185 | child_total); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | static void on_row_activated(GtkTreeView *view, GtkTreePath *path, | ||
190 | GtkTreeViewColumn *col __maybe_unused, | ||
191 | gpointer user_data __maybe_unused) | ||
192 | { | ||
193 | bool expanded = gtk_tree_view_row_expanded(view, path); | ||
194 | |||
195 | if (expanded) | ||
196 | gtk_tree_view_collapse_row(view, path); | ||
197 | else | ||
198 | gtk_tree_view_expand_row(view, path, FALSE); | ||
199 | } | ||
200 | |||
127 | static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | 201 | static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, |
128 | float min_pcnt) | 202 | float min_pcnt) |
129 | { | 203 | { |
@@ -131,10 +205,11 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
131 | GType col_types[MAX_COLUMNS]; | 205 | GType col_types[MAX_COLUMNS]; |
132 | GtkCellRenderer *renderer; | 206 | GtkCellRenderer *renderer; |
133 | struct sort_entry *se; | 207 | struct sort_entry *se; |
134 | GtkListStore *store; | 208 | GtkTreeStore *store; |
135 | struct rb_node *nd; | 209 | struct rb_node *nd; |
136 | GtkWidget *view; | 210 | GtkWidget *view; |
137 | int col_idx; | 211 | int col_idx; |
212 | int sym_col = -1; | ||
138 | int nr_cols; | 213 | int nr_cols; |
139 | char s[512]; | 214 | char s[512]; |
140 | 215 | ||
@@ -153,10 +228,13 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
153 | if (se->elide) | 228 | if (se->elide) |
154 | continue; | 229 | continue; |
155 | 230 | ||
231 | if (se == &sort_sym) | ||
232 | sym_col = nr_cols; | ||
233 | |||
156 | col_types[nr_cols++] = G_TYPE_STRING; | 234 | col_types[nr_cols++] = G_TYPE_STRING; |
157 | } | 235 | } |
158 | 236 | ||
159 | store = gtk_list_store_newv(nr_cols, col_types); | 237 | store = gtk_tree_store_newv(nr_cols, col_types); |
160 | 238 | ||
161 | view = gtk_tree_view_new(); | 239 | view = gtk_tree_view_new(); |
162 | 240 | ||
@@ -165,7 +243,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
165 | col_idx = 0; | 243 | col_idx = 0; |
166 | 244 | ||
167 | perf_hpp__for_each_format(fmt) { | 245 | perf_hpp__for_each_format(fmt) { |
168 | fmt->header(&hpp); | 246 | fmt->header(fmt, &hpp); |
169 | 247 | ||
170 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | 248 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), |
171 | -1, ltrim(s), | 249 | -1, ltrim(s), |
@@ -183,6 +261,18 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
183 | col_idx++, NULL); | 261 | col_idx++, NULL); |
184 | } | 262 | } |
185 | 263 | ||
264 | for (col_idx = 0; col_idx < nr_cols; col_idx++) { | ||
265 | GtkTreeViewColumn *column; | ||
266 | |||
267 | column = gtk_tree_view_get_column(GTK_TREE_VIEW(view), col_idx); | ||
268 | gtk_tree_view_column_set_resizable(column, TRUE); | ||
269 | |||
270 | if (col_idx == sym_col) { | ||
271 | gtk_tree_view_set_expander_column(GTK_TREE_VIEW(view), | ||
272 | column); | ||
273 | } | ||
274 | } | ||
275 | |||
186 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | 276 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); |
187 | 277 | ||
188 | g_object_unref(GTK_TREE_MODEL(store)); | 278 | g_object_unref(GTK_TREE_MODEL(store)); |
@@ -199,17 +289,17 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
199 | if (percent < min_pcnt) | 289 | if (percent < min_pcnt) |
200 | continue; | 290 | continue; |
201 | 291 | ||
202 | gtk_list_store_append(store, &iter); | 292 | gtk_tree_store_append(store, &iter, NULL); |
203 | 293 | ||
204 | col_idx = 0; | 294 | col_idx = 0; |
205 | 295 | ||
206 | perf_hpp__for_each_format(fmt) { | 296 | perf_hpp__for_each_format(fmt) { |
207 | if (fmt->color) | 297 | if (fmt->color) |
208 | fmt->color(&hpp, h); | 298 | fmt->color(fmt, &hpp, h); |
209 | else | 299 | else |
210 | fmt->entry(&hpp, h); | 300 | fmt->entry(fmt, &hpp, h); |
211 | 301 | ||
212 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | 302 | gtk_tree_store_set(store, &iter, col_idx++, s, -1); |
213 | } | 303 | } |
214 | 304 | ||
215 | list_for_each_entry(se, &hist_entry__sort_list, list) { | 305 | list_for_each_entry(se, &hist_entry__sort_list, list) { |
@@ -219,10 +309,26 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists, | |||
219 | se->se_snprintf(h, s, ARRAY_SIZE(s), | 309 | se->se_snprintf(h, s, ARRAY_SIZE(s), |
220 | hists__col_len(hists, se->se_width_idx)); | 310 | hists__col_len(hists, se->se_width_idx)); |
221 | 311 | ||
222 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | 312 | gtk_tree_store_set(store, &iter, col_idx++, s, -1); |
313 | } | ||
314 | |||
315 | if (symbol_conf.use_callchain && sort__has_sym) { | ||
316 | u64 total; | ||
317 | |||
318 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
319 | total = h->stat.period; | ||
320 | else | ||
321 | total = hists->stats.total_period; | ||
322 | |||
323 | perf_gtk__add_callchain(&h->sorted_chain, store, &iter, | ||
324 | sym_col, total); | ||
223 | } | 325 | } |
224 | } | 326 | } |
225 | 327 | ||
328 | gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE); | ||
329 | |||
330 | g_signal_connect(view, "row-activated", | ||
331 | G_CALLBACK(on_row_activated), NULL); | ||
226 | gtk_container_add(GTK_CONTAINER(window), view); | 332 | gtk_container_add(GTK_CONTAINER(window), view); |
227 | } | 333 | } |
228 | 334 | ||
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 4bf91b09d62d..0a193281eba8 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <math.h> | 1 | #include <math.h> |
2 | #include <linux/compiler.h> | ||
2 | 3 | ||
3 | #include "../util/hist.h" | 4 | #include "../util/hist.h" |
4 | #include "../util/util.h" | 5 | #include "../util/util.h" |
@@ -79,7 +80,8 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, | |||
79 | } | 80 | } |
80 | 81 | ||
81 | #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ | 82 | #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ |
82 | static int hpp__header_##_type(struct perf_hpp *hpp) \ | 83 | static int hpp__header_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ |
84 | struct perf_hpp *hpp) \ | ||
83 | { \ | 85 | { \ |
84 | int len = _min_width; \ | 86 | int len = _min_width; \ |
85 | \ | 87 | \ |
@@ -92,7 +94,8 @@ static int hpp__header_##_type(struct perf_hpp *hpp) \ | |||
92 | } | 94 | } |
93 | 95 | ||
94 | #define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ | 96 | #define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ |
95 | static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused) \ | 97 | static int hpp__width_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ |
98 | struct perf_hpp *hpp __maybe_unused) \ | ||
96 | { \ | 99 | { \ |
97 | int len = _min_width; \ | 100 | int len = _min_width; \ |
98 | \ | 101 | \ |
@@ -110,14 +113,16 @@ static u64 he_get_##_field(struct hist_entry *he) \ | |||
110 | return he->stat._field; \ | 113 | return he->stat._field; \ |
111 | } \ | 114 | } \ |
112 | \ | 115 | \ |
113 | static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ | 116 | static int hpp__color_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ |
117 | struct perf_hpp *hpp, struct hist_entry *he) \ | ||
114 | { \ | 118 | { \ |
115 | return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ | 119 | return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ |
116 | (hpp_snprint_fn)percent_color_snprintf, true); \ | 120 | (hpp_snprint_fn)percent_color_snprintf, true); \ |
117 | } | 121 | } |
118 | 122 | ||
119 | #define __HPP_ENTRY_PERCENT_FN(_type, _field) \ | 123 | #define __HPP_ENTRY_PERCENT_FN(_type, _field) \ |
120 | static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ | 124 | static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ |
125 | struct perf_hpp *hpp, struct hist_entry *he) \ | ||
121 | { \ | 126 | { \ |
122 | const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ | 127 | const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ |
123 | return __hpp__fmt(hpp, he, he_get_##_field, fmt, \ | 128 | return __hpp__fmt(hpp, he, he_get_##_field, fmt, \ |
@@ -130,7 +135,8 @@ static u64 he_get_raw_##_field(struct hist_entry *he) \ | |||
130 | return he->stat._field; \ | 135 | return he->stat._field; \ |
131 | } \ | 136 | } \ |
132 | \ | 137 | \ |
133 | static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ | 138 | static int hpp__entry_##_type(struct perf_hpp_fmt *_fmt __maybe_unused, \ |
139 | struct perf_hpp *hpp, struct hist_entry *he) \ | ||
134 | { \ | 140 | { \ |
135 | const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ | 141 | const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ |
136 | return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \ | 142 | return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \ |
@@ -157,196 +163,6 @@ HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) | |||
157 | HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) | 163 | HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) |
158 | HPP_RAW_FNS(period, "Period", period, 12, 12) | 164 | HPP_RAW_FNS(period, "Period", period, 12, 12) |
159 | 165 | ||
160 | |||
161 | static int hpp__header_baseline(struct perf_hpp *hpp) | ||
162 | { | ||
163 | return scnprintf(hpp->buf, hpp->size, "Baseline"); | ||
164 | } | ||
165 | |||
166 | static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused) | ||
167 | { | ||
168 | return 8; | ||
169 | } | ||
170 | |||
171 | static double baseline_percent(struct hist_entry *he) | ||
172 | { | ||
173 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
174 | struct hists *pair_hists = pair ? pair->hists : NULL; | ||
175 | double percent = 0.0; | ||
176 | |||
177 | if (pair) { | ||
178 | u64 total_period = pair_hists->stats.total_period; | ||
179 | u64 base_period = pair->stat.period; | ||
180 | |||
181 | percent = 100.0 * base_period / total_period; | ||
182 | } | ||
183 | |||
184 | return percent; | ||
185 | } | ||
186 | |||
187 | static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he) | ||
188 | { | ||
189 | double percent = baseline_percent(he); | ||
190 | |||
191 | if (hist_entry__has_pairs(he) || symbol_conf.field_sep) | ||
192 | return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); | ||
193 | else | ||
194 | return scnprintf(hpp->buf, hpp->size, " "); | ||
195 | } | ||
196 | |||
197 | static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) | ||
198 | { | ||
199 | double percent = baseline_percent(he); | ||
200 | const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; | ||
201 | |||
202 | if (hist_entry__has_pairs(he) || symbol_conf.field_sep) | ||
203 | return scnprintf(hpp->buf, hpp->size, fmt, percent); | ||
204 | else | ||
205 | return scnprintf(hpp->buf, hpp->size, " "); | ||
206 | } | ||
207 | |||
208 | static int hpp__header_period_baseline(struct perf_hpp *hpp) | ||
209 | { | ||
210 | const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; | ||
211 | |||
212 | return scnprintf(hpp->buf, hpp->size, fmt, "Period Base"); | ||
213 | } | ||
214 | |||
215 | static int hpp__width_period_baseline(struct perf_hpp *hpp __maybe_unused) | ||
216 | { | ||
217 | return 12; | ||
218 | } | ||
219 | |||
220 | static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *he) | ||
221 | { | ||
222 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
223 | u64 period = pair ? pair->stat.period : 0; | ||
224 | const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64; | ||
225 | |||
226 | return scnprintf(hpp->buf, hpp->size, fmt, period); | ||
227 | } | ||
228 | |||
229 | static int hpp__header_delta(struct perf_hpp *hpp) | ||
230 | { | ||
231 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; | ||
232 | |||
233 | return scnprintf(hpp->buf, hpp->size, fmt, "Delta"); | ||
234 | } | ||
235 | |||
236 | static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused) | ||
237 | { | ||
238 | return 7; | ||
239 | } | ||
240 | |||
241 | static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) | ||
242 | { | ||
243 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
244 | const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; | ||
245 | char buf[32] = " "; | ||
246 | double diff = 0.0; | ||
247 | |||
248 | if (pair) { | ||
249 | if (he->diff.computed) | ||
250 | diff = he->diff.period_ratio_delta; | ||
251 | else | ||
252 | diff = perf_diff__compute_delta(he, pair); | ||
253 | } else | ||
254 | diff = perf_diff__period_percent(he, he->stat.period); | ||
255 | |||
256 | if (fabs(diff) >= 0.01) | ||
257 | scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); | ||
258 | |||
259 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | ||
260 | } | ||
261 | |||
262 | static int hpp__header_ratio(struct perf_hpp *hpp) | ||
263 | { | ||
264 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | ||
265 | |||
266 | return scnprintf(hpp->buf, hpp->size, fmt, "Ratio"); | ||
267 | } | ||
268 | |||
269 | static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused) | ||
270 | { | ||
271 | return 14; | ||
272 | } | ||
273 | |||
274 | static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he) | ||
275 | { | ||
276 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
277 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | ||
278 | char buf[32] = " "; | ||
279 | double ratio = 0.0; | ||
280 | |||
281 | if (pair) { | ||
282 | if (he->diff.computed) | ||
283 | ratio = he->diff.period_ratio; | ||
284 | else | ||
285 | ratio = perf_diff__compute_ratio(he, pair); | ||
286 | } | ||
287 | |||
288 | if (ratio > 0.0) | ||
289 | scnprintf(buf, sizeof(buf), "%+14.6F", ratio); | ||
290 | |||
291 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | ||
292 | } | ||
293 | |||
294 | static int hpp__header_wdiff(struct perf_hpp *hpp) | ||
295 | { | ||
296 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | ||
297 | |||
298 | return scnprintf(hpp->buf, hpp->size, fmt, "Weighted diff"); | ||
299 | } | ||
300 | |||
301 | static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused) | ||
302 | { | ||
303 | return 14; | ||
304 | } | ||
305 | |||
306 | static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he) | ||
307 | { | ||
308 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
309 | const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; | ||
310 | char buf[32] = " "; | ||
311 | s64 wdiff = 0; | ||
312 | |||
313 | if (pair) { | ||
314 | if (he->diff.computed) | ||
315 | wdiff = he->diff.wdiff; | ||
316 | else | ||
317 | wdiff = perf_diff__compute_wdiff(he, pair); | ||
318 | } | ||
319 | |||
320 | if (wdiff != 0) | ||
321 | scnprintf(buf, sizeof(buf), "%14ld", wdiff); | ||
322 | |||
323 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | ||
324 | } | ||
325 | |||
326 | static int hpp__header_formula(struct perf_hpp *hpp) | ||
327 | { | ||
328 | const char *fmt = symbol_conf.field_sep ? "%s" : "%70s"; | ||
329 | |||
330 | return scnprintf(hpp->buf, hpp->size, fmt, "Formula"); | ||
331 | } | ||
332 | |||
333 | static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused) | ||
334 | { | ||
335 | return 70; | ||
336 | } | ||
337 | |||
338 | static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he) | ||
339 | { | ||
340 | struct hist_entry *pair = hist_entry__next_pair(he); | ||
341 | const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s"; | ||
342 | char buf[96] = " "; | ||
343 | |||
344 | if (pair) | ||
345 | perf_diff__formula(he, pair, buf, sizeof(buf)); | ||
346 | |||
347 | return scnprintf(hpp->buf, hpp->size, fmt, buf); | ||
348 | } | ||
349 | |||
350 | #define HPP__COLOR_PRINT_FNS(_name) \ | 166 | #define HPP__COLOR_PRINT_FNS(_name) \ |
351 | { \ | 167 | { \ |
352 | .header = hpp__header_ ## _name, \ | 168 | .header = hpp__header_ ## _name, \ |
@@ -363,19 +179,13 @@ static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he) | |||
363 | } | 179 | } |
364 | 180 | ||
365 | struct perf_hpp_fmt perf_hpp__format[] = { | 181 | struct perf_hpp_fmt perf_hpp__format[] = { |
366 | HPP__COLOR_PRINT_FNS(baseline), | ||
367 | HPP__COLOR_PRINT_FNS(overhead), | 182 | HPP__COLOR_PRINT_FNS(overhead), |
368 | HPP__COLOR_PRINT_FNS(overhead_sys), | 183 | HPP__COLOR_PRINT_FNS(overhead_sys), |
369 | HPP__COLOR_PRINT_FNS(overhead_us), | 184 | HPP__COLOR_PRINT_FNS(overhead_us), |
370 | HPP__COLOR_PRINT_FNS(overhead_guest_sys), | 185 | HPP__COLOR_PRINT_FNS(overhead_guest_sys), |
371 | HPP__COLOR_PRINT_FNS(overhead_guest_us), | 186 | HPP__COLOR_PRINT_FNS(overhead_guest_us), |
372 | HPP__PRINT_FNS(samples), | 187 | HPP__PRINT_FNS(samples), |
373 | HPP__PRINT_FNS(period), | 188 | HPP__PRINT_FNS(period) |
374 | HPP__PRINT_FNS(period_baseline), | ||
375 | HPP__PRINT_FNS(delta), | ||
376 | HPP__PRINT_FNS(ratio), | ||
377 | HPP__PRINT_FNS(wdiff), | ||
378 | HPP__PRINT_FNS(formula) | ||
379 | }; | 189 | }; |
380 | 190 | ||
381 | LIST_HEAD(perf_hpp__list); | 191 | LIST_HEAD(perf_hpp__list); |
@@ -396,6 +206,8 @@ LIST_HEAD(perf_hpp__list); | |||
396 | 206 | ||
397 | void perf_hpp__init(void) | 207 | void perf_hpp__init(void) |
398 | { | 208 | { |
209 | perf_hpp__column_enable(PERF_HPP__OVERHEAD); | ||
210 | |||
399 | if (symbol_conf.show_cpu_utilization) { | 211 | if (symbol_conf.show_cpu_utilization) { |
400 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS); | 212 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS); |
401 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_US); | 213 | perf_hpp__column_enable(PERF_HPP__OVERHEAD_US); |
@@ -424,46 +236,6 @@ void perf_hpp__column_enable(unsigned col) | |||
424 | perf_hpp__column_register(&perf_hpp__format[col]); | 236 | perf_hpp__column_register(&perf_hpp__format[col]); |
425 | } | 237 | } |
426 | 238 | ||
427 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) | ||
428 | { | ||
429 | hpp->buf += inc; | ||
430 | hpp->size -= inc; | ||
431 | } | ||
432 | |||
433 | int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, | ||
434 | bool color) | ||
435 | { | ||
436 | const char *sep = symbol_conf.field_sep; | ||
437 | struct perf_hpp_fmt *fmt; | ||
438 | char *start = hpp->buf; | ||
439 | int ret; | ||
440 | bool first = true; | ||
441 | |||
442 | if (symbol_conf.exclude_other && !he->parent) | ||
443 | return 0; | ||
444 | |||
445 | perf_hpp__for_each_format(fmt) { | ||
446 | /* | ||
447 | * If there's no field_sep, we still need | ||
448 | * to display initial ' '. | ||
449 | */ | ||
450 | if (!sep || !first) { | ||
451 | ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); | ||
452 | advance_hpp(hpp, ret); | ||
453 | } else | ||
454 | first = false; | ||
455 | |||
456 | if (color && fmt->color) | ||
457 | ret = fmt->color(hpp, he); | ||
458 | else | ||
459 | ret = fmt->entry(hpp, he); | ||
460 | |||
461 | advance_hpp(hpp, ret); | ||
462 | } | ||
463 | |||
464 | return hpp->buf - start; | ||
465 | } | ||
466 | |||
467 | int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, | 239 | int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, |
468 | struct hists *hists) | 240 | struct hists *hists) |
469 | { | 241 | { |
@@ -499,7 +271,7 @@ unsigned int hists__sort_list_width(struct hists *hists) | |||
499 | if (i) | 271 | if (i) |
500 | ret += 2; | 272 | ret += 2; |
501 | 273 | ||
502 | ret += fmt->width(&dummy_hpp); | 274 | ret += fmt->width(fmt, &dummy_hpp); |
503 | } | 275 | } |
504 | 276 | ||
505 | list_for_each_entry(se, &hist_entry__sort_list, list) | 277 | list_for_each_entry(se, &hist_entry__sort_list, list) |
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c index ae6a789cb0f6..47d9a571f261 100644 --- a/tools/perf/ui/setup.c +++ b/tools/perf/ui/setup.c | |||
@@ -30,7 +30,6 @@ 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); | ||
34 | perf_hpp__init(); | 33 | perf_hpp__init(); |
35 | break; | 34 | break; |
36 | } | 35 | } |
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index ae7a75432249..6c152686e837 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c | |||
@@ -308,21 +308,60 @@ static size_t hist_entry__callchain_fprintf(struct hist_entry *he, | |||
308 | return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); | 308 | return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); |
309 | } | 309 | } |
310 | 310 | ||
311 | static inline void advance_hpp(struct perf_hpp *hpp, int inc) | ||
312 | { | ||
313 | hpp->buf += inc; | ||
314 | hpp->size -= inc; | ||
315 | } | ||
316 | |||
317 | static int hist_entry__period_snprintf(struct perf_hpp *hpp, | ||
318 | struct hist_entry *he) | ||
319 | { | ||
320 | const char *sep = symbol_conf.field_sep; | ||
321 | struct perf_hpp_fmt *fmt; | ||
322 | char *start = hpp->buf; | ||
323 | int ret; | ||
324 | bool first = true; | ||
325 | |||
326 | if (symbol_conf.exclude_other && !he->parent) | ||
327 | return 0; | ||
328 | |||
329 | perf_hpp__for_each_format(fmt) { | ||
330 | /* | ||
331 | * If there's no field_sep, we still need | ||
332 | * to display initial ' '. | ||
333 | */ | ||
334 | if (!sep || !first) { | ||
335 | ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); | ||
336 | advance_hpp(hpp, ret); | ||
337 | } else | ||
338 | first = false; | ||
339 | |||
340 | if (perf_hpp__use_color() && fmt->color) | ||
341 | ret = fmt->color(fmt, hpp, he); | ||
342 | else | ||
343 | ret = fmt->entry(fmt, hpp, he); | ||
344 | |||
345 | advance_hpp(hpp, ret); | ||
346 | } | ||
347 | |||
348 | return hpp->buf - start; | ||
349 | } | ||
350 | |||
311 | static int hist_entry__fprintf(struct hist_entry *he, size_t size, | 351 | static int hist_entry__fprintf(struct hist_entry *he, size_t size, |
312 | struct hists *hists, FILE *fp) | 352 | struct hists *hists, |
353 | char *bf, size_t bfsz, FILE *fp) | ||
313 | { | 354 | { |
314 | char bf[512]; | ||
315 | int ret; | 355 | int ret; |
316 | struct perf_hpp hpp = { | 356 | struct perf_hpp hpp = { |
317 | .buf = bf, | 357 | .buf = bf, |
318 | .size = size, | 358 | .size = size, |
319 | }; | 359 | }; |
320 | bool color = !symbol_conf.field_sep; | ||
321 | 360 | ||
322 | if (size == 0 || size > sizeof(bf)) | 361 | if (size == 0 || size > bfsz) |
323 | size = hpp.size = sizeof(bf); | 362 | size = hpp.size = bfsz; |
324 | 363 | ||
325 | ret = hist_entry__period_snprintf(&hpp, he, color); | 364 | ret = hist_entry__period_snprintf(&hpp, he); |
326 | hist_entry__sort_snprintf(he, bf + ret, size - ret, hists); | 365 | hist_entry__sort_snprintf(he, bf + ret, size - ret, hists); |
327 | 366 | ||
328 | ret = fprintf(fp, "%s\n", bf); | 367 | ret = fprintf(fp, "%s\n", bf); |
@@ -351,6 +390,8 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
351 | .ptr = hists_to_evsel(hists), | 390 | .ptr = hists_to_evsel(hists), |
352 | }; | 391 | }; |
353 | bool first = true; | 392 | bool first = true; |
393 | size_t linesz; | ||
394 | char *line = NULL; | ||
354 | 395 | ||
355 | init_rem_hits(); | 396 | init_rem_hits(); |
356 | 397 | ||
@@ -365,7 +406,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
365 | else | 406 | else |
366 | first = false; | 407 | first = false; |
367 | 408 | ||
368 | fmt->header(&dummy_hpp); | 409 | fmt->header(fmt, &dummy_hpp); |
369 | fprintf(fp, "%s", bf); | 410 | fprintf(fp, "%s", bf); |
370 | } | 411 | } |
371 | 412 | ||
@@ -410,7 +451,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
410 | else | 451 | else |
411 | first = false; | 452 | first = false; |
412 | 453 | ||
413 | width = fmt->width(&dummy_hpp); | 454 | width = fmt->width(fmt, &dummy_hpp); |
414 | for (i = 0; i < width; i++) | 455 | for (i = 0; i < width; i++) |
415 | fprintf(fp, "."); | 456 | fprintf(fp, "."); |
416 | } | 457 | } |
@@ -438,6 +479,14 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, | |||
438 | goto out; | 479 | goto out; |
439 | 480 | ||
440 | print_entries: | 481 | print_entries: |
482 | linesz = hists__sort_list_width(hists) + 3 + 1; | ||
483 | linesz += perf_hpp__color_overhead(); | ||
484 | line = malloc(linesz); | ||
485 | if (line == NULL) { | ||
486 | ret = -1; | ||
487 | goto out; | ||
488 | } | ||
489 | |||
441 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 490 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
442 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 491 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
443 | float percent = h->stat.period * 100.0 / | 492 | float percent = h->stat.period * 100.0 / |
@@ -449,10 +498,10 @@ print_entries: | |||
449 | if (percent < min_pcnt) | 498 | if (percent < min_pcnt) |
450 | continue; | 499 | continue; |
451 | 500 | ||
452 | ret += hist_entry__fprintf(h, max_cols, hists, fp); | 501 | ret += hist_entry__fprintf(h, max_cols, hists, line, linesz, fp); |
453 | 502 | ||
454 | if (max_rows && ++nr_rows >= max_rows) | 503 | if (max_rows && ++nr_rows >= max_rows) |
455 | goto out; | 504 | break; |
456 | 505 | ||
457 | if (h->ms.map == NULL && verbose > 1) { | 506 | if (h->ms.map == NULL && verbose > 1) { |
458 | __map_groups__fprintf_maps(&h->thread->mg, | 507 | __map_groups__fprintf_maps(&h->thread->mg, |
@@ -460,6 +509,8 @@ print_entries: | |||
460 | fprintf(fp, "%.10s end\n", graph_dotted_line); | 509 | fprintf(fp, "%.10s end\n", graph_dotted_line); |
461 | } | 510 | } |
462 | } | 511 | } |
512 | |||
513 | free(line); | ||
463 | out: | 514 | out: |
464 | free(rem_sq_bracket); | 515 | free(rem_sq_bracket); |
465 | 516 | ||
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index d102716c43a1..7eae5488ecea 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -110,10 +110,10 @@ static int jump__parse(struct ins_operands *ops) | |||
110 | { | 110 | { |
111 | const char *s = strchr(ops->raw, '+'); | 111 | const char *s = strchr(ops->raw, '+'); |
112 | 112 | ||
113 | ops->target.addr = strtoll(ops->raw, NULL, 16); | 113 | ops->target.addr = strtoull(ops->raw, NULL, 16); |
114 | 114 | ||
115 | if (s++ != NULL) | 115 | if (s++ != NULL) |
116 | ops->target.offset = strtoll(s, NULL, 16); | 116 | ops->target.offset = strtoull(s, NULL, 16); |
117 | else | 117 | else |
118 | ops->target.offset = UINT64_MAX; | 118 | ops->target.offset = UINT64_MAX; |
119 | 119 | ||
@@ -809,7 +809,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
809 | end = map__rip_2objdump(map, sym->end); | 809 | end = map__rip_2objdump(map, sym->end); |
810 | 810 | ||
811 | offset = line_ip - start; | 811 | offset = line_ip - start; |
812 | if (offset < 0 || (u64)line_ip > end) | 812 | if ((u64)line_ip < start || (u64)line_ip > end) |
813 | offset = -1; | 813 | offset = -1; |
814 | else | 814 | else |
815 | parsed_line = tmp2 + 1; | 815 | parsed_line = tmp2 + 1; |
@@ -821,11 +821,55 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map, | |||
821 | if (dl == NULL) | 821 | if (dl == NULL) |
822 | return -1; | 822 | return -1; |
823 | 823 | ||
824 | if (dl->ops.target.offset == UINT64_MAX) | ||
825 | dl->ops.target.offset = dl->ops.target.addr - | ||
826 | map__rip_2objdump(map, sym->start); | ||
827 | |||
828 | /* | ||
829 | * kcore has no symbols, so add the call target name if it is on the | ||
830 | * same map. | ||
831 | */ | ||
832 | if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { | ||
833 | struct symbol *s; | ||
834 | u64 ip = dl->ops.target.addr; | ||
835 | |||
836 | if (ip >= map->start && ip <= map->end) { | ||
837 | ip = map->map_ip(map, ip); | ||
838 | s = map__find_symbol(map, ip, NULL); | ||
839 | if (s && s->start == ip) | ||
840 | dl->ops.target.name = strdup(s->name); | ||
841 | } | ||
842 | } | ||
843 | |||
824 | disasm__add(¬es->src->source, dl); | 844 | disasm__add(¬es->src->source, dl); |
825 | 845 | ||
826 | return 0; | 846 | return 0; |
827 | } | 847 | } |
828 | 848 | ||
849 | static void delete_last_nop(struct symbol *sym) | ||
850 | { | ||
851 | struct annotation *notes = symbol__annotation(sym); | ||
852 | struct list_head *list = ¬es->src->source; | ||
853 | struct disasm_line *dl; | ||
854 | |||
855 | while (!list_empty(list)) { | ||
856 | dl = list_entry(list->prev, struct disasm_line, node); | ||
857 | |||
858 | if (dl->ins && dl->ins->ops) { | ||
859 | if (dl->ins->ops != &nop_ops) | ||
860 | return; | ||
861 | } else { | ||
862 | if (!strstr(dl->line, " nop ") && | ||
863 | !strstr(dl->line, " nopl ") && | ||
864 | !strstr(dl->line, " nopw ")) | ||
865 | return; | ||
866 | } | ||
867 | |||
868 | list_del(&dl->node); | ||
869 | disasm_line__free(dl); | ||
870 | } | ||
871 | } | ||
872 | |||
829 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | 873 | int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) |
830 | { | 874 | { |
831 | struct dso *dso = map->dso; | 875 | struct dso *dso = map->dso; |
@@ -864,7 +908,8 @@ fallback: | |||
864 | free_filename = false; | 908 | free_filename = false; |
865 | } | 909 | } |
866 | 910 | ||
867 | if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) { | 911 | if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS && |
912 | !dso__is_kcore(dso)) { | ||
868 | char bf[BUILD_ID_SIZE * 2 + 16] = " with build id "; | 913 | char bf[BUILD_ID_SIZE * 2 + 16] = " with build id "; |
869 | char *build_id_msg = NULL; | 914 | char *build_id_msg = NULL; |
870 | 915 | ||
@@ -898,7 +943,7 @@ fallback: | |||
898 | snprintf(command, sizeof(command), | 943 | snprintf(command, sizeof(command), |
899 | "%s %s%s --start-address=0x%016" PRIx64 | 944 | "%s %s%s --start-address=0x%016" PRIx64 |
900 | " --stop-address=0x%016" PRIx64 | 945 | " --stop-address=0x%016" PRIx64 |
901 | " -d %s %s -C %s|grep -v %s|expand", | 946 | " -d %s %s -C %s 2>/dev/null|grep -v %s|expand", |
902 | objdump_path ? objdump_path : "objdump", | 947 | objdump_path ? objdump_path : "objdump", |
903 | disassembler_style ? "-M " : "", | 948 | disassembler_style ? "-M " : "", |
904 | disassembler_style ? disassembler_style : "", | 949 | disassembler_style ? disassembler_style : "", |
@@ -918,6 +963,13 @@ fallback: | |||
918 | if (symbol__parse_objdump_line(sym, map, file, privsize) < 0) | 963 | if (symbol__parse_objdump_line(sym, map, file, privsize) < 0) |
919 | break; | 964 | break; |
920 | 965 | ||
966 | /* | ||
967 | * kallsyms does not have symbol sizes so there may a nop at the end. | ||
968 | * Remove it. | ||
969 | */ | ||
970 | if (dso__is_kcore(dso)) | ||
971 | delete_last_nop(sym); | ||
972 | |||
921 | pclose(file); | 973 | pclose(file); |
922 | out_free_filename: | 974 | out_free_filename: |
923 | if (free_filename) | 975 | if (free_filename) |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 5295625c0c00..7ded71d19d75 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c | |||
@@ -18,13 +18,14 @@ | |||
18 | 18 | ||
19 | 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, |
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; |
27 | struct thread *thread = machine__findnew_thread(machine, event->ip.pid); | 27 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
28 | sample->pid); | ||
28 | 29 | ||
29 | if (thread == NULL) { | 30 | if (thread == NULL) { |
30 | pr_err("problem processing %d event, skipping it.\n", | 31 | pr_err("problem processing %d event, skipping it.\n", |
@@ -33,7 +34,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, | |||
33 | } | 34 | } |
34 | 35 | ||
35 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 36 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, |
36 | event->ip.ip, &al); | 37 | sample->ip, &al); |
37 | 38 | ||
38 | if (al.map != NULL) | 39 | if (al.map != NULL) |
39 | al.map->dso->hit = 1; | 40 | al.map->dso->hit = 1; |
@@ -47,7 +48,9 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, | |||
47 | __maybe_unused, | 48 | __maybe_unused, |
48 | struct machine *machine) | 49 | struct machine *machine) |
49 | { | 50 | { |
50 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | 51 | struct thread *thread = machine__findnew_thread(machine, |
52 | event->fork.pid, | ||
53 | event->fork.tid); | ||
51 | 54 | ||
52 | dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, | 55 | dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, |
53 | event->fork.ppid, event->fork.ptid); | 56 | event->fork.ppid, event->fork.ptid); |
@@ -64,6 +67,7 @@ static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, | |||
64 | struct perf_tool build_id__mark_dso_hit_ops = { | 67 | struct perf_tool build_id__mark_dso_hit_ops = { |
65 | .sample = build_id__mark_dso_hit, | 68 | .sample = build_id__mark_dso_hit, |
66 | .mmap = perf_event__process_mmap, | 69 | .mmap = perf_event__process_mmap, |
70 | .mmap2 = perf_event__process_mmap2, | ||
67 | .fork = perf_event__process_fork, | 71 | .fork = perf_event__process_fork, |
68 | .exit = perf_event__exit_del_thread, | 72 | .exit = perf_event__exit_del_thread, |
69 | .attr = perf_event__process_attr, | 73 | .attr = perf_event__process_attr, |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 42b6a632fe7b..482f68081cd8 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -15,19 +15,12 @@ | |||
15 | #include <errno.h> | 15 | #include <errno.h> |
16 | #include <math.h> | 16 | #include <math.h> |
17 | 17 | ||
18 | #include "hist.h" | ||
18 | #include "util.h" | 19 | #include "util.h" |
19 | #include "callchain.h" | 20 | #include "callchain.h" |
20 | 21 | ||
21 | __thread struct callchain_cursor callchain_cursor; | 22 | __thread struct callchain_cursor callchain_cursor; |
22 | 23 | ||
23 | bool ip_callchain__valid(struct ip_callchain *chain, | ||
24 | const union perf_event *event) | ||
25 | { | ||
26 | unsigned int chain_size = event->header.size; | ||
27 | chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; | ||
28 | return chain->nr * sizeof(u64) <= chain_size; | ||
29 | } | ||
30 | |||
31 | #define chain_for_each_child(child, parent) \ | 24 | #define chain_for_each_child(child, parent) \ |
32 | list_for_each_entry(child, &parent->children, siblings) | 25 | list_for_each_entry(child, &parent->children, siblings) |
33 | 26 | ||
@@ -327,7 +320,8 @@ append_chain(struct callchain_node *root, | |||
327 | /* | 320 | /* |
328 | * Lookup in the current node | 321 | * Lookup in the current node |
329 | * If we have a symbol, then compare the start to match | 322 | * If we have a symbol, then compare the start to match |
330 | * anywhere inside a function. | 323 | * anywhere inside a function, unless function |
324 | * mode is disabled. | ||
331 | */ | 325 | */ |
332 | list_for_each_entry(cnode, &root->val, list) { | 326 | list_for_each_entry(cnode, &root->val, list) { |
333 | struct callchain_cursor_node *node; | 327 | struct callchain_cursor_node *node; |
@@ -339,7 +333,8 @@ append_chain(struct callchain_node *root, | |||
339 | 333 | ||
340 | sym = node->sym; | 334 | sym = node->sym; |
341 | 335 | ||
342 | if (cnode->ms.sym && sym) { | 336 | if (cnode->ms.sym && sym && |
337 | callchain_param.key == CCKEY_FUNCTION) { | ||
343 | if (cnode->ms.sym->start != sym->start) | 338 | if (cnode->ms.sym->start != sym->start) |
344 | break; | 339 | break; |
345 | } else if (cnode->ip != node->ip) | 340 | } else if (cnode->ip != node->ip) |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 3ee9f67d5af0..9e99060408ae 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -41,12 +41,18 @@ struct callchain_param; | |||
41 | typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *, | 41 | typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_root *, |
42 | u64, struct callchain_param *); | 42 | u64, struct callchain_param *); |
43 | 43 | ||
44 | enum chain_key { | ||
45 | CCKEY_FUNCTION, | ||
46 | CCKEY_ADDRESS | ||
47 | }; | ||
48 | |||
44 | struct callchain_param { | 49 | struct callchain_param { |
45 | enum chain_mode mode; | 50 | enum chain_mode mode; |
46 | u32 print_limit; | 51 | u32 print_limit; |
47 | double min_percent; | 52 | double min_percent; |
48 | sort_chain_func_t sort; | 53 | sort_chain_func_t sort; |
49 | enum chain_order order; | 54 | enum chain_order order; |
55 | enum chain_key key; | ||
50 | }; | 56 | }; |
51 | 57 | ||
52 | struct callchain_list { | 58 | struct callchain_list { |
@@ -103,11 +109,6 @@ int callchain_append(struct callchain_root *root, | |||
103 | int callchain_merge(struct callchain_cursor *cursor, | 109 | int callchain_merge(struct callchain_cursor *cursor, |
104 | struct callchain_root *dst, struct callchain_root *src); | 110 | struct callchain_root *dst, struct callchain_root *src); |
105 | 111 | ||
106 | struct ip_callchain; | ||
107 | union perf_event; | ||
108 | |||
109 | bool ip_callchain__valid(struct ip_callchain *chain, | ||
110 | const union perf_event *event); | ||
111 | /* | 112 | /* |
112 | * Initialize a cursor before adding entries inside, but keep | 113 | * Initialize a cursor before adding entries inside, but keep |
113 | * the previously allocated entries as a cache. | 114 | * the previously allocated entries as a cache. |
@@ -146,6 +147,9 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) | |||
146 | 147 | ||
147 | struct option; | 148 | struct option; |
148 | 149 | ||
150 | int record_parse_callchain(const char *arg, struct perf_record_opts *opts); | ||
149 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); | 151 | int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); |
152 | int record_callchain_opt(const struct option *opt, const char *arg, int unset); | ||
153 | |||
150 | extern const char record_callchain_help[]; | 154 | extern const char record_callchain_help[]; |
151 | #endif /* __PERF_CALLCHAIN_H */ | 155 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 9bed02e5fb3d..b123bb9d6f55 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -41,7 +41,7 @@ static inline int cpu_map__nr(const struct cpu_map *map) | |||
41 | return map ? map->nr : 1; | 41 | return map ? map->nr : 1; |
42 | } | 42 | } |
43 | 43 | ||
44 | static inline bool cpu_map__all(const struct cpu_map *map) | 44 | static inline bool cpu_map__empty(const struct cpu_map *map) |
45 | { | 45 | { |
46 | return map ? map->map[0] == -1 : true; | 46 | return map ? map->map[0] == -1 : true; |
47 | } | 47 | } |
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index c4374f07603c..e3c1ff8512c8 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c | |||
@@ -78,6 +78,8 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | |||
78 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); | 78 | symbol_conf.symfs, build_id_hex, build_id_hex + 2); |
79 | break; | 79 | break; |
80 | 80 | ||
81 | case DSO_BINARY_TYPE__VMLINUX: | ||
82 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | ||
81 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: | 83 | case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: |
82 | snprintf(file, size, "%s%s", | 84 | snprintf(file, size, "%s%s", |
83 | symbol_conf.symfs, dso->long_name); | 85 | symbol_conf.symfs, dso->long_name); |
@@ -93,11 +95,14 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, | |||
93 | dso->long_name); | 95 | dso->long_name); |
94 | break; | 96 | break; |
95 | 97 | ||
98 | case DSO_BINARY_TYPE__KCORE: | ||
99 | case DSO_BINARY_TYPE__GUEST_KCORE: | ||
100 | snprintf(file, size, "%s", dso->long_name); | ||
101 | break; | ||
102 | |||
96 | default: | 103 | default: |
97 | case DSO_BINARY_TYPE__KALLSYMS: | 104 | case DSO_BINARY_TYPE__KALLSYMS: |
98 | case DSO_BINARY_TYPE__VMLINUX: | ||
99 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: | 105 | case DSO_BINARY_TYPE__GUEST_KALLSYMS: |
100 | case DSO_BINARY_TYPE__GUEST_VMLINUX: | ||
101 | case DSO_BINARY_TYPE__JAVA_JIT: | 106 | case DSO_BINARY_TYPE__JAVA_JIT: |
102 | case DSO_BINARY_TYPE__NOT_FOUND: | 107 | case DSO_BINARY_TYPE__NOT_FOUND: |
103 | ret = -1; | 108 | ret = -1; |
@@ -419,6 +424,7 @@ struct dso *dso__new(const char *name) | |||
419 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; | 424 | dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; |
420 | dso->data_type = DSO_BINARY_TYPE__NOT_FOUND; | 425 | dso->data_type = DSO_BINARY_TYPE__NOT_FOUND; |
421 | dso->loaded = 0; | 426 | dso->loaded = 0; |
427 | dso->rel = 0; | ||
422 | dso->sorted_by_name = 0; | 428 | dso->sorted_by_name = 0; |
423 | dso->has_build_id = 0; | 429 | dso->has_build_id = 0; |
424 | dso->kernel = DSO_TYPE_USER; | 430 | dso->kernel = DSO_TYPE_USER; |
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index d51aaf272c68..b793053335d6 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
6 | #include <stdbool.h> | ||
6 | #include "types.h" | 7 | #include "types.h" |
7 | #include "map.h" | 8 | #include "map.h" |
8 | 9 | ||
@@ -20,6 +21,8 @@ enum dso_binary_type { | |||
20 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, | 21 | DSO_BINARY_TYPE__SYSTEM_PATH_DSO, |
21 | DSO_BINARY_TYPE__GUEST_KMODULE, | 22 | DSO_BINARY_TYPE__GUEST_KMODULE, |
22 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, | 23 | DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE, |
24 | DSO_BINARY_TYPE__KCORE, | ||
25 | DSO_BINARY_TYPE__GUEST_KCORE, | ||
23 | DSO_BINARY_TYPE__NOT_FOUND, | 26 | DSO_BINARY_TYPE__NOT_FOUND, |
24 | }; | 27 | }; |
25 | 28 | ||
@@ -84,6 +87,7 @@ struct dso { | |||
84 | u8 lname_alloc:1; | 87 | u8 lname_alloc:1; |
85 | u8 sorted_by_name; | 88 | u8 sorted_by_name; |
86 | u8 loaded; | 89 | u8 loaded; |
90 | u8 rel; | ||
87 | u8 build_id[BUILD_ID_SIZE]; | 91 | u8 build_id[BUILD_ID_SIZE]; |
88 | const char *short_name; | 92 | const char *short_name; |
89 | char *long_name; | 93 | char *long_name; |
@@ -146,4 +150,17 @@ size_t dso__fprintf_buildid(struct dso *dso, FILE *fp); | |||
146 | size_t dso__fprintf_symbols_by_name(struct dso *dso, | 150 | size_t dso__fprintf_symbols_by_name(struct dso *dso, |
147 | enum map_type type, FILE *fp); | 151 | enum map_type type, FILE *fp); |
148 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); | 152 | size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); |
153 | |||
154 | static inline bool dso__is_vmlinux(struct dso *dso) | ||
155 | { | ||
156 | return dso->data_type == DSO_BINARY_TYPE__VMLINUX || | ||
157 | dso->data_type == DSO_BINARY_TYPE__GUEST_VMLINUX; | ||
158 | } | ||
159 | |||
160 | static inline bool dso__is_kcore(struct dso *dso) | ||
161 | { | ||
162 | return dso->data_type == DSO_BINARY_TYPE__KCORE || | ||
163 | dso->data_type == DSO_BINARY_TYPE__GUEST_KCORE; | ||
164 | } | ||
165 | |||
149 | #endif /* __PERF_DSO */ | 166 | #endif /* __PERF_DSO */ |
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 3e5f5430a28a..7defd77105d0 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c | |||
@@ -263,6 +263,21 @@ bool die_is_signed_type(Dwarf_Die *tp_die) | |||
263 | } | 263 | } |
264 | 264 | ||
265 | /** | 265 | /** |
266 | * die_is_func_def - Ensure that this DIE is a subprogram and definition | ||
267 | * @dw_die: a DIE | ||
268 | * | ||
269 | * Ensure that this DIE is a subprogram and NOT a declaration. This | ||
270 | * returns true if @dw_die is a function definition. | ||
271 | **/ | ||
272 | bool die_is_func_def(Dwarf_Die *dw_die) | ||
273 | { | ||
274 | Dwarf_Attribute attr; | ||
275 | |||
276 | return (dwarf_tag(dw_die) == DW_TAG_subprogram && | ||
277 | dwarf_attr(dw_die, DW_AT_declaration, &attr) == NULL); | ||
278 | } | ||
279 | |||
280 | /** | ||
266 | * die_get_data_member_location - Get the data-member offset | 281 | * die_get_data_member_location - Get the data-member offset |
267 | * @mb_die: a DIE of a member of a data structure | 282 | * @mb_die: a DIE of a member of a data structure |
268 | * @offs: The offset of the member in the data structure | 283 | * @offs: The offset of the member in the data structure |
@@ -392,6 +407,10 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | |||
392 | { | 407 | { |
393 | struct __addr_die_search_param *ad = data; | 408 | struct __addr_die_search_param *ad = data; |
394 | 409 | ||
410 | /* | ||
411 | * Since a declaration entry doesn't has given pc, this always returns | ||
412 | * function definition entry. | ||
413 | */ | ||
395 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && | 414 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && |
396 | dwarf_haspc(fn_die, ad->addr)) { | 415 | dwarf_haspc(fn_die, ad->addr)) { |
397 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); | 416 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); |
@@ -407,7 +426,7 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | |||
407 | * @die_mem: a buffer for result DIE | 426 | * @die_mem: a buffer for result DIE |
408 | * | 427 | * |
409 | * Search a non-inlined function DIE which includes @addr. Stores the | 428 | * Search a non-inlined function DIE which includes @addr. Stores the |
410 | * DIE to @die_mem and returns it if found. Returns NULl if failed. | 429 | * DIE to @die_mem and returns it if found. Returns NULL if failed. |
411 | */ | 430 | */ |
412 | Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | 431 | Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, |
413 | Dwarf_Die *die_mem) | 432 | Dwarf_Die *die_mem) |
@@ -435,15 +454,32 @@ static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) | |||
435 | } | 454 | } |
436 | 455 | ||
437 | /** | 456 | /** |
457 | * die_find_top_inlinefunc - Search the top inlined function at given address | ||
458 | * @sp_die: a subprogram DIE which including @addr | ||
459 | * @addr: target address | ||
460 | * @die_mem: a buffer for result DIE | ||
461 | * | ||
462 | * Search an inlined function DIE which includes @addr. Stores the | ||
463 | * DIE to @die_mem and returns it if found. Returns NULL if failed. | ||
464 | * Even if several inlined functions are expanded recursively, this | ||
465 | * doesn't trace it down, and returns the topmost one. | ||
466 | */ | ||
467 | Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
468 | Dwarf_Die *die_mem) | ||
469 | { | ||
470 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); | ||
471 | } | ||
472 | |||
473 | /** | ||
438 | * die_find_inlinefunc - Search an inlined function at given address | 474 | * die_find_inlinefunc - Search an inlined function at given address |
439 | * @cu_die: a CU DIE which including @addr | 475 | * @sp_die: a subprogram DIE which including @addr |
440 | * @addr: target address | 476 | * @addr: target address |
441 | * @die_mem: a buffer for result DIE | 477 | * @die_mem: a buffer for result DIE |
442 | * | 478 | * |
443 | * Search an inlined function DIE which includes @addr. Stores the | 479 | * Search an inlined function DIE which includes @addr. Stores the |
444 | * DIE to @die_mem and returns it if found. Returns NULl if failed. | 480 | * DIE to @die_mem and returns it if found. Returns NULL if failed. |
445 | * If several inlined functions are expanded recursively, this trace | 481 | * If several inlined functions are expanded recursively, this trace |
446 | * it and returns deepest one. | 482 | * it down and returns deepest one. |
447 | */ | 483 | */ |
448 | Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | 484 | Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
449 | Dwarf_Die *die_mem) | 485 | Dwarf_Die *die_mem) |
diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index 6ce1717784b7..b4fe90c6cb2d 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h | |||
@@ -38,6 +38,9 @@ extern int cu_find_lineinfo(Dwarf_Die *cudie, unsigned long addr, | |||
38 | extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, | 38 | extern int cu_walk_functions_at(Dwarf_Die *cu_die, Dwarf_Addr addr, |
39 | int (*callback)(Dwarf_Die *, void *), void *data); | 39 | int (*callback)(Dwarf_Die *, void *), void *data); |
40 | 40 | ||
41 | /* Ensure that this DIE is a subprogram and definition (not declaration) */ | ||
42 | extern bool die_is_func_def(Dwarf_Die *dw_die); | ||
43 | |||
41 | /* Compare diename and tname */ | 44 | /* Compare diename and tname */ |
42 | extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); | 45 | extern bool die_compare_name(Dwarf_Die *dw_die, const char *tname); |
43 | 46 | ||
@@ -76,7 +79,11 @@ extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | |||
76 | extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, | 79 | extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr, |
77 | Dwarf_Die *die_mem); | 80 | Dwarf_Die *die_mem); |
78 | 81 | ||
79 | /* Search an inlined function including given address */ | 82 | /* Search the top inlined function including given address */ |
83 | extern Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | ||
84 | Dwarf_Die *die_mem); | ||
85 | |||
86 | /* Search the deepest inlined function including given address */ | ||
80 | extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | 87 | extern Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
81 | Dwarf_Die *die_mem); | 88 | Dwarf_Die *die_mem); |
82 | 89 | ||
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 5cd13d768cec..49096ea58a15 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -11,6 +11,7 @@ | |||
11 | static const char *perf_event__names[] = { | 11 | static const char *perf_event__names[] = { |
12 | [0] = "TOTAL", | 12 | [0] = "TOTAL", |
13 | [PERF_RECORD_MMAP] = "MMAP", | 13 | [PERF_RECORD_MMAP] = "MMAP", |
14 | [PERF_RECORD_MMAP2] = "MMAP2", | ||
14 | [PERF_RECORD_LOST] = "LOST", | 15 | [PERF_RECORD_LOST] = "LOST", |
15 | [PERF_RECORD_COMM] = "COMM", | 16 | [PERF_RECORD_COMM] = "COMM", |
16 | [PERF_RECORD_EXIT] = "EXIT", | 17 | [PERF_RECORD_EXIT] = "EXIT", |
@@ -198,6 +199,7 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
198 | char execname[PATH_MAX]; | 199 | char execname[PATH_MAX]; |
199 | char anonstr[] = "//anon"; | 200 | char anonstr[] = "//anon"; |
200 | size_t size; | 201 | size_t size; |
202 | ssize_t n; | ||
201 | 203 | ||
202 | if (fgets(bf, sizeof(bf), fp) == NULL) | 204 | if (fgets(bf, sizeof(bf), fp) == NULL) |
203 | break; | 205 | break; |
@@ -206,9 +208,13 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
206 | strcpy(execname, ""); | 208 | strcpy(execname, ""); |
207 | 209 | ||
208 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ | 210 | /* 00400000-0040c000 r-xp 00000000 fd:01 41038 /bin/cat */ |
209 | sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n", | 211 | n = sscanf(bf, "%"PRIx64"-%"PRIx64" %s %"PRIx64" %*x:%*x %*u %s\n", |
210 | &event->mmap.start, &event->mmap.len, prot, | 212 | &event->mmap.start, &event->mmap.len, prot, |
211 | &event->mmap.pgoff, execname); | 213 | &event->mmap.pgoff, |
214 | execname); | ||
215 | |||
216 | if (n != 5) | ||
217 | continue; | ||
212 | 218 | ||
213 | if (prot[2] != 'x') | 219 | if (prot[2] != 'x') |
214 | continue; | 220 | continue; |
@@ -221,7 +227,7 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, | |||
221 | size = PERF_ALIGN(size, sizeof(u64)); | 227 | size = PERF_ALIGN(size, sizeof(u64)); |
222 | event->mmap.len -= event->mmap.start; | 228 | event->mmap.len -= event->mmap.start; |
223 | event->mmap.header.size = (sizeof(event->mmap) - | 229 | event->mmap.header.size = (sizeof(event->mmap) - |
224 | (sizeof(event->mmap.filename) - size)); | 230 | (sizeof(event->mmap.filename) - size)); |
225 | memset(event->mmap.filename + size, 0, machine->id_hdr_size); | 231 | memset(event->mmap.filename + size, 0, machine->id_hdr_size); |
226 | event->mmap.header.size += machine->id_hdr_size; | 232 | event->mmap.header.size += machine->id_hdr_size; |
227 | event->mmap.pid = tgid; | 233 | event->mmap.pid = tgid; |
@@ -527,6 +533,17 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp) | |||
527 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); | 533 | event->mmap.len, event->mmap.pgoff, event->mmap.filename); |
528 | } | 534 | } |
529 | 535 | ||
536 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp) | ||
537 | { | ||
538 | return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 | ||
539 | " %02x:%02x %"PRIu64" %"PRIu64"]: %s\n", | ||
540 | event->mmap2.pid, event->mmap2.tid, event->mmap2.start, | ||
541 | event->mmap2.len, event->mmap2.pgoff, event->mmap2.maj, | ||
542 | event->mmap2.min, event->mmap2.ino, | ||
543 | event->mmap2.ino_generation, | ||
544 | event->mmap2.filename); | ||
545 | } | ||
546 | |||
530 | int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, | 547 | int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, |
531 | union perf_event *event, | 548 | union perf_event *event, |
532 | struct perf_sample *sample __maybe_unused, | 549 | struct perf_sample *sample __maybe_unused, |
@@ -535,6 +552,14 @@ int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, | |||
535 | return machine__process_mmap_event(machine, event); | 552 | return machine__process_mmap_event(machine, event); |
536 | } | 553 | } |
537 | 554 | ||
555 | int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused, | ||
556 | union perf_event *event, | ||
557 | struct perf_sample *sample __maybe_unused, | ||
558 | struct machine *machine) | ||
559 | { | ||
560 | return machine__process_mmap2_event(machine, event); | ||
561 | } | ||
562 | |||
538 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) | 563 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) |
539 | { | 564 | { |
540 | return fprintf(fp, "(%d:%d):(%d:%d)\n", | 565 | return fprintf(fp, "(%d:%d):(%d:%d)\n", |
@@ -574,6 +599,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp) | |||
574 | case PERF_RECORD_MMAP: | 599 | case PERF_RECORD_MMAP: |
575 | ret += perf_event__fprintf_mmap(event, fp); | 600 | ret += perf_event__fprintf_mmap(event, fp); |
576 | break; | 601 | break; |
602 | case PERF_RECORD_MMAP2: | ||
603 | ret += perf_event__fprintf_mmap2(event, fp); | ||
604 | break; | ||
577 | default: | 605 | default: |
578 | ret += fprintf(fp, "\n"); | 606 | ret += fprintf(fp, "\n"); |
579 | } | 607 | } |
@@ -595,6 +623,7 @@ void thread__find_addr_map(struct thread *self, | |||
595 | struct addr_location *al) | 623 | struct addr_location *al) |
596 | { | 624 | { |
597 | struct map_groups *mg = &self->mg; | 625 | struct map_groups *mg = &self->mg; |
626 | bool load_map = false; | ||
598 | 627 | ||
599 | al->thread = self; | 628 | al->thread = self; |
600 | al->addr = addr; | 629 | al->addr = addr; |
@@ -609,11 +638,13 @@ void thread__find_addr_map(struct thread *self, | |||
609 | if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { | 638 | if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { |
610 | al->level = 'k'; | 639 | al->level = 'k'; |
611 | mg = &machine->kmaps; | 640 | mg = &machine->kmaps; |
641 | load_map = true; | ||
612 | } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { | 642 | } else if (cpumode == PERF_RECORD_MISC_USER && perf_host) { |
613 | al->level = '.'; | 643 | al->level = '.'; |
614 | } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { | 644 | } else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) { |
615 | al->level = 'g'; | 645 | al->level = 'g'; |
616 | mg = &machine->kmaps; | 646 | mg = &machine->kmaps; |
647 | load_map = true; | ||
617 | } else { | 648 | } else { |
618 | /* | 649 | /* |
619 | * 'u' means guest os user space. | 650 | * 'u' means guest os user space. |
@@ -654,18 +685,25 @@ try_again: | |||
654 | mg = &machine->kmaps; | 685 | mg = &machine->kmaps; |
655 | goto try_again; | 686 | goto try_again; |
656 | } | 687 | } |
657 | } else | 688 | } else { |
689 | /* | ||
690 | * Kernel maps might be changed when loading symbols so loading | ||
691 | * must be done prior to using kernel maps. | ||
692 | */ | ||
693 | if (load_map) | ||
694 | map__load(al->map, machine->symbol_filter); | ||
658 | al->addr = al->map->map_ip(al->map, al->addr); | 695 | al->addr = al->map->map_ip(al->map, al->addr); |
696 | } | ||
659 | } | 697 | } |
660 | 698 | ||
661 | void thread__find_addr_location(struct thread *thread, struct machine *machine, | 699 | void thread__find_addr_location(struct thread *thread, struct machine *machine, |
662 | u8 cpumode, enum map_type type, u64 addr, | 700 | u8 cpumode, enum map_type type, u64 addr, |
663 | struct addr_location *al, | 701 | struct addr_location *al) |
664 | symbol_filter_t filter) | ||
665 | { | 702 | { |
666 | thread__find_addr_map(thread, machine, cpumode, type, addr, al); | 703 | thread__find_addr_map(thread, machine, cpumode, type, addr, al); |
667 | if (al->map != NULL) | 704 | if (al->map != NULL) |
668 | al->sym = map__find_symbol(al->map, al->addr, filter); | 705 | al->sym = map__find_symbol(al->map, al->addr, |
706 | machine->symbol_filter); | ||
669 | else | 707 | else |
670 | al->sym = NULL; | 708 | al->sym = NULL; |
671 | } | 709 | } |
@@ -673,11 +711,11 @@ void thread__find_addr_location(struct thread *thread, struct machine *machine, | |||
673 | int perf_event__preprocess_sample(const union perf_event *event, | 711 | int perf_event__preprocess_sample(const union perf_event *event, |
674 | struct machine *machine, | 712 | struct machine *machine, |
675 | struct addr_location *al, | 713 | struct addr_location *al, |
676 | struct perf_sample *sample, | 714 | struct perf_sample *sample) |
677 | symbol_filter_t filter) | ||
678 | { | 715 | { |
679 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 716 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
680 | struct thread *thread = machine__findnew_thread(machine, event->ip.pid); | 717 | struct thread *thread = machine__findnew_thread(machine, sample->pid, |
718 | sample->pid); | ||
681 | 719 | ||
682 | if (thread == NULL) | 720 | if (thread == NULL) |
683 | return -1; | 721 | return -1; |
@@ -686,7 +724,7 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
686 | !strlist__has_entry(symbol_conf.comm_list, thread->comm)) | 724 | !strlist__has_entry(symbol_conf.comm_list, thread->comm)) |
687 | goto out_filtered; | 725 | goto out_filtered; |
688 | 726 | ||
689 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 727 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->tid); |
690 | /* | 728 | /* |
691 | * Have we already created the kernel maps for this machine? | 729 | * Have we already created the kernel maps for this machine? |
692 | * | 730 | * |
@@ -699,7 +737,7 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
699 | machine__create_kernel_maps(machine); | 737 | machine__create_kernel_maps(machine); |
700 | 738 | ||
701 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, | 739 | thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION, |
702 | event->ip.ip, al); | 740 | sample->ip, al); |
703 | dump_printf(" ...... dso: %s\n", | 741 | dump_printf(" ...... dso: %s\n", |
704 | al->map ? al->map->dso->long_name : | 742 | al->map ? al->map->dso->long_name : |
705 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 743 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
@@ -717,7 +755,8 @@ int perf_event__preprocess_sample(const union perf_event *event, | |||
717 | dso->long_name))))) | 755 | dso->long_name))))) |
718 | goto out_filtered; | 756 | goto out_filtered; |
719 | 757 | ||
720 | al->sym = map__find_symbol(al->map, al->addr, filter); | 758 | al->sym = map__find_symbol(al->map, al->addr, |
759 | machine->symbol_filter); | ||
721 | } | 760 | } |
722 | 761 | ||
723 | if (symbol_conf.sym_list && | 762 | if (symbol_conf.sym_list && |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 181389535c0c..c67ecc457d29 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -8,22 +8,25 @@ | |||
8 | #include "map.h" | 8 | #include "map.h" |
9 | #include "build-id.h" | 9 | #include "build-id.h" |
10 | 10 | ||
11 | /* | 11 | struct mmap_event { |
12 | * PERF_SAMPLE_IP | PERF_SAMPLE_TID | * | ||
13 | */ | ||
14 | struct ip_event { | ||
15 | struct perf_event_header header; | 12 | struct perf_event_header header; |
16 | u64 ip; | ||
17 | u32 pid, tid; | 13 | u32 pid, tid; |
18 | unsigned char __more_data[]; | 14 | u64 start; |
15 | u64 len; | ||
16 | u64 pgoff; | ||
17 | char filename[PATH_MAX]; | ||
19 | }; | 18 | }; |
20 | 19 | ||
21 | struct mmap_event { | 20 | struct mmap2_event { |
22 | struct perf_event_header header; | 21 | struct perf_event_header header; |
23 | u32 pid, tid; | 22 | u32 pid, tid; |
24 | u64 start; | 23 | u64 start; |
25 | u64 len; | 24 | u64 len; |
26 | u64 pgoff; | 25 | u64 pgoff; |
26 | u32 maj; | ||
27 | u32 min; | ||
28 | u64 ino; | ||
29 | u64 ino_generation; | ||
27 | char filename[PATH_MAX]; | 30 | char filename[PATH_MAX]; |
28 | }; | 31 | }; |
29 | 32 | ||
@@ -63,7 +66,8 @@ struct read_event { | |||
63 | (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \ | 66 | (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \ |
64 | PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | \ | 67 | PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | \ |
65 | PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID | \ | 68 | PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID | \ |
66 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD) | 69 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | \ |
70 | PERF_SAMPLE_IDENTIFIER) | ||
67 | 71 | ||
68 | struct sample_event { | 72 | struct sample_event { |
69 | struct perf_event_header header; | 73 | struct perf_event_header header; |
@@ -71,6 +75,7 @@ struct sample_event { | |||
71 | }; | 75 | }; |
72 | 76 | ||
73 | struct regs_dump { | 77 | struct regs_dump { |
78 | u64 abi; | ||
74 | u64 *regs; | 79 | u64 *regs; |
75 | }; | 80 | }; |
76 | 81 | ||
@@ -80,6 +85,23 @@ struct stack_dump { | |||
80 | char *data; | 85 | char *data; |
81 | }; | 86 | }; |
82 | 87 | ||
88 | struct sample_read_value { | ||
89 | u64 value; | ||
90 | u64 id; | ||
91 | }; | ||
92 | |||
93 | struct sample_read { | ||
94 | u64 time_enabled; | ||
95 | u64 time_running; | ||
96 | union { | ||
97 | struct { | ||
98 | u64 nr; | ||
99 | struct sample_read_value *values; | ||
100 | } group; | ||
101 | struct sample_read_value one; | ||
102 | }; | ||
103 | }; | ||
104 | |||
83 | struct perf_sample { | 105 | struct perf_sample { |
84 | u64 ip; | 106 | u64 ip; |
85 | u32 pid, tid; | 107 | u32 pid, tid; |
@@ -97,6 +119,7 @@ struct perf_sample { | |||
97 | struct branch_stack *branch_stack; | 119 | struct branch_stack *branch_stack; |
98 | struct regs_dump user_regs; | 120 | struct regs_dump user_regs; |
99 | struct stack_dump user_stack; | 121 | struct stack_dump user_stack; |
122 | struct sample_read read; | ||
100 | }; | 123 | }; |
101 | 124 | ||
102 | #define PERF_MEM_DATA_SRC_NONE \ | 125 | #define PERF_MEM_DATA_SRC_NONE \ |
@@ -116,7 +139,7 @@ struct build_id_event { | |||
116 | enum perf_user_event_type { /* above any possible kernel type */ | 139 | enum perf_user_event_type { /* above any possible kernel type */ |
117 | PERF_RECORD_USER_TYPE_START = 64, | 140 | PERF_RECORD_USER_TYPE_START = 64, |
118 | PERF_RECORD_HEADER_ATTR = 64, | 141 | PERF_RECORD_HEADER_ATTR = 64, |
119 | PERF_RECORD_HEADER_EVENT_TYPE = 65, | 142 | PERF_RECORD_HEADER_EVENT_TYPE = 65, /* depreceated */ |
120 | PERF_RECORD_HEADER_TRACING_DATA = 66, | 143 | PERF_RECORD_HEADER_TRACING_DATA = 66, |
121 | PERF_RECORD_HEADER_BUILD_ID = 67, | 144 | PERF_RECORD_HEADER_BUILD_ID = 67, |
122 | PERF_RECORD_FINISHED_ROUND = 68, | 145 | PERF_RECORD_FINISHED_ROUND = 68, |
@@ -148,8 +171,8 @@ struct tracing_data_event { | |||
148 | 171 | ||
149 | union perf_event { | 172 | union perf_event { |
150 | struct perf_event_header header; | 173 | struct perf_event_header header; |
151 | struct ip_event ip; | ||
152 | struct mmap_event mmap; | 174 | struct mmap_event mmap; |
175 | struct mmap2_event mmap2; | ||
153 | struct comm_event comm; | 176 | struct comm_event comm; |
154 | struct fork_event fork; | 177 | struct fork_event fork; |
155 | struct lost_event lost; | 178 | struct lost_event lost; |
@@ -199,6 +222,10 @@ int perf_event__process_mmap(struct perf_tool *tool, | |||
199 | union perf_event *event, | 222 | union perf_event *event, |
200 | struct perf_sample *sample, | 223 | struct perf_sample *sample, |
201 | struct machine *machine); | 224 | struct machine *machine); |
225 | int perf_event__process_mmap2(struct perf_tool *tool, | ||
226 | union perf_event *event, | ||
227 | struct perf_sample *sample, | ||
228 | struct machine *machine); | ||
202 | int perf_event__process_fork(struct perf_tool *tool, | 229 | int perf_event__process_fork(struct perf_tool *tool, |
203 | union perf_event *event, | 230 | union perf_event *event, |
204 | struct perf_sample *sample, | 231 | struct perf_sample *sample, |
@@ -216,17 +243,20 @@ struct addr_location; | |||
216 | int perf_event__preprocess_sample(const union perf_event *self, | 243 | int perf_event__preprocess_sample(const union perf_event *self, |
217 | struct machine *machine, | 244 | struct machine *machine, |
218 | struct addr_location *al, | 245 | struct addr_location *al, |
219 | struct perf_sample *sample, | 246 | struct perf_sample *sample); |
220 | symbol_filter_t filter); | ||
221 | 247 | ||
222 | const char *perf_event__name(unsigned int id); | 248 | const char *perf_event__name(unsigned int id); |
223 | 249 | ||
250 | size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, | ||
251 | u64 sample_regs_user, u64 read_format); | ||
224 | int perf_event__synthesize_sample(union perf_event *event, u64 type, | 252 | int perf_event__synthesize_sample(union perf_event *event, u64 type, |
253 | u64 sample_regs_user, u64 read_format, | ||
225 | const struct perf_sample *sample, | 254 | const struct perf_sample *sample, |
226 | bool swapped); | 255 | bool swapped); |
227 | 256 | ||
228 | size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp); | 257 | size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp); |
229 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp); | 258 | size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp); |
259 | size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp); | ||
230 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp); | 260 | size_t perf_event__fprintf_task(union perf_event *event, FILE *fp); |
231 | size_t perf_event__fprintf(union perf_event *event, FILE *fp); | 261 | size_t perf_event__fprintf(union perf_event *event, FILE *fp); |
232 | 262 | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 8065ce8fa9a5..e584cd30b0f2 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "target.h" | 14 | #include "target.h" |
15 | #include "evlist.h" | 15 | #include "evlist.h" |
16 | #include "evsel.h" | 16 | #include "evsel.h" |
17 | #include "debug.h" | ||
17 | #include <unistd.h> | 18 | #include <unistd.h> |
18 | 19 | ||
19 | #include "parse-events.h" | 20 | #include "parse-events.h" |
@@ -48,26 +49,29 @@ struct perf_evlist *perf_evlist__new(void) | |||
48 | return evlist; | 49 | return evlist; |
49 | } | 50 | } |
50 | 51 | ||
51 | void perf_evlist__config(struct perf_evlist *evlist, | 52 | /** |
52 | struct perf_record_opts *opts) | 53 | * perf_evlist__set_id_pos - set the positions of event ids. |
54 | * @evlist: selected event list | ||
55 | * | ||
56 | * Events with compatible sample types all have the same id_pos | ||
57 | * and is_pos. For convenience, put a copy on evlist. | ||
58 | */ | ||
59 | void perf_evlist__set_id_pos(struct perf_evlist *evlist) | ||
53 | { | 60 | { |
54 | struct perf_evsel *evsel; | 61 | struct perf_evsel *first = perf_evlist__first(evlist); |
55 | /* | ||
56 | * Set the evsel leader links before we configure attributes, | ||
57 | * since some might depend on this info. | ||
58 | */ | ||
59 | if (opts->group) | ||
60 | perf_evlist__set_leader(evlist); | ||
61 | 62 | ||
62 | if (evlist->cpus->map[0] < 0) | 63 | evlist->id_pos = first->id_pos; |
63 | opts->no_inherit = true; | 64 | evlist->is_pos = first->is_pos; |
65 | } | ||
64 | 66 | ||
65 | list_for_each_entry(evsel, &evlist->entries, node) { | 67 | static void perf_evlist__update_id_pos(struct perf_evlist *evlist) |
66 | perf_evsel__config(evsel, opts); | 68 | { |
69 | struct perf_evsel *evsel; | ||
67 | 70 | ||
68 | if (evlist->nr_entries > 1) | 71 | list_for_each_entry(evsel, &evlist->entries, node) |
69 | perf_evsel__set_sample_id(evsel); | 72 | perf_evsel__calc_id_pos(evsel); |
70 | } | 73 | |
74 | perf_evlist__set_id_pos(evlist); | ||
71 | } | 75 | } |
72 | 76 | ||
73 | static void perf_evlist__purge(struct perf_evlist *evlist) | 77 | static void perf_evlist__purge(struct perf_evlist *evlist) |
@@ -100,15 +104,20 @@ void perf_evlist__delete(struct perf_evlist *evlist) | |||
100 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | 104 | void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) |
101 | { | 105 | { |
102 | list_add_tail(&entry->node, &evlist->entries); | 106 | list_add_tail(&entry->node, &evlist->entries); |
103 | ++evlist->nr_entries; | 107 | if (!evlist->nr_entries++) |
108 | perf_evlist__set_id_pos(evlist); | ||
104 | } | 109 | } |
105 | 110 | ||
106 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | 111 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, |
107 | struct list_head *list, | 112 | struct list_head *list, |
108 | int nr_entries) | 113 | int nr_entries) |
109 | { | 114 | { |
115 | bool set_id_pos = !evlist->nr_entries; | ||
116 | |||
110 | list_splice_tail(list, &evlist->entries); | 117 | list_splice_tail(list, &evlist->entries); |
111 | evlist->nr_entries += nr_entries; | 118 | evlist->nr_entries += nr_entries; |
119 | if (set_id_pos) | ||
120 | perf_evlist__set_id_pos(evlist); | ||
112 | } | 121 | } |
113 | 122 | ||
114 | void __perf_evlist__set_leader(struct list_head *list) | 123 | void __perf_evlist__set_leader(struct list_head *list) |
@@ -209,6 +218,21 @@ perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) | |||
209 | return NULL; | 218 | return NULL; |
210 | } | 219 | } |
211 | 220 | ||
221 | struct perf_evsel * | ||
222 | perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, | ||
223 | const char *name) | ||
224 | { | ||
225 | struct perf_evsel *evsel; | ||
226 | |||
227 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
228 | if ((evsel->attr.type == PERF_TYPE_TRACEPOINT) && | ||
229 | (strcmp(evsel->name, name) == 0)) | ||
230 | return evsel; | ||
231 | } | ||
232 | |||
233 | return NULL; | ||
234 | } | ||
235 | |||
212 | int perf_evlist__add_newtp(struct perf_evlist *evlist, | 236 | int perf_evlist__add_newtp(struct perf_evlist *evlist, |
213 | const char *sys, const char *name, void *handler) | 237 | const char *sys, const char *name, void *handler) |
214 | { | 238 | { |
@@ -232,7 +256,7 @@ void perf_evlist__disable(struct perf_evlist *evlist) | |||
232 | 256 | ||
233 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 257 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
234 | list_for_each_entry(pos, &evlist->entries, node) { | 258 | list_for_each_entry(pos, &evlist->entries, node) { |
235 | if (!perf_evsel__is_group_leader(pos)) | 259 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) |
236 | continue; | 260 | continue; |
237 | for (thread = 0; thread < nr_threads; thread++) | 261 | for (thread = 0; thread < nr_threads; thread++) |
238 | ioctl(FD(pos, cpu, thread), | 262 | ioctl(FD(pos, cpu, thread), |
@@ -250,7 +274,7 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
250 | 274 | ||
251 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 275 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
252 | list_for_each_entry(pos, &evlist->entries, node) { | 276 | list_for_each_entry(pos, &evlist->entries, node) { |
253 | if (!perf_evsel__is_group_leader(pos)) | 277 | if (!perf_evsel__is_group_leader(pos) || !pos->fd) |
254 | continue; | 278 | continue; |
255 | for (thread = 0; thread < nr_threads; thread++) | 279 | for (thread = 0; thread < nr_threads; thread++) |
256 | ioctl(FD(pos, cpu, thread), | 280 | ioctl(FD(pos, cpu, thread), |
@@ -259,6 +283,44 @@ void perf_evlist__enable(struct perf_evlist *evlist) | |||
259 | } | 283 | } |
260 | } | 284 | } |
261 | 285 | ||
286 | int perf_evlist__disable_event(struct perf_evlist *evlist, | ||
287 | struct perf_evsel *evsel) | ||
288 | { | ||
289 | int cpu, thread, err; | ||
290 | |||
291 | if (!evsel->fd) | ||
292 | return 0; | ||
293 | |||
294 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | ||
295 | for (thread = 0; thread < evlist->threads->nr; thread++) { | ||
296 | err = ioctl(FD(evsel, cpu, thread), | ||
297 | PERF_EVENT_IOC_DISABLE, 0); | ||
298 | if (err) | ||
299 | return err; | ||
300 | } | ||
301 | } | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | int perf_evlist__enable_event(struct perf_evlist *evlist, | ||
306 | struct perf_evsel *evsel) | ||
307 | { | ||
308 | int cpu, thread, err; | ||
309 | |||
310 | if (!evsel->fd) | ||
311 | return -EINVAL; | ||
312 | |||
313 | for (cpu = 0; cpu < evlist->cpus->nr; cpu++) { | ||
314 | for (thread = 0; thread < evlist->threads->nr; thread++) { | ||
315 | err = ioctl(FD(evsel, cpu, thread), | ||
316 | PERF_EVENT_IOC_ENABLE, 0); | ||
317 | if (err) | ||
318 | return err; | ||
319 | } | ||
320 | } | ||
321 | return 0; | ||
322 | } | ||
323 | |||
262 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) | 324 | static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist) |
263 | { | 325 | { |
264 | int nr_cpus = cpu_map__nr(evlist->cpus); | 326 | int nr_cpus = cpu_map__nr(evlist->cpus); |
@@ -302,6 +364,24 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist, | |||
302 | { | 364 | { |
303 | u64 read_data[4] = { 0, }; | 365 | u64 read_data[4] = { 0, }; |
304 | int id_idx = 1; /* The first entry is the counter value */ | 366 | int id_idx = 1; /* The first entry is the counter value */ |
367 | u64 id; | ||
368 | int ret; | ||
369 | |||
370 | ret = ioctl(fd, PERF_EVENT_IOC_ID, &id); | ||
371 | if (!ret) | ||
372 | goto add; | ||
373 | |||
374 | if (errno != ENOTTY) | ||
375 | return -1; | ||
376 | |||
377 | /* Legacy way to get event id.. All hail to old kernels! */ | ||
378 | |||
379 | /* | ||
380 | * This way does not work with group format read, so bail | ||
381 | * out in that case. | ||
382 | */ | ||
383 | if (perf_evlist__read_format(evlist) & PERF_FORMAT_GROUP) | ||
384 | return -1; | ||
305 | 385 | ||
306 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || | 386 | if (!(evsel->attr.read_format & PERF_FORMAT_ID) || |
307 | read(fd, &read_data, sizeof(read_data)) == -1) | 387 | read(fd, &read_data, sizeof(read_data)) == -1) |
@@ -312,25 +392,39 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist, | |||
312 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | 392 | if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) |
313 | ++id_idx; | 393 | ++id_idx; |
314 | 394 | ||
315 | perf_evlist__id_add(evlist, evsel, cpu, thread, read_data[id_idx]); | 395 | id = read_data[id_idx]; |
396 | |||
397 | add: | ||
398 | perf_evlist__id_add(evlist, evsel, cpu, thread, id); | ||
316 | return 0; | 399 | return 0; |
317 | } | 400 | } |
318 | 401 | ||
319 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | 402 | struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id) |
320 | { | 403 | { |
321 | struct hlist_head *head; | 404 | struct hlist_head *head; |
322 | struct perf_sample_id *sid; | 405 | struct perf_sample_id *sid; |
323 | int hash; | 406 | int hash; |
324 | 407 | ||
325 | if (evlist->nr_entries == 1) | ||
326 | return perf_evlist__first(evlist); | ||
327 | |||
328 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | 408 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); |
329 | head = &evlist->heads[hash]; | 409 | head = &evlist->heads[hash]; |
330 | 410 | ||
331 | hlist_for_each_entry(sid, head, node) | 411 | hlist_for_each_entry(sid, head, node) |
332 | if (sid->id == id) | 412 | if (sid->id == id) |
333 | return sid->evsel; | 413 | return sid; |
414 | |||
415 | return NULL; | ||
416 | } | ||
417 | |||
418 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | ||
419 | { | ||
420 | struct perf_sample_id *sid; | ||
421 | |||
422 | if (evlist->nr_entries == 1) | ||
423 | return perf_evlist__first(evlist); | ||
424 | |||
425 | sid = perf_evlist__id2sid(evlist, id); | ||
426 | if (sid) | ||
427 | return sid->evsel; | ||
334 | 428 | ||
335 | if (!perf_evlist__sample_id_all(evlist)) | 429 | if (!perf_evlist__sample_id_all(evlist)) |
336 | return perf_evlist__first(evlist); | 430 | return perf_evlist__first(evlist); |
@@ -338,6 +432,60 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
338 | return NULL; | 432 | return NULL; |
339 | } | 433 | } |
340 | 434 | ||
435 | static int perf_evlist__event2id(struct perf_evlist *evlist, | ||
436 | union perf_event *event, u64 *id) | ||
437 | { | ||
438 | const u64 *array = event->sample.array; | ||
439 | ssize_t n; | ||
440 | |||
441 | n = (event->header.size - sizeof(event->header)) >> 3; | ||
442 | |||
443 | if (event->header.type == PERF_RECORD_SAMPLE) { | ||
444 | if (evlist->id_pos >= n) | ||
445 | return -1; | ||
446 | *id = array[evlist->id_pos]; | ||
447 | } else { | ||
448 | if (evlist->is_pos > n) | ||
449 | return -1; | ||
450 | n -= evlist->is_pos; | ||
451 | *id = array[n]; | ||
452 | } | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | static struct perf_evsel *perf_evlist__event2evsel(struct perf_evlist *evlist, | ||
457 | union perf_event *event) | ||
458 | { | ||
459 | struct perf_evsel *first = perf_evlist__first(evlist); | ||
460 | struct hlist_head *head; | ||
461 | struct perf_sample_id *sid; | ||
462 | int hash; | ||
463 | u64 id; | ||
464 | |||
465 | if (evlist->nr_entries == 1) | ||
466 | return first; | ||
467 | |||
468 | if (!first->attr.sample_id_all && | ||
469 | event->header.type != PERF_RECORD_SAMPLE) | ||
470 | return first; | ||
471 | |||
472 | if (perf_evlist__event2id(evlist, event, &id)) | ||
473 | return NULL; | ||
474 | |||
475 | /* Synthesized events have an id of zero */ | ||
476 | if (!id) | ||
477 | return first; | ||
478 | |||
479 | hash = hash_64(id, PERF_EVLIST__HLIST_BITS); | ||
480 | head = &evlist->heads[hash]; | ||
481 | |||
482 | hlist_for_each_entry(sid, head, node) { | ||
483 | if (sid->id == id) | ||
484 | return sid->evsel; | ||
485 | } | ||
486 | return NULL; | ||
487 | } | ||
488 | |||
341 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | 489 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) |
342 | { | 490 | { |
343 | struct perf_mmap *md = &evlist->mmap[idx]; | 491 | struct perf_mmap *md = &evlist->mmap[idx]; |
@@ -397,22 +545,33 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *evlist, int idx) | |||
397 | 545 | ||
398 | md->prev = old; | 546 | md->prev = old; |
399 | 547 | ||
400 | if (!evlist->overwrite) | 548 | return event; |
549 | } | ||
550 | |||
551 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx) | ||
552 | { | ||
553 | if (!evlist->overwrite) { | ||
554 | struct perf_mmap *md = &evlist->mmap[idx]; | ||
555 | unsigned int old = md->prev; | ||
556 | |||
401 | perf_mmap__write_tail(md, old); | 557 | perf_mmap__write_tail(md, old); |
558 | } | ||
559 | } | ||
402 | 560 | ||
403 | return event; | 561 | static void __perf_evlist__munmap(struct perf_evlist *evlist, int idx) |
562 | { | ||
563 | if (evlist->mmap[idx].base != NULL) { | ||
564 | munmap(evlist->mmap[idx].base, evlist->mmap_len); | ||
565 | evlist->mmap[idx].base = NULL; | ||
566 | } | ||
404 | } | 567 | } |
405 | 568 | ||
406 | void perf_evlist__munmap(struct perf_evlist *evlist) | 569 | void perf_evlist__munmap(struct perf_evlist *evlist) |
407 | { | 570 | { |
408 | int i; | 571 | int i; |
409 | 572 | ||
410 | for (i = 0; i < evlist->nr_mmaps; i++) { | 573 | for (i = 0; i < evlist->nr_mmaps; i++) |
411 | if (evlist->mmap[i].base != NULL) { | 574 | __perf_evlist__munmap(evlist, i); |
412 | munmap(evlist->mmap[i].base, evlist->mmap_len); | ||
413 | evlist->mmap[i].base = NULL; | ||
414 | } | ||
415 | } | ||
416 | 575 | ||
417 | free(evlist->mmap); | 576 | free(evlist->mmap); |
418 | evlist->mmap = NULL; | 577 | evlist->mmap = NULL; |
@@ -421,7 +580,7 @@ void perf_evlist__munmap(struct perf_evlist *evlist) | |||
421 | static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) | 580 | static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) |
422 | { | 581 | { |
423 | evlist->nr_mmaps = cpu_map__nr(evlist->cpus); | 582 | evlist->nr_mmaps = cpu_map__nr(evlist->cpus); |
424 | if (cpu_map__all(evlist->cpus)) | 583 | if (cpu_map__empty(evlist->cpus)) |
425 | evlist->nr_mmaps = thread_map__nr(evlist->threads); | 584 | evlist->nr_mmaps = thread_map__nr(evlist->threads); |
426 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); | 585 | evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap)); |
427 | return evlist->mmap != NULL ? 0 : -ENOMEM; | 586 | return evlist->mmap != NULL ? 0 : -ENOMEM; |
@@ -450,6 +609,7 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m | |||
450 | int nr_cpus = cpu_map__nr(evlist->cpus); | 609 | int nr_cpus = cpu_map__nr(evlist->cpus); |
451 | int nr_threads = thread_map__nr(evlist->threads); | 610 | int nr_threads = thread_map__nr(evlist->threads); |
452 | 611 | ||
612 | pr_debug2("perf event ring buffer mmapped per cpu\n"); | ||
453 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 613 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
454 | int output = -1; | 614 | int output = -1; |
455 | 615 | ||
@@ -477,12 +637,8 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m | |||
477 | return 0; | 637 | return 0; |
478 | 638 | ||
479 | out_unmap: | 639 | out_unmap: |
480 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 640 | for (cpu = 0; cpu < nr_cpus; cpu++) |
481 | if (evlist->mmap[cpu].base != NULL) { | 641 | __perf_evlist__munmap(evlist, cpu); |
482 | munmap(evlist->mmap[cpu].base, evlist->mmap_len); | ||
483 | evlist->mmap[cpu].base = NULL; | ||
484 | } | ||
485 | } | ||
486 | return -1; | 642 | return -1; |
487 | } | 643 | } |
488 | 644 | ||
@@ -492,6 +648,7 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in | |||
492 | int thread; | 648 | int thread; |
493 | int nr_threads = thread_map__nr(evlist->threads); | 649 | int nr_threads = thread_map__nr(evlist->threads); |
494 | 650 | ||
651 | pr_debug2("perf event ring buffer mmapped per thread\n"); | ||
495 | for (thread = 0; thread < nr_threads; thread++) { | 652 | for (thread = 0; thread < nr_threads; thread++) { |
496 | int output = -1; | 653 | int output = -1; |
497 | 654 | ||
@@ -517,12 +674,8 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in | |||
517 | return 0; | 674 | return 0; |
518 | 675 | ||
519 | out_unmap: | 676 | out_unmap: |
520 | for (thread = 0; thread < nr_threads; thread++) { | 677 | for (thread = 0; thread < nr_threads; thread++) |
521 | if (evlist->mmap[thread].base != NULL) { | 678 | __perf_evlist__munmap(evlist, thread); |
522 | munmap(evlist->mmap[thread].base, evlist->mmap_len); | ||
523 | evlist->mmap[thread].base = NULL; | ||
524 | } | ||
525 | } | ||
526 | return -1; | 679 | return -1; |
527 | } | 680 | } |
528 | 681 | ||
@@ -573,7 +726,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
573 | return -ENOMEM; | 726 | return -ENOMEM; |
574 | } | 727 | } |
575 | 728 | ||
576 | if (cpu_map__all(cpus)) | 729 | if (cpu_map__empty(cpus)) |
577 | return perf_evlist__mmap_per_thread(evlist, prot, mask); | 730 | return perf_evlist__mmap_per_thread(evlist, prot, mask); |
578 | 731 | ||
579 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); | 732 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); |
@@ -650,20 +803,66 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) | |||
650 | 803 | ||
651 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) | 804 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) |
652 | { | 805 | { |
806 | struct perf_evsel *pos; | ||
807 | |||
808 | if (evlist->nr_entries == 1) | ||
809 | return true; | ||
810 | |||
811 | if (evlist->id_pos < 0 || evlist->is_pos < 0) | ||
812 | return false; | ||
813 | |||
814 | list_for_each_entry(pos, &evlist->entries, node) { | ||
815 | if (pos->id_pos != evlist->id_pos || | ||
816 | pos->is_pos != evlist->is_pos) | ||
817 | return false; | ||
818 | } | ||
819 | |||
820 | return true; | ||
821 | } | ||
822 | |||
823 | u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist) | ||
824 | { | ||
825 | struct perf_evsel *evsel; | ||
826 | |||
827 | if (evlist->combined_sample_type) | ||
828 | return evlist->combined_sample_type; | ||
829 | |||
830 | list_for_each_entry(evsel, &evlist->entries, node) | ||
831 | evlist->combined_sample_type |= evsel->attr.sample_type; | ||
832 | |||
833 | return evlist->combined_sample_type; | ||
834 | } | ||
835 | |||
836 | u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist) | ||
837 | { | ||
838 | evlist->combined_sample_type = 0; | ||
839 | return __perf_evlist__combined_sample_type(evlist); | ||
840 | } | ||
841 | |||
842 | bool perf_evlist__valid_read_format(struct perf_evlist *evlist) | ||
843 | { | ||
653 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; | 844 | struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; |
845 | u64 read_format = first->attr.read_format; | ||
846 | u64 sample_type = first->attr.sample_type; | ||
654 | 847 | ||
655 | list_for_each_entry_continue(pos, &evlist->entries, node) { | 848 | list_for_each_entry_continue(pos, &evlist->entries, node) { |
656 | if (first->attr.sample_type != pos->attr.sample_type) | 849 | if (read_format != pos->attr.read_format) |
657 | return false; | 850 | return false; |
658 | } | 851 | } |
659 | 852 | ||
853 | /* PERF_SAMPLE_READ imples PERF_FORMAT_ID. */ | ||
854 | if ((sample_type & PERF_SAMPLE_READ) && | ||
855 | !(read_format & PERF_FORMAT_ID)) { | ||
856 | return false; | ||
857 | } | ||
858 | |||
660 | return true; | 859 | return true; |
661 | } | 860 | } |
662 | 861 | ||
663 | u64 perf_evlist__sample_type(struct perf_evlist *evlist) | 862 | u64 perf_evlist__read_format(struct perf_evlist *evlist) |
664 | { | 863 | { |
665 | struct perf_evsel *first = perf_evlist__first(evlist); | 864 | struct perf_evsel *first = perf_evlist__first(evlist); |
666 | return first->attr.sample_type; | 865 | return first->attr.read_format; |
667 | } | 866 | } |
668 | 867 | ||
669 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist) | 868 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist) |
@@ -692,6 +891,9 @@ u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist) | |||
692 | 891 | ||
693 | if (sample_type & PERF_SAMPLE_CPU) | 892 | if (sample_type & PERF_SAMPLE_CPU) |
694 | size += sizeof(data->cpu) * 2; | 893 | size += sizeof(data->cpu) * 2; |
894 | |||
895 | if (sample_type & PERF_SAMPLE_IDENTIFIER) | ||
896 | size += sizeof(data->id); | ||
695 | out: | 897 | out: |
696 | return size; | 898 | return size; |
697 | } | 899 | } |
@@ -735,6 +937,8 @@ int perf_evlist__open(struct perf_evlist *evlist) | |||
735 | struct perf_evsel *evsel; | 937 | struct perf_evsel *evsel; |
736 | int err; | 938 | int err; |
737 | 939 | ||
940 | perf_evlist__update_id_pos(evlist); | ||
941 | |||
738 | list_for_each_entry(evsel, &evlist->entries, node) { | 942 | list_for_each_entry(evsel, &evlist->entries, node) { |
739 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); | 943 | err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); |
740 | if (err < 0) | 944 | if (err < 0) |
@@ -783,13 +987,6 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
783 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); | 987 | fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC); |
784 | 988 | ||
785 | /* | 989 | /* |
786 | * Do a dummy execvp to get the PLT entry resolved, | ||
787 | * so we avoid the resolver overhead on the real | ||
788 | * execvp call. | ||
789 | */ | ||
790 | execvp("", (char **)argv); | ||
791 | |||
792 | /* | ||
793 | * Tell the parent we're ready to go | 990 | * Tell the parent we're ready to go |
794 | */ | 991 | */ |
795 | close(child_ready_pipe[1]); | 992 | close(child_ready_pipe[1]); |
@@ -838,7 +1035,7 @@ out_close_ready_pipe: | |||
838 | int perf_evlist__start_workload(struct perf_evlist *evlist) | 1035 | int perf_evlist__start_workload(struct perf_evlist *evlist) |
839 | { | 1036 | { |
840 | if (evlist->workload.cork_fd > 0) { | 1037 | if (evlist->workload.cork_fd > 0) { |
841 | char bf; | 1038 | char bf = 0; |
842 | int ret; | 1039 | int ret; |
843 | /* | 1040 | /* |
844 | * Remove the cork, let it rip! | 1041 | * Remove the cork, let it rip! |
@@ -857,7 +1054,10 @@ int perf_evlist__start_workload(struct perf_evlist *evlist) | |||
857 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, | 1054 | int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event, |
858 | struct perf_sample *sample) | 1055 | struct perf_sample *sample) |
859 | { | 1056 | { |
860 | struct perf_evsel *evsel = perf_evlist__first(evlist); | 1057 | struct perf_evsel *evsel = perf_evlist__event2evsel(evlist, event); |
1058 | |||
1059 | if (!evsel) | ||
1060 | return -EFAULT; | ||
861 | return perf_evsel__parse_sample(evsel, event, sample); | 1061 | return perf_evsel__parse_sample(evsel, event, sample); |
862 | } | 1062 | } |
863 | 1063 | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 0583d36252be..206d09339306 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -32,6 +32,9 @@ struct perf_evlist { | |||
32 | int nr_fds; | 32 | int nr_fds; |
33 | int nr_mmaps; | 33 | int nr_mmaps; |
34 | int mmap_len; | 34 | int mmap_len; |
35 | int id_pos; | ||
36 | int is_pos; | ||
37 | u64 combined_sample_type; | ||
35 | struct { | 38 | struct { |
36 | int cork_fd; | 39 | int cork_fd; |
37 | pid_t pid; | 40 | pid_t pid; |
@@ -71,6 +74,10 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter); | |||
71 | struct perf_evsel * | 74 | struct perf_evsel * |
72 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id); | 75 | perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id); |
73 | 76 | ||
77 | struct perf_evsel * | ||
78 | perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, | ||
79 | const char *name); | ||
80 | |||
74 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, | 81 | void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel, |
75 | int cpu, int thread, u64 id); | 82 | int cpu, int thread, u64 id); |
76 | 83 | ||
@@ -78,11 +85,17 @@ void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd); | |||
78 | 85 | ||
79 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); | 86 | struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id); |
80 | 87 | ||
88 | struct perf_sample_id *perf_evlist__id2sid(struct perf_evlist *evlist, u64 id); | ||
89 | |||
81 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); | 90 | union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx); |
82 | 91 | ||
92 | void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx); | ||
93 | |||
83 | int perf_evlist__open(struct perf_evlist *evlist); | 94 | int perf_evlist__open(struct perf_evlist *evlist); |
84 | void perf_evlist__close(struct perf_evlist *evlist); | 95 | void perf_evlist__close(struct perf_evlist *evlist); |
85 | 96 | ||
97 | void perf_evlist__set_id_pos(struct perf_evlist *evlist); | ||
98 | bool perf_can_sample_identifier(void); | ||
86 | void perf_evlist__config(struct perf_evlist *evlist, | 99 | void perf_evlist__config(struct perf_evlist *evlist, |
87 | struct perf_record_opts *opts); | 100 | struct perf_record_opts *opts); |
88 | 101 | ||
@@ -99,6 +112,11 @@ void perf_evlist__munmap(struct perf_evlist *evlist); | |||
99 | void perf_evlist__disable(struct perf_evlist *evlist); | 112 | void perf_evlist__disable(struct perf_evlist *evlist); |
100 | void perf_evlist__enable(struct perf_evlist *evlist); | 113 | void perf_evlist__enable(struct perf_evlist *evlist); |
101 | 114 | ||
115 | int perf_evlist__disable_event(struct perf_evlist *evlist, | ||
116 | struct perf_evsel *evsel); | ||
117 | int perf_evlist__enable_event(struct perf_evlist *evlist, | ||
118 | struct perf_evsel *evsel); | ||
119 | |||
102 | void perf_evlist__set_selected(struct perf_evlist *evlist, | 120 | void perf_evlist__set_selected(struct perf_evlist *evlist, |
103 | struct perf_evsel *evsel); | 121 | struct perf_evsel *evsel); |
104 | 122 | ||
@@ -118,7 +136,9 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist); | |||
118 | void __perf_evlist__set_leader(struct list_head *list); | 136 | void __perf_evlist__set_leader(struct list_head *list); |
119 | void perf_evlist__set_leader(struct perf_evlist *evlist); | 137 | void perf_evlist__set_leader(struct perf_evlist *evlist); |
120 | 138 | ||
121 | u64 perf_evlist__sample_type(struct perf_evlist *evlist); | 139 | u64 perf_evlist__read_format(struct perf_evlist *evlist); |
140 | u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist); | ||
141 | u64 perf_evlist__combined_sample_type(struct perf_evlist *evlist); | ||
122 | bool perf_evlist__sample_id_all(struct perf_evlist *evlist); | 142 | bool perf_evlist__sample_id_all(struct perf_evlist *evlist); |
123 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist); | 143 | u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist); |
124 | 144 | ||
@@ -127,6 +147,7 @@ int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *even | |||
127 | 147 | ||
128 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist); | 148 | bool perf_evlist__valid_sample_type(struct perf_evlist *evlist); |
129 | bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist); | 149 | bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist); |
150 | bool perf_evlist__valid_read_format(struct perf_evlist *evlist); | ||
130 | 151 | ||
131 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | 152 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, |
132 | struct list_head *list, | 153 | struct list_head *list, |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index c9c7494506a1..9f1ef9bee2d0 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -9,27 +9,30 @@ | |||
9 | 9 | ||
10 | #include <byteswap.h> | 10 | #include <byteswap.h> |
11 | #include <linux/bitops.h> | 11 | #include <linux/bitops.h> |
12 | #include "asm/bug.h" | ||
13 | #include <lk/debugfs.h> | 12 | #include <lk/debugfs.h> |
14 | #include "event-parse.h" | 13 | #include <traceevent/event-parse.h> |
14 | #include <linux/hw_breakpoint.h> | ||
15 | #include <linux/perf_event.h> | ||
16 | #include <sys/resource.h> | ||
17 | #include "asm/bug.h" | ||
15 | #include "evsel.h" | 18 | #include "evsel.h" |
16 | #include "evlist.h" | 19 | #include "evlist.h" |
17 | #include "util.h" | 20 | #include "util.h" |
18 | #include "cpumap.h" | 21 | #include "cpumap.h" |
19 | #include "thread_map.h" | 22 | #include "thread_map.h" |
20 | #include "target.h" | 23 | #include "target.h" |
21 | #include <linux/hw_breakpoint.h> | ||
22 | #include <linux/perf_event.h> | ||
23 | #include "perf_regs.h" | 24 | #include "perf_regs.h" |
25 | #include "debug.h" | ||
24 | 26 | ||
25 | static struct { | 27 | static struct { |
26 | bool sample_id_all; | 28 | bool sample_id_all; |
27 | bool exclude_guest; | 29 | bool exclude_guest; |
30 | bool mmap2; | ||
28 | } perf_missing_features; | 31 | } perf_missing_features; |
29 | 32 | ||
30 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) | 33 | #define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y)) |
31 | 34 | ||
32 | static int __perf_evsel__sample_size(u64 sample_type) | 35 | int __perf_evsel__sample_size(u64 sample_type) |
33 | { | 36 | { |
34 | u64 mask = sample_type & PERF_SAMPLE_MASK; | 37 | u64 mask = sample_type & PERF_SAMPLE_MASK; |
35 | int size = 0; | 38 | int size = 0; |
@@ -45,6 +48,72 @@ static int __perf_evsel__sample_size(u64 sample_type) | |||
45 | return size; | 48 | return size; |
46 | } | 49 | } |
47 | 50 | ||
51 | /** | ||
52 | * __perf_evsel__calc_id_pos - calculate id_pos. | ||
53 | * @sample_type: sample type | ||
54 | * | ||
55 | * This function returns the position of the event id (PERF_SAMPLE_ID or | ||
56 | * PERF_SAMPLE_IDENTIFIER) in a sample event i.e. in the array of struct | ||
57 | * sample_event. | ||
58 | */ | ||
59 | static int __perf_evsel__calc_id_pos(u64 sample_type) | ||
60 | { | ||
61 | int idx = 0; | ||
62 | |||
63 | if (sample_type & PERF_SAMPLE_IDENTIFIER) | ||
64 | return 0; | ||
65 | |||
66 | if (!(sample_type & PERF_SAMPLE_ID)) | ||
67 | return -1; | ||
68 | |||
69 | if (sample_type & PERF_SAMPLE_IP) | ||
70 | idx += 1; | ||
71 | |||
72 | if (sample_type & PERF_SAMPLE_TID) | ||
73 | idx += 1; | ||
74 | |||
75 | if (sample_type & PERF_SAMPLE_TIME) | ||
76 | idx += 1; | ||
77 | |||
78 | if (sample_type & PERF_SAMPLE_ADDR) | ||
79 | idx += 1; | ||
80 | |||
81 | return idx; | ||
82 | } | ||
83 | |||
84 | /** | ||
85 | * __perf_evsel__calc_is_pos - calculate is_pos. | ||
86 | * @sample_type: sample type | ||
87 | * | ||
88 | * This function returns the position (counting backwards) of the event id | ||
89 | * (PERF_SAMPLE_ID or PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if | ||
90 | * sample_id_all is used there is an id sample appended to non-sample events. | ||
91 | */ | ||
92 | static int __perf_evsel__calc_is_pos(u64 sample_type) | ||
93 | { | ||
94 | int idx = 1; | ||
95 | |||
96 | if (sample_type & PERF_SAMPLE_IDENTIFIER) | ||
97 | return 1; | ||
98 | |||
99 | if (!(sample_type & PERF_SAMPLE_ID)) | ||
100 | return -1; | ||
101 | |||
102 | if (sample_type & PERF_SAMPLE_CPU) | ||
103 | idx += 1; | ||
104 | |||
105 | if (sample_type & PERF_SAMPLE_STREAM_ID) | ||
106 | idx += 1; | ||
107 | |||
108 | return idx; | ||
109 | } | ||
110 | |||
111 | void perf_evsel__calc_id_pos(struct perf_evsel *evsel) | ||
112 | { | ||
113 | evsel->id_pos = __perf_evsel__calc_id_pos(evsel->attr.sample_type); | ||
114 | evsel->is_pos = __perf_evsel__calc_is_pos(evsel->attr.sample_type); | ||
115 | } | ||
116 | |||
48 | void hists__init(struct hists *hists) | 117 | void hists__init(struct hists *hists) |
49 | { | 118 | { |
50 | memset(hists, 0, sizeof(*hists)); | 119 | memset(hists, 0, sizeof(*hists)); |
@@ -61,6 +130,7 @@ void __perf_evsel__set_sample_bit(struct perf_evsel *evsel, | |||
61 | if (!(evsel->attr.sample_type & bit)) { | 130 | if (!(evsel->attr.sample_type & bit)) { |
62 | evsel->attr.sample_type |= bit; | 131 | evsel->attr.sample_type |= bit; |
63 | evsel->sample_size += sizeof(u64); | 132 | evsel->sample_size += sizeof(u64); |
133 | perf_evsel__calc_id_pos(evsel); | ||
64 | } | 134 | } |
65 | } | 135 | } |
66 | 136 | ||
@@ -70,12 +140,19 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel, | |||
70 | if (evsel->attr.sample_type & bit) { | 140 | if (evsel->attr.sample_type & bit) { |
71 | evsel->attr.sample_type &= ~bit; | 141 | evsel->attr.sample_type &= ~bit; |
72 | evsel->sample_size -= sizeof(u64); | 142 | evsel->sample_size -= sizeof(u64); |
143 | perf_evsel__calc_id_pos(evsel); | ||
73 | } | 144 | } |
74 | } | 145 | } |
75 | 146 | ||
76 | void perf_evsel__set_sample_id(struct perf_evsel *evsel) | 147 | void perf_evsel__set_sample_id(struct perf_evsel *evsel, |
148 | bool can_sample_identifier) | ||
77 | { | 149 | { |
78 | perf_evsel__set_sample_bit(evsel, ID); | 150 | if (can_sample_identifier) { |
151 | perf_evsel__reset_sample_bit(evsel, ID); | ||
152 | perf_evsel__set_sample_bit(evsel, IDENTIFIER); | ||
153 | } else { | ||
154 | perf_evsel__set_sample_bit(evsel, ID); | ||
155 | } | ||
79 | evsel->attr.read_format |= PERF_FORMAT_ID; | 156 | evsel->attr.read_format |= PERF_FORMAT_ID; |
80 | } | 157 | } |
81 | 158 | ||
@@ -88,6 +165,7 @@ void perf_evsel__init(struct perf_evsel *evsel, | |||
88 | INIT_LIST_HEAD(&evsel->node); | 165 | INIT_LIST_HEAD(&evsel->node); |
89 | hists__init(&evsel->hists); | 166 | hists__init(&evsel->hists); |
90 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); | 167 | evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); |
168 | perf_evsel__calc_id_pos(evsel); | ||
91 | } | 169 | } |
92 | 170 | ||
93 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | 171 | struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) |
@@ -246,6 +324,7 @@ const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = { | |||
246 | "major-faults", | 324 | "major-faults", |
247 | "alignment-faults", | 325 | "alignment-faults", |
248 | "emulation-faults", | 326 | "emulation-faults", |
327 | "dummy", | ||
249 | }; | 328 | }; |
250 | 329 | ||
251 | static const char *__perf_evsel__sw_name(u64 config) | 330 | static const char *__perf_evsel__sw_name(u64 config) |
@@ -490,6 +569,7 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) | |||
490 | void perf_evsel__config(struct perf_evsel *evsel, | 569 | void perf_evsel__config(struct perf_evsel *evsel, |
491 | struct perf_record_opts *opts) | 570 | struct perf_record_opts *opts) |
492 | { | 571 | { |
572 | struct perf_evsel *leader = evsel->leader; | ||
493 | struct perf_event_attr *attr = &evsel->attr; | 573 | struct perf_event_attr *attr = &evsel->attr; |
494 | int track = !evsel->idx; /* only the first counter needs these */ | 574 | int track = !evsel->idx; /* only the first counter needs these */ |
495 | 575 | ||
@@ -499,6 +579,25 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
499 | perf_evsel__set_sample_bit(evsel, IP); | 579 | perf_evsel__set_sample_bit(evsel, IP); |
500 | perf_evsel__set_sample_bit(evsel, TID); | 580 | perf_evsel__set_sample_bit(evsel, TID); |
501 | 581 | ||
582 | if (evsel->sample_read) { | ||
583 | perf_evsel__set_sample_bit(evsel, READ); | ||
584 | |||
585 | /* | ||
586 | * We need ID even in case of single event, because | ||
587 | * PERF_SAMPLE_READ process ID specific data. | ||
588 | */ | ||
589 | perf_evsel__set_sample_id(evsel, false); | ||
590 | |||
591 | /* | ||
592 | * Apply group format only if we belong to group | ||
593 | * with more than one members. | ||
594 | */ | ||
595 | if (leader->nr_members > 1) { | ||
596 | attr->read_format |= PERF_FORMAT_GROUP; | ||
597 | attr->inherit = 0; | ||
598 | } | ||
599 | } | ||
600 | |||
502 | /* | 601 | /* |
503 | * We default some events to a 1 default interval. But keep | 602 | * We default some events to a 1 default interval. But keep |
504 | * it a weak assumption overridable by the user. | 603 | * it a weak assumption overridable by the user. |
@@ -514,6 +613,15 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
514 | } | 613 | } |
515 | } | 614 | } |
516 | 615 | ||
616 | /* | ||
617 | * Disable sampling for all group members other | ||
618 | * than leader in case leader 'leads' the sampling. | ||
619 | */ | ||
620 | if ((leader != evsel) && leader->sample_read) { | ||
621 | attr->sample_freq = 0; | ||
622 | attr->sample_period = 0; | ||
623 | } | ||
624 | |||
517 | if (opts->no_samples) | 625 | if (opts->no_samples) |
518 | attr->sample_freq = 0; | 626 | attr->sample_freq = 0; |
519 | 627 | ||
@@ -569,8 +677,8 @@ void perf_evsel__config(struct perf_evsel *evsel, | |||
569 | if (opts->sample_weight) | 677 | if (opts->sample_weight) |
570 | attr->sample_type |= PERF_SAMPLE_WEIGHT; | 678 | attr->sample_type |= PERF_SAMPLE_WEIGHT; |
571 | 679 | ||
572 | attr->mmap = track; | 680 | attr->mmap = track; |
573 | attr->comm = track; | 681 | attr->comm = track; |
574 | 682 | ||
575 | /* | 683 | /* |
576 | * XXX see the function comment above | 684 | * XXX see the function comment above |
@@ -605,15 +713,15 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | |||
605 | return evsel->fd != NULL ? 0 : -ENOMEM; | 713 | return evsel->fd != NULL ? 0 : -ENOMEM; |
606 | } | 714 | } |
607 | 715 | ||
608 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | 716 | static int perf_evsel__run_ioctl(struct perf_evsel *evsel, int ncpus, int nthreads, |
609 | const char *filter) | 717 | int ioc, void *arg) |
610 | { | 718 | { |
611 | int cpu, thread; | 719 | int cpu, thread; |
612 | 720 | ||
613 | for (cpu = 0; cpu < ncpus; cpu++) { | 721 | for (cpu = 0; cpu < ncpus; cpu++) { |
614 | for (thread = 0; thread < nthreads; thread++) { | 722 | for (thread = 0; thread < nthreads; thread++) { |
615 | int fd = FD(evsel, cpu, thread), | 723 | int fd = FD(evsel, cpu, thread), |
616 | err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter); | 724 | err = ioctl(fd, ioc, arg); |
617 | 725 | ||
618 | if (err) | 726 | if (err) |
619 | return err; | 727 | return err; |
@@ -623,6 +731,21 @@ int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | |||
623 | return 0; | 731 | return 0; |
624 | } | 732 | } |
625 | 733 | ||
734 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | ||
735 | const char *filter) | ||
736 | { | ||
737 | return perf_evsel__run_ioctl(evsel, ncpus, nthreads, | ||
738 | PERF_EVENT_IOC_SET_FILTER, | ||
739 | (void *)filter); | ||
740 | } | ||
741 | |||
742 | int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) | ||
743 | { | ||
744 | return perf_evsel__run_ioctl(evsel, ncpus, nthreads, | ||
745 | PERF_EVENT_IOC_ENABLE, | ||
746 | 0); | ||
747 | } | ||
748 | |||
626 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) | 749 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) |
627 | { | 750 | { |
628 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); | 751 | evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id)); |
@@ -817,12 +940,72 @@ static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread) | |||
817 | return fd; | 940 | return fd; |
818 | } | 941 | } |
819 | 942 | ||
943 | #define __PRINT_ATTR(fmt, cast, field) \ | ||
944 | fprintf(fp, " %-19s "fmt"\n", #field, cast attr->field) | ||
945 | |||
946 | #define PRINT_ATTR_U32(field) __PRINT_ATTR("%u" , , field) | ||
947 | #define PRINT_ATTR_X32(field) __PRINT_ATTR("%#x", , field) | ||
948 | #define PRINT_ATTR_U64(field) __PRINT_ATTR("%" PRIu64, (uint64_t), field) | ||
949 | #define PRINT_ATTR_X64(field) __PRINT_ATTR("%#"PRIx64, (uint64_t), field) | ||
950 | |||
951 | #define PRINT_ATTR2N(name1, field1, name2, field2) \ | ||
952 | fprintf(fp, " %-19s %u %-19s %u\n", \ | ||
953 | name1, attr->field1, name2, attr->field2) | ||
954 | |||
955 | #define PRINT_ATTR2(field1, field2) \ | ||
956 | PRINT_ATTR2N(#field1, field1, #field2, field2) | ||
957 | |||
958 | static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | ||
959 | { | ||
960 | size_t ret = 0; | ||
961 | |||
962 | ret += fprintf(fp, "%.60s\n", graph_dotted_line); | ||
963 | ret += fprintf(fp, "perf_event_attr:\n"); | ||
964 | |||
965 | ret += PRINT_ATTR_U32(type); | ||
966 | ret += PRINT_ATTR_U32(size); | ||
967 | ret += PRINT_ATTR_X64(config); | ||
968 | ret += PRINT_ATTR_U64(sample_period); | ||
969 | ret += PRINT_ATTR_U64(sample_freq); | ||
970 | ret += PRINT_ATTR_X64(sample_type); | ||
971 | ret += PRINT_ATTR_X64(read_format); | ||
972 | |||
973 | ret += PRINT_ATTR2(disabled, inherit); | ||
974 | ret += PRINT_ATTR2(pinned, exclusive); | ||
975 | ret += PRINT_ATTR2(exclude_user, exclude_kernel); | ||
976 | ret += PRINT_ATTR2(exclude_hv, exclude_idle); | ||
977 | ret += PRINT_ATTR2(mmap, comm); | ||
978 | ret += PRINT_ATTR2(freq, inherit_stat); | ||
979 | ret += PRINT_ATTR2(enable_on_exec, task); | ||
980 | ret += PRINT_ATTR2(watermark, precise_ip); | ||
981 | ret += PRINT_ATTR2(mmap_data, sample_id_all); | ||
982 | ret += PRINT_ATTR2(exclude_host, exclude_guest); | ||
983 | ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, | ||
984 | "excl.callchain_user", exclude_callchain_user); | ||
985 | |||
986 | ret += PRINT_ATTR_U32(wakeup_events); | ||
987 | ret += PRINT_ATTR_U32(wakeup_watermark); | ||
988 | ret += PRINT_ATTR_X32(bp_type); | ||
989 | ret += PRINT_ATTR_X64(bp_addr); | ||
990 | ret += PRINT_ATTR_X64(config1); | ||
991 | ret += PRINT_ATTR_U64(bp_len); | ||
992 | ret += PRINT_ATTR_X64(config2); | ||
993 | ret += PRINT_ATTR_X64(branch_sample_type); | ||
994 | ret += PRINT_ATTR_X64(sample_regs_user); | ||
995 | ret += PRINT_ATTR_U32(sample_stack_user); | ||
996 | |||
997 | ret += fprintf(fp, "%.60s\n", graph_dotted_line); | ||
998 | |||
999 | return ret; | ||
1000 | } | ||
1001 | |||
820 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | 1002 | static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, |
821 | struct thread_map *threads) | 1003 | struct thread_map *threads) |
822 | { | 1004 | { |
823 | int cpu, thread; | 1005 | int cpu, thread; |
824 | unsigned long flags = 0; | 1006 | unsigned long flags = 0; |
825 | int pid = -1, err; | 1007 | int pid = -1, err; |
1008 | enum { NO_CHANGE, SET_TO_MAX, INCREASED_MAX } set_rlimit = NO_CHANGE; | ||
826 | 1009 | ||
827 | if (evsel->fd == NULL && | 1010 | if (evsel->fd == NULL && |
828 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) | 1011 | perf_evsel__alloc_fd(evsel, cpus->nr, threads->nr) < 0) |
@@ -834,12 +1017,17 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus, | |||
834 | } | 1017 | } |
835 | 1018 | ||
836 | fallback_missing_features: | 1019 | fallback_missing_features: |
1020 | if (perf_missing_features.mmap2) | ||
1021 | evsel->attr.mmap2 = 0; | ||
837 | if (perf_missing_features.exclude_guest) | 1022 | if (perf_missing_features.exclude_guest) |
838 | evsel->attr.exclude_guest = evsel->attr.exclude_host = 0; | 1023 | evsel->attr.exclude_guest = evsel->attr.exclude_host = 0; |
839 | retry_sample_id: | 1024 | retry_sample_id: |
840 | if (perf_missing_features.sample_id_all) | 1025 | if (perf_missing_features.sample_id_all) |
841 | evsel->attr.sample_id_all = 0; | 1026 | evsel->attr.sample_id_all = 0; |
842 | 1027 | ||
1028 | if (verbose >= 2) | ||
1029 | perf_event_attr__fprintf(&evsel->attr, stderr); | ||
1030 | |||
843 | for (cpu = 0; cpu < cpus->nr; cpu++) { | 1031 | for (cpu = 0; cpu < cpus->nr; cpu++) { |
844 | 1032 | ||
845 | for (thread = 0; thread < threads->nr; thread++) { | 1033 | for (thread = 0; thread < threads->nr; thread++) { |
@@ -849,6 +1037,9 @@ retry_sample_id: | |||
849 | pid = threads->map[thread]; | 1037 | pid = threads->map[thread]; |
850 | 1038 | ||
851 | group_fd = get_group_fd(evsel, cpu, thread); | 1039 | group_fd = get_group_fd(evsel, cpu, thread); |
1040 | retry_open: | ||
1041 | pr_debug2("perf_event_open: pid %d cpu %d group_fd %d flags %#lx\n", | ||
1042 | pid, cpus->map[cpu], group_fd, flags); | ||
852 | 1043 | ||
853 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, | 1044 | FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr, |
854 | pid, | 1045 | pid, |
@@ -858,17 +1049,45 @@ retry_sample_id: | |||
858 | err = -errno; | 1049 | err = -errno; |
859 | goto try_fallback; | 1050 | goto try_fallback; |
860 | } | 1051 | } |
1052 | set_rlimit = NO_CHANGE; | ||
861 | } | 1053 | } |
862 | } | 1054 | } |
863 | 1055 | ||
864 | return 0; | 1056 | return 0; |
865 | 1057 | ||
866 | try_fallback: | 1058 | try_fallback: |
1059 | /* | ||
1060 | * perf stat needs between 5 and 22 fds per CPU. When we run out | ||
1061 | * of them try to increase the limits. | ||
1062 | */ | ||
1063 | if (err == -EMFILE && set_rlimit < INCREASED_MAX) { | ||
1064 | struct rlimit l; | ||
1065 | int old_errno = errno; | ||
1066 | |||
1067 | if (getrlimit(RLIMIT_NOFILE, &l) == 0) { | ||
1068 | if (set_rlimit == NO_CHANGE) | ||
1069 | l.rlim_cur = l.rlim_max; | ||
1070 | else { | ||
1071 | l.rlim_cur = l.rlim_max + 1000; | ||
1072 | l.rlim_max = l.rlim_cur; | ||
1073 | } | ||
1074 | if (setrlimit(RLIMIT_NOFILE, &l) == 0) { | ||
1075 | set_rlimit++; | ||
1076 | errno = old_errno; | ||
1077 | goto retry_open; | ||
1078 | } | ||
1079 | } | ||
1080 | errno = old_errno; | ||
1081 | } | ||
1082 | |||
867 | if (err != -EINVAL || cpu > 0 || thread > 0) | 1083 | if (err != -EINVAL || cpu > 0 || thread > 0) |
868 | goto out_close; | 1084 | goto out_close; |
869 | 1085 | ||
870 | if (!perf_missing_features.exclude_guest && | 1086 | if (!perf_missing_features.mmap2 && evsel->attr.mmap2) { |
871 | (evsel->attr.exclude_guest || evsel->attr.exclude_host)) { | 1087 | perf_missing_features.mmap2 = true; |
1088 | goto fallback_missing_features; | ||
1089 | } else if (!perf_missing_features.exclude_guest && | ||
1090 | (evsel->attr.exclude_guest || evsel->attr.exclude_host)) { | ||
872 | perf_missing_features.exclude_guest = true; | 1091 | perf_missing_features.exclude_guest = true; |
873 | goto fallback_missing_features; | 1092 | goto fallback_missing_features; |
874 | } else if (!perf_missing_features.sample_id_all) { | 1093 | } else if (!perf_missing_features.sample_id_all) { |
@@ -951,6 +1170,11 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, | |||
951 | array += ((event->header.size - | 1170 | array += ((event->header.size - |
952 | sizeof(event->header)) / sizeof(u64)) - 1; | 1171 | sizeof(event->header)) / sizeof(u64)) - 1; |
953 | 1172 | ||
1173 | if (type & PERF_SAMPLE_IDENTIFIER) { | ||
1174 | sample->id = *array; | ||
1175 | array--; | ||
1176 | } | ||
1177 | |||
954 | if (type & PERF_SAMPLE_CPU) { | 1178 | if (type & PERF_SAMPLE_CPU) { |
955 | u.val64 = *array; | 1179 | u.val64 = *array; |
956 | if (swapped) { | 1180 | if (swapped) { |
@@ -994,24 +1218,30 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, | |||
994 | return 0; | 1218 | return 0; |
995 | } | 1219 | } |
996 | 1220 | ||
997 | static bool sample_overlap(const union perf_event *event, | 1221 | static inline bool overflow(const void *endp, u16 max_size, const void *offset, |
998 | const void *offset, u64 size) | 1222 | u64 size) |
999 | { | 1223 | { |
1000 | const void *base = event; | 1224 | return size > max_size || offset + size > endp; |
1225 | } | ||
1001 | 1226 | ||
1002 | if (offset + size > base + event->header.size) | 1227 | #define OVERFLOW_CHECK(offset, size, max_size) \ |
1003 | return true; | 1228 | do { \ |
1229 | if (overflow(endp, (max_size), (offset), (size))) \ | ||
1230 | return -EFAULT; \ | ||
1231 | } while (0) | ||
1004 | 1232 | ||
1005 | return false; | 1233 | #define OVERFLOW_CHECK_u64(offset) \ |
1006 | } | 1234 | OVERFLOW_CHECK(offset, sizeof(u64), sizeof(u64)) |
1007 | 1235 | ||
1008 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | 1236 | int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, |
1009 | struct perf_sample *data) | 1237 | struct perf_sample *data) |
1010 | { | 1238 | { |
1011 | u64 type = evsel->attr.sample_type; | 1239 | u64 type = evsel->attr.sample_type; |
1012 | u64 regs_user = evsel->attr.sample_regs_user; | ||
1013 | bool swapped = evsel->needs_swap; | 1240 | bool swapped = evsel->needs_swap; |
1014 | const u64 *array; | 1241 | const u64 *array; |
1242 | u16 max_size = event->header.size; | ||
1243 | const void *endp = (void *)event + max_size; | ||
1244 | u64 sz; | ||
1015 | 1245 | ||
1016 | /* | 1246 | /* |
1017 | * used for cross-endian analysis. See git commit 65014ab3 | 1247 | * used for cross-endian analysis. See git commit 65014ab3 |
@@ -1033,11 +1263,22 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1033 | 1263 | ||
1034 | array = event->sample.array; | 1264 | array = event->sample.array; |
1035 | 1265 | ||
1266 | /* | ||
1267 | * The evsel's sample_size is based on PERF_SAMPLE_MASK which includes | ||
1268 | * up to PERF_SAMPLE_PERIOD. After that overflow() must be used to | ||
1269 | * check the format does not go past the end of the event. | ||
1270 | */ | ||
1036 | if (evsel->sample_size + sizeof(event->header) > event->header.size) | 1271 | if (evsel->sample_size + sizeof(event->header) > event->header.size) |
1037 | return -EFAULT; | 1272 | return -EFAULT; |
1038 | 1273 | ||
1274 | data->id = -1ULL; | ||
1275 | if (type & PERF_SAMPLE_IDENTIFIER) { | ||
1276 | data->id = *array; | ||
1277 | array++; | ||
1278 | } | ||
1279 | |||
1039 | if (type & PERF_SAMPLE_IP) { | 1280 | if (type & PERF_SAMPLE_IP) { |
1040 | data->ip = event->ip.ip; | 1281 | data->ip = *array; |
1041 | array++; | 1282 | array++; |
1042 | } | 1283 | } |
1043 | 1284 | ||
@@ -1066,7 +1307,6 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1066 | array++; | 1307 | array++; |
1067 | } | 1308 | } |
1068 | 1309 | ||
1069 | data->id = -1ULL; | ||
1070 | if (type & PERF_SAMPLE_ID) { | 1310 | if (type & PERF_SAMPLE_ID) { |
1071 | data->id = *array; | 1311 | data->id = *array; |
1072 | array++; | 1312 | array++; |
@@ -1096,25 +1336,62 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1096 | } | 1336 | } |
1097 | 1337 | ||
1098 | if (type & PERF_SAMPLE_READ) { | 1338 | if (type & PERF_SAMPLE_READ) { |
1099 | fprintf(stderr, "PERF_SAMPLE_READ is unsupported for now\n"); | 1339 | u64 read_format = evsel->attr.read_format; |
1100 | return -1; | 1340 | |
1341 | OVERFLOW_CHECK_u64(array); | ||
1342 | if (read_format & PERF_FORMAT_GROUP) | ||
1343 | data->read.group.nr = *array; | ||
1344 | else | ||
1345 | data->read.one.value = *array; | ||
1346 | |||
1347 | array++; | ||
1348 | |||
1349 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { | ||
1350 | OVERFLOW_CHECK_u64(array); | ||
1351 | data->read.time_enabled = *array; | ||
1352 | array++; | ||
1353 | } | ||
1354 | |||
1355 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { | ||
1356 | OVERFLOW_CHECK_u64(array); | ||
1357 | data->read.time_running = *array; | ||
1358 | array++; | ||
1359 | } | ||
1360 | |||
1361 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | ||
1362 | if (read_format & PERF_FORMAT_GROUP) { | ||
1363 | const u64 max_group_nr = UINT64_MAX / | ||
1364 | sizeof(struct sample_read_value); | ||
1365 | |||
1366 | if (data->read.group.nr > max_group_nr) | ||
1367 | return -EFAULT; | ||
1368 | sz = data->read.group.nr * | ||
1369 | sizeof(struct sample_read_value); | ||
1370 | OVERFLOW_CHECK(array, sz, max_size); | ||
1371 | data->read.group.values = | ||
1372 | (struct sample_read_value *)array; | ||
1373 | array = (void *)array + sz; | ||
1374 | } else { | ||
1375 | OVERFLOW_CHECK_u64(array); | ||
1376 | data->read.one.id = *array; | ||
1377 | array++; | ||
1378 | } | ||
1101 | } | 1379 | } |
1102 | 1380 | ||
1103 | if (type & PERF_SAMPLE_CALLCHAIN) { | 1381 | if (type & PERF_SAMPLE_CALLCHAIN) { |
1104 | if (sample_overlap(event, array, sizeof(data->callchain->nr))) | 1382 | const u64 max_callchain_nr = UINT64_MAX / sizeof(u64); |
1105 | return -EFAULT; | ||
1106 | |||
1107 | data->callchain = (struct ip_callchain *)array; | ||
1108 | 1383 | ||
1109 | if (sample_overlap(event, array, data->callchain->nr)) | 1384 | OVERFLOW_CHECK_u64(array); |
1385 | data->callchain = (struct ip_callchain *)array++; | ||
1386 | if (data->callchain->nr > max_callchain_nr) | ||
1110 | return -EFAULT; | 1387 | return -EFAULT; |
1111 | 1388 | sz = data->callchain->nr * sizeof(u64); | |
1112 | array += 1 + data->callchain->nr; | 1389 | OVERFLOW_CHECK(array, sz, max_size); |
1390 | array = (void *)array + sz; | ||
1113 | } | 1391 | } |
1114 | 1392 | ||
1115 | if (type & PERF_SAMPLE_RAW) { | 1393 | if (type & PERF_SAMPLE_RAW) { |
1116 | const u64 *pdata; | 1394 | OVERFLOW_CHECK_u64(array); |
1117 | |||
1118 | u.val64 = *array; | 1395 | u.val64 = *array; |
1119 | if (WARN_ONCE(swapped, | 1396 | if (WARN_ONCE(swapped, |
1120 | "Endianness of raw data not corrected!\n")) { | 1397 | "Endianness of raw data not corrected!\n")) { |
@@ -1123,65 +1400,71 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1123 | u.val32[0] = bswap_32(u.val32[0]); | 1400 | u.val32[0] = bswap_32(u.val32[0]); |
1124 | u.val32[1] = bswap_32(u.val32[1]); | 1401 | u.val32[1] = bswap_32(u.val32[1]); |
1125 | } | 1402 | } |
1126 | |||
1127 | if (sample_overlap(event, array, sizeof(u32))) | ||
1128 | return -EFAULT; | ||
1129 | |||
1130 | data->raw_size = u.val32[0]; | 1403 | data->raw_size = u.val32[0]; |
1131 | pdata = (void *) array + sizeof(u32); | 1404 | array = (void *)array + sizeof(u32); |
1132 | 1405 | ||
1133 | if (sample_overlap(event, pdata, data->raw_size)) | 1406 | OVERFLOW_CHECK(array, data->raw_size, max_size); |
1134 | return -EFAULT; | 1407 | data->raw_data = (void *)array; |
1135 | 1408 | array = (void *)array + data->raw_size; | |
1136 | data->raw_data = (void *) pdata; | ||
1137 | |||
1138 | array = (void *)array + data->raw_size + sizeof(u32); | ||
1139 | } | 1409 | } |
1140 | 1410 | ||
1141 | if (type & PERF_SAMPLE_BRANCH_STACK) { | 1411 | if (type & PERF_SAMPLE_BRANCH_STACK) { |
1142 | u64 sz; | 1412 | const u64 max_branch_nr = UINT64_MAX / |
1413 | sizeof(struct branch_entry); | ||
1143 | 1414 | ||
1144 | data->branch_stack = (struct branch_stack *)array; | 1415 | OVERFLOW_CHECK_u64(array); |
1145 | array++; /* nr */ | 1416 | data->branch_stack = (struct branch_stack *)array++; |
1146 | 1417 | ||
1418 | if (data->branch_stack->nr > max_branch_nr) | ||
1419 | return -EFAULT; | ||
1147 | sz = data->branch_stack->nr * sizeof(struct branch_entry); | 1420 | sz = data->branch_stack->nr * sizeof(struct branch_entry); |
1148 | sz /= sizeof(u64); | 1421 | OVERFLOW_CHECK(array, sz, max_size); |
1149 | array += sz; | 1422 | array = (void *)array + sz; |
1150 | } | 1423 | } |
1151 | 1424 | ||
1152 | if (type & PERF_SAMPLE_REGS_USER) { | 1425 | if (type & PERF_SAMPLE_REGS_USER) { |
1153 | /* First u64 tells us if we have any regs in sample. */ | 1426 | OVERFLOW_CHECK_u64(array); |
1154 | u64 avail = *array++; | 1427 | data->user_regs.abi = *array; |
1428 | array++; | ||
1429 | |||
1430 | if (data->user_regs.abi) { | ||
1431 | u64 regs_user = evsel->attr.sample_regs_user; | ||
1155 | 1432 | ||
1156 | if (avail) { | 1433 | sz = hweight_long(regs_user) * sizeof(u64); |
1434 | OVERFLOW_CHECK(array, sz, max_size); | ||
1157 | data->user_regs.regs = (u64 *)array; | 1435 | data->user_regs.regs = (u64 *)array; |
1158 | array += hweight_long(regs_user); | 1436 | array = (void *)array + sz; |
1159 | } | 1437 | } |
1160 | } | 1438 | } |
1161 | 1439 | ||
1162 | if (type & PERF_SAMPLE_STACK_USER) { | 1440 | if (type & PERF_SAMPLE_STACK_USER) { |
1163 | u64 size = *array++; | 1441 | OVERFLOW_CHECK_u64(array); |
1442 | sz = *array++; | ||
1164 | 1443 | ||
1165 | data->user_stack.offset = ((char *)(array - 1) | 1444 | data->user_stack.offset = ((char *)(array - 1) |
1166 | - (char *) event); | 1445 | - (char *) event); |
1167 | 1446 | ||
1168 | if (!size) { | 1447 | if (!sz) { |
1169 | data->user_stack.size = 0; | 1448 | data->user_stack.size = 0; |
1170 | } else { | 1449 | } else { |
1450 | OVERFLOW_CHECK(array, sz, max_size); | ||
1171 | data->user_stack.data = (char *)array; | 1451 | data->user_stack.data = (char *)array; |
1172 | array += size / sizeof(*array); | 1452 | array = (void *)array + sz; |
1453 | OVERFLOW_CHECK_u64(array); | ||
1173 | data->user_stack.size = *array++; | 1454 | data->user_stack.size = *array++; |
1174 | } | 1455 | } |
1175 | } | 1456 | } |
1176 | 1457 | ||
1177 | data->weight = 0; | 1458 | data->weight = 0; |
1178 | if (type & PERF_SAMPLE_WEIGHT) { | 1459 | if (type & PERF_SAMPLE_WEIGHT) { |
1460 | OVERFLOW_CHECK_u64(array); | ||
1179 | data->weight = *array; | 1461 | data->weight = *array; |
1180 | array++; | 1462 | array++; |
1181 | } | 1463 | } |
1182 | 1464 | ||
1183 | data->data_src = PERF_MEM_DATA_SRC_NONE; | 1465 | data->data_src = PERF_MEM_DATA_SRC_NONE; |
1184 | if (type & PERF_SAMPLE_DATA_SRC) { | 1466 | if (type & PERF_SAMPLE_DATA_SRC) { |
1467 | OVERFLOW_CHECK_u64(array); | ||
1185 | data->data_src = *array; | 1468 | data->data_src = *array; |
1186 | array++; | 1469 | array++; |
1187 | } | 1470 | } |
@@ -1189,12 +1472,105 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event, | |||
1189 | return 0; | 1472 | return 0; |
1190 | } | 1473 | } |
1191 | 1474 | ||
1475 | size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, | ||
1476 | u64 sample_regs_user, u64 read_format) | ||
1477 | { | ||
1478 | size_t sz, result = sizeof(struct sample_event); | ||
1479 | |||
1480 | if (type & PERF_SAMPLE_IDENTIFIER) | ||
1481 | result += sizeof(u64); | ||
1482 | |||
1483 | if (type & PERF_SAMPLE_IP) | ||
1484 | result += sizeof(u64); | ||
1485 | |||
1486 | if (type & PERF_SAMPLE_TID) | ||
1487 | result += sizeof(u64); | ||
1488 | |||
1489 | if (type & PERF_SAMPLE_TIME) | ||
1490 | result += sizeof(u64); | ||
1491 | |||
1492 | if (type & PERF_SAMPLE_ADDR) | ||
1493 | result += sizeof(u64); | ||
1494 | |||
1495 | if (type & PERF_SAMPLE_ID) | ||
1496 | result += sizeof(u64); | ||
1497 | |||
1498 | if (type & PERF_SAMPLE_STREAM_ID) | ||
1499 | result += sizeof(u64); | ||
1500 | |||
1501 | if (type & PERF_SAMPLE_CPU) | ||
1502 | result += sizeof(u64); | ||
1503 | |||
1504 | if (type & PERF_SAMPLE_PERIOD) | ||
1505 | result += sizeof(u64); | ||
1506 | |||
1507 | if (type & PERF_SAMPLE_READ) { | ||
1508 | result += sizeof(u64); | ||
1509 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
1510 | result += sizeof(u64); | ||
1511 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
1512 | result += sizeof(u64); | ||
1513 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | ||
1514 | if (read_format & PERF_FORMAT_GROUP) { | ||
1515 | sz = sample->read.group.nr * | ||
1516 | sizeof(struct sample_read_value); | ||
1517 | result += sz; | ||
1518 | } else { | ||
1519 | result += sizeof(u64); | ||
1520 | } | ||
1521 | } | ||
1522 | |||
1523 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
1524 | sz = (sample->callchain->nr + 1) * sizeof(u64); | ||
1525 | result += sz; | ||
1526 | } | ||
1527 | |||
1528 | if (type & PERF_SAMPLE_RAW) { | ||
1529 | result += sizeof(u32); | ||
1530 | result += sample->raw_size; | ||
1531 | } | ||
1532 | |||
1533 | if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
1534 | sz = sample->branch_stack->nr * sizeof(struct branch_entry); | ||
1535 | sz += sizeof(u64); | ||
1536 | result += sz; | ||
1537 | } | ||
1538 | |||
1539 | if (type & PERF_SAMPLE_REGS_USER) { | ||
1540 | if (sample->user_regs.abi) { | ||
1541 | result += sizeof(u64); | ||
1542 | sz = hweight_long(sample_regs_user) * sizeof(u64); | ||
1543 | result += sz; | ||
1544 | } else { | ||
1545 | result += sizeof(u64); | ||
1546 | } | ||
1547 | } | ||
1548 | |||
1549 | if (type & PERF_SAMPLE_STACK_USER) { | ||
1550 | sz = sample->user_stack.size; | ||
1551 | result += sizeof(u64); | ||
1552 | if (sz) { | ||
1553 | result += sz; | ||
1554 | result += sizeof(u64); | ||
1555 | } | ||
1556 | } | ||
1557 | |||
1558 | if (type & PERF_SAMPLE_WEIGHT) | ||
1559 | result += sizeof(u64); | ||
1560 | |||
1561 | if (type & PERF_SAMPLE_DATA_SRC) | ||
1562 | result += sizeof(u64); | ||
1563 | |||
1564 | return result; | ||
1565 | } | ||
1566 | |||
1192 | int perf_event__synthesize_sample(union perf_event *event, u64 type, | 1567 | int perf_event__synthesize_sample(union perf_event *event, u64 type, |
1568 | u64 sample_regs_user, u64 read_format, | ||
1193 | const struct perf_sample *sample, | 1569 | const struct perf_sample *sample, |
1194 | bool swapped) | 1570 | bool swapped) |
1195 | { | 1571 | { |
1196 | u64 *array; | 1572 | u64 *array; |
1197 | 1573 | size_t sz; | |
1198 | /* | 1574 | /* |
1199 | * used for cross-endian analysis. See git commit 65014ab3 | 1575 | * used for cross-endian analysis. See git commit 65014ab3 |
1200 | * for why this goofiness is needed. | 1576 | * for why this goofiness is needed. |
@@ -1203,8 +1579,13 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, | |||
1203 | 1579 | ||
1204 | array = event->sample.array; | 1580 | array = event->sample.array; |
1205 | 1581 | ||
1582 | if (type & PERF_SAMPLE_IDENTIFIER) { | ||
1583 | *array = sample->id; | ||
1584 | array++; | ||
1585 | } | ||
1586 | |||
1206 | if (type & PERF_SAMPLE_IP) { | 1587 | if (type & PERF_SAMPLE_IP) { |
1207 | event->ip.ip = sample->ip; | 1588 | *array = sample->ip; |
1208 | array++; | 1589 | array++; |
1209 | } | 1590 | } |
1210 | 1591 | ||
@@ -1262,6 +1643,97 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, | |||
1262 | array++; | 1643 | array++; |
1263 | } | 1644 | } |
1264 | 1645 | ||
1646 | if (type & PERF_SAMPLE_READ) { | ||
1647 | if (read_format & PERF_FORMAT_GROUP) | ||
1648 | *array = sample->read.group.nr; | ||
1649 | else | ||
1650 | *array = sample->read.one.value; | ||
1651 | array++; | ||
1652 | |||
1653 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { | ||
1654 | *array = sample->read.time_enabled; | ||
1655 | array++; | ||
1656 | } | ||
1657 | |||
1658 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { | ||
1659 | *array = sample->read.time_running; | ||
1660 | array++; | ||
1661 | } | ||
1662 | |||
1663 | /* PERF_FORMAT_ID is forced for PERF_SAMPLE_READ */ | ||
1664 | if (read_format & PERF_FORMAT_GROUP) { | ||
1665 | sz = sample->read.group.nr * | ||
1666 | sizeof(struct sample_read_value); | ||
1667 | memcpy(array, sample->read.group.values, sz); | ||
1668 | array = (void *)array + sz; | ||
1669 | } else { | ||
1670 | *array = sample->read.one.id; | ||
1671 | array++; | ||
1672 | } | ||
1673 | } | ||
1674 | |||
1675 | if (type & PERF_SAMPLE_CALLCHAIN) { | ||
1676 | sz = (sample->callchain->nr + 1) * sizeof(u64); | ||
1677 | memcpy(array, sample->callchain, sz); | ||
1678 | array = (void *)array + sz; | ||
1679 | } | ||
1680 | |||
1681 | if (type & PERF_SAMPLE_RAW) { | ||
1682 | u.val32[0] = sample->raw_size; | ||
1683 | if (WARN_ONCE(swapped, | ||
1684 | "Endianness of raw data not corrected!\n")) { | ||
1685 | /* | ||
1686 | * Inverse of what is done in perf_evsel__parse_sample | ||
1687 | */ | ||
1688 | u.val32[0] = bswap_32(u.val32[0]); | ||
1689 | u.val32[1] = bswap_32(u.val32[1]); | ||
1690 | u.val64 = bswap_64(u.val64); | ||
1691 | } | ||
1692 | *array = u.val64; | ||
1693 | array = (void *)array + sizeof(u32); | ||
1694 | |||
1695 | memcpy(array, sample->raw_data, sample->raw_size); | ||
1696 | array = (void *)array + sample->raw_size; | ||
1697 | } | ||
1698 | |||
1699 | if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
1700 | sz = sample->branch_stack->nr * sizeof(struct branch_entry); | ||
1701 | sz += sizeof(u64); | ||
1702 | memcpy(array, sample->branch_stack, sz); | ||
1703 | array = (void *)array + sz; | ||
1704 | } | ||
1705 | |||
1706 | if (type & PERF_SAMPLE_REGS_USER) { | ||
1707 | if (sample->user_regs.abi) { | ||
1708 | *array++ = sample->user_regs.abi; | ||
1709 | sz = hweight_long(sample_regs_user) * sizeof(u64); | ||
1710 | memcpy(array, sample->user_regs.regs, sz); | ||
1711 | array = (void *)array + sz; | ||
1712 | } else { | ||
1713 | *array++ = 0; | ||
1714 | } | ||
1715 | } | ||
1716 | |||
1717 | if (type & PERF_SAMPLE_STACK_USER) { | ||
1718 | sz = sample->user_stack.size; | ||
1719 | *array++ = sz; | ||
1720 | if (sz) { | ||
1721 | memcpy(array, sample->user_stack.data, sz); | ||
1722 | array = (void *)array + sz; | ||
1723 | *array++ = sz; | ||
1724 | } | ||
1725 | } | ||
1726 | |||
1727 | if (type & PERF_SAMPLE_WEIGHT) { | ||
1728 | *array = sample->weight; | ||
1729 | array++; | ||
1730 | } | ||
1731 | |||
1732 | if (type & PERF_SAMPLE_DATA_SRC) { | ||
1733 | *array = sample->data_src; | ||
1734 | array++; | ||
1735 | } | ||
1736 | |||
1265 | return 0; | 1737 | return 0; |
1266 | } | 1738 | } |
1267 | 1739 | ||
@@ -1391,6 +1863,7 @@ static int sample_type__fprintf(FILE *fp, bool *first, u64 value) | |||
1391 | bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), | 1863 | bit_name(READ), bit_name(CALLCHAIN), bit_name(ID), bit_name(CPU), |
1392 | bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), | 1864 | bit_name(PERIOD), bit_name(STREAM_ID), bit_name(RAW), |
1393 | bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), | 1865 | bit_name(BRANCH_STACK), bit_name(REGS_USER), bit_name(STACK_USER), |
1866 | bit_name(IDENTIFIER), | ||
1394 | { .name = NULL, } | 1867 | { .name = NULL, } |
1395 | }; | 1868 | }; |
1396 | #undef bit_name | 1869 | #undef bit_name |
@@ -1458,6 +1931,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, | |||
1458 | if_print(exclude_hv); | 1931 | if_print(exclude_hv); |
1459 | if_print(exclude_idle); | 1932 | if_print(exclude_idle); |
1460 | if_print(mmap); | 1933 | if_print(mmap); |
1934 | if_print(mmap2); | ||
1461 | if_print(comm); | 1935 | if_print(comm); |
1462 | if_print(freq); | 1936 | if_print(freq); |
1463 | if_print(inherit_stat); | 1937 | if_print(inherit_stat); |
@@ -1482,7 +1956,7 @@ out: | |||
1482 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, | 1956 | bool perf_evsel__fallback(struct perf_evsel *evsel, int err, |
1483 | char *msg, size_t msgsize) | 1957 | char *msg, size_t msgsize) |
1484 | { | 1958 | { |
1485 | if ((err == ENOENT || err == ENXIO) && | 1959 | if ((err == ENOENT || err == ENXIO || err == ENODEV) && |
1486 | evsel->attr.type == PERF_TYPE_HARDWARE && | 1960 | evsel->attr.type == PERF_TYPE_HARDWARE && |
1487 | evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) { | 1961 | evsel->attr.config == PERF_COUNT_HW_CPU_CYCLES) { |
1488 | /* | 1962 | /* |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 3f156ccc1acb..4a7bdc713bab 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -38,6 +38,9 @@ struct perf_sample_id { | |||
38 | struct hlist_node node; | 38 | struct hlist_node node; |
39 | u64 id; | 39 | u64 id; |
40 | struct perf_evsel *evsel; | 40 | struct perf_evsel *evsel; |
41 | |||
42 | /* Holds total ID period value for PERF_SAMPLE_READ processing. */ | ||
43 | u64 period; | ||
41 | }; | 44 | }; |
42 | 45 | ||
43 | /** struct perf_evsel - event selector | 46 | /** struct perf_evsel - event selector |
@@ -45,6 +48,12 @@ struct perf_sample_id { | |||
45 | * @name - Can be set to retain the original event name passed by the user, | 48 | * @name - Can be set to retain the original event name passed by the user, |
46 | * so that when showing results in tools such as 'perf stat', we | 49 | * so that when showing results in tools such as 'perf stat', we |
47 | * show the name used, not some alias. | 50 | * show the name used, not some alias. |
51 | * @id_pos: the position of the event id (PERF_SAMPLE_ID or | ||
52 | * PERF_SAMPLE_IDENTIFIER) in a sample event i.e. in the array of | ||
53 | * struct sample_event | ||
54 | * @is_pos: the position (counting backwards) of the event id (PERF_SAMPLE_ID or | ||
55 | * PERF_SAMPLE_IDENTIFIER) in a non-sample event i.e. if sample_id_all | ||
56 | * is used there is an id sample appended to non-sample events | ||
48 | */ | 57 | */ |
49 | struct perf_evsel { | 58 | struct perf_evsel { |
50 | struct list_head node; | 59 | struct list_head node; |
@@ -71,11 +80,14 @@ struct perf_evsel { | |||
71 | } handler; | 80 | } handler; |
72 | struct cpu_map *cpus; | 81 | struct cpu_map *cpus; |
73 | unsigned int sample_size; | 82 | unsigned int sample_size; |
83 | int id_pos; | ||
84 | int is_pos; | ||
74 | bool supported; | 85 | bool supported; |
75 | bool needs_swap; | 86 | bool needs_swap; |
76 | /* parse modifier helper */ | 87 | /* parse modifier helper */ |
77 | int exclude_GH; | 88 | int exclude_GH; |
78 | int nr_members; | 89 | int nr_members; |
90 | int sample_read; | ||
79 | struct perf_evsel *leader; | 91 | struct perf_evsel *leader; |
80 | char *group_name; | 92 | char *group_name; |
81 | }; | 93 | }; |
@@ -100,6 +112,9 @@ void perf_evsel__delete(struct perf_evsel *evsel); | |||
100 | void perf_evsel__config(struct perf_evsel *evsel, | 112 | void perf_evsel__config(struct perf_evsel *evsel, |
101 | struct perf_record_opts *opts); | 113 | struct perf_record_opts *opts); |
102 | 114 | ||
115 | int __perf_evsel__sample_size(u64 sample_type); | ||
116 | void perf_evsel__calc_id_pos(struct perf_evsel *evsel); | ||
117 | |||
103 | bool perf_evsel__is_cache_op_valid(u8 type, u8 op); | 118 | bool perf_evsel__is_cache_op_valid(u8 type, u8 op); |
104 | 119 | ||
105 | #define PERF_EVSEL__MAX_ALIASES 8 | 120 | #define PERF_EVSEL__MAX_ALIASES 8 |
@@ -138,10 +153,12 @@ void __perf_evsel__reset_sample_bit(struct perf_evsel *evsel, | |||
138 | #define perf_evsel__reset_sample_bit(evsel, bit) \ | 153 | #define perf_evsel__reset_sample_bit(evsel, bit) \ |
139 | __perf_evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit) | 154 | __perf_evsel__reset_sample_bit(evsel, PERF_SAMPLE_##bit) |
140 | 155 | ||
141 | void perf_evsel__set_sample_id(struct perf_evsel *evsel); | 156 | void perf_evsel__set_sample_id(struct perf_evsel *evsel, |
157 | bool use_sample_identifier); | ||
142 | 158 | ||
143 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, | 159 | int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads, |
144 | const char *filter); | 160 | const char *filter); |
161 | int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads); | ||
145 | 162 | ||
146 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, | 163 | int perf_evsel__open_per_cpu(struct perf_evsel *evsel, |
147 | struct cpu_map *cpus); | 164 | struct cpu_map *cpus); |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index a4dafbee2511..c3e5a3b817ab 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -25,41 +25,9 @@ | |||
25 | 25 | ||
26 | static bool no_buildid_cache = false; | 26 | static bool no_buildid_cache = false; |
27 | 27 | ||
28 | static int trace_event_count; | ||
29 | static struct perf_trace_event_type *trace_events; | ||
30 | |||
31 | static u32 header_argc; | 28 | static u32 header_argc; |
32 | static const char **header_argv; | 29 | static const char **header_argv; |
33 | 30 | ||
34 | int perf_header__push_event(u64 id, const char *name) | ||
35 | { | ||
36 | struct perf_trace_event_type *nevents; | ||
37 | |||
38 | if (strlen(name) > MAX_EVENT_NAME) | ||
39 | pr_warning("Event %s will be truncated\n", name); | ||
40 | |||
41 | nevents = realloc(trace_events, (trace_event_count + 1) * sizeof(*trace_events)); | ||
42 | if (nevents == NULL) | ||
43 | return -ENOMEM; | ||
44 | trace_events = nevents; | ||
45 | |||
46 | memset(&trace_events[trace_event_count], 0, sizeof(struct perf_trace_event_type)); | ||
47 | trace_events[trace_event_count].event_id = id; | ||
48 | strncpy(trace_events[trace_event_count].name, name, MAX_EVENT_NAME - 1); | ||
49 | trace_event_count++; | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | char *perf_header__find_event(u64 id) | ||
54 | { | ||
55 | int i; | ||
56 | for (i = 0 ; i < trace_event_count; i++) { | ||
57 | if (trace_events[i].event_id == id) | ||
58 | return trace_events[i].name; | ||
59 | } | ||
60 | return NULL; | ||
61 | } | ||
62 | |||
63 | /* | 31 | /* |
64 | * magic2 = "PERFILE2" | 32 | * magic2 = "PERFILE2" |
65 | * must be a numerical value to let the endianness | 33 | * must be a numerical value to let the endianness |
@@ -231,9 +199,11 @@ static int write_buildid(char *name, size_t name_len, u8 *build_id, | |||
231 | return write_padded(fd, name, name_len + 1, len); | 199 | return write_padded(fd, name, name_len + 1, len); |
232 | } | 200 | } |
233 | 201 | ||
234 | static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, | 202 | static int __dsos__write_buildid_table(struct list_head *head, |
235 | u16 misc, int fd) | 203 | struct machine *machine, |
204 | pid_t pid, u16 misc, int fd) | ||
236 | { | 205 | { |
206 | char nm[PATH_MAX]; | ||
237 | struct dso *pos; | 207 | struct dso *pos; |
238 | 208 | ||
239 | dsos__for_each_with_build_id(pos, head) { | 209 | dsos__for_each_with_build_id(pos, head) { |
@@ -247,6 +217,10 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid, | |||
247 | if (is_vdso_map(pos->short_name)) { | 217 | if (is_vdso_map(pos->short_name)) { |
248 | name = (char *) VDSO__MAP_NAME; | 218 | name = (char *) VDSO__MAP_NAME; |
249 | name_len = sizeof(VDSO__MAP_NAME) + 1; | 219 | name_len = sizeof(VDSO__MAP_NAME) + 1; |
220 | } else if (dso__is_kcore(pos)) { | ||
221 | machine__mmap_name(machine, nm, sizeof(nm)); | ||
222 | name = nm; | ||
223 | name_len = strlen(nm) + 1; | ||
250 | } else { | 224 | } else { |
251 | name = pos->long_name; | 225 | name = pos->long_name; |
252 | name_len = pos->long_name_len + 1; | 226 | name_len = pos->long_name_len + 1; |
@@ -272,10 +246,10 @@ static int machine__write_buildid_table(struct machine *machine, int fd) | |||
272 | umisc = PERF_RECORD_MISC_GUEST_USER; | 246 | umisc = PERF_RECORD_MISC_GUEST_USER; |
273 | } | 247 | } |
274 | 248 | ||
275 | err = __dsos__write_buildid_table(&machine->kernel_dsos, machine->pid, | 249 | err = __dsos__write_buildid_table(&machine->kernel_dsos, machine, |
276 | kmisc, fd); | 250 | machine->pid, kmisc, fd); |
277 | if (err == 0) | 251 | if (err == 0) |
278 | err = __dsos__write_buildid_table(&machine->user_dsos, | 252 | err = __dsos__write_buildid_table(&machine->user_dsos, machine, |
279 | machine->pid, umisc, fd); | 253 | machine->pid, umisc, fd); |
280 | return err; | 254 | return err; |
281 | } | 255 | } |
@@ -407,23 +381,31 @@ out_free: | |||
407 | return err; | 381 | return err; |
408 | } | 382 | } |
409 | 383 | ||
410 | static int dso__cache_build_id(struct dso *dso, const char *debugdir) | 384 | static int dso__cache_build_id(struct dso *dso, struct machine *machine, |
385 | const char *debugdir) | ||
411 | { | 386 | { |
412 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; | 387 | bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; |
413 | bool is_vdso = is_vdso_map(dso->short_name); | 388 | bool is_vdso = is_vdso_map(dso->short_name); |
389 | char *name = dso->long_name; | ||
390 | char nm[PATH_MAX]; | ||
414 | 391 | ||
415 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), | 392 | if (dso__is_kcore(dso)) { |
416 | dso->long_name, debugdir, | 393 | is_kallsyms = true; |
417 | is_kallsyms, is_vdso); | 394 | machine__mmap_name(machine, nm, sizeof(nm)); |
395 | name = nm; | ||
396 | } | ||
397 | return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name, | ||
398 | debugdir, is_kallsyms, is_vdso); | ||
418 | } | 399 | } |
419 | 400 | ||
420 | static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | 401 | static int __dsos__cache_build_ids(struct list_head *head, |
402 | struct machine *machine, const char *debugdir) | ||
421 | { | 403 | { |
422 | struct dso *pos; | 404 | struct dso *pos; |
423 | int err = 0; | 405 | int err = 0; |
424 | 406 | ||
425 | dsos__for_each_with_build_id(pos, head) | 407 | dsos__for_each_with_build_id(pos, head) |
426 | if (dso__cache_build_id(pos, debugdir)) | 408 | if (dso__cache_build_id(pos, machine, debugdir)) |
427 | err = -1; | 409 | err = -1; |
428 | 410 | ||
429 | return err; | 411 | return err; |
@@ -431,8 +413,9 @@ static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir) | |||
431 | 413 | ||
432 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) | 414 | static int machine__cache_build_ids(struct machine *machine, const char *debugdir) |
433 | { | 415 | { |
434 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos, debugdir); | 416 | int ret = __dsos__cache_build_ids(&machine->kernel_dsos, machine, |
435 | ret |= __dsos__cache_build_ids(&machine->user_dsos, debugdir); | 417 | debugdir); |
418 | ret |= __dsos__cache_build_ids(&machine->user_dsos, machine, debugdir); | ||
436 | return ret; | 419 | return ret; |
437 | } | 420 | } |
438 | 421 | ||
@@ -748,18 +731,19 @@ static int build_cpu_topo(struct cpu_topo *tp, int cpu) | |||
748 | char filename[MAXPATHLEN]; | 731 | char filename[MAXPATHLEN]; |
749 | char *buf = NULL, *p; | 732 | char *buf = NULL, *p; |
750 | size_t len = 0; | 733 | size_t len = 0; |
734 | ssize_t sret; | ||
751 | u32 i = 0; | 735 | u32 i = 0; |
752 | int ret = -1; | 736 | int ret = -1; |
753 | 737 | ||
754 | sprintf(filename, CORE_SIB_FMT, cpu); | 738 | sprintf(filename, CORE_SIB_FMT, cpu); |
755 | fp = fopen(filename, "r"); | 739 | fp = fopen(filename, "r"); |
756 | if (!fp) | 740 | if (!fp) |
757 | return -1; | 741 | goto try_threads; |
758 | |||
759 | if (getline(&buf, &len, fp) <= 0) | ||
760 | goto done; | ||
761 | 742 | ||
743 | sret = getline(&buf, &len, fp); | ||
762 | fclose(fp); | 744 | fclose(fp); |
745 | if (sret <= 0) | ||
746 | goto try_threads; | ||
763 | 747 | ||
764 | p = strchr(buf, '\n'); | 748 | p = strchr(buf, '\n'); |
765 | if (p) | 749 | if (p) |
@@ -775,7 +759,9 @@ static int build_cpu_topo(struct cpu_topo *tp, int cpu) | |||
775 | buf = NULL; | 759 | buf = NULL; |
776 | len = 0; | 760 | len = 0; |
777 | } | 761 | } |
762 | ret = 0; | ||
778 | 763 | ||
764 | try_threads: | ||
779 | sprintf(filename, THRD_SIB_FMT, cpu); | 765 | sprintf(filename, THRD_SIB_FMT, cpu); |
780 | fp = fopen(filename, "r"); | 766 | fp = fopen(filename, "r"); |
781 | if (!fp) | 767 | if (!fp) |
@@ -1380,6 +1366,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1380 | 1366 | ||
1381 | fprintf(fp, ", precise_ip = %d", evsel->attr.precise_ip); | 1367 | fprintf(fp, ", precise_ip = %d", evsel->attr.precise_ip); |
1382 | 1368 | ||
1369 | fprintf(fp, ", attr_mmap2 = %d", evsel->attr.mmap2); | ||
1370 | fprintf(fp, ", attr_mmap = %d", evsel->attr.mmap); | ||
1371 | fprintf(fp, ", attr_mmap_data = %d", evsel->attr.mmap_data); | ||
1383 | if (evsel->ids) { | 1372 | if (evsel->ids) { |
1384 | fprintf(fp, ", id = {"); | 1373 | fprintf(fp, ", id = {"); |
1385 | for (j = 0, id = evsel->id; j < evsel->ids; j++, id++) { | 1374 | for (j = 0, id = evsel->id; j < evsel->ids; j++, id++) { |
@@ -2257,7 +2246,7 @@ static int perf_header__adds_write(struct perf_header *header, | |||
2257 | 2246 | ||
2258 | sec_size = sizeof(*feat_sec) * nr_sections; | 2247 | sec_size = sizeof(*feat_sec) * nr_sections; |
2259 | 2248 | ||
2260 | sec_start = header->data_offset + header->data_size; | 2249 | sec_start = header->feat_offset; |
2261 | lseek(fd, sec_start + sec_size, SEEK_SET); | 2250 | lseek(fd, sec_start + sec_size, SEEK_SET); |
2262 | 2251 | ||
2263 | for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) { | 2252 | for_each_set_bit(feat, header->adds_features, HEADER_FEAT_BITS) { |
@@ -2304,6 +2293,7 @@ int perf_session__write_header(struct perf_session *session, | |||
2304 | struct perf_file_attr f_attr; | 2293 | struct perf_file_attr f_attr; |
2305 | struct perf_header *header = &session->header; | 2294 | struct perf_header *header = &session->header; |
2306 | struct perf_evsel *evsel; | 2295 | struct perf_evsel *evsel; |
2296 | u64 attr_offset; | ||
2307 | int err; | 2297 | int err; |
2308 | 2298 | ||
2309 | lseek(fd, sizeof(f_header), SEEK_SET); | 2299 | lseek(fd, sizeof(f_header), SEEK_SET); |
@@ -2317,7 +2307,7 @@ int perf_session__write_header(struct perf_session *session, | |||
2317 | } | 2307 | } |
2318 | } | 2308 | } |
2319 | 2309 | ||
2320 | header->attr_offset = lseek(fd, 0, SEEK_CUR); | 2310 | attr_offset = lseek(fd, 0, SEEK_CUR); |
2321 | 2311 | ||
2322 | list_for_each_entry(evsel, &evlist->entries, node) { | 2312 | list_for_each_entry(evsel, &evlist->entries, node) { |
2323 | f_attr = (struct perf_file_attr){ | 2313 | f_attr = (struct perf_file_attr){ |
@@ -2334,17 +2324,8 @@ int perf_session__write_header(struct perf_session *session, | |||
2334 | } | 2324 | } |
2335 | } | 2325 | } |
2336 | 2326 | ||
2337 | header->event_offset = lseek(fd, 0, SEEK_CUR); | ||
2338 | header->event_size = trace_event_count * sizeof(struct perf_trace_event_type); | ||
2339 | if (trace_events) { | ||
2340 | err = do_write(fd, trace_events, header->event_size); | ||
2341 | if (err < 0) { | ||
2342 | pr_debug("failed to write perf header events\n"); | ||
2343 | return err; | ||
2344 | } | ||
2345 | } | ||
2346 | |||
2347 | header->data_offset = lseek(fd, 0, SEEK_CUR); | 2327 | header->data_offset = lseek(fd, 0, SEEK_CUR); |
2328 | header->feat_offset = header->data_offset + header->data_size; | ||
2348 | 2329 | ||
2349 | if (at_exit) { | 2330 | if (at_exit) { |
2350 | err = perf_header__adds_write(header, evlist, fd); | 2331 | err = perf_header__adds_write(header, evlist, fd); |
@@ -2357,17 +2338,14 @@ int perf_session__write_header(struct perf_session *session, | |||
2357 | .size = sizeof(f_header), | 2338 | .size = sizeof(f_header), |
2358 | .attr_size = sizeof(f_attr), | 2339 | .attr_size = sizeof(f_attr), |
2359 | .attrs = { | 2340 | .attrs = { |
2360 | .offset = header->attr_offset, | 2341 | .offset = attr_offset, |
2361 | .size = evlist->nr_entries * sizeof(f_attr), | 2342 | .size = evlist->nr_entries * sizeof(f_attr), |
2362 | }, | 2343 | }, |
2363 | .data = { | 2344 | .data = { |
2364 | .offset = header->data_offset, | 2345 | .offset = header->data_offset, |
2365 | .size = header->data_size, | 2346 | .size = header->data_size, |
2366 | }, | 2347 | }, |
2367 | .event_types = { | 2348 | /* event_types is ignored, store zeros */ |
2368 | .offset = header->event_offset, | ||
2369 | .size = header->event_size, | ||
2370 | }, | ||
2371 | }; | 2349 | }; |
2372 | 2350 | ||
2373 | memcpy(&f_header.adds_features, &header->adds_features, sizeof(header->adds_features)); | 2351 | memcpy(&f_header.adds_features, &header->adds_features, sizeof(header->adds_features)); |
@@ -2417,7 +2395,7 @@ int perf_header__process_sections(struct perf_header *header, int fd, | |||
2417 | 2395 | ||
2418 | sec_size = sizeof(*feat_sec) * nr_sections; | 2396 | sec_size = sizeof(*feat_sec) * nr_sections; |
2419 | 2397 | ||
2420 | lseek(fd, header->data_offset + header->data_size, SEEK_SET); | 2398 | lseek(fd, header->feat_offset, SEEK_SET); |
2421 | 2399 | ||
2422 | err = perf_header__getbuffer64(header, fd, feat_sec, sec_size); | 2400 | err = perf_header__getbuffer64(header, fd, feat_sec, sec_size); |
2423 | if (err < 0) | 2401 | if (err < 0) |
@@ -2523,6 +2501,7 @@ static int check_magic_endian(u64 magic, uint64_t hdr_sz, | |||
2523 | /* check for legacy format */ | 2501 | /* check for legacy format */ |
2524 | ret = memcmp(&magic, __perf_magic1, sizeof(magic)); | 2502 | ret = memcmp(&magic, __perf_magic1, sizeof(magic)); |
2525 | if (ret == 0) { | 2503 | if (ret == 0) { |
2504 | ph->version = PERF_HEADER_VERSION_1; | ||
2526 | pr_debug("legacy perf.data format\n"); | 2505 | pr_debug("legacy perf.data format\n"); |
2527 | if (is_pipe) | 2506 | if (is_pipe) |
2528 | return try_all_pipe_abis(hdr_sz, ph); | 2507 | return try_all_pipe_abis(hdr_sz, ph); |
@@ -2544,6 +2523,7 @@ static int check_magic_endian(u64 magic, uint64_t hdr_sz, | |||
2544 | return -1; | 2523 | return -1; |
2545 | 2524 | ||
2546 | ph->needs_swap = true; | 2525 | ph->needs_swap = true; |
2526 | ph->version = PERF_HEADER_VERSION_2; | ||
2547 | 2527 | ||
2548 | return 0; | 2528 | return 0; |
2549 | } | 2529 | } |
@@ -2614,10 +2594,9 @@ int perf_file_header__read(struct perf_file_header *header, | |||
2614 | memcpy(&ph->adds_features, &header->adds_features, | 2594 | memcpy(&ph->adds_features, &header->adds_features, |
2615 | sizeof(ph->adds_features)); | 2595 | sizeof(ph->adds_features)); |
2616 | 2596 | ||
2617 | ph->event_offset = header->event_types.offset; | ||
2618 | ph->event_size = header->event_types.size; | ||
2619 | ph->data_offset = header->data.offset; | 2597 | ph->data_offset = header->data.offset; |
2620 | ph->data_size = header->data.size; | 2598 | ph->data_size = header->data.size; |
2599 | ph->feat_offset = header->data.offset + header->data.size; | ||
2621 | return 0; | 2600 | return 0; |
2622 | } | 2601 | } |
2623 | 2602 | ||
@@ -2666,19 +2645,17 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, | |||
2666 | return 0; | 2645 | return 0; |
2667 | } | 2646 | } |
2668 | 2647 | ||
2669 | static int perf_header__read_pipe(struct perf_session *session, int fd) | 2648 | static int perf_header__read_pipe(struct perf_session *session) |
2670 | { | 2649 | { |
2671 | struct perf_header *header = &session->header; | 2650 | struct perf_header *header = &session->header; |
2672 | struct perf_pipe_file_header f_header; | 2651 | struct perf_pipe_file_header f_header; |
2673 | 2652 | ||
2674 | if (perf_file_header__read_pipe(&f_header, header, fd, | 2653 | if (perf_file_header__read_pipe(&f_header, header, session->fd, |
2675 | session->repipe) < 0) { | 2654 | session->repipe) < 0) { |
2676 | pr_debug("incompatible file format\n"); | 2655 | pr_debug("incompatible file format\n"); |
2677 | return -EINVAL; | 2656 | return -EINVAL; |
2678 | } | 2657 | } |
2679 | 2658 | ||
2680 | session->fd = fd; | ||
2681 | |||
2682 | return 0; | 2659 | return 0; |
2683 | } | 2660 | } |
2684 | 2661 | ||
@@ -2772,24 +2749,37 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist, | |||
2772 | return 0; | 2749 | return 0; |
2773 | } | 2750 | } |
2774 | 2751 | ||
2775 | int perf_session__read_header(struct perf_session *session, int fd) | 2752 | int perf_session__read_header(struct perf_session *session) |
2776 | { | 2753 | { |
2777 | struct perf_header *header = &session->header; | 2754 | struct perf_header *header = &session->header; |
2778 | struct perf_file_header f_header; | 2755 | struct perf_file_header f_header; |
2779 | struct perf_file_attr f_attr; | 2756 | struct perf_file_attr f_attr; |
2780 | u64 f_id; | 2757 | u64 f_id; |
2781 | int nr_attrs, nr_ids, i, j; | 2758 | int nr_attrs, nr_ids, i, j; |
2759 | int fd = session->fd; | ||
2782 | 2760 | ||
2783 | session->evlist = perf_evlist__new(); | 2761 | session->evlist = perf_evlist__new(); |
2784 | if (session->evlist == NULL) | 2762 | if (session->evlist == NULL) |
2785 | return -ENOMEM; | 2763 | return -ENOMEM; |
2786 | 2764 | ||
2787 | if (session->fd_pipe) | 2765 | if (session->fd_pipe) |
2788 | return perf_header__read_pipe(session, fd); | 2766 | return perf_header__read_pipe(session); |
2789 | 2767 | ||
2790 | if (perf_file_header__read(&f_header, header, fd) < 0) | 2768 | if (perf_file_header__read(&f_header, header, fd) < 0) |
2791 | return -EINVAL; | 2769 | return -EINVAL; |
2792 | 2770 | ||
2771 | /* | ||
2772 | * Sanity check that perf.data was written cleanly; data size is | ||
2773 | * initialized to 0 and updated only if the on_exit function is run. | ||
2774 | * If data size is still 0 then the file contains only partial | ||
2775 | * information. Just warn user and process it as much as it can. | ||
2776 | */ | ||
2777 | if (f_header.data.size == 0) { | ||
2778 | pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" | ||
2779 | "Was the 'perf record' command properly terminated?\n", | ||
2780 | session->filename); | ||
2781 | } | ||
2782 | |||
2793 | nr_attrs = f_header.attrs.size / f_header.attr_size; | 2783 | nr_attrs = f_header.attrs.size / f_header.attr_size; |
2794 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 2784 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
2795 | 2785 | ||
@@ -2839,22 +2829,9 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
2839 | 2829 | ||
2840 | symbol_conf.nr_events = nr_attrs; | 2830 | symbol_conf.nr_events = nr_attrs; |
2841 | 2831 | ||
2842 | if (f_header.event_types.size) { | ||
2843 | lseek(fd, f_header.event_types.offset, SEEK_SET); | ||
2844 | trace_events = malloc(f_header.event_types.size); | ||
2845 | if (trace_events == NULL) | ||
2846 | return -ENOMEM; | ||
2847 | if (perf_header__getbuffer64(header, fd, trace_events, | ||
2848 | f_header.event_types.size)) | ||
2849 | goto out_errno; | ||
2850 | trace_event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); | ||
2851 | } | ||
2852 | |||
2853 | perf_header__process_sections(header, fd, &session->pevent, | 2832 | perf_header__process_sections(header, fd, &session->pevent, |
2854 | perf_file_section__process); | 2833 | perf_file_section__process); |
2855 | 2834 | ||
2856 | lseek(fd, header->data_offset, SEEK_SET); | ||
2857 | |||
2858 | if (perf_evlist__prepare_tracepoint_events(session->evlist, | 2835 | if (perf_evlist__prepare_tracepoint_events(session->evlist, |
2859 | session->pevent)) | 2836 | session->pevent)) |
2860 | goto out_delete_evlist; | 2837 | goto out_delete_evlist; |
@@ -2922,7 +2899,8 @@ int perf_event__synthesize_attrs(struct perf_tool *tool, | |||
2922 | return err; | 2899 | return err; |
2923 | } | 2900 | } |
2924 | 2901 | ||
2925 | int perf_event__process_attr(union perf_event *event, | 2902 | int perf_event__process_attr(struct perf_tool *tool __maybe_unused, |
2903 | union perf_event *event, | ||
2926 | struct perf_evlist **pevlist) | 2904 | struct perf_evlist **pevlist) |
2927 | { | 2905 | { |
2928 | u32 i, ids, n_ids; | 2906 | u32 i, ids, n_ids; |
@@ -2961,64 +2939,6 @@ int perf_event__process_attr(union perf_event *event, | |||
2961 | return 0; | 2939 | return 0; |
2962 | } | 2940 | } |
2963 | 2941 | ||
2964 | int perf_event__synthesize_event_type(struct perf_tool *tool, | ||
2965 | u64 event_id, char *name, | ||
2966 | perf_event__handler_t process, | ||
2967 | struct machine *machine) | ||
2968 | { | ||
2969 | union perf_event ev; | ||
2970 | size_t size = 0; | ||
2971 | int err = 0; | ||
2972 | |||
2973 | memset(&ev, 0, sizeof(ev)); | ||
2974 | |||
2975 | ev.event_type.event_type.event_id = event_id; | ||
2976 | memset(ev.event_type.event_type.name, 0, MAX_EVENT_NAME); | ||
2977 | strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1); | ||
2978 | |||
2979 | ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; | ||
2980 | size = strlen(ev.event_type.event_type.name); | ||
2981 | size = PERF_ALIGN(size, sizeof(u64)); | ||
2982 | ev.event_type.header.size = sizeof(ev.event_type) - | ||
2983 | (sizeof(ev.event_type.event_type.name) - size); | ||
2984 | |||
2985 | err = process(tool, &ev, NULL, machine); | ||
2986 | |||
2987 | return err; | ||
2988 | } | ||
2989 | |||
2990 | int perf_event__synthesize_event_types(struct perf_tool *tool, | ||
2991 | perf_event__handler_t process, | ||
2992 | struct machine *machine) | ||
2993 | { | ||
2994 | struct perf_trace_event_type *type; | ||
2995 | int i, err = 0; | ||
2996 | |||
2997 | for (i = 0; i < trace_event_count; i++) { | ||
2998 | type = &trace_events[i]; | ||
2999 | |||
3000 | err = perf_event__synthesize_event_type(tool, type->event_id, | ||
3001 | type->name, process, | ||
3002 | machine); | ||
3003 | if (err) { | ||
3004 | pr_debug("failed to create perf header event type\n"); | ||
3005 | return err; | ||
3006 | } | ||
3007 | } | ||
3008 | |||
3009 | return err; | ||
3010 | } | ||
3011 | |||
3012 | int perf_event__process_event_type(struct perf_tool *tool __maybe_unused, | ||
3013 | union perf_event *event) | ||
3014 | { | ||
3015 | if (perf_header__push_event(event->event_type.event_type.event_id, | ||
3016 | event->event_type.event_type.name) < 0) | ||
3017 | return -ENOMEM; | ||
3018 | |||
3019 | return 0; | ||
3020 | } | ||
3021 | |||
3022 | int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, | 2942 | int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, |
3023 | struct perf_evlist *evlist, | 2943 | struct perf_evlist *evlist, |
3024 | perf_event__handler_t process) | 2944 | perf_event__handler_t process) |
@@ -3065,7 +2985,8 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, | |||
3065 | return aligned_size; | 2985 | return aligned_size; |
3066 | } | 2986 | } |
3067 | 2987 | ||
3068 | int perf_event__process_tracing_data(union perf_event *event, | 2988 | int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, |
2989 | union perf_event *event, | ||
3069 | struct perf_session *session) | 2990 | struct perf_session *session) |
3070 | { | 2991 | { |
3071 | ssize_t size_read, padding, size = event->tracing_data.size; | 2992 | ssize_t size_read, padding, size = event->tracing_data.size; |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 16a3e83c584e..307c9aed972e 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -34,6 +34,11 @@ enum { | |||
34 | HEADER_FEAT_BITS = 256, | 34 | HEADER_FEAT_BITS = 256, |
35 | }; | 35 | }; |
36 | 36 | ||
37 | enum perf_header_version { | ||
38 | PERF_HEADER_VERSION_1, | ||
39 | PERF_HEADER_VERSION_2, | ||
40 | }; | ||
41 | |||
37 | struct perf_file_section { | 42 | struct perf_file_section { |
38 | u64 offset; | 43 | u64 offset; |
39 | u64 size; | 44 | u64 size; |
@@ -45,6 +50,7 @@ struct perf_file_header { | |||
45 | u64 attr_size; | 50 | u64 attr_size; |
46 | struct perf_file_section attrs; | 51 | struct perf_file_section attrs; |
47 | struct perf_file_section data; | 52 | struct perf_file_section data; |
53 | /* event_types is ignored */ | ||
48 | struct perf_file_section event_types; | 54 | struct perf_file_section event_types; |
49 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | 55 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); |
50 | }; | 56 | }; |
@@ -84,28 +90,24 @@ struct perf_session_env { | |||
84 | }; | 90 | }; |
85 | 91 | ||
86 | struct perf_header { | 92 | struct perf_header { |
87 | bool needs_swap; | 93 | enum perf_header_version version; |
88 | s64 attr_offset; | 94 | bool needs_swap; |
89 | u64 data_offset; | 95 | u64 data_offset; |
90 | u64 data_size; | 96 | u64 data_size; |
91 | u64 event_offset; | 97 | u64 feat_offset; |
92 | u64 event_size; | ||
93 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); | 98 | DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); |
94 | struct perf_session_env env; | 99 | struct perf_session_env env; |
95 | }; | 100 | }; |
96 | 101 | ||
97 | struct perf_evlist; | 102 | struct perf_evlist; |
98 | struct perf_session; | 103 | struct perf_session; |
99 | 104 | ||
100 | int perf_session__read_header(struct perf_session *session, int fd); | 105 | int perf_session__read_header(struct perf_session *session); |
101 | int perf_session__write_header(struct perf_session *session, | 106 | int perf_session__write_header(struct perf_session *session, |
102 | struct perf_evlist *evlist, | 107 | struct perf_evlist *evlist, |
103 | int fd, bool at_exit); | 108 | int fd, bool at_exit); |
104 | int perf_header__write_pipe(int fd); | 109 | int perf_header__write_pipe(int fd); |
105 | 110 | ||
106 | int perf_header__push_event(u64 id, const char *name); | ||
107 | char *perf_header__find_event(u64 id); | ||
108 | |||
109 | void perf_header__set_feat(struct perf_header *header, int feat); | 111 | void perf_header__set_feat(struct perf_header *header, int feat); |
110 | void perf_header__clear_feat(struct perf_header *header, int feat); | 112 | void perf_header__clear_feat(struct perf_header *header, int feat); |
111 | bool perf_header__has_feat(const struct perf_header *header, int feat); | 113 | bool perf_header__has_feat(const struct perf_header *header, int feat); |
@@ -130,22 +132,14 @@ int perf_event__synthesize_attr(struct perf_tool *tool, | |||
130 | int perf_event__synthesize_attrs(struct perf_tool *tool, | 132 | int perf_event__synthesize_attrs(struct perf_tool *tool, |
131 | struct perf_session *session, | 133 | struct perf_session *session, |
132 | perf_event__handler_t process); | 134 | perf_event__handler_t process); |
133 | int perf_event__process_attr(union perf_event *event, struct perf_evlist **pevlist); | 135 | int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, |
134 | 136 | struct perf_evlist **pevlist); | |
135 | int perf_event__synthesize_event_type(struct perf_tool *tool, | ||
136 | u64 event_id, char *name, | ||
137 | perf_event__handler_t process, | ||
138 | struct machine *machine); | ||
139 | int perf_event__synthesize_event_types(struct perf_tool *tool, | ||
140 | perf_event__handler_t process, | ||
141 | struct machine *machine); | ||
142 | int perf_event__process_event_type(struct perf_tool *tool, | ||
143 | union perf_event *event); | ||
144 | 137 | ||
145 | int perf_event__synthesize_tracing_data(struct perf_tool *tool, | 138 | int perf_event__synthesize_tracing_data(struct perf_tool *tool, |
146 | int fd, struct perf_evlist *evlist, | 139 | int fd, struct perf_evlist *evlist, |
147 | perf_event__handler_t process); | 140 | perf_event__handler_t process); |
148 | int perf_event__process_tracing_data(union perf_event *event, | 141 | int perf_event__process_tracing_data(struct perf_tool *tool, |
142 | union perf_event *event, | ||
149 | struct perf_session *session); | 143 | struct perf_session *session); |
150 | 144 | ||
151 | int perf_event__synthesize_build_id(struct perf_tool *tool, | 145 | int perf_event__synthesize_build_id(struct perf_tool *tool, |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index b11a6cfdb414..9ff6cf3e9a99 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -24,7 +24,8 @@ enum hist_filter { | |||
24 | struct callchain_param callchain_param = { | 24 | struct callchain_param callchain_param = { |
25 | .mode = CHAIN_GRAPH_REL, | 25 | .mode = CHAIN_GRAPH_REL, |
26 | .min_percent = 0.5, | 26 | .min_percent = 0.5, |
27 | .order = ORDER_CALLEE | 27 | .order = ORDER_CALLEE, |
28 | .key = CCKEY_FUNCTION | ||
28 | }; | 29 | }; |
29 | 30 | ||
30 | u16 hists__col_len(struct hists *hists, enum hist_column col) | 31 | u16 hists__col_len(struct hists *hists, enum hist_column col) |
@@ -610,6 +611,8 @@ void hists__collapse_resort(struct hists *hists) | |||
610 | next = rb_first(root); | 611 | next = rb_first(root); |
611 | 612 | ||
612 | while (next) { | 613 | while (next) { |
614 | if (session_done()) | ||
615 | break; | ||
613 | n = rb_entry(next, struct hist_entry, rb_node_in); | 616 | n = rb_entry(next, struct hist_entry, rb_node_in); |
614 | next = rb_next(&n->rb_node_in); | 617 | next = rb_next(&n->rb_node_in); |
615 | 618 | ||
@@ -912,6 +915,7 @@ static struct hist_entry *hists__add_dummy_entry(struct hists *hists, | |||
912 | rb_link_node(&he->rb_node_in, parent, p); | 915 | rb_link_node(&he->rb_node_in, parent, p); |
913 | rb_insert_color(&he->rb_node_in, root); | 916 | rb_insert_color(&he->rb_node_in, root); |
914 | hists__inc_nr_entries(hists, he); | 917 | hists__inc_nr_entries(hists, he); |
918 | he->dummy = true; | ||
915 | } | 919 | } |
916 | out: | 920 | out: |
917 | return he; | 921 | return he; |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 2d3790fd99bb..ce8dc61ce2c3 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <pthread.h> | 5 | #include <pthread.h> |
6 | #include "callchain.h" | 6 | #include "callchain.h" |
7 | #include "header.h" | 7 | #include "header.h" |
8 | #include "color.h" | ||
8 | 9 | ||
9 | extern struct callchain_param callchain_param; | 10 | extern struct callchain_param callchain_param; |
10 | 11 | ||
@@ -141,10 +142,12 @@ struct perf_hpp { | |||
141 | }; | 142 | }; |
142 | 143 | ||
143 | struct perf_hpp_fmt { | 144 | struct perf_hpp_fmt { |
144 | int (*header)(struct perf_hpp *hpp); | 145 | int (*header)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp); |
145 | int (*width)(struct perf_hpp *hpp); | 146 | int (*width)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp); |
146 | int (*color)(struct perf_hpp *hpp, struct hist_entry *he); | 147 | int (*color)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, |
147 | int (*entry)(struct perf_hpp *hpp, struct hist_entry *he); | 148 | struct hist_entry *he); |
149 | int (*entry)(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, | ||
150 | struct hist_entry *he); | ||
148 | 151 | ||
149 | struct list_head list; | 152 | struct list_head list; |
150 | }; | 153 | }; |
@@ -157,7 +160,7 @@ extern struct list_head perf_hpp__list; | |||
157 | extern struct perf_hpp_fmt perf_hpp__format[]; | 160 | extern struct perf_hpp_fmt perf_hpp__format[]; |
158 | 161 | ||
159 | enum { | 162 | enum { |
160 | PERF_HPP__BASELINE, | 163 | /* Matches perf_hpp__format array. */ |
161 | PERF_HPP__OVERHEAD, | 164 | PERF_HPP__OVERHEAD, |
162 | PERF_HPP__OVERHEAD_SYS, | 165 | PERF_HPP__OVERHEAD_SYS, |
163 | PERF_HPP__OVERHEAD_US, | 166 | PERF_HPP__OVERHEAD_US, |
@@ -165,11 +168,6 @@ enum { | |||
165 | PERF_HPP__OVERHEAD_GUEST_US, | 168 | PERF_HPP__OVERHEAD_GUEST_US, |
166 | PERF_HPP__SAMPLES, | 169 | PERF_HPP__SAMPLES, |
167 | PERF_HPP__PERIOD, | 170 | PERF_HPP__PERIOD, |
168 | PERF_HPP__PERIOD_BASELINE, | ||
169 | PERF_HPP__DELTA, | ||
170 | PERF_HPP__RATIO, | ||
171 | PERF_HPP__WEIGHTED_DIFF, | ||
172 | PERF_HPP__FORMULA, | ||
173 | 171 | ||
174 | PERF_HPP__MAX_INDEX | 172 | PERF_HPP__MAX_INDEX |
175 | }; | 173 | }; |
@@ -177,8 +175,18 @@ enum { | |||
177 | void perf_hpp__init(void); | 175 | void perf_hpp__init(void); |
178 | void perf_hpp__column_register(struct perf_hpp_fmt *format); | 176 | void perf_hpp__column_register(struct perf_hpp_fmt *format); |
179 | void perf_hpp__column_enable(unsigned col); | 177 | void perf_hpp__column_enable(unsigned col); |
180 | int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, | 178 | |
181 | bool color); | 179 | static inline size_t perf_hpp__use_color(void) |
180 | { | ||
181 | return !symbol_conf.field_sep; | ||
182 | } | ||
183 | |||
184 | static inline size_t perf_hpp__color_overhead(void) | ||
185 | { | ||
186 | return perf_hpp__use_color() ? | ||
187 | (COLOR_MAXLEN + sizeof(PERF_COLOR_RESET)) * PERF_HPP__MAX_INDEX | ||
188 | : 0; | ||
189 | } | ||
182 | 190 | ||
183 | struct perf_evlist; | 191 | struct perf_evlist; |
184 | 192 | ||
@@ -245,11 +253,4 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused, | |||
245 | #endif | 253 | #endif |
246 | 254 | ||
247 | unsigned int hists__sort_list_width(struct hists *self); | 255 | unsigned int hists__sort_list_width(struct hists *self); |
248 | |||
249 | double perf_diff__compute_delta(struct hist_entry *he, struct hist_entry *pair); | ||
250 | double perf_diff__compute_ratio(struct hist_entry *he, struct hist_entry *pair); | ||
251 | s64 perf_diff__compute_wdiff(struct hist_entry *he, struct hist_entry *pair); | ||
252 | int perf_diff__formula(struct hist_entry *he, struct hist_entry *pair, | ||
253 | char *buf, size_t size); | ||
254 | double perf_diff__period_percent(struct hist_entry *he, u64 period); | ||
255 | #endif /* __PERF_HIST_H */ | 256 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h index 6f19c548ecc0..97a800738226 100644 --- a/tools/perf/util/include/linux/string.h +++ b/tools/perf/util/include/linux/string.h | |||
@@ -1,3 +1,4 @@ | |||
1 | #include <string.h> | 1 | #include <string.h> |
2 | 2 | ||
3 | void *memdup(const void *src, size_t len); | 3 | void *memdup(const void *src, size_t len); |
4 | int str_append(char **s, int *len, const char *a); | ||
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index b2ecad6ec46b..6188d2876a71 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -25,12 +25,15 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) | |||
25 | machine->kmaps.machine = machine; | 25 | machine->kmaps.machine = machine; |
26 | machine->pid = pid; | 26 | machine->pid = pid; |
27 | 27 | ||
28 | machine->symbol_filter = NULL; | ||
29 | |||
28 | machine->root_dir = strdup(root_dir); | 30 | machine->root_dir = strdup(root_dir); |
29 | if (machine->root_dir == NULL) | 31 | if (machine->root_dir == NULL) |
30 | return -ENOMEM; | 32 | return -ENOMEM; |
31 | 33 | ||
32 | if (pid != HOST_KERNEL_ID) { | 34 | if (pid != HOST_KERNEL_ID) { |
33 | struct thread *thread = machine__findnew_thread(machine, pid); | 35 | struct thread *thread = machine__findnew_thread(machine, 0, |
36 | pid); | ||
34 | char comm[64]; | 37 | char comm[64]; |
35 | 38 | ||
36 | if (thread == NULL) | 39 | if (thread == NULL) |
@@ -95,6 +98,7 @@ void machines__init(struct machines *machines) | |||
95 | { | 98 | { |
96 | machine__init(&machines->host, "", HOST_KERNEL_ID); | 99 | machine__init(&machines->host, "", HOST_KERNEL_ID); |
97 | machines->guests = RB_ROOT; | 100 | machines->guests = RB_ROOT; |
101 | machines->symbol_filter = NULL; | ||
98 | } | 102 | } |
99 | 103 | ||
100 | void machines__exit(struct machines *machines) | 104 | void machines__exit(struct machines *machines) |
@@ -118,6 +122,8 @@ struct machine *machines__add(struct machines *machines, pid_t pid, | |||
118 | return NULL; | 122 | return NULL; |
119 | } | 123 | } |
120 | 124 | ||
125 | machine->symbol_filter = machines->symbol_filter; | ||
126 | |||
121 | while (*p != NULL) { | 127 | while (*p != NULL) { |
122 | parent = *p; | 128 | parent = *p; |
123 | pos = rb_entry(parent, struct machine, rb_node); | 129 | pos = rb_entry(parent, struct machine, rb_node); |
@@ -133,6 +139,21 @@ struct machine *machines__add(struct machines *machines, pid_t pid, | |||
133 | return machine; | 139 | return machine; |
134 | } | 140 | } |
135 | 141 | ||
142 | void machines__set_symbol_filter(struct machines *machines, | ||
143 | symbol_filter_t symbol_filter) | ||
144 | { | ||
145 | struct rb_node *nd; | ||
146 | |||
147 | machines->symbol_filter = symbol_filter; | ||
148 | machines->host.symbol_filter = symbol_filter; | ||
149 | |||
150 | for (nd = rb_first(&machines->guests); nd; nd = rb_next(nd)) { | ||
151 | struct machine *machine = rb_entry(nd, struct machine, rb_node); | ||
152 | |||
153 | machine->symbol_filter = symbol_filter; | ||
154 | } | ||
155 | } | ||
156 | |||
136 | struct machine *machines__find(struct machines *machines, pid_t pid) | 157 | struct machine *machines__find(struct machines *machines, pid_t pid) |
137 | { | 158 | { |
138 | struct rb_node **p = &machines->guests.rb_node; | 159 | struct rb_node **p = &machines->guests.rb_node; |
@@ -233,7 +254,8 @@ void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size) | |||
233 | return; | 254 | return; |
234 | } | 255 | } |
235 | 256 | ||
236 | static struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, | 257 | static struct thread *__machine__findnew_thread(struct machine *machine, |
258 | pid_t pid, pid_t tid, | ||
237 | bool create) | 259 | bool create) |
238 | { | 260 | { |
239 | struct rb_node **p = &machine->threads.rb_node; | 261 | struct rb_node **p = &machine->threads.rb_node; |
@@ -241,23 +263,28 @@ static struct thread *__machine__findnew_thread(struct machine *machine, pid_t p | |||
241 | struct thread *th; | 263 | struct thread *th; |
242 | 264 | ||
243 | /* | 265 | /* |
244 | * Font-end cache - PID lookups come in blocks, | 266 | * Front-end cache - TID lookups come in blocks, |
245 | * so most of the time we dont have to look up | 267 | * so most of the time we dont have to look up |
246 | * the full rbtree: | 268 | * the full rbtree: |
247 | */ | 269 | */ |
248 | if (machine->last_match && machine->last_match->pid == pid) | 270 | if (machine->last_match && machine->last_match->tid == tid) { |
271 | if (pid && pid != machine->last_match->pid_) | ||
272 | machine->last_match->pid_ = pid; | ||
249 | return machine->last_match; | 273 | return machine->last_match; |
274 | } | ||
250 | 275 | ||
251 | while (*p != NULL) { | 276 | while (*p != NULL) { |
252 | parent = *p; | 277 | parent = *p; |
253 | th = rb_entry(parent, struct thread, rb_node); | 278 | th = rb_entry(parent, struct thread, rb_node); |
254 | 279 | ||
255 | if (th->pid == pid) { | 280 | if (th->tid == tid) { |
256 | machine->last_match = th; | 281 | machine->last_match = th; |
282 | if (pid && pid != th->pid_) | ||
283 | th->pid_ = pid; | ||
257 | return th; | 284 | return th; |
258 | } | 285 | } |
259 | 286 | ||
260 | if (pid < th->pid) | 287 | if (tid < th->tid) |
261 | p = &(*p)->rb_left; | 288 | p = &(*p)->rb_left; |
262 | else | 289 | else |
263 | p = &(*p)->rb_right; | 290 | p = &(*p)->rb_right; |
@@ -266,7 +293,7 @@ static struct thread *__machine__findnew_thread(struct machine *machine, pid_t p | |||
266 | if (!create) | 293 | if (!create) |
267 | return NULL; | 294 | return NULL; |
268 | 295 | ||
269 | th = thread__new(pid); | 296 | th = thread__new(pid, tid); |
270 | if (th != NULL) { | 297 | if (th != NULL) { |
271 | rb_link_node(&th->rb_node, parent, p); | 298 | rb_link_node(&th->rb_node, parent, p); |
272 | rb_insert_color(&th->rb_node, &machine->threads); | 299 | rb_insert_color(&th->rb_node, &machine->threads); |
@@ -276,19 +303,22 @@ static struct thread *__machine__findnew_thread(struct machine *machine, pid_t p | |||
276 | return th; | 303 | return th; |
277 | } | 304 | } |
278 | 305 | ||
279 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid) | 306 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, |
307 | pid_t tid) | ||
280 | { | 308 | { |
281 | return __machine__findnew_thread(machine, pid, true); | 309 | return __machine__findnew_thread(machine, pid, tid, true); |
282 | } | 310 | } |
283 | 311 | ||
284 | struct thread *machine__find_thread(struct machine *machine, pid_t pid) | 312 | struct thread *machine__find_thread(struct machine *machine, pid_t tid) |
285 | { | 313 | { |
286 | return __machine__findnew_thread(machine, pid, false); | 314 | return __machine__findnew_thread(machine, 0, tid, false); |
287 | } | 315 | } |
288 | 316 | ||
289 | int machine__process_comm_event(struct machine *machine, union perf_event *event) | 317 | int machine__process_comm_event(struct machine *machine, union perf_event *event) |
290 | { | 318 | { |
291 | struct thread *thread = machine__findnew_thread(machine, event->comm.tid); | 319 | struct thread *thread = machine__findnew_thread(machine, |
320 | event->comm.pid, | ||
321 | event->comm.tid); | ||
292 | 322 | ||
293 | if (dump_trace) | 323 | if (dump_trace) |
294 | perf_event__fprintf_comm(event, stdout); | 324 | perf_event__fprintf_comm(event, stdout); |
@@ -628,10 +658,8 @@ int machine__load_vmlinux_path(struct machine *machine, enum map_type type, | |||
628 | struct map *map = machine->vmlinux_maps[type]; | 658 | struct map *map = machine->vmlinux_maps[type]; |
629 | int ret = dso__load_vmlinux_path(map->dso, map, filter); | 659 | int ret = dso__load_vmlinux_path(map->dso, map, filter); |
630 | 660 | ||
631 | if (ret > 0) { | 661 | if (ret > 0) |
632 | dso__set_loaded(map->dso, type); | 662 | dso__set_loaded(map->dso, type); |
633 | map__reloc_vmlinux(map); | ||
634 | } | ||
635 | 663 | ||
636 | return ret; | 664 | return ret; |
637 | } | 665 | } |
@@ -764,7 +792,7 @@ static int machine__create_modules(struct machine *machine) | |||
764 | modules = path; | 792 | modules = path; |
765 | } | 793 | } |
766 | 794 | ||
767 | if (symbol__restricted_filename(path, "/proc/modules")) | 795 | if (symbol__restricted_filename(modules, "/proc/modules")) |
768 | return -1; | 796 | return -1; |
769 | 797 | ||
770 | file = fopen(modules, "r"); | 798 | file = fopen(modules, "r"); |
@@ -808,7 +836,10 @@ static int machine__create_modules(struct machine *machine) | |||
808 | free(line); | 836 | free(line); |
809 | fclose(file); | 837 | fclose(file); |
810 | 838 | ||
811 | return machine__set_modules_path(machine); | 839 | if (machine__set_modules_path(machine) < 0) { |
840 | pr_debug("Problems setting modules path maps, continuing anyway...\n"); | ||
841 | } | ||
842 | return 0; | ||
812 | 843 | ||
813 | out_delete_line: | 844 | out_delete_line: |
814 | free(line); | 845 | free(line); |
@@ -858,6 +889,18 @@ static void machine__set_kernel_mmap_len(struct machine *machine, | |||
858 | } | 889 | } |
859 | } | 890 | } |
860 | 891 | ||
892 | static bool machine__uses_kcore(struct machine *machine) | ||
893 | { | ||
894 | struct dso *dso; | ||
895 | |||
896 | list_for_each_entry(dso, &machine->kernel_dsos, node) { | ||
897 | if (dso__is_kcore(dso)) | ||
898 | return true; | ||
899 | } | ||
900 | |||
901 | return false; | ||
902 | } | ||
903 | |||
861 | static int machine__process_kernel_mmap_event(struct machine *machine, | 904 | static int machine__process_kernel_mmap_event(struct machine *machine, |
862 | union perf_event *event) | 905 | union perf_event *event) |
863 | { | 906 | { |
@@ -866,6 +909,10 @@ static int machine__process_kernel_mmap_event(struct machine *machine, | |||
866 | enum dso_kernel_type kernel_type; | 909 | enum dso_kernel_type kernel_type; |
867 | bool is_kernel_mmap; | 910 | bool is_kernel_mmap; |
868 | 911 | ||
912 | /* If we have maps from kcore then we do not need or want any others */ | ||
913 | if (machine__uses_kcore(machine)) | ||
914 | return 0; | ||
915 | |||
869 | machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); | 916 | machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix)); |
870 | if (machine__is_host(machine)) | 917 | if (machine__is_host(machine)) |
871 | kernel_type = DSO_TYPE_KERNEL; | 918 | kernel_type = DSO_TYPE_KERNEL; |
@@ -950,6 +997,54 @@ out_problem: | |||
950 | return -1; | 997 | return -1; |
951 | } | 998 | } |
952 | 999 | ||
1000 | int machine__process_mmap2_event(struct machine *machine, | ||
1001 | union perf_event *event) | ||
1002 | { | ||
1003 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1004 | struct thread *thread; | ||
1005 | struct map *map; | ||
1006 | enum map_type type; | ||
1007 | int ret = 0; | ||
1008 | |||
1009 | if (dump_trace) | ||
1010 | perf_event__fprintf_mmap2(event, stdout); | ||
1011 | |||
1012 | if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL || | ||
1013 | cpumode == PERF_RECORD_MISC_KERNEL) { | ||
1014 | ret = machine__process_kernel_mmap_event(machine, event); | ||
1015 | if (ret < 0) | ||
1016 | goto out_problem; | ||
1017 | return 0; | ||
1018 | } | ||
1019 | |||
1020 | thread = machine__findnew_thread(machine, event->mmap2.pid, | ||
1021 | event->mmap2.pid); | ||
1022 | if (thread == NULL) | ||
1023 | goto out_problem; | ||
1024 | |||
1025 | if (event->header.misc & PERF_RECORD_MISC_MMAP_DATA) | ||
1026 | type = MAP__VARIABLE; | ||
1027 | else | ||
1028 | type = MAP__FUNCTION; | ||
1029 | |||
1030 | map = map__new(&machine->user_dsos, event->mmap2.start, | ||
1031 | event->mmap2.len, event->mmap2.pgoff, | ||
1032 | event->mmap2.pid, event->mmap2.maj, | ||
1033 | event->mmap2.min, event->mmap2.ino, | ||
1034 | event->mmap2.ino_generation, | ||
1035 | event->mmap2.filename, type); | ||
1036 | |||
1037 | if (map == NULL) | ||
1038 | goto out_problem; | ||
1039 | |||
1040 | thread__insert_map(thread, map); | ||
1041 | return 0; | ||
1042 | |||
1043 | out_problem: | ||
1044 | dump_printf("problem processing PERF_RECORD_MMAP2, skipping event.\n"); | ||
1045 | return 0; | ||
1046 | } | ||
1047 | |||
953 | int machine__process_mmap_event(struct machine *machine, union perf_event *event) | 1048 | int machine__process_mmap_event(struct machine *machine, union perf_event *event) |
954 | { | 1049 | { |
955 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 1050 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
@@ -969,7 +1064,8 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
969 | return 0; | 1064 | return 0; |
970 | } | 1065 | } |
971 | 1066 | ||
972 | thread = machine__findnew_thread(machine, event->mmap.pid); | 1067 | thread = machine__findnew_thread(machine, event->mmap.pid, |
1068 | event->mmap.pid); | ||
973 | if (thread == NULL) | 1069 | if (thread == NULL) |
974 | goto out_problem; | 1070 | goto out_problem; |
975 | 1071 | ||
@@ -980,7 +1076,8 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event | |||
980 | 1076 | ||
981 | map = map__new(&machine->user_dsos, event->mmap.start, | 1077 | map = map__new(&machine->user_dsos, event->mmap.start, |
982 | event->mmap.len, event->mmap.pgoff, | 1078 | event->mmap.len, event->mmap.pgoff, |
983 | event->mmap.pid, event->mmap.filename, | 1079 | event->mmap.pid, 0, 0, 0, 0, |
1080 | event->mmap.filename, | ||
984 | type); | 1081 | type); |
985 | 1082 | ||
986 | if (map == NULL) | 1083 | if (map == NULL) |
@@ -994,11 +1091,30 @@ out_problem: | |||
994 | return 0; | 1091 | return 0; |
995 | } | 1092 | } |
996 | 1093 | ||
1094 | static void machine__remove_thread(struct machine *machine, struct thread *th) | ||
1095 | { | ||
1096 | machine->last_match = NULL; | ||
1097 | rb_erase(&th->rb_node, &machine->threads); | ||
1098 | /* | ||
1099 | * We may have references to this thread, for instance in some hist_entry | ||
1100 | * instances, so just move them to a separate list. | ||
1101 | */ | ||
1102 | list_add_tail(&th->node, &machine->dead_threads); | ||
1103 | } | ||
1104 | |||
997 | int machine__process_fork_event(struct machine *machine, union perf_event *event) | 1105 | int machine__process_fork_event(struct machine *machine, union perf_event *event) |
998 | { | 1106 | { |
999 | struct thread *thread = machine__findnew_thread(machine, event->fork.tid); | 1107 | struct thread *thread = machine__find_thread(machine, event->fork.tid); |
1000 | struct thread *parent = machine__findnew_thread(machine, event->fork.ptid); | 1108 | struct thread *parent = machine__findnew_thread(machine, |
1109 | event->fork.ppid, | ||
1110 | event->fork.ptid); | ||
1001 | 1111 | ||
1112 | /* if a thread currently exists for the thread id remove it */ | ||
1113 | if (thread != NULL) | ||
1114 | machine__remove_thread(machine, thread); | ||
1115 | |||
1116 | thread = machine__findnew_thread(machine, event->fork.pid, | ||
1117 | event->fork.tid); | ||
1002 | if (dump_trace) | 1118 | if (dump_trace) |
1003 | perf_event__fprintf_task(event, stdout); | 1119 | perf_event__fprintf_task(event, stdout); |
1004 | 1120 | ||
@@ -1011,18 +1127,8 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event | |||
1011 | return 0; | 1127 | return 0; |
1012 | } | 1128 | } |
1013 | 1129 | ||
1014 | static void machine__remove_thread(struct machine *machine, struct thread *th) | 1130 | int machine__process_exit_event(struct machine *machine __maybe_unused, |
1015 | { | 1131 | union perf_event *event) |
1016 | machine->last_match = NULL; | ||
1017 | rb_erase(&th->rb_node, &machine->threads); | ||
1018 | /* | ||
1019 | * We may have references to this thread, for instance in some hist_entry | ||
1020 | * instances, so just move them to a separate list. | ||
1021 | */ | ||
1022 | list_add_tail(&th->node, &machine->dead_threads); | ||
1023 | } | ||
1024 | |||
1025 | int machine__process_exit_event(struct machine *machine, union perf_event *event) | ||
1026 | { | 1132 | { |
1027 | struct thread *thread = machine__find_thread(machine, event->fork.tid); | 1133 | struct thread *thread = machine__find_thread(machine, event->fork.tid); |
1028 | 1134 | ||
@@ -1030,7 +1136,7 @@ int machine__process_exit_event(struct machine *machine, union perf_event *event | |||
1030 | perf_event__fprintf_task(event, stdout); | 1136 | perf_event__fprintf_task(event, stdout); |
1031 | 1137 | ||
1032 | if (thread != NULL) | 1138 | if (thread != NULL) |
1033 | machine__remove_thread(machine, thread); | 1139 | thread__exited(thread); |
1034 | 1140 | ||
1035 | return 0; | 1141 | return 0; |
1036 | } | 1142 | } |
@@ -1044,6 +1150,8 @@ int machine__process_event(struct machine *machine, union perf_event *event) | |||
1044 | ret = machine__process_comm_event(machine, event); break; | 1150 | ret = machine__process_comm_event(machine, event); break; |
1045 | case PERF_RECORD_MMAP: | 1151 | case PERF_RECORD_MMAP: |
1046 | ret = machine__process_mmap_event(machine, event); break; | 1152 | ret = machine__process_mmap_event(machine, event); break; |
1153 | case PERF_RECORD_MMAP2: | ||
1154 | ret = machine__process_mmap2_event(machine, event); break; | ||
1047 | case PERF_RECORD_FORK: | 1155 | case PERF_RECORD_FORK: |
1048 | ret = machine__process_fork_event(machine, event); break; | 1156 | ret = machine__process_fork_event(machine, event); break; |
1049 | case PERF_RECORD_EXIT: | 1157 | case PERF_RECORD_EXIT: |
@@ -1058,11 +1166,10 @@ int machine__process_event(struct machine *machine, union perf_event *event) | |||
1058 | return ret; | 1166 | return ret; |
1059 | } | 1167 | } |
1060 | 1168 | ||
1061 | static bool symbol__match_parent_regex(struct symbol *sym) | 1169 | static bool symbol__match_regex(struct symbol *sym, regex_t *regex) |
1062 | { | 1170 | { |
1063 | if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) | 1171 | if (sym->name && !regexec(regex, sym->name, 0, NULL, 0)) |
1064 | return 1; | 1172 | return 1; |
1065 | |||
1066 | return 0; | 1173 | return 0; |
1067 | } | 1174 | } |
1068 | 1175 | ||
@@ -1094,7 +1201,7 @@ static void ip__resolve_ams(struct machine *machine, struct thread *thread, | |||
1094 | * or else, the symbol is unknown | 1201 | * or else, the symbol is unknown |
1095 | */ | 1202 | */ |
1096 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, | 1203 | thread__find_addr_location(thread, machine, m, MAP__FUNCTION, |
1097 | ip, &al, NULL); | 1204 | ip, &al); |
1098 | if (al.sym) | 1205 | if (al.sym) |
1099 | goto found; | 1206 | goto found; |
1100 | } | 1207 | } |
@@ -1112,8 +1219,8 @@ static void ip__resolve_data(struct machine *machine, struct thread *thread, | |||
1112 | 1219 | ||
1113 | memset(&al, 0, sizeof(al)); | 1220 | memset(&al, 0, sizeof(al)); |
1114 | 1221 | ||
1115 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, &al, | 1222 | thread__find_addr_location(thread, machine, m, MAP__VARIABLE, addr, |
1116 | NULL); | 1223 | &al); |
1117 | ams->addr = addr; | 1224 | ams->addr = addr; |
1118 | ams->al_addr = al.addr; | 1225 | ams->al_addr = al.addr; |
1119 | ams->sym = al.sym; | 1226 | ams->sym = al.sym; |
@@ -1159,8 +1266,8 @@ struct branch_info *machine__resolve_bstack(struct machine *machine, | |||
1159 | static int machine__resolve_callchain_sample(struct machine *machine, | 1266 | static int machine__resolve_callchain_sample(struct machine *machine, |
1160 | struct thread *thread, | 1267 | struct thread *thread, |
1161 | struct ip_callchain *chain, | 1268 | struct ip_callchain *chain, |
1162 | struct symbol **parent) | 1269 | struct symbol **parent, |
1163 | 1270 | struct addr_location *root_al) | |
1164 | { | 1271 | { |
1165 | u8 cpumode = PERF_RECORD_MISC_USER; | 1272 | u8 cpumode = PERF_RECORD_MISC_USER; |
1166 | unsigned int i; | 1273 | unsigned int i; |
@@ -1208,11 +1315,18 @@ static int machine__resolve_callchain_sample(struct machine *machine, | |||
1208 | 1315 | ||
1209 | al.filtered = false; | 1316 | al.filtered = false; |
1210 | thread__find_addr_location(thread, machine, cpumode, | 1317 | thread__find_addr_location(thread, machine, cpumode, |
1211 | MAP__FUNCTION, ip, &al, NULL); | 1318 | MAP__FUNCTION, ip, &al); |
1212 | if (al.sym != NULL) { | 1319 | if (al.sym != NULL) { |
1213 | if (sort__has_parent && !*parent && | 1320 | if (sort__has_parent && !*parent && |
1214 | symbol__match_parent_regex(al.sym)) | 1321 | symbol__match_regex(al.sym, &parent_regex)) |
1215 | *parent = al.sym; | 1322 | *parent = al.sym; |
1323 | else if (have_ignore_callees && root_al && | ||
1324 | symbol__match_regex(al.sym, &ignore_callees_regex)) { | ||
1325 | /* Treat this symbol as the root, | ||
1326 | forgetting its callees. */ | ||
1327 | *root_al = al; | ||
1328 | callchain_cursor_reset(&callchain_cursor); | ||
1329 | } | ||
1216 | if (!symbol_conf.use_callchain) | 1330 | if (!symbol_conf.use_callchain) |
1217 | break; | 1331 | break; |
1218 | } | 1332 | } |
@@ -1237,15 +1351,13 @@ int machine__resolve_callchain(struct machine *machine, | |||
1237 | struct perf_evsel *evsel, | 1351 | struct perf_evsel *evsel, |
1238 | struct thread *thread, | 1352 | struct thread *thread, |
1239 | struct perf_sample *sample, | 1353 | struct perf_sample *sample, |
1240 | struct symbol **parent) | 1354 | struct symbol **parent, |
1241 | 1355 | struct addr_location *root_al) | |
1242 | { | 1356 | { |
1243 | int ret; | 1357 | int ret; |
1244 | 1358 | ||
1245 | callchain_cursor_reset(&callchain_cursor); | ||
1246 | |||
1247 | ret = machine__resolve_callchain_sample(machine, thread, | 1359 | ret = machine__resolve_callchain_sample(machine, thread, |
1248 | sample->callchain, parent); | 1360 | sample->callchain, parent, root_al); |
1249 | if (ret) | 1361 | if (ret) |
1250 | return ret; | 1362 | return ret; |
1251 | 1363 | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 77940680f1fc..58a6be1fc739 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <linux/rbtree.h> | 5 | #include <linux/rbtree.h> |
6 | #include "map.h" | 6 | #include "map.h" |
7 | 7 | ||
8 | struct addr_location; | ||
8 | struct branch_stack; | 9 | struct branch_stack; |
9 | struct perf_evsel; | 10 | struct perf_evsel; |
10 | struct perf_sample; | 11 | struct perf_sample; |
@@ -28,6 +29,7 @@ struct machine { | |||
28 | struct list_head kernel_dsos; | 29 | struct list_head kernel_dsos; |
29 | struct map_groups kmaps; | 30 | struct map_groups kmaps; |
30 | struct map *vmlinux_maps[MAP__NR_TYPES]; | 31 | struct map *vmlinux_maps[MAP__NR_TYPES]; |
32 | symbol_filter_t symbol_filter; | ||
31 | }; | 33 | }; |
32 | 34 | ||
33 | static inline | 35 | static inline |
@@ -36,13 +38,14 @@ struct map *machine__kernel_map(struct machine *machine, enum map_type type) | |||
36 | return machine->vmlinux_maps[type]; | 38 | return machine->vmlinux_maps[type]; |
37 | } | 39 | } |
38 | 40 | ||
39 | struct thread *machine__find_thread(struct machine *machine, pid_t pid); | 41 | struct thread *machine__find_thread(struct machine *machine, pid_t tid); |
40 | 42 | ||
41 | int machine__process_comm_event(struct machine *machine, union perf_event *event); | 43 | 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); | 44 | 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); | 45 | 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); | 46 | 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); | 47 | int machine__process_mmap_event(struct machine *machine, union perf_event *event); |
48 | int machine__process_mmap2_event(struct machine *machine, union perf_event *event); | ||
46 | int machine__process_event(struct machine *machine, union perf_event *event); | 49 | int machine__process_event(struct machine *machine, union perf_event *event); |
47 | 50 | ||
48 | typedef void (*machine__process_t)(struct machine *machine, void *data); | 51 | typedef void (*machine__process_t)(struct machine *machine, void *data); |
@@ -50,6 +53,7 @@ typedef void (*machine__process_t)(struct machine *machine, void *data); | |||
50 | struct machines { | 53 | struct machines { |
51 | struct machine host; | 54 | struct machine host; |
52 | struct rb_root guests; | 55 | struct rb_root guests; |
56 | symbol_filter_t symbol_filter; | ||
53 | }; | 57 | }; |
54 | 58 | ||
55 | void machines__init(struct machines *machines); | 59 | void machines__init(struct machines *machines); |
@@ -67,6 +71,9 @@ struct machine *machines__findnew(struct machines *machines, pid_t pid); | |||
67 | void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size); | 71 | 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); | 72 | char *machine__mmap_name(struct machine *machine, char *bf, size_t size); |
69 | 73 | ||
74 | void machines__set_symbol_filter(struct machines *machines, | ||
75 | symbol_filter_t symbol_filter); | ||
76 | |||
70 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); | 77 | int machine__init(struct machine *machine, const char *root_dir, pid_t pid); |
71 | void machine__exit(struct machine *machine); | 78 | void machine__exit(struct machine *machine); |
72 | void machine__delete_dead_threads(struct machine *machine); | 79 | void machine__delete_dead_threads(struct machine *machine); |
@@ -83,7 +90,8 @@ int machine__resolve_callchain(struct machine *machine, | |||
83 | struct perf_evsel *evsel, | 90 | struct perf_evsel *evsel, |
84 | struct thread *thread, | 91 | struct thread *thread, |
85 | struct perf_sample *sample, | 92 | struct perf_sample *sample, |
86 | struct symbol **parent); | 93 | struct symbol **parent, |
94 | struct addr_location *root_al); | ||
87 | 95 | ||
88 | /* | 96 | /* |
89 | * Default guest kernel is defined by parameter --guestkallsyms | 97 | * Default guest kernel is defined by parameter --guestkallsyms |
@@ -99,7 +107,8 @@ static inline bool machine__is_host(struct machine *machine) | |||
99 | return machine ? machine->pid == HOST_KERNEL_ID : false; | 107 | return machine ? machine->pid == HOST_KERNEL_ID : false; |
100 | } | 108 | } |
101 | 109 | ||
102 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid); | 110 | struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, |
111 | pid_t tid); | ||
103 | 112 | ||
104 | size_t machine__fprintf(struct machine *machine, FILE *fp); | 113 | size_t machine__fprintf(struct machine *machine, FILE *fp); |
105 | 114 | ||
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 8bcdf9e54089..4f6680d2043b 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -48,7 +48,8 @@ void map__init(struct map *map, enum map_type type, | |||
48 | } | 48 | } |
49 | 49 | ||
50 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 50 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
51 | u64 pgoff, u32 pid, char *filename, | 51 | u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, |
52 | u64 ino_gen, char *filename, | ||
52 | enum map_type type) | 53 | enum map_type type) |
53 | { | 54 | { |
54 | struct map *map = malloc(sizeof(*map)); | 55 | struct map *map = malloc(sizeof(*map)); |
@@ -62,6 +63,11 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
62 | vdso = is_vdso_map(filename); | 63 | vdso = is_vdso_map(filename); |
63 | no_dso = is_no_dso_memory(filename); | 64 | no_dso = is_no_dso_memory(filename); |
64 | 65 | ||
66 | map->maj = d_maj; | ||
67 | map->min = d_min; | ||
68 | map->ino = ino; | ||
69 | map->ino_generation = ino_gen; | ||
70 | |||
65 | if (anon) { | 71 | if (anon) { |
66 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); | 72 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); |
67 | filename = newfilename; | 73 | filename = newfilename; |
@@ -182,12 +188,6 @@ int map__load(struct map *map, symbol_filter_t filter) | |||
182 | #endif | 188 | #endif |
183 | return -1; | 189 | return -1; |
184 | } | 190 | } |
185 | /* | ||
186 | * Only applies to the kernel, as its symtabs aren't relative like the | ||
187 | * module ones. | ||
188 | */ | ||
189 | if (map->dso->kernel) | ||
190 | map__reloc_vmlinux(map); | ||
191 | 191 | ||
192 | return 0; | 192 | return 0; |
193 | } | 193 | } |
@@ -254,14 +254,18 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp) | |||
254 | 254 | ||
255 | /* | 255 | /* |
256 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. | 256 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. |
257 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases. | 257 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases except ET_REL which is |
258 | * relative to section start. | ||
258 | */ | 259 | */ |
259 | u64 map__rip_2objdump(struct map *map, u64 rip) | 260 | u64 map__rip_2objdump(struct map *map, u64 rip) |
260 | { | 261 | { |
261 | u64 addr = map->dso->adjust_symbols ? | 262 | if (!map->dso->adjust_symbols) |
262 | map->unmap_ip(map, rip) : /* RIP -> IP */ | 263 | return rip; |
263 | rip; | 264 | |
264 | return addr; | 265 | if (map->dso->rel) |
266 | return rip - map->pgoff; | ||
267 | |||
268 | return map->unmap_ip(map, rip); | ||
265 | } | 269 | } |
266 | 270 | ||
267 | void map_groups__init(struct map_groups *mg) | 271 | void map_groups__init(struct map_groups *mg) |
@@ -513,35 +517,6 @@ int map_groups__clone(struct map_groups *mg, | |||
513 | return 0; | 517 | return 0; |
514 | } | 518 | } |
515 | 519 | ||
516 | static u64 map__reloc_map_ip(struct map *map, u64 ip) | ||
517 | { | ||
518 | return ip + (s64)map->pgoff; | ||
519 | } | ||
520 | |||
521 | static u64 map__reloc_unmap_ip(struct map *map, u64 ip) | ||
522 | { | ||
523 | return ip - (s64)map->pgoff; | ||
524 | } | ||
525 | |||
526 | void map__reloc_vmlinux(struct map *map) | ||
527 | { | ||
528 | struct kmap *kmap = map__kmap(map); | ||
529 | s64 reloc; | ||
530 | |||
531 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) | ||
532 | return; | ||
533 | |||
534 | reloc = (kmap->ref_reloc_sym->unrelocated_addr - | ||
535 | kmap->ref_reloc_sym->addr); | ||
536 | |||
537 | if (!reloc) | ||
538 | return; | ||
539 | |||
540 | map->map_ip = map__reloc_map_ip; | ||
541 | map->unmap_ip = map__reloc_unmap_ip; | ||
542 | map->pgoff = reloc; | ||
543 | } | ||
544 | |||
545 | void maps__insert(struct rb_root *maps, struct map *map) | 520 | void maps__insert(struct rb_root *maps, struct map *map) |
546 | { | 521 | { |
547 | struct rb_node **p = &maps->rb_node; | 522 | struct rb_node **p = &maps->rb_node; |
@@ -586,3 +561,21 @@ struct map *maps__find(struct rb_root *maps, u64 ip) | |||
586 | 561 | ||
587 | return NULL; | 562 | return NULL; |
588 | } | 563 | } |
564 | |||
565 | struct map *maps__first(struct rb_root *maps) | ||
566 | { | ||
567 | struct rb_node *first = rb_first(maps); | ||
568 | |||
569 | if (first) | ||
570 | return rb_entry(first, struct map, rb_node); | ||
571 | return NULL; | ||
572 | } | ||
573 | |||
574 | struct map *maps__next(struct map *map) | ||
575 | { | ||
576 | struct rb_node *next = rb_next(&map->rb_node); | ||
577 | |||
578 | if (next) | ||
579 | return rb_entry(next, struct map, rb_node); | ||
580 | return NULL; | ||
581 | } | ||
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index a887f2c9dfbb..4886ca280536 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -36,6 +36,9 @@ struct map { | |||
36 | bool erange_warned; | 36 | bool erange_warned; |
37 | u32 priv; | 37 | u32 priv; |
38 | u64 pgoff; | 38 | u64 pgoff; |
39 | u32 maj, min; /* only valid for MMAP2 record */ | ||
40 | u64 ino; /* only valid for MMAP2 record */ | ||
41 | u64 ino_generation;/* only valid for MMAP2 record */ | ||
39 | 42 | ||
40 | /* ip -> dso rip */ | 43 | /* ip -> dso rip */ |
41 | u64 (*map_ip)(struct map *, u64); | 44 | u64 (*map_ip)(struct map *, u64); |
@@ -88,8 +91,9 @@ typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | |||
88 | void map__init(struct map *map, enum map_type type, | 91 | void map__init(struct map *map, enum map_type type, |
89 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 92 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
90 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 93 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
91 | u64 pgoff, u32 pid, char *filename, | 94 | u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino, |
92 | enum map_type type); | 95 | u64 ino_gen, |
96 | char *filename, enum map_type type); | ||
93 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 97 | struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
94 | void map__delete(struct map *map); | 98 | void map__delete(struct map *map); |
95 | struct map *map__clone(struct map *map); | 99 | struct map *map__clone(struct map *map); |
@@ -112,6 +116,8 @@ size_t __map_groups__fprintf_maps(struct map_groups *mg, | |||
112 | void maps__insert(struct rb_root *maps, struct map *map); | 116 | void maps__insert(struct rb_root *maps, struct map *map); |
113 | void maps__remove(struct rb_root *maps, struct map *map); | 117 | void maps__remove(struct rb_root *maps, struct map *map); |
114 | struct map *maps__find(struct rb_root *maps, u64 addr); | 118 | struct map *maps__find(struct rb_root *maps, u64 addr); |
119 | struct map *maps__first(struct rb_root *maps); | ||
120 | struct map *maps__next(struct map *map); | ||
115 | void map_groups__init(struct map_groups *mg); | 121 | void map_groups__init(struct map_groups *mg); |
116 | void map_groups__exit(struct map_groups *mg); | 122 | void map_groups__exit(struct map_groups *mg); |
117 | int map_groups__clone(struct map_groups *mg, | 123 | int map_groups__clone(struct map_groups *mg, |
@@ -139,6 +145,17 @@ static inline struct map *map_groups__find(struct map_groups *mg, | |||
139 | return maps__find(&mg->maps[type], addr); | 145 | return maps__find(&mg->maps[type], addr); |
140 | } | 146 | } |
141 | 147 | ||
148 | static inline struct map *map_groups__first(struct map_groups *mg, | ||
149 | enum map_type type) | ||
150 | { | ||
151 | return maps__first(&mg->maps[type]); | ||
152 | } | ||
153 | |||
154 | static inline struct map *map_groups__next(struct map *map) | ||
155 | { | ||
156 | return maps__next(map); | ||
157 | } | ||
158 | |||
142 | struct symbol *map_groups__find_symbol(struct map_groups *mg, | 159 | struct symbol *map_groups__find_symbol(struct map_groups *mg, |
143 | enum map_type type, u64 addr, | 160 | enum map_type type, u64 addr, |
144 | struct map **mapp, | 161 | struct map **mapp, |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 995fc25db8c6..98125319b158 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -6,7 +6,7 @@ | |||
6 | #include "parse-options.h" | 6 | #include "parse-options.h" |
7 | #include "parse-events.h" | 7 | #include "parse-events.h" |
8 | #include "exec_cmd.h" | 8 | #include "exec_cmd.h" |
9 | #include "string.h" | 9 | #include "linux/string.h" |
10 | #include "symbol.h" | 10 | #include "symbol.h" |
11 | #include "cache.h" | 11 | #include "cache.h" |
12 | #include "header.h" | 12 | #include "header.h" |
@@ -15,6 +15,7 @@ | |||
15 | #define YY_EXTRA_TYPE int | 15 | #define YY_EXTRA_TYPE int |
16 | #include "parse-events-flex.h" | 16 | #include "parse-events-flex.h" |
17 | #include "pmu.h" | 17 | #include "pmu.h" |
18 | #include "thread_map.h" | ||
18 | 19 | ||
19 | #define MAX_NAME_LEN 100 | 20 | #define MAX_NAME_LEN 100 |
20 | 21 | ||
@@ -108,6 +109,10 @@ static struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = { | |||
108 | .symbol = "emulation-faults", | 109 | .symbol = "emulation-faults", |
109 | .alias = "", | 110 | .alias = "", |
110 | }, | 111 | }, |
112 | [PERF_COUNT_SW_DUMMY] = { | ||
113 | .symbol = "dummy", | ||
114 | .alias = "", | ||
115 | }, | ||
111 | }; | 116 | }; |
112 | 117 | ||
113 | #define __PERF_EVENT_FIELD(config, name) \ | 118 | #define __PERF_EVENT_FIELD(config, name) \ |
@@ -217,6 +222,29 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
217 | return NULL; | 222 | return NULL; |
218 | } | 223 | } |
219 | 224 | ||
225 | struct tracepoint_path *tracepoint_name_to_path(const char *name) | ||
226 | { | ||
227 | struct tracepoint_path *path = zalloc(sizeof(*path)); | ||
228 | char *str = strchr(name, ':'); | ||
229 | |||
230 | if (path == NULL || str == NULL) { | ||
231 | free(path); | ||
232 | return NULL; | ||
233 | } | ||
234 | |||
235 | path->system = strndup(name, str - name); | ||
236 | path->name = strdup(str+1); | ||
237 | |||
238 | if (path->system == NULL || path->name == NULL) { | ||
239 | free(path->system); | ||
240 | free(path->name); | ||
241 | free(path); | ||
242 | path = NULL; | ||
243 | } | ||
244 | |||
245 | return path; | ||
246 | } | ||
247 | |||
220 | const char *event_type(int type) | 248 | const char *event_type(int type) |
221 | { | 249 | { |
222 | switch (type) { | 250 | switch (type) { |
@@ -241,40 +269,29 @@ const char *event_type(int type) | |||
241 | 269 | ||
242 | 270 | ||
243 | 271 | ||
244 | static int __add_event(struct list_head **_list, int *idx, | 272 | static int __add_event(struct list_head *list, int *idx, |
245 | struct perf_event_attr *attr, | 273 | struct perf_event_attr *attr, |
246 | char *name, struct cpu_map *cpus) | 274 | char *name, struct cpu_map *cpus) |
247 | { | 275 | { |
248 | struct perf_evsel *evsel; | 276 | struct perf_evsel *evsel; |
249 | struct list_head *list = *_list; | ||
250 | |||
251 | if (!list) { | ||
252 | list = malloc(sizeof(*list)); | ||
253 | if (!list) | ||
254 | return -ENOMEM; | ||
255 | INIT_LIST_HEAD(list); | ||
256 | } | ||
257 | 277 | ||
258 | event_attr_init(attr); | 278 | event_attr_init(attr); |
259 | 279 | ||
260 | evsel = perf_evsel__new(attr, (*idx)++); | 280 | evsel = perf_evsel__new(attr, (*idx)++); |
261 | if (!evsel) { | 281 | if (!evsel) |
262 | free(list); | ||
263 | return -ENOMEM; | 282 | return -ENOMEM; |
264 | } | ||
265 | 283 | ||
266 | evsel->cpus = cpus; | 284 | evsel->cpus = cpus; |
267 | if (name) | 285 | if (name) |
268 | evsel->name = strdup(name); | 286 | evsel->name = strdup(name); |
269 | list_add_tail(&evsel->node, list); | 287 | list_add_tail(&evsel->node, list); |
270 | *_list = list; | ||
271 | return 0; | 288 | return 0; |
272 | } | 289 | } |
273 | 290 | ||
274 | static int add_event(struct list_head **_list, int *idx, | 291 | static int add_event(struct list_head *list, int *idx, |
275 | struct perf_event_attr *attr, char *name) | 292 | struct perf_event_attr *attr, char *name) |
276 | { | 293 | { |
277 | return __add_event(_list, idx, attr, name, NULL); | 294 | return __add_event(list, idx, attr, name, NULL); |
278 | } | 295 | } |
279 | 296 | ||
280 | static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) | 297 | static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) |
@@ -295,7 +312,7 @@ static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES] | |||
295 | return -1; | 312 | return -1; |
296 | } | 313 | } |
297 | 314 | ||
298 | int parse_events_add_cache(struct list_head **list, int *idx, | 315 | int parse_events_add_cache(struct list_head *list, int *idx, |
299 | char *type, char *op_result1, char *op_result2) | 316 | char *type, char *op_result1, char *op_result2) |
300 | { | 317 | { |
301 | struct perf_event_attr attr; | 318 | struct perf_event_attr attr; |
@@ -356,31 +373,21 @@ int parse_events_add_cache(struct list_head **list, int *idx, | |||
356 | return add_event(list, idx, &attr, name); | 373 | return add_event(list, idx, &attr, name); |
357 | } | 374 | } |
358 | 375 | ||
359 | static int add_tracepoint(struct list_head **listp, int *idx, | 376 | static int add_tracepoint(struct list_head *list, int *idx, |
360 | char *sys_name, char *evt_name) | 377 | char *sys_name, char *evt_name) |
361 | { | 378 | { |
362 | struct perf_evsel *evsel; | 379 | struct perf_evsel *evsel; |
363 | struct list_head *list = *listp; | ||
364 | |||
365 | if (!list) { | ||
366 | list = malloc(sizeof(*list)); | ||
367 | if (!list) | ||
368 | return -ENOMEM; | ||
369 | INIT_LIST_HEAD(list); | ||
370 | } | ||
371 | 380 | ||
372 | evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++); | 381 | evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++); |
373 | if (!evsel) { | 382 | if (!evsel) |
374 | free(list); | ||
375 | return -ENOMEM; | 383 | return -ENOMEM; |
376 | } | ||
377 | 384 | ||
378 | list_add_tail(&evsel->node, list); | 385 | list_add_tail(&evsel->node, list); |
379 | *listp = list; | 386 | |
380 | return 0; | 387 | return 0; |
381 | } | 388 | } |
382 | 389 | ||
383 | static int add_tracepoint_multi_event(struct list_head **list, int *idx, | 390 | static int add_tracepoint_multi_event(struct list_head *list, int *idx, |
384 | char *sys_name, char *evt_name) | 391 | char *sys_name, char *evt_name) |
385 | { | 392 | { |
386 | char evt_path[MAXPATHLEN]; | 393 | char evt_path[MAXPATHLEN]; |
@@ -412,7 +419,7 @@ static int add_tracepoint_multi_event(struct list_head **list, int *idx, | |||
412 | return ret; | 419 | return ret; |
413 | } | 420 | } |
414 | 421 | ||
415 | static int add_tracepoint_event(struct list_head **list, int *idx, | 422 | static int add_tracepoint_event(struct list_head *list, int *idx, |
416 | char *sys_name, char *evt_name) | 423 | char *sys_name, char *evt_name) |
417 | { | 424 | { |
418 | return strpbrk(evt_name, "*?") ? | 425 | return strpbrk(evt_name, "*?") ? |
@@ -420,7 +427,7 @@ static int add_tracepoint_event(struct list_head **list, int *idx, | |||
420 | add_tracepoint(list, idx, sys_name, evt_name); | 427 | add_tracepoint(list, idx, sys_name, evt_name); |
421 | } | 428 | } |
422 | 429 | ||
423 | static int add_tracepoint_multi_sys(struct list_head **list, int *idx, | 430 | static int add_tracepoint_multi_sys(struct list_head *list, int *idx, |
424 | char *sys_name, char *evt_name) | 431 | char *sys_name, char *evt_name) |
425 | { | 432 | { |
426 | struct dirent *events_ent; | 433 | struct dirent *events_ent; |
@@ -452,7 +459,7 @@ static int add_tracepoint_multi_sys(struct list_head **list, int *idx, | |||
452 | return ret; | 459 | return ret; |
453 | } | 460 | } |
454 | 461 | ||
455 | int parse_events_add_tracepoint(struct list_head **list, int *idx, | 462 | int parse_events_add_tracepoint(struct list_head *list, int *idx, |
456 | char *sys, char *event) | 463 | char *sys, char *event) |
457 | { | 464 | { |
458 | int ret; | 465 | int ret; |
@@ -507,7 +514,7 @@ do { \ | |||
507 | return 0; | 514 | return 0; |
508 | } | 515 | } |
509 | 516 | ||
510 | int parse_events_add_breakpoint(struct list_head **list, int *idx, | 517 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
511 | void *ptr, char *type) | 518 | void *ptr, char *type) |
512 | { | 519 | { |
513 | struct perf_event_attr attr; | 520 | struct perf_event_attr attr; |
@@ -588,7 +595,7 @@ static int config_attr(struct perf_event_attr *attr, | |||
588 | return 0; | 595 | return 0; |
589 | } | 596 | } |
590 | 597 | ||
591 | int parse_events_add_numeric(struct list_head **list, int *idx, | 598 | int parse_events_add_numeric(struct list_head *list, int *idx, |
592 | u32 type, u64 config, | 599 | u32 type, u64 config, |
593 | struct list_head *head_config) | 600 | struct list_head *head_config) |
594 | { | 601 | { |
@@ -621,7 +628,7 @@ static char *pmu_event_name(struct list_head *head_terms) | |||
621 | return NULL; | 628 | return NULL; |
622 | } | 629 | } |
623 | 630 | ||
624 | int parse_events_add_pmu(struct list_head **list, int *idx, | 631 | int parse_events_add_pmu(struct list_head *list, int *idx, |
625 | char *name, struct list_head *head_config) | 632 | char *name, struct list_head *head_config) |
626 | { | 633 | { |
627 | struct perf_event_attr attr; | 634 | struct perf_event_attr attr; |
@@ -664,6 +671,7 @@ void parse_events__set_leader(char *name, struct list_head *list) | |||
664 | leader->group_name = name ? strdup(name) : NULL; | 671 | leader->group_name = name ? strdup(name) : NULL; |
665 | } | 672 | } |
666 | 673 | ||
674 | /* list_event is assumed to point to malloc'ed memory */ | ||
667 | void parse_events_update_lists(struct list_head *list_event, | 675 | void parse_events_update_lists(struct list_head *list_event, |
668 | struct list_head *list_all) | 676 | struct list_head *list_all) |
669 | { | 677 | { |
@@ -684,6 +692,8 @@ struct event_modifier { | |||
684 | int eG; | 692 | int eG; |
685 | int precise; | 693 | int precise; |
686 | int exclude_GH; | 694 | int exclude_GH; |
695 | int sample_read; | ||
696 | int pinned; | ||
687 | }; | 697 | }; |
688 | 698 | ||
689 | static int get_event_modifier(struct event_modifier *mod, char *str, | 699 | static int get_event_modifier(struct event_modifier *mod, char *str, |
@@ -695,6 +705,8 @@ static int get_event_modifier(struct event_modifier *mod, char *str, | |||
695 | int eH = evsel ? evsel->attr.exclude_host : 0; | 705 | int eH = evsel ? evsel->attr.exclude_host : 0; |
696 | int eG = evsel ? evsel->attr.exclude_guest : 0; | 706 | int eG = evsel ? evsel->attr.exclude_guest : 0; |
697 | int precise = evsel ? evsel->attr.precise_ip : 0; | 707 | int precise = evsel ? evsel->attr.precise_ip : 0; |
708 | int sample_read = 0; | ||
709 | int pinned = evsel ? evsel->attr.pinned : 0; | ||
698 | 710 | ||
699 | int exclude = eu | ek | eh; | 711 | int exclude = eu | ek | eh; |
700 | int exclude_GH = evsel ? evsel->exclude_GH : 0; | 712 | int exclude_GH = evsel ? evsel->exclude_GH : 0; |
@@ -727,6 +739,10 @@ static int get_event_modifier(struct event_modifier *mod, char *str, | |||
727 | /* use of precise requires exclude_guest */ | 739 | /* use of precise requires exclude_guest */ |
728 | if (!exclude_GH) | 740 | if (!exclude_GH) |
729 | eG = 1; | 741 | eG = 1; |
742 | } else if (*str == 'S') { | ||
743 | sample_read = 1; | ||
744 | } else if (*str == 'D') { | ||
745 | pinned = 1; | ||
730 | } else | 746 | } else |
731 | break; | 747 | break; |
732 | 748 | ||
@@ -753,6 +769,9 @@ static int get_event_modifier(struct event_modifier *mod, char *str, | |||
753 | mod->eG = eG; | 769 | mod->eG = eG; |
754 | mod->precise = precise; | 770 | mod->precise = precise; |
755 | mod->exclude_GH = exclude_GH; | 771 | mod->exclude_GH = exclude_GH; |
772 | mod->sample_read = sample_read; | ||
773 | mod->pinned = pinned; | ||
774 | |||
756 | return 0; | 775 | return 0; |
757 | } | 776 | } |
758 | 777 | ||
@@ -765,7 +784,7 @@ static int check_modifier(char *str) | |||
765 | char *p = str; | 784 | char *p = str; |
766 | 785 | ||
767 | /* The sizeof includes 0 byte as well. */ | 786 | /* The sizeof includes 0 byte as well. */ |
768 | if (strlen(str) > (sizeof("ukhGHppp") - 1)) | 787 | if (strlen(str) > (sizeof("ukhGHpppSD") - 1)) |
769 | return -1; | 788 | return -1; |
770 | 789 | ||
771 | while (*p) { | 790 | while (*p) { |
@@ -803,6 +822,10 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add) | |||
803 | evsel->attr.exclude_host = mod.eH; | 822 | evsel->attr.exclude_host = mod.eH; |
804 | evsel->attr.exclude_guest = mod.eG; | 823 | evsel->attr.exclude_guest = mod.eG; |
805 | evsel->exclude_GH = mod.exclude_GH; | 824 | evsel->exclude_GH = mod.exclude_GH; |
825 | evsel->sample_read = mod.sample_read; | ||
826 | |||
827 | if (perf_evsel__is_group_leader(evsel)) | ||
828 | evsel->attr.pinned = mod.pinned; | ||
806 | } | 829 | } |
807 | 830 | ||
808 | return 0; | 831 | return 0; |
@@ -820,6 +843,32 @@ int parse_events_name(struct list_head *list, char *name) | |||
820 | return 0; | 843 | return 0; |
821 | } | 844 | } |
822 | 845 | ||
846 | static int parse_events__scanner(const char *str, void *data, int start_token); | ||
847 | |||
848 | static int parse_events_fixup(int ret, const char *str, void *data, | ||
849 | int start_token) | ||
850 | { | ||
851 | char *o = strdup(str); | ||
852 | char *s = NULL; | ||
853 | char *t = o; | ||
854 | char *p; | ||
855 | int len = 0; | ||
856 | |||
857 | if (!o) | ||
858 | return ret; | ||
859 | while ((p = strsep(&t, ",")) != NULL) { | ||
860 | if (s) | ||
861 | str_append(&s, &len, ","); | ||
862 | str_append(&s, &len, "cpu/"); | ||
863 | str_append(&s, &len, p); | ||
864 | str_append(&s, &len, "/"); | ||
865 | } | ||
866 | free(o); | ||
867 | if (!s) | ||
868 | return -ENOMEM; | ||
869 | return parse_events__scanner(s, data, start_token); | ||
870 | } | ||
871 | |||
823 | static int parse_events__scanner(const char *str, void *data, int start_token) | 872 | static int parse_events__scanner(const char *str, void *data, int start_token) |
824 | { | 873 | { |
825 | YY_BUFFER_STATE buffer; | 874 | YY_BUFFER_STATE buffer; |
@@ -840,6 +889,8 @@ static int parse_events__scanner(const char *str, void *data, int start_token) | |||
840 | parse_events__flush_buffer(buffer, scanner); | 889 | parse_events__flush_buffer(buffer, scanner); |
841 | parse_events__delete_buffer(buffer, scanner); | 890 | parse_events__delete_buffer(buffer, scanner); |
842 | parse_events_lex_destroy(scanner); | 891 | parse_events_lex_destroy(scanner); |
892 | if (ret && !strchr(str, '/')) | ||
893 | ret = parse_events_fixup(ret, str, data, start_token); | ||
843 | return ret; | 894 | return ret; |
844 | } | 895 | } |
845 | 896 | ||
@@ -1026,6 +1077,33 @@ int is_valid_tracepoint(const char *event_string) | |||
1026 | return 0; | 1077 | return 0; |
1027 | } | 1078 | } |
1028 | 1079 | ||
1080 | static bool is_event_supported(u8 type, unsigned config) | ||
1081 | { | ||
1082 | bool ret = true; | ||
1083 | struct perf_evsel *evsel; | ||
1084 | struct perf_event_attr attr = { | ||
1085 | .type = type, | ||
1086 | .config = config, | ||
1087 | .disabled = 1, | ||
1088 | .exclude_kernel = 1, | ||
1089 | }; | ||
1090 | struct { | ||
1091 | struct thread_map map; | ||
1092 | int threads[1]; | ||
1093 | } tmap = { | ||
1094 | .map.nr = 1, | ||
1095 | .threads = { 0 }, | ||
1096 | }; | ||
1097 | |||
1098 | evsel = perf_evsel__new(&attr, 0); | ||
1099 | if (evsel) { | ||
1100 | ret = perf_evsel__open(evsel, NULL, &tmap.map) >= 0; | ||
1101 | perf_evsel__delete(evsel); | ||
1102 | } | ||
1103 | |||
1104 | return ret; | ||
1105 | } | ||
1106 | |||
1029 | static void __print_events_type(u8 type, struct event_symbol *syms, | 1107 | static void __print_events_type(u8 type, struct event_symbol *syms, |
1030 | unsigned max) | 1108 | unsigned max) |
1031 | { | 1109 | { |
@@ -1033,14 +1111,16 @@ static void __print_events_type(u8 type, struct event_symbol *syms, | |||
1033 | unsigned i; | 1111 | unsigned i; |
1034 | 1112 | ||
1035 | for (i = 0; i < max ; i++, syms++) { | 1113 | for (i = 0; i < max ; i++, syms++) { |
1114 | if (!is_event_supported(type, i)) | ||
1115 | continue; | ||
1116 | |||
1036 | if (strlen(syms->alias)) | 1117 | if (strlen(syms->alias)) |
1037 | snprintf(name, sizeof(name), "%s OR %s", | 1118 | snprintf(name, sizeof(name), "%s OR %s", |
1038 | syms->symbol, syms->alias); | 1119 | syms->symbol, syms->alias); |
1039 | else | 1120 | else |
1040 | snprintf(name, sizeof(name), "%s", syms->symbol); | 1121 | snprintf(name, sizeof(name), "%s", syms->symbol); |
1041 | 1122 | ||
1042 | printf(" %-50s [%s]\n", name, | 1123 | printf(" %-50s [%s]\n", name, event_type_descriptors[type]); |
1043 | event_type_descriptors[type]); | ||
1044 | } | 1124 | } |
1045 | } | 1125 | } |
1046 | 1126 | ||
@@ -1069,6 +1149,10 @@ int print_hwcache_events(const char *event_glob, bool name_only) | |||
1069 | if (event_glob != NULL && !strglobmatch(name, event_glob)) | 1149 | if (event_glob != NULL && !strglobmatch(name, event_glob)) |
1070 | continue; | 1150 | continue; |
1071 | 1151 | ||
1152 | if (!is_event_supported(PERF_TYPE_HW_CACHE, | ||
1153 | type | (op << 8) | (i << 16))) | ||
1154 | continue; | ||
1155 | |||
1072 | if (name_only) | 1156 | if (name_only) |
1073 | printf("%s ", name); | 1157 | printf("%s ", name); |
1074 | else | 1158 | else |
@@ -1079,6 +1163,8 @@ int print_hwcache_events(const char *event_glob, bool name_only) | |||
1079 | } | 1163 | } |
1080 | } | 1164 | } |
1081 | 1165 | ||
1166 | if (printed) | ||
1167 | printf("\n"); | ||
1082 | return printed; | 1168 | return printed; |
1083 | } | 1169 | } |
1084 | 1170 | ||
@@ -1096,6 +1182,9 @@ static void print_symbol_events(const char *event_glob, unsigned type, | |||
1096 | (syms->alias && strglobmatch(syms->alias, event_glob)))) | 1182 | (syms->alias && strglobmatch(syms->alias, event_glob)))) |
1097 | continue; | 1183 | continue; |
1098 | 1184 | ||
1185 | if (!is_event_supported(type, i)) | ||
1186 | continue; | ||
1187 | |||
1099 | if (name_only) { | 1188 | if (name_only) { |
1100 | printf("%s ", syms->symbol); | 1189 | printf("%s ", syms->symbol); |
1101 | continue; | 1190 | continue; |
@@ -1133,11 +1222,12 @@ void print_events(const char *event_glob, bool name_only) | |||
1133 | 1222 | ||
1134 | print_hwcache_events(event_glob, name_only); | 1223 | print_hwcache_events(event_glob, name_only); |
1135 | 1224 | ||
1225 | print_pmu_events(event_glob, name_only); | ||
1226 | |||
1136 | if (event_glob != NULL) | 1227 | if (event_glob != NULL) |
1137 | return; | 1228 | return; |
1138 | 1229 | ||
1139 | if (!name_only) { | 1230 | if (!name_only) { |
1140 | printf("\n"); | ||
1141 | printf(" %-50s [%s]\n", | 1231 | printf(" %-50s [%s]\n", |
1142 | "rNNN", | 1232 | "rNNN", |
1143 | event_type_descriptors[PERF_TYPE_RAW]); | 1233 | event_type_descriptors[PERF_TYPE_RAW]); |
@@ -1237,6 +1327,4 @@ void parse_events__free_terms(struct list_head *terms) | |||
1237 | 1327 | ||
1238 | list_for_each_entry_safe(term, h, terms, list) | 1328 | list_for_each_entry_safe(term, h, terms, list) |
1239 | free(term); | 1329 | free(term); |
1240 | |||
1241 | free(terms); | ||
1242 | } | 1330 | } |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 8a4859315fd9..f1cb4c4b3c70 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -23,6 +23,7 @@ struct tracepoint_path { | |||
23 | }; | 23 | }; |
24 | 24 | ||
25 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); | 25 | extern struct tracepoint_path *tracepoint_id_to_path(u64 config); |
26 | extern struct tracepoint_path *tracepoint_name_to_path(const char *name); | ||
26 | extern bool have_tracepoints(struct list_head *evlist); | 27 | extern bool have_tracepoints(struct list_head *evlist); |
27 | 28 | ||
28 | const char *event_type(int type); | 29 | const char *event_type(int type); |
@@ -84,16 +85,16 @@ void parse_events__free_terms(struct list_head *terms); | |||
84 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); | 85 | int parse_events__modifier_event(struct list_head *list, char *str, bool add); |
85 | int parse_events__modifier_group(struct list_head *list, char *event_mod); | 86 | int parse_events__modifier_group(struct list_head *list, char *event_mod); |
86 | int parse_events_name(struct list_head *list, char *name); | 87 | int parse_events_name(struct list_head *list, char *name); |
87 | int parse_events_add_tracepoint(struct list_head **list, int *idx, | 88 | int parse_events_add_tracepoint(struct list_head *list, int *idx, |
88 | char *sys, char *event); | 89 | char *sys, char *event); |
89 | int parse_events_add_numeric(struct list_head **list, int *idx, | 90 | int parse_events_add_numeric(struct list_head *list, int *idx, |
90 | u32 type, u64 config, | 91 | u32 type, u64 config, |
91 | struct list_head *head_config); | 92 | struct list_head *head_config); |
92 | int parse_events_add_cache(struct list_head **list, int *idx, | 93 | int parse_events_add_cache(struct list_head *list, int *idx, |
93 | char *type, char *op_result1, char *op_result2); | 94 | char *type, char *op_result1, char *op_result2); |
94 | int parse_events_add_breakpoint(struct list_head **list, int *idx, | 95 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
95 | void *ptr, char *type); | 96 | void *ptr, char *type); |
96 | int parse_events_add_pmu(struct list_head **list, int *idx, | 97 | int parse_events_add_pmu(struct list_head *list, int *idx, |
97 | char *pmu , struct list_head *head_config); | 98 | char *pmu , struct list_head *head_config); |
98 | void parse_events__set_leader(char *name, struct list_head *list); | 99 | void parse_events__set_leader(char *name, struct list_head *list); |
99 | void parse_events_update_lists(struct list_head *list_event, | 100 | void parse_events_update_lists(struct list_head *list_event, |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index e9d1134c2c68..91346b753960 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l | |||
@@ -82,7 +82,8 @@ 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 | name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?]* | 84 | name_minus [a-zA-Z_*?][a-zA-Z0-9\-_*?]* |
85 | modifier_event [ukhpGH]+ | 85 | /* If you add a modifier you need to update check_modifier() */ |
86 | modifier_event [ukhpGHSD]+ | ||
86 | modifier_bp [rwx]{1,3} | 87 | modifier_bp [rwx]{1,3} |
87 | 88 | ||
88 | %% | 89 | %% |
@@ -144,6 +145,7 @@ context-switches|cs { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW | |||
144 | cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); } | 145 | cpu-migrations|migrations { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); } |
145 | alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } | 146 | alignment-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } |
146 | emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } | 147 | emulation-faults { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } |
148 | dummy { return sym(yyscanner, PERF_TYPE_SOFTWARE, PERF_COUNT_SW_DUMMY); } | ||
147 | 149 | ||
148 | L1-dcache|l1-d|l1d|L1-data | | 150 | L1-dcache|l1-d|l1d|L1-data | |
149 | L1-icache|l1-i|l1i|L1-instruction | | 151 | L1-icache|l1-i|l1i|L1-instruction | |
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y index afc44c18dfe1..4eb67ec333f1 100644 --- a/tools/perf/util/parse-events.y +++ b/tools/perf/util/parse-events.y | |||
@@ -22,6 +22,13 @@ do { \ | |||
22 | YYABORT; \ | 22 | YYABORT; \ |
23 | } while (0) | 23 | } while (0) |
24 | 24 | ||
25 | #define ALLOC_LIST(list) \ | ||
26 | do { \ | ||
27 | list = malloc(sizeof(*list)); \ | ||
28 | ABORT_ON(!list); \ | ||
29 | INIT_LIST_HEAD(list); \ | ||
30 | } while (0) | ||
31 | |||
25 | static inc_group_count(struct list_head *list, | 32 | static inc_group_count(struct list_head *list, |
26 | struct parse_events_evlist *data) | 33 | struct parse_events_evlist *data) |
27 | { | 34 | { |
@@ -196,9 +203,10 @@ event_pmu: | |||
196 | PE_NAME '/' event_config '/' | 203 | PE_NAME '/' event_config '/' |
197 | { | 204 | { |
198 | struct parse_events_evlist *data = _data; | 205 | struct parse_events_evlist *data = _data; |
199 | struct list_head *list = NULL; | 206 | struct list_head *list; |
200 | 207 | ||
201 | ABORT_ON(parse_events_add_pmu(&list, &data->idx, $1, $3)); | 208 | ALLOC_LIST(list); |
209 | ABORT_ON(parse_events_add_pmu(list, &data->idx, $1, $3)); | ||
202 | parse_events__free_terms($3); | 210 | parse_events__free_terms($3); |
203 | $$ = list; | 211 | $$ = list; |
204 | } | 212 | } |
@@ -212,11 +220,12 @@ event_legacy_symbol: | |||
212 | value_sym '/' event_config '/' | 220 | value_sym '/' event_config '/' |
213 | { | 221 | { |
214 | struct parse_events_evlist *data = _data; | 222 | struct parse_events_evlist *data = _data; |
215 | struct list_head *list = NULL; | 223 | struct list_head *list; |
216 | int type = $1 >> 16; | 224 | int type = $1 >> 16; |
217 | int config = $1 & 255; | 225 | int config = $1 & 255; |
218 | 226 | ||
219 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, | 227 | ALLOC_LIST(list); |
228 | ABORT_ON(parse_events_add_numeric(list, &data->idx, | ||
220 | type, config, $3)); | 229 | type, config, $3)); |
221 | parse_events__free_terms($3); | 230 | parse_events__free_terms($3); |
222 | $$ = list; | 231 | $$ = list; |
@@ -225,11 +234,12 @@ value_sym '/' event_config '/' | |||
225 | value_sym sep_slash_dc | 234 | value_sym sep_slash_dc |
226 | { | 235 | { |
227 | struct parse_events_evlist *data = _data; | 236 | struct parse_events_evlist *data = _data; |
228 | struct list_head *list = NULL; | 237 | struct list_head *list; |
229 | int type = $1 >> 16; | 238 | int type = $1 >> 16; |
230 | int config = $1 & 255; | 239 | int config = $1 & 255; |
231 | 240 | ||
232 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, | 241 | ALLOC_LIST(list); |
242 | ABORT_ON(parse_events_add_numeric(list, &data->idx, | ||
233 | type, config, NULL)); | 243 | type, config, NULL)); |
234 | $$ = list; | 244 | $$ = list; |
235 | } | 245 | } |
@@ -238,27 +248,30 @@ event_legacy_cache: | |||
238 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT | 248 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT |
239 | { | 249 | { |
240 | struct parse_events_evlist *data = _data; | 250 | struct parse_events_evlist *data = _data; |
241 | struct list_head *list = NULL; | 251 | struct list_head *list; |
242 | 252 | ||
243 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, $5)); | 253 | ALLOC_LIST(list); |
254 | ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, $5)); | ||
244 | $$ = list; | 255 | $$ = list; |
245 | } | 256 | } |
246 | | | 257 | | |
247 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT | 258 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT |
248 | { | 259 | { |
249 | struct parse_events_evlist *data = _data; | 260 | struct parse_events_evlist *data = _data; |
250 | struct list_head *list = NULL; | 261 | struct list_head *list; |
251 | 262 | ||
252 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, $3, NULL)); | 263 | ALLOC_LIST(list); |
264 | ABORT_ON(parse_events_add_cache(list, &data->idx, $1, $3, NULL)); | ||
253 | $$ = list; | 265 | $$ = list; |
254 | } | 266 | } |
255 | | | 267 | | |
256 | PE_NAME_CACHE_TYPE | 268 | PE_NAME_CACHE_TYPE |
257 | { | 269 | { |
258 | struct parse_events_evlist *data = _data; | 270 | struct parse_events_evlist *data = _data; |
259 | struct list_head *list = NULL; | 271 | struct list_head *list; |
260 | 272 | ||
261 | ABORT_ON(parse_events_add_cache(&list, &data->idx, $1, NULL, NULL)); | 273 | ALLOC_LIST(list); |
274 | ABORT_ON(parse_events_add_cache(list, &data->idx, $1, NULL, NULL)); | ||
262 | $$ = list; | 275 | $$ = list; |
263 | } | 276 | } |
264 | 277 | ||
@@ -266,9 +279,10 @@ event_legacy_mem: | |||
266 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | 279 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc |
267 | { | 280 | { |
268 | struct parse_events_evlist *data = _data; | 281 | struct parse_events_evlist *data = _data; |
269 | struct list_head *list = NULL; | 282 | struct list_head *list; |
270 | 283 | ||
271 | ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, | 284 | ALLOC_LIST(list); |
285 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, | ||
272 | (void *) $2, $4)); | 286 | (void *) $2, $4)); |
273 | $$ = list; | 287 | $$ = list; |
274 | } | 288 | } |
@@ -276,9 +290,10 @@ PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | |||
276 | PE_PREFIX_MEM PE_VALUE sep_dc | 290 | PE_PREFIX_MEM PE_VALUE sep_dc |
277 | { | 291 | { |
278 | struct parse_events_evlist *data = _data; | 292 | struct parse_events_evlist *data = _data; |
279 | struct list_head *list = NULL; | 293 | struct list_head *list; |
280 | 294 | ||
281 | ABORT_ON(parse_events_add_breakpoint(&list, &data->idx, | 295 | ALLOC_LIST(list); |
296 | ABORT_ON(parse_events_add_breakpoint(list, &data->idx, | ||
282 | (void *) $2, NULL)); | 297 | (void *) $2, NULL)); |
283 | $$ = list; | 298 | $$ = list; |
284 | } | 299 | } |
@@ -287,9 +302,10 @@ event_legacy_tracepoint: | |||
287 | PE_NAME ':' PE_NAME | 302 | PE_NAME ':' PE_NAME |
288 | { | 303 | { |
289 | struct parse_events_evlist *data = _data; | 304 | struct parse_events_evlist *data = _data; |
290 | struct list_head *list = NULL; | 305 | struct list_head *list; |
291 | 306 | ||
292 | ABORT_ON(parse_events_add_tracepoint(&list, &data->idx, $1, $3)); | 307 | ALLOC_LIST(list); |
308 | ABORT_ON(parse_events_add_tracepoint(list, &data->idx, $1, $3)); | ||
293 | $$ = list; | 309 | $$ = list; |
294 | } | 310 | } |
295 | 311 | ||
@@ -297,9 +313,10 @@ event_legacy_numeric: | |||
297 | PE_VALUE ':' PE_VALUE | 313 | PE_VALUE ':' PE_VALUE |
298 | { | 314 | { |
299 | struct parse_events_evlist *data = _data; | 315 | struct parse_events_evlist *data = _data; |
300 | struct list_head *list = NULL; | 316 | struct list_head *list; |
301 | 317 | ||
302 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL)); | 318 | ALLOC_LIST(list); |
319 | ABORT_ON(parse_events_add_numeric(list, &data->idx, (u32)$1, $3, NULL)); | ||
303 | $$ = list; | 320 | $$ = list; |
304 | } | 321 | } |
305 | 322 | ||
@@ -307,9 +324,10 @@ event_legacy_raw: | |||
307 | PE_RAW | 324 | PE_RAW |
308 | { | 325 | { |
309 | struct parse_events_evlist *data = _data; | 326 | struct parse_events_evlist *data = _data; |
310 | struct list_head *list = NULL; | 327 | struct list_head *list; |
311 | 328 | ||
312 | ABORT_ON(parse_events_add_numeric(&list, &data->idx, | 329 | ALLOC_LIST(list); |
330 | ABORT_ON(parse_events_add_numeric(list, &data->idx, | ||
313 | PERF_TYPE_RAW, $1, NULL)); | 331 | PERF_TYPE_RAW, $1, NULL)); |
314 | $$ = list; | 332 | $$ = list; |
315 | } | 333 | } |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 4c6f9c490a8d..bc9d8069d376 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -73,7 +73,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head) | |||
73 | * located at: | 73 | * located at: |
74 | * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes. | 74 | * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes. |
75 | */ | 75 | */ |
76 | static int pmu_format(char *name, struct list_head *format) | 76 | static int pmu_format(const char *name, struct list_head *format) |
77 | { | 77 | { |
78 | struct stat st; | 78 | struct stat st; |
79 | char path[PATH_MAX]; | 79 | char path[PATH_MAX]; |
@@ -162,7 +162,7 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) | |||
162 | * Reading the pmu event aliases definition, which should be located at: | 162 | * Reading the pmu event aliases definition, which should be located at: |
163 | * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes. | 163 | * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes. |
164 | */ | 164 | */ |
165 | static int pmu_aliases(char *name, struct list_head *head) | 165 | static int pmu_aliases(const char *name, struct list_head *head) |
166 | { | 166 | { |
167 | struct stat st; | 167 | struct stat st; |
168 | char path[PATH_MAX]; | 168 | char path[PATH_MAX]; |
@@ -208,7 +208,7 @@ static int pmu_alias_terms(struct perf_pmu_alias *alias, | |||
208 | * located at: | 208 | * located at: |
209 | * /sys/bus/event_source/devices/<dev>/type as sysfs attribute. | 209 | * /sys/bus/event_source/devices/<dev>/type as sysfs attribute. |
210 | */ | 210 | */ |
211 | static int pmu_type(char *name, __u32 *type) | 211 | static int pmu_type(const char *name, __u32 *type) |
212 | { | 212 | { |
213 | struct stat st; | 213 | struct stat st; |
214 | char path[PATH_MAX]; | 214 | char path[PATH_MAX]; |
@@ -266,7 +266,7 @@ static void pmu_read_sysfs(void) | |||
266 | closedir(dir); | 266 | closedir(dir); |
267 | } | 267 | } |
268 | 268 | ||
269 | static struct cpu_map *pmu_cpumask(char *name) | 269 | static struct cpu_map *pmu_cpumask(const char *name) |
270 | { | 270 | { |
271 | struct stat st; | 271 | struct stat st; |
272 | char path[PATH_MAX]; | 272 | char path[PATH_MAX]; |
@@ -293,7 +293,7 @@ static struct cpu_map *pmu_cpumask(char *name) | |||
293 | return cpus; | 293 | return cpus; |
294 | } | 294 | } |
295 | 295 | ||
296 | static struct perf_pmu *pmu_lookup(char *name) | 296 | static struct perf_pmu *pmu_lookup(const char *name) |
297 | { | 297 | { |
298 | struct perf_pmu *pmu; | 298 | struct perf_pmu *pmu; |
299 | LIST_HEAD(format); | 299 | LIST_HEAD(format); |
@@ -330,7 +330,7 @@ static struct perf_pmu *pmu_lookup(char *name) | |||
330 | return pmu; | 330 | return pmu; |
331 | } | 331 | } |
332 | 332 | ||
333 | static struct perf_pmu *pmu_find(char *name) | 333 | static struct perf_pmu *pmu_find(const char *name) |
334 | { | 334 | { |
335 | struct perf_pmu *pmu; | 335 | struct perf_pmu *pmu; |
336 | 336 | ||
@@ -356,7 +356,7 @@ struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu) | |||
356 | return NULL; | 356 | return NULL; |
357 | } | 357 | } |
358 | 358 | ||
359 | struct perf_pmu *perf_pmu__find(char *name) | 359 | struct perf_pmu *perf_pmu__find(const char *name) |
360 | { | 360 | { |
361 | struct perf_pmu *pmu; | 361 | struct perf_pmu *pmu; |
362 | 362 | ||
@@ -564,3 +564,76 @@ void perf_pmu__set_format(unsigned long *bits, long from, long to) | |||
564 | for (b = from; b <= to; b++) | 564 | for (b = from; b <= to; b++) |
565 | set_bit(b, bits); | 565 | set_bit(b, bits); |
566 | } | 566 | } |
567 | |||
568 | static char *format_alias(char *buf, int len, struct perf_pmu *pmu, | ||
569 | struct perf_pmu_alias *alias) | ||
570 | { | ||
571 | snprintf(buf, len, "%s/%s/", pmu->name, alias->name); | ||
572 | return buf; | ||
573 | } | ||
574 | |||
575 | static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu, | ||
576 | struct perf_pmu_alias *alias) | ||
577 | { | ||
578 | snprintf(buf, len, "%s OR %s/%s/", alias->name, pmu->name, alias->name); | ||
579 | return buf; | ||
580 | } | ||
581 | |||
582 | static int cmp_string(const void *a, const void *b) | ||
583 | { | ||
584 | const char * const *as = a; | ||
585 | const char * const *bs = b; | ||
586 | return strcmp(*as, *bs); | ||
587 | } | ||
588 | |||
589 | void print_pmu_events(const char *event_glob, bool name_only) | ||
590 | { | ||
591 | struct perf_pmu *pmu; | ||
592 | struct perf_pmu_alias *alias; | ||
593 | char buf[1024]; | ||
594 | int printed = 0; | ||
595 | int len, j; | ||
596 | char **aliases; | ||
597 | |||
598 | pmu = NULL; | ||
599 | len = 0; | ||
600 | while ((pmu = perf_pmu__scan(pmu)) != NULL) | ||
601 | list_for_each_entry(alias, &pmu->aliases, list) | ||
602 | len++; | ||
603 | aliases = malloc(sizeof(char *) * len); | ||
604 | if (!aliases) | ||
605 | return; | ||
606 | pmu = NULL; | ||
607 | j = 0; | ||
608 | while ((pmu = perf_pmu__scan(pmu)) != NULL) | ||
609 | list_for_each_entry(alias, &pmu->aliases, list) { | ||
610 | char *name = format_alias(buf, sizeof(buf), pmu, alias); | ||
611 | bool is_cpu = !strcmp(pmu->name, "cpu"); | ||
612 | |||
613 | if (event_glob != NULL && | ||
614 | !(strglobmatch(name, event_glob) || | ||
615 | (!is_cpu && strglobmatch(alias->name, | ||
616 | event_glob)))) | ||
617 | continue; | ||
618 | aliases[j] = name; | ||
619 | if (is_cpu && !name_only) | ||
620 | aliases[j] = format_alias_or(buf, sizeof(buf), | ||
621 | pmu, alias); | ||
622 | aliases[j] = strdup(aliases[j]); | ||
623 | j++; | ||
624 | } | ||
625 | len = j; | ||
626 | qsort(aliases, len, sizeof(char *), cmp_string); | ||
627 | for (j = 0; j < len; j++) { | ||
628 | if (name_only) { | ||
629 | printf("%s ", aliases[j]); | ||
630 | continue; | ||
631 | } | ||
632 | printf(" %-50s [Kernel PMU event]\n", aliases[j]); | ||
633 | free(aliases[j]); | ||
634 | printed++; | ||
635 | } | ||
636 | if (printed) | ||
637 | printf("\n"); | ||
638 | free(aliases); | ||
639 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 32fe55b659fa..6b2cbe2d4cc3 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <linux/bitops.h> | 4 | #include <linux/bitops.h> |
5 | #include <linux/perf_event.h> | 5 | #include <linux/perf_event.h> |
6 | #include <stdbool.h> | ||
6 | 7 | ||
7 | enum { | 8 | enum { |
8 | PERF_PMU_FORMAT_VALUE_CONFIG, | 9 | PERF_PMU_FORMAT_VALUE_CONFIG, |
@@ -21,7 +22,7 @@ struct perf_pmu { | |||
21 | struct list_head list; | 22 | struct list_head list; |
22 | }; | 23 | }; |
23 | 24 | ||
24 | struct perf_pmu *perf_pmu__find(char *name); | 25 | struct perf_pmu *perf_pmu__find(const char *name); |
25 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | 26 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, |
26 | struct list_head *head_terms); | 27 | struct list_head *head_terms); |
27 | int perf_pmu__config_terms(struct list_head *formats, | 28 | int perf_pmu__config_terms(struct list_head *formats, |
@@ -40,5 +41,7 @@ int perf_pmu__format_parse(char *dir, struct list_head *head); | |||
40 | 41 | ||
41 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | 42 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); |
42 | 43 | ||
44 | void print_pmu_events(const char *event_glob, bool name_only); | ||
45 | |||
43 | int perf_pmu__test(void); | 46 | int perf_pmu__test(void); |
44 | #endif /* __PMU_H */ | 47 | #endif /* __PMU_H */ |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index be0329394d56..f0692737ebf1 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -118,7 +118,6 @@ static const Dwfl_Callbacks offline_callbacks = { | |||
118 | static int debuginfo__init_offline_dwarf(struct debuginfo *self, | 118 | static int debuginfo__init_offline_dwarf(struct debuginfo *self, |
119 | const char *path) | 119 | const char *path) |
120 | { | 120 | { |
121 | Dwfl_Module *mod; | ||
122 | int fd; | 121 | int fd; |
123 | 122 | ||
124 | fd = open(path, O_RDONLY); | 123 | fd = open(path, O_RDONLY); |
@@ -129,11 +128,11 @@ static int debuginfo__init_offline_dwarf(struct debuginfo *self, | |||
129 | if (!self->dwfl) | 128 | if (!self->dwfl) |
130 | goto error; | 129 | goto error; |
131 | 130 | ||
132 | mod = dwfl_report_offline(self->dwfl, "", "", fd); | 131 | self->mod = dwfl_report_offline(self->dwfl, "", "", fd); |
133 | if (!mod) | 132 | if (!self->mod) |
134 | goto error; | 133 | goto error; |
135 | 134 | ||
136 | self->dbg = dwfl_module_getdwarf(mod, &self->bias); | 135 | self->dbg = dwfl_module_getdwarf(self->mod, &self->bias); |
137 | if (!self->dbg) | 136 | if (!self->dbg) |
138 | goto error; | 137 | goto error; |
139 | 138 | ||
@@ -676,37 +675,42 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
676 | } | 675 | } |
677 | 676 | ||
678 | /* Convert subprogram DIE to trace point */ | 677 | /* Convert subprogram DIE to trace point */ |
679 | static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, | 678 | static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, |
680 | bool retprobe, struct probe_trace_point *tp) | 679 | Dwarf_Addr paddr, bool retprobe, |
680 | struct probe_trace_point *tp) | ||
681 | { | 681 | { |
682 | Dwarf_Addr eaddr, highaddr; | 682 | Dwarf_Addr eaddr, highaddr; |
683 | const char *name; | 683 | GElf_Sym sym; |
684 | 684 | const char *symbol; | |
685 | /* Copy the name of probe point */ | 685 | |
686 | name = dwarf_diename(sp_die); | 686 | /* Verify the address is correct */ |
687 | if (name) { | 687 | if (dwarf_entrypc(sp_die, &eaddr) != 0) { |
688 | if (dwarf_entrypc(sp_die, &eaddr) != 0) { | 688 | pr_warning("Failed to get entry address of %s\n", |
689 | pr_warning("Failed to get entry address of %s\n", | 689 | dwarf_diename(sp_die)); |
690 | dwarf_diename(sp_die)); | 690 | return -ENOENT; |
691 | return -ENOENT; | 691 | } |
692 | } | 692 | if (dwarf_highpc(sp_die, &highaddr) != 0) { |
693 | if (dwarf_highpc(sp_die, &highaddr) != 0) { | 693 | pr_warning("Failed to get end address of %s\n", |
694 | pr_warning("Failed to get end address of %s\n", | 694 | dwarf_diename(sp_die)); |
695 | dwarf_diename(sp_die)); | 695 | return -ENOENT; |
696 | return -ENOENT; | 696 | } |
697 | } | 697 | if (paddr > highaddr) { |
698 | if (paddr > highaddr) { | 698 | pr_warning("Offset specified is greater than size of %s\n", |
699 | pr_warning("Offset specified is greater than size of %s\n", | 699 | dwarf_diename(sp_die)); |
700 | dwarf_diename(sp_die)); | 700 | return -EINVAL; |
701 | return -EINVAL; | 701 | } |
702 | } | 702 | |
703 | tp->symbol = strdup(name); | 703 | /* Get an appropriate symbol from symtab */ |
704 | if (tp->symbol == NULL) | 704 | symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL); |
705 | return -ENOMEM; | 705 | if (!symbol) { |
706 | tp->offset = (unsigned long)(paddr - eaddr); | 706 | pr_warning("Failed to find symbol at 0x%lx\n", |
707 | } else | 707 | (unsigned long)paddr); |
708 | /* This function has no name. */ | 708 | return -ENOENT; |
709 | tp->offset = (unsigned long)paddr; | 709 | } |
710 | tp->offset = (unsigned long)(paddr - sym.st_value); | ||
711 | tp->symbol = strdup(symbol); | ||
712 | if (!tp->symbol) | ||
713 | return -ENOMEM; | ||
710 | 714 | ||
711 | /* Return probe must be on the head of a subprogram */ | 715 | /* Return probe must be on the head of a subprogram */ |
712 | if (retprobe) { | 716 | if (retprobe) { |
@@ -734,7 +738,7 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
734 | } | 738 | } |
735 | 739 | ||
736 | /* If not a real subprogram, find a real one */ | 740 | /* If not a real subprogram, find a real one */ |
737 | if (dwarf_tag(sc_die) != DW_TAG_subprogram) { | 741 | if (!die_is_func_def(sc_die)) { |
738 | if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { | 742 | if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) { |
739 | pr_warning("Failed to find probe point in any " | 743 | pr_warning("Failed to find probe point in any " |
740 | "functions.\n"); | 744 | "functions.\n"); |
@@ -980,12 +984,10 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
980 | struct dwarf_callback_param *param = data; | 984 | struct dwarf_callback_param *param = data; |
981 | struct probe_finder *pf = param->data; | 985 | struct probe_finder *pf = param->data; |
982 | struct perf_probe_point *pp = &pf->pev->point; | 986 | struct perf_probe_point *pp = &pf->pev->point; |
983 | Dwarf_Attribute attr; | ||
984 | 987 | ||
985 | /* Check tag and diename */ | 988 | /* Check tag and diename */ |
986 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | 989 | if (!die_is_func_def(sp_die) || |
987 | !die_compare_name(sp_die, pp->function) || | 990 | !die_compare_name(sp_die, pp->function)) |
988 | dwarf_attr(sp_die, DW_AT_declaration, &attr)) | ||
989 | return DWARF_CB_OK; | 991 | return DWARF_CB_OK; |
990 | 992 | ||
991 | /* Check declared file */ | 993 | /* Check declared file */ |
@@ -1151,7 +1153,7 @@ static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
1151 | tev = &tf->tevs[tf->ntevs++]; | 1153 | tev = &tf->tevs[tf->ntevs++]; |
1152 | 1154 | ||
1153 | /* Trace point should be converted from subprogram DIE */ | 1155 | /* Trace point should be converted from subprogram DIE */ |
1154 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, | 1156 | ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr, |
1155 | pf->pev->point.retprobe, &tev->point); | 1157 | pf->pev->point.retprobe, &tev->point); |
1156 | if (ret < 0) | 1158 | if (ret < 0) |
1157 | return ret; | 1159 | return ret; |
@@ -1183,7 +1185,7 @@ int debuginfo__find_trace_events(struct debuginfo *self, | |||
1183 | { | 1185 | { |
1184 | struct trace_event_finder tf = { | 1186 | struct trace_event_finder tf = { |
1185 | .pf = {.pev = pev, .callback = add_probe_trace_event}, | 1187 | .pf = {.pev = pev, .callback = add_probe_trace_event}, |
1186 | .max_tevs = max_tevs}; | 1188 | .mod = self->mod, .max_tevs = max_tevs}; |
1187 | int ret; | 1189 | int ret; |
1188 | 1190 | ||
1189 | /* Allocate result tevs array */ | 1191 | /* Allocate result tevs array */ |
@@ -1252,7 +1254,7 @@ static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
1252 | vl = &af->vls[af->nvls++]; | 1254 | vl = &af->vls[af->nvls++]; |
1253 | 1255 | ||
1254 | /* Trace point should be converted from subprogram DIE */ | 1256 | /* Trace point should be converted from subprogram DIE */ |
1255 | ret = convert_to_trace_point(&pf->sp_die, pf->addr, | 1257 | ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr, |
1256 | pf->pev->point.retprobe, &vl->point); | 1258 | pf->pev->point.retprobe, &vl->point); |
1257 | if (ret < 0) | 1259 | if (ret < 0) |
1258 | return ret; | 1260 | return ret; |
@@ -1291,6 +1293,7 @@ int debuginfo__find_available_vars_at(struct debuginfo *self, | |||
1291 | { | 1293 | { |
1292 | struct available_var_finder af = { | 1294 | struct available_var_finder af = { |
1293 | .pf = {.pev = pev, .callback = add_available_vars}, | 1295 | .pf = {.pev = pev, .callback = add_available_vars}, |
1296 | .mod = self->mod, | ||
1294 | .max_vls = max_vls, .externs = externs}; | 1297 | .max_vls = max_vls, .externs = externs}; |
1295 | int ret; | 1298 | int ret; |
1296 | 1299 | ||
@@ -1324,8 +1327,8 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, | |||
1324 | struct perf_probe_point *ppt) | 1327 | struct perf_probe_point *ppt) |
1325 | { | 1328 | { |
1326 | Dwarf_Die cudie, spdie, indie; | 1329 | Dwarf_Die cudie, spdie, indie; |
1327 | Dwarf_Addr _addr, baseaddr; | 1330 | Dwarf_Addr _addr = 0, baseaddr = 0; |
1328 | const char *fname = NULL, *func = NULL, *tmp; | 1331 | const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp; |
1329 | int baseline = 0, lineno = 0, ret = 0; | 1332 | int baseline = 0, lineno = 0, ret = 0; |
1330 | 1333 | ||
1331 | /* Adjust address with bias */ | 1334 | /* Adjust address with bias */ |
@@ -1346,27 +1349,36 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, | |||
1346 | /* Find a corresponding function (name, baseline and baseaddr) */ | 1349 | /* Find a corresponding function (name, baseline and baseaddr) */ |
1347 | if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { | 1350 | if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) { |
1348 | /* Get function entry information */ | 1351 | /* Get function entry information */ |
1349 | tmp = dwarf_diename(&spdie); | 1352 | func = basefunc = dwarf_diename(&spdie); |
1350 | if (!tmp || | 1353 | if (!func || |
1351 | dwarf_entrypc(&spdie, &baseaddr) != 0 || | 1354 | dwarf_entrypc(&spdie, &baseaddr) != 0 || |
1352 | dwarf_decl_line(&spdie, &baseline) != 0) | 1355 | dwarf_decl_line(&spdie, &baseline) != 0) { |
1356 | lineno = 0; | ||
1353 | goto post; | 1357 | goto post; |
1354 | func = tmp; | 1358 | } |
1355 | 1359 | ||
1356 | if (addr == (unsigned long)baseaddr) | 1360 | fname = dwarf_decl_file(&spdie); |
1361 | if (addr == (unsigned long)baseaddr) { | ||
1357 | /* Function entry - Relative line number is 0 */ | 1362 | /* Function entry - Relative line number is 0 */ |
1358 | lineno = baseline; | 1363 | lineno = baseline; |
1359 | else if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, | 1364 | goto post; |
1360 | &indie)) { | 1365 | } |
1366 | |||
1367 | /* Track down the inline functions step by step */ | ||
1368 | while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr, | ||
1369 | &indie)) { | ||
1370 | /* There is an inline function */ | ||
1361 | if (dwarf_entrypc(&indie, &_addr) == 0 && | 1371 | if (dwarf_entrypc(&indie, &_addr) == 0 && |
1362 | _addr == addr) | 1372 | _addr == addr) { |
1363 | /* | 1373 | /* |
1364 | * addr is at an inline function entry. | 1374 | * addr is at an inline function entry. |
1365 | * In this case, lineno should be the call-site | 1375 | * In this case, lineno should be the call-site |
1366 | * line number. | 1376 | * line number. (overwrite lineinfo) |
1367 | */ | 1377 | */ |
1368 | lineno = die_get_call_lineno(&indie); | 1378 | lineno = die_get_call_lineno(&indie); |
1369 | else { | 1379 | fname = die_get_call_file(&indie); |
1380 | break; | ||
1381 | } else { | ||
1370 | /* | 1382 | /* |
1371 | * addr is in an inline function body. | 1383 | * addr is in an inline function body. |
1372 | * Since lineno points one of the lines | 1384 | * Since lineno points one of the lines |
@@ -1374,19 +1386,27 @@ int debuginfo__find_probe_point(struct debuginfo *self, unsigned long addr, | |||
1374 | * be the entry line of the inline function. | 1386 | * be the entry line of the inline function. |
1375 | */ | 1387 | */ |
1376 | tmp = dwarf_diename(&indie); | 1388 | tmp = dwarf_diename(&indie); |
1377 | if (tmp && | 1389 | if (!tmp || |
1378 | dwarf_decl_line(&spdie, &baseline) == 0) | 1390 | dwarf_decl_line(&indie, &baseline) != 0) |
1379 | func = tmp; | 1391 | break; |
1392 | func = tmp; | ||
1393 | spdie = indie; | ||
1380 | } | 1394 | } |
1381 | } | 1395 | } |
1396 | /* Verify the lineno and baseline are in a same file */ | ||
1397 | tmp = dwarf_decl_file(&spdie); | ||
1398 | if (!tmp || strcmp(tmp, fname) != 0) | ||
1399 | lineno = 0; | ||
1382 | } | 1400 | } |
1383 | 1401 | ||
1384 | post: | 1402 | post: |
1385 | /* Make a relative line number or an offset */ | 1403 | /* Make a relative line number or an offset */ |
1386 | if (lineno) | 1404 | if (lineno) |
1387 | ppt->line = lineno - baseline; | 1405 | ppt->line = lineno - baseline; |
1388 | else if (func) | 1406 | else if (basefunc) { |
1389 | ppt->offset = addr - (unsigned long)baseaddr; | 1407 | ppt->offset = addr - (unsigned long)baseaddr; |
1408 | func = basefunc; | ||
1409 | } | ||
1390 | 1410 | ||
1391 | /* Duplicate strings */ | 1411 | /* Duplicate strings */ |
1392 | if (func) { | 1412 | if (func) { |
@@ -1474,7 +1494,7 @@ static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | |||
1474 | return 0; | 1494 | return 0; |
1475 | } | 1495 | } |
1476 | 1496 | ||
1477 | /* Search function from function name */ | 1497 | /* Search function definition from function name */ |
1478 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | 1498 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) |
1479 | { | 1499 | { |
1480 | struct dwarf_callback_param *param = data; | 1500 | struct dwarf_callback_param *param = data; |
@@ -1485,7 +1505,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
1485 | if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die))) | 1505 | if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die))) |
1486 | return DWARF_CB_OK; | 1506 | return DWARF_CB_OK; |
1487 | 1507 | ||
1488 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1508 | if (die_is_func_def(sp_die) && |
1489 | die_compare_name(sp_die, lr->function)) { | 1509 | die_compare_name(sp_die, lr->function)) { |
1490 | lf->fname = dwarf_decl_file(sp_die); | 1510 | lf->fname = dwarf_decl_file(sp_die); |
1491 | dwarf_decl_line(sp_die, &lr->offset); | 1511 | dwarf_decl_line(sp_die, &lr->offset); |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 17e94d0c36f9..3b7d63018960 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -23,6 +23,7 @@ static inline int is_c_varname(const char *name) | |||
23 | /* debug information structure */ | 23 | /* debug information structure */ |
24 | struct debuginfo { | 24 | struct debuginfo { |
25 | Dwarf *dbg; | 25 | Dwarf *dbg; |
26 | Dwfl_Module *mod; | ||
26 | Dwfl *dwfl; | 27 | Dwfl *dwfl; |
27 | Dwarf_Addr bias; | 28 | Dwarf_Addr bias; |
28 | }; | 29 | }; |
@@ -77,6 +78,7 @@ struct probe_finder { | |||
77 | 78 | ||
78 | struct trace_event_finder { | 79 | struct trace_event_finder { |
79 | struct probe_finder pf; | 80 | struct probe_finder pf; |
81 | Dwfl_Module *mod; /* For solving symbols */ | ||
80 | struct probe_trace_event *tevs; /* Found trace events */ | 82 | struct probe_trace_event *tevs; /* Found trace events */ |
81 | int ntevs; /* Number of trace events */ | 83 | int ntevs; /* Number of trace events */ |
82 | int max_tevs; /* Max number of trace events */ | 84 | int max_tevs; /* Max number of trace events */ |
@@ -84,6 +86,7 @@ struct trace_event_finder { | |||
84 | 86 | ||
85 | struct available_var_finder { | 87 | struct available_var_finder { |
86 | struct probe_finder pf; | 88 | struct probe_finder pf; |
89 | Dwfl_Module *mod; /* For solving symbols */ | ||
87 | struct variable_list *vls; /* Found variable lists */ | 90 | struct variable_list *vls; /* Found variable lists */ |
88 | int nvls; /* Number of variable lists */ | 91 | int nvls; /* Number of variable lists */ |
89 | int max_vls; /* Max no. of variable lists */ | 92 | int max_vls; /* Max no. of variable lists */ |
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 925e0c3e6d91..2ac4bc92bb1f 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -8,6 +8,26 @@ | |||
8 | #include "cpumap.h" | 8 | #include "cpumap.h" |
9 | #include "thread_map.h" | 9 | #include "thread_map.h" |
10 | 10 | ||
11 | /* | ||
12 | * Support debug printing even though util/debug.c is not linked. That means | ||
13 | * implementing 'verbose' and 'eprintf'. | ||
14 | */ | ||
15 | int verbose; | ||
16 | |||
17 | int eprintf(int level, const char *fmt, ...) | ||
18 | { | ||
19 | va_list args; | ||
20 | int ret = 0; | ||
21 | |||
22 | if (verbose >= level) { | ||
23 | va_start(args, fmt); | ||
24 | ret = vfprintf(stderr, fmt, args); | ||
25 | va_end(args); | ||
26 | } | ||
27 | |||
28 | return ret; | ||
29 | } | ||
30 | |||
11 | /* Define PyVarObject_HEAD_INIT for python 2.5 */ | 31 | /* Define PyVarObject_HEAD_INIT for python 2.5 */ |
12 | #ifndef PyVarObject_HEAD_INIT | 32 | #ifndef PyVarObject_HEAD_INIT |
13 | # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, | 33 | # define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, |
@@ -802,6 +822,8 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist, | |||
802 | PyObject *pyevent = pyrf_event__new(event); | 822 | PyObject *pyevent = pyrf_event__new(event); |
803 | struct pyrf_event *pevent = (struct pyrf_event *)pyevent; | 823 | struct pyrf_event *pevent = (struct pyrf_event *)pyevent; |
804 | 824 | ||
825 | perf_evlist__mmap_consume(evlist, cpu); | ||
826 | |||
805 | if (pyevent == NULL) | 827 | if (pyevent == NULL) |
806 | return PyErr_NoMemory(); | 828 | return PyErr_NoMemory(); |
807 | 829 | ||
@@ -967,6 +989,7 @@ static struct { | |||
967 | { "COUNT_SW_PAGE_FAULTS_MAJ", PERF_COUNT_SW_PAGE_FAULTS_MAJ }, | 989 | { "COUNT_SW_PAGE_FAULTS_MAJ", PERF_COUNT_SW_PAGE_FAULTS_MAJ }, |
968 | { "COUNT_SW_ALIGNMENT_FAULTS", PERF_COUNT_SW_ALIGNMENT_FAULTS }, | 990 | { "COUNT_SW_ALIGNMENT_FAULTS", PERF_COUNT_SW_ALIGNMENT_FAULTS }, |
969 | { "COUNT_SW_EMULATION_FAULTS", PERF_COUNT_SW_EMULATION_FAULTS }, | 991 | { "COUNT_SW_EMULATION_FAULTS", PERF_COUNT_SW_EMULATION_FAULTS }, |
992 | { "COUNT_SW_DUMMY", PERF_COUNT_SW_DUMMY }, | ||
970 | 993 | ||
971 | { "SAMPLE_IP", PERF_SAMPLE_IP }, | 994 | { "SAMPLE_IP", PERF_SAMPLE_IP }, |
972 | { "SAMPLE_TID", PERF_SAMPLE_TID }, | 995 | { "SAMPLE_TID", PERF_SAMPLE_TID }, |
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c new file mode 100644 index 000000000000..18d73aa2f0f8 --- /dev/null +++ b/tools/perf/util/record.c | |||
@@ -0,0 +1,108 @@ | |||
1 | #include "evlist.h" | ||
2 | #include "evsel.h" | ||
3 | #include "cpumap.h" | ||
4 | #include "parse-events.h" | ||
5 | |||
6 | typedef void (*setup_probe_fn_t)(struct perf_evsel *evsel); | ||
7 | |||
8 | static int perf_do_probe_api(setup_probe_fn_t fn, int cpu, const char *str) | ||
9 | { | ||
10 | struct perf_evlist *evlist; | ||
11 | struct perf_evsel *evsel; | ||
12 | int err = -EAGAIN, fd; | ||
13 | |||
14 | evlist = perf_evlist__new(); | ||
15 | if (!evlist) | ||
16 | return -ENOMEM; | ||
17 | |||
18 | if (parse_events(evlist, str)) | ||
19 | goto out_delete; | ||
20 | |||
21 | evsel = perf_evlist__first(evlist); | ||
22 | |||
23 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); | ||
24 | if (fd < 0) | ||
25 | goto out_delete; | ||
26 | close(fd); | ||
27 | |||
28 | fn(evsel); | ||
29 | |||
30 | fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); | ||
31 | if (fd < 0) { | ||
32 | if (errno == EINVAL) | ||
33 | err = -EINVAL; | ||
34 | goto out_delete; | ||
35 | } | ||
36 | close(fd); | ||
37 | err = 0; | ||
38 | |||
39 | out_delete: | ||
40 | perf_evlist__delete(evlist); | ||
41 | return err; | ||
42 | } | ||
43 | |||
44 | static bool perf_probe_api(setup_probe_fn_t fn) | ||
45 | { | ||
46 | const char *try[] = {"cycles:u", "instructions:u", "cpu-clock", NULL}; | ||
47 | struct cpu_map *cpus; | ||
48 | int cpu, ret, i = 0; | ||
49 | |||
50 | cpus = cpu_map__new(NULL); | ||
51 | if (!cpus) | ||
52 | return false; | ||
53 | cpu = cpus->map[0]; | ||
54 | cpu_map__delete(cpus); | ||
55 | |||
56 | do { | ||
57 | ret = perf_do_probe_api(fn, cpu, try[i++]); | ||
58 | if (!ret) | ||
59 | return true; | ||
60 | } while (ret == -EAGAIN && try[i]); | ||
61 | |||
62 | return false; | ||
63 | } | ||
64 | |||
65 | static void perf_probe_sample_identifier(struct perf_evsel *evsel) | ||
66 | { | ||
67 | evsel->attr.sample_type |= PERF_SAMPLE_IDENTIFIER; | ||
68 | } | ||
69 | |||
70 | bool perf_can_sample_identifier(void) | ||
71 | { | ||
72 | return perf_probe_api(perf_probe_sample_identifier); | ||
73 | } | ||
74 | |||
75 | void perf_evlist__config(struct perf_evlist *evlist, | ||
76 | struct perf_record_opts *opts) | ||
77 | { | ||
78 | struct perf_evsel *evsel; | ||
79 | bool use_sample_identifier = false; | ||
80 | |||
81 | /* | ||
82 | * Set the evsel leader links before we configure attributes, | ||
83 | * since some might depend on this info. | ||
84 | */ | ||
85 | if (opts->group) | ||
86 | perf_evlist__set_leader(evlist); | ||
87 | |||
88 | if (evlist->cpus->map[0] < 0) | ||
89 | opts->no_inherit = true; | ||
90 | |||
91 | list_for_each_entry(evsel, &evlist->entries, node) | ||
92 | perf_evsel__config(evsel, opts); | ||
93 | |||
94 | if (evlist->nr_entries > 1) { | ||
95 | struct perf_evsel *first = perf_evlist__first(evlist); | ||
96 | |||
97 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
98 | if (evsel->attr.sample_type == first->attr.sample_type) | ||
99 | continue; | ||
100 | use_sample_identifier = perf_can_sample_identifier(); | ||
101 | break; | ||
102 | } | ||
103 | list_for_each_entry(evsel, &evlist->entries, node) | ||
104 | perf_evsel__set_sample_id(evsel, use_sample_identifier); | ||
105 | } | ||
106 | |||
107 | perf_evlist__set_id_pos(evlist); | ||
108 | } | ||
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index eacec859f299..c0c9795c4f02 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
@@ -261,7 +261,8 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, | |||
261 | struct perf_sample *sample, | 261 | struct perf_sample *sample, |
262 | struct perf_evsel *evsel, | 262 | struct perf_evsel *evsel, |
263 | struct machine *machine __maybe_unused, | 263 | struct machine *machine __maybe_unused, |
264 | struct addr_location *al) | 264 | struct thread *thread, |
265 | struct addr_location *al) | ||
265 | { | 266 | { |
266 | struct format_field *field; | 267 | struct format_field *field; |
267 | static char handler[256]; | 268 | static char handler[256]; |
@@ -272,7 +273,6 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, | |||
272 | int cpu = sample->cpu; | 273 | int cpu = sample->cpu; |
273 | void *data = sample->raw_data; | 274 | void *data = sample->raw_data; |
274 | unsigned long long nsecs = sample->time; | 275 | unsigned long long nsecs = sample->time; |
275 | struct thread *thread = al->thread; | ||
276 | char *comm = thread->comm; | 276 | char *comm = thread->comm; |
277 | 277 | ||
278 | dSP; | 278 | dSP; |
@@ -282,7 +282,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, | |||
282 | 282 | ||
283 | event = find_cache_event(evsel); | 283 | event = find_cache_event(evsel); |
284 | if (!event) | 284 | if (!event) |
285 | die("ug! no event found for type %" PRIu64, evsel->attr.config); | 285 | die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); |
286 | 286 | ||
287 | pid = raw_field_value(event, "common_pid", data); | 287 | pid = raw_field_value(event, "common_pid", data); |
288 | 288 | ||
@@ -351,7 +351,8 @@ static void perl_process_event_generic(union perf_event *event, | |||
351 | struct perf_sample *sample, | 351 | struct perf_sample *sample, |
352 | struct perf_evsel *evsel, | 352 | struct perf_evsel *evsel, |
353 | struct machine *machine __maybe_unused, | 353 | struct machine *machine __maybe_unused, |
354 | struct addr_location *al __maybe_unused) | 354 | struct thread *thread __maybe_unused, |
355 | struct addr_location *al __maybe_unused) | ||
355 | { | 356 | { |
356 | dSP; | 357 | dSP; |
357 | 358 | ||
@@ -377,10 +378,11 @@ static void perl_process_event(union perf_event *event, | |||
377 | struct perf_sample *sample, | 378 | struct perf_sample *sample, |
378 | struct perf_evsel *evsel, | 379 | struct perf_evsel *evsel, |
379 | struct machine *machine, | 380 | struct machine *machine, |
380 | struct addr_location *al) | 381 | struct thread *thread, |
382 | struct addr_location *al) | ||
381 | { | 383 | { |
382 | perl_process_tracepoint(event, sample, evsel, machine, al); | 384 | perl_process_tracepoint(event, sample, evsel, machine, thread, al); |
383 | perl_process_event_generic(event, sample, evsel, machine, al); | 385 | perl_process_event_generic(event, sample, evsel, machine, thread, al); |
384 | } | 386 | } |
385 | 387 | ||
386 | static void run_start_sub(void) | 388 | static void run_start_sub(void) |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index e87aa5d9696b..95d91a0b23af 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -56,6 +56,17 @@ static void handler_call_die(const char *handler_name) | |||
56 | Py_FatalError("problem in Python trace event handler"); | 56 | Py_FatalError("problem in Python trace event handler"); |
57 | } | 57 | } |
58 | 58 | ||
59 | /* | ||
60 | * Insert val into into the dictionary and decrement the reference counter. | ||
61 | * This is necessary for dictionaries since PyDict_SetItemString() does not | ||
62 | * steal a reference, as opposed to PyTuple_SetItem(). | ||
63 | */ | ||
64 | static void pydict_set_item_string_decref(PyObject *dict, const char *key, PyObject *val) | ||
65 | { | ||
66 | PyDict_SetItemString(dict, key, val); | ||
67 | Py_DECREF(val); | ||
68 | } | ||
69 | |||
59 | static void define_value(enum print_arg_type field_type, | 70 | static void define_value(enum print_arg_type field_type, |
60 | const char *ev_name, | 71 | const char *ev_name, |
61 | const char *field_name, | 72 | const char *field_name, |
@@ -225,6 +236,7 @@ static void python_process_tracepoint(union perf_event *perf_event | |||
225 | struct perf_sample *sample, | 236 | struct perf_sample *sample, |
226 | struct perf_evsel *evsel, | 237 | struct perf_evsel *evsel, |
227 | struct machine *machine __maybe_unused, | 238 | struct machine *machine __maybe_unused, |
239 | struct thread *thread, | ||
228 | struct addr_location *al) | 240 | struct addr_location *al) |
229 | { | 241 | { |
230 | PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; | 242 | PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; |
@@ -238,7 +250,6 @@ static void python_process_tracepoint(union perf_event *perf_event | |||
238 | int cpu = sample->cpu; | 250 | int cpu = sample->cpu; |
239 | void *data = sample->raw_data; | 251 | void *data = sample->raw_data; |
240 | unsigned long long nsecs = sample->time; | 252 | unsigned long long nsecs = sample->time; |
241 | struct thread *thread = al->thread; | ||
242 | char *comm = thread->comm; | 253 | char *comm = thread->comm; |
243 | 254 | ||
244 | t = PyTuple_New(MAX_FIELDS); | 255 | t = PyTuple_New(MAX_FIELDS); |
@@ -279,11 +290,11 @@ static void python_process_tracepoint(union perf_event *perf_event | |||
279 | PyTuple_SetItem(t, n++, PyInt_FromLong(pid)); | 290 | PyTuple_SetItem(t, n++, PyInt_FromLong(pid)); |
280 | PyTuple_SetItem(t, n++, PyString_FromString(comm)); | 291 | PyTuple_SetItem(t, n++, PyString_FromString(comm)); |
281 | } else { | 292 | } else { |
282 | PyDict_SetItemString(dict, "common_cpu", PyInt_FromLong(cpu)); | 293 | pydict_set_item_string_decref(dict, "common_cpu", PyInt_FromLong(cpu)); |
283 | PyDict_SetItemString(dict, "common_s", PyInt_FromLong(s)); | 294 | pydict_set_item_string_decref(dict, "common_s", PyInt_FromLong(s)); |
284 | PyDict_SetItemString(dict, "common_ns", PyInt_FromLong(ns)); | 295 | pydict_set_item_string_decref(dict, "common_ns", PyInt_FromLong(ns)); |
285 | PyDict_SetItemString(dict, "common_pid", PyInt_FromLong(pid)); | 296 | pydict_set_item_string_decref(dict, "common_pid", PyInt_FromLong(pid)); |
286 | PyDict_SetItemString(dict, "common_comm", PyString_FromString(comm)); | 297 | pydict_set_item_string_decref(dict, "common_comm", PyString_FromString(comm)); |
287 | } | 298 | } |
288 | for (field = event->format.fields; field; field = field->next) { | 299 | for (field = event->format.fields; field; field = field->next) { |
289 | if (field->flags & FIELD_IS_STRING) { | 300 | if (field->flags & FIELD_IS_STRING) { |
@@ -313,7 +324,7 @@ static void python_process_tracepoint(union perf_event *perf_event | |||
313 | if (handler) | 324 | if (handler) |
314 | PyTuple_SetItem(t, n++, obj); | 325 | PyTuple_SetItem(t, n++, obj); |
315 | else | 326 | else |
316 | PyDict_SetItemString(dict, field->name, obj); | 327 | pydict_set_item_string_decref(dict, field->name, obj); |
317 | 328 | ||
318 | } | 329 | } |
319 | if (!handler) | 330 | if (!handler) |
@@ -345,12 +356,12 @@ static void python_process_general_event(union perf_event *perf_event | |||
345 | struct perf_sample *sample, | 356 | struct perf_sample *sample, |
346 | struct perf_evsel *evsel, | 357 | struct perf_evsel *evsel, |
347 | struct machine *machine __maybe_unused, | 358 | struct machine *machine __maybe_unused, |
359 | struct thread *thread, | ||
348 | struct addr_location *al) | 360 | struct addr_location *al) |
349 | { | 361 | { |
350 | PyObject *handler, *retval, *t, *dict; | 362 | PyObject *handler, *retval, *t, *dict; |
351 | static char handler_name[64]; | 363 | static char handler_name[64]; |
352 | unsigned n = 0; | 364 | unsigned n = 0; |
353 | struct thread *thread = al->thread; | ||
354 | 365 | ||
355 | /* | 366 | /* |
356 | * Use the MAX_FIELDS to make the function expandable, though | 367 | * Use the MAX_FIELDS to make the function expandable, though |
@@ -370,21 +381,21 @@ static void python_process_general_event(union perf_event *perf_event | |||
370 | if (!handler || !PyCallable_Check(handler)) | 381 | if (!handler || !PyCallable_Check(handler)) |
371 | goto exit; | 382 | goto exit; |
372 | 383 | ||
373 | PyDict_SetItemString(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); | 384 | pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); |
374 | PyDict_SetItemString(dict, "attr", PyString_FromStringAndSize( | 385 | pydict_set_item_string_decref(dict, "attr", PyString_FromStringAndSize( |
375 | (const char *)&evsel->attr, sizeof(evsel->attr))); | 386 | (const char *)&evsel->attr, sizeof(evsel->attr))); |
376 | PyDict_SetItemString(dict, "sample", PyString_FromStringAndSize( | 387 | pydict_set_item_string_decref(dict, "sample", PyString_FromStringAndSize( |
377 | (const char *)sample, sizeof(*sample))); | 388 | (const char *)sample, sizeof(*sample))); |
378 | PyDict_SetItemString(dict, "raw_buf", PyString_FromStringAndSize( | 389 | pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize( |
379 | (const char *)sample->raw_data, sample->raw_size)); | 390 | (const char *)sample->raw_data, sample->raw_size)); |
380 | PyDict_SetItemString(dict, "comm", | 391 | pydict_set_item_string_decref(dict, "comm", |
381 | PyString_FromString(thread->comm)); | 392 | PyString_FromString(thread->comm)); |
382 | if (al->map) { | 393 | if (al->map) { |
383 | PyDict_SetItemString(dict, "dso", | 394 | pydict_set_item_string_decref(dict, "dso", |
384 | PyString_FromString(al->map->dso->name)); | 395 | PyString_FromString(al->map->dso->name)); |
385 | } | 396 | } |
386 | if (al->sym) { | 397 | if (al->sym) { |
387 | PyDict_SetItemString(dict, "symbol", | 398 | pydict_set_item_string_decref(dict, "symbol", |
388 | PyString_FromString(al->sym->name)); | 399 | PyString_FromString(al->sym->name)); |
389 | } | 400 | } |
390 | 401 | ||
@@ -404,17 +415,18 @@ static void python_process_event(union perf_event *perf_event, | |||
404 | struct perf_sample *sample, | 415 | struct perf_sample *sample, |
405 | struct perf_evsel *evsel, | 416 | struct perf_evsel *evsel, |
406 | struct machine *machine, | 417 | struct machine *machine, |
418 | struct thread *thread, | ||
407 | struct addr_location *al) | 419 | struct addr_location *al) |
408 | { | 420 | { |
409 | switch (evsel->attr.type) { | 421 | switch (evsel->attr.type) { |
410 | case PERF_TYPE_TRACEPOINT: | 422 | case PERF_TYPE_TRACEPOINT: |
411 | python_process_tracepoint(perf_event, sample, evsel, | 423 | python_process_tracepoint(perf_event, sample, evsel, |
412 | machine, al); | 424 | machine, thread, al); |
413 | break; | 425 | break; |
414 | /* Reserve for future process_hw/sw/raw APIs */ | 426 | /* Reserve for future process_hw/sw/raw APIs */ |
415 | default: | 427 | default: |
416 | python_process_general_event(perf_event, sample, evsel, | 428 | python_process_general_event(perf_event, sample, evsel, |
417 | machine, al); | 429 | machine, thread, al); |
418 | } | 430 | } |
419 | } | 431 | } |
420 | 432 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index cf1fe01b7e89..568b750c01f6 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include <linux/kernel.h> | 1 | #include <linux/kernel.h> |
2 | #include <traceevent/event-parse.h> | ||
2 | 3 | ||
3 | #include <byteswap.h> | 4 | #include <byteswap.h> |
4 | #include <unistd.h> | 5 | #include <unistd.h> |
@@ -12,7 +13,6 @@ | |||
12 | #include "sort.h" | 13 | #include "sort.h" |
13 | #include "util.h" | 14 | #include "util.h" |
14 | #include "cpumap.h" | 15 | #include "cpumap.h" |
15 | #include "event-parse.h" | ||
16 | #include "perf_regs.h" | 16 | #include "perf_regs.h" |
17 | #include "vdso.h" | 17 | #include "vdso.h" |
18 | 18 | ||
@@ -24,7 +24,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
24 | self->fd_pipe = true; | 24 | self->fd_pipe = true; |
25 | self->fd = STDIN_FILENO; | 25 | self->fd = STDIN_FILENO; |
26 | 26 | ||
27 | if (perf_session__read_header(self, self->fd) < 0) | 27 | if (perf_session__read_header(self) < 0) |
28 | pr_err("incompatible file format (rerun with -v to learn more)"); | 28 | pr_err("incompatible file format (rerun with -v to learn more)"); |
29 | 29 | ||
30 | return 0; | 30 | return 0; |
@@ -56,7 +56,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
56 | goto out_close; | 56 | goto out_close; |
57 | } | 57 | } |
58 | 58 | ||
59 | if (perf_session__read_header(self, self->fd) < 0) { | 59 | if (perf_session__read_header(self) < 0) { |
60 | pr_err("incompatible file format (rerun with -v to learn more)"); | 60 | pr_err("incompatible file format (rerun with -v to learn more)"); |
61 | goto out_close; | 61 | goto out_close; |
62 | } | 62 | } |
@@ -71,6 +71,11 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
71 | goto out_close; | 71 | goto out_close; |
72 | } | 72 | } |
73 | 73 | ||
74 | if (!perf_evlist__valid_read_format(self->evlist)) { | ||
75 | pr_err("non matching read_format"); | ||
76 | goto out_close; | ||
77 | } | ||
78 | |||
74 | self->size = input_stat.st_size; | 79 | self->size = input_stat.st_size; |
75 | return 0; | 80 | return 0; |
76 | 81 | ||
@@ -193,7 +198,9 @@ void perf_session__delete(struct perf_session *self) | |||
193 | vdso__exit(); | 198 | vdso__exit(); |
194 | } | 199 | } |
195 | 200 | ||
196 | static int process_event_synth_tracing_data_stub(union perf_event *event | 201 | static int process_event_synth_tracing_data_stub(struct perf_tool *tool |
202 | __maybe_unused, | ||
203 | union perf_event *event | ||
197 | __maybe_unused, | 204 | __maybe_unused, |
198 | struct perf_session *session | 205 | struct perf_session *session |
199 | __maybe_unused) | 206 | __maybe_unused) |
@@ -202,7 +209,8 @@ static int process_event_synth_tracing_data_stub(union perf_event *event | |||
202 | return 0; | 209 | return 0; |
203 | } | 210 | } |
204 | 211 | ||
205 | static int process_event_synth_attr_stub(union perf_event *event __maybe_unused, | 212 | static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused, |
213 | union perf_event *event __maybe_unused, | ||
206 | struct perf_evlist **pevlist | 214 | struct perf_evlist **pevlist |
207 | __maybe_unused) | 215 | __maybe_unused) |
208 | { | 216 | { |
@@ -238,23 +246,18 @@ static int process_finished_round_stub(struct perf_tool *tool __maybe_unused, | |||
238 | return 0; | 246 | return 0; |
239 | } | 247 | } |
240 | 248 | ||
241 | static int process_event_type_stub(struct perf_tool *tool __maybe_unused, | ||
242 | union perf_event *event __maybe_unused) | ||
243 | { | ||
244 | dump_printf(": unhandled!\n"); | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | static int process_finished_round(struct perf_tool *tool, | 249 | static int process_finished_round(struct perf_tool *tool, |
249 | union perf_event *event, | 250 | union perf_event *event, |
250 | struct perf_session *session); | 251 | struct perf_session *session); |
251 | 252 | ||
252 | static void perf_tool__fill_defaults(struct perf_tool *tool) | 253 | void perf_tool__fill_defaults(struct perf_tool *tool) |
253 | { | 254 | { |
254 | if (tool->sample == NULL) | 255 | if (tool->sample == NULL) |
255 | tool->sample = process_event_sample_stub; | 256 | tool->sample = process_event_sample_stub; |
256 | if (tool->mmap == NULL) | 257 | if (tool->mmap == NULL) |
257 | tool->mmap = process_event_stub; | 258 | tool->mmap = process_event_stub; |
259 | if (tool->mmap2 == NULL) | ||
260 | tool->mmap2 = process_event_stub; | ||
258 | if (tool->comm == NULL) | 261 | if (tool->comm == NULL) |
259 | tool->comm = process_event_stub; | 262 | tool->comm = process_event_stub; |
260 | if (tool->fork == NULL) | 263 | if (tool->fork == NULL) |
@@ -271,8 +274,6 @@ static void perf_tool__fill_defaults(struct perf_tool *tool) | |||
271 | tool->unthrottle = process_event_stub; | 274 | tool->unthrottle = process_event_stub; |
272 | if (tool->attr == NULL) | 275 | if (tool->attr == NULL) |
273 | tool->attr = process_event_synth_attr_stub; | 276 | tool->attr = process_event_synth_attr_stub; |
274 | if (tool->event_type == NULL) | ||
275 | tool->event_type = process_event_type_stub; | ||
276 | if (tool->tracing_data == NULL) | 277 | if (tool->tracing_data == NULL) |
277 | tool->tracing_data = process_event_synth_tracing_data_stub; | 278 | tool->tracing_data = process_event_synth_tracing_data_stub; |
278 | if (tool->build_id == NULL) | 279 | if (tool->build_id == NULL) |
@@ -352,6 +353,25 @@ static void perf_event__mmap_swap(union perf_event *event, | |||
352 | } | 353 | } |
353 | } | 354 | } |
354 | 355 | ||
356 | static void perf_event__mmap2_swap(union perf_event *event, | ||
357 | bool sample_id_all) | ||
358 | { | ||
359 | event->mmap2.pid = bswap_32(event->mmap2.pid); | ||
360 | event->mmap2.tid = bswap_32(event->mmap2.tid); | ||
361 | event->mmap2.start = bswap_64(event->mmap2.start); | ||
362 | event->mmap2.len = bswap_64(event->mmap2.len); | ||
363 | event->mmap2.pgoff = bswap_64(event->mmap2.pgoff); | ||
364 | event->mmap2.maj = bswap_32(event->mmap2.maj); | ||
365 | event->mmap2.min = bswap_32(event->mmap2.min); | ||
366 | event->mmap2.ino = bswap_64(event->mmap2.ino); | ||
367 | |||
368 | if (sample_id_all) { | ||
369 | void *data = &event->mmap2.filename; | ||
370 | |||
371 | data += PERF_ALIGN(strlen(data) + 1, sizeof(u64)); | ||
372 | swap_sample_id_all(event, data); | ||
373 | } | ||
374 | } | ||
355 | static void perf_event__task_swap(union perf_event *event, bool sample_id_all) | 375 | static void perf_event__task_swap(union perf_event *event, bool sample_id_all) |
356 | { | 376 | { |
357 | event->fork.pid = bswap_32(event->fork.pid); | 377 | event->fork.pid = bswap_32(event->fork.pid); |
@@ -456,6 +476,7 @@ typedef void (*perf_event__swap_op)(union perf_event *event, | |||
456 | 476 | ||
457 | static perf_event__swap_op perf_event__swap_ops[] = { | 477 | static perf_event__swap_op perf_event__swap_ops[] = { |
458 | [PERF_RECORD_MMAP] = perf_event__mmap_swap, | 478 | [PERF_RECORD_MMAP] = perf_event__mmap_swap, |
479 | [PERF_RECORD_MMAP2] = perf_event__mmap2_swap, | ||
459 | [PERF_RECORD_COMM] = perf_event__comm_swap, | 480 | [PERF_RECORD_COMM] = perf_event__comm_swap, |
460 | [PERF_RECORD_FORK] = perf_event__task_swap, | 481 | [PERF_RECORD_FORK] = perf_event__task_swap, |
461 | [PERF_RECORD_EXIT] = perf_event__task_swap, | 482 | [PERF_RECORD_EXIT] = perf_event__task_swap, |
@@ -496,7 +517,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
496 | u64 file_offset); | 517 | u64 file_offset); |
497 | 518 | ||
498 | static int flush_sample_queue(struct perf_session *s, | 519 | static int flush_sample_queue(struct perf_session *s, |
499 | struct perf_tool *tool) | 520 | struct perf_tool *tool) |
500 | { | 521 | { |
501 | struct ordered_samples *os = &s->ordered_samples; | 522 | struct ordered_samples *os = &s->ordered_samples; |
502 | struct list_head *head = &os->samples; | 523 | struct list_head *head = &os->samples; |
@@ -505,12 +526,16 @@ static int flush_sample_queue(struct perf_session *s, | |||
505 | u64 limit = os->next_flush; | 526 | u64 limit = os->next_flush; |
506 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; | 527 | u64 last_ts = os->last_sample ? os->last_sample->timestamp : 0ULL; |
507 | unsigned idx = 0, progress_next = os->nr_samples / 16; | 528 | unsigned idx = 0, progress_next = os->nr_samples / 16; |
529 | bool show_progress = limit == ULLONG_MAX; | ||
508 | int ret; | 530 | int ret; |
509 | 531 | ||
510 | if (!tool->ordered_samples || !limit) | 532 | if (!tool->ordered_samples || !limit) |
511 | return 0; | 533 | return 0; |
512 | 534 | ||
513 | list_for_each_entry_safe(iter, tmp, head, list) { | 535 | list_for_each_entry_safe(iter, tmp, head, list) { |
536 | if (session_done()) | ||
537 | return 0; | ||
538 | |||
514 | if (iter->timestamp > limit) | 539 | if (iter->timestamp > limit) |
515 | break; | 540 | break; |
516 | 541 | ||
@@ -527,7 +552,7 @@ static int flush_sample_queue(struct perf_session *s, | |||
527 | os->last_flush = iter->timestamp; | 552 | os->last_flush = iter->timestamp; |
528 | list_del(&iter->list); | 553 | list_del(&iter->list); |
529 | list_add(&iter->list, &os->sample_cache); | 554 | list_add(&iter->list, &os->sample_cache); |
530 | if (++idx >= progress_next) { | 555 | if (show_progress && (++idx >= progress_next)) { |
531 | progress_next += os->nr_samples / 16; | 556 | progress_next += os->nr_samples / 16; |
532 | ui_progress__update(idx, os->nr_samples, | 557 | ui_progress__update(idx, os->nr_samples, |
533 | "Processing time ordered events..."); | 558 | "Processing time ordered events..."); |
@@ -644,7 +669,7 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s) | |||
644 | 669 | ||
645 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) | 670 | #define MAX_SAMPLE_BUFFER (64 * 1024 / sizeof(struct sample_queue)) |
646 | 671 | ||
647 | static int perf_session_queue_event(struct perf_session *s, union perf_event *event, | 672 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, |
648 | struct perf_sample *sample, u64 file_offset) | 673 | struct perf_sample *sample, u64 file_offset) |
649 | { | 674 | { |
650 | struct ordered_samples *os = &s->ordered_samples; | 675 | struct ordered_samples *os = &s->ordered_samples; |
@@ -740,7 +765,7 @@ static void perf_session__print_tstamp(struct perf_session *session, | |||
740 | union perf_event *event, | 765 | union perf_event *event, |
741 | struct perf_sample *sample) | 766 | struct perf_sample *sample) |
742 | { | 767 | { |
743 | u64 sample_type = perf_evlist__sample_type(session->evlist); | 768 | u64 sample_type = __perf_evlist__combined_sample_type(session->evlist); |
744 | 769 | ||
745 | if (event->header.type != PERF_RECORD_SAMPLE && | 770 | if (event->header.type != PERF_RECORD_SAMPLE && |
746 | !perf_evlist__sample_id_all(session->evlist)) { | 771 | !perf_evlist__sample_id_all(session->evlist)) { |
@@ -755,6 +780,36 @@ static void perf_session__print_tstamp(struct perf_session *session, | |||
755 | printf("%" PRIu64 " ", sample->time); | 780 | printf("%" PRIu64 " ", sample->time); |
756 | } | 781 | } |
757 | 782 | ||
783 | static void sample_read__printf(struct perf_sample *sample, u64 read_format) | ||
784 | { | ||
785 | printf("... sample_read:\n"); | ||
786 | |||
787 | if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) | ||
788 | printf("...... time enabled %016" PRIx64 "\n", | ||
789 | sample->read.time_enabled); | ||
790 | |||
791 | if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) | ||
792 | printf("...... time running %016" PRIx64 "\n", | ||
793 | sample->read.time_running); | ||
794 | |||
795 | if (read_format & PERF_FORMAT_GROUP) { | ||
796 | u64 i; | ||
797 | |||
798 | printf(".... group nr %" PRIu64 "\n", sample->read.group.nr); | ||
799 | |||
800 | for (i = 0; i < sample->read.group.nr; i++) { | ||
801 | struct sample_read_value *value; | ||
802 | |||
803 | value = &sample->read.group.values[i]; | ||
804 | printf("..... id %016" PRIx64 | ||
805 | ", value %016" PRIx64 "\n", | ||
806 | value->id, value->value); | ||
807 | } | ||
808 | } else | ||
809 | printf("..... id %016" PRIx64 ", value %016" PRIx64 "\n", | ||
810 | sample->read.one.id, sample->read.one.value); | ||
811 | } | ||
812 | |||
758 | static void dump_event(struct perf_session *session, union perf_event *event, | 813 | static void dump_event(struct perf_session *session, union perf_event *event, |
759 | u64 file_offset, struct perf_sample *sample) | 814 | u64 file_offset, struct perf_sample *sample) |
760 | { | 815 | { |
@@ -804,11 +859,15 @@ static void dump_sample(struct perf_evsel *evsel, union perf_event *event, | |||
804 | 859 | ||
805 | if (sample_type & PERF_SAMPLE_DATA_SRC) | 860 | if (sample_type & PERF_SAMPLE_DATA_SRC) |
806 | printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); | 861 | printf(" . data_src: 0x%"PRIx64"\n", sample->data_src); |
862 | |||
863 | if (sample_type & PERF_SAMPLE_READ) | ||
864 | sample_read__printf(sample, evsel->attr.read_format); | ||
807 | } | 865 | } |
808 | 866 | ||
809 | static struct machine * | 867 | static struct machine * |
810 | perf_session__find_machine_for_cpumode(struct perf_session *session, | 868 | perf_session__find_machine_for_cpumode(struct perf_session *session, |
811 | union perf_event *event) | 869 | union perf_event *event, |
870 | struct perf_sample *sample) | ||
812 | { | 871 | { |
813 | const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 872 | const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
814 | 873 | ||
@@ -817,10 +876,11 @@ static struct machine * | |||
817 | (cpumode == PERF_RECORD_MISC_GUEST_USER))) { | 876 | (cpumode == PERF_RECORD_MISC_GUEST_USER))) { |
818 | u32 pid; | 877 | u32 pid; |
819 | 878 | ||
820 | if (event->header.type == PERF_RECORD_MMAP) | 879 | if (event->header.type == PERF_RECORD_MMAP |
880 | || event->header.type == PERF_RECORD_MMAP2) | ||
821 | pid = event->mmap.pid; | 881 | pid = event->mmap.pid; |
822 | else | 882 | else |
823 | pid = event->ip.pid; | 883 | pid = sample->pid; |
824 | 884 | ||
825 | return perf_session__findnew_machine(session, pid); | 885 | return perf_session__findnew_machine(session, pid); |
826 | } | 886 | } |
@@ -828,6 +888,75 @@ static struct machine * | |||
828 | return &session->machines.host; | 888 | return &session->machines.host; |
829 | } | 889 | } |
830 | 890 | ||
891 | static int deliver_sample_value(struct perf_session *session, | ||
892 | struct perf_tool *tool, | ||
893 | union perf_event *event, | ||
894 | struct perf_sample *sample, | ||
895 | struct sample_read_value *v, | ||
896 | struct machine *machine) | ||
897 | { | ||
898 | struct perf_sample_id *sid; | ||
899 | |||
900 | sid = perf_evlist__id2sid(session->evlist, v->id); | ||
901 | if (sid) { | ||
902 | sample->id = v->id; | ||
903 | sample->period = v->value - sid->period; | ||
904 | sid->period = v->value; | ||
905 | } | ||
906 | |||
907 | if (!sid || sid->evsel == NULL) { | ||
908 | ++session->stats.nr_unknown_id; | ||
909 | return 0; | ||
910 | } | ||
911 | |||
912 | return tool->sample(tool, event, sample, sid->evsel, machine); | ||
913 | } | ||
914 | |||
915 | static int deliver_sample_group(struct perf_session *session, | ||
916 | struct perf_tool *tool, | ||
917 | union perf_event *event, | ||
918 | struct perf_sample *sample, | ||
919 | struct machine *machine) | ||
920 | { | ||
921 | int ret = -EINVAL; | ||
922 | u64 i; | ||
923 | |||
924 | for (i = 0; i < sample->read.group.nr; i++) { | ||
925 | ret = deliver_sample_value(session, tool, event, sample, | ||
926 | &sample->read.group.values[i], | ||
927 | machine); | ||
928 | if (ret) | ||
929 | break; | ||
930 | } | ||
931 | |||
932 | return ret; | ||
933 | } | ||
934 | |||
935 | static int | ||
936 | perf_session__deliver_sample(struct perf_session *session, | ||
937 | struct perf_tool *tool, | ||
938 | union perf_event *event, | ||
939 | struct perf_sample *sample, | ||
940 | struct perf_evsel *evsel, | ||
941 | struct machine *machine) | ||
942 | { | ||
943 | /* We know evsel != NULL. */ | ||
944 | u64 sample_type = evsel->attr.sample_type; | ||
945 | u64 read_format = evsel->attr.read_format; | ||
946 | |||
947 | /* Standard sample delievery. */ | ||
948 | if (!(sample_type & PERF_SAMPLE_READ)) | ||
949 | return tool->sample(tool, event, sample, evsel, machine); | ||
950 | |||
951 | /* For PERF_SAMPLE_READ we have either single or group mode. */ | ||
952 | if (read_format & PERF_FORMAT_GROUP) | ||
953 | return deliver_sample_group(session, tool, event, sample, | ||
954 | machine); | ||
955 | else | ||
956 | return deliver_sample_value(session, tool, event, sample, | ||
957 | &sample->read.one, machine); | ||
958 | } | ||
959 | |||
831 | static int perf_session_deliver_event(struct perf_session *session, | 960 | static int perf_session_deliver_event(struct perf_session *session, |
832 | union perf_event *event, | 961 | union perf_event *event, |
833 | struct perf_sample *sample, | 962 | struct perf_sample *sample, |
@@ -857,7 +986,8 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
857 | hists__inc_nr_events(&evsel->hists, event->header.type); | 986 | hists__inc_nr_events(&evsel->hists, event->header.type); |
858 | } | 987 | } |
859 | 988 | ||
860 | machine = perf_session__find_machine_for_cpumode(session, event); | 989 | machine = perf_session__find_machine_for_cpumode(session, event, |
990 | sample); | ||
861 | 991 | ||
862 | switch (event->header.type) { | 992 | switch (event->header.type) { |
863 | case PERF_RECORD_SAMPLE: | 993 | case PERF_RECORD_SAMPLE: |
@@ -870,9 +1000,12 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
870 | ++session->stats.nr_unprocessable_samples; | 1000 | ++session->stats.nr_unprocessable_samples; |
871 | return 0; | 1001 | return 0; |
872 | } | 1002 | } |
873 | return tool->sample(tool, event, sample, evsel, machine); | 1003 | return perf_session__deliver_sample(session, tool, event, |
1004 | sample, evsel, machine); | ||
874 | case PERF_RECORD_MMAP: | 1005 | case PERF_RECORD_MMAP: |
875 | return tool->mmap(tool, event, sample, machine); | 1006 | return tool->mmap(tool, event, sample, machine); |
1007 | case PERF_RECORD_MMAP2: | ||
1008 | return tool->mmap2(tool, event, sample, machine); | ||
876 | case PERF_RECORD_COMM: | 1009 | case PERF_RECORD_COMM: |
877 | return tool->comm(tool, event, sample, machine); | 1010 | return tool->comm(tool, event, sample, machine); |
878 | case PERF_RECORD_FORK: | 1011 | case PERF_RECORD_FORK: |
@@ -895,22 +1028,6 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
895 | } | 1028 | } |
896 | } | 1029 | } |
897 | 1030 | ||
898 | static int perf_session__preprocess_sample(struct perf_session *session, | ||
899 | union perf_event *event, struct perf_sample *sample) | ||
900 | { | ||
901 | if (event->header.type != PERF_RECORD_SAMPLE || | ||
902 | !(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_CALLCHAIN)) | ||
903 | return 0; | ||
904 | |||
905 | if (!ip_callchain__valid(sample->callchain, event)) { | ||
906 | pr_debug("call-chain problem with event, skipping it.\n"); | ||
907 | ++session->stats.nr_invalid_chains; | ||
908 | session->stats.total_invalid_chains += sample->period; | ||
909 | return -EINVAL; | ||
910 | } | ||
911 | return 0; | ||
912 | } | ||
913 | |||
914 | static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, | 1031 | static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, |
915 | struct perf_tool *tool, u64 file_offset) | 1032 | struct perf_tool *tool, u64 file_offset) |
916 | { | 1033 | { |
@@ -921,16 +1038,14 @@ static int perf_session__process_user_event(struct perf_session *session, union | |||
921 | /* These events are processed right away */ | 1038 | /* These events are processed right away */ |
922 | switch (event->header.type) { | 1039 | switch (event->header.type) { |
923 | case PERF_RECORD_HEADER_ATTR: | 1040 | case PERF_RECORD_HEADER_ATTR: |
924 | err = tool->attr(event, &session->evlist); | 1041 | err = tool->attr(tool, event, &session->evlist); |
925 | if (err == 0) | 1042 | if (err == 0) |
926 | perf_session__set_id_hdr_size(session); | 1043 | perf_session__set_id_hdr_size(session); |
927 | return err; | 1044 | return err; |
928 | case PERF_RECORD_HEADER_EVENT_TYPE: | ||
929 | return tool->event_type(tool, event); | ||
930 | case PERF_RECORD_HEADER_TRACING_DATA: | 1045 | case PERF_RECORD_HEADER_TRACING_DATA: |
931 | /* setup for reading amidst mmap */ | 1046 | /* setup for reading amidst mmap */ |
932 | lseek(session->fd, file_offset, SEEK_SET); | 1047 | lseek(session->fd, file_offset, SEEK_SET); |
933 | return tool->tracing_data(event, session); | 1048 | return tool->tracing_data(tool, event, session); |
934 | case PERF_RECORD_HEADER_BUILD_ID: | 1049 | case PERF_RECORD_HEADER_BUILD_ID: |
935 | return tool->build_id(tool, event, session); | 1050 | return tool->build_id(tool, event, session); |
936 | case PERF_RECORD_FINISHED_ROUND: | 1051 | case PERF_RECORD_FINISHED_ROUND: |
@@ -975,10 +1090,6 @@ static int perf_session__process_event(struct perf_session *session, | |||
975 | if (ret) | 1090 | if (ret) |
976 | return ret; | 1091 | return ret; |
977 | 1092 | ||
978 | /* Preprocess sample records - precheck callchains */ | ||
979 | if (perf_session__preprocess_sample(session, event, &sample)) | ||
980 | return 0; | ||
981 | |||
982 | if (tool->ordered_samples) { | 1093 | if (tool->ordered_samples) { |
983 | ret = perf_session_queue_event(session, event, &sample, | 1094 | ret = perf_session_queue_event(session, event, &sample, |
984 | file_offset); | 1095 | file_offset); |
@@ -999,7 +1110,7 @@ void perf_event_header__bswap(struct perf_event_header *self) | |||
999 | 1110 | ||
1000 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) | 1111 | struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) |
1001 | { | 1112 | { |
1002 | return machine__findnew_thread(&session->machines.host, pid); | 1113 | return machine__findnew_thread(&session->machines.host, 0, pid); |
1003 | } | 1114 | } |
1004 | 1115 | ||
1005 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) | 1116 | static struct thread *perf_session__register_idle_thread(struct perf_session *self) |
@@ -1054,7 +1165,6 @@ static void perf_session__warn_about_errors(const struct perf_session *session, | |||
1054 | } | 1165 | } |
1055 | } | 1166 | } |
1056 | 1167 | ||
1057 | #define session_done() (*(volatile int *)(&session_done)) | ||
1058 | volatile int session_done; | 1168 | volatile int session_done; |
1059 | 1169 | ||
1060 | static int __perf_session__process_pipe_events(struct perf_session *self, | 1170 | static int __perf_session__process_pipe_events(struct perf_session *self, |
@@ -1091,8 +1201,10 @@ more: | |||
1091 | perf_event_header__bswap(&event->header); | 1201 | perf_event_header__bswap(&event->header); |
1092 | 1202 | ||
1093 | size = event->header.size; | 1203 | size = event->header.size; |
1094 | if (size == 0) | 1204 | if (size < sizeof(struct perf_event_header)) { |
1095 | size = 8; | 1205 | pr_err("bad event header size\n"); |
1206 | goto out_err; | ||
1207 | } | ||
1096 | 1208 | ||
1097 | if (size > cur_size) { | 1209 | if (size > cur_size) { |
1098 | void *new = realloc(buf, size); | 1210 | void *new = realloc(buf, size); |
@@ -1161,8 +1273,12 @@ fetch_mmaped_event(struct perf_session *session, | |||
1161 | if (session->header.needs_swap) | 1273 | if (session->header.needs_swap) |
1162 | perf_event_header__bswap(&event->header); | 1274 | perf_event_header__bswap(&event->header); |
1163 | 1275 | ||
1164 | if (head + event->header.size > mmap_size) | 1276 | if (head + event->header.size > mmap_size) { |
1277 | /* We're not fetching the event so swap back again */ | ||
1278 | if (session->header.needs_swap) | ||
1279 | perf_event_header__bswap(&event->header); | ||
1165 | return NULL; | 1280 | return NULL; |
1281 | } | ||
1166 | 1282 | ||
1167 | return event; | 1283 | return event; |
1168 | } | 1284 | } |
@@ -1196,7 +1312,7 @@ int __perf_session__process_events(struct perf_session *session, | |||
1196 | file_offset = page_offset; | 1312 | file_offset = page_offset; |
1197 | head = data_offset - page_offset; | 1313 | head = data_offset - page_offset; |
1198 | 1314 | ||
1199 | if (data_offset + data_size < file_size) | 1315 | if (data_size && (data_offset + data_size < file_size)) |
1200 | file_size = data_offset + data_size; | 1316 | file_size = data_offset + data_size; |
1201 | 1317 | ||
1202 | progress_next = file_size / 16; | 1318 | progress_next = file_size / 16; |
@@ -1242,7 +1358,7 @@ more: | |||
1242 | 1358 | ||
1243 | size = event->header.size; | 1359 | size = event->header.size; |
1244 | 1360 | ||
1245 | if (size == 0 || | 1361 | if (size < sizeof(struct perf_event_header) || |
1246 | perf_session__process_event(session, event, tool, file_pos) < 0) { | 1362 | perf_session__process_event(session, event, tool, file_pos) < 0) { |
1247 | pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", | 1363 | pr_err("%#" PRIx64 " [%#x]: failed to process type: %d\n", |
1248 | file_offset + head, event->header.size, | 1364 | file_offset + head, event->header.size, |
@@ -1260,10 +1376,13 @@ more: | |||
1260 | "Processing events..."); | 1376 | "Processing events..."); |
1261 | } | 1377 | } |
1262 | 1378 | ||
1379 | err = 0; | ||
1380 | if (session_done()) | ||
1381 | goto out_err; | ||
1382 | |||
1263 | if (file_pos < file_size) | 1383 | if (file_pos < file_size) |
1264 | goto more; | 1384 | goto more; |
1265 | 1385 | ||
1266 | err = 0; | ||
1267 | /* do the final flush for ordered samples */ | 1386 | /* do the final flush for ordered samples */ |
1268 | session->ordered_samples.next_flush = ULLONG_MAX; | 1387 | session->ordered_samples.next_flush = ULLONG_MAX; |
1269 | err = flush_sample_queue(session, tool); | 1388 | err = flush_sample_queue(session, tool); |
@@ -1295,12 +1414,15 @@ int perf_session__process_events(struct perf_session *self, | |||
1295 | 1414 | ||
1296 | bool perf_session__has_traces(struct perf_session *session, const char *msg) | 1415 | bool perf_session__has_traces(struct perf_session *session, const char *msg) |
1297 | { | 1416 | { |
1298 | if (!(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_RAW)) { | 1417 | struct perf_evsel *evsel; |
1299 | pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg); | 1418 | |
1300 | return false; | 1419 | list_for_each_entry(evsel, &session->evlist->entries, node) { |
1420 | if (evsel->attr.type == PERF_TYPE_TRACEPOINT) | ||
1421 | return true; | ||
1301 | } | 1422 | } |
1302 | 1423 | ||
1303 | return true; | 1424 | pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg); |
1425 | return false; | ||
1304 | } | 1426 | } |
1305 | 1427 | ||
1306 | int maps__set_kallsyms_ref_reloc_sym(struct map **maps, | 1428 | int maps__set_kallsyms_ref_reloc_sym(struct map **maps, |
@@ -1383,13 +1505,18 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | |||
1383 | 1505 | ||
1384 | void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, | 1506 | void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, |
1385 | struct perf_sample *sample, struct machine *machine, | 1507 | struct perf_sample *sample, struct machine *machine, |
1386 | int print_sym, int print_dso, int print_symoffset) | 1508 | unsigned int print_opts, unsigned int stack_depth) |
1387 | { | 1509 | { |
1388 | struct addr_location al; | 1510 | struct addr_location al; |
1389 | struct callchain_cursor_node *node; | 1511 | struct callchain_cursor_node *node; |
1390 | 1512 | int print_ip = print_opts & PRINT_IP_OPT_IP; | |
1391 | if (perf_event__preprocess_sample(event, machine, &al, sample, | 1513 | int print_sym = print_opts & PRINT_IP_OPT_SYM; |
1392 | NULL) < 0) { | 1514 | int print_dso = print_opts & PRINT_IP_OPT_DSO; |
1515 | int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET; | ||
1516 | int print_oneline = print_opts & PRINT_IP_OPT_ONELINE; | ||
1517 | char s = print_oneline ? ' ' : '\t'; | ||
1518 | |||
1519 | if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { | ||
1393 | error("problem processing %d event, skipping it.\n", | 1520 | error("problem processing %d event, skipping it.\n", |
1394 | event->header.type); | 1521 | event->header.type); |
1395 | return; | 1522 | return; |
@@ -1397,37 +1524,50 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, | |||
1397 | 1524 | ||
1398 | if (symbol_conf.use_callchain && sample->callchain) { | 1525 | if (symbol_conf.use_callchain && sample->callchain) { |
1399 | 1526 | ||
1400 | |||
1401 | if (machine__resolve_callchain(machine, evsel, al.thread, | 1527 | if (machine__resolve_callchain(machine, evsel, al.thread, |
1402 | sample, NULL) != 0) { | 1528 | sample, NULL, NULL) != 0) { |
1403 | if (verbose) | 1529 | if (verbose) |
1404 | error("Failed to resolve callchain. Skipping\n"); | 1530 | error("Failed to resolve callchain. Skipping\n"); |
1405 | return; | 1531 | return; |
1406 | } | 1532 | } |
1407 | callchain_cursor_commit(&callchain_cursor); | 1533 | callchain_cursor_commit(&callchain_cursor); |
1408 | 1534 | ||
1409 | while (1) { | 1535 | while (stack_depth) { |
1410 | node = callchain_cursor_current(&callchain_cursor); | 1536 | node = callchain_cursor_current(&callchain_cursor); |
1411 | if (!node) | 1537 | if (!node) |
1412 | break; | 1538 | break; |
1413 | 1539 | ||
1414 | printf("\t%16" PRIx64, node->ip); | 1540 | if (print_ip) |
1541 | printf("%c%16" PRIx64, s, node->ip); | ||
1542 | |||
1415 | if (print_sym) { | 1543 | if (print_sym) { |
1416 | printf(" "); | 1544 | printf(" "); |
1417 | symbol__fprintf_symname(node->sym, stdout); | 1545 | if (print_symoffset) { |
1546 | al.addr = node->ip; | ||
1547 | al.map = node->map; | ||
1548 | symbol__fprintf_symname_offs(node->sym, &al, stdout); | ||
1549 | } else | ||
1550 | symbol__fprintf_symname(node->sym, stdout); | ||
1418 | } | 1551 | } |
1552 | |||
1419 | if (print_dso) { | 1553 | if (print_dso) { |
1420 | printf(" ("); | 1554 | printf(" ("); |
1421 | map__fprintf_dsoname(node->map, stdout); | 1555 | map__fprintf_dsoname(node->map, stdout); |
1422 | printf(")"); | 1556 | printf(")"); |
1423 | } | 1557 | } |
1424 | printf("\n"); | 1558 | |
1559 | if (!print_oneline) | ||
1560 | printf("\n"); | ||
1425 | 1561 | ||
1426 | callchain_cursor_advance(&callchain_cursor); | 1562 | callchain_cursor_advance(&callchain_cursor); |
1563 | |||
1564 | stack_depth--; | ||
1427 | } | 1565 | } |
1428 | 1566 | ||
1429 | } else { | 1567 | } else { |
1430 | printf("%16" PRIx64, sample->ip); | 1568 | if (print_ip) |
1569 | printf("%16" PRIx64, sample->ip); | ||
1570 | |||
1431 | if (print_sym) { | 1571 | if (print_sym) { |
1432 | printf(" "); | 1572 | printf(" "); |
1433 | if (print_symoffset) | 1573 | if (print_symoffset) |
@@ -1510,52 +1650,26 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, | |||
1510 | const struct perf_evsel_str_handler *assocs, | 1650 | const struct perf_evsel_str_handler *assocs, |
1511 | size_t nr_assocs) | 1651 | size_t nr_assocs) |
1512 | { | 1652 | { |
1513 | struct perf_evlist *evlist = session->evlist; | ||
1514 | struct event_format *format; | ||
1515 | struct perf_evsel *evsel; | 1653 | struct perf_evsel *evsel; |
1516 | char *tracepoint, *name; | ||
1517 | size_t i; | 1654 | size_t i; |
1518 | int err; | 1655 | int err; |
1519 | 1656 | ||
1520 | for (i = 0; i < nr_assocs; i++) { | 1657 | for (i = 0; i < nr_assocs; i++) { |
1521 | err = -ENOMEM; | 1658 | /* |
1522 | tracepoint = strdup(assocs[i].name); | 1659 | * Adding a handler for an event not in the session, |
1523 | if (tracepoint == NULL) | 1660 | * just ignore it. |
1524 | goto out; | 1661 | */ |
1525 | 1662 | evsel = perf_evlist__find_tracepoint_by_name(session->evlist, assocs[i].name); | |
1526 | err = -ENOENT; | ||
1527 | name = strchr(tracepoint, ':'); | ||
1528 | if (name == NULL) | ||
1529 | goto out_free; | ||
1530 | |||
1531 | *name++ = '\0'; | ||
1532 | format = pevent_find_event_by_name(session->pevent, | ||
1533 | tracepoint, name); | ||
1534 | if (format == NULL) { | ||
1535 | /* | ||
1536 | * Adding a handler for an event not in the session, | ||
1537 | * just ignore it. | ||
1538 | */ | ||
1539 | goto next; | ||
1540 | } | ||
1541 | |||
1542 | evsel = perf_evlist__find_tracepoint_by_id(evlist, format->id); | ||
1543 | if (evsel == NULL) | 1663 | if (evsel == NULL) |
1544 | goto next; | 1664 | continue; |
1545 | 1665 | ||
1546 | err = -EEXIST; | 1666 | err = -EEXIST; |
1547 | if (evsel->handler.func != NULL) | 1667 | if (evsel->handler.func != NULL) |
1548 | goto out_free; | 1668 | goto out; |
1549 | evsel->handler.func = assocs[i].handler; | 1669 | evsel->handler.func = assocs[i].handler; |
1550 | next: | ||
1551 | free(tracepoint); | ||
1552 | } | 1670 | } |
1553 | 1671 | ||
1554 | err = 0; | 1672 | err = 0; |
1555 | out: | 1673 | out: |
1556 | return err; | 1674 | return err; |
1557 | |||
1558 | out_free: | ||
1559 | free(tracepoint); | ||
1560 | goto out; | ||
1561 | } | 1675 | } |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index f3b235ec7bf4..04bf7373a7e5 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -37,11 +37,16 @@ struct perf_session { | |||
37 | int fd; | 37 | int fd; |
38 | bool fd_pipe; | 38 | bool fd_pipe; |
39 | bool repipe; | 39 | bool repipe; |
40 | char *cwd; | ||
41 | struct ordered_samples ordered_samples; | 40 | struct ordered_samples ordered_samples; |
42 | char filename[1]; | 41 | char filename[1]; |
43 | }; | 42 | }; |
44 | 43 | ||
44 | #define PRINT_IP_OPT_IP (1<<0) | ||
45 | #define PRINT_IP_OPT_SYM (1<<1) | ||
46 | #define PRINT_IP_OPT_DSO (1<<2) | ||
47 | #define PRINT_IP_OPT_SYMOFFSET (1<<3) | ||
48 | #define PRINT_IP_OPT_ONELINE (1<<4) | ||
49 | |||
45 | struct perf_tool; | 50 | struct perf_tool; |
46 | 51 | ||
47 | struct perf_session *perf_session__new(const char *filename, int mode, | 52 | struct perf_session *perf_session__new(const char *filename, int mode, |
@@ -57,6 +62,11 @@ int __perf_session__process_events(struct perf_session *self, | |||
57 | int perf_session__process_events(struct perf_session *self, | 62 | int perf_session__process_events(struct perf_session *self, |
58 | struct perf_tool *tool); | 63 | struct perf_tool *tool); |
59 | 64 | ||
65 | int perf_session_queue_event(struct perf_session *s, union perf_event *event, | ||
66 | struct perf_sample *sample, u64 file_offset); | ||
67 | |||
68 | void perf_tool__fill_defaults(struct perf_tool *tool); | ||
69 | |||
60 | int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel, | 70 | int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel, |
61 | struct thread *thread, | 71 | struct thread *thread, |
62 | struct ip_callchain *chain, | 72 | struct ip_callchain *chain, |
@@ -99,7 +109,7 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | |||
99 | 109 | ||
100 | void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, | 110 | void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, |
101 | struct perf_sample *sample, struct machine *machine, | 111 | struct perf_sample *sample, struct machine *machine, |
102 | int print_sym, int print_dso, int print_symoffset); | 112 | unsigned int print_opts, unsigned int stack_depth); |
103 | 113 | ||
104 | int perf_session__cpu_bitmap(struct perf_session *session, | 114 | int perf_session__cpu_bitmap(struct perf_session *session, |
105 | const char *cpu_list, unsigned long *cpu_bitmap); | 115 | const char *cpu_list, unsigned long *cpu_bitmap); |
@@ -114,4 +124,8 @@ int __perf_session__set_tracepoints_handlers(struct perf_session *session, | |||
114 | 124 | ||
115 | #define perf_session__set_tracepoints_handlers(session, array) \ | 125 | #define perf_session__set_tracepoints_handlers(session, array) \ |
116 | __perf_session__set_tracepoints_handlers(session, array, ARRAY_SIZE(array)) | 126 | __perf_session__set_tracepoints_handlers(session, array, ARRAY_SIZE(array)) |
127 | |||
128 | extern volatile int session_done; | ||
129 | |||
130 | #define session_done() (*(volatile int *)(&session_done)) | ||
117 | #endif /* __PERF_SESSION_H */ | 131 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 313a5a730112..5f118a089519 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -7,6 +7,8 @@ const char default_parent_pattern[] = "^sys_|^do_page_fault"; | |||
7 | const char *parent_pattern = default_parent_pattern; | 7 | const char *parent_pattern = default_parent_pattern; |
8 | const char default_sort_order[] = "comm,dso,symbol"; | 8 | const char default_sort_order[] = "comm,dso,symbol"; |
9 | const char *sort_order = default_sort_order; | 9 | const char *sort_order = default_sort_order; |
10 | regex_t ignore_callees_regex; | ||
11 | int have_ignore_callees = 0; | ||
10 | int sort__need_collapse = 0; | 12 | int sort__need_collapse = 0; |
11 | int sort__has_parent = 0; | 13 | int sort__has_parent = 0; |
12 | int sort__has_sym = 0; | 14 | int sort__has_sym = 0; |
@@ -55,14 +57,14 @@ static int64_t cmp_null(void *l, void *r) | |||
55 | static int64_t | 57 | static int64_t |
56 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) | 58 | sort__thread_cmp(struct hist_entry *left, struct hist_entry *right) |
57 | { | 59 | { |
58 | return right->thread->pid - left->thread->pid; | 60 | return right->thread->tid - left->thread->tid; |
59 | } | 61 | } |
60 | 62 | ||
61 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, | 63 | static int hist_entry__thread_snprintf(struct hist_entry *self, char *bf, |
62 | size_t size, unsigned int width) | 64 | size_t size, unsigned int width) |
63 | { | 65 | { |
64 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, | 66 | return repsep_snprintf(bf, size, "%*s:%5d", width - 6, |
65 | self->thread->comm ?: "", self->thread->pid); | 67 | self->thread->comm ?: "", self->thread->tid); |
66 | } | 68 | } |
67 | 69 | ||
68 | struct sort_entry sort_thread = { | 70 | struct sort_entry sort_thread = { |
@@ -77,7 +79,7 @@ struct sort_entry sort_thread = { | |||
77 | static int64_t | 79 | static int64_t |
78 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) | 80 | sort__comm_cmp(struct hist_entry *left, struct hist_entry *right) |
79 | { | 81 | { |
80 | return right->thread->pid - left->thread->pid; | 82 | return right->thread->tid - left->thread->tid; |
81 | } | 83 | } |
82 | 84 | ||
83 | static int64_t | 85 | static int64_t |
@@ -872,6 +874,8 @@ static struct sort_dimension common_sort_dimensions[] = { | |||
872 | DIM(SORT_PARENT, "parent", sort_parent), | 874 | DIM(SORT_PARENT, "parent", sort_parent), |
873 | DIM(SORT_CPU, "cpu", sort_cpu), | 875 | DIM(SORT_CPU, "cpu", sort_cpu), |
874 | DIM(SORT_SRCLINE, "srcline", sort_srcline), | 876 | DIM(SORT_SRCLINE, "srcline", sort_srcline), |
877 | DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), | ||
878 | DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), | ||
875 | }; | 879 | }; |
876 | 880 | ||
877 | #undef DIM | 881 | #undef DIM |
@@ -891,8 +895,6 @@ static struct sort_dimension bstack_sort_dimensions[] = { | |||
891 | #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) } | 895 | #define DIM(d, n, func) [d - __SORT_MEMORY_MODE] = { .name = n, .entry = &(func) } |
892 | 896 | ||
893 | static struct sort_dimension memory_sort_dimensions[] = { | 897 | static struct sort_dimension memory_sort_dimensions[] = { |
894 | DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight), | ||
895 | DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight), | ||
896 | DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym), | 898 | DIM(SORT_MEM_DADDR_SYMBOL, "symbol_daddr", sort_mem_daddr_sym), |
897 | DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso), | 899 | DIM(SORT_MEM_DADDR_DSO, "dso_daddr", sort_mem_daddr_dso), |
898 | DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked), | 900 | DIM(SORT_MEM_LOCKED, "locked", sort_mem_locked), |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 45ac84c1e037..4e80dbd271e7 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -29,6 +29,8 @@ extern const char *sort_order; | |||
29 | extern const char default_parent_pattern[]; | 29 | extern const char default_parent_pattern[]; |
30 | extern const char *parent_pattern; | 30 | extern const char *parent_pattern; |
31 | extern const char default_sort_order[]; | 31 | extern const char default_sort_order[]; |
32 | extern regex_t ignore_callees_regex; | ||
33 | extern int have_ignore_callees; | ||
32 | extern int sort__need_collapse; | 34 | extern int sort__need_collapse; |
33 | extern int sort__has_parent; | 35 | extern int sort__has_parent; |
34 | extern int sort__has_sym; | 36 | extern int sort__has_sym; |
@@ -87,6 +89,9 @@ struct hist_entry { | |||
87 | 89 | ||
88 | struct hist_entry_diff diff; | 90 | struct hist_entry_diff diff; |
89 | 91 | ||
92 | /* We are added by hists__add_dummy_entry. */ | ||
93 | bool dummy; | ||
94 | |||
90 | /* XXX These two should move to some tree widget lib */ | 95 | /* XXX These two should move to some tree widget lib */ |
91 | u16 row_offset; | 96 | u16 row_offset; |
92 | u16 nr_rows; | 97 | u16 nr_rows; |
@@ -138,6 +143,8 @@ enum sort_type { | |||
138 | SORT_PARENT, | 143 | SORT_PARENT, |
139 | SORT_CPU, | 144 | SORT_CPU, |
140 | SORT_SRCLINE, | 145 | SORT_SRCLINE, |
146 | SORT_LOCAL_WEIGHT, | ||
147 | SORT_GLOBAL_WEIGHT, | ||
141 | 148 | ||
142 | /* branch stack specific sort keys */ | 149 | /* branch stack specific sort keys */ |
143 | __SORT_BRANCH_STACK, | 150 | __SORT_BRANCH_STACK, |
@@ -149,9 +156,7 @@ enum sort_type { | |||
149 | 156 | ||
150 | /* memory mode specific sort keys */ | 157 | /* memory mode specific sort keys */ |
151 | __SORT_MEMORY_MODE, | 158 | __SORT_MEMORY_MODE, |
152 | SORT_LOCAL_WEIGHT = __SORT_MEMORY_MODE, | 159 | SORT_MEM_DADDR_SYMBOL = __SORT_MEMORY_MODE, |
153 | SORT_GLOBAL_WEIGHT, | ||
154 | SORT_MEM_DADDR_SYMBOL, | ||
155 | SORT_MEM_DADDR_DSO, | 160 | SORT_MEM_DADDR_DSO, |
156 | SORT_MEM_LOCKED, | 161 | SORT_MEM_LOCKED, |
157 | SORT_MEM_TLB, | 162 | SORT_MEM_TLB, |
@@ -183,4 +188,6 @@ int setup_sorting(void); | |||
183 | extern int sort_dimension__add(const char *); | 188 | extern int sort_dimension__add(const char *); |
184 | void sort__setup_elide(FILE *fp); | 189 | void sort__setup_elide(FILE *fp); |
185 | 190 | ||
191 | int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); | ||
192 | |||
186 | #endif /* __PERF_SORT_H */ | 193 | #endif /* __PERF_SORT_H */ |
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c index 7c59c28afcc5..6506b3dfb605 100644 --- a/tools/perf/util/stat.c +++ b/tools/perf/util/stat.c | |||
@@ -10,6 +10,12 @@ void update_stats(struct stats *stats, u64 val) | |||
10 | delta = val - stats->mean; | 10 | delta = val - stats->mean; |
11 | stats->mean += delta / stats->n; | 11 | stats->mean += delta / stats->n; |
12 | stats->M2 += delta*(val - stats->mean); | 12 | stats->M2 += delta*(val - stats->mean); |
13 | |||
14 | if (val > stats->max) | ||
15 | stats->max = val; | ||
16 | |||
17 | if (val < stats->min) | ||
18 | stats->min = val; | ||
13 | } | 19 | } |
14 | 20 | ||
15 | double avg_stats(struct stats *stats) | 21 | double avg_stats(struct stats *stats) |
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h index 588367c3c767..ae8ccd7227cf 100644 --- a/tools/perf/util/stat.h +++ b/tools/perf/util/stat.h | |||
@@ -6,6 +6,7 @@ | |||
6 | struct stats | 6 | struct stats |
7 | { | 7 | { |
8 | double n, mean, M2; | 8 | double n, mean, M2; |
9 | u64 max, min; | ||
9 | }; | 10 | }; |
10 | 11 | ||
11 | void update_stats(struct stats *stats, u64 val); | 12 | void update_stats(struct stats *stats, u64 val); |
@@ -13,4 +14,12 @@ double avg_stats(struct stats *stats); | |||
13 | double stddev_stats(struct stats *stats); | 14 | double stddev_stats(struct stats *stats); |
14 | double rel_stddev_stats(double stddev, double avg); | 15 | double rel_stddev_stats(double stddev, double avg); |
15 | 16 | ||
17 | static inline void init_stats(struct stats *stats) | ||
18 | { | ||
19 | stats->n = 0.0; | ||
20 | stats->mean = 0.0; | ||
21 | stats->M2 = 0.0; | ||
22 | stats->min = (u64) -1; | ||
23 | stats->max = 0; | ||
24 | } | ||
16 | #endif | 25 | #endif |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 29c7b2cb2521..f0b0c008c507 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -387,3 +387,27 @@ void *memdup(const void *src, size_t len) | |||
387 | 387 | ||
388 | return p; | 388 | return p; |
389 | } | 389 | } |
390 | |||
391 | /** | ||
392 | * str_append - reallocate string and append another | ||
393 | * @s: pointer to string pointer | ||
394 | * @len: pointer to len (initialized) | ||
395 | * @a: string to append. | ||
396 | */ | ||
397 | int str_append(char **s, int *len, const char *a) | ||
398 | { | ||
399 | int olen = *s ? strlen(*s) : 0; | ||
400 | int nlen = olen + strlen(a) + 1; | ||
401 | if (*len < nlen) { | ||
402 | *len = *len * 2; | ||
403 | if (*len < nlen) | ||
404 | *len = nlen; | ||
405 | *s = realloc(*s, *len); | ||
406 | if (!*s) | ||
407 | return -ENOMEM; | ||
408 | if (olen == 0) | ||
409 | **s = 0; | ||
410 | } | ||
411 | strcat(*s, a); | ||
412 | return 0; | ||
413 | } | ||
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 4b12bf850325..a9c829be5216 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -8,6 +8,22 @@ | |||
8 | #include "symbol.h" | 8 | #include "symbol.h" |
9 | #include "debug.h" | 9 | #include "debug.h" |
10 | 10 | ||
11 | #ifndef HAVE_ELF_GETPHDRNUM | ||
12 | static int elf_getphdrnum(Elf *elf, size_t *dst) | ||
13 | { | ||
14 | GElf_Ehdr gehdr; | ||
15 | GElf_Ehdr *ehdr; | ||
16 | |||
17 | ehdr = gelf_getehdr(elf, &gehdr); | ||
18 | if (!ehdr) | ||
19 | return -1; | ||
20 | |||
21 | *dst = ehdr->e_phnum; | ||
22 | |||
23 | return 0; | ||
24 | } | ||
25 | #endif | ||
26 | |||
11 | #ifndef NT_GNU_BUILD_ID | 27 | #ifndef NT_GNU_BUILD_ID |
12 | #define NT_GNU_BUILD_ID 3 | 28 | #define NT_GNU_BUILD_ID 3 |
13 | #endif | 29 | #endif |
@@ -599,11 +615,13 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, | |||
599 | if (dso->kernel == DSO_TYPE_USER) { | 615 | if (dso->kernel == DSO_TYPE_USER) { |
600 | GElf_Shdr shdr; | 616 | GElf_Shdr shdr; |
601 | ss->adjust_symbols = (ehdr.e_type == ET_EXEC || | 617 | ss->adjust_symbols = (ehdr.e_type == ET_EXEC || |
618 | ehdr.e_type == ET_REL || | ||
602 | elf_section_by_name(elf, &ehdr, &shdr, | 619 | elf_section_by_name(elf, &ehdr, &shdr, |
603 | ".gnu.prelink_undo", | 620 | ".gnu.prelink_undo", |
604 | NULL) != NULL); | 621 | NULL) != NULL); |
605 | } else { | 622 | } else { |
606 | ss->adjust_symbols = 0; | 623 | ss->adjust_symbols = ehdr.e_type == ET_EXEC || |
624 | ehdr.e_type == ET_REL; | ||
607 | } | 625 | } |
608 | 626 | ||
609 | ss->name = strdup(name); | 627 | ss->name = strdup(name); |
@@ -624,6 +642,37 @@ out_close: | |||
624 | return err; | 642 | return err; |
625 | } | 643 | } |
626 | 644 | ||
645 | /** | ||
646 | * ref_reloc_sym_not_found - has kernel relocation symbol been found. | ||
647 | * @kmap: kernel maps and relocation reference symbol | ||
648 | * | ||
649 | * This function returns %true if we are dealing with the kernel maps and the | ||
650 | * relocation reference symbol has not yet been found. Otherwise %false is | ||
651 | * returned. | ||
652 | */ | ||
653 | static bool ref_reloc_sym_not_found(struct kmap *kmap) | ||
654 | { | ||
655 | return kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && | ||
656 | !kmap->ref_reloc_sym->unrelocated_addr; | ||
657 | } | ||
658 | |||
659 | /** | ||
660 | * ref_reloc - kernel relocation offset. | ||
661 | * @kmap: kernel maps and relocation reference symbol | ||
662 | * | ||
663 | * This function returns the offset of kernel addresses as determined by using | ||
664 | * the relocation reference symbol i.e. if the kernel has not been relocated | ||
665 | * then the return value is zero. | ||
666 | */ | ||
667 | static u64 ref_reloc(struct kmap *kmap) | ||
668 | { | ||
669 | if (kmap && kmap->ref_reloc_sym && | ||
670 | kmap->ref_reloc_sym->unrelocated_addr) | ||
671 | return kmap->ref_reloc_sym->addr - | ||
672 | kmap->ref_reloc_sym->unrelocated_addr; | ||
673 | return 0; | ||
674 | } | ||
675 | |||
627 | int dso__load_sym(struct dso *dso, struct map *map, | 676 | int dso__load_sym(struct dso *dso, struct map *map, |
628 | struct symsrc *syms_ss, struct symsrc *runtime_ss, | 677 | struct symsrc *syms_ss, struct symsrc *runtime_ss, |
629 | symbol_filter_t filter, int kmodule) | 678 | symbol_filter_t filter, int kmodule) |
@@ -642,8 +691,17 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
642 | Elf_Scn *sec, *sec_strndx; | 691 | Elf_Scn *sec, *sec_strndx; |
643 | Elf *elf; | 692 | Elf *elf; |
644 | int nr = 0; | 693 | int nr = 0; |
694 | bool remap_kernel = false, adjust_kernel_syms = false; | ||
645 | 695 | ||
646 | dso->symtab_type = syms_ss->type; | 696 | dso->symtab_type = syms_ss->type; |
697 | dso->rel = syms_ss->ehdr.e_type == ET_REL; | ||
698 | |||
699 | /* | ||
700 | * Modules may already have symbols from kallsyms, but those symbols | ||
701 | * have the wrong values for the dso maps, so remove them. | ||
702 | */ | ||
703 | if (kmodule && syms_ss->symtab) | ||
704 | symbols__delete(&dso->symbols[map->type]); | ||
647 | 705 | ||
648 | if (!syms_ss->symtab) { | 706 | if (!syms_ss->symtab) { |
649 | syms_ss->symtab = syms_ss->dynsym; | 707 | syms_ss->symtab = syms_ss->dynsym; |
@@ -681,7 +739,31 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
681 | nr_syms = shdr.sh_size / shdr.sh_entsize; | 739 | nr_syms = shdr.sh_size / shdr.sh_entsize; |
682 | 740 | ||
683 | memset(&sym, 0, sizeof(sym)); | 741 | memset(&sym, 0, sizeof(sym)); |
684 | dso->adjust_symbols = runtime_ss->adjust_symbols; | 742 | |
743 | /* | ||
744 | * The kernel relocation symbol is needed in advance in order to adjust | ||
745 | * kernel maps correctly. | ||
746 | */ | ||
747 | if (ref_reloc_sym_not_found(kmap)) { | ||
748 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | ||
749 | const char *elf_name = elf_sym__name(&sym, symstrs); | ||
750 | |||
751 | if (strcmp(elf_name, kmap->ref_reloc_sym->name)) | ||
752 | continue; | ||
753 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | ||
754 | break; | ||
755 | } | ||
756 | } | ||
757 | |||
758 | dso->adjust_symbols = runtime_ss->adjust_symbols || ref_reloc(kmap); | ||
759 | /* | ||
760 | * Initial kernel and module mappings do not map to the dso. For | ||
761 | * function mappings, flag the fixups. | ||
762 | */ | ||
763 | if (map->type == MAP__FUNCTION && (dso->kernel || kmodule)) { | ||
764 | remap_kernel = true; | ||
765 | adjust_kernel_syms = dso->adjust_symbols; | ||
766 | } | ||
685 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { | 767 | elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) { |
686 | struct symbol *f; | 768 | struct symbol *f; |
687 | const char *elf_name = elf_sym__name(&sym, symstrs); | 769 | const char *elf_name = elf_sym__name(&sym, symstrs); |
@@ -690,10 +772,6 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
690 | const char *section_name; | 772 | const char *section_name; |
691 | bool used_opd = false; | 773 | bool used_opd = false; |
692 | 774 | ||
693 | if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name && | ||
694 | strcmp(elf_name, kmap->ref_reloc_sym->name) == 0) | ||
695 | kmap->ref_reloc_sym->unrelocated_addr = sym.st_value; | ||
696 | |||
697 | if (!is_label && !elf_sym__is_a(&sym, map->type)) | 775 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
698 | continue; | 776 | continue; |
699 | 777 | ||
@@ -745,20 +823,55 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
745 | (sym.st_value & 1)) | 823 | (sym.st_value & 1)) |
746 | --sym.st_value; | 824 | --sym.st_value; |
747 | 825 | ||
748 | if (dso->kernel != DSO_TYPE_USER || kmodule) { | 826 | if (dso->kernel || kmodule) { |
749 | char dso_name[PATH_MAX]; | 827 | char dso_name[PATH_MAX]; |
750 | 828 | ||
829 | /* Adjust symbol to map to file offset */ | ||
830 | if (adjust_kernel_syms) | ||
831 | sym.st_value -= shdr.sh_addr - shdr.sh_offset; | ||
832 | |||
751 | if (strcmp(section_name, | 833 | if (strcmp(section_name, |
752 | (curr_dso->short_name + | 834 | (curr_dso->short_name + |
753 | dso->short_name_len)) == 0) | 835 | dso->short_name_len)) == 0) |
754 | goto new_symbol; | 836 | goto new_symbol; |
755 | 837 | ||
756 | if (strcmp(section_name, ".text") == 0) { | 838 | if (strcmp(section_name, ".text") == 0) { |
839 | /* | ||
840 | * The initial kernel mapping is based on | ||
841 | * kallsyms and identity maps. Overwrite it to | ||
842 | * map to the kernel dso. | ||
843 | */ | ||
844 | if (remap_kernel && dso->kernel) { | ||
845 | remap_kernel = false; | ||
846 | map->start = shdr.sh_addr + | ||
847 | ref_reloc(kmap); | ||
848 | map->end = map->start + shdr.sh_size; | ||
849 | map->pgoff = shdr.sh_offset; | ||
850 | map->map_ip = map__map_ip; | ||
851 | map->unmap_ip = map__unmap_ip; | ||
852 | /* Ensure maps are correctly ordered */ | ||
853 | map_groups__remove(kmap->kmaps, map); | ||
854 | map_groups__insert(kmap->kmaps, map); | ||
855 | } | ||
856 | |||
857 | /* | ||
858 | * The initial module mapping is based on | ||
859 | * /proc/modules mapped to offset zero. | ||
860 | * Overwrite it to map to the module dso. | ||
861 | */ | ||
862 | if (remap_kernel && kmodule) { | ||
863 | remap_kernel = false; | ||
864 | map->pgoff = shdr.sh_offset; | ||
865 | } | ||
866 | |||
757 | curr_map = map; | 867 | curr_map = map; |
758 | curr_dso = dso; | 868 | curr_dso = dso; |
759 | goto new_symbol; | 869 | goto new_symbol; |
760 | } | 870 | } |
761 | 871 | ||
872 | if (!kmap) | ||
873 | goto new_symbol; | ||
874 | |||
762 | snprintf(dso_name, sizeof(dso_name), | 875 | snprintf(dso_name, sizeof(dso_name), |
763 | "%s%s", dso->short_name, section_name); | 876 | "%s%s", dso->short_name, section_name); |
764 | 877 | ||
@@ -781,8 +894,16 @@ int dso__load_sym(struct dso *dso, struct map *map, | |||
781 | dso__delete(curr_dso); | 894 | dso__delete(curr_dso); |
782 | goto out_elf_end; | 895 | goto out_elf_end; |
783 | } | 896 | } |
784 | curr_map->map_ip = identity__map_ip; | 897 | if (adjust_kernel_syms) { |
785 | curr_map->unmap_ip = identity__map_ip; | 898 | curr_map->start = shdr.sh_addr + |
899 | ref_reloc(kmap); | ||
900 | curr_map->end = curr_map->start + | ||
901 | shdr.sh_size; | ||
902 | curr_map->pgoff = shdr.sh_offset; | ||
903 | } else { | ||
904 | curr_map->map_ip = identity__map_ip; | ||
905 | curr_map->unmap_ip = identity__map_ip; | ||
906 | } | ||
786 | curr_dso->symtab_type = dso->symtab_type; | 907 | curr_dso->symtab_type = dso->symtab_type; |
787 | map_groups__insert(kmap->kmaps, curr_map); | 908 | map_groups__insert(kmap->kmaps, curr_map); |
788 | dsos__add(&dso->node, curr_dso); | 909 | dsos__add(&dso->node, curr_dso); |
@@ -846,6 +967,57 @@ out_elf_end: | |||
846 | return err; | 967 | return err; |
847 | } | 968 | } |
848 | 969 | ||
970 | static int elf_read_maps(Elf *elf, bool exe, mapfn_t mapfn, void *data) | ||
971 | { | ||
972 | GElf_Phdr phdr; | ||
973 | size_t i, phdrnum; | ||
974 | int err; | ||
975 | u64 sz; | ||
976 | |||
977 | if (elf_getphdrnum(elf, &phdrnum)) | ||
978 | return -1; | ||
979 | |||
980 | for (i = 0; i < phdrnum; i++) { | ||
981 | if (gelf_getphdr(elf, i, &phdr) == NULL) | ||
982 | return -1; | ||
983 | if (phdr.p_type != PT_LOAD) | ||
984 | continue; | ||
985 | if (exe) { | ||
986 | if (!(phdr.p_flags & PF_X)) | ||
987 | continue; | ||
988 | } else { | ||
989 | if (!(phdr.p_flags & PF_R)) | ||
990 | continue; | ||
991 | } | ||
992 | sz = min(phdr.p_memsz, phdr.p_filesz); | ||
993 | if (!sz) | ||
994 | continue; | ||
995 | err = mapfn(phdr.p_vaddr, sz, phdr.p_offset, data); | ||
996 | if (err) | ||
997 | return err; | ||
998 | } | ||
999 | return 0; | ||
1000 | } | ||
1001 | |||
1002 | int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, | ||
1003 | bool *is_64_bit) | ||
1004 | { | ||
1005 | int err; | ||
1006 | Elf *elf; | ||
1007 | |||
1008 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1009 | if (elf == NULL) | ||
1010 | return -1; | ||
1011 | |||
1012 | if (is_64_bit) | ||
1013 | *is_64_bit = (gelf_getclass(elf) == ELFCLASS64); | ||
1014 | |||
1015 | err = elf_read_maps(elf, exe, mapfn, data); | ||
1016 | |||
1017 | elf_end(elf); | ||
1018 | return err; | ||
1019 | } | ||
1020 | |||
849 | void symbol__elf_init(void) | 1021 | void symbol__elf_init(void) |
850 | { | 1022 | { |
851 | elf_version(EV_CURRENT); | 1023 | elf_version(EV_CURRENT); |
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index a7390cde63bc..3a802c300fc5 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c | |||
@@ -301,6 +301,13 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused, | |||
301 | return 0; | 301 | return 0; |
302 | } | 302 | } |
303 | 303 | ||
304 | int file__read_maps(int fd __maybe_unused, bool exe __maybe_unused, | ||
305 | mapfn_t mapfn __maybe_unused, void *data __maybe_unused, | ||
306 | bool *is_64_bit __maybe_unused) | ||
307 | { | ||
308 | return -1; | ||
309 | } | ||
310 | |||
304 | void symbol__elf_init(void) | 311 | void symbol__elf_init(void) |
305 | { | 312 | { |
306 | } | 313 | } |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index d5528e1cc03a..7eb0362f4ffd 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -87,6 +87,7 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | |||
87 | { | 87 | { |
88 | s64 a; | 88 | s64 a; |
89 | s64 b; | 89 | s64 b; |
90 | size_t na, nb; | ||
90 | 91 | ||
91 | /* Prefer a symbol with non zero length */ | 92 | /* Prefer a symbol with non zero length */ |
92 | a = syma->end - syma->start; | 93 | a = syma->end - syma->start; |
@@ -120,11 +121,21 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb) | |||
120 | else if (a > b) | 121 | else if (a > b) |
121 | return SYMBOL_B; | 122 | return SYMBOL_B; |
122 | 123 | ||
123 | /* If all else fails, choose the symbol with the longest name */ | 124 | /* Choose the symbol with the longest name */ |
124 | if (strlen(syma->name) >= strlen(symb->name)) | 125 | na = strlen(syma->name); |
126 | nb = strlen(symb->name); | ||
127 | if (na > nb) | ||
125 | return SYMBOL_A; | 128 | return SYMBOL_A; |
126 | else | 129 | else if (na < nb) |
130 | return SYMBOL_B; | ||
131 | |||
132 | /* Avoid "SyS" kernel syscall aliases */ | ||
133 | if (na >= 3 && !strncmp(syma->name, "SyS", 3)) | ||
134 | return SYMBOL_B; | ||
135 | if (na >= 10 && !strncmp(syma->name, "compat_SyS", 10)) | ||
127 | return SYMBOL_B; | 136 | return SYMBOL_B; |
137 | |||
138 | return SYMBOL_A; | ||
128 | } | 139 | } |
129 | 140 | ||
130 | void symbols__fixup_duplicate(struct rb_root *symbols) | 141 | void symbols__fixup_duplicate(struct rb_root *symbols) |
@@ -248,7 +259,10 @@ size_t symbol__fprintf_symname_offs(const struct symbol *sym, | |||
248 | if (sym && sym->name) { | 259 | if (sym && sym->name) { |
249 | length = fprintf(fp, "%s", sym->name); | 260 | length = fprintf(fp, "%s", sym->name); |
250 | if (al) { | 261 | if (al) { |
251 | offset = al->addr - sym->start; | 262 | if (al->addr < sym->end) |
263 | offset = al->addr - sym->start; | ||
264 | else | ||
265 | offset = al->addr - al->map->start - sym->start; | ||
252 | length += fprintf(fp, "+0x%lx", offset); | 266 | length += fprintf(fp, "+0x%lx", offset); |
253 | } | 267 | } |
254 | return length; | 268 | return length; |
@@ -316,6 +330,16 @@ static struct symbol *symbols__find(struct rb_root *symbols, u64 ip) | |||
316 | return NULL; | 330 | return NULL; |
317 | } | 331 | } |
318 | 332 | ||
333 | static struct symbol *symbols__first(struct rb_root *symbols) | ||
334 | { | ||
335 | struct rb_node *n = rb_first(symbols); | ||
336 | |||
337 | if (n) | ||
338 | return rb_entry(n, struct symbol, rb_node); | ||
339 | |||
340 | return NULL; | ||
341 | } | ||
342 | |||
319 | struct symbol_name_rb_node { | 343 | struct symbol_name_rb_node { |
320 | struct rb_node rb_node; | 344 | struct rb_node rb_node; |
321 | struct symbol sym; | 345 | struct symbol sym; |
@@ -386,6 +410,11 @@ struct symbol *dso__find_symbol(struct dso *dso, | |||
386 | return symbols__find(&dso->symbols[type], addr); | 410 | return symbols__find(&dso->symbols[type], addr); |
387 | } | 411 | } |
388 | 412 | ||
413 | struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) | ||
414 | { | ||
415 | return symbols__first(&dso->symbols[type]); | ||
416 | } | ||
417 | |||
389 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | 418 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, |
390 | const char *name) | 419 | const char *name) |
391 | { | 420 | { |
@@ -522,6 +551,53 @@ static int dso__load_all_kallsyms(struct dso *dso, const char *filename, | |||
522 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); | 551 | return kallsyms__parse(filename, &args, map__process_kallsym_symbol); |
523 | } | 552 | } |
524 | 553 | ||
554 | static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map, | ||
555 | symbol_filter_t filter) | ||
556 | { | ||
557 | struct map_groups *kmaps = map__kmap(map)->kmaps; | ||
558 | struct map *curr_map; | ||
559 | struct symbol *pos; | ||
560 | int count = 0, moved = 0; | ||
561 | struct rb_root *root = &dso->symbols[map->type]; | ||
562 | struct rb_node *next = rb_first(root); | ||
563 | |||
564 | while (next) { | ||
565 | char *module; | ||
566 | |||
567 | pos = rb_entry(next, struct symbol, rb_node); | ||
568 | next = rb_next(&pos->rb_node); | ||
569 | |||
570 | module = strchr(pos->name, '\t'); | ||
571 | if (module) | ||
572 | *module = '\0'; | ||
573 | |||
574 | curr_map = map_groups__find(kmaps, map->type, pos->start); | ||
575 | |||
576 | if (!curr_map || (filter && filter(curr_map, pos))) { | ||
577 | rb_erase(&pos->rb_node, root); | ||
578 | symbol__delete(pos); | ||
579 | } else { | ||
580 | pos->start -= curr_map->start - curr_map->pgoff; | ||
581 | if (pos->end) | ||
582 | pos->end -= curr_map->start - curr_map->pgoff; | ||
583 | if (curr_map != map) { | ||
584 | rb_erase(&pos->rb_node, root); | ||
585 | symbols__insert( | ||
586 | &curr_map->dso->symbols[curr_map->type], | ||
587 | pos); | ||
588 | ++moved; | ||
589 | } else { | ||
590 | ++count; | ||
591 | } | ||
592 | } | ||
593 | } | ||
594 | |||
595 | /* Symbols have been adjusted */ | ||
596 | dso->adjust_symbols = 1; | ||
597 | |||
598 | return count + moved; | ||
599 | } | ||
600 | |||
525 | /* | 601 | /* |
526 | * Split the symbols into maps, making sure there are no overlaps, i.e. the | 602 | * Split the symbols into maps, making sure there are no overlaps, i.e. the |
527 | * kernel range is broken in several maps, named [kernel].N, as we don't have | 603 | * kernel range is broken in several maps, named [kernel].N, as we don't have |
@@ -663,6 +739,161 @@ bool symbol__restricted_filename(const char *filename, | |||
663 | return restricted; | 739 | return restricted; |
664 | } | 740 | } |
665 | 741 | ||
742 | struct kcore_mapfn_data { | ||
743 | struct dso *dso; | ||
744 | enum map_type type; | ||
745 | struct list_head maps; | ||
746 | }; | ||
747 | |||
748 | static int kcore_mapfn(u64 start, u64 len, u64 pgoff, void *data) | ||
749 | { | ||
750 | struct kcore_mapfn_data *md = data; | ||
751 | struct map *map; | ||
752 | |||
753 | map = map__new2(start, md->dso, md->type); | ||
754 | if (map == NULL) | ||
755 | return -ENOMEM; | ||
756 | |||
757 | map->end = map->start + len; | ||
758 | map->pgoff = pgoff; | ||
759 | |||
760 | list_add(&map->node, &md->maps); | ||
761 | |||
762 | return 0; | ||
763 | } | ||
764 | |||
765 | /* | ||
766 | * If kallsyms is referenced by name then we look for kcore in the same | ||
767 | * directory. | ||
768 | */ | ||
769 | static bool kcore_filename_from_kallsyms_filename(char *kcore_filename, | ||
770 | const char *kallsyms_filename) | ||
771 | { | ||
772 | char *name; | ||
773 | |||
774 | strcpy(kcore_filename, kallsyms_filename); | ||
775 | name = strrchr(kcore_filename, '/'); | ||
776 | if (!name) | ||
777 | return false; | ||
778 | |||
779 | if (!strcmp(name, "/kallsyms")) { | ||
780 | strcpy(name, "/kcore"); | ||
781 | return true; | ||
782 | } | ||
783 | |||
784 | return false; | ||
785 | } | ||
786 | |||
787 | static int dso__load_kcore(struct dso *dso, struct map *map, | ||
788 | const char *kallsyms_filename) | ||
789 | { | ||
790 | struct map_groups *kmaps = map__kmap(map)->kmaps; | ||
791 | struct machine *machine = kmaps->machine; | ||
792 | struct kcore_mapfn_data md; | ||
793 | struct map *old_map, *new_map, *replacement_map = NULL; | ||
794 | bool is_64_bit; | ||
795 | int err, fd; | ||
796 | char kcore_filename[PATH_MAX]; | ||
797 | struct symbol *sym; | ||
798 | |||
799 | /* This function requires that the map is the kernel map */ | ||
800 | if (map != machine->vmlinux_maps[map->type]) | ||
801 | return -EINVAL; | ||
802 | |||
803 | if (!kcore_filename_from_kallsyms_filename(kcore_filename, | ||
804 | kallsyms_filename)) | ||
805 | return -EINVAL; | ||
806 | |||
807 | md.dso = dso; | ||
808 | md.type = map->type; | ||
809 | INIT_LIST_HEAD(&md.maps); | ||
810 | |||
811 | fd = open(kcore_filename, O_RDONLY); | ||
812 | if (fd < 0) | ||
813 | return -EINVAL; | ||
814 | |||
815 | /* Read new maps into temporary lists */ | ||
816 | err = file__read_maps(fd, md.type == MAP__FUNCTION, kcore_mapfn, &md, | ||
817 | &is_64_bit); | ||
818 | if (err) | ||
819 | goto out_err; | ||
820 | |||
821 | if (list_empty(&md.maps)) { | ||
822 | err = -EINVAL; | ||
823 | goto out_err; | ||
824 | } | ||
825 | |||
826 | /* Remove old maps */ | ||
827 | old_map = map_groups__first(kmaps, map->type); | ||
828 | while (old_map) { | ||
829 | struct map *next = map_groups__next(old_map); | ||
830 | |||
831 | if (old_map != map) | ||
832 | map_groups__remove(kmaps, old_map); | ||
833 | old_map = next; | ||
834 | } | ||
835 | |||
836 | /* Find the kernel map using the first symbol */ | ||
837 | sym = dso__first_symbol(dso, map->type); | ||
838 | list_for_each_entry(new_map, &md.maps, node) { | ||
839 | if (sym && sym->start >= new_map->start && | ||
840 | sym->start < new_map->end) { | ||
841 | replacement_map = new_map; | ||
842 | break; | ||
843 | } | ||
844 | } | ||
845 | |||
846 | if (!replacement_map) | ||
847 | replacement_map = list_entry(md.maps.next, struct map, node); | ||
848 | |||
849 | /* Add new maps */ | ||
850 | while (!list_empty(&md.maps)) { | ||
851 | new_map = list_entry(md.maps.next, struct map, node); | ||
852 | list_del(&new_map->node); | ||
853 | if (new_map == replacement_map) { | ||
854 | map->start = new_map->start; | ||
855 | map->end = new_map->end; | ||
856 | map->pgoff = new_map->pgoff; | ||
857 | map->map_ip = new_map->map_ip; | ||
858 | map->unmap_ip = new_map->unmap_ip; | ||
859 | map__delete(new_map); | ||
860 | /* Ensure maps are correctly ordered */ | ||
861 | map_groups__remove(kmaps, map); | ||
862 | map_groups__insert(kmaps, map); | ||
863 | } else { | ||
864 | map_groups__insert(kmaps, new_map); | ||
865 | } | ||
866 | } | ||
867 | |||
868 | /* | ||
869 | * Set the data type and long name so that kcore can be read via | ||
870 | * dso__data_read_addr(). | ||
871 | */ | ||
872 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | ||
873 | dso->data_type = DSO_BINARY_TYPE__GUEST_KCORE; | ||
874 | else | ||
875 | dso->data_type = DSO_BINARY_TYPE__KCORE; | ||
876 | dso__set_long_name(dso, strdup(kcore_filename)); | ||
877 | |||
878 | close(fd); | ||
879 | |||
880 | if (map->type == MAP__FUNCTION) | ||
881 | pr_debug("Using %s for kernel object code\n", kcore_filename); | ||
882 | else | ||
883 | pr_debug("Using %s for kernel data\n", kcore_filename); | ||
884 | |||
885 | return 0; | ||
886 | |||
887 | out_err: | ||
888 | while (!list_empty(&md.maps)) { | ||
889 | map = list_entry(md.maps.next, struct map, node); | ||
890 | list_del(&map->node); | ||
891 | map__delete(map); | ||
892 | } | ||
893 | close(fd); | ||
894 | return -EINVAL; | ||
895 | } | ||
896 | |||
666 | int dso__load_kallsyms(struct dso *dso, const char *filename, | 897 | int dso__load_kallsyms(struct dso *dso, const char *filename, |
667 | struct map *map, symbol_filter_t filter) | 898 | struct map *map, symbol_filter_t filter) |
668 | { | 899 | { |
@@ -680,7 +911,10 @@ int dso__load_kallsyms(struct dso *dso, const char *filename, | |||
680 | else | 911 | else |
681 | dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS; | 912 | dso->symtab_type = DSO_BINARY_TYPE__KALLSYMS; |
682 | 913 | ||
683 | return dso__split_kallsyms(dso, map, filter); | 914 | if (!dso__load_kcore(dso, map, filename)) |
915 | return dso__split_kallsyms_for_kcore(dso, map, filter); | ||
916 | else | ||
917 | return dso__split_kallsyms(dso, map, filter); | ||
684 | } | 918 | } |
685 | 919 | ||
686 | static int dso__load_perf_map(struct dso *dso, struct map *map, | 920 | static int dso__load_perf_map(struct dso *dso, struct map *map, |
@@ -843,10 +1077,15 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) | |||
843 | if (!runtime_ss && syms_ss) | 1077 | if (!runtime_ss && syms_ss) |
844 | runtime_ss = syms_ss; | 1078 | runtime_ss = syms_ss; |
845 | 1079 | ||
846 | if (syms_ss) | 1080 | if (syms_ss) { |
847 | ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, 0); | 1081 | int km; |
848 | else | 1082 | |
1083 | km = dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || | ||
1084 | dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE; | ||
1085 | ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, km); | ||
1086 | } else { | ||
849 | ret = -1; | 1087 | ret = -1; |
1088 | } | ||
850 | 1089 | ||
851 | if (ret > 0) { | 1090 | if (ret > 0) { |
852 | int nr_plt; | 1091 | int nr_plt; |
@@ -888,8 +1127,11 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, | |||
888 | char symfs_vmlinux[PATH_MAX]; | 1127 | char symfs_vmlinux[PATH_MAX]; |
889 | enum dso_binary_type symtab_type; | 1128 | enum dso_binary_type symtab_type; |
890 | 1129 | ||
891 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", | 1130 | if (vmlinux[0] == '/') |
892 | symbol_conf.symfs, vmlinux); | 1131 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s", vmlinux); |
1132 | else | ||
1133 | snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s", | ||
1134 | symbol_conf.symfs, vmlinux); | ||
893 | 1135 | ||
894 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | 1136 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) |
895 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; | 1137 | symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX; |
@@ -903,6 +1145,10 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, | |||
903 | symsrc__destroy(&ss); | 1145 | symsrc__destroy(&ss); |
904 | 1146 | ||
905 | if (err > 0) { | 1147 | if (err > 0) { |
1148 | if (dso->kernel == DSO_TYPE_GUEST_KERNEL) | ||
1149 | dso->data_type = DSO_BINARY_TYPE__GUEST_VMLINUX; | ||
1150 | else | ||
1151 | dso->data_type = DSO_BINARY_TYPE__VMLINUX; | ||
906 | dso__set_long_name(dso, (char *)vmlinux); | 1152 | dso__set_long_name(dso, (char *)vmlinux); |
907 | dso__set_loaded(dso, map->type); | 1153 | dso__set_loaded(dso, map->type); |
908 | pr_debug("Using %s for symbols\n", symfs_vmlinux); | 1154 | pr_debug("Using %s for symbols\n", symfs_vmlinux); |
@@ -975,7 +1221,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
975 | dso__set_long_name(dso, | 1221 | dso__set_long_name(dso, |
976 | strdup(symbol_conf.vmlinux_name)); | 1222 | strdup(symbol_conf.vmlinux_name)); |
977 | dso->lname_alloc = 1; | 1223 | dso->lname_alloc = 1; |
978 | goto out_fixup; | 1224 | return err; |
979 | } | 1225 | } |
980 | return err; | 1226 | return err; |
981 | } | 1227 | } |
@@ -983,7 +1229,7 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, | |||
983 | if (vmlinux_path != NULL) { | 1229 | if (vmlinux_path != NULL) { |
984 | err = dso__load_vmlinux_path(dso, map, filter); | 1230 | err = dso__load_vmlinux_path(dso, map, filter); |
985 | if (err > 0) | 1231 | if (err > 0) |
986 | goto out_fixup; | 1232 | return err; |
987 | } | 1233 | } |
988 | 1234 | ||
989 | /* do not try local files if a symfs was given */ | 1235 | /* do not try local files if a symfs was given */ |
@@ -1042,9 +1288,8 @@ do_kallsyms: | |||
1042 | pr_debug("Using %s for symbols\n", kallsyms_filename); | 1288 | pr_debug("Using %s for symbols\n", kallsyms_filename); |
1043 | free(kallsyms_allocated_filename); | 1289 | free(kallsyms_allocated_filename); |
1044 | 1290 | ||
1045 | if (err > 0) { | 1291 | if (err > 0 && !dso__is_kcore(dso)) { |
1046 | dso__set_long_name(dso, strdup("[kernel.kallsyms]")); | 1292 | dso__set_long_name(dso, strdup("[kernel.kallsyms]")); |
1047 | out_fixup: | ||
1048 | map__fixup_start(map); | 1293 | map__fixup_start(map); |
1049 | map__fixup_end(map); | 1294 | map__fixup_end(map); |
1050 | } | 1295 | } |
@@ -1075,7 +1320,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | |||
1075 | if (symbol_conf.default_guest_vmlinux_name != NULL) { | 1320 | if (symbol_conf.default_guest_vmlinux_name != NULL) { |
1076 | err = dso__load_vmlinux(dso, map, | 1321 | err = dso__load_vmlinux(dso, map, |
1077 | symbol_conf.default_guest_vmlinux_name, filter); | 1322 | symbol_conf.default_guest_vmlinux_name, filter); |
1078 | goto out_try_fixup; | 1323 | return err; |
1079 | } | 1324 | } |
1080 | 1325 | ||
1081 | kallsyms_filename = symbol_conf.default_guest_kallsyms; | 1326 | kallsyms_filename = symbol_conf.default_guest_kallsyms; |
@@ -1089,13 +1334,9 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, | |||
1089 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); | 1334 | err = dso__load_kallsyms(dso, kallsyms_filename, map, filter); |
1090 | if (err > 0) | 1335 | if (err > 0) |
1091 | pr_debug("Using %s for symbols\n", kallsyms_filename); | 1336 | pr_debug("Using %s for symbols\n", kallsyms_filename); |
1092 | 1337 | if (err > 0 && !dso__is_kcore(dso)) { | |
1093 | out_try_fixup: | 1338 | machine__mmap_name(machine, path, sizeof(path)); |
1094 | if (err > 0) { | 1339 | dso__set_long_name(dso, strdup(path)); |
1095 | if (kallsyms_filename != NULL) { | ||
1096 | machine__mmap_name(machine, path, sizeof(path)); | ||
1097 | dso__set_long_name(dso, strdup(path)); | ||
1098 | } | ||
1099 | map__fixup_start(map); | 1340 | map__fixup_start(map); |
1100 | map__fixup_end(map); | 1341 | map__fixup_end(map); |
1101 | } | 1342 | } |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 5f720dc076da..fd5b70ea2981 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -215,6 +215,7 @@ struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, | |||
215 | u64 addr); | 215 | u64 addr); |
216 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, | 216 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, |
217 | const char *name); | 217 | const char *name); |
218 | struct symbol *dso__first_symbol(struct dso *dso, enum map_type type); | ||
218 | 219 | ||
219 | int filename__read_build_id(const char *filename, void *bf, size_t size); | 220 | int filename__read_build_id(const char *filename, void *bf, size_t size); |
220 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); | 221 | int sysfs__read_build_id(const char *filename, void *bf, size_t size); |
@@ -247,4 +248,8 @@ void symbols__fixup_duplicate(struct rb_root *symbols); | |||
247 | void symbols__fixup_end(struct rb_root *symbols); | 248 | void symbols__fixup_end(struct rb_root *symbols); |
248 | void __map_groups__fixup_end(struct map_groups *mg, enum map_type type); | 249 | void __map_groups__fixup_end(struct map_groups *mg, enum map_type type); |
249 | 250 | ||
251 | typedef int (*mapfn_t)(u64 start, u64 len, u64 pgoff, void *data); | ||
252 | int file__read_maps(int fd, bool exe, mapfn_t mapfn, void *data, | ||
253 | bool *is_64_bit); | ||
254 | |||
250 | #endif /* __PERF_SYMBOL */ | 255 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 40399cbcca77..e3d4a550a703 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -7,17 +7,18 @@ | |||
7 | #include "util.h" | 7 | #include "util.h" |
8 | #include "debug.h" | 8 | #include "debug.h" |
9 | 9 | ||
10 | struct thread *thread__new(pid_t pid) | 10 | struct thread *thread__new(pid_t pid, pid_t tid) |
11 | { | 11 | { |
12 | struct thread *self = zalloc(sizeof(*self)); | 12 | struct thread *self = zalloc(sizeof(*self)); |
13 | 13 | ||
14 | if (self != NULL) { | 14 | if (self != NULL) { |
15 | map_groups__init(&self->mg); | 15 | map_groups__init(&self->mg); |
16 | self->pid = pid; | 16 | self->pid_ = pid; |
17 | self->tid = tid; | ||
17 | self->ppid = -1; | 18 | self->ppid = -1; |
18 | self->comm = malloc(32); | 19 | self->comm = malloc(32); |
19 | if (self->comm) | 20 | if (self->comm) |
20 | snprintf(self->comm, 32, ":%d", self->pid); | 21 | snprintf(self->comm, 32, ":%d", self->tid); |
21 | } | 22 | } |
22 | 23 | ||
23 | return self; | 24 | return self; |
@@ -57,7 +58,7 @@ int thread__comm_len(struct thread *self) | |||
57 | 58 | ||
58 | size_t thread__fprintf(struct thread *thread, FILE *fp) | 59 | size_t thread__fprintf(struct thread *thread, FILE *fp) |
59 | { | 60 | { |
60 | return fprintf(fp, "Thread %d %s\n", thread->pid, thread->comm) + | 61 | return fprintf(fp, "Thread %d %s\n", thread->tid, thread->comm) + |
61 | map_groups__fprintf(&thread->mg, verbose, fp); | 62 | map_groups__fprintf(&thread->mg, verbose, fp); |
62 | } | 63 | } |
63 | 64 | ||
@@ -84,7 +85,7 @@ int thread__fork(struct thread *self, struct thread *parent) | |||
84 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) | 85 | if (map_groups__clone(&self->mg, &parent->mg, i) < 0) |
85 | return -ENOMEM; | 86 | return -ENOMEM; |
86 | 87 | ||
87 | self->ppid = parent->pid; | 88 | self->ppid = parent->tid; |
88 | 89 | ||
89 | return 0; | 90 | return 0; |
90 | } | 91 | } |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index eeb7ac62b9e3..4ebbb40d46d4 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -12,10 +12,12 @@ struct thread { | |||
12 | struct list_head node; | 12 | struct list_head node; |
13 | }; | 13 | }; |
14 | struct map_groups mg; | 14 | struct map_groups mg; |
15 | pid_t pid; | 15 | pid_t pid_; /* Not all tools update this */ |
16 | pid_t tid; | ||
16 | pid_t ppid; | 17 | pid_t ppid; |
17 | char shortname[3]; | 18 | char shortname[3]; |
18 | bool comm_set; | 19 | bool comm_set; |
20 | bool dead; /* if set thread has exited */ | ||
19 | char *comm; | 21 | char *comm; |
20 | int comm_len; | 22 | int comm_len; |
21 | 23 | ||
@@ -24,8 +26,12 @@ struct thread { | |||
24 | 26 | ||
25 | struct machine; | 27 | struct machine; |
26 | 28 | ||
27 | struct thread *thread__new(pid_t pid); | 29 | struct thread *thread__new(pid_t pid, pid_t tid); |
28 | void thread__delete(struct thread *self); | 30 | void thread__delete(struct thread *self); |
31 | static inline void thread__exited(struct thread *thread) | ||
32 | { | ||
33 | thread->dead = true; | ||
34 | } | ||
29 | 35 | ||
30 | int thread__set_comm(struct thread *self, const char *comm); | 36 | int thread__set_comm(struct thread *self, const char *comm); |
31 | int thread__comm_len(struct thread *self); | 37 | int thread__comm_len(struct thread *self); |
@@ -45,6 +51,15 @@ void thread__find_addr_map(struct thread *thread, struct machine *machine, | |||
45 | 51 | ||
46 | void thread__find_addr_location(struct thread *thread, struct machine *machine, | 52 | void thread__find_addr_location(struct thread *thread, struct machine *machine, |
47 | u8 cpumode, enum map_type type, u64 addr, | 53 | u8 cpumode, enum map_type type, u64 addr, |
48 | struct addr_location *al, | 54 | struct addr_location *al); |
49 | symbol_filter_t filter); | 55 | |
56 | static inline void *thread__priv(struct thread *thread) | ||
57 | { | ||
58 | return thread->priv; | ||
59 | } | ||
60 | |||
61 | static inline void thread__set_priv(struct thread *thread, void *p) | ||
62 | { | ||
63 | thread->priv = p; | ||
64 | } | ||
50 | #endif /* __PERF_THREAD_H */ | 65 | #endif /* __PERF_THREAD_H */ |
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index b0e1aadba8d5..4385816d3d49 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h | |||
@@ -18,12 +18,9 @@ typedef int (*event_sample)(struct perf_tool *tool, union perf_event *event, | |||
18 | typedef int (*event_op)(struct perf_tool *tool, union perf_event *event, | 18 | typedef int (*event_op)(struct perf_tool *tool, union perf_event *event, |
19 | struct perf_sample *sample, struct machine *machine); | 19 | struct perf_sample *sample, struct machine *machine); |
20 | 20 | ||
21 | typedef int (*event_attr_op)(union perf_event *event, | 21 | typedef int (*event_attr_op)(struct perf_tool *tool, |
22 | union perf_event *event, | ||
22 | struct perf_evlist **pevlist); | 23 | struct perf_evlist **pevlist); |
23 | typedef int (*event_simple_op)(struct perf_tool *tool, union perf_event *event); | ||
24 | |||
25 | typedef int (*event_synth_op)(union perf_event *event, | ||
26 | struct perf_session *session); | ||
27 | 24 | ||
28 | typedef int (*event_op2)(struct perf_tool *tool, union perf_event *event, | 25 | typedef int (*event_op2)(struct perf_tool *tool, union perf_event *event, |
29 | struct perf_session *session); | 26 | struct perf_session *session); |
@@ -32,6 +29,7 @@ struct perf_tool { | |||
32 | event_sample sample, | 29 | event_sample sample, |
33 | read; | 30 | read; |
34 | event_op mmap, | 31 | event_op mmap, |
32 | mmap2, | ||
35 | comm, | 33 | comm, |
36 | fork, | 34 | fork, |
37 | exit, | 35 | exit, |
@@ -39,8 +37,7 @@ struct perf_tool { | |||
39 | throttle, | 37 | throttle, |
40 | unthrottle; | 38 | unthrottle; |
41 | event_attr_op attr; | 39 | event_attr_op attr; |
42 | event_synth_op tracing_data; | 40 | event_op2 tracing_data; |
43 | event_simple_op event_type; | ||
44 | event_op2 finished_round, | 41 | event_op2 finished_round, |
45 | build_id; | 42 | build_id; |
46 | bool ordered_samples; | 43 | bool ordered_samples; |
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index df46be93d902..b554ffc462b6 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -39,6 +39,8 @@ struct perf_top { | |||
39 | float min_percent; | 39 | float min_percent; |
40 | }; | 40 | }; |
41 | 41 | ||
42 | #define CONSOLE_CLEAR "[H[2J" | ||
43 | |||
42 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | 44 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); |
43 | void perf_top__reset_sample_counters(struct perf_top *top); | 45 | void perf_top__reset_sample_counters(struct perf_top *top); |
44 | #endif /* __PERF_TOP_H */ | 46 | #endif /* __PERF_TOP_H */ |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 3917eb9a8479..f3c9e551bd35 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -46,65 +46,6 @@ | |||
46 | static int output_fd; | 46 | static int output_fd; |
47 | 47 | ||
48 | 48 | ||
49 | static const char *find_debugfs(void) | ||
50 | { | ||
51 | const char *path = perf_debugfs_mount(NULL); | ||
52 | |||
53 | if (!path) | ||
54 | pr_debug("Your kernel does not support the debugfs filesystem"); | ||
55 | |||
56 | return path; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * Finds the path to the debugfs/tracing | ||
61 | * Allocates the string and stores it. | ||
62 | */ | ||
63 | static const char *find_tracing_dir(void) | ||
64 | { | ||
65 | static char *tracing; | ||
66 | static int tracing_found; | ||
67 | const char *debugfs; | ||
68 | |||
69 | if (tracing_found) | ||
70 | return tracing; | ||
71 | |||
72 | debugfs = find_debugfs(); | ||
73 | if (!debugfs) | ||
74 | return NULL; | ||
75 | |||
76 | tracing = malloc(strlen(debugfs) + 9); | ||
77 | if (!tracing) | ||
78 | return NULL; | ||
79 | |||
80 | sprintf(tracing, "%s/tracing", debugfs); | ||
81 | |||
82 | tracing_found = 1; | ||
83 | return tracing; | ||
84 | } | ||
85 | |||
86 | static char *get_tracing_file(const char *name) | ||
87 | { | ||
88 | const char *tracing; | ||
89 | char *file; | ||
90 | |||
91 | tracing = find_tracing_dir(); | ||
92 | if (!tracing) | ||
93 | return NULL; | ||
94 | |||
95 | file = malloc(strlen(tracing) + strlen(name) + 2); | ||
96 | if (!file) | ||
97 | return NULL; | ||
98 | |||
99 | sprintf(file, "%s/%s", tracing, name); | ||
100 | return file; | ||
101 | } | ||
102 | |||
103 | static void put_tracing_file(char *file) | ||
104 | { | ||
105 | free(file); | ||
106 | } | ||
107 | |||
108 | int bigendian(void) | 49 | int bigendian(void) |
109 | { | 50 | { |
110 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0}; | 51 | unsigned char str[] = { 0x1, 0x2, 0x3, 0x4, 0x0, 0x0, 0x0, 0x0}; |
@@ -160,7 +101,7 @@ out: | |||
160 | return err; | 101 | return err; |
161 | } | 102 | } |
162 | 103 | ||
163 | static int read_header_files(void) | 104 | static int record_header_files(void) |
164 | { | 105 | { |
165 | char *path; | 106 | char *path; |
166 | struct stat st; | 107 | struct stat st; |
@@ -299,7 +240,7 @@ out: | |||
299 | return err; | 240 | return err; |
300 | } | 241 | } |
301 | 242 | ||
302 | static int read_ftrace_files(struct tracepoint_path *tps) | 243 | static int record_ftrace_files(struct tracepoint_path *tps) |
303 | { | 244 | { |
304 | char *path; | 245 | char *path; |
305 | int ret; | 246 | int ret; |
@@ -328,7 +269,7 @@ static bool system_in_tp_list(char *sys, struct tracepoint_path *tps) | |||
328 | return false; | 269 | return false; |
329 | } | 270 | } |
330 | 271 | ||
331 | static int read_event_files(struct tracepoint_path *tps) | 272 | static int record_event_files(struct tracepoint_path *tps) |
332 | { | 273 | { |
333 | struct dirent *dent; | 274 | struct dirent *dent; |
334 | struct stat st; | 275 | struct stat st; |
@@ -403,7 +344,7 @@ out: | |||
403 | return err; | 344 | return err; |
404 | } | 345 | } |
405 | 346 | ||
406 | static int read_proc_kallsyms(void) | 347 | static int record_proc_kallsyms(void) |
407 | { | 348 | { |
408 | unsigned int size; | 349 | unsigned int size; |
409 | const char *path = "/proc/kallsyms"; | 350 | const char *path = "/proc/kallsyms"; |
@@ -421,7 +362,7 @@ static int read_proc_kallsyms(void) | |||
421 | return record_file(path, 4); | 362 | return record_file(path, 4); |
422 | } | 363 | } |
423 | 364 | ||
424 | static int read_ftrace_printk(void) | 365 | static int record_ftrace_printk(void) |
425 | { | 366 | { |
426 | unsigned int size; | 367 | unsigned int size; |
427 | char *path; | 368 | char *path; |
@@ -473,12 +414,27 @@ get_tracepoints_path(struct list_head *pattrs) | |||
473 | if (pos->attr.type != PERF_TYPE_TRACEPOINT) | 414 | if (pos->attr.type != PERF_TYPE_TRACEPOINT) |
474 | continue; | 415 | continue; |
475 | ++nr_tracepoints; | 416 | ++nr_tracepoints; |
417 | |||
418 | if (pos->name) { | ||
419 | ppath->next = tracepoint_name_to_path(pos->name); | ||
420 | if (ppath->next) | ||
421 | goto next; | ||
422 | |||
423 | if (strchr(pos->name, ':') == NULL) | ||
424 | goto try_id; | ||
425 | |||
426 | goto error; | ||
427 | } | ||
428 | |||
429 | try_id: | ||
476 | ppath->next = tracepoint_id_to_path(pos->attr.config); | 430 | ppath->next = tracepoint_id_to_path(pos->attr.config); |
477 | if (!ppath->next) { | 431 | if (!ppath->next) { |
432 | error: | ||
478 | pr_debug("No memory to alloc tracepoints list\n"); | 433 | pr_debug("No memory to alloc tracepoints list\n"); |
479 | put_tracepoints_path(&path); | 434 | put_tracepoints_path(&path); |
480 | return NULL; | 435 | return NULL; |
481 | } | 436 | } |
437 | next: | ||
482 | ppath = ppath->next; | 438 | ppath = ppath->next; |
483 | } | 439 | } |
484 | 440 | ||
@@ -520,8 +476,6 @@ static int tracing_data_header(void) | |||
520 | else | 476 | else |
521 | buf[0] = 0; | 477 | buf[0] = 0; |
522 | 478 | ||
523 | read_trace_init(buf[0], buf[0]); | ||
524 | |||
525 | if (write(output_fd, buf, 1) != 1) | 479 | if (write(output_fd, buf, 1) != 1) |
526 | return -1; | 480 | return -1; |
527 | 481 | ||
@@ -583,19 +537,19 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs, | |||
583 | err = tracing_data_header(); | 537 | err = tracing_data_header(); |
584 | if (err) | 538 | if (err) |
585 | goto out; | 539 | goto out; |
586 | err = read_header_files(); | 540 | err = record_header_files(); |
587 | if (err) | 541 | if (err) |
588 | goto out; | 542 | goto out; |
589 | err = read_ftrace_files(tps); | 543 | err = record_ftrace_files(tps); |
590 | if (err) | 544 | if (err) |
591 | goto out; | 545 | goto out; |
592 | err = read_event_files(tps); | 546 | err = record_event_files(tps); |
593 | if (err) | 547 | if (err) |
594 | goto out; | 548 | goto out; |
595 | err = read_proc_kallsyms(); | 549 | err = record_proc_kallsyms(); |
596 | if (err) | 550 | if (err) |
597 | goto out; | 551 | goto out; |
598 | err = read_ftrace_printk(); | 552 | err = record_ftrace_printk(); |
599 | 553 | ||
600 | out: | 554 | out: |
601 | /* | 555 | /* |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 4454835a9ebc..e9e1c03f927d 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -28,12 +28,6 @@ | |||
28 | #include "util.h" | 28 | #include "util.h" |
29 | #include "trace-event.h" | 29 | #include "trace-event.h" |
30 | 30 | ||
31 | int header_page_size_size; | ||
32 | int header_page_ts_size; | ||
33 | int header_page_data_offset; | ||
34 | |||
35 | bool latency_format; | ||
36 | |||
37 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian) | 31 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian) |
38 | { | 32 | { |
39 | struct pevent *pevent = pevent_alloc(); | 33 | struct pevent *pevent = pevent_alloc(); |
@@ -192,7 +186,7 @@ void parse_proc_kallsyms(struct pevent *pevent, | |||
192 | char *next = NULL; | 186 | char *next = NULL; |
193 | char *addr_str; | 187 | char *addr_str; |
194 | char *mod; | 188 | char *mod; |
195 | char *fmt; | 189 | char *fmt = NULL; |
196 | 190 | ||
197 | line = strtok_r(file, "\n", &next); | 191 | line = strtok_r(file, "\n", &next); |
198 | while (line) { | 192 | while (line) { |
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index af215c0d2379..f2112270c663 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -39,10 +39,6 @@ | |||
39 | 39 | ||
40 | static int input_fd; | 40 | static int input_fd; |
41 | 41 | ||
42 | int file_bigendian; | ||
43 | int host_bigendian; | ||
44 | static int long_size; | ||
45 | |||
46 | static ssize_t trace_data_size; | 42 | static ssize_t trace_data_size; |
47 | static bool repipe; | 43 | static bool repipe; |
48 | 44 | ||
@@ -216,7 +212,7 @@ static int read_ftrace_printk(struct pevent *pevent) | |||
216 | static int read_header_files(struct pevent *pevent) | 212 | static int read_header_files(struct pevent *pevent) |
217 | { | 213 | { |
218 | unsigned long long size; | 214 | unsigned long long size; |
219 | char *header_event; | 215 | char *header_page; |
220 | char buf[BUFSIZ]; | 216 | char buf[BUFSIZ]; |
221 | int ret = 0; | 217 | int ret = 0; |
222 | 218 | ||
@@ -229,13 +225,26 @@ static int read_header_files(struct pevent *pevent) | |||
229 | } | 225 | } |
230 | 226 | ||
231 | size = read8(pevent); | 227 | size = read8(pevent); |
232 | skip(size); | ||
233 | 228 | ||
234 | /* | 229 | header_page = malloc(size); |
235 | * The size field in the page is of type long, | 230 | if (header_page == NULL) |
236 | * use that instead, since it represents the kernel. | 231 | return -1; |
237 | */ | 232 | |
238 | long_size = header_page_size_size; | 233 | if (do_read(header_page, size) < 0) { |
234 | pr_debug("did not read header page"); | ||
235 | free(header_page); | ||
236 | return -1; | ||
237 | } | ||
238 | |||
239 | if (!pevent_parse_header_page(pevent, header_page, size, | ||
240 | pevent_get_long_size(pevent))) { | ||
241 | /* | ||
242 | * The commit field in the page is of type long, | ||
243 | * use that instead, since it represents the kernel. | ||
244 | */ | ||
245 | pevent_set_long_size(pevent, pevent->header_page_size_size); | ||
246 | } | ||
247 | free(header_page); | ||
239 | 248 | ||
240 | if (do_read(buf, 13) < 0) | 249 | if (do_read(buf, 13) < 0) |
241 | return -1; | 250 | return -1; |
@@ -246,14 +255,8 @@ static int read_header_files(struct pevent *pevent) | |||
246 | } | 255 | } |
247 | 256 | ||
248 | size = read8(pevent); | 257 | size = read8(pevent); |
249 | header_event = malloc(size); | 258 | skip(size); |
250 | if (header_event == NULL) | ||
251 | return -1; | ||
252 | |||
253 | if (do_read(header_event, size) < 0) | ||
254 | ret = -1; | ||
255 | 259 | ||
256 | free(header_event); | ||
257 | return ret; | 260 | return ret; |
258 | } | 261 | } |
259 | 262 | ||
@@ -349,6 +352,10 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) | |||
349 | int show_funcs = 0; | 352 | int show_funcs = 0; |
350 | int show_printk = 0; | 353 | int show_printk = 0; |
351 | ssize_t size = -1; | 354 | ssize_t size = -1; |
355 | int file_bigendian; | ||
356 | int host_bigendian; | ||
357 | int file_long_size; | ||
358 | int file_page_size; | ||
352 | struct pevent *pevent; | 359 | struct pevent *pevent; |
353 | int err; | 360 | int err; |
354 | 361 | ||
@@ -391,12 +398,15 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) | |||
391 | 398 | ||
392 | if (do_read(buf, 1) < 0) | 399 | if (do_read(buf, 1) < 0) |
393 | goto out; | 400 | goto out; |
394 | long_size = buf[0]; | 401 | file_long_size = buf[0]; |
395 | 402 | ||
396 | page_size = read4(pevent); | 403 | file_page_size = read4(pevent); |
397 | if (!page_size) | 404 | if (!file_page_size) |
398 | goto out; | 405 | goto out; |
399 | 406 | ||
407 | pevent_set_long_size(pevent, file_long_size); | ||
408 | pevent_set_page_size(pevent, file_page_size); | ||
409 | |||
400 | err = read_header_files(pevent); | 410 | err = read_header_files(pevent); |
401 | if (err) | 411 | if (err) |
402 | goto out; | 412 | goto out; |
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 8715a1006d00..95199e4eea97 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -39,7 +39,8 @@ static void process_event_unsupported(union perf_event *event __maybe_unused, | |||
39 | struct perf_sample *sample __maybe_unused, | 39 | struct perf_sample *sample __maybe_unused, |
40 | struct perf_evsel *evsel __maybe_unused, | 40 | struct perf_evsel *evsel __maybe_unused, |
41 | struct machine *machine __maybe_unused, | 41 | struct machine *machine __maybe_unused, |
42 | struct addr_location *al __maybe_unused) | 42 | struct thread *thread __maybe_unused, |
43 | struct addr_location *al __maybe_unused) | ||
43 | { | 44 | { |
44 | } | 45 | } |
45 | 46 | ||
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 1978c398ad87..fafe1a40444a 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h | |||
@@ -1,32 +1,18 @@ | |||
1 | #ifndef _PERF_UTIL_TRACE_EVENT_H | 1 | #ifndef _PERF_UTIL_TRACE_EVENT_H |
2 | #define _PERF_UTIL_TRACE_EVENT_H | 2 | #define _PERF_UTIL_TRACE_EVENT_H |
3 | 3 | ||
4 | #include <traceevent/event-parse.h> | ||
4 | #include "parse-events.h" | 5 | #include "parse-events.h" |
5 | #include "event-parse.h" | ||
6 | #include "session.h" | 6 | #include "session.h" |
7 | 7 | ||
8 | struct machine; | 8 | struct machine; |
9 | struct perf_sample; | 9 | struct perf_sample; |
10 | union perf_event; | 10 | union perf_event; |
11 | struct perf_tool; | 11 | struct perf_tool; |
12 | struct thread; | ||
12 | 13 | ||
13 | extern int header_page_size_size; | ||
14 | extern int header_page_ts_size; | ||
15 | extern int header_page_data_offset; | ||
16 | |||
17 | extern bool latency_format; | ||
18 | extern struct pevent *perf_pevent; | 14 | extern struct pevent *perf_pevent; |
19 | 15 | ||
20 | enum { | ||
21 | RINGBUF_TYPE_PADDING = 29, | ||
22 | RINGBUF_TYPE_TIME_EXTEND = 30, | ||
23 | RINGBUF_TYPE_TIME_STAMP = 31, | ||
24 | }; | ||
25 | |||
26 | #ifndef TS_SHIFT | ||
27 | #define TS_SHIFT 27 | ||
28 | #endif | ||
29 | |||
30 | int bigendian(void); | 16 | int bigendian(void); |
31 | 17 | ||
32 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian); | 18 | struct pevent *read_trace_init(int file_bigendian, int host_bigendian); |
@@ -83,7 +69,8 @@ struct scripting_ops { | |||
83 | struct perf_sample *sample, | 69 | struct perf_sample *sample, |
84 | struct perf_evsel *evsel, | 70 | struct perf_evsel *evsel, |
85 | struct machine *machine, | 71 | struct machine *machine, |
86 | struct addr_location *al); | 72 | struct thread *thread, |
73 | struct addr_location *al); | ||
87 | int (*generate_script) (struct pevent *pevent, const char *outfile); | 74 | int (*generate_script) (struct pevent *pevent, const char *outfile); |
88 | }; | 75 | }; |
89 | 76 | ||
diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c index 958723ba3d2e..2f891f7e70bf 100644 --- a/tools/perf/util/unwind.c +++ b/tools/perf/util/unwind.c | |||
@@ -473,7 +473,7 @@ static int entry(u64 ip, struct thread *thread, struct machine *machine, | |||
473 | 473 | ||
474 | thread__find_addr_location(thread, machine, | 474 | thread__find_addr_location(thread, machine, |
475 | PERF_RECORD_MISC_USER, | 475 | PERF_RECORD_MISC_USER, |
476 | MAP__FUNCTION, ip, &al, NULL); | 476 | MAP__FUNCTION, ip, &al); |
477 | 477 | ||
478 | e.ip = ip; | 478 | e.ip = ip; |
479 | e.map = al.map; | 479 | e.map = al.map; |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 59d868add275..6d17b18e915d 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -269,3 +269,95 @@ void perf_debugfs_set_path(const char *mntpt) | |||
269 | snprintf(debugfs_mountpoint, strlen(debugfs_mountpoint), "%s", mntpt); | 269 | snprintf(debugfs_mountpoint, strlen(debugfs_mountpoint), "%s", mntpt); |
270 | set_tracing_events_path(mntpt); | 270 | set_tracing_events_path(mntpt); |
271 | } | 271 | } |
272 | |||
273 | static const char *find_debugfs(void) | ||
274 | { | ||
275 | const char *path = perf_debugfs_mount(NULL); | ||
276 | |||
277 | if (!path) | ||
278 | fprintf(stderr, "Your kernel does not support the debugfs filesystem"); | ||
279 | |||
280 | return path; | ||
281 | } | ||
282 | |||
283 | /* | ||
284 | * Finds the path to the debugfs/tracing | ||
285 | * Allocates the string and stores it. | ||
286 | */ | ||
287 | const char *find_tracing_dir(void) | ||
288 | { | ||
289 | static char *tracing; | ||
290 | static int tracing_found; | ||
291 | const char *debugfs; | ||
292 | |||
293 | if (tracing_found) | ||
294 | return tracing; | ||
295 | |||
296 | debugfs = find_debugfs(); | ||
297 | if (!debugfs) | ||
298 | return NULL; | ||
299 | |||
300 | tracing = malloc(strlen(debugfs) + 9); | ||
301 | if (!tracing) | ||
302 | return NULL; | ||
303 | |||
304 | sprintf(tracing, "%s/tracing", debugfs); | ||
305 | |||
306 | tracing_found = 1; | ||
307 | return tracing; | ||
308 | } | ||
309 | |||
310 | char *get_tracing_file(const char *name) | ||
311 | { | ||
312 | const char *tracing; | ||
313 | char *file; | ||
314 | |||
315 | tracing = find_tracing_dir(); | ||
316 | if (!tracing) | ||
317 | return NULL; | ||
318 | |||
319 | file = malloc(strlen(tracing) + strlen(name) + 2); | ||
320 | if (!file) | ||
321 | return NULL; | ||
322 | |||
323 | sprintf(file, "%s/%s", tracing, name); | ||
324 | return file; | ||
325 | } | ||
326 | |||
327 | void put_tracing_file(char *file) | ||
328 | { | ||
329 | free(file); | ||
330 | } | ||
331 | |||
332 | int parse_nsec_time(const char *str, u64 *ptime) | ||
333 | { | ||
334 | u64 time_sec, time_nsec; | ||
335 | char *end; | ||
336 | |||
337 | time_sec = strtoul(str, &end, 10); | ||
338 | if (*end != '.' && *end != '\0') | ||
339 | return -1; | ||
340 | |||
341 | if (*end == '.') { | ||
342 | int i; | ||
343 | char nsec_buf[10]; | ||
344 | |||
345 | if (strlen(++end) > 9) | ||
346 | return -1; | ||
347 | |||
348 | strncpy(nsec_buf, end, 9); | ||
349 | nsec_buf[9] = '\0'; | ||
350 | |||
351 | /* make it nsec precision */ | ||
352 | for (i = strlen(nsec_buf); i < 9; i++) | ||
353 | nsec_buf[i] = '0'; | ||
354 | |||
355 | time_nsec = strtoul(nsec_buf, &end, 10); | ||
356 | if (*end != '\0') | ||
357 | return -1; | ||
358 | } else | ||
359 | time_nsec = 0; | ||
360 | |||
361 | *ptime = time_sec * NSEC_PER_SEC + time_nsec; | ||
362 | return 0; | ||
363 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 2732fad03908..a53535949043 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -80,6 +80,9 @@ extern char buildid_dir[]; | |||
80 | extern char tracing_events_path[]; | 80 | extern char tracing_events_path[]; |
81 | extern void perf_debugfs_set_path(const char *mountpoint); | 81 | extern void perf_debugfs_set_path(const char *mountpoint); |
82 | const char *perf_debugfs_mount(const char *mountpoint); | 82 | const char *perf_debugfs_mount(const char *mountpoint); |
83 | const char *find_tracing_dir(void); | ||
84 | char *get_tracing_file(const char *name); | ||
85 | void put_tracing_file(char *file); | ||
83 | 86 | ||
84 | /* On most systems <limits.h> would have given us this, but | 87 | /* On most systems <limits.h> would have given us this, but |
85 | * not on some systems (e.g. GNU/Hurd). | 88 | * not on some systems (e.g. GNU/Hurd). |
@@ -205,6 +208,8 @@ static inline int has_extension(const char *filename, const char *ext) | |||
205 | #define NSEC_PER_MSEC 1000000L | 208 | #define NSEC_PER_MSEC 1000000L |
206 | #endif | 209 | #endif |
207 | 210 | ||
211 | int parse_nsec_time(const char *str, u64 *ptime); | ||
212 | |||
208 | extern unsigned char sane_ctype[256]; | 213 | extern unsigned char sane_ctype[256]; |
209 | #define GIT_SPACE 0x01 | 214 | #define GIT_SPACE 0x01 |
210 | #define GIT_DIGIT 0x02 | 215 | #define GIT_DIGIT 0x02 |
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 4cb14cae3791..9f3eae290900 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
@@ -8,6 +8,7 @@ TARGETS += net | |||
8 | TARGETS += ptrace | 8 | TARGETS += ptrace |
9 | TARGETS += timers | 9 | TARGETS += timers |
10 | TARGETS += vm | 10 | TARGETS += vm |
11 | TARGETS += powerpc | ||
11 | 12 | ||
12 | all: | 13 | all: |
13 | for TARGET in $(TARGETS); do \ | 14 | for TARGET in $(TARGETS); do \ |
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile new file mode 100644 index 000000000000..bd24ae5aaeab --- /dev/null +++ b/tools/testing/selftests/powerpc/Makefile | |||
@@ -0,0 +1,39 @@ | |||
1 | # Makefile for powerpc selftests | ||
2 | |||
3 | # ARCH can be overridden by the user for cross compiling | ||
4 | ARCH ?= $(shell uname -m) | ||
5 | ARCH := $(shell echo $(ARCH) | sed -e s/ppc.*/powerpc/) | ||
6 | |||
7 | ifeq ($(ARCH),powerpc) | ||
8 | |||
9 | GIT_VERSION = $(shell git describe --always --long --dirty || echo "unknown") | ||
10 | |||
11 | CC := $(CROSS_COMPILE)$(CC) | ||
12 | CFLAGS := -Wall -O2 -flto -Wall -Werror -DGIT_VERSION='"$(GIT_VERSION)"' -I$(CURDIR) $(CFLAGS) | ||
13 | |||
14 | export CC CFLAGS | ||
15 | |||
16 | TARGETS = pmu | ||
17 | |||
18 | endif | ||
19 | |||
20 | all: | ||
21 | @for TARGET in $(TARGETS); do \ | ||
22 | $(MAKE) -C $$TARGET all; \ | ||
23 | done; | ||
24 | |||
25 | run_tests: all | ||
26 | @for TARGET in $(TARGETS); do \ | ||
27 | $(MAKE) -C $$TARGET run_tests; \ | ||
28 | done; | ||
29 | |||
30 | clean: | ||
31 | @for TARGET in $(TARGETS); do \ | ||
32 | $(MAKE) -C $$TARGET clean; \ | ||
33 | done; | ||
34 | rm -f tags | ||
35 | |||
36 | tags: | ||
37 | find . -name '*.c' -o -name '*.h' | xargs ctags | ||
38 | |||
39 | .PHONY: all run_tests clean tags | ||
diff --git a/tools/testing/selftests/powerpc/harness.c b/tools/testing/selftests/powerpc/harness.c new file mode 100644 index 000000000000..e80c42a584fe --- /dev/null +++ b/tools/testing/selftests/powerpc/harness.c | |||
@@ -0,0 +1,105 @@ | |||
1 | /* | ||
2 | * Copyright 2013, Michael Ellerman, IBM Corp. | ||
3 | * Licensed under GPLv2. | ||
4 | */ | ||
5 | |||
6 | #include <errno.h> | ||
7 | #include <signal.h> | ||
8 | #include <stdbool.h> | ||
9 | #include <stdio.h> | ||
10 | #include <stdlib.h> | ||
11 | #include <sys/types.h> | ||
12 | #include <sys/wait.h> | ||
13 | #include <unistd.h> | ||
14 | |||
15 | #include "subunit.h" | ||
16 | #include "utils.h" | ||
17 | |||
18 | #define TIMEOUT 120 | ||
19 | #define KILL_TIMEOUT 5 | ||
20 | |||
21 | |||
22 | int run_test(int (test_function)(void), char *name) | ||
23 | { | ||
24 | bool terminated; | ||
25 | int rc, status; | ||
26 | pid_t pid; | ||
27 | |||
28 | /* Make sure output is flushed before forking */ | ||
29 | fflush(stdout); | ||
30 | |||
31 | pid = fork(); | ||
32 | if (pid == 0) { | ||
33 | exit(test_function()); | ||
34 | } else if (pid == -1) { | ||
35 | perror("fork"); | ||
36 | return 1; | ||
37 | } | ||
38 | |||
39 | /* Wake us up in timeout seconds */ | ||
40 | alarm(TIMEOUT); | ||
41 | terminated = false; | ||
42 | |||
43 | wait: | ||
44 | rc = waitpid(pid, &status, 0); | ||
45 | if (rc == -1) { | ||
46 | if (errno != EINTR) { | ||
47 | printf("unknown error from waitpid\n"); | ||
48 | return 1; | ||
49 | } | ||
50 | |||
51 | if (terminated) { | ||
52 | printf("!! force killing %s\n", name); | ||
53 | kill(pid, SIGKILL); | ||
54 | return 1; | ||
55 | } else { | ||
56 | printf("!! killing %s\n", name); | ||
57 | kill(pid, SIGTERM); | ||
58 | terminated = true; | ||
59 | alarm(KILL_TIMEOUT); | ||
60 | goto wait; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | if (WIFEXITED(status)) | ||
65 | status = WEXITSTATUS(status); | ||
66 | else { | ||
67 | if (WIFSIGNALED(status)) | ||
68 | printf("!! child died by signal %d\n", WTERMSIG(status)); | ||
69 | else | ||
70 | printf("!! child died by unknown cause\n"); | ||
71 | |||
72 | status = 1; /* Signal or other */ | ||
73 | } | ||
74 | |||
75 | return status; | ||
76 | } | ||
77 | |||
78 | static void alarm_handler(int signum) | ||
79 | { | ||
80 | /* Jut wake us up from waitpid */ | ||
81 | } | ||
82 | |||
83 | static struct sigaction alarm_action = { | ||
84 | .sa_handler = alarm_handler, | ||
85 | }; | ||
86 | |||
87 | int test_harness(int (test_function)(void), char *name) | ||
88 | { | ||
89 | int rc; | ||
90 | |||
91 | test_start(name); | ||
92 | test_set_git_version(GIT_VERSION); | ||
93 | |||
94 | if (sigaction(SIGALRM, &alarm_action, NULL)) { | ||
95 | perror("sigaction"); | ||
96 | test_error(name); | ||
97 | return 1; | ||
98 | } | ||
99 | |||
100 | rc = run_test(test_function, name); | ||
101 | |||
102 | test_finish(name, rc); | ||
103 | |||
104 | return rc; | ||
105 | } | ||
diff --git a/tools/testing/selftests/powerpc/pmu/Makefile b/tools/testing/selftests/powerpc/pmu/Makefile new file mode 100644 index 000000000000..7216f0091655 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/Makefile | |||
@@ -0,0 +1,23 @@ | |||
1 | noarg: | ||
2 | $(MAKE) -C ../ | ||
3 | |||
4 | PROGS := count_instructions | ||
5 | EXTRA_SOURCES := ../harness.c event.c | ||
6 | |||
7 | all: $(PROGS) | ||
8 | |||
9 | $(PROGS): $(EXTRA_SOURCES) | ||
10 | |||
11 | # loop.S can only be built 64-bit | ||
12 | count_instructions: loop.S count_instructions.c $(EXTRA_SOURCES) | ||
13 | $(CC) $(CFLAGS) -m64 -o $@ $^ | ||
14 | |||
15 | run_tests: all | ||
16 | @-for PROG in $(PROGS); do \ | ||
17 | ./$$PROG; \ | ||
18 | done; | ||
19 | |||
20 | clean: | ||
21 | rm -f $(PROGS) loop.o | ||
22 | |||
23 | .PHONY: all run_tests clean | ||
diff --git a/tools/testing/selftests/powerpc/pmu/count_instructions.c b/tools/testing/selftests/powerpc/pmu/count_instructions.c new file mode 100644 index 000000000000..312b4f0fd27c --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/count_instructions.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * Copyright 2013, Michael Ellerman, IBM Corp. | ||
3 | * Licensed under GPLv2. | ||
4 | */ | ||
5 | |||
6 | #define _GNU_SOURCE | ||
7 | |||
8 | #include <stdio.h> | ||
9 | #include <stdbool.h> | ||
10 | #include <string.h> | ||
11 | #include <sys/prctl.h> | ||
12 | |||
13 | #include "event.h" | ||
14 | #include "utils.h" | ||
15 | |||
16 | extern void thirty_two_instruction_loop(u64 loops); | ||
17 | |||
18 | static void setup_event(struct event *e, u64 config, char *name) | ||
19 | { | ||
20 | event_init_opts(e, config, PERF_TYPE_HARDWARE, name); | ||
21 | |||
22 | e->attr.disabled = 1; | ||
23 | e->attr.exclude_kernel = 1; | ||
24 | e->attr.exclude_hv = 1; | ||
25 | e->attr.exclude_idle = 1; | ||
26 | } | ||
27 | |||
28 | static int do_count_loop(struct event *events, u64 instructions, | ||
29 | u64 overhead, bool report) | ||
30 | { | ||
31 | s64 difference, expected; | ||
32 | double percentage; | ||
33 | |||
34 | prctl(PR_TASK_PERF_EVENTS_ENABLE); | ||
35 | |||
36 | /* Run for 1M instructions */ | ||
37 | thirty_two_instruction_loop(instructions >> 5); | ||
38 | |||
39 | prctl(PR_TASK_PERF_EVENTS_DISABLE); | ||
40 | |||
41 | event_read(&events[0]); | ||
42 | event_read(&events[1]); | ||
43 | |||
44 | expected = instructions + overhead; | ||
45 | difference = events[0].result.value - expected; | ||
46 | percentage = (double)difference / events[0].result.value * 100; | ||
47 | |||
48 | if (report) { | ||
49 | event_report(&events[0]); | ||
50 | event_report(&events[1]); | ||
51 | |||
52 | printf("Looped for %llu instructions, overhead %llu\n", instructions, overhead); | ||
53 | printf("Expected %llu\n", expected); | ||
54 | printf("Actual %llu\n", events[0].result.value); | ||
55 | printf("Delta %lld, %f%%\n", difference, percentage); | ||
56 | } | ||
57 | |||
58 | event_reset(&events[0]); | ||
59 | event_reset(&events[1]); | ||
60 | |||
61 | if (difference < 0) | ||
62 | difference = -difference; | ||
63 | |||
64 | /* Tolerate a difference below 0.0001 % */ | ||
65 | difference *= 10000 * 100; | ||
66 | if (difference / events[0].result.value) | ||
67 | return -1; | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | /* Count how many instructions it takes to do a null loop */ | ||
73 | static u64 determine_overhead(struct event *events) | ||
74 | { | ||
75 | u64 current, overhead; | ||
76 | int i; | ||
77 | |||
78 | do_count_loop(events, 0, 0, false); | ||
79 | overhead = events[0].result.value; | ||
80 | |||
81 | for (i = 0; i < 100; i++) { | ||
82 | do_count_loop(events, 0, 0, false); | ||
83 | current = events[0].result.value; | ||
84 | if (current < overhead) { | ||
85 | printf("Replacing overhead %llu with %llu\n", overhead, current); | ||
86 | overhead = current; | ||
87 | } | ||
88 | } | ||
89 | |||
90 | return overhead; | ||
91 | } | ||
92 | |||
93 | static int count_instructions(void) | ||
94 | { | ||
95 | struct event events[2]; | ||
96 | u64 overhead; | ||
97 | |||
98 | setup_event(&events[0], PERF_COUNT_HW_INSTRUCTIONS, "instructions"); | ||
99 | setup_event(&events[1], PERF_COUNT_HW_CPU_CYCLES, "cycles"); | ||
100 | |||
101 | if (event_open(&events[0])) { | ||
102 | perror("perf_event_open"); | ||
103 | return -1; | ||
104 | } | ||
105 | |||
106 | if (event_open_with_group(&events[1], events[0].fd)) { | ||
107 | perror("perf_event_open"); | ||
108 | return -1; | ||
109 | } | ||
110 | |||
111 | overhead = determine_overhead(events); | ||
112 | printf("Overhead of null loop: %llu instructions\n", overhead); | ||
113 | |||
114 | /* Run for 1M instructions */ | ||
115 | FAIL_IF(do_count_loop(events, 0x100000, overhead, true)); | ||
116 | |||
117 | /* Run for 10M instructions */ | ||
118 | FAIL_IF(do_count_loop(events, 0xa00000, overhead, true)); | ||
119 | |||
120 | /* Run for 100M instructions */ | ||
121 | FAIL_IF(do_count_loop(events, 0x6400000, overhead, true)); | ||
122 | |||
123 | /* Run for 1G instructions */ | ||
124 | FAIL_IF(do_count_loop(events, 0x40000000, overhead, true)); | ||
125 | |||
126 | event_close(&events[0]); | ||
127 | event_close(&events[1]); | ||
128 | |||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | int main(void) | ||
133 | { | ||
134 | return test_harness(count_instructions, "count_instructions"); | ||
135 | } | ||
diff --git a/tools/testing/selftests/powerpc/pmu/event.c b/tools/testing/selftests/powerpc/pmu/event.c new file mode 100644 index 000000000000..2b2d11df2450 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/event.c | |||
@@ -0,0 +1,105 @@ | |||
1 | /* | ||
2 | * Copyright 2013, Michael Ellerman, IBM Corp. | ||
3 | * Licensed under GPLv2. | ||
4 | */ | ||
5 | |||
6 | #define _GNU_SOURCE | ||
7 | #include <unistd.h> | ||
8 | #include <sys/syscall.h> | ||
9 | #include <string.h> | ||
10 | #include <stdio.h> | ||
11 | #include <sys/ioctl.h> | ||
12 | |||
13 | #include "event.h" | ||
14 | |||
15 | |||
16 | int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, | ||
17 | int group_fd, unsigned long flags) | ||
18 | { | ||
19 | return syscall(__NR_perf_event_open, attr, pid, cpu, | ||
20 | group_fd, flags); | ||
21 | } | ||
22 | |||
23 | void event_init_opts(struct event *e, u64 config, int type, char *name) | ||
24 | { | ||
25 | memset(e, 0, sizeof(*e)); | ||
26 | |||
27 | e->name = name; | ||
28 | |||
29 | e->attr.type = type; | ||
30 | e->attr.config = config; | ||
31 | e->attr.size = sizeof(e->attr); | ||
32 | /* This has to match the structure layout in the header */ | ||
33 | e->attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | \ | ||
34 | PERF_FORMAT_TOTAL_TIME_RUNNING; | ||
35 | } | ||
36 | |||
37 | void event_init_named(struct event *e, u64 config, char *name) | ||
38 | { | ||
39 | event_init_opts(e, config, PERF_TYPE_RAW, name); | ||
40 | } | ||
41 | |||
42 | #define PERF_CURRENT_PID 0 | ||
43 | #define PERF_NO_CPU -1 | ||
44 | #define PERF_NO_GROUP -1 | ||
45 | |||
46 | int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd) | ||
47 | { | ||
48 | e->fd = perf_event_open(&e->attr, pid, cpu, group_fd, 0); | ||
49 | if (e->fd == -1) { | ||
50 | perror("perf_event_open"); | ||
51 | return -1; | ||
52 | } | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | int event_open_with_group(struct event *e, int group_fd) | ||
58 | { | ||
59 | return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, group_fd); | ||
60 | } | ||
61 | |||
62 | int event_open(struct event *e) | ||
63 | { | ||
64 | return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, PERF_NO_GROUP); | ||
65 | } | ||
66 | |||
67 | void event_close(struct event *e) | ||
68 | { | ||
69 | close(e->fd); | ||
70 | } | ||
71 | |||
72 | int event_reset(struct event *e) | ||
73 | { | ||
74 | return ioctl(e->fd, PERF_EVENT_IOC_RESET); | ||
75 | } | ||
76 | |||
77 | int event_read(struct event *e) | ||
78 | { | ||
79 | int rc; | ||
80 | |||
81 | rc = read(e->fd, &e->result, sizeof(e->result)); | ||
82 | if (rc != sizeof(e->result)) { | ||
83 | fprintf(stderr, "read error on event %p!\n", e); | ||
84 | return -1; | ||
85 | } | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | void event_report_justified(struct event *e, int name_width, int result_width) | ||
91 | { | ||
92 | printf("%*s: result %*llu ", name_width, e->name, result_width, | ||
93 | e->result.value); | ||
94 | |||
95 | if (e->result.running == e->result.enabled) | ||
96 | printf("running/enabled %llu\n", e->result.running); | ||
97 | else | ||
98 | printf("running %llu enabled %llu\n", e->result.running, | ||
99 | e->result.enabled); | ||
100 | } | ||
101 | |||
102 | void event_report(struct event *e) | ||
103 | { | ||
104 | event_report_justified(e, 0, 0); | ||
105 | } | ||
diff --git a/tools/testing/selftests/powerpc/pmu/event.h b/tools/testing/selftests/powerpc/pmu/event.h new file mode 100644 index 000000000000..e6993192ff34 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/event.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /* | ||
2 | * Copyright 2013, Michael Ellerman, IBM Corp. | ||
3 | * Licensed under GPLv2. | ||
4 | */ | ||
5 | |||
6 | #ifndef _SELFTESTS_POWERPC_PMU_EVENT_H | ||
7 | #define _SELFTESTS_POWERPC_PMU_EVENT_H | ||
8 | |||
9 | #include <unistd.h> | ||
10 | #include <linux/perf_event.h> | ||
11 | |||
12 | #include "utils.h" | ||
13 | |||
14 | |||
15 | struct event { | ||
16 | struct perf_event_attr attr; | ||
17 | char *name; | ||
18 | int fd; | ||
19 | /* This must match the read_format we use */ | ||
20 | struct { | ||
21 | u64 value; | ||
22 | u64 running; | ||
23 | u64 enabled; | ||
24 | } result; | ||
25 | }; | ||
26 | |||
27 | void event_init(struct event *e, u64 config); | ||
28 | void event_init_named(struct event *e, u64 config, char *name); | ||
29 | void event_init_opts(struct event *e, u64 config, int type, char *name); | ||
30 | int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd); | ||
31 | int event_open_with_group(struct event *e, int group_fd); | ||
32 | int event_open(struct event *e); | ||
33 | void event_close(struct event *e); | ||
34 | int event_reset(struct event *e); | ||
35 | int event_read(struct event *e); | ||
36 | void event_report_justified(struct event *e, int name_width, int result_width); | ||
37 | void event_report(struct event *e); | ||
38 | |||
39 | #endif /* _SELFTESTS_POWERPC_PMU_EVENT_H */ | ||
diff --git a/tools/testing/selftests/powerpc/pmu/loop.S b/tools/testing/selftests/powerpc/pmu/loop.S new file mode 100644 index 000000000000..8820e3df1444 --- /dev/null +++ b/tools/testing/selftests/powerpc/pmu/loop.S | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * Copyright 2013, Michael Ellerman, IBM Corp. | ||
3 | * Licensed under GPLv2. | ||
4 | */ | ||
5 | |||
6 | .text | ||
7 | |||
8 | .global thirty_two_instruction_loop | ||
9 | .type .thirty_two_instruction_loop,@function | ||
10 | .section ".opd","aw",@progbits | ||
11 | thirty_two_instruction_loop: | ||
12 | .quad .thirty_two_instruction_loop, .TOC.@tocbase, 0 | ||
13 | .previous | ||
14 | .thirty_two_instruction_loop: | ||
15 | cmpwi %r3,0 | ||
16 | beqlr | ||
17 | addi %r4,%r3,1 | ||
18 | addi %r4,%r4,1 | ||
19 | addi %r4,%r4,1 | ||
20 | addi %r4,%r4,1 | ||
21 | addi %r4,%r4,1 | ||
22 | addi %r4,%r4,1 | ||
23 | addi %r4,%r4,1 | ||
24 | addi %r4,%r4,1 | ||
25 | addi %r4,%r4,1 | ||
26 | addi %r4,%r4,1 | ||
27 | addi %r4,%r4,1 | ||
28 | addi %r4,%r4,1 | ||
29 | addi %r4,%r4,1 | ||
30 | addi %r4,%r4,1 | ||
31 | addi %r4,%r4,1 | ||
32 | addi %r4,%r4,1 | ||
33 | addi %r4,%r4,1 | ||
34 | addi %r4,%r4,1 | ||
35 | addi %r4,%r4,1 | ||
36 | addi %r4,%r4,1 | ||
37 | addi %r4,%r4,1 | ||
38 | addi %r4,%r4,1 | ||
39 | addi %r4,%r4,1 | ||
40 | addi %r4,%r4,1 | ||
41 | addi %r4,%r4,1 | ||
42 | addi %r4,%r4,1 | ||
43 | addi %r4,%r4,1 | ||
44 | addi %r4,%r4,1 # 28 addi's | ||
45 | subi %r3,%r3,1 | ||
46 | b .thirty_two_instruction_loop | ||
diff --git a/tools/testing/selftests/powerpc/subunit.h b/tools/testing/selftests/powerpc/subunit.h new file mode 100644 index 000000000000..98a22920792d --- /dev/null +++ b/tools/testing/selftests/powerpc/subunit.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Copyright 2013, Michael Ellerman, IBM Corp. | ||
3 | * Licensed under GPLv2. | ||
4 | */ | ||
5 | |||
6 | #ifndef _SELFTESTS_POWERPC_SUBUNIT_H | ||
7 | #define _SELFTESTS_POWERPC_SUBUNIT_H | ||
8 | |||
9 | static inline void test_start(char *name) | ||
10 | { | ||
11 | printf("test: %s\n", name); | ||
12 | } | ||
13 | |||
14 | static inline void test_failure_detail(char *name, char *detail) | ||
15 | { | ||
16 | printf("failure: %s [%s]\n", name, detail); | ||
17 | } | ||
18 | |||
19 | static inline void test_failure(char *name) | ||
20 | { | ||
21 | printf("failure: %s\n", name); | ||
22 | } | ||
23 | |||
24 | static inline void test_error(char *name) | ||
25 | { | ||
26 | printf("error: %s\n", name); | ||
27 | } | ||
28 | |||
29 | static inline void test_success(char *name) | ||
30 | { | ||
31 | printf("success: %s\n", name); | ||
32 | } | ||
33 | |||
34 | static inline void test_finish(char *name, int status) | ||
35 | { | ||
36 | if (status) | ||
37 | test_failure(name); | ||
38 | else | ||
39 | test_success(name); | ||
40 | } | ||
41 | |||
42 | static inline void test_set_git_version(char *value) | ||
43 | { | ||
44 | printf("tags: git_version:%s\n", value); | ||
45 | } | ||
46 | |||
47 | #endif /* _SELFTESTS_POWERPC_SUBUNIT_H */ | ||
diff --git a/tools/testing/selftests/powerpc/utils.h b/tools/testing/selftests/powerpc/utils.h new file mode 100644 index 000000000000..5851c4b0f553 --- /dev/null +++ b/tools/testing/selftests/powerpc/utils.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * Copyright 2013, Michael Ellerman, IBM Corp. | ||
3 | * Licensed under GPLv2. | ||
4 | */ | ||
5 | |||
6 | #ifndef _SELFTESTS_POWERPC_UTILS_H | ||
7 | #define _SELFTESTS_POWERPC_UTILS_H | ||
8 | |||
9 | #include <stdint.h> | ||
10 | #include <stdbool.h> | ||
11 | |||
12 | /* Avoid headaches with PRI?64 - just use %ll? always */ | ||
13 | typedef unsigned long long u64; | ||
14 | typedef signed long long s64; | ||
15 | |||
16 | /* Just for familiarity */ | ||
17 | typedef uint32_t u32; | ||
18 | typedef uint8_t u8; | ||
19 | |||
20 | |||
21 | int test_harness(int (test_function)(void), char *name); | ||
22 | |||
23 | |||
24 | /* Yes, this is evil */ | ||
25 | #define FAIL_IF(x) \ | ||
26 | do { \ | ||
27 | if ((x)) { \ | ||
28 | fprintf(stderr, \ | ||
29 | "[FAIL] Test FAILED on line %d\n", __LINE__); \ | ||
30 | return 1; \ | ||
31 | } \ | ||
32 | } while (0) | ||
33 | |||
34 | #endif /* _SELFTESTS_POWERPC_UTILS_H */ | ||
diff --git a/tools/testing/selftests/timers/posix_timers.c b/tools/testing/selftests/timers/posix_timers.c index 4fa655d68a81..41bd85559d4b 100644 --- a/tools/testing/selftests/timers/posix_timers.c +++ b/tools/testing/selftests/timers/posix_timers.c | |||
@@ -151,7 +151,7 @@ static int check_timer_create(int which) | |||
151 | fflush(stdout); | 151 | fflush(stdout); |
152 | 152 | ||
153 | done = 0; | 153 | done = 0; |
154 | timer_create(which, NULL, &id); | 154 | err = timer_create(which, NULL, &id); |
155 | if (err < 0) { | 155 | if (err < 0) { |
156 | perror("Can't create timer\n"); | 156 | perror("Can't create timer\n"); |
157 | return -1; | 157 | return -1; |
diff --git a/tools/virtio/.gitignore b/tools/virtio/.gitignore new file mode 100644 index 000000000000..1cfbb0157a46 --- /dev/null +++ b/tools/virtio/.gitignore | |||
@@ -0,0 +1,3 @@ | |||
1 | *.d | ||
2 | virtio_test | ||
3 | vringh_test | ||