|  | /* Copyright (C) 2021-2025 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")); | 
|  | } | 
|  | } |