summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Keniston <jkenisto@us.ibm.com>2008-05-05 17:03:49 -0700
committerJim Keniston <jkenisto@us.ibm.com>2008-05-05 17:03:49 -0700
commit9addf322a677eef2ee0efeca8bc41a9bda58de94 (patch)
tree6aed722c05e4de268544d211d0b1d321be92593b
parent5edbdffdc71747c402b781b2720228406eda7666 (diff)
downloadsystemtap-steved-9addf322a677eef2ee0efeca8bc41a9bda58de94.tar.gz
systemtap-steved-9addf322a677eef2ee0efeca8bc41a9bda58de94.tar.xz
systemtap-steved-9addf322a677eef2ee0efeca8bc41a9bda58de94.zip
Replaced [u_]arg() with [u]int_arg(), [u]long_arg(), {s|u}32_arg(),
{s|u}64_arg(), etc. Added asmlinkage(), fastcall(), regparm(). Dealt with some surprises -- e.g., rax is ZERO-extended eax. Seems to work well with -m32 and -m64 user apps, and with a (small) dwarfless subset of syscall.stp.
-rw-r--r--runtime/regs.c75
-rw-r--r--tapset/i686/registers.stp104
-rw-r--r--tapset/x86_64/registers.stp148
-rw-r--r--tapsets.cxx1
-rw-r--r--translate.cxx1
5 files changed, 268 insertions, 61 deletions
diff --git a/runtime/regs.c b/runtime/regs.c
index 743a230d..2daeaa3c 100644
--- a/runtime/regs.c
+++ b/runtime/regs.c
@@ -400,6 +400,7 @@ struct _stp_register_table {
unsigned nr_slots; // capacity
};
+static DEFINE_SPINLOCK(_stp_register_table_lock);
static void _stp_populate_register_table(void);
/*
@@ -445,12 +446,17 @@ static int _stp_find_register(const char *name,
struct _stp_register_table *table, size_t *size, size_t *offset)
{
int slot, found;
- if (table->nr_registers == 0)
+ if (unlikely(table->nr_registers == 0)) {
+ unsigned long flags;
/*
* Should we do this at the beginning of time to avoid
* the possibility of spending too long in a handler?
*/
- _stp_populate_register_table();
+ spin_lock_irqsave(&_stp_register_table_lock, flags);
+ if (table->nr_registers == 0)
+ _stp_populate_register_table();
+ spin_unlock_irqrestore(&_stp_register_table_lock, flags);
+ }
slot = _stp_lookup_register(name, table, &found);
if (found) {
if (size)
@@ -549,6 +555,7 @@ static struct _stp_register_table i386_register_table = {
#define ADD_EREG(nm) ADD_PT_REG("e" #nm, r##nm)
#define ADD_XREG(nm) ADD_PT_REG("x" #nm, nm)
#define ADD_FLAGS_REG() ADD2NAMES("flags", "eflags", eflags)
+/* Note: After a store to %eax, %rax holds the ZERO-extended %eax. */
#define EREG(nm, regs) ((regs)->r##nm)
#define RREG(nm, regs) ((regs)->r##nm)
#endif /* __x86_64__ */
@@ -592,6 +599,10 @@ static void _stp_populate_i386_register_table(void)
ADD_XREG(ss);
}
+/*
+ * For x86_64, this gets a copy of the saved 64-bit register (e.g., regs->rax).
+ * After a store to %eax, %rax holds the ZERO-extended %eax.
+ */
static long
_stp_get_reg32_by_name(const char *name, struct pt_regs *regs)
{
@@ -687,31 +698,50 @@ static void _stp_populate_register_table(void)
static int _stp_probing_32bit_app(struct pt_regs *regs)
{
+ if (!regs)
+ return 0;
return (user_mode(regs) && test_tsk_thread_flag(current, TIF_IA32));
}
+/* Ensure that the upper 32 bits of val are a sign-extension of the lower 32. */
+static long _stp_sign_extend32(long val)
+{
+ int32_t *val_ptr32 = (int32_t*) &val;
+ return *val_ptr32;
+}
+
+/*
+ * Get the value of the 64-bit register with the specified name. "rax",
+ * "ax", and "eax" all get you regs->[r]ax. Sets *reg32=1 if the name
+ * designates a 32-bit register (e.g., "eax"), 0 otherwise.
+ */
static unsigned long
-_stp_get_reg64_by_name(const char *name, struct pt_regs *regs)
+_stp_get_reg64_by_name(const char *name, struct pt_regs *regs, int *reg32)
{
size_t offset = 0;
unsigned long value;
BUG_ON(!name);
- if (!regs)
+ if (!regs) {
_stp_error("Register values not available in this context.\n");
+ return 0;
+ }
if (_stp_find_register(name, &x86_64_register_table, NULL, &offset)) {
+ if (reg32)
+ *reg32 = 0;
(void) memcpy(&value, ((char*)regs) + offset, sizeof(value));
return value;
}
- if (_stp_probing_32bit_app(regs))
- return _stp_get_reg32_by_name(name, regs);
- _stp_error("Unknown register name: %s\n", name);
- /* NOTREACHED */
- return 0;
+ if (reg32)
+ *reg32 = 1;
+ return _stp_get_reg32_by_name(name, regs);
}
#endif /* __x86_64__ */
/* Function arguments */
+#define _STP_REGPARM 0x8000
+#define _STP_REGPARM_MASK ((_STP_REGPARM) - 1)
+
#if defined(__i386__) || defined(__x86_64__)
static long _stp_get_sp(struct pt_regs *regs)
{
@@ -762,6 +792,21 @@ static int _stp_get_arg32_by_number(int n, int nr_regargs,
}
#endif /* __i386__ || __x86_64__ */
+#ifdef __i386__
+static int _stp_get_regparm(int regparm, struct pt_regs *regs)
+{
+ if (regparm == 0) {
+ /* Default */
+ if (user_mode(regs))
+ return 0;
+ else
+ // Kernel is built with -mregparm=3.
+ return 3;
+ } else
+ return (regparm & _STP_REGPARM_MASK);
+}
+#endif
+
#ifdef __x86_64__
/* See _stp_get_arg32_by_number(). */
static int _stp_get_arg64_by_number(int n, int nr_regargs,
@@ -790,6 +835,18 @@ static int _stp_get_arg64_by_number(int n, int nr_regargs,
return 0;
}
}
+
+static int _stp_get_regparm(int regparm, struct pt_regs *regs)
+{
+ if (regparm == 0) {
+ /* Default */
+ if (_stp_probing_32bit_app(regs))
+ return 0;
+ else
+ return 6;
+ } else
+ return (regparm & _STP_REGPARM_MASK);
+}
#endif /* __x86_64__ */
/** @} */
diff --git a/tapset/i686/registers.stp b/tapset/i686/registers.stp
index 2afbbb9b..85aa7a7f 100644
--- a/tapset/i686/registers.stp
+++ b/tapset/i686/registers.stp
@@ -1,10 +1,7 @@
/* Return the named register value as a signed value. */
function register:long (name:string) %{ /* pure */
- if (CONTEXT->regs)
- THIS->__retvalue = (int64_t)
+ THIS->__retvalue = (int64_t)
_stp_get_reg32_by_name(THIS->name, CONTEXT->regs);
- else
- THIS->__retvalue = 0;
%}
/*
@@ -16,10 +13,9 @@ function u_register:long (name:string) {
}
/* Return the value of function arg #argnum (1=first arg) as a signed value. */
-function arg:long (argnum:long) %{
+function _stp_arg:long (argnum:long) %{
long val;
- int n;
- int result;
+ int n, nr_regargs, result;
THIS->__retvalue = 0;
if (!CONTEXT->regs) {
@@ -31,24 +27,30 @@ function arg:long (argnum:long) %{
if (THIS->argnum < 1)
goto bad_argnum;
n = (int) THIS->argnum;
- // TODO: Get nr_regargs from .linkage clause.
- result = _stp_get_arg32_by_number(n, 0, CONTEXT->regs, &val);
+ nr_regargs = _stp_get_regparm(CONTEXT->regparm, CONTEXT->regs);
+ result = _stp_get_arg32_by_number(n, nr_regargs, CONTEXT->regs, &val);
switch (result) {
case 0:
+ /* Arg is in register. */
THIS->__retvalue = (int64_t) val;
break;
case 1:
+ /* Arg is on kernel stack. */
THIS->__retvalue = kread((long *) val);
break;
case 2:
- /*
- * Is copy_from_user satisfactory, since uprobe
- * handlers can block?
- */
- snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
- "arg() not yet implemented for user functions");
- CONTEXT->last_error = CONTEXT->error_buffer;
+ {
+ /* Arg is on user stack. */
+ const char __user *vaddr = (const char __user*) val;
+ if (_stp_copy_from_user((char*)&val, vaddr, sizeof(val)) != 0) {
+ /* Stack page not resident. */
+ _stp_warn("cannot access arg(%d) "
+ "at user stack address %p\n", n, vaddr);
+ THIS->__retvalue = 0;
+ } else
+ THIS->__retvalue = (int64_t) val;
break;
+ }
default:
goto bad_argnum;
}
@@ -69,7 +71,71 @@ deref_fault: /* branched to from deref() */
}
%}
-/* Return the value of function arg #argnum as an unsigned value. */
-function u_arg:long (argnum:long) {
- return arg(argnum) & 0xffffffff;
+/* Return the value of function arg #argnum as a signed int. */
+function int_arg:long (argnum:long) {
+ return _stp_arg(argnum)
+}
+
+/* Return the value of function arg #argnum as an unsigned int. */
+function uint_arg:long (argnum:long) {
+ return _stp_arg(argnum) & 0xffffffff;
+}
+
+function long_arg:long (argnum:long) {
+ return int_arg(argnum)
+}
+
+function ulong_arg:long (argnum:long) {
+ return uint_arg(argnum)
+}
+
+function longlong_arg:long (argnum:long) {
+ /*
+ * TODO: If argnum == nr_regarg, gcc puts the whole 64-bit arg
+ * on the stack.
+ */
+ lowbits = uint_arg(argnum)
+ highbits = uint_arg(argnum+1)
+ return ((highbits << 32) | lowbits)
+}
+
+function ulonglong_arg:long (argnum:long) {
+ return longlong_arg(argnum)
+}
+
+function pointer_arg:long (argnum:long) {
+ return ulong_arg(argnum)
+}
+
+function s32_arg:long (argnum:long) {
+ return int_arg(argnum)
+}
+
+function u32_arg:long (argnum:long) {
+ return uint_arg(argnum)
+}
+
+function s64_arg:long (argnum:long) {
+ return longlong_arg(argnum)
+}
+
+function u64_arg:long (argnum:long) {
+ return ulonglong_arg(argnum)
}
+
+function asmlinkage() %{
+ CONTEXT->regparm = _STP_REGPARM | 0;
+%}
+
+function fastcall() %{
+ CONTEXT->regparm = _STP_REGPARM | 3;
+%}
+
+function regparm(n) %{
+ if (THIS->n < 0 || THIS->n > 3) {
+ snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
+ "For i386, regparm value must be in the range 0-3.");
+ CONTEXT->last_error = CONTEXT->error_buffer;
+ } else
+ CONTEXT->regparm = _STP_REGPARM | (int) n;
+%}
diff --git a/tapset/x86_64/registers.stp b/tapset/x86_64/registers.stp
index 375be396..45acddd1 100644
--- a/tapset/x86_64/registers.stp
+++ b/tapset/x86_64/registers.stp
@@ -1,22 +1,27 @@
/* Return the named register value as a signed value. */
function register:long (name:string) %{ /* pure */
- if (CONTEXT->regs)
- THIS->__retvalue = (int64_t)
- _stp_get_reg64_by_name(THIS->name, CONTEXT->regs);
- else
- THIS->__retvalue = 0;
+ int reg32 = 0;
+ THIS->__retvalue = (int64_t) _stp_get_reg64_by_name(THIS->name,
+ CONTEXT->regs, &reg32);
+ if (reg32)
+ THIS->__retvalue = _stp_sign_extend32(THIS->__retvalue);
%}
/* Return the named register value as an unsigned value. */
-function u_register:long (name:string) {
- return register(name)
-}
+function u_register:long (name:string) %{
+ THIS->__retvalue = (int64_t) _stp_get_reg64_by_name(THIS->name,
+ CONTEXT->regs, NULL);
+%}
-/* Return the value of function arg #argnum (1=first arg) as a signed value. */
-function arg:long (argnum:long) %{
+/*
+ * Return the value of function arg #argnum (1=first arg).
+ * If truncate=1, mask off the top 32 bits.
+ * If sign_extend=1 and (truncate=1 or the probepoint we've hit is in a
+ * 32-bit app), sign-extend the 32-bit value.
+ */
+function _stp_arg:long (argnum:long, sign_extend:long, truncate:long) %{
long val;
- int result;
- int n;
+ int result, n, nr_regargs;
size_t argsz = sizeof(long);
THIS->__retvalue = 0;
@@ -29,33 +34,46 @@ function arg:long (argnum:long) %{
if (THIS->argnum < 1)
goto bad_argnum;
n = (int) THIS->argnum;
+ nr_regargs = _stp_get_regparm(CONTEXT->regparm, CONTEXT->regs);
if (_stp_probing_32bit_app(CONTEXT->regs)) {
- // TODO: Get nr_regargs from .linkage clause.
argsz = sizeof(int);
- result = _stp_get_arg32_by_number(n, 0, CONTEXT->regs, &val);
- } else {
- // TODO: Get nr_regargs from .linkage clause.
- result = _stp_get_arg64_by_number(n, 6, CONTEXT->regs, &val);
- }
+ result = _stp_get_arg32_by_number(n, nr_regargs, CONTEXT->regs,
+ &val);
+ } else
+ result = _stp_get_arg64_by_number(n, nr_regargs, CONTEXT->regs,
+ &val);
switch (result) {
case 0:
- THIS->__retvalue = (int64_t) val;
+ /* Arg is in register. */
break;
case 1:
- THIS->__retvalue = kread((long *) val);
+ /* Arg is on kernel stack. */
+ val = kread((long *) val);
break;
case 2:
- /*
- * Is copy_from_user satisfactory, since uprobe
- * handlers can block?
- */
- snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
- "arg() not yet implemented for user functions");
- CONTEXT->last_error = CONTEXT->error_buffer;
+ {
+ /* Arg is on user stack. */
+ const char __user *vaddr = (const char __user*) val;
+ if (_stp_copy_from_user((char*)&val, vaddr, argsz) != 0) {
+ /* Stack page not resident. */
+ _stp_warn("cannot access arg(%d) "
+ "at user stack address %p\n", n, vaddr);
+ THIS->__retvalue = 0;
+ return;
+ }
break;
+ }
default:
goto bad_argnum;
}
+ if (THIS->truncate || argsz == sizeof(int)) {
+ if (THIS->sign_extend)
+ THIS->__retvalue = (int64_t) _stp_sign_extend32(val);
+ else
+ /* High bits may be garbage. */
+ THIS->__retvalue = (int64_t) (val & 0xffffffff);
+ } else
+ THIS->__retvalue = (int64_t) val;
return;
bad_argnum:
@@ -77,10 +95,74 @@ function probing_32bit_app() %{
THIS->__retvalue = _stp_probing_32bit_app(CONTEXT->regs);
%}
-/* Return the value of function arg #argnum as an unsigned value. */
-function u_arg:long (argnum:long) {
- if (probing_32bit_app())
- return arg(argnum) & 0xffffffff
- else
- return arg(argnum)
+/* Return the value of function arg #argnum (1=first arg) as a signed int. */
+function int_arg:long (argnum:long) {
+ return _stp_arg(argnum, 1, 1)
+}
+
+/* Return the value of function arg #argnum (1=first arg) as an unsigned int. */
+function uint_arg:long (argnum:long) {
+ return _stp_arg(argnum, 0, 1)
+}
+
+function long_arg:long (argnum:long) {
+ return _stp_arg(argnum, 1, 0)
+}
+
+function ulong_arg:long (argnum:long) {
+ return _stp_arg(argnum, 0, 0)
+}
+
+function longlong_arg:long (argnum:long) {
+ if (probing_32bit_app()) {
+ lowbits = _stp_arg(argnum, 0, 1)
+ highbits = _stp_arg(argnum+1, 0, 1)
+ return ((highbits << 32) | lowbits)
+ } else
+ return _stp_arg(argnum, 0, 0)
+}
+
+function ulonglong_arg:long (argnum:long) {
+ return longlong_arg(argnum)
+}
+
+function pointer_arg:long (argnum:long) {
+ return _stp_arg(argnum, 0, 0)
}
+
+function s32_arg:long (argnum:long) {
+ return int_arg(argnum)
+}
+
+function u32_arg:long (argnum:long) {
+ return uint_arg(argnum)
+}
+
+function s64_arg:long (argnum:long) {
+ return longlong_arg(argnum)
+}
+
+function u64_arg:long (argnum:long) {
+ return ulonglong_arg(argnum)
+}
+
+function asmlinkage() {
+}
+
+function fastcall() {
+}
+
+function regparm(n) %{
+ if (_stp_probing_32bit_app(CONTEXT->regs) &&
+ (THIS->n < 0 || THIS->n > 3)) {
+ snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
+ "For -m32 programs, "
+ "regparm value must be in the range 0-3.");
+ CONTEXT->last_error = CONTEXT->error_buffer;
+ } else if (THIS->n < 0 || THIS->n > 6) {
+ snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
+ "For x86_64, regparm value must be in the range 0-6.");
+ CONTEXT->last_error = CONTEXT->error_buffer;
+ } else
+ CONTEXT->regparm = _STP_REGPARM | (int) n;
+%}
diff --git a/tapsets.cxx b/tapsets.cxx
index af7e5590..47bb20e2 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -209,6 +209,7 @@ common_probe_entryfn_prologue (translator_output* o, string statestr,
o->newline() << "c->unwaddr = 0;";
// reset unwound address cache
o->newline() << "c->pi = 0;";
+ o->newline() << "c->regparm = 0;";
o->newline() << "c->probe_point = 0;";
if (! interruptible)
o->newline() << "c->actionremaining = MAXACTION;";
diff --git a/translate.cxx b/translate.cxx
index c9ec094a..6aef37f0 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -877,6 +877,7 @@ c_unparser::emit_common_header ()
o->newline() << "unsigned long *unwaddr;";
// unwaddr is caching unwound address in each probe handler on ia64.
o->newline() << "struct kretprobe_instance *pi;";
+ o->newline() << "int regparm;";
o->newline() << "va_list *mark_va_list;";
o->newline() << "void *data;";
o->newline() << "#ifdef STP_TIMING";