runtime/linux/itrace.c - systemtap
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/rcupdate.h>
#include <linux/utrace.h>
#include "ptrace_compatibility.h"
#if defined(__ia64__)
#error "Unsupported itrace architecture"
#endif
#ifdef UTRACE_API_VERSION
#define utrace_attached_engine utrace_engine
#endif
#include <asm/string.h>
#include "uprobes/uprobes.h"
#include "utrace_compatibility.h"
#ifndef put_task_struct
#define put_task_struct(t) \
BUG_ON(atomic_dec_and_test(&tsk->usage))
#endif
#ifdef CONFIG_PPC
struct bpt_info {
unsigned long addr;
unsigned int instr;
};
struct atomic_ss_info {
int step_over_atomic;
struct bpt_info end_bpt;
struct bpt_info br_bpt;
};
static int handle_ppc_atomic_seq(struct task_struct *tsk, struct pt_regs *regs,
struct atomic_ss_info *ss_info);
static void remove_atomic_ss_breakpoint (struct task_struct *tsk,
struct bpt_info *bpt);
#endif
struct itrace_info {
u32 step_flag;
struct stap_itrace_probe *itrace_probe;
#ifdef CONFIG_PPC
struct atomic_ss_info ppc_atomic_ss;
#endif
struct task_struct *tsk;
struct utrace_attached_engine *engine;
struct list_head link;
};
static u32 debug = 0 ;
static LIST_HEAD(usr_itrace_info);
static spinlock_t itrace_lock;
static struct itrace_info *create_itrace_info(
struct task_struct *tsk, u32 step_flag,
struct stap_itrace_probe *itrace_probe);
#ifdef UTRACE_ORIG_VERSION
static u32 usr_itrace_report_quiesce(struct utrace_attached_engine *engine,
struct task_struct *tsk)
#else
#if defined(UTRACE_API_VERSION) && (UTRACE_API_VERSION >= 20091216)
static u32 usr_itrace_report_quiesce(u32 action,
struct utrace_attached_engine *engine,
unsigned long event)
#else
static u32 usr_itrace_report_quiesce(enum utrace_resume_action action,
struct utrace_attached_engine *engine,
struct task_struct *tsk,
unsigned long event)
#endif
#endif
{
int status;
struct itrace_info *ui;
rcu_read_lock();
ui = rcu_dereference(engine->data);
rcu_read_unlock();
WARN_ON(!ui);
#ifdef UTRACE_ORIG_VERSION
return (ui->step_flag | UTRACE_ACTION_NEWSTATE);
#else
return (event == 0 ? ui->step_flag : UTRACE_RESUME);
#endif
}
#ifdef UTRACE_ORIG_VERSION
static u32 usr_itrace_report_signal(
struct utrace_attached_engine *engine,
struct task_struct *tsk,
struct pt_regs *regs,
u32 action, siginfo_t *info,
const struct k_sigaction *orig_ka,
struct k_sigaction *return_ka)
#else
#if defined(UTRACE_API_VERSION) && (UTRACE_API_VERSION >= 20091216)
static u32 usr_itrace_report_signal(u32 action,
struct utrace_attached_engine *engine,
struct pt_regs *regs,
siginfo_t *info,
const struct k_sigaction *orig_ka,
struct k_sigaction *return_ka)
#else
static u32 usr_itrace_report_signal(u32 action,
struct utrace_attached_engine *engine,
struct task_struct *tsk,
struct pt_regs *regs,
siginfo_t *info,
const struct k_sigaction *orig_ka,
struct k_sigaction *return_ka)
#endif
#endif
{
#if defined(UTRACE_API_VERSION) && (UTRACE_API_VERSION >= 20091216)
struct task_struct *tsk = current;
#endif
struct itrace_info *ui;
u32 return_flags;
unsigned long data = 0;
#ifdef CONFIG_PPC
data = mfspr(SPRN_SDAR);
#endif
rcu_read_lock();
ui = rcu_dereference(engine->data);
rcu_read_unlock();
WARN_ON(!ui);
#if defined(UTRACE_ORIG_VERSION)
if (info->si_signo != SIGTRAP || !ui)
return UTRACE_RESUME;
#else
if (utrace_signal_action(action) == UTRACE_SIGNAL_HANDLER ||
utrace_signal_action(action) == UTRACE_SIGNAL_REPORT ||
info->si_signo != SIGTRAP || !ui)
return UTRACE_RESUME | utrace_signal_action(action);
#endif
#if defined(UTRACE_ORIG_VERSION) && defined(CONFIG_PPC)
return_flags = UTRACE_SIGNAL_IGN | UTRACE_STOP | UTRACE_ACTION_NEWSTATE;
#else
return_flags = ui->step_flag | UTRACE_SIGNAL_IGN;
#endif
#ifdef CONFIG_PPC
if (ui->ppc_atomic_ss.step_over_atomic) {
remove_atomic_ss_breakpoint(tsk, &ui->ppc_atomic_ss.end_bpt);
if (ui->ppc_atomic_ss.br_bpt.addr)
remove_atomic_ss_breakpoint(tsk,
&ui->ppc_atomic_ss.br_bpt);
ui->ppc_atomic_ss.step_over_atomic = 0;
}
if (handle_ppc_atomic_seq(tsk, regs, &ui->ppc_atomic_ss))
return_flags = UTRACE_RESUME | UTRACE_SIGNAL_IGN;
#endif
enter_itrace_probe(ui->itrace_probe, regs, (void *)&data);
return return_flags;
}
#ifdef UTRACE_ORIG_VERSION
static u32 usr_itrace_report_clone(
struct utrace_attached_engine *engine,
struct task_struct *parent,
unsigned long clone_flags,
struct task_struct *child)
#else
#if defined(UTRACE_API_VERSION) && (UTRACE_API_VERSION >= 20091216)
static u32 usr_itrace_report_clone(u32 action,
struct utrace_attached_engine *engine,
unsigned long clone_flags,
struct task_struct *child)
#else
static u32 usr_itrace_report_clone(enum utrace_resume_action action,
struct utrace_attached_engine *engine,
struct task_struct *parent, unsigned long clone_flags,
struct task_struct *child)
#endif
#endif
{
return UTRACE_RESUME;
}
#ifdef UTRACE_ORIG_VERSION
static u32 usr_itrace_report_death(struct utrace_attached_engine *e,
struct task_struct *tsk)
#else
#if defined(UTRACE_API_VERSION) && (UTRACE_API_VERSION >= 20091216)
static u32 usr_itrace_report_death(struct utrace_attached_engine *e,
bool group_dead, int signal)
#else
static u32 usr_itrace_report_death(struct utrace_attached_engine *e,
struct task_struct *tsk, bool group_dead, int signal)
#endif
#endif
{
struct itrace_info *ui;
rcu_read_lock();
ui = rcu_dereference(e->data);
rcu_read_unlock();
WARN_ON(!ui);
return (UTRACE_DETACH);
}
static const struct utrace_engine_ops utrace_ops =
{
.report_quiesce = usr_itrace_report_quiesce,
.report_signal = usr_itrace_report_signal,
.report_clone = usr_itrace_report_clone,
.report_death = usr_itrace_report_death
};
static struct itrace_info *create_itrace_info(
struct task_struct *tsk, u32 step_flag,
struct stap_itrace_probe *itrace_probe)
{
struct itrace_info *ui;
int status;
if (debug)
printk(KERN_INFO "create_itrace_info: tid=%d\n", tsk->pid);
ui = _stp_kzalloc(sizeof(struct itrace_info));
if (ui == NULL) {
printk(KERN_ERR "%s:%d: Unable to allocate memory\n",
__FUNCTION__, __LINE__);
return NULL;
}
ui->tsk = tsk;
ui->step_flag = step_flag;
ui->itrace_probe = itrace_probe;
#ifdef CONFIG_PPC
ui->ppc_atomic_ss.step_over_atomic = 0;
#endif
INIT_LIST_HEAD(&ui->link);
spin_lock(&itrace_lock);
list_add(&ui->link, &usr_itrace_info);
spin_unlock(&itrace_lock);
ui->engine = utrace_attach_task(ui->tsk,
(UTRACE_ATTACH_CREATE
| UTRACE_ATTACH_ATOMIC),
&utrace_ops, ui);
if (IS_ERR(ui->engine)) {
printk(KERN_ERR "utrace_attach returns %ld\n",
PTR_ERR(ui->engine));
return NULL;
}
status = utrace_set_events(tsk, ui->engine, ui->engine->flags |
UTRACE_EVENT(QUIESCE) |
UTRACE_EVENT(CLONE) | UTRACE_EVENT_SIGNAL_ALL |
UTRACE_EVENT(DEATH));
if (status < 0) {
printk(KERN_ERR "utrace_attach returns %d\n", status);
utrace_engine_put(ui->engine);
return NULL;
}
status = utrace_control(tsk, ui->engine, UTRACE_STOP);
if (status == 0) {
status = utrace_control(tsk, ui->engine, step_flag);
if (status < 0) {
printk(KERN_ERR "utrace_control(%d) returns %d\n",
step_flag, status);
utrace_engine_put(ui->engine);
return NULL;
}
}
utrace_engine_put(ui->engine);
return ui;
}
static struct itrace_info *find_itrace_info(struct task_struct *tsk)
{
struct itrace_info *ui = NULL;
spin_lock(&itrace_lock);
list_for_each_entry(ui, &usr_itrace_info, link) {
if (ui->tsk == tsk)
goto done;
}
ui = NULL;
done:
spin_unlock(&itrace_lock);
return ui;
}
static int usr_itrace_init(int single_step, struct task_struct *tsk, struct stap_itrace_probe *p)
{
struct itrace_info *ui;
spin_lock_init(&itrace_lock);
rcu_read_lock();
if (tsk == NULL) {
printk(KERN_ERR "usr_itrace_init: Invalid task\n");
rcu_read_unlock();
return 1;
}
get_task_struct(tsk);
ui = create_itrace_info(tsk,
(single_step ?
UTRACE_SINGLESTEP : UTRACE_BLOCKSTEP), p);
if (!ui)
return 1;
put_task_struct(tsk);
rcu_read_unlock();
if (debug)
printk(KERN_INFO "usr_itrace_init: completed for tid = %d\n",
tsk->pid);
return 0;
}
void static remove_usr_itrace_info(struct itrace_info *ui)
{
struct itrace_info *tmp;
int status;
if (!ui)
return;
if (debug)
printk(KERN_INFO "remove_usr_itrace_info: tid=%d\n",
(ui->tsk ? ui->tsk->pid : -1));
if (ui->tsk && ui->engine) {
status = utrace_control(ui->tsk, ui->engine, UTRACE_DETACH);
if (status == -EINPROGRESS) {
do {
status = utrace_barrier(ui->tsk, ui->engine);
} while (status == -ERESTARTSYS);
}
if (status < 0 && status != -ESRCH && status != -EALREADY)
printk(KERN_ERR
"utrace_control(UTRACE_DETACH) returns %d\n",
status);
}
spin_lock(&itrace_lock);
list_del(&ui->link);
spin_unlock(&itrace_lock);
_stp_kfree(ui);
}
void static cleanup_usr_itrace(void)
{
struct itrace_info *tmp;
struct itrace_info *ui;
if (debug)
printk(KERN_INFO "cleanup_usr_itrace called\n");
list_for_each_entry_safe(ui, tmp, &usr_itrace_info, link) {
remove_usr_itrace_info(ui);
}
}
#ifdef CONFIG_PPC
#define PPC_INSTR_SIZE 4
#define TEXT_SEGMENT_BASE 1
#define LWARX_MASK 0xfc0007fe
#define LWARX_INSTR 0x7c000028
#define LDARX_INSTR 0x7c0000A8
#define STWCX_MASK 0xfc0007ff
#define STWCX_INSTR 0x7c00012d
#define STDCX_INSTR 0x7c0001ad
#define BC_MASK 0xfc000000
#define BC_INSTR 0x40000000
#define ATOMIC_SEQ_LENGTH 16
#define BPT_TRAP 0x7fe00008
#define INSTR_SZ sizeof(int)
static int get_instr(unsigned long addr, char *msg)
{
unsigned int instr;
if (copy_from_user(&instr, (const void __user *) addr,
sizeof(instr))) {
printk(KERN_ERR "get_instr failed: %s\n", msg);
WARN_ON(1);
}
return instr;
}
static void insert_atomic_ss_breakpoint (struct task_struct *tsk,
struct bpt_info *bpt)
{
unsigned int bp_instr = BPT_TRAP;
unsigned int cur_instr;
cur_instr = get_instr(bpt->addr, "insert_atomic_ss_breakpoint");
if (cur_instr != BPT_TRAP) {
bpt->instr = cur_instr;
WARN_ON(__access_process_vm(tsk, bpt->addr, &bp_instr, INSTR_SZ, 1) !=
INSTR_SZ);
}
}
static void remove_atomic_ss_breakpoint (struct task_struct *tsk,
struct bpt_info *bpt)
{
WARN_ON(__access_process_vm(tsk, bpt->addr, &bpt->instr, INSTR_SZ, 1) !=
INSTR_SZ);
}
static unsigned long
branch_dest (int opcode, int instr, struct pt_regs *regs, unsigned long pc)
{
unsigned long dest;
int immediate;
int absolute;
int ext_op;
absolute = (int) ((instr >> 1) & 1);
switch (opcode) {
case 18:
immediate = ((instr & ~3) << 6) >> 6; if (absolute)
dest = immediate;
else
dest = pc + immediate;
break;
case 16:
immediate = ((instr & ~3) << 16) >> 16; if (absolute)
dest = immediate;
else
dest = pc + immediate;
break;
case 19:
ext_op = (instr >> 1) & 0x3ff;
if (ext_op == 16) {
dest = regs->link & ~3;
WARN_ON(dest > 0);
} else if (ext_op == 528) {
dest = regs->ctr & ~3;
if (dest < TEXT_SEGMENT_BASE)
dest = regs->link & ~3;
} else
return -1;
break;
default:
return -1;
}
return dest;
}
static int handle_ppc_atomic_seq(struct task_struct *tsk, struct pt_regs *regs,
struct atomic_ss_info *ss_info)
{
unsigned long ip = regs->nip;
unsigned long start_addr;
unsigned int instr;
int got_stx = 0;
int i;
int ret;
unsigned long br_dest; int bc_instr_count = 0;
instr = get_instr(regs->nip, "handle_ppc_atomic_seq:1");
if ((instr & LWARX_MASK) != LWARX_INSTR
&& (instr & LWARX_MASK) != LDARX_INSTR)
return 0;
start_addr = regs->nip;
for (i = 0; i < ATOMIC_SEQ_LENGTH; ++i) {
ip += INSTR_SZ;
instr = get_instr(ip, "handle_ppc_atomic_seq:2");
if ((instr & BC_MASK) == BC_INSTR) {
if (bc_instr_count >= 1)
return 0;
br_dest = branch_dest (BC_INSTR >> 26, instr, regs, ip);
if (br_dest != -1 &&
br_dest >= TEXT_SEGMENT_BASE) {
ss_info->br_bpt.addr = br_dest;
bc_instr_count++;
}
}
if ((instr & STWCX_MASK) == STWCX_INSTR
|| (instr & STWCX_MASK) == STDCX_INSTR) {
got_stx = 1;
break;
}
}
if (!got_stx)
return 0;
ip += INSTR_SZ;
instr = get_instr(ip, "handle_ppc_atomic_seq:3");
if ((instr & BC_MASK) == BC_INSTR) {
ip += INSTR_SZ;
instr = get_instr(ip, "handle_ppc_atomic_seq:4");
}
ss_info->end_bpt.addr = ip;
if (bc_instr_count && (ss_info->br_bpt.addr >= start_addr &&
ss_info->br_bpt.addr <= ss_info->end_bpt.addr))
ss_info->br_bpt.addr = 0;
insert_atomic_ss_breakpoint (tsk, &ss_info->end_bpt);
if (ss_info->br_bpt.addr)
insert_atomic_ss_breakpoint (tsk, &ss_info->br_bpt);
ss_info->step_over_atomic = 1;
return 1;
}
#endif