summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Brolley <brolley@redhat.com>2010-02-02 08:28:16 -0500
committerDave Brolley <brolley@redhat.com>2010-02-02 08:28:16 -0500
commit743757687f9c09bf9ef84b576bc0aa0fc19dea4c (patch)
treebe77bd3f7d03be09774a25f7260182941e99907a
parent241443ad36a5a2cacb9e8e6f12f808d304835f2a (diff)
parentcc57beca8d9d168ef42edb1f8b43f594105dfdf2 (diff)
downloadsystemtap-steved-743757687f9c09bf9ef84b576bc0aa0fc19dea4c.tar.gz
systemtap-steved-743757687f9c09bf9ef84b576bc0aa0fc19dea4c.tar.xz
systemtap-steved-743757687f9c09bf9ef84b576bc0aa0fc19dea4c.zip
Merge branch 'master' of ssh://sources.redhat.com/git/systemtap
-rw-r--r--NEWS11
-rw-r--r--README.security4
-rw-r--r--dwflpp.cxx34
-rw-r--r--elaborate.cxx1
-rw-r--r--initscript/README.stap-server8
-rw-r--r--initscript/README.systemtap8
-rw-r--r--loc2c-test.c4
-rw-r--r--runtime/map.h8
-rw-r--r--session.h2
-rwxr-xr-xstap-serverd13
-rw-r--r--stapprobes.3stap.in52
-rw-r--r--staptree.cxx14
-rw-r--r--tapset-procfs.cxx7
-rw-r--r--tapset/aux_syscalls.stp28
-rw-r--r--tapsets.cxx359
-rwxr-xr-xtestsuite/buildok/hwbkpt.stp14
-rw-r--r--testsuite/lib/systemtap.exp2
-rw-r--r--testsuite/systemtap.base/array_string.exp45
-rw-r--r--testsuite/systemtap.base/global_end.exp3
-rw-r--r--testsuite/systemtap.base/global_end.stp4
-rw-r--r--testsuite/systemtap.base/overflow-get_argv.exp5
-rw-r--r--testsuite/systemtap.base/overflow-get_argv.stp62
-rw-r--r--testsuite/systemtap.base/procfs_write.exp135
-rw-r--r--testsuite/systemtap.examples/index.html3
-rw-r--r--testsuite/systemtap.examples/index.txt9
-rw-r--r--testsuite/systemtap.examples/keyword-index.html3
-rw-r--r--testsuite/systemtap.examples/keyword-index.txt9
-rw-r--r--testsuite/systemtap.examples/profiling/fntimes.meta6
-rwxr-xr-xtestsuite/systemtap.examples/profiling/fntimes.stp30
-rw-r--r--testsuite/systemtap.exelib/lib.stp4
-rw-r--r--testsuite/systemtap.exelib/lib.tcl12
-rw-r--r--testsuite/systemtap.exelib/libmarkunamestack.stp4
-rw-r--r--testsuite/systemtap.exelib/libmarkunamestack.tcl4
33 files changed, 839 insertions, 68 deletions
diff --git a/NEWS b/NEWS
index a23d56a1..82aa205f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,10 @@
+* What's new
+- In order to limit potential impact from future security problems,
+ the stap-server process does not permit its being launched as root.
+
+- New : systemtap now supports probes for Hardware Breakpoints on x86
+ and x86_64. For syntax details, see man page for stapprobes.
+
* What's new in version 1.1
- New tracepoint based tapset for memory subsystem.
@@ -20,8 +27,8 @@
See initscript/README.stap-server for details.
-- Some of the of the compile server client, server and certificate
- management tools have been moved from $bindir to $libexecdir/systemtap.
+- Some of the compile server client, server and certificate management
+ tools have been moved from $bindir to $libexecdir/systemtap.
You should use the new stap-server script or the stap-server initscript
for server management where possible. The stap-server script provides the same
functionality as the stap-server initscript except that the servers are
diff --git a/README.security b/README.security
index 323840f6..4f649d5d 100644
--- a/README.security
+++ b/README.security
@@ -71,7 +71,7 @@ the permissions should be 755, like this:
drwxr-xr-x 2 root root 4096 2007-08-07 13:54 systemtap/
2) More permissive usage. If all systemtap developers should be able
-to add "approved" systemtap modules to /lib/modules/VERSION/system,
+to add "approved" systemtap modules to /lib/modules/VERSION/systemtap,
its permissions should be 775 (and be owned by root, group stapdev),
like this:
@@ -108,6 +108,6 @@ permitted to have. In addition, the effective set of capabilities, the
capabilities from the permitted set that are currently enabled, is
cleared. When needed, a particular capability is enabled, the
operation is performed, then the capability is disabled. The staprun
-program was designed this way to prevent several classes of security
+program was designed in this way to prevent several classes of security
attacks. Security is also heightened by the fact that the only
external program that staprun executes is stapio.
diff --git a/dwflpp.cxx b/dwflpp.cxx
index e6fe017d..d16411c5 100644
--- a/dwflpp.cxx
+++ b/dwflpp.cxx
@@ -1726,9 +1726,10 @@ dwflpp::translate_location(struct obstack *pool,
e->tok);
}
- // pc is relative to current module, which is what get_cfa_ops
- // and c_translate_location expects.
- Dwarf_Op *cfa_ops = get_cfa_ops (pc);
+ // pc is in the dw address space of the current module, which is what
+ // c_translate_location expects. get_cfa_ops wants the global dwfl address.
+ Dwarf_Addr addr = pc + module_bias;
+ Dwarf_Op *cfa_ops = get_cfa_ops (addr);
return c_translate_location (pool, &loc2c_error, this,
&loc2c_emit_address,
1, 0 /* PR9768 */,
@@ -2783,17 +2784,17 @@ dwflpp::get_cfa_ops (Dwarf_Addr pc)
clog << "get_cfa_ops @0x" << hex << pc << dec
<< ", module_start @0x" << hex << module_start << dec << endl;
-#if _ELFUTILS_PREREQ(0,142)
// Try debug_frame first, then fall back on eh_frame.
- size_t cfa_nops;
- Dwarf_Addr bias;
+ size_t cfa_nops = 0;
+ Dwarf_Addr bias = 0;
+ Dwarf_Frame *frame = NULL;
+#if _ELFUTILS_PREREQ(0,142)
Dwarf_CFI *cfi = dwfl_module_dwarf_cfi (module, &bias);
if (cfi != NULL)
{
if (sess.verbose > 3)
clog << "got dwarf cfi bias: 0x" << hex << bias << dec << endl;
- Dwarf_Frame *frame = NULL;
- if (dwarf_cfi_addrframe (cfi, pc, &frame) == 0)
+ if (dwarf_cfi_addrframe (cfi, pc - bias, &frame) == 0)
dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops);
else if (sess.verbose > 3)
clog << "dwarf_cfi_addrframe failed: " << dwarf_errmsg(-1) << endl;
@@ -2809,7 +2810,7 @@ dwflpp::get_cfa_ops (Dwarf_Addr pc)
if (sess.verbose > 3)
clog << "got eh cfi bias: 0x" << hex << bias << dec << endl;
Dwarf_Frame *frame = NULL;
- if (dwarf_cfi_addrframe (cfi, pc, &frame) == 0)
+ if (dwarf_cfi_addrframe (cfi, pc - bias, &frame) == 0)
dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops);
else if (sess.verbose > 3)
clog << "dwarf_cfi_addrframe failed: " << dwarf_errmsg(-1) << endl;
@@ -2821,7 +2822,20 @@ dwflpp::get_cfa_ops (Dwarf_Addr pc)
#endif
if (sess.verbose > 2)
- clog << (cfa_ops == NULL ? "not " : " ") << "found cfa" << endl;
+ {
+ if (cfa_ops == NULL)
+ clog << "not found cfa" << endl;
+ else
+ {
+ Dwarf_Addr frame_start, frame_end;
+ bool frame_signalp;
+ int info = dwarf_frame_info (frame, &frame_start, &frame_end,
+ &frame_signalp);
+ clog << "found cfa, info:" << info << " [start: 0x" << hex
+ << frame_start << dec << ", end: 0x" << hex << frame_end
+ << dec << "), nops: " << cfa_nops << endl;
+ }
+ }
return cfa_ops;
}
diff --git a/elaborate.cxx b/elaborate.cxx
index c1465e97..2ae90139 100644
--- a/elaborate.cxx
+++ b/elaborate.cxx
@@ -1523,6 +1523,7 @@ systemtap_session::systemtap_session ():
be_derived_probes(0),
dwarf_derived_probes(0),
kprobe_derived_probes(0),
+ hwbkpt_derived_probes(0),
uprobe_derived_probes(0),
utrace_derived_probes(0),
itrace_derived_probes(0),
diff --git a/initscript/README.stap-server b/initscript/README.stap-server
index c4089299..b1cbdbbd 100644
--- a/initscript/README.stap-server
+++ b/initscript/README.stap-server
@@ -77,7 +77,7 @@ to specify servers to be managed.
The default architecture is the architecture of the host.
2.3.3 -r kernel-release
- This option specifies the target kernel release of the server an is
+ This option specifies the target kernel release of the server and is
analogous to the -r option of 'stap'. See stap(1) for more details.
The default release is that of the currently running kernel.
@@ -88,7 +88,7 @@ to specify servers to be managed.
must be a member of the group 'stap-server'.
2.3.5 -I path
- This option specifies an additional path to be searched by thes server for
+ This option specifies an additional path to be searched by the server for
tapsets and is analogous to the -I option of 'stap'. See stap(1) for more
details.
@@ -119,14 +119,14 @@ to specify servers to be managed.
the given nickname is found, and the action is 'start' (or an action behaving
like 'start' (see below), the server will be started with the given nickname.
If no configuration with the given nickname is found, and the action is not
- 'start' (or an action behaving" "like 'start', it is an error. If a nickname is
+ 'start' (or an action behaving like 'start', it is an error. If a nickname is
not specified for a server, its nickname will be its process id.
2.3.10 -p pid
This option allows the specification of a server configuration by process id.
When -p is specified, a currently running server with the given process id will
be searched for. If no such server is found, it is an error. If a server with
- the given procss id is found, the -a, -r, -I, -R, -B and -u options for that
+ the given process id is found, the -a, -r, -I, -R, -B and -u options for that
server will be used as if they were specified on the command line.
3. Files
diff --git a/initscript/README.systemtap b/initscript/README.systemtap
index ebce285e..61bd5b24 100644
--- a/initscript/README.systemtap
+++ b/initscript/README.systemtap
@@ -199,7 +199,7 @@ Configuration file allows us
4.1.9 AUTOCOMPILE
If this is set 'yes', systemtap initscript automatically tries to compile
- specified script if there is no valid cache. Otherwides, it just fails to
+ specified script if there is no valid cache. Otherwise, it just fails to
run script(s).
(default: yes)
@@ -396,10 +396,10 @@ script2_REQ=script1
resulting module on the client host.
6.1.2 Unprivileged Users (Module Signing)
- If the user on the client host is a an ordinary unprivileged user or a member
+ If the user on the client host is an ordinary unprivileged user or a member
of the group stapusr, he will get an error when running the example above,
since he does not have authority to load the unsigned module returned by the
- server. Howeverm if he is a member of the group stapusr and if he can get the
+ server. However if he is a member of the group stapusr and if he can get the
module signed by a trusted signer, he will be able to load it.
The server will sign the module if the --unprivileged option is specified by
@@ -428,7 +428,7 @@ script2_REQ=script1
The example above should now work as expected.
6.1.3 Ordinary Users
- Ordinary users are users who are not root and not a member of either of the
+ Ordinary users are users who are not root or a member of either of the
groups stapdev or stapusr.
These users can still use the stap-server service to compile their scripts
diff --git a/loc2c-test.c b/loc2c-test.c
index 9d4973ed..6631252c 100644
--- a/loc2c-test.c
+++ b/loc2c-test.c
@@ -556,7 +556,7 @@ main (int argc, char **argv)
if (cfi != NULL)
{
Dwarf_Frame *frame = NULL;
- if (dwarf_cfi_addrframe (cfi, pc, &frame) == 0)
+ if (dwarf_cfi_addrframe (cfi, pc - bias, &frame) == 0)
dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops);
}
if (cfa_ops == NULL)
@@ -565,7 +565,7 @@ main (int argc, char **argv)
if (cfi != NULL)
{
Dwarf_Frame *frame = NULL;
- if (dwarf_cfi_addrframe (cfi, pc, &frame) == 0)
+ if (dwarf_cfi_addrframe (cfi, pc - bias, &frame) == 0)
dwarf_frame_cfa (frame, &cfa_ops, &cfa_nops);
}
}
diff --git a/runtime/map.h b/runtime/map.h
index 6c1c855b..09e46f85 100644
--- a/runtime/map.h
+++ b/runtime/map.h
@@ -49,10 +49,12 @@ amount of memory. Do not increase above 5. */
#define MAX_KEY_ARITY 5
#endif
-/** Maximum length of strings in maps. This sets the amount of space reserved
- for each string. */
+/** Maximum length of strings in maps. This sets the amount of space
+ reserved for each string. This should match MAXSTRINGLEN. If
+ MAP_STRING_LENGTH is less than MAXSTRINGLEN, a user could get
+ strings truncated that are stored in arrays. */
#ifndef MAP_STRING_LENGTH
-#define MAP_STRING_LENGTH 256
+#define MAP_STRING_LENGTH MAXSTRINGLEN
#endif
/** @cond DONT_INCLUDE */
diff --git a/session.h b/session.h
index cc13c321..df3aae41 100644
--- a/session.h
+++ b/session.h
@@ -31,6 +31,7 @@ struct derived_probe;
struct be_derived_probe_group;
struct dwarf_derived_probe_group;
struct kprobe_derived_probe_group;
+struct hwbkpt_derived_probe_group;
struct uprobe_derived_probe_group;
struct utrace_derived_probe_group;
struct itrace_derived_probe_group;
@@ -175,6 +176,7 @@ struct systemtap_session
be_derived_probe_group* be_derived_probes;
dwarf_derived_probe_group* dwarf_derived_probes;
kprobe_derived_probe_group* kprobe_derived_probes;
+ hwbkpt_derived_probe_group* hwbkpt_derived_probes;
uprobe_derived_probe_group* uprobe_derived_probes;
utrace_derived_probe_group* utrace_derived_probes;
itrace_derived_probe_group* itrace_derived_probes;
diff --git a/stap-serverd b/stap-serverd
index 4d8a10ce..fc186233 100755
--- a/stap-serverd
+++ b/stap-serverd
@@ -2,7 +2,7 @@
# Compile server manager for systemtap
#
-# Copyright (C) 2008, 2009 Red Hat Inc.
+# Copyright (C) 2008-2010 Red Hat Inc.
#
# 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
@@ -19,6 +19,17 @@ trap 'terminate' SIGTERM SIGINT
# Initialize the environment
. ${PKGLIBEXECDIR}stap-env
+# PR11197: security prophylactics
+if [ -z "$STAP_PR11197_OVERRIDE" ]; then
+ # 1) reject use as root, except via a special environment variable
+ if [ `id -u` -eq 0 ]; then
+ echo "For security reasons, invocation of stap-server as root is not supported." 1>&2
+ exit 1
+ fi
+ # 2) ... etc ...
+fi
+
+
#-----------------------------------------------------------------------------
# Helper functions.
#-----------------------------------------------------------------------------
diff --git a/stapprobes.3stap.in b/stapprobes.3stap.in
index 8b39545e..23de56b0 100644
--- a/stapprobes.3stap.in
+++ b/stapprobes.3stap.in
@@ -660,6 +660,55 @@ and a string of name=value pairs for all parameters of the tracepoint
is available in
.BR $$vars " or " $$parms .
+.SS HARDWARE BREAKPOINTS
+This family of probes is used to set hardware watchpoints for a given
+ (global) kernel symbol. The probes take three components as inputs :
+
+1. The
+.BR virtual address / name
+of the kernel symbol to be traced is supplied as argument to this class
+of probes. ( Probes for only data segment variables are supported. Probing
+local variables of a function cannot be done.)
+
+2. Nature of access to be probed :
+a.
+.I .write
+probe gets triggered when a write happens at the specified address/symbol
+name.
+b.
+.I rw
+probe is triggered when either a read or write happens.
+
+3.
+.BR .length
+(optional)
+Users have the option of specifying the address interval to be probed
+using "length" constructs. The user-specified length gets approximated
+to the closest possible address length that the architecture can
+support. If the specified length exceeds the limits imposed by
+architecture, an error message is flagged and probe registration fails.
+Wherever 'length' is not specified, the translator requests a hardware
+breakpoint probe of length 1. It should be noted that the "length"
+construct is not valid with symbol names.
+
+Following constructs are supported :
+.SAMPLE
+probe kernel.data(ADDRESS).write
+probe kernel.data(ADDRESS).rw
+probe kernel.data(ADDRESS).length(LEN).write
+probe kernel.data(ADDRESS).length(LEN).rw
+probe kernel.data("SYMBOL_NAME").write
+probe kernel.data("SYMBOL_NAME").rw
+.ESAMPLE
+
+This set of probes make use of the debug registers of the processor,
+which is a scarce resource. (4 on x86 , 1 on powerpc ) The script
+translation flags a warning if a user requests more hardware breakpoint probes
+than the limits set by architecture. For example,a pass-2 warning is flashed
+when an input script requests 5 hardware breakpoint probes on an x86
+system while x86 architecture supports a maximum of 4 breakpoints.
+Users are cautioned to set probes judiciously.
+
.SH EXAMPLES
.PP
Here are some example probe points, defining the associated events.
@@ -696,6 +745,9 @@ refers to the statement of line 2917 within "kernel/sched.c".
kernel.statement("bio_init@fs/bio.c+3")
refers to the statement at line bio_init+3 within "fs/bio.c".
.TP
+kernel.data("pid_max").write
+refers to a hardware preakpoint of type "write" set on pid_max
+.TP
syscall.*.return
refers to the group of probe aliases with any name in the third position
diff --git a/staptree.cxx b/staptree.cxx
index 4c3b3090..7a335fc3 100644
--- a/staptree.cxx
+++ b/staptree.cxx
@@ -1897,19 +1897,21 @@ varuse_collecting_visitor::visit_arrayindex (arrayindex *e)
void
varuse_collecting_visitor::visit_pre_crement (pre_crement *e)
{
- expression* last_lrvalue = current_lrvalue;
- current_lrvalue = e->operand; // leave a mark for ::visit_symbol
+ // PR6954: regard as pure writes
+ expression* last_lvalue = current_lvalue;
+ current_lvalue = e->operand; // leave a mark for ::visit_symbol
functioncall_traversing_visitor::visit_pre_crement (e);
- current_lrvalue = last_lrvalue;
+ current_lvalue = last_lvalue;
}
void
varuse_collecting_visitor::visit_post_crement (post_crement *e)
{
- expression* last_lrvalue = current_lrvalue;
- current_lrvalue = e->operand; // leave a mark for ::visit_symbol
+ // PR6954: regard as pure writes
+ expression* last_lvalue = current_lvalue;
+ current_lvalue = e->operand; // leave a mark for ::visit_symbol
functioncall_traversing_visitor::visit_post_crement (e);
- current_lrvalue = last_lrvalue;
+ current_lvalue = last_lvalue;
}
void
diff --git a/tapset-procfs.cxx b/tapset-procfs.cxx
index aa75af42..f5ab95f8 100644
--- a/tapset-procfs.cxx
+++ b/tapset-procfs.cxx
@@ -383,9 +383,10 @@ procfs_var_expanding_visitor::visit_target_symbol (target_symbol* e)
string locvalue = "CONTEXT->data";
if (! lvalue)
- ec->code = string("_stp_copy_from_user(THIS->__retvalue, ((struct _stp_procfs_data *)(")
- + locvalue + string("))->buffer, ((struct _stp_procfs_data *)(") + locvalue
- + string("))->count); /* pure */");
+ ec->code = string(" struct _stp_procfs_data *data = (struct _stp_procfs_data *)(") + locvalue + string("); /* pure */\n")
+
+ + string(" _stp_copy_from_user(THIS->__retvalue, data->buffer, data->count);\n")
+ + string(" THIS->__retvalue[data->count] = '\\0';\n");
else
ec->code = string("int bytes = 0;\n")
+ string(" struct _stp_procfs_data *data = (struct _stp_procfs_data *)(") + locvalue + string(");\n")
diff --git a/tapset/aux_syscalls.stp b/tapset/aux_syscalls.stp
index 4577d64e..2d0ba210 100644
--- a/tapset/aux_syscalls.stp
+++ b/tapset/aux_syscalls.stp
@@ -401,20 +401,20 @@ function __sem_flags:string(semflg:long)
/* This function copies an argv from userspace. */
function __get_argv:string(a:long, first:long)
%{ /* pure */
- char __user *__user *argv = (char __user *__user *)(long)THIS->a;
+ char __user *__user *argv = (char __user *__user *)(long)THIS->a;
char __user *vstr;
int space, rc, len = MAXSTRINGLEN;
char *str = THIS->__retvalue;
char buf[80];
char *ptr = buf;
-
+
if (THIS->first && argv)
argv++;
- while (argv != NULL) {
+ while (argv != NULL && len) {
if (__stp_get_user (vstr, argv))
- break;
+ break;
if (vstr == NULL)
break;
@@ -443,8 +443,8 @@ function __get_argv:string(a:long, first:long)
*str++='\"';
len--;
}
-
- rc = strlcpy (str, buf, len);
+
+ rc = min(len, (int) strlcpy (str, buf, len));
str += rc;
len -= rc;
@@ -455,13 +455,15 @@ function __get_argv:string(a:long, first:long)
argv++;
}
+ if (!len)
+ --str;
*str = 0;
%}
/* This function copies an argv from userspace. */
function __get_compat_argv:string(a:long, first:long)
%{ /* pure */
#ifdef CONFIG_COMPAT
- compat_uptr_t __user *__user *argv = (compat_uptr_t __user *__user *)(long)THIS->a;
+ compat_uptr_t __user *__user *argv = (compat_uptr_t __user *__user *)(long)THIS->a;
compat_uptr_t __user *vstr;
int space, rc, len = MAXSTRINGLEN;
char *str = THIS->__retvalue;
@@ -471,9 +473,9 @@ function __get_compat_argv:string(a:long, first:long)
if (THIS->first && argv)
argv++;
- while (argv != NULL) {
+ while (argv != NULL && len) {
if (__stp_get_user (vstr, argv))
- break;
+ break;
if (vstr == NULL)
break;
@@ -502,8 +504,8 @@ function __get_compat_argv:string(a:long, first:long)
*str++='\"';
len--;
}
-
- rc = strlcpy (str, buf, len);
+
+ rc = min(len, (int) strlcpy (str, buf, len));
str += rc;
len -= rc;
@@ -514,6 +516,8 @@ function __get_compat_argv:string(a:long, first:long)
argv++;
}
+ if (!len)
+ --str;
*str = 0;
#endif
%}
@@ -1336,7 +1340,7 @@ function _sock_type_str:string(type:long)
strlcpy (THIS->__retvalue, "SOCK_PACKET", MAXSTRINGLEN);
break;
default:
- strlcpy (THIS->__retvalue, "UNKNOWN VALUE: %d", t);
+ snprintf (THIS->__retvalue, MAXSTRINGLEN, "UNKNOWN VALUE: %d", t);
break;
}
diff --git a/tapsets.cxx b/tapsets.cxx
index 22aae041..0711411e 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -3040,7 +3040,6 @@ dwarf_derived_probe::saveargs(dwarf_query& q, Dwarf_Die* scope_die, dwarf_var_ex
if (null_die(scope_die))
return;
- stringstream argstream;
string type_name;
Dwarf_Die type_die;
@@ -5319,7 +5318,341 @@ kprobe_builder::build(systemtap_session & sess,
}
}
+// ------------------------------------------------------------------------
+// Hardware breakpoint based probes.
+// ------------------------------------------------------------------------
+
+static const string TOK_HWBKPT("data");
+static const string TOK_HWBKPT_WRITE("write");
+static const string TOK_HWBKPT_RW("rw");
+static const string TOK_LENGTH("length");
+
+#define HWBKPT_READ 0
+#define HWBKPT_WRITE 1
+#define HWBKPT_RW 2
+struct hwbkpt_derived_probe: public derived_probe
+{
+ hwbkpt_derived_probe (probe *base,
+ probe_point *location,
+ uint64_t addr,
+ string symname,
+ unsigned int len,
+ bool has_only_read_access,
+ bool has_only_write_access,
+ bool has_rw_access
+ );
+ Dwarf_Addr hwbkpt_addr;
+ string symbol_name;
+ unsigned int hwbkpt_access,hwbkpt_len;
+
+ void printsig (std::ostream &o) const;
+ void join_group (systemtap_session& s);
+};
+
+struct hwbkpt_derived_probe_group: public derived_probe_group
+{
+ unsigned int max_hwbkpt_probes_by_arch;
+
+private:
+ vector<hwbkpt_derived_probe*> hwbkpt_probes_vector;
+ typedef vector<hwbkpt_derived_probe*>::iterator h_p_v_iterator;
+
+public:
+ void enroll (hwbkpt_derived_probe* probe, systemtap_session& s);
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
+
+hwbkpt_derived_probe::hwbkpt_derived_probe (probe *base,
+ probe_point *location,
+ uint64_t addr,
+ string symname,
+ unsigned int len,
+ bool has_only_read_access,
+ bool has_only_write_access,
+ bool has_rw_access
+ ):
+ derived_probe (base, location),
+ hwbkpt_addr (addr),
+ symbol_name (symname),
+ hwbkpt_len (len)
+{
+ this->tok = base->tok;
+
+ vector<probe_point::component*> comps;
+ comps.push_back (new probe_point::component(TOK_KERNEL));
+
+ if (hwbkpt_addr)
+ comps.push_back (new probe_point::component (TOK_HWBKPT, new literal_number(hwbkpt_addr)));
+ else
+ if (symbol_name.size())
+ comps.push_back (new probe_point::component (TOK_HWBKPT, new literal_string(symbol_name)));
+
+ comps.push_back (new probe_point::component (TOK_LENGTH, new literal_number(hwbkpt_len)));
+
+ if (has_only_read_access)
+ this->hwbkpt_access = HWBKPT_READ ;
+//TODO add code for comps.push_back for read, since this flag is not for x86
+
+ else
+ {
+ if (has_only_write_access)
+ {
+ this->hwbkpt_access = HWBKPT_WRITE ;
+ comps.push_back (new probe_point::component(TOK_HWBKPT_WRITE));
+ }
+ else
+ {
+ this->hwbkpt_access = HWBKPT_RW ;
+ comps.push_back (new probe_point::component(TOK_HWBKPT_RW));
+ }
+ }
+
+ this->sole_location()->components = comps;
+}
+
+void hwbkpt_derived_probe::printsig (ostream& o) const
+{
+ sole_location()->print (o);
+ printsig_nested (o);
+}
+
+void hwbkpt_derived_probe::join_group (systemtap_session& s)
+{
+ if (! s.hwbkpt_derived_probes) {
+ s.hwbkpt_derived_probes = new hwbkpt_derived_probe_group ();
+ if (s.architecture == "i386" || s.architecture == "x86_64" )
+ s.hwbkpt_derived_probes->max_hwbkpt_probes_by_arch = 4;
+ else {
+ //TODO: Add code for other archs too
+ s.hwbkpt_derived_probes->max_hwbkpt_probes_by_arch = 0;
+ throw semantic_error ("Hardware Breakpoints not supported on arch " + s.architecture);
+ }
+ }
+ s.hwbkpt_derived_probes->enroll (this, s);
+}
+
+void hwbkpt_derived_probe_group::enroll (hwbkpt_derived_probe* p, systemtap_session& s)
+{
+ if (hwbkpt_probes_vector.size() >= max_hwbkpt_probes_by_arch ) {
+ clog << "Warning:No of Hardware breakpoint probes in script exceeds "
+ << "the number of hardware breakpoints that the architecture "
+ << "can support : max " << max_hwbkpt_probes_by_arch
+ << " on " << s.architecture ;
+ }
+ hwbkpt_probes_vector.push_back (p);
+}
+
+void
+hwbkpt_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (hwbkpt_probes_vector.empty()) return;
+
+ s.op->newline() << "/* ---- hwbkpt-based probes ---- */";
+
+ s.op->newline() << "#include <linux/perf_event.h>";
+ s.op->newline() << "#include <linux/hw_breakpoint.h>";
+ s.op->newline();
+
+ // Forward declare the master entry functions
+ s.op->newline() << "static int enter_hwbkpt_probe (struct perf_event *bp,";
+ s.op->line() << " int nmi,";
+ s.op->line() << " struct perf_sample_data *data,";
+ s.op->line() << " struct pt_regs *regs);";
+ // Emit the actual probe list.
+
+ s.op->newline() << "static struct perf_event_attr ";
+ s.op->newline() << "stap_hwbkpt_probe_array[" << hwbkpt_probes_vector.size() << "];";
+
+ s.op->newline() << "static struct perf_event **";
+ s.op->newline() << "stap_hwbkpt_ret_array[" << hwbkpt_probes_vector.size() << "];";
+ s.op->newline() << "static struct stap_hwbkpt_probe {";
+ s.op->newline() << "int registered_p:1;";
+// registered_p = 0 signifies a probe that failed registration
+// registered_p = 1 signifies a probe that got registered successfully
+
+ // Probe point & Symbol Names are mostly small and uniform enough
+ // to justify putting const char*.
+#define CALCIT(var) \
+ s.op->newline() << "const char * const " << #var << ";";
+ CALCIT(pp);
+ CALCIT(symbol);
+#undef CALCIT
+
+ s.op->newline() << "const unsigned long address;";
+ s.op->newline() << "uint8_t atype;";
+ s.op->newline() << "uint8_t len;";
+ s.op->newline() << "void (* const ph) (struct context*);";
+ s.op->newline() << "} stap_hwbkpt_probes[] = {";
+ s.op->indent(1);
+
+ for (unsigned int it = 0; it < hwbkpt_probes_vector.size(); it++)
+ {
+ hwbkpt_derived_probe* p = hwbkpt_probes_vector.at(it);
+ s.op->newline() << "{";
+ s.op->line() << " .registered_p=1,";
+ if (p->symbol_name.size())
+ s.op->line() << " .address=(unsigned long)0x0" << "ULL,";
+ else
+ s.op->line() << " .address=(unsigned long)0x" << hex << p->hwbkpt_addr << dec << "ULL,";
+ switch(p->hwbkpt_access){
+ case HWBKPT_READ:
+ s.op->line() << " .atype=HW_BREAKPOINT_R ,";
+ case HWBKPT_WRITE:
+ s.op->line() << " .atype=HW_BREAKPOINT_W ,";
+ case HWBKPT_RW:
+ s.op->line() << " .atype=HW_BREAKPOINT_R|HW_BREAKPOINT_W ,";
+ };
+ s.op->line() << " .len=" << p->hwbkpt_len << ",";
+ s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
+ s.op->line() << " .symbol=\"" << p->symbol_name << "\",";
+ s.op->line() << " .ph=&" << p->name << "";
+ s.op->line() << " },";
+ }
+ s.op->newline() << "};";
+
+ // Emit the hwbkpt callback function
+ s.op->newline() ;
+ s.op->newline() << "static int enter_hwbkpt_probe (struct perf_event *bp,";
+ s.op->line() << " int nmi,";
+ s.op->line() << " struct perf_sample_data *data,";
+ s.op->line() << " struct pt_regs *regs) {";
+ s.op->newline() << "unsigned int i;";
+ s.op->newline() << "if (bp->attr.type != PERF_TYPE_BREAKPOINT )";
+ s.op->newline() << "return -1;";
+ s.op->newline() << "for ( i=0; i<" << hwbkpt_probes_vector.size() << "; i++) {";
+ s.op->newline() << "struct perf_event_attr *hp = & stap_hwbkpt_probe_array[i];";
+ s.op->newline() << "if (bp->attr.bp_addr==hp->bp_addr && bp->attr.bp_type==hp->bp_type && bp->attr.bp_len==hp->bp_len ) {";
+ s.op->newline() << "struct stap_hwbkpt_probe *sdp = &stap_hwbkpt_probes[i];";
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp");
+ s.op->newline() << "c->regs = regs;";
+ s.op->newline() << "(*sdp->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline() << "return 0;";
+ s.op->newline() << "}";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "return 0;";
+ s.op->newline() << "}";
+}
+
+void
+hwbkpt_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+ s.op->newline() << "for (i=0; i<" << hwbkpt_probes_vector.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_hwbkpt_probe *sdp = & stap_hwbkpt_probes[i];";
+ s.op->newline() << "struct perf_event_attr *hp = & stap_hwbkpt_probe_array[i];";
+ s.op->newline() << "void *addr = (void *) sdp->address;";
+ s.op->newline() << "const char *hwbkpt_symbol_name = addr ? NULL : sdp->symbol;";
+ s.op->newline() << " hw_breakpoint_init(hp);";
+ s.op->newline() << " if (addr) ";
+ s.op->newline() << " hp->bp_addr = (unsigned long) addr;";
+ s.op->newline() << " else { ";
+ s.op->newline() << " hp->bp_addr = kallsyms_lookup_name(hwbkpt_symbol_name);";
+ s.op->newline() << " if (!hp->bp_addr) { ";
+ s.op->newline() << " _stp_warn(\"Probe %s registration skipped : invalid symbol %s \",sdp->pp,hwbkpt_symbol_name);";
+ s.op->newline() << " sdp->registered_p = 0;";
+ s.op->newline() << " } ";
+ s.op->newline() << " } ";
+ if (s.architecture == "i386" || s.architecture == "x86_64" ) {
+ s.op->newline() << " switch(sdp->len) {";
+ s.op->newline() << " case 1:";
+ s.op->newline() << " hp->bp_len = HW_BREAKPOINT_LEN_1;";
+ s.op->newline() << " break;";
+ s.op->newline() << " case 2:";
+ s.op->newline() << " hp->bp_len = HW_BREAKPOINT_LEN_2;";
+ s.op->newline() << " break;";
+ s.op->newline() << " case 3:";
+ s.op->newline() << " case 4:";
+ s.op->newline() << " hp->bp_len = HW_BREAKPOINT_LEN_4;";
+ s.op->newline() << " break;";
+ s.op->newline() << " case 5:";
+ s.op->newline() << " case 6:";
+ s.op->newline() << " case 7:";
+ s.op->newline() << " case 8:";
+ s.op->newline() << " hp->bp_len = HW_BREAKPOINT_LEN_8;";
+ s.op->newline() << " break;";
+ s.op->newline() << " default:";
+ s.op->newline() << " _stp_warn(\"Probe %s registration skipped : unsupported length. Supported lengths for x86 are 1,2,4 {8 for x86_84 only} \",sdp->pp);";
+ s.op->newline() << " sdp->registered_p = 0;";
+ s.op->newline() << " }";
+ s.op->newline() << " hp->bp_type = sdp->atype;";
+ s.op->newline() << " if (sdp->atype == HW_BREAKPOINT_R) {";
+ s.op->newline() << " _stp_warn(\"Probe %s registration skipped : READ-ONLY hardware breakpoints not supported on x86\",sdp->pp);";
+ s.op->newline() << " sdp->registered_p = 0;";
+ s.op->newline() << " }";
+ } // end of x86 / x86_64 specific checks
+ s.op->newline() << "probe_point = sdp->pp;"; // for error messages
+ s.op->newline() << "if ( sdp->registered_p ) {";
+ s.op->newline() << " stap_hwbkpt_ret_array[i] = register_wide_hw_breakpoint( hp, (void *)&enter_hwbkpt_probe );";
+ s.op->newline() << " if (IS_ERR(stap_hwbkpt_ret_array[i])) {";
+ s.op->newline() << " int err_code = PTR_ERR(stap_hwbkpt_ret_array[i]);";
+ s.op->newline() << " sdp->registered_p = 0;";
+ s.op->newline(1) << " _stp_warn(\" Hwbkpt Probe %s : Registration error = %d, Addr = %p, name = %s\", probe_point, err_code, addr, hwbkpt_symbol_name);";
+ s.op->newline(-1) << " }";
+ s.op->newline() << " else sdp->registered_p = 1;";
+ s.op->newline() << "}";
+ s.op->newline(-1) << "}"; // for loop
+}
+
+void
+hwbkpt_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ //Unregister hwbkpt probes.
+ s.op->newline() << "for (i=0; i<" << hwbkpt_probes_vector.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_hwbkpt_probe *sdp = & stap_hwbkpt_probes[i];";
+ s.op->newline() << " if ( sdp->registered_p == 0 ) continue;";
+ s.op->newline() << " unregister_wide_hw_breakpoint(stap_hwbkpt_ret_array[i]);";
+ s.op->newline() << "sdp->registered_p = 0;";
+ s.op->newline(-1) << "}";
+}
+
+struct hwbkpt_builder: public derived_probe_builder
+{
+ hwbkpt_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results);
+};
+
+void
+hwbkpt_builder::build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+{
+ string symbol_str_val;
+ int64_t hwbkpt_address, len;
+ bool has_addr, has_symbol_str, has_write, has_rw, has_len;
+
+ has_addr = get_param (parameters, TOK_HWBKPT, hwbkpt_address);
+ has_symbol_str = get_param (parameters, TOK_HWBKPT, symbol_str_val);
+ has_len = get_param (parameters, TOK_LENGTH, len);
+ has_write = (parameters.find(TOK_HWBKPT_WRITE) != parameters.end());
+ has_rw = (parameters.find(TOK_HWBKPT_RW) != parameters.end());
+
+ if (!has_len)
+ len = 1;
+
+ if (has_addr)
+ finished_results.push_back (new hwbkpt_derived_probe (base,
+ location,
+ hwbkpt_address,
+ "",len,0,
+ has_write,
+ has_rw));
+ else // has symbol_str
+ finished_results.push_back (new hwbkpt_derived_probe (base,
+ location,
+ 0,
+ symbol_str_val,len,0,
+ has_write,
+ has_rw));
+}
// ------------------------------------------------------------------------
// statically inserted kernel-tracepoint derived probes
@@ -5811,7 +6144,7 @@ tracepoint_derived_probe_group::emit_module_decls (systemtap_session& s)
}
s.op->line() << ") {";
s.op->indent(1);
- common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING",
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING",
lex_cast_qstring (*p->sole_location()));
s.op->newline() << "c->marker_name = "
<< lex_cast_qstring (p->tracepoint_name)
@@ -6140,7 +6473,6 @@ tracepoint_builder::init_dw(systemtap_session& s)
return true;
}
-
void
tracepoint_builder::build(systemtap_session& s,
probe *base, probe_point *location,
@@ -6158,7 +6490,6 @@ tracepoint_builder::build(systemtap_session& s,
}
-
// ------------------------------------------------------------------------
// Standard tapset registry.
// ------------------------------------------------------------------------
@@ -6205,6 +6536,25 @@ register_standard_tapsets(systemtap_session & s)
->bind_num(TOK_MAXACTIVE)->bind(new kprobe_builder());
s.pattern_root->bind(TOK_KPROBE)->bind_num(TOK_STATEMENT)
->bind(TOK_ABSOLUTE)->bind(new kprobe_builder());
+
+ //Hwbkpt based probe
+ if (s.kernel_config["CONFIG_PERF_EVENTS"] == string("y") &&
+ s.kernel_config["CONFIG_HAVE_HW_BREAKPOINT"] == string("y")) {
+ s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+ ->bind(TOK_HWBKPT_WRITE)->bind(new hwbkpt_builder());
+ s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_HWBKPT)
+ ->bind(TOK_HWBKPT_WRITE)->bind(new hwbkpt_builder());
+ s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+ ->bind(TOK_HWBKPT_RW)->bind(new hwbkpt_builder());
+ s.pattern_root->bind(TOK_KERNEL)->bind_str(TOK_HWBKPT)
+ ->bind(TOK_HWBKPT_RW)->bind(new hwbkpt_builder());
+ s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+ ->bind_num(TOK_LENGTH)->bind(TOK_HWBKPT_WRITE)->bind(new hwbkpt_builder());
+ s.pattern_root->bind(TOK_KERNEL)->bind_num(TOK_HWBKPT)
+ ->bind_num(TOK_LENGTH)->bind(TOK_HWBKPT_RW)->bind(new hwbkpt_builder());
+ // length supported with address only, not symbol names
+ }
+
}
@@ -6231,6 +6581,7 @@ all_session_groups(systemtap_session& s)
DOONE(mark);
DOONE(tracepoint);
DOONE(kprobe);
+ DOONE(hwbkpt);
DOONE(hrtimer);
DOONE(perfmon);
DOONE(procfs);
diff --git a/testsuite/buildok/hwbkpt.stp b/testsuite/buildok/hwbkpt.stp
new file mode 100755
index 00000000..ac1a6f9c
--- /dev/null
+++ b/testsuite/buildok/hwbkpt.stp
@@ -0,0 +1,14 @@
+#! stap -wp4
+
+probe
+%(CONFIG_PERF_EVENTS=="y" && CONFIG_HAVE_HW_BREAKPOINT=="y" %?
+ kernel.data("pid_max").write
+%:
+ never
+%) {}
+probe
+%(CONFIG_PERF_EVENTS=="y" && CONFIG_HAVE_HW_BREAKPOINT=="y" %?
+ kernel.data("pid_max").rw
+%:
+ never
+%) {}
diff --git a/testsuite/lib/systemtap.exp b/testsuite/lib/systemtap.exp
index e0e01bcc..03170081 100644
--- a/testsuite/lib/systemtap.exp
+++ b/testsuite/lib/systemtap.exp
@@ -153,7 +153,7 @@ proc setup_server {} {
exec chmod 666 $logfile
# Try to find or start the server.
- set server_pid [exec stap-start-server --log=$logfile]
+ set server_pid [exec env STAP_PR11197_OVERRIDE=1 stap-start-server --log=$logfile]
if { "$server_pid" == "" } then {
print "Cannot start a systemtap server"
set server_pid 0
diff --git a/testsuite/systemtap.base/array_string.exp b/testsuite/systemtap.base/array_string.exp
new file mode 100644
index 00000000..92df1149
--- /dev/null
+++ b/testsuite/systemtap.base/array_string.exp
@@ -0,0 +1,45 @@
+# PR 11220. Make sure we can store large strings in arrays.
+
+if {![installtest_p]} { untested $test; return }
+
+set test "ARRAY_STRING"
+
+set test_script {
+ global str
+ global str_array[1]
+
+ probe begin {
+ str = " 0:123456789+123456789+123456789+123456789+123456789+123456789 1:123456789+123456789+123456789+123456789+123456789+123456789 2:123456789+123456789+123456789+123456789+123456789+123456789 3:123456789+123456789+123456789+123456789+123456789+123456789 4:123456789+123456789+123456789+123456789+123456789+123456789 5:123456789+123456789+123456789+123456789+123456789+123456789 6:123456789+123456789+123456789+123456789+123456789+123456789 7:123456789+123456789+123456789+123456789+123456789+123456789"
+ str_array[0] = str
+ printf("systemtap starting probe\n")
+ }
+
+ probe end {
+ printf("systemtap ending probe\n")
+ if (strlen(str) < 500) {
+ printf("string str is too short: %d\n", strlen(str))
+ printf("%s\n", str)
+ }
+ if (strlen(str) == strlen(str_array[0])) {
+ printf("string lengths match\n")
+ }
+ else {
+ printf("string lengths *don't* match\n")
+ printf("str: %d\n", strlen(str))
+ printf("str_array[0]: %d\n", strlen(str_array[0]))
+ }
+ if (str_array[0] == str) {
+ printf("strings match\n")
+ }
+ else {
+ printf("strings *don't* match!\n")
+ printf("str: %s\n", str)
+ printf("str_array[0]: %s\n", str_array[0])
+ }
+ }
+}
+
+set output "string lengths match\r\nstrings match\r\n"
+
+# Set MAXSTRINGLEN to 512 to be sure we're testing what we intend.
+stap_run $test no_load $output -DMAXSTRINGLEN=512 -e $test_script
diff --git a/testsuite/systemtap.base/global_end.exp b/testsuite/systemtap.base/global_end.exp
index d245212f..9d9c8d9b 100644
--- a/testsuite/systemtap.base/global_end.exp
+++ b/testsuite/systemtap.base/global_end.exp
@@ -19,6 +19,7 @@ expect {
-re {epsilon."one",1. @count=0x4 @min=0x1 @max=0x4 @sum=0xa @avg=0x2} { incr ok; exp_continue }
-re {epsilon."two",2. @count=0x4 @min=0xa @max=0x28 @sum=0x64 @avg=0x19} { incr ok; exp_continue }
-re {phi @count=0x4 @min=0x1 @max=0x4 @sum=0xa @avg=0x2} { incr ok; exp_continue }
+ -re {var=0x1} { incr ok; exp_continue }
timeout { fail "$test (timeout)" }
eof { }
}
@@ -32,4 +33,4 @@ expect {
-re {Avg time = [1-9]} { incr ok; exp_continue }
}
-if {$ok == 12} { pass "$test ($ok)" } { fail "$test ($ok)" }
+if {$ok == 13} { pass "$test ($ok)" } { fail "$test ($ok)" }
diff --git a/testsuite/systemtap.base/global_end.stp b/testsuite/systemtap.base/global_end.stp
index 4a5baff7..1f707d54 100644
--- a/testsuite/systemtap.base/global_end.stp
+++ b/testsuite/systemtap.base/global_end.stp
@@ -1,4 +1,4 @@
-global alpha, beta, gamma, iota, epsilon, phi
+global alpha, beta, gamma, iota, epsilon, phi, var
probe begin {
gamma = "abcdefghijklmnopqrstuvwxyz"
@@ -29,6 +29,8 @@ probe begin {
epsilon["two",2] <<< 20
epsilon["two",2] <<< 30
epsilon["two",2] <<< 40
+
+ var++
}
probe timer.ms(100) {
diff --git a/testsuite/systemtap.base/overflow-get_argv.exp b/testsuite/systemtap.base/overflow-get_argv.exp
new file mode 100644
index 00000000..ac7fddc5
--- /dev/null
+++ b/testsuite/systemtap.base/overflow-get_argv.exp
@@ -0,0 +1,5 @@
+# PR11234: __get_argv can overflow its return buffer
+
+set test "overflow-get_argv"
+
+stap_run $srcdir/$subdir/$test.stp no_load $all_pass_string -g -c "/bin/true /usr/bin/*"
diff --git a/testsuite/systemtap.base/overflow-get_argv.stp b/testsuite/systemtap.base/overflow-get_argv.stp
new file mode 100644
index 00000000..159ef4a8
--- /dev/null
+++ b/testsuite/systemtap.base/overflow-get_argv.stp
@@ -0,0 +1,62 @@
+// PR11234: __get_argv can overflow its return buffer
+
+// __get_argv has a signature like this:
+// struct function___get_argv_locals {
+// int64_t a;
+// int64_t first;
+// string_t __retvalue;
+// } function___get_argv;
+//
+// These functions are meant to have an overlap such that we can tell if
+// __get_argv overran its __retvalue.
+//
+// int64_t x;
+// int64_t y;
+// string_t z;
+// string_t __retvalue;
+//
+// NB: __retvalue[0] always gets cleared on call, but the rest should be
+// untouched, so we can use it as a sentinal.
+
+function clear:string(x:long, y:long, z:string) %{
+ memset(THIS->__retvalue, 0, MAXSTRINGLEN);
+%}
+
+function check:string(x:long, y:long, z:string) %{
+ int i, bad = 0;
+ for (i=1; i<MAXSTRINGLEN; ++i)
+ if (THIS->__retvalue[i])
+ ++bad;
+
+ if (bad)
+ snprintf(THIS->__retvalue, MAXSTRINGLEN, "%d non-zero bytes", bad);
+ else
+ strlcpy(THIS->__retvalue, "ok", MAXSTRINGLEN);
+%}
+
+global result = "untested"
+
+probe syscall.execve {
+ if (pid() != target())
+ next
+
+ clear(0, 0, "")
+ foo = __get_argv($argv, 0)
+ result = check(0, 0, "")
+
+ // ensure that foo isn't optimized away
+ if (foo == "foo")
+ next
+}
+
+probe begin {
+ println("systemtap starting probe")
+}
+
+probe end {
+ println("systemtap ending probe")
+ if (result == "ok")
+ println("systemtap test success")
+ else
+ println("systemtap test failure: ", result)
+}
diff --git a/testsuite/systemtap.base/procfs_write.exp b/testsuite/systemtap.base/procfs_write.exp
new file mode 100644
index 00000000..5bb35532
--- /dev/null
+++ b/testsuite/systemtap.base/procfs_write.exp
@@ -0,0 +1,135 @@
+# PR 11223. Make sure we can handle large amounts of procfs write data.
+
+set test "PROCFS_WRITE"
+
+if {![installtest_p]} { untested $test; return }
+
+proc proc_write_value { test path value} {
+ if [catch {open $path WRONLY} channel] {
+ fail "$test $channel"
+ } else {
+ puts $channel $value
+ close $channel
+ pass "$test wrote $value"
+ }
+}
+
+# This test string is ~4224 bytes long (66 lines of 64 chars). The
+# procfs kernel system will use a buffer of 4K. Our string of ~4224
+# should take around 9 trips through the write function (4224 chars /
+# 512 bytes per systemtap string = ~9 trips).
+#
+# The kernel reuses the 4K buffer for the 9th trip and doesn't null
+# terminate the string. Instead, it tells consumers how many bytes
+# are present.
+#
+# We want to make sure we can handle > 4K worth of data properly.
+set test_string \
+" 0:12345678901234567890123456789012345678901234567890123456789
+ 1:12345678901234567890123456789012345678901234567890123456789
+ 2:12345678901234567890123456789012345678901234567890123456789
+ 3:12345678901234567890123456789012345678901234567890123456789
+ 4:12345678901234567890123456789012345678901234567890123456789
+ 5:12345678901234567890123456789012345678901234567890123456789
+ 6:12345678901234567890123456789012345678901234567890123456789
+ 7:12345678901234567890123456789012345678901234567890123456789
+ 8:12345678901234567890123456789012345678901234567890123456789
+ 9:12345678901234567890123456789012345678901234567890123456789
+ 10:12345678901234567890123456789012345678901234567890123456789
+ 11:12345678901234567890123456789012345678901234567890123456789
+ 12:12345678901234567890123456789012345678901234567890123456789
+ 13:12345678901234567890123456789012345678901234567890123456789
+ 14:12345678901234567890123456789012345678901234567890123456789
+ 15:12345678901234567890123456789012345678901234567890123456789
+ 16:12345678901234567890123456789012345678901234567890123456789
+ 17:12345678901234567890123456789012345678901234567890123456789
+ 18:12345678901234567890123456789012345678901234567890123456789
+ 19:12345678901234567890123456789012345678901234567890123456789
+ 20:12345678901234567890123456789012345678901234567890123456789
+ 21:12345678901234567890123456789012345678901234567890123456789
+ 22:12345678901234567890123456789012345678901234567890123456789
+ 23:12345678901234567890123456789012345678901234567890123456789
+ 24:12345678901234567890123456789012345678901234567890123456789
+ 25:12345678901234567890123456789012345678901234567890123456789
+ 26:12345678901234567890123456789012345678901234567890123456789
+ 27:12345678901234567890123456789012345678901234567890123456789
+ 28:12345678901234567890123456789012345678901234567890123456789
+ 29:12345678901234567890123456789012345678901234567890123456789
+ 30:12345678901234567890123456789012345678901234567890123456789
+ 31:12345678901234567890123456789012345678901234567890123456789
+ 32:12345678901234567890123456789012345678901234567890123456789
+ 33:12345678901234567890123456789012345678901234567890123456789
+ 34:12345678901234567890123456789012345678901234567890123456789
+ 35:12345678901234567890123456789012345678901234567890123456789
+ 36:12345678901234567890123456789012345678901234567890123456789
+ 37:12345678901234567890123456789012345678901234567890123456789
+ 38:12345678901234567890123456789012345678901234567890123456789
+ 39:12345678901234567890123456789012345678901234567890123456789
+ 40:12345678901234567890123456789012345678901234567890123456789
+ 41:12345678901234567890123456789012345678901234567890123456789
+ 42:12345678901234567890123456789012345678901234567890123456789
+ 43:12345678901234567890123456789012345678901234567890123456789
+ 44:12345678901234567890123456789012345678901234567890123456789
+ 45:12345678901234567890123456789012345678901234567890123456789
+ 46:12345678901234567890123456789012345678901234567890123456789
+ 47:12345678901234567890123456789012345678901234567890123456789
+ 48:12345678901234567890123456789012345678901234567890123456789
+ 49:12345678901234567890123456789012345678901234567890123456789
+ 50:12345678901234567890123456789012345678901234567890123456789
+ 51:12345678901234567890123456789012345678901234567890123456789
+ 52:12345678901234567890123456789012345678901234567890123456789
+ 53:12345678901234567890123456789012345678901234567890123456789
+ 54:12345678901234567890123456789012345678901234567890123456789
+ 55:12345678901234567890123456789012345678901234567890123456789
+ 56:12345678901234567890123456789012345678901234567890123456789
+ 57:12345678901234567890123456789012345678901234567890123456789
+ 58:12345678901234567890123456789012345678901234567890123456789
+ 59:12345678901234567890123456789012345678901234567890123456789
+ 60:12345678901234567890123456789012345678901234567890123456789
+ 61:12345678901234567890123456789012345678901234567890123456789
+ 62:12345678901234567890123456789012345678901234567890123456789
+ 63:12345678901234567890123456789012345678901234567890123456789
+ 64:12345678901234567890123456789012345678901234567890123456789
+ 65:12345678901234567890123456789012345678901234567890123456789
+ 66:12345678901234567890123456789012345678901234567890123456789"
+
+# Now we need the version of the above string that expect will look
+# for. So, we need to convert all '\n' to '\r\n' and add a trailing
+# '\r\n'.
+regsub -all {\n} $test_string {\r\n} test_string2
+set test_string2 "$test_string2\\r\\n"
+
+proc proc_write_test {} {
+ global test test_string
+ set path "/proc/systemtap/$test/command"
+
+ proc_write_value $test $path $test_string
+ return 0;
+}
+
+set systemtap_write_script {
+ global iteration = 0
+ global saved_value[20]
+
+ probe procfs("command").write {
+ saved_value[iteration] = $value
+ iteration++
+ }
+
+ probe begin {
+ printf("systemtap starting probe\n")
+ }
+
+ probe end {
+ printf("systemtap ending probe\n")
+ for (i = 0; i < iteration; i++) {
+ printf("%s", saved_value[i])
+ }
+ }
+}
+
+# We're forcing the MAXSTRINGLEN to be 512 chars to make sure we know
+# what we're testing.
+stap_run $test proc_write_test $test_string2 -DMAXSTRINGLEN=512 \
+ -e $systemtap_write_script -m $test
+exec /bin/rm -f ${test}.ko
diff --git a/testsuite/systemtap.examples/index.html b/testsuite/systemtap.examples/index.html
index 5b4ef659..294c6991 100644
--- a/testsuite/systemtap.examples/index.html
+++ b/testsuite/systemtap.examples/index.html
@@ -211,6 +211,9 @@ keywords: <a href="keyword-index.html#SYSCALL">SYSCALL</a> <br>
<li><a href="process/wait4time.stp">process/wait4time.stp</a> - Trace Time Spent in wait4 Syscalls<br>
keywords: <a href="keyword-index.html#SYSCALL">SYSCALL</a> <a href="keyword-index.html#WAIT4">WAIT4</a> <br>
<p>The script watches each wait4 syscall on the system. At the end of each wait4 syscall the script prints out a line with a timestamp in microseconds, the pid, the executable name in parentheses, the "wait4:" key, the duration of the wait and the PID that the wait4 was waiting for. If the waited for PID is not specified , it is "-1".</p></li>
+<li><a href="profiling/fntimes.stp">profiling/fntimes.stp</a> - Show functions taking longer than usual<br>
+keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <br>
+<p>The fntimes.stp script monitors the execution time history of a given function family (assumed non-recursive). Each time (beyond a warmup interval) is then compared to the historical maximum. If it exceeds a certain threshold (250%), a message is printed.</p></li>
<li><a href="profiling/functioncallcount.stp">profiling/functioncallcount.stp</a> - Count Times Functions Called<br>
keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <a href="keyword-index.html#FUNCTIONS">FUNCTIONS</a> <br>
<p>The functioncallcount.stp script takes one argument, a list of functions to probe. The script will run and count the number of times that each of the functions on the list is called. On exit the script will print a sorted list from most frequently to least frequently called function.</p></li>
diff --git a/testsuite/systemtap.examples/index.txt b/testsuite/systemtap.examples/index.txt
index 48494841..ebcd17a3 100644
--- a/testsuite/systemtap.examples/index.txt
+++ b/testsuite/systemtap.examples/index.txt
@@ -526,6 +526,15 @@ keywords: syscall wait4
waiting for. If the waited for PID is not specified , it is "-1".
+profiling/fntimes.stp - Show functions taking longer than usual
+keywords: profiling
+
+ The fntimes.stp script monitors the execution time history of a given
+ function family (assumed non-recursive). Each time (beyond a warmup
+ interval) is then compared to the historical maximum. If it exceeds
+ a certain threshold (250%), a message is printed.
+
+
profiling/functioncallcount.stp - Count Times Functions Called
keywords: profiling functions
diff --git a/testsuite/systemtap.examples/keyword-index.html b/testsuite/systemtap.examples/keyword-index.html
index c33c56ef..39d29031 100644
--- a/testsuite/systemtap.examples/keyword-index.html
+++ b/testsuite/systemtap.examples/keyword-index.html
@@ -333,6 +333,9 @@ keywords: <a href="keyword-index.html#IO">IO</a> <a href="keyword-index.html#PRO
<li><a href="process/pf2.stp">process/pf2.stp</a> - Profile kernel functions<br>
keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <br>
<p>The pf2.stp script sets up time-based sampling. Every five seconds it prints out a sorted list with the top ten kernel functions with samples.</p></li>
+<li><a href="profiling/fntimes.stp">profiling/fntimes.stp</a> - Show functions taking longer than usual<br>
+keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <br>
+<p>The fntimes.stp script monitors the execution time history of a given function family (assumed non-recursive). Each time (beyond a warmup interval) is then compared to the historical maximum. If it exceeds a certain threshold (250%), a message is printed.</p></li>
<li><a href="profiling/functioncallcount.stp">profiling/functioncallcount.stp</a> - Count Times Functions Called<br>
keywords: <a href="keyword-index.html#PROFILING">PROFILING</a> <a href="keyword-index.html#FUNCTIONS">FUNCTIONS</a> <br>
<p>The functioncallcount.stp script takes one argument, a list of functions to probe. The script will run and count the number of times that each of the functions on the list is called. On exit the script will print a sorted list from most frequently to least frequently called function.</p></li>
diff --git a/testsuite/systemtap.examples/keyword-index.txt b/testsuite/systemtap.examples/keyword-index.txt
index 4a9d5ccb..96ad06bd 100644
--- a/testsuite/systemtap.examples/keyword-index.txt
+++ b/testsuite/systemtap.examples/keyword-index.txt
@@ -697,6 +697,15 @@ keywords: profiling
samples.
+profiling/fntimes.stp - Show functions taking longer than usual
+keywords: profiling
+
+ The fntimes.stp script monitors the execution time history of a given
+ function family (assumed non-recursive). Each time (beyond a warmup
+ interval) is then compared to the historical maximum. If it exceeds
+ a certain threshold (250%), a message is printed.
+
+
profiling/functioncallcount.stp - Count Times Functions Called
keywords: profiling functions
diff --git a/testsuite/systemtap.examples/profiling/fntimes.meta b/testsuite/systemtap.examples/profiling/fntimes.meta
new file mode 100644
index 00000000..4b4ff1ed
--- /dev/null
+++ b/testsuite/systemtap.examples/profiling/fntimes.meta
@@ -0,0 +1,6 @@
+title: Show functions taking longer than usual
+name: fntimes.stp
+keywords: profiling
+description: The fntimes.stp script monitors the execution time history of a given function family (assumed non-recursive). Each time (beyond a warmup interval) is then compared to the historical maximum. If it exceeds a certain threshold (250%), a message is printed.
+test_check: stap -p4 fntimes.stp 'kernel.function("sys_*")'
+test_installcheck: stap fntimes.stp 'kernel.function("sys_*")' -c "sleep 7"
diff --git a/testsuite/systemtap.examples/profiling/fntimes.stp b/testsuite/systemtap.examples/profiling/fntimes.stp
new file mode 100755
index 00000000..e9daac77
--- /dev/null
+++ b/testsuite/systemtap.examples/profiling/fntimes.stp
@@ -0,0 +1,30 @@
+#! /usr/bin/stap
+
+# usage: fntimes.stp FUNCTIONPROBE
+# e.g. fntimes.stp 'module("ext4").function("*")'
+
+global mincount = 100 # training: beneath this number of hits, only collect data
+global note_percent = 250 # percent beyond maximum-so-far to generate report for
+function time() { return gettimeofday_us() } # time measurement function
+
+global times, entry
+
+probe $1.call {
+ entry[probefunc(),tid()] = time()
+}
+
+probe $1.return {
+ pf=probefunc()
+ tid=tid()
+ if ([pf,tid] in entry) { # seen function entry?
+ t = time()-entry[pf,tid] # t: elapsed time
+ delete entry[pf,tid]
+ if (@count(times[pf]) >= mincount
+ && t >= @max(times[pf]) * note_percent / 100) { # also consider @avg()
+ printf("function %s well over %s time (%d vs %d)\n",
+ pf, "maximum", t, @max(times[pf]))
+ # also consider: print_backtrace()
+ }
+ times[pf] <<< t # (increments @count, updates @max)
+ }
+}
diff --git a/testsuite/systemtap.exelib/lib.stp b/testsuite/systemtap.exelib/lib.stp
index 0151282e..3fdc6db9 100644
--- a/testsuite/systemtap.exelib/lib.stp
+++ b/testsuite/systemtap.exelib/lib.stp
@@ -6,7 +6,7 @@ probe process(@1).function("main") {
}
probe process(@1).function("main_func") {
- printf("main_func\n");
+ printf("main_func %d\n", $foo);
}
probe process(@2).function("lib_main") {
@@ -14,5 +14,5 @@ probe process(@2).function("lib_main") {
}
probe process(@2).function("lib_func") {
- printf("lib_func\n");
+ printf("lib_func %d\n", $bar);
}
diff --git a/testsuite/systemtap.exelib/lib.tcl b/testsuite/systemtap.exelib/lib.tcl
index c5b7402a..a33290b1 100644
--- a/testsuite/systemtap.exelib/lib.tcl
+++ b/testsuite/systemtap.exelib/lib.tcl
@@ -1,11 +1,11 @@
set ::result_string {main
-main_func
-main_func
-main_func
+main_func 3
+main_func 2
+main_func 1
lib_main
-lib_func
-lib_func
-lib_func}
+lib_func 3
+lib_func 2
+lib_func 1}
# Only run on make installcheck
if {! [installtest_p]} { untested "lib-$testname"; return }
diff --git a/testsuite/systemtap.exelib/libmarkunamestack.stp b/testsuite/systemtap.exelib/libmarkunamestack.stp
index 0efbae0e..5ee229df 100644
--- a/testsuite/systemtap.exelib/libmarkunamestack.stp
+++ b/testsuite/systemtap.exelib/libmarkunamestack.stp
@@ -7,7 +7,7 @@ probe process(@1).function("main") {
}
probe process(@1).function("main_func") {
- printf("main_func\n");
+ printf("main_func: %d\n", $foo);
}
probe process(@2).function("lib_main") {
@@ -15,7 +15,7 @@ probe process(@2).function("lib_main") {
}
probe process(@2).function("lib_func") {
- printf("lib_func\n");
+ printf("lib_func: %d\n", $bar);
}
#mark
diff --git a/testsuite/systemtap.exelib/libmarkunamestack.tcl b/testsuite/systemtap.exelib/libmarkunamestack.tcl
index 55dc10ee..20111b3f 100644
--- a/testsuite/systemtap.exelib/libmarkunamestack.tcl
+++ b/testsuite/systemtap.exelib/libmarkunamestack.tcl
@@ -47,9 +47,9 @@ expect {
# lib
-re {^main\r\n} {incr lib; exp_continue}
- -re {^main_func\r\n} {incr lib; exp_continue}
+ -re {^main_func: [1-3]\r\n} {incr lib; exp_continue}
-re {^lib_main\r\n} {incr lib; exp_continue}
- -re {^lib_func\r\n} {incr lib; exp_continue}
+ -re {^lib_func: [1-3]\r\n} {incr lib; exp_continue}
# mark
-re {^main_count: [1-3]\r\n} {incr mark; exp_continue}