diff options
author | Markus Metzger <markus.t.metzger@intel.com> | 2008-01-30 07:31:09 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-01-30 07:31:09 -0500 |
commit | eee3af4a2c83a97fff107ddc445d9df6fded9ce4 (patch) | |
tree | a7e9179b82b4df9e4cf6e810c54309324589395b /arch/x86/kernel/ptrace.c | |
parent | 7796931f542518092d1fd2fb7cf1f1d96e0cd4b5 (diff) |
x86, ptrace: support for branch trace store(BTS)
Resend using different mail client
Changes to the last version:
- split implementation into two layers: ds/bts and ptrace
- renamed TIF's
- save/restore ds save area msr in __switch_to_xtra()
- make block-stepping only look at BTF bit
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/kernel/ptrace.c')
-rw-r--r-- | arch/x86/kernel/ptrace.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c index 3399c1be79b8..8d0dd8b5effe 100644 --- a/arch/x86/kernel/ptrace.c +++ b/arch/x86/kernel/ptrace.c | |||
@@ -2,6 +2,9 @@ | |||
2 | /* | 2 | /* |
3 | * Pentium III FXSR, SSE support | 3 | * Pentium III FXSR, SSE support |
4 | * Gareth Hughes <gareth@valinux.com>, May 2000 | 4 | * Gareth Hughes <gareth@valinux.com>, May 2000 |
5 | * | ||
6 | * BTS tracing | ||
7 | * Markus Metzger <markus.t.metzger@intel.com>, Dec 2007 | ||
5 | */ | 8 | */ |
6 | 9 | ||
7 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
@@ -26,6 +29,14 @@ | |||
26 | #include <asm/desc.h> | 29 | #include <asm/desc.h> |
27 | #include <asm/prctl.h> | 30 | #include <asm/prctl.h> |
28 | #include <asm/proto.h> | 31 | #include <asm/proto.h> |
32 | #include <asm/ds.h> | ||
33 | |||
34 | |||
35 | /* | ||
36 | * The maximal size of a BTS buffer per traced task in number of BTS | ||
37 | * records. | ||
38 | */ | ||
39 | #define PTRACE_BTS_BUFFER_MAX 4000 | ||
29 | 40 | ||
30 | /* | 41 | /* |
31 | * does not yet catch signals sent when the child dies. | 42 | * does not yet catch signals sent when the child dies. |
@@ -455,6 +466,165 @@ static int ptrace_set_debugreg(struct task_struct *child, | |||
455 | return 0; | 466 | return 0; |
456 | } | 467 | } |
457 | 468 | ||
469 | static int ptrace_bts_max_buffer_size(void) | ||
470 | { | ||
471 | return PTRACE_BTS_BUFFER_MAX; | ||
472 | } | ||
473 | |||
474 | static int ptrace_bts_get_buffer_size(struct task_struct *child) | ||
475 | { | ||
476 | if (!child->thread.ds_area_msr) | ||
477 | return -ENXIO; | ||
478 | |||
479 | return ds_get_bts_size((void *)child->thread.ds_area_msr); | ||
480 | } | ||
481 | |||
482 | static int ptrace_bts_get_index(struct task_struct *child) | ||
483 | { | ||
484 | if (!child->thread.ds_area_msr) | ||
485 | return -ENXIO; | ||
486 | |||
487 | return ds_get_bts_index((void *)child->thread.ds_area_msr); | ||
488 | } | ||
489 | |||
490 | static int ptrace_bts_read_record(struct task_struct *child, | ||
491 | long index, | ||
492 | struct bts_struct __user *out) | ||
493 | { | ||
494 | struct bts_struct ret; | ||
495 | int retval; | ||
496 | |||
497 | if (!child->thread.ds_area_msr) | ||
498 | return -ENXIO; | ||
499 | |||
500 | retval = ds_read_bts((void *)child->thread.ds_area_msr, | ||
501 | index, &ret); | ||
502 | if (retval) | ||
503 | return retval; | ||
504 | |||
505 | if (copy_to_user(out, &ret, sizeof(ret))) | ||
506 | return -EFAULT; | ||
507 | |||
508 | return sizeof(ret); | ||
509 | } | ||
510 | |||
511 | static int ptrace_bts_write_record(struct task_struct *child, | ||
512 | const struct bts_struct *in) | ||
513 | { | ||
514 | int retval; | ||
515 | |||
516 | if (!child->thread.ds_area_msr) | ||
517 | return -ENXIO; | ||
518 | |||
519 | retval = ds_write_bts((void *)child->thread.ds_area_msr, in); | ||
520 | if (retval) | ||
521 | return retval; | ||
522 | |||
523 | return sizeof(*in); | ||
524 | } | ||
525 | |||
526 | static int ptrace_bts_config(struct task_struct *child, | ||
527 | unsigned long options) | ||
528 | { | ||
529 | unsigned long debugctl_mask = ds_debugctl_mask(); | ||
530 | int retval; | ||
531 | |||
532 | retval = ptrace_bts_get_buffer_size(child); | ||
533 | if (retval < 0) | ||
534 | return retval; | ||
535 | if (retval == 0) | ||
536 | return -ENXIO; | ||
537 | |||
538 | if (options & PTRACE_BTS_O_TRACE_TASK) { | ||
539 | child->thread.debugctlmsr |= debugctl_mask; | ||
540 | set_tsk_thread_flag(child, TIF_DEBUGCTLMSR); | ||
541 | } else { | ||
542 | /* there is no way for us to check whether we 'own' | ||
543 | * the respective bits in the DEBUGCTL MSR, we're | ||
544 | * about to clear */ | ||
545 | child->thread.debugctlmsr &= ~debugctl_mask; | ||
546 | |||
547 | if (!child->thread.debugctlmsr) | ||
548 | clear_tsk_thread_flag(child, TIF_DEBUGCTLMSR); | ||
549 | } | ||
550 | |||
551 | if (options & PTRACE_BTS_O_TIMESTAMPS) | ||
552 | set_tsk_thread_flag(child, TIF_BTS_TRACE_TS); | ||
553 | else | ||
554 | clear_tsk_thread_flag(child, TIF_BTS_TRACE_TS); | ||
555 | |||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | static int ptrace_bts_status(struct task_struct *child) | ||
560 | { | ||
561 | unsigned long debugctl_mask = ds_debugctl_mask(); | ||
562 | int retval, status = 0; | ||
563 | |||
564 | retval = ptrace_bts_get_buffer_size(child); | ||
565 | if (retval < 0) | ||
566 | return retval; | ||
567 | if (retval == 0) | ||
568 | return -ENXIO; | ||
569 | |||
570 | if (ptrace_bts_get_buffer_size(child) <= 0) | ||
571 | return -ENXIO; | ||
572 | |||
573 | if (test_tsk_thread_flag(child, TIF_DEBUGCTLMSR) && | ||
574 | child->thread.debugctlmsr & debugctl_mask) | ||
575 | status |= PTRACE_BTS_O_TRACE_TASK; | ||
576 | if (test_tsk_thread_flag(child, TIF_BTS_TRACE_TS)) | ||
577 | status |= PTRACE_BTS_O_TIMESTAMPS; | ||
578 | |||
579 | return status; | ||
580 | } | ||
581 | |||
582 | static int ptrace_bts_allocate_bts(struct task_struct *child, | ||
583 | int size_in_records) | ||
584 | { | ||
585 | int retval = 0; | ||
586 | void *ds; | ||
587 | |||
588 | if (size_in_records < 0) | ||
589 | return -EINVAL; | ||
590 | |||
591 | if (size_in_records > ptrace_bts_max_buffer_size()) | ||
592 | return -EINVAL; | ||
593 | |||
594 | if (size_in_records == 0) { | ||
595 | ptrace_bts_config(child, /* options = */ 0); | ||
596 | } else { | ||
597 | retval = ds_allocate(&ds, size_in_records); | ||
598 | if (retval) | ||
599 | return retval; | ||
600 | } | ||
601 | |||
602 | if (child->thread.ds_area_msr) | ||
603 | ds_free((void **)&child->thread.ds_area_msr); | ||
604 | |||
605 | child->thread.ds_area_msr = (unsigned long)ds; | ||
606 | if (child->thread.ds_area_msr) | ||
607 | set_tsk_thread_flag(child, TIF_DS_AREA_MSR); | ||
608 | else | ||
609 | clear_tsk_thread_flag(child, TIF_DS_AREA_MSR); | ||
610 | |||
611 | return retval; | ||
612 | } | ||
613 | |||
614 | void ptrace_bts_take_timestamp(struct task_struct *tsk, | ||
615 | enum bts_qualifier qualifier) | ||
616 | { | ||
617 | struct bts_struct rec = { | ||
618 | .qualifier = qualifier, | ||
619 | .variant.timestamp = sched_clock() | ||
620 | }; | ||
621 | |||
622 | if (ptrace_bts_get_buffer_size(tsk) <= 0) | ||
623 | return; | ||
624 | |||
625 | ptrace_bts_write_record(tsk, &rec); | ||
626 | } | ||
627 | |||
458 | /* | 628 | /* |
459 | * Called by kernel/ptrace.c when detaching.. | 629 | * Called by kernel/ptrace.c when detaching.. |
460 | * | 630 | * |
@@ -466,6 +636,11 @@ void ptrace_disable(struct task_struct *child) | |||
466 | #ifdef TIF_SYSCALL_EMU | 636 | #ifdef TIF_SYSCALL_EMU |
467 | clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); | 637 | clear_tsk_thread_flag(child, TIF_SYSCALL_EMU); |
468 | #endif | 638 | #endif |
639 | ptrace_bts_config(child, /* options = */ 0); | ||
640 | if (child->thread.ds_area_msr) { | ||
641 | ds_free((void **)&child->thread.ds_area_msr); | ||
642 | clear_tsk_thread_flag(child, TIF_DS_AREA_MSR); | ||
643 | } | ||
469 | } | 644 | } |
470 | 645 | ||
471 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 646 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
@@ -626,6 +801,36 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
626 | break; | 801 | break; |
627 | #endif | 802 | #endif |
628 | 803 | ||
804 | case PTRACE_BTS_MAX_BUFFER_SIZE: | ||
805 | ret = ptrace_bts_max_buffer_size(); | ||
806 | break; | ||
807 | |||
808 | case PTRACE_BTS_ALLOCATE_BUFFER: | ||
809 | ret = ptrace_bts_allocate_bts(child, data); | ||
810 | break; | ||
811 | |||
812 | case PTRACE_BTS_GET_BUFFER_SIZE: | ||
813 | ret = ptrace_bts_get_buffer_size(child); | ||
814 | break; | ||
815 | |||
816 | case PTRACE_BTS_GET_INDEX: | ||
817 | ret = ptrace_bts_get_index(child); | ||
818 | break; | ||
819 | |||
820 | case PTRACE_BTS_READ_RECORD: | ||
821 | ret = ptrace_bts_read_record | ||
822 | (child, data, | ||
823 | (struct bts_struct __user *) addr); | ||
824 | break; | ||
825 | |||
826 | case PTRACE_BTS_CONFIG: | ||
827 | ret = ptrace_bts_config(child, data); | ||
828 | break; | ||
829 | |||
830 | case PTRACE_BTS_STATUS: | ||
831 | ret = ptrace_bts_status(child); | ||
832 | break; | ||
833 | |||
629 | default: | 834 | default: |
630 | ret = ptrace_request(child, request, addr, data); | 835 | ret = ptrace_request(child, request, addr, data); |
631 | break; | 836 | break; |
@@ -809,6 +1014,13 @@ asmlinkage long sys32_ptrace(long request, u32 pid, u32 addr, u32 data) | |||
809 | case PTRACE_SETOPTIONS: | 1014 | case PTRACE_SETOPTIONS: |
810 | case PTRACE_SET_THREAD_AREA: | 1015 | case PTRACE_SET_THREAD_AREA: |
811 | case PTRACE_GET_THREAD_AREA: | 1016 | case PTRACE_GET_THREAD_AREA: |
1017 | case PTRACE_BTS_MAX_BUFFER_SIZE: | ||
1018 | case PTRACE_BTS_ALLOCATE_BUFFER: | ||
1019 | case PTRACE_BTS_GET_BUFFER_SIZE: | ||
1020 | case PTRACE_BTS_GET_INDEX: | ||
1021 | case PTRACE_BTS_READ_RECORD: | ||
1022 | case PTRACE_BTS_CONFIG: | ||
1023 | case PTRACE_BTS_STATUS: | ||
812 | return sys_ptrace(request, pid, addr, data); | 1024 | return sys_ptrace(request, pid, addr, data); |
813 | 1025 | ||
814 | default: | 1026 | default: |