This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[PATCH 1/2][RFC] user space instruction tracing tapset
- From: Dave Nomura <dcnltc at us dot ibm dot com>
- To: systemtap at sourceware dot org
- Cc: Maynard Johnson <mpjohn at us dot ibm dot com>
- Date: Wed, 17 Oct 2007 13:56:53 -0700
- Subject: [PATCH 1/2][RFC] user space instruction tracing tapset
- Organization: Linux Power Toolchain
- Reply-to: dcnltc at us dot ibm dot com
This patch is the tapset usr_itrace.stp that I propose putting into
runtimes/tapset to support user space instruction tracing.
diff -paurN ../null/tapset/usr_itrace.stp ./tapset/usr_itrace.stp
--- ../null/tapset/usr_itrace.stp 1969-12-31 16:00:00.000000000 -0800
+++ ./tapset/usr_itrace.stp 2007-10-17 13:12:20.000000000 -0700
@@ -0,0 +1,271 @@
+%{
+/*
+ * user space instruction tracing tapset
+ * Copyright (C) 2005, 2006, 2007 IBM Corp.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/rcupdate.h>
+#include <linux/utrace.h>
+#include <linux/uprobes.h>
+#include <asm/string.h>
+#include <asm/tracehook.h>
+
+#ifndef put_task_struct
+#define put_task_struct(t) \
+ BUG_ON(atomic_dec_and_test(&tsk->usage))
+#endif
+
+struct itrace_info {
+ pid_t tid;
+ int itrace_on;
+ struct task_struct *tsk;
+ struct utrace_attached_engine *engine;
+ struct list_head link;
+};
+
+
+static LIST_HEAD(usr_itrace_info);
+static DEFINE_MUTEX(itrace_mutex);
+
+
+typedef void (*USR_ITRACE_HANDLER) (struct task_struct *, struct pt_regs *);
+static USR_ITRACE_HANDLER usr_itrace_handler = NULL;
+static u32 step_flag = 0;
+
+static struct task_struct *get_task_by_pid(pid_t pid)
+{
+ struct task_struct *tsk;
+
+ rcu_read_lock();
+ tsk = find_task_by_pid(pid);
+ if (tsk)
+ get_task_struct(tsk);
+ return tsk;
+}
+
+static struct itrace_info *create_itrace_info(pid_t tid)
+{
+ struct task_struct *tsk;
+ struct itrace_info *ui;
+
+ tsk = get_task_by_pid(tid);
+ if (!tsk) {
+ printk(KERN_ERR "Cannot find process %d\n", tid);
+ return NULL;
+ }
+
+ /* initialize ui */
+ ui = kzalloc(sizeof(struct itrace_info), GFP_USER);
+ ui->tsk = tsk;
+ ui->tid = tid;
+ INIT_LIST_HEAD(&ui->link);
+
+ /* push ui onto usr_itrace_info */
+ mutex_lock(&itrace_mutex);
+ rcu_read_lock();
+ list_add_rcu(&ui->link, &usr_itrace_info);
+ mutex_unlock(&itrace_mutex);
+ rcu_read_unlock();
+
+ put_task_struct(tsk);
+
+ return ui;
+}
+
+static struct itrace_info *find_itrace_info(pid_t tid)
+{
+ struct itrace_info *ui = NULL;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(ui, &usr_itrace_info, link) {
+ if (ui->tid == tid)
+ goto done;
+ }
+ ui = NULL;
+done:
+ rcu_read_unlock();
+ return ui;
+}
+
+static struct itrace_info *get_itrace_info(pid_t tid)
+{
+ struct itrace_info *ui = find_itrace_info(tid);
+
+ if (!ui) {
+ /* none found, so create one and push onto usr_itrace_info */
+ ui = create_itrace_info(tid);
+ }
+ return ui;
+}
+
+void static cleanup_usr_itrace(void)
+{
+ struct itrace_info *tmp;
+ struct itrace_info *ui;
+
+ list_for_each_entry_safe(ui, tmp, &usr_itrace_info, link) {
+ mutex_lock(&itrace_mutex);
+ if (ui->tsk && ui->engine) {
+ (void) utrace_detach(ui->tsk, ui->engine);
+ }
+ list_del_rcu(&ui->link);
+ mutex_unlock(&itrace_mutex);
+ synchronize_rcu();
+ kfree(ui);
+ }
+}
+
+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)
+{
+ struct itrace_info *ui = engine->data;
+
+ if (info->si_signo != SIGTRAP || !ui)
+ return UTRACE_ACTION_RESUME;
+
+ if (!ui->itrace_on) {
+ printk(KERN_INFO "usr_itrace_off: stop tracing tid %d\n", ui->tid);
+ return UTRACE_ACTION_NEWSTATE | UTRACE_SIGNAL_IGN;
+ }
+
+
+ usr_itrace_handler(tsk, regs);
+
+ /* continue stepping, don't let any other engines see this trap */
+ return step_flag | UTRACE_ACTION_HIDE | UTRACE_SIGNAL_IGN |
+ UTRACE_ACTION_NEWSTATE;
+}
+
+static u32 usr_itrace_report_death(struct utrace_attached_engine *e,
+ struct task_struct *tsk)
+{
+ cleanup_usr_itrace();
+ return (UTRACE_ACTION_NEWSTATE | UTRACE_ACTION_DETACH);
+}
+
+static const struct utrace_engine_ops utrace_ops =
+{
+ .report_signal = usr_itrace_report_signal,
+ .report_death = usr_itrace_report_death
+};
+
+static int attach_utrace_engine(struct itrace_info *ui)
+{
+ struct utrace_attached_engine *engine;
+
+ if (!ui || !ui->tsk) {
+ return 0;
+ }
+
+ /* if engine doesn't already exist create one */
+ if (ui->engine)
+ return 1;
+
+ ui->engine = utrace_attach(ui->tsk, UTRACE_ATTACH_CREATE, &utrace_ops, ui);
+
+ if (IS_ERR(ui->engine)) {
+ printk(KERN_ERR "utrace_attach returns %ld\n",
+ PTR_ERR(ui->engine));
+ return 0;
+ }
+ printk(KERN_INFO "attach_utrace_engine: created engine. flags=%lx\n",
+ ui->engine->flags);
+ return 1;
+}
+
+%}
+
+
+function usr_itrace_on:long (tid:long)
+%{
+ int ret;
+ pid_t tid = (pid_t)THIS->tid;
+ struct itrace_info *ui;
+
+ THIS->__retvalue = 0;
+ if (!usr_itrace_handler) {
+ printk(KERN_ERR "usr_itrace_on:usr_itrace_init() not called\n");
+ return;
+ }
+
+ if (!(ui = get_itrace_info(tid))) {
+ return;
+ }
+
+ /* attach a single stepping engine */
+ if (!attach_utrace_engine(ui))
+ return;
+
+ /* success */
+ THIS->__retvalue = 1;
+ printk(KERN_INFO "usr_itrace_on: start tracing tid %d\n", ui->tid);
+
+ /* start single-stepping engine */
+ utrace_set_flags(ui->tsk, ui->engine, ui->engine->flags | step_flag |
+ UTRACE_EVENT_SIGNAL_ALL | UTRACE_EVENT(DEATH));
+ ui->itrace_on = 1;
+%}
+
+function usr_itrace_off (tid:long)
+%{
+ int ret;
+ pid_t tid = (pid_t)THIS->tid;
+ struct itrace_info *ui = find_itrace_info(tid);
+
+ if (!ui) {
+ printk(KERN_ERR "usr_itrace_off: cannot find engine for tid %d\n",
+ tid);
+ return;
+ }
+
+ /* turn off tracing on next single step trap */
+ ui->itrace_on = 0;
+
+%}
+
+function usr_itrace_init:long (step_mode:string, handler:long)
+%{
+ step_flag = 0;
+ if (strcmp(THIS->step_mode, "single_step") == 0)
+#if defined(ARCH_HAS_SINGLE_STEP) && (ARCH_HAS_SINGLE_STEP != 0)
+ step_flag = UTRACE_ACTION_SINGLESTEP;
+#else
+ _stp_printf("SINGLESTEP not allowed in <asm/tracehook.h> for this architecture\n");
+#endif
+ else if (strcmp(THIS->step_mode, "block_step") == 0)
+#if defined( ARCH_HAS_BLOCK_STEP) && (ARCH_HAS_BLOCK_STEP != 0)
+ step_flag = UTRACE_ACTION_BLOCKSTEP;
+#else
+ _stp_printf("BLOCKSTEP not allowed in <asm/tracehook.h> for this architecture\n");
+#endif
+ else
+ _stp_printf("unknown stepping mode: %s\n", THIS->step_mode);
+
+ if (step_flag == 0) {
+ THIS->__retvalue = 0;
+ return;
+ }
+
+ usr_itrace_handler = (USR_ITRACE_HANDLER)(long)THIS->handler;
+ THIS->__retvalue = 1;
+%}
+
+function cleanup_usr_itrace()
+%{
+ cleanup_usr_itrace();
+%}