diff options
author | Frank Ch. Eigler <fche@elastic.org> | 2007-11-04 14:11:15 -0500 |
---|---|---|
committer | Frank Ch. Eigler <fche@elastic.org> | 2007-11-04 14:11:15 -0500 |
commit | 806b26a85d30f59af7dcb0025c68a50bf4bbb352 (patch) | |
tree | 8b1cc9edab49eda46278199a7027d956549f6ddb | |
parent | 24993e4fcae48ca014e6b53f3f9a011c9cfa8f06 (diff) | |
parent | 600e72b28cc0676dc581b8fc5d54c09772979b79 (diff) | |
download | systemtap-steved-806b26a85d30f59af7dcb0025c68a50bf4bbb352.tar.gz systemtap-steved-806b26a85d30f59af7dcb0025c68a50bf4bbb352.tar.xz systemtap-steved-806b26a85d30f59af7dcb0025c68a50bf4bbb352.zip |
Merge branch 'master' of git://sources.redhat.com/git/systemtap
35 files changed, 1797 insertions, 79 deletions
@@ -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 */ @@ -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() +} |