summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrank Ch. Eigler <fche@elastic.org>2007-11-04 14:11:15 -0500
committerFrank Ch. Eigler <fche@elastic.org>2007-11-04 14:11:15 -0500
commit806b26a85d30f59af7dcb0025c68a50bf4bbb352 (patch)
tree8b1cc9edab49eda46278199a7027d956549f6ddb
parent24993e4fcae48ca014e6b53f3f9a011c9cfa8f06 (diff)
parent600e72b28cc0676dc581b8fc5d54c09772979b79 (diff)
downloadsystemtap-steved-806b26a85d30f59af7dcb0025c68a50bf4bbb352.tar.gz
systemtap-steved-806b26a85d30f59af7dcb0025c68a50bf4bbb352.tar.xz
systemtap-steved-806b26a85d30f59af7dcb0025c68a50bf4bbb352.zip
Merge branch 'master' of git://sources.redhat.com/git/systemtap
-rw-r--r--ChangeLog10
-rw-r--r--runtime/ChangeLog6
-rw-r--r--runtime/staprun/ChangeLog5
-rw-r--r--runtime/staprun/stapio.c6
-rw-r--r--runtime/stat-common.c64
-rw-r--r--runtime/transport/ChangeLog6
-rw-r--r--runtime/transport/control.c17
-rw-r--r--runtime/transport/procfs.c14
-rw-r--r--runtime/transport/transport.c49
-rw-r--r--runtime/transport/transport.h1
-rw-r--r--runtime/uprobes/uprobes.c4
-rw-r--r--runtime/uprobes/uprobes_ppc64.c111
-rw-r--r--runtime/uprobes/uprobes_ppc64.h23
-rw-r--r--runtime/uprobes/uprobes_x86_64.c697
-rw-r--r--runtime/uprobes/uprobes_x86_64.h84
-rw-r--r--stap.1.in5
-rw-r--r--tapset/ChangeLog18
-rw-r--r--tapset/nfs_proc.stp16
-rw-r--r--tapset/nfsd.stp4
-rw-r--r--tapset/rpc.stp2
-rw-r--r--tapset/syscalls2.stp4
-rw-r--r--testsuite/ChangeLog24
-rw-r--r--testsuite/config/unix.exp1
-rw-r--r--testsuite/lib/stap_run_exact.exp30
-rwxr-xr-xtestsuite/systemtap.maps/elision.exp397
-rw-r--r--testsuite/systemtap.maps/elision.stp54
-rw-r--r--testsuite/systemtap.printf/print.exp18
-rw-r--r--testsuite/systemtap.printf/print.stp47
-rw-r--r--testsuite/systemtap.printf/print_char.exp5
-rw-r--r--testsuite/systemtap.printf/print_char.stp16
-rw-r--r--testsuite/systemtap.printf/printd.exp20
-rw-r--r--testsuite/systemtap.printf/printd.stp36
-rw-r--r--testsuite/systemtap.printf/printdln.stp27
-rw-r--r--testsuite/systemtap.printf/println.exp18
-rw-r--r--testsuite/systemtap.printf/println.stp37
35 files changed, 1797 insertions, 79 deletions
diff --git a/ChangeLog b/ChangeLog
index 52452b7b..72f8c77b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2007-10-25 Josh Stone <joshua.i.stone@intel.com>
+
+ PR 5219
+ * stap.1.in: Add a note about string literal limitations in printf
+ and printd.
+
+2007-10-22 Jim Keniston <jkenisto@us.ibm.com>
+
+ * runtime/uprobes/uprobes_x86_64.[ch]: Added x86_64 uprobes support
+
2007-10-19 Jim Keniston <jkenisto@us.ibm.com>
* runtime/uprobes/uprobes_ppc64.h,uprobes_s390.c: Fixed 10/16
diff --git a/runtime/ChangeLog b/runtime/ChangeLog
index 798d56d3..75272aa0 100644
--- a/runtime/ChangeLog
+++ b/runtime/ChangeLog
@@ -1,3 +1,9 @@
+2007-10-25 Mike Mason <mmlnx@us.ibm.com>
+
+ * stat-common.c: Allow histogram bucket elision to be turned off
+ with -DHIST_ELISION=<negative #>. Also cleaned up looping code
+ to prevent unnecessary interation over non-existent buckets.
+
2007-10-17 Masami Hiramatsu <mhiramat@redhat.com>
* autoconf-tsc-khz.c: Not to be compiled if the kernel version is
diff --git a/runtime/staprun/ChangeLog b/runtime/staprun/ChangeLog
index f03f3606..7c6b89ad 100644
--- a/runtime/staprun/ChangeLog
+++ b/runtime/staprun/ChangeLog
@@ -1,3 +1,8 @@
+2007-10-26 Martin Hunt <hunt@redhat.com>
+ PR5218
+ * stapio.c (main): Set initialized properly when
+ attached.
+
2007-10-12 Martin Hunt <hunt@redhat.com>
Changes to separate the symbols from the command channel.
diff --git a/runtime/staprun/stapio.c b/runtime/staprun/stapio.c
index 696167af..ee30a1a1 100644
--- a/runtime/staprun/stapio.c
+++ b/runtime/staprun/stapio.c
@@ -59,7 +59,11 @@ int main(int argc, char **argv)
exit(1);
initialized = 1;
-
+ if (attach_mod) {
+ /* already started */
+ initialized++;
+ }
+
if (stp_main_loop()) {
err("ERROR: Couldn't enter main loop. Exiting.\n");
exit(1);
diff --git a/runtime/stat-common.c b/runtime/stat-common.c
index 8b2f3cf3..a62297bf 100644
--- a/runtime/stat-common.c
+++ b/runtime/stat-common.c
@@ -222,36 +222,43 @@ static void _stp_stat_print_histogram (Hist st, stat *sd)
reprint (HIST_WIDTH, "-");
_stp_print(" count\n");
- eliding=0;
+ eliding=0;
for (i = low_bucket; i <= high_bucket; i++) {
int over_under = 0;
- /* Elide consecutive zero buckets. Specifically, skip
- this row if it is zero and some of its nearest
- neighbours are also zero. */
- int k;
- int elide=1;
- for (k=i-HIST_ELISION; k<=i+HIST_ELISION; k++)
- {
- if (k >= 0 && k < st->buckets && sd->histogram[k] != 0)
- elide = 0;
- }
- if (elide)
- {
- eliding = 1;
- continue;
- }
-
- /* State change: we have elided some rows, but now are about
- to print a new one. So let's print a mark on the vertical
- axis to represent the missing rows. */
- if (eliding)
- {
- reprint (val_space, " ");
- _stp_print(" ~\n");
- eliding = 0;
- }
-
+ /* Elide consecutive zero buckets. Specifically, skip
+ this row if it is zero and some of its nearest
+ neighbours are also zero. Don't elide zero buckets
+ if HIST_ELISION is negative */
+ if ((long)HIST_ELISION >= 0) {
+ int k, elide=1;
+ /* Can't elide more than the total # of buckets */
+ int max_elide = min_t(long, HIST_ELISION, st->buckets);
+ int min_bucket = low_bucket;
+ int max_bucket = high_bucket;
+
+ if (i - max_elide > min_bucket)
+ min_bucket = i - max_elide;
+ if (i + max_elide < max_bucket)
+ max_bucket = i + max_elide;
+ for (k = min_bucket; k <= max_bucket; k++) {
+ if (sd->histogram[k] != 0)
+ elide = 0;
+ }
+ if (elide) {
+ eliding = 1;
+ continue;
+ }
+
+ /* State change: we have elided some rows, but now are
+ about to print a new one. So let's print a mark on
+ the vertical axis to represent the missing rows. */
+ if (eliding) {
+ reprint (val_space, " ");
+ _stp_print(" ~\n");
+ eliding = 0;
+ }
+ }
if (st->type == HIST_LINEAR) {
if (i == 0) {
@@ -266,8 +273,7 @@ static void _stp_stat_print_histogram (Hist st, stat *sd)
val = st->start + (i - 1) * st->interval;
} else
val = _stp_bucket_to_val(i);
-
-
+
reprint (val_space - needed_space(val) - over_under, " ");
if (over_under) {
diff --git a/runtime/transport/ChangeLog b/runtime/transport/ChangeLog
index 66811376..4872fa11 100644
--- a/runtime/transport/ChangeLog
+++ b/runtime/transport/ChangeLog
@@ -1,3 +1,9 @@
+2007-11-01 Martin Hunt <hunt@redhat.com>
+
+ * procfs.c, control.c, transport.c: Recognize when stapio
+ is detached and disable delayed work. Enable when attached.
+ Cleanup code to destroy workqueue on exit.
+
2007-10-12 Martin Hunt <hunt@redhat.com>
* transport.c (_stp_ask_for_symbols): Don't ask for
diff --git a/runtime/transport/control.c b/runtime/transport/control.c
index ae731659..7846572a 100644
--- a/runtime/transport/control.c
+++ b/runtime/transport/control.c
@@ -261,6 +261,8 @@ static int _stp_ctl_send (int type, void *data, int len)
} else {
while ((err = _stp_ctl_write(type, data, len)) < 0 && trylimit--)
msleep (5);
+ if (err > 0)
+ wake_up_interruptible(&_stp_ctl_wq);
}
kbug("returning %d\n", err);
return err;
@@ -368,26 +370,19 @@ static int _stp_sym_close_cmd (struct inode *inode, struct file *file)
return 0;
}
-static int _stp_ctl_opens = 0;
static int _stp_ctl_open_cmd (struct inode *inode, struct file *file)
{
- /* only allow one reader */
- if (_stp_ctl_opens)
+ if (_stp_attached)
return -1;
- _stp_ctl_opens++;
- _stp_pid = current->pid;
- utt_overwrite_flag = 0;
+ _stp_attach();
return 0;
}
static int _stp_ctl_close_cmd (struct inode *inode, struct file *file)
{
- if (_stp_ctl_opens)
- _stp_ctl_opens--;
- _stp_pid = 0;
- if (!_stp_exit_flag)
- utt_overwrite_flag = 1;
+ if (_stp_attached)
+ _stp_detach();
return 0;
}
diff --git a/runtime/transport/procfs.c b/runtime/transport/procfs.c
index 88740847..069e379e 100644
--- a/runtime/transport/procfs.c
+++ b/runtime/transport/procfs.c
@@ -324,6 +324,8 @@ static int _stp_ctl_send (int type, void *data, int len)
} else {
while ((err = _stp_ctl_write(type, data, len)) < 0 && trylimit--)
msleep (5);
+ if (err > 0)
+ wake_up_interruptible(&_stp_ctl_wq);
}
return err;
}
@@ -430,23 +432,19 @@ static int _stp_sym_close_cmd (struct inode *inode, struct file *file)
return 0;
}
-static int _stp_ctl_opens = 0;
static int _stp_ctl_open_cmd (struct inode *inode, struct file *file)
{
- /* only allow one reader */
- if (_stp_ctl_opens)
+ if (_stp_attached)
return -1;
- _stp_ctl_opens++;
- _stp_pid = current->pid;
+ _stp_attach();
return 0;
}
static int _stp_ctl_close_cmd (struct inode *inode, struct file *file)
{
- if (_stp_ctl_opens)
- _stp_ctl_opens--;
- _stp_pid = 0;
+ if (_stp_attached)
+ _stp_detach();
return 0;
}
diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c
index d44d6851..0a959917 100644
--- a/runtime/transport/transport.c
+++ b/runtime/transport/transport.c
@@ -47,7 +47,8 @@ void probe_exit(void);
int probe_start(void);
void _stp_exit(void);
void _stp_handle_start (struct _stp_msg_start *st);
-
+static void _stp_detach(void);
+static void _stp_attach(void);
/* check for new workqueue API */
#ifdef DECLARE_DELAYED_WORK
@@ -115,6 +116,7 @@ static void _stp_cleanup_and_exit (int dont_rmmod)
if (!_stp_exit_called) {
int failures;
+ _stp_exit_flag = 1;
unregister_module_notifier(&_stp_module_load_nb);
/* we only want to do this stuff once */
@@ -142,6 +144,34 @@ static void _stp_cleanup_and_exit (int dont_rmmod)
}
/*
+ * Called when stapio closes the control channel.
+ */
+static void _stp_detach(void)
+{
+ kbug("detach\n");
+ _stp_attached = 0;
+ _stp_pid = 0;
+
+ if (!_stp_exit_flag)
+ utt_overwrite_flag = 1;
+
+ cancel_delayed_work(&_stp_work);
+ wake_up_interruptible(&_stp_ctl_wq);
+}
+
+/*
+ * Called when stapio opens the control channel.
+ */
+static void _stp_attach(void)
+{
+ kbug("attach\n");
+ _stp_attached = 1;
+ _stp_pid = current->pid;
+ utt_overwrite_flag = 0;
+ queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER);
+}
+
+/*
* _stp_work_queue - periodically check for IO or exit
* This is run by a kernel thread and may sleep.
*/
@@ -162,12 +192,9 @@ static void _stp_work_queue (void *data)
wake_up_interruptible(&_stp_ctl_wq);
/* if exit flag is set AND we have finished with probe_start() */
- if (unlikely(_stp_exit_flag && _stp_start_finished)) {
+ if (unlikely(_stp_exit_flag && _stp_start_finished))
_stp_cleanup_and_exit(0);
- cancel_delayed_work(&_stp_work);
- flush_workqueue(_stp_wq);
- wake_up_interruptible(&_stp_ctl_wq);
- } else
+ else if (likely(_stp_attached))
queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER);
}
@@ -179,18 +206,14 @@ static void _stp_work_queue (void *data)
*/
void _stp_transport_close()
{
- kbug("************** transport_close *************\n");
+ kbug("%d: ************** transport_close *************\n", current->pid);
_stp_cleanup_and_exit(1);
- cancel_delayed_work(&_stp_work);
destroy_workqueue(_stp_wq);
- wake_up_interruptible(&_stp_ctl_wq);
- unregister_module_notifier(&_stp_module_load_nb);
_stp_unregister_ctl_channel();
if (_stp_utt) utt_trace_remove(_stp_utt);
_stp_free_modules();
_stp_kill_time();
_stp_print_cleanup(); /* free print buffers */
-
kbug("---- CLOSED ----\n");
}
@@ -257,9 +280,7 @@ int _stp_transport_init(void)
_stp_wq = create_workqueue("systemtap");
if (!_stp_wq)
goto err3;
-
- queue_delayed_work(_stp_wq, &_stp_work, STP_WORK_TIMER);
-
+
/* request symbolic information */
_stp_ask_for_symbols();
return 0;
diff --git a/runtime/transport/transport.h b/runtime/transport/transport.h
index 0a83001e..6dc00d2b 100644
--- a/runtime/transport/transport.h
+++ b/runtime/transport/transport.h
@@ -26,4 +26,5 @@ int _stp_pid = 0;
uid_t _stp_uid = 0;
gid_t _stp_gid = 0;
pid_t _stp_init_pid = 0;
+int _stp_attached = 0;
#endif /* _TRANSPORT_TRANSPORT_H_ */
diff --git a/runtime/uprobes/uprobes.c b/runtime/uprobes/uprobes.c
index 9f5fddd0..50930709 100644
--- a/runtime/uprobes/uprobes.c
+++ b/runtime/uprobes/uprobes.c
@@ -40,6 +40,8 @@
#define SET_ENGINE_FLAGS 1
#define CLEAR_ENGINE_FLAGS 0
+#define MAX_SSOL_SLOTS 1024
+
extern int access_process_vm(struct task_struct *tsk, unsigned long addr,
void *buf, int len, int write);
static int utask_fake_quiesce(struct uprobe_task *utask);
@@ -1240,6 +1242,8 @@ static noinline void uprobe_init_ssol(struct uprobe_process *uproc)
return;
area->nfree = area->nslots = PAGE_SIZE / MAX_UINSN_BYTES;
+ if (area->nslots > MAX_SSOL_SLOTS)
+ area->nfree = area->nslots = MAX_SSOL_SLOTS;
area->slots = (struct uprobe_ssol_slot *)
kzalloc(sizeof(struct uprobe_ssol_slot) * area->nslots,
GFP_USER);
diff --git a/runtime/uprobes/uprobes_ppc64.c b/runtime/uprobes/uprobes_ppc64.c
index d80676e9..819ac73d 100644
--- a/runtime/uprobes/uprobes_ppc64.c
+++ b/runtime/uprobes/uprobes_ppc64.c
@@ -31,6 +31,117 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address,
struct pt_regs *regs, struct uprobe_task *utask)
{
unsigned long orig_ret_addr = regs->link;
+
regs->link = trampoline_address;
return orig_ret_addr;
}
+
+/*
+ * Get an instruction slot from the process's SSOL area, containing the
+ * instruction at ppt's probepoint. Point the eip at that slot, in preparation
+ * for single-stepping out of line.
+ */
+static
+void uprobe_pre_ssout(struct uprobe_task *utask, struct uprobe_probept *ppt,
+ struct pt_regs *regs)
+{
+ struct uprobe_ssol_slot *slot;
+
+ slot = uprobe_get_insn_slot(ppt);
+ if (!slot) {
+ utask->doomed = 1;
+ return;
+ }
+ regs->nip = (long)slot->insn;
+}
+
+
+static inline void calc_offset(struct uprobe_probept *ppt,
+ struct pt_regs *regs)
+{
+ int offset = 0;
+ unsigned int opcode = 0;
+ unsigned int insn = *ppt->insn;
+
+ opcode = insn >> 26;
+ switch (opcode) {
+ case 16: /* bc */
+ if ((insn & 2) == 0) {
+ offset = (signed short)(insn & 0xfffc);
+ regs->nip = ppt->vaddr + offset;
+ }
+ if (insn & 1)
+ regs->link = ppt->vaddr + MAX_UINSN_BYTES;
+ break;
+ case 17: /* sc */
+ /* Do we need to do anything */
+ break;
+ case 18: /* b */
+ if ((insn & 2) == 0) {
+ offset = insn & 0x03fffffc;
+ if (offset & 0x02000000)
+ offset -= 0x04000000;
+ regs->nip = ppt->vaddr + offset;
+ }
+ if (insn & 1)
+ regs->link = ppt->vaddr + MAX_UINSN_BYTES;
+ break;
+ }
+#ifdef UPROBES_DEBUG
+ printk (KERN_ERR "ppt->vaddr=%p, regs->nip=%p, offset=%ld\n",
+ ppt->vaddr, regs->nip, offset);
+ if (insn & 1)
+ printk (KERN_ERR "regs->link=%p \n", regs->link);
+#endif
+ return;
+}
+
+/*
+ * Called after single-stepping. ppt->vaddr is the address of the
+ * instruction which was replaced by a breakpoint instruction. To avoid
+ * the SMP problems that can occur when we temporarily put back the
+ * original opcode to single-step, we single-stepped a copy of the
+ * instruction.
+ *
+ * This function prepares to return from the post-single-step
+ * interrupt.
+ *
+ * 1) Typically, the new nip is relative to the copied instruction. We
+ * need to make it relative to the original instruction. Exceptions are
+ * branch instructions.
+ *
+ * 2) For branch instructions, update the nip if the branch uses
+ * relative addressing. Update the link instruction to the instruction
+ * following the original instruction address.
+ */
+
+static
+void uprobe_post_ssout(struct uprobe_task *utask, struct uprobe_probept *ppt,
+ struct pt_regs *regs)
+{
+ unsigned long copy_nip;
+
+ copy_nip = (unsigned long) ppt->slot->insn;
+ up_read(&ppt->slot->rwsem);
+
+ /*
+ * If the single stepped instruction is non-branch instruction
+ * then update the IP to be relative to probepoint.
+ */
+ if (regs->nip == copy_nip + MAX_UINSN_BYTES)
+ regs->nip = ppt->vaddr + MAX_UINSN_BYTES;
+ else
+ calc_offset(ppt,regs);
+}
+
+static
+int arch_validate_probed_insn(struct uprobe_probept *ppt,
+ struct task_struct *tsk)
+{
+ if ((unsigned long)ppt->vaddr & 0x03) {
+ printk(KERN_WARNING
+ "Attempt to register uprobe at an unaligned addr\n");
+ return -EINVAL;
+ }
+ return 0;
+}
diff --git a/runtime/uprobes/uprobes_ppc64.h b/runtime/uprobes/uprobes_ppc64.h
index 9f56119a..1e274a94 100644
--- a/runtime/uprobes/uprobes_ppc64.h
+++ b/runtime/uprobes/uprobes_ppc64.h
@@ -27,7 +27,7 @@
#define SSTEP_SIGNAL SIGTRAP
/* Normally defined in Kconfig */
-#undef CONFIG_UPROBES_SSOL
+#define CONFIG_UPROBES_SSOL
#define CONFIG_URETPROBES 1
typedef unsigned int uprobe_opcode_t;
@@ -46,12 +46,6 @@ struct uprobe_probept;
struct uprobe_task;
struct task_struct;
-static inline int arch_validate_probed_insn(struct uprobe_probept *ppt,
- struct task_struct *tsk)
-{
- return 0;
-}
-
/* On powerpc, nip points to the trap. */
static inline unsigned long arch_get_probept(struct pt_regs *regs)
{
@@ -62,14 +56,15 @@ static inline void arch_reset_ip_for_sstep(struct pt_regs *regs)
{
}
-#ifdef CONFIG_URETPROBES
-static inline void arch_restore_uret_addr(unsigned long ret_addr,
- struct pt_regs *regs)
-{
- regs->nip = ret_addr;
-}
-#endif /* CONFIG_URETPROBES */
+static inline int arch_validate_probed_insn(struct uprobe_probept *ppt,
+ struct task_struct *tsk);
static unsigned long arch_hijack_uret_addr(unsigned long trampoline_addr,
struct pt_regs *regs, struct uprobe_task *utask);
+
+static inline void arch_restore_uret_addr(unsigned long ret_addr,
+ struct pt_regs *regs)
+{
+ regs->nip = ret_addr;
+}
#endif /* _ASM_UPROBES_H */
diff --git a/runtime/uprobes/uprobes_x86_64.c b/runtime/uprobes/uprobes_x86_64.c
new file mode 100644
index 00000000..23dcdadb
--- /dev/null
+++ b/runtime/uprobes/uprobes_x86_64.c
@@ -0,0 +1,697 @@
+/*
+ * Userspace Probes (UProbes)
+ * arch/x86_64/kernel/uprobes.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2006-2007
+ */
+/*
+ * In versions of uprobes built in the SystemTap runtime, this file
+ * is #included at the end of uprobes.c.
+ */
+
+#define is_32bit_app(tsk) (test_tsk_thread_flag(tsk, TIF_IA32))
+
+/* Adapted from arch/x86_64/kprobes.c */
+#undef W
+#define W(row,b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,ba,bb,bc,bd,be,bf) \
+ (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \
+ (b4##UL << 0x4)|(b5##UL << 0x5)|(b6##UL << 0x6)|(b7##UL << 0x7) | \
+ (b8##UL << 0x8)|(b9##UL << 0x9)|(ba##UL << 0xa)|(bb##UL << 0xb) | \
+ (bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf)) \
+ << (row % 64))
+
+static const unsigned long good_insns_64[256 / 64] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ W(0x00, 1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0)| /* 00 */
+ W(0x10, 1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0)| /* 10 */
+ W(0x20, 1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0)| /* 20 */
+ W(0x30, 1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0), /* 30 */
+ W(0x40, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)| /* 40 */
+ W(0x50, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 50 */
+ W(0x60, 0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0)| /* 60 */
+ W(0x70, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1), /* 70 */
+ W(0x80, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 80 */
+ W(0x90, 1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1)| /* 90 */
+ W(0xa0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* a0 */
+ W(0xb0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1), /* b0 */
+ W(0xc0, 1,1,1,1,0,0,1,1,1,1,1,1,0,0,0,0)| /* c0 */
+ W(0xd0, 1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1)| /* d0 */
+ W(0xe0, 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0)| /* e0 */
+ W(0xf0, 0,0,1,1,0,1,1,1,1,1,0,0,1,1,1,1) /* f0 */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+/* Good-instruction tables for 32-bit apps -- copied from i386 uprobes */
+
+static const unsigned long good_insns_32[256 / 64] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ W(0x00, 1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0)| /* 00 */
+ W(0x10, 1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0)| /* 10 */
+ W(0x20, 1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1)| /* 20 */
+ W(0x30, 1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1), /* 30 */
+ W(0x40, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 40 */
+ W(0x50, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 50 */
+ W(0x60, 1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0)| /* 60 */
+ W(0x70, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1), /* 70 */
+ W(0x80, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 80 */
+ W(0x90, 1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1)| /* 90 */
+ W(0xa0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* a0 */
+ W(0xb0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1), /* b0 */
+ W(0xc0, 1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0)| /* c0 */
+ W(0xd0, 1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1)| /* d0 */
+ W(0xe0, 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0)| /* e0 */
+ W(0xf0, 0,0,1,1,0,1,1,1,1,1,0,0,1,1,1,1) /* f0 */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+/* Using this for both 64-bit and 32-bit apps */
+static const unsigned long good_2byte_insns[256 / 64] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ W(0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1)| /* 00 */
+ W(0x10, 1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1)| /* 10 */
+ W(0x20, 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1)| /* 20 */
+ W(0x30, 0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0), /* 30 */
+ W(0x40, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 40 */
+ W(0x50, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 50 */
+ W(0x60, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 60 */
+ W(0x70, 1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1), /* 70 */
+ W(0x80, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 80 */
+ W(0x90, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 90 */
+ W(0xa0, 1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,1)| /* a0 */
+ W(0xb0, 1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1), /* b0 */
+ W(0xc0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* c0 */
+ W(0xd0, 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* d0 */
+ W(0xe0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* e0 */
+ W(0xf0, 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0) /* f0 */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+/*
+ * opcodes we'll probably never support:
+ * 63 - arpl
+ * 6c-6d, e4-e5, ec-ed - in
+ * 6e-6f, e6-e7, ee-ef - out
+ * cc, cd - int3, int
+ * cf - iret
+ * d6 - illegal instruction
+ * f1 - int1/icebp
+ * f4 - hlt
+ * fa, fb - cli, sti
+ * 0f - lar, lsl, syscall, clts, sysret, sysenter, sysexit, invd, wbinvd, ud2
+ *
+ * invalid opcodes in 64-bit mode:
+ * 06, 0e, 16, 1e, 27, 2f, 37, 3f, 60-62, c4-c5, d4-d5
+ *
+ * opcodes we may need to refine support for:
+ * 0f - 2-byte instructions: For many of these instructions, the validity
+ * depends on the prefix and/or the reg field. On such instructions, we
+ * just consider the opcode combination valid if it corresponds to any
+ * valid instruction.
+ * 8f - Group 1 - only reg = 0 is OK
+ * c6-c7 - Group 11 - only reg = 0 is OK
+ * d9-df - fpu insns with some illegal encodings
+ * f2, f3 - repnz, repz prefixes. These are also the first byte for
+ * certain floating-point instructions, such as addsd.
+ * fe - Group 4 - only reg = 0 or 1 is OK
+ * ff - Group 5 - only reg = 0-6 is OK
+ *
+ * others -- Do we need to support these?
+ * 0f - (floating-point?) prefetch instructions
+ * 07, 17, 1f - pop es, pop ss, pop ds
+ * 26, 2e, 36, 3e, 64, 65 - es:, cs:, ss:, ds:, fs:, gs: segment prefixes
+ * 67 - addr16 prefix
+ * 9b - wait/fwait
+ * ce - into
+ * f0 - lock prefix
+ */
+
+/*
+ * TODO:
+ * - Where necessary, examine the modrm byte and allow only valid instructions
+ * in the different Groups and fpu instructions.
+ * - Note: If we go past the first byte, do we need to verify that
+ * subsequent bytes were actually there, rather than off the last page?
+ * - Be clearer about which instructions we'll never probe.
+ */
+
+/*
+ * Return 1 if this is a legacy instruction prefix we support, -1 if
+ * it's one we don't support, or 0 if it's not a prefix at all.
+ */
+static inline int check_legacy_prefix(u8 byte)
+{
+ switch (byte) {
+ case 0x26:
+ case 0x2e:
+ case 0x36:
+ case 0x3e:
+ case 0x64:
+ case 0x65:
+ case 0xf0:
+ return -1;
+ case 0x66:
+ case 0x67:
+ case 0xf2:
+ case 0xf3:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static void report_bad_1byte_opcode(int mode, uprobe_opcode_t op)
+{
+ printk(KERN_ERR "In %d-bit apps, "
+ "uprobes does not currently support probing "
+ "instructions whose first byte is 0x%2.2x\n", mode, op);
+}
+
+static void report_bad_2byte_opcode(uprobe_opcode_t op)
+{
+ printk(KERN_ERR "uprobes does not currently support probing "
+ "instructions with the 2-byte opcode 0x0f 0x%2.2x\n", op);
+}
+
+static int validate_insn_32bits(struct uprobe_probept *ppt)
+{
+ uprobe_opcode_t *insn = ppt->insn;
+ int pfx;
+
+ /* Skip good instruction prefixes; reject "bad" ones. */
+ while ((pfx = check_legacy_prefix(insn[0])) == 1)
+ insn++;
+ if (pfx < 0) {
+ report_bad_1byte_opcode(32, insn[0]);
+ return -EPERM;
+ }
+ if (test_bit(insn[0], good_insns_32))
+ return 0;
+ if (insn[0] == 0x0f) {
+ if (test_bit(insn[1], good_2byte_insns))
+ return 0;
+ report_bad_2byte_opcode(insn[1]);
+ } else
+ report_bad_1byte_opcode(32, insn[0]);
+ return -EPERM;
+}
+
+static int validate_insn_64bits(struct uprobe_probept *ppt)
+{
+ uprobe_opcode_t *insn = ppt->insn;
+ int pfx;
+
+ /* Skip good instruction prefixes; reject "bad" ones. */
+ while ((pfx = check_legacy_prefix(insn[0])) == 1)
+ insn++;
+ if (pfx < 0) {
+ report_bad_1byte_opcode(64, insn[0]);
+ return -EPERM;
+ }
+ /* Skip REX prefix. */
+ if ((insn[0] & 0xf0) == 0x40)
+ insn++;
+ if (test_bit(insn[0], good_insns_64))
+ return 0;
+ if (insn[0] == 0x0f) {
+ if (test_bit(insn[1], good_2byte_insns))
+ return 0;
+ report_bad_2byte_opcode(insn[1]);
+ } else
+ report_bad_1byte_opcode(64, insn[0]);
+ return -EPERM;
+}
+
+static int handle_riprel_insn(struct uprobe_probept *ppt);
+
+static
+int arch_validate_probed_insn(struct uprobe_probept *ppt,
+ struct task_struct *tsk)
+{
+ int ret;
+
+ ppt->arch_info.flags = 0x0;
+ ppt->arch_info.rip_target_address = 0x0;
+
+ if (is_32bit_app(tsk))
+ return validate_insn_32bits(ppt);
+ if ((ret = validate_insn_64bits(ppt)) != 0)
+ return ret;
+ (void) handle_riprel_insn(ppt);
+ return 0;
+}
+
+/*
+ * Returns 0 if the indicated instruction has no immediate operand
+ * and/or can't use rip-relative addressing. Otherwise returns
+ * the size of the immediate operand in the instruction. (Note that
+ * for instructions such as "movq $7,xxxx(%rip)" the immediate-operand
+ * field is 4 bytes, even though 8 bytes are stored.)
+ */
+static int immediate_operand_size(u8 opcode1, u8 opcode2, u8 reg,
+ int operand_size_prefix)
+{
+ switch (opcode1) {
+ case 0x6b: /* imul immed,mem,reg */
+ case 0x80: /* Group 1 */
+ case 0x83: /* Group 1 */
+ case 0xc0: /* Group 2 */
+ case 0xc1: /* Group 2 */
+ case 0xc6: /* Group 11 */
+ return 1;
+ case 0x69: /* imul immed,mem,reg */
+ case 0x81: /* Group 1 */
+ case 0xc7: /* Group 11 */
+ return (operand_size_prefix ? 2 : 4);
+ case 0xf6: /* Group 3, reg field == 0 or 1 */
+ return (reg > 1 ? 0 : 1);
+ case 0xf7: /* Group 3, reg field == 0 or 1 */
+ if (reg > 1)
+ return 0;
+ return (operand_size_prefix ? 2 : 4);
+ case 0x0f:
+ /* 2-byte opcodes */
+ switch (opcode2) {
+ /*
+ * Note: 0x71-73 (Groups 12-14) have immediate operands,
+ * but not memory operands.
+ */
+ case 0x70: /* pshuf* immed,mem,reg */
+ case 0xa4: /* shld immed,reg,mem */
+ case 0xac: /* shrd immed,reg,mem */
+ case 0xc2: /* cmpps or cmppd */
+ case 0xc4: /* pinsrw */
+ case 0xc5: /* pextrw */
+ case 0xc6: /* shufps or shufpd */
+ case 0x0f: /* 3DNow extensions */
+ return 1;
+ default:
+ return 0;
+ }
+ }
+ return 0;
+}
+
+/*
+ * TODO: These tables are common for kprobes and uprobes and can be moved
+ * to a common place.
+ */
+static const u64 onebyte_has_modrm[256 / 64] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ W(0x00, 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0)| /* 00 */
+ W(0x10, 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0)| /* 10 */
+ W(0x20, 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0)| /* 20 */
+ W(0x30, 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0), /* 30 */
+ W(0x40, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)| /* 40 */
+ W(0x50, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)| /* 50 */
+ W(0x60, 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0)| /* 60 */
+ W(0x70, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), /* 70 */
+ W(0x80, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 80 */
+ W(0x90, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)| /* 90 */
+ W(0xa0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)| /* a0 */
+ W(0xb0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), /* b0 */
+ W(0xc0, 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0)| /* c0 */
+ W(0xd0, 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1)| /* d0 */
+ W(0xe0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)| /* e0 */
+ W(0xf0, 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1) /* f0 */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+static const u64 twobyte_has_modrm[256 / 64] = {
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ /* ------------------------------- */
+ W(0x00, 1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1)| /* 0f */
+ W(0x10, 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0)| /* 1f */
+ W(0x20, 1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1)| /* 2f */
+ W(0x30, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), /* 3f */
+ W(0x40, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 4f */
+ W(0x50, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 5f */
+ W(0x60, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 6f */
+ W(0x70, 1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1), /* 7f */
+ W(0x80, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)| /* 8f */
+ W(0x90, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* 9f */
+ W(0xa0, 0,0,0,1,1,1,1,1,0,0,0,1,1,1,1,1)| /* af */
+ W(0xb0, 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1), /* bf */
+ W(0xc0, 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0)| /* cf */
+ W(0xd0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* df */
+ W(0xe0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)| /* ef */
+ W(0xf0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0) /* ff */
+ /* ------------------------------- */
+ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
+};
+
+/*
+ * If pp->insn doesn't use rip-relative addressing, return 0. Otherwise,
+ * rewrite the instruction so that it accesses its memory operand
+ * indirectly through a scratch register. Set flags and rip_target_address
+ * in ppt->arch_info accordingly. (The contents of the scratch register
+ * will be saved before we single-step the modified instruction, and
+ * restored afterward.) Return 1.
+ *
+ * We do this because a rip-relative instruction can access only a
+ * relatively small area (+/- 2 GB from the instruction), and the SSOL
+ * area typically lies beyond that area. At least for instructions
+ * that store to memory, we can't single-step the original instruction
+ * and "fix things up" later, because the misdirected store could be
+ * disastrous.
+ *
+ * Some useful facts about rip-relative instructions:
+ * - There's always a modrm byte.
+ * - There's never a SIB byte.
+ * - The offset is always 4 bytes.
+ */
+static int handle_riprel_insn(struct uprobe_probept *ppt)
+{
+ u8 *insn = (u8*) ppt->insn;
+ u8 opcode1, opcode2, modrm, reg;
+ int need_modrm;
+ int operand_size_prefix = 0;
+ int immed_size, instruction_size;
+
+ /*
+ * Skip legacy instruction prefixes. Some of these we don't
+ * support (yet), but here we pretend to support all of them.
+ * Skip the REX prefix, if any.
+ */
+ while (check_legacy_prefix(*insn)) {
+ if (*insn == 0x66)
+ operand_size_prefix = 1;
+ insn++;
+ }
+ if ((*insn & 0xf0) == 0x40)
+ insn++;
+
+ opcode1 = *insn;
+ if (opcode1 == 0x0f) { /* Two-byte opcode. */
+ opcode2 = *++insn;
+ need_modrm = test_bit(opcode2, twobyte_has_modrm);
+ } else { /* One-byte opcode. */
+ opcode2 = 0x0;
+ need_modrm = test_bit(opcode1, onebyte_has_modrm);
+ }
+
+ if (!need_modrm)
+ return 0;
+
+ modrm = *++insn;
+ /*
+ * For rip-relative instructions, the mod field (top 2 bits)
+ * is zero and the r/m field (bottom 3 bits) is 0x5.
+ */
+ if ((modrm & 0xc7) != 0x5)
+ return 0;
+
+ /*
+ * We have a rip-relative instruction. insn points at the
+ * modrm byte. The next 4 bytes are the offset. Beyond the
+ * offset, for some instructions, is the immediate operand.
+ */
+ reg = (modrm >> 3) & 0x7;
+ immed_size = immediate_operand_size(opcode1, opcode2, reg,
+ operand_size_prefix);
+ instruction_size =
+ (insn - (u8*) ppt->insn) /* prefixes + opcodes */
+ + 1 /* modrm byte */
+ + 4 /* offset */
+ + immed_size; /* immediate field */
+#undef DEBUG_UPROBES_RIP
+#ifdef DEBUG_UPROBES_RIP
+{
+ int i;
+ BUG_ON(instruction_size > 15);
+ printk(KERN_INFO "Munging rip-relative insn:");
+ for (i = 0; i < instruction_size; i++)
+ printk(" %2.2x", ppt->insn[i]);
+ printk("\n");
+}
+#endif
+
+ /*
+ * Convert from rip-relative addressing to indirect addressing
+ * via a scratch register. Change the r/m field from 0x5 (%rip)
+ * to 0x0 (%rax) or 0x1 (%rcx), and squeeze out the offset field.
+ */
+ if (reg == 0) {
+ /*
+ * The register operand (if any) is either the A register
+ * (%rax, %eax, etc.) or (if the 0x4 bit is set in the
+ * REX prefix) %r8. In any case, we know the C register
+ * is NOT the register operand, so we use %rcx (register
+ * #1) for the scratch register.
+ */
+ ppt->arch_info.flags = UPFIX_RIP_RCX;
+ /* Change modrm from 00 000 101 to 00 000 001. */
+ *insn = 0x1;
+ } else {
+ /* Use %rax (register #0) for the scratch register. */
+ ppt->arch_info.flags = UPFIX_RIP_RAX;
+ /* Change modrm from 00 xxx 101 to 00 xxx 000 */
+ *insn = (reg << 3);
+ }
+
+ /* Target address = address of next instruction + (signed) offset */
+ insn++;
+ ppt->arch_info.rip_target_address =
+ (long) ppt->vaddr + instruction_size + *((s32*)insn);
+ if (immed_size)
+ memmove(insn, insn+4, immed_size);
+#ifdef DEBUG_UPROBES_RIP
+{
+ int i;
+ printk(KERN_INFO "Munged rip-relative insn: ");
+ for (i = 0; i < instruction_size-4; i++)
+ printk(" %2.2x", ppt->insn[i]);
+ printk("\n");
+ printk(KERN_INFO "Target address = %#lx\n",
+ ppt->arch_info.rip_target_address);
+}
+#endif
+ return 1;
+}
+
+/*
+ * Get an instruction slot from the process's SSOL area, containing the
+ * instruction at ppt's probepoint. Point the rip at that slot, in
+ * preparation for single-stepping out of line.
+ *
+ * If we're emulating a rip-relative instruction, save the contents
+ * of the scratch register and store the target address in that register.
+ */
+static
+void uprobe_pre_ssout(struct uprobe_task *utask, struct uprobe_probept *ppt,
+ struct pt_regs *regs)
+{
+ struct uprobe_ssol_slot *slot;
+
+ slot = uprobe_get_insn_slot(ppt);
+ if (!slot) {
+ utask->doomed = 1;
+ return;
+ }
+
+ regs->rip = (long)slot->insn;
+ utask->singlestep_addr = regs->rip;
+
+ if (ppt->arch_info.flags == UPFIX_RIP_RAX) {
+ utask->arch_info.saved_scratch_register = regs->rax;
+ regs->rax = ppt->arch_info.rip_target_address;
+ } else if (ppt->arch_info.flags == UPFIX_RIP_RCX) {
+ utask->arch_info.saved_scratch_register = regs->rcx;
+ regs->rcx = ppt->arch_info.rip_target_address;
+ }
+}
+
+/*
+ * Called by uprobe_post_ssout() to adjust the return address
+ * pushed by a call instruction executed out of line.
+ */
+static void adjust_ret_addr(unsigned long rsp, long correction,
+ struct uprobe_task *utask)
+{
+ unsigned long nleft;
+ if (is_32bit_app(current)) {
+ s32 ra;
+ nleft = copy_from_user(&ra, (const void __user *) rsp, 4);
+ if (unlikely(nleft != 0))
+ goto fail;
+ ra += (s32) correction;
+ nleft = copy_to_user((void __user *) rsp, &ra, 4);
+ if (unlikely(nleft != 0))
+ goto fail;
+ } else {
+ s64 ra;
+ nleft = copy_from_user(&ra, (const void __user *) rsp, 8);
+ if (unlikely(nleft != 0))
+ goto fail;
+ ra += correction;
+ nleft = copy_to_user((void __user *) rsp, &ra, 8);
+ if (unlikely(nleft != 0))
+ goto fail;
+ }
+ return;
+
+fail:
+ printk(KERN_ERR
+ "uprobes: Failed to adjust return address after"
+ " single-stepping call instruction;"
+ " pid=%d, rsp=%#lx\n", current->pid, rsp);
+ utask->doomed = 1;
+}
+
+/*
+ * Called after single-stepping. ppt->vaddr is the address of the
+ * instruction whose first byte has been replaced by the "int3"
+ * instruction. To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction. The address of this
+ * copy is utask->singlestep_addr.
+ *
+ * This function prepares to return from the post-single-step
+ * trap. We have to fix things up as follows:
+ *
+ * 0) Typically, the new rip is relative to the copied instruction. We
+ * need to make it relative to the original instruction. Exceptions are
+ * return instructions and absolute or indirect jump or call instructions.
+ *
+ * 1) If the single-stepped instruction was a call, the return address
+ * that is atop the stack is the address following the copied instruction.
+ * We need to make it the address following the original instruction.
+ *
+ * 2) If the original instruction was a rip-relative instruction such as
+ * "movl %edx,0xnnnn(%rip)", we have instead executed an equivalent
+ * instruction using a scratch register -- e.g., "movl %edx,(%rax)".
+ * We need to restore the contents of the scratch register and adjust
+ * the rip, keeping in mind that the instruction we executed is 4 bytes
+ * shorter than the original instruction (since we squeezed out the offset
+ * field).
+ */
+static
+void uprobe_post_ssout(struct uprobe_task *utask, struct uprobe_probept *ppt,
+ struct pt_regs *regs)
+{
+ unsigned long next_rip = 0;
+ unsigned long copy_rip = utask->singlestep_addr;
+ unsigned long orig_rip = ppt->vaddr;
+ long correction = (long) (orig_rip - copy_rip);
+ uprobe_opcode_t *insn = ppt->insn;
+ unsigned long flags = ppt->arch_info.flags;
+
+ up_read(&ppt->slot->rwsem);
+
+ if (flags & (UPFIX_RIP_RAX | UPFIX_RIP_RCX)) {
+ if (flags & UPFIX_RIP_RAX)
+ regs->rax = utask->arch_info.saved_scratch_register;
+ else
+ regs->rcx = utask->arch_info.saved_scratch_register;
+ regs->rip += (4 + correction);
+ return;
+ }
+
+ /*
+ * TODO: Move all this instruction parsing to
+ * arch_validate_probed_insn(), and store what we learn in
+ * ppt->arch_info.flags.
+ *
+ * We don't bother skipping prefixes here because none of the
+ * non-rip-relative instructions that require special treatment
+ * involve prefixes.
+ */
+
+ switch (*insn) {
+ case 0xc3: /* ret/lret */
+ case 0xcb:
+ case 0xc2:
+ case 0xca:
+ /* rip is correct */
+ next_rip = regs->rip;
+ break;
+ case 0xe8: /* call relative - Fix return addr */
+ adjust_ret_addr(regs->rsp, correction, utask);
+ break;
+ case 0xff:
+ if ((insn[1] & 0x30) == 0x10) {
+ /* call absolute, indirect */
+ /* Fix return addr; rip is correct. */
+ next_rip = regs->rip;
+ adjust_ret_addr(regs->rsp, correction, utask);
+ } else if ((insn[1] & 0x31) == 0x20 || /* jmp near, absolute indirect */
+ (insn[1] & 0x31) == 0x21) { /* jmp far, absolute indirect */
+ /* rip is correct. */
+ next_rip = regs->rip;
+ }
+ break;
+ case 0xea: /* jmp absolute -- rip is correct */
+ next_rip = regs->rip;
+ break;
+ default:
+ break;
+ }
+
+ if (next_rip)
+ regs->rip = next_rip;
+ else
+ regs->rip += correction;
+}
+
+/*
+ * Replace the return address with the trampoline address. Returns
+ * the original return address.
+ */
+static
+unsigned long arch_hijack_uret_addr(unsigned long trampoline_address,
+ struct pt_regs *regs, struct uprobe_task *utask)
+{
+ int nleft;
+ unsigned long orig_ret_addr = 0; /* clear high bits for 32-bit apps */
+ size_t rasize;
+
+ if (is_32bit_app(current))
+ rasize = 4;
+ else
+ rasize = 8;
+ nleft = copy_from_user(&orig_ret_addr, (const void __user *) regs->rsp,
+ rasize);
+ if (unlikely(nleft != 0))
+ return 0;
+ if (orig_ret_addr == trampoline_address)
+ /*
+ * There's another uretprobe on this function, and it was
+ * processed first, so the return address has already
+ * been hijacked.
+ */
+ return orig_ret_addr;
+
+ nleft = copy_to_user((void __user *) regs->rsp, &trampoline_address,
+ rasize);
+ if (unlikely(nleft != 0)) {
+ if (nleft != rasize) {
+ printk(KERN_ERR "uretprobe_entry_handler: "
+ "return address partially clobbered -- "
+ "pid=%d, %%rsp=%#lx, %%rip=%#lx\n",
+ current->pid, regs->rsp, regs->rip);
+ utask->doomed = 1;
+ } // else nothing written, so no harm
+ return 0;
+ }
+ return orig_ret_addr;
+}
diff --git a/runtime/uprobes/uprobes_x86_64.h b/runtime/uprobes/uprobes_x86_64.h
new file mode 100644
index 00000000..c9345073
--- /dev/null
+++ b/runtime/uprobes/uprobes_x86_64.h
@@ -0,0 +1,84 @@
+#ifndef _ASM_UPROBES_H
+#define _ASM_UPROBES_H
+/*
+ * Userspace Probes (UProbes)
+ * include/asm-x86_64/uprobes.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2006
+ */
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <asm/thread_info.h>
+
+/* Normally defined in Kconfig */
+#define CONFIG_URETPROBES 1
+#define CONFIG_UPROBES_SSOL 1
+
+typedef u8 uprobe_opcode_t;
+#define BREAKPOINT_INSTRUCTION 0xcc
+#define BP_INSN_SIZE 1
+#define MAX_UINSN_BYTES 16
+
+// SLOT_IP should be 16 for 64-bit apps (include/asm-x86_64/elf.h)
+// but 12 for 32-bit apps (arch/x86_64/ia32/ia32_binfmt.c)
+#define SLOT_IP(tsk) (test_tsk_thread_flag(tsk, TIF_IA32) ? 12 : 16)
+
+#define BREAKPOINT_SIGNAL SIGTRAP
+#define SSTEP_SIGNAL SIGTRAP
+
+/* Architecture specific switch for where the IP points after a bp hit */
+#define ARCH_BP_INST_PTR(inst_ptr) (inst_ptr - BP_INSN_SIZE)
+
+#define UPFIX_RIP_RAX 0x1 /* (%rip) insn rewritten to use (%rax) */
+#define UPFIX_RIP_RCX 0x2 /* (%rip) insn rewritten to use (%rcx) */
+
+struct uprobe_probept_arch_info {
+ unsigned long flags;
+ unsigned long rip_target_address;
+};
+
+struct uprobe_task_arch_info {
+ unsigned long saved_scratch_register;
+};
+
+struct uprobe_probept;
+struct uprobe_task;
+
+static int arch_validate_probed_insn(struct uprobe_probept *ppt,
+ struct task_struct *tsk);
+
+/* On x86_64, the int3 traps leaves rip pointing past the int3 instruction. */
+static inline unsigned long arch_get_probept(struct pt_regs *regs)
+{
+ return (unsigned long) (regs->rip - BP_INSN_SIZE);
+}
+
+static inline void arch_reset_ip_for_sstep(struct pt_regs *regs)
+{
+ regs->rip -= BP_INSN_SIZE;
+}
+
+static inline void arch_restore_uret_addr(unsigned long ret_addr,
+ struct pt_regs *regs)
+{
+ regs->rip = ret_addr;
+}
+
+static unsigned long arch_hijack_uret_addr(unsigned long trampoline_addr,
+ struct pt_regs*, struct uprobe_task*);
+#endif /* _ASM_UPROBES_H */
diff --git a/stap.1.in b/stap.1.in
index 8b23c595..8fd7fec7 100644
--- a/stap.1.in
+++ b/stap.1.in
@@ -531,7 +531,8 @@ but also append a newline.
.TP
.BR printd ", " sprintd
Take a string delimiter and two or more values of any type, and print the
-values with the delimiter interposed.
+values with the delimiter interposed. The delimiter must be a literal
+string constant.
.TP
.BR printdln ", " sprintdln
Print values with a delimiter like
@@ -540,7 +541,7 @@ but also append a newline.
.TP
.BR printf ", " sprintf
Take a formatting string and a number of values of corresponding types,
-and print them all.
+and print them all. The format must be a literal string constant.
.PP
The
.IR printf
diff --git a/tapset/ChangeLog b/tapset/ChangeLog
index f242c310..11cdea4c 100644
--- a/tapset/ChangeLog
+++ b/tapset/ChangeLog
@@ -1,3 +1,21 @@
+2007-11-2 Zhaolei <zhaolei@cn.fujitsu.com>
+
+ From Cai Fei <caifei@cn.fujitsu.com>
+ * nfsd.stp: Fix the semantic error caused by the difference of
+ kernel versions.
+ * nfs_proc.stp: Ditto.
+
+2007-11-1 Will Cohen <wcohen@redhat.com>
+
+ * syscall2.stp (sys_remap_file_pages): Fix compile error with kernel
+ version greater than 2.6.23.
+
+2007-11-1 Zhaolei <zhaolei@cn.fujitsu.com>
+
+ From Lai Jiangshan <laijs@cn.fujitsu.com>
+ * rpc.stp (clones_from_clnt): Add CATCH_DEREF_FAULT().
+ (tasks_from_clnt): Ditto.
+
2007-10-17 Martin Hunt <hunt@redhat.com>
PR5000
* endian.stp (set_endian): Remove.
diff --git a/tapset/nfs_proc.stp b/tapset/nfs_proc.stp
index af57a14e..6ec2572e 100644
--- a/tapset/nfs_proc.stp
+++ b/tapset/nfs_proc.stp
@@ -770,7 +770,9 @@ probe nfs.proc2.read_done.return = kernel.function("nfs_read_done").return?,
{
version =2
name = "nfs.proc2.read_done.return"
+%( kernel_v >= "2.6.17" %?
retstr = sprintf("%d", $return)
+%)
}
@@ -799,7 +801,9 @@ probe nfs.proc3.read_done.return = kernel.function("nfs3_read_done").return?,
{
version =3
name = "nfs.proc3.read_done.return"
+%( kernel_v >= "2.6.17" %?
retstr = sprintf("%d", $return)
+%)
}
@@ -830,7 +834,9 @@ probe nfs.proc4.read_done.return = kernel.function("nfs4_read_done").return?,
{
version =4
name = "nfs.proc4.read_done.return"
+%( kernel_v >= "2.6.17" %?
retstr = sprintf("%d", $return)
+%)
}
/*probe nfs.proc.write_setup
@@ -990,7 +996,9 @@ probe nfs.proc2.write_done.return = kernel.function("nfs_write_done").return ?,
{
version =2
name = "nfs.proc2.write_done.return"
+%( kernel_v >= "2.6.17" %?
retstr = sprintf("%d", $return)
+%)
}
probe nfs.proc3.write_done = kernel.function("nfs3_write_done") ?,
@@ -1019,7 +1027,9 @@ probe nfs.proc3.write_done.return = kernel.function("nfs3_write_done").return ?,
{
version =3
name = "nfs.proc3.write_done.return"
+%( kernel_v >= "2.6.17" %?
retstr = sprintf("%d", $return)
+%)
}
probe nfs.proc4.write_done = kernel.function("nfs4_write_done") ?,
@@ -1050,7 +1060,9 @@ probe nfs.proc4.write_done.return = kernel.function("nfs4_write_done").return ?,
{
version =4
name = "nfs.proc4.write_done.return"
+%( kernel_v >= "2.6.17" %?
retstr = sprintf("%d", $return)
+%)
}
@@ -1182,7 +1194,9 @@ probe nfs.proc3.commit_done.return = kernel.function("nfs3_commit_done").return
{
version =3
name = "nfs.proc3.commit_done.return"
+%( kernel_v >= "2.6.17" %?
retstr = sprintf("%d", $return)
+%)
}
probe nfs.proc4.commit_done = kernel.function("nfs4_commit_done") ?,
@@ -1213,7 +1227,9 @@ probe nfs.proc4.commit_done.return = kernel.function("nfs4_commit_done").return
{
version =4
name = "nfs.proc4.commit_done.return"
+%( kernel_v >= "2.6.17" %?
retstr = sprintf("%d", $return)
+%)
}
/*probe nfs.proc.open
*
diff --git a/tapset/nfsd.stp b/tapset/nfsd.stp
index 92a85cf1..52add690 100644
--- a/tapset/nfsd.stp
+++ b/tapset/nfsd.stp
@@ -809,7 +809,9 @@ probe nfsd.read = kernel.function("nfsd_read") ?,
{
fh = __svc_fh($fhp)
+%( kernel_v >= "2.6.12" %?
file = $file
+%)
count = p_long($count)
offset = $offset
vec = $vec
@@ -847,7 +849,9 @@ probe nfsd.write = kernel.function("nfsd_write")?,
{
fh = __svc_fh($fhp)
+%( kernel_v >= "2.6.12" %?
file = $file
+%)
count = $cnt
offset = $offset
vec = $vec
diff --git a/tapset/rpc.stp b/tapset/rpc.stp
index eecdb796..38f53401 100644
--- a/tapset/rpc.stp
+++ b/tapset/rpc.stp
@@ -949,6 +949,7 @@ function clones_from_clnt:long(clnt:long)
struct rpc_clnt *clnt = (struct rpc_clnt *)(long)THIS->clnt;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
THIS->__retvalue = kread(&(clnt->cl_count));
+ CATCH_DEREF_FAULT();
#else
THIS->__retvalue = -1;
#endif
@@ -959,6 +960,7 @@ function tasks_from_clnt:long(clnt:long)
struct rpc_clnt *clnt = (struct rpc_clnt *)(long)THIS->clnt;
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,22)
THIS->__retvalue = kread(&(clnt->cl_users));
+ CATCH_DEREF_FAULT();
#else
THIS->__retvalue = -1;
#endif
diff --git a/tapset/syscalls2.stp b/tapset/syscalls2.stp
index e702bbcd..aa22377c 100644
--- a/tapset/syscalls2.stp
+++ b/tapset/syscalls2.stp
@@ -784,7 +784,11 @@ probe syscall.remap_file_pages = kernel.function("sys_remap_file_pages") ? {
name = "remap_file_pages"
start = $start
size = $size
+%( kernel_vr > "2.6.23" %?
+ prot = $prot
+%:
prot = $__prot
+%)
pgoff = $pgoff
flags = $flags
argstr = sprintf("%p, %p, %p, %p, %p", start, size, prot,
diff --git a/testsuite/ChangeLog b/testsuite/ChangeLog
index 9f54311e..3d71c5a4 100644
--- a/testsuite/ChangeLog
+++ b/testsuite/ChangeLog
@@ -1,3 +1,27 @@
+2007-10-25 Martin Hunt <hunt@redhat.com>
+
+ * systemtap.printf/printd.exp: New.
+ Runs printd.stp and printdln.stp.
+ * systemtap.printf/printdln.stp: Removed bad tests.
+ * systemtap.printf/printd.stp: Ditto.
+
+2007-10-25 Martin Hunt <hunt@redhat.com>
+
+ * systemtap.printf/print_char.*: New test.
+ * systemtap.printf/print.*: New test.
+ * systemtap.printf/println.*: New test.
+
+ * systemtap.maps/elision.*: New tests.
+
+ * config/unix.exp: Added stap_run_exact.
+ * lib/stap_run_exact.exp: New. Like stap_run2 but
+ takes a seperate test name.
+
+2007-10-25 Martin Hunt <hunt@redhat.com>
+
+ * systemtap.printf/printd.stp: New
+ * systemtap.printf/printdln.stp: New
+
2007-10-16 Martin Hunt <hunt@redhat.com>
PR 5000
diff --git a/testsuite/config/unix.exp b/testsuite/config/unix.exp
index b3be70f5..625c5604 100644
--- a/testsuite/config/unix.exp
+++ b/testsuite/config/unix.exp
@@ -1,4 +1,5 @@
load_lib "systemtap.exp"
load_lib "stap_run2.exp"
+load_lib "stap_run_exact.exp"
load_lib "stap_run_binary.exp"
load_lib "stap_run.exp"
diff --git a/testsuite/lib/stap_run_exact.exp b/testsuite/lib/stap_run_exact.exp
new file mode 100644
index 00000000..6a473798
--- /dev/null
+++ b/testsuite/lib/stap_run_exact.exp
@@ -0,0 +1,30 @@
+# stap_run_exact.exp
+#
+# Simple script for testing multiple lines of exact output.
+
+# stap_run_exact TEST_NAME filename args
+# TEST_NAME is the name printed
+# filename is path to the current test
+# Additional arguments are passed to stap as-is.
+#
+# global result_string must be set to the expected output
+
+proc stap_run_exact { TEST_NAME test_file_name args } {
+ if {[info procs installtest_p] != "" && ![installtest_p]} { untested $TEST_NAME; return }
+
+ set cmd [concat stap $args $test_file_name]
+ catch {eval exec $cmd} res
+
+ set n 0
+ set expected [split $::result_string "\n"]
+ foreach line [split $res "\n"] {
+ if {![string equal $line [lindex $expected $n]]} {
+ fail "$TEST_NAME"
+ send_log "line [expr $n + 1]: expected \"[lindex $expected $n]\"\n"
+ send_log "Got \"$line\"\n"
+ return
+ }
+ incr n
+ }
+ pass "$TEST_NAME"
+}
diff --git a/testsuite/systemtap.maps/elision.exp b/testsuite/systemtap.maps/elision.exp
new file mode 100755
index 00000000..60c52bbe
--- /dev/null
+++ b/testsuite/systemtap.maps/elision.exp
@@ -0,0 +1,397 @@
+# test elision of zero buckets in histograms
+
+set myresults(-1) {value |-------------------------------------------------- count
+ 300 | 0
+ 350 | 0
+ 400 |@ 1
+ 450 | 0
+ 500 | 0
+ 550 | 0
+ 600 | 0
+ 650 | 0
+ 700 | 0
+ 750 | 0
+ 800 | 0
+ 850 | 0
+ 900 | 0
+ 950 |@ 1
+ 1000 | 0
+ 1050 | 0
+
+value |-------------------------------------------------- count
+ 300 | 0
+ 350 | 0
+ 400 |@ 1
+ 450 | 0
+ 500 |@ 1
+ 550 | 0
+ 600 | 0
+
+value |-------------------------------------------------- count
+ 0 |@ 1
+ 50 | 0
+ 100 | 0
+
+value |-------------------------------------------------- count
+ 0 | 0
+ 50 |@ 1
+ 100 | 0
+ 150 | 0
+
+value |-------------------------------------------------- count
+ 1350 | 0
+ 1400 | 0
+ 1450 |@ 1
+ 1500 | 0
+
+value |-------------------------------------------------- count
+ 1400 | 0
+ 1450 | 0
+ 1500 |@ 1
+
+value |-------------------------------------------------- count
+ -30 | 0
+ -20 | 0
+ -10 |@ 1
+ 0 | 0
+ 10 | 0
+
+value |-------------------------------------------------- count
+ -20 | 0
+ -10 | 0
+ 0 |@ 1
+ 10 | 0
+ 20 | 0
+
+value |-------------------------------------------------- count
+ 32 | 0
+ 64 | 0
+ 128 |@ 1
+ 256 | 0
+ 512 | 0
+
+value |-------------------------------------------------- count
+ 0 |@ 1
+ 1 | 0
+ 2 | 0
+
+ value |-------------------------------------------------- count
+-16384 | 0
+ -8192 | 0
+ -4096 |@ 1
+ -2048 | 0
+ -1024 | 0
+ -512 | 0
+ -256 | 0
+ -128 | 0
+ -64 | 0
+ -32 | 0
+ -16 | 0
+ -8 | 0
+ -4 | 0
+ -2 |@ 1
+ -1 | 0
+ 0 | 0
+
+}
+set myresults(0) {value |-------------------------------------------------- count
+ ~
+ 400 |@ 1
+ ~
+ 950 |@ 1
+
+value |-------------------------------------------------- count
+ ~
+ 400 |@ 1
+ ~
+ 500 |@ 1
+
+value |-------------------------------------------------- count
+ 0 |@ 1
+
+value |-------------------------------------------------- count
+ ~
+ 50 |@ 1
+
+value |-------------------------------------------------- count
+ ~
+ 1450 |@ 1
+
+value |-------------------------------------------------- count
+ ~
+ 1500 |@ 1
+
+value |-------------------------------------------------- count
+ ~
+ -10 |@ 1
+
+value |-------------------------------------------------- count
+ ~
+ 0 |@ 1
+
+value |-------------------------------------------------- count
+ ~
+ 128 |@ 1
+
+value |-------------------------------------------------- count
+ 0 |@ 1
+
+ value |-------------------------------------------------- count
+ ~
+ -4096 |@ 1
+ ~
+ -2 |@ 1
+
+}
+set myresults(1) {value |-------------------------------------------------- count
+ ~
+ 350 | 0
+ 400 |@ 1
+ 450 | 0
+ ~
+ 900 | 0
+ 950 |@ 1
+ 1000 | 0
+
+value |-------------------------------------------------- count
+ ~
+ 350 | 0
+ 400 |@ 1
+ 450 | 0
+ 500 |@ 1
+ 550 | 0
+
+value |-------------------------------------------------- count
+ 0 |@ 1
+ 50 | 0
+
+value |-------------------------------------------------- count
+ 0 | 0
+ 50 |@ 1
+ 100 | 0
+
+value |-------------------------------------------------- count
+ ~
+ 1400 | 0
+ 1450 |@ 1
+ 1500 | 0
+
+value |-------------------------------------------------- count
+ ~
+ 1450 | 0
+ 1500 |@ 1
+
+value |-------------------------------------------------- count
+ ~
+ -20 | 0
+ -10 |@ 1
+ 0 | 0
+
+value |-------------------------------------------------- count
+ ~
+ -10 | 0
+ 0 |@ 1
+ 10 | 0
+
+value |-------------------------------------------------- count
+ ~
+ 64 | 0
+ 128 |@ 1
+ 256 | 0
+
+value |-------------------------------------------------- count
+ 0 |@ 1
+ 1 | 0
+
+ value |-------------------------------------------------- count
+ ~
+ -8192 | 0
+ -4096 |@ 1
+ -2048 | 0
+ ~
+ -4 | 0
+ -2 |@ 1
+ -1 | 0
+
+}
+set myresults(2) {value |-------------------------------------------------- count
+ 300 | 0
+ 350 | 0
+ 400 |@ 1
+ 450 | 0
+ 500 | 0
+ ~
+ 850 | 0
+ 900 | 0
+ 950 |@ 1
+ 1000 | 0
+ 1050 | 0
+
+value |-------------------------------------------------- count
+ 300 | 0
+ 350 | 0
+ 400 |@ 1
+ 450 | 0
+ 500 |@ 1
+ 550 | 0
+ 600 | 0
+
+value |-------------------------------------------------- count
+ 0 |@ 1
+ 50 | 0
+ 100 | 0
+
+value |-------------------------------------------------- count
+ 0 | 0
+ 50 |@ 1
+ 100 | 0
+ 150 | 0
+
+value |-------------------------------------------------- count
+ 1350 | 0
+ 1400 | 0
+ 1450 |@ 1
+ 1500 | 0
+
+value |-------------------------------------------------- count
+ 1400 | 0
+ 1450 | 0
+ 1500 |@ 1
+
+value |-------------------------------------------------- count
+ -30 | 0
+ -20 | 0
+ -10 |@ 1
+ 0 | 0
+ 10 | 0
+
+value |-------------------------------------------------- count
+ -20 | 0
+ -10 | 0
+ 0 |@ 1
+ 10 | 0
+ 20 | 0
+
+value |-------------------------------------------------- count
+ 32 | 0
+ 64 | 0
+ 128 |@ 1
+ 256 | 0
+ 512 | 0
+
+value |-------------------------------------------------- count
+ 0 |@ 1
+ 1 | 0
+ 2 | 0
+
+ value |-------------------------------------------------- count
+-16384 | 0
+ -8192 | 0
+ -4096 |@ 1
+ -2048 | 0
+ -1024 | 0
+ ~
+ -8 | 0
+ -4 | 0
+ -2 |@ 1
+ -1 | 0
+ 0 | 0
+
+}
+set myresults(3) {value |-------------------------------------------------- count
+ 300 | 0
+ 350 | 0
+ 400 |@ 1
+ 450 | 0
+ 500 | 0
+ 550 | 0
+ ~
+ 800 | 0
+ 850 | 0
+ 900 | 0
+ 950 |@ 1
+ 1000 | 0
+ 1050 | 0
+
+value |-------------------------------------------------- count
+ 300 | 0
+ 350 | 0
+ 400 |@ 1
+ 450 | 0
+ 500 |@ 1
+ 550 | 0
+ 600 | 0
+
+value |-------------------------------------------------- count
+ 0 |@ 1
+ 50 | 0
+ 100 | 0
+
+value |-------------------------------------------------- count
+ 0 | 0
+ 50 |@ 1
+ 100 | 0
+ 150 | 0
+
+value |-------------------------------------------------- count
+ 1350 | 0
+ 1400 | 0
+ 1450 |@ 1
+ 1500 | 0
+
+value |-------------------------------------------------- count
+ 1400 | 0
+ 1450 | 0
+ 1500 |@ 1
+
+value |-------------------------------------------------- count
+ -30 | 0
+ -20 | 0
+ -10 |@ 1
+ 0 | 0
+ 10 | 0
+
+value |-------------------------------------------------- count
+ -20 | 0
+ -10 | 0
+ 0 |@ 1
+ 10 | 0
+ 20 | 0
+
+value |-------------------------------------------------- count
+ 32 | 0
+ 64 | 0
+ 128 |@ 1
+ 256 | 0
+ 512 | 0
+
+value |-------------------------------------------------- count
+ 0 |@ 1
+ 1 | 0
+ 2 | 0
+
+ value |-------------------------------------------------- count
+-16384 | 0
+ -8192 | 0
+ -4096 |@ 1
+ -2048 | 0
+ -1024 | 0
+ -512 | 0
+ ~
+ -16 | 0
+ -8 | 0
+ -4 | 0
+ -2 |@ 1
+ -1 | 0
+ 0 | 0
+
+}
+
+
+for {set i -1} { $i < 4} {incr i} {
+ set test "elision-$i"
+ set ::result_string $myresults($i)
+ stap_run_exact "elision$i" $srcdir/$subdir/elision.stp -DHIST_ELISION=$i
+}
+
+
diff --git a/testsuite/systemtap.maps/elision.stp b/testsuite/systemtap.maps/elision.stp
new file mode 100644
index 00000000..e903f569
--- /dev/null
+++ b/testsuite/systemtap.maps/elision.stp
@@ -0,0 +1,54 @@
+# test that we can check the count of an empty array
+
+global a,b,c
+
+probe begin
+{
+ a <<< 444
+ a <<< 999
+ print(@hist_linear(a, 0, 1500, 50))
+ delete a
+
+ a <<< 444
+ a <<< 500
+ print(@hist_linear(a, 0, 1500, 50))
+ delete a
+
+ a <<< 0
+ print(@hist_linear(a, 0, 1500, 50))
+ delete a
+
+ a <<< 50
+ print(@hist_linear(a, 0, 1500, 50))
+ delete a
+
+ a <<< 1450
+ print(@hist_linear(a, 0, 1500, 50))
+ delete a
+
+ a <<< 1500
+ print(@hist_linear(a, 0, 1500, 50))
+ delete a
+
+ b <<< -10
+ print(@hist_linear(b, -100, 100, 10))
+ delete b
+
+ b <<< 0
+ print(@hist_linear(b, -100, 100, 10))
+ delete b
+
+ c <<< 128
+ print(@hist_log(c))
+ delete c
+
+ c <<< 0
+ print(@hist_log(c))
+ delete c
+
+ c <<< -2
+ c <<< -4096
+ print(@hist_log(c))
+
+ exit()
+}
diff --git a/testsuite/systemtap.printf/print.exp b/testsuite/systemtap.printf/print.exp
new file mode 100644
index 00000000..3a4de529
--- /dev/null
+++ b/testsuite/systemtap.printf/print.exp
@@ -0,0 +1,18 @@
+set test "print"
+set ::result_string {hello world
+777
+hello world
+hello world
+foo
+foobar
+foobar
+foobar
+foo99
+99foo
+777
+888
+123456789
+hello999
+999hello
+}
+stap_run2 $srcdir/$subdir/$test.stp
diff --git a/testsuite/systemtap.printf/print.stp b/testsuite/systemtap.printf/print.stp
new file mode 100644
index 00000000..ae770935
--- /dev/null
+++ b/testsuite/systemtap.printf/print.stp
@@ -0,0 +1,47 @@
+# test the print function with string and integers,
+# variables and constants.
+
+probe begin {
+ a = "hello"
+ b = " "
+ c = "world"
+ d = 777
+
+ # print variables
+ print(a)
+ print(b)
+ print(c)
+ print("\n")
+
+ print(d)
+ print("\n")
+
+ print(a,b,c,"\n")
+ print(a.b.c."\n")
+
+ # print literals
+ print("foo")
+ print("\n")
+
+ print("foo")
+ print("bar")
+ print("\n")
+
+ print("foo","bar","\n")
+ print("foo"."bar"."\n")
+
+ print("foo",99,"\n")
+ print(99, "foo","\n")
+
+ print(777)
+ print("\n")
+ print(888,"\n")
+
+ print(123,456,789,"\n")
+
+ # mixed
+ print(a,999,"\n")
+ print(999,a,"\n")
+
+ exit()
+}
diff --git a/testsuite/systemtap.printf/print_char.exp b/testsuite/systemtap.printf/print_char.exp
new file mode 100644
index 00000000..bab056dd
--- /dev/null
+++ b/testsuite/systemtap.printf/print_char.exp
@@ -0,0 +1,5 @@
+set test "print_char"
+set ::result_string {ABC
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+}
+stap_run2 $srcdir/$subdir/$test.stp
diff --git a/testsuite/systemtap.printf/print_char.stp b/testsuite/systemtap.printf/print_char.stp
new file mode 100644
index 00000000..0900fe1d
--- /dev/null
+++ b/testsuite/systemtap.printf/print_char.stp
@@ -0,0 +1,16 @@
+# test the print_char function
+
+probe begin {
+ endl = 10
+ print_char(65)
+ print_char(66)
+ print_char(67)
+ print_char(endl)
+
+ for (i = 65; i < 91; i++)
+ print_char(i)
+ print_char(endl)
+
+
+ exit()
+}
diff --git a/testsuite/systemtap.printf/printd.exp b/testsuite/systemtap.printf/printd.exp
new file mode 100644
index 00000000..5ba90341
--- /dev/null
+++ b/testsuite/systemtap.printf/printd.exp
@@ -0,0 +1,20 @@
+# printd and printdln tests
+
+# both tests have the same result
+
+set ::result_string {hello world
+hello<-->world<-->777
+foo *** bar
+foo *** bar *** baz
+foo,99
+99,foo
+123 456 789
+hello-999
+999-hello
+}
+
+foreach x {"d" "dln"} {
+ set test "print$x"
+ stap_run_exact $test $srcdir/$subdir/$test.stp
+}
+
diff --git a/testsuite/systemtap.printf/printd.stp b/testsuite/systemtap.printf/printd.stp
new file mode 100644
index 00000000..8dcff3f5
--- /dev/null
+++ b/testsuite/systemtap.printf/printd.stp
@@ -0,0 +1,36 @@
+# test the printd function with string and integers,
+# variables and constants.
+
+probe begin {
+ a = "hello"
+ c = "world"
+ d = 777
+
+ # print variables
+ printd(" ",a,c)
+ println("")
+ printd("<-->",a,c,d)
+ println("")
+
+ # print literals
+ printd(" *** ","foo","bar")
+ println("")
+ printd(" *** ","foo","bar","baz")
+ println("")
+
+ printd(",","foo",99)
+ println("")
+ printd(",",99, "foo")
+ println("")
+
+ printd(" ",123,456,789)
+ println("")
+
+ # mixed
+ printd("-",a,999)
+ println("")
+ printd("-",999,a)
+ println("")
+
+ exit()
+}
diff --git a/testsuite/systemtap.printf/printdln.stp b/testsuite/systemtap.printf/printdln.stp
new file mode 100644
index 00000000..ed78eac5
--- /dev/null
+++ b/testsuite/systemtap.printf/printdln.stp
@@ -0,0 +1,27 @@
+# test the printdln function with string and integers,
+# variables and constants.
+
+probe begin {
+ a = "hello"
+ c = "world"
+ d = 777
+
+ # print variables
+ printdln(" ",a,c)
+ printdln("<-->",a,c,d)
+
+ # print literals
+ printdln(" *** ","foo","bar")
+ printdln(" *** ","foo","bar","baz")
+
+ printdln(",","foo",99)
+ printdln(",",99, "foo")
+
+ printdln(" ",123,456,789)
+
+ # mixed
+ printdln("-",a,999)
+ printdln("-",999,a)
+
+ exit()
+}
diff --git a/testsuite/systemtap.printf/println.exp b/testsuite/systemtap.printf/println.exp
new file mode 100644
index 00000000..8deba53f
--- /dev/null
+++ b/testsuite/systemtap.printf/println.exp
@@ -0,0 +1,18 @@
+set test "println"
+set ::result_string {hello
+
+world
+777
+hello world
+hello world
+foo
+foobar
+foobar
+777
+foo99
+99foo
+123456789
+hello999
+999hello
+}
+stap_run2 $srcdir/$subdir/$test.stp
diff --git a/testsuite/systemtap.printf/println.stp b/testsuite/systemtap.printf/println.stp
new file mode 100644
index 00000000..0b02f0ac
--- /dev/null
+++ b/testsuite/systemtap.printf/println.stp
@@ -0,0 +1,37 @@
+# test the println function with string and integers,
+# variables and constants.
+
+probe begin {
+ a = "hello"
+ b = " "
+ c = "world"
+ d = 777
+
+ # println variables
+ println(a)
+ println(b)
+ println(c)
+
+ println(d)
+
+ println(a,b,c)
+ println(a.b.c)
+
+ # println literals
+ println("foo")
+
+ println("foo","bar")
+ println("foo"."bar")
+
+ println(777)
+ println("foo",99)
+ println(99, "foo")
+
+ println(123,456,789)
+
+ # mixed
+ println(a,999)
+ println(999,a)
+
+ exit()
+}