summaryrefslogtreecommitdiffstats
path: root/dwflpp.cxx
diff options
context:
space:
mode:
authorJosh Stone <jistone@redhat.com>2009-06-01 18:47:30 -0700
committerJosh Stone <jistone@redhat.com>2009-06-01 18:47:30 -0700
commit276465828851648edc5b56f762a0d100051c9e32 (patch)
tree25bc37f7800741949fd433cdaf25bbd198b2479e /dwflpp.cxx
parentbec8cf694b0cd89dfa3d082e611326d7bcfad884 (diff)
downloadsystemtap-steved-276465828851648edc5b56f762a0d100051c9e32.tar.gz
systemtap-steved-276465828851648edc5b56f762a0d100051c9e32.tar.xz
systemtap-steved-276465828851648edc5b56f762a0d100051c9e32.zip
Move the blacklist functions into dwflpp
For a call like "stap -l 'syscall.*'", I found that ~10% of the time was spent compiling the blacklist regexps over again for each probe point. By moving this functionality into the kernel dwflpp instance, we can reuse the regexps and get an easy speed boost.
Diffstat (limited to 'dwflpp.cxx')
-rw-r--r--dwflpp.cxx291
1 files changed, 290 insertions, 1 deletions
diff --git a/dwflpp.cxx b/dwflpp.cxx
index d05bdb97..52981d3f 100644
--- a/dwflpp.cxx
+++ b/dwflpp.cxx
@@ -66,7 +66,7 @@ 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)
{
if (user_module.empty())
setup_kernel();
@@ -322,6 +322,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();
}
@@ -2094,4 +2096,291 @@ 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;
+}
+
+
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */