/* Copyright (C) 2021-2024 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 #include #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; 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; 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 (""))); 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*)NULL : new Vector(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 *stack = (Vector*)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 *mlist = dbev->get_all_reg_metrics (); Vector 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 *objs, Histable *context, Vector *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 *funclist = new Vector; funclist->append ((Function*) obj); get_metrics (funclist, context); delete funclist; break; } case Histable::MODULE: { Vector *comparableModules = obj->get_comparable_objs (); if (comparableModules != NULL) { Vector *functions = new Vector; 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 *funclist = new Vector; 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 *funclist = NULL; if (context->get_type () == Histable::MODULE) funclist = ((Module*) context)->functions->copy (); else { funclist = new Vector; 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 *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 *objs) { get_clr_metrics (objs, root_idx, -1, 0); } void PathTree::get_cle_metrics (Vector *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 *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*> *tmp = (Vector*>*)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 *nodeIdxList = (Vector *)tmp->get (0); Vector *ancestorNodeIdxList = (Vector *)tmp->get (1); Vector *idList = (Vector *)tmp->get (2); Vector *vals = (Vector *)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*>(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 *node_idxs = depth_map->get (dpth); if (node_idxs == NULL) { node_idxs = new Vector(); 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* PathTree::get_ftree_funcs () { // external use only ftree_reset (); if (!ftree_internal) return NULL; return ftree_internal->get_funcs (); } Vector* PathTree::get_funcs () { // get unique functions if (fn_map == NULL) return NULL; return fn_map->keySet (); } Vector* 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* 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 *node_idxs = depth_map->get (dpth); return get_nodes (bm, node_idxs); } Vector* 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* 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 *node_idxs = node->descendants; return get_nodes (bm, node_idxs); } Vector* PathTree::get_nodes (BaseMetric *bm, Vector *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 *results = new Vector(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 *vals = new Vector(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 *vals = new Vector(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 *vals = new Vector(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 *nodeIdxList = new Vector(sz); Vector *ancestorNodeIdxList = new Vector(sz); Vector *idList = new Vector(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 *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 *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 *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 *objs) { get_self_metrics (objs, root_idx, false, 0); } void PathTree::get_self_metrics (Histable *obj, Vector *funclist, Vector* 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 * PathTree::get_clr_instr (Histable * func) { Vector * 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(); int instr_num = 0; for (; node; node = NODE_IDX (node->funclist)) instr_num++; instrs = new Vector(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 * PathTree::get_cle_instr (Histable * func, Vector*&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(); return new Vector(); } int instr_num = 0; for (; node; node = NODE_IDX (node->funclist)) instr_num++; instrs = new Vector(instr_num); Vector *callee_info = new Vector(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 * callee_instrs = new Vector(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 * callee_instrs = new Vector(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(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")); } }