summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog20
-rw-r--r--elaborate.cxx1
-rw-r--r--runtime/itrace.c438
-rw-r--r--session.h2
-rw-r--r--stapprobes.5.in5
-rw-r--r--systemtap.base/itrace.exp102
-rw-r--r--tapsets.cxx282
-rw-r--r--testsuite/ChangeLog4
8 files changed, 854 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 11ef1787..53693fa1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/session.h b/session.h
index 734c8d7d..9f38372f 100644
--- a/session.h
+++ b/session.h
@@ -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.