]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
dtrace: add psinfo/cpuinfo OS level support
authorKris Van Hees <kris.van.hees@oracle.com>
Sun, 9 Sep 2012 21:27:14 +0000 (17:27 -0400)
committerNick Alcock <nick.alcock@oracle.com>
Mon, 29 Jun 2015 21:40:32 +0000 (22:40 +0100)
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 <kris.van.hees@oracle.com>
fs/exec.c
include/linux/dtrace_cpu.h
include/linux/dtrace_os.h
include/linux/sched.h
kernel/dtrace/dtrace_cpu.c
kernel/dtrace/dtrace_os.c
kernel/fork.c

index cc95013de1e624ffbc3f2df80d4ca3b652eddd1a..36f4f76763c14168dfba7ec86285cda0e66f90d8 100644 (file)
--- 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);
index 37fd6d38454e2eb4d2beb8057bced9dd6f8bb4cc..db02a84ac2e06c29dcc3ba7d86a53b34290bd0fb 100644 (file)
@@ -2,7 +2,6 @@
 #define _DTRACE_CPU_H_
 
 #include <linux/ktime.h>
-#include <linux/module.h>
 #include <linux/mutex.h>
 
 #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_ */
index fe7a17ad728dd51324030756a29e4e33e55d3ac4..4076a7bdde835fdcd6328688825e6444b0fad6ef 100644 (file)
@@ -5,6 +5,27 @@
 
 #include <asm/unistd.h>
 
+#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_ */
index 548a3b6c0037bc3db69728a477202057e98cb225..0240d52f1ed11d94417ef23660d2e477393aa54b 100644 (file)
@@ -58,6 +58,7 @@ struct sched_param {
 #include <linux/uidgid.h>
 #include <linux/gfp.h>
 #include <linux/magic.h>
+#include <linux/dtrace_os.h>
 
 #include <asm/processor.h>
 
@@ -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
index 4b1d249c70dc42ed6d46c755cdcf2a400f10f3af..632b92f5faa0fc426e4df22516e75c04ee61db2d 100644 (file)
@@ -6,7 +6,27 @@
  */
 
 #include <linux/dtrace_cpu.h>
+#include <linux/module.h>
 #include <asm/percpu.h>
 
-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;
+       }
+}
index 4c2c1cbb2c036975e871c85c680b7711b220f60f..732a538709022335ca306a83dff34e7cb64d23cd 100644 (file)
@@ -5,10 +5,12 @@
  * Copyright (C) 2010, 2011 Oracle Corporation
  */
 
+#include <linux/binfmts.h>
 #include <linux/cyclic.h>
 #include <linux/dtrace_cpu.h>
 #include <linux/dtrace_os.h>
 #include <linux/fs.h>
+#include <linux/hardirq.h>
 #include <linux/hrtimer.h>
 #include <linux/kdebug.h>
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
 #include <linux/kallsyms.h>
+#include <linux/workqueue.h>
+#include <linux/mm.h>
 #include <asm/insn.h>
 #include <asm/stacktrace.h>
 #include <asm/syscalls.h>
 
-/*
- * 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                                               *)
 \*---------------------------------------------------------------------------*/
index aea67de221bcc09efabcc25aab56198cee14a04c..dfd447cf0def47254775c32f8e41104703b48344 100644 (file)
@@ -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: