diff options
Diffstat (limited to 'tools/perf/builtin-record.c')
-rw-r--r-- | tools/perf/builtin-record.c | 344 |
1 files changed, 226 insertions, 118 deletions
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index d7ebbd75754..a5a050af8e7 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -14,6 +14,11 @@ | |||
14 | #include "util/parse-events.h" | 14 | #include "util/parse-events.h" |
15 | #include "util/string.h" | 15 | #include "util/string.h" |
16 | 16 | ||
17 | #include "util/header.h" | ||
18 | #include "util/event.h" | ||
19 | #include "util/debug.h" | ||
20 | #include "util/trace-event.h" | ||
21 | |||
17 | #include <unistd.h> | 22 | #include <unistd.h> |
18 | #include <sched.h> | 23 | #include <sched.h> |
19 | 24 | ||
@@ -32,13 +37,19 @@ static int output; | |||
32 | static const char *output_name = "perf.data"; | 37 | static const char *output_name = "perf.data"; |
33 | static int group = 0; | 38 | static int group = 0; |
34 | static unsigned int realtime_prio = 0; | 39 | static unsigned int realtime_prio = 0; |
40 | static int raw_samples = 0; | ||
35 | static int system_wide = 0; | 41 | static int system_wide = 0; |
42 | static int profile_cpu = -1; | ||
36 | static pid_t target_pid = -1; | 43 | static pid_t target_pid = -1; |
37 | static int inherit = 1; | 44 | static int inherit = 1; |
38 | static int force = 0; | 45 | static int force = 0; |
39 | static int append_file = 0; | 46 | static int append_file = 0; |
40 | static int call_graph = 0; | 47 | static int call_graph = 0; |
41 | static int verbose = 0; | 48 | static int inherit_stat = 0; |
49 | static int no_samples = 0; | ||
50 | static int sample_address = 0; | ||
51 | static int multiplex = 0; | ||
52 | static int multiplex_fd = -1; | ||
42 | 53 | ||
43 | static long samples; | 54 | static long samples; |
44 | static struct timeval last_read; | 55 | static struct timeval last_read; |
@@ -52,25 +63,8 @@ static int nr_poll; | |||
52 | static int nr_cpu; | 63 | static int nr_cpu; |
53 | 64 | ||
54 | static int file_new = 1; | 65 | static int file_new = 1; |
55 | static struct perf_file_header file_header; | ||
56 | |||
57 | struct mmap_event { | ||
58 | struct perf_event_header header; | ||
59 | u32 pid; | ||
60 | u32 tid; | ||
61 | u64 start; | ||
62 | u64 len; | ||
63 | u64 pgoff; | ||
64 | char filename[PATH_MAX]; | ||
65 | }; | ||
66 | |||
67 | struct comm_event { | ||
68 | struct perf_event_header header; | ||
69 | u32 pid; | ||
70 | u32 tid; | ||
71 | char comm[16]; | ||
72 | }; | ||
73 | 66 | ||
67 | struct perf_header *header; | ||
74 | 68 | ||
75 | struct mmap_data { | 69 | struct mmap_data { |
76 | int counter; | 70 | int counter; |
@@ -83,7 +77,7 @@ static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; | |||
83 | 77 | ||
84 | static unsigned long mmap_read_head(struct mmap_data *md) | 78 | static unsigned long mmap_read_head(struct mmap_data *md) |
85 | { | 79 | { |
86 | struct perf_counter_mmap_page *pc = md->base; | 80 | struct perf_event_mmap_page *pc = md->base; |
87 | long head; | 81 | long head; |
88 | 82 | ||
89 | head = pc->data_head; | 83 | head = pc->data_head; |
@@ -94,7 +88,7 @@ static unsigned long mmap_read_head(struct mmap_data *md) | |||
94 | 88 | ||
95 | static void mmap_write_tail(struct mmap_data *md, unsigned long tail) | 89 | static void mmap_write_tail(struct mmap_data *md, unsigned long tail) |
96 | { | 90 | { |
97 | struct perf_counter_mmap_page *pc = md->base; | 91 | struct perf_event_mmap_page *pc = md->base; |
98 | 92 | ||
99 | /* | 93 | /* |
100 | * ensure all reads are done before we write the tail out. | 94 | * ensure all reads are done before we write the tail out. |
@@ -197,47 +191,49 @@ static void sig_atexit(void) | |||
197 | kill(getpid(), signr); | 191 | kill(getpid(), signr); |
198 | } | 192 | } |
199 | 193 | ||
200 | static void pid_synthesize_comm_event(pid_t pid, int full) | 194 | static pid_t pid_synthesize_comm_event(pid_t pid, int full) |
201 | { | 195 | { |
202 | struct comm_event comm_ev; | 196 | struct comm_event comm_ev; |
203 | char filename[PATH_MAX]; | 197 | char filename[PATH_MAX]; |
204 | char bf[BUFSIZ]; | 198 | char bf[BUFSIZ]; |
205 | int fd; | 199 | FILE *fp; |
206 | size_t size; | 200 | size_t size = 0; |
207 | char *field, *sep; | ||
208 | DIR *tasks; | 201 | DIR *tasks; |
209 | struct dirent dirent, *next; | 202 | struct dirent dirent, *next; |
203 | pid_t tgid = 0; | ||
210 | 204 | ||
211 | snprintf(filename, sizeof(filename), "/proc/%d/stat", pid); | 205 | snprintf(filename, sizeof(filename), "/proc/%d/status", pid); |
212 | 206 | ||
213 | fd = open(filename, O_RDONLY); | 207 | fp = fopen(filename, "r"); |
214 | if (fd < 0) { | 208 | if (fp == NULL) { |
215 | /* | 209 | /* |
216 | * We raced with a task exiting - just return: | 210 | * We raced with a task exiting - just return: |
217 | */ | 211 | */ |
218 | if (verbose) | 212 | if (verbose) |
219 | fprintf(stderr, "couldn't open %s\n", filename); | 213 | fprintf(stderr, "couldn't open %s\n", filename); |
220 | return; | 214 | return 0; |
221 | } | 215 | } |
222 | if (read(fd, bf, sizeof(bf)) < 0) { | ||
223 | fprintf(stderr, "couldn't read %s\n", filename); | ||
224 | exit(EXIT_FAILURE); | ||
225 | } | ||
226 | close(fd); | ||
227 | 216 | ||
228 | /* 9027 (cat) R 6747 9027 6747 34816 9027 ... */ | ||
229 | memset(&comm_ev, 0, sizeof(comm_ev)); | 217 | memset(&comm_ev, 0, sizeof(comm_ev)); |
230 | field = strchr(bf, '('); | 218 | while (!comm_ev.comm[0] || !comm_ev.pid) { |
231 | if (field == NULL) | 219 | if (fgets(bf, sizeof(bf), fp) == NULL) |
232 | goto out_failure; | 220 | goto out_failure; |
233 | sep = strchr(++field, ')'); | 221 | |
234 | if (sep == NULL) | 222 | if (memcmp(bf, "Name:", 5) == 0) { |
235 | goto out_failure; | 223 | char *name = bf + 5; |
236 | size = sep - field; | 224 | while (*name && isspace(*name)) |
237 | memcpy(comm_ev.comm, field, size++); | 225 | ++name; |
238 | 226 | size = strlen(name) - 1; | |
239 | comm_ev.pid = pid; | 227 | memcpy(comm_ev.comm, name, size++); |
240 | comm_ev.header.type = PERF_EVENT_COMM; | 228 | } else if (memcmp(bf, "Tgid:", 5) == 0) { |
229 | char *tgids = bf + 5; | ||
230 | while (*tgids && isspace(*tgids)) | ||
231 | ++tgids; | ||
232 | tgid = comm_ev.pid = atoi(tgids); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | comm_ev.header.type = PERF_RECORD_COMM; | ||
241 | size = ALIGN(size, sizeof(u64)); | 237 | size = ALIGN(size, sizeof(u64)); |
242 | comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); | 238 | comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); |
243 | 239 | ||
@@ -245,7 +241,7 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
245 | comm_ev.tid = pid; | 241 | comm_ev.tid = pid; |
246 | 242 | ||
247 | write_output(&comm_ev, comm_ev.header.size); | 243 | write_output(&comm_ev, comm_ev.header.size); |
248 | return; | 244 | goto out_fclose; |
249 | } | 245 | } |
250 | 246 | ||
251 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); | 247 | snprintf(filename, sizeof(filename), "/proc/%d/task", pid); |
@@ -262,7 +258,10 @@ static void pid_synthesize_comm_event(pid_t pid, int full) | |||
262 | write_output(&comm_ev, comm_ev.header.size); | 258 | write_output(&comm_ev, comm_ev.header.size); |
263 | } | 259 | } |
264 | closedir(tasks); | 260 | closedir(tasks); |
265 | return; | 261 | |
262 | out_fclose: | ||
263 | fclose(fp); | ||
264 | return tgid; | ||
266 | 265 | ||
267 | out_failure: | 266 | out_failure: |
268 | fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", | 267 | fprintf(stderr, "couldn't get COMM and pgid, malformed %s\n", |
@@ -270,7 +269,7 @@ out_failure: | |||
270 | exit(EXIT_FAILURE); | 269 | exit(EXIT_FAILURE); |
271 | } | 270 | } |
272 | 271 | ||
273 | static void pid_synthesize_mmap_samples(pid_t pid) | 272 | static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid) |
274 | { | 273 | { |
275 | char filename[PATH_MAX]; | 274 | char filename[PATH_MAX]; |
276 | FILE *fp; | 275 | FILE *fp; |
@@ -289,7 +288,7 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
289 | while (1) { | 288 | while (1) { |
290 | char bf[BUFSIZ], *pbf = bf; | 289 | char bf[BUFSIZ], *pbf = bf; |
291 | struct mmap_event mmap_ev = { | 290 | struct mmap_event mmap_ev = { |
292 | .header.type = PERF_EVENT_MMAP, | 291 | .header = { .type = PERF_RECORD_MMAP }, |
293 | }; | 292 | }; |
294 | int n; | 293 | int n; |
295 | size_t size; | 294 | size_t size; |
@@ -306,12 +305,15 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
306 | continue; | 305 | continue; |
307 | pbf += n + 3; | 306 | pbf += n + 3; |
308 | if (*pbf == 'x') { /* vm_exec */ | 307 | if (*pbf == 'x') { /* vm_exec */ |
309 | char *execname = strrchr(bf, ' '); | 308 | char *execname = strchr(bf, '/'); |
309 | |||
310 | /* Catch VDSO */ | ||
311 | if (execname == NULL) | ||
312 | execname = strstr(bf, "[vdso]"); | ||
310 | 313 | ||
311 | if (execname == NULL || execname[1] != '/') | 314 | if (execname == NULL) |
312 | continue; | 315 | continue; |
313 | 316 | ||
314 | execname += 1; | ||
315 | size = strlen(execname); | 317 | size = strlen(execname); |
316 | execname[size - 1] = '\0'; /* Remove \n */ | 318 | execname[size - 1] = '\0'; /* Remove \n */ |
317 | memcpy(mmap_ev.filename, execname, size); | 319 | memcpy(mmap_ev.filename, execname, size); |
@@ -319,7 +321,7 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
319 | mmap_ev.len -= mmap_ev.start; | 321 | mmap_ev.len -= mmap_ev.start; |
320 | mmap_ev.header.size = (sizeof(mmap_ev) - | 322 | mmap_ev.header.size = (sizeof(mmap_ev) - |
321 | (sizeof(mmap_ev.filename) - size)); | 323 | (sizeof(mmap_ev.filename) - size)); |
322 | mmap_ev.pid = pid; | 324 | mmap_ev.pid = tgid; |
323 | mmap_ev.tid = pid; | 325 | mmap_ev.tid = pid; |
324 | 326 | ||
325 | write_output(&mmap_ev, mmap_ev.header.size); | 327 | write_output(&mmap_ev, mmap_ev.header.size); |
@@ -329,7 +331,7 @@ static void pid_synthesize_mmap_samples(pid_t pid) | |||
329 | fclose(fp); | 331 | fclose(fp); |
330 | } | 332 | } |
331 | 333 | ||
332 | static void synthesize_samples(void) | 334 | static void synthesize_all(void) |
333 | { | 335 | { |
334 | DIR *proc; | 336 | DIR *proc; |
335 | struct dirent dirent, *next; | 337 | struct dirent dirent, *next; |
@@ -338,14 +340,14 @@ static void synthesize_samples(void) | |||
338 | 340 | ||
339 | while (!readdir_r(proc, &dirent, &next) && next) { | 341 | while (!readdir_r(proc, &dirent, &next) && next) { |
340 | char *end; | 342 | char *end; |
341 | pid_t pid; | 343 | pid_t pid, tgid; |
342 | 344 | ||
343 | pid = strtol(dirent.d_name, &end, 10); | 345 | pid = strtol(dirent.d_name, &end, 10); |
344 | if (*end) /* only interested in proper numerical dirents */ | 346 | if (*end) /* only interested in proper numerical dirents */ |
345 | continue; | 347 | continue; |
346 | 348 | ||
347 | pid_synthesize_comm_event(pid, 1); | 349 | tgid = pid_synthesize_comm_event(pid, 1); |
348 | pid_synthesize_mmap_samples(pid); | 350 | pid_synthesize_mmap_samples(pid, tgid); |
349 | } | 351 | } |
350 | 352 | ||
351 | closedir(proc); | 353 | closedir(proc); |
@@ -353,12 +355,37 @@ static void synthesize_samples(void) | |||
353 | 355 | ||
354 | static int group_fd; | 356 | static int group_fd; |
355 | 357 | ||
356 | static void create_counter(int counter, int cpu, pid_t pid) | 358 | static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) |
357 | { | 359 | { |
358 | struct perf_counter_attr *attr = attrs + counter; | 360 | struct perf_header_attr *h_attr; |
359 | int track = 1; | 361 | |
362 | if (nr < header->attrs) { | ||
363 | h_attr = header->attr[nr]; | ||
364 | } else { | ||
365 | h_attr = perf_header_attr__new(a); | ||
366 | perf_header__add_attr(header, h_attr); | ||
367 | } | ||
360 | 368 | ||
361 | attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID; | 369 | return h_attr; |
370 | } | ||
371 | |||
372 | static void create_counter(int counter, int cpu, pid_t pid) | ||
373 | { | ||
374 | struct perf_event_attr *attr = attrs + counter; | ||
375 | struct perf_header_attr *h_attr; | ||
376 | int track = !counter; /* only the first counter needs these */ | ||
377 | struct { | ||
378 | u64 count; | ||
379 | u64 time_enabled; | ||
380 | u64 time_running; | ||
381 | u64 id; | ||
382 | } read_data; | ||
383 | |||
384 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | | ||
385 | PERF_FORMAT_TOTAL_TIME_RUNNING | | ||
386 | PERF_FORMAT_ID; | ||
387 | |||
388 | attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID; | ||
362 | 389 | ||
363 | if (freq) { | 390 | if (freq) { |
364 | attr->sample_type |= PERF_SAMPLE_PERIOD; | 391 | attr->sample_type |= PERF_SAMPLE_PERIOD; |
@@ -366,16 +393,22 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
366 | attr->sample_freq = freq; | 393 | attr->sample_freq = freq; |
367 | } | 394 | } |
368 | 395 | ||
396 | if (no_samples) | ||
397 | attr->sample_freq = 0; | ||
398 | |||
399 | if (inherit_stat) | ||
400 | attr->inherit_stat = 1; | ||
401 | |||
402 | if (sample_address) | ||
403 | attr->sample_type |= PERF_SAMPLE_ADDR; | ||
404 | |||
369 | if (call_graph) | 405 | if (call_graph) |
370 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; | 406 | attr->sample_type |= PERF_SAMPLE_CALLCHAIN; |
371 | 407 | ||
372 | if (file_new) { | 408 | if (raw_samples) { |
373 | file_header.sample_type = attr->sample_type; | 409 | attr->sample_type |= PERF_SAMPLE_TIME; |
374 | } else { | 410 | attr->sample_type |= PERF_SAMPLE_RAW; |
375 | if (file_header.sample_type != attr->sample_type) { | 411 | attr->sample_type |= PERF_SAMPLE_CPU; |
376 | fprintf(stderr, "incompatible append\n"); | ||
377 | exit(-1); | ||
378 | } | ||
379 | } | 412 | } |
380 | 413 | ||
381 | attr->mmap = track; | 414 | attr->mmap = track; |
@@ -383,16 +416,16 @@ static void create_counter(int counter, int cpu, pid_t pid) | |||
383 | attr->inherit = (cpu < 0) && inherit; | 416 | attr->inherit = (cpu < 0) && inherit; |
384 | attr->disabled = 1; | 417 | attr->disabled = 1; |
385 | 418 | ||
386 | track = 0; /* only the first counter needs these */ | ||
387 | |||
388 | try_again: | 419 | try_again: |
389 | fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0); | 420 | fd[nr_cpu][counter] = sys_perf_event_open(attr, pid, cpu, group_fd, 0); |
390 | 421 | ||
391 | if (fd[nr_cpu][counter] < 0) { | 422 | if (fd[nr_cpu][counter] < 0) { |
392 | int err = errno; | 423 | int err = errno; |
393 | 424 | ||
394 | if (err == EPERM) | 425 | if (err == EPERM) |
395 | die("Permission error - are you root?\n"); | 426 | die("Permission error - are you root?\n"); |
427 | else if (err == ENODEV && profile_cpu != -1) | ||
428 | die("No such device - did you specify an out-of-range profile CPU?\n"); | ||
396 | 429 | ||
397 | /* | 430 | /* |
398 | * If it's cycles then fall back to hrtimer | 431 | * If it's cycles then fall back to hrtimer |
@@ -411,10 +444,26 @@ try_again: | |||
411 | printf("\n"); | 444 | printf("\n"); |
412 | error("perfcounter syscall returned with %d (%s)\n", | 445 | error("perfcounter syscall returned with %d (%s)\n", |
413 | fd[nr_cpu][counter], strerror(err)); | 446 | fd[nr_cpu][counter], strerror(err)); |
414 | die("No CONFIG_PERF_COUNTERS=y kernel support configured?\n"); | 447 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); |
448 | exit(-1); | ||
449 | } | ||
450 | |||
451 | h_attr = get_header_attr(attr, counter); | ||
452 | |||
453 | if (!file_new) { | ||
454 | if (memcmp(&h_attr->attr, attr, sizeof(*attr))) { | ||
455 | fprintf(stderr, "incompatible append\n"); | ||
456 | exit(-1); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | if (read(fd[nr_cpu][counter], &read_data, sizeof(read_data)) == -1) { | ||
461 | perror("Unable to read perf file descriptor\n"); | ||
415 | exit(-1); | 462 | exit(-1); |
416 | } | 463 | } |
417 | 464 | ||
465 | perf_header_attr__add_id(h_attr, read_data.id); | ||
466 | |||
418 | assert(fd[nr_cpu][counter] >= 0); | 467 | assert(fd[nr_cpu][counter] >= 0); |
419 | fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); | 468 | fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK); |
420 | 469 | ||
@@ -423,33 +472,37 @@ try_again: | |||
423 | */ | 472 | */ |
424 | if (group && group_fd == -1) | 473 | if (group && group_fd == -1) |
425 | group_fd = fd[nr_cpu][counter]; | 474 | group_fd = fd[nr_cpu][counter]; |
475 | if (multiplex && multiplex_fd == -1) | ||
476 | multiplex_fd = fd[nr_cpu][counter]; | ||
426 | 477 | ||
427 | event_array[nr_poll].fd = fd[nr_cpu][counter]; | 478 | if (multiplex && fd[nr_cpu][counter] != multiplex_fd) { |
428 | event_array[nr_poll].events = POLLIN; | 479 | int ret; |
429 | nr_poll++; | 480 | |
430 | 481 | ret = ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); | |
431 | mmap_array[nr_cpu][counter].counter = counter; | 482 | assert(ret != -1); |
432 | mmap_array[nr_cpu][counter].prev = 0; | 483 | } else { |
433 | mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1; | 484 | event_array[nr_poll].fd = fd[nr_cpu][counter]; |
434 | mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size, | 485 | event_array[nr_poll].events = POLLIN; |
435 | PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0); | 486 | nr_poll++; |
436 | if (mmap_array[nr_cpu][counter].base == MAP_FAILED) { | 487 | |
437 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | 488 | mmap_array[nr_cpu][counter].counter = counter; |
438 | exit(-1); | 489 | mmap_array[nr_cpu][counter].prev = 0; |
490 | mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1; | ||
491 | mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size, | ||
492 | PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0); | ||
493 | if (mmap_array[nr_cpu][counter].base == MAP_FAILED) { | ||
494 | error("failed to mmap with %d (%s)\n", errno, strerror(errno)); | ||
495 | exit(-1); | ||
496 | } | ||
439 | } | 497 | } |
440 | 498 | ||
441 | ioctl(fd[nr_cpu][counter], PERF_COUNTER_IOC_ENABLE); | 499 | ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_ENABLE); |
442 | } | 500 | } |
443 | 501 | ||
444 | static void open_counters(int cpu, pid_t pid) | 502 | static void open_counters(int cpu, pid_t pid) |
445 | { | 503 | { |
446 | int counter; | 504 | int counter; |
447 | 505 | ||
448 | if (pid > 0) { | ||
449 | pid_synthesize_comm_event(pid, 0); | ||
450 | pid_synthesize_mmap_samples(pid); | ||
451 | } | ||
452 | |||
453 | group_fd = -1; | 506 | group_fd = -1; |
454 | for (counter = 0; counter < nr_counters; counter++) | 507 | for (counter = 0; counter < nr_counters; counter++) |
455 | create_counter(counter, cpu, pid); | 508 | create_counter(counter, cpu, pid); |
@@ -459,19 +512,19 @@ static void open_counters(int cpu, pid_t pid) | |||
459 | 512 | ||
460 | static void atexit_header(void) | 513 | static void atexit_header(void) |
461 | { | 514 | { |
462 | file_header.data_size += bytes_written; | 515 | header->data_size += bytes_written; |
463 | 516 | ||
464 | if (pwrite(output, &file_header, sizeof(file_header), 0) == -1) | 517 | perf_header__write(header, output); |
465 | perror("failed to write on file headers"); | ||
466 | } | 518 | } |
467 | 519 | ||
468 | static int __cmd_record(int argc, const char **argv) | 520 | static int __cmd_record(int argc, const char **argv) |
469 | { | 521 | { |
470 | int i, counter; | 522 | int i, counter; |
471 | struct stat st; | 523 | struct stat st; |
472 | pid_t pid; | 524 | pid_t pid = 0; |
473 | int flags; | 525 | int flags; |
474 | int ret; | 526 | int ret; |
527 | unsigned long waking = 0; | ||
475 | 528 | ||
476 | page_size = sysconf(_SC_PAGE_SIZE); | 529 | page_size = sysconf(_SC_PAGE_SIZE); |
477 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | 530 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); |
@@ -482,10 +535,14 @@ static int __cmd_record(int argc, const char **argv) | |||
482 | signal(SIGCHLD, sig_handler); | 535 | signal(SIGCHLD, sig_handler); |
483 | signal(SIGINT, sig_handler); | 536 | signal(SIGINT, sig_handler); |
484 | 537 | ||
485 | if (!stat(output_name, &st) && !force && !append_file) { | 538 | if (!stat(output_name, &st) && st.st_size) { |
486 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", | 539 | if (!force && !append_file) { |
487 | output_name); | 540 | fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", |
488 | exit(-1); | 541 | output_name); |
542 | exit(-1); | ||
543 | } | ||
544 | } else { | ||
545 | append_file = 0; | ||
489 | } | 546 | } |
490 | 547 | ||
491 | flags = O_CREAT|O_RDWR; | 548 | flags = O_CREAT|O_RDWR; |
@@ -500,21 +557,47 @@ static int __cmd_record(int argc, const char **argv) | |||
500 | exit(-1); | 557 | exit(-1); |
501 | } | 558 | } |
502 | 559 | ||
503 | if (!file_new) { | 560 | if (!file_new) |
504 | if (read(output, &file_header, sizeof(file_header)) == -1) { | 561 | header = perf_header__read(output); |
505 | perror("failed to read file headers"); | 562 | else |
506 | exit(-1); | 563 | header = perf_header__new(); |
564 | |||
565 | |||
566 | if (raw_samples) { | ||
567 | read_tracing_data(attrs, nr_counters); | ||
568 | } else { | ||
569 | for (i = 0; i < nr_counters; i++) { | ||
570 | if (attrs[i].sample_type & PERF_SAMPLE_RAW) { | ||
571 | read_tracing_data(attrs, nr_counters); | ||
572 | break; | ||
573 | } | ||
507 | } | 574 | } |
575 | } | ||
576 | atexit(atexit_header); | ||
508 | 577 | ||
509 | lseek(output, file_header.data_size, SEEK_CUR); | 578 | if (!system_wide) { |
579 | pid = target_pid; | ||
580 | if (pid == -1) | ||
581 | pid = getpid(); | ||
582 | |||
583 | open_counters(profile_cpu, pid); | ||
584 | } else { | ||
585 | if (profile_cpu != -1) { | ||
586 | open_counters(profile_cpu, target_pid); | ||
587 | } else { | ||
588 | for (i = 0; i < nr_cpus; i++) | ||
589 | open_counters(i, target_pid); | ||
590 | } | ||
510 | } | 591 | } |
511 | 592 | ||
512 | atexit(atexit_header); | 593 | if (file_new) |
594 | perf_header__write(header, output); | ||
513 | 595 | ||
514 | if (!system_wide) { | 596 | if (!system_wide) { |
515 | open_counters(-1, target_pid != -1 ? target_pid : getpid()); | 597 | pid_t tgid = pid_synthesize_comm_event(pid, 0); |
516 | } else for (i = 0; i < nr_cpus; i++) | 598 | pid_synthesize_mmap_samples(pid, tgid); |
517 | open_counters(i, target_pid); | 599 | } else |
600 | synthesize_all(); | ||
518 | 601 | ||
519 | if (target_pid == -1 && argc) { | 602 | if (target_pid == -1 && argc) { |
520 | pid = fork(); | 603 | pid = fork(); |
@@ -539,21 +622,33 @@ static int __cmd_record(int argc, const char **argv) | |||
539 | } | 622 | } |
540 | } | 623 | } |
541 | 624 | ||
542 | if (system_wide) | 625 | for (;;) { |
543 | synthesize_samples(); | ||
544 | |||
545 | while (!done) { | ||
546 | int hits = samples; | 626 | int hits = samples; |
547 | 627 | ||
548 | for (i = 0; i < nr_cpu; i++) { | 628 | for (i = 0; i < nr_cpu; i++) { |
549 | for (counter = 0; counter < nr_counters; counter++) | 629 | for (counter = 0; counter < nr_counters; counter++) { |
550 | mmap_read(&mmap_array[i][counter]); | 630 | if (mmap_array[i][counter].base) |
631 | mmap_read(&mmap_array[i][counter]); | ||
632 | } | ||
551 | } | 633 | } |
552 | 634 | ||
553 | if (hits == samples) | 635 | if (hits == samples) { |
554 | ret = poll(event_array, nr_poll, 100); | 636 | if (done) |
637 | break; | ||
638 | ret = poll(event_array, nr_poll, -1); | ||
639 | waking++; | ||
640 | } | ||
641 | |||
642 | if (done) { | ||
643 | for (i = 0; i < nr_cpu; i++) { | ||
644 | for (counter = 0; counter < nr_counters; counter++) | ||
645 | ioctl(fd[i][counter], PERF_EVENT_IOC_DISABLE); | ||
646 | } | ||
647 | } | ||
555 | } | 648 | } |
556 | 649 | ||
650 | fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking); | ||
651 | |||
557 | /* | 652 | /* |
558 | * Approximate RIP event size: 24 bytes. | 653 | * Approximate RIP event size: 24 bytes. |
559 | */ | 654 | */ |
@@ -580,10 +675,14 @@ static const struct option options[] = { | |||
580 | "record events on existing pid"), | 675 | "record events on existing pid"), |
581 | OPT_INTEGER('r', "realtime", &realtime_prio, | 676 | OPT_INTEGER('r', "realtime", &realtime_prio, |
582 | "collect data with this RT SCHED_FIFO priority"), | 677 | "collect data with this RT SCHED_FIFO priority"), |
678 | OPT_BOOLEAN('R', "raw-samples", &raw_samples, | ||
679 | "collect raw sample records from all opened counters"), | ||
583 | OPT_BOOLEAN('a', "all-cpus", &system_wide, | 680 | OPT_BOOLEAN('a', "all-cpus", &system_wide, |
584 | "system-wide collection from all CPUs"), | 681 | "system-wide collection from all CPUs"), |
585 | OPT_BOOLEAN('A', "append", &append_file, | 682 | OPT_BOOLEAN('A', "append", &append_file, |
586 | "append to the output file to do incremental profiling"), | 683 | "append to the output file to do incremental profiling"), |
684 | OPT_INTEGER('C', "profile_cpu", &profile_cpu, | ||
685 | "CPU to profile on"), | ||
587 | OPT_BOOLEAN('f', "force", &force, | 686 | OPT_BOOLEAN('f', "force", &force, |
588 | "overwrite existing data file"), | 687 | "overwrite existing data file"), |
589 | OPT_LONG('c', "count", &default_interval, | 688 | OPT_LONG('c', "count", &default_interval, |
@@ -600,14 +699,23 @@ static const struct option options[] = { | |||
600 | "do call-graph (stack chain/backtrace) recording"), | 699 | "do call-graph (stack chain/backtrace) recording"), |
601 | OPT_BOOLEAN('v', "verbose", &verbose, | 700 | OPT_BOOLEAN('v', "verbose", &verbose, |
602 | "be more verbose (show counter open errors, etc)"), | 701 | "be more verbose (show counter open errors, etc)"), |
702 | OPT_BOOLEAN('s', "stat", &inherit_stat, | ||
703 | "per thread counts"), | ||
704 | OPT_BOOLEAN('d', "data", &sample_address, | ||
705 | "Sample addresses"), | ||
706 | OPT_BOOLEAN('n', "no-samples", &no_samples, | ||
707 | "don't sample"), | ||
708 | OPT_BOOLEAN('M', "multiplex", &multiplex, | ||
709 | "multiplex counter output in a single channel"), | ||
603 | OPT_END() | 710 | OPT_END() |
604 | }; | 711 | }; |
605 | 712 | ||
606 | int cmd_record(int argc, const char **argv, const char *prefix) | 713 | int cmd_record(int argc, const char **argv, const char *prefix __used) |
607 | { | 714 | { |
608 | int counter; | 715 | int counter; |
609 | 716 | ||
610 | argc = parse_options(argc, argv, options, record_usage, 0); | 717 | argc = parse_options(argc, argv, options, record_usage, |
718 | PARSE_OPT_STOP_AT_NON_OPTION); | ||
611 | if (!argc && target_pid == -1 && !system_wide) | 719 | if (!argc && target_pid == -1 && !system_wide) |
612 | usage_with_options(record_usage, options); | 720 | usage_with_options(record_usage, options); |
613 | 721 | ||