summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dwflpp.cxx272
-rw-r--r--dwflpp.h25
-rw-r--r--tapsets.cxx66
-rw-r--r--util.h12
4 files changed, 279 insertions, 96 deletions
diff --git a/dwflpp.cxx b/dwflpp.cxx
index 2437630e..3e451916 100644
--- a/dwflpp.cxx
+++ b/dwflpp.cxx
@@ -56,6 +56,10 @@ extern "C" {
}
+// debug flag to compare to the uncached version from libdw
+// #define DEBUG_DWFLPP_GETSCOPES 1
+
+
using namespace std;
using namespace __gnu_cxx;
@@ -66,8 +70,7 @@ static string TOK_KERNEL("kernel");
dwflpp::dwflpp(systemtap_session & session, const string& name, bool kernel_p):
sess(session), module(NULL), module_bias(0), mod_info(NULL),
module_start(0), module_end(0), cu(NULL), dwfl(NULL),
- module_dwarf(NULL), function(NULL), blacklist_enabled(false),
- pc_cached_scopes(0), num_cached_scopes(0), cached_scopes(NULL)
+ module_dwarf(NULL), function(NULL), blacklist_enabled(false)
{
if (kernel_p)
setup_kernel(name);
@@ -83,8 +86,7 @@ dwflpp::dwflpp(systemtap_session & session, const string& name, bool kernel_p):
dwflpp::dwflpp(systemtap_session & session, const vector<string>& names):
sess(session), module(NULL), module_bias(0), mod_info(NULL),
module_start(0), module_end(0), cu(NULL), dwfl(NULL),
- module_dwarf(NULL), function(NULL), blacklist_enabled(false),
- pc_cached_scopes(0), num_cached_scopes(0), cached_scopes(NULL)
+ module_dwarf(NULL), function(NULL), blacklist_enabled(false)
{
setup_user(names);
}
@@ -92,23 +94,11 @@ 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;
+ delete_map(module_cu_cache);
+ delete_map(cu_function_cache);
+ delete_map(cu_inl_function_cache);
+ delete_map(global_alias_cache);
+ delete_map(cu_die_parent_cache);
if (dwfl)
dwfl_end(dwfl);
@@ -185,9 +175,6 @@ dwflpp::focus_on_cu(Dwarf_Die * c)
// Reset existing pointers and names
function_name.clear();
function = NULL;
-
- free(cached_scopes);
- cached_scopes = NULL;
}
@@ -591,6 +578,178 @@ dwflpp::iterate_over_inline_instances (int (* callback)(Dwarf_Die * die, void *
}
+void
+dwflpp::cache_die_parents(cu_die_parent_cache_t* parents, Dwarf_Die* die)
+{
+ // Record and recurse through DIEs we care about
+ Dwarf_Die child, import;
+ if (dwarf_child(die, &child) == 0)
+ do
+ {
+ switch (dwarf_tag (&child))
+ {
+ // normal tags to recurse
+ case DW_TAG_compile_unit:
+ case DW_TAG_module:
+ case DW_TAG_lexical_block:
+ case DW_TAG_with_stmt:
+ case DW_TAG_catch_block:
+ case DW_TAG_try_block:
+ case DW_TAG_entry_point:
+ case DW_TAG_inlined_subroutine:
+ case DW_TAG_subprogram:
+ parents->insert(make_pair(child.addr, *die));
+ cache_die_parents(parents, &child);
+ break;
+
+ // record only, nothing to recurse
+ case DW_TAG_label:
+ parents->insert(make_pair(child.addr, *die));
+ break;
+
+ // imported dies should be followed
+ case DW_TAG_imported_unit:
+ if (dwarf_attr_die(&child, DW_AT_import, &import))
+ {
+ parents->insert(make_pair(import.addr, *die));
+ cache_die_parents(parents, &import);
+ }
+ break;
+
+ // nothing to do for other tags
+ default:
+ break;
+ }
+ }
+ while (dwarf_siblingof(&child, &child) == 0);
+}
+
+
+cu_die_parent_cache_t*
+dwflpp::get_die_parents()
+{
+ assert (cu);
+
+ cu_die_parent_cache_t *& parents = cu_die_parent_cache[cu->addr];
+ if (!parents)
+ {
+ parents = new cu_die_parent_cache_t;
+ cache_die_parents(parents, cu);
+ if (sess.verbose > 4)
+ clog << "die parent cache " << module_name << ":" << cu_name()
+ << " size " << parents->size() << endl;
+ }
+ return parents;
+}
+
+
+vector<Dwarf_Die>
+dwflpp::getscopes_die(Dwarf_Die* die)
+{
+ cu_die_parent_cache_t *parents = get_die_parents();
+
+ vector<Dwarf_Die> scopes;
+ Dwarf_Die *scope = die;
+ cu_die_parent_cache_t::iterator it;
+ do
+ {
+ scopes.push_back(*scope);
+ it = parents->find(scope->addr);
+ scope = &it->second;
+ }
+ while (it != parents->end());
+
+#ifdef DEBUG_DWFLPP_GETSCOPES
+ Dwarf_Die *dscopes = NULL;
+ int nscopes = dwarf_getscopes_die(die, &dscopes);
+
+ assert(nscopes == (int)scopes.size());
+ for (unsigned i = 0; i < scopes.size(); ++i)
+ assert(scopes[i].addr == dscopes[i].addr);
+ free(dscopes);
+#endif
+
+ return scopes;
+}
+
+
+std::vector<Dwarf_Die>
+dwflpp::getscopes(Dwarf_Die* die)
+{
+ cu_die_parent_cache_t *parents = get_die_parents();
+
+ vector<Dwarf_Die> scopes;
+
+ Dwarf_Die origin;
+ Dwarf_Die *scope = die;
+ cu_die_parent_cache_t::iterator it;
+ do
+ {
+ scopes.push_back(*scope);
+ if (dwarf_tag(scope) == DW_TAG_inlined_subroutine &&
+ dwarf_attr_die(scope, DW_AT_abstract_origin, &origin))
+ scope = &origin;
+
+ it = parents->find(scope->addr);
+ scope = &it->second;
+ }
+ while (it != parents->end());
+
+#ifdef DEBUG_DWFLPP_GETSCOPES
+ // there isn't an exact libdw equivalent, but if dwarf_getscopes on the
+ // entrypc returns the same first die, then all the scopes should match
+ Dwarf_Addr pc;
+ if (die_entrypc(die, &pc))
+ {
+ Dwarf_Die *dscopes = NULL;
+ int nscopes = dwarf_getscopes(cu, pc, &dscopes);
+ if (nscopes > 0 && dscopes[0].addr == die->addr)
+ {
+ assert(nscopes == (int)scopes.size());
+ for (unsigned i = 0; i < scopes.size(); ++i)
+ assert(scopes[i].addr == dscopes[i].addr);
+ }
+ free(dscopes);
+ }
+#endif
+
+ return scopes;
+}
+
+
+std::vector<Dwarf_Die>
+dwflpp::getscopes(Dwarf_Addr pc)
+{
+ // The die_parent_cache doesn't help us without knowing where the pc is
+ // contained, so we have to do this one the old fashioned way.
+
+ assert (cu);
+
+ vector<Dwarf_Die> scopes;
+
+ Dwarf_Die* dwarf_scopes;
+ int nscopes = dwarf_getscopes(cu, pc, &dwarf_scopes);
+ if (nscopes > 0)
+ {
+ scopes.assign(dwarf_scopes, dwarf_scopes + nscopes);
+ free(dwarf_scopes);
+ }
+
+#ifdef DEBUG_DWFLPP_GETSCOPES
+ // check that getscopes on the starting die gets the same result
+ if (!scopes.empty())
+ {
+ vector<Dwarf_Die> other = getscopes(&scopes[0]);
+ assert(scopes.size() == other.size());
+ for (unsigned i = 0; i < scopes.size(); ++i)
+ assert(scopes[i].addr == other[i].addr);
+ }
+#endif
+
+ return scopes;
+}
+
+
int
dwflpp::global_alias_caching_callback(Dwarf_Die *die, void *arg)
{
@@ -1025,9 +1184,8 @@ dwflpp::iterate_over_labels (Dwarf_Die *begin_die,
Dwarf_Addr stmt_addr;
if (dwarf_lowpc (&die, &stmt_addr) == 0)
{
- Dwarf_Die *scopes;
- int nscopes = dwarf_getscopes_die (&die, &scopes);
- if (nscopes > 1)
+ vector<Dwarf_Die> scopes = getscopes_die(&die);
+ if (scopes.size() > 1)
callback(current_function, name, file, dline,
&scopes[1], stmt_addr, q);
}
@@ -1412,11 +1570,13 @@ dwflpp::loc2c_emit_address (void *arg, struct obstack *pool,
void
-dwflpp::print_locals(Dwarf_Die *die, ostream &o)
+dwflpp::print_locals(vector<Dwarf_Die>& scopes, ostream &o)
{
+ // XXX Shouldn't this be walking up to outer scopes too?
+
// Try to get the first child of die.
Dwarf_Die child;
- if (dwarf_child (die, &child) == 0)
+ if (dwarf_child (&scopes[0], &child) == 0)
{
do
{
@@ -1441,34 +1601,19 @@ dwflpp::print_locals(Dwarf_Die *die, ostream &o)
Dwarf_Attribute *
-dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die,
+dwflpp::find_variable_and_frame_base (vector<Dwarf_Die>& scopes,
Dwarf_Addr pc,
string const & local,
const target_symbol *e,
Dwarf_Die *vardie,
Dwarf_Attribute *fb_attr_mem)
{
- Dwarf_Die *scopes;
- int nscopes = 0;
+ Dwarf_Die *scope_die = &scopes[0];
Dwarf_Attribute *fb_attr = NULL;
assert (cu);
- nscopes = dwarf_getscopes_cached (pc, &scopes);
- if (nscopes <= 0)
- {
- throw semantic_error ("unable to find any scopes containing "
- + lex_cast_hex(pc)
- + ((scope_die == NULL) ? ""
- : (string (" in ")
- + (dwarf_diename(scope_die) ?: "<unknown>")
- + "(" + (dwarf_diename(cu) ?: "<unknown>")
- + ")"))
- + " while searching for local '" + local + "'",
- e->tok);
- }
-
- int declaring_scope = dwarf_getscopevar (scopes, nscopes,
+ int declaring_scope = dwarf_getscopevar (&scopes[0], scopes.size(),
local.c_str(),
0, NULL, 0, 0,
vardie);
@@ -1491,17 +1636,19 @@ dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die,
* as returned by dwarf_getscopes for the address, starting with the
* declaring_scope that the variable was found in.
*/
- for (int inner = declaring_scope;
- inner < nscopes && fb_attr == NULL;
+ vector<Dwarf_Die> physcopes, *fbscopes = &scopes;
+ for (size_t inner = declaring_scope;
+ inner < fbscopes->size() && fb_attr == NULL;
++inner)
{
- switch (dwarf_tag (&scopes[inner]))
+ Dwarf_Die& scope = (*fbscopes)[inner];
+ switch (dwarf_tag (&scope))
{
default:
continue;
case DW_TAG_subprogram:
case DW_TAG_entry_point:
- fb_attr = dwarf_attr_integrate (&scopes[inner],
+ fb_attr = dwarf_attr_integrate (&scope,
DW_AT_frame_base,
fb_attr_mem);
break;
@@ -1511,11 +1658,12 @@ dwflpp::find_variable_and_frame_base (Dwarf_Die *scope_die,
* subroutine is inlined to find the appropriate frame base. */
if (declaring_scope != -1)
{
- nscopes = dwarf_getscopes_die (&scopes[inner], &scopes);
- if (nscopes == -1)
+ physcopes = getscopes_die(&scope);
+ if (physcopes.empty())
throw semantic_error ("unable to get die scopes for '" +
local + "' in an inlined subroutines",
e->tok);
+ fbscopes = &physcopes;
inner = 0; // zero is current scope, for look will increase.
declaring_scope = -1;
}
@@ -2093,7 +2241,7 @@ dwflpp::express_as_string (string prelude,
string
-dwflpp::literal_stmt_for_local (Dwarf_Die *scope_die,
+dwflpp::literal_stmt_for_local (vector<Dwarf_Die>& scopes,
Dwarf_Addr pc,
string const & local,
const target_symbol *e,
@@ -2103,7 +2251,7 @@ dwflpp::literal_stmt_for_local (Dwarf_Die *scope_die,
Dwarf_Die vardie;
Dwarf_Attribute fb_attr_mem, *fb_attr = NULL;
- fb_attr = find_variable_and_frame_base (scope_die, pc, local, e,
+ fb_attr = find_variable_and_frame_base (scopes, pc, local, e,
&vardie, &fb_attr_mem);
if (sess.verbose>2)
@@ -2606,20 +2754,6 @@ dwflpp::literal_addr_to_sym_addr(Dwarf_Addr lit_addr)
return lit_addr;
}
-int
-dwflpp::dwarf_getscopes_cached (Dwarf_Addr pc, Dwarf_Die **scopes)
-{
- if (!cached_scopes || pc != pc_cached_scopes)
- {
- free(cached_scopes);
- cached_scopes = NULL;
- pc_cached_scopes = pc;
- num_cached_scopes = dwarf_getscopes(cu, pc, &cached_scopes);
- }
- *scopes = cached_scopes;
- return num_cached_scopes;
-}
-
/* Returns the call frame address operations for the given program counter
* in the libdw address space.
*/
diff --git a/dwflpp.h b/dwflpp.h
index 74a3ae00..d379b265 100644
--- a/dwflpp.h
+++ b/dwflpp.h
@@ -65,6 +65,12 @@ typedef unordered_map<void*, cu_function_cache_t*> mod_cu_function_cache_t;
// inline function die -> instance die[]
typedef unordered_map<void*, std::vector<Dwarf_Die>*> cu_inl_function_cache_t;
+// die -> parent die
+typedef unordered_map<void*, Dwarf_Die> cu_die_parent_cache_t;
+
+// cu die -> (die -> parent die)
+typedef unordered_map<void*, cu_die_parent_cache_t*> mod_cu_die_parent_cache_t;
+
typedef std::vector<func_info> func_info_map_t;
typedef std::vector<inline_instance_info> inline_instance_map_t;
@@ -201,6 +207,10 @@ struct dwflpp
void iterate_over_inline_instances (int (* callback)(Dwarf_Die * die, void * arg),
void * data);
+ std::vector<Dwarf_Die> getscopes_die(Dwarf_Die* die);
+ std::vector<Dwarf_Die> getscopes(Dwarf_Die* die);
+ std::vector<Dwarf_Die> getscopes(Dwarf_Addr pc);
+
Dwarf_Die *declaration_resolve(const char *name);
mod_cu_function_cache_t cu_function_cache;
@@ -245,7 +255,7 @@ struct dwflpp
bool die_has_pc (Dwarf_Die & die, Dwarf_Addr pc);
- std::string literal_stmt_for_local (Dwarf_Die *scope_die,
+ std::string literal_stmt_for_local (std::vector<Dwarf_Die>& scopes,
Dwarf_Addr pc,
std::string const & local,
const target_symbol *e,
@@ -292,6 +302,10 @@ private:
cu_inl_function_cache_t cu_inl_function_cache;
void cache_inline_instances (Dwarf_Die* die);
+ mod_cu_die_parent_cache_t cu_die_parent_cache;
+ void cache_die_parents(cu_die_parent_cache_t* parents, Dwarf_Die* die);
+ cu_die_parent_cache_t *get_die_parents();
+
/* The global alias cache is used to resolve any DIE found in a
* module that is stubbed out with DW_AT_declaration with a defining
* DIE found in a different module. The current assumption is that
@@ -317,10 +331,10 @@ private:
static void loc2c_emit_address (void *arg, struct obstack *pool,
Dwarf_Addr address);
- void print_locals(Dwarf_Die *die, std::ostream &o);
+ void print_locals(std::vector<Dwarf_Die>& scopes, std::ostream &o);
void print_members(Dwarf_Die *vardie, std::ostream &o);
- Dwarf_Attribute *find_variable_and_frame_base (Dwarf_Die *scope_die,
+ Dwarf_Attribute *find_variable_and_frame_base (std::vector<Dwarf_Die>& scopes,
Dwarf_Addr pc,
std::string const & local,
const target_symbol *e,
@@ -374,11 +388,6 @@ private:
void build_blacklist();
std::string get_blacklist_section(Dwarf_Addr addr);
- Dwarf_Addr pc_cached_scopes;
- int num_cached_scopes;
- Dwarf_Die *cached_scopes;
- int dwarf_getscopes_cached (Dwarf_Addr pc, Dwarf_Die **scopes);
-
// Returns the call frame address operations for the given program counter.
Dwarf_Op *get_cfa_ops (Dwarf_Addr pc);
diff --git a/tapsets.cxx b/tapsets.cxx
index 7912ed99..deb1e795 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -355,7 +355,7 @@ struct dwarf_derived_probe: public derived_probe
void emit_probe_local_init(translator_output * o);
string args;
- void saveargs(Dwarf_Die* scope_die);
+ void saveargs(dwarf_query& q, Dwarf_Die* scope_die);
void printargs(std::ostream &o) const;
// Pattern registration helpers.
@@ -401,7 +401,7 @@ struct uprobe_derived_probe: public derived_probe
bool return_p);
string args;
- void saveargs(Dwarf_Die* scope_die);
+ void saveargs(dwarf_query& q, Dwarf_Die* scope_die);
void printargs(std::ostream &o) const;
void printsig (std::ostream &o) const;
@@ -1761,7 +1761,8 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor
Dwarf_Addr addr;
block *add_block;
probe *add_probe;
- std::map<std::string, symbol *> return_ts_map;
+ map<std::string, symbol *> return_ts_map;
+ vector<Dwarf_Die> scopes;
bool visited;
dwarf_var_expanding_visitor(dwarf_query & q, Dwarf_Die *sd, Dwarf_Addr a):
@@ -1770,6 +1771,8 @@ struct dwarf_var_expanding_visitor: public var_expanding_visitor
void visit_target_symbol_context (target_symbol* e);
void visit_target_symbol (target_symbol* e);
void visit_cast_op (cast_op* e);
+private:
+ vector<Dwarf_Die>& getscopes(target_symbol *e);
};
@@ -2110,10 +2113,8 @@ dwarf_var_expanding_visitor::visit_target_symbol_saved_return (target_symbol* e)
void
dwarf_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
{
- Dwarf_Die *scopes;
- if (dwarf_getscopes_die (scope_die, &scopes) == 0)
+ if (null_die(scope_die))
return;
- auto_free free_scopes(scopes);
target_symbol *tsym = new target_symbol;
print_format* pf = new print_format;
@@ -2161,6 +2162,7 @@ dwarf_var_expanding_visitor::visit_target_symbol_context (target_symbol* e)
// non-.return probe: support $$parms, $$vars, $$locals
bool first = true;
Dwarf_Die result;
+ vector<Dwarf_Die> scopes = q.dw.getscopes_die(scope_die);
if (dwarf_child (&scopes[0], &result) == 0)
do
{
@@ -2282,7 +2284,7 @@ dwarf_var_expanding_visitor::visit_target_symbol (target_symbol *e)
}
else
{
- ec->code = q.dw.literal_stmt_for_local (scope_die,
+ ec->code = q.dw.literal_stmt_for_local (getscopes(e),
addr,
e->base_name.substr(1),
e,
@@ -2392,6 +2394,35 @@ dwarf_var_expanding_visitor::visit_cast_op (cast_op *e)
}
+vector<Dwarf_Die>&
+dwarf_var_expanding_visitor::getscopes(target_symbol *e)
+{
+ if (scopes.empty())
+ {
+ // If the address is at the beginning of the scope_die, we can do a fast
+ // getscopes from there. Otherwise we need to look it up by address.
+ Dwarf_Addr entrypc;
+ if (q.dw.die_entrypc(scope_die, &entrypc) && entrypc == addr)
+ scopes = q.dw.getscopes(scope_die);
+ else
+ scopes = q.dw.getscopes(addr);
+
+ if (scopes.empty())
+ throw semantic_error ("unable to find any scopes containing "
+ + lex_cast_hex(addr)
+ + ((scope_die == NULL) ? ""
+ : (string (" in ")
+ + (dwarf_diename(scope_die) ?: "<unknown>")
+ + "(" + (dwarf_diename(q.dw.cu) ?: "<unknown>")
+ + ")"))
+ + " while searching for local '"
+ + e->base_name.substr(1) + "'",
+ e->tok);
+ }
+ return scopes;
+}
+
+
struct dwarf_cast_query : public base_query
{
cast_op& e;
@@ -2745,7 +2776,7 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname,
// Save the local variables for listing mode
if (q.sess.listing_mode_vars)
- saveargs(scope_die);
+ saveargs(q, scope_die);
// Reset the sole element of the "locations" vector as a
// "reverse-engineered" form of the incoming (q.base_loc) probe
@@ -2812,12 +2843,10 @@ dwarf_derived_probe::dwarf_derived_probe(const string& funcname,
void
-dwarf_derived_probe::saveargs(Dwarf_Die* scope_die)
+dwarf_derived_probe::saveargs(dwarf_query& q, Dwarf_Die* scope_die)
{
- Dwarf_Die *scopes;
- if (!null_die(scope_die) && dwarf_getscopes_die (scope_die, &scopes) == 0)
+ if (null_die(scope_die))
return;
- auto_free free_scopes(scopes);
stringstream argstream;
string type_name;
@@ -2829,6 +2858,7 @@ dwarf_derived_probe::saveargs(Dwarf_Die* scope_die)
argstream << " $return:" << type_name;
Dwarf_Die arg;
+ vector<Dwarf_Die> scopes = q.dw.getscopes_die(scope_die);
if (dwarf_child (&scopes[0], &arg) == 0)
do
{
@@ -3822,8 +3852,7 @@ dwarf_builder::build(systemtap_session & sess,
symbol_table::~symbol_table()
{
- for (iterator_t i = map_by_addr.begin(); i != map_by_addr.end(); ++i)
- delete i->second;
+ delete_map(map_by_addr);
}
void
@@ -4232,7 +4261,7 @@ uprobe_derived_probe::uprobe_derived_probe (const string& function,
// Save the local variables for listing mode
if (q.sess.listing_mode_vars)
- saveargs(scope_die);
+ saveargs(q, scope_die);
// Reset the sole element of the "locations" vector as a
// "reverse-engineered" form of the incoming (q.base_loc) probe
@@ -4308,14 +4337,12 @@ uprobe_derived_probe::uprobe_derived_probe (probe *base,
void
-uprobe_derived_probe::saveargs(Dwarf_Die* scope_die)
+uprobe_derived_probe::saveargs(dwarf_query& q, Dwarf_Die* scope_die)
{
// same as dwarf_derived_probe::saveargs
- Dwarf_Die *scopes;
- if (!null_die(scope_die) && dwarf_getscopes_die (scope_die, &scopes) == 0)
+ if (null_die(scope_die))
return;
- auto_free free_scopes(scopes);
stringstream argstream;
string type_name;
@@ -4327,6 +4354,7 @@ uprobe_derived_probe::saveargs(Dwarf_Die* scope_die)
argstream << " $return:" << type_name;
Dwarf_Die arg;
+ vector<Dwarf_Die> scopes = q.dw.getscopes_die(scope_die);
if (dwarf_child (&scopes[0], &arg) == 0)
do
{
diff --git a/util.h b/util.h
index b38d01fd..24845545 100644
--- a/util.h
+++ b/util.h
@@ -97,4 +97,16 @@ lex_cast_qstring(std::string const & in)
return out;
}
+
+// Delete all values from a map-like container and clear it
+// (The template is permissive -- be good!)
+template <typename T>
+void delete_map(T& t)
+{
+ for (typename T::iterator i = t.begin(); i != t.end(); ++i)
+ delete i->second;
+ t.clear();
+}
+
+
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */