This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC 3/3] Kprobes: userspace probes providing callback functions exection


Hi Roland,

On Wed, Sep 14, 2005 at 06:26:17PM -0700, Roland McGrath wrote:
> > Single sepping in-line has a drawback, that the probes might get missed
> > during single stepping in SMP environment. The next patch overcomes this
> > limitation of probes misses in SMP environment.
> 
> This crucial comment came in patch 3 of 3.
> What is the solution here?

The patch to the above problem is not complete yet and this patch need 
some more work. Below are some code snippets with comments that
addresses the above problem.
Please review and provide your comments.

Thanks
Prasanna 

The method adopted in the present userspace probes involves inserting
a breakpoint instruction at a given address, since it is the smallest
instruction in all architectures. Once the trace data is collected in
the breakpoint handler, breakpoint instruction is replaced by original
instruction and single stepped and after single stepping the original
instruction is replaced by breakpoint instruction. This method works 
in uniprocessor environment and all the instances of the probe point
are traced successfully. But in Symmetric multiprocessor environment,
probes seem to get missed on other processors while some one processor
single steps on the original instruction. 

Probe misses can be avoided in smp environment by single stepping 
out-of-line similar to kprobes. But kprobes executes the original 
instruction in the kernel mode, but for userspace probes cannot 
execute the instruction in the kernel mode. Hence we adopt to find 
free space in the current process address space. User processes use
stack space to store local variables, agruments and return values. 
Normally the stack space either below or above the stack pointer 
indicates the free stack space. If the stack grows downwards, the 
stack space below the stack pointer indicates the unused stack free 
space and if the stack grows upwards, the stack space above the stack
pointer indicates the unused stack free space.

The instruction to be single stepped can modify the stack space, hence
before using the unused stack free space, sufficient stack space 
should be left. Now the original instruction is copied to the bottom 
of the stack page and check should be also made such that the copied
instruction does not cross the page boundry. The copied instruction 
is then single stepped. Thus accomplishing single stepping out-of-line.

Several architectures does not allow the instruction to be executed 
from the stack location, since no-exec bit is set for the stack 
pages. In thouse architectures, the page table entry corresponding 
to the stack page is identified and the no-exec bit is unset making 
the instruction on that stack page to be executed.

There are situations where even the unused free stack space is not 
enough for the user instruction to be copied and single stepped. In 
such situations, the virtual memory area(vma) can be expanded beyond 
the current stack vma. Now this expaneded stack can be used to copy
the original instruction and single step out-of-line.

Signed-of-by: Prasanna S Panchamukhi <prasanna@in.ibm.com>

---



---

 linux-2.6.13-prasanna/arch/i386/kernel/kprobes.c |   52 ++++++++++++++++++++---
 1 files changed, 46 insertions(+), 6 deletions(-)

diff -puN arch/i386/kernel/kprobes.c~kprobes_avoid_smp_missprobes arch/i386/kernel/kprobes.c
--- linux-2.6.13/arch/i386/kernel/kprobes.c~kprobes_avoid_smp_missprobes	2005-09-15 13:56:24.105192248 +0530
+++ linux-2.6.13-prasanna/arch/i386/kernel/kprobes.c	2005-09-15 13:56:24.137187384 +0530
@@ -44,6 +44,7 @@ static unsigned long kprobe_status_prev,
 static struct pt_regs jprobe_saved_regs;
 static long *jprobe_saved_esp;
 static kprobe_opcode_t *uprobe_addr;
+static kprobe_opcode_t *singlestep_addr;
 struct uprobe *current_uprobe;
 /* copy of the kernel stack at the probe fire time */
 static kprobe_opcode_t jprobes_stack[MAX_STACK_SIZE];
@@ -131,6 +132,43 @@ static inline void set_current_kprobe(st
 	if (is_IF_modifier(p->opcode))
 		kprobe_saved_eflags &= ~IF_MASK;
 }
+/*
+ * This routine provides the functionality of single stepping out of line by
+ * copying the original instruction in the user process free stack space.
+ */
+static inline int uprobe_single_step(struct uprobe *p, struct pt_regs *regs)
+{
+	unsigned long page_addr, stack_addr = regs->esp;
+	struct vm_area_struct *vma = NULL;
+	int size = MAX_INSN_SIZE * sizeof(kprobe_opcode_t);
+
+	if (!(vma = find_vma(current->mm, PAGE_ALIGN(stack_addr)))) {
+		printk("No vma found\n");
+		return -ENOENT;
+	}
+
+	if (vma->vm_flags & VM_GROWSDOWN) {
+		page_addr = PAGE_ALIGN(stack_addr);
+		if (((stack_addr - sizeof(long long)) - page_addr) > size) {
+			if (copy_to_user((unsigned long *)(page_addr + size),
+					(unsigned long *)&p->kp.ainsn.insn,
+					 size))
+				return -EFAULT;
+			regs->eip = page_addr + size;
+		}
+	} else {
+		page_addr = PAGE_ALIGN(stack_addr) + PAGE_SIZE;
+		if ((page_addr - (stack_addr + sizeof(long long))) > size) {
+			if (copy_to_user((unsigned long *)page_addr,
+					(unsigned long *)&p->kp.ainsn.insn,
+\					 size))
+				return -EFAULT;
+			regs->eip = page_addr;
+		}
+	}
+	singlestep_addr = (kprobe_opcode_t *)regs->eip;
+	return 0;
+}
 
 static inline void prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
 {
@@ -140,10 +178,13 @@ static inline void prepare_singlestep(st
 	if (p->opcode == BREAKPOINT_INSTRUCTION)
 		regs->eip = (unsigned long)p->addr;
 	else {
-		if (!kernel_text_address((unsigned long)p->addr)) {
-			arch_disarm_uprobe(current_uprobe);
-			regs->eip = (unsigned long)uprobe_addr;
-		} else
+		if (!kernel_text_address((unsigned long)p->addr))
+			uprobe_single_step(current_uprobe, regs);
+		/* TODO if no space left on the stack page, expand the
+		 * process address space beyond the stack and copy the
+		 * instruction to that expanded stack and then single step.
+		 */
+		else
 			regs->eip = (unsigned long)&p->ainsn.insn;
 
 	}
@@ -426,9 +467,8 @@ static void resume_execution_user(struct
 {
 	unsigned long delta;
 	/*TODO : need to fixup special instructions as done with kernel probes */
-	delta = (unsigned long) regs->eip - (unsigned long)uprobe_addr;
+	delta = (unsigned long) regs->eip - (unsigned long)singlestep_addr;
 	regs->eip = (unsigned long) (uprobe_addr + delta);
-	arch_arm_uprobe(p);
 	p->kp.addr = uprobe_addr;
 	regs->eflags &= ~TF_MASK;
 }

_
-- 

Prasanna S Panchamukhi
Linux Technology Center
India Software Labs, IBM Bangalore
Ph: 91-80-25044636
<prasanna@in.ibm.com>


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]