summaryrefslogtreecommitdiffstats
path: root/dwflpp.cxx
diff options
context:
space:
mode:
authorDave Brolley <brolley@redhat.com>2009-06-02 11:25:36 -0400
committerDave Brolley <brolley@redhat.com>2009-06-02 11:25:36 -0400
commitea9d509619ae8dc1512576bccdff2288a2c256dc (patch)
treeeb2e737cef374fc16cb1469da9b5e1b05ccd8889 /dwflpp.cxx
parentc16e888ee192f0cd57c97bae7d4764dfadb1731b (diff)
parent874b38cf7d259179a2a455cd34ea5a5b9348604b (diff)
downloadsystemtap-steved-ea9d509619ae8dc1512576bccdff2288a2c256dc.tar.gz
systemtap-steved-ea9d509619ae8dc1512576bccdff2288a2c256dc.tar.xz
systemtap-steved-ea9d509619ae8dc1512576bccdff2288a2c256dc.zip
Merge branch 'master' of git://sources.redhat.com/git/systemtap
Diffstat (limited to 'dwflpp.cxx')
-rw-r--r--dwflpp.cxx321
1 files changed, 318 insertions, 3 deletions
diff --git a/dwflpp.cxx b/dwflpp.cxx
index d05bdb97..3af26053 100644
--- a/dwflpp.cxx
+++ b/dwflpp.cxx
@@ -66,7 +66,8 @@ static string TOK_KERNEL("kernel");
dwflpp::dwflpp(systemtap_session & session, const string& user_module):
sess(session), module(NULL), module_bias(0), mod_info(NULL),
module_start(0), module_end(0), cu(NULL), dwfl(NULL),
- module_dwarf(NULL), function(NULL)
+ module_dwarf(NULL), function(NULL), blacklist_enabled(false),
+ pc_cached_scopes(0), num_cached_scopes(0), cached_scopes(NULL)
{
if (user_module.empty())
setup_kernel();
@@ -77,6 +78,7 @@ dwflpp::dwflpp(systemtap_session & session, const string& user_module):
dwflpp::~dwflpp()
{
+ free(cached_scopes);
if (dwfl)
dwfl_end(dwfl);
}
@@ -165,6 +167,9 @@ dwflpp::focus_on_cu(Dwarf_Die * c)
// Reset existing pointers and names
function_name.clear();
function = NULL;
+
+ free(cached_scopes);
+ cached_scopes = NULL;
}
@@ -299,11 +304,17 @@ dwflpp::setup_kernel(bool debuginfo_needed)
elfutils_kernel_path.c_str(),
&dwfl_report_offline_predicate);
- if (debuginfo_needed)
+ if (debuginfo_needed) {
+ if (rc) {
+ // Suggest a likely kernel dir to find debuginfo rpm for
+ string dir = string("/lib/modules/" + sess.kernel_release );
+ find_debug_rpms(sess, dir.c_str());
+ }
dwfl_assert (string("missing ") + sess.architecture +
string(" kernel/module debuginfo under '") +
sess.kernel_build_tree + string("'"),
rc);
+ }
// XXX: it would be nice if we could do a single
// ..._report_offline call for an entire systemtap script, so
@@ -322,6 +333,8 @@ dwflpp::setup_kernel(bool debuginfo_needed)
// run time. See the dwarf_derived_probe ctor and its caller.
dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
+
+ build_blacklist();
}
@@ -1339,7 +1352,7 @@ dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die,
assert (cu);
- nscopes = dwarf_getscopes (cu, pc, &scopes);
+ nscopes = dwarf_getscopes_cached (pc, &scopes);
int sidx;
// if pc and scope_die are disjoint then we need dwarf_getscopes_die
for (sidx = 0; sidx < nscopes; sidx++)
@@ -2094,4 +2107,306 @@ dwflpp::literal_stmt_for_pointer (Dwarf_Die *type_die,
}
+static bool
+in_kprobes_function(systemtap_session& sess, Dwarf_Addr addr)
+{
+ if (sess.sym_kprobes_text_start != 0 && sess.sym_kprobes_text_end != 0)
+ {
+ // If the probe point address is anywhere in the __kprobes
+ // address range, we can't use this probe point.
+ if (addr >= sess.sym_kprobes_text_start && addr < sess.sym_kprobes_text_end)
+ return true;
+ }
+ return false;
+}
+
+
+bool
+dwflpp::blacklisted_p(const string& funcname,
+ const string& filename,
+ int,
+ const string& module,
+ const string& section,
+ Dwarf_Addr addr,
+ bool has_return)
+{
+ if (!blacklist_enabled)
+ return false; // no blacklist for userspace
+
+ if (section.substr(0, 6) == string(".init.") ||
+ section.substr(0, 6) == string(".exit.") ||
+ section.substr(0, 9) == string(".devinit.") ||
+ section.substr(0, 9) == string(".devexit.") ||
+ section.substr(0, 9) == string(".cpuinit.") ||
+ section.substr(0, 9) == string(".cpuexit.") ||
+ section.substr(0, 9) == string(".meminit.") ||
+ section.substr(0, 9) == string(".memexit."))
+ {
+ // NB: module .exit. routines could be probed in theory:
+ // if the exit handler in "struct module" is diverted,
+ // first inserting the kprobes
+ // then allowing the exit code to run
+ // then removing these kprobes
+ if (sess.verbose>1)
+ clog << " skipping - init/exit";
+ return true;
+ }
+
+ // Check for function marked '__kprobes'.
+ if (module == TOK_KERNEL && in_kprobes_function(sess, addr))
+ {
+ if (sess.verbose>1)
+ clog << " skipping - __kprobes";
+ return true;
+ }
+
+ // Check probe point against blacklist.
+ int goodfn = regexec (&blacklist_func, funcname.c_str(), 0, NULL, 0);
+ if (has_return)
+ goodfn = goodfn && regexec (&blacklist_func_ret, funcname.c_str(), 0, NULL, 0);
+ int goodfile = regexec (&blacklist_file, filename.c_str(), 0, NULL, 0);
+
+ if (! (goodfn && goodfile))
+ {
+ if (sess.guru_mode)
+ {
+ if (sess.verbose>1)
+ clog << " guru mode enabled - ignoring blacklist";
+ }
+ else
+ {
+ if (sess.verbose>1)
+ clog << " skipping - blacklisted";
+ return true;
+ }
+ }
+
+ // This probe point is not blacklisted.
+ return false;
+}
+
+
+void
+dwflpp::build_blacklist()
+{
+ // We build up the regexps in these strings
+
+ // Add ^ anchors at the front; $ will be added just before regcomp.
+
+ string blfn = "^(";
+ string blfn_ret = "^(";
+ string blfile = "^(";
+
+ blfile += "kernel/kprobes.c"; // first alternative, no "|"
+ blfile += "|arch/.*/kernel/kprobes.c";
+ // Older kernels need ...
+ blfile += "|include/asm/io.h";
+ blfile += "|include/asm/bitops.h";
+ // While newer ones need ...
+ blfile += "|arch/.*/include/asm/io.h";
+ blfile += "|arch/.*/include/asm/bitops.h";
+ blfile += "|drivers/ide/ide-iops.c";
+
+ // XXX: it would be nice if these blacklisted functions were pulled
+ // in dynamically, instead of being statically defined here.
+ // Perhaps it could be populated from script files. A "noprobe
+ // kernel.function("...")" construct might do the trick.
+
+ // Most of these are marked __kprobes in newer kernels. We list
+ // them here (anyway) so the translator can block them on older
+ // kernels that don't have the __kprobes function decorator. This
+ // also allows detection of problems at translate- rather than
+ // run-time.
+
+ blfn += "atomic_notifier_call_chain"; // first blfn; no "|"
+ blfn += "|default_do_nmi";
+ blfn += "|__die";
+ blfn += "|die_nmi";
+ blfn += "|do_debug";
+ blfn += "|do_general_protection";
+ blfn += "|do_int3";
+ blfn += "|do_IRQ";
+ blfn += "|do_page_fault";
+ blfn += "|do_sparc64_fault";
+ blfn += "|do_trap";
+ blfn += "|dummy_nmi_callback";
+ blfn += "|flush_icache_range";
+ blfn += "|ia64_bad_break";
+ blfn += "|ia64_do_page_fault";
+ blfn += "|ia64_fault";
+ blfn += "|io_check_error";
+ blfn += "|mem_parity_error";
+ blfn += "|nmi_watchdog_tick";
+ blfn += "|notifier_call_chain";
+ blfn += "|oops_begin";
+ blfn += "|oops_end";
+ blfn += "|program_check_exception";
+ blfn += "|single_step_exception";
+ blfn += "|sync_regs";
+ blfn += "|unhandled_fault";
+ blfn += "|unknown_nmi_error";
+
+ // Lots of locks
+ blfn += "|.*raw_.*lock.*";
+ blfn += "|.*read_.*lock.*";
+ blfn += "|.*write_.*lock.*";
+ blfn += "|.*spin_.*lock.*";
+ blfn += "|.*rwlock_.*lock.*";
+ blfn += "|.*rwsem_.*lock.*";
+ blfn += "|.*mutex_.*lock.*";
+ blfn += "|raw_.*";
+ blfn += "|.*seq_.*lock.*";
+
+ // atomic functions
+ blfn += "|atomic_.*";
+ blfn += "|atomic64_.*";
+
+ // few other problematic cases
+ blfn += "|get_bh";
+ blfn += "|put_bh";
+
+ // Experimental
+ blfn += "|.*apic.*|.*APIC.*";
+ blfn += "|.*softirq.*";
+ blfn += "|.*IRQ.*";
+ blfn += "|.*_intr.*";
+ blfn += "|__delay";
+ blfn += "|.*kernel_text.*";
+ blfn += "|get_current";
+ blfn += "|current_.*";
+ blfn += "|.*exception_tables.*";
+ blfn += "|.*setup_rt_frame.*";
+
+ // PR 5759, CONFIG_PREEMPT kernels
+ blfn += "|.*preempt_count.*";
+ blfn += "|preempt_schedule";
+
+ // These functions don't return, so return probes would never be recovered
+ blfn_ret += "do_exit"; // no "|"
+ blfn_ret += "|sys_exit";
+ blfn_ret += "|sys_exit_group";
+
+ // __switch_to changes "current" on x86_64 and i686, so return probes
+ // would cause kernel panic, and it is marked as "__kprobes" on x86_64
+ if (sess.architecture == "x86_64")
+ blfn += "|__switch_to";
+ if (sess.architecture == "i686")
+ blfn_ret += "|__switch_to";
+
+ blfn += ")$";
+ blfn_ret += ")$";
+ blfile += ")$";
+
+ if (sess.verbose > 2)
+ {
+ clog << "blacklist regexps:" << endl;
+ clog << "blfn: " << blfn << endl;
+ clog << "blfn_ret: " << blfn_ret << endl;
+ clog << "blfile: " << blfile << endl;
+ }
+
+ int rc = regcomp (& blacklist_func, blfn.c_str(), REG_NOSUB|REG_EXTENDED);
+ if (rc) throw semantic_error ("blacklist_func regcomp failed");
+ rc = regcomp (& blacklist_func_ret, blfn_ret.c_str(), REG_NOSUB|REG_EXTENDED);
+ if (rc) throw semantic_error ("blacklist_func_ret regcomp failed");
+ rc = regcomp (& blacklist_file, blfile.c_str(), REG_NOSUB|REG_EXTENDED);
+ if (rc) throw semantic_error ("blacklist_file regcomp failed");
+
+ blacklist_enabled = true;
+}
+
+
+string
+dwflpp::get_blacklist_section(Dwarf_Addr addr)
+{
+ string blacklist_section;
+ Dwarf_Addr bias;
+ // We prefer dwfl_module_getdwarf to dwfl_module_getelf here,
+ // because dwfl_module_getelf can force costly section relocations
+ // we don't really need, while either will do for this purpose.
+ Elf* elf = (dwarf_getelf (dwfl_module_getdwarf (module, &bias))
+ ?: dwfl_module_getelf (module, &bias));
+
+ Dwarf_Addr offset = addr - bias;
+ if (elf)
+ {
+ Elf_Scn* scn = 0;
+ size_t shstrndx;
+ dwfl_assert ("getshstrndx", elf_getshstrndx (elf, &shstrndx));
+ while ((scn = elf_nextscn (elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (! shdr)
+ continue; // XXX error?
+
+ if (!(shdr->sh_flags & SHF_ALLOC))
+ continue;
+
+ GElf_Addr start = shdr->sh_addr;
+ GElf_Addr end = start + shdr->sh_size;
+ if (! (offset >= start && offset < end))
+ continue;
+
+ blacklist_section = elf_strptr (elf, shstrndx, shdr->sh_name);
+ break;
+ }
+ }
+ return blacklist_section;
+}
+
+
+Dwarf_Addr
+dwflpp::relocate_address(Dwarf_Addr addr,
+ string& reloc_section,
+ string& blacklist_section)
+{
+ Dwarf_Addr reloc_addr = addr;
+ if (!module)
+ {
+ assert(module_name == TOK_KERNEL);
+ reloc_section = "";
+ blacklist_section = "";
+ }
+ else if (dwfl_module_relocations (module) > 0)
+ {
+ // This is a relocatable module; libdwfl already knows its
+ // sections, so we can relativize addr.
+ int idx = dwfl_module_relocate_address (module, &reloc_addr);
+ const char* r_s = dwfl_module_relocation_info (module, idx, NULL);
+ if (r_s)
+ reloc_section = r_s;
+ blacklist_section = reloc_section;
+
+ if (reloc_section == "" && dwfl_module_relocations (module) == 1)
+ {
+ blacklist_section = get_blacklist_section(addr);
+ reloc_section = ".dynamic";
+ reloc_addr = addr;
+ }
+ }
+ else
+ {
+ blacklist_section = get_blacklist_section(addr);
+ reloc_section = ".absolute";
+ }
+ return reloc_addr;
+}
+
+
+int
+dwflpp::dwarf_getscopes_cached (Dwarf_Addr pc, Dwarf_Die **scopes)
+{
+ if (!cached_scopes || pc != pc_cached_scopes)
+ {
+ free(cached_scopes);
+ cached_scopes = NULL;
+ pc_cached_scopes = pc;
+ num_cached_scopes = dwarf_getscopes(cu, pc, &cached_scopes);
+ }
+ *scopes = cached_scopes;
+ return num_cached_scopes;
+}
+
+
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */