summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Brolley <brolley@redhat.com>2009-04-21 11:11:21 -0400
committerDave Brolley <brolley@redhat.com>2009-04-21 11:11:21 -0400
commit623a41aeb47995f6b5790e38f9e0e10959f98b4e (patch)
treee79bd792bfc682f842f804af1c119a751ae18c0c
parent09fd19d66b9e3318e9e33f604eb2dbe623955123 (diff)
parentd0ea46ceac2e72fe0b86269ea50c004711148158 (diff)
downloadsystemtap-steved-623a41aeb47995f6b5790e38f9e0e10959f98b4e.tar.gz
systemtap-steved-623a41aeb47995f6b5790e38f9e0e10959f98b4e.tar.xz
systemtap-steved-623a41aeb47995f6b5790e38f9e0e10959f98b4e.zip
Merge branch 'master' of git://sources.redhat.com/git/systemtap
-rw-r--r--NEWS6
-rw-r--r--buildrun.cxx57
-rw-r--r--buildrun.h2
-rw-r--r--cache.cxx18
-rw-r--r--elaborate.cxx1
-rw-r--r--grapher/GraphWidget.cxx8
-rw-r--r--hash.cxx20
-rw-r--r--hash.h2
-rw-r--r--runtime/syscall.h143
-rw-r--r--runtime/task_finder.c3
-rw-r--r--runtime/uprobes/uprobes_i386.c13
-rw-r--r--runtime/uprobes/uprobes_x86.c13
-rw-r--r--runtime/uprobes2/uprobes_x86.h13
-rw-r--r--session.h2
-rw-r--r--stapprobes.3stap.in35
-rw-r--r--tapsets.cxx808
-rwxr-xr-xtestsuite/semok/cast.stp4
-rw-r--r--testsuite/systemtap.base/bz10078.c22
-rw-r--r--testsuite/systemtap.base/bz10078.exp35
-rw-r--r--testsuite/systemtap.base/bz10078.stp4
-rw-r--r--testsuite/systemtap.base/cast.exp6
-rw-r--r--testsuite/systemtap.base/cast.stp21
-rw-r--r--testsuite/systemtap.base/kprobes.exp2
-rw-r--r--testsuite/systemtap.base/kprobes.stp21
-rw-r--r--testsuite/systemtap.base/utrace_syscall_args.stp40
-rw-r--r--testsuite/systemtap.examples/general/grapher.stp10
-rwxr-xr-xtestsuite/systemtap.examples/profiling/latencytap.stp2
-rw-r--r--testsuite/systemtap.printf/basic6.exp3
-rw-r--r--testsuite/systemtap.printf/basic6.stp5
-rw-r--r--translate.cxx3
30 files changed, 1263 insertions, 59 deletions
diff --git a/NEWS b/NEWS
index 37a424d8..2a713ba6 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,12 @@
Such accesses can originate from $context expressions fueled by
erroneous debug data, or by kernel_{long,string,...}() tapset calls.
+- New probes kprobe.function(FUNCTION) and kprobe.function(FUNCTION).return
+ for dwarfless probing. These postpone function address resolution to
+ run-time and use the kprobe symbol-resolution mechanism.
+ Probing of absolute statements can be done using the
+ kprobe.statement(ADDRESS).absolute construct.
+
* What's new in version 0.9.5
- New probes process().insn and process().insn.block that allows
diff --git a/buildrun.cxx b/buildrun.cxx
index 5055f7fe..3814013f 100644
--- a/buildrun.cxx
+++ b/buildrun.cxx
@@ -454,4 +454,61 @@ make_tracequery(systemtap_session& s, string& name, const vector<string>& extra_
return run_make_cmd(s, make_cmd);
}
+
+// Build a tiny kernel module to query type information
+int
+make_typequery_kmod(systemtap_session& s, const string& header, string& name)
+{
+ static unsigned tick = 0;
+ string basename("typequery_kmod_" + lex_cast<string>(++tick));
+
+ // create a subdirectory for the module
+ string dir(s.tmpdir + "/" + basename);
+ if (create_dir(dir.c_str()) != 0)
+ {
+ if (! s.suppress_warnings)
+ cerr << "Warning: failed to create directory for querying types." << endl;
+ return 1;
+ }
+
+ name = dir + "/" + basename + ".ko";
+
+ // create a simple Makefile
+ string makefile(dir + "/Makefile");
+ ofstream omf(makefile.c_str());
+ omf << "EXTRA_CFLAGS := -g -fno-eliminate-unused-debug-types" << endl;
+ omf << "CFLAGS_" << basename << ".o := -include " << header << endl;
+ omf << "obj-m := " + basename + ".o" << endl;
+ omf.close();
+
+ // create our empty source file
+ string source(dir + "/" + basename + ".c");
+ ofstream osrc(source.c_str());
+ osrc.close();
+
+ // make the module
+ string make_cmd = "make -C '" + s.kernel_build_tree + "'"
+ + " M='" + dir + "' modules";
+ if (s.verbose < 4)
+ make_cmd += " >/dev/null 2>&1";
+ return run_make_cmd(s, make_cmd);
+}
+
+
+// Build a tiny user module to query type information
+int
+make_typequery_umod(systemtap_session& s, const string& header, string& name)
+{
+ static unsigned tick = 0;
+
+ name = s.tmpdir + "/typequery_umod_" + lex_cast<string>(++tick) + ".so";
+
+ // make the module
+ string cmd = "gcc -shared -g -fno-eliminate-unused-debug-types -o "
+ + name + " -xc /dev/null -include " + header;
+ if (s.verbose < 4)
+ cmd += " >/dev/null 2>&1";
+ return stap_system (cmd.c_str());
+}
+
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/buildrun.h b/buildrun.h
index e87b7b85..fb33b4c8 100644
--- a/buildrun.h
+++ b/buildrun.h
@@ -15,6 +15,8 @@ int compile_pass (systemtap_session& s);
int run_pass (systemtap_session& s);
int make_tracequery(systemtap_session& s, std::string& name, const std::vector<std::string>& extra_headers);
+int make_typequery_kmod(systemtap_session& s, const std::string& header, std::string& name);
+int make_typequery_umod(systemtap_session& s, const std::string& header, std::string& name);
#endif // BUILDRUN_H
diff --git a/cache.cxx b/cache.cxx
index 1e4d7f18..766600a8 100644
--- a/cache.cxx
+++ b/cache.cxx
@@ -310,14 +310,28 @@ clean_cache(systemtap_session& s)
globfree(&cache_glob);
+ //grab info for each typequery user module (.so)
+ glob_str = s.cache_path + "/*/*.so";
+ glob(glob_str.c_str(), 0, NULL, &cache_glob);
+ for (unsigned int i = 0; i < cache_glob.gl_pathc; i++)
+ {
+ string cache_ent_path = cache_glob.gl_pathv[i];
+ struct cache_ent_info cur_info(cache_ent_path, false);
+ if (cur_info.size != 0 && cur_info.weight != 0)
+ {
+ cache_size_b += cur_info.size;
+ cache_contents.insert(cur_info);
+ }
+ }
+
+ globfree(&cache_glob);
+
//grab info for each stapconf cache entry (.h)
glob_str = s.cache_path + "/*/*.h";
glob(glob_str.c_str(), 0, NULL, &cache_glob);
for (unsigned int i = 0; i < cache_glob.gl_pathc; i++)
{
string cache_ent_path = cache_glob.gl_pathv[i];
- cache_ent_path.resize(cache_ent_path.length() - 3);
-
struct cache_ent_info cur_info(cache_ent_path, false);
if (cur_info.size != 0 && cur_info.weight != 0)
{
diff --git a/elaborate.cxx b/elaborate.cxx
index 7bece6d4..b5d6046b 100644
--- a/elaborate.cxx
+++ b/elaborate.cxx
@@ -1460,6 +1460,7 @@ systemtap_session::systemtap_session ():
user_file (0),
be_derived_probes(0),
dwarf_derived_probes(0),
+ kprobe_derived_probes(0),
uprobe_derived_probes(0),
utrace_derived_probes(0),
itrace_derived_probes(0),
diff --git a/grapher/GraphWidget.cxx b/grapher/GraphWidget.cxx
index 5186c471..a82a8bb6 100644
--- a/grapher/GraphWidget.cxx
+++ b/grapher/GraphWidget.cxx
@@ -85,8 +85,8 @@ namespace systemtap
}
if (_autoScaling)
{
- // line separation
- int linesPossible = width / (_lineWidth + 2);
+ // line separation
+ int linesPossible = width / ((int)_lineWidth + 2);
// Find latest time.
double latestTime = 0;
for (DatasetList::iterator ditr = _datasets.begin(),
@@ -223,9 +223,9 @@ namespace systemtap
cr->move_to(20.0, height);
cr->line_to(graphWidth, height);
cr->stroke();
- std::vector<double> dash(1);
+ std::valarray<double> dash(1);
dash[0] = height / 10;
- cr->set_dash(dash, 0);
+ cr->set_dash(dash, 0.0);
for (double tickVal = startTime; tickVal < _right; tickVal += majorUnit)
{
cr->move_to((tickVal - _left) * horizScale + 20.0, graphHeight - 5);
diff --git a/hash.cxx b/hash.cxx
index 01013c43..45ae05eb 100644
--- a/hash.cxx
+++ b/hash.cxx
@@ -273,4 +273,24 @@ find_tracequery_hash (systemtap_session& s)
s.tracequery_path = hashdir + "/tracequery_" + result + ".ko";
}
+
+void
+find_typequery_hash (systemtap_session& s, const string& name, string& module)
+{
+ hash h;
+ get_base_hash(s, h);
+
+ // Add the typequery name to distinguish the hash
+ h.add(name);
+
+ // Get the directory path to store our cached module
+ string result, hashdir;
+ h.result(result);
+ if (!create_hashdir(s, result, hashdir))
+ return;
+
+ module = hashdir + "/typequery_" + result
+ + (name[0] == 'k' ? ".ko" : ".so");
+}
+
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/hash.h b/hash.h
index bb3d5ae1..7e432216 100644
--- a/hash.h
+++ b/hash.h
@@ -37,5 +37,7 @@ public:
void find_hash (systemtap_session& s, const std::string& script);
void find_tracequery_hash (systemtap_session& s);
+void find_typequery_hash (systemtap_session& s, const std::string& name,
+ std::string& module);
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/runtime/syscall.h b/runtime/syscall.h
index 5e538389..ffc21efc 100644
--- a/runtime/syscall.h
+++ b/runtime/syscall.h
@@ -124,6 +124,14 @@ syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
static inline long
syscall_get_nr(struct task_struct *task, struct pt_regs *regs)
{
+ if ((long)regs->cr_ifs < 0) /* Not a syscall */
+ return -1;
+
+#ifdef CONFIG_IA32_SUPPORT
+ if (IS_IA32_PROCESS(regs))
+ return regs->r1;
+#endif
+
return regs->r15;
}
#endif
@@ -320,38 +328,119 @@ syscall_get_arguments(struct task_struct *task, struct pt_regs *regs,
#endif
#if defined(__ia64__)
-#define syscall_get_arguments(task, regs, i, n, args) \
- __ia64_syscall_get_arguments(task, regs, i, n, args, &c->unwaddr)
-static inline void
-__ia64_syscall_get_arguments(struct task_struct *task, struct pt_regs *regs,
- unsigned int i, unsigned int n,
- unsigned long *args, unsigned long **cache)
+/* Return TRUE if PT was created due to kernel-entry via a system-call. */
+
+static inline int
+in_syscall (struct pt_regs *pt)
{
- if (i + n > 6) {
- _stp_error("invalid syscall arg request");
+ return (long) pt->cr_ifs >= 0;
+}
+
+struct syscall_get_set_args {
+ unsigned int i;
+ unsigned int n;
+ unsigned long *args;
+ struct pt_regs *regs;
+ int rw;
+};
+
+static void syscall_get_set_args_cb(struct unw_frame_info *info, void *data)
+{
+ struct syscall_get_set_args *args = data;
+ struct pt_regs *pt = args->regs;
+ unsigned long *krbs, cfm, ndirty;
+ int i, count;
+
+ if (unw_unwind_to_user(info) < 0)
return;
+
+ cfm = pt->cr_ifs;
+ krbs = (unsigned long *)info->task + IA64_RBS_OFFSET/8;
+ ndirty = ia64_rse_num_regs(krbs, krbs + (pt->loadrs >> 19));
+
+ count = 0;
+ if (in_syscall(pt))
+ count = min_t(int, args->n, cfm & 0x7f);
+
+ for (i = 0; i < count; i++) {
+ if (args->rw)
+ *ia64_rse_skip_regs(krbs, ndirty + i + args->i) =
+ args->args[i];
+ else
+ args->args[i] = *ia64_rse_skip_regs(krbs,
+ ndirty + i + args->i);
}
- switch (i) {
- case 0:
- if (!n--) break;
- *args++ = ia64_fetch_register(32, regs, cache);
- case 1:
- if (!n--) break;
- *args++ = ia64_fetch_register(33, regs, cache);
- case 2:
- if (!n--) break;
- *args++ = ia64_fetch_register(34, regs, cache);
- case 3:
- if (!n--) break;
- *args++ = ia64_fetch_register(35, regs, cache);
- case 4:
- if (!n--) break;
- *args++ = ia64_fetch_register(36, regs, cache);
- case 5:
- if (!n--) break;
- *args++ = ia64_fetch_register(37, regs, cache);
+
+ if (!args->rw) {
+ while (i < args->n) {
+ args->args[i] = 0;
+ i++;
+ }
+ }
+}
+
+void ia64_syscall_get_set_arguments(struct task_struct *task,
+ struct pt_regs *regs, unsigned int i, unsigned int n,
+ unsigned long *args, int rw)
+{
+ struct syscall_get_set_args data = {
+ .i = i,
+ .n = n,
+ .args = args,
+ .regs = regs,
+ .rw = rw,
+ };
+
+ if (task == current)
+ unw_init_running(syscall_get_set_args_cb, &data);
+ else {
+ struct unw_frame_info ufi;
+ memset(&ufi, 0, sizeof(ufi));
+ unw_init_from_blocked_task(&ufi, task);
+ syscall_get_set_args_cb(&ufi, &data);
+ }
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ unsigned long *args)
+{
+ BUG_ON(i + n > 6);
+
+#ifdef CONFIG_IA32_SUPPORT
+ if (IS_IA32_PROCESS(regs)) {
+ switch (i + n) {
+ case 6:
+ if (!n--) break;
+ *args++ = regs->r13;
+ case 5:
+ if (!n--) break;
+ *args++ = regs->r15;
+ case 4:
+ if (!n--) break;
+ *args++ = regs->r14;
+ case 3:
+ if (!n--) break;
+ *args++ = regs->r10;
+ case 2:
+ if (!n--) break;
+ *args++ = regs->r9;
+ case 1:
+ if (!n--) break;
+ *args++ = regs->r11;
+ case 0:
+ if (!n--) break;
+ default:
+ BUG();
+ break;
+ }
+
+ return;
}
+#endif
+ ia64_syscall_get_set_arguments(task, regs, i, n, args, 0);
}
#endif
diff --git a/runtime/task_finder.c b/runtime/task_finder.c
index fa6c296e..93b89cb9 100644
--- a/runtime/task_finder.c
+++ b/runtime/task_finder.c
@@ -1071,9 +1071,6 @@ __stp_utrace_task_finder_target_syscall_exit(enum utrace_resume_action action,
int rc;
struct mm_struct *mm;
struct vm_area_struct *vma;
-#if defined(__ia64__)
- struct { unsigned long *unwaddr; } _c = {.unwaddr = NULL}, *c = &_c;
-#endif
if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) {
debug_task_finder_detach();
diff --git a/runtime/uprobes/uprobes_i386.c b/runtime/uprobes/uprobes_i386.c
index c43f87bf..7743f400 100644
--- a/runtime/uprobes/uprobes_i386.c
+++ b/runtime/uprobes/uprobes_i386.c
@@ -301,9 +301,20 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address,
return orig_ret_addr;
}
+/*
+ * On x86_32, if a function returns a struct or union, the return
+ * value is copied into an area created by the caller. The address
+ * of this area is passed on the stack as a "hidden" first argument.
+ * When such a function returns, it uses a "ret $4" instruction to pop
+ * not only the return address but also the hidden arg. To accommodate
+ * such functions, we add 4 bytes of slop when predicting the return
+ * address. See PR #10078.
+ */
+#define STRUCT_RETURN_SLOP 4
+
static
unsigned long arch_predict_sp_at_ret(struct pt_regs *regs,
struct task_struct *tsk)
{
- return (unsigned long) (regs->esp + 4);
+ return (unsigned long) (regs->esp + 4 + STRUCT_RETURN_SLOP);
}
diff --git a/runtime/uprobes/uprobes_x86.c b/runtime/uprobes/uprobes_x86.c
index 404c9518..93331715 100644
--- a/runtime/uprobes/uprobes_x86.c
+++ b/runtime/uprobes/uprobes_x86.c
@@ -716,12 +716,23 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address,
return orig_ret_addr;
}
+/*
+ * On x86_32, if a function returns a struct or union, the return
+ * value is copied into an area created by the caller. The address
+ * of this area is passed on the stack as a "hidden" first argument.
+ * When such a function returns, it uses a "ret $4" instruction to pop
+ * not only the return address but also the hidden arg. To accommodate
+ * such functions, we add 4 bytes of slop when predicting the return
+ * address. See PR #10078.
+ */
+#define STRUCT_RETURN_SLOP 4
+
static
unsigned long arch_predict_sp_at_ret(struct pt_regs *regs,
struct task_struct *tsk)
{
if (test_tsk_thread_flag(tsk, TIF_IA32))
- return (unsigned long) (REGS_SP + 4);
+ return (unsigned long) (REGS_SP + 4 + STRUCT_RETURN_SLOP);
else
return (unsigned long) (REGS_SP + 8);
}
diff --git a/runtime/uprobes2/uprobes_x86.h b/runtime/uprobes2/uprobes_x86.h
index ca3f4873..a07fa0d3 100644
--- a/runtime/uprobes2/uprobes_x86.h
+++ b/runtime/uprobes2/uprobes_x86.h
@@ -93,11 +93,22 @@ static inline unsigned long arch_get_cur_sp(struct pt_regs *regs)
return (unsigned long) regs->sp;
}
+/*
+ * On x86_32, if a function returns a struct or union, the return
+ * value is copied into an area created by the caller. The address
+ * of this area is passed on the stack as a "hidden" first argument.
+ * When such a function returns, it uses a "ret $4" instruction to pop
+ * not only the return address but also the hidden arg. To accommodate
+ * such functions, we add 4 bytes of slop when predicting the return
+ * address. See PR #10078.
+ */
+#define STRUCT_RETURN_SLOP 4
+
static inline unsigned long arch_predict_sp_at_ret(struct pt_regs *regs,
struct task_struct *tsk)
{
if (test_tsk_thread_flag(tsk, TIF_IA32))
- return (unsigned long) (regs->sp + 4);
+ return (unsigned long) (regs->sp + 4 + STRUCT_RETURN_SLOP);
else
return (unsigned long) (regs->sp + 8);
}
diff --git a/session.h b/session.h
index 0b687090..9ffafdcd 100644
--- a/session.h
+++ b/session.h
@@ -30,6 +30,7 @@ struct functiondecl;
struct derived_probe;
struct be_derived_probe_group;
struct dwarf_derived_probe_group;
+struct kprobe_derived_probe_group;
struct uprobe_derived_probe_group;
struct utrace_derived_probe_group;
struct itrace_derived_probe_group;
@@ -172,6 +173,7 @@ struct systemtap_session
// session.probes vector.
be_derived_probe_group* be_derived_probes;
dwarf_derived_probe_group* dwarf_derived_probes;
+ kprobe_derived_probe_group* kprobe_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/stapprobes.3stap.in b/stapprobes.3stap.in
index a8988c71..e60a8fe4 100644
--- a/stapprobes.3stap.in
+++ b/stapprobes.3stap.in
@@ -64,6 +64,7 @@ syscall.*
kernel.function("no_such_function") ?
module("awol").function("no_such_function") !
signal.*? if (switch)
+kprobe.function("foo")
.ESAMPLE
@@ -378,6 +379,40 @@ Other local variables are not generally accessible, since by the time
a ".return" probe hits, the probed function will have already returned.
+.SS DWARFLESS
+In absence of debugging information, entry & exit points of kernel & module
+functions can be probed using the "kprobe" family of probes.
+However, these do not permit looking up the arguments / local variables
+of the function.
+Following constructs are supported :
+.SAMPLE
+kprobe.function(FUNCTION)
+kprobe.function(FUNCTION).return
+kprobe.module(NAME).function(FUNCTION)
+kprobe.module(NAME).function(FUNCTION).return
+kprobe.statement.(ADDRESS).absolute
+.ESAMPLE
+.PP
+Probes of type
+.B function
+are recommended for kernel functions, whereas probes of type
+.B module
+are recommended for probing functions of the specified module.
+In case the absolute address of a kernel or module function is known,
+.B statement
+probes can be utilized.
+.PP
+Note that
+.I FUNCTION
+and
+.I MODULE
+names
+.B must not
+contain wildcards, or the probe will not be registered.
+Also, statement probes must be run under guru-mode only.
+
+
+
.SS USER-SPACE
Early prototype support for user-space probing is available in the
form of a non-symbolic probe point:
diff --git a/tapsets.cxx b/tapsets.cxx
index 01c838d9..f99fbef4 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -2619,7 +2619,38 @@ struct uprobe_derived_probe: public derived_probe
void join_group (systemtap_session& s);
};
+struct kprobe_derived_probe: public derived_probe
+{
+ kprobe_derived_probe (probe *base,
+ probe_point *location,
+ const string& name,
+ int64_t stmt_addr,
+ bool has_return,
+ bool has_statement
+ );
+ string symbol_name;
+ Dwarf_Addr addr;
+ bool has_return;
+ bool has_statement;
+ bool has_maxactive;
+ long maxactive_val;
+ bool access_var;
+ void printsig (std::ostream &o) const;
+ void join_group (systemtap_session& s);
+};
+struct kprobe_derived_probe_group: public derived_probe_group
+{
+private:
+ multimap<string,kprobe_derived_probe*> probes_by_module;
+ typedef multimap<string,kprobe_derived_probe*>::iterator p_b_m_iterator;
+
+public:
+ void enroll (kprobe_derived_probe* probe);
+ void emit_module_decls (systemtap_session& s);
+ void emit_module_init (systemtap_session& s);
+ void emit_module_exit (systemtap_session& s);
+};
struct dwarf_derived_probe_group: public derived_probe_group
{
@@ -4449,7 +4480,6 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor
};
-
unsigned var_expanding_visitor::tick = 0;
void
@@ -5104,9 +5134,64 @@ struct dwarf_cast_expanding_visitor: public var_expanding_visitor
dwarf_cast_expanding_visitor(systemtap_session& s, dwarf_builder& db):
s(s), db(db) {}
void visit_cast_op (cast_op* e);
+ void filter_special_modules(string& module);
};
+void dwarf_cast_expanding_visitor::filter_special_modules(string& module)
+{
+ // look for "kmod<path/to/header>" or "umod<path/to/header>"
+ // for those cases, build a module including that header
+ if (module.rfind('>') == module.size() - 1 &&
+ (module.compare(0, 5, "kmod<") == 0 ||
+ module.compare(0, 5, "umod<") == 0))
+ {
+ string cached_module;
+ if (s.use_cache)
+ {
+ // see if the cached module exists
+ find_typequery_hash(s, module, cached_module);
+ if (!cached_module.empty())
+ {
+ int fd = open(cached_module.c_str(), O_RDONLY);
+ if (fd != -1)
+ {
+ if (s.verbose > 2)
+ clog << "Pass 2: using cached " << cached_module << endl;
+ module = cached_module;
+ close(fd);
+ return;
+ }
+ }
+ }
+
+ // no cached module, time to make it
+ int rc;
+ string new_module, header = module.substr(5, module.size() - 6);
+ if (module[0] == 'k')
+ rc = make_typequery_kmod(s, header, new_module);
+ else
+ rc = make_typequery_umod(s, header, new_module);
+ if (rc == 0)
+ {
+ module = new_module;
+
+ if (s.use_cache)
+ {
+ // try to save typequery in the cache
+ if (s.verbose > 2)
+ clog << "Copying " << new_module
+ << " to " << cached_module << endl;
+ if (copy_file(new_module.c_str(),
+ cached_module.c_str()) != 0)
+ cerr << "Copy failed (\"" << new_module << "\" to \""
+ << cached_module << "\"): " << strerror(errno) << endl;
+ }
+ }
+ }
+}
+
+
void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
{
bool lvalue = is_active_lvalue(e);
@@ -5125,6 +5210,7 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
size_t mod_begin = mod_end + 1;
mod_end = e->module.find(':', mod_begin);
string module = e->module.substr(mod_begin, mod_end - mod_begin);
+ filter_special_modules(module);
// NB: This uses '/' to distinguish between kernel modules and userspace,
// which means that userspace modules won't get any PATH searching.
@@ -5134,10 +5220,21 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
// kernel or kernel module target
if (! db.kern_dw)
{
- db.kern_dw = new dwflpp(s);
- db.kern_dw->setup_kernel(true);
+ dw = new dwflpp(s);
+ try
+ {
+ dw->setup_kernel(true);
+ }
+ catch (const semantic_error& er)
+ {
+ /* ignore and go to the next module */
+ delete dw;
+ continue;
+ }
+ db.kern_dw = dw;
}
- dw = db.kern_dw;
+ else
+ dw = db.kern_dw;
}
else
{
@@ -5148,7 +5245,16 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
if (db.user_dw.find(module) == db.user_dw.end())
{
dw = new dwflpp(s);
- dw->setup_user(module);
+ try
+ {
+ dw->setup_user(module);
+ }
+ catch (const semantic_error& er)
+ {
+ /* ignore and go to the next module */
+ delete dw;
+ continue;
+ }
db.user_dw[module] = dw;
}
else
@@ -5468,6 +5574,23 @@ dwarf_derived_probe_group::enroll (dwarf_derived_probe* p)
// sequentially.
}
+/*
+void
+dwarf_derived_probe_group::enroll (kprobe_derived_probe* p)
+{
+ dwarf_derived_probe *dw_probe = new dwarf_derived_probe (p->symbol_name,
+ "",0,
+ p->module_name,
+ p->section_name,
+ 0,0,
+ p->q,NULL);
+ probes_by_module.insert (make_pair (p->module, p));
+
+ // XXX: probes put at the same address should all share a
+ // single kprobe/kretprobe, and have their handlers executed
+ // sequentially.
+}
+*/
void
dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
@@ -5550,6 +5673,7 @@ dwarf_derived_probe_group::emit_module_decls (systemtap_session& s)
CALCIT(module);
CALCIT(section);
CALCIT(pp);
+#undef CALCIT
s.op->newline() << "const unsigned long address;";
s.op->newline() << "void (* const ph) (struct context*);";
@@ -6319,7 +6443,6 @@ emit_vma_callback_probe_decl (systemtap_session& s,
s.op->line() << " },";
}
-
// ------------------------------------------------------------------------
// task_finder derived 'probes': These don't really exist. The whole
// purpose of the task_finder_derived_probe_group is to make sure that
@@ -6728,17 +6851,24 @@ public:
struct utrace_var_expanding_visitor: public var_expanding_visitor
{
- utrace_var_expanding_visitor(systemtap_session& s, const string& pn,
+ utrace_var_expanding_visitor(systemtap_session& s, probe_point* l,
+ const string& pn,
enum utrace_derived_probe_flags f):
- sess (s), probe_name (pn), flags (f), target_symbol_seen (false) {}
+ sess (s), base_loc (l), probe_name (pn), flags (f),
+ target_symbol_seen (false), add_block(NULL), add_probe(NULL) {}
systemtap_session& sess;
+ probe_point* base_loc;
string probe_name;
enum utrace_derived_probe_flags flags;
bool target_symbol_seen;
+ block *add_block;
+ probe *add_probe;
+ std::map<std::string, symbol *> return_ts_map;
void visit_target_symbol_arg (target_symbol* e);
void visit_target_symbol_context (target_symbol* e);
+ void visit_target_symbol_cached (target_symbol* e);
void visit_target_symbol (target_symbol* e);
};
@@ -6753,10 +6883,23 @@ utrace_derived_probe::utrace_derived_probe (systemtap_session &s,
target_symbol_seen(false)
{
// Expand local variables in the probe body
- utrace_var_expanding_visitor v (s, name, flags);
+ utrace_var_expanding_visitor v (s, l, name, flags);
this->body = v.require (this->body);
target_symbol_seen = v.target_symbol_seen;
+ // If during target-variable-expanding the probe, we added a new block
+ // of code, add it to the start of the probe.
+ if (v.add_block)
+ this->body = new block(v.add_block, this->body);
+ // If when target-variable-expanding the probe, we added a new
+ // probe, add it in a new file to the list of files to be processed.
+ if (v.add_probe)
+ {
+ stapfile *f = new stapfile;
+ f->probes.push_back(v.add_probe);
+ s.files.push_back(f);
+ }
+
// Reset the sole element of the "locations" vector as a
// "reverse-engineered" form of the incoming (q.base_loc) probe
// point. This allows a user to see what program etc.
@@ -6816,6 +6959,216 @@ utrace_derived_probe::join_group (systemtap_session& s)
void
+utrace_var_expanding_visitor::visit_target_symbol_cached (target_symbol* e)
+{
+ // Get the full name of the target symbol.
+ stringstream ts_name_stream;
+ e->print(ts_name_stream);
+ string ts_name = ts_name_stream.str();
+
+ // Check and make sure we haven't already seen this target
+ // variable in this return probe. If we have, just return our
+ // last replacement.
+ map<string, symbol *>::iterator i = return_ts_map.find(ts_name);
+ if (i != return_ts_map.end())
+ {
+ provide (i->second);
+ return;
+ }
+
+ // We've got to do several things here to handle target
+ // variables in return probes.
+
+ // (1) Synthesize a global array which is the cache of the
+ // target variable value. We don't need a nesting level counter
+ // like the dwarf_var_expanding_visitor::visit_target_symbol()
+ // does since a particular thread can only be in one system
+ // calls at a time. The array will look like this:
+ //
+ // _utrace_tvar_{name}_{num}
+ string aname = (string("_utrace_tvar_")
+ + e->base_name.substr(1)
+ + "_" + lex_cast<string>(tick++));
+ vardecl* vd = new vardecl;
+ vd->name = aname;
+ vd->tok = e->tok;
+ sess.globals.push_back (vd);
+
+ // (2) Create a new code block we're going to insert at the
+ // beginning of this probe to get the cached value into a
+ // temporary variable. We'll replace the target variable
+ // reference with the temporary variable reference. The code
+ // will look like this:
+ //
+ // _utrace_tvar_tid = tid()
+ // _utrace_tvar_{name}_{num}_tmp
+ // = _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+ // delete _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+
+ // (2a) Synthesize the tid temporary expression, which will look
+ // like this:
+ //
+ // _utrace_tvar_tid = tid()
+ symbol* tidsym = new symbol;
+ tidsym->name = string("_utrace_tvar_tid");
+ tidsym->tok = e->tok;
+
+ if (add_block == NULL)
+ {
+ add_block = new block;
+ add_block->tok = e->tok;
+
+ // Synthesize a functioncall to grab the thread id.
+ functioncall* fc = new functioncall;
+ fc->tok = e->tok;
+ fc->function = string("tid");
+
+ // Assign the tid to '_utrace_tvar_tid'.
+ assignment* a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = tidsym;
+ a->right = fc;
+
+ expr_statement* es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+ add_block->statements.push_back (es);
+ }
+
+ // (2b) Synthesize an array reference and assign it to a
+ // temporary variable (that we'll use as replacement for the
+ // target variable reference). It will look like this:
+ //
+ // _utrace_tvar_{name}_{num}_tmp
+ // = _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+
+ arrayindex* ai_tvar = new arrayindex;
+ ai_tvar->tok = e->tok;
+
+ symbol* sym = new symbol;
+ sym->name = aname;
+ sym->tok = e->tok;
+ ai_tvar->base = sym;
+
+ ai_tvar->indexes.push_back(tidsym);
+
+ symbol* tmpsym = new symbol;
+ tmpsym->name = aname + "_tmp";
+ tmpsym->tok = e->tok;
+
+ assignment* a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = tmpsym;
+ a->right = ai_tvar;
+
+ expr_statement* es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+
+ add_block->statements.push_back (es);
+
+ // (2c) Delete the array value. It will look like this:
+ //
+ // delete _utrace_tvar_{name}_{num}[_utrace_tvar_tid]
+
+ delete_statement* ds = new delete_statement;
+ ds->tok = e->tok;
+ ds->value = ai_tvar;
+ add_block->statements.push_back (ds);
+
+ // (3) We need an entry probe that saves the value for us in the
+ // global array we created. Create the entry probe, which will
+ // look like this:
+ //
+ // probe process(PATH_OR_PID).syscall {
+ // _utrace_tvar_tid = tid()
+ // _utrace_tvar_{name}_{num}[_utrace_tvar_tid] = ${param}
+ // }
+ //
+ // Why the temporary for tid()? If we end up caching more
+ // than one target variable, we can reuse the temporary instead
+ // of calling tid() multiple times.
+
+ if (add_probe == NULL)
+ {
+ add_probe = new probe;
+ add_probe->tok = e->tok;
+
+ // We need the name of the current probe point, minus the
+ // ".return". Create a new probe point, copying all the
+ // components, stopping when we see the ".return"
+ // component.
+ probe_point* pp = new probe_point;
+ for (unsigned c = 0; c < base_loc->components.size(); c++)
+ {
+ if (base_loc->components[c]->functor == "return")
+ break;
+ else
+ pp->components.push_back(base_loc->components[c]);
+ }
+ pp->tok = e->tok;
+ pp->optional = base_loc->optional;
+ add_probe->locations.push_back(pp);
+
+ add_probe->body = new block;
+ add_probe->body->tok = e->tok;
+
+ // Synthesize a functioncall to grab the thread id.
+ functioncall* fc = new functioncall;
+ fc->tok = e->tok;
+ fc->function = string("tid");
+
+ // Assign the tid to '_utrace_tvar_tid'.
+ assignment* a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = tidsym;
+ a->right = fc;
+
+ expr_statement* es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+ add_probe->body = new block(add_probe->body, es);
+
+ vardecl* vd = new vardecl;
+ vd->tok = e->tok;
+ vd->name = tidsym->name;
+ vd->type = pe_long;
+ vd->set_arity(0);
+ add_probe->locals.push_back(vd);
+ }
+
+ // Save the value, like this:
+ //
+ // _utrace_tvar_{name}_{num}[_utrace_tvar_tid] = ${param}
+ a = new assignment;
+ a->tok = e->tok;
+ a->op = "=";
+ a->left = ai_tvar;
+ a->right = e;
+
+ es = new expr_statement;
+ es->tok = e->tok;
+ es->value = a;
+
+ add_probe->body = new block(add_probe->body, es);
+
+ // (4) Provide the '_utrace_tvar_{name}_{num}_tmp' variable to
+ // our parent so it can be used as a substitute for the target
+ // symbol.
+ provide (tmpsym);
+
+ // (5) Remember this replacement since we might be able to reuse
+ // it later if the same return probe references this target
+ // symbol again.
+ return_ts_map[ts_name] = tmpsym;
+ return;
+}
+
+
+void
utrace_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
{
string argnum_s = e->base_name.substr(4,e->base_name.length()-4);
@@ -6903,8 +7256,30 @@ utrace_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
throw semantic_error ("only \"process(PATH_OR_PID).syscall.return\" support $return.", e->tok);
fname = "_utrace_syscall_return";
}
+ else if (sname == "$syscall")
+ {
+ // If we've got a syscall entry probe, we can just call the
+ // right function.
+ if (flags == UDPF_SYSCALL) {
+ fname = "_utrace_syscall_nr";
+ }
+ // If we're in a syscal return probe, we can't really access
+ // $syscall. So, similar to what
+ // dwarf_var_expanding_visitor::visit_target_symbol() does,
+ // we'll create an syscall entry probe to cache $syscall, then
+ // we'll access the cached value in the syscall return probe.
+ else {
+ visit_target_symbol_cached (e);
+
+ // Remember that we've seen a target variable.
+ target_symbol_seen = true;
+ return;
+ }
+ }
else
- fname = "_utrace_syscall_nr";
+ {
+ throw semantic_error ("unknown target variable", e->tok);
+ }
// Remember that we've seen a target variable.
target_symbol_seen = true;
@@ -7258,7 +7633,7 @@ utrace_derived_probe_group::emit_module_decls (systemtap_session& s)
s.op->indent(1);
s.op->newline() << "switch (p->flags) {";
s.op->indent(1);
- // For death probes, go ahead and call the probe directly.
+ // For end probes, go ahead and call the probe directly.
if (flags_seen[UDPF_END])
{
s.op->newline() << "case UDPF_END:";
@@ -7952,8 +8327,402 @@ uprobe_derived_probe_group::emit_module_exit (systemtap_session& s)
s.op->newline() << "mutex_destroy (& stap_uprobes_lock);";
}
+// ------------------------------------------------------------------------
+// Kprobe derived probes
+// ------------------------------------------------------------------------
+
+static string TOK_KPROBE("kprobe");
+
+kprobe_derived_probe::kprobe_derived_probe (probe *base,
+ probe_point *location,
+ const string& name,
+ int64_t stmt_addr,
+ bool if_return,
+ bool if_statement
+):
+ derived_probe (base, location),
+ symbol_name (name), addr (stmt_addr),
+ has_return (if_return), has_statement (if_statement)
+{
+ this->tok = base->tok;
+ this->access_var = false;
+
+#ifndef USHRT_MAX
+#define USHRT_MAX 32767
+#endif
+
+ // Expansion of $target variables in the probe body produces an error during translate phase
+ vector<probe_point::component*> comps;
+
+ if (has_return)
+ comps.push_back (new probe_point::component(TOK_RETURN));
+
+ this->sole_location()->components = comps;
+}
+
+void kprobe_derived_probe::printsig (ostream& o) const
+{
+ sole_location()->print (o);
+ o << " /* " << " name = " << symbol_name << "*/";
+ printsig_nested (o);
+}
+
+void kprobe_derived_probe::join_group (systemtap_session& s)
+{
+
+ if (! s.kprobe_derived_probes)
+ s.kprobe_derived_probes = new kprobe_derived_probe_group ();
+ s.kprobe_derived_probes->enroll (this);
+}
+void kprobe_derived_probe_group::enroll (kprobe_derived_probe* p)
+{
+ probes_by_module.insert (make_pair (p->symbol_name, p));
+ // probes of same symbol should share single kprobe/kretprobe
+}
+
+void
+kprobe_derived_probe_group::emit_module_decls (systemtap_session& s)
+{
+ if (probes_by_module.empty()) return;
+
+ s.op->newline() << "/* ---- kprobe-based probes ---- */";
+
+ // Warn of misconfigured kernels
+ s.op->newline() << "#if ! defined(CONFIG_KPROBES)";
+ s.op->newline() << "#error \"Need CONFIG_KPROBES!\"";
+ s.op->newline() << "#endif";
+ s.op->newline();
+
+ // Forward declare the master entry functions
+ s.op->newline() << "static int enter_kprobe_probe (struct kprobe *inst,";
+ s.op->line() << " struct pt_regs *regs);";
+ s.op->newline() << "static int enter_kretprobe_probe (struct kretprobe_instance *inst,";
+ s.op->line() << " struct pt_regs *regs);";
+
+ // Emit an array of kprobe/kretprobe pointers
+ s.op->newline() << "#if defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline() << "static void * stap_unreg_kprobes[" << probes_by_module.size() << "];";
+ s.op->newline() << "#endif";
+
+ // Emit the actual probe list.
+
+ s.op->newline() << "static struct stap_dwarfless_kprobe {";
+ s.op->newline(1) << "union { struct kprobe kp; struct kretprobe krp; } u;";
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "struct kprobe dummy;";
+ s.op->newline() << "#endif";
+ s.op->newline(-1) << "} stap_dwarfless_kprobes[" << probes_by_module.size() << "];";
+ // NB: bss!
+
+ s.op->newline() << "static struct stap_dwarfless_probe {";
+ s.op->newline(1) << "const unsigned return_p:1;";
+ s.op->newline() << "const unsigned maxactive_p:1;";
+ s.op->newline() << "const unsigned statement_p:1;";
+ s.op->newline() << "unsigned registered_p:1;";
+ s.op->newline() << "const unsigned short maxactive_val;";
+
+ // Function Names are mostly small and uniform enough to justify putting
+ // char[MAX]'s into the array instead of relocated char*'s.
+
+ size_t pp_name_max = 0, symbol_string_name_max = 0;
+ size_t pp_name_tot = 0, symbol_string_name_tot = 0;
+ for (p_b_m_iterator it = probes_by_module.begin(); it != probes_by_module.end(); it++)
+ {
+ kprobe_derived_probe* p = it->second;
+#define DOIT(var,expr) do { \
+ size_t var##_size = (expr) + 1; \
+ var##_max = max (var##_max, var##_size); \
+ var##_tot += var##_size; } while (0)
+ DOIT(pp_name, lex_cast_qstring(*p->sole_location()).size());
+ DOIT(symbol_string_name, p->symbol_name.size());
+#undef DOIT
+ }
+
+#define CALCIT(var) \
+ s.op->newline() << "const char " << #var << "[" << var##_name_max << "] ;";
+
+ CALCIT(pp);
+ CALCIT(symbol_string);
+#undef CALCIT
+
+ s.op->newline() << "const unsigned long address;";
+ s.op->newline() << "void (* const ph) (struct context*);";
+ s.op->newline(-1) << "} stap_dwarfless_probes[] = {";
+ s.op->indent(1);
+
+ for (p_b_m_iterator it = probes_by_module.begin(); it != probes_by_module.end(); it++)
+ {
+ kprobe_derived_probe* p = it->second;
+ s.op->newline() << "{";
+ if (p->has_return)
+ s.op->line() << " .return_p=1,";
+
+ if (p->has_maxactive)
+ {
+ s.op->line() << " .maxactive_p=1,";
+ assert (p->maxactive_val >= 0 && p->maxactive_val <= USHRT_MAX);
+ s.op->line() << " .maxactive_val=" << p->maxactive_val << ",";
+ }
+ if (p->has_statement)
+ {
+ s.op->line() << " .statement_p=1,";
+ s.op->line() << " .address=(unsigned long)0x" << hex << p->addr << dec << "ULL,";
+ s.op->line() << " .symbol_string=\"" << "\",";
+ }
+ else
+ {
+ s.op->line() << " .address=(unsigned long)0x" << hex << 0 << dec << "ULL,";
+ s.op->line() << " .symbol_string=\"" << p->symbol_name << "\",";
+ }
+
+ s.op->line() << " .pp=" << lex_cast_qstring (*p->sole_location()) << ",";
+ s.op->line() << " .ph=&" << p->name;
+ s.op->line() << " },";
+ }
+
+ s.op->newline(-1) << "};";
+
+ // Emit the kprobes callback function
+ s.op->newline();
+ s.op->newline() << "static int enter_kprobe_probe (struct kprobe *inst,";
+ s.op->line() << " struct pt_regs *regs) {";
+ // NB: as of PR5673, the kprobe|kretprobe union struct is in BSS
+ s.op->newline(1) << "int kprobe_idx = ((uintptr_t)inst-(uintptr_t)stap_dwarfless_kprobes)/sizeof(struct stap_dwarfless_kprobe);";
+ // Check that the index is plausible
+ s.op->newline() << "struct stap_dwarfless_probe *sdp = &stap_dwarfless_probes[";
+ s.op->line() << "((kprobe_idx >= 0 && kprobe_idx < " << probes_by_module.size() << ")?";
+ s.op->line() << "kprobe_idx:0)"; // NB: at least we avoid memory corruption
+ // XXX: it would be nice to give a more verbose error though; BUG_ON later?
+ s.op->line() << "];";
+ 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(-1) << "}";
+
+ // Same for kretprobes
+ s.op->newline();
+ s.op->newline() << "static int enter_kretprobe_probe (struct kretprobe_instance *inst,";
+ s.op->line() << " struct pt_regs *regs) {";
+ s.op->newline(1) << "struct kretprobe *krp = inst->rp;";
+
+ // NB: as of PR5673, the kprobe|kretprobe union struct is in BSS
+ s.op->newline() << "int kprobe_idx = ((uintptr_t)krp-(uintptr_t)stap_dwarfless_kprobes)/sizeof(struct stap_dwarfless_kprobe);";
+ // Check that the index is plausible
+ s.op->newline() << "struct stap_dwarfless_probe *sdp = &stap_dwarfless_probes[";
+ s.op->line() << "((kprobe_idx >= 0 && kprobe_idx < " << probes_by_module.size() << ")?";
+ s.op->line() << "kprobe_idx:0)"; // NB: at least we avoid memory corruption
+ // XXX: it would be nice to give a more verbose error though; BUG_ON later?
+ s.op->line() << "];";
+
+ common_probe_entryfn_prologue (s.op, "STAP_SESSION_RUNNING", "sdp->pp");
+ s.op->newline() << "c->regs = regs;";
+ s.op->newline() << "c->pi = inst;"; // for assisting runtime's backtrace logic
+ s.op->newline() << "(*sdp->ph) (c);";
+ common_probe_entryfn_epilogue (s.op);
+ s.op->newline() << "return 0;";
+ s.op->newline(-1) << "}";
+}
+
+
+void
+kprobe_derived_probe_group::emit_module_init (systemtap_session& s)
+{
+#define CHECK_STMT(var) \
+ s.op->newline() << "if (sdp->statement_p) {"; \
+ s.op->newline() << var << ".symbol_name = NULL;"; \
+ s.op->newline() << "} else {"; \
+ s.op->newline() << var << ".symbol_name = sdp->symbol_string;"; \
+ s.op->newline() << "}";
+
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline() << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "unsigned long relocated_addr = sdp->address;";
+ s.op->newline() << "probe_point = sdp->pp;"; // for error messages
+ s.op->newline() << "if (sdp->return_p) {";
+ s.op->newline(1) << "kp->u.krp.kp.addr = (void *) relocated_addr;";
+ CHECK_STMT("kp->u.krp.kp");
+ s.op->newline() << "if (sdp->maxactive_p) {";
+ s.op->newline(1) << "kp->u.krp.maxactive = sdp->maxactive_val;";
+ s.op->newline(-1) << "} else {";
+ s.op->newline(1) << "kp->u.krp.maxactive = max(10, 4*NR_CPUS);";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "kp->u.krp.handler = &enter_kretprobe_probe;";
+ // to ensure safeness of bspcache, always use aggr_kprobe on ia64
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "kp->dummy.pre_handler = NULL;";
+ s.op->newline() << "kp->dummy.addr = kp->u.krp.kp.addr;";
+ CHECK_STMT("kp->dummy");
+ s.op->newline() << "rc = register_kprobe (& kp->dummy);";
+ s.op->newline() << "if (rc == 0) {";
+ s.op->newline(1) << "rc = register_kretprobe (& kp->u.krp);";
+ s.op->newline() << "if (rc != 0)";
+ s.op->newline(1) << "unregister_kprobe (& kp->dummy);";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "#else";
+ s.op->newline() << "rc = register_kretprobe (& kp->u.krp);";
+ s.op->newline() << "#endif";
+ s.op->newline(-1) << "} else {";
+ // to ensure safeness of bspcache, always use aggr_kprobe on ia64
+ s.op->newline(1) << "kp->u.kp.addr = (void *) relocated_addr;";
+ CHECK_STMT("kp->u.kp");
+ s.op->newline(1) << "kp->u.kp.pre_handler = &enter_kprobe_probe;";
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "kp->dummy.addr = kp->u.kp.addr;";
+ s.op->newline() << "kp->dummy.pre_handler = NULL;";
+ CHECK_STMT("kp->dummy");
+ s.op->newline() << "rc = register_kprobe (& kp->dummy);";
+ s.op->newline() << "if (rc == 0) {";
+ s.op->newline(1) << "rc = register_kprobe (& kp->u.kp);";
+ s.op->newline() << "if (rc != 0)";
+ s.op->newline(1) << "unregister_kprobe (& kp->dummy);";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "#else";
+ s.op->newline() << "rc = register_kprobe (& kp->u.kp);";
+ s.op->newline() << "#endif";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "if (rc) {"; // PR6749: tolerate a failed register_*probe.
+ s.op->newline(1) << "sdp->registered_p = 0;";
+ s.op->newline() << "if (rc == -EINVAL)";
+ s.op->newline() << "{";
+ s.op->newline() << " _stp_error (\"Error registering kprobe,possibly an incorrect name %s OR addr = %p, rc = %d \", sdp->symbol_string, sdp->address, rc);";
+ s.op->newline() << " atomic_set (&session_state, STAP_SESSION_ERROR);";
+ s.op->newline() << " goto out;";
+ s.op->newline() << "}";
+ s.op->newline() << "else";
+ s.op->newline() << "_stp_warn (\"probe %s for %s registration error (rc %d)\", probe_point, sdp->pp, rc);";
+ s.op->newline() << "rc = 0;"; // continue with other probes
+ // XXX: shall we increment numskipped?
+ s.op->newline(-1) << "}";
+
+ s.op->newline() << "else sdp->registered_p = 1;";
+ s.op->newline(-1) << "}"; // for loop
+#undef CHECK_STMT
+}
+
+void
+kprobe_derived_probe_group::emit_module_exit (systemtap_session& s)
+{
+ //Unregister kprobes by batch interfaces.
+ s.op->newline() << "#if defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline() << "j = 0;";
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "if (!sdp->return_p)";
+ s.op->newline(1) << "stap_unreg_kprobes[j++] = &kp->u.kp;";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "unregister_kprobes((struct kprobe **)stap_unreg_kprobes, j);";
+ s.op->newline() << "j = 0;";
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "if (sdp->return_p)";
+ s.op->newline(1) << "stap_unreg_kprobes[j++] = &kp->u.krp;";
+ s.op->newline(-2) << "}";
+ s.op->newline() << "unregister_kretprobes((struct kretprobe **)stap_unreg_kprobes, j);";
+ s.op->newline() << "#ifdef __ia64__";
+ s.op->newline() << "j = 0;";
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "stap_unreg_kprobes[j++] = &kp->dummy;";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "unregister_kprobes((struct kprobe **)stap_unreg_kprobes, j);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "#endif";
+
+ s.op->newline() << "for (i=0; i<" << probes_by_module.size() << "; i++) {";
+ s.op->newline(1) << "struct stap_dwarfless_probe *sdp = & stap_dwarfless_probes[i];";
+ s.op->newline() << "struct stap_dwarfless_kprobe *kp = & stap_dwarfless_kprobes[i];";
+ s.op->newline() << "if (! sdp->registered_p) continue;";
+ s.op->newline() << "if (sdp->return_p) {";
+ s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline(1) << "unregister_kretprobe (&kp->u.krp);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "atomic_add (kp->u.krp.nmissed, & skipped_count);";
+ s.op->newline() << "#ifdef STP_TIMING";
+ s.op->newline() << "if (kp->u.krp.nmissed)";
+ s.op->newline(1) << "_stp_warn (\"Skipped due to missed kretprobe/1 on '%s': %d\\n\", sdp->pp, kp->u.krp.nmissed);";
+ s.op->newline(-1) << "#endif";
+ s.op->newline() << "atomic_add (kp->u.krp.kp.nmissed, & skipped_count);";
+ s.op->newline() << "#ifdef STP_TIMING";
+ s.op->newline() << "if (kp->u.krp.kp.nmissed)";
+ s.op->newline(1) << "_stp_warn (\"Skipped due to missed kretprobe/2 on '%s': %d\\n\", sdp->pp, kp->u.krp.kp.nmissed);";
+ s.op->newline(-1) << "#endif";
+ s.op->newline(-1) << "} else {";
+ s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES)";
+ s.op->newline(1) << "unregister_kprobe (&kp->u.kp);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "atomic_add (kp->u.kp.nmissed, & skipped_count);";
+ s.op->newline() << "#ifdef STP_TIMING";
+ s.op->newline() << "if (kp->u.kp.nmissed)";
+ s.op->newline(1) << "_stp_warn (\"Skipped due to missed kprobe on '%s': %d\\n\", sdp->pp, kp->u.kp.nmissed);";
+ s.op->newline(-1) << "#endif";
+ s.op->newline(-1) << "}";
+ s.op->newline() << "#if !defined(STAPCONF_UNREGISTER_KPROBES) && defined(__ia64__)";
+ s.op->newline() << "unregister_kprobe (&kp->dummy);";
+ s.op->newline() << "#endif";
+ s.op->newline() << "sdp->registered_p = 0;";
+ s.op->newline(-1) << "}";
+}
+
+struct kprobe_builder: public derived_probe_builder
+{
+ kprobe_builder() {}
+ virtual void build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results);
+};
+
+
+void
+kprobe_builder::build(systemtap_session & sess,
+ probe * base,
+ probe_point * location,
+ literal_map_t const & parameters,
+ vector<derived_probe *> & finished_results)
+{
+ string function_string_val, module_string_val;
+ int64_t statement_num_val = 0;
+ bool has_function_str, has_module_str, has_statement_num, has_absolute, has_return;
+
+ has_function_str = this->get_param(parameters, TOK_FUNCTION, function_string_val);
+ has_module_str = this->get_param(parameters, TOK_MODULE, module_string_val);
+ has_return = this->has_null_param (parameters, TOK_RETURN);
+ has_statement_num = this->get_param(parameters, TOK_STATEMENT, statement_num_val);
+ has_absolute = this->has_null_param (parameters, TOK_ABSOLUTE);
+
+ if ( has_function_str )
+ {
+ if ( has_module_str )
+ function_string_val = module_string_val + ":" + function_string_val;
+ finished_results.push_back ( new kprobe_derived_probe ( base,
+ location, function_string_val,
+ 0, has_return,
+ has_statement_num) );
+ }
+ else
+ {
+ // assert guru mode for absolute probes
+ if ( has_statement_num && has_absolute && !base->privileged )
+ throw semantic_error ("absolute statement probe in unprivileged script", base->tok);
+
+ finished_results.push_back(new kprobe_derived_probe ( base,
+ location,"",
+ statement_num_val, has_return,
+ has_statement_num));
+ }
+}
// ------------------------------------------------------------------------
// timer derived probes
// ------------------------------------------------------------------------
@@ -10977,6 +11746,10 @@ perfmon_derived_probe_group::emit_module_init (translator_output* o)
}
#endif
+// ------------------------------------------------------------------------
+// kprobes-based probes,which postpone symbol resolution until runtime.
+// ------------------------------------------------------------------------
+
// ------------------------------------------------------------------------
// Standard tapset registry.
@@ -11075,6 +11848,18 @@ register_standard_tapsets(systemtap_session & s)
s.pattern_root->bind(TOK_PROCFS)->bind(TOK_WRITE)->bind(new procfs_builder());
s.pattern_root->bind_str(TOK_PROCFS)->bind(TOK_WRITE)
->bind(new procfs_builder());
+
+ // Kprobe based probe
+ s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_FUNCTION)
+ ->bind(new kprobe_builder());
+ s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_MODULE)
+ ->bind_str(TOK_FUNCTION)->bind(new kprobe_builder());
+ s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_FUNCTION)->bind(TOK_RETURN)
+ ->bind(new kprobe_builder());
+ s.pattern_root->bind(TOK_KPROBE)->bind_str(TOK_MODULE)
+ ->bind_str(TOK_FUNCTION)->bind(TOK_RETURN)->bind(new kprobe_builder());
+ s.pattern_root->bind(TOK_KPROBE)->bind_num(TOK_STATEMENT)
+ ->bind(TOK_ABSOLUTE)->bind(new kprobe_builder());
}
@@ -11097,6 +11882,7 @@ all_session_groups(systemtap_session& s)
DOONE(profile);
DOONE(mark);
DOONE(tracepoint);
+ DOONE(kprobe);
DOONE(hrtimer);
DOONE(perfmon);
DOONE(procfs);
diff --git a/testsuite/semok/cast.stp b/testsuite/semok/cast.stp
index 93da18ef..d30823cd 100755
--- a/testsuite/semok/cast.stp
+++ b/testsuite/semok/cast.stp
@@ -10,4 +10,8 @@ probe begin {
// would be nice to test usermode @cast too,
// but who knows what debuginfo is installed...
+
+ // check modules generated from headers
+ println(@cast(0, "task_struct", "kmod<linux/sched.h>")->tgid)
+ println(@cast(0, "timeval", "umod<sys/time.h>")->tv_sec)
}
diff --git a/testsuite/systemtap.base/bz10078.c b/testsuite/systemtap.base/bz10078.c
new file mode 100644
index 00000000..9075fbc7
--- /dev/null
+++ b/testsuite/systemtap.base/bz10078.c
@@ -0,0 +1,22 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+struct point { int x, y; };
+
+struct point mkpoint2(void)
+{
+ struct point p = { 1, 2 };
+ return p;
+}
+
+struct point mkpoint1(void)
+{
+ return mkpoint2();
+}
+
+main()
+{
+ struct point p = mkpoint1();
+ printf("%d,%d\n", p.x, p.y);
+ exit(0);
+}
diff --git a/testsuite/systemtap.base/bz10078.exp b/testsuite/systemtap.base/bz10078.exp
new file mode 100644
index 00000000..cad3a3a8
--- /dev/null
+++ b/testsuite/systemtap.base/bz10078.exp
@@ -0,0 +1,35 @@
+set test bz10078
+
+catch {exec gcc -g -o $test $srcdir/$subdir/$test.c} err
+if {$err == "" && [file exists $test]} then { pass "$test compile" } else { fail "$test compile" }
+
+if {![utrace_p]} {
+ catch {exec rm -f $test}
+ untested "$test -p4"
+ untested "$test -p5"
+ return
+}
+
+set rc [stap_run_batch $srcdir/$subdir/$test.stp]
+if {$rc == 0} then { pass "$test -p4" } else { fail "$test -p4" }
+
+if {! [installtest_p]} {
+ catch {exec rm -f $test}
+ untested "$test -p5"
+ return
+}
+
+# Pick up the stap being tested.
+set stapexe [exec /usr/bin/which stap]
+spawn sudo $stapexe $srcdir/$subdir/$test.stp -c ./$test
+set ok 0
+expect {
+ -timeout 60
+ -re {mkpoint[^\r\n]* returns\r\n} { incr ok; exp_continue }
+ -re {1,2\r\n} { incr ok; exp_continue }
+ timeout { fail "$test (timeout)" }
+ eof { }
+}
+wait
+if {$ok == 3} then { pass "$test -p5" } else { fail "$test -p5 ($ok)" }
+exec rm -f $test
diff --git a/testsuite/systemtap.base/bz10078.stp b/testsuite/systemtap.base/bz10078.stp
new file mode 100644
index 00000000..0318e4e9
--- /dev/null
+++ b/testsuite/systemtap.base/bz10078.stp
@@ -0,0 +1,4 @@
+#! stap -p4
+probe process("./bz10078").function("mkpoint*").return {
+ printf("%s returns\n", probefunc())
+}
diff --git a/testsuite/systemtap.base/cast.exp b/testsuite/systemtap.base/cast.exp
index df3246e8..74c4d72a 100644
--- a/testsuite/systemtap.base/cast.exp
+++ b/testsuite/systemtap.base/cast.exp
@@ -1,4 +1,6 @@
set test "cast"
set ::result_string {PID OK
-execname OK}
-stap_run2 $srcdir/$subdir/$test.stp
+PID2 OK
+execname OK
+tv_sec OK}
+stap_run2 $srcdir/$subdir/$test.stp -g
diff --git a/testsuite/systemtap.base/cast.stp b/testsuite/systemtap.base/cast.stp
index bec0cc9b..33a14a28 100644
--- a/testsuite/systemtap.base/cast.stp
+++ b/testsuite/systemtap.base/cast.stp
@@ -10,6 +10,13 @@ probe begin
else
printf("PID %d != %d\n", pid, cast_pid)
+ // Compare PIDs using a generated kernel module
+ cast_pid = @cast(curr, "task_struct", "kmod<linux/sched.h>")->tgid
+ if (pid == cast_pid)
+ println("PID2 OK")
+ else
+ printf("PID2 %d != %d\n", pid, cast_pid)
+
// Compare execnames
name = execname()
cast_name = kernel_string(@cast(curr, "task_struct")->comm)
@@ -18,5 +25,19 @@ probe begin
else
printf("execname \"%s\" != \"%s\"\n", name, cast_name)
+ // Compare tv_sec using a generated user module
+ sec = 42
+ cast_sec = @cast(get_timeval(sec), "timeval", "umod<sys/time.h>")->tv_sec
+ if (sec == cast_sec)
+ println("tv_sec OK")
+ else
+ printf("tv_sec %d != %d\n", sec, cast_sec)
+
exit()
}
+
+function get_timeval:long(sec:long) %{
+ static struct timeval mytime = {0};
+ mytime.tv_sec = THIS->sec;
+ THIS->__retvalue = (long)&mytime;
+%}
diff --git a/testsuite/systemtap.base/kprobes.exp b/testsuite/systemtap.base/kprobes.exp
new file mode 100644
index 00000000..240ecd82
--- /dev/null
+++ b/testsuite/systemtap.base/kprobes.exp
@@ -0,0 +1,2 @@
+set test "kprobes"
+stap_run $srcdir/$subdir/$test.stp
diff --git a/testsuite/systemtap.base/kprobes.stp b/testsuite/systemtap.base/kprobes.stp
new file mode 100644
index 00000000..62c18347
--- /dev/null
+++ b/testsuite/systemtap.base/kprobes.stp
@@ -0,0 +1,21 @@
+/*
+ * kprobes.stp
+ * Probe to test the functionality of kprobe-based probes
+ * (Dwarfless Probing)
+ */
+
+probe begin
+{
+ println("\n Systemtap starting probe");
+}
+
+probe kprobe.function("vfs_read")
+{
+ printf("\n probe point hit");
+ exit();
+}
+
+probe end
+{
+ println("\n Systemtap starting probe");
+}
diff --git a/testsuite/systemtap.base/utrace_syscall_args.stp b/testsuite/systemtap.base/utrace_syscall_args.stp
index 166e1ace..6c9e14fc 100644
--- a/testsuite/systemtap.base/utrace_syscall_args.stp
+++ b/testsuite/systemtap.base/utrace_syscall_args.stp
@@ -113,18 +113,23 @@ probe process("utrace_syscall_args").syscall.return {
if (syscalls_seen >= 4) {
if (syscalls_seen == 4) {
mmap_args[7] = $return
+ mmap_args[8] = $syscall
}
else if (syscalls_seen == 5) {
munmap_args[3] = $return
+ munmap_args[4] = $syscall
}
else if (syscalls_seen == 6) {
close_args[2] = $return
+ close_args[3] = $syscall
}
else if (syscalls_seen == 7) {
dup_args[7] = $return
+ dup_args[8] = $syscall
}
else if (syscalls_seen == 8) {
bad_syscall_args[7] = $return
+ bad_syscall_args[8] = $syscall
syscalls_seen = 0
}
}
@@ -159,6 +164,13 @@ probe end
failures += 1
printf("mmap bad arg 6: 0x%x vs 0x0\n", mmap_args[6])
}
+
+ # Validate syscall number
+ if (mmap_args[0] != mmap_args[8]) {
+ failures += 1
+ printf("mmap $syscall mismatch: %d vs. %d\n",
+ mmap_args[0], mmap_args[8])
+ }
}
# print munmap info
@@ -191,6 +203,13 @@ probe end
failures += 1
printf("munmap bad return value: 0x%x vs 0x0\n", munmap_args[7])
}
+
+ # Validate syscall number
+ if (munmap_args[0] != munmap_args[4]) {
+ failures += 1
+ printf("munmap $syscall mismatch: %d vs. %d\n",
+ munmap_args[0], munmap_args[4])
+ }
}
# print close info
@@ -207,6 +226,13 @@ probe end
printf("close bad arg 1: 0x%x vs 0x%x\n",
close_args[0], mmap_args[5])
}
+
+ # Validate syscall number
+ if (close_args[0] != close_args[3]) {
+ failures += 1
+ printf("close $syscall mismatch: %d vs. %d\n",
+ close_args[0], close_args[3])
+ }
}
# print dup info
@@ -278,6 +304,13 @@ probe end
printf("dup bad arg 6: 0x%x vs 0xe38e38e3\n", dup_args[6])
}
}
+
+ # Validate syscall number
+ if (dup_args[0] != dup_args[8]) {
+ failures += 1
+ printf("dup $syscall mismatch: %d vs. %d\n",
+ dup_args[0], dup_args[8])
+ }
}
# print bad_syscall info
@@ -355,6 +388,13 @@ probe end
bad_syscall_args[6])
}
}
+
+ # Validate syscall number
+ if (bad_syscall_args[0] != bad_syscall_args[8]) {
+ failures += 1
+ printf("bad_syscall $syscall mismatch: %d vs. %d\n",
+ bad_syscall_args[0], bad_syscall_args[8])
+ }
}
if (failures == 0) {
diff --git a/testsuite/systemtap.examples/general/grapher.stp b/testsuite/systemtap.examples/general/grapher.stp
index 04463979..4f326ec1 100644
--- a/testsuite/systemtap.examples/general/grapher.stp
+++ b/testsuite/systemtap.examples/general/grapher.stp
@@ -2,11 +2,11 @@
probe begin
{
-printf ("%s\n", "%Title:CPU utilization");
-printf ("%s\n", "%XAxisTitle:Time");
-printf ("%s\n", "%YAxisTitle:Percent");
-printf ("%s\n", "%DataSet:cpu 100 00ff00 bar");
-printf ("%s\n", "%DataSet:kbd 100 ff0000 dot");
+printf ("%%Title:CPU utilization\n");
+printf ("%%XAxisTitle:Time");
+printf ("%%YAxisTitle:Percent");
+printf ("%%DataSet:cpu 100 00ff00 bar");
+printf ("%%DataSet:kbd 100 ff0000 dot");
}
# CPU utilization
diff --git a/testsuite/systemtap.examples/profiling/latencytap.stp b/testsuite/systemtap.examples/profiling/latencytap.stp
index 28956129..d202ec81 100755
--- a/testsuite/systemtap.examples/profiling/latencytap.stp
+++ b/testsuite/systemtap.examples/profiling/latencytap.stp
@@ -23,7 +23,7 @@ function _get_sleep_time:long(rq_param:long, p_param:long)
function task_backtrace:string (task:long)
%{
_stp_stack_snprint_tsk(THIS->__retvalue, MAXSTRINGLEN,
- (struct task_struct *)THIS->task, 0, MAXTRACE);
+ (struct task_struct *)(unsigned long)THIS->task, 0, MAXTRACE);
%}
probe kernel.function("enqueue_task_fair") {
diff --git a/testsuite/systemtap.printf/basic6.exp b/testsuite/systemtap.printf/basic6.exp
new file mode 100644
index 00000000..72bf8f57
--- /dev/null
+++ b/testsuite/systemtap.printf/basic6.exp
@@ -0,0 +1,3 @@
+set test "basic6"
+set ::result_string {Hello%World}
+stap_run2 $srcdir/$subdir/$test.stp
diff --git a/testsuite/systemtap.printf/basic6.stp b/testsuite/systemtap.printf/basic6.stp
new file mode 100644
index 00000000..69721188
--- /dev/null
+++ b/testsuite/systemtap.printf/basic6.stp
@@ -0,0 +1,5 @@
+probe begin
+{
+ printf("Hello%%World");
+ exit()
+}
diff --git a/translate.cxx b/translate.cxx
index a6357766..79c05d4b 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -4202,7 +4202,8 @@ c_unparser::visit_print_format (print_format* e)
int use_print = 0;
string format_string = print_format::components_to_string(components);
- if (tmp.size() == 0 || (tmp.size() == 1 && format_string == "%s"))
+ if ((tmp.size() == 0 && format_string.find("%%") == std::string::npos)
+ || (tmp.size() == 1 && format_string == "%s"))
use_print = 1;
else if (tmp.size() == 1
&& e->args[0]->tok->type == tok_string