summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Brolley <brolley@redhat.com>2009-09-03 19:53:24 -0400
committerDave Brolley <brolley@redhat.com>2009-09-03 19:53:24 -0400
commita5d268f35032292b8f85cc75a316930ed0b95aab (patch)
treedd78d073ab80624478e77eebab3c5e9767524faa
parent8402bcf68e5321b3459cbbbd27d5111bb184922e (diff)
parentdf56da84549816b15a1f7aea3c9f71de2b2001ad (diff)
downloadsystemtap-steved-a5d268f35032292b8f85cc75a316930ed0b95aab.tar.gz
systemtap-steved-a5d268f35032292b8f85cc75a316930ed0b95aab.tar.xz
systemtap-steved-a5d268f35032292b8f85cc75a316930ed0b95aab.zip
Merge branch 'master' of ssh://sources.redhat.com/git/systemtap
-rw-r--r--buildrun.cxx10
-rw-r--r--dwflpp.cxx130
-rw-r--r--dwflpp.h43
-rw-r--r--elaborate.cxx6
-rw-r--r--hash.cxx1
-rw-r--r--main.cxx4
-rw-r--r--parse.cxx5
-rw-r--r--staptree.cxx2
-rw-r--r--tapset-mark.cxx12
-rw-r--r--tapset-perfmon.cxx6
-rw-r--r--tapset-procfs.cxx2
-rw-r--r--tapset-timers.cxx4
-rw-r--r--tapset-utrace.cxx6
-rw-r--r--tapsets.cxx65
-rw-r--r--translate.cxx36
-rw-r--r--unordered.h56
-rw-r--r--util.h68
17 files changed, 271 insertions, 185 deletions
diff --git a/buildrun.cxx b/buildrun.cxx
index a40dab15..6bef4095 100644
--- a/buildrun.cxx
+++ b/buildrun.cxx
@@ -363,10 +363,10 @@ run_pass (systemtap_session& s)
staprun_cmd += "-c " + cmdstr_quoted(s.cmd) + " ";
if (s.target_pid)
- staprun_cmd += "-t " + stringify(s.target_pid) + " ";
+ staprun_cmd += "-t " + lex_cast(s.target_pid) + " ";
if (s.buffer_size)
- staprun_cmd += "-b " + stringify(s.buffer_size) + " ";
+ staprun_cmd += "-b " + lex_cast(s.buffer_size) + " ";
if (s.need_uprobes)
staprun_cmd += "-u ";
@@ -390,7 +390,7 @@ make_tracequery(systemtap_session& s, string& name,
const vector<string>& extra_headers)
{
static unsigned tick = 0;
- string basename("tracequery_kmod_" + lex_cast<string>(++tick));
+ string basename("tracequery_kmod_" + lex_cast(++tick));
// create a subdirectory for the module
string dir(s.tmpdir + "/" + basename);
@@ -461,7 +461,7 @@ static int
make_typequery_kmod(systemtap_session& s, const string& header, string& name)
{
static unsigned tick = 0;
- string basename("typequery_kmod_" + lex_cast<string>(++tick));
+ string basename("typequery_kmod_" + lex_cast(++tick));
// create a subdirectory for the module
string dir(s.tmpdir + "/" + basename);
@@ -519,7 +519,7 @@ make_typequery_umod(systemtap_session& s, const string& header, string& name)
{
static unsigned tick = 0;
- name = s.tmpdir + "/typequery_umod_" + lex_cast<string>(++tick) + ".so";
+ name = s.tmpdir + "/typequery_umod_" + lex_cast(++tick) + ".so";
// make the module
//
diff --git a/dwflpp.cxx b/dwflpp.cxx
index ba8dcec7..2437630e 100644
--- a/dwflpp.cxx
+++ b/dwflpp.cxx
@@ -93,6 +93,23 @@ dwflpp::dwflpp(systemtap_session & session, const vector<string>& names):
dwflpp::~dwflpp()
{
free(cached_scopes);
+
+ for (module_cu_cache_t::iterator it = module_cu_cache.begin();
+ it != module_cu_cache.end(); ++it)
+ delete it->second;
+
+ for (mod_cu_function_cache_t::iterator it = cu_function_cache.begin();
+ it != cu_function_cache.end(); ++it)
+ delete it->second;
+
+ for (cu_inl_function_cache_t::iterator it = cu_inl_function_cache.begin();
+ it != cu_inl_function_cache.end(); ++it)
+ delete it->second;
+
+ for (mod_cu_type_cache_t::iterator it = global_alias_cache.begin();
+ it != global_alias_cache.end(); ++it)
+ delete it->second;
+
if (dwfl)
dwfl_end(dwfl);
}
@@ -577,7 +594,7 @@ dwflpp::iterate_over_inline_instances (int (* callback)(Dwarf_Die * die, void *
int
dwflpp::global_alias_caching_callback(Dwarf_Die *die, void *arg)
{
- cu_function_cache_t *cache = static_cast<cu_function_cache_t*>(arg);
+ cu_type_cache_t *cache = static_cast<cu_type_cache_t*>(arg);
const char *name = dwarf_diename(die);
if (!name)
@@ -599,10 +616,10 @@ dwflpp::declaration_resolve(const char *name)
if (!name)
return NULL;
- cu_function_cache_t *v = global_alias_cache[cu->addr];
+ cu_type_cache_t *v = global_alias_cache[cu->addr];
if (v == 0) // need to build the cache, just once per encountered module/cu
{
- v = new cu_function_cache_t;
+ v = new cu_type_cache_t;
global_alias_cache[cu->addr] = v;
iterate_over_globals(global_alias_caching_callback, v);
if (sess.verbose > 4)
@@ -633,8 +650,7 @@ dwflpp::cu_function_caching_callback (Dwarf_Die* func, void *arg)
if (!name)
return DWARF_CB_OK;
- string function_name = name;
- (*v)[function_name] = * func;
+ v->insert(make_pair(string(name), *func));
return DWARF_CB_OK;
}
@@ -660,14 +676,19 @@ dwflpp::iterate_over_functions (int (* callback)(Dwarf_Die * func, base_query *
mod_info->update_symtab(v);
}
- cu_function_cache_t::iterator it = v->find(function);
- if (it != v->end())
+ cu_function_cache_t::iterator it;
+ cu_function_cache_range_t range = v->equal_range(function);
+ if (range.first != range.second)
{
- Dwarf_Die& die = it->second;
- if (sess.verbose > 4)
- clog << "function cache " << module_name << ":" << cu_name()
- << " hit " << function << endl;
- return (*callback)(& die, q);
+ for (it = range.first; it != range.second; ++it)
+ {
+ Dwarf_Die& die = it->second;
+ if (sess.verbose > 4)
+ clog << "function cache " << module_name << ":" << cu_name()
+ << " hit " << function << endl;
+ rc = (*callback)(& die, q);
+ if (rc != DWARF_CB_OK) break;
+ }
}
else if (name_has_wildcard (function))
{
@@ -1243,7 +1264,7 @@ dwflpp::die_entrypc (Dwarf_Die * die, Dwarf_Addr * addr)
while ((offset = dwarf_ranges (die, offset, &base, &begin, &end)) > 0)
extra ++;
if (extra)
- lookup_method += ", ignored " + lex_cast<string>(extra) + " more";
+ lookup_method += ", ignored " + lex_cast(extra) + " more";
}
}
@@ -1437,7 +1458,7 @@ dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die,
if (nscopes <= 0)
{
throw semantic_error ("unable to find any scopes containing "
- + lex_cast_hex<string>(pc)
+ + lex_cast_hex(pc)
+ ((scope_die == NULL) ? ""
: (string (" in ")
+ (dwarf_diename(scope_die) ?: "<unknown>")
@@ -1456,7 +1477,7 @@ dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die,
stringstream alternatives;
print_locals (scopes, alternatives);
throw semantic_error ("unable to find local '" + local + "'"
- + " near pc " + lex_cast_hex<string>(pc)
+ + " near pc " + lex_cast_hex(pc)
+ ((scope_die == NULL) ? ""
: (string (" in ")
+ (dwarf_diename(scope_die) ?: "<unknown>")
@@ -1727,7 +1748,7 @@ dwflpp::translate_components(struct obstack *pool,
#if 0
// Emit a marker to note which field is being access-attempted, to give
// better error messages if deref() fails.
- string piece = string(...target_symbol token...) + string ("#") + stringify(components[i].second);
+ string piece = string(...target_symbol token...) + string ("#") + lex_cast(components[i].second);
obstack_printf (pool, "c->last_stmt = %s;", lex_cast_qstring(piece).c_str());
#endif
@@ -1749,7 +1770,7 @@ dwflpp::translate_components(struct obstack *pool,
case DW_TAG_pointer_type:
/* A pointer with no type is a void* -- can't dereference it. */
if (!dwarf_hasattr_integrate (die, DW_AT_type))
- throw semantic_error ("invalid access '" + lex_cast<string>(c)
+ throw semantic_error ("invalid access '" + lex_cast(c)
+ "' vs. " + dwarf_type_name(die),
c.tok);
@@ -1768,14 +1789,14 @@ dwflpp::translate_components(struct obstack *pool,
}
else if (c.type == target_symbol::comp_expression_array_index)
{
- string index = "THIS->index" + lex_cast<string>(i);
+ string index = "THIS->index" + lex_cast(i);
c_translate_array (pool, 1, 0 /* PR9768 */, die, tail,
index.c_str(), 0);
++i;
}
else
throw semantic_error ("invalid access '"
- + lex_cast<string>(c)
+ + lex_cast(c)
+ "' for array type",
c.tok);
break;
@@ -1785,7 +1806,7 @@ dwflpp::translate_components(struct obstack *pool,
case DW_TAG_class_type:
if (c.type != target_symbol::comp_struct_member)
throw semantic_error ("invalid access '"
- + lex_cast<string>(c)
+ + lex_cast(c)
+ "' for " + dwarf_type_name(die),
c.tok);
@@ -1811,7 +1832,7 @@ dwflpp::translate_components(struct obstack *pool,
const char *file = dwarf_decl_file(&parentdie);
if (file && dwarf_decl_line(&parentdie, &line) == 0)
source = " (" + string(file) + ":"
- + lex_cast<string>(line) + ")";
+ + lex_cast(line) + ")";
}
string alternatives;
@@ -1836,7 +1857,7 @@ dwflpp::translate_components(struct obstack *pool,
case DW_TAG_enumeration_type:
case DW_TAG_base_type:
throw semantic_error ("invalid access '"
- + lex_cast<string>(c)
+ + lex_cast(c)
+ "' vs. " + dwarf_type_name(die),
c.tok);
break;
@@ -1848,7 +1869,7 @@ dwflpp::translate_components(struct obstack *pool,
default:
throw semantic_error (dwarf_type_name(die) + ": unexpected type tag "
- + lex_cast<string>(dwarf_tag (die)),
+ + lex_cast(dwarf_tag (die)),
c.tok);
break;
}
@@ -1936,7 +1957,7 @@ dwflpp::translate_final_fetch_or_store (struct obstack *pool,
{
default:
throw semantic_error ("unsupported type tag "
- + lex_cast<string>(typetag)
+ + lex_cast(typetag)
+ " for " + dwarf_type_name(typedie), e->tok);
break;
@@ -1959,7 +1980,7 @@ dwflpp::translate_final_fetch_or_store (struct obstack *pool,
if (encoding < 0)
{
// clog << "bad type1 " << encoding << " diestr" << endl;
- throw semantic_error ("unsupported type (mystery encoding " + lex_cast<string>(encoding) + ")" +
+ throw semantic_error ("unsupported type (mystery encoding " + lex_cast(encoding) + ")" +
" for " + dwarf_type_name(typedie), e->tok);
}
@@ -1968,7 +1989,7 @@ dwflpp::translate_final_fetch_or_store (struct obstack *pool,
/* XXX || many others? */)
{
// clog << "bad type " << encoding << " diestr" << endl;
- throw semantic_error ("unsupported type (encoding " + lex_cast<string>(encoding) + ")" +
+ throw semantic_error ("unsupported type (encoding " + lex_cast(encoding) + ")" +
" for " + dwarf_type_name(typedie), e->tok);
}
}
@@ -2097,7 +2118,7 @@ dwflpp::literal_stmt_for_local (Dwarf_Die *scope_die,
throw semantic_error("failed to retrieve location "
"attribute for local '" + local
+ "' (dieoffset: "
- + lex_cast_hex<string>(dwarf_dieoffset (&vardie))
+ + lex_cast_hex(dwarf_dieoffset (&vardie))
+ ")",
e->tok);
}
@@ -2281,21 +2302,14 @@ dwflpp::blacklisted_p(const string& funcname,
const string& filename,
int,
const string& module,
- const string& section,
Dwarf_Addr addr,
bool has_return)
{
if (!blacklist_enabled)
return false; // no blacklist for userspace
- if (section.substr(0, 6) == string(".init.") ||
- section.substr(0, 6) == string(".exit.") ||
- section.substr(0, 9) == string(".devinit.") ||
- section.substr(0, 9) == string(".devexit.") ||
- section.substr(0, 9) == string(".cpuinit.") ||
- section.substr(0, 9) == string(".cpuexit.") ||
- section.substr(0, 9) == string(".meminit.") ||
- section.substr(0, 9) == string(".memexit."))
+ string section = get_blacklist_section(addr);
+ if (!regexec (&blacklist_section, section.c_str(), 0, NULL, 0))
{
// NB: module .exit. routines could be probed in theory:
// if the exit handler in "struct module" is diverted,
@@ -2351,16 +2365,26 @@ dwflpp::build_blacklist()
string blfn = "^(";
string blfn_ret = "^(";
string blfile = "^(";
-
- blfile += "kernel/kprobes.c"; // first alternative, no "|"
- blfile += "|arch/.*/kernel/kprobes.c";
+ string blsection = "^(";
+
+ blsection += "\\.init\\."; // first alternative, no "|"
+ blsection += "|\\.exit\\.";
+ blsection += "|\\.devinit\\.";
+ blsection += "|\\.devexit\\.";
+ blsection += "|\\.cpuinit\\.";
+ blsection += "|\\.cpuexit\\.";
+ blsection += "|\\.meminit\\.";
+ blsection += "|\\.memexit\\.";
+
+ blfile += "kernel/kprobes\\.c"; // first alternative, no "|"
+ blfile += "|arch/.*/kernel/kprobes\\.c";
// Older kernels need ...
- blfile += "|include/asm/io.h";
- blfile += "|include/asm/bitops.h";
+ blfile += "|include/asm/io\\.h";
+ blfile += "|include/asm/bitops\\.h";
// While newer ones need ...
- blfile += "|arch/.*/include/asm/io.h";
- blfile += "|arch/.*/include/asm/bitops.h";
- blfile += "|drivers/ide/ide-iops.c";
+ blfile += "|arch/.*/include/asm/io\\.h";
+ blfile += "|arch/.*/include/asm/bitops\\.h";
+ blfile += "|drivers/ide/ide-iops\\.c";
// XXX: it would be nice if these blacklisted functions were pulled
// in dynamically, instead of being statically defined here.
@@ -2451,6 +2475,7 @@ dwflpp::build_blacklist()
blfn += ")$";
blfn_ret += ")$";
blfile += ")$";
+ blsection += ")"; // NB: no $, sections match just the beginning
if (sess.verbose > 2)
{
@@ -2458,6 +2483,7 @@ dwflpp::build_blacklist()
clog << "blfn: " << blfn << endl;
clog << "blfn_ret: " << blfn_ret << endl;
clog << "blfile: " << blfile << endl;
+ clog << "blsection: " << blsection << endl;
}
int rc = regcomp (& blacklist_func, blfn.c_str(), REG_NOSUB|REG_EXTENDED);
@@ -2466,6 +2492,8 @@ dwflpp::build_blacklist()
if (rc) throw semantic_error ("blacklist_func_ret regcomp failed");
rc = regcomp (& blacklist_file, blfile.c_str(), REG_NOSUB|REG_EXTENDED);
if (rc) throw semantic_error ("blacklist_file regcomp failed");
+ rc = regcomp (& blacklist_section, blsection.c_str(), REG_NOSUB|REG_EXTENDED);
+ if (rc) throw semantic_error ("blacklist_section regcomp failed");
blacklist_enabled = true;
}
@@ -2512,9 +2540,7 @@ dwflpp::get_blacklist_section(Dwarf_Addr addr)
Dwarf_Addr
-dwflpp::relocate_address(Dwarf_Addr dw_addr,
- string& reloc_section,
- string& blacklist_section)
+dwflpp::relocate_address(Dwarf_Addr dw_addr, string& reloc_section)
{
// PR10273
// libdw address, so adjust for bias gotten from dwfl_module_getdwarf
@@ -2523,7 +2549,6 @@ dwflpp::relocate_address(Dwarf_Addr dw_addr,
{
assert(module_name == TOK_KERNEL);
reloc_section = "";
- blacklist_section = "";
}
else if (dwfl_module_relocations (module) > 0)
{
@@ -2533,19 +2558,12 @@ dwflpp::relocate_address(Dwarf_Addr dw_addr,
const char* r_s = dwfl_module_relocation_info (module, idx, NULL);
if (r_s)
reloc_section = r_s;
- blacklist_section = reloc_section;
if (reloc_section == "" && dwfl_module_relocations (module) == 1)
- {
- blacklist_section = get_blacklist_section(dw_addr);
reloc_section = ".dynamic";
- }
}
else
- {
- blacklist_section = get_blacklist_section(dw_addr);
- reloc_section = ".absolute";
- }
+ reloc_section = ".absolute";
return reloc_addr;
}
diff --git a/dwflpp.h b/dwflpp.h
index 047ad602..74a3ae00 100644
--- a/dwflpp.h
+++ b/dwflpp.h
@@ -15,6 +15,7 @@
#include "dwarf_wrappers.h"
#include "elaborate.h"
#include "session.h"
+#include "unordered.h"
#include <cstring>
#include <iostream>
@@ -43,33 +44,26 @@ struct dwarf_query;
enum line_t { ABSOLUTE, RELATIVE, RANGE, WILDCARD };
enum info_status { info_unknown, info_present, info_absent };
-#ifdef HAVE_TR1_UNORDERED_MAP
-#include <tr1/unordered_map>
-template<class K, class V> struct stap_map {
- typedef std::tr1::unordered_map<K, V> type;
-};
-#else
-#include <ext/hash_map>
-template<class K, class V> struct stap_map {
- typedef __gnu_cxx::hash_map<K, V, stap_map> type;
- size_t operator() (std::string const& s) const
- { __gnu_cxx::hash<const char*> h; return h(s.c_str()); }
- size_t operator() (void* const& p) const
- { __gnu_cxx::hash<long> h; return h(reinterpret_cast<long>(p)); }
-};
-#endif
-
// module -> cu die[]
-typedef stap_map<Dwarf*, std::vector<Dwarf_Die>*>::type module_cu_cache_t;
+typedef unordered_map<Dwarf*, std::vector<Dwarf_Die>*> module_cu_cache_t;
+
+// typename -> die
+typedef unordered_map<std::string, Dwarf_Die> cu_type_cache_t;
+
+// cu die -> (typename -> die)
+typedef unordered_map<void*, cu_type_cache_t*> mod_cu_type_cache_t;
// function -> die
-typedef stap_map<std::string, Dwarf_Die>::type cu_function_cache_t;
+typedef unordered_multimap<std::string, Dwarf_Die> cu_function_cache_t;
+typedef std::pair<cu_function_cache_t::iterator,
+ cu_function_cache_t::iterator>
+ cu_function_cache_range_t;
// cu die -> (function -> die)
-typedef stap_map<void*, cu_function_cache_t*>::type mod_cu_function_cache_t;
+typedef unordered_map<void*, cu_function_cache_t*> mod_cu_function_cache_t;
// inline function die -> instance die[]
-typedef stap_map<void*, std::vector<Dwarf_Die>*>::type cu_inl_function_cache_t;
+typedef unordered_map<void*, std::vector<Dwarf_Die>*> cu_inl_function_cache_t;
typedef std::vector<func_info> func_info_map_t;
typedef std::vector<inline_instance_info> inline_instance_map_t;
@@ -145,6 +139,7 @@ struct inline_instance_info
{
std::memset(&die, 0, sizeof(die));
}
+ bool operator<(const inline_instance_info& other) const;
std::string name;
char const * decl_file;
int decl_line;
@@ -273,13 +268,10 @@ struct dwflpp
const std::string& filename,
int line,
const std::string& module,
- const std::string& section,
Dwarf_Addr addr,
bool has_return);
- Dwarf_Addr relocate_address(Dwarf_Addr addr,
- std::string& reloc_section,
- std::string& blacklist_section);
+ Dwarf_Addr relocate_address(Dwarf_Addr addr, std::string& reloc_section);
Dwarf_Addr literal_addr_to_sym_addr(Dwarf_Addr lit_addr);
@@ -308,7 +300,7 @@ private:
* cache is indexed by name. If other declaration lookups were
* added to it, it would have to be indexed by name and tag
*/
- mod_cu_function_cache_t global_alias_cache;
+ mod_cu_type_cache_t global_alias_cache;
static int global_alias_caching_callback(Dwarf_Die *die, void *arg);
int iterate_over_globals (int (* callback)(Dwarf_Die *, void *),
void * data);
@@ -377,6 +369,7 @@ private:
regex_t blacklist_func; // function/statement probes
regex_t blacklist_func_ret; // only for .return probes
regex_t blacklist_file; // file name
+ regex_t blacklist_section; // init/exit sections
bool blacklist_enabled;
void build_blacklist();
std::string get_blacklist_section(Dwarf_Addr addr);
diff --git a/elaborate.cxx b/elaborate.cxx
index 17f335d0..c1a2e898 100644
--- a/elaborate.cxx
+++ b/elaborate.cxx
@@ -331,7 +331,7 @@ match_node::find_and_build (systemtap_session& s,
alternatives += string(" ") + i->first.str();
throw semantic_error (string("probe point truncated at position ") +
- lex_cast<string> (pos) +
+ lex_cast (pos) +
" (follow:" + alternatives + ")", loc->tok);
}
@@ -416,7 +416,7 @@ match_node::find_and_build (systemtap_session& s,
alternatives += string(" ") + i->first.str();
throw semantic_error(string("probe point mismatch at position ") +
- lex_cast<string> (pos) +
+ lex_cast (pos) +
" (alternatives:" + alternatives + ")" +
" didn't find any wildcard matches",
loc->tok);
@@ -433,7 +433,7 @@ match_node::find_and_build (systemtap_session& s,
alternatives += string(" ") + i->first.str();
throw semantic_error (string("probe point mismatch at position ") +
- lex_cast<string> (pos) +
+ lex_cast (pos) +
" (alternatives:" + alternatives + ")",
loc->tok);
}
diff --git a/hash.cxx b/hash.cxx
index bcf0ebcc..df29afed 100644
--- a/hash.cxx
+++ b/hash.cxx
@@ -174,6 +174,7 @@ find_script_hash (systemtap_session& s, const string& script, const hash &base)
h.add(s.ignore_dwarf); // --ignore-dwarf
h.add(s.consult_symtab); // --kelf, --kmap
h.add(s.skip_badvars); // --skip-badvars
+ h.add(s.unprivileged); // --unprivileged
if (!s.kernel_symtab_path.empty()) // --kmap
{
h.add(s.kernel_symtab_path);
diff --git a/main.cxx b/main.cxx
index b97d0092..d8c780c2 100644
--- a/main.cxx
+++ b/main.cxx
@@ -486,8 +486,8 @@ main (int argc, char * const argv [])
s.buffer_size = 0;
s.last_pass = 5;
- s.module_name = "stap_" + stringify(getpid());
- s.stapconf_name = "stapconf_" + stringify(getpid()) + ".h";
+ s.module_name = "stap_" + lex_cast(getpid());
+ s.stapconf_name = "stapconf_" + lex_cast(getpid()) + ".h";
s.output_file = ""; // -o FILE
s.keep_tmpdir = false;
s.cmd = "";
diff --git a/parse.cxx b/parse.cxx
index a2e2b656..bfd7600f 100644
--- a/parse.cxx
+++ b/parse.cxx
@@ -754,9 +754,8 @@ lexer::scan (bool wildcard)
idx <= session.args.size()); // prevent overflow
if (idx == 0 ||
idx-1 >= session.args.size())
- throw parse_error ("command line argument index " + lex_cast<string>(idx)
- + " out of range [1-" + lex_cast<string>(session.args.size()) + "]", n);
-
+ throw parse_error ("command line argument index " + lex_cast(idx)
+ + " out of range [1-" + lex_cast(session.args.size()) + "]", n);
string arg = session.args[idx-1];
if (c == '$') input_put (arg);
else input_put (lex_cast_qstring (arg));
diff --git a/staptree.cxx b/staptree.cxx
index b4975017..090f0bd3 100644
--- a/staptree.cxx
+++ b/staptree.cxx
@@ -101,7 +101,7 @@ probe::probe ():
body (0), tok (0)
{
static unsigned last_probeidx = 0;
- this->name = string ("probe_") + lex_cast<string>(last_probeidx ++);
+ this->name = string ("probe_") + lex_cast(last_probeidx ++);
}
diff --git a/tapset-mark.cxx b/tapset-mark.cxx
index 6b4f47c5..b70098e3 100644
--- a/tapset-mark.cxx
+++ b/tapset-mark.cxx
@@ -108,7 +108,7 @@ mark_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
// Remember that we've seen a target variable.
target_symbol_seen = true;
- e->probe_context_var = "__mark_arg" + lex_cast<string>(argnum);
+ e->probe_context_var = "__mark_arg" + lex_cast(argnum);
e->type = mark_args[argnum-1]->stp_type;
provide (e);
}
@@ -156,10 +156,10 @@ mark_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
{
if (i > 0)
pf->raw_components += " ";
- pf->raw_components += "$arg" + lex_cast<string>(i+1);
+ pf->raw_components += "$arg" + lex_cast(i+1);
target_symbol *tsym = new target_symbol;
tsym->tok = e->tok;
- tsym->base_name = "$arg" + lex_cast<string>(i+1);
+ tsym->base_name = "$arg" + lex_cast(i+1);
tsym->saved_conversion_error = 0;
expression *texp = require (tsym); //same treatment as tracepoint
@@ -414,7 +414,7 @@ mark_derived_probe::emit_probe_context_vars (translator_output* o)
for (unsigned i = 0; i < mark_args.size(); i++)
{
- string localname = "__mark_arg" + lex_cast<string>(i+1);
+ string localname = "__mark_arg" + lex_cast(i+1);
switch (mark_args[i]->stp_type)
{
case pe_long:
@@ -441,7 +441,7 @@ mark_derived_probe::initialize_probe_context_vars (translator_output* o)
bool deref_fault_needed = false;
for (unsigned i = 0; i < mark_args.size(); i++)
{
- string localname = "l->__mark_arg" + lex_cast<string>(i+1);
+ string localname = "l->__mark_arg" + lex_cast(i+1);
switch (mark_args[i]->stp_type)
{
case pe_long:
@@ -475,7 +475,7 @@ mark_derived_probe::printargs(std::ostream &o) const
{
for (unsigned i = 0; i < mark_args.size(); i++)
{
- string localname = "$arg" + lex_cast<string>(i+1);
+ string localname = "$arg" + lex_cast(i+1);
switch (mark_args[i]->stp_type)
{
case pe_long:
diff --git a/tapset-perfmon.cxx b/tapset-perfmon.cxx
index 827e88ca..56abb997 100644
--- a/tapset-perfmon.cxx
+++ b/tapset-perfmon.cxx
@@ -59,7 +59,7 @@ perfmon_var_expanding_visitor::visit_target_symbol (target_symbol *e)
string fname = string("_perfmon_tvar_get")
+ "_" + e->base_name.substr(1)
- + "_" + lex_cast<string>(counter_number);
+ + "_" + lex_cast(counter_number);
if (e->base_name != "$counter")
throw semantic_error ("target variables not available to perfmon probes");
@@ -70,7 +70,7 @@ perfmon_var_expanding_visitor::visit_target_symbol (target_symbol *e)
e->assert_no_components("perfmon");
ec->code = "THIS->__retvalue = _pfm_pmd_x[" +
- lex_cast<string>(counter_number) + "].reg_num;";
+ lex_cast(counter_number) + "].reg_num;";
ec->code += "/* pure */";
fdecl->name = fname;
fdecl->body = ec;
@@ -390,7 +390,7 @@ perfmon_derived_probe_group::emit_module_init (translator_output* o)
/* output the needed bits for pmc here */
for (unsigned i=0; i < outp.pfp_pmc_count; i++) {
o->newline() << "{.reg_num=" << pc[i].reg_num << ", "
- << ".reg_value=" << lex_cast_hex<string>(pc[i].reg_value)
+ << ".reg_value=" << lex_cast_hex(pc[i].reg_value)
<< "},";
}
diff --git a/tapset-procfs.cxx b/tapset-procfs.cxx
index 527b4486..fd8ad62a 100644
--- a/tapset-procfs.cxx
+++ b/tapset-procfs.cxx
@@ -379,7 +379,7 @@ procfs_var_expanding_visitor::visit_target_symbol (target_symbol* e)
ec->tok = e->tok;
string fname = (string(lvalue ? "_procfs_value_set" : "_procfs_value_get")
- + "_" + lex_cast<string>(tick++));
+ + "_" + lex_cast(tick++));
string locvalue = "CONTEXT->data";
if (! lvalue)
diff --git a/tapset-timers.cxx b/tapset-timers.cxx
index 565a54e8..16dcefcb 100644
--- a/tapset-timers.cxx
+++ b/tapset-timers.cxx
@@ -193,10 +193,10 @@ struct hrtimer_derived_probe: public derived_probe
{
if ((i < min_ns_interval) || (i > max_ns_interval))
throw semantic_error(string("interval value out of range (")
- + lex_cast<string>(scale < min_ns_interval
+ + lex_cast(scale < min_ns_interval
? min_ns_interval/scale : 1)
+ ","
- + lex_cast<string>(max_ns_interval/scale) + ")");
+ + lex_cast(max_ns_interval/scale) + ")");
// randomize = 0 means no randomization
if ((r < 0) || (r > i))
diff --git a/tapset-utrace.cxx b/tapset-utrace.cxx
index d9d95f82..490af20f 100644
--- a/tapset-utrace.cxx
+++ b/tapset-utrace.cxx
@@ -224,7 +224,7 @@ utrace_var_expanding_visitor::visit_target_symbol_cached (target_symbol* e)
// _utrace_tvar_{name}_{num}
string aname = (string("_utrace_tvar_")
+ e->base_name.substr(1)
- + "_" + lex_cast<string>(tick++));
+ + "_" + lex_cast(tick++));
vardecl* vd = new vardecl;
vd->name = aname;
vd->tok = e->tok;
@@ -429,10 +429,10 @@ utrace_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
{
if (i > 0)
pf->raw_components += " ";
- pf->raw_components += "$arg" + lex_cast<string>(i+1);
+ pf->raw_components += "$arg" + lex_cast(i+1);
target_symbol *tsym = new target_symbol;
tsym->tok = e->tok;
- tsym->base_name = "$arg" + lex_cast<string>(i+1);
+ tsym->base_name = "$arg" + lex_cast(i+1);
tsym->saved_conversion_error = 0;
pf->raw_components += "=%#x"; //FIXME: missing type info
diff --git a/tapsets.cxx b/tapsets.cxx
index 6a52050c..053344cf 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -552,6 +552,10 @@ struct dwarf_query : public base_query
// Track addresses we've already seen in a given module
set<Dwarf_Addr> alias_dupes;
+ // Track inlines we've already seen as well
+ // NB: this can't be compared just by entrypc, as inlines can overlap
+ set<inline_instance_info> inline_dupes;
+
// Extracted parameters.
string function_val;
@@ -850,6 +854,7 @@ dwarf_query::handle_query_module()
// reset the dupe-checking for each new module
alias_dupes.clear();
+ inline_dupes.clear();
if (dw.mod_info->dwarf_status == info_present)
query_module_dwarf();
@@ -960,12 +965,11 @@ dwarf_query::add_probe_point(const string& funcname,
{
string reloc_section; // base section for relocation purposes
Dwarf_Addr reloc_addr; // relocated
- string blacklist_section; // linking section for blacklist purposes
const string& module = dw.module_name; // "kernel" or other
assert (! has_absolute); // already handled in dwarf_builder::build()
- reloc_addr = dw.relocate_address(addr, reloc_section, blacklist_section);
+ reloc_addr = dw.relocate_address(addr, reloc_section);
if (sess.verbose > 1)
{
@@ -977,12 +981,11 @@ dwarf_query::add_probe_point(const string& funcname,
else if (has_process)
clog << " process=" << module;
if (reloc_section != "") clog << " reloc=" << reloc_section;
- if (blacklist_section != "") clog << " section=" << blacklist_section;
clog << " pc=0x" << hex << addr << dec;
}
bool bad = dw.blacklisted_p (funcname, filename, line, module,
- blacklist_section, addr, has_return);
+ addr, has_return);
if (sess.verbose > 1)
clog << endl;
@@ -1248,6 +1251,22 @@ query_srcfile_line (const dwarf_line_t& line, void * arg)
}
+bool
+inline_instance_info::operator<(const inline_instance_info& other) const
+{
+ if (entrypc != other.entrypc)
+ return entrypc < other.entrypc;
+
+ if (decl_line != other.decl_line)
+ return decl_line < other.decl_line;
+
+ int cmp = name.compare(other.name);
+ if (!cmp)
+ cmp = strcmp(decl_file, other.decl_file);
+ return cmp < 0;
+}
+
+
static int
query_dwarf_inline_instance (Dwarf_Die * die, void * arg)
{
@@ -1275,7 +1294,11 @@ query_dwarf_inline_instance (Dwarf_Die * die, void * arg)
inl.entrypc = entrypc;
q->dw.function_file (&inl.decl_file);
q->dw.function_line (&inl.decl_line);
- q->filtered_inlines.push_back(inl);
+
+ // make sure that this inline hasn't already
+ // been matched from a different CU
+ if (q->inline_dupes.insert(inl).second)
+ q->filtered_inlines.push_back(inl);
}
}
return DWARF_CB_OK;
@@ -1319,9 +1342,6 @@ query_dwarf_func (Dwarf_Die * func, base_query * bq)
clog << "checking instances of inline " << q->dw.function_name
<< "\n";
q->dw.iterate_over_inline_instances (query_dwarf_inline_instance, q);
-
- if (q->dw.function_name_final_match (q->function))
- return DWARF_CB_ABORT;
}
else if (!q->dw.func_is_inline () && (! q->has_inline))
{
@@ -1382,14 +1402,9 @@ query_dwarf_func (Dwarf_Die * func, base_query * bq)
func.entrypc -= q->dw.module_bias;
q->filtered_functions.push_back (func);
- if (q->dw.function_name_final_match (q->function))
- return DWARF_CB_ABORT;
}
else
assert(0);
-
- if (q->dw.function_name_final_match (q->function))
- return DWARF_CB_ABORT;
}
}
return DWARF_CB_OK;
@@ -1845,7 +1860,7 @@ dwarf_var_expanding_visitor::visit_target_symbol_saved_return (target_symbol* e)
string aname = (string("_dwarf_tvar_")
+ e->base_name.substr(1)
- + "_" + lex_cast<string>(tick++));
+ + "_" + lex_cast(tick++));
vardecl* vd = new vardecl;
vd->name = aname;
vd->tok = e->tok;
@@ -2256,7 +2271,7 @@ dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e)
string fname = (string(lvalue ? "_dwarf_tvar_set" : "_dwarf_tvar_get")
+ "_" + e->base_name.substr(1)
- + "_" + lex_cast<string>(tick++));
+ + "_" + lex_cast(tick++));
try
{
@@ -2322,7 +2337,7 @@ dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e)
{
vardecl *v = new vardecl;
v->type = pe_long;
- v->name = "index" + lex_cast<string>(i);
+ v->name = "index" + lex_cast(i);
v->tok = e->tok;
fdecl->formal_args.push_back(v);
}
@@ -2565,7 +2580,7 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
string fname = (string(lvalue ? "_dwarf_tvar_set" : "_dwarf_tvar_get")
+ "_" + e->base_name.substr(1)
- + "_" + lex_cast<string>(tick++));
+ + "_" + lex_cast(tick++));
// Synthesize a function.
functiondecl *fdecl = new functiondecl;
@@ -2591,7 +2606,7 @@ void dwarf_cast_expanding_visitor::visit_cast_op (cast_op* e)
{
vardecl *v = new vardecl;
v->type = pe_long;
- v->name = "index" + lex_cast<string>(i);
+ v->name = "index" + lex_cast(i);
v->tok = e->tok;
fdecl->formal_args.push_back(v);
}
@@ -2706,7 +2721,7 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname,
// Range limit maxactive() value
if (q.has_maxactive && (q.maxactive_val < 0 || q.maxactive_val > USHRT_MAX))
throw semantic_error ("maxactive value out of range [0,"
- + lex_cast<string>(USHRT_MAX) + "]",
+ + lex_cast(USHRT_MAX) + "]",
q.base_loc->tok);
// Expand target variables in the probe body
@@ -2763,7 +2778,7 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname,
{
retro_name += ("@" + string (filename));
if (line > 0)
- retro_name += (":" + lex_cast<string> (line));
+ retro_name += (":" + lex_cast (line));
}
comps.push_back
(new probe_point::component
@@ -3363,7 +3378,7 @@ sdt_var_expanding_visitor::visit_target_symbol (target_symbol *e)
cast->tok = e->tok;
cast->operand = fc;
cast->components = e->components;
- cast->type = probe_name + "_arg" + lex_cast<string>(argno);
+ cast->type = probe_name + "_arg" + lex_cast(argno);
cast->module = process_name;
cast->visit(this);
@@ -4152,7 +4167,7 @@ module_info::update_symtab(cu_function_cache_t *funcs)
// if this function is a new alias, then
// save it to merge into the function cache
if (it->second != fi)
- new_funcs[it->second->name] = it->second->die;
+ new_funcs.insert(make_pair(it->second->name, it->second->die));
}
}
@@ -4251,7 +4266,7 @@ uprobe_derived_probe::uprobe_derived_probe (const string& function,
{
retro_name += ("@" + string (filename));
if (line > 0)
- retro_name += (":" + lex_cast<string> (line));
+ retro_name += (":" + lex_cast (line));
}
comps.push_back
(new probe_point::component
@@ -5387,7 +5402,7 @@ tracepoint_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
string fname = (string(lvalue ? "_tracepoint_tvar_set" : "_tracepoint_tvar_get")
+ "_" + e->base_name.substr(1)
- + "_" + lex_cast<string>(tick++));
+ + "_" + lex_cast(tick++));
fdecl->name = fname;
fdecl->body = ec;
@@ -5426,7 +5441,7 @@ tracepoint_var_expanding_visitor::visit_target_symbol_arg (target_symbol* e)
{
vardecl *v = new vardecl;
v->type = pe_long;
- v->name = "index" + lex_cast<string>(i);
+ v->name = "index" + lex_cast(i);
v->tok = e->tok;
fdecl->formal_args.push_back(v);
}
diff --git a/translate.cxx b/translate.cxx
index c0f7b48b..9a25df61 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -384,9 +384,9 @@ public:
case statistic_decl::linear:
prefix += string("HIST_LINEAR")
- + ", " + stringify(sd.linear_low)
- + ", " + stringify(sd.linear_high)
- + ", " + stringify(sd.linear_step);
+ + ", " + lex_cast(sd.linear_low)
+ + ", " + lex_cast(sd.linear_high)
+ + ", " + lex_cast(sd.linear_step);
break;
case statistic_decl::logarithmic:
@@ -456,7 +456,7 @@ protected:
public:
tmpvar(exp_type ty,
unsigned & counter)
- : var(true, ty, ("__tmp" + stringify(counter++))), overridden(false)
+ : var(true, ty, ("__tmp" + lex_cast(counter++))), overridden(false)
{}
tmpvar(const var& source)
@@ -487,7 +487,7 @@ struct aggvar
: public var
{
aggvar(unsigned & counter)
- : var(true, pe_stats, ("__tmp" + stringify(counter++)))
+ : var(true, pe_stats, ("__tmp" + lex_cast(counter++)))
{}
string init() const
@@ -625,8 +625,8 @@ struct mapvar
throw semantic_error("adding a value of an unsupported map type");
res += "; if (unlikely(rc)) { c->last_error = \"Array overflow, check " +
- stringify(maxsize > 0 ?
- "size limit (" + stringify(maxsize) + ")" : "MAXMAPENTRIES")
+ lex_cast(maxsize > 0 ?
+ "size limit (" + lex_cast(maxsize) + ")" : "MAXMAPENTRIES")
+ "\"; goto out; }}";
return res;
@@ -646,8 +646,8 @@ struct mapvar
throw semantic_error("setting a value of an unsupported map type");
res += "; if (unlikely(rc)) { c->last_error = \"Array overflow, check " +
- stringify(maxsize > 0 ?
- "size limit (" + stringify(maxsize) + ")" : "MAXMAPENTRIES")
+ lex_cast(maxsize > 0 ?
+ "size limit (" + lex_cast(maxsize) + ")" : "MAXMAPENTRIES")
+ "\"; goto out; }}";
return res;
@@ -671,7 +671,7 @@ struct mapvar
{
string mtype = is_parallel() ? "pmap" : "map";
string prefix = value() + " = _stp_" + mtype + "_new_" + keysym() + " (" +
- (maxsize > 0 ? stringify(maxsize) : "MAXMAPENTRIES") ;
+ (maxsize > 0 ? lex_cast(maxsize) : "MAXMAPENTRIES") ;
// See also var::init().
@@ -689,9 +689,9 @@ struct mapvar
case statistic_decl::linear:
// FIXME: check for "reasonable" values in linear stats
prefix = prefix + ", HIST_LINEAR"
- + ", " + stringify(sdecl().linear_low)
- + ", " + stringify(sdecl().linear_high)
- + ", " + stringify(sdecl().linear_step);
+ + ", " + lex_cast(sdecl().linear_low)
+ + ", " + lex_cast(sdecl().linear_high)
+ + ", " + lex_cast(sdecl().linear_step);
break;
case statistic_decl::logarithmic:
@@ -728,7 +728,7 @@ public:
itervar (symbol* e, unsigned & counter)
: referent_ty(e->referent->type),
- name("__tmp" + stringify(counter++))
+ name("__tmp" + lex_cast(counter++))
{
if (referent_ty == pe_unknown)
throw semantic_error("iterating over unknown reference type", e->tok);
@@ -775,11 +775,11 @@ public:
switch (ty)
{
case pe_long:
- return "_stp_key_get_int64 ("+ value() + ", " + stringify(i+1) + ")";
+ return "_stp_key_get_int64 ("+ value() + ", " + lex_cast(i+1) + ")";
case pe_string:
// impedance matching: NULL -> empty strings
return "({ char *v = "
- "_stp_key_get_str ("+ value() + ", " + stringify(i+1) + "); "
+ "_stp_key_get_str ("+ value() + ", " + lex_cast(i+1) + "); "
"if (! v) v = \"\"; "
"v; })";
default:
@@ -2464,7 +2464,7 @@ c_tmpcounter::visit_for_loop (for_loop *s)
void
c_unparser::visit_for_loop (for_loop *s)
{
- string ctr = stringify (label_counter++);
+ string ctr = lex_cast (label_counter++);
string toplabel = "top_" + ctr;
string contlabel = "continue_" + ctr;
string breaklabel = "break_" + ctr;
@@ -2617,7 +2617,7 @@ c_unparser::visit_foreach_loop (foreach_loop *s)
itervar iv = getiter (array);
vector<var> keys;
- string ctr = stringify (label_counter++);
+ string ctr = lex_cast (label_counter++);
string toplabel = "top_" + ctr;
string contlabel = "continue_" + ctr;
string breaklabel = "break_" + ctr;
diff --git a/unordered.h b/unordered.h
new file mode 100644
index 00000000..6204dbfb
--- /dev/null
+++ b/unordered.h
@@ -0,0 +1,56 @@
+// backward-compatible unordered containers
+// Copyright (C) 2009 Red Hat Inc.
+//
+// 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.
+
+#ifndef UNORDERED_H
+#define UNORDERED_H
+
+#include "config.h"
+
+#if 0 // uncomment to force the old mode
+#undef HAVE_TR1_UNORDERED_MAP
+#define _BACKWARD_BACKWARD_WARNING_H 1 // defeat deprecation warning
+#endif
+
+#ifdef HAVE_TR1_UNORDERED_MAP
+
+#include <tr1/unordered_map>
+using std::tr1::unordered_map;
+using std::tr1::unordered_multimap;
+
+#include <tr1/unordered_set>
+using std::tr1::unordered_set;
+using std::tr1::unordered_multiset;
+
+#else
+
+#include <ext/hash_map>
+#define unordered_map __gnu_cxx::hash_map
+#define unordered_multimap __gnu_cxx::hash_multimap
+
+#include <ext/hash_set>
+#define unordered_set __gnu_cxx::hash_set
+#define unordered_multiset __gnu_cxx::hash_multiset
+
+// Hack in common hash functions for strings and raw pointers
+namespace __gnu_cxx
+{
+ template<class T> struct hash<T*> {
+ size_t operator() (T* p) const
+ { hash<long> h; return h(reinterpret_cast<long>(p)); }
+ };
+ template<> struct hash<std::string> {
+ size_t operator() (std::string const& s) const
+ { hash<const char*> h; return h(s.c_str()); }
+ };
+}
+
+#endif
+
+#endif // UNORDERED_H
+
+/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/util.h b/util.h
index 1577bb54..b38d01fd 100644
--- a/util.h
+++ b/util.h
@@ -23,38 +23,35 @@ int kill_stap_spawn(int sig);
// stringification generics
-template <typename T>
-inline std::string
-stringify(T t)
+template <typename IN>
+inline std::string lex_cast(IN const & in)
{
- std::ostringstream s;
- s << t;
- return s.str ();
+ std::ostringstream ss;
+ if (!(ss << in))
+ throw std::runtime_error("bad lexical cast");
+ return ss.str();
}
-template <typename OUT, typename IN>
-inline OUT lex_cast(IN const & in)
+template <typename OUT>
+inline OUT lex_cast(std::string const & in)
{
- std::stringstream ss;
+ std::istringstream ss(in);
OUT out;
- // NB: ss >> string out assumes that "in" renders to one word
- if (!(ss << in && ss >> out))
+ if (!(ss >> out && ss.eof()))
throw std::runtime_error("bad lexical cast");
return out;
}
-template <typename OUT, typename IN>
-inline OUT
+template <typename IN>
+inline std::string
lex_cast_hex(IN const & in)
{
- std::stringstream ss;
- OUT out;
- // NB: ss >> string out assumes that "in" renders to one word
- if (!(ss << "0x" << std::hex << in && ss >> out))
+ std::ostringstream ss;
+ if (!(ss << std::showbase << std::hex << in))
throw std::runtime_error("bad lexical cast");
- return out;
+ return ss.str();
}
@@ -65,32 +62,39 @@ inline std::string
lex_cast_qstring(IN const & in)
{
std::stringstream ss;
- std::string out, out2;
if (!(ss << in))
throw std::runtime_error("bad lexical cast");
- out = ss.str(); // "in" is expected to render to more than one word
- out2 += '"';
- for (unsigned i=0; i<out.length(); i++)
+ return lex_cast_qstring(ss.str());
+}
+
+
+template <>
+inline std::string
+lex_cast_qstring(std::string const & in)
+{
+ std::string out;
+ out += '"';
+ for (const char *p = in.c_str(); *p; ++p)
{
- unsigned char c = out[i];
+ unsigned char c = *p;
if (! isprint(c))
{
- out2 += '\\';
+ out += '\\';
// quick & dirty octal converter
- out2 += "01234567" [(c >> 6) & 0x07];
- out2 += "01234567" [(c >> 3) & 0x07];
- out2 += "01234567" [(c >> 0) & 0x07];
+ out += "01234567" [(c >> 6) & 0x07];
+ out += "01234567" [(c >> 3) & 0x07];
+ out += "01234567" [(c >> 0) & 0x07];
}
else if (c == '"' || c == '\\')
{
- out2 += '\\';
- out2 += c;
+ out += '\\';
+ out += c;
}
else
- out2 += c;
+ out += c;
}
- out2 += '"';
- return out2;
+ out += '"';
+ return out;
}
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */