diff options
37 files changed, 947 insertions, 292 deletions
diff --git a/arch/x86/kernel/cpu/perf_event_intel_rapl.c b/arch/x86/kernel/cpu/perf_event_intel_rapl.c index 81431c0f0614..ed446bdcbf31 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_rapl.c +++ b/arch/x86/kernel/cpu/perf_event_intel_rapl.c | |||
| @@ -107,12 +107,6 @@ static ssize_t __rapl_##_var##_show(struct kobject *kobj, \ | |||
| 107 | static struct kobj_attribute format_attr_##_var = \ | 107 | static struct kobj_attribute format_attr_##_var = \ |
| 108 | __ATTR(_name, 0444, __rapl_##_var##_show, NULL) | 108 | __ATTR(_name, 0444, __rapl_##_var##_show, NULL) |
| 109 | 109 | ||
| 110 | #define RAPL_EVENT_DESC(_name, _config) \ | ||
| 111 | { \ | ||
| 112 | .attr = __ATTR(_name, 0444, rapl_event_show, NULL), \ | ||
| 113 | .config = _config, \ | ||
| 114 | } | ||
| 115 | |||
| 116 | #define RAPL_CNTR_WIDTH 32 /* 32-bit rapl counters */ | 110 | #define RAPL_CNTR_WIDTH 32 /* 32-bit rapl counters */ |
| 117 | 111 | ||
| 118 | #define RAPL_EVENT_ATTR_STR(_name, v, str) \ | 112 | #define RAPL_EVENT_ATTR_STR(_name, v, str) \ |
diff --git a/kernel/events/core.c b/kernel/events/core.c index 1a734e0adfa7..36babfd20648 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c | |||
| @@ -1050,13 +1050,13 @@ retry: | |||
| 1050 | /* | 1050 | /* |
| 1051 | * One of the few rules of preemptible RCU is that one cannot do | 1051 | * One of the few rules of preemptible RCU is that one cannot do |
| 1052 | * rcu_read_unlock() while holding a scheduler (or nested) lock when | 1052 | * rcu_read_unlock() while holding a scheduler (or nested) lock when |
| 1053 | * part of the read side critical section was preemptible -- see | 1053 | * part of the read side critical section was irqs-enabled -- see |
| 1054 | * rcu_read_unlock_special(). | 1054 | * rcu_read_unlock_special(). |
| 1055 | * | 1055 | * |
| 1056 | * Since ctx->lock nests under rq->lock we must ensure the entire read | 1056 | * Since ctx->lock nests under rq->lock we must ensure the entire read |
| 1057 | * side critical section is non-preemptible. | 1057 | * side critical section has interrupts disabled. |
| 1058 | */ | 1058 | */ |
| 1059 | preempt_disable(); | 1059 | local_irq_save(*flags); |
| 1060 | rcu_read_lock(); | 1060 | rcu_read_lock(); |
| 1061 | ctx = rcu_dereference(task->perf_event_ctxp[ctxn]); | 1061 | ctx = rcu_dereference(task->perf_event_ctxp[ctxn]); |
| 1062 | if (ctx) { | 1062 | if (ctx) { |
| @@ -1070,21 +1070,22 @@ retry: | |||
| 1070 | * if so. If we locked the right context, then it | 1070 | * if so. If we locked the right context, then it |
| 1071 | * can't get swapped on us any more. | 1071 | * can't get swapped on us any more. |
| 1072 | */ | 1072 | */ |
| 1073 | raw_spin_lock_irqsave(&ctx->lock, *flags); | 1073 | raw_spin_lock(&ctx->lock); |
| 1074 | if (ctx != rcu_dereference(task->perf_event_ctxp[ctxn])) { | 1074 | if (ctx != rcu_dereference(task->perf_event_ctxp[ctxn])) { |
| 1075 | raw_spin_unlock_irqrestore(&ctx->lock, *flags); | 1075 | raw_spin_unlock(&ctx->lock); |
| 1076 | rcu_read_unlock(); | 1076 | rcu_read_unlock(); |
| 1077 | preempt_enable(); | 1077 | local_irq_restore(*flags); |
| 1078 | goto retry; | 1078 | goto retry; |
| 1079 | } | 1079 | } |
| 1080 | 1080 | ||
| 1081 | if (!atomic_inc_not_zero(&ctx->refcount)) { | 1081 | if (!atomic_inc_not_zero(&ctx->refcount)) { |
| 1082 | raw_spin_unlock_irqrestore(&ctx->lock, *flags); | 1082 | raw_spin_unlock(&ctx->lock); |
| 1083 | ctx = NULL; | 1083 | ctx = NULL; |
| 1084 | } | 1084 | } |
| 1085 | } | 1085 | } |
| 1086 | rcu_read_unlock(); | 1086 | rcu_read_unlock(); |
| 1087 | preempt_enable(); | 1087 | if (!ctx) |
| 1088 | local_irq_restore(*flags); | ||
| 1088 | return ctx; | 1089 | return ctx; |
| 1089 | } | 1090 | } |
| 1090 | 1091 | ||
| @@ -6913,6 +6914,10 @@ static int perf_tp_filter_match(struct perf_event *event, | |||
| 6913 | { | 6914 | { |
| 6914 | void *record = data->raw->data; | 6915 | void *record = data->raw->data; |
| 6915 | 6916 | ||
| 6917 | /* only top level events have filters set */ | ||
| 6918 | if (event->parent) | ||
| 6919 | event = event->parent; | ||
| 6920 | |||
| 6916 | if (likely(!event->filter) || filter_match_preds(event->filter, record)) | 6921 | if (likely(!event->filter) || filter_match_preds(event->filter, record)) |
| 6917 | return 1; | 6922 | return 1; |
| 6918 | return 0; | 6923 | return 0; |
diff --git a/tools/include/linux/list.h b/tools/include/linux/list.h index 76b014c96893..a017f1595676 100644 --- a/tools/include/linux/list.h +++ b/tools/include/linux/list.h | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | #include <linux/compiler.h> | ||
| 1 | #include <linux/kernel.h> | 2 | #include <linux/kernel.h> |
| 2 | #include <linux/types.h> | 3 | #include <linux/types.h> |
| 3 | 4 | ||
diff --git a/tools/lib/bpf/.gitignore b/tools/lib/bpf/.gitignore index 812aeedaea38..f81e549ddfdb 100644 --- a/tools/lib/bpf/.gitignore +++ b/tools/lib/bpf/.gitignore | |||
| @@ -1,2 +1,2 @@ | |||
| 1 | libbpf_version.h | 1 | libbpf_version.h |
| 2 | FEATURE-DUMP | 2 | FEATURE-DUMP.libbpf |
diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index fc9af57b666e..a3caaf3eafbd 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile | |||
| @@ -180,7 +180,7 @@ config-clean: | |||
| 180 | clean: | 180 | clean: |
| 181 | $(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \ | 181 | $(call QUIET_CLEAN, libbpf) $(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \ |
| 182 | $(RM) LIBBPF-CFLAGS | 182 | $(RM) LIBBPF-CFLAGS |
| 183 | $(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP | 183 | $(call QUIET_CLEAN, core-gen) $(RM) $(OUTPUT)FEATURE-DUMP.libbpf |
| 184 | 184 | ||
| 185 | 185 | ||
| 186 | 186 | ||
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 4252fc22f78f..e176bad19bcb 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c | |||
| @@ -61,6 +61,60 @@ void libbpf_set_print(libbpf_print_fn_t warn, | |||
| 61 | __pr_debug = debug; | 61 | __pr_debug = debug; |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | #define STRERR_BUFSIZE 128 | ||
| 65 | |||
| 66 | #define ERRNO_OFFSET(e) ((e) - __LIBBPF_ERRNO__START) | ||
| 67 | #define ERRCODE_OFFSET(c) ERRNO_OFFSET(LIBBPF_ERRNO__##c) | ||
| 68 | #define NR_ERRNO (__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START) | ||
| 69 | |||
| 70 | static const char *libbpf_strerror_table[NR_ERRNO] = { | ||
| 71 | [ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf", | ||
| 72 | [ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid", | ||
| 73 | [ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost", | ||
| 74 | [ERRCODE_OFFSET(ENDIAN)] = "Endian missmatch", | ||
| 75 | [ERRCODE_OFFSET(INTERNAL)] = "Internal error in libbpf", | ||
| 76 | [ERRCODE_OFFSET(RELOC)] = "Relocation failed", | ||
| 77 | [ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading", | ||
| 78 | [ERRCODE_OFFSET(PROG2BIG)] = "Program too big", | ||
| 79 | [ERRCODE_OFFSET(KVER)] = "Incorrect kernel version", | ||
| 80 | }; | ||
| 81 | |||
| 82 | int libbpf_strerror(int err, char *buf, size_t size) | ||
| 83 | { | ||
| 84 | if (!buf || !size) | ||
| 85 | return -1; | ||
| 86 | |||
| 87 | err = err > 0 ? err : -err; | ||
| 88 | |||
| 89 | if (err < __LIBBPF_ERRNO__START) { | ||
| 90 | int ret; | ||
| 91 | |||
| 92 | ret = strerror_r(err, buf, size); | ||
| 93 | buf[size - 1] = '\0'; | ||
| 94 | return ret; | ||
| 95 | } | ||
| 96 | |||
| 97 | if (err < __LIBBPF_ERRNO__END) { | ||
| 98 | const char *msg; | ||
| 99 | |||
| 100 | msg = libbpf_strerror_table[ERRNO_OFFSET(err)]; | ||
| 101 | snprintf(buf, size, "%s", msg); | ||
| 102 | buf[size - 1] = '\0'; | ||
| 103 | return 0; | ||
| 104 | } | ||
| 105 | |||
| 106 | snprintf(buf, size, "Unknown libbpf error %d", err); | ||
| 107 | buf[size - 1] = '\0'; | ||
| 108 | return -1; | ||
| 109 | } | ||
| 110 | |||
| 111 | #define CHECK_ERR(action, err, out) do { \ | ||
| 112 | err = action; \ | ||
| 113 | if (err) \ | ||
| 114 | goto out; \ | ||
| 115 | } while(0) | ||
| 116 | |||
| 117 | |||
| 64 | /* Copied from tools/perf/util/util.h */ | 118 | /* Copied from tools/perf/util/util.h */ |
| 65 | #ifndef zfree | 119 | #ifndef zfree |
| 66 | # define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) | 120 | # define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) |
| @@ -258,7 +312,7 @@ static struct bpf_object *bpf_object__new(const char *path, | |||
| 258 | obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1); | 312 | obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1); |
| 259 | if (!obj) { | 313 | if (!obj) { |
| 260 | pr_warning("alloc memory failed for %s\n", path); | 314 | pr_warning("alloc memory failed for %s\n", path); |
| 261 | return NULL; | 315 | return ERR_PTR(-ENOMEM); |
| 262 | } | 316 | } |
| 263 | 317 | ||
| 264 | strcpy(obj->path, path); | 318 | strcpy(obj->path, path); |
| @@ -305,7 +359,7 @@ static int bpf_object__elf_init(struct bpf_object *obj) | |||
| 305 | 359 | ||
| 306 | if (obj_elf_valid(obj)) { | 360 | if (obj_elf_valid(obj)) { |
| 307 | pr_warning("elf init: internal error\n"); | 361 | pr_warning("elf init: internal error\n"); |
| 308 | return -EEXIST; | 362 | return -LIBBPF_ERRNO__LIBELF; |
| 309 | } | 363 | } |
| 310 | 364 | ||
| 311 | if (obj->efile.obj_buf_sz > 0) { | 365 | if (obj->efile.obj_buf_sz > 0) { |
| @@ -331,14 +385,14 @@ static int bpf_object__elf_init(struct bpf_object *obj) | |||
| 331 | if (!obj->efile.elf) { | 385 | if (!obj->efile.elf) { |
| 332 | pr_warning("failed to open %s as ELF file\n", | 386 | pr_warning("failed to open %s as ELF file\n", |
| 333 | obj->path); | 387 | obj->path); |
| 334 | err = -EINVAL; | 388 | err = -LIBBPF_ERRNO__LIBELF; |
| 335 | goto errout; | 389 | goto errout; |
| 336 | } | 390 | } |
| 337 | 391 | ||
| 338 | if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) { | 392 | if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) { |
| 339 | pr_warning("failed to get EHDR from %s\n", | 393 | pr_warning("failed to get EHDR from %s\n", |
| 340 | obj->path); | 394 | obj->path); |
| 341 | err = -EINVAL; | 395 | err = -LIBBPF_ERRNO__FORMAT; |
| 342 | goto errout; | 396 | goto errout; |
| 343 | } | 397 | } |
| 344 | ep = &obj->efile.ehdr; | 398 | ep = &obj->efile.ehdr; |
| @@ -346,7 +400,7 @@ static int bpf_object__elf_init(struct bpf_object *obj) | |||
| 346 | if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) { | 400 | if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) { |
| 347 | pr_warning("%s is not an eBPF object file\n", | 401 | pr_warning("%s is not an eBPF object file\n", |
| 348 | obj->path); | 402 | obj->path); |
| 349 | err = -EINVAL; | 403 | err = -LIBBPF_ERRNO__FORMAT; |
| 350 | goto errout; | 404 | goto errout; |
| 351 | } | 405 | } |
| 352 | 406 | ||
| @@ -374,14 +428,14 @@ bpf_object__check_endianness(struct bpf_object *obj) | |||
| 374 | goto mismatch; | 428 | goto mismatch; |
| 375 | break; | 429 | break; |
| 376 | default: | 430 | default: |
| 377 | return -EINVAL; | 431 | return -LIBBPF_ERRNO__ENDIAN; |
| 378 | } | 432 | } |
| 379 | 433 | ||
| 380 | return 0; | 434 | return 0; |
| 381 | 435 | ||
| 382 | mismatch: | 436 | mismatch: |
| 383 | pr_warning("Error: endianness mismatch.\n"); | 437 | pr_warning("Error: endianness mismatch.\n"); |
| 384 | return -EINVAL; | 438 | return -LIBBPF_ERRNO__ENDIAN; |
| 385 | } | 439 | } |
| 386 | 440 | ||
| 387 | static int | 441 | static int |
| @@ -402,7 +456,7 @@ bpf_object__init_kversion(struct bpf_object *obj, | |||
| 402 | 456 | ||
| 403 | if (size != sizeof(kver)) { | 457 | if (size != sizeof(kver)) { |
| 404 | pr_warning("invalid kver section in %s\n", obj->path); | 458 | pr_warning("invalid kver section in %s\n", obj->path); |
| 405 | return -EINVAL; | 459 | return -LIBBPF_ERRNO__FORMAT; |
| 406 | } | 460 | } |
| 407 | memcpy(&kver, data, sizeof(kver)); | 461 | memcpy(&kver, data, sizeof(kver)); |
| 408 | obj->kern_version = kver; | 462 | obj->kern_version = kver; |
| @@ -444,7 +498,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
| 444 | if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) { | 498 | if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) { |
| 445 | pr_warning("failed to get e_shstrndx from %s\n", | 499 | pr_warning("failed to get e_shstrndx from %s\n", |
| 446 | obj->path); | 500 | obj->path); |
| 447 | return -EINVAL; | 501 | return -LIBBPF_ERRNO__FORMAT; |
| 448 | } | 502 | } |
| 449 | 503 | ||
| 450 | while ((scn = elf_nextscn(elf, scn)) != NULL) { | 504 | while ((scn = elf_nextscn(elf, scn)) != NULL) { |
| @@ -456,7 +510,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
| 456 | if (gelf_getshdr(scn, &sh) != &sh) { | 510 | if (gelf_getshdr(scn, &sh) != &sh) { |
| 457 | pr_warning("failed to get section header from %s\n", | 511 | pr_warning("failed to get section header from %s\n", |
| 458 | obj->path); | 512 | obj->path); |
| 459 | err = -EINVAL; | 513 | err = -LIBBPF_ERRNO__FORMAT; |
| 460 | goto out; | 514 | goto out; |
| 461 | } | 515 | } |
| 462 | 516 | ||
| @@ -464,7 +518,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
| 464 | if (!name) { | 518 | if (!name) { |
| 465 | pr_warning("failed to get section name from %s\n", | 519 | pr_warning("failed to get section name from %s\n", |
| 466 | obj->path); | 520 | obj->path); |
| 467 | err = -EINVAL; | 521 | err = -LIBBPF_ERRNO__FORMAT; |
| 468 | goto out; | 522 | goto out; |
| 469 | } | 523 | } |
| 470 | 524 | ||
| @@ -472,7 +526,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
| 472 | if (!data) { | 526 | if (!data) { |
| 473 | pr_warning("failed to get section data from %s(%s)\n", | 527 | pr_warning("failed to get section data from %s(%s)\n", |
| 474 | name, obj->path); | 528 | name, obj->path); |
| 475 | err = -EINVAL; | 529 | err = -LIBBPF_ERRNO__FORMAT; |
| 476 | goto out; | 530 | goto out; |
| 477 | } | 531 | } |
| 478 | pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n", | 532 | pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n", |
| @@ -495,7 +549,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
| 495 | if (obj->efile.symbols) { | 549 | if (obj->efile.symbols) { |
| 496 | pr_warning("bpf: multiple SYMTAB in %s\n", | 550 | pr_warning("bpf: multiple SYMTAB in %s\n", |
| 497 | obj->path); | 551 | obj->path); |
| 498 | err = -EEXIST; | 552 | err = -LIBBPF_ERRNO__FORMAT; |
| 499 | } else | 553 | } else |
| 500 | obj->efile.symbols = data; | 554 | obj->efile.symbols = data; |
| 501 | } else if ((sh.sh_type == SHT_PROGBITS) && | 555 | } else if ((sh.sh_type == SHT_PROGBITS) && |
| @@ -504,7 +558,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
| 504 | err = bpf_object__add_program(obj, data->d_buf, | 558 | err = bpf_object__add_program(obj, data->d_buf, |
| 505 | data->d_size, name, idx); | 559 | data->d_size, name, idx); |
| 506 | if (err) { | 560 | if (err) { |
| 507 | char errmsg[128]; | 561 | char errmsg[STRERR_BUFSIZE]; |
| 562 | |||
| 508 | strerror_r(-err, errmsg, sizeof(errmsg)); | 563 | strerror_r(-err, errmsg, sizeof(errmsg)); |
| 509 | pr_warning("failed to alloc program %s (%s): %s", | 564 | pr_warning("failed to alloc program %s (%s): %s", |
| 510 | name, obj->path, errmsg); | 565 | name, obj->path, errmsg); |
| @@ -576,7 +631,7 @@ bpf_program__collect_reloc(struct bpf_program *prog, | |||
| 576 | 631 | ||
| 577 | if (!gelf_getrel(data, i, &rel)) { | 632 | if (!gelf_getrel(data, i, &rel)) { |
| 578 | pr_warning("relocation: failed to get %d reloc\n", i); | 633 | pr_warning("relocation: failed to get %d reloc\n", i); |
| 579 | return -EINVAL; | 634 | return -LIBBPF_ERRNO__FORMAT; |
| 580 | } | 635 | } |
| 581 | 636 | ||
| 582 | insn_idx = rel.r_offset / sizeof(struct bpf_insn); | 637 | insn_idx = rel.r_offset / sizeof(struct bpf_insn); |
| @@ -587,20 +642,20 @@ bpf_program__collect_reloc(struct bpf_program *prog, | |||
| 587 | &sym)) { | 642 | &sym)) { |
| 588 | pr_warning("relocation: symbol %"PRIx64" not found\n", | 643 | pr_warning("relocation: symbol %"PRIx64" not found\n", |
| 589 | GELF_R_SYM(rel.r_info)); | 644 | GELF_R_SYM(rel.r_info)); |
| 590 | return -EINVAL; | 645 | return -LIBBPF_ERRNO__FORMAT; |
| 591 | } | 646 | } |
| 592 | 647 | ||
| 593 | if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) { | 648 | if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) { |
| 594 | pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n", | 649 | pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n", |
| 595 | insn_idx, insns[insn_idx].code); | 650 | insn_idx, insns[insn_idx].code); |
| 596 | return -EINVAL; | 651 | return -LIBBPF_ERRNO__RELOC; |
| 597 | } | 652 | } |
| 598 | 653 | ||
| 599 | map_idx = sym.st_value / sizeof(struct bpf_map_def); | 654 | map_idx = sym.st_value / sizeof(struct bpf_map_def); |
| 600 | if (map_idx >= nr_maps) { | 655 | if (map_idx >= nr_maps) { |
| 601 | pr_warning("bpf relocation: map_idx %d large than %d\n", | 656 | pr_warning("bpf relocation: map_idx %d large than %d\n", |
| 602 | (int)map_idx, (int)nr_maps - 1); | 657 | (int)map_idx, (int)nr_maps - 1); |
| 603 | return -EINVAL; | 658 | return -LIBBPF_ERRNO__RELOC; |
| 604 | } | 659 | } |
| 605 | 660 | ||
| 606 | prog->reloc_desc[i].insn_idx = insn_idx; | 661 | prog->reloc_desc[i].insn_idx = insn_idx; |
| @@ -683,7 +738,7 @@ bpf_program__relocate(struct bpf_program *prog, int *map_fds) | |||
| 683 | if (insn_idx >= (int)prog->insns_cnt) { | 738 | if (insn_idx >= (int)prog->insns_cnt) { |
| 684 | pr_warning("relocation out of range: '%s'\n", | 739 | pr_warning("relocation out of range: '%s'\n", |
| 685 | prog->section_name); | 740 | prog->section_name); |
| 686 | return -ERANGE; | 741 | return -LIBBPF_ERRNO__RELOC; |
| 687 | } | 742 | } |
| 688 | insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; | 743 | insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD; |
| 689 | insns[insn_idx].imm = map_fds[map_idx]; | 744 | insns[insn_idx].imm = map_fds[map_idx]; |
| @@ -721,7 +776,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) | |||
| 721 | 776 | ||
| 722 | if (!obj_elf_valid(obj)) { | 777 | if (!obj_elf_valid(obj)) { |
| 723 | pr_warning("Internal error: elf object is closed\n"); | 778 | pr_warning("Internal error: elf object is closed\n"); |
| 724 | return -EINVAL; | 779 | return -LIBBPF_ERRNO__INTERNAL; |
| 725 | } | 780 | } |
| 726 | 781 | ||
| 727 | for (i = 0; i < obj->efile.nr_reloc; i++) { | 782 | for (i = 0; i < obj->efile.nr_reloc; i++) { |
| @@ -734,21 +789,21 @@ static int bpf_object__collect_reloc(struct bpf_object *obj) | |||
| 734 | 789 | ||
| 735 | if (shdr->sh_type != SHT_REL) { | 790 | if (shdr->sh_type != SHT_REL) { |
| 736 | pr_warning("internal error at %d\n", __LINE__); | 791 | pr_warning("internal error at %d\n", __LINE__); |
| 737 | return -EINVAL; | 792 | return -LIBBPF_ERRNO__INTERNAL; |
| 738 | } | 793 | } |
| 739 | 794 | ||
| 740 | prog = bpf_object__find_prog_by_idx(obj, idx); | 795 | prog = bpf_object__find_prog_by_idx(obj, idx); |
| 741 | if (!prog) { | 796 | if (!prog) { |
| 742 | pr_warning("relocation failed: no %d section\n", | 797 | pr_warning("relocation failed: no %d section\n", |
| 743 | idx); | 798 | idx); |
| 744 | return -ENOENT; | 799 | return -LIBBPF_ERRNO__RELOC; |
| 745 | } | 800 | } |
| 746 | 801 | ||
| 747 | err = bpf_program__collect_reloc(prog, nr_maps, | 802 | err = bpf_program__collect_reloc(prog, nr_maps, |
| 748 | shdr, data, | 803 | shdr, data, |
| 749 | obj->efile.symbols); | 804 | obj->efile.symbols); |
| 750 | if (err) | 805 | if (err) |
| 751 | return -EINVAL; | 806 | return err; |
| 752 | } | 807 | } |
| 753 | return 0; | 808 | return 0; |
| 754 | } | 809 | } |
| @@ -777,13 +832,23 @@ load_program(struct bpf_insn *insns, int insns_cnt, | |||
| 777 | goto out; | 832 | goto out; |
| 778 | } | 833 | } |
| 779 | 834 | ||
| 780 | ret = -EINVAL; | 835 | ret = -LIBBPF_ERRNO__LOAD; |
| 781 | pr_warning("load bpf program failed: %s\n", strerror(errno)); | 836 | pr_warning("load bpf program failed: %s\n", strerror(errno)); |
| 782 | 837 | ||
| 783 | if (log_buf) { | 838 | if (log_buf && log_buf[0] != '\0') { |
| 839 | ret = -LIBBPF_ERRNO__VERIFY; | ||
| 784 | pr_warning("-- BEGIN DUMP LOG ---\n"); | 840 | pr_warning("-- BEGIN DUMP LOG ---\n"); |
| 785 | pr_warning("\n%s\n", log_buf); | 841 | pr_warning("\n%s\n", log_buf); |
| 786 | pr_warning("-- END LOG --\n"); | 842 | pr_warning("-- END LOG --\n"); |
| 843 | } else { | ||
| 844 | if (insns_cnt >= BPF_MAXINSNS) { | ||
| 845 | pr_warning("Program too large (%d insns), at most %d insns\n", | ||
| 846 | insns_cnt, BPF_MAXINSNS); | ||
| 847 | ret = -LIBBPF_ERRNO__PROG2BIG; | ||
| 848 | } else if (log_buf) { | ||
| 849 | pr_warning("log buffer is empty\n"); | ||
| 850 | ret = -LIBBPF_ERRNO__KVER; | ||
| 851 | } | ||
| 787 | } | 852 | } |
| 788 | 853 | ||
| 789 | out: | 854 | out: |
| @@ -831,7 +896,7 @@ static int bpf_object__validate(struct bpf_object *obj) | |||
| 831 | if (obj->kern_version == 0) { | 896 | if (obj->kern_version == 0) { |
| 832 | pr_warning("%s doesn't provide kernel version\n", | 897 | pr_warning("%s doesn't provide kernel version\n", |
| 833 | obj->path); | 898 | obj->path); |
| 834 | return -EINVAL; | 899 | return -LIBBPF_ERRNO__KVERSION; |
| 835 | } | 900 | } |
| 836 | return 0; | 901 | return 0; |
| 837 | } | 902 | } |
| @@ -840,32 +905,28 @@ static struct bpf_object * | |||
| 840 | __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz) | 905 | __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz) |
| 841 | { | 906 | { |
| 842 | struct bpf_object *obj; | 907 | struct bpf_object *obj; |
| 908 | int err; | ||
| 843 | 909 | ||
| 844 | if (elf_version(EV_CURRENT) == EV_NONE) { | 910 | if (elf_version(EV_CURRENT) == EV_NONE) { |
| 845 | pr_warning("failed to init libelf for %s\n", path); | 911 | pr_warning("failed to init libelf for %s\n", path); |
| 846 | return NULL; | 912 | return ERR_PTR(-LIBBPF_ERRNO__LIBELF); |
| 847 | } | 913 | } |
| 848 | 914 | ||
| 849 | obj = bpf_object__new(path, obj_buf, obj_buf_sz); | 915 | obj = bpf_object__new(path, obj_buf, obj_buf_sz); |
| 850 | if (!obj) | 916 | if (IS_ERR(obj)) |
| 851 | return NULL; | 917 | return obj; |
| 852 | 918 | ||
| 853 | if (bpf_object__elf_init(obj)) | 919 | CHECK_ERR(bpf_object__elf_init(obj), err, out); |
| 854 | goto out; | 920 | CHECK_ERR(bpf_object__check_endianness(obj), err, out); |
| 855 | if (bpf_object__check_endianness(obj)) | 921 | CHECK_ERR(bpf_object__elf_collect(obj), err, out); |
| 856 | goto out; | 922 | CHECK_ERR(bpf_object__collect_reloc(obj), err, out); |
| 857 | if (bpf_object__elf_collect(obj)) | 923 | CHECK_ERR(bpf_object__validate(obj), err, out); |
| 858 | goto out; | ||
| 859 | if (bpf_object__collect_reloc(obj)) | ||
| 860 | goto out; | ||
| 861 | if (bpf_object__validate(obj)) | ||
| 862 | goto out; | ||
| 863 | 924 | ||
| 864 | bpf_object__elf_finish(obj); | 925 | bpf_object__elf_finish(obj); |
| 865 | return obj; | 926 | return obj; |
| 866 | out: | 927 | out: |
| 867 | bpf_object__close(obj); | 928 | bpf_object__close(obj); |
| 868 | return NULL; | 929 | return ERR_PTR(err); |
| 869 | } | 930 | } |
| 870 | 931 | ||
| 871 | struct bpf_object *bpf_object__open(const char *path) | 932 | struct bpf_object *bpf_object__open(const char *path) |
| @@ -922,6 +983,8 @@ int bpf_object__unload(struct bpf_object *obj) | |||
| 922 | 983 | ||
| 923 | int bpf_object__load(struct bpf_object *obj) | 984 | int bpf_object__load(struct bpf_object *obj) |
| 924 | { | 985 | { |
| 986 | int err; | ||
| 987 | |||
| 925 | if (!obj) | 988 | if (!obj) |
| 926 | return -EINVAL; | 989 | return -EINVAL; |
| 927 | 990 | ||
| @@ -931,18 +994,16 @@ int bpf_object__load(struct bpf_object *obj) | |||
| 931 | } | 994 | } |
| 932 | 995 | ||
| 933 | obj->loaded = true; | 996 | obj->loaded = true; |
| 934 | if (bpf_object__create_maps(obj)) | 997 | |
| 935 | goto out; | 998 | CHECK_ERR(bpf_object__create_maps(obj), err, out); |
| 936 | if (bpf_object__relocate(obj)) | 999 | CHECK_ERR(bpf_object__relocate(obj), err, out); |
| 937 | goto out; | 1000 | CHECK_ERR(bpf_object__load_progs(obj), err, out); |
| 938 | if (bpf_object__load_progs(obj)) | ||
| 939 | goto out; | ||
| 940 | 1001 | ||
| 941 | return 0; | 1002 | return 0; |
| 942 | out: | 1003 | out: |
| 943 | bpf_object__unload(obj); | 1004 | bpf_object__unload(obj); |
| 944 | pr_warning("failed to load object '%s'\n", obj->path); | 1005 | pr_warning("failed to load object '%s'\n", obj->path); |
| 945 | return -EINVAL; | 1006 | return err; |
| 946 | } | 1007 | } |
| 947 | 1008 | ||
| 948 | void bpf_object__close(struct bpf_object *obj) | 1009 | void bpf_object__close(struct bpf_object *obj) |
| @@ -990,10 +1051,18 @@ const char * | |||
| 990 | bpf_object__get_name(struct bpf_object *obj) | 1051 | bpf_object__get_name(struct bpf_object *obj) |
| 991 | { | 1052 | { |
| 992 | if (!obj) | 1053 | if (!obj) |
| 993 | return NULL; | 1054 | return ERR_PTR(-EINVAL); |
| 994 | return obj->path; | 1055 | return obj->path; |
| 995 | } | 1056 | } |
| 996 | 1057 | ||
| 1058 | unsigned int | ||
| 1059 | bpf_object__get_kversion(struct bpf_object *obj) | ||
| 1060 | { | ||
| 1061 | if (!obj) | ||
| 1062 | return 0; | ||
| 1063 | return obj->kern_version; | ||
| 1064 | } | ||
| 1065 | |||
| 997 | struct bpf_program * | 1066 | struct bpf_program * |
| 998 | bpf_program__next(struct bpf_program *prev, struct bpf_object *obj) | 1067 | bpf_program__next(struct bpf_program *prev, struct bpf_object *obj) |
| 999 | { | 1068 | { |
| @@ -1034,16 +1103,16 @@ int bpf_program__get_private(struct bpf_program *prog, void **ppriv) | |||
| 1034 | return 0; | 1103 | return 0; |
| 1035 | } | 1104 | } |
| 1036 | 1105 | ||
| 1037 | const char *bpf_program__title(struct bpf_program *prog, bool dup) | 1106 | const char *bpf_program__title(struct bpf_program *prog, bool needs_copy) |
| 1038 | { | 1107 | { |
| 1039 | const char *title; | 1108 | const char *title; |
| 1040 | 1109 | ||
| 1041 | title = prog->section_name; | 1110 | title = prog->section_name; |
| 1042 | if (dup) { | 1111 | if (needs_copy) { |
| 1043 | title = strdup(title); | 1112 | title = strdup(title); |
| 1044 | if (!title) { | 1113 | if (!title) { |
| 1045 | pr_warning("failed to strdup program title\n"); | 1114 | pr_warning("failed to strdup program title\n"); |
| 1046 | return NULL; | 1115 | return ERR_PTR(-ENOMEM); |
| 1047 | } | 1116 | } |
| 1048 | } | 1117 | } |
| 1049 | 1118 | ||
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index f16170c95ffd..c9a9aef2806c 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h | |||
| @@ -10,6 +10,26 @@ | |||
| 10 | 10 | ||
| 11 | #include <stdio.h> | 11 | #include <stdio.h> |
| 12 | #include <stdbool.h> | 12 | #include <stdbool.h> |
| 13 | #include <linux/err.h> | ||
| 14 | |||
| 15 | enum libbpf_errno { | ||
| 16 | __LIBBPF_ERRNO__START = 4000, | ||
| 17 | |||
| 18 | /* Something wrong in libelf */ | ||
| 19 | LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START, | ||
| 20 | LIBBPF_ERRNO__FORMAT, /* BPF object format invalid */ | ||
| 21 | LIBBPF_ERRNO__KVERSION, /* Incorrect or no 'version' section */ | ||
| 22 | LIBBPF_ERRNO__ENDIAN, /* Endian missmatch */ | ||
| 23 | LIBBPF_ERRNO__INTERNAL, /* Internal error in libbpf */ | ||
| 24 | LIBBPF_ERRNO__RELOC, /* Relocation failed */ | ||
| 25 | LIBBPF_ERRNO__LOAD, /* Load program failure for unknown reason */ | ||
| 26 | LIBBPF_ERRNO__VERIFY, /* Kernel verifier blocks program loading */ | ||
| 27 | LIBBPF_ERRNO__PROG2BIG, /* Program too big */ | ||
| 28 | LIBBPF_ERRNO__KVER, /* Incorrect kernel version */ | ||
| 29 | __LIBBPF_ERRNO__END, | ||
| 30 | }; | ||
| 31 | |||
| 32 | int libbpf_strerror(int err, char *buf, size_t size); | ||
| 13 | 33 | ||
| 14 | /* | 34 | /* |
| 15 | * In include/linux/compiler-gcc.h, __printf is defined. However | 35 | * In include/linux/compiler-gcc.h, __printf is defined. However |
| @@ -36,6 +56,7 @@ void bpf_object__close(struct bpf_object *object); | |||
| 36 | int bpf_object__load(struct bpf_object *obj); | 56 | int bpf_object__load(struct bpf_object *obj); |
| 37 | int bpf_object__unload(struct bpf_object *obj); | 57 | int bpf_object__unload(struct bpf_object *obj); |
| 38 | const char *bpf_object__get_name(struct bpf_object *obj); | 58 | const char *bpf_object__get_name(struct bpf_object *obj); |
| 59 | unsigned int bpf_object__get_kversion(struct bpf_object *obj); | ||
| 39 | 60 | ||
| 40 | struct bpf_object *bpf_object__next(struct bpf_object *prev); | 61 | struct bpf_object *bpf_object__next(struct bpf_object *prev); |
| 41 | #define bpf_object__for_each_safe(pos, tmp) \ | 62 | #define bpf_object__for_each_safe(pos, tmp) \ |
| @@ -63,7 +84,7 @@ int bpf_program__set_private(struct bpf_program *prog, void *priv, | |||
| 63 | int bpf_program__get_private(struct bpf_program *prog, | 84 | int bpf_program__get_private(struct bpf_program *prog, |
| 64 | void **ppriv); | 85 | void **ppriv); |
| 65 | 86 | ||
| 66 | const char *bpf_program__title(struct bpf_program *prog, bool dup); | 87 | const char *bpf_program__title(struct bpf_program *prog, bool needs_copy); |
| 67 | 88 | ||
| 68 | int bpf_program__fd(struct bpf_program *prog); | 89 | int bpf_program__fd(struct bpf_program *prog); |
| 69 | 90 | ||
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 7ea078658a87..13293de8869f 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
| @@ -62,7 +62,6 @@ OPTIONS | |||
| 62 | --verbose=:: | 62 | --verbose=:: |
| 63 | Verbosity level. | 63 | Verbosity level. |
| 64 | 64 | ||
| 65 | -i:: | ||
| 66 | --no-inherit:: | 65 | --no-inherit:: |
| 67 | Child tasks do not inherit counters. | 66 | Child tasks do not inherit counters. |
| 68 | 67 | ||
diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 480546d5f13b..dcd9a70c7193 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile | |||
| @@ -78,7 +78,7 @@ clean: | |||
| 78 | # The build-test target is not really parallel, don't print the jobs info: | 78 | # The build-test target is not really parallel, don't print the jobs info: |
| 79 | # | 79 | # |
| 80 | build-test: | 80 | build-test: |
| 81 | @$(MAKE) -f tests/make --no-print-directory | 81 | @$(MAKE) SHUF=1 -f tests/make --no-print-directory |
| 82 | 82 | ||
| 83 | # | 83 | # |
| 84 | # All other targets get passed through: | 84 | # All other targets get passed through: |
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 0ee6d900e100..e3d3e32c0a93 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
| @@ -1203,12 +1203,13 @@ static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_ | |||
| 1203 | 1203 | ||
| 1204 | static int pid_cmp(struct work_atoms *l, struct work_atoms *r) | 1204 | static int pid_cmp(struct work_atoms *l, struct work_atoms *r) |
| 1205 | { | 1205 | { |
| 1206 | if (l->thread == r->thread) | ||
| 1207 | return 0; | ||
| 1206 | if (l->thread->tid < r->thread->tid) | 1208 | if (l->thread->tid < r->thread->tid) |
| 1207 | return -1; | 1209 | return -1; |
| 1208 | if (l->thread->tid > r->thread->tid) | 1210 | if (l->thread->tid > r->thread->tid) |
| 1209 | return 1; | 1211 | return 1; |
| 1210 | 1212 | return (int)(l->thread - r->thread); | |
| 1211 | return 0; | ||
| 1212 | } | 1213 | } |
| 1213 | 1214 | ||
| 1214 | static int avg_cmp(struct work_atoms *l, struct work_atoms *r) | 1215 | static int avg_cmp(struct work_atoms *l, struct work_atoms *r) |
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 2f438f76cceb..e77880b5094d 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
| @@ -122,6 +122,9 @@ static bool forever = false; | |||
| 122 | static struct timespec ref_time; | 122 | static struct timespec ref_time; |
| 123 | static struct cpu_map *aggr_map; | 123 | static struct cpu_map *aggr_map; |
| 124 | static aggr_get_id_t aggr_get_id; | 124 | static aggr_get_id_t aggr_get_id; |
| 125 | static bool append_file; | ||
| 126 | static const char *output_name; | ||
| 127 | static int output_fd; | ||
| 125 | 128 | ||
| 126 | static volatile int done = 0; | 129 | static volatile int done = 0; |
| 127 | 130 | ||
| @@ -513,15 +516,6 @@ static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
| 513 | 516 | ||
| 514 | if (evsel->cgrp) | 517 | if (evsel->cgrp) |
| 515 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 518 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
| 516 | |||
| 517 | if (csv_output || stat_config.interval) | ||
| 518 | return; | ||
| 519 | |||
| 520 | if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) | ||
| 521 | fprintf(output, " # %8.3f CPUs utilized ", | ||
| 522 | avg / avg_stats(&walltime_nsecs_stats)); | ||
| 523 | else | ||
| 524 | fprintf(output, " "); | ||
| 525 | } | 519 | } |
| 526 | 520 | ||
| 527 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | 521 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
| @@ -529,7 +523,6 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
| 529 | FILE *output = stat_config.output; | 523 | FILE *output = stat_config.output; |
| 530 | double sc = evsel->scale; | 524 | double sc = evsel->scale; |
| 531 | const char *fmt; | 525 | const char *fmt; |
| 532 | int cpu = cpu_map__id_to_cpu(id); | ||
| 533 | 526 | ||
| 534 | if (csv_output) { | 527 | if (csv_output) { |
| 535 | fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; | 528 | fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s"; |
| @@ -542,9 +535,6 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
| 542 | 535 | ||
| 543 | aggr_printout(evsel, id, nr); | 536 | aggr_printout(evsel, id, nr); |
| 544 | 537 | ||
| 545 | if (stat_config.aggr_mode == AGGR_GLOBAL) | ||
| 546 | cpu = 0; | ||
| 547 | |||
| 548 | fprintf(output, fmt, avg, csv_sep); | 538 | fprintf(output, fmt, avg, csv_sep); |
| 549 | 539 | ||
| 550 | if (evsel->unit) | 540 | if (evsel->unit) |
| @@ -556,12 +546,24 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
| 556 | 546 | ||
| 557 | if (evsel->cgrp) | 547 | if (evsel->cgrp) |
| 558 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 548 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
| 549 | } | ||
| 559 | 550 | ||
| 560 | if (csv_output || stat_config.interval) | 551 | static void printout(int id, int nr, struct perf_evsel *counter, double uval) |
| 561 | return; | 552 | { |
| 553 | int cpu = cpu_map__id_to_cpu(id); | ||
| 554 | |||
| 555 | if (stat_config.aggr_mode == AGGR_GLOBAL) | ||
| 556 | cpu = 0; | ||
| 562 | 557 | ||
| 563 | perf_stat__print_shadow_stats(output, evsel, avg, cpu, | 558 | if (nsec_counter(counter)) |
| 564 | stat_config.aggr_mode); | 559 | nsec_printout(id, nr, counter, uval); |
| 560 | else | ||
| 561 | abs_printout(id, nr, counter, uval); | ||
| 562 | |||
| 563 | if (!csv_output && !stat_config.interval) | ||
| 564 | perf_stat__print_shadow_stats(stat_config.output, counter, | ||
| 565 | uval, cpu, | ||
| 566 | stat_config.aggr_mode); | ||
| 565 | } | 567 | } |
| 566 | 568 | ||
| 567 | static void print_aggr(char *prefix) | 569 | static void print_aggr(char *prefix) |
| @@ -617,12 +619,7 @@ static void print_aggr(char *prefix) | |||
| 617 | continue; | 619 | continue; |
| 618 | } | 620 | } |
| 619 | uval = val * counter->scale; | 621 | uval = val * counter->scale; |
| 620 | 622 | printout(id, nr, counter, uval); | |
| 621 | if (nsec_counter(counter)) | ||
| 622 | nsec_printout(id, nr, counter, uval); | ||
| 623 | else | ||
| 624 | abs_printout(id, nr, counter, uval); | ||
| 625 | |||
| 626 | if (!csv_output) | 623 | if (!csv_output) |
| 627 | print_noise(counter, 1.0); | 624 | print_noise(counter, 1.0); |
| 628 | 625 | ||
| @@ -653,11 +650,7 @@ static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | |||
| 653 | fprintf(output, "%s", prefix); | 650 | fprintf(output, "%s", prefix); |
| 654 | 651 | ||
| 655 | uval = val * counter->scale; | 652 | uval = val * counter->scale; |
| 656 | 653 | printout(thread, 0, counter, uval); | |
| 657 | if (nsec_counter(counter)) | ||
| 658 | nsec_printout(thread, 0, counter, uval); | ||
| 659 | else | ||
| 660 | abs_printout(thread, 0, counter, uval); | ||
| 661 | 654 | ||
| 662 | if (!csv_output) | 655 | if (!csv_output) |
| 663 | print_noise(counter, 1.0); | 656 | print_noise(counter, 1.0); |
| @@ -707,11 +700,7 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix) | |||
| 707 | } | 700 | } |
| 708 | 701 | ||
| 709 | uval = avg * counter->scale; | 702 | uval = avg * counter->scale; |
| 710 | 703 | printout(-1, 0, counter, uval); | |
| 711 | if (nsec_counter(counter)) | ||
| 712 | nsec_printout(-1, 0, counter, uval); | ||
| 713 | else | ||
| 714 | abs_printout(-1, 0, counter, uval); | ||
| 715 | 704 | ||
| 716 | print_noise(counter, avg); | 705 | print_noise(counter, avg); |
| 717 | 706 | ||
| @@ -764,12 +753,7 @@ static void print_counter(struct perf_evsel *counter, char *prefix) | |||
| 764 | } | 753 | } |
| 765 | 754 | ||
| 766 | uval = val * counter->scale; | 755 | uval = val * counter->scale; |
| 767 | 756 | printout(cpu, 0, counter, uval); | |
| 768 | if (nsec_counter(counter)) | ||
| 769 | nsec_printout(cpu, 0, counter, uval); | ||
| 770 | else | ||
| 771 | abs_printout(cpu, 0, counter, uval); | ||
| 772 | |||
| 773 | if (!csv_output) | 757 | if (!csv_output) |
| 774 | print_noise(counter, 1.0); | 758 | print_noise(counter, 1.0); |
| 775 | print_running(run, ena); | 759 | print_running(run, ena); |
| @@ -946,6 +930,67 @@ static int stat__set_big_num(const struct option *opt __maybe_unused, | |||
| 946 | return 0; | 930 | return 0; |
| 947 | } | 931 | } |
| 948 | 932 | ||
| 933 | static const struct option stat_options[] = { | ||
| 934 | OPT_BOOLEAN('T', "transaction", &transaction_run, | ||
| 935 | "hardware transaction statistics"), | ||
| 936 | OPT_CALLBACK('e', "event", &evsel_list, "event", | ||
| 937 | "event selector. use 'perf list' to list available events", | ||
| 938 | parse_events_option), | ||
| 939 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", | ||
| 940 | "event filter", parse_filter), | ||
| 941 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | ||
| 942 | "child tasks do not inherit counters"), | ||
| 943 | OPT_STRING('p', "pid", &target.pid, "pid", | ||
| 944 | "stat events on existing process id"), | ||
| 945 | OPT_STRING('t', "tid", &target.tid, "tid", | ||
| 946 | "stat events on existing thread id"), | ||
| 947 | OPT_BOOLEAN('a', "all-cpus", &target.system_wide, | ||
| 948 | "system-wide collection from all CPUs"), | ||
| 949 | OPT_BOOLEAN('g', "group", &group, | ||
| 950 | "put the counters into a counter group"), | ||
| 951 | OPT_BOOLEAN('c', "scale", &stat_config.scale, "scale/normalize counters"), | ||
| 952 | OPT_INCR('v', "verbose", &verbose, | ||
| 953 | "be more verbose (show counter open errors, etc)"), | ||
| 954 | OPT_INTEGER('r', "repeat", &run_count, | ||
| 955 | "repeat command and print average + stddev (max: 100, forever: 0)"), | ||
| 956 | OPT_BOOLEAN('n', "null", &null_run, | ||
| 957 | "null run - dont start any counters"), | ||
| 958 | OPT_INCR('d', "detailed", &detailed_run, | ||
| 959 | "detailed run - start a lot of events"), | ||
| 960 | OPT_BOOLEAN('S', "sync", &sync_run, | ||
| 961 | "call sync() before starting a run"), | ||
| 962 | OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, | ||
| 963 | "print large numbers with thousands\' separators", | ||
| 964 | stat__set_big_num), | ||
| 965 | OPT_STRING('C', "cpu", &target.cpu_list, "cpu", | ||
| 966 | "list of cpus to monitor in system-wide"), | ||
| 967 | OPT_SET_UINT('A', "no-aggr", &stat_config.aggr_mode, | ||
| 968 | "disable CPU count aggregation", AGGR_NONE), | ||
| 969 | OPT_STRING('x', "field-separator", &csv_sep, "separator", | ||
| 970 | "print counts with custom separator"), | ||
| 971 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | ||
| 972 | "monitor event in cgroup name only", parse_cgroups), | ||
| 973 | OPT_STRING('o', "output", &output_name, "file", "output file name"), | ||
| 974 | OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), | ||
| 975 | OPT_INTEGER(0, "log-fd", &output_fd, | ||
| 976 | "log output to fd, instead of stderr"), | ||
| 977 | OPT_STRING(0, "pre", &pre_cmd, "command", | ||
| 978 | "command to run prior to the measured command"), | ||
| 979 | OPT_STRING(0, "post", &post_cmd, "command", | ||
| 980 | "command to run after to the measured command"), | ||
| 981 | OPT_UINTEGER('I', "interval-print", &stat_config.interval, | ||
| 982 | "print counts at regular interval in ms (>= 10)"), | ||
| 983 | OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode, | ||
| 984 | "aggregate counts per processor socket", AGGR_SOCKET), | ||
| 985 | OPT_SET_UINT(0, "per-core", &stat_config.aggr_mode, | ||
| 986 | "aggregate counts per physical processor core", AGGR_CORE), | ||
| 987 | OPT_SET_UINT(0, "per-thread", &stat_config.aggr_mode, | ||
| 988 | "aggregate counts per thread", AGGR_THREAD), | ||
| 989 | OPT_UINTEGER('D', "delay", &initial_delay, | ||
| 990 | "ms to wait before starting measurement after program start"), | ||
| 991 | OPT_END() | ||
| 992 | }; | ||
| 993 | |||
| 949 | static int perf_stat__get_socket(struct cpu_map *map, int cpu) | 994 | static int perf_stat__get_socket(struct cpu_map *map, int cpu) |
| 950 | { | 995 | { |
| 951 | return cpu_map__get_socket(map, cpu, NULL); | 996 | return cpu_map__get_socket(map, cpu, NULL); |
| @@ -1193,69 +1238,6 @@ static int add_default_attributes(void) | |||
| 1193 | 1238 | ||
| 1194 | int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | 1239 | int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) |
| 1195 | { | 1240 | { |
| 1196 | bool append_file = false; | ||
| 1197 | int output_fd = 0; | ||
| 1198 | const char *output_name = NULL; | ||
| 1199 | const struct option options[] = { | ||
| 1200 | OPT_BOOLEAN('T', "transaction", &transaction_run, | ||
| 1201 | "hardware transaction statistics"), | ||
| 1202 | OPT_CALLBACK('e', "event", &evsel_list, "event", | ||
| 1203 | "event selector. use 'perf list' to list available events", | ||
| 1204 | parse_events_option), | ||
| 1205 | OPT_CALLBACK(0, "filter", &evsel_list, "filter", | ||
| 1206 | "event filter", parse_filter), | ||
| 1207 | OPT_BOOLEAN('i', "no-inherit", &no_inherit, | ||
| 1208 | "child tasks do not inherit counters"), | ||
| 1209 | OPT_STRING('p', "pid", &target.pid, "pid", | ||
| 1210 | "stat events on existing process id"), | ||
| 1211 | OPT_STRING('t', "tid", &target.tid, "tid", | ||
| 1212 | "stat events on existing thread id"), | ||
| 1213 | OPT_BOOLEAN('a', "all-cpus", &target.system_wide, | ||
| 1214 | "system-wide collection from all CPUs"), | ||
| 1215 | OPT_BOOLEAN('g', "group", &group, | ||
| 1216 | "put the counters into a counter group"), | ||
| 1217 | OPT_BOOLEAN('c', "scale", &stat_config.scale, "scale/normalize counters"), | ||
| 1218 | OPT_INCR('v', "verbose", &verbose, | ||
| 1219 | "be more verbose (show counter open errors, etc)"), | ||
| 1220 | OPT_INTEGER('r', "repeat", &run_count, | ||
| 1221 | "repeat command and print average + stddev (max: 100, forever: 0)"), | ||
| 1222 | OPT_BOOLEAN('n', "null", &null_run, | ||
| 1223 | "null run - dont start any counters"), | ||
| 1224 | OPT_INCR('d', "detailed", &detailed_run, | ||
| 1225 | "detailed run - start a lot of events"), | ||
| 1226 | OPT_BOOLEAN('S', "sync", &sync_run, | ||
| 1227 | "call sync() before starting a run"), | ||
| 1228 | OPT_CALLBACK_NOOPT('B', "big-num", NULL, NULL, | ||
| 1229 | "print large numbers with thousands\' separators", | ||
| 1230 | stat__set_big_num), | ||
| 1231 | OPT_STRING('C', "cpu", &target.cpu_list, "cpu", | ||
| 1232 | "list of cpus to monitor in system-wide"), | ||
| 1233 | OPT_SET_UINT('A', "no-aggr", &stat_config.aggr_mode, | ||
| 1234 | "disable CPU count aggregation", AGGR_NONE), | ||
| 1235 | OPT_STRING('x', "field-separator", &csv_sep, "separator", | ||
| 1236 | "print counts with custom separator"), | ||
| 1237 | OPT_CALLBACK('G', "cgroup", &evsel_list, "name", | ||
| 1238 | "monitor event in cgroup name only", parse_cgroups), | ||
| 1239 | OPT_STRING('o', "output", &output_name, "file", "output file name"), | ||
| 1240 | OPT_BOOLEAN(0, "append", &append_file, "append to the output file"), | ||
| 1241 | OPT_INTEGER(0, "log-fd", &output_fd, | ||
| 1242 | "log output to fd, instead of stderr"), | ||
| 1243 | OPT_STRING(0, "pre", &pre_cmd, "command", | ||
| 1244 | "command to run prior to the measured command"), | ||
| 1245 | OPT_STRING(0, "post", &post_cmd, "command", | ||
| 1246 | "command to run after to the measured command"), | ||
| 1247 | OPT_UINTEGER('I', "interval-print", &stat_config.interval, | ||
| 1248 | "print counts at regular interval in ms (>= 10)"), | ||
| 1249 | OPT_SET_UINT(0, "per-socket", &stat_config.aggr_mode, | ||
| 1250 | "aggregate counts per processor socket", AGGR_SOCKET), | ||
| 1251 | OPT_SET_UINT(0, "per-core", &stat_config.aggr_mode, | ||
| 1252 | "aggregate counts per physical processor core", AGGR_CORE), | ||
| 1253 | OPT_SET_UINT(0, "per-thread", &stat_config.aggr_mode, | ||
| 1254 | "aggregate counts per thread", AGGR_THREAD), | ||
| 1255 | OPT_UINTEGER('D', "delay", &initial_delay, | ||
| 1256 | "ms to wait before starting measurement after program start"), | ||
| 1257 | OPT_END() | ||
| 1258 | }; | ||
| 1259 | const char * const stat_usage[] = { | 1241 | const char * const stat_usage[] = { |
| 1260 | "perf stat [<options>] [<command>]", | 1242 | "perf stat [<options>] [<command>]", |
| 1261 | NULL | 1243 | NULL |
| @@ -1271,7 +1253,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1271 | if (evsel_list == NULL) | 1253 | if (evsel_list == NULL) |
| 1272 | return -ENOMEM; | 1254 | return -ENOMEM; |
| 1273 | 1255 | ||
| 1274 | argc = parse_options(argc, argv, options, stat_usage, | 1256 | argc = parse_options(argc, argv, stat_options, stat_usage, |
| 1275 | PARSE_OPT_STOP_AT_NON_OPTION); | 1257 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 1276 | 1258 | ||
| 1277 | interval = stat_config.interval; | 1259 | interval = stat_config.interval; |
| @@ -1281,14 +1263,14 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1281 | 1263 | ||
| 1282 | if (output_name && output_fd) { | 1264 | if (output_name && output_fd) { |
| 1283 | fprintf(stderr, "cannot use both --output and --log-fd\n"); | 1265 | fprintf(stderr, "cannot use both --output and --log-fd\n"); |
| 1284 | parse_options_usage(stat_usage, options, "o", 1); | 1266 | parse_options_usage(stat_usage, stat_options, "o", 1); |
| 1285 | parse_options_usage(NULL, options, "log-fd", 0); | 1267 | parse_options_usage(NULL, stat_options, "log-fd", 0); |
| 1286 | goto out; | 1268 | goto out; |
| 1287 | } | 1269 | } |
| 1288 | 1270 | ||
| 1289 | if (output_fd < 0) { | 1271 | if (output_fd < 0) { |
| 1290 | fprintf(stderr, "argument to --log-fd must be a > 0\n"); | 1272 | fprintf(stderr, "argument to --log-fd must be a > 0\n"); |
| 1291 | parse_options_usage(stat_usage, options, "log-fd", 0); | 1273 | parse_options_usage(stat_usage, stat_options, "log-fd", 0); |
| 1292 | goto out; | 1274 | goto out; |
| 1293 | } | 1275 | } |
| 1294 | 1276 | ||
| @@ -1328,8 +1310,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1328 | /* User explicitly passed -B? */ | 1310 | /* User explicitly passed -B? */ |
| 1329 | if (big_num_opt == 1) { | 1311 | if (big_num_opt == 1) { |
| 1330 | fprintf(stderr, "-B option not supported with -x\n"); | 1312 | fprintf(stderr, "-B option not supported with -x\n"); |
| 1331 | parse_options_usage(stat_usage, options, "B", 1); | 1313 | parse_options_usage(stat_usage, stat_options, "B", 1); |
| 1332 | parse_options_usage(NULL, options, "x", 1); | 1314 | parse_options_usage(NULL, stat_options, "x", 1); |
| 1333 | goto out; | 1315 | goto out; |
| 1334 | } else /* Nope, so disable big number formatting */ | 1316 | } else /* Nope, so disable big number formatting */ |
| 1335 | big_num = false; | 1317 | big_num = false; |
| @@ -1337,11 +1319,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1337 | big_num = false; | 1319 | big_num = false; |
| 1338 | 1320 | ||
| 1339 | if (!argc && target__none(&target)) | 1321 | if (!argc && target__none(&target)) |
| 1340 | usage_with_options(stat_usage, options); | 1322 | usage_with_options(stat_usage, stat_options); |
| 1341 | 1323 | ||
| 1342 | if (run_count < 0) { | 1324 | if (run_count < 0) { |
| 1343 | pr_err("Run count must be a positive number\n"); | 1325 | pr_err("Run count must be a positive number\n"); |
| 1344 | parse_options_usage(stat_usage, options, "r", 1); | 1326 | parse_options_usage(stat_usage, stat_options, "r", 1); |
| 1345 | goto out; | 1327 | goto out; |
| 1346 | } else if (run_count == 0) { | 1328 | } else if (run_count == 0) { |
| 1347 | forever = true; | 1329 | forever = true; |
| @@ -1351,8 +1333,8 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1351 | if ((stat_config.aggr_mode == AGGR_THREAD) && !target__has_task(&target)) { | 1333 | if ((stat_config.aggr_mode == AGGR_THREAD) && !target__has_task(&target)) { |
| 1352 | fprintf(stderr, "The --per-thread option is only available " | 1334 | fprintf(stderr, "The --per-thread option is only available " |
| 1353 | "when monitoring via -p -t options.\n"); | 1335 | "when monitoring via -p -t options.\n"); |
| 1354 | parse_options_usage(NULL, options, "p", 1); | 1336 | parse_options_usage(NULL, stat_options, "p", 1); |
| 1355 | parse_options_usage(NULL, options, "t", 1); | 1337 | parse_options_usage(NULL, stat_options, "t", 1); |
| 1356 | goto out; | 1338 | goto out; |
| 1357 | } | 1339 | } |
| 1358 | 1340 | ||
| @@ -1366,9 +1348,9 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1366 | fprintf(stderr, "both cgroup and no-aggregation " | 1348 | fprintf(stderr, "both cgroup and no-aggregation " |
| 1367 | "modes only available in system-wide mode\n"); | 1349 | "modes only available in system-wide mode\n"); |
| 1368 | 1350 | ||
| 1369 | parse_options_usage(stat_usage, options, "G", 1); | 1351 | parse_options_usage(stat_usage, stat_options, "G", 1); |
| 1370 | parse_options_usage(NULL, options, "A", 1); | 1352 | parse_options_usage(NULL, stat_options, "A", 1); |
| 1371 | parse_options_usage(NULL, options, "a", 1); | 1353 | parse_options_usage(NULL, stat_options, "a", 1); |
| 1372 | goto out; | 1354 | goto out; |
| 1373 | } | 1355 | } |
| 1374 | 1356 | ||
| @@ -1380,12 +1362,12 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1380 | if (perf_evlist__create_maps(evsel_list, &target) < 0) { | 1362 | if (perf_evlist__create_maps(evsel_list, &target) < 0) { |
| 1381 | if (target__has_task(&target)) { | 1363 | if (target__has_task(&target)) { |
| 1382 | pr_err("Problems finding threads of monitor\n"); | 1364 | pr_err("Problems finding threads of monitor\n"); |
| 1383 | parse_options_usage(stat_usage, options, "p", 1); | 1365 | parse_options_usage(stat_usage, stat_options, "p", 1); |
| 1384 | parse_options_usage(NULL, options, "t", 1); | 1366 | parse_options_usage(NULL, stat_options, "t", 1); |
| 1385 | } else if (target__has_cpu(&target)) { | 1367 | } else if (target__has_cpu(&target)) { |
| 1386 | perror("failed to parse CPUs map"); | 1368 | perror("failed to parse CPUs map"); |
| 1387 | parse_options_usage(stat_usage, options, "C", 1); | 1369 | parse_options_usage(stat_usage, stat_options, "C", 1); |
| 1388 | parse_options_usage(NULL, options, "a", 1); | 1370 | parse_options_usage(NULL, stat_options, "a", 1); |
| 1389 | } | 1371 | } |
| 1390 | goto out; | 1372 | goto out; |
| 1391 | } | 1373 | } |
| @@ -1400,7 +1382,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1400 | if (interval && interval < 100) { | 1382 | if (interval && interval < 100) { |
| 1401 | if (interval < 10) { | 1383 | if (interval < 10) { |
| 1402 | pr_err("print interval must be >= 10ms\n"); | 1384 | pr_err("print interval must be >= 10ms\n"); |
| 1403 | parse_options_usage(stat_usage, options, "I", 1); | 1385 | parse_options_usage(stat_usage, stat_options, "I", 1); |
| 1404 | goto out; | 1386 | goto out; |
| 1405 | } else | 1387 | } else |
| 1406 | pr_warning("print interval < 100ms. " | 1388 | pr_warning("print interval < 100ms. " |
diff --git a/tools/perf/tests/.gitignore b/tools/perf/tests/.gitignore new file mode 100644 index 000000000000..489fc9ffbcb0 --- /dev/null +++ b/tools/perf/tests/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | llvm-src-base.c | ||
| 2 | llvm-src-kbuild.c | ||
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index 50de2253cff6..f41ebf8849fe 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build | |||
| @@ -31,9 +31,24 @@ perf-y += sample-parsing.o | |||
| 31 | perf-y += parse-no-sample-id-all.o | 31 | perf-y += parse-no-sample-id-all.o |
| 32 | perf-y += kmod-path.o | 32 | perf-y += kmod-path.o |
| 33 | perf-y += thread-map.o | 33 | perf-y += thread-map.o |
| 34 | perf-y += llvm.o | 34 | perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o |
| 35 | perf-y += bpf.o | ||
| 35 | perf-y += topology.o | 36 | perf-y += topology.o |
| 36 | 37 | ||
| 38 | $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c | ||
| 39 | $(call rule_mkdir) | ||
| 40 | $(Q)echo '#include <tests/llvm.h>' > $@ | ||
| 41 | $(Q)echo 'const char test_llvm__bpf_base_prog[] =' >> $@ | ||
| 42 | $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ | ||
| 43 | $(Q)echo ';' >> $@ | ||
| 44 | |||
| 45 | $(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c | ||
| 46 | $(call rule_mkdir) | ||
| 47 | $(Q)echo '#include <tests/llvm.h>' > $@ | ||
| 48 | $(Q)echo 'const char test_llvm__bpf_test_kbuild_prog[] =' >> $@ | ||
| 49 | $(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@ | ||
| 50 | $(Q)echo ';' >> $@ | ||
| 51 | |||
| 37 | ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64)) | 52 | ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64)) |
| 38 | perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o | 53 | perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o |
| 39 | endif | 54 | endif |
diff --git a/tools/perf/tests/attr.c b/tools/perf/tests/attr.c index 2dfc9ad0e6f2..638875a0960a 100644 --- a/tools/perf/tests/attr.c +++ b/tools/perf/tests/attr.c | |||
| @@ -171,6 +171,5 @@ int test__attr(void) | |||
| 171 | !lstat(path_perf, &st)) | 171 | !lstat(path_perf, &st)) |
| 172 | return run_dir(path_dir, path_perf); | 172 | return run_dir(path_dir, path_perf); |
| 173 | 173 | ||
| 174 | fprintf(stderr, " (omitted)"); | 174 | return TEST_SKIP; |
| 175 | return 0; | ||
| 176 | } | 175 | } |
diff --git a/tools/perf/tests/bpf-script-example.c b/tools/perf/tests/bpf-script-example.c index 410a70b93b93..0ec9c2c03164 100644 --- a/tools/perf/tests/bpf-script-example.c +++ b/tools/perf/tests/bpf-script-example.c | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | /* | ||
| 2 | * bpf-script-example.c | ||
| 3 | * Test basic LLVM building | ||
| 4 | */ | ||
| 1 | #ifndef LINUX_VERSION_CODE | 5 | #ifndef LINUX_VERSION_CODE |
| 2 | # error Need LINUX_VERSION_CODE | 6 | # error Need LINUX_VERSION_CODE |
| 3 | # error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' | 7 | # error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' |
diff --git a/tools/perf/tests/bpf-script-test-kbuild.c b/tools/perf/tests/bpf-script-test-kbuild.c new file mode 100644 index 000000000000..3626924740d8 --- /dev/null +++ b/tools/perf/tests/bpf-script-test-kbuild.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* | ||
| 2 | * bpf-script-test-kbuild.c | ||
| 3 | * Test include from kernel header | ||
| 4 | */ | ||
| 5 | #ifndef LINUX_VERSION_CODE | ||
| 6 | # error Need LINUX_VERSION_CODE | ||
| 7 | # error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig' | ||
| 8 | #endif | ||
| 9 | #define SEC(NAME) __attribute__((section(NAME), used)) | ||
| 10 | |||
| 11 | #include <uapi/linux/fs.h> | ||
| 12 | #include <uapi/asm/ptrace.h> | ||
| 13 | |||
| 14 | SEC("func=vfs_llseek") | ||
| 15 | int bpf_func__vfs_llseek(void *ctx) | ||
| 16 | { | ||
| 17 | return 0; | ||
| 18 | } | ||
| 19 | |||
| 20 | char _license[] SEC("license") = "GPL"; | ||
| 21 | int _version SEC("version") = LINUX_VERSION_CODE; | ||
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c new file mode 100644 index 000000000000..ec16f7812c8b --- /dev/null +++ b/tools/perf/tests/bpf.c | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <sys/epoll.h> | ||
| 3 | #include <util/bpf-loader.h> | ||
| 4 | #include <util/evlist.h> | ||
| 5 | #include "tests.h" | ||
| 6 | #include "llvm.h" | ||
| 7 | #include "debug.h" | ||
| 8 | #define NR_ITERS 111 | ||
| 9 | |||
| 10 | #ifdef HAVE_LIBBPF_SUPPORT | ||
| 11 | |||
| 12 | static int epoll_pwait_loop(void) | ||
| 13 | { | ||
| 14 | int i; | ||
| 15 | |||
| 16 | /* Should fail NR_ITERS times */ | ||
| 17 | for (i = 0; i < NR_ITERS; i++) | ||
| 18 | epoll_pwait(-(i + 1), NULL, 0, 0, NULL); | ||
| 19 | return 0; | ||
| 20 | } | ||
| 21 | |||
| 22 | static struct { | ||
| 23 | enum test_llvm__testcase prog_id; | ||
| 24 | const char *desc; | ||
| 25 | const char *name; | ||
| 26 | const char *msg_compile_fail; | ||
| 27 | const char *msg_load_fail; | ||
| 28 | int (*target_func)(void); | ||
| 29 | int expect_result; | ||
| 30 | } bpf_testcase_table[] = { | ||
| 31 | { | ||
| 32 | LLVM_TESTCASE_BASE, | ||
| 33 | "Test basic BPF filtering", | ||
| 34 | "[basic_bpf_test]", | ||
| 35 | "fix 'perf test LLVM' first", | ||
| 36 | "load bpf object failed", | ||
| 37 | &epoll_pwait_loop, | ||
| 38 | (NR_ITERS + 1) / 2, | ||
| 39 | }, | ||
| 40 | }; | ||
| 41 | |||
| 42 | static int do_test(struct bpf_object *obj, int (*func)(void), | ||
| 43 | int expect) | ||
| 44 | { | ||
| 45 | struct record_opts opts = { | ||
| 46 | .target = { | ||
| 47 | .uid = UINT_MAX, | ||
| 48 | .uses_mmap = true, | ||
| 49 | }, | ||
| 50 | .freq = 0, | ||
| 51 | .mmap_pages = 256, | ||
| 52 | .default_interval = 1, | ||
| 53 | }; | ||
| 54 | |||
| 55 | char pid[16]; | ||
| 56 | char sbuf[STRERR_BUFSIZE]; | ||
| 57 | struct perf_evlist *evlist; | ||
| 58 | int i, ret = TEST_FAIL, err = 0, count = 0; | ||
| 59 | |||
| 60 | struct parse_events_evlist parse_evlist; | ||
| 61 | struct parse_events_error parse_error; | ||
| 62 | |||
| 63 | bzero(&parse_error, sizeof(parse_error)); | ||
| 64 | bzero(&parse_evlist, sizeof(parse_evlist)); | ||
| 65 | parse_evlist.error = &parse_error; | ||
| 66 | INIT_LIST_HEAD(&parse_evlist.list); | ||
| 67 | |||
| 68 | err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj); | ||
| 69 | if (err || list_empty(&parse_evlist.list)) { | ||
| 70 | pr_debug("Failed to add events selected by BPF\n"); | ||
| 71 | if (!err) | ||
| 72 | return TEST_FAIL; | ||
| 73 | } | ||
| 74 | |||
| 75 | snprintf(pid, sizeof(pid), "%d", getpid()); | ||
| 76 | pid[sizeof(pid) - 1] = '\0'; | ||
| 77 | opts.target.tid = opts.target.pid = pid; | ||
| 78 | |||
| 79 | /* Instead of perf_evlist__new_default, don't add default events */ | ||
| 80 | evlist = perf_evlist__new(); | ||
| 81 | if (!evlist) { | ||
| 82 | pr_debug("No ehough memory to create evlist\n"); | ||
| 83 | return TEST_FAIL; | ||
| 84 | } | ||
| 85 | |||
| 86 | err = perf_evlist__create_maps(evlist, &opts.target); | ||
| 87 | if (err < 0) { | ||
| 88 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
| 89 | goto out_delete_evlist; | ||
| 90 | } | ||
| 91 | |||
| 92 | perf_evlist__splice_list_tail(evlist, &parse_evlist.list); | ||
| 93 | evlist->nr_groups = parse_evlist.nr_groups; | ||
| 94 | |||
| 95 | perf_evlist__config(evlist, &opts); | ||
| 96 | |||
| 97 | err = perf_evlist__open(evlist); | ||
| 98 | if (err < 0) { | ||
| 99 | pr_debug("perf_evlist__open: %s\n", | ||
| 100 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 101 | goto out_delete_evlist; | ||
| 102 | } | ||
| 103 | |||
| 104 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | ||
| 105 | if (err < 0) { | ||
| 106 | pr_debug("perf_evlist__mmap: %s\n", | ||
| 107 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
| 108 | goto out_delete_evlist; | ||
| 109 | } | ||
| 110 | |||
| 111 | perf_evlist__enable(evlist); | ||
| 112 | (*func)(); | ||
| 113 | perf_evlist__disable(evlist); | ||
| 114 | |||
| 115 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
| 116 | union perf_event *event; | ||
| 117 | |||
| 118 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
| 119 | const u32 type = event->header.type; | ||
| 120 | |||
| 121 | if (type == PERF_RECORD_SAMPLE) | ||
| 122 | count ++; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | if (count != expect) | ||
| 127 | pr_debug("BPF filter result incorrect\n"); | ||
| 128 | |||
| 129 | ret = TEST_OK; | ||
| 130 | |||
| 131 | out_delete_evlist: | ||
| 132 | perf_evlist__delete(evlist); | ||
| 133 | return ret; | ||
| 134 | } | ||
| 135 | |||
| 136 | static struct bpf_object * | ||
| 137 | prepare_bpf(void *obj_buf, size_t obj_buf_sz, const char *name) | ||
| 138 | { | ||
| 139 | struct bpf_object *obj; | ||
| 140 | |||
| 141 | obj = bpf__prepare_load_buffer(obj_buf, obj_buf_sz, name); | ||
| 142 | if (IS_ERR(obj)) { | ||
| 143 | pr_debug("Compile BPF program failed.\n"); | ||
| 144 | return NULL; | ||
| 145 | } | ||
| 146 | return obj; | ||
| 147 | } | ||
| 148 | |||
| 149 | static int __test__bpf(int index) | ||
| 150 | { | ||
| 151 | int ret; | ||
| 152 | void *obj_buf; | ||
| 153 | size_t obj_buf_sz; | ||
| 154 | struct bpf_object *obj; | ||
| 155 | |||
| 156 | ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, | ||
| 157 | bpf_testcase_table[index].prog_id, | ||
| 158 | true); | ||
| 159 | if (ret != TEST_OK || !obj_buf || !obj_buf_sz) { | ||
| 160 | pr_debug("Unable to get BPF object, %s\n", | ||
| 161 | bpf_testcase_table[index].msg_compile_fail); | ||
| 162 | if (index == 0) | ||
| 163 | return TEST_SKIP; | ||
| 164 | else | ||
| 165 | return TEST_FAIL; | ||
| 166 | } | ||
| 167 | |||
| 168 | obj = prepare_bpf(obj_buf, obj_buf_sz, | ||
| 169 | bpf_testcase_table[index].name); | ||
| 170 | if (!obj) { | ||
| 171 | ret = TEST_FAIL; | ||
| 172 | goto out; | ||
| 173 | } | ||
| 174 | |||
| 175 | ret = do_test(obj, | ||
| 176 | bpf_testcase_table[index].target_func, | ||
| 177 | bpf_testcase_table[index].expect_result); | ||
| 178 | out: | ||
| 179 | bpf__clear(); | ||
| 180 | return ret; | ||
| 181 | } | ||
| 182 | |||
| 183 | int test__bpf(void) | ||
| 184 | { | ||
| 185 | unsigned int i; | ||
| 186 | int err; | ||
| 187 | |||
| 188 | if (geteuid() != 0) { | ||
| 189 | pr_debug("Only root can run BPF test\n"); | ||
| 190 | return TEST_SKIP; | ||
| 191 | } | ||
| 192 | |||
| 193 | for (i = 0; i < ARRAY_SIZE(bpf_testcase_table); i++) { | ||
| 194 | err = __test__bpf(i); | ||
| 195 | |||
| 196 | if (err != TEST_OK) | ||
| 197 | return err; | ||
| 198 | } | ||
| 199 | |||
| 200 | return TEST_OK; | ||
| 201 | } | ||
| 202 | |||
| 203 | #else | ||
| 204 | int test__bpf(void) | ||
| 205 | { | ||
| 206 | pr_debug("Skip BPF test because BPF support is not compiled\n"); | ||
| 207 | return TEST_SKIP; | ||
| 208 | } | ||
| 209 | #endif | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 66f72d3d6677..80c442eab767 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
| @@ -166,6 +166,10 @@ static struct test generic_tests[] = { | |||
| 166 | .func = test_session_topology, | 166 | .func = test_session_topology, |
| 167 | }, | 167 | }, |
| 168 | { | 168 | { |
| 169 | .desc = "Test BPF filter", | ||
| 170 | .func = test__bpf, | ||
| 171 | }, | ||
| 172 | { | ||
| 169 | .func = NULL, | 173 | .func = NULL, |
| 170 | }, | 174 | }, |
| 171 | }; | 175 | }; |
| @@ -192,7 +196,7 @@ static bool perf_test__matches(struct test *test, int curr, int argc, const char | |||
| 192 | continue; | 196 | continue; |
| 193 | } | 197 | } |
| 194 | 198 | ||
| 195 | if (strstr(test->desc, argv[i])) | 199 | if (strcasestr(test->desc, argv[i])) |
| 196 | return true; | 200 | return true; |
| 197 | } | 201 | } |
| 198 | 202 | ||
diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 49b1959dda41..a767a6400c5c 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c | |||
| @@ -613,16 +613,16 @@ int test__code_reading(void) | |||
| 613 | case TEST_CODE_READING_OK: | 613 | case TEST_CODE_READING_OK: |
| 614 | return 0; | 614 | return 0; |
| 615 | case TEST_CODE_READING_NO_VMLINUX: | 615 | case TEST_CODE_READING_NO_VMLINUX: |
| 616 | fprintf(stderr, " (no vmlinux)"); | 616 | pr_debug("no vmlinux\n"); |
| 617 | return 0; | 617 | return 0; |
| 618 | case TEST_CODE_READING_NO_KCORE: | 618 | case TEST_CODE_READING_NO_KCORE: |
| 619 | fprintf(stderr, " (no kcore)"); | 619 | pr_debug("no kcore\n"); |
| 620 | return 0; | 620 | return 0; |
| 621 | case TEST_CODE_READING_NO_ACCESS: | 621 | case TEST_CODE_READING_NO_ACCESS: |
| 622 | fprintf(stderr, " (no access)"); | 622 | pr_debug("no access\n"); |
| 623 | return 0; | 623 | return 0; |
| 624 | case TEST_CODE_READING_NO_KERNEL_OBJ: | 624 | case TEST_CODE_READING_NO_KERNEL_OBJ: |
| 625 | fprintf(stderr, " (no kernel obj)"); | 625 | pr_debug("no kernel obj\n"); |
| 626 | return 0; | 626 | return 0; |
| 627 | default: | 627 | default: |
| 628 | return -1; | 628 | return -1; |
diff --git a/tools/perf/tests/keep-tracking.c b/tools/perf/tests/keep-tracking.c index 4d4b9837b630..a2e2269aa093 100644 --- a/tools/perf/tests/keep-tracking.c +++ b/tools/perf/tests/keep-tracking.c | |||
| @@ -90,8 +90,8 @@ int test__keep_tracking(void) | |||
| 90 | evsel->attr.enable_on_exec = 0; | 90 | evsel->attr.enable_on_exec = 0; |
| 91 | 91 | ||
| 92 | if (perf_evlist__open(evlist) < 0) { | 92 | if (perf_evlist__open(evlist) < 0) { |
| 93 | fprintf(stderr, " (not supported)"); | 93 | pr_debug("Unable to open dummy and cycles event\n"); |
| 94 | err = 0; | 94 | err = TEST_SKIP; |
| 95 | goto out_err; | 95 | goto out_err; |
| 96 | } | 96 | } |
| 97 | 97 | ||
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c index 52d55971f66f..bc4cf507cde5 100644 --- a/tools/perf/tests/llvm.c +++ b/tools/perf/tests/llvm.c | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | #include <bpf/libbpf.h> | 2 | #include <bpf/libbpf.h> |
| 3 | #include <util/llvm-utils.h> | 3 | #include <util/llvm-utils.h> |
| 4 | #include <util/cache.h> | 4 | #include <util/cache.h> |
| 5 | #include "llvm.h" | ||
| 5 | #include "tests.h" | 6 | #include "tests.h" |
| 6 | #include "debug.h" | 7 | #include "debug.h" |
| 7 | 8 | ||
| @@ -11,42 +12,58 @@ static int perf_config_cb(const char *var, const char *val, | |||
| 11 | return perf_default_config(var, val, arg); | 12 | return perf_default_config(var, val, arg); |
| 12 | } | 13 | } |
| 13 | 14 | ||
| 14 | /* | ||
| 15 | * Randomly give it a "version" section since we don't really load it | ||
| 16 | * into kernel | ||
| 17 | */ | ||
| 18 | static const char test_bpf_prog[] = | ||
| 19 | "__attribute__((section(\"do_fork\"), used)) " | ||
| 20 | "int fork(void *ctx) {return 0;} " | ||
| 21 | "char _license[] __attribute__((section(\"license\"), used)) = \"GPL\";" | ||
| 22 | "int _version __attribute__((section(\"version\"), used)) = 0x40100;"; | ||
| 23 | |||
| 24 | #ifdef HAVE_LIBBPF_SUPPORT | 15 | #ifdef HAVE_LIBBPF_SUPPORT |
| 25 | static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) | 16 | static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz) |
| 26 | { | 17 | { |
| 27 | struct bpf_object *obj; | 18 | struct bpf_object *obj; |
| 28 | 19 | ||
| 29 | obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL); | 20 | obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL); |
| 30 | if (!obj) | 21 | if (IS_ERR(obj)) |
| 31 | return -1; | 22 | return TEST_FAIL; |
| 32 | bpf_object__close(obj); | 23 | bpf_object__close(obj); |
| 33 | return 0; | 24 | return TEST_OK; |
| 34 | } | 25 | } |
| 35 | #else | 26 | #else |
| 36 | static int test__bpf_parsing(void *obj_buf __maybe_unused, | 27 | static int test__bpf_parsing(void *obj_buf __maybe_unused, |
| 37 | size_t obj_buf_sz __maybe_unused) | 28 | size_t obj_buf_sz __maybe_unused) |
| 38 | { | 29 | { |
| 39 | fprintf(stderr, " (skip bpf parsing)"); | 30 | pr_debug("Skip bpf parsing\n"); |
| 40 | return 0; | 31 | return TEST_OK; |
| 41 | } | 32 | } |
| 42 | #endif | 33 | #endif |
| 43 | 34 | ||
| 44 | int test__llvm(void) | 35 | static struct { |
| 36 | const char *source; | ||
| 37 | const char *desc; | ||
| 38 | } bpf_source_table[__LLVM_TESTCASE_MAX] = { | ||
| 39 | [LLVM_TESTCASE_BASE] = { | ||
| 40 | .source = test_llvm__bpf_base_prog, | ||
| 41 | .desc = "Basic BPF llvm compiling test", | ||
| 42 | }, | ||
| 43 | [LLVM_TESTCASE_KBUILD] = { | ||
| 44 | .source = test_llvm__bpf_test_kbuild_prog, | ||
| 45 | .desc = "Test kbuild searching", | ||
| 46 | }, | ||
| 47 | }; | ||
| 48 | |||
| 49 | |||
| 50 | int | ||
| 51 | test_llvm__fetch_bpf_obj(void **p_obj_buf, | ||
| 52 | size_t *p_obj_buf_sz, | ||
| 53 | enum test_llvm__testcase index, | ||
| 54 | bool force) | ||
| 45 | { | 55 | { |
| 46 | char *tmpl_new, *clang_opt_new; | 56 | const char *source; |
| 47 | void *obj_buf; | 57 | const char *desc; |
| 48 | size_t obj_buf_sz; | 58 | const char *tmpl_old, *clang_opt_old; |
| 49 | int err, old_verbose; | 59 | char *tmpl_new = NULL, *clang_opt_new = NULL; |
| 60 | int err, old_verbose, ret = TEST_FAIL; | ||
| 61 | |||
| 62 | if (index >= __LLVM_TESTCASE_MAX) | ||
| 63 | return TEST_FAIL; | ||
| 64 | |||
| 65 | source = bpf_source_table[index].source; | ||
| 66 | desc = bpf_source_table[index].desc; | ||
| 50 | 67 | ||
| 51 | perf_config(perf_config_cb, NULL); | 68 | perf_config(perf_config_cb, NULL); |
| 52 | 69 | ||
| @@ -54,45 +71,100 @@ int test__llvm(void) | |||
| 54 | * Skip this test if user's .perfconfig doesn't set [llvm] section | 71 | * Skip this test if user's .perfconfig doesn't set [llvm] section |
| 55 | * and clang is not found in $PATH, and this is not perf test -v | 72 | * and clang is not found in $PATH, and this is not perf test -v |
| 56 | */ | 73 | */ |
| 57 | if (verbose == 0 && !llvm_param.user_set_param && llvm__search_clang()) { | 74 | if (!force && (verbose == 0 && |
| 58 | fprintf(stderr, " (no clang, try 'perf test -v LLVM')"); | 75 | !llvm_param.user_set_param && |
| 76 | llvm__search_clang())) { | ||
| 77 | pr_debug("No clang and no verbosive, skip this test\n"); | ||
| 59 | return TEST_SKIP; | 78 | return TEST_SKIP; |
| 60 | } | 79 | } |
| 61 | 80 | ||
| 62 | old_verbose = verbose; | ||
| 63 | /* | 81 | /* |
| 64 | * llvm is verbosity when error. Suppress all error output if | 82 | * llvm is verbosity when error. Suppress all error output if |
| 65 | * not 'perf test -v'. | 83 | * not 'perf test -v'. |
| 66 | */ | 84 | */ |
| 85 | old_verbose = verbose; | ||
| 67 | if (verbose == 0) | 86 | if (verbose == 0) |
| 68 | verbose = -1; | 87 | verbose = -1; |
| 69 | 88 | ||
| 89 | *p_obj_buf = NULL; | ||
| 90 | *p_obj_buf_sz = 0; | ||
| 91 | |||
| 70 | if (!llvm_param.clang_bpf_cmd_template) | 92 | if (!llvm_param.clang_bpf_cmd_template) |
| 71 | return -1; | 93 | goto out; |
| 72 | 94 | ||
| 73 | if (!llvm_param.clang_opt) | 95 | if (!llvm_param.clang_opt) |
| 74 | llvm_param.clang_opt = strdup(""); | 96 | llvm_param.clang_opt = strdup(""); |
| 75 | 97 | ||
| 76 | err = asprintf(&tmpl_new, "echo '%s' | %s", test_bpf_prog, | 98 | err = asprintf(&tmpl_new, "echo '%s' | %s%s", source, |
| 77 | llvm_param.clang_bpf_cmd_template); | 99 | llvm_param.clang_bpf_cmd_template, |
| 100 | old_verbose ? "" : " 2>/dev/null"); | ||
| 78 | if (err < 0) | 101 | if (err < 0) |
| 79 | return -1; | 102 | goto out; |
| 80 | err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt); | 103 | err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt); |
| 81 | if (err < 0) | 104 | if (err < 0) |
| 82 | return -1; | 105 | goto out; |
| 83 | 106 | ||
| 107 | tmpl_old = llvm_param.clang_bpf_cmd_template; | ||
| 84 | llvm_param.clang_bpf_cmd_template = tmpl_new; | 108 | llvm_param.clang_bpf_cmd_template = tmpl_new; |
| 109 | clang_opt_old = llvm_param.clang_opt; | ||
| 85 | llvm_param.clang_opt = clang_opt_new; | 110 | llvm_param.clang_opt = clang_opt_new; |
| 86 | err = llvm__compile_bpf("-", &obj_buf, &obj_buf_sz); | 111 | |
| 112 | err = llvm__compile_bpf("-", p_obj_buf, p_obj_buf_sz); | ||
| 113 | |||
| 114 | llvm_param.clang_bpf_cmd_template = tmpl_old; | ||
| 115 | llvm_param.clang_opt = clang_opt_old; | ||
| 87 | 116 | ||
| 88 | verbose = old_verbose; | 117 | verbose = old_verbose; |
| 89 | if (err) { | 118 | if (err) |
| 90 | if (!verbose) | 119 | goto out; |
| 91 | fprintf(stderr, " (use -v to see error message)"); | 120 | |
| 92 | return -1; | 121 | ret = TEST_OK; |
| 93 | } | 122 | out: |
| 123 | free(tmpl_new); | ||
| 124 | free(clang_opt_new); | ||
| 125 | if (ret != TEST_OK) | ||
| 126 | pr_debug("Failed to compile test case: '%s'\n", desc); | ||
| 127 | return ret; | ||
| 128 | } | ||
| 129 | |||
| 130 | int test__llvm(void) | ||
| 131 | { | ||
| 132 | enum test_llvm__testcase i; | ||
| 133 | |||
| 134 | for (i = 0; i < __LLVM_TESTCASE_MAX; i++) { | ||
| 135 | int ret; | ||
| 136 | void *obj_buf = NULL; | ||
| 137 | size_t obj_buf_sz = 0; | ||
| 138 | |||
| 139 | ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, | ||
| 140 | i, false); | ||
| 94 | 141 | ||
| 95 | err = test__bpf_parsing(obj_buf, obj_buf_sz); | 142 | if (ret == TEST_OK) { |
| 96 | free(obj_buf); | 143 | ret = test__bpf_parsing(obj_buf, obj_buf_sz); |
| 97 | return err; | 144 | if (ret != TEST_OK) |
| 145 | pr_debug("Failed to parse test case '%s'\n", | ||
| 146 | bpf_source_table[i].desc); | ||
| 147 | } | ||
| 148 | free(obj_buf); | ||
| 149 | |||
| 150 | switch (ret) { | ||
| 151 | case TEST_SKIP: | ||
| 152 | return TEST_SKIP; | ||
| 153 | case TEST_OK: | ||
| 154 | break; | ||
| 155 | default: | ||
| 156 | /* | ||
| 157 | * Test 0 is the basic LLVM test. If test 0 | ||
| 158 | * fail, the basic LLVM support not functional | ||
| 159 | * so the whole test should fail. If other test | ||
| 160 | * case fail, it can be fixed by adjusting | ||
| 161 | * config so don't report error. | ||
| 162 | */ | ||
| 163 | if (i == 0) | ||
| 164 | return TEST_FAIL; | ||
| 165 | else | ||
| 166 | return TEST_SKIP; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | return TEST_OK; | ||
| 98 | } | 170 | } |
diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h new file mode 100644 index 000000000000..d91d8f44efee --- /dev/null +++ b/tools/perf/tests/llvm.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #ifndef PERF_TEST_LLVM_H | ||
| 2 | #define PERF_TEST_LLVM_H | ||
| 3 | |||
| 4 | #include <stddef.h> /* for size_t */ | ||
| 5 | #include <stdbool.h> /* for bool */ | ||
| 6 | |||
| 7 | extern const char test_llvm__bpf_base_prog[]; | ||
| 8 | extern const char test_llvm__bpf_test_kbuild_prog[]; | ||
| 9 | |||
| 10 | enum test_llvm__testcase { | ||
| 11 | LLVM_TESTCASE_BASE, | ||
| 12 | LLVM_TESTCASE_KBUILD, | ||
| 13 | __LLVM_TESTCASE_MAX, | ||
| 14 | }; | ||
| 15 | |||
| 16 | int test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz, | ||
| 17 | enum test_llvm__testcase index, bool force); | ||
| 18 | #endif | ||
diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 2cbd0c6901e3..8ea3dffc5065 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make | |||
| @@ -221,6 +221,11 @@ test_O = $(if $(test_$1),$(test_$1),$(test_default_O)) | |||
| 221 | 221 | ||
| 222 | all: | 222 | all: |
| 223 | 223 | ||
| 224 | ifdef SHUF | ||
| 225 | run := $(shell shuf -e $(run)) | ||
| 226 | run_O := $(shell shuf -e $(run_O)) | ||
| 227 | endif | ||
| 228 | |||
| 224 | ifdef DEBUG | 229 | ifdef DEBUG |
| 225 | d := $(info run $(run)) | 230 | d := $(info run $(run)) |
| 226 | d := $(info run_O $(run_O)) | 231 | d := $(info run_O $(run_O)) |
diff --git a/tools/perf/tests/switch-tracking.c b/tools/perf/tests/switch-tracking.c index e698742d4fec..a02af503100c 100644 --- a/tools/perf/tests/switch-tracking.c +++ b/tools/perf/tests/switch-tracking.c | |||
| @@ -366,7 +366,7 @@ int test__switch_tracking(void) | |||
| 366 | 366 | ||
| 367 | /* Third event */ | 367 | /* Third event */ |
| 368 | if (!perf_evlist__can_select_event(evlist, sched_switch)) { | 368 | if (!perf_evlist__can_select_event(evlist, sched_switch)) { |
| 369 | fprintf(stderr, " (no sched_switch)"); | 369 | pr_debug("No sched_switch\n"); |
| 370 | err = 0; | 370 | err = 0; |
| 371 | goto out; | 371 | goto out; |
| 372 | } | 372 | } |
| @@ -442,7 +442,7 @@ int test__switch_tracking(void) | |||
| 442 | } | 442 | } |
| 443 | 443 | ||
| 444 | if (perf_evlist__open(evlist) < 0) { | 444 | if (perf_evlist__open(evlist) < 0) { |
| 445 | fprintf(stderr, " (not supported)"); | 445 | pr_debug("Not supported\n"); |
| 446 | err = 0; | 446 | err = 0; |
| 447 | goto out; | 447 | goto out; |
| 448 | } | 448 | } |
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index c80486969f83..3c8734a3abbc 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
| @@ -66,6 +66,7 @@ int test__fdarray__add(void); | |||
| 66 | int test__kmod_path__parse(void); | 66 | int test__kmod_path__parse(void); |
| 67 | int test__thread_map(void); | 67 | int test__thread_map(void); |
| 68 | int test__llvm(void); | 68 | int test__llvm(void); |
| 69 | int test__bpf(void); | ||
| 69 | int test_session_topology(void); | 70 | int test_session_topology(void); |
| 70 | 71 | ||
| 71 | #if defined(__arm__) || defined(__aarch64__) | 72 | #if defined(__arm__) || defined(__aarch64__) |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 0fc8d7a2fea5..1dd1949b0e79 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
| @@ -1084,6 +1084,7 @@ int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize) | |||
| 1084 | struct kcore_extract kce; | 1084 | struct kcore_extract kce; |
| 1085 | bool delete_extract = false; | 1085 | bool delete_extract = false; |
| 1086 | int lineno = 0; | 1086 | int lineno = 0; |
| 1087 | int nline; | ||
| 1087 | 1088 | ||
| 1088 | if (filename) | 1089 | if (filename) |
| 1089 | symbol__join_symfs(symfs_filename, filename); | 1090 | symbol__join_symfs(symfs_filename, filename); |
| @@ -1179,6 +1180,9 @@ fallback: | |||
| 1179 | 1180 | ||
| 1180 | ret = decompress_to_file(m.ext, symfs_filename, fd); | 1181 | ret = decompress_to_file(m.ext, symfs_filename, fd); |
| 1181 | 1182 | ||
| 1183 | if (ret) | ||
| 1184 | pr_err("Cannot decompress %s %s\n", m.ext, symfs_filename); | ||
| 1185 | |||
| 1182 | free(m.ext); | 1186 | free(m.ext); |
| 1183 | close(fd); | 1187 | close(fd); |
| 1184 | 1188 | ||
| @@ -1204,13 +1208,25 @@ fallback: | |||
| 1204 | pr_debug("Executing: %s\n", command); | 1208 | pr_debug("Executing: %s\n", command); |
| 1205 | 1209 | ||
| 1206 | file = popen(command, "r"); | 1210 | file = popen(command, "r"); |
| 1207 | if (!file) | 1211 | if (!file) { |
| 1212 | pr_err("Failure running %s\n", command); | ||
| 1213 | /* | ||
| 1214 | * If we were using debug info should retry with | ||
| 1215 | * original binary. | ||
| 1216 | */ | ||
| 1208 | goto out_remove_tmp; | 1217 | goto out_remove_tmp; |
| 1218 | } | ||
| 1209 | 1219 | ||
| 1210 | while (!feof(file)) | 1220 | nline = 0; |
| 1221 | while (!feof(file)) { | ||
| 1211 | if (symbol__parse_objdump_line(sym, map, file, privsize, | 1222 | if (symbol__parse_objdump_line(sym, map, file, privsize, |
| 1212 | &lineno) < 0) | 1223 | &lineno) < 0) |
| 1213 | break; | 1224 | break; |
| 1225 | nline++; | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | if (nline == 0) | ||
| 1229 | pr_err("No output from %s\n", command); | ||
| 1214 | 1230 | ||
| 1215 | /* | 1231 | /* |
| 1216 | * kallsyms does not have symbol sizes so there may a nop at the end. | 1232 | * kallsyms does not have symbol sizes so there may a nop at the end. |
| @@ -1604,6 +1620,7 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, | |||
| 1604 | len = symbol__size(sym); | 1620 | len = symbol__size(sym); |
| 1605 | 1621 | ||
| 1606 | if (print_lines) { | 1622 | if (print_lines) { |
| 1623 | srcline_full_filename = full_paths; | ||
| 1607 | symbol__get_source_line(sym, map, evsel, &source_line, len); | 1624 | symbol__get_source_line(sym, map, evsel, &source_line, len); |
| 1608 | print_summary(&source_line, dso->long_name); | 1625 | print_summary(&source_line, dso->long_name); |
| 1609 | } | 1626 | } |
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index ba6f7526b282..4c50411371db 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c | |||
| @@ -26,18 +26,40 @@ static int libbpf_##name(const char *fmt, ...) \ | |||
| 26 | return ret; \ | 26 | return ret; \ |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | DEFINE_PRINT_FN(warning, 0) | 29 | DEFINE_PRINT_FN(warning, 1) |
| 30 | DEFINE_PRINT_FN(info, 0) | 30 | DEFINE_PRINT_FN(info, 1) |
| 31 | DEFINE_PRINT_FN(debug, 1) | 31 | DEFINE_PRINT_FN(debug, 1) |
| 32 | 32 | ||
| 33 | struct bpf_prog_priv { | 33 | struct bpf_prog_priv { |
| 34 | struct perf_probe_event pev; | 34 | struct perf_probe_event pev; |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | static bool libbpf_initialized; | ||
| 38 | |||
| 39 | struct bpf_object * | ||
| 40 | bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name) | ||
| 41 | { | ||
| 42 | struct bpf_object *obj; | ||
| 43 | |||
| 44 | if (!libbpf_initialized) { | ||
| 45 | libbpf_set_print(libbpf_warning, | ||
| 46 | libbpf_info, | ||
| 47 | libbpf_debug); | ||
| 48 | libbpf_initialized = true; | ||
| 49 | } | ||
| 50 | |||
| 51 | obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, name); | ||
| 52 | if (IS_ERR(obj)) { | ||
| 53 | pr_debug("bpf: failed to load buffer\n"); | ||
| 54 | return ERR_PTR(-EINVAL); | ||
| 55 | } | ||
| 56 | |||
| 57 | return obj; | ||
| 58 | } | ||
| 59 | |||
| 37 | struct bpf_object *bpf__prepare_load(const char *filename, bool source) | 60 | struct bpf_object *bpf__prepare_load(const char *filename, bool source) |
| 38 | { | 61 | { |
| 39 | struct bpf_object *obj; | 62 | struct bpf_object *obj; |
| 40 | static bool libbpf_initialized; | ||
| 41 | 63 | ||
| 42 | if (!libbpf_initialized) { | 64 | if (!libbpf_initialized) { |
| 43 | libbpf_set_print(libbpf_warning, | 65 | libbpf_set_print(libbpf_warning, |
| @@ -53,15 +75,15 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source) | |||
| 53 | 75 | ||
| 54 | err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz); | 76 | err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz); |
| 55 | if (err) | 77 | if (err) |
| 56 | return ERR_PTR(err); | 78 | return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE); |
| 57 | obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename); | 79 | obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename); |
| 58 | free(obj_buf); | 80 | free(obj_buf); |
| 59 | } else | 81 | } else |
| 60 | obj = bpf_object__open(filename); | 82 | obj = bpf_object__open(filename); |
| 61 | 83 | ||
| 62 | if (!obj) { | 84 | if (IS_ERR(obj)) { |
| 63 | pr_debug("bpf: failed to load %s\n", filename); | 85 | pr_debug("bpf: failed to load %s\n", filename); |
| 64 | return ERR_PTR(-EINVAL); | 86 | return obj; |
| 65 | } | 87 | } |
| 66 | 88 | ||
| 67 | return obj; | 89 | return obj; |
| @@ -96,9 +118,9 @@ config_bpf_program(struct bpf_program *prog) | |||
| 96 | int err; | 118 | int err; |
| 97 | 119 | ||
| 98 | config_str = bpf_program__title(prog, false); | 120 | config_str = bpf_program__title(prog, false); |
| 99 | if (!config_str) { | 121 | if (IS_ERR(config_str)) { |
| 100 | pr_debug("bpf: unable to get title for program\n"); | 122 | pr_debug("bpf: unable to get title for program\n"); |
| 101 | return -EINVAL; | 123 | return PTR_ERR(config_str); |
| 102 | } | 124 | } |
| 103 | 125 | ||
| 104 | priv = calloc(sizeof(*priv), 1); | 126 | priv = calloc(sizeof(*priv), 1); |
| @@ -113,14 +135,14 @@ config_bpf_program(struct bpf_program *prog) | |||
| 113 | if (err < 0) { | 135 | if (err < 0) { |
| 114 | pr_debug("bpf: '%s' is not a valid config string\n", | 136 | pr_debug("bpf: '%s' is not a valid config string\n", |
| 115 | config_str); | 137 | config_str); |
| 116 | err = -EINVAL; | 138 | err = -BPF_LOADER_ERRNO__CONFIG; |
| 117 | goto errout; | 139 | goto errout; |
| 118 | } | 140 | } |
| 119 | 141 | ||
| 120 | if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { | 142 | if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { |
| 121 | pr_debug("bpf: '%s': group for event is set and not '%s'.\n", | 143 | pr_debug("bpf: '%s': group for event is set and not '%s'.\n", |
| 122 | config_str, PERF_BPF_PROBE_GROUP); | 144 | config_str, PERF_BPF_PROBE_GROUP); |
| 123 | err = -EINVAL; | 145 | err = -BPF_LOADER_ERRNO__GROUP; |
| 124 | goto errout; | 146 | goto errout; |
| 125 | } else if (!pev->group) | 147 | } else if (!pev->group) |
| 126 | pev->group = strdup(PERF_BPF_PROBE_GROUP); | 148 | pev->group = strdup(PERF_BPF_PROBE_GROUP); |
| @@ -132,9 +154,9 @@ config_bpf_program(struct bpf_program *prog) | |||
| 132 | } | 154 | } |
| 133 | 155 | ||
| 134 | if (!pev->event) { | 156 | if (!pev->event) { |
| 135 | pr_debug("bpf: '%s': event name is missing\n", | 157 | pr_debug("bpf: '%s': event name is missing. Section name should be 'key=value'\n", |
| 136 | config_str); | 158 | config_str); |
| 137 | err = -EINVAL; | 159 | err = -BPF_LOADER_ERRNO__EVENTNAME; |
| 138 | goto errout; | 160 | goto errout; |
| 139 | } | 161 | } |
| 140 | pr_debug("bpf: config '%s' is ok\n", config_str); | 162 | pr_debug("bpf: config '%s' is ok\n", config_str); |
| @@ -285,7 +307,7 @@ int bpf__foreach_tev(struct bpf_object *obj, | |||
| 285 | (void **)&priv); | 307 | (void **)&priv); |
| 286 | if (err || !priv) { | 308 | if (err || !priv) { |
| 287 | pr_debug("bpf: failed to get private field\n"); | 309 | pr_debug("bpf: failed to get private field\n"); |
| 288 | return -EINVAL; | 310 | return -BPF_LOADER_ERRNO__INTERNAL; |
| 289 | } | 311 | } |
| 290 | 312 | ||
| 291 | pev = &priv->pev; | 313 | pev = &priv->pev; |
| @@ -308,13 +330,57 @@ int bpf__foreach_tev(struct bpf_object *obj, | |||
| 308 | return 0; | 330 | return 0; |
| 309 | } | 331 | } |
| 310 | 332 | ||
| 333 | #define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START) | ||
| 334 | #define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c) | ||
| 335 | #define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START) | ||
| 336 | |||
| 337 | static const char *bpf_loader_strerror_table[NR_ERRNO] = { | ||
| 338 | [ERRCODE_OFFSET(CONFIG)] = "Invalid config string", | ||
| 339 | [ERRCODE_OFFSET(GROUP)] = "Invalid group name", | ||
| 340 | [ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string", | ||
| 341 | [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error", | ||
| 342 | [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet", | ||
| 343 | }; | ||
| 344 | |||
| 345 | static int | ||
| 346 | bpf_loader_strerror(int err, char *buf, size_t size) | ||
| 347 | { | ||
| 348 | char sbuf[STRERR_BUFSIZE]; | ||
| 349 | const char *msg; | ||
| 350 | |||
| 351 | if (!buf || !size) | ||
| 352 | return -1; | ||
| 353 | |||
| 354 | err = err > 0 ? err : -err; | ||
| 355 | |||
| 356 | if (err >= __LIBBPF_ERRNO__START) | ||
| 357 | return libbpf_strerror(err, buf, size); | ||
| 358 | |||
| 359 | if (err >= __BPF_LOADER_ERRNO__START && err < __BPF_LOADER_ERRNO__END) { | ||
| 360 | msg = bpf_loader_strerror_table[ERRNO_OFFSET(err)]; | ||
| 361 | snprintf(buf, size, "%s", msg); | ||
| 362 | buf[size - 1] = '\0'; | ||
| 363 | return 0; | ||
| 364 | } | ||
| 365 | |||
| 366 | if (err >= __BPF_LOADER_ERRNO__END) | ||
| 367 | snprintf(buf, size, "Unknown bpf loader error %d", err); | ||
| 368 | else | ||
| 369 | snprintf(buf, size, "%s", | ||
| 370 | strerror_r(err, sbuf, sizeof(sbuf))); | ||
| 371 | |||
| 372 | buf[size - 1] = '\0'; | ||
| 373 | return -1; | ||
| 374 | } | ||
| 375 | |||
| 311 | #define bpf__strerror_head(err, buf, size) \ | 376 | #define bpf__strerror_head(err, buf, size) \ |
| 312 | char sbuf[STRERR_BUFSIZE], *emsg;\ | 377 | char sbuf[STRERR_BUFSIZE], *emsg;\ |
| 313 | if (!size)\ | 378 | if (!size)\ |
| 314 | return 0;\ | 379 | return 0;\ |
| 315 | if (err < 0)\ | 380 | if (err < 0)\ |
| 316 | err = -err;\ | 381 | err = -err;\ |
| 317 | emsg = strerror_r(err, sbuf, sizeof(sbuf));\ | 382 | bpf_loader_strerror(err, sbuf, sizeof(sbuf));\ |
| 383 | emsg = sbuf;\ | ||
| 318 | switch (err) {\ | 384 | switch (err) {\ |
| 319 | default:\ | 385 | default:\ |
| 320 | scnprintf(buf, size, "%s", emsg);\ | 386 | scnprintf(buf, size, "%s", emsg);\ |
| @@ -330,23 +396,62 @@ int bpf__foreach_tev(struct bpf_object *obj, | |||
| 330 | }\ | 396 | }\ |
| 331 | buf[size - 1] = '\0'; | 397 | buf[size - 1] = '\0'; |
| 332 | 398 | ||
| 399 | int bpf__strerror_prepare_load(const char *filename, bool source, | ||
| 400 | int err, char *buf, size_t size) | ||
| 401 | { | ||
| 402 | size_t n; | ||
| 403 | int ret; | ||
| 404 | |||
| 405 | n = snprintf(buf, size, "Failed to load %s%s: ", | ||
| 406 | filename, source ? " from source" : ""); | ||
| 407 | if (n >= size) { | ||
| 408 | buf[size - 1] = '\0'; | ||
| 409 | return 0; | ||
| 410 | } | ||
| 411 | buf += n; | ||
| 412 | size -= n; | ||
| 413 | |||
| 414 | ret = bpf_loader_strerror(err, buf, size); | ||
| 415 | buf[size - 1] = '\0'; | ||
| 416 | return ret; | ||
| 417 | } | ||
| 418 | |||
| 333 | int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, | 419 | int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, |
| 334 | int err, char *buf, size_t size) | 420 | int err, char *buf, size_t size) |
| 335 | { | 421 | { |
| 336 | bpf__strerror_head(err, buf, size); | 422 | bpf__strerror_head(err, buf, size); |
| 337 | bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'"); | 423 | bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'"); |
| 338 | bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0\n"); | 424 | bpf__strerror_entry(EACCES, "You need to be root"); |
| 339 | bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file\n"); | 425 | bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0"); |
| 426 | bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file"); | ||
| 340 | bpf__strerror_end(buf, size); | 427 | bpf__strerror_end(buf, size); |
| 341 | return 0; | 428 | return 0; |
| 342 | } | 429 | } |
| 343 | 430 | ||
| 344 | int bpf__strerror_load(struct bpf_object *obj __maybe_unused, | 431 | int bpf__strerror_load(struct bpf_object *obj, |
| 345 | int err, char *buf, size_t size) | 432 | int err, char *buf, size_t size) |
| 346 | { | 433 | { |
| 347 | bpf__strerror_head(err, buf, size); | 434 | bpf__strerror_head(err, buf, size); |
| 348 | bpf__strerror_entry(EINVAL, "%s: Are you root and runing a CONFIG_BPF_SYSCALL kernel?", | 435 | case LIBBPF_ERRNO__KVER: { |
| 349 | emsg) | 436 | unsigned int obj_kver = bpf_object__get_kversion(obj); |
| 437 | unsigned int real_kver; | ||
| 438 | |||
| 439 | if (fetch_kernel_version(&real_kver, NULL, 0)) { | ||
| 440 | scnprintf(buf, size, "Unable to fetch kernel version"); | ||
| 441 | break; | ||
| 442 | } | ||
| 443 | |||
| 444 | if (obj_kver != real_kver) { | ||
| 445 | scnprintf(buf, size, | ||
| 446 | "'version' ("KVER_FMT") doesn't match running kernel ("KVER_FMT")", | ||
| 447 | KVER_PARAM(obj_kver), | ||
| 448 | KVER_PARAM(real_kver)); | ||
| 449 | break; | ||
| 450 | } | ||
| 451 | |||
| 452 | scnprintf(buf, size, "Failed to load program for unknown reason"); | ||
| 453 | break; | ||
| 454 | } | ||
| 350 | bpf__strerror_end(buf, size); | 455 | bpf__strerror_end(buf, size); |
| 351 | return 0; | 456 | return 0; |
| 352 | } | 457 | } |
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index ccd8d7fd79d3..9caf3ae4acf3 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h | |||
| @@ -8,9 +8,21 @@ | |||
| 8 | #include <linux/compiler.h> | 8 | #include <linux/compiler.h> |
| 9 | #include <linux/err.h> | 9 | #include <linux/err.h> |
| 10 | #include <string.h> | 10 | #include <string.h> |
| 11 | #include <bpf/libbpf.h> | ||
| 11 | #include "probe-event.h" | 12 | #include "probe-event.h" |
| 12 | #include "debug.h" | 13 | #include "debug.h" |
| 13 | 14 | ||
| 15 | enum bpf_loader_errno { | ||
| 16 | __BPF_LOADER_ERRNO__START = __LIBBPF_ERRNO__START - 100, | ||
| 17 | /* Invalid config string */ | ||
| 18 | BPF_LOADER_ERRNO__CONFIG = __BPF_LOADER_ERRNO__START, | ||
| 19 | BPF_LOADER_ERRNO__GROUP, /* Invalid group name */ | ||
| 20 | BPF_LOADER_ERRNO__EVENTNAME, /* Event name is missing */ | ||
| 21 | BPF_LOADER_ERRNO__INTERNAL, /* BPF loader internal error */ | ||
| 22 | BPF_LOADER_ERRNO__COMPILE, /* Error when compiling BPF scriptlet */ | ||
| 23 | __BPF_LOADER_ERRNO__END, | ||
| 24 | }; | ||
| 25 | |||
| 14 | struct bpf_object; | 26 | struct bpf_object; |
| 15 | #define PERF_BPF_PROBE_GROUP "perf_bpf_probe" | 27 | #define PERF_BPF_PROBE_GROUP "perf_bpf_probe" |
| 16 | 28 | ||
| @@ -19,6 +31,11 @@ typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev, | |||
| 19 | 31 | ||
| 20 | #ifdef HAVE_LIBBPF_SUPPORT | 32 | #ifdef HAVE_LIBBPF_SUPPORT |
| 21 | struct bpf_object *bpf__prepare_load(const char *filename, bool source); | 33 | struct bpf_object *bpf__prepare_load(const char *filename, bool source); |
| 34 | int bpf__strerror_prepare_load(const char *filename, bool source, | ||
| 35 | int err, char *buf, size_t size); | ||
| 36 | |||
| 37 | struct bpf_object *bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, | ||
| 38 | const char *name); | ||
| 22 | 39 | ||
| 23 | void bpf__clear(void); | 40 | void bpf__clear(void); |
| 24 | 41 | ||
| @@ -41,6 +58,13 @@ bpf__prepare_load(const char *filename __maybe_unused, | |||
| 41 | return ERR_PTR(-ENOTSUP); | 58 | return ERR_PTR(-ENOTSUP); |
| 42 | } | 59 | } |
| 43 | 60 | ||
| 61 | static inline struct bpf_object * | ||
| 62 | bpf__prepare_load_buffer(void *obj_buf __maybe_unused, | ||
| 63 | size_t obj_buf_sz __maybe_unused) | ||
| 64 | { | ||
| 65 | return ERR_PTR(-ENOTSUP); | ||
| 66 | } | ||
| 67 | |||
| 44 | static inline void bpf__clear(void) { } | 68 | static inline void bpf__clear(void) { } |
| 45 | 69 | ||
| 46 | static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;} | 70 | static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;} |
| @@ -67,6 +91,15 @@ __bpf_strerror(char *buf, size_t size) | |||
| 67 | return 0; | 91 | return 0; |
| 68 | } | 92 | } |
| 69 | 93 | ||
| 94 | static inline | ||
| 95 | int bpf__strerror_prepare_load(const char *filename __maybe_unused, | ||
| 96 | bool source __maybe_unused, | ||
| 97 | int err __maybe_unused, | ||
| 98 | char *buf, size_t size) | ||
| 99 | { | ||
| 100 | return __bpf_strerror(buf, size); | ||
| 101 | } | ||
| 102 | |||
| 70 | static inline int | 103 | static inline int |
| 71 | bpf__strerror_probe(struct bpf_object *obj __maybe_unused, | 104 | bpf__strerror_probe(struct bpf_object *obj __maybe_unused, |
| 72 | int err __maybe_unused, | 105 | int err __maybe_unused, |
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 4f6a4780bd5f..00724d496d38 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c | |||
| @@ -4,17 +4,18 @@ | |||
| 4 | */ | 4 | */ |
| 5 | 5 | ||
| 6 | #include <stdio.h> | 6 | #include <stdio.h> |
| 7 | #include <sys/utsname.h> | ||
| 8 | #include "util.h" | 7 | #include "util.h" |
| 9 | #include "debug.h" | 8 | #include "debug.h" |
| 10 | #include "llvm-utils.h" | 9 | #include "llvm-utils.h" |
| 11 | #include "cache.h" | 10 | #include "cache.h" |
| 12 | 11 | ||
| 13 | #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ | 12 | #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ |
| 14 | "$CLANG_EXEC -D__KERNEL__ $CLANG_OPTIONS " \ | 13 | "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ |
| 15 | "$KERNEL_INC_OPTIONS -Wno-unused-value " \ | 14 | "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ |
| 16 | "-Wno-pointer-sign -working-directory " \ | 15 | "$CLANG_OPTIONS $KERNEL_INC_OPTIONS " \ |
| 17 | "$WORKING_DIR -c \"$CLANG_SOURCE\" -target bpf -O2 -o -" | 16 | "-Wno-unused-value -Wno-pointer-sign " \ |
| 17 | "-working-directory $WORKING_DIR " \ | ||
| 18 | "-c \"$CLANG_SOURCE\" -target bpf -O2 -o -" | ||
| 18 | 19 | ||
| 19 | struct llvm_param llvm_param = { | 20 | struct llvm_param llvm_param = { |
| 20 | .clang_path = "clang", | 21 | .clang_path = "clang", |
| @@ -214,18 +215,19 @@ static int detect_kbuild_dir(char **kbuild_dir) | |||
| 214 | const char *suffix_dir = ""; | 215 | const char *suffix_dir = ""; |
| 215 | 216 | ||
| 216 | char *autoconf_path; | 217 | char *autoconf_path; |
| 217 | struct utsname utsname; | ||
| 218 | 218 | ||
| 219 | int err; | 219 | int err; |
| 220 | 220 | ||
| 221 | if (!test_dir) { | 221 | if (!test_dir) { |
| 222 | err = uname(&utsname); | 222 | /* _UTSNAME_LENGTH is 65 */ |
| 223 | if (err) { | 223 | char release[128]; |
| 224 | pr_warning("uname failed: %s\n", strerror(errno)); | 224 | |
| 225 | err = fetch_kernel_version(NULL, release, | ||
| 226 | sizeof(release)); | ||
| 227 | if (err) | ||
| 225 | return -EINVAL; | 228 | return -EINVAL; |
| 226 | } | ||
| 227 | 229 | ||
| 228 | test_dir = utsname.release; | 230 | test_dir = release; |
| 229 | prefix_dir = "/lib/modules/"; | 231 | prefix_dir = "/lib/modules/"; |
| 230 | suffix_dir = "/build"; | 232 | suffix_dir = "/build"; |
| 231 | } | 233 | } |
| @@ -326,13 +328,15 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) | |||
| 326 | int llvm__compile_bpf(const char *path, void **p_obj_buf, | 328 | int llvm__compile_bpf(const char *path, void **p_obj_buf, |
| 327 | size_t *p_obj_buf_sz) | 329 | size_t *p_obj_buf_sz) |
| 328 | { | 330 | { |
| 329 | int err; | 331 | size_t obj_buf_sz; |
| 330 | char clang_path[PATH_MAX]; | 332 | void *obj_buf = NULL; |
| 333 | int err, nr_cpus_avail; | ||
| 334 | unsigned int kernel_version; | ||
| 335 | char linux_version_code_str[64]; | ||
| 331 | const char *clang_opt = llvm_param.clang_opt; | 336 | const char *clang_opt = llvm_param.clang_opt; |
| 332 | const char *template = llvm_param.clang_bpf_cmd_template; | 337 | char clang_path[PATH_MAX], nr_cpus_avail_str[64]; |
| 333 | char *kbuild_dir = NULL, *kbuild_include_opts = NULL; | 338 | char *kbuild_dir = NULL, *kbuild_include_opts = NULL; |
| 334 | void *obj_buf = NULL; | 339 | const char *template = llvm_param.clang_bpf_cmd_template; |
| 335 | size_t obj_buf_sz; | ||
| 336 | 340 | ||
| 337 | if (!template) | 341 | if (!template) |
| 338 | template = CLANG_BPF_CMD_DEFAULT_TEMPLATE; | 342 | template = CLANG_BPF_CMD_DEFAULT_TEMPLATE; |
| @@ -354,6 +358,24 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf, | |||
| 354 | */ | 358 | */ |
| 355 | get_kbuild_opts(&kbuild_dir, &kbuild_include_opts); | 359 | get_kbuild_opts(&kbuild_dir, &kbuild_include_opts); |
| 356 | 360 | ||
| 361 | nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF); | ||
| 362 | if (nr_cpus_avail <= 0) { | ||
| 363 | pr_err( | ||
| 364 | "WARNING:\tunable to get available CPUs in this system: %s\n" | ||
| 365 | " \tUse 128 instead.\n", strerror(errno)); | ||
| 366 | nr_cpus_avail = 128; | ||
| 367 | } | ||
| 368 | snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d", | ||
| 369 | nr_cpus_avail); | ||
| 370 | |||
| 371 | if (fetch_kernel_version(&kernel_version, NULL, 0)) | ||
| 372 | kernel_version = 0; | ||
| 373 | |||
| 374 | snprintf(linux_version_code_str, sizeof(linux_version_code_str), | ||
| 375 | "0x%x", kernel_version); | ||
| 376 | |||
| 377 | force_set_env("NR_CPUS", nr_cpus_avail_str); | ||
| 378 | force_set_env("LINUX_VERSION_CODE", linux_version_code_str); | ||
| 357 | force_set_env("CLANG_EXEC", clang_path); | 379 | force_set_env("CLANG_EXEC", clang_path); |
| 358 | force_set_env("CLANG_OPTIONS", clang_opt); | 380 | force_set_env("CLANG_OPTIONS", clang_opt); |
| 359 | force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts); | 381 | force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts); |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 4e38c396a897..afc6b56cf749 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
| @@ -644,6 +644,12 @@ size_t map_groups__fprintf(struct map_groups *mg, FILE *fp) | |||
| 644 | return printed; | 644 | return printed; |
| 645 | } | 645 | } |
| 646 | 646 | ||
| 647 | static void __map_groups__insert(struct map_groups *mg, struct map *map) | ||
| 648 | { | ||
| 649 | __maps__insert(&mg->maps[map->type], map); | ||
| 650 | map->groups = mg; | ||
| 651 | } | ||
| 652 | |||
| 647 | static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp) | 653 | static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp) |
| 648 | { | 654 | { |
| 649 | struct rb_root *root; | 655 | struct rb_root *root; |
| @@ -682,7 +688,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp | |||
| 682 | } | 688 | } |
| 683 | 689 | ||
| 684 | before->end = map->start; | 690 | before->end = map->start; |
| 685 | __maps__insert(maps, before); | 691 | __map_groups__insert(pos->groups, before); |
| 686 | if (verbose >= 2) | 692 | if (verbose >= 2) |
| 687 | map__fprintf(before, fp); | 693 | map__fprintf(before, fp); |
| 688 | } | 694 | } |
| @@ -696,7 +702,7 @@ static int maps__fixup_overlappings(struct maps *maps, struct map *map, FILE *fp | |||
| 696 | } | 702 | } |
| 697 | 703 | ||
| 698 | after->start = map->end; | 704 | after->start = map->end; |
| 699 | __maps__insert(maps, after); | 705 | __map_groups__insert(pos->groups, after); |
| 700 | if (verbose >= 2) | 706 | if (verbose >= 2) |
| 701 | map__fprintf(after, fp); | 707 | map__fprintf(after, fp); |
| 702 | } | 708 | } |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index bee60583839a..e48d9da75707 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -632,19 +632,20 @@ int parse_events_load_bpf(struct parse_events_evlist *data, | |||
| 632 | struct bpf_object *obj; | 632 | struct bpf_object *obj; |
| 633 | 633 | ||
| 634 | obj = bpf__prepare_load(bpf_file_name, source); | 634 | obj = bpf__prepare_load(bpf_file_name, source); |
| 635 | if (IS_ERR(obj) || !obj) { | 635 | if (IS_ERR(obj)) { |
| 636 | char errbuf[BUFSIZ]; | 636 | char errbuf[BUFSIZ]; |
| 637 | int err; | 637 | int err; |
| 638 | 638 | ||
| 639 | err = obj ? PTR_ERR(obj) : -EINVAL; | 639 | err = PTR_ERR(obj); |
| 640 | 640 | ||
| 641 | if (err == -ENOTSUP) | 641 | if (err == -ENOTSUP) |
| 642 | snprintf(errbuf, sizeof(errbuf), | 642 | snprintf(errbuf, sizeof(errbuf), |
| 643 | "BPF support is not compiled"); | 643 | "BPF support is not compiled"); |
| 644 | else | 644 | else |
| 645 | snprintf(errbuf, sizeof(errbuf), | 645 | bpf__strerror_prepare_load(bpf_file_name, |
| 646 | "BPF object file '%s' is invalid", | 646 | source, |
| 647 | bpf_file_name); | 647 | -err, errbuf, |
| 648 | sizeof(errbuf)); | ||
| 648 | 649 | ||
| 649 | data->error->help = strdup("(add -v to see detail)"); | 650 | data->error->help = strdup("(add -v to see detail)"); |
| 650 | data->error->str = strdup(errbuf); | 651 | data->error->str = strdup(errbuf); |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index b51a8bfb40f9..03875f9154e7 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -1895,9 +1895,8 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp, | |||
| 1895 | sym = map__find_symbol(map, addr, NULL); | 1895 | sym = map__find_symbol(map, addr, NULL); |
| 1896 | } else { | 1896 | } else { |
| 1897 | if (tp->symbol && !addr) { | 1897 | if (tp->symbol && !addr) { |
| 1898 | ret = kernel_get_symbol_address_by_name(tp->symbol, | 1898 | if (kernel_get_symbol_address_by_name(tp->symbol, |
| 1899 | &addr, true, false); | 1899 | &addr, true, false) < 0) |
| 1900 | if (ret < 0) | ||
| 1901 | goto out; | 1900 | goto out; |
| 1902 | } | 1901 | } |
| 1903 | if (addr) { | 1902 | if (addr) { |
| @@ -1905,6 +1904,7 @@ static int find_perf_probe_point_from_map(struct probe_trace_point *tp, | |||
| 1905 | sym = __find_kernel_function(addr, &map); | 1904 | sym = __find_kernel_function(addr, &map); |
| 1906 | } | 1905 | } |
| 1907 | } | 1906 | } |
| 1907 | |||
| 1908 | if (!sym) | 1908 | if (!sym) |
| 1909 | goto out; | 1909 | goto out; |
| 1910 | 1910 | ||
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c index 89dbeb92c68e..e3b3b92e4458 100644 --- a/tools/perf/util/probe-file.c +++ b/tools/perf/util/probe-file.c | |||
| @@ -138,6 +138,9 @@ struct strlist *probe_file__get_rawlist(int fd) | |||
| 138 | char *p; | 138 | char *p; |
| 139 | struct strlist *sl; | 139 | struct strlist *sl; |
| 140 | 140 | ||
| 141 | if (fd < 0) | ||
| 142 | return NULL; | ||
| 143 | |||
| 141 | sl = strlist__new(NULL, NULL); | 144 | sl = strlist__new(NULL, NULL); |
| 142 | 145 | ||
| 143 | fp = fdopen(dup(fd), "r"); | 146 | fp = fdopen(dup(fd), "r"); |
| @@ -271,6 +274,9 @@ int probe_file__get_events(int fd, struct strfilter *filter, | |||
| 271 | const char *p; | 274 | const char *p; |
| 272 | int ret = -ENOENT; | 275 | int ret = -ENOENT; |
| 273 | 276 | ||
| 277 | if (!plist) | ||
| 278 | return -EINVAL; | ||
| 279 | |||
| 274 | namelist = __probe_file__get_namelist(fd, true); | 280 | namelist = __probe_file__get_namelist(fd, true); |
| 275 | if (!namelist) | 281 | if (!namelist) |
| 276 | return -ENOENT; | 282 | return -ENOENT; |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 428149bc64d2..c35ffdd360fe 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -29,7 +29,7 @@ static int perf_session__open(struct perf_session *session) | |||
| 29 | struct perf_data_file *file = session->file; | 29 | struct perf_data_file *file = session->file; |
| 30 | 30 | ||
| 31 | if (perf_session__read_header(session) < 0) { | 31 | if (perf_session__read_header(session) < 0) { |
| 32 | pr_err("incompatible file format (rerun with -v to learn more)"); | 32 | pr_err("incompatible file format (rerun with -v to learn more)\n"); |
| 33 | return -1; | 33 | return -1; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| @@ -37,17 +37,17 @@ static int perf_session__open(struct perf_session *session) | |||
| 37 | return 0; | 37 | return 0; |
| 38 | 38 | ||
| 39 | if (!perf_evlist__valid_sample_type(session->evlist)) { | 39 | if (!perf_evlist__valid_sample_type(session->evlist)) { |
| 40 | pr_err("non matching sample_type"); | 40 | pr_err("non matching sample_type\n"); |
| 41 | return -1; | 41 | return -1; |
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | if (!perf_evlist__valid_sample_id_all(session->evlist)) { | 44 | if (!perf_evlist__valid_sample_id_all(session->evlist)) { |
| 45 | pr_err("non matching sample_id_all"); | 45 | pr_err("non matching sample_id_all\n"); |
| 46 | return -1; | 46 | return -1; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | if (!perf_evlist__valid_read_format(session->evlist)) { | 49 | if (!perf_evlist__valid_read_format(session->evlist)) { |
| 50 | pr_err("non matching read_format"); | 50 | pr_err("non matching read_format\n"); |
| 51 | return -1; | 51 | return -1; |
| 52 | } | 52 | } |
| 53 | 53 | ||
diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 2a5d8d7698ae..6ac03146889d 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c | |||
| @@ -413,6 +413,11 @@ void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel, | |||
| 413 | ratio = total / avg; | 413 | ratio = total / avg; |
| 414 | 414 | ||
| 415 | fprintf(out, " # %8.0f cycles / elision ", ratio); | 415 | fprintf(out, " # %8.0f cycles / elision ", ratio); |
| 416 | } else if (perf_evsel__match(evsel, SOFTWARE, SW_TASK_CLOCK)) { | ||
| 417 | if ((ratio = avg_stats(&walltime_nsecs_stats)) != 0) | ||
| 418 | fprintf(out, " # %8.3f CPUs utilized ", avg / ratio); | ||
| 419 | else | ||
| 420 | fprintf(out, " "); | ||
| 416 | } else if (runtime_nsecs_stats[cpu].n != 0) { | 421 | } else if (runtime_nsecs_stats[cpu].n != 0) { |
| 417 | char unit = 'M'; | 422 | char unit = 'M'; |
| 418 | 423 | ||
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index cd12c25e4ea4..47b1e36c7ea0 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include "debug.h" | 3 | #include "debug.h" |
| 4 | #include <api/fs/fs.h> | 4 | #include <api/fs/fs.h> |
| 5 | #include <sys/mman.h> | 5 | #include <sys/mman.h> |
| 6 | #include <sys/utsname.h> | ||
| 6 | #ifdef HAVE_BACKTRACE_SUPPORT | 7 | #ifdef HAVE_BACKTRACE_SUPPORT |
| 7 | #include <execinfo.h> | 8 | #include <execinfo.h> |
| 8 | #endif | 9 | #endif |
| @@ -665,3 +666,32 @@ bool find_process(const char *name) | |||
| 665 | closedir(dir); | 666 | closedir(dir); |
| 666 | return ret ? false : true; | 667 | return ret ? false : true; |
| 667 | } | 668 | } |
| 669 | |||
| 670 | int | ||
| 671 | fetch_kernel_version(unsigned int *puint, char *str, | ||
| 672 | size_t str_size) | ||
| 673 | { | ||
| 674 | struct utsname utsname; | ||
| 675 | int version, patchlevel, sublevel, err; | ||
| 676 | |||
| 677 | if (uname(&utsname)) | ||
| 678 | return -1; | ||
| 679 | |||
| 680 | if (str && str_size) { | ||
| 681 | strncpy(str, utsname.release, str_size); | ||
| 682 | str[str_size - 1] = '\0'; | ||
| 683 | } | ||
| 684 | |||
| 685 | err = sscanf(utsname.release, "%d.%d.%d", | ||
| 686 | &version, &patchlevel, &sublevel); | ||
| 687 | |||
| 688 | if (err != 3) { | ||
| 689 | pr_debug("Unablt to get kernel version from uname '%s'\n", | ||
| 690 | utsname.release); | ||
| 691 | return -1; | ||
| 692 | } | ||
| 693 | |||
| 694 | if (puint) | ||
| 695 | *puint = (version << 16) + (patchlevel << 8) + sublevel; | ||
| 696 | return 0; | ||
| 697 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 4cfb913aa9e0..dcc659017976 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
| @@ -350,4 +350,12 @@ static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int | |||
| 350 | 350 | ||
| 351 | int get_stack_size(const char *str, unsigned long *_size); | 351 | int get_stack_size(const char *str, unsigned long *_size); |
| 352 | 352 | ||
| 353 | int fetch_kernel_version(unsigned int *puint, | ||
| 354 | char *str, size_t str_sz); | ||
| 355 | #define KVER_VERSION(x) (((x) >> 16) & 0xff) | ||
| 356 | #define KVER_PATCHLEVEL(x) (((x) >> 8) & 0xff) | ||
| 357 | #define KVER_SUBLEVEL(x) ((x) & 0xff) | ||
| 358 | #define KVER_FMT "%d.%d.%d" | ||
| 359 | #define KVER_PARAM(x) KVER_VERSION(x), KVER_PATCHLEVEL(x), KVER_SUBLEVEL(x) | ||
| 360 | |||
| 353 | #endif /* GIT_COMPAT_UTIL_H */ | 361 | #endif /* GIT_COMPAT_UTIL_H */ |
