76bdc7266a
This adds 'Innovative Computing Labs' as an external author to update-copyright.py, to cover the copyright notice in gprofng/common/opteron_pcbe.c, and uses that plus another external author 'Oracle and' to update gprofng copyright dates. I'm not going to commit 'Oracle and' as an accepted author, but that covers the string "Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved." found in gprofng/testsuite/gprofng.display/jsynprog files.
2638 lines
68 KiB
C++
2638 lines
68 KiB
C++
/* Copyright (C) 2021-2023 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"));
|
|
}
|
|
}
|