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

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