From a7ed0d3e9d68f5f83e8b9f6679ce12637842b03b Mon Sep 17 00:00:00 2001 From: "Frank Ch. Eigler" Date: Tue, 18 Aug 2009 15:52:02 -0400 Subject: [PATCH] PR10518: context shrinkage with function recursion analysis feeding MAXNESTING * translate.cxx (emit_common_header, translate_pass): Use new recursion_info visitor to calculate appropriate MAXNESTING value for scripts with or without recursion. * tapsets.cxx (common_probe_entryfn_prologue): Initialize c->nesting = -1. * stap.1.in: Clarify MAXNESTING value. --- stap.1.in | 4 ++- tapsets.cxx | 2 +- translate.cxx | 88 ++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/stap.1.in b/stap.1.in index a9af3a132..a45c90a67 100644 --- a/stap.1.in +++ b/stap.1.in @@ -1042,7 +1042,9 @@ These may be overridden with the flag. A selection of these is as follows: .TP MAXNESTING -Maximum number of recursive function call levels, default 10. +Maximum number of nested function calls. Default determined by +script analysis, with a bonus 10 slots added for recursive +scripts. .TP MAXSTRINGLEN Maximum length of strings, default 128. diff --git a/tapsets.cxx b/tapsets.cxx index e12ff6bd1..3d38a3ce7 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -121,7 +121,7 @@ common_probe_entryfn_prologue (translator_output* o, string statestr, o->newline(); o->newline() << "c->last_stmt = 0;"; o->newline() << "c->last_error = 0;"; - o->newline() << "c->nesting = 0;"; + o->newline() << "c->nesting = -1;"; // NB: PR10516 packs locals[] tighter o->newline() << "c->regs = 0;"; o->newline() << "c->unwaddr = 0;"; o->newline() << "c->probe_point = " << new_pp << ";"; diff --git a/translate.cxx b/translate.cxx index 2e2716e2b..57e7e7ce2 100644 --- a/translate.cxx +++ b/translate.cxx @@ -874,7 +874,7 @@ c_unparser::emit_common_header () o->newline(1) << "atomic_t busy;"; o->newline() << "const char *probe_point;"; o->newline() << "int actionremaining;"; - o->newline() << "unsigned nesting;"; + o->newline() << "int nesting;"; o->newline() << "string_t error_buffer;"; o->newline() << "const char *last_error;"; // NB: last_error is used as a health flag within a probe. @@ -1003,7 +1003,22 @@ c_unparser::emit_common_header () } o->newline(-1) << "} function_" << c_varname (fd->name) << ";"; } - o->newline(-1) << "} locals [MAXNESTING];"; + o->newline(-1) << "} locals [MAXNESTING+1];"; + + // NB: The +1 above for extra room for outgoing arguments of next nested function. + // If MAXNESTING is set too small, the args will be written, but the MAXNESTING + // check done at c_unparser::emit_function will reject. + // + // This policy wastes memory (one row of locals[] that cannot really + // be used), but trades that for smaller code (not having to check + // c->nesting against MAXNESTING at every call site). + + // Try to catch a crazy user dude passing in -DMAXNESTING=-1, leading to a [0]-sized + // locals[] array. + o->newline() << "#if MAXNESTING < 0"; + o->newline() << "#error \"MAXNESTING must be positive\""; + o->newline() << "#endif"; + o->newline(-1) << "};\n"; o->newline() << "static void *contexts = NULL; /* alloc_percpu */\n"; @@ -1461,7 +1476,11 @@ c_unparser::emit_function (functiondecl* v) o->newline() << "c->last_stmt = " << lex_cast_qstring(*v->tok) << ";"; // check/increment nesting level - o->newline() << "if (unlikely (c->nesting+2 >= MAXNESTING)) {"; + // NB: incoming c->nesting level will be -1 (if we're called directly from a probe), + // or 0...N (if we're called from another function). Incoming parameters are already + // stored in c->locals[c->nesting+1]. See also ::emit_common_header() for more. + + o->newline() << "if (unlikely (c->nesting+1 > MAXNESTING)) {"; o->newline(1) << "c->last_error = \"MAXNESTING exceeded\";"; o->newline() << "return;"; o->newline(-1) << "} else {"; @@ -5117,6 +5136,49 @@ emit_symbol_data_done (unwindsym_dump_context *ctx, systemtap_session& s) } + + +struct recursion_info: public traversing_visitor +{ + recursion_info (systemtap_session& s): sess(s), nesting_max(0), recursive(false) {} + systemtap_session& sess; + unsigned nesting_max; + bool recursive; + std::vector current_nesting; + + void visit_functioncall (functioncall* e) { + traversing_visitor::visit_functioncall (e); // for arguments + + // check for nesting level + unsigned nesting_depth = current_nesting.size() + 1; + if (nesting_max < nesting_depth) + { + if (sess.verbose > 3) + clog << "identified max-nested function: " << e->referent->name + << " (" << nesting_depth << ")" << endl; + nesting_max = nesting_depth; + } + + // check for (direct or mutual) recursion + for (unsigned j=0; jreferent) + { + recursive = true; + if (sess.verbose > 3) + clog << "identified recursive function: " << e->referent->name << endl; + return; + } + + // non-recursive traversal + current_nesting.push_back (e->referent); + e->referent->body->visit (this); + current_nesting.pop_back (); + } +}; + + + + int translate_pass (systemtap_session& s) { @@ -5128,10 +5190,26 @@ translate_pass (systemtap_session& s) try { - // This is at the very top of the file. + recursion_info ri (s); + // NB: we start our traversal from the s.functions[] rather than the probes. + // We assume that each function is called at least once, or else it would have + // been elided already. + for (map::iterator it = s.functions.begin(); it != s.functions.end(); it++) + { + functiondecl *fd = it->second; + fd->body->visit (& ri); + } + + if (s.verbose > 1) + clog << "function recursion-analysis: max-nesting " << ri.nesting_max + << (ri.recursive ? " recursive" : " non-recursive") << endl; + unsigned nesting = ri.nesting_max + 1; /* to account for initial probe->function call */ + if (ri.recursive) nesting += 10; + + // This is at the very top of the file. s.op->newline() << "#ifndef MAXNESTING"; - s.op->newline() << "#define MAXNESTING 10"; + s.op->newline() << "#define MAXNESTING " << nesting; s.op->newline() << "#endif"; // Strings are used for storing backtraces, they are larger on 64bit -- 2.43.5