Files
binutils-gdb/gprofng/src/PathTree.cc
T
Alan Modra 76bdc7266a Update year range in gprofng copyright notices
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.
2023-01-01 23:26:30 +10:30

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