Blame SOURCES/rhbz1643997.0007-stapbpf-assembler-WIP-6-other-call-functions-s-print.patch

e4e640
From 1d21523d10628fd14c4a16d5f85d98add9b5c0a6 Mon Sep 17 00:00:00 2001
e4e640
From: Serhei Makarov <smakarov@redhat.com>
e4e640
Date: Tue, 23 Oct 2018 13:35:08 -0400
e4e640
Subject: [PATCH 07/32] stapbpf assembler WIP #6 :: other call functions
e4e640
 ({s}printf and tapset)
e4e640
e4e640
Only very limited support for tapset functions (restricted to exit()
e4e640
for now) due to the difficulty of resolving symbols after the semantic
e4e640
pass is already completed. Could address this in the future.
e4e640
e4e640
* bpf_internal.h (program::use_tmp_space): check for overflow.
e4e640
(globals::session): new field for systemtap_session (used by function lookup).
e4e640
e4e640
* bpf_translate.cxx (asm_stmt::has_jmp_target): new field.
e4e640
(operator <<): printing rules for alloc, call.
e4e640
(bpf_unparser::parse_asm_stmt): remove printf/error, BUGFIX alloc, call, string literal.
e4e640
Also calculate has_jmp_target in the resulting stmt.
e4e640
(bpf_unparser::visit_embeddedcode): handle printf, sprintf and exit().
e4e640
Also fix the way fallthrough fields are populated to avoid spurious extra jump.
e4e640
(bpf_unparser::emit_functioncall): new function. Factors out non-staptree code.
e4e640
(bpf_unparser::visit_functioncall): use new emit_functioncall().
e4e640
(print_format_add_tag): new function on std::string. Factors out string operations.
e4e640
(bpf_unparser::emit_print_format): new function. Factors out non-staptree code.
e4e640
(bpf_unparser::visit_print_format): use new emit_print_format().
e4e640
(translate_bpf_pass): store session in globals.
e4e640
---
e4e640
 bpf-internal.h    |   5 +
e4e640
 bpf-translate.cxx | 346 +++++++++++++++++++++++++++++++++++++++---------------
e4e640
 2 files changed, 255 insertions(+), 96 deletions(-)
e4e640
e4e640
diff --git a/bpf-internal.h b/bpf-internal.h
e4e640
index 61514db9f..3041bbdf5 100644
e4e640
--- a/bpf-internal.h
e4e640
+++ b/bpf-internal.h
e4e640
@@ -30,6 +30,7 @@ namespace bpf {
e4e640
 #define MAX_BPF_STACK 512
e4e640
 #define BPF_REG_SIZE 8
e4e640
 #define BPF_MAXSTRINGLEN 64
e4e640
+// #define BPF_MAXSTRINGLEN 128 // TODO: Longer strings require storage allocator & better printf().
e4e640
 #define BPF_MAXFORMATLEN 256
e4e640
 #define BPF_MAXMAPENTRIES 2048
e4e640
 // TODO: add BPF_MAXSPRINTFLEN
e4e640
@@ -245,6 +246,7 @@ struct program
e4e640
   {
e4e640
     if (max_tmp_space < bytes)
e4e640
       max_tmp_space = bytes;
e4e640
+    assert(max_tmp_space <= MAX_BPF_STACK);
e4e640
   }
e4e640
 
e4e640
   void mk_ld(insn_inserter &ins, int sz, value *dest, value *base, int off);
e4e640
@@ -318,6 +320,9 @@ struct globals
e4e640
     EXIT = 0,
e4e640
     NUM_INTERNALS, // non-ABI
e4e640
   };
e4e640
+
e4e640
+  // Used to resolve function symbols in embedded code.
e4e640
+  systemtap_session *session;
e4e640
 };
e4e640
 } // namespace bpf
e4e640
 
e4e640
diff --git a/bpf-translate.cxx b/bpf-translate.cxx
e4e640
index af3f54b50..df22401ad 100644
e4e640
--- a/bpf-translate.cxx
e4e640
+++ b/bpf-translate.cxx
e4e640
@@ -135,8 +135,10 @@ has_side_effects (expression *e)
e4e640
   return t.side_effects;
e4e640
 }
e4e640
 
e4e640
-/* forward declaration */
e4e640
+/* forward declarations */
e4e640
 struct asm_stmt;
e4e640
+static void print_format_add_tag(std::string&);
e4e640
+static void print_format_add_tag(print_format*);
e4e640
 
e4e640
 struct bpf_unparser : public throwing_visitor
e4e640
 {
e4e640
@@ -238,6 +240,11 @@ struct bpf_unparser : public throwing_visitor
e4e640
   value *emit_bool(expression *e);
e4e640
   value *emit_context_var(bpf_context_vardecl *v);
e4e640
 
e4e640
+  value *emit_functioncall(functiondecl *f, const std::vector<value *> &args);
e4e640
+  value *emit_print_format(const std::string &format,
e4e640
+                           const std::vector<value *> &actual,
e4e640
+                           bool print_to_stream = true);
e4e640
+
e4e640
   // Used for the embedded-code assembler:
e4e640
   int64_t parse_imm (const asm_stmt &stmt, const std::string &str);
e4e640
   size_t parse_asm_stmt (embeddedcode *s, size_t start,
e4e640
@@ -594,24 +601,22 @@ bpf_unparser::visit_block (::block *s)
e4e640
 
e4e640
    <stmt> ::= label, <dest=label>;
e4e640
    <stmt> ::= alloc, <dest=reg>, <imm=imm>;
e4e640
-   <stmt> ::= call, <dest=reg>, <param[0]=function name>, <param[1]=arg>, ...;
e4e640
-   <stmt> ::= printf, <param[0]=string constant>, <param[1]=arg>, ...;
e4e640
-   <stmt> ::= error, <param[0]=string constant>, <param[1]=arg>, ...;
e4e640
+   <stmt> ::= call, <dest=optreg>, <param[0]=function name>, <param[1]=arg>, ...;
e4e640
    <stmt> ::= <code=integer opcode>, <dest=reg>, <src1=reg>,
e4e640
               <off/jmp_target=off>, <imm=imm>;
e4e640
 
e4e640
    Supported argument types include:
e4e640
 
e4e640
-   <arg> ::= <reg> | <imm>
e4e640
-   <reg> ::= <register index> | r<register index> |
e4e640
-             $<identifier> | $<integer constant> | $$ | <string constant>
e4e640
-   <imm> ::= <integer constant> | BPF_MAXSTRINGLEN
e4e640
-   <off> ::= <imm> | <jump label>
e4e640
+   <arg>    ::= <reg> | <imm>
e4e640
+   <optreg> ::= <reg> | -
e4e640
+   <reg>    ::= <register index> | r<register index> |
e4e640
+                $<identifier> | $<integer constant> | $$ | <string constant>
e4e640
+   <imm>    ::= <integer constant> | BPF_MAXSTRINGLEN | -
e4e640
+   <off>    ::= <imm> | <jump label>
e4e640
 
e4e640
 */
e4e640
 
e4e640
-// TODO
e4e640
-#define BPF_ASM_DEBUG
e4e640
+// #define BPF_ASM_DEBUG
e4e640
 
e4e640
 struct asm_stmt {
e4e640
   std::string kind;
e4e640
@@ -621,6 +626,8 @@ struct asm_stmt {
e4e640
   int64_t off, imm;
e4e640
 
e4e640
   // metadata for jmp instructions
e4e640
+  // ??? The logic around these flags could be pruned a bit.
e4e640
+  bool has_jmp_target = false;
e4e640
   bool has_fallthrough = false;
e4e640
   std::string jmp_target, fallthrough;
e4e640
 
e4e640
@@ -650,6 +657,19 @@ operator << (std::ostream& o, const asm_stmt& stmt)
e4e640
         << stmt.imm << ";"
e4e640
         << (stmt.has_fallthrough ? " +FALLTHROUGH " + stmt.fallthrough : "");
e4e640
     }
e4e640
+  else if (stmt.kind == "alloc")
e4e640
+    {
e4e640
+      o << "alloc, " << stmt.dest << ", " << stmt.imm << ";";
e4e640
+    }
e4e640
+  else if (stmt.kind == "call")
e4e640
+    {
e4e640
+      o << "call, " << stmt.dest << ", ";
e4e640
+      for (unsigned k = 0; k < stmt.params.size(); k++)
e4e640
+        {
e4e640
+          o << stmt.params[k];
e4e640
+          o << (k >= stmt.params.size() - 1 ? ";" : ", ");
e4e640
+        }
e4e640
+    }
e4e640
   else
e4e640
     o << "<unknown asm_stmt kind '" << stmt.kind << "'>";
e4e640
   return o;
e4e640
@@ -711,7 +731,7 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
e4e640
   {
e4e640
     char c = code[pos];
e4e640
     char c2 = pos + 1 < n ? code [pos + 1] : 0;
e4e640
-    if (isspace(c))
e4e640
+    if (isspace(c) && !in_string)
e4e640
       continue; // skip
e4e640
     else if (in_comment)
e4e640
       {
e4e640
@@ -799,6 +819,10 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
e4e640
         adjusted_loc.column++;
e4e640
     }
e4e640
 
e4e640
+  // Now populate the statement data.
e4e640
+
e4e640
+  stmt = asm_stmt(); // clear pre-existing data
e4e640
+
e4e640
   // set token with adjusted source location
e4e640
   stmt.tok = s->tok->adjust_location(adjusted_loc);
e4e640
   adjusted_toks.push_back(stmt.tok);
e4e640
@@ -830,14 +854,7 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
e4e640
         throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (call expects at least 2 args, found %lu)", args.size()-1), stmt.tok);
e4e640
       stmt.kind = args[0];
e4e640
       stmt.dest = args[1];
e4e640
-      for (unsigned k = 2; k < args.size(); k++)
e4e640
-        stmt.params.push_back(args[k]);
e4e640
-    }
e4e640
-  else if (args[0] == "printf" || args[0] == "error")
e4e640
-    {
e4e640
-      if (args.size() < 2)
e4e640
-        throw SEMANTIC_ERROR (_F("invalid bpf embeddedcode syntax (%s expects at least 2 args, found %lu)", args[0].c_str(), args.size()-1), stmt.tok);
e4e640
-      stmt.kind = args[0];
e4e640
+      assert(stmt.params.empty());
e4e640
       for (unsigned k = 2; k < args.size(); k++)
e4e640
         stmt.params.push_back(args[k]);
e4e640
     }
e4e640
@@ -855,16 +872,16 @@ bpf_unparser::parse_asm_stmt (embeddedcode *s, size_t start,
e4e640
       stmt.dest = args[1];
e4e640
       stmt.src1 = args[2];
e4e640
 
e4e640
-      bool has_jmp_target =
e4e640
+      stmt.has_jmp_target =
e4e640
         BPF_CLASS(stmt.code) == BPF_JMP
e4e640
         && BPF_OP(stmt.code) != BPF_EXIT
e4e640
         && BPF_OP(stmt.code) != BPF_CALL;
e4e640
       stmt.has_fallthrough = // only for jcond
e4e640
-        has_jmp_target
e4e640
+        stmt.has_jmp_target
e4e640
         && BPF_OP(stmt.code) != BPF_JA;
e4e640
       // XXX: stmt.fallthrough is computed by visit_embeddedcode
e4e640
 
e4e640
-      if (has_jmp_target)
e4e640
+      if (stmt.has_jmp_target)
e4e640
         {
e4e640
           stmt.off = 0;
e4e640
           stmt.jmp_target = args[3];
e4e640
@@ -1168,6 +1185,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
e4e640
 
e4e640
       if (after_jump != NULL && stmt.kind == "label")
e4e640
         {
e4e640
+          after_jump->has_fallthrough = true;
e4e640
           after_jump->fallthrough = stmt.dest;
e4e640
         }
e4e640
       else if (after_jump != NULL)
e4e640
@@ -1183,6 +1201,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
e4e640
           label_map[fallthrough_label] = b;
e4e640
           set_block(b);
e4e640
 
e4e640
+          after_jump->has_fallthrough = true;
e4e640
           after_jump->fallthrough = fallthrough_label;
e4e640
         }
e4e640
 
e4e640
@@ -1205,6 +1224,12 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
e4e640
           after_label = false;
e4e640
           after_jump = &*it; // be sure to refer to original, not copied stmt
e4e640
         }
e4e640
+      else if (stmt.kind == "opcode" && BPF_CLASS(stmt.code) == BPF_JMP
e4e640
+               && BPF_OP(stmt.code) != BPF_CALL /* CALL stays in the same block */)
e4e640
+        {
e4e640
+          after_label = false;
e4e640
+          after_jump = &*it; // be sure to refer to original, not copied stmt
e4e640
+        }
e4e640
       else
e4e640
         {
e4e640
           after_label = false;
e4e640
@@ -1216,7 +1241,7 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
e4e640
                             "fallthrough on final asm_stmt"), stmt.tok);
e4e640
 
e4e640
   // emit statements
e4e640
-  bool jumped_already = true;
e4e640
+  bool jumped_already = false;
e4e640
   set_block(entry_block);
e4e640
   for (std::vector<asm_stmt>::iterator it = statements.begin();
e4e640
        it != statements.end(); it++)
e4e640
@@ -1234,24 +1259,27 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
e4e640
       else if (stmt.kind == "alloc")
e4e640
         {
e4e640
           /* Reserve stack space and store its address in dest. */
e4e640
-          int ofs = this_prog.max_tmp_space + stmt.imm;
e4e640
-          value *dest = get_asm_reg(stmt, stmt.dest);
e4e640
+          int ofs = -this_prog.max_tmp_space - stmt.imm;
e4e640
           this_prog.use_tmp_space(-ofs);
e4e640
+          // ??? Consider using a storage allocator and this_prog.new_obj().
e4e640
+
e4e640
+          value *dest = get_asm_reg(stmt, stmt.dest);
e4e640
           this_prog.mk_binary(this_ins, BPF_ADD, dest,
e4e640
                               this_prog.lookup_reg(BPF_REG_10) /*frame*/,
e4e640
                               this_prog.new_imm(ofs));
e4e640
         }
e4e640
       else if (stmt.kind == "call")
e4e640
         {
e4e640
+          assert (!stmt.params.empty());
e4e640
           std::string func_name = stmt.params[0];
e4e640
           bpf_func_id hid = bpf_function_id(func_name);
e4e640
           if (hid != __BPF_FUNC_MAX_ID)
e4e640
             {
e4e640
-              // TODO diagnostic: check if the number of arguments is correct
e4e640
+              // ??? For diagnostics: check if the number of arguments is correct.
e4e640
               regno r = BPF_REG_1; unsigned nargs = 0;
e4e640
               for (unsigned k = 1; k < stmt.params.size(); k++)
e4e640
                 {
e4e640
-                  // ??? Could make params optional to avoid this part,
e4e640
+                  // ??? Could make params optional to avoid the MOVs,
e4e640
                   // ??? since the calling convention is well-known.
e4e640
                   value *from_reg = emit_asm_arg(stmt, stmt.params[k]);
e4e640
                   value *to_reg = this_prog.lookup_reg(r);
e4e640
@@ -1259,30 +1287,117 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
e4e640
                   nargs++; r++;
e4e640
                 }
e4e640
               this_prog.mk_call(this_ins, hid, nargs);
e4e640
-              this_prog.mk_mov(this_ins, get_asm_reg(stmt, stmt.dest),
e4e640
-                               this_prog.lookup_reg(BPF_REG_0) /* returnval */);
e4e640
-              // ??? Could make stmt.dest optional to avoid this extra mov,
e4e640
-              // ??? since the BPF_REG_0 convention is well-known.
e4e640
+              if (stmt.dest != "-")
e4e640
+                {
e4e640
+                  value *dest = get_asm_reg(stmt, stmt.dest);
e4e640
+                  this_prog.mk_mov(this_ins, dest,
e4e640
+                                   this_prog.lookup_reg(BPF_REG_0) /* returnval */);
e4e640
+                }
e4e640
+              // ??? For diagnostics: check other cases with stmt.dest.
e4e640
+            }
e4e640
+          else if (func_name == "printf" || func_name == "sprintf")
e4e640
+            {
e4e640
+              if (stmt.params.size() < 2)
e4e640
+                throw SEMANTIC_ERROR (_F("bpf embeddedcode '%s' expects format string, "
e4e640
+                                         "none provided", func_name.c_str()),
e4e640
+                                      stmt.tok);
e4e640
+              std::string format = stmt.params[1];
e4e640
+              if (format.size() < 2 || format[0] != '"'
e4e640
+                  || format[format.size()-1] != '"')
e4e640
+                throw SEMANTIC_ERROR (_F("bpf embeddedcode '%s' expects format string, "
e4e640
+                                         "but first parameter is not a string literal",
e4e640
+                                         func_name.c_str()), stmt.tok);
e4e640
+              format = format.substr(1,format.size()-2); /* strip quotes */
e4e640
+              format = translate_escapes(format);
e4e640
+
e4e640
+              bool print_to_stream = (func_name == "printf");
e4e640
+              if (print_to_stream)
e4e640
+                print_format_add_tag(format);
e4e640
+
e4e640
+              size_t format_bytes = format.size() + 1;
e4e640
+              if (format_bytes > BPF_MAXFORMATLEN)
e4e640
+                throw SEMANTIC_ERROR(_("Format string for print too long"), stmt.tok);
e4e640
+
e4e640
+              std::vector<value *> args;
e4e640
+              for (unsigned k = 2; k < stmt.params.size(); k++)
e4e640
+                args.push_back(emit_asm_arg(stmt, stmt.params[k]));
e4e640
+              if (args.size() > 3)
e4e640
+                throw SEMANTIC_ERROR(_NF("additional argument to print",
e4e640
+                                         "too many arguments to print (%zu)",
e4e640
+                                         args.size(), args.size()), stmt.tok);
e4e640
+
e4e640
+              value *retval = emit_print_format(format, args, print_to_stream);
e4e640
+              if (retval != NULL && stmt.dest != "-")
e4e640
+                {
e4e640
+                  value *dest = get_asm_reg(stmt, stmt.dest);
e4e640
+                  this_prog.mk_mov(this_ins, dest, retval);
e4e640
+
e4e640
+                }
e4e640
+              // ??? For diagnostics: check other cases with retval and stmt.dest.
e4e640
             }
e4e640
           else
e4e640
             {
e4e640
-              // TODO function_name = params[0];
e4e640
-              // TODO args = parse_reg(params[1]), parse_reg(params[2]), ...
e4e640
-              // TODO emit_functioncall() with good bits from visit_functioncall()
e4e640
-              throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode non-helper 'call' not yet supported"),
e4e640
-                                    stmt.tok);
e4e640
+              // TODO: Experimental code for supporting basic functioncalls.
e4e640
+              // Needs improvement and simplification to work with full generality.
e4e640
+              // But thus far, it is sufficient for calling exit().
e4e640
+#if 1
e4e640
+              if (func_name != "exit")
e4e640
+                throw SEMANTIC_ERROR(_("BUG: bpf embeddedcode non-helper 'call' operation only supports printf(),sprintf(),exit() for now"), stmt.tok);
e4e640
+#elif 1
e4e640
+              throw SEMANTIC_ERROR(_("BUG: bpf embeddedcode non-helper 'call' operation only supports printf(),sprintf() for now"), stmt.tok);
e4e640
+#endif
e4e640
+#if 1
e4e640
+              // ???: Passing systemtap_session through all the way to here
e4e640
+              // seems intrusive, but less intrusive than moving
e4e640
+              // embedded-code assembly to the translate_globals() pass.
e4e640
+              symresolution_info sym (*glob.session);
e4e640
+              functioncall *call = new functioncall;
e4e640
+              call->tok = stmt.tok;
e4e640
+              unsigned nargs = stmt.params.size() - 1;
e4e640
+              std::vector<functiondecl*> fds
e4e640
+                = sym.find_functions (call, func_name, nargs, stmt.tok);
e4e640
+              delete call;
e4e640
+
e4e640
+              if (fds.empty())
e4e640
+                // ??? Could call levenshtein_suggest() as in
e4e640
+                // symresolution_info::visit_functioncall().
e4e640
+                throw SEMANTIC_ERROR(_("bpf embeddedcode unresolved function call"), stmt.tok);
e4e640
+              if (fds.size() > 1)
e4e640
+                throw SEMANTIC_ERROR(_("bpf embeddedcode unhandled function overloading"), stmt.tok);
e4e640
+              functiondecl *f = fds[0];
e4e640
+              // TODO: Imitation of semantic_pass_symbols, does not
e4e640
+              // cover full generality of the lookup process.
e4e640
+              update_visitor_loop (*glob.session, glob.session->code_filters, f->body);
e4e640
+              sym.current_function = f; sym.current_probe = 0;
e4e640
+              f->body->visit (&sym);
e4e640
+
e4e640
+              // ??? For now, always inline the function call.
e4e640
+              for (auto i = func_calls.begin(); i != func_calls.end(); ++i)
e4e640
+                if (f == *i)
e4e640
+                  throw SEMANTIC_ERROR (_("unhandled function recursion"), stmt.tok);
e4e640
+
e4e640
+              // Collect the function arguments.
e4e640
+              std::vector<value *> args;
e4e640
+              for (unsigned k = 1; k < stmt.params.size(); k++)
e4e640
+                args.push_back(emit_asm_arg(stmt, stmt.params[k]));
e4e640
+
e4e640
+              if (args.size () != f->formal_args.size())
e4e640
+                throw SEMANTIC_ERROR(_F("bpf embeddedcode call to function '%s' "
e4e640
+                                        "expected %zu arguments, got %zu",
e4e640
+                                        func_name.c_str(),
e4e640
+                                        f->formal_args.size(), args.size()),
e4e640
+                                     stmt.tok);
e4e640
+
e4e640
+              value *retval = emit_functioncall(f, args);
e4e640
+              if (stmt.dest != "-")
e4e640
+                {
e4e640
+                  value *dest = get_asm_reg(stmt, stmt.dest);
e4e640
+                  this_prog.mk_mov(this_ins, dest, retval);
e4e640
+                }
e4e640
+              // ??? For diagnostics: check other cases with retval and stmt.dest.
e4e640
+#endif
e4e640
             }
e4e640
         }
e4e640
-      else if (stmt.kind == "printf" || stmt.kind == "error")
e4e640
-        {
e4e640
-          // TODO Note that error() should be modeled on the tapset function in tapset/logging.stp
e4e640
-          // TODO format = params[0];
e4e640
-          // TODO args = parse_reg(params[1]), parse_reg(params[2]), ...
e4e640
-          // TODO emit_print_format() with good bits from visit_print_format()
e4e640
-          // TODO if (stmt.kind == "error") emit functioncall to exit() 
e4e640
-          throw SEMANTIC_ERROR (_("BUG: bpf embeddedcode 'printf/error' not yet supported"),
e4e640
-                                stmt.tok);
e4e640
-        }
e4e640
       else if (stmt.kind == "opcode")
e4e640
         {
e4e640
           emit_asm_opcode (stmt, label_map);
e4e640
@@ -1291,9 +1406,13 @@ bpf_unparser::visit_embeddedcode (embeddedcode *s)
e4e640
         throw SEMANTIC_ERROR (_F("BUG: bpf embeddedcode contains unexpected "
e4e640
                                  "asm_stmt kind '%s'", stmt.kind.c_str()),
e4e640
                               stmt.tok);
e4e640
-      jumped_already = stmt.has_fallthrough;
e4e640
       if (stmt.has_fallthrough)
e4e640
-        set_block(label_map[stmt.fallthrough]);
e4e640
+        {
e4e640
+          jumped_already = true;
e4e640
+          set_block(label_map[stmt.fallthrough]);
e4e640
+        }
e4e640
+      else
e4e640
+        jumped_already = false;
e4e640
     }
e4e640
 
e4e640
   // housekeeping -- deallocate adjusted_toks along with statements
e4e640
@@ -1779,7 +1898,8 @@ bpf_unparser::visit_logical_and_expr (logical_and_expr* e)
e4e640
   result = emit_bool (e);
e4e640
 }
e4e640
 
e4e640
-// TODO: This matches translate.cxx, but it looks like the functionality is disabled in the parser.
e4e640
+// ??? This matches the code in translate.cxx, but it looks like the
e4e640
+// functionality has been disabled in the SystemTap parser.
e4e640
 void
e4e640
 bpf_unparser::visit_compound_expression (compound_expression* e)
e4e640
 {
e4e640
@@ -2573,30 +2693,17 @@ bpf_unparser::emit_str_arg(value *arg, int ofs, value *str)
e4e640
   emit_mov(arg, out);
e4e640
 }
e4e640
 
e4e640
-void
e4e640
-bpf_unparser::visit_functioncall (functioncall *e)
e4e640
+value *
e4e640
+bpf_unparser::emit_functioncall (functiondecl *f, const std::vector<value *>& args)
e4e640
 {
e4e640
-  // ??? For now, always inline the function call.
e4e640
-  // ??? Function overloading isn't handled.
e4e640
-  if (e->referents.size () != 1)
e4e640
-    throw SEMANTIC_ERROR (_("unhandled function overloading"), e->tok);
e4e640
-  functiondecl *f = e->referents[0];
e4e640
-
e4e640
-  for (auto i = func_calls.begin(); i != func_calls.end(); ++i)
e4e640
-    if (f == *i)
e4e640
-      throw SEMANTIC_ERROR (_("unhandled function recursion"), e->tok);
e4e640
-
e4e640
-  assert (e->args.size () == f->formal_args.size ());
e4e640
-
e4e640
   // Create a new map for the function's local variables.
e4e640
   locals_map *locals = new_locals(f->locals);
e4e640
 
e4e640
-  // Evaluate the function arguments and install in the map.
e4e640
-  for (unsigned n = e->args.size (), i = 0; i < n; ++i)
e4e640
+  // Install locals in the map.
e4e640
+  unsigned n = args.size();
e4e640
+  for (unsigned i = 0; i < n; ++i)
e4e640
     {
e4e640
-      value *r = this_prog.new_reg ();
e4e640
-      emit_mov (r, emit_expr (e->args[i]));
e4e640
-      const locals_map::value_type v (f->formal_args[i], r);
e4e640
+      const locals_map::value_type v (f->formal_args[i], args[i]);
e4e640
       auto ok = locals->insert (v);
e4e640
       assert (ok.second);
e4e640
     }
e4e640
@@ -2622,7 +2729,47 @@ bpf_unparser::visit_functioncall (functioncall *e)
e4e640
   this_locals = old_locals;
e4e640
   delete locals;
e4e640
 
e4e640
-  result = retval;
e4e640
+  return retval;
e4e640
+}
e4e640
+
e4e640
+void
e4e640
+bpf_unparser::visit_functioncall (functioncall *e)
e4e640
+{
e4e640
+  // ??? Function overloading isn't handled.
e4e640
+  if (e->referents.size () != 1)
e4e640
+    throw SEMANTIC_ERROR (_("unhandled function overloading"), e->tok);
e4e640
+  functiondecl *f = e->referents[0];
e4e640
+
e4e640
+  // ??? For now, always inline the function call.
e4e640
+  for (auto i = func_calls.begin(); i != func_calls.end(); ++i)
e4e640
+    if (f == *i)
e4e640
+      throw SEMANTIC_ERROR (_("unhandled function recursion"), e->tok);
e4e640
+
e4e640
+  // XXX: Should have been checked in earlier pass.
e4e640
+  assert (e->args.size () == f->formal_args.size ());
e4e640
+
e4e640
+  // Evaluate and collect the function arguments.
e4e640
+  std::vector<value *> args;
e4e640
+  for (unsigned n = e->args.size (), i = 0; i < n; ++i)
e4e640
+    {
e4e640
+      value *r = this_prog.new_reg ();
e4e640
+      emit_mov (r, emit_expr (e->args[i]));
e4e640
+      args.push_back(r);
e4e640
+    }
e4e640
+
e4e640
+  result = emit_functioncall(f, args);
e4e640
+}
e4e640
+
e4e640
+static void
e4e640
+print_format_add_tag(std::string& format)
e4e640
+{
e4e640
+  // surround the string with <MODNAME>...</MODNAME> to facilitate
e4e640
+  // stapbpf recovering it from debugfs.
e4e640
+  std::string start_tag = module_name;
e4e640
+  start_tag = "<" + start_tag.erase(4,1) + ">";
e4e640
+  std::string end_tag = start_tag + "\n";
e4e640
+  end_tag.insert(1, "/");
e4e640
+  format = start_tag + format + end_tag;
e4e640
 }
e4e640
 
e4e640
 static void
e4e640
@@ -2677,6 +2824,32 @@ print_format_add_tag(print_format *e)
e4e640
     }
e4e640
 }
e4e640
 
e4e640
+value *
e4e640
+bpf_unparser::emit_print_format (const std::string& format,
e4e640
+                                 const std::vector<value *>& actual,
e4e640
+                                 bool print_to_stream)
e4e640
+{
e4e640
+  size_t nargs = actual.size();
e4e640
+
e4e640
+  // The bpf verifier requires that the format string be stored on the
e4e640
+  // bpf program stack.  This is handled by bpf-opt.cxx lowering STR values.
e4e640
+  size_t format_bytes = format.size() + 1;
e4e640
+  this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1),
e4e640
+                   this_prog.new_str(format));
e4e640
+  emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes));
e4e640
+  for (size_t i = 0; i < nargs; ++i)
e4e640
+    emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]);
e4e640
+
e4e640
+  if (print_to_stream)
e4e640
+    this_prog.mk_call(this_ins, BPF_FUNC_trace_printk, nargs + 2);
e4e640
+  else
e4e640
+    {
e4e640
+      this_prog.mk_call(this_ins, BPF_FUNC_sprintf, nargs + 2);
e4e640
+      return this_prog.lookup_reg(BPF_REG_0);
e4e640
+    }
e4e640
+  return NULL;
e4e640
+}
e4e640
+
e4e640
 void
e4e640
 bpf_unparser::visit_print_format (print_format *e)
e4e640
 {
e4e640
@@ -2696,9 +2869,9 @@ bpf_unparser::visit_print_format (print_format *e)
e4e640
 			     "too many arguments to print (%zu)",
e4e640
 			     e->args.size(), e->args.size()), e->tok);
e4e640
 
e4e640
-  value *actual[3] = { NULL, NULL, NULL };
e4e640
+  std::vector<value *> actual;
e4e640
   for (i = 0; i < nargs; ++i)
e4e640
-    actual[i] = emit_expr(e->args[i]);
e4e640
+    actual.push_back(emit_expr(e->args[i]));
e4e640
 
e4e640
   std::string format;
e4e640
   if (e->print_with_format)
e4e640
@@ -2750,36 +2923,17 @@ bpf_unparser::visit_print_format (print_format *e)
e4e640
       if (e->print_with_newline)
e4e640
 	format += '\n';
e4e640
 
e4e640
-      // surround the string with <MODNAME>...</MODNAME> to facilitate
e4e640
-      // stapbpf recovering it from debugfs.
e4e640
       if (e->print_to_stream)
e4e640
-        {
e4e640
-          std::string start_tag = module_name;
e4e640
-          start_tag = "<" + start_tag.erase(4,1) + ">";
e4e640
-          std::string end_tag = start_tag + "\n";
e4e640
-          end_tag.insert(1, "/");
e4e640
-          format = start_tag + format + end_tag;
e4e640
-        }
e4e640
+        print_format_add_tag(format);
e4e640
     }
e4e640
 
e4e640
-  // The bpf verifier requires that the format string be stored on the
e4e640
-  // bpf program stack.  This is handled by bpf-opt.cxx lowering STR values.
e4e640
   size_t format_bytes = format.size() + 1;
e4e640
   if (format_bytes > BPF_MAXFORMATLEN)
e4e640
     throw SEMANTIC_ERROR(_("Format string for print too long"), e->tok);
e4e640
-  this_prog.mk_mov(this_ins, this_prog.lookup_reg(BPF_REG_1),
e4e640
-                   this_prog.new_str(format));
e4e640
-  emit_mov(this_prog.lookup_reg(BPF_REG_2), this_prog.new_imm(format_bytes));
e4e640
-  for (i = 0; i < nargs; ++i)
e4e640
-    emit_mov(this_prog.lookup_reg(BPF_REG_3 + i), actual[i]);
e4e640
 
e4e640
-  if (e->print_to_stream)
e4e640
-    this_prog.mk_call(this_ins, BPF_FUNC_trace_printk, nargs + 2);
e4e640
-  else
e4e640
-    {
e4e640
-      this_prog.mk_call(this_ins, BPF_FUNC_sprintf, nargs + 2);
e4e640
-      result = this_prog.lookup_reg(BPF_REG_0);
e4e640
-    }
e4e640
+  value *retval = emit_print_format(format, actual, e->print_to_stream);
e4e640
+  if (retval != NULL)
e4e640
+    result = retval;
e4e640
 }
e4e640
 
e4e640
 // } // anon namespace
e4e640
@@ -3409,7 +3563,7 @@ translate_bpf_pass (systemtap_session& s)
e4e640
     return 1;
e4e640
 
e4e640
   BPF_Output eo(fd);
e4e640
-  globals glob;
e4e640
+  globals glob; glob.session = &s;
e4e640
   int ret = 0;
e4e640
   const token* t = 0;
e4e640
   try
e4e640
-- 
e4e640
2.14.5
e4e640