diff options
Diffstat (limited to 'kernel/trace/trace_functions.c')
-rw-r--r-- | kernel/trace/trace_functions.c | 123 |
1 files changed, 85 insertions, 38 deletions
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index b99f6231281e..d9cbde8575a8 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c | |||
@@ -267,10 +267,12 @@ static struct tracer function_trace __tracer_data = | |||
267 | }; | 267 | }; |
268 | 268 | ||
269 | #ifdef CONFIG_DYNAMIC_FTRACE | 269 | #ifdef CONFIG_DYNAMIC_FTRACE |
270 | static void update_traceon_count(void **data, bool on) | 270 | static void update_traceon_count(struct ftrace_probe_ops *ops, |
271 | unsigned long ip, bool on) | ||
271 | { | 272 | { |
272 | long *count = (long *)data; | 273 | struct ftrace_func_mapper *mapper = ops->private_data; |
273 | long old_count = *count; | 274 | long *count; |
275 | long old_count; | ||
274 | 276 | ||
275 | /* | 277 | /* |
276 | * Tracing gets disabled (or enabled) once per count. | 278 | * Tracing gets disabled (or enabled) once per count. |
@@ -301,7 +303,10 @@ static void update_traceon_count(void **data, bool on) | |||
301 | * setting the tracing_on file. But we currently don't care | 303 | * setting the tracing_on file. But we currently don't care |
302 | * about that. | 304 | * about that. |
303 | */ | 305 | */ |
304 | if (!old_count) | 306 | count = (long *)ftrace_func_mapper_find_ip(mapper, ip); |
307 | old_count = *count; | ||
308 | |||
309 | if (old_count <= 0) | ||
305 | return; | 310 | return; |
306 | 311 | ||
307 | /* Make sure we see count before checking tracing state */ | 312 | /* Make sure we see count before checking tracing state */ |
@@ -315,10 +320,6 @@ static void update_traceon_count(void **data, bool on) | |||
315 | else | 320 | else |
316 | tracing_off(); | 321 | tracing_off(); |
317 | 322 | ||
318 | /* unlimited? */ | ||
319 | if (old_count == -1) | ||
320 | return; | ||
321 | |||
322 | /* Make sure tracing state is visible before updating count */ | 323 | /* Make sure tracing state is visible before updating count */ |
323 | smp_wmb(); | 324 | smp_wmb(); |
324 | 325 | ||
@@ -329,14 +330,14 @@ static void | |||
329 | ftrace_traceon_count(unsigned long ip, unsigned long parent_ip, | 330 | ftrace_traceon_count(unsigned long ip, unsigned long parent_ip, |
330 | struct ftrace_probe_ops *ops, void **data) | 331 | struct ftrace_probe_ops *ops, void **data) |
331 | { | 332 | { |
332 | update_traceon_count(data, 1); | 333 | update_traceon_count(ops, ip, 1); |
333 | } | 334 | } |
334 | 335 | ||
335 | static void | 336 | static void |
336 | ftrace_traceoff_count(unsigned long ip, unsigned long parent_ip, | 337 | ftrace_traceoff_count(unsigned long ip, unsigned long parent_ip, |
337 | struct ftrace_probe_ops *ops, void **data) | 338 | struct ftrace_probe_ops *ops, void **data) |
338 | { | 339 | { |
339 | update_traceon_count(data, 0); | 340 | update_traceon_count(ops, ip, 0); |
340 | } | 341 | } |
341 | 342 | ||
342 | static void | 343 | static void |
@@ -379,47 +380,56 @@ static void | |||
379 | ftrace_stacktrace_count(unsigned long ip, unsigned long parent_ip, | 380 | ftrace_stacktrace_count(unsigned long ip, unsigned long parent_ip, |
380 | struct ftrace_probe_ops *ops, void **data) | 381 | struct ftrace_probe_ops *ops, void **data) |
381 | { | 382 | { |
382 | long *count = (long *)data; | 383 | struct ftrace_func_mapper *mapper = ops->private_data; |
384 | long *count; | ||
383 | long old_count; | 385 | long old_count; |
384 | long new_count; | 386 | long new_count; |
385 | 387 | ||
388 | if (!tracing_is_on()) | ||
389 | return; | ||
390 | |||
391 | /* unlimited? */ | ||
392 | if (!mapper) { | ||
393 | trace_dump_stack(STACK_SKIP); | ||
394 | return; | ||
395 | } | ||
396 | |||
397 | count = (long *)ftrace_func_mapper_find_ip(mapper, ip); | ||
398 | |||
386 | /* | 399 | /* |
387 | * Stack traces should only execute the number of times the | 400 | * Stack traces should only execute the number of times the |
388 | * user specified in the counter. | 401 | * user specified in the counter. |
389 | */ | 402 | */ |
390 | do { | 403 | do { |
391 | |||
392 | if (!tracing_is_on()) | ||
393 | return; | ||
394 | |||
395 | old_count = *count; | 404 | old_count = *count; |
396 | 405 | ||
397 | if (!old_count) | 406 | if (!old_count) |
398 | return; | 407 | return; |
399 | 408 | ||
400 | /* unlimited? */ | ||
401 | if (old_count == -1) { | ||
402 | trace_dump_stack(STACK_SKIP); | ||
403 | return; | ||
404 | } | ||
405 | |||
406 | new_count = old_count - 1; | 409 | new_count = old_count - 1; |
407 | new_count = cmpxchg(count, old_count, new_count); | 410 | new_count = cmpxchg(count, old_count, new_count); |
408 | if (new_count == old_count) | 411 | if (new_count == old_count) |
409 | trace_dump_stack(STACK_SKIP); | 412 | trace_dump_stack(STACK_SKIP); |
410 | 413 | ||
414 | if (!tracing_is_on()) | ||
415 | return; | ||
416 | |||
411 | } while (new_count != old_count); | 417 | } while (new_count != old_count); |
412 | } | 418 | } |
413 | 419 | ||
414 | static int update_count(void **data) | 420 | static int update_count(struct ftrace_probe_ops *ops, unsigned long ip) |
415 | { | 421 | { |
416 | unsigned long *count = (long *)data; | 422 | struct ftrace_func_mapper *mapper = ops->private_data; |
423 | long *count = NULL; | ||
417 | 424 | ||
418 | if (!*count) | 425 | if (mapper) |
419 | return 0; | 426 | count = (long *)ftrace_func_mapper_find_ip(mapper, ip); |
420 | 427 | ||
421 | if (*count != -1) | 428 | if (count) { |
429 | if (*count <= 0) | ||
430 | return 0; | ||
422 | (*count)--; | 431 | (*count)--; |
432 | } | ||
423 | 433 | ||
424 | return 1; | 434 | return 1; |
425 | } | 435 | } |
@@ -428,7 +438,7 @@ static void | |||
428 | ftrace_dump_probe(unsigned long ip, unsigned long parent_ip, | 438 | ftrace_dump_probe(unsigned long ip, unsigned long parent_ip, |
429 | struct ftrace_probe_ops *ops, void **data) | 439 | struct ftrace_probe_ops *ops, void **data) |
430 | { | 440 | { |
431 | if (update_count(data)) | 441 | if (update_count(ops, ip)) |
432 | ftrace_dump(DUMP_ALL); | 442 | ftrace_dump(DUMP_ALL); |
433 | } | 443 | } |
434 | 444 | ||
@@ -437,22 +447,26 @@ static void | |||
437 | ftrace_cpudump_probe(unsigned long ip, unsigned long parent_ip, | 447 | ftrace_cpudump_probe(unsigned long ip, unsigned long parent_ip, |
438 | struct ftrace_probe_ops *ops, void **data) | 448 | struct ftrace_probe_ops *ops, void **data) |
439 | { | 449 | { |
440 | if (update_count(data)) | 450 | if (update_count(ops, ip)) |
441 | ftrace_dump(DUMP_ORIG); | 451 | ftrace_dump(DUMP_ORIG); |
442 | } | 452 | } |
443 | 453 | ||
444 | static int | 454 | static int |
445 | ftrace_probe_print(const char *name, struct seq_file *m, | 455 | ftrace_probe_print(const char *name, struct seq_file *m, |
446 | unsigned long ip, void *data) | 456 | unsigned long ip, struct ftrace_probe_ops *ops) |
447 | { | 457 | { |
448 | long count = (long)data; | 458 | struct ftrace_func_mapper *mapper = ops->private_data; |
459 | long *count = NULL; | ||
449 | 460 | ||
450 | seq_printf(m, "%ps:%s", (void *)ip, name); | 461 | seq_printf(m, "%ps:%s", (void *)ip, name); |
451 | 462 | ||
452 | if (count == -1) | 463 | if (mapper) |
453 | seq_puts(m, ":unlimited\n"); | 464 | count = (long *)ftrace_func_mapper_find_ip(mapper, ip); |
465 | |||
466 | if (count) | ||
467 | seq_printf(m, ":count=%ld\n", *count); | ||
454 | else | 468 | else |
455 | seq_printf(m, ":count=%ld\n", count); | 469 | seq_puts(m, ":unlimited\n"); |
456 | 470 | ||
457 | return 0; | 471 | return 0; |
458 | } | 472 | } |
@@ -461,55 +475,82 @@ static int | |||
461 | ftrace_traceon_print(struct seq_file *m, unsigned long ip, | 475 | ftrace_traceon_print(struct seq_file *m, unsigned long ip, |
462 | struct ftrace_probe_ops *ops, void *data) | 476 | struct ftrace_probe_ops *ops, void *data) |
463 | { | 477 | { |
464 | return ftrace_probe_print("traceon", m, ip, data); | 478 | return ftrace_probe_print("traceon", m, ip, ops); |
465 | } | 479 | } |
466 | 480 | ||
467 | static int | 481 | static int |
468 | ftrace_traceoff_print(struct seq_file *m, unsigned long ip, | 482 | ftrace_traceoff_print(struct seq_file *m, unsigned long ip, |
469 | struct ftrace_probe_ops *ops, void *data) | 483 | struct ftrace_probe_ops *ops, void *data) |
470 | { | 484 | { |
471 | return ftrace_probe_print("traceoff", m, ip, data); | 485 | return ftrace_probe_print("traceoff", m, ip, ops); |
472 | } | 486 | } |
473 | 487 | ||
474 | static int | 488 | static int |
475 | ftrace_stacktrace_print(struct seq_file *m, unsigned long ip, | 489 | ftrace_stacktrace_print(struct seq_file *m, unsigned long ip, |
476 | struct ftrace_probe_ops *ops, void *data) | 490 | struct ftrace_probe_ops *ops, void *data) |
477 | { | 491 | { |
478 | return ftrace_probe_print("stacktrace", m, ip, data); | 492 | return ftrace_probe_print("stacktrace", m, ip, ops); |
479 | } | 493 | } |
480 | 494 | ||
481 | static int | 495 | static int |
482 | ftrace_dump_print(struct seq_file *m, unsigned long ip, | 496 | ftrace_dump_print(struct seq_file *m, unsigned long ip, |
483 | struct ftrace_probe_ops *ops, void *data) | 497 | struct ftrace_probe_ops *ops, void *data) |
484 | { | 498 | { |
485 | return ftrace_probe_print("dump", m, ip, data); | 499 | return ftrace_probe_print("dump", m, ip, ops); |
486 | } | 500 | } |
487 | 501 | ||
488 | static int | 502 | static int |
489 | ftrace_cpudump_print(struct seq_file *m, unsigned long ip, | 503 | ftrace_cpudump_print(struct seq_file *m, unsigned long ip, |
490 | struct ftrace_probe_ops *ops, void *data) | 504 | struct ftrace_probe_ops *ops, void *data) |
491 | { | 505 | { |
492 | return ftrace_probe_print("cpudump", m, ip, data); | 506 | return ftrace_probe_print("cpudump", m, ip, ops); |
507 | } | ||
508 | |||
509 | |||
510 | static int | ||
511 | ftrace_count_init(struct ftrace_probe_ops *ops, unsigned long ip, | ||
512 | void **data) | ||
513 | { | ||
514 | struct ftrace_func_mapper *mapper = ops->private_data; | ||
515 | |||
516 | return ftrace_func_mapper_add_ip(mapper, ip, *data); | ||
517 | } | ||
518 | |||
519 | static void | ||
520 | ftrace_count_free(struct ftrace_probe_ops *ops, unsigned long ip, | ||
521 | void **_data) | ||
522 | { | ||
523 | struct ftrace_func_mapper *mapper = ops->private_data; | ||
524 | |||
525 | ftrace_func_mapper_remove_ip(mapper, ip); | ||
493 | } | 526 | } |
494 | 527 | ||
495 | static struct ftrace_probe_ops traceon_count_probe_ops = { | 528 | static struct ftrace_probe_ops traceon_count_probe_ops = { |
496 | .func = ftrace_traceon_count, | 529 | .func = ftrace_traceon_count, |
497 | .print = ftrace_traceon_print, | 530 | .print = ftrace_traceon_print, |
531 | .init = ftrace_count_init, | ||
532 | .free = ftrace_count_free, | ||
498 | }; | 533 | }; |
499 | 534 | ||
500 | static struct ftrace_probe_ops traceoff_count_probe_ops = { | 535 | static struct ftrace_probe_ops traceoff_count_probe_ops = { |
501 | .func = ftrace_traceoff_count, | 536 | .func = ftrace_traceoff_count, |
502 | .print = ftrace_traceoff_print, | 537 | .print = ftrace_traceoff_print, |
538 | .init = ftrace_count_init, | ||
539 | .free = ftrace_count_free, | ||
503 | }; | 540 | }; |
504 | 541 | ||
505 | static struct ftrace_probe_ops stacktrace_count_probe_ops = { | 542 | static struct ftrace_probe_ops stacktrace_count_probe_ops = { |
506 | .func = ftrace_stacktrace_count, | 543 | .func = ftrace_stacktrace_count, |
507 | .print = ftrace_stacktrace_print, | 544 | .print = ftrace_stacktrace_print, |
545 | .init = ftrace_count_init, | ||
546 | .free = ftrace_count_free, | ||
508 | }; | 547 | }; |
509 | 548 | ||
510 | static struct ftrace_probe_ops dump_probe_ops = { | 549 | static struct ftrace_probe_ops dump_probe_ops = { |
511 | .func = ftrace_dump_probe, | 550 | .func = ftrace_dump_probe, |
512 | .print = ftrace_dump_print, | 551 | .print = ftrace_dump_print, |
552 | .init = ftrace_count_init, | ||
553 | .free = ftrace_count_free, | ||
513 | }; | 554 | }; |
514 | 555 | ||
515 | static struct ftrace_probe_ops cpudump_probe_ops = { | 556 | static struct ftrace_probe_ops cpudump_probe_ops = { |
@@ -558,6 +599,12 @@ ftrace_trace_probe_callback(struct ftrace_probe_ops *ops, | |||
558 | if (!strlen(number)) | 599 | if (!strlen(number)) |
559 | goto out_reg; | 600 | goto out_reg; |
560 | 601 | ||
602 | if (!ops->private_data) { | ||
603 | ops->private_data = allocate_ftrace_func_mapper(); | ||
604 | if (!ops->private_data) | ||
605 | return -ENOMEM; | ||
606 | } | ||
607 | |||
561 | /* | 608 | /* |
562 | * We use the callback data field (which is a pointer) | 609 | * We use the callback data field (which is a pointer) |
563 | * as our counter. | 610 | * as our counter. |