diff options
-rw-r--r-- | ChangeLog | 35 | ||||
-rw-r--r-- | NEWS | 13 | ||||
-rw-r--r-- | cache.cxx | 168 | ||||
-rw-r--r-- | cache.h | 17 | ||||
-rw-r--r-- | elaborate.cxx | 43 | ||||
-rw-r--r-- | main.cxx | 2 | ||||
-rw-r--r-- | parse.cxx | 30 | ||||
-rw-r--r-- | parse.h | 10 | ||||
-rw-r--r-- | session.h | 2 | ||||
-rwxr-xr-x | stap-report | 32 | ||||
-rw-r--r-- | stap.1.in | 8 | ||||
-rw-r--r-- | staptree.h | 4 | ||||
-rw-r--r-- | tapsets.cxx | 13 | ||||
-rw-r--r-- | testsuite/ChangeLog | 14 | ||||
-rwxr-xr-x | testsuite/parseko/source_context.stp | 5 | ||||
-rw-r--r-- | testsuite/systemtap.base/badkprobe.exp | 11 | ||||
-rwxr-xr-x[-rw-r--r--] | testsuite/systemtap.base/bz6850.stp | 0 | ||||
-rw-r--r-- | testsuite/systemtap.base/uprobes.exp | 6 | ||||
-rw-r--r-- | testsuite/systemtap.base/warnings.exp | 2 |
19 files changed, 402 insertions, 13 deletions
@@ -1,3 +1,38 @@ +2008-10-10 Frank Ch. Eigler <fche@elastic.org> + + PR6749 + * tapsets.cxx (dwarf_derived_probe_group::emit_module_init): Tolerate + failed register_*kprobe during initialization; warn instead. + +2008-10-10 Kent Sebastian <ksebasti@redhat.com> + + * cache.cxx (cache_clean): New group of functions implementing cache + size limiting. + +2008-10-09 Rajan Arora <rarora@redhat.com> + + * elaborate.cxx (systemtap_session::print_error_source): New. + (systemtap_session::print_error): Call it. + (systemtap_session::print_warning): Likewise. + * parse.cxx (parser::print_error): Likewise. + * session.h (struct systemtap_session::print_error_source): + Declare it. + * parse.cxx (lexer::get_input_contents): New. + (parser::parse): Call it. + (lexer::set_current_file): New. + (parser::parse): Call it. + * parse.h (class lexer::get_input_contents): Declare it. + (class lexer::set_current_file): Declare it. + (class lexer): New member current_file. + (class lexer): Update input_contents from vector<char> + to std::string. + (struct source_loc): New member stap_file. + * staptree.h (struct stapfile): New member file_contents. + +2008-10-09 Stan Cox <scox@redhat.com> + + * tapsets.cxx (query_cu): Fix typo. + 2008-10-06 Wenji Huang <wenji.huang@oracle.com> PR 4886 @@ -1,5 +1,17 @@ * What's new +- Cache limiting is now available. If the compiled module cache size is + over a limit specified in the $SYSTEMTAP_DIR/cache/cache_mb_limit file, + some old cache entries will be unlinked. See man stap(1) for more. + +- Error and warning messages are now followed by source context displaying + the erroneous line/s and a handy '^' in the following line pointing to the + appropriate column. + +- A bug reporting tool "stap-report" is now available which will quickly + retrieve much of the information requested here: + http://sourceware.org/systemtap/wiki/HowToReportBugs + - The translator can resolve members of anonymous structs / unions: given struct { int foo; struct { int bar; }; } *p; this now works: $p->bar @@ -88,6 +100,7 @@ but is for some reason unavailable.) In return probes only, $$return expands to an empty string for a void function, or "return=0xf00". + * What's new in version 0.7 - .statement("func@file:*") and .statement("func@file:M-N") probes are now @@ -19,6 +19,7 @@ extern "C" { #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> +#include <glob.h> } using namespace std; @@ -50,6 +51,8 @@ add_to_cache(systemtap_session& s) cerr << "Copy failed (\"" << s.translated_source << "\" to \"" << c_dest_path << "\"): " << strerror(errno) << endl; } + + clean_cache(s); } @@ -135,3 +138,168 @@ get_from_cache(systemtap_session& s) return true; } + + +void +clean_cache(systemtap_session& s) +{ + if (s.cache_path != "") + { + /* Get cache size limit from file in the stap cache dir */ + string cache_max_filename = s.cache_path + "/"; + cache_max_filename += SYSTEMTAP_CACHE_MAX_FILENAME; + ifstream cache_max_file(cache_max_filename.c_str(), ios::in); + + if (cache_max_file.is_open()) + { + cache_max_file >> s.cache_max; + cache_max_file.close(); + s.cache_max *= 1024 * 1024; //convert to bytes + + //bad content in the file? + if (s.cache_max < 0) + s.cache_max = 0; + } + else + { + //file doesnt exist or error + s.cache_max = 0; + } + + if (s.cache_max == 0) + { + if (s.verbose > 1) + clog << "Missing cache limit file " << s.cache_path << "/" << SYSTEMTAP_CACHE_MAX_FILENAME << ", I/O error or invalid content." << endl; + + return; + } + + + //glob for all kernel modules in the cache dir + glob_t cache_glob; + string glob_str = s.cache_path + "/*/*.ko"; + glob(glob_str.c_str(), 0, NULL, &cache_glob); + + + set<struct cache_ent_info, struct weight_sorter> cache_contents; + long cache_size = 0; + + //grab info for each cache entry (.ko and .c) + for (unsigned int i = 0; i < cache_glob.gl_pathc; i++) + { + struct cache_ent_info cur_info; + string cache_ent_path = cache_glob.gl_pathv[i]; + long cur_size = 0; + + cache_ent_path = cache_ent_path.substr(0, cache_ent_path.length() - 3); + cur_info.path = cache_ent_path; + cur_info.weight = get_cache_file_weight(cache_ent_path); + + cur_size = get_cache_file_size(cache_ent_path); + cur_info.size = cur_size; + cache_size += cur_size; + + if (cur_info.size != 0 && cur_info.weight != 0) + { + cache_contents.insert(cur_info); + } + } + + globfree(&cache_glob); + + set<struct cache_ent_info, struct weight_sorter>::iterator i; + long r_cache_size = cache_size; + string removed_dirs = ""; + + //unlink .ko and .c until the cache size is under the limit + for (i = cache_contents.begin(); i != cache_contents.end(); ++i) + { + if (r_cache_size < s.cache_max) + break; + + //delete this (*i) cache_entry, add to removed list + r_cache_size -= (*i).size; + unlink_cache_entry((*i).path); + removed_dirs += (*i).path + ", "; + } + + cache_contents.clear(); + + if (s.verbose > 1) + { + if (removed_dirs == "") + { + clog << "Cache size under limit, no entries removed." << endl; + } + else + { + //remove trailing ", " + removed_dirs = removed_dirs.substr(0, removed_dirs.length() - 2); + clog << "Cache cleaning successful, removed entries: " << removed_dirs << endl; + } + } + } + else + { + if (s.verbose > 1) + clog << "Cache cleaning skipped, no cache path." << endl; + } +} + +//Get the size, in bytes, of the module (.ko) and the +// corresponding source (.c) +long +get_cache_file_size(const string &cache_ent_path) +{ + size_t cache_ent_size = 0; + string mod_path = cache_ent_path + ".ko", + source_path = cache_ent_path + ".c"; + + struct stat file_info; + + if (stat(mod_path.c_str(), &file_info) == 0) + cache_ent_size += file_info.st_size; + else + return 0; + + //Don't care if the .c isn't there, it's much smaller + // than the .ko anyway + if (stat(source_path.c_str(), &file_info) == 0) + cache_ent_size += file_info.st_size; + + + return cache_ent_size; +} + +//Assign a weight to this cache entry. A lower weight +// will be removed before a higher weight. +//TODO: for now use system mtime... later base a +// weighting on size, ctime, atime etc.. +long +get_cache_file_weight(const string &cache_ent_path) +{ + time_t dir_mtime = 0; + struct stat dir_stat_info; + string module_path = cache_ent_path + ".ko"; + + if (stat(module_path.c_str(), &dir_stat_info) == 0) + //GNU struct stat defines st_atime as st_atim.tv_sec + // but it doesnt seem to work properly in practice + // so use st_atim.tv_sec -- bad for portability? + dir_mtime = dir_stat_info.st_mtim.tv_sec; + + return dir_mtime; +} + + +//deletes the module and source file contain +void +unlink_cache_entry(const string &cache_ent_path) +{ + //remove both .ko and .c files + string mod_path = cache_ent_path + ".ko"; + string source_path = cache_ent_path + ".c"; + + unlink(mod_path.c_str()); //it must exist, globbed for it earlier + unlink(source_path.c_str()); //if its not there, no matter +} @@ -1,2 +1,19 @@ +#define SYSTEMTAP_CACHE_MAX_FILENAME "cache_mb_limit" + +struct cache_ent_info { + std::string path; + size_t size; + long weight; //lower == removed earlier +}; + +struct weight_sorter { + bool operator() (const struct cache_ent_info& c1, const struct cache_ent_info& c2) const + { return c1.weight < c2.weight;} +}; + void add_to_cache(systemtap_session& s); bool get_from_cache(systemtap_session& s); +void clean_cache(systemtap_session& s); +long get_cache_file_size(const std::string &cache_ent_path); +long get_cache_file_weight(const std::string &cache_ent_path); +void unlink_cache_entry(const std::string &cache_ent_path); diff --git a/elaborate.cxx b/elaborate.cxx index afdc796e..9f0a2a74 100644 --- a/elaborate.cxx +++ b/elaborate.cxx @@ -1426,6 +1426,7 @@ void systemtap_session::print_error (const semantic_error& e) { string message_str[2]; + string align_semantic_error (" "); // NB: we don't print error messages during listing mode. if (listing_mode) return; @@ -1462,6 +1463,12 @@ systemtap_session::print_error (const semantic_error& e) { seen_errors.insert (message_str[1]); cerr << message_str[0]; + + if (e.tok1) + print_error_source (cerr, align_semantic_error, e.tok1); + + if (e.tok2) + print_error_source (cerr, align_semantic_error, e.tok2); } if (e.chain) @@ -1469,15 +1476,51 @@ systemtap_session::print_error (const semantic_error& e) } void +systemtap_session::print_error_source (std::ostream& message, + std::string& align, const token* tok) +{ + unsigned i = 0; + unsigned line = tok->location.line; + unsigned col = tok->location.column; + string file_contents; + if (tok->location.stap_file) + file_contents = tok->location.stap_file->file_contents; + else + //No source to print, silently exit + return; + size_t start_pos = 0, end_pos = 0; + //Navigate to the appropriate line + while (i != line && end_pos != std::string::npos) + { + start_pos = end_pos; + end_pos = file_contents.find ('\n', start_pos) + 1; + i++; + } + message << align << "source: " << file_contents.substr (start_pos, end_pos-start_pos-1) << endl; + message << align << " "; + //Navigate to the appropriate column + for (i=start_pos; i<start_pos+col-1; i++) + { + if(isspace(file_contents[i])) + message << file_contents[i]; + else + message << ' '; + } + message << "^" << endl; +} + +void systemtap_session::print_warning (const string& message_str, const token* tok) { // Duplicate elimination + string align_warning (" "); if (seen_warnings.find (message_str) == seen_warnings.end()) { seen_warnings.insert (message_str); clog << "WARNING: " << message_str; if (tok) { clog << ": "; print_token (clog, tok); } clog << endl; + print_error_source (clog, align_warning, tok); } } @@ -1030,7 +1030,7 @@ main (int argc, char * const argv []) << endl; else { - // Update cache. + // Update cache. Cache cleaning is kicked off at the end of this function. if (s.use_cache) add_to_cache(s); @@ -124,17 +124,22 @@ operator << (ostream& o, const token& t) void parser::print_error (const parse_error &pe) { + string align_parse_error (" "); cerr << "parse error: " << pe.what () << endl; if (pe.tok) { cerr << "\tat: " << *pe.tok << endl; + session.print_error_source (cerr, align_parse_error, pe.tok); } else { const token* t = last_t; if (t) - cerr << "\tsaw: " << *t << endl; + { + cerr << "\tsaw: " << *t << endl; + session.print_error_source (cerr, align_parse_error, t); + } else cerr << "\tsaw: " << input_name << " EOF" << endl; } @@ -579,15 +584,27 @@ parser::peek_kw (std::string const & kw) lexer::lexer (istream& i, const string& in, systemtap_session& s): - input (i), input_name (in), + input (i), input_name (in), input_contents (""), input_pointer (0), cursor_suspend_count(0), - cursor_line (1), cursor_column (1), session(s) + cursor_line (1), cursor_column (1), session(s), + current_file (0) { char c; while(input.get(c)) input_contents.push_back(c); } +std::string +lexer::get_input_contents () +{ + return input_contents; +} + +void +lexer::set_current_file (stapfile* f) +{ + current_file = f; +} int lexer::input_peek (unsigned n) @@ -642,6 +659,8 @@ lexer::scan (bool wildcard) { token* n = new token; n->location.file = input_name; + if (current_file) + n->location.stap_file = current_file; unsigned semiskipped_p = 0; @@ -945,6 +964,8 @@ stapfile* parser::parse () { stapfile* f = new stapfile; + input.set_current_file (f); + f->file_contents = input.get_input_contents (); f->name = input_name; bool empty = true; @@ -1013,15 +1034,18 @@ parser::parse () { cerr << "Input file '" << input_name << "' is empty or missing." << endl; delete f; + input.set_current_file (0); return 0; } else if (num_errors > 0) { cerr << num_errors << " parse error(s)." << endl; delete f; + input.set_current_file (0); return 0; } + input.set_current_file (0); return f; } @@ -18,11 +18,14 @@ #include <stdexcept> #include <stdint.h> +struct stapfile; + struct source_loc { std::string file; unsigned line; unsigned column; + stapfile* stap_file; }; std::ostream& operator << (std::ostream& o, const source_loc& loc); @@ -71,6 +74,8 @@ class lexer public: token* scan (bool wildcard=false); lexer (std::istream&, const std::string&, systemtap_session&); + std::string get_input_contents (); + void set_current_file (stapfile* f); private: int input_get (); @@ -79,16 +84,15 @@ private: int input_peek (unsigned n=0); std::istream& input; std::string input_name; - std::vector<char> input_contents; + std::string input_contents; int input_pointer; // index into input_contents unsigned cursor_suspend_count; unsigned cursor_line; unsigned cursor_column; systemtap_session& session; + stapfile* current_file; }; - -struct stapfile; struct probe; struct probe_alias; struct vardecl; @@ -110,6 +110,7 @@ struct systemtap_session bool use_cache; std::string cache_path; std::string hash_path; + long cache_max; // dwarfless operation bool consult_symtab; @@ -179,6 +180,7 @@ struct systemtap_session const token* last_token; void print_token (std::ostream& o, const token* tok); void print_error (const semantic_error& e); + void print_error_source (std::ostream&, std::string&, const token* tok); void print_warning (const std::string& w, const token* tok = 0); // reNB: new POD members likely need to be explicitly cleared in the ctor. diff --git a/stap-report b/stap-report new file mode 100755 index 00000000..c2a5d070 --- /dev/null +++ b/stap-report @@ -0,0 +1,32 @@ +#!/usr/bin/python + +import sys +import time +import subprocess + +ofname = "/tmp/stapreport-" + time.strftime("%Y%m%d%H%M%S") + ".txt" +ofile = open(ofname, "w") + +def run(command): + ofile.write("== " + command + " ==\n") + ofile.flush() + p = subprocess.Popen(command, shell=True, stdout=ofile, stderr=ofile) + p.wait() + ofile.write("\n") + sys.stdout.write(".") + sys.stdout.flush() + +if __name__ == "__main__": + sys.stdout.write("Collecting data") + sys.stdout.flush() + run("stap -V") + run("which stap") + run("ls -ald `locate -r '/stap$'` `locate -r '/staprun$'`") + run("printenv | egrep '^PATH=|^LD_LIBRARY_PATH=|^SYSTEMTAP_.*='") + run("gcc -v") + run("uname -a") + run("dmesg | grep 'gcc'") + run("dmesg | egrep 'stap|systemtap' | tail -n 10") + run("cat /proc/cpuinfo | egrep 'processor|vendor_id|model name'") + run(r"rpm -qa --qf %{name}-%{version}-%{release}.%{arch}\\n | egrep 'systemtap|elfutils|kernel|gcc' | sort") + print "\nPlease include the following file in your bug report:", ofname @@ -865,7 +865,13 @@ script is translated again assuming the same conditions exist (same kernel version, same systemtap version, etc.). Cached files are stored in the .I $SYSTEMTAP_DIR/cache -directory, which may be periodically cleaned/erased by the user. +directory. The cache can be limited by having the file +.I cache_mb_limit +placed in the cache directory (shown above) containing only an ASCII +integer representing how many MiB the cache should not exceed. Note that +this is a 'soft' limit in that the cache will be cleaned after a new entry +is added, so the cache size may temporarily exceed this limit. In the +absence of this file, cache cleaning is up to the user. .SH SAFETY AND SECURITY Systemtap is an administrative tool. It exposes kernel internal data @@ -574,8 +574,10 @@ struct stapfile std::vector<functiondecl*> functions; std::vector<vardecl*> globals; std::vector<embeddedcode*> embeds; + std::string file_contents; bool privileged; - stapfile (): privileged (false) {} + stapfile (): file_contents (""), + privileged (false) {} void print (std::ostream& o) const; }; diff --git a/tapsets.cxx b/tapsets.cxx index a5a62c7a..bed27967 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -3799,7 +3799,7 @@ query_cu (Dwarf_Die * cudie, void * arg) { stringstream msg; msg << "address 0x" << hex << queryaddr - << "does not match the begining of a statement"; + << " does not match the beginning of a statement"; throw semantic_error(msg.str()); } } @@ -5062,7 +5062,14 @@ dwarf_derived_probe_group::emit_module_init (systemtap_session& s) s.op->newline() << "rc = register_kprobe (& kp->u.kp);"; s.op->newline() << "#endif"; s.op->newline(-1) << "}"; - s.op->newline() << "if (rc) {"; + s.op->newline() << "if (rc) {"; // PR6749: tolerate a failed register_*probe. + s.op->newline(1) << "sdp->registered_p = 0;"; + s.op->newline() << "_stp_warn (\"probe %s registration error (rc %d)\", probe_point, rc);"; + s.op->newline() << "rc = 0;"; // continue with other probes + // XXX: shall we increment numskipped? + s.op->newline(-1) << "}"; + +#if 0 /* pre PR 6749; XXX consider making an option */ s.op->newline(1) << "for (j=i-1; j>=0; j--) {"; // partial rollback s.op->newline(1) << "struct stap_dwarf_probe *sdp2 = & stap_dwarf_probes[j];"; s.op->newline() << "struct stap_dwarf_kprobe *kp2 = & stap_dwarf_kprobes[j];"; @@ -5076,6 +5083,8 @@ dwarf_derived_probe_group::emit_module_init (systemtap_session& s) s.op->newline(-1) << "}"; s.op->newline() << "break;"; // don't attempt to register any more probes s.op->newline(-1) << "}"; +#endif + s.op->newline() << "else sdp->registered_p = 1;"; s.op->newline(-1) << "}"; // for loop } diff --git a/testsuite/ChangeLog b/testsuite/ChangeLog index 5ddee32c..1664f8c2 100644 --- a/testsuite/ChangeLog +++ b/testsuite/ChangeLog @@ -1,3 +1,17 @@ +2008-10-10 Frank Ch. Eigler <fche@elastic.org> + + PR6749. + * systemtap.base/badkprobe.exp: New test. + +2008-10-09 Mark Wielaard <mjw@redhat.com> + + * systemtap.base/uprobes.exp: Cleanup generated files. + +2008-10-09 Rajan Arora <rarora@redhat.com> + + * systemtap.base/warnings.exp: Allow for source: lines. + * parseko/source_context.stp: New file. + 2008-10-04 Mark Wielaard <mjw@redhat.com> * systemtap.base/debugpath.exp: Guess a bit more intelligently diff --git a/testsuite/parseko/source_context.stp b/testsuite/parseko/source_context.stp new file mode 100755 index 00000000..403b203b --- /dev/null +++ b/testsuite/parseko/source_context.stp @@ -0,0 +1,5 @@ +probe timer.ms(123) +{ +printf("Probe successful\n") + eeexit () +} diff --git a/testsuite/systemtap.base/badkprobe.exp b/testsuite/systemtap.base/badkprobe.exp new file mode 100644 index 00000000..9e4de901 --- /dev/null +++ b/testsuite/systemtap.base/badkprobe.exp @@ -0,0 +1,11 @@ +set script "probe kernel.statement(-1).absolute {} probe timer.s(1) { exit() }" + +set test "bad kprobe registration" +spawn stap -g -w -e "$script" +expect { + -re "^WARNING: probe .*registration error.*" { pass $test } + eof { fail "$test (eof)" } + timeout { fail "$test (timeout)" } +} +catch {close} +catch {wait} diff --git a/testsuite/systemtap.base/bz6850.stp b/testsuite/systemtap.base/bz6850.stp index d6f41862..d6f41862 100644..100755 --- a/testsuite/systemtap.base/bz6850.stp +++ b/testsuite/systemtap.base/bz6850.stp diff --git a/testsuite/systemtap.base/uprobes.exp b/testsuite/systemtap.base/uprobes.exp index 391f5028..02a0fd33 100644 --- a/testsuite/systemtap.base/uprobes.exp +++ b/testsuite/systemtap.base/uprobes.exp @@ -16,12 +16,12 @@ close $fp if [file exists $path] then { pass "$test prep" } else { fail "$test prep" } catch {exec gcc -g -o jennie jennie.c} err -if {$err == "" && [file exists jennie]} then { pass "$test compile" } else { pass "$test compile" } +if {$err == "" && [file exists jennie]} then { pass "$test compile" } else { fail "$test compile" } set rc [stap_run_batch $srcdir/$subdir/uprobes.stp] if {$rc == 0} then { pass "$test -p4" } else { fail "$test -p4" } -if {! [installtest_p]} { untested "$test -p5"; return } +if {! [installtest_p]} { untested "$test -p5"; exec rm -f jennie.c jennie; return } spawn sudo stap -v $srcdir/$subdir/uprobes.stp -c "./jennie 1 2 3 4" set ok 0 @@ -39,3 +39,5 @@ expect { } if {$ok == 10} then { pass "$test -p5" } else { fail "$test -p5 ($ok)" } catch {wait; close} + +exec rm -f jennie.c jennie diff --git a/testsuite/systemtap.base/warnings.exp b/testsuite/systemtap.base/warnings.exp index a90860d9..4bbef40d 100644 --- a/testsuite/systemtap.base/warnings.exp +++ b/testsuite/systemtap.base/warnings.exp @@ -6,6 +6,8 @@ expect { -timeout 30 -re {^WARNING:[^\r\n]*\r\n} { incr ok; exp_continue } -re {^[^\r\n]*.ko\r\n} { incr ok; exp_continue } + -re {^[^\r]*source:[^\r\n]*\r\n} {exp_continue} + -re {^[^\r\n]*\^[^\r\n]*\r\n} {exp_continue} timeout { fail "$test (timeout)" } eof { } } |