From 209dd533fc8de83015d7e83d0426a1cb956ff9fc Mon Sep 17 00:00:00 2001 From: William Cohen Date: Mon, 1 Jun 2009 14:45:22 -0400 Subject: Add debuginfo-install suggestion for kernel probing. --- dwflpp.cxx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'dwflpp.cxx') diff --git a/dwflpp.cxx b/dwflpp.cxx index d05bdb97..f3429118 100644 --- a/dwflpp.cxx +++ b/dwflpp.cxx @@ -299,11 +299,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 -- cgit From 276465828851648edc5b56f762a0d100051c9e32 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 1 Jun 2009 18:47:30 -0700 Subject: 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. --- dwflpp.cxx | 291 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 290 insertions(+), 1 deletion(-) (limited to 'dwflpp.cxx') 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 : */ -- cgit From c8ad068755e7424e767660f2c27cb3b1e2d5343d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 2 Jun 2009 00:43:49 -0700 Subject: Cache the last result of dwarf_getscopes This one function accounted for ~30% of my callgrind profile of "stap -l 'syscall.*'", even though it was only called ~1200 times. We call dwarf_getscopes for each $target variable, with the same parameters within a given probe. Since they're no nicely grouped, it's easy to just cache the most recent call, and the next few calls will be a hit. Overall this cuts the number of calls down to about 300, for an easy speed gain. --- dwflpp.cxx | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'dwflpp.cxx') diff --git a/dwflpp.cxx b/dwflpp.cxx index 52981d3f..6ca9780d 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), blacklist_enabled(false) + 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; } @@ -1341,7 +1346,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++) @@ -2383,4 +2388,19 @@ dwflpp::relocate_address(Dwarf_Addr 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 : */ -- cgit