mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-07 13:33:06 +00:00
2638 lines
68 KiB
C++
2638 lines
68 KiB
C++
/* Copyright (C) 2021 Free Software Foundation, Inc.
|
|
Contributed by Oracle.
|
|
|
|
This file is part of GNU Binutils.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#include "config.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "util.h"
|
|
#include "DefaultMap.h"
|
|
#include "CacheMap.h"
|
|
|
|
#include "DbeSession.h"
|
|
#include "Application.h"
|
|
#include "CallStack.h"
|
|
#include "Emsg.h"
|
|
#include "Experiment.h"
|
|
#include "Expression.h"
|
|
#include "Function.h"
|
|
#include "Histable.h"
|
|
#include "IndexObject.h"
|
|
#include "MetricList.h"
|
|
#include "Module.h"
|
|
#include "DbeView.h"
|
|
#include "Metric.h"
|
|
#include "PathTree.h"
|
|
#include "LoadObject.h"
|
|
#include "Sample.h"
|
|
#include "StringBuilder.h"
|
|
#include "Table.h"
|
|
|
|
// Define counts, rate for error warnings for statistical profiles
|
|
#define MIN_PROF_CNT 100
|
|
#define MAX_PROF_RATE 1000.
|
|
|
|
#define NUM_DESCENDANTS(nd) ((nd)->descendants ? (nd)->descendants->size() : 0)
|
|
#define IS_LEAF(nd) ((nd)->descendants == NULL)
|
|
|
|
#ifdef DEBUG
|
|
#define DBG(__func) __func
|
|
#else
|
|
#define DBG(__func)
|
|
#endif
|
|
|
|
void
|
|
PathTree::construct (DbeView *_dbev, int _indxtype, PathTreeType _pathTreeType)
|
|
{
|
|
dbev = _dbev;
|
|
indxtype = _indxtype;
|
|
pathTreeType = _pathTreeType;
|
|
status = 0;
|
|
nchunks = 0;
|
|
chunks = NULL;
|
|
nodes = 1; // don't use node 0
|
|
nslots = 0;
|
|
slots = NULL;
|
|
root_idx = 0;
|
|
root = NULL;
|
|
depth = 1;
|
|
dnodes = 0;
|
|
phaseIdx = -1;
|
|
nexps = 0;
|
|
total_obj = NULL;
|
|
indx_expr = NULL;
|
|
statsq = NULL;
|
|
warningq = NULL;
|
|
cancel_ok = 1;
|
|
ptree_internal = NULL;
|
|
ftree_internal = NULL;
|
|
ftree_needs_update = false;
|
|
depth_map = NULL;
|
|
init ();
|
|
}
|
|
|
|
PathTree::~PathTree ()
|
|
{
|
|
fini ();
|
|
for (long i = 0; i < nchunks; i++)
|
|
delete[] chunks[i];
|
|
delete[] chunks;
|
|
}
|
|
|
|
void
|
|
PathTree::init ()
|
|
{
|
|
fn_map = new DefaultMap<Function*, NodeIdx>;
|
|
stack_prop = PROP_NONE;
|
|
desc_htable_size = 511;
|
|
desc_htable_nelem = 0;
|
|
descHT = new hash_node_t*[desc_htable_size];
|
|
for (int i = 0; i < desc_htable_size; i++)
|
|
descHT[i] = NULL;
|
|
pathMap = new CacheMap<uint64_t, NodeIdx>;
|
|
statsq = new Emsgqueue (NTXT ("statsq"));
|
|
warningq = new Emsgqueue (NTXT ("warningq"));
|
|
if (indxtype < 0)
|
|
{
|
|
Function *ftotal = dbeSession->get_Total_Function ();
|
|
if (pathTreeType == PATHTREE_INTERNAL_FUNCTREE)
|
|
total_obj = ftotal;
|
|
else
|
|
total_obj = ftotal->find_dbeinstr (0, 0);
|
|
VMode view_mode = dbev->get_view_mode ();
|
|
if (view_mode == VMODE_MACHINE)
|
|
stack_prop = PROP_MSTACK;
|
|
else if (view_mode == VMODE_EXPERT)
|
|
stack_prop = PROP_XSTACK;
|
|
else if (view_mode == VMODE_USER)
|
|
{
|
|
stack_prop = PROP_USTACK;
|
|
if (dbeSession->is_omp_available ()
|
|
&& pathTreeType == PATHTREE_INTERNAL_OMP)
|
|
stack_prop = PROP_XSTACK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
total_obj = new IndexObject (indxtype, (uint64_t) - 2);
|
|
total_obj->set_name (dbe_strdup (NTXT ("<Total>")));
|
|
char *idxname = dbeSession->getIndexSpaceName (indxtype);
|
|
if (streq (idxname, NTXT ("OMP_preg")))
|
|
stack_prop = PROP_CPRID;
|
|
else if (streq (idxname, NTXT ("OMP_task")))
|
|
stack_prop = PROP_TSKID;
|
|
else
|
|
indx_expr = dbeSession->getIndexSpaceExpr (indxtype);
|
|
}
|
|
root_idx = new_Node (0, total_obj, false);
|
|
root = NODE_IDX (root_idx);
|
|
}
|
|
|
|
void
|
|
PathTree::fini ()
|
|
{
|
|
// For each node free its descendants vector
|
|
// and reset the node list of its function
|
|
for (long i = 1; i < nodes; i++)
|
|
{
|
|
Node *node = NODE_IDX (i);
|
|
if (node->descendants)
|
|
delete node->descendants;
|
|
}
|
|
nodes = 1; // don't use node 0
|
|
|
|
for (int i = 0; i < nslots; i++)
|
|
{
|
|
int **tmp = slots[i].mvals;
|
|
for (long j = 0; j < nchunks; j++)
|
|
delete[] tmp[j];
|
|
delete[] tmp;
|
|
}
|
|
delete[] slots;
|
|
slots = NULL;
|
|
nslots = 0;
|
|
delete fn_map;
|
|
fn_map = NULL;
|
|
delete pathMap;
|
|
pathMap = NULL;
|
|
destroy (depth_map);
|
|
depth_map = NULL;
|
|
if (indxtype >= 0)
|
|
delete total_obj;
|
|
|
|
for (int i = 0; i < desc_htable_size; i++)
|
|
{
|
|
hash_node_t *p = descHT[i];
|
|
while (p)
|
|
{
|
|
hash_node_t *p1 = p;
|
|
p = p->next;
|
|
delete p1;
|
|
}
|
|
}
|
|
delete[] descHT;
|
|
delete statsq;
|
|
delete warningq;
|
|
depth = 1;
|
|
dnodes = 0;
|
|
phaseIdx = -1;
|
|
nexps = 0;
|
|
status = 0;
|
|
}
|
|
|
|
PtreePhaseStatus
|
|
PathTree::reset ()
|
|
{
|
|
if (pathTreeType == PATHTREE_INTERNAL_FUNCTREE)
|
|
return NORMAL; // never process reset for ftree_internal.
|
|
|
|
if (dbeSession->is_omp_available () && dbev->get_view_mode () == VMODE_USER
|
|
&& pathTreeType == PATHTREE_MAIN && ptree_internal == NULL)
|
|
ptree_internal = new PathTree (dbev, indxtype, PATHTREE_INTERNAL_OMP);
|
|
|
|
if (phaseIdx != dbev->getPhaseIdx ())
|
|
{
|
|
fini ();
|
|
init ();
|
|
phaseIdx = dbev->getPhaseIdx ();
|
|
ftree_needs_update = true;
|
|
}
|
|
for (; nexps < dbeSession->nexps (); nexps++)
|
|
{
|
|
ftree_needs_update = true;
|
|
if (add_experiment (nexps) == CANCELED)
|
|
return CANCELED;
|
|
}
|
|
|
|
// LIBRARY_VISIBILITY
|
|
if (dbev->isNewViewMode ())
|
|
dbev->resetNewViewMode ();
|
|
if (dbev->isShowHideChanged ())
|
|
dbev->resetShowHideChanged ();
|
|
return NORMAL;
|
|
}
|
|
|
|
int
|
|
PathTree::allocate_slot (int id, ValueTag vtype)
|
|
{
|
|
|
|
int i;
|
|
int slot_idx = find_slot (id);
|
|
if (slot_idx >= 0)
|
|
{
|
|
DBG (assert (slots[slot_idx].vtype == vtype));
|
|
return slot_idx;
|
|
}
|
|
slot_idx = nslots++;
|
|
|
|
Slot *old_slots = slots;
|
|
slots = new Slot[nslots];
|
|
for (i = 0; i < slot_idx; i++)
|
|
slots[i] = old_slots[i];
|
|
delete[] old_slots;
|
|
|
|
slots[slot_idx].id = id;
|
|
slots[slot_idx].vtype = vtype;
|
|
int **ip = new int*[nchunks];
|
|
for (i = 0; i < nchunks; i++)
|
|
ip[i] = NULL;
|
|
slots[slot_idx].mvals = ip;
|
|
|
|
return slot_idx;
|
|
}
|
|
|
|
void
|
|
PathTree::allocate_slots (Slot *new_slots, int new_nslots)
|
|
{
|
|
// duplicates new_slots
|
|
|
|
// if previously had more slots than currently requested, delete the data from those slots.
|
|
for (int i = new_nslots; i < nslots; i++)
|
|
{
|
|
int **tmp = slots[i].mvals;
|
|
for (long j = 0; j < nchunks; j++)
|
|
delete tmp[j];
|
|
delete tmp;
|
|
}
|
|
if (new_nslots == 0)
|
|
{
|
|
nslots = new_nslots;
|
|
delete[] slots;
|
|
slots = NULL;
|
|
return;
|
|
}
|
|
|
|
Slot *old_slots = slots;
|
|
slots = new Slot[new_nslots];
|
|
for (int i = 0; i < new_nslots; i++)
|
|
{
|
|
slots[i] = new_slots[i]; // pick up id and vtype
|
|
if (i < nslots)
|
|
slots[i].mvals = old_slots[i].mvals;
|
|
else
|
|
{
|
|
if (nchunks == 0)
|
|
slots[i].mvals = NULL;
|
|
else
|
|
{
|
|
int **ip = new int*[nchunks];
|
|
for (long j = 0; j < nchunks; j++)
|
|
ip[j] = NULL;
|
|
slots[i].mvals = ip;
|
|
}
|
|
}
|
|
}
|
|
nslots = new_nslots;
|
|
delete old_slots;
|
|
}
|
|
|
|
int
|
|
PathTree::find_slot (int id)
|
|
{
|
|
for (int i = 0; i < nslots; i++)
|
|
if (slots[i].id == id)
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
PathTree::NodeIdx
|
|
PathTree::new_Node (NodeIdx anc, Histable *instr, bool leaf)
|
|
{
|
|
if (nodes >= nchunks * CHUNKSZ)
|
|
{
|
|
long idx = nchunks++;
|
|
|
|
// Reallocate Node chunk array
|
|
Node **old_chunks = chunks;
|
|
chunks = new Node*[nchunks];
|
|
for (long k = 0; k < idx; k++)
|
|
chunks[k] = old_chunks[k];
|
|
delete[] old_chunks;
|
|
|
|
// Reallocate metric value chunk arrays.
|
|
for (int i = 0; i < nslots; i++)
|
|
{
|
|
int **mvals = new int*[nchunks];
|
|
for (long k = 0; k < idx; k++)
|
|
{
|
|
mvals[k] = slots[i].mvals[k];
|
|
}
|
|
delete[] slots[i].mvals;
|
|
slots[i].mvals = mvals;
|
|
slots[i].mvals[idx] = NULL;
|
|
}
|
|
|
|
// Allocate new chunk for nodes.
|
|
// Note that we don't need to allocate new chunks
|
|
// for metric values at this point as we rely on
|
|
// lazy allocation.
|
|
//
|
|
allocate_chunk (chunks, idx);
|
|
}
|
|
NodeIdx node_idx = nodes++;
|
|
Node *node = NODE_IDX (node_idx);
|
|
node->ancestor = anc;
|
|
node->descendants = leaf ? (Vector<NodeIdx>*)NULL : new Vector<NodeIdx>(2);
|
|
node->instr = instr;
|
|
Function *func = (Function*) (instr->convertto (Histable::FUNCTION));
|
|
node->funclist = fn_map->get (func);
|
|
fn_map->put (func, node_idx);
|
|
return node_idx;
|
|
}
|
|
|
|
PathTree::NodeIdx
|
|
PathTree::find_path (Experiment *exp, DataView *dview, long recIdx)
|
|
{
|
|
if (indx_expr != NULL)
|
|
{
|
|
Expression::Context ctx (dbev, exp, dview, recIdx);
|
|
uint64_t idx = indx_expr->eval (&ctx);
|
|
Histable *cur_obj = dbeSession->createIndexObject (indxtype, idx);
|
|
cur_obj->set_name_from_context (&ctx);
|
|
NodeIdx dsc_idx = find_in_desc_htable (root_idx, cur_obj, true);
|
|
depth = 2;
|
|
return dsc_idx;
|
|
}
|
|
|
|
bool showAll = dbev->isShowAll ();
|
|
int t_stack_prop = stack_prop;
|
|
void *stackId = dview->getObjValue (t_stack_prop, recIdx);
|
|
NodeIdx node_idx;
|
|
if (stackId != NULL)
|
|
{
|
|
// pathMap does not work with NULL key
|
|
node_idx = pathMap->get ((uint64_t) stackId);
|
|
if (node_idx != 0)
|
|
return node_idx;
|
|
}
|
|
Vector<Histable*> *stack = (Vector<Histable*>*)CallStack::getStackPCs (stackId, !showAll);
|
|
int stack_size = stack->size ();
|
|
if (stack_size == 0)
|
|
return root_idx;
|
|
|
|
node_idx = root_idx;
|
|
int thisdepth = 1;
|
|
|
|
for (int i = stack_size - 1; i >= 0; i--)
|
|
{
|
|
bool leaf = (i == 0);
|
|
Histable *cur_addr = stack->fetch (i);
|
|
|
|
// bail out of loop if load object API-only is set
|
|
// and this is not the top frame
|
|
// This is now done in HSTACK if hide is set
|
|
|
|
Function *func = (Function*) cur_addr->convertto (Histable::FUNCTION);
|
|
if (func != NULL)
|
|
{
|
|
Module *mod = func->module;
|
|
LoadObject *lo = mod->loadobject;
|
|
int segx = lo->seg_idx;
|
|
if (showAll && dbev->get_lo_expand (segx) == LIBEX_API
|
|
&& i != stack_size - 1)
|
|
leaf = true;
|
|
}
|
|
|
|
NodeIdx dsc_idx = find_desc_node (node_idx, cur_addr, leaf);
|
|
thisdepth++;
|
|
node_idx = dsc_idx;
|
|
|
|
// LIBEX_API processing might have set leaf to true
|
|
if (leaf)
|
|
break;
|
|
}
|
|
if (thisdepth > depth)
|
|
depth = thisdepth;
|
|
delete stack;
|
|
pathMap->put ((uint64_t) stackId, node_idx);
|
|
return node_idx;
|
|
}
|
|
|
|
static int
|
|
desc_node_comp (const void *s1, const void *s2, const void *ptree)
|
|
{
|
|
PathTree::NodeIdx t1, t2;
|
|
t1 = *(PathTree::NodeIdx *)s1;
|
|
t2 = *(PathTree::NodeIdx *)s2;
|
|
PathTree* Ptree = (PathTree *) ptree;
|
|
PathTree::Node *n1 = Ptree->NODE_IDX (t1);
|
|
PathTree::Node *n2 = Ptree->NODE_IDX (t2);
|
|
Histable *d1 = n1->instr;
|
|
Histable *d2 = n2->instr;
|
|
if (d1->id < d2->id)
|
|
return -1;
|
|
else if (d1->id > d2->id)
|
|
return +1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
PathTree::NodeIdx
|
|
PathTree::find_in_desc_htable (NodeIdx node_idx, Histable *instr, bool leaf)
|
|
{
|
|
unsigned int hash_code = (unsigned int) instr->id % desc_htable_size;
|
|
Node *node = NODE_IDX (node_idx);
|
|
hash_node_t *p = NULL;
|
|
for (p = descHT[hash_code]; p; p = p->next)
|
|
{
|
|
Node *dsc = NODE_IDX (p->nd);
|
|
Histable *dinstr = dsc->instr;
|
|
if (dinstr->id == instr->id && leaf == IS_LEAF (dsc))
|
|
return p->nd;
|
|
}
|
|
// Not found
|
|
NodeIdx dsc_idx = new_Node (node_idx, instr, leaf);
|
|
node->descendants->append (dsc_idx);
|
|
p = new hash_node_t ();
|
|
p->nd = dsc_idx;
|
|
p->next = descHT[hash_code];
|
|
descHT[hash_code] = p;
|
|
desc_htable_nelem++;
|
|
|
|
// time to resize
|
|
if (desc_htable_nelem == desc_htable_size)
|
|
{
|
|
int old_htable_size = desc_htable_size;
|
|
desc_htable_size = old_htable_size * 2 + 1;
|
|
hash_node_t **old_htable = descHT;
|
|
descHT = new hash_node_t*[desc_htable_size];
|
|
for (int i = 0; i < desc_htable_size; i++)
|
|
descHT[i] = NULL;
|
|
|
|
for (int i = 0; i < old_htable_size; i++)
|
|
if (old_htable[i] != NULL)
|
|
{
|
|
hash_node *old_p;
|
|
hash_node_t *hash_p = old_htable[i];
|
|
while (hash_p != NULL)
|
|
{
|
|
hash_node_t *new_p = new hash_node_t ();
|
|
new_p->nd = hash_p->nd;
|
|
Node *dnode = NODE_IDX (hash_p->nd);
|
|
Histable *dnode_instr = dnode->instr;
|
|
hash_code = (unsigned int) dnode_instr->id % desc_htable_size;
|
|
new_p->next = descHT[hash_code];
|
|
descHT[hash_code] = new_p;
|
|
old_p = hash_p;
|
|
hash_p = hash_p->next;
|
|
delete old_p;
|
|
}
|
|
}
|
|
delete[] old_htable;
|
|
}
|
|
return dsc_idx;
|
|
}
|
|
|
|
PathTree::NodeIdx
|
|
PathTree::find_desc_node (NodeIdx node_idx, Histable *instr, bool leaf)
|
|
{
|
|
// Binary search. All nodes are ordered by Histable::id.
|
|
|
|
// We have a special case when two nodes with the same
|
|
// id value may co-exist: one representing a leaf node and
|
|
// another one representing a call site.
|
|
Node *node = NODE_IDX (node_idx);
|
|
int left = 0;
|
|
int right = NUM_DESCENDANTS (node) - 1;
|
|
while (left <= right)
|
|
{
|
|
int index = (left + right) / 2;
|
|
NodeIdx dsc_idx = node->descendants->fetch (index);
|
|
Node *dsc = NODE_IDX (dsc_idx);
|
|
Histable *dinstr = dsc->instr;
|
|
if (instr->id < dinstr->id)
|
|
right = index - 1;
|
|
else if (instr->id > dinstr->id)
|
|
left = index + 1;
|
|
else if (leaf == IS_LEAF (dsc))
|
|
return dsc_idx;
|
|
else if (leaf)
|
|
right = index - 1;
|
|
else
|
|
left = index + 1;
|
|
}
|
|
|
|
// None was found. Create one.
|
|
NodeIdx dsc_idx = new_Node (node_idx, instr, leaf);
|
|
node->descendants->insert (left, dsc_idx);
|
|
return dsc_idx;
|
|
}
|
|
|
|
PtreePhaseStatus
|
|
PathTree::process_packets (Experiment *exp, DataView *packets, int data_type)
|
|
{
|
|
Expression::Context ctx (dbev, exp);
|
|
char *progress_bar_msg = NULL;
|
|
int progress_bar_percent = -1;
|
|
|
|
Vector<BaseMetric*> *mlist = dbev->get_all_reg_metrics ();
|
|
Vector<BaseMetric*> mlist2;
|
|
StringBuilder stb;
|
|
for (int midx = 0, mlist_sz = mlist->size (); midx < mlist_sz; ++midx)
|
|
{
|
|
BaseMetric *mtr = mlist->fetch (midx);
|
|
if (mtr->get_packet_type () == data_type &&
|
|
(mtr->get_expr () == NULL || mtr->get_expr ()->passes (&ctx)))
|
|
{
|
|
Hwcentry *hwc = mtr->get_hw_ctr ();
|
|
if (hwc)
|
|
{
|
|
stb.setLength (0);
|
|
// XXX this should be done at metric registration
|
|
Collection_params *col_params = exp->get_params ();
|
|
for (int i = 0; i < MAX_HWCOUNT; i++)
|
|
{
|
|
// We may have duplicate counters in col_params,
|
|
// check for all (see 5081284).
|
|
if (dbe_strcmp (hwc->name, col_params->hw_aux_name[i]) == 0)
|
|
{
|
|
if (stb.length () != 0)
|
|
stb.append (NTXT ("||"));
|
|
stb.append (NTXT ("HWCTAG=="));
|
|
stb.append (i);
|
|
}
|
|
}
|
|
if (stb.length () == 0)
|
|
continue;
|
|
stb.append (NTXT ("&& ((HWCINT & "));
|
|
stb.append ((long long) HWCVAL_ERR_FLAG);
|
|
stb.append (NTXT (")==0)"));
|
|
char *s = stb.toString ();
|
|
mtr->set_cond_spec (s);
|
|
free (s);
|
|
}
|
|
ValueTag vtype = mtr->get_vtype ();
|
|
switch (vtype)
|
|
{
|
|
case VT_INT:
|
|
case VT_ULLONG:
|
|
case VT_LLONG:
|
|
break; // nothing to do
|
|
default:
|
|
vtype = VT_ULLONG; // ym: not sure when this would happen
|
|
break;
|
|
}
|
|
allocate_slot (mtr->get_id (), vtype);
|
|
mlist2.append (mtr);
|
|
}
|
|
}
|
|
|
|
Slot **mslots = new Slot*[mlist2.size ()];
|
|
for (int midx = 0, mlist_sz = mlist2.size (); midx < mlist_sz; ++midx)
|
|
{
|
|
BaseMetric *mtr = mlist2.fetch (midx);
|
|
int id = mtr->get_id ();
|
|
int slot_ind = find_slot (id);
|
|
mslots[midx] = SLOT_IDX (slot_ind);
|
|
}
|
|
|
|
for (long i = 0, packets_sz = packets->getSize (); i < packets_sz; ++i)
|
|
{
|
|
if (dbeSession->is_interactive ())
|
|
{
|
|
if (NULL == progress_bar_msg)
|
|
progress_bar_msg = dbe_sprintf (GTXT ("Processing Experiment: %s"),
|
|
get_basename (exp->get_expt_name ()));
|
|
int val = (int) (100 * i / packets_sz);
|
|
if (val > progress_bar_percent)
|
|
{
|
|
progress_bar_percent += 10;
|
|
if (theApplication->set_progress (val, progress_bar_msg)
|
|
&& cancel_ok)
|
|
{
|
|
delete[] mslots;
|
|
return CANCELED;
|
|
}
|
|
}
|
|
}
|
|
|
|
NodeIdx path_idx = 0;
|
|
ctx.put (packets, i);
|
|
|
|
for (int midx = 0, mlist_sz = mlist2.size (); midx < mlist_sz; ++midx)
|
|
{
|
|
BaseMetric *mtr = mlist2.fetch (midx);
|
|
if (mtr->get_cond () != NULL && !mtr->get_cond ()->passes (&ctx))
|
|
continue;
|
|
|
|
int64_t mval = mtr->get_val ()->eval (&ctx);
|
|
if (mval == 0)
|
|
continue;
|
|
if (path_idx == 0)
|
|
path_idx = find_path (exp, packets, i);
|
|
NodeIdx node_idx = path_idx;
|
|
Slot *mslot = mslots[midx];
|
|
while (node_idx)
|
|
{
|
|
INCREMENT_METRIC (mslot, node_idx, mval);
|
|
node_idx = NODE_IDX (node_idx)->ancestor;
|
|
}
|
|
}
|
|
}
|
|
if (dbeSession->is_interactive ())
|
|
free (progress_bar_msg);
|
|
delete[] mslots;
|
|
if (indx_expr != NULL)
|
|
root->descendants->sort ((CompareFunc) desc_node_comp, this);
|
|
return NORMAL;
|
|
}
|
|
|
|
DataView *
|
|
PathTree::get_filtered_events (int exp_index, int data_type)
|
|
{
|
|
if (indx_expr != NULL)
|
|
{
|
|
IndexObjType_t *indexObj = dbeSession->getIndexSpace (indxtype);
|
|
if (indexObj->memObj && data_type != DATA_HWC)
|
|
return NULL;
|
|
}
|
|
return dbev->get_filtered_events (exp_index, data_type);
|
|
}
|
|
|
|
PtreePhaseStatus
|
|
PathTree::add_experiment (int exp_index)
|
|
{
|
|
StringBuilder sb;
|
|
char *expt_name;
|
|
char *base_name;
|
|
Emsg *m;
|
|
Experiment *experiment = dbeSession->get_exp (exp_index);
|
|
if (experiment->broken != 0)
|
|
return NORMAL;
|
|
status = 0;
|
|
expt_name = experiment->get_expt_name ();
|
|
base_name = get_basename (expt_name);
|
|
|
|
hrtime_t starttime = gethrtime ();
|
|
hrtime_t startvtime = gethrvtime ();
|
|
|
|
// Experiment::getEndTime was initially implemented as
|
|
// returning exp->last_event. To preserve the semantics
|
|
// new Experiment::getLastEvent() is used here.
|
|
hrtime_t tot_time = experiment->getLastEvent () - experiment->getStartTime ();
|
|
|
|
if (!dbev->isShowAll () && (dbev->isShowHideChanged ()
|
|
|| dbev->isNewViewMode ()))
|
|
experiment->resetShowHideStack ();
|
|
|
|
// To report experiment index to the user,
|
|
// start numeration from 1, not 0
|
|
sb.sprintf (GTXT ("PathTree processing experiment %d (`%s'); duration %lld.%06lld"),
|
|
exp_index + 1, base_name,
|
|
tot_time / NANOSEC, (tot_time % NANOSEC / 1000));
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
|
|
DataView *prof_packet = get_filtered_events (exp_index, DATA_CLOCK);
|
|
if (prof_packet && prof_packet->getSize () > 0)
|
|
{
|
|
if (process_packets (experiment, prof_packet, DATA_CLOCK) == CANCELED)
|
|
return CANCELED;
|
|
long clock_cnt = prof_packet->getSize ();
|
|
double clock_rate;
|
|
if (tot_time != 0)
|
|
clock_rate = (double) clock_cnt / (double) tot_time * (double) NANOSEC;
|
|
else
|
|
clock_rate = (double) 0.;
|
|
if (experiment->timelineavail)
|
|
sb.sprintf (GTXT (" Processed %ld clock-profile events (%3.2f/sec.)"),
|
|
clock_cnt, clock_rate);
|
|
else
|
|
sb.sprintf (GTXT (" Processed %ld clock-profile events"), clock_cnt);
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
|
|
// check for statistical validity
|
|
if ((experiment->timelineavail == true)
|
|
&& !dbev->get_filter_active () && (clock_cnt < MIN_PROF_CNT))
|
|
{
|
|
sb.sprintf (GTXT ("WARNING: too few clock-profile events (%ld) in experiment %d (`%s') for statistical validity"),
|
|
clock_cnt, exp_index + 1, base_name);
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
}
|
|
}
|
|
|
|
DataView *sync_packet = get_filtered_events (exp_index, DATA_SYNCH);
|
|
if (sync_packet && sync_packet->getSize () > 0)
|
|
{
|
|
if (process_packets (experiment, sync_packet, DATA_SYNCH) == CANCELED)
|
|
return CANCELED;
|
|
long sync_cnt = sync_packet->getSize ();
|
|
sb.sprintf (GTXT (" Processed %ld synctrace events"), sync_cnt);
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
}
|
|
|
|
DataView *iotrace_packet = get_filtered_events (exp_index, DATA_IOTRACE);
|
|
if (iotrace_packet && iotrace_packet->getSize () > 0)
|
|
{
|
|
if (process_packets (experiment, iotrace_packet, DATA_IOTRACE) == CANCELED)
|
|
return CANCELED;
|
|
long iotrace_cnt = iotrace_packet->getSize ();
|
|
sb.sprintf (GTXT (" Processed %ld IO trace events"), iotrace_cnt);
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
}
|
|
|
|
DataView *hwc_packet = get_filtered_events (exp_index, DATA_HWC);
|
|
if (hwc_packet && hwc_packet->getSize () > 0)
|
|
{
|
|
if (process_packets (experiment, hwc_packet, DATA_HWC) == CANCELED)
|
|
return CANCELED;
|
|
long hwc_cnt = hwc_packet->getSize ();
|
|
double hwc_rate = (double) hwc_cnt / (double) tot_time * (double) NANOSEC;
|
|
if (experiment->timelineavail)
|
|
sb.sprintf (GTXT (" Processed %ld hwc-profile events (%3.2f/sec.)"),
|
|
hwc_cnt, hwc_rate);
|
|
else
|
|
sb.sprintf (GTXT (" Processed %ld hwc-profile events"), hwc_cnt);
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
|
|
// check for statistical validity
|
|
if (experiment->timelineavail && !dbev->get_filter_active () && (hwc_cnt < MIN_PROF_CNT))
|
|
{
|
|
sb.sprintf (GTXT ("WARNING: too few HW counter profile events (%ld) in experiment %d (`%s') for statistical validity"),
|
|
hwc_cnt, exp_index + 1, base_name);
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
}
|
|
}
|
|
|
|
DataView *heap_packet = get_filtered_events (exp_index, DATA_HEAP);
|
|
if (heap_packet && heap_packet->getSize () > 0)
|
|
{
|
|
if (process_packets (experiment, heap_packet, DATA_HEAP) == CANCELED)
|
|
return CANCELED;
|
|
long heap_cnt = heap_packet->getSize ();
|
|
sb.sprintf (GTXT (" Processed %ld heaptrace events"), heap_cnt);
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
}
|
|
|
|
DataView *race_packet = get_filtered_events (exp_index, DATA_RACE);
|
|
if (race_packet && race_packet->getSize () > 0)
|
|
{
|
|
if (process_packets (experiment, race_packet, DATA_RACE) == CANCELED)
|
|
return CANCELED;
|
|
long race_cnt = race_packet->getSize ();
|
|
sb.sprintf (GTXT (" Processed %ld race access events"), race_cnt);
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
}
|
|
|
|
DataView *deadlock_packet = get_filtered_events (exp_index, DATA_DLCK);
|
|
if (deadlock_packet && deadlock_packet->getSize () > 0)
|
|
{
|
|
if (process_packets (experiment, deadlock_packet, DATA_DLCK) == CANCELED)
|
|
return CANCELED;
|
|
long race_cnt = deadlock_packet->getSize ();
|
|
sb.sprintf (GTXT (" Processed %ld race access events"), race_cnt);
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
}
|
|
|
|
hrtime_t pathtime = gethrtime () - starttime;
|
|
hrtime_t pathvtime = gethrvtime () - startvtime;
|
|
sb.sprintf (GTXT ("PathTree time = %lld.%06lld CPU-time %lld.%06lld\n"),
|
|
pathtime / NANOSEC, (pathtime % NANOSEC) / 1000,
|
|
pathvtime / NANOSEC, (pathvtime % NANOSEC) / 1000);
|
|
m = new Emsg (CMSG_COMMENT, sb);
|
|
statsq->append (m);
|
|
return NORMAL;
|
|
}
|
|
|
|
Hist_data *
|
|
PathTree::compute_metrics (MetricList *mlist, Histable::Type type,
|
|
Hist_data::Mode mode, Vector<Histable*> *objs,
|
|
Histable *context, Vector<Histable*> *sel_objs,
|
|
PtreeComputeOption computeOpt)
|
|
{
|
|
VMode view_mode = dbev->get_view_mode ();
|
|
|
|
// For displaying disassembly correctly in user mode with openmp
|
|
if (ptree_internal != NULL &&
|
|
(view_mode == VMODE_EXPERT ||
|
|
(view_mode == VMODE_USER && (type == Histable::INSTR
|
|
|| (dbev->isOmpDisMode ()
|
|
&& type == Histable::FUNCTION
|
|
&& mode == Hist_data::CALLEES
|
|
&& computeOpt == COMPUTEOPT_OMP_CALLEE))
|
|
)))
|
|
return ptree_internal->compute_metrics (mlist, type, mode, objs, context,
|
|
sel_objs);
|
|
|
|
PtreePhaseStatus resetStatus = reset ();
|
|
|
|
hist_data = new Hist_data (mlist, type, mode);
|
|
int nmetrics = mlist->get_items ()->size ();
|
|
int sort_ind = -1;
|
|
Hist_data::HistItem *hi;
|
|
int index;
|
|
|
|
if (status != 0 || resetStatus == CANCELED)
|
|
return hist_data;
|
|
|
|
hist_data->set_status (Hist_data::SUCCESS);
|
|
if (dbeSession->is_interactive () && mode != Hist_data::CALLEES)
|
|
theApplication->set_progress (0, GTXT ("Constructing Metrics"));
|
|
|
|
xlate = new int[nmetrics];
|
|
for (int mind = 0; mind < nmetrics; mind++)
|
|
{
|
|
Metric *mtr = mlist->get (mind);
|
|
xlate[mind] = find_slot (mtr->get_id ());
|
|
}
|
|
|
|
// Compute dynamic metrics
|
|
obj_list = new Histable*[depth];
|
|
if ((type == Histable::LINE || type == Histable::INSTR)
|
|
&& mode == Hist_data::CALLERS)
|
|
node_list = new Node*[depth];
|
|
percent = 0;
|
|
ndone = 0;
|
|
if (mode == Hist_data::MODL)
|
|
{
|
|
Histable *obj = objs && objs->size () > 0 ? objs->fetch (0) : NULL;
|
|
if (obj != NULL)
|
|
{
|
|
switch (obj->get_type ())
|
|
{
|
|
case Histable::FUNCTION:
|
|
{
|
|
Vector<Function*> *funclist = new Vector<Function*>;
|
|
funclist->append ((Function*) obj);
|
|
get_metrics (funclist, context);
|
|
delete funclist;
|
|
break;
|
|
}
|
|
case Histable::MODULE:
|
|
{
|
|
Vector<Histable*> *comparableModules = obj->get_comparable_objs ();
|
|
if (comparableModules != NULL)
|
|
{
|
|
Vector<Function*> *functions = new Vector<Function*>;
|
|
for (int i = 0; i < comparableModules->size (); i++)
|
|
{
|
|
Module *mod = (Module*) comparableModules->fetch (i);
|
|
if (mod)
|
|
{
|
|
bool found = false;
|
|
for (int i1 = 0; i1 < i; i1++)
|
|
{
|
|
if (mod == comparableModules->fetch (i1))
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found)
|
|
functions->addAll (mod->functions);
|
|
}
|
|
}
|
|
get_metrics (functions, context);
|
|
delete functions;
|
|
}
|
|
else
|
|
get_metrics (((Module*) obj)->functions, context);
|
|
break;
|
|
}
|
|
case Histable::SOURCEFILE:
|
|
get_metrics (((SourceFile *) obj)->get_functions (), context);
|
|
break;
|
|
default:
|
|
DBG (assert (0));
|
|
}
|
|
}
|
|
}
|
|
else if (mode == Hist_data::CALLERS)
|
|
{
|
|
if (objs && objs->size () > 0)
|
|
get_clr_metrics (objs);
|
|
}
|
|
else if (mode == Hist_data::CALLEES)
|
|
{
|
|
if (objs && objs->size () > 0)
|
|
get_cle_metrics (objs);
|
|
else // Special case: get root
|
|
get_cle_metrics (NULL);
|
|
}
|
|
else if (mode == Hist_data::SELF)
|
|
{
|
|
if (objs->size () == 1)
|
|
{
|
|
Histable *obj = objs->fetch (0);
|
|
if (obj != NULL)
|
|
{
|
|
if (obj->get_type () == Histable::LINE)
|
|
{
|
|
Vector<Function*> *funclist = new Vector<Function*>;
|
|
for (DbeLine *dl = (DbeLine*) obj->convertto (Histable::LINE);
|
|
dl; dl = dl->dbeline_func_next)
|
|
if (dl->func)
|
|
funclist->append (dl->func);
|
|
|
|
get_self_metrics (obj, funclist, sel_objs);
|
|
delete funclist;
|
|
}
|
|
else if (obj->get_type () == Histable::FUNCTION
|
|
|| obj->get_type () == Histable::INSTR)
|
|
{
|
|
// Use shortcut for functions and oth.
|
|
if (context)
|
|
{
|
|
Vector<Function*> *funclist = NULL;
|
|
if (context->get_type () == Histable::MODULE)
|
|
funclist = ((Module*) context)->functions->copy ();
|
|
else
|
|
{
|
|
funclist = new Vector<Function*>;
|
|
funclist->append ((Function*) context);
|
|
}
|
|
get_self_metrics (obj, funclist, sel_objs);
|
|
delete funclist;
|
|
}
|
|
else
|
|
get_self_metrics (objs);
|
|
}
|
|
else
|
|
get_self_metrics (objs);
|
|
}
|
|
}
|
|
else
|
|
get_self_metrics (objs);
|
|
}
|
|
else // Hist_data::ALL
|
|
get_metrics (root_idx, 0);
|
|
|
|
delete[] obj_list;
|
|
if ((type == Histable::LINE || type == Histable::INSTR)
|
|
&& mode == Hist_data::CALLERS)
|
|
delete[] node_list;
|
|
|
|
// Postprocess; find total
|
|
for (long mind = 0, sz = mlist->get_items ()->size (); mind < sz; mind++)
|
|
{
|
|
Metric *mtr = mlist->get_items ()->get (mind);
|
|
Metric::SubType subtype = mtr->get_subtype ();
|
|
ValueTag vtype = mtr->get_vtype ();
|
|
hist_data->total->value[mind].tag = vtype;
|
|
|
|
switch (vtype)
|
|
{
|
|
// ignoring the following cases (why?)
|
|
case VT_SHORT:
|
|
case VT_FLOAT:
|
|
case VT_HRTIME:
|
|
case VT_LABEL:
|
|
case VT_ADDRESS:
|
|
case VT_OFFSET:
|
|
break;
|
|
|
|
case VT_INT:
|
|
// Calculate total as the sum of all values in hist_data for
|
|
// ATTRIBUTED metrics only. For all others, use root node values.
|
|
//
|
|
if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES)
|
|
&& subtype == Metric::ATTRIBUTED)
|
|
{
|
|
hist_data->total->value[mind].i = 0;
|
|
Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi)
|
|
{
|
|
hist_data->total->value[mind].i += hi->value[mind].i;
|
|
}
|
|
if (mode == Hist_data::CALLEES)
|
|
hist_data->total->value[mind].i += hist_data->gprof_item->value[mind].i;
|
|
}
|
|
else if (xlate[mind] != -1)
|
|
ASN_METRIC_VAL (hist_data->total->value[mind], slots[xlate[mind]],
|
|
root_idx);
|
|
break;
|
|
|
|
case VT_LLONG:
|
|
Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi)
|
|
{
|
|
hi->value[mind].tag = vtype;
|
|
}
|
|
|
|
if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES)
|
|
&& subtype == Metric::ATTRIBUTED)
|
|
{
|
|
hist_data->total->value[mind].ll = 0;
|
|
Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi)
|
|
{
|
|
hist_data->total->value[mind].ll += hi->value[mind].ll;
|
|
}
|
|
if (mode == Hist_data::CALLEES)
|
|
hist_data->total->value[mind].ll += hist_data->gprof_item->value[mind].ll;
|
|
}
|
|
else if (xlate[mind] != -1)
|
|
ASN_METRIC_VAL (hist_data->total->value[mind], slots[xlate[mind]], root_idx);
|
|
break;
|
|
|
|
case VT_ULLONG:
|
|
Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi)
|
|
{
|
|
hi->value[mind].tag = vtype;
|
|
}
|
|
if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES)
|
|
&& subtype == Metric::ATTRIBUTED)
|
|
{
|
|
hist_data->total->value[mind].ull = 0;
|
|
Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi)
|
|
{
|
|
hist_data->total->value[mind].ull += hi->value[mind].ull;
|
|
}
|
|
if (mode == Hist_data::CALLEES)
|
|
hist_data->total->value[mind].ull += hist_data->gprof_item->value[mind].ull;
|
|
}
|
|
else if (xlate[mind] != -1)
|
|
ASN_METRIC_VAL (hist_data->total->value[mind], slots[xlate[mind]], root_idx);
|
|
break;
|
|
|
|
case VT_DOUBLE:
|
|
double prec = mtr->get_precision ();
|
|
ValueTag vt = (xlate[mind] != -1) ? slots[xlate[mind]].vtype : VT_INT;
|
|
Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi)
|
|
{
|
|
double val = (vt == VT_LLONG ? hi->value[mind].ll :
|
|
(vt == VT_ULLONG ? hi->value[mind].ull
|
|
: hi->value[mind].i));
|
|
hi->value[mind].tag = vtype;
|
|
hi->value[mind].d = val / prec;
|
|
}
|
|
|
|
if ((mode == Hist_data::CALLERS || mode == Hist_data::CALLEES)
|
|
&& subtype == Metric::ATTRIBUTED)
|
|
{
|
|
hist_data->total->value[mind].d = 0.0;
|
|
Vec_loop (Hist_data::HistItem*, hist_data->hist_items, index, hi)
|
|
{
|
|
hist_data->total->value[mind].d += hi->value[mind].d;
|
|
}
|
|
if (mode == Hist_data::CALLEES)
|
|
hist_data->total->value[mind].d +=
|
|
(double) (vt == VT_LLONG ? hist_data->gprof_item->value[mind].ll :
|
|
(vt == VT_ULLONG ? hist_data->gprof_item->value[mind].ull :
|
|
hist_data->gprof_item->value[mind].i)) / prec;
|
|
}
|
|
else if (xlate[mind] != -1)
|
|
{
|
|
TValue& total = hist_data->total->value[mind];
|
|
ASN_METRIC_VAL (total, slots[xlate[mind]], root_idx);
|
|
double val = (vt == VT_LLONG ? total.ll :
|
|
(vt == VT_ULLONG ? total.ll : total.i));
|
|
total.d = val / prec;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
delete[] xlate;
|
|
|
|
// Determine by which metric to sort if any
|
|
bool rev_sort = mlist->get_sort_rev ();
|
|
for (long mind = 0, sz = mlist->get_items ()->size (); mind < sz; mind++)
|
|
{
|
|
Metric *mtr = mlist->get_items ()->get (mind);
|
|
if (mlist->get_sort_ref_index () == mind)
|
|
sort_ind = mind;
|
|
|
|
switch (mtr->get_type ())
|
|
{
|
|
case BaseMetric::SIZES:
|
|
Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi)
|
|
{
|
|
Histable *h = mtr->get_comparable_obj (hi->obj);
|
|
hi->value[mind].tag = VT_LLONG;
|
|
hi->value[mind].ll = h ? h->get_size () : 0;
|
|
}
|
|
break;
|
|
case BaseMetric::ADDRESS:
|
|
Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi)
|
|
{
|
|
Histable *h = mtr->get_comparable_obj (hi->obj);
|
|
hi->value[mind].tag = VT_ADDRESS;
|
|
hi->value[mind].ll = h ? h->get_addr () : 0;
|
|
}
|
|
break;
|
|
case BaseMetric::DERIVED:
|
|
{
|
|
Definition *def = mtr->get_definition ();
|
|
long *map = def->get_map ();
|
|
for (long i1 = 0, sz1 = hist_data->hist_items->size (); i1 < sz1; i1++)
|
|
{
|
|
/* Hist_data::HistItem * */hi = hist_data->hist_items->get (i1);
|
|
hi->value[mind].tag = VT_DOUBLE;
|
|
hi->value[mind].d = def->eval (map, hi->value);
|
|
}
|
|
hist_data->total->value[mind].tag = VT_DOUBLE;
|
|
hist_data->total->value[mind].d = def->eval (map, hist_data->total->value);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
hist_data->sort (sort_ind, rev_sort);
|
|
hist_data->compute_minmax ();
|
|
if (dbeSession->is_interactive () && mode != Hist_data::CALLERS)
|
|
theApplication->set_progress (0, GTXT (""));
|
|
|
|
#if DEBUG_FTREE
|
|
if (ftree_hist_data)
|
|
{
|
|
bool matches = ftree_debug_match_hist_data (hist_data, ftree_hist_data);
|
|
if (!matches)
|
|
assert (false);
|
|
delete hist_data;
|
|
hist_data = ftree_hist_data; // return the debug version
|
|
}
|
|
#endif
|
|
return hist_data;
|
|
}
|
|
|
|
#if DEBUG_FTREE
|
|
bool
|
|
PathTree::ftree_debug_match_hist_data (Hist_data *data /* ref */,
|
|
Hist_data *data_tmp)
|
|
{
|
|
if (data->get_status () != Hist_data::SUCCESS)
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
if (data == NULL && data != data_tmp)
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
|
|
MetricList *mlist;
|
|
mlist = data->get_metric_list ();
|
|
MetricList *mlist_tmp;
|
|
mlist_tmp = data_tmp->get_metric_list ();
|
|
if (mlist->size () != mlist_tmp->size ())
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
|
|
// Get table size: count visible metrics
|
|
int nitems = data->size ();
|
|
if (data->size () != data_tmp->size ())
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < nitems; ++i)
|
|
{
|
|
Hist_data::HistItem *item = data->fetch (i);
|
|
Hist_data::HistItem *item_tmp = data_tmp->fetch (i);
|
|
if (item->obj->id != item_tmp->obj->id)
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (long i = 0, sz = mlist->size (); i < sz; i++)
|
|
{
|
|
long met_ind = i;
|
|
Metric *mitem = mlist->get (i);
|
|
Metric *mitem_tmp = mlist_tmp->get (i);
|
|
|
|
if (mitem->get_id () != mitem_tmp->get_id ())
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
if (mitem->get_visbits () != mitem_tmp->get_visbits ())
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
if (mitem->get_vtype () != mitem_tmp->get_vtype ())
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
|
|
if (!mitem->is_visible () && !mitem->is_tvisible ()
|
|
&& !mitem->is_pvisible ())
|
|
continue;
|
|
// table->append(dbeGetTableDataOneColumn(data, i));
|
|
for (long row = 0, sz_row = data->size (); row < sz_row; row++)
|
|
{
|
|
Metric *m = mitem;
|
|
TValue res;
|
|
TValue res_tmp;
|
|
TValue *v = data->get_value (&res, met_ind, row);
|
|
TValue *v_tmp = data_tmp->get_value (&res_tmp, met_ind, row);
|
|
if ((m->get_visbits () & VAL_RATIO) != 0)
|
|
{
|
|
if (v->tag != VT_LABEL)
|
|
{
|
|
if (v->to_double () != v_tmp->to_double ())
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
switch (m->get_vtype ())
|
|
{
|
|
case VT_DOUBLE:
|
|
{
|
|
double diff = v->d - v_tmp->d;
|
|
if (diff < 0) diff = -diff;
|
|
if (diff > 0.0001)
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
else
|
|
DBG (assert (true));
|
|
break;
|
|
}
|
|
case VT_INT:
|
|
if (v->i != v_tmp->i)
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
break;
|
|
case VT_ULLONG:
|
|
case VT_LLONG:
|
|
case VT_ADDRESS:
|
|
if (v->ll != v_tmp->ll)
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
break;
|
|
|
|
case VT_LABEL:
|
|
if (dbe_strcmp (v->l, v_tmp->l))
|
|
{
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
break;
|
|
default:
|
|
DBG (assert (false));
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
Histable *
|
|
PathTree::get_hist_func_obj (Node *node)
|
|
{
|
|
LoadObject *lo;
|
|
Function *func;
|
|
func = (Function*) (node->instr->convertto (Histable::FUNCTION));
|
|
// LIBRARY VISIBILITY
|
|
lo = func->module->loadobject;
|
|
if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE)
|
|
return lo->get_hide_function ();
|
|
return get_compare_obj (func);
|
|
}
|
|
|
|
Histable *
|
|
PathTree::get_hist_obj (Node *node, Histable* context)
|
|
{
|
|
LoadObject *lo;
|
|
Function *func;
|
|
switch (hist_data->type)
|
|
{
|
|
case Histable::INSTR:
|
|
if (hist_data->mode == Hist_data::MODL)
|
|
{
|
|
if (node->instr->get_type () != Histable::INSTR)
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
// LIBRARY VISIBILITY
|
|
func = (Function*) (node->instr->convertto (Histable::FUNCTION));
|
|
lo = func->module->loadobject;
|
|
if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE)
|
|
return lo->get_hide_function ();
|
|
}
|
|
return node->instr;
|
|
|
|
case Histable::LINE:
|
|
if (hist_data->mode != Hist_data::MODL)
|
|
{
|
|
func = (Function*) (node->instr->convertto (Histable::FUNCTION));
|
|
lo = func->module->loadobject;
|
|
// LIBRARY VISIBILITY
|
|
if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE)
|
|
return lo->get_hide_function ();
|
|
}
|
|
// For openmp user mode - the stack is already made with dbelines,
|
|
// no need to convert it
|
|
if (node->instr->get_type () == Histable::LINE)
|
|
return node->instr;
|
|
return node->instr->convertto (Histable::LINE, context);
|
|
|
|
case Histable::FUNCTION:
|
|
if (pathTreeType == PATHTREE_INTERNAL_FUNCTREE && node->ancestor != 0)
|
|
func = (Function*) node->instr;
|
|
else
|
|
func = (Function*) (node->instr->convertto (Histable::FUNCTION));
|
|
lo = func->module->loadobject;
|
|
// LIBRARY VISIBILITY
|
|
if (dbev->get_lo_expand (lo->seg_idx) == LIBEX_HIDE)
|
|
return lo->get_hide_function ();
|
|
return get_compare_obj (func);
|
|
case Histable::MODULE:
|
|
func = (Function*) (node->instr->convertto (Histable::FUNCTION));
|
|
return func->module;
|
|
case Histable::LOADOBJECT:
|
|
func = (Function*) (node->instr->convertto (Histable::FUNCTION));
|
|
return func->module->loadobject;
|
|
case Histable::INDEXOBJ:
|
|
case Histable::MEMOBJ:
|
|
return node->instr;
|
|
default:
|
|
DBG (assert (0));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
Histable *
|
|
PathTree::get_compare_obj (Histable *obj)
|
|
{
|
|
if (obj && dbev->comparingExperiments ())
|
|
obj = dbev->get_compare_obj (obj);
|
|
return obj;
|
|
}
|
|
|
|
void
|
|
PathTree::get_metrics (NodeIdx node_idx, int dpth)
|
|
{
|
|
Node *node = NODE_IDX (node_idx);
|
|
Histable *cur_obj = get_hist_obj (node);
|
|
obj_list[dpth] = cur_obj;
|
|
|
|
// Check for recursion (inclusive metrics)
|
|
int incl_ok = 1;
|
|
for (int i = dpth - 1; i >= 0; i--)
|
|
if (cur_obj == obj_list[i])
|
|
{
|
|
incl_ok = 0;
|
|
break;
|
|
}
|
|
|
|
// Check for leaf nodes (exclusive metrics)
|
|
int excl_ok = 0;
|
|
if (IS_LEAF (node) || node == NODE_IDX (root_idx))
|
|
excl_ok = 1;
|
|
|
|
// We shouldn't eliminate empty subtrees here because
|
|
// we create the list of hist items dynamically and want
|
|
// one for each object in the tree.
|
|
cur_obj = get_compare_obj (cur_obj);
|
|
Hist_data::HistItem *hi = hist_data->append_hist_item (cur_obj);
|
|
DBG (assert (hi != NULL));
|
|
|
|
MetricList *mlist = hist_data->get_metric_list ();
|
|
for (long ind = 0, sz = mlist->size (); ind < sz; ind++)
|
|
{
|
|
if (xlate[ind] == -1)
|
|
continue;
|
|
Metric *mtr = mlist->get (ind);
|
|
Metric::SubType subtype = mtr->get_subtype ();
|
|
if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx))
|
|
continue;
|
|
|
|
switch (subtype)
|
|
{
|
|
case Metric::INCLUSIVE:
|
|
if (incl_ok && hi)
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
break;
|
|
case Metric::EXCLUSIVE:
|
|
if (excl_ok && hi)
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
break;
|
|
// ignoring the following cases (why?)
|
|
case Metric::STATIC:
|
|
case Metric::ATTRIBUTED:
|
|
break;
|
|
case Metric::DATASPACE:
|
|
if (hi)
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dbeSession->is_interactive ())
|
|
{
|
|
ndone++;
|
|
int new_percent = 95 * ndone / nodes;
|
|
if (new_percent > percent)
|
|
{
|
|
percent = new_percent;
|
|
theApplication->set_progress (percent, NULL);
|
|
}
|
|
}
|
|
|
|
// Recursively process all descendants
|
|
int index;
|
|
int dsize = NUM_DESCENDANTS (node);
|
|
for (index = 0; index < dsize; index++)
|
|
get_metrics (node->descendants->fetch (index), dpth + 1);
|
|
}
|
|
|
|
void
|
|
PathTree::get_clr_metrics (Vector<Histable*> *objs, NodeIdx node_idx,
|
|
int pmatch, int dpth)
|
|
{
|
|
Node *node = NODE_IDX (node_idx);
|
|
Histable *cur_obj;
|
|
if (hist_data->type == Histable::LINE || hist_data->type == Histable::INSTR)
|
|
{
|
|
cur_obj = get_hist_func_obj (node);
|
|
node_list[dpth] = node;
|
|
}
|
|
else
|
|
cur_obj = get_hist_obj (node);
|
|
obj_list[dpth] = cur_obj;
|
|
|
|
bool match = false;
|
|
int nobj = objs->size ();
|
|
if (dpth + 1 >= nobj)
|
|
{
|
|
match = true;
|
|
for (int i = 0; i < nobj; ++i)
|
|
{
|
|
if (objs->fetch (i) != obj_list[dpth - nobj + 1 + i])
|
|
{
|
|
match = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Hist_data::HistItem *hi = NULL;
|
|
Hist_data::HistItem *hi_adj = NULL;
|
|
if (match && dpth >= nobj)
|
|
{
|
|
if (hist_data->type == Histable::LINE
|
|
|| hist_data->type == Histable::INSTR)
|
|
hi = hist_data->append_hist_item (get_hist_obj (node_list[dpth - nobj]));
|
|
else
|
|
hi = hist_data->append_hist_item (obj_list[dpth - nobj]);
|
|
|
|
if (pmatch >= 0 && pmatch >= nobj)
|
|
{
|
|
if (hist_data->type == Histable::LINE
|
|
|| hist_data->type == Histable::INSTR)
|
|
hi_adj = hist_data->append_hist_item (get_hist_obj (
|
|
node_list[pmatch - nobj]));
|
|
else
|
|
hi_adj = hist_data->append_hist_item (obj_list[pmatch - nobj]);
|
|
}
|
|
}
|
|
|
|
if (hi != NULL)
|
|
{
|
|
MetricList *mlist = hist_data->get_metric_list ();
|
|
for (long ind = 0, sz = mlist->size (); ind < sz; ind++)
|
|
{
|
|
if (xlate[ind] == -1)
|
|
continue;
|
|
if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx))
|
|
continue;
|
|
Metric *mtr = mlist->get (ind);
|
|
Metric::SubType subtype = mtr->get_subtype ();
|
|
|
|
switch (subtype)
|
|
{
|
|
case Metric::ATTRIBUTED:
|
|
if (hi)
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
if (hi_adj)
|
|
SUB_METRIC_VAL (hi_adj->value[ind], slots[xlate[ind]], node_idx);
|
|
break;
|
|
case Metric::STATIC:
|
|
case Metric::EXCLUSIVE:
|
|
case Metric::INCLUSIVE:
|
|
case Metric::DATASPACE:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recursively process all descendants
|
|
int dsize = NUM_DESCENDANTS (node);
|
|
for (int index = 0; index < dsize; index++)
|
|
get_clr_metrics (objs, node->descendants->fetch (index),
|
|
match ? dpth : pmatch, dpth + 1);
|
|
}
|
|
|
|
void
|
|
PathTree::get_clr_metrics (Vector<Histable*> *objs)
|
|
{
|
|
get_clr_metrics (objs, root_idx, -1, 0);
|
|
}
|
|
|
|
void
|
|
PathTree::get_cle_metrics (Vector<Histable*> *objs, NodeIdx node_idx, int pcle,
|
|
int pmatch, int dpth)
|
|
{
|
|
Node *node = NODE_IDX (node_idx);
|
|
Histable *cur_obj = get_hist_obj (node);
|
|
obj_list[dpth] = cur_obj;
|
|
|
|
bool match = false;
|
|
int nobj = objs->size ();
|
|
if (dpth + 1 >= nobj)
|
|
{
|
|
match = true;
|
|
for (int i = 0; i < nobj; ++i)
|
|
if (objs->fetch (i) != obj_list[dpth - nobj + 1 + i])
|
|
{
|
|
match = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Hist_data::HistItem *hi = NULL;
|
|
Hist_data::HistItem *hi_adj = NULL;
|
|
if (pmatch >= 0 && dpth == pmatch + 1)
|
|
hi = hist_data->append_hist_item (cur_obj);
|
|
if (match && IS_LEAF (node))
|
|
hi = hist_data->gprof_item;
|
|
if (pcle >= 0)
|
|
hi_adj = hist_data->append_hist_item (obj_list[pcle]);
|
|
|
|
if (hi != NULL)
|
|
{
|
|
MetricList *mlist = hist_data->get_metric_list ();
|
|
for (long ind = 0, sz = mlist->size (); ind < sz; ind++)
|
|
{
|
|
if (xlate[ind] == -1)
|
|
continue;
|
|
if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx))
|
|
continue;
|
|
Metric *mtr = mlist->get (ind);
|
|
Metric::SubType subtype = mtr->get_subtype ();
|
|
if (subtype == Metric::ATTRIBUTED)
|
|
{
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
if (hi_adj)
|
|
SUB_METRIC_VAL (hi_adj->value[ind], slots[xlate[ind]], node_idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recursively process all descendants
|
|
int dsize = NUM_DESCENDANTS (node);
|
|
for (int index = 0; index < dsize; index++)
|
|
get_cle_metrics (objs, node->descendants->fetch (index),
|
|
pmatch >= 0 && dpth == pmatch + 1 ? dpth : pcle,
|
|
match ? dpth : pmatch, dpth + 1);
|
|
}
|
|
|
|
void
|
|
PathTree::get_cle_metrics (Vector<Histable*> *objs, NodeIdx node_idx, int dpth)
|
|
{
|
|
Node *node = NODE_IDX (node_idx);
|
|
Histable *cur_obj = get_hist_obj (node);
|
|
Hist_data::HistItem *hi = NULL;
|
|
if (NULL == objs) // Special case: get root
|
|
hi = hist_data->append_hist_item (cur_obj);
|
|
else
|
|
{
|
|
if (dpth == objs->size ())
|
|
hi = hist_data->append_hist_item (cur_obj);
|
|
else if (cur_obj == objs->fetch (dpth))
|
|
{
|
|
// Recursively process all descendants
|
|
int dsize = NUM_DESCENDANTS (node);
|
|
for (int index = 0; index < dsize; index++)
|
|
get_cle_metrics (objs, node->descendants->fetch (index), dpth + 1);
|
|
if (dpth == objs->size () - 1 && dsize == 0)
|
|
hi = hist_data->gprof_item;
|
|
}
|
|
}
|
|
|
|
if (hi != NULL)
|
|
{
|
|
MetricList *mlist = hist_data->get_metric_list ();
|
|
for (long ind = 0, sz = mlist->size (); ind < sz; ind++)
|
|
{
|
|
if (xlate[ind] == -1)
|
|
continue;
|
|
if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx))
|
|
continue;
|
|
Metric *mtr = mlist->get (ind);
|
|
Metric::SubType subtype = mtr->get_subtype ();
|
|
if (subtype == Metric::ATTRIBUTED)
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PathTree::ftree_reset ()
|
|
{
|
|
if (pathTreeType == PATHTREE_MAIN && indxtype < 0)
|
|
{
|
|
reset ();
|
|
if (ftree_needs_update)
|
|
{
|
|
if (ftree_internal == NULL)
|
|
{
|
|
ftree_internal = new PathTree (dbev, indxtype,
|
|
PATHTREE_INTERNAL_FUNCTREE);
|
|
if (ftree_internal == NULL)
|
|
return;
|
|
}
|
|
ftree_internal->ftree_build (this);
|
|
ftree_needs_update = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PathTree::ftree_build (PathTree * mstr)
|
|
{
|
|
fini ();
|
|
init ();
|
|
allocate_slots (mstr->slots, mstr->nslots);
|
|
ftree_build (mstr, mstr->root_idx, root_idx);
|
|
depth = mstr->depth;
|
|
depth_map_build ();
|
|
}
|
|
|
|
#if DEBUG_FTREE // possibly TBR
|
|
void
|
|
PathTree::ftree_dump ()
|
|
{
|
|
hrtime_t starttime, endtime;
|
|
int nmetrics = 1;
|
|
// int nmetrics = nslots;
|
|
for (int kk = 0; kk < nmetrics; kk++)
|
|
{
|
|
int id = slots[kk].id;
|
|
starttime = gethrtime ();
|
|
long nodecnt = 0;
|
|
for (int ii = 0; ii < depth; ii++)
|
|
{
|
|
Vector<Vector<void*>*> *tmp = (Vector<Vector<void*>*>*)get_ftree_level
|
|
(id, ii);
|
|
if (tmp == NULL)
|
|
continue;
|
|
long sz = tmp->get (0)->size ();
|
|
nodecnt += sz;
|
|
#if 1
|
|
// fprintf(stderr, "... finished (%ld nodes)\n", sz);
|
|
#else
|
|
Vector<NodeIdx> *nodeIdxList = (Vector<NodeIdx> *)tmp->get (0);
|
|
Vector<NodeIdx> *ancestorNodeIdxList = (Vector<NodeIdx> *)tmp->get (1);
|
|
Vector<uint64_t> *idList = (Vector<uint64_t> *)tmp->get (2);
|
|
Vector<uint64_t> *vals = (Vector<uint64_t> *)tmp->get (3);
|
|
for (int jj = 0; jj < sz; jj++)
|
|
fprintf (stderr, " ...%d:%d node=%ld, anc=%ld, id=%llu, val=%llu\n",
|
|
sz, jj, nodeIdxList->get (jj),
|
|
ancestorNodeIdxList->get (jj),
|
|
idList->get (jj), vals->get (jj));
|
|
#endif
|
|
destroy (tmp);
|
|
}
|
|
endtime = gethrtime ();
|
|
fprintf (stderr, "====================== %ld nodes time=%llu\n",
|
|
nodecnt, (endtime - starttime) / 1000 / 1000);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// ftree: translate mstr Histable::INSTR to Histable::FUNCTION
|
|
void
|
|
PathTree::ftree_build (PathTree *mstr, NodeIdx mstr_node_idx,
|
|
NodeIdx local_node_idx)
|
|
{
|
|
// requires: slots, nslots
|
|
Node *mstr_node = mstr->NODE_IDX (mstr_node_idx);
|
|
int dsize = NUM_DESCENDANTS (mstr_node);
|
|
|
|
// Add metrics
|
|
for (int i = 0; i < nslots; i++)
|
|
{
|
|
if (i >= mstr->nslots)
|
|
continue; //weird
|
|
if (slots[i].vtype != mstr->slots[i].vtype)
|
|
continue; //weird
|
|
TValue val;
|
|
val.ll = 0;
|
|
mstr->ASN_METRIC_VAL (val, mstr->slots[i], mstr_node_idx);
|
|
int64_t mval;
|
|
switch (slots[i].vtype)
|
|
{
|
|
case VT_ULLONG:
|
|
case VT_LLONG:
|
|
mval = val.ll;
|
|
break;
|
|
case VT_INT:
|
|
mval = val.i;
|
|
break;
|
|
default:
|
|
mval = 0;
|
|
break;
|
|
}
|
|
if (mval)
|
|
{
|
|
Slot * mslot = SLOT_IDX (i);
|
|
if (mslot)
|
|
INCREMENT_METRIC (mslot, local_node_idx, mval);
|
|
}
|
|
}
|
|
|
|
// Recursively process all descendants
|
|
for (int index = 0; index < dsize; index++)
|
|
{
|
|
NodeIdx mstr_desc_node_idx = mstr_node->descendants->fetch (index);
|
|
Node *mstr_desc_node = mstr->NODE_IDX (mstr_desc_node_idx);
|
|
Function *func = (Function*) mstr_desc_node->instr->convertto (Histable::FUNCTION);
|
|
int mstr_desc_dsize = NUM_DESCENDANTS (mstr_desc_node);
|
|
bool leaf = (mstr_desc_dsize == 0);
|
|
NodeIdx local_desc_node_idx = find_desc_node (local_node_idx, func, leaf);
|
|
ftree_build (mstr, mstr_desc_node_idx, local_desc_node_idx);
|
|
}
|
|
}
|
|
|
|
void
|
|
PathTree::depth_map_build ()
|
|
{
|
|
destroy (depth_map);
|
|
depth_map = new Vector<Vector<NodeIdx>*>(depth);
|
|
if (depth)
|
|
{
|
|
depth_map->put (depth - 1, 0); // fill vector with nulls
|
|
depth_map_build (root_idx, 0);
|
|
}
|
|
}
|
|
|
|
void
|
|
PathTree::depth_map_build (NodeIdx node_idx, int dpth)
|
|
{
|
|
Node *node = NODE_IDX (node_idx);
|
|
|
|
Vector<NodeIdx> *node_idxs = depth_map->get (dpth);
|
|
if (node_idxs == NULL)
|
|
{
|
|
node_idxs = new Vector<NodeIdx>();
|
|
depth_map->store (dpth, node_idxs);
|
|
}
|
|
node_idxs->append (node_idx);
|
|
|
|
// Recursively process all descendants
|
|
int dsize = NUM_DESCENDANTS (node);
|
|
for (int index = 0; index < dsize; index++)
|
|
{
|
|
NodeIdx desc_node_idx = node->descendants->fetch (index);
|
|
depth_map_build (desc_node_idx, dpth + 1);
|
|
}
|
|
}
|
|
|
|
int
|
|
PathTree::get_ftree_depth ()
|
|
{ // external use only
|
|
ftree_reset ();
|
|
if (!ftree_internal)
|
|
return 0;
|
|
return ftree_internal->get_depth ();
|
|
}
|
|
|
|
Vector<Function*>*
|
|
PathTree::get_ftree_funcs ()
|
|
{ // external use only
|
|
ftree_reset ();
|
|
if (!ftree_internal)
|
|
return NULL;
|
|
return ftree_internal->get_funcs ();
|
|
}
|
|
|
|
Vector<Function*>*
|
|
PathTree::get_funcs ()
|
|
{
|
|
// get unique functions
|
|
if (fn_map == NULL)
|
|
return NULL;
|
|
return fn_map->keySet ();
|
|
}
|
|
|
|
Vector<void*>*
|
|
PathTree::get_ftree_level (BaseMetric *bm, int dpth)
|
|
{ // external use only
|
|
ftree_reset ();
|
|
if (!ftree_internal)
|
|
return NULL;
|
|
return ftree_internal->get_level (bm, dpth);
|
|
}
|
|
|
|
Vector<void*>*
|
|
PathTree::get_level (BaseMetric *bm, int dpth)
|
|
{
|
|
// Nodes at tree depth dpth
|
|
if (dpth < 0 || dpth >= depth)
|
|
return NULL;
|
|
if (depth_map == NULL)
|
|
return NULL;
|
|
Vector<NodeIdx> *node_idxs = depth_map->get (dpth);
|
|
return get_nodes (bm, node_idxs);
|
|
}
|
|
|
|
Vector<void*>*
|
|
PathTree::get_ftree_node_children (BaseMetric *bm, NodeIdx node_idx)
|
|
{ // external use only
|
|
ftree_reset ();
|
|
if (!ftree_internal)
|
|
return NULL;
|
|
return ftree_internal->get_node_children (bm, node_idx);
|
|
}
|
|
|
|
Vector<void*>*
|
|
PathTree::get_node_children (BaseMetric *bm, NodeIdx node_idx)
|
|
{
|
|
// Nodes that are children of node_idx
|
|
if (depth_map == NULL)
|
|
return NULL;
|
|
if (node_idx == 0) // special case for root
|
|
return get_nodes (bm, depth_map->get (0));
|
|
if (node_idx < 0 || node_idx >= nodes)
|
|
return NULL;
|
|
Node *node = NODE_IDX (node_idx);
|
|
if (node == NULL)
|
|
return NULL;
|
|
Vector<NodeIdx> *node_idxs = node->descendants;
|
|
return get_nodes (bm, node_idxs);
|
|
}
|
|
|
|
Vector<void*>*
|
|
PathTree::get_nodes (BaseMetric *bm, Vector<NodeIdx> *node_idxs)
|
|
{ // used for ftree
|
|
// capture info for node_idxs:
|
|
// node's idx
|
|
// node->ancestor idx
|
|
// node->instr->id
|
|
// mind metric value // in the future, could instead accept vector of mind
|
|
if (node_idxs == NULL)
|
|
return NULL;
|
|
long sz = node_idxs->size ();
|
|
if (sz <= 0)
|
|
return NULL;
|
|
|
|
bool calculate_metric = false;
|
|
ValueTag vtype;
|
|
int slot_idx;
|
|
double prec;
|
|
if (bm != NULL)
|
|
{
|
|
int mind = bm->get_id ();
|
|
slot_idx = find_slot (mind); // may be -1 (CPI and IPC)
|
|
prec = bm->get_precision ();
|
|
vtype = bm->get_vtype ();
|
|
}
|
|
else
|
|
{
|
|
slot_idx = -1;
|
|
prec = 1.0;
|
|
vtype = VT_INT;
|
|
}
|
|
|
|
if (slot_idx >= 0)
|
|
{
|
|
switch (vtype)
|
|
{
|
|
case VT_ULLONG:
|
|
case VT_LLONG:
|
|
case VT_INT:
|
|
if (slots[slot_idx].vtype == vtype)
|
|
calculate_metric = true;
|
|
else
|
|
DBG (assert (false));
|
|
break;
|
|
case VT_DOUBLE:
|
|
calculate_metric = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Vector<void*> *results = new Vector<void*>(4);
|
|
if (!calculate_metric)
|
|
results->store (3, NULL);
|
|
else
|
|
{
|
|
// Code below cribbed from Dbe.cc:dbeGetTableDataV2Data.
|
|
// TBD: possibly create an intermediate HistData and instead call that routine
|
|
switch (vtype)
|
|
{
|
|
case VT_ULLONG:
|
|
case VT_LLONG:
|
|
{
|
|
Vector<long long> *vals = new Vector<long long>(sz);
|
|
for (long i = 0; i < sz; i++)
|
|
{
|
|
NodeIdx node_idx = node_idxs->get (i);
|
|
TValue val;
|
|
val.ll = 0;
|
|
ASN_METRIC_VAL (val, slots[slot_idx], node_idx);
|
|
vals->append (val.ll);
|
|
}
|
|
results->store (3, vals);
|
|
break;
|
|
}
|
|
case VT_DOUBLE:
|
|
{
|
|
Vector<double> *vals = new Vector<double>(sz);
|
|
TValue val;
|
|
val.tag = slots[slot_idx].vtype; // required for to_double();
|
|
for (long i = 0; i < sz; i++)
|
|
{
|
|
NodeIdx node_idx = node_idxs->get (i);
|
|
val.ll = 0;
|
|
ASN_METRIC_VAL (val, slots[slot_idx], node_idx);
|
|
double dval = val.to_double ();
|
|
dval /= prec;
|
|
vals->append (dval);
|
|
}
|
|
results->store (3, vals);
|
|
break;
|
|
}
|
|
case VT_INT:
|
|
{
|
|
Vector<int> *vals = new Vector<int>(sz);
|
|
for (long i = 0; i < sz; i++)
|
|
{
|
|
NodeIdx node_idx = node_idxs->get (i);
|
|
TValue val;
|
|
val.i = 0;
|
|
ASN_METRIC_VAL (val, slots[slot_idx], node_idx);
|
|
vals->append (val.i);
|
|
}
|
|
results->store (3, vals);
|
|
break;
|
|
}
|
|
default:
|
|
results->store (3, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Vector<int> *nodeIdxList = new Vector<int>(sz);
|
|
Vector<int> *ancestorNodeIdxList = new Vector<int>(sz);
|
|
Vector<uint64_t> *idList = new Vector<uint64_t>(sz);
|
|
for (long i = 0; i < sz; i++)
|
|
{
|
|
NodeIdx node_idx = node_idxs->get (i);
|
|
Node *node = NODE_IDX (node_idx);
|
|
NodeIdx ancestor_idx = node->ancestor;
|
|
Histable *func = node->instr;
|
|
nodeIdxList->append (node_idx);
|
|
ancestorNodeIdxList->append (ancestor_idx);
|
|
idList->append (func->id);
|
|
}
|
|
|
|
results->store (0, nodeIdxList);
|
|
results->store (1, ancestorNodeIdxList);
|
|
results->store (2, idList);
|
|
return results;
|
|
}
|
|
|
|
void
|
|
PathTree::get_cle_metrics (Vector<Histable*> *objs)
|
|
{
|
|
if (NULL == objs || objs->fetch (0) == get_hist_obj (NODE_IDX (root_idx)))
|
|
// Call Tree optimization
|
|
get_cle_metrics (objs, root_idx, 0);
|
|
else
|
|
// General case
|
|
get_cle_metrics (objs, root_idx, -1, -1, 0);
|
|
}
|
|
|
|
void
|
|
PathTree::get_metrics (Vector<Function*> *functions, Histable *context)
|
|
{
|
|
Function *fitem;
|
|
int excl_ok, incl_ok;
|
|
NodeIdx node_idx;
|
|
Node *node, *anc;
|
|
int index;
|
|
|
|
Vec_loop (Function*, functions, index, fitem)
|
|
{
|
|
node_idx = fn_map->get (fitem);
|
|
for (; node_idx; node_idx = node->funclist)
|
|
{
|
|
node = NODE_IDX (node_idx);
|
|
Histable *h_obj = get_hist_obj (node, context);
|
|
if (h_obj == NULL)
|
|
continue;
|
|
|
|
// Check for recursion (inclusive metrics)
|
|
incl_ok = 1;
|
|
for (anc = NODE_IDX (node->ancestor); anc;
|
|
anc = NODE_IDX (anc->ancestor))
|
|
{
|
|
if (h_obj == get_hist_obj (anc, context))
|
|
{
|
|
incl_ok = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check for leaf nodes (exclusive metrics)
|
|
excl_ok = 0;
|
|
if (IS_LEAF (node))
|
|
excl_ok = 1;
|
|
|
|
h_obj = get_compare_obj (h_obj);
|
|
Hist_data::HistItem *hi = hist_data->append_hist_item (h_obj);
|
|
|
|
if (!excl_ok)
|
|
hist_data->get_callsite_mark ()->put (h_obj, 1);
|
|
MetricList *mlist = hist_data->get_metric_list ();
|
|
for (long ind = 0, sz = mlist->size (); ind < sz; ind++)
|
|
{
|
|
if (xlate[ind] == -1)
|
|
continue;
|
|
Metric *mtr = mlist->get (ind);
|
|
Metric::SubType subtype = mtr->get_subtype ();
|
|
if (subtype == Metric::INCLUSIVE && !incl_ok)
|
|
continue;
|
|
if (subtype == Metric::EXCLUSIVE && !excl_ok)
|
|
continue;
|
|
if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx))
|
|
continue;
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PathTree::get_self_metrics (Vector<Histable*> *objs, NodeIdx node_idx,
|
|
bool seen, int dpth)
|
|
{
|
|
Node *node = NODE_IDX (node_idx);
|
|
Histable *cur_obj = get_hist_obj (node);
|
|
obj_list[dpth] = cur_obj;
|
|
|
|
bool match = false;
|
|
int nobj = objs->size ();
|
|
if (dpth + 1 >= nobj)
|
|
{
|
|
match = true;
|
|
for (int i = 0; i < nobj; ++i)
|
|
{
|
|
if (objs->fetch (i) != obj_list[dpth - nobj + 1 + i])
|
|
{
|
|
match = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (match)
|
|
{
|
|
Hist_data::HistItem *hi = hist_data->append_hist_item (cur_obj);
|
|
int incl_ok = !seen;
|
|
int excl_ok = 0;
|
|
if (IS_LEAF (node) || node == NODE_IDX (root_idx))
|
|
excl_ok = 1;
|
|
MetricList *mlist = hist_data->get_metric_list ();
|
|
for (long ind = 0, sz = mlist->size (); ind < sz; ind++)
|
|
{
|
|
if (xlate[ind] == -1)
|
|
continue;
|
|
if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx))
|
|
continue;
|
|
Metric *mtr = mlist->get (ind);
|
|
Metric::SubType subtype = mtr->get_subtype ();
|
|
switch (subtype)
|
|
{
|
|
case Metric::INCLUSIVE:
|
|
if (incl_ok && hi)
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
break;
|
|
case Metric::EXCLUSIVE:
|
|
case Metric::ATTRIBUTED:
|
|
if (excl_ok && hi)
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
break;
|
|
case Metric::DATASPACE:
|
|
if (hi)
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
break;
|
|
// ignoring the following cases (why?)
|
|
case Metric::STATIC:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dbeSession->is_interactive ())
|
|
{
|
|
ndone++;
|
|
int new_percent = 95 * ndone / nodes;
|
|
if (new_percent > percent)
|
|
{
|
|
percent = new_percent;
|
|
theApplication->set_progress (percent, NULL);
|
|
}
|
|
}
|
|
|
|
// Recursively process all descendants
|
|
int index;
|
|
int dsize = NUM_DESCENDANTS (node);
|
|
for (index = 0; index < dsize; index++)
|
|
get_self_metrics (objs, node->descendants->fetch (index),
|
|
seen || match, dpth + 1);
|
|
}
|
|
|
|
void
|
|
PathTree::get_self_metrics (Vector<Histable*> *objs)
|
|
{
|
|
get_self_metrics (objs, root_idx, false, 0);
|
|
}
|
|
|
|
void
|
|
PathTree::get_self_metrics (Histable *obj, Vector<Function*> *funclist,
|
|
Vector<Histable*>* sel_objs)
|
|
{
|
|
int excl_ok, incl_ok;
|
|
NodeIdx node_idx;
|
|
Node *node, *anc;
|
|
|
|
if (obj == NULL)
|
|
return;
|
|
|
|
SourceFile *src = NULL;
|
|
if (obj && obj->get_type () == Histable::LINE)
|
|
{
|
|
DbeLine *dbeline = (DbeLine*) obj;
|
|
src = dbeline->sourceFile;
|
|
}
|
|
|
|
Hist_data::HistItem *hi = hist_data->append_hist_item (obj);
|
|
for (int i = 0, sz = funclist ? funclist->size () : 0; i < sz; i++)
|
|
{
|
|
Function *fitem = (Function*) get_compare_obj (funclist->fetch (i));
|
|
node_idx = fn_map->get (fitem);
|
|
for (; node_idx; node_idx = node->funclist)
|
|
{
|
|
node = NODE_IDX (node_idx);
|
|
if (obj && obj->get_type () == Histable::LINE)
|
|
{
|
|
Histable *h = get_hist_obj (node, src);
|
|
if (h == NULL)
|
|
continue;
|
|
if (h->convertto (Histable::LINE) != obj->convertto (Histable::LINE))
|
|
continue;
|
|
}
|
|
else if (get_hist_obj (node, src) != obj)
|
|
continue;
|
|
|
|
// Check for recursion (inclusive metrics)
|
|
incl_ok = 1;
|
|
for (anc = NODE_IDX (node->ancestor); anc;
|
|
anc = NODE_IDX (anc->ancestor))
|
|
{
|
|
if (get_hist_obj (anc, src) == obj)
|
|
{
|
|
incl_ok = 0;
|
|
break;
|
|
}
|
|
if (sel_objs != NULL)
|
|
for (int k = 0; k < sel_objs->size (); k++)
|
|
if (sel_objs->fetch (k) == get_hist_obj (anc, src))
|
|
{
|
|
incl_ok = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check for leaf nodes (exclusive metrics)
|
|
excl_ok = 0;
|
|
if (IS_LEAF (node) || node == NODE_IDX (root_idx))
|
|
excl_ok = 1;
|
|
|
|
MetricList *mlist = hist_data->get_metric_list ();
|
|
for (long ind = 0, ind_sz = mlist->size (); ind < ind_sz; ind++)
|
|
{
|
|
if (xlate[ind] == -1)
|
|
continue;
|
|
Metric *mtr = mlist->get (ind);
|
|
Metric::SubType subtype = mtr->get_subtype ();
|
|
if (subtype == Metric::INCLUSIVE && !incl_ok)
|
|
continue;
|
|
if (subtype == Metric::EXCLUSIVE && !excl_ok)
|
|
continue;
|
|
if (subtype == Metric::ATTRIBUTED && !excl_ok)
|
|
continue;
|
|
if (IS_MVAL_ZERO (slots[xlate[ind]], node_idx))
|
|
continue;
|
|
ADD_METRIC_VAL (hi->value[ind], slots[xlate[ind]], node_idx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector<Histable*> *
|
|
PathTree::get_clr_instr (Histable * func)
|
|
{
|
|
Vector<Histable*> * instrs = NULL;
|
|
if (func->get_type () != Histable::FUNCTION)
|
|
return NULL;
|
|
NodeIdx node_idx = fn_map->get ((Function*) func);
|
|
Node *node = NODE_IDX (node_idx);
|
|
if (node == NULL)
|
|
return new Vector<Histable*>();
|
|
int instr_num = 0;
|
|
for (; node; node = NODE_IDX (node->funclist))
|
|
instr_num++;
|
|
instrs = new Vector<Histable*>(instr_num);
|
|
node = NODE_IDX (node_idx);
|
|
Histable *instr = NODE_IDX (node->ancestor)->instr;
|
|
instr_num = 0;
|
|
instrs->store (instr_num, instr);
|
|
node = NODE_IDX (node->funclist);
|
|
for (; node; node = NODE_IDX (node->funclist))
|
|
{
|
|
instr = NODE_IDX (node->ancestor)->instr;
|
|
instr_num++;
|
|
instrs->store (instr_num, instr);
|
|
}
|
|
return instrs;
|
|
}
|
|
|
|
Vector<void*> *
|
|
PathTree::get_cle_instr (Histable * func, Vector<Histable*>*&instrs)
|
|
{
|
|
if (func->get_type () != Histable::FUNCTION)
|
|
return NULL;
|
|
NodeIdx node_idx = fn_map->get ((Function*) func);
|
|
Node *node = NODE_IDX (node_idx);
|
|
if (node == NULL)
|
|
{
|
|
instrs = new Vector<Histable*>();
|
|
return new Vector<void*>();
|
|
}
|
|
int instr_num = 0;
|
|
for (; node; node = NODE_IDX (node->funclist))
|
|
instr_num++;
|
|
instrs = new Vector<Histable*>(instr_num);
|
|
Vector<void*> *callee_info = new Vector<void*>(instr_num);
|
|
node = NODE_IDX (node_idx);
|
|
Histable *instr = node->instr;
|
|
instr_num = 0;
|
|
instrs->store (instr_num, instr);
|
|
int dec_num = 0;
|
|
NodeIdx dec_idx = 0;
|
|
if (NUM_DESCENDANTS (node) > 0)
|
|
{
|
|
Vector<Histable*> * callee_instrs = new Vector<Histable*>(node->descendants->size ());
|
|
Vec_loop (NodeIdx, node->descendants, dec_num, dec_idx)
|
|
{
|
|
Node * dec_node = NODE_IDX (dec_idx);
|
|
//XXXX Note: there can be more than one instrs in one leaf function
|
|
callee_instrs->store (dec_num, dec_node->instr);
|
|
}
|
|
callee_info->store (instr_num, callee_instrs);
|
|
}
|
|
else
|
|
callee_info->store (instr_num, NULL);
|
|
node = NODE_IDX (node->funclist);
|
|
for (; node; node = NODE_IDX (node->funclist))
|
|
{
|
|
instr = node->instr;
|
|
instr_num++;
|
|
instrs->store (instr_num, instr);
|
|
if (NUM_DESCENDANTS (node) > 0)
|
|
{
|
|
Vector<Histable*> * callee_instrs = new Vector<Histable*>(node->descendants->size ());
|
|
Vec_loop (NodeIdx, node->descendants, dec_num, dec_idx)
|
|
{
|
|
Node * dec_node = NODE_IDX (dec_idx);
|
|
//XXXX Note: there can be more than one instrs in one leaf function
|
|
callee_instrs->store (dec_num, dec_node->instr);
|
|
}
|
|
callee_info->store (instr_num, callee_instrs);
|
|
}
|
|
else
|
|
callee_info->store (instr_num, NULL);
|
|
}
|
|
return callee_info;
|
|
}
|
|
|
|
//
|
|
//
|
|
// The following methods are used for debugging purpose only.
|
|
//
|
|
//
|
|
static int maxdepth;
|
|
static int maxwidth;
|
|
|
|
void
|
|
PathTree::print (FILE *fd)
|
|
{
|
|
(void) reset ();
|
|
fprintf (fd, NTXT ("n = %lld, dn = %lld, MD = %lld\n\n"),
|
|
(long long) nodes, (long long) dnodes, (long long) depth);
|
|
maxdepth = 0;
|
|
maxwidth = 0;
|
|
print (fd, root, 0);
|
|
fprintf (fd, NTXT ("md = %lld, mw = %lld\n"),
|
|
(long long) maxdepth, (long long) maxwidth);
|
|
}
|
|
|
|
void
|
|
PathTree::print (FILE *fd, PathTree::Node *node, int lvl)
|
|
{
|
|
const char *t;
|
|
char *n;
|
|
if (lvl + 1 > maxdepth)
|
|
maxdepth = lvl + 1;
|
|
for (int i = 0; i < lvl; i++)
|
|
fprintf (fd, NTXT ("-"));
|
|
Histable *instr = node->instr;
|
|
if (instr->get_type () == Histable::LINE)
|
|
{
|
|
t = "L";
|
|
n = ((DbeLine *) instr)->func->get_name ();
|
|
}
|
|
else if (instr->get_type () == Histable::INSTR)
|
|
{
|
|
t = "I";
|
|
n = ((DbeInstr *) instr)->func->get_name ();
|
|
}
|
|
else
|
|
{
|
|
t = "O";
|
|
n = instr->get_name ();
|
|
}
|
|
long long addr = (long long) instr->get_addr ();
|
|
fprintf (fd, NTXT ("%s %s (0x%08llx) -- ndesc = %lld\n"),
|
|
t, n, addr, (long long) (NUM_DESCENDANTS (node)));
|
|
|
|
// Recursively process all descendants
|
|
int dsize = NUM_DESCENDANTS (node);
|
|
if (dsize > maxwidth)
|
|
maxwidth = dsize;
|
|
for (int index = 0; index < dsize; index++)
|
|
print (fd, NODE_IDX (node->descendants->fetch (index)), lvl + 1);
|
|
}
|
|
|
|
void
|
|
PathTree::printn (FILE *fd)
|
|
{
|
|
int n = dbg_nodes (root);
|
|
fprintf (fd, GTXT ("Number of nodes: %d, total size: %d\n"), n, (int) (n * sizeof (Node)));
|
|
}
|
|
|
|
void
|
|
PathTree::dumpNodes (FILE *fd, Histable *obj)
|
|
{
|
|
const char *t;
|
|
char *n;
|
|
NodeIdx node_idx = fn_map->get ((Function*) obj);
|
|
Node *node = NODE_IDX (node_idx);
|
|
if (node == NULL)
|
|
{
|
|
fprintf (fd, GTXT ("No nodes associated with %s\n"), obj->get_name ());
|
|
return;
|
|
}
|
|
Histable *instr = node->instr;
|
|
for (; node; node = NODE_IDX (node->funclist))
|
|
{
|
|
instr = node->instr;
|
|
if (instr->get_type () == Histable::LINE)
|
|
{
|
|
t = "L";
|
|
n = ((DbeLine *) instr)->func->get_name ();
|
|
}
|
|
else if (instr->get_type () == Histable::INSTR)
|
|
{
|
|
t = "I";
|
|
n = ((DbeInstr *) instr)->func->get_name ();
|
|
}
|
|
else
|
|
{
|
|
t = "O";
|
|
n = instr->get_name ();
|
|
}
|
|
long long addr = (long long) instr->get_addr ();
|
|
if (addr <= 0xFFFFFFFFU)
|
|
fprintf (fd, NTXT ("0x%08x -- %s %s\n"), (uint32_t) addr, t, n);
|
|
else
|
|
fprintf (fd, NTXT ("0x%016llX -- %s %s\n"), addr, t, n);
|
|
}
|
|
}
|
|
|
|
int
|
|
PathTree::dbg_nodes (PathTree::Node *node)
|
|
{
|
|
int res = 1;
|
|
int dsize = NUM_DESCENDANTS (node);
|
|
for (int index = 0; index < dsize; index++)
|
|
res += dbg_nodes (NODE_IDX (node->descendants->fetch (index)));
|
|
return res;
|
|
}
|
|
|
|
static int mind_g;
|
|
|
|
int
|
|
leak_alloc_comp (const void *s1, const void *s2)
|
|
{
|
|
// See Hist_data::sort_compare() for duplicate code
|
|
int result = 0;
|
|
CStack_data::CStack_item *t1, *t2;
|
|
t1 = *(CStack_data::CStack_item **)s1;
|
|
t2 = *(CStack_data::CStack_item **)s2;
|
|
|
|
switch (t1->value[mind_g].tag)
|
|
{
|
|
case VT_INT:
|
|
if (t1->value[mind_g].i < t2->value[mind_g].i)
|
|
result = -1;
|
|
else if (t1->value[mind_g].i > t2->value[mind_g].i)
|
|
result = 1;
|
|
else
|
|
result = 0;
|
|
break;
|
|
case VT_LLONG:
|
|
if (t1->value[mind_g].ll < t2->value[mind_g].ll)
|
|
result = -1;
|
|
else if (t1->value[mind_g].ll > t2->value[mind_g].ll)
|
|
result = 1;
|
|
else
|
|
result = 0;
|
|
break;
|
|
case VT_ULLONG:
|
|
if (t1->value[mind_g].ull < t2->value[mind_g].ull)
|
|
result = -1;
|
|
else if (t1->value[mind_g].ull > t2->value[mind_g].ull)
|
|
result = 1;
|
|
else
|
|
result = 0;
|
|
break;
|
|
// ignoring the following cases (why?)
|
|
case VT_SHORT:
|
|
case VT_FLOAT:
|
|
case VT_DOUBLE:
|
|
case VT_HRTIME:
|
|
case VT_LABEL:
|
|
case VT_ADDRESS:
|
|
case VT_OFFSET:
|
|
break;
|
|
}
|
|
// Sort in descending order
|
|
return -result;
|
|
}
|
|
|
|
CStack_data *
|
|
PathTree::get_cstack_data (MetricList *mlist)
|
|
{
|
|
(void) reset ();
|
|
CStack_data *lam = new CStack_data (mlist);
|
|
int nmetrics = mlist->get_items ()->size ();
|
|
mind_g = -1;
|
|
xlate = new int[nmetrics];
|
|
for (int mind = 0; mind < nmetrics; mind++)
|
|
{
|
|
xlate[mind] = -1;
|
|
Metric *mtr = mlist->get_items ()->fetch (mind);
|
|
if (mlist->get_sort_ref_index () == mind)
|
|
mind_g = mind;
|
|
xlate[mind] = find_slot (mtr->get_id ());
|
|
}
|
|
|
|
// now fill in the actual data
|
|
obj_list = new Histable*[depth];
|
|
get_cstack_list (lam, root_idx, 0);
|
|
delete[] obj_list;
|
|
|
|
if (mind_g >= 0)
|
|
lam->cstack_items->sort (leak_alloc_comp);
|
|
delete[] xlate;
|
|
return lam;
|
|
}
|
|
|
|
void
|
|
PathTree::get_cstack_list (CStack_data *lam, NodeIdx node_idx, int dpth)
|
|
{
|
|
|
|
Node *node = NODE_IDX (node_idx);
|
|
obj_list[dpth] = node->instr;
|
|
|
|
CStack_data::CStack_item *item = NULL;
|
|
if (IS_LEAF (node))
|
|
item = lam->new_cstack_item ();
|
|
int nmetrics = lam->metrics->get_items ()->size ();
|
|
bool subtree_empty = true;
|
|
|
|
for (int mind = 0; mind < nmetrics; mind++)
|
|
{
|
|
if (xlate[mind] == -1)
|
|
continue;
|
|
if (IS_MVAL_ZERO (slots[xlate[mind]], node_idx))
|
|
continue;
|
|
else
|
|
subtree_empty = false;
|
|
if (item)
|
|
{
|
|
ADD_METRIC_VAL (item->value[mind], slots[xlate[mind]], node_idx);
|
|
ADD_METRIC_VAL (lam->total->value[mind], slots[xlate[mind]], node_idx);
|
|
}
|
|
}
|
|
|
|
if (subtree_empty)
|
|
{
|
|
delete item;
|
|
return;
|
|
}
|
|
|
|
if (item)
|
|
{
|
|
// Finish processing a leaf node
|
|
item->stack = new Vector<DbeInstr*>(dpth);
|
|
for (int i = 1; i <= dpth; i++)
|
|
item->stack->append ((DbeInstr*) obj_list[i]);
|
|
lam->cstack_items->append (item);
|
|
}
|
|
else
|
|
{
|
|
// Recursively process all descendants
|
|
int dsize = NUM_DESCENDANTS (node);
|
|
for (int index = 0; index < dsize; index++)
|
|
get_cstack_list (lam, node->descendants->fetch (index), dpth + 1);
|
|
}
|
|
}
|
|
|
|
Emsg *
|
|
PathTree::fetch_stats ()
|
|
{
|
|
if (statsq == NULL)
|
|
return NULL;
|
|
return statsq->fetch ();
|
|
}
|
|
|
|
void
|
|
PathTree::delete_stats ()
|
|
{
|
|
if (statsq != NULL)
|
|
{
|
|
delete statsq;
|
|
statsq = new Emsgqueue (NTXT ("statsq"));
|
|
}
|
|
}
|
|
|
|
Emsg *
|
|
PathTree::fetch_warnings ()
|
|
{
|
|
if (warningq == NULL)
|
|
return NULL;
|
|
return warningq->fetch ();
|
|
}
|
|
|
|
void
|
|
PathTree::delete_warnings ()
|
|
{
|
|
if (warningq != NULL)
|
|
{
|
|
delete warningq;
|
|
warningq = new Emsgqueue (NTXT ("warningq"));
|
|
}
|
|
}
|