From: Kris Van Hees Date: Sun, 9 Sep 2012 21:27:14 +0000 (-0400) Subject: dtrace: add psinfo/cpuinfo OS level support X-Git-Tag: v4.1.12-92~313^2~119 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=e97ad59a3e37b73b88751cab56335770c64a3970;p=users%2Fjedix%2Flinux-maple.git dtrace: add psinfo/cpuinfo OS level support Added a member to the task structure, to hold DTrace specific process info. It stores (up to) the first 79 characters of the command line (with arguments), as required for the pr_psargs elements in psinfo_t. It also stores the number of initial arguments (pr_argc), and the initial argument (pr_argv) and environment variable (pr_envp) vectors. This process information is pre-populated when an execve takes place in the current task. Note that if a process alters the arguments, this altertation will be visible when pr_argv is consulted, and thus returned argument strings may not match the originally provided values. Enforce that invop handlers return a uint8 value, to be used in the future for knowing what instruction to emulate upon return from an invop trap. Added per-cpu CPU information that is used to provide cpuinfo_t data. Signed-off-by: Kris Van Hees --- diff --git a/fs/exec.c b/fs/exec.c index cc95013de1e6..36f4f76763c1 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1588,6 +1588,9 @@ static int do_execveat_common(int fd, struct filename *filename, goto out; /* execve succeeded */ +#ifdef CONFIG_DTRACE + current->dtrace_psinfo = dtrace_psinfo_alloc(current); +#endif current->fs->in_exec = 0; current->in_execve = 0; acct_update_integrals(current); diff --git a/include/linux/dtrace_cpu.h b/include/linux/dtrace_cpu.h index 37fd6d38454e..db02a84ac2e0 100644 --- a/include/linux/dtrace_cpu.h +++ b/include/linux/dtrace_cpu.h @@ -2,7 +2,6 @@ #define _DTRACE_CPU_H_ #include -#include #include #define CPUC_SIZE (sizeof (uint16_t) + sizeof(uint8_t) + \ @@ -21,10 +20,10 @@ typedef struct cpu_core { ktime_t cpu_dtrace_chilled; } cpu_core_t; -DECLARE_PER_CPU_SHARED_ALIGNED(cpu_core_t, dtrace_cpu_info); +DECLARE_PER_CPU_SHARED_ALIGNED(cpu_core_t, dtrace_cpu_core); -#define per_cpu_core(cpu) (&per_cpu(dtrace_cpu_info, (cpu))) -#define this_cpu_core (&__get_cpu_var(dtrace_cpu_info)) +#define per_cpu_core(cpu) (&per_cpu(dtrace_cpu_core, (cpu))) +#define this_cpu_core (&__get_cpu_var(dtrace_cpu_core)) #define DTRACE_CPUFLAG_ISSET(flag) \ (this_cpu_core->cpuc_dtrace_flags & (flag)) @@ -55,4 +54,24 @@ DECLARE_PER_CPU_SHARED_ALIGNED(cpu_core_t, dtrace_cpu_info); CPU_DTRACE_BADSTACK) #define CPU_DTRACE_ERROR (CPU_DTRACE_FAULT | CPU_DTRACE_DROP) +typedef uint32_t processorid_t; +typedef uint32_t psetid_t; +typedef uint32_t chipid_t; +typedef uint32_t lgrp_id_t; + +typedef struct cpuinfo { + processorid_t cpu_id; + psetid_t cpu_pset; + chipid_t cpu_chip; + lgrp_id_t cpu_lgrp; + struct cpuinfo_x86 *cpu_info; +} cpuinfo_t; + +DECLARE_PER_CPU_SHARED_ALIGNED(cpuinfo_t, dtrace_cpu_info); + +#define per_cpu_info(cpu) (&per_cpu(dtrace_cpu_info, (cpu))) +#define this_cpu_info (&__get_cpu_var(dtrace_cpu_info)) + +extern void dtrace_cpu_init(void); + #endif /* _DTRACE_CPU_H_ */ diff --git a/include/linux/dtrace_os.h b/include/linux/dtrace_os.h index fe7a17ad728d..4076a7bdde83 100644 --- a/include/linux/dtrace_os.h +++ b/include/linux/dtrace_os.h @@ -5,6 +5,27 @@ #include +#define ASSIST_PR_PSARGS 0 +#define ASSIST_PR_ARGC 1 +#define ASSIST_PR_ARGV 2 +#define ASSIST_PR_ENVP 3 + +#define PR_PSARGS_SZ 80 + +typedef struct dtrace_psinfo { + union { + unsigned long argc; + struct dtrace_psinfo *next; + }; + char **argv; + char **envp; + char psargs[PR_PSARGS_SZ]; +} dtrace_psinfo_t; + +extern dtrace_psinfo_t *dtrace_psinfo_alloc(struct task_struct *); +extern void dtrace_psinfo_free(dtrace_psinfo_t *); + +#ifdef NR_syscalls typedef uint32_t dtrace_id_t; #define DTRACE_IDNONE 0 @@ -26,8 +47,11 @@ extern void dtrace_os_exit(void); extern void dtrace_enable(void); extern void dtrace_disable(void); -extern void dtrace_invop_add(int (*func)(struct pt_regs *)); -extern void dtrace_invop_remove(int (*func)(struct pt_regs *)); +extern void dtrace_invop_add(uint8_t (*func)(struct pt_regs *)); +extern void dtrace_invop_remove(uint8_t (*func)(struct pt_regs *)); + +extern void dtrace_invop_enable(uint8_t *); +extern void dtrace_invop_disable(uint8_t *, uint8_t); typedef void (*sys_call_ptr_t)(void); typedef long (*dt_sys_call_t)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, @@ -78,4 +102,6 @@ typedef void *(fbt_provide_fn)(struct module *, char *, uint8_t, extern void dtrace_fbt_init(fbt_provide_fn); +#endif + #endif /* _DTRACE_OS_H_ */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 548a3b6c0037..0240d52f1ed1 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -58,6 +58,7 @@ struct sched_param { #include #include #include +#include #include @@ -1719,6 +1720,7 @@ struct task_struct { ktime_t dtrace_start; uint8_t dtrace_stop; uint8_t dtrace_sig; + dtrace_psinfo_t *dtrace_psinfo; void *dtrace_helpers; #endif diff --git a/kernel/dtrace/dtrace_cpu.c b/kernel/dtrace/dtrace_cpu.c index 4b1d249c70dc..632b92f5faa0 100644 --- a/kernel/dtrace/dtrace_cpu.c +++ b/kernel/dtrace/dtrace_cpu.c @@ -6,7 +6,27 @@ */ #include +#include #include -DEFINE_PER_CPU_SHARED_ALIGNED(cpu_core_t, dtrace_cpu_info); +DEFINE_PER_CPU_SHARED_ALIGNED(cpu_core_t, dtrace_cpu_core); +EXPORT_PER_CPU_SYMBOL(dtrace_cpu_core); + +DEFINE_PER_CPU_SHARED_ALIGNED(cpuinfo_t, dtrace_cpu_info); EXPORT_PER_CPU_SYMBOL(dtrace_cpu_info); + +void dtrace_cpu_init(void) +{ + int cpu; + + for_each_present_cpu(cpu) { + struct cpuinfo_x86 *c = &cpu_data(cpu); + cpuinfo_t *i = per_cpu_info(cpu); + + i->cpu_id = cpu; + i->cpu_pset = 0; + i->cpu_chip = c->phys_proc_id; + i->cpu_lgrp = 0; + i->cpu_info = c; + } +} diff --git a/kernel/dtrace/dtrace_os.c b/kernel/dtrace/dtrace_os.c index 4c2c1cbb2c03..732a53870902 100644 --- a/kernel/dtrace/dtrace_os.c +++ b/kernel/dtrace/dtrace_os.c @@ -5,10 +5,12 @@ * Copyright (C) 2010, 2011 Oracle Corporation */ +#include #include #include #include #include +#include #include #include #include @@ -16,16 +18,20 @@ #include #include #include +#include +#include #include #include #include -/* - * Perform OS specific DTrace setup. - */ +/*---------------------------------------------------------------------------*\ +(* OS SPECIFIC DTRACE SETUP *) +\*---------------------------------------------------------------------------*/ struct module *dtrace_kmod = NULL; EXPORT_SYMBOL(dtrace_kmod); +int dtrace_ustackdepth_max = 2048; + void dtrace_os_init(void) { if (dtrace_kmod != NULL) { @@ -42,6 +48,8 @@ void dtrace_os_init(void) dtrace_kmod->state = MODULE_STATE_LIVE; strlcpy(dtrace_kmod->name, "vmlinux", MODULE_NAME_LEN); + + dtrace_sdt_register(dtrace_kmod); } EXPORT_SYMBOL(dtrace_os_init); @@ -58,6 +66,156 @@ void dtrace_os_exit(void) } EXPORT_SYMBOL(dtrace_os_exit); +/*---------------------------------------------------------------------------*\ +(* TASK PSINFO SUPPORT *) +\*---------------------------------------------------------------------------*/ +/* + * Allocate a new dtrace_psinfo_t structure. + */ +dtrace_psinfo_t *dtrace_psinfo_alloc(struct task_struct *task) +{ + dtrace_psinfo_t *psinfo; + struct mm_struct *mm; + + psinfo = kzalloc(sizeof(dtrace_psinfo_t), GFP_KERNEL); + if (psinfo == NULL) { + pr_warning("%s: cannot allocate DTrace psinfo structure\n", + __func__); + return NULL; + } + + mm = get_task_mm(task); + if (mm) { + size_t len = mm->arg_end - mm->arg_start; + int i, envc = 0; + char *p; + + /* + * Construct the psargs string. + */ + if (len >= PR_PSARGS_SZ) + len = PR_PSARGS_SZ - 1; + + i = access_process_vm(task, mm->arg_start, psinfo->psargs, + len, 0); + while (i < PR_PSARGS_SZ) + psinfo->psargs[i++] = 0; + + mmput(mm); + + for (i = 0; i < len; i++) { + if (psinfo->psargs[i] == '\0') + psinfo->psargs[i] = ' '; + } + + /* + * Determine the number of arguments. + */ + for (p = (char *)mm->arg_start; p < (char *)mm->arg_end; + psinfo->argc++) { + size_t l = strnlen(p, MAX_ARG_STRLEN); + + if (!l) + break; + + p += l + 1; + } + + psinfo->argv = vmalloc((psinfo->argc + 1) * sizeof(char *)); + + /* + * Now populate the array of argument strings. + */ + for (i = 0, p = (char *)mm->arg_start; i < psinfo->argc; i++) { + psinfo->argv[i] = p; + p += strnlen(p, MAX_ARG_STRLEN) + 1; + } + psinfo->argv[psinfo->argc] = NULL; + + /* + * Determine the number of environment variables. + */ + for (p = (char *)mm->env_start; p < (char *)mm->env_end; + envc++) { + size_t l = strnlen(p, MAX_ARG_STRLEN); + + if (!l) + break; + + p += l + 1; + } + + psinfo->envp = vmalloc((envc + 1) * sizeof(char *)); + + /* + * Now populate the array of environment variable strings. + */ + for (i = 0, p = (char *)mm->env_start; i < envc; i++) { + psinfo->envp[i] = p; + p += strnlen(p, MAX_ARG_STRLEN) + 1; + } + psinfo->envp[envc] = NULL; + } + + return psinfo; +} + +static DEFINE_SPINLOCK(psinfo_lock); +static dtrace_psinfo_t *psinfo_free_list; + +/* + * Work queue handler to clean up psinfo structures for tasks that no longer + * exist. + */ +static void psinfo_cleaner(struct work_struct *work) +{ + unsigned long flags; + dtrace_psinfo_t *psinfo; + + spin_lock_irqsave(&psinfo_lock, flags); + psinfo = psinfo_free_list; + psinfo_free_list = NULL; + spin_unlock_irqrestore(&psinfo_lock, flags); + + while (psinfo) { + dtrace_psinfo_t *next = psinfo->next; + + if (psinfo->argv) + vfree(psinfo->argv); + if (psinfo->envp) + vfree(psinfo->envp); + + kfree(psinfo); + psinfo = next; + } +} + +static DECLARE_WORK(psinfo_cleanup, psinfo_cleaner); + +/* + * Schedule a psinfo structure for free'ing. + */ +void dtrace_psinfo_free(dtrace_psinfo_t *psinfo) +{ + unsigned long flags; + + /* + * For most tasks, we never populate any DTrace psinfo. + */ + if (!psinfo) + return; + + spin_lock_irqsave(&psinfo_lock, flags); + psinfo->next = psinfo_free_list; + psinfo_free_list = psinfo; + spin_unlock_irqrestore(&psinfo_lock, flags); + + schedule_work(&psinfo_cleanup); +} + +/*---------------------------------------------------------------------------*\ +(* TIME QUERIES *) +\*---------------------------------------------------------------------------*/ /* * Return a high resolution timer value that is guaranteed to always increase. */ @@ -206,6 +364,13 @@ static void dtrace_stacktrace_address(void *data, unsigned long addr, } else st->depth++; } + + if (st->depth >= dtrace_ustackdepth_max) { + DTRACE_CPUFLAG_SET(CPU_DTRACE_BADSTACK); + this_cpu_core->cpuc_dtrace_illval = st->depth; + + return; + } } static inline int valid_sp(struct thread_info *tinfo, void *p, @@ -275,12 +440,14 @@ EXPORT_SYMBOL(dtrace_stacktrace); (* INVALID OPCODE HANDLING *) \*---------------------------------------------------------------------------*/ typedef struct dtrace_invop_hdlr { - int (*dtih_func)(struct pt_regs *); + uint8_t (*dtih_func)(struct pt_regs *); struct dtrace_invop_hdlr *dtih_next; } dtrace_invop_hdlr_t; static dtrace_invop_hdlr_t *dtrace_invop_hdlrs; +#define INVOP_TRAP_INSTR 0xf0 + static int dtrace_die_notifier(struct notifier_block *nb, unsigned long val, void *args) { @@ -384,7 +551,7 @@ void dtrace_disable(void) } EXPORT_SYMBOL(dtrace_disable); -void dtrace_invop_add(int (*func)(struct pt_regs *)) +void dtrace_invop_add(uint8_t (*func)(struct pt_regs *)) { dtrace_invop_hdlr_t *hdlr; @@ -395,7 +562,7 @@ void dtrace_invop_add(int (*func)(struct pt_regs *)) } EXPORT_SYMBOL(dtrace_invop_add); -void dtrace_invop_remove(int (*func)(struct pt_regs *)) +void dtrace_invop_remove(uint8_t (*func)(struct pt_regs *)) { dtrace_invop_hdlr_t *hdlr = dtrace_invop_hdlrs, *prev = NULL; @@ -419,6 +586,18 @@ void dtrace_invop_remove(int (*func)(struct pt_regs *)) } EXPORT_SYMBOL(dtrace_invop_remove); +void dtrace_invop_enable(uint8_t *addr) +{ + text_poke(addr, ((unsigned char []){INVOP_TRAP_INSTR}), 1); +} +EXPORT_SYMBOL(dtrace_invop_enable); + +void dtrace_invop_disable(uint8_t *addr, uint8_t opcode) +{ + text_poke(addr, ((unsigned char []){opcode}), 1); +} +EXPORT_SYMBOL(dtrace_invop_disable); + /*---------------------------------------------------------------------------*\ (* SYSTEM CALL TRACING SUPPORT *) \*---------------------------------------------------------------------------*/ diff --git a/kernel/fork.c b/kernel/fork.c index aea67de221bc..dfd447cf0def 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -222,6 +222,10 @@ static void account_kernel_stack(struct thread_info *ti, int account) void free_task(struct task_struct *tsk) { +#ifdef CONFIG_DTRACE + dtrace_psinfo_free(tsk->dtrace_psinfo); +#endif + account_kernel_stack(tsk->stack, -1); arch_release_thread_info(tsk->stack); free_thread_info(tsk->stack); @@ -378,6 +382,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig) account_kernel_stack(ti, 1); +#ifdef CONFIG_DTRACE + tsk->dtrace_psinfo = NULL; +#endif + return tsk; free_ti: