diff options
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/ChangeLog | 5 | ||||
-rw-r--r-- | runtime/autoconf-module-nsections.c | 8 | ||||
-rw-r--r-- | runtime/autoconf-oneachcpu-retry.c | 7 | ||||
-rw-r--r-- | runtime/itrace.c | 438 | ||||
-rw-r--r-- | runtime/time.c | 4 |
5 files changed, 454 insertions, 8 deletions
diff --git a/runtime/ChangeLog b/runtime/ChangeLog index f12b5d9a..a8d73ffd 100644 --- a/runtime/ChangeLog +++ b/runtime/ChangeLog @@ -1,3 +1,8 @@ +2008-07-24 Josh Stone <joshua.i.stone@intel.com> + + * runtime/autoconf-module-nsections.c: removed + * runtime/autoconf-oneachcpu-retry.c: added + 2008-07-21 David Smith <dsmith@redhat.com> * task_finder_vma.c (__stp_tf_vma_initialize): New function to diff --git a/runtime/autoconf-module-nsections.c b/runtime/autoconf-module-nsections.c deleted file mode 100644 index c1ce58b7..00000000 --- a/runtime/autoconf-module-nsections.c +++ /dev/null @@ -1,8 +0,0 @@ -#include <linux/module.h> - -struct module_sect_attrs x; - -void foo (void) -{ - (void) x.nsections; -} diff --git a/runtime/autoconf-oneachcpu-retry.c b/runtime/autoconf-oneachcpu-retry.c new file mode 100644 index 00000000..304d9842 --- /dev/null +++ b/runtime/autoconf-oneachcpu-retry.c @@ -0,0 +1,7 @@ +#include <linux/smp.h> + +void ____autoconf_func(void) +{ + /* Older on_each_cpu() calls had a "retry" parameter */ + (void)on_each_cpu(NULL, NULL, 0, 0); +} diff --git a/runtime/itrace.c b/runtime/itrace.c new file mode 100644 index 00000000..3ee48265 --- /dev/null +++ b/runtime/itrace.c @@ -0,0 +1,438 @@ +/* + * user space instruction tracing + * Copyright (C) 2005, 2006, 2007, 2008 IBM Corp. + * + * This file is part of systemtap, and is free software. You can + * redistribute it and/or modify it under the terms of the GNU General + * Public License (GPL); either version 2, or (at your option) any + * later version. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/sched.h> +#include <linux/rcupdate.h> +#include <linux/utrace.h> +#include <asm/string.h> +#include <asm/tracehook.h> +#include <asm/ptrace.h> +#include "uprobes/uprobes.h" + +#ifndef put_task_struct +#define put_task_struct(t) \ + BUG_ON(atomic_dec_and_test(&tsk->usage)) +#endif + +#ifdef CONFIG_PPC +struct bpt_info { + unsigned long addr; + unsigned int instr; +}; + +struct atomic_ss_info { + int step_over_atomic; + struct bpt_info end_bpt; + struct bpt_info br_bpt; +}; + +static int handle_ppc_atomic_seq(struct task_struct *tsk, struct pt_regs *regs, + struct atomic_ss_info *ss_info); +static void remove_atomic_ss_breakpoint (struct task_struct *tsk, + struct bpt_info *bpt); +#endif + +struct itrace_info { + pid_t tid; + u32 step_flag; + struct stap_itrace_probe *itrace_probe; +#ifdef CONFIG_PPC + struct atomic_ss_info ppc_atomic_ss; +#endif + struct task_struct *tsk; + struct utrace_attached_engine *engine; + struct list_head link; +}; + +static u32 debug = 1; + +static LIST_HEAD(usr_itrace_info); +static spinlock_t itrace_lock; +static struct itrace_info *create_itrace_info( + struct task_struct *tsk, u32 step_flag, + struct stap_itrace_probe *itrace_probe); + +static u32 usr_itrace_report_signal(struct utrace_attached_engine *engine, + struct task_struct *tsk, + struct pt_regs *regs, + u32 action, siginfo_t *info, + const struct k_sigaction *orig_ka, + struct k_sigaction *return_ka) +{ + struct itrace_info *ui; + u32 return_flags; + unsigned long data = 0; +#ifdef CONFIG_PPC + data = mfspr(SPRN_SDAR); +#endif + + ui = rcu_dereference(engine->data); + WARN_ON(!ui); + + if (info->si_signo != SIGTRAP || !ui) + return UTRACE_ACTION_RESUME; + + /* normal case: continue stepping, hide this trap from other engines */ + return_flags = ui->step_flag | UTRACE_ACTION_HIDE | UTRACE_SIGNAL_IGN | + UTRACE_ACTION_NEWSTATE; + +#ifdef CONFIG_PPC + if (ui->ppc_atomic_ss.step_over_atomic) { + remove_atomic_ss_breakpoint(tsk, &ui->ppc_atomic_ss.end_bpt); + if (ui->ppc_atomic_ss.br_bpt.addr) + remove_atomic_ss_breakpoint(tsk, + &ui->ppc_atomic_ss.br_bpt); + ui->ppc_atomic_ss.step_over_atomic = 0; + } + + if (handle_ppc_atomic_seq(tsk, regs, &ui->ppc_atomic_ss)) + return_flags = UTRACE_ACTION_RESUME | UTRACE_ACTION_NEWSTATE | + UTRACE_SIGNAL_IGN; +#endif + + enter_itrace_probe(ui->itrace_probe, regs, (void *)&data); + + return return_flags; +} + +static u32 usr_itrace_report_clone(struct utrace_attached_engine *engine, + struct task_struct *parent, unsigned long clone_flags, + struct task_struct *child) +{ + return UTRACE_ACTION_RESUME; +} + +static u32 usr_itrace_report_death(struct utrace_attached_engine *e, + struct task_struct *tsk) +{ + struct itrace_info *ui = rcu_dereference(e->data); + WARN_ON(!ui); + + return (UTRACE_ACTION_NEWSTATE | UTRACE_ACTION_DETACH); +} + +static const struct utrace_engine_ops utrace_ops = +{ + .report_signal = usr_itrace_report_signal, + .report_clone = usr_itrace_report_clone, + .report_death = usr_itrace_report_death +}; + + +static struct itrace_info *create_itrace_info( + struct task_struct *tsk, u32 step_flag, + struct stap_itrace_probe *itrace_probe) +{ + struct itrace_info *ui; + + if (debug) + printk(KERN_INFO "create_itrace_info: tid=%d\n", tsk->pid); + /* initialize ui */ + ui = kzalloc(sizeof(struct itrace_info), GFP_USER); + ui->tsk = tsk; + ui->tid = tsk->pid; + ui->step_flag = step_flag; + ui->itrace_probe = itrace_probe; +#ifdef CONFIG_PPC + ui->ppc_atomic_ss.step_over_atomic = 0; +#endif + INIT_LIST_HEAD(&ui->link); + + /* push ui onto usr_itrace_info */ + spin_lock(&itrace_lock); + list_add(&ui->link, &usr_itrace_info); + + /* attach a single stepping engine */ + ui->engine = utrace_attach(ui->tsk, UTRACE_ATTACH_CREATE, &utrace_ops, ui); + if (IS_ERR(ui->engine)) { + printk(KERN_ERR "utrace_attach returns %ld\n", + PTR_ERR(ui->engine)); + ui = NULL; + } else { + utrace_set_flags(tsk, ui->engine, ui->engine->flags | + ui->step_flag | + UTRACE_EVENT(CLONE) | UTRACE_EVENT_SIGNAL_ALL | + UTRACE_EVENT(DEATH)); + } + spin_unlock(&itrace_lock); + return ui; +} + +static struct itrace_info *find_itrace_info(pid_t tid) +{ + struct itrace_info *ui = NULL; + + spin_lock(&itrace_lock); + list_for_each_entry(ui, &usr_itrace_info, link) { + if (ui->tid == tid) + goto done; + } + ui = NULL; +done: + spin_unlock(&itrace_lock); + return ui; +} + + +int usr_itrace_init(int single_step, pid_t tid, struct stap_itrace_probe *p) +{ + struct itrace_info *ui; + struct task_struct *tsk; + + rcu_read_lock(); + tsk = find_task_by_pid(tid); + if (!tsk) { + printk(KERN_ERR "usr_itrace_init: Cannot find process %d\n", tid); + rcu_read_unlock(); + return 1; + } + + get_task_struct(tsk); + ui = create_itrace_info(tsk, + (single_step ? + UTRACE_ACTION_SINGLESTEP : UTRACE_ACTION_BLOCKSTEP), p); + if (!ui) + return 1; + + put_task_struct(tsk); + rcu_read_unlock(); + + spin_lock_init(&itrace_lock); + + /* set initial state */ + spin_lock(&itrace_lock); + spin_unlock(&itrace_lock); + printk(KERN_INFO "usr_itrace_init: completed for tid = %d\n", tid); + + return 0; +} + +void static remove_usr_itrace_info(struct itrace_info *ui) +{ + struct itrace_info *tmp; + + if (!ui) + return; + + if (debug) + printk(KERN_INFO "remove_usr_itrace_info: tid=%d\n", ui->tid); + + spin_lock(&itrace_lock); + if (ui->tsk && ui->engine) { + (void) utrace_detach(ui->tsk, ui->engine); + } + list_del(&ui->link); + spin_unlock(&itrace_lock); + kfree(ui); +} + +void static cleanup_usr_itrace(void) +{ + struct itrace_info *tmp; + struct itrace_info *ui; + + if (debug) + printk(KERN_INFO "cleanup_usr_itrace called\n"); + + list_for_each_entry_safe(ui, tmp, &usr_itrace_info, link) { + remove_usr_itrace_info(ui); + } +} + + +#ifdef CONFIG_PPC +#define PPC_INSTR_SIZE 4 +#define TEXT_SEGMENT_BASE 1 + +/* Instruction masks used during single-stepping of atomic sequences. */ +#define LWARX_MASK 0xfc0007fe +#define LWARX_INSTR 0x7c000028 +#define LDARX_INSTR 0x7c0000A8 +#define STWCX_MASK 0xfc0007ff +#define STWCX_INSTR 0x7c00012d +#define STDCX_INSTR 0x7c0001ad +#define BC_MASK 0xfc000000 +#define BC_INSTR 0x40000000 +#define ATOMIC_SEQ_LENGTH 16 +#define BPT_TRAP 0x7fe00008 +#define INSTR_SZ sizeof(int) + +static int get_instr(unsigned long addr, char *msg) +{ + unsigned int instr; + + if (copy_from_user(&instr, (const void __user *) addr, + sizeof(instr))) { + printk(KERN_ERR "get_instr failed: %s\n", msg); + WARN_ON(1); + } + return instr; + +} + +static void insert_atomic_ss_breakpoint (struct task_struct *tsk, + struct bpt_info *bpt) +{ + unsigned int bp_instr = BPT_TRAP; + unsigned int cur_instr; + + cur_instr = get_instr(bpt->addr, "insert_atomic_ss_breakpoint"); + if (cur_instr != BPT_TRAP) { + bpt->instr = cur_instr; + WARN_ON(access_process_vm(tsk, bpt->addr, &bp_instr, INSTR_SZ, 1) != + INSTR_SZ); + } +} + +static void remove_atomic_ss_breakpoint (struct task_struct *tsk, + struct bpt_info *bpt) +{ + WARN_ON(access_process_vm(tsk, bpt->addr, &bpt->instr, INSTR_SZ, 1) != + INSTR_SZ); +} + +/* locate the branch destination. Return -1 if not a branch. */ +static unsigned long +branch_dest (int opcode, int instr, struct pt_regs *regs, unsigned long pc) +{ + unsigned long dest; + int immediate; + int absolute; + int ext_op; + + absolute = (int) ((instr >> 1) & 1); + + switch (opcode) { + case 18: + immediate = ((instr & ~3) << 6) >> 6; /* br unconditional */ + if (absolute) + dest = immediate; + else + dest = pc + immediate; + break; + + case 16: + immediate = ((instr & ~3) << 16) >> 16; /* br conditional */ + if (absolute) + dest = immediate; + else + dest = pc + immediate; + break; + + case 19: + ext_op = (instr >> 1) & 0x3ff; + + if (ext_op == 16) { + /* br conditional register */ + dest = regs->link & ~3; + /* FIX: we might be in a signal handler */ + WARN_ON(dest > 0); + } else if (ext_op == 528) { + /* br cond to ctr reg */ + dest = regs->ctr & ~3; + + /* for system call dest < TEXT_SEGMENT_BASE */ + if (dest < TEXT_SEGMENT_BASE) + dest = regs->link & ~3; + } else + return -1; + break; + + default: + return -1; + } + return dest; +} + +/* Checks for an atomic sequence of instructions beginning with a LWARX/LDARX + instruction and ending with a STWCX/STDCX instruction. If such a sequence + is found, attempt to step through it. A breakpoint is placed at the end of + the sequence. */ + +static int handle_ppc_atomic_seq(struct task_struct *tsk, struct pt_regs *regs, + struct atomic_ss_info *ss_info) +{ + unsigned long ip = regs->nip; + unsigned long start_addr; + unsigned int instr; + int got_stx = 0; + int i; + int ret; + + unsigned long br_dest; /* bpt at branch instr's destination */ + int bc_instr_count = 0; /* conditional branch instr count */ + + instr = get_instr(regs->nip, "handle_ppc_atomic_seq:1"); + /* Beginning of atomic sequence starts with lwarx/ldarx instr */ + if ((instr & LWARX_MASK) != LWARX_INSTR + && (instr & LWARX_MASK) != LDARX_INSTR) + return 0; + + start_addr = regs->nip; + for (i = 0; i < ATOMIC_SEQ_LENGTH; ++i) { + ip += INSTR_SZ; + instr = get_instr(ip, "handle_ppc_atomic_seq:2"); + + /* look for at most one conditional branch in the sequence + * and put a bpt at it's destination address + */ + if ((instr & BC_MASK) == BC_INSTR) { + if (bc_instr_count >= 1) + return 0; /* only handle a single branch */ + + br_dest = branch_dest (BC_INSTR >> 26, instr, regs, ip); + + if (br_dest != -1 && + br_dest >= TEXT_SEGMENT_BASE) { + ss_info->br_bpt.addr = br_dest; + bc_instr_count++; + } + } + + if ((instr & STWCX_MASK) == STWCX_INSTR + || (instr & STWCX_MASK) == STDCX_INSTR) { + got_stx = 1; + break; + } + } + + /* Atomic sequence ends with a stwcx/stdcx instr */ + if (!got_stx) + return 0; + + ip += INSTR_SZ; + instr = get_instr(ip, "handle_ppc_atomic_seq:3"); + if ((instr & BC_MASK) == BC_INSTR) { + ip += INSTR_SZ; + instr = get_instr(ip, "handle_ppc_atomic_seq:4"); + } + + /* Insert a breakpoint right after the end of the atomic sequence. */ + ss_info->end_bpt.addr = ip; + + /* Check for duplicate bpts */ + if (bc_instr_count && (ss_info->br_bpt.addr >= start_addr && + ss_info->br_bpt.addr <= ss_info->end_bpt.addr)) + ss_info->br_bpt.addr = 0; + + insert_atomic_ss_breakpoint (tsk, &ss_info->end_bpt); + if (ss_info->br_bpt.addr) + insert_atomic_ss_breakpoint (tsk, &ss_info->br_bpt); + + ss_info->step_over_atomic = 1; + return 1; +} +#endif diff --git a/runtime/time.c b/runtime/time.c index 8a0b6fad..6b01cebe 100644 --- a/runtime/time.c +++ b/runtime/time.c @@ -237,7 +237,11 @@ _stp_init_time(void) return -1; stp_timer_reregister = 1; +#ifdef STAPCONF_ONEACHCPU_RETRY ret = on_each_cpu(__stp_init_time, NULL, 0, 1); +#else + ret = on_each_cpu(__stp_init_time, NULL, 1); +#endif #ifdef CONFIG_CPU_FREQ if (!ret && !__stp_constant_freq()) { |