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 /tools/perf | |
| 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>
Diffstat (limited to 'tools/perf')
| -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 |
