translate.cxx - systemtap

Global variables defined

Data types defined

Functions defined

Macros defined

Source code

// translation pass
// Copyright (C) 2005-2014 Red Hat Inc.
// Copyright (C) 2005-2008 Intel Corporation.
// Copyright (C) 2010 Novell Corporation.
//
// This file is part of systemtap, and is free software.  You can
// redistribute it and/or modify it under the terms of the GNU General
// Public License (GPL); either version 2, or (at your option) any
// later version.

#include "config.h"
#include "staptree.h"
#include "elaborate.h"
#include "translate.h"
#include "session.h"
#include "tapsets.h"
#include "util.h"
#include "dwarf_wrappers.h"
#include "setupdwfl.h"
#include "task_finder.h"
#include "runtime/k_syms.h"
#include "dwflpp.h"
#include "stapregex.h"

#include <cstdlib>
#include <iostream>
#include <set>
#include <sstream>
#include <string>
#include <cassert>
#include <cstring>
#include <cerrno>

extern "C" {
#include <dwarf.h>
#include <elfutils/libdwfl.h>
#include <elfutils/libdw.h>
#include <ftw.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
}

// Max unwind table size (debug or eh) per module. Somewhat arbitrary
// limit (a bit more than twice the .debug_frame size of my local
// vmlinux for 2.6.31.4-83.fc12.x86_64).
// A larger value was recently found in a libxul.so build.
// ... and yet again in libxul.so, PR15162
#define MAX_UNWIND_TABLE_SIZE (16 * 1024 * 1024)

#define STAP_T_01 _("\"Array overflow, check ")
#define STAP_T_02 _("\"MAXNESTING exceeded\";")
#define STAP_T_03 _("\"division by 0\";")
#define STAP_T_04 _("\"MAXACTION exceeded\";")
#define STAP_T_05 _("\"aggregation overflow in ")
#define STAP_T_06 _("\"empty aggregate\";")
#define STAP_T_07 _("\"histogram index out of range\";")
using namespace std;

class var;
struct tmpvar;
struct aggvar;
struct mapvar;
class itervar;

struct c_unparser: public unparser, public visitor
{
  systemtap_session* session;
  translator_output* o;

  derived_probe* current_probe;
  functiondecl* current_function;
  unsigned tmpvar_counter;
  unsigned label_counter;
  unsigned action_counter;
  bool already_checked_action_count;

  varuse_collecting_visitor vcv_needs_global_locks;

  map<string, string> probe_contents;

  map<pair<bool, string>, string> compiled_printfs;

  c_unparser (systemtap_session* ss):
    session (ss), o (ss->op), current_probe(0), current_function (0),
    tmpvar_counter (0), label_counter (0), action_counter(0),
    already_checked_action_count(false), vcv_needs_global_locks (*ss) {}
  ~c_unparser () {}

  void emit_map_type_instantiations ();
  void emit_common_header ();
  void emit_global (vardecl* v);
  void emit_global_init (vardecl* v);
  void emit_global_init_type (vardecl *v);
  void emit_global_param (vardecl* v);
  void emit_global_init_setters ();
  void emit_functionsig (functiondecl* v);
  void emit_kernel_module_init ();
  void emit_kernel_module_exit ();
  void emit_module_init ();
  void emit_module_refresh ();
  void emit_module_exit ();
  void emit_function (functiondecl* v);
  void emit_lock_decls (const varuse_collecting_visitor& v);
  void emit_locks ();
  void emit_probe (derived_probe* v);
  void emit_probe_condition_initialize(derived_probe* v);
  void emit_probe_condition_update(derived_probe* v);
  void emit_unlocks ();

  void emit_compiled_printfs ();
  void emit_compiled_printf_locals ();
  void declare_compiled_printf (bool print_to_stream, const string& format);
  const string& get_compiled_printf (bool print_to_stream, const string& format);

  // for use by stats (pmap) foreach
  set<string> aggregations_active;

  // values immediately available in foreach_loop iterations
  map<string, string> foreach_loop_values;
  void visit_foreach_loop_value (visitor* vis, foreach_loop* s,
                                 const string& value="");
  bool get_foreach_loop_value (arrayindex* ai, string& value);

  // for use by looping constructs
  vector<string> loop_break_labels;
  vector<string> loop_continue_labels;

  string c_typename (exp_type e);
  virtual string c_localname (const string& e, bool mangle_oldstyle = false);
  virtual string c_globalname (const string &e);
  virtual string c_funcname (const string &e);
  string c_expression (expression* e);

  string c_arg_define (const string& e);
  string c_arg_undef (const string& e);

  void c_assign (var& lvalue, const string& rvalue, const token* tok);
  void c_assign (const string& lvalue, expression* rvalue, const string& msg);
  void c_assign (const string& lvalue, const string& rvalue, exp_type type,
                 const string& msg, const token* tok);

  void c_declare(exp_type ty, const string &ident);
  void c_declare_static(exp_type ty, const string &ident);

  void c_strcat (const string& lvalue, const string& rvalue);
  void c_strcat (const string& lvalue, expression* rvalue);

  void c_strcpy (const string& lvalue, const string& rvalue);
  void c_strcpy (const string& lvalue, expression* rvalue);

  bool is_local (vardecl const* r, token const* tok);

  tmpvar gensym(exp_type ty);
  aggvar gensym_aggregate();

  var getvar(vardecl* v, token const* tok = NULL);
  itervar getiter(symbol* s);
  mapvar getmap(vardecl* v, token const* tok = NULL);

  void load_map_indices(arrayindex* e,
            vector<tmpvar> & idx);

  var* load_aggregate (expression *e, aggvar & agg);
  string histogram_index_check(var & vase, tmpvar & idx) const;

  void collect_map_index_types(vector<vardecl* > const & vars,
                   set< pair<vector<exp_type>, exp_type> > & types);

  void record_actions (unsigned actions, const token* tok, bool update=false);

  void visit_block (block* s);
  void visit_try_block (try_block* s);
  void visit_embeddedcode (embeddedcode* s);
  void visit_null_statement (null_statement* s);
  void visit_expr_statement (expr_statement* s);
  void visit_if_statement (if_statement* s);
  void visit_for_loop (for_loop* s);
  void visit_foreach_loop (foreach_loop* s);
  void visit_return_statement (return_statement* s);
  void visit_delete_statement (delete_statement* s);
  void visit_next_statement (next_statement* s);
  void visit_break_statement (break_statement* s);
  void visit_continue_statement (continue_statement* s);
  void visit_literal_string (literal_string* e);
  void visit_literal_number (literal_number* e);
  void visit_embedded_expr (embedded_expr* e);
  void visit_binary_expression (binary_expression* e);
  void visit_unary_expression (unary_expression* e);
  void visit_pre_crement (pre_crement* e);
  void visit_post_crement (post_crement* e);
  void visit_logical_or_expr (logical_or_expr* e);
  void visit_logical_and_expr (logical_and_expr* e);
  void visit_array_in (array_in* e);
  void visit_regex_query (regex_query* e);
  void visit_comparison (comparison* e);
  void visit_concatenation (concatenation* e);
  void visit_ternary_expression (ternary_expression* e);
  void visit_assignment (assignment* e);
  void visit_symbol (symbol* e);
  void visit_target_symbol (target_symbol* e);
  void visit_arrayindex (arrayindex* e);
  void visit_functioncall (functioncall* e);
  void visit_print_format (print_format* e);
  void visit_stat_op (stat_op* e);
  void visit_hist_op (hist_op* e);
  void visit_cast_op (cast_op* e);
  void visit_autocast_op (autocast_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);
};

// A shadow visitor, meant to generate temporary variable declarations
// for function or probe bodies.  Member functions should exactly match
// the corresponding c_unparser logic and traversal sequence,
// to ensure interlocking naming and declaration of temp variables.
struct c_tmpcounter:
  public traversing_visitor
{
  c_unparser* parent;
  c_tmpcounter (c_unparser* p):
    parent (p)
  {
    parent->tmpvar_counter = 0;
  }

  void load_map_indices(arrayindex* e);
  void load_aggregate (expression *e);

  void visit_block (block *s);
  void visit_for_loop (for_loop* s);
  void visit_foreach_loop (foreach_loop* s);
  // void visit_return_statement (return_statement* s);
  void visit_delete_statement (delete_statement* s);
  // void visit_embedded_expr (embedded_expr* e);
  void visit_binary_expression (binary_expression* e);
  // void visit_unary_expression (unary_expression* e);
  void visit_pre_crement (pre_crement* e);
  void visit_post_crement (post_crement* e);
  // void visit_logical_or_expr (logical_or_expr* e);
  // void visit_logical_and_expr (logical_and_expr* e);
  void visit_array_in (array_in* e);
  void visit_regex_query (regex_query* e);
  void visit_comparison (comparison* e);
  void visit_concatenation (concatenation* e);
  // void visit_ternary_expression (ternary_expression* e);
  void visit_assignment (assignment* e);
  void visit_arrayindex (arrayindex* e);
  void visit_functioncall (functioncall* e);
  void visit_print_format (print_format* e);
  void visit_stat_op (stat_op* e);
};

struct c_unparser_assignment:
  public throwing_visitor
{
  c_unparser* parent;
  string op;
  expression* rvalue;
  bool post; // true == value saved before modify operator
  c_unparser_assignment (c_unparser* p, const string& o, expression* e):
    throwing_visitor ("invalid lvalue type"),
    parent (p), op (o), rvalue (e), post (false) {}
  c_unparser_assignment (c_unparser* p, const string& o, bool pp):
    throwing_visitor ("invalid lvalue type"),
    parent (p), op (o), rvalue (0), post (pp) {}

  void prepare_rvalue (string const & op,
               tmpvar & rval,
               token const*  tok);

  void c_assignop(tmpvar & res,
          var const & lvar,
          tmpvar const & tmp,
          token const*  tok);

  // only symbols and arrayindex nodes are possible lvalues
  void visit_symbol (symbol* e);
  void visit_arrayindex (arrayindex* e);
};


struct c_tmpcounter_assignment:
  public traversing_visitor
// leave throwing for illegal lvalues to the c_unparser_assignment instance
{
  c_tmpcounter* parent;
  const string& op;
  expression* rvalue;
  bool post; // true == value saved before modify operator
  c_tmpcounter_assignment (c_tmpcounter* p, const string& o, expression* e, bool pp = false):
    parent (p), op (o), rvalue (e), post (pp) {}

  void prepare_rvalue (tmpvar & rval);

  void c_assignop(tmpvar & res);

  // only symbols and arrayindex nodes are possible lvalues
  void visit_symbol (symbol* e);
  void visit_arrayindex (arrayindex* e);
};


ostream & operator<<(ostream & o, var const & v);


/*
  Some clarification on the runtime structures involved in statistics:

  The basic type for collecting statistics in the runtime is struct
  stat_data. This contains the count, min, max, sum, and possibly
  histogram fields.

  There are two places struct stat_data shows up.

  1. If you declare a statistic variable of any sort, you want to make
  a struct _Stat. A struct _Stat* is also called a Stat. Struct _Stat
  contains a per-CPU array of struct stat_data values, as well as a
  struct stat_data which it aggregates into. Writes into a Struct
  _Stat go into the per-CPU struct stat. Reads involve write-locking
  the struct _Stat, aggregating into its aggregate struct stat_data,
  unlocking, read-locking the struct _Stat, then reading values out of
  the aggregate and unlocking.

  2. If you declare a statistic-valued map, you want to make a
  pmap. This is a per-CPU array of maps, each of which holds struct
  stat_data values, as well as an aggregate *map*. Writes into a pmap
  go into the per-CPU map. Reads involve write-locking the pmap,
  aggregating into its aggregate map, unlocking, read-locking the
  pmap, then reading values out of its aggregate (which is a normal
  map) and unlocking.

  Because, at the moment, the runtime does not support the concept of
  a statistic which collects multiple histogram types, we may need to
  instantiate one pmap or struct _Stat for each histogram variation
  the user wants to track.
*/

class var
{

protected:
  // Required for accurate mangling:
  c_unparser *u;

  bool local;
  exp_type ty;
  statistic_decl sd;
  string name;
  bool do_mangle;

public:

  var(c_unparser *u, bool local, exp_type ty,
      statistic_decl const & sd, string const & name)
    : u(u), local(local), ty(ty), sd(sd), name(name), do_mangle(true)
  {}

  var(c_unparser *u, bool local, exp_type ty, string const & name)
    : u(u), local(local), ty(ty), name(name), do_mangle(true)
  {}

  var(c_unparser *u, bool local, exp_type ty,
      string const & name, bool do_mangle)
    : u(u), local(local), ty(ty), name(name), do_mangle(do_mangle)
  {}

  virtual ~var() {}

  bool is_local() const
  {
    return local;
  }

  statistic_decl const & sdecl() const
  {
    return sd;
  }

  void assert_hist_compatible(hist_op const & hop)
  {
    // Semantic checks in elaborate should have caught this if it was
    // false. This is just a double-check.
    switch (sd.type)
      {
      case statistic_decl::linear:
    assert(hop.htype == hist_linear);
    assert(hop.params.size() == 3);
    assert(hop.params[0] == sd.linear_low);
    assert(hop.params[1] == sd.linear_high);
    assert(hop.params[2] == sd.linear_step);
    break;
      case statistic_decl::logarithmic:
    assert(hop.htype == hist_log);
    assert(hop.params.size() == 0);
    break;
      case statistic_decl::none:
    assert(false);
      }
  }

  exp_type type() const
  {
    return ty;
  }

  string c_name() const
  {
    if (!do_mangle)
      return name;
    else if (local)
      return u->c_localname(name);
    else
      return u->c_globalname(name);
  }

  string value() const
  {
    if (local)
      return "l->" + c_name();
    else
      return "global(" + c_name() + ")";
  }

  virtual string hist() const
  {
    assert (ty == pe_stats);
    assert (sd.type != statistic_decl::none);
    return "(&(" + value() + "->hist))";
  }

  virtual string buckets() const
  {
    assert (ty == pe_stats);
    assert (sd.type != statistic_decl::none);
    return "(" + value() + "->hist.buckets)";
  }

  string init() const
  {
    switch (type())
      {
      case pe_string:
        if (! local)
          return ""; // module_param
        else
          return value() + "[0] = '\\0';";
      case pe_long:
        if (! local)
          return ""; // module_param
        else
          return value() + " = 0;";
      case pe_stats:
        {
          // See also mapvar::init().

          if (local)
            throw SEMANTIC_ERROR(_F("unsupported local stats init for %s", value().c_str()));

          string prefix = "global_set(" + c_name() + ", _stp_stat_init (";
          // Check for errors during allocation.
          string suffix = "if (" + value () + " == NULL) rc = -ENOMEM;";

          switch (sd.type)
            {
            case statistic_decl::none:
              prefix += "HIST_NONE";
              break;

            case statistic_decl::linear:
              prefix += string("HIST_LINEAR")
                + ", " + lex_cast(sd.linear_low)
                + ", " + lex_cast(sd.linear_high)
                + ", " + lex_cast(sd.linear_step);
              break;

            case statistic_decl::logarithmic:
              prefix += string("HIST_LOG");
              break;

            default:
              throw SEMANTIC_ERROR(_F("unsupported stats type for %s", value().c_str()));
            }

          prefix = prefix + ")); ";
          return string (prefix + suffix);
        }

      default:
        throw SEMANTIC_ERROR(_F("unsupported initializer for %s", value().c_str()));
      }
  }

  string fini () const
  {
    switch (type())
      {
      case pe_string:
      case pe_long:
    return ""; // no action required
      case pe_stats:
    return "_stp_stat_del (" + value () + ");";
      default:
        throw SEMANTIC_ERROR(_F("unsupported deallocator for %s", value().c_str()));
      }
  }

  void declare(c_unparser &c) const
  {
    c.c_declare(ty, c_name());
  }
};

ostream & operator<<(ostream & o, var const & v)
{
  return o << v.value();
}

struct stmt_expr
{
  c_unparser & c;
  stmt_expr(c_unparser & c) : c(c)
  {
    c.o->newline() << "({";
    c.o->indent(1);
  }
  ~stmt_expr()
  {
    c.o->newline(-1) << "})";
  }
};


struct tmpvar
  : public var
{
protected:
  bool overridden;
  string override_value;

public:
  tmpvar(c_unparser *u, exp_type ty, unsigned & counter)
    : var(u, true, ty, ("__tmp" + lex_cast(counter++)), false),
      overridden(false)
  {}

  tmpvar(const var& source)
    : var(source), overridden(false)
  {}

  void override(const string &value)
  {
    overridden = true;
    override_value = value;
  }

  string value() const
  {
    if (overridden)
      return override_value;
    else
      return var::value();
  }
};

ostream & operator<<(ostream & o, tmpvar const & v)
{
  return o << v.value();
}

struct aggvar
  : public var
{
  aggvar(c_unparser *u, unsigned & counter)
    : var(u, true, pe_stats, ("__tmp" + lex_cast(counter++)), false)
  {}

  string init() const
  {
    assert (type() == pe_stats);
    return value() + " = NULL;";
  }

  void declare(c_unparser &c) const
  {
    assert (type() == pe_stats);
    c.o->newline() << "struct stat_data *" << name << ";";
  }

  string get_hist (var& index) const
  {
    return "(" + value() + "->histogram[" + index.value() + "])";
  }
};

struct mapvar
  : public var
{
  vector<exp_type> index_types;
  int maxsize;
  bool wrap;
  mapvar (c_unparser *u,
          bool local, exp_type ty,
      statistic_decl const & sd,
      string const & name,
      vector<exp_type> const & index_types,
      int maxsize, bool wrap)
    : var (u, local, ty, sd, name),
      index_types (index_types),
      maxsize (maxsize), wrap(wrap)
  {}

  static string shortname(exp_type e);
  static string key_typename(exp_type e);
  static string value_typename(exp_type e);

  string keysym () const
  {
    string result;
    vector<exp_type> tmp = index_types;
    tmp.push_back (type ());
    for (unsigned i = 0; i < tmp.size(); ++i)
      {
    switch (tmp[i])
      {
      case pe_long:
        result += 'i';
        break;
      case pe_string:
        result += 's';
        break;
      case pe_stats:
        result += 'x';
        break;
      default:
        throw SEMANTIC_ERROR(_("unknown type of map"));
        break;
      }
      }
    return result;
  }

  string function_keysym(string const & fname, bool pre_agg=false) const
  {
    string mtype = (is_parallel() && !pre_agg) ? "pmap" : "map";
    string result = "_stp_" + mtype + "_" + fname + "_" + keysym();
    return result;
  }

  string call_prefix (string const & fname, vector<tmpvar> const & indices, bool pre_agg=false) const
  {
    string result = function_keysym(fname, pre_agg) + " (";
    result += pre_agg? fetch_existing_aggregate() : value();
    for (unsigned i = 0; i < indices.size(); ++i)
      {
    if (indices[i].type() != index_types[i])
      throw SEMANTIC_ERROR(_("index type mismatch"));
    result += ", ";
    result += indices[i].value();
      }

    return result;
  }

  bool is_parallel() const
  {
    return type() == pe_stats;
  }

  string calculate_aggregate() const
  {
    if (!is_parallel())
      throw SEMANTIC_ERROR(_("aggregating non-parallel map type"));

    return function_keysym("agg") + " (" + value() + ")";
  }

  string fetch_existing_aggregate() const
  {
    if (!is_parallel())
      throw SEMANTIC_ERROR(_("fetching aggregate of non-parallel map type"));

    return "_stp_pmap_get_agg(" + value() + ")";
  }

  string del (vector<tmpvar> const & indices) const
  {
    return (call_prefix("del", indices) + ")");
  }

  string exists (vector<tmpvar> const & indices) const
  {
    if (type() == pe_long || type() == pe_string)
      return (call_prefix("exists", indices) + ")");
    else if (type() == pe_stats)
      return ("((uintptr_t)" + call_prefix("get", indices)
          + ") != (uintptr_t) 0)");
    else
      throw SEMANTIC_ERROR(_("checking existence of an unsupported map type"));
  }

  string get (vector<tmpvar> const & indices, bool pre_agg=false) const
  {
    // see also itervar::get_key
    if (type() == pe_string)
        // impedance matching: NULL -> empty strings
      return ("({ char *v = " + call_prefix("get", indices, pre_agg) + ");"
          + "if (!v) v = \"\"; v; })");
    else if (type() == pe_long || type() == pe_stats)
      return call_prefix("get", indices, pre_agg) + ")";
    else
      throw SEMANTIC_ERROR(_("getting a value from an unsupported map type"));
  }

  string add (vector<tmpvar> const & indices, tmpvar const & val) const
  {
    string res = "{ int rc = ";

    // impedance matching: empty strings -> NULL
    if (type() == pe_stats)
      res += (call_prefix("add", indices) + ", " + val.value() + ")");
    else
      throw SEMANTIC_ERROR(_("adding a value of an unsupported map type"));

    res += "; if (unlikely(rc)) { c->last_error = ";
    res += STAP_T_01 +
      lex_cast(maxsize > 0 ?
      "size limit (" + lex_cast(maxsize) + ")" : "MAXMAPENTRIES")
      + "\"; goto out; }}";

    return res;
  }

  string set (vector<tmpvar> const & indices, tmpvar const & val) const
  {
    string res = "{ int rc = ";

    // impedance matching: empty strings -> NULL
    if (type() == pe_string)
      res += (call_prefix("set", indices)
          + ", (" + val.value() + "[0] ? " + val.value() + " : NULL))");
    else if (type() == pe_long)
      res += (call_prefix("set", indices) + ", " + val.value() + ")");
    else
      throw SEMANTIC_ERROR(_("setting a value of an unsupported map type"));

    res += "; if (unlikely(rc)) { c->last_error = ";
    res += STAP_T_01 +
      lex_cast(maxsize > 0 ?
      "size limit (" + lex_cast(maxsize) + ")" : "MAXMAPENTRIES")
      + "\"; goto out; }}";

    return res;
  }

  string hist() const
  {
    assert (ty == pe_stats);
    assert (sd.type != statistic_decl::none);
    return "(&(" + fetch_existing_aggregate() + "->hist))";
  }

  string buckets() const
  {
    assert (ty == pe_stats);
    assert (sd.type != statistic_decl::none);
    return "(" + fetch_existing_aggregate() + "->hist.buckets)";
  }

  string init () const
  {
    if (local)
      throw SEMANTIC_ERROR(_F("unsupported local map init for %s", value().c_str()));

    string prefix = "global_set(" + c_name() + ", ";
    prefix += function_keysym("new") + " ("
      + (maxsize > 0 ? lex_cast(maxsize) : "MAXMAPENTRIES")
      + ((wrap == true) ? ", 1" : ", 0");

    // See also var::init().

    // Check for errors during allocation.
    string suffix = "if (" + value () + " == NULL) rc = -ENOMEM;";

    if (type() == pe_stats)
      {
    switch (sdecl().type)
      {
      case statistic_decl::none:
        prefix = prefix + ", HIST_NONE";
        break;

      case statistic_decl::linear:
        // FIXME: check for "reasonable" values in linear stats
        prefix = prefix + ", HIST_LINEAR"
          + ", " + lex_cast(sdecl().linear_low)
          + ", " + lex_cast(sdecl().linear_high)
          + ", " + lex_cast(sdecl().linear_step);
        break;

      case statistic_decl::logarithmic:
        prefix = prefix + ", HIST_LOG";
        break;
      }
      }

    prefix = prefix + ")); ";
    return (prefix + suffix);
  }

  string fini () const
  {
    // NB: fini() is safe to call even for globals that have not
    // successfully initialized (that is to say, on NULL pointers),
    // because the runtime specifically tolerates that in its _del
    // functions.

    if (is_parallel())
      return "_stp_pmap_del (" + value() + ");";
    else
      return "_stp_map_del (" + value() + ");";
  }
};


class itervar
{
  exp_type referent_ty;
  string name;

public:

  itervar (symbol* e, unsigned & counter)
    : referent_ty(e->referent->type),
      name("__tmp" + lex_cast(counter++))
  {
    if (referent_ty == pe_unknown)
      throw SEMANTIC_ERROR(_("iterating over unknown reference type"), e->tok);
  }

  string declare () const
  {
    return "struct map_node *" + name + ";";
  }

  string start (mapvar const & mv) const
  {
    string res;

    if (mv.type() != referent_ty)
      throw SEMANTIC_ERROR(_("inconsistent iterator type in itervar::start()"));

    if (mv.is_parallel())
      return "_stp_map_start (" + mv.fetch_existing_aggregate() + ")";
    else
      return "_stp_map_start (" + mv.value() + ")";
  }

  string next (mapvar const & mv) const
  {
    if (mv.type() != referent_ty)
      throw SEMANTIC_ERROR(_("inconsistent iterator type in itervar::next()"));

    if (mv.is_parallel())
      return "_stp_map_iter (" + mv.fetch_existing_aggregate() + ", " + value() + ")";
    else
      return "_stp_map_iter (" + mv.value() + ", " + value() + ")";
  }

  // Cannot handle deleting and iterating on pmaps
  string del_next (mapvar const & mv) const
  {
    if (mv.type() != referent_ty)
      throw SEMANTIC_ERROR(_("inconsistent iterator type in itervar::next()"));

    if (mv.is_parallel())
      throw SEMANTIC_ERROR(_("deleting a value of an unsupported map type"));
    else
      return "_stp_map_iterdel (" + mv.value() + ", " + value() + ")";
  }

  string value () const
  {
    return "l->" + name;
  }

  string get_key (mapvar const& mv, exp_type ty, unsigned i) const
  {
    // bug translator/1175: runtime uses base index 1 for the first dimension
    // see also mapval::get
    switch (ty)
      {
      case pe_long:
    return mv.function_keysym("key_get_int64", true)
      + " (" + value() + ", " + lex_cast(i+1) + ")";
      case pe_string:
        // impedance matching: NULL -> empty strings
    return "(" + mv.function_keysym("key_get_str", true)
      + " (" + value() + ", " + lex_cast(i+1) + ") ?: \"\")";
      default:
    throw SEMANTIC_ERROR(_("illegal key type"));
      }
  }

  string get_value (mapvar const& mv, exp_type ty) const
  {
    if (ty != referent_ty)
      throw SEMANTIC_ERROR(_("inconsistent iterator value in itervar::get_value()"));

    switch (ty)
      {
      case pe_long:
    return mv.function_keysym("get_int64", true) + " ("+ value() + ")";
      case pe_string:
        // impedance matching: NULL -> empty strings
    return "(" + mv.function_keysym("get_str", true) + " ("+ value() + ") ?: \"\")";
      case pe_stats:
    return mv.function_keysym("get_stat_data", true) + " ("+ value() + ")";
      default:
    throw SEMANTIC_ERROR(_("illegal value type"));
      }
  }
};

ostream & operator<<(ostream & o, itervar const & v)
{
  return o << v.value();
}

// ------------------------------------------------------------------------

// translator_output moved to translator-output.cxx

// ------------------------------------------------------------------------

struct unmodified_fnargs_checker : public embedded_tags_visitor
{
  bool embedded_seen; // excludes code with pure and unmodified-args
  unmodified_fnargs_checker (): embedded_tags_visitor(true), embedded_seen(false) {}

  void visit_embeddedcode (embeddedcode *e)
    {
      embedded_tags_visitor::visit_embeddedcode(e);
      // set embedded_seen to true if it does not contain /* unmodified-fnargs */
      embedded_seen = (embedded_seen || !tagged_p("/* unmodified-fnargs */"));
    }
};

bool
is_unmodified_string_fnarg (systemtap_session* sess, functiondecl* fd, vardecl* v)
{
  if (sess->unoptimized || v->type != pe_string)
    return false;

  // check that there is no embedded code that might modify the fn args
  unmodified_fnargs_checker ecv;
  fd->body->visit(& ecv);
  if (ecv.embedded_seen)
    return false;

  varuse_collecting_visitor vut (*sess);
  vut.current_function = fd;
  fd->body->visit(& vut);
  return (find(vut.written.begin(), vut.written.end(), v) == vut.written.end());
}

void
c_unparser::emit_common_header ()
{
  o->newline();

  // Per CPU context for probes. Includes common shared state held for
  // all probes (defined in common_probe_context), the probe locals (union)
  // and the function locals (union).
  o->newline() << "struct context {";

  // Common state held shared by probes.
  o->newline(1) << "#include \"common_probe_context.h\"";

  // PR10516: probe locals
  o->newline() << "union {";
  o->indent(1);

  // To elide context variables for probe handler functions that
  // themselves are about to get duplicate-eliminated, we XXX
  // duplicate the parse-tree-hash method from ::emit_probe().
  map<string, string> tmp_probe_contents;
  // The reason we don't use c_unparser::probe_contents itself
  // for this is that we don't want to muck up the data for
  // that later routine.

  for (unsigned i=0; i<session->probes.size(); i++)
    {
      derived_probe* dp = session->probes[i];

      // NB: see c_unparser::emit_probe() for original copy of duplicate-hashing logic.
      ostringstream oss;
      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
      // "hashed" in above.

      if (session->unoptimized || tmp_probe_contents.count(oss.str()) == 0) // unique
        {
          tmp_probe_contents[oss.str()] = dp->name; // save it

          o->newline() << "struct " << dp->name << "_locals {";
          o->indent(1);
          for (unsigned j=0; j<dp->locals.size(); j++)
            {
              vardecl* v = dp->locals[j];
              try
                {
                  o->newline() << c_typename (v->type) << " "
                               << c_localname (v->name) << ";";
                } catch (const semantic_error& e) {
                semantic_error e2 (e);
                if (e2.tok1 == 0) e2.tok1 = v->tok;
                throw e2;
              }
            }

          // NB: This part is finicky.  The logic here must
          // match up with
          c_tmpcounter ct (this);
          dp->body->visit (& ct);

          o->newline(-1) << "} " << dp->name << ";";
        }
    }
  o->newline(-1) << "} probe_locals;";

  // PR10516: function locals
  o->newline() << "union {";
  o->indent(1);

  for (map<string,functiondecl*>::iterator it = session->functions.begin(); it != session->functions.end(); it++)
    {
      functiondecl* fd = it->second;
      o->newline()
        << "struct " << c_funcname (fd->name) << "_locals {";
      o->indent(1);
      for (unsigned j=0; j<fd->locals.size(); j++)
        {
      vardecl* v = fd->locals[j];
      try
        {
              if (fd->mangle_oldstyle)
                {
                  // PR14524: retain old way of referring to the locals
                  o->newline() << "union { "
                               << c_typename (v->type) << " "
                               << c_localname (v->name) << "; "
                               << c_typename (v->type) << " "
                               << c_localname (v->name, true) << "; };";
                }
              else
                {
                  o->newline() << c_typename (v->type) << " "
                               << c_localname (v->name) << ";";
                }
        } catch (const semantic_error& e) {
          semantic_error e2 (e);
          if (e2.tok1 == 0) e2.tok1 = v->tok;
          throw e2;
        }
        }
      for (unsigned j=0; j<fd->formal_args.size(); j++)
        {
          vardecl* v = fd->formal_args[j];
      try
        {
              v->char_ptr_arg = (is_unmodified_string_fnarg (session, fd, v));

              if (v->char_ptr_arg && session->verbose > 2)
                clog << _F("variable %s for function %s will be passed by reference (char *)",
                           v->name.c_str(), fd->name.c_str()) << endl;

              if (fd->mangle_oldstyle)
                {
                  // PR14524: retain old way of referring to the locals
                  o->newline() << "union { "
                               << (v->char_ptr_arg ? "const char *" : c_typename (v->type))
                               << " " << c_localname (v->name) << "; "
                               << (v->char_ptr_arg ? "const char *" : c_typename (v->type))
                               << " " << c_localname (v->name, true) << "; };";
                }
              else
                {
                  o->newline() << (v->char_ptr_arg ? "const char *" : c_typename (v->type))
                               << " " << c_localname (v->name) << ";";
                }
        } catch (const semantic_error& e) {
          semantic_error e2 (e);
          if (e2.tok1 == 0) e2.tok1 = v->tok;
          throw e2;
        }
        }
      c_tmpcounter ct (this);
      fd->body->visit (& ct);
      if (fd->type == pe_unknown)
    o->newline() << "/* no return value */";
      else
    {
          if (!session->unoptimized && fd->type == pe_string&& session->verbose > 2)
            clog << _F("return value for function %s will be passed by reference (char *)",
                       fd->name.c_str()) << endl;
      o->newline() << (!session->unoptimized && fd->type == pe_string ? "char *" : c_typename (fd->type))
                       << " __retvalue;";
    }
      o->newline(-1) << "} " << c_funcname (fd->name) << ";";
    }
  o->newline(-1) << "} locals [MAXNESTING+1];";

  // NB: The +1 above for extra room for outgoing arguments of next nested function.
  // If MAXNESTING is set too small, the args will be written, but the MAXNESTING
  // check done at c_unparser::emit_function will reject.
  //
  // This policy wastes memory (one row of locals[] that cannot really
  // be used), but trades that for smaller code (not having to check
  // c->nesting against MAXNESTING at every call site).

  // Try to catch a crazy user dude passing in -DMAXNESTING=-1, leading to a [0]-sized
  // locals[] array.
  o->newline() << "#if MAXNESTING < 0";
  o->newline() << "#error \"MAXNESTING must be positive\"";
  o->newline() << "#endif";

  // Use a separate union for compiled-printf locals, no nesting required.
  emit_compiled_printf_locals ();

  o->newline(-1) << "};\n"; // end of struct context

  o->newline() << "#include \"runtime_context.h\"";

  emit_map_type_instantiations ();

  emit_compiled_printfs();

  if (!session->runtime_usermode_p())
    {
      // Updated in probe handlers to signal that a module refresh is needed.
      // Checked and cleared by common epilogue after scheduling refresh work.
      o->newline( 0)  << "static atomic_t need_module_refresh = ATOMIC_INIT(0);";

      // We will use a workqueue to schedule module_refresh work when we need
      // to enable/disable probes.
      o->newline( 0)  << "#include <linux/workqueue.h>";
      o->newline( 0)  << "static struct work_struct module_refresher_work;";
      o->newline( 0)  << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)";
      o->newline( 0)  << "static void module_refresher(void *data) {";
      o->newline( 0)  << "#else";
      o->newline( 0)  << "static void module_refresher(struct work_struct *work) {";
      o->newline( 0)  << "#endif";
      o->newline( 1)  <<    "systemtap_module_refresh(NULL);";
      o->newline(-1)  << "}";

      o->newline( 0)  << "#ifdef STP_ON_THE_FLY_TIMER_ENABLE";
      o->newline( 0)  << "#include <linux/hrtimer.h>";
      o->newline( 0)  << "#include \"timer.h\"";
      o->newline( 0)  << "static struct hrtimer module_refresh_timer;";

      o->newline( 0)  << "#ifndef STP_ON_THE_FLY_INTERVAL";
      o->newline( 0)  << "#define STP_ON_THE_FLY_INTERVAL (100*1000*1000)"; // default to 100 ms
      o->newline( 0)  << "#endif";

      o->newline( 0)  << "hrtimer_return_t module_refresh_timer_cb(struct hrtimer *timer) {";
      o->newline(+1)  <<   "if (atomic_cmpxchg(&need_module_refresh, 1, 0) == 1)";
      // NB: one might like to invoke systemtap_module_refresh(NULL) directly from
      // here ... however hrtimers are called from an unsleepable context, so no can do.
      o->newline(+1)  <<     "schedule_work(&module_refresher_work);";
      o->newline(-1)  <<   "hrtimer_set_expires(timer,";
      o->newline( 0)  <<   "  ktime_add(hrtimer_get_expires(timer),";
      o->newline( 0)  <<   "            ktime_set(0, STP_ON_THE_FLY_INTERVAL))); ";
      o->newline( 0)  <<   "return HRTIMER_RESTART;";
      o->newline(-1)  << "}";
      o->newline( 0)  << "#endif /* STP_ON_THE_FLY_ENABLE */";
    }

  o->newline();
}


void
c_unparser::declare_compiled_printf (bool print_to_stream, const string& format)
{
  pair<bool, string> index (print_to_stream, format);
  map<pair<bool, string>, string>::iterator it = compiled_printfs.find(index);
  if (it == compiled_printfs.end())
    compiled_printfs[index] = (print_to_stream ? "stp_printf_" : "stp_sprintf_")
      + lex_cast(compiled_printfs.size() + 1);
}

const string&
c_unparser::get_compiled_printf (bool print_to_stream, const string& format)
{
  map<pair<bool, string>, string>::iterator it =
    compiled_printfs.find(make_pair(print_to_stream, format));
  if (it == compiled_printfs.end())
    throw SEMANTIC_ERROR (_("internal error translating printf"));
  return it->second;
}

void
c_unparser::emit_compiled_printf_locals ()
{
  o->newline() << "#ifndef STP_LEGACY_PRINT";
  o->newline() << "union {";
  o->indent(1);
  map<pair<bool, string>, string>::iterator it;
  for (it = compiled_printfs.begin(); it != compiled_printfs.end(); ++it)
    {
      bool print_to_stream = it->first.first;
      const string& format_string = it->first.second;
      const string& name = it->second;
      vector<print_format::format_component> components =
    print_format::string_to_components(format_string);

      o->newline() << "struct " << name << "_locals {";
      o->indent(1);

      size_t arg_ix = 0;
      vector<print_format::format_component>::const_iterator c;
      for (c = components.begin(); c != components.end(); ++c)
    {
      if (c->type == print_format::conv_literal)
        continue;

      // Take note of the width and precision arguments, if any.
      if (c->widthtype == print_format::width_dynamic)
        o->newline() << "int64_t arg" << arg_ix++ << ";";
      if (c->prectype == print_format::prec_dynamic)
        o->newline() << "int64_t arg" << arg_ix++ << ";";

      // Output the actual argument.
      switch (c->type)
        {
        case print_format::conv_pointer:
        case print_format::conv_number:
        case print_format::conv_char:
        case print_format::conv_memory:
        case print_format::conv_memory_hex:
        case print_format::conv_binary:
          o->newline() << "int64_t arg" << arg_ix++ << ";";
          break;

        case print_format::conv_string:
          // NB: Since we know incoming strings are immutable, we can use
          // const char* rather than a private char[] copy.  This is a
          // special case of the sort of optimizations desired in PR11528.
          o->newline() << "const char* arg" << arg_ix++ << ";";
          break;

        default:
          assert(false); // XXX
          break;
        }
    }


      if (!print_to_stream)
    o->newline() << "char * __retvalue;";

      o->newline(-1) << "} " << name << ";";
    }
  o->newline(-1) << "} printf_locals;";
  o->newline() << "#endif // STP_LEGACY_PRINT";
}

void
c_unparser::emit_compiled_printfs ()
{
  o->newline() << "#ifndef STP_LEGACY_PRINT";
  map<pair<bool, string>, string>::iterator it;
  for (it = compiled_printfs.begin(); it != compiled_printfs.end(); ++it)
    {
      bool print_to_stream = it->first.first;
      const string& format_string = it->first.second;
      const string& name = it->second;
      vector<print_format::format_component> components =
    print_format::string_to_components(format_string);

      o->newline();

      // Might be nice to output the format string in a comment, but we'd have
      // to be extra careful about format strings not escaping the comment...
      o->newline() << "static void " << name
           << " (struct context* __restrict__ c) {";
      o->newline(1) << "struct " << name << "_locals * __restrict__ l = "
            << "& c->printf_locals." << name << ";";
      o->newline() << "char *str = NULL, *end = NULL;";
      o->newline() << "const char *src;";
      o->newline() << "int width;";
      o->newline() << "int precision;";
      o->newline() << "unsigned long ptr_value;";
      o->newline() << "int num_bytes;";

      o->newline() << "(void) width;";
      o->newline() << "(void) precision;";
      o->newline() << "(void) ptr_value;";
      o->newline() << "(void) num_bytes;";

      if (print_to_stream)
        {
      // Compute the buffer size needed for these arguments.
      size_t arg_ix = 0;
      o->newline() << "num_bytes = 0;";
      vector<print_format::format_component>::const_iterator c;
      for (c = components.begin(); c != components.end(); ++c)
        {
          if (c->type == print_format::conv_literal)
        {
          literal_string ls(c->literal_string);
          o->newline() << "num_bytes += sizeof(";
          visit_literal_string(&ls);
          o->line() << ") - 1;"; // don't count the '\0'
          continue;
        }

          o->newline() << "width = ";
          if (c->widthtype == print_format::width_dynamic)
        o->line() << "clamp_t(int, l->arg" << arg_ix++
                  << ", 0, STP_BUFFER_SIZE);";
          else if (c->widthtype == print_format::width_static)
        o->line() << "clamp_t(int, " << c->width
                  << ", 0, STP_BUFFER_SIZE);";
          else
        o->line() << "-1;";

          o->newline() << "precision = ";
          if (c->prectype == print_format::prec_dynamic)
        o->line() << "clamp_t(int, l->arg" << arg_ix++
                  << ", 0, STP_BUFFER_SIZE);";
          else if (c->prectype == print_format::prec_static)
        o->line() << "clamp_t(int, " << c->precision
                  << ", 0, STP_BUFFER_SIZE);";
          else
        o->line() << "-1;";

          string value = "l->arg" + lex_cast(arg_ix++);
          switch (c->type)
        {
        case print_format::conv_pointer:
          // NB: stap < 1.3 had odd %p behavior... see _stp_vsnprintf
          if (strverscmp(session->compatible.c_str(), "1.3") < 0)
            {
              o->newline() << "ptr_value = " << value << ";";
              o->newline() << "if (width == -1)";
              o->newline(1) << "width = 2 + 2 * sizeof(void*);";
              o->newline(-1) << "precision = width - 2;";
              if (!c->test_flag(print_format::fmt_flag_left))
            o->newline() << "precision = min_t(int, precision, 2 * sizeof(void*));";
              o->newline() << "num_bytes += number_size(ptr_value, "
            << c->base << ", width, precision, " << c->flags << ");";
              break;
            }
          // else fall-through to conv_number
        case print_format::conv_number:
          o->newline() << "num_bytes += number_size(" << value << ", "
                   << c->base << ", width, precision, " << c->flags << ");";
          break;

        case print_format::conv_char:
          o->newline() << "num_bytes += _stp_vsprint_char_size("
                   << value << ", width, " << c->flags << ");";
          break;

        case print_format::conv_string:
          o->newline() << "num_bytes += _stp_vsprint_memory_size("
                   << value << ", width, precision, 's', "
                   << c->flags << ");";
          break;

        case print_format::conv_memory:
        case print_format::conv_memory_hex:
          o->newline() << "num_bytes += _stp_vsprint_memory_size("
                   << "(const char*)(intptr_t)" << value
                   << ", width, precision, '"
                   << ((c->type == print_format::conv_memory) ? "m" : "M")
                   << "', " << c->flags << ");";
          break;

        case print_format::conv_binary:
          o->newline() << "num_bytes += _stp_vsprint_binary_size("
                   << value << ", width, precision);";
          break;

        default:
          assert(false); // XXX
          break;
        }
        }

      o->newline() << "num_bytes = clamp(num_bytes, 0, STP_BUFFER_SIZE);";
      o->newline() << "str = (char*)_stp_reserve_bytes(num_bytes);";
      o->newline() << "end = str ? str + num_bytes - 1 : 0;";
        }
      else // !print_to_stream
    {
      // String results are a known buffer and size;
      o->newline() << "str = l->__retvalue;";
      o->newline() << "end = str + MAXSTRINGLEN - 1;";
    }

      o->newline() << "if (str && str <= end) {";
      o->indent(1);

      // Generate code to print the actual arguments.
      size_t arg_ix = 0;
      vector<print_format::format_component>::const_iterator c;
      for (c = components.begin(); c != components.end(); ++c)
    {
      if (c->type == print_format::conv_literal)
        {
          literal_string ls(c->literal_string);
          o->newline() << "src = ";
          visit_literal_string(&ls);
          o->line() << ";";
          o->newline() << "while (*src && str <= end)";
          o->newline(1) << "*str++ = *src++;";
              o->indent(-1);
          continue;
        }

      o->newline() << "width = ";
      if (c->widthtype == print_format::width_dynamic)
        o->line() << "clamp_t(int, l->arg" << arg_ix++
              << ", 0, end - str + 1);";
      else if (c->widthtype == print_format::width_static)
        o->line() << "clamp_t(int, " << c->width
              << ", 0, end - str + 1);";
      else
        o->line() << "-1;";

      o->newline() << "precision = ";
      if (c->prectype == print_format::prec_dynamic)
        o->line() << "clamp_t(int, l->arg" << arg_ix++
              << ", 0, end - str + 1);";
      else if (c->prectype == print_format::prec_static)
        o->line() << "clamp_t(int, " << c->precision
              << ", 0, end - str + 1);";
      else
        o->line() << "-1;";

      string value = "l->arg" + lex_cast(arg_ix++);
      switch (c->type)
        {
        case print_format::conv_pointer:
          // NB: stap < 1.3 had odd %p behavior... see _stp_vsnprintf
          if (strverscmp(session->compatible.c_str(), "1.3") < 0)
        {
          o->newline() << "ptr_value = " << value << ";";
          o->newline() << "if (width == -1)";
          o->newline(1) << "width = 2 + 2 * sizeof(void*);";
          o->newline(-1) << "precision = width - 2;";
          if (!c->test_flag(print_format::fmt_flag_left))
            o->newline() << "precision = min_t(int, precision, 2 * sizeof(void*));";
          o->newline() << "str = number(str, end, ptr_value, "
            << c->base << ", width, precision, " << c->flags << ");";
          break;
        }
          // else fall-through to conv_number
        case print_format::conv_number:
          o->newline() << "str = number(str, end, " << value << ", "
               << c->base << ", width, precision, " << c->flags << ");";
          break;

        case print_format::conv_char:
          o->newline() << "str = _stp_vsprint_char(str, end, "
               << value << ", width, " << c->flags << ");";
          break;

        case print_format::conv_string:
          o->newline() << "str = _stp_vsprint_memory(str, end, "
               << value << ", width, precision, 's', "
               << c->flags << ");";
          break;

        case print_format::conv_memory:
        case print_format::conv_memory_hex:
          o->newline() << "str = _stp_vsprint_memory(str, end, "
               << "(const char*)(intptr_t)" << value
               << ", width, precision, '"
               << ((c->type == print_format::conv_memory) ? "m" : "M")
               << "', " << c->flags << ");";
          o->newline() << "if (unlikely(str == NULL)) {";
          o->indent(1);
          if (print_to_stream)
          o->newline() << "_stp_unreserve_bytes(num_bytes);";
          o->newline() << "return;";
          o->newline(-1) << "}";
          break;

        case print_format::conv_binary:
          o->newline() << "str = _stp_vsprint_binary(str, end, "
               << value << ", width, precision, "
               << c->flags << ");";
          break;

        default:
          assert(false); // XXX
          break;
        }
    }

      if (!print_to_stream)
    {
      o->newline() << "if (str <= end)";
      o->newline(1) << "*str = '\\0';";
      o->newline(-1) << "else";
      o->newline(1) << "*end = '\\0';";
      o->indent(-1);
    }

      o->newline(-1) << "}";

      o->newline(-1) << "}";
    }
  o->newline() << "#endif // STP_LEGACY_PRINT";
}


void
c_unparser::emit_global_param (vardecl *v)
{
  string vn = c_globalname (v->name);

  // For dyninst, use the emit_global_init_* functionality instead.
  assert (!session->runtime_usermode_p());

  // NB: systemtap globals can collide with linux macros,
  // e.g. VM_FAULT_MAJOR.  We want the parameter name anyway.  This
  // #undef is spit out at the end of the C file, so that removing the
  // definition won't affect any other embedded-C or generated code.
  // XXX: better not have a global variable named module_param_named etc.!
  o->newline() << "#undef " << v->name; // avoid colliding with non-mangled name

  // Emit module_params for this global, if its type is convenient.
  if (v->arity == 0 && v->type == pe_long)
    {
      o->newline() << "module_param_named (" << v->name << ", "
                   << "global(" << vn << "), int64_t, 0);";
    }
  else if (v->arity == 0 && v->type == pe_string)
    {
      // NB: no special copying is needed.
      o->newline() << "module_param_string (" << v->name << ", "
                   << "global(" << vn << "), MAXSTRINGLEN, 0);";
    }
}


void
c_unparser::emit_global_init_setters ()
{
  // Hack for dyninst module params: setter function forms a little
  // linear lookup table ditty to find a global variable by name.
  o->newline() << "int stp_global_setter (const char *name, const char *value) {";
  o->newline(1);
  for (unsigned i=0; i<session->globals.size(); i++)
    {
      vardecl* v = session->globals[i];
      if (v->arity > 0) continue;
      if (v->type != pe_string && v->type != pe_long) continue;

      // Do not mangle v->name for the comparison!
      o->line() << "if (0 == strcmp(name,\"" << v->name << "\"))" << " {";

      o->indent(1);
      if (v->type == pe_string)
        {
          c_assign("stp_global_init." + c_globalname(v->name), "value", pe_string, "BUG: global module param", v->tok);
          o->newline() << "return 0;";
        }
      else
        {
          o->newline() << "return set_int64_t(value, &stp_global_init." << c_globalname(v->name) << ");";
        }

      o->newline(-1) << "} else ";
    }

  // Call the runtime function that handles session attributes, like
  // log_level, etc.
  o->line() << "return stp_session_attribute_setter(name, value);";
  o->newline(-1) << "}";
  o->newline();
}


void
c_unparser::emit_global (vardecl *v)
{
  string vn = c_globalname (v->name);

  string type;
  if (v->arity > 0)
    type = (v->type == pe_stats) ? "PMAP" : "MAP";
  else
    type = c_typename (v->type);

  if (session->runtime_usermode_p())
    {
      // In stapdyn mode, the stat/map/pmap pointers are stored as offptr_t in
      // shared memory.  However, we can keep a little type safety by emitting
      // FOO_typed and using typeof(FOO_typed) in the global() macros.
      bool offptr_p  = (v->type == pe_stats) || (v->arity > 0);
      string stored_type = offptr_p ? "offptr_t" : type;

      // NB: The casted_type is in the unused side of a __builtin_choose_expr
      // for non-offptr types, so it doesn't matter what we put for them, as
      // long as it passes syntax long enough for gcc to choose the other expr.
      string casted_type = offptr_p ? type : "void*";

      o->newline() << "union {";
      o->newline(1) << casted_type << " " << vn << "_typed;";
      o->newline() << stored_type << " " << vn << ";";
      o->newline(-1) << "};";
    }
  else
    o->newline() << type << " " << vn << ";";

  o->newline() << "rwlock_t " << vn << "_lock;";
  o->newline() << "#ifdef STP_TIMING";
  o->newline() << "atomic_t " << vn << "_lock_skip_count;";
  o->newline() << "#endif\n";
}


void
c_unparser::emit_global_init (vardecl *v)
{
  // We can only statically initialize some scalars.
  if (v->arity == 0 && v->init)
    {
      o->newline() << "." << c_globalname (v->name) << " = ";
      v->init->visit(this);
      o->line() << ",";
    }
  else if (v->arity == 0 && session->runtime_usermode_p())
    {
      // For dyninst: always try to put a default value into the initial
      // static structure, so we don't have to guess if it was customized.
      if (v->type == pe_long)
        o->newline() << "." << c_globalname (v->name) << " = 0,";
      else if (v->type == pe_string)
        o->newline() << "." << c_globalname (v->name) << " = { '\\0' },"; // XXX: ""
    }
  // The lock and lock_skip_count are handled in emit_module_init.
}


void
c_unparser::emit_global_init_type (vardecl *v)
{
  // We can only statically initialize some scalars.
  if (v->arity == 0) // ... although we still allow !v->init here.
    {
      o->newline() << c_typename(v->type) << " " << c_globalname(v->name) << ";";
    }
}


void
c_unparser::emit_functionsig (functiondecl* v)
{
  o->newline() << "static void " << c_funcname(v->name)
           << " (struct context * __restrict__ c);";
}


void
c_unparser::emit_kernel_module_init ()
{
  if (session->runtime_usermode_p())
    return;

  o->newline();
  o->newline() << "static int systemtap_kernel_module_init (void) {";
  o->newline(1) << "int rc = 0;";
  o->newline() << "int i=0, j=0;"; // for derived_probe_group use

  vector<derived_probe_group*> g = all_session_groups (*session);
  for (unsigned i=0; i<g.size(); i++)
    {
      g[i]->emit_kernel_module_init (*session);

      o->newline() << "if (rc) {";
      o->indent(1);
      if (i>0)
        {
      for (int j=i-1; j>=0; j--)
        g[j]->emit_kernel_module_exit (*session);
    }
      o->newline() << "goto out;";
      o->newline(-1) << "}";
    }
  o->newline(-1) << "out:";
  o->indent(1);
  o->newline() << "return rc;";
  o->newline(-1) << "}\n";
  o->assert_0_indent();
}


void
c_unparser::emit_kernel_module_exit ()
{
  if (session->runtime_usermode_p())
    return;

  o->newline();
  o->newline() << "static void systemtap_kernel_module_exit (void) {";
  o->newline(1) << "int i=0, j=0;"; // for derived_probe_group use

  // We're processing the derived_probe_group list in reverse order.
  // This ensures that probe groups get unregistered in reverse order
  // of the way they were registered.
  vector<derived_probe_group*> g = all_session_groups (*session);
  for (vector<derived_probe_group*>::reverse_iterator i = g.rbegin();
       i != g.rend(); i++)
    {
      (*i)->emit_kernel_module_exit (*session);
    }
  o->newline(-1) << "}\n";
  o->assert_0_indent();
}


void
c_unparser::emit_module_init ()
{
  vector<derived_probe_group*> g = all_session_groups (*session);
  for (unsigned i=0; i<g.size(); i++)
    {
      g[i]->emit_module_decls (*session);
      o->assert_0_indent();
    }

  o->newline() << "#ifdef STAP_NEED_TRACEPOINTS";
  o->newline() << "#include \"linux/stp_tracepoint.c\"";
  o->newline() << "#endif";

  o->newline();
  o->newline() << "static int systemtap_module_init (void) {";
  o->newline(1) << "int rc = 0;";
  o->newline() << "int cpu;";
  o->newline() << "int i=0, j=0;"; // for derived_probe_group use
  o->newline() << "const char *probe_point = \"\";";

  // NB: This block of initialization only makes sense in kernel
  if (! session->runtime_usermode_p())
  {
      // XXX Plus, most of this code is completely static, so it probably should
      // move into the runtime, where kernel/dyninst is more easily separated.

      // Compare actual and targeted kernel releases/machines.  Sometimes
      // one may install the incorrect debuginfo or -devel RPM, and try to
      // run a probe compiled for a different version.  Catch this early,
      // just in case modversions didn't.
      o->newline() << "{";
      o->newline(1) << "const char* release = UTS_RELEASE;";
      o->newline() << "#ifdef STAPCONF_GENERATED_COMPILE";
      o->newline() << "const char* version = UTS_VERSION;";
      o->newline() << "#endif";

      // NB: This UTS_RELEASE compile-time macro directly checks only that
      // the compile-time kbuild tree matches the compile-time debuginfo/etc.
      // It does not check the run time kernel value.  However, this is
      // probably OK since the kbuild modversions system aims to prevent
      // mismatches between kbuild and runtime versions at module-loading time.

      // o->newline() << "const char* machine = UTS_MACHINE;";
      // NB: We could compare UTS_MACHINE too, but on x86 it lies
      // (UTS_MACHINE=i386, but uname -m is i686).  Sheesh.

      o->newline() << "if (strcmp (release, "
           << lex_cast_qstring (session->kernel_release) << ")) {";
      o->newline(1) << "_stp_error (\"module release mismatch (%s vs %s)\", "
            << "release, "
            << lex_cast_qstring (session->kernel_release)
            << ");";
      o->newline() << "rc = -EINVAL;";
      o->newline(-1) << "}";

      o->newline() << "#ifdef STAPCONF_GENERATED_COMPILE";
      o->newline() << "if (strcmp (utsname()->version, version)) {";
      o->newline(1) << "_stp_error (\"module version mismatch (%s vs %s), release %s\", "
            << "version, "
            << "utsname()->version, "
            << "release"
            << ");";
      o->newline() << "rc = -EINVAL;";
      o->newline(-1) << "}";
      o->newline() << "#endif";

      // perform buildid-based checking if able
      o->newline() << "if (_stp_module_check()) rc = -EINVAL;";

      // Perform checking on the user's credentials vs those required to load/run this module.
      o->newline() << "if (_stp_privilege_credentials == 0) {";
      o->newline(1) << "if (STP_PRIVILEGE_CONTAINS(STP_PRIVILEGE, STP_PR_STAPDEV) ||";
      o->newline() << "    STP_PRIVILEGE_CONTAINS(STP_PRIVILEGE, STP_PR_STAPUSR)) {";
      o->newline(1) << "_stp_privilege_credentials = STP_PRIVILEGE;";
      o->newline() << "#ifdef DEBUG_PRIVILEGE";
      o->newline(1) << "_dbug(\"User's privilege credentials default to %s\\n\",";
      o->newline() << "      privilege_to_text(_stp_privilege_credentials));";
      o->newline(-1) << "#endif";
      o->newline(-1) << "}";
      o->newline() << "else {";
      o->newline(1) << "_stp_error (\"Unable to verify that you have the required privilege credentials to run this module (%s required). You must use staprun version 1.7 or higher.\",";
      o->newline() << "            privilege_to_text(STP_PRIVILEGE));";
      o->newline() << "rc = -EINVAL;";
      o->newline(-1) << "}";
      o->newline(-1) << "}";
      o->newline() << "else {";
      o->newline(1) << "#ifdef DEBUG_PRIVILEGE";
      o->newline(1) << "_dbug(\"User's privilege credentials provided as %s\\n\",";
      o->newline() << "      privilege_to_text(_stp_privilege_credentials));";
      o->newline(-1) << "#endif";
      o->newline() << "if (! STP_PRIVILEGE_CONTAINS(_stp_privilege_credentials, STP_PRIVILEGE)) {";
      o->newline(1) << "_stp_error (\"Your privilege credentials (%s) are insufficient to run this module (%s required).\",";
      o->newline () << "            privilege_to_text(_stp_privilege_credentials), privilege_to_text(STP_PRIVILEGE));";
      o->newline() << "rc = -EINVAL;";
      o->newline(-1) << "}";
      o->newline(-1) << "}";

      o->newline(-1) << "}";

      o->newline() << "if (rc) goto out;";
  }

  // Now that kernel version and permissions are correct,
  // initialize the global session states before anything else.
  o->newline() << "rc = stp_session_init();";
  o->newline() << "if (rc) {";
  o->newline(1) << "_stp_error (\"couldn't initialize the main session (rc %d)\", rc);";
  o->newline() << "goto out;";
  o->newline(-1) << "}";

  // initialize gettimeofday (if needed)
  o->newline() << "#ifdef STAP_NEED_GETTIMEOFDAY";
  o->newline() << "rc = _stp_init_time();"// Kick off the Big Bang.
  o->newline() << "if (rc) {";
  o->newline(1) << "_stp_error (\"couldn't initialize gettimeofday\");";
  o->newline() << "goto out;";
  o->newline(-1) << "}";
  o->newline() << "#endif";

  // initialize tracepoints (if needed)
  o->newline() << "#ifdef STAP_NEED_TRACEPOINTS";
  o->newline() << "rc = stp_tracepoint_init();";
  o->newline() << "if (rc) {";
  o->newline(1) << "_stp_error (\"couldn't initialize tracepoints\");";
  o->newline() << "goto out;";
  o->newline(-1) << "}";
  o->newline() << "#endif";

  // NB: we don't need per-_stp_module task_finders, since a single common one
  // set up in runtime/sym.c's _stp_sym_init() will scan through all _stp_modules. XXX - check this!
  o->newline() << "(void) probe_point;";
  o->newline() << "(void) i;";
  o->newline() << "(void) j;";
  o->newline() << "atomic_set (session_state(), STAP_SESSION_STARTING);";
  // This signals any other probes that may be invoked in the next little
  // while to abort right away.  Currently running probes are allowed to
  // terminate.  These may set STAP_SESSION_ERROR!

  // Allocate context structures.
  o->newline() << "rc = _stp_runtime_contexts_alloc();";
  o->newline() << "if (rc != 0)";
  o->newline(1) << "goto out;";
  o->indent(-1);

  for (unsigned i=0; i<session->globals.size(); i++)
    {
      vardecl* v = session->globals[i];
      if (v->index_types.size() > 0)
    o->newline() << getmap (v).init();
      else if (session->runtime_usermode_p() && v->arity == 0
               && (v->type == pe_long || v->type == pe_string))
    c_assign(getvar (v).value(), "stp_global_init." + c_globalname(v->name), v->type, "BUG: global initialization", v->tok);
      else
    o->newline() << getvar (v).init();
      // NB: in case of failure of allocation, "rc" will be set to non-zero.
      // Allocation can in general continue.

      o->newline() << "if (rc) {";
      o->newline(1) << "_stp_error (\"global variable '" << v->name << "' allocation failed\");";
      o->newline() << "goto out;";
      o->newline(-1) << "}";

      o->newline() << "global_lock_init(" << c_globalname (v->name) << ");";
      o->newline() << "#ifdef STP_TIMING";
      o->newline() << "atomic_set(global_skipped(" << c_globalname (v->name) << "), 0);";
      o->newline() << "#endif";
    }

  // Print a message to the kernel log about this module.  This is
  // intended to help debug problems with systemtap modules.
  if (! session->runtime_usermode_p())
    o->newline() << "_stp_print_kernel_info("
         << "\"" << VERSION
         << "/" << dwfl_version (NULL) << "\""
         << ", (num_online_cpus() * sizeof(struct context))"
         << ", " << session->probes.size()
         << ");";
  // In dyninst mode, we need to know when all the globals have been
  // allocated and we're ready to run probe registration.
  else
    {
      o->newline() << "rc = stp_session_init_finished();";
      o->newline() << "if (rc) goto out;";
    }

  if (!session->runtime_usermode_p())
    {
      // Initialize workqueue needed for on-the-fly arming/disarming
      o->newline() << "#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)";
      o->newline() << "INIT_WORK(&module_refresher_work, module_refresher, NULL);";
      o->newline() << "#else";
      o->newline() << "INIT_WORK(&module_refresher_work, module_refresher);";
      o->newline() << "#endif";
    }

  // Initialize probe conditions
  for (unsigned i=0; i<session->probes.size(); i++)
    emit_probe_condition_initialize(session->probes[i]);

  // Run all probe registrations.  This actually runs begin probes.

  for (unsigned i=0; i<g.size(); i++)
    {
      g[i]->emit_module_init (*session);
      // NB: this gives O(N**2) amount of code, but luckily there
      // are only seven or eight derived_probe_groups, so it's ok.
      o->newline() << "if (rc) {";
      // If a probe types's emit_module_init() wants to handle error
      // messages itself, it should set probe_point to NULL,
      o->newline(1) << "if (probe_point)";
      o->newline(1) << "_stp_error (\"probe %s registration error (rc %d)\", probe_point, rc);";
      o->indent(-1);
      // NB: we need to be in the error state so timers can shutdown cleanly,
      // and so end probes don't run.  OTOH, error probes can run.
      o->newline() << "atomic_set (session_state(), STAP_SESSION_ERROR);";
      if (i>0)
        for (int j=i-1; j>=0; j--)
          g[j]->emit_module_exit (*session);
      o->newline() << "goto out;";
      o->newline(-1) << "}";
    }

  // All registrations were successful.  Consider the system started.
  o->newline() << "if (atomic_read (session_state()) == STAP_SESSION_STARTING)";
  // NB: only other valid state value is ERROR, in which case we don't
  o->newline(1) << "atomic_set (session_state(), STAP_SESSION_RUNNING);";
  o->newline(-1);

  // Run all post-session starting code.
  for (unsigned i=0; i<g.size(); i++)
    {
      g[i]->emit_module_post_init (*session);
    }

  if (!session->runtime_usermode_p())
    {
      o->newline() << "#ifdef STP_ON_THE_FLY_TIMER_ENABLE";

      // Initialize hrtimer needed for on-the-fly arming/disarming
      o->newline() << "hrtimer_init(&module_refresh_timer, CLOCK_MONOTONIC,";
      o->newline() << "             HRTIMER_MODE_REL);";
      o->newline() << "module_refresh_timer.function = &module_refresh_timer_cb;";

      // We check here if it's worth it to start the timer at all. We only need
      // the background timer if there is a probe which doesn't support directy
      // scheduling work (otf_safe_context() == false), but yet does affect the
      // condition of at least one probe which supports on-the-fly operations.
      {
        // for each derived probe...
        bool start_timer = false;
        for (unsigned i=0; i<session->probes.size() && !start_timer; i++)
          {
            // if it isn't safe in this probe type to directly schedule work,
            // and this probe could affect other probes...
            if (session->probes[i]->group
                && !session->probes[i]->group->otf_safe_context(*session)
                && !session->probes[i]->probes_with_affected_conditions.empty())
              {
                // and if any of those possible probes support on-the-fly operations,
                // then we'll need the timer
                for (set<derived_probe*>::const_iterator
                      it  = session->probes[i]->probes_with_affected_conditions.begin();
                      it != session->probes[i]->probes_with_affected_conditions.end()
                            && !start_timer; ++it)
                  {
                    if ((*it)->group->otf_supported(*session))
                      start_timer = true;
                  }
              }
          }

        if (start_timer)
          {
            o->newline() << "hrtimer_start(&module_refresh_timer,";
            o->newline() << "              ktime_set(0, STP_ON_THE_FLY_INTERVAL),";
            o->newline() << "              HRTIMER_MODE_REL);";
          }
      }

      o->newline() << "#endif /* STP_ON_THE_FLY_TIMER_ENABLE */";
    }

  o->newline() << "return 0;";

  // Error handling path; by now all partially registered probe groups
  // have been unregistered.
  o->newline(-1) << "out:";
  o->indent(1);

  // If any registrations failed, we will need to deregister the globals,
  // as this is our only chance.
  for (unsigned i=0; i<session->globals.size(); i++)
    {
      vardecl* v = session->globals[i];
      if (v->index_types.size() > 0)
    o->newline() << getmap (v).fini();
      else
    o->newline() << getvar (v).fini();
    }

  // For any partially registered/unregistered kernel facilities.
  o->newline() << "atomic_set (session_state(), STAP_SESSION_STOPPED);";
  o->newline() << "stp_synchronize_sched();";

  // In case tracepoints were started, they need to be cleaned up
  o->newline() << "#ifdef STAP_NEED_TRACEPOINTS";
  o->newline() << " stp_tracepoint_exit();";
  o->newline() << "#endif";

  // In case gettimeofday was started, it needs to be stopped
  o->newline() << "#ifdef STAP_NEED_GETTIMEOFDAY";
  o->newline() << " _stp_kill_time();"// An error is no cause to hurry...
  o->newline() << "#endif";

  // Free up the context memory after an error too
  o->newline() << "_stp_runtime_contexts_free();";

  o->newline() << "return rc;";
  o->newline(-1) << "}\n";
}


void
c_unparser::emit_module_refresh ()
{
  o->newline() << "static void systemtap_module_refresh (const char *modname) {";
  o->newline(1) << "int state;";
  o->newline() << "int i=0, j=0;"; // for derived_probe_group use

  if (!session->runtime_usermode_p())
    {
      o->newline() << "#if defined(STP_TIMING)";
      o->newline() << "cycles_t cycles_atstart = get_cycles();";
      o->newline() << "#endif";
    }

  // Ensure we're only doing the refreshing one at a time. NB: it's important
  // that we get the lock prior to checking the session_state, in case whoever
  // is holding the lock (e.g. systemtap_module_exit()) changes it.
  if (!session->runtime_usermode_p())
    o->newline() << "mutex_lock(&module_refresh_mutex);";

  /* If we're not in STARTING/RUNNING state, don't try doing any work.
     PR16766 */
  o->newline() << "state = atomic_read (session_state());";
  o->newline() << "if (state != STAP_SESSION_RUNNING && state != STAP_SESSION_STARTING && state != STAP_SESSION_ERROR) {";
  // cannot _stp_warn etc. since we're not in probe context
  o->newline(1) << "#if defined(__KERNEL__)";
  o->newline() << "if (state != STAP_SESSION_STOPPING)";
  o->newline(1) << "printk (KERN_ERR \"stap module notifier triggered in unexpected state %d\\n\", state);";
  o->indent(-1);
  o->newline() << "#endif";

  if (!session->runtime_usermode_p())
    o->newline() << "mutex_unlock(&module_refresh_mutex);";

  o->newline() << "return;";
  o->newline(-1) << "}";

  o->newline() << "(void) i;";
  o->newline() << "(void) j;";

  vector<derived_probe_group*> g = all_session_groups (*session);
  for (unsigned i=0; i<g.size(); i++)
    {
      g[i]->emit_module_refresh (*session);
    }

  if (!session->runtime_usermode_p())
    {
      // see also common_probe_entryfn_epilogue()
      o->newline() << "#if defined(STP_TIMING)";
      o->newline() << "if (likely(g_refresh_timing)) {";
      o->newline(1) << "cycles_t cycles_atend = get_cycles ();";
      o->newline() << "int32_t cycles_elapsed = ((int32_t)cycles_atend > (int32_t)cycles_atstart)";
      o->newline(1) << "? ((int32_t)cycles_atend - (int32_t)cycles_atstart)";
      o->newline() << ": (~(int32_t)0) - (int32_t)cycles_atstart + (int32_t)cycles_atend + 1;";
      o->indent(-1);
      o->newline() << "_stp_stat_add(g_refresh_timing, cycles_elapsed);";
      o->newline(-1) << "}";
      o->newline() << "#endif";
    }

  if (!session->runtime_usermode_p())
    o->newline() << "mutex_unlock(&module_refresh_mutex);";

  o->newline(-1) << "}\n";
}


void
c_unparser::emit_module_exit ()
{
  o->newline() << "static void systemtap_module_exit (void) {";
  // rc?
  o->newline(1) << "int i=0, j=0;"; // for derived_probe_group use
  o->newline() << "(void) i;";
  o->newline() << "(void) j;";
  // If we aborted startup, then everything has been cleaned up already, and
  // module_exit shouldn't even have been called.  But since it might be, let's
  // beat a hasty retreat to avoid double uninitialization.
  o->newline() << "if (atomic_read (session_state()) == STAP_SESSION_STARTING)";
  o->newline(1) << "return;";
  o->indent(-1);

  o->newline() << "if (atomic_read (session_state()) == STAP_SESSION_RUNNING)";
  // NB: only other valid state value is ERROR, in which case we don't
  o->newline(1) << "atomic_set (session_state(), STAP_SESSION_STOPPING);";
  o->indent(-1);
  // This signals any other probes that may be invoked in the next little
  // while to abort right away.  Currently running probes are allowed to
  // terminate.  These may set STAP_SESSION_ERROR!

  if (!session->runtime_usermode_p())
    {
      o->newline() << "#ifdef STP_ON_THE_FLY_TIMER_ENABLE";
      o->newline() << "hrtimer_cancel(&module_refresh_timer);";
      o->newline() << "#endif";
    }

  // cargo cult prologue ... hope to flush any pending workqueue items too
  o->newline() << "stp_synchronize_sched();";

  // Get the lock before exiting to ensure there's no one in module_refresh
  // NB: this should't be able to happen, because both the module_refresh_timer
  // and the workqueue ought to have been shut down by now.
  if (!session->runtime_usermode_p())
    o->newline() << "mutex_lock(&module_refresh_mutex);";

  // We're processing the derived_probe_group list in reverse
  // order.  This ensures that probes get unregistered in reverse
  // order of the way they were registered.
  vector<derived_probe_group*> g = all_session_groups (*session);
  for (vector<derived_probe_group*>::reverse_iterator i = g.rbegin();
       i != g.rend(); i++)
    (*i)->emit_module_exit (*session); // NB: runs "end" probes

  if (!session->runtime_usermode_p())
    o->newline() << "mutex_unlock(&module_refresh_mutex);";

  // But some other probes may have launched too during unregistration.
  // Let's wait a while to make sure they're all done, done, done.

  // cargo cult prologue
  o->newline() << "stp_synchronize_sched();";

  // NB: systemtap_module_exit is assumed to be called from ordinary
  // user context, say during module unload.  Among other things, this
  // means we can sleep a while.
  o->newline() << "_stp_runtime_context_wait();";

  // cargo cult epilogue
  o->newline() << "atomic_set (session_state(), STAP_SESSION_STOPPED);";
  o->newline() << "stp_synchronize_sched();";

  // XXX: might like to have an escape hatch, in case some probe is
  // genuinely stuck somehow

  for (unsigned i=0; i<session->globals.size(); i++)
    {
      vardecl* v = session->globals[i];
      if (v->index_types.size() > 0)
    o->newline() << getmap (v).fini();
      else
    o->newline() << getvar (v).fini();
    }

  // We're finished with the contexts if we're not in dyninst
  // mode. The dyninst mode needs the contexts, since print buffers
  // are stored there.
  if (!session->runtime_usermode_p())
    {
      o->newline() << "_stp_runtime_contexts_free();";
    }
  else
    {
      o->newline() << "struct context* __restrict__ c;";
      o->newline() << "c = _stp_runtime_entryfn_get_context();";
    }

  // teardown tracepoints (if needed)
  o->newline() << "#ifdef STAP_NEED_TRACEPOINTS";
  o->newline() << " stp_tracepoint_exit();";
  o->newline() << "#endif";

  // teardown gettimeofday (if needed)
  o->newline() << "#ifdef STAP_NEED_GETTIMEOFDAY";
  o->newline() << " _stp_kill_time();"// Go to a beach.  Drink a beer.
  o->newline() << "#endif";

  // NB: PR13386 points out that _stp_printf may be called from contexts
  // without already active preempt disabling, which breaks various uses
  // of smp_processor_id().  So we temporary block preemption around this
  // whole printing blockXXX: get_cpu() / put_cpu() may work just as well.
  o->newline() << "preempt_disable();";

  // print per probe point timing/alibi statistics
  o->newline() << "#if defined(STP_TIMING) || defined(STP_ALIBI)";
  o->newline() << "_stp_printf(\"----- probe hit report: \\n\");";
  o->newline() << "for (i = 0; i < ARRAY_SIZE(stap_probes); ++i) {";
  o->newline(1) << "const struct stap_probe *const p = &stap_probes[i];";
  o->newline() << "#ifdef STP_ALIBI";
  o->newline() << "int alibi = atomic_read(probe_alibi(i));";
  o->newline() << "if (alibi)";
  o->newline(1) << "_stp_printf (\"%s, (%s), hits: %d,%s, index: %d\\n\",";
  o->newline(2) << "p->pp, p->location, alibi, p->derivation, i);";
  o->newline(-3) << "#endif"; // STP_ALIBI
  o->newline() << "#ifdef STP_TIMING";
  o->newline() << "if (likely (probe_timing(i))) {"; // NB: check for null stat object
  o->newline(1) << "struct stat_data *stats = _stp_stat_get (probe_timing(i), 0);";
  o->newline() << "if (stats->count) {";
  o->newline(1) << "int64_t avg = _stp_div64 (NULL, stats->sum, stats->count);";
  o->newline() << "_stp_printf (\"%s, (%s), hits: %lld, "
           << (!session->runtime_usermode_p() ? "cycles" : "nsecs")
           << ": %lldmin/%lldavg/%lldmax,%s, index: %d\\n\",";
  o->newline(2) << "p->pp, p->location, (long long) stats->count,";
  o->newline() << "(long long) stats->min, (long long) avg, (long long) stats->max,";
  o->newline() << "p->derivation, i);";
  o->newline(-3) << "}";
  o->newline() << "_stp_stat_del (probe_timing(i));";
  o->newline(-1) << "}";
  o->newline() << "#endif"; // STP_TIMING
  o->newline(-1) << "}";

  if (!session->runtime_usermode_p())
    {
      o->newline() << "#if defined(STP_TIMING)";
      o->newline() << "_stp_printf(\"----- refresh report:\\n\");";
      o->newline() << "if (likely (g_refresh_timing)) {";
      o->newline(1) << "struct stat_data *stats = _stp_stat_get (g_refresh_timing, 0);";
      o->newline() << "if (stats->count) {";
      o->newline(1) << "int64_t avg = _stp_div64 (NULL, stats->sum, stats->count);";
      o->newline() << "_stp_printf (\"hits: %lld, cycles: %lldmin/%lldavg/%lldmax\\n\",";
      o->newline(2) << "(long long) stats->count, (long long) stats->min, ";
      o->newline() <<  "(long long) avg, (long long) stats->max);";
      o->newline(-3) << "}";
      o->newline() << "_stp_stat_del (g_refresh_timing);";
      o->newline(-1) << "}";
      o->newline() << "#endif"; // STP_TIMING
    }

  o->newline() << "_stp_print_flush();";
  o->newline() << "#endif";

  // print final error/skipped counts if non-zero
  o->newline() << "if (atomic_read (skipped_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()), "
                << "(int) atomic_read (skipped_count()));";
  o->newline() << "#ifdef STP_TIMING";
  o->newline() << "{";
  o->newline(1) << "int ctr;";
  for (unsigned i=0; i<session->globals.size(); i++)
    {
      string orig_vn = session->globals[i]->name;
      string vn = c_globalname (orig_vn);
      o->newline() << "ctr = atomic_read (global_skipped(" << vn << "));";
      o->newline() << "if (ctr) _stp_warn (\"Skipped due to global '%s' lock timeout: %d\\n\", "
                   << lex_cast_qstring(orig_vn) << ", ctr);";
    }
  o->newline() << "ctr = atomic_read (skipped_count_lowstack());";
  o->newline() << "if (ctr) _stp_warn (\"Skipped due to low stack: %d\\n\", ctr);";
  o->newline() << "ctr = atomic_read (skipped_count_reentrant());";
  o->newline() << "if (ctr) _stp_warn (\"Skipped due to reentrancy: %d\\n\", ctr);";
  o->newline() << "ctr = atomic_read (skipped_count_uprobe_reg());";
  o->newline() << "if (ctr) _stp_warn (\"Skipped due to uprobe register failure: %d\\n\", ctr);";
  o->newline() << "ctr = atomic_read (skipped_count_uprobe_unreg());";
  o->newline() << "if (ctr) _stp_warn (\"Skipped due to uprobe unregister failure: %d\\n\", ctr);";
  o->newline(-1) << "}";
  o->newline () << "#endif";
  o->newline() << "_stp_print_flush();";
  o->newline(-1) << "}";

  // NB: PR13386 needs to restore preemption-blocking counts
  o->newline() << "preempt_enable_no_resched();";

  // In dyninst mode, now we're done with the contexts, transport, everything!
  if (session->runtime_usermode_p())
    {
      o->newline() << "_stp_runtime_entryfn_put_context(c);";
      o->newline() << "_stp_dyninst_transport_shutdown();";
      o->newline() << "_stp_runtime_contexts_free();";
    }

  o->newline(-1) << "}\n";
}

struct max_action_info: public functioncall_traversing_visitor
{
  max_action_info(systemtap_session& s): sess(s), statement_count(0) {}

  systemtap_session& sess;
  unsigned statement_count;
  static const unsigned max_statement_count = ~0;

  void add_stmt_count (unsigned val)
    {
      statement_count = (statement_count > max_statement_count - val) ? max_statement_count : statement_count + val;
    }
  void add_max_stmt_count () { statement_count = max_statement_count; }
  bool statement_count_finite() { return statement_count < max_statement_count; }

  void visit_for_loop (for_loop* stmt) { add_max_stmt_count(); }
  void visit_foreach_loop (foreach_loop* stmt) { add_max_stmt_count(); }
  void visit_expr_statement (expr_statement *stmt)
    {
      add_stmt_count(1);
      traversing_visitor::visit_expr_statement(stmt); // which will trigger visit_functioncall, if applicable
    }
  void visit_if_statement (if_statement *stmt)
    {
      add_stmt_count(1);
      stmt->condition->visit(this);

      // Create new visitors for the two forks.  Copy the nested[] set
      // to prevent infinite recursion for a   function f () { if (a) f() }
      max_action_info tmp_visitor_then (*this);
      max_action_info tmp_visitor_else (*this);
      stmt->thenblock->visit(& tmp_visitor_then);
      if (stmt->elseblock)
        {
          stmt->elseblock->visit(& tmp_visitor_else);
        }

      // Simply overwrite our copy of statement_count, since these
      // visitor copies already included our starting count.
      statement_count = max(tmp_visitor_then.statement_count, tmp_visitor_else.statement_count);
    }

  void note_recursive_functioncall (functioncall *e) { add_max_stmt_count(); }

  void visit_null_statement (null_statement *stmt) { add_stmt_count(1); }
  void visit_return_statement (return_statement *stmt) { add_stmt_count(1); }
  void visit_delete_statement (delete_statement *stmt) { add_stmt_count(1); }
  void visit_next_statement (next_statement *stmt) { add_stmt_count(1); }
  void visit_break_statement (break_statement *stmt) { add_stmt_count(1); }
  void visit_continue_statement (continue_statement *stmt) { add_stmt_count(1); }
};

void
c_unparser::emit_function (functiondecl* v)
{
  o->newline() << "static void " << c_funcname (v->name)
            << " (struct context* __restrict__ c) {";
  o->indent(1);
  this->current_probe = 0;
  this->current_function = v;
  this->tmpvar_counter = 0;
  this->action_counter = 0;
  this->already_checked_action_count = false;

  o->newline() << "__label__ out;";
  o->newline()
    << "struct " << c_funcname (v->name) << "_locals * "
    << " __restrict__ l = "
    << "& c->locals[c->nesting+1]." << c_funcname (v->name) // NB: nesting+1
    << ";";
  o->newline() << "(void) l;"; // make sure "l" is marked used
  o->newline() << "#define CONTEXT c";
  o->newline() << "#define THIS l";
  for (unsigned i = 0; i < v->formal_args.size(); i++) {
    o->newline() << c_arg_define(v->formal_args[i]->name); // #define STAP_ARG_foo ...
  }
  for (unsigned i = 0; i < v->locals.size(); i++) {
    o->newline() << c_arg_define(v->locals[i]->name); // #define STAP_ARG_foo ...
  }
  // define STAP_RETVALUE only if the function is non-void
  if (v->type != pe_unknown)
    o->newline() << "#define STAP_RETVALUE THIS->__retvalue";

  // set this, in case embedded-c code sets last_error but doesn't otherwise identify itself
  o->newline() << "c->last_stmt = " << lex_cast_qstring(*v->tok) << ";";

  // check/increment nesting level
  // NB: incoming c->nesting level will be -1 (if we're called directly from a probe),
  // or 0...N (if we're called from another function).  Incoming parameters are already
  // stored in c->locals[c->nesting+1].  See also ::emit_common_header() for more.

  o->newline() << "if (unlikely (c->nesting+1 >= MAXNESTING)) {";
  o->newline(1) << "c->last_error = ";
  o->line() << STAP_T_02;
  o->newline() << "return;";
  o->newline(-1) << "} else {";
  o->newline(1) << "c->nesting ++;";
  o->newline(-1) << "}";

  // initialize locals
  // XXX: optimization: use memset instead
  for (unsigned i=0; i<v->locals.size(); i++)
    {
      if (v->locals[i]->index_types.size() > 0) // array?
    throw SEMANTIC_ERROR (_("array locals not supported, missing global declaration?"),
                              v->locals[i]->tok);

      o->newline() << getvar (v->locals[i]).init();
    }

  // initialize return value, if any
  if (v->type != pe_unknown)
    {
      var retvalue = var(this, true, v->type, "__retvalue", false); // not mangled
      o->newline() << retvalue.init();
    }

  switch (v->type)
    {
    case pe_long:
      o->newline() << "#define STAP_RETURN(v) do { STAP_RETVALUE = (int64_t) (v); "
        "goto out; } while(0)";
      break;

    case pe_string:
      o->newline() <<
        "#define STAP_RETURN(v) do { strlcpy(STAP_RETVALUE, (v), MAXSTRINGLEN); "
        "goto out; } while(0)";
      break;

    default:
      o->newline() << "#define STAP_RETURN() do { goto out; } while(0)";
      break;
    }

  o->newline() << "#define STAP_ERROR(...) do { snprintf(CONTEXT->error_buffer, MAXSTRINGLEN, __VA_ARGS__); CONTEXT->last_error = CONTEXT->error_buffer; goto out; } while (0)";
  o->newline() << "#define return goto out"; // redirect embedded-C return

  max_action_info mai (*session);
  v->body->visit (&mai);

  if (mai.statement_count_finite() && !session->suppress_time_limits
      && !session->unoptimized) // this is a finite-statement-count function
    {
      o->newline() << "if (c->actionremaining < " << mai.statement_count
                   << ") { c->last_error = " << STAP_T_04 << "goto out; }";
      this->already_checked_action_count = true;
    }

  v->body->visit (this);
  o->newline() << "#undef return";
  o->newline() << "#undef STAP_ERROR";
  o->newline() << "#undef STAP_RETURN";

  this->current_function = 0;

  record_actions(0, v->body->tok, true);

  o->newline(-1) << "out:";
  o->newline(1) << "if (0) goto out;"; // make sure out: is marked used

  // Function prologue: this is why we redirect the "return" above.
  // Decrement nesting level.
  o->newline() << "c->nesting --;";

  o->newline() << "#undef CONTEXT";
  o->newline() << "#undef THIS";
  for (unsigned i = 0; i < v->formal_args.size(); i++) {
    o->newline() << c_arg_undef(v->formal_args[i]->name); // #undef STAP_ARG_foo
  }
  for (unsigned i = 0; i < v->locals.size(); i++) {
    o->newline() << c_arg_undef(v->locals[i]->name); // #undef STAP_ARG_foo
  }
  o->newline() << "#undef STAP_RETVALUE";
  o->newline(-1) << "}\n";

  this->already_checked_action_count = false;
}

#define DUPMETHOD_CALL 0
#define DUPMETHOD_ALIAS 0
#define DUPMETHOD_RENAME 1

void
c_unparser::emit_probe (derived_probe* v)
{
  this->current_function = 0;
  this->current_probe = v;
  this->tmpvar_counter = 0;
  this->action_counter = 0;
  this->already_checked_action_count = false;

  // If we about to emit a probe that is exactly the same as another
  // probe previously emitted, make the second probe just call the
  // first one.
  //
  // Notice we're using the probe body itself instead of the emitted C
  // probe body to compare probes.  We need to do this because the
  // emitted C probe body has stuff in it like:
  //   c->last_stmt = "identifier 'printf' at foo.stp:<line>:<column>";
  //
  // which would make comparisons impossible.
  //
  // --------------------------------------------------------------------------
  // NB: see also c_unparser:emit_common_header(), which deliberately but sadly
  // duplicates this calculation.
  // --------------------------------------------------------------------------
  //
  ostringstream oss;

  v->print_dupe_stamp (oss);
  v->body->print(oss);

  // Since the generated C changes based on whether or not the probe
  // needs locks around global variables, this needs to be reflected
  // here.  We don't want to treat as duplicate the handlers of
  // begin/end and normal probes that differ only in need_global_locks.
  oss << "# needs_global_locks: " << v->needs_global_locks () << endl;

  // If an identical probe has already been emitted, just call that
  // one.
  if (!session->unoptimized && probe_contents.count(oss.str()) != 0)
    {
      string dupe = probe_contents[oss.str()];

      // NB: Elision of context variable structs is a separate
      // operation which has already taken place by now.
      if (session->verbose > 1)
        clog << _F("%s elided, duplicates %s\n", v->name.c_str(), dupe.c_str());

#if DUPMETHOD_CALL
      // This one emits a direct call to the first copy.
      o->newline();
      o->newline() << "static void " << v->name << " (struct context * __restrict__ c) ";
      o->newline() << "{ " << dupe << " (c); }";
#elif DUPMETHOD_ALIAS
      // This one defines a function alias, arranging gcc to emit
      // several equivalent symbols for the same function body.
      // For some reason, on gcc 4.1, this is twice as slow as
      // the CALL option.
      o->newline();
      o->newline() << "static void " << v->name << " (struct context * __restrict__ c) ";
      o->line() << "__attribute__ ((alias (\"" << dupe << "\")));";
#elif DUPMETHOD_RENAME
      // This one is sneaky.  It emits nothing for duplicate probe
      // handlers.  It instead redirects subsequent references to the
      // probe handler function to the first copy, *by name*.
      v->name = dupe;
#else
#error "Unknown duplicate elimination method"
#endif
    }
  else // This probe is unique.  Remember it and output it.
    {
      o->newline();
      o->newline() << "static void " << v->name << " (struct context * __restrict__ c) ";
      o->line () << "{";
      o->indent (1);

      probe_contents[oss.str()] = v->name;

      o->newline() << "__label__ out;";

      // emit static read/write lock decls for global variables
      if (v->needs_global_locks ())
        {
          varuse_collecting_visitor vut(*session);
          v->body->visit (& vut);

          // also visit any probe conditions which this current probe might
          // evaluate so that read locks are emitted as necessary: e.g. suppose
          //    probe X if (a || b) {...} probe Y {a = ...} probe Z {b = ...}
          // then Y and Z will already write-lock a and b respectively, but they
          // also need a read-lock on b and a respectively, since they will read
          // them when evaluating the new cond_enabled field (see c_unparser::
          // emit_probe_condition_update()).
          for (set<derived_probe*>::const_iterator
                it  = v->probes_with_affected_conditions.begin();
                it != v->probes_with_affected_conditions.end(); ++it)
            {
              assert((*it)->sole_location()->condition != NULL);
              (*it)->sole_location()->condition->visit (& vut);
            }

          emit_lock_decls (vut);
        }

      // initialize frame pointer
      o->newline() << "struct " << v->name << "_locals * __restrict__ l = "
                   << "& c->probe_locals." << v->name << ";";
      o->newline() << "(void) l;"; // make sure "l" is marked used

      // Emit runtime safety net for unprivileged mode.
      // NB: In usermode, the system restricts our privilege for us.
      if (!session->runtime_usermode_p())
        v->emit_privilege_assertion (o);

      // emit probe local initialization block

      v->emit_probe_local_init(*this->session, o);

      // emit all read/write locks for global variables
      if (v->needs_global_locks ())
        emit_locks ();

      // initialize locals
      for (unsigned j=0; j<v->locals.size(); j++)
        {
      if (v->locals[j]->synthetic)
            continue;
      if (v->locals[j]->index_types.size() > 0) // array?
            throw SEMANTIC_ERROR (_("array locals not supported, missing global declaration?"),
                                  v->locals[j]->tok);
      else if (v->locals[j]->type == pe_long)
        o->newline() << "l->" << c_localname (v->locals[j]->name)
             << " = 0;";
      else if (v->locals[j]->type == pe_string)
        o->newline() << "l->" << c_localname (v->locals[j]->name)
             << "[0] = '\\0';";
      else
        throw SEMANTIC_ERROR (_("unsupported local variable type"),
                  v->locals[j]->tok);
        }

      v->initialize_probe_context_vars (o);

      max_action_info mai (*session);
      v->body->visit (&mai);
      if (session->verbose > 1)
        clog << _F("%d statements for probe %s", mai.statement_count, v->name.c_str()) << endl;

      if (mai.statement_count_finite() && !session->suppress_time_limits
          && !session->unoptimized) // this is a finite-statement-count probe
        {
          o->newline() << "if (c->actionremaining < " << mai.statement_count
                       << ") { c->last_error = " << STAP_T_04 << " goto out; }";
          this->already_checked_action_count = true;
        }


      v->body->visit (this);

      record_actions(0, v->body->tok, true);

      o->newline(-1) << "out:";
      // NB: no need to uninitialize locals, except if arrays/stats can
      // someday be local

      o->indent(1);

      if (!v->probes_with_affected_conditions.empty())
        {
          for (set<derived_probe*>::const_iterator
                it  = v->probes_with_affected_conditions.begin();
                it != v->probes_with_affected_conditions.end(); ++it)
            emit_probe_condition_update(*it);
        }

      if (v->needs_global_locks ())
    emit_unlocks ();

      // XXX: do this flush only if the body included a
      // print/printf/etc. routine!
      o->newline() << "_stp_print_flush();";
      o->newline(-1) << "}\n";
    }


  this->current_probe = 0;
  this->already_checked_action_count = false;
}

// Initializes the cond_enabled field by evaluating condition predicate.
void
c_unparser::emit_probe_condition_initialize(derived_probe* v)
{
  unsigned i = v->session_index;
  assert(i < session->probes.size());

  expression *cond = v->sole_location()->condition;
  string cond_enabled = "stap_probes[" + lex_cast(i) + "].cond_enabled";

  // no condition --> always enabled (initialized via STAP_PROBE_INIT)
  if (!cond)
    return;

  // turn general integer into boolean 0/1
  o->newline() << cond_enabled << " = !!";
  cond->visit(this);
  o->line() << ";";
}

// Updates the cond_enabled field and sets need_module_refresh if it was
// changed.
void
c_unparser::emit_probe_condition_update(derived_probe* v)
{
  unsigned i = v->session_index;
  assert(i < session->probes.size());

  expression *cond = v->sole_location()->condition;
  assert(cond);

  string cond_enabled = "stap_probes[" + lex_cast(i) + "].cond_enabled";

  // Concurrency note: we're safe modifying cond_enabled here since we emit
  // locks not only for globals we write to, but also for globals read in other
  // probes' whose conditions we visit below (see in c_unparser::emit_probe). So
  // we can be assured we're the only ones modifying cond_enabled.

  o->newline() << "if (" << cond_enabled << " != ";
  o->line() << "!!"; // NB: turn general integer into boolean 1 or 0
  v->sole_location()->condition->visit(this);
  o->line() << ") {";
  o->newline(1) << cond_enabled << " ^= 1;"; // toggle it

  // don't bother refreshing if on-the-fly not supported
  if (!session->runtime_usermode_p()
      && v->group && v->group->otf_supported(*session))
    o->newline() << "atomic_set(&need_module_refresh, 1);";

  o->newline(-1) << "}";
}

void
c_unparser::emit_lock_decls(const varuse_collecting_visitor& vut)
{
  unsigned numvars = 0;

  if (session->verbose > 1)
    clog << "probe " << current_probe->session_index << " "
            "('" << *current_probe->sole_location() << "') locks";

  // We can only make this static in kernel mode.  In stapdyn mode,
  // the globals and their locks are in shared memory.
  o->newline();
  if (!session->runtime_usermode_p())
    o->line() << "static ";
  o->line() << "const struct stp_probe_lock locks[] = {";
  o->indent(1);

  for (unsigned i = 0; i < session->globals.size(); i++)
    {
      vardecl* v = session->globals[i];
      bool read_p = vut.read.count(v) > 0;
      bool write_p = vut.written.count(v) > 0;
      if (!read_p && !write_p) continue;

      bool written_p;
      if (v->type == pe_stats) // read and write locks are flipped
        // Specifically, a "<<<" to a stats object is considered a
        // "shared-lock" operation, since it's implicitly done
        // per-cpu.  But a "@op(x)" extraction is an "exclusive-lock"
        // one, as is a (sorted or unsorted) foreach, so those cases
        // are excluded by the w & !r condition below.
        {
          if (write_p && !read_p) { read_p = true; write_p = false; }
          else if (read_p && !write_p) { read_p = false; write_p = true; }
          written_p = vcv_needs_global_locks.read.count(v) > 0;
        }
      else
        written_p = vcv_needs_global_locks.written.count(v) > 0;

      // We don't need to read lock "read-mostly" global variables.  A
      // "read-mostly" global variable is only written to within
      // probes that don't need global variable locking (such as
      // begin/end probes).  If vcv_needs_global_locks doesn't mark
      // the global as written to, then we don't have to lock it
      // here to read it safely.
      if (!written_p && read_p && !write_p)
        continue;

      o->newline() << "{";
      o->newline(1) << ".lock = global_lock(" + c_globalname(v->name) + "),";
      o->newline() << ".write_p = " << (write_p ? 1 : 0) << ",";
      o->newline() << "#ifdef STP_TIMING";
      o->newline() << ".skipped = global_skipped(" << c_globalname (v->name) << "),";
      o->newline() << "#endif";
      o->newline(-1) << "},";

      numvars ++;
      if (session->verbose > 1)
        clog << " " << v->name << "[" << (read_p ? "r" : "")
             << (write_p ? "w" : "")  << "]";
    }

  o->newline(-1) << "};";

  if (session->verbose > 1)
    {
      if (!numvars)
        clog << _(" nothing");
      clog << endl;
    }
}


void
c_unparser::emit_locks()
{
  o->newline() << "if (!stp_lock_probe(locks, ARRAY_SIZE(locks)))";
  o->newline(1) << "return;";
  o->indent(-1);
}


void
c_unparser::emit_unlocks()
{
  o->newline() << "stp_unlock_probe(locks, ARRAY_SIZE(locks));";
}


void
c_unparser::collect_map_index_types(vector<vardecl *> const & vars,
                    set< pair<vector<exp_type>, exp_type> > & types)
{
  for (unsigned i = 0; i < vars.size(); ++i)
    {
      vardecl *v = vars[i];
      if (v->arity > 0)
    {
      types.insert(make_pair(v->index_types, v->type));
    }
    }
}

string
mapvar::value_typename(exp_type e)
{
  switch (e)
    {
    case pe_long:
      return "INT64";
    case pe_string:
      return "STRING";
    case pe_stats:
      return "STAT";
    default:
      throw SEMANTIC_ERROR(_("array type is neither string nor long"));
    }
}

string
mapvar::key_typename(exp_type e)
{
  switch (e)
    {
    case pe_long:
      return "INT64";
    case pe_string:
      return "STRING";
    default:
      throw SEMANTIC_ERROR(_("array key is neither string nor long"));
    }
}

string
mapvar::shortname(exp_type e)
{
  switch (e)
    {
    case pe_long:
      return "i";
    case pe_string:
      return "s";
    default:
      throw SEMANTIC_ERROR(_("array type is neither string nor long"));
    }
}


void
c_unparser::emit_map_type_instantiations ()
{
  set< pair<vector<exp_type>, exp_type> > types;

  collect_map_index_types(session->globals, types);

  for (unsigned i = 0; i < session->probes.size(); ++i)
    collect_map_index_types(session->probes[i]->locals, types);

  for (map<string,functiondecl*>::iterator it = session->functions.begin(); it != session->functions.end(); it++)
    collect_map_index_types(it->second->locals, types);

  if (!types.empty())
    o->newline() << "#include \"alloc.c\"";

  for (set< pair<vector<exp_type>, exp_type> >::const_iterator i = types.begin();
       i != types.end(); ++i)
    {
      o->newline() << "#define VALUE_TYPE " << mapvar::value_typename(i->second);
      for (unsigned j = 0; j < i->first.size(); ++j)
    {
      string ktype = mapvar::key_typename(i->first.at(j));
      o->newline() << "#define KEY" << (j+1) << "_TYPE " << ktype;
    }
      /* For statistics, flag map-gen to pull in nested pmap-gen too.  */
      if (i->second == pe_stats)
    o->newline() << "#define MAP_DO_PMAP 1";
      o->newline() << "#include \"map-gen.c\"";
      o->newline() << "#undef MAP_DO_PMAP";
      o->newline() << "#undef VALUE_TYPE";
      for (unsigned j = 0; j < i->first.size(); ++j)
    {
      o->newline() << "#undef KEY" << (j+1) << "_TYPE";
    }
    }

  if (!types.empty())
    o->newline() << "#include \"map.c\"";

};


string
c_unparser::c_typename (exp_type e)
{
  switch (e)
    {
    case pe_long: return string("int64_t");
    case pe_string: return string("string_t");
    case pe_stats: return string("Stat");
    case pe_unknown:
    default:
      throw SEMANTIC_ERROR (_("cannot expand unknown type"));
    }
}


// XXX: the below is just for the sake of example; it's possibly
// better to expose the hash function in hash.cxx

// unsigned int
// do_hash (const char *e)
// {
//   unsigned int foo = 0;
//   while (*e) {
//     foo *= 101; foo += *e; e++;
//   }
//   return foo;
// }


string
c_unparser::c_localname (const string& e, bool mangle_oldstyle)
{
  if (strverscmp(session->compatible.c_str(), "1.8") < 0 || mangle_oldstyle)
    return e; // old mangling behaviour
  else
// XXX: we may wish to invent and/or test other mangling schemes, e.g.:
//  return "l_" + e + "_" + lex_cast(do_hash(e.c_str()));
    return "l_" + e;
}


string
c_unparser::c_globalname (const string& e)
{
  // XXX uncomment to test custom mangling:
  // return "s_" + e + "_" + lex_cast(do_hash(e.c_str()));
  return "s_" + e;
}


string
c_unparser::c_funcname (const string& e)
{
  // XXX uncomment to test custom mangling:
  // return "function_" + e + "_" + lex_cast(do_hash(e.c_str()));
  return "function_" + e;
}


string
c_unparser::c_arg_define (const string& e)
{
  return "#define STAP_ARG_" + e + " THIS->" + c_localname(e);
}


string
c_unparser::c_arg_undef (const string& e)
{
  return "#undef STAP_ARG_" + e;
}


string
c_unparser::c_expression (expression *e)
{
  // We want to evaluate expression 'e' and return its value as a
  // string.  In the case of expressions that are just numeric
  // constants, if we just print the value into a string, it won't
  // have the same value as being visited by c_unparser.  For
  // instance, a numeric constant evaluated using print() would return
  // "5", while c_unparser::visit_literal_number() would
  // return "((int64_t)5LL)".  String constants evaluated using
  // print() would just return the string, while
  // c_unparser::visit_literal_string() would return the string with
  // escaped double quote characters.  So, we need to "visit" the
  // expression.

  // However, we have to be careful of side effects.  Currently this
  // code is only being used for evaluating literal numbers and
  // strings, which currently have no side effects.  Until needed
  // otherwise, limit the use of this function to literal numbers and
  // strings.
  if (e->tok->type != tok_number && e->tok->type != tok_string)
    throw SEMANTIC_ERROR(_("unsupported c_expression token type"));

  // Create a fake output stream so we can grab the string output.
  ostringstream oss;
  translator_output tmp_o(oss);

  // Temporarily swap out the real translator_output stream with our
  // fake one.
  translator_output *saved_o = o;
  o = &tmp_o;

  // Visit the expression then restore the original output stream
  e->visit (this);
  o = saved_o;

  return (oss.str());
}


void
c_unparser::c_assign (var& lvalue, const string& rvalue, const token *tok)
{
  switch (lvalue.type())
    {
    case pe_string:
      c_strcpy(lvalue.value(), rvalue);
      break;
    case pe_long:
      o->newline() << lvalue << " = " << rvalue << ";";
      break;
    default:
      throw SEMANTIC_ERROR (_("unknown lvalue type in assignment"), tok);
    }
}


struct expression_is_functioncall : public expression_visitor
{
  c_unparser* parent;
  functioncall* fncall;
  expression_is_functioncall (c_unparser* p)
    : parent(p), fncall(NULL) {}

  void visit_expression (expression* e)
    {
      // works on the basis that every expression will, by default, call this
      // function, except for functioncall, as the visitor is overwritten
      fncall = NULL;
    }
  void visit_functioncall (functioncall* e)
    {
      fncall = e;
    }
};

void
c_unparser::c_assign (const string& lvalue, expression* rvalue,
              const string& msg)
{
  if (rvalue->type == pe_long)
    {
      o->newline() << lvalue << " = ";
      rvalue->visit (this);
      o->line() << ";";
    }
  else if (rvalue->type == pe_string)
    {
      expression_is_functioncall eif (this);
      rvalue->visit(& eif);
      if (!session->unoptimized && eif.fncall)
        {
          // let the functioncall know that the return value is being saved/used
          // and keep track of the lvalue, so that the retval assignment can
          // happen in ::visit_functioncall, to avoid complications with nesting.
          eif.fncall->var_assigned_to_retval = lvalue;
          eif.fncall->visit (this);
          o->line() << ";";
        }
      else
        {
          // will call rvalue->visit()
          c_strcpy (lvalue, rvalue);
        }
    }
  else
    {
      string fullmsg = msg + _(" type unsupported");
      throw SEMANTIC_ERROR (fullmsg, rvalue->tok);
    }
}


void
c_unparser::c_assign (const string& lvalue, const string& rvalue,
              exp_type type, const string& msg, const token* tok)
{
  if (type == pe_long)
    {
      o->newline() << lvalue << " = " << rvalue << ";";
    }
  else if (type == pe_string)
    {
      c_strcpy (lvalue, rvalue);
    }
  else
    {
      string fullmsg = msg + _(" type unsupported");
      throw SEMANTIC_ERROR (fullmsg, tok);
    }
}


void
c_unparser_assignment::c_assignop(tmpvar & res,
                  var const & lval,
                  tmpvar const & rval,
                  token const * tok)
{
  // This is common code used by scalar and array-element assignments.
  // It assumes an operator-and-assignment (defined by the 'pre' and
  // 'op' fields of c_unparser_assignment) is taking place between the
  // following set of variables:
  //
  // res: the result of evaluating the expression, a temporary
  // lval: the lvalue of the expression, which may be damaged
  // rval: the rvalue of the expression, which is a temporary or constant

  // we'd like to work with a local tmpvar so we can overwrite it in
  // some optimized cases

  translator_output* o = parent->o;

  if (res.type() == pe_string)
    {
      if (post)
    throw SEMANTIC_ERROR (_("post assignment on strings not supported"),
                  tok);
      if (op == "=")
    {
      parent->c_strcpy (lval.value(), rval.value());
      // no need for second copy
      res = rval;
        }
      else if (op == ".=")
    {
      parent->c_strcat (lval.value(), rval.value());
      res = lval;
    }
      else
        throw SEMANTIC_ERROR (_F("string assignment operator %s unsupported", op.c_str()), tok);
    }
  else if (op == "<<<")
    {
      assert(lval.type() == pe_stats);
      assert(rval.type() == pe_long);
      assert(res.type() == pe_long);
      o->newline() << "_stp_stat_add (" << lval << ", " << rval << ");";
      res = rval;
    }
  else if (res.type() == pe_long)
    {
      // a lot of operators come through this "gate":
      // - vanilla assignment "="
      // - stats aggregation "<<<"
      // - modify-accumulate "+=" and many friends
      // - pre/post-crement "++"/"--"
      // - "/" and "%" operators, but these need special handling in kernel

      // compute the modify portion of a modify-accumulate
      string macop;
      unsigned oplen = op.size();
      if (op == "=")
    macop = "*error*"; // special shortcuts below
      else if (op == "++" || op == "+=")
    macop = "+=";
      else if (op == "--" || op == "-=")
    macop = "-=";
      else if (oplen > 1 && op[oplen-1] == '=') // for *=, <<=, etc...
    macop = op;
      else
    // internal error
    throw SEMANTIC_ERROR (_("unknown macop for assignment"), tok);

      if (post)
    {
          if (macop == "/" || macop == "%" || op == "=")
            throw SEMANTIC_ERROR (_("invalid post-mode operator"), tok);

      o->newline() << res << " = " << lval << ";";

      if (macop == "+=" || macop == "-=")
        o->newline() << lval << " " << macop << " " << rval << ";";
      else
        o->newline() << lval << " = " << res << " " << macop << " " << rval << ";";
    }
      else
    {
          if (op == "=") // shortcut simple assignment
        {
          o->newline() << lval << " = " << rval << ";";
          res = rval;
        }
      else
        {
          if (macop == "/=" || macop == "%=")
        {
          o->newline() << "if (unlikely(!" << rval << ")) {";
          o->newline(1) << "c->last_error = ";
                  o->line() << STAP_T_03;
          o->newline() << "c->last_stmt = " << lex_cast_qstring(*rvalue->tok) << ";";
          o->newline() << "goto out;";
          o->newline(-1) << "}";
          o->newline() << lval << " = "
                   << ((macop == "/=") ? "_stp_div64" : "_stp_mod64")
                   << " (NULL, " << lval << ", " << rval << ");";
        }
          else
        o->newline() << lval << " " << macop << " " << rval << ";";
          res = lval;
        }
    }
    }
    else
      throw SEMANTIC_ERROR (_("assignment type not yet implemented"), tok);
}


void
c_unparser::c_declare(exp_type ty, const string &ident)
{
  o->newline() << c_typename (ty) << " " << ident << ";";
}


void
c_unparser::c_declare_static(exp_type ty, const string &ident)
{
  o->newline() << "static " << c_typename (ty) << " " << ident << ";";
}


void
c_unparser::c_strcpy (const string& lvalue, const string& rvalue)
{
  o->newline() << "strlcpy ("
           << lvalue << ", "
           << rvalue << ", MAXSTRINGLEN);";
}


void
c_unparser::c_strcpy (const string& lvalue, expression* rvalue)
{
  o->newline() << "strlcpy (" << lvalue << ", ";
  rvalue->visit (this);
  o->line() << ", MAXSTRINGLEN);";
}


void
c_unparser::c_strcat (const string& lvalue, const string& rvalue)
{
  o->newline() << "strlcat ("
           << lvalue << ", "
           << rvalue << ", MAXSTRINGLEN);";
}


void
c_unparser::c_strcat (const string& lvalue, expression* rvalue)
{
  o->newline() << "strlcat (" << lvalue << ", ";
  rvalue->visit (this);
  o->line() << ", MAXSTRINGLEN);";
}


bool
c_unparser::is_local(vardecl const *r, token const *tok)
{
  if (current_probe)
    {
      for (unsigned i=0; i<current_probe->locals.size(); i++)
    {
      if (current_probe->locals[i] == r)
        return true;
    }
    }
  else if (current_function)
    {
      for (unsigned i=0; i<current_function->locals.size(); i++)
    {
      if (current_function->locals[i] == r)
        return true;
    }

      for (unsigned i=0; i<current_function->formal_args.size(); i++)
    {
      if (current_function->formal_args[i] == r)
        return true;
    }
    }

  for (unsigned i=0; i<session->globals.size(); i++)
    {
      if (session->globals[i] == r)
    return false;
    }

  if (tok)
    throw SEMANTIC_ERROR (_("unresolved symbol"), tok);
  else
    throw SEMANTIC_ERROR (_("unresolved symbol: ") + r->name);
}


tmpvar
c_unparser::gensym(exp_type ty)
{
  return tmpvar (this, ty, tmpvar_counter);
}

aggvar
c_unparser::gensym_aggregate()
{
  return aggvar (this, tmpvar_counter);
}


var
c_unparser::getvar(vardecl *v, token const *tok)
{
  bool loc = is_local (v, tok);
  if (loc)
    return var (this, loc, v->type, v->name);
  else
    {
      statistic_decl sd;
      std::map<std::string, statistic_decl>::const_iterator i;
      i = session->stat_decls.find(v->name);
      if (i != session->stat_decls.end())
    sd = i->second;
      return var (this, loc, v->type, sd, v->name);
    }
}


mapvar
c_unparser::getmap(vardecl *v, token const *tok)
{
  if (v->arity < 1)
    throw SEMANTIC_ERROR(_("attempt to use scalar where map expected"), tok);
  statistic_decl sd;
  std::map<std::string, statistic_decl>::const_iterator i;
  i = session->stat_decls.find(v->name);
  if (i != session->stat_decls.end())
    sd = i->second;
  return mapvar (this, is_local (v, tok), v->type, sd,
      v->name, v->index_types, v->maxsize, v->wrap);
}


itervar
c_unparser::getiter(symbol *s)
{
  return itervar (s, tmpvar_counter);
}


// Queue up some actions to remove from actionremaining.  Set update=true at
// the end of basic blocks to actually update actionremaining and check it
// against MAXACTION.
void
c_unparser::record_actions (unsigned actions, const token* tok, bool update)
{
  action_counter += actions;

  // Update if needed, or after queueing up a few actions, in case of very
  // large code sequences.
  if (((update && action_counter > 0) || action_counter >= 10/*<-arbitrary*/)
    && !session->suppress_time_limits && !already_checked_action_count)
    {

      o->newline() << "c->actionremaining -= " << action_counter << ";";
      o->newline() << "if (unlikely (c->actionremaining <= 0)) {";
      o->newline(1) << "c->last_error = ";
      o->line() << STAP_T_04;

      // XXX it really ought to be illegal for anything to be missing a token,
      // but until we're sure of that, we need to defend against NULL.
      if (tok)
        o->newline() << "c->last_stmt = " << lex_cast_qstring(*tok) << ";";

      o->newline() << "goto out;";
      o->newline(-1) << "}";
      action_counter = 0;
    }
}


void
c_unparser::visit_block (block *s)
{
  o->newline() << "{";
  o->indent (1);

  for (unsigned i=0; i<s->statements.size(); i++)
    {
      try
        {
          s->statements[i]->visit (this);
      o->newline();
        }
      catch (const semantic_error& e)
        {
          session->print_error (e);
        }
    }
  o->newline(-1) << "}";
}


void c_unparser::visit_try_block (try_block *s)
{
  record_actions(0, s->tok, true); // flush prior actions

  o->newline() << "{";
  o->newline(1) << "__label__ normal_fallthrough;";
  o->newline(1) << "{";
  o->newline() << "__label__ out;";

  assert (!session->unoptimized || s->try_block); // dead_stmtexpr_remover would zap it
  if (s->try_block)
    {
      s->try_block->visit (this);
      record_actions(0, s->try_block->tok, true); // flush accumulated actions
    }
  o->newline() << "goto normal_fallthrough;";

  o->newline() << "if (0) goto out;"; // to prevent 'unused label' warnings
  o->newline() << "out:";
  o->newline() << ";"; // to have _some_ statement

  // Close the scope of the above nested 'out' label, to make sure
  // that the catch block, should it encounter errors, does not resolve
  // a 'goto out;' to the above label, causing infinite looping.
  o->newline(-1) << "}";

  o->newline() << "if (likely(c->last_error == NULL)) goto out;";

  if (s->catch_error_var)
    {
      var cev(getvar(s->catch_error_var->referent, s->catch_error_var->tok));
      c_strcpy (cev.value(), "c->last_error");
    }
  o->newline() << "c->last_error = NULL;";

  // Prevent the catch{} handler from even starting if MAXACTIONS have
  // already been used up.  Add one for the act of catching too.
  record_actions(1, s->tok, true);

  if (s->catch_block)
    {
      s->catch_block->visit (this);
      record_actions(0, s->catch_block->tok, true); // flush accumulated actions
    }

  o->newline() << "normal_fallthrough:";
  o->newline() << ";"; // to have _some_ statement
  o->newline(-1) << "}";
}


void
c_unparser::visit_embeddedcode (embeddedcode *s)
{
  // Automatically add a call to assert_is_myproc to any code tagged with
  // /* myproc-unprivileged */
  if (s->code.find ("/* myproc-unprivileged */") != string::npos)
    o->newline() << "assert_is_myproc();";
  o->newline() << "{";
  o->newline(1) << s->code;
  o->newline(-1) << "}";
}


void
c_unparser::visit_null_statement (null_statement *)
{
  o->newline() << "/* null */;";
}


void
c_unparser::visit_expr_statement (expr_statement *s)
{
  o->newline() << "(void) ";
  s->value->visit (this);
  o->line() << ";";
  record_actions(1, s->tok);
}


void
c_unparser::visit_if_statement (if_statement *s)
{
  record_actions(1, s->tok, true);
  o->newline() << "if (";
  o->indent (1);
  s->condition->visit (this);
  o->indent (-1);
  o->line() << ") {";
  o->indent (1);
  s->thenblock->visit (this);
  record_actions(0, s->thenblock->tok, true);
  o->newline(-1) << "}";
  if (s->elseblock)
    {
      o->newline() << "else {";
      o->indent (1);
      s->elseblock->visit (this);
      record_actions(0, s->elseblock->tok, true);
      o->newline(-1) << "}";
    }
}


void
c_tmpcounter::visit_block (block *s)
{
  // Key insight: individual statements of a block can reuse
  // temporary variable slots, since temporaries don't survive
  // statement boundaries.  So we use gcc's anonymous union/struct
  // facility to explicitly overlay the temporaries.
  parent->o->newline() << "union {";
  parent->o->indent(1);
  for (unsigned i=0; i<s->statements.size(); i++)
    {
      // To avoid lots of empty structs inside the union, remember
      // where we are now.  Then, output the struct start and remember
      // that positon.  If when we get done with the statement we
      // haven't moved, then we don't really need the struct.  To get
      // rid of the struct start we output, we'll seek back to where
      // we were before we output the struct.
      std::ostream::pos_type before_struct_pos = parent->o->tellp();
      parent->o->newline() << "struct {";
      parent->o->indent(1);
      std::ostream::pos_type after_struct_pos = parent->o->tellp();
      s->statements[i]->visit (this);
      parent->o->indent(-1);
      if (after_struct_pos == parent->o->tellp())
    parent->o->seekp(before_struct_pos);
      else
    parent->o->newline() << "};";
    }
  parent->o->newline(-1) << "};";
}

void
c_tmpcounter::visit_for_loop (for_loop *s)
{
  if (s->init) s->init->visit (this);
  s->cond->visit (this);
  s->block->visit (this);
  if (s->incr) s->incr->visit (this);
}


void
c_unparser::visit_for_loop (for_loop *s)
{
  string ctr = lex_cast (label_counter++);
  string toplabel = "top_" + ctr;
  string contlabel = "continue_" + ctr;
  string breaklabel = "break_" + ctr;

  // initialization
  if (s->init) s->init->visit (this);
  record_actions(1, s->tok, true);

  // condition
  o->newline(-1) << toplabel << ":";

  // Emit an explicit action here to cover the act of iteration.
  // Equivalently, it can stand for the evaluation of the condition
  // expression.
  o->indent(1);
  record_actions(1, s->tok);

  o->newline() << "if (! (";
  if (s->cond->type != pe_long)
    throw SEMANTIC_ERROR (_("expected numeric type"), s->cond->tok);
  s->cond->visit (this);
  o->line() << ")) goto " << breaklabel << ";";

  // body
  loop_break_labels.push_back (breaklabel);
  loop_continue_labels.push_back (contlabel);
  s->block->visit (this);
  record_actions(0, s->block->tok, true);
  loop_break_labels.pop_back ();
  loop_continue_labels.pop_back ();

  // iteration
  o->newline(-1) << contlabel << ":";
  o->indent(1);
  if (s->incr) s->incr->visit (this);
  o->newline() << "goto " << toplabel << ";";

  // exit
  o->newline(-1) << breaklabel << ":";
  o->newline(1) << "; /* dummy statement */";
}


struct arrayindex_downcaster
  : public traversing_visitor
{
  arrayindex *& arr;

  arrayindex_downcaster (arrayindex *& arr)
    : arr(arr)
  {}

  void visit_arrayindex (arrayindex* e)
  {
    arr = e;
  }
};


static bool
expression_is_arrayindex (expression *e,
              arrayindex *& hist)
{
  arrayindex *h = NULL;
  arrayindex_downcaster d(h);
  e->visit (&d);
  if (static_cast<void*>(h) == static_cast<void*>(e))
    {
      hist = h;
      return true;
    }
  return false;
}


// Look for opportunities to used a saved value at the beginning of the loop
void
c_unparser::visit_foreach_loop_value (visitor* vis, foreach_loop* s,
                                      const string& value)
{
  bool stable_value = false;

  // There are three possible cases that we might easily retrieve the value:
  //   1. foreach ([keys] in any_array_type)
  //   2. foreach (idx in @hist_*(stat))
  //   3. foreach (idx in @hist_*(stat[keys]))
  //
  // For 1 and 2, we just need to check that the keys/idx are const throughout
  // the loop.  For 3, we'd have to check also that the arbitrary keys
  // expressions indexing the stat are const -- much harder, so I'm punting
  // that case for now.

  symbol *array;
  hist_op *hist;
  classify_indexable (s->base, array, hist);

  if (!(hist && get_symbol_within_expression(hist->stat)->referent->arity > 0))
    {
      set<vardecl*> indexes;
      for (unsigned i=0; i < s->indexes.size(); ++i)
        indexes.insert(s->indexes[i]->referent);

      varuse_collecting_visitor v(*session);
      s->block->visit (&v);
      v.embedded_seen = false; // reset because we only care about the indexes
      if (v.side_effect_free_wrt(indexes))
        stable_value = true;
    }

  if (stable_value)
    {
      // Rather than trying to compare arrayindexes to this foreach_loop
      // manually, we just create a fake arrayindex that would match the
      // foreach_loop, render it as a string, and later render encountered
      // arrayindexes as strings and compare.
      arrayindex ai;
      ai.base = s->base;
      for (unsigned i=0; i < s->indexes.size(); ++i)
        ai.indexes.push_back(s->indexes[i]);
      string loopai = lex_cast(ai);
      foreach_loop_values[loopai] = value;
      s->block->visit (vis);
      foreach_loop_values.erase(loopai);
    }
  else
    s->block->visit (vis);
}


bool
c_unparser::get_foreach_loop_value (arrayindex* ai, string& value)
{
  if (!ai)
    return false;
  map<string,string>::iterator it = foreach_loop_values.find(lex_cast(*ai));
  if (it == foreach_loop_values.end())
    return false;
  value = it->second;
  return true;
}


void
c_tmpcounter::visit_foreach_loop (foreach_loop *s)
{
  symbol *array;
  hist_op *hist;
  classify_indexable (s->base, array, hist);

  if (array)
    {
      itervar iv = parent->getiter (array);
      parent->o->newline() << iv.declare();

      // Create temporaries for the array slice indexes that aren't wildcards
      for (unsigned i=0; i<s->array_slice.size(); i++)
        {
          if (s->array_slice[i])
            {
              tmpvar slice_index = parent->gensym (s->array_slice[i]->type);
              slice_index.declare(*parent);
              s->array_slice[i]->visit (this);
            }
        }
    }
  else
   {
     // See commentary in c_tmpcounter::visit_arrayindex for
     // discussion of tmpvars required to look into @hist_op(...)
     // expressions.

     // First make sure we have exactly one pe_long variable to use as
     // our bucket index.

     if (s->indexes.size() != 1 || s->indexes[0]->referent->type != pe_long)
       throw SEMANTIC_ERROR(_("Invalid indexing of histogram"), s->tok);

      // Then declare what we need to form the aggregate we're
      // iterating over, and all the tmpvars needed by our call to
      // load_aggregate().

      aggvar agg = parent->gensym_aggregate ();
      agg.declare(*(this->parent));
      load_aggregate (hist->stat);
    }

  // Create a temporary for the loop limit counter and the limit
  // expression result.
  if (s->limit)
    {
      tmpvar res_limit = parent->gensym (pe_long);
      res_limit.declare(*parent);

      s->limit->visit (this);

      tmpvar limitv = parent->gensym (pe_long);
      limitv.declare(*parent);
    }

  parent->visit_foreach_loop_value(this, s);
}

void
c_unparser::visit_foreach_loop (foreach_loop *s)
{
  symbol *array;
  hist_op *hist;
  classify_indexable (s->base, array, hist);

  string ctr = lex_cast (label_counter++);
  string toplabel = "top_" + ctr;
  string contlabel = "continue_" + ctr;
  string breaklabel = "break_" + ctr;

  if (array)
    {
      mapvar mv = getmap (array->referent, s->tok);
      itervar iv = getiter (array);
      vector<var> keys;

      // NB: structure parallels for_loop

      // initialization

      tmpvar *res_limit = NULL;
      if (s->limit)
        {
      // Evaluate the limit expression once.
      res_limit = new tmpvar(gensym(pe_long));
      c_assign (res_limit->value(), s->limit, "foreach limit");
    }

      // aggregate array if required
      if (mv.is_parallel())
    {
      o->newline() << "if (unlikely(NULL == " << mv.calculate_aggregate() << ")) {";
      o->newline(1) << "c->last_error = ";
          o->line() << STAP_T_05 << mv << "\";";
      o->newline() << "c->last_stmt = " << lex_cast_qstring(*s->tok) << ";";
      o->newline() << "goto out;";
      o->newline(-1) << "}";

      // sort array if desired
      if (s->sort_direction)
        {
          string sort_column;

          // If the user wanted us to sort by value, we'll sort by
          // @count or selected function instead for aggregates.
          // See runtime/map.c
          if (s->sort_column == 0)
                switch (s->sort_aggr) {
                default: case sc_none: case sc_count: sort_column = "SORT_COUNT"; break;
                case sc_sum: sort_column = "SORT_SUM"; break;
                case sc_min: sort_column = "SORT_MIN"; break;
                case sc_max: sort_column = "SORT_MAX"; break;
                case sc_average: sort_column = "SORT_AVG"; break;
                }
          else
        sort_column = lex_cast(s->sort_column);

          o->newline() << "else"; // only sort if aggregation was ok
          if (s->limit)
            {
          o->newline(1) << mv.function_keysym("sortn", true) <<" ("
                << mv.fetch_existing_aggregate() << ", "
                << *res_limit << ", " << sort_column << ", "
                << - s->sort_direction << ");";
        }
          else
            {
          o->newline(1) << mv.function_keysym("sort", true) <<" ("
                << mv.fetch_existing_aggregate() << ", "
                << sort_column << ", "
                << - s->sort_direction << ");";
        }
          o->indent(-1);
        }
        }
      else
    {
      // sort array if desired
      if (s->sort_direction)
        {
          if (s->limit)
            {
          o->newline() << mv.function_keysym("sortn") <<" ("
                   << mv.value() << ", "
                   << *res_limit << ", " << s->sort_column << ", "
                   << - s->sort_direction << ");";
        }
          else
            {
          o->newline() << mv.function_keysym("sort") <<" ("
                   << mv.value() << ", "
                   << s->sort_column << ", "
                   << - s->sort_direction << ");";
        }
        }
    }

      // NB: sort direction sense is opposite in runtime, thus the negation

      if (mv.is_parallel())
    aggregations_active.insert(mv.value());
      o->newline() << iv << " = " << iv.start (mv) << ";";

      tmpvar *limitv = NULL;
      if (s->limit)
      {
      // Create the loop limit variable here and initialize it.
      limitv = new tmpvar(gensym (pe_long));
      o->newline() << *limitv << " = 0LL;";
      }

      vector<tmpvar *> array_slice_vars;
      // store the the variables corresponding to the index of the array slice
      // as temporary variables
      if (!s->array_slice.empty())
          for (unsigned i = 0; i < s->array_slice.size(); ++i)
            {
              if (s->array_slice[i])
                {
                  tmpvar *asvar = new tmpvar(gensym(s->array_slice[i]->type));
                  c_assign(asvar->value(), s->array_slice[i], "array slice index");
                  array_slice_vars.push_back(asvar);
                }
              else
                array_slice_vars.push_back(NULL);
            }

      record_actions(1, s->tok, true);

      // condition
      o->newline(-1) << toplabel << ":";

      // Emit an explicit action here to cover the act of iteration.
      // Equivalently, it can stand for the evaluation of the
      // condition expression.
      o->indent(1);
      record_actions(1, s->tok);

      o->newline() << "if (! (" << iv << ")) goto " << breaklabel << ";";

      // body
      loop_break_labels.push_back (breaklabel);
      loop_continue_labels.push_back (contlabel);
      o->newline() << "{";
      o->indent (1);

      if (s->limit)
      {
      // If we've been through LIMIT loop iterations, quit.
      o->newline() << "if (" << *limitv << "++ >= " << *res_limit
               << ") goto " << breaklabel << ";";

      // We're done with limitv and res_limit.
      delete limitv;
      delete res_limit;
      }

      for (unsigned i = 0; i < s->indexes.size(); ++i)
    {
      // copy the iter values into the specified locals
      var v = getvar (s->indexes[i]->referent);
      c_assign (v, iv.get_key (mv, v.type(), i), s->tok);
    }

      // in the case that the user specified something like
      // foreach ([a,b] in foo[*, 123]), need to check that it iterates over
      // the specified values, ie b is alwasy going to be 123
      if (!s->array_slice.empty())
        {
          //add in the beginning portion of the if statement
          o->newline() << "if (0"; // in case all are wildcards
          for (unsigned i = 0; i < s->array_slice.size(); ++i)

            // only output a comparsion if the expression is not "*".
            if (s->array_slice[i])
            {
              o->line() << " || ";
              if (s->indexes[i]->type == pe_string)
                {
                  if (s->array_slice[i]->type != pe_string)
                    throw SEMANTIC_ERROR (_("expected string types"), s->tok);
                  o->line() << "strncmp(" << getvar (s->indexes[i]->referent)
                            << ", " << *array_slice_vars[i];
                  o->line() << ", MAXSTRINGLEN) !=0";
                }
              else if (s->indexes[i]->type == pe_long)
                {
                  if (s->array_slice[i]->type != pe_long)
                    throw SEMANTIC_ERROR (_("expected numeric types"), s->tok);
                  o->line() << getvar (s->indexes[i]->referent) << " != "
                            << *array_slice_vars[i];
                }
              else
              {
                  throw SEMANTIC_ERROR (_("unexpected type"), s->tok);
              }
            }
          o->line() << ") goto " << contlabel << ";"; // end of the if statment
        }

      if (s->value)
        {
      var v = getvar (s->value->referent);
      c_assign (v, iv.get_value (mv, v.type()), s->tok);
        }

      visit_foreach_loop_value(this, s, iv.get_value(mv, array->type));
      record_actions(0, s->block->tok, true);
      o->newline(-1) << "}";
      loop_break_labels.pop_back ();
      loop_continue_labels.pop_back ();

      // iteration
      o->newline(-1) << contlabel << ":";
      o->newline(1) << iv << " = " << iv.next (mv) << ";";
      o->newline() << "goto " << toplabel << ";";

      // exit
      o->newline(-1) << breaklabel << ":";
      o->newline(1) << "; /* dummy statement */";

      if (mv.is_parallel())
    aggregations_active.erase(mv.value());
    }
  else
    {
      // Iterating over buckets in a histogram.
      assert(s->indexes.size() == 1);
      assert(s->indexes[0]->referent->type == pe_long);
      var bucketvar = getvar (s->indexes[0]->referent);

      aggvar agg = gensym_aggregate ();

      var *v = load_aggregate(hist->stat, agg);
      v->assert_hist_compatible(*hist);

      tmpvar *res_limit = NULL;
      tmpvar *limitv = NULL;
      if (s->limit)
        {
      // Evaluate the limit expression once.
      res_limit = new tmpvar(gensym(pe_long));
      c_assign (res_limit->value(), s->limit, "foreach limit");

      // Create the loop limit variable here and initialize it.
      limitv = new tmpvar(gensym (pe_long));
      o->newline() << *limitv << " = 0LL;";
    }

      record_actions(1, s->tok, true);
      o->newline() << "for (" << bucketvar << " = 0; "
           << bucketvar << " < " << v->buckets() << "; "
           << bucketvar << "++) { ";
      o->newline(1);
      loop_break_labels.push_back (breaklabel);
      loop_continue_labels.push_back (contlabel);

      if (s->limit)
      {
      // If we've been through LIMIT loop iterations, quit.
      o->newline() << "if (" << *limitv << "++ >= " << *res_limit
               << ") break;";

      // We're done with limitv and res_limit.
      delete limitv;
      delete res_limit;
      }

      if (s->value)
        {
          var v = getvar (s->value->referent);
          c_assign (v, agg.get_hist (bucketvar), s->tok);
        }

      visit_foreach_loop_value(this, s, agg.get_hist(bucketvar));
      record_actions(1, s->block->tok, true);

      o->newline(-1) << contlabel << ":";
      o->newline(1) << "continue;";
      o->newline(-1) << breaklabel << ":";
      o->newline(1) << "break;";
      o->newline(-1) << "}";
      loop_break_labels.pop_back ();
      loop_continue_labels.pop_back ();

      delete v;
    }
}


void
c_unparser::visit_return_statement (return_statement* s)
{
  if (current_function == 0)
    throw SEMANTIC_ERROR (_("cannot 'return' from probe"), s->tok);

  if (s->value->type != current_function->type)
    throw SEMANTIC_ERROR (_("return type mismatch"), current_function->tok,
                          s->tok);

  c_assign ("l->__retvalue", s->value, "return value");
  record_actions(1, s->tok, true);
  o->newline() << "goto out;";
}


void
c_unparser::visit_next_statement (next_statement* s)
{
  if (current_probe == 0)
    throw SEMANTIC_ERROR (_("cannot 'next' from function"), s->tok);

  record_actions(1, s->tok, true);
  o->newline() << "goto out;";
}


struct delete_statement_operand_tmp_visitor:
  public traversing_visitor
{
  c_tmpcounter *parent;
  delete_statement_operand_tmp_visitor (c_tmpcounter *p):
    parent (p)
  {}
  //void visit_symbol (symbol* e);
  void visit_arrayindex (arrayindex* e);
};


struct delete_statement_operand_visitor:
  public throwing_visitor
{
  c_unparser *parent;
  delete_statement_operand_visitor (c_unparser *p):
    throwing_visitor (_("invalid operand of delete expression")),
    parent (p)
  {}
  void visit_symbol (symbol* e);
  void visit_arrayindex (arrayindex* e);
};

void
delete_statement_operand_visitor::visit_symbol (symbol* e)
{
  assert (e->referent != 0);
  if (e->referent->arity > 0)
    {
      mapvar mvar = parent->getmap(e->referent, e->tok);
      /* NB: Memory deallocation/allocation operations
       are not generally safe.
      parent->o->newline() << mvar.fini ();
      parent->o->newline() << mvar.init ();
      */
      if (mvar.is_parallel())
    parent->o->newline() << "_stp_pmap_clear (" << mvar.value() << ");";
      else
    parent->o->newline() << "_stp_map_clear (" << mvar.value() << ");";
    }
  else
    {
      var v = parent->getvar(e->referent, e->tok);
      switch (e->type)
    {
    case pe_stats:
      parent->o->newline() << "_stp_stat_clear (" << v.value() << ");";
      break;
    case pe_long:
      parent->o->newline() << v.value() << " = 0;";
      break;
    case pe_string:
      parent->o->newline() << v.value() << "[0] = '\\0';";
      break;
    case pe_unknown:
    default:
      throw SEMANTIC_ERROR(_("Cannot delete unknown expression type"), e->tok);
    }
    }
}

void
delete_statement_operand_tmp_visitor::visit_arrayindex (arrayindex* e)
{
  symbol *array;
  hist_op *hist;
  classify_indexable (e->base, array, hist);

  if (array)
    {
      assert (array->referent != 0);
      vardecl* r = array->referent;
      bool array_slice = false;

      for (unsigned i = 0; i < e->indexes.size(); i ++)
        if (e->indexes[i] == NULL)
          {
            array_slice = true;
            break;
          }

      if (array_slice)
        {
          itervar iv = parent->parent->getiter(array);
          parent->parent->o->newline() << iv.declare();
        }

      // One temporary per index dimension.
      for (unsigned i=0; i<r->index_types.size(); i++)
    {
          if (array->referent->type  == pe_stats || !array_slice || e->indexes[i])
            {
          tmpvar ix = parent->parent->gensym (r->index_types[i]);
          ix.declare (*(parent->parent));
              if (e->indexes[i])
            e->indexes[i]->visit(parent);
            }
    }
    }
  else
    {
      throw SEMANTIC_ERROR(_("cannot delete histogram bucket entries\n"), e->tok);
    }
}

void
delete_statement_operand_visitor::visit_arrayindex (arrayindex* e)
{
  symbol *array;
  hist_op *hist;
  classify_indexable (e->base, array, hist);

  if (array)
    {
      bool array_slice = false;
      for (unsigned i = 0; i < e->indexes.size(); i ++)
        if (e->indexes[i] == NULL)
          {
            array_slice = true;
            break;
          }

      if (!array_slice) // delete a single element
        {
          vector<tmpvar> idx;
          parent->load_map_indices (e, idx);
          mapvar mvar = parent->getmap (array->referent, e->tok);
          parent->o->newline() << mvar.del (idx) << ";";
        }
      else // delete elements if they match the array slice.
        {
          vardecl* r = array->referent;
          mapvar mvar = parent->getmap (r, e->tok);
          itervar iv = parent->getiter(array);

          // create tmpvars for the array indexes, storing NULL where there is
          // no specific value that the index should be
          vector<tmpvar *> array_slice_vars;
          vector<tmpvar> idx; // for the indexes if the variable is a pmap
          for (unsigned i=0; i<e->indexes.size(); i++)
            {
              if (e->indexes[i])
                {
                  tmpvar *asvar = new tmpvar(parent->gensym(e->indexes[i]->type));
                  parent->c_assign (asvar->value(), e->indexes[i], "tmp var");
                  array_slice_vars.push_back(asvar);
                  if (mvar.is_parallel())
                    idx.push_back(*asvar);
                }
              else
                {
                  array_slice_vars.push_back(NULL);
                  if (mvar.is_parallel())
                    {
                      tmpvar *asvar = new tmpvar(parent->gensym(r->index_types[i]));
                      idx.push_back(*asvar);
                    }
                }
            }

          if (mvar.is_parallel())
            {
              parent->o->newline() << "if (unlikely(NULL == "
                                   << mvar.calculate_aggregate() << ")) {";
              parent->o->newline(1) << "c->last_error = ";
              parent->o->line() << STAP_T_05 << mvar << "\";";
              parent->o->newline() << "c->last_stmt = "
                                   << lex_cast_qstring(*e->tok) << ";";
              parent->o->newline() << "goto out;";
              parent->o->newline(-1) << "}";
            }

          // iterate through the map, deleting elements that match the array slice
          string ctr = lex_cast (parent->label_counter++);
          string toplabel = "top_" + ctr;
          string breaklabel = "break_" + ctr;

          parent->o->newline() << iv << " = " << iv.start(mvar) << ";";
          parent->o->newline() << toplabel << ":";

          parent->o->newline(1) << "if (!(" << iv << ")){";
          parent->o->newline(1) << "goto " << breaklabel << ";}";

          // insert the comparison for keys that aren't wildcards
          parent->o->newline(-1) << "if (1"; // in case all are wildcards
          for (unsigned i=0; i<array_slice_vars.size(); i++)
            if (array_slice_vars[i] != NULL)
              {
              if (array_slice_vars[i]->type() == pe_long)
                parent->o->line() << " && " << *array_slice_vars[i] << " == "
                                  << iv.get_key(mvar, array_slice_vars[i]->type(), i);
              else if (array_slice_vars[i]->type() == pe_string)
                parent->o->line() << " && strncmp(" << *array_slice_vars[i] << ", "
                                  << iv.get_key(mvar, array_slice_vars[i]->type(), i)
                                  << ", MAXSTRINGLEN) == 0";
              else
                throw SEMANTIC_ERROR (_("unexpected type"), e->tok);
              }

          parent->o->line() <<  ") {";

          // conditional is true, so delete item and go to the next item
          if (mvar.is_parallel())
            {
              parent->o->indent(1);
              // fills in the wildcards with the current iteration's (map) indexes
              for (unsigned i = 0; i<array_slice_vars.size(); i++)
                if (array_slice_vars[i] == NULL)
                  parent->c_assign (idx[i].value(),
                                    iv.get_key(mvar, r->index_types[i], i),
                                    r->index_types[i], "tmpvar", e->tok);
              parent->o->newline() << iv << " = " << iv.next(mvar) << ";";
              parent->o->newline() << mvar.del(idx) << ";";
            }
          else
            parent->o->newline(1) << iv << " = " << iv.del_next(mvar) << ";";

          parent->o->newline(-1) << "} else";
          parent->o->newline(1) << iv << " = " << iv.next(mvar) << ";";

          parent->o->newline(-1) << "goto " << toplabel << ";";

          parent->o->newline(-1) << breaklabel<< ":";
          parent->o->newline(1) << "; /* dummy statement */";
          parent->o->indent(-1);
        }
    }
  else
    {
      throw SEMANTIC_ERROR(_("cannot delete histogram bucket entries\n"), e->tok);
    }
}


void
c_tmpcounter::visit_delete_statement (delete_statement* s)
{
  delete_statement_operand_tmp_visitor dv (this);
  s->value->visit (&dv);
}


void
c_unparser::visit_delete_statement (delete_statement* s)
{
  delete_statement_operand_visitor dv (this);
  s->value->visit (&dv);
  record_actions(1, s->tok);
}


void
c_unparser::visit_break_statement (break_statement* s)
{
  if (loop_break_labels.empty())
    throw SEMANTIC_ERROR (_("cannot 'break' outside loop"), s->tok);

  record_actions(1, s->tok, true);
  o->newline() << "goto " << loop_break_labels.back() << ";";
}


void
c_unparser::visit_continue_statement (continue_statement* s)
{
  if (loop_continue_labels.empty())
    throw SEMANTIC_ERROR (_("cannot 'continue' outside loop"), s->tok);

  record_actions(1, s->tok, true);
  o->newline() << "goto " << loop_continue_labels.back() << ";";
}



void
c_unparser::visit_literal_string (literal_string* e)
{
  const string& v = e->value;
  o->line() << '"';
  for (unsigned i=0; i<v.size(); i++)
    // NB: The backslash character is specifically passed through as is.
    // This is because our parser treats "\" as an ordinary character, not
    // an escape sequence, leaving it to the C compiler (and this function)
    // to treat it as such.  If we were to escape it, there would be no way
    // of generating C-level escapes from script code.
    // See also print_format::components_to_string and lex_cast_qstring
    if (v[i] == '"') // or other escapeworthy characters?
      o->line() << '\\' << '"';
    else
      o->line() << v[i];
  o->line() << '"';
}


void
c_unparser::visit_literal_number (literal_number* e)
{
  // This looks ugly, but tries to be warning-free on 32- and 64-bit
  // hosts.
  // NB: this needs to be signed!
  if (e->value == -9223372036854775807LL-1) // PR 5023
    o->line() << "((int64_t)" << (unsigned long long) e->value << "ULL)";
  else
    o->line() << "((int64_t)" << e->value << "LL)";
}


void
c_tmpcounter::visit_binary_expression (binary_expression* e)
{
  if (e->op == "/" || e->op == "%")
    {
      tmpvar left = parent->gensym (pe_long);
      tmpvar right = parent->gensym (pe_long);
      if (e->left->tok->type != tok_number)
        left.declare (*parent);
      if (e->right->tok->type != tok_number)
    right.declare (*parent);
    }

  e->left->visit (this);
  e->right->visit (this);
}


void
c_unparser::visit_embedded_expr (embedded_expr* e)
{
  o->line() << "(";

  // Automatically add a call to assert_is_myproc to any code tagged with
  // /* myproc-unprivileged */
  if (e->code.find ("/* myproc-unprivileged */") != string::npos)
    o->line() << "({ assert_is_myproc(); }), ";

  if (e->type == pe_long)
    o->line() << "((int64_t) (" << e->code << "))";
  else if (e->type == pe_string)
    o->line() << "((const char *) (" << e->code << "))";
  else
    throw SEMANTIC_ERROR (_("expected numeric or string type"), e->tok);

  o->line() << ")";
}


void
c_unparser::visit_binary_expression (binary_expression* e)
{
  if (e->type != pe_long ||
      e->left->type != pe_long ||
      e->right->type != pe_long)
    throw SEMANTIC_ERROR (_("expected numeric types"), e->tok);

  if (e->op == "+" ||
      e->op == "-" ||
      e->op == "*" ||
      e->op == "&" ||
      e->op == "|" ||
      e->op == "^")
    {
      o->line() << "((";
      e->left->visit (this);
      o->line() << ") " << e->op << " (";
      e->right->visit (this);
      o->line() << "))";
    }
  else if (e->op == ">>" ||
           e->op == "<<")
    {
      o->line() << "((";
      e->left->visit (this);
      o->line() << ") " << e->op << "max(min(";
      e->right->visit (this);
      o->line() << ", (int64_t)64LL), (int64_t)0LL))"; // between 0 and 64
    }
  else if (e->op == "/" ||
           e->op == "%")
    {
      // % and / need a division-by-zero check; and thus two temporaries
      // for proper evaluation order
      tmpvar left = gensym (pe_long);
      tmpvar right = gensym (pe_long);

      o->line() << "({";
      o->indent(1);

      if (e->left->tok->type == tok_number)
    left.override(c_expression(e->left));
      else
        {
      o->newline() << left << " = ";
      e->left->visit (this);
      o->line() << ";";
    }

      if (e->right->tok->type == tok_number)
    right.override(c_expression(e->right));
      else
        {
      o->newline() << right << " = ";
      e->right->visit (this);
      o->line() << ";";
    }

      o->newline() << "if (unlikely(!" << right << ")) {";
      o->newline(1) << "c->last_error = ";
      o->line() << STAP_T_03;
      o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
      o->newline() << "goto out;";
      o->newline(-1) << "}";
      o->newline() << ((e->op == "/") ? "_stp_div64" : "_stp_mod64")
           << " (NULL, " << left << ", " << right << ");";

      o->newline(-1) << "})";
    }
  else
    throw SEMANTIC_ERROR (_("operator not yet implemented"), e->tok);
}


void
c_unparser::visit_unary_expression (unary_expression* e)
{
  if (e->type != pe_long ||
      e->operand->type != pe_long)
    throw SEMANTIC_ERROR (_("expected numeric types"), e->tok);

  if (e->op == "-")
    {
      // NB: Subtraction is special, since negative literals in the
      // script language show up as unary negations over positive
      // literals here.  This makes it "exciting" for emitting pure
      // C since: - 0x8000_0000_0000_0000 ==> - (- 9223372036854775808)
      // This would constitute a signed overflow, which gcc warns on
      // unless -ftrapv/-J are in CFLAGS - which they're not.

      o->line() << "(int64_t)(0 " << e->op << " (uint64_t)(";
      e->operand->visit (this);
      o->line() << "))";
    }
  else
    {
      o->line() << "(" << e->op << " (";
      e->operand->visit (this);
      o->line() << "))";
    }
}

void
c_unparser::visit_logical_or_expr (logical_or_expr* e)
{
  if (e->type != pe_long ||
      e->left->type != pe_long ||
      e->right->type != pe_long)
    throw SEMANTIC_ERROR (_("expected numeric types"), e->tok);

  o->line() << "((";
  e->left->visit (this);
  o->line() << ") " << e->op << " (";
  e->right->visit (this);
  o->line() << "))";
}


void
c_unparser::visit_logical_and_expr (logical_and_expr* e)
{
  if (e->type != pe_long ||
      e->left->type != pe_long ||
      e->right->type != pe_long)
    throw SEMANTIC_ERROR (_("expected numeric types"), e->tok);

  o->line() << "((";
  e->left->visit (this);
  o->line() << ") " << e->op << " (";
  e->right->visit (this);
  o->line() << "))";
}


void
c_tmpcounter::visit_array_in (array_in* e)
{
  symbol *array;
  hist_op *hist;
  classify_indexable (e->operand->base, array, hist);

  if (array)
    {
      assert (array->referent != 0);
      vardecl* r = array->referent;
      bool array_slice = false;

      // A boolean result.
      tmpvar res = parent->gensym (e->type);
      res.declare (*parent);

      for (unsigned i = 0; i < e->operand->indexes.size(); i ++)
        if (e->operand->indexes[i] == NULL)
          {
            array_slice = true;
            break;
          }

      // One temporary per index dimension.
      for (unsigned i=0; i<r->index_types.size(); i++)
    {
      if (!array_slice || e->operand->indexes[i])
            {
              tmpvar ix = parent->gensym (r->index_types[i]);
              ix.declare (*parent);
              e->operand->indexes[i]->visit(this);
            }
    }

      if (array_slice)
        {
          itervar iv = parent->getiter(array);
          parent->o->newline() << iv.declare();
        }
    }
  else
    {
      // By definition:
      //
      // 'foo in @hist_op(...)'  is true iff
      // '@hist_op(...)[foo]'    is nonzero
      //
      // so we just delegate to the latter call, since int64_t is also
      // our boolean type.
      e->operand->visit(this);
    }
}


void
c_unparser::visit_array_in (array_in* e)
{
  symbol *array;
  hist_op *hist;
  classify_indexable (e->operand->base, array, hist);

  if (array)
    {
      stmt_expr block(*this);

      tmpvar res = gensym (pe_long);
      vector<tmpvar> idx;

      // determine if the array index contains an asterisk
      bool array_slice = false;
      for (unsigned i = 0; i < e->operand->indexes.size(); i ++)
        if (e->operand->indexes[i] == NULL)
          {
            array_slice = true;
            break;
          }

      if (!array_slice) // checking for membership of a specific element
        {
          load_map_indices (e->operand, idx);
          // o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";

          mapvar mvar = getmap (array->referent, e->tok);
          c_assign (res, mvar.exists(idx), e->tok);

          o->newline() << res << ";";
        }
      else
        {
          // create tmpvars for the array indexes, storing NULL where there is
          // no specific value that the index should be
          vector<tmpvar *> array_slice_vars;
          for (unsigned i=0; i<e->operand->indexes.size(); i++)
            {
              if (e->operand->indexes[i])
                {
                  tmpvar *asvar = new tmpvar(gensym(e->operand->indexes[i]->type));
                  c_assign (asvar->value(), e->operand->indexes[i], "tmp var");
                  array_slice_vars.push_back(asvar);
                }
              else
                array_slice_vars.push_back(NULL);
            }

          mapvar mvar = getmap (array->referent, e->operand->tok);
          itervar iv = getiter(array);
          vector<tmpvar> idx;

          // we may not need to aggregate if we're already in a foreach
          bool pre_agg = (aggregations_active.count(mvar.value()) > 0);
          if (mvar.is_parallel() && !pre_agg)
            {
              o->newline() << "if (unlikely(NULL == "
                           << mvar.calculate_aggregate() << ")) {";
              o->newline(1) << "c->last_error = ";
              o->line() << STAP_T_05 << mvar << "\";";
              o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
              o->newline() << "goto out;";
              o->newline(-1) << "}";
            }

          string ctr = lex_cast (label_counter++);
          string toplabel = "top_" + ctr;
          string contlabel = "continue_" + ctr;
          string breaklabel = "break_" + ctr;

          o->newline() << iv << " = " << iv.start(mvar) << ";";
          c_assign (res, "0", e->tok); // set the default to 0

          o->newline() << toplabel << ":";

          o->newline(1) << "if (!(" << iv << "))";
          o->newline(1) << "goto " << breaklabel << ";";

          // generate code for comparing the keys to the index slice
          o->newline(-1) << "if (1"; // in case all are wildcards
          for (unsigned i=0; i<array_slice_vars.size(); i++)
            {
              if (array_slice_vars[i] != NULL)
                {
                if (array_slice_vars[i]->type() == pe_long)
                  o->line() << " && " << *array_slice_vars[i] << " == "
                            << iv.get_key(mvar, array_slice_vars[i]->type(), i);
                else if (array_slice_vars[i]->type() == pe_string)
                  o->line() << " && strncmp(" << *array_slice_vars[i] << ", "
                            << iv.get_key(mvar, array_slice_vars[i]->type(), i)
                            << ", MAXSTRINGLEN) == 0";
                else
                  throw SEMANTIC_ERROR (_("unexpected type"), e->tok);
                }
            }
          o->line() <<  "){";
          o->indent(1);
          // conditional is true, so set res and go to break
          c_assign (res, "1", e->tok);
          o->newline() << "goto " << breaklabel << ";";
          o->newline(-1) << "}";

          // else, keep iterating
          o->newline() << iv << " = " << iv.next(mvar) << ";";
          o->newline() << "goto " << toplabel << ";";

          o->newline(-1) << breaklabel<< ":";
          o->newline(1) << "; /* dummy statement */";
          o->newline(-1) << res << ";";
        }

    }
  else
    {
      // By definition:
      //
      // 'foo in @hist_op(...)'  is true iff
      // '@hist_op(...)[foo]'    is nonzero
      //
      // so we just delegate to the latter call, since int64_t is also
      // our boolean type.
      e->operand->visit(this);
    }
}

void
c_tmpcounter::visit_regex_query (regex_query* e)
{
  e->left->visit(this);
  e->right->visit(this);
}

void
c_unparser::visit_regex_query (regex_query* e)
{
  o->line() << "(";
  o->indent(1);
  o->newline();
  if (e->op == "!~") o->line() << "!";
  stapdfa *dfa = session->dfas[e->right->value];
  dfa->emit_matchop_start (o);
  e->left->visit(this);
  dfa->emit_matchop_end (o);
  o->newline(-1) << ")";
}

void
c_tmpcounter::visit_comparison (comparison* e)
{
  // When computing string operands, their results may be in overlapping
  // __retvalue memory, so we need to save at least one in a tmpvar.
  if (e->left->type == pe_string)
    {
      tmpvar left = parent->gensym (pe_string);
      if (e->left->tok->type != tok_string)
        left.declare (*parent);
    }

  e->left->visit (this);
  e->right->visit (this);
}

void
c_unparser::visit_comparison (comparison* e)
{
  o->line() << "(";

  if (e->left->type == pe_string)
    {
      if (e->right->type != pe_string)
        throw SEMANTIC_ERROR (_("expected string types"), e->tok);

      o->line() << "({";
      o->indent(1);

      tmpvar left = gensym (pe_string);
      if (e->left->tok->type == tok_string)
        left.override(c_expression(e->left));
      else
        c_assign (left.value(), e->left, "assignment");

      o->newline() << "strncmp (" << left << ", ";
      e->right->visit (this);
      o->line() << ", MAXSTRINGLEN) " << e->op << " 0;";
      o->newline(-1) << "})";
    }
  else if (e->left->type == pe_long)
    {
      if (e->right->type != pe_long)
        throw SEMANTIC_ERROR (_("expected numeric types"), e->tok);

      o->line() << "((";
      e->left->visit (this);
      o->line() << ") " << e->op << " (";
      e->right->visit (this);
      o->line() << "))";
    }
  else
    throw SEMANTIC_ERROR (_("unexpected type"), e->left->tok);

  o->line() << ")";
}


void
c_tmpcounter::visit_concatenation (concatenation* e)
{
  tmpvar t = parent->gensym (e->type);
  t.declare (*parent);
  e->left->visit (this);
  e->right->visit (this);
}


void
c_unparser::visit_concatenation (concatenation* e)
{
  if (e->op != ".")
    throw SEMANTIC_ERROR (_("unexpected concatenation operator"), e->tok);

  if (e->type != pe_string ||
      e->left->type != pe_string ||
      e->right->type != pe_string)
    throw SEMANTIC_ERROR (_("expected string types"), e->tok);

  tmpvar t = gensym (e->type);

  o->line() << "({ ";
  o->indent(1);
  // o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
  c_assign (t.value(), e->left, "assignment");
  c_strcat (t.value(), e->right);
  o->newline() << t << ";";
  o->newline(-1) << "})";
}


void
c_unparser::visit_ternary_expression (ternary_expression* e)
{
  if (e->cond->type != pe_long)
    throw SEMANTIC_ERROR (_("expected numeric condition"), e->cond->tok);

  if (e->truevalue->type != e->falsevalue->type ||
      e->type != e->truevalue->type ||
      (e->truevalue->type != pe_long && e->truevalue->type != pe_string))
    throw SEMANTIC_ERROR (_("expected matching types"), e->tok);

  o->line() << "((";
  e->cond->visit (this);
  o->line() << ") ? (";
  e->truevalue->visit (this);
  o->line() << ") : (";
  e->falsevalue->visit (this);
  o->line() << "))";
}


void
c_tmpcounter::visit_assignment (assignment *e)
{
  c_tmpcounter_assignment tav (this, e->op, e->right);
  e->left->visit (& tav);
}


void
c_unparser::visit_assignment (assignment* e)
{
  if (e->op == "<<<")
    {
      if (e->type != pe_long)
    throw SEMANTIC_ERROR (_("non-number <<< expression"), e->tok);

      if (e->left->type != pe_stats)
    throw SEMANTIC_ERROR (_("non-stats left operand to <<< expression"), e->left->tok);

      if (e->right->type != pe_long)
    throw SEMANTIC_ERROR (_("non-number right operand to <<< expression"), e->right->tok);

    }
  else
    {
      if (e->type != e->left->type)
    throw SEMANTIC_ERROR (_("type mismatch"), e->tok, e->left->tok);
      if (e->right->type != e->left->type)
    throw SEMANTIC_ERROR (_("type mismatch"), e->right->tok, e->left->tok);
    }

  c_unparser_assignment tav (this, e->op, e->right);
  e->left->visit (& tav);
}


void
c_tmpcounter::visit_pre_crement (pre_crement* e)
{
  c_tmpcounter_assignment tav (this, e->op, 0);
  e->operand->visit (& tav);
}


void
c_unparser::visit_pre_crement (pre_crement* e)
{
  if (e->type != pe_long ||
      e->type != e->operand->type)
    throw SEMANTIC_ERROR (_("expected numeric type"), e->tok);

  c_unparser_assignment tav (this, e->op, false);
  e->operand->visit (& tav);
}


void
c_tmpcounter::visit_post_crement (post_crement* e)
{
  c_tmpcounter_assignment tav (this, e->op, 0, true);
  e->operand->visit (& tav);
}


void
c_unparser::visit_post_crement (post_crement* e)
{
  if (e->type != pe_long ||
      e->type != e->operand->type)
    throw SEMANTIC_ERROR (_("expected numeric type"), e->tok);

  c_unparser_assignment tav (this, e->op, true);
  e->operand->visit (& tav);
}


void
c_unparser::visit_symbol (symbol* e)
{
  assert (e->referent != 0);
  vardecl* r = e->referent;

  if (r->index_types.size() != 0)
    throw SEMANTIC_ERROR (_("invalid reference to array"), e->tok);

  var v = getvar(r, e->tok);
  o->line() << v;
}


void
c_tmpcounter_assignment::prepare_rvalue (tmpvar & rval)
{
  if (rvalue)
    {
      // literal number and strings don't need any temporaries declared
      if (rvalue->tok->type != tok_number && rvalue->tok->type != tok_string)
    rval.declare (*(parent->parent));

      rvalue->visit (parent);
    }
}

void
c_tmpcounter_assignment::c_assignop(tmpvar & res)
{
  if (res.type() == pe_string)
    {
      // string assignment doesn't need any temporaries declared
    }
  else if (op == "<<<")
    res.declare (*(parent->parent));
  else if (res.type() == pe_long)
    {
      // Only the 'post' operators ('x++') need a temporary declared.
      if (post)
    res.declare (*(parent->parent));
    }
}

// Assignment expansion is tricky.
//
// Because assignments are nestable expressions, we have
// to emit C constructs that are nestable expressions too.
// We have to evaluate the given expressions the proper number of times,
// including array indices.
// We have to lock the lvalue (if global) against concurrent modification,
// especially with modify-assignment operations (+=, ++).
// We have to check the rvalue (for division-by-zero checks).

// In the normal "pre=false" case, for (A op B) emit:
// ({ tmp = B; check(B); lock(A); res = A op tmp; A = res; unlock(A); res; })
// In the "pre=true" case, emit instead:
// ({ tmp = B; check(B); lock(A); res = A; A = res op tmp; unlock(A); res; })
//
// (op is the plain operator portion of a combined calculate/assignment:
// "+" for "+=", and so on.  It is in the "macop" variable below.)
//
// For array assignments, additional temporaries are used for each
// index, which are expanded before the "tmp=B" expression, in order
// to consistently order evaluation of lhs before rhs.
//

void
c_tmpcounter_assignment::visit_symbol (symbol *e)
{
  exp_type ty = rvalue ? rvalue->type : e->type;
  tmpvar rval = parent->parent->gensym (ty);
  tmpvar res = parent->parent->gensym (ty);

  prepare_rvalue(rval);

  c_assignop (res);
}


void
c_unparser_assignment::prepare_rvalue (string const & op,
                       tmpvar & rval,
                       token const * tok)
{
  if (rvalue)
    {
      if (rvalue->tok->type == tok_number || rvalue->tok->type == tok_string)
    // Instead of assigning the numeric or string constant to a
    // temporary, then assigning the temporary to the final, let's
    // just override the temporary with the constant.
    rval.override(parent->c_expression(rvalue));
      else
    parent->c_assign (rval.value(), rvalue, "assignment");
    }
  else
    {
      if (op == "++" || op == "--")
    // Here is part of the conversion proccess of turning "x++" to
    // "x += 1".
        rval.override("1");
      else
        throw SEMANTIC_ERROR (_("need rvalue for assignment"), tok);
    }
}

void
c_unparser_assignment::visit_symbol (symbol *e)
{
  stmt_expr block(*parent);

  assert (e->referent != 0);
  if (e->referent->index_types.size() != 0)
    throw SEMANTIC_ERROR (_("unexpected reference to array"), e->tok);

  // parent->o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
  exp_type ty = rvalue ? rvalue->type : e->type;
  tmpvar rval = parent->gensym (ty);
  tmpvar res = parent->gensym (ty);

  prepare_rvalue (op, rval, e->tok);

  var lvar = parent->getvar (e->referent, e->tok);
  c_assignop (res, lvar, rval, e->tok);

  parent->o->newline() << res << ";";
}


void
c_unparser::visit_target_symbol (target_symbol* e)
{
  throw SEMANTIC_ERROR(_("cannot translate general target-symbol expression"), e->tok);
}


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);
}


void
c_unparser::visit_autocast_op (autocast_op* e)
{
  throw SEMANTIC_ERROR(_("cannot translate general dereference expression"), e->tok);
}


void
c_unparser::visit_defined_op (defined_op* e)
{
  throw SEMANTIC_ERROR(_("cannot translate general @defined expression"), e->tok);
}


void
c_unparser::visit_entry_op (entry_op* e)
{
  throw SEMANTIC_ERROR(_("cannot translate general @entry expression"), e->tok);
}


void
c_unparser::visit_perf_op (perf_op* e)
{
  throw SEMANTIC_ERROR(_("cannot translate general @perf expression"), e->tok);
}


void
c_tmpcounter::load_map_indices(arrayindex *e)
{
  symbol *array;
  hist_op *hist;
  classify_indexable (e->base, array, hist);

  if (array)
    {
      assert (array->referent != 0);
      vardecl* r = array->referent;

      // One temporary per index dimension, except in the case of
      // number or string constants.
      for (unsigned i=0; i<r->index_types.size(); i++)
    {
      tmpvar ix = parent->gensym (r->index_types[i]);
      if (e->indexes[i]->tok->type == tok_number
          || e->indexes[i]->tok->type == tok_string)
        {
          // Do nothing
        }
      else
        ix.declare (*parent);
      e->indexes[i]->visit(this);
    }
    }
}


void
c_unparser::load_map_indices(arrayindex *e,
                 vector<tmpvar> & idx)
{
  symbol *array;
  hist_op *hist;
  classify_indexable (e->base, array, hist);

  if (array)
    {
      idx.clear();

      assert (array->referent != 0);
      vardecl* r = array->referent;

      if (r->index_types.size() == 0 ||
      r->index_types.size() != e->indexes.size())
    throw SEMANTIC_ERROR (_("invalid array reference"), e->tok);

      for (unsigned i=0; i<r->index_types.size(); i++)
    {
      if (r->index_types[i] != e->indexes[i]->type)
        throw SEMANTIC_ERROR (_("array index type mismatch"), e->indexes[i]->tok);

      tmpvar ix = gensym (r->index_types[i]);
      if (e->indexes[i]->tok->type == tok_number
          || e->indexes[i]->tok->type == tok_string)
        // Instead of assigning the numeric or string constant to a
        // temporary, then using the temporary, let's just
        // override the temporary with the constant.
        ix.override(c_expression(e->indexes[i]));
      else
        {
          // o->newline() << "c->last_stmt = "
              // << lex_cast_qstring(*e->indexes[i]->tok) << ";";
          c_assign (ix.value(), e->indexes[i], "array index copy");
        }
      idx.push_back (ix);
    }
    }
  else
    {
      assert (e->indexes.size() == 1);
      assert (e->indexes[0]->type == pe_long);
      tmpvar ix = gensym (pe_long);
      // o->newline() << "c->last_stmt = "
      //       << lex_cast_qstring(*e->indexes[0]->tok) << ";";
      c_assign (ix.value(), e->indexes[0], "array index copy");
      idx.push_back(ix);
    }
}


void
c_tmpcounter::load_aggregate (expression *e)
{
  symbol *sym = get_symbol_within_expression (e);
  string agg_value;
  arrayindex* arr = NULL;
  expression_is_arrayindex (e, arr);

  // If we have a foreach_loop value, we don't need tmps for indexes
  if (sym->referent->arity != 0 &&
      !parent->get_foreach_loop_value(arr, agg_value))
    {
      if (!arr)
    throw SEMANTIC_ERROR(_("expected arrayindex expression"), e->tok);
      load_map_indices (arr);
    }
}


var*
c_unparser::load_aggregate (expression *e, aggvar & agg)
{
  symbol *sym = get_symbol_within_expression (e);

  if (sym->referent->type != pe_stats)
    throw SEMANTIC_ERROR (_("unexpected aggregate of non-statistic"), sym->tok);

  var *v;
  if (sym->referent->arity == 0)
    {
      v = new var(getvar(sym->referent, sym->tok));
      // o->newline() << "c->last_stmt = " << lex_cast_qstring(*sym->tok) << ";";
      o->newline() << agg << " = _stp_stat_get (" << *v << ", 0);";
    }
  else
    {
      mapvar *mv = new mapvar(getmap(sym->referent, sym->tok));
      v = mv;

      arrayindex *arr = NULL;
      if (!expression_is_arrayindex (e, arr))
    throw SEMANTIC_ERROR(_("unexpected aggregate of non-arrayindex"), e->tok);

      // If we have a foreach_loop value, we don't need to index the map
      string agg_value;
      if (get_foreach_loop_value(arr, agg_value))
        o->newline() << agg << " = " << agg_value << ";";
      else
        {
          vector<tmpvar> idx;
          load_map_indices (arr, idx);
          // o->newline() << "c->last_stmt = " << lex_cast_qstring(*sym->tok) << ";";
      bool pre_agg = (aggregations_active.count(mv->value()) > 0);
          o->newline() << agg << " = " << mv->get(idx, pre_agg) << ";";
        }
    }

  return v;
}


string
c_unparser::histogram_index_check(var & base, tmpvar & idx) const
{
  return "((" + idx.value() + " >= 0)"
    + " && (" + idx.value() + " < " + base.buckets() + "))";
}


void
c_tmpcounter::visit_arrayindex (arrayindex *e)
{
  // If we have a foreach_loop value, no other tmps are needed
  string ai_value;
  if (parent->get_foreach_loop_value(e, ai_value))
    return;

  symbol *array;
  hist_op *hist;
  classify_indexable (e->base, array, hist);

  if (array)
    {
      load_map_indices(e);

      // The index-expression result.
      tmpvar res = parent->gensym (e->type);
      res.declare (*parent);
    }
  else
    {

      assert(hist);

      // Note: this is a slightly tricker-than-it-looks allocation of
      // temporaries. The reason is that we're in the branch handling
      // histogram-indexing, and the histogram might be build over an
      // indexable entity itself. For example if we have:
      //
      //  global foo
      //  ...
      //  foo[getpid(), geteuid()] <<< 1
      //  ...
      //  print @log_hist(foo[pid, euid])[bucket]
      //
      // We are looking at the @log_hist(...)[bucket] expression, so
      // allocating one tmpvar for calculating bucket (the "index" of
      // this arrayindex expression), and one tmpvar for storing the
      // result in, just as normal.
      //
      // But we are *also* going to call load_aggregate on foo, which
      // will itself require tmpvars for each of its indices. Since
      // this is not handled by delving into the subexpression (it
      // would be if hist were first-class in the type system, but
      // it's not) we we allocate all the tmpvars used in such a
      // subexpression up here: first our own aggvar, then our index
      // (bucket) tmpvar, then all the index tmpvars of our
      // pe_stat-valued subexpression, then our result.


      // First all the stuff related to indexing into the histogram

      if (e->indexes.size() != 1)
    throw SEMANTIC_ERROR(_("Invalid indexing of histogram"), e->tok);
      tmpvar ix = parent->gensym (pe_long);
      ix.declare (*parent);
      e->indexes[0]->visit(this);
      tmpvar res = parent->gensym (pe_long);
      res.declare (*parent);

      // Then the aggregate, and all the tmpvars needed by our call to
      // load_aggregate().

      aggvar agg = parent->gensym_aggregate ();
      agg.declare(*(this->parent));
      load_aggregate (hist->stat);
    }
}


void
c_unparser::visit_arrayindex (arrayindex* e)
{
  // If we have a foreach_loop value, use it and call it a day!
  string ai_value;
  if (get_foreach_loop_value(e, ai_value))
    {
      o->line() << ai_value;
      return;
    }

  symbol *array;
  hist_op *hist;
  classify_indexable (e->base, array, hist);

  if (array)
    {
      // Visiting an statistic-valued array in a non-lvalue context is prohibited.
      if (array->referent->type == pe_stats)
    throw SEMANTIC_ERROR (_("statistic-valued array in rvalue context"), e->tok);

      stmt_expr block(*this);

      // NB: Do not adjust the order of the next few lines; the tmpvar
      // allocation order must remain the same between
      // c_unparser::visit_arrayindex and c_tmpcounter::visit_arrayindex

      vector<tmpvar> idx;
      load_map_indices (e, idx);
      tmpvar res = gensym (e->type);

      mapvar mvar = getmap (array->referent, e->tok);
      // o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
      c_assign (res, mvar.get(idx), e->tok);

      o->newline() << res << ";";
    }
  else
    {
      // See commentary in c_tmpcounter::visit_arrayindex

      assert(hist);
      stmt_expr block(*this);

      // NB: Do not adjust the order of the next few lines; the tmpvar
      // allocation order must remain the same between
      // c_unparser::visit_arrayindex and c_tmpcounter::visit_arrayindex

      vector<tmpvar> idx;
      load_map_indices (e, idx);
      tmpvar res = gensym (e->type);

      aggvar agg = gensym_aggregate ();

      // These should have faulted during elaboration if not true.
      assert(idx.size() == 1);
      assert(idx[0].type() == pe_long);

      var *v = load_aggregate(hist->stat, agg);
      v->assert_hist_compatible(*hist);

      o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";

      // PR 2142+2610: empty aggregates
      o->newline() << "if (unlikely (" << agg.value() << " == NULL)"
                   << " || " <<  agg.value() << "->count == 0) {";
      o->newline(1) << "c->last_error = ";
      o->line() << STAP_T_06;
      o->newline() << "goto out;";
      o->newline(-1) << "} else {";
      o->newline(1) << "if (" << histogram_index_check(*v, idx[0]) << ")";
      o->newline(1)  << res << " = " << agg << "->histogram[" << idx[0] << "];";
      o->newline(-1) << "else {";
      o->newline(1)  << "c->last_error = ";
      o->line() << STAP_T_07;
      o->newline() << "goto out;";
      o->newline(-1) << "}";

      o->newline(-1) << "}";
      o->newline() << res << ";";

      delete v;
    }
}


void
c_tmpcounter_assignment::visit_arrayindex (arrayindex *e)
{
  symbol *array;
  hist_op *hist;
  classify_indexable (e->base, array, hist);

  if (array)
    {
      parent->load_map_indices(e);

      // The expression rval, lval, and result.
      exp_type ty = rvalue ? rvalue->type : e->type;
      tmpvar rval = parent->parent->gensym (ty);
      tmpvar lval = parent->parent->gensym (ty);
      tmpvar res = parent->parent->gensym (ty);

      prepare_rvalue(rval);
      lval.declare (*(parent->parent));

      if (op == "<<<")
    res.declare (*(parent->parent));
      else
    c_assignop(res);
    }
  else
    {
      throw SEMANTIC_ERROR(_("cannot assign to histogram buckets"), e->tok);
    }
}


void
c_unparser_assignment::visit_arrayindex (arrayindex *e)
{
  symbol *array;
  hist_op *hist;
  classify_indexable (e->base, array, hist);

  if (array)
    {

      stmt_expr block(*parent);

      translator_output *o = parent->o;

      if (array->referent->index_types.size() == 0)
    throw SEMANTIC_ERROR (_("unexpected reference to scalar"), e->tok);

      // nb: Do not adjust the order of the next few lines; the tmpvar
      // allocation order must remain the same between
      // c_unparser_assignment::visit_arrayindex and
      // c_tmpcounter_assignment::visit_arrayindex

      vector<tmpvar> idx;
      parent->load_map_indices (e, idx);
      exp_type ty = rvalue ? rvalue->type : e->type;
      tmpvar rvar = parent->gensym (ty);
      tmpvar lvar = parent->gensym (ty);
      tmpvar res = parent->gensym (ty);

      // NB: because these expressions are nestable, emit this construct
      // thusly:
      // ({ tmp0=(idx0); ... tmpN=(idxN); rvar=(rhs); lvar; res;
      //    lock (array);
      //    lvar = get (array,idx0...N); // if necessary
      //    assignop (res, lvar, rvar);
      //    set (array, idx0...N, lvar);
      //    unlock (array);
      //    res; })
      //
      // we store all indices in temporary variables to avoid nasty
      // reentrancy issues that pop up with nested expressions:
      // e.g. ++a[a[c]=5] could deadlock
      //
      //
      // There is an exception to the above form: if we're doign a <<< assigment to
      // a statistic-valued map, there's a special form we follow:
      //
      // ({ tmp0=(idx0); ... tmpN=(idxN); rvar=(rhs);
      //    *no need to* lock (array);
      //    _stp_map_add_stat (array, idx0...N, rvar);
      //    *no need to* unlock (array);
      //    rvar; })
      //
      // To simplify variable-allocation rules, we assign rvar to lvar and
      // res in this block as well, even though they are technically
      // superfluous.

      prepare_rvalue (op, rvar, e->tok);

      if (op == "<<<")
    {
      assert (e->type == pe_stats);
      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() << mvar.add (idx, rvar) << ";";
          res = rvar;
      // no need for these dummy assignments
      // o->newline() << lvar << " = " << rvar << ";";
      // o->newline() << res << " = " << rvar << ";";
    }
      else
    {
      mapvar mvar = parent->getmap (array->referent, 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);
      o->newline() << mvar.set (idx, lvar) << ";";
    }

      o->newline() << res << ";";
    }
  else
    {
      throw SEMANTIC_ERROR(_("cannot assign to histogram buckets"), e->tok);
    }
}


void
c_tmpcounter::visit_functioncall (functioncall *e)
{
  assert (e->referent != 0);
  functiondecl* r = e->referent;

  // one temporary per argument, unless literal numbers or strings
  for (unsigned i=0; i<r->formal_args.size(); i++)
    {
      tmpvar t = parent->gensym (r->formal_args[i]->type);
      if (e->args[i]->tok->type != tok_number
      && e->args[i]->tok->type != tok_string)
    t.declare (*parent);
      e->args[i]->visit (this);
    }

  tmpvar t = parent->gensym (e->type);
  if (!parent->session->unoptimized && e->type == pe_string && e->var_assigned_to_retval.empty())
    t.declare(*parent);
}


void
c_unparser::visit_functioncall (functioncall* e)
{
  assert (e->referent != 0);
  functiondecl* r = e->referent;

  if (r->formal_args.size() != e->args.size())
    throw SEMANTIC_ERROR (_("invalid length argument list"), e->tok);

  stmt_expr block(*this);

  // NB: we store all actual arguments in temporary variables,
  // to avoid colliding sharing of context variables with
  // nested function calls: f(f(f(1)))

  // compute actual arguments
  vector<tmpvar> tmp;

  for (unsigned i=0; i<e->args.size(); i++)
    {
      tmpvar t = gensym(e->args[i]->type);

      if (r->formal_args[i]->type != e->args[i]->type)
    throw SEMANTIC_ERROR (_("function argument type mismatch"),
                  e->args[i]->tok, r->formal_args[i]->tok);

      symbol *sym_out;
      if (e->args[i]->tok->type == tok_number
      || e->args[i]->tok->type == tok_string)
    t.override(c_expression(e->args[i]));
      else if (r->formal_args[i]->char_ptr_arg && e->args[i]->is_symbol(sym_out)
               && is_local(sym_out->referent, sym_out->tok))
        t.override(getvar(sym_out->referent, e->args[i]->tok).value());
      else
        {
      // o->newline() << "c->last_stmt = "
          // << lex_cast_qstring(*e->args[i]->tok) << ";";
      c_assign (t.value(), e->args[i],
            _("function actual argument evaluation"));
    }
      tmp.push_back(t);
    }

  // copy in actual arguments
  for (unsigned i=0; i<e->args.size(); i++)
    {
      if (r->formal_args[i]->type != e->args[i]->type)
    throw SEMANTIC_ERROR (_("function argument type mismatch"),
                  e->args[i]->tok, r->formal_args[i]->tok);

      if (r->formal_args[i]->char_ptr_arg)
        o->newline() << "c->locals[c->nesting+1]." + c_funcname (r->name) + "."
                        + c_localname (r->formal_args[i]->name) << " = "
                     << tmp[i].value() << ";";
      else
        c_assign ("c->locals[c->nesting+1]." +
                  c_funcname (r->name) + "." +
                  c_localname (r->formal_args[i]->name),
                  tmp[i].value(),
                  e->args[i]->type,
                  "function actual argument copy",
                  e->args[i]->tok);
    }

  tmpvar tmp_ret = gensym (e->type);
  // store the return value after the function arguments have been worked out
  // to avoid problems that may occure with nesting.
  if (!e->var_assigned_to_retval.empty())
    o->newline() << "c->locals[c->nesting+1]." << c_funcname(r->name)
                 << ".__retvalue = &" << e->var_assigned_to_retval << "[0];";
  // store the return value into a tmpvar in case the return is never used/assigned
  else if (!session->unoptimized && e->type == pe_string)
    o->newline() << "c->locals[c->nesting+1]." << c_funcname(r->name)
                 << ".__retvalue = &" << tmp_ret.value() << "[0];";

  // call function
  o->newline() << c_funcname (r->name) << " (c);";
  o->newline() << "if (unlikely(c->last_error)) goto out;";

  if (!already_checked_action_count && !session->suppress_time_limits
      && !session->unoptimized)
    {
      max_action_info mai (*session);
      e->referent->body->visit(&mai);
      // if an unoptimized function/probe called an optimized function, then
      // increase the counter, since the subtraction isn't done within an
      // optimized function
      if(mai.statement_count_finite())
        record_actions (mai.statement_count, e->tok, true);
    }

  // return result from retvalue slot NB: this must be last, for the
  // enclosing statement-expression ({ ... }) to carry this value.
  if (r->type == pe_unknown)
    // If we passed typechecking, then nothing will use this return value
    o->newline() << "(void) 0;";
  else if (session->unoptimized || r->type != pe_string)
    o->newline() << "c->locals[c->nesting+1]"
                 << "." << c_funcname (r->name)
                 << ".__retvalue;";
  else if (e->var_assigned_to_retval.empty())
    o->newline() << tmp_ret.value() << ";";

}


static int
preprocess_print_format(print_format* e, vector<tmpvar>& tmp,
                        vector<print_format::format_component>& components,
                        string& format_string)
{
  int use_print = 0;

  if (e->print_with_format)
    {
      format_string = e->raw_components;
      components = e->components;
    }
  else
    {
      string delim;
      if (e->print_with_delim)
    {
      stringstream escaped_delim;
      const string& dstr = e->delimiter.literal_string;
      for (string::const_iterator i = dstr.begin();
           i != dstr.end(); ++i)
        {
          if (*i == '%')
        escaped_delim << '%';
          escaped_delim << *i;
        }
      delim = escaped_delim.str();
    }

      // Synthesize a print-format string if the user didn't
      // provide one; the synthetic string simply contains one
      // directive for each argument.
      stringstream format;
      for (unsigned i = 0; i < e->args.size(); ++i)
    {
      if (i > 0 && e->print_with_delim)
        format << delim;
      switch (e->args[i]->type)
        {
        default:
        case pe_unknown:
          throw SEMANTIC_ERROR(_("cannot print unknown expression type"), e->args[i]->tok);
        case pe_stats:
          throw SEMANTIC_ERROR(_("cannot print a raw stats object"), e->args[i]->tok);
        case pe_long:
          format << "%d";
          break;
        case pe_string:
          format << "%s";
          break;
        }
    }
      if (e->print_with_newline)
    format << "\\n";

      format_string = format.str();
      components = print_format::string_to_components(format_string);
    }


  if ((tmp.size() == 0 && format_string.find("%%") == string::npos)
      || (tmp.size() == 1 && format_string == "%s"))
    use_print = 1;
  else if (tmp.size() == 1
       && e->args[0]->tok->type == tok_string
       && format_string == "%s\\n")
    {
      use_print = 1;
      tmp[0].override(tmp[0].value() + "\"\\n\"");
    }

  return use_print;
}

void
c_tmpcounter::visit_print_format (print_format* e)
{
  if (e->hist)
    {
      aggvar agg = parent->gensym_aggregate ();
      agg.declare(*(this->parent));
      load_aggregate (e->hist->stat);

      // And the result for sprint[ln](@hist_*)
      if (!e->print_to_stream)
        {
          exp_type ty = pe_string;
          tmpvar res = parent->gensym(ty);
          res.declare(*parent);
        }
    }
  else
    {
      // One temporary per argument
      vector<tmpvar> tmp;
      for (unsigned i=0; i < e->args.size(); i++)
    {
      tmpvar t = parent->gensym (e->args[i]->type);
      tmp.push_back(t);
      if (e->args[i]->type == pe_unknown)
        {
          throw SEMANTIC_ERROR(_("unknown type of arg to print operator"),
                   e->args[i]->tok);
        }

      if (e->args[i]->tok->type != tok_number
          && e->args[i]->tok->type != tok_string)
        t.declare (*parent);
      e->args[i]->visit (this);
    }

      // And the result
      exp_type ty = e->print_to_stream ? pe_long : pe_string;
      tmpvar res = parent->gensym (ty);
      if (ty == pe_string)
    res.declare (*parent);

      // Munge so we can find our compiled printf
      vector<print_format::format_component> components;
      string format_string;
      int use_print = preprocess_print_format(e, tmp, components, format_string);

      // If not in a shortcut case, declare the compiled printf
      if (!(e->print_to_stream && (e->print_char || use_print)))
    parent->declare_compiled_printf(e->print_to_stream, format_string);
    }
}


void
c_unparser::visit_print_format (print_format* e)
{
  // Print formats can contain a general argument list *or* a special
  // type of argument which gets its own processing: a single,
  // non-format-string'ed, histogram-type stat_op expression.

  if (e->hist)
    {
      stmt_expr block(*this);
      aggvar agg = gensym_aggregate ();

      var *v = load_aggregate(e->hist->stat, agg);
      v->assert_hist_compatible(*e->hist);

      {
        // PR 2142+2610: empty aggregates
        o->newline() << "if (unlikely (" << agg.value() << " == NULL)"
                     << " || " <<  agg.value() << "->count == 0) {";
        o->newline(1) << "c->last_error = ";
        o->line() << STAP_T_06;
    o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
    o->newline() << "goto out;";
        o->newline(-1) << "} else";
        if (e->print_to_stream)
          {
            o->newline(1) << "_stp_stat_print_histogram (" << v->hist() << ", " << agg.value() << ");";
            o->indent(-1);
          }
        else
          {
            exp_type ty = pe_string;
            tmpvar res = gensym (ty);
            o->newline(1) << "_stp_stat_print_histogram_buf (" << res.value() << ", MAXSTRINGLEN, " << v->hist() << ", " << agg.value() << ");";
            o->newline(-1) << res.value() << ";";
          }
      }

      delete v;
    }
  else
    {
      stmt_expr block(*this);

      // PR10750: Enforce a reasonable limit on # of varargs
      // 32 varargs leads to max 256 bytes on the stack
      if (e->args.size() > 32)
        throw SEMANTIC_ERROR(_NF("additional argument to print", "too many arguments to print (%zu)",
                                e->args.size(), e->args.size()), e->tok);

      // Compute actual arguments
      vector<tmpvar> tmp;

      for (unsigned i=0; i<e->args.size(); i++)
    {
      tmpvar t = gensym(e->args[i]->type);
      tmp.push_back(t);

      // o->newline() << "c->last_stmt = "
          //           << lex_cast_qstring(*e->args[i]->tok) << ";";

      // If we've got a numeric or string constant, instead of
      // assigning the numeric or string constant to a temporary,
      // then passing the temporary to _stp_printf/_stp_snprintf,
      // let's just override the temporary with the constant.
      if (e->args[i]->tok->type == tok_number
          || e->args[i]->tok->type == tok_string)
        tmp[i].override(c_expression(e->args[i]));
      else
        c_assign (t.value(), e->args[i],
              "print format actual argument evaluation");
    }

      // Allocate the result
      exp_type ty = e->print_to_stream ? pe_long : pe_string;
      tmpvar res = gensym (ty);

      // Munge so we can find our compiled printf
      vector<print_format::format_component> components;
      string format_string, format_string_out;
      int use_print = preprocess_print_format(e, tmp, components, format_string);
      format_string_out = print_format::components_to_string(components);

      // Make the [s]printf call...

      // Generate code to check that any pointer arguments are actually accessible.
      size_t arg_ix = 0;
      for (unsigned i = 0; i < components.size(); ++i) {
    if (components[i].type == print_format::conv_literal)
      continue;

    /* Take note of the width and precision arguments, if any.  */
    int width_ix = -1, prec_ix= -1;
    if (components[i].widthtype == print_format::width_dynamic)
      width_ix = arg_ix++;
    if (components[i].prectype == print_format::prec_dynamic)
      prec_ix = arg_ix++;

        (void) width_ix; /* XXX: notused */

        /* %m and %M need special care for digging into memory. */
    if (components[i].type == print_format::conv_memory
        || components[i].type == print_format::conv_memory_hex)
      {
        string mem_size;
        const token* prec_tok = e->tok;
        if (prec_ix != -1)
          {
        mem_size = tmp[prec_ix].value();
        prec_tok = e->args[prec_ix]->tok;
          }
        else if (components[i].prectype == print_format::prec_static &&
             components[i].precision > 0)
          mem_size = lex_cast(components[i].precision) + "LL";
        else
          mem_size = "1LL";

        /* Limit how much can be printed at a time. (see also PR10490) */
        o->newline() << "c->last_stmt = " << lex_cast_qstring(*prec_tok) << ";";
        o->newline() << "if (" << mem_size << " > 1024) {";
        o->newline(1) << "snprintf(c->error_buffer, sizeof(c->error_buffer), "
              << "\"%lld is too many bytes for a memory dump\", (long long)"
              << mem_size << ");";
        o->newline() << "c->last_error = c->error_buffer;";
        o->newline() << "goto out;";
        o->newline(-1) << "}";
      }

    ++arg_ix;
      }

      // Shortcuts for cases that aren't formatted at all
      if (e->print_to_stream)
        {
      if (e->print_char)
        {
          o->newline() << "_stp_print_char (";
          if (tmp.size())
        o->line() << tmp[0].value() << ");";
          else
        o->line() << '"' << format_string_out << "\");";
          return;
        }
      if (use_print)
        {
          o->newline() << "_stp_print (";
          if (tmp.size())
        o->line() << tmp[0].value() << ");";
          else
        o->line() << '"' << format_string_out << "\");";
          return;
        }
    }

      // The default it to use the new compiled-printf, but one can fall back
      // to the old code with -DSTP_LEGACY_PRINT if desired.
      o->newline() << "#ifndef STP_LEGACY_PRINT";
      o->indent(1);

      // Copy all arguments to the compiled-printf's space, then call it
      const string& compiled_printf =
    get_compiled_printf (e->print_to_stream, format_string);
      for (unsigned i = 0; i < tmp.size(); ++i)
    o->newline() << "c->printf_locals." << compiled_printf
             << ".arg" << i << " = " << tmp[i].value() << ";";
      if (e->print_to_stream)
    // We'll just hardcode the result of 0 instead of using the
    // temporary.
    res.override("((int64_t)0LL)");
      else
    o->newline() << "c->printf_locals." << compiled_printf
             << ".__retvalue = " << res.value() << ";";
      o->newline() << compiled_printf << " (c);";

      o->newline(-1) << "#else // STP_LEGACY_PRINT";
      o->indent(1);

      // Generate the legacy call that goes through _stp_vsnprintf.
      if (e->print_to_stream)
    o->newline() << "_stp_printf (";
      else
    o->newline() << "_stp_snprintf (" << res.value() << ", MAXSTRINGLEN, ";
      o->line() << '"' << format_string_out << '"';

      // Make sure arguments match the expected type of the format specifier.
      arg_ix = 0;
      for (unsigned i = 0; i < components.size(); ++i)
    {
      if (components[i].type == print_format::conv_literal)
        continue;

      /* Cast the width and precision arguments, if any, to 'int'.  */
      if (components[i].widthtype == print_format::width_dynamic)
        o->line() << ", (int)" << tmp[arg_ix++].value();
      if (components[i].prectype == print_format::prec_dynamic)
        o->line() << ", (int)" << tmp[arg_ix++].value();

      /* The type of the %m argument is 'char*'.  */
      if (components[i].type == print_format::conv_memory
          || components[i].type == print_format::conv_memory_hex)
        o->line() << ", (char*)(uintptr_t)" << tmp[arg_ix++].value();
      /* The type of the %c argument is 'int'.  */
      else if (components[i].type == print_format::conv_char)
        o->line() << ", (int)" << tmp[arg_ix++].value();
      else if (arg_ix < tmp.size())
        o->line() << ", " << tmp[arg_ix++].value();
    }
      o->line() << ");";
      o->newline(-1) << "#endif // STP_LEGACY_PRINT";
      o->newline() << "if (unlikely(c->last_error)) goto out;";
      o->newline() << res.value() << ";";
    }
}


void
c_tmpcounter::visit_stat_op (stat_op* e)
{
  aggvar agg = parent->gensym_aggregate ();
  tmpvar res = parent->gensym (pe_long);

  agg.declare(*(this->parent));
  res.declare(*(this->parent));

  load_aggregate (e->stat);
}

void
c_unparser::visit_stat_op (stat_op* e)
{
  // Stat ops can be *applied* to two types of expression:
  //
  //  1. An arrayindex expression on a pe_stats-valued array.
  //
  //  2. A symbol of type pe_stats.

  // FIXME: classify the expression the stat_op is being applied to,
  // call appropriate stp_get_stat() / stp_pmap_get_stat() helper,
  // then reach into resultant struct stat_data.

  // FIXME: also note that summarizing anything is expensive, and we
  // really ought to pass a timeout handler into the summary routine,
  // check its response, possibly exit if it ran out of cycles.

  {
    stmt_expr block(*this);
    aggvar agg = gensym_aggregate ();
    tmpvar res = gensym (pe_long);
    var *v = load_aggregate(e->stat, agg);
    {
      // PR 2142+2610: empty aggregates
      if ((e->ctype == sc_count) ||
          (e->ctype == sc_sum &&
           strverscmp(session->compatible.c_str(), "1.5") >= 0))
        {
          o->newline() << "if (unlikely (" << agg.value() << " == NULL))";
          o->indent(1);
          c_assign(res, "0", e->tok);
          o->indent(-1);
        }
      else
        {
          o->newline() << "if (unlikely (" << agg.value() << " == NULL)"
                       << " || " <<  agg.value() << "->count == 0) {";
          o->newline(1) << "c->last_error = ";
          o->line() << STAP_T_06;
          o->newline() << "c->last_stmt = " << lex_cast_qstring(*e->tok) << ";";
          o->newline() << "goto out;";
          o->newline(-1) << "}";
        }
      o->newline() << "else";
      o->indent(1);
      switch (e->ctype)
        {
        case sc_average:
          c_assign(res, ("_stp_div64(NULL, " + agg.value() + "->sum, "
                         + agg.value() + "->count)"),
                   e->tok);
          break;
        case sc_count:
          c_assign(res, agg.value() + "->count", e->tok);
          break;
        case sc_sum:
          c_assign(res, agg.value() + "->sum", e->tok);
          break;
        case sc_min:
          c_assign(res, agg.value() + "->min", e->tok);
          break;
        case sc_max:
          c_assign(res, agg.value() + "->max", e->tok);
          break;
        case sc_none:
          assert (0); // should not happen, as sc_none is only used in foreach sorts
        }
      o->indent(-1);
    }
    o->newline() << res << ";";
    delete v;
  }
}


void
c_unparser::visit_hist_op (hist_op*)
{
  // Hist ops can only occur in a limited set of circumstances:
  //
  //  1. Inside an arrayindex expression, as the base referent. See
  //     c_unparser::visit_arrayindex for handling of this case.
  //
  //  2. Inside a foreach statement, as the base referent. See
  //     c_unparser::visit_foreach_loop for handling this case.
  //
  //  3. Inside a print_format expression, as the sole argument. See
  //     c_unparser::visit_print_format for handling this case.
  //
  // Note that none of these cases involves the c_unparser ever
  // visiting this node. We should not get here.

  assert(false);
}


typedef map<Dwarf_Addr,const char*> addrmap_t; // NB: plain map, sorted by address

struct unwindsym_dump_context
{
  systemtap_session& session;
  ostream& output;
  unsigned stp_module_index;

  int build_id_len;
  unsigned char *build_id_bits;
  GElf_Addr build_id_vaddr;

  unsigned long stp_kretprobe_trampoline_addr;
  Dwarf_Addr stext_offset;

  vector<pair<string,unsigned> > seclist; // encountered relocation bases
                                          // (section names and sizes)
  map<unsigned, addrmap_t> addrmap; // per-relocation-base sorted addrmap

  void *debug_frame;
  size_t debug_len;
  void *debug_frame_hdr;
  size_t debug_frame_hdr_len;
  Dwarf_Addr debug_frame_off;
  void *eh_frame;
  void *eh_frame_hdr;
  size_t eh_len;
  size_t eh_frame_hdr_len;
  Dwarf_Addr eh_addr;
  Dwarf_Addr eh_frame_hdr_addr;

  set<string> undone_unwindsym_modules;
};

static void create_debug_frame_hdr (const unsigned char e_ident[],
                    Elf_Data *debug_frame,
                    void **debug_frame_hdr,
                    size_t *debug_frame_hdr_len,
                    Dwarf_Addr *debug_frame_off,
                    systemtap_session& session,
                    Dwfl_Module *mod)
{
  *debug_frame_hdr = NULL;
  *debug_frame_hdr_len = 0;

  int cies = 0;
  set< pair<Dwarf_Addr, Dwarf_Off> > fdes;
  set< pair<Dwarf_Addr, Dwarf_Off> >::iterator it;

  // In the .debug_frame the FDE encoding is always DW_EH_PE_absptr.
  // So there is no need to read the CIEs.  And the size is either 4
  // or 8, depending on the elf class from e_ident.
  int size = (e_ident[EI_CLASS] == ELFCLASS32) ? 4 : 8;
  int res = 0;
  Dwarf_Off off = 0;
  Dwarf_CFI_Entry entry;

  while (res != 1)
    {
      Dwarf_Off next_off;
      res = dwarf_next_cfi (e_ident, debug_frame, false, off, &next_off,
                &entry);
      if (res == 0)
    {
      if (entry.CIE_id == DW_CIE_ID_64)
        cies++; // We can just ignore the CIEs.
      else
        {
          Dwarf_Addr addr;
          if (size == 4)
        addr = (*((uint32_t *) entry.fde.start));
          else
        addr = (*((uint64_t *) entry.fde.start));
          fdes.insert(pair<Dwarf_Addr, Dwarf_Off>(addr, off));
        }
    }
      else if (res > 0)
    ; // Great, all done.
      else
    {
      // Warn, but continue, backtracing will be slow...
          if (session.verbose > 2 && ! session.suppress_warnings)
        {
          const char *modname = dwfl_module_info (mod, NULL,
                              NULL, NULL, NULL,
                              NULL, NULL, NULL);
          session.print_warning("Problem creating debug frame hdr for "
                    + lex_cast_qstring(modname)
                    + ", " + dwarf_errmsg (-1));
        }
      return;
    }
      off = next_off;
    }

  if (fdes.size() > 0)
    {
      it = fdes.begin();
      Dwarf_Addr first_addr = (*it).first;
      int res = dwfl_module_relocate_address (mod, &first_addr);
      DWFL_ASSERT ("create_debug_frame_hdr, dwfl_module_relocate_address",
           res >= 0);
      *debug_frame_off = (*it).first - first_addr;
    }

  size_t total_size = 4 + (2 * size) + (2 * size * fdes.size());
  uint8_t *hdr = (uint8_t *) malloc(total_size);
  *debug_frame_hdr = hdr;
  *debug_frame_hdr_len = total_size;

  hdr[0] = 1; // version
  hdr[1] = DW_EH_PE_absptr; // ptr encoding
  hdr[2] = (size == 4) ? DW_EH_PE_udata4 : DW_EH_PE_udata8; // count encoding
  hdr[3] = DW_EH_PE_absptr; // table encoding
  if (size == 4)
    {
      uint32_t *table = (uint32_t *)(hdr + 4);
      *table++ = (uint32_t) 0; // eh_frame_ptr, unused
      *table++ = (uint32_t) fdes.size();
      for (it = fdes.begin(); it != fdes.end(); it++)
    {
      *table++ = (*it).first;
      *table++ = (*it).second;
    }
    }
  else
    {
      uint64_t *table = (uint64_t *)(hdr + 4);
      *table++ = (uint64_t) 0; // eh_frame_ptr, unused
      *table++ = (uint64_t) fdes.size();
      for (it = fdes.begin(); it != fdes.end(); it++)
    {
      *table++ = (*it).first;
      *table++ = (*it).second;
    }
    }
}

static set<string> vdso_paths;

// Get the .debug_frame end .eh_frame sections for the given module.
// Also returns the lenght of both sections when found, plus the section
// address (offset) of the eh_frame data. If a debug_frame is found, a
// synthesized debug_frame_hdr is also returned.
static void get_unwind_data (Dwfl_Module *m,
                 void **debug_frame, void **eh_frame,
                 size_t *debug_len, size_t *eh_len,
                 Dwarf_Addr *eh_addr,
                 void **eh_frame_hdr, size_t *eh_frame_hdr_len,
                 void **debug_frame_hdr,
                 size_t *debug_frame_hdr_len,
                 Dwarf_Addr *debug_frame_off,
                 Dwarf_Addr *eh_frame_hdr_addr,
                 systemtap_session& session)
{
  Dwarf_Addr start, bias = 0;
  GElf_Ehdr *ehdr, ehdr_mem;
  GElf_Shdr *shdr, shdr_mem;
  Elf_Scn *scn;
  Elf_Data *data = NULL;
  Elf *elf;

  // fetch .eh_frame info preferably from main elf file.
  dwfl_module_info (m, NULL, &start, NULL, NULL, NULL, NULL, NULL);
  elf = dwfl_module_getelf(m, &bias);
  ehdr = gelf_getehdr(elf, &ehdr_mem);

  scn = NULL;
  bool eh_frame_seen = false;
  bool eh_frame_hdr_seen = false;
  while ((scn = elf_nextscn(elf, scn)))
    {
      shdr = gelf_getshdr(scn, &shdr_mem);
      const char* scn_name = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name);
      if (!eh_frame_seen
      && strcmp(scn_name, ".eh_frame") == 0
      && shdr->sh_type == SHT_PROGBITS)
    {
      data = elf_rawdata(scn, NULL);
      *eh_frame = data->d_buf;
      *eh_len = data->d_size;
      // For ".dynamic" sections we want the offset, not absolute addr.
      // Note we don't trust dwfl_module_relocations() for ET_EXEC.
      if (ehdr->e_type != ET_EXEC && dwfl_module_relocations (m) > 0)
        *eh_addr = shdr->sh_addr - start + bias;
      else
        *eh_addr = shdr->sh_addr;
      eh_frame_seen = true;
    }
      else if (!eh_frame_hdr_seen
           && strcmp(scn_name, ".eh_frame_hdr") == 0
           && shdr->sh_type == SHT_PROGBITS)
        {
          data = elf_rawdata(scn, NULL);
          *eh_frame_hdr = data->d_buf;
          *eh_frame_hdr_len = data->d_size;
          if (ehdr->e_type != ET_EXEC && dwfl_module_relocations (m) > 0)
        *eh_frame_hdr_addr = shdr->sh_addr - start + bias;
      else
        *eh_frame_hdr_addr = shdr->sh_addr;
          eh_frame_hdr_seen = true;
        }
      if (eh_frame_seen && eh_frame_hdr_seen)
        break;
    }

  // fetch .debug_frame info preferably from dwarf debuginfo file.
  elf = (dwarf_getelf (dwfl_module_getdwarf (m, &bias))
     ?: dwfl_module_getelf (m, &bias));
  ehdr = gelf_getehdr(elf, &ehdr_mem);
  scn = NULL;
  while ((scn = elf_nextscn(elf, scn)))
    {
      shdr = gelf_getshdr(scn, &shdr_mem);
      if (strcmp(elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name),
         ".debug_frame") == 0)
    {
      data = elf_rawdata(scn, NULL);
      *debug_frame = data->d_buf;
      *debug_len = data->d_size;
      break;
    }
    }

  if (*debug_frame != NULL && *debug_len > 0)
    create_debug_frame_hdr (ehdr->e_ident, data,
                debug_frame_hdr, debug_frame_hdr_len,
                debug_frame_off, session, m);
}

static int
dump_build_id (Dwfl_Module *m,
           unwindsym_dump_context *c,
           const char *name, Dwarf_Addr base)
{
  string modname = name;

  //extract build-id from debuginfo file
  int build_id_len = 0;
  unsigned char *build_id_bits;
  GElf_Addr build_id_vaddr;

  if ((build_id_len=dwfl_module_build_id(m,
                                        (const unsigned char **)&build_id_bits,
                                         &build_id_vaddr)) > 0)
  {
     if (modname != "kernel")
      {
        Dwarf_Addr reloc_vaddr = build_id_vaddr;
        const char *secname;
        int i;

        i = dwfl_module_relocate_address (m, &reloc_vaddr);
        DWFL_ASSERT ("dwfl_module_relocate_address reloc_vaddr", i >= 0);

        secname = dwfl_module_relocation_info (m, i, NULL);

        // assert same section name as in runtime/transport/symbols.c
        // NB: this is applicable only to module("...") probes.
        // process("...") ones may have relocation bases like '.dynamic',
        // and so we'll have to store not just a generic offset but
        // the relocation section/symbol name too: just like we do
        // for probe PC addresses themselves.  We want to set build_id_vaddr for
        // user modules even though they will not have a secname.

    if (modname[0] != '/')
      if (!secname || strcmp(secname, ".note.gnu.build-id"))
        throw SEMANTIC_ERROR (_("unexpected build-id reloc section ") +
                  string(secname ?: "null"));

        build_id_vaddr = reloc_vaddr;
      }

    if (c->session.verbose > 1)
      {
        clog << _F("Found build-id in %s, length %d, start at %#" PRIx64,
                   name, build_id_len, build_id_vaddr) << endl;
      }

    c->build_id_len = build_id_len;
    c->build_id_vaddr = build_id_vaddr;
    c->build_id_bits = build_id_bits;
  }

  return DWARF_CB_OK;
}

static int
dump_section_list (Dwfl_Module *m,
                   unwindsym_dump_context *c,
                   const char *name, Dwarf_Addr base)
{
  // Depending on ELF section names normally means you are doing it WRONG.
  // Sadly it seems we do need it for the kernel modules. Which are ET_REL
  // files, which are "dynamically loaded" by the kernel. We keep a section
  // list for them to know which symbol corresponds to which section.
  //
  // Luckily for the kernel, normal executables (ET_EXEC) or shared
  // libraries (ET_DYN) we don't need it. We just have one "section",
  // which we will just give the arbitrary names "_stext", ".absolute"
  // or ".dynamic"

  string modname = name;

  // Use start and end as to calculate size for _stext, .dynamic and
  // .absolute sections.
  Dwarf_Addr start, end;
  dwfl_module_info (m, NULL, &start, &end, NULL, NULL, NULL, NULL);

  // Look up the relocation basis for symbols
  int n = dwfl_module_relocations (m);
  DWFL_ASSERT ("dwfl_module_relocations", n >= 0);

if (n == 0)
    {
      // ET_EXEC, no relocations.
      string secname = ".absolute";
      unsigned size = end - start;
      c->seclist.push_back (make_pair (secname, size));
      return DWARF_CB_OK;
    }
  else if (n == 1)
    {
      // kernel or shared library (ET_DYN).
      string secname;
      secname = (modname == "kernel") ? "_stext" : ".dynamic";
      unsigned size = end - start;
      c->seclist.push_back (make_pair (secname, size));
      return DWARF_CB_OK;
    }
  else if (n > 1)
    {
      // ET_REL, kernel module.
      string secname;
      unsigned size;
      Dwarf_Addr bias;
      GElf_Ehdr *ehdr, ehdr_mem;
      GElf_Shdr *shdr, shdr_mem;
      Elf *elf = dwfl_module_getelf(m, &bias);
      ehdr = gelf_getehdr(elf, &ehdr_mem);
      Elf_Scn *scn = NULL;
      while ((scn = elf_nextscn(elf, scn)))
    {
      // Just the "normal" sections with program bits please.
      shdr = gelf_getshdr(scn, &shdr_mem);
      if ((shdr->sh_type == SHT_PROGBITS || shdr->sh_type == SHT_NOBITS)
          && (shdr->sh_flags & SHF_ALLOC))
        {
          size = shdr->sh_size;
          const char* scn_name = elf_strptr(elf, ehdr->e_shstrndx,
                        shdr->sh_name);
          secname = scn_name;
          c->seclist.push_back (make_pair (secname, size));
        }
    }

      return DWARF_CB_OK;
    }

  // Impossible... dflw_assert above will have triggered.
  return DWARF_CB_ABORT;
}

/* Some architectures create special local symbols that are not
   interesting. */
static int
skippable_arch_symbol (GElf_Half e_machine, const char *name, GElf_Sym *sym)
{
  /* Filter out ARM mapping symbols */
  if (e_machine == EM_ARM
      && GELF_ST_TYPE (sym->st_info) == STT_NOTYPE
      && (! strcmp(name, "$a") || ! strcmp(name, "$t")
      || ! strcmp(name, "$t.x") || ! strcmp(name, "$d")
      || ! strcmp(name, "$v") || ! strcmp(name, "$d.realdata")))
    return 1;

  return 0;
}

static int
dump_symbol_tables (Dwfl_Module *m,
            unwindsym_dump_context *c,
            const char *modname, Dwarf_Addr base)
{
  // Use end as sanity check when resolving symbol addresses.
  Dwarf_Addr end;
  dwfl_module_info (m, NULL, NULL, &end, NULL, NULL, NULL, NULL);

  int syments = dwfl_module_getsymtab(m);
  DWFL_ASSERT (_F("Getting symbol table for %s", modname), syments >= 0);

  // Look up the relocation basis for symbols
  int n = dwfl_module_relocations (m);
  DWFL_ASSERT ("dwfl_module_relocations", n >= 0);

  /* Needed on ppc64, for function descriptors. */
  Dwarf_Addr elf_bias;
  GElf_Ehdr *ehdr, ehdr_mem;
  Elf *elf;
  elf = dwfl_module_getelf(m, &elf_bias);
  ehdr = gelf_getehdr(elf, &ehdr_mem);

  // XXX: unfortunate duplication with tapsets.cxx:emit_address()

  // extra_offset is for the special kernel case.
  Dwarf_Addr extra_offset = 0;
  Dwarf_Addr kretprobe_trampoline_addr = (unsigned long) -1;
  int is_kernel = !strcmp(modname, "kernel");

  /* Set to bail early if we are just examining the kernel
     and don't need anything more. */
  int done = 0;
  for (int i = 0; i < syments && !done; ++i)
    {
      if (pending_interrupts)
        return DWARF_CB_ABORT;

      GElf_Sym sym;
      GElf_Word shndxp;

      const char *name = dwfl_module_getsym(m, i, &sym, &shndxp);
      if (name)
        {
          Dwarf_Addr sym_addr = sym.st_value;

      // We always need two special values from the kernel.
      // _stext for extra_offset and kretprobe_trampoline_holder
      // for the unwinder.
          if (is_kernel)
        {
          // NB: Yey, we found the kernel's _stext value.
          // Sess.sym_stext may be unset (0) at this point, since
          // there may have been no kernel probes set.  We could
          // use tapsets.cxx:lookup_symbol_address(), but then
          // we're already iterating over the same data here...
          if (! strcmp(name, KERNEL_RELOC_SYMBOL))
        {
          int ki;
          extra_offset = sym_addr;
          ki = dwfl_module_relocate_address (m, &extra_offset);
          DWFL_ASSERT ("dwfl_module_relocate_address extra_offset",
                   ki >= 0);

          if (c->session.verbose > 2)
            clog << _F("Found kernel _stext extra offset %#" PRIx64,
                   extra_offset) << endl;

          if (! c->session.need_symbols
              && (kretprobe_trampoline_addr != (unsigned long) -1
              || ! c->session.need_unwind))
            done = 1;
        }
          else if (kretprobe_trampoline_addr == (unsigned long) -1
               && c->session.need_unwind
               && ! strcmp(name, "kretprobe_trampoline_holder"))
        {
          int ki;
                  kretprobe_trampoline_addr = sym_addr;
                  ki = dwfl_module_relocate_address(m,
                            &kretprobe_trampoline_addr);
                  DWFL_ASSERT ("dwfl_module_relocate_address, kretprobe_trampoline_addr", ki >= 0);

          if (! c->session.need_symbols
              && extra_offset != 0)
            done = 1;
        }
            }

      // We are only interested in "real" symbols.
      // We omit symbols that have suspicious addresses
      // (before base, or after end).
          if (!done && c->session.need_symbols
          && ! skippable_arch_symbol(ehdr->e_machine, name, &sym)
          && (GELF_ST_TYPE (sym.st_info) == STT_FUNC
          || (GELF_ST_TYPE (sym.st_info) == STT_NOTYPE
              && (ehdr->e_type == ET_REL // PR10206 ppc fn-desc in .opd
              || is_kernel)) // kernel entry functions are NOTYPE
          || GELF_ST_TYPE (sym.st_info) == STT_OBJECT) // PR10000: .data
               && !(sym.st_shndx == SHN_UNDEF    // Value undefined,
            || shndxp == (GElf_Word) -1    // in a non-allocated section,
            || sym_addr >= end    // beyond current module,
            || sym_addr < base))    // before first section.
            {
              const char *secname = NULL;
              unsigned secidx = 0; /* Most things have just one section. */
          Dwarf_Addr func_desc_addr = 0; /* Function descriptor */

          /* PPC64 uses function descriptors.
         Note: for kernel ET_REL modules we rely on finding the
         .function symbols instead of going through the opd function
         descriptors. */
          if (ehdr->e_machine == EM_PPC64
          && GELF_ST_TYPE (sym.st_info) == STT_FUNC
          && ehdr->e_type != ET_REL)
        {
          Elf64_Addr opd_addr;
          Dwarf_Addr opd_bias;
          Elf_Scn *opd;

          func_desc_addr = sym_addr;

          opd = dwfl_module_address_section (m, &sym_addr, &opd_bias);
          DWFL_ASSERT ("dwfl_module_address_section opd", opd != NULL);

          Elf_Data *opd_data = elf_rawdata (opd, NULL);
          assert(opd_data != NULL);

          Elf_Data opd_in, opd_out;
          opd_out.d_buf = &opd_addr;
          opd_in.d_buf = (char *) opd_data->d_buf + sym_addr;
          opd_out.d_size = opd_in.d_size = sizeof (Elf64_Addr);
          opd_out.d_type = opd_in.d_type = ELF_T_ADDR;
          if (elf64_xlatetom (&opd_out, &opd_in,
                      ehdr->e_ident[EI_DATA]) == NULL)
            throw runtime_error ("elf64_xlatetom failed");

          // So the real address of the function is...
          sym_addr = opd_addr + opd_bias;
        }

              if (n > 0) // only try to relocate if there exist relocation bases
                {
                  int ki = dwfl_module_relocate_address (m, &sym_addr);
                  DWFL_ASSERT ("dwfl_module_relocate_address sym_addr", ki >= 0);
                  secname = dwfl_module_relocation_info (m, ki, NULL);

          if (func_desc_addr != 0)
            dwfl_module_relocate_address (m, &func_desc_addr);
        }

              if (n == 1 && is_kernel)
                {
                  // 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.
                }
              else if (n > 0)
                {
                  assert (secname != NULL);
                  // secname adequately set

                  // NB: it may be an empty string for ET_DYN objects
                  // like shared libraries, as their relocation base
                  // is implicit.
                  if (secname[0] == '\0')
            secname = ".dynamic";
          else
            {
              // Compute our section number
              for (secidx = 0; secidx < c->seclist.size(); secidx++)
            if (c->seclist[secidx].first==secname)
              break;

              if (secidx == c->seclist.size()) // whoa! We messed up...
            {
              string m = _F("%s has unknown section %s for sym %s",
                    modname, secname, name);
              throw runtime_error(m);
            }
            }
                }
              else
                {
                  assert (n == 0);
                  // sym_addr is absolute, as it must be since there are
                  // no relocation bases
                  secname = ".absolute"; // sentinel
                }

              (c->addrmap[secidx])[sym_addr] = name;
          /* If we have a function descriptor, register that address
             under the same name */
          if (func_desc_addr != 0)
        (c->addrmap[secidx])[func_desc_addr] = name;
            }
        }
    }

  if (is_kernel)
    {
      c->stext_offset = extra_offset;
      // Must be relative to actual kernel load address.
      if (kretprobe_trampoline_addr != (unsigned long) -1)
    c->stp_kretprobe_trampoline_addr = (kretprobe_trampoline_addr
                        - extra_offset);
    }

  return DWARF_CB_OK;
}

static int
dump_unwind_tables (Dwfl_Module *m,
            unwindsym_dump_context *c,
            const char *name, Dwarf_Addr base)
{
  // Add unwind data to be included if it exists for this module.
  get_unwind_data (m, &c->debug_frame, &c->eh_frame,
           &c->debug_len, &c->eh_len,
           &c->eh_addr, &c->eh_frame_hdr, &c->eh_frame_hdr_len,
           &c->debug_frame_hdr, &c->debug_frame_hdr_len,
           &c->debug_frame_off, &c->eh_frame_hdr_addr,
                   c->session);
  return DWARF_CB_OK;
}

static void
dump_unwindsym_cxt_table(systemtap_session& session, ostream& output,
             const string& modname, unsigned modindex,
             const string& secname, unsigned secindex,
             const string& table, void*& data, size_t& len)
{
  if (data == NULL || len == 0)
    return;

  if (len > MAX_UNWIND_TABLE_SIZE)
    {
      if (secname.empty())
    session.print_warning (_F("skipping module %s %s table (too big: %zi > %zi)",
                  modname.c_str(), table.c_str(),
                  len, (size_t)MAX_UNWIND_TABLE_SIZE));
      else
    session.print_warning (_F("skipping module %s, section %s %s table (too big: %zi > %zi)",
                  modname.c_str(), secname.c_str(), table.c_str(),
                  len, (size_t)MAX_UNWIND_TABLE_SIZE));
      data = NULL;
      len = 0;
      return;
    }

  output << "#if defined(STP_USE_DWARF_UNWINDER) && defined(STP_NEED_UNWIND_DATA)\n";
  output << "static uint8_t _stp_module_" << modindex << "_" << table;
  if (!secname.empty())
    output << "_" << secindex;
  output << "[] = \n";
  output << "  {";
  for (size_t i = 0; i < len; i++)
    {
      int h = ((uint8_t *)data)[i];
      output << h << ","; // decimal is less wordy than hex
      if ((i + 1) % 16 == 0)
    output << "\n" << "   ";
    }
  output << "};\n";
  output << "#endif /* STP_USE_DWARF_UNWINDER && STP_NEED_UNWIND_DATA */\n";
}

static int
dump_unwindsym_cxt (Dwfl_Module *m,
            unwindsym_dump_context *c,
            const char *name, Dwarf_Addr base)
{
  string modname = name;
  unsigned stpmod_idx = c->stp_module_index;
  void *debug_frame = c->debug_frame;
  size_t debug_len = c->debug_len;
  void *debug_frame_hdr = c->debug_frame_hdr;
  size_t debug_frame_hdr_len = c->debug_frame_hdr_len;
  Dwarf_Addr debug_frame_off = c->debug_frame_off;
  void *eh_frame = c->eh_frame;
  void *eh_frame_hdr = c->eh_frame_hdr;
  size_t eh_len = c->eh_len;
  size_t eh_frame_hdr_len = c->eh_frame_hdr_len;
  Dwarf_Addr eh_addr = c->eh_addr;
  Dwarf_Addr eh_frame_hdr_addr = c->eh_frame_hdr_addr;

  dump_unwindsym_cxt_table(c->session, c->output, modname, stpmod_idx, "", 0,
               "debug_frame", debug_frame, debug_len);

  dump_unwindsym_cxt_table(c->session, c->output, modname, stpmod_idx, "", 0,
               "eh_frame", eh_frame, eh_len);

  dump_unwindsym_cxt_table(c->session, c->output, modname, stpmod_idx, "", 0,
               "eh_frame_hdr", eh_frame_hdr, eh_frame_hdr_len);

  if (c->session.need_unwind && debug_frame == NULL && eh_frame == NULL)
    {
      // 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.
      // 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 < c->seclist.size(); secidx++)
    {
      c->output << "static struct _stp_symbol "
                << "_stp_module_" << stpmod_idx<< "_symbols_" << secidx << "[] = {\n";

      string secname = c->seclist[secidx].first;
      Dwarf_Addr extra_offset;
      extra_offset = (secname == "_stext") ? c->stext_offset : 0;

      // Only include symbols if they will be used
      if (c->session.need_symbols)
    {
      // We write out a *sorted* symbol table, so the runtime doesn't
      // have to sort them later.
      for (addrmap_t::iterator it = c->addrmap[secidx].begin();
           it != c->addrmap[secidx].end(); it++)
        {
          // skip symbols that occur before our chosen base address
          if (it->first < extra_offset)
        continue;

          c->output << "  { 0x" << hex << it->first-extra_offset << dec
            << ", " << lex_cast_qstring (it->second) << " },\n";
              // XXX: these literal strings all suffer ELF relocation bloat too.
              // See if the tapsets.cxx:dwarf_derived_probe_group::emit_module_decls
              // CALCIT hack could work here.
        }
    }

      c->output << "};\n";

      /* For now output debug_frame index only in "magic" sections. */
      if (secname == ".dynamic" || secname == ".absolute"
      || secname == ".text" || secname == "_stext")
    {
      dump_unwindsym_cxt_table(c->session, c->output, modname, stpmod_idx, secname, secidx,
                   "debug_frame_hdr", debug_frame_hdr, debug_frame_hdr_len);
    }
    }

  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 < c->seclist.size(); secidx++)
    {
      c->output << "{\n"
                << ".name = " << lex_cast_qstring(c->seclist[secidx].first) << ",\n"
                << ".size = 0x" << hex << c->seclist[secidx].second << dec << ",\n"
                << ".symbols = _stp_module_" << stpmod_idx << "_symbols_" << secidx << ",\n"
                << ".num_symbols = " << c->addrmap[secidx].size() << ",\n";

      /* For now output debug_frame index only in "magic" sections. */
      string secname = c->seclist[secidx].first;
      if (debug_frame_hdr && (secname == ".dynamic" || secname == ".absolute"
                  || secname == ".text" || secname == "_stext"))
    {
      c->output << "#if defined(STP_USE_DWARF_UNWINDER)"
            << " && defined(STP_NEED_UNWIND_DATA)\n";

          c->output << ".debug_hdr = "
            << "_stp_module_" << stpmod_idx
            << "_debug_frame_hdr_" << secidx << ",\n";
          c->output << ".debug_hdr_len = " << debug_frame_hdr_len << ", \n";

      Dwarf_Addr dwbias = 0;
      dwfl_module_getdwarf (m, &dwbias);
      c->output << ".sec_load_offset = 0x"
            << hex << debug_frame_off - dwbias << dec << "\n";

      c->output << "#else\n";
      c->output << ".debug_hdr = NULL,\n";
      c->output << ".debug_hdr_len = 0,\n";
      c->output << ".sec_load_offset = 0\n";
      c->output << "#endif /* STP_USE_DWARF_UNWINDER"
            << " && STP_NEED_UNWIND_DATA */\n";

    }
      else
    {
      c->output << ".debug_hdr = NULL,\n";
      c->output << ".debug_hdr_len = 0,\n";
      c->output << ".sec_load_offset = 0\n";
    }

    c->output << "},\n";
    }
  c->output << "};\n";

  // 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.
  const char *mainfile;
  dwfl_module_info (m, NULL, NULL, NULL, NULL, NULL, &mainfile, NULL);

  // For user space modules store canonical path.
  // For kernel modules just the name itself.
  string mainpath = resolve_path(mainfile);
  string mainname;
  if (is_user_module(modname)) // userspace
    mainname = lex_cast_qstring (path_remove_sysroot(c->session,mainpath));
  else
    { // kernel module

      // If the module name is the full path to the ko, then we have to retrieve
      // the actual name by which the module will be known inside the kernel.
      // Otherwise, section relocations would be mismatched.
      if (is_fully_resolved(modname, c->session.sysroot, c->session.sysenv))
        mainname = lex_cast_qstring (modname_from_path(modname));
      else
        mainname = lex_cast_qstring (modname);
    }

  c->output << "static struct _stp_module _stp_module_" << stpmod_idx << " = {\n";
  c->output << ".name = " << mainname.c_str() << ",\n";
  c->output << ".path = " << lex_cast_qstring (path_remove_sysroot(c->session,mainpath)) << ",\n";
  c->output << ".eh_frame_addr = 0x" << hex << eh_addr << dec << ", \n";
  c->output << ".unwind_hdr_addr = 0x" << hex << eh_frame_hdr_addr
        << dec << ", \n";

  if (debug_frame != NULL)
    {
      c->output << "#if defined(STP_USE_DWARF_UNWINDER) && defined(STP_NEED_UNWIND_DATA)\n";
      c->output << ".debug_frame = "
        << "_stp_module_" << stpmod_idx << "_debug_frame, \n";
      c->output << ".debug_frame_len = " << debug_len << ", \n";
      c->output << "#else\n";
    }

  c->output << ".debug_frame = NULL,\n";
  c->output << ".debug_frame_len = 0,\n";

  if (debug_frame != NULL)
    c->output << "#endif /* STP_USE_DWARF_UNWINDER && STP_NEED_UNWIND_DATA*/\n";

  if (eh_frame != NULL)
    {
      c->output << "#if defined(STP_USE_DWARF_UNWINDER) && defined(STP_NEED_UNWIND_DATA)\n";
      c->output << ".eh_frame = "
        << "_stp_module_" << stpmod_idx << "_eh_frame, \n";
      c->output << ".eh_frame_len = " << eh_len << ", \n";
      if (eh_frame_hdr)
        {
          c->output << ".unwind_hdr = "
                    << "_stp_module_" << stpmod_idx << "_eh_frame_hdr, \n";
          c->output << ".unwind_hdr_len = " << eh_frame_hdr_len << ", \n";
        }
      else
        {
          c->output << ".unwind_hdr = NULL,\n";
          c->output << ".unwind_hdr_len = 0,\n";
        }
      c->output << "#else\n";
    }

  c->output << ".eh_frame = NULL,\n";
  c->output << ".eh_frame_len = 0,\n";
  c->output << ".unwind_hdr = NULL,\n";
  c->output << ".unwind_hdr_len = 0,\n";
  if (eh_frame != NULL)
    c->output << "#endif /* STP_USE_DWARF_UNWINDER && STP_NEED_UNWIND_DATA*/\n";
  c->output << ".sections = _stp_module_" << stpmod_idx << "_sections" << ",\n";
  c->output << ".num_sections = sizeof(_stp_module_" << stpmod_idx << "_sections)/"
            << "sizeof(struct _stp_section),\n";

  /* Don't save build-id if it is located before _stext.
   * This probably means that build-id will not be loaded at all and
   * happens for example with ARM kernel.  Allow user space modules since the
   * check fails for a shared object.
   *
   * See also:
   *    http://sourceware.org/ml/systemtap/2009-q4/msg00574.html
   */
  if (c->build_id_len > 0
      && (modname != "kernel" || (c->build_id_vaddr > base + c->stext_offset))) {
    c->output << ".build_id_bits = (unsigned char *)\"" ;
    for (int j=0; j<c->build_id_len;j++)
      c->output << "\\x" << hex
                << (unsigned short) *(c->build_id_bits+j) << dec;

    c->output << "\",\n";
    c->output << ".build_id_len = " << c->build_id_len << ",\n";

    /* XXX: kernel data boot-time relocation works differently from text.
       This hack assumes that offset between _stext and build id
       stays constant after relocation, but that's not necessarily
       correct either.  We may instead need a relocation basis different
       from _stext, such as __start_notes.  */
    if (modname == "kernel")
      c->output << ".build_id_offset = 0x" << hex << c->build_id_vaddr - (base + c->stext_offset)
                << dec << ",\n";
    // ET_DYN: task finder gives the load address. ET_EXEC: this is absolute address
    else
      c->output << ".build_id_offset = 0x" << hex
                << c->build_id_vaddr /* - base */
                << dec << ",\n";
  } else
    c->output << ".build_id_len = 0,\n";

  //initialize the note section representing unloaded
  c->output << ".notes_sect = 0,\n";

  c->output << "};\n\n";

  c->undone_unwindsym_modules.erase (modname);

  // release various malloc'd tables
  // if (eh_frame_hdr) free (eh_frame_hdr); -- nope, this one comes from the elf image in memory
  if (debug_frame_hdr) free (debug_frame_hdr);

  return DWARF_CB_OK;
}

static int
dump_unwindsyms (Dwfl_Module *m,
                 void **userdata __attribute__ ((unused)),
                 const char *name,
                 Dwarf_Addr base,
                 void *arg)
{
  if (pending_interrupts)
    return DWARF_CB_ABORT;

  unwindsym_dump_context *c = (unwindsym_dump_context*) arg;
  assert (c);

  // skip modules/files we're not actually interested in
  string modname = name;
  if (c->session.unwindsym_modules.find(modname)
      == c->session.unwindsym_modules.end())
    return DWARF_CB_OK;

  if (c->session.verbose > 1)
    clog << "dump_unwindsyms " << name
         << " index=" << c->stp_module_index
         << " base=0x" << hex << base << dec << endl;

  // We want to extract several bits of information:
  //
  // - parts of the program-header that map the file's physical offsets to the text section
  // - section table: just a list of section (relocation) base addresses
  // - symbol table of the text-like sections, with all addresses relativized to each base
  // - the contents of .debug_frame and/or .eh_frame section, for unwinding purposes

  int res = DWARF_CB_OK;

  c->build_id_len = 0;
  c->build_id_vaddr = 0;
  c->build_id_bits = NULL;
  res = dump_build_id (m, c, name, base);

  c->seclist.clear();
  if (res == DWARF_CB_OK)
    res = dump_section_list(m, c, name, base);

  // We always need to check the symbols of the kernel if we use it,
  // for the extra_offset (also used for build_ids) and possibly
  // stp_kretprobe_trampoline_addr for the dwarf unwinder.
  c->addrmap.clear();
  if (res == DWARF_CB_OK
      && (c->session.need_symbols || ! strcmp(name, "kernel")))
    res = dump_symbol_tables (m, c, name, base);

  c->debug_frame = NULL;
  c->debug_len = 0;
  c->debug_frame_hdr = NULL;
  c->debug_frame_hdr_len = 0;
  c->debug_frame_off = 0;
  c->eh_frame = NULL;
  c->eh_frame_hdr = NULL;
  c->eh_len = 0;
  c->eh_frame_hdr_len = 0;
  c->eh_addr = 0;
  c->eh_frame_hdr_addr = 0;
  if (res == DWARF_CB_OK && c->session.need_unwind)
    res = dump_unwind_tables (m, c, name, base);

  /* And finally dump everything collected in the output. */
  if (res == DWARF_CB_OK)
    res = dump_unwindsym_cxt (m, c, name, base);

  if (res == DWARF_CB_OK)
    c->stp_module_index++;

  return res;
}


// 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
add_unwindsym_iol_callback (set<string> *added, const char *data)
{
  added->insert (string (data));
}


static int
query_module (Dwfl_Module *mod,
              void **,
              const char *,
              Dwarf_Addr,
              struct dwflpp *dwflpp)
{
  dwflpp->focus_on_module(mod, NULL);
  return DWARF_CB_OK;
}


void
add_unwindsym_ldd (systemtap_session &s)
{
  std::set<std::string> added;

  for (std::set<std::string>::iterator it = s.unwindsym_modules.begin();
       it != s.unwindsym_modules.end();
       it++)
    {
      string modname = *it;
      assert (modname.length() != 0);
      if (! is_user_module (modname)) continue;

      dwflpp mod_dwflpp (s, modname, false);
      mod_dwflpp.iterate_over_modules(&query_module, &mod_dwflpp);
      if (mod_dwflpp.module) // existing binary
        {
          assert (mod_dwflpp.module_name != "");
          mod_dwflpp.iterate_over_libraries (&add_unwindsym_iol_callback, &added);
        }
    }

  s.unwindsym_modules.insert (added.begin(), added.end());
}

static int find_vdso(const char *path, const struct stat *, int type)
{
  if (type == FTW_F)
    {
      /* Assume that if the path's basename starts with 'vdso' and
       * ends with '.so', it is the vdso.
       *
       * Note that this logic should match up with the logic in the
       * _stp_vma_match_vdso() function in runtime/vma.c. */
      const char *name = strrchr(path, '/');
      if (name)
    {
      const char *ext;

      name++;
      ext = strrchr(name, '.');
      if (ext
          && strncmp("vdso", name, 4) == 0
          && strcmp(".so", ext) == 0)
        vdso_paths.insert(path);
    }
    }
  return 0;
}

void
add_unwindsym_vdso (systemtap_session &s)
{
  // This is to disambiguate between -r REVISION vs -r BUILDDIR.
  // See also dwflsetup.c (setup_dwfl_kernel). In case of only
  // having the BUILDDIR we need to do a deep search (the specific
  // arch name dir in the kernel build tree is unknown).
  string vdso_dir;
  if (s.kernel_build_tree == string(s.sysroot + "/lib/modules/"
                    + s.kernel_release
                    + "/build"))
    vdso_dir = s.sysroot + "/lib/modules/" + s.kernel_release + "/vdso";
  else
    vdso_dir = s.kernel_build_tree + "/arch/";

  if (s.verbose > 1)
    clog << _("Searching for vdso candidates: ") << vdso_dir << endl;

  ftw(vdso_dir.c_str(), find_vdso, 1);

  for (set<string>::iterator it = vdso_paths.begin();
       it != vdso_paths.end();
       it++)
    {
      s.unwindsym_modules.insert(*it);
      if (s.verbose > 1)
    clog << _("vdso candidate: ") << *it << endl;
    }
}

static void
prepare_symbol_data (systemtap_session& s)
{
  // step 0: run ldd on any user modules if requested
  if (s.unwindsym_ldd)
    add_unwindsym_ldd (s);
  // step 0.5: add vdso(s) when vma tracker was requested
  if (vma_tracker_enabled (s))
    add_unwindsym_vdso (s);
  // NB: do this before the ctx.unwindsym_modules copy is taken
}

void
emit_symbol_data (systemtap_session& s)
{
  string symfile = "stap-symbols.h";

  s.op->newline() << "#include " << lex_cast_qstring (symfile);

  ofstream kallsyms_out ((s.tmpdir + "/" + symfile).c_str());

  vector<pair<string,unsigned> > seclist;
  map<unsigned, addrmap_t> addrmap;
  unwindsym_dump_context ctx = { s, kallsyms_out,
                 0, /* module index */
                 0, NULL, 0, /* build_id len, bits, vaddr */
                 ~0UL, /* stp_kretprobe_trampoline_addr */
                 0, /* stext_offset */
                 seclist, addrmap,
                 NULL, /* debug_frame */
                 0, /* debug_len */
                 NULL, /* debug_frame_hdr */
                 0, /* debug_frame_hdr_len */
                 0, /* debug_frame_off */
                 NULL, /* eh_frame */
                 NULL, /* eh_frame_hdr */
                 0, /* eh_len */
                 0, /* eh_frame_hdr_len */
                 0, /* eh_addr */
                 0, /* eh_frame_hdr_addr */
                 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;
    }

  // ---- step 1: process any kernel modules listed
  set<string> offline_search_modules;
  unsigned count;
  for (set<string>::iterator it = s.unwindsym_modules.begin();
       it != s.unwindsym_modules.end();
       it++)
    {
      string foo = *it;
      if (! is_user_module (foo)) /* Omit user-space, since we're only
                     using this for kernel space
                     offline searches. */
        offline_search_modules.insert (foo);
    }
  Dwfl *dwfl = setup_dwfl_kernel (offline_search_modules, &count, s);
  /* NB: It's not an error to find a few fewer modules than requested.
     There might be third-party modules loaded (e.g. uprobes). */
  /* DWFL_ASSERT("all kernel modules found",
     count >= offline_search_modules.size()); */

  ptrdiff_t off = 0;
  do
    {
      assert_no_interrupts();
      if (ctx.undone_unwindsym_modules.empty()) break;
      off = dwfl_getmodules (dwfl, &dump_unwindsyms, (void *) &ctx, off);
    }
  while (off > 0);
  DWFL_ASSERT("dwfl_getmodules", off == 0);
  dwfl_end(dwfl);

  // ---- step 2: process any user modules (files) listed
  for (std::set<std::string>::iterator it = s.unwindsym_modules.begin();
       it != s.unwindsym_modules.end();
       it++)
    {
      string modname = *it;
      assert (modname.length() != 0);
      if (! is_user_module (modname)) continue;
      Dwfl *dwfl = setup_dwfl_user (modname);
      if (dwfl != NULL) // tolerate missing data; will warn below
        {
          ptrdiff_t off = 0;
          do
            {
              assert_no_interrupts();
              if (ctx.undone_unwindsym_modules.empty()) break;
              off = dwfl_getmodules (dwfl, &dump_unwindsyms, (void *) &ctx, off);
            }
          while (off > 0);
          DWFL_ASSERT("dwfl_getmodules", off == 0);
        }
      dwfl_end(dwfl);
    }

  emit_symbol_data_done (&ctx, s);
}

void
self_unwind_declarations(unwindsym_dump_context *ctx)
{
  ctx->output << "static uint8_t _stp_module_self_eh_frame [] = {0,};\n";
  ctx->output << "static struct _stp_symbol _stp_module_self_symbols_0[] = {{0},};\n";
  ctx->output << "static struct _stp_symbol _stp_module_self_symbols_1[] = {{0},};\n";
  ctx->output << "static struct _stp_section _stp_module_self_sections[] = {\n";
  ctx->output << "{.name = \".symtab\", .symbols = _stp_module_self_symbols_0, .num_symbols = 0},\n";
  ctx->output << "{.name = \".text\", .symbols = _stp_module_self_symbols_1, .num_symbols = 0},\n";
  ctx->output << "};\n";
  ctx->output << "static struct _stp_module _stp_module_self = {\n";
  ctx->output << ".name = \"stap_self_tmp_value\",\n";
  ctx->output << ".path = \"stap_self_tmp_value\",\n";
  ctx->output << ".num_sections = 2,\n";
  ctx->output << ".sections = _stp_module_self_sections,\n";
  ctx->output << ".eh_frame = _stp_module_self_eh_frame,\n";
  ctx->output << ".eh_frame_len = 0,\n";
  ctx->output << ".unwind_hdr_addr = 0x0,\n";
  ctx->output << ".unwind_hdr = NULL,\n";
  ctx->output << ".unwind_hdr_len = 0,\n";
  ctx->output << ".debug_frame = NULL,\n";
  ctx->output << ".debug_frame_len = 0,\n";
  ctx->output << "};\n";
}

void
emit_symbol_data_done (unwindsym_dump_context *ctx, systemtap_session& s)
{
  // Add a .eh_frame terminator dummy object file, much like
  // libgcc/crtstuff.c's EH_FRAME_SECTION_NAME closer.  We need this in
  // order for runtime/sym.c
  translator_output *T_800 = s.op_create_auxiliary(true);
  T_800->newline() << "__extension__ unsigned int T_800 []"; // assumed 32-bits wide
  T_800->newline(1) << "__attribute__((used, section(\".eh_frame\"), aligned(4)))";
  T_800->newline() << "= { 0 };";
  T_800->newline(-1);
  T_800->assert_0_indent (); // flush to disk

  // Print out a definition of the runtime's _stp_modules[] globals.
  ctx->output << "\n";
  self_unwind_declarations(ctx);
   ctx->output << "static struct _stp_module *_stp_modules [] = {\n";
  for (unsigned i=0; i<ctx->stp_module_index; i++)
    {
      ctx->output << "& _stp_module_" << i << ",\n";
    }
  ctx->output << "& _stp_module_self,\n";
  ctx->output << "};\n";
  ctx->output << "static const unsigned _stp_num_modules = ARRAY_SIZE(_stp_modules);\n";

  ctx->output << "static unsigned long _stp_kretprobe_trampoline = ";
  // Special case for -1, which is invalid in hex if host width > target width.
  if (ctx->stp_kretprobe_trampoline_addr == (unsigned long) -1)
    ctx->output << "-1;\n";
  else
    ctx->output << "0x" << hex << ctx->stp_kretprobe_trampoline_addr << dec
        << ";\n";

  // Some nonexistent modules may have been identified with "-d".  Note them.
  if (! s.suppress_warnings)
    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) + "'");
}

struct recursion_info: public traversing_visitor
{
  recursion_info (systemtap_session& s): sess(s), nesting_max(0), recursive(false) {}
  systemtap_session& sess;
  unsigned nesting_max;
  bool recursive;
  std::vector <functiondecl *> current_nesting;

  void visit_functioncall (functioncall* e) {
    traversing_visitor::visit_functioncall (e); // for arguments

    // check for nesting level
    unsigned nesting_depth = current_nesting.size() + 1;
    if (nesting_max < nesting_depth)
      {
        if (sess.verbose > 3)
          clog << _F("identified max-nested function: %s (%d)",
                     e->referent->name.c_str(), nesting_depth) << endl;
        nesting_max = nesting_depth;
      }

    // check for (direct or mutual) recursion
    for (unsigned j=0; j<current_nesting.size(); j++)
      if (current_nesting[j] == e->referent)
        {
          recursive = true;
          if (sess.verbose > 3)
            clog << _F("identified recursive function: %s", e->referent->name.c_str()) << endl;
          return;
        }

    // non-recursive traversal
    current_nesting.push_back (e->referent);
    e->referent->body->visit (this);
    current_nesting.pop_back ();
  }
};


void translate_runtime(systemtap_session& s)
{
  s.op->newline() << "#define STAP_MSG_RUNTIME_H_01 "
                  << lex_cast_qstring(_("myproc-unprivileged tapset function called "
                                        "without is_myproc checking for pid %d (euid %d)"));

  s.op->newline() << "#define STAP_MSG_LOC2C_01 "
                  << lex_cast_qstring(_("read fault [man error::fault] at 0x%p (%s)"));
  s.op->newline() << "#define STAP_MSG_LOC2C_02 "
                  << lex_cast_qstring(_("write fault [man error::fault] at 0x%p (%s)"));
  s.op->newline() << "#define STAP_MSG_LOC2C_03 "
                  << lex_cast_qstring(_("divide by zero in DWARF operand (%s)"));
}


int
prepare_translate_pass (systemtap_session& s)
{
  int rc = 0;
  try
    {
      prepare_symbol_data (s);
    }
  catch (const semantic_error& e)
    {
      s.print_error (e);
      rc = 1;
    }

  return rc;
}


int
translate_pass (systemtap_session& s)
{
  int rc = 0;

  s.op = new translator_output (s.translated_source);
  // additional outputs might be found in s.auxiliary_outputs
  c_unparser cup (& s);
  s.up = & cup;
  translate_runtime(s);

  try
    {
      int64_t major=0, minor=0;
      try
    {
      vector<string> versions;
      tokenize (s.compatible, versions, ".");
      if (versions.size() >= 1)
        major = lex_cast<int64_t> (versions[0]);
      if (versions.size() >= 2)
        minor = lex_cast<int64_t> (versions[1]);
      if (versions.size() >= 3 && s.verbose > 1)
        clog << _F("ignoring extra parts of compat version: %s", s.compatible.c_str()) << endl;
    }
      catch (const runtime_error)
    {
      throw SEMANTIC_ERROR(_F("parse error in compatibility version: %s", s.compatible.c_str()));
    }
      if (major < 0 || major > 255 || minor < 0 || minor > 255)
    throw SEMANTIC_ERROR(_F("compatibility version out of range: %s", s.compatible.c_str()));
      s.op->newline() << "#define STAP_VERSION(a, b) ( ((a) << 8) + (b) )";
      s.op->newline() << "#ifndef STAP_COMPAT_VERSION";
      s.op->newline() << "#define STAP_COMPAT_VERSION STAP_VERSION("
              << major << ", " << minor << ")";
      s.op->newline() << "#endif";

      recursion_info ri (s);

      // NB: we start our traversal from the s.functions[] rather than the probes.
      // We assume that each function is called at least once, or else it would have
      // been elided already.
      for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
    {
          functiondecl *fd = it->second;
          fd->body->visit (& ri);
    }

      if (s.verbose > 1)
        clog << _F("function recursion-analysis: max-nesting %d %s", ri.nesting_max,
                  (ri.recursive ? _(" recursive") : _(" non-recursive"))) << endl;
      unsigned nesting = ri.nesting_max + 1; /* to account for initial probe->function call */
      if (ri.recursive) nesting += 10;

      // This is at the very top of the file.
      // All "static" defines (not dependend on session state).
      s.op->newline() << "#include \"runtime_defines.h\"";
      if (s.perf_derived_probes)
    s.op->newline() << "#define _HAVE_PERF_ 1";
      s.op->newline() << "#include \"linux/perf_read.h\"";

      // Generated macros describing the privilege level required to load/run this module.
      s.op->newline() << "#define STP_PR_STAPUSR 0x" << hex << pr_stapusr << dec;
      s.op->newline() << "#define STP_PR_STAPSYS 0x" << hex << pr_stapsys << dec;
      s.op->newline() << "#define STP_PR_STAPDEV 0x" << hex << pr_stapdev << dec;
      s.op->newline() << "#define STP_PRIVILEGE 0x" << hex << s.privilege << dec;

      // Generate a section containing a mask of the privilege levels required to load/run this
      // module.
      s.op->newline() << "int stp_required_privilege "
              << "__attribute__ ((section (\"" << STAP_PRIVILEGE_SECTION <<"\")))"
              << " = STP_PRIVILEGE;";

      s.op->newline() << "#ifndef MAXNESTING";
      s.op->newline() << "#define MAXNESTING " << nesting;
      s.op->newline() << "#endif";

      // Generated macros specifying how much storage is required for
      // regexp subexpressions:
      s.op->newline() << "#define STAPREGEX_MAX_STATE" << s.dfa_maxstate;
      s.op->newline() << "#define STAPREGEX_MAX_TAG" << s.dfa_maxtag;

      s.op->newline() << "#define STP_SKIP_BADVARS " << (s.skip_badvars ? 1 : 0);

      if (s.bulk_mode)
      s.op->newline() << "#define STP_BULKMODE";

      if (s.timing)
    s.op->newline() << "#define STP_TIMING";

      if (s.need_unwind)
    s.op->newline() << "#define STP_NEED_UNWIND_DATA 1";

      // Emit the total number of probes (not regarding merged probe handlers)
      s.op->newline() << "#define STP_PROBE_COUNT " << s.probes.size();

      // Emit systemtap_module_refresh() prototype so we can reference it
      s.op->newline() << "static void systemtap_module_refresh (const char* modname);";

      if (!s.runtime_usermode_p())
        {
          // When on-the-fly [dis]arming is used, module_refresh can be called from
          // both the module notifier, as well as when probes need to be
          // armed/disarmed. We need to protect it to ensure it's only run one at a
          // time.
          s.op->newline() << "#include <linux/mutex.h>";
          s.op->newline() << "static DEFINE_MUTEX(module_refresh_mutex);";

          // For some probes, on-the-fly support is provided through a
          // background timer (module_refresh_timer). We need to disable that
          // part if hrtimers are not supported.
          s.op->newline() << "#include <linux/version.h>";
          s.op->newline() << "#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)";
          s.op->newline() << "#define STP_ON_THE_FLY_TIMER_ENABLE";
          s.op->newline() << "#endif";
        }

      s.op->newline() << "#include \"runtime.h\"";

      // Emit embeds ahead of time, in case they affect context layout
      for (unsigned i=0; i<s.embeds.size(); i++)
        {
          s.op->newline() << s.embeds[i]->code << "\n";
        }

      s.up->emit_common_header (); // context etc.

      if (s.need_unwind)
    s.op->newline() << "#include \"stack.c\"";

      if (s.globals.size()>0)
    {
      s.op->newline() << "struct stp_globals {";
      s.op->indent(1);
      for (unsigned i=0; i<s.globals.size(); i++)
        {
          s.up->emit_global (s.globals[i]);
        }
      s.op->newline(-1) << "};";

      // We only need to statically initialize globals in kernel modules,
      // where module parameters may want to override the script's value.  In
      // stapdyn, the globals are actually part of the dynamic shared memory,
      // and the static structure is merely used as a source of default values.
      s.op->newline();
      if (!s.runtime_usermode_p ())
        s.op->newline() << "static struct stp_globals stp_global = {";
      else
       {
         s.op->newline() << "static struct {";
         s.op->indent(1);
         for (unsigned i=0; i<s.globals.size(); i++)
           {
         assert_no_interrupts();
                 s.up->emit_global_init_type (s.globals[i]);
           }
         s.op->newline(-1) << "} stp_global_init = {";
       }
      s.op->newline(1);
      for (unsigned i=0; i<s.globals.size(); i++)
        {
          assert_no_interrupts();
              s.up->emit_global_init (s.globals[i]);
        }
      s.op->newline(-1) << "};";

      s.op->assert_0_indent();
    }
      else
        // stp_runtime_session wants to incorporate globals, but it
        // can be empty
    s.op->newline() << "struct stp_globals {};";

      // Common (static atomic) state of the stap session.
      s.op->newline();
      s.op->newline() << "#include \"common_session_state.h\"";

      s.op->newline() << "#include \"probe_lock.h\" ";

      s.op->newline() << "#ifdef STAP_NEED_GETTIMEOFDAY";
      s.op->newline() << "#include \"time.c\""// Don't we all need more?
      s.op->newline() << "#endif";

      for (map<string,stapdfa*>::iterator it = s.dfas.begin(); it != s.dfas.end(); it++)
        {
          assert_no_interrupts();
          s.op->newline();
          try
            {
              it->second->emit_declaration (s.op);
            }
          catch (const semantic_error &e)
            {
              s.print_error(e);
            }
        }
      s.op->assert_0_indent();

      for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
    {
          assert_no_interrupts();
      s.op->newline();
      s.up->emit_functionsig (it->second);
    }
      s.op->assert_0_indent();

      for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
    {
          assert_no_interrupts();
      s.op->newline();
      s.up->emit_function (it->second);
    }
      s.op->assert_0_indent();

      // Let's find some stats for the embedded pp strings.  Maybe they
      // are small and uniform enough to justify putting char[MAX]'s into
      // the array instead of relocated char*'s.
      size_t pp_max = 0, pn_max = 0, location_max = 0, derivation_max = 0;
      size_t pp_tot = 0, pn_tot = 0, location_tot = 0, derivation_tot = 0;
      for (unsigned i=0; i<s.probes.size(); i++)
        {
          derived_probe* p = s.probes[i];
#define DOIT(var,expr) do {                             \
        size_t var##_size = (expr) + 1;                 \
        var##_max = max (var##_max, var##_size);        \
        var##_tot += var##_size; } while (0)
          DOIT(pp, lex_cast_qstring(*p->sole_location()).size());
          DOIT(pn, lex_cast_qstring(*p->script_location()).size());
          DOIT(location, lex_cast_qstring(p->tok->location).size());
          DOIT(derivation, lex_cast_qstring(p->derived_locations()).size());
#undef DOIT
        }

      // Decide whether it's worthwhile to use char[] or char* by comparing
      // the amount of average waste (max - avg) to the relocation data size
      // (3 native long words).
#define CALCIT(var)                                                             \
      if (s.verbose > 2)                                                        \
        clog << "adapt " << #var << ":" << var##_max << "max - " << var##_tot << "/" << s.probes.size() << "tot =>"; \
      if ((var##_max-(var##_tot/s.probes.size())) < (3 * sizeof(void*)))        \
        {                                                                       \
          s.op->newline() << "const char " << #var << "[" << var##_max << "];"; \
          if (s.verbose > 2)                                                    \
            clog << "[]" << endl;                                               \
        }                                                                       \
      else                                                                      \
        {                                                                       \
          s.op->newline() << "const char * const " << #var << ";";              \
          if (s.verbose > 2)                                                    \
            clog << "*" << endl;                                                \
        }

      s.op->newline();
      s.op->newline() << "struct stap_probe {";
      s.op->newline(1) << "const size_t index;";
      s.op->newline() << "void (* const ph) (struct context*);";
      s.op->newline() << "unsigned cond_enabled:1;"; // just one bit required
      s.op->newline() << "#if defined(STP_TIMING) || defined(STP_ALIBI)";
      CALCIT(location);
      CALCIT(derivation);
      s.op->newline() << "#define STAP_PROBE_INIT_TIMING(L, D) "
                      << ".location=(L), .derivation=(D),";
      s.op->newline() << "#else";
      s.op->newline() << "#define STAP_PROBE_INIT_TIMING(L, D)";
      s.op->newline() << "#endif";
      CALCIT(pp);
      s.op->newline() << "#ifdef STP_NEED_PROBE_NAME";
      CALCIT(pn);
      s.op->newline() << "#define STAP_PROBE_INIT_NAME(PN) .pn=(PN),";
      s.op->newline() << "#else";
      s.op->newline() << "#define STAP_PROBE_INIT_NAME(PN)";
      s.op->newline() << "#endif";
      s.op->newline() << "#define STAP_PROBE_INIT(I, PH, PP, PN, L, D) "
                      << "{ .index=(I), .ph=(PH), .cond_enabled=1, .pp=(PP), "
                      << "STAP_PROBE_INIT_NAME(PN) "
                      << "STAP_PROBE_INIT_TIMING(L, D) "
                      << "}";
      s.op->newline(-1) << "} static stap_probes[];";
      s.op->assert_0_indent();
#undef CALCIT

      // Run a varuse_collecting_visitor over probes that need global
      // variable locks.  We'll use this information later in
      // emit_locks()/emit_unlocks().
      for (unsigned i=0; i<s.probes.size(); i++)
    {
        assert_no_interrupts();
        s.probes[i]->session_index = i;
        if (s.probes[i]->needs_global_locks())
        s.probes[i]->body->visit (&cup.vcv_needs_global_locks);
    }
      s.op->assert_0_indent();

      for (unsigned i=0; i<s.probes.size(); i++)
        {
          assert_no_interrupts();
          s.up->emit_probe (s.probes[i]);
        }
      s.op->assert_0_indent();

      s.op->newline() << "static struct stap_probe stap_probes[] = {";
      s.op->indent(1);
      for (unsigned i=0; i<s.probes.size(); ++i)
        {
          derived_probe* p = s.probes[i];
          s.op->newline() << "STAP_PROBE_INIT(" << i << ", &" << p->name << ", "
                          << lex_cast_qstring (*p->sole_location()) << ", "
                          << lex_cast_qstring (*p->script_location()) << ", "
                          << lex_cast_qstring (p->tok->location) << ", "
                          << lex_cast_qstring (p->derived_locations()) << "),";
        }
      s.op->newline(-1) << "};";

      if (s.runtime_usermode_p())
        {
          s.op->newline() << "static const char* stp_probe_point(size_t index) {";
          s.op->newline(1) << "if (index < ARRAY_SIZE(stap_probes))";
          s.op->newline(1) << "return stap_probes[index].pp;";
          s.op->newline(-1) << "return NULL;";
          s.op->newline(-1) << "}";
          s.op->assert_0_indent();
        }

      s.op->assert_0_indent();
      s.op->newline();
      s.up->emit_module_init ();
      s.op->assert_0_indent();
      s.op->newline();
      s.up->emit_module_refresh ();
      s.op->assert_0_indent();
      s.op->newline();
      s.up->emit_module_exit ();
      s.op->assert_0_indent();
      s.up->emit_kernel_module_init ();
      s.op->assert_0_indent();
      s.up->emit_kernel_module_exit ();
      s.op->assert_0_indent();
      s.op->newline();

      emit_symbol_data (s);

      s.op->newline() << "MODULE_DESCRIPTION(\"systemtap-generated probe\");";
      s.op->newline() << "MODULE_LICENSE(\"GPL\");";

      for (unsigned i = 0; i < s.modinfos.size(); i++)
        {
          const string& mi = s.modinfos[i];
          size_t loc = mi.find('=');
          string tag = mi.substr (0, loc);
          string value = mi.substr (loc+1);
          s.op->newline() << "MODULE_INFO(" << tag << "," << lex_cast_qstring(value) << ");";
        }

      s.op->assert_0_indent();

      if (s.runtime_usermode_p())
        s.up->emit_global_init_setters();
      else
        // PR10298: attempt to avoid collisions with symbols
        for (unsigned i=0; i<s.globals.size(); i++)
          {
            s.op->newline();
            s.up->emit_global_param (s.globals[i]);
          }
      s.op->assert_0_indent();
    }
  catch (const semantic_error& e)
    {
      s.print_error (e);
    }

  s.op->line() << "\n";

  delete s.op;
  s.op = 0;
  s.up = 0;

  return rc + s.num_errors();
}

/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */