diff options
author | Dave Brolley <brolley@redhat.com> | 2009-06-02 11:25:36 -0400 |
---|---|---|
committer | Dave Brolley <brolley@redhat.com> | 2009-06-02 11:25:36 -0400 |
commit | ea9d509619ae8dc1512576bccdff2288a2c256dc (patch) | |
tree | eb2e737cef374fc16cb1469da9b5e1b05ccd8889 /dwflpp.cxx | |
parent | c16e888ee192f0cd57c97bae7d4764dfadb1731b (diff) | |
parent | 874b38cf7d259179a2a455cd34ea5a5b9348604b (diff) | |
download | systemtap-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.cxx | 321 |
1 files changed, 318 insertions, 3 deletions
@@ -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 : */ |