diff options
Diffstat (limited to 'tools/perf/util/bpf-loader.c')
| -rw-r--r-- | tools/perf/util/bpf-loader.c | 433 |
1 files changed, 425 insertions, 8 deletions
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index 4c50411371db..540a7efa657e 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c | |||
| @@ -5,11 +5,15 @@ | |||
| 5 | * Copyright (C) 2015 Huawei Inc. | 5 | * Copyright (C) 2015 Huawei Inc. |
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/bpf.h> | ||
| 8 | #include <bpf/libbpf.h> | 9 | #include <bpf/libbpf.h> |
| 9 | #include <linux/err.h> | 10 | #include <linux/err.h> |
| 11 | #include <linux/string.h> | ||
| 10 | #include "perf.h" | 12 | #include "perf.h" |
| 11 | #include "debug.h" | 13 | #include "debug.h" |
| 12 | #include "bpf-loader.h" | 14 | #include "bpf-loader.h" |
| 15 | #include "bpf-prologue.h" | ||
| 16 | #include "llvm-utils.h" | ||
| 13 | #include "probe-event.h" | 17 | #include "probe-event.h" |
| 14 | #include "probe-finder.h" // for MAX_PROBES | 18 | #include "probe-finder.h" // for MAX_PROBES |
| 15 | #include "llvm-utils.h" | 19 | #include "llvm-utils.h" |
| @@ -32,6 +36,10 @@ DEFINE_PRINT_FN(debug, 1) | |||
| 32 | 36 | ||
| 33 | struct bpf_prog_priv { | 37 | struct bpf_prog_priv { |
| 34 | struct perf_probe_event pev; | 38 | struct perf_probe_event pev; |
| 39 | bool need_prologue; | ||
| 40 | struct bpf_insn *insns_buf; | ||
| 41 | int nr_types; | ||
| 42 | int *type_mapping; | ||
| 35 | }; | 43 | }; |
| 36 | 44 | ||
| 37 | static bool libbpf_initialized; | 45 | static bool libbpf_initialized; |
| @@ -106,10 +114,178 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused, | |||
| 106 | struct bpf_prog_priv *priv = _priv; | 114 | struct bpf_prog_priv *priv = _priv; |
| 107 | 115 | ||
| 108 | cleanup_perf_probe_events(&priv->pev, 1); | 116 | cleanup_perf_probe_events(&priv->pev, 1); |
| 117 | zfree(&priv->insns_buf); | ||
| 118 | zfree(&priv->type_mapping); | ||
| 109 | free(priv); | 119 | free(priv); |
| 110 | } | 120 | } |
| 111 | 121 | ||
| 112 | static int | 122 | static int |
| 123 | prog_config__exec(const char *value, struct perf_probe_event *pev) | ||
| 124 | { | ||
| 125 | pev->uprobes = true; | ||
| 126 | pev->target = strdup(value); | ||
| 127 | if (!pev->target) | ||
| 128 | return -ENOMEM; | ||
| 129 | return 0; | ||
| 130 | } | ||
| 131 | |||
| 132 | static int | ||
| 133 | prog_config__module(const char *value, struct perf_probe_event *pev) | ||
| 134 | { | ||
| 135 | pev->uprobes = false; | ||
| 136 | pev->target = strdup(value); | ||
| 137 | if (!pev->target) | ||
| 138 | return -ENOMEM; | ||
| 139 | return 0; | ||
| 140 | } | ||
| 141 | |||
| 142 | static int | ||
| 143 | prog_config__bool(const char *value, bool *pbool, bool invert) | ||
| 144 | { | ||
| 145 | int err; | ||
| 146 | bool bool_value; | ||
| 147 | |||
| 148 | if (!pbool) | ||
| 149 | return -EINVAL; | ||
| 150 | |||
| 151 | err = strtobool(value, &bool_value); | ||
| 152 | if (err) | ||
| 153 | return err; | ||
| 154 | |||
| 155 | *pbool = invert ? !bool_value : bool_value; | ||
| 156 | return 0; | ||
| 157 | } | ||
| 158 | |||
| 159 | static int | ||
| 160 | prog_config__inlines(const char *value, | ||
| 161 | struct perf_probe_event *pev __maybe_unused) | ||
| 162 | { | ||
| 163 | return prog_config__bool(value, &probe_conf.no_inlines, true); | ||
| 164 | } | ||
| 165 | |||
| 166 | static int | ||
| 167 | prog_config__force(const char *value, | ||
| 168 | struct perf_probe_event *pev __maybe_unused) | ||
| 169 | { | ||
| 170 | return prog_config__bool(value, &probe_conf.force_add, false); | ||
| 171 | } | ||
| 172 | |||
| 173 | static struct { | ||
| 174 | const char *key; | ||
| 175 | const char *usage; | ||
| 176 | const char *desc; | ||
| 177 | int (*func)(const char *, struct perf_probe_event *); | ||
| 178 | } bpf_prog_config_terms[] = { | ||
| 179 | { | ||
| 180 | .key = "exec", | ||
| 181 | .usage = "exec=<full path of file>", | ||
| 182 | .desc = "Set uprobe target", | ||
| 183 | .func = prog_config__exec, | ||
| 184 | }, | ||
| 185 | { | ||
| 186 | .key = "module", | ||
| 187 | .usage = "module=<module name> ", | ||
| 188 | .desc = "Set kprobe module", | ||
| 189 | .func = prog_config__module, | ||
| 190 | }, | ||
| 191 | { | ||
| 192 | .key = "inlines", | ||
| 193 | .usage = "inlines=[yes|no] ", | ||
| 194 | .desc = "Probe at inline symbol", | ||
| 195 | .func = prog_config__inlines, | ||
| 196 | }, | ||
| 197 | { | ||
| 198 | .key = "force", | ||
| 199 | .usage = "force=[yes|no] ", | ||
| 200 | .desc = "Forcibly add events with existing name", | ||
| 201 | .func = prog_config__force, | ||
| 202 | }, | ||
| 203 | }; | ||
| 204 | |||
| 205 | static int | ||
| 206 | do_prog_config(const char *key, const char *value, | ||
| 207 | struct perf_probe_event *pev) | ||
| 208 | { | ||
| 209 | unsigned int i; | ||
| 210 | |||
| 211 | pr_debug("config bpf program: %s=%s\n", key, value); | ||
| 212 | for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++) | ||
| 213 | if (strcmp(key, bpf_prog_config_terms[i].key) == 0) | ||
| 214 | return bpf_prog_config_terms[i].func(value, pev); | ||
| 215 | |||
| 216 | pr_debug("BPF: ERROR: invalid program config option: %s=%s\n", | ||
| 217 | key, value); | ||
| 218 | |||
| 219 | pr_debug("\nHint: Valid options are:\n"); | ||
| 220 | for (i = 0; i < ARRAY_SIZE(bpf_prog_config_terms); i++) | ||
| 221 | pr_debug("\t%s:\t%s\n", bpf_prog_config_terms[i].usage, | ||
| 222 | bpf_prog_config_terms[i].desc); | ||
| 223 | pr_debug("\n"); | ||
| 224 | |||
| 225 | return -BPF_LOADER_ERRNO__PROGCONF_TERM; | ||
| 226 | } | ||
| 227 | |||
| 228 | static const char * | ||
| 229 | parse_prog_config_kvpair(const char *config_str, struct perf_probe_event *pev) | ||
| 230 | { | ||
| 231 | char *text = strdup(config_str); | ||
| 232 | char *sep, *line; | ||
| 233 | const char *main_str = NULL; | ||
| 234 | int err = 0; | ||
| 235 | |||
| 236 | if (!text) { | ||
| 237 | pr_debug("No enough memory: dup config_str failed\n"); | ||
| 238 | return ERR_PTR(-ENOMEM); | ||
| 239 | } | ||
| 240 | |||
| 241 | line = text; | ||
| 242 | while ((sep = strchr(line, ';'))) { | ||
| 243 | char *equ; | ||
| 244 | |||
| 245 | *sep = '\0'; | ||
| 246 | equ = strchr(line, '='); | ||
| 247 | if (!equ) { | ||
| 248 | pr_warning("WARNING: invalid config in BPF object: %s\n", | ||
| 249 | line); | ||
| 250 | pr_warning("\tShould be 'key=value'.\n"); | ||
| 251 | goto nextline; | ||
| 252 | } | ||
| 253 | *equ = '\0'; | ||
| 254 | |||
| 255 | err = do_prog_config(line, equ + 1, pev); | ||
| 256 | if (err) | ||
| 257 | break; | ||
| 258 | nextline: | ||
| 259 | line = sep + 1; | ||
| 260 | } | ||
| 261 | |||
| 262 | if (!err) | ||
| 263 | main_str = config_str + (line - text); | ||
| 264 | free(text); | ||
| 265 | |||
| 266 | return err ? ERR_PTR(err) : main_str; | ||
| 267 | } | ||
| 268 | |||
| 269 | static int | ||
| 270 | parse_prog_config(const char *config_str, struct perf_probe_event *pev) | ||
| 271 | { | ||
| 272 | int err; | ||
| 273 | const char *main_str = parse_prog_config_kvpair(config_str, pev); | ||
| 274 | |||
| 275 | if (IS_ERR(main_str)) | ||
| 276 | return PTR_ERR(main_str); | ||
| 277 | |||
| 278 | err = parse_perf_probe_command(main_str, pev); | ||
| 279 | if (err < 0) { | ||
| 280 | pr_debug("bpf: '%s' is not a valid config string\n", | ||
| 281 | config_str); | ||
| 282 | /* parse failed, don't need clear pev. */ | ||
| 283 | return -BPF_LOADER_ERRNO__CONFIG; | ||
| 284 | } | ||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
| 288 | static int | ||
| 113 | config_bpf_program(struct bpf_program *prog) | 289 | config_bpf_program(struct bpf_program *prog) |
| 114 | { | 290 | { |
| 115 | struct perf_probe_event *pev = NULL; | 291 | struct perf_probe_event *pev = NULL; |
| @@ -117,6 +293,10 @@ config_bpf_program(struct bpf_program *prog) | |||
| 117 | const char *config_str; | 293 | const char *config_str; |
| 118 | int err; | 294 | int err; |
| 119 | 295 | ||
| 296 | /* Initialize per-program probing setting */ | ||
| 297 | probe_conf.no_inlines = false; | ||
| 298 | probe_conf.force_add = false; | ||
| 299 | |||
| 120 | config_str = bpf_program__title(prog, false); | 300 | config_str = bpf_program__title(prog, false); |
| 121 | if (IS_ERR(config_str)) { | 301 | if (IS_ERR(config_str)) { |
| 122 | pr_debug("bpf: unable to get title for program\n"); | 302 | pr_debug("bpf: unable to get title for program\n"); |
| @@ -131,13 +311,9 @@ config_bpf_program(struct bpf_program *prog) | |||
| 131 | pev = &priv->pev; | 311 | pev = &priv->pev; |
| 132 | 312 | ||
| 133 | pr_debug("bpf: config program '%s'\n", config_str); | 313 | pr_debug("bpf: config program '%s'\n", config_str); |
| 134 | err = parse_perf_probe_command(config_str, pev); | 314 | err = parse_prog_config(config_str, pev); |
| 135 | if (err < 0) { | 315 | if (err) |
| 136 | pr_debug("bpf: '%s' is not a valid config string\n", | ||
| 137 | config_str); | ||
| 138 | err = -BPF_LOADER_ERRNO__CONFIG; | ||
| 139 | goto errout; | 316 | goto errout; |
| 140 | } | ||
| 141 | 317 | ||
| 142 | if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { | 318 | if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { |
| 143 | pr_debug("bpf: '%s': group for event is set and not '%s'.\n", | 319 | pr_debug("bpf: '%s': group for event is set and not '%s'.\n", |
| @@ -197,6 +373,220 @@ static int bpf__prepare_probe(void) | |||
| 197 | return err; | 373 | return err; |
| 198 | } | 374 | } |
| 199 | 375 | ||
| 376 | static int | ||
| 377 | preproc_gen_prologue(struct bpf_program *prog, int n, | ||
| 378 | struct bpf_insn *orig_insns, int orig_insns_cnt, | ||
| 379 | struct bpf_prog_prep_result *res) | ||
| 380 | { | ||
| 381 | struct probe_trace_event *tev; | ||
| 382 | struct perf_probe_event *pev; | ||
| 383 | struct bpf_prog_priv *priv; | ||
| 384 | struct bpf_insn *buf; | ||
| 385 | size_t prologue_cnt = 0; | ||
| 386 | int i, err; | ||
| 387 | |||
| 388 | err = bpf_program__get_private(prog, (void **)&priv); | ||
| 389 | if (err || !priv) | ||
| 390 | goto errout; | ||
| 391 | |||
| 392 | pev = &priv->pev; | ||
| 393 | |||
| 394 | if (n < 0 || n >= priv->nr_types) | ||
| 395 | goto errout; | ||
| 396 | |||
| 397 | /* Find a tev belongs to that type */ | ||
| 398 | for (i = 0; i < pev->ntevs; i++) { | ||
| 399 | if (priv->type_mapping[i] == n) | ||
| 400 | break; | ||
| 401 | } | ||
| 402 | |||
| 403 | if (i >= pev->ntevs) { | ||
| 404 | pr_debug("Internal error: prologue type %d not found\n", n); | ||
| 405 | return -BPF_LOADER_ERRNO__PROLOGUE; | ||
| 406 | } | ||
| 407 | |||
| 408 | tev = &pev->tevs[i]; | ||
| 409 | |||
| 410 | buf = priv->insns_buf; | ||
| 411 | err = bpf__gen_prologue(tev->args, tev->nargs, | ||
| 412 | buf, &prologue_cnt, | ||
| 413 | BPF_MAXINSNS - orig_insns_cnt); | ||
| 414 | if (err) { | ||
| 415 | const char *title; | ||
| 416 | |||
| 417 | title = bpf_program__title(prog, false); | ||
| 418 | if (!title) | ||
| 419 | title = "[unknown]"; | ||
| 420 | |||
| 421 | pr_debug("Failed to generate prologue for program %s\n", | ||
| 422 | title); | ||
| 423 | return err; | ||
| 424 | } | ||
| 425 | |||
| 426 | memcpy(&buf[prologue_cnt], orig_insns, | ||
| 427 | sizeof(struct bpf_insn) * orig_insns_cnt); | ||
| 428 | |||
| 429 | res->new_insn_ptr = buf; | ||
| 430 | res->new_insn_cnt = prologue_cnt + orig_insns_cnt; | ||
| 431 | res->pfd = NULL; | ||
| 432 | return 0; | ||
| 433 | |||
| 434 | errout: | ||
| 435 | pr_debug("Internal error in preproc_gen_prologue\n"); | ||
| 436 | return -BPF_LOADER_ERRNO__PROLOGUE; | ||
| 437 | } | ||
| 438 | |||
| 439 | /* | ||
| 440 | * compare_tev_args is reflexive, transitive and antisymmetric. | ||
| 441 | * I can proof it but this margin is too narrow to contain. | ||
| 442 | */ | ||
| 443 | static int compare_tev_args(const void *ptev1, const void *ptev2) | ||
| 444 | { | ||
| 445 | int i, ret; | ||
| 446 | const struct probe_trace_event *tev1 = | ||
| 447 | *(const struct probe_trace_event **)ptev1; | ||
| 448 | const struct probe_trace_event *tev2 = | ||
| 449 | *(const struct probe_trace_event **)ptev2; | ||
| 450 | |||
| 451 | ret = tev2->nargs - tev1->nargs; | ||
| 452 | if (ret) | ||
| 453 | return ret; | ||
| 454 | |||
| 455 | for (i = 0; i < tev1->nargs; i++) { | ||
| 456 | struct probe_trace_arg *arg1, *arg2; | ||
| 457 | struct probe_trace_arg_ref *ref1, *ref2; | ||
| 458 | |||
| 459 | arg1 = &tev1->args[i]; | ||
| 460 | arg2 = &tev2->args[i]; | ||
| 461 | |||
| 462 | ret = strcmp(arg1->value, arg2->value); | ||
| 463 | if (ret) | ||
| 464 | return ret; | ||
| 465 | |||
| 466 | ref1 = arg1->ref; | ||
| 467 | ref2 = arg2->ref; | ||
| 468 | |||
| 469 | while (ref1 && ref2) { | ||
| 470 | ret = ref2->offset - ref1->offset; | ||
| 471 | if (ret) | ||
| 472 | return ret; | ||
| 473 | |||
| 474 | ref1 = ref1->next; | ||
| 475 | ref2 = ref2->next; | ||
| 476 | } | ||
| 477 | |||
| 478 | if (ref1 || ref2) | ||
| 479 | return ref2 ? 1 : -1; | ||
| 480 | } | ||
| 481 | |||
| 482 | return 0; | ||
| 483 | } | ||
| 484 | |||
| 485 | /* | ||
| 486 | * Assign a type number to each tevs in a pev. | ||
| 487 | * mapping is an array with same slots as tevs in that pev. | ||
| 488 | * nr_types will be set to number of types. | ||
| 489 | */ | ||
| 490 | static int map_prologue(struct perf_probe_event *pev, int *mapping, | ||
| 491 | int *nr_types) | ||
| 492 | { | ||
| 493 | int i, type = 0; | ||
| 494 | struct probe_trace_event **ptevs; | ||
| 495 | |||
| 496 | size_t array_sz = sizeof(*ptevs) * pev->ntevs; | ||
| 497 | |||
| 498 | ptevs = malloc(array_sz); | ||
| 499 | if (!ptevs) { | ||
| 500 | pr_debug("No ehough memory: alloc ptevs failed\n"); | ||
| 501 | return -ENOMEM; | ||
| 502 | } | ||
| 503 | |||
| 504 | pr_debug("In map_prologue, ntevs=%d\n", pev->ntevs); | ||
| 505 | for (i = 0; i < pev->ntevs; i++) | ||
| 506 | ptevs[i] = &pev->tevs[i]; | ||
| 507 | |||
| 508 | qsort(ptevs, pev->ntevs, sizeof(*ptevs), | ||
| 509 | compare_tev_args); | ||
| 510 | |||
| 511 | for (i = 0; i < pev->ntevs; i++) { | ||
| 512 | int n; | ||
| 513 | |||
| 514 | n = ptevs[i] - pev->tevs; | ||
| 515 | if (i == 0) { | ||
| 516 | mapping[n] = type; | ||
| 517 | pr_debug("mapping[%d]=%d\n", n, type); | ||
| 518 | continue; | ||
| 519 | } | ||
| 520 | |||
| 521 | if (compare_tev_args(ptevs + i, ptevs + i - 1) == 0) | ||
| 522 | mapping[n] = type; | ||
| 523 | else | ||
| 524 | mapping[n] = ++type; | ||
| 525 | |||
| 526 | pr_debug("mapping[%d]=%d\n", n, mapping[n]); | ||
| 527 | } | ||
| 528 | free(ptevs); | ||
| 529 | *nr_types = type + 1; | ||
| 530 | |||
| 531 | return 0; | ||
| 532 | } | ||
| 533 | |||
| 534 | static int hook_load_preprocessor(struct bpf_program *prog) | ||
| 535 | { | ||
| 536 | struct perf_probe_event *pev; | ||
| 537 | struct bpf_prog_priv *priv; | ||
| 538 | bool need_prologue = false; | ||
| 539 | int err, i; | ||
| 540 | |||
| 541 | err = bpf_program__get_private(prog, (void **)&priv); | ||
| 542 | if (err || !priv) { | ||
| 543 | pr_debug("Internal error when hook preprocessor\n"); | ||
| 544 | return -BPF_LOADER_ERRNO__INTERNAL; | ||
| 545 | } | ||
| 546 | |||
| 547 | pev = &priv->pev; | ||
| 548 | for (i = 0; i < pev->ntevs; i++) { | ||
| 549 | struct probe_trace_event *tev = &pev->tevs[i]; | ||
| 550 | |||
| 551 | if (tev->nargs > 0) { | ||
| 552 | need_prologue = true; | ||
| 553 | break; | ||
| 554 | } | ||
| 555 | } | ||
| 556 | |||
| 557 | /* | ||
| 558 | * Since all tevs don't have argument, we don't need generate | ||
| 559 | * prologue. | ||
| 560 | */ | ||
| 561 | if (!need_prologue) { | ||
| 562 | priv->need_prologue = false; | ||
| 563 | return 0; | ||
| 564 | } | ||
| 565 | |||
| 566 | priv->need_prologue = true; | ||
| 567 | priv->insns_buf = malloc(sizeof(struct bpf_insn) * BPF_MAXINSNS); | ||
| 568 | if (!priv->insns_buf) { | ||
| 569 | pr_debug("No enough memory: alloc insns_buf failed\n"); | ||
| 570 | return -ENOMEM; | ||
| 571 | } | ||
| 572 | |||
| 573 | priv->type_mapping = malloc(sizeof(int) * pev->ntevs); | ||
| 574 | if (!priv->type_mapping) { | ||
| 575 | pr_debug("No enough memory: alloc type_mapping failed\n"); | ||
| 576 | return -ENOMEM; | ||
| 577 | } | ||
| 578 | memset(priv->type_mapping, -1, | ||
| 579 | sizeof(int) * pev->ntevs); | ||
| 580 | |||
| 581 | err = map_prologue(pev, priv->type_mapping, &priv->nr_types); | ||
| 582 | if (err) | ||
| 583 | return err; | ||
| 584 | |||
| 585 | err = bpf_program__set_prep(prog, priv->nr_types, | ||
| 586 | preproc_gen_prologue); | ||
| 587 | return err; | ||
| 588 | } | ||
| 589 | |||
| 200 | int bpf__probe(struct bpf_object *obj) | 590 | int bpf__probe(struct bpf_object *obj) |
| 201 | { | 591 | { |
| 202 | int err = 0; | 592 | int err = 0; |
| @@ -231,6 +621,18 @@ int bpf__probe(struct bpf_object *obj) | |||
| 231 | pr_debug("bpf_probe: failed to apply perf probe events"); | 621 | pr_debug("bpf_probe: failed to apply perf probe events"); |
| 232 | goto out; | 622 | goto out; |
| 233 | } | 623 | } |
| 624 | |||
| 625 | /* | ||
| 626 | * After probing, let's consider prologue, which | ||
| 627 | * adds program fetcher to BPF programs. | ||
| 628 | * | ||
| 629 | * hook_load_preprocessorr() hooks pre-processor | ||
| 630 | * to bpf_program, let it generate prologue | ||
| 631 | * dynamically during loading. | ||
| 632 | */ | ||
| 633 | err = hook_load_preprocessor(prog); | ||
| 634 | if (err) | ||
| 635 | goto out; | ||
| 234 | } | 636 | } |
| 235 | out: | 637 | out: |
| 236 | return err < 0 ? err : 0; | 638 | return err < 0 ? err : 0; |
| @@ -314,7 +716,14 @@ int bpf__foreach_tev(struct bpf_object *obj, | |||
| 314 | for (i = 0; i < pev->ntevs; i++) { | 716 | for (i = 0; i < pev->ntevs; i++) { |
| 315 | tev = &pev->tevs[i]; | 717 | tev = &pev->tevs[i]; |
| 316 | 718 | ||
| 317 | fd = bpf_program__fd(prog); | 719 | if (priv->need_prologue) { |
| 720 | int type = priv->type_mapping[i]; | ||
| 721 | |||
| 722 | fd = bpf_program__nth_fd(prog, type); | ||
| 723 | } else { | ||
| 724 | fd = bpf_program__fd(prog); | ||
| 725 | } | ||
| 726 | |||
| 318 | if (fd < 0) { | 727 | if (fd < 0) { |
| 319 | pr_debug("bpf: failed to get file descriptor\n"); | 728 | pr_debug("bpf: failed to get file descriptor\n"); |
| 320 | return fd; | 729 | return fd; |
| @@ -340,6 +749,10 @@ static const char *bpf_loader_strerror_table[NR_ERRNO] = { | |||
| 340 | [ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string", | 749 | [ERRCODE_OFFSET(EVENTNAME)] = "No event name found in config string", |
| 341 | [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error", | 750 | [ERRCODE_OFFSET(INTERNAL)] = "BPF loader internal error", |
| 342 | [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet", | 751 | [ERRCODE_OFFSET(COMPILE)] = "Error when compiling BPF scriptlet", |
| 752 | [ERRCODE_OFFSET(PROGCONF_TERM)] = "Invalid program config term in config string", | ||
| 753 | [ERRCODE_OFFSET(PROLOGUE)] = "Failed to generate prologue", | ||
| 754 | [ERRCODE_OFFSET(PROLOGUE2BIG)] = "Prologue too big for program", | ||
| 755 | [ERRCODE_OFFSET(PROLOGUEOOB)] = "Offset out of bound for prologue", | ||
| 343 | }; | 756 | }; |
| 344 | 757 | ||
| 345 | static int | 758 | static int |
| @@ -420,7 +833,11 @@ int bpf__strerror_probe(struct bpf_object *obj __maybe_unused, | |||
| 420 | int err, char *buf, size_t size) | 833 | int err, char *buf, size_t size) |
| 421 | { | 834 | { |
| 422 | bpf__strerror_head(err, buf, size); | 835 | bpf__strerror_head(err, buf, size); |
| 423 | bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'"); | 836 | case BPF_LOADER_ERRNO__PROGCONF_TERM: { |
| 837 | scnprintf(buf, size, "%s (add -v to see detail)", emsg); | ||
| 838 | break; | ||
| 839 | } | ||
| 840 | bpf__strerror_entry(EEXIST, "Probe point exist. Try 'perf probe -d \"*\"' and set 'force=yes'"); | ||
| 424 | bpf__strerror_entry(EACCES, "You need to be root"); | 841 | bpf__strerror_entry(EACCES, "You need to be root"); |
| 425 | bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0"); | 842 | 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"); | 843 | bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file"); |
