]> sourceware.org Git - systemtap.git/blobdiff - translate.cxx
Merge branch 'master' of git://sources.redhat.com/git/systemtap
[systemtap.git] / translate.cxx
index 530b077dc0aa025af9c662aee9329223c62fe1a0..79c05d4b2ec79b23ef901f4879b813fce6ae6bbe 100644 (file)
@@ -66,6 +66,7 @@ struct c_unparser: public unparser, public visitor
   void emit_global_init (vardecl* v);
   void emit_global_param (vardecl* v);
   void emit_functionsig (functiondecl* v);
+  void emit_unprivileged_user_check ();
   void emit_module_init ();
   void emit_module_exit ();
   void emit_function (functiondecl* v);
@@ -867,12 +868,10 @@ c_unparser::emit_common_header ()
   o->newline() << "static atomic_t session_state = ATOMIC_INIT (STAP_SESSION_STARTING);";
   o->newline() << "static atomic_t error_count = ATOMIC_INIT (0);";
   o->newline() << "static atomic_t skipped_count = ATOMIC_INIT (0);";
-  o->newline() << "#ifdef STP_TIMING";
   o->newline() << "static atomic_t skipped_count_lowstack = ATOMIC_INIT (0);";
   o->newline() << "static atomic_t skipped_count_reentrant = ATOMIC_INIT (0);";
   o->newline() << "static atomic_t skipped_count_uprobe_reg = ATOMIC_INIT (0);";
   o->newline() << "static atomic_t skipped_count_uprobe_unreg = ATOMIC_INIT (0);";
-  o->newline() << "#endif";
   o->newline();
   o->newline() << "struct context {";
   o->newline(1) << "atomic_t busy;";
@@ -920,6 +919,7 @@ c_unparser::emit_common_header ()
       ostringstream oss;
       oss << "c->statp = & time_" << dp->basest()->name << ";" << endl;  // -t anti-dupe
       oss << "# needs_global_locks: " << dp->needs_global_locks () << endl;
+      dp->print_dupe_stamp (oss);
       dp->body->print(oss);
       // NB: dependent probe conditions *could* be listed here, but don't need to be.
       // That's because they're only dependent on the probe body, which is already
@@ -1088,6 +1088,29 @@ c_unparser::emit_functionsig (functiondecl* v)
 }
 
 
+void
+c_unparser::emit_unprivileged_user_check ()
+{
+  // If the --unprivileged option was specified then the module
+  // will be safe for unprivileged users, if it is successfully generated,
+  // so no check need be emitted.
+  if (session->unprivileged)
+    return;
+
+  // Otherwise, generate code to check whether the user is unprivileged.
+  // If so, then generate an error and indicate that the check has failed.
+  o->newline();
+  o->newline() << "static int systemtap_unprivileged_user_check (void) {";
+  o->newline(1) << "if (! _stp_unprivileged_user)";
+  o->newline(1) << "return 0;";
+
+  o->newline(-1) << "_stp_error (\"You are attempting to run stap as an ordinary user.\");";
+  o->newline() << "_stp_error (\"Your module must be compiled using the --unprivileged option.\");";
+  o->newline() << "return 1;";
+
+  o->newline(-1) << "}\n";
+}
+
 
 void
 c_unparser::emit_module_init ()
@@ -1132,6 +1155,12 @@ c_unparser::emit_module_init ()
   o->newline() << "if (_stp_module_check()) rc = -EINVAL;";
 
   o->newline(-1) << "}";
+
+  if (! session->unprivileged) {
+    // Check whether the user is unprivileged.
+    o->newline() << "if (systemtap_unprivileged_user_check ()) rc = -EINVAL;";
+  }
+
   o->newline() << "if (rc) goto out;";
 
   o->newline() << "(void) probe_point;";
@@ -1236,6 +1265,11 @@ c_unparser::emit_module_init ()
        o->newline() << getvar (v).fini();
     }
 
+  // For any partially registered/unregistered kernel facilities.
+  o->newline() << "#ifdef STAPCONF_SYNCHRONIZE_SCHED";
+  o->newline() << "synchronize_sched();";
+  o->newline() << "#endif";
+
   o->newline() << "return rc;";
   o->newline(-1) << "}\n";
 }
@@ -1355,9 +1389,10 @@ c_unparser::emit_module_exit ()
     o->newline() << "#endif";
   }
 
-  // print final error/reentrancy counts if non-zero
+  // print final error/skipped counts if non-zero
   o->newline() << "if (atomic_read (& skipped_count) || "
-               << "atomic_read (& error_count)) {";
+               << "atomic_read (& error_count) || "
+               << "atomic_read (& skipped_count_reentrant)) {"; // PR9967
   o->newline(1) << "_stp_warn (\"Number of errors: %d, "
                 << "skipped probes: %d\\n\", "
                 << "(int) atomic_read (& error_count), "
@@ -1503,6 +1538,7 @@ c_unparser::emit_probe (derived_probe* v)
   // be very different with or without -t.
   oss << "c->statp = & time_" << v->basest()->name << ";" << endl;
 
+  v->print_dupe_stamp (oss);
   v->body->print(oss);
 
   // Since the generated C changes based on whether or not the probe
@@ -3484,7 +3520,10 @@ c_unparser_assignment::visit_symbol (symbol *e)
 void
 c_unparser::visit_target_symbol (target_symbol* e)
 {
-  throw semantic_error("cannot translate general target-symbol expression", e->tok);
+  if (!e->probe_context_var.empty())
+    o->line() << "l->" << e->probe_context_var;
+  else
+    throw semantic_error("cannot translate general cast expression", e->tok);
 }
 
 
@@ -3882,7 +3921,7 @@ c_unparser_assignment::visit_arrayindex (arrayindex *e)
          assert (rvalue->type == pe_long);
 
          mapvar mvar = parent->getmap (array->referent, e->tok);
-         // o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
+         o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
          o->newline() << mvar.add (idx, rvar) << ";";
           res = rvar;
          // no need for these dummy assignments
@@ -3892,7 +3931,7 @@ c_unparser_assignment::visit_arrayindex (arrayindex *e)
       else
        {
          mapvar mvar = parent->getmap (array->referent, e->tok);
-         // o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
+         o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
          if (op != "=") // don't bother fetch slot if we will just overwrite it
            parent->c_assign (lvar, mvar.get(idx), e->tok);
          c_assignop (res, lvar, rvar, e->tok);
@@ -4163,7 +4202,8 @@ c_unparser::visit_print_format (print_format* e)
       int use_print = 0;
 
       string format_string = print_format::components_to_string(components);
-      if (tmp.size() == 0 || (tmp.size() == 1 && format_string == "%s"))
+      if ((tmp.size() == 0 && format_string.find("%%") == std::string::npos)
+          || (tmp.size() == 1 && format_string == "%s"))
        use_print = 1;
       else if (tmp.size() == 1
               && e->args[0]->tok->type == tok_string
@@ -4453,6 +4493,9 @@ dump_unwindsyms (Dwfl_Module *m,
 
   string modname = name;
 
+  if (pending_interrupts)
+    return DWARF_CB_ABORT;
+
   // skip modules/files we're not actually interested in
   if (c->session.unwindsym_modules.find(modname) == c->session.unwindsym_modules.end())
     return DWARF_CB_OK;
@@ -4474,7 +4517,7 @@ dump_unwindsyms (Dwfl_Module *m,
   // In the future, we'll also care about data symbols.
 
   int syments = dwfl_module_getsymtab(m);
-  assert(syments);
+  dwfl_assert ("Getting symbol table for " + modname, syments >= 0);
 
   //extract build-id from debuginfo file
   int build_id_len = 0;
@@ -4489,19 +4532,46 @@ dump_unwindsyms (Dwfl_Module *m,
     // see https://bugzilla.redhat.com/show_bug.cgi?id=465872
     // and http://sourceware.org/ml/systemtap/2008-q4/msg00579.html
 #ifdef _ELFUTILS_PREREQ
-#if _ELFUTILS_PREREQ(0,138)
+  #if _ELFUTILS_PREREQ(0,138)
     // Let's standardize to the buggy "end of build-id bits" behavior. 
     build_id_vaddr += build_id_len;
+  #endif
+  #if !_ELFUTILS_PREREQ(0,141)
+    #define NEED_ELFUTILS_BUILDID_WORKAROUND
+  #endif
+#else
+  #define NEED_ELFUTILS_BUILDID_WORKAROUND
 #endif
+
+    // And check for another workaround needed.
+    // see https://bugzilla.redhat.com/show_bug.cgi?id=489439
+    // and http://sourceware.org/ml/systemtap/2009-q1/msg00513.html
+#ifdef NEED_ELFUTILS_BUILDID_WORKAROUND
+    if (build_id_vaddr < base && dwfl_module_relocations (m) == 1)
+      {
+        GElf_Addr main_bias;
+        dwfl_module_getelf (m, &main_bias);
+        build_id_vaddr += main_bias;
+      }
 #endif
-        if (c->session.verbose > 1) {
-           clog << "Found build-id in " << name
-                << ", length " << build_id_len;
-           clog << ", end at 0x" << hex << build_id_vaddr
-                << dec << endl;
-        }
+    if (c->session.verbose > 1)
+      {
+        clog << "Found build-id in " << name
+             << ", length " << build_id_len;
+        clog << ", end at 0x" << hex << build_id_vaddr
+             << dec << endl;
+      }
   }
 
+  // Get the canonical path of the main file for comparison at runtime.
+  // When given directly by the user through -d or in case of the kernel
+  // name and path might differ. path should be used for matching.
+  // Use end as sanity check when resolving symbol addresses and to
+  // calculate size for .dynamic and .absolute sections.
+  const char *mainfile;
+  Dwarf_Addr start, end;
+  dwfl_module_info (m, NULL, &start, &end, NULL, NULL, &mainfile, NULL);
+
   // Look up the relocation basis for symbols
   int n = dwfl_module_relocations (m);
 
@@ -4511,15 +4581,17 @@ dump_unwindsyms (Dwfl_Module *m,
   // XXX: unfortunate duplication with tapsets.cxx:emit_address()
 
   typedef map<Dwarf_Addr,const char*> addrmap_t; // NB: plain map, sorted by address
-  vector<string> seclist; // encountered relocation bases (section names)
+  vector<pair<string,unsigned> > seclist; // encountered relocation bases
+                                       // (section names and sizes)
   map<unsigned, addrmap_t> addrmap; // per-relocation-base sorted addrmap
 
   Dwarf_Addr extra_offset = 0;
 
-  for (int i = 1 /* XXX: why not 0? */ ; i < syments; ++i)
+  for (int i = 0; i < syments; ++i)
     {
       GElf_Sym sym;
-      const char *name = dwfl_module_getsym(m, i, &sym, NULL);
+      GElf_Word shndxp;
+      const char *name = dwfl_module_getsym(m, i, &sym, &shndxp);
       if (name)
         {
           // NB: Yey, we found the kernel's _stext value.
@@ -4534,24 +4606,30 @@ dump_unwindsyms (Dwfl_Module *m,
               ki = dwfl_module_relocate_address (m, &extra_offset);
               dwfl_assert ("dwfl_module_relocate_address extra_offset",
                            ki >= 0);
+              // Sadly dwfl_module_relocate_address is broken on
+              // elfutils < 0.138, so we need to adjust for the module
+              // base address outself. (see also below).
+              extra_offset = sym.st_value - base;
               if (c->session.verbose > 2)
-                clog << "Found kernel _stext 0x" << hex << extra_offset << dec << endl;
+                clog << "Found kernel _stext extra offset 0x" << hex << extra_offset << dec << endl;
             }
 
-          // We only need the function symbols to identify kernel-mode
-          // PC's, so we omit undefined or "fake" absolute addresses.
-          // These fake absolute addresses occur in some older i386
-          // kernels to indicate they are vDSO symbols, not real
-          // functions in the kernel.
-          if (GELF_ST_TYPE (sym.st_info) == STT_FUNC &&
-              ! (sym.st_shndx == SHN_UNDEF || sym.st_shndx == SHN_ABS))
+         // We are only interested in "real" symbols.
+         // We omit symbols that have suspicious addresses (before base,
+         // or after end).
+          if ((GELF_ST_TYPE (sym.st_info) == STT_FUNC ||
+               GELF_ST_TYPE (sym.st_info) == STT_OBJECT) // PR10000: also need .data
+               && !(sym.st_shndx == SHN_UNDEF  // Value undefined,
+                   || shndxp == (GElf_Word) -1 // in a non-allocated section,
+                   || sym.st_value >= end      // beyond current module,
+                   || sym.st_value < base))    // before first section.
             {
               Dwarf_Addr sym_addr = sym.st_value;
+              Dwarf_Addr save_addr = sym_addr;
               const char *secname = NULL;
 
               if (n > 0) // only try to relocate if there exist relocation bases
                 {
-                  Dwarf_Addr save_addr = sym_addr;
                   int ki = dwfl_module_relocate_address (m, &sym_addr);
                   dwfl_assert ("dwfl_module_relocate_address", ki >= 0);
                   secname = dwfl_module_relocation_info (m, ki, NULL);
@@ -4569,6 +4647,16 @@ dump_unwindsyms (Dwfl_Module *m,
                 {
                   // This is a symbol within a (possibly relocatable)
                   // kernel image.
+                  
+                 // We only need the function symbols to identify kernel-mode
+                 // PC's, so we omit undefined or "fake" absolute addresses.
+                 // These fake absolute addresses occur in some older i386
+                 // kernels to indicate they are vDSO symbols, not real
+                 // functions in the kernel. We also omit symbols that have
+                  if (GELF_ST_TYPE (sym.st_info) == STT_FUNC
+                     && sym.st_shndx == SHN_ABS)
+                   continue;
+
                   secname = "_stext";
                   // NB: don't subtract session.sym_stext, which could be inconveniently NULL.
                   // Instead, sym_addr will get compensated later via extra_offset.
@@ -4594,10 +4682,31 @@ dump_unwindsyms (Dwfl_Module *m,
               // Compute our section number
               unsigned secidx;
               for (secidx=0; secidx<seclist.size(); secidx++)
-                if (seclist[secidx]==secname) break;
+                if (seclist[secidx].first==secname) break;
 
               if (secidx == seclist.size()) // new section name
-                seclist.push_back (secname);
+               {
+                  // absolute, dynamic or kernel have just one relocation
+                  // section, which covers the whole module address range.
+                  unsigned size;
+                  if (secidx == 0
+                      && (n == 0
+                          || (n == 1
+                              && (strcmp(secname, ".dynamic") == 0
+                                  || strcmp(secname, "_stext") == 0))))
+                    size = end - start;
+                  else
+                   {
+                     Dwarf_Addr b;
+                     Elf_Scn *scn;
+                     GElf_Shdr *shdr, shdr_mem;
+                     scn = dwfl_module_address_section (m, &save_addr, &b);
+                     assert (scn != NULL);
+                     shdr = gelf_getshdr(scn, &shdr_mem);
+                     size = shdr->sh_size;
+                   }
+                  seclist.push_back (make_pair(secname,size));
+               }
 
               (addrmap[secidx])[sym_addr] = name;
             }
@@ -4628,10 +4737,10 @@ dump_unwindsyms (Dwfl_Module *m,
       // There would be only a small benefit to warning.  A user
       // likely can't do anything about this; backtraces for the
       // affected module would just get all icky heuristicy.
-#if 0
-      c->session.print_warning ("No unwind data for " + modname
-                               + ", " + dwfl_errmsg (-1));
-#endif
+      // So only report in verbose mode.
+      if (c->session.verbose > 2)
+       c->session.print_warning ("No unwind data for " + modname
+                                 + ", " + dwfl_errmsg (-1));
     }
 
   for (unsigned secidx = 0; secidx < seclist.size(); secidx++)
@@ -4658,18 +4767,27 @@ dump_unwindsyms (Dwfl_Module *m,
     }
 
   c->output << "static struct _stp_section _stp_module_" << stpmod_idx<< "_sections[] = {\n";
+  // For the kernel, executables (ET_EXEC) or shared libraries (ET_DYN)
+  // there is just one section that covers the whole address space of
+  // the module. For kernel modules (ET_REL) there can be multiple
+  // sections that get relocated separately.
   for (unsigned secidx = 0; secidx < seclist.size(); secidx++)
     {
       c->output << "{\n"
-                << ".name = " << lex_cast_qstring(seclist[secidx]) << ",\n"
+                << ".name = " << lex_cast_qstring(seclist[secidx].first) << ",\n"
+                << ".size = 0x" << hex << seclist[secidx].second << dec << ",\n"
                 << ".symbols = _stp_module_" << stpmod_idx << "_symbols_" << secidx << ",\n"
-                << ".num_symbols = sizeof(_stp_module_" << stpmod_idx << "_symbols_" << secidx << ")/sizeof(struct _stp_symbol)\n"
+                << ".num_symbols = " << addrmap[secidx].size() << "\n"
                 << "},\n";
     }
   c->output << "};\n";
 
   c->output << "static struct _stp_module _stp_module_" << stpmod_idx << " = {\n";
   c->output << ".name = " << lex_cast_qstring (modname) << ", \n";
+
+  mainfile = canonicalize_file_name(mainfile);
+  c->output << ".path = " << lex_cast_qstring (mainfile) << ",\n";
+
   c->output << ".dwarf_module_base = 0x" << hex << base << dec << ", \n";
 
   if (unwind != NULL)
@@ -4731,6 +4849,7 @@ dump_unwindsyms (Dwfl_Module *m,
 
 // Emit symbol table & unwind data, plus any calls needed to register
 // them with the runtime.
+void emit_symbol_data_done (unwindsym_dump_context*, systemtap_session&);
 
 void
 emit_symbol_data (systemtap_session& s)
@@ -4743,6 +4862,14 @@ emit_symbol_data (systemtap_session& s)
 
   unwindsym_dump_context ctx = { s, kallsyms_out, 0, s.unwindsym_modules };
 
+  // Micro optimization, mainly to speed up tiny regression tests
+  // using just begin probe.
+  if (s.unwindsym_modules.size () == 0)
+    {
+      emit_symbol_data_done(&ctx, s);
+      return;
+    }
+
   // XXX: copied from tapsets.cxx dwflpp::, sadly
   static const char *debuginfo_path_arr = "+:.debug:/usr/lib/debug:build";
   static const char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH");
@@ -4786,6 +4913,7 @@ emit_symbol_data (systemtap_session& s)
       do
         {
           if (pending_interrupts) return;
+          if (ctx.undone_unwindsym_modules.empty()) break;
           off = dwfl_getmodules (dwfl, &dump_unwindsyms, (void *) &ctx, 0);
         }
       while (off > 0);
@@ -4800,7 +4928,8 @@ emit_symbol_data (systemtap_session& s)
     {
       NULL, /* dwfl_linux_kernel_find_elf, */
       dwfl_standard_find_debuginfo,
-      dwfl_offline_section_address,
+      NULL, /* ET_REL not supported for user space, only ET_EXEC and ET_DYN.
+              dwfl_offline_section_address, */
       (char **) & debuginfo_path
     };
 
@@ -4824,6 +4953,7 @@ emit_symbol_data (systemtap_session& s)
           do
             {
               if (pending_interrupts) return;
+              if (ctx.undone_unwindsym_modules.empty()) break;
               off = dwfl_getmodules (dwfl, &dump_unwindsyms, (void *) &ctx, 0);
             }
           while (off > 0);
@@ -4832,20 +4962,25 @@ emit_symbol_data (systemtap_session& s)
       dwfl_end(dwfl);
     }
 
+  emit_symbol_data_done (&ctx, s);
+}
 
+void
+emit_symbol_data_done (unwindsym_dump_context *ctx, systemtap_session& s)
+{
   // Print out a definition of the runtime's _stp_modules[] globals.
-  kallsyms_out << "\n";
-  kallsyms_out << "static struct _stp_module *_stp_modules [] = {\n";
-  for (unsigned i=0; i<ctx.stp_module_index; i++)
+  ctx->output << "\n";
+  ctx->output << "static struct _stp_module *_stp_modules [] = {\n";
+  for (unsigned i=0; i<ctx->stp_module_index; i++)
     {
-      kallsyms_out << "& _stp_module_" << i << ",\n";
+      ctx->output << "& _stp_module_" << i << ",\n";
     }
-  kallsyms_out << "};\n";
-  kallsyms_out << "static unsigned _stp_num_modules = " << ctx.stp_module_index << ";\n";
+  ctx->output << "};\n";
+  ctx->output << "static unsigned _stp_num_modules = " << ctx->stp_module_index << ";\n";
 
   // Some nonexistent modules may have been identified with "-d".  Note them.
-  for (set<string>::iterator it = ctx.undone_unwindsym_modules.begin();
-       it != ctx.undone_unwindsym_modules.end();
+  for (set<string>::iterator it = ctx->undone_unwindsym_modules.begin();
+       it != ctx->undone_unwindsym_modules.end();
        it ++)
     {
       s.print_warning ("missing unwind/symbol data for module '" + (*it) + "'");
@@ -4896,6 +5031,9 @@ translate_pass (systemtap_session& s)
       s.op->newline() << "#ifndef MINSTACKSPACE";
       s.op->newline() << "#define MINSTACKSPACE 1024";
       s.op->newline() << "#endif";
+      s.op->newline() << "#ifndef INTERRUPTIBLE";
+      s.op->newline() << "#define INTERRUPTIBLE 1";
+      s.op->newline() << "#endif";
 
       // Overload processing
       s.op->newline() << "#ifndef STP_OVERLOAD_INTERVAL";
@@ -4911,6 +5049,8 @@ translate_pass (systemtap_session& s)
       s.op->newline() << "#define STP_OVERLOAD";
       s.op->newline() << "#endif";
 
+      s.op->newline() << "#define STP_SKIP_BADVARS " << (s.skip_badvars ? 1 : 0);
+
       if (s.bulk_mode)
          s.op->newline() << "#define STP_BULKMODE";
 
@@ -4921,9 +5061,7 @@ translate_pass (systemtap_session& s)
        s.op->newline() << "#define STP_PERFMON";
 
       s.op->newline() << "#include \"runtime.h\"";
-      s.op->newline() << "#include \"regs.c\"";
       s.op->newline() << "#include \"stack.c\"";
-      s.op->newline() << "#include \"regs-ia64.c\"";
       s.op->newline() << "#include \"stat.c\"";
       s.op->newline() << "#include <linux/string.h>";
       s.op->newline() << "#include <linux/timer.h>";
@@ -4950,21 +5088,23 @@ translate_pass (systemtap_session& s)
           s.op->newline() << s.embeds[i]->code << "\n";
         }
 
-      s.op->newline() << "static struct {";
-      s.op->indent(1);
-      for (unsigned i=0; i<s.globals.size(); i++)
-        {
-          s.up->emit_global (s.globals[i]);
-        }
-      s.op->newline(-1) << "} global = {";
-      s.op->newline(1);
-      for (unsigned i=0; i<s.globals.size(); i++)
-        {
-          if (pending_interrupts) return 1;
-          s.up->emit_global_init (s.globals[i]);
-        }
-      s.op->newline(-1) << "};";
-      s.op->assert_0_indent();
+      if (s.globals.size()>0) {
+        s.op->newline() << "static struct {";
+        s.op->indent(1);
+        for (unsigned i=0; i<s.globals.size(); i++)
+          {
+            s.up->emit_global (s.globals[i]);
+          }
+        s.op->newline(-1) << "} global = {";
+        s.op->newline(1);
+        for (unsigned i=0; i<s.globals.size(); i++)
+          {
+            if (pending_interrupts) return 1;
+            s.up->emit_global_init (s.globals[i]);
+          }
+        s.op->newline(-1) << "};";
+        s.op->assert_0_indent();
+      }
 
       for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
        {
@@ -5000,6 +5140,9 @@ translate_pass (systemtap_session& s)
         }
       s.op->assert_0_indent();
 
+      s.op->newline();
+      s.up->emit_unprivileged_user_check ();
+      s.op->assert_0_indent();
       s.op->newline();
       s.up->emit_module_init ();
       s.op->assert_0_indent();
@@ -5009,11 +5152,11 @@ translate_pass (systemtap_session& s)
       s.op->newline();
 
       // XXX impedance mismatch
-      s.op->newline() << "static int probe_start () {";
+      s.op->newline() << "static int probe_start (void) {";
       s.op->newline(1) << "return systemtap_module_init () ? -1 : 0;";
       s.op->newline(-1) << "}";
       s.op->newline();
-      s.op->newline() << "static void probe_exit () {";
+      s.op->newline() << "static void probe_exit (void) {";
       s.op->newline(1) << "systemtap_module_exit ();";
       s.op->newline(-1) << "}";
       s.op->assert_0_indent();
This page took 0.042244 seconds and 5 git commands to generate.