From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 24449 invoked by alias); 26 Jun 2013 05:43:11 -0000 Mailing-List: contact systemtap-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Post: List-Help: , Sender: systemtap-owner@sourceware.org Received: (qmail 24440 invoked by uid 89); 26 Jun 2013 05:43:11 -0000 X-Spam-SWARE-Status: No, score=-3.9 required=5.0 tests=AWL,BAYES_00,FREEMAIL_FROM,KHOP_THREADED,RCVD_IN_DNSWL_LOW,RCVD_IN_HOSTKARMA_YE,SPF_PASS,TW_DW autolearn=ham version=3.3.1 Received: from mail-pa0-f48.google.com (HELO mail-pa0-f48.google.com) (209.85.220.48) by sourceware.org (qpsmtpd/0.84/v0.84-167-ge50287c) with ESMTP; Wed, 26 Jun 2013 05:43:06 +0000 Received: by mail-pa0-f48.google.com with SMTP id kp12so13597693pab.7 for ; Tue, 25 Jun 2013 22:43:05 -0700 (PDT) X-Received: by 10.68.225.197 with SMTP id rm5mr2280026pbc.159.1372225384936; Tue, 25 Jun 2013 22:43:04 -0700 (PDT) Received: from w530.att.net (99-7-169-37.lightspeed.sntcca.sbcglobal.net. [99.7.169.37]) by mx.google.com with ESMTPSA id z5sm26584363pbk.0.2013.06.25.22.43.00 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Tue, 25 Jun 2013 22:43:04 -0700 (PDT) From: "Yichun Zhang (agentzh)" To: systemtap@sourceware.org Cc: Josh Stone , "Yichun Zhang (agentzh)" Subject: [PATCH v3 1/1] PR11096: Add support for the "module" argument to @var Date: Wed, 26 Jun 2013 05:43:00 -0000 Message-Id: <1372225343-3618-2-git-send-email-agentzh@gmail.com> In-Reply-To: <1372225343-3618-1-git-send-email-agentzh@gmail.com> References: <51C8EF57.2050801@redhat.com> <1372225343-3618-1-git-send-email-agentzh@gmail.com> X-SW-Source: 2013-q2/txt/msg00378.txt.bz2 From: "Yichun Zhang (agentzh)" The notation @var("varname@cuname", "module") is now supported and @var can thus effectively be used almost everywhere like the context of stap functions, probe timer.profile, and probe kernel.trace(). Just like @cast, multiple module names can be specified by separating them with ":", for example, "module1:module2:module3". And they will be attempted in turn until a match is found. Refactored the code by introducing atvar_op as suggested by Josh Stone to make the implementation cleaner. The fields "target_name" and "cu_name" have been moved from target_symbol to its subclass atvar_op. @var now searches all the CUs that matches the "cuname" specified for the variable. And when "cuname" is missing, @var just searches all the CUs. Accessing global variables in PIE and DSO via @var now works for the default (kernel) runtime. Thanks Josh Stone for reviewing this patch and providing a lot of invaluable suggestions. * parse.cxx: Add support for the optional "module" parameter to the parser. * staptree.h: Remove the "target_name" field from target_symbol and make sym_name() virtual. Define atvar_op which inherits target_symbol. Add method visit_atvar_op to the visitor classes. * staptree.cxx: Define visit_atvar_op for the visitor classes. Define methods of atvar_op. * tapsets.cxx: Define visit_atvar_op for dwarf_var_expanding_visitor, sdt_uprobe_var_expanding_visitor, and tracepoint_var_expanding_visitor. Define dwarf_atvar_expanding_visitor to run in series with dwarf_cast_expanding_visitor in dwarf_derived_probe. Add dwarf_atvar_query to handle the DWARF queres of dwarf_atvar_expanding_visitor. Postpone the processing of @var with either cu name or module name or both to dwarf_atvar_expanding_visitor in order to eliminate code duplication. * elaborate.h: Declare visit_atvar_op for typeresolution_info. void_statement_reducer. * elaborate.cxx: Define visit_atvar_op for symbol_fetcher, typeresolution_info, and void_statement_reducer. * translate.cxx: Define visit_atvar_op for c_unparser. * testsuite/systemtap.base/: Add many more test cases for @var. Signed-off-by: Yichun Zhang (agentzh) --- dwflpp.cxx | 12 +- elaborate.cxx | 48 ++++ elaborate.h | 1 + parse.cxx | 29 +- staptree.cxx | 76 +++++- staptree.h | 17 +- tapsets.cxx | 300 ++++++++++++++++----- testsuite/systemtap.base/at_var.exp | 3 + testsuite/systemtap.base/at_var_cu.exp | 57 ++++ testsuite/systemtap.base/at_var_cu.stp | 33 +++ testsuite/systemtap.base/at_var_cu_1.c | 14 + testsuite/systemtap.base/at_var_cu_2.c | 13 + testsuite/systemtap.base/at_var_cu_3.c | 13 + testsuite/systemtap.base/at_var_func.exp | 48 ++++ testsuite/systemtap.base/at_var_func.stp | 21 ++ testsuite/systemtap.base/at_var_lvalue.c | 29 ++ testsuite/systemtap.base/at_var_lvalue.exp | 34 +++ testsuite/systemtap.base/at_var_lvalue.stp | 25 ++ testsuite/systemtap.base/at_var_mark.exp | 5 +- testsuite/systemtap.base/at_var_mark_func.exp | 26 ++ testsuite/systemtap.base/at_var_mark_func.stp | 24 ++ testsuite/systemtap.base/at_var_pie.exp | 50 ++++ testsuite/systemtap.base/at_var_timer_profile.c | 27 ++ testsuite/systemtap.base/at_var_timer_profile.exp | 51 ++++ testsuite/systemtap.base/at_var_timer_profile.stp | 23 ++ testsuite/systemtap.base/at_var_tracepoint.exp | 10 + testsuite/systemtap.base/at_var_tracepoint.stp | 9 + testsuite/systemtap.base/at_var_unresolved.exp | 67 +++++ .../systemtap.base/at_var_unresolved_lvalue.exp | 42 +++ .../systemtap.base/at_var_unresolved_lvalue.stp | 5 + testsuite/systemtap.base/at_var_void_stmt.c | 27 ++ testsuite/systemtap.base/at_var_void_stmt.exp | 49 ++++ testsuite/systemtap.base/at_var_void_stmt.stp | 13 + testsuite/systemtap.base/global_var_kernel.exp | 5 +- testsuite/systemtap.base/global_var_kernel.stp | 7 + translate.cxx | 8 + 36 files changed, 1119 insertions(+), 102 deletions(-) create mode 100644 testsuite/systemtap.base/at_var_cu.exp create mode 100644 testsuite/systemtap.base/at_var_cu.stp create mode 100644 testsuite/systemtap.base/at_var_cu_1.c create mode 100644 testsuite/systemtap.base/at_var_cu_2.c create mode 100644 testsuite/systemtap.base/at_var_cu_3.c create mode 100644 testsuite/systemtap.base/at_var_func.exp create mode 100644 testsuite/systemtap.base/at_var_func.stp create mode 100644 testsuite/systemtap.base/at_var_lvalue.c create mode 100644 testsuite/systemtap.base/at_var_lvalue.exp create mode 100644 testsuite/systemtap.base/at_var_lvalue.stp create mode 100644 testsuite/systemtap.base/at_var_mark_func.exp create mode 100644 testsuite/systemtap.base/at_var_mark_func.stp create mode 100644 testsuite/systemtap.base/at_var_pie.exp create mode 100644 testsuite/systemtap.base/at_var_timer_profile.c create mode 100644 testsuite/systemtap.base/at_var_timer_profile.exp create mode 100644 testsuite/systemtap.base/at_var_timer_profile.stp create mode 100644 testsuite/systemtap.base/at_var_tracepoint.exp create mode 100644 testsuite/systemtap.base/at_var_tracepoint.stp create mode 100644 testsuite/systemtap.base/at_var_unresolved.exp create mode 100644 testsuite/systemtap.base/at_var_unresolved_lvalue.exp create mode 100644 testsuite/systemtap.base/at_var_unresolved_lvalue.stp create mode 100644 testsuite/systemtap.base/at_var_void_stmt.c create mode 100644 testsuite/systemtap.base/at_var_void_stmt.exp create mode 100644 testsuite/systemtap.base/at_var_void_stmt.stp diff --git a/dwflpp.cxx b/dwflpp.cxx index bb0d34a..cfaf5af 100644 --- a/dwflpp.cxx +++ b/dwflpp.cxx @@ -2270,7 +2270,7 @@ dwflpp::find_variable_and_frame_base (vector& scopes, { stringstream alternatives; print_locals (scopes, alternatives); - if (e->cu_name == "") + if (pc) throw semantic_error (_F("unable to find local '%s', [man error::dwarf] dieoffset %s in %s, near pc %s %s %s %s (%s)", local.c_str(), lex_cast_hex(dwarf_dieoffset(scope_die)).c_str(), @@ -2291,7 +2291,7 @@ dwflpp::find_variable_and_frame_base (vector& scopes, module_name.c_str(), (scope_die == NULL) ? "" : _("in"), (dwarf_diename(scope_die) ?: ""), - e->cu_name.c_str(), + cu_name().c_str(), (alternatives.str() == "" ? (_("")) : (_("alternatives:") @@ -2333,7 +2333,7 @@ dwflpp::find_variable_and_frame_base (vector& scopes, } // Global vars don't need (cannot use) frame base in location descriptor. - if (e->cu_name != "") + if (pc == 0) return NULL; /* We start out walking the "lexical scopes" as returned by @@ -2438,7 +2438,7 @@ dwflpp::translate_location(struct obstack *pool, // pc is in the dw address space of the current module, which is what // c_translate_location expects. get_cfa_ops wants the global dwfl address. // cfa_ops only make sense for locals. - if (e->cu_name == "") + if (pc) { Dwarf_Addr addr = pc + module_bias; cfa_ops = get_cfa_ops (addr); @@ -3013,13 +3013,13 @@ dwflpp::literal_stmt_for_local (vector& scopes, if (sess.verbose>2) { - if (e->cu_name == "") + if (pc) clog << _F("finding location for local '%s' near address %#" PRIx64 ", module bias %#" PRIx64 "\n", local.c_str(), pc, module_bias); else clog << _F("finding location for global '%s' in CU '%s'\n", - local.c_str(), e->cu_name.c_str()); + local.c_str(), cu_name().c_str()); } diff --git a/elaborate.cxx b/elaborate.cxx index c53e9f7..925bce5 100644 --- a/elaborate.cxx +++ b/elaborate.cxx @@ -1085,6 +1085,11 @@ struct symbol_fetcher e->base->visit (this); } + void visit_atvar_op (atvar_op *e) + { + sym = e; + } + void visit_cast_op (cast_op* e) { sym = e; @@ -2960,6 +2965,7 @@ struct void_statement_reducer: public update_visitor void visit_functioncall (functioncall* e); void visit_print_format (print_format* e); void visit_target_symbol (target_symbol* e); + void visit_atvar_op (atvar_op* e); void visit_cast_op (cast_op* e); void visit_defined_op (defined_op* e); @@ -3252,6 +3258,12 @@ void_statement_reducer::visit_print_format (print_format* e) } void +void_statement_reducer::visit_atvar_op (atvar_op* e) +{ + visit_target_symbol (e); +} + +void void_statement_reducer::visit_target_symbol (target_symbol* e) { // When target_symbol isn't needed, it's just as good to @@ -4587,6 +4599,42 @@ typeresolution_info::visit_target_symbol (target_symbol* e) void +typeresolution_info::visit_atvar_op (atvar_op* e) +{ + // This occurs only if an @var() was not resolved over in + // tapset.cxx land, that error was properly suppressed, and the + // later unused-expression-elimination pass didn't get rid of it + // either. So we have an @var() that is believed to be of + // genuine use, yet unresolved by the provider. + + if (session.verbose > 2) + { + clog << _("Resolution problem with "); + if (current_function) + { + clog << "function " << current_function->name << endl; + current_function->body->print (clog); + clog << endl; + } + else if (current_probe) + { + clog << "probe " << *current_probe->sole_location() << endl; + current_probe->body->print (clog); + clog << endl; + } + else + //TRANSLATORS: simply saying not an issue with a probe or function + clog << _("other") << endl; + } + + if (e->saved_conversion_error) + throw (* (e->saved_conversion_error)); + else + throw semantic_error(_("unresolved @var() expression"), e->tok); +} + + +void typeresolution_info::visit_defined_op (defined_op* e) { throw semantic_error(_("unexpected @defined"), e->tok); diff --git a/elaborate.h b/elaborate.h index 6ac5b80..c0c27bb 100644 --- a/elaborate.h +++ b/elaborate.h @@ -124,6 +124,7 @@ struct typeresolution_info: public visitor void visit_stat_op (stat_op* e); void visit_hist_op (hist_op* e); void visit_cast_op (cast_op* e); + void visit_atvar_op (atvar_op* e); void visit_defined_op (defined_op* e); void visit_entry_op (entry_op* e); void visit_perf_op (perf_op* e); diff --git a/parse.cxx b/parse.cxx index be1150f..c106b73 100644 --- a/parse.cxx +++ b/parse.cxx @@ -3646,8 +3646,6 @@ target_symbol* parser::parse_target_symbol (const token* t) target_symbol *tsym = new target_symbol; tsym->tok = t; tsym->name = t->content; - tsym->target_name = ""; - tsym->cu_name = ""; parse_target_symbol_components(tsym); tsym->addressof = addressof; return tsym; @@ -3655,20 +3653,27 @@ target_symbol* parser::parse_target_symbol (const token* t) if (t->type == tok_operator && t->content == "@var") { - target_symbol *tsym = new target_symbol; - tsym->tok = t; - tsym->name = t->content; + atvar_op *aop = new atvar_op; + aop->tok = t; + aop->name = t->content; expect_op("("); - expect_unknown(tok_string, tsym->target_name); - size_t found_at = tsym->target_name.find("@"); + expect_unknown(tok_string, aop->target_name); + size_t found_at = aop->target_name.find("@"); if (found_at != string::npos) - tsym->cu_name = tsym->target_name.substr(found_at + 1); + aop->cu_name = aop->target_name.substr(found_at + 1); else - tsym->cu_name = ""; + aop->cu_name = ""; + if (peek_op (",")) + { + swallow (); + expect_unknown (tok_string, aop->module); + } + else + aop->module = ""; expect_op(")"); - parse_target_symbol_components(tsym); - tsym->addressof = addressof; - return tsym; + parse_target_symbol_components(aop); + aop->addressof = addressof; + return aop; } throw parse_error (_("expected @cast, @var or $var")); diff --git a/staptree.cxx b/staptree.cxx index e1be9e3..82ff5fc 100644 --- a/staptree.cxx +++ b/staptree.cxx @@ -281,19 +281,22 @@ void target_symbol::chain (const semantic_error &er) this->saved_conversion_error = e; } + string target_symbol::sym_name () { - if (name == "@var") - { - if (cu_name == "") - return target_name; - else - return target_name.substr(0, target_name.length() - cu_name.length() - 1); - } + return name.substr(1); +} + + +string atvar_op::sym_name () +{ + if (cu_name == "") + return target_name; else - return name.substr(1); + return target_name.substr(0, target_name.length() - cu_name.length() - 1); } + // ------------------------------------------------------------------------ // parse tree printing @@ -416,8 +419,16 @@ void target_symbol::print (ostream& o) const if (addressof) o << "&"; o << name; - if (name == "@var") - o << "(\"" << target_name << "\")"; + for (unsigned i = 0; i < components.size(); ++i) + o << components[i]; +} + + +void atvar_op::print (ostream& o) const +{ + if (addressof) + o << "&"; + o << name << "(\"" << target_name << "\")"; for (unsigned i = 0; i < components.size(); ++i) o << components[i]; } @@ -1495,6 +1506,13 @@ cast_op::visit (visitor* u) void +atvar_op::visit (visitor* u) +{ + u->visit_atvar_op(this); +} + + +void defined_op::visit (visitor* u) { u->visit_defined_op(this); @@ -1831,6 +1849,12 @@ traversing_visitor::visit_cast_op (cast_op* e) } void +traversing_visitor::visit_atvar_op (atvar_op* e) +{ + e->visit_components (this); +} + +void traversing_visitor::visit_defined_op (defined_op* e) { e->operand->visit (this); @@ -2016,6 +2040,19 @@ varuse_collecting_visitor::visit_target_symbol (target_symbol *e) functioncall_traversing_visitor::visit_target_symbol (e); } + +void +varuse_collecting_visitor::visit_atvar_op (atvar_op *e) +{ + // Similar to visit_target_symbol + + if (is_active_lvalue (e)) + embedded_seen = true; + + functioncall_traversing_visitor::visit_atvar_op (e); +} + + void varuse_collecting_visitor::visit_cast_op (cast_op *e) { @@ -2462,6 +2499,12 @@ throwing_visitor::visit_target_symbol (target_symbol* e) } void +throwing_visitor::visit_atvar_op (atvar_op* e) +{ + throwone (e->tok); +} + +void throwing_visitor::visit_cast_op (cast_op* e) { throwone (e->tok); @@ -2754,6 +2797,13 @@ update_visitor::visit_cast_op (cast_op* e) } void +update_visitor::visit_atvar_op (atvar_op* e) +{ + e->visit_components (this); + provide (e); +} + +void update_visitor::visit_defined_op (defined_op* e) { replace (e->operand); @@ -3010,6 +3060,12 @@ deep_copy_visitor::visit_cast_op (cast_op* e) } void +deep_copy_visitor::visit_atvar_op (atvar_op* e) +{ + update_visitor::visit_atvar_op(new atvar_op(*e)); +} + +void deep_copy_visitor::visit_defined_op (defined_op* e) { update_visitor::visit_defined_op(new defined_op(*e)); diff --git a/staptree.h b/staptree.h index 8eac23d..2a68ce7 100644 --- a/staptree.h +++ b/staptree.h @@ -268,12 +268,10 @@ struct target_symbol: public symbol }; bool addressof; - std::string target_name; - std::string cu_name; std::vector components; semantic_error* saved_conversion_error; // hand-made linked list target_symbol(): addressof(false), saved_conversion_error (0) {} - std::string sym_name (); + virtual std::string sym_name (); void chain (const semantic_error& er); void print (std::ostream& o) const; void visit (visitor* u); @@ -293,6 +291,13 @@ struct cast_op: public target_symbol void visit (visitor* u); }; +struct atvar_op: public target_symbol +{ + std::string target_name, cu_name, module; + virtual std::string sym_name (); + void print (std::ostream& o) const; + void visit (visitor* u); +}; struct defined_op: public expression { @@ -791,6 +796,7 @@ struct visitor virtual void visit_stat_op (stat_op* e) = 0; virtual void visit_hist_op (hist_op* e) = 0; virtual void visit_cast_op (cast_op* e) = 0; + virtual void visit_atvar_op (atvar_op* e) = 0; virtual void visit_defined_op (defined_op* e) = 0; virtual void visit_entry_op (entry_op* e) = 0; virtual void visit_perf_op (perf_op* e) = 0; @@ -838,6 +844,7 @@ struct traversing_visitor: public visitor void visit_stat_op (stat_op* e); void visit_hist_op (hist_op* e); void visit_cast_op (cast_op* e); + void visit_atvar_op (atvar_op* e); void visit_defined_op (defined_op* e); void visit_entry_op (entry_op* e); void visit_perf_op (perf_op* e); @@ -888,6 +895,7 @@ struct varuse_collecting_visitor: public functioncall_traversing_visitor void visit_post_crement (post_crement *e); void visit_foreach_loop (foreach_loop *s); void visit_cast_op (cast_op* e); + void visit_atvar_op (atvar_op *e); void visit_defined_op (defined_op* e); void visit_entry_op (entry_op* e); void visit_perf_op (perf_op* e); @@ -943,6 +951,7 @@ struct throwing_visitor: public visitor void visit_stat_op (stat_op* e); void visit_hist_op (hist_op* e); void visit_cast_op (cast_op* e); + void visit_atvar_op (atvar_op* e); void visit_defined_op (defined_op* e); void visit_entry_op (entry_op* e); void visit_perf_op (perf_op* e); @@ -1021,6 +1030,7 @@ struct update_visitor: public visitor virtual void visit_stat_op (stat_op* e); virtual void visit_hist_op (hist_op* e); virtual void visit_cast_op (cast_op* e); + virtual void visit_atvar_op (atvar_op* e); virtual void visit_defined_op (defined_op* e); virtual void visit_entry_op (entry_op* e); virtual void visit_perf_op (perf_op* e); @@ -1079,6 +1089,7 @@ struct deep_copy_visitor: public update_visitor virtual void visit_stat_op (stat_op* e); virtual void visit_hist_op (hist_op* e); virtual void visit_cast_op (cast_op* e); + virtual void visit_atvar_op (atvar_op* e); virtual void visit_defined_op (defined_op* e); virtual void visit_entry_op (entry_op* e); virtual void visit_perf_op (perf_op* e); diff --git a/tapsets.cxx b/tapsets.cxx index 623b4c9..c67beaf 100644 --- a/tapsets.cxx +++ b/tapsets.cxx @@ -2280,11 +2280,11 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor void visit_target_symbol_saved_return (target_symbol* e); void visit_target_symbol_context (target_symbol* e); void visit_target_symbol (target_symbol* e); + void visit_atvar_op (atvar_op* e); void visit_cast_op (cast_op* e); void visit_entry_op (entry_op* e); void visit_perf_op (perf_op* e); private: - vector& getcuscope(target_symbol *e); vector& getscopes(target_symbol *e); }; @@ -3686,11 +3686,33 @@ dwarf_var_expanding_visitor::visit_target_symbol_context (target_symbol* e) void +dwarf_var_expanding_visitor::visit_atvar_op (atvar_op *e) +{ + if (e->module.empty() && e->cu_name.empty()) + { + // Fill in the module name so that even if there is no match among + // local variables, dwarf_atvar_expanding_visitor can still scan + // all the CUs in the current module for a global variable match. + e->module = q.dw.module_name; + + // process like any other local + // e->sym_name() will do the right thing + visit_target_symbol(e); + return; + } + + // Fill in our current module context if needed + if (e->module.empty()) + e->module = q.dw.module_name; + + var_expanding_visitor::visit_atvar_op(e); +} + + +void dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e) { - assert(e->name.size() > 0 - && ((e->name[0] == '$' && e->target_name == "") - || (e->name == "@var" && e->target_name != ""))); + assert(e->name.size() > 0 && (e->name[0] == '$' || e->name == "@var")); visited = true; bool defined_being_checked = (defined_ops.size() > 0 && (defined_ops.top()->operand == e)); // In this mode, we avoid hiding errors or generating extra code such as for .return saved $vars @@ -3864,65 +3886,10 @@ dwarf_var_expanding_visitor::visit_perf_op (perf_op *e) throw semantic_error(_F("perf counter '%s' not defined", e_lit_val.c_str())); } -vector& -dwarf_var_expanding_visitor::getcuscope(target_symbol *e) -{ - Dwarf_Off cu_off = 0; - const char *cu_name = NULL; - - string prefixed_srcfile = string("*/") + e->cu_name; - - Dwarf_Off off = 0; - size_t cuhl; - Dwarf_Off noff; - Dwarf_Off module_bias; - Dwarf *dw = dwfl_module_getdwarf(q.dw.module, &module_bias); - while (dwarf_nextcu (dw, off, &noff, &cuhl, NULL, NULL, NULL) == 0) - { - Dwarf_Die die_mem; - Dwarf_Die *die; - die = dwarf_offdie (dw, off + cuhl, &die_mem); - - /* We are not interested in partial units. */ - if (dwarf_tag (die) == DW_TAG_compile_unit) - { - const char *die_name = dwarf_diename (die); - if (strcmp (die_name, e->cu_name.c_str()) == 0) // Perfect match. - { - cu_name = die_name; - cu_off = off + cuhl; - break; - } - - if (fnmatch(prefixed_srcfile.c_str(), die_name, 0) == 0) - if (cu_name == NULL || strlen (die_name) < strlen (cu_name)) - { - cu_name = die_name; - cu_off = off + cuhl; - } - } - off = noff; - } - - if (cu_name == NULL) - throw semantic_error ("unable to find CU '" + e->cu_name + "'" - + " while searching for '" + e->target_name + "'", - e->tok); - - vector *cu_scope = new vector; - Dwarf_Die cu_die; - dwarf_offdie (dw, cu_off, &cu_die); - cu_scope->push_back(cu_die); - return *cu_scope; -} vector& dwarf_var_expanding_visitor::getscopes(target_symbol *e) { - // "static globals" can only be found in the top-level CU. - if (e->name == "@var" && e->cu_name != "") - return this->getcuscope(e); - if (scopes.empty()) { if(scope_die != NULL) @@ -4162,6 +4129,187 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e) } +struct dwarf_atvar_expanding_visitor: public var_expanding_visitor +{ + systemtap_session& s; + dwarf_builder& db; + + dwarf_atvar_expanding_visitor(systemtap_session& s, dwarf_builder& db): + s(s), db(db) {} + void visit_atvar_op (atvar_op* e); +}; + + +struct dwarf_atvar_query: public base_query +{ + atvar_op& e; + const bool userspace_p, lvalue; + functioncall*& result; + unsigned& tick; + const string cu_name_pattern; + + dwarf_atvar_query(dwflpp& dw, const string& module, atvar_op& e, + const bool userspace_p, const bool lvalue, + functioncall*& result, + unsigned& tick): + base_query(dw, module), e(e), + userspace_p(userspace_p), lvalue(lvalue), result(result), + tick(tick), cu_name_pattern(string("*/") + e.cu_name) {} + + void handle_query_module (); + void query_library (const char *) {} + void query_plt (const char *entry, size_t addr) {} + static int atvar_query_cu (Dwarf_Die *cudie, void *data); +}; + + +int +dwarf_atvar_query::atvar_query_cu (Dwarf_Die * cudie, void * data) +{ + dwarf_atvar_query * q = static_cast(data); + + if (! q->e.cu_name.empty()) + { + const char *die_name = dwarf_diename(cudie); + + if (strcmp(die_name, q->e.cu_name.c_str()) != 0 // Perfect match + && fnmatch(q->cu_name_pattern.c_str(), die_name, 0) != 0) + { + return DWARF_CB_OK; + } + } + + try + { + vector scopes(1, *cudie); + + q->dw.focus_on_cu (cudie); + + if (! q->e.components.empty() && + q->e.components.back().type == target_symbol::comp_pretty_print) + { + dwarf_pretty_print dpp (q->dw, scopes, 0, q->e.sym_name(), + q->userspace_p, q->e); + q->result = dpp.expand(); + return DWARF_CB_ABORT; + } + + exp_type type = pe_long; + string code = q->dw.literal_stmt_for_local (scopes, 0, q->e.sym_name(), + &q->e, q->lvalue, type); + + if (code.empty()) + return DWARF_CB_OK; + + string fname = (string(q->lvalue ? "_dwarf_tvar_set" + : "_dwarf_tvar_get") + + "_" + q->e.sym_name() + + "_" + lex_cast(q->tick++)); + + q->result = synthetic_embedded_deref_call (q->sess, fname, code, type, + q->userspace_p, q->lvalue, + &q->e); + } + catch (const semantic_error& er) + { + // Here we suppress the error because we often just have too many + // when scanning all the CUs. + return DWARF_CB_OK; + } + + if (q->result) { + return DWARF_CB_ABORT; + } + + return DWARF_CB_OK; +} + + +void +dwarf_atvar_query::handle_query_module () +{ + + dw.iterate_over_cus(atvar_query_cu, this, false); +} + + +void +dwarf_atvar_expanding_visitor::visit_atvar_op (atvar_op* e) +{ + const bool lvalue = is_active_lvalue(e); + if (lvalue && !s.guru_mode) + throw semantic_error(_("write to @var variable not permitted; " + "need stap -g"), e->tok); + + if (e->module.empty()) + e->module = "kernel"; + + functioncall* result = NULL; + + // split the module string by ':' for alternatives + vector modules; + tokenize(e->module, modules, ":"); + bool userspace_p = false; + for (unsigned i = 0; !result && i < modules.size(); ++i) + { + string& module = modules[i]; + + dwflpp* dw; + try + { + userspace_p = is_user_module(module); + if (!userspace_p) + { + // kernel or kernel module target + dw = db.get_kern_dw(s, module); + } + else + { + module = find_executable(module, "", s.sysenv); + dw = db.get_user_dw(s, module); + } + } + catch (const semantic_error& er) + { + /* ignore and go to the next module */ + continue; + } + + dwarf_atvar_query q (*dw, module, *e, userspace_p, lvalue, result, tick); + dw->iterate_over_modules(&query_module, &q); + + if (result) + { + s.unwindsym_modules.insert(module); + if (userspace_p && !s.runtime_usermode_p()) + enable_vma_tracker(s); + + if (lvalue) + { + // Provide the functioncall to our parent, so that it can be + // used to substitute for the assignment node immediately above + // us. + assert(!target_symbol_setter_functioncalls.empty()); + *(target_symbol_setter_functioncalls.top()) = result; + } + + result->visit(this); + return; + } + + /* Unable to find the variable in the current module, so we chain + * an error in atvar_op */ + semantic_error er(_F("unable to find global '%s' in %s, %s %s", + e->sym_name().c_str(), module.c_str(), + e->cu_name.empty() ? "" : _("in"), + e->cu_name.c_str())); + e->chain (er); + } + + provide(e); +} + + void dwarf_derived_probe::printsig (ostream& o) const { @@ -4722,6 +4870,9 @@ dwarf_derived_probe::register_patterns(systemtap_session& s) update_visitor *filter = new dwarf_cast_expanding_visitor(s, *dw); s.code_filters.push_back(filter); + filter = new dwarf_atvar_expanding_visitor(s, *dw); + s.code_filters.push_back(filter); + register_function_and_statement_variants(s, root->bind(TOK_KERNEL), dw, pr_privileged); register_function_and_statement_variants(s, root->bind_str(TOK_MODULE), dw, pr_privileged); root->bind(TOK_KERNEL)->bind_num(TOK_STATEMENT)->bind(TOK_ABSOLUTE) @@ -5475,6 +5626,7 @@ struct sdt_uprobe_var_expanding_visitor: public var_expanding_visitor void visit_target_symbol (target_symbol* e); void visit_target_symbol_arg (target_symbol* e); void visit_target_symbol_context (target_symbol* e); + void visit_atvar_op (atvar_op* e); void visit_cast_op (cast_op* e); }; @@ -5959,8 +6111,7 @@ sdt_uprobe_var_expanding_visitor::visit_target_symbol (target_symbol* e) try { assert(e->name.size() > 0 - && ((e->name[0] == '$' && e->target_name == "") - || (e->name == "@var" && e->target_name != ""))); + && (e->name[0] == '$' || e->name == "@var")); if (e->name == "$$name" || e->name == "$$provider" || e->name == "$$parms" || e->name == "$$vars") visit_target_symbol_context (e); @@ -5976,6 +6127,25 @@ sdt_uprobe_var_expanding_visitor::visit_target_symbol (target_symbol* e) void +sdt_uprobe_var_expanding_visitor::visit_atvar_op (atvar_op* e) +{ + if (e->module.empty() && e->cu_name.empty()) + { + // process like any other local + // e->sym_name() will do the right thing + visit_target_symbol(e); + return; + } + + // Fill in our current module context if needed + if (e->module.empty()) + e->module = process_name; + + var_expanding_visitor::visit_atvar_op(e); +} + + +void sdt_uprobe_var_expanding_visitor::visit_cast_op (cast_op* e) { // Fill in our current module context if needed @@ -9226,14 +9396,11 @@ tracepoint_var_expanding_visitor::visit_target_symbol (target_symbol* e) { try { - assert(e->name.size() > 0 - && ((e->name[0] == '$' && e->target_name == "") - || (e->name == "@var" && e->target_name != ""))); + assert(e->name.size() > 0 && e->name[0] == '$'); if (e->name == "$$name" || e->name == "$$parms" || e->name == "$$vars") visit_target_symbol_context (e); - else if (e->name == "@var") - throw semantic_error(_("cannot use @var DWARF variables in tracepoints"), e->tok); + else visit_target_symbol_arg (e); } @@ -9245,7 +9412,6 @@ tracepoint_var_expanding_visitor::visit_target_symbol (target_symbol* e) } - tracepoint_derived_probe::tracepoint_derived_probe (systemtap_session& s, dwflpp& dw, Dwarf_Die& func_die, const string& tracepoint_name, diff --git a/testsuite/systemtap.base/at_var.exp b/testsuite/systemtap.base/at_var.exp index 806ce03..88755fd 100644 --- a/testsuite/systemtap.base/at_var.exp +++ b/testsuite/systemtap.base/at_var.exp @@ -32,3 +32,6 @@ if { $res != "" } { } stap_run3 $test $srcdir/$subdir/$test.stp -c ./${test} + +# Cleanup +if { $verbose == 0 } { catch { exec rm -f $test } } diff --git a/testsuite/systemtap.base/at_var_cu.exp b/testsuite/systemtap.base/at_var_cu.exp new file mode 100644 index 0000000..6546824 --- /dev/null +++ b/testsuite/systemtap.base/at_var_cu.exp @@ -0,0 +1,57 @@ +set test "at_var_cu" +set testpath "$srcdir/$subdir" +set exefile "[pwd]/$test" +set stap_path $env(SYSTEMTAP_PATH)/stap +set staprun_path $env(SYSTEMTAP_PATH)/staprun + +# Test that @var("var@cu", "module") and @var("var@cu") search +# in all the CUs that look like a match for the pattern "cu". + +set ::result_string {foo: @var("counter", @1): 0 +foo: @var("counter@at_var_cu_2.c", @1): 0 +foo: @var("counter@at_var_cu_3.c", @1): 0 +foo: @var("counter@at_var_cu*.c", @1): 0 +bar: @var("counter", @1): 7 +bar: @var("counter@at_var_cu_2.c", @1): 7 +bar: @var("counter@at_var_cu_3.c", @1): 0 +bar: @var("counter@at_var_cu*.c", @1): 7 +baz: @var("counter", @1): 8 +baz: @var("counter@at_var_cu_2.c", @1): 8 +baz: @var("counter@at_var_cu_3.c", @1): 0 +baz: @var("counter@at_var_cu*.c", @1): 8 +bah: @var("counter", @1): 8 +bah: @var("counter@at_var_cu_2.c", @1): 8 +bah: @var("counter@at_var_cu_3.c", @1): 3 +bah: @var("counter@at_var_cu*.c", @1): 8 +bah': @var("counter@at_var_cu*.c"): 8 +bah': @var("main_global"): 5} + +# Only run on make installcheck and uprobes present. +if {! [installtest_p]} { untested "$test"; return } + +set sources \ + "$testpath/${test}_1.c $testpath/${test}_2.c $testpath/${test}_3.c" + +set res [target_compile "$sources" $exefile executable \ + "additional_flags=-O2 additional_flags=-g"] + +if { $res != "" } { + verbose "target_compile failed: $res" 2 + fail "unable to compile ${test}.c" +} + +foreach runtime [get_runtime_list] { + if {$runtime != ""} { + stap_run3 "$test ($runtime)" $srcdir/$subdir/$test.stp -c ./$test \ + $exefile --runtime=$runtime + + } elseif {[uprobes_p]} { + stap_run3 $test $srcdir/$subdir/$test.stp -c ./$test $exefile + + } else { + untested "$test" + } +} + +# Cleanup +if { $verbose == 0 } { catch { exec rm -f $test } } diff --git a/testsuite/systemtap.base/at_var_cu.stp b/testsuite/systemtap.base/at_var_cu.stp new file mode 100644 index 0000000..bc6ead9 --- /dev/null +++ b/testsuite/systemtap.base/at_var_cu.stp @@ -0,0 +1,33 @@ +function out(ctx) { + printf("%s: @var(\"counter\", @1): %d\n", ctx, @var("counter", @1)) + printf("%s: @var(\"counter@at_var_cu_2.c\", @1): %d\n", ctx, + @var("counter@at_var_cu_2.c", @1)) + printf("%s: @var(\"counter@at_var_cu_3.c\", @1): %d\n", ctx, + @var("counter@at_var_cu_3.c", @1)) + printf("%s: @var(\"counter@at_var_cu*.c\", @1): %d\n", ctx, + @var("counter@at_var_cu*.c", @1)) +} + +probe process.function("foo") +{ + out("foo") +} + +probe process.function("bar") +{ + out("bar") +} + +probe process.function("baz") +{ + out("baz") +} + +probe process.function("bah") +{ + out("bah") + printf("bah': @var(\"counter@at_var_cu*.c\"): %d\n", + @var("counter@at_var_cu*.c")) + printf("bah': @var(\"main_global\"): %d\n", + @var("main_global")) +} diff --git a/testsuite/systemtap.base/at_var_cu_1.c b/testsuite/systemtap.base/at_var_cu_1.c new file mode 100644 index 0000000..094d40b --- /dev/null +++ b/testsuite/systemtap.base/at_var_cu_1.c @@ -0,0 +1,14 @@ +extern void foo(void); +extern void bar(void); + +static int main_global = 5; + +int +main(void) +{ + foo(); + bar(); + baz(); + bah(); + return 0; +} diff --git a/testsuite/systemtap.base/at_var_cu_2.c b/testsuite/systemtap.base/at_var_cu_2.c new file mode 100644 index 0000000..5c9fb43 --- /dev/null +++ b/testsuite/systemtap.base/at_var_cu_2.c @@ -0,0 +1,13 @@ +static int counter; + +void +foo(void) +{ + counter = 7; +} + +void +bar(void) +{ + counter++; +} diff --git a/testsuite/systemtap.base/at_var_cu_3.c b/testsuite/systemtap.base/at_var_cu_3.c new file mode 100644 index 0000000..1c75641 --- /dev/null +++ b/testsuite/systemtap.base/at_var_cu_3.c @@ -0,0 +1,13 @@ +static int counter; + +void +baz(void) +{ + counter = 3; +} + +void +bah(void) +{ + counter--; +} diff --git a/testsuite/systemtap.base/at_var_func.exp b/testsuite/systemtap.base/at_var_func.exp new file mode 100644 index 0000000..f515bab --- /dev/null +++ b/testsuite/systemtap.base/at_var_func.exp @@ -0,0 +1,48 @@ +set test "at_var_func" +set testpath "$srcdir/$subdir" +set exefile "[pwd]/$test" +set stap_path $env(SYSTEMTAP_PATH)/stap +set staprun_path $env(SYSTEMTAP_PATH)/staprun + +# Test that @var("var@cu", "module") work in contextless stap functions. +# Also ensure that the module argument can be multiple module names +# separated by colon, e.g., "module1:module2:module3". +# Also test that unresolved @var() can be properly caught by @defined(). + +set ::result_string {@var("foo", @1)->bar: 42 +@var("foo@at_var.c", @1)->bar: 42 +@var("foo@at_var.c", @2)->bar: 42 +@var("foo", @1)$: {.bar=42} +@var("foo", @1)$$: {.bar=42} +@defined(@var("foo", "badmodle")->bar): NO +@defined(@var("foo", @3)->bar): NO +@defined(@var("foo@blah.c", @1)->bar): NO} + +# Only run on make installcheck and uprobes present. +if {! [installtest_p]} { untested "$test"; return } + +set res [target_compile ${testpath}/at_var.c $exefile executable \ + "additional_flags=-O2 additional_flags=-g"] + +if { $res != "" } { + verbose "target_compile failed: $res" 2 + fail "unable to compile ${test}.c" +} + +foreach runtime [get_runtime_list] { + if {$runtime != ""} { + stap_run3 "$test ($runtime)" $srcdir/$subdir/$test.stp -c ./$test \ + $exefile "$stap_path:$exefile:$staprun_path" \ + "$stap_path:$staprun_path" --runtime=$runtime + + } elseif {[uprobes_p]} { + stap_run3 $test $srcdir/$subdir/$test.stp -c ./$test $exefile \ + "$stap_path:$exefile:$staprun_path" "$stap_path:$staprun_path" + + } else { + untested "$test" + } +} + +# Cleanup +if { $verbose == 0 } { catch { exec rm -f $test } } diff --git a/testsuite/systemtap.base/at_var_func.stp b/testsuite/systemtap.base/at_var_func.stp new file mode 100644 index 0000000..3da6536 --- /dev/null +++ b/testsuite/systemtap.base/at_var_func.stp @@ -0,0 +1,21 @@ +function f() +{ + printf("@var(\"foo\", @1)->bar: %d\n", @var("foo", @1)->bar); + printf("@var(\"foo@at_var.c\", @1)->bar: %d\n", + @var("foo@at_var.c", @1)->bar); + printf("@var(\"foo@at_var.c\", @2)->bar: %d\n", + @var("foo@at_var.c", @2)->bar); + printf("@var(\"foo\", @1)$: %s\n", @var("foo", @1)$); + printf("@var(\"foo\", @1)$$: %s\n", @var("foo", @1)$$); + printf("@defined(@var(\"foo\", \"badmodle\")->bar): %s\n", + @defined(@var("foo", "badmodule")->bar) ? "YES" : "NO") + printf("@defined(@var(\"foo\", @3)->bar): %s\n", + @defined(@var("foo", @3)->bar) ? "YES" : "NO") + printf("@defined(@var(\"foo@blah.c\", @1)->bar): %s\n", + @defined(@var("foo@blah.c", @1)->bar) ? "YES" : "NO") +} + +probe process.function("sub") +{ + f() +} diff --git a/testsuite/systemtap.base/at_var_lvalue.c b/testsuite/systemtap.base/at_var_lvalue.c new file mode 100644 index 0000000..9d14e80 --- /dev/null +++ b/testsuite/systemtap.base/at_var_lvalue.c @@ -0,0 +1,29 @@ +#include + +struct foo +{ + int bar; +}; + +static struct foo foo; + +void sub(const char *file) +{ + struct timeval times[2]; + times[0].tv_sec = 1; + times[0].tv_usec = 2; + times[1].tv_sec = 3; + times[1].tv_usec = 4; + utimes (file, times); + foo.bar -= 2; /* 40 */ +} + +int +main (int argc, char **argv) +{ + foo.bar = 41 + argc; /* 42 */ + sub(argv[0]); + sub(argv[0]); + sub(argv[0]); + return 0; +} diff --git a/testsuite/systemtap.base/at_var_lvalue.exp b/testsuite/systemtap.base/at_var_lvalue.exp new file mode 100644 index 0000000..0329b30 --- /dev/null +++ b/testsuite/systemtap.base/at_var_lvalue.exp @@ -0,0 +1,34 @@ +set test "at_var_lvalue" +set testpath "$srcdir/$subdir" +set exefile "[pwd]/$test" + +# Test that @var("var") can be used as an lvalue in guru mode. +set ::result_string {0: @var("foo")->bar: 41 +0: @var("foo@at_var_lvalue.c")->bar: 40 +1: @var("foo@at_var_lvalue.c", @1)->bar: 37 +2: @var("foo@at_var_lvalue.c", @1)->bar: 34} + +# Only run on make installcheck and uprobes present. +if {! [installtest_p]} { untested "$test"; return } + +set res [target_compile ${testpath}/${test}.c ${test} executable "additional_flags=-O2 additional_flags=-g"] +if { $res != "" } { + verbose "target_compile failed: $res" 2 + fail "unable to compile ${test}.c" +} + +foreach runtime [get_runtime_list] { + if {$runtime != ""} { + stap_run3 "$test ($runtime)" $srcdir/$subdir/$test.stp -c ./${test} -g $exefile \ + --runtime=$runtime + + } elseif {[uprobes_p]} { + stap_run3 $test $srcdir/$subdir/$test.stp -c ./${test} -g $exefile + + } else { + untested "$test" + } +} + +# Cleanup +if { $verbose == 0 } { catch { exec rm -f $test } } diff --git a/testsuite/systemtap.base/at_var_lvalue.stp b/testsuite/systemtap.base/at_var_lvalue.stp new file mode 100644 index 0000000..14fdd41 --- /dev/null +++ b/testsuite/systemtap.base/at_var_lvalue.stp @@ -0,0 +1,25 @@ +global i = 0 + +function f(n) +{ + @var("foo@at_var_lvalue.c", @1)->bar = + @var("foo@at_var_lvalue.c", @1)->bar - 1 + printf("%d: @var(\"foo@at_var_lvalue.c\", @1)->bar: %d\n", n, + @var("foo@at_var_lvalue.c", @1)->bar) +} + +probe process.function("sub") +{ + if (i == 0) { + @var("foo")->bar = @var("foo")->bar - 1 + printf("0: @var(\"foo\")->bar: %d\n", @var("foo")->bar) + @var("foo@at_var_lvalue.c")->bar = @var("foo@at_var_lvalue.c")->bar - 1 + printf("0: @var(\"foo@at_var_lvalue.c\")->bar: %d\n", + @var("foo@at_var_lvalue.c")->bar) + + } else { + f(i) + } + + i++ +} diff --git a/testsuite/systemtap.base/at_var_mark.exp b/testsuite/systemtap.base/at_var_mark.exp index 5200ded..a3b1199 100644 --- a/testsuite/systemtap.base/at_var_mark.exp +++ b/testsuite/systemtap.base/at_var_mark.exp @@ -5,7 +5,7 @@ set stap_path $env(SYSTEMTAP_PATH)/stap # Check @var, even when var doesn't exist, works in process.mark probes. set output_string "pass:yes:0\r\n" -set invoke "$stap_path -p4 -e 'probe begin \{ log(\"start\")\}'" +set invoke "$stap_path -e 'probe begin \{ exit() \}'" # Only run on make installcheck and uprobes present. if {! [installtest_p]} { untested "$test"; return } @@ -17,7 +17,8 @@ foreach runtime [get_runtime_list] { } elseif {[uprobes_p]} { stap_run ${test} wait_5_secs $output_string \ - ${testpath}/${test}.stp -c $invoke + ${testpath}/${test}.stp -c $invoke + } else { untested "$test" } diff --git a/testsuite/systemtap.base/at_var_mark_func.exp b/testsuite/systemtap.base/at_var_mark_func.exp new file mode 100644 index 0000000..9335229 --- /dev/null +++ b/testsuite/systemtap.base/at_var_mark_func.exp @@ -0,0 +1,26 @@ +set test "at_var_mark_func" +set testpath "$srcdir/$subdir" + +set stap_path $env(SYSTEMTAP_PATH)/stap + +# Check @var in contextless stap functions, even when @var +# doesn't exist, works in process.mark probes. +set output_string "pass:yes:0\r\n" +set invoke "$stap_path -e 'probe begin \{ exit() \}'" + +# Only run on make installcheck and uprobes present. +if {! [installtest_p]} { untested "$test"; return } + +foreach runtime [get_runtime_list] { + if {$runtime != ""} { + stap_run "${test} ($runtime)" wait_5_secs $output_string \ + ${testpath}/${test}.stp "$stap_path" -c $invoke --runtime=$runtime + + } elseif {[uprobes_p]} { + stap_run "${test}" wait_5_secs $output_string \ + ${testpath}/${test}.stp "$stap_path" -c $invoke + + } else { + untested "$test" + } +} diff --git a/testsuite/systemtap.base/at_var_mark_func.stp b/testsuite/systemtap.base/at_var_mark_func.stp new file mode 100644 index 0000000..353a302 --- /dev/null +++ b/testsuite/systemtap.base/at_var_mark_func.stp @@ -0,0 +1,24 @@ +global more_addr = 0; + +function foo(name) +{ + if (more_addr == 0) + { + log("systemtap starting probe"); + more_addr = @var("morehelp@session.cxx", @1); + } + else + { + log("systemtap ending probe"); + name = substr(name, 0, 4); + correct = @defined(@var("no_such_var_really_not", @1)) ? "no" : "yes"; + diff = more_addr - @var("morehelp@session.cxx", @1); + printf("%s:%s:%d\n", name, correct, diff); + exit(); + } +} + +probe process.mark("pass*") +{ + foo($$name) +} diff --git a/testsuite/systemtap.base/at_var_pie.exp b/testsuite/systemtap.base/at_var_pie.exp new file mode 100644 index 0000000..2cabb8f --- /dev/null +++ b/testsuite/systemtap.base/at_var_pie.exp @@ -0,0 +1,50 @@ +set test "at_var_pie" +set testpath "$srcdir/$subdir" +set exefile "[pwd]/$test" +set stap_path $env(SYSTEMTAP_PATH)/stap +set staprun_path $env(SYSTEMTAP_PATH)/staprun + +# Test that @var("var@cu", "module") work with PIE. + +set ::result_string {@var("foo", @1)->bar: 42 +@var("foo@at_var.c", @1)->bar: 42 +@var("foo@at_var.c", @2)->bar: 42 +@var("foo", @1)$: {.bar=42} +@var("foo", @1)$$: {.bar=42} +@defined(@var("foo", "badmodle")->bar): NO +@defined(@var("foo", @3)->bar): NO +@defined(@var("foo@blah.c", @1)->bar): NO} + +# Only run on make installcheck and uprobes present. +if {! [installtest_p]} { untested "$test"; return } + +set res [target_compile ${testpath}/at_var.c $exefile executable \ + "additional_flags=-O2 additional_flags=-g additional_flags=-fPIE additional_flags=-pie"] + +if { $res != "" } { + verbose "target_compile failed: $res" 2 + fail "unable to compile ${test}.c" +} + +foreach runtime [get_runtime_list] { + if {$runtime != ""} { + if {$runtime == "dyninst"} { + # dyninst lacks support for relocations as required by PIE + setup_kfail 15052 "*-*-*" + } + + stap_run3 "$test ($runtime)" $srcdir/$subdir/at_var_func.stp -c ./$test \ + $exefile "$stap_path:$exefile:$staprun_path" \ + "$stap_path:$staprun_path" --runtime=$runtime + + } elseif {[uprobes_p]} { + stap_run3 $test $srcdir/$subdir/at_var_func.stp -c ./$test $exefile \ + "$stap_path:$exefile:$staprun_path" "$stap_path:$staprun_path" + + } else { + untested "$test" + } +} + +# Cleanup +if { $verbose == 0 } { catch { exec rm -f $test } } diff --git a/testsuite/systemtap.base/at_var_timer_profile.c b/testsuite/systemtap.base/at_var_timer_profile.c new file mode 100644 index 0000000..91e9f23 --- /dev/null +++ b/testsuite/systemtap.base/at_var_timer_profile.c @@ -0,0 +1,27 @@ +#include +#include + +struct foo +{ + int bar; +}; + +static struct foo foo; + +int +sub(int a) +{ + int i, j; + for (i = 0; i < 1000; i++) + for (j = 0; j < 1000; j++) + a += i + j + rand(); + return a; +} + +int +main (int argc, char **argv) +{ + foo.bar = 41 + argc; /* 42 */ + sub(argc); + return 0; +} diff --git a/testsuite/systemtap.base/at_var_timer_profile.exp b/testsuite/systemtap.base/at_var_timer_profile.exp new file mode 100644 index 0000000..215270d --- /dev/null +++ b/testsuite/systemtap.base/at_var_timer_profile.exp @@ -0,0 +1,51 @@ +set test "at_var_timer_profile" +set testpath "$srcdir/$subdir" +set exefile "[pwd]/$test" +set stap_path $env(SYSTEMTAP_PATH)/stap +set staprun_path $env(SYSTEMTAP_PATH)/staprun + +# Test that @var("var@cu", "module") work in the context of +# timer.profile. +# Also ensure that the module argument can be multiple module names +# separated by colon, e.g., "module1:module2:module3". +# Also test that unresolved @var() can be properly caught by @defined(). + +set ::result_string {@var("foo", @1)->bar: 42 +@var("foo@at_var_timer_profile.c", @1)->bar: 42 +@var("foo@at_var_timer_profile.c", @2)->bar: 42 +@var("foo", @1)$: {.bar=42} +@var("foo", @1)$$: {.bar=42} +@defined(@var("foo", "badmodle")): NO +@defined(@var("foo", @3)): NO} + +# Only run on make installcheck and uprobes present. +if {! [installtest_p]} { untested "$test"; return } + +set res [target_compile ${testpath}/$test.c $exefile executable "additional_flags=-O2 additional_flags=-g"] +if { $res != "" } { + verbose "target_compile failed: $res" 2 + fail "unable to compile ${test}.c" +} + +foreach runtime [get_runtime_list] { + if {$runtime != ""} { + if {$runtime == "dyninst"} { + # dyninst lacks support for probe timer.profile + setup_kfail 15540 "*-*-*" + } + + stap_run3 "$test ($runtime)" $srcdir/$subdir/$test.stp -c ./$test $exefile \ + "$stap_path:$exefile:$staprun_path" "$stap_path:$staprun_path" \ + --runtime=$runtime + + } elseif {[uprobes_p]} { + stap_run3 $test $srcdir/$subdir/$test.stp -c ./$test $exefile \ + "$stap_path:$exefile:$staprun_path" "$stap_path:$staprun_path" + + } else { + untested "$test" + } +} + +# Cleanup +if { $verbose == 0 } { catch { exec rm -f $test } } diff --git a/testsuite/systemtap.base/at_var_timer_profile.stp b/testsuite/systemtap.base/at_var_timer_profile.stp new file mode 100644 index 0000000..479f749 --- /dev/null +++ b/testsuite/systemtap.base/at_var_timer_profile.stp @@ -0,0 +1,23 @@ +global active = 0 + +probe process.function("sub") +{ + active = 1 +} + +probe timer.profile { + if (active && pid() == target()) { + printf("@var(\"foo\", @1)->bar: %d\n", @var("foo", @1)->bar); + printf("@var(\"foo@at_var_timer_profile.c\", @1)->bar: %d\n", + @var("foo@at_var_timer_profile.c", @1)->bar); + printf("@var(\"foo@at_var_timer_profile.c\", @2)->bar: %d\n", + @var("foo@at_var_timer_profile.c", @2)->bar); + printf("@var(\"foo\", @1)$: %s\n", @var("foo", @1)$); + printf("@var(\"foo\", @1)$$: %s\n", @var("foo", @1)$$); + printf("@defined(@var(\"foo\", \"badmodle\")): %s\n", + @defined(@var("foo", "badmodule")) ? "YES" : "NO") + printf("@defined(@var(\"foo\", @3)): %s\n", + @defined(@var("foo", @3)) ? "YES" : "NO") + exit() + } +} diff --git a/testsuite/systemtap.base/at_var_tracepoint.exp b/testsuite/systemtap.base/at_var_tracepoint.exp new file mode 100644 index 0000000..5dc6837 --- /dev/null +++ b/testsuite/systemtap.base/at_var_tracepoint.exp @@ -0,0 +1,10 @@ +set test "at_var_tracepoint" +set testpath "$srcdir/$subdir" + +# Check @var() is usable in the kernel tracepoint probes. +set output_string "sys_tz = {.tz_minuteswest=-?\\d+, .tz_dsttime=\\d+}" + +# Only run on make installcheck +if {! [installtest_p]} { untested "$test"; return } + +stap_run ${test} no_load $output_string ${testpath}/${test}.stp diff --git a/testsuite/systemtap.base/at_var_tracepoint.stp b/testsuite/systemtap.base/at_var_tracepoint.stp new file mode 100644 index 0000000..e1b2e22 --- /dev/null +++ b/testsuite/systemtap.base/at_var_tracepoint.stp @@ -0,0 +1,9 @@ +# Test global vars are usable in the kernel tracepoint probes. + +probe kernel.trace("sched_switch") +{ + log("systemtap starting probe"); + log("systemtap ending probe"); + printf("sys_tz = %s\n", @var("sys_tz@kernel/time.c")$$); + exit(); +} diff --git a/testsuite/systemtap.base/at_var_unresolved.exp b/testsuite/systemtap.base/at_var_unresolved.exp new file mode 100644 index 0000000..10db59d --- /dev/null +++ b/testsuite/systemtap.base/at_var_unresolved.exp @@ -0,0 +1,67 @@ +set test "at_var_unresolved" + +# Ensure that there is only one error message for an unresolved @var. + +# Only run on make installcheck +if {! [installtest_p]} { untested "$test"; return } + +# Case 1: Test @var("name"): + +spawn stap -e "probe begin { println(@var(\"var_really_not_exist\")) exit() }" +set errs 0 + +expect { + -timeout 180 + + -re {^semantic error: unable to find global 'var_really_not_exist'.*? in kernel} { + incr errs; exp_continue + } + + -re {^sematnic error: } { + incr errs; exp_continue + } + + -re {^[^\r\n]*\r\n} { exp_continue } + + timeout { fail "$test (timeout)" } + eof { } +} +wait + +if {$errs == 1} { + pass "$test ($errs)" +} { + fail "$test ($errs)" +} + + +# Case 2: Test @var("name@cuname"): + +set errs 0 + +spawn stap -e "probe begin { println(@var(\"var_really_not_exist@*.c\")) exit() }" +set ok 0 + +expect { + -timeout 180 + + -re {^semantic error: unable to find global 'var_really_not_exist'.*? in kernel, in \*\.c} { + incr ok; exp_continue + } + + -re {^sematnic error: } { + incr errs; exp_continue + } + + -re {^[^\r\n]*\r\n} { exp_continue } + + timeout { fail "$test (timeout)" } + eof { } +} +wait + +if {$ok == 1 && $errs == 0} { + pass "$test (cuname: $ok, $errs)" +} { + fail "$test (cuname: $ok, $errs)" +} diff --git a/testsuite/systemtap.base/at_var_unresolved_lvalue.exp b/testsuite/systemtap.base/at_var_unresolved_lvalue.exp new file mode 100644 index 0000000..1306797 --- /dev/null +++ b/testsuite/systemtap.base/at_var_unresolved_lvalue.exp @@ -0,0 +1,42 @@ +set test "at_var_unresolved_lvalue" +set testpath "$srcdir/$subdir" + +# Check unresolved @var used as lvalue. + +# Only run on make installcheck and uprobes present. +if {! [installtest_p]} { untested "$test"; return } +if {! [uprobes_p]} { untested "$test"; return } + +set res [target_compile ${testpath}/at_var.c $test executable \ + "additional_flags=-O2 additional_flags=-g"] + +if { $res != "" } { + verbose "target_compile failed: $res" 2 + fail "unable to compile ${test}.c" +} + +spawn stap $srcdir/$subdir/$test.stp -c ./$test $test -g +set ok 0 + +expect { + -timeout 180 + + -re {^semantic error: unable to find global 'no_such_variable' in at_var_unresolved_lvalue} { + incr ok; exp_continue + } + + -re {^[^\r\n]*\r\n} { exp_continue } + + timeout { fail "$test (timeout)" } + eof { } +} +wait + +if {$ok == 1} { + pass "$test" +} { + fail "$test" +} + +# Cleanup +if { $verbose == 0 } { catch { exec rm -f $test } } diff --git a/testsuite/systemtap.base/at_var_unresolved_lvalue.stp b/testsuite/systemtap.base/at_var_unresolved_lvalue.stp new file mode 100644 index 0000000..56fa268 --- /dev/null +++ b/testsuite/systemtap.base/at_var_unresolved_lvalue.stp @@ -0,0 +1,5 @@ +probe process.function("sub") +{ + @var("no_such_variable", @1) = 3 + exit() +} diff --git a/testsuite/systemtap.base/at_var_void_stmt.c b/testsuite/systemtap.base/at_var_void_stmt.c new file mode 100644 index 0000000..e8d5f08 --- /dev/null +++ b/testsuite/systemtap.base/at_var_void_stmt.c @@ -0,0 +1,27 @@ +#include + +struct foo +{ + int bar[1]; +}; + +static struct foo foo; + +void sub(const char *file) +{ + struct timeval times[2]; + times[0].tv_sec = 1; + times[0].tv_usec = 2; + times[1].tv_sec = 3; + times[1].tv_usec = 4; + utimes (file, times); + foo.bar[0] -= 2; /* 40 */ +} + +int +main (int argc, char **argv) +{ + foo.bar[0] = 41 + argc; /* 42 */ + sub(argv[0]); + return 0; +} diff --git a/testsuite/systemtap.base/at_var_void_stmt.exp b/testsuite/systemtap.base/at_var_void_stmt.exp new file mode 100644 index 0000000..3065ae3 --- /dev/null +++ b/testsuite/systemtap.base/at_var_void_stmt.exp @@ -0,0 +1,49 @@ +set test "at_var_void_stmt" +set testpath "$srcdir/$subdir" + +# Check the void statement reducer with @var. +set output_string ".*?enter func sub\r\n" + +# Only run on make installcheck and uprobes present. +if {! [installtest_p]} { untested "$test"; return } + +set res [target_compile ${testpath}/$test.c $test executable \ + "additional_flags=-O2 additional_flags=-g"] + +if { $res != "" } { + verbose "target_compile failed: $res" 2 + fail "unable to compile ${test}.c" +} + +spawn stap -vvv $srcdir/$subdir/$test.stp -c ./$test $test +set ok 0 +set logs 0 + +expect { + -timeout 180 + + -re {^Eliding unused target symbol operator '@var'} { + incr logs; exp_continue + } + + -re {^Eliding side-effect-free singleton block operator '@var'} { + incr logs; exp_continue + } + + -re {^count = 3\r\n} { incr ok; exp_continue; } + + -re {^[^\r\n]*\r\n} { exp_continue } + + timeout { fail "$test (timeout)" } + eof { } +} +wait + +if {$ok == 1 && $logs == 2} { + pass "$test ($ok, $logs)" +} { + fail "$test ($ok, $logs)" +} + +# Cleanup +if { $verbose == 0 } { catch { exec rm -f $test } } diff --git a/testsuite/systemtap.base/at_var_void_stmt.stp b/testsuite/systemtap.base/at_var_void_stmt.stp new file mode 100644 index 0000000..c62557d --- /dev/null +++ b/testsuite/systemtap.base/at_var_void_stmt.stp @@ -0,0 +1,13 @@ +global count = 1 + +function foo() { + count += 2 + return 0 +} + +probe process.function("sub") +{ + @var("foo", @1)->bar[foo()] + printf("count = %d\n", count) + exit() +} diff --git a/testsuite/systemtap.base/global_var_kernel.exp b/testsuite/systemtap.base/global_var_kernel.exp index f6b3c9a..a8e5010 100644 --- a/testsuite/systemtap.base/global_var_kernel.exp +++ b/testsuite/systemtap.base/global_var_kernel.exp @@ -1,8 +1,9 @@ set test "global_var_kernel" set testpath "$srcdir/$subdir" -# Check the righ "kernel/time.c" is picked up. -set output_string "sys_tz = {.tz_minuteswest=-?\\d+, .tz_dsttime=\\d+}\r\n" +# Check the righ "kernel/time.c" is picked up (both in the syscall probe +# context and the stap function context. +set output_string "sys_tz = {.tz_minuteswest=-?\\d+, .tz_dsttime=\\d+}\r\nf: {.tz_minuteswest=-?\\d+, .tz_dsttime=\\d+}" # Only run on make installcheck if {! [installtest_p]} { untested "$test"; return } diff --git a/testsuite/systemtap.base/global_var_kernel.stp b/testsuite/systemtap.base/global_var_kernel.stp index f128e34..5eb22c7 100644 --- a/testsuite/systemtap.base/global_var_kernel.stp +++ b/testsuite/systemtap.base/global_var_kernel.stp @@ -1,8 +1,15 @@ # Test the correct kernel/time.c CU is selected. + +function f() +{ + printf("f: %s\n", @var("sys_tz@kernel/time.c")$$); +} + probe syscall.open { log("systemtap starting probe"); log("systemtap ending probe"); printf("sys_tz = %s\n", @var("sys_tz@kernel/time.c")$$); + f() exit(); } diff --git a/translate.cxx b/translate.cxx index e14b492..f2ae600 100644 --- a/translate.cxx +++ b/translate.cxx @@ -202,6 +202,7 @@ struct c_unparser: public unparser, public visitor void visit_stat_op (stat_op* e); void visit_hist_op (hist_op* e); void visit_cast_op (cast_op* e); + void visit_atvar_op (atvar_op* e); void visit_defined_op (defined_op* e); void visit_entry_op (entry_op* e); void visit_perf_op (perf_op* e); @@ -4407,6 +4408,13 @@ c_unparser::visit_target_symbol (target_symbol* e) void +c_unparser::visit_atvar_op (atvar_op* e) +{ + throw semantic_error(_("cannot translate general @var expression"), e->tok); +} + + +void c_unparser::visit_cast_op (cast_op* e) { throw semantic_error(_("cannot translate general @cast expression"), e->tok); -- 1.7.11.7