diff options
author | Masami Hiramatsu <mhiramat@kernel.org> | 2016-06-14 23:28:30 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2016-06-15 13:34:31 -0400 |
commit | dd975497adcdd2526ae332d3938bd5d6e1c3731a (patch) | |
tree | 5d77c4d3f774c3a9c3caae1db06dffabbe9e7ed9 | |
parent | da1b0407c866e7a8679cd3b64b35d83825c58a14 (diff) |
perf probe: Introduce perf_cache interfaces
Introduce perf_cache object and interfaces to create, add entries,
commit, and delete the object.
perf_cache represents a file for the cached "perf probe" definitions on
one binary file or vmlinux which has its own build id. The probe cache
file is located under the build-id cache directory of the target binary,
as below;
<perf-debug-dir>/.build-id/<BU>/<ILDID>/probe
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20160615032830.31330.84998.stgit@devbox
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/util/probe-file.c | 331 | ||||
-rw-r--r-- | tools/perf/util/probe-file.h | 20 |
2 files changed, 351 insertions, 0 deletions
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 3fe6214970e6..25a40427003e 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c | |||
@@ -14,6 +14,7 @@ | |||
14 | * GNU General Public License for more details. | 14 | * GNU General Public License for more details. |
15 | * | 15 | * |
16 | */ | 16 | */ |
17 | #include <sys/uio.h> | ||
17 | #include "util.h" | 18 | #include "util.h" |
18 | #include "event.h" | 19 | #include "event.h" |
19 | #include "strlist.h" | 20 | #include "strlist.h" |
@@ -324,3 +325,333 @@ int probe_file__del_events(int fd, struct strfilter *filter) | |||
324 | 325 | ||
325 | return ret; | 326 | return ret; |
326 | } | 327 | } |
328 | |||
329 | /* Caller must ensure to remove this entry from list */ | ||
330 | static void probe_cache_entry__delete(struct probe_cache_entry *entry) | ||
331 | { | ||
332 | if (entry) { | ||
333 | BUG_ON(!list_empty(&entry->node)); | ||
334 | |||
335 | strlist__delete(entry->tevlist); | ||
336 | clear_perf_probe_event(&entry->pev); | ||
337 | zfree(&entry->spev); | ||
338 | free(entry); | ||
339 | } | ||
340 | } | ||
341 | |||
342 | static struct probe_cache_entry * | ||
343 | probe_cache_entry__new(struct perf_probe_event *pev) | ||
344 | { | ||
345 | struct probe_cache_entry *entry = zalloc(sizeof(*entry)); | ||
346 | |||
347 | if (entry) { | ||
348 | INIT_LIST_HEAD(&entry->node); | ||
349 | entry->tevlist = strlist__new(NULL, NULL); | ||
350 | if (!entry->tevlist) | ||
351 | zfree(&entry); | ||
352 | else if (pev) { | ||
353 | entry->spev = synthesize_perf_probe_command(pev); | ||
354 | if (!entry->spev || | ||
355 | perf_probe_event__copy(&entry->pev, pev) < 0) { | ||
356 | probe_cache_entry__delete(entry); | ||
357 | return NULL; | ||
358 | } | ||
359 | } | ||
360 | } | ||
361 | |||
362 | return entry; | ||
363 | } | ||
364 | |||
365 | /* For the kernel probe caches, pass target = NULL */ | ||
366 | static int probe_cache__open(struct probe_cache *pcache, const char *target) | ||
367 | { | ||
368 | char cpath[PATH_MAX]; | ||
369 | char sbuildid[SBUILD_ID_SIZE]; | ||
370 | char *dir_name; | ||
371 | bool is_kallsyms = !target; | ||
372 | int ret, fd; | ||
373 | |||
374 | if (target) | ||
375 | ret = filename__sprintf_build_id(target, sbuildid); | ||
376 | else { | ||
377 | target = DSO__NAME_KALLSYMS; | ||
378 | ret = sysfs__sprintf_build_id("/", sbuildid); | ||
379 | } | ||
380 | if (ret < 0) { | ||
381 | pr_debug("Failed to get build-id from %s.\n", target); | ||
382 | return ret; | ||
383 | } | ||
384 | |||
385 | /* If we have no buildid cache, make it */ | ||
386 | if (!build_id_cache__cached(sbuildid)) { | ||
387 | ret = build_id_cache__add_s(sbuildid, target, | ||
388 | is_kallsyms, NULL); | ||
389 | if (ret < 0) { | ||
390 | pr_debug("Failed to add build-id cache: %s\n", target); | ||
391 | return ret; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | dir_name = build_id_cache__cachedir(sbuildid, target, is_kallsyms, | ||
396 | false); | ||
397 | if (!dir_name) | ||
398 | return -ENOMEM; | ||
399 | |||
400 | snprintf(cpath, PATH_MAX, "%s/probes", dir_name); | ||
401 | fd = open(cpath, O_CREAT | O_RDWR, 0644); | ||
402 | if (fd < 0) | ||
403 | pr_debug("Failed to open cache(%d): %s\n", fd, cpath); | ||
404 | free(dir_name); | ||
405 | pcache->fd = fd; | ||
406 | |||
407 | return fd; | ||
408 | } | ||
409 | |||
410 | static int probe_cache__load(struct probe_cache *pcache) | ||
411 | { | ||
412 | struct probe_cache_entry *entry = NULL; | ||
413 | char buf[MAX_CMDLEN], *p; | ||
414 | int ret = 0; | ||
415 | FILE *fp; | ||
416 | |||
417 | fp = fdopen(dup(pcache->fd), "r"); | ||
418 | if (!fp) | ||
419 | return -EINVAL; | ||
420 | |||
421 | while (!feof(fp)) { | ||
422 | if (!fgets(buf, MAX_CMDLEN, fp)) | ||
423 | break; | ||
424 | p = strchr(buf, '\n'); | ||
425 | if (p) | ||
426 | *p = '\0'; | ||
427 | if (buf[0] == '#') { /* #perf_probe_event */ | ||
428 | entry = probe_cache_entry__new(NULL); | ||
429 | if (!entry) { | ||
430 | ret = -ENOMEM; | ||
431 | goto out; | ||
432 | } | ||
433 | entry->spev = strdup(buf + 1); | ||
434 | if (entry->spev) | ||
435 | ret = parse_perf_probe_command(buf + 1, | ||
436 | &entry->pev); | ||
437 | else | ||
438 | ret = -ENOMEM; | ||
439 | if (ret < 0) { | ||
440 | probe_cache_entry__delete(entry); | ||
441 | goto out; | ||
442 | } | ||
443 | list_add_tail(&entry->node, &pcache->entries); | ||
444 | } else { /* trace_probe_event */ | ||
445 | if (!entry) { | ||
446 | ret = -EINVAL; | ||
447 | goto out; | ||
448 | } | ||
449 | strlist__add(entry->tevlist, buf); | ||
450 | } | ||
451 | } | ||
452 | out: | ||
453 | fclose(fp); | ||
454 | return ret; | ||
455 | } | ||
456 | |||
457 | static struct probe_cache *probe_cache__alloc(void) | ||
458 | { | ||
459 | struct probe_cache *pcache = zalloc(sizeof(*pcache)); | ||
460 | |||
461 | if (pcache) { | ||
462 | INIT_LIST_HEAD(&pcache->entries); | ||
463 | pcache->fd = -EINVAL; | ||
464 | } | ||
465 | return pcache; | ||
466 | } | ||
467 | |||
468 | void probe_cache__purge(struct probe_cache *pcache) | ||
469 | { | ||
470 | struct probe_cache_entry *entry, *n; | ||
471 | |||
472 | list_for_each_entry_safe(entry, n, &pcache->entries, node) { | ||
473 | list_del_init(&entry->node); | ||
474 | probe_cache_entry__delete(entry); | ||
475 | } | ||
476 | } | ||
477 | |||
478 | void probe_cache__delete(struct probe_cache *pcache) | ||
479 | { | ||
480 | if (!pcache) | ||
481 | return; | ||
482 | |||
483 | probe_cache__purge(pcache); | ||
484 | if (pcache->fd > 0) | ||
485 | close(pcache->fd); | ||
486 | free(pcache); | ||
487 | } | ||
488 | |||
489 | struct probe_cache *probe_cache__new(const char *target) | ||
490 | { | ||
491 | struct probe_cache *pcache = probe_cache__alloc(); | ||
492 | int ret; | ||
493 | |||
494 | if (!pcache) | ||
495 | return NULL; | ||
496 | |||
497 | ret = probe_cache__open(pcache, target); | ||
498 | if (ret < 0) { | ||
499 | pr_debug("Cache open error: %d\n", ret); | ||
500 | goto out_err; | ||
501 | } | ||
502 | |||
503 | ret = probe_cache__load(pcache); | ||
504 | if (ret < 0) { | ||
505 | pr_debug("Cache read error: %d\n", ret); | ||
506 | goto out_err; | ||
507 | } | ||
508 | |||
509 | return pcache; | ||
510 | |||
511 | out_err: | ||
512 | probe_cache__delete(pcache); | ||
513 | return NULL; | ||
514 | } | ||
515 | |||
516 | static bool streql(const char *a, const char *b) | ||
517 | { | ||
518 | if (a == b) | ||
519 | return true; | ||
520 | |||
521 | if (!a || !b) | ||
522 | return false; | ||
523 | |||
524 | return !strcmp(a, b); | ||
525 | } | ||
526 | |||
527 | static struct probe_cache_entry * | ||
528 | probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev) | ||
529 | { | ||
530 | struct probe_cache_entry *entry = NULL; | ||
531 | char *cmd = synthesize_perf_probe_command(pev); | ||
532 | |||
533 | if (!cmd) | ||
534 | return NULL; | ||
535 | |||
536 | list_for_each_entry(entry, &pcache->entries, node) { | ||
537 | /* Hit if same event name or same command-string */ | ||
538 | if ((pev->event && | ||
539 | (streql(entry->pev.group, pev->group) && | ||
540 | streql(entry->pev.event, pev->event))) || | ||
541 | (!strcmp(entry->spev, cmd))) | ||
542 | goto found; | ||
543 | } | ||
544 | entry = NULL; | ||
545 | |||
546 | found: | ||
547 | free(cmd); | ||
548 | return entry; | ||
549 | } | ||
550 | |||
551 | int probe_cache__add_entry(struct probe_cache *pcache, | ||
552 | struct perf_probe_event *pev, | ||
553 | struct probe_trace_event *tevs, int ntevs) | ||
554 | { | ||
555 | struct probe_cache_entry *entry = NULL; | ||
556 | char *command; | ||
557 | int i, ret = 0; | ||
558 | |||
559 | if (!pcache || !pev || !tevs || ntevs <= 0) { | ||
560 | ret = -EINVAL; | ||
561 | goto out_err; | ||
562 | } | ||
563 | |||
564 | /* Remove old cache entry */ | ||
565 | entry = probe_cache__find(pcache, pev); | ||
566 | if (entry) { | ||
567 | list_del_init(&entry->node); | ||
568 | probe_cache_entry__delete(entry); | ||
569 | } | ||
570 | |||
571 | ret = -ENOMEM; | ||
572 | entry = probe_cache_entry__new(pev); | ||
573 | if (!entry) | ||
574 | goto out_err; | ||
575 | |||
576 | for (i = 0; i < ntevs; i++) { | ||
577 | if (!tevs[i].point.symbol) | ||
578 | continue; | ||
579 | |||
580 | command = synthesize_probe_trace_command(&tevs[i]); | ||
581 | if (!command) | ||
582 | goto out_err; | ||
583 | strlist__add(entry->tevlist, command); | ||
584 | free(command); | ||
585 | } | ||
586 | list_add_tail(&entry->node, &pcache->entries); | ||
587 | pr_debug("Added probe cache: %d\n", ntevs); | ||
588 | return 0; | ||
589 | |||
590 | out_err: | ||
591 | pr_debug("Failed to add probe caches\n"); | ||
592 | probe_cache_entry__delete(entry); | ||
593 | return ret; | ||
594 | } | ||
595 | |||
596 | static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd) | ||
597 | { | ||
598 | struct str_node *snode; | ||
599 | struct stat st; | ||
600 | struct iovec iov[3]; | ||
601 | int ret; | ||
602 | /* Save stat for rollback */ | ||
603 | ret = fstat(fd, &st); | ||
604 | if (ret < 0) | ||
605 | return ret; | ||
606 | |||
607 | pr_debug("Writing cache: #%s\n", entry->spev); | ||
608 | iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1; | ||
609 | iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev); | ||
610 | iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1; | ||
611 | ret = writev(fd, iov, 3); | ||
612 | if (ret < (int)iov[1].iov_len + 2) | ||
613 | goto rollback; | ||
614 | |||
615 | strlist__for_each(snode, entry->tevlist) { | ||
616 | iov[0].iov_base = (void *)snode->s; | ||
617 | iov[0].iov_len = strlen(snode->s); | ||
618 | iov[1].iov_base = (void *)"\n"; iov[1].iov_len = 1; | ||
619 | ret = writev(fd, iov, 2); | ||
620 | if (ret < (int)iov[0].iov_len + 1) | ||
621 | goto rollback; | ||
622 | } | ||
623 | return 0; | ||
624 | |||
625 | rollback: | ||
626 | /* Rollback to avoid cache file corruption */ | ||
627 | if (ret > 0) | ||
628 | ret = -1; | ||
629 | if (ftruncate(fd, st.st_size) < 0) | ||
630 | ret = -2; | ||
631 | |||
632 | return ret; | ||
633 | } | ||
634 | |||
635 | int probe_cache__commit(struct probe_cache *pcache) | ||
636 | { | ||
637 | struct probe_cache_entry *entry; | ||
638 | int ret = 0; | ||
639 | |||
640 | /* TBD: if we do not update existing entries, skip it */ | ||
641 | ret = lseek(pcache->fd, 0, SEEK_SET); | ||
642 | if (ret < 0) | ||
643 | goto out; | ||
644 | |||
645 | ret = ftruncate(pcache->fd, 0); | ||
646 | if (ret < 0) | ||
647 | goto out; | ||
648 | |||
649 | list_for_each_entry(entry, &pcache->entries, node) { | ||
650 | ret = probe_cache_entry__write(entry, pcache->fd); | ||
651 | pr_debug("Cache committed: %d\n", ret); | ||
652 | if (ret < 0) | ||
653 | break; | ||
654 | } | ||
655 | out: | ||
656 | return ret; | ||
657 | } | ||
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h index 18ac9cf51c34..d872e3df7e59 100644 --- a/tools/perf/util/probe-file.h +++ b/tools/perf/util/probe-file.h | |||
@@ -5,6 +5,19 @@ | |||
5 | #include "strfilter.h" | 5 | #include "strfilter.h" |
6 | #include "probe-event.h" | 6 | #include "probe-event.h" |
7 | 7 | ||
8 | /* Cache of probe definitions */ | ||
9 | struct probe_cache_entry { | ||
10 | struct list_head node; | ||
11 | struct perf_probe_event pev; | ||
12 | char *spev; | ||
13 | struct strlist *tevlist; | ||
14 | }; | ||
15 | |||
16 | struct probe_cache { | ||
17 | int fd; | ||
18 | struct list_head entries; | ||
19 | }; | ||
20 | |||
8 | #define PF_FL_UPROBE 1 | 21 | #define PF_FL_UPROBE 1 |
9 | #define PF_FL_RW 2 | 22 | #define PF_FL_RW 2 |
10 | 23 | ||
@@ -18,5 +31,12 @@ int probe_file__get_events(int fd, struct strfilter *filter, | |||
18 | struct strlist *plist); | 31 | struct strlist *plist); |
19 | int probe_file__del_strlist(int fd, struct strlist *namelist); | 32 | int probe_file__del_strlist(int fd, struct strlist *namelist); |
20 | 33 | ||
34 | struct probe_cache *probe_cache__new(const char *target); | ||
35 | int probe_cache__add_entry(struct probe_cache *pcache, | ||
36 | struct perf_probe_event *pev, | ||
37 | struct probe_trace_event *tevs, int ntevs); | ||
38 | int probe_cache__commit(struct probe_cache *pcache); | ||
39 | void probe_cache__purge(struct probe_cache *pcache); | ||
40 | void probe_cache__delete(struct probe_cache *pcache); | ||
21 | 41 | ||
22 | #endif | 42 | #endif |