diff options
author | Jim Keniston <jkenisto@us.ibm.com> | 2008-11-03 14:47:12 -0800 |
---|---|---|
committer | Jim Keniston <jkenisto@us.ibm.com> | 2008-11-03 14:47:12 -0800 |
commit | 904b272c43bf358300719d01836bcd10c04aa356 (patch) | |
tree | c6fdb97c5643780ad37001a421080ff30ec76a5b /runtime/uprobes2/uprobes.h | |
parent | b8aa3fa820c059f0f8abb4025f464d90d1f9cb9e (diff) | |
download | systemtap-steved-904b272c43bf358300719d01836bcd10c04aa356.tar.gz systemtap-steved-904b272c43bf358300719d01836bcd10c04aa356.tar.xz systemtap-steved-904b272c43bf358300719d01836bcd10c04aa356.zip |
Add a version of uprobes that works with the 2.6.27 version of utrace.
Diffstat (limited to 'runtime/uprobes2/uprobes.h')
-rw-r--r-- | runtime/uprobes2/uprobes.h | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/runtime/uprobes2/uprobes.h b/runtime/uprobes2/uprobes.h new file mode 100644 index 00000000..11d01f5c --- /dev/null +++ b/runtime/uprobes2/uprobes.h @@ -0,0 +1,406 @@ +#ifndef _LINUX_UPROBES_H +#define _LINUX_UPROBES_H +/* + * Userspace Probes (UProbes) + * include/linux/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/list.h> + +struct pt_regs; + +enum uprobe_type { + UPTY_UPROBE, + UPTY_URETPROBE +}; + +/* This is what the user supplies us. */ +struct uprobe { + /* + * The pid of the probed process. Currently, this can be the + * thread ID (task->pid) of any active thread in the process. + */ + pid_t pid; + + /* Location of the probepoint */ + unsigned long vaddr; + + /* Handler to run when the probepoint is hit */ + void (*handler)(struct uprobe*, struct pt_regs*); + + /* + * This function, if non-NULL, will be called upon completion of + * an ASYNCHRONOUS registration (i.e., one initiated by a uprobe + * handler). reg = 1 for register, 0 for unregister. type + * specifies the type of [un]register call (uprobe or uretprobe). + */ + void (*registration_callback)(struct uprobe *u, int reg, + enum uprobe_type type, int result); + + /* Reserved for use by uprobes */ + void *kdata; +}; + +struct uretprobe_instance; + +struct uretprobe { + struct uprobe u; + void (*handler)(struct uretprobe_instance*, struct pt_regs*); +}; + +struct uretprobe_instance { + struct uretprobe *rp; + unsigned long ret_addr; + struct hlist_node hlist; + unsigned long sp; // stack pointer value expected on return + unsigned long reserved1; +}; + +extern int register_uprobe(struct uprobe *u); +extern void unregister_uprobe(struct uprobe *u); +/* For runtime, assume uprobes support includes uretprobes. */ +extern int register_uretprobe(struct uretprobe *rp); +extern void unregister_uretprobe(struct uretprobe *rp); + +#ifdef UPROBES_IMPLEMENTATION + +#include <linux/mutex.h> +#include <linux/rwsem.h> +#include <linux/wait.h> +#include <asm/atomic.h> +#include "uprobes_arch.h" + +struct task_struct; +struct utrace_attached_engine; +struct pid; + +enum uprobe_probept_state { + UPROBE_INSERTING, // process quiescing prior to insertion + UPROBE_BP_SET, // breakpoint in place + UPROBE_REMOVING, // process quiescing prior to removal + UPROBE_DISABLED // removal completed +}; + +enum uprobe_task_state { + UPTASK_QUIESCENT, + UPTASK_SLEEPING, // used when task may not be able to quiesce + UPTASK_RUNNING, + UPTASK_BP_HIT, + UPTASK_TRAMPOLINE_HIT, + UPTASK_PRE_SSTEP, + UPTASK_SSTEP, + UPTASK_POST_SSTEP +}; + +#define UPROBE_HASH_BITS 5 +#define UPROBE_TABLE_SIZE (1 << UPROBE_HASH_BITS) + +/* Used when deciding which instruction slot to steal. */ +enum uprobe_slot_state { + SSOL_FREE, + SSOL_ASSIGNED, + SSOL_BEING_STOLEN, + SSOL_RESERVED // e.g., for uretprobe trampoline +}; + +/* + * For a uprobe_process that uses an SSOL area, there's an array of these + * objects matching the array of instruction slots in the SSOL area. + */ +struct uprobe_ssol_slot { + /* The slot in the SSOL area that holds the instruction-copy */ + __user uprobe_opcode_t *insn; + + enum uprobe_slot_state state; + + /* The probepoint that currently owns this slot */ + struct uprobe_probept *owner; + + /* + * Read-locked when slot is in use during single-stepping. + * Write-locked by stealing task. + */ + struct rw_semaphore rwsem; + + /* Used for LRU heuristics. If this overflows, it's OK. */ + unsigned long last_used; +}; + +/* + * The per-process single-stepping out-of-line (SSOL) area + */ +struct uprobe_ssol_area { + /* Array of instruction slots in the vma we allocate */ + __user uprobe_opcode_t *insn_area; + + int nslots; + int nfree; + + /* Array of slot objects, one per instruction slot */ + struct uprobe_ssol_slot *slots; + + /* lock held while finding a free slot */ + spinlock_t lock; + + /* + * We currently use access_process_vm() to populate instruction + * slots. Calls must be serialized because access_process_vm() + * isn't thread-safe. + */ + struct mutex populate_mutex; + + /* Next slot to steal */ + int next_slot; + + /* First slot not reserved for trampoline or some such */ + int first_ssol_slot; + + /* Ensures 2 threads don't try to set up the vma simultaneously. */ + struct mutex setup_mutex; + + /* 1 = we've at least tried. IS_ERR(insn_area) if we failed. */ + int initialized; +}; + +/* + * uprobe_process -- not a user-visible struct. + * A uprobe_process represents a probed process. A process can have + * multiple probepoints (each represented by a uprobe_probept) and + * one or more threads (each represented by a uprobe_task). + */ +struct uprobe_process { + /* + * rwsem is write-locked for any change to the uprobe_process's + * graph (including uprobe_tasks, uprobe_probepts, and uprobe_kimgs) -- + * e.g., due to probe [un]registration or special events like exit. + * It's read-locked during the whole time we process a probepoint hit. + */ + struct rw_semaphore rwsem; + + /* Table of uprobe_probepts registered for this process */ + /* TODO: Switch to list_head[] per Ingo. */ + struct hlist_head uprobe_table[UPROBE_TABLE_SIZE]; + int nppt; /* number of probepoints */ + + /* List of uprobe_probepts awaiting insertion or removal */ + struct list_head pending_uprobes; + + /* List of uprobe_tasks in this task group */ + struct list_head thread_list; + int nthreads; + int n_quiescent_threads; + + /* this goes on the uproc_table */ + struct hlist_node hlist; + + /* + * All threads (tasks) in a process share the same uprobe_process. + */ + struct pid *tg_leader; + pid_t tgid; + + /* Threads in UTASK_SLEEPING state wait here to be roused. */ + wait_queue_head_t waitq; + + /* + * We won't free the uprobe_process while... + * - any register/unregister operations on it are in progress; or + * - any uprobe_report_* callbacks are running; or + * - uprobe_table[] is not empty; or + * - any tasks are UTASK_SLEEPING in the waitq; or + * - any uretprobe_instances are outstanding. + * refcount reflects this. We do NOT ref-count tasks (threads), + * since once the last thread has exited, the rest is academic. + */ + atomic_t refcount; + + /* Return-probed functions return via this trampoline. */ + __user uprobe_opcode_t *uretprobe_trampoline_addr; + + /* + * finished = 1 means the process is execing or the last thread + * is exiting, and we're cleaning up the uproc. If the execed + * process is probed, a new uproc will be created. + */ + int finished; + + /* + * Manages slots for instruction-copies to be single-stepped + * out of line. + */ + struct uprobe_ssol_area ssol_area; + + /* + * 1 to single-step out of line; 0 for inline. This can drop to + * 0 if we can't set up the SSOL area, but never goes from 0 to 1. + */ + int sstep_out_of_line; +}; + +/* + * uprobe_kimg -- not a user-visible struct. + * Holds implementation-only per-uprobe data. + * uprobe->kdata points to this. + */ +struct uprobe_kimg { + struct uprobe *uprobe; + struct uprobe_probept *ppt; + + /* + * -EBUSY while we're waiting for all threads to quiesce so the + * associated breakpoint can be inserted or removed. + * 0 if the the insert/remove operation has succeeded, or -errno + * otherwise. + */ + int status; + + /* on ppt's list */ + struct list_head list; +}; + +/* + * uprobe_probept -- not a user-visible struct. + * A probepoint, at which several uprobes can be registered. + * Guarded by uproc->rwsem. + */ +struct uprobe_probept { + /* vaddr copied from (first) uprobe */ + unsigned long vaddr; + + /* The uprobe_kimg(s) associated with this uprobe_probept */ + struct list_head uprobe_list; + + enum uprobe_probept_state state; + + /* Saved opcode (which has been replaced with breakpoint) */ + uprobe_opcode_t opcode; + + /* + * Saved original instruction. This may be modified by + * architecture-specific code if the original instruction + * can't be single-stepped out of line as-is. + */ + uprobe_opcode_t insn[MAX_UINSN_BYTES / sizeof(uprobe_opcode_t)]; + + struct uprobe_probept_arch_info arch_info; + + /* The parent uprobe_process */ + struct uprobe_process *uproc; + + /* + * ppt goes in the uprobe_process->uprobe_table when registered -- + * even before the breakpoint has been inserted. + */ + struct hlist_node ut_node; + + /* + * ppt sits in the uprobe_process->pending_uprobes queue while + * awaiting insertion or removal of the breakpoint. + */ + struct list_head pd_node; + + /* [un]register_uprobe() waits 'til bkpt inserted/removed. */ + wait_queue_head_t waitq; + + /* + * Serialize single-stepping inline, so threads don't clobber + * each other swapping the breakpoint instruction in and out. + * This helps prevent crashing the probed app, but it does NOT + * prevent probe misses while the breakpoint is swapped out. + */ + struct mutex ssil_mutex; + + /* + * We put the instruction-copy here to single-step it. + * We don't own it unless slot->owner points back to us. + */ + struct uprobe_ssol_slot *slot; + + /* + * Hold this while stealing an insn slot to ensure that no + * other thread, having also hit this probepoint, simultaneously + * steals a slot for it. + */ + struct mutex slot_mutex; +}; + +/* + * uprobe_utask -- not a user-visible struct. + * Corresponds to a thread in a probed process. + * Guarded by uproc->rwsem. + */ +struct uprobe_task { + /* Lives in the global utask_table */ + struct hlist_node hlist; + + /* Lives on the thread_list for the uprobe_process */ + struct list_head list; + + struct task_struct *tsk; + struct pid *pid; + + /* The utrace engine for this task */ + struct utrace_attached_engine *engine; + + /* Back pointer to the associated uprobe_process */ + struct uprobe_process *uproc; + + enum uprobe_task_state state; + + /* + * quiescing = 1 means this task has been asked to quiesce. + * It may not be able to comply immediately if it's hit a bkpt. + */ + int quiescing; + + /* Set before running handlers; cleared after single-stepping. */ + struct uprobe_probept *active_probe; + + /* Saved address of copied original instruction */ + long singlestep_addr; + + struct uprobe_task_arch_info arch_info; + + /* + * Unexpected error in probepoint handling has left task's + * text or stack corrupted. Kill task ASAP. + */ + int doomed; + + /* LIFO -- active instances */ + struct hlist_head uretprobe_instances; + + /* [un]registrations initiated by handlers must be asynchronous. */ + struct list_head deferred_registrations; + + /* Delay handler-destined signals 'til after single-step done. */ + struct list_head delayed_signals; +}; + +#ifdef CONFIG_UPROBES_SSOL +static struct uprobe_ssol_slot *uprobe_get_insn_slot(struct uprobe_probept*); +static void uprobe_pre_ssout(struct uprobe_task*, struct uprobe_probept*, + struct pt_regs*); +static void uprobe_post_ssout(struct uprobe_task*, struct uprobe_probept*, + struct pt_regs*); +#endif + +#endif /* UPROBES_IMPLEMENTATION */ + +#endif /* _LINUX_UPROBES_H */ |