diff options
Diffstat (limited to 'tools')
152 files changed, 10918 insertions, 1837 deletions
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 11224eddcdc2..146fd6147e84 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c | |||
@@ -34,21 +34,13 @@ | |||
34 | #include <errno.h> | 34 | #include <errno.h> |
35 | #include <arpa/inet.h> | 35 | #include <arpa/inet.h> |
36 | #include <linux/connector.h> | 36 | #include <linux/connector.h> |
37 | #include <linux/hyperv.h> | ||
37 | #include <linux/netlink.h> | 38 | #include <linux/netlink.h> |
38 | #include <ifaddrs.h> | 39 | #include <ifaddrs.h> |
39 | #include <netdb.h> | 40 | #include <netdb.h> |
40 | #include <syslog.h> | 41 | #include <syslog.h> |
41 | 42 | #include <sys/stat.h> | |
42 | /* | 43 | #include <fcntl.h> |
43 | * KYS: TODO. Need to register these in the kernel. | ||
44 | * | ||
45 | * The following definitions are shared with the in-kernel component; do not | ||
46 | * change any of this without making the corresponding changes in | ||
47 | * the KVP kernel component. | ||
48 | */ | ||
49 | #define CN_KVP_IDX 0x9 /* MSFT KVP functionality */ | ||
50 | #define CN_KVP_VAL 0x1 /* This supports queries from the kernel */ | ||
51 | #define CN_KVP_USER_VAL 0x2 /* This supports queries from the user */ | ||
52 | 44 | ||
53 | /* | 45 | /* |
54 | * KVP protocol: The user mode component first registers with the | 46 | * KVP protocol: The user mode component first registers with the |
@@ -60,25 +52,8 @@ | |||
60 | * We use this infrastructure for also supporting queries from user mode | 52 | * We use this infrastructure for also supporting queries from user mode |
61 | * application for state that may be maintained in the KVP kernel component. | 53 | * application for state that may be maintained in the KVP kernel component. |
62 | * | 54 | * |
63 | * XXXKYS: Have a shared header file between the user and kernel (TODO) | ||
64 | */ | 55 | */ |
65 | 56 | ||
66 | enum kvp_op { | ||
67 | KVP_REGISTER = 0, /* Register the user mode component*/ | ||
68 | KVP_KERNEL_GET, /*Kernel is requesting the value for the specified key*/ | ||
69 | KVP_KERNEL_SET, /*Kernel is providing the value for the specified key*/ | ||
70 | KVP_USER_GET, /*User is requesting the value for the specified key*/ | ||
71 | KVP_USER_SET /*User is providing the value for the specified key*/ | ||
72 | }; | ||
73 | |||
74 | #define HV_KVP_EXCHANGE_MAX_KEY_SIZE 512 | ||
75 | #define HV_KVP_EXCHANGE_MAX_VALUE_SIZE 2048 | ||
76 | |||
77 | struct hv_ku_msg { | ||
78 | __u32 kvp_index; | ||
79 | __u8 kvp_key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; /* Key name */ | ||
80 | __u8 kvp_value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; /* Key value */ | ||
81 | }; | ||
82 | 57 | ||
83 | enum key_index { | 58 | enum key_index { |
84 | FullyQualifiedDomainName = 0, | 59 | FullyQualifiedDomainName = 0, |
@@ -93,10 +68,6 @@ enum key_index { | |||
93 | ProcessorArchitecture | 68 | ProcessorArchitecture |
94 | }; | 69 | }; |
95 | 70 | ||
96 | /* | ||
97 | * End of shared definitions. | ||
98 | */ | ||
99 | |||
100 | static char kvp_send_buffer[4096]; | 71 | static char kvp_send_buffer[4096]; |
101 | static char kvp_recv_buffer[4096]; | 72 | static char kvp_recv_buffer[4096]; |
102 | static struct sockaddr_nl addr; | 73 | static struct sockaddr_nl addr; |
@@ -109,6 +80,345 @@ static char *os_build; | |||
109 | static char *lic_version; | 80 | static char *lic_version; |
110 | static struct utsname uts_buf; | 81 | static struct utsname uts_buf; |
111 | 82 | ||
83 | |||
84 | #define MAX_FILE_NAME 100 | ||
85 | #define ENTRIES_PER_BLOCK 50 | ||
86 | |||
87 | struct kvp_record { | ||
88 | __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE]; | ||
89 | __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE]; | ||
90 | }; | ||
91 | |||
92 | struct kvp_file_state { | ||
93 | int fd; | ||
94 | int num_blocks; | ||
95 | struct kvp_record *records; | ||
96 | int num_records; | ||
97 | __u8 fname[MAX_FILE_NAME]; | ||
98 | }; | ||
99 | |||
100 | static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT]; | ||
101 | |||
102 | static void kvp_acquire_lock(int pool) | ||
103 | { | ||
104 | struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0}; | ||
105 | fl.l_pid = getpid(); | ||
106 | |||
107 | if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) { | ||
108 | syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool); | ||
109 | exit(-1); | ||
110 | } | ||
111 | } | ||
112 | |||
113 | static void kvp_release_lock(int pool) | ||
114 | { | ||
115 | struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0}; | ||
116 | fl.l_pid = getpid(); | ||
117 | |||
118 | if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) { | ||
119 | perror("fcntl"); | ||
120 | syslog(LOG_ERR, "Failed to release the lock pool: %d", pool); | ||
121 | exit(-1); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | static void kvp_update_file(int pool) | ||
126 | { | ||
127 | FILE *filep; | ||
128 | size_t bytes_written; | ||
129 | |||
130 | /* | ||
131 | * We are going to write our in-memory registry out to | ||
132 | * disk; acquire the lock first. | ||
133 | */ | ||
134 | kvp_acquire_lock(pool); | ||
135 | |||
136 | filep = fopen(kvp_file_info[pool].fname, "w"); | ||
137 | if (!filep) { | ||
138 | kvp_release_lock(pool); | ||
139 | syslog(LOG_ERR, "Failed to open file, pool: %d", pool); | ||
140 | exit(-1); | ||
141 | } | ||
142 | |||
143 | bytes_written = fwrite(kvp_file_info[pool].records, | ||
144 | sizeof(struct kvp_record), | ||
145 | kvp_file_info[pool].num_records, filep); | ||
146 | |||
147 | fflush(filep); | ||
148 | kvp_release_lock(pool); | ||
149 | } | ||
150 | |||
151 | static void kvp_update_mem_state(int pool) | ||
152 | { | ||
153 | FILE *filep; | ||
154 | size_t records_read = 0; | ||
155 | struct kvp_record *record = kvp_file_info[pool].records; | ||
156 | struct kvp_record *readp; | ||
157 | int num_blocks = kvp_file_info[pool].num_blocks; | ||
158 | int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; | ||
159 | |||
160 | kvp_acquire_lock(pool); | ||
161 | |||
162 | filep = fopen(kvp_file_info[pool].fname, "r"); | ||
163 | if (!filep) { | ||
164 | kvp_release_lock(pool); | ||
165 | syslog(LOG_ERR, "Failed to open file, pool: %d", pool); | ||
166 | exit(-1); | ||
167 | } | ||
168 | while (!feof(filep)) { | ||
169 | readp = &record[records_read]; | ||
170 | records_read += fread(readp, sizeof(struct kvp_record), | ||
171 | ENTRIES_PER_BLOCK * num_blocks, | ||
172 | filep); | ||
173 | |||
174 | if (!feof(filep)) { | ||
175 | /* | ||
176 | * We have more data to read. | ||
177 | */ | ||
178 | num_blocks++; | ||
179 | record = realloc(record, alloc_unit * num_blocks); | ||
180 | |||
181 | if (record == NULL) { | ||
182 | syslog(LOG_ERR, "malloc failed"); | ||
183 | exit(-1); | ||
184 | } | ||
185 | continue; | ||
186 | } | ||
187 | break; | ||
188 | } | ||
189 | |||
190 | kvp_file_info[pool].num_blocks = num_blocks; | ||
191 | kvp_file_info[pool].records = record; | ||
192 | kvp_file_info[pool].num_records = records_read; | ||
193 | |||
194 | kvp_release_lock(pool); | ||
195 | } | ||
196 | static int kvp_file_init(void) | ||
197 | { | ||
198 | int ret, fd; | ||
199 | FILE *filep; | ||
200 | size_t records_read; | ||
201 | __u8 *fname; | ||
202 | struct kvp_record *record; | ||
203 | struct kvp_record *readp; | ||
204 | int num_blocks; | ||
205 | int i; | ||
206 | int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; | ||
207 | |||
208 | if (access("/var/opt/hyperv", F_OK)) { | ||
209 | if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) { | ||
210 | syslog(LOG_ERR, " Failed to create /var/opt/hyperv"); | ||
211 | exit(-1); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | for (i = 0; i < KVP_POOL_COUNT; i++) { | ||
216 | fname = kvp_file_info[i].fname; | ||
217 | records_read = 0; | ||
218 | num_blocks = 1; | ||
219 | sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i); | ||
220 | fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH); | ||
221 | |||
222 | if (fd == -1) | ||
223 | return 1; | ||
224 | |||
225 | |||
226 | filep = fopen(fname, "r"); | ||
227 | if (!filep) | ||
228 | return 1; | ||
229 | |||
230 | record = malloc(alloc_unit * num_blocks); | ||
231 | if (record == NULL) { | ||
232 | fclose(filep); | ||
233 | return 1; | ||
234 | } | ||
235 | while (!feof(filep)) { | ||
236 | readp = &record[records_read]; | ||
237 | records_read += fread(readp, sizeof(struct kvp_record), | ||
238 | ENTRIES_PER_BLOCK, | ||
239 | filep); | ||
240 | |||
241 | if (!feof(filep)) { | ||
242 | /* | ||
243 | * We have more data to read. | ||
244 | */ | ||
245 | num_blocks++; | ||
246 | record = realloc(record, alloc_unit * | ||
247 | num_blocks); | ||
248 | if (record == NULL) { | ||
249 | fclose(filep); | ||
250 | return 1; | ||
251 | } | ||
252 | continue; | ||
253 | } | ||
254 | break; | ||
255 | } | ||
256 | kvp_file_info[i].fd = fd; | ||
257 | kvp_file_info[i].num_blocks = num_blocks; | ||
258 | kvp_file_info[i].records = record; | ||
259 | kvp_file_info[i].num_records = records_read; | ||
260 | fclose(filep); | ||
261 | |||
262 | } | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int kvp_key_delete(int pool, __u8 *key, int key_size) | ||
268 | { | ||
269 | int i; | ||
270 | int j, k; | ||
271 | int num_records; | ||
272 | struct kvp_record *record; | ||
273 | |||
274 | /* | ||
275 | * First update the in-memory state. | ||
276 | */ | ||
277 | kvp_update_mem_state(pool); | ||
278 | |||
279 | num_records = kvp_file_info[pool].num_records; | ||
280 | record = kvp_file_info[pool].records; | ||
281 | |||
282 | for (i = 0; i < num_records; i++) { | ||
283 | if (memcmp(key, record[i].key, key_size)) | ||
284 | continue; | ||
285 | /* | ||
286 | * Found a match; just move the remaining | ||
287 | * entries up. | ||
288 | */ | ||
289 | if (i == num_records) { | ||
290 | kvp_file_info[pool].num_records--; | ||
291 | kvp_update_file(pool); | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | j = i; | ||
296 | k = j + 1; | ||
297 | for (; k < num_records; k++) { | ||
298 | strcpy(record[j].key, record[k].key); | ||
299 | strcpy(record[j].value, record[k].value); | ||
300 | j++; | ||
301 | } | ||
302 | |||
303 | kvp_file_info[pool].num_records--; | ||
304 | kvp_update_file(pool); | ||
305 | return 0; | ||
306 | } | ||
307 | return 1; | ||
308 | } | ||
309 | |||
310 | static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value, | ||
311 | int value_size) | ||
312 | { | ||
313 | int i; | ||
314 | int j, k; | ||
315 | int num_records; | ||
316 | struct kvp_record *record; | ||
317 | int num_blocks; | ||
318 | |||
319 | if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || | ||
320 | (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) | ||
321 | return 1; | ||
322 | |||
323 | /* | ||
324 | * First update the in-memory state. | ||
325 | */ | ||
326 | kvp_update_mem_state(pool); | ||
327 | |||
328 | num_records = kvp_file_info[pool].num_records; | ||
329 | record = kvp_file_info[pool].records; | ||
330 | num_blocks = kvp_file_info[pool].num_blocks; | ||
331 | |||
332 | for (i = 0; i < num_records; i++) { | ||
333 | if (memcmp(key, record[i].key, key_size)) | ||
334 | continue; | ||
335 | /* | ||
336 | * Found a match; just update the value - | ||
337 | * this is the modify case. | ||
338 | */ | ||
339 | memcpy(record[i].value, value, value_size); | ||
340 | kvp_update_file(pool); | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | /* | ||
345 | * Need to add a new entry; | ||
346 | */ | ||
347 | if (num_records == (ENTRIES_PER_BLOCK * num_blocks)) { | ||
348 | /* Need to allocate a larger array for reg entries. */ | ||
349 | record = realloc(record, sizeof(struct kvp_record) * | ||
350 | ENTRIES_PER_BLOCK * (num_blocks + 1)); | ||
351 | |||
352 | if (record == NULL) | ||
353 | return 1; | ||
354 | kvp_file_info[pool].num_blocks++; | ||
355 | |||
356 | } | ||
357 | memcpy(record[i].value, value, value_size); | ||
358 | memcpy(record[i].key, key, key_size); | ||
359 | kvp_file_info[pool].records = record; | ||
360 | kvp_file_info[pool].num_records++; | ||
361 | kvp_update_file(pool); | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value, | ||
366 | int value_size) | ||
367 | { | ||
368 | int i; | ||
369 | int num_records; | ||
370 | struct kvp_record *record; | ||
371 | |||
372 | if ((key_size > HV_KVP_EXCHANGE_MAX_KEY_SIZE) || | ||
373 | (value_size > HV_KVP_EXCHANGE_MAX_VALUE_SIZE)) | ||
374 | return 1; | ||
375 | |||
376 | /* | ||
377 | * First update the in-memory state. | ||
378 | */ | ||
379 | kvp_update_mem_state(pool); | ||
380 | |||
381 | num_records = kvp_file_info[pool].num_records; | ||
382 | record = kvp_file_info[pool].records; | ||
383 | |||
384 | for (i = 0; i < num_records; i++) { | ||
385 | if (memcmp(key, record[i].key, key_size)) | ||
386 | continue; | ||
387 | /* | ||
388 | * Found a match; just copy the value out. | ||
389 | */ | ||
390 | memcpy(value, record[i].value, value_size); | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | return 1; | ||
395 | } | ||
396 | |||
397 | static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size, | ||
398 | __u8 *value, int value_size) | ||
399 | { | ||
400 | struct kvp_record *record; | ||
401 | |||
402 | /* | ||
403 | * First update our in-memory database. | ||
404 | */ | ||
405 | kvp_update_mem_state(pool); | ||
406 | record = kvp_file_info[pool].records; | ||
407 | |||
408 | if (index >= kvp_file_info[pool].num_records) { | ||
409 | /* | ||
410 | * This is an invalid index; terminate enumeration; | ||
411 | * - a NULL value will do the trick. | ||
412 | */ | ||
413 | strcpy(value, ""); | ||
414 | return; | ||
415 | } | ||
416 | |||
417 | memcpy(key, record[index].key, key_size); | ||
418 | memcpy(value, record[index].value, value_size); | ||
419 | } | ||
420 | |||
421 | |||
112 | void kvp_get_os_info(void) | 422 | void kvp_get_os_info(void) |
113 | { | 423 | { |
114 | FILE *file; | 424 | FILE *file; |
@@ -332,7 +642,7 @@ int main(void) | |||
332 | struct pollfd pfd; | 642 | struct pollfd pfd; |
333 | struct nlmsghdr *incoming_msg; | 643 | struct nlmsghdr *incoming_msg; |
334 | struct cn_msg *incoming_cn_msg; | 644 | struct cn_msg *incoming_cn_msg; |
335 | struct hv_ku_msg *hv_msg; | 645 | struct hv_kvp_msg *hv_msg; |
336 | char *p; | 646 | char *p; |
337 | char *key_value; | 647 | char *key_value; |
338 | char *key_name; | 648 | char *key_name; |
@@ -345,6 +655,11 @@ int main(void) | |||
345 | */ | 655 | */ |
346 | kvp_get_os_info(); | 656 | kvp_get_os_info(); |
347 | 657 | ||
658 | if (kvp_file_init()) { | ||
659 | syslog(LOG_ERR, "Failed to initialize the pools"); | ||
660 | exit(-1); | ||
661 | } | ||
662 | |||
348 | fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); | 663 | fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); |
349 | if (fd < 0) { | 664 | if (fd < 0) { |
350 | syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); | 665 | syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); |
@@ -370,9 +685,11 @@ int main(void) | |||
370 | message = (struct cn_msg *)kvp_send_buffer; | 685 | message = (struct cn_msg *)kvp_send_buffer; |
371 | message->id.idx = CN_KVP_IDX; | 686 | message->id.idx = CN_KVP_IDX; |
372 | message->id.val = CN_KVP_VAL; | 687 | message->id.val = CN_KVP_VAL; |
373 | message->seq = KVP_REGISTER; | 688 | |
689 | hv_msg = (struct hv_kvp_msg *)message->data; | ||
690 | hv_msg->kvp_hdr.operation = KVP_OP_REGISTER; | ||
374 | message->ack = 0; | 691 | message->ack = 0; |
375 | message->len = 0; | 692 | message->len = sizeof(struct hv_kvp_msg); |
376 | 693 | ||
377 | len = netlink_send(fd, message); | 694 | len = netlink_send(fd, message); |
378 | if (len < 0) { | 695 | if (len < 0) { |
@@ -398,14 +715,15 @@ int main(void) | |||
398 | 715 | ||
399 | incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; | 716 | incoming_msg = (struct nlmsghdr *)kvp_recv_buffer; |
400 | incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); | 717 | incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg); |
718 | hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; | ||
401 | 719 | ||
402 | switch (incoming_cn_msg->seq) { | 720 | switch (hv_msg->kvp_hdr.operation) { |
403 | case KVP_REGISTER: | 721 | case KVP_OP_REGISTER: |
404 | /* | 722 | /* |
405 | * Driver is registering with us; stash away the version | 723 | * Driver is registering with us; stash away the version |
406 | * information. | 724 | * information. |
407 | */ | 725 | */ |
408 | p = (char *)incoming_cn_msg->data; | 726 | p = (char *)hv_msg->body.kvp_register.version; |
409 | lic_version = malloc(strlen(p) + 1); | 727 | lic_version = malloc(strlen(p) + 1); |
410 | if (lic_version) { | 728 | if (lic_version) { |
411 | strcpy(lic_version, p); | 729 | strcpy(lic_version, p); |
@@ -416,17 +734,65 @@ int main(void) | |||
416 | } | 734 | } |
417 | continue; | 735 | continue; |
418 | 736 | ||
419 | case KVP_KERNEL_GET: | 737 | /* |
738 | * The current protocol with the kernel component uses a | ||
739 | * NULL key name to pass an error condition. | ||
740 | * For the SET, GET and DELETE operations, | ||
741 | * use the existing protocol to pass back error. | ||
742 | */ | ||
743 | |||
744 | case KVP_OP_SET: | ||
745 | if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool, | ||
746 | hv_msg->body.kvp_set.data.key, | ||
747 | hv_msg->body.kvp_set.data.key_size, | ||
748 | hv_msg->body.kvp_set.data.value, | ||
749 | hv_msg->body.kvp_set.data.value_size)) | ||
750 | strcpy(hv_msg->body.kvp_set.data.key, ""); | ||
751 | break; | ||
752 | |||
753 | case KVP_OP_GET: | ||
754 | if (kvp_get_value(hv_msg->kvp_hdr.pool, | ||
755 | hv_msg->body.kvp_set.data.key, | ||
756 | hv_msg->body.kvp_set.data.key_size, | ||
757 | hv_msg->body.kvp_set.data.value, | ||
758 | hv_msg->body.kvp_set.data.value_size)) | ||
759 | strcpy(hv_msg->body.kvp_set.data.key, ""); | ||
760 | break; | ||
761 | |||
762 | case KVP_OP_DELETE: | ||
763 | if (kvp_key_delete(hv_msg->kvp_hdr.pool, | ||
764 | hv_msg->body.kvp_delete.key, | ||
765 | hv_msg->body.kvp_delete.key_size)) | ||
766 | strcpy(hv_msg->body.kvp_delete.key, ""); | ||
420 | break; | 767 | break; |
768 | |||
421 | default: | 769 | default: |
422 | continue; | 770 | break; |
771 | } | ||
772 | |||
773 | if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE) | ||
774 | goto kvp_done; | ||
775 | |||
776 | /* | ||
777 | * If the pool is KVP_POOL_AUTO, dynamically generate | ||
778 | * both the key and the value; if not read from the | ||
779 | * appropriate pool. | ||
780 | */ | ||
781 | if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) { | ||
782 | kvp_pool_enumerate(hv_msg->kvp_hdr.pool, | ||
783 | hv_msg->body.kvp_enum_data.index, | ||
784 | hv_msg->body.kvp_enum_data.data.key, | ||
785 | HV_KVP_EXCHANGE_MAX_KEY_SIZE, | ||
786 | hv_msg->body.kvp_enum_data.data.value, | ||
787 | HV_KVP_EXCHANGE_MAX_VALUE_SIZE); | ||
788 | goto kvp_done; | ||
423 | } | 789 | } |
424 | 790 | ||
425 | hv_msg = (struct hv_ku_msg *)incoming_cn_msg->data; | 791 | hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data; |
426 | key_name = (char *)hv_msg->kvp_key; | 792 | key_name = (char *)hv_msg->body.kvp_enum_data.data.key; |
427 | key_value = (char *)hv_msg->kvp_value; | 793 | key_value = (char *)hv_msg->body.kvp_enum_data.data.value; |
428 | 794 | ||
429 | switch (hv_msg->kvp_index) { | 795 | switch (hv_msg->body.kvp_enum_data.index) { |
430 | case FullyQualifiedDomainName: | 796 | case FullyQualifiedDomainName: |
431 | kvp_get_domain_name(key_value, | 797 | kvp_get_domain_name(key_value, |
432 | HV_KVP_EXCHANGE_MAX_VALUE_SIZE); | 798 | HV_KVP_EXCHANGE_MAX_VALUE_SIZE); |
@@ -483,12 +849,12 @@ int main(void) | |||
483 | * already in the receive buffer. Update the cn_msg header to | 849 | * already in the receive buffer. Update the cn_msg header to |
484 | * reflect the key value that has been added to the message | 850 | * reflect the key value that has been added to the message |
485 | */ | 851 | */ |
852 | kvp_done: | ||
486 | 853 | ||
487 | incoming_cn_msg->id.idx = CN_KVP_IDX; | 854 | incoming_cn_msg->id.idx = CN_KVP_IDX; |
488 | incoming_cn_msg->id.val = CN_KVP_VAL; | 855 | incoming_cn_msg->id.val = CN_KVP_VAL; |
489 | incoming_cn_msg->seq = KVP_USER_SET; | ||
490 | incoming_cn_msg->ack = 0; | 856 | incoming_cn_msg->ack = 0; |
491 | incoming_cn_msg->len = sizeof(struct hv_ku_msg); | 857 | incoming_cn_msg->len = sizeof(struct hv_kvp_msg); |
492 | 858 | ||
493 | len = netlink_send(fd, incoming_cn_msg); | 859 | len = netlink_send(fd, incoming_cn_msg); |
494 | if (len < 0) { | 860 | if (len < 0) { |
diff --git a/tools/include/tools/be_byteshift.h b/tools/include/tools/be_byteshift.h new file mode 100644 index 000000000000..f4912e2668ba --- /dev/null +++ b/tools/include/tools/be_byteshift.h | |||
@@ -0,0 +1,70 @@ | |||
1 | #ifndef _TOOLS_BE_BYTESHIFT_H | ||
2 | #define _TOOLS_BE_BYTESHIFT_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | |||
6 | static inline __u16 __get_unaligned_be16(const __u8 *p) | ||
7 | { | ||
8 | return p[0] << 8 | p[1]; | ||
9 | } | ||
10 | |||
11 | static inline __u32 __get_unaligned_be32(const __u8 *p) | ||
12 | { | ||
13 | return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; | ||
14 | } | ||
15 | |||
16 | static inline __u64 __get_unaligned_be64(const __u8 *p) | ||
17 | { | ||
18 | return (__u64)__get_unaligned_be32(p) << 32 | | ||
19 | __get_unaligned_be32(p + 4); | ||
20 | } | ||
21 | |||
22 | static inline void __put_unaligned_be16(__u16 val, __u8 *p) | ||
23 | { | ||
24 | *p++ = val >> 8; | ||
25 | *p++ = val; | ||
26 | } | ||
27 | |||
28 | static inline void __put_unaligned_be32(__u32 val, __u8 *p) | ||
29 | { | ||
30 | __put_unaligned_be16(val >> 16, p); | ||
31 | __put_unaligned_be16(val, p + 2); | ||
32 | } | ||
33 | |||
34 | static inline void __put_unaligned_be64(__u64 val, __u8 *p) | ||
35 | { | ||
36 | __put_unaligned_be32(val >> 32, p); | ||
37 | __put_unaligned_be32(val, p + 4); | ||
38 | } | ||
39 | |||
40 | static inline __u16 get_unaligned_be16(const void *p) | ||
41 | { | ||
42 | return __get_unaligned_be16((const __u8 *)p); | ||
43 | } | ||
44 | |||
45 | static inline __u32 get_unaligned_be32(const void *p) | ||
46 | { | ||
47 | return __get_unaligned_be32((const __u8 *)p); | ||
48 | } | ||
49 | |||
50 | static inline __u64 get_unaligned_be64(const void *p) | ||
51 | { | ||
52 | return __get_unaligned_be64((const __u8 *)p); | ||
53 | } | ||
54 | |||
55 | static inline void put_unaligned_be16(__u16 val, void *p) | ||
56 | { | ||
57 | __put_unaligned_be16(val, p); | ||
58 | } | ||
59 | |||
60 | static inline void put_unaligned_be32(__u32 val, void *p) | ||
61 | { | ||
62 | __put_unaligned_be32(val, p); | ||
63 | } | ||
64 | |||
65 | static inline void put_unaligned_be64(__u64 val, void *p) | ||
66 | { | ||
67 | __put_unaligned_be64(val, p); | ||
68 | } | ||
69 | |||
70 | #endif /* _TOOLS_BE_BYTESHIFT_H */ | ||
diff --git a/tools/include/tools/le_byteshift.h b/tools/include/tools/le_byteshift.h new file mode 100644 index 000000000000..c99d45a68bda --- /dev/null +++ b/tools/include/tools/le_byteshift.h | |||
@@ -0,0 +1,70 @@ | |||
1 | #ifndef _TOOLS_LE_BYTESHIFT_H | ||
2 | #define _TOOLS_LE_BYTESHIFT_H | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | |||
6 | static inline __u16 __get_unaligned_le16(const __u8 *p) | ||
7 | { | ||
8 | return p[0] | p[1] << 8; | ||
9 | } | ||
10 | |||
11 | static inline __u32 __get_unaligned_le32(const __u8 *p) | ||
12 | { | ||
13 | return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; | ||
14 | } | ||
15 | |||
16 | static inline __u64 __get_unaligned_le64(const __u8 *p) | ||
17 | { | ||
18 | return (__u64)__get_unaligned_le32(p + 4) << 32 | | ||
19 | __get_unaligned_le32(p); | ||
20 | } | ||
21 | |||
22 | static inline void __put_unaligned_le16(__u16 val, __u8 *p) | ||
23 | { | ||
24 | *p++ = val; | ||
25 | *p++ = val >> 8; | ||
26 | } | ||
27 | |||
28 | static inline void __put_unaligned_le32(__u32 val, __u8 *p) | ||
29 | { | ||
30 | __put_unaligned_le16(val >> 16, p + 2); | ||
31 | __put_unaligned_le16(val, p); | ||
32 | } | ||
33 | |||
34 | static inline void __put_unaligned_le64(__u64 val, __u8 *p) | ||
35 | { | ||
36 | __put_unaligned_le32(val >> 32, p + 4); | ||
37 | __put_unaligned_le32(val, p); | ||
38 | } | ||
39 | |||
40 | static inline __u16 get_unaligned_le16(const void *p) | ||
41 | { | ||
42 | return __get_unaligned_le16((const __u8 *)p); | ||
43 | } | ||
44 | |||
45 | static inline __u32 get_unaligned_le32(const void *p) | ||
46 | { | ||
47 | return __get_unaligned_le32((const __u8 *)p); | ||
48 | } | ||
49 | |||
50 | static inline __u64 get_unaligned_le64(const void *p) | ||
51 | { | ||
52 | return __get_unaligned_le64((const __u8 *)p); | ||
53 | } | ||
54 | |||
55 | static inline void put_unaligned_le16(__u16 val, void *p) | ||
56 | { | ||
57 | __put_unaligned_le16(val, p); | ||
58 | } | ||
59 | |||
60 | static inline void put_unaligned_le32(__u32 val, void *p) | ||
61 | { | ||
62 | __put_unaligned_le32(val, p); | ||
63 | } | ||
64 | |||
65 | static inline void put_unaligned_le64(__u64 val, void *p) | ||
66 | { | ||
67 | __put_unaligned_le64(val, p); | ||
68 | } | ||
69 | |||
70 | #endif /* _TOOLS_LE_BYTESHIFT_H */ | ||
diff --git a/tools/lguest/.gitignore b/tools/lguest/.gitignore new file mode 100644 index 000000000000..115587fd5f65 --- /dev/null +++ b/tools/lguest/.gitignore | |||
@@ -0,0 +1 @@ | |||
lguest | |||
diff --git a/tools/lguest/Makefile b/tools/lguest/Makefile new file mode 100644 index 000000000000..0ac34206f7a7 --- /dev/null +++ b/tools/lguest/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | # This creates the demonstration utility "lguest" which runs a Linux guest. | ||
2 | # Missing headers? Add "-I../../../include -I../../../arch/x86/include" | ||
3 | CFLAGS:=-m32 -Wall -Wmissing-declarations -Wmissing-prototypes -O3 -U_FORTIFY_SOURCE | ||
4 | |||
5 | all: lguest | ||
6 | |||
7 | clean: | ||
8 | rm -f lguest | ||
diff --git a/tools/lguest/extract b/tools/lguest/extract new file mode 100644 index 000000000000..7730bb6e4b94 --- /dev/null +++ b/tools/lguest/extract | |||
@@ -0,0 +1,58 @@ | |||
1 | #! /bin/sh | ||
2 | |||
3 | set -e | ||
4 | |||
5 | PREFIX=$1 | ||
6 | shift | ||
7 | |||
8 | trap 'rm -r $TMPDIR' 0 | ||
9 | TMPDIR=`mktemp -d` | ||
10 | |||
11 | exec 3>/dev/null | ||
12 | for f; do | ||
13 | while IFS=" | ||
14 | " read -r LINE; do | ||
15 | case "$LINE" in | ||
16 | *$PREFIX:[0-9]*:\**) | ||
17 | NUM=`echo "$LINE" | sed "s/.*$PREFIX:\([0-9]*\).*/\1/"` | ||
18 | if [ -f $TMPDIR/$NUM ]; then | ||
19 | echo "$TMPDIR/$NUM already exits prior to $f" | ||
20 | exit 1 | ||
21 | fi | ||
22 | exec 3>>$TMPDIR/$NUM | ||
23 | echo $f | sed 's,\.\./,,g' > $TMPDIR/.$NUM | ||
24 | /bin/echo "$LINE" | sed -e "s/$PREFIX:[0-9]*//" -e "s/:\*/*/" >&3 | ||
25 | ;; | ||
26 | *$PREFIX:[0-9]*) | ||
27 | NUM=`echo "$LINE" | sed "s/.*$PREFIX:\([0-9]*\).*/\1/"` | ||
28 | if [ -f $TMPDIR/$NUM ]; then | ||
29 | echo "$TMPDIR/$NUM already exits prior to $f" | ||
30 | exit 1 | ||
31 | fi | ||
32 | exec 3>>$TMPDIR/$NUM | ||
33 | echo $f | sed 's,\.\./,,g' > $TMPDIR/.$NUM | ||
34 | /bin/echo "$LINE" | sed "s/$PREFIX:[0-9]*//" >&3 | ||
35 | ;; | ||
36 | *:\**) | ||
37 | /bin/echo "$LINE" | sed -e "s/:\*/*/" -e "s,/\*\*/,," >&3 | ||
38 | echo >&3 | ||
39 | exec 3>/dev/null | ||
40 | ;; | ||
41 | *) | ||
42 | /bin/echo "$LINE" >&3 | ||
43 | ;; | ||
44 | esac | ||
45 | done < $f | ||
46 | echo >&3 | ||
47 | exec 3>/dev/null | ||
48 | done | ||
49 | |||
50 | LASTFILE="" | ||
51 | for f in $TMPDIR/*; do | ||
52 | if [ "$LASTFILE" != $(cat $TMPDIR/.$(basename $f) ) ]; then | ||
53 | LASTFILE=$(cat $TMPDIR/.$(basename $f) ) | ||
54 | echo "[ $LASTFILE ]" | ||
55 | fi | ||
56 | cat $f | ||
57 | done | ||
58 | |||
diff --git a/tools/lguest/lguest.c b/tools/lguest/lguest.c new file mode 100644 index 000000000000..f759f4f097c7 --- /dev/null +++ b/tools/lguest/lguest.c | |||
@@ -0,0 +1,2065 @@ | |||
1 | /*P:100 | ||
2 | * This is the Launcher code, a simple program which lays out the "physical" | ||
3 | * memory for the new Guest by mapping the kernel image and the virtual | ||
4 | * devices, then opens /dev/lguest to tell the kernel about the Guest and | ||
5 | * control it. | ||
6 | :*/ | ||
7 | #define _LARGEFILE64_SOURCE | ||
8 | #define _GNU_SOURCE | ||
9 | #include <stdio.h> | ||
10 | #include <string.h> | ||
11 | #include <unistd.h> | ||
12 | #include <err.h> | ||
13 | #include <stdint.h> | ||
14 | #include <stdlib.h> | ||
15 | #include <elf.h> | ||
16 | #include <sys/mman.h> | ||
17 | #include <sys/param.h> | ||
18 | #include <sys/types.h> | ||
19 | #include <sys/stat.h> | ||
20 | #include <sys/wait.h> | ||
21 | #include <sys/eventfd.h> | ||
22 | #include <fcntl.h> | ||
23 | #include <stdbool.h> | ||
24 | #include <errno.h> | ||
25 | #include <ctype.h> | ||
26 | #include <sys/socket.h> | ||
27 | #include <sys/ioctl.h> | ||
28 | #include <sys/time.h> | ||
29 | #include <time.h> | ||
30 | #include <netinet/in.h> | ||
31 | #include <net/if.h> | ||
32 | #include <linux/sockios.h> | ||
33 | #include <linux/if_tun.h> | ||
34 | #include <sys/uio.h> | ||
35 | #include <termios.h> | ||
36 | #include <getopt.h> | ||
37 | #include <assert.h> | ||
38 | #include <sched.h> | ||
39 | #include <limits.h> | ||
40 | #include <stddef.h> | ||
41 | #include <signal.h> | ||
42 | #include <pwd.h> | ||
43 | #include <grp.h> | ||
44 | |||
45 | #include <linux/virtio_config.h> | ||
46 | #include <linux/virtio_net.h> | ||
47 | #include <linux/virtio_blk.h> | ||
48 | #include <linux/virtio_console.h> | ||
49 | #include <linux/virtio_rng.h> | ||
50 | #include <linux/virtio_ring.h> | ||
51 | #include <asm/bootparam.h> | ||
52 | #include "../../include/linux/lguest_launcher.h" | ||
53 | /*L:110 | ||
54 | * We can ignore the 43 include files we need for this program, but I do want | ||
55 | * to draw attention to the use of kernel-style types. | ||
56 | * | ||
57 | * As Linus said, "C is a Spartan language, and so should your naming be." I | ||
58 | * like these abbreviations, so we define them here. Note that u64 is always | ||
59 | * unsigned long long, which works on all Linux systems: this means that we can | ||
60 | * use %llu in printf for any u64. | ||
61 | */ | ||
62 | typedef unsigned long long u64; | ||
63 | typedef uint32_t u32; | ||
64 | typedef uint16_t u16; | ||
65 | typedef uint8_t u8; | ||
66 | /*:*/ | ||
67 | |||
68 | #define BRIDGE_PFX "bridge:" | ||
69 | #ifndef SIOCBRADDIF | ||
70 | #define SIOCBRADDIF 0x89a2 /* add interface to bridge */ | ||
71 | #endif | ||
72 | /* We can have up to 256 pages for devices. */ | ||
73 | #define DEVICE_PAGES 256 | ||
74 | /* This will occupy 3 pages: it must be a power of 2. */ | ||
75 | #define VIRTQUEUE_NUM 256 | ||
76 | |||
77 | /*L:120 | ||
78 | * verbose is both a global flag and a macro. The C preprocessor allows | ||
79 | * this, and although I wouldn't recommend it, it works quite nicely here. | ||
80 | */ | ||
81 | static bool verbose; | ||
82 | #define verbose(args...) \ | ||
83 | do { if (verbose) printf(args); } while(0) | ||
84 | /*:*/ | ||
85 | |||
86 | /* The pointer to the start of guest memory. */ | ||
87 | static void *guest_base; | ||
88 | /* The maximum guest physical address allowed, and maximum possible. */ | ||
89 | static unsigned long guest_limit, guest_max; | ||
90 | /* The /dev/lguest file descriptor. */ | ||
91 | static int lguest_fd; | ||
92 | |||
93 | /* a per-cpu variable indicating whose vcpu is currently running */ | ||
94 | static unsigned int __thread cpu_id; | ||
95 | |||
96 | /* This is our list of devices. */ | ||
97 | struct device_list { | ||
98 | /* Counter to assign interrupt numbers. */ | ||
99 | unsigned int next_irq; | ||
100 | |||
101 | /* Counter to print out convenient device numbers. */ | ||
102 | unsigned int device_num; | ||
103 | |||
104 | /* The descriptor page for the devices. */ | ||
105 | u8 *descpage; | ||
106 | |||
107 | /* A single linked list of devices. */ | ||
108 | struct device *dev; | ||
109 | /* And a pointer to the last device for easy append. */ | ||
110 | struct device *lastdev; | ||
111 | }; | ||
112 | |||
113 | /* The list of Guest devices, based on command line arguments. */ | ||
114 | static struct device_list devices; | ||
115 | |||
116 | /* The device structure describes a single device. */ | ||
117 | struct device { | ||
118 | /* The linked-list pointer. */ | ||
119 | struct device *next; | ||
120 | |||
121 | /* The device's descriptor, as mapped into the Guest. */ | ||
122 | struct lguest_device_desc *desc; | ||
123 | |||
124 | /* We can't trust desc values once Guest has booted: we use these. */ | ||
125 | unsigned int feature_len; | ||
126 | unsigned int num_vq; | ||
127 | |||
128 | /* The name of this device, for --verbose. */ | ||
129 | const char *name; | ||
130 | |||
131 | /* Any queues attached to this device */ | ||
132 | struct virtqueue *vq; | ||
133 | |||
134 | /* Is it operational */ | ||
135 | bool running; | ||
136 | |||
137 | /* Device-specific data. */ | ||
138 | void *priv; | ||
139 | }; | ||
140 | |||
141 | /* The virtqueue structure describes a queue attached to a device. */ | ||
142 | struct virtqueue { | ||
143 | struct virtqueue *next; | ||
144 | |||
145 | /* Which device owns me. */ | ||
146 | struct device *dev; | ||
147 | |||
148 | /* The configuration for this queue. */ | ||
149 | struct lguest_vqconfig config; | ||
150 | |||
151 | /* The actual ring of buffers. */ | ||
152 | struct vring vring; | ||
153 | |||
154 | /* Last available index we saw. */ | ||
155 | u16 last_avail_idx; | ||
156 | |||
157 | /* How many are used since we sent last irq? */ | ||
158 | unsigned int pending_used; | ||
159 | |||
160 | /* Eventfd where Guest notifications arrive. */ | ||
161 | int eventfd; | ||
162 | |||
163 | /* Function for the thread which is servicing this virtqueue. */ | ||
164 | void (*service)(struct virtqueue *vq); | ||
165 | pid_t thread; | ||
166 | }; | ||
167 | |||
168 | /* Remember the arguments to the program so we can "reboot" */ | ||
169 | static char **main_args; | ||
170 | |||
171 | /* The original tty settings to restore on exit. */ | ||
172 | static struct termios orig_term; | ||
173 | |||
174 | /* | ||
175 | * We have to be careful with barriers: our devices are all run in separate | ||
176 | * threads and so we need to make sure that changes visible to the Guest happen | ||
177 | * in precise order. | ||
178 | */ | ||
179 | #define wmb() __asm__ __volatile__("" : : : "memory") | ||
180 | #define mb() __asm__ __volatile__("" : : : "memory") | ||
181 | |||
182 | /* | ||
183 | * Convert an iovec element to the given type. | ||
184 | * | ||
185 | * This is a fairly ugly trick: we need to know the size of the type and | ||
186 | * alignment requirement to check the pointer is kosher. It's also nice to | ||
187 | * have the name of the type in case we report failure. | ||
188 | * | ||
189 | * Typing those three things all the time is cumbersome and error prone, so we | ||
190 | * have a macro which sets them all up and passes to the real function. | ||
191 | */ | ||
192 | #define convert(iov, type) \ | ||
193 | ((type *)_convert((iov), sizeof(type), __alignof__(type), #type)) | ||
194 | |||
195 | static void *_convert(struct iovec *iov, size_t size, size_t align, | ||
196 | const char *name) | ||
197 | { | ||
198 | if (iov->iov_len != size) | ||
199 | errx(1, "Bad iovec size %zu for %s", iov->iov_len, name); | ||
200 | if ((unsigned long)iov->iov_base % align != 0) | ||
201 | errx(1, "Bad alignment %p for %s", iov->iov_base, name); | ||
202 | return iov->iov_base; | ||
203 | } | ||
204 | |||
205 | /* Wrapper for the last available index. Makes it easier to change. */ | ||
206 | #define lg_last_avail(vq) ((vq)->last_avail_idx) | ||
207 | |||
208 | /* | ||
209 | * The virtio configuration space is defined to be little-endian. x86 is | ||
210 | * little-endian too, but it's nice to be explicit so we have these helpers. | ||
211 | */ | ||
212 | #define cpu_to_le16(v16) (v16) | ||
213 | #define cpu_to_le32(v32) (v32) | ||
214 | #define cpu_to_le64(v64) (v64) | ||
215 | #define le16_to_cpu(v16) (v16) | ||
216 | #define le32_to_cpu(v32) (v32) | ||
217 | #define le64_to_cpu(v64) (v64) | ||
218 | |||
219 | /* Is this iovec empty? */ | ||
220 | static bool iov_empty(const struct iovec iov[], unsigned int num_iov) | ||
221 | { | ||
222 | unsigned int i; | ||
223 | |||
224 | for (i = 0; i < num_iov; i++) | ||
225 | if (iov[i].iov_len) | ||
226 | return false; | ||
227 | return true; | ||
228 | } | ||
229 | |||
230 | /* Take len bytes from the front of this iovec. */ | ||
231 | static void iov_consume(struct iovec iov[], unsigned num_iov, unsigned len) | ||
232 | { | ||
233 | unsigned int i; | ||
234 | |||
235 | for (i = 0; i < num_iov; i++) { | ||
236 | unsigned int used; | ||
237 | |||
238 | used = iov[i].iov_len < len ? iov[i].iov_len : len; | ||
239 | iov[i].iov_base += used; | ||
240 | iov[i].iov_len -= used; | ||
241 | len -= used; | ||
242 | } | ||
243 | assert(len == 0); | ||
244 | } | ||
245 | |||
246 | /* The device virtqueue descriptors are followed by feature bitmasks. */ | ||
247 | static u8 *get_feature_bits(struct device *dev) | ||
248 | { | ||
249 | return (u8 *)(dev->desc + 1) | ||
250 | + dev->num_vq * sizeof(struct lguest_vqconfig); | ||
251 | } | ||
252 | |||
253 | /*L:100 | ||
254 | * The Launcher code itself takes us out into userspace, that scary place where | ||
255 | * pointers run wild and free! Unfortunately, like most userspace programs, | ||
256 | * it's quite boring (which is why everyone likes to hack on the kernel!). | ||
257 | * Perhaps if you make up an Lguest Drinking Game at this point, it will get | ||
258 | * you through this section. Or, maybe not. | ||
259 | * | ||
260 | * The Launcher sets up a big chunk of memory to be the Guest's "physical" | ||
261 | * memory and stores it in "guest_base". In other words, Guest physical == | ||
262 | * Launcher virtual with an offset. | ||
263 | * | ||
264 | * This can be tough to get your head around, but usually it just means that we | ||
265 | * use these trivial conversion functions when the Guest gives us its | ||
266 | * "physical" addresses: | ||
267 | */ | ||
268 | static void *from_guest_phys(unsigned long addr) | ||
269 | { | ||
270 | return guest_base + addr; | ||
271 | } | ||
272 | |||
273 | static unsigned long to_guest_phys(const void *addr) | ||
274 | { | ||
275 | return (addr - guest_base); | ||
276 | } | ||
277 | |||
278 | /*L:130 | ||
279 | * Loading the Kernel. | ||
280 | * | ||
281 | * We start with couple of simple helper routines. open_or_die() avoids | ||
282 | * error-checking code cluttering the callers: | ||
283 | */ | ||
284 | static int open_or_die(const char *name, int flags) | ||
285 | { | ||
286 | int fd = open(name, flags); | ||
287 | if (fd < 0) | ||
288 | err(1, "Failed to open %s", name); | ||
289 | return fd; | ||
290 | } | ||
291 | |||
292 | /* map_zeroed_pages() takes a number of pages. */ | ||
293 | static void *map_zeroed_pages(unsigned int num) | ||
294 | { | ||
295 | int fd = open_or_die("/dev/zero", O_RDONLY); | ||
296 | void *addr; | ||
297 | |||
298 | /* | ||
299 | * We use a private mapping (ie. if we write to the page, it will be | ||
300 | * copied). We allocate an extra two pages PROT_NONE to act as guard | ||
301 | * pages against read/write attempts that exceed allocated space. | ||
302 | */ | ||
303 | addr = mmap(NULL, getpagesize() * (num+2), | ||
304 | PROT_NONE, MAP_PRIVATE, fd, 0); | ||
305 | |||
306 | if (addr == MAP_FAILED) | ||
307 | err(1, "Mmapping %u pages of /dev/zero", num); | ||
308 | |||
309 | if (mprotect(addr + getpagesize(), getpagesize() * num, | ||
310 | PROT_READ|PROT_WRITE) == -1) | ||
311 | err(1, "mprotect rw %u pages failed", num); | ||
312 | |||
313 | /* | ||
314 | * One neat mmap feature is that you can close the fd, and it | ||
315 | * stays mapped. | ||
316 | */ | ||
317 | close(fd); | ||
318 | |||
319 | /* Return address after PROT_NONE page */ | ||
320 | return addr + getpagesize(); | ||
321 | } | ||
322 | |||
323 | /* Get some more pages for a device. */ | ||
324 | static void *get_pages(unsigned int num) | ||
325 | { | ||
326 | void *addr = from_guest_phys(guest_limit); | ||
327 | |||
328 | guest_limit += num * getpagesize(); | ||
329 | if (guest_limit > guest_max) | ||
330 | errx(1, "Not enough memory for devices"); | ||
331 | return addr; | ||
332 | } | ||
333 | |||
334 | /* | ||
335 | * This routine is used to load the kernel or initrd. It tries mmap, but if | ||
336 | * that fails (Plan 9's kernel file isn't nicely aligned on page boundaries), | ||
337 | * it falls back to reading the memory in. | ||
338 | */ | ||
339 | static void map_at(int fd, void *addr, unsigned long offset, unsigned long len) | ||
340 | { | ||
341 | ssize_t r; | ||
342 | |||
343 | /* | ||
344 | * We map writable even though for some segments are marked read-only. | ||
345 | * The kernel really wants to be writable: it patches its own | ||
346 | * instructions. | ||
347 | * | ||
348 | * MAP_PRIVATE means that the page won't be copied until a write is | ||
349 | * done to it. This allows us to share untouched memory between | ||
350 | * Guests. | ||
351 | */ | ||
352 | if (mmap(addr, len, PROT_READ|PROT_WRITE, | ||
353 | MAP_FIXED|MAP_PRIVATE, fd, offset) != MAP_FAILED) | ||
354 | return; | ||
355 | |||
356 | /* pread does a seek and a read in one shot: saves a few lines. */ | ||
357 | r = pread(fd, addr, len, offset); | ||
358 | if (r != len) | ||
359 | err(1, "Reading offset %lu len %lu gave %zi", offset, len, r); | ||
360 | } | ||
361 | |||
362 | /* | ||
363 | * This routine takes an open vmlinux image, which is in ELF, and maps it into | ||
364 | * the Guest memory. ELF = Embedded Linking Format, which is the format used | ||
365 | * by all modern binaries on Linux including the kernel. | ||
366 | * | ||
367 | * The ELF headers give *two* addresses: a physical address, and a virtual | ||
368 | * address. We use the physical address; the Guest will map itself to the | ||
369 | * virtual address. | ||
370 | * | ||
371 | * We return the starting address. | ||
372 | */ | ||
373 | static unsigned long map_elf(int elf_fd, const Elf32_Ehdr *ehdr) | ||
374 | { | ||
375 | Elf32_Phdr phdr[ehdr->e_phnum]; | ||
376 | unsigned int i; | ||
377 | |||
378 | /* | ||
379 | * Sanity checks on the main ELF header: an x86 executable with a | ||
380 | * reasonable number of correctly-sized program headers. | ||
381 | */ | ||
382 | if (ehdr->e_type != ET_EXEC | ||
383 | || ehdr->e_machine != EM_386 | ||
384 | || ehdr->e_phentsize != sizeof(Elf32_Phdr) | ||
385 | || ehdr->e_phnum < 1 || ehdr->e_phnum > 65536U/sizeof(Elf32_Phdr)) | ||
386 | errx(1, "Malformed elf header"); | ||
387 | |||
388 | /* | ||
389 | * An ELF executable contains an ELF header and a number of "program" | ||
390 | * headers which indicate which parts ("segments") of the program to | ||
391 | * load where. | ||
392 | */ | ||
393 | |||
394 | /* We read in all the program headers at once: */ | ||
395 | if (lseek(elf_fd, ehdr->e_phoff, SEEK_SET) < 0) | ||
396 | err(1, "Seeking to program headers"); | ||
397 | if (read(elf_fd, phdr, sizeof(phdr)) != sizeof(phdr)) | ||
398 | err(1, "Reading program headers"); | ||
399 | |||
400 | /* | ||
401 | * Try all the headers: there are usually only three. A read-only one, | ||
402 | * a read-write one, and a "note" section which we don't load. | ||
403 | */ | ||
404 | for (i = 0; i < ehdr->e_phnum; i++) { | ||
405 | /* If this isn't a loadable segment, we ignore it */ | ||
406 | if (phdr[i].p_type != PT_LOAD) | ||
407 | continue; | ||
408 | |||
409 | verbose("Section %i: size %i addr %p\n", | ||
410 | i, phdr[i].p_memsz, (void *)phdr[i].p_paddr); | ||
411 | |||
412 | /* We map this section of the file at its physical address. */ | ||
413 | map_at(elf_fd, from_guest_phys(phdr[i].p_paddr), | ||
414 | phdr[i].p_offset, phdr[i].p_filesz); | ||
415 | } | ||
416 | |||
417 | /* The entry point is given in the ELF header. */ | ||
418 | return ehdr->e_entry; | ||
419 | } | ||
420 | |||
421 | /*L:150 | ||
422 | * A bzImage, unlike an ELF file, is not meant to be loaded. You're supposed | ||
423 | * to jump into it and it will unpack itself. We used to have to perform some | ||
424 | * hairy magic because the unpacking code scared me. | ||
425 | * | ||
426 | * Fortunately, Jeremy Fitzhardinge convinced me it wasn't that hard and wrote | ||
427 | * a small patch to jump over the tricky bits in the Guest, so now we just read | ||
428 | * the funky header so we know where in the file to load, and away we go! | ||
429 | */ | ||
430 | static unsigned long load_bzimage(int fd) | ||
431 | { | ||
432 | struct boot_params boot; | ||
433 | int r; | ||
434 | /* Modern bzImages get loaded at 1M. */ | ||
435 | void *p = from_guest_phys(0x100000); | ||
436 | |||
437 | /* | ||
438 | * Go back to the start of the file and read the header. It should be | ||
439 | * a Linux boot header (see Documentation/x86/boot.txt) | ||
440 | */ | ||
441 | lseek(fd, 0, SEEK_SET); | ||
442 | read(fd, &boot, sizeof(boot)); | ||
443 | |||
444 | /* Inside the setup_hdr, we expect the magic "HdrS" */ | ||
445 | if (memcmp(&boot.hdr.header, "HdrS", 4) != 0) | ||
446 | errx(1, "This doesn't look like a bzImage to me"); | ||
447 | |||
448 | /* Skip over the extra sectors of the header. */ | ||
449 | lseek(fd, (boot.hdr.setup_sects+1) * 512, SEEK_SET); | ||
450 | |||
451 | /* Now read everything into memory. in nice big chunks. */ | ||
452 | while ((r = read(fd, p, 65536)) > 0) | ||
453 | p += r; | ||
454 | |||
455 | /* Finally, code32_start tells us where to enter the kernel. */ | ||
456 | return boot.hdr.code32_start; | ||
457 | } | ||
458 | |||
459 | /*L:140 | ||
460 | * Loading the kernel is easy when it's a "vmlinux", but most kernels | ||
461 | * come wrapped up in the self-decompressing "bzImage" format. With a little | ||
462 | * work, we can load those, too. | ||
463 | */ | ||
464 | static unsigned long load_kernel(int fd) | ||
465 | { | ||
466 | Elf32_Ehdr hdr; | ||
467 | |||
468 | /* Read in the first few bytes. */ | ||
469 | if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) | ||
470 | err(1, "Reading kernel"); | ||
471 | |||
472 | /* If it's an ELF file, it starts with "\177ELF" */ | ||
473 | if (memcmp(hdr.e_ident, ELFMAG, SELFMAG) == 0) | ||
474 | return map_elf(fd, &hdr); | ||
475 | |||
476 | /* Otherwise we assume it's a bzImage, and try to load it. */ | ||
477 | return load_bzimage(fd); | ||
478 | } | ||
479 | |||
480 | /* | ||
481 | * This is a trivial little helper to align pages. Andi Kleen hated it because | ||
482 | * it calls getpagesize() twice: "it's dumb code." | ||
483 | * | ||
484 | * Kernel guys get really het up about optimization, even when it's not | ||
485 | * necessary. I leave this code as a reaction against that. | ||
486 | */ | ||
487 | static inline unsigned long page_align(unsigned long addr) | ||
488 | { | ||
489 | /* Add upwards and truncate downwards. */ | ||
490 | return ((addr + getpagesize()-1) & ~(getpagesize()-1)); | ||
491 | } | ||
492 | |||
493 | /*L:180 | ||
494 | * An "initial ram disk" is a disk image loaded into memory along with the | ||
495 | * kernel which the kernel can use to boot from without needing any drivers. | ||
496 | * Most distributions now use this as standard: the initrd contains the code to | ||
497 | * load the appropriate driver modules for the current machine. | ||
498 | * | ||
499 | * Importantly, James Morris works for RedHat, and Fedora uses initrds for its | ||
500 | * kernels. He sent me this (and tells me when I break it). | ||
501 | */ | ||
502 | static unsigned long load_initrd(const char *name, unsigned long mem) | ||
503 | { | ||
504 | int ifd; | ||
505 | struct stat st; | ||
506 | unsigned long len; | ||
507 | |||
508 | ifd = open_or_die(name, O_RDONLY); | ||
509 | /* fstat() is needed to get the file size. */ | ||
510 | if (fstat(ifd, &st) < 0) | ||
511 | err(1, "fstat() on initrd '%s'", name); | ||
512 | |||
513 | /* | ||
514 | * We map the initrd at the top of memory, but mmap wants it to be | ||
515 | * page-aligned, so we round the size up for that. | ||
516 | */ | ||
517 | len = page_align(st.st_size); | ||
518 | map_at(ifd, from_guest_phys(mem - len), 0, st.st_size); | ||
519 | /* | ||
520 | * Once a file is mapped, you can close the file descriptor. It's a | ||
521 | * little odd, but quite useful. | ||
522 | */ | ||
523 | close(ifd); | ||
524 | verbose("mapped initrd %s size=%lu @ %p\n", name, len, (void*)mem-len); | ||
525 | |||
526 | /* We return the initrd size. */ | ||
527 | return len; | ||
528 | } | ||
529 | /*:*/ | ||
530 | |||
531 | /* | ||
532 | * Simple routine to roll all the commandline arguments together with spaces | ||
533 | * between them. | ||
534 | */ | ||
535 | static void concat(char *dst, char *args[]) | ||
536 | { | ||
537 | unsigned int i, len = 0; | ||
538 | |||
539 | for (i = 0; args[i]; i++) { | ||
540 | if (i) { | ||
541 | strcat(dst+len, " "); | ||
542 | len++; | ||
543 | } | ||
544 | strcpy(dst+len, args[i]); | ||
545 | len += strlen(args[i]); | ||
546 | } | ||
547 | /* In case it's empty. */ | ||
548 | dst[len] = '\0'; | ||
549 | } | ||
550 | |||
551 | /*L:185 | ||
552 | * This is where we actually tell the kernel to initialize the Guest. We | ||
553 | * saw the arguments it expects when we looked at initialize() in lguest_user.c: | ||
554 | * the base of Guest "physical" memory, the top physical page to allow and the | ||
555 | * entry point for the Guest. | ||
556 | */ | ||
557 | static void tell_kernel(unsigned long start) | ||
558 | { | ||
559 | unsigned long args[] = { LHREQ_INITIALIZE, | ||
560 | (unsigned long)guest_base, | ||
561 | guest_limit / getpagesize(), start }; | ||
562 | verbose("Guest: %p - %p (%#lx)\n", | ||
563 | guest_base, guest_base + guest_limit, guest_limit); | ||
564 | lguest_fd = open_or_die("/dev/lguest", O_RDWR); | ||
565 | if (write(lguest_fd, args, sizeof(args)) < 0) | ||
566 | err(1, "Writing to /dev/lguest"); | ||
567 | } | ||
568 | /*:*/ | ||
569 | |||
570 | /*L:200 | ||
571 | * Device Handling. | ||
572 | * | ||
573 | * When the Guest gives us a buffer, it sends an array of addresses and sizes. | ||
574 | * We need to make sure it's not trying to reach into the Launcher itself, so | ||
575 | * we have a convenient routine which checks it and exits with an error message | ||
576 | * if something funny is going on: | ||
577 | */ | ||
578 | static void *_check_pointer(unsigned long addr, unsigned int size, | ||
579 | unsigned int line) | ||
580 | { | ||
581 | /* | ||
582 | * Check if the requested address and size exceeds the allocated memory, | ||
583 | * or addr + size wraps around. | ||
584 | */ | ||
585 | if ((addr + size) > guest_limit || (addr + size) < addr) | ||
586 | errx(1, "%s:%i: Invalid address %#lx", __FILE__, line, addr); | ||
587 | /* | ||
588 | * We return a pointer for the caller's convenience, now we know it's | ||
589 | * safe to use. | ||
590 | */ | ||
591 | return from_guest_phys(addr); | ||
592 | } | ||
593 | /* A macro which transparently hands the line number to the real function. */ | ||
594 | #define check_pointer(addr,size) _check_pointer(addr, size, __LINE__) | ||
595 | |||
596 | /* | ||
597 | * Each buffer in the virtqueues is actually a chain of descriptors. This | ||
598 | * function returns the next descriptor in the chain, or vq->vring.num if we're | ||
599 | * at the end. | ||
600 | */ | ||
601 | static unsigned next_desc(struct vring_desc *desc, | ||
602 | unsigned int i, unsigned int max) | ||
603 | { | ||
604 | unsigned int next; | ||
605 | |||
606 | /* If this descriptor says it doesn't chain, we're done. */ | ||
607 | if (!(desc[i].flags & VRING_DESC_F_NEXT)) | ||
608 | return max; | ||
609 | |||
610 | /* Check they're not leading us off end of descriptors. */ | ||
611 | next = desc[i].next; | ||
612 | /* Make sure compiler knows to grab that: we don't want it changing! */ | ||
613 | wmb(); | ||
614 | |||
615 | if (next >= max) | ||
616 | errx(1, "Desc next is %u", next); | ||
617 | |||
618 | return next; | ||
619 | } | ||
620 | |||
621 | /* | ||
622 | * This actually sends the interrupt for this virtqueue, if we've used a | ||
623 | * buffer. | ||
624 | */ | ||
625 | static void trigger_irq(struct virtqueue *vq) | ||
626 | { | ||
627 | unsigned long buf[] = { LHREQ_IRQ, vq->config.irq }; | ||
628 | |||
629 | /* Don't inform them if nothing used. */ | ||
630 | if (!vq->pending_used) | ||
631 | return; | ||
632 | vq->pending_used = 0; | ||
633 | |||
634 | /* If they don't want an interrupt, don't send one... */ | ||
635 | if (vq->vring.avail->flags & VRING_AVAIL_F_NO_INTERRUPT) { | ||
636 | return; | ||
637 | } | ||
638 | |||
639 | /* Send the Guest an interrupt tell them we used something up. */ | ||
640 | if (write(lguest_fd, buf, sizeof(buf)) != 0) | ||
641 | err(1, "Triggering irq %i", vq->config.irq); | ||
642 | } | ||
643 | |||
644 | /* | ||
645 | * This looks in the virtqueue for the first available buffer, and converts | ||
646 | * it to an iovec for convenient access. Since descriptors consist of some | ||
647 | * number of output then some number of input descriptors, it's actually two | ||
648 | * iovecs, but we pack them into one and note how many of each there were. | ||
649 | * | ||
650 | * This function waits if necessary, and returns the descriptor number found. | ||
651 | */ | ||
652 | static unsigned wait_for_vq_desc(struct virtqueue *vq, | ||
653 | struct iovec iov[], | ||
654 | unsigned int *out_num, unsigned int *in_num) | ||
655 | { | ||
656 | unsigned int i, head, max; | ||
657 | struct vring_desc *desc; | ||
658 | u16 last_avail = lg_last_avail(vq); | ||
659 | |||
660 | /* There's nothing available? */ | ||
661 | while (last_avail == vq->vring.avail->idx) { | ||
662 | u64 event; | ||
663 | |||
664 | /* | ||
665 | * Since we're about to sleep, now is a good time to tell the | ||
666 | * Guest about what we've used up to now. | ||
667 | */ | ||
668 | trigger_irq(vq); | ||
669 | |||
670 | /* OK, now we need to know about added descriptors. */ | ||
671 | vq->vring.used->flags &= ~VRING_USED_F_NO_NOTIFY; | ||
672 | |||
673 | /* | ||
674 | * They could have slipped one in as we were doing that: make | ||
675 | * sure it's written, then check again. | ||
676 | */ | ||
677 | mb(); | ||
678 | if (last_avail != vq->vring.avail->idx) { | ||
679 | vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY; | ||
680 | break; | ||
681 | } | ||
682 | |||
683 | /* Nothing new? Wait for eventfd to tell us they refilled. */ | ||
684 | if (read(vq->eventfd, &event, sizeof(event)) != sizeof(event)) | ||
685 | errx(1, "Event read failed?"); | ||
686 | |||
687 | /* We don't need to be notified again. */ | ||
688 | vq->vring.used->flags |= VRING_USED_F_NO_NOTIFY; | ||
689 | } | ||
690 | |||
691 | /* Check it isn't doing very strange things with descriptor numbers. */ | ||
692 | if ((u16)(vq->vring.avail->idx - last_avail) > vq->vring.num) | ||
693 | errx(1, "Guest moved used index from %u to %u", | ||
694 | last_avail, vq->vring.avail->idx); | ||
695 | |||
696 | /* | ||
697 | * Grab the next descriptor number they're advertising, and increment | ||
698 | * the index we've seen. | ||
699 | */ | ||
700 | head = vq->vring.avail->ring[last_avail % vq->vring.num]; | ||
701 | lg_last_avail(vq)++; | ||
702 | |||
703 | /* If their number is silly, that's a fatal mistake. */ | ||
704 | if (head >= vq->vring.num) | ||
705 | errx(1, "Guest says index %u is available", head); | ||
706 | |||
707 | /* When we start there are none of either input nor output. */ | ||
708 | *out_num = *in_num = 0; | ||
709 | |||
710 | max = vq->vring.num; | ||
711 | desc = vq->vring.desc; | ||
712 | i = head; | ||
713 | |||
714 | /* | ||
715 | * If this is an indirect entry, then this buffer contains a descriptor | ||
716 | * table which we handle as if it's any normal descriptor chain. | ||
717 | */ | ||
718 | if (desc[i].flags & VRING_DESC_F_INDIRECT) { | ||
719 | if (desc[i].len % sizeof(struct vring_desc)) | ||
720 | errx(1, "Invalid size for indirect buffer table"); | ||
721 | |||
722 | max = desc[i].len / sizeof(struct vring_desc); | ||
723 | desc = check_pointer(desc[i].addr, desc[i].len); | ||
724 | i = 0; | ||
725 | } | ||
726 | |||
727 | do { | ||
728 | /* Grab the first descriptor, and check it's OK. */ | ||
729 | iov[*out_num + *in_num].iov_len = desc[i].len; | ||
730 | iov[*out_num + *in_num].iov_base | ||
731 | = check_pointer(desc[i].addr, desc[i].len); | ||
732 | /* If this is an input descriptor, increment that count. */ | ||
733 | if (desc[i].flags & VRING_DESC_F_WRITE) | ||
734 | (*in_num)++; | ||
735 | else { | ||
736 | /* | ||
737 | * If it's an output descriptor, they're all supposed | ||
738 | * to come before any input descriptors. | ||
739 | */ | ||
740 | if (*in_num) | ||
741 | errx(1, "Descriptor has out after in"); | ||
742 | (*out_num)++; | ||
743 | } | ||
744 | |||
745 | /* If we've got too many, that implies a descriptor loop. */ | ||
746 | if (*out_num + *in_num > max) | ||
747 | errx(1, "Looped descriptor"); | ||
748 | } while ((i = next_desc(desc, i, max)) != max); | ||
749 | |||
750 | return head; | ||
751 | } | ||
752 | |||
753 | /* | ||
754 | * After we've used one of their buffers, we tell the Guest about it. Sometime | ||
755 | * later we'll want to send them an interrupt using trigger_irq(); note that | ||
756 | * wait_for_vq_desc() does that for us if it has to wait. | ||
757 | */ | ||
758 | static void add_used(struct virtqueue *vq, unsigned int head, int len) | ||
759 | { | ||
760 | struct vring_used_elem *used; | ||
761 | |||
762 | /* | ||
763 | * The virtqueue contains a ring of used buffers. Get a pointer to the | ||
764 | * next entry in that used ring. | ||
765 | */ | ||
766 | used = &vq->vring.used->ring[vq->vring.used->idx % vq->vring.num]; | ||
767 | used->id = head; | ||
768 | used->len = len; | ||
769 | /* Make sure buffer is written before we update index. */ | ||
770 | wmb(); | ||
771 | vq->vring.used->idx++; | ||
772 | vq->pending_used++; | ||
773 | } | ||
774 | |||
775 | /* And here's the combo meal deal. Supersize me! */ | ||
776 | static void add_used_and_trigger(struct virtqueue *vq, unsigned head, int len) | ||
777 | { | ||
778 | add_used(vq, head, len); | ||
779 | trigger_irq(vq); | ||
780 | } | ||
781 | |||
782 | /* | ||
783 | * The Console | ||
784 | * | ||
785 | * We associate some data with the console for our exit hack. | ||
786 | */ | ||
787 | struct console_abort { | ||
788 | /* How many times have they hit ^C? */ | ||
789 | int count; | ||
790 | /* When did they start? */ | ||
791 | struct timeval start; | ||
792 | }; | ||
793 | |||
794 | /* This is the routine which handles console input (ie. stdin). */ | ||
795 | static void console_input(struct virtqueue *vq) | ||
796 | { | ||
797 | int len; | ||
798 | unsigned int head, in_num, out_num; | ||
799 | struct console_abort *abort = vq->dev->priv; | ||
800 | struct iovec iov[vq->vring.num]; | ||
801 | |||
802 | /* Make sure there's a descriptor available. */ | ||
803 | head = wait_for_vq_desc(vq, iov, &out_num, &in_num); | ||
804 | if (out_num) | ||
805 | errx(1, "Output buffers in console in queue?"); | ||
806 | |||
807 | /* Read into it. This is where we usually wait. */ | ||
808 | len = readv(STDIN_FILENO, iov, in_num); | ||
809 | if (len <= 0) { | ||
810 | /* Ran out of input? */ | ||
811 | warnx("Failed to get console input, ignoring console."); | ||
812 | /* | ||
813 | * For simplicity, dying threads kill the whole Launcher. So | ||
814 | * just nap here. | ||
815 | */ | ||
816 | for (;;) | ||
817 | pause(); | ||
818 | } | ||
819 | |||
820 | /* Tell the Guest we used a buffer. */ | ||
821 | add_used_and_trigger(vq, head, len); | ||
822 | |||
823 | /* | ||
824 | * Three ^C within one second? Exit. | ||
825 | * | ||
826 | * This is such a hack, but works surprisingly well. Each ^C has to | ||
827 | * be in a buffer by itself, so they can't be too fast. But we check | ||
828 | * that we get three within about a second, so they can't be too | ||
829 | * slow. | ||
830 | */ | ||
831 | if (len != 1 || ((char *)iov[0].iov_base)[0] != 3) { | ||
832 | abort->count = 0; | ||
833 | return; | ||
834 | } | ||
835 | |||
836 | abort->count++; | ||
837 | if (abort->count == 1) | ||
838 | gettimeofday(&abort->start, NULL); | ||
839 | else if (abort->count == 3) { | ||
840 | struct timeval now; | ||
841 | gettimeofday(&now, NULL); | ||
842 | /* Kill all Launcher processes with SIGINT, like normal ^C */ | ||
843 | if (now.tv_sec <= abort->start.tv_sec+1) | ||
844 | kill(0, SIGINT); | ||
845 | abort->count = 0; | ||
846 | } | ||
847 | } | ||
848 | |||
849 | /* This is the routine which handles console output (ie. stdout). */ | ||
850 | static void console_output(struct virtqueue *vq) | ||
851 | { | ||
852 | unsigned int head, out, in; | ||
853 | struct iovec iov[vq->vring.num]; | ||
854 | |||
855 | /* We usually wait in here, for the Guest to give us something. */ | ||
856 | head = wait_for_vq_desc(vq, iov, &out, &in); | ||
857 | if (in) | ||
858 | errx(1, "Input buffers in console output queue?"); | ||
859 | |||
860 | /* writev can return a partial write, so we loop here. */ | ||
861 | while (!iov_empty(iov, out)) { | ||
862 | int len = writev(STDOUT_FILENO, iov, out); | ||
863 | if (len <= 0) { | ||
864 | warn("Write to stdout gave %i (%d)", len, errno); | ||
865 | break; | ||
866 | } | ||
867 | iov_consume(iov, out, len); | ||
868 | } | ||
869 | |||
870 | /* | ||
871 | * We're finished with that buffer: if we're going to sleep, | ||
872 | * wait_for_vq_desc() will prod the Guest with an interrupt. | ||
873 | */ | ||
874 | add_used(vq, head, 0); | ||
875 | } | ||
876 | |||
877 | /* | ||
878 | * The Network | ||
879 | * | ||
880 | * Handling output for network is also simple: we get all the output buffers | ||
881 | * and write them to /dev/net/tun. | ||
882 | */ | ||
883 | struct net_info { | ||
884 | int tunfd; | ||
885 | }; | ||
886 | |||
887 | static void net_output(struct virtqueue *vq) | ||
888 | { | ||
889 | struct net_info *net_info = vq->dev->priv; | ||
890 | unsigned int head, out, in; | ||
891 | struct iovec iov[vq->vring.num]; | ||
892 | |||
893 | /* We usually wait in here for the Guest to give us a packet. */ | ||
894 | head = wait_for_vq_desc(vq, iov, &out, &in); | ||
895 | if (in) | ||
896 | errx(1, "Input buffers in net output queue?"); | ||
897 | /* | ||
898 | * Send the whole thing through to /dev/net/tun. It expects the exact | ||
899 | * same format: what a coincidence! | ||
900 | */ | ||
901 | if (writev(net_info->tunfd, iov, out) < 0) | ||
902 | warnx("Write to tun failed (%d)?", errno); | ||
903 | |||
904 | /* | ||
905 | * Done with that one; wait_for_vq_desc() will send the interrupt if | ||
906 | * all packets are processed. | ||
907 | */ | ||
908 | add_used(vq, head, 0); | ||
909 | } | ||
910 | |||
911 | /* | ||
912 | * Handling network input is a bit trickier, because I've tried to optimize it. | ||
913 | * | ||
914 | * First we have a helper routine which tells is if from this file descriptor | ||
915 | * (ie. the /dev/net/tun device) will block: | ||
916 | */ | ||
917 | static bool will_block(int fd) | ||
918 | { | ||
919 | fd_set fdset; | ||
920 | struct timeval zero = { 0, 0 }; | ||
921 | FD_ZERO(&fdset); | ||
922 | FD_SET(fd, &fdset); | ||
923 | return select(fd+1, &fdset, NULL, NULL, &zero) != 1; | ||
924 | } | ||
925 | |||
926 | /* | ||
927 | * This handles packets coming in from the tun device to our Guest. Like all | ||
928 | * service routines, it gets called again as soon as it returns, so you don't | ||
929 | * see a while(1) loop here. | ||
930 | */ | ||
931 | static void net_input(struct virtqueue *vq) | ||
932 | { | ||
933 | int len; | ||
934 | unsigned int head, out, in; | ||
935 | struct iovec iov[vq->vring.num]; | ||
936 | struct net_info *net_info = vq->dev->priv; | ||
937 | |||
938 | /* | ||
939 | * Get a descriptor to write an incoming packet into. This will also | ||
940 | * send an interrupt if they're out of descriptors. | ||
941 | */ | ||
942 | head = wait_for_vq_desc(vq, iov, &out, &in); | ||
943 | if (out) | ||
944 | errx(1, "Output buffers in net input queue?"); | ||
945 | |||
946 | /* | ||
947 | * If it looks like we'll block reading from the tun device, send them | ||
948 | * an interrupt. | ||
949 | */ | ||
950 | if (vq->pending_used && will_block(net_info->tunfd)) | ||
951 | trigger_irq(vq); | ||
952 | |||
953 | /* | ||
954 | * Read in the packet. This is where we normally wait (when there's no | ||
955 | * incoming network traffic). | ||
956 | */ | ||
957 | len = readv(net_info->tunfd, iov, in); | ||
958 | if (len <= 0) | ||
959 | warn("Failed to read from tun (%d).", errno); | ||
960 | |||
961 | /* | ||
962 | * Mark that packet buffer as used, but don't interrupt here. We want | ||
963 | * to wait until we've done as much work as we can. | ||
964 | */ | ||
965 | add_used(vq, head, len); | ||
966 | } | ||
967 | /*:*/ | ||
968 | |||
969 | /* This is the helper to create threads: run the service routine in a loop. */ | ||
970 | static int do_thread(void *_vq) | ||
971 | { | ||
972 | struct virtqueue *vq = _vq; | ||
973 | |||
974 | for (;;) | ||
975 | vq->service(vq); | ||
976 | return 0; | ||
977 | } | ||
978 | |||
979 | /* | ||
980 | * When a child dies, we kill our entire process group with SIGTERM. This | ||
981 | * also has the side effect that the shell restores the console for us! | ||
982 | */ | ||
983 | static void kill_launcher(int signal) | ||
984 | { | ||
985 | kill(0, SIGTERM); | ||
986 | } | ||
987 | |||
988 | static void reset_device(struct device *dev) | ||
989 | { | ||
990 | struct virtqueue *vq; | ||
991 | |||
992 | verbose("Resetting device %s\n", dev->name); | ||
993 | |||
994 | /* Clear any features they've acked. */ | ||
995 | memset(get_feature_bits(dev) + dev->feature_len, 0, dev->feature_len); | ||
996 | |||
997 | /* We're going to be explicitly killing threads, so ignore them. */ | ||
998 | signal(SIGCHLD, SIG_IGN); | ||
999 | |||
1000 | /* Zero out the virtqueues, get rid of their threads */ | ||
1001 | for (vq = dev->vq; vq; vq = vq->next) { | ||
1002 | if (vq->thread != (pid_t)-1) { | ||
1003 | kill(vq->thread, SIGTERM); | ||
1004 | waitpid(vq->thread, NULL, 0); | ||
1005 | vq->thread = (pid_t)-1; | ||
1006 | } | ||
1007 | memset(vq->vring.desc, 0, | ||
1008 | vring_size(vq->config.num, LGUEST_VRING_ALIGN)); | ||
1009 | lg_last_avail(vq) = 0; | ||
1010 | } | ||
1011 | dev->running = false; | ||
1012 | |||
1013 | /* Now we care if threads die. */ | ||
1014 | signal(SIGCHLD, (void *)kill_launcher); | ||
1015 | } | ||
1016 | |||
1017 | /*L:216 | ||
1018 | * This actually creates the thread which services the virtqueue for a device. | ||
1019 | */ | ||
1020 | static void create_thread(struct virtqueue *vq) | ||
1021 | { | ||
1022 | /* | ||
1023 | * Create stack for thread. Since the stack grows upwards, we point | ||
1024 | * the stack pointer to the end of this region. | ||
1025 | */ | ||
1026 | char *stack = malloc(32768); | ||
1027 | unsigned long args[] = { LHREQ_EVENTFD, | ||
1028 | vq->config.pfn*getpagesize(), 0 }; | ||
1029 | |||
1030 | /* Create a zero-initialized eventfd. */ | ||
1031 | vq->eventfd = eventfd(0, 0); | ||
1032 | if (vq->eventfd < 0) | ||
1033 | err(1, "Creating eventfd"); | ||
1034 | args[2] = vq->eventfd; | ||
1035 | |||
1036 | /* | ||
1037 | * Attach an eventfd to this virtqueue: it will go off when the Guest | ||
1038 | * does an LHCALL_NOTIFY for this vq. | ||
1039 | */ | ||
1040 | if (write(lguest_fd, &args, sizeof(args)) != 0) | ||
1041 | err(1, "Attaching eventfd"); | ||
1042 | |||
1043 | /* | ||
1044 | * CLONE_VM: because it has to access the Guest memory, and SIGCHLD so | ||
1045 | * we get a signal if it dies. | ||
1046 | */ | ||
1047 | vq->thread = clone(do_thread, stack + 32768, CLONE_VM | SIGCHLD, vq); | ||
1048 | if (vq->thread == (pid_t)-1) | ||
1049 | err(1, "Creating clone"); | ||
1050 | |||
1051 | /* We close our local copy now the child has it. */ | ||
1052 | close(vq->eventfd); | ||
1053 | } | ||
1054 | |||
1055 | static void start_device(struct device *dev) | ||
1056 | { | ||
1057 | unsigned int i; | ||
1058 | struct virtqueue *vq; | ||
1059 | |||
1060 | verbose("Device %s OK: offered", dev->name); | ||
1061 | for (i = 0; i < dev->feature_len; i++) | ||
1062 | verbose(" %02x", get_feature_bits(dev)[i]); | ||
1063 | verbose(", accepted"); | ||
1064 | for (i = 0; i < dev->feature_len; i++) | ||
1065 | verbose(" %02x", get_feature_bits(dev) | ||
1066 | [dev->feature_len+i]); | ||
1067 | |||
1068 | for (vq = dev->vq; vq; vq = vq->next) { | ||
1069 | if (vq->service) | ||
1070 | create_thread(vq); | ||
1071 | } | ||
1072 | dev->running = true; | ||
1073 | } | ||
1074 | |||
1075 | static void cleanup_devices(void) | ||
1076 | { | ||
1077 | struct device *dev; | ||
1078 | |||
1079 | for (dev = devices.dev; dev; dev = dev->next) | ||
1080 | reset_device(dev); | ||
1081 | |||
1082 | /* If we saved off the original terminal settings, restore them now. */ | ||
1083 | if (orig_term.c_lflag & (ISIG|ICANON|ECHO)) | ||
1084 | tcsetattr(STDIN_FILENO, TCSANOW, &orig_term); | ||
1085 | } | ||
1086 | |||
1087 | /* When the Guest tells us they updated the status field, we handle it. */ | ||
1088 | static void update_device_status(struct device *dev) | ||
1089 | { | ||
1090 | /* A zero status is a reset, otherwise it's a set of flags. */ | ||
1091 | if (dev->desc->status == 0) | ||
1092 | reset_device(dev); | ||
1093 | else if (dev->desc->status & VIRTIO_CONFIG_S_FAILED) { | ||
1094 | warnx("Device %s configuration FAILED", dev->name); | ||
1095 | if (dev->running) | ||
1096 | reset_device(dev); | ||
1097 | } else { | ||
1098 | if (dev->running) | ||
1099 | err(1, "Device %s features finalized twice", dev->name); | ||
1100 | start_device(dev); | ||
1101 | } | ||
1102 | } | ||
1103 | |||
1104 | /*L:215 | ||
1105 | * This is the generic routine we call when the Guest uses LHCALL_NOTIFY. In | ||
1106 | * particular, it's used to notify us of device status changes during boot. | ||
1107 | */ | ||
1108 | static void handle_output(unsigned long addr) | ||
1109 | { | ||
1110 | struct device *i; | ||
1111 | |||
1112 | /* Check each device. */ | ||
1113 | for (i = devices.dev; i; i = i->next) { | ||
1114 | struct virtqueue *vq; | ||
1115 | |||
1116 | /* | ||
1117 | * Notifications to device descriptors mean they updated the | ||
1118 | * device status. | ||
1119 | */ | ||
1120 | if (from_guest_phys(addr) == i->desc) { | ||
1121 | update_device_status(i); | ||
1122 | return; | ||
1123 | } | ||
1124 | |||
1125 | /* Devices should not be used before features are finalized. */ | ||
1126 | for (vq = i->vq; vq; vq = vq->next) { | ||
1127 | if (addr != vq->config.pfn*getpagesize()) | ||
1128 | continue; | ||
1129 | errx(1, "Notification on %s before setup!", i->name); | ||
1130 | } | ||
1131 | } | ||
1132 | |||
1133 | /* | ||
1134 | * Early console write is done using notify on a nul-terminated string | ||
1135 | * in Guest memory. It's also great for hacking debugging messages | ||
1136 | * into a Guest. | ||
1137 | */ | ||
1138 | if (addr >= guest_limit) | ||
1139 | errx(1, "Bad NOTIFY %#lx", addr); | ||
1140 | |||
1141 | write(STDOUT_FILENO, from_guest_phys(addr), | ||
1142 | strnlen(from_guest_phys(addr), guest_limit - addr)); | ||
1143 | } | ||
1144 | |||
1145 | /*L:190 | ||
1146 | * Device Setup | ||
1147 | * | ||
1148 | * All devices need a descriptor so the Guest knows it exists, and a "struct | ||
1149 | * device" so the Launcher can keep track of it. We have common helper | ||
1150 | * routines to allocate and manage them. | ||
1151 | */ | ||
1152 | |||
1153 | /* | ||
1154 | * The layout of the device page is a "struct lguest_device_desc" followed by a | ||
1155 | * number of virtqueue descriptors, then two sets of feature bits, then an | ||
1156 | * array of configuration bytes. This routine returns the configuration | ||
1157 | * pointer. | ||
1158 | */ | ||
1159 | static u8 *device_config(const struct device *dev) | ||
1160 | { | ||
1161 | return (void *)(dev->desc + 1) | ||
1162 | + dev->num_vq * sizeof(struct lguest_vqconfig) | ||
1163 | + dev->feature_len * 2; | ||
1164 | } | ||
1165 | |||
1166 | /* | ||
1167 | * This routine allocates a new "struct lguest_device_desc" from descriptor | ||
1168 | * table page just above the Guest's normal memory. It returns a pointer to | ||
1169 | * that descriptor. | ||
1170 | */ | ||
1171 | static struct lguest_device_desc *new_dev_desc(u16 type) | ||
1172 | { | ||
1173 | struct lguest_device_desc d = { .type = type }; | ||
1174 | void *p; | ||
1175 | |||
1176 | /* Figure out where the next device config is, based on the last one. */ | ||
1177 | if (devices.lastdev) | ||
1178 | p = device_config(devices.lastdev) | ||
1179 | + devices.lastdev->desc->config_len; | ||
1180 | else | ||
1181 | p = devices.descpage; | ||
1182 | |||
1183 | /* We only have one page for all the descriptors. */ | ||
1184 | if (p + sizeof(d) > (void *)devices.descpage + getpagesize()) | ||
1185 | errx(1, "Too many devices"); | ||
1186 | |||
1187 | /* p might not be aligned, so we memcpy in. */ | ||
1188 | return memcpy(p, &d, sizeof(d)); | ||
1189 | } | ||
1190 | |||
1191 | /* | ||
1192 | * Each device descriptor is followed by the description of its virtqueues. We | ||
1193 | * specify how many descriptors the virtqueue is to have. | ||
1194 | */ | ||
1195 | static void add_virtqueue(struct device *dev, unsigned int num_descs, | ||
1196 | void (*service)(struct virtqueue *)) | ||
1197 | { | ||
1198 | unsigned int pages; | ||
1199 | struct virtqueue **i, *vq = malloc(sizeof(*vq)); | ||
1200 | void *p; | ||
1201 | |||
1202 | /* First we need some memory for this virtqueue. */ | ||
1203 | pages = (vring_size(num_descs, LGUEST_VRING_ALIGN) + getpagesize() - 1) | ||
1204 | / getpagesize(); | ||
1205 | p = get_pages(pages); | ||
1206 | |||
1207 | /* Initialize the virtqueue */ | ||
1208 | vq->next = NULL; | ||
1209 | vq->last_avail_idx = 0; | ||
1210 | vq->dev = dev; | ||
1211 | |||
1212 | /* | ||
1213 | * This is the routine the service thread will run, and its Process ID | ||
1214 | * once it's running. | ||
1215 | */ | ||
1216 | vq->service = service; | ||
1217 | vq->thread = (pid_t)-1; | ||
1218 | |||
1219 | /* Initialize the configuration. */ | ||
1220 | vq->config.num = num_descs; | ||
1221 | vq->config.irq = devices.next_irq++; | ||
1222 | vq->config.pfn = to_guest_phys(p) / getpagesize(); | ||
1223 | |||
1224 | /* Initialize the vring. */ | ||
1225 | vring_init(&vq->vring, num_descs, p, LGUEST_VRING_ALIGN); | ||
1226 | |||
1227 | /* | ||
1228 | * Append virtqueue to this device's descriptor. We use | ||
1229 | * device_config() to get the end of the device's current virtqueues; | ||
1230 | * we check that we haven't added any config or feature information | ||
1231 | * yet, otherwise we'd be overwriting them. | ||
1232 | */ | ||
1233 | assert(dev->desc->config_len == 0 && dev->desc->feature_len == 0); | ||
1234 | memcpy(device_config(dev), &vq->config, sizeof(vq->config)); | ||
1235 | dev->num_vq++; | ||
1236 | dev->desc->num_vq++; | ||
1237 | |||
1238 | verbose("Virtqueue page %#lx\n", to_guest_phys(p)); | ||
1239 | |||
1240 | /* | ||
1241 | * Add to tail of list, so dev->vq is first vq, dev->vq->next is | ||
1242 | * second. | ||
1243 | */ | ||
1244 | for (i = &dev->vq; *i; i = &(*i)->next); | ||
1245 | *i = vq; | ||
1246 | } | ||
1247 | |||
1248 | /* | ||
1249 | * The first half of the feature bitmask is for us to advertise features. The | ||
1250 | * second half is for the Guest to accept features. | ||
1251 | */ | ||
1252 | static void add_feature(struct device *dev, unsigned bit) | ||
1253 | { | ||
1254 | u8 *features = get_feature_bits(dev); | ||
1255 | |||
1256 | /* We can't extend the feature bits once we've added config bytes */ | ||
1257 | if (dev->desc->feature_len <= bit / CHAR_BIT) { | ||
1258 | assert(dev->desc->config_len == 0); | ||
1259 | dev->feature_len = dev->desc->feature_len = (bit/CHAR_BIT) + 1; | ||
1260 | } | ||
1261 | |||
1262 | features[bit / CHAR_BIT] |= (1 << (bit % CHAR_BIT)); | ||
1263 | } | ||
1264 | |||
1265 | /* | ||
1266 | * This routine sets the configuration fields for an existing device's | ||
1267 | * descriptor. It only works for the last device, but that's OK because that's | ||
1268 | * how we use it. | ||
1269 | */ | ||
1270 | static void set_config(struct device *dev, unsigned len, const void *conf) | ||
1271 | { | ||
1272 | /* Check we haven't overflowed our single page. */ | ||
1273 | if (device_config(dev) + len > devices.descpage + getpagesize()) | ||
1274 | errx(1, "Too many devices"); | ||
1275 | |||
1276 | /* Copy in the config information, and store the length. */ | ||
1277 | memcpy(device_config(dev), conf, len); | ||
1278 | dev->desc->config_len = len; | ||
1279 | |||
1280 | /* Size must fit in config_len field (8 bits)! */ | ||
1281 | assert(dev->desc->config_len == len); | ||
1282 | } | ||
1283 | |||
1284 | /* | ||
1285 | * This routine does all the creation and setup of a new device, including | ||
1286 | * calling new_dev_desc() to allocate the descriptor and device memory. We | ||
1287 | * don't actually start the service threads until later. | ||
1288 | * | ||
1289 | * See what I mean about userspace being boring? | ||
1290 | */ | ||
1291 | static struct device *new_device(const char *name, u16 type) | ||
1292 | { | ||
1293 | struct device *dev = malloc(sizeof(*dev)); | ||
1294 | |||
1295 | /* Now we populate the fields one at a time. */ | ||
1296 | dev->desc = new_dev_desc(type); | ||
1297 | dev->name = name; | ||
1298 | dev->vq = NULL; | ||
1299 | dev->feature_len = 0; | ||
1300 | dev->num_vq = 0; | ||
1301 | dev->running = false; | ||
1302 | |||
1303 | /* | ||
1304 | * Append to device list. Prepending to a single-linked list is | ||
1305 | * easier, but the user expects the devices to be arranged on the bus | ||
1306 | * in command-line order. The first network device on the command line | ||
1307 | * is eth0, the first block device /dev/vda, etc. | ||
1308 | */ | ||
1309 | if (devices.lastdev) | ||
1310 | devices.lastdev->next = dev; | ||
1311 | else | ||
1312 | devices.dev = dev; | ||
1313 | devices.lastdev = dev; | ||
1314 | |||
1315 | return dev; | ||
1316 | } | ||
1317 | |||
1318 | /* | ||
1319 | * Our first setup routine is the console. It's a fairly simple device, but | ||
1320 | * UNIX tty handling makes it uglier than it could be. | ||
1321 | */ | ||
1322 | static void setup_console(void) | ||
1323 | { | ||
1324 | struct device *dev; | ||
1325 | |||
1326 | /* If we can save the initial standard input settings... */ | ||
1327 | if (tcgetattr(STDIN_FILENO, &orig_term) == 0) { | ||
1328 | struct termios term = orig_term; | ||
1329 | /* | ||
1330 | * Then we turn off echo, line buffering and ^C etc: We want a | ||
1331 | * raw input stream to the Guest. | ||
1332 | */ | ||
1333 | term.c_lflag &= ~(ISIG|ICANON|ECHO); | ||
1334 | tcsetattr(STDIN_FILENO, TCSANOW, &term); | ||
1335 | } | ||
1336 | |||
1337 | dev = new_device("console", VIRTIO_ID_CONSOLE); | ||
1338 | |||
1339 | /* We store the console state in dev->priv, and initialize it. */ | ||
1340 | dev->priv = malloc(sizeof(struct console_abort)); | ||
1341 | ((struct console_abort *)dev->priv)->count = 0; | ||
1342 | |||
1343 | /* | ||
1344 | * The console needs two virtqueues: the input then the output. When | ||
1345 | * they put something the input queue, we make sure we're listening to | ||
1346 | * stdin. When they put something in the output queue, we write it to | ||
1347 | * stdout. | ||
1348 | */ | ||
1349 | add_virtqueue(dev, VIRTQUEUE_NUM, console_input); | ||
1350 | add_virtqueue(dev, VIRTQUEUE_NUM, console_output); | ||
1351 | |||
1352 | verbose("device %u: console\n", ++devices.device_num); | ||
1353 | } | ||
1354 | /*:*/ | ||
1355 | |||
1356 | /*M:010 | ||
1357 | * Inter-guest networking is an interesting area. Simplest is to have a | ||
1358 | * --sharenet=<name> option which opens or creates a named pipe. This can be | ||
1359 | * used to send packets to another guest in a 1:1 manner. | ||
1360 | * | ||
1361 | * More sophisticated is to use one of the tools developed for project like UML | ||
1362 | * to do networking. | ||
1363 | * | ||
1364 | * Faster is to do virtio bonding in kernel. Doing this 1:1 would be | ||
1365 | * completely generic ("here's my vring, attach to your vring") and would work | ||
1366 | * for any traffic. Of course, namespace and permissions issues need to be | ||
1367 | * dealt with. A more sophisticated "multi-channel" virtio_net.c could hide | ||
1368 | * multiple inter-guest channels behind one interface, although it would | ||
1369 | * require some manner of hotplugging new virtio channels. | ||
1370 | * | ||
1371 | * Finally, we could use a virtio network switch in the kernel, ie. vhost. | ||
1372 | :*/ | ||
1373 | |||
1374 | static u32 str2ip(const char *ipaddr) | ||
1375 | { | ||
1376 | unsigned int b[4]; | ||
1377 | |||
1378 | if (sscanf(ipaddr, "%u.%u.%u.%u", &b[0], &b[1], &b[2], &b[3]) != 4) | ||
1379 | errx(1, "Failed to parse IP address '%s'", ipaddr); | ||
1380 | return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]; | ||
1381 | } | ||
1382 | |||
1383 | static void str2mac(const char *macaddr, unsigned char mac[6]) | ||
1384 | { | ||
1385 | unsigned int m[6]; | ||
1386 | if (sscanf(macaddr, "%02x:%02x:%02x:%02x:%02x:%02x", | ||
1387 | &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) != 6) | ||
1388 | errx(1, "Failed to parse mac address '%s'", macaddr); | ||
1389 | mac[0] = m[0]; | ||
1390 | mac[1] = m[1]; | ||
1391 | mac[2] = m[2]; | ||
1392 | mac[3] = m[3]; | ||
1393 | mac[4] = m[4]; | ||
1394 | mac[5] = m[5]; | ||
1395 | } | ||
1396 | |||
1397 | /* | ||
1398 | * This code is "adapted" from libbridge: it attaches the Host end of the | ||
1399 | * network device to the bridge device specified by the command line. | ||
1400 | * | ||
1401 | * This is yet another James Morris contribution (I'm an IP-level guy, so I | ||
1402 | * dislike bridging), and I just try not to break it. | ||
1403 | */ | ||
1404 | static void add_to_bridge(int fd, const char *if_name, const char *br_name) | ||
1405 | { | ||
1406 | int ifidx; | ||
1407 | struct ifreq ifr; | ||
1408 | |||
1409 | if (!*br_name) | ||
1410 | errx(1, "must specify bridge name"); | ||
1411 | |||
1412 | ifidx = if_nametoindex(if_name); | ||
1413 | if (!ifidx) | ||
1414 | errx(1, "interface %s does not exist!", if_name); | ||
1415 | |||
1416 | strncpy(ifr.ifr_name, br_name, IFNAMSIZ); | ||
1417 | ifr.ifr_name[IFNAMSIZ-1] = '\0'; | ||
1418 | ifr.ifr_ifindex = ifidx; | ||
1419 | if (ioctl(fd, SIOCBRADDIF, &ifr) < 0) | ||
1420 | err(1, "can't add %s to bridge %s", if_name, br_name); | ||
1421 | } | ||
1422 | |||
1423 | /* | ||
1424 | * This sets up the Host end of the network device with an IP address, brings | ||
1425 | * it up so packets will flow, the copies the MAC address into the hwaddr | ||
1426 | * pointer. | ||
1427 | */ | ||
1428 | static void configure_device(int fd, const char *tapif, u32 ipaddr) | ||
1429 | { | ||
1430 | struct ifreq ifr; | ||
1431 | struct sockaddr_in sin; | ||
1432 | |||
1433 | memset(&ifr, 0, sizeof(ifr)); | ||
1434 | strcpy(ifr.ifr_name, tapif); | ||
1435 | |||
1436 | /* Don't read these incantations. Just cut & paste them like I did! */ | ||
1437 | sin.sin_family = AF_INET; | ||
1438 | sin.sin_addr.s_addr = htonl(ipaddr); | ||
1439 | memcpy(&ifr.ifr_addr, &sin, sizeof(sin)); | ||
1440 | if (ioctl(fd, SIOCSIFADDR, &ifr) != 0) | ||
1441 | err(1, "Setting %s interface address", tapif); | ||
1442 | ifr.ifr_flags = IFF_UP; | ||
1443 | if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) | ||
1444 | err(1, "Bringing interface %s up", tapif); | ||
1445 | } | ||
1446 | |||
1447 | static int get_tun_device(char tapif[IFNAMSIZ]) | ||
1448 | { | ||
1449 | struct ifreq ifr; | ||
1450 | int netfd; | ||
1451 | |||
1452 | /* Start with this zeroed. Messy but sure. */ | ||
1453 | memset(&ifr, 0, sizeof(ifr)); | ||
1454 | |||
1455 | /* | ||
1456 | * We open the /dev/net/tun device and tell it we want a tap device. A | ||
1457 | * tap device is like a tun device, only somehow different. To tell | ||
1458 | * the truth, I completely blundered my way through this code, but it | ||
1459 | * works now! | ||
1460 | */ | ||
1461 | netfd = open_or_die("/dev/net/tun", O_RDWR); | ||
1462 | ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR; | ||
1463 | strcpy(ifr.ifr_name, "tap%d"); | ||
1464 | if (ioctl(netfd, TUNSETIFF, &ifr) != 0) | ||
1465 | err(1, "configuring /dev/net/tun"); | ||
1466 | |||
1467 | if (ioctl(netfd, TUNSETOFFLOAD, | ||
1468 | TUN_F_CSUM|TUN_F_TSO4|TUN_F_TSO6|TUN_F_TSO_ECN) != 0) | ||
1469 | err(1, "Could not set features for tun device"); | ||
1470 | |||
1471 | /* | ||
1472 | * We don't need checksums calculated for packets coming in this | ||
1473 | * device: trust us! | ||
1474 | */ | ||
1475 | ioctl(netfd, TUNSETNOCSUM, 1); | ||
1476 | |||
1477 | memcpy(tapif, ifr.ifr_name, IFNAMSIZ); | ||
1478 | return netfd; | ||
1479 | } | ||
1480 | |||
1481 | /*L:195 | ||
1482 | * Our network is a Host<->Guest network. This can either use bridging or | ||
1483 | * routing, but the principle is the same: it uses the "tun" device to inject | ||
1484 | * packets into the Host as if they came in from a normal network card. We | ||
1485 | * just shunt packets between the Guest and the tun device. | ||
1486 | */ | ||
1487 | static void setup_tun_net(char *arg) | ||
1488 | { | ||
1489 | struct device *dev; | ||
1490 | struct net_info *net_info = malloc(sizeof(*net_info)); | ||
1491 | int ipfd; | ||
1492 | u32 ip = INADDR_ANY; | ||
1493 | bool bridging = false; | ||
1494 | char tapif[IFNAMSIZ], *p; | ||
1495 | struct virtio_net_config conf; | ||
1496 | |||
1497 | net_info->tunfd = get_tun_device(tapif); | ||
1498 | |||
1499 | /* First we create a new network device. */ | ||
1500 | dev = new_device("net", VIRTIO_ID_NET); | ||
1501 | dev->priv = net_info; | ||
1502 | |||
1503 | /* Network devices need a recv and a send queue, just like console. */ | ||
1504 | add_virtqueue(dev, VIRTQUEUE_NUM, net_input); | ||
1505 | add_virtqueue(dev, VIRTQUEUE_NUM, net_output); | ||
1506 | |||
1507 | /* | ||
1508 | * We need a socket to perform the magic network ioctls to bring up the | ||
1509 | * tap interface, connect to the bridge etc. Any socket will do! | ||
1510 | */ | ||
1511 | ipfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); | ||
1512 | if (ipfd < 0) | ||
1513 | err(1, "opening IP socket"); | ||
1514 | |||
1515 | /* If the command line was --tunnet=bridge:<name> do bridging. */ | ||
1516 | if (!strncmp(BRIDGE_PFX, arg, strlen(BRIDGE_PFX))) { | ||
1517 | arg += strlen(BRIDGE_PFX); | ||
1518 | bridging = true; | ||
1519 | } | ||
1520 | |||
1521 | /* A mac address may follow the bridge name or IP address */ | ||
1522 | p = strchr(arg, ':'); | ||
1523 | if (p) { | ||
1524 | str2mac(p+1, conf.mac); | ||
1525 | add_feature(dev, VIRTIO_NET_F_MAC); | ||
1526 | *p = '\0'; | ||
1527 | } | ||
1528 | |||
1529 | /* arg is now either an IP address or a bridge name */ | ||
1530 | if (bridging) | ||
1531 | add_to_bridge(ipfd, tapif, arg); | ||
1532 | else | ||
1533 | ip = str2ip(arg); | ||
1534 | |||
1535 | /* Set up the tun device. */ | ||
1536 | configure_device(ipfd, tapif, ip); | ||
1537 | |||
1538 | /* Expect Guest to handle everything except UFO */ | ||
1539 | add_feature(dev, VIRTIO_NET_F_CSUM); | ||
1540 | add_feature(dev, VIRTIO_NET_F_GUEST_CSUM); | ||
1541 | add_feature(dev, VIRTIO_NET_F_GUEST_TSO4); | ||
1542 | add_feature(dev, VIRTIO_NET_F_GUEST_TSO6); | ||
1543 | add_feature(dev, VIRTIO_NET_F_GUEST_ECN); | ||
1544 | add_feature(dev, VIRTIO_NET_F_HOST_TSO4); | ||
1545 | add_feature(dev, VIRTIO_NET_F_HOST_TSO6); | ||
1546 | add_feature(dev, VIRTIO_NET_F_HOST_ECN); | ||
1547 | /* We handle indirect ring entries */ | ||
1548 | add_feature(dev, VIRTIO_RING_F_INDIRECT_DESC); | ||
1549 | set_config(dev, sizeof(conf), &conf); | ||
1550 | |||
1551 | /* We don't need the socket any more; setup is done. */ | ||
1552 | close(ipfd); | ||
1553 | |||
1554 | devices.device_num++; | ||
1555 | |||
1556 | if (bridging) | ||
1557 | verbose("device %u: tun %s attached to bridge: %s\n", | ||
1558 | devices.device_num, tapif, arg); | ||
1559 | else | ||
1560 | verbose("device %u: tun %s: %s\n", | ||
1561 | devices.device_num, tapif, arg); | ||
1562 | } | ||
1563 | /*:*/ | ||
1564 | |||
1565 | /* This hangs off device->priv. */ | ||
1566 | struct vblk_info { | ||
1567 | /* The size of the file. */ | ||
1568 | off64_t len; | ||
1569 | |||
1570 | /* The file descriptor for the file. */ | ||
1571 | int fd; | ||
1572 | |||
1573 | }; | ||
1574 | |||
1575 | /*L:210 | ||
1576 | * The Disk | ||
1577 | * | ||
1578 | * The disk only has one virtqueue, so it only has one thread. It is really | ||
1579 | * simple: the Guest asks for a block number and we read or write that position | ||
1580 | * in the file. | ||
1581 | * | ||
1582 | * Before we serviced each virtqueue in a separate thread, that was unacceptably | ||
1583 | * slow: the Guest waits until the read is finished before running anything | ||
1584 | * else, even if it could have been doing useful work. | ||
1585 | * | ||
1586 | * We could have used async I/O, except it's reputed to suck so hard that | ||
1587 | * characters actually go missing from your code when you try to use it. | ||
1588 | */ | ||
1589 | static void blk_request(struct virtqueue *vq) | ||
1590 | { | ||
1591 | struct vblk_info *vblk = vq->dev->priv; | ||
1592 | unsigned int head, out_num, in_num, wlen; | ||
1593 | int ret; | ||
1594 | u8 *in; | ||
1595 | struct virtio_blk_outhdr *out; | ||
1596 | struct iovec iov[vq->vring.num]; | ||
1597 | off64_t off; | ||
1598 | |||
1599 | /* | ||
1600 | * Get the next request, where we normally wait. It triggers the | ||
1601 | * interrupt to acknowledge previously serviced requests (if any). | ||
1602 | */ | ||
1603 | head = wait_for_vq_desc(vq, iov, &out_num, &in_num); | ||
1604 | |||
1605 | /* | ||
1606 | * Every block request should contain at least one output buffer | ||
1607 | * (detailing the location on disk and the type of request) and one | ||
1608 | * input buffer (to hold the result). | ||
1609 | */ | ||
1610 | if (out_num == 0 || in_num == 0) | ||
1611 | errx(1, "Bad virtblk cmd %u out=%u in=%u", | ||
1612 | head, out_num, in_num); | ||
1613 | |||
1614 | out = convert(&iov[0], struct virtio_blk_outhdr); | ||
1615 | in = convert(&iov[out_num+in_num-1], u8); | ||
1616 | /* | ||
1617 | * For historical reasons, block operations are expressed in 512 byte | ||
1618 | * "sectors". | ||
1619 | */ | ||
1620 | off = out->sector * 512; | ||
1621 | |||
1622 | /* | ||
1623 | * In general the virtio block driver is allowed to try SCSI commands. | ||
1624 | * It'd be nice if we supported eject, for example, but we don't. | ||
1625 | */ | ||
1626 | if (out->type & VIRTIO_BLK_T_SCSI_CMD) { | ||
1627 | fprintf(stderr, "Scsi commands unsupported\n"); | ||
1628 | *in = VIRTIO_BLK_S_UNSUPP; | ||
1629 | wlen = sizeof(*in); | ||
1630 | } else if (out->type & VIRTIO_BLK_T_OUT) { | ||
1631 | /* | ||
1632 | * Write | ||
1633 | * | ||
1634 | * Move to the right location in the block file. This can fail | ||
1635 | * if they try to write past end. | ||
1636 | */ | ||
1637 | if (lseek64(vblk->fd, off, SEEK_SET) != off) | ||
1638 | err(1, "Bad seek to sector %llu", out->sector); | ||
1639 | |||
1640 | ret = writev(vblk->fd, iov+1, out_num-1); | ||
1641 | verbose("WRITE to sector %llu: %i\n", out->sector, ret); | ||
1642 | |||
1643 | /* | ||
1644 | * Grr... Now we know how long the descriptor they sent was, we | ||
1645 | * make sure they didn't try to write over the end of the block | ||
1646 | * file (possibly extending it). | ||
1647 | */ | ||
1648 | if (ret > 0 && off + ret > vblk->len) { | ||
1649 | /* Trim it back to the correct length */ | ||
1650 | ftruncate64(vblk->fd, vblk->len); | ||
1651 | /* Die, bad Guest, die. */ | ||
1652 | errx(1, "Write past end %llu+%u", off, ret); | ||
1653 | } | ||
1654 | |||
1655 | wlen = sizeof(*in); | ||
1656 | *in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR); | ||
1657 | } else if (out->type & VIRTIO_BLK_T_FLUSH) { | ||
1658 | /* Flush */ | ||
1659 | ret = fdatasync(vblk->fd); | ||
1660 | verbose("FLUSH fdatasync: %i\n", ret); | ||
1661 | wlen = sizeof(*in); | ||
1662 | *in = (ret >= 0 ? VIRTIO_BLK_S_OK : VIRTIO_BLK_S_IOERR); | ||
1663 | } else { | ||
1664 | /* | ||
1665 | * Read | ||
1666 | * | ||
1667 | * Move to the right location in the block file. This can fail | ||
1668 | * if they try to read past end. | ||
1669 | */ | ||
1670 | if (lseek64(vblk->fd, off, SEEK_SET) != off) | ||
1671 | err(1, "Bad seek to sector %llu", out->sector); | ||
1672 | |||
1673 | ret = readv(vblk->fd, iov+1, in_num-1); | ||
1674 | verbose("READ from sector %llu: %i\n", out->sector, ret); | ||
1675 | if (ret >= 0) { | ||
1676 | wlen = sizeof(*in) + ret; | ||
1677 | *in = VIRTIO_BLK_S_OK; | ||
1678 | } else { | ||
1679 | wlen = sizeof(*in); | ||
1680 | *in = VIRTIO_BLK_S_IOERR; | ||
1681 | } | ||
1682 | } | ||
1683 | |||
1684 | /* Finished that request. */ | ||
1685 | add_used(vq, head, wlen); | ||
1686 | } | ||
1687 | |||
1688 | /*L:198 This actually sets up a virtual block device. */ | ||
1689 | static void setup_block_file(const char *filename) | ||
1690 | { | ||
1691 | struct device *dev; | ||
1692 | struct vblk_info *vblk; | ||
1693 | struct virtio_blk_config conf; | ||
1694 | |||
1695 | /* Creat the device. */ | ||
1696 | dev = new_device("block", VIRTIO_ID_BLOCK); | ||
1697 | |||
1698 | /* The device has one virtqueue, where the Guest places requests. */ | ||
1699 | add_virtqueue(dev, VIRTQUEUE_NUM, blk_request); | ||
1700 | |||
1701 | /* Allocate the room for our own bookkeeping */ | ||
1702 | vblk = dev->priv = malloc(sizeof(*vblk)); | ||
1703 | |||
1704 | /* First we open the file and store the length. */ | ||
1705 | vblk->fd = open_or_die(filename, O_RDWR|O_LARGEFILE); | ||
1706 | vblk->len = lseek64(vblk->fd, 0, SEEK_END); | ||
1707 | |||
1708 | /* We support FLUSH. */ | ||
1709 | add_feature(dev, VIRTIO_BLK_F_FLUSH); | ||
1710 | |||
1711 | /* Tell Guest how many sectors this device has. */ | ||
1712 | conf.capacity = cpu_to_le64(vblk->len / 512); | ||
1713 | |||
1714 | /* | ||
1715 | * Tell Guest not to put in too many descriptors at once: two are used | ||
1716 | * for the in and out elements. | ||
1717 | */ | ||
1718 | add_feature(dev, VIRTIO_BLK_F_SEG_MAX); | ||
1719 | conf.seg_max = cpu_to_le32(VIRTQUEUE_NUM - 2); | ||
1720 | |||
1721 | /* Don't try to put whole struct: we have 8 bit limit. */ | ||
1722 | set_config(dev, offsetof(struct virtio_blk_config, geometry), &conf); | ||
1723 | |||
1724 | verbose("device %u: virtblock %llu sectors\n", | ||
1725 | ++devices.device_num, le64_to_cpu(conf.capacity)); | ||
1726 | } | ||
1727 | |||
1728 | /*L:211 | ||
1729 | * Our random number generator device reads from /dev/random into the Guest's | ||
1730 | * input buffers. The usual case is that the Guest doesn't want random numbers | ||
1731 | * and so has no buffers although /dev/random is still readable, whereas | ||
1732 | * console is the reverse. | ||
1733 | * | ||
1734 | * The same logic applies, however. | ||
1735 | */ | ||
1736 | struct rng_info { | ||
1737 | int rfd; | ||
1738 | }; | ||
1739 | |||
1740 | static void rng_input(struct virtqueue *vq) | ||
1741 | { | ||
1742 | int len; | ||
1743 | unsigned int head, in_num, out_num, totlen = 0; | ||
1744 | struct rng_info *rng_info = vq->dev->priv; | ||
1745 | struct iovec iov[vq->vring.num]; | ||
1746 | |||
1747 | /* First we need a buffer from the Guests's virtqueue. */ | ||
1748 | head = wait_for_vq_desc(vq, iov, &out_num, &in_num); | ||
1749 | if (out_num) | ||
1750 | errx(1, "Output buffers in rng?"); | ||
1751 | |||
1752 | /* | ||
1753 | * Just like the console write, we loop to cover the whole iovec. | ||
1754 | * In this case, short reads actually happen quite a bit. | ||
1755 | */ | ||
1756 | while (!iov_empty(iov, in_num)) { | ||
1757 | len = readv(rng_info->rfd, iov, in_num); | ||
1758 | if (len <= 0) | ||
1759 | err(1, "Read from /dev/random gave %i", len); | ||
1760 | iov_consume(iov, in_num, len); | ||
1761 | totlen += len; | ||
1762 | } | ||
1763 | |||
1764 | /* Tell the Guest about the new input. */ | ||
1765 | add_used(vq, head, totlen); | ||
1766 | } | ||
1767 | |||
1768 | /*L:199 | ||
1769 | * This creates a "hardware" random number device for the Guest. | ||
1770 | */ | ||
1771 | static void setup_rng(void) | ||
1772 | { | ||
1773 | struct device *dev; | ||
1774 | struct rng_info *rng_info = malloc(sizeof(*rng_info)); | ||
1775 | |||
1776 | /* Our device's privat info simply contains the /dev/random fd. */ | ||
1777 | rng_info->rfd = open_or_die("/dev/random", O_RDONLY); | ||
1778 | |||
1779 | /* Create the new device. */ | ||
1780 | dev = new_device("rng", VIRTIO_ID_RNG); | ||
1781 | dev->priv = rng_info; | ||
1782 | |||
1783 | /* The device has one virtqueue, where the Guest places inbufs. */ | ||
1784 | add_virtqueue(dev, VIRTQUEUE_NUM, rng_input); | ||
1785 | |||
1786 | verbose("device %u: rng\n", devices.device_num++); | ||
1787 | } | ||
1788 | /* That's the end of device setup. */ | ||
1789 | |||
1790 | /*L:230 Reboot is pretty easy: clean up and exec() the Launcher afresh. */ | ||
1791 | static void __attribute__((noreturn)) restart_guest(void) | ||
1792 | { | ||
1793 | unsigned int i; | ||
1794 | |||
1795 | /* | ||
1796 | * Since we don't track all open fds, we simply close everything beyond | ||
1797 | * stderr. | ||
1798 | */ | ||
1799 | for (i = 3; i < FD_SETSIZE; i++) | ||
1800 | close(i); | ||
1801 | |||
1802 | /* Reset all the devices (kills all threads). */ | ||
1803 | cleanup_devices(); | ||
1804 | |||
1805 | execv(main_args[0], main_args); | ||
1806 | err(1, "Could not exec %s", main_args[0]); | ||
1807 | } | ||
1808 | |||
1809 | /*L:220 | ||
1810 | * Finally we reach the core of the Launcher which runs the Guest, serves | ||
1811 | * its input and output, and finally, lays it to rest. | ||
1812 | */ | ||
1813 | static void __attribute__((noreturn)) run_guest(void) | ||
1814 | { | ||
1815 | for (;;) { | ||
1816 | unsigned long notify_addr; | ||
1817 | int readval; | ||
1818 | |||
1819 | /* We read from the /dev/lguest device to run the Guest. */ | ||
1820 | readval = pread(lguest_fd, ¬ify_addr, | ||
1821 | sizeof(notify_addr), cpu_id); | ||
1822 | |||
1823 | /* One unsigned long means the Guest did HCALL_NOTIFY */ | ||
1824 | if (readval == sizeof(notify_addr)) { | ||
1825 | verbose("Notify on address %#lx\n", notify_addr); | ||
1826 | handle_output(notify_addr); | ||
1827 | /* ENOENT means the Guest died. Reading tells us why. */ | ||
1828 | } else if (errno == ENOENT) { | ||
1829 | char reason[1024] = { 0 }; | ||
1830 | pread(lguest_fd, reason, sizeof(reason)-1, cpu_id); | ||
1831 | errx(1, "%s", reason); | ||
1832 | /* ERESTART means that we need to reboot the guest */ | ||
1833 | } else if (errno == ERESTART) { | ||
1834 | restart_guest(); | ||
1835 | /* Anything else means a bug or incompatible change. */ | ||
1836 | } else | ||
1837 | err(1, "Running guest failed"); | ||
1838 | } | ||
1839 | } | ||
1840 | /*L:240 | ||
1841 | * This is the end of the Launcher. The good news: we are over halfway | ||
1842 | * through! The bad news: the most fiendish part of the code still lies ahead | ||
1843 | * of us. | ||
1844 | * | ||
1845 | * Are you ready? Take a deep breath and join me in the core of the Host, in | ||
1846 | * "make Host". | ||
1847 | :*/ | ||
1848 | |||
1849 | static struct option opts[] = { | ||
1850 | { "verbose", 0, NULL, 'v' }, | ||
1851 | { "tunnet", 1, NULL, 't' }, | ||
1852 | { "block", 1, NULL, 'b' }, | ||
1853 | { "rng", 0, NULL, 'r' }, | ||
1854 | { "initrd", 1, NULL, 'i' }, | ||
1855 | { "username", 1, NULL, 'u' }, | ||
1856 | { "chroot", 1, NULL, 'c' }, | ||
1857 | { NULL }, | ||
1858 | }; | ||
1859 | static void usage(void) | ||
1860 | { | ||
1861 | errx(1, "Usage: lguest [--verbose] " | ||
1862 | "[--tunnet=(<ipaddr>:<macaddr>|bridge:<bridgename>:<macaddr>)\n" | ||
1863 | "|--block=<filename>|--initrd=<filename>]...\n" | ||
1864 | "<mem-in-mb> vmlinux [args...]"); | ||
1865 | } | ||
1866 | |||
1867 | /*L:105 The main routine is where the real work begins: */ | ||
1868 | int main(int argc, char *argv[]) | ||
1869 | { | ||
1870 | /* Memory, code startpoint and size of the (optional) initrd. */ | ||
1871 | unsigned long mem = 0, start, initrd_size = 0; | ||
1872 | /* Two temporaries. */ | ||
1873 | int i, c; | ||
1874 | /* The boot information for the Guest. */ | ||
1875 | struct boot_params *boot; | ||
1876 | /* If they specify an initrd file to load. */ | ||
1877 | const char *initrd_name = NULL; | ||
1878 | |||
1879 | /* Password structure for initgroups/setres[gu]id */ | ||
1880 | struct passwd *user_details = NULL; | ||
1881 | |||
1882 | /* Directory to chroot to */ | ||
1883 | char *chroot_path = NULL; | ||
1884 | |||
1885 | /* Save the args: we "reboot" by execing ourselves again. */ | ||
1886 | main_args = argv; | ||
1887 | |||
1888 | /* | ||
1889 | * First we initialize the device list. We keep a pointer to the last | ||
1890 | * device, and the next interrupt number to use for devices (1: | ||
1891 | * remember that 0 is used by the timer). | ||
1892 | */ | ||
1893 | devices.lastdev = NULL; | ||
1894 | devices.next_irq = 1; | ||
1895 | |||
1896 | /* We're CPU 0. In fact, that's the only CPU possible right now. */ | ||
1897 | cpu_id = 0; | ||
1898 | |||
1899 | /* | ||
1900 | * We need to know how much memory so we can set up the device | ||
1901 | * descriptor and memory pages for the devices as we parse the command | ||
1902 | * line. So we quickly look through the arguments to find the amount | ||
1903 | * of memory now. | ||
1904 | */ | ||
1905 | for (i = 1; i < argc; i++) { | ||
1906 | if (argv[i][0] != '-') { | ||
1907 | mem = atoi(argv[i]) * 1024 * 1024; | ||
1908 | /* | ||
1909 | * We start by mapping anonymous pages over all of | ||
1910 | * guest-physical memory range. This fills it with 0, | ||
1911 | * and ensures that the Guest won't be killed when it | ||
1912 | * tries to access it. | ||
1913 | */ | ||
1914 | guest_base = map_zeroed_pages(mem / getpagesize() | ||
1915 | + DEVICE_PAGES); | ||
1916 | guest_limit = mem; | ||
1917 | guest_max = mem + DEVICE_PAGES*getpagesize(); | ||
1918 | devices.descpage = get_pages(1); | ||
1919 | break; | ||
1920 | } | ||
1921 | } | ||
1922 | |||
1923 | /* The options are fairly straight-forward */ | ||
1924 | while ((c = getopt_long(argc, argv, "v", opts, NULL)) != EOF) { | ||
1925 | switch (c) { | ||
1926 | case 'v': | ||
1927 | verbose = true; | ||
1928 | break; | ||
1929 | case 't': | ||
1930 | setup_tun_net(optarg); | ||
1931 | break; | ||
1932 | case 'b': | ||
1933 | setup_block_file(optarg); | ||
1934 | break; | ||
1935 | case 'r': | ||
1936 | setup_rng(); | ||
1937 | break; | ||
1938 | case 'i': | ||
1939 | initrd_name = optarg; | ||
1940 | break; | ||
1941 | case 'u': | ||
1942 | user_details = getpwnam(optarg); | ||
1943 | if (!user_details) | ||
1944 | err(1, "getpwnam failed, incorrect username?"); | ||
1945 | break; | ||
1946 | case 'c': | ||
1947 | chroot_path = optarg; | ||
1948 | break; | ||
1949 | default: | ||
1950 | warnx("Unknown argument %s", argv[optind]); | ||
1951 | usage(); | ||
1952 | } | ||
1953 | } | ||
1954 | /* | ||
1955 | * After the other arguments we expect memory and kernel image name, | ||
1956 | * followed by command line arguments for the kernel. | ||
1957 | */ | ||
1958 | if (optind + 2 > argc) | ||
1959 | usage(); | ||
1960 | |||
1961 | verbose("Guest base is at %p\n", guest_base); | ||
1962 | |||
1963 | /* We always have a console device */ | ||
1964 | setup_console(); | ||
1965 | |||
1966 | /* Now we load the kernel */ | ||
1967 | start = load_kernel(open_or_die(argv[optind+1], O_RDONLY)); | ||
1968 | |||
1969 | /* Boot information is stashed at physical address 0 */ | ||
1970 | boot = from_guest_phys(0); | ||
1971 | |||
1972 | /* Map the initrd image if requested (at top of physical memory) */ | ||
1973 | if (initrd_name) { | ||
1974 | initrd_size = load_initrd(initrd_name, mem); | ||
1975 | /* | ||
1976 | * These are the location in the Linux boot header where the | ||
1977 | * start and size of the initrd are expected to be found. | ||
1978 | */ | ||
1979 | boot->hdr.ramdisk_image = mem - initrd_size; | ||
1980 | boot->hdr.ramdisk_size = initrd_size; | ||
1981 | /* The bootloader type 0xFF means "unknown"; that's OK. */ | ||
1982 | boot->hdr.type_of_loader = 0xFF; | ||
1983 | } | ||
1984 | |||
1985 | /* | ||
1986 | * The Linux boot header contains an "E820" memory map: ours is a | ||
1987 | * simple, single region. | ||
1988 | */ | ||
1989 | boot->e820_entries = 1; | ||
1990 | boot->e820_map[0] = ((struct e820entry) { 0, mem, E820_RAM }); | ||
1991 | /* | ||
1992 | * The boot header contains a command line pointer: we put the command | ||
1993 | * line after the boot header. | ||
1994 | */ | ||
1995 | boot->hdr.cmd_line_ptr = to_guest_phys(boot + 1); | ||
1996 | /* We use a simple helper to copy the arguments separated by spaces. */ | ||
1997 | concat((char *)(boot + 1), argv+optind+2); | ||
1998 | |||
1999 | /* Set kernel alignment to 16M (CONFIG_PHYSICAL_ALIGN) */ | ||
2000 | boot->hdr.kernel_alignment = 0x1000000; | ||
2001 | |||
2002 | /* Boot protocol version: 2.07 supports the fields for lguest. */ | ||
2003 | boot->hdr.version = 0x207; | ||
2004 | |||
2005 | /* The hardware_subarch value of "1" tells the Guest it's an lguest. */ | ||
2006 | boot->hdr.hardware_subarch = 1; | ||
2007 | |||
2008 | /* Tell the entry path not to try to reload segment registers. */ | ||
2009 | boot->hdr.loadflags |= KEEP_SEGMENTS; | ||
2010 | |||
2011 | /* We tell the kernel to initialize the Guest. */ | ||
2012 | tell_kernel(start); | ||
2013 | |||
2014 | /* Ensure that we terminate if a device-servicing child dies. */ | ||
2015 | signal(SIGCHLD, kill_launcher); | ||
2016 | |||
2017 | /* If we exit via err(), this kills all the threads, restores tty. */ | ||
2018 | atexit(cleanup_devices); | ||
2019 | |||
2020 | /* If requested, chroot to a directory */ | ||
2021 | if (chroot_path) { | ||
2022 | if (chroot(chroot_path) != 0) | ||
2023 | err(1, "chroot(\"%s\") failed", chroot_path); | ||
2024 | |||
2025 | if (chdir("/") != 0) | ||
2026 | err(1, "chdir(\"/\") failed"); | ||
2027 | |||
2028 | verbose("chroot done\n"); | ||
2029 | } | ||
2030 | |||
2031 | /* If requested, drop privileges */ | ||
2032 | if (user_details) { | ||
2033 | uid_t u; | ||
2034 | gid_t g; | ||
2035 | |||
2036 | u = user_details->pw_uid; | ||
2037 | g = user_details->pw_gid; | ||
2038 | |||
2039 | if (initgroups(user_details->pw_name, g) != 0) | ||
2040 | err(1, "initgroups failed"); | ||
2041 | |||
2042 | if (setresgid(g, g, g) != 0) | ||
2043 | err(1, "setresgid failed"); | ||
2044 | |||
2045 | if (setresuid(u, u, u) != 0) | ||
2046 | err(1, "setresuid failed"); | ||
2047 | |||
2048 | verbose("Dropping privileges completed\n"); | ||
2049 | } | ||
2050 | |||
2051 | /* Finally, run the Guest. This doesn't return. */ | ||
2052 | run_guest(); | ||
2053 | } | ||
2054 | /*:*/ | ||
2055 | |||
2056 | /*M:999 | ||
2057 | * Mastery is done: you now know everything I do. | ||
2058 | * | ||
2059 | * But surely you have seen code, features and bugs in your wanderings which | ||
2060 | * you now yearn to attack? That is the real game, and I look forward to you | ||
2061 | * patching and forking lguest into the Your-Name-Here-visor. | ||
2062 | * | ||
2063 | * Farewell, and good coding! | ||
2064 | * Rusty Russell. | ||
2065 | */ | ||
diff --git a/tools/lguest/lguest.txt b/tools/lguest/lguest.txt new file mode 100644 index 000000000000..bff0c554485d --- /dev/null +++ b/tools/lguest/lguest.txt | |||
@@ -0,0 +1,129 @@ | |||
1 | __ | ||
2 | (___()'`; Rusty's Remarkably Unreliable Guide to Lguest | ||
3 | /, /` - or, A Young Coder's Illustrated Hypervisor | ||
4 | \\"--\\ http://lguest.ozlabs.org | ||
5 | |||
6 | Lguest is designed to be a minimal 32-bit x86 hypervisor for the Linux kernel, | ||
7 | for Linux developers and users to experiment with virtualization with the | ||
8 | minimum of complexity. Nonetheless, it should have sufficient features to | ||
9 | make it useful for specific tasks, and, of course, you are encouraged to fork | ||
10 | and enhance it (see drivers/lguest/README). | ||
11 | |||
12 | Features: | ||
13 | |||
14 | - Kernel module which runs in a normal kernel. | ||
15 | - Simple I/O model for communication. | ||
16 | - Simple program to create new guests. | ||
17 | - Logo contains cute puppies: http://lguest.ozlabs.org | ||
18 | |||
19 | Developer features: | ||
20 | |||
21 | - Fun to hack on. | ||
22 | - No ABI: being tied to a specific kernel anyway, you can change anything. | ||
23 | - Many opportunities for improvement or feature implementation. | ||
24 | |||
25 | Running Lguest: | ||
26 | |||
27 | - The easiest way to run lguest is to use same kernel as guest and host. | ||
28 | You can configure them differently, but usually it's easiest not to. | ||
29 | |||
30 | You will need to configure your kernel with the following options: | ||
31 | |||
32 | "General setup": | ||
33 | "Prompt for development and/or incomplete code/drivers" = Y | ||
34 | (CONFIG_EXPERIMENTAL=y) | ||
35 | |||
36 | "Processor type and features": | ||
37 | "Paravirtualized guest support" = Y | ||
38 | "Lguest guest support" = Y | ||
39 | "High Memory Support" = off/4GB | ||
40 | "Alignment value to which kernel should be aligned" = 0x100000 | ||
41 | (CONFIG_PARAVIRT=y, CONFIG_LGUEST_GUEST=y, CONFIG_HIGHMEM64G=n and | ||
42 | CONFIG_PHYSICAL_ALIGN=0x100000) | ||
43 | |||
44 | "Device Drivers": | ||
45 | "Block devices" | ||
46 | "Virtio block driver (EXPERIMENTAL)" = M/Y | ||
47 | "Network device support" | ||
48 | "Universal TUN/TAP device driver support" = M/Y | ||
49 | "Virtio network driver (EXPERIMENTAL)" = M/Y | ||
50 | (CONFIG_VIRTIO_BLK=m, CONFIG_VIRTIO_NET=m and CONFIG_TUN=m) | ||
51 | |||
52 | "Virtualization" | ||
53 | "Linux hypervisor example code" = M/Y | ||
54 | (CONFIG_LGUEST=m) | ||
55 | |||
56 | - A tool called "lguest" is available in this directory: type "make" | ||
57 | to build it. If you didn't build your kernel in-tree, use "make | ||
58 | O=<builddir>". | ||
59 | |||
60 | - Create or find a root disk image. There are several useful ones | ||
61 | around, such as the xm-test tiny root image at | ||
62 | http://xm-test.xensource.com/ramdisks/initrd-1.1-i386.img | ||
63 | |||
64 | For more serious work, I usually use a distribution ISO image and | ||
65 | install it under qemu, then make multiple copies: | ||
66 | |||
67 | dd if=/dev/zero of=rootfile bs=1M count=2048 | ||
68 | qemu -cdrom image.iso -hda rootfile -net user -net nic -boot d | ||
69 | |||
70 | Make sure that you install a getty on /dev/hvc0 if you want to log in on the | ||
71 | console! | ||
72 | |||
73 | - "modprobe lg" if you built it as a module. | ||
74 | |||
75 | - Run an lguest as root: | ||
76 | |||
77 | Documentation/virtual/lguest/lguest 64 vmlinux --tunnet=192.168.19.1 \ | ||
78 | --block=rootfile root=/dev/vda | ||
79 | |||
80 | Explanation: | ||
81 | 64: the amount of memory to use, in MB. | ||
82 | |||
83 | vmlinux: the kernel image found in the top of your build directory. You | ||
84 | can also use a standard bzImage. | ||
85 | |||
86 | --tunnet=192.168.19.1: configures a "tap" device for networking with this | ||
87 | IP address. | ||
88 | |||
89 | --block=rootfile: a file or block device which becomes /dev/vda | ||
90 | inside the guest. | ||
91 | |||
92 | root=/dev/vda: this (and anything else on the command line) are | ||
93 | kernel boot parameters. | ||
94 | |||
95 | - Configuring networking. I usually have the host masquerade, using | ||
96 | "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE" and "echo 1 > | ||
97 | /proc/sys/net/ipv4/ip_forward". In this example, I would configure | ||
98 | eth0 inside the guest at 192.168.19.2. | ||
99 | |||
100 | Another method is to bridge the tap device to an external interface | ||
101 | using --tunnet=bridge:<bridgename>, and perhaps run dhcp on the guest | ||
102 | to obtain an IP address. The bridge needs to be configured first: | ||
103 | this option simply adds the tap interface to it. | ||
104 | |||
105 | A simple example on my system: | ||
106 | |||
107 | ifconfig eth0 0.0.0.0 | ||
108 | brctl addbr lg0 | ||
109 | ifconfig lg0 up | ||
110 | brctl addif lg0 eth0 | ||
111 | dhclient lg0 | ||
112 | |||
113 | Then use --tunnet=bridge:lg0 when launching the guest. | ||
114 | |||
115 | See: | ||
116 | |||
117 | http://www.linuxfoundation.org/collaborate/workgroups/networking/bridge | ||
118 | |||
119 | for general information on how to get bridging to work. | ||
120 | |||
121 | - Random number generation. Using the --rng option will provide a | ||
122 | /dev/hwrng in the guest that will read from the host's /dev/random. | ||
123 | Use this option in conjunction with rng-tools (see ../hw_random.txt) | ||
124 | to provide entropy to the guest kernel's /dev/random. | ||
125 | |||
126 | There is a helpful mailing list at http://ozlabs.org/mailman/listinfo/lguest | ||
127 | |||
128 | Good luck! | ||
129 | Rusty Russell rusty@rustcorp.com.au. | ||
diff --git a/tools/nfsd/inject_fault.sh b/tools/nfsd/inject_fault.sh new file mode 100755 index 000000000000..06a399ac8b2f --- /dev/null +++ b/tools/nfsd/inject_fault.sh | |||
@@ -0,0 +1,49 @@ | |||
1 | #!/bin/bash | ||
2 | # | ||
3 | # Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com> | ||
4 | # | ||
5 | # Script for easier NFSD fault injection | ||
6 | |||
7 | # Check that debugfs has been mounted | ||
8 | DEBUGFS=`cat /proc/mounts | grep debugfs` | ||
9 | if [ "$DEBUGFS" == "" ]; then | ||
10 | echo "debugfs does not appear to be mounted!" | ||
11 | echo "Please mount debugfs and try again" | ||
12 | exit 1 | ||
13 | fi | ||
14 | |||
15 | # Check that the fault injection directory exists | ||
16 | DEBUGDIR=`echo $DEBUGFS | awk '{print $2}'`/nfsd | ||
17 | if [ ! -d "$DEBUGDIR" ]; then | ||
18 | echo "$DEBUGDIR does not exist" | ||
19 | echo "Check that your .config selects CONFIG_NFSD_FAULT_INJECTION" | ||
20 | exit 1 | ||
21 | fi | ||
22 | |||
23 | function help() | ||
24 | { | ||
25 | echo "Usage $0 injection_type [count]" | ||
26 | echo "" | ||
27 | echo "Injection types are:" | ||
28 | ls $DEBUGDIR | ||
29 | exit 1 | ||
30 | } | ||
31 | |||
32 | if [ $# == 0 ]; then | ||
33 | help | ||
34 | elif [ ! -f $DEBUGDIR/$1 ]; then | ||
35 | help | ||
36 | elif [ $# != 2 ]; then | ||
37 | COUNT=0 | ||
38 | else | ||
39 | COUNT=$2 | ||
40 | fi | ||
41 | |||
42 | BEFORE=`mktemp` | ||
43 | AFTER=`mktemp` | ||
44 | dmesg > $BEFORE | ||
45 | echo $COUNT > $DEBUGDIR/$1 | ||
46 | dmesg > $AFTER | ||
47 | # Capture lines that only exist in the $AFTER file | ||
48 | diff $BEFORE $AFTER | grep ">" | ||
49 | rm -f $BEFORE $AFTER | ||
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile index 4626a398836a..ca600e09c8d4 100644 --- a/tools/perf/Documentation/Makefile +++ b/tools/perf/Documentation/Makefile | |||
@@ -1,3 +1,10 @@ | |||
1 | OUTPUT := ./ | ||
2 | ifeq ("$(origin O)", "command line") | ||
3 | ifneq ($(O),) | ||
4 | OUTPUT := $(O)/ | ||
5 | endif | ||
6 | endif | ||
7 | |||
1 | MAN1_TXT= \ | 8 | MAN1_TXT= \ |
2 | $(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \ | 9 | $(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \ |
3 | $(wildcard perf-*.txt)) \ | 10 | $(wildcard perf-*.txt)) \ |
@@ -6,10 +13,11 @@ MAN5_TXT= | |||
6 | MAN7_TXT= | 13 | MAN7_TXT= |
7 | 14 | ||
8 | MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT) | 15 | MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT) |
9 | MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT)) | 16 | _MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT)) |
10 | MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT)) | 17 | _MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT)) |
11 | 18 | ||
12 | DOC_HTML=$(MAN_HTML) | 19 | MAN_XML=$(addprefix $(OUTPUT),$(_MAN_XML)) |
20 | MAN_HTML=$(addprefix $(OUTPUT),$(_MAN_HTML)) | ||
13 | 21 | ||
14 | ARTICLES = | 22 | ARTICLES = |
15 | # with their own formatting rules. | 23 | # with their own formatting rules. |
@@ -18,11 +26,17 @@ API_DOCS = $(patsubst %.txt,%,$(filter-out technical/api-index-skel.txt technica | |||
18 | SP_ARTICLES += $(API_DOCS) | 26 | SP_ARTICLES += $(API_DOCS) |
19 | SP_ARTICLES += technical/api-index | 27 | SP_ARTICLES += technical/api-index |
20 | 28 | ||
21 | DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES)) | 29 | _DOC_HTML = $(_MAN_HTML) |
30 | _DOC_HTML+=$(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES)) | ||
31 | DOC_HTML=$(addprefix $(OUTPUT),$(_DOC_HTML)) | ||
22 | 32 | ||
23 | DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT)) | 33 | _DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT)) |
24 | DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT)) | 34 | _DOC_MAN5=$(patsubst %.txt,%.5,$(MAN5_TXT)) |
25 | DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT)) | 35 | _DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT)) |
36 | |||
37 | DOC_MAN1=$(addprefix $(OUTPUT),$(_DOC_MAN1)) | ||
38 | DOC_MAN5=$(addprefix $(OUTPUT),$(_DOC_MAN5)) | ||
39 | DOC_MAN7=$(addprefix $(OUTPUT),$(_DOC_MAN7)) | ||
26 | 40 | ||
27 | # Make the path relative to DESTDIR, not prefix | 41 | # Make the path relative to DESTDIR, not prefix |
28 | ifndef DESTDIR | 42 | ifndef DESTDIR |
@@ -150,9 +164,9 @@ man1: $(DOC_MAN1) | |||
150 | man5: $(DOC_MAN5) | 164 | man5: $(DOC_MAN5) |
151 | man7: $(DOC_MAN7) | 165 | man7: $(DOC_MAN7) |
152 | 166 | ||
153 | info: perf.info perfman.info | 167 | info: $(OUTPUT)perf.info $(OUTPUT)perfman.info |
154 | 168 | ||
155 | pdf: user-manual.pdf | 169 | pdf: $(OUTPUT)user-manual.pdf |
156 | 170 | ||
157 | install: install-man | 171 | install: install-man |
158 | 172 | ||
@@ -166,7 +180,7 @@ install-man: man | |||
166 | 180 | ||
167 | install-info: info | 181 | install-info: info |
168 | $(INSTALL) -d -m 755 $(DESTDIR)$(infodir) | 182 | $(INSTALL) -d -m 755 $(DESTDIR)$(infodir) |
169 | $(INSTALL) -m 644 perf.info perfman.info $(DESTDIR)$(infodir) | 183 | $(INSTALL) -m 644 $(OUTPUT)perf.info $(OUTPUT)perfman.info $(DESTDIR)$(infodir) |
170 | if test -r $(DESTDIR)$(infodir)/dir; then \ | 184 | if test -r $(DESTDIR)$(infodir)/dir; then \ |
171 | $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perf.info ;\ | 185 | $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perf.info ;\ |
172 | $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perfman.info ;\ | 186 | $(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) perfman.info ;\ |
@@ -176,7 +190,7 @@ install-info: info | |||
176 | 190 | ||
177 | install-pdf: pdf | 191 | install-pdf: pdf |
178 | $(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir) | 192 | $(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir) |
179 | $(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir) | 193 | $(INSTALL) -m 644 $(OUTPUT)user-manual.pdf $(DESTDIR)$(pdfdir) |
180 | 194 | ||
181 | #install-html: html | 195 | #install-html: html |
182 | # '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir) | 196 | # '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir) |
@@ -189,14 +203,14 @@ install-pdf: pdf | |||
189 | # | 203 | # |
190 | # Determine "include::" file references in asciidoc files. | 204 | # Determine "include::" file references in asciidoc files. |
191 | # | 205 | # |
192 | doc.dep : $(wildcard *.txt) build-docdep.perl | 206 | $(OUTPUT)doc.dep : $(wildcard *.txt) build-docdep.perl |
193 | $(QUIET_GEN)$(RM) $@+ $@ && \ | 207 | $(QUIET_GEN)$(RM) $@+ $@ && \ |
194 | $(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \ | 208 | $(PERL_PATH) ./build-docdep.perl >$@+ $(QUIET_STDERR) && \ |
195 | mv $@+ $@ | 209 | mv $@+ $@ |
196 | 210 | ||
197 | -include doc.dep | 211 | -include $(OUPTUT)doc.dep |
198 | 212 | ||
199 | cmds_txt = cmds-ancillaryinterrogators.txt \ | 213 | _cmds_txt = cmds-ancillaryinterrogators.txt \ |
200 | cmds-ancillarymanipulators.txt \ | 214 | cmds-ancillarymanipulators.txt \ |
201 | cmds-mainporcelain.txt \ | 215 | cmds-mainporcelain.txt \ |
202 | cmds-plumbinginterrogators.txt \ | 216 | cmds-plumbinginterrogators.txt \ |
@@ -205,32 +219,36 @@ cmds_txt = cmds-ancillaryinterrogators.txt \ | |||
205 | cmds-synchelpers.txt \ | 219 | cmds-synchelpers.txt \ |
206 | cmds-purehelpers.txt \ | 220 | cmds-purehelpers.txt \ |
207 | cmds-foreignscminterface.txt | 221 | cmds-foreignscminterface.txt |
222 | cmds_txt=$(addprefix $(OUTPUT),$(_cmds_txt)) | ||
208 | 223 | ||
209 | $(cmds_txt): cmd-list.made | 224 | $(cmds_txt): $(OUTPUT)cmd-list.made |
210 | 225 | ||
211 | cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT) | 226 | $(OUTPUT)cmd-list.made: cmd-list.perl ../command-list.txt $(MAN1_TXT) |
212 | $(QUIET_GEN)$(RM) $@ && \ | 227 | $(QUIET_GEN)$(RM) $@ && \ |
213 | $(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \ | 228 | $(PERL_PATH) ./cmd-list.perl ../command-list.txt $(QUIET_STDERR) && \ |
214 | date >$@ | 229 | date >$@ |
215 | 230 | ||
216 | clean: | 231 | clean: |
217 | $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 | 232 | $(RM) $(MAN_XML) $(addsuffix +,$(MAN_XML)) |
218 | $(RM) *.texi *.texi+ *.texi++ perf.info perfman.info | 233 | $(RM) $(MAN_HTML) $(addsuffix +,$(MAN_HTML)) |
219 | $(RM) howto-index.txt howto/*.html doc.dep | 234 | $(RM) $(DOC_HTML) $(DOC_MAN1) $(DOC_MAN5) $(DOC_MAN7) |
220 | $(RM) technical/api-*.html technical/api-index.txt | 235 | $(RM) $(OUTPUT)*.texi $(OUTPUT)*.texi+ $(OUTPUT)*.texi++ |
221 | $(RM) $(cmds_txt) *.made | 236 | $(RM) $(OUTPUT)perf.info $(OUTPUT)perfman.info |
222 | 237 | $(RM) $(OUTPUT)howto-index.txt $(OUTPUT)howto/*.html $(OUTPUT)doc.dep | |
223 | $(MAN_HTML): %.html : %.txt | 238 | $(RM) $(OUTPUT)technical/api-*.html $(OUTPUT)technical/api-index.txt |
239 | $(RM) $(cmds_txt) $(OUTPUT)*.made | ||
240 | |||
241 | $(MAN_HTML): $(OUTPUT)%.html : %.txt | ||
224 | $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ | 242 | $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ |
225 | $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \ | 243 | $(ASCIIDOC) -b xhtml11 -d manpage -f asciidoc.conf \ |
226 | $(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) -o $@+ $< && \ | 244 | $(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) -o $@+ $< && \ |
227 | mv $@+ $@ | 245 | mv $@+ $@ |
228 | 246 | ||
229 | %.1 %.5 %.7 : %.xml | 247 | $(OUTPUT)%.1 $(OUTPUT)%.5 $(OUTPUT)%.7 : $(OUTPUT)%.xml |
230 | $(QUIET_XMLTO)$(RM) $@ && \ | 248 | $(QUIET_XMLTO)$(RM) $@ && \ |
231 | xmlto -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< | 249 | xmlto -o $(OUTPUT) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $< |
232 | 250 | ||
233 | %.xml : %.txt | 251 | $(OUTPUT)%.xml : %.txt |
234 | $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ | 252 | $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \ |
235 | $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ | 253 | $(ASCIIDOC) -b docbook -d manpage -f asciidoc.conf \ |
236 | $(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) -o $@+ $< && \ | 254 | $(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) -o $@+ $< && \ |
@@ -239,25 +257,25 @@ $(MAN_HTML): %.html : %.txt | |||
239 | XSLT = docbook.xsl | 257 | XSLT = docbook.xsl |
240 | XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css | 258 | XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css |
241 | 259 | ||
242 | user-manual.html: user-manual.xml | 260 | $(OUTPUT)user-manual.html: $(OUTPUT)user-manual.xml |
243 | $(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $< | 261 | $(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $< |
244 | 262 | ||
245 | perf.info: user-manual.texi | 263 | $(OUTPUT)perf.info: $(OUTPUT)user-manual.texi |
246 | $(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ user-manual.texi | 264 | $(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ $(OUTPUT)user-manual.texi |
247 | 265 | ||
248 | user-manual.texi: user-manual.xml | 266 | $(OUTPUT)user-manual.texi: $(OUTPUT)user-manual.xml |
249 | $(QUIET_DB2TEXI)$(RM) $@+ $@ && \ | 267 | $(QUIET_DB2TEXI)$(RM) $@+ $@ && \ |
250 | $(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \ | 268 | $(DOCBOOK2X_TEXI) $(OUTPUT)user-manual.xml --encoding=UTF-8 --to-stdout >$@++ && \ |
251 | $(PERL_PATH) fix-texi.perl <$@++ >$@+ && \ | 269 | $(PERL_PATH) fix-texi.perl <$@++ >$@+ && \ |
252 | rm $@++ && \ | 270 | rm $@++ && \ |
253 | mv $@+ $@ | 271 | mv $@+ $@ |
254 | 272 | ||
255 | user-manual.pdf: user-manual.xml | 273 | $(OUTPUT)user-manual.pdf: $(OUTPUT)user-manual.xml |
256 | $(QUIET_DBLATEX)$(RM) $@+ $@ && \ | 274 | $(QUIET_DBLATEX)$(RM) $@+ $@ && \ |
257 | $(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< && \ | 275 | $(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $< && \ |
258 | mv $@+ $@ | 276 | mv $@+ $@ |
259 | 277 | ||
260 | perfman.texi: $(MAN_XML) cat-texi.perl | 278 | $(OUTPUT)perfman.texi: $(MAN_XML) cat-texi.perl |
261 | $(QUIET_DB2TEXI)$(RM) $@+ $@ && \ | 279 | $(QUIET_DB2TEXI)$(RM) $@+ $@ && \ |
262 | ($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \ | 280 | ($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \ |
263 | --to-stdout $(xml) &&) true) > $@++ && \ | 281 | --to-stdout $(xml) &&) true) > $@++ && \ |
@@ -265,7 +283,7 @@ perfman.texi: $(MAN_XML) cat-texi.perl | |||
265 | rm $@++ && \ | 283 | rm $@++ && \ |
266 | mv $@+ $@ | 284 | mv $@+ $@ |
267 | 285 | ||
268 | perfman.info: perfman.texi | 286 | $(OUTPUT)perfman.info: $(OUTPUT)perfman.texi |
269 | $(QUIET_MAKEINFO)$(MAKEINFO) --no-split --no-validate $*.texi | 287 | $(QUIET_MAKEINFO)$(MAKEINFO) --no-split --no-validate $*.texi |
270 | 288 | ||
271 | $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml | 289 | $(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml |
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 7a527f7e9da9..ddc22525228d 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
@@ -21,6 +21,8 @@ EVENT MODIFIERS | |||
21 | Events can optionally have a modifer by appending a colon and one or | 21 | Events can optionally have a modifer by appending a colon and one or |
22 | more modifiers. Modifiers allow the user to restrict when events are | 22 | more modifiers. Modifiers allow the user to restrict when events are |
23 | counted with 'u' for user-space, 'k' for kernel, 'h' for hypervisor. | 23 | counted with 'u' for user-space, 'k' for kernel, 'h' for hypervisor. |
24 | Additional modifiers are 'G' for guest counting (in KVM guests) and 'H' | ||
25 | for host counting (not in KVM guests). | ||
24 | 26 | ||
25 | The 'p' modifier can be used for specifying how precise the instruction | 27 | The 'p' modifier can be used for specifying how precise the instruction |
26 | address should be. The 'p' modifier is currently only implemented for | 28 | address should be. The 'p' modifier is currently only implemented for |
diff --git a/tools/perf/Documentation/perf-lock.txt b/tools/perf/Documentation/perf-lock.txt index d6b2a4f2108b..c7f5f55634ac 100644 --- a/tools/perf/Documentation/perf-lock.txt +++ b/tools/perf/Documentation/perf-lock.txt | |||
@@ -8,7 +8,7 @@ perf-lock - Analyze lock events | |||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf lock' {record|report|trace} | 11 | 'perf lock' {record|report|script|info} |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
@@ -20,10 +20,13 @@ and statistics with this 'perf lock' command. | |||
20 | produces the file "perf.data" which contains tracing | 20 | produces the file "perf.data" which contains tracing |
21 | results of lock events. | 21 | results of lock events. |
22 | 22 | ||
23 | 'perf lock trace' shows raw lock events. | ||
24 | |||
25 | 'perf lock report' reports statistical data. | 23 | 'perf lock report' reports statistical data. |
26 | 24 | ||
25 | 'perf lock script' shows raw lock events. | ||
26 | |||
27 | 'perf lock info' shows metadata like threads or addresses | ||
28 | of lock instances. | ||
29 | |||
27 | COMMON OPTIONS | 30 | COMMON OPTIONS |
28 | -------------- | 31 | -------------- |
29 | 32 | ||
@@ -47,6 +50,17 @@ REPORT OPTIONS | |||
47 | Sorting key. Possible values: acquired (default), contended, | 50 | Sorting key. Possible values: acquired (default), contended, |
48 | wait_total, wait_max, wait_min. | 51 | wait_total, wait_max, wait_min. |
49 | 52 | ||
53 | INFO OPTIONS | ||
54 | ------------ | ||
55 | |||
56 | -t:: | ||
57 | --threads:: | ||
58 | dump thread list in perf.data | ||
59 | |||
60 | -m:: | ||
61 | --map:: | ||
62 | dump map of lock instances (address:name table) | ||
63 | |||
50 | SEE ALSO | 64 | SEE ALSO |
51 | -------- | 65 | -------- |
52 | linkperf:perf[1] | 66 | linkperf:perf[1] |
diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 2937f7e14bb7..a1386b2fff00 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt | |||
@@ -52,11 +52,15 @@ OPTIONS | |||
52 | 52 | ||
53 | -p:: | 53 | -p:: |
54 | --pid=:: | 54 | --pid=:: |
55 | Record events on existing process ID. | 55 | Record events on existing process ID (comma separated list). |
56 | 56 | ||
57 | -t:: | 57 | -t:: |
58 | --tid=:: | 58 | --tid=:: |
59 | Record events on existing thread ID. | 59 | Record events on existing thread ID (comma separated list). |
60 | |||
61 | -u:: | ||
62 | --uid=:: | ||
63 | Record events in threads owned by uid. Name or number. | ||
60 | 64 | ||
61 | -r:: | 65 | -r:: |
62 | --realtime=:: | 66 | --realtime=:: |
@@ -148,6 +152,36 @@ an empty cgroup (monitor all the time) using, e.g., -G foo,,bar. Cgroups must ha | |||
148 | corresponding events, i.e., they always refer to events defined earlier on the command | 152 | corresponding events, i.e., they always refer to events defined earlier on the command |
149 | line. | 153 | line. |
150 | 154 | ||
155 | -b:: | ||
156 | --branch-any:: | ||
157 | Enable taken branch stack sampling. Any type of taken branch may be sampled. | ||
158 | This is a shortcut for --branch-filter any. See --branch-filter for more infos. | ||
159 | |||
160 | -j:: | ||
161 | --branch-filter:: | ||
162 | Enable taken branch stack sampling. Each sample captures a series of consecutive | ||
163 | taken branches. The number of branches captured with each sample depends on the | ||
164 | underlying hardware, the type of branches of interest, and the executed code. | ||
165 | It is possible to select the types of branches captured by enabling filters. The | ||
166 | following filters are defined: | ||
167 | |||
168 | - any: any type of branches | ||
169 | - any_call: any function call or system call | ||
170 | - any_ret: any function return or system call return | ||
171 | - any_ind: any indirect branch | ||
172 | - u: only when the branch target is at the user level | ||
173 | - k: only when the branch target is in the kernel | ||
174 | - hv: only when the target is at the hypervisor level | ||
175 | |||
176 | + | ||
177 | The option requires at least one branch type among any, any_call, any_ret, ind_call. | ||
178 | The privilege levels may be ommitted, in which case, the privilege levels of the associated | ||
179 | event are applied to the branch filter. Both kernel (k) and hypervisor (hv) privilege | ||
180 | levels are subject to permissions. When sampling on multiple events, branch stack sampling | ||
181 | is enabled for all the sampling events. The sampled branch type is the same for all events. | ||
182 | The various filters must be specified as a comma separated list: --branch-filter any_ret,u,k | ||
183 | Note that this feature may not be available on all processors. | ||
184 | |||
151 | SEE ALSO | 185 | SEE ALSO |
152 | -------- | 186 | -------- |
153 | linkperf:perf-stat[1], linkperf:perf-list[1] | 187 | linkperf:perf-stat[1], linkperf:perf-list[1] |
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index 9b430e98712e..2d89f02719b5 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
@@ -48,6 +48,9 @@ OPTIONS | |||
48 | Only consider these symbols. CSV that understands | 48 | Only consider these symbols. CSV that understands |
49 | file://filename entries. | 49 | file://filename entries. |
50 | 50 | ||
51 | --symbol-filter=:: | ||
52 | Only show symbols that match (partially) with this filter. | ||
53 | |||
51 | -U:: | 54 | -U:: |
52 | --hide-unresolved:: | 55 | --hide-unresolved:: |
53 | Only display entries resolved to a symbol. | 56 | Only display entries resolved to a symbol. |
@@ -110,6 +113,8 @@ OPTIONS | |||
110 | requires a tty, if one is not present, as when piping to other | 113 | requires a tty, if one is not present, as when piping to other |
111 | commands, the stdio interface is used. | 114 | commands, the stdio interface is used. |
112 | 115 | ||
116 | --gtk:: Use the GTK2 interface. | ||
117 | |||
113 | -k:: | 118 | -k:: |
114 | --vmlinux=<file>:: | 119 | --vmlinux=<file>:: |
115 | vmlinux pathname | 120 | vmlinux pathname |
@@ -153,6 +158,16 @@ OPTIONS | |||
153 | information which may be very large and thus may clutter the display. | 158 | information which may be very large and thus may clutter the display. |
154 | It currently includes: cpu and numa topology of the host system. | 159 | It currently includes: cpu and numa topology of the host system. |
155 | 160 | ||
161 | -b:: | ||
162 | --branch-stack:: | ||
163 | Use the addresses of sampled taken branches instead of the instruction | ||
164 | address to build the histograms. To generate meaningful output, the | ||
165 | perf.data file must have been obtained using perf record -b or | ||
166 | perf record --branch-filter xxx where xxx is a branch filter option. | ||
167 | perf report is able to auto-detect whether a perf.data file contains | ||
168 | branch stacks and it will automatically switch to the branch view mode, | ||
169 | unless --no-branch-stack is used. | ||
170 | |||
156 | SEE ALSO | 171 | SEE ALSO |
157 | -------- | 172 | -------- |
158 | linkperf:perf-stat[1], linkperf:perf-annotate[1] | 173 | linkperf:perf-stat[1], linkperf:perf-annotate[1] |
diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 2f6cef43da25..e9cbfcddfa3f 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt | |||
@@ -115,7 +115,7 @@ OPTIONS | |||
115 | -f:: | 115 | -f:: |
116 | --fields:: | 116 | --fields:: |
117 | Comma separated list of fields to print. Options are: | 117 | Comma separated list of fields to print. Options are: |
118 | comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr. | 118 | comm, tid, pid, time, cpu, event, trace, ip, sym, dso, addr, symoff. |
119 | Field list can be prepended with the type, trace, sw or hw, | 119 | Field list can be prepended with the type, trace, sw or hw, |
120 | to indicate to which event type the field list applies. | 120 | to indicate to which event type the field list applies. |
121 | e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace | 121 | e.g., -f sw:comm,tid,time,ip,sym and -f trace:time,cpu,trace |
@@ -200,6 +200,9 @@ OPTIONS | |||
200 | It currently includes: cpu and numa topology of the host system. | 200 | It currently includes: cpu and numa topology of the host system. |
201 | It can only be used with the perf script report mode. | 201 | It can only be used with the perf script report mode. |
202 | 202 | ||
203 | --show-kernel-path:: | ||
204 | Try to resolve the path of [kernel.kallsyms] | ||
205 | |||
203 | SEE ALSO | 206 | SEE ALSO |
204 | -------- | 207 | -------- |
205 | linkperf:perf-record[1], linkperf:perf-script-perl[1], | 208 | linkperf:perf-record[1], linkperf:perf-script-perl[1], |
diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 8966b9ab2014..2fa173b51970 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt | |||
@@ -35,11 +35,11 @@ OPTIONS | |||
35 | child tasks do not inherit counters | 35 | child tasks do not inherit counters |
36 | -p:: | 36 | -p:: |
37 | --pid=<pid>:: | 37 | --pid=<pid>:: |
38 | stat events on existing process id | 38 | stat events on existing process id (comma separated list) |
39 | 39 | ||
40 | -t:: | 40 | -t:: |
41 | --tid=<tid>:: | 41 | --tid=<tid>:: |
42 | stat events on existing thread id | 42 | stat events on existing thread id (comma separated list) |
43 | 43 | ||
44 | 44 | ||
45 | -a:: | 45 | -a:: |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index b1a5bbbfebef..4a5680cb242e 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
@@ -72,11 +72,15 @@ Default is to monitor all CPUS. | |||
72 | 72 | ||
73 | -p <pid>:: | 73 | -p <pid>:: |
74 | --pid=<pid>:: | 74 | --pid=<pid>:: |
75 | Profile events on existing Process ID. | 75 | Profile events on existing Process ID (comma separated list). |
76 | 76 | ||
77 | -t <tid>:: | 77 | -t <tid>:: |
78 | --tid=<tid>:: | 78 | --tid=<tid>:: |
79 | Profile events on existing thread ID. | 79 | Profile events on existing thread ID (comma separated list). |
80 | |||
81 | -u:: | ||
82 | --uid=:: | ||
83 | Record events in threads owned by uid. Name or number. | ||
80 | 84 | ||
81 | -r <priority>:: | 85 | -r <priority>:: |
82 | --realtime=<priority>:: | 86 | --realtime=<priority>:: |
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index c12659d8cb26..5476bc0a1eac 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST | |||
@@ -1,4 +1,5 @@ | |||
1 | tools/perf | 1 | tools/perf |
2 | include/linux/const.h | ||
2 | include/linux/perf_event.h | 3 | include/linux/perf_event.h |
3 | include/linux/rbtree.h | 4 | include/linux/rbtree.h |
4 | include/linux/list.h | 5 | include/linux/list.h |
@@ -8,6 +9,7 @@ lib/rbtree.c | |||
8 | include/linux/swab.h | 9 | include/linux/swab.h |
9 | arch/*/include/asm/unistd*.h | 10 | arch/*/include/asm/unistd*.h |
10 | arch/*/lib/memcpy*.S | 11 | arch/*/lib/memcpy*.S |
12 | arch/*/lib/memset*.S | ||
11 | include/linux/poison.h | 13 | include/linux/poison.h |
12 | include/linux/magic.h | 14 | include/linux/magic.h |
13 | include/linux/hw_breakpoint.h | 15 | include/linux/hw_breakpoint.h |
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index ac86d67b636e..820371f10d1b 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
@@ -15,6 +15,16 @@ endif | |||
15 | 15 | ||
16 | # Define V to have a more verbose compile. | 16 | # Define V to have a more verbose compile. |
17 | # | 17 | # |
18 | # Define O to save output files in a separate directory. | ||
19 | # | ||
20 | # Define ARCH as name of target architecture if you want cross-builds. | ||
21 | # | ||
22 | # Define CROSS_COMPILE as prefix name of compiler if you want cross-builds. | ||
23 | # | ||
24 | # Define NO_LIBPERL to disable perl script extension. | ||
25 | # | ||
26 | # Define NO_LIBPYTHON to disable python script extension. | ||
27 | # | ||
18 | # Define PYTHON to point to the python binary if the default | 28 | # Define PYTHON to point to the python binary if the default |
19 | # `python' is not correct; for example: PYTHON=python2 | 29 | # `python' is not correct; for example: PYTHON=python2 |
20 | # | 30 | # |
@@ -32,6 +42,10 @@ endif | |||
32 | # Define NO_DWARF if you do not want debug-info analysis feature at all. | 42 | # Define NO_DWARF if you do not want debug-info analysis feature at all. |
33 | # | 43 | # |
34 | # Define WERROR=0 to disable treating any warnings as errors. | 44 | # Define WERROR=0 to disable treating any warnings as errors. |
45 | # | ||
46 | # Define NO_NEWT if you do not want TUI support. | ||
47 | # | ||
48 | # Define NO_DEMANGLE if you do not want C++ symbol demangling. | ||
35 | 49 | ||
36 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE | 50 | $(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE |
37 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) | 51 | @$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT) |
@@ -61,7 +75,7 @@ ifeq ($(ARCH),x86_64) | |||
61 | ifeq (${IS_X86_64}, 1) | 75 | ifeq (${IS_X86_64}, 1) |
62 | RAW_ARCH := x86_64 | 76 | RAW_ARCH := x86_64 |
63 | ARCH_CFLAGS := -DARCH_X86_64 | 77 | ARCH_CFLAGS := -DARCH_X86_64 |
64 | ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S | 78 | ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S |
65 | endif | 79 | endif |
66 | endif | 80 | endif |
67 | 81 | ||
@@ -104,7 +118,7 @@ endif | |||
104 | 118 | ||
105 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) | 119 | CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) |
106 | EXTLIBS = -lpthread -lrt -lelf -lm | 120 | EXTLIBS = -lpthread -lrt -lelf -lm |
107 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 | 121 | ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE |
108 | ALL_LDFLAGS = $(LDFLAGS) | 122 | ALL_LDFLAGS = $(LDFLAGS) |
109 | STRIP ?= strip | 123 | STRIP ?= strip |
110 | 124 | ||
@@ -168,10 +182,7 @@ endif | |||
168 | 182 | ||
169 | ### --- END CONFIGURATION SECTION --- | 183 | ### --- END CONFIGURATION SECTION --- |
170 | 184 | ||
171 | # Those must not be GNU-specific; they are shared with perl/ which may | 185 | BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include -I$(OUTPUT)/util -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE |
172 | # be built by a different compiler. (Note that this is an artifact now | ||
173 | # but it still might be nice to keep that distinction.) | ||
174 | BASIC_CFLAGS = -Iutil/include -Iarch/$(ARCH)/include | ||
175 | BASIC_LDFLAGS = | 186 | BASIC_LDFLAGS = |
176 | 187 | ||
177 | # Guard against environment variables | 188 | # Guard against environment variables |
@@ -186,7 +197,10 @@ SCRIPT_SH += perf-archive.sh | |||
186 | grep-libs = $(filter -l%,$(1)) | 197 | grep-libs = $(filter -l%,$(1)) |
187 | strip-libs = $(filter-out -l%,$(1)) | 198 | strip-libs = $(filter-out -l%,$(1)) |
188 | 199 | ||
189 | $(OUTPUT)python/perf.so: $(PYRF_OBJS) | 200 | PYTHON_EXT_SRCS := $(shell grep -v ^\# util/python-ext-sources) |
201 | PYTHON_EXT_DEPS := util/python-ext-sources util/setup.py | ||
202 | |||
203 | $(OUTPUT)python/perf.so: $(PYRF_OBJS) $(PYTHON_EXT_SRCS) $(PYTHON_EXT_DEPS) | ||
190 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ | 204 | $(QUIET_GEN)CFLAGS='$(BASIC_CFLAGS)' $(PYTHON_WORD) util/setup.py \ |
191 | --quiet build_ext; \ | 205 | --quiet build_ext; \ |
192 | mkdir -p $(OUTPUT)python && \ | 206 | mkdir -p $(OUTPUT)python && \ |
@@ -220,6 +234,25 @@ endif | |||
220 | 234 | ||
221 | export PERL_PATH | 235 | export PERL_PATH |
222 | 236 | ||
237 | FLEX = $(CROSS_COMPILE)flex | ||
238 | BISON= $(CROSS_COMPILE)bison | ||
239 | |||
240 | event-parser: | ||
241 | $(QUIET_BISON)$(BISON) -v util/parse-events.y -d -o $(OUTPUT)util/parse-events-bison.c | ||
242 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c | ||
243 | |||
244 | $(OUTPUT)util/parse-events-flex.c: event-parser | ||
245 | $(OUTPUT)util/parse-events-bison.c: event-parser | ||
246 | |||
247 | pmu-parser: | ||
248 | $(QUIET_BISON)$(BISON) -v util/pmu.y -d -o $(OUTPUT)util/pmu-bison.c | ||
249 | $(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c | ||
250 | |||
251 | $(OUTPUT)util/pmu-flex.c: pmu-parser | ||
252 | $(OUTPUT)util/pmu-bison.c: pmu-parser | ||
253 | |||
254 | $(OUTPUT)util/parse-events.o: event-parser pmu-parser | ||
255 | |||
223 | LIB_FILE=$(OUTPUT)libperf.a | 256 | LIB_FILE=$(OUTPUT)libperf.a |
224 | 257 | ||
225 | LIB_H += ../../include/linux/perf_event.h | 258 | LIB_H += ../../include/linux/perf_event.h |
@@ -235,7 +268,7 @@ LIB_H += util/include/linux/const.h | |||
235 | LIB_H += util/include/linux/ctype.h | 268 | LIB_H += util/include/linux/ctype.h |
236 | LIB_H += util/include/linux/kernel.h | 269 | LIB_H += util/include/linux/kernel.h |
237 | LIB_H += util/include/linux/list.h | 270 | LIB_H += util/include/linux/list.h |
238 | LIB_H += util/include/linux/module.h | 271 | LIB_H += util/include/linux/export.h |
239 | LIB_H += util/include/linux/poison.h | 272 | LIB_H += util/include/linux/poison.h |
240 | LIB_H += util/include/linux/prefetch.h | 273 | LIB_H += util/include/linux/prefetch.h |
241 | LIB_H += util/include/linux/rbtree.h | 274 | LIB_H += util/include/linux/rbtree.h |
@@ -252,6 +285,8 @@ LIB_H += util/include/asm/uaccess.h | |||
252 | LIB_H += util/include/dwarf-regs.h | 285 | LIB_H += util/include/dwarf-regs.h |
253 | LIB_H += util/include/asm/dwarf2.h | 286 | LIB_H += util/include/asm/dwarf2.h |
254 | LIB_H += util/include/asm/cpufeature.h | 287 | LIB_H += util/include/asm/cpufeature.h |
288 | LIB_H += util/include/asm/unistd_32.h | ||
289 | LIB_H += util/include/asm/unistd_64.h | ||
255 | LIB_H += perf.h | 290 | LIB_H += perf.h |
256 | LIB_H += util/annotate.h | 291 | LIB_H += util/annotate.h |
257 | LIB_H += util/cache.h | 292 | LIB_H += util/cache.h |
@@ -259,6 +294,8 @@ LIB_H += util/callchain.h | |||
259 | LIB_H += util/build-id.h | 294 | LIB_H += util/build-id.h |
260 | LIB_H += util/debug.h | 295 | LIB_H += util/debug.h |
261 | LIB_H += util/debugfs.h | 296 | LIB_H += util/debugfs.h |
297 | LIB_H += util/sysfs.h | ||
298 | LIB_H += util/pmu.h | ||
262 | LIB_H += util/event.h | 299 | LIB_H += util/event.h |
263 | LIB_H += util/evsel.h | 300 | LIB_H += util/evsel.h |
264 | LIB_H += util/evlist.h | 301 | LIB_H += util/evlist.h |
@@ -305,6 +342,8 @@ LIB_OBJS += $(OUTPUT)util/build-id.o | |||
305 | LIB_OBJS += $(OUTPUT)util/config.o | 342 | LIB_OBJS += $(OUTPUT)util/config.o |
306 | LIB_OBJS += $(OUTPUT)util/ctype.o | 343 | LIB_OBJS += $(OUTPUT)util/ctype.o |
307 | LIB_OBJS += $(OUTPUT)util/debugfs.o | 344 | LIB_OBJS += $(OUTPUT)util/debugfs.o |
345 | LIB_OBJS += $(OUTPUT)util/sysfs.o | ||
346 | LIB_OBJS += $(OUTPUT)util/pmu.o | ||
308 | LIB_OBJS += $(OUTPUT)util/environment.o | 347 | LIB_OBJS += $(OUTPUT)util/environment.o |
309 | LIB_OBJS += $(OUTPUT)util/event.o | 348 | LIB_OBJS += $(OUTPUT)util/event.o |
310 | LIB_OBJS += $(OUTPUT)util/evlist.o | 349 | LIB_OBJS += $(OUTPUT)util/evlist.o |
@@ -341,6 +380,10 @@ LIB_OBJS += $(OUTPUT)util/session.o | |||
341 | LIB_OBJS += $(OUTPUT)util/thread.o | 380 | LIB_OBJS += $(OUTPUT)util/thread.o |
342 | LIB_OBJS += $(OUTPUT)util/thread_map.o | 381 | LIB_OBJS += $(OUTPUT)util/thread_map.o |
343 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o | 382 | LIB_OBJS += $(OUTPUT)util/trace-event-parse.o |
383 | LIB_OBJS += $(OUTPUT)util/parse-events-flex.o | ||
384 | LIB_OBJS += $(OUTPUT)util/parse-events-bison.o | ||
385 | LIB_OBJS += $(OUTPUT)util/pmu-flex.o | ||
386 | LIB_OBJS += $(OUTPUT)util/pmu-bison.o | ||
344 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o | 387 | LIB_OBJS += $(OUTPUT)util/trace-event-read.o |
345 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o | 388 | LIB_OBJS += $(OUTPUT)util/trace-event-info.o |
346 | LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o | 389 | LIB_OBJS += $(OUTPUT)util/trace-event-scripting.o |
@@ -362,8 +405,10 @@ BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o | |||
362 | BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o | 405 | BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o |
363 | ifeq ($(RAW_ARCH),x86_64) | 406 | ifeq ($(RAW_ARCH),x86_64) |
364 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o | 407 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy-x86-64-asm.o |
408 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memset-x86-64-asm.o | ||
365 | endif | 409 | endif |
366 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o | 410 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memcpy.o |
411 | BUILTIN_OBJS += $(OUTPUT)bench/mem-memset.o | ||
367 | 412 | ||
368 | BUILTIN_OBJS += $(OUTPUT)builtin-diff.o | 413 | BUILTIN_OBJS += $(OUTPUT)builtin-diff.o |
369 | BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o | 414 | BUILTIN_OBJS += $(OUTPUT)builtin-evlist.o |
@@ -481,6 +526,20 @@ else | |||
481 | endif | 526 | endif |
482 | endif | 527 | endif |
483 | 528 | ||
529 | ifdef NO_GTK2 | ||
530 | BASIC_CFLAGS += -DNO_GTK2 | ||
531 | else | ||
532 | FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0) | ||
533 | ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y) | ||
534 | msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev); | ||
535 | BASIC_CFLAGS += -DNO_GTK2_SUPPORT | ||
536 | else | ||
537 | BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0) | ||
538 | EXTLIBS += $(shell pkg-config --libs gtk+-2.0) | ||
539 | LIB_OBJS += $(OUTPUT)util/gtk/browser.o | ||
540 | endif | ||
541 | endif | ||
542 | |||
484 | ifdef NO_LIBPERL | 543 | ifdef NO_LIBPERL |
485 | BASIC_CFLAGS += -DNO_LIBPERL | 544 | BASIC_CFLAGS += -DNO_LIBPERL |
486 | else | 545 | else |
@@ -627,6 +686,8 @@ ifndef V | |||
627 | QUIET_LINK = @echo ' ' LINK $@; | 686 | QUIET_LINK = @echo ' ' LINK $@; |
628 | QUIET_MKDIR = @echo ' ' MKDIR $@; | 687 | QUIET_MKDIR = @echo ' ' MKDIR $@; |
629 | QUIET_GEN = @echo ' ' GEN $@; | 688 | QUIET_GEN = @echo ' ' GEN $@; |
689 | QUIET_FLEX = @echo ' ' FLEX $@; | ||
690 | QUIET_BISON = @echo ' ' BISON $@; | ||
630 | endif | 691 | endif |
631 | endif | 692 | endif |
632 | 693 | ||
@@ -707,12 +768,28 @@ $(OUTPUT)perf.o perf.spec \ | |||
707 | $(SCRIPTS) \ | 768 | $(SCRIPTS) \ |
708 | : $(OUTPUT)PERF-VERSION-FILE | 769 | : $(OUTPUT)PERF-VERSION-FILE |
709 | 770 | ||
771 | .SUFFIXES: | ||
772 | .SUFFIXES: .o .c .S .s | ||
773 | |||
774 | # These two need to be here so that when O= is not used they take precedence | ||
775 | # over the general rule for .o | ||
776 | |||
777 | $(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS | ||
778 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $< | ||
779 | |||
780 | $(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS | ||
781 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -Iutil/ -Wno-redundant-decls -Wno-switch-default -Wno-unused-function $< | ||
782 | |||
710 | $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS | 783 | $(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS |
711 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | 784 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< |
785 | $(OUTPUT)%.i: %.c $(OUTPUT)PERF-CFLAGS | ||
786 | $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $< | ||
712 | $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS | 787 | $(OUTPUT)%.s: %.c $(OUTPUT)PERF-CFLAGS |
713 | $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< | 788 | $(QUIET_CC)$(CC) -o $@ -S $(ALL_CFLAGS) $< |
714 | $(OUTPUT)%.o: %.S | 789 | $(OUTPUT)%.o: %.S |
715 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< | 790 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< |
791 | $(OUTPUT)%.s: %.S | ||
792 | $(QUIET_CC)$(CC) -o $@ -E $(ALL_CFLAGS) $< | ||
716 | 793 | ||
717 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS | 794 | $(OUTPUT)util/exec_cmd.o: util/exec_cmd.c $(OUTPUT)PERF-CFLAGS |
718 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ | 795 | $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) \ |
@@ -775,6 +852,8 @@ help: | |||
775 | @echo ' html - make html documentation' | 852 | @echo ' html - make html documentation' |
776 | @echo ' info - make GNU info documentation (access with info <foo>)' | 853 | @echo ' info - make GNU info documentation (access with info <foo>)' |
777 | @echo ' pdf - make pdf documentation' | 854 | @echo ' pdf - make pdf documentation' |
855 | @echo ' event-parser - make event parser code' | ||
856 | @echo ' pmu-parser - make pmu format parser code' | ||
778 | @echo ' TAGS - use etags to make tag information for source browsing' | 857 | @echo ' TAGS - use etags to make tag information for source browsing' |
779 | @echo ' tags - use ctags to make tag information for source browsing' | 858 | @echo ' tags - use ctags to make tag information for source browsing' |
780 | @echo ' cscope - use cscope to make interactive browsing database' | 859 | @echo ' cscope - use cscope to make interactive browsing database' |
@@ -795,7 +874,6 @@ help: | |||
795 | @echo ' quick-install-html - install the html documentation quickly' | 874 | @echo ' quick-install-html - install the html documentation quickly' |
796 | @echo '' | 875 | @echo '' |
797 | @echo 'Perf maintainer targets:' | 876 | @echo 'Perf maintainer targets:' |
798 | @echo ' distclean - alias to clean' | ||
799 | @echo ' clean - clean all binary objects and build output' | 877 | @echo ' clean - clean all binary objects and build output' |
800 | 878 | ||
801 | doc: | 879 | doc: |
@@ -912,6 +990,7 @@ clean: | |||
912 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* | 990 | $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* |
913 | $(MAKE) -C Documentation/ clean | 991 | $(MAKE) -C Documentation/ clean |
914 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS | 992 | $(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS |
993 | $(RM) $(OUTPUT)util/*-{bison,flex}* | ||
915 | $(python-clean) | 994 | $(python-clean) |
916 | 995 | ||
917 | .PHONY: all install clean strip | 996 | .PHONY: all install clean strip |
diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c index eba80c292945..2f7073d107fd 100644 --- a/tools/perf/arch/powerpc/util/header.c +++ b/tools/perf/arch/powerpc/util/header.c | |||
@@ -25,7 +25,7 @@ get_cpuid(char *buffer, size_t sz) | |||
25 | 25 | ||
26 | pvr = mfspr(SPRN_PVR); | 26 | pvr = mfspr(SPRN_PVR); |
27 | 27 | ||
28 | nb = snprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr)); | 28 | nb = scnprintf(buffer, sz, "%lu,%lu$", PVR_VER(pvr), PVR_REV(pvr)); |
29 | 29 | ||
30 | /* look for end marker to ensure the entire data fit */ | 30 | /* look for end marker to ensure the entire data fit */ |
31 | if (strchr(buffer, '$')) { | 31 | if (strchr(buffer, '$')) { |
diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c index f94006068d2b..146d12a1cec0 100644 --- a/tools/perf/arch/x86/util/header.c +++ b/tools/perf/arch/x86/util/header.c | |||
@@ -48,7 +48,7 @@ get_cpuid(char *buffer, size_t sz) | |||
48 | if (family >= 0x6) | 48 | if (family >= 0x6) |
49 | model += ((a >> 16) & 0xf) << 4; | 49 | model += ((a >> 16) & 0xf) << 4; |
50 | } | 50 | } |
51 | nb = snprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step); | 51 | nb = scnprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step); |
52 | 52 | ||
53 | /* look for end marker to ensure the entire data fit */ | 53 | /* look for end marker to ensure the entire data fit */ |
54 | if (strchr(buffer, '$')) { | 54 | if (strchr(buffer, '$')) { |
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h index f7781c6267c0..a09bece6dad2 100644 --- a/tools/perf/bench/bench.h +++ b/tools/perf/bench/bench.h | |||
@@ -4,6 +4,7 @@ | |||
4 | extern int bench_sched_messaging(int argc, const char **argv, const char *prefix); | 4 | extern int bench_sched_messaging(int argc, const char **argv, const char *prefix); |
5 | extern int bench_sched_pipe(int argc, const char **argv, const char *prefix); | 5 | extern int bench_sched_pipe(int argc, const char **argv, const char *prefix); |
6 | extern int bench_mem_memcpy(int argc, const char **argv, const char *prefix __used); | 6 | extern int bench_mem_memcpy(int argc, const char **argv, const char *prefix __used); |
7 | extern int bench_mem_memset(int argc, const char **argv, const char *prefix); | ||
7 | 8 | ||
8 | #define BENCH_FORMAT_DEFAULT_STR "default" | 9 | #define BENCH_FORMAT_DEFAULT_STR "default" |
9 | #define BENCH_FORMAT_DEFAULT 0 | 10 | #define BENCH_FORMAT_DEFAULT 0 |
diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm-def.h b/tools/perf/bench/mem-memcpy-x86-64-asm-def.h index d588b87696fc..d66ab799b35f 100644 --- a/tools/perf/bench/mem-memcpy-x86-64-asm-def.h +++ b/tools/perf/bench/mem-memcpy-x86-64-asm-def.h | |||
@@ -2,3 +2,11 @@ | |||
2 | MEMCPY_FN(__memcpy, | 2 | MEMCPY_FN(__memcpy, |
3 | "x86-64-unrolled", | 3 | "x86-64-unrolled", |
4 | "unrolled memcpy() in arch/x86/lib/memcpy_64.S") | 4 | "unrolled memcpy() in arch/x86/lib/memcpy_64.S") |
5 | |||
6 | MEMCPY_FN(memcpy_c, | ||
7 | "x86-64-movsq", | ||
8 | "movsq-based memcpy() in arch/x86/lib/memcpy_64.S") | ||
9 | |||
10 | MEMCPY_FN(memcpy_c_e, | ||
11 | "x86-64-movsb", | ||
12 | "movsb-based memcpy() in arch/x86/lib/memcpy_64.S") | ||
diff --git a/tools/perf/bench/mem-memcpy-x86-64-asm.S b/tools/perf/bench/mem-memcpy-x86-64-asm.S index a57b66e853c2..fcd9cf00600a 100644 --- a/tools/perf/bench/mem-memcpy-x86-64-asm.S +++ b/tools/perf/bench/mem-memcpy-x86-64-asm.S | |||
@@ -1,2 +1,12 @@ | |||
1 | 1 | #define memcpy MEMCPY /* don't hide glibc's memcpy() */ | |
2 | #define altinstr_replacement text | ||
3 | #define globl p2align 4; .globl | ||
4 | #define Lmemcpy_c globl memcpy_c; memcpy_c | ||
5 | #define Lmemcpy_c_e globl memcpy_c_e; memcpy_c_e | ||
2 | #include "../../../arch/x86/lib/memcpy_64.S" | 6 | #include "../../../arch/x86/lib/memcpy_64.S" |
7 | /* | ||
8 | * We need to provide note.GNU-stack section, saying that we want | ||
9 | * NOT executable stack. Otherwise the final linking will assume that | ||
10 | * the ELF stack should not be restricted at all and set it RWX. | ||
11 | */ | ||
12 | .section .note.GNU-stack,"",@progbits | ||
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c index db82021f4b91..71557225bf92 100644 --- a/tools/perf/bench/mem-memcpy.c +++ b/tools/perf/bench/mem-memcpy.c | |||
@@ -5,7 +5,6 @@ | |||
5 | * | 5 | * |
6 | * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> | 6 | * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> |
7 | */ | 7 | */ |
8 | #include <ctype.h> | ||
9 | 8 | ||
10 | #include "../perf.h" | 9 | #include "../perf.h" |
11 | #include "../util/util.h" | 10 | #include "../util/util.h" |
@@ -24,6 +23,7 @@ | |||
24 | 23 | ||
25 | static const char *length_str = "1MB"; | 24 | static const char *length_str = "1MB"; |
26 | static const char *routine = "default"; | 25 | static const char *routine = "default"; |
26 | static int iterations = 1; | ||
27 | static bool use_clock; | 27 | static bool use_clock; |
28 | static int clock_fd; | 28 | static int clock_fd; |
29 | static bool only_prefault; | 29 | static bool only_prefault; |
@@ -35,6 +35,8 @@ static const struct option options[] = { | |||
35 | "available unit: B, MB, GB (upper and lower)"), | 35 | "available unit: B, MB, GB (upper and lower)"), |
36 | OPT_STRING('r', "routine", &routine, "default", | 36 | OPT_STRING('r', "routine", &routine, "default", |
37 | "Specify routine to copy"), | 37 | "Specify routine to copy"), |
38 | OPT_INTEGER('i', "iterations", &iterations, | ||
39 | "repeat memcpy() invocation this number of times"), | ||
38 | OPT_BOOLEAN('c', "clock", &use_clock, | 40 | OPT_BOOLEAN('c', "clock", &use_clock, |
39 | "Use CPU clock for measuring"), | 41 | "Use CPU clock for measuring"), |
40 | OPT_BOOLEAN('o', "only-prefault", &only_prefault, | 42 | OPT_BOOLEAN('o', "only-prefault", &only_prefault, |
@@ -121,6 +123,7 @@ static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault) | |||
121 | { | 123 | { |
122 | u64 clock_start = 0ULL, clock_end = 0ULL; | 124 | u64 clock_start = 0ULL, clock_end = 0ULL; |
123 | void *src = NULL, *dst = NULL; | 125 | void *src = NULL, *dst = NULL; |
126 | int i; | ||
124 | 127 | ||
125 | alloc_mem(&src, &dst, len); | 128 | alloc_mem(&src, &dst, len); |
126 | 129 | ||
@@ -128,7 +131,8 @@ static u64 do_memcpy_clock(memcpy_t fn, size_t len, bool prefault) | |||
128 | fn(dst, src, len); | 131 | fn(dst, src, len); |
129 | 132 | ||
130 | clock_start = get_clock(); | 133 | clock_start = get_clock(); |
131 | fn(dst, src, len); | 134 | for (i = 0; i < iterations; ++i) |
135 | fn(dst, src, len); | ||
132 | clock_end = get_clock(); | 136 | clock_end = get_clock(); |
133 | 137 | ||
134 | free(src); | 138 | free(src); |
@@ -140,6 +144,7 @@ static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault) | |||
140 | { | 144 | { |
141 | struct timeval tv_start, tv_end, tv_diff; | 145 | struct timeval tv_start, tv_end, tv_diff; |
142 | void *src = NULL, *dst = NULL; | 146 | void *src = NULL, *dst = NULL; |
147 | int i; | ||
143 | 148 | ||
144 | alloc_mem(&src, &dst, len); | 149 | alloc_mem(&src, &dst, len); |
145 | 150 | ||
@@ -147,7 +152,8 @@ static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault) | |||
147 | fn(dst, src, len); | 152 | fn(dst, src, len); |
148 | 153 | ||
149 | BUG_ON(gettimeofday(&tv_start, NULL)); | 154 | BUG_ON(gettimeofday(&tv_start, NULL)); |
150 | fn(dst, src, len); | 155 | for (i = 0; i < iterations; ++i) |
156 | fn(dst, src, len); | ||
151 | BUG_ON(gettimeofday(&tv_end, NULL)); | 157 | BUG_ON(gettimeofday(&tv_end, NULL)); |
152 | 158 | ||
153 | timersub(&tv_end, &tv_start, &tv_diff); | 159 | timersub(&tv_end, &tv_start, &tv_diff); |
diff --git a/tools/perf/bench/mem-memset-arch.h b/tools/perf/bench/mem-memset-arch.h new file mode 100644 index 000000000000..a040fa77665b --- /dev/null +++ b/tools/perf/bench/mem-memset-arch.h | |||
@@ -0,0 +1,12 @@ | |||
1 | |||
2 | #ifdef ARCH_X86_64 | ||
3 | |||
4 | #define MEMSET_FN(fn, name, desc) \ | ||
5 | extern void *fn(void *, int, size_t); | ||
6 | |||
7 | #include "mem-memset-x86-64-asm-def.h" | ||
8 | |||
9 | #undef MEMSET_FN | ||
10 | |||
11 | #endif | ||
12 | |||
diff --git a/tools/perf/bench/mem-memset-x86-64-asm-def.h b/tools/perf/bench/mem-memset-x86-64-asm-def.h new file mode 100644 index 000000000000..a71dff97c1f5 --- /dev/null +++ b/tools/perf/bench/mem-memset-x86-64-asm-def.h | |||
@@ -0,0 +1,12 @@ | |||
1 | |||
2 | MEMSET_FN(__memset, | ||
3 | "x86-64-unrolled", | ||
4 | "unrolled memset() in arch/x86/lib/memset_64.S") | ||
5 | |||
6 | MEMSET_FN(memset_c, | ||
7 | "x86-64-stosq", | ||
8 | "movsq-based memset() in arch/x86/lib/memset_64.S") | ||
9 | |||
10 | MEMSET_FN(memset_c_e, | ||
11 | "x86-64-stosb", | ||
12 | "movsb-based memset() in arch/x86/lib/memset_64.S") | ||
diff --git a/tools/perf/bench/mem-memset-x86-64-asm.S b/tools/perf/bench/mem-memset-x86-64-asm.S new file mode 100644 index 000000000000..9e5af89ed13a --- /dev/null +++ b/tools/perf/bench/mem-memset-x86-64-asm.S | |||
@@ -0,0 +1,13 @@ | |||
1 | #define memset MEMSET /* don't hide glibc's memset() */ | ||
2 | #define altinstr_replacement text | ||
3 | #define globl p2align 4; .globl | ||
4 | #define Lmemset_c globl memset_c; memset_c | ||
5 | #define Lmemset_c_e globl memset_c_e; memset_c_e | ||
6 | #include "../../../arch/x86/lib/memset_64.S" | ||
7 | |||
8 | /* | ||
9 | * We need to provide note.GNU-stack section, saying that we want | ||
10 | * NOT executable stack. Otherwise the final linking will assume that | ||
11 | * the ELF stack should not be restricted at all and set it RWX. | ||
12 | */ | ||
13 | .section .note.GNU-stack,"",@progbits | ||
diff --git a/tools/perf/bench/mem-memset.c b/tools/perf/bench/mem-memset.c new file mode 100644 index 000000000000..e9079185bd72 --- /dev/null +++ b/tools/perf/bench/mem-memset.c | |||
@@ -0,0 +1,297 @@ | |||
1 | /* | ||
2 | * mem-memset.c | ||
3 | * | ||
4 | * memset: Simple memory set in various ways | ||
5 | * | ||
6 | * Trivial clone of mem-memcpy.c. | ||
7 | */ | ||
8 | |||
9 | #include "../perf.h" | ||
10 | #include "../util/util.h" | ||
11 | #include "../util/parse-options.h" | ||
12 | #include "../util/header.h" | ||
13 | #include "bench.h" | ||
14 | #include "mem-memset-arch.h" | ||
15 | |||
16 | #include <stdio.h> | ||
17 | #include <stdlib.h> | ||
18 | #include <string.h> | ||
19 | #include <sys/time.h> | ||
20 | #include <errno.h> | ||
21 | |||
22 | #define K 1024 | ||
23 | |||
24 | static const char *length_str = "1MB"; | ||
25 | static const char *routine = "default"; | ||
26 | static int iterations = 1; | ||
27 | static bool use_clock; | ||
28 | static int clock_fd; | ||
29 | static bool only_prefault; | ||
30 | static bool no_prefault; | ||
31 | |||
32 | static const struct option options[] = { | ||
33 | OPT_STRING('l', "length", &length_str, "1MB", | ||
34 | "Specify length of memory to copy. " | ||
35 | "available unit: B, MB, GB (upper and lower)"), | ||
36 | OPT_STRING('r', "routine", &routine, "default", | ||
37 | "Specify routine to copy"), | ||
38 | OPT_INTEGER('i', "iterations", &iterations, | ||
39 | "repeat memset() invocation this number of times"), | ||
40 | OPT_BOOLEAN('c', "clock", &use_clock, | ||
41 | "Use CPU clock for measuring"), | ||
42 | OPT_BOOLEAN('o', "only-prefault", &only_prefault, | ||
43 | "Show only the result with page faults before memset()"), | ||
44 | OPT_BOOLEAN('n', "no-prefault", &no_prefault, | ||
45 | "Show only the result without page faults before memset()"), | ||
46 | OPT_END() | ||
47 | }; | ||
48 | |||
49 | typedef void *(*memset_t)(void *, int, size_t); | ||
50 | |||
51 | struct routine { | ||
52 | const char *name; | ||
53 | const char *desc; | ||
54 | memset_t fn; | ||
55 | }; | ||
56 | |||
57 | static const struct routine routines[] = { | ||
58 | { "default", | ||
59 | "Default memset() provided by glibc", | ||
60 | memset }, | ||
61 | #ifdef ARCH_X86_64 | ||
62 | |||
63 | #define MEMSET_FN(fn, name, desc) { name, desc, fn }, | ||
64 | #include "mem-memset-x86-64-asm-def.h" | ||
65 | #undef MEMSET_FN | ||
66 | |||
67 | #endif | ||
68 | |||
69 | { NULL, | ||
70 | NULL, | ||
71 | NULL } | ||
72 | }; | ||
73 | |||
74 | static const char * const bench_mem_memset_usage[] = { | ||
75 | "perf bench mem memset <options>", | ||
76 | NULL | ||
77 | }; | ||
78 | |||
79 | static struct perf_event_attr clock_attr = { | ||
80 | .type = PERF_TYPE_HARDWARE, | ||
81 | .config = PERF_COUNT_HW_CPU_CYCLES | ||
82 | }; | ||
83 | |||
84 | static void init_clock(void) | ||
85 | { | ||
86 | clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0); | ||
87 | |||
88 | if (clock_fd < 0 && errno == ENOSYS) | ||
89 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | ||
90 | else | ||
91 | BUG_ON(clock_fd < 0); | ||
92 | } | ||
93 | |||
94 | static u64 get_clock(void) | ||
95 | { | ||
96 | int ret; | ||
97 | u64 clk; | ||
98 | |||
99 | ret = read(clock_fd, &clk, sizeof(u64)); | ||
100 | BUG_ON(ret != sizeof(u64)); | ||
101 | |||
102 | return clk; | ||
103 | } | ||
104 | |||
105 | static double timeval2double(struct timeval *ts) | ||
106 | { | ||
107 | return (double)ts->tv_sec + | ||
108 | (double)ts->tv_usec / (double)1000000; | ||
109 | } | ||
110 | |||
111 | static void alloc_mem(void **dst, size_t length) | ||
112 | { | ||
113 | *dst = zalloc(length); | ||
114 | if (!dst) | ||
115 | die("memory allocation failed - maybe length is too large?\n"); | ||
116 | } | ||
117 | |||
118 | static u64 do_memset_clock(memset_t fn, size_t len, bool prefault) | ||
119 | { | ||
120 | u64 clock_start = 0ULL, clock_end = 0ULL; | ||
121 | void *dst = NULL; | ||
122 | int i; | ||
123 | |||
124 | alloc_mem(&dst, len); | ||
125 | |||
126 | if (prefault) | ||
127 | fn(dst, -1, len); | ||
128 | |||
129 | clock_start = get_clock(); | ||
130 | for (i = 0; i < iterations; ++i) | ||
131 | fn(dst, i, len); | ||
132 | clock_end = get_clock(); | ||
133 | |||
134 | free(dst); | ||
135 | return clock_end - clock_start; | ||
136 | } | ||
137 | |||
138 | static double do_memset_gettimeofday(memset_t fn, size_t len, bool prefault) | ||
139 | { | ||
140 | struct timeval tv_start, tv_end, tv_diff; | ||
141 | void *dst = NULL; | ||
142 | int i; | ||
143 | |||
144 | alloc_mem(&dst, len); | ||
145 | |||
146 | if (prefault) | ||
147 | fn(dst, -1, len); | ||
148 | |||
149 | BUG_ON(gettimeofday(&tv_start, NULL)); | ||
150 | for (i = 0; i < iterations; ++i) | ||
151 | fn(dst, i, len); | ||
152 | BUG_ON(gettimeofday(&tv_end, NULL)); | ||
153 | |||
154 | timersub(&tv_end, &tv_start, &tv_diff); | ||
155 | |||
156 | free(dst); | ||
157 | return (double)((double)len / timeval2double(&tv_diff)); | ||
158 | } | ||
159 | |||
160 | #define pf (no_prefault ? 0 : 1) | ||
161 | |||
162 | #define print_bps(x) do { \ | ||
163 | if (x < K) \ | ||
164 | printf(" %14lf B/Sec", x); \ | ||
165 | else if (x < K * K) \ | ||
166 | printf(" %14lfd KB/Sec", x / K); \ | ||
167 | else if (x < K * K * K) \ | ||
168 | printf(" %14lf MB/Sec", x / K / K); \ | ||
169 | else \ | ||
170 | printf(" %14lf GB/Sec", x / K / K / K); \ | ||
171 | } while (0) | ||
172 | |||
173 | int bench_mem_memset(int argc, const char **argv, | ||
174 | const char *prefix __used) | ||
175 | { | ||
176 | int i; | ||
177 | size_t len; | ||
178 | double result_bps[2]; | ||
179 | u64 result_clock[2]; | ||
180 | |||
181 | argc = parse_options(argc, argv, options, | ||
182 | bench_mem_memset_usage, 0); | ||
183 | |||
184 | if (use_clock) | ||
185 | init_clock(); | ||
186 | |||
187 | len = (size_t)perf_atoll((char *)length_str); | ||
188 | |||
189 | result_clock[0] = result_clock[1] = 0ULL; | ||
190 | result_bps[0] = result_bps[1] = 0.0; | ||
191 | |||
192 | if ((s64)len <= 0) { | ||
193 | fprintf(stderr, "Invalid length:%s\n", length_str); | ||
194 | return 1; | ||
195 | } | ||
196 | |||
197 | /* same to without specifying either of prefault and no-prefault */ | ||
198 | if (only_prefault && no_prefault) | ||
199 | only_prefault = no_prefault = false; | ||
200 | |||
201 | for (i = 0; routines[i].name; i++) { | ||
202 | if (!strcmp(routines[i].name, routine)) | ||
203 | break; | ||
204 | } | ||
205 | if (!routines[i].name) { | ||
206 | printf("Unknown routine:%s\n", routine); | ||
207 | printf("Available routines...\n"); | ||
208 | for (i = 0; routines[i].name; i++) { | ||
209 | printf("\t%s ... %s\n", | ||
210 | routines[i].name, routines[i].desc); | ||
211 | } | ||
212 | return 1; | ||
213 | } | ||
214 | |||
215 | if (bench_format == BENCH_FORMAT_DEFAULT) | ||
216 | printf("# Copying %s Bytes ...\n\n", length_str); | ||
217 | |||
218 | if (!only_prefault && !no_prefault) { | ||
219 | /* show both of results */ | ||
220 | if (use_clock) { | ||
221 | result_clock[0] = | ||
222 | do_memset_clock(routines[i].fn, len, false); | ||
223 | result_clock[1] = | ||
224 | do_memset_clock(routines[i].fn, len, true); | ||
225 | } else { | ||
226 | result_bps[0] = | ||
227 | do_memset_gettimeofday(routines[i].fn, | ||
228 | len, false); | ||
229 | result_bps[1] = | ||
230 | do_memset_gettimeofday(routines[i].fn, | ||
231 | len, true); | ||
232 | } | ||
233 | } else { | ||
234 | if (use_clock) { | ||
235 | result_clock[pf] = | ||
236 | do_memset_clock(routines[i].fn, | ||
237 | len, only_prefault); | ||
238 | } else { | ||
239 | result_bps[pf] = | ||
240 | do_memset_gettimeofday(routines[i].fn, | ||
241 | len, only_prefault); | ||
242 | } | ||
243 | } | ||
244 | |||
245 | switch (bench_format) { | ||
246 | case BENCH_FORMAT_DEFAULT: | ||
247 | if (!only_prefault && !no_prefault) { | ||
248 | if (use_clock) { | ||
249 | printf(" %14lf Clock/Byte\n", | ||
250 | (double)result_clock[0] | ||
251 | / (double)len); | ||
252 | printf(" %14lf Clock/Byte (with prefault)\n ", | ||
253 | (double)result_clock[1] | ||
254 | / (double)len); | ||
255 | } else { | ||
256 | print_bps(result_bps[0]); | ||
257 | printf("\n"); | ||
258 | print_bps(result_bps[1]); | ||
259 | printf(" (with prefault)\n"); | ||
260 | } | ||
261 | } else { | ||
262 | if (use_clock) { | ||
263 | printf(" %14lf Clock/Byte", | ||
264 | (double)result_clock[pf] | ||
265 | / (double)len); | ||
266 | } else | ||
267 | print_bps(result_bps[pf]); | ||
268 | |||
269 | printf("%s\n", only_prefault ? " (with prefault)" : ""); | ||
270 | } | ||
271 | break; | ||
272 | case BENCH_FORMAT_SIMPLE: | ||
273 | if (!only_prefault && !no_prefault) { | ||
274 | if (use_clock) { | ||
275 | printf("%lf %lf\n", | ||
276 | (double)result_clock[0] / (double)len, | ||
277 | (double)result_clock[1] / (double)len); | ||
278 | } else { | ||
279 | printf("%lf %lf\n", | ||
280 | result_bps[0], result_bps[1]); | ||
281 | } | ||
282 | } else { | ||
283 | if (use_clock) { | ||
284 | printf("%lf\n", (double)result_clock[pf] | ||
285 | / (double)len); | ||
286 | } else | ||
287 | printf("%lf\n", result_bps[pf]); | ||
288 | } | ||
289 | break; | ||
290 | default: | ||
291 | /* reaching this means there's some disaster: */ | ||
292 | die("unknown format: %d\n", bench_format); | ||
293 | break; | ||
294 | } | ||
295 | |||
296 | return 0; | ||
297 | } | ||
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 214ba7f9f577..806e0a286634 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
@@ -235,7 +235,7 @@ out_delete: | |||
235 | } | 235 | } |
236 | 236 | ||
237 | static const char * const annotate_usage[] = { | 237 | static const char * const annotate_usage[] = { |
238 | "perf annotate [<options>] <command>", | 238 | "perf annotate [<options>]", |
239 | NULL | 239 | NULL |
240 | }; | 240 | }; |
241 | 241 | ||
@@ -313,10 +313,5 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used) | |||
313 | annotate.sym_hist_filter = argv[0]; | 313 | annotate.sym_hist_filter = argv[0]; |
314 | } | 314 | } |
315 | 315 | ||
316 | if (field_sep && *field_sep == '.') { | ||
317 | pr_err("'.' is the only non valid --field-separator argument\n"); | ||
318 | return -1; | ||
319 | } | ||
320 | |||
321 | return __cmd_annotate(&annotate); | 316 | return __cmd_annotate(&annotate); |
322 | } | 317 | } |
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c index fcb96269852a..b0e74ab2d7a2 100644 --- a/tools/perf/builtin-bench.c +++ b/tools/perf/builtin-bench.c | |||
@@ -52,6 +52,9 @@ static struct bench_suite mem_suites[] = { | |||
52 | { "memcpy", | 52 | { "memcpy", |
53 | "Simple memory copy in various ways", | 53 | "Simple memory copy in various ways", |
54 | bench_mem_memcpy }, | 54 | bench_mem_memcpy }, |
55 | { "memset", | ||
56 | "Simple memory set in various ways", | ||
57 | bench_mem_memset }, | ||
55 | suite_all, | 58 | suite_all, |
56 | { NULL, | 59 | { NULL, |
57 | NULL, | 60 | NULL, |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 4f19513d7dda..d29d350fb2b7 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -24,6 +24,11 @@ static char diff__default_sort_order[] = "dso,symbol"; | |||
24 | static bool force; | 24 | static bool force; |
25 | static bool show_displacement; | 25 | static bool show_displacement; |
26 | 26 | ||
27 | struct perf_diff { | ||
28 | struct perf_tool tool; | ||
29 | struct perf_session *session; | ||
30 | }; | ||
31 | |||
27 | static int hists__add_entry(struct hists *self, | 32 | static int hists__add_entry(struct hists *self, |
28 | struct addr_location *al, u64 period) | 33 | struct addr_location *al, u64 period) |
29 | { | 34 | { |
@@ -32,12 +37,14 @@ static int hists__add_entry(struct hists *self, | |||
32 | return -ENOMEM; | 37 | return -ENOMEM; |
33 | } | 38 | } |
34 | 39 | ||
35 | static int diff__process_sample_event(struct perf_tool *tool __used, | 40 | static int diff__process_sample_event(struct perf_tool *tool, |
36 | union perf_event *event, | 41 | union perf_event *event, |
37 | struct perf_sample *sample, | 42 | struct perf_sample *sample, |
38 | struct perf_evsel *evsel __used, | 43 | struct perf_evsel *evsel __used, |
39 | struct machine *machine) | 44 | struct machine *machine) |
40 | { | 45 | { |
46 | struct perf_diff *_diff = container_of(tool, struct perf_diff, tool); | ||
47 | struct perf_session *session = _diff->session; | ||
41 | struct addr_location al; | 48 | struct addr_location al; |
42 | 49 | ||
43 | if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) { | 50 | if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) { |
@@ -49,24 +56,26 @@ static int diff__process_sample_event(struct perf_tool *tool __used, | |||
49 | if (al.filtered || al.sym == NULL) | 56 | if (al.filtered || al.sym == NULL) |
50 | return 0; | 57 | return 0; |
51 | 58 | ||
52 | if (hists__add_entry(&evsel->hists, &al, sample->period)) { | 59 | if (hists__add_entry(&session->hists, &al, sample->period)) { |
53 | pr_warning("problem incrementing symbol period, skipping event\n"); | 60 | pr_warning("problem incrementing symbol period, skipping event\n"); |
54 | return -1; | 61 | return -1; |
55 | } | 62 | } |
56 | 63 | ||
57 | evsel->hists.stats.total_period += sample->period; | 64 | session->hists.stats.total_period += sample->period; |
58 | return 0; | 65 | return 0; |
59 | } | 66 | } |
60 | 67 | ||
61 | static struct perf_tool perf_diff = { | 68 | static struct perf_diff diff = { |
62 | .sample = diff__process_sample_event, | 69 | .tool = { |
63 | .mmap = perf_event__process_mmap, | 70 | .sample = diff__process_sample_event, |
64 | .comm = perf_event__process_comm, | 71 | .mmap = perf_event__process_mmap, |
65 | .exit = perf_event__process_task, | 72 | .comm = perf_event__process_comm, |
66 | .fork = perf_event__process_task, | 73 | .exit = perf_event__process_task, |
67 | .lost = perf_event__process_lost, | 74 | .fork = perf_event__process_task, |
68 | .ordered_samples = true, | 75 | .lost = perf_event__process_lost, |
69 | .ordering_requires_timestamps = true, | 76 | .ordered_samples = true, |
77 | .ordering_requires_timestamps = true, | ||
78 | }, | ||
70 | }; | 79 | }; |
71 | 80 | ||
72 | static void perf_session__insert_hist_entry_by_name(struct rb_root *root, | 81 | static void perf_session__insert_hist_entry_by_name(struct rb_root *root, |
@@ -107,12 +116,6 @@ static void hists__resort_entries(struct hists *self) | |||
107 | self->entries = tmp; | 116 | self->entries = tmp; |
108 | } | 117 | } |
109 | 118 | ||
110 | static void hists__set_positions(struct hists *self) | ||
111 | { | ||
112 | hists__output_resort(self); | ||
113 | hists__resort_entries(self); | ||
114 | } | ||
115 | |||
116 | static struct hist_entry *hists__find_entry(struct hists *self, | 119 | static struct hist_entry *hists__find_entry(struct hists *self, |
117 | struct hist_entry *he) | 120 | struct hist_entry *he) |
118 | { | 121 | { |
@@ -146,30 +149,37 @@ static void hists__match(struct hists *older, struct hists *newer) | |||
146 | static int __cmd_diff(void) | 149 | static int __cmd_diff(void) |
147 | { | 150 | { |
148 | int ret, i; | 151 | int ret, i; |
152 | #define older (session[0]) | ||
153 | #define newer (session[1]) | ||
149 | struct perf_session *session[2]; | 154 | struct perf_session *session[2]; |
150 | 155 | ||
151 | session[0] = perf_session__new(input_old, O_RDONLY, force, false, &perf_diff); | 156 | older = perf_session__new(input_old, O_RDONLY, force, false, |
152 | session[1] = perf_session__new(input_new, O_RDONLY, force, false, &perf_diff); | 157 | &diff.tool); |
158 | newer = perf_session__new(input_new, O_RDONLY, force, false, | ||
159 | &diff.tool); | ||
153 | if (session[0] == NULL || session[1] == NULL) | 160 | if (session[0] == NULL || session[1] == NULL) |
154 | return -ENOMEM; | 161 | return -ENOMEM; |
155 | 162 | ||
156 | for (i = 0; i < 2; ++i) { | 163 | for (i = 0; i < 2; ++i) { |
157 | ret = perf_session__process_events(session[i], &perf_diff); | 164 | diff.session = session[i]; |
165 | ret = perf_session__process_events(session[i], &diff.tool); | ||
158 | if (ret) | 166 | if (ret) |
159 | goto out_delete; | 167 | goto out_delete; |
168 | hists__output_resort(&session[i]->hists); | ||
160 | } | 169 | } |
161 | 170 | ||
162 | hists__output_resort(&session[1]->hists); | ||
163 | if (show_displacement) | 171 | if (show_displacement) |
164 | hists__set_positions(&session[0]->hists); | 172 | hists__resort_entries(&older->hists); |
165 | 173 | ||
166 | hists__match(&session[0]->hists, &session[1]->hists); | 174 | hists__match(&older->hists, &newer->hists); |
167 | hists__fprintf(&session[1]->hists, &session[0]->hists, | 175 | hists__fprintf(&newer->hists, &older->hists, |
168 | show_displacement, true, 0, 0, stdout); | 176 | show_displacement, true, 0, 0, stdout); |
169 | out_delete: | 177 | out_delete: |
170 | for (i = 0; i < 2; ++i) | 178 | for (i = 0; i < 2; ++i) |
171 | perf_session__delete(session[i]); | 179 | perf_session__delete(session[i]); |
172 | return ret; | 180 | return ret; |
181 | #undef older | ||
182 | #undef newer | ||
173 | } | 183 | } |
174 | 184 | ||
175 | static const char * const diff_usage[] = { | 185 | static const char * const diff_usage[] = { |
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index fe1ad8f21961..39104c0beea3 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
@@ -108,7 +108,9 @@ static void setup_cpunode_map(void) | |||
108 | continue; | 108 | continue; |
109 | cpunode_map[cpu] = mem; | 109 | cpunode_map[cpu] = mem; |
110 | } | 110 | } |
111 | closedir(dir2); | ||
111 | } | 112 | } |
113 | closedir(dir1); | ||
112 | } | 114 | } |
113 | 115 | ||
114 | static void insert_alloc_stat(unsigned long call_site, unsigned long ptr, | 116 | static void insert_alloc_stat(unsigned long call_site, unsigned long ptr, |
@@ -645,6 +647,7 @@ static int setup_sorting(struct list_head *sort_list, const char *arg) | |||
645 | break; | 647 | break; |
646 | if (sort_dimension__add(tok, sort_list) < 0) { | 648 | if (sort_dimension__add(tok, sort_list) < 0) { |
647 | error("Unknown --sort key: '%s'", tok); | 649 | error("Unknown --sort key: '%s'", tok); |
650 | free(str); | ||
648 | return -1; | 651 | return -1; |
649 | } | 652 | } |
650 | } | 653 | } |
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 032324a76b87..9fc6e0fa3dce 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
@@ -22,9 +22,6 @@ | |||
22 | static const char *file_name; | 22 | static const char *file_name; |
23 | static char name_buffer[256]; | 23 | static char name_buffer[256]; |
24 | 24 | ||
25 | bool perf_host = 1; | ||
26 | bool perf_guest; | ||
27 | |||
28 | static const char * const kvm_usage[] = { | 25 | static const char * const kvm_usage[] = { |
29 | "perf kvm [<options>] {top|record|report|diff|buildid-list}", | 26 | "perf kvm [<options>] {top|record|report|diff|buildid-list}", |
30 | NULL | 27 | NULL |
@@ -107,7 +104,8 @@ static int __cmd_buildid_list(int argc, const char **argv) | |||
107 | 104 | ||
108 | int cmd_kvm(int argc, const char **argv, const char *prefix __used) | 105 | int cmd_kvm(int argc, const char **argv, const char *prefix __used) |
109 | { | 106 | { |
110 | perf_host = perf_guest = 0; | 107 | perf_host = 0; |
108 | perf_guest = 1; | ||
111 | 109 | ||
112 | argc = parse_options(argc, argv, kvm_options, kvm_usage, | 110 | argc = parse_options(argc, argv, kvm_options, kvm_usage, |
113 | PARSE_OPT_STOP_AT_NON_OPTION); | 111 | PARSE_OPT_STOP_AT_NON_OPTION); |
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 2296c391d0f5..12c814838993 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
@@ -922,12 +922,12 @@ static const struct option info_options[] = { | |||
922 | OPT_BOOLEAN('t', "threads", &info_threads, | 922 | OPT_BOOLEAN('t', "threads", &info_threads, |
923 | "dump thread list in perf.data"), | 923 | "dump thread list in perf.data"), |
924 | OPT_BOOLEAN('m', "map", &info_map, | 924 | OPT_BOOLEAN('m', "map", &info_map, |
925 | "map of lock instances (name:address table)"), | 925 | "map of lock instances (address:name table)"), |
926 | OPT_END() | 926 | OPT_END() |
927 | }; | 927 | }; |
928 | 928 | ||
929 | static const char * const lock_usage[] = { | 929 | static const char * const lock_usage[] = { |
930 | "perf lock [<options>] {record|trace|report}", | 930 | "perf lock [<options>] {record|report|script|info}", |
931 | NULL | 931 | NULL |
932 | }; | 932 | }; |
933 | 933 | ||
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 59d43abfbfec..4935c09dd5b5 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -20,7 +20,6 @@ | |||
20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 20 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21 | * | 21 | * |
22 | */ | 22 | */ |
23 | #define _GNU_SOURCE | ||
24 | #include <sys/utsname.h> | 23 | #include <sys/utsname.h> |
25 | #include <sys/types.h> | 24 | #include <sys/types.h> |
26 | #include <sys/stat.h> | 25 | #include <sys/stat.h> |
@@ -31,7 +30,6 @@ | |||
31 | #include <stdlib.h> | 30 | #include <stdlib.h> |
32 | #include <string.h> | 31 | #include <string.h> |
33 | 32 | ||
34 | #undef _GNU_SOURCE | ||
35 | #include "perf.h" | 33 | #include "perf.h" |
36 | #include "builtin.h" | 34 | #include "builtin.h" |
37 | #include "util/util.h" | 35 | #include "util/util.h" |
@@ -60,7 +58,7 @@ static struct { | |||
60 | struct perf_probe_event events[MAX_PROBES]; | 58 | struct perf_probe_event events[MAX_PROBES]; |
61 | struct strlist *dellist; | 59 | struct strlist *dellist; |
62 | struct line_range line_range; | 60 | struct line_range line_range; |
63 | const char *target_module; | 61 | const char *target; |
64 | int max_probe_points; | 62 | int max_probe_points; |
65 | struct strfilter *filter; | 63 | struct strfilter *filter; |
66 | } params; | 64 | } params; |
@@ -248,7 +246,7 @@ static const struct option options[] = { | |||
248 | "file", "vmlinux pathname"), | 246 | "file", "vmlinux pathname"), |
249 | OPT_STRING('s', "source", &symbol_conf.source_prefix, | 247 | OPT_STRING('s', "source", &symbol_conf.source_prefix, |
250 | "directory", "path to kernel source"), | 248 | "directory", "path to kernel source"), |
251 | OPT_STRING('m', "module", ¶ms.target_module, | 249 | OPT_STRING('m', "module", ¶ms.target, |
252 | "modname|path", | 250 | "modname|path", |
253 | "target module name (for online) or path (for offline)"), | 251 | "target module name (for online) or path (for offline)"), |
254 | #endif | 252 | #endif |
@@ -335,7 +333,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
335 | if (!params.filter) | 333 | if (!params.filter) |
336 | params.filter = strfilter__new(DEFAULT_FUNC_FILTER, | 334 | params.filter = strfilter__new(DEFAULT_FUNC_FILTER, |
337 | NULL); | 335 | NULL); |
338 | ret = show_available_funcs(params.target_module, | 336 | ret = show_available_funcs(params.target, |
339 | params.filter); | 337 | params.filter); |
340 | strfilter__delete(params.filter); | 338 | strfilter__delete(params.filter); |
341 | if (ret < 0) | 339 | if (ret < 0) |
@@ -356,7 +354,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
356 | usage_with_options(probe_usage, options); | 354 | usage_with_options(probe_usage, options); |
357 | } | 355 | } |
358 | 356 | ||
359 | ret = show_line_range(¶ms.line_range, params.target_module); | 357 | ret = show_line_range(¶ms.line_range, params.target); |
360 | if (ret < 0) | 358 | if (ret < 0) |
361 | pr_err(" Error: Failed to show lines. (%d)\n", ret); | 359 | pr_err(" Error: Failed to show lines. (%d)\n", ret); |
362 | return ret; | 360 | return ret; |
@@ -373,7 +371,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
373 | 371 | ||
374 | ret = show_available_vars(params.events, params.nevents, | 372 | ret = show_available_vars(params.events, params.nevents, |
375 | params.max_probe_points, | 373 | params.max_probe_points, |
376 | params.target_module, | 374 | params.target, |
377 | params.filter, | 375 | params.filter, |
378 | params.show_ext_vars); | 376 | params.show_ext_vars); |
379 | strfilter__delete(params.filter); | 377 | strfilter__delete(params.filter); |
@@ -395,7 +393,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
395 | if (params.nevents) { | 393 | if (params.nevents) { |
396 | ret = add_perf_probe_events(params.events, params.nevents, | 394 | ret = add_perf_probe_events(params.events, params.nevents, |
397 | params.max_probe_points, | 395 | params.max_probe_points, |
398 | params.target_module, | 396 | params.target, |
399 | params.force_add); | 397 | params.force_add); |
400 | if (ret < 0) { | 398 | if (ret < 0) { |
401 | pr_err(" Error: Failed to add events. (%d)\n", ret); | 399 | pr_err(" Error: Failed to add events. (%d)\n", ret); |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 0abfb18b911f..be4e1eee782e 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -44,6 +44,7 @@ struct perf_record { | |||
44 | struct perf_evlist *evlist; | 44 | struct perf_evlist *evlist; |
45 | struct perf_session *session; | 45 | struct perf_session *session; |
46 | const char *progname; | 46 | const char *progname; |
47 | const char *uid_str; | ||
47 | int output; | 48 | int output; |
48 | unsigned int page_size; | 49 | unsigned int page_size; |
49 | int realtime_prio; | 50 | int realtime_prio; |
@@ -204,8 +205,11 @@ static void perf_record__open(struct perf_record *rec) | |||
204 | 205 | ||
205 | if (opts->group && pos != first) | 206 | if (opts->group && pos != first) |
206 | group_fd = first->fd; | 207 | group_fd = first->fd; |
208 | fallback_missing_features: | ||
209 | if (opts->exclude_guest_missing) | ||
210 | attr->exclude_guest = attr->exclude_host = 0; | ||
207 | retry_sample_id: | 211 | retry_sample_id: |
208 | attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0; | 212 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; |
209 | try_again: | 213 | try_again: |
210 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, | 214 | if (perf_evsel__open(pos, evlist->cpus, evlist->threads, |
211 | opts->group, group_fd) < 0) { | 215 | opts->group, group_fd) < 0) { |
@@ -217,15 +221,23 @@ try_again: | |||
217 | } else if (err == ENODEV && opts->cpu_list) { | 221 | } else if (err == ENODEV && opts->cpu_list) { |
218 | die("No such device - did you specify" | 222 | die("No such device - did you specify" |
219 | " an out-of-range profile CPU?\n"); | 223 | " an out-of-range profile CPU?\n"); |
220 | } else if (err == EINVAL && opts->sample_id_all_avail) { | 224 | } else if (err == EINVAL) { |
221 | /* | 225 | if (!opts->exclude_guest_missing && |
222 | * Old kernel, no attr->sample_id_type_all field | 226 | (attr->exclude_guest || attr->exclude_host)) { |
223 | */ | 227 | pr_debug("Old kernel, cannot exclude " |
224 | opts->sample_id_all_avail = false; | 228 | "guest or host samples.\n"); |
225 | if (!opts->sample_time && !opts->raw_samples && !time_needed) | 229 | opts->exclude_guest_missing = true; |
226 | attr->sample_type &= ~PERF_SAMPLE_TIME; | 230 | goto fallback_missing_features; |
227 | 231 | } else if (!opts->sample_id_all_missing) { | |
228 | goto retry_sample_id; | 232 | /* |
233 | * Old kernel, no attr->sample_id_type_all field | ||
234 | */ | ||
235 | opts->sample_id_all_missing = true; | ||
236 | if (!opts->sample_time && !opts->raw_samples && !time_needed) | ||
237 | attr->sample_type &= ~PERF_SAMPLE_TIME; | ||
238 | |||
239 | goto retry_sample_id; | ||
240 | } | ||
229 | } | 241 | } |
230 | 242 | ||
231 | /* | 243 | /* |
@@ -385,7 +397,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
385 | { | 397 | { |
386 | struct stat st; | 398 | struct stat st; |
387 | int flags; | 399 | int flags; |
388 | int err, output; | 400 | int err, output, feat; |
389 | unsigned long waking = 0; | 401 | unsigned long waking = 0; |
390 | const bool forks = argc > 0; | 402 | const bool forks = argc > 0; |
391 | struct machine *machine; | 403 | struct machine *machine; |
@@ -452,8 +464,17 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
452 | 464 | ||
453 | rec->session = session; | 465 | rec->session = session; |
454 | 466 | ||
455 | if (!rec->no_buildid) | 467 | for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++) |
456 | perf_header__set_feat(&session->header, HEADER_BUILD_ID); | 468 | perf_header__set_feat(&session->header, feat); |
469 | |||
470 | if (rec->no_buildid) | ||
471 | perf_header__clear_feat(&session->header, HEADER_BUILD_ID); | ||
472 | |||
473 | if (!have_tracepoints(&evsel_list->entries)) | ||
474 | perf_header__clear_feat(&session->header, HEADER_TRACE_INFO); | ||
475 | |||
476 | if (!rec->opts.branch_stack) | ||
477 | perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK); | ||
457 | 478 | ||
458 | if (!rec->file_new) { | 479 | if (!rec->file_new) { |
459 | err = perf_session__read_header(session, output); | 480 | err = perf_session__read_header(session, output); |
@@ -461,22 +482,6 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
461 | goto out_delete_session; | 482 | goto out_delete_session; |
462 | } | 483 | } |
463 | 484 | ||
464 | if (have_tracepoints(&evsel_list->entries)) | ||
465 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | ||
466 | |||
467 | perf_header__set_feat(&session->header, HEADER_HOSTNAME); | ||
468 | perf_header__set_feat(&session->header, HEADER_OSRELEASE); | ||
469 | perf_header__set_feat(&session->header, HEADER_ARCH); | ||
470 | perf_header__set_feat(&session->header, HEADER_CPUDESC); | ||
471 | perf_header__set_feat(&session->header, HEADER_NRCPUS); | ||
472 | perf_header__set_feat(&session->header, HEADER_EVENT_DESC); | ||
473 | perf_header__set_feat(&session->header, HEADER_CMDLINE); | ||
474 | perf_header__set_feat(&session->header, HEADER_VERSION); | ||
475 | perf_header__set_feat(&session->header, HEADER_CPU_TOPOLOGY); | ||
476 | perf_header__set_feat(&session->header, HEADER_TOTAL_MEM); | ||
477 | perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY); | ||
478 | perf_header__set_feat(&session->header, HEADER_CPUID); | ||
479 | |||
480 | if (forks) { | 485 | if (forks) { |
481 | err = perf_evlist__prepare_workload(evsel_list, opts, argv); | 486 | err = perf_evlist__prepare_workload(evsel_list, opts, argv); |
482 | if (err < 0) { | 487 | if (err < 0) { |
@@ -503,9 +508,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
503 | return err; | 508 | return err; |
504 | } | 509 | } |
505 | 510 | ||
506 | if (!!rec->no_buildid | 511 | if (!rec->no_buildid |
507 | && !perf_header__has_feat(&session->header, HEADER_BUILD_ID)) { | 512 | && !perf_header__has_feat(&session->header, HEADER_BUILD_ID)) { |
508 | pr_err("Couldn't generating buildids. " | 513 | pr_err("Couldn't generate buildids. " |
509 | "Use --no-buildid to profile anyway.\n"); | 514 | "Use --no-buildid to profile anyway.\n"); |
510 | return -1; | 515 | return -1; |
511 | } | 516 | } |
@@ -636,6 +641,90 @@ out_delete_session: | |||
636 | return err; | 641 | return err; |
637 | } | 642 | } |
638 | 643 | ||
644 | #define BRANCH_OPT(n, m) \ | ||
645 | { .name = n, .mode = (m) } | ||
646 | |||
647 | #define BRANCH_END { .name = NULL } | ||
648 | |||
649 | struct branch_mode { | ||
650 | const char *name; | ||
651 | int mode; | ||
652 | }; | ||
653 | |||
654 | static const struct branch_mode branch_modes[] = { | ||
655 | BRANCH_OPT("u", PERF_SAMPLE_BRANCH_USER), | ||
656 | BRANCH_OPT("k", PERF_SAMPLE_BRANCH_KERNEL), | ||
657 | BRANCH_OPT("hv", PERF_SAMPLE_BRANCH_HV), | ||
658 | BRANCH_OPT("any", PERF_SAMPLE_BRANCH_ANY), | ||
659 | BRANCH_OPT("any_call", PERF_SAMPLE_BRANCH_ANY_CALL), | ||
660 | BRANCH_OPT("any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN), | ||
661 | BRANCH_OPT("ind_call", PERF_SAMPLE_BRANCH_IND_CALL), | ||
662 | BRANCH_END | ||
663 | }; | ||
664 | |||
665 | static int | ||
666 | parse_branch_stack(const struct option *opt, const char *str, int unset) | ||
667 | { | ||
668 | #define ONLY_PLM \ | ||
669 | (PERF_SAMPLE_BRANCH_USER |\ | ||
670 | PERF_SAMPLE_BRANCH_KERNEL |\ | ||
671 | PERF_SAMPLE_BRANCH_HV) | ||
672 | |||
673 | uint64_t *mode = (uint64_t *)opt->value; | ||
674 | const struct branch_mode *br; | ||
675 | char *s, *os = NULL, *p; | ||
676 | int ret = -1; | ||
677 | |||
678 | if (unset) | ||
679 | return 0; | ||
680 | |||
681 | /* | ||
682 | * cannot set it twice, -b + --branch-filter for instance | ||
683 | */ | ||
684 | if (*mode) | ||
685 | return -1; | ||
686 | |||
687 | /* str may be NULL in case no arg is passed to -b */ | ||
688 | if (str) { | ||
689 | /* because str is read-only */ | ||
690 | s = os = strdup(str); | ||
691 | if (!s) | ||
692 | return -1; | ||
693 | |||
694 | for (;;) { | ||
695 | p = strchr(s, ','); | ||
696 | if (p) | ||
697 | *p = '\0'; | ||
698 | |||
699 | for (br = branch_modes; br->name; br++) { | ||
700 | if (!strcasecmp(s, br->name)) | ||
701 | break; | ||
702 | } | ||
703 | if (!br->name) { | ||
704 | ui__warning("unknown branch filter %s," | ||
705 | " check man page\n", s); | ||
706 | goto error; | ||
707 | } | ||
708 | |||
709 | *mode |= br->mode; | ||
710 | |||
711 | if (!p) | ||
712 | break; | ||
713 | |||
714 | s = p + 1; | ||
715 | } | ||
716 | } | ||
717 | ret = 0; | ||
718 | |||
719 | /* default to any branch */ | ||
720 | if ((*mode & ~ONLY_PLM) == 0) { | ||
721 | *mode = PERF_SAMPLE_BRANCH_ANY; | ||
722 | } | ||
723 | error: | ||
724 | free(os); | ||
725 | return ret; | ||
726 | } | ||
727 | |||
639 | static const char * const record_usage[] = { | 728 | static const char * const record_usage[] = { |
640 | "perf record [<options>] [<command>]", | 729 | "perf record [<options>] [<command>]", |
641 | "perf record [<options>] -- <command> [<options>]", | 730 | "perf record [<options>] -- <command> [<options>]", |
@@ -654,13 +743,10 @@ static const char * const record_usage[] = { | |||
654 | */ | 743 | */ |
655 | static struct perf_record record = { | 744 | static struct perf_record record = { |
656 | .opts = { | 745 | .opts = { |
657 | .target_pid = -1, | ||
658 | .target_tid = -1, | ||
659 | .mmap_pages = UINT_MAX, | 746 | .mmap_pages = UINT_MAX, |
660 | .user_freq = UINT_MAX, | 747 | .user_freq = UINT_MAX, |
661 | .user_interval = ULLONG_MAX, | 748 | .user_interval = ULLONG_MAX, |
662 | .freq = 1000, | 749 | .freq = 1000, |
663 | .sample_id_all_avail = true, | ||
664 | }, | 750 | }, |
665 | .write_mode = WRITE_FORCE, | 751 | .write_mode = WRITE_FORCE, |
666 | .file_new = true, | 752 | .file_new = true, |
@@ -679,9 +765,9 @@ const struct option record_options[] = { | |||
679 | parse_events_option), | 765 | parse_events_option), |
680 | OPT_CALLBACK(0, "filter", &record.evlist, "filter", | 766 | OPT_CALLBACK(0, "filter", &record.evlist, "filter", |
681 | "event filter", parse_filter), | 767 | "event filter", parse_filter), |
682 | OPT_INTEGER('p', "pid", &record.opts.target_pid, | 768 | OPT_STRING('p', "pid", &record.opts.target_pid, "pid", |
683 | "record events on existing process id"), | 769 | "record events on existing process id"), |
684 | OPT_INTEGER('t', "tid", &record.opts.target_tid, | 770 | OPT_STRING('t', "tid", &record.opts.target_tid, "tid", |
685 | "record events on existing thread id"), | 771 | "record events on existing thread id"), |
686 | OPT_INTEGER('r', "realtime", &record.realtime_prio, | 772 | OPT_INTEGER('r', "realtime", &record.realtime_prio, |
687 | "collect data with this RT SCHED_FIFO priority"), | 773 | "collect data with this RT SCHED_FIFO priority"), |
@@ -727,6 +813,15 @@ const struct option record_options[] = { | |||
727 | OPT_CALLBACK('G', "cgroup", &record.evlist, "name", | 813 | OPT_CALLBACK('G', "cgroup", &record.evlist, "name", |
728 | "monitor event in cgroup name only", | 814 | "monitor event in cgroup name only", |
729 | parse_cgroups), | 815 | parse_cgroups), |
816 | OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"), | ||
817 | |||
818 | OPT_CALLBACK_NOOPT('b', "branch-any", &record.opts.branch_stack, | ||
819 | "branch any", "sample any taken branches", | ||
820 | parse_branch_stack), | ||
821 | |||
822 | OPT_CALLBACK('j', "branch-filter", &record.opts.branch_stack, | ||
823 | "branch filter mask", "branch stack filter modes", | ||
824 | parse_branch_stack), | ||
730 | OPT_END() | 825 | OPT_END() |
731 | }; | 826 | }; |
732 | 827 | ||
@@ -747,8 +842,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
747 | 842 | ||
748 | argc = parse_options(argc, argv, record_options, record_usage, | 843 | argc = parse_options(argc, argv, record_options, record_usage, |
749 | PARSE_OPT_STOP_AT_NON_OPTION); | 844 | PARSE_OPT_STOP_AT_NON_OPTION); |
750 | if (!argc && rec->opts.target_pid == -1 && rec->opts.target_tid == -1 && | 845 | if (!argc && !rec->opts.target_pid && !rec->opts.target_tid && |
751 | !rec->opts.system_wide && !rec->opts.cpu_list) | 846 | !rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str) |
752 | usage_with_options(record_usage, record_options); | 847 | usage_with_options(record_usage, record_options); |
753 | 848 | ||
754 | if (rec->force && rec->append_file) { | 849 | if (rec->force && rec->append_file) { |
@@ -788,11 +883,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
788 | goto out_symbol_exit; | 883 | goto out_symbol_exit; |
789 | } | 884 | } |
790 | 885 | ||
791 | if (rec->opts.target_pid != -1) | 886 | rec->opts.uid = parse_target_uid(rec->uid_str, rec->opts.target_tid, |
887 | rec->opts.target_pid); | ||
888 | if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1) | ||
889 | goto out_free_fd; | ||
890 | |||
891 | if (rec->opts.target_pid) | ||
792 | rec->opts.target_tid = rec->opts.target_pid; | 892 | rec->opts.target_tid = rec->opts.target_pid; |
793 | 893 | ||
794 | if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid, | 894 | if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid, |
795 | rec->opts.target_tid, rec->opts.cpu_list) < 0) | 895 | rec->opts.target_tid, rec->opts.uid, |
896 | rec->opts.cpu_list) < 0) | ||
796 | usage_with_options(record_usage, record_options); | 897 | usage_with_options(record_usage, record_options); |
797 | 898 | ||
798 | list_for_each_entry(pos, &evsel_list->entries, node) { | 899 | list_for_each_entry(pos, &evsel_list->entries, node) { |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 25d34d483e49..2e317438980b 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -40,7 +40,7 @@ struct perf_report { | |||
40 | struct perf_tool tool; | 40 | struct perf_tool tool; |
41 | struct perf_session *session; | 41 | struct perf_session *session; |
42 | char const *input_name; | 42 | char const *input_name; |
43 | bool force, use_tui, use_stdio; | 43 | bool force, use_tui, use_gtk, use_stdio; |
44 | bool hide_unresolved; | 44 | bool hide_unresolved; |
45 | bool dont_use_callchains; | 45 | bool dont_use_callchains; |
46 | bool show_full_info; | 46 | bool show_full_info; |
@@ -50,9 +50,86 @@ struct perf_report { | |||
50 | const char *pretty_printing_style; | 50 | const char *pretty_printing_style; |
51 | symbol_filter_t annotate_init; | 51 | symbol_filter_t annotate_init; |
52 | const char *cpu_list; | 52 | const char *cpu_list; |
53 | const char *symbol_filter_str; | ||
53 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | 54 | DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); |
54 | }; | 55 | }; |
55 | 56 | ||
57 | static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | ||
58 | struct addr_location *al, | ||
59 | struct perf_sample *sample, | ||
60 | struct perf_evsel *evsel, | ||
61 | struct machine *machine) | ||
62 | { | ||
63 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | ||
64 | struct symbol *parent = NULL; | ||
65 | int err = 0; | ||
66 | unsigned i; | ||
67 | struct hist_entry *he; | ||
68 | struct branch_info *bi, *bx; | ||
69 | |||
70 | if ((sort__has_parent || symbol_conf.use_callchain) | ||
71 | && sample->callchain) { | ||
72 | err = machine__resolve_callchain(machine, evsel, al->thread, | ||
73 | sample->callchain, &parent); | ||
74 | if (err) | ||
75 | return err; | ||
76 | } | ||
77 | |||
78 | bi = machine__resolve_bstack(machine, al->thread, | ||
79 | sample->branch_stack); | ||
80 | if (!bi) | ||
81 | return -ENOMEM; | ||
82 | |||
83 | for (i = 0; i < sample->branch_stack->nr; i++) { | ||
84 | if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym)) | ||
85 | continue; | ||
86 | /* | ||
87 | * The report shows the percentage of total branches captured | ||
88 | * and not events sampled. Thus we use a pseudo period of 1. | ||
89 | */ | ||
90 | he = __hists__add_branch_entry(&evsel->hists, al, parent, | ||
91 | &bi[i], 1); | ||
92 | if (he) { | ||
93 | struct annotation *notes; | ||
94 | err = -ENOMEM; | ||
95 | bx = he->branch_info; | ||
96 | if (bx->from.sym && use_browser > 0) { | ||
97 | notes = symbol__annotation(bx->from.sym); | ||
98 | if (!notes->src | ||
99 | && symbol__alloc_hist(bx->from.sym) < 0) | ||
100 | goto out; | ||
101 | |||
102 | err = symbol__inc_addr_samples(bx->from.sym, | ||
103 | bx->from.map, | ||
104 | evsel->idx, | ||
105 | bx->from.al_addr); | ||
106 | if (err) | ||
107 | goto out; | ||
108 | } | ||
109 | |||
110 | if (bx->to.sym && use_browser > 0) { | ||
111 | notes = symbol__annotation(bx->to.sym); | ||
112 | if (!notes->src | ||
113 | && symbol__alloc_hist(bx->to.sym) < 0) | ||
114 | goto out; | ||
115 | |||
116 | err = symbol__inc_addr_samples(bx->to.sym, | ||
117 | bx->to.map, | ||
118 | evsel->idx, | ||
119 | bx->to.al_addr); | ||
120 | if (err) | ||
121 | goto out; | ||
122 | } | ||
123 | evsel->hists.stats.total_period += 1; | ||
124 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | ||
125 | err = 0; | ||
126 | } else | ||
127 | return -ENOMEM; | ||
128 | } | ||
129 | out: | ||
130 | return err; | ||
131 | } | ||
132 | |||
56 | static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, | 133 | static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, |
57 | struct addr_location *al, | 134 | struct addr_location *al, |
58 | struct perf_sample *sample, | 135 | struct perf_sample *sample, |
@@ -126,14 +203,21 @@ static int process_sample_event(struct perf_tool *tool, | |||
126 | if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) | 203 | if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap)) |
127 | return 0; | 204 | return 0; |
128 | 205 | ||
129 | if (al.map != NULL) | 206 | if (sort__branch_mode == 1) { |
130 | al.map->dso->hit = 1; | 207 | if (perf_report__add_branch_hist_entry(tool, &al, sample, |
208 | evsel, machine)) { | ||
209 | pr_debug("problem adding lbr entry, skipping event\n"); | ||
210 | return -1; | ||
211 | } | ||
212 | } else { | ||
213 | if (al.map != NULL) | ||
214 | al.map->dso->hit = 1; | ||
131 | 215 | ||
132 | if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) { | 216 | if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) { |
133 | pr_debug("problem incrementing symbol period, skipping event\n"); | 217 | pr_debug("problem incrementing symbol period, skipping event\n"); |
134 | return -1; | 218 | return -1; |
219 | } | ||
135 | } | 220 | } |
136 | |||
137 | return 0; | 221 | return 0; |
138 | } | 222 | } |
139 | 223 | ||
@@ -188,6 +272,15 @@ static int perf_report__setup_sample_type(struct perf_report *rep) | |||
188 | } | 272 | } |
189 | } | 273 | } |
190 | 274 | ||
275 | if (sort__branch_mode == 1) { | ||
276 | if (!(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) { | ||
277 | fprintf(stderr, "selected -b but no branch data." | ||
278 | " Did you call perf record without" | ||
279 | " -b?\n"); | ||
280 | return -1; | ||
281 | } | ||
282 | } | ||
283 | |||
191 | return 0; | 284 | return 0; |
192 | } | 285 | } |
193 | 286 | ||
@@ -246,7 +339,7 @@ static int __cmd_report(struct perf_report *rep) | |||
246 | { | 339 | { |
247 | int ret = -EINVAL; | 340 | int ret = -EINVAL; |
248 | u64 nr_samples; | 341 | u64 nr_samples; |
249 | struct perf_session *session; | 342 | struct perf_session *session = rep->session; |
250 | struct perf_evsel *pos; | 343 | struct perf_evsel *pos; |
251 | struct map *kernel_map; | 344 | struct map *kernel_map; |
252 | struct kmap *kernel_kmap; | 345 | struct kmap *kernel_kmap; |
@@ -254,13 +347,6 @@ static int __cmd_report(struct perf_report *rep) | |||
254 | 347 | ||
255 | signal(SIGINT, sig_handler); | 348 | signal(SIGINT, sig_handler); |
256 | 349 | ||
257 | session = perf_session__new(rep->input_name, O_RDONLY, | ||
258 | rep->force, false, &rep->tool); | ||
259 | if (session == NULL) | ||
260 | return -ENOMEM; | ||
261 | |||
262 | rep->session = session; | ||
263 | |||
264 | if (rep->cpu_list) { | 350 | if (rep->cpu_list) { |
265 | ret = perf_session__cpu_bitmap(session, rep->cpu_list, | 351 | ret = perf_session__cpu_bitmap(session, rep->cpu_list, |
266 | rep->cpu_bitmap); | 352 | rep->cpu_bitmap); |
@@ -315,6 +401,9 @@ static int __cmd_report(struct perf_report *rep) | |||
315 | list_for_each_entry(pos, &session->evlist->entries, node) { | 401 | list_for_each_entry(pos, &session->evlist->entries, node) { |
316 | struct hists *hists = &pos->hists; | 402 | struct hists *hists = &pos->hists; |
317 | 403 | ||
404 | if (pos->idx == 0) | ||
405 | hists->symbol_filter_str = rep->symbol_filter_str; | ||
406 | |||
318 | hists__collapse_resort(hists); | 407 | hists__collapse_resort(hists); |
319 | hists__output_resort(hists); | 408 | hists__output_resort(hists); |
320 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; | 409 | nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; |
@@ -326,8 +415,13 @@ static int __cmd_report(struct perf_report *rep) | |||
326 | } | 415 | } |
327 | 416 | ||
328 | if (use_browser > 0) { | 417 | if (use_browser > 0) { |
329 | perf_evlist__tui_browse_hists(session->evlist, help, | 418 | if (use_browser == 1) { |
330 | NULL, NULL, 0); | 419 | perf_evlist__tui_browse_hists(session->evlist, help, |
420 | NULL, NULL, 0); | ||
421 | } else if (use_browser == 2) { | ||
422 | perf_evlist__gtk_browse_hists(session->evlist, help, | ||
423 | NULL, NULL, 0); | ||
424 | } | ||
331 | } else | 425 | } else |
332 | perf_evlist__tty_browse_hists(session->evlist, rep, help); | 426 | perf_evlist__tty_browse_hists(session->evlist, rep, help); |
333 | 427 | ||
@@ -427,9 +521,19 @@ setup: | |||
427 | return 0; | 521 | return 0; |
428 | } | 522 | } |
429 | 523 | ||
524 | static int | ||
525 | parse_branch_mode(const struct option *opt __used, const char *str __used, int unset) | ||
526 | { | ||
527 | sort__branch_mode = !unset; | ||
528 | return 0; | ||
529 | } | ||
530 | |||
430 | int cmd_report(int argc, const char **argv, const char *prefix __used) | 531 | int cmd_report(int argc, const char **argv, const char *prefix __used) |
431 | { | 532 | { |
533 | struct perf_session *session; | ||
432 | struct stat st; | 534 | struct stat st; |
535 | bool has_br_stack = false; | ||
536 | int ret = -1; | ||
433 | char callchain_default_opt[] = "fractal,0.5,callee"; | 537 | char callchain_default_opt[] = "fractal,0.5,callee"; |
434 | const char * const report_usage[] = { | 538 | const char * const report_usage[] = { |
435 | "perf report [<options>]", | 539 | "perf report [<options>]", |
@@ -474,10 +578,12 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
474 | OPT_STRING(0, "pretty", &report.pretty_printing_style, "key", | 578 | OPT_STRING(0, "pretty", &report.pretty_printing_style, "key", |
475 | "pretty printing style key: normal raw"), | 579 | "pretty printing style key: normal raw"), |
476 | OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"), | 580 | OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"), |
581 | OPT_BOOLEAN(0, "gtk", &report.use_gtk, "Use the GTK2 interface"), | ||
477 | OPT_BOOLEAN(0, "stdio", &report.use_stdio, | 582 | OPT_BOOLEAN(0, "stdio", &report.use_stdio, |
478 | "Use the stdio interface"), | 583 | "Use the stdio interface"), |
479 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 584 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
480 | "sort by key(s): pid, comm, dso, symbol, parent"), | 585 | "sort by key(s): pid, comm, dso, symbol, parent, dso_to," |
586 | " dso_from, symbol_to, symbol_from, mispredict"), | ||
481 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, | 587 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, |
482 | "Show sample percentage for different cpu modes"), | 588 | "Show sample percentage for different cpu modes"), |
483 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 589 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
@@ -495,6 +601,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
495 | "only consider symbols in these comms"), | 601 | "only consider symbols in these comms"), |
496 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", | 602 | OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]", |
497 | "only consider these symbols"), | 603 | "only consider these symbols"), |
604 | OPT_STRING(0, "symbol-filter", &report.symbol_filter_str, "filter", | ||
605 | "only show symbols that (partially) match with this filter"), | ||
498 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, | 606 | OPT_STRING('w', "column-widths", &symbol_conf.col_width_list_str, |
499 | "width[,width...]", | 607 | "width[,width...]", |
500 | "don't try to adjust column width, use these fixed values"), | 608 | "don't try to adjust column width, use these fixed values"), |
@@ -517,6 +625,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
517 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 625 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
518 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, | 626 | OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, |
519 | "Show a column with the sum of periods"), | 627 | "Show a column with the sum of periods"), |
628 | OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "", | ||
629 | "use branch records for histogram filling", parse_branch_mode), | ||
520 | OPT_END() | 630 | OPT_END() |
521 | }; | 631 | }; |
522 | 632 | ||
@@ -526,6 +636,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
526 | use_browser = 0; | 636 | use_browser = 0; |
527 | else if (report.use_tui) | 637 | else if (report.use_tui) |
528 | use_browser = 1; | 638 | use_browser = 1; |
639 | else if (report.use_gtk) | ||
640 | use_browser = 2; | ||
529 | 641 | ||
530 | if (report.inverted_callchain) | 642 | if (report.inverted_callchain) |
531 | callchain_param.order = ORDER_CALLER; | 643 | callchain_param.order = ORDER_CALLER; |
@@ -536,11 +648,39 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
536 | else | 648 | else |
537 | report.input_name = "perf.data"; | 649 | report.input_name = "perf.data"; |
538 | } | 650 | } |
651 | session = perf_session__new(report.input_name, O_RDONLY, | ||
652 | report.force, false, &report.tool); | ||
653 | if (session == NULL) | ||
654 | return -ENOMEM; | ||
539 | 655 | ||
540 | if (strcmp(report.input_name, "-") != 0) | 656 | report.session = session; |
541 | setup_browser(true); | 657 | |
542 | else | 658 | has_br_stack = perf_header__has_feat(&session->header, |
659 | HEADER_BRANCH_STACK); | ||
660 | |||
661 | if (sort__branch_mode == -1 && has_br_stack) | ||
662 | sort__branch_mode = 1; | ||
663 | |||
664 | /* sort__branch_mode could be 0 if --no-branch-stack */ | ||
665 | if (sort__branch_mode == 1) { | ||
666 | /* | ||
667 | * if no sort_order is provided, then specify | ||
668 | * branch-mode specific order | ||
669 | */ | ||
670 | if (sort_order == default_sort_order) | ||
671 | sort_order = "comm,dso_from,symbol_from," | ||
672 | "dso_to,symbol_to"; | ||
673 | |||
674 | } | ||
675 | |||
676 | if (strcmp(report.input_name, "-") != 0) { | ||
677 | if (report.use_gtk) | ||
678 | perf_gtk_setup_browser(argc, argv, true); | ||
679 | else | ||
680 | setup_browser(true); | ||
681 | } else { | ||
543 | use_browser = 0; | 682 | use_browser = 0; |
683 | } | ||
544 | 684 | ||
545 | /* | 685 | /* |
546 | * Only in the newt browser we are doing integrated annotation, | 686 | * Only in the newt browser we are doing integrated annotation, |
@@ -568,13 +708,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
568 | } | 708 | } |
569 | 709 | ||
570 | if (symbol__init() < 0) | 710 | if (symbol__init() < 0) |
571 | return -1; | 711 | goto error; |
572 | 712 | ||
573 | setup_sorting(report_usage, options); | 713 | setup_sorting(report_usage, options); |
574 | 714 | ||
575 | if (parent_pattern != default_parent_pattern) { | 715 | if (parent_pattern != default_parent_pattern) { |
576 | if (sort_dimension__add("parent") < 0) | 716 | if (sort_dimension__add("parent") < 0) |
577 | return -1; | 717 | goto error; |
578 | 718 | ||
579 | /* | 719 | /* |
580 | * Only show the parent fields if we explicitly | 720 | * Only show the parent fields if we explicitly |
@@ -586,15 +726,31 @@ int cmd_report(int argc, const char **argv, const char *prefix __used) | |||
586 | } else | 726 | } else |
587 | symbol_conf.exclude_other = false; | 727 | symbol_conf.exclude_other = false; |
588 | 728 | ||
589 | /* | 729 | if (argc) { |
590 | * Any (unrecognized) arguments left? | 730 | /* |
591 | */ | 731 | * Special case: if there's an argument left then assume that |
592 | if (argc) | 732 | * it's a symbol filter: |
593 | usage_with_options(report_usage, options); | 733 | */ |
734 | if (argc > 1) | ||
735 | usage_with_options(report_usage, options); | ||
736 | |||
737 | report.symbol_filter_str = argv[0]; | ||
738 | } | ||
594 | 739 | ||
595 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); | ||
596 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); | 740 | sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout); |
597 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); | ||
598 | 741 | ||
599 | return __cmd_report(&report); | 742 | if (sort__branch_mode == 1) { |
743 | sort_entry__setup_elide(&sort_dso_from, symbol_conf.dso_from_list, "dso_from", stdout); | ||
744 | sort_entry__setup_elide(&sort_dso_to, symbol_conf.dso_to_list, "dso_to", stdout); | ||
745 | sort_entry__setup_elide(&sort_sym_from, symbol_conf.sym_from_list, "sym_from", stdout); | ||
746 | sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", stdout); | ||
747 | } else { | ||
748 | sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout); | ||
749 | sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout); | ||
750 | } | ||
751 | |||
752 | ret = __cmd_report(&report); | ||
753 | error: | ||
754 | perf_session__delete(session); | ||
755 | return ret; | ||
600 | } | 756 | } |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index fb8b5f83b4a0..1cad3af4bf4c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "util/debug.h" | 17 | #include "util/debug.h" |
18 | 18 | ||
19 | #include <sys/prctl.h> | 19 | #include <sys/prctl.h> |
20 | #include <sys/resource.h> | ||
20 | 21 | ||
21 | #include <semaphore.h> | 22 | #include <semaphore.h> |
22 | #include <pthread.h> | 23 | #include <pthread.h> |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index fd1909afcfd6..d4ce733b9eba 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
@@ -40,6 +40,7 @@ enum perf_output_field { | |||
40 | PERF_OUTPUT_SYM = 1U << 8, | 40 | PERF_OUTPUT_SYM = 1U << 8, |
41 | PERF_OUTPUT_DSO = 1U << 9, | 41 | PERF_OUTPUT_DSO = 1U << 9, |
42 | PERF_OUTPUT_ADDR = 1U << 10, | 42 | PERF_OUTPUT_ADDR = 1U << 10, |
43 | PERF_OUTPUT_SYMOFFSET = 1U << 11, | ||
43 | }; | 44 | }; |
44 | 45 | ||
45 | struct output_option { | 46 | struct output_option { |
@@ -57,6 +58,7 @@ struct output_option { | |||
57 | {.str = "sym", .field = PERF_OUTPUT_SYM}, | 58 | {.str = "sym", .field = PERF_OUTPUT_SYM}, |
58 | {.str = "dso", .field = PERF_OUTPUT_DSO}, | 59 | {.str = "dso", .field = PERF_OUTPUT_DSO}, |
59 | {.str = "addr", .field = PERF_OUTPUT_ADDR}, | 60 | {.str = "addr", .field = PERF_OUTPUT_ADDR}, |
61 | {.str = "symoff", .field = PERF_OUTPUT_SYMOFFSET}, | ||
60 | }; | 62 | }; |
61 | 63 | ||
62 | /* default set to maintain compatibility with current format */ | 64 | /* default set to maintain compatibility with current format */ |
@@ -193,6 +195,11 @@ static int perf_evsel__check_attr(struct perf_evsel *evsel, | |||
193 | "to symbols.\n"); | 195 | "to symbols.\n"); |
194 | return -EINVAL; | 196 | return -EINVAL; |
195 | } | 197 | } |
198 | if (PRINT_FIELD(SYMOFFSET) && !PRINT_FIELD(SYM)) { | ||
199 | pr_err("Display of offsets requested but symbol is not" | ||
200 | "selected.\n"); | ||
201 | return -EINVAL; | ||
202 | } | ||
196 | if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { | 203 | if (PRINT_FIELD(DSO) && !PRINT_FIELD(IP) && !PRINT_FIELD(ADDR)) { |
197 | pr_err("Display of DSO requested but neither sample IP nor " | 204 | pr_err("Display of DSO requested but neither sample IP nor " |
198 | "sample address\nis selected. Hence, no addresses to convert " | 205 | "sample address\nis selected. Hence, no addresses to convert " |
@@ -300,10 +307,17 @@ static void print_sample_start(struct perf_sample *sample, | |||
300 | } else | 307 | } else |
301 | evname = __event_name(attr->type, attr->config); | 308 | evname = __event_name(attr->type, attr->config); |
302 | 309 | ||
303 | printf("%s: ", evname ? evname : "(unknown)"); | 310 | printf("%s: ", evname ? evname : "[unknown]"); |
304 | } | 311 | } |
305 | } | 312 | } |
306 | 313 | ||
314 | static bool is_bts_event(struct perf_event_attr *attr) | ||
315 | { | ||
316 | return ((attr->type == PERF_TYPE_HARDWARE) && | ||
317 | (attr->config & PERF_COUNT_HW_BRANCH_INSTRUCTIONS) && | ||
318 | (attr->sample_period == 1)); | ||
319 | } | ||
320 | |||
307 | static bool sample_addr_correlates_sym(struct perf_event_attr *attr) | 321 | static bool sample_addr_correlates_sym(struct perf_event_attr *attr) |
308 | { | 322 | { |
309 | if ((attr->type == PERF_TYPE_SOFTWARE) && | 323 | if ((attr->type == PERF_TYPE_SOFTWARE) && |
@@ -312,6 +326,9 @@ static bool sample_addr_correlates_sym(struct perf_event_attr *attr) | |||
312 | (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ))) | 326 | (attr->config == PERF_COUNT_SW_PAGE_FAULTS_MAJ))) |
313 | return true; | 327 | return true; |
314 | 328 | ||
329 | if (is_bts_event(attr)) | ||
330 | return true; | ||
331 | |||
315 | return false; | 332 | return false; |
316 | } | 333 | } |
317 | 334 | ||
@@ -323,7 +340,6 @@ static void print_sample_addr(union perf_event *event, | |||
323 | { | 340 | { |
324 | struct addr_location al; | 341 | struct addr_location al; |
325 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 342 | u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
326 | const char *symname, *dsoname; | ||
327 | 343 | ||
328 | printf("%16" PRIx64, sample->addr); | 344 | printf("%16" PRIx64, sample->addr); |
329 | 345 | ||
@@ -343,22 +359,46 @@ static void print_sample_addr(union perf_event *event, | |||
343 | al.sym = map__find_symbol(al.map, al.addr, NULL); | 359 | al.sym = map__find_symbol(al.map, al.addr, NULL); |
344 | 360 | ||
345 | if (PRINT_FIELD(SYM)) { | 361 | if (PRINT_FIELD(SYM)) { |
346 | if (al.sym && al.sym->name) | 362 | printf(" "); |
347 | symname = al.sym->name; | 363 | if (PRINT_FIELD(SYMOFFSET)) |
364 | symbol__fprintf_symname_offs(al.sym, &al, stdout); | ||
348 | else | 365 | else |
349 | symname = ""; | 366 | symbol__fprintf_symname(al.sym, stdout); |
350 | |||
351 | printf(" %16s", symname); | ||
352 | } | 367 | } |
353 | 368 | ||
354 | if (PRINT_FIELD(DSO)) { | 369 | if (PRINT_FIELD(DSO)) { |
355 | if (al.map && al.map->dso && al.map->dso->name) | 370 | printf(" ("); |
356 | dsoname = al.map->dso->name; | 371 | map__fprintf_dsoname(al.map, stdout); |
357 | else | 372 | printf(")"); |
358 | dsoname = ""; | 373 | } |
374 | } | ||
359 | 375 | ||
360 | printf(" (%s)", dsoname); | 376 | static void print_sample_bts(union perf_event *event, |
377 | struct perf_sample *sample, | ||
378 | struct perf_evsel *evsel, | ||
379 | struct machine *machine, | ||
380 | struct thread *thread) | ||
381 | { | ||
382 | struct perf_event_attr *attr = &evsel->attr; | ||
383 | |||
384 | /* print branch_from information */ | ||
385 | if (PRINT_FIELD(IP)) { | ||
386 | if (!symbol_conf.use_callchain) | ||
387 | printf(" "); | ||
388 | else | ||
389 | printf("\n"); | ||
390 | perf_event__print_ip(event, sample, machine, evsel, | ||
391 | PRINT_FIELD(SYM), PRINT_FIELD(DSO), | ||
392 | PRINT_FIELD(SYMOFFSET)); | ||
361 | } | 393 | } |
394 | |||
395 | printf(" => "); | ||
396 | |||
397 | /* print branch_to information */ | ||
398 | if (PRINT_FIELD(ADDR)) | ||
399 | print_sample_addr(event, sample, machine, thread, attr); | ||
400 | |||
401 | printf("\n"); | ||
362 | } | 402 | } |
363 | 403 | ||
364 | static void process_event(union perf_event *event __unused, | 404 | static void process_event(union perf_event *event __unused, |
@@ -374,6 +414,11 @@ static void process_event(union perf_event *event __unused, | |||
374 | 414 | ||
375 | print_sample_start(sample, thread, attr); | 415 | print_sample_start(sample, thread, attr); |
376 | 416 | ||
417 | if (is_bts_event(attr)) { | ||
418 | print_sample_bts(event, sample, evsel, machine, thread); | ||
419 | return; | ||
420 | } | ||
421 | |||
377 | if (PRINT_FIELD(TRACE)) | 422 | if (PRINT_FIELD(TRACE)) |
378 | print_trace_event(sample->cpu, sample->raw_data, | 423 | print_trace_event(sample->cpu, sample->raw_data, |
379 | sample->raw_size); | 424 | sample->raw_size); |
@@ -387,7 +432,8 @@ static void process_event(union perf_event *event __unused, | |||
387 | else | 432 | else |
388 | printf("\n"); | 433 | printf("\n"); |
389 | perf_event__print_ip(event, sample, machine, evsel, | 434 | perf_event__print_ip(event, sample, machine, evsel, |
390 | PRINT_FIELD(SYM), PRINT_FIELD(DSO)); | 435 | PRINT_FIELD(SYM), PRINT_FIELD(DSO), |
436 | PRINT_FIELD(SYMOFFSET)); | ||
391 | } | 437 | } |
392 | 438 | ||
393 | printf("\n"); | 439 | printf("\n"); |
@@ -1018,13 +1064,17 @@ static char *get_script_path(const char *script_root, const char *suffix) | |||
1018 | __script_root = get_script_root(&script_dirent, suffix); | 1064 | __script_root = get_script_root(&script_dirent, suffix); |
1019 | if (__script_root && !strcmp(script_root, __script_root)) { | 1065 | if (__script_root && !strcmp(script_root, __script_root)) { |
1020 | free(__script_root); | 1066 | free(__script_root); |
1067 | closedir(lang_dir); | ||
1068 | closedir(scripts_dir); | ||
1021 | snprintf(script_path, MAXPATHLEN, "%s/%s", | 1069 | snprintf(script_path, MAXPATHLEN, "%s/%s", |
1022 | lang_path, script_dirent.d_name); | 1070 | lang_path, script_dirent.d_name); |
1023 | return strdup(script_path); | 1071 | return strdup(script_path); |
1024 | } | 1072 | } |
1025 | free(__script_root); | 1073 | free(__script_root); |
1026 | } | 1074 | } |
1075 | closedir(lang_dir); | ||
1027 | } | 1076 | } |
1077 | closedir(scripts_dir); | ||
1028 | 1078 | ||
1029 | return NULL; | 1079 | return NULL; |
1030 | } | 1080 | } |
@@ -1093,7 +1143,10 @@ static const struct option options[] = { | |||
1093 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", | 1143 | OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", |
1094 | "Look for files with symbols relative to this directory"), | 1144 | "Look for files with symbols relative to this directory"), |
1095 | OPT_CALLBACK('f', "fields", NULL, "str", | 1145 | OPT_CALLBACK('f', "fields", NULL, "str", |
1096 | "comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr", | 1146 | "comma separated output fields prepend with 'type:'. " |
1147 | "Valid types: hw,sw,trace,raw. " | ||
1148 | "Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso," | ||
1149 | "addr,symoff", | ||
1097 | parse_output_fields), | 1150 | parse_output_fields), |
1098 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 1151 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1099 | "system-wide collection from all CPUs"), | 1152 | "system-wide collection from all CPUs"), |
@@ -1102,6 +1155,9 @@ static const struct option options[] = { | |||
1102 | "only display events for these comms"), | 1155 | "only display events for these comms"), |
1103 | OPT_BOOLEAN('I', "show-info", &show_full_info, | 1156 | OPT_BOOLEAN('I', "show-info", &show_full_info, |
1104 | "display extended information from perf.data file"), | 1157 | "display extended information from perf.data file"), |
1158 | OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, | ||
1159 | "Show the path of [kernel.kallsyms]"), | ||
1160 | |||
1105 | OPT_END() | 1161 | OPT_END() |
1106 | }; | 1162 | }; |
1107 | 1163 | ||
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index f5d2a63eba66..c941bb640f49 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -182,8 +182,8 @@ static int run_count = 1; | |||
182 | static bool no_inherit = false; | 182 | static bool no_inherit = false; |
183 | static bool scale = true; | 183 | static bool scale = true; |
184 | static bool no_aggr = false; | 184 | static bool no_aggr = false; |
185 | static pid_t target_pid = -1; | 185 | static const char *target_pid; |
186 | static pid_t target_tid = -1; | 186 | static const char *target_tid; |
187 | static pid_t child_pid = -1; | 187 | static pid_t child_pid = -1; |
188 | static bool null_run = false; | 188 | static bool null_run = false; |
189 | static int detailed_run = 0; | 189 | static int detailed_run = 0; |
@@ -296,7 +296,7 @@ static int create_perf_stat_counter(struct perf_evsel *evsel, | |||
296 | if (system_wide) | 296 | if (system_wide) |
297 | return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, | 297 | return perf_evsel__open_per_cpu(evsel, evsel_list->cpus, |
298 | group, group_fd); | 298 | group, group_fd); |
299 | if (target_pid == -1 && target_tid == -1) { | 299 | if (!target_pid && !target_tid && (!group || evsel == first)) { |
300 | attr->disabled = 1; | 300 | attr->disabled = 1; |
301 | attr->enable_on_exec = 1; | 301 | attr->enable_on_exec = 1; |
302 | } | 302 | } |
@@ -446,7 +446,7 @@ static int run_perf_stat(int argc __used, const char **argv) | |||
446 | exit(-1); | 446 | exit(-1); |
447 | } | 447 | } |
448 | 448 | ||
449 | if (target_tid == -1 && target_pid == -1 && !system_wide) | 449 | if (!target_tid && !target_pid && !system_wide) |
450 | evsel_list->threads->map[0] = child_pid; | 450 | evsel_list->threads->map[0] = child_pid; |
451 | 451 | ||
452 | /* | 452 | /* |
@@ -576,6 +576,8 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
576 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) | 576 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) |
577 | fprintf(output, " # %8.3f CPUs utilized ", | 577 | fprintf(output, " # %8.3f CPUs utilized ", |
578 | avg / avg_stats(&walltime_nsecs_stats)); | 578 | avg / avg_stats(&walltime_nsecs_stats)); |
579 | else | ||
580 | fprintf(output, " "); | ||
579 | } | 581 | } |
580 | 582 | ||
581 | /* used for get_ratio_color() */ | 583 | /* used for get_ratio_color() */ |
@@ -844,12 +846,18 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg) | |||
844 | 846 | ||
845 | fprintf(output, " # %8.3f GHz ", ratio); | 847 | fprintf(output, " # %8.3f GHz ", ratio); |
846 | } else if (runtime_nsecs_stats[cpu].n != 0) { | 848 | } else if (runtime_nsecs_stats[cpu].n != 0) { |
849 | char unit = 'M'; | ||
850 | |||
847 | total = avg_stats(&runtime_nsecs_stats[cpu]); | 851 | total = avg_stats(&runtime_nsecs_stats[cpu]); |
848 | 852 | ||
849 | if (total) | 853 | if (total) |
850 | ratio = 1000.0 * avg / total; | 854 | ratio = 1000.0 * avg / total; |
855 | if (ratio < 0.001) { | ||
856 | ratio *= 1000; | ||
857 | unit = 'K'; | ||
858 | } | ||
851 | 859 | ||
852 | fprintf(output, " # %8.3f M/sec ", ratio); | 860 | fprintf(output, " # %8.3f %c/sec ", ratio, unit); |
853 | } else { | 861 | } else { |
854 | fprintf(output, " "); | 862 | fprintf(output, " "); |
855 | } | 863 | } |
@@ -960,14 +968,14 @@ static void print_stat(int argc, const char **argv) | |||
960 | if (!csv_output) { | 968 | if (!csv_output) { |
961 | fprintf(output, "\n"); | 969 | fprintf(output, "\n"); |
962 | fprintf(output, " Performance counter stats for "); | 970 | fprintf(output, " Performance counter stats for "); |
963 | if(target_pid == -1 && target_tid == -1) { | 971 | if (!target_pid && !target_tid) { |
964 | fprintf(output, "\'%s", argv[0]); | 972 | fprintf(output, "\'%s", argv[0]); |
965 | for (i = 1; i < argc; i++) | 973 | for (i = 1; i < argc; i++) |
966 | fprintf(output, " %s", argv[i]); | 974 | fprintf(output, " %s", argv[i]); |
967 | } else if (target_pid != -1) | 975 | } else if (target_pid) |
968 | fprintf(output, "process id \'%d", target_pid); | 976 | fprintf(output, "process id \'%s", target_pid); |
969 | else | 977 | else |
970 | fprintf(output, "thread id \'%d", target_tid); | 978 | fprintf(output, "thread id \'%s", target_tid); |
971 | 979 | ||
972 | fprintf(output, "\'"); | 980 | fprintf(output, "\'"); |
973 | if (run_count > 1) | 981 | if (run_count > 1) |
@@ -1041,10 +1049,10 @@ static const struct option options[] = { | |||
1041 | "event filter", parse_filter), | 1049 | "event filter", parse_filter), |
1042 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | 1050 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, |
1043 | "child tasks do not inherit counters"), | 1051 | "child tasks do not inherit counters"), |
1044 | OPT_INTEGER('p', "pid", &target_pid, | 1052 | OPT_STRING('p', "pid", &target_pid, "pid", |
1045 | "stat events on existing process id"), | 1053 | "stat events on existing process id"), |
1046 | OPT_INTEGER('t', "tid", &target_tid, | 1054 | OPT_STRING('t', "tid", &target_tid, "tid", |
1047 | "stat events on existing thread id"), | 1055 | "stat events on existing thread id"), |
1048 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 1056 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
1049 | "system-wide collection from all CPUs"), | 1057 | "system-wide collection from all CPUs"), |
1050 | OPT_BOOLEAN('g', "group", &group, | 1058 | OPT_BOOLEAN('g', "group", &group, |
@@ -1182,7 +1190,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
1182 | } else if (big_num_opt == 0) /* User passed --no-big-num */ | 1190 | } else if (big_num_opt == 0) /* User passed --no-big-num */ |
1183 | big_num = false; | 1191 | big_num = false; |
1184 | 1192 | ||
1185 | if (!argc && target_pid == -1 && target_tid == -1) | 1193 | if (!argc && !target_pid && !target_tid) |
1186 | usage_with_options(stat_usage, options); | 1194 | usage_with_options(stat_usage, options); |
1187 | if (run_count <= 0) | 1195 | if (run_count <= 0) |
1188 | usage_with_options(stat_usage, options); | 1196 | usage_with_options(stat_usage, options); |
@@ -1198,10 +1206,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used) | |||
1198 | if (add_default_attributes()) | 1206 | if (add_default_attributes()) |
1199 | goto out; | 1207 | goto out; |
1200 | 1208 | ||
1201 | if (target_pid != -1) | 1209 | if (target_pid) |
1202 | target_tid = target_pid; | 1210 | target_tid = target_pid; |
1203 | 1211 | ||
1204 | evsel_list->threads = thread_map__new(target_pid, target_tid); | 1212 | evsel_list->threads = thread_map__new_str(target_pid, |
1213 | target_tid, UINT_MAX); | ||
1205 | if (evsel_list->threads == NULL) { | 1214 | if (evsel_list->threads == NULL) { |
1206 | pr_err("Problems finding threads of monitor\n"); | 1215 | pr_err("Problems finding threads of monitor\n"); |
1207 | usage_with_options(stat_usage, options); | 1216 | usage_with_options(stat_usage, options); |
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c index 2b9a7f497a20..1c5b9801ac61 100644 --- a/tools/perf/builtin-test.c +++ b/tools/perf/builtin-test.c | |||
@@ -13,8 +13,11 @@ | |||
13 | #include "util/parse-events.h" | 13 | #include "util/parse-events.h" |
14 | #include "util/symbol.h" | 14 | #include "util/symbol.h" |
15 | #include "util/thread_map.h" | 15 | #include "util/thread_map.h" |
16 | #include "util/pmu.h" | ||
16 | #include "../../include/linux/hw_breakpoint.h" | 17 | #include "../../include/linux/hw_breakpoint.h" |
17 | 18 | ||
19 | #include <sys/mman.h> | ||
20 | |||
18 | static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym) | 21 | static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym) |
19 | { | 22 | { |
20 | bool *visited = symbol__priv(sym); | 23 | bool *visited = symbol__priv(sym); |
@@ -276,7 +279,7 @@ static int test__open_syscall_event(void) | |||
276 | return -1; | 279 | return -1; |
277 | } | 280 | } |
278 | 281 | ||
279 | threads = thread_map__new(-1, getpid()); | 282 | threads = thread_map__new(-1, getpid(), UINT_MAX); |
280 | if (threads == NULL) { | 283 | if (threads == NULL) { |
281 | pr_debug("thread_map__new\n"); | 284 | pr_debug("thread_map__new\n"); |
282 | return -1; | 285 | return -1; |
@@ -342,7 +345,7 @@ static int test__open_syscall_event_on_all_cpus(void) | |||
342 | return -1; | 345 | return -1; |
343 | } | 346 | } |
344 | 347 | ||
345 | threads = thread_map__new(-1, getpid()); | 348 | threads = thread_map__new(-1, getpid(), UINT_MAX); |
346 | if (threads == NULL) { | 349 | if (threads == NULL) { |
347 | pr_debug("thread_map__new\n"); | 350 | pr_debug("thread_map__new\n"); |
348 | return -1; | 351 | return -1; |
@@ -490,7 +493,7 @@ static int test__basic_mmap(void) | |||
490 | expected_nr_events[i] = random() % 257; | 493 | expected_nr_events[i] = random() % 257; |
491 | } | 494 | } |
492 | 495 | ||
493 | threads = thread_map__new(-1, getpid()); | 496 | threads = thread_map__new(-1, getpid(), UINT_MAX); |
494 | if (threads == NULL) { | 497 | if (threads == NULL) { |
495 | pr_debug("thread_map__new\n"); | 498 | pr_debug("thread_map__new\n"); |
496 | return -1; | 499 | return -1; |
@@ -648,7 +651,7 @@ static int test__checkevent_raw(struct perf_evlist *evlist) | |||
648 | 651 | ||
649 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | 652 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); |
650 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | 653 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); |
651 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | 654 | TEST_ASSERT_VAL("wrong config", 0x1a == evsel->attr.config); |
652 | return 0; | 655 | return 0; |
653 | } | 656 | } |
654 | 657 | ||
@@ -675,6 +678,24 @@ static int test__checkevent_symbolic_name(struct perf_evlist *evlist) | |||
675 | return 0; | 678 | return 0; |
676 | } | 679 | } |
677 | 680 | ||
681 | static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist) | ||
682 | { | ||
683 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
684 | struct perf_evsel, node); | ||
685 | |||
686 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
687 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type); | ||
688 | TEST_ASSERT_VAL("wrong config", | ||
689 | PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config); | ||
690 | TEST_ASSERT_VAL("wrong period", | ||
691 | 100000 == evsel->attr.sample_period); | ||
692 | TEST_ASSERT_VAL("wrong config1", | ||
693 | 0 == evsel->attr.config1); | ||
694 | TEST_ASSERT_VAL("wrong config2", | ||
695 | 1 == evsel->attr.config2); | ||
696 | return 0; | ||
697 | } | ||
698 | |||
678 | static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) | 699 | static int test__checkevent_symbolic_alias(struct perf_evlist *evlist) |
679 | { | 700 | { |
680 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | 701 | struct perf_evsel *evsel = list_entry(evlist->entries.next, |
@@ -856,6 +877,115 @@ static int test__checkevent_genhw_modifier(struct perf_evlist *evlist) | |||
856 | return test__checkevent_genhw(evlist); | 877 | return test__checkevent_genhw(evlist); |
857 | } | 878 | } |
858 | 879 | ||
880 | static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist) | ||
881 | { | ||
882 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
883 | struct perf_evsel, node); | ||
884 | |||
885 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
886 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
887 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
888 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
889 | |||
890 | return test__checkevent_breakpoint(evlist); | ||
891 | } | ||
892 | |||
893 | static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist) | ||
894 | { | ||
895 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
896 | struct perf_evsel, node); | ||
897 | |||
898 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
899 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
900 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
901 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
902 | |||
903 | return test__checkevent_breakpoint_x(evlist); | ||
904 | } | ||
905 | |||
906 | static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist) | ||
907 | { | ||
908 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
909 | struct perf_evsel, node); | ||
910 | |||
911 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
912 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
913 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
914 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | ||
915 | |||
916 | return test__checkevent_breakpoint_r(evlist); | ||
917 | } | ||
918 | |||
919 | static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist) | ||
920 | { | ||
921 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
922 | struct perf_evsel, node); | ||
923 | |||
924 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
925 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
926 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
927 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | ||
928 | |||
929 | return test__checkevent_breakpoint_w(evlist); | ||
930 | } | ||
931 | |||
932 | static int test__checkevent_pmu(struct perf_evlist *evlist) | ||
933 | { | ||
934 | |||
935 | struct perf_evsel *evsel = list_entry(evlist->entries.next, | ||
936 | struct perf_evsel, node); | ||
937 | |||
938 | TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); | ||
939 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | ||
940 | TEST_ASSERT_VAL("wrong config", 10 == evsel->attr.config); | ||
941 | TEST_ASSERT_VAL("wrong config1", 1 == evsel->attr.config1); | ||
942 | TEST_ASSERT_VAL("wrong config2", 3 == evsel->attr.config2); | ||
943 | TEST_ASSERT_VAL("wrong period", 1000 == evsel->attr.sample_period); | ||
944 | |||
945 | return 0; | ||
946 | } | ||
947 | |||
948 | static int test__checkevent_list(struct perf_evlist *evlist) | ||
949 | { | ||
950 | struct perf_evsel *evsel; | ||
951 | |||
952 | TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); | ||
953 | |||
954 | /* r1 */ | ||
955 | evsel = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
956 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type); | ||
957 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | ||
958 | TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1); | ||
959 | TEST_ASSERT_VAL("wrong config2", 0 == evsel->attr.config2); | ||
960 | TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user); | ||
961 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
962 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
963 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
964 | |||
965 | /* syscalls:sys_enter_open:k */ | ||
966 | evsel = list_entry(evsel->node.next, struct perf_evsel, node); | ||
967 | TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); | ||
968 | TEST_ASSERT_VAL("wrong sample_type", | ||
969 | (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) == | ||
970 | evsel->attr.sample_type); | ||
971 | TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period); | ||
972 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
973 | TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel); | ||
974 | TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv); | ||
975 | TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); | ||
976 | |||
977 | /* 1:1:hp */ | ||
978 | evsel = list_entry(evsel->node.next, struct perf_evsel, node); | ||
979 | TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type); | ||
980 | TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config); | ||
981 | TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user); | ||
982 | TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel); | ||
983 | TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv); | ||
984 | TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip); | ||
985 | |||
986 | return 0; | ||
987 | } | ||
988 | |||
859 | static struct test__event_st { | 989 | static struct test__event_st { |
860 | const char *name; | 990 | const char *name; |
861 | __u32 type; | 991 | __u32 type; |
@@ -870,7 +1000,7 @@ static struct test__event_st { | |||
870 | .check = test__checkevent_tracepoint_multi, | 1000 | .check = test__checkevent_tracepoint_multi, |
871 | }, | 1001 | }, |
872 | { | 1002 | { |
873 | .name = "r1", | 1003 | .name = "r1a", |
874 | .check = test__checkevent_raw, | 1004 | .check = test__checkevent_raw, |
875 | }, | 1005 | }, |
876 | { | 1006 | { |
@@ -882,6 +1012,10 @@ static struct test__event_st { | |||
882 | .check = test__checkevent_symbolic_name, | 1012 | .check = test__checkevent_symbolic_name, |
883 | }, | 1013 | }, |
884 | { | 1014 | { |
1015 | .name = "cycles/period=100000,config2/", | ||
1016 | .check = test__checkevent_symbolic_name_config, | ||
1017 | }, | ||
1018 | { | ||
885 | .name = "faults", | 1019 | .name = "faults", |
886 | .check = test__checkevent_symbolic_alias, | 1020 | .check = test__checkevent_symbolic_alias, |
887 | }, | 1021 | }, |
@@ -914,7 +1048,7 @@ static struct test__event_st { | |||
914 | .check = test__checkevent_tracepoint_multi_modifier, | 1048 | .check = test__checkevent_tracepoint_multi_modifier, |
915 | }, | 1049 | }, |
916 | { | 1050 | { |
917 | .name = "r1:kp", | 1051 | .name = "r1a:kp", |
918 | .check = test__checkevent_raw_modifier, | 1052 | .check = test__checkevent_raw_modifier, |
919 | }, | 1053 | }, |
920 | { | 1054 | { |
@@ -933,6 +1067,30 @@ static struct test__event_st { | |||
933 | .name = "L1-dcache-load-miss:kp", | 1067 | .name = "L1-dcache-load-miss:kp", |
934 | .check = test__checkevent_genhw_modifier, | 1068 | .check = test__checkevent_genhw_modifier, |
935 | }, | 1069 | }, |
1070 | { | ||
1071 | .name = "mem:0:u", | ||
1072 | .check = test__checkevent_breakpoint_modifier, | ||
1073 | }, | ||
1074 | { | ||
1075 | .name = "mem:0:x:k", | ||
1076 | .check = test__checkevent_breakpoint_x_modifier, | ||
1077 | }, | ||
1078 | { | ||
1079 | .name = "mem:0:r:hp", | ||
1080 | .check = test__checkevent_breakpoint_r_modifier, | ||
1081 | }, | ||
1082 | { | ||
1083 | .name = "mem:0:w:up", | ||
1084 | .check = test__checkevent_breakpoint_w_modifier, | ||
1085 | }, | ||
1086 | { | ||
1087 | .name = "cpu/config=10,config1,config2=3,period=1000/u", | ||
1088 | .check = test__checkevent_pmu, | ||
1089 | }, | ||
1090 | { | ||
1091 | .name = "r1,syscalls:sys_enter_open:k,1:1:hp", | ||
1092 | .check = test__checkevent_list, | ||
1093 | }, | ||
936 | }; | 1094 | }; |
937 | 1095 | ||
938 | #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) | 1096 | #define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st)) |
@@ -958,10 +1116,9 @@ static int test__parse_events(void) | |||
958 | } | 1116 | } |
959 | 1117 | ||
960 | ret = e->check(evlist); | 1118 | ret = e->check(evlist); |
1119 | perf_evlist__delete(evlist); | ||
961 | if (ret) | 1120 | if (ret) |
962 | break; | 1121 | break; |
963 | |||
964 | perf_evlist__delete(evlist); | ||
965 | } | 1122 | } |
966 | 1123 | ||
967 | return ret; | 1124 | return ret; |
@@ -1008,12 +1165,9 @@ realloc: | |||
1008 | static int test__PERF_RECORD(void) | 1165 | static int test__PERF_RECORD(void) |
1009 | { | 1166 | { |
1010 | struct perf_record_opts opts = { | 1167 | struct perf_record_opts opts = { |
1011 | .target_pid = -1, | ||
1012 | .target_tid = -1, | ||
1013 | .no_delay = true, | 1168 | .no_delay = true, |
1014 | .freq = 10, | 1169 | .freq = 10, |
1015 | .mmap_pages = 256, | 1170 | .mmap_pages = 256, |
1016 | .sample_id_all_avail = true, | ||
1017 | }; | 1171 | }; |
1018 | cpu_set_t *cpu_mask = NULL; | 1172 | cpu_set_t *cpu_mask = NULL; |
1019 | size_t cpu_mask_size = 0; | 1173 | size_t cpu_mask_size = 0; |
@@ -1054,7 +1208,7 @@ static int test__PERF_RECORD(void) | |||
1054 | * we're monitoring, the one forked there. | 1208 | * we're monitoring, the one forked there. |
1055 | */ | 1209 | */ |
1056 | err = perf_evlist__create_maps(evlist, opts.target_pid, | 1210 | err = perf_evlist__create_maps(evlist, opts.target_pid, |
1057 | opts.target_tid, opts.cpu_list); | 1211 | opts.target_tid, UINT_MAX, opts.cpu_list); |
1058 | if (err < 0) { | 1212 | if (err < 0) { |
1059 | pr_debug("Not enough memory to create thread/cpu maps\n"); | 1213 | pr_debug("Not enough memory to create thread/cpu maps\n"); |
1060 | goto out_delete_evlist; | 1214 | goto out_delete_evlist; |
@@ -1296,6 +1450,178 @@ out: | |||
1296 | return (err < 0 || errs > 0) ? -1 : 0; | 1450 | return (err < 0 || errs > 0) ? -1 : 0; |
1297 | } | 1451 | } |
1298 | 1452 | ||
1453 | |||
1454 | #if defined(__x86_64__) || defined(__i386__) | ||
1455 | |||
1456 | #define barrier() asm volatile("" ::: "memory") | ||
1457 | |||
1458 | static u64 rdpmc(unsigned int counter) | ||
1459 | { | ||
1460 | unsigned int low, high; | ||
1461 | |||
1462 | asm volatile("rdpmc" : "=a" (low), "=d" (high) : "c" (counter)); | ||
1463 | |||
1464 | return low | ((u64)high) << 32; | ||
1465 | } | ||
1466 | |||
1467 | static u64 rdtsc(void) | ||
1468 | { | ||
1469 | unsigned int low, high; | ||
1470 | |||
1471 | asm volatile("rdtsc" : "=a" (low), "=d" (high)); | ||
1472 | |||
1473 | return low | ((u64)high) << 32; | ||
1474 | } | ||
1475 | |||
1476 | static u64 mmap_read_self(void *addr) | ||
1477 | { | ||
1478 | struct perf_event_mmap_page *pc = addr; | ||
1479 | u32 seq, idx, time_mult = 0, time_shift = 0; | ||
1480 | u64 count, cyc = 0, time_offset = 0, enabled, running, delta; | ||
1481 | |||
1482 | do { | ||
1483 | seq = pc->lock; | ||
1484 | barrier(); | ||
1485 | |||
1486 | enabled = pc->time_enabled; | ||
1487 | running = pc->time_running; | ||
1488 | |||
1489 | if (enabled != running) { | ||
1490 | cyc = rdtsc(); | ||
1491 | time_mult = pc->time_mult; | ||
1492 | time_shift = pc->time_shift; | ||
1493 | time_offset = pc->time_offset; | ||
1494 | } | ||
1495 | |||
1496 | idx = pc->index; | ||
1497 | count = pc->offset; | ||
1498 | if (idx) | ||
1499 | count += rdpmc(idx - 1); | ||
1500 | |||
1501 | barrier(); | ||
1502 | } while (pc->lock != seq); | ||
1503 | |||
1504 | if (enabled != running) { | ||
1505 | u64 quot, rem; | ||
1506 | |||
1507 | quot = (cyc >> time_shift); | ||
1508 | rem = cyc & ((1 << time_shift) - 1); | ||
1509 | delta = time_offset + quot * time_mult + | ||
1510 | ((rem * time_mult) >> time_shift); | ||
1511 | |||
1512 | enabled += delta; | ||
1513 | if (idx) | ||
1514 | running += delta; | ||
1515 | |||
1516 | quot = count / running; | ||
1517 | rem = count % running; | ||
1518 | count = quot * enabled + (rem * enabled) / running; | ||
1519 | } | ||
1520 | |||
1521 | return count; | ||
1522 | } | ||
1523 | |||
1524 | /* | ||
1525 | * If the RDPMC instruction faults then signal this back to the test parent task: | ||
1526 | */ | ||
1527 | static void segfault_handler(int sig __used, siginfo_t *info __used, void *uc __used) | ||
1528 | { | ||
1529 | exit(-1); | ||
1530 | } | ||
1531 | |||
1532 | static int __test__rdpmc(void) | ||
1533 | { | ||
1534 | long page_size = sysconf(_SC_PAGE_SIZE); | ||
1535 | volatile int tmp = 0; | ||
1536 | u64 i, loops = 1000; | ||
1537 | int n; | ||
1538 | int fd; | ||
1539 | void *addr; | ||
1540 | struct perf_event_attr attr = { | ||
1541 | .type = PERF_TYPE_HARDWARE, | ||
1542 | .config = PERF_COUNT_HW_INSTRUCTIONS, | ||
1543 | .exclude_kernel = 1, | ||
1544 | }; | ||
1545 | u64 delta_sum = 0; | ||
1546 | struct sigaction sa; | ||
1547 | |||
1548 | sigfillset(&sa.sa_mask); | ||
1549 | sa.sa_sigaction = segfault_handler; | ||
1550 | sigaction(SIGSEGV, &sa, NULL); | ||
1551 | |||
1552 | fprintf(stderr, "\n\n"); | ||
1553 | |||
1554 | fd = sys_perf_event_open(&attr, 0, -1, -1, 0); | ||
1555 | if (fd < 0) { | ||
1556 | die("Error: sys_perf_event_open() syscall returned " | ||
1557 | "with %d (%s)\n", fd, strerror(errno)); | ||
1558 | } | ||
1559 | |||
1560 | addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); | ||
1561 | if (addr == (void *)(-1)) { | ||
1562 | die("Error: mmap() syscall returned " | ||
1563 | "with (%s)\n", strerror(errno)); | ||
1564 | } | ||
1565 | |||
1566 | for (n = 0; n < 6; n++) { | ||
1567 | u64 stamp, now, delta; | ||
1568 | |||
1569 | stamp = mmap_read_self(addr); | ||
1570 | |||
1571 | for (i = 0; i < loops; i++) | ||
1572 | tmp++; | ||
1573 | |||
1574 | now = mmap_read_self(addr); | ||
1575 | loops *= 10; | ||
1576 | |||
1577 | delta = now - stamp; | ||
1578 | fprintf(stderr, "%14d: %14Lu\n", n, (long long)delta); | ||
1579 | |||
1580 | delta_sum += delta; | ||
1581 | } | ||
1582 | |||
1583 | munmap(addr, page_size); | ||
1584 | close(fd); | ||
1585 | |||
1586 | fprintf(stderr, " "); | ||
1587 | |||
1588 | if (!delta_sum) | ||
1589 | return -1; | ||
1590 | |||
1591 | return 0; | ||
1592 | } | ||
1593 | |||
1594 | static int test__rdpmc(void) | ||
1595 | { | ||
1596 | int status = 0; | ||
1597 | int wret = 0; | ||
1598 | int ret; | ||
1599 | int pid; | ||
1600 | |||
1601 | pid = fork(); | ||
1602 | if (pid < 0) | ||
1603 | return -1; | ||
1604 | |||
1605 | if (!pid) { | ||
1606 | ret = __test__rdpmc(); | ||
1607 | |||
1608 | exit(ret); | ||
1609 | } | ||
1610 | |||
1611 | wret = waitpid(pid, &status, 0); | ||
1612 | if (wret < 0 || status) | ||
1613 | return -1; | ||
1614 | |||
1615 | return 0; | ||
1616 | } | ||
1617 | |||
1618 | #endif | ||
1619 | |||
1620 | static int test__perf_pmu(void) | ||
1621 | { | ||
1622 | return perf_pmu__test(); | ||
1623 | } | ||
1624 | |||
1299 | static struct test { | 1625 | static struct test { |
1300 | const char *desc; | 1626 | const char *desc; |
1301 | int (*func)(void); | 1627 | int (*func)(void); |
@@ -1320,11 +1646,21 @@ static struct test { | |||
1320 | .desc = "parse events tests", | 1646 | .desc = "parse events tests", |
1321 | .func = test__parse_events, | 1647 | .func = test__parse_events, |
1322 | }, | 1648 | }, |
1649 | #if defined(__x86_64__) || defined(__i386__) | ||
1650 | { | ||
1651 | .desc = "x86 rdpmc test", | ||
1652 | .func = test__rdpmc, | ||
1653 | }, | ||
1654 | #endif | ||
1323 | { | 1655 | { |
1324 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", | 1656 | .desc = "Validate PERF_RECORD_* events & perf_sample fields", |
1325 | .func = test__PERF_RECORD, | 1657 | .func = test__PERF_RECORD, |
1326 | }, | 1658 | }, |
1327 | { | 1659 | { |
1660 | .desc = "Test perf pmu format parsing", | ||
1661 | .func = test__perf_pmu, | ||
1662 | }, | ||
1663 | { | ||
1328 | .func = NULL, | 1664 | .func = NULL, |
1329 | }, | 1665 | }, |
1330 | }; | 1666 | }; |
@@ -1396,7 +1732,7 @@ int cmd_test(int argc, const char **argv, const char *prefix __used) | |||
1396 | NULL, | 1732 | NULL, |
1397 | }; | 1733 | }; |
1398 | const struct option test_options[] = { | 1734 | const struct option test_options[] = { |
1399 | OPT_INTEGER('v', "verbose", &verbose, | 1735 | OPT_INCR('v', "verbose", &verbose, |
1400 | "be more verbose (show symbol address, etc)"), | 1736 | "be more verbose (show symbol address, etc)"), |
1401 | OPT_END() | 1737 | OPT_END() |
1402 | }; | 1738 | }; |
@@ -1412,7 +1748,5 @@ int cmd_test(int argc, const char **argv, const char *prefix __used) | |||
1412 | if (symbol__init() < 0) | 1748 | if (symbol__init() < 0) |
1413 | return -1; | 1749 | return -1; |
1414 | 1750 | ||
1415 | setup_pager(); | ||
1416 | |||
1417 | return __cmd_test(argc, argv); | 1751 | return __cmd_test(argc, argv); |
1418 | } | 1752 | } |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 4f81eeb99875..e3c63aef8efc 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
@@ -64,7 +64,6 @@ | |||
64 | #include <linux/unistd.h> | 64 | #include <linux/unistd.h> |
65 | #include <linux/types.h> | 65 | #include <linux/types.h> |
66 | 66 | ||
67 | |||
68 | void get_term_dimensions(struct winsize *ws) | 67 | void get_term_dimensions(struct winsize *ws) |
69 | { | 68 | { |
70 | char *s = getenv("LINES"); | 69 | char *s = getenv("LINES"); |
@@ -89,8 +88,6 @@ void get_term_dimensions(struct winsize *ws) | |||
89 | 88 | ||
90 | static void perf_top__update_print_entries(struct perf_top *top) | 89 | static void perf_top__update_print_entries(struct perf_top *top) |
91 | { | 90 | { |
92 | top->print_entries = top->winsize.ws_row; | ||
93 | |||
94 | if (top->print_entries > 9) | 91 | if (top->print_entries > 9) |
95 | top->print_entries -= 9; | 92 | top->print_entries -= 9; |
96 | } | 93 | } |
@@ -100,6 +97,13 @@ static void perf_top__sig_winch(int sig __used, siginfo_t *info __used, void *ar | |||
100 | struct perf_top *top = arg; | 97 | struct perf_top *top = arg; |
101 | 98 | ||
102 | get_term_dimensions(&top->winsize); | 99 | get_term_dimensions(&top->winsize); |
100 | if (!top->print_entries | ||
101 | || (top->print_entries+4) > top->winsize.ws_row) { | ||
102 | top->print_entries = top->winsize.ws_row; | ||
103 | } else { | ||
104 | top->print_entries += 4; | ||
105 | top->winsize.ws_row = top->print_entries; | ||
106 | } | ||
103 | perf_top__update_print_entries(top); | 107 | perf_top__update_print_entries(top); |
104 | } | 108 | } |
105 | 109 | ||
@@ -235,7 +239,6 @@ static struct hist_entry *perf_evsel__add_hist_entry(struct perf_evsel *evsel, | |||
235 | if (he == NULL) | 239 | if (he == NULL) |
236 | return NULL; | 240 | return NULL; |
237 | 241 | ||
238 | evsel->hists.stats.total_period += sample->period; | ||
239 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); | 242 | hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE); |
240 | return he; | 243 | return he; |
241 | } | 244 | } |
@@ -454,8 +457,10 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) | |||
454 | }; | 457 | }; |
455 | perf_top__sig_winch(SIGWINCH, NULL, top); | 458 | perf_top__sig_winch(SIGWINCH, NULL, top); |
456 | sigaction(SIGWINCH, &act, NULL); | 459 | sigaction(SIGWINCH, &act, NULL); |
457 | } else | 460 | } else { |
461 | perf_top__sig_winch(SIGWINCH, NULL, top); | ||
458 | signal(SIGWINCH, SIG_DFL); | 462 | signal(SIGWINCH, SIG_DFL); |
463 | } | ||
459 | break; | 464 | break; |
460 | case 'E': | 465 | case 'E': |
461 | if (top->evlist->nr_entries > 1) { | 466 | if (top->evlist->nr_entries > 1) { |
@@ -538,10 +543,20 @@ static void perf_top__sort_new_samples(void *arg) | |||
538 | 543 | ||
539 | static void *display_thread_tui(void *arg) | 544 | static void *display_thread_tui(void *arg) |
540 | { | 545 | { |
546 | struct perf_evsel *pos; | ||
541 | struct perf_top *top = arg; | 547 | struct perf_top *top = arg; |
542 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; | 548 | const char *help = "For a higher level overview, try: perf top --sort comm,dso"; |
543 | 549 | ||
544 | perf_top__sort_new_samples(top); | 550 | perf_top__sort_new_samples(top); |
551 | |||
552 | /* | ||
553 | * Initialize the uid_filter_str, in the future the TUI will allow | ||
554 | * Zooming in/out UIDs. For now juse use whatever the user passed | ||
555 | * via --uid. | ||
556 | */ | ||
557 | list_for_each_entry(pos, &top->evlist->entries, node) | ||
558 | pos->hists.uid_filter_str = top->uid_str; | ||
559 | |||
545 | perf_evlist__tui_browse_hists(top->evlist, help, | 560 | perf_evlist__tui_browse_hists(top->evlist, help, |
546 | perf_top__sort_new_samples, | 561 | perf_top__sort_new_samples, |
547 | top, top->delay_secs); | 562 | top, top->delay_secs); |
@@ -662,6 +677,12 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
662 | return; | 677 | return; |
663 | } | 678 | } |
664 | 679 | ||
680 | if (!machine) { | ||
681 | pr_err("%u unprocessable samples recorded.", | ||
682 | top->session->hists.stats.nr_unprocessable_samples++); | ||
683 | return; | ||
684 | } | ||
685 | |||
665 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) | 686 | if (event->header.misc & PERF_RECORD_MISC_EXACT_IP) |
666 | top->exact_samples++; | 687 | top->exact_samples++; |
667 | 688 | ||
@@ -851,8 +872,11 @@ static void perf_top__start_counters(struct perf_top *top) | |||
851 | attr->mmap = 1; | 872 | attr->mmap = 1; |
852 | attr->comm = 1; | 873 | attr->comm = 1; |
853 | attr->inherit = top->inherit; | 874 | attr->inherit = top->inherit; |
875 | fallback_missing_features: | ||
876 | if (top->exclude_guest_missing) | ||
877 | attr->exclude_guest = attr->exclude_host = 0; | ||
854 | retry_sample_id: | 878 | retry_sample_id: |
855 | attr->sample_id_all = top->sample_id_all_avail ? 1 : 0; | 879 | attr->sample_id_all = top->sample_id_all_missing ? 0 : 1; |
856 | try_again: | 880 | try_again: |
857 | if (perf_evsel__open(counter, top->evlist->cpus, | 881 | if (perf_evsel__open(counter, top->evlist->cpus, |
858 | top->evlist->threads, top->group, | 882 | top->evlist->threads, top->group, |
@@ -862,12 +886,20 @@ try_again: | |||
862 | if (err == EPERM || err == EACCES) { | 886 | if (err == EPERM || err == EACCES) { |
863 | ui__error_paranoid(); | 887 | ui__error_paranoid(); |
864 | goto out_err; | 888 | goto out_err; |
865 | } else if (err == EINVAL && top->sample_id_all_avail) { | 889 | } else if (err == EINVAL) { |
866 | /* | 890 | if (!top->exclude_guest_missing && |
867 | * Old kernel, no attr->sample_id_type_all field | 891 | (attr->exclude_guest || attr->exclude_host)) { |
868 | */ | 892 | pr_debug("Old kernel, cannot exclude " |
869 | top->sample_id_all_avail = false; | 893 | "guest or host samples.\n"); |
870 | goto retry_sample_id; | 894 | top->exclude_guest_missing = true; |
895 | goto fallback_missing_features; | ||
896 | } else if (!top->sample_id_all_missing) { | ||
897 | /* | ||
898 | * Old kernel, no attr->sample_id_type_all field | ||
899 | */ | ||
900 | top->sample_id_all_missing = true; | ||
901 | goto retry_sample_id; | ||
902 | } | ||
871 | } | 903 | } |
872 | /* | 904 | /* |
873 | * If it's cycles then fall back to hrtimer | 905 | * If it's cycles then fall back to hrtimer |
@@ -889,6 +921,10 @@ try_again: | |||
889 | ui__warning("The %s event is not supported.\n", | 921 | ui__warning("The %s event is not supported.\n", |
890 | event_name(counter)); | 922 | event_name(counter)); |
891 | goto out_err; | 923 | goto out_err; |
924 | } else if (err == EMFILE) { | ||
925 | ui__warning("Too many events are opened.\n" | ||
926 | "Try again after reducing the number of events\n"); | ||
927 | goto out_err; | ||
892 | } | 928 | } |
893 | 929 | ||
894 | ui__warning("The sys_perf_event_open() syscall " | 930 | ui__warning("The sys_perf_event_open() syscall " |
@@ -946,7 +982,7 @@ static int __cmd_top(struct perf_top *top) | |||
946 | if (ret) | 982 | if (ret) |
947 | goto out_delete; | 983 | goto out_delete; |
948 | 984 | ||
949 | if (top->target_tid != -1) | 985 | if (top->target_tid || top->uid != UINT_MAX) |
950 | perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, | 986 | perf_event__synthesize_thread_map(&top->tool, top->evlist->threads, |
951 | perf_event__process, | 987 | perf_event__process, |
952 | &top->session->host_machine); | 988 | &top->session->host_machine); |
@@ -1084,10 +1120,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1084 | struct perf_top top = { | 1120 | struct perf_top top = { |
1085 | .count_filter = 5, | 1121 | .count_filter = 5, |
1086 | .delay_secs = 2, | 1122 | .delay_secs = 2, |
1087 | .target_pid = -1, | 1123 | .uid = UINT_MAX, |
1088 | .target_tid = -1, | ||
1089 | .freq = 1000, /* 1 KHz */ | 1124 | .freq = 1000, /* 1 KHz */ |
1090 | .sample_id_all_avail = true, | ||
1091 | .mmap_pages = 128, | 1125 | .mmap_pages = 128, |
1092 | .sym_pcnt_filter = 5, | 1126 | .sym_pcnt_filter = 5, |
1093 | }; | 1127 | }; |
@@ -1098,9 +1132,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1098 | parse_events_option), | 1132 | parse_events_option), |
1099 | OPT_INTEGER('c', "count", &top.default_interval, | 1133 | OPT_INTEGER('c', "count", &top.default_interval, |
1100 | "event period to sample"), | 1134 | "event period to sample"), |
1101 | OPT_INTEGER('p', "pid", &top.target_pid, | 1135 | OPT_STRING('p', "pid", &top.target_pid, "pid", |
1102 | "profile events on existing process id"), | 1136 | "profile events on existing process id"), |
1103 | OPT_INTEGER('t', "tid", &top.target_tid, | 1137 | OPT_STRING('t', "tid", &top.target_tid, "tid", |
1104 | "profile events on existing thread id"), | 1138 | "profile events on existing thread id"), |
1105 | OPT_BOOLEAN('a', "all-cpus", &top.system_wide, | 1139 | OPT_BOOLEAN('a', "all-cpus", &top.system_wide, |
1106 | "system-wide collection from all CPUs"), | 1140 | "system-wide collection from all CPUs"), |
@@ -1159,6 +1193,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1159 | "Display raw encoding of assembly instructions (default)"), | 1193 | "Display raw encoding of assembly instructions (default)"), |
1160 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", | 1194 | OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style", |
1161 | "Specify disassembler style (e.g. -M intel for intel syntax)"), | 1195 | "Specify disassembler style (e.g. -M intel for intel syntax)"), |
1196 | OPT_STRING('u', "uid", &top.uid_str, "user", "user to profile"), | ||
1162 | OPT_END() | 1197 | OPT_END() |
1163 | }; | 1198 | }; |
1164 | 1199 | ||
@@ -1184,18 +1219,22 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1184 | 1219 | ||
1185 | setup_browser(false); | 1220 | setup_browser(false); |
1186 | 1221 | ||
1222 | top.uid = parse_target_uid(top.uid_str, top.target_tid, top.target_pid); | ||
1223 | if (top.uid_str != NULL && top.uid == UINT_MAX - 1) | ||
1224 | goto out_delete_evlist; | ||
1225 | |||
1187 | /* CPU and PID are mutually exclusive */ | 1226 | /* CPU and PID are mutually exclusive */ |
1188 | if (top.target_tid > 0 && top.cpu_list) { | 1227 | if (top.target_tid && top.cpu_list) { |
1189 | printf("WARNING: PID switch overriding CPU\n"); | 1228 | printf("WARNING: PID switch overriding CPU\n"); |
1190 | sleep(1); | 1229 | sleep(1); |
1191 | top.cpu_list = NULL; | 1230 | top.cpu_list = NULL; |
1192 | } | 1231 | } |
1193 | 1232 | ||
1194 | if (top.target_pid != -1) | 1233 | if (top.target_pid) |
1195 | top.target_tid = top.target_pid; | 1234 | top.target_tid = top.target_pid; |
1196 | 1235 | ||
1197 | if (perf_evlist__create_maps(top.evlist, top.target_pid, | 1236 | if (perf_evlist__create_maps(top.evlist, top.target_pid, |
1198 | top.target_tid, top.cpu_list) < 0) | 1237 | top.target_tid, top.uid, top.cpu_list) < 0) |
1199 | usage_with_options(top_usage, options); | 1238 | usage_with_options(top_usage, options); |
1200 | 1239 | ||
1201 | if (!top.evlist->nr_entries && | 1240 | if (!top.evlist->nr_entries && |
@@ -1259,6 +1298,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used) | |||
1259 | 1298 | ||
1260 | status = __cmd_top(&top); | 1299 | status = __cmd_top(&top); |
1261 | 1300 | ||
1301 | out_delete_evlist: | ||
1262 | perf_evlist__delete(top.evlist); | 1302 | perf_evlist__delete(top.evlist); |
1263 | 1303 | ||
1264 | return status; | 1304 | return status; |
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak index 6170fd2531b5..d9084e03ce56 100644 --- a/tools/perf/config/feature-tests.mak +++ b/tools/perf/config/feature-tests.mak | |||
@@ -65,6 +65,21 @@ int main(void) | |||
65 | endef | 65 | endef |
66 | endif | 66 | endif |
67 | 67 | ||
68 | ifndef NO_GTK2 | ||
69 | define SOURCE_GTK2 | ||
70 | #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" | ||
71 | #include <gtk/gtk.h> | ||
72 | #pragma GCC diagnostic error \"-Wstrict-prototypes\" | ||
73 | |||
74 | int main(int argc, char *argv[]) | ||
75 | { | ||
76 | gtk_init(&argc, &argv); | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | endef | ||
81 | endif | ||
82 | |||
68 | ifndef NO_LIBPERL | 83 | ifndef NO_LIBPERL |
69 | define SOURCE_PERL_EMBED | 84 | define SOURCE_PERL_EMBED |
70 | #include <EXTERN.h> | 85 | #include <EXTERN.h> |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 64f8bee31ced..89e3355ab173 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
@@ -10,6 +10,9 @@ void get_term_dimensions(struct winsize *ws); | |||
10 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") | 10 | #define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory") |
11 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 11 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
12 | #define CPUINFO_PROC "model name" | 12 | #define CPUINFO_PROC "model name" |
13 | #ifndef __NR_perf_event_open | ||
14 | # define __NR_perf_event_open 336 | ||
15 | #endif | ||
13 | #endif | 16 | #endif |
14 | 17 | ||
15 | #if defined(__x86_64__) | 18 | #if defined(__x86_64__) |
@@ -17,6 +20,9 @@ void get_term_dimensions(struct winsize *ws); | |||
17 | #define rmb() asm volatile("lfence" ::: "memory") | 20 | #define rmb() asm volatile("lfence" ::: "memory") |
18 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); | 21 | #define cpu_relax() asm volatile("rep; nop" ::: "memory"); |
19 | #define CPUINFO_PROC "model name" | 22 | #define CPUINFO_PROC "model name" |
23 | #ifndef __NR_perf_event_open | ||
24 | # define __NR_perf_event_open 298 | ||
25 | #endif | ||
20 | #endif | 26 | #endif |
21 | 27 | ||
22 | #ifdef __powerpc__ | 28 | #ifdef __powerpc__ |
@@ -167,7 +173,6 @@ sys_perf_event_open(struct perf_event_attr *attr, | |||
167 | pid_t pid, int cpu, int group_fd, | 173 | pid_t pid, int cpu, int group_fd, |
168 | unsigned long flags) | 174 | unsigned long flags) |
169 | { | 175 | { |
170 | attr->size = sizeof(*attr); | ||
171 | return syscall(__NR_perf_event_open, attr, pid, cpu, | 176 | return syscall(__NR_perf_event_open, attr, pid, cpu, |
172 | group_fd, flags); | 177 | group_fd, flags); |
173 | } | 178 | } |
@@ -180,14 +185,32 @@ struct ip_callchain { | |||
180 | u64 ips[0]; | 185 | u64 ips[0]; |
181 | }; | 186 | }; |
182 | 187 | ||
188 | struct branch_flags { | ||
189 | u64 mispred:1; | ||
190 | u64 predicted:1; | ||
191 | u64 reserved:62; | ||
192 | }; | ||
193 | |||
194 | struct branch_entry { | ||
195 | u64 from; | ||
196 | u64 to; | ||
197 | struct branch_flags flags; | ||
198 | }; | ||
199 | |||
200 | struct branch_stack { | ||
201 | u64 nr; | ||
202 | struct branch_entry entries[0]; | ||
203 | }; | ||
204 | |||
183 | extern bool perf_host, perf_guest; | 205 | extern bool perf_host, perf_guest; |
184 | extern const char perf_version_string[]; | 206 | extern const char perf_version_string[]; |
185 | 207 | ||
186 | void pthread__unblock_sigwinch(void); | 208 | void pthread__unblock_sigwinch(void); |
187 | 209 | ||
188 | struct perf_record_opts { | 210 | struct perf_record_opts { |
189 | pid_t target_pid; | 211 | const char *target_pid; |
190 | pid_t target_tid; | 212 | const char *target_tid; |
213 | uid_t uid; | ||
191 | bool call_graph; | 214 | bool call_graph; |
192 | bool group; | 215 | bool group; |
193 | bool inherit_stat; | 216 | bool inherit_stat; |
@@ -198,12 +221,14 @@ struct perf_record_opts { | |||
198 | bool raw_samples; | 221 | bool raw_samples; |
199 | bool sample_address; | 222 | bool sample_address; |
200 | bool sample_time; | 223 | bool sample_time; |
201 | bool sample_id_all_avail; | 224 | bool sample_id_all_missing; |
225 | bool exclude_guest_missing; | ||
202 | bool system_wide; | 226 | bool system_wide; |
203 | bool period; | 227 | bool period; |
204 | unsigned int freq; | 228 | unsigned int freq; |
205 | unsigned int mmap_pages; | 229 | unsigned int mmap_pages; |
206 | unsigned int user_freq; | 230 | unsigned int user_freq; |
231 | int branch_stack; | ||
207 | u64 default_interval; | 232 | u64 default_interval; |
208 | u64 user_interval; | 233 | u64 user_interval; |
209 | const char *cpu_list; | 234 | const char *cpu_list; |
diff --git a/tools/perf/python/twatch.py b/tools/perf/python/twatch.py index df638c438a9f..b11cca584238 100755 --- a/tools/perf/python/twatch.py +++ b/tools/perf/python/twatch.py | |||
@@ -19,7 +19,7 @@ def main(): | |||
19 | cpus = perf.cpu_map() | 19 | cpus = perf.cpu_map() |
20 | threads = perf.thread_map() | 20 | threads = perf.thread_map() |
21 | evsel = perf.evsel(task = 1, comm = 1, mmap = 0, | 21 | evsel = perf.evsel(task = 1, comm = 1, mmap = 0, |
22 | wakeup_events = 1, sample_period = 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 | perf.SAMPLE_TID) |
25 | evsel.open(cpus = cpus, threads = threads); | 25 | evsel.open(cpus = cpus, threads = threads); |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 011ed2676604..199f69ec656f 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -28,8 +28,8 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym) | |||
28 | int symbol__alloc_hist(struct symbol *sym) | 28 | int symbol__alloc_hist(struct symbol *sym) |
29 | { | 29 | { |
30 | struct annotation *notes = symbol__annotation(sym); | 30 | struct annotation *notes = symbol__annotation(sym); |
31 | size_t sizeof_sym_hist = (sizeof(struct sym_hist) + | 31 | const size_t size = sym->end - sym->start + 1; |
32 | (sym->end - sym->start) * sizeof(u64)); | 32 | size_t sizeof_sym_hist = (sizeof(struct sym_hist) + size * sizeof(u64)); |
33 | 33 | ||
34 | notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); | 34 | notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist); |
35 | if (notes->src == NULL) | 35 | if (notes->src == NULL) |
@@ -64,7 +64,7 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map, | |||
64 | 64 | ||
65 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); | 65 | pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); |
66 | 66 | ||
67 | if (addr >= sym->end) | 67 | if (addr > sym->end) |
68 | return 0; | 68 | return 0; |
69 | 69 | ||
70 | offset = addr - sym->start; | 70 | offset = addr - sym->start; |
@@ -315,7 +315,7 @@ fallback: | |||
315 | "Please use:\n\n" | 315 | "Please use:\n\n" |
316 | " perf buildid-cache -av vmlinux\n\n" | 316 | " perf buildid-cache -av vmlinux\n\n" |
317 | "or:\n\n" | 317 | "or:\n\n" |
318 | " --vmlinux vmlinux", | 318 | " --vmlinux vmlinux\n", |
319 | sym->name, build_id_msg ?: ""); | 319 | sym->name, build_id_msg ?: ""); |
320 | goto out_free_filename; | 320 | goto out_free_filename; |
321 | } | 321 | } |
@@ -408,7 +408,7 @@ static int symbol__get_source_line(struct symbol *sym, struct map *map, | |||
408 | if (!notes->src->lines) | 408 | if (!notes->src->lines) |
409 | return -1; | 409 | return -1; |
410 | 410 | ||
411 | start = map->unmap_ip(map, sym->start); | 411 | start = map__rip_2objdump(map, sym->start); |
412 | 412 | ||
413 | for (i = 0; i < len; i++) { | 413 | for (i = 0; i < len; i++) { |
414 | char *path = NULL; | 414 | char *path = NULL; |
diff --git a/tools/perf/util/bitmap.c b/tools/perf/util/bitmap.c index 5e230acae1e9..0a1adc1111fd 100644 --- a/tools/perf/util/bitmap.c +++ b/tools/perf/util/bitmap.c | |||
@@ -19,3 +19,13 @@ int __bitmap_weight(const unsigned long *bitmap, int bits) | |||
19 | 19 | ||
20 | return w; | 20 | return w; |
21 | } | 21 | } |
22 | |||
23 | void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, | ||
24 | const unsigned long *bitmap2, int bits) | ||
25 | { | ||
26 | int k; | ||
27 | int nr = BITS_TO_LONGS(bits); | ||
28 | |||
29 | for (k = 0; k < nr; k++) | ||
30 | dst[k] = bitmap1[k] | bitmap2[k]; | ||
31 | } | ||
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index fc5e5a09d5b9..8dd224df3e54 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -45,6 +45,18 @@ void setup_browser(bool fallback_to_pager); | |||
45 | void exit_browser(bool wait_for_ok); | 45 | void exit_browser(bool wait_for_ok); |
46 | #endif | 46 | #endif |
47 | 47 | ||
48 | #ifdef NO_GTK2_SUPPORT | ||
49 | static inline void perf_gtk_setup_browser(int argc __used, const char *argv[] __used, bool fallback_to_pager) | ||
50 | { | ||
51 | if (fallback_to_pager) | ||
52 | setup_pager(); | ||
53 | } | ||
54 | static inline void perf_gtk_exit_browser(bool wait_for_ok __used) {} | ||
55 | #else | ||
56 | void perf_gtk_setup_browser(int argc, const char *argv[], bool fallback_to_pager); | ||
57 | void perf_gtk_exit_browser(bool wait_for_ok); | ||
58 | #endif | ||
59 | |||
48 | char *alias_lookup(const char *alias); | 60 | char *alias_lookup(const char *alias); |
49 | int split_cmdline(char *cmdline, const char ***argv); | 61 | int split_cmdline(char *cmdline, const char ***argv); |
50 | 62 | ||
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 521c38a79190..11e46da17bbb 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
@@ -1,3 +1,4 @@ | |||
1 | #include <linux/kernel.h> | ||
1 | #include "cache.h" | 2 | #include "cache.h" |
2 | #include "color.h" | 3 | #include "color.h" |
3 | 4 | ||
@@ -182,12 +183,12 @@ static int __color_vsnprintf(char *bf, size_t size, const char *color, | |||
182 | } | 183 | } |
183 | 184 | ||
184 | if (perf_use_color_default && *color) | 185 | if (perf_use_color_default && *color) |
185 | r += snprintf(bf, size, "%s", color); | 186 | r += scnprintf(bf, size, "%s", color); |
186 | r += vsnprintf(bf + r, size - r, fmt, args); | 187 | r += vscnprintf(bf + r, size - r, fmt, args); |
187 | if (perf_use_color_default && *color) | 188 | if (perf_use_color_default && *color) |
188 | r += snprintf(bf + r, size - r, "%s", PERF_COLOR_RESET); | 189 | r += scnprintf(bf + r, size - r, "%s", PERF_COLOR_RESET); |
189 | if (trail) | 190 | if (trail) |
190 | r += snprintf(bf + r, size - r, "%s", trail); | 191 | r += scnprintf(bf + r, size - r, "%s", trail); |
191 | return r; | 192 | return r; |
192 | } | 193 | } |
193 | 194 | ||
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 6893eec693ab..adc72f09914d 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c | |||
@@ -166,6 +166,17 @@ out: | |||
166 | return cpus; | 166 | return cpus; |
167 | } | 167 | } |
168 | 168 | ||
169 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp) | ||
170 | { | ||
171 | int i; | ||
172 | size_t printed = fprintf(fp, "%d cpu%s: ", | ||
173 | map->nr, map->nr > 1 ? "s" : ""); | ||
174 | for (i = 0; i < map->nr; ++i) | ||
175 | printed += fprintf(fp, "%s%d", i ? ", " : "", map->map[i]); | ||
176 | |||
177 | return printed + fprintf(fp, "\n"); | ||
178 | } | ||
179 | |||
169 | struct cpu_map *cpu_map__dummy_new(void) | 180 | struct cpu_map *cpu_map__dummy_new(void) |
170 | { | 181 | { |
171 | struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int)); | 182 | struct cpu_map *cpus = malloc(sizeof(*cpus) + sizeof(int)); |
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 072c0a374794..c41518573c6a 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h | |||
@@ -1,6 +1,8 @@ | |||
1 | #ifndef __PERF_CPUMAP_H | 1 | #ifndef __PERF_CPUMAP_H |
2 | #define __PERF_CPUMAP_H | 2 | #define __PERF_CPUMAP_H |
3 | 3 | ||
4 | #include <stdio.h> | ||
5 | |||
4 | struct cpu_map { | 6 | struct cpu_map { |
5 | int nr; | 7 | int nr; |
6 | int map[]; | 8 | int map[]; |
@@ -10,4 +12,6 @@ struct cpu_map *cpu_map__new(const char *cpu_list); | |||
10 | struct cpu_map *cpu_map__dummy_new(void); | 12 | struct cpu_map *cpu_map__dummy_new(void); |
11 | void cpu_map__delete(struct cpu_map *map); | 13 | void cpu_map__delete(struct cpu_map *map); |
12 | 14 | ||
15 | size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp); | ||
16 | |||
13 | #endif /* __PERF_CPUMAP_H */ | 17 | #endif /* __PERF_CPUMAP_H */ |
diff --git a/tools/perf/util/ctype.c b/tools/perf/util/ctype.c index 35073621e5de..aada3ac5e891 100644 --- a/tools/perf/util/ctype.c +++ b/tools/perf/util/ctype.c | |||
@@ -3,7 +3,7 @@ | |||
3 | * | 3 | * |
4 | * No surprises, and works with signed and unsigned chars. | 4 | * No surprises, and works with signed and unsigned chars. |
5 | */ | 5 | */ |
6 | #include "cache.h" | 6 | #include "util.h" |
7 | 7 | ||
8 | enum { | 8 | enum { |
9 | S = GIT_SPACE, | 9 | S = GIT_SPACE, |
diff --git a/tools/perf/util/debugfs.c b/tools/perf/util/debugfs.c index ffc35e748e89..dd8b19319c03 100644 --- a/tools/perf/util/debugfs.c +++ b/tools/perf/util/debugfs.c | |||
@@ -15,32 +15,6 @@ static const char *debugfs_known_mountpoints[] = { | |||
15 | 0, | 15 | 0, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | /* use this to force a umount */ | ||
19 | void debugfs_force_cleanup(void) | ||
20 | { | ||
21 | debugfs_find_mountpoint(); | ||
22 | debugfs_premounted = 0; | ||
23 | debugfs_umount(); | ||
24 | } | ||
25 | |||
26 | /* construct a full path to a debugfs element */ | ||
27 | int debugfs_make_path(const char *element, char *buffer, int size) | ||
28 | { | ||
29 | int len; | ||
30 | |||
31 | if (strlen(debugfs_mountpoint) == 0) { | ||
32 | buffer[0] = '\0'; | ||
33 | return -1; | ||
34 | } | ||
35 | |||
36 | len = strlen(debugfs_mountpoint) + strlen(element) + 1; | ||
37 | if (len >= size) | ||
38 | return len+1; | ||
39 | |||
40 | snprintf(buffer, size-1, "%s/%s", debugfs_mountpoint, element); | ||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | static int debugfs_found; | 18 | static int debugfs_found; |
45 | 19 | ||
46 | /* find the path to the mounted debugfs */ | 20 | /* find the path to the mounted debugfs */ |
@@ -97,17 +71,6 @@ int debugfs_valid_mountpoint(const char *debugfs) | |||
97 | return 0; | 71 | return 0; |
98 | } | 72 | } |
99 | 73 | ||
100 | |||
101 | int debugfs_valid_entry(const char *path) | ||
102 | { | ||
103 | struct stat st; | ||
104 | |||
105 | if (stat(path, &st)) | ||
106 | return -errno; | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static void debugfs_set_tracing_events_path(const char *mountpoint) | 74 | static void debugfs_set_tracing_events_path(const char *mountpoint) |
112 | { | 75 | { |
113 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", | 76 | snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s", |
@@ -149,107 +112,3 @@ void debugfs_set_path(const char *mountpoint) | |||
149 | snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint); | 112 | snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint); |
150 | debugfs_set_tracing_events_path(mountpoint); | 113 | debugfs_set_tracing_events_path(mountpoint); |
151 | } | 114 | } |
152 | |||
153 | /* umount the debugfs */ | ||
154 | |||
155 | int debugfs_umount(void) | ||
156 | { | ||
157 | char umountcmd[128]; | ||
158 | int ret; | ||
159 | |||
160 | /* if it was already mounted, leave it */ | ||
161 | if (debugfs_premounted) | ||
162 | return 0; | ||
163 | |||
164 | /* make sure it's a valid mount point */ | ||
165 | ret = debugfs_valid_mountpoint(debugfs_mountpoint); | ||
166 | if (ret) | ||
167 | return ret; | ||
168 | |||
169 | snprintf(umountcmd, sizeof(umountcmd), | ||
170 | "/bin/umount %s", debugfs_mountpoint); | ||
171 | return system(umountcmd); | ||
172 | } | ||
173 | |||
174 | int debugfs_write(const char *entry, const char *value) | ||
175 | { | ||
176 | char path[PATH_MAX + 1]; | ||
177 | int ret, count; | ||
178 | int fd; | ||
179 | |||
180 | /* construct the path */ | ||
181 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
182 | |||
183 | /* verify that it exists */ | ||
184 | ret = debugfs_valid_entry(path); | ||
185 | if (ret) | ||
186 | return ret; | ||
187 | |||
188 | /* get how many chars we're going to write */ | ||
189 | count = strlen(value); | ||
190 | |||
191 | /* open the debugfs entry */ | ||
192 | fd = open(path, O_RDWR); | ||
193 | if (fd < 0) | ||
194 | return -errno; | ||
195 | |||
196 | while (count > 0) { | ||
197 | /* write it */ | ||
198 | ret = write(fd, value, count); | ||
199 | if (ret <= 0) { | ||
200 | if (ret == EAGAIN) | ||
201 | continue; | ||
202 | close(fd); | ||
203 | return -errno; | ||
204 | } | ||
205 | count -= ret; | ||
206 | } | ||
207 | |||
208 | /* close it */ | ||
209 | close(fd); | ||
210 | |||
211 | /* return success */ | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | /* | ||
216 | * read a debugfs entry | ||
217 | * returns the number of chars read or a negative errno | ||
218 | */ | ||
219 | int debugfs_read(const char *entry, char *buffer, size_t size) | ||
220 | { | ||
221 | char path[PATH_MAX + 1]; | ||
222 | int ret; | ||
223 | int fd; | ||
224 | |||
225 | /* construct the path */ | ||
226 | snprintf(path, sizeof(path), "%s/%s", debugfs_mountpoint, entry); | ||
227 | |||
228 | /* verify that it exists */ | ||
229 | ret = debugfs_valid_entry(path); | ||
230 | if (ret) | ||
231 | return ret; | ||
232 | |||
233 | /* open the debugfs entry */ | ||
234 | fd = open(path, O_RDONLY); | ||
235 | if (fd < 0) | ||
236 | return -errno; | ||
237 | |||
238 | do { | ||
239 | /* read it */ | ||
240 | ret = read(fd, buffer, size); | ||
241 | if (ret == 0) { | ||
242 | close(fd); | ||
243 | return EOF; | ||
244 | } | ||
245 | } while (ret < 0 && errno == EAGAIN); | ||
246 | |||
247 | /* close it */ | ||
248 | close(fd); | ||
249 | |||
250 | /* make *sure* there's a null character at the end */ | ||
251 | buffer[ret] = '\0'; | ||
252 | |||
253 | /* return the number of chars read */ | ||
254 | return ret; | ||
255 | } | ||
diff --git a/tools/perf/util/debugfs.h b/tools/perf/util/debugfs.h index 4a878f735eb0..68f3e87ec57f 100644 --- a/tools/perf/util/debugfs.h +++ b/tools/perf/util/debugfs.h | |||
@@ -3,14 +3,8 @@ | |||
3 | 3 | ||
4 | const char *debugfs_find_mountpoint(void); | 4 | const char *debugfs_find_mountpoint(void); |
5 | int debugfs_valid_mountpoint(const char *debugfs); | 5 | int debugfs_valid_mountpoint(const char *debugfs); |
6 | int debugfs_valid_entry(const char *path); | ||
7 | char *debugfs_mount(const char *mountpoint); | 6 | char *debugfs_mount(const char *mountpoint); |
8 | int debugfs_umount(void); | ||
9 | void debugfs_set_path(const char *mountpoint); | 7 | void debugfs_set_path(const char *mountpoint); |
10 | int debugfs_write(const char *entry, const char *value); | ||
11 | int debugfs_read(const char *entry, char *buffer, size_t size); | ||
12 | void debugfs_force_cleanup(void); | ||
13 | int debugfs_make_path(const char *element, char *buffer, int size); | ||
14 | 8 | ||
15 | extern char debugfs_mountpoint[]; | 9 | extern char debugfs_mountpoint[]; |
16 | extern char tracing_events_path[]; | 10 | extern char tracing_events_path[]; |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 73ddaf06b8e7..2a6f33cd888c 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -74,6 +74,7 @@ static pid_t perf_event__get_comm_tgid(pid_t pid, char *comm, size_t len) | |||
74 | if (size >= len) | 74 | if (size >= len) |
75 | size = len - 1; | 75 | size = len - 1; |
76 | memcpy(comm, name, size); | 76 | memcpy(comm, name, size); |
77 | comm[size] = '\0'; | ||
77 | 78 | ||
78 | } else if (memcmp(bf, "Tgid:", 5) == 0) { | 79 | } else if (memcmp(bf, "Tgid:", 5) == 0) { |
79 | char *tgids = bf + 5; | 80 | char *tgids = bf + 5; |
@@ -554,7 +555,7 @@ static int perf_event__process_kernel_mmap(struct perf_tool *tool __used, | |||
554 | 555 | ||
555 | is_kernel_mmap = memcmp(event->mmap.filename, | 556 | is_kernel_mmap = memcmp(event->mmap.filename, |
556 | kmmap_prefix, | 557 | kmmap_prefix, |
557 | strlen(kmmap_prefix)) == 0; | 558 | strlen(kmmap_prefix) - 1) == 0; |
558 | if (event->mmap.filename[0] == '/' || | 559 | if (event->mmap.filename[0] == '/' || |
559 | (!is_kernel_mmap && event->mmap.filename[0] == '[')) { | 560 | (!is_kernel_mmap && event->mmap.filename[0] == '[')) { |
560 | 561 | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index cbdeaad9c5e5..1b197280c621 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -81,6 +81,7 @@ struct perf_sample { | |||
81 | u32 raw_size; | 81 | u32 raw_size; |
82 | void *raw_data; | 82 | void *raw_data; |
83 | struct ip_callchain *callchain; | 83 | struct ip_callchain *callchain; |
84 | struct branch_stack *branch_stack; | ||
84 | }; | 85 | }; |
85 | 86 | ||
86 | #define BUILD_ID_SIZE 20 | 87 | #define BUILD_ID_SIZE 20 |
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index fa1837088ca8..1986d8051bd1 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -51,13 +51,15 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus, | |||
51 | void perf_evlist__config_attrs(struct perf_evlist *evlist, | 51 | void perf_evlist__config_attrs(struct perf_evlist *evlist, |
52 | struct perf_record_opts *opts) | 52 | struct perf_record_opts *opts) |
53 | { | 53 | { |
54 | struct perf_evsel *evsel; | 54 | struct perf_evsel *evsel, *first; |
55 | 55 | ||
56 | if (evlist->cpus->map[0] < 0) | 56 | if (evlist->cpus->map[0] < 0) |
57 | opts->no_inherit = true; | 57 | opts->no_inherit = true; |
58 | 58 | ||
59 | first = list_entry(evlist->entries.next, struct perf_evsel, node); | ||
60 | |||
59 | list_for_each_entry(evsel, &evlist->entries, node) { | 61 | list_for_each_entry(evsel, &evlist->entries, node) { |
60 | perf_evsel__config(evsel, opts); | 62 | perf_evsel__config(evsel, opts, first); |
61 | 63 | ||
62 | if (evlist->nr_entries > 1) | 64 | if (evlist->nr_entries > 1) |
63 | evsel->attr.sample_type |= PERF_SAMPLE_ID; | 65 | evsel->attr.sample_type |= PERF_SAMPLE_ID; |
@@ -97,9 +99,9 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry) | |||
97 | ++evlist->nr_entries; | 99 | ++evlist->nr_entries; |
98 | } | 100 | } |
99 | 101 | ||
100 | static void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | 102 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, |
101 | struct list_head *list, | 103 | struct list_head *list, |
102 | int nr_entries) | 104 | int nr_entries) |
103 | { | 105 | { |
104 | list_splice_tail(list, &evlist->entries); | 106 | list_splice_tail(list, &evlist->entries); |
105 | evlist->nr_entries += nr_entries; | 107 | evlist->nr_entries += nr_entries; |
@@ -111,8 +113,11 @@ int perf_evlist__add_default(struct perf_evlist *evlist) | |||
111 | .type = PERF_TYPE_HARDWARE, | 113 | .type = PERF_TYPE_HARDWARE, |
112 | .config = PERF_COUNT_HW_CPU_CYCLES, | 114 | .config = PERF_COUNT_HW_CPU_CYCLES, |
113 | }; | 115 | }; |
114 | struct perf_evsel *evsel = perf_evsel__new(&attr, 0); | 116 | struct perf_evsel *evsel; |
115 | 117 | ||
118 | event_attr_init(&attr); | ||
119 | |||
120 | evsel = perf_evsel__new(&attr, 0); | ||
116 | if (evsel == NULL) | 121 | if (evsel == NULL) |
117 | goto error; | 122 | goto error; |
118 | 123 | ||
@@ -346,6 +351,10 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id) | |||
346 | hlist_for_each_entry(sid, pos, head, node) | 351 | hlist_for_each_entry(sid, pos, head, node) |
347 | if (sid->id == id) | 352 | if (sid->id == id) |
348 | return sid->evsel; | 353 | return sid->evsel; |
354 | |||
355 | if (!perf_evlist__sample_id_all(evlist)) | ||
356 | return list_entry(evlist->entries.next, struct perf_evsel, node); | ||
357 | |||
349 | return NULL; | 358 | return NULL; |
350 | } | 359 | } |
351 | 360 | ||
@@ -590,15 +599,15 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
590 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); | 599 | return perf_evlist__mmap_per_cpu(evlist, prot, mask); |
591 | } | 600 | } |
592 | 601 | ||
593 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | 602 | int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, |
594 | pid_t target_tid, const char *cpu_list) | 603 | const char *target_tid, uid_t uid, const char *cpu_list) |
595 | { | 604 | { |
596 | evlist->threads = thread_map__new(target_pid, target_tid); | 605 | evlist->threads = thread_map__new_str(target_pid, target_tid, uid); |
597 | 606 | ||
598 | if (evlist->threads == NULL) | 607 | if (evlist->threads == NULL) |
599 | return -1; | 608 | return -1; |
600 | 609 | ||
601 | if (cpu_list == NULL && target_tid != -1) | 610 | if (uid != UINT_MAX || (cpu_list == NULL && target_tid)) |
602 | evlist->cpus = cpu_map__dummy_new(); | 611 | evlist->cpus = cpu_map__dummy_new(); |
603 | else | 612 | else |
604 | evlist->cpus = cpu_map__new(cpu_list); | 613 | evlist->cpus = cpu_map__new(cpu_list); |
@@ -758,6 +767,7 @@ out_err: | |||
758 | list_for_each_entry_reverse(evsel, &evlist->entries, node) | 767 | list_for_each_entry_reverse(evsel, &evlist->entries, node) |
759 | perf_evsel__close(evsel, ncpus, nthreads); | 768 | perf_evsel__close(evsel, ncpus, nthreads); |
760 | 769 | ||
770 | errno = -err; | ||
761 | return err; | 771 | return err; |
762 | } | 772 | } |
763 | 773 | ||
@@ -817,7 +827,7 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, | |||
817 | exit(-1); | 827 | exit(-1); |
818 | } | 828 | } |
819 | 829 | ||
820 | if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1) | 830 | if (!opts->system_wide && !opts->target_tid && !opts->target_pid) |
821 | evlist->threads->map[0] = evlist->workload.pid; | 831 | evlist->threads->map[0] = evlist->workload.pid; |
822 | 832 | ||
823 | close(child_ready_pipe[1]); | 833 | close(child_ready_pipe[1]); |
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 8922aeed0467..21f1c9e57f13 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
@@ -106,8 +106,8 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, | |||
106 | evlist->threads = threads; | 106 | evlist->threads = threads; |
107 | } | 107 | } |
108 | 108 | ||
109 | int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid, | 109 | int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid, |
110 | pid_t target_tid, const char *cpu_list); | 110 | const char *tid, uid_t uid, const char *cpu_list); |
111 | void perf_evlist__delete_maps(struct perf_evlist *evlist); | 111 | void perf_evlist__delete_maps(struct perf_evlist *evlist); |
112 | int perf_evlist__set_filters(struct perf_evlist *evlist); | 112 | int perf_evlist__set_filters(struct perf_evlist *evlist); |
113 | 113 | ||
@@ -117,4 +117,9 @@ u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist); | |||
117 | 117 | ||
118 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist); | 118 | bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist); |
119 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist); | 119 | bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist); |
120 | |||
121 | void perf_evlist__splice_list_tail(struct perf_evlist *evlist, | ||
122 | struct list_head *list, | ||
123 | int nr_entries); | ||
124 | |||
120 | #endif /* __PERF_EVLIST_H */ | 125 | #endif /* __PERF_EVLIST_H */ |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 667f3b78bb2c..8c13dbcb84b9 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -34,7 +34,7 @@ int __perf_evsel__sample_size(u64 sample_type) | |||
34 | return size; | 34 | return size; |
35 | } | 35 | } |
36 | 36 | ||
37 | static void hists__init(struct hists *hists) | 37 | void hists__init(struct hists *hists) |
38 | { | 38 | { |
39 | memset(hists, 0, sizeof(*hists)); | 39 | memset(hists, 0, sizeof(*hists)); |
40 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; | 40 | hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT; |
@@ -63,12 +63,13 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) | |||
63 | return evsel; | 63 | return evsel; |
64 | } | 64 | } |
65 | 65 | ||
66 | void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | 66 | void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts, |
67 | struct perf_evsel *first) | ||
67 | { | 68 | { |
68 | struct perf_event_attr *attr = &evsel->attr; | 69 | struct perf_event_attr *attr = &evsel->attr; |
69 | int track = !evsel->idx; /* only the first counter needs these */ | 70 | int track = !evsel->idx; /* only the first counter needs these */ |
70 | 71 | ||
71 | attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0; | 72 | attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1; |
72 | attr->inherit = !opts->no_inherit; | 73 | attr->inherit = !opts->no_inherit; |
73 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | 74 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | |
74 | PERF_FORMAT_TOTAL_TIME_RUNNING | | 75 | PERF_FORMAT_TOTAL_TIME_RUNNING | |
@@ -111,7 +112,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | |||
111 | if (opts->period) | 112 | if (opts->period) |
112 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 113 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
113 | 114 | ||
114 | if (opts->sample_id_all_avail && | 115 | if (!opts->sample_id_all_missing && |
115 | (opts->sample_time || opts->system_wide || | 116 | (opts->sample_time || opts->system_wide || |
116 | !opts->no_inherit || opts->cpu_list)) | 117 | !opts->no_inherit || opts->cpu_list)) |
117 | attr->sample_type |= PERF_SAMPLE_TIME; | 118 | attr->sample_type |= PERF_SAMPLE_TIME; |
@@ -126,11 +127,16 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts) | |||
126 | attr->watermark = 0; | 127 | attr->watermark = 0; |
127 | attr->wakeup_events = 1; | 128 | attr->wakeup_events = 1; |
128 | } | 129 | } |
130 | if (opts->branch_stack) { | ||
131 | attr->sample_type |= PERF_SAMPLE_BRANCH_STACK; | ||
132 | attr->branch_sample_type = opts->branch_stack; | ||
133 | } | ||
129 | 134 | ||
130 | attr->mmap = track; | 135 | attr->mmap = track; |
131 | attr->comm = track; | 136 | attr->comm = track; |
132 | 137 | ||
133 | if (opts->target_pid == -1 && opts->target_tid == -1 && !opts->system_wide) { | 138 | if (!opts->target_pid && !opts->target_tid && !opts->system_wide && |
139 | (!opts->group || evsel == first)) { | ||
134 | attr->disabled = 1; | 140 | attr->disabled = 1; |
135 | attr->enable_on_exec = 1; | 141 | attr->enable_on_exec = 1; |
136 | } | 142 | } |
@@ -463,6 +469,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
463 | memset(data, 0, sizeof(*data)); | 469 | memset(data, 0, sizeof(*data)); |
464 | data->cpu = data->pid = data->tid = -1; | 470 | data->cpu = data->pid = data->tid = -1; |
465 | data->stream_id = data->id = data->time = -1ULL; | 471 | data->stream_id = data->id = data->time = -1ULL; |
472 | data->period = 1; | ||
466 | 473 | ||
467 | if (event->header.type != PERF_RECORD_SAMPLE) { | 474 | if (event->header.type != PERF_RECORD_SAMPLE) { |
468 | if (!sample_id_all) | 475 | if (!sample_id_all) |
@@ -535,7 +542,7 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
535 | } | 542 | } |
536 | 543 | ||
537 | if (type & PERF_SAMPLE_READ) { | 544 | if (type & PERF_SAMPLE_READ) { |
538 | fprintf(stderr, "PERF_SAMPLE_READ is unsuported for now\n"); | 545 | fprintf(stderr, "PERF_SAMPLE_READ is unsupported for now\n"); |
539 | return -1; | 546 | return -1; |
540 | } | 547 | } |
541 | 548 | ||
@@ -573,8 +580,20 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, | |||
573 | return -EFAULT; | 580 | return -EFAULT; |
574 | 581 | ||
575 | data->raw_data = (void *) pdata; | 582 | data->raw_data = (void *) pdata; |
583 | |||
584 | array = (void *)array + data->raw_size + sizeof(u32); | ||
576 | } | 585 | } |
577 | 586 | ||
587 | if (type & PERF_SAMPLE_BRANCH_STACK) { | ||
588 | u64 sz; | ||
589 | |||
590 | data->branch_stack = (struct branch_stack *)array; | ||
591 | array++; /* nr */ | ||
592 | |||
593 | sz = data->branch_stack->nr * sizeof(struct branch_entry); | ||
594 | sz /= sizeof(u64); | ||
595 | array += sz; | ||
596 | } | ||
578 | return 0; | 597 | return 0; |
579 | } | 598 | } |
580 | 599 | ||
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 326b8e4d5035..3d6b3e4cb66b 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -80,7 +80,8 @@ void perf_evsel__exit(struct perf_evsel *evsel); | |||
80 | void perf_evsel__delete(struct perf_evsel *evsel); | 80 | void perf_evsel__delete(struct perf_evsel *evsel); |
81 | 81 | ||
82 | void perf_evsel__config(struct perf_evsel *evsel, | 82 | void perf_evsel__config(struct perf_evsel *evsel, |
83 | struct perf_record_opts *opts); | 83 | struct perf_record_opts *opts, |
84 | struct perf_evsel *first); | ||
84 | 85 | ||
85 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 86 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
86 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | 87 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); |
@@ -169,4 +170,6 @@ static inline int perf_evsel__sample_size(struct perf_evsel *evsel) | |||
169 | return __perf_evsel__sample_size(evsel->attr.sample_type); | 170 | return __perf_evsel__sample_size(evsel->attr.sample_type); |
170 | } | 171 | } |
171 | 172 | ||
173 | void hists__init(struct hists *hists); | ||
174 | |||
172 | #endif /* __PERF_EVSEL_H */ | 175 | #endif /* __PERF_EVSEL_H */ |
diff --git a/tools/perf/util/gtk/browser.c b/tools/perf/util/gtk/browser.c new file mode 100644 index 000000000000..258352a2356c --- /dev/null +++ b/tools/perf/util/gtk/browser.c | |||
@@ -0,0 +1,189 @@ | |||
1 | #include "../evlist.h" | ||
2 | #include "../cache.h" | ||
3 | #include "../evsel.h" | ||
4 | #include "../sort.h" | ||
5 | #include "../hist.h" | ||
6 | #include "gtk.h" | ||
7 | |||
8 | #include <signal.h> | ||
9 | |||
10 | #define MAX_COLUMNS 32 | ||
11 | |||
12 | void perf_gtk_setup_browser(int argc, const char *argv[], | ||
13 | bool fallback_to_pager __used) | ||
14 | { | ||
15 | gtk_init(&argc, (char ***)&argv); | ||
16 | } | ||
17 | |||
18 | void perf_gtk_exit_browser(bool wait_for_ok __used) | ||
19 | { | ||
20 | gtk_main_quit(); | ||
21 | } | ||
22 | |||
23 | static void perf_gtk_signal(int sig) | ||
24 | { | ||
25 | psignal(sig, "perf"); | ||
26 | gtk_main_quit(); | ||
27 | } | ||
28 | |||
29 | static void perf_gtk_resize_window(GtkWidget *window) | ||
30 | { | ||
31 | GdkRectangle rect; | ||
32 | GdkScreen *screen; | ||
33 | int monitor; | ||
34 | int height; | ||
35 | int width; | ||
36 | |||
37 | screen = gtk_widget_get_screen(window); | ||
38 | |||
39 | monitor = gdk_screen_get_monitor_at_window(screen, window->window); | ||
40 | |||
41 | gdk_screen_get_monitor_geometry(screen, monitor, &rect); | ||
42 | |||
43 | width = rect.width * 3 / 4; | ||
44 | height = rect.height * 3 / 4; | ||
45 | |||
46 | gtk_window_resize(GTK_WINDOW(window), width, height); | ||
47 | } | ||
48 | |||
49 | static void perf_gtk_show_hists(GtkWidget *window, struct hists *hists) | ||
50 | { | ||
51 | GType col_types[MAX_COLUMNS]; | ||
52 | GtkCellRenderer *renderer; | ||
53 | struct sort_entry *se; | ||
54 | GtkListStore *store; | ||
55 | struct rb_node *nd; | ||
56 | u64 total_period; | ||
57 | GtkWidget *view; | ||
58 | int col_idx; | ||
59 | int nr_cols; | ||
60 | |||
61 | nr_cols = 0; | ||
62 | |||
63 | /* The percentage column */ | ||
64 | col_types[nr_cols++] = G_TYPE_STRING; | ||
65 | |||
66 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
67 | if (se->elide) | ||
68 | continue; | ||
69 | |||
70 | col_types[nr_cols++] = G_TYPE_STRING; | ||
71 | } | ||
72 | |||
73 | store = gtk_list_store_newv(nr_cols, col_types); | ||
74 | |||
75 | view = gtk_tree_view_new(); | ||
76 | |||
77 | renderer = gtk_cell_renderer_text_new(); | ||
78 | |||
79 | col_idx = 0; | ||
80 | |||
81 | /* The percentage column */ | ||
82 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
83 | -1, "Overhead (%)", | ||
84 | renderer, "text", | ||
85 | col_idx++, NULL); | ||
86 | |||
87 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
88 | if (se->elide) | ||
89 | continue; | ||
90 | |||
91 | gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), | ||
92 | -1, se->se_header, | ||
93 | renderer, "text", | ||
94 | col_idx++, NULL); | ||
95 | } | ||
96 | |||
97 | gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store)); | ||
98 | |||
99 | g_object_unref(GTK_TREE_MODEL(store)); | ||
100 | |||
101 | total_period = hists->stats.total_period; | ||
102 | |||
103 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
104 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
105 | GtkTreeIter iter; | ||
106 | double percent; | ||
107 | char s[512]; | ||
108 | |||
109 | if (h->filtered) | ||
110 | continue; | ||
111 | |||
112 | gtk_list_store_append(store, &iter); | ||
113 | |||
114 | col_idx = 0; | ||
115 | |||
116 | percent = (h->period * 100.0) / total_period; | ||
117 | |||
118 | snprintf(s, ARRAY_SIZE(s), "%.2f", percent); | ||
119 | |||
120 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
121 | |||
122 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
123 | if (se->elide) | ||
124 | continue; | ||
125 | |||
126 | se->se_snprintf(h, s, ARRAY_SIZE(s), | ||
127 | hists__col_len(hists, se->se_width_idx)); | ||
128 | |||
129 | gtk_list_store_set(store, &iter, col_idx++, s, -1); | ||
130 | } | ||
131 | } | ||
132 | |||
133 | gtk_container_add(GTK_CONTAINER(window), view); | ||
134 | } | ||
135 | |||
136 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, | ||
137 | const char *help __used, | ||
138 | void (*timer) (void *arg)__used, | ||
139 | void *arg __used, int delay_secs __used) | ||
140 | { | ||
141 | struct perf_evsel *pos; | ||
142 | GtkWidget *notebook; | ||
143 | GtkWidget *window; | ||
144 | |||
145 | signal(SIGSEGV, perf_gtk_signal); | ||
146 | signal(SIGFPE, perf_gtk_signal); | ||
147 | signal(SIGINT, perf_gtk_signal); | ||
148 | signal(SIGQUIT, perf_gtk_signal); | ||
149 | signal(SIGTERM, perf_gtk_signal); | ||
150 | |||
151 | window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | ||
152 | |||
153 | gtk_window_set_title(GTK_WINDOW(window), "perf report"); | ||
154 | |||
155 | g_signal_connect(window, "delete_event", gtk_main_quit, NULL); | ||
156 | |||
157 | notebook = gtk_notebook_new(); | ||
158 | |||
159 | list_for_each_entry(pos, &evlist->entries, node) { | ||
160 | struct hists *hists = &pos->hists; | ||
161 | const char *evname = event_name(pos); | ||
162 | GtkWidget *scrolled_window; | ||
163 | GtkWidget *tab_label; | ||
164 | |||
165 | scrolled_window = gtk_scrolled_window_new(NULL, NULL); | ||
166 | |||
167 | gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), | ||
168 | GTK_POLICY_AUTOMATIC, | ||
169 | GTK_POLICY_AUTOMATIC); | ||
170 | |||
171 | perf_gtk_show_hists(scrolled_window, hists); | ||
172 | |||
173 | tab_label = gtk_label_new(evname); | ||
174 | |||
175 | gtk_notebook_append_page(GTK_NOTEBOOK(notebook), scrolled_window, tab_label); | ||
176 | } | ||
177 | |||
178 | gtk_container_add(GTK_CONTAINER(window), notebook); | ||
179 | |||
180 | gtk_widget_show_all(window); | ||
181 | |||
182 | perf_gtk_resize_window(window); | ||
183 | |||
184 | gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); | ||
185 | |||
186 | gtk_main(); | ||
187 | |||
188 | return 0; | ||
189 | } | ||
diff --git a/tools/perf/util/gtk/gtk.h b/tools/perf/util/gtk/gtk.h new file mode 100644 index 000000000000..75177ee04032 --- /dev/null +++ b/tools/perf/util/gtk/gtk.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef _PERF_GTK_H_ | ||
2 | #define _PERF_GTK_H_ 1 | ||
3 | |||
4 | #pragma GCC diagnostic ignored "-Wstrict-prototypes" | ||
5 | #include <gtk/gtk.h> | ||
6 | #pragma GCC diagnostic error "-Wstrict-prototypes" | ||
7 | |||
8 | #endif /* _PERF_GTK_H_ */ | ||
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 3e7e0b09c12c..4c7c2d73251f 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
@@ -63,9 +63,20 @@ char *perf_header__find_event(u64 id) | |||
63 | return NULL; | 63 | return NULL; |
64 | } | 64 | } |
65 | 65 | ||
66 | static const char *__perf_magic = "PERFFILE"; | 66 | /* |
67 | * magic2 = "PERFILE2" | ||
68 | * must be a numerical value to let the endianness | ||
69 | * determine the memory layout. That way we are able | ||
70 | * to detect endianness when reading the perf.data file | ||
71 | * back. | ||
72 | * | ||
73 | * we check for legacy (PERFFILE) format. | ||
74 | */ | ||
75 | static const char *__perf_magic1 = "PERFFILE"; | ||
76 | static const u64 __perf_magic2 = 0x32454c4946524550ULL; | ||
77 | static const u64 __perf_magic2_sw = 0x50455246494c4532ULL; | ||
67 | 78 | ||
68 | #define PERF_MAGIC (*(u64 *)__perf_magic) | 79 | #define PERF_MAGIC __perf_magic2 |
69 | 80 | ||
70 | struct perf_file_attr { | 81 | struct perf_file_attr { |
71 | struct perf_event_attr attr; | 82 | struct perf_event_attr attr; |
@@ -280,7 +291,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
280 | if (realname == NULL || filename == NULL || linkname == NULL) | 291 | if (realname == NULL || filename == NULL || linkname == NULL) |
281 | goto out_free; | 292 | goto out_free; |
282 | 293 | ||
283 | len = snprintf(filename, size, "%s%s%s", | 294 | len = scnprintf(filename, size, "%s%s%s", |
284 | debugdir, is_kallsyms ? "/" : "", realname); | 295 | debugdir, is_kallsyms ? "/" : "", realname); |
285 | if (mkdir_p(filename, 0755)) | 296 | if (mkdir_p(filename, 0755)) |
286 | goto out_free; | 297 | goto out_free; |
@@ -295,7 +306,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, | |||
295 | goto out_free; | 306 | goto out_free; |
296 | } | 307 | } |
297 | 308 | ||
298 | len = snprintf(linkname, size, "%s/.build-id/%.2s", | 309 | len = scnprintf(linkname, size, "%s/.build-id/%.2s", |
299 | debugdir, sbuild_id); | 310 | debugdir, sbuild_id); |
300 | 311 | ||
301 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) | 312 | if (access(linkname, X_OK) && mkdir_p(linkname, 0755)) |
@@ -1012,6 +1023,12 @@ write_it: | |||
1012 | return do_write_string(fd, buffer); | 1023 | return do_write_string(fd, buffer); |
1013 | } | 1024 | } |
1014 | 1025 | ||
1026 | static int write_branch_stack(int fd __used, struct perf_header *h __used, | ||
1027 | struct perf_evlist *evlist __used) | ||
1028 | { | ||
1029 | return 0; | ||
1030 | } | ||
1031 | |||
1015 | static void print_hostname(struct perf_header *ph, int fd, FILE *fp) | 1032 | static void print_hostname(struct perf_header *ph, int fd, FILE *fp) |
1016 | { | 1033 | { |
1017 | char *str = do_read_string(fd, ph); | 1034 | char *str = do_read_string(fd, ph); |
@@ -1133,8 +1150,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1133 | uint64_t id; | 1150 | uint64_t id; |
1134 | void *buf = NULL; | 1151 | void *buf = NULL; |
1135 | char *str; | 1152 | char *str; |
1136 | u32 nre, sz, nr, i, j, msz; | 1153 | u32 nre, sz, nr, i, j; |
1137 | int ret; | 1154 | ssize_t ret; |
1155 | size_t msz; | ||
1138 | 1156 | ||
1139 | /* number of events */ | 1157 | /* number of events */ |
1140 | ret = read(fd, &nre, sizeof(nre)); | 1158 | ret = read(fd, &nre, sizeof(nre)); |
@@ -1151,15 +1169,9 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1151 | if (ph->needs_swap) | 1169 | if (ph->needs_swap) |
1152 | sz = bswap_32(sz); | 1170 | sz = bswap_32(sz); |
1153 | 1171 | ||
1154 | /* | ||
1155 | * ensure it is at least to our ABI rev | ||
1156 | */ | ||
1157 | if (sz < (u32)sizeof(attr)) | ||
1158 | goto error; | ||
1159 | |||
1160 | memset(&attr, 0, sizeof(attr)); | 1172 | memset(&attr, 0, sizeof(attr)); |
1161 | 1173 | ||
1162 | /* read entire region to sync up to next field */ | 1174 | /* buffer to hold on file attr struct */ |
1163 | buf = malloc(sz); | 1175 | buf = malloc(sz); |
1164 | if (!buf) | 1176 | if (!buf) |
1165 | goto error; | 1177 | goto error; |
@@ -1170,6 +1182,10 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp) | |||
1170 | 1182 | ||
1171 | for (i = 0 ; i < nre; i++) { | 1183 | for (i = 0 ; i < nre; i++) { |
1172 | 1184 | ||
1185 | /* | ||
1186 | * must read entire on-file attr struct to | ||
1187 | * sync up with layout. | ||
1188 | */ | ||
1173 | ret = read(fd, buf, sz); | 1189 | ret = read(fd, buf, sz); |
1174 | if (ret != (ssize_t)sz) | 1190 | if (ret != (ssize_t)sz) |
1175 | goto error; | 1191 | goto error; |
@@ -1305,25 +1321,204 @@ static void print_cpuid(struct perf_header *ph, int fd, FILE *fp) | |||
1305 | free(str); | 1321 | free(str); |
1306 | } | 1322 | } |
1307 | 1323 | ||
1324 | static void print_branch_stack(struct perf_header *ph __used, int fd __used, | ||
1325 | FILE *fp) | ||
1326 | { | ||
1327 | fprintf(fp, "# contains samples with branch stack\n"); | ||
1328 | } | ||
1329 | |||
1330 | static int __event_process_build_id(struct build_id_event *bev, | ||
1331 | char *filename, | ||
1332 | struct perf_session *session) | ||
1333 | { | ||
1334 | int err = -1; | ||
1335 | struct list_head *head; | ||
1336 | struct machine *machine; | ||
1337 | u16 misc; | ||
1338 | struct dso *dso; | ||
1339 | enum dso_kernel_type dso_type; | ||
1340 | |||
1341 | machine = perf_session__findnew_machine(session, bev->pid); | ||
1342 | if (!machine) | ||
1343 | goto out; | ||
1344 | |||
1345 | misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1346 | |||
1347 | switch (misc) { | ||
1348 | case PERF_RECORD_MISC_KERNEL: | ||
1349 | dso_type = DSO_TYPE_KERNEL; | ||
1350 | head = &machine->kernel_dsos; | ||
1351 | break; | ||
1352 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
1353 | dso_type = DSO_TYPE_GUEST_KERNEL; | ||
1354 | head = &machine->kernel_dsos; | ||
1355 | break; | ||
1356 | case PERF_RECORD_MISC_USER: | ||
1357 | case PERF_RECORD_MISC_GUEST_USER: | ||
1358 | dso_type = DSO_TYPE_USER; | ||
1359 | head = &machine->user_dsos; | ||
1360 | break; | ||
1361 | default: | ||
1362 | goto out; | ||
1363 | } | ||
1364 | |||
1365 | dso = __dsos__findnew(head, filename); | ||
1366 | if (dso != NULL) { | ||
1367 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1368 | |||
1369 | dso__set_build_id(dso, &bev->build_id); | ||
1370 | |||
1371 | if (filename[0] == '[') | ||
1372 | dso->kernel = dso_type; | ||
1373 | |||
1374 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | ||
1375 | sbuild_id); | ||
1376 | pr_debug("build id event received for %s: %s\n", | ||
1377 | dso->long_name, sbuild_id); | ||
1378 | } | ||
1379 | |||
1380 | err = 0; | ||
1381 | out: | ||
1382 | return err; | ||
1383 | } | ||
1384 | |||
1385 | static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, | ||
1386 | int input, u64 offset, u64 size) | ||
1387 | { | ||
1388 | struct perf_session *session = container_of(header, struct perf_session, header); | ||
1389 | struct { | ||
1390 | struct perf_event_header header; | ||
1391 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | ||
1392 | char filename[0]; | ||
1393 | } old_bev; | ||
1394 | struct build_id_event bev; | ||
1395 | char filename[PATH_MAX]; | ||
1396 | u64 limit = offset + size; | ||
1397 | |||
1398 | while (offset < limit) { | ||
1399 | ssize_t len; | ||
1400 | |||
1401 | if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) | ||
1402 | return -1; | ||
1403 | |||
1404 | if (header->needs_swap) | ||
1405 | perf_event_header__bswap(&old_bev.header); | ||
1406 | |||
1407 | len = old_bev.header.size - sizeof(old_bev); | ||
1408 | if (read(input, filename, len) != len) | ||
1409 | return -1; | ||
1410 | |||
1411 | bev.header = old_bev.header; | ||
1412 | |||
1413 | /* | ||
1414 | * As the pid is the missing value, we need to fill | ||
1415 | * it properly. The header.misc value give us nice hint. | ||
1416 | */ | ||
1417 | bev.pid = HOST_KERNEL_ID; | ||
1418 | if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || | ||
1419 | bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) | ||
1420 | bev.pid = DEFAULT_GUEST_KERNEL_ID; | ||
1421 | |||
1422 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); | ||
1423 | __event_process_build_id(&bev, filename, session); | ||
1424 | |||
1425 | offset += bev.header.size; | ||
1426 | } | ||
1427 | |||
1428 | return 0; | ||
1429 | } | ||
1430 | |||
1431 | static int perf_header__read_build_ids(struct perf_header *header, | ||
1432 | int input, u64 offset, u64 size) | ||
1433 | { | ||
1434 | struct perf_session *session = container_of(header, struct perf_session, header); | ||
1435 | struct build_id_event bev; | ||
1436 | char filename[PATH_MAX]; | ||
1437 | u64 limit = offset + size, orig_offset = offset; | ||
1438 | int err = -1; | ||
1439 | |||
1440 | while (offset < limit) { | ||
1441 | ssize_t len; | ||
1442 | |||
1443 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
1444 | goto out; | ||
1445 | |||
1446 | if (header->needs_swap) | ||
1447 | perf_event_header__bswap(&bev.header); | ||
1448 | |||
1449 | len = bev.header.size - sizeof(bev); | ||
1450 | if (read(input, filename, len) != len) | ||
1451 | goto out; | ||
1452 | /* | ||
1453 | * The a1645ce1 changeset: | ||
1454 | * | ||
1455 | * "perf: 'perf kvm' tool for monitoring guest performance from host" | ||
1456 | * | ||
1457 | * Added a field to struct build_id_event that broke the file | ||
1458 | * format. | ||
1459 | * | ||
1460 | * Since the kernel build-id is the first entry, process the | ||
1461 | * table using the old format if the well known | ||
1462 | * '[kernel.kallsyms]' string for the kernel build-id has the | ||
1463 | * first 4 characters chopped off (where the pid_t sits). | ||
1464 | */ | ||
1465 | if (memcmp(filename, "nel.kallsyms]", 13) == 0) { | ||
1466 | if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) | ||
1467 | return -1; | ||
1468 | return perf_header__read_build_ids_abi_quirk(header, input, offset, size); | ||
1469 | } | ||
1470 | |||
1471 | __event_process_build_id(&bev, filename, session); | ||
1472 | |||
1473 | offset += bev.header.size; | ||
1474 | } | ||
1475 | err = 0; | ||
1476 | out: | ||
1477 | return err; | ||
1478 | } | ||
1479 | |||
1480 | static int process_trace_info(struct perf_file_section *section __unused, | ||
1481 | struct perf_header *ph __unused, | ||
1482 | int feat __unused, int fd) | ||
1483 | { | ||
1484 | trace_report(fd, false); | ||
1485 | return 0; | ||
1486 | } | ||
1487 | |||
1488 | static int process_build_id(struct perf_file_section *section, | ||
1489 | struct perf_header *ph, | ||
1490 | int feat __unused, int fd) | ||
1491 | { | ||
1492 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) | ||
1493 | pr_debug("Failed to read buildids, continuing...\n"); | ||
1494 | return 0; | ||
1495 | } | ||
1496 | |||
1308 | struct feature_ops { | 1497 | struct feature_ops { |
1309 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); | 1498 | int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); |
1310 | void (*print)(struct perf_header *h, int fd, FILE *fp); | 1499 | void (*print)(struct perf_header *h, int fd, FILE *fp); |
1500 | int (*process)(struct perf_file_section *section, | ||
1501 | struct perf_header *h, int feat, int fd); | ||
1311 | const char *name; | 1502 | const char *name; |
1312 | bool full_only; | 1503 | bool full_only; |
1313 | }; | 1504 | }; |
1314 | 1505 | ||
1315 | #define FEAT_OPA(n, func) \ | 1506 | #define FEAT_OPA(n, func) \ |
1316 | [n] = { .name = #n, .write = write_##func, .print = print_##func } | 1507 | [n] = { .name = #n, .write = write_##func, .print = print_##func } |
1508 | #define FEAT_OPP(n, func) \ | ||
1509 | [n] = { .name = #n, .write = write_##func, .print = print_##func, \ | ||
1510 | .process = process_##func } | ||
1317 | #define FEAT_OPF(n, func) \ | 1511 | #define FEAT_OPF(n, func) \ |
1318 | [n] = { .name = #n, .write = write_##func, .print = print_##func, .full_only = true } | 1512 | [n] = { .name = #n, .write = write_##func, .print = print_##func, \ |
1513 | .full_only = true } | ||
1319 | 1514 | ||
1320 | /* feature_ops not implemented: */ | 1515 | /* feature_ops not implemented: */ |
1321 | #define print_trace_info NULL | 1516 | #define print_trace_info NULL |
1322 | #define print_build_id NULL | 1517 | #define print_build_id NULL |
1323 | 1518 | ||
1324 | static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | 1519 | static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { |
1325 | FEAT_OPA(HEADER_TRACE_INFO, trace_info), | 1520 | FEAT_OPP(HEADER_TRACE_INFO, trace_info), |
1326 | FEAT_OPA(HEADER_BUILD_ID, build_id), | 1521 | FEAT_OPP(HEADER_BUILD_ID, build_id), |
1327 | FEAT_OPA(HEADER_HOSTNAME, hostname), | 1522 | FEAT_OPA(HEADER_HOSTNAME, hostname), |
1328 | FEAT_OPA(HEADER_OSRELEASE, osrelease), | 1523 | FEAT_OPA(HEADER_OSRELEASE, osrelease), |
1329 | FEAT_OPA(HEADER_VERSION, version), | 1524 | FEAT_OPA(HEADER_VERSION, version), |
@@ -1336,6 +1531,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = { | |||
1336 | FEAT_OPA(HEADER_CMDLINE, cmdline), | 1531 | FEAT_OPA(HEADER_CMDLINE, cmdline), |
1337 | FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), | 1532 | FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology), |
1338 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), | 1533 | FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), |
1534 | FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), | ||
1339 | }; | 1535 | }; |
1340 | 1536 | ||
1341 | struct header_print_data { | 1537 | struct header_print_data { |
@@ -1620,24 +1816,128 @@ out_free: | |||
1620 | return err; | 1816 | return err; |
1621 | } | 1817 | } |
1622 | 1818 | ||
1819 | static const int attr_file_abi_sizes[] = { | ||
1820 | [0] = PERF_ATTR_SIZE_VER0, | ||
1821 | [1] = PERF_ATTR_SIZE_VER1, | ||
1822 | 0, | ||
1823 | }; | ||
1824 | |||
1825 | /* | ||
1826 | * In the legacy file format, the magic number is not used to encode endianness. | ||
1827 | * hdr_sz was used to encode endianness. But given that hdr_sz can vary based | ||
1828 | * on ABI revisions, we need to try all combinations for all endianness to | ||
1829 | * detect the endianness. | ||
1830 | */ | ||
1831 | static int try_all_file_abis(uint64_t hdr_sz, struct perf_header *ph) | ||
1832 | { | ||
1833 | uint64_t ref_size, attr_size; | ||
1834 | int i; | ||
1835 | |||
1836 | for (i = 0 ; attr_file_abi_sizes[i]; i++) { | ||
1837 | ref_size = attr_file_abi_sizes[i] | ||
1838 | + sizeof(struct perf_file_section); | ||
1839 | if (hdr_sz != ref_size) { | ||
1840 | attr_size = bswap_64(hdr_sz); | ||
1841 | if (attr_size != ref_size) | ||
1842 | continue; | ||
1843 | |||
1844 | ph->needs_swap = true; | ||
1845 | } | ||
1846 | pr_debug("ABI%d perf.data file detected, need_swap=%d\n", | ||
1847 | i, | ||
1848 | ph->needs_swap); | ||
1849 | return 0; | ||
1850 | } | ||
1851 | /* could not determine endianness */ | ||
1852 | return -1; | ||
1853 | } | ||
1854 | |||
1855 | #define PERF_PIPE_HDR_VER0 16 | ||
1856 | |||
1857 | static const size_t attr_pipe_abi_sizes[] = { | ||
1858 | [0] = PERF_PIPE_HDR_VER0, | ||
1859 | 0, | ||
1860 | }; | ||
1861 | |||
1862 | /* | ||
1863 | * In the legacy pipe format, there is an implicit assumption that endiannesss | ||
1864 | * between host recording the samples, and host parsing the samples is the | ||
1865 | * same. This is not always the case given that the pipe output may always be | ||
1866 | * redirected into a file and analyzed on a different machine with possibly a | ||
1867 | * different endianness and perf_event ABI revsions in the perf tool itself. | ||
1868 | */ | ||
1869 | static int try_all_pipe_abis(uint64_t hdr_sz, struct perf_header *ph) | ||
1870 | { | ||
1871 | u64 attr_size; | ||
1872 | int i; | ||
1873 | |||
1874 | for (i = 0 ; attr_pipe_abi_sizes[i]; i++) { | ||
1875 | if (hdr_sz != attr_pipe_abi_sizes[i]) { | ||
1876 | attr_size = bswap_64(hdr_sz); | ||
1877 | if (attr_size != hdr_sz) | ||
1878 | continue; | ||
1879 | |||
1880 | ph->needs_swap = true; | ||
1881 | } | ||
1882 | pr_debug("Pipe ABI%d perf.data file detected\n", i); | ||
1883 | return 0; | ||
1884 | } | ||
1885 | return -1; | ||
1886 | } | ||
1887 | |||
1888 | static int check_magic_endian(u64 magic, uint64_t hdr_sz, | ||
1889 | bool is_pipe, struct perf_header *ph) | ||
1890 | { | ||
1891 | int ret; | ||
1892 | |||
1893 | /* check for legacy format */ | ||
1894 | ret = memcmp(&magic, __perf_magic1, sizeof(magic)); | ||
1895 | if (ret == 0) { | ||
1896 | pr_debug("legacy perf.data format\n"); | ||
1897 | if (is_pipe) | ||
1898 | return try_all_pipe_abis(hdr_sz, ph); | ||
1899 | |||
1900 | return try_all_file_abis(hdr_sz, ph); | ||
1901 | } | ||
1902 | /* | ||
1903 | * the new magic number serves two purposes: | ||
1904 | * - unique number to identify actual perf.data files | ||
1905 | * - encode endianness of file | ||
1906 | */ | ||
1907 | |||
1908 | /* check magic number with one endianness */ | ||
1909 | if (magic == __perf_magic2) | ||
1910 | return 0; | ||
1911 | |||
1912 | /* check magic number with opposite endianness */ | ||
1913 | if (magic != __perf_magic2_sw) | ||
1914 | return -1; | ||
1915 | |||
1916 | ph->needs_swap = true; | ||
1917 | |||
1918 | return 0; | ||
1919 | } | ||
1920 | |||
1623 | int perf_file_header__read(struct perf_file_header *header, | 1921 | int perf_file_header__read(struct perf_file_header *header, |
1624 | struct perf_header *ph, int fd) | 1922 | struct perf_header *ph, int fd) |
1625 | { | 1923 | { |
1924 | int ret; | ||
1925 | |||
1626 | lseek(fd, 0, SEEK_SET); | 1926 | lseek(fd, 0, SEEK_SET); |
1627 | 1927 | ||
1628 | if (readn(fd, header, sizeof(*header)) <= 0 || | 1928 | ret = readn(fd, header, sizeof(*header)); |
1629 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) | 1929 | if (ret <= 0) |
1630 | return -1; | 1930 | return -1; |
1631 | 1931 | ||
1632 | if (header->attr_size != sizeof(struct perf_file_attr)) { | 1932 | if (check_magic_endian(header->magic, |
1633 | u64 attr_size = bswap_64(header->attr_size); | 1933 | header->attr_size, false, ph) < 0) { |
1634 | 1934 | pr_debug("magic/endian check failed\n"); | |
1635 | if (attr_size != sizeof(struct perf_file_attr)) | 1935 | return -1; |
1636 | return -1; | 1936 | } |
1637 | 1937 | ||
1938 | if (ph->needs_swap) { | ||
1638 | mem_bswap_64(header, offsetof(struct perf_file_header, | 1939 | mem_bswap_64(header, offsetof(struct perf_file_header, |
1639 | adds_features)); | 1940 | adds_features)); |
1640 | ph->needs_swap = true; | ||
1641 | } | 1941 | } |
1642 | 1942 | ||
1643 | if (header->size != sizeof(*header)) { | 1943 | if (header->size != sizeof(*header)) { |
@@ -1689,156 +1989,6 @@ int perf_file_header__read(struct perf_file_header *header, | |||
1689 | return 0; | 1989 | return 0; |
1690 | } | 1990 | } |
1691 | 1991 | ||
1692 | static int __event_process_build_id(struct build_id_event *bev, | ||
1693 | char *filename, | ||
1694 | struct perf_session *session) | ||
1695 | { | ||
1696 | int err = -1; | ||
1697 | struct list_head *head; | ||
1698 | struct machine *machine; | ||
1699 | u16 misc; | ||
1700 | struct dso *dso; | ||
1701 | enum dso_kernel_type dso_type; | ||
1702 | |||
1703 | machine = perf_session__findnew_machine(session, bev->pid); | ||
1704 | if (!machine) | ||
1705 | goto out; | ||
1706 | |||
1707 | misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | ||
1708 | |||
1709 | switch (misc) { | ||
1710 | case PERF_RECORD_MISC_KERNEL: | ||
1711 | dso_type = DSO_TYPE_KERNEL; | ||
1712 | head = &machine->kernel_dsos; | ||
1713 | break; | ||
1714 | case PERF_RECORD_MISC_GUEST_KERNEL: | ||
1715 | dso_type = DSO_TYPE_GUEST_KERNEL; | ||
1716 | head = &machine->kernel_dsos; | ||
1717 | break; | ||
1718 | case PERF_RECORD_MISC_USER: | ||
1719 | case PERF_RECORD_MISC_GUEST_USER: | ||
1720 | dso_type = DSO_TYPE_USER; | ||
1721 | head = &machine->user_dsos; | ||
1722 | break; | ||
1723 | default: | ||
1724 | goto out; | ||
1725 | } | ||
1726 | |||
1727 | dso = __dsos__findnew(head, filename); | ||
1728 | if (dso != NULL) { | ||
1729 | char sbuild_id[BUILD_ID_SIZE * 2 + 1]; | ||
1730 | |||
1731 | dso__set_build_id(dso, &bev->build_id); | ||
1732 | |||
1733 | if (filename[0] == '[') | ||
1734 | dso->kernel = dso_type; | ||
1735 | |||
1736 | build_id__sprintf(dso->build_id, sizeof(dso->build_id), | ||
1737 | sbuild_id); | ||
1738 | pr_debug("build id event received for %s: %s\n", | ||
1739 | dso->long_name, sbuild_id); | ||
1740 | } | ||
1741 | |||
1742 | err = 0; | ||
1743 | out: | ||
1744 | return err; | ||
1745 | } | ||
1746 | |||
1747 | static int perf_header__read_build_ids_abi_quirk(struct perf_header *header, | ||
1748 | int input, u64 offset, u64 size) | ||
1749 | { | ||
1750 | struct perf_session *session = container_of(header, struct perf_session, header); | ||
1751 | struct { | ||
1752 | struct perf_event_header header; | ||
1753 | u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))]; | ||
1754 | char filename[0]; | ||
1755 | } old_bev; | ||
1756 | struct build_id_event bev; | ||
1757 | char filename[PATH_MAX]; | ||
1758 | u64 limit = offset + size; | ||
1759 | |||
1760 | while (offset < limit) { | ||
1761 | ssize_t len; | ||
1762 | |||
1763 | if (read(input, &old_bev, sizeof(old_bev)) != sizeof(old_bev)) | ||
1764 | return -1; | ||
1765 | |||
1766 | if (header->needs_swap) | ||
1767 | perf_event_header__bswap(&old_bev.header); | ||
1768 | |||
1769 | len = old_bev.header.size - sizeof(old_bev); | ||
1770 | if (read(input, filename, len) != len) | ||
1771 | return -1; | ||
1772 | |||
1773 | bev.header = old_bev.header; | ||
1774 | |||
1775 | /* | ||
1776 | * As the pid is the missing value, we need to fill | ||
1777 | * it properly. The header.misc value give us nice hint. | ||
1778 | */ | ||
1779 | bev.pid = HOST_KERNEL_ID; | ||
1780 | if (bev.header.misc == PERF_RECORD_MISC_GUEST_USER || | ||
1781 | bev.header.misc == PERF_RECORD_MISC_GUEST_KERNEL) | ||
1782 | bev.pid = DEFAULT_GUEST_KERNEL_ID; | ||
1783 | |||
1784 | memcpy(bev.build_id, old_bev.build_id, sizeof(bev.build_id)); | ||
1785 | __event_process_build_id(&bev, filename, session); | ||
1786 | |||
1787 | offset += bev.header.size; | ||
1788 | } | ||
1789 | |||
1790 | return 0; | ||
1791 | } | ||
1792 | |||
1793 | static int perf_header__read_build_ids(struct perf_header *header, | ||
1794 | int input, u64 offset, u64 size) | ||
1795 | { | ||
1796 | struct perf_session *session = container_of(header, struct perf_session, header); | ||
1797 | struct build_id_event bev; | ||
1798 | char filename[PATH_MAX]; | ||
1799 | u64 limit = offset + size, orig_offset = offset; | ||
1800 | int err = -1; | ||
1801 | |||
1802 | while (offset < limit) { | ||
1803 | ssize_t len; | ||
1804 | |||
1805 | if (read(input, &bev, sizeof(bev)) != sizeof(bev)) | ||
1806 | goto out; | ||
1807 | |||
1808 | if (header->needs_swap) | ||
1809 | perf_event_header__bswap(&bev.header); | ||
1810 | |||
1811 | len = bev.header.size - sizeof(bev); | ||
1812 | if (read(input, filename, len) != len) | ||
1813 | goto out; | ||
1814 | /* | ||
1815 | * The a1645ce1 changeset: | ||
1816 | * | ||
1817 | * "perf: 'perf kvm' tool for monitoring guest performance from host" | ||
1818 | * | ||
1819 | * Added a field to struct build_id_event that broke the file | ||
1820 | * format. | ||
1821 | * | ||
1822 | * Since the kernel build-id is the first entry, process the | ||
1823 | * table using the old format if the well known | ||
1824 | * '[kernel.kallsyms]' string for the kernel build-id has the | ||
1825 | * first 4 characters chopped off (where the pid_t sits). | ||
1826 | */ | ||
1827 | if (memcmp(filename, "nel.kallsyms]", 13) == 0) { | ||
1828 | if (lseek(input, orig_offset, SEEK_SET) == (off_t)-1) | ||
1829 | return -1; | ||
1830 | return perf_header__read_build_ids_abi_quirk(header, input, offset, size); | ||
1831 | } | ||
1832 | |||
1833 | __event_process_build_id(&bev, filename, session); | ||
1834 | |||
1835 | offset += bev.header.size; | ||
1836 | } | ||
1837 | err = 0; | ||
1838 | out: | ||
1839 | return err; | ||
1840 | } | ||
1841 | |||
1842 | static int perf_file_section__process(struct perf_file_section *section, | 1992 | static int perf_file_section__process(struct perf_file_section *section, |
1843 | struct perf_header *ph, | 1993 | struct perf_header *ph, |
1844 | int feat, int fd, void *data __used) | 1994 | int feat, int fd, void *data __used) |
@@ -1854,40 +2004,32 @@ static int perf_file_section__process(struct perf_file_section *section, | |||
1854 | return 0; | 2004 | return 0; |
1855 | } | 2005 | } |
1856 | 2006 | ||
1857 | switch (feat) { | 2007 | if (!feat_ops[feat].process) |
1858 | case HEADER_TRACE_INFO: | 2008 | return 0; |
1859 | trace_report(fd, false); | ||
1860 | break; | ||
1861 | case HEADER_BUILD_ID: | ||
1862 | if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) | ||
1863 | pr_debug("Failed to read buildids, continuing...\n"); | ||
1864 | break; | ||
1865 | default: | ||
1866 | break; | ||
1867 | } | ||
1868 | 2009 | ||
1869 | return 0; | 2010 | return feat_ops[feat].process(section, ph, feat, fd); |
1870 | } | 2011 | } |
1871 | 2012 | ||
1872 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, | 2013 | static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, |
1873 | struct perf_header *ph, int fd, | 2014 | struct perf_header *ph, int fd, |
1874 | bool repipe) | 2015 | bool repipe) |
1875 | { | 2016 | { |
1876 | if (readn(fd, header, sizeof(*header)) <= 0 || | 2017 | int ret; |
1877 | memcmp(&header->magic, __perf_magic, sizeof(header->magic))) | ||
1878 | return -1; | ||
1879 | 2018 | ||
1880 | if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) | 2019 | ret = readn(fd, header, sizeof(*header)); |
2020 | if (ret <= 0) | ||
1881 | return -1; | 2021 | return -1; |
1882 | 2022 | ||
1883 | if (header->size != sizeof(*header)) { | 2023 | if (check_magic_endian(header->magic, header->size, true, ph) < 0) { |
1884 | u64 size = bswap_64(header->size); | 2024 | pr_debug("endian/magic failed\n"); |
2025 | return -1; | ||
2026 | } | ||
1885 | 2027 | ||
1886 | if (size != sizeof(*header)) | 2028 | if (ph->needs_swap) |
1887 | return -1; | 2029 | header->size = bswap_64(header->size); |
1888 | 2030 | ||
1889 | ph->needs_swap = true; | 2031 | if (repipe && do_write(STDOUT_FILENO, header, sizeof(*header)) < 0) |
1890 | } | 2032 | return -1; |
1891 | 2033 | ||
1892 | return 0; | 2034 | return 0; |
1893 | } | 2035 | } |
@@ -1908,6 +2050,52 @@ static int perf_header__read_pipe(struct perf_session *session, int fd) | |||
1908 | return 0; | 2050 | return 0; |
1909 | } | 2051 | } |
1910 | 2052 | ||
2053 | static int read_attr(int fd, struct perf_header *ph, | ||
2054 | struct perf_file_attr *f_attr) | ||
2055 | { | ||
2056 | struct perf_event_attr *attr = &f_attr->attr; | ||
2057 | size_t sz, left; | ||
2058 | size_t our_sz = sizeof(f_attr->attr); | ||
2059 | int ret; | ||
2060 | |||
2061 | memset(f_attr, 0, sizeof(*f_attr)); | ||
2062 | |||
2063 | /* read minimal guaranteed structure */ | ||
2064 | ret = readn(fd, attr, PERF_ATTR_SIZE_VER0); | ||
2065 | if (ret <= 0) { | ||
2066 | pr_debug("cannot read %d bytes of header attr\n", | ||
2067 | PERF_ATTR_SIZE_VER0); | ||
2068 | return -1; | ||
2069 | } | ||
2070 | |||
2071 | /* on file perf_event_attr size */ | ||
2072 | sz = attr->size; | ||
2073 | |||
2074 | if (ph->needs_swap) | ||
2075 | sz = bswap_32(sz); | ||
2076 | |||
2077 | if (sz == 0) { | ||
2078 | /* assume ABI0 */ | ||
2079 | sz = PERF_ATTR_SIZE_VER0; | ||
2080 | } else if (sz > our_sz) { | ||
2081 | pr_debug("file uses a more recent and unsupported ABI" | ||
2082 | " (%zu bytes extra)\n", sz - our_sz); | ||
2083 | return -1; | ||
2084 | } | ||
2085 | /* what we have not yet read and that we know about */ | ||
2086 | left = sz - PERF_ATTR_SIZE_VER0; | ||
2087 | if (left) { | ||
2088 | void *ptr = attr; | ||
2089 | ptr += PERF_ATTR_SIZE_VER0; | ||
2090 | |||
2091 | ret = readn(fd, ptr, left); | ||
2092 | } | ||
2093 | /* read perf_file_section, ids are read in caller */ | ||
2094 | ret = readn(fd, &f_attr->ids, sizeof(f_attr->ids)); | ||
2095 | |||
2096 | return ret <= 0 ? -1 : 0; | ||
2097 | } | ||
2098 | |||
1911 | int perf_session__read_header(struct perf_session *session, int fd) | 2099 | int perf_session__read_header(struct perf_session *session, int fd) |
1912 | { | 2100 | { |
1913 | struct perf_header *header = &session->header; | 2101 | struct perf_header *header = &session->header; |
@@ -1923,19 +2111,17 @@ int perf_session__read_header(struct perf_session *session, int fd) | |||
1923 | if (session->fd_pipe) | 2111 | if (session->fd_pipe) |
1924 | return perf_header__read_pipe(session, fd); | 2112 | return perf_header__read_pipe(session, fd); |
1925 | 2113 | ||
1926 | if (perf_file_header__read(&f_header, header, fd) < 0) { | 2114 | if (perf_file_header__read(&f_header, header, fd) < 0) |
1927 | pr_debug("incompatible file format\n"); | ||
1928 | return -EINVAL; | 2115 | return -EINVAL; |
1929 | } | ||
1930 | 2116 | ||
1931 | nr_attrs = f_header.attrs.size / sizeof(f_attr); | 2117 | nr_attrs = f_header.attrs.size / f_header.attr_size; |
1932 | lseek(fd, f_header.attrs.offset, SEEK_SET); | 2118 | lseek(fd, f_header.attrs.offset, SEEK_SET); |
1933 | 2119 | ||
1934 | for (i = 0; i < nr_attrs; i++) { | 2120 | for (i = 0; i < nr_attrs; i++) { |
1935 | struct perf_evsel *evsel; | 2121 | struct perf_evsel *evsel; |
1936 | off_t tmp; | 2122 | off_t tmp; |
1937 | 2123 | ||
1938 | if (readn(fd, &f_attr, sizeof(f_attr)) <= 0) | 2124 | if (read_attr(fd, header, &f_attr) < 0) |
1939 | goto out_errno; | 2125 | goto out_errno; |
1940 | 2126 | ||
1941 | if (header->needs_swap) | 2127 | if (header->needs_swap) |
@@ -2105,7 +2291,7 @@ int perf_event__synthesize_event_type(struct perf_tool *tool, | |||
2105 | strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1); | 2291 | strncpy(ev.event_type.event_type.name, name, MAX_EVENT_NAME - 1); |
2106 | 2292 | ||
2107 | ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; | 2293 | ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE; |
2108 | size = strlen(name); | 2294 | size = strlen(ev.event_type.event_type.name); |
2109 | size = ALIGN(size, sizeof(u64)); | 2295 | size = ALIGN(size, sizeof(u64)); |
2110 | ev.event_type.header.size = sizeof(ev.event_type) - | 2296 | ev.event_type.header.size = sizeof(ev.event_type) - |
2111 | (sizeof(ev.event_type.event_type.name) - size); | 2297 | (sizeof(ev.event_type.event_type.name) - size); |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index ac4ec956024e..21a6be09c129 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -11,6 +11,7 @@ | |||
11 | 11 | ||
12 | enum { | 12 | enum { |
13 | HEADER_RESERVED = 0, /* always cleared */ | 13 | HEADER_RESERVED = 0, /* always cleared */ |
14 | HEADER_FIRST_FEATURE = 1, | ||
14 | HEADER_TRACE_INFO = 1, | 15 | HEADER_TRACE_INFO = 1, |
15 | HEADER_BUILD_ID, | 16 | HEADER_BUILD_ID, |
16 | 17 | ||
@@ -26,7 +27,7 @@ enum { | |||
26 | HEADER_EVENT_DESC, | 27 | HEADER_EVENT_DESC, |
27 | HEADER_CPU_TOPOLOGY, | 28 | HEADER_CPU_TOPOLOGY, |
28 | HEADER_NUMA_TOPOLOGY, | 29 | HEADER_NUMA_TOPOLOGY, |
29 | 30 | HEADER_BRANCH_STACK, | |
30 | HEADER_LAST_FEATURE, | 31 | HEADER_LAST_FEATURE, |
31 | HEADER_FEAT_BITS = 256, | 32 | HEADER_FEAT_BITS = 256, |
32 | }; | 33 | }; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index abef2703cd24..2ec4b60aff6c 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -10,11 +10,14 @@ static bool hists__filter_entry_by_dso(struct hists *hists, | |||
10 | struct hist_entry *he); | 10 | struct hist_entry *he); |
11 | static bool hists__filter_entry_by_thread(struct hists *hists, | 11 | static bool hists__filter_entry_by_thread(struct hists *hists, |
12 | struct hist_entry *he); | 12 | struct hist_entry *he); |
13 | static bool hists__filter_entry_by_symbol(struct hists *hists, | ||
14 | struct hist_entry *he); | ||
13 | 15 | ||
14 | enum hist_filter { | 16 | enum hist_filter { |
15 | HIST_FILTER__DSO, | 17 | HIST_FILTER__DSO, |
16 | HIST_FILTER__THREAD, | 18 | HIST_FILTER__THREAD, |
17 | HIST_FILTER__PARENT, | 19 | HIST_FILTER__PARENT, |
20 | HIST_FILTER__SYMBOL, | ||
18 | }; | 21 | }; |
19 | 22 | ||
20 | struct callchain_param callchain_param = { | 23 | struct callchain_param callchain_param = { |
@@ -50,21 +53,25 @@ static void hists__reset_col_len(struct hists *hists) | |||
50 | hists__set_col_len(hists, col, 0); | 53 | hists__set_col_len(hists, col, 0); |
51 | } | 54 | } |
52 | 55 | ||
56 | static void hists__set_unres_dso_col_len(struct hists *hists, int dso) | ||
57 | { | ||
58 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | ||
59 | |||
60 | if (hists__col_len(hists, dso) < unresolved_col_width && | ||
61 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
62 | !symbol_conf.dso_list) | ||
63 | hists__set_col_len(hists, dso, unresolved_col_width); | ||
64 | } | ||
65 | |||
53 | static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | 66 | static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) |
54 | { | 67 | { |
68 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | ||
55 | u16 len; | 69 | u16 len; |
56 | 70 | ||
57 | if (h->ms.sym) | 71 | if (h->ms.sym) |
58 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen); | 72 | hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4); |
59 | else { | 73 | else |
60 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 74 | hists__set_unres_dso_col_len(hists, HISTC_DSO); |
61 | |||
62 | if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width && | ||
63 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | ||
64 | !symbol_conf.dso_list) | ||
65 | hists__set_col_len(hists, HISTC_DSO, | ||
66 | unresolved_col_width); | ||
67 | } | ||
68 | 75 | ||
69 | len = thread__comm_len(h->thread); | 76 | len = thread__comm_len(h->thread); |
70 | if (hists__new_col_len(hists, HISTC_COMM, len)) | 77 | if (hists__new_col_len(hists, HISTC_COMM, len)) |
@@ -74,23 +81,54 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) | |||
74 | len = dso__name_len(h->ms.map->dso); | 81 | len = dso__name_len(h->ms.map->dso); |
75 | hists__new_col_len(hists, HISTC_DSO, len); | 82 | hists__new_col_len(hists, HISTC_DSO, len); |
76 | } | 83 | } |
84 | |||
85 | if (h->branch_info) { | ||
86 | int symlen; | ||
87 | /* | ||
88 | * +4 accounts for '[x] ' priv level info | ||
89 | * +2 account of 0x prefix on raw addresses | ||
90 | */ | ||
91 | if (h->branch_info->from.sym) { | ||
92 | symlen = (int)h->branch_info->from.sym->namelen + 4; | ||
93 | hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen); | ||
94 | |||
95 | symlen = dso__name_len(h->branch_info->from.map->dso); | ||
96 | hists__new_col_len(hists, HISTC_DSO_FROM, symlen); | ||
97 | } else { | ||
98 | symlen = unresolved_col_width + 4 + 2; | ||
99 | hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen); | ||
100 | hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM); | ||
101 | } | ||
102 | |||
103 | if (h->branch_info->to.sym) { | ||
104 | symlen = (int)h->branch_info->to.sym->namelen + 4; | ||
105 | hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen); | ||
106 | |||
107 | symlen = dso__name_len(h->branch_info->to.map->dso); | ||
108 | hists__new_col_len(hists, HISTC_DSO_TO, symlen); | ||
109 | } else { | ||
110 | symlen = unresolved_col_width + 4 + 2; | ||
111 | hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen); | ||
112 | hists__set_unres_dso_col_len(hists, HISTC_DSO_TO); | ||
113 | } | ||
114 | } | ||
77 | } | 115 | } |
78 | 116 | ||
79 | static void hist_entry__add_cpumode_period(struct hist_entry *self, | 117 | static void hist_entry__add_cpumode_period(struct hist_entry *he, |
80 | unsigned int cpumode, u64 period) | 118 | unsigned int cpumode, u64 period) |
81 | { | 119 | { |
82 | switch (cpumode) { | 120 | switch (cpumode) { |
83 | case PERF_RECORD_MISC_KERNEL: | 121 | case PERF_RECORD_MISC_KERNEL: |
84 | self->period_sys += period; | 122 | he->period_sys += period; |
85 | break; | 123 | break; |
86 | case PERF_RECORD_MISC_USER: | 124 | case PERF_RECORD_MISC_USER: |
87 | self->period_us += period; | 125 | he->period_us += period; |
88 | break; | 126 | break; |
89 | case PERF_RECORD_MISC_GUEST_KERNEL: | 127 | case PERF_RECORD_MISC_GUEST_KERNEL: |
90 | self->period_guest_sys += period; | 128 | he->period_guest_sys += period; |
91 | break; | 129 | break; |
92 | case PERF_RECORD_MISC_GUEST_USER: | 130 | case PERF_RECORD_MISC_GUEST_USER: |
93 | self->period_guest_us += period; | 131 | he->period_guest_us += period; |
94 | break; | 132 | break; |
95 | default: | 133 | default: |
96 | break; | 134 | break; |
@@ -165,18 +203,18 @@ void hists__decay_entries_threaded(struct hists *hists, | |||
165 | static struct hist_entry *hist_entry__new(struct hist_entry *template) | 203 | static struct hist_entry *hist_entry__new(struct hist_entry *template) |
166 | { | 204 | { |
167 | size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; | 205 | size_t callchain_size = symbol_conf.use_callchain ? sizeof(struct callchain_root) : 0; |
168 | struct hist_entry *self = malloc(sizeof(*self) + callchain_size); | 206 | struct hist_entry *he = malloc(sizeof(*he) + callchain_size); |
169 | 207 | ||
170 | if (self != NULL) { | 208 | if (he != NULL) { |
171 | *self = *template; | 209 | *he = *template; |
172 | self->nr_events = 1; | 210 | he->nr_events = 1; |
173 | if (self->ms.map) | 211 | if (he->ms.map) |
174 | self->ms.map->referenced = true; | 212 | he->ms.map->referenced = true; |
175 | if (symbol_conf.use_callchain) | 213 | if (symbol_conf.use_callchain) |
176 | callchain_init(self->callchain); | 214 | callchain_init(he->callchain); |
177 | } | 215 | } |
178 | 216 | ||
179 | return self; | 217 | return he; |
180 | } | 218 | } |
181 | 219 | ||
182 | static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) | 220 | static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h) |
@@ -195,26 +233,14 @@ static u8 symbol__parent_filter(const struct symbol *parent) | |||
195 | return 0; | 233 | return 0; |
196 | } | 234 | } |
197 | 235 | ||
198 | struct hist_entry *__hists__add_entry(struct hists *hists, | 236 | static struct hist_entry *add_hist_entry(struct hists *hists, |
237 | struct hist_entry *entry, | ||
199 | struct addr_location *al, | 238 | struct addr_location *al, |
200 | struct symbol *sym_parent, u64 period) | 239 | u64 period) |
201 | { | 240 | { |
202 | struct rb_node **p; | 241 | struct rb_node **p; |
203 | struct rb_node *parent = NULL; | 242 | struct rb_node *parent = NULL; |
204 | struct hist_entry *he; | 243 | struct hist_entry *he; |
205 | struct hist_entry entry = { | ||
206 | .thread = al->thread, | ||
207 | .ms = { | ||
208 | .map = al->map, | ||
209 | .sym = al->sym, | ||
210 | }, | ||
211 | .cpu = al->cpu, | ||
212 | .ip = al->addr, | ||
213 | .level = al->level, | ||
214 | .period = period, | ||
215 | .parent = sym_parent, | ||
216 | .filtered = symbol__parent_filter(sym_parent), | ||
217 | }; | ||
218 | int cmp; | 244 | int cmp; |
219 | 245 | ||
220 | pthread_mutex_lock(&hists->lock); | 246 | pthread_mutex_lock(&hists->lock); |
@@ -225,7 +251,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, | |||
225 | parent = *p; | 251 | parent = *p; |
226 | he = rb_entry(parent, struct hist_entry, rb_node_in); | 252 | he = rb_entry(parent, struct hist_entry, rb_node_in); |
227 | 253 | ||
228 | cmp = hist_entry__cmp(&entry, he); | 254 | cmp = hist_entry__cmp(entry, he); |
229 | 255 | ||
230 | if (!cmp) { | 256 | if (!cmp) { |
231 | he->period += period; | 257 | he->period += period; |
@@ -239,7 +265,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, | |||
239 | p = &(*p)->rb_right; | 265 | p = &(*p)->rb_right; |
240 | } | 266 | } |
241 | 267 | ||
242 | he = hist_entry__new(&entry); | 268 | he = hist_entry__new(entry); |
243 | if (!he) | 269 | if (!he) |
244 | goto out_unlock; | 270 | goto out_unlock; |
245 | 271 | ||
@@ -252,6 +278,51 @@ out_unlock: | |||
252 | return he; | 278 | return he; |
253 | } | 279 | } |
254 | 280 | ||
281 | struct hist_entry *__hists__add_branch_entry(struct hists *self, | ||
282 | struct addr_location *al, | ||
283 | struct symbol *sym_parent, | ||
284 | struct branch_info *bi, | ||
285 | u64 period) | ||
286 | { | ||
287 | struct hist_entry entry = { | ||
288 | .thread = al->thread, | ||
289 | .ms = { | ||
290 | .map = bi->to.map, | ||
291 | .sym = bi->to.sym, | ||
292 | }, | ||
293 | .cpu = al->cpu, | ||
294 | .ip = bi->to.addr, | ||
295 | .level = al->level, | ||
296 | .period = period, | ||
297 | .parent = sym_parent, | ||
298 | .filtered = symbol__parent_filter(sym_parent), | ||
299 | .branch_info = bi, | ||
300 | }; | ||
301 | |||
302 | return add_hist_entry(self, &entry, al, period); | ||
303 | } | ||
304 | |||
305 | struct hist_entry *__hists__add_entry(struct hists *self, | ||
306 | struct addr_location *al, | ||
307 | struct symbol *sym_parent, u64 period) | ||
308 | { | ||
309 | struct hist_entry entry = { | ||
310 | .thread = al->thread, | ||
311 | .ms = { | ||
312 | .map = al->map, | ||
313 | .sym = al->sym, | ||
314 | }, | ||
315 | .cpu = al->cpu, | ||
316 | .ip = al->addr, | ||
317 | .level = al->level, | ||
318 | .period = period, | ||
319 | .parent = sym_parent, | ||
320 | .filtered = symbol__parent_filter(sym_parent), | ||
321 | }; | ||
322 | |||
323 | return add_hist_entry(self, &entry, al, period); | ||
324 | } | ||
325 | |||
255 | int64_t | 326 | int64_t |
256 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) | 327 | hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) |
257 | { | 328 | { |
@@ -352,6 +423,7 @@ static void hists__apply_filters(struct hists *hists, struct hist_entry *he) | |||
352 | { | 423 | { |
353 | hists__filter_entry_by_dso(hists, he); | 424 | hists__filter_entry_by_dso(hists, he); |
354 | hists__filter_entry_by_thread(hists, he); | 425 | hists__filter_entry_by_thread(hists, he); |
426 | hists__filter_entry_by_symbol(hists, he); | ||
355 | } | 427 | } |
356 | 428 | ||
357 | static void __hists__collapse_resort(struct hists *hists, bool threaded) | 429 | static void __hists__collapse_resort(struct hists *hists, bool threaded) |
@@ -535,7 +607,7 @@ static void init_rem_hits(void) | |||
535 | rem_hits.ms.sym = rem_sq_bracket; | 607 | rem_hits.ms.sym = rem_sq_bracket; |
536 | } | 608 | } |
537 | 609 | ||
538 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 610 | static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root, |
539 | u64 total_samples, int depth, | 611 | u64 total_samples, int depth, |
540 | int depth_mask, int left_margin) | 612 | int depth_mask, int left_margin) |
541 | { | 613 | { |
@@ -543,21 +615,16 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
543 | struct callchain_node *child; | 615 | struct callchain_node *child; |
544 | struct callchain_list *chain; | 616 | struct callchain_list *chain; |
545 | int new_depth_mask = depth_mask; | 617 | int new_depth_mask = depth_mask; |
546 | u64 new_total; | ||
547 | u64 remaining; | 618 | u64 remaining; |
548 | size_t ret = 0; | 619 | size_t ret = 0; |
549 | int i; | 620 | int i; |
550 | uint entries_printed = 0; | 621 | uint entries_printed = 0; |
551 | 622 | ||
552 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 623 | remaining = total_samples; |
553 | new_total = self->children_hit; | ||
554 | else | ||
555 | new_total = total_samples; | ||
556 | |||
557 | remaining = new_total; | ||
558 | 624 | ||
559 | node = rb_first(&self->rb_root); | 625 | node = rb_first(root); |
560 | while (node) { | 626 | while (node) { |
627 | u64 new_total; | ||
561 | u64 cumul; | 628 | u64 cumul; |
562 | 629 | ||
563 | child = rb_entry(node, struct callchain_node, rb_node); | 630 | child = rb_entry(node, struct callchain_node, rb_node); |
@@ -585,11 +652,17 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
585 | list_for_each_entry(chain, &child->val, list) { | 652 | list_for_each_entry(chain, &child->val, list) { |
586 | ret += ipchain__fprintf_graph(fp, chain, depth, | 653 | ret += ipchain__fprintf_graph(fp, chain, depth, |
587 | new_depth_mask, i++, | 654 | new_depth_mask, i++, |
588 | new_total, | 655 | total_samples, |
589 | cumul, | 656 | cumul, |
590 | left_margin); | 657 | left_margin); |
591 | } | 658 | } |
592 | ret += __callchain__fprintf_graph(fp, child, new_total, | 659 | |
660 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
661 | new_total = child->children_hit; | ||
662 | else | ||
663 | new_total = total_samples; | ||
664 | |||
665 | ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total, | ||
593 | depth + 1, | 666 | depth + 1, |
594 | new_depth_mask | (1 << depth), | 667 | new_depth_mask | (1 << depth), |
595 | left_margin); | 668 | left_margin); |
@@ -599,61 +672,75 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
599 | } | 672 | } |
600 | 673 | ||
601 | if (callchain_param.mode == CHAIN_GRAPH_REL && | 674 | if (callchain_param.mode == CHAIN_GRAPH_REL && |
602 | remaining && remaining != new_total) { | 675 | remaining && remaining != total_samples) { |
603 | 676 | ||
604 | if (!rem_sq_bracket) | 677 | if (!rem_sq_bracket) |
605 | return ret; | 678 | return ret; |
606 | 679 | ||
607 | new_depth_mask &= ~(1 << (depth - 1)); | 680 | new_depth_mask &= ~(1 << (depth - 1)); |
608 | |||
609 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, | 681 | ret += ipchain__fprintf_graph(fp, &rem_hits, depth, |
610 | new_depth_mask, 0, new_total, | 682 | new_depth_mask, 0, total_samples, |
611 | remaining, left_margin); | 683 | remaining, left_margin); |
612 | } | 684 | } |
613 | 685 | ||
614 | return ret; | 686 | return ret; |
615 | } | 687 | } |
616 | 688 | ||
617 | static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 689 | static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root, |
618 | u64 total_samples, int left_margin) | 690 | u64 total_samples, int left_margin) |
619 | { | 691 | { |
692 | struct callchain_node *cnode; | ||
620 | struct callchain_list *chain; | 693 | struct callchain_list *chain; |
694 | u32 entries_printed = 0; | ||
621 | bool printed = false; | 695 | bool printed = false; |
696 | struct rb_node *node; | ||
622 | int i = 0; | 697 | int i = 0; |
623 | int ret = 0; | 698 | int ret; |
624 | u32 entries_printed = 0; | ||
625 | |||
626 | list_for_each_entry(chain, &self->val, list) { | ||
627 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
628 | continue; | ||
629 | |||
630 | if (!printed) { | ||
631 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
632 | ret += fprintf(fp, "|\n"); | ||
633 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
634 | ret += fprintf(fp, "---"); | ||
635 | |||
636 | left_margin += 3; | ||
637 | printed = true; | ||
638 | } else | ||
639 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
640 | 699 | ||
641 | if (chain->ms.sym) | 700 | /* |
642 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | 701 | * If have one single callchain root, don't bother printing |
643 | else | 702 | * its percentage (100 % in fractal mode and the same percentage |
644 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | 703 | * than the hist in graph mode). This also avoid one level of column. |
704 | */ | ||
705 | node = rb_first(root); | ||
706 | if (node && !rb_next(node)) { | ||
707 | cnode = rb_entry(node, struct callchain_node, rb_node); | ||
708 | list_for_each_entry(chain, &cnode->val, list) { | ||
709 | /* | ||
710 | * If we sort by symbol, the first entry is the same than | ||
711 | * the symbol. No need to print it otherwise it appears as | ||
712 | * displayed twice. | ||
713 | */ | ||
714 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
715 | continue; | ||
716 | if (!printed) { | ||
717 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
718 | ret += fprintf(fp, "|\n"); | ||
719 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
720 | ret += fprintf(fp, "---"); | ||
721 | left_margin += 3; | ||
722 | printed = true; | ||
723 | } else | ||
724 | ret += callchain__fprintf_left_margin(fp, left_margin); | ||
725 | |||
726 | if (chain->ms.sym) | ||
727 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); | ||
728 | else | ||
729 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | ||
645 | 730 | ||
646 | if (++entries_printed == callchain_param.print_limit) | 731 | if (++entries_printed == callchain_param.print_limit) |
647 | break; | 732 | break; |
733 | } | ||
734 | root = &cnode->rb_root; | ||
648 | } | 735 | } |
649 | 736 | ||
650 | ret += __callchain__fprintf_graph(fp, self, total_samples, 1, 1, left_margin); | 737 | return __callchain__fprintf_graph(fp, root, total_samples, |
651 | 738 | 1, 1, left_margin); | |
652 | return ret; | ||
653 | } | 739 | } |
654 | 740 | ||
655 | static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | 741 | static size_t __callchain__fprintf_flat(FILE *fp, |
656 | u64 total_samples) | 742 | struct callchain_node *self, |
743 | u64 total_samples) | ||
657 | { | 744 | { |
658 | struct callchain_list *chain; | 745 | struct callchain_list *chain; |
659 | size_t ret = 0; | 746 | size_t ret = 0; |
@@ -661,7 +748,7 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |||
661 | if (!self) | 748 | if (!self) |
662 | return 0; | 749 | return 0; |
663 | 750 | ||
664 | ret += callchain__fprintf_flat(fp, self->parent, total_samples); | 751 | ret += __callchain__fprintf_flat(fp, self->parent, total_samples); |
665 | 752 | ||
666 | 753 | ||
667 | list_for_each_entry(chain, &self->val, list) { | 754 | list_for_each_entry(chain, &self->val, list) { |
@@ -677,43 +764,58 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |||
677 | return ret; | 764 | return ret; |
678 | } | 765 | } |
679 | 766 | ||
680 | static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | 767 | static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self, |
681 | u64 total_samples, int left_margin) | 768 | u64 total_samples) |
682 | { | 769 | { |
683 | struct rb_node *rb_node; | ||
684 | struct callchain_node *chain; | ||
685 | size_t ret = 0; | 770 | size_t ret = 0; |
686 | u32 entries_printed = 0; | 771 | u32 entries_printed = 0; |
772 | struct rb_node *rb_node; | ||
773 | struct callchain_node *chain; | ||
687 | 774 | ||
688 | rb_node = rb_first(&self->sorted_chain); | 775 | rb_node = rb_first(self); |
689 | while (rb_node) { | 776 | while (rb_node) { |
690 | double percent; | 777 | double percent; |
691 | 778 | ||
692 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | 779 | chain = rb_entry(rb_node, struct callchain_node, rb_node); |
693 | percent = chain->hit * 100.0 / total_samples; | 780 | percent = chain->hit * 100.0 / total_samples; |
694 | switch (callchain_param.mode) { | 781 | |
695 | case CHAIN_FLAT: | 782 | ret = percent_color_fprintf(fp, " %6.2f%%\n", percent); |
696 | ret += percent_color_fprintf(fp, " %6.2f%%\n", | 783 | ret += __callchain__fprintf_flat(fp, chain, total_samples); |
697 | percent); | ||
698 | ret += callchain__fprintf_flat(fp, chain, total_samples); | ||
699 | break; | ||
700 | case CHAIN_GRAPH_ABS: /* Falldown */ | ||
701 | case CHAIN_GRAPH_REL: | ||
702 | ret += callchain__fprintf_graph(fp, chain, total_samples, | ||
703 | left_margin); | ||
704 | case CHAIN_NONE: | ||
705 | default: | ||
706 | break; | ||
707 | } | ||
708 | ret += fprintf(fp, "\n"); | 784 | ret += fprintf(fp, "\n"); |
709 | if (++entries_printed == callchain_param.print_limit) | 785 | if (++entries_printed == callchain_param.print_limit) |
710 | break; | 786 | break; |
787 | |||
711 | rb_node = rb_next(rb_node); | 788 | rb_node = rb_next(rb_node); |
712 | } | 789 | } |
713 | 790 | ||
714 | return ret; | 791 | return ret; |
715 | } | 792 | } |
716 | 793 | ||
794 | static size_t hist_entry_callchain__fprintf(struct hist_entry *he, | ||
795 | u64 total_samples, int left_margin, | ||
796 | FILE *fp) | ||
797 | { | ||
798 | switch (callchain_param.mode) { | ||
799 | case CHAIN_GRAPH_REL: | ||
800 | return callchain__fprintf_graph(fp, &he->sorted_chain, he->period, | ||
801 | left_margin); | ||
802 | break; | ||
803 | case CHAIN_GRAPH_ABS: | ||
804 | return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples, | ||
805 | left_margin); | ||
806 | break; | ||
807 | case CHAIN_FLAT: | ||
808 | return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples); | ||
809 | break; | ||
810 | case CHAIN_NONE: | ||
811 | break; | ||
812 | default: | ||
813 | pr_err("Bad callchain mode\n"); | ||
814 | } | ||
815 | |||
816 | return 0; | ||
817 | } | ||
818 | |||
717 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) | 819 | void hists__output_recalc_col_len(struct hists *hists, int max_rows) |
718 | { | 820 | { |
719 | struct rb_node *next = rb_first(&hists->entries); | 821 | struct rb_node *next = rb_first(&hists->entries); |
@@ -730,35 +832,35 @@ void hists__output_recalc_col_len(struct hists *hists, int max_rows) | |||
730 | } | 832 | } |
731 | } | 833 | } |
732 | 834 | ||
733 | static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s, | 835 | static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s, |
734 | size_t size, struct hists *pair_hists, | 836 | size_t size, struct hists *pair_hists, |
735 | bool show_displacement, long displacement, | 837 | bool show_displacement, long displacement, |
736 | bool color, u64 session_total) | 838 | bool color, u64 total_period) |
737 | { | 839 | { |
738 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; | 840 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; |
739 | u64 nr_events; | 841 | u64 nr_events; |
740 | const char *sep = symbol_conf.field_sep; | 842 | const char *sep = symbol_conf.field_sep; |
741 | int ret; | 843 | int ret; |
742 | 844 | ||
743 | if (symbol_conf.exclude_other && !self->parent) | 845 | if (symbol_conf.exclude_other && !he->parent) |
744 | return 0; | 846 | return 0; |
745 | 847 | ||
746 | if (pair_hists) { | 848 | if (pair_hists) { |
747 | period = self->pair ? self->pair->period : 0; | 849 | period = he->pair ? he->pair->period : 0; |
748 | nr_events = self->pair ? self->pair->nr_events : 0; | 850 | nr_events = he->pair ? he->pair->nr_events : 0; |
749 | total = pair_hists->stats.total_period; | 851 | total = pair_hists->stats.total_period; |
750 | period_sys = self->pair ? self->pair->period_sys : 0; | 852 | period_sys = he->pair ? he->pair->period_sys : 0; |
751 | period_us = self->pair ? self->pair->period_us : 0; | 853 | period_us = he->pair ? he->pair->period_us : 0; |
752 | period_guest_sys = self->pair ? self->pair->period_guest_sys : 0; | 854 | period_guest_sys = he->pair ? he->pair->period_guest_sys : 0; |
753 | period_guest_us = self->pair ? self->pair->period_guest_us : 0; | 855 | period_guest_us = he->pair ? he->pair->period_guest_us : 0; |
754 | } else { | 856 | } else { |
755 | period = self->period; | 857 | period = he->period; |
756 | nr_events = self->nr_events; | 858 | nr_events = he->nr_events; |
757 | total = session_total; | 859 | total = total_period; |
758 | period_sys = self->period_sys; | 860 | period_sys = he->period_sys; |
759 | period_us = self->period_us; | 861 | period_us = he->period_us; |
760 | period_guest_sys = self->period_guest_sys; | 862 | period_guest_sys = he->period_guest_sys; |
761 | period_guest_us = self->period_guest_us; | 863 | period_guest_us = he->period_guest_us; |
762 | } | 864 | } |
763 | 865 | ||
764 | if (total) { | 866 | if (total) { |
@@ -767,7 +869,7 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s, | |||
767 | sep ? "%.2f" : " %6.2f%%", | 869 | sep ? "%.2f" : " %6.2f%%", |
768 | (period * 100.0) / total); | 870 | (period * 100.0) / total); |
769 | else | 871 | else |
770 | ret = snprintf(s, size, sep ? "%.2f" : " %6.2f%%", | 872 | ret = scnprintf(s, size, sep ? "%.2f" : " %6.2f%%", |
771 | (period * 100.0) / total); | 873 | (period * 100.0) / total); |
772 | if (symbol_conf.show_cpu_utilization) { | 874 | if (symbol_conf.show_cpu_utilization) { |
773 | ret += percent_color_snprintf(s + ret, size - ret, | 875 | ret += percent_color_snprintf(s + ret, size - ret, |
@@ -790,20 +892,20 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s, | |||
790 | } | 892 | } |
791 | } | 893 | } |
792 | } else | 894 | } else |
793 | ret = snprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); | 895 | ret = scnprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period); |
794 | 896 | ||
795 | if (symbol_conf.show_nr_samples) { | 897 | if (symbol_conf.show_nr_samples) { |
796 | if (sep) | 898 | if (sep) |
797 | ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events); | 899 | ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events); |
798 | else | 900 | else |
799 | ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); | 901 | ret += scnprintf(s + ret, size - ret, "%11" PRIu64, nr_events); |
800 | } | 902 | } |
801 | 903 | ||
802 | if (symbol_conf.show_total_period) { | 904 | if (symbol_conf.show_total_period) { |
803 | if (sep) | 905 | if (sep) |
804 | ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); | 906 | ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); |
805 | else | 907 | else |
806 | ret += snprintf(s + ret, size - ret, " %12" PRIu64, period); | 908 | ret += scnprintf(s + ret, size - ret, " %12" PRIu64, period); |
807 | } | 909 | } |
808 | 910 | ||
809 | if (pair_hists) { | 911 | if (pair_hists) { |
@@ -812,31 +914,31 @@ static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s, | |||
812 | 914 | ||
813 | if (total > 0) | 915 | if (total > 0) |
814 | old_percent = (period * 100.0) / total; | 916 | old_percent = (period * 100.0) / total; |
815 | if (session_total > 0) | 917 | if (total_period > 0) |
816 | new_percent = (self->period * 100.0) / session_total; | 918 | new_percent = (he->period * 100.0) / total_period; |
817 | 919 | ||
818 | diff = new_percent - old_percent; | 920 | diff = new_percent - old_percent; |
819 | 921 | ||
820 | if (fabs(diff) >= 0.01) | 922 | if (fabs(diff) >= 0.01) |
821 | snprintf(bf, sizeof(bf), "%+4.2F%%", diff); | 923 | scnprintf(bf, sizeof(bf), "%+4.2F%%", diff); |
822 | else | 924 | else |
823 | snprintf(bf, sizeof(bf), " "); | 925 | scnprintf(bf, sizeof(bf), " "); |
824 | 926 | ||
825 | if (sep) | 927 | if (sep) |
826 | ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); | 928 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); |
827 | else | 929 | else |
828 | ret += snprintf(s + ret, size - ret, "%11.11s", bf); | 930 | ret += scnprintf(s + ret, size - ret, "%11.11s", bf); |
829 | 931 | ||
830 | if (show_displacement) { | 932 | if (show_displacement) { |
831 | if (displacement) | 933 | if (displacement) |
832 | snprintf(bf, sizeof(bf), "%+4ld", displacement); | 934 | scnprintf(bf, sizeof(bf), "%+4ld", displacement); |
833 | else | 935 | else |
834 | snprintf(bf, sizeof(bf), " "); | 936 | scnprintf(bf, sizeof(bf), " "); |
835 | 937 | ||
836 | if (sep) | 938 | if (sep) |
837 | ret += snprintf(s + ret, size - ret, "%c%s", *sep, bf); | 939 | ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf); |
838 | else | 940 | else |
839 | ret += snprintf(s + ret, size - ret, "%6.6s", bf); | 941 | ret += scnprintf(s + ret, size - ret, "%6.6s", bf); |
840 | } | 942 | } |
841 | } | 943 | } |
842 | 944 | ||
@@ -854,7 +956,7 @@ int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size, | |||
854 | if (se->elide) | 956 | if (se->elide) |
855 | continue; | 957 | continue; |
856 | 958 | ||
857 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); | 959 | ret += scnprintf(s + ret, size - ret, "%s", sep ?: " "); |
858 | ret += se->se_snprintf(he, s + ret, size - ret, | 960 | ret += se->se_snprintf(he, s + ret, size - ret, |
859 | hists__col_len(hists, se->se_width_idx)); | 961 | hists__col_len(hists, se->se_width_idx)); |
860 | } | 962 | } |
@@ -862,9 +964,10 @@ int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size, | |||
862 | return ret; | 964 | return ret; |
863 | } | 965 | } |
864 | 966 | ||
865 | int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, | 967 | static int hist_entry__fprintf(struct hist_entry *he, size_t size, |
866 | struct hists *pair_hists, bool show_displacement, | 968 | struct hists *hists, struct hists *pair_hists, |
867 | long displacement, FILE *fp, u64 session_total) | 969 | bool show_displacement, long displacement, |
970 | u64 total_period, FILE *fp) | ||
868 | { | 971 | { |
869 | char bf[512]; | 972 | char bf[512]; |
870 | int ret; | 973 | int ret; |
@@ -874,14 +977,14 @@ int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, | |||
874 | 977 | ||
875 | ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists, | 978 | ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists, |
876 | show_displacement, displacement, | 979 | show_displacement, displacement, |
877 | true, session_total); | 980 | true, total_period); |
878 | hist_entry__snprintf(he, bf + ret, size - ret, hists); | 981 | hist_entry__snprintf(he, bf + ret, size - ret, hists); |
879 | return fprintf(fp, "%s\n", bf); | 982 | return fprintf(fp, "%s\n", bf); |
880 | } | 983 | } |
881 | 984 | ||
882 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, | 985 | static size_t hist_entry__fprintf_callchain(struct hist_entry *he, |
883 | struct hists *hists, FILE *fp, | 986 | struct hists *hists, |
884 | u64 session_total) | 987 | u64 total_period, FILE *fp) |
885 | { | 988 | { |
886 | int left_margin = 0; | 989 | int left_margin = 0; |
887 | 990 | ||
@@ -889,11 +992,10 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, | |||
889 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, | 992 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, |
890 | typeof(*se), list); | 993 | typeof(*se), list); |
891 | left_margin = hists__col_len(hists, se->se_width_idx); | 994 | left_margin = hists__col_len(hists, se->se_width_idx); |
892 | left_margin -= thread__comm_len(self->thread); | 995 | left_margin -= thread__comm_len(he->thread); |
893 | } | 996 | } |
894 | 997 | ||
895 | return hist_entry_callchain__fprintf(fp, self, session_total, | 998 | return hist_entry_callchain__fprintf(he, total_period, left_margin, fp); |
896 | left_margin); | ||
897 | } | 999 | } |
898 | 1000 | ||
899 | size_t hists__fprintf(struct hists *hists, struct hists *pair, | 1001 | size_t hists__fprintf(struct hists *hists, struct hists *pair, |
@@ -903,6 +1005,7 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, | |||
903 | struct sort_entry *se; | 1005 | struct sort_entry *se; |
904 | struct rb_node *nd; | 1006 | struct rb_node *nd; |
905 | size_t ret = 0; | 1007 | size_t ret = 0; |
1008 | u64 total_period; | ||
906 | unsigned long position = 1; | 1009 | unsigned long position = 1; |
907 | long displacement = 0; | 1010 | long displacement = 0; |
908 | unsigned int width; | 1011 | unsigned int width; |
@@ -917,20 +1020,6 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, | |||
917 | 1020 | ||
918 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); | 1021 | fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); |
919 | 1022 | ||
920 | if (symbol_conf.show_nr_samples) { | ||
921 | if (sep) | ||
922 | fprintf(fp, "%cSamples", *sep); | ||
923 | else | ||
924 | fputs(" Samples ", fp); | ||
925 | } | ||
926 | |||
927 | if (symbol_conf.show_total_period) { | ||
928 | if (sep) | ||
929 | ret += fprintf(fp, "%cPeriod", *sep); | ||
930 | else | ||
931 | ret += fprintf(fp, " Period "); | ||
932 | } | ||
933 | |||
934 | if (symbol_conf.show_cpu_utilization) { | 1023 | if (symbol_conf.show_cpu_utilization) { |
935 | if (sep) { | 1024 | if (sep) { |
936 | ret += fprintf(fp, "%csys", *sep); | 1025 | ret += fprintf(fp, "%csys", *sep); |
@@ -940,8 +1029,8 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, | |||
940 | ret += fprintf(fp, "%cguest us", *sep); | 1029 | ret += fprintf(fp, "%cguest us", *sep); |
941 | } | 1030 | } |
942 | } else { | 1031 | } else { |
943 | ret += fprintf(fp, " sys "); | 1032 | ret += fprintf(fp, " sys "); |
944 | ret += fprintf(fp, " us "); | 1033 | ret += fprintf(fp, " us "); |
945 | if (perf_guest) { | 1034 | if (perf_guest) { |
946 | ret += fprintf(fp, " guest sys "); | 1035 | ret += fprintf(fp, " guest sys "); |
947 | ret += fprintf(fp, " guest us "); | 1036 | ret += fprintf(fp, " guest us "); |
@@ -949,6 +1038,20 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, | |||
949 | } | 1038 | } |
950 | } | 1039 | } |
951 | 1040 | ||
1041 | if (symbol_conf.show_nr_samples) { | ||
1042 | if (sep) | ||
1043 | fprintf(fp, "%cSamples", *sep); | ||
1044 | else | ||
1045 | fputs(" Samples ", fp); | ||
1046 | } | ||
1047 | |||
1048 | if (symbol_conf.show_total_period) { | ||
1049 | if (sep) | ||
1050 | ret += fprintf(fp, "%cPeriod", *sep); | ||
1051 | else | ||
1052 | ret += fprintf(fp, " Period "); | ||
1053 | } | ||
1054 | |||
952 | if (pair) { | 1055 | if (pair) { |
953 | if (sep) | 1056 | if (sep) |
954 | ret += fprintf(fp, "%cDelta", *sep); | 1057 | ret += fprintf(fp, "%cDelta", *sep); |
@@ -993,6 +1096,8 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, | |||
993 | goto print_entries; | 1096 | goto print_entries; |
994 | 1097 | ||
995 | fprintf(fp, "# ........"); | 1098 | fprintf(fp, "# ........"); |
1099 | if (symbol_conf.show_cpu_utilization) | ||
1100 | fprintf(fp, " ....... ......."); | ||
996 | if (symbol_conf.show_nr_samples) | 1101 | if (symbol_conf.show_nr_samples) |
997 | fprintf(fp, " .........."); | 1102 | fprintf(fp, " .........."); |
998 | if (symbol_conf.show_total_period) | 1103 | if (symbol_conf.show_total_period) |
@@ -1025,6 +1130,8 @@ size_t hists__fprintf(struct hists *hists, struct hists *pair, | |||
1025 | goto out; | 1130 | goto out; |
1026 | 1131 | ||
1027 | print_entries: | 1132 | print_entries: |
1133 | total_period = hists->stats.total_period; | ||
1134 | |||
1028 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | 1135 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { |
1029 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 1136 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
1030 | 1137 | ||
@@ -1040,11 +1147,10 @@ print_entries: | |||
1040 | ++position; | 1147 | ++position; |
1041 | } | 1148 | } |
1042 | ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement, | 1149 | ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement, |
1043 | displacement, fp, hists->stats.total_period); | 1150 | displacement, total_period, fp); |
1044 | 1151 | ||
1045 | if (symbol_conf.use_callchain) | 1152 | if (symbol_conf.use_callchain) |
1046 | ret += hist_entry__fprintf_callchain(h, hists, fp, | 1153 | ret += hist_entry__fprintf_callchain(h, hists, total_period, fp); |
1047 | hists->stats.total_period); | ||
1048 | if (max_rows && ++nr_rows >= max_rows) | 1154 | if (max_rows && ++nr_rows >= max_rows) |
1049 | goto out; | 1155 | goto out; |
1050 | 1156 | ||
@@ -1174,6 +1280,37 @@ void hists__filter_by_thread(struct hists *hists) | |||
1174 | } | 1280 | } |
1175 | } | 1281 | } |
1176 | 1282 | ||
1283 | static bool hists__filter_entry_by_symbol(struct hists *hists, | ||
1284 | struct hist_entry *he) | ||
1285 | { | ||
1286 | if (hists->symbol_filter_str != NULL && | ||
1287 | (!he->ms.sym || strstr(he->ms.sym->name, | ||
1288 | hists->symbol_filter_str) == NULL)) { | ||
1289 | he->filtered |= (1 << HIST_FILTER__SYMBOL); | ||
1290 | return true; | ||
1291 | } | ||
1292 | |||
1293 | return false; | ||
1294 | } | ||
1295 | |||
1296 | void hists__filter_by_symbol(struct hists *hists) | ||
1297 | { | ||
1298 | struct rb_node *nd; | ||
1299 | |||
1300 | hists->nr_entries = hists->stats.total_period = 0; | ||
1301 | hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | ||
1302 | hists__reset_col_len(hists); | ||
1303 | |||
1304 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
1305 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1306 | |||
1307 | if (hists__filter_entry_by_symbol(hists, h)) | ||
1308 | continue; | ||
1309 | |||
1310 | hists__remove_entry_filter(hists, h, HIST_FILTER__SYMBOL); | ||
1311 | } | ||
1312 | } | ||
1313 | |||
1177 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) | 1314 | int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) |
1178 | { | 1315 | { |
1179 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); | 1316 | return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index ff6f9d56ea41..2cae9df40e04 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -32,6 +32,7 @@ struct events_stats { | |||
32 | u32 nr_unknown_events; | 32 | u32 nr_unknown_events; |
33 | u32 nr_invalid_chains; | 33 | u32 nr_invalid_chains; |
34 | u32 nr_unknown_id; | 34 | u32 nr_unknown_id; |
35 | u32 nr_unprocessable_samples; | ||
35 | }; | 36 | }; |
36 | 37 | ||
37 | enum hist_column { | 38 | enum hist_column { |
@@ -41,6 +42,11 @@ enum hist_column { | |||
41 | HISTC_COMM, | 42 | HISTC_COMM, |
42 | HISTC_PARENT, | 43 | HISTC_PARENT, |
43 | HISTC_CPU, | 44 | HISTC_CPU, |
45 | HISTC_MISPREDICT, | ||
46 | HISTC_SYMBOL_FROM, | ||
47 | HISTC_SYMBOL_TO, | ||
48 | HISTC_DSO_FROM, | ||
49 | HISTC_DSO_TO, | ||
44 | HISTC_NR_COLS, /* Last entry */ | 50 | HISTC_NR_COLS, /* Last entry */ |
45 | }; | 51 | }; |
46 | 52 | ||
@@ -55,6 +61,8 @@ struct hists { | |||
55 | u64 nr_entries; | 61 | u64 nr_entries; |
56 | const struct thread *thread_filter; | 62 | const struct thread *thread_filter; |
57 | const struct dso *dso_filter; | 63 | const struct dso *dso_filter; |
64 | const char *uid_filter_str; | ||
65 | const char *symbol_filter_str; | ||
58 | pthread_mutex_t lock; | 66 | pthread_mutex_t lock; |
59 | struct events_stats stats; | 67 | struct events_stats stats; |
60 | u64 event_stream; | 68 | u64 event_stream; |
@@ -66,15 +74,18 @@ struct hists { | |||
66 | struct hist_entry *__hists__add_entry(struct hists *self, | 74 | struct hist_entry *__hists__add_entry(struct hists *self, |
67 | struct addr_location *al, | 75 | struct addr_location *al, |
68 | struct symbol *parent, u64 period); | 76 | struct symbol *parent, u64 period); |
69 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | 77 | int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right); |
70 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | 78 | int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right); |
71 | int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists, | ||
72 | struct hists *pair_hists, bool show_displacement, | ||
73 | long displacement, FILE *fp, u64 session_total); | ||
74 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, | 79 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, |
75 | struct hists *hists); | 80 | struct hists *hists); |
76 | void hist_entry__free(struct hist_entry *); | 81 | void hist_entry__free(struct hist_entry *); |
77 | 82 | ||
83 | struct hist_entry *__hists__add_branch_entry(struct hists *self, | ||
84 | struct addr_location *al, | ||
85 | struct symbol *sym_parent, | ||
86 | struct branch_info *bi, | ||
87 | u64 period); | ||
88 | |||
78 | void hists__output_resort(struct hists *self); | 89 | void hists__output_resort(struct hists *self); |
79 | void hists__output_resort_threaded(struct hists *hists); | 90 | void hists__output_resort_threaded(struct hists *hists); |
80 | void hists__collapse_resort(struct hists *self); | 91 | void hists__collapse_resort(struct hists *self); |
@@ -97,6 +108,7 @@ int hist_entry__annotate(struct hist_entry *self, size_t privsize); | |||
97 | 108 | ||
98 | void hists__filter_by_dso(struct hists *hists); | 109 | void hists__filter_by_dso(struct hists *hists); |
99 | void hists__filter_by_thread(struct hists *hists); | 110 | void hists__filter_by_thread(struct hists *hists); |
111 | void hists__filter_by_symbol(struct hists *hists); | ||
100 | 112 | ||
101 | u16 hists__col_len(struct hists *self, enum hist_column col); | 113 | u16 hists__col_len(struct hists *self, enum hist_column col); |
102 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | 114 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); |
@@ -135,6 +147,23 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | |||
135 | int refresh); | 147 | int refresh); |
136 | #endif | 148 | #endif |
137 | 149 | ||
150 | #ifdef NO_GTK2_SUPPORT | ||
151 | static inline | ||
152 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __used, | ||
153 | const char *help __used, | ||
154 | void(*timer)(void *arg) __used, | ||
155 | void *arg __used, | ||
156 | int refresh __used) | ||
157 | { | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | #else | ||
162 | int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, const char *help, | ||
163 | void(*timer)(void *arg), void *arg, | ||
164 | int refresh); | ||
165 | #endif | ||
166 | |||
138 | unsigned int hists__sort_list_width(struct hists *self); | 167 | unsigned int hists__sort_list_width(struct hists *self); |
139 | 168 | ||
140 | #endif /* __PERF_HIST_H */ | 169 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/include/asm/dwarf2.h b/tools/perf/util/include/asm/dwarf2.h index bb4198e7837a..afe38199e922 100644 --- a/tools/perf/util/include/asm/dwarf2.h +++ b/tools/perf/util/include/asm/dwarf2.h | |||
@@ -2,10 +2,12 @@ | |||
2 | #ifndef PERF_DWARF2_H | 2 | #ifndef PERF_DWARF2_H |
3 | #define PERF_DWARF2_H | 3 | #define PERF_DWARF2_H |
4 | 4 | ||
5 | /* dwarf2.h ... dummy header file for including arch/x86/lib/memcpy_64.S */ | 5 | /* dwarf2.h ... dummy header file for including arch/x86/lib/mem{cpy,set}_64.S */ |
6 | 6 | ||
7 | #define CFI_STARTPROC | 7 | #define CFI_STARTPROC |
8 | #define CFI_ENDPROC | 8 | #define CFI_ENDPROC |
9 | #define CFI_REMEMBER_STATE | ||
10 | #define CFI_RESTORE_STATE | ||
9 | 11 | ||
10 | #endif /* PERF_DWARF2_H */ | 12 | #endif /* PERF_DWARF2_H */ |
11 | 13 | ||
diff --git a/tools/perf/util/include/asm/unistd_32.h b/tools/perf/util/include/asm/unistd_32.h new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tools/perf/util/include/asm/unistd_32.h | |||
@@ -0,0 +1 @@ | |||
diff --git a/tools/perf/util/include/asm/unistd_64.h b/tools/perf/util/include/asm/unistd_64.h new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tools/perf/util/include/asm/unistd_64.h | |||
@@ -0,0 +1 @@ | |||
diff --git a/tools/perf/util/include/linux/bitmap.h b/tools/perf/util/include/linux/bitmap.h index eda4416efa0a..bb162e40c76c 100644 --- a/tools/perf/util/include/linux/bitmap.h +++ b/tools/perf/util/include/linux/bitmap.h | |||
@@ -5,6 +5,8 @@ | |||
5 | #include <linux/bitops.h> | 5 | #include <linux/bitops.h> |
6 | 6 | ||
7 | int __bitmap_weight(const unsigned long *bitmap, int bits); | 7 | int __bitmap_weight(const unsigned long *bitmap, int bits); |
8 | void __bitmap_or(unsigned long *dst, const unsigned long *bitmap1, | ||
9 | const unsigned long *bitmap2, int bits); | ||
8 | 10 | ||
9 | #define BITMAP_LAST_WORD_MASK(nbits) \ | 11 | #define BITMAP_LAST_WORD_MASK(nbits) \ |
10 | ( \ | 12 | ( \ |
@@ -32,4 +34,13 @@ static inline int bitmap_weight(const unsigned long *src, int nbits) | |||
32 | return __bitmap_weight(src, nbits); | 34 | return __bitmap_weight(src, nbits); |
33 | } | 35 | } |
34 | 36 | ||
37 | static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, | ||
38 | const unsigned long *src2, int nbits) | ||
39 | { | ||
40 | if (small_const_nbits(nbits)) | ||
41 | *dst = *src1 | *src2; | ||
42 | else | ||
43 | __bitmap_or(dst, src1, src2, nbits); | ||
44 | } | ||
45 | |||
35 | #endif /* _PERF_BITOPS_H */ | 46 | #endif /* _PERF_BITOPS_H */ |
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h index 62cdee78db7b..f1584833bd22 100644 --- a/tools/perf/util/include/linux/bitops.h +++ b/tools/perf/util/include/linux/bitops.h | |||
@@ -15,7 +15,7 @@ | |||
15 | (bit) = find_next_bit((addr), (size), (bit) + 1)) | 15 | (bit) = find_next_bit((addr), (size), (bit) + 1)) |
16 | 16 | ||
17 | /* same as for_each_set_bit() but use bit as value to start with */ | 17 | /* same as for_each_set_bit() but use bit as value to start with */ |
18 | #define for_each_set_bit_cont(bit, addr, size) \ | 18 | #define for_each_set_bit_from(bit, addr, size) \ |
19 | for ((bit) = find_next_bit((addr), (size), (bit)); \ | 19 | for ((bit) = find_next_bit((addr), (size), (bit)); \ |
20 | (bit) < (size); \ | 20 | (bit) < (size); \ |
21 | (bit) = find_next_bit((addr), (size), (bit) + 1)) | 21 | (bit) = find_next_bit((addr), (size), (bit) + 1)) |
diff --git a/tools/perf/util/include/linux/module.h b/tools/perf/util/include/linux/export.h index b43e2dc21e04..b43e2dc21e04 100644 --- a/tools/perf/util/include/linux/module.h +++ b/tools/perf/util/include/linux/export.h | |||
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 316aa0ab7122..dea6d1c1a954 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -212,6 +212,21 @@ size_t map__fprintf(struct map *self, FILE *fp) | |||
212 | self->start, self->end, self->pgoff, self->dso->name); | 212 | self->start, self->end, self->pgoff, self->dso->name); |
213 | } | 213 | } |
214 | 214 | ||
215 | size_t map__fprintf_dsoname(struct map *map, FILE *fp) | ||
216 | { | ||
217 | const char *dsoname; | ||
218 | |||
219 | if (map && map->dso && (map->dso->name || map->dso->long_name)) { | ||
220 | if (symbol_conf.show_kernel_path && map->dso->long_name) | ||
221 | dsoname = map->dso->long_name; | ||
222 | else if (map->dso->name) | ||
223 | dsoname = map->dso->name; | ||
224 | } else | ||
225 | dsoname = "[unknown]"; | ||
226 | |||
227 | return fprintf(fp, "%s", dsoname); | ||
228 | } | ||
229 | |||
215 | /* | 230 | /* |
216 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. | 231 | * objdump wants/reports absolute IPs for ET_EXEC, and RIPs for ET_DYN. |
217 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases. | 232 | * map->dso->adjust_symbols==1 for ET_EXEC-like cases. |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 2b8017f8a930..b100c20b7f94 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -118,6 +118,7 @@ void map__delete(struct map *self); | |||
118 | struct map *map__clone(struct map *self); | 118 | struct map *map__clone(struct map *self); |
119 | int map__overlap(struct map *l, struct map *r); | 119 | int map__overlap(struct map *l, struct map *r); |
120 | size_t map__fprintf(struct map *self, FILE *fp); | 120 | size_t map__fprintf(struct map *self, FILE *fp); |
121 | size_t map__fprintf_dsoname(struct map *map, FILE *fp); | ||
121 | 122 | ||
122 | int map__load(struct map *self, symbol_filter_t filter); | 123 | int map__load(struct map *self, symbol_filter_t filter); |
123 | struct symbol *map__find_symbol(struct map *self, | 124 | struct symbol *map__find_symbol(struct map *self, |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 531c283fc0c5..5b3a0ef4e232 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -11,6 +11,10 @@ | |||
11 | #include "cache.h" | 11 | #include "cache.h" |
12 | #include "header.h" | 12 | #include "header.h" |
13 | #include "debugfs.h" | 13 | #include "debugfs.h" |
14 | #include "parse-events-flex.h" | ||
15 | #include "pmu.h" | ||
16 | |||
17 | #define MAX_NAME_LEN 100 | ||
14 | 18 | ||
15 | struct event_symbol { | 19 | struct event_symbol { |
16 | u8 type; | 20 | u8 type; |
@@ -19,11 +23,8 @@ struct event_symbol { | |||
19 | const char *alias; | 23 | const char *alias; |
20 | }; | 24 | }; |
21 | 25 | ||
22 | enum event_result { | 26 | int parse_events_parse(struct list_head *list, struct list_head *list_tmp, |
23 | EVT_FAILED, | 27 | int *idx); |
24 | EVT_HANDLED, | ||
25 | EVT_HANDLED_ALL | ||
26 | }; | ||
27 | 28 | ||
28 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x | 29 | #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x |
29 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x | 30 | #define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x |
@@ -165,7 +166,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) | |||
165 | struct tracepoint_path *path = NULL; | 166 | struct tracepoint_path *path = NULL; |
166 | DIR *sys_dir, *evt_dir; | 167 | DIR *sys_dir, *evt_dir; |
167 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; | 168 | struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; |
168 | char id_buf[4]; | 169 | char id_buf[24]; |
169 | int fd; | 170 | int fd; |
170 | u64 id; | 171 | u64 id; |
171 | char evt_path[MAXPATHLEN]; | 172 | char evt_path[MAXPATHLEN]; |
@@ -354,7 +355,24 @@ const char *__event_name(int type, u64 config) | |||
354 | return "unknown"; | 355 | return "unknown"; |
355 | } | 356 | } |
356 | 357 | ||
357 | static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int size) | 358 | static int add_event(struct list_head *list, int *idx, |
359 | struct perf_event_attr *attr, char *name) | ||
360 | { | ||
361 | struct perf_evsel *evsel; | ||
362 | |||
363 | event_attr_init(attr); | ||
364 | |||
365 | evsel = perf_evsel__new(attr, (*idx)++); | ||
366 | if (!evsel) | ||
367 | return -ENOMEM; | ||
368 | |||
369 | list_add_tail(&evsel->node, list); | ||
370 | |||
371 | evsel->name = strdup(name); | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static int parse_aliases(char *str, const char *names[][MAX_ALIASES], int size) | ||
358 | { | 376 | { |
359 | int i, j; | 377 | int i, j; |
360 | int n, longest = -1; | 378 | int n, longest = -1; |
@@ -362,58 +380,57 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int | |||
362 | for (i = 0; i < size; i++) { | 380 | for (i = 0; i < size; i++) { |
363 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { | 381 | for (j = 0; j < MAX_ALIASES && names[i][j]; j++) { |
364 | n = strlen(names[i][j]); | 382 | n = strlen(names[i][j]); |
365 | if (n > longest && !strncasecmp(*str, names[i][j], n)) | 383 | if (n > longest && !strncasecmp(str, names[i][j], n)) |
366 | longest = n; | 384 | longest = n; |
367 | } | 385 | } |
368 | if (longest > 0) { | 386 | if (longest > 0) |
369 | *str += longest; | ||
370 | return i; | 387 | return i; |
371 | } | ||
372 | } | 388 | } |
373 | 389 | ||
374 | return -1; | 390 | return -1; |
375 | } | 391 | } |
376 | 392 | ||
377 | static enum event_result | 393 | int parse_events_add_cache(struct list_head *list, int *idx, |
378 | parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | 394 | char *type, char *op_result1, char *op_result2) |
379 | { | 395 | { |
380 | const char *s = *str; | 396 | struct perf_event_attr attr; |
397 | char name[MAX_NAME_LEN]; | ||
381 | int cache_type = -1, cache_op = -1, cache_result = -1; | 398 | int cache_type = -1, cache_op = -1, cache_result = -1; |
399 | char *op_result[2] = { op_result1, op_result2 }; | ||
400 | int i, n; | ||
382 | 401 | ||
383 | cache_type = parse_aliases(&s, hw_cache, PERF_COUNT_HW_CACHE_MAX); | ||
384 | /* | 402 | /* |
385 | * No fallback - if we cannot get a clear cache type | 403 | * No fallback - if we cannot get a clear cache type |
386 | * then bail out: | 404 | * then bail out: |
387 | */ | 405 | */ |
406 | cache_type = parse_aliases(type, hw_cache, | ||
407 | PERF_COUNT_HW_CACHE_MAX); | ||
388 | if (cache_type == -1) | 408 | if (cache_type == -1) |
389 | return EVT_FAILED; | 409 | return -EINVAL; |
410 | |||
411 | n = snprintf(name, MAX_NAME_LEN, "%s", type); | ||
412 | |||
413 | for (i = 0; (i < 2) && (op_result[i]); i++) { | ||
414 | char *str = op_result[i]; | ||
390 | 415 | ||
391 | while ((cache_op == -1 || cache_result == -1) && *s == '-') { | 416 | snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str); |
392 | ++s; | ||
393 | 417 | ||
394 | if (cache_op == -1) { | 418 | if (cache_op == -1) { |
395 | cache_op = parse_aliases(&s, hw_cache_op, | 419 | cache_op = parse_aliases(str, hw_cache_op, |
396 | PERF_COUNT_HW_CACHE_OP_MAX); | 420 | PERF_COUNT_HW_CACHE_OP_MAX); |
397 | if (cache_op >= 0) { | 421 | if (cache_op >= 0) { |
398 | if (!is_cache_op_valid(cache_type, cache_op)) | 422 | if (!is_cache_op_valid(cache_type, cache_op)) |
399 | return EVT_FAILED; | 423 | return -EINVAL; |
400 | continue; | 424 | continue; |
401 | } | 425 | } |
402 | } | 426 | } |
403 | 427 | ||
404 | if (cache_result == -1) { | 428 | if (cache_result == -1) { |
405 | cache_result = parse_aliases(&s, hw_cache_result, | 429 | cache_result = parse_aliases(str, hw_cache_result, |
406 | PERF_COUNT_HW_CACHE_RESULT_MAX); | 430 | PERF_COUNT_HW_CACHE_RESULT_MAX); |
407 | if (cache_result >= 0) | 431 | if (cache_result >= 0) |
408 | continue; | 432 | continue; |
409 | } | 433 | } |
410 | |||
411 | /* | ||
412 | * Can't parse this as a cache op or result, so back up | ||
413 | * to the '-'. | ||
414 | */ | ||
415 | --s; | ||
416 | break; | ||
417 | } | 434 | } |
418 | 435 | ||
419 | /* | 436 | /* |
@@ -428,20 +445,17 @@ parse_generic_hw_event(const char **str, struct perf_event_attr *attr) | |||
428 | if (cache_result == -1) | 445 | if (cache_result == -1) |
429 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; | 446 | cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS; |
430 | 447 | ||
431 | attr->config = cache_type | (cache_op << 8) | (cache_result << 16); | 448 | memset(&attr, 0, sizeof(attr)); |
432 | attr->type = PERF_TYPE_HW_CACHE; | 449 | attr.config = cache_type | (cache_op << 8) | (cache_result << 16); |
433 | 450 | attr.type = PERF_TYPE_HW_CACHE; | |
434 | *str = s; | 451 | return add_event(list, idx, &attr, name); |
435 | return EVT_HANDLED; | ||
436 | } | 452 | } |
437 | 453 | ||
438 | static enum event_result | 454 | static int add_tracepoint(struct list_head *list, int *idx, |
439 | parse_single_tracepoint_event(char *sys_name, | 455 | char *sys_name, char *evt_name) |
440 | const char *evt_name, | ||
441 | unsigned int evt_length, | ||
442 | struct perf_event_attr *attr, | ||
443 | const char **strp) | ||
444 | { | 456 | { |
457 | struct perf_event_attr attr; | ||
458 | char name[MAX_NAME_LEN]; | ||
445 | char evt_path[MAXPATHLEN]; | 459 | char evt_path[MAXPATHLEN]; |
446 | char id_buf[4]; | 460 | char id_buf[4]; |
447 | u64 id; | 461 | u64 id; |
@@ -452,130 +466,80 @@ parse_single_tracepoint_event(char *sys_name, | |||
452 | 466 | ||
453 | fd = open(evt_path, O_RDONLY); | 467 | fd = open(evt_path, O_RDONLY); |
454 | if (fd < 0) | 468 | if (fd < 0) |
455 | return EVT_FAILED; | 469 | return -1; |
456 | 470 | ||
457 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { | 471 | if (read(fd, id_buf, sizeof(id_buf)) < 0) { |
458 | close(fd); | 472 | close(fd); |
459 | return EVT_FAILED; | 473 | return -1; |
460 | } | 474 | } |
461 | 475 | ||
462 | close(fd); | 476 | close(fd); |
463 | id = atoll(id_buf); | 477 | id = atoll(id_buf); |
464 | attr->config = id; | ||
465 | attr->type = PERF_TYPE_TRACEPOINT; | ||
466 | *strp += strlen(sys_name) + evt_length + 1; /* + 1 for the ':' */ | ||
467 | |||
468 | attr->sample_type |= PERF_SAMPLE_RAW; | ||
469 | attr->sample_type |= PERF_SAMPLE_TIME; | ||
470 | attr->sample_type |= PERF_SAMPLE_CPU; | ||
471 | 478 | ||
472 | attr->sample_period = 1; | 479 | memset(&attr, 0, sizeof(attr)); |
480 | attr.config = id; | ||
481 | attr.type = PERF_TYPE_TRACEPOINT; | ||
482 | attr.sample_type |= PERF_SAMPLE_RAW; | ||
483 | attr.sample_type |= PERF_SAMPLE_TIME; | ||
484 | attr.sample_type |= PERF_SAMPLE_CPU; | ||
485 | attr.sample_period = 1; | ||
473 | 486 | ||
474 | 487 | snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name); | |
475 | return EVT_HANDLED; | 488 | return add_event(list, idx, &attr, name); |
476 | } | 489 | } |
477 | 490 | ||
478 | /* sys + ':' + event + ':' + flags*/ | 491 | static int add_tracepoint_multi(struct list_head *list, int *idx, |
479 | #define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128) | 492 | char *sys_name, char *evt_name) |
480 | static enum event_result | ||
481 | parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name, | ||
482 | const char *evt_exp, char *flags) | ||
483 | { | 493 | { |
484 | char evt_path[MAXPATHLEN]; | 494 | char evt_path[MAXPATHLEN]; |
485 | struct dirent *evt_ent; | 495 | struct dirent *evt_ent; |
486 | DIR *evt_dir; | 496 | DIR *evt_dir; |
497 | int ret = 0; | ||
487 | 498 | ||
488 | snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name); | 499 | snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name); |
489 | evt_dir = opendir(evt_path); | 500 | evt_dir = opendir(evt_path); |
490 | |||
491 | if (!evt_dir) { | 501 | if (!evt_dir) { |
492 | perror("Can't open event dir"); | 502 | perror("Can't open event dir"); |
493 | return EVT_FAILED; | 503 | return -1; |
494 | } | 504 | } |
495 | 505 | ||
496 | while ((evt_ent = readdir(evt_dir))) { | 506 | while (!ret && (evt_ent = readdir(evt_dir))) { |
497 | char event_opt[MAX_EVOPT_LEN + 1]; | ||
498 | int len; | ||
499 | |||
500 | if (!strcmp(evt_ent->d_name, ".") | 507 | if (!strcmp(evt_ent->d_name, ".") |
501 | || !strcmp(evt_ent->d_name, "..") | 508 | || !strcmp(evt_ent->d_name, "..") |
502 | || !strcmp(evt_ent->d_name, "enable") | 509 | || !strcmp(evt_ent->d_name, "enable") |
503 | || !strcmp(evt_ent->d_name, "filter")) | 510 | || !strcmp(evt_ent->d_name, "filter")) |
504 | continue; | 511 | continue; |
505 | 512 | ||
506 | if (!strglobmatch(evt_ent->d_name, evt_exp)) | 513 | if (!strglobmatch(evt_ent->d_name, evt_name)) |
507 | continue; | 514 | continue; |
508 | 515 | ||
509 | len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name, | 516 | ret = add_tracepoint(list, idx, sys_name, evt_ent->d_name); |
510 | evt_ent->d_name, flags ? ":" : "", | ||
511 | flags ?: ""); | ||
512 | if (len < 0) | ||
513 | return EVT_FAILED; | ||
514 | |||
515 | if (parse_events(evlist, event_opt, 0)) | ||
516 | return EVT_FAILED; | ||
517 | } | 517 | } |
518 | 518 | ||
519 | return EVT_HANDLED_ALL; | 519 | return ret; |
520 | } | 520 | } |
521 | 521 | ||
522 | static enum event_result | 522 | int parse_events_add_tracepoint(struct list_head *list, int *idx, |
523 | parse_tracepoint_event(struct perf_evlist *evlist, const char **strp, | 523 | char *sys, char *event) |
524 | struct perf_event_attr *attr) | ||
525 | { | 524 | { |
526 | const char *evt_name; | 525 | int ret; |
527 | char *flags = NULL, *comma_loc; | ||
528 | char sys_name[MAX_EVENT_LENGTH]; | ||
529 | unsigned int sys_length, evt_length; | ||
530 | |||
531 | if (debugfs_valid_mountpoint(tracing_events_path)) | ||
532 | return 0; | ||
533 | |||
534 | evt_name = strchr(*strp, ':'); | ||
535 | if (!evt_name) | ||
536 | return EVT_FAILED; | ||
537 | |||
538 | sys_length = evt_name - *strp; | ||
539 | if (sys_length >= MAX_EVENT_LENGTH) | ||
540 | return 0; | ||
541 | 526 | ||
542 | strncpy(sys_name, *strp, sys_length); | 527 | ret = debugfs_valid_mountpoint(tracing_events_path); |
543 | sys_name[sys_length] = '\0'; | 528 | if (ret) |
544 | evt_name = evt_name + 1; | 529 | return ret; |
545 | 530 | ||
546 | comma_loc = strchr(evt_name, ','); | 531 | return strpbrk(event, "*?") ? |
547 | if (comma_loc) { | 532 | add_tracepoint_multi(list, idx, sys, event) : |
548 | /* take the event name up to the comma */ | 533 | add_tracepoint(list, idx, sys, event); |
549 | evt_name = strndup(evt_name, comma_loc - evt_name); | ||
550 | } | ||
551 | flags = strchr(evt_name, ':'); | ||
552 | if (flags) { | ||
553 | /* split it out: */ | ||
554 | evt_name = strndup(evt_name, flags - evt_name); | ||
555 | flags++; | ||
556 | } | ||
557 | |||
558 | evt_length = strlen(evt_name); | ||
559 | if (evt_length >= MAX_EVENT_LENGTH) | ||
560 | return EVT_FAILED; | ||
561 | if (strpbrk(evt_name, "*?")) { | ||
562 | *strp += strlen(sys_name) + evt_length + 1; /* 1 == the ':' */ | ||
563 | return parse_multiple_tracepoint_event(evlist, sys_name, | ||
564 | evt_name, flags); | ||
565 | } else { | ||
566 | return parse_single_tracepoint_event(sys_name, evt_name, | ||
567 | evt_length, attr, strp); | ||
568 | } | ||
569 | } | 534 | } |
570 | 535 | ||
571 | static enum event_result | 536 | static int |
572 | parse_breakpoint_type(const char *type, const char **strp, | 537 | parse_breakpoint_type(const char *type, struct perf_event_attr *attr) |
573 | struct perf_event_attr *attr) | ||
574 | { | 538 | { |
575 | int i; | 539 | int i; |
576 | 540 | ||
577 | for (i = 0; i < 3; i++) { | 541 | for (i = 0; i < 3; i++) { |
578 | if (!type[i]) | 542 | if (!type || !type[i]) |
579 | break; | 543 | break; |
580 | 544 | ||
581 | switch (type[i]) { | 545 | switch (type[i]) { |
@@ -589,164 +553,146 @@ parse_breakpoint_type(const char *type, const char **strp, | |||
589 | attr->bp_type |= HW_BREAKPOINT_X; | 553 | attr->bp_type |= HW_BREAKPOINT_X; |
590 | break; | 554 | break; |
591 | default: | 555 | default: |
592 | return EVT_FAILED; | 556 | return -EINVAL; |
593 | } | 557 | } |
594 | } | 558 | } |
559 | |||
595 | if (!attr->bp_type) /* Default */ | 560 | if (!attr->bp_type) /* Default */ |
596 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | 561 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; |
597 | 562 | ||
598 | *strp = type + i; | 563 | return 0; |
599 | |||
600 | return EVT_HANDLED; | ||
601 | } | 564 | } |
602 | 565 | ||
603 | static enum event_result | 566 | int parse_events_add_breakpoint(struct list_head *list, int *idx, |
604 | parse_breakpoint_event(const char **strp, struct perf_event_attr *attr) | 567 | void *ptr, char *type) |
605 | { | 568 | { |
606 | const char *target; | 569 | struct perf_event_attr attr; |
607 | const char *type; | 570 | char name[MAX_NAME_LEN]; |
608 | char *endaddr; | ||
609 | u64 addr; | ||
610 | enum event_result err; | ||
611 | |||
612 | target = strchr(*strp, ':'); | ||
613 | if (!target) | ||
614 | return EVT_FAILED; | ||
615 | |||
616 | if (strncmp(*strp, "mem", target - *strp) != 0) | ||
617 | return EVT_FAILED; | ||
618 | |||
619 | target++; | ||
620 | |||
621 | addr = strtoull(target, &endaddr, 0); | ||
622 | if (target == endaddr) | ||
623 | return EVT_FAILED; | ||
624 | |||
625 | attr->bp_addr = addr; | ||
626 | *strp = endaddr; | ||
627 | 571 | ||
628 | type = strchr(target, ':'); | 572 | memset(&attr, 0, sizeof(attr)); |
573 | attr.bp_addr = (unsigned long) ptr; | ||
629 | 574 | ||
630 | /* If no type is defined, just rw as default */ | 575 | if (parse_breakpoint_type(type, &attr)) |
631 | if (!type) { | 576 | return -EINVAL; |
632 | attr->bp_type = HW_BREAKPOINT_R | HW_BREAKPOINT_W; | ||
633 | } else { | ||
634 | err = parse_breakpoint_type(++type, strp, attr); | ||
635 | if (err == EVT_FAILED) | ||
636 | return EVT_FAILED; | ||
637 | } | ||
638 | 577 | ||
639 | /* | 578 | /* |
640 | * We should find a nice way to override the access length | 579 | * We should find a nice way to override the access length |
641 | * Provide some defaults for now | 580 | * Provide some defaults for now |
642 | */ | 581 | */ |
643 | if (attr->bp_type == HW_BREAKPOINT_X) | 582 | if (attr.bp_type == HW_BREAKPOINT_X) |
644 | attr->bp_len = sizeof(long); | 583 | attr.bp_len = sizeof(long); |
645 | else | 584 | else |
646 | attr->bp_len = HW_BREAKPOINT_LEN_4; | 585 | attr.bp_len = HW_BREAKPOINT_LEN_4; |
647 | 586 | ||
648 | attr->type = PERF_TYPE_BREAKPOINT; | 587 | attr.type = PERF_TYPE_BREAKPOINT; |
649 | 588 | ||
650 | return EVT_HANDLED; | 589 | snprintf(name, MAX_NAME_LEN, "mem:%p:%s", ptr, type ? type : "rw"); |
590 | return add_event(list, idx, &attr, name); | ||
651 | } | 591 | } |
652 | 592 | ||
653 | static int check_events(const char *str, unsigned int i) | 593 | static int config_term(struct perf_event_attr *attr, |
594 | struct parse_events__term *term) | ||
654 | { | 595 | { |
655 | int n; | 596 | switch (term->type) { |
597 | case PARSE_EVENTS__TERM_TYPE_CONFIG: | ||
598 | attr->config = term->val.num; | ||
599 | break; | ||
600 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: | ||
601 | attr->config1 = term->val.num; | ||
602 | break; | ||
603 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: | ||
604 | attr->config2 = term->val.num; | ||
605 | break; | ||
606 | case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: | ||
607 | attr->sample_period = term->val.num; | ||
608 | break; | ||
609 | case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: | ||
610 | /* | ||
611 | * TODO uncomment when the field is available | ||
612 | * attr->branch_sample_type = term->val.num; | ||
613 | */ | ||
614 | break; | ||
615 | default: | ||
616 | return -EINVAL; | ||
617 | } | ||
618 | return 0; | ||
619 | } | ||
656 | 620 | ||
657 | n = strlen(event_symbols[i].symbol); | 621 | static int config_attr(struct perf_event_attr *attr, |
658 | if (!strncasecmp(str, event_symbols[i].symbol, n)) | 622 | struct list_head *head, int fail) |
659 | return n; | 623 | { |
624 | struct parse_events__term *term; | ||
660 | 625 | ||
661 | n = strlen(event_symbols[i].alias); | 626 | list_for_each_entry(term, head, list) |
662 | if (n) { | 627 | if (config_term(attr, term) && fail) |
663 | if (!strncasecmp(str, event_symbols[i].alias, n)) | 628 | return -EINVAL; |
664 | return n; | ||
665 | } | ||
666 | 629 | ||
667 | return 0; | 630 | return 0; |
668 | } | 631 | } |
669 | 632 | ||
670 | static enum event_result | 633 | int parse_events_add_numeric(struct list_head *list, int *idx, |
671 | parse_symbolic_event(const char **strp, struct perf_event_attr *attr) | 634 | unsigned long type, unsigned long config, |
635 | struct list_head *head_config) | ||
672 | { | 636 | { |
673 | const char *str = *strp; | 637 | struct perf_event_attr attr; |
674 | unsigned int i; | 638 | |
675 | int n; | 639 | memset(&attr, 0, sizeof(attr)); |
676 | 640 | attr.type = type; | |
677 | for (i = 0; i < ARRAY_SIZE(event_symbols); i++) { | 641 | attr.config = config; |
678 | n = check_events(str, i); | 642 | |
679 | if (n > 0) { | 643 | if (head_config && |
680 | attr->type = event_symbols[i].type; | 644 | config_attr(&attr, head_config, 1)) |
681 | attr->config = event_symbols[i].config; | 645 | return -EINVAL; |
682 | *strp = str + n; | 646 | |
683 | return EVT_HANDLED; | 647 | return add_event(list, idx, &attr, |
684 | } | 648 | (char *) __event_name(type, config)); |
685 | } | ||
686 | return EVT_FAILED; | ||
687 | } | 649 | } |
688 | 650 | ||
689 | static enum event_result | 651 | int parse_events_add_pmu(struct list_head *list, int *idx, |
690 | parse_raw_event(const char **strp, struct perf_event_attr *attr) | 652 | char *name, struct list_head *head_config) |
691 | { | 653 | { |
692 | const char *str = *strp; | 654 | struct perf_event_attr attr; |
693 | u64 config; | 655 | struct perf_pmu *pmu; |
694 | int n; | 656 | |
695 | 657 | pmu = perf_pmu__find(name); | |
696 | if (*str != 'r') | 658 | if (!pmu) |
697 | return EVT_FAILED; | 659 | return -EINVAL; |
698 | n = hex2u64(str + 1, &config); | 660 | |
699 | if (n > 0) { | 661 | memset(&attr, 0, sizeof(attr)); |
700 | const char *end = str + n + 1; | 662 | |
701 | if (*end != '\0' && *end != ',' && *end != ':') | 663 | /* |
702 | return EVT_FAILED; | 664 | * Configure hardcoded terms first, no need to check |
703 | 665 | * return value when called with fail == 0 ;) | |
704 | *strp = end; | 666 | */ |
705 | attr->type = PERF_TYPE_RAW; | 667 | config_attr(&attr, head_config, 0); |
706 | attr->config = config; | 668 | |
707 | return EVT_HANDLED; | 669 | if (perf_pmu__config(pmu, &attr, head_config)) |
708 | } | 670 | return -EINVAL; |
709 | return EVT_FAILED; | 671 | |
672 | return add_event(list, idx, &attr, (char *) "pmu"); | ||
710 | } | 673 | } |
711 | 674 | ||
712 | static enum event_result | 675 | void parse_events_update_lists(struct list_head *list_event, |
713 | parse_numeric_event(const char **strp, struct perf_event_attr *attr) | 676 | struct list_head *list_all) |
714 | { | 677 | { |
715 | const char *str = *strp; | 678 | /* |
716 | char *endp; | 679 | * Called for single event definition. Update the |
717 | unsigned long type; | 680 | * 'all event' list, and reinit the 'signle event' |
718 | u64 config; | 681 | * list, for next event definition. |
719 | 682 | */ | |
720 | type = strtoul(str, &endp, 0); | 683 | list_splice_tail(list_event, list_all); |
721 | if (endp > str && type < PERF_TYPE_MAX && *endp == ':') { | 684 | INIT_LIST_HEAD(list_event); |
722 | str = endp + 1; | ||
723 | config = strtoul(str, &endp, 0); | ||
724 | if (endp > str) { | ||
725 | attr->type = type; | ||
726 | attr->config = config; | ||
727 | *strp = endp; | ||
728 | return EVT_HANDLED; | ||
729 | } | ||
730 | } | ||
731 | return EVT_FAILED; | ||
732 | } | 685 | } |
733 | 686 | ||
734 | static int | 687 | int parse_events_modifier(struct list_head *list, char *str) |
735 | parse_event_modifier(const char **strp, struct perf_event_attr *attr) | ||
736 | { | 688 | { |
737 | const char *str = *strp; | 689 | struct perf_evsel *evsel; |
738 | int exclude = 0; | 690 | int exclude = 0, exclude_GH = 0; |
739 | int eu = 0, ek = 0, eh = 0, precise = 0; | 691 | int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0; |
740 | 692 | ||
741 | if (!*str) | 693 | if (str == NULL) |
742 | return 0; | 694 | return 0; |
743 | 695 | ||
744 | if (*str == ',') | ||
745 | return 0; | ||
746 | |||
747 | if (*str++ != ':') | ||
748 | return -1; | ||
749 | |||
750 | while (*str) { | 696 | while (*str) { |
751 | if (*str == 'u') { | 697 | if (*str == 'u') { |
752 | if (!exclude) | 698 | if (!exclude) |
@@ -760,6 +706,14 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
760 | if (!exclude) | 706 | if (!exclude) |
761 | exclude = eu = ek = eh = 1; | 707 | exclude = eu = ek = eh = 1; |
762 | eh = 0; | 708 | eh = 0; |
709 | } else if (*str == 'G') { | ||
710 | if (!exclude_GH) | ||
711 | exclude_GH = eG = eH = 1; | ||
712 | eG = 0; | ||
713 | } else if (*str == 'H') { | ||
714 | if (!exclude_GH) | ||
715 | exclude_GH = eG = eH = 1; | ||
716 | eH = 0; | ||
763 | } else if (*str == 'p') { | 717 | } else if (*str == 'p') { |
764 | precise++; | 718 | precise++; |
765 | } else | 719 | } else |
@@ -767,108 +721,62 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr) | |||
767 | 721 | ||
768 | ++str; | 722 | ++str; |
769 | } | 723 | } |
770 | if (str < *strp + 2) | ||
771 | return -1; | ||
772 | 724 | ||
773 | *strp = str; | 725 | /* |
726 | * precise ip: | ||
727 | * | ||
728 | * 0 - SAMPLE_IP can have arbitrary skid | ||
729 | * 1 - SAMPLE_IP must have constant skid | ||
730 | * 2 - SAMPLE_IP requested to have 0 skid | ||
731 | * 3 - SAMPLE_IP must have 0 skid | ||
732 | * | ||
733 | * See also PERF_RECORD_MISC_EXACT_IP | ||
734 | */ | ||
735 | if (precise > 3) | ||
736 | return -EINVAL; | ||
774 | 737 | ||
775 | attr->exclude_user = eu; | 738 | list_for_each_entry(evsel, list, node) { |
776 | attr->exclude_kernel = ek; | 739 | evsel->attr.exclude_user = eu; |
777 | attr->exclude_hv = eh; | 740 | evsel->attr.exclude_kernel = ek; |
778 | attr->precise_ip = precise; | 741 | evsel->attr.exclude_hv = eh; |
742 | evsel->attr.precise_ip = precise; | ||
743 | evsel->attr.exclude_host = eH; | ||
744 | evsel->attr.exclude_guest = eG; | ||
745 | } | ||
779 | 746 | ||
780 | return 0; | 747 | return 0; |
781 | } | 748 | } |
782 | 749 | ||
783 | /* | 750 | int parse_events(struct perf_evlist *evlist, const char *str, int unset __used) |
784 | * Each event can have multiple symbolic names. | ||
785 | * Symbolic names are (almost) exactly matched. | ||
786 | */ | ||
787 | static enum event_result | ||
788 | parse_event_symbols(struct perf_evlist *evlist, const char **str, | ||
789 | struct perf_event_attr *attr) | ||
790 | { | 751 | { |
791 | enum event_result ret; | 752 | LIST_HEAD(list); |
792 | 753 | LIST_HEAD(list_tmp); | |
793 | ret = parse_tracepoint_event(evlist, str, attr); | 754 | YY_BUFFER_STATE buffer; |
794 | if (ret != EVT_FAILED) | 755 | int ret, idx = evlist->nr_entries; |
795 | goto modifier; | ||
796 | |||
797 | ret = parse_raw_event(str, attr); | ||
798 | if (ret != EVT_FAILED) | ||
799 | goto modifier; | ||
800 | 756 | ||
801 | ret = parse_numeric_event(str, attr); | 757 | buffer = parse_events__scan_string(str); |
802 | if (ret != EVT_FAILED) | ||
803 | goto modifier; | ||
804 | 758 | ||
805 | ret = parse_symbolic_event(str, attr); | 759 | ret = parse_events_parse(&list, &list_tmp, &idx); |
806 | if (ret != EVT_FAILED) | ||
807 | goto modifier; | ||
808 | 760 | ||
809 | ret = parse_generic_hw_event(str, attr); | 761 | parse_events__flush_buffer(buffer); |
810 | if (ret != EVT_FAILED) | 762 | parse_events__delete_buffer(buffer); |
811 | goto modifier; | ||
812 | 763 | ||
813 | ret = parse_breakpoint_event(str, attr); | 764 | if (!ret) { |
814 | if (ret != EVT_FAILED) | 765 | int entries = idx - evlist->nr_entries; |
815 | goto modifier; | 766 | perf_evlist__splice_list_tail(evlist, &list, entries); |
816 | 767 | return 0; | |
817 | fprintf(stderr, "invalid or unsupported event: '%s'\n", *str); | ||
818 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
819 | return EVT_FAILED; | ||
820 | |||
821 | modifier: | ||
822 | if (parse_event_modifier(str, attr) < 0) { | ||
823 | fprintf(stderr, "invalid event modifier: '%s'\n", *str); | ||
824 | fprintf(stderr, "Run 'perf list' for a list of valid events and modifiers\n"); | ||
825 | |||
826 | return EVT_FAILED; | ||
827 | } | 768 | } |
828 | 769 | ||
770 | /* | ||
771 | * There are 2 users - builtin-record and builtin-test objects. | ||
772 | * Both call perf_evlist__delete in case of error, so we dont | ||
773 | * need to bother. | ||
774 | */ | ||
775 | fprintf(stderr, "invalid or unsupported event: '%s'\n", str); | ||
776 | fprintf(stderr, "Run 'perf list' for a list of valid events\n"); | ||
829 | return ret; | 777 | return ret; |
830 | } | 778 | } |
831 | 779 | ||
832 | int parse_events(struct perf_evlist *evlist , const char *str, int unset __used) | ||
833 | { | ||
834 | struct perf_event_attr attr; | ||
835 | enum event_result ret; | ||
836 | const char *ostr; | ||
837 | |||
838 | for (;;) { | ||
839 | ostr = str; | ||
840 | memset(&attr, 0, sizeof(attr)); | ||
841 | ret = parse_event_symbols(evlist, &str, &attr); | ||
842 | if (ret == EVT_FAILED) | ||
843 | return -1; | ||
844 | |||
845 | if (!(*str == 0 || *str == ',' || isspace(*str))) | ||
846 | return -1; | ||
847 | |||
848 | if (ret != EVT_HANDLED_ALL) { | ||
849 | struct perf_evsel *evsel; | ||
850 | evsel = perf_evsel__new(&attr, evlist->nr_entries); | ||
851 | if (evsel == NULL) | ||
852 | return -1; | ||
853 | perf_evlist__add(evlist, evsel); | ||
854 | |||
855 | evsel->name = calloc(str - ostr + 1, 1); | ||
856 | if (!evsel->name) | ||
857 | return -1; | ||
858 | strncpy(evsel->name, ostr, str - ostr); | ||
859 | } | ||
860 | |||
861 | if (*str == 0) | ||
862 | break; | ||
863 | if (*str == ',') | ||
864 | ++str; | ||
865 | while (isspace(*str)) | ||
866 | ++str; | ||
867 | } | ||
868 | |||
869 | return 0; | ||
870 | } | ||
871 | |||
872 | int parse_events_option(const struct option *opt, const char *str, | 780 | int parse_events_option(const struct option *opt, const char *str, |
873 | int unset __used) | 781 | int unset __used) |
874 | { | 782 | { |
@@ -1041,8 +949,6 @@ int print_hwcache_events(const char *event_glob) | |||
1041 | return printed; | 949 | return printed; |
1042 | } | 950 | } |
1043 | 951 | ||
1044 | #define MAX_NAME_LEN 100 | ||
1045 | |||
1046 | /* | 952 | /* |
1047 | * Print the help text for the event symbols: | 953 | * Print the help text for the event symbols: |
1048 | */ | 954 | */ |
@@ -1091,8 +997,12 @@ void print_events(const char *event_glob) | |||
1091 | 997 | ||
1092 | printf("\n"); | 998 | printf("\n"); |
1093 | printf(" %-50s [%s]\n", | 999 | printf(" %-50s [%s]\n", |
1094 | "rNNN (see 'perf list --help' on how to encode it)", | 1000 | "rNNN", |
1095 | event_type_descriptors[PERF_TYPE_RAW]); | 1001 | event_type_descriptors[PERF_TYPE_RAW]); |
1002 | printf(" %-50s [%s]\n", | ||
1003 | "cpu/t1=v1[,t2=v2,t3 ...]/modifier", | ||
1004 | event_type_descriptors[PERF_TYPE_RAW]); | ||
1005 | printf(" (see 'perf list --help' on how to encode it)\n"); | ||
1096 | printf("\n"); | 1006 | printf("\n"); |
1097 | 1007 | ||
1098 | printf(" %-50s [%s]\n", | 1008 | printf(" %-50s [%s]\n", |
@@ -1102,3 +1012,51 @@ void print_events(const char *event_glob) | |||
1102 | 1012 | ||
1103 | print_tracepoint_events(NULL, NULL); | 1013 | print_tracepoint_events(NULL, NULL); |
1104 | } | 1014 | } |
1015 | |||
1016 | int parse_events__is_hardcoded_term(struct parse_events__term *term) | ||
1017 | { | ||
1018 | return term->type <= PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX; | ||
1019 | } | ||
1020 | |||
1021 | int parse_events__new_term(struct parse_events__term **_term, int type, | ||
1022 | char *config, char *str, long num) | ||
1023 | { | ||
1024 | struct parse_events__term *term; | ||
1025 | |||
1026 | term = zalloc(sizeof(*term)); | ||
1027 | if (!term) | ||
1028 | return -ENOMEM; | ||
1029 | |||
1030 | INIT_LIST_HEAD(&term->list); | ||
1031 | term->type = type; | ||
1032 | term->config = config; | ||
1033 | |||
1034 | switch (type) { | ||
1035 | case PARSE_EVENTS__TERM_TYPE_CONFIG: | ||
1036 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: | ||
1037 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: | ||
1038 | case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: | ||
1039 | case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE: | ||
1040 | case PARSE_EVENTS__TERM_TYPE_NUM: | ||
1041 | term->val.num = num; | ||
1042 | break; | ||
1043 | case PARSE_EVENTS__TERM_TYPE_STR: | ||
1044 | term->val.str = str; | ||
1045 | break; | ||
1046 | default: | ||
1047 | return -EINVAL; | ||
1048 | } | ||
1049 | |||
1050 | *_term = term; | ||
1051 | return 0; | ||
1052 | } | ||
1053 | |||
1054 | void parse_events__free_terms(struct list_head *terms) | ||
1055 | { | ||
1056 | struct parse_events__term *term, *h; | ||
1057 | |||
1058 | list_for_each_entry_safe(term, h, terms, list) | ||
1059 | free(term); | ||
1060 | |||
1061 | free(terms); | ||
1062 | } | ||
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 7e0cbe75d5f1..ca069f893381 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -33,6 +33,55 @@ extern int parse_filter(const struct option *opt, const char *str, int unset); | |||
33 | 33 | ||
34 | #define EVENTS_HELP_MAX (128*1024) | 34 | #define EVENTS_HELP_MAX (128*1024) |
35 | 35 | ||
36 | enum { | ||
37 | PARSE_EVENTS__TERM_TYPE_CONFIG, | ||
38 | PARSE_EVENTS__TERM_TYPE_CONFIG1, | ||
39 | PARSE_EVENTS__TERM_TYPE_CONFIG2, | ||
40 | PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD, | ||
41 | PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, | ||
42 | PARSE_EVENTS__TERM_TYPE_NUM, | ||
43 | PARSE_EVENTS__TERM_TYPE_STR, | ||
44 | |||
45 | PARSE_EVENTS__TERM_TYPE_HARDCODED_MAX = | ||
46 | PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE, | ||
47 | }; | ||
48 | |||
49 | struct parse_events__term { | ||
50 | char *config; | ||
51 | union { | ||
52 | char *str; | ||
53 | long num; | ||
54 | } val; | ||
55 | int type; | ||
56 | |||
57 | struct list_head list; | ||
58 | }; | ||
59 | |||
60 | int parse_events__is_hardcoded_term(struct parse_events__term *term); | ||
61 | int parse_events__new_term(struct parse_events__term **term, int type, | ||
62 | char *config, char *str, long num); | ||
63 | void parse_events__free_terms(struct list_head *terms); | ||
64 | int parse_events_modifier(struct list_head *list __used, char *str __used); | ||
65 | int parse_events_add_tracepoint(struct list_head *list, int *idx, | ||
66 | char *sys, char *event); | ||
67 | int parse_events_add_raw(struct perf_evlist *evlist, unsigned long config, | ||
68 | unsigned long config1, unsigned long config2, | ||
69 | char *mod); | ||
70 | int parse_events_add_numeric(struct list_head *list, int *idx, | ||
71 | unsigned long type, unsigned long config, | ||
72 | struct list_head *head_config); | ||
73 | int parse_events_add_cache(struct list_head *list, int *idx, | ||
74 | char *type, char *op_result1, char *op_result2); | ||
75 | int parse_events_add_breakpoint(struct list_head *list, int *idx, | ||
76 | void *ptr, char *type); | ||
77 | int parse_events_add_pmu(struct list_head *list, int *idx, | ||
78 | char *pmu , struct list_head *head_config); | ||
79 | void parse_events_update_lists(struct list_head *list_event, | ||
80 | struct list_head *list_all); | ||
81 | void parse_events_error(struct list_head *list_all, | ||
82 | struct list_head *list_event, | ||
83 | int *idx, char const *msg); | ||
84 | |||
36 | void print_events(const char *event_glob); | 85 | void print_events(const char *event_glob); |
37 | void print_events_type(u8 type); | 86 | void print_events_type(u8 type); |
38 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob); | 87 | void print_tracepoint_events(const char *subsys_glob, const char *event_glob); |
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l new file mode 100644 index 000000000000..05d766e3ecb5 --- /dev/null +++ b/tools/perf/util/parse-events.l | |||
@@ -0,0 +1,127 @@ | |||
1 | |||
2 | %option prefix="parse_events_" | ||
3 | |||
4 | %{ | ||
5 | #include <errno.h> | ||
6 | #include "../perf.h" | ||
7 | #include "parse-events-bison.h" | ||
8 | #include "parse-events.h" | ||
9 | |||
10 | static int __value(char *str, int base, int token) | ||
11 | { | ||
12 | long num; | ||
13 | |||
14 | errno = 0; | ||
15 | num = strtoul(str, NULL, base); | ||
16 | if (errno) | ||
17 | return PE_ERROR; | ||
18 | |||
19 | parse_events_lval.num = num; | ||
20 | return token; | ||
21 | } | ||
22 | |||
23 | static int value(int base) | ||
24 | { | ||
25 | return __value(parse_events_text, base, PE_VALUE); | ||
26 | } | ||
27 | |||
28 | static int raw(void) | ||
29 | { | ||
30 | return __value(parse_events_text + 1, 16, PE_RAW); | ||
31 | } | ||
32 | |||
33 | static int str(int token) | ||
34 | { | ||
35 | parse_events_lval.str = strdup(parse_events_text); | ||
36 | return token; | ||
37 | } | ||
38 | |||
39 | static int sym(int type, int config) | ||
40 | { | ||
41 | parse_events_lval.num = (type << 16) + config; | ||
42 | return PE_VALUE_SYM; | ||
43 | } | ||
44 | |||
45 | static int term(int type) | ||
46 | { | ||
47 | parse_events_lval.num = type; | ||
48 | return PE_TERM; | ||
49 | } | ||
50 | |||
51 | %} | ||
52 | |||
53 | num_dec [0-9]+ | ||
54 | num_hex 0x[a-fA-F0-9]+ | ||
55 | num_raw_hex [a-fA-F0-9]+ | ||
56 | name [a-zA-Z_*?][a-zA-Z0-9_*?]* | ||
57 | modifier_event [ukhp]{1,5} | ||
58 | modifier_bp [rwx] | ||
59 | |||
60 | %% | ||
61 | cpu-cycles|cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); } | ||
62 | stalled-cycles-frontend|idle-cycles-frontend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); } | ||
63 | stalled-cycles-backend|idle-cycles-backend { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); } | ||
64 | instructions { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS); } | ||
65 | cache-references { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES); } | ||
66 | cache-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES); } | ||
67 | branch-instructions|branches { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS); } | ||
68 | branch-misses { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES); } | ||
69 | bus-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES); } | ||
70 | ref-cycles { return sym(PERF_TYPE_HARDWARE, PERF_COUNT_HW_REF_CPU_CYCLES); } | ||
71 | cpu-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK); } | ||
72 | task-clock { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK); } | ||
73 | page-faults|faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS); } | ||
74 | minor-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN); } | ||
75 | major-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ); } | ||
76 | context-switches|cs { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES); } | ||
77 | cpu-migrations|migrations { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS); } | ||
78 | alignment-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS); } | ||
79 | emulation-faults { return sym(PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS); } | ||
80 | |||
81 | L1-dcache|l1-d|l1d|L1-data | | ||
82 | L1-icache|l1-i|l1i|L1-instruction | | ||
83 | LLC|L2 | | ||
84 | dTLB|d-tlb|Data-TLB | | ||
85 | iTLB|i-tlb|Instruction-TLB | | ||
86 | branch|branches|bpu|btb|bpc | | ||
87 | node { return str(PE_NAME_CACHE_TYPE); } | ||
88 | |||
89 | load|loads|read | | ||
90 | store|stores|write | | ||
91 | prefetch|prefetches | | ||
92 | speculative-read|speculative-load | | ||
93 | refs|Reference|ops|access | | ||
94 | misses|miss { return str(PE_NAME_CACHE_OP_RESULT); } | ||
95 | |||
96 | /* | ||
97 | * These are event config hardcoded term names to be specified | ||
98 | * within xxx/.../ syntax. So far we dont clash with other names, | ||
99 | * so we can put them here directly. In case the we have a conflict | ||
100 | * in future, this needs to go into '//' condition block. | ||
101 | */ | ||
102 | config { return term(PARSE_EVENTS__TERM_TYPE_CONFIG); } | ||
103 | config1 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG1); } | ||
104 | config2 { return term(PARSE_EVENTS__TERM_TYPE_CONFIG2); } | ||
105 | period { return term(PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); } | ||
106 | branch_type { return term(PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); } | ||
107 | |||
108 | mem: { return PE_PREFIX_MEM; } | ||
109 | r{num_raw_hex} { return raw(); } | ||
110 | {num_dec} { return value(10); } | ||
111 | {num_hex} { return value(16); } | ||
112 | |||
113 | {modifier_event} { return str(PE_MODIFIER_EVENT); } | ||
114 | {modifier_bp} { return str(PE_MODIFIER_BP); } | ||
115 | {name} { return str(PE_NAME); } | ||
116 | "/" { return '/'; } | ||
117 | - { return '-'; } | ||
118 | , { return ','; } | ||
119 | : { return ':'; } | ||
120 | = { return '='; } | ||
121 | |||
122 | %% | ||
123 | |||
124 | int parse_events_wrap(void) | ||
125 | { | ||
126 | return 1; | ||
127 | } | ||
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y new file mode 100644 index 000000000000..d9637da7333c --- /dev/null +++ b/tools/perf/util/parse-events.y | |||
@@ -0,0 +1,229 @@ | |||
1 | |||
2 | %name-prefix "parse_events_" | ||
3 | %parse-param {struct list_head *list_all} | ||
4 | %parse-param {struct list_head *list_event} | ||
5 | %parse-param {int *idx} | ||
6 | |||
7 | %{ | ||
8 | |||
9 | #define YYDEBUG 1 | ||
10 | |||
11 | #include <linux/compiler.h> | ||
12 | #include <linux/list.h> | ||
13 | #include "types.h" | ||
14 | #include "util.h" | ||
15 | #include "parse-events.h" | ||
16 | |||
17 | extern int parse_events_lex (void); | ||
18 | |||
19 | #define ABORT_ON(val) \ | ||
20 | do { \ | ||
21 | if (val) \ | ||
22 | YYABORT; \ | ||
23 | } while (0) | ||
24 | |||
25 | %} | ||
26 | |||
27 | %token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM | ||
28 | %token PE_NAME | ||
29 | %token PE_MODIFIER_EVENT PE_MODIFIER_BP | ||
30 | %token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT | ||
31 | %token PE_PREFIX_MEM PE_PREFIX_RAW | ||
32 | %token PE_ERROR | ||
33 | %type <num> PE_VALUE | ||
34 | %type <num> PE_VALUE_SYM | ||
35 | %type <num> PE_RAW | ||
36 | %type <num> PE_TERM | ||
37 | %type <str> PE_NAME | ||
38 | %type <str> PE_NAME_CACHE_TYPE | ||
39 | %type <str> PE_NAME_CACHE_OP_RESULT | ||
40 | %type <str> PE_MODIFIER_EVENT | ||
41 | %type <str> PE_MODIFIER_BP | ||
42 | %type <head> event_config | ||
43 | %type <term> event_term | ||
44 | |||
45 | %union | ||
46 | { | ||
47 | char *str; | ||
48 | unsigned long num; | ||
49 | struct list_head *head; | ||
50 | struct parse_events__term *term; | ||
51 | } | ||
52 | %% | ||
53 | |||
54 | events: | ||
55 | events ',' event | event | ||
56 | |||
57 | event: | ||
58 | event_def PE_MODIFIER_EVENT | ||
59 | { | ||
60 | /* | ||
61 | * Apply modifier on all events added by single event definition | ||
62 | * (there could be more events added for multiple tracepoint | ||
63 | * definitions via '*?'. | ||
64 | */ | ||
65 | ABORT_ON(parse_events_modifier(list_event, $2)); | ||
66 | parse_events_update_lists(list_event, list_all); | ||
67 | } | ||
68 | | | ||
69 | event_def | ||
70 | { | ||
71 | parse_events_update_lists(list_event, list_all); | ||
72 | } | ||
73 | |||
74 | event_def: event_pmu | | ||
75 | event_legacy_symbol | | ||
76 | event_legacy_cache sep_dc | | ||
77 | event_legacy_mem | | ||
78 | event_legacy_tracepoint sep_dc | | ||
79 | event_legacy_numeric sep_dc | | ||
80 | event_legacy_raw sep_dc | ||
81 | |||
82 | event_pmu: | ||
83 | PE_NAME '/' event_config '/' | ||
84 | { | ||
85 | ABORT_ON(parse_events_add_pmu(list_event, idx, $1, $3)); | ||
86 | parse_events__free_terms($3); | ||
87 | } | ||
88 | |||
89 | event_legacy_symbol: | ||
90 | PE_VALUE_SYM '/' event_config '/' | ||
91 | { | ||
92 | int type = $1 >> 16; | ||
93 | int config = $1 & 255; | ||
94 | |||
95 | ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, $3)); | ||
96 | parse_events__free_terms($3); | ||
97 | } | ||
98 | | | ||
99 | PE_VALUE_SYM sep_slash_dc | ||
100 | { | ||
101 | int type = $1 >> 16; | ||
102 | int config = $1 & 255; | ||
103 | |||
104 | ABORT_ON(parse_events_add_numeric(list_event, idx, type, config, NULL)); | ||
105 | } | ||
106 | |||
107 | event_legacy_cache: | ||
108 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT '-' PE_NAME_CACHE_OP_RESULT | ||
109 | { | ||
110 | ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, $5)); | ||
111 | } | ||
112 | | | ||
113 | PE_NAME_CACHE_TYPE '-' PE_NAME_CACHE_OP_RESULT | ||
114 | { | ||
115 | ABORT_ON(parse_events_add_cache(list_event, idx, $1, $3, NULL)); | ||
116 | } | ||
117 | | | ||
118 | PE_NAME_CACHE_TYPE | ||
119 | { | ||
120 | ABORT_ON(parse_events_add_cache(list_event, idx, $1, NULL, NULL)); | ||
121 | } | ||
122 | |||
123 | event_legacy_mem: | ||
124 | PE_PREFIX_MEM PE_VALUE ':' PE_MODIFIER_BP sep_dc | ||
125 | { | ||
126 | ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, $4)); | ||
127 | } | ||
128 | | | ||
129 | PE_PREFIX_MEM PE_VALUE sep_dc | ||
130 | { | ||
131 | ABORT_ON(parse_events_add_breakpoint(list_event, idx, (void *) $2, NULL)); | ||
132 | } | ||
133 | |||
134 | event_legacy_tracepoint: | ||
135 | PE_NAME ':' PE_NAME | ||
136 | { | ||
137 | ABORT_ON(parse_events_add_tracepoint(list_event, idx, $1, $3)); | ||
138 | } | ||
139 | |||
140 | event_legacy_numeric: | ||
141 | PE_VALUE ':' PE_VALUE | ||
142 | { | ||
143 | ABORT_ON(parse_events_add_numeric(list_event, idx, $1, $3, NULL)); | ||
144 | } | ||
145 | |||
146 | event_legacy_raw: | ||
147 | PE_RAW | ||
148 | { | ||
149 | ABORT_ON(parse_events_add_numeric(list_event, idx, PERF_TYPE_RAW, $1, NULL)); | ||
150 | } | ||
151 | |||
152 | event_config: | ||
153 | event_config ',' event_term | ||
154 | { | ||
155 | struct list_head *head = $1; | ||
156 | struct parse_events__term *term = $3; | ||
157 | |||
158 | ABORT_ON(!head); | ||
159 | list_add_tail(&term->list, head); | ||
160 | $$ = $1; | ||
161 | } | ||
162 | | | ||
163 | event_term | ||
164 | { | ||
165 | struct list_head *head = malloc(sizeof(*head)); | ||
166 | struct parse_events__term *term = $1; | ||
167 | |||
168 | ABORT_ON(!head); | ||
169 | INIT_LIST_HEAD(head); | ||
170 | list_add_tail(&term->list, head); | ||
171 | $$ = head; | ||
172 | } | ||
173 | |||
174 | event_term: | ||
175 | PE_NAME '=' PE_NAME | ||
176 | { | ||
177 | struct parse_events__term *term; | ||
178 | |||
179 | ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_STR, | ||
180 | $1, $3, 0)); | ||
181 | $$ = term; | ||
182 | } | ||
183 | | | ||
184 | PE_NAME '=' PE_VALUE | ||
185 | { | ||
186 | struct parse_events__term *term; | ||
187 | |||
188 | ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM, | ||
189 | $1, NULL, $3)); | ||
190 | $$ = term; | ||
191 | } | ||
192 | | | ||
193 | PE_NAME | ||
194 | { | ||
195 | struct parse_events__term *term; | ||
196 | |||
197 | ABORT_ON(parse_events__new_term(&term, PARSE_EVENTS__TERM_TYPE_NUM, | ||
198 | $1, NULL, 1)); | ||
199 | $$ = term; | ||
200 | } | ||
201 | | | ||
202 | PE_TERM '=' PE_VALUE | ||
203 | { | ||
204 | struct parse_events__term *term; | ||
205 | |||
206 | ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, $3)); | ||
207 | $$ = term; | ||
208 | } | ||
209 | | | ||
210 | PE_TERM | ||
211 | { | ||
212 | struct parse_events__term *term; | ||
213 | |||
214 | ABORT_ON(parse_events__new_term(&term, $1, NULL, NULL, 1)); | ||
215 | $$ = term; | ||
216 | } | ||
217 | |||
218 | sep_dc: ':' | | ||
219 | |||
220 | sep_slash_dc: '/' | ':' | | ||
221 | |||
222 | %% | ||
223 | |||
224 | void parse_events_error(struct list_head *list_all __used, | ||
225 | struct list_head *list_event __used, | ||
226 | int *idx __used, | ||
227 | char const *msg __used) | ||
228 | { | ||
229 | } | ||
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c new file mode 100644 index 000000000000..cb08a118e811 --- /dev/null +++ b/tools/perf/util/pmu.c | |||
@@ -0,0 +1,469 @@ | |||
1 | |||
2 | #include <linux/list.h> | ||
3 | #include <sys/types.h> | ||
4 | #include <sys/stat.h> | ||
5 | #include <unistd.h> | ||
6 | #include <stdio.h> | ||
7 | #include <dirent.h> | ||
8 | #include "sysfs.h" | ||
9 | #include "util.h" | ||
10 | #include "pmu.h" | ||
11 | #include "parse-events.h" | ||
12 | |||
13 | int perf_pmu_parse(struct list_head *list, char *name); | ||
14 | extern FILE *perf_pmu_in; | ||
15 | |||
16 | static LIST_HEAD(pmus); | ||
17 | |||
18 | /* | ||
19 | * Parse & process all the sysfs attributes located under | ||
20 | * the directory specified in 'dir' parameter. | ||
21 | */ | ||
22 | static int pmu_format_parse(char *dir, struct list_head *head) | ||
23 | { | ||
24 | struct dirent *evt_ent; | ||
25 | DIR *format_dir; | ||
26 | int ret = 0; | ||
27 | |||
28 | format_dir = opendir(dir); | ||
29 | if (!format_dir) | ||
30 | return -EINVAL; | ||
31 | |||
32 | while (!ret && (evt_ent = readdir(format_dir))) { | ||
33 | char path[PATH_MAX]; | ||
34 | char *name = evt_ent->d_name; | ||
35 | FILE *file; | ||
36 | |||
37 | if (!strcmp(name, ".") || !strcmp(name, "..")) | ||
38 | continue; | ||
39 | |||
40 | snprintf(path, PATH_MAX, "%s/%s", dir, name); | ||
41 | |||
42 | ret = -EINVAL; | ||
43 | file = fopen(path, "r"); | ||
44 | if (!file) | ||
45 | break; | ||
46 | |||
47 | perf_pmu_in = file; | ||
48 | ret = perf_pmu_parse(head, name); | ||
49 | fclose(file); | ||
50 | } | ||
51 | |||
52 | closedir(format_dir); | ||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Reading/parsing the default pmu format definition, which should be | ||
58 | * located at: | ||
59 | * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes. | ||
60 | */ | ||
61 | static int pmu_format(char *name, struct list_head *format) | ||
62 | { | ||
63 | struct stat st; | ||
64 | char path[PATH_MAX]; | ||
65 | const char *sysfs; | ||
66 | |||
67 | sysfs = sysfs_find_mountpoint(); | ||
68 | if (!sysfs) | ||
69 | return -1; | ||
70 | |||
71 | snprintf(path, PATH_MAX, | ||
72 | "%s/bus/event_source/devices/%s/format", sysfs, name); | ||
73 | |||
74 | if (stat(path, &st) < 0) | ||
75 | return -1; | ||
76 | |||
77 | if (pmu_format_parse(path, format)) | ||
78 | return -1; | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Reading/parsing the default pmu type value, which should be | ||
85 | * located at: | ||
86 | * /sys/bus/event_source/devices/<dev>/type as sysfs attribute. | ||
87 | */ | ||
88 | static int pmu_type(char *name, __u32 *type) | ||
89 | { | ||
90 | struct stat st; | ||
91 | char path[PATH_MAX]; | ||
92 | const char *sysfs; | ||
93 | FILE *file; | ||
94 | int ret = 0; | ||
95 | |||
96 | sysfs = sysfs_find_mountpoint(); | ||
97 | if (!sysfs) | ||
98 | return -1; | ||
99 | |||
100 | snprintf(path, PATH_MAX, | ||
101 | "%s/bus/event_source/devices/%s/type", sysfs, name); | ||
102 | |||
103 | if (stat(path, &st) < 0) | ||
104 | return -1; | ||
105 | |||
106 | file = fopen(path, "r"); | ||
107 | if (!file) | ||
108 | return -EINVAL; | ||
109 | |||
110 | if (1 != fscanf(file, "%u", type)) | ||
111 | ret = -1; | ||
112 | |||
113 | fclose(file); | ||
114 | return ret; | ||
115 | } | ||
116 | |||
117 | static struct perf_pmu *pmu_lookup(char *name) | ||
118 | { | ||
119 | struct perf_pmu *pmu; | ||
120 | LIST_HEAD(format); | ||
121 | __u32 type; | ||
122 | |||
123 | /* | ||
124 | * The pmu data we store & need consists of the pmu | ||
125 | * type value and format definitions. Load both right | ||
126 | * now. | ||
127 | */ | ||
128 | if (pmu_format(name, &format)) | ||
129 | return NULL; | ||
130 | |||
131 | if (pmu_type(name, &type)) | ||
132 | return NULL; | ||
133 | |||
134 | pmu = zalloc(sizeof(*pmu)); | ||
135 | if (!pmu) | ||
136 | return NULL; | ||
137 | |||
138 | INIT_LIST_HEAD(&pmu->format); | ||
139 | list_splice(&format, &pmu->format); | ||
140 | pmu->name = strdup(name); | ||
141 | pmu->type = type; | ||
142 | return pmu; | ||
143 | } | ||
144 | |||
145 | static struct perf_pmu *pmu_find(char *name) | ||
146 | { | ||
147 | struct perf_pmu *pmu; | ||
148 | |||
149 | list_for_each_entry(pmu, &pmus, list) | ||
150 | if (!strcmp(pmu->name, name)) | ||
151 | return pmu; | ||
152 | |||
153 | return NULL; | ||
154 | } | ||
155 | |||
156 | struct perf_pmu *perf_pmu__find(char *name) | ||
157 | { | ||
158 | struct perf_pmu *pmu; | ||
159 | |||
160 | /* | ||
161 | * Once PMU is loaded it stays in the list, | ||
162 | * so we keep us from multiple reading/parsing | ||
163 | * the pmu format definitions. | ||
164 | */ | ||
165 | pmu = pmu_find(name); | ||
166 | if (pmu) | ||
167 | return pmu; | ||
168 | |||
169 | return pmu_lookup(name); | ||
170 | } | ||
171 | |||
172 | static struct perf_pmu__format* | ||
173 | pmu_find_format(struct list_head *formats, char *name) | ||
174 | { | ||
175 | struct perf_pmu__format *format; | ||
176 | |||
177 | list_for_each_entry(format, formats, list) | ||
178 | if (!strcmp(format->name, name)) | ||
179 | return format; | ||
180 | |||
181 | return NULL; | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * Returns value based on the format definition (format parameter) | ||
186 | * and unformated value (value parameter). | ||
187 | * | ||
188 | * TODO maybe optimize a little ;) | ||
189 | */ | ||
190 | static __u64 pmu_format_value(unsigned long *format, __u64 value) | ||
191 | { | ||
192 | unsigned long fbit, vbit; | ||
193 | __u64 v = 0; | ||
194 | |||
195 | for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { | ||
196 | |||
197 | if (!test_bit(fbit, format)) | ||
198 | continue; | ||
199 | |||
200 | if (!(value & (1llu << vbit++))) | ||
201 | continue; | ||
202 | |||
203 | v |= (1llu << fbit); | ||
204 | } | ||
205 | |||
206 | return v; | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * Setup one of config[12] attr members based on the | ||
211 | * user input data - temr parameter. | ||
212 | */ | ||
213 | static int pmu_config_term(struct list_head *formats, | ||
214 | struct perf_event_attr *attr, | ||
215 | struct parse_events__term *term) | ||
216 | { | ||
217 | struct perf_pmu__format *format; | ||
218 | __u64 *vp; | ||
219 | |||
220 | /* | ||
221 | * Support only for hardcoded and numnerial terms. | ||
222 | * Hardcoded terms should be already in, so nothing | ||
223 | * to be done for them. | ||
224 | */ | ||
225 | if (parse_events__is_hardcoded_term(term)) | ||
226 | return 0; | ||
227 | |||
228 | if (term->type != PARSE_EVENTS__TERM_TYPE_NUM) | ||
229 | return -EINVAL; | ||
230 | |||
231 | format = pmu_find_format(formats, term->config); | ||
232 | if (!format) | ||
233 | return -EINVAL; | ||
234 | |||
235 | switch (format->value) { | ||
236 | case PERF_PMU_FORMAT_VALUE_CONFIG: | ||
237 | vp = &attr->config; | ||
238 | break; | ||
239 | case PERF_PMU_FORMAT_VALUE_CONFIG1: | ||
240 | vp = &attr->config1; | ||
241 | break; | ||
242 | case PERF_PMU_FORMAT_VALUE_CONFIG2: | ||
243 | vp = &attr->config2; | ||
244 | break; | ||
245 | default: | ||
246 | return -EINVAL; | ||
247 | } | ||
248 | |||
249 | *vp |= pmu_format_value(format->bits, term->val.num); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int pmu_config(struct list_head *formats, struct perf_event_attr *attr, | ||
254 | struct list_head *head_terms) | ||
255 | { | ||
256 | struct parse_events__term *term, *h; | ||
257 | |||
258 | list_for_each_entry_safe(term, h, head_terms, list) | ||
259 | if (pmu_config_term(formats, attr, term)) | ||
260 | return -EINVAL; | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * Configures event's 'attr' parameter based on the: | ||
267 | * 1) users input - specified in terms parameter | ||
268 | * 2) pmu format definitions - specified by pmu parameter | ||
269 | */ | ||
270 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | ||
271 | struct list_head *head_terms) | ||
272 | { | ||
273 | attr->type = pmu->type; | ||
274 | return pmu_config(&pmu->format, attr, head_terms); | ||
275 | } | ||
276 | |||
277 | int perf_pmu__new_format(struct list_head *list, char *name, | ||
278 | int config, unsigned long *bits) | ||
279 | { | ||
280 | struct perf_pmu__format *format; | ||
281 | |||
282 | format = zalloc(sizeof(*format)); | ||
283 | if (!format) | ||
284 | return -ENOMEM; | ||
285 | |||
286 | format->name = strdup(name); | ||
287 | format->value = config; | ||
288 | memcpy(format->bits, bits, sizeof(format->bits)); | ||
289 | |||
290 | list_add_tail(&format->list, list); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | void perf_pmu__set_format(unsigned long *bits, long from, long to) | ||
295 | { | ||
296 | long b; | ||
297 | |||
298 | if (!to) | ||
299 | to = from; | ||
300 | |||
301 | memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS)); | ||
302 | for (b = from; b <= to; b++) | ||
303 | set_bit(b, bits); | ||
304 | } | ||
305 | |||
306 | /* Simulated format definitions. */ | ||
307 | static struct test_format { | ||
308 | const char *name; | ||
309 | const char *value; | ||
310 | } test_formats[] = { | ||
311 | { "krava01", "config:0-1,62-63\n", }, | ||
312 | { "krava02", "config:10-17\n", }, | ||
313 | { "krava03", "config:5\n", }, | ||
314 | { "krava11", "config1:0,2,4,6,8,20-28\n", }, | ||
315 | { "krava12", "config1:63\n", }, | ||
316 | { "krava13", "config1:45-47\n", }, | ||
317 | { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", }, | ||
318 | { "krava22", "config2:8,18,48,58\n", }, | ||
319 | { "krava23", "config2:28-29,38\n", }, | ||
320 | }; | ||
321 | |||
322 | #define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format)) | ||
323 | |||
324 | /* Simulated users input. */ | ||
325 | static struct parse_events__term test_terms[] = { | ||
326 | { | ||
327 | .config = (char *) "krava01", | ||
328 | .val.num = 15, | ||
329 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
330 | }, | ||
331 | { | ||
332 | .config = (char *) "krava02", | ||
333 | .val.num = 170, | ||
334 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
335 | }, | ||
336 | { | ||
337 | .config = (char *) "krava03", | ||
338 | .val.num = 1, | ||
339 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
340 | }, | ||
341 | { | ||
342 | .config = (char *) "krava11", | ||
343 | .val.num = 27, | ||
344 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
345 | }, | ||
346 | { | ||
347 | .config = (char *) "krava12", | ||
348 | .val.num = 1, | ||
349 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
350 | }, | ||
351 | { | ||
352 | .config = (char *) "krava13", | ||
353 | .val.num = 2, | ||
354 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
355 | }, | ||
356 | { | ||
357 | .config = (char *) "krava21", | ||
358 | .val.num = 119, | ||
359 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
360 | }, | ||
361 | { | ||
362 | .config = (char *) "krava22", | ||
363 | .val.num = 11, | ||
364 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
365 | }, | ||
366 | { | ||
367 | .config = (char *) "krava23", | ||
368 | .val.num = 2, | ||
369 | .type = PARSE_EVENTS__TERM_TYPE_NUM, | ||
370 | }, | ||
371 | }; | ||
372 | #define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term)) | ||
373 | |||
374 | /* | ||
375 | * Prepare format directory data, exported by kernel | ||
376 | * at /sys/bus/event_source/devices/<dev>/format. | ||
377 | */ | ||
378 | static char *test_format_dir_get(void) | ||
379 | { | ||
380 | static char dir[PATH_MAX]; | ||
381 | unsigned int i; | ||
382 | |||
383 | snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX"); | ||
384 | if (!mkdtemp(dir)) | ||
385 | return NULL; | ||
386 | |||
387 | for (i = 0; i < TEST_FORMATS_CNT; i++) { | ||
388 | static char name[PATH_MAX]; | ||
389 | struct test_format *format = &test_formats[i]; | ||
390 | FILE *file; | ||
391 | |||
392 | snprintf(name, PATH_MAX, "%s/%s", dir, format->name); | ||
393 | |||
394 | file = fopen(name, "w"); | ||
395 | if (!file) | ||
396 | return NULL; | ||
397 | |||
398 | if (1 != fwrite(format->value, strlen(format->value), 1, file)) | ||
399 | break; | ||
400 | |||
401 | fclose(file); | ||
402 | } | ||
403 | |||
404 | return dir; | ||
405 | } | ||
406 | |||
407 | /* Cleanup format directory. */ | ||
408 | static int test_format_dir_put(char *dir) | ||
409 | { | ||
410 | char buf[PATH_MAX]; | ||
411 | snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir); | ||
412 | if (system(buf)) | ||
413 | return -1; | ||
414 | |||
415 | snprintf(buf, PATH_MAX, "rmdir %s\n", dir); | ||
416 | return system(buf); | ||
417 | } | ||
418 | |||
419 | static struct list_head *test_terms_list(void) | ||
420 | { | ||
421 | static LIST_HEAD(terms); | ||
422 | unsigned int i; | ||
423 | |||
424 | for (i = 0; i < TERMS_CNT; i++) | ||
425 | list_add_tail(&test_terms[i].list, &terms); | ||
426 | |||
427 | return &terms; | ||
428 | } | ||
429 | |||
430 | #undef TERMS_CNT | ||
431 | |||
432 | int perf_pmu__test(void) | ||
433 | { | ||
434 | char *format = test_format_dir_get(); | ||
435 | LIST_HEAD(formats); | ||
436 | struct list_head *terms = test_terms_list(); | ||
437 | int ret; | ||
438 | |||
439 | if (!format) | ||
440 | return -EINVAL; | ||
441 | |||
442 | do { | ||
443 | struct perf_event_attr attr; | ||
444 | |||
445 | memset(&attr, 0, sizeof(attr)); | ||
446 | |||
447 | ret = pmu_format_parse(format, &formats); | ||
448 | if (ret) | ||
449 | break; | ||
450 | |||
451 | ret = pmu_config(&formats, &attr, terms); | ||
452 | if (ret) | ||
453 | break; | ||
454 | |||
455 | ret = -EINVAL; | ||
456 | |||
457 | if (attr.config != 0xc00000000002a823) | ||
458 | break; | ||
459 | if (attr.config1 != 0x8000400000000145) | ||
460 | break; | ||
461 | if (attr.config2 != 0x0400000020041d07) | ||
462 | break; | ||
463 | |||
464 | ret = 0; | ||
465 | } while (0); | ||
466 | |||
467 | test_format_dir_put(format); | ||
468 | return ret; | ||
469 | } | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h new file mode 100644 index 000000000000..68c0db965e1f --- /dev/null +++ b/tools/perf/util/pmu.h | |||
@@ -0,0 +1,41 @@ | |||
1 | #ifndef __PMU_H | ||
2 | #define __PMU_H | ||
3 | |||
4 | #include <linux/bitops.h> | ||
5 | #include "../../../include/linux/perf_event.h" | ||
6 | |||
7 | enum { | ||
8 | PERF_PMU_FORMAT_VALUE_CONFIG, | ||
9 | PERF_PMU_FORMAT_VALUE_CONFIG1, | ||
10 | PERF_PMU_FORMAT_VALUE_CONFIG2, | ||
11 | }; | ||
12 | |||
13 | #define PERF_PMU_FORMAT_BITS 64 | ||
14 | |||
15 | struct perf_pmu__format { | ||
16 | char *name; | ||
17 | int value; | ||
18 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | ||
19 | struct list_head list; | ||
20 | }; | ||
21 | |||
22 | struct perf_pmu { | ||
23 | char *name; | ||
24 | __u32 type; | ||
25 | struct list_head format; | ||
26 | struct list_head list; | ||
27 | }; | ||
28 | |||
29 | struct perf_pmu *perf_pmu__find(char *name); | ||
30 | int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, | ||
31 | struct list_head *head_terms); | ||
32 | |||
33 | int perf_pmu_wrap(void); | ||
34 | void perf_pmu_error(struct list_head *list, char *name, char const *msg); | ||
35 | |||
36 | int perf_pmu__new_format(struct list_head *list, char *name, | ||
37 | int config, unsigned long *bits); | ||
38 | void perf_pmu__set_format(unsigned long *bits, long from, long to); | ||
39 | |||
40 | int perf_pmu__test(void); | ||
41 | #endif /* __PMU_H */ | ||
diff --git a/tools/perf/util/pmu.l b/tools/perf/util/pmu.l new file mode 100644 index 000000000000..a15d9fbd7c0e --- /dev/null +++ b/tools/perf/util/pmu.l | |||
@@ -0,0 +1,43 @@ | |||
1 | %option prefix="perf_pmu_" | ||
2 | |||
3 | %{ | ||
4 | #include <stdlib.h> | ||
5 | #include <linux/bitops.h> | ||
6 | #include "pmu.h" | ||
7 | #include "pmu-bison.h" | ||
8 | |||
9 | static int value(int base) | ||
10 | { | ||
11 | long num; | ||
12 | |||
13 | errno = 0; | ||
14 | num = strtoul(perf_pmu_text, NULL, base); | ||
15 | if (errno) | ||
16 | return PP_ERROR; | ||
17 | |||
18 | perf_pmu_lval.num = num; | ||
19 | return PP_VALUE; | ||
20 | } | ||
21 | |||
22 | %} | ||
23 | |||
24 | num_dec [0-9]+ | ||
25 | |||
26 | %% | ||
27 | |||
28 | {num_dec} { return value(10); } | ||
29 | config { return PP_CONFIG; } | ||
30 | config1 { return PP_CONFIG1; } | ||
31 | config2 { return PP_CONFIG2; } | ||
32 | - { return '-'; } | ||
33 | : { return ':'; } | ||
34 | , { return ','; } | ||
35 | . { ; } | ||
36 | \n { ; } | ||
37 | |||
38 | %% | ||
39 | |||
40 | int perf_pmu_wrap(void) | ||
41 | { | ||
42 | return 1; | ||
43 | } | ||
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y new file mode 100644 index 000000000000..20ea77e93169 --- /dev/null +++ b/tools/perf/util/pmu.y | |||
@@ -0,0 +1,93 @@ | |||
1 | |||
2 | %name-prefix "perf_pmu_" | ||
3 | %parse-param {struct list_head *format} | ||
4 | %parse-param {char *name} | ||
5 | |||
6 | %{ | ||
7 | |||
8 | #include <linux/compiler.h> | ||
9 | #include <linux/list.h> | ||
10 | #include <linux/bitmap.h> | ||
11 | #include <string.h> | ||
12 | #include "pmu.h" | ||
13 | |||
14 | extern int perf_pmu_lex (void); | ||
15 | |||
16 | #define ABORT_ON(val) \ | ||
17 | do { \ | ||
18 | if (val) \ | ||
19 | YYABORT; \ | ||
20 | } while (0) | ||
21 | |||
22 | %} | ||
23 | |||
24 | %token PP_CONFIG PP_CONFIG1 PP_CONFIG2 | ||
25 | %token PP_VALUE PP_ERROR | ||
26 | %type <num> PP_VALUE | ||
27 | %type <bits> bit_term | ||
28 | %type <bits> bits | ||
29 | |||
30 | %union | ||
31 | { | ||
32 | unsigned long num; | ||
33 | DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); | ||
34 | } | ||
35 | |||
36 | %% | ||
37 | |||
38 | format: | ||
39 | format format_term | ||
40 | | | ||
41 | format_term | ||
42 | |||
43 | format_term: | ||
44 | PP_CONFIG ':' bits | ||
45 | { | ||
46 | ABORT_ON(perf_pmu__new_format(format, name, | ||
47 | PERF_PMU_FORMAT_VALUE_CONFIG, | ||
48 | $3)); | ||
49 | } | ||
50 | | | ||
51 | PP_CONFIG1 ':' bits | ||
52 | { | ||
53 | ABORT_ON(perf_pmu__new_format(format, name, | ||
54 | PERF_PMU_FORMAT_VALUE_CONFIG1, | ||
55 | $3)); | ||
56 | } | ||
57 | | | ||
58 | PP_CONFIG2 ':' bits | ||
59 | { | ||
60 | ABORT_ON(perf_pmu__new_format(format, name, | ||
61 | PERF_PMU_FORMAT_VALUE_CONFIG2, | ||
62 | $3)); | ||
63 | } | ||
64 | |||
65 | bits: | ||
66 | bits ',' bit_term | ||
67 | { | ||
68 | bitmap_or($$, $1, $3, 64); | ||
69 | } | ||
70 | | | ||
71 | bit_term | ||
72 | { | ||
73 | memcpy($$, $1, sizeof($1)); | ||
74 | } | ||
75 | |||
76 | bit_term: | ||
77 | PP_VALUE '-' PP_VALUE | ||
78 | { | ||
79 | perf_pmu__set_format($$, $1, $3); | ||
80 | } | ||
81 | | | ||
82 | PP_VALUE | ||
83 | { | ||
84 | perf_pmu__set_format($$, $1, 0); | ||
85 | } | ||
86 | |||
87 | %% | ||
88 | |||
89 | void perf_pmu_error(struct list_head *list __used, | ||
90 | char *name __used, | ||
91 | char const *msg __used) | ||
92 | { | ||
93 | } | ||
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index eb25900e2211..8a8ee64e72d1 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -19,7 +19,6 @@ | |||
19 | * | 19 | * |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #define _GNU_SOURCE | ||
23 | #include <sys/utsname.h> | 22 | #include <sys/utsname.h> |
24 | #include <sys/types.h> | 23 | #include <sys/types.h> |
25 | #include <sys/stat.h> | 24 | #include <sys/stat.h> |
@@ -33,10 +32,8 @@ | |||
33 | #include <limits.h> | 32 | #include <limits.h> |
34 | #include <elf.h> | 33 | #include <elf.h> |
35 | 34 | ||
36 | #undef _GNU_SOURCE | ||
37 | #include "util.h" | 35 | #include "util.h" |
38 | #include "event.h" | 36 | #include "event.h" |
39 | #include "string.h" | ||
40 | #include "strlist.h" | 37 | #include "strlist.h" |
41 | #include "debug.h" | 38 | #include "debug.h" |
42 | #include "cache.h" | 39 | #include "cache.h" |
@@ -275,10 +272,10 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, | |||
275 | /* Try to find perf_probe_event with debuginfo */ | 272 | /* Try to find perf_probe_event with debuginfo */ |
276 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | 273 | static int try_to_find_probe_trace_events(struct perf_probe_event *pev, |
277 | struct probe_trace_event **tevs, | 274 | struct probe_trace_event **tevs, |
278 | int max_tevs, const char *module) | 275 | int max_tevs, const char *target) |
279 | { | 276 | { |
280 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | 277 | bool need_dwarf = perf_probe_event_need_dwarf(pev); |
281 | struct debuginfo *dinfo = open_debuginfo(module); | 278 | struct debuginfo *dinfo = open_debuginfo(target); |
282 | int ntevs, ret = 0; | 279 | int ntevs, ret = 0; |
283 | 280 | ||
284 | if (!dinfo) { | 281 | if (!dinfo) { |
@@ -297,9 +294,9 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, | |||
297 | 294 | ||
298 | if (ntevs > 0) { /* Succeeded to find trace events */ | 295 | if (ntevs > 0) { /* Succeeded to find trace events */ |
299 | pr_debug("find %d probe_trace_events.\n", ntevs); | 296 | pr_debug("find %d probe_trace_events.\n", ntevs); |
300 | if (module) | 297 | if (target) |
301 | ret = add_module_to_probe_trace_events(*tevs, ntevs, | 298 | ret = add_module_to_probe_trace_events(*tevs, ntevs, |
302 | module); | 299 | target); |
303 | return ret < 0 ? ret : ntevs; | 300 | return ret < 0 ? ret : ntevs; |
304 | } | 301 | } |
305 | 302 | ||
@@ -1731,7 +1728,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
1731 | } | 1728 | } |
1732 | 1729 | ||
1733 | ret = 0; | 1730 | ret = 0; |
1734 | printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); | 1731 | printf("Added new event%s\n", (ntevs > 1) ? "s:" : ":"); |
1735 | for (i = 0; i < ntevs; i++) { | 1732 | for (i = 0; i < ntevs; i++) { |
1736 | tev = &tevs[i]; | 1733 | tev = &tevs[i]; |
1737 | if (pev->event) | 1734 | if (pev->event) |
@@ -1786,7 +1783,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
1786 | 1783 | ||
1787 | if (ret >= 0) { | 1784 | if (ret >= 0) { |
1788 | /* Show how to use the event. */ | 1785 | /* Show how to use the event. */ |
1789 | printf("\nYou can now use it on all perf tools, such as:\n\n"); | 1786 | printf("\nYou can now use it in all perf tools, such as:\n\n"); |
1790 | printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, | 1787 | printf("\tperf record -e %s:%s -aR sleep 1\n\n", tev->group, |
1791 | tev->event); | 1788 | tev->event); |
1792 | } | 1789 | } |
@@ -1798,14 +1795,14 @@ static int __add_probe_trace_events(struct perf_probe_event *pev, | |||
1798 | 1795 | ||
1799 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, | 1796 | static int convert_to_probe_trace_events(struct perf_probe_event *pev, |
1800 | struct probe_trace_event **tevs, | 1797 | struct probe_trace_event **tevs, |
1801 | int max_tevs, const char *module) | 1798 | int max_tevs, const char *target) |
1802 | { | 1799 | { |
1803 | struct symbol *sym; | 1800 | struct symbol *sym; |
1804 | int ret = 0, i; | 1801 | int ret = 0, i; |
1805 | struct probe_trace_event *tev; | 1802 | struct probe_trace_event *tev; |
1806 | 1803 | ||
1807 | /* Convert perf_probe_event with debuginfo */ | 1804 | /* Convert perf_probe_event with debuginfo */ |
1808 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, module); | 1805 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); |
1809 | if (ret != 0) | 1806 | if (ret != 0) |
1810 | return ret; /* Found in debuginfo or got an error */ | 1807 | return ret; /* Found in debuginfo or got an error */ |
1811 | 1808 | ||
@@ -1821,8 +1818,8 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
1821 | goto error; | 1818 | goto error; |
1822 | } | 1819 | } |
1823 | 1820 | ||
1824 | if (module) { | 1821 | if (target) { |
1825 | tev->point.module = strdup(module); | 1822 | tev->point.module = strdup(target); |
1826 | if (tev->point.module == NULL) { | 1823 | if (tev->point.module == NULL) { |
1827 | ret = -ENOMEM; | 1824 | ret = -ENOMEM; |
1828 | goto error; | 1825 | goto error; |
@@ -1869,6 +1866,12 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, | |||
1869 | tev->point.symbol); | 1866 | tev->point.symbol); |
1870 | ret = -ENOENT; | 1867 | ret = -ENOENT; |
1871 | goto error; | 1868 | goto error; |
1869 | } else if (tev->point.offset > sym->end - sym->start) { | ||
1870 | pr_warning("Offset specified is greater than size of %s\n", | ||
1871 | tev->point.symbol); | ||
1872 | ret = -ENOENT; | ||
1873 | goto error; | ||
1874 | |||
1872 | } | 1875 | } |
1873 | 1876 | ||
1874 | return 1; | 1877 | return 1; |
@@ -1886,7 +1889,7 @@ struct __event_package { | |||
1886 | }; | 1889 | }; |
1887 | 1890 | ||
1888 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | 1891 | int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, |
1889 | int max_tevs, const char *module, bool force_add) | 1892 | int max_tevs, const char *target, bool force_add) |
1890 | { | 1893 | { |
1891 | int i, j, ret; | 1894 | int i, j, ret; |
1892 | struct __event_package *pkgs; | 1895 | struct __event_package *pkgs; |
@@ -1909,7 +1912,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1909 | ret = convert_to_probe_trace_events(pkgs[i].pev, | 1912 | ret = convert_to_probe_trace_events(pkgs[i].pev, |
1910 | &pkgs[i].tevs, | 1913 | &pkgs[i].tevs, |
1911 | max_tevs, | 1914 | max_tevs, |
1912 | module); | 1915 | target); |
1913 | if (ret < 0) | 1916 | if (ret < 0) |
1914 | goto end; | 1917 | goto end; |
1915 | pkgs[i].ntevs = ret; | 1918 | pkgs[i].ntevs = ret; |
@@ -1961,7 +1964,7 @@ static int __del_trace_probe_event(int fd, struct str_node *ent) | |||
1961 | goto error; | 1964 | goto error; |
1962 | } | 1965 | } |
1963 | 1966 | ||
1964 | printf("Remove event: %s\n", ent->s); | 1967 | printf("Removed event: %s\n", ent->s); |
1965 | return 0; | 1968 | return 0; |
1966 | error: | 1969 | error: |
1967 | pr_warning("Failed to delete event: %s\n", strerror(-ret)); | 1970 | pr_warning("Failed to delete event: %s\n", strerror(-ret)); |
@@ -2065,7 +2068,7 @@ static int filter_available_functions(struct map *map __unused, | |||
2065 | return 1; | 2068 | return 1; |
2066 | } | 2069 | } |
2067 | 2070 | ||
2068 | int show_available_funcs(const char *module, struct strfilter *_filter) | 2071 | int show_available_funcs(const char *target, struct strfilter *_filter) |
2069 | { | 2072 | { |
2070 | struct map *map; | 2073 | struct map *map; |
2071 | int ret; | 2074 | int ret; |
@@ -2076,9 +2079,9 @@ int show_available_funcs(const char *module, struct strfilter *_filter) | |||
2076 | if (ret < 0) | 2079 | if (ret < 0) |
2077 | return ret; | 2080 | return ret; |
2078 | 2081 | ||
2079 | map = kernel_get_module_map(module); | 2082 | map = kernel_get_module_map(target); |
2080 | if (!map) { | 2083 | if (!map) { |
2081 | pr_err("Failed to find %s map.\n", (module) ? : "kernel"); | 2084 | pr_err("Failed to find %s map.\n", (target) ? : "kernel"); |
2082 | return -EINVAL; | 2085 | return -EINVAL; |
2083 | } | 2086 | } |
2084 | available_func_filter = _filter; | 2087 | available_func_filter = _filter; |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 5d732621a462..d448984ed789 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -30,7 +30,6 @@ | |||
30 | #include <stdlib.h> | 30 | #include <stdlib.h> |
31 | #include <string.h> | 31 | #include <string.h> |
32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
33 | #include <ctype.h> | ||
34 | #include <dwarf-regs.h> | 33 | #include <dwarf-regs.h> |
35 | 34 | ||
36 | #include <linux/bitops.h> | 35 | #include <linux/bitops.h> |
@@ -672,7 +671,7 @@ static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf) | |||
672 | static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, | 671 | static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, |
673 | bool retprobe, struct probe_trace_point *tp) | 672 | bool retprobe, struct probe_trace_point *tp) |
674 | { | 673 | { |
675 | Dwarf_Addr eaddr; | 674 | Dwarf_Addr eaddr, highaddr; |
676 | const char *name; | 675 | const char *name; |
677 | 676 | ||
678 | /* Copy the name of probe point */ | 677 | /* Copy the name of probe point */ |
@@ -683,6 +682,16 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwarf_Addr paddr, | |||
683 | dwarf_diename(sp_die)); | 682 | dwarf_diename(sp_die)); |
684 | return -ENOENT; | 683 | return -ENOENT; |
685 | } | 684 | } |
685 | if (dwarf_highpc(sp_die, &highaddr) != 0) { | ||
686 | pr_warning("Failed to get end address of %s\n", | ||
687 | dwarf_diename(sp_die)); | ||
688 | return -ENOENT; | ||
689 | } | ||
690 | if (paddr > highaddr) { | ||
691 | pr_warning("Offset specified is greater than size of %s\n", | ||
692 | dwarf_diename(sp_die)); | ||
693 | return -EINVAL; | ||
694 | } | ||
686 | tp->symbol = strdup(name); | 695 | tp->symbol = strdup(name); |
687 | if (tp->symbol == NULL) | 696 | if (tp->symbol == NULL) |
688 | return -ENOMEM; | 697 | return -ENOMEM; |
@@ -963,10 +972,12 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
963 | struct dwarf_callback_param *param = data; | 972 | struct dwarf_callback_param *param = data; |
964 | struct probe_finder *pf = param->data; | 973 | struct probe_finder *pf = param->data; |
965 | struct perf_probe_point *pp = &pf->pev->point; | 974 | struct perf_probe_point *pp = &pf->pev->point; |
975 | Dwarf_Attribute attr; | ||
966 | 976 | ||
967 | /* Check tag and diename */ | 977 | /* Check tag and diename */ |
968 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | 978 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
969 | !die_compare_name(sp_die, pp->function)) | 979 | !die_compare_name(sp_die, pp->function) || |
980 | dwarf_attr(sp_die, DW_AT_declaration, &attr)) | ||
970 | return DWARF_CB_OK; | 981 | return DWARF_CB_OK; |
971 | 982 | ||
972 | /* Check declared file */ | 983 | /* Check declared file */ |
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources new file mode 100644 index 000000000000..2884e67ee625 --- /dev/null +++ b/tools/perf/util/python-ext-sources | |||
@@ -0,0 +1,19 @@ | |||
1 | # | ||
2 | # List of files needed by perf python extention | ||
3 | # | ||
4 | # Each source file must be placed on its own line so that it can be | ||
5 | # processed by Makefile and util/setup.py accordingly. | ||
6 | # | ||
7 | |||
8 | util/python.c | ||
9 | util/ctype.c | ||
10 | util/evlist.c | ||
11 | util/evsel.c | ||
12 | util/cpumap.c | ||
13 | util/thread_map.c | ||
14 | util/util.c | ||
15 | util/xyarray.c | ||
16 | util/cgroup.c | ||
17 | util/debugfs.c | ||
18 | util/strlist.c | ||
19 | ../../lib/rbtree.c | ||
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 9dd47a4f2596..e03b58a48424 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c | |||
@@ -425,14 +425,14 @@ struct pyrf_thread_map { | |||
425 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, | 425 | static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads, |
426 | PyObject *args, PyObject *kwargs) | 426 | PyObject *args, PyObject *kwargs) |
427 | { | 427 | { |
428 | static char *kwlist[] = { "pid", "tid", NULL }; | 428 | static char *kwlist[] = { "pid", "tid", "uid", NULL }; |
429 | int pid = -1, tid = -1; | 429 | int pid = -1, tid = -1, uid = UINT_MAX; |
430 | 430 | ||
431 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii", | 431 | if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iii", |
432 | kwlist, &pid, &tid)) | 432 | kwlist, &pid, &tid, &uid)) |
433 | return -1; | 433 | return -1; |
434 | 434 | ||
435 | pthreads->threads = thread_map__new(pid, tid); | 435 | pthreads->threads = thread_map__new(pid, tid, uid); |
436 | if (pthreads->threads == NULL) | 436 | if (pthreads->threads == NULL) |
437 | return -1; | 437 | return -1; |
438 | return 0; | 438 | return 0; |
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 0b2a48783172..c2623c6f9b51 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c | |||
@@ -24,7 +24,6 @@ | |||
24 | #include <stdio.h> | 24 | #include <stdio.h> |
25 | #include <stdlib.h> | 25 | #include <stdlib.h> |
26 | #include <string.h> | 26 | #include <string.h> |
27 | #include <ctype.h> | ||
28 | #include <errno.h> | 27 | #include <errno.h> |
29 | 28 | ||
30 | #include "../../perf.h" | 29 | #include "../../perf.h" |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b5ca2558c7bb..9412e3b05f68 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -24,7 +24,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
24 | self->fd = STDIN_FILENO; | 24 | self->fd = STDIN_FILENO; |
25 | 25 | ||
26 | if (perf_session__read_header(self, self->fd) < 0) | 26 | if (perf_session__read_header(self, self->fd) < 0) |
27 | pr_err("incompatible file format"); | 27 | pr_err("incompatible file format (rerun with -v to learn more)"); |
28 | 28 | ||
29 | return 0; | 29 | return 0; |
30 | } | 30 | } |
@@ -56,7 +56,7 @@ static int perf_session__open(struct perf_session *self, bool force) | |||
56 | } | 56 | } |
57 | 57 | ||
58 | if (perf_session__read_header(self, self->fd) < 0) { | 58 | if (perf_session__read_header(self, self->fd) < 0) { |
59 | pr_err("incompatible file format"); | 59 | pr_err("incompatible file format (rerun with -v to learn more)"); |
60 | goto out_close; | 60 | goto out_close; |
61 | } | 61 | } |
62 | 62 | ||
@@ -140,6 +140,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
140 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); | 140 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); |
141 | INIT_LIST_HEAD(&self->ordered_samples.to_free); | 141 | INIT_LIST_HEAD(&self->ordered_samples.to_free); |
142 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); | 142 | machine__init(&self->host_machine, "", HOST_KERNEL_ID); |
143 | hists__init(&self->hists); | ||
143 | 144 | ||
144 | if (mode == O_RDONLY) { | 145 | if (mode == O_RDONLY) { |
145 | if (perf_session__open(self, force) < 0) | 146 | if (perf_session__open(self, force) < 0) |
@@ -229,6 +230,64 @@ static bool symbol__match_parent_regex(struct symbol *sym) | |||
229 | return 0; | 230 | return 0; |
230 | } | 231 | } |
231 | 232 | ||
233 | static const u8 cpumodes[] = { | ||
234 | PERF_RECORD_MISC_USER, | ||
235 | PERF_RECORD_MISC_KERNEL, | ||
236 | PERF_RECORD_MISC_GUEST_USER, | ||
237 | PERF_RECORD_MISC_GUEST_KERNEL | ||
238 | }; | ||
239 | #define NCPUMODES (sizeof(cpumodes)/sizeof(u8)) | ||
240 | |||
241 | static void ip__resolve_ams(struct machine *self, struct thread *thread, | ||
242 | struct addr_map_symbol *ams, | ||
243 | u64 ip) | ||
244 | { | ||
245 | struct addr_location al; | ||
246 | size_t i; | ||
247 | u8 m; | ||
248 | |||
249 | memset(&al, 0, sizeof(al)); | ||
250 | |||
251 | for (i = 0; i < NCPUMODES; i++) { | ||
252 | m = cpumodes[i]; | ||
253 | /* | ||
254 | * We cannot use the header.misc hint to determine whether a | ||
255 | * branch stack address is user, kernel, guest, hypervisor. | ||
256 | * Branches may straddle the kernel/user/hypervisor boundaries. | ||
257 | * Thus, we have to try consecutively until we find a match | ||
258 | * or else, the symbol is unknown | ||
259 | */ | ||
260 | thread__find_addr_location(thread, self, m, MAP__FUNCTION, | ||
261 | ip, &al, NULL); | ||
262 | if (al.sym) | ||
263 | goto found; | ||
264 | } | ||
265 | found: | ||
266 | ams->addr = ip; | ||
267 | ams->al_addr = al.addr; | ||
268 | ams->sym = al.sym; | ||
269 | ams->map = al.map; | ||
270 | } | ||
271 | |||
272 | struct branch_info *machine__resolve_bstack(struct machine *self, | ||
273 | struct thread *thr, | ||
274 | struct branch_stack *bs) | ||
275 | { | ||
276 | struct branch_info *bi; | ||
277 | unsigned int i; | ||
278 | |||
279 | bi = calloc(bs->nr, sizeof(struct branch_info)); | ||
280 | if (!bi) | ||
281 | return NULL; | ||
282 | |||
283 | for (i = 0; i < bs->nr; i++) { | ||
284 | ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to); | ||
285 | ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from); | ||
286 | bi[i].flags = bs->entries[i].flags; | ||
287 | } | ||
288 | return bi; | ||
289 | } | ||
290 | |||
232 | int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel, | 291 | int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel, |
233 | struct thread *thread, | 292 | struct thread *thread, |
234 | struct ip_callchain *chain, | 293 | struct ip_callchain *chain, |
@@ -697,6 +756,18 @@ static void callchain__printf(struct perf_sample *sample) | |||
697 | i, sample->callchain->ips[i]); | 756 | i, sample->callchain->ips[i]); |
698 | } | 757 | } |
699 | 758 | ||
759 | static void branch_stack__printf(struct perf_sample *sample) | ||
760 | { | ||
761 | uint64_t i; | ||
762 | |||
763 | printf("... branch stack: nr:%" PRIu64 "\n", sample->branch_stack->nr); | ||
764 | |||
765 | for (i = 0; i < sample->branch_stack->nr; i++) | ||
766 | printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 "\n", | ||
767 | i, sample->branch_stack->entries[i].from, | ||
768 | sample->branch_stack->entries[i].to); | ||
769 | } | ||
770 | |||
700 | static void perf_session__print_tstamp(struct perf_session *session, | 771 | static void perf_session__print_tstamp(struct perf_session *session, |
701 | union perf_event *event, | 772 | union perf_event *event, |
702 | struct perf_sample *sample) | 773 | struct perf_sample *sample) |
@@ -744,6 +815,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event, | |||
744 | 815 | ||
745 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) | 816 | if (session->sample_type & PERF_SAMPLE_CALLCHAIN) |
746 | callchain__printf(sample); | 817 | callchain__printf(sample); |
818 | |||
819 | if (session->sample_type & PERF_SAMPLE_BRANCH_STACK) | ||
820 | branch_stack__printf(sample); | ||
747 | } | 821 | } |
748 | 822 | ||
749 | static struct machine * | 823 | static struct machine * |
@@ -796,6 +870,10 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
796 | ++session->hists.stats.nr_unknown_id; | 870 | ++session->hists.stats.nr_unknown_id; |
797 | return -1; | 871 | return -1; |
798 | } | 872 | } |
873 | if (machine == NULL) { | ||
874 | ++session->hists.stats.nr_unprocessable_samples; | ||
875 | return -1; | ||
876 | } | ||
799 | return tool->sample(tool, event, sample, evsel, machine); | 877 | return tool->sample(tool, event, sample, evsel, machine); |
800 | case PERF_RECORD_MMAP: | 878 | case PERF_RECORD_MMAP: |
801 | return tool->mmap(tool, event, sample, machine); | 879 | return tool->mmap(tool, event, sample, machine); |
@@ -964,6 +1042,12 @@ static void perf_session__warn_about_errors(const struct perf_session *session, | |||
964 | session->hists.stats.nr_invalid_chains, | 1042 | session->hists.stats.nr_invalid_chains, |
965 | session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); | 1043 | session->hists.stats.nr_events[PERF_RECORD_SAMPLE]); |
966 | } | 1044 | } |
1045 | |||
1046 | if (session->hists.stats.nr_unprocessable_samples != 0) { | ||
1047 | ui__warning("%u unprocessable samples recorded.\n" | ||
1048 | "Do you have a KVM guest running and not using 'perf kvm'?\n", | ||
1049 | session->hists.stats.nr_unprocessable_samples); | ||
1050 | } | ||
967 | } | 1051 | } |
968 | 1052 | ||
969 | #define session_done() (*(volatile int *)(&session_done)) | 1053 | #define session_done() (*(volatile int *)(&session_done)) |
@@ -1293,10 +1377,9 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | |||
1293 | 1377 | ||
1294 | void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | 1378 | void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, |
1295 | struct machine *machine, struct perf_evsel *evsel, | 1379 | struct machine *machine, struct perf_evsel *evsel, |
1296 | int print_sym, int print_dso) | 1380 | int print_sym, int print_dso, int print_symoffset) |
1297 | { | 1381 | { |
1298 | struct addr_location al; | 1382 | struct addr_location al; |
1299 | const char *symname, *dsoname; | ||
1300 | struct callchain_cursor *cursor = &evsel->hists.callchain_cursor; | 1383 | struct callchain_cursor *cursor = &evsel->hists.callchain_cursor; |
1301 | struct callchain_cursor_node *node; | 1384 | struct callchain_cursor_node *node; |
1302 | 1385 | ||
@@ -1324,20 +1407,13 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | |||
1324 | 1407 | ||
1325 | printf("\t%16" PRIx64, node->ip); | 1408 | printf("\t%16" PRIx64, node->ip); |
1326 | if (print_sym) { | 1409 | if (print_sym) { |
1327 | if (node->sym && node->sym->name) | 1410 | printf(" "); |
1328 | symname = node->sym->name; | 1411 | symbol__fprintf_symname(node->sym, stdout); |
1329 | else | ||
1330 | symname = ""; | ||
1331 | |||
1332 | printf(" %s", symname); | ||
1333 | } | 1412 | } |
1334 | if (print_dso) { | 1413 | if (print_dso) { |
1335 | if (node->map && node->map->dso && node->map->dso->name) | 1414 | printf(" ("); |
1336 | dsoname = node->map->dso->name; | 1415 | map__fprintf_dsoname(al.map, stdout); |
1337 | else | 1416 | printf(")"); |
1338 | dsoname = ""; | ||
1339 | |||
1340 | printf(" (%s)", dsoname); | ||
1341 | } | 1417 | } |
1342 | printf("\n"); | 1418 | printf("\n"); |
1343 | 1419 | ||
@@ -1347,21 +1423,18 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | |||
1347 | } else { | 1423 | } else { |
1348 | printf("%16" PRIx64, sample->ip); | 1424 | printf("%16" PRIx64, sample->ip); |
1349 | if (print_sym) { | 1425 | if (print_sym) { |
1350 | if (al.sym && al.sym->name) | 1426 | printf(" "); |
1351 | symname = al.sym->name; | 1427 | if (print_symoffset) |
1428 | symbol__fprintf_symname_offs(al.sym, &al, | ||
1429 | stdout); | ||
1352 | else | 1430 | else |
1353 | symname = ""; | 1431 | symbol__fprintf_symname(al.sym, stdout); |
1354 | |||
1355 | printf(" %s", symname); | ||
1356 | } | 1432 | } |
1357 | 1433 | ||
1358 | if (print_dso) { | 1434 | if (print_dso) { |
1359 | if (al.map && al.map->dso && al.map->dso->name) | 1435 | printf(" ("); |
1360 | dsoname = al.map->dso->name; | 1436 | map__fprintf_dsoname(al.map, stdout); |
1361 | else | 1437 | printf(")"); |
1362 | dsoname = ""; | ||
1363 | |||
1364 | printf(" (%s)", dsoname); | ||
1365 | } | 1438 | } |
1366 | } | 1439 | } |
1367 | } | 1440 | } |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 37bc38381fb6..7a5434c00565 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -73,6 +73,10 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel | |||
73 | struct ip_callchain *chain, | 73 | struct ip_callchain *chain, |
74 | struct symbol **parent); | 74 | struct symbol **parent); |
75 | 75 | ||
76 | struct branch_info *machine__resolve_bstack(struct machine *self, | ||
77 | struct thread *thread, | ||
78 | struct branch_stack *bs); | ||
79 | |||
76 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 80 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
77 | 81 | ||
78 | void mem_bswap_64(void *src, int byte_size); | 82 | void mem_bswap_64(void *src, int byte_size); |
@@ -147,7 +151,7 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, | |||
147 | 151 | ||
148 | void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, | 152 | void perf_event__print_ip(union perf_event *event, struct perf_sample *sample, |
149 | struct machine *machine, struct perf_evsel *evsel, | 153 | struct machine *machine, struct perf_evsel *evsel, |
150 | int print_sym, int print_dso); | 154 | int print_sym, int print_dso, int print_symoffset); |
151 | 155 | ||
152 | int perf_session__cpu_bitmap(struct perf_session *session, | 156 | int perf_session__cpu_bitmap(struct perf_session *session, |
153 | const char *cpu_list, unsigned long *cpu_bitmap); | 157 | const char *cpu_list, unsigned long *cpu_bitmap); |
diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 36d4c5619575..d0f9f29cf181 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py | |||
@@ -24,11 +24,11 @@ cflags += getenv('CFLAGS', '').split() | |||
24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') | 24 | build_lib = getenv('PYTHON_EXTBUILD_LIB') |
25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') | 25 | build_tmp = getenv('PYTHON_EXTBUILD_TMP') |
26 | 26 | ||
27 | ext_sources = [f.strip() for f in file('util/python-ext-sources') | ||
28 | if len(f.strip()) > 0 and f[0] != '#'] | ||
29 | |||
27 | perf = Extension('perf', | 30 | perf = Extension('perf', |
28 | sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c', | 31 | sources = ext_sources, |
29 | 'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c', | ||
30 | 'util/util.c', 'util/xyarray.c', 'util/cgroup.c', | ||
31 | 'util/debugfs.c'], | ||
32 | include_dirs = ['util/include'], | 32 | include_dirs = ['util/include'], |
33 | extra_compile_args = cflags, | 33 | extra_compile_args = cflags, |
34 | ) | 34 | ) |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 16da30d8d765..a27237430c5f 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -8,6 +8,7 @@ const char default_sort_order[] = "comm,dso,symbol"; | |||
8 | const char *sort_order = default_sort_order; | 8 | const char *sort_order = default_sort_order; |
9 | int sort__need_collapse = 0; | 9 | int sort__need_collapse = 0; |
10 | int sort__has_parent = 0; | 10 | int sort__has_parent = 0; |
11 | int sort__branch_mode = -1; /* -1 = means not set */ | ||
11 | 12 | ||
12 | enum sort_type sort__first_dimension; | 13 | enum sort_type sort__first_dimension; |
13 | 14 | ||
@@ -33,6 +34,9 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...) | |||
33 | } | 34 | } |
34 | } | 35 | } |
35 | va_end(ap); | 36 | va_end(ap); |
37 | |||
38 | if (n >= (int)size) | ||
39 | return size - 1; | ||
36 | return n; | 40 | return n; |
37 | } | 41 | } |
38 | 42 | ||
@@ -94,6 +98,26 @@ static int hist_entry__comm_snprintf(struct hist_entry *self, char *bf, | |||
94 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); | 98 | return repsep_snprintf(bf, size, "%*s", width, self->thread->comm); |
95 | } | 99 | } |
96 | 100 | ||
101 | static int64_t _sort__dso_cmp(struct map *map_l, struct map *map_r) | ||
102 | { | ||
103 | struct dso *dso_l = map_l ? map_l->dso : NULL; | ||
104 | struct dso *dso_r = map_r ? map_r->dso : NULL; | ||
105 | const char *dso_name_l, *dso_name_r; | ||
106 | |||
107 | if (!dso_l || !dso_r) | ||
108 | return cmp_null(dso_l, dso_r); | ||
109 | |||
110 | if (verbose) { | ||
111 | dso_name_l = dso_l->long_name; | ||
112 | dso_name_r = dso_r->long_name; | ||
113 | } else { | ||
114 | dso_name_l = dso_l->short_name; | ||
115 | dso_name_r = dso_r->short_name; | ||
116 | } | ||
117 | |||
118 | return strcmp(dso_name_l, dso_name_r); | ||
119 | } | ||
120 | |||
97 | struct sort_entry sort_comm = { | 121 | struct sort_entry sort_comm = { |
98 | .se_header = "Command", | 122 | .se_header = "Command", |
99 | .se_cmp = sort__comm_cmp, | 123 | .se_cmp = sort__comm_cmp, |
@@ -107,36 +131,74 @@ struct sort_entry sort_comm = { | |||
107 | static int64_t | 131 | static int64_t |
108 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | 132 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
109 | { | 133 | { |
110 | struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; | 134 | return _sort__dso_cmp(left->ms.map, right->ms.map); |
111 | struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; | 135 | } |
112 | const char *dso_name_l, *dso_name_r; | ||
113 | 136 | ||
114 | if (!dso_l || !dso_r) | ||
115 | return cmp_null(dso_l, dso_r); | ||
116 | 137 | ||
117 | if (verbose) { | 138 | static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r, |
118 | dso_name_l = dso_l->long_name; | 139 | u64 ip_l, u64 ip_r) |
119 | dso_name_r = dso_r->long_name; | 140 | { |
120 | } else { | 141 | if (!sym_l || !sym_r) |
121 | dso_name_l = dso_l->short_name; | 142 | return cmp_null(sym_l, sym_r); |
122 | dso_name_r = dso_r->short_name; | 143 | |
144 | if (sym_l == sym_r) | ||
145 | return 0; | ||
146 | |||
147 | if (sym_l) | ||
148 | ip_l = sym_l->start; | ||
149 | if (sym_r) | ||
150 | ip_r = sym_r->start; | ||
151 | |||
152 | return (int64_t)(ip_r - ip_l); | ||
153 | } | ||
154 | |||
155 | static int _hist_entry__dso_snprintf(struct map *map, char *bf, | ||
156 | size_t size, unsigned int width) | ||
157 | { | ||
158 | if (map && map->dso) { | ||
159 | const char *dso_name = !verbose ? map->dso->short_name : | ||
160 | map->dso->long_name; | ||
161 | return repsep_snprintf(bf, size, "%-*s", width, dso_name); | ||
123 | } | 162 | } |
124 | 163 | ||
125 | return strcmp(dso_name_l, dso_name_r); | 164 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); |
126 | } | 165 | } |
127 | 166 | ||
128 | static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, | 167 | static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf, |
129 | size_t size, unsigned int width) | 168 | size_t size, unsigned int width) |
130 | { | 169 | { |
131 | if (self->ms.map && self->ms.map->dso) { | 170 | return _hist_entry__dso_snprintf(self->ms.map, bf, size, width); |
132 | const char *dso_name = !verbose ? self->ms.map->dso->short_name : | 171 | } |
133 | self->ms.map->dso->long_name; | 172 | |
134 | return repsep_snprintf(bf, size, "%-*s", width, dso_name); | 173 | static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym, |
174 | u64 ip, char level, char *bf, size_t size, | ||
175 | unsigned int width __used) | ||
176 | { | ||
177 | size_t ret = 0; | ||
178 | |||
179 | if (verbose) { | ||
180 | char o = map ? dso__symtab_origin(map->dso) : '!'; | ||
181 | ret += repsep_snprintf(bf, size, "%-#*llx %c ", | ||
182 | BITS_PER_LONG / 4, ip, o); | ||
135 | } | 183 | } |
136 | 184 | ||
137 | return repsep_snprintf(bf, size, "%-*s", width, "[unknown]"); | 185 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level); |
186 | if (sym) | ||
187 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | ||
188 | width - ret, | ||
189 | sym->name); | ||
190 | else { | ||
191 | size_t len = BITS_PER_LONG / 4; | ||
192 | ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx", | ||
193 | len, ip); | ||
194 | ret += repsep_snprintf(bf + ret, size - ret, "%-*s", | ||
195 | width - ret, ""); | ||
196 | } | ||
197 | |||
198 | return ret; | ||
138 | } | 199 | } |
139 | 200 | ||
201 | |||
140 | struct sort_entry sort_dso = { | 202 | struct sort_entry sort_dso = { |
141 | .se_header = "Shared Object", | 203 | .se_header = "Shared Object", |
142 | .se_cmp = sort__dso_cmp, | 204 | .se_cmp = sort__dso_cmp, |
@@ -144,8 +206,14 @@ struct sort_entry sort_dso = { | |||
144 | .se_width_idx = HISTC_DSO, | 206 | .se_width_idx = HISTC_DSO, |
145 | }; | 207 | }; |
146 | 208 | ||
147 | /* --sort symbol */ | 209 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, |
210 | size_t size, unsigned int width __used) | ||
211 | { | ||
212 | return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip, | ||
213 | self->level, bf, size, width); | ||
214 | } | ||
148 | 215 | ||
216 | /* --sort symbol */ | ||
149 | static int64_t | 217 | static int64_t |
150 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 218 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
151 | { | 219 | { |
@@ -163,31 +231,7 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |||
163 | ip_l = left->ms.sym->start; | 231 | ip_l = left->ms.sym->start; |
164 | ip_r = right->ms.sym->start; | 232 | ip_r = right->ms.sym->start; |
165 | 233 | ||
166 | return (int64_t)(ip_r - ip_l); | 234 | return _sort__sym_cmp(left->ms.sym, right->ms.sym, ip_l, ip_r); |
167 | } | ||
168 | |||
169 | static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf, | ||
170 | size_t size, unsigned int width __used) | ||
171 | { | ||
172 | size_t ret = 0; | ||
173 | |||
174 | if (verbose) { | ||
175 | char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; | ||
176 | ret += repsep_snprintf(bf, size, "%-#*llx %c ", | ||
177 | BITS_PER_LONG / 4, self->ip, o); | ||
178 | } | ||
179 | |||
180 | if (!sort_dso.elide) | ||
181 | ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); | ||
182 | |||
183 | if (self->ms.sym) | ||
184 | ret += repsep_snprintf(bf + ret, size - ret, "%s", | ||
185 | self->ms.sym->name); | ||
186 | else | ||
187 | ret += repsep_snprintf(bf + ret, size - ret, "%-#*llx", | ||
188 | BITS_PER_LONG / 4, self->ip); | ||
189 | |||
190 | return ret; | ||
191 | } | 235 | } |
192 | 236 | ||
193 | struct sort_entry sort_sym = { | 237 | struct sort_entry sort_sym = { |
@@ -246,19 +290,155 @@ struct sort_entry sort_cpu = { | |||
246 | .se_width_idx = HISTC_CPU, | 290 | .se_width_idx = HISTC_CPU, |
247 | }; | 291 | }; |
248 | 292 | ||
293 | static int64_t | ||
294 | sort__dso_from_cmp(struct hist_entry *left, struct hist_entry *right) | ||
295 | { | ||
296 | return _sort__dso_cmp(left->branch_info->from.map, | ||
297 | right->branch_info->from.map); | ||
298 | } | ||
299 | |||
300 | static int hist_entry__dso_from_snprintf(struct hist_entry *self, char *bf, | ||
301 | size_t size, unsigned int width) | ||
302 | { | ||
303 | return _hist_entry__dso_snprintf(self->branch_info->from.map, | ||
304 | bf, size, width); | ||
305 | } | ||
306 | |||
307 | struct sort_entry sort_dso_from = { | ||
308 | .se_header = "Source Shared Object", | ||
309 | .se_cmp = sort__dso_from_cmp, | ||
310 | .se_snprintf = hist_entry__dso_from_snprintf, | ||
311 | .se_width_idx = HISTC_DSO_FROM, | ||
312 | }; | ||
313 | |||
314 | static int64_t | ||
315 | sort__dso_to_cmp(struct hist_entry *left, struct hist_entry *right) | ||
316 | { | ||
317 | return _sort__dso_cmp(left->branch_info->to.map, | ||
318 | right->branch_info->to.map); | ||
319 | } | ||
320 | |||
321 | static int hist_entry__dso_to_snprintf(struct hist_entry *self, char *bf, | ||
322 | size_t size, unsigned int width) | ||
323 | { | ||
324 | return _hist_entry__dso_snprintf(self->branch_info->to.map, | ||
325 | bf, size, width); | ||
326 | } | ||
327 | |||
328 | static int64_t | ||
329 | sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) | ||
330 | { | ||
331 | struct addr_map_symbol *from_l = &left->branch_info->from; | ||
332 | struct addr_map_symbol *from_r = &right->branch_info->from; | ||
333 | |||
334 | if (!from_l->sym && !from_r->sym) | ||
335 | return right->level - left->level; | ||
336 | |||
337 | return _sort__sym_cmp(from_l->sym, from_r->sym, from_l->addr, | ||
338 | from_r->addr); | ||
339 | } | ||
340 | |||
341 | static int64_t | ||
342 | sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) | ||
343 | { | ||
344 | struct addr_map_symbol *to_l = &left->branch_info->to; | ||
345 | struct addr_map_symbol *to_r = &right->branch_info->to; | ||
346 | |||
347 | if (!to_l->sym && !to_r->sym) | ||
348 | return right->level - left->level; | ||
349 | |||
350 | return _sort__sym_cmp(to_l->sym, to_r->sym, to_l->addr, to_r->addr); | ||
351 | } | ||
352 | |||
353 | static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf, | ||
354 | size_t size, unsigned int width __used) | ||
355 | { | ||
356 | struct addr_map_symbol *from = &self->branch_info->from; | ||
357 | return _hist_entry__sym_snprintf(from->map, from->sym, from->addr, | ||
358 | self->level, bf, size, width); | ||
359 | |||
360 | } | ||
361 | |||
362 | static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf, | ||
363 | size_t size, unsigned int width __used) | ||
364 | { | ||
365 | struct addr_map_symbol *to = &self->branch_info->to; | ||
366 | return _hist_entry__sym_snprintf(to->map, to->sym, to->addr, | ||
367 | self->level, bf, size, width); | ||
368 | |||
369 | } | ||
370 | |||
371 | struct sort_entry sort_dso_to = { | ||
372 | .se_header = "Target Shared Object", | ||
373 | .se_cmp = sort__dso_to_cmp, | ||
374 | .se_snprintf = hist_entry__dso_to_snprintf, | ||
375 | .se_width_idx = HISTC_DSO_TO, | ||
376 | }; | ||
377 | |||
378 | struct sort_entry sort_sym_from = { | ||
379 | .se_header = "Source Symbol", | ||
380 | .se_cmp = sort__sym_from_cmp, | ||
381 | .se_snprintf = hist_entry__sym_from_snprintf, | ||
382 | .se_width_idx = HISTC_SYMBOL_FROM, | ||
383 | }; | ||
384 | |||
385 | struct sort_entry sort_sym_to = { | ||
386 | .se_header = "Target Symbol", | ||
387 | .se_cmp = sort__sym_to_cmp, | ||
388 | .se_snprintf = hist_entry__sym_to_snprintf, | ||
389 | .se_width_idx = HISTC_SYMBOL_TO, | ||
390 | }; | ||
391 | |||
392 | static int64_t | ||
393 | sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right) | ||
394 | { | ||
395 | const unsigned char mp = left->branch_info->flags.mispred != | ||
396 | right->branch_info->flags.mispred; | ||
397 | const unsigned char p = left->branch_info->flags.predicted != | ||
398 | right->branch_info->flags.predicted; | ||
399 | |||
400 | return mp || p; | ||
401 | } | ||
402 | |||
403 | static int hist_entry__mispredict_snprintf(struct hist_entry *self, char *bf, | ||
404 | size_t size, unsigned int width){ | ||
405 | static const char *out = "N/A"; | ||
406 | |||
407 | if (self->branch_info->flags.predicted) | ||
408 | out = "N"; | ||
409 | else if (self->branch_info->flags.mispred) | ||
410 | out = "Y"; | ||
411 | |||
412 | return repsep_snprintf(bf, size, "%-*s", width, out); | ||
413 | } | ||
414 | |||
415 | struct sort_entry sort_mispredict = { | ||
416 | .se_header = "Branch Mispredicted", | ||
417 | .se_cmp = sort__mispredict_cmp, | ||
418 | .se_snprintf = hist_entry__mispredict_snprintf, | ||
419 | .se_width_idx = HISTC_MISPREDICT, | ||
420 | }; | ||
421 | |||
249 | struct sort_dimension { | 422 | struct sort_dimension { |
250 | const char *name; | 423 | const char *name; |
251 | struct sort_entry *entry; | 424 | struct sort_entry *entry; |
252 | int taken; | 425 | int taken; |
253 | }; | 426 | }; |
254 | 427 | ||
428 | #define DIM(d, n, func) [d] = { .name = n, .entry = &(func) } | ||
429 | |||
255 | static struct sort_dimension sort_dimensions[] = { | 430 | static struct sort_dimension sort_dimensions[] = { |
256 | { .name = "pid", .entry = &sort_thread, }, | 431 | DIM(SORT_PID, "pid", sort_thread), |
257 | { .name = "comm", .entry = &sort_comm, }, | 432 | DIM(SORT_COMM, "comm", sort_comm), |
258 | { .name = "dso", .entry = &sort_dso, }, | 433 | DIM(SORT_DSO, "dso", sort_dso), |
259 | { .name = "symbol", .entry = &sort_sym, }, | 434 | DIM(SORT_DSO_FROM, "dso_from", sort_dso_from), |
260 | { .name = "parent", .entry = &sort_parent, }, | 435 | DIM(SORT_DSO_TO, "dso_to", sort_dso_to), |
261 | { .name = "cpu", .entry = &sort_cpu, }, | 436 | DIM(SORT_SYM, "symbol", sort_sym), |
437 | DIM(SORT_SYM_TO, "symbol_from", sort_sym_from), | ||
438 | DIM(SORT_SYM_FROM, "symbol_to", sort_sym_to), | ||
439 | DIM(SORT_PARENT, "parent", sort_parent), | ||
440 | DIM(SORT_CPU, "cpu", sort_cpu), | ||
441 | DIM(SORT_MISPREDICT, "mispredict", sort_mispredict), | ||
262 | }; | 442 | }; |
263 | 443 | ||
264 | int sort_dimension__add(const char *tok) | 444 | int sort_dimension__add(const char *tok) |
@@ -270,7 +450,6 @@ int sort_dimension__add(const char *tok) | |||
270 | 450 | ||
271 | if (strncasecmp(tok, sd->name, strlen(tok))) | 451 | if (strncasecmp(tok, sd->name, strlen(tok))) |
272 | continue; | 452 | continue; |
273 | |||
274 | if (sd->entry == &sort_parent) { | 453 | if (sd->entry == &sort_parent) { |
275 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); | 454 | int ret = regcomp(&parent_regex, parent_pattern, REG_EXTENDED); |
276 | if (ret) { | 455 | if (ret) { |
@@ -302,6 +481,16 @@ int sort_dimension__add(const char *tok) | |||
302 | sort__first_dimension = SORT_PARENT; | 481 | sort__first_dimension = SORT_PARENT; |
303 | else if (!strcmp(sd->name, "cpu")) | 482 | else if (!strcmp(sd->name, "cpu")) |
304 | sort__first_dimension = SORT_CPU; | 483 | sort__first_dimension = SORT_CPU; |
484 | else if (!strcmp(sd->name, "symbol_from")) | ||
485 | sort__first_dimension = SORT_SYM_FROM; | ||
486 | else if (!strcmp(sd->name, "symbol_to")) | ||
487 | sort__first_dimension = SORT_SYM_TO; | ||
488 | else if (!strcmp(sd->name, "dso_from")) | ||
489 | sort__first_dimension = SORT_DSO_FROM; | ||
490 | else if (!strcmp(sd->name, "dso_to")) | ||
491 | sort__first_dimension = SORT_DSO_TO; | ||
492 | else if (!strcmp(sd->name, "mispredict")) | ||
493 | sort__first_dimension = SORT_MISPREDICT; | ||
305 | } | 494 | } |
306 | 495 | ||
307 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | 496 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); |
@@ -309,7 +498,6 @@ int sort_dimension__add(const char *tok) | |||
309 | 498 | ||
310 | return 0; | 499 | return 0; |
311 | } | 500 | } |
312 | |||
313 | return -ESRCH; | 501 | return -ESRCH; |
314 | } | 502 | } |
315 | 503 | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 3f67ae395752..472aa5a63a58 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -31,11 +31,16 @@ extern const char *parent_pattern; | |||
31 | extern const char default_sort_order[]; | 31 | extern const char default_sort_order[]; |
32 | extern int sort__need_collapse; | 32 | extern int sort__need_collapse; |
33 | extern int sort__has_parent; | 33 | extern int sort__has_parent; |
34 | extern int sort__branch_mode; | ||
34 | extern char *field_sep; | 35 | extern char *field_sep; |
35 | extern struct sort_entry sort_comm; | 36 | extern struct sort_entry sort_comm; |
36 | extern struct sort_entry sort_dso; | 37 | extern struct sort_entry sort_dso; |
37 | extern struct sort_entry sort_sym; | 38 | extern struct sort_entry sort_sym; |
38 | extern struct sort_entry sort_parent; | 39 | extern struct sort_entry sort_parent; |
40 | extern struct sort_entry sort_dso_from; | ||
41 | extern struct sort_entry sort_dso_to; | ||
42 | extern struct sort_entry sort_sym_from; | ||
43 | extern struct sort_entry sort_sym_to; | ||
39 | extern enum sort_type sort__first_dimension; | 44 | extern enum sort_type sort__first_dimension; |
40 | 45 | ||
41 | /** | 46 | /** |
@@ -72,6 +77,7 @@ struct hist_entry { | |||
72 | struct hist_entry *pair; | 77 | struct hist_entry *pair; |
73 | struct rb_root sorted_chain; | 78 | struct rb_root sorted_chain; |
74 | }; | 79 | }; |
80 | struct branch_info *branch_info; | ||
75 | struct callchain_root callchain[0]; | 81 | struct callchain_root callchain[0]; |
76 | }; | 82 | }; |
77 | 83 | ||
@@ -82,6 +88,11 @@ enum sort_type { | |||
82 | SORT_SYM, | 88 | SORT_SYM, |
83 | SORT_PARENT, | 89 | SORT_PARENT, |
84 | SORT_CPU, | 90 | SORT_CPU, |
91 | SORT_DSO_FROM, | ||
92 | SORT_DSO_TO, | ||
93 | SORT_SYM_FROM, | ||
94 | SORT_SYM_TO, | ||
95 | SORT_MISPREDICT, | ||
85 | }; | 96 | }; |
86 | 97 | ||
87 | /* | 98 | /* |
diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index 92e068517c1a..2eeb51baf077 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c | |||
@@ -1,4 +1,5 @@ | |||
1 | #include "cache.h" | 1 | #include "cache.h" |
2 | #include <linux/kernel.h> | ||
2 | 3 | ||
3 | int prefixcmp(const char *str, const char *prefix) | 4 | int prefixcmp(const char *str, const char *prefix) |
4 | { | 5 | { |
@@ -89,14 +90,14 @@ void strbuf_addf(struct strbuf *sb, const char *fmt, ...) | |||
89 | if (!strbuf_avail(sb)) | 90 | if (!strbuf_avail(sb)) |
90 | strbuf_grow(sb, 64); | 91 | strbuf_grow(sb, 64); |
91 | va_start(ap, fmt); | 92 | va_start(ap, fmt); |
92 | len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); | 93 | len = vscnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); |
93 | va_end(ap); | 94 | va_end(ap); |
94 | if (len < 0) | 95 | if (len < 0) |
95 | die("your vsnprintf is broken"); | 96 | die("your vscnprintf is broken"); |
96 | if (len > strbuf_avail(sb)) { | 97 | if (len > strbuf_avail(sb)) { |
97 | strbuf_grow(sb, len); | 98 | strbuf_grow(sb, len); |
98 | va_start(ap, fmt); | 99 | va_start(ap, fmt); |
99 | len = vsnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); | 100 | len = vscnprintf(sb->buf + sb->len, sb->alloc - sb->len, fmt, ap); |
100 | va_end(ap); | 101 | va_end(ap); |
101 | if (len > strbuf_avail(sb)) { | 102 | if (len > strbuf_avail(sb)) { |
102 | die("this should not happen, your snprintf is broken"); | 103 | die("this should not happen, your snprintf is broken"); |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 215d50f2042e..c0a028c3ebaf 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1,8 +1,5 @@ | |||
1 | #define _GNU_SOURCE | ||
2 | #include <ctype.h> | ||
3 | #include <dirent.h> | 1 | #include <dirent.h> |
4 | #include <errno.h> | 2 | #include <errno.h> |
5 | #include <libgen.h> | ||
6 | #include <stdlib.h> | 3 | #include <stdlib.h> |
7 | #include <stdio.h> | 4 | #include <stdio.h> |
8 | #include <string.h> | 5 | #include <string.h> |
@@ -13,6 +10,7 @@ | |||
13 | #include <unistd.h> | 10 | #include <unistd.h> |
14 | #include <inttypes.h> | 11 | #include <inttypes.h> |
15 | #include "build-id.h" | 12 | #include "build-id.h" |
13 | #include "util.h" | ||
16 | #include "debug.h" | 14 | #include "debug.h" |
17 | #include "symbol.h" | 15 | #include "symbol.h" |
18 | #include "strlist.h" | 16 | #include "strlist.h" |
@@ -52,6 +50,8 @@ struct symbol_conf symbol_conf = { | |||
52 | 50 | ||
53 | int dso__name_len(const struct dso *dso) | 51 | int dso__name_len(const struct dso *dso) |
54 | { | 52 | { |
53 | if (!dso) | ||
54 | return strlen("[unknown]"); | ||
55 | if (verbose) | 55 | if (verbose) |
56 | return dso->long_name_len; | 56 | return dso->long_name_len; |
57 | 57 | ||
@@ -264,6 +264,28 @@ static size_t symbol__fprintf(struct symbol *sym, FILE *fp) | |||
264 | sym->name); | 264 | sym->name); |
265 | } | 265 | } |
266 | 266 | ||
267 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | ||
268 | const struct addr_location *al, FILE *fp) | ||
269 | { | ||
270 | unsigned long offset; | ||
271 | size_t length; | ||
272 | |||
273 | if (sym && sym->name) { | ||
274 | length = fprintf(fp, "%s", sym->name); | ||
275 | if (al) { | ||
276 | offset = al->addr - sym->start; | ||
277 | length += fprintf(fp, "+0x%lx", offset); | ||
278 | } | ||
279 | return length; | ||
280 | } else | ||
281 | return fprintf(fp, "[unknown]"); | ||
282 | } | ||
283 | |||
284 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp) | ||
285 | { | ||
286 | return symbol__fprintf_symname_offs(sym, NULL, fp); | ||
287 | } | ||
288 | |||
267 | void dso__set_long_name(struct dso *dso, char *name) | 289 | void dso__set_long_name(struct dso *dso, char *name) |
268 | { | 290 | { |
269 | if (name == NULL) | 291 | if (name == NULL) |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 123c2e14353e..ac49ef208a5f 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include <stdint.h> | 6 | #include <stdint.h> |
7 | #include "map.h" | 7 | #include "map.h" |
8 | #include "../perf.h" | ||
8 | #include <linux/list.h> | 9 | #include <linux/list.h> |
9 | #include <linux/rbtree.h> | 10 | #include <linux/rbtree.h> |
10 | #include <stdio.h> | 11 | #include <stdio.h> |
@@ -70,6 +71,7 @@ struct symbol_conf { | |||
70 | unsigned short priv_size; | 71 | unsigned short priv_size; |
71 | unsigned short nr_events; | 72 | unsigned short nr_events; |
72 | bool try_vmlinux_path, | 73 | bool try_vmlinux_path, |
74 | show_kernel_path, | ||
73 | use_modules, | 75 | use_modules, |
74 | sort_by_name, | 76 | sort_by_name, |
75 | show_nr_samples, | 77 | show_nr_samples, |
@@ -95,7 +97,11 @@ struct symbol_conf { | |||
95 | *col_width_list_str; | 97 | *col_width_list_str; |
96 | struct strlist *dso_list, | 98 | struct strlist *dso_list, |
97 | *comm_list, | 99 | *comm_list, |
98 | *sym_list; | 100 | *sym_list, |
101 | *dso_from_list, | ||
102 | *dso_to_list, | ||
103 | *sym_from_list, | ||
104 | *sym_to_list; | ||
99 | const char *symfs; | 105 | const char *symfs; |
100 | }; | 106 | }; |
101 | 107 | ||
@@ -119,6 +125,19 @@ struct map_symbol { | |||
119 | bool has_children; | 125 | bool has_children; |
120 | }; | 126 | }; |
121 | 127 | ||
128 | struct addr_map_symbol { | ||
129 | struct map *map; | ||
130 | struct symbol *sym; | ||
131 | u64 addr; | ||
132 | u64 al_addr; | ||
133 | }; | ||
134 | |||
135 | struct branch_info { | ||
136 | struct addr_map_symbol from; | ||
137 | struct addr_map_symbol to; | ||
138 | struct branch_flags flags; | ||
139 | }; | ||
140 | |||
122 | struct addr_location { | 141 | struct addr_location { |
123 | struct thread *thread; | 142 | struct thread *thread; |
124 | struct map *map; | 143 | struct map *map; |
@@ -241,6 +260,9 @@ void machines__destroy_guest_kernel_maps(struct rb_root *machines); | |||
241 | 260 | ||
242 | int symbol__init(void); | 261 | int symbol__init(void); |
243 | void symbol__exit(void); | 262 | void symbol__exit(void); |
263 | size_t symbol__fprintf_symname_offs(const struct symbol *sym, | ||
264 | const struct addr_location *al, FILE *fp); | ||
265 | size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); | ||
244 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 266 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
245 | 267 | ||
246 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); | 268 | size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp); |
diff --git a/tools/perf/util/sysfs.c b/tools/perf/util/sysfs.c new file mode 100644 index 000000000000..48c6902e749f --- /dev/null +++ b/tools/perf/util/sysfs.c | |||
@@ -0,0 +1,60 @@ | |||
1 | |||
2 | #include "util.h" | ||
3 | #include "sysfs.h" | ||
4 | |||
5 | static const char * const sysfs_known_mountpoints[] = { | ||
6 | "/sys", | ||
7 | 0, | ||
8 | }; | ||
9 | |||
10 | static int sysfs_found; | ||
11 | char sysfs_mountpoint[PATH_MAX]; | ||
12 | |||
13 | static int sysfs_valid_mountpoint(const char *sysfs) | ||
14 | { | ||
15 | struct statfs st_fs; | ||
16 | |||
17 | if (statfs(sysfs, &st_fs) < 0) | ||
18 | return -ENOENT; | ||
19 | else if (st_fs.f_type != (long) SYSFS_MAGIC) | ||
20 | return -ENOENT; | ||
21 | |||
22 | return 0; | ||
23 | } | ||
24 | |||
25 | const char *sysfs_find_mountpoint(void) | ||
26 | { | ||
27 | const char * const *ptr; | ||
28 | char type[100]; | ||
29 | FILE *fp; | ||
30 | |||
31 | if (sysfs_found) | ||
32 | return (const char *) sysfs_mountpoint; | ||
33 | |||
34 | ptr = sysfs_known_mountpoints; | ||
35 | while (*ptr) { | ||
36 | if (sysfs_valid_mountpoint(*ptr) == 0) { | ||
37 | sysfs_found = 1; | ||
38 | strcpy(sysfs_mountpoint, *ptr); | ||
39 | return sysfs_mountpoint; | ||
40 | } | ||
41 | ptr++; | ||
42 | } | ||
43 | |||
44 | /* give up and parse /proc/mounts */ | ||
45 | fp = fopen("/proc/mounts", "r"); | ||
46 | if (fp == NULL) | ||
47 | return NULL; | ||
48 | |||
49 | while (!sysfs_found && | ||
50 | fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", | ||
51 | sysfs_mountpoint, type) == 2) { | ||
52 | |||
53 | if (strcmp(type, "sysfs") == 0) | ||
54 | sysfs_found = 1; | ||
55 | } | ||
56 | |||
57 | fclose(fp); | ||
58 | |||
59 | return sysfs_found ? sysfs_mountpoint : NULL; | ||
60 | } | ||
diff --git a/tools/perf/util/sysfs.h b/tools/perf/util/sysfs.h new file mode 100644 index 000000000000..a813b7203938 --- /dev/null +++ b/tools/perf/util/sysfs.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef __SYSFS_H__ | ||
2 | #define __SYSFS_H__ | ||
3 | |||
4 | const char *sysfs_find_mountpoint(void); | ||
5 | |||
6 | #endif /* __DEBUGFS_H__ */ | ||
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index a5df131b77c3..84d9bd782004 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c | |||
@@ -1,6 +1,13 @@ | |||
1 | #include <dirent.h> | 1 | #include <dirent.h> |
2 | #include <limits.h> | ||
3 | #include <stdbool.h> | ||
2 | #include <stdlib.h> | 4 | #include <stdlib.h> |
3 | #include <stdio.h> | 5 | #include <stdio.h> |
6 | #include <sys/types.h> | ||
7 | #include <sys/stat.h> | ||
8 | #include <unistd.h> | ||
9 | #include "strlist.h" | ||
10 | #include <string.h> | ||
4 | #include "thread_map.h" | 11 | #include "thread_map.h" |
5 | 12 | ||
6 | /* Skip "." and ".." directories */ | 13 | /* Skip "." and ".." directories */ |
@@ -23,7 +30,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid) | |||
23 | sprintf(name, "/proc/%d/task", pid); | 30 | sprintf(name, "/proc/%d/task", pid); |
24 | items = scandir(name, &namelist, filter, NULL); | 31 | items = scandir(name, &namelist, filter, NULL); |
25 | if (items <= 0) | 32 | if (items <= 0) |
26 | return NULL; | 33 | return NULL; |
27 | 34 | ||
28 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); | 35 | threads = malloc(sizeof(*threads) + sizeof(pid_t) * items); |
29 | if (threads != NULL) { | 36 | if (threads != NULL) { |
@@ -51,14 +58,240 @@ struct thread_map *thread_map__new_by_tid(pid_t tid) | |||
51 | return threads; | 58 | return threads; |
52 | } | 59 | } |
53 | 60 | ||
54 | struct thread_map *thread_map__new(pid_t pid, pid_t tid) | 61 | struct thread_map *thread_map__new_by_uid(uid_t uid) |
62 | { | ||
63 | DIR *proc; | ||
64 | int max_threads = 32, items, i; | ||
65 | char path[256]; | ||
66 | struct dirent dirent, *next, **namelist = NULL; | ||
67 | struct thread_map *threads = malloc(sizeof(*threads) + | ||
68 | max_threads * sizeof(pid_t)); | ||
69 | if (threads == NULL) | ||
70 | goto out; | ||
71 | |||
72 | proc = opendir("/proc"); | ||
73 | if (proc == NULL) | ||
74 | goto out_free_threads; | ||
75 | |||
76 | threads->nr = 0; | ||
77 | |||
78 | while (!readdir_r(proc, &dirent, &next) && next) { | ||
79 | char *end; | ||
80 | bool grow = false; | ||
81 | struct stat st; | ||
82 | pid_t pid = strtol(dirent.d_name, &end, 10); | ||
83 | |||
84 | if (*end) /* only interested in proper numerical dirents */ | ||
85 | continue; | ||
86 | |||
87 | snprintf(path, sizeof(path), "/proc/%s", dirent.d_name); | ||
88 | |||
89 | if (stat(path, &st) != 0) | ||
90 | continue; | ||
91 | |||
92 | if (st.st_uid != uid) | ||
93 | continue; | ||
94 | |||
95 | snprintf(path, sizeof(path), "/proc/%d/task", pid); | ||
96 | items = scandir(path, &namelist, filter, NULL); | ||
97 | if (items <= 0) | ||
98 | goto out_free_closedir; | ||
99 | |||
100 | while (threads->nr + items >= max_threads) { | ||
101 | max_threads *= 2; | ||
102 | grow = true; | ||
103 | } | ||
104 | |||
105 | if (grow) { | ||
106 | struct thread_map *tmp; | ||
107 | |||
108 | tmp = realloc(threads, (sizeof(*threads) + | ||
109 | max_threads * sizeof(pid_t))); | ||
110 | if (tmp == NULL) | ||
111 | goto out_free_namelist; | ||
112 | |||
113 | threads = tmp; | ||
114 | } | ||
115 | |||
116 | for (i = 0; i < items; i++) | ||
117 | threads->map[threads->nr + i] = atoi(namelist[i]->d_name); | ||
118 | |||
119 | for (i = 0; i < items; i++) | ||
120 | free(namelist[i]); | ||
121 | free(namelist); | ||
122 | |||
123 | threads->nr += items; | ||
124 | } | ||
125 | |||
126 | out_closedir: | ||
127 | closedir(proc); | ||
128 | out: | ||
129 | return threads; | ||
130 | |||
131 | out_free_threads: | ||
132 | free(threads); | ||
133 | return NULL; | ||
134 | |||
135 | out_free_namelist: | ||
136 | for (i = 0; i < items; i++) | ||
137 | free(namelist[i]); | ||
138 | free(namelist); | ||
139 | |||
140 | out_free_closedir: | ||
141 | free(threads); | ||
142 | threads = NULL; | ||
143 | goto out_closedir; | ||
144 | } | ||
145 | |||
146 | struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid) | ||
55 | { | 147 | { |
56 | if (pid != -1) | 148 | if (pid != -1) |
57 | return thread_map__new_by_pid(pid); | 149 | return thread_map__new_by_pid(pid); |
150 | |||
151 | if (tid == -1 && uid != UINT_MAX) | ||
152 | return thread_map__new_by_uid(uid); | ||
153 | |||
58 | return thread_map__new_by_tid(tid); | 154 | return thread_map__new_by_tid(tid); |
59 | } | 155 | } |
60 | 156 | ||
157 | static struct thread_map *thread_map__new_by_pid_str(const char *pid_str) | ||
158 | { | ||
159 | struct thread_map *threads = NULL, *nt; | ||
160 | char name[256]; | ||
161 | int items, total_tasks = 0; | ||
162 | struct dirent **namelist = NULL; | ||
163 | int i, j = 0; | ||
164 | pid_t pid, prev_pid = INT_MAX; | ||
165 | char *end_ptr; | ||
166 | struct str_node *pos; | ||
167 | struct strlist *slist = strlist__new(false, pid_str); | ||
168 | |||
169 | if (!slist) | ||
170 | return NULL; | ||
171 | |||
172 | strlist__for_each(pos, slist) { | ||
173 | pid = strtol(pos->s, &end_ptr, 10); | ||
174 | |||
175 | if (pid == INT_MIN || pid == INT_MAX || | ||
176 | (*end_ptr != '\0' && *end_ptr != ',')) | ||
177 | goto out_free_threads; | ||
178 | |||
179 | if (pid == prev_pid) | ||
180 | continue; | ||
181 | |||
182 | sprintf(name, "/proc/%d/task", pid); | ||
183 | items = scandir(name, &namelist, filter, NULL); | ||
184 | if (items <= 0) | ||
185 | goto out_free_threads; | ||
186 | |||
187 | total_tasks += items; | ||
188 | nt = realloc(threads, (sizeof(*threads) + | ||
189 | sizeof(pid_t) * total_tasks)); | ||
190 | if (nt == NULL) | ||
191 | goto out_free_threads; | ||
192 | |||
193 | threads = nt; | ||
194 | |||
195 | if (threads) { | ||
196 | for (i = 0; i < items; i++) | ||
197 | threads->map[j++] = atoi(namelist[i]->d_name); | ||
198 | threads->nr = total_tasks; | ||
199 | } | ||
200 | |||
201 | for (i = 0; i < items; i++) | ||
202 | free(namelist[i]); | ||
203 | free(namelist); | ||
204 | |||
205 | if (!threads) | ||
206 | break; | ||
207 | } | ||
208 | |||
209 | out: | ||
210 | strlist__delete(slist); | ||
211 | return threads; | ||
212 | |||
213 | out_free_threads: | ||
214 | free(threads); | ||
215 | threads = NULL; | ||
216 | goto out; | ||
217 | } | ||
218 | |||
219 | static struct thread_map *thread_map__new_by_tid_str(const char *tid_str) | ||
220 | { | ||
221 | struct thread_map *threads = NULL, *nt; | ||
222 | int ntasks = 0; | ||
223 | pid_t tid, prev_tid = INT_MAX; | ||
224 | char *end_ptr; | ||
225 | struct str_node *pos; | ||
226 | struct strlist *slist; | ||
227 | |||
228 | /* perf-stat expects threads to be generated even if tid not given */ | ||
229 | if (!tid_str) { | ||
230 | threads = malloc(sizeof(*threads) + sizeof(pid_t)); | ||
231 | if (threads != NULL) { | ||
232 | threads->map[0] = -1; | ||
233 | threads->nr = 1; | ||
234 | } | ||
235 | return threads; | ||
236 | } | ||
237 | |||
238 | slist = strlist__new(false, tid_str); | ||
239 | if (!slist) | ||
240 | return NULL; | ||
241 | |||
242 | strlist__for_each(pos, slist) { | ||
243 | tid = strtol(pos->s, &end_ptr, 10); | ||
244 | |||
245 | if (tid == INT_MIN || tid == INT_MAX || | ||
246 | (*end_ptr != '\0' && *end_ptr != ',')) | ||
247 | goto out_free_threads; | ||
248 | |||
249 | if (tid == prev_tid) | ||
250 | continue; | ||
251 | |||
252 | ntasks++; | ||
253 | nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks); | ||
254 | |||
255 | if (nt == NULL) | ||
256 | goto out_free_threads; | ||
257 | |||
258 | threads = nt; | ||
259 | threads->map[ntasks - 1] = tid; | ||
260 | threads->nr = ntasks; | ||
261 | } | ||
262 | out: | ||
263 | return threads; | ||
264 | |||
265 | out_free_threads: | ||
266 | free(threads); | ||
267 | threads = NULL; | ||
268 | goto out; | ||
269 | } | ||
270 | |||
271 | struct thread_map *thread_map__new_str(const char *pid, const char *tid, | ||
272 | uid_t uid) | ||
273 | { | ||
274 | if (pid) | ||
275 | return thread_map__new_by_pid_str(pid); | ||
276 | |||
277 | if (!tid && uid != UINT_MAX) | ||
278 | return thread_map__new_by_uid(uid); | ||
279 | |||
280 | return thread_map__new_by_tid_str(tid); | ||
281 | } | ||
282 | |||
61 | void thread_map__delete(struct thread_map *threads) | 283 | void thread_map__delete(struct thread_map *threads) |
62 | { | 284 | { |
63 | free(threads); | 285 | free(threads); |
64 | } | 286 | } |
287 | |||
288 | size_t thread_map__fprintf(struct thread_map *threads, FILE *fp) | ||
289 | { | ||
290 | int i; | ||
291 | size_t printed = fprintf(fp, "%d thread%s: ", | ||
292 | threads->nr, threads->nr > 1 ? "s" : ""); | ||
293 | for (i = 0; i < threads->nr; ++i) | ||
294 | printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]); | ||
295 | |||
296 | return printed + fprintf(fp, "\n"); | ||
297 | } | ||
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h index 3cb907311409..7da80f14418b 100644 --- a/tools/perf/util/thread_map.h +++ b/tools/perf/util/thread_map.h | |||
@@ -2,6 +2,7 @@ | |||
2 | #define __PERF_THREAD_MAP_H | 2 | #define __PERF_THREAD_MAP_H |
3 | 3 | ||
4 | #include <sys/types.h> | 4 | #include <sys/types.h> |
5 | #include <stdio.h> | ||
5 | 6 | ||
6 | struct thread_map { | 7 | struct thread_map { |
7 | int nr; | 8 | int nr; |
@@ -10,6 +11,14 @@ struct thread_map { | |||
10 | 11 | ||
11 | struct thread_map *thread_map__new_by_pid(pid_t pid); | 12 | struct thread_map *thread_map__new_by_pid(pid_t pid); |
12 | struct thread_map *thread_map__new_by_tid(pid_t tid); | 13 | struct thread_map *thread_map__new_by_tid(pid_t tid); |
13 | struct thread_map *thread_map__new(pid_t pid, pid_t tid); | 14 | struct thread_map *thread_map__new_by_uid(uid_t uid); |
15 | struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid); | ||
16 | |||
17 | struct thread_map *thread_map__new_str(const char *pid, | ||
18 | const char *tid, uid_t uid); | ||
19 | |||
14 | void thread_map__delete(struct thread_map *threads); | 20 | void thread_map__delete(struct thread_map *threads); |
21 | |||
22 | size_t thread_map__fprintf(struct thread_map *threads, FILE *fp); | ||
23 | |||
15 | #endif /* __PERF_THREAD_MAP_H */ | 24 | #endif /* __PERF_THREAD_MAP_H */ |
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index 500471dffa4f..09fe579ccafb 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c | |||
@@ -69,12 +69,15 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
69 | 69 | ||
70 | ret += SNPRINTF(bf + ret, size - ret, "], "); | 70 | ret += SNPRINTF(bf + ret, size - ret, "], "); |
71 | 71 | ||
72 | if (top->target_pid != -1) | 72 | if (top->target_pid) |
73 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %d", | 73 | ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s", |
74 | top->target_pid); | 74 | top->target_pid); |
75 | else if (top->target_tid != -1) | 75 | else if (top->target_tid) |
76 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d", | 76 | ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s", |
77 | top->target_tid); | 77 | top->target_tid); |
78 | else if (top->uid_str != NULL) | ||
79 | ret += SNPRINTF(bf + ret, size - ret, " (uid: %s", | ||
80 | top->uid_str); | ||
78 | else | 81 | else |
79 | ret += SNPRINTF(bf + ret, size - ret, " (all"); | 82 | ret += SNPRINTF(bf + ret, size - ret, " (all"); |
80 | 83 | ||
@@ -82,7 +85,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) | |||
82 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", | 85 | ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)", |
83 | top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); | 86 | top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list); |
84 | else { | 87 | else { |
85 | if (top->target_tid != -1) | 88 | if (top->target_tid) |
86 | ret += SNPRINTF(bf + ret, size - ret, ")"); | 89 | ret += SNPRINTF(bf + ret, size - ret, ")"); |
87 | else | 90 | else |
88 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", | 91 | ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)", |
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index a248f3c2c60d..ce61cb2d1acf 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
@@ -23,7 +23,8 @@ struct perf_top { | |||
23 | u64 guest_us_samples, guest_kernel_samples; | 23 | u64 guest_us_samples, guest_kernel_samples; |
24 | int print_entries, count_filter, delay_secs; | 24 | int print_entries, count_filter, delay_secs; |
25 | int freq; | 25 | int freq; |
26 | pid_t target_pid, target_tid; | 26 | const char *target_pid, *target_tid; |
27 | uid_t uid; | ||
27 | bool hide_kernel_symbols, hide_user_symbols, zero; | 28 | bool hide_kernel_symbols, hide_user_symbols, zero; |
28 | bool system_wide; | 29 | bool system_wide; |
29 | bool use_tui, use_stdio; | 30 | bool use_tui, use_stdio; |
@@ -33,7 +34,8 @@ struct perf_top { | |||
33 | bool vmlinux_warned; | 34 | bool vmlinux_warned; |
34 | bool inherit; | 35 | bool inherit; |
35 | bool group; | 36 | bool group; |
36 | bool sample_id_all_avail; | 37 | bool sample_id_all_missing; |
38 | bool exclude_guest_missing; | ||
37 | bool dump_symtab; | 39 | bool dump_symtab; |
38 | const char *cpu_list; | 40 | const char *cpu_list; |
39 | struct hist_entry *sym_filter_entry; | 41 | struct hist_entry *sym_filter_entry; |
@@ -45,6 +47,7 @@ struct perf_top { | |||
45 | int realtime_prio; | 47 | int realtime_prio; |
46 | int sym_pcnt_filter; | 48 | int sym_pcnt_filter; |
47 | const char *sym_filter; | 49 | const char *sym_filter; |
50 | const char *uid_str; | ||
48 | }; | 51 | }; |
49 | 52 | ||
50 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); | 53 | size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); |
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index ac6830d8292b..fc22cf5c605f 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c | |||
@@ -18,7 +18,6 @@ | |||
18 | * | 18 | * |
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
20 | */ | 20 | */ |
21 | #include <ctype.h> | ||
22 | #include "util.h" | 21 | #include "util.h" |
23 | #include <dirent.h> | 22 | #include <dirent.h> |
24 | #include <mntent.h> | 23 | #include <mntent.h> |
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 6c164dc9ee95..dfd1bd8371a4 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c | |||
@@ -21,14 +21,12 @@ | |||
21 | * The parts for function graph printing was taken and modified from the | 21 | * The parts for function graph printing was taken and modified from the |
22 | * Linux Kernel that were written by Frederic Weisbecker. | 22 | * Linux Kernel that were written by Frederic Weisbecker. |
23 | */ | 23 | */ |
24 | #define _GNU_SOURCE | 24 | |
25 | #include <stdio.h> | 25 | #include <stdio.h> |
26 | #include <stdlib.h> | 26 | #include <stdlib.h> |
27 | #include <string.h> | 27 | #include <string.h> |
28 | #include <ctype.h> | ||
29 | #include <errno.h> | 28 | #include <errno.h> |
30 | 29 | ||
31 | #undef _GNU_SOURCE | ||
32 | #include "../perf.h" | 30 | #include "../perf.h" |
33 | #include "util.h" | 31 | #include "util.h" |
34 | #include "trace-event.h" | 32 | #include "trace-event.h" |
@@ -724,7 +722,7 @@ static char *event_read_name(void) | |||
724 | static int event_read_id(void) | 722 | static int event_read_id(void) |
725 | { | 723 | { |
726 | char *token; | 724 | char *token; |
727 | int id; | 725 | int id = -1; |
728 | 726 | ||
729 | if (read_expected_item(EVENT_ITEM, "ID") < 0) | 727 | if (read_expected_item(EVENT_ITEM, "ID") < 0) |
730 | return -1; | 728 | return -1; |
@@ -733,15 +731,13 @@ static int event_read_id(void) | |||
733 | return -1; | 731 | return -1; |
734 | 732 | ||
735 | if (read_expect_type(EVENT_ITEM, &token) < 0) | 733 | if (read_expect_type(EVENT_ITEM, &token) < 0) |
736 | goto fail; | 734 | goto free; |
737 | 735 | ||
738 | id = strtoul(token, NULL, 0); | 736 | id = strtoul(token, NULL, 0); |
739 | free_token(token); | ||
740 | return id; | ||
741 | 737 | ||
742 | fail: | 738 | free: |
743 | free_token(token); | 739 | free_token(token); |
744 | return -1; | 740 | return id; |
745 | } | 741 | } |
746 | 742 | ||
747 | static int field_is_string(struct format_field *field) | 743 | static int field_is_string(struct format_field *field) |
@@ -1425,6 +1421,11 @@ static long long arg_num_eval(struct print_arg *arg) | |||
1425 | die("unknown op '%s'", arg->op.op); | 1421 | die("unknown op '%s'", arg->op.op); |
1426 | } | 1422 | } |
1427 | break; | 1423 | break; |
1424 | case '+': | ||
1425 | left = arg_num_eval(arg->op.left); | ||
1426 | right = arg_num_eval(arg->op.right); | ||
1427 | val = left + right; | ||
1428 | break; | ||
1428 | default: | 1429 | default: |
1429 | die("unknown op '%s'", arg->op.op); | 1430 | die("unknown op '%s'", arg->op.op); |
1430 | } | 1431 | } |
@@ -1485,6 +1486,13 @@ process_fields(struct event *event, struct print_flag_sym **list, char **tok) | |||
1485 | 1486 | ||
1486 | free_token(token); | 1487 | free_token(token); |
1487 | type = process_arg(event, arg, &token); | 1488 | type = process_arg(event, arg, &token); |
1489 | |||
1490 | if (type == EVENT_OP) | ||
1491 | type = process_op(event, arg, &token); | ||
1492 | |||
1493 | if (type == EVENT_ERROR) | ||
1494 | goto out_free; | ||
1495 | |||
1488 | if (test_type_token(type, token, EVENT_DELIM, ",")) | 1496 | if (test_type_token(type, token, EVENT_DELIM, ",")) |
1489 | goto out_free; | 1497 | goto out_free; |
1490 | 1498 | ||
diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index f55cc3a765a1..b9592e0de8d7 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c | |||
@@ -33,7 +33,6 @@ | |||
33 | #include <pthread.h> | 33 | #include <pthread.h> |
34 | #include <fcntl.h> | 34 | #include <fcntl.h> |
35 | #include <unistd.h> | 35 | #include <unistd.h> |
36 | #include <ctype.h> | ||
37 | #include <errno.h> | 36 | #include <errno.h> |
38 | 37 | ||
39 | #include "../perf.h" | 38 | #include "../perf.h" |
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index a3fdf55f317b..18ae6c1831d3 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c | |||
@@ -22,7 +22,6 @@ | |||
22 | #include <stdio.h> | 22 | #include <stdio.h> |
23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
24 | #include <string.h> | 24 | #include <string.h> |
25 | #include <ctype.h> | ||
26 | #include <errno.h> | 25 | #include <errno.h> |
27 | 26 | ||
28 | #include "../perf.h" | 27 | #include "../perf.h" |
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h index 84d761b730c1..6ee82f60feaf 100644 --- a/tools/perf/util/ui/browser.h +++ b/tools/perf/util/ui/browser.h | |||
@@ -49,6 +49,8 @@ int ui_browser__warning(struct ui_browser *browser, int timeout, | |||
49 | const char *format, ...); | 49 | const char *format, ...); |
50 | int ui_browser__help_window(struct ui_browser *browser, const char *text); | 50 | int ui_browser__help_window(struct ui_browser *browser, const char *text); |
51 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); | 51 | bool ui_browser__dialog_yesno(struct ui_browser *browser, const char *text); |
52 | int ui_browser__input_window(const char *title, const char *text, char *input, | ||
53 | const char *exit_msg, int delay_sec); | ||
52 | 54 | ||
53 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); | 55 | void ui_browser__argv_seek(struct ui_browser *browser, off_t offset, int whence); |
54 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser); | 56 | unsigned int ui_browser__argv_refresh(struct ui_browser *browser); |
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c index 295a9c93f945..57a4c6ef3fd2 100644 --- a/tools/perf/util/ui/browsers/annotate.c +++ b/tools/perf/util/ui/browsers/annotate.c | |||
@@ -69,14 +69,17 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro | |||
69 | if (!self->navkeypressed) | 69 | if (!self->navkeypressed) |
70 | width += 1; | 70 | width += 1; |
71 | 71 | ||
72 | if (!ab->hide_src_code && ol->offset != -1) | ||
73 | if (!current_entry || (self->use_navkeypressed && | ||
74 | !self->navkeypressed)) | ||
75 | ui_browser__set_color(self, HE_COLORSET_CODE); | ||
76 | |||
72 | if (!*ol->line) | 77 | if (!*ol->line) |
73 | slsmg_write_nstring(" ", width - 18); | 78 | slsmg_write_nstring(" ", width - 18); |
74 | else | 79 | else |
75 | slsmg_write_nstring(ol->line, width - 18); | 80 | slsmg_write_nstring(ol->line, width - 18); |
76 | 81 | ||
77 | if (!current_entry) | 82 | if (current_entry) |
78 | ui_browser__set_color(self, HE_COLORSET_CODE); | ||
79 | else | ||
80 | ab->selection = ol; | 83 | ab->selection = ol; |
81 | } | 84 | } |
82 | 85 | ||
@@ -230,9 +233,9 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
230 | struct rb_node *nd = NULL; | 233 | struct rb_node *nd = NULL; |
231 | struct map_symbol *ms = self->b.priv; | 234 | struct map_symbol *ms = self->b.priv; |
232 | struct symbol *sym = ms->sym; | 235 | struct symbol *sym = ms->sym; |
233 | const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, " | 236 | const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, " |
234 | "H: Hottest, -> Line action, S -> Toggle source " | 237 | "H: Go to hottest line, ->/ENTER: Line action, " |
235 | "code view"; | 238 | "S: Toggle source code view"; |
236 | int key; | 239 | int key; |
237 | 240 | ||
238 | if (ui_browser__show(&self->b, sym->name, help) < 0) | 241 | if (ui_browser__show(&self->b, sym->name, help) < 0) |
@@ -284,9 +287,11 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
284 | nd = self->curr_hot; | 287 | nd = self->curr_hot; |
285 | break; | 288 | break; |
286 | case 'H': | 289 | case 'H': |
290 | case 'h': | ||
287 | nd = self->curr_hot; | 291 | nd = self->curr_hot; |
288 | break; | 292 | break; |
289 | case 'S': | 293 | case 'S': |
294 | case 's': | ||
290 | if (annotate_browser__toggle_source(self)) | 295 | if (annotate_browser__toggle_source(self)) |
291 | ui_helpline__puts(help); | 296 | ui_helpline__puts(help); |
292 | continue; | 297 | continue; |
@@ -338,6 +343,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx, | |||
338 | pthread_mutex_unlock(¬es->lock); | 343 | pthread_mutex_unlock(¬es->lock); |
339 | symbol__tui_annotate(target, ms->map, evidx, | 344 | symbol__tui_annotate(target, ms->map, evidx, |
340 | timer, arg, delay_secs); | 345 | timer, arg, delay_secs); |
346 | ui_browser__show_title(&self->b, sym->name); | ||
341 | } | 347 | } |
342 | continue; | 348 | continue; |
343 | case K_LEFT: | 349 | case K_LEFT: |
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c index 1212a386a033..d7a1c4afe28b 100644 --- a/tools/perf/util/ui/browsers/hists.c +++ b/tools/perf/util/ui/browsers/hists.c | |||
@@ -1,6 +1,4 @@ | |||
1 | #define _GNU_SOURCE | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #undef _GNU_SOURCE | ||
4 | #include "../libslang.h" | 2 | #include "../libslang.h" |
5 | #include <stdlib.h> | 3 | #include <stdlib.h> |
6 | #include <string.h> | 4 | #include <string.h> |
@@ -807,8 +805,11 @@ static struct hist_browser *hist_browser__new(struct hists *hists) | |||
807 | self->hists = hists; | 805 | self->hists = hists; |
808 | self->b.refresh = hist_browser__refresh; | 806 | self->b.refresh = hist_browser__refresh; |
809 | self->b.seek = ui_browser__hists_seek; | 807 | self->b.seek = ui_browser__hists_seek; |
810 | self->b.use_navkeypressed = true, | 808 | self->b.use_navkeypressed = true; |
811 | self->has_symbols = sort_sym.list.next != NULL; | 809 | if (sort__branch_mode == 1) |
810 | self->has_symbols = sort_sym_from.list.next != NULL; | ||
811 | else | ||
812 | self->has_symbols = sort_sym.list.next != NULL; | ||
812 | } | 813 | } |
813 | 814 | ||
814 | return self; | 815 | return self; |
@@ -839,19 +840,32 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size, | |||
839 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; | 840 | unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; |
840 | 841 | ||
841 | nr_events = convert_unit(nr_events, &unit); | 842 | nr_events = convert_unit(nr_events, &unit); |
842 | printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name); | 843 | printed = scnprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name); |
843 | 844 | ||
844 | if (thread) | 845 | if (self->uid_filter_str) |
845 | printed += snprintf(bf + printed, size - printed, | 846 | printed += snprintf(bf + printed, size - printed, |
847 | ", UID: %s", self->uid_filter_str); | ||
848 | if (thread) | ||
849 | printed += scnprintf(bf + printed, size - printed, | ||
846 | ", Thread: %s(%d)", | 850 | ", Thread: %s(%d)", |
847 | (thread->comm_set ? thread->comm : ""), | 851 | (thread->comm_set ? thread->comm : ""), |
848 | thread->pid); | 852 | thread->pid); |
849 | if (dso) | 853 | if (dso) |
850 | printed += snprintf(bf + printed, size - printed, | 854 | printed += scnprintf(bf + printed, size - printed, |
851 | ", DSO: %s", dso->short_name); | 855 | ", DSO: %s", dso->short_name); |
852 | return printed; | 856 | return printed; |
853 | } | 857 | } |
854 | 858 | ||
859 | static inline void free_popup_options(char **options, int n) | ||
860 | { | ||
861 | int i; | ||
862 | |||
863 | for (i = 0; i < n; ++i) { | ||
864 | free(options[i]); | ||
865 | options[i] = NULL; | ||
866 | } | ||
867 | } | ||
868 | |||
855 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | 869 | static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, |
856 | const char *helpline, const char *ev_name, | 870 | const char *helpline, const char *ev_name, |
857 | bool left_exits, | 871 | bool left_exits, |
@@ -860,8 +874,12 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
860 | { | 874 | { |
861 | struct hists *self = &evsel->hists; | 875 | struct hists *self = &evsel->hists; |
862 | struct hist_browser *browser = hist_browser__new(self); | 876 | struct hist_browser *browser = hist_browser__new(self); |
877 | struct branch_info *bi; | ||
863 | struct pstack *fstack; | 878 | struct pstack *fstack; |
879 | char *options[16]; | ||
880 | int nr_options = 0; | ||
864 | int key = -1; | 881 | int key = -1; |
882 | char buf[64]; | ||
865 | 883 | ||
866 | if (browser == NULL) | 884 | if (browser == NULL) |
867 | return -1; | 885 | return -1; |
@@ -872,13 +890,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
872 | 890 | ||
873 | ui_helpline__push(helpline); | 891 | ui_helpline__push(helpline); |
874 | 892 | ||
893 | memset(options, 0, sizeof(options)); | ||
894 | |||
875 | while (1) { | 895 | while (1) { |
876 | const struct thread *thread = NULL; | 896 | const struct thread *thread = NULL; |
877 | const struct dso *dso = NULL; | 897 | const struct dso *dso = NULL; |
878 | char *options[16]; | 898 | int choice = 0, |
879 | int nr_options = 0, choice = 0, i, | ||
880 | annotate = -2, zoom_dso = -2, zoom_thread = -2, | 899 | annotate = -2, zoom_dso = -2, zoom_thread = -2, |
881 | browse_map = -2; | 900 | annotate_f = -2, annotate_t = -2, browse_map = -2; |
901 | |||
902 | nr_options = 0; | ||
882 | 903 | ||
883 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); | 904 | key = hist_browser__run(browser, ev_name, timer, arg, delay_secs); |
884 | 905 | ||
@@ -886,7 +907,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
886 | thread = hist_browser__selected_thread(browser); | 907 | thread = hist_browser__selected_thread(browser); |
887 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | 908 | dso = browser->selection->map ? browser->selection->map->dso : NULL; |
888 | } | 909 | } |
889 | |||
890 | switch (key) { | 910 | switch (key) { |
891 | case K_TAB: | 911 | case K_TAB: |
892 | case K_UNTAB: | 912 | case K_UNTAB: |
@@ -901,7 +921,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
901 | if (!browser->has_symbols) { | 921 | if (!browser->has_symbols) { |
902 | ui_browser__warning(&browser->b, delay_secs * 2, | 922 | ui_browser__warning(&browser->b, delay_secs * 2, |
903 | "Annotation is only available for symbolic views, " | 923 | "Annotation is only available for symbolic views, " |
904 | "include \"sym\" in --sort to use it."); | 924 | "include \"sym*\" in --sort to use it."); |
905 | continue; | 925 | continue; |
906 | } | 926 | } |
907 | 927 | ||
@@ -914,6 +934,16 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
914 | goto zoom_dso; | 934 | goto zoom_dso; |
915 | case 't': | 935 | case 't': |
916 | goto zoom_thread; | 936 | goto zoom_thread; |
937 | case 's': | ||
938 | if (ui_browser__input_window("Symbol to show", | ||
939 | "Please enter the name of symbol you want to see", | ||
940 | buf, "ENTER: OK, ESC: Cancel", | ||
941 | delay_secs * 2) == K_ENTER) { | ||
942 | self->symbol_filter_str = *buf ? buf : NULL; | ||
943 | hists__filter_by_symbol(self); | ||
944 | hist_browser__reset(browser); | ||
945 | } | ||
946 | continue; | ||
917 | case K_F1: | 947 | case K_F1: |
918 | case 'h': | 948 | case 'h': |
919 | case '?': | 949 | case '?': |
@@ -931,7 +961,8 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
931 | "C Collapse all callchains\n" | 961 | "C Collapse all callchains\n" |
932 | "E Expand all callchains\n" | 962 | "E Expand all callchains\n" |
933 | "d Zoom into current DSO\n" | 963 | "d Zoom into current DSO\n" |
934 | "t Zoom into current Thread"); | 964 | "t Zoom into current Thread\n" |
965 | "s Filter symbol by name"); | ||
935 | continue; | 966 | continue; |
936 | case K_ENTER: | 967 | case K_ENTER: |
937 | case K_RIGHT: | 968 | case K_RIGHT: |
@@ -971,12 +1002,34 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
971 | if (!browser->has_symbols) | 1002 | if (!browser->has_symbols) |
972 | goto add_exit_option; | 1003 | goto add_exit_option; |
973 | 1004 | ||
974 | if (browser->selection != NULL && | 1005 | if (sort__branch_mode == 1) { |
975 | browser->selection->sym != NULL && | 1006 | bi = browser->he_selection->branch_info; |
976 | !browser->selection->map->dso->annotate_warned && | 1007 | if (browser->selection != NULL && |
977 | asprintf(&options[nr_options], "Annotate %s", | 1008 | bi && |
978 | browser->selection->sym->name) > 0) | 1009 | bi->from.sym != NULL && |
979 | annotate = nr_options++; | 1010 | !bi->from.map->dso->annotate_warned && |
1011 | asprintf(&options[nr_options], "Annotate %s", | ||
1012 | bi->from.sym->name) > 0) | ||
1013 | annotate_f = nr_options++; | ||
1014 | |||
1015 | if (browser->selection != NULL && | ||
1016 | bi && | ||
1017 | bi->to.sym != NULL && | ||
1018 | !bi->to.map->dso->annotate_warned && | ||
1019 | (bi->to.sym != bi->from.sym || | ||
1020 | bi->to.map->dso != bi->from.map->dso) && | ||
1021 | asprintf(&options[nr_options], "Annotate %s", | ||
1022 | bi->to.sym->name) > 0) | ||
1023 | annotate_t = nr_options++; | ||
1024 | } else { | ||
1025 | |||
1026 | if (browser->selection != NULL && | ||
1027 | browser->selection->sym != NULL && | ||
1028 | !browser->selection->map->dso->annotate_warned && | ||
1029 | asprintf(&options[nr_options], "Annotate %s", | ||
1030 | browser->selection->sym->name) > 0) | ||
1031 | annotate = nr_options++; | ||
1032 | } | ||
980 | 1033 | ||
981 | if (thread != NULL && | 1034 | if (thread != NULL && |
982 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", | 1035 | asprintf(&options[nr_options], "Zoom %s %s(%d) thread", |
@@ -997,25 +1050,39 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events, | |||
997 | browse_map = nr_options++; | 1050 | browse_map = nr_options++; |
998 | add_exit_option: | 1051 | add_exit_option: |
999 | options[nr_options++] = (char *)"Exit"; | 1052 | options[nr_options++] = (char *)"Exit"; |
1000 | 1053 | retry_popup_menu: | |
1001 | choice = ui__popup_menu(nr_options, options); | 1054 | choice = ui__popup_menu(nr_options, options); |
1002 | 1055 | ||
1003 | for (i = 0; i < nr_options - 1; ++i) | ||
1004 | free(options[i]); | ||
1005 | |||
1006 | if (choice == nr_options - 1) | 1056 | if (choice == nr_options - 1) |
1007 | break; | 1057 | break; |
1008 | 1058 | ||
1009 | if (choice == -1) | 1059 | if (choice == -1) { |
1060 | free_popup_options(options, nr_options - 1); | ||
1010 | continue; | 1061 | continue; |
1062 | } | ||
1011 | 1063 | ||
1012 | if (choice == annotate) { | 1064 | if (choice == annotate || choice == annotate_t || choice == annotate_f) { |
1013 | struct hist_entry *he; | 1065 | struct hist_entry *he; |
1014 | int err; | 1066 | int err; |
1015 | do_annotate: | 1067 | do_annotate: |
1016 | he = hist_browser__selected_entry(browser); | 1068 | he = hist_browser__selected_entry(browser); |
1017 | if (he == NULL) | 1069 | if (he == NULL) |
1018 | continue; | 1070 | continue; |
1071 | |||
1072 | /* | ||
1073 | * we stash the branch_info symbol + map into the | ||
1074 | * the ms so we don't have to rewrite all the annotation | ||
1075 | * code to use branch_info. | ||
1076 | * in branch mode, the ms struct is not used | ||
1077 | */ | ||
1078 | if (choice == annotate_f) { | ||
1079 | he->ms.sym = he->branch_info->from.sym; | ||
1080 | he->ms.map = he->branch_info->from.map; | ||
1081 | } else if (choice == annotate_t) { | ||
1082 | he->ms.sym = he->branch_info->to.sym; | ||
1083 | he->ms.map = he->branch_info->to.map; | ||
1084 | } | ||
1085 | |||
1019 | /* | 1086 | /* |
1020 | * Don't let this be freed, say, by hists__decay_entry. | 1087 | * Don't let this be freed, say, by hists__decay_entry. |
1021 | */ | 1088 | */ |
@@ -1023,9 +1090,18 @@ do_annotate: | |||
1023 | err = hist_entry__tui_annotate(he, evsel->idx, | 1090 | err = hist_entry__tui_annotate(he, evsel->idx, |
1024 | timer, arg, delay_secs); | 1091 | timer, arg, delay_secs); |
1025 | he->used = false; | 1092 | he->used = false; |
1093 | /* | ||
1094 | * offer option to annotate the other branch source or target | ||
1095 | * (if they exists) when returning from annotate | ||
1096 | */ | ||
1097 | if ((err == 'q' || err == CTRL('c')) | ||
1098 | && annotate_t != -2 && annotate_f != -2) | ||
1099 | goto retry_popup_menu; | ||
1100 | |||
1026 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); | 1101 | ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries); |
1027 | if (err) | 1102 | if (err) |
1028 | ui_browser__handle_resize(&browser->b); | 1103 | ui_browser__handle_resize(&browser->b); |
1104 | |||
1029 | } else if (choice == browse_map) | 1105 | } else if (choice == browse_map) |
1030 | map__browse(browser->selection->map); | 1106 | map__browse(browser->selection->map); |
1031 | else if (choice == zoom_dso) { | 1107 | else if (choice == zoom_dso) { |
@@ -1071,6 +1147,7 @@ out_free_stack: | |||
1071 | pstack__delete(fstack); | 1147 | pstack__delete(fstack); |
1072 | out: | 1148 | out: |
1073 | hist_browser__delete(browser); | 1149 | hist_browser__delete(browser); |
1150 | free_popup_options(options, nr_options - 1); | ||
1074 | return key; | 1151 | return key; |
1075 | } | 1152 | } |
1076 | 1153 | ||
@@ -1097,7 +1174,7 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1097 | HE_COLORSET_NORMAL); | 1174 | HE_COLORSET_NORMAL); |
1098 | 1175 | ||
1099 | nr_events = convert_unit(nr_events, &unit); | 1176 | nr_events = convert_unit(nr_events, &unit); |
1100 | printed = snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, | 1177 | printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, |
1101 | unit, unit == ' ' ? "" : " ", ev_name); | 1178 | unit, unit == ' ' ? "" : " ", ev_name); |
1102 | slsmg_printf("%s", bf); | 1179 | slsmg_printf("%s", bf); |
1103 | 1180 | ||
@@ -1107,8 +1184,8 @@ static void perf_evsel_menu__write(struct ui_browser *browser, | |||
1107 | if (!current_entry) | 1184 | if (!current_entry) |
1108 | ui_browser__set_color(browser, HE_COLORSET_TOP); | 1185 | ui_browser__set_color(browser, HE_COLORSET_TOP); |
1109 | nr_events = convert_unit(nr_events, &unit); | 1186 | nr_events = convert_unit(nr_events, &unit); |
1110 | snprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", nr_events, | 1187 | printed += scnprintf(bf, sizeof(bf), ": %ld%c%schunks LOST!", |
1111 | unit, unit == ' ' ? "" : " "); | 1188 | nr_events, unit, unit == ' ' ? "" : " "); |
1112 | warn = bf; | 1189 | warn = bf; |
1113 | } | 1190 | } |
1114 | 1191 | ||
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c index 6905bcc8be2d..eca6575abfd0 100644 --- a/tools/perf/util/ui/browsers/map.c +++ b/tools/perf/util/ui/browsers/map.c | |||
@@ -3,9 +3,9 @@ | |||
3 | #include <newt.h> | 3 | #include <newt.h> |
4 | #include <inttypes.h> | 4 | #include <inttypes.h> |
5 | #include <sys/ttydefaults.h> | 5 | #include <sys/ttydefaults.h> |
6 | #include <ctype.h> | ||
7 | #include <string.h> | 6 | #include <string.h> |
8 | #include <linux/bitops.h> | 7 | #include <linux/bitops.h> |
8 | #include "../../util.h" | ||
9 | #include "../../debug.h" | 9 | #include "../../debug.h" |
10 | #include "../../symbol.h" | 10 | #include "../../symbol.h" |
11 | #include "../browser.h" | 11 | #include "../browser.h" |
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c index 6ef3c5691762..2f950c2641c8 100644 --- a/tools/perf/util/ui/helpline.c +++ b/tools/perf/util/ui/helpline.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #define _GNU_SOURCE | ||
2 | #include <stdio.h> | 1 | #include <stdio.h> |
3 | #include <stdlib.h> | 2 | #include <stdlib.h> |
4 | #include <string.h> | 3 | #include <string.h> |
@@ -65,7 +64,7 @@ int ui_helpline__show_help(const char *format, va_list ap) | |||
65 | static int backlog; | 64 | static int backlog; |
66 | 65 | ||
67 | pthread_mutex_lock(&ui__lock); | 66 | pthread_mutex_lock(&ui__lock); |
68 | ret = vsnprintf(ui_helpline__last_msg + backlog, | 67 | ret = vscnprintf(ui_helpline__last_msg + backlog, |
69 | sizeof(ui_helpline__last_msg) - backlog, format, ap); | 68 | sizeof(ui_helpline__last_msg) - backlog, format, ap); |
70 | backlog += ret; | 69 | backlog += ret; |
71 | 70 | ||
diff --git a/tools/perf/util/ui/keysyms.h b/tools/perf/util/ui/keysyms.h index 3458b1985761..809eca5707fa 100644 --- a/tools/perf/util/ui/keysyms.h +++ b/tools/perf/util/ui/keysyms.h | |||
@@ -16,6 +16,8 @@ | |||
16 | #define K_TAB '\t' | 16 | #define K_TAB '\t' |
17 | #define K_UNTAB SL_KEY_UNTAB | 17 | #define K_UNTAB SL_KEY_UNTAB |
18 | #define K_UP SL_KEY_UP | 18 | #define K_UP SL_KEY_UP |
19 | #define K_BKSPC 0x7f | ||
20 | #define K_DEL SL_KEY_DELETE | ||
19 | 21 | ||
20 | /* Not really keys */ | 22 | /* Not really keys */ |
21 | #define K_TIMER -1 | 23 | #define K_TIMER -1 |
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c index 45daa7c41dad..ad4374a16bb0 100644 --- a/tools/perf/util/ui/util.c +++ b/tools/perf/util/ui/util.c | |||
@@ -69,6 +69,88 @@ int ui__popup_menu(int argc, char * const argv[]) | |||
69 | return popup_menu__run(&menu); | 69 | return popup_menu__run(&menu); |
70 | } | 70 | } |
71 | 71 | ||
72 | int ui_browser__input_window(const char *title, const char *text, char *input, | ||
73 | const char *exit_msg, int delay_secs) | ||
74 | { | ||
75 | int x, y, len, key; | ||
76 | int max_len = 60, nr_lines = 0; | ||
77 | static char buf[50]; | ||
78 | const char *t; | ||
79 | |||
80 | t = text; | ||
81 | while (1) { | ||
82 | const char *sep = strchr(t, '\n'); | ||
83 | |||
84 | if (sep == NULL) | ||
85 | sep = strchr(t, '\0'); | ||
86 | len = sep - t; | ||
87 | if (max_len < len) | ||
88 | max_len = len; | ||
89 | ++nr_lines; | ||
90 | if (*sep == '\0') | ||
91 | break; | ||
92 | t = sep + 1; | ||
93 | } | ||
94 | |||
95 | max_len += 2; | ||
96 | nr_lines += 8; | ||
97 | y = SLtt_Screen_Rows / 2 - nr_lines / 2; | ||
98 | x = SLtt_Screen_Cols / 2 - max_len / 2; | ||
99 | |||
100 | SLsmg_set_color(0); | ||
101 | SLsmg_draw_box(y, x++, nr_lines, max_len); | ||
102 | if (title) { | ||
103 | SLsmg_gotorc(y, x + 1); | ||
104 | SLsmg_write_string((char *)title); | ||
105 | } | ||
106 | SLsmg_gotorc(++y, x); | ||
107 | nr_lines -= 7; | ||
108 | max_len -= 2; | ||
109 | SLsmg_write_wrapped_string((unsigned char *)text, y, x, | ||
110 | nr_lines, max_len, 1); | ||
111 | y += nr_lines; | ||
112 | len = 5; | ||
113 | while (len--) { | ||
114 | SLsmg_gotorc(y + len - 1, x); | ||
115 | SLsmg_write_nstring((char *)" ", max_len); | ||
116 | } | ||
117 | SLsmg_draw_box(y++, x + 1, 3, max_len - 2); | ||
118 | |||
119 | SLsmg_gotorc(y + 3, x); | ||
120 | SLsmg_write_nstring((char *)exit_msg, max_len); | ||
121 | SLsmg_refresh(); | ||
122 | |||
123 | x += 2; | ||
124 | len = 0; | ||
125 | key = ui__getch(delay_secs); | ||
126 | while (key != K_TIMER && key != K_ENTER && key != K_ESC) { | ||
127 | if (key == K_BKSPC) { | ||
128 | if (len == 0) | ||
129 | goto next_key; | ||
130 | SLsmg_gotorc(y, x + --len); | ||
131 | SLsmg_write_char(' '); | ||
132 | } else { | ||
133 | buf[len] = key; | ||
134 | SLsmg_gotorc(y, x + len++); | ||
135 | SLsmg_write_char(key); | ||
136 | } | ||
137 | SLsmg_refresh(); | ||
138 | |||
139 | /* XXX more graceful overflow handling needed */ | ||
140 | if (len == sizeof(buf) - 1) { | ||
141 | ui_helpline__push("maximum size of symbol name reached!"); | ||
142 | key = K_ENTER; | ||
143 | break; | ||
144 | } | ||
145 | next_key: | ||
146 | key = ui__getch(delay_secs); | ||
147 | } | ||
148 | |||
149 | buf[len] = '\0'; | ||
150 | strncpy(input, buf, len+1); | ||
151 | return key; | ||
152 | } | ||
153 | |||
72 | int ui__question_window(const char *title, const char *text, | 154 | int ui__question_window(const char *title, const char *text, |
73 | const char *exit_msg, int delay_secs) | 155 | const char *exit_msg, int delay_secs) |
74 | { | 156 | { |
diff --git a/tools/perf/util/usage.c b/tools/perf/util/usage.c index d76d1c0ff98f..52bb07c6442a 100644 --- a/tools/perf/util/usage.c +++ b/tools/perf/util/usage.c | |||
@@ -7,6 +7,7 @@ | |||
7 | * Copyright (C) Linus Torvalds, 2005 | 7 | * Copyright (C) Linus Torvalds, 2005 |
8 | */ | 8 | */ |
9 | #include "util.h" | 9 | #include "util.h" |
10 | #include "debug.h" | ||
10 | 11 | ||
11 | static void report(const char *prefix, const char *err, va_list params) | 12 | static void report(const char *prefix, const char *err, va_list params) |
12 | { | 13 | { |
@@ -81,3 +82,41 @@ void warning(const char *warn, ...) | |||
81 | warn_routine(warn, params); | 82 | warn_routine(warn, params); |
82 | va_end(params); | 83 | va_end(params); |
83 | } | 84 | } |
85 | |||
86 | uid_t parse_target_uid(const char *str, const char *tid, const char *pid) | ||
87 | { | ||
88 | struct passwd pwd, *result; | ||
89 | char buf[1024]; | ||
90 | |||
91 | if (str == NULL) | ||
92 | return UINT_MAX; | ||
93 | |||
94 | /* UID and PID are mutually exclusive */ | ||
95 | if (tid || pid) { | ||
96 | ui__warning("PID/TID switch overriding UID\n"); | ||
97 | sleep(1); | ||
98 | return UINT_MAX; | ||
99 | } | ||
100 | |||
101 | getpwnam_r(str, &pwd, buf, sizeof(buf), &result); | ||
102 | |||
103 | if (result == NULL) { | ||
104 | char *endptr; | ||
105 | int uid = strtol(str, &endptr, 10); | ||
106 | |||
107 | if (*endptr != '\0') { | ||
108 | ui__error("Invalid user %s\n", str); | ||
109 | return UINT_MAX - 1; | ||
110 | } | ||
111 | |||
112 | getpwuid_r(uid, &pwd, buf, sizeof(buf), &result); | ||
113 | |||
114 | if (result == NULL) { | ||
115 | ui__error("Problems obtaining information for user %s\n", | ||
116 | str); | ||
117 | return UINT_MAX - 1; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | return result->pw_uid; | ||
122 | } | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 5b3ea49aa63e..8109a907841e 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -1,6 +1,23 @@ | |||
1 | #include "../perf.h" | ||
1 | #include "util.h" | 2 | #include "util.h" |
2 | #include <sys/mman.h> | 3 | #include <sys/mman.h> |
3 | 4 | ||
5 | /* | ||
6 | * XXX We need to find a better place for these things... | ||
7 | */ | ||
8 | bool perf_host = true; | ||
9 | bool perf_guest = false; | ||
10 | |||
11 | void event_attr_init(struct perf_event_attr *attr) | ||
12 | { | ||
13 | if (!perf_host) | ||
14 | attr->exclude_host = 1; | ||
15 | if (!perf_guest) | ||
16 | attr->exclude_guest = 1; | ||
17 | /* to capture ABI version */ | ||
18 | attr->size = sizeof(*attr); | ||
19 | } | ||
20 | |||
4 | int mkdir_p(char *path, mode_t mode) | 21 | int mkdir_p(char *path, mode_t mode) |
5 | { | 22 | { |
6 | struct stat st; | 23 | struct stat st; |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 37be34dff798..0f99f394d8e0 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -40,7 +40,6 @@ | |||
40 | #define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) | 40 | #define decimal_length(x) ((int)(sizeof(x) * 2.56 + 0.5) + 1) |
41 | 41 | ||
42 | #define _ALL_SOURCE 1 | 42 | #define _ALL_SOURCE 1 |
43 | #define _GNU_SOURCE 1 | ||
44 | #define _BSD_SOURCE 1 | 43 | #define _BSD_SOURCE 1 |
45 | #define HAS_BOOL | 44 | #define HAS_BOOL |
46 | 45 | ||
@@ -200,6 +199,8 @@ static inline int has_extension(const char *filename, const char *ext) | |||
200 | #undef isalpha | 199 | #undef isalpha |
201 | #undef isprint | 200 | #undef isprint |
202 | #undef isalnum | 201 | #undef isalnum |
202 | #undef islower | ||
203 | #undef isupper | ||
203 | #undef tolower | 204 | #undef tolower |
204 | #undef toupper | 205 | #undef toupper |
205 | 206 | ||
@@ -220,6 +221,8 @@ extern unsigned char sane_ctype[256]; | |||
220 | #define isalpha(x) sane_istest(x,GIT_ALPHA) | 221 | #define isalpha(x) sane_istest(x,GIT_ALPHA) |
221 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) | 222 | #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT) |
222 | #define isprint(x) sane_istest(x,GIT_PRINT) | 223 | #define isprint(x) sane_istest(x,GIT_PRINT) |
224 | #define islower(x) (sane_istest(x,GIT_ALPHA) && sane_istest(x,0x20)) | ||
225 | #define isupper(x) (sane_istest(x,GIT_ALPHA) && !sane_istest(x,0x20)) | ||
223 | #define tolower(x) sane_case((unsigned char)(x), 0x20) | 226 | #define tolower(x) sane_case((unsigned char)(x), 0x20) |
224 | #define toupper(x) sane_case((unsigned char)(x), 0) | 227 | #define toupper(x) sane_case((unsigned char)(x), 0) |
225 | 228 | ||
@@ -242,6 +245,12 @@ int strtailcmp(const char *s1, const char *s2); | |||
242 | unsigned long convert_unit(unsigned long value, char *unit); | 245 | unsigned long convert_unit(unsigned long value, char *unit); |
243 | int readn(int fd, void *buf, size_t size); | 246 | int readn(int fd, void *buf, size_t size); |
244 | 247 | ||
248 | struct perf_event_attr; | ||
249 | |||
250 | void event_attr_init(struct perf_event_attr *attr); | ||
251 | |||
252 | uid_t parse_target_uid(const char *str, const char *tid, const char *pid); | ||
253 | |||
245 | #define _STR(x) #x | 254 | #define _STR(x) #x |
246 | #define STR(x) _STR(x) | 255 | #define STR(x) _STR(x) |
247 | 256 | ||
diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index e8a03aceceb1..a93e06cfcc2a 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile | |||
@@ -19,6 +19,16 @@ | |||
19 | # along with this program; if not, write to the Free Software | 19 | # along with this program; if not, write to the Free Software |
20 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 20 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 | # | 21 | # |
22 | OUTPUT=./ | ||
23 | ifeq ("$(origin O)", "command line") | ||
24 | OUTPUT := $(O)/ | ||
25 | endif | ||
26 | |||
27 | ifneq ($(OUTPUT),) | ||
28 | # check that the output directory actually exists | ||
29 | OUTDIR := $(shell cd $(OUTPUT) && /bin/pwd) | ||
30 | $(if $(OUTDIR),, $(error output directory "$(OUTPUT)" does not exist)) | ||
31 | endif | ||
22 | 32 | ||
23 | # --- CONFIGURATION BEGIN --- | 33 | # --- CONFIGURATION BEGIN --- |
24 | 34 | ||
@@ -87,6 +97,7 @@ AR = $(CROSS)ar | |||
87 | STRIP = $(CROSS)strip | 97 | STRIP = $(CROSS)strip |
88 | RANLIB = $(CROSS)ranlib | 98 | RANLIB = $(CROSS)ranlib |
89 | HOSTCC = gcc | 99 | HOSTCC = gcc |
100 | MKDIR = mkdir | ||
90 | 101 | ||
91 | 102 | ||
92 | # Now we set up the build system | 103 | # Now we set up the build system |
@@ -95,7 +106,7 @@ HOSTCC = gcc | |||
95 | # set up PWD so that older versions of make will work with our build. | 106 | # set up PWD so that older versions of make will work with our build. |
96 | PWD = $(shell pwd) | 107 | PWD = $(shell pwd) |
97 | 108 | ||
98 | GMO_FILES = ${shell for HLANG in ${LANGUAGES}; do echo po/$$HLANG.gmo; done;} | 109 | GMO_FILES = ${shell for HLANG in ${LANGUAGES}; do echo $(OUTPUT)po/$$HLANG.gmo; done;} |
99 | 110 | ||
100 | export CROSS CC AR STRIP RANLIB CFLAGS LDFLAGS LIB_OBJS | 111 | export CROSS CC AR STRIP RANLIB CFLAGS LDFLAGS LIB_OBJS |
101 | 112 | ||
@@ -122,15 +133,18 @@ UTIL_OBJS = utils/helpers/amd.o utils/helpers/topology.o utils/helpers/msr.o \ | |||
122 | utils/cpupower.o utils/cpufreq-info.o utils/cpufreq-set.o \ | 133 | utils/cpupower.o utils/cpufreq-info.o utils/cpufreq-set.o \ |
123 | utils/cpupower-set.o utils/cpupower-info.o utils/cpuidle-info.o | 134 | utils/cpupower-set.o utils/cpupower-info.o utils/cpuidle-info.o |
124 | 135 | ||
136 | UTIL_SRC := $(UTIL_OBJS:.o=.c) | ||
137 | |||
138 | UTIL_OBJS := $(addprefix $(OUTPUT),$(UTIL_OBJS)) | ||
139 | |||
125 | UTIL_HEADERS = utils/helpers/helpers.h utils/idle_monitor/cpupower-monitor.h \ | 140 | UTIL_HEADERS = utils/helpers/helpers.h utils/idle_monitor/cpupower-monitor.h \ |
126 | utils/helpers/bitmask.h \ | 141 | utils/helpers/bitmask.h \ |
127 | utils/idle_monitor/idle_monitors.h utils/idle_monitor/idle_monitors.def | 142 | utils/idle_monitor/idle_monitors.h utils/idle_monitor/idle_monitors.def |
128 | 143 | ||
129 | UTIL_SRC := $(UTIL_OBJS:.o=.c) | ||
130 | |||
131 | LIB_HEADERS = lib/cpufreq.h lib/sysfs.h | 144 | LIB_HEADERS = lib/cpufreq.h lib/sysfs.h |
132 | LIB_SRC = lib/cpufreq.c lib/sysfs.c | 145 | LIB_SRC = lib/cpufreq.c lib/sysfs.c |
133 | LIB_OBJS = lib/cpufreq.o lib/sysfs.o | 146 | LIB_OBJS = lib/cpufreq.o lib/sysfs.o |
147 | LIB_OBJS := $(addprefix $(OUTPUT),$(LIB_OBJS)) | ||
134 | 148 | ||
135 | CFLAGS += -pipe | 149 | CFLAGS += -pipe |
136 | 150 | ||
@@ -168,83 +182,90 @@ endif | |||
168 | 182 | ||
169 | # the actual make rules | 183 | # the actual make rules |
170 | 184 | ||
171 | all: libcpupower cpupower $(COMPILE_NLS) $(COMPILE_BENCH) | 185 | all: libcpupower $(OUTPUT)cpupower $(COMPILE_NLS) $(COMPILE_BENCH) |
172 | 186 | ||
173 | lib/%.o: $(LIB_SRC) $(LIB_HEADERS) | 187 | $(OUTPUT)lib/%.o: $(LIB_SRC) $(LIB_HEADERS) |
174 | $(ECHO) " CC " $@ | 188 | $(ECHO) " CC " $@ |
175 | $(QUIET) $(CC) $(CFLAGS) -fPIC -o $@ -c lib/$*.c | 189 | $(QUIET) $(CC) $(CFLAGS) -fPIC -o $@ -c lib/$*.c |
176 | 190 | ||
177 | libcpupower.so.$(LIB_MAJ): $(LIB_OBJS) | 191 | $(OUTPUT)libcpupower.so.$(LIB_MAJ): $(LIB_OBJS) |
178 | $(ECHO) " LD " $@ | 192 | $(ECHO) " LD " $@ |
179 | $(QUIET) $(CC) -shared $(CFLAGS) $(LDFLAGS) -o $@ \ | 193 | $(QUIET) $(CC) -shared $(CFLAGS) $(LDFLAGS) -o $@ \ |
180 | -Wl,-soname,libcpupower.so.$(LIB_MIN) $(LIB_OBJS) | 194 | -Wl,-soname,libcpupower.so.$(LIB_MIN) $(LIB_OBJS) |
181 | @ln -sf $@ libcpupower.so | 195 | @ln -sf $(@F) $(OUTPUT)libcpupower.so |
182 | @ln -sf $@ libcpupower.so.$(LIB_MIN) | 196 | @ln -sf $(@F) $(OUTPUT)libcpupower.so.$(LIB_MIN) |
183 | 197 | ||
184 | libcpupower: libcpupower.so.$(LIB_MAJ) | 198 | libcpupower: $(OUTPUT)libcpupower.so.$(LIB_MAJ) |
185 | 199 | ||
186 | # Let all .o files depend on its .c file and all headers | 200 | # Let all .o files depend on its .c file and all headers |
187 | # Might be worth to put this into utils/Makefile at some point of time | 201 | # Might be worth to put this into utils/Makefile at some point of time |
188 | $(UTIL_OBJS): $(UTIL_HEADERS) | 202 | $(UTIL_OBJS): $(UTIL_HEADERS) |
189 | 203 | ||
190 | .c.o: | 204 | $(OUTPUT)%.o: %.c |
191 | $(ECHO) " CC " $@ | 205 | $(ECHO) " CC " $@ |
192 | $(QUIET) $(CC) $(CFLAGS) -I./lib -I ./utils -o $@ -c $*.c | 206 | $(QUIET) $(CC) $(CFLAGS) -I./lib -I ./utils -o $@ -c $*.c |
193 | 207 | ||
194 | cpupower: $(UTIL_OBJS) libcpupower.so.$(LIB_MAJ) | 208 | $(OUTPUT)cpupower: $(UTIL_OBJS) $(OUTPUT)libcpupower.so.$(LIB_MAJ) |
195 | $(ECHO) " CC " $@ | 209 | $(ECHO) " CC " $@ |
196 | $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) -lcpupower -lrt -lpci -L. -o $@ $(UTIL_OBJS) | 210 | $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lcpupower -lrt -lpci -L$(OUTPUT) -o $@ |
197 | $(QUIET) $(STRIPCMD) $@ | 211 | $(QUIET) $(STRIPCMD) $@ |
198 | 212 | ||
199 | po/$(PACKAGE).pot: $(UTIL_SRC) | 213 | $(OUTPUT)po/$(PACKAGE).pot: $(UTIL_SRC) |
200 | $(ECHO) " GETTEXT " $@ | 214 | $(ECHO) " GETTEXT " $@ |
201 | $(QUIET) xgettext --default-domain=$(PACKAGE) --add-comments \ | 215 | $(QUIET) xgettext --default-domain=$(PACKAGE) --add-comments \ |
202 | --keyword=_ --keyword=N_ $(UTIL_SRC) && \ | 216 | --keyword=_ --keyword=N_ $(UTIL_SRC) -p $(@D) -o $(@F) |
203 | test -f $(PACKAGE).po && \ | ||
204 | mv -f $(PACKAGE).po po/$(PACKAGE).pot | ||
205 | 217 | ||
206 | po/%.gmo: po/%.po | 218 | $(OUTPUT)po/%.gmo: po/%.po |
207 | $(ECHO) " MSGFMT " $@ | 219 | $(ECHO) " MSGFMT " $@ |
208 | $(QUIET) msgfmt -o $@ po/$*.po | 220 | $(QUIET) msgfmt -o $@ po/$*.po |
209 | 221 | ||
210 | create-gmo: ${GMO_FILES} | 222 | create-gmo: ${GMO_FILES} |
211 | 223 | ||
212 | update-po: po/$(PACKAGE).pot | 224 | update-po: $(OUTPUT)po/$(PACKAGE).pot |
213 | $(ECHO) " MSGMRG " $@ | 225 | $(ECHO) " MSGMRG " $@ |
214 | $(QUIET) @for HLANG in $(LANGUAGES); do \ | 226 | $(QUIET) @for HLANG in $(LANGUAGES); do \ |
215 | echo -n "Updating $$HLANG "; \ | 227 | echo -n "Updating $$HLANG "; \ |
216 | if msgmerge po/$$HLANG.po po/$(PACKAGE).pot -o \ | 228 | if msgmerge po/$$HLANG.po $< -o \ |
217 | po/$$HLANG.new.po; then \ | 229 | $(OUTPUT)po/$$HLANG.new.po; then \ |
218 | mv -f po/$$HLANG.new.po po/$$HLANG.po; \ | 230 | mv -f $(OUTPUT)po/$$HLANG.new.po $(OUTPUT)po/$$HLANG.po; \ |
219 | else \ | 231 | else \ |
220 | echo "msgmerge for $$HLANG failed!"; \ | 232 | echo "msgmerge for $$HLANG failed!"; \ |
221 | rm -f po/$$HLANG.new.po; \ | 233 | rm -f $(OUTPUT)po/$$HLANG.new.po; \ |
222 | fi; \ | 234 | fi; \ |
223 | done; | 235 | done; |
224 | 236 | ||
225 | compile-bench: libcpupower.so.$(LIB_MAJ) | 237 | compile-bench: $(OUTPUT)libcpupower.so.$(LIB_MAJ) |
226 | @V=$(V) confdir=$(confdir) $(MAKE) -C bench | 238 | @V=$(V) confdir=$(confdir) $(MAKE) -C bench O=$(OUTPUT) |
239 | |||
240 | # we compile into subdirectories. if the target directory is not the | ||
241 | # source directory, they might not exists. So we depend the various | ||
242 | # files onto their directories. | ||
243 | DIRECTORY_DEPS = $(LIB_OBJS) $(UTIL_OBJS) $(GMO_FILES) | ||
244 | $(DIRECTORY_DEPS): | $(sort $(dir $(DIRECTORY_DEPS))) | ||
245 | |||
246 | # In the second step, we make a rule to actually create these directories | ||
247 | $(sort $(dir $(DIRECTORY_DEPS))): | ||
248 | $(ECHO) " MKDIR " $@ | ||
249 | $(QUIET) $(MKDIR) -p $@ 2>/dev/null | ||
227 | 250 | ||
228 | clean: | 251 | clean: |
229 | -find . \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' \) -type f -print \ | 252 | -find $(OUTPUT) \( -not -type d \) -and \( -name '*~' -o -name '*.[oas]' \) -type f -print \ |
230 | | xargs rm -f | 253 | | xargs rm -f |
231 | -rm -f $(UTIL_BINS) | 254 | -rm -f $(OUTPUT)cpupower |
232 | -rm -f $(IDLE_OBJS) | 255 | -rm -f $(OUTPUT)libcpupower.so* |
233 | -rm -f cpupower | 256 | -rm -rf $(OUTPUT)po/*.{gmo,pot} |
234 | -rm -f libcpupower.so* | 257 | $(MAKE) -C bench O=$(OUTPUT) clean |
235 | -rm -rf po/*.gmo po/*.pot | ||
236 | $(MAKE) -C bench clean | ||
237 | 258 | ||
238 | 259 | ||
239 | install-lib: | 260 | install-lib: |
240 | $(INSTALL) -d $(DESTDIR)${libdir} | 261 | $(INSTALL) -d $(DESTDIR)${libdir} |
241 | $(CP) libcpupower.so* $(DESTDIR)${libdir}/ | 262 | $(CP) $(OUTPUT)libcpupower.so* $(DESTDIR)${libdir}/ |
242 | $(INSTALL) -d $(DESTDIR)${includedir} | 263 | $(INSTALL) -d $(DESTDIR)${includedir} |
243 | $(INSTALL_DATA) lib/cpufreq.h $(DESTDIR)${includedir}/cpufreq.h | 264 | $(INSTALL_DATA) lib/cpufreq.h $(DESTDIR)${includedir}/cpufreq.h |
244 | 265 | ||
245 | install-tools: | 266 | install-tools: |
246 | $(INSTALL) -d $(DESTDIR)${bindir} | 267 | $(INSTALL) -d $(DESTDIR)${bindir} |
247 | $(INSTALL_PROGRAM) cpupower $(DESTDIR)${bindir} | 268 | $(INSTALL_PROGRAM) $(OUTPUT)cpupower $(DESTDIR)${bindir} |
248 | 269 | ||
249 | install-man: | 270 | install-man: |
250 | $(INSTALL_DATA) -D man/cpupower.1 $(DESTDIR)${mandir}/man1/cpupower.1 | 271 | $(INSTALL_DATA) -D man/cpupower.1 $(DESTDIR)${mandir}/man1/cpupower.1 |
@@ -257,13 +278,13 @@ install-man: | |||
257 | install-gmo: | 278 | install-gmo: |
258 | $(INSTALL) -d $(DESTDIR)${localedir} | 279 | $(INSTALL) -d $(DESTDIR)${localedir} |
259 | for HLANG in $(LANGUAGES); do \ | 280 | for HLANG in $(LANGUAGES); do \ |
260 | echo '$(INSTALL_DATA) -D po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo'; \ | 281 | echo '$(INSTALL_DATA) -D $(OUTPUT)po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo'; \ |
261 | $(INSTALL_DATA) -D po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo; \ | 282 | $(INSTALL_DATA) -D $(OUTPUT)po/$$HLANG.gmo $(DESTDIR)${localedir}/$$HLANG/LC_MESSAGES/cpupower.mo; \ |
262 | done; | 283 | done; |
263 | 284 | ||
264 | install-bench: | 285 | install-bench: |
265 | @#DESTDIR must be set from outside to survive | 286 | @#DESTDIR must be set from outside to survive |
266 | @sbindir=$(sbindir) bindir=$(bindir) docdir=$(docdir) confdir=$(confdir) $(MAKE) -C bench install | 287 | @sbindir=$(sbindir) bindir=$(bindir) docdir=$(docdir) confdir=$(confdir) $(MAKE) -C bench O=$(OUTPUT) install |
267 | 288 | ||
268 | install: all install-lib install-tools install-man $(INSTALL_NLS) $(INSTALL_BENCH) | 289 | install: all install-lib install-tools install-man $(INSTALL_NLS) $(INSTALL_BENCH) |
269 | 290 | ||
diff --git a/tools/power/cpupower/bench/Makefile b/tools/power/cpupower/bench/Makefile index 2b67606fc3e3..7ec7021a29cd 100644 --- a/tools/power/cpupower/bench/Makefile +++ b/tools/power/cpupower/bench/Makefile | |||
@@ -1,29 +1,36 @@ | |||
1 | LIBS = -L../ -lm -lcpupower | 1 | OUTPUT := ./ |
2 | ifeq ("$(origin O)", "command line") | ||
3 | ifneq ($(O),) | ||
4 | OUTPUT := $(O)/ | ||
5 | endif | ||
6 | endif | ||
2 | 7 | ||
3 | OBJS = main.o parse.o system.o benchmark.o | 8 | LIBS = -L../ -L$(OUTPUT) -lm -lcpupower |
9 | |||
10 | OBJS = $(OUTPUT)main.o $(OUTPUT)parse.o $(OUTPUT)system.o $(OUTPUT)benchmark.o | ||
4 | CFLAGS += -D_GNU_SOURCE -I../lib -DDEFAULT_CONFIG_FILE=\"$(confdir)/cpufreq-bench.conf\" | 11 | CFLAGS += -D_GNU_SOURCE -I../lib -DDEFAULT_CONFIG_FILE=\"$(confdir)/cpufreq-bench.conf\" |
5 | 12 | ||
6 | %.o : %.c | 13 | $(OUTPUT)%.o : %.c |
7 | $(ECHO) " CC " $@ | 14 | $(ECHO) " CC " $@ |
8 | $(QUIET) $(CC) -c $(CFLAGS) $< -o $@ | 15 | $(QUIET) $(CC) -c $(CFLAGS) $< -o $@ |
9 | 16 | ||
10 | cpufreq-bench: $(OBJS) | 17 | $(OUTPUT)cpufreq-bench: $(OBJS) |
11 | $(ECHO) " CC " $@ | 18 | $(ECHO) " CC " $@ |
12 | $(QUIET) $(CC) -o $@ $(CFLAGS) $(OBJS) $(LIBS) | 19 | $(QUIET) $(CC) -o $@ $(CFLAGS) $(OBJS) $(LIBS) |
13 | 20 | ||
14 | all: cpufreq-bench | 21 | all: $(OUTPUT)cpufreq-bench |
15 | 22 | ||
16 | install: | 23 | install: |
17 | mkdir -p $(DESTDIR)/$(sbindir) | 24 | mkdir -p $(DESTDIR)/$(sbindir) |
18 | mkdir -p $(DESTDIR)/$(bindir) | 25 | mkdir -p $(DESTDIR)/$(bindir) |
19 | mkdir -p $(DESTDIR)/$(docdir) | 26 | mkdir -p $(DESTDIR)/$(docdir) |
20 | mkdir -p $(DESTDIR)/$(confdir) | 27 | mkdir -p $(DESTDIR)/$(confdir) |
21 | install -m 755 cpufreq-bench $(DESTDIR)/$(sbindir)/cpufreq-bench | 28 | install -m 755 $(OUTPUT)cpufreq-bench $(DESTDIR)/$(sbindir)/cpufreq-bench |
22 | install -m 755 cpufreq-bench_plot.sh $(DESTDIR)/$(bindir)/cpufreq-bench_plot.sh | 29 | install -m 755 cpufreq-bench_plot.sh $(DESTDIR)/$(bindir)/cpufreq-bench_plot.sh |
23 | install -m 644 README-BENCH $(DESTDIR)/$(docdir)/README-BENCH | 30 | install -m 644 README-BENCH $(DESTDIR)/$(docdir)/README-BENCH |
24 | install -m 755 cpufreq-bench_script.sh $(DESTDIR)/$(docdir)/cpufreq-bench_script.sh | 31 | install -m 755 cpufreq-bench_script.sh $(DESTDIR)/$(docdir)/cpufreq-bench_script.sh |
25 | install -m 644 example.cfg $(DESTDIR)/$(confdir)/cpufreq-bench.conf | 32 | install -m 644 example.cfg $(DESTDIR)/$(confdir)/cpufreq-bench.conf |
26 | 33 | ||
27 | clean: | 34 | clean: |
28 | rm -f *.o | 35 | rm -f $(OUTPUT)*.o |
29 | rm -f cpufreq-bench | 36 | rm -f $(OUTPUT)cpufreq-bench |
diff --git a/tools/power/cpupower/debug/i386/Makefile b/tools/power/cpupower/debug/i386/Makefile index d08cc1ead9bc..3ba158f0e287 100644 --- a/tools/power/cpupower/debug/i386/Makefile +++ b/tools/power/cpupower/debug/i386/Makefile | |||
@@ -1,20 +1,38 @@ | |||
1 | OUTPUT=./ | ||
2 | ifeq ("$(origin O)", "command line") | ||
3 | OUTPUT := $(O)/ | ||
4 | endif | ||
5 | |||
6 | DESTDIR = | ||
7 | bindir = /usr/bin | ||
8 | |||
9 | INSTALL = /usr/bin/install | ||
10 | |||
11 | |||
1 | default: all | 12 | default: all |
2 | 13 | ||
3 | centrino-decode: centrino-decode.c | 14 | $(OUTPUT)centrino-decode: centrino-decode.c |
4 | $(CC) $(CFLAGS) -o centrino-decode centrino-decode.c | 15 | $(CC) $(CFLAGS) -o $@ centrino-decode.c |
5 | 16 | ||
6 | dump_psb: dump_psb.c | 17 | $(OUTPUT)dump_psb: dump_psb.c |
7 | $(CC) $(CFLAGS) -o dump_psb dump_psb.c | 18 | $(CC) $(CFLAGS) -o $@ dump_psb.c |
8 | 19 | ||
9 | intel_gsic: intel_gsic.c | 20 | $(OUTPUT)intel_gsic: intel_gsic.c |
10 | $(CC) $(CFLAGS) -o intel_gsic -llrmi intel_gsic.c | 21 | $(CC) $(CFLAGS) -o $@ -llrmi intel_gsic.c |
11 | 22 | ||
12 | powernow-k8-decode: powernow-k8-decode.c | 23 | $(OUTPUT)powernow-k8-decode: powernow-k8-decode.c |
13 | $(CC) $(CFLAGS) -o powernow-k8-decode powernow-k8-decode.c | 24 | $(CC) $(CFLAGS) -o $@ powernow-k8-decode.c |
14 | 25 | ||
15 | all: centrino-decode dump_psb intel_gsic powernow-k8-decode | 26 | all: $(OUTPUT)centrino-decode $(OUTPUT)dump_psb $(OUTPUT)intel_gsic $(OUTPUT)powernow-k8-decode |
16 | 27 | ||
17 | clean: | 28 | clean: |
18 | rm -rf centrino-decode dump_psb intel_gsic powernow-k8-decode | 29 | rm -rf $(OUTPUT){centrino-decode,dump_psb,intel_gsic,powernow-k8-decode} |
30 | |||
31 | install: | ||
32 | $(INSTALL) -d $(DESTDIR)${bindir} | ||
33 | $(INSTALL) $(OUTPUT)centrino-decode $(DESTDIR)${bindir} | ||
34 | $(INSTALL) $(OUTPUT)powernow-k8-decode $(DESTDIR)${bindir} | ||
35 | $(INSTALL) $(OUTPUT)dump_psb $(DESTDIR)${bindir} | ||
36 | $(INSTALL) $(OUTPUT)intel_gsic $(DESTDIR)${bindir} | ||
19 | 37 | ||
20 | .PHONY: all default clean | 38 | .PHONY: all default clean install |
diff --git a/tools/power/cpupower/debug/x86_64/Makefile b/tools/power/cpupower/debug/x86_64/Makefile index 3326217dd311..1c5214526716 100644 --- a/tools/power/cpupower/debug/x86_64/Makefile +++ b/tools/power/cpupower/debug/x86_64/Makefile | |||
@@ -1,14 +1,30 @@ | |||
1 | OUTPUT=./ | ||
2 | ifeq ("$(origin O)", "command line") | ||
3 | OUTPUT := $(O)/ | ||
4 | endif | ||
5 | |||
6 | DESTDIR = | ||
7 | bindir = /usr/bin | ||
8 | |||
9 | INSTALL = /usr/bin/install | ||
10 | |||
11 | |||
1 | default: all | 12 | default: all |
2 | 13 | ||
3 | centrino-decode: ../i386/centrino-decode.c | 14 | $(OUTPUT)centrino-decode: ../i386/centrino-decode.c |
4 | $(CC) $(CFLAGS) -o $@ $< | 15 | $(CC) $(CFLAGS) -o $@ $< |
5 | 16 | ||
6 | powernow-k8-decode: ../i386/powernow-k8-decode.c | 17 | $(OUTPUT)powernow-k8-decode: ../i386/powernow-k8-decode.c |
7 | $(CC) $(CFLAGS) -o $@ $< | 18 | $(CC) $(CFLAGS) -o $@ $< |
8 | 19 | ||
9 | all: centrino-decode powernow-k8-decode | 20 | all: $(OUTPUT)centrino-decode $(OUTPUT)powernow-k8-decode |
10 | 21 | ||
11 | clean: | 22 | clean: |
12 | rm -rf centrino-decode powernow-k8-decode | 23 | rm -rf $(OUTPUT)centrino-decode $(OUTPUT)powernow-k8-decode |
24 | |||
25 | install: | ||
26 | $(INSTALL) -d $(DESTDIR)${bindir} | ||
27 | $(INSTALL) $(OUTPUT)centrino-decode $(DESTDIR)${bindir} | ||
28 | $(INSTALL) $(OUTPUT)powernow-k8-decode $(DESTDIR)${bindir} | ||
13 | 29 | ||
14 | .PHONY: all default clean | 30 | .PHONY: all default clean install |
diff --git a/tools/power/cpupower/man/cpupower-frequency-info.1 b/tools/power/cpupower/man/cpupower-frequency-info.1 index bb60a8d1e45a..4a1918ea8f9c 100644 --- a/tools/power/cpupower/man/cpupower-frequency-info.1 +++ b/tools/power/cpupower/man/cpupower-frequency-info.1 | |||
@@ -1,4 +1,4 @@ | |||
1 | .TH "cpupower-frequency-info" "1" "0.1" "Mattia Dongili" "" | 1 | .TH "CPUPOWER\-FREQUENCY\-INFO" "1" "0.1" "" "cpupower Manual" |
2 | .SH "NAME" | 2 | .SH "NAME" |
3 | .LP | 3 | .LP |
4 | cpupower frequency\-info \- Utility to retrieve cpufreq kernel information | 4 | cpupower frequency\-info \- Utility to retrieve cpufreq kernel information |
@@ -50,8 +50,6 @@ Prints out information like provided by the /proc/cpufreq interface in 2.4. and | |||
50 | \fB\-m\fR \fB\-\-human\fR | 50 | \fB\-m\fR \fB\-\-human\fR |
51 | human\-readable output for the \-f, \-w, \-s and \-y parameters. | 51 | human\-readable output for the \-f, \-w, \-s and \-y parameters. |
52 | .TP | 52 | .TP |
53 | \fB\-h\fR \fB\-\-help\fR | ||
54 | Prints out the help screen. | ||
55 | .SH "REMARKS" | 53 | .SH "REMARKS" |
56 | .LP | 54 | .LP |
57 | By default only values of core zero are displayed. How to display settings of | 55 | By default only values of core zero are displayed. How to display settings of |
diff --git a/tools/power/cpupower/man/cpupower-frequency-set.1 b/tools/power/cpupower/man/cpupower-frequency-set.1 index 685f469093ad..3eacc8d03d1a 100644 --- a/tools/power/cpupower/man/cpupower-frequency-set.1 +++ b/tools/power/cpupower/man/cpupower-frequency-set.1 | |||
@@ -1,4 +1,4 @@ | |||
1 | .TH "cpupower-freqency-set" "1" "0.1" "Mattia Dongili" "" | 1 | .TH "CPUPOWER\-FREQUENCY\-SET" "1" "0.1" "" "cpupower Manual" |
2 | .SH "NAME" | 2 | .SH "NAME" |
3 | .LP | 3 | .LP |
4 | cpupower frequency\-set \- A small tool which allows to modify cpufreq settings. | 4 | cpupower frequency\-set \- A small tool which allows to modify cpufreq settings. |
@@ -26,8 +26,6 @@ specific frequency to be set. Requires userspace governor to be available and lo | |||
26 | \fB\-r\fR \fB\-\-related\fR | 26 | \fB\-r\fR \fB\-\-related\fR |
27 | modify all hardware-related CPUs at the same time | 27 | modify all hardware-related CPUs at the same time |
28 | .TP | 28 | .TP |
29 | \fB\-h\fR \fB\-\-help\fR | ||
30 | Prints out the help screen. | ||
31 | .SH "REMARKS" | 29 | .SH "REMARKS" |
32 | .LP | 30 | .LP |
33 | By default values are applied on all cores. How to modify single core | 31 | By default values are applied on all cores. How to modify single core |
diff --git a/tools/power/cpupower/man/cpupower-idle-info.1 b/tools/power/cpupower/man/cpupower-idle-info.1 new file mode 100644 index 000000000000..4178effd9e99 --- /dev/null +++ b/tools/power/cpupower/man/cpupower-idle-info.1 | |||
@@ -0,0 +1,90 @@ | |||
1 | .TH "CPUPOWER-IDLE-INFO" "1" "0.1" "" "cpupower Manual" | ||
2 | .SH "NAME" | ||
3 | .LP | ||
4 | cpupower idle\-info \- Utility to retrieve cpu idle kernel information | ||
5 | .SH "SYNTAX" | ||
6 | .LP | ||
7 | cpupower [ \-c cpulist ] idle\-info [\fIoptions\fP] | ||
8 | .SH "DESCRIPTION" | ||
9 | .LP | ||
10 | A tool which prints out per cpu idle information helpful to developers and interested users. | ||
11 | .SH "OPTIONS" | ||
12 | .LP | ||
13 | .TP | ||
14 | \fB\-f\fR \fB\-\-silent\fR | ||
15 | Only print a summary of all available C-states in the system. | ||
16 | .TP | ||
17 | \fB\-e\fR \fB\-\-proc\fR | ||
18 | deprecated. | ||
19 | Prints out idle information in old /proc/acpi/processor/*/power format. This | ||
20 | interface has been removed from the kernel for quite some time, do not let | ||
21 | further code depend on this option, best do not use it. | ||
22 | |||
23 | .SH IDLE\-INFO DESCRIPTIONS | ||
24 | CPU sleep state statistics and descriptions are retrieved from sysfs files, | ||
25 | exported by the cpuidle kernel subsystem. The kernel only updates these | ||
26 | statistics when it enters or leaves an idle state, therefore on a very idle or | ||
27 | a very busy system, these statistics may not be accurate. They still provide a | ||
28 | good overview about the usage and availability of processor sleep states on | ||
29 | the platform. | ||
30 | |||
31 | Be aware that the sleep states as exported by the hardware or BIOS and used by | ||
32 | the Linux kernel may not exactly reflect the capabilities of the | ||
33 | processor. This often is the case on the X86 architecture when the acpi_idle | ||
34 | driver is used. It is also possible that the hardware overrules the kernel | ||
35 | requests, due to internal activity monitors or other reasons. | ||
36 | On recent X86 platforms it is often possible to read out hardware registers | ||
37 | which monitor the duration of sleep states the processor resided in. The | ||
38 | cpupower monitor tool (cpupower\-monitor(1)) can be used to show real sleep | ||
39 | state residencies. Please refer to the architecture specific description | ||
40 | section below. | ||
41 | |||
42 | .SH IDLE\-INFO ARCHITECTURE SPECIFIC DESCRIPTIONS | ||
43 | .SS "X86" | ||
44 | POLL idle state | ||
45 | |||
46 | If cpuidle is active, X86 platforms have one special idle state. | ||
47 | The POLL idle state is not a real idle state, it does not save any | ||
48 | power. Instead, a busy\-loop is executed doing nothing for a short period of | ||
49 | time. This state is used if the kernel knows that work has to be processed | ||
50 | very soon and entering any real hardware idle state may result in a slight | ||
51 | performance penalty. | ||
52 | |||
53 | There exist two different cpuidle drivers on the X86 architecture platform: | ||
54 | |||
55 | "acpi_idle" cpuidle driver | ||
56 | |||
57 | The acpi_idle cpuidle driver retrieves available sleep states (C\-states) from | ||
58 | the ACPI BIOS tables (from the _CST ACPI function on recent platforms or from | ||
59 | the FADT BIOS table on older ones). | ||
60 | The C1 state is not retrieved from ACPI tables. If the C1 state is entered, | ||
61 | the kernel will call the hlt instruction (or mwait on Intel). | ||
62 | |||
63 | "intel_idle" cpuidle driver | ||
64 | |||
65 | In kernel 2.6.36 the intel_idle driver was introduced. | ||
66 | It only serves recent Intel CPUs (Nehalem, Westmere, Sandybridge, Atoms or | ||
67 | newer). On older Intel CPUs the acpi_idle driver is still used (if the BIOS | ||
68 | provides C\-state ACPI tables). | ||
69 | The intel_idle driver knows the sleep state capabilities of the processor and | ||
70 | ignores ACPI BIOS exported processor sleep states tables. | ||
71 | |||
72 | .SH "REMARKS" | ||
73 | .LP | ||
74 | By default only values of core zero are displayed. How to display settings of | ||
75 | other cores is described in the cpupower(1) manpage in the \-\-cpu option | ||
76 | section. | ||
77 | .SH REFERENCES | ||
78 | http://www.acpi.info/spec.htm | ||
79 | .SH "FILES" | ||
80 | .nf | ||
81 | \fI/sys/devices/system/cpu/cpu*/cpuidle/state*\fP | ||
82 | \fI/sys/devices/system/cpu/cpuidle/*\fP | ||
83 | .fi | ||
84 | .SH "AUTHORS" | ||
85 | .nf | ||
86 | Thomas Renninger <trenn@suse.de> | ||
87 | .fi | ||
88 | .SH "SEE ALSO" | ||
89 | .LP | ||
90 | cpupower(1), cpupower\-monitor(1), cpupower\-info(1), cpupower\-set(1) | ||
diff --git a/tools/power/cpupower/man/cpupower-monitor.1 b/tools/power/cpupower/man/cpupower-monitor.1 index d5cfa265c3d3..1141c2073719 100644 --- a/tools/power/cpupower/man/cpupower-monitor.1 +++ b/tools/power/cpupower/man/cpupower-monitor.1 | |||
@@ -107,7 +107,7 @@ Deepest package sleep states may in reality show up as machine/platform wide | |||
107 | sleep states and can only be entered if all cores are idle. Look up Intel | 107 | sleep states and can only be entered if all cores are idle. Look up Intel |
108 | manuals (some are provided in the References section) for further details. | 108 | manuals (some are provided in the References section) for further details. |
109 | 109 | ||
110 | .SS "Ontario" "Liano" | 110 | .SS "Fam_12h" "Fam_14h" |
111 | AMD laptop and desktop processor (family 12h and 14h) sleep state counters. | 111 | AMD laptop and desktop processor (family 12h and 14h) sleep state counters. |
112 | The registers are accessed via PCI and therefore can still be read out while | 112 | The registers are accessed via PCI and therefore can still be read out while |
113 | cores have been offlined. | 113 | cores have been offlined. |
diff --git a/tools/power/cpupower/utils/cpuidle-info.c b/tools/power/cpupower/utils/cpuidle-info.c index b028267c1376..8145af5f93a6 100644 --- a/tools/power/cpupower/utils/cpuidle-info.c +++ b/tools/power/cpupower/utils/cpuidle-info.c | |||
@@ -35,17 +35,9 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose) | |||
35 | printf(_("CPU %u: Can't read idle state info\n"), cpu); | 35 | printf(_("CPU %u: Can't read idle state info\n"), cpu); |
36 | return; | 36 | return; |
37 | } | 37 | } |
38 | tmp = sysfs_get_idlestate_name(cpu, idlestates - 1); | ||
39 | if (!tmp) { | ||
40 | printf(_("Could not determine max idle state %u\n"), | ||
41 | idlestates - 1); | ||
42 | return; | ||
43 | } | ||
44 | |||
45 | printf(_("Number of idle states: %d\n"), idlestates); | 38 | printf(_("Number of idle states: %d\n"), idlestates); |
46 | |||
47 | printf(_("Available idle states:")); | 39 | printf(_("Available idle states:")); |
48 | for (idlestate = 1; idlestate < idlestates; idlestate++) { | 40 | for (idlestate = 0; idlestate < idlestates; idlestate++) { |
49 | tmp = sysfs_get_idlestate_name(cpu, idlestate); | 41 | tmp = sysfs_get_idlestate_name(cpu, idlestate); |
50 | if (!tmp) | 42 | if (!tmp) |
51 | continue; | 43 | continue; |
@@ -57,7 +49,7 @@ static void cpuidle_cpu_output(unsigned int cpu, int verbose) | |||
57 | if (!verbose) | 49 | if (!verbose) |
58 | return; | 50 | return; |
59 | 51 | ||
60 | for (idlestate = 1; idlestate < idlestates; idlestate++) { | 52 | for (idlestate = 0; idlestate < idlestates; idlestate++) { |
61 | tmp = sysfs_get_idlestate_name(cpu, idlestate); | 53 | tmp = sysfs_get_idlestate_name(cpu, idlestate); |
62 | if (!tmp) | 54 | if (!tmp) |
63 | continue; | 55 | continue; |
diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index 87d5605bdda8..6437ef39aeea 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c | |||
@@ -112,14 +112,12 @@ int decode_pstates(unsigned int cpu, unsigned int cpu_family, | |||
112 | int amd_pci_get_num_boost_states(int *active, int *states) | 112 | int amd_pci_get_num_boost_states(int *active, int *states) |
113 | { | 113 | { |
114 | struct pci_access *pci_acc; | 114 | struct pci_access *pci_acc; |
115 | int vendor_id = 0x1022; | ||
116 | int boost_dev_ids[4] = {0x1204, 0x1604, 0x1704, 0}; | ||
117 | struct pci_dev *device; | 115 | struct pci_dev *device; |
118 | uint8_t val = 0; | 116 | uint8_t val = 0; |
119 | 117 | ||
120 | *active = *states = 0; | 118 | *active = *states = 0; |
121 | 119 | ||
122 | device = pci_acc_init(&pci_acc, vendor_id, boost_dev_ids); | 120 | device = pci_slot_func_init(&pci_acc, 0x18, 4); |
123 | 121 | ||
124 | if (device == NULL) | 122 | if (device == NULL) |
125 | return -ENODEV; | 123 | return -ENODEV; |
diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 2747e738efb0..2eb584cf2f55 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h | |||
@@ -66,8 +66,8 @@ enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL, | |||
66 | #define CPUPOWER_CAP_AMD_CBP 0x00000004 | 66 | #define CPUPOWER_CAP_AMD_CBP 0x00000004 |
67 | #define CPUPOWER_CAP_PERF_BIAS 0x00000008 | 67 | #define CPUPOWER_CAP_PERF_BIAS 0x00000008 |
68 | #define CPUPOWER_CAP_HAS_TURBO_RATIO 0x00000010 | 68 | #define CPUPOWER_CAP_HAS_TURBO_RATIO 0x00000010 |
69 | #define CPUPOWER_CAP_IS_SNB 0x00000011 | 69 | #define CPUPOWER_CAP_IS_SNB 0x00000020 |
70 | #define CPUPOWER_CAP_INTEL_IDA 0x00000012 | 70 | #define CPUPOWER_CAP_INTEL_IDA 0x00000040 |
71 | 71 | ||
72 | #define MAX_HW_PSTATES 10 | 72 | #define MAX_HW_PSTATES 10 |
73 | 73 | ||
@@ -132,8 +132,11 @@ extern unsigned long long msr_intel_get_turbo_ratio(unsigned int cpu); | |||
132 | 132 | ||
133 | /* PCI stuff ****************************/ | 133 | /* PCI stuff ****************************/ |
134 | extern int amd_pci_get_num_boost_states(int *active, int *states); | 134 | extern int amd_pci_get_num_boost_states(int *active, int *states); |
135 | extern struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id, | 135 | extern struct pci_dev *pci_acc_init(struct pci_access **pacc, int domain, |
136 | int *dev_ids); | 136 | int bus, int slot, int func, int vendor, |
137 | int dev); | ||
138 | extern struct pci_dev *pci_slot_func_init(struct pci_access **pacc, | ||
139 | int slot, int func); | ||
137 | 140 | ||
138 | /* PCI stuff ****************************/ | 141 | /* PCI stuff ****************************/ |
139 | 142 | ||
diff --git a/tools/power/cpupower/utils/helpers/pci.c b/tools/power/cpupower/utils/helpers/pci.c index cd2eb6fe41c4..9690798e6446 100644 --- a/tools/power/cpupower/utils/helpers/pci.c +++ b/tools/power/cpupower/utils/helpers/pci.c | |||
@@ -10,19 +10,24 @@ | |||
10 | * **pacc : if a valid pci_dev is returned | 10 | * **pacc : if a valid pci_dev is returned |
11 | * *pacc must be passed to pci_acc_cleanup to free it | 11 | * *pacc must be passed to pci_acc_cleanup to free it |
12 | * | 12 | * |
13 | * vendor_id : the pci vendor id matching the pci device to access | 13 | * domain: domain |
14 | * dev_ids : device ids matching the pci device to access | 14 | * bus: bus |
15 | * slot: slot | ||
16 | * func: func | ||
17 | * vendor: vendor | ||
18 | * device: device | ||
19 | * Pass -1 for one of the six above to match any | ||
15 | * | 20 | * |
16 | * Returns : | 21 | * Returns : |
17 | * struct pci_dev which can be used with pci_{read,write}_* functions | 22 | * struct pci_dev which can be used with pci_{read,write}_* functions |
18 | * to access the PCI config space of matching pci devices | 23 | * to access the PCI config space of matching pci devices |
19 | */ | 24 | */ |
20 | struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id, | 25 | struct pci_dev *pci_acc_init(struct pci_access **pacc, int domain, int bus, |
21 | int *dev_ids) | 26 | int slot, int func, int vendor, int dev) |
22 | { | 27 | { |
23 | struct pci_filter filter_nb_link = { -1, -1, -1, -1, vendor_id, 0}; | 28 | struct pci_filter filter_nb_link = { domain, bus, slot, func, |
29 | vendor, dev }; | ||
24 | struct pci_dev *device; | 30 | struct pci_dev *device; |
25 | unsigned int i; | ||
26 | 31 | ||
27 | *pacc = pci_alloc(); | 32 | *pacc = pci_alloc(); |
28 | if (*pacc == NULL) | 33 | if (*pacc == NULL) |
@@ -31,14 +36,20 @@ struct pci_dev *pci_acc_init(struct pci_access **pacc, int vendor_id, | |||
31 | pci_init(*pacc); | 36 | pci_init(*pacc); |
32 | pci_scan_bus(*pacc); | 37 | pci_scan_bus(*pacc); |
33 | 38 | ||
34 | for (i = 0; dev_ids[i] != 0; i++) { | 39 | for (device = (*pacc)->devices; device; device = device->next) { |
35 | filter_nb_link.device = dev_ids[i]; | 40 | if (pci_filter_match(&filter_nb_link, device)) |
36 | for (device = (*pacc)->devices; device; device = device->next) { | 41 | return device; |
37 | if (pci_filter_match(&filter_nb_link, device)) | ||
38 | return device; | ||
39 | } | ||
40 | } | 42 | } |
41 | pci_cleanup(*pacc); | 43 | pci_cleanup(*pacc); |
42 | return NULL; | 44 | return NULL; |
43 | } | 45 | } |
46 | |||
47 | /* Typically one wants to get a specific slot(device)/func of the root domain | ||
48 | and bus */ | ||
49 | struct pci_dev *pci_slot_func_init(struct pci_access **pacc, int slot, | ||
50 | int func) | ||
51 | { | ||
52 | return pci_acc_init(pacc, 0, 0, slot, func, -1, -1); | ||
53 | } | ||
54 | |||
44 | #endif /* defined(__i386__) || defined(__x86_64__) */ | 55 | #endif /* defined(__i386__) || defined(__x86_64__) */ |
diff --git a/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c b/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c index 202e555988be..2116df9ad832 100644 --- a/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c +++ b/tools/power/cpupower/utils/idle_monitor/amd_fam14h_idle.c | |||
@@ -20,8 +20,6 @@ | |||
20 | #include "idle_monitor/cpupower-monitor.h" | 20 | #include "idle_monitor/cpupower-monitor.h" |
21 | #include "helpers/helpers.h" | 21 | #include "helpers/helpers.h" |
22 | 22 | ||
23 | /******** PCI parts could go into own file and get shared ***************/ | ||
24 | |||
25 | #define PCI_NON_PC0_OFFSET 0xb0 | 23 | #define PCI_NON_PC0_OFFSET 0xb0 |
26 | #define PCI_PC1_OFFSET 0xb4 | 24 | #define PCI_PC1_OFFSET 0xb4 |
27 | #define PCI_PC6_OFFSET 0xb8 | 25 | #define PCI_PC6_OFFSET 0xb8 |
@@ -82,10 +80,7 @@ static cstate_t amd_fam14h_cstates[AMD_FAM14H_STATE_NUM] = { | |||
82 | }; | 80 | }; |
83 | 81 | ||
84 | static struct pci_access *pci_acc; | 82 | static struct pci_access *pci_acc; |
85 | static int pci_vendor_id = 0x1022; | ||
86 | static int pci_dev_ids[2] = {0x1716, 0}; | ||
87 | static struct pci_dev *amd_fam14h_pci_dev; | 83 | static struct pci_dev *amd_fam14h_pci_dev; |
88 | |||
89 | static int nbp1_entered; | 84 | static int nbp1_entered; |
90 | 85 | ||
91 | struct timespec start_time; | 86 | struct timespec start_time; |
@@ -286,13 +281,13 @@ struct cpuidle_monitor *amd_fam14h_register(void) | |||
286 | if (cpupower_cpu_info.vendor != X86_VENDOR_AMD) | 281 | if (cpupower_cpu_info.vendor != X86_VENDOR_AMD) |
287 | return NULL; | 282 | return NULL; |
288 | 283 | ||
289 | if (cpupower_cpu_info.family == 0x14) { | 284 | if (cpupower_cpu_info.family == 0x14) |
290 | if (cpu_count <= 0 || cpu_count > 2) { | 285 | strncpy(amd_fam14h_monitor.name, "Fam_14h", |
291 | fprintf(stderr, "AMD fam14h: Invalid cpu count: %d\n", | 286 | MONITOR_NAME_LEN - 1); |
292 | cpu_count); | 287 | else if (cpupower_cpu_info.family == 0x12) |
293 | return NULL; | 288 | strncpy(amd_fam14h_monitor.name, "Fam_12h", |
294 | } | 289 | MONITOR_NAME_LEN - 1); |
295 | } else | 290 | else |
296 | return NULL; | 291 | return NULL; |
297 | 292 | ||
298 | /* We do not alloc for nbp1 machine wide counter */ | 293 | /* We do not alloc for nbp1 machine wide counter */ |
@@ -303,7 +298,9 @@ struct cpuidle_monitor *amd_fam14h_register(void) | |||
303 | sizeof(unsigned long long)); | 298 | sizeof(unsigned long long)); |
304 | } | 299 | } |
305 | 300 | ||
306 | amd_fam14h_pci_dev = pci_acc_init(&pci_acc, pci_vendor_id, pci_dev_ids); | 301 | /* We need PCI device: Slot 18, Func 6, compare with BKDG |
302 | for fam 12h/14h */ | ||
303 | amd_fam14h_pci_dev = pci_slot_func_init(&pci_acc, 0x18, 6); | ||
307 | if (amd_fam14h_pci_dev == NULL || pci_acc == NULL) | 304 | if (amd_fam14h_pci_dev == NULL || pci_acc == NULL) |
308 | return NULL; | 305 | return NULL; |
309 | 306 | ||
@@ -325,7 +322,7 @@ static void amd_fam14h_unregister(void) | |||
325 | } | 322 | } |
326 | 323 | ||
327 | struct cpuidle_monitor amd_fam14h_monitor = { | 324 | struct cpuidle_monitor amd_fam14h_monitor = { |
328 | .name = "Ontario", | 325 | .name = "", |
329 | .hw_states = amd_fam14h_cstates, | 326 | .hw_states = amd_fam14h_cstates, |
330 | .hw_states_num = AMD_FAM14H_STATE_NUM, | 327 | .hw_states_num = AMD_FAM14H_STATE_NUM, |
331 | .start = amd_fam14h_start, | 328 | .start = amd_fam14h_start, |
diff --git a/tools/power/x86/turbostat/turbostat.8 b/tools/power/x86/turbostat/turbostat.8 index 555c69a5592a..adf175f61496 100644 --- a/tools/power/x86/turbostat/turbostat.8 +++ b/tools/power/x86/turbostat/turbostat.8 | |||
@@ -4,11 +4,13 @@ turbostat \- Report processor frequency and idle statistics | |||
4 | .SH SYNOPSIS | 4 | .SH SYNOPSIS |
5 | .ft B | 5 | .ft B |
6 | .B turbostat | 6 | .B turbostat |
7 | .RB [ "\-s" ] | ||
7 | .RB [ "\-v" ] | 8 | .RB [ "\-v" ] |
8 | .RB [ "\-M MSR#" ] | 9 | .RB [ "\-M MSR#" ] |
9 | .RB command | 10 | .RB command |
10 | .br | 11 | .br |
11 | .B turbostat | 12 | .B turbostat |
13 | .RB [ "\-s" ] | ||
12 | .RB [ "\-v" ] | 14 | .RB [ "\-v" ] |
13 | .RB [ "\-M MSR#" ] | 15 | .RB [ "\-M MSR#" ] |
14 | .RB [ "\-i interval_sec" ] | 16 | .RB [ "\-i interval_sec" ] |
@@ -25,6 +27,8 @@ supports an "invariant" TSC, plus the APERF and MPERF MSRs. | |||
25 | on processors that additionally support C-state residency counters. | 27 | on processors that additionally support C-state residency counters. |
26 | 28 | ||
27 | .SS Options | 29 | .SS Options |
30 | The \fB-s\fP option prints only a 1-line summary for each sample interval. | ||
31 | .PP | ||
28 | The \fB-v\fP option increases verbosity. | 32 | The \fB-v\fP option increases verbosity. |
29 | .PP | 33 | .PP |
30 | The \fB-M MSR#\fP option dumps the specified MSR, | 34 | The \fB-M MSR#\fP option dumps the specified MSR, |
@@ -39,13 +43,14 @@ displays the statistics gathered since it was forked. | |||
39 | .SH FIELD DESCRIPTIONS | 43 | .SH FIELD DESCRIPTIONS |
40 | .nf | 44 | .nf |
41 | \fBpk\fP processor package number. | 45 | \fBpk\fP processor package number. |
42 | \fBcr\fP processor core number. | 46 | \fBcor\fP processor core number. |
43 | \fBCPU\fP Linux CPU (logical processor) number. | 47 | \fBCPU\fP Linux CPU (logical processor) number. |
48 | Note that multiple CPUs per core indicate support for Intel(R) Hyper-Threading Technology. | ||
44 | \fB%c0\fP percent of the interval that the CPU retired instructions. | 49 | \fB%c0\fP percent of the interval that the CPU retired instructions. |
45 | \fBGHz\fP average clock rate while the CPU was in c0 state. | 50 | \fBGHz\fP average clock rate while the CPU was in c0 state. |
46 | \fBTSC\fP average GHz that the TSC ran during the entire interval. | 51 | \fBTSC\fP average GHz that the TSC ran during the entire interval. |
47 | \fB%c1, %c3, %c6\fP show the percentage residency in hardware core idle states. | 52 | \fB%c1, %c3, %c6, %c7\fP show the percentage residency in hardware core idle states. |
48 | \fB%pc3, %pc6\fP percentage residency in hardware package idle states. | 53 | \fB%pc2, %pc3, %pc6, %pc7\fP percentage residency in hardware package idle states. |
49 | .fi | 54 | .fi |
50 | .PP | 55 | .PP |
51 | .SH EXAMPLE | 56 | .SH EXAMPLE |
@@ -53,25 +58,37 @@ Without any parameters, turbostat prints out counters ever 5 seconds. | |||
53 | (override interval with "-i sec" option, or specify a command | 58 | (override interval with "-i sec" option, or specify a command |
54 | for turbostat to fork). | 59 | for turbostat to fork). |
55 | 60 | ||
56 | The first row of statistics reflect the average for the entire system. | 61 | The first row of statistics is a summary for the entire system. |
62 | Note that the summary is a weighted average. | ||
57 | Subsequent rows show per-CPU statistics. | 63 | Subsequent rows show per-CPU statistics. |
58 | 64 | ||
59 | .nf | 65 | .nf |
60 | [root@x980]# ./turbostat | 66 | [root@x980]# ./turbostat |
61 | cr CPU %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6 | 67 | cor CPU %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6 |
62 | 0.04 1.62 3.38 0.11 0.00 99.85 0.00 95.07 | 68 | 0.60 1.63 3.38 2.91 0.00 96.49 0.00 76.64 |
63 | 0 0 0.04 1.62 3.38 0.06 0.00 99.90 0.00 95.07 | 69 | 0 0 0.59 1.62 3.38 4.51 0.00 94.90 0.00 76.64 |
64 | 0 6 0.02 1.62 3.38 0.08 0.00 99.90 0.00 95.07 | 70 | 0 6 1.13 1.64 3.38 3.97 0.00 94.90 0.00 76.64 |
65 | 1 2 0.10 1.62 3.38 0.29 0.00 99.61 0.00 95.07 | 71 | 1 2 0.08 1.62 3.38 0.07 0.00 99.85 0.00 76.64 |
66 | 1 8 0.11 1.62 3.38 0.28 0.00 99.61 0.00 95.07 | 72 | 1 8 0.03 1.62 3.38 0.12 0.00 99.85 0.00 76.64 |
67 | 2 4 0.01 1.62 3.38 0.01 0.00 99.98 0.00 95.07 | 73 | 2 4 0.01 1.62 3.38 0.06 0.00 99.93 0.00 76.64 |
68 | 2 10 0.01 1.61 3.38 0.02 0.00 99.98 0.00 95.07 | 74 | 2 10 0.04 1.62 3.38 0.02 0.00 99.93 0.00 76.64 |
69 | 8 1 0.07 1.62 3.38 0.15 0.00 99.78 0.00 95.07 | 75 | 8 1 2.85 1.62 3.38 11.71 0.00 85.44 0.00 76.64 |
70 | 8 7 0.03 1.62 3.38 0.19 0.00 99.78 0.00 95.07 | 76 | 8 7 1.98 1.62 3.38 12.58 0.00 85.44 0.00 76.64 |
71 | 9 3 0.01 1.62 3.38 0.02 0.00 99.98 0.00 95.07 | 77 | 9 3 0.36 1.62 3.38 0.71 0.00 98.93 0.00 76.64 |
72 | 9 9 0.01 1.62 3.38 0.02 0.00 99.98 0.00 95.07 | 78 | 9 9 0.09 1.62 3.38 0.98 0.00 98.93 0.00 76.64 |
73 | 10 5 0.01 1.62 3.38 0.13 0.00 99.86 0.00 95.07 | 79 | 10 5 0.03 1.62 3.38 0.09 0.00 99.87 0.00 76.64 |
74 | 10 11 0.08 1.62 3.38 0.05 0.00 99.86 0.00 95.07 | 80 | 10 11 0.07 1.62 3.38 0.06 0.00 99.87 0.00 76.64 |
81 | .fi | ||
82 | .SH SUMMARY EXAMPLE | ||
83 | The "-s" option prints the column headers just once, | ||
84 | and then the one line system summary for each sample interval. | ||
85 | |||
86 | .nf | ||
87 | [root@x980]# ./turbostat -s | ||
88 | %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6 | ||
89 | 0.61 1.89 3.38 5.95 0.00 93.44 0.00 66.33 | ||
90 | 0.52 1.62 3.38 6.83 0.00 92.65 0.00 61.11 | ||
91 | 0.62 1.92 3.38 5.47 0.00 93.91 0.00 67.31 | ||
75 | .fi | 92 | .fi |
76 | .SH VERBOSE EXAMPLE | 93 | .SH VERBOSE EXAMPLE |
77 | The "-v" option adds verbosity to the output: | 94 | The "-v" option adds verbosity to the output: |
@@ -101,33 +118,33 @@ until ^C while the other CPUs are mostly idle: | |||
101 | 118 | ||
102 | .nf | 119 | .nf |
103 | [root@x980 lenb]# ./turbostat cat /dev/zero > /dev/null | 120 | [root@x980 lenb]# ./turbostat cat /dev/zero > /dev/null |
104 | 121 | ^C | |
105 | ^Ccr CPU %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6 | 122 | cor CPU %c0 GHz TSC %c1 %c3 %c6 %pc3 %pc6 |
106 | 8.49 3.63 3.38 16.23 0.66 74.63 0.00 0.00 | 123 | 8.63 3.64 3.38 14.46 0.49 76.42 0.00 0.00 |
107 | 0 0 1.22 3.62 3.38 32.18 0.00 66.60 0.00 0.00 | 124 | 0 0 0.34 3.36 3.38 99.66 0.00 0.00 0.00 0.00 |
108 | 0 6 0.40 3.61 3.38 33.00 0.00 66.60 0.00 0.00 | 125 | 0 6 99.96 3.64 3.38 0.04 0.00 0.00 0.00 0.00 |
109 | 1 2 0.11 3.14 3.38 0.19 3.95 95.75 0.00 0.00 | 126 | 1 2 0.14 3.50 3.38 1.75 2.04 96.07 0.00 0.00 |
110 | 1 8 0.05 2.88 3.38 0.25 3.95 95.75 0.00 0.00 | 127 | 1 8 0.38 3.57 3.38 1.51 2.04 96.07 0.00 0.00 |
111 | 2 4 0.00 3.13 3.38 0.02 0.00 99.98 0.00 0.00 | 128 | 2 4 0.01 2.65 3.38 0.06 0.00 99.93 0.00 0.00 |
112 | 2 10 0.00 3.09 3.38 0.02 0.00 99.98 0.00 0.00 | 129 | 2 10 0.03 2.12 3.38 0.04 0.00 99.93 0.00 0.00 |
113 | 8 1 0.04 3.50 3.38 14.43 0.00 85.54 0.00 0.00 | 130 | 8 1 0.91 3.59 3.38 35.27 0.92 62.90 0.00 0.00 |
114 | 8 7 0.03 2.98 3.38 14.43 0.00 85.54 0.00 0.00 | 131 | 8 7 1.61 3.63 3.38 34.57 0.92 62.90 0.00 0.00 |
115 | 9 3 0.00 3.16 3.38 100.00 0.00 0.00 0.00 0.00 | 132 | 9 3 0.04 3.38 3.38 0.20 0.00 99.76 0.00 0.00 |
116 | 9 9 99.93 3.63 3.38 0.06 0.00 0.00 0.00 0.00 | 133 | 9 9 0.04 3.29 3.38 0.20 0.00 99.76 0.00 0.00 |
117 | 10 5 0.01 2.82 3.38 0.08 0.00 99.91 0.00 0.00 | 134 | 10 5 0.03 3.08 3.38 0.12 0.00 99.85 0.00 0.00 |
118 | 10 11 0.02 3.36 3.38 0.06 0.00 99.91 0.00 0.00 | 135 | 10 11 0.05 3.07 3.38 0.10 0.00 99.85 0.00 0.00 |
119 | 6.950866 sec | 136 | 4.907015 sec |
120 | 137 | ||
121 | .fi | 138 | .fi |
122 | Above the cycle soaker drives cpu9 up 3.6 Ghz turbo limit | 139 | Above the cycle soaker drives cpu6 up 3.6 Ghz turbo limit |
123 | while the other processors are generally in various states of idle. | 140 | while the other processors are generally in various states of idle. |
124 | 141 | ||
125 | Note that cpu3 is an HT sibling sharing core9 | 142 | Note that cpu0 is an HT sibling sharing core0 |
126 | with cpu9, and thus it is unable to get to an idle state | 143 | with cpu6, and thus it is unable to get to an idle state |
127 | deeper than c1 while cpu9 is busy. | 144 | deeper than c1 while cpu6 is busy. |
128 | 145 | ||
129 | Note that turbostat reports average GHz of 3.61, while | 146 | Note that turbostat reports average GHz of 3.64, while |
130 | the arithmetic average of the GHz column above is 3.24. | 147 | the arithmetic average of the GHz column above is lower. |
131 | This is a weighted average, where the weight is %c0. ie. it is the total number of | 148 | This is a weighted average, where the weight is %c0. ie. it is the total number of |
132 | un-halted cycles elapsed per time divided by the number of CPUs. | 149 | un-halted cycles elapsed per time divided by the number of CPUs. |
133 | .SH NOTES | 150 | .SH NOTES |
@@ -167,6 +184,6 @@ http://www.intel.com/products/processor/manuals/ | |||
167 | .SH "SEE ALSO" | 184 | .SH "SEE ALSO" |
168 | msr(4), vmstat(8) | 185 | msr(4), vmstat(8) |
169 | .PP | 186 | .PP |
170 | .SH AUTHORS | 187 | .SH AUTHOR |
171 | .nf | 188 | .nf |
172 | Written by Len Brown <len.brown@intel.com> | 189 | Written by Len Brown <len.brown@intel.com> |
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c index 3c6f7808efae..ab2f682fd44c 100644 --- a/tools/power/x86/turbostat/turbostat.c +++ b/tools/power/x86/turbostat/turbostat.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * turbostat -- show CPU frequency and C-state residency | 2 | * turbostat -- show CPU frequency and C-state residency |
3 | * on modern Intel turbo-capable processors. | 3 | * on modern Intel turbo-capable processors. |
4 | * | 4 | * |
5 | * Copyright (c) 2010, Intel Corporation. | 5 | * Copyright (c) 2012 Intel Corporation. |
6 | * Len Brown <len.brown@intel.com> | 6 | * Len Brown <len.brown@intel.com> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 8 | * This program is free software; you can redistribute it and/or modify it |
@@ -19,6 +19,7 @@ | |||
19 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 19 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #define _GNU_SOURCE | ||
22 | #include <stdio.h> | 23 | #include <stdio.h> |
23 | #include <unistd.h> | 24 | #include <unistd.h> |
24 | #include <sys/types.h> | 25 | #include <sys/types.h> |
@@ -32,6 +33,7 @@ | |||
32 | #include <dirent.h> | 33 | #include <dirent.h> |
33 | #include <string.h> | 34 | #include <string.h> |
34 | #include <ctype.h> | 35 | #include <ctype.h> |
36 | #include <sched.h> | ||
35 | 37 | ||
36 | #define MSR_TSC 0x10 | 38 | #define MSR_TSC 0x10 |
37 | #define MSR_NEHALEM_PLATFORM_INFO 0xCE | 39 | #define MSR_NEHALEM_PLATFORM_INFO 0xCE |
@@ -49,6 +51,7 @@ | |||
49 | char *proc_stat = "/proc/stat"; | 51 | char *proc_stat = "/proc/stat"; |
50 | unsigned int interval_sec = 5; /* set with -i interval_sec */ | 52 | unsigned int interval_sec = 5; /* set with -i interval_sec */ |
51 | unsigned int verbose; /* set with -v */ | 53 | unsigned int verbose; /* set with -v */ |
54 | unsigned int summary_only; /* set with -s */ | ||
52 | unsigned int skip_c0; | 55 | unsigned int skip_c0; |
53 | unsigned int skip_c1; | 56 | unsigned int skip_c1; |
54 | unsigned int do_nhm_cstates; | 57 | unsigned int do_nhm_cstates; |
@@ -68,9 +71,10 @@ unsigned int show_cpu; | |||
68 | int aperf_mperf_unstable; | 71 | int aperf_mperf_unstable; |
69 | int backwards_count; | 72 | int backwards_count; |
70 | char *progname; | 73 | char *progname; |
71 | int need_reinitialize; | ||
72 | 74 | ||
73 | int num_cpus; | 75 | int num_cpus; |
76 | cpu_set_t *cpu_mask; | ||
77 | size_t cpu_mask_size; | ||
74 | 78 | ||
75 | struct counters { | 79 | struct counters { |
76 | unsigned long long tsc; /* per thread */ | 80 | unsigned long long tsc; /* per thread */ |
@@ -99,44 +103,76 @@ struct timeval tv_even; | |||
99 | struct timeval tv_odd; | 103 | struct timeval tv_odd; |
100 | struct timeval tv_delta; | 104 | struct timeval tv_delta; |
101 | 105 | ||
102 | unsigned long long get_msr(int cpu, off_t offset) | 106 | /* |
107 | * cpu_mask_init(ncpus) | ||
108 | * | ||
109 | * allocate and clear cpu_mask | ||
110 | * set cpu_mask_size | ||
111 | */ | ||
112 | void cpu_mask_init(int ncpus) | ||
113 | { | ||
114 | cpu_mask = CPU_ALLOC(ncpus); | ||
115 | if (cpu_mask == NULL) { | ||
116 | perror("CPU_ALLOC"); | ||
117 | exit(3); | ||
118 | } | ||
119 | cpu_mask_size = CPU_ALLOC_SIZE(ncpus); | ||
120 | CPU_ZERO_S(cpu_mask_size, cpu_mask); | ||
121 | } | ||
122 | |||
123 | void cpu_mask_uninit() | ||
124 | { | ||
125 | CPU_FREE(cpu_mask); | ||
126 | cpu_mask = NULL; | ||
127 | cpu_mask_size = 0; | ||
128 | } | ||
129 | |||
130 | int cpu_migrate(int cpu) | ||
131 | { | ||
132 | CPU_ZERO_S(cpu_mask_size, cpu_mask); | ||
133 | CPU_SET_S(cpu, cpu_mask_size, cpu_mask); | ||
134 | if (sched_setaffinity(0, cpu_mask_size, cpu_mask) == -1) | ||
135 | return -1; | ||
136 | else | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | int get_msr(int cpu, off_t offset, unsigned long long *msr) | ||
103 | { | 141 | { |
104 | ssize_t retval; | 142 | ssize_t retval; |
105 | unsigned long long msr; | ||
106 | char pathname[32]; | 143 | char pathname[32]; |
107 | int fd; | 144 | int fd; |
108 | 145 | ||
109 | sprintf(pathname, "/dev/cpu/%d/msr", cpu); | 146 | sprintf(pathname, "/dev/cpu/%d/msr", cpu); |
110 | fd = open(pathname, O_RDONLY); | 147 | fd = open(pathname, O_RDONLY); |
111 | if (fd < 0) { | 148 | if (fd < 0) |
112 | perror(pathname); | 149 | return -1; |
113 | need_reinitialize = 1; | ||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | retval = pread(fd, &msr, sizeof msr, offset); | ||
118 | if (retval != sizeof msr) { | ||
119 | fprintf(stderr, "cpu%d pread(..., 0x%zx) = %jd\n", | ||
120 | cpu, offset, retval); | ||
121 | exit(-2); | ||
122 | } | ||
123 | 150 | ||
151 | retval = pread(fd, msr, sizeof *msr, offset); | ||
124 | close(fd); | 152 | close(fd); |
125 | return msr; | 153 | |
154 | if (retval != sizeof *msr) | ||
155 | return -1; | ||
156 | |||
157 | return 0; | ||
126 | } | 158 | } |
127 | 159 | ||
128 | void print_header(void) | 160 | void print_header(void) |
129 | { | 161 | { |
130 | if (show_pkg) | 162 | if (show_pkg) |
131 | fprintf(stderr, "pk"); | 163 | fprintf(stderr, "pk"); |
164 | if (show_pkg) | ||
165 | fprintf(stderr, " "); | ||
132 | if (show_core) | 166 | if (show_core) |
133 | fprintf(stderr, " cr"); | 167 | fprintf(stderr, "cor"); |
134 | if (show_cpu) | 168 | if (show_cpu) |
135 | fprintf(stderr, " CPU"); | 169 | fprintf(stderr, " CPU"); |
170 | if (show_pkg || show_core || show_cpu) | ||
171 | fprintf(stderr, " "); | ||
136 | if (do_nhm_cstates) | 172 | if (do_nhm_cstates) |
137 | fprintf(stderr, " %%c0 "); | 173 | fprintf(stderr, " %%c0"); |
138 | if (has_aperf) | 174 | if (has_aperf) |
139 | fprintf(stderr, " GHz"); | 175 | fprintf(stderr, " GHz"); |
140 | fprintf(stderr, " TSC"); | 176 | fprintf(stderr, " TSC"); |
141 | if (do_nhm_cstates) | 177 | if (do_nhm_cstates) |
142 | fprintf(stderr, " %%c1"); | 178 | fprintf(stderr, " %%c1"); |
@@ -147,13 +183,13 @@ void print_header(void) | |||
147 | if (do_snb_cstates) | 183 | if (do_snb_cstates) |
148 | fprintf(stderr, " %%c7"); | 184 | fprintf(stderr, " %%c7"); |
149 | if (do_snb_cstates) | 185 | if (do_snb_cstates) |
150 | fprintf(stderr, " %%pc2"); | 186 | fprintf(stderr, " %%pc2"); |
151 | if (do_nhm_cstates) | 187 | if (do_nhm_cstates) |
152 | fprintf(stderr, " %%pc3"); | 188 | fprintf(stderr, " %%pc3"); |
153 | if (do_nhm_cstates) | 189 | if (do_nhm_cstates) |
154 | fprintf(stderr, " %%pc6"); | 190 | fprintf(stderr, " %%pc6"); |
155 | if (do_snb_cstates) | 191 | if (do_snb_cstates) |
156 | fprintf(stderr, " %%pc7"); | 192 | fprintf(stderr, " %%pc7"); |
157 | if (extra_msr_offset) | 193 | if (extra_msr_offset) |
158 | fprintf(stderr, " MSR 0x%x ", extra_msr_offset); | 194 | fprintf(stderr, " MSR 0x%x ", extra_msr_offset); |
159 | 195 | ||
@@ -187,6 +223,15 @@ void dump_list(struct counters *cnt) | |||
187 | dump_cnt(cnt); | 223 | dump_cnt(cnt); |
188 | } | 224 | } |
189 | 225 | ||
226 | /* | ||
227 | * column formatting convention & formats | ||
228 | * package: "pk" 2 columns %2d | ||
229 | * core: "cor" 3 columns %3d | ||
230 | * CPU: "CPU" 3 columns %3d | ||
231 | * GHz: "GHz" 3 columns %3.2 | ||
232 | * TSC: "TSC" 3 columns %3.2 | ||
233 | * percentage " %pc3" %6.2 | ||
234 | */ | ||
190 | void print_cnt(struct counters *p) | 235 | void print_cnt(struct counters *p) |
191 | { | 236 | { |
192 | double interval_float; | 237 | double interval_float; |
@@ -196,39 +241,45 @@ void print_cnt(struct counters *p) | |||
196 | /* topology columns, print blanks on 1st (average) line */ | 241 | /* topology columns, print blanks on 1st (average) line */ |
197 | if (p == cnt_average) { | 242 | if (p == cnt_average) { |
198 | if (show_pkg) | 243 | if (show_pkg) |
244 | fprintf(stderr, " "); | ||
245 | if (show_pkg && show_core) | ||
199 | fprintf(stderr, " "); | 246 | fprintf(stderr, " "); |
200 | if (show_core) | 247 | if (show_core) |
201 | fprintf(stderr, " "); | 248 | fprintf(stderr, " "); |
202 | if (show_cpu) | 249 | if (show_cpu) |
203 | fprintf(stderr, " "); | 250 | fprintf(stderr, " " " "); |
204 | } else { | 251 | } else { |
205 | if (show_pkg) | 252 | if (show_pkg) |
206 | fprintf(stderr, "%d", p->pkg); | 253 | fprintf(stderr, "%2d", p->pkg); |
254 | if (show_pkg && show_core) | ||
255 | fprintf(stderr, " "); | ||
207 | if (show_core) | 256 | if (show_core) |
208 | fprintf(stderr, "%4d", p->core); | 257 | fprintf(stderr, "%3d", p->core); |
209 | if (show_cpu) | 258 | if (show_cpu) |
210 | fprintf(stderr, "%4d", p->cpu); | 259 | fprintf(stderr, " %3d", p->cpu); |
211 | } | 260 | } |
212 | 261 | ||
213 | /* %c0 */ | 262 | /* %c0 */ |
214 | if (do_nhm_cstates) { | 263 | if (do_nhm_cstates) { |
264 | if (show_pkg || show_core || show_cpu) | ||
265 | fprintf(stderr, " "); | ||
215 | if (!skip_c0) | 266 | if (!skip_c0) |
216 | fprintf(stderr, "%7.2f", 100.0 * p->mperf/p->tsc); | 267 | fprintf(stderr, "%6.2f", 100.0 * p->mperf/p->tsc); |
217 | else | 268 | else |
218 | fprintf(stderr, " ****"); | 269 | fprintf(stderr, " ****"); |
219 | } | 270 | } |
220 | 271 | ||
221 | /* GHz */ | 272 | /* GHz */ |
222 | if (has_aperf) { | 273 | if (has_aperf) { |
223 | if (!aperf_mperf_unstable) { | 274 | if (!aperf_mperf_unstable) { |
224 | fprintf(stderr, "%5.2f", | 275 | fprintf(stderr, " %3.2f", |
225 | 1.0 * p->tsc / units * p->aperf / | 276 | 1.0 * p->tsc / units * p->aperf / |
226 | p->mperf / interval_float); | 277 | p->mperf / interval_float); |
227 | } else { | 278 | } else { |
228 | if (p->aperf > p->tsc || p->mperf > p->tsc) { | 279 | if (p->aperf > p->tsc || p->mperf > p->tsc) { |
229 | fprintf(stderr, " ****"); | 280 | fprintf(stderr, " ***"); |
230 | } else { | 281 | } else { |
231 | fprintf(stderr, "%4.1f*", | 282 | fprintf(stderr, "%3.1f*", |
232 | 1.0 * p->tsc / | 283 | 1.0 * p->tsc / |
233 | units * p->aperf / | 284 | units * p->aperf / |
234 | p->mperf / interval_float); | 285 | p->mperf / interval_float); |
@@ -241,7 +292,7 @@ void print_cnt(struct counters *p) | |||
241 | 292 | ||
242 | if (do_nhm_cstates) { | 293 | if (do_nhm_cstates) { |
243 | if (!skip_c1) | 294 | if (!skip_c1) |
244 | fprintf(stderr, "%7.2f", 100.0 * p->c1/p->tsc); | 295 | fprintf(stderr, " %6.2f", 100.0 * p->c1/p->tsc); |
245 | else | 296 | else |
246 | fprintf(stderr, " ****"); | 297 | fprintf(stderr, " ****"); |
247 | } | 298 | } |
@@ -252,13 +303,13 @@ void print_cnt(struct counters *p) | |||
252 | if (do_snb_cstates) | 303 | if (do_snb_cstates) |
253 | fprintf(stderr, " %6.2f", 100.0 * p->c7/p->tsc); | 304 | fprintf(stderr, " %6.2f", 100.0 * p->c7/p->tsc); |
254 | if (do_snb_cstates) | 305 | if (do_snb_cstates) |
255 | fprintf(stderr, " %5.2f", 100.0 * p->pc2/p->tsc); | 306 | fprintf(stderr, " %6.2f", 100.0 * p->pc2/p->tsc); |
256 | if (do_nhm_cstates) | 307 | if (do_nhm_cstates) |
257 | fprintf(stderr, " %5.2f", 100.0 * p->pc3/p->tsc); | 308 | fprintf(stderr, " %6.2f", 100.0 * p->pc3/p->tsc); |
258 | if (do_nhm_cstates) | 309 | if (do_nhm_cstates) |
259 | fprintf(stderr, " %5.2f", 100.0 * p->pc6/p->tsc); | 310 | fprintf(stderr, " %6.2f", 100.0 * p->pc6/p->tsc); |
260 | if (do_snb_cstates) | 311 | if (do_snb_cstates) |
261 | fprintf(stderr, " %5.2f", 100.0 * p->pc7/p->tsc); | 312 | fprintf(stderr, " %6.2f", 100.0 * p->pc7/p->tsc); |
262 | if (extra_msr_offset) | 313 | if (extra_msr_offset) |
263 | fprintf(stderr, " 0x%016llx", p->extra_msr); | 314 | fprintf(stderr, " 0x%016llx", p->extra_msr); |
264 | putc('\n', stderr); | 315 | putc('\n', stderr); |
@@ -267,12 +318,20 @@ void print_cnt(struct counters *p) | |||
267 | void print_counters(struct counters *counters) | 318 | void print_counters(struct counters *counters) |
268 | { | 319 | { |
269 | struct counters *cnt; | 320 | struct counters *cnt; |
321 | static int printed; | ||
322 | |||
270 | 323 | ||
271 | print_header(); | 324 | if (!printed || !summary_only) |
325 | print_header(); | ||
272 | 326 | ||
273 | if (num_cpus > 1) | 327 | if (num_cpus > 1) |
274 | print_cnt(cnt_average); | 328 | print_cnt(cnt_average); |
275 | 329 | ||
330 | printed = 1; | ||
331 | |||
332 | if (summary_only) | ||
333 | return; | ||
334 | |||
276 | for (cnt = counters; cnt != NULL; cnt = cnt->next) | 335 | for (cnt = counters; cnt != NULL; cnt = cnt->next) |
277 | print_cnt(cnt); | 336 | print_cnt(cnt); |
278 | 337 | ||
@@ -440,31 +499,51 @@ void compute_average(struct counters *delta, struct counters *avg) | |||
440 | free(sum); | 499 | free(sum); |
441 | } | 500 | } |
442 | 501 | ||
443 | void get_counters(struct counters *cnt) | 502 | int get_counters(struct counters *cnt) |
444 | { | 503 | { |
445 | for ( ; cnt; cnt = cnt->next) { | 504 | for ( ; cnt; cnt = cnt->next) { |
446 | cnt->tsc = get_msr(cnt->cpu, MSR_TSC); | 505 | |
447 | if (do_nhm_cstates) | 506 | if (cpu_migrate(cnt->cpu)) |
448 | cnt->c3 = get_msr(cnt->cpu, MSR_CORE_C3_RESIDENCY); | 507 | return -1; |
449 | if (do_nhm_cstates) | 508 | |
450 | cnt->c6 = get_msr(cnt->cpu, MSR_CORE_C6_RESIDENCY); | 509 | if (get_msr(cnt->cpu, MSR_TSC, &cnt->tsc)) |
451 | if (do_snb_cstates) | 510 | return -1; |
452 | cnt->c7 = get_msr(cnt->cpu, MSR_CORE_C7_RESIDENCY); | 511 | |
453 | if (has_aperf) | 512 | if (has_aperf) { |
454 | cnt->aperf = get_msr(cnt->cpu, MSR_APERF); | 513 | if (get_msr(cnt->cpu, MSR_APERF, &cnt->aperf)) |
455 | if (has_aperf) | 514 | return -1; |
456 | cnt->mperf = get_msr(cnt->cpu, MSR_MPERF); | 515 | if (get_msr(cnt->cpu, MSR_MPERF, &cnt->mperf)) |
457 | if (do_snb_cstates) | 516 | return -1; |
458 | cnt->pc2 = get_msr(cnt->cpu, MSR_PKG_C2_RESIDENCY); | 517 | } |
459 | if (do_nhm_cstates) | 518 | |
460 | cnt->pc3 = get_msr(cnt->cpu, MSR_PKG_C3_RESIDENCY); | 519 | if (do_nhm_cstates) { |
461 | if (do_nhm_cstates) | 520 | if (get_msr(cnt->cpu, MSR_CORE_C3_RESIDENCY, &cnt->c3)) |
462 | cnt->pc6 = get_msr(cnt->cpu, MSR_PKG_C6_RESIDENCY); | 521 | return -1; |
522 | if (get_msr(cnt->cpu, MSR_CORE_C6_RESIDENCY, &cnt->c6)) | ||
523 | return -1; | ||
524 | } | ||
525 | |||
463 | if (do_snb_cstates) | 526 | if (do_snb_cstates) |
464 | cnt->pc7 = get_msr(cnt->cpu, MSR_PKG_C7_RESIDENCY); | 527 | if (get_msr(cnt->cpu, MSR_CORE_C7_RESIDENCY, &cnt->c7)) |
528 | return -1; | ||
529 | |||
530 | if (do_nhm_cstates) { | ||
531 | if (get_msr(cnt->cpu, MSR_PKG_C3_RESIDENCY, &cnt->pc3)) | ||
532 | return -1; | ||
533 | if (get_msr(cnt->cpu, MSR_PKG_C6_RESIDENCY, &cnt->pc6)) | ||
534 | return -1; | ||
535 | } | ||
536 | if (do_snb_cstates) { | ||
537 | if (get_msr(cnt->cpu, MSR_PKG_C2_RESIDENCY, &cnt->pc2)) | ||
538 | return -1; | ||
539 | if (get_msr(cnt->cpu, MSR_PKG_C7_RESIDENCY, &cnt->pc7)) | ||
540 | return -1; | ||
541 | } | ||
465 | if (extra_msr_offset) | 542 | if (extra_msr_offset) |
466 | cnt->extra_msr = get_msr(cnt->cpu, extra_msr_offset); | 543 | if (get_msr(cnt->cpu, extra_msr_offset, &cnt->extra_msr)) |
544 | return -1; | ||
467 | } | 545 | } |
546 | return 0; | ||
468 | } | 547 | } |
469 | 548 | ||
470 | void print_nehalem_info(void) | 549 | void print_nehalem_info(void) |
@@ -475,7 +554,7 @@ void print_nehalem_info(void) | |||
475 | if (!do_nehalem_platform_info) | 554 | if (!do_nehalem_platform_info) |
476 | return; | 555 | return; |
477 | 556 | ||
478 | msr = get_msr(0, MSR_NEHALEM_PLATFORM_INFO); | 557 | get_msr(0, MSR_NEHALEM_PLATFORM_INFO, &msr); |
479 | 558 | ||
480 | ratio = (msr >> 40) & 0xFF; | 559 | ratio = (msr >> 40) & 0xFF; |
481 | fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n", | 560 | fprintf(stderr, "%d * %.0f = %.0f MHz max efficiency\n", |
@@ -491,7 +570,7 @@ void print_nehalem_info(void) | |||
491 | if (!do_nehalem_turbo_ratio_limit) | 570 | if (!do_nehalem_turbo_ratio_limit) |
492 | return; | 571 | return; |
493 | 572 | ||
494 | msr = get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT); | 573 | get_msr(0, MSR_NEHALEM_TURBO_RATIO_LIMIT, &msr); |
495 | 574 | ||
496 | ratio = (msr >> 24) & 0xFF; | 575 | ratio = (msr >> 24) & 0xFF; |
497 | if (ratio) | 576 | if (ratio) |
@@ -557,7 +636,8 @@ void insert_counters(struct counters **list, | |||
557 | return; | 636 | return; |
558 | } | 637 | } |
559 | 638 | ||
560 | show_cpu = 1; /* there is more than one CPU */ | 639 | if (!summary_only) |
640 | show_cpu = 1; /* there is more than one CPU */ | ||
561 | 641 | ||
562 | /* | 642 | /* |
563 | * insert on front of list. | 643 | * insert on front of list. |
@@ -575,13 +655,15 @@ void insert_counters(struct counters **list, | |||
575 | 655 | ||
576 | while (prev->next && (prev->next->pkg < new->pkg)) { | 656 | while (prev->next && (prev->next->pkg < new->pkg)) { |
577 | prev = prev->next; | 657 | prev = prev->next; |
578 | show_pkg = 1; /* there is more than 1 package */ | 658 | if (!summary_only) |
659 | show_pkg = 1; /* there is more than 1 package */ | ||
579 | } | 660 | } |
580 | 661 | ||
581 | while (prev->next && (prev->next->pkg == new->pkg) | 662 | while (prev->next && (prev->next->pkg == new->pkg) |
582 | && (prev->next->core < new->core)) { | 663 | && (prev->next->core < new->core)) { |
583 | prev = prev->next; | 664 | prev = prev->next; |
584 | show_core = 1; /* there is more than 1 core */ | 665 | if (!summary_only) |
666 | show_core = 1; /* there is more than 1 core */ | ||
585 | } | 667 | } |
586 | 668 | ||
587 | while (prev->next && (prev->next->pkg == new->pkg) | 669 | while (prev->next && (prev->next->pkg == new->pkg) |
@@ -681,7 +763,7 @@ int get_core_id(int cpu) | |||
681 | } | 763 | } |
682 | 764 | ||
683 | /* | 765 | /* |
684 | * run func(index, cpu) on every cpu in /proc/stat | 766 | * run func(pkg, core, cpu) on every cpu in /proc/stat |
685 | */ | 767 | */ |
686 | 768 | ||
687 | int for_all_cpus(void (func)(int, int, int)) | 769 | int for_all_cpus(void (func)(int, int, int)) |
@@ -717,18 +799,18 @@ int for_all_cpus(void (func)(int, int, int)) | |||
717 | 799 | ||
718 | void re_initialize(void) | 800 | void re_initialize(void) |
719 | { | 801 | { |
720 | printf("turbostat: topology changed, re-initializing.\n"); | ||
721 | free_all_counters(); | 802 | free_all_counters(); |
722 | num_cpus = for_all_cpus(alloc_new_counters); | 803 | num_cpus = for_all_cpus(alloc_new_counters); |
723 | need_reinitialize = 0; | 804 | cpu_mask_uninit(); |
724 | printf("num_cpus is now %d\n", num_cpus); | 805 | cpu_mask_init(num_cpus); |
806 | printf("turbostat: re-initialized with num_cpus %d\n", num_cpus); | ||
725 | } | 807 | } |
726 | 808 | ||
727 | void dummy(int pkg, int core, int cpu) { return; } | 809 | void dummy(int pkg, int core, int cpu) { return; } |
728 | /* | 810 | /* |
729 | * check to see if a cpu came on-line | 811 | * check to see if a cpu came on-line |
730 | */ | 812 | */ |
731 | void verify_num_cpus(void) | 813 | int verify_num_cpus(void) |
732 | { | 814 | { |
733 | int new_num_cpus; | 815 | int new_num_cpus; |
734 | 816 | ||
@@ -738,8 +820,9 @@ void verify_num_cpus(void) | |||
738 | if (verbose) | 820 | if (verbose) |
739 | printf("num_cpus was %d, is now %d\n", | 821 | printf("num_cpus was %d, is now %d\n", |
740 | num_cpus, new_num_cpus); | 822 | num_cpus, new_num_cpus); |
741 | need_reinitialize = 1; | 823 | return -1; |
742 | } | 824 | } |
825 | return 0; | ||
743 | } | 826 | } |
744 | 827 | ||
745 | void turbostat_loop() | 828 | void turbostat_loop() |
@@ -749,25 +832,25 @@ restart: | |||
749 | gettimeofday(&tv_even, (struct timezone *)NULL); | 832 | gettimeofday(&tv_even, (struct timezone *)NULL); |
750 | 833 | ||
751 | while (1) { | 834 | while (1) { |
752 | verify_num_cpus(); | 835 | if (verify_num_cpus()) { |
753 | if (need_reinitialize) { | ||
754 | re_initialize(); | 836 | re_initialize(); |
755 | goto restart; | 837 | goto restart; |
756 | } | 838 | } |
757 | sleep(interval_sec); | 839 | sleep(interval_sec); |
758 | get_counters(cnt_odd); | 840 | if (get_counters(cnt_odd)) { |
841 | re_initialize(); | ||
842 | goto restart; | ||
843 | } | ||
759 | gettimeofday(&tv_odd, (struct timezone *)NULL); | 844 | gettimeofday(&tv_odd, (struct timezone *)NULL); |
760 | |||
761 | compute_delta(cnt_odd, cnt_even, cnt_delta); | 845 | compute_delta(cnt_odd, cnt_even, cnt_delta); |
762 | timersub(&tv_odd, &tv_even, &tv_delta); | 846 | timersub(&tv_odd, &tv_even, &tv_delta); |
763 | compute_average(cnt_delta, cnt_average); | 847 | compute_average(cnt_delta, cnt_average); |
764 | print_counters(cnt_delta); | 848 | print_counters(cnt_delta); |
765 | if (need_reinitialize) { | 849 | sleep(interval_sec); |
850 | if (get_counters(cnt_even)) { | ||
766 | re_initialize(); | 851 | re_initialize(); |
767 | goto restart; | 852 | goto restart; |
768 | } | 853 | } |
769 | sleep(interval_sec); | ||
770 | get_counters(cnt_even); | ||
771 | gettimeofday(&tv_even, (struct timezone *)NULL); | 854 | gettimeofday(&tv_even, (struct timezone *)NULL); |
772 | compute_delta(cnt_even, cnt_odd, cnt_delta); | 855 | compute_delta(cnt_even, cnt_odd, cnt_delta); |
773 | timersub(&tv_even, &tv_odd, &tv_delta); | 856 | timersub(&tv_even, &tv_odd, &tv_delta); |
@@ -811,6 +894,8 @@ int has_nehalem_turbo_ratio_limit(unsigned int family, unsigned int model) | |||
811 | case 0x2C: /* Westmere EP - Gulftown */ | 894 | case 0x2C: /* Westmere EP - Gulftown */ |
812 | case 0x2A: /* SNB */ | 895 | case 0x2A: /* SNB */ |
813 | case 0x2D: /* SNB Xeon */ | 896 | case 0x2D: /* SNB Xeon */ |
897 | case 0x3A: /* IVB */ | ||
898 | case 0x3D: /* IVB Xeon */ | ||
814 | return 1; | 899 | return 1; |
815 | case 0x2E: /* Nehalem-EX Xeon - Beckton */ | 900 | case 0x2E: /* Nehalem-EX Xeon - Beckton */ |
816 | case 0x2F: /* Westmere-EX Xeon - Eagleton */ | 901 | case 0x2F: /* Westmere-EX Xeon - Eagleton */ |
@@ -951,6 +1036,7 @@ void turbostat_init() | |||
951 | check_super_user(); | 1036 | check_super_user(); |
952 | 1037 | ||
953 | num_cpus = for_all_cpus(alloc_new_counters); | 1038 | num_cpus = for_all_cpus(alloc_new_counters); |
1039 | cpu_mask_init(num_cpus); | ||
954 | 1040 | ||
955 | if (verbose) | 1041 | if (verbose) |
956 | print_nehalem_info(); | 1042 | print_nehalem_info(); |
@@ -1003,8 +1089,11 @@ void cmdline(int argc, char **argv) | |||
1003 | 1089 | ||
1004 | progname = argv[0]; | 1090 | progname = argv[0]; |
1005 | 1091 | ||
1006 | while ((opt = getopt(argc, argv, "+vi:M:")) != -1) { | 1092 | while ((opt = getopt(argc, argv, "+svi:M:")) != -1) { |
1007 | switch (opt) { | 1093 | switch (opt) { |
1094 | case 's': | ||
1095 | summary_only++; | ||
1096 | break; | ||
1008 | case 'v': | 1097 | case 'v': |
1009 | verbose++; | 1098 | verbose++; |
1010 | break; | 1099 | break; |
diff --git a/tools/testing/ktest/compare-ktest-sample.pl b/tools/testing/ktest/compare-ktest-sample.pl index 9a571e71683c..a373a5bfff68 100755 --- a/tools/testing/ktest/compare-ktest-sample.pl +++ b/tools/testing/ktest/compare-ktest-sample.pl | |||
@@ -2,7 +2,9 @@ | |||
2 | 2 | ||
3 | open (IN,"ktest.pl"); | 3 | open (IN,"ktest.pl"); |
4 | while (<IN>) { | 4 | while (<IN>) { |
5 | # hashes are now used | ||
5 | if (/\$opt\{"?([A-Z].*?)(\[.*\])?"?\}/ || | 6 | if (/\$opt\{"?([A-Z].*?)(\[.*\])?"?\}/ || |
7 | /^\s*"?([A-Z].*?)"?\s*=>\s*/ || | ||
6 | /set_test_option\("(.*?)"/) { | 8 | /set_test_option\("(.*?)"/) { |
7 | $opt{$1} = 1; | 9 | $opt{$1} = 1; |
8 | } | 10 | } |
@@ -11,7 +13,7 @@ close IN; | |||
11 | 13 | ||
12 | open (IN, "sample.conf"); | 14 | open (IN, "sample.conf"); |
13 | while (<IN>) { | 15 | while (<IN>) { |
14 | if (/^\s*#?\s*(\S+)\s*=/) { | 16 | if (/^\s*#?\s*([A-Z]\S*)\s*=/) { |
15 | $samp{$1} = 1; | 17 | $samp{$1} = 1; |
16 | } | 18 | } |
17 | } | 19 | } |
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 8b4c2535b266..95d6a6f7c33a 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl | |||
@@ -18,40 +18,51 @@ $| = 1; | |||
18 | my %opt; | 18 | my %opt; |
19 | my %repeat_tests; | 19 | my %repeat_tests; |
20 | my %repeats; | 20 | my %repeats; |
21 | my %default; | ||
22 | 21 | ||
23 | #default opts | 22 | #default opts |
24 | $default{"NUM_TESTS"} = 1; | 23 | my %default = ( |
25 | $default{"REBOOT_TYPE"} = "grub"; | 24 | "NUM_TESTS" => 1, |
26 | $default{"TEST_TYPE"} = "test"; | 25 | "TEST_TYPE" => "build", |
27 | $default{"BUILD_TYPE"} = "randconfig"; | 26 | "BUILD_TYPE" => "randconfig", |
28 | $default{"MAKE_CMD"} = "make"; | 27 | "MAKE_CMD" => "make", |
29 | $default{"TIMEOUT"} = 120; | 28 | "TIMEOUT" => 120, |
30 | $default{"TMP_DIR"} = "/tmp/ktest/\${MACHINE}"; | 29 | "TMP_DIR" => "/tmp/ktest/\${MACHINE}", |
31 | $default{"SLEEP_TIME"} = 60; # sleep time between tests | 30 | "SLEEP_TIME" => 60, # sleep time between tests |
32 | $default{"BUILD_NOCLEAN"} = 0; | 31 | "BUILD_NOCLEAN" => 0, |
33 | $default{"REBOOT_ON_ERROR"} = 0; | 32 | "REBOOT_ON_ERROR" => 0, |
34 | $default{"POWEROFF_ON_ERROR"} = 0; | 33 | "POWEROFF_ON_ERROR" => 0, |
35 | $default{"REBOOT_ON_SUCCESS"} = 1; | 34 | "REBOOT_ON_SUCCESS" => 1, |
36 | $default{"POWEROFF_ON_SUCCESS"} = 0; | 35 | "POWEROFF_ON_SUCCESS" => 0, |
37 | $default{"BUILD_OPTIONS"} = ""; | 36 | "BUILD_OPTIONS" => "", |
38 | $default{"BISECT_SLEEP_TIME"} = 60; # sleep time between bisects | 37 | "BISECT_SLEEP_TIME" => 60, # sleep time between bisects |
39 | $default{"PATCHCHECK_SLEEP_TIME"} = 60; # sleep time between patch checks | 38 | "PATCHCHECK_SLEEP_TIME" => 60, # sleep time between patch checks |
40 | $default{"CLEAR_LOG"} = 0; | 39 | "CLEAR_LOG" => 0, |
41 | $default{"BISECT_MANUAL"} = 0; | 40 | "BISECT_MANUAL" => 0, |
42 | $default{"BISECT_SKIP"} = 1; | 41 | "BISECT_SKIP" => 1, |
43 | $default{"SUCCESS_LINE"} = "login:"; | 42 | "SUCCESS_LINE" => "login:", |
44 | $default{"DETECT_TRIPLE_FAULT"} = 1; | 43 | "DETECT_TRIPLE_FAULT" => 1, |
45 | $default{"NO_INSTALL"} = 0; | 44 | "NO_INSTALL" => 0, |
46 | $default{"BOOTED_TIMEOUT"} = 1; | 45 | "BOOTED_TIMEOUT" => 1, |
47 | $default{"DIE_ON_FAILURE"} = 1; | 46 | "DIE_ON_FAILURE" => 1, |
48 | $default{"SSH_EXEC"} = "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND"; | 47 | "SSH_EXEC" => "ssh \$SSH_USER\@\$MACHINE \$SSH_COMMAND", |
49 | $default{"SCP_TO_TARGET"} = "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE"; | 48 | "SCP_TO_TARGET" => "scp \$SRC_FILE \$SSH_USER\@\$MACHINE:\$DST_FILE", |
50 | $default{"REBOOT"} = "ssh \$SSH_USER\@\$MACHINE reboot"; | 49 | "SCP_TO_TARGET_INSTALL" => "\${SCP_TO_TARGET}", |
51 | $default{"STOP_AFTER_SUCCESS"} = 10; | 50 | "REBOOT" => "ssh \$SSH_USER\@\$MACHINE reboot", |
52 | $default{"STOP_AFTER_FAILURE"} = 60; | 51 | "STOP_AFTER_SUCCESS" => 10, |
53 | $default{"STOP_TEST_AFTER"} = 600; | 52 | "STOP_AFTER_FAILURE" => 60, |
54 | $default{"LOCALVERSION"} = "-test"; | 53 | "STOP_TEST_AFTER" => 600, |
54 | |||
55 | # required, and we will ask users if they don't have them but we keep the default | ||
56 | # value something that is common. | ||
57 | "REBOOT_TYPE" => "grub", | ||
58 | "LOCALVERSION" => "-test", | ||
59 | "SSH_USER" => "root", | ||
60 | "BUILD_TARGET" => "arch/x86/boot/bzImage", | ||
61 | "TARGET_IMAGE" => "/boot/vmlinuz-test", | ||
62 | |||
63 | "LOG_FILE" => undef, | ||
64 | "IGNORE_UNUSED" => 0, | ||
65 | ); | ||
55 | 66 | ||
56 | my $ktest_config; | 67 | my $ktest_config; |
57 | my $version; | 68 | my $version; |
@@ -73,12 +84,16 @@ my $reboot_script; | |||
73 | my $power_cycle; | 84 | my $power_cycle; |
74 | my $reboot; | 85 | my $reboot; |
75 | my $reboot_on_error; | 86 | my $reboot_on_error; |
87 | my $switch_to_good; | ||
88 | my $switch_to_test; | ||
76 | my $poweroff_on_error; | 89 | my $poweroff_on_error; |
90 | my $reboot_on_success; | ||
77 | my $die_on_failure; | 91 | my $die_on_failure; |
78 | my $powercycle_after_reboot; | 92 | my $powercycle_after_reboot; |
79 | my $poweroff_after_halt; | 93 | my $poweroff_after_halt; |
80 | my $ssh_exec; | 94 | my $ssh_exec; |
81 | my $scp_to_target; | 95 | my $scp_to_target; |
96 | my $scp_to_target_install; | ||
82 | my $power_off; | 97 | my $power_off; |
83 | my $grub_menu; | 98 | my $grub_menu; |
84 | my $grub_number; | 99 | my $grub_number; |
@@ -92,17 +107,24 @@ my $start_minconfig; | |||
92 | my $start_minconfig_defined; | 107 | my $start_minconfig_defined; |
93 | my $output_minconfig; | 108 | my $output_minconfig; |
94 | my $ignore_config; | 109 | my $ignore_config; |
110 | my $ignore_errors; | ||
95 | my $addconfig; | 111 | my $addconfig; |
96 | my $in_bisect = 0; | 112 | my $in_bisect = 0; |
97 | my $bisect_bad = ""; | 113 | my $bisect_bad_commit = ""; |
98 | my $reverse_bisect; | 114 | my $reverse_bisect; |
99 | my $bisect_manual; | 115 | my $bisect_manual; |
100 | my $bisect_skip; | 116 | my $bisect_skip; |
101 | my $config_bisect_good; | 117 | my $config_bisect_good; |
118 | my $bisect_ret_good; | ||
119 | my $bisect_ret_bad; | ||
120 | my $bisect_ret_skip; | ||
121 | my $bisect_ret_abort; | ||
122 | my $bisect_ret_default; | ||
102 | my $in_patchcheck = 0; | 123 | my $in_patchcheck = 0; |
103 | my $run_test; | 124 | my $run_test; |
104 | my $redirect; | 125 | my $redirect; |
105 | my $buildlog; | 126 | my $buildlog; |
127 | my $testlog; | ||
106 | my $dmesg; | 128 | my $dmesg; |
107 | my $monitor_fp; | 129 | my $monitor_fp; |
108 | my $monitor_pid; | 130 | my $monitor_pid; |
@@ -112,6 +134,7 @@ my $bisect_sleep_time; | |||
112 | my $patchcheck_sleep_time; | 134 | my $patchcheck_sleep_time; |
113 | my $ignore_warnings; | 135 | my $ignore_warnings; |
114 | my $store_failures; | 136 | my $store_failures; |
137 | my $store_successes; | ||
115 | my $test_name; | 138 | my $test_name; |
116 | my $timeout; | 139 | my $timeout; |
117 | my $booted_timeout; | 140 | my $booted_timeout; |
@@ -124,10 +147,34 @@ my $stop_after_failure; | |||
124 | my $stop_test_after; | 147 | my $stop_test_after; |
125 | my $build_target; | 148 | my $build_target; |
126 | my $target_image; | 149 | my $target_image; |
150 | my $checkout; | ||
127 | my $localversion; | 151 | my $localversion; |
128 | my $iteration = 0; | 152 | my $iteration = 0; |
129 | my $successes = 0; | 153 | my $successes = 0; |
130 | 154 | ||
155 | my $bisect_good; | ||
156 | my $bisect_bad; | ||
157 | my $bisect_type; | ||
158 | my $bisect_start; | ||
159 | my $bisect_replay; | ||
160 | my $bisect_files; | ||
161 | my $bisect_reverse; | ||
162 | my $bisect_check; | ||
163 | |||
164 | my $config_bisect; | ||
165 | my $config_bisect_type; | ||
166 | |||
167 | my $patchcheck_type; | ||
168 | my $patchcheck_start; | ||
169 | my $patchcheck_end; | ||
170 | |||
171 | # set when a test is something other that just building or install | ||
172 | # which would require more options. | ||
173 | my $buildonly = 1; | ||
174 | |||
175 | # set when creating a new config | ||
176 | my $newconfig = 0; | ||
177 | |||
131 | my %entered_configs; | 178 | my %entered_configs; |
132 | my %config_help; | 179 | my %config_help; |
133 | my %variable; | 180 | my %variable; |
@@ -136,11 +183,101 @@ my %force_config; | |||
136 | # do not force reboots on config problems | 183 | # do not force reboots on config problems |
137 | my $no_reboot = 1; | 184 | my $no_reboot = 1; |
138 | 185 | ||
186 | my %option_map = ( | ||
187 | "MACHINE" => \$machine, | ||
188 | "SSH_USER" => \$ssh_user, | ||
189 | "TMP_DIR" => \$tmpdir, | ||
190 | "OUTPUT_DIR" => \$outputdir, | ||
191 | "BUILD_DIR" => \$builddir, | ||
192 | "TEST_TYPE" => \$test_type, | ||
193 | "BUILD_TYPE" => \$build_type, | ||
194 | "BUILD_OPTIONS" => \$build_options, | ||
195 | "PRE_BUILD" => \$pre_build, | ||
196 | "POST_BUILD" => \$post_build, | ||
197 | "PRE_BUILD_DIE" => \$pre_build_die, | ||
198 | "POST_BUILD_DIE" => \$post_build_die, | ||
199 | "POWER_CYCLE" => \$power_cycle, | ||
200 | "REBOOT" => \$reboot, | ||
201 | "BUILD_NOCLEAN" => \$noclean, | ||
202 | "MIN_CONFIG" => \$minconfig, | ||
203 | "OUTPUT_MIN_CONFIG" => \$output_minconfig, | ||
204 | "START_MIN_CONFIG" => \$start_minconfig, | ||
205 | "IGNORE_CONFIG" => \$ignore_config, | ||
206 | "TEST" => \$run_test, | ||
207 | "ADD_CONFIG" => \$addconfig, | ||
208 | "REBOOT_TYPE" => \$reboot_type, | ||
209 | "GRUB_MENU" => \$grub_menu, | ||
210 | "POST_INSTALL" => \$post_install, | ||
211 | "NO_INSTALL" => \$no_install, | ||
212 | "REBOOT_SCRIPT" => \$reboot_script, | ||
213 | "REBOOT_ON_ERROR" => \$reboot_on_error, | ||
214 | "SWITCH_TO_GOOD" => \$switch_to_good, | ||
215 | "SWITCH_TO_TEST" => \$switch_to_test, | ||
216 | "POWEROFF_ON_ERROR" => \$poweroff_on_error, | ||
217 | "REBOOT_ON_SUCCESS" => \$reboot_on_success, | ||
218 | "DIE_ON_FAILURE" => \$die_on_failure, | ||
219 | "POWER_OFF" => \$power_off, | ||
220 | "POWERCYCLE_AFTER_REBOOT" => \$powercycle_after_reboot, | ||
221 | "POWEROFF_AFTER_HALT" => \$poweroff_after_halt, | ||
222 | "SLEEP_TIME" => \$sleep_time, | ||
223 | "BISECT_SLEEP_TIME" => \$bisect_sleep_time, | ||
224 | "PATCHCHECK_SLEEP_TIME" => \$patchcheck_sleep_time, | ||
225 | "IGNORE_WARNINGS" => \$ignore_warnings, | ||
226 | "IGNORE_ERRORS" => \$ignore_errors, | ||
227 | "BISECT_MANUAL" => \$bisect_manual, | ||
228 | "BISECT_SKIP" => \$bisect_skip, | ||
229 | "CONFIG_BISECT_GOOD" => \$config_bisect_good, | ||
230 | "BISECT_RET_GOOD" => \$bisect_ret_good, | ||
231 | "BISECT_RET_BAD" => \$bisect_ret_bad, | ||
232 | "BISECT_RET_SKIP" => \$bisect_ret_skip, | ||
233 | "BISECT_RET_ABORT" => \$bisect_ret_abort, | ||
234 | "BISECT_RET_DEFAULT" => \$bisect_ret_default, | ||
235 | "STORE_FAILURES" => \$store_failures, | ||
236 | "STORE_SUCCESSES" => \$store_successes, | ||
237 | "TEST_NAME" => \$test_name, | ||
238 | "TIMEOUT" => \$timeout, | ||
239 | "BOOTED_TIMEOUT" => \$booted_timeout, | ||
240 | "CONSOLE" => \$console, | ||
241 | "DETECT_TRIPLE_FAULT" => \$detect_triplefault, | ||
242 | "SUCCESS_LINE" => \$success_line, | ||
243 | "REBOOT_SUCCESS_LINE" => \$reboot_success_line, | ||
244 | "STOP_AFTER_SUCCESS" => \$stop_after_success, | ||
245 | "STOP_AFTER_FAILURE" => \$stop_after_failure, | ||
246 | "STOP_TEST_AFTER" => \$stop_test_after, | ||
247 | "BUILD_TARGET" => \$build_target, | ||
248 | "SSH_EXEC" => \$ssh_exec, | ||
249 | "SCP_TO_TARGET" => \$scp_to_target, | ||
250 | "SCP_TO_TARGET_INSTALL" => \$scp_to_target_install, | ||
251 | "CHECKOUT" => \$checkout, | ||
252 | "TARGET_IMAGE" => \$target_image, | ||
253 | "LOCALVERSION" => \$localversion, | ||
254 | |||
255 | "BISECT_GOOD" => \$bisect_good, | ||
256 | "BISECT_BAD" => \$bisect_bad, | ||
257 | "BISECT_TYPE" => \$bisect_type, | ||
258 | "BISECT_START" => \$bisect_start, | ||
259 | "BISECT_REPLAY" => \$bisect_replay, | ||
260 | "BISECT_FILES" => \$bisect_files, | ||
261 | "BISECT_REVERSE" => \$bisect_reverse, | ||
262 | "BISECT_CHECK" => \$bisect_check, | ||
263 | |||
264 | "CONFIG_BISECT" => \$config_bisect, | ||
265 | "CONFIG_BISECT_TYPE" => \$config_bisect_type, | ||
266 | |||
267 | "PATCHCHECK_TYPE" => \$patchcheck_type, | ||
268 | "PATCHCHECK_START" => \$patchcheck_start, | ||
269 | "PATCHCHECK_END" => \$patchcheck_end, | ||
270 | ); | ||
271 | |||
272 | # Options may be used by other options, record them. | ||
273 | my %used_options; | ||
274 | |||
139 | # default variables that can be used | 275 | # default variables that can be used |
140 | chomp ($variable{"PWD"} = `pwd`); | 276 | chomp ($variable{"PWD"} = `pwd`); |
141 | 277 | ||
142 | $config_help{"MACHINE"} = << "EOF" | 278 | $config_help{"MACHINE"} = << "EOF" |
143 | The machine hostname that you will test. | 279 | The machine hostname that you will test. |
280 | For build only tests, it is still needed to differentiate log files. | ||
144 | EOF | 281 | EOF |
145 | ; | 282 | ; |
146 | $config_help{"SSH_USER"} = << "EOF" | 283 | $config_help{"SSH_USER"} = << "EOF" |
@@ -150,11 +287,15 @@ EOF | |||
150 | ; | 287 | ; |
151 | $config_help{"BUILD_DIR"} = << "EOF" | 288 | $config_help{"BUILD_DIR"} = << "EOF" |
152 | The directory that contains the Linux source code (full path). | 289 | The directory that contains the Linux source code (full path). |
290 | You can use \${PWD} that will be the path where ktest.pl is run, or use | ||
291 | \${THIS_DIR} which is assigned \${PWD} but may be changed later. | ||
153 | EOF | 292 | EOF |
154 | ; | 293 | ; |
155 | $config_help{"OUTPUT_DIR"} = << "EOF" | 294 | $config_help{"OUTPUT_DIR"} = << "EOF" |
156 | The directory that the objects will be built (full path). | 295 | The directory that the objects will be built (full path). |
157 | (can not be same as BUILD_DIR) | 296 | (can not be same as BUILD_DIR) |
297 | You can use \${PWD} that will be the path where ktest.pl is run, or use | ||
298 | \${THIS_DIR} which is assigned \${PWD} but may be changed later. | ||
158 | EOF | 299 | EOF |
159 | ; | 300 | ; |
160 | $config_help{"BUILD_TARGET"} = << "EOF" | 301 | $config_help{"BUILD_TARGET"} = << "EOF" |
@@ -162,6 +303,11 @@ $config_help{"BUILD_TARGET"} = << "EOF" | |||
162 | (relative to OUTPUT_DIR) | 303 | (relative to OUTPUT_DIR) |
163 | EOF | 304 | EOF |
164 | ; | 305 | ; |
306 | $config_help{"BUILD_OPTIONS"} = << "EOF" | ||
307 | Options to add to \"make\" when building. | ||
308 | i.e. -j20 | ||
309 | EOF | ||
310 | ; | ||
165 | $config_help{"TARGET_IMAGE"} = << "EOF" | 311 | $config_help{"TARGET_IMAGE"} = << "EOF" |
166 | The place to put your image on the test machine. | 312 | The place to put your image on the test machine. |
167 | EOF | 313 | EOF |
@@ -227,20 +373,36 @@ $config_help{"REBOOT_SCRIPT"} = << "EOF" | |||
227 | EOF | 373 | EOF |
228 | ; | 374 | ; |
229 | 375 | ||
230 | sub read_yn { | 376 | sub read_prompt { |
231 | my ($prompt) = @_; | 377 | my ($cancel, $prompt) = @_; |
232 | 378 | ||
233 | my $ans; | 379 | my $ans; |
234 | 380 | ||
235 | for (;;) { | 381 | for (;;) { |
236 | print "$prompt [Y/n] "; | 382 | if ($cancel) { |
383 | print "$prompt [y/n/C] "; | ||
384 | } else { | ||
385 | print "$prompt [Y/n] "; | ||
386 | } | ||
237 | $ans = <STDIN>; | 387 | $ans = <STDIN>; |
238 | chomp $ans; | 388 | chomp $ans; |
239 | if ($ans =~ /^\s*$/) { | 389 | if ($ans =~ /^\s*$/) { |
240 | $ans = "y"; | 390 | if ($cancel) { |
391 | $ans = "c"; | ||
392 | } else { | ||
393 | $ans = "y"; | ||
394 | } | ||
241 | } | 395 | } |
242 | last if ($ans =~ /^y$/i || $ans =~ /^n$/i); | 396 | last if ($ans =~ /^y$/i || $ans =~ /^n$/i); |
243 | print "Please answer either 'y' or 'n'.\n"; | 397 | if ($cancel) { |
398 | last if ($ans =~ /^c$/i); | ||
399 | print "Please answer either 'y', 'n' or 'c'.\n"; | ||
400 | } else { | ||
401 | print "Please answer either 'y' or 'n'.\n"; | ||
402 | } | ||
403 | } | ||
404 | if ($ans =~ /^c/i) { | ||
405 | exit; | ||
244 | } | 406 | } |
245 | if ($ans !~ /^y$/i) { | 407 | if ($ans !~ /^y$/i) { |
246 | return 0; | 408 | return 0; |
@@ -248,6 +410,18 @@ sub read_yn { | |||
248 | return 1; | 410 | return 1; |
249 | } | 411 | } |
250 | 412 | ||
413 | sub read_yn { | ||
414 | my ($prompt) = @_; | ||
415 | |||
416 | return read_prompt 0, $prompt; | ||
417 | } | ||
418 | |||
419 | sub read_ync { | ||
420 | my ($prompt) = @_; | ||
421 | |||
422 | return read_prompt 1, $prompt; | ||
423 | } | ||
424 | |||
251 | sub get_ktest_config { | 425 | sub get_ktest_config { |
252 | my ($config) = @_; | 426 | my ($config) = @_; |
253 | my $ans; | 427 | my $ans; |
@@ -261,7 +435,7 @@ sub get_ktest_config { | |||
261 | 435 | ||
262 | for (;;) { | 436 | for (;;) { |
263 | print "$config = "; | 437 | print "$config = "; |
264 | if (defined($default{$config})) { | 438 | if (defined($default{$config}) && length($default{$config})) { |
265 | print "\[$default{$config}\] "; | 439 | print "\[$default{$config}\] "; |
266 | } | 440 | } |
267 | $ans = <STDIN>; | 441 | $ans = <STDIN>; |
@@ -274,22 +448,37 @@ sub get_ktest_config { | |||
274 | next; | 448 | next; |
275 | } | 449 | } |
276 | } | 450 | } |
277 | $entered_configs{$config} = process_variables($ans); | 451 | $entered_configs{$config} = ${ans}; |
278 | last; | 452 | last; |
279 | } | 453 | } |
280 | } | 454 | } |
281 | 455 | ||
282 | sub get_ktest_configs { | 456 | sub get_ktest_configs { |
283 | get_ktest_config("MACHINE"); | 457 | get_ktest_config("MACHINE"); |
284 | get_ktest_config("SSH_USER"); | ||
285 | get_ktest_config("BUILD_DIR"); | 458 | get_ktest_config("BUILD_DIR"); |
286 | get_ktest_config("OUTPUT_DIR"); | 459 | get_ktest_config("OUTPUT_DIR"); |
287 | get_ktest_config("BUILD_TARGET"); | 460 | |
288 | get_ktest_config("TARGET_IMAGE"); | 461 | if ($newconfig) { |
289 | get_ktest_config("POWER_CYCLE"); | 462 | get_ktest_config("BUILD_OPTIONS"); |
290 | get_ktest_config("CONSOLE"); | 463 | } |
464 | |||
465 | # options required for other than just building a kernel | ||
466 | if (!$buildonly) { | ||
467 | get_ktest_config("POWER_CYCLE"); | ||
468 | get_ktest_config("CONSOLE"); | ||
469 | } | ||
470 | |||
471 | # options required for install and more | ||
472 | if ($buildonly != 1) { | ||
473 | get_ktest_config("SSH_USER"); | ||
474 | get_ktest_config("BUILD_TARGET"); | ||
475 | get_ktest_config("TARGET_IMAGE"); | ||
476 | } | ||
477 | |||
291 | get_ktest_config("LOCALVERSION"); | 478 | get_ktest_config("LOCALVERSION"); |
292 | 479 | ||
480 | return if ($buildonly); | ||
481 | |||
293 | my $rtype = $opt{"REBOOT_TYPE"}; | 482 | my $rtype = $opt{"REBOOT_TYPE"}; |
294 | 483 | ||
295 | if (!defined($rtype)) { | 484 | if (!defined($rtype)) { |
@@ -303,8 +492,6 @@ sub get_ktest_configs { | |||
303 | 492 | ||
304 | if ($rtype eq "grub") { | 493 | if ($rtype eq "grub") { |
305 | get_ktest_config("GRUB_MENU"); | 494 | get_ktest_config("GRUB_MENU"); |
306 | } else { | ||
307 | get_ktest_config("REBOOT_SCRIPT"); | ||
308 | } | 495 | } |
309 | } | 496 | } |
310 | 497 | ||
@@ -334,6 +521,10 @@ sub process_variables { | |||
334 | } else { | 521 | } else { |
335 | # put back the origin piece. | 522 | # put back the origin piece. |
336 | $retval = "$retval\$\{$var\}"; | 523 | $retval = "$retval\$\{$var\}"; |
524 | # This could be an option that is used later, save | ||
525 | # it so we don't warn if this option is not one of | ||
526 | # ktests options. | ||
527 | $used_options{$var} = 1; | ||
337 | } | 528 | } |
338 | $value = $end; | 529 | $value = $end; |
339 | } | 530 | } |
@@ -348,6 +539,19 @@ sub process_variables { | |||
348 | sub set_value { | 539 | sub set_value { |
349 | my ($lvalue, $rvalue, $override, $overrides, $name) = @_; | 540 | my ($lvalue, $rvalue, $override, $overrides, $name) = @_; |
350 | 541 | ||
542 | my $prvalue = process_variables($rvalue); | ||
543 | |||
544 | if ($buildonly && $lvalue =~ /^TEST_TYPE(\[.*\])?$/ && $prvalue ne "build") { | ||
545 | # Note if a test is something other than build, then we | ||
546 | # will need other manditory options. | ||
547 | if ($prvalue ne "install") { | ||
548 | $buildonly = 0; | ||
549 | } else { | ||
550 | # install still limits some manditory options. | ||
551 | $buildonly = 2; | ||
552 | } | ||
553 | } | ||
554 | |||
351 | if (defined($opt{$lvalue})) { | 555 | if (defined($opt{$lvalue})) { |
352 | if (!$override || defined(${$overrides}{$lvalue})) { | 556 | if (!$override || defined(${$overrides}{$lvalue})) { |
353 | my $extra = ""; | 557 | my $extra = ""; |
@@ -356,13 +560,12 @@ sub set_value { | |||
356 | } | 560 | } |
357 | die "$name: $.: Option $lvalue defined more than once!\n$extra"; | 561 | die "$name: $.: Option $lvalue defined more than once!\n$extra"; |
358 | } | 562 | } |
359 | ${$overrides}{$lvalue} = $rvalue; | 563 | ${$overrides}{$lvalue} = $prvalue; |
360 | } | 564 | } |
361 | if ($rvalue =~ /^\s*$/) { | 565 | if ($rvalue =~ /^\s*$/) { |
362 | delete $opt{$lvalue}; | 566 | delete $opt{$lvalue}; |
363 | } else { | 567 | } else { |
364 | $rvalue = process_variables($rvalue); | 568 | $opt{$lvalue} = $prvalue; |
365 | $opt{$lvalue} = $rvalue; | ||
366 | } | 569 | } |
367 | } | 570 | } |
368 | 571 | ||
@@ -712,6 +915,15 @@ sub __read_config { | |||
712 | return $test_case; | 915 | return $test_case; |
713 | } | 916 | } |
714 | 917 | ||
918 | sub get_test_case { | ||
919 | print "What test case would you like to run?\n"; | ||
920 | print " (build, install or boot)\n"; | ||
921 | print " Other tests are available but require editing the config file\n"; | ||
922 | my $ans = <STDIN>; | ||
923 | chomp $ans; | ||
924 | $default{"TEST_TYPE"} = $ans; | ||
925 | } | ||
926 | |||
715 | sub read_config { | 927 | sub read_config { |
716 | my ($config) = @_; | 928 | my ($config) = @_; |
717 | 929 | ||
@@ -726,10 +938,7 @@ sub read_config { | |||
726 | # was a test specified? | 938 | # was a test specified? |
727 | if (!$test_case) { | 939 | if (!$test_case) { |
728 | print "No test case specified.\n"; | 940 | print "No test case specified.\n"; |
729 | print "What test case would you like to run?\n"; | 941 | get_test_case; |
730 | my $ans = <STDIN>; | ||
731 | chomp $ans; | ||
732 | $default{"TEST_TYPE"} = $ans; | ||
733 | } | 942 | } |
734 | 943 | ||
735 | # set any defaults | 944 | # set any defaults |
@@ -739,6 +948,37 @@ sub read_config { | |||
739 | $opt{$default} = $default{$default}; | 948 | $opt{$default} = $default{$default}; |
740 | } | 949 | } |
741 | } | 950 | } |
951 | |||
952 | if ($opt{"IGNORE_UNUSED"} == 1) { | ||
953 | return; | ||
954 | } | ||
955 | |||
956 | my %not_used; | ||
957 | |||
958 | # check if there are any stragglers (typos?) | ||
959 | foreach my $option (keys %opt) { | ||
960 | my $op = $option; | ||
961 | # remove per test labels. | ||
962 | $op =~ s/\[.*\]//; | ||
963 | if (!exists($option_map{$op}) && | ||
964 | !exists($default{$op}) && | ||
965 | !exists($used_options{$op})) { | ||
966 | $not_used{$op} = 1; | ||
967 | } | ||
968 | } | ||
969 | |||
970 | if (%not_used) { | ||
971 | my $s = "s are"; | ||
972 | $s = " is" if (keys %not_used == 1); | ||
973 | print "The following option$s not used; could be a typo:\n"; | ||
974 | foreach my $option (keys %not_used) { | ||
975 | print "$option\n"; | ||
976 | } | ||
977 | print "Set IGRNORE_UNUSED = 1 to have ktest ignore unused variables\n"; | ||
978 | if (!read_yn "Do you want to continue?") { | ||
979 | exit -1; | ||
980 | } | ||
981 | } | ||
742 | } | 982 | } |
743 | 983 | ||
744 | sub __eval_option { | 984 | sub __eval_option { |
@@ -873,6 +1113,16 @@ sub reboot { | |||
873 | } | 1113 | } |
874 | } | 1114 | } |
875 | 1115 | ||
1116 | sub reboot_to_good { | ||
1117 | my ($time) = @_; | ||
1118 | |||
1119 | if (defined($switch_to_good)) { | ||
1120 | run_command $switch_to_good; | ||
1121 | } | ||
1122 | |||
1123 | reboot $time; | ||
1124 | } | ||
1125 | |||
876 | sub do_not_reboot { | 1126 | sub do_not_reboot { |
877 | my $i = $iteration; | 1127 | my $i = $iteration; |
878 | 1128 | ||
@@ -889,7 +1139,7 @@ sub dodie { | |||
889 | if ($reboot_on_error && !do_not_reboot) { | 1139 | if ($reboot_on_error && !do_not_reboot) { |
890 | 1140 | ||
891 | doprint "REBOOTING\n"; | 1141 | doprint "REBOOTING\n"; |
892 | reboot; | 1142 | reboot_to_good; |
893 | 1143 | ||
894 | } elsif ($poweroff_on_error && defined($power_off)) { | 1144 | } elsif ($poweroff_on_error && defined($power_off)) { |
895 | doprint "POWERING OFF\n"; | 1145 | doprint "POWERING OFF\n"; |
@@ -975,6 +1225,43 @@ sub wait_for_monitor { | |||
975 | print "** Monitor flushed **\n"; | 1225 | print "** Monitor flushed **\n"; |
976 | } | 1226 | } |
977 | 1227 | ||
1228 | sub save_logs { | ||
1229 | my ($result, $basedir) = @_; | ||
1230 | my @t = localtime; | ||
1231 | my $date = sprintf "%04d%02d%02d%02d%02d%02d", | ||
1232 | 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; | ||
1233 | |||
1234 | my $type = $build_type; | ||
1235 | if ($type =~ /useconfig/) { | ||
1236 | $type = "useconfig"; | ||
1237 | } | ||
1238 | |||
1239 | my $dir = "$machine-$test_type-$type-$result-$date"; | ||
1240 | |||
1241 | $dir = "$basedir/$dir"; | ||
1242 | |||
1243 | if (!-d $dir) { | ||
1244 | mkpath($dir) or | ||
1245 | die "can't create $dir"; | ||
1246 | } | ||
1247 | |||
1248 | my %files = ( | ||
1249 | "config" => $output_config, | ||
1250 | "buildlog" => $buildlog, | ||
1251 | "dmesg" => $dmesg, | ||
1252 | "testlog" => $testlog, | ||
1253 | ); | ||
1254 | |||
1255 | while (my ($name, $source) = each(%files)) { | ||
1256 | if (-f "$source") { | ||
1257 | cp "$source", "$dir/$name" or | ||
1258 | die "failed to copy $source"; | ||
1259 | } | ||
1260 | } | ||
1261 | |||
1262 | doprint "*** Saved info to $dir ***\n"; | ||
1263 | } | ||
1264 | |||
978 | sub fail { | 1265 | sub fail { |
979 | 1266 | ||
980 | if ($die_on_failure) { | 1267 | if ($die_on_failure) { |
@@ -988,7 +1275,7 @@ sub fail { | |||
988 | # no need to reboot for just building. | 1275 | # no need to reboot for just building. |
989 | if (!do_not_reboot) { | 1276 | if (!do_not_reboot) { |
990 | doprint "REBOOTING\n"; | 1277 | doprint "REBOOTING\n"; |
991 | reboot $sleep_time; | 1278 | reboot_to_good $sleep_time; |
992 | } | 1279 | } |
993 | 1280 | ||
994 | my $name = ""; | 1281 | my $name = ""; |
@@ -1003,38 +1290,9 @@ sub fail { | |||
1003 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; | 1290 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; |
1004 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; | 1291 | doprint "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n"; |
1005 | 1292 | ||
1006 | return 1 if (!defined($store_failures)); | 1293 | if (defined($store_failures)) { |
1007 | 1294 | save_logs "fail", $store_failures; | |
1008 | my @t = localtime; | 1295 | } |
1009 | my $date = sprintf "%04d%02d%02d%02d%02d%02d", | ||
1010 | 1900+$t[5],$t[4],$t[3],$t[2],$t[1],$t[0]; | ||
1011 | |||
1012 | my $type = $build_type; | ||
1013 | if ($type =~ /useconfig/) { | ||
1014 | $type = "useconfig"; | ||
1015 | } | ||
1016 | |||
1017 | my $dir = "$machine-$test_type-$type-fail-$date"; | ||
1018 | my $faildir = "$store_failures/$dir"; | ||
1019 | |||
1020 | if (!-d $faildir) { | ||
1021 | mkpath($faildir) or | ||
1022 | die "can't create $faildir"; | ||
1023 | } | ||
1024 | if (-f "$output_config") { | ||
1025 | cp "$output_config", "$faildir/config" or | ||
1026 | die "failed to copy .config"; | ||
1027 | } | ||
1028 | if (-f $buildlog) { | ||
1029 | cp $buildlog, "$faildir/buildlog" or | ||
1030 | die "failed to move $buildlog"; | ||
1031 | } | ||
1032 | if (-f $dmesg) { | ||
1033 | cp $dmesg, "$faildir/dmesg" or | ||
1034 | die "failed to move $dmesg"; | ||
1035 | } | ||
1036 | |||
1037 | doprint "*** Saved info to $faildir ***\n"; | ||
1038 | 1296 | ||
1039 | return 1; | 1297 | return 1; |
1040 | } | 1298 | } |
@@ -1095,8 +1353,7 @@ sub run_ssh { | |||
1095 | } | 1353 | } |
1096 | 1354 | ||
1097 | sub run_scp { | 1355 | sub run_scp { |
1098 | my ($src, $dst) = @_; | 1356 | my ($src, $dst, $cp_scp) = @_; |
1099 | my $cp_scp = $scp_to_target; | ||
1100 | 1357 | ||
1101 | $cp_scp =~ s/\$SRC_FILE/$src/g; | 1358 | $cp_scp =~ s/\$SRC_FILE/$src/g; |
1102 | $cp_scp =~ s/\$DST_FILE/$dst/g; | 1359 | $cp_scp =~ s/\$DST_FILE/$dst/g; |
@@ -1104,6 +1361,22 @@ sub run_scp { | |||
1104 | return run_command "$cp_scp"; | 1361 | return run_command "$cp_scp"; |
1105 | } | 1362 | } |
1106 | 1363 | ||
1364 | sub run_scp_install { | ||
1365 | my ($src, $dst) = @_; | ||
1366 | |||
1367 | my $cp_scp = $scp_to_target_install; | ||
1368 | |||
1369 | return run_scp($src, $dst, $cp_scp); | ||
1370 | } | ||
1371 | |||
1372 | sub run_scp_mod { | ||
1373 | my ($src, $dst) = @_; | ||
1374 | |||
1375 | my $cp_scp = $scp_to_target; | ||
1376 | |||
1377 | return run_scp($src, $dst, $cp_scp); | ||
1378 | } | ||
1379 | |||
1107 | sub get_grub_index { | 1380 | sub get_grub_index { |
1108 | 1381 | ||
1109 | if ($reboot_type ne "grub") { | 1382 | if ($reboot_type ne "grub") { |
@@ -1170,13 +1443,16 @@ sub wait_for_input | |||
1170 | } | 1443 | } |
1171 | 1444 | ||
1172 | sub reboot_to { | 1445 | sub reboot_to { |
1446 | if (defined($switch_to_test)) { | ||
1447 | run_command $switch_to_test; | ||
1448 | } | ||
1449 | |||
1173 | if ($reboot_type eq "grub") { | 1450 | if ($reboot_type eq "grub") { |
1174 | run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch)'"; | 1451 | run_ssh "'(echo \"savedefault --default=$grub_number --once\" | grub --batch)'"; |
1175 | reboot; | 1452 | } elsif (defined $reboot_script) { |
1176 | return; | 1453 | run_command "$reboot_script"; |
1177 | } | 1454 | } |
1178 | 1455 | reboot; | |
1179 | run_command "$reboot_script"; | ||
1180 | } | 1456 | } |
1181 | 1457 | ||
1182 | sub get_sha1 { | 1458 | sub get_sha1 { |
@@ -1203,6 +1479,7 @@ sub get_sha1 { | |||
1203 | sub monitor { | 1479 | sub monitor { |
1204 | my $booted = 0; | 1480 | my $booted = 0; |
1205 | my $bug = 0; | 1481 | my $bug = 0; |
1482 | my $bug_ignored = 0; | ||
1206 | my $skip_call_trace = 0; | 1483 | my $skip_call_trace = 0; |
1207 | my $loops; | 1484 | my $loops; |
1208 | 1485 | ||
@@ -1275,8 +1552,12 @@ sub monitor { | |||
1275 | 1552 | ||
1276 | if ($full_line =~ /call trace:/i) { | 1553 | if ($full_line =~ /call trace:/i) { |
1277 | if (!$bug && !$skip_call_trace) { | 1554 | if (!$bug && !$skip_call_trace) { |
1278 | $bug = 1; | 1555 | if ($ignore_errors) { |
1279 | $failure_start = time; | 1556 | $bug_ignored = 1; |
1557 | } else { | ||
1558 | $bug = 1; | ||
1559 | $failure_start = time; | ||
1560 | } | ||
1280 | } | 1561 | } |
1281 | } | 1562 | } |
1282 | 1563 | ||
@@ -1338,15 +1619,26 @@ sub monitor { | |||
1338 | fail "failed - never got a boot prompt." and return 0; | 1619 | fail "failed - never got a boot prompt." and return 0; |
1339 | } | 1620 | } |
1340 | 1621 | ||
1622 | if ($bug_ignored) { | ||
1623 | doprint "WARNING: Call Trace detected but ignored due to IGNORE_ERRORS=1\n"; | ||
1624 | } | ||
1625 | |||
1341 | return 1; | 1626 | return 1; |
1342 | } | 1627 | } |
1343 | 1628 | ||
1629 | sub eval_kernel_version { | ||
1630 | my ($option) = @_; | ||
1631 | |||
1632 | $option =~ s/\$KERNEL_VERSION/$version/g; | ||
1633 | |||
1634 | return $option; | ||
1635 | } | ||
1636 | |||
1344 | sub do_post_install { | 1637 | sub do_post_install { |
1345 | 1638 | ||
1346 | return if (!defined($post_install)); | 1639 | return if (!defined($post_install)); |
1347 | 1640 | ||
1348 | my $cp_post_install = $post_install; | 1641 | my $cp_post_install = eval_kernel_version $post_install; |
1349 | $cp_post_install =~ s/\$KERNEL_VERSION/$version/g; | ||
1350 | run_command "$cp_post_install" or | 1642 | run_command "$cp_post_install" or |
1351 | dodie "Failed to run post install"; | 1643 | dodie "Failed to run post install"; |
1352 | } | 1644 | } |
@@ -1355,7 +1647,9 @@ sub install { | |||
1355 | 1647 | ||
1356 | return if ($no_install); | 1648 | return if ($no_install); |
1357 | 1649 | ||
1358 | run_scp "$outputdir/$build_target", "$target_image" or | 1650 | my $cp_target = eval_kernel_version $target_image; |
1651 | |||
1652 | run_scp_install "$outputdir/$build_target", "$cp_target" or | ||
1359 | dodie "failed to copy image"; | 1653 | dodie "failed to copy image"; |
1360 | 1654 | ||
1361 | my $install_mods = 0; | 1655 | my $install_mods = 0; |
@@ -1377,7 +1671,7 @@ sub install { | |||
1377 | return; | 1671 | return; |
1378 | } | 1672 | } |
1379 | 1673 | ||
1380 | run_command "$make INSTALL_MOD_PATH=$tmpdir modules_install" or | 1674 | run_command "$make INSTALL_MOD_STRIP=1 INSTALL_MOD_PATH=$tmpdir modules_install" or |
1381 | dodie "Failed to install modules"; | 1675 | dodie "Failed to install modules"; |
1382 | 1676 | ||
1383 | my $modlib = "/lib/modules/$version"; | 1677 | my $modlib = "/lib/modules/$version"; |
@@ -1390,7 +1684,7 @@ sub install { | |||
1390 | run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or | 1684 | run_command "cd $tmpdir && tar -cjf $modtar lib/modules/$version" or |
1391 | dodie "making tarball"; | 1685 | dodie "making tarball"; |
1392 | 1686 | ||
1393 | run_scp "$tmpdir/$modtar", "/tmp" or | 1687 | run_scp_mod "$tmpdir/$modtar", "/tmp" or |
1394 | dodie "failed to copy modules"; | 1688 | dodie "failed to copy modules"; |
1395 | 1689 | ||
1396 | unlink "$tmpdir/$modtar"; | 1690 | unlink "$tmpdir/$modtar"; |
@@ -1640,9 +1934,13 @@ sub success { | |||
1640 | doprint "*******************************************\n"; | 1934 | doprint "*******************************************\n"; |
1641 | doprint "*******************************************\n"; | 1935 | doprint "*******************************************\n"; |
1642 | 1936 | ||
1937 | if (defined($store_successes)) { | ||
1938 | save_logs "success", $store_successes; | ||
1939 | } | ||
1940 | |||
1643 | if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) { | 1941 | if ($i != $opt{"NUM_TESTS"} && !do_not_reboot) { |
1644 | doprint "Reboot and wait $sleep_time seconds\n"; | 1942 | doprint "Reboot and wait $sleep_time seconds\n"; |
1645 | reboot $sleep_time; | 1943 | reboot_to_good $sleep_time; |
1646 | } | 1944 | } |
1647 | } | 1945 | } |
1648 | 1946 | ||
@@ -1669,7 +1967,10 @@ sub child_run_test { | |||
1669 | $poweroff_on_error = 0; | 1967 | $poweroff_on_error = 0; |
1670 | $die_on_failure = 1; | 1968 | $die_on_failure = 1; |
1671 | 1969 | ||
1970 | $redirect = "$testlog"; | ||
1672 | run_command $run_test or $failed = 1; | 1971 | run_command $run_test or $failed = 1; |
1972 | undef $redirect; | ||
1973 | |||
1673 | exit $failed; | 1974 | exit $failed; |
1674 | } | 1975 | } |
1675 | 1976 | ||
@@ -1744,6 +2045,43 @@ sub do_run_test { | |||
1744 | waitpid $child_pid, 0; | 2045 | waitpid $child_pid, 0; |
1745 | $child_exit = $?; | 2046 | $child_exit = $?; |
1746 | 2047 | ||
2048 | if (!$bug && $in_bisect) { | ||
2049 | if (defined($bisect_ret_good)) { | ||
2050 | if ($child_exit == $bisect_ret_good) { | ||
2051 | return 1; | ||
2052 | } | ||
2053 | } | ||
2054 | if (defined($bisect_ret_skip)) { | ||
2055 | if ($child_exit == $bisect_ret_skip) { | ||
2056 | return -1; | ||
2057 | } | ||
2058 | } | ||
2059 | if (defined($bisect_ret_abort)) { | ||
2060 | if ($child_exit == $bisect_ret_abort) { | ||
2061 | fail "test abort" and return -2; | ||
2062 | } | ||
2063 | } | ||
2064 | if (defined($bisect_ret_bad)) { | ||
2065 | if ($child_exit == $bisect_ret_skip) { | ||
2066 | return 0; | ||
2067 | } | ||
2068 | } | ||
2069 | if (defined($bisect_ret_default)) { | ||
2070 | if ($bisect_ret_default eq "good") { | ||
2071 | return 1; | ||
2072 | } elsif ($bisect_ret_default eq "bad") { | ||
2073 | return 0; | ||
2074 | } elsif ($bisect_ret_default eq "skip") { | ||
2075 | return -1; | ||
2076 | } elsif ($bisect_ret_default eq "abort") { | ||
2077 | return -2; | ||
2078 | } else { | ||
2079 | fail "unknown default action: $bisect_ret_default" | ||
2080 | and return -2; | ||
2081 | } | ||
2082 | } | ||
2083 | } | ||
2084 | |||
1747 | if ($bug || $child_exit) { | 2085 | if ($bug || $child_exit) { |
1748 | return 0 if $in_bisect; | 2086 | return 0 if $in_bisect; |
1749 | fail "test failed" and return 0; | 2087 | fail "test failed" and return 0; |
@@ -1770,7 +2108,7 @@ sub run_git_bisect { | |||
1770 | if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) { | 2108 | if ($output =~ m/^(Bisecting: .*\(roughly \d+ steps?\))\s+\[([[:xdigit:]]+)\]/) { |
1771 | doprint "$1 [$2]\n"; | 2109 | doprint "$1 [$2]\n"; |
1772 | } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { | 2110 | } elsif ($output =~ m/^([[:xdigit:]]+) is the first bad commit/) { |
1773 | $bisect_bad = $1; | 2111 | $bisect_bad_commit = $1; |
1774 | doprint "Found bad commit... $1\n"; | 2112 | doprint "Found bad commit... $1\n"; |
1775 | return 0; | 2113 | return 0; |
1776 | } else { | 2114 | } else { |
@@ -1783,7 +2121,7 @@ sub run_git_bisect { | |||
1783 | 2121 | ||
1784 | sub bisect_reboot { | 2122 | sub bisect_reboot { |
1785 | doprint "Reboot and sleep $bisect_sleep_time seconds\n"; | 2123 | doprint "Reboot and sleep $bisect_sleep_time seconds\n"; |
1786 | reboot $bisect_sleep_time; | 2124 | reboot_to_good $bisect_sleep_time; |
1787 | } | 2125 | } |
1788 | 2126 | ||
1789 | # returns 1 on success, 0 on failure, -1 on skip | 2127 | # returns 1 on success, 0 on failure, -1 on skip |
@@ -1868,21 +2206,28 @@ sub run_bisect { | |||
1868 | } | 2206 | } |
1869 | } | 2207 | } |
1870 | 2208 | ||
2209 | sub update_bisect_replay { | ||
2210 | my $tmp_log = "$tmpdir/ktest_bisect_log"; | ||
2211 | run_command "git bisect log > $tmp_log" or | ||
2212 | die "can't create bisect log"; | ||
2213 | return $tmp_log; | ||
2214 | } | ||
2215 | |||
1871 | sub bisect { | 2216 | sub bisect { |
1872 | my ($i) = @_; | 2217 | my ($i) = @_; |
1873 | 2218 | ||
1874 | my $result; | 2219 | my $result; |
1875 | 2220 | ||
1876 | die "BISECT_GOOD[$i] not defined\n" if (!defined($opt{"BISECT_GOOD[$i]"})); | 2221 | die "BISECT_GOOD[$i] not defined\n" if (!defined($bisect_good)); |
1877 | die "BISECT_BAD[$i] not defined\n" if (!defined($opt{"BISECT_BAD[$i]"})); | 2222 | die "BISECT_BAD[$i] not defined\n" if (!defined($bisect_bad)); |
1878 | die "BISECT_TYPE[$i] not defined\n" if (!defined($opt{"BISECT_TYPE[$i]"})); | 2223 | die "BISECT_TYPE[$i] not defined\n" if (!defined($bisect_type)); |
1879 | 2224 | ||
1880 | my $good = $opt{"BISECT_GOOD[$i]"}; | 2225 | my $good = $bisect_good; |
1881 | my $bad = $opt{"BISECT_BAD[$i]"}; | 2226 | my $bad = $bisect_bad; |
1882 | my $type = $opt{"BISECT_TYPE[$i]"}; | 2227 | my $type = $bisect_type; |
1883 | my $start = $opt{"BISECT_START[$i]"}; | 2228 | my $start = $bisect_start; |
1884 | my $replay = $opt{"BISECT_REPLAY[$i]"}; | 2229 | my $replay = $bisect_replay; |
1885 | my $start_files = $opt{"BISECT_FILES[$i]"}; | 2230 | my $start_files = $bisect_files; |
1886 | 2231 | ||
1887 | if (defined($start_files)) { | 2232 | if (defined($start_files)) { |
1888 | $start_files = " -- " . $start_files; | 2233 | $start_files = " -- " . $start_files; |
@@ -1894,8 +2239,7 @@ sub bisect { | |||
1894 | $good = get_sha1($good); | 2239 | $good = get_sha1($good); |
1895 | $bad = get_sha1($bad); | 2240 | $bad = get_sha1($bad); |
1896 | 2241 | ||
1897 | if (defined($opt{"BISECT_REVERSE[$i]"}) && | 2242 | if (defined($bisect_reverse) && $bisect_reverse == 1) { |
1898 | $opt{"BISECT_REVERSE[$i]"} == 1) { | ||
1899 | doprint "Performing a reverse bisect (bad is good, good is bad!)\n"; | 2243 | doprint "Performing a reverse bisect (bad is good, good is bad!)\n"; |
1900 | $reverse_bisect = 1; | 2244 | $reverse_bisect = 1; |
1901 | } else { | 2245 | } else { |
@@ -1907,8 +2251,31 @@ sub bisect { | |||
1907 | $type = "boot"; | 2251 | $type = "boot"; |
1908 | } | 2252 | } |
1909 | 2253 | ||
1910 | my $check = $opt{"BISECT_CHECK[$i]"}; | 2254 | # Check if a bisect was running |
1911 | if (defined($check) && $check ne "0") { | 2255 | my $bisect_start_file = "$builddir/.git/BISECT_START"; |
2256 | |||
2257 | my $check = $bisect_check; | ||
2258 | my $do_check = defined($check) && $check ne "0"; | ||
2259 | |||
2260 | if ( -f $bisect_start_file ) { | ||
2261 | print "Bisect in progress found\n"; | ||
2262 | if ($do_check) { | ||
2263 | print " If you say yes, then no checks of good or bad will be done\n"; | ||
2264 | } | ||
2265 | if (defined($replay)) { | ||
2266 | print "** BISECT_REPLAY is defined in config file **"; | ||
2267 | print " Ignore config option and perform new git bisect log?\n"; | ||
2268 | if (read_ync " (yes, no, or cancel) ") { | ||
2269 | $replay = update_bisect_replay; | ||
2270 | $do_check = 0; | ||
2271 | } | ||
2272 | } elsif (read_yn "read git log and continue?") { | ||
2273 | $replay = update_bisect_replay; | ||
2274 | $do_check = 0; | ||
2275 | } | ||
2276 | } | ||
2277 | |||
2278 | if ($do_check) { | ||
1912 | 2279 | ||
1913 | # get current HEAD | 2280 | # get current HEAD |
1914 | my $head = get_sha1("HEAD"); | 2281 | my $head = get_sha1("HEAD"); |
@@ -1973,7 +2340,7 @@ sub bisect { | |||
1973 | run_command "git bisect reset" or | 2340 | run_command "git bisect reset" or |
1974 | dodie "could not reset git bisect"; | 2341 | dodie "could not reset git bisect"; |
1975 | 2342 | ||
1976 | doprint "Bad commit was [$bisect_bad]\n"; | 2343 | doprint "Bad commit was [$bisect_bad_commit]\n"; |
1977 | 2344 | ||
1978 | success $i; | 2345 | success $i; |
1979 | } | 2346 | } |
@@ -2129,7 +2496,7 @@ sub run_config_bisect { | |||
2129 | } | 2496 | } |
2130 | 2497 | ||
2131 | doprint "***** RUN TEST ***\n"; | 2498 | doprint "***** RUN TEST ***\n"; |
2132 | my $type = $opt{"CONFIG_BISECT_TYPE[$iteration]"}; | 2499 | my $type = $config_bisect_type; |
2133 | my $ret; | 2500 | my $ret; |
2134 | my %current_config; | 2501 | my %current_config; |
2135 | 2502 | ||
@@ -2233,7 +2600,7 @@ sub run_config_bisect { | |||
2233 | sub config_bisect { | 2600 | sub config_bisect { |
2234 | my ($i) = @_; | 2601 | my ($i) = @_; |
2235 | 2602 | ||
2236 | my $start_config = $opt{"CONFIG_BISECT[$i]"}; | 2603 | my $start_config = $config_bisect; |
2237 | 2604 | ||
2238 | my $tmpconfig = "$tmpdir/use_config"; | 2605 | my $tmpconfig = "$tmpdir/use_config"; |
2239 | 2606 | ||
@@ -2262,7 +2629,7 @@ sub config_bisect { | |||
2262 | # read directly what we want to check | 2629 | # read directly what we want to check |
2263 | my %config_check; | 2630 | my %config_check; |
2264 | open (IN, $output_config) | 2631 | open (IN, $output_config) |
2265 | or dodie "faied to open $output_config"; | 2632 | or dodie "failed to open $output_config"; |
2266 | 2633 | ||
2267 | while (<IN>) { | 2634 | while (<IN>) { |
2268 | if (/^((CONFIG\S*)=.*)/) { | 2635 | if (/^((CONFIG\S*)=.*)/) { |
@@ -2346,29 +2713,29 @@ sub config_bisect { | |||
2346 | 2713 | ||
2347 | sub patchcheck_reboot { | 2714 | sub patchcheck_reboot { |
2348 | doprint "Reboot and sleep $patchcheck_sleep_time seconds\n"; | 2715 | doprint "Reboot and sleep $patchcheck_sleep_time seconds\n"; |
2349 | reboot $patchcheck_sleep_time; | 2716 | reboot_to_good $patchcheck_sleep_time; |
2350 | } | 2717 | } |
2351 | 2718 | ||
2352 | sub patchcheck { | 2719 | sub patchcheck { |
2353 | my ($i) = @_; | 2720 | my ($i) = @_; |
2354 | 2721 | ||
2355 | die "PATCHCHECK_START[$i] not defined\n" | 2722 | die "PATCHCHECK_START[$i] not defined\n" |
2356 | if (!defined($opt{"PATCHCHECK_START[$i]"})); | 2723 | if (!defined($patchcheck_start)); |
2357 | die "PATCHCHECK_TYPE[$i] not defined\n" | 2724 | die "PATCHCHECK_TYPE[$i] not defined\n" |
2358 | if (!defined($opt{"PATCHCHECK_TYPE[$i]"})); | 2725 | if (!defined($patchcheck_type)); |
2359 | 2726 | ||
2360 | my $start = $opt{"PATCHCHECK_START[$i]"}; | 2727 | my $start = $patchcheck_start; |
2361 | 2728 | ||
2362 | my $end = "HEAD"; | 2729 | my $end = "HEAD"; |
2363 | if (defined($opt{"PATCHCHECK_END[$i]"})) { | 2730 | if (defined($patchcheck_end)) { |
2364 | $end = $opt{"PATCHCHECK_END[$i]"}; | 2731 | $end = $patchcheck_end; |
2365 | } | 2732 | } |
2366 | 2733 | ||
2367 | # Get the true sha1's since we can use things like HEAD~3 | 2734 | # Get the true sha1's since we can use things like HEAD~3 |
2368 | $start = get_sha1($start); | 2735 | $start = get_sha1($start); |
2369 | $end = get_sha1($end); | 2736 | $end = get_sha1($end); |
2370 | 2737 | ||
2371 | my $type = $opt{"PATCHCHECK_TYPE[$i]"}; | 2738 | my $type = $patchcheck_type; |
2372 | 2739 | ||
2373 | # Can't have a test without having a test to run | 2740 | # Can't have a test without having a test to run |
2374 | if ($type eq "test" && !defined($run_test)) { | 2741 | if ($type eq "test" && !defined($run_test)) { |
@@ -2905,9 +3272,11 @@ sub make_min_config { | |||
2905 | $in_bisect = 1; | 3272 | $in_bisect = 1; |
2906 | 3273 | ||
2907 | my $failed = 0; | 3274 | my $failed = 0; |
2908 | build "oldconfig"; | 3275 | build "oldconfig" or $failed = 1; |
2909 | start_monitor_and_boot or $failed = 1; | 3276 | if (!$failed) { |
2910 | end_monitor; | 3277 | start_monitor_and_boot or $failed = 1; |
3278 | end_monitor; | ||
3279 | } | ||
2911 | 3280 | ||
2912 | $in_bisect = 0; | 3281 | $in_bisect = 0; |
2913 | 3282 | ||
@@ -2963,7 +3332,7 @@ sub make_min_config { | |||
2963 | } | 3332 | } |
2964 | 3333 | ||
2965 | doprint "Reboot and wait $sleep_time seconds\n"; | 3334 | doprint "Reboot and wait $sleep_time seconds\n"; |
2966 | reboot $sleep_time; | 3335 | reboot_to_good $sleep_time; |
2967 | } | 3336 | } |
2968 | 3337 | ||
2969 | success $i; | 3338 | success $i; |
@@ -2985,13 +3354,27 @@ if ($#ARGV == 0) { | |||
2985 | } | 3354 | } |
2986 | 3355 | ||
2987 | if (! -f $ktest_config) { | 3356 | if (! -f $ktest_config) { |
3357 | $newconfig = 1; | ||
3358 | get_test_case; | ||
2988 | open(OUT, ">$ktest_config") or die "Can not create $ktest_config"; | 3359 | open(OUT, ">$ktest_config") or die "Can not create $ktest_config"; |
2989 | print OUT << "EOF" | 3360 | print OUT << "EOF" |
2990 | # Generated by ktest.pl | 3361 | # Generated by ktest.pl |
2991 | # | 3362 | # |
3363 | |||
3364 | # PWD is a ktest.pl variable that will result in the process working | ||
3365 | # directory that ktest.pl is executed in. | ||
3366 | |||
3367 | # THIS_DIR is automatically assigned the PWD of the path that generated | ||
3368 | # the config file. It is best to use this variable when assigning other | ||
3369 | # directory paths within this directory. This allows you to easily | ||
3370 | # move the test cases to other locations or to other machines. | ||
3371 | # | ||
3372 | THIS_DIR := $variable{"PWD"} | ||
3373 | |||
2992 | # Define each test with TEST_START | 3374 | # Define each test with TEST_START |
2993 | # The config options below it will override the defaults | 3375 | # The config options below it will override the defaults |
2994 | TEST_START | 3376 | TEST_START |
3377 | TEST_TYPE = $default{"TEST_TYPE"} | ||
2995 | 3378 | ||
2996 | DEFAULTS | 3379 | DEFAULTS |
2997 | EOF | 3380 | EOF |
@@ -3011,7 +3394,7 @@ if ($#new_configs >= 0) { | |||
3011 | open(OUT, ">>$ktest_config") or die "Can not append to $ktest_config"; | 3394 | open(OUT, ">>$ktest_config") or die "Can not append to $ktest_config"; |
3012 | foreach my $config (@new_configs) { | 3395 | foreach my $config (@new_configs) { |
3013 | print OUT "$config = $entered_configs{$config}\n"; | 3396 | print OUT "$config = $entered_configs{$config}\n"; |
3014 | $opt{$config} = $entered_configs{$config}; | 3397 | $opt{$config} = process_variables($entered_configs{$config}); |
3015 | } | 3398 | } |
3016 | } | 3399 | } |
3017 | 3400 | ||
@@ -3091,61 +3474,10 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
3091 | 3474 | ||
3092 | my $makecmd = set_test_option("MAKE_CMD", $i); | 3475 | my $makecmd = set_test_option("MAKE_CMD", $i); |
3093 | 3476 | ||
3094 | $machine = set_test_option("MACHINE", $i); | 3477 | # Load all the options into their mapped variable names |
3095 | $ssh_user = set_test_option("SSH_USER", $i); | 3478 | foreach my $opt (keys %option_map) { |
3096 | $tmpdir = set_test_option("TMP_DIR", $i); | 3479 | ${$option_map{$opt}} = set_test_option($opt, $i); |
3097 | $outputdir = set_test_option("OUTPUT_DIR", $i); | 3480 | } |
3098 | $builddir = set_test_option("BUILD_DIR", $i); | ||
3099 | $test_type = set_test_option("TEST_TYPE", $i); | ||
3100 | $build_type = set_test_option("BUILD_TYPE", $i); | ||
3101 | $build_options = set_test_option("BUILD_OPTIONS", $i); | ||
3102 | $pre_build = set_test_option("PRE_BUILD", $i); | ||
3103 | $post_build = set_test_option("POST_BUILD", $i); | ||
3104 | $pre_build_die = set_test_option("PRE_BUILD_DIE", $i); | ||
3105 | $post_build_die = set_test_option("POST_BUILD_DIE", $i); | ||
3106 | $power_cycle = set_test_option("POWER_CYCLE", $i); | ||
3107 | $reboot = set_test_option("REBOOT", $i); | ||
3108 | $noclean = set_test_option("BUILD_NOCLEAN", $i); | ||
3109 | $minconfig = set_test_option("MIN_CONFIG", $i); | ||
3110 | $output_minconfig = set_test_option("OUTPUT_MIN_CONFIG", $i); | ||
3111 | $start_minconfig = set_test_option("START_MIN_CONFIG", $i); | ||
3112 | $ignore_config = set_test_option("IGNORE_CONFIG", $i); | ||
3113 | $run_test = set_test_option("TEST", $i); | ||
3114 | $addconfig = set_test_option("ADD_CONFIG", $i); | ||
3115 | $reboot_type = set_test_option("REBOOT_TYPE", $i); | ||
3116 | $grub_menu = set_test_option("GRUB_MENU", $i); | ||
3117 | $post_install = set_test_option("POST_INSTALL", $i); | ||
3118 | $no_install = set_test_option("NO_INSTALL", $i); | ||
3119 | $reboot_script = set_test_option("REBOOT_SCRIPT", $i); | ||
3120 | $reboot_on_error = set_test_option("REBOOT_ON_ERROR", $i); | ||
3121 | $poweroff_on_error = set_test_option("POWEROFF_ON_ERROR", $i); | ||
3122 | $die_on_failure = set_test_option("DIE_ON_FAILURE", $i); | ||
3123 | $power_off = set_test_option("POWER_OFF", $i); | ||
3124 | $powercycle_after_reboot = set_test_option("POWERCYCLE_AFTER_REBOOT", $i); | ||
3125 | $poweroff_after_halt = set_test_option("POWEROFF_AFTER_HALT", $i); | ||
3126 | $sleep_time = set_test_option("SLEEP_TIME", $i); | ||
3127 | $bisect_sleep_time = set_test_option("BISECT_SLEEP_TIME", $i); | ||
3128 | $patchcheck_sleep_time = set_test_option("PATCHCHECK_SLEEP_TIME", $i); | ||
3129 | $ignore_warnings = set_test_option("IGNORE_WARNINGS", $i); | ||
3130 | $bisect_manual = set_test_option("BISECT_MANUAL", $i); | ||
3131 | $bisect_skip = set_test_option("BISECT_SKIP", $i); | ||
3132 | $config_bisect_good = set_test_option("CONFIG_BISECT_GOOD", $i); | ||
3133 | $store_failures = set_test_option("STORE_FAILURES", $i); | ||
3134 | $test_name = set_test_option("TEST_NAME", $i); | ||
3135 | $timeout = set_test_option("TIMEOUT", $i); | ||
3136 | $booted_timeout = set_test_option("BOOTED_TIMEOUT", $i); | ||
3137 | $console = set_test_option("CONSOLE", $i); | ||
3138 | $detect_triplefault = set_test_option("DETECT_TRIPLE_FAULT", $i); | ||
3139 | $success_line = set_test_option("SUCCESS_LINE", $i); | ||
3140 | $reboot_success_line = set_test_option("REBOOT_SUCCESS_LINE", $i); | ||
3141 | $stop_after_success = set_test_option("STOP_AFTER_SUCCESS", $i); | ||
3142 | $stop_after_failure = set_test_option("STOP_AFTER_FAILURE", $i); | ||
3143 | $stop_test_after = set_test_option("STOP_TEST_AFTER", $i); | ||
3144 | $build_target = set_test_option("BUILD_TARGET", $i); | ||
3145 | $ssh_exec = set_test_option("SSH_EXEC", $i); | ||
3146 | $scp_to_target = set_test_option("SCP_TO_TARGET", $i); | ||
3147 | $target_image = set_test_option("TARGET_IMAGE", $i); | ||
3148 | $localversion = set_test_option("LOCALVERSION", $i); | ||
3149 | 3481 | ||
3150 | $start_minconfig_defined = 1; | 3482 | $start_minconfig_defined = 1; |
3151 | 3483 | ||
@@ -3166,26 +3498,26 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
3166 | $ENV{"SSH_USER"} = $ssh_user; | 3498 | $ENV{"SSH_USER"} = $ssh_user; |
3167 | $ENV{"MACHINE"} = $machine; | 3499 | $ENV{"MACHINE"} = $machine; |
3168 | 3500 | ||
3169 | $target = "$ssh_user\@$machine"; | ||
3170 | |||
3171 | $buildlog = "$tmpdir/buildlog-$machine"; | 3501 | $buildlog = "$tmpdir/buildlog-$machine"; |
3502 | $testlog = "$tmpdir/testlog-$machine"; | ||
3172 | $dmesg = "$tmpdir/dmesg-$machine"; | 3503 | $dmesg = "$tmpdir/dmesg-$machine"; |
3173 | $make = "$makecmd O=$outputdir"; | 3504 | $make = "$makecmd O=$outputdir"; |
3174 | $output_config = "$outputdir/.config"; | 3505 | $output_config = "$outputdir/.config"; |
3175 | 3506 | ||
3176 | if ($reboot_type eq "grub") { | 3507 | if (!$buildonly) { |
3177 | dodie "GRUB_MENU not defined" if (!defined($grub_menu)); | 3508 | $target = "$ssh_user\@$machine"; |
3178 | } elsif (!defined($reboot_script)) { | 3509 | if ($reboot_type eq "grub") { |
3179 | dodie "REBOOT_SCRIPT not defined" | 3510 | dodie "GRUB_MENU not defined" if (!defined($grub_menu)); |
3511 | } | ||
3180 | } | 3512 | } |
3181 | 3513 | ||
3182 | my $run_type = $build_type; | 3514 | my $run_type = $build_type; |
3183 | if ($test_type eq "patchcheck") { | 3515 | if ($test_type eq "patchcheck") { |
3184 | $run_type = $opt{"PATCHCHECK_TYPE[$i]"}; | 3516 | $run_type = $patchcheck_type; |
3185 | } elsif ($test_type eq "bisect") { | 3517 | } elsif ($test_type eq "bisect") { |
3186 | $run_type = $opt{"BISECT_TYPE[$i]"}; | 3518 | $run_type = $bisect_type; |
3187 | } elsif ($test_type eq "config_bisect") { | 3519 | } elsif ($test_type eq "config_bisect") { |
3188 | $run_type = $opt{"CONFIG_BISECT_TYPE[$i]"}; | 3520 | $run_type = $config_bisect_type; |
3189 | } | 3521 | } |
3190 | 3522 | ||
3191 | if ($test_type eq "make_min_config") { | 3523 | if ($test_type eq "make_min_config") { |
@@ -3205,6 +3537,7 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
3205 | 3537 | ||
3206 | unlink $dmesg; | 3538 | unlink $dmesg; |
3207 | unlink $buildlog; | 3539 | unlink $buildlog; |
3540 | unlink $testlog; | ||
3208 | 3541 | ||
3209 | if (defined($addconfig)) { | 3542 | if (defined($addconfig)) { |
3210 | my $min = $minconfig; | 3543 | my $min = $minconfig; |
@@ -3216,14 +3549,15 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
3216 | $minconfig = "$tmpdir/add_config"; | 3549 | $minconfig = "$tmpdir/add_config"; |
3217 | } | 3550 | } |
3218 | 3551 | ||
3219 | my $checkout = $opt{"CHECKOUT[$i]"}; | ||
3220 | if (defined($checkout)) { | 3552 | if (defined($checkout)) { |
3221 | run_command "git checkout $checkout" or | 3553 | run_command "git checkout $checkout" or |
3222 | die "failed to checkout $checkout"; | 3554 | die "failed to checkout $checkout"; |
3223 | } | 3555 | } |
3224 | 3556 | ||
3225 | $no_reboot = 0; | 3557 | # A test may opt to not reboot the box |
3226 | 3558 | if ($reboot_on_success) { | |
3559 | $no_reboot = 0; | ||
3560 | } | ||
3227 | 3561 | ||
3228 | if ($test_type eq "bisect") { | 3562 | if ($test_type eq "bisect") { |
3229 | bisect $i; | 3563 | bisect $i; |
@@ -3267,9 +3601,13 @@ for (my $i = 1; $i <= $opt{"NUM_TESTS"}; $i++) { | |||
3267 | if ($opt{"POWEROFF_ON_SUCCESS"}) { | 3601 | if ($opt{"POWEROFF_ON_SUCCESS"}) { |
3268 | halt; | 3602 | halt; |
3269 | } elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) { | 3603 | } elsif ($opt{"REBOOT_ON_SUCCESS"} && !do_not_reboot) { |
3270 | reboot; | 3604 | reboot_to_good; |
3605 | } elsif (defined($switch_to_good)) { | ||
3606 | # still need to get to the good kernel | ||
3607 | run_command $switch_to_good; | ||
3271 | } | 3608 | } |
3272 | 3609 | ||
3610 | |||
3273 | doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n"; | 3611 | doprint "\n $successes of $opt{NUM_TESTS} tests were successful\n\n"; |
3274 | 3612 | ||
3275 | exit 0; | 3613 | exit 0; |
diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 553c06b7d6f2..b682456afda8 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf | |||
@@ -346,7 +346,10 @@ | |||
346 | #GRUB_MENU = Test Kernel | 346 | #GRUB_MENU = Test Kernel |
347 | 347 | ||
348 | # A script to reboot the target into the test kernel | 348 | # A script to reboot the target into the test kernel |
349 | # (Only mandatory if REBOOT_TYPE = script) | 349 | # This and SWITCH_TO_TEST are about the same, except |
350 | # SWITCH_TO_TEST is run even for REBOOT_TYPE = grub. | ||
351 | # This may be left undefined. | ||
352 | # (default undefined) | ||
350 | #REBOOT_SCRIPT = | 353 | #REBOOT_SCRIPT = |
351 | 354 | ||
352 | #### Optional Config Options (all have defaults) #### | 355 | #### Optional Config Options (all have defaults) #### |
@@ -468,6 +471,27 @@ | |||
468 | # The test will not modify that file. | 471 | # The test will not modify that file. |
469 | #REBOOT_TYPE = grub | 472 | #REBOOT_TYPE = grub |
470 | 473 | ||
474 | # If you are using a machine that doesn't boot with grub, and | ||
475 | # perhaps gets its kernel from a remote server (tftp), then | ||
476 | # you can use this option to update the target image with the | ||
477 | # test image. | ||
478 | # | ||
479 | # You could also do the same with POST_INSTALL, but the difference | ||
480 | # between that option and this option is that POST_INSTALL runs | ||
481 | # after the install, where this one runs just before a reboot. | ||
482 | # (default undefined) | ||
483 | #SWITCH_TO_TEST = cp ${OUTPUT_DIR}/${BUILD_TARGET} ${TARGET_IMAGE} | ||
484 | |||
485 | # If you are using a machine that doesn't boot with grub, and | ||
486 | # perhaps gets its kernel from a remote server (tftp), then | ||
487 | # you can use this option to update the target image with the | ||
488 | # the known good image to reboot safely back into. | ||
489 | # | ||
490 | # This option holds a command that will execute before needing | ||
491 | # to reboot to a good known image. | ||
492 | # (default undefined) | ||
493 | #SWITCH_TO_GOOD = ssh ${SSH_USER}/${MACHINE} cp good_image ${TARGET_IMAGE} | ||
494 | |||
471 | # The min config that is needed to build for the machine | 495 | # The min config that is needed to build for the machine |
472 | # A nice way to create this is with the following: | 496 | # A nice way to create this is with the following: |
473 | # | 497 | # |
@@ -589,6 +613,12 @@ | |||
589 | # (default undefined) | 613 | # (default undefined) |
590 | #STORE_FAILURES = /home/test/failures | 614 | #STORE_FAILURES = /home/test/failures |
591 | 615 | ||
616 | # Directory to store success directories on success. If this is not | ||
617 | # set, the .config, dmesg and bootlog will not be saved if a | ||
618 | # test succeeds. | ||
619 | # (default undefined) | ||
620 | #STORE_SUCCESSES = /home/test/successes | ||
621 | |||
592 | # Build without doing a make mrproper, or removing .config | 622 | # Build without doing a make mrproper, or removing .config |
593 | # (default 0) | 623 | # (default 0) |
594 | #BUILD_NOCLEAN = 0 | 624 | #BUILD_NOCLEAN = 0 |
@@ -680,10 +710,18 @@ | |||
680 | # The variables SSH_USER, MACHINE and SSH_COMMAND are defined | 710 | # The variables SSH_USER, MACHINE and SSH_COMMAND are defined |
681 | #SSH_EXEC = ssh $SSH_USER@$MACHINE $SSH_COMMAND"; | 711 | #SSH_EXEC = ssh $SSH_USER@$MACHINE $SSH_COMMAND"; |
682 | 712 | ||
683 | # The way to copy a file to the target | 713 | # The way to copy a file to the target (install and modules) |
684 | # (default scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE) | 714 | # (default scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE) |
685 | # The variables SSH_USER, MACHINE, SRC_FILE and DST_FILE are defined. | 715 | # The variables SSH_USER, MACHINE are defined by the config |
686 | #SCP_TO_TARGET = scp $SRC_FILE $SSH_USER@$MACHINE:$DST_FILE | 716 | # SRC_FILE and DST_FILE are ktest internal variables and |
717 | # should only have '$' and not the '${}' notation. | ||
718 | # (default scp $SRC_FILE ${SSH_USER}@${MACHINE}:$DST_FILE) | ||
719 | #SCP_TO_TARGET = echo skip scp for $SRC_FILE $DST_FILE | ||
720 | |||
721 | # If install needs to be different than modules, then this | ||
722 | # option will override the SCP_TO_TARGET for installation. | ||
723 | # (default ${SCP_TO_TARGET} ) | ||
724 | #SCP_TO_TARGET_INSTALL = scp $SRC_FILE tftp@tftpserver:$DST_FILE | ||
687 | 725 | ||
688 | # The nice way to reboot the target | 726 | # The nice way to reboot the target |
689 | # (default ssh $SSH_USER@$MACHINE reboot) | 727 | # (default ssh $SSH_USER@$MACHINE reboot) |
@@ -700,6 +738,25 @@ | |||
700 | # (default 1) | 738 | # (default 1) |
701 | #DETECT_TRIPLE_FAULT = 0 | 739 | #DETECT_TRIPLE_FAULT = 0 |
702 | 740 | ||
741 | # All options in the config file should be either used by ktest | ||
742 | # or could be used within a value of another option. If an option | ||
743 | # in the config file is not used, ktest will warn about it and ask | ||
744 | # if you want to continue. | ||
745 | # | ||
746 | # If you don't care if there are non-used options, enable this | ||
747 | # option. Be careful though, a non-used option is usually a sign | ||
748 | # of an option name being typed incorrectly. | ||
749 | # (default 0) | ||
750 | #IGNORE_UNUSED = 1 | ||
751 | |||
752 | # When testing a kernel that happens to have WARNINGs, and call | ||
753 | # traces, ktest.pl will detect these and fail a boot or test run | ||
754 | # due to warnings. By setting this option, ktest will ignore | ||
755 | # call traces, and will not fail a test if the kernel produces | ||
756 | # an oops. Use this option with care. | ||
757 | # (default 0) | ||
758 | #IGNORE_ERRORS = 1 | ||
759 | |||
703 | #### Per test run options #### | 760 | #### Per test run options #### |
704 | # The following options are only allowed in TEST_START sections. | 761 | # The following options are only allowed in TEST_START sections. |
705 | # They are ignored in the DEFAULTS sections. | 762 | # They are ignored in the DEFAULTS sections. |
@@ -862,6 +919,42 @@ | |||
862 | # BISECT_BAD with BISECT_CHECK = good or | 919 | # BISECT_BAD with BISECT_CHECK = good or |
863 | # BISECT_CHECK = bad, respectively. | 920 | # BISECT_CHECK = bad, respectively. |
864 | # | 921 | # |
922 | # BISECT_RET_GOOD = 0 (optional, default undefined) | ||
923 | # | ||
924 | # In case the specificed test returns something other than just | ||
925 | # 0 for good, and non-zero for bad, you can override 0 being | ||
926 | # good by defining BISECT_RET_GOOD. | ||
927 | # | ||
928 | # BISECT_RET_BAD = 1 (optional, default undefined) | ||
929 | # | ||
930 | # In case the specificed test returns something other than just | ||
931 | # 0 for good, and non-zero for bad, you can override non-zero being | ||
932 | # bad by defining BISECT_RET_BAD. | ||
933 | # | ||
934 | # BISECT_RET_ABORT = 255 (optional, default undefined) | ||
935 | # | ||
936 | # If you need to abort the bisect if the test discovers something | ||
937 | # that was wrong, you can define BISECT_RET_ABORT to be the error | ||
938 | # code returned by the test in order to abort the bisect. | ||
939 | # | ||
940 | # BISECT_RET_SKIP = 2 (optional, default undefined) | ||
941 | # | ||
942 | # If the test detects that the current commit is neither good | ||
943 | # nor bad, but something else happened (another bug detected) | ||
944 | # you can specify BISECT_RET_SKIP to an error code that the | ||
945 | # test returns when it should skip the current commit. | ||
946 | # | ||
947 | # BISECT_RET_DEFAULT = good (optional, default undefined) | ||
948 | # | ||
949 | # You can override the default of what to do when the above | ||
950 | # options are not hit. This may be one of, "good", "bad", | ||
951 | # "abort" or "skip" (without the quotes). | ||
952 | # | ||
953 | # Note, if you do not define any of the previous BISECT_RET_* | ||
954 | # and define BISECT_RET_DEFAULT, all bisects results will do | ||
955 | # what the BISECT_RET_DEFAULT has. | ||
956 | # | ||
957 | # | ||
865 | # Example: | 958 | # Example: |
866 | # TEST_START | 959 | # TEST_START |
867 | # TEST_TYPE = bisect | 960 | # TEST_TYPE = bisect |
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile new file mode 100644 index 000000000000..28bc57ee757c --- /dev/null +++ b/tools/testing/selftests/Makefile | |||
@@ -0,0 +1,16 @@ | |||
1 | TARGETS = breakpoints vm | ||
2 | |||
3 | all: | ||
4 | for TARGET in $(TARGETS); do \ | ||
5 | make -C $$TARGET; \ | ||
6 | done; | ||
7 | |||
8 | run_tests: all | ||
9 | for TARGET in $(TARGETS); do \ | ||
10 | make -C $$TARGET run_tests; \ | ||
11 | done; | ||
12 | |||
13 | clean: | ||
14 | for TARGET in $(TARGETS); do \ | ||
15 | make -C $$TARGET clean; \ | ||
16 | done; | ||
diff --git a/tools/testing/selftests/breakpoints/Makefile b/tools/testing/selftests/breakpoints/Makefile new file mode 100644 index 000000000000..931278035f5c --- /dev/null +++ b/tools/testing/selftests/breakpoints/Makefile | |||
@@ -0,0 +1,23 @@ | |||
1 | # Taken from perf makefile | ||
2 | uname_M := $(shell uname -m 2>/dev/null || echo not) | ||
3 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) | ||
4 | ifeq ($(ARCH),i386) | ||
5 | ARCH := x86 | ||
6 | endif | ||
7 | ifeq ($(ARCH),x86_64) | ||
8 | ARCH := x86 | ||
9 | endif | ||
10 | |||
11 | |||
12 | all: | ||
13 | ifeq ($(ARCH),x86) | ||
14 | gcc breakpoint_test.c -o breakpoint_test | ||
15 | else | ||
16 | echo "Not an x86 target, can't build breakpoints selftests" | ||
17 | endif | ||
18 | |||
19 | run_tests: | ||
20 | ./breakpoint_test | ||
21 | |||
22 | clean: | ||
23 | rm -fr breakpoint_test | ||
diff --git a/tools/testing/selftests/breakpoints/breakpoint_test.c b/tools/testing/selftests/breakpoints/breakpoint_test.c new file mode 100644 index 000000000000..a0743f3b2b57 --- /dev/null +++ b/tools/testing/selftests/breakpoints/breakpoint_test.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com> | ||
3 | * | ||
4 | * Licensed under the terms of the GNU GPL License version 2 | ||
5 | * | ||
6 | * Selftests for breakpoints (and more generally the do_debug() path) in x86. | ||
7 | */ | ||
8 | |||
9 | |||
10 | #include <sys/ptrace.h> | ||
11 | #include <unistd.h> | ||
12 | #include <stddef.h> | ||
13 | #include <sys/user.h> | ||
14 | #include <stdio.h> | ||
15 | #include <stdlib.h> | ||
16 | #include <signal.h> | ||
17 | #include <sys/types.h> | ||
18 | #include <sys/wait.h> | ||
19 | |||
20 | |||
21 | /* Breakpoint access modes */ | ||
22 | enum { | ||
23 | BP_X = 1, | ||
24 | BP_RW = 2, | ||
25 | BP_W = 4, | ||
26 | }; | ||
27 | |||
28 | static pid_t child_pid; | ||
29 | |||
30 | /* | ||
31 | * Ensures the child and parent are always "talking" about | ||
32 | * the same test sequence. (ie: that we haven't forgotten | ||
33 | * to call check_trapped() somewhere). | ||
34 | */ | ||
35 | static int nr_tests; | ||
36 | |||
37 | static void set_breakpoint_addr(void *addr, int n) | ||
38 | { | ||
39 | int ret; | ||
40 | |||
41 | ret = ptrace(PTRACE_POKEUSER, child_pid, | ||
42 | offsetof(struct user, u_debugreg[n]), addr); | ||
43 | if (ret) { | ||
44 | perror("Can't set breakpoint addr\n"); | ||
45 | exit(-1); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | static void toggle_breakpoint(int n, int type, int len, | ||
50 | int local, int global, int set) | ||
51 | { | ||
52 | int ret; | ||
53 | |||
54 | int xtype, xlen; | ||
55 | unsigned long vdr7, dr7; | ||
56 | |||
57 | switch (type) { | ||
58 | case BP_X: | ||
59 | xtype = 0; | ||
60 | break; | ||
61 | case BP_W: | ||
62 | xtype = 1; | ||
63 | break; | ||
64 | case BP_RW: | ||
65 | xtype = 3; | ||
66 | break; | ||
67 | } | ||
68 | |||
69 | switch (len) { | ||
70 | case 1: | ||
71 | xlen = 0; | ||
72 | break; | ||
73 | case 2: | ||
74 | xlen = 4; | ||
75 | break; | ||
76 | case 4: | ||
77 | xlen = 0xc; | ||
78 | break; | ||
79 | case 8: | ||
80 | xlen = 8; | ||
81 | break; | ||
82 | } | ||
83 | |||
84 | dr7 = ptrace(PTRACE_PEEKUSER, child_pid, | ||
85 | offsetof(struct user, u_debugreg[7]), 0); | ||
86 | |||
87 | vdr7 = (xlen | xtype) << 16; | ||
88 | vdr7 <<= 4 * n; | ||
89 | |||
90 | if (local) { | ||
91 | vdr7 |= 1 << (2 * n); | ||
92 | vdr7 |= 1 << 8; | ||
93 | } | ||
94 | if (global) { | ||
95 | vdr7 |= 2 << (2 * n); | ||
96 | vdr7 |= 1 << 9; | ||
97 | } | ||
98 | |||
99 | if (set) | ||
100 | dr7 |= vdr7; | ||
101 | else | ||
102 | dr7 &= ~vdr7; | ||
103 | |||
104 | ret = ptrace(PTRACE_POKEUSER, child_pid, | ||
105 | offsetof(struct user, u_debugreg[7]), dr7); | ||
106 | if (ret) { | ||
107 | perror("Can't set dr7"); | ||
108 | exit(-1); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | /* Dummy variables to test read/write accesses */ | ||
113 | static unsigned long long dummy_var[4]; | ||
114 | |||
115 | /* Dummy functions to test execution accesses */ | ||
116 | static void dummy_func(void) { } | ||
117 | static void dummy_func1(void) { } | ||
118 | static void dummy_func2(void) { } | ||
119 | static void dummy_func3(void) { } | ||
120 | |||
121 | static void (*dummy_funcs[])(void) = { | ||
122 | dummy_func, | ||
123 | dummy_func1, | ||
124 | dummy_func2, | ||
125 | dummy_func3, | ||
126 | }; | ||
127 | |||
128 | static int trapped; | ||
129 | |||
130 | static void check_trapped(void) | ||
131 | { | ||
132 | /* | ||
133 | * If we haven't trapped, wake up the parent | ||
134 | * so that it notices the failure. | ||
135 | */ | ||
136 | if (!trapped) | ||
137 | kill(getpid(), SIGUSR1); | ||
138 | trapped = 0; | ||
139 | |||
140 | nr_tests++; | ||
141 | } | ||
142 | |||
143 | static void write_var(int len) | ||
144 | { | ||
145 | char *pcval; short *psval; int *pival; long long *plval; | ||
146 | int i; | ||
147 | |||
148 | for (i = 0; i < 4; i++) { | ||
149 | switch (len) { | ||
150 | case 1: | ||
151 | pcval = (char *)&dummy_var[i]; | ||
152 | *pcval = 0xff; | ||
153 | break; | ||
154 | case 2: | ||
155 | psval = (short *)&dummy_var[i]; | ||
156 | *psval = 0xffff; | ||
157 | break; | ||
158 | case 4: | ||
159 | pival = (int *)&dummy_var[i]; | ||
160 | *pival = 0xffffffff; | ||
161 | break; | ||
162 | case 8: | ||
163 | plval = (long long *)&dummy_var[i]; | ||
164 | *plval = 0xffffffffffffffffLL; | ||
165 | break; | ||
166 | } | ||
167 | check_trapped(); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | static void read_var(int len) | ||
172 | { | ||
173 | char cval; short sval; int ival; long long lval; | ||
174 | int i; | ||
175 | |||
176 | for (i = 0; i < 4; i++) { | ||
177 | switch (len) { | ||
178 | case 1: | ||
179 | cval = *(char *)&dummy_var[i]; | ||
180 | break; | ||
181 | case 2: | ||
182 | sval = *(short *)&dummy_var[i]; | ||
183 | break; | ||
184 | case 4: | ||
185 | ival = *(int *)&dummy_var[i]; | ||
186 | break; | ||
187 | case 8: | ||
188 | lval = *(long long *)&dummy_var[i]; | ||
189 | break; | ||
190 | } | ||
191 | check_trapped(); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | /* | ||
196 | * Do the r/w/x accesses to trigger the breakpoints. And run | ||
197 | * the usual traps. | ||
198 | */ | ||
199 | static void trigger_tests(void) | ||
200 | { | ||
201 | int len, local, global, i; | ||
202 | char val; | ||
203 | int ret; | ||
204 | |||
205 | ret = ptrace(PTRACE_TRACEME, 0, NULL, 0); | ||
206 | if (ret) { | ||
207 | perror("Can't be traced?\n"); | ||
208 | return; | ||
209 | } | ||
210 | |||
211 | /* Wake up father so that it sets up the first test */ | ||
212 | kill(getpid(), SIGUSR1); | ||
213 | |||
214 | /* Test instruction breakpoints */ | ||
215 | for (local = 0; local < 2; local++) { | ||
216 | for (global = 0; global < 2; global++) { | ||
217 | if (!local && !global) | ||
218 | continue; | ||
219 | |||
220 | for (i = 0; i < 4; i++) { | ||
221 | dummy_funcs[i](); | ||
222 | check_trapped(); | ||
223 | } | ||
224 | } | ||
225 | } | ||
226 | |||
227 | /* Test write watchpoints */ | ||
228 | for (len = 1; len <= sizeof(long); len <<= 1) { | ||
229 | for (local = 0; local < 2; local++) { | ||
230 | for (global = 0; global < 2; global++) { | ||
231 | if (!local && !global) | ||
232 | continue; | ||
233 | write_var(len); | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | /* Test read/write watchpoints (on read accesses) */ | ||
239 | for (len = 1; len <= sizeof(long); len <<= 1) { | ||
240 | for (local = 0; local < 2; local++) { | ||
241 | for (global = 0; global < 2; global++) { | ||
242 | if (!local && !global) | ||
243 | continue; | ||
244 | read_var(len); | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | |||
249 | /* Icebp trap */ | ||
250 | asm(".byte 0xf1\n"); | ||
251 | check_trapped(); | ||
252 | |||
253 | /* Int 3 trap */ | ||
254 | asm("int $3\n"); | ||
255 | check_trapped(); | ||
256 | |||
257 | kill(getpid(), SIGUSR1); | ||
258 | } | ||
259 | |||
260 | static void check_success(const char *msg) | ||
261 | { | ||
262 | const char *msg2; | ||
263 | int child_nr_tests; | ||
264 | int status; | ||
265 | |||
266 | /* Wait for the child to SIGTRAP */ | ||
267 | wait(&status); | ||
268 | |||
269 | msg2 = "Failed"; | ||
270 | |||
271 | if (WSTOPSIG(status) == SIGTRAP) { | ||
272 | child_nr_tests = ptrace(PTRACE_PEEKDATA, child_pid, | ||
273 | &nr_tests, 0); | ||
274 | if (child_nr_tests == nr_tests) | ||
275 | msg2 = "Ok"; | ||
276 | if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1)) { | ||
277 | perror("Can't poke\n"); | ||
278 | exit(-1); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | nr_tests++; | ||
283 | |||
284 | printf("%s [%s]\n", msg, msg2); | ||
285 | } | ||
286 | |||
287 | static void launch_instruction_breakpoints(char *buf, int local, int global) | ||
288 | { | ||
289 | int i; | ||
290 | |||
291 | for (i = 0; i < 4; i++) { | ||
292 | set_breakpoint_addr(dummy_funcs[i], i); | ||
293 | toggle_breakpoint(i, BP_X, 1, local, global, 1); | ||
294 | ptrace(PTRACE_CONT, child_pid, NULL, 0); | ||
295 | sprintf(buf, "Test breakpoint %d with local: %d global: %d", | ||
296 | i, local, global); | ||
297 | check_success(buf); | ||
298 | toggle_breakpoint(i, BP_X, 1, local, global, 0); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | static void launch_watchpoints(char *buf, int mode, int len, | ||
303 | int local, int global) | ||
304 | { | ||
305 | const char *mode_str; | ||
306 | int i; | ||
307 | |||
308 | if (mode == BP_W) | ||
309 | mode_str = "write"; | ||
310 | else | ||
311 | mode_str = "read"; | ||
312 | |||
313 | for (i = 0; i < 4; i++) { | ||
314 | set_breakpoint_addr(&dummy_var[i], i); | ||
315 | toggle_breakpoint(i, mode, len, local, global, 1); | ||
316 | ptrace(PTRACE_CONT, child_pid, NULL, 0); | ||
317 | sprintf(buf, "Test %s watchpoint %d with len: %d local: " | ||
318 | "%d global: %d", mode_str, i, len, local, global); | ||
319 | check_success(buf); | ||
320 | toggle_breakpoint(i, mode, len, local, global, 0); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | /* Set the breakpoints and check the child successfully trigger them */ | ||
325 | static void launch_tests(void) | ||
326 | { | ||
327 | char buf[1024]; | ||
328 | int len, local, global, i; | ||
329 | |||
330 | /* Instruction breakpoints */ | ||
331 | for (local = 0; local < 2; local++) { | ||
332 | for (global = 0; global < 2; global++) { | ||
333 | if (!local && !global) | ||
334 | continue; | ||
335 | launch_instruction_breakpoints(buf, local, global); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | /* Write watchpoint */ | ||
340 | for (len = 1; len <= sizeof(long); len <<= 1) { | ||
341 | for (local = 0; local < 2; local++) { | ||
342 | for (global = 0; global < 2; global++) { | ||
343 | if (!local && !global) | ||
344 | continue; | ||
345 | launch_watchpoints(buf, BP_W, len, | ||
346 | local, global); | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | |||
351 | /* Read-Write watchpoint */ | ||
352 | for (len = 1; len <= sizeof(long); len <<= 1) { | ||
353 | for (local = 0; local < 2; local++) { | ||
354 | for (global = 0; global < 2; global++) { | ||
355 | if (!local && !global) | ||
356 | continue; | ||
357 | launch_watchpoints(buf, BP_RW, len, | ||
358 | local, global); | ||
359 | } | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /* Icebp traps */ | ||
364 | ptrace(PTRACE_CONT, child_pid, NULL, 0); | ||
365 | check_success("Test icebp"); | ||
366 | |||
367 | /* Int 3 traps */ | ||
368 | ptrace(PTRACE_CONT, child_pid, NULL, 0); | ||
369 | check_success("Test int 3 trap"); | ||
370 | |||
371 | ptrace(PTRACE_CONT, child_pid, NULL, 0); | ||
372 | } | ||
373 | |||
374 | int main(int argc, char **argv) | ||
375 | { | ||
376 | pid_t pid; | ||
377 | int ret; | ||
378 | |||
379 | pid = fork(); | ||
380 | if (!pid) { | ||
381 | trigger_tests(); | ||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | child_pid = pid; | ||
386 | |||
387 | wait(NULL); | ||
388 | |||
389 | launch_tests(); | ||
390 | |||
391 | wait(NULL); | ||
392 | |||
393 | return 0; | ||
394 | } | ||
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile new file mode 100644 index 000000000000..b336b24aa6c0 --- /dev/null +++ b/tools/testing/selftests/vm/Makefile | |||
@@ -0,0 +1,14 @@ | |||
1 | # Makefile for vm selftests | ||
2 | |||
3 | CC = $(CROSS_COMPILE)gcc | ||
4 | CFLAGS = -Wall -Wextra | ||
5 | |||
6 | all: hugepage-mmap hugepage-shm map_hugetlb | ||
7 | %: %.c | ||
8 | $(CC) $(CFLAGS) -o $@ $^ | ||
9 | |||
10 | run_tests: all | ||
11 | /bin/sh ./run_vmtests | ||
12 | |||
13 | clean: | ||
14 | $(RM) hugepage-mmap hugepage-shm map_hugetlb | ||
diff --git a/tools/testing/selftests/vm/hugepage-mmap.c b/tools/testing/selftests/vm/hugepage-mmap.c new file mode 100644 index 000000000000..a10f310d2362 --- /dev/null +++ b/tools/testing/selftests/vm/hugepage-mmap.c | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * hugepage-mmap: | ||
3 | * | ||
4 | * Example of using huge page memory in a user application using the mmap | ||
5 | * system call. Before running this application, make sure that the | ||
6 | * administrator has mounted the hugetlbfs filesystem (on some directory | ||
7 | * like /mnt) using the command mount -t hugetlbfs nodev /mnt. In this | ||
8 | * example, the app is requesting memory of size 256MB that is backed by | ||
9 | * huge pages. | ||
10 | * | ||
11 | * For the ia64 architecture, the Linux kernel reserves Region number 4 for | ||
12 | * huge pages. That means that if one requires a fixed address, a huge page | ||
13 | * aligned address starting with 0x800000... will be required. If a fixed | ||
14 | * address is not required, the kernel will select an address in the proper | ||
15 | * range. | ||
16 | * Other architectures, such as ppc64, i386 or x86_64 are not so constrained. | ||
17 | */ | ||
18 | |||
19 | #include <stdlib.h> | ||
20 | #include <stdio.h> | ||
21 | #include <unistd.h> | ||
22 | #include <sys/mman.h> | ||
23 | #include <fcntl.h> | ||
24 | |||
25 | #define FILE_NAME "huge/hugepagefile" | ||
26 | #define LENGTH (256UL*1024*1024) | ||
27 | #define PROTECTION (PROT_READ | PROT_WRITE) | ||
28 | |||
29 | /* Only ia64 requires this */ | ||
30 | #ifdef __ia64__ | ||
31 | #define ADDR (void *)(0x8000000000000000UL) | ||
32 | #define FLAGS (MAP_SHARED | MAP_FIXED) | ||
33 | #else | ||
34 | #define ADDR (void *)(0x0UL) | ||
35 | #define FLAGS (MAP_SHARED) | ||
36 | #endif | ||
37 | |||
38 | static void check_bytes(char *addr) | ||
39 | { | ||
40 | printf("First hex is %x\n", *((unsigned int *)addr)); | ||
41 | } | ||
42 | |||
43 | static void write_bytes(char *addr) | ||
44 | { | ||
45 | unsigned long i; | ||
46 | |||
47 | for (i = 0; i < LENGTH; i++) | ||
48 | *(addr + i) = (char)i; | ||
49 | } | ||
50 | |||
51 | static int read_bytes(char *addr) | ||
52 | { | ||
53 | unsigned long i; | ||
54 | |||
55 | check_bytes(addr); | ||
56 | for (i = 0; i < LENGTH; i++) | ||
57 | if (*(addr + i) != (char)i) { | ||
58 | printf("Mismatch at %lu\n", i); | ||
59 | return 1; | ||
60 | } | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | int main(void) | ||
65 | { | ||
66 | void *addr; | ||
67 | int fd, ret; | ||
68 | |||
69 | fd = open(FILE_NAME, O_CREAT | O_RDWR, 0755); | ||
70 | if (fd < 0) { | ||
71 | perror("Open failed"); | ||
72 | exit(1); | ||
73 | } | ||
74 | |||
75 | addr = mmap(ADDR, LENGTH, PROTECTION, FLAGS, fd, 0); | ||
76 | if (addr == MAP_FAILED) { | ||
77 | perror("mmap"); | ||
78 | unlink(FILE_NAME); | ||
79 | exit(1); | ||
80 | } | ||
81 | |||
82 | printf("Returned address is %p\n", addr); | ||
83 | check_bytes(addr); | ||
84 | write_bytes(addr); | ||
85 | ret = read_bytes(addr); | ||
86 | |||
87 | munmap(addr, LENGTH); | ||
88 | close(fd); | ||
89 | unlink(FILE_NAME); | ||
90 | |||
91 | return ret; | ||
92 | } | ||
diff --git a/tools/testing/selftests/vm/hugepage-shm.c b/tools/testing/selftests/vm/hugepage-shm.c new file mode 100644 index 000000000000..0d0ef4fc0c04 --- /dev/null +++ b/tools/testing/selftests/vm/hugepage-shm.c | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * hugepage-shm: | ||
3 | * | ||
4 | * Example of using huge page memory in a user application using Sys V shared | ||
5 | * memory system calls. In this example the app is requesting 256MB of | ||
6 | * memory that is backed by huge pages. The application uses the flag | ||
7 | * SHM_HUGETLB in the shmget system call to inform the kernel that it is | ||
8 | * requesting huge pages. | ||
9 | * | ||
10 | * For the ia64 architecture, the Linux kernel reserves Region number 4 for | ||
11 | * huge pages. That means that if one requires a fixed address, a huge page | ||
12 | * aligned address starting with 0x800000... will be required. If a fixed | ||
13 | * address is not required, the kernel will select an address in the proper | ||
14 | * range. | ||
15 | * Other architectures, such as ppc64, i386 or x86_64 are not so constrained. | ||
16 | * | ||
17 | * Note: The default shared memory limit is quite low on many kernels, | ||
18 | * you may need to increase it via: | ||
19 | * | ||
20 | * echo 268435456 > /proc/sys/kernel/shmmax | ||
21 | * | ||
22 | * This will increase the maximum size per shared memory segment to 256MB. | ||
23 | * The other limit that you will hit eventually is shmall which is the | ||
24 | * total amount of shared memory in pages. To set it to 16GB on a system | ||
25 | * with a 4kB pagesize do: | ||
26 | * | ||
27 | * echo 4194304 > /proc/sys/kernel/shmall | ||
28 | */ | ||
29 | |||
30 | #include <stdlib.h> | ||
31 | #include <stdio.h> | ||
32 | #include <sys/types.h> | ||
33 | #include <sys/ipc.h> | ||
34 | #include <sys/shm.h> | ||
35 | #include <sys/mman.h> | ||
36 | |||
37 | #ifndef SHM_HUGETLB | ||
38 | #define SHM_HUGETLB 04000 | ||
39 | #endif | ||
40 | |||
41 | #define LENGTH (256UL*1024*1024) | ||
42 | |||
43 | #define dprintf(x) printf(x) | ||
44 | |||
45 | /* Only ia64 requires this */ | ||
46 | #ifdef __ia64__ | ||
47 | #define ADDR (void *)(0x8000000000000000UL) | ||
48 | #define SHMAT_FLAGS (SHM_RND) | ||
49 | #else | ||
50 | #define ADDR (void *)(0x0UL) | ||
51 | #define SHMAT_FLAGS (0) | ||
52 | #endif | ||
53 | |||
54 | int main(void) | ||
55 | { | ||
56 | int shmid; | ||
57 | unsigned long i; | ||
58 | char *shmaddr; | ||
59 | |||
60 | shmid = shmget(2, LENGTH, SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W); | ||
61 | if (shmid < 0) { | ||
62 | perror("shmget"); | ||
63 | exit(1); | ||
64 | } | ||
65 | printf("shmid: 0x%x\n", shmid); | ||
66 | |||
67 | shmaddr = shmat(shmid, ADDR, SHMAT_FLAGS); | ||
68 | if (shmaddr == (char *)-1) { | ||
69 | perror("Shared memory attach failure"); | ||
70 | shmctl(shmid, IPC_RMID, NULL); | ||
71 | exit(2); | ||
72 | } | ||
73 | printf("shmaddr: %p\n", shmaddr); | ||
74 | |||
75 | dprintf("Starting the writes:\n"); | ||
76 | for (i = 0; i < LENGTH; i++) { | ||
77 | shmaddr[i] = (char)(i); | ||
78 | if (!(i % (1024 * 1024))) | ||
79 | dprintf("."); | ||
80 | } | ||
81 | dprintf("\n"); | ||
82 | |||
83 | dprintf("Starting the Check..."); | ||
84 | for (i = 0; i < LENGTH; i++) | ||
85 | if (shmaddr[i] != (char)i) { | ||
86 | printf("\nIndex %lu mismatched\n", i); | ||
87 | exit(3); | ||
88 | } | ||
89 | dprintf("Done.\n"); | ||
90 | |||
91 | if (shmdt((const void *)shmaddr) != 0) { | ||
92 | perror("Detach failure"); | ||
93 | shmctl(shmid, IPC_RMID, NULL); | ||
94 | exit(4); | ||
95 | } | ||
96 | |||
97 | shmctl(shmid, IPC_RMID, NULL); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
diff --git a/tools/testing/selftests/vm/map_hugetlb.c b/tools/testing/selftests/vm/map_hugetlb.c new file mode 100644 index 000000000000..ac56639dd4a9 --- /dev/null +++ b/tools/testing/selftests/vm/map_hugetlb.c | |||
@@ -0,0 +1,79 @@ | |||
1 | /* | ||
2 | * Example of using hugepage memory in a user application using the mmap | ||
3 | * system call with MAP_HUGETLB flag. Before running this program make | ||
4 | * sure the administrator has allocated enough default sized huge pages | ||
5 | * to cover the 256 MB allocation. | ||
6 | * | ||
7 | * For ia64 architecture, Linux kernel reserves Region number 4 for hugepages. | ||
8 | * That means the addresses starting with 0x800000... will need to be | ||
9 | * specified. Specifying a fixed address is not required on ppc64, i386 | ||
10 | * or x86_64. | ||
11 | */ | ||
12 | #include <stdlib.h> | ||
13 | #include <stdio.h> | ||
14 | #include <unistd.h> | ||
15 | #include <sys/mman.h> | ||
16 | #include <fcntl.h> | ||
17 | |||
18 | #define LENGTH (256UL*1024*1024) | ||
19 | #define PROTECTION (PROT_READ | PROT_WRITE) | ||
20 | |||
21 | #ifndef MAP_HUGETLB | ||
22 | #define MAP_HUGETLB 0x40000 /* arch specific */ | ||
23 | #endif | ||
24 | |||
25 | /* Only ia64 requires this */ | ||
26 | #ifdef __ia64__ | ||
27 | #define ADDR (void *)(0x8000000000000000UL) | ||
28 | #define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_FIXED) | ||
29 | #else | ||
30 | #define ADDR (void *)(0x0UL) | ||
31 | #define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB) | ||
32 | #endif | ||
33 | |||
34 | static void check_bytes(char *addr) | ||
35 | { | ||
36 | printf("First hex is %x\n", *((unsigned int *)addr)); | ||
37 | } | ||
38 | |||
39 | static void write_bytes(char *addr) | ||
40 | { | ||
41 | unsigned long i; | ||
42 | |||
43 | for (i = 0; i < LENGTH; i++) | ||
44 | *(addr + i) = (char)i; | ||
45 | } | ||
46 | |||
47 | static int read_bytes(char *addr) | ||
48 | { | ||
49 | unsigned long i; | ||
50 | |||
51 | check_bytes(addr); | ||
52 | for (i = 0; i < LENGTH; i++) | ||
53 | if (*(addr + i) != (char)i) { | ||
54 | printf("Mismatch at %lu\n", i); | ||
55 | return 1; | ||
56 | } | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | int main(void) | ||
61 | { | ||
62 | void *addr; | ||
63 | int ret; | ||
64 | |||
65 | addr = mmap(ADDR, LENGTH, PROTECTION, FLAGS, 0, 0); | ||
66 | if (addr == MAP_FAILED) { | ||
67 | perror("mmap"); | ||
68 | exit(1); | ||
69 | } | ||
70 | |||
71 | printf("Returned address is %p\n", addr); | ||
72 | check_bytes(addr); | ||
73 | write_bytes(addr); | ||
74 | ret = read_bytes(addr); | ||
75 | |||
76 | munmap(addr, LENGTH); | ||
77 | |||
78 | return ret; | ||
79 | } | ||
diff --git a/tools/testing/selftests/vm/run_vmtests b/tools/testing/selftests/vm/run_vmtests new file mode 100644 index 000000000000..8b40bd5e5cc2 --- /dev/null +++ b/tools/testing/selftests/vm/run_vmtests | |||
@@ -0,0 +1,77 @@ | |||
1 | #!/bin/bash | ||
2 | #please run as root | ||
3 | |||
4 | #we need 256M, below is the size in kB | ||
5 | needmem=262144 | ||
6 | mnt=./huge | ||
7 | |||
8 | #get pagesize and freepages from /proc/meminfo | ||
9 | while read name size unit; do | ||
10 | if [ "$name" = "HugePages_Free:" ]; then | ||
11 | freepgs=$size | ||
12 | fi | ||
13 | if [ "$name" = "Hugepagesize:" ]; then | ||
14 | pgsize=$size | ||
15 | fi | ||
16 | done < /proc/meminfo | ||
17 | |||
18 | #set proper nr_hugepages | ||
19 | if [ -n "$freepgs" ] && [ -n "$pgsize" ]; then | ||
20 | nr_hugepgs=`cat /proc/sys/vm/nr_hugepages` | ||
21 | needpgs=`expr $needmem / $pgsize` | ||
22 | if [ $freepgs -lt $needpgs ]; then | ||
23 | lackpgs=$(( $needpgs - $freepgs )) | ||
24 | echo $(( $lackpgs + $nr_hugepgs )) > /proc/sys/vm/nr_hugepages | ||
25 | if [ $? -ne 0 ]; then | ||
26 | echo "Please run this test as root" | ||
27 | exit 1 | ||
28 | fi | ||
29 | fi | ||
30 | else | ||
31 | echo "no hugetlbfs support in kernel?" | ||
32 | exit 1 | ||
33 | fi | ||
34 | |||
35 | mkdir $mnt | ||
36 | mount -t hugetlbfs none $mnt | ||
37 | |||
38 | echo "--------------------" | ||
39 | echo "runing hugepage-mmap" | ||
40 | echo "--------------------" | ||
41 | ./hugepage-mmap | ||
42 | if [ $? -ne 0 ]; then | ||
43 | echo "[FAIL]" | ||
44 | else | ||
45 | echo "[PASS]" | ||
46 | fi | ||
47 | |||
48 | shmmax=`cat /proc/sys/kernel/shmmax` | ||
49 | shmall=`cat /proc/sys/kernel/shmall` | ||
50 | echo 268435456 > /proc/sys/kernel/shmmax | ||
51 | echo 4194304 > /proc/sys/kernel/shmall | ||
52 | echo "--------------------" | ||
53 | echo "runing hugepage-shm" | ||
54 | echo "--------------------" | ||
55 | ./hugepage-shm | ||
56 | if [ $? -ne 0 ]; then | ||
57 | echo "[FAIL]" | ||
58 | else | ||
59 | echo "[PASS]" | ||
60 | fi | ||
61 | echo $shmmax > /proc/sys/kernel/shmmax | ||
62 | echo $shmall > /proc/sys/kernel/shmall | ||
63 | |||
64 | echo "--------------------" | ||
65 | echo "runing map_hugetlb" | ||
66 | echo "--------------------" | ||
67 | ./map_hugetlb | ||
68 | if [ $? -ne 0 ]; then | ||
69 | echo "[FAIL]" | ||
70 | else | ||
71 | echo "[PASS]" | ||
72 | fi | ||
73 | |||
74 | #cleanup | ||
75 | umount $mnt | ||
76 | rm -rf $mnt | ||
77 | echo $nr_hugepgs > /proc/sys/vm/nr_hugepages | ||
diff --git a/tools/usb/Makefile b/tools/usb/Makefile index 8b704af14349..396d6c44e9d7 100644 --- a/tools/usb/Makefile +++ b/tools/usb/Makefile | |||
@@ -3,7 +3,7 @@ | |||
3 | CC = $(CROSS_COMPILE)gcc | 3 | CC = $(CROSS_COMPILE)gcc |
4 | PTHREAD_LIBS = -lpthread | 4 | PTHREAD_LIBS = -lpthread |
5 | WARNINGS = -Wall -Wextra | 5 | WARNINGS = -Wall -Wextra |
6 | CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) | 6 | CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) -I../include |
7 | 7 | ||
8 | all: testusb ffs-test | 8 | all: testusb ffs-test |
9 | %: %.c | 9 | %: %.c |
diff --git a/tools/usb/ffs-test.c b/tools/usb/ffs-test.c index b9c798631699..4b107b5e623f 100644 --- a/tools/usb/ffs-test.c +++ b/tools/usb/ffs-test.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * ffs-test.c.c -- user mode filesystem api for usb composite function | 2 | * ffs-test.c.c -- user mode filesystem api for usb composite function |
3 | * | 3 | * |
4 | * Copyright (C) 2010 Samsung Electronics | 4 | * Copyright (C) 2010 Samsung Electronics |
5 | * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> | 5 | * Author: Michal Nazarewicz <mina86@mina86.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by | 8 | * it under the terms of the GNU General Public License as published by |
@@ -36,6 +36,7 @@ | |||
36 | #include <sys/stat.h> | 36 | #include <sys/stat.h> |
37 | #include <sys/types.h> | 37 | #include <sys/types.h> |
38 | #include <unistd.h> | 38 | #include <unistd.h> |
39 | #include <tools/le_byteshift.h> | ||
39 | 40 | ||
40 | #include "../../include/linux/usb/functionfs.h" | 41 | #include "../../include/linux/usb/functionfs.h" |
41 | 42 | ||
@@ -47,34 +48,6 @@ | |||
47 | #define le32_to_cpu(x) le32toh(x) | 48 | #define le32_to_cpu(x) le32toh(x) |
48 | #define le16_to_cpu(x) le16toh(x) | 49 | #define le16_to_cpu(x) le16toh(x) |
49 | 50 | ||
50 | static inline __u16 get_unaligned_le16(const void *_ptr) | ||
51 | { | ||
52 | const __u8 *ptr = _ptr; | ||
53 | return ptr[0] | (ptr[1] << 8); | ||
54 | } | ||
55 | |||
56 | static inline __u32 get_unaligned_le32(const void *_ptr) | ||
57 | { | ||
58 | const __u8 *ptr = _ptr; | ||
59 | return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); | ||
60 | } | ||
61 | |||
62 | static inline void put_unaligned_le16(__u16 val, void *_ptr) | ||
63 | { | ||
64 | __u8 *ptr = _ptr; | ||
65 | *ptr++ = val; | ||
66 | *ptr++ = val >> 8; | ||
67 | } | ||
68 | |||
69 | static inline void put_unaligned_le32(__u32 val, void *_ptr) | ||
70 | { | ||
71 | __u8 *ptr = _ptr; | ||
72 | *ptr++ = val; | ||
73 | *ptr++ = val >> 8; | ||
74 | *ptr++ = val >> 16; | ||
75 | *ptr++ = val >> 24; | ||
76 | } | ||
77 | |||
78 | 51 | ||
79 | /******************** Messages and Errors ***********************************/ | 52 | /******************** Messages and Errors ***********************************/ |
80 | 53 | ||
diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c index f08e89463842..6e0f56701e44 100644 --- a/tools/usb/testusb.c +++ b/tools/usb/testusb.c | |||
@@ -3,7 +3,7 @@ | |||
3 | /* | 3 | /* |
4 | * Copyright (c) 2002 by David Brownell | 4 | * Copyright (c) 2002 by David Brownell |
5 | * Copyright (c) 2010 by Samsung Electronics | 5 | * Copyright (c) 2010 by Samsung Electronics |
6 | * Author: Michal Nazarewicz <m.nazarewicz@samsung.com> | 6 | * Author: Michal Nazarewicz <mina86@mina86.com> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify it | 8 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the | 9 | * under the terms of the GNU General Public License as published by the |
diff --git a/tools/virtio/linux/hrtimer.h b/tools/virtio/linux/hrtimer.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/virtio/linux/hrtimer.h | |||
diff --git a/tools/virtio/linux/module.h b/tools/virtio/linux/module.h new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/tools/virtio/linux/module.h | |||
diff --git a/tools/virtio/linux/virtio.h b/tools/virtio/linux/virtio.h index 669bcdd45805..7579f19e61e0 100644 --- a/tools/virtio/linux/virtio.h +++ b/tools/virtio/linux/virtio.h | |||
@@ -181,26 +181,20 @@ struct virtqueue { | |||
181 | #define smp_mb() mb() | 181 | #define smp_mb() mb() |
182 | # define smp_rmb() barrier() | 182 | # define smp_rmb() barrier() |
183 | # define smp_wmb() barrier() | 183 | # define smp_wmb() barrier() |
184 | /* Weak barriers should be used. If not - it's a bug */ | ||
185 | # define rmb() abort() | ||
186 | # define wmb() abort() | ||
184 | #else | 187 | #else |
185 | #error Please fill in barrier macros | 188 | #error Please fill in barrier macros |
186 | #endif | 189 | #endif |
187 | 190 | ||
188 | /* Interfaces exported by virtio_ring. */ | 191 | /* Interfaces exported by virtio_ring. */ |
189 | int virtqueue_add_buf_gfp(struct virtqueue *vq, | 192 | int virtqueue_add_buf(struct virtqueue *vq, |
190 | struct scatterlist sg[], | 193 | struct scatterlist sg[], |
191 | unsigned int out_num, | 194 | unsigned int out_num, |
192 | unsigned int in_num, | 195 | unsigned int in_num, |
193 | void *data, | 196 | void *data, |
194 | gfp_t gfp); | 197 | gfp_t gfp); |
195 | |||
196 | static inline int virtqueue_add_buf(struct virtqueue *vq, | ||
197 | struct scatterlist sg[], | ||
198 | unsigned int out_num, | ||
199 | unsigned int in_num, | ||
200 | void *data) | ||
201 | { | ||
202 | return virtqueue_add_buf_gfp(vq, sg, out_num, in_num, data, GFP_ATOMIC); | ||
203 | } | ||
204 | 198 | ||
205 | void virtqueue_kick(struct virtqueue *vq); | 199 | void virtqueue_kick(struct virtqueue *vq); |
206 | 200 | ||
@@ -214,6 +208,7 @@ void *virtqueue_detach_unused_buf(struct virtqueue *vq); | |||
214 | struct virtqueue *vring_new_virtqueue(unsigned int num, | 208 | struct virtqueue *vring_new_virtqueue(unsigned int num, |
215 | unsigned int vring_align, | 209 | unsigned int vring_align, |
216 | struct virtio_device *vdev, | 210 | struct virtio_device *vdev, |
211 | bool weak_barriers, | ||
217 | void *pages, | 212 | void *pages, |
218 | void (*notify)(struct virtqueue *vq), | 213 | void (*notify)(struct virtqueue *vq), |
219 | void (*callback)(struct virtqueue *vq), | 214 | void (*callback)(struct virtqueue *vq), |
diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c index 74d3331bdaf9..6bf95f995364 100644 --- a/tools/virtio/virtio_test.c +++ b/tools/virtio/virtio_test.c | |||
@@ -92,7 +92,8 @@ static void vq_info_add(struct vdev_info *dev, int num) | |||
92 | assert(r >= 0); | 92 | assert(r >= 0); |
93 | memset(info->ring, 0, vring_size(num, 4096)); | 93 | memset(info->ring, 0, vring_size(num, 4096)); |
94 | vring_init(&info->vring, num, info->ring, 4096); | 94 | vring_init(&info->vring, num, info->ring, 4096); |
95 | info->vq = vring_new_virtqueue(info->vring.num, 4096, &dev->vdev, info->ring, | 95 | info->vq = vring_new_virtqueue(info->vring.num, 4096, &dev->vdev, |
96 | true, info->ring, | ||
96 | vq_notify, vq_callback, "test"); | 97 | vq_notify, vq_callback, "test"); |
97 | assert(info->vq); | 98 | assert(info->vq); |
98 | info->vq->priv = info; | 99 | info->vq->priv = info; |
@@ -160,7 +161,8 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq, int bufs) | |||
160 | if (started < bufs) { | 161 | if (started < bufs) { |
161 | sg_init_one(&sl, dev->buf, dev->buf_size); | 162 | sg_init_one(&sl, dev->buf, dev->buf_size); |
162 | r = virtqueue_add_buf(vq->vq, &sl, 1, 0, | 163 | r = virtqueue_add_buf(vq->vq, &sl, 1, 0, |
163 | dev->buf + started); | 164 | dev->buf + started, |
165 | GFP_ATOMIC); | ||
164 | if (likely(r >= 0)) { | 166 | if (likely(r >= 0)) { |
165 | ++started; | 167 | ++started; |
166 | virtqueue_kick(vq->vq); | 168 | virtqueue_kick(vq->vq); |
diff --git a/tools/vm/Makefile b/tools/vm/Makefile new file mode 100644 index 000000000000..8e30e5c40f8a --- /dev/null +++ b/tools/vm/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # Makefile for vm tools | ||
2 | |||
3 | CC = $(CROSS_COMPILE)gcc | ||
4 | CFLAGS = -Wall -Wextra | ||
5 | |||
6 | all: page-types slabinfo | ||
7 | %: %.c | ||
8 | $(CC) $(CFLAGS) -o $@ $^ | ||
9 | |||
10 | clean: | ||
11 | $(RM) page-types slabinfo | ||
diff --git a/tools/vm/page-types.c b/tools/vm/page-types.c new file mode 100644 index 000000000000..7dab7b25b5c6 --- /dev/null +++ b/tools/vm/page-types.c | |||
@@ -0,0 +1,1102 @@ | |||
1 | /* | ||
2 | * page-types: Tool for querying page flags | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the Free | ||
6 | * Software Foundation; version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should find a copy of v2 of the GNU General Public License somewhere on | ||
14 | * your Linux system; if not, write to the Free Software Foundation, Inc., 59 | ||
15 | * Temple Place, Suite 330, Boston, MA 02111-1307 USA. | ||
16 | * | ||
17 | * Copyright (C) 2009 Intel corporation | ||
18 | * | ||
19 | * Authors: Wu Fengguang <fengguang.wu@intel.com> | ||
20 | */ | ||
21 | |||
22 | #define _LARGEFILE64_SOURCE | ||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <unistd.h> | ||
26 | #include <stdint.h> | ||
27 | #include <stdarg.h> | ||
28 | #include <string.h> | ||
29 | #include <getopt.h> | ||
30 | #include <limits.h> | ||
31 | #include <assert.h> | ||
32 | #include <sys/types.h> | ||
33 | #include <sys/errno.h> | ||
34 | #include <sys/fcntl.h> | ||
35 | #include <sys/mount.h> | ||
36 | #include <sys/statfs.h> | ||
37 | #include "../../include/linux/magic.h" | ||
38 | |||
39 | |||
40 | #ifndef MAX_PATH | ||
41 | # define MAX_PATH 256 | ||
42 | #endif | ||
43 | |||
44 | #ifndef STR | ||
45 | # define _STR(x) #x | ||
46 | # define STR(x) _STR(x) | ||
47 | #endif | ||
48 | |||
49 | /* | ||
50 | * pagemap kernel ABI bits | ||
51 | */ | ||
52 | |||
53 | #define PM_ENTRY_BYTES sizeof(uint64_t) | ||
54 | #define PM_STATUS_BITS 3 | ||
55 | #define PM_STATUS_OFFSET (64 - PM_STATUS_BITS) | ||
56 | #define PM_STATUS_MASK (((1LL << PM_STATUS_BITS) - 1) << PM_STATUS_OFFSET) | ||
57 | #define PM_STATUS(nr) (((nr) << PM_STATUS_OFFSET) & PM_STATUS_MASK) | ||
58 | #define PM_PSHIFT_BITS 6 | ||
59 | #define PM_PSHIFT_OFFSET (PM_STATUS_OFFSET - PM_PSHIFT_BITS) | ||
60 | #define PM_PSHIFT_MASK (((1LL << PM_PSHIFT_BITS) - 1) << PM_PSHIFT_OFFSET) | ||
61 | #define PM_PSHIFT(x) (((u64) (x) << PM_PSHIFT_OFFSET) & PM_PSHIFT_MASK) | ||
62 | #define PM_PFRAME_MASK ((1LL << PM_PSHIFT_OFFSET) - 1) | ||
63 | #define PM_PFRAME(x) ((x) & PM_PFRAME_MASK) | ||
64 | |||
65 | #define PM_PRESENT PM_STATUS(4LL) | ||
66 | #define PM_SWAP PM_STATUS(2LL) | ||
67 | |||
68 | |||
69 | /* | ||
70 | * kernel page flags | ||
71 | */ | ||
72 | |||
73 | #define KPF_BYTES 8 | ||
74 | #define PROC_KPAGEFLAGS "/proc/kpageflags" | ||
75 | |||
76 | /* copied from kpageflags_read() */ | ||
77 | #define KPF_LOCKED 0 | ||
78 | #define KPF_ERROR 1 | ||
79 | #define KPF_REFERENCED 2 | ||
80 | #define KPF_UPTODATE 3 | ||
81 | #define KPF_DIRTY 4 | ||
82 | #define KPF_LRU 5 | ||
83 | #define KPF_ACTIVE 6 | ||
84 | #define KPF_SLAB 7 | ||
85 | #define KPF_WRITEBACK 8 | ||
86 | #define KPF_RECLAIM 9 | ||
87 | #define KPF_BUDDY 10 | ||
88 | |||
89 | /* [11-20] new additions in 2.6.31 */ | ||
90 | #define KPF_MMAP 11 | ||
91 | #define KPF_ANON 12 | ||
92 | #define KPF_SWAPCACHE 13 | ||
93 | #define KPF_SWAPBACKED 14 | ||
94 | #define KPF_COMPOUND_HEAD 15 | ||
95 | #define KPF_COMPOUND_TAIL 16 | ||
96 | #define KPF_HUGE 17 | ||
97 | #define KPF_UNEVICTABLE 18 | ||
98 | #define KPF_HWPOISON 19 | ||
99 | #define KPF_NOPAGE 20 | ||
100 | #define KPF_KSM 21 | ||
101 | #define KPF_THP 22 | ||
102 | |||
103 | /* [32-] kernel hacking assistances */ | ||
104 | #define KPF_RESERVED 32 | ||
105 | #define KPF_MLOCKED 33 | ||
106 | #define KPF_MAPPEDTODISK 34 | ||
107 | #define KPF_PRIVATE 35 | ||
108 | #define KPF_PRIVATE_2 36 | ||
109 | #define KPF_OWNER_PRIVATE 37 | ||
110 | #define KPF_ARCH 38 | ||
111 | #define KPF_UNCACHED 39 | ||
112 | |||
113 | /* [48-] take some arbitrary free slots for expanding overloaded flags | ||
114 | * not part of kernel API | ||
115 | */ | ||
116 | #define KPF_READAHEAD 48 | ||
117 | #define KPF_SLOB_FREE 49 | ||
118 | #define KPF_SLUB_FROZEN 50 | ||
119 | #define KPF_SLUB_DEBUG 51 | ||
120 | |||
121 | #define KPF_ALL_BITS ((uint64_t)~0ULL) | ||
122 | #define KPF_HACKERS_BITS (0xffffULL << 32) | ||
123 | #define KPF_OVERLOADED_BITS (0xffffULL << 48) | ||
124 | #define BIT(name) (1ULL << KPF_##name) | ||
125 | #define BITS_COMPOUND (BIT(COMPOUND_HEAD) | BIT(COMPOUND_TAIL)) | ||
126 | |||
127 | static const char * const page_flag_names[] = { | ||
128 | [KPF_LOCKED] = "L:locked", | ||
129 | [KPF_ERROR] = "E:error", | ||
130 | [KPF_REFERENCED] = "R:referenced", | ||
131 | [KPF_UPTODATE] = "U:uptodate", | ||
132 | [KPF_DIRTY] = "D:dirty", | ||
133 | [KPF_LRU] = "l:lru", | ||
134 | [KPF_ACTIVE] = "A:active", | ||
135 | [KPF_SLAB] = "S:slab", | ||
136 | [KPF_WRITEBACK] = "W:writeback", | ||
137 | [KPF_RECLAIM] = "I:reclaim", | ||
138 | [KPF_BUDDY] = "B:buddy", | ||
139 | |||
140 | [KPF_MMAP] = "M:mmap", | ||
141 | [KPF_ANON] = "a:anonymous", | ||
142 | [KPF_SWAPCACHE] = "s:swapcache", | ||
143 | [KPF_SWAPBACKED] = "b:swapbacked", | ||
144 | [KPF_COMPOUND_HEAD] = "H:compound_head", | ||
145 | [KPF_COMPOUND_TAIL] = "T:compound_tail", | ||
146 | [KPF_HUGE] = "G:huge", | ||
147 | [KPF_UNEVICTABLE] = "u:unevictable", | ||
148 | [KPF_HWPOISON] = "X:hwpoison", | ||
149 | [KPF_NOPAGE] = "n:nopage", | ||
150 | [KPF_KSM] = "x:ksm", | ||
151 | [KPF_THP] = "t:thp", | ||
152 | |||
153 | [KPF_RESERVED] = "r:reserved", | ||
154 | [KPF_MLOCKED] = "m:mlocked", | ||
155 | [KPF_MAPPEDTODISK] = "d:mappedtodisk", | ||
156 | [KPF_PRIVATE] = "P:private", | ||
157 | [KPF_PRIVATE_2] = "p:private_2", | ||
158 | [KPF_OWNER_PRIVATE] = "O:owner_private", | ||
159 | [KPF_ARCH] = "h:arch", | ||
160 | [KPF_UNCACHED] = "c:uncached", | ||
161 | |||
162 | [KPF_READAHEAD] = "I:readahead", | ||
163 | [KPF_SLOB_FREE] = "P:slob_free", | ||
164 | [KPF_SLUB_FROZEN] = "A:slub_frozen", | ||
165 | [KPF_SLUB_DEBUG] = "E:slub_debug", | ||
166 | }; | ||
167 | |||
168 | |||
169 | static const char * const debugfs_known_mountpoints[] = { | ||
170 | "/sys/kernel/debug", | ||
171 | "/debug", | ||
172 | 0, | ||
173 | }; | ||
174 | |||
175 | /* | ||
176 | * data structures | ||
177 | */ | ||
178 | |||
179 | static int opt_raw; /* for kernel developers */ | ||
180 | static int opt_list; /* list pages (in ranges) */ | ||
181 | static int opt_no_summary; /* don't show summary */ | ||
182 | static pid_t opt_pid; /* process to walk */ | ||
183 | |||
184 | #define MAX_ADDR_RANGES 1024 | ||
185 | static int nr_addr_ranges; | ||
186 | static unsigned long opt_offset[MAX_ADDR_RANGES]; | ||
187 | static unsigned long opt_size[MAX_ADDR_RANGES]; | ||
188 | |||
189 | #define MAX_VMAS 10240 | ||
190 | static int nr_vmas; | ||
191 | static unsigned long pg_start[MAX_VMAS]; | ||
192 | static unsigned long pg_end[MAX_VMAS]; | ||
193 | |||
194 | #define MAX_BIT_FILTERS 64 | ||
195 | static int nr_bit_filters; | ||
196 | static uint64_t opt_mask[MAX_BIT_FILTERS]; | ||
197 | static uint64_t opt_bits[MAX_BIT_FILTERS]; | ||
198 | |||
199 | static int page_size; | ||
200 | |||
201 | static int pagemap_fd; | ||
202 | static int kpageflags_fd; | ||
203 | |||
204 | static int opt_hwpoison; | ||
205 | static int opt_unpoison; | ||
206 | |||
207 | static char hwpoison_debug_fs[MAX_PATH+1]; | ||
208 | static int hwpoison_inject_fd; | ||
209 | static int hwpoison_forget_fd; | ||
210 | |||
211 | #define HASH_SHIFT 13 | ||
212 | #define HASH_SIZE (1 << HASH_SHIFT) | ||
213 | #define HASH_MASK (HASH_SIZE - 1) | ||
214 | #define HASH_KEY(flags) (flags & HASH_MASK) | ||
215 | |||
216 | static unsigned long total_pages; | ||
217 | static unsigned long nr_pages[HASH_SIZE]; | ||
218 | static uint64_t page_flags[HASH_SIZE]; | ||
219 | |||
220 | |||
221 | /* | ||
222 | * helper functions | ||
223 | */ | ||
224 | |||
225 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) | ||
226 | |||
227 | #define min_t(type, x, y) ({ \ | ||
228 | type __min1 = (x); \ | ||
229 | type __min2 = (y); \ | ||
230 | __min1 < __min2 ? __min1 : __min2; }) | ||
231 | |||
232 | #define max_t(type, x, y) ({ \ | ||
233 | type __max1 = (x); \ | ||
234 | type __max2 = (y); \ | ||
235 | __max1 > __max2 ? __max1 : __max2; }) | ||
236 | |||
237 | static unsigned long pages2mb(unsigned long pages) | ||
238 | { | ||
239 | return (pages * page_size) >> 20; | ||
240 | } | ||
241 | |||
242 | static void fatal(const char *x, ...) | ||
243 | { | ||
244 | va_list ap; | ||
245 | |||
246 | va_start(ap, x); | ||
247 | vfprintf(stderr, x, ap); | ||
248 | va_end(ap); | ||
249 | exit(EXIT_FAILURE); | ||
250 | } | ||
251 | |||
252 | static int checked_open(const char *pathname, int flags) | ||
253 | { | ||
254 | int fd = open(pathname, flags); | ||
255 | |||
256 | if (fd < 0) { | ||
257 | perror(pathname); | ||
258 | exit(EXIT_FAILURE); | ||
259 | } | ||
260 | |||
261 | return fd; | ||
262 | } | ||
263 | |||
264 | /* | ||
265 | * pagemap/kpageflags routines | ||
266 | */ | ||
267 | |||
268 | static unsigned long do_u64_read(int fd, char *name, | ||
269 | uint64_t *buf, | ||
270 | unsigned long index, | ||
271 | unsigned long count) | ||
272 | { | ||
273 | long bytes; | ||
274 | |||
275 | if (index > ULONG_MAX / 8) | ||
276 | fatal("index overflow: %lu\n", index); | ||
277 | |||
278 | if (lseek(fd, index * 8, SEEK_SET) < 0) { | ||
279 | perror(name); | ||
280 | exit(EXIT_FAILURE); | ||
281 | } | ||
282 | |||
283 | bytes = read(fd, buf, count * 8); | ||
284 | if (bytes < 0) { | ||
285 | perror(name); | ||
286 | exit(EXIT_FAILURE); | ||
287 | } | ||
288 | if (bytes % 8) | ||
289 | fatal("partial read: %lu bytes\n", bytes); | ||
290 | |||
291 | return bytes / 8; | ||
292 | } | ||
293 | |||
294 | static unsigned long kpageflags_read(uint64_t *buf, | ||
295 | unsigned long index, | ||
296 | unsigned long pages) | ||
297 | { | ||
298 | return do_u64_read(kpageflags_fd, PROC_KPAGEFLAGS, buf, index, pages); | ||
299 | } | ||
300 | |||
301 | static unsigned long pagemap_read(uint64_t *buf, | ||
302 | unsigned long index, | ||
303 | unsigned long pages) | ||
304 | { | ||
305 | return do_u64_read(pagemap_fd, "/proc/pid/pagemap", buf, index, pages); | ||
306 | } | ||
307 | |||
308 | static unsigned long pagemap_pfn(uint64_t val) | ||
309 | { | ||
310 | unsigned long pfn; | ||
311 | |||
312 | if (val & PM_PRESENT) | ||
313 | pfn = PM_PFRAME(val); | ||
314 | else | ||
315 | pfn = 0; | ||
316 | |||
317 | return pfn; | ||
318 | } | ||
319 | |||
320 | |||
321 | /* | ||
322 | * page flag names | ||
323 | */ | ||
324 | |||
325 | static char *page_flag_name(uint64_t flags) | ||
326 | { | ||
327 | static char buf[65]; | ||
328 | int present; | ||
329 | int i, j; | ||
330 | |||
331 | for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) { | ||
332 | present = (flags >> i) & 1; | ||
333 | if (!page_flag_names[i]) { | ||
334 | if (present) | ||
335 | fatal("unknown flag bit %d\n", i); | ||
336 | continue; | ||
337 | } | ||
338 | buf[j++] = present ? page_flag_names[i][0] : '_'; | ||
339 | } | ||
340 | |||
341 | return buf; | ||
342 | } | ||
343 | |||
344 | static char *page_flag_longname(uint64_t flags) | ||
345 | { | ||
346 | static char buf[1024]; | ||
347 | int i, n; | ||
348 | |||
349 | for (i = 0, n = 0; i < ARRAY_SIZE(page_flag_names); i++) { | ||
350 | if (!page_flag_names[i]) | ||
351 | continue; | ||
352 | if ((flags >> i) & 1) | ||
353 | n += snprintf(buf + n, sizeof(buf) - n, "%s,", | ||
354 | page_flag_names[i] + 2); | ||
355 | } | ||
356 | if (n) | ||
357 | n--; | ||
358 | buf[n] = '\0'; | ||
359 | |||
360 | return buf; | ||
361 | } | ||
362 | |||
363 | |||
364 | /* | ||
365 | * page list and summary | ||
366 | */ | ||
367 | |||
368 | static void show_page_range(unsigned long voffset, | ||
369 | unsigned long offset, uint64_t flags) | ||
370 | { | ||
371 | static uint64_t flags0; | ||
372 | static unsigned long voff; | ||
373 | static unsigned long index; | ||
374 | static unsigned long count; | ||
375 | |||
376 | if (flags == flags0 && offset == index + count && | ||
377 | (!opt_pid || voffset == voff + count)) { | ||
378 | count++; | ||
379 | return; | ||
380 | } | ||
381 | |||
382 | if (count) { | ||
383 | if (opt_pid) | ||
384 | printf("%lx\t", voff); | ||
385 | printf("%lx\t%lx\t%s\n", | ||
386 | index, count, page_flag_name(flags0)); | ||
387 | } | ||
388 | |||
389 | flags0 = flags; | ||
390 | index = offset; | ||
391 | voff = voffset; | ||
392 | count = 1; | ||
393 | } | ||
394 | |||
395 | static void show_page(unsigned long voffset, | ||
396 | unsigned long offset, uint64_t flags) | ||
397 | { | ||
398 | if (opt_pid) | ||
399 | printf("%lx\t", voffset); | ||
400 | printf("%lx\t%s\n", offset, page_flag_name(flags)); | ||
401 | } | ||
402 | |||
403 | static void show_summary(void) | ||
404 | { | ||
405 | int i; | ||
406 | |||
407 | printf(" flags\tpage-count MB" | ||
408 | " symbolic-flags\t\t\tlong-symbolic-flags\n"); | ||
409 | |||
410 | for (i = 0; i < ARRAY_SIZE(nr_pages); i++) { | ||
411 | if (nr_pages[i]) | ||
412 | printf("0x%016llx\t%10lu %8lu %s\t%s\n", | ||
413 | (unsigned long long)page_flags[i], | ||
414 | nr_pages[i], | ||
415 | pages2mb(nr_pages[i]), | ||
416 | page_flag_name(page_flags[i]), | ||
417 | page_flag_longname(page_flags[i])); | ||
418 | } | ||
419 | |||
420 | printf(" total\t%10lu %8lu\n", | ||
421 | total_pages, pages2mb(total_pages)); | ||
422 | } | ||
423 | |||
424 | |||
425 | /* | ||
426 | * page flag filters | ||
427 | */ | ||
428 | |||
429 | static int bit_mask_ok(uint64_t flags) | ||
430 | { | ||
431 | int i; | ||
432 | |||
433 | for (i = 0; i < nr_bit_filters; i++) { | ||
434 | if (opt_bits[i] == KPF_ALL_BITS) { | ||
435 | if ((flags & opt_mask[i]) == 0) | ||
436 | return 0; | ||
437 | } else { | ||
438 | if ((flags & opt_mask[i]) != opt_bits[i]) | ||
439 | return 0; | ||
440 | } | ||
441 | } | ||
442 | |||
443 | return 1; | ||
444 | } | ||
445 | |||
446 | static uint64_t expand_overloaded_flags(uint64_t flags) | ||
447 | { | ||
448 | /* SLOB/SLUB overload several page flags */ | ||
449 | if (flags & BIT(SLAB)) { | ||
450 | if (flags & BIT(PRIVATE)) | ||
451 | flags ^= BIT(PRIVATE) | BIT(SLOB_FREE); | ||
452 | if (flags & BIT(ACTIVE)) | ||
453 | flags ^= BIT(ACTIVE) | BIT(SLUB_FROZEN); | ||
454 | if (flags & BIT(ERROR)) | ||
455 | flags ^= BIT(ERROR) | BIT(SLUB_DEBUG); | ||
456 | } | ||
457 | |||
458 | /* PG_reclaim is overloaded as PG_readahead in the read path */ | ||
459 | if ((flags & (BIT(RECLAIM) | BIT(WRITEBACK))) == BIT(RECLAIM)) | ||
460 | flags ^= BIT(RECLAIM) | BIT(READAHEAD); | ||
461 | |||
462 | return flags; | ||
463 | } | ||
464 | |||
465 | static uint64_t well_known_flags(uint64_t flags) | ||
466 | { | ||
467 | /* hide flags intended only for kernel hacker */ | ||
468 | flags &= ~KPF_HACKERS_BITS; | ||
469 | |||
470 | /* hide non-hugeTLB compound pages */ | ||
471 | if ((flags & BITS_COMPOUND) && !(flags & BIT(HUGE))) | ||
472 | flags &= ~BITS_COMPOUND; | ||
473 | |||
474 | return flags; | ||
475 | } | ||
476 | |||
477 | static uint64_t kpageflags_flags(uint64_t flags) | ||
478 | { | ||
479 | flags = expand_overloaded_flags(flags); | ||
480 | |||
481 | if (!opt_raw) | ||
482 | flags = well_known_flags(flags); | ||
483 | |||
484 | return flags; | ||
485 | } | ||
486 | |||
487 | /* verify that a mountpoint is actually a debugfs instance */ | ||
488 | static int debugfs_valid_mountpoint(const char *debugfs) | ||
489 | { | ||
490 | struct statfs st_fs; | ||
491 | |||
492 | if (statfs(debugfs, &st_fs) < 0) | ||
493 | return -ENOENT; | ||
494 | else if (st_fs.f_type != (long) DEBUGFS_MAGIC) | ||
495 | return -ENOENT; | ||
496 | |||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | /* find the path to the mounted debugfs */ | ||
501 | static const char *debugfs_find_mountpoint(void) | ||
502 | { | ||
503 | const char **ptr; | ||
504 | char type[100]; | ||
505 | FILE *fp; | ||
506 | |||
507 | ptr = debugfs_known_mountpoints; | ||
508 | while (*ptr) { | ||
509 | if (debugfs_valid_mountpoint(*ptr) == 0) { | ||
510 | strcpy(hwpoison_debug_fs, *ptr); | ||
511 | return hwpoison_debug_fs; | ||
512 | } | ||
513 | ptr++; | ||
514 | } | ||
515 | |||
516 | /* give up and parse /proc/mounts */ | ||
517 | fp = fopen("/proc/mounts", "r"); | ||
518 | if (fp == NULL) | ||
519 | perror("Can't open /proc/mounts for read"); | ||
520 | |||
521 | while (fscanf(fp, "%*s %" | ||
522 | STR(MAX_PATH) | ||
523 | "s %99s %*s %*d %*d\n", | ||
524 | hwpoison_debug_fs, type) == 2) { | ||
525 | if (strcmp(type, "debugfs") == 0) | ||
526 | break; | ||
527 | } | ||
528 | fclose(fp); | ||
529 | |||
530 | if (strcmp(type, "debugfs") != 0) | ||
531 | return NULL; | ||
532 | |||
533 | return hwpoison_debug_fs; | ||
534 | } | ||
535 | |||
536 | /* mount the debugfs somewhere if it's not mounted */ | ||
537 | |||
538 | static void debugfs_mount(void) | ||
539 | { | ||
540 | const char **ptr; | ||
541 | |||
542 | /* see if it's already mounted */ | ||
543 | if (debugfs_find_mountpoint()) | ||
544 | return; | ||
545 | |||
546 | ptr = debugfs_known_mountpoints; | ||
547 | while (*ptr) { | ||
548 | if (mount(NULL, *ptr, "debugfs", 0, NULL) == 0) { | ||
549 | /* save the mountpoint */ | ||
550 | strcpy(hwpoison_debug_fs, *ptr); | ||
551 | break; | ||
552 | } | ||
553 | ptr++; | ||
554 | } | ||
555 | |||
556 | if (*ptr == NULL) { | ||
557 | perror("mount debugfs"); | ||
558 | exit(EXIT_FAILURE); | ||
559 | } | ||
560 | } | ||
561 | |||
562 | /* | ||
563 | * page actions | ||
564 | */ | ||
565 | |||
566 | static void prepare_hwpoison_fd(void) | ||
567 | { | ||
568 | char buf[MAX_PATH + 1]; | ||
569 | |||
570 | debugfs_mount(); | ||
571 | |||
572 | if (opt_hwpoison && !hwpoison_inject_fd) { | ||
573 | snprintf(buf, MAX_PATH, "%s/hwpoison/corrupt-pfn", | ||
574 | hwpoison_debug_fs); | ||
575 | hwpoison_inject_fd = checked_open(buf, O_WRONLY); | ||
576 | } | ||
577 | |||
578 | if (opt_unpoison && !hwpoison_forget_fd) { | ||
579 | snprintf(buf, MAX_PATH, "%s/hwpoison/unpoison-pfn", | ||
580 | hwpoison_debug_fs); | ||
581 | hwpoison_forget_fd = checked_open(buf, O_WRONLY); | ||
582 | } | ||
583 | } | ||
584 | |||
585 | static int hwpoison_page(unsigned long offset) | ||
586 | { | ||
587 | char buf[100]; | ||
588 | int len; | ||
589 | |||
590 | len = sprintf(buf, "0x%lx\n", offset); | ||
591 | len = write(hwpoison_inject_fd, buf, len); | ||
592 | if (len < 0) { | ||
593 | perror("hwpoison inject"); | ||
594 | return len; | ||
595 | } | ||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | static int unpoison_page(unsigned long offset) | ||
600 | { | ||
601 | char buf[100]; | ||
602 | int len; | ||
603 | |||
604 | len = sprintf(buf, "0x%lx\n", offset); | ||
605 | len = write(hwpoison_forget_fd, buf, len); | ||
606 | if (len < 0) { | ||
607 | perror("hwpoison forget"); | ||
608 | return len; | ||
609 | } | ||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | /* | ||
614 | * page frame walker | ||
615 | */ | ||
616 | |||
617 | static int hash_slot(uint64_t flags) | ||
618 | { | ||
619 | int k = HASH_KEY(flags); | ||
620 | int i; | ||
621 | |||
622 | /* Explicitly reserve slot 0 for flags 0: the following logic | ||
623 | * cannot distinguish an unoccupied slot from slot (flags==0). | ||
624 | */ | ||
625 | if (flags == 0) | ||
626 | return 0; | ||
627 | |||
628 | /* search through the remaining (HASH_SIZE-1) slots */ | ||
629 | for (i = 1; i < ARRAY_SIZE(page_flags); i++, k++) { | ||
630 | if (!k || k >= ARRAY_SIZE(page_flags)) | ||
631 | k = 1; | ||
632 | if (page_flags[k] == 0) { | ||
633 | page_flags[k] = flags; | ||
634 | return k; | ||
635 | } | ||
636 | if (page_flags[k] == flags) | ||
637 | return k; | ||
638 | } | ||
639 | |||
640 | fatal("hash table full: bump up HASH_SHIFT?\n"); | ||
641 | exit(EXIT_FAILURE); | ||
642 | } | ||
643 | |||
644 | static void add_page(unsigned long voffset, | ||
645 | unsigned long offset, uint64_t flags) | ||
646 | { | ||
647 | flags = kpageflags_flags(flags); | ||
648 | |||
649 | if (!bit_mask_ok(flags)) | ||
650 | return; | ||
651 | |||
652 | if (opt_hwpoison) | ||
653 | hwpoison_page(offset); | ||
654 | if (opt_unpoison) | ||
655 | unpoison_page(offset); | ||
656 | |||
657 | if (opt_list == 1) | ||
658 | show_page_range(voffset, offset, flags); | ||
659 | else if (opt_list == 2) | ||
660 | show_page(voffset, offset, flags); | ||
661 | |||
662 | nr_pages[hash_slot(flags)]++; | ||
663 | total_pages++; | ||
664 | } | ||
665 | |||
666 | #define KPAGEFLAGS_BATCH (64 << 10) /* 64k pages */ | ||
667 | static void walk_pfn(unsigned long voffset, | ||
668 | unsigned long index, | ||
669 | unsigned long count) | ||
670 | { | ||
671 | uint64_t buf[KPAGEFLAGS_BATCH]; | ||
672 | unsigned long batch; | ||
673 | long pages; | ||
674 | unsigned long i; | ||
675 | |||
676 | while (count) { | ||
677 | batch = min_t(unsigned long, count, KPAGEFLAGS_BATCH); | ||
678 | pages = kpageflags_read(buf, index, batch); | ||
679 | if (pages == 0) | ||
680 | break; | ||
681 | |||
682 | for (i = 0; i < pages; i++) | ||
683 | add_page(voffset + i, index + i, buf[i]); | ||
684 | |||
685 | index += pages; | ||
686 | count -= pages; | ||
687 | } | ||
688 | } | ||
689 | |||
690 | #define PAGEMAP_BATCH (64 << 10) | ||
691 | static void walk_vma(unsigned long index, unsigned long count) | ||
692 | { | ||
693 | uint64_t buf[PAGEMAP_BATCH]; | ||
694 | unsigned long batch; | ||
695 | unsigned long pages; | ||
696 | unsigned long pfn; | ||
697 | unsigned long i; | ||
698 | |||
699 | while (count) { | ||
700 | batch = min_t(unsigned long, count, PAGEMAP_BATCH); | ||
701 | pages = pagemap_read(buf, index, batch); | ||
702 | if (pages == 0) | ||
703 | break; | ||
704 | |||
705 | for (i = 0; i < pages; i++) { | ||
706 | pfn = pagemap_pfn(buf[i]); | ||
707 | if (pfn) | ||
708 | walk_pfn(index + i, pfn, 1); | ||
709 | } | ||
710 | |||
711 | index += pages; | ||
712 | count -= pages; | ||
713 | } | ||
714 | } | ||
715 | |||
716 | static void walk_task(unsigned long index, unsigned long count) | ||
717 | { | ||
718 | const unsigned long end = index + count; | ||
719 | unsigned long start; | ||
720 | int i = 0; | ||
721 | |||
722 | while (index < end) { | ||
723 | |||
724 | while (pg_end[i] <= index) | ||
725 | if (++i >= nr_vmas) | ||
726 | return; | ||
727 | if (pg_start[i] >= end) | ||
728 | return; | ||
729 | |||
730 | start = max_t(unsigned long, pg_start[i], index); | ||
731 | index = min_t(unsigned long, pg_end[i], end); | ||
732 | |||
733 | assert(start < index); | ||
734 | walk_vma(start, index - start); | ||
735 | } | ||
736 | } | ||
737 | |||
738 | static void add_addr_range(unsigned long offset, unsigned long size) | ||
739 | { | ||
740 | if (nr_addr_ranges >= MAX_ADDR_RANGES) | ||
741 | fatal("too many addr ranges\n"); | ||
742 | |||
743 | opt_offset[nr_addr_ranges] = offset; | ||
744 | opt_size[nr_addr_ranges] = min_t(unsigned long, size, ULONG_MAX-offset); | ||
745 | nr_addr_ranges++; | ||
746 | } | ||
747 | |||
748 | static void walk_addr_ranges(void) | ||
749 | { | ||
750 | int i; | ||
751 | |||
752 | kpageflags_fd = checked_open(PROC_KPAGEFLAGS, O_RDONLY); | ||
753 | |||
754 | if (!nr_addr_ranges) | ||
755 | add_addr_range(0, ULONG_MAX); | ||
756 | |||
757 | for (i = 0; i < nr_addr_ranges; i++) | ||
758 | if (!opt_pid) | ||
759 | walk_pfn(0, opt_offset[i], opt_size[i]); | ||
760 | else | ||
761 | walk_task(opt_offset[i], opt_size[i]); | ||
762 | |||
763 | close(kpageflags_fd); | ||
764 | } | ||
765 | |||
766 | |||
767 | /* | ||
768 | * user interface | ||
769 | */ | ||
770 | |||
771 | static const char *page_flag_type(uint64_t flag) | ||
772 | { | ||
773 | if (flag & KPF_HACKERS_BITS) | ||
774 | return "(r)"; | ||
775 | if (flag & KPF_OVERLOADED_BITS) | ||
776 | return "(o)"; | ||
777 | return " "; | ||
778 | } | ||
779 | |||
780 | static void usage(void) | ||
781 | { | ||
782 | int i, j; | ||
783 | |||
784 | printf( | ||
785 | "page-types [options]\n" | ||
786 | " -r|--raw Raw mode, for kernel developers\n" | ||
787 | " -d|--describe flags Describe flags\n" | ||
788 | " -a|--addr addr-spec Walk a range of pages\n" | ||
789 | " -b|--bits bits-spec Walk pages with specified bits\n" | ||
790 | " -p|--pid pid Walk process address space\n" | ||
791 | #if 0 /* planned features */ | ||
792 | " -f|--file filename Walk file address space\n" | ||
793 | #endif | ||
794 | " -l|--list Show page details in ranges\n" | ||
795 | " -L|--list-each Show page details one by one\n" | ||
796 | " -N|--no-summary Don't show summary info\n" | ||
797 | " -X|--hwpoison hwpoison pages\n" | ||
798 | " -x|--unpoison unpoison pages\n" | ||
799 | " -h|--help Show this usage message\n" | ||
800 | "flags:\n" | ||
801 | " 0x10 bitfield format, e.g.\n" | ||
802 | " anon bit-name, e.g.\n" | ||
803 | " 0x10,anon comma-separated list, e.g.\n" | ||
804 | "addr-spec:\n" | ||
805 | " N one page at offset N (unit: pages)\n" | ||
806 | " N+M pages range from N to N+M-1\n" | ||
807 | " N,M pages range from N to M-1\n" | ||
808 | " N, pages range from N to end\n" | ||
809 | " ,M pages range from 0 to M-1\n" | ||
810 | "bits-spec:\n" | ||
811 | " bit1,bit2 (flags & (bit1|bit2)) != 0\n" | ||
812 | " bit1,bit2=bit1 (flags & (bit1|bit2)) == bit1\n" | ||
813 | " bit1,~bit2 (flags & (bit1|bit2)) == bit1\n" | ||
814 | " =bit1,bit2 flags == (bit1|bit2)\n" | ||
815 | "bit-names:\n" | ||
816 | ); | ||
817 | |||
818 | for (i = 0, j = 0; i < ARRAY_SIZE(page_flag_names); i++) { | ||
819 | if (!page_flag_names[i]) | ||
820 | continue; | ||
821 | printf("%16s%s", page_flag_names[i] + 2, | ||
822 | page_flag_type(1ULL << i)); | ||
823 | if (++j > 3) { | ||
824 | j = 0; | ||
825 | putchar('\n'); | ||
826 | } | ||
827 | } | ||
828 | printf("\n " | ||
829 | "(r) raw mode bits (o) overloaded bits\n"); | ||
830 | } | ||
831 | |||
832 | static unsigned long long parse_number(const char *str) | ||
833 | { | ||
834 | unsigned long long n; | ||
835 | |||
836 | n = strtoll(str, NULL, 0); | ||
837 | |||
838 | if (n == 0 && str[0] != '0') | ||
839 | fatal("invalid name or number: %s\n", str); | ||
840 | |||
841 | return n; | ||
842 | } | ||
843 | |||
844 | static void parse_pid(const char *str) | ||
845 | { | ||
846 | FILE *file; | ||
847 | char buf[5000]; | ||
848 | |||
849 | opt_pid = parse_number(str); | ||
850 | |||
851 | sprintf(buf, "/proc/%d/pagemap", opt_pid); | ||
852 | pagemap_fd = checked_open(buf, O_RDONLY); | ||
853 | |||
854 | sprintf(buf, "/proc/%d/maps", opt_pid); | ||
855 | file = fopen(buf, "r"); | ||
856 | if (!file) { | ||
857 | perror(buf); | ||
858 | exit(EXIT_FAILURE); | ||
859 | } | ||
860 | |||
861 | while (fgets(buf, sizeof(buf), file) != NULL) { | ||
862 | unsigned long vm_start; | ||
863 | unsigned long vm_end; | ||
864 | unsigned long long pgoff; | ||
865 | int major, minor; | ||
866 | char r, w, x, s; | ||
867 | unsigned long ino; | ||
868 | int n; | ||
869 | |||
870 | n = sscanf(buf, "%lx-%lx %c%c%c%c %llx %x:%x %lu", | ||
871 | &vm_start, | ||
872 | &vm_end, | ||
873 | &r, &w, &x, &s, | ||
874 | &pgoff, | ||
875 | &major, &minor, | ||
876 | &ino); | ||
877 | if (n < 10) { | ||
878 | fprintf(stderr, "unexpected line: %s\n", buf); | ||
879 | continue; | ||
880 | } | ||
881 | pg_start[nr_vmas] = vm_start / page_size; | ||
882 | pg_end[nr_vmas] = vm_end / page_size; | ||
883 | if (++nr_vmas >= MAX_VMAS) { | ||
884 | fprintf(stderr, "too many VMAs\n"); | ||
885 | break; | ||
886 | } | ||
887 | } | ||
888 | fclose(file); | ||
889 | } | ||
890 | |||
891 | static void parse_file(const char *name) | ||
892 | { | ||
893 | } | ||
894 | |||
895 | static void parse_addr_range(const char *optarg) | ||
896 | { | ||
897 | unsigned long offset; | ||
898 | unsigned long size; | ||
899 | char *p; | ||
900 | |||
901 | p = strchr(optarg, ','); | ||
902 | if (!p) | ||
903 | p = strchr(optarg, '+'); | ||
904 | |||
905 | if (p == optarg) { | ||
906 | offset = 0; | ||
907 | size = parse_number(p + 1); | ||
908 | } else if (p) { | ||
909 | offset = parse_number(optarg); | ||
910 | if (p[1] == '\0') | ||
911 | size = ULONG_MAX; | ||
912 | else { | ||
913 | size = parse_number(p + 1); | ||
914 | if (*p == ',') { | ||
915 | if (size < offset) | ||
916 | fatal("invalid range: %lu,%lu\n", | ||
917 | offset, size); | ||
918 | size -= offset; | ||
919 | } | ||
920 | } | ||
921 | } else { | ||
922 | offset = parse_number(optarg); | ||
923 | size = 1; | ||
924 | } | ||
925 | |||
926 | add_addr_range(offset, size); | ||
927 | } | ||
928 | |||
929 | static void add_bits_filter(uint64_t mask, uint64_t bits) | ||
930 | { | ||
931 | if (nr_bit_filters >= MAX_BIT_FILTERS) | ||
932 | fatal("too much bit filters\n"); | ||
933 | |||
934 | opt_mask[nr_bit_filters] = mask; | ||
935 | opt_bits[nr_bit_filters] = bits; | ||
936 | nr_bit_filters++; | ||
937 | } | ||
938 | |||
939 | static uint64_t parse_flag_name(const char *str, int len) | ||
940 | { | ||
941 | int i; | ||
942 | |||
943 | if (!*str || !len) | ||
944 | return 0; | ||
945 | |||
946 | if (len <= 8 && !strncmp(str, "compound", len)) | ||
947 | return BITS_COMPOUND; | ||
948 | |||
949 | for (i = 0; i < ARRAY_SIZE(page_flag_names); i++) { | ||
950 | if (!page_flag_names[i]) | ||
951 | continue; | ||
952 | if (!strncmp(str, page_flag_names[i] + 2, len)) | ||
953 | return 1ULL << i; | ||
954 | } | ||
955 | |||
956 | return parse_number(str); | ||
957 | } | ||
958 | |||
959 | static uint64_t parse_flag_names(const char *str, int all) | ||
960 | { | ||
961 | const char *p = str; | ||
962 | uint64_t flags = 0; | ||
963 | |||
964 | while (1) { | ||
965 | if (*p == ',' || *p == '=' || *p == '\0') { | ||
966 | if ((*str != '~') || (*str == '~' && all && *++str)) | ||
967 | flags |= parse_flag_name(str, p - str); | ||
968 | if (*p != ',') | ||
969 | break; | ||
970 | str = p + 1; | ||
971 | } | ||
972 | p++; | ||
973 | } | ||
974 | |||
975 | return flags; | ||
976 | } | ||
977 | |||
978 | static void parse_bits_mask(const char *optarg) | ||
979 | { | ||
980 | uint64_t mask; | ||
981 | uint64_t bits; | ||
982 | const char *p; | ||
983 | |||
984 | p = strchr(optarg, '='); | ||
985 | if (p == optarg) { | ||
986 | mask = KPF_ALL_BITS; | ||
987 | bits = parse_flag_names(p + 1, 0); | ||
988 | } else if (p) { | ||
989 | mask = parse_flag_names(optarg, 0); | ||
990 | bits = parse_flag_names(p + 1, 0); | ||
991 | } else if (strchr(optarg, '~')) { | ||
992 | mask = parse_flag_names(optarg, 1); | ||
993 | bits = parse_flag_names(optarg, 0); | ||
994 | } else { | ||
995 | mask = parse_flag_names(optarg, 0); | ||
996 | bits = KPF_ALL_BITS; | ||
997 | } | ||
998 | |||
999 | add_bits_filter(mask, bits); | ||
1000 | } | ||
1001 | |||
1002 | static void describe_flags(const char *optarg) | ||
1003 | { | ||
1004 | uint64_t flags = parse_flag_names(optarg, 0); | ||
1005 | |||
1006 | printf("0x%016llx\t%s\t%s\n", | ||
1007 | (unsigned long long)flags, | ||
1008 | page_flag_name(flags), | ||
1009 | page_flag_longname(flags)); | ||
1010 | } | ||
1011 | |||
1012 | static const struct option opts[] = { | ||
1013 | { "raw" , 0, NULL, 'r' }, | ||
1014 | { "pid" , 1, NULL, 'p' }, | ||
1015 | { "file" , 1, NULL, 'f' }, | ||
1016 | { "addr" , 1, NULL, 'a' }, | ||
1017 | { "bits" , 1, NULL, 'b' }, | ||
1018 | { "describe" , 1, NULL, 'd' }, | ||
1019 | { "list" , 0, NULL, 'l' }, | ||
1020 | { "list-each" , 0, NULL, 'L' }, | ||
1021 | { "no-summary", 0, NULL, 'N' }, | ||
1022 | { "hwpoison" , 0, NULL, 'X' }, | ||
1023 | { "unpoison" , 0, NULL, 'x' }, | ||
1024 | { "help" , 0, NULL, 'h' }, | ||
1025 | { NULL , 0, NULL, 0 } | ||
1026 | }; | ||
1027 | |||
1028 | int main(int argc, char *argv[]) | ||
1029 | { | ||
1030 | int c; | ||
1031 | |||
1032 | page_size = getpagesize(); | ||
1033 | |||
1034 | while ((c = getopt_long(argc, argv, | ||
1035 | "rp:f:a:b:d:lLNXxh", opts, NULL)) != -1) { | ||
1036 | switch (c) { | ||
1037 | case 'r': | ||
1038 | opt_raw = 1; | ||
1039 | break; | ||
1040 | case 'p': | ||
1041 | parse_pid(optarg); | ||
1042 | break; | ||
1043 | case 'f': | ||
1044 | parse_file(optarg); | ||
1045 | break; | ||
1046 | case 'a': | ||
1047 | parse_addr_range(optarg); | ||
1048 | break; | ||
1049 | case 'b': | ||
1050 | parse_bits_mask(optarg); | ||
1051 | break; | ||
1052 | case 'd': | ||
1053 | describe_flags(optarg); | ||
1054 | exit(0); | ||
1055 | case 'l': | ||
1056 | opt_list = 1; | ||
1057 | break; | ||
1058 | case 'L': | ||
1059 | opt_list = 2; | ||
1060 | break; | ||
1061 | case 'N': | ||
1062 | opt_no_summary = 1; | ||
1063 | break; | ||
1064 | case 'X': | ||
1065 | opt_hwpoison = 1; | ||
1066 | prepare_hwpoison_fd(); | ||
1067 | break; | ||
1068 | case 'x': | ||
1069 | opt_unpoison = 1; | ||
1070 | prepare_hwpoison_fd(); | ||
1071 | break; | ||
1072 | case 'h': | ||
1073 | usage(); | ||
1074 | exit(0); | ||
1075 | default: | ||
1076 | usage(); | ||
1077 | exit(1); | ||
1078 | } | ||
1079 | } | ||
1080 | |||
1081 | if (opt_list && opt_pid) | ||
1082 | printf("voffset\t"); | ||
1083 | if (opt_list == 1) | ||
1084 | printf("offset\tlen\tflags\n"); | ||
1085 | if (opt_list == 2) | ||
1086 | printf("offset\tflags\n"); | ||
1087 | |||
1088 | walk_addr_ranges(); | ||
1089 | |||
1090 | if (opt_list == 1) | ||
1091 | show_page_range(0, 0, 0); /* drain the buffer */ | ||
1092 | |||
1093 | if (opt_no_summary) | ||
1094 | return 0; | ||
1095 | |||
1096 | if (opt_list) | ||
1097 | printf("\n\n"); | ||
1098 | |||
1099 | show_summary(); | ||
1100 | |||
1101 | return 0; | ||
1102 | } | ||
diff --git a/tools/slub/slabinfo.c b/tools/vm/slabinfo.c index 164cbcf61106..164cbcf61106 100644 --- a/tools/slub/slabinfo.c +++ b/tools/vm/slabinfo.c | |||