From 3e1c25aa1f788ad5075e2563cc208b58ab6273b5 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 17 Apr 2009 20:34:13 -0700 Subject: Add functions to build umod & kmod typequery --- buildrun.cxx | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ buildrun.h | 2 ++ 2 files changed, 59 insertions(+) diff --git a/buildrun.cxx b/buildrun.cxx index e0f22f29..71753e9f 100644 --- a/buildrun.cxx +++ b/buildrun.cxx @@ -443,4 +443,61 @@ make_tracequery(systemtap_session& s, string& name, const vector& 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(++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(++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& 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 -- cgit From 1f329b5e2af4710a254e252529e8eee2fab4fd67 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 20 Apr 2009 16:41:52 -0700 Subject: Add a function to hash typequery modules --- hash.cxx | 20 ++++++++++++++++++++ hash.h | 2 ++ 2 files changed, 22 insertions(+) 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 : */ -- cgit From 462c90c3a1de9e8472bc84f9c9f8d5192aebd778 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 20 Apr 2009 16:43:15 -0700 Subject: Suppress module-loading errors in @casts If a @cast encounters a module that it can't load, it should just go on to the next module instead of throwing an exception. If there is no next module, we'll get a better "type not found" exception anyway. --- tapsets.cxx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tapsets.cxx b/tapsets.cxx index 01c838d9..ee799e74 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -5148,7 +5148,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 -- cgit From fb0274bc9ad5b82caffa33fbd077104b0b7f3b4e Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 20 Apr 2009 16:45:26 -0700 Subject: PR10055: generate dummy modules w/ types for @cast The module field in @cast can now also be "kmod" or "umod" to generate a kernel or user module which includes the specified header. The appropriate compiler flags are used to save all possible type debuginfo from the header. --- tapsets.cxx | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tapsets.cxx b/tapsets.cxx index ee799e74..5a62d47f 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -5104,9 +5104,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" or "umod" + // 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 +5180,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. -- cgit From 2ca818756fff583220c238cf344add80b71f7f8c Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 20 Apr 2009 16:58:37 -0700 Subject: Cleanup typequery user modules from the cache too --- cache.cxx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/cache.cxx b/cache.cxx index 86f7213a..61bc243f 100644 --- a/cache.cxx +++ b/cache.cxx @@ -253,14 +253,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) { -- cgit From 219b3700b8603b6fe3610d6f06e353f3c041ee0b Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 20 Apr 2009 17:18:32 -0700 Subject: Add tests for @cast-generated modules --- testsuite/semok/cast.stp | 4 ++++ testsuite/systemtap.base/cast.exp | 6 ++++-- testsuite/systemtap.base/cast.stp | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) 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")->tgid) + println(@cast(0, "timeval", "umod")->tv_sec) } 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")->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")->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; +%} -- cgit