diff options
author | dcn <dcn@vervainp2.rchland.ibm.com> | 2008-07-29 06:32:54 -0500 |
---|---|---|
committer | dcn <dcn@vervainp2.rchland.ibm.com> | 2008-07-29 06:32:54 -0500 |
commit | a96d1db083ba958e4da2a212dd044c52d001511a (patch) | |
tree | 789f242c34357b4e5b60d43a84c92e82d99bfb0f | |
parent | b1af668d224b0673f27f991a77455d6e0ecb6891 (diff) | |
download | systemtap-steved-a96d1db083ba958e4da2a212dd044c52d001511a.tar.gz systemtap-steved-a96d1db083ba958e4da2a212dd044c52d001511a.tar.xz systemtap-steved-a96d1db083ba958e4da2a212dd044c52d001511a.zip |
session.h (struct systemtap_session): Added itrace_derived_probe
group.
elaborate.cxx (systemtap_session::systemtap_session): Added
initialization of itrace_derived_probes.
tapsets.cxx (struct itrace_derived_probe): Add derived_probe
struct for holding info needed by itrace probes.
(struct itrace_derived_probe_group): New derived_probe_group
to handle itrace probes.
(itrace_derived_probe::itrace_derived_probe): Needed for use with
task_finder.
(itrace_derived_probe_group::join_group): Ditto.
(itrace_derived_probe_group::enroll): Ditto.
(itrace_derived_probe_group::emit_probe_decl): Ditto.
(itrace_derived_probe_group::emit_module_decls): Ditto.
(itrace_derived_probe_group::emit_module_init): Ditto.
(itrace_derived_probe_group::emit_module_exit): Ditto.
stapprobes.5.in : Added documentation of itrace probe.
-rw-r--r-- | ChangeLog | 20 | ||||
-rw-r--r-- | elaborate.cxx | 1 | ||||
-rw-r--r-- | runtime/itrace.c | 438 | ||||
-rw-r--r-- | session.h | 2 | ||||
-rw-r--r-- | stapprobes.5.in | 5 | ||||
-rw-r--r-- | systemtap.base/itrace.exp | 102 | ||||
-rw-r--r-- | tapsets.cxx | 282 | ||||
-rw-r--r-- | testsuite/ChangeLog | 4 |
8 files changed, 854 insertions, 0 deletions
@@ -1,3 +1,23 @@ +2008-07-28 Dave Nomura <dcnltc@us.ibm.com> + + * session.h (struct systemtap_session): Added itrace_derived_probe + group. + * elaborate.cxx (systemtap_session::systemtap_session): Added + initialization of itrace_derived_probes. + * tapsets.cxx (struct itrace_derived_probe): Add derived_probe + struct for holding info needed by itrace probes. + (struct itrace_derived_probe_group): New derived_probe_group + to handle itrace probes. + (itrace_derived_probe::itrace_derived_probe): Needed for use with + task_finder. + (itrace_derived_probe_group::join_group): Ditto. + (itrace_derived_probe_group::enroll): Ditto. + (itrace_derived_probe_group::emit_probe_decl): Ditto. + (itrace_derived_probe_group::emit_module_decls): Ditto. + (itrace_derived_probe_group::emit_module_init): Ditto. + (itrace_derived_probe_group::emit_module_exit): Ditto. + * stapprobes.5.in : Added documentation of itrace probe. + 2008-07-24 Josh Stone <joshua.i.stone@intel.com> * buildrun.cxx (compile_pass): Remove STAPCONF_MODULE_NSECTIONS, diff --git a/elaborate.cxx b/elaborate.cxx index 73358a1d..0950b086 100644 --- a/elaborate.cxx +++ b/elaborate.cxx @@ -1187,6 +1187,7 @@ systemtap_session::systemtap_session (): dwarf_derived_probes(0), uprobe_derived_probes(0), utrace_derived_probes(0), + itrace_derived_probes(0), task_finder_derived_probes(0), timer_derived_probes(0), profile_derived_probes(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 @@ -32,6 +32,7 @@ struct be_derived_probe_group; struct dwarf_derived_probe_group; struct uprobe_derived_probe_group; struct utrace_derived_probe_group; +struct itrace_derived_probe_group; struct task_finder_derived_probe_group; struct timer_derived_probe_group; struct profile_derived_probe_group; @@ -146,6 +147,7 @@ struct systemtap_session dwarf_derived_probe_group* dwarf_derived_probes; uprobe_derived_probe_group* uprobe_derived_probes; utrace_derived_probe_group* utrace_derived_probes; + itrace_derived_probe_group* itrace_derived_probes; task_finder_derived_probe_group* task_finder_derived_probes; timer_derived_probe_group* timer_derived_probes; profile_derived_probe_group* profile_derived_probes; diff --git a/stapprobes.5.in b/stapprobes.5.in index 700452a7..fcc44df1 100644 --- a/stapprobes.5.in +++ b/stapprobes.5.in @@ -367,6 +367,8 @@ process(PID).syscall process("PATH").syscall process(PID).syscall.return process("PATH").syscall.return +process(PID).itrace +process("PATH").itrace .ESAMPLE .PP A @@ -391,6 +393,9 @@ A probe gets called when a thread described by PID or PATH returns from a system call. The system call number is available in the "$syscall" context variable. +A +.B .itrace +probe gets called for every single step of the process described by PID or PATH. .PP Note that .I PATH diff --git a/systemtap.base/itrace.exp b/systemtap.base/itrace.exp new file mode 100644 index 00000000..4b73ac1c --- /dev/null +++ b/systemtap.base/itrace.exp @@ -0,0 +1,102 @@ +# itrace test + +# Initialize variables +set utrace_support_found 0 +set exepath "[pwd]/ls_[pid]" + +set itrace1_script { + global instrs = 0 + probe begin { printf("systemtap starting probe\n") } + probe process("%s").itrace + { + instrs += 1 + if (instrs == 5) + exit() + } + + + probe end { printf("systemtap ending probe\n") + printf("itraced = %%d\n", instrs) + } +} +set itrace1_script_output "itraced = 5\r\n" + +set itrace2_script { + global instrs = 0, itrace_on = 0, start_timer = 0 + probe begin { start_timer = 1; printf("systemtap starting probe\n") } + probe process("%s").itrace if (itrace_on) + { + instrs += 1 + if (instrs == 5) + exit() + } + + + probe timer.ms(1) if (start_timer) + { + itrace_on = 1 + } + + probe timer.ms(10) if (start_timer) + { + itrace_on = 0 + } + probe end { printf("systemtap ending probe\n") + printf("itraced = %%d\n", instrs) + } +} +set itrace2_script_output "itraced = 5\r\n" + + +# Set up our own copy of /bin/ls, to make testing for a particular +# executable easy. We can't use 'ln' here, since we might be creating +# a cross-device link. We can't use 'ln -s' here, since the kernel +# resolves the symbolic link and reports that /bin/ls is being +# exec'ed (instead of our local copy). +if {[catch {exec cp /bin/ls $exepath} res]} { + fail "unable to copy /bin/ls: $res" + return +} + +# "load" generation function for stap_run. It spawns our own copy of +# /bin/ls, waits 5 seconds, then kills it. +proc run_ls_5_sec {} { + global exepath + + spawn $exepath + set exe_id $spawn_id + after 5000; + exec kill -INT -[exp_pid -i $exe_id] + return 0; +} + + +# Try to find utrace_attach symbol in /proc/kallsyms +set path "/proc/kallsyms" +if {! [catch {exec grep -q utrace_attach $path} dummy]} { + set utrace_support_found 1 +} + +set TEST_NAME "itrace1" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} elseif {![installtest_p]} { + untested "$TEST_NAME : not installtest_p" +} else { + set script [format $itrace1_script $exepath] + stap_run $TEST_NAME run_ls_5_sec $itrace1_script_output -e $script +} + + +set TEST_NAME "itrace2" +if {$utrace_support_found == 0} { + untested "$TEST_NAME : no kernel utrace support found" +} elseif {![installtest_p]} { + untested "$TEST_NAME : not installtest_p" +} else { + set script [format $itrace2_script $exepath] + stap_run $TEST_NAME run_ls_5_sec $itrace2_script_output -e $script +} + +# Cleanup +exec rm -f $exepath diff --git a/tapsets.cxx b/tapsets.cxx index 5a3fbdc8..83463f85 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -5342,6 +5342,280 @@ task_finder_derived_probe_group::emit_module_exit (systemtap_session& s) s.op->newline() << "stap_stop_task_finder();"; } +// ------------------------------------------------------------------------ +// itrace user-space probes +// ------------------------------------------------------------------------ + + +struct itrace_derived_probe: public derived_probe +{ + bool has_path; + string path; + int64_t pid; + int single_step; + + itrace_derived_probe (systemtap_session &s, probe* p, probe_point* l, + bool hp, string &pn, int64_t pd, int ss + ); + void join_group (systemtap_session& s); +}; + + +struct itrace_derived_probe_group: public generic_dpg<itrace_derived_probe> +{ +private: + map<string, vector<itrace_derived_probe*> > probes_by_path; + typedef map<string, vector<itrace_derived_probe*> >::iterator p_b_path_iterator; + map<int64_t, vector<itrace_derived_probe*> > probes_by_pid; + typedef map<int64_t, vector<itrace_derived_probe*> >::iterator p_b_pid_iterator; + unsigned num_probes; + + void emit_probe_decl (systemtap_session& s, itrace_derived_probe *p); + +public: + itrace_derived_probe_group(): num_probes(0) { } + + void enroll (itrace_derived_probe* probe); + void emit_module_decls (systemtap_session& s); + void emit_module_init (systemtap_session& s); + void emit_module_exit (systemtap_session& s); +}; + + +itrace_derived_probe::itrace_derived_probe (systemtap_session &s, + probe* p, probe_point* l, + bool hp, string &pn, int64_t pd, + int ss + ): + derived_probe(p, l), has_path(hp), path(pn), pid(pd), single_step(ss) +{ +} + + +void +itrace_derived_probe::join_group (systemtap_session& s) +{ + if (! s.itrace_derived_probes) + s.itrace_derived_probes = new itrace_derived_probe_group (); + + s.itrace_derived_probes->enroll (this); + + task_finder_derived_probe_group::create_session_group (s); +} + +struct itrace_builder: public derived_probe_builder +{ + itrace_builder() {} + virtual void build(systemtap_session & sess, + probe * base, + probe_point * location, + std::map<std::string, literal *> const & parameters, + vector<derived_probe *> & finished_results) + { + string path; + int64_t pid; + int single_step; + + bool has_path = get_param (parameters, TOK_PROCESS, path); + bool has_pid = get_param (parameters, TOK_PROCESS, pid); + assert (has_path || has_pid); + + single_step = 1; + + // If we have a path, we need to validate it. + if (has_path) + { + string::size_type start_pos, end_pos; + string component; + + // Make sure it starts with '/'. + if (path[0] != '/') + throw semantic_error ("process path must start with a '/'", + location->tok); + + start_pos = 1; // get past the initial '/' + while ((end_pos = path.find('/', start_pos)) != string::npos) + { + component = path.substr(start_pos, end_pos - start_pos); + // Make sure it isn't empty. + if (component.size() == 0) + throw semantic_error ("process path component cannot be empty", + location->tok); + // Make sure it isn't relative. + else if (component == "." || component == "..") + throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok); + + start_pos = end_pos + 1; + } + component = path.substr(start_pos); + // Make sure it doesn't end with '/'. + if (component.size() == 0) + throw semantic_error ("process path cannot end with a '/'", location->tok); + // Make sure it isn't relative. + else if (component == "." || component == "..") + throw semantic_error ("process path cannot be relative (and contain '.' or '..')", location->tok); + } + + finished_results.push_back(new itrace_derived_probe(sess, base, location, + has_path, path, pid, + single_step + )); + } +}; + + +void +itrace_derived_probe_group::enroll (itrace_derived_probe* p) +{ + if (p->has_path) + probes_by_path[p->path].push_back(p); + else + probes_by_pid[p->pid].push_back(p); + num_probes++; + + // XXX: multiple exec probes (for instance) for the same path (or + // pid) should all share a itrace report function, and have their + // handlers executed sequentially. +} + + +void +itrace_derived_probe_group::emit_probe_decl (systemtap_session& s, + itrace_derived_probe *p) +{ + s.op->newline() << "{"; + s.op->line() << " .tgt={"; + + if (p->has_path) + { + s.op->line() << " .pathname=\"" << p->path << "\","; + s.op->line() << " .pid=0,"; + } + else + { + s.op->line() << " .pathname=NULL,"; + s.op->line() << " .pid=" << p->pid << ","; + } + + s.op->line() << " .callback=&_stp_itrace_probe_cb,"; + s.op->line() << " },"; + s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ","; + s.op->line() << " .single_step=" << p->single_step << ","; + s.op->line() << " .ph=&" << p->name << ","; + + s.op->line() << " },"; +} + + +void +itrace_derived_probe_group::emit_module_decls (systemtap_session& s) +{ + if (probes_by_path.empty() && probes_by_pid.empty()) + return; + + s.op->newline(); + s.op->newline() << "/* ---- itrace probes ---- */"; + s.op->newline() << "struct stap_itrace_probe {"; + s.op->indent(1); + s.op->newline() << "struct stap_task_finder_target tgt;"; + s.op->newline() << "const char *pp;"; + s.op->newline() << "void (*ph) (struct context*);"; + s.op->newline() << "int single_step;"; + s.op->newline(-1) << "};"; + s.op->newline() << "static void enter_itrace_probe(struct stap_itrace_probe *p, struct pt_regs *regs, void *data);"; + s.op->newline() << "#include \"itrace.c\""; + + // output routine to call itrace probe + s.op->newline() << "static void enter_itrace_probe(struct stap_itrace_probe *p, struct pt_regs *regs, void *data) {"; + s.op->indent(1); + + common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING"); + s.op->newline() << "c->probe_point = p->pp;"; + s.op->newline() << "c->regs = regs;"; + s.op->newline() << "c->data = data;"; + + // call probe function + s.op->newline() << "(*p->ph) (c);"; + common_probe_entryfn_epilogue (s.op); + + s.op->newline() << "return;"; + s.op->newline(-1) << "}"; + + // Output task finder callback routine that gets called for all + // itrace probe types. + s.op->newline() << "static int _stp_itrace_probe_cb(struct task_struct *tsk, int register_p, int process_p, struct stap_task_finder_target *tgt) {"; + s.op->indent(1); + s.op->newline() << "int rc = 0;"; + s.op->newline() << "struct stap_itrace_probe *p = container_of(tgt, struct stap_itrace_probe, tgt);"; + + s.op->newline() << "if (register_p) "; + s.op->indent(1); + + s.op->newline() << "rc = usr_itrace_init(p->single_step, tsk->pid, p);"; + s.op->newline(-1) << "else"; + s.op->newline(1) << "remove_usr_itrace_info(find_itrace_info(p->tgt.pid));"; + s.op->newline(-1) << "return rc;"; + s.op->newline(-1) << "}"; + + s.op->newline() << "struct stap_itrace_probe stap_itrace_probes[] = {"; + s.op->indent(1); + + // Set up 'process(PATH)' probes + if (! probes_by_path.empty()) + { + for (p_b_path_iterator it = probes_by_path.begin(); + it != probes_by_path.end(); it++) + { + for (unsigned i = 0; i < it->second.size(); i++) + { + itrace_derived_probe *p = it->second[i]; + emit_probe_decl(s, p); + } + } + } + + // Set up 'process(PID)' probes + if (! probes_by_pid.empty()) + { + for (p_b_pid_iterator it = probes_by_pid.begin(); + it != probes_by_pid.end(); it++) + { + for (unsigned i = 0; i < it->second.size(); i++) + { + itrace_derived_probe *p = it->second[i]; + emit_probe_decl(s, p); + } + } + } + s.op->newline(-1) << "};"; +} + + +void +itrace_derived_probe_group::emit_module_init (systemtap_session& s) +{ + if (probes_by_path.empty() && probes_by_pid.empty()) + return; + + s.op->newline(); + s.op->newline() << "/* ---- itrace probes ---- */"; + + s.op->newline() << "for (i=0; i<" << num_probes << "; i++) {"; + s.op->indent(1); + s.op->newline() << "struct stap_itrace_probe *p = &stap_itrace_probes[i];"; + s.op->newline() << "rc = stap_register_task_finder_target(&p->tgt);"; + s.op->newline(-1) << "}"; +} + + +void +itrace_derived_probe_group::emit_module_exit (systemtap_session& s) +{ + if (probes_by_path.empty() && probes_by_pid.empty()) return; + s.op->newline(); + s.op->newline() << "/* ---- itrace probes ---- */"; + s.op->newline() << "cleanup_usr_itrace();"; +} // ------------------------------------------------------------------------ // utrace user-space probes @@ -8483,6 +8757,13 @@ register_standard_tapsets(systemtap_session & s) ->bind(new utrace_builder ()); s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_THREAD)->bind(TOK_END) ->bind(new utrace_builder ()); + + // itrace user-space probes + s.pattern_root->bind_str(TOK_PROCESS)->bind("itrace") + ->bind(new itrace_builder ()); + s.pattern_root->bind_num(TOK_PROCESS)->bind("itrace") + ->bind(new itrace_builder ()); + s.pattern_root->bind_str(TOK_PROCESS)->bind(TOK_SYSCALL) ->bind(new utrace_builder ()); s.pattern_root->bind_num(TOK_PROCESS)->bind(TOK_SYSCALL) @@ -8534,6 +8815,7 @@ all_session_groups(systemtap_session& s) // "register" the dummy task_finder probe group after all probe // groups that use the task_finder. DOONE(utrace); + DOONE(itrace); DOONE(task_finder); #undef DOONE return g; diff --git a/testsuite/ChangeLog b/testsuite/ChangeLog index 14243b80..b69a8dfa 100644 --- a/testsuite/ChangeLog +++ b/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2008-07-24 Dave Nomura <dcnltc@us.ibm.com> + + * systemtap.base/itrace.stp: Added simple tests of itrace probe. + 2008-07-14 Dave Brolley <brolley@redhat.com> * Makefile.am (TOOL_OPTS): New variable. |