Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
dtrace: core and x86
This implements DTrace's core kernel (linked-in) components, including platform-dependent portions for x86. (Most of this machinery is not used until the next commit.) Signed-off-by: Nick Alcock <nick.alcock@oracle.com> Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com> Signed-off-by: Tomas Jedlicka <tomas.jedlicka@oracle.com> Signed-off-by: Eugene Loh <eugene.loh@oracle.com> Signed-off-by: David Mc Lean <david.mclean@oracle.com> Signed-off-by: Vincent Lim <vincent.lim@oracle.com>
- Loading branch information
1 parent
0fd5e39
commit 62a2550ef447ac372a5bb92e5baad3e2932865c1
Showing
85 changed files
with
7,133 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0 */ | ||
|
|
||
| /* | ||
| * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. | ||
| */ | ||
|
|
||
| #ifndef _X86_DTRACE_ARCH_H | ||
| #define _X86_DTRACE_ARCH_H | ||
|
|
||
| /* Number of arguments stored inside the mstate. */ | ||
| #define DTRACE_MSTATE_ARGS_MAX 6 | ||
|
|
||
| typedef uint8_t asm_instr_t; | ||
|
|
||
| typedef int (*prov_exit_f)(void); | ||
|
|
||
| /* | ||
| * Structure to hold DTrace specific information about modules (including the | ||
| * core kernel module). Note that each module (and the main kernel) already | ||
| * has one field that relates to probing: | ||
| * - pdata: pointer to a dtrace_module struct (for DTrace) | ||
| */ | ||
| struct dtrace_module { | ||
| int enabled_cnt; | ||
| prov_exit_f prov_exit; /* Called with module_mutex held */ | ||
| }; | ||
|
|
||
| #endif /* _X86_DTRACE_ARCH_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0 */ | ||
|
|
||
| /* Copyright (C) 2013-2014 Oracle, Inc. */ | ||
|
|
||
| #ifndef _ASM_X86_DTRACE_CPUINFO_H_ | ||
| #define _ASM_X86_DTRACE_CPUINFO_H_ | ||
|
|
||
| #include <asm/processor.h> | ||
|
|
||
| typedef struct cpuinfo_x86 cpuinfo_arch_t; | ||
|
|
||
| #define dtrace_cpuinfo_chip(ci) ((ci)->phys_proc_id) | ||
|
|
||
| #endif /* _ASM_X86_DTRACE_CPUINFO_H_ */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0 */ | ||
| /* | ||
| * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. | ||
| */ | ||
|
|
||
| #ifndef _X86_DTRACE_UTIL_H | ||
| #define _X86_DTRACE_UTIL_H | ||
|
|
||
| #ifndef __ASSEMBLY__ | ||
|
|
||
| #include <asm/dtrace_arch.h> | ||
| #include <asm/ptrace.h> | ||
|
|
||
| #endif | ||
|
|
||
| #endif /* _X86_DTRACE_UTIL_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,244 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0 */ | ||
| /* | ||
| * FILE: dtrace_util.c | ||
| * DESCRIPTION: Dynamic Tracing: Architecture utility functions | ||
| * | ||
| * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. | ||
| */ | ||
|
|
||
| #include <linux/dtrace_cpu.h> | ||
| #include <linux/dtrace_os.h> | ||
| #include <linux/dtrace_task_impl.h> | ||
| #include <linux/kdebug.h> | ||
| #include <linux/mm.h> | ||
| #include <linux/module.h> | ||
| #include <linux/memory.h> | ||
| #include <linux/notifier.h> | ||
| #include <linux/ptrace.h> | ||
| #include <linux/sched.h> | ||
| #include <linux/slab.h> | ||
| #include <linux/uaccess.h> | ||
| #include <linux/sched/task_stack.h> | ||
| #include <asm/insn.h> | ||
| #include <asm/pgtable.h> | ||
| #include <asm/ptrace.h> | ||
| #include <asm/text-patching.h> | ||
| #include <asm/dtrace_arch.h> | ||
| #include <asm/dtrace_util.h> | ||
|
|
||
| int dtrace_instr_size(const asm_instr_t *addr) | ||
| { | ||
| struct insn insn; | ||
|
|
||
| kernel_insn_init(&insn, addr, MAX_INSN_SIZE); | ||
| insn_get_length(&insn); | ||
|
|
||
| return insn_complete(&insn) ? insn.length : -1; | ||
| } | ||
| EXPORT_SYMBOL(dtrace_instr_size); | ||
|
|
||
| /* | ||
| * Move the instruction pointer forward to the next instruction, effectiely | ||
| * skipping the current one. | ||
| */ | ||
| static void dtrace_skip_instruction(struct pt_regs *regs) | ||
| { | ||
| int delta; | ||
|
|
||
| delta = dtrace_instr_size((asm_instr_t *)regs->ip); | ||
| BUG_ON(delta <= 0); | ||
|
|
||
| regs->ip += delta; | ||
| } | ||
|
|
||
| void dtrace_handle_badaddr(struct pt_regs *regs) | ||
| { | ||
| unsigned long addr = read_cr2(); | ||
|
|
||
| DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR); | ||
| this_cpu_core->cpuc_dtrace_illval = addr; | ||
|
|
||
| dtrace_skip_instruction(regs); | ||
| } | ||
|
|
||
| /* | ||
| * Trap notification handler. | ||
| */ | ||
| int dtrace_die_notifier(struct notifier_block *nb, unsigned long val, | ||
| void *args) | ||
| { | ||
| struct die_args *dargs = args; | ||
|
|
||
| switch (val) { | ||
| case DIE_PAGE_FAULT: { | ||
| if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT)) | ||
| return NOTIFY_DONE; | ||
|
|
||
| dtrace_handle_badaddr(dargs->regs); | ||
|
|
||
| return NOTIFY_OK | NOTIFY_STOP_MASK; | ||
| } | ||
| case DIE_GPF: { | ||
| if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT)) | ||
| return NOTIFY_DONE; | ||
|
|
||
| dtrace_handle_badaddr(dargs->regs); | ||
|
|
||
| return NOTIFY_OK | NOTIFY_STOP_MASK; | ||
| } | ||
| fallthrough; | ||
| default: | ||
| return NOTIFY_DONE; | ||
| } | ||
| } | ||
|
|
||
| static inline int dtrace_bad_address(void *addr) | ||
| { | ||
| unsigned long dummy; | ||
|
|
||
| return get_kernel_nofault(dummy, (unsigned long *)addr); | ||
| } | ||
|
|
||
| static int dtrace_user_addr_is_exec(uintptr_t addr) | ||
| { | ||
| struct mm_struct *mm = current->mm; | ||
| pgd_t *pgd; | ||
|
|
||
| #if CONFIG_PGTABLE_LEVELS > 3 | ||
| p4d_t *p4d; | ||
| #endif | ||
|
|
||
| pud_t *pud; | ||
| pmd_t *pmd; | ||
| pte_t *pte; | ||
| unsigned long flags; | ||
| int ret = 0; | ||
|
|
||
| if (mm == NULL) | ||
| return 0; | ||
|
|
||
| addr &= PAGE_MASK; | ||
|
|
||
| local_irq_save(flags); | ||
|
|
||
| pgd = pgd_offset(mm, addr); | ||
| if (dtrace_bad_address(pgd)) | ||
| goto out; | ||
| if (pgd_none(*pgd) || !pgd_present(*pgd)) | ||
| goto out; | ||
|
|
||
| #if CONFIG_PGTABLE_LEVELS > 3 | ||
| p4d = p4d_offset(pgd, addr); | ||
| if (dtrace_bad_address(p4d)) | ||
| goto out; | ||
| if (p4d_none(*p4d) || !p4d_present(*p4d)) | ||
| goto out; | ||
|
|
||
| pud = pud_offset(p4d, addr); | ||
| #else | ||
| pud = pud_offset(pgd, addr); | ||
| #endif | ||
|
|
||
| if (dtrace_bad_address(pud)) | ||
| goto out; | ||
| if (pud_none(*pud) || !pud_present(*pud)) | ||
| goto out; | ||
| if (unlikely(pud_large(*pud))) { | ||
| pte = (pte_t *)pud; | ||
| if (dtrace_bad_address(pte)) | ||
| goto out; | ||
|
|
||
| ret = pte_exec(*pte); | ||
| goto out; | ||
| } | ||
|
|
||
| pmd = pmd_offset(pud, addr); | ||
| if (dtrace_bad_address(pmd)) | ||
| goto out; | ||
| if (pmd_none(*pmd)) | ||
| goto out; | ||
| if (unlikely(pmd_large(*pmd) || !pmd_present(*pmd))) { | ||
| pte = (pte_t *)pmd; | ||
| if (dtrace_bad_address(pte)) | ||
| goto out; | ||
|
|
||
| ret = pte_exec(*pte); | ||
| goto out; | ||
| } | ||
|
|
||
| pte = pte_offset_map(pmd, addr); | ||
| if (dtrace_bad_address(pte)) | ||
| goto out; | ||
| if (pte_protnone(*pte)) | ||
| goto out; | ||
| if ((pte_flags(*pte) & (_PAGE_PRESENT|_PAGE_USER|_PAGE_SPECIAL)) != | ||
| (_PAGE_PRESENT|_PAGE_USER)) | ||
| goto out; | ||
|
|
||
| ret = pte_exec(*pte); | ||
|
|
||
| out: | ||
| local_irq_restore(flags); | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| void dtrace_user_stacktrace(struct stacktrace_state *st) | ||
| { | ||
| struct pt_regs *regs = current_pt_regs(); | ||
| uint64_t *pcs = st->pcs; | ||
| int limit = st->limit; | ||
| unsigned long *bos; | ||
| unsigned long *sp = (unsigned long *)user_stack_pointer(regs); | ||
| int ret; | ||
|
|
||
| if (!user_mode(regs)) | ||
| goto out; | ||
|
|
||
| if (current->dt_task == NULL) | ||
| goto out; | ||
|
|
||
| bos = current->dt_task->dt_ustack; | ||
|
|
||
| st->depth = 1; | ||
| if (pcs) | ||
| *pcs++ = (uint64_t)instruction_pointer(regs); | ||
| limit--; | ||
|
|
||
| if (!limit) | ||
| goto out; | ||
|
|
||
| while (sp <= bos && limit) { | ||
| unsigned long pc; | ||
|
|
||
| pagefault_disable(); | ||
| ret = __copy_from_user_inatomic(&pc, sp, sizeof(pc)); | ||
| pagefault_enable(); | ||
|
|
||
| if (ret) | ||
| break; | ||
|
|
||
| if (dtrace_user_addr_is_exec(pc)) { | ||
| if (pcs) | ||
| *pcs++ = pc; | ||
| limit--; | ||
| st->depth++; | ||
| } | ||
|
|
||
| sp++; | ||
| } | ||
|
|
||
| out: | ||
| if (pcs) { | ||
| while (limit--) | ||
| *pcs++ = 0; | ||
| } | ||
| } | ||
|
|
||
| void dtrace_mod_pdata_init(struct dtrace_module *pdata) | ||
| { | ||
| } | ||
|
|
||
| void dtrace_mod_pdata_cleanup(struct dtrace_module *pdata) | ||
| { | ||
| } |
Oops, something went wrong.