[PATCH v4 2/2] powerpc: Uprobes port to powerpc
Michael Ellerman
michael at ellerman.id.au
Thu Aug 23 14:28:20 EST 2012
On Wed, 2012-08-22 at 13:57 +0530, Ananth N Mavinakayanahalli wrote:
> From: Ananth N Mavinakayanahalli <ananth at in.ibm.com>
>
> This is the port of uprobes to powerpc. Usage is similar to x86.
Hi Ananth,
Excuse my ignorance of uprobes, some comments inline ...
> [root at xxxx ~]# ./bin/perf probe -x /lib64/libc.so.6 malloc
> Added new event:
> probe_libc:malloc (on 0xb4860)
>
> You can now use it in all perf tools, such as:
>
> perf record -e probe_libc:malloc -aR sleep 1
Is there a test suite for any of this?
> Index: linux-tip-16aug/arch/powerpc/include/asm/uprobes.h
> ===================================================================
> --- /dev/null
> +++ linux-tip-16aug/arch/powerpc/include/asm/uprobes.h
> @@ -0,0 +1,58 @@
> +#ifndef _ASM_UPROBES_H
> +#define _ASM_UPROBES_H
> +/*
> + * User-space Probes (UProbes) for powerpc
> + *
> + * 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, 2007-2012
The lawyers say we shouldn't use (C).
Is it really copyright IBM 2007-2012? Or is that because you copied
another header?
> +typedef unsigned int uprobe_opcode_t;
I'd prefer u32.
It would be nice if someone could consolidate this with kprobe_opcode_t.
> +#define MAX_UINSN_BYTES 4
> +#define UPROBE_XOL_SLOT_BYTES (MAX_UINSN_BYTES)
> +
> +#define UPROBE_SWBP_INSN 0x7fe00008
This is just "trap" ?
> +#define UPROBE_SWBP_INSN_SIZE 4 /* swbp insn size in bytes */
> +
> +#define IS_TW(instr) (((instr) & 0xfc0007fe) == 0x7c000008)
> +#define IS_TD(instr) (((instr) & 0xfc0007fe) == 0x7c000088)
> +#define IS_TDI(instr) (((instr) & 0xfc000000) == 0x08000000)
> +#define IS_TWI(instr) (((instr) & 0xfc000000) == 0x0c000000)
> +
> +#define is_trap(instr) (IS_TW(instr) || IS_TD(instr) || \
> + IS_TWI(instr) || IS_TDI(instr))
These seem to be duplicated in kprobes.h, can we consolidate them.
> +struct arch_uprobe {
> + u8 insn[MAX_UINSN_BYTES];
> +};
Why not uprobe_opcode_t insn ?
> Index: linux-tip-16aug/arch/powerpc/kernel/signal.c
> ===================================================================
> --- linux-tip-16aug.orig/arch/powerpc/kernel/signal.c
> +++ linux-tip-16aug/arch/powerpc/kernel/signal.c
> @@ -11,6 +11,7 @@
>
> #include <linux/tracehook.h>
> #include <linux/signal.h>
> +#include <linux/uprobes.h>
> #include <linux/key.h>
> #include <asm/hw_breakpoint.h>
> #include <asm/uaccess.h>
> @@ -157,6 +158,11 @@ static int do_signal(struct pt_regs *reg
>
> void do_notify_resume(struct pt_regs *regs, unsigned long thread_info_flags)
> {
> + if (thread_info_flags & _TIF_UPROBE) {
> + clear_thread_flag(TIF_UPROBE);
> + uprobe_notify_resume(regs);
> + }
Presumably this ordering is crucial, ie. uprobes before signals.
> if (thread_info_flags & _TIF_SIGPENDING)
> do_signal(regs);
>
> Index: linux-tip-16aug/arch/powerpc/kernel/uprobes.c
> ===================================================================
> --- /dev/null
> +++ linux-tip-16aug/arch/powerpc/kernel/uprobes.c
> @@ -0,0 +1,180 @@
> +/*
> + * User-space Probes (UProbes) for powerpc
> + *
> + * 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, 2007-2012
> + *
> + * Adapted from the x86 port by Ananth N Mavinakayanahalli <ananth at in.ibm.com>
> + */
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/ptrace.h>
> +#include <linux/uprobes.h>
> +#include <linux/uaccess.h>
> +#include <linux/kdebug.h>
> +
> +#include <asm/sstep.h>
> +
> +#define UPROBE_TRAP_NR UINT_MAX
In the comments below you talk about -1 a few times, but you actually
mean UINT_MAX.
> +/**
> + * arch_uprobe_analyze_insn
Analyze what about the instruction?
> + * @mm: the probed address space.
> + * @arch_uprobe: the probepoint information.
> + * @addr: vaddr to probe.
> + * Return 0 on success or a -ve number on error.
> + */
> +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long addr)
> +{
> + unsigned int insn;
> +
> + if (addr & 0x03)
> + return -EINVAL;
> +
> + memcpy(&insn, auprobe->insn, MAX_UINSN_BYTES);
We shouldn't need to use memcpy, we know it's a u32.
> + if (is_trap(insn))
> + return -ENOTSUPP;
A comment saying why we can't handle this would be nice.
> + return 0;
> +}
I am probably missing something, but why do we need to execute out of
line?
> +/*
> + * arch_uprobe_pre_xol - prepare to execute out of line.
> + * @auprobe: the probepoint information.
> + * @regs: reflects the saved user state of current task.
> + */
> +int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> + struct arch_uprobe_task *autask = ¤t->utask->autask;
> +
> + autask->saved_trap_nr = current->thread.trap_nr;
> + current->thread.trap_nr = UPROBE_TRAP_NR;
> + regs->nip = current->utask->xol_vaddr;
> + return 0;
> +}
> +
> +/**
> + * uprobe_get_swbp_addr - compute address of swbp given post-swbp regs
> + * @regs: Reflects the saved state of the task after it has hit a breakpoint
> + * instruction.
> + * Return the address of the breakpoint instruction.
> + */
> +unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
> +{
> + return instruction_pointer(regs);
> +}
This seems like it would be better in asm/uprobes.h as a static inline,
but that's not your fault.
> +/*
> + * If xol insn itself traps and generates a signal (SIGILL/SIGSEGV/etc),
> + * then detect the case where a singlestepped instruction jumps back to its
> + * own address. It is assumed that anything like do_page_fault/do_trap/etc
> + * sets thread.trap_nr != -1.
> + *
> + * arch_uprobe_pre_xol/arch_uprobe_post_xol save/restore thread.trap_nr,
> + * arch_uprobe_xol_was_trapped() simply checks that ->trap_nr is not equal to
> + * UPROBE_TRAP_NR == -1 set by arch_uprobe_pre_xol().
> + */
> +bool arch_uprobe_xol_was_trapped(struct task_struct *t)
> +{
> + if (t->thread.trap_nr != UPROBE_TRAP_NR)
> + return true;
> +
> + return false;
> +}
> +
> +/*
> + * Called after single-stepping. 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 resume execution after the single-step.
> + */
> +int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> + struct uprobe_task *utask = current->utask;
> +
> + WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR);
> +
> + current->thread.trap_nr = utask->autask.saved_trap_nr;
> +
> + /*
> + * On powerpc, except for loads and stores, most instructions
> + * including ones that alter code flow (branches, calls, returns)
> + * are emulated in the kernel. We get here only if the emulation
> + * support doesn't exist and have to fix-up the next instruction
> + * to be executed.
> + */
> + regs->nip = utask->vaddr + MAX_UINSN_BYTES;
> + return 0;
> +}
> +
> +/* callback routine for handling exceptions. */
> +int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data)
> +{
> + struct die_args *args = data;
> + struct pt_regs *regs = args->regs;
> +
> + /* We are only interested in userspace traps */
> + if (regs && !user_mode(regs))
> + return NOTIFY_DONE;
Do we ever get here with a NULL regs?
> + switch (val) {
> + case DIE_BPT:
> + if (uprobe_pre_sstep_notifier(regs))
> + return NOTIFY_STOP;
> + break;
> + case DIE_SSTEP:
> + if (uprobe_post_sstep_notifier(regs))
> + return NOTIFY_STOP;
> + default:
> + break;
> + }
> + return NOTIFY_DONE;
> +}
> +
> +/*
> + * This function gets called when XOL instruction either gets trapped or
> + * the thread has a fatal signal, so reset the instruction pointer to its
> + * probed address.
> + */
> +void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> + struct uprobe_task *utask = current->utask;
> +
> + current->thread.trap_nr = utask->autask.saved_trap_nr;
> + instruction_pointer_set(regs, utask->vaddr);
> +}
> +
> +/*
> + * See if the instruction can be emulated.
> + * Returns true if instruction was emulated, false otherwise.
> + */
> +bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
> +{
> + int ret;
> + unsigned int insn;
> +
> + memcpy(&insn, auprobe->insn, MAX_UINSN_BYTES);
Why memcpy?
> +
> + /*
> + * emulate_step() returns 1 if the insn was successfully emulated.
> + * For all other cases, we need to single-step in hardware.
> + */
> + ret = emulate_step(regs, insn);
> + if (ret > 0)
> + return true;
This actually emulates the instruction, ie. the contents of regs are
changed based on the instruction.
That seems to differ vs x86, where arch_uprobe_skip_sstep() just checks
the instruction and returns true/false. Is that because on x86 they are
only returning true for nops? ie. there is no emulation to be done?
It's a little surprising that can_skip_sstep() actually emulates the
instruction, but again that's not your fault.
cheers
More information about the Linuxppc-dev
mailing list