/* Copyright (C) 2021 Free Software Foundation, Inc. Contributed by Oracle. This file is part of GNU Binutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include "util.h" #include "DefaultMap.h" #include "DbeSession.h" #include "Experiment.h" #include "DataObject.h" #include "Function.h" #include "Hist_data.h" #include "Histable.h" #include "MemObject.h" #include "IndexObject.h" #include "MetricList.h" #include "Metric.h" #include "Module.h" #include "LoadObject.h" #include "Settings.h" #include "StringBuilder.h" #include "ExpGroup.h" #include "PathTree.h" #include "DbeView.h" #include "FileData.h" Hist_data::HistItem::HistItem (long n) { obj = NULL; type = 0; size = n; value = new TValue[n]; memset (value, 0, sizeof (TValue) * n); } Hist_data::HistItem::~HistItem () { for (long i = 0; i < size; i++) if (value[i].tag == VT_LABEL) free (value[i].l); delete[] value; } long Hist_data::size () { // If the data values have not been computed, do so // Return the total number of items return hist_items->size (); } Hist_data::HistItem * Hist_data::fetch (long index) { return (index < VecSize (hist_items)) ? hist_items->get (index) : NULL; } int Hist_data::sort_compare (HistItem *hi_1, HistItem *hi_2, Sort_type stype, long ind, Hist_data *hdata) { // Sort the data depending upon order and type int result = 0; Histable::Type type = hi_1->obj->get_type (); if (stype == ALPHA) { if (type != Histable::MEMOBJ && type != Histable::INDEXOBJ && type != Histable::IOACTVFD && type != Histable::IOACTFILE && type != Histable::IOCALLSTACK) { char *nm1 = hi_1->obj->get_name (); char *nm2 = hi_2->obj->get_name (); if (nm1 != NULL && nm2 != NULL) result = strcoll (nm1, nm2); } else if (type == Histable::IOCALLSTACK || type == Histable::IOACTVFD || type == Histable::IOACTFILE) { uint64_t idx1, idx2; idx1 = ((FileData *) (hi_1->obj))->get_index (); idx2 = ((FileData *) (hi_2->obj))->get_index (); if (idx1 < idx2) result = -1; else if (idx1 > idx2) result = 1; else result = 0; } else { // for memory and index objects, "alphabetic" is really by index // has index -2, and always comes first // has index -1, and always comes second. uint64_t i1, i2; bool needsStringCompare = false; if (type == Histable::MEMOBJ) { i1 = ((MemObj *) (hi_1->obj))->get_index (); i2 = ((MemObj *) (hi_2->obj))->get_index (); } else if (type == Histable::INDEXOBJ) { i1 = ((IndexObject *) (hi_1->obj))->get_index (); i2 = ((IndexObject *) (hi_2->obj))->get_index (); needsStringCompare = ((IndexObject *) (hi_1->obj))->requires_string_sort (); } else abort (); if (i1 == (uint64_t) - 2) result = -1; else if (i2 == (uint64_t) - 2) result = 1; else if (i1 == (uint64_t) - 1) result = -1; else if (i2 == (uint64_t) - 1) result = 1; else if (needsStringCompare) { char *nm1 = hi_1->obj->get_name (); char *nm2 = hi_2->obj->get_name (); if (nm1 != NULL && nm2 != NULL) { char nm1_lead = nm1[0]; char nm2_lead = nm2[0]; // put "(unknown)" and friends at end of list if (nm1_lead == '(' && nm1_lead != nm2_lead) result = 1; else if (nm2_lead == '(' && nm1_lead != nm2_lead) result = -1; else result = strcoll (nm1, nm2); } } if (result == 0) { // matches, resolve by index if (i1 < i2) result = -1; else if (i1 > i2) result = 1; } } } else if (stype == AUX) { switch (type) { case Histable::INSTR: { DbeInstr *instr1 = (DbeInstr*) hi_1->obj; DbeInstr *instr2 = (DbeInstr*) hi_2->obj; result = instr1 ? instr1->pc_cmp (instr2) : instr2 ? 1 : 0; break; } case Histable::LINE: { DbeLine *dbl1 = (DbeLine*) hi_1->obj; DbeLine *dbl2 = (DbeLine*) hi_2->obj; result = dbl1->line_cmp (dbl2); } break; default: assert (0); } } else if (stype == VALUE) { Metric *m = hdata->get_metric_list ()->get (ind); if ((m->get_visbits () & (VAL_DELTA | VAL_RATIO)) != 0) { TValue v1, v2; int first_ind = hdata->hist_metrics[ind].indFirstExp; if ((m->get_visbits () & VAL_DELTA) != 0) { v1.make_delta (hi_1->value + ind, hi_1->value + first_ind); v2.make_delta (hi_2->value + ind, hi_2->value + first_ind); } else { v1.make_ratio (hi_1->value + ind, hi_1->value + first_ind); v2.make_ratio (hi_2->value + ind, hi_2->value + first_ind); } result = v1.compare (&v2); } else result = hi_1->value[ind].compare (hi_2->value + ind); } return result; } int Hist_data::sort_compare_all (const void *a, const void *b, const void *arg) { HistItem *hi_1 = *((HistItem **) a); HistItem *hi_2 = *((HistItem **) b); Hist_data *hdata = (Hist_data*) arg; int result = sort_compare (hi_1, hi_2, hdata->sort_type, hdata->sort_ind, hdata); if (hdata->sort_order == DESCEND) result = -result; // Use the name as the 2d sort key (always ASCEND) // except for MemoryObjects and IndexObjects, where the index is used // For the Alphabetic sort if (result == 0) { result = sort_compare (hi_1, hi_2, ALPHA, 0, NULL); if (result == 0) { for (long i = 0, sz = hdata->metrics->size (); i < sz; i++) { Metric *m = hdata->metrics->get (i); if (m->get_type () != Metric::ONAME) { result = sort_compare (hi_1, hi_2, VALUE, i, hdata); if (result != 0) { if (hdata->sort_order == DESCEND) result = -result; break; } } } } } // Use the address as the 3d sort key // ( FUNCTION only, always ASCEND ) if (result == 0 && hi_1->obj->get_type () == Histable::FUNCTION) { Function *f1 = (Function*) hi_1->obj; Function *f2 = (Function*) hi_2->obj; if (f1->get_addr () < f2->get_addr ()) result = -1; else if (f1->get_addr () > f2->get_addr ()) result = 1; } // Use the Histable id (ID of function, line, etc.) as the 4th sort key // Note that IDs are not guaranteed to be stable, if (result == 0) { if (hi_1->obj->id < hi_2->obj->id) result = -1; else if (hi_1->obj->id > hi_2->obj->id) result = 1; } if (result == 0) return result; // shouldn't happen in most cases; line allows for breakpoint if (hdata->rev_sort) result = -result; return result; } int Hist_data::sort_compare_dlayout (const void *a, const void *b, const void *arg) { assert ((a != (const void *) NULL)); assert ((b != (const void *) NULL)); HistItem *hi_1 = *((HistItem **) a); HistItem *hi_2 = *((HistItem **) b); DataObject * dobj1 = (DataObject *) (hi_1->obj); DataObject * dobj2 = (DataObject *) (hi_2->obj); DataObject * parent1 = dobj1->parent; DataObject * parent2 = dobj2->parent; Hist_data *hdata = (Hist_data*) arg; // are the two items members of the same object? if (parent1 == parent2) { // yes if (parent1) { // and they have real parents... if (parent1->get_typename ()) { // element // use dobj1/dobj2 offset for sorting uint64_t off1 = dobj1->get_offset (); uint64_t off2 = dobj2->get_offset (); if (off1 < off2) return -1; if (off1 > off2) return 1; return 0; } } } else { // parents differ if (parent1) { if (parent1 == dobj2) // sorting an object and its parent: parent always first return 1; dobj1 = parent1; } if (parent2) { if (parent2 == dobj1) return -1; dobj2 = parent2; } } // Either two unknowns, or two scalars, or two parents hi_1 = hdata->hi_map->get (dobj1); hi_2 = hdata->hi_map->get (dobj2); return sort_compare_all ((const void*) &hi_1, (const void*) &hi_2, hdata); } Hist_data::Hist_data (MetricList *_metrics, Histable::Type _type, Hist_data::Mode _mode, bool _viewowned) { hist_items = new Vector; metrics = _metrics; nmetrics = metrics->get_items ()->size (); type = _type; mode = _mode; gprof_item = new_hist_item (NULL); viewowned = _viewowned; sort_ind = -1; rev_sort = false; Histable *tobj = new Other; tobj->name = dbe_strdup (NTXT ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")); minimum = new_hist_item (tobj); tobj = new Other; tobj->name = dbe_strdup (NTXT ("")); maximum = new_hist_item (tobj); tobj = new Other; tobj->name = dbe_strdup (NTXT ("xxxxxxxxxxxxxxxxxxxxxx")); maximum_inc = new_hist_item (tobj); tobj = new Other; tobj->name = dbe_strdup (NTXT ("")); total = new_hist_item (tobj); tobj = new Other; tobj->name = dbe_strdup (NTXT ("XXXX Threshold XXXX")); threshold = new_hist_item (tobj); hi_map = new HashMap; callsite_mark = new DefaultMap; hist_metrics = new Metric::HistMetric[metrics->size ()]; for (long i = 0, sz = metrics->size (); i < sz; i++) { Metric::HistMetric *h = hist_metrics + i; h->init (); Metric *m = metrics->get (i); if (0 != (m->get_visbits () & (VAL_DELTA | VAL_RATIO))) h->indFirstExp = metrics->get_listorder (m->get_cmd (), m->get_subtype (), "EXPGRID==1"); if (m->is_tvisible () && m->get_type () == BaseMetric::HWCNTR && m->get_dependent_bm ()) h->indTimeVal = metrics->get_listorder (m->get_dependent_bm ()->get_cmd (), m->get_subtype (), m->get_expr_spec ()); } status = NO_DATA; } Hist_data::~Hist_data () { delete[] hist_metrics; if (hist_items) { hist_items->destroy (); delete hist_items; hist_items = NULL; } if (gprof_item) { delete gprof_item; gprof_item = NULL; } if (maximum) { delete maximum->obj; delete maximum; maximum = NULL; } if (maximum_inc) { delete maximum_inc->obj; delete maximum_inc; maximum_inc = NULL; } if (minimum) { delete minimum->obj; delete minimum; minimum = NULL; } if (total) { delete total->obj; delete total; total = NULL; } if (threshold) { delete threshold->obj; delete threshold; threshold = NULL; } delete metrics; delete hi_map; delete callsite_mark; } void Hist_data::dump (char *msg, FILE *f) { fprintf (f, " Hist_data dump: %s\n", msg); fprintf (f, " %d=%d metrics\n", (int) nmetrics, (int) metrics->size ()); for (int i = 0; i < nmetrics; i++) { Metric *m = metrics->get_items ()->fetch (i); char *s = m->get_expr_spec (); fprintf (f, " %4d %15s %4d %15s\n", i, m->get_mcmd (0), m->get_id (), s ? s : "(NULL)"); } fprintf (f, NTXT (" HistItem listing\n")); int n = hist_items->size (); for (int j = -1; j < n; j++) { HistItem *hi; if (j < 0) { hi = total; fprintf (f, NTXT (" total")); } else { hi = hist_items->fetch (j); fprintf (f, NTXT ("%30s"), hi->obj->get_name ()); } for (int i = 0; i < nmetrics; i++) { char *stmp = hi->value[i].l; switch (hi->value[i].tag) { case VT_SHORT: fprintf (f, NTXT (" %d"), hi->value[i].s); break; case VT_INT: fprintf (f, NTXT (" %d"), hi->value[i].i); break; case VT_LLONG: fprintf (f, NTXT (" %12lld"), hi->value[i].ll); break; case VT_FLOAT: fprintf (f, NTXT (" %f"), hi->value[i].f); break; case VT_DOUBLE: fprintf (f, NTXT (" %12.6lf"), hi->value[i].d); break; case VT_HRTIME: fprintf (f, NTXT (" %12llu"), hi->value[i].ull); break; case VT_LABEL: fprintf (f, NTXT (" %s"), stmp ? stmp: "(unnamed)"); break; case VT_ADDRESS: fprintf (f, NTXT (" %12lld"), hi->value[i].ll); break; case VT_OFFSET: fprintf (f, NTXT (" %p"), hi->value[i].p); break; case VT_ULLONG: fprintf (f, NTXT (" %12llu"), hi->value[i].ull); break; default: fprintf (f, NTXT (" ")); break; } } fprintf (f, NTXT ("\n")); } } void Hist_data::sort (long ind, bool reverse) { if (mode != MODL && ind != -1 && ind == sort_ind && reverse == rev_sort) // there's no change to the sorting return; if (mode == MODL) { sort_type = AUX; sort_order = ASCEND; } else { if (ind == -1) return; Metric::Type mtype = metrics->get_items ()->fetch (ind)->get_type (); sort_type = mtype == Metric::ONAME ? ALPHA : VALUE; sort_order = (mtype == Metric::ONAME || mtype == Metric::ADDRESS) ? ASCEND : DESCEND; sort_ind = ind; rev_sort = reverse; } if (mode == Hist_data::LAYOUT || mode == Hist_data::DETAIL) hist_items->sort ((CompareFunc) sort_compare_dlayout, this); else hist_items->sort ((CompareFunc) sort_compare_all, this); // ensure that comes first/last char *tname = NTXT (""); for (int i = 0; i < hist_items->size (); ++i) { HistItem *hi = hist_items->fetch (i); char *name = hi->obj->get_name (); if (name != NULL && streq (name, tname)) { int idx0 = rev_sort ? hist_items->size () - 1 : 0; if (i != idx0) { hist_items->remove (i); hist_items->insert (idx0, hi); } break; } } } void Hist_data::resort (MetricList *mlist) { if (mlist->get_type () != metrics->get_type ()) if (metrics->get_type () == MET_CALL) // wrong type of list -- internal error abort (); // get the new sort order int ind = mlist->get_sort_ref_index (); bool reverse = mlist->get_sort_rev (); sort (ind, reverse); } void Hist_data::compute_minmax () { HistItem *hi; int index; for (int mind = 0; mind < nmetrics; mind++) { Metric *mtr = metrics->get_items ()->fetch (mind); if (mtr->get_subtype () == Metric::STATIC) continue; if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) continue; ValueTag vtype = mtr->get_vtype2 (); switch (vtype) { case VT_INT: minimum->value[mind].tag = VT_INT; minimum->value[mind].i = 0; maximum->value[mind].tag = VT_INT; maximum->value[mind].i = 0; maximum_inc->value[mind].tag = VT_INT; maximum_inc->value[mind].i = 0; Vec_loop (HistItem *, hist_items, index, hi) { if (metrics->get_type () == MET_SRCDIS && callsite_mark->get (hi->obj)) { if (hi->value[mind].i > maximum_inc->value[mind].i) maximum_inc->value[mind].i = hi->value[mind].i; // ignore ones that has inclusive time for src/dis view } else if (hi->value[mind].i > maximum->value[mind].i) maximum->value[mind].i = hi->value[mind].i; if (hi->value[mind].i < minimum->value[mind].i) minimum->value[mind].i = hi->value[mind].i; } break; case VT_DOUBLE: minimum->value[mind].tag = VT_DOUBLE; minimum->value[mind].d = 0.0; maximum->value[mind].tag = VT_DOUBLE; maximum->value[mind].d = 0.0; maximum_inc->value[mind].tag = VT_DOUBLE; maximum_inc->value[mind].d = 0.0; Vec_loop (HistItem*, hist_items, index, hi) { if (metrics->get_type () == MET_SRCDIS && callsite_mark->get (hi->obj)) { if (hi->value[mind].d > maximum_inc->value[mind].d) { maximum_inc->value[mind].d = hi->value[mind].d; maximum_inc->value[mind].sign = hi->value[mind].sign; } // ignore ones that has inclusive time for src/dis view } else { if (hi->value[mind].d > maximum->value[mind].d) { maximum->value[mind].d = hi->value[mind].d; maximum->value[mind].sign = hi->value[mind].sign; } if (hi->value[mind].d < minimum->value[mind].d) { minimum->value[mind].d = hi->value[mind].d; minimum->value[mind].sign = hi->value[mind].sign; } } } break; case VT_LLONG: case VT_ULLONG: case VT_ADDRESS: minimum->value[mind].tag = vtype; minimum->value[mind].ll = 0; maximum->value[mind].tag = vtype; maximum->value[mind].ll = 0; maximum_inc->value[mind].tag = vtype; maximum_inc->value[mind].ll = 0; Vec_loop (HistItem*, hist_items, index, hi) { if (metrics->get_type () == MET_SRCDIS && callsite_mark->get (hi->obj)) { if (hi->value[mind].ll > maximum_inc->value[mind].ll) { maximum_inc->value[mind].ll = hi->value[mind].ll; maximum_inc->value[mind].sign = hi->value[mind].sign; } // ignore ones that has inclusive time for src/dis view } else { if (hi->value[mind].ll > maximum->value[mind].ll) { maximum->value[mind].ll = hi->value[mind].ll; maximum->value[mind].sign = hi->value[mind].sign; } if (hi->value[mind].ll < minimum->value[mind].ll) { minimum->value[mind].ll = hi->value[mind].ll; minimum->value[mind].sign = hi->value[mind].sign; } } } break; default: break; } } } Hist_data::HistItem * Hist_data::new_hist_item (Histable *obj) { long sz = get_metric_list ()->size (); HistItem *hi = new HistItem (sz); hi->obj = obj; // We precalculate all metrics as integer values // and convert them to appropriate types later. for (long i = 0; i < sz; i++) { hi->value[i].tag = VT_INT; hi->value[i].i = 0; } return hi; } Hist_data::HistItem * Hist_data::new_hist_item (Histable *obj, int itype, TValue *value) { long sz = get_metric_list ()->size (); HistItem *hi = new HistItem (sz); hi->obj = obj; hi->type = itype; if (value) for (long i = 0; i < sz; i++) hi->value[i] = value[i]; return hi; } Hist_data::HistItem * Hist_data::find_hist_item (Histable *obj) { if (obj == NULL) return NULL; return hi_map->get (obj); } Hist_data::HistItem * Hist_data::append_hist_item (Histable *obj) { if (obj == NULL) return NULL; HistItem *hi = hi_map->get (obj); if (hi == NULL) { hi = new_hist_item (obj); hist_items->append (hi); hi_map->put (obj, hi); } if (status == NO_DATA) status = SUCCESS; return hi; } void Hist_data::append_hist_item (HistItem *hi) { hist_items->append (hi); } bool Hist_data::above_threshold (HistItem* hi) { bool mark = false; int index; Metric *mitem; Vec_loop (Metric*, metrics->get_items (), index, mitem) { if (mitem->get_subtype () == Metric::STATIC) continue; switch (hi->value[index].tag) { case VT_DOUBLE: if (hi->value[index].d > threshold->value[index].d) mark = true; break; case VT_INT: if (hi->value[index].i > threshold->value[index].i) mark = true; break; case VT_LLONG: if (hi->value[index].ll > threshold->value[index].ll) mark = true; break; case VT_ULLONG: if (hi->value[index].ull > threshold->value[index].ull) mark = true; break; // ignoring the following cases (why?) case VT_SHORT: case VT_FLOAT: case VT_HRTIME: case VT_LABEL: case VT_ADDRESS: case VT_OFFSET: break; } } return mark; } void Hist_data::set_threshold (double proportion) { int index; Metric *mitem; Vec_loop (Metric*, metrics->get_items (), index, mitem) { TValue *thresh = &threshold->value[index]; TValue *mtotal = &total->value[index]; thresh->tag = mitem->get_vtype (); if (mitem->get_subtype () == Metric::STATIC) continue; switch (thresh->tag) { case VT_INT: thresh->i = (int) (proportion * (double) mtotal->i); break; case VT_DOUBLE: thresh->d = proportion * mtotal->d; break; case VT_LLONG: case VT_ULLONG: thresh->ull = (unsigned long long) (proportion * (double) mtotal->ll); break; case VT_SHORT: case VT_FLOAT: case VT_HRTIME: case VT_LABEL: case VT_ADDRESS: case VT_OFFSET: break; } } } double Hist_data::get_percentage (double value, int mindex) { double total_value; if (value == 0.0) return 0.0; // Get the total value of this sample set. // The value must be greater than 0. total_value = total->value[mindex].to_double (); // Find out what percentage of the total value this item is. // Make sure we don't divide by zero. if (total_value == 0.0) return 0.0; return value / total_value; } int Hist_data::print_label (FILE *out_file, Metric::HistMetric *hist_metric, int space) { int name_offset = 0; StringBuilder sb, sb1, sb2, sb3; if (space > 0) { char *fmt = NTXT ("%*s"); sb.appendf (fmt, space, NTXT ("")); sb1.appendf (fmt, space, NTXT ("")); sb2.appendf (fmt, space, NTXT ("")); sb3.appendf (fmt, space, NTXT ("")); } for (int i = 0; i < nmetrics; i++) { Metric *m = metrics->get (i); Metric::HistMetric *hm = &hist_metric[i]; int len = hm->width; char *fmt = NTXT ("%-*s"); if ((i > 0) && (m->get_type () == Metric::ONAME)) { name_offset = sb1.length (); fmt = NTXT (" %-*s"); len--; } sb.appendf (fmt, len, m->legend ? m->legend : NTXT ("")); sb1.appendf (fmt, len, hm->legend1); sb2.appendf (fmt, len, hm->legend2); sb3.appendf (fmt, len, hm->legend3); } sb.trim (); if (sb.length () != 0) { sb.toFileLn (out_file); } sb1.toFileLn (out_file); sb2.toFileLn (out_file); sb3.toFileLn (out_file); return name_offset; } void Hist_data::print_content (FILE *out_file, Metric::HistMetric *hist_metric, int limit) { StringBuilder sb; int cnt = VecSize (hist_items); if (cnt > limit && limit > 0) cnt = limit; for (int i = 0; i < cnt; i++) { sb.setLength (0); print_row (&sb, i, hist_metric, NTXT (" ")); sb.toFileLn (out_file); } } static void append_str (StringBuilder *sb, char *s, size_t len, int vis_bits) { if ((vis_bits & VAL_RATIO) != 0) { if (*s != 'N') // Nan sb->appendf (NTXT ("x ")); else sb->appendf (NTXT (" ")); sb->appendf (NTXT ("%*s"), (int) (len - 2), s); } else sb->appendf (NTXT ("%*s"), (int) len, s); } void Hist_data::print_row (StringBuilder *sb, int row, Metric::HistMetric *hmp, char *mark) { TValue res; char buf[256]; // Print only a list of user's metrics. ( nmetrics <= mlist->size() ) for (long i = 0; i < nmetrics; i++) { // Print only a list of user's metrics. Metric *m = metrics->get (i); if (!m->is_any_visible ()) continue; Metric::HistMetric *hm = hmp + i; int len = sb->length (); if (m->is_tvisible ()) { TValue *v = get_value (&res, hist_metrics[i].indTimeVal, row); char *s = v->to_str (buf, sizeof (buf)); append_str (sb, s, hm->maxtime_width, m->get_visbits ()); } if (m->is_visible ()) { TValue *v = get_value (&res, i, row); char *s = v->to_str (buf, sizeof (buf)); if (m->get_type () == BaseMetric::ONAME) { sb->append (mark); if (i + 1 == nmetrics) sb->appendf (NTXT ("%s"), s); else sb->appendf (NTXT ("%-*s "), (int) hm->maxvalue_width, s); continue; } else { if (len != sb->length ()) sb->append (' '); append_str (sb, s, hm->maxvalue_width, m->get_visbits ()); } } if (m->is_pvisible ()) { if (len != sb->length ()) sb->append (' '); long met_ind = i; if (m->is_tvisible () && !m->is_visible ()) met_ind = hist_metrics[i].indTimeVal; TValue *v = get_real_value (&res, met_ind, row); double percent = get_percentage (v->to_double (), met_ind); if (percent == 0.0) // adjust to change format from xx.yy% sb->append (NTXT (" 0. ")); else // adjust format below to change format from xx.yy% sb->appendf (NTXT ("%6.2f"), (100.0 * percent)); } len = sb->length () - len; if (hm->width > len && i + 1 != nmetrics) sb->appendf (NTXT ("%*s"), (int) (hm->width - len), NTXT (" ")); } } TValue * Hist_data::get_real_value (TValue *res, int met_index, int row) { HistItem *hi = hist_items->get (row); Metric *m = metrics->get (met_index); if (m->get_type () == BaseMetric::ONAME) { res->l = dbe_strdup (hi->obj->get_name ()); res->tag = VT_LABEL; return res; } return hi->value + met_index; } TValue * Hist_data::get_value (TValue *res, int met_index, int row) { HistItem *hi = hist_items->get (row); Metric *m = metrics->get (met_index); if ((m->get_visbits () & (VAL_DELTA | VAL_RATIO)) != 0) { int ind = hist_metrics[met_index].indFirstExp; if ((m->get_visbits () & VAL_DELTA) != 0) res->make_delta (hi->value + met_index, hi->value + ind); else res->make_ratio (hi->value + met_index, hi->value + ind); return res; } return get_real_value (res, met_index, row); } TValue * Hist_data::get_value (TValue *res, int met_index, HistItem *hi) { Metric *m = metrics->get (met_index); if ((m->get_visbits () & (VAL_DELTA | VAL_RATIO)) != 0) { int ind = hist_metrics[met_index].indFirstExp; if ((m->get_visbits () & VAL_DELTA) != 0) res->make_delta (hi->value + met_index, hi->value + ind); else res->make_ratio (hi->value + met_index, hi->value + ind); return res; } if (m->get_type () == BaseMetric::ONAME) { res->l = dbe_strdup (hi->obj->get_name ()); res->tag = VT_LABEL; return res; } return hi->value + met_index; } Metric::HistMetric * Hist_data::get_histmetrics () { // find the width for each column. for (long i = 0, sz = metrics->size (); i < sz; i++) { Metric *m = metrics->get (i); Metric::HistMetric *hm = hist_metrics + i; if (m->is_value_visible ()) { TValue res; for (long i1 = 0, sz1 = VecSize(hist_items); i1 < sz1; i1++) { TValue *v = get_value (&res, i, i1); long len = v->get_len (); if (hm->maxvalue_width < len) hm->maxvalue_width = len; } if ((m->get_visbits () & VAL_RATIO) != 0) hm->maxvalue_width += 2; // "x " } } for (long i = 0, sz = metrics->size (); i < sz; i++) { Metric *m = metrics->get (i); Metric::HistMetric *hm = hist_metrics + i; if (m->is_time_visible ()) // take a value from depended metric hm->maxtime_width = hist_metrics[hm->indTimeVal].maxvalue_width; m->legend_width (hm, 2); } return hist_metrics; } void Hist_data::update_total (Hist_data::HistItem *new_total) { for (long i = 0, sz = metrics->size (); i < sz; i++) total->value[i] = new_total->value[i]; } void Hist_data::update_max (Metric::HistMetric *hm_tmp) { Metric::HistMetric *hms = get_histmetrics (); for (int i = 0; i < nmetrics; i++) { Metric::HistMetric *hm = hms + i; Metric::HistMetric *hm1 = hm_tmp + i; if (hm1->maxtime_width < hm->maxtime_width) hm1->maxtime_width = hm->maxtime_width; if (hm1->maxvalue_width < hm->maxvalue_width) hm1->maxvalue_width = hm->maxvalue_width; } } void Hist_data::update_legend_width (Metric::HistMetric *hm_tmp) { for (int i = 0; i < nmetrics; i++) { Metric *m = metrics->get (i); m->legend_width (hm_tmp + i, 2); } } void Metric::HistMetric::update_max (Metric::HistMetric *hm) { if (maxtime_width < hm->maxtime_width) maxtime_width = hm->maxtime_width; if (maxvalue_width < hm->maxvalue_width) maxvalue_width = hm->maxvalue_width; } void Metric::HistMetric::init () { width = 0; maxvalue_width = 0; maxtime_width = 0; legend1[0] = 0; legend2[0] = 0; legend3[0] = 0; indFirstExp = -1; indTimeVal = -1; } size_t Hist_data::value_maxlen (int mindex) { size_t maxlen = maximum->value[mindex].get_len (); size_t minlen = minimum->value[mindex].get_len (); // minlen can be bigger than maxlen only for negative value return minlen > maxlen ? minlen : maxlen; } size_t Hist_data::time_len (TValue *value, int clock) { TValue tm_value; tm_value.tag = VT_DOUBLE; tm_value.sign = value->sign; tm_value.d = 1.e-6 * value->ll / clock; return tm_value.get_len (); } size_t Hist_data::time_maxlen (int mindex, int clock) { size_t maxlen = time_len (&(maximum->value[mindex]), clock); size_t minlen = time_len (&(minimum->value[mindex]), clock); // minlen can be bigger than maxlen only for negative value return minlen > maxlen ? minlen : maxlen; } size_t Hist_data::name_len (HistItem *item) { char *name = item->obj->get_name (); return strlen (name); } size_t Hist_data::name_maxlen () { size_t res = 0; for (long i = 0; i < size (); i++) { HistItem *hi = fetch (i); size_t len = name_len (hi); if (res < len) res = len; } return res; } // Returns vector of object ids for the vector of selections // returns NULL if no valid selections Vector * Hist_data::get_object_indices (Vector *selections) { // if no selections, return NULL if (selections == NULL || selections->size () == 0) return NULL; Vector *indices = new Vector; for (long i = 0, sz = selections->size (); i < sz; i++) { int sel = selections->get (i); HistItem *hi = hist_items->get (sel); if (hi == NULL || hi->obj == NULL) continue; Vector *v = hi->obj->get_comparable_objs (); for (long i1 = 0, sz1 = v ? v->size () : 0; i1 < sz1; i1++) { Histable *h1 = v->get (i1); if (h1 && (indices->find_r (h1->id) < 0)) indices->append (h1->id); } if (indices->find_r (hi->obj->id) < 0) indices->append (hi->obj->id); } return indices; } DbeInstr::DbeInstr (uint64_t _id, int _flags, Function *_func, uint64_t _addr) { id = _id; flags = _flags; addr = _addr; func = _func; img_offset = addr + func->img_offset; lineno = -1; size = 0; current_name_format = NA; isUsed = false; inlinedInd = -1; } int DbeInstr::pc_cmp (DbeInstr *instr2) { int result = 0; if (instr2 == NULL) return -1; // All PC's with the Line flag go to the // end of the list. See Module::init_index() if (flags & PCLineFlag) { if (instr2->flags & PCLineFlag) { if (addr < instr2->addr) result = -1; else if (addr > instr2->addr) result = 1; else result = 0; } else result = 1; } else if (instr2->flags & PCLineFlag) result = -1; else if (func == instr2->func) { if (size == 0) { if (addr < instr2->addr) result = -1; else if (addr == instr2->addr) result = 0; else if (addr >= instr2->addr + instr2->size) result = 1; else result = 0; } else if (instr2->size == 0) { if (addr > instr2->addr) result = 1; else if (addr + size <= instr2->addr) result = -1; else result = 0; } else if (addr < instr2->addr) result = -1; else if (addr > instr2->addr) result = 1; else result = 0; if (result == 0) { if (flags & PCTrgtFlag) { if (!(instr2->flags & PCTrgtFlag)) result = -1; } else if (instr2->flags & PCTrgtFlag) result = 1; } } else result = func->func_cmp (instr2->func); return result; } char * DbeInstr::get_name (NameFormat nfmt) { if (name && (nfmt == current_name_format || nfmt == Histable::NA)) return name; free (name); name = NULL; current_name_format = nfmt; char *fname = func->get_name (nfmt); if (func->flags & FUNC_FLAG_NO_OFFSET) name = dbe_strdup (fname); else if (addr == (uint64_t) - 1 && func != dbeSession->get_JUnknown_Function ()) // We use three heuristics above to recognize this special case. // Once the original problem with bci == -1 is fixed, we don't // need it any more. name = dbe_sprintf (GTXT (""), fname); else if (addr == (uint64_t) - 3) name = dbe_sprintf (GTXT ("%s "), fname); else { char buf[64], *typetag = NTXT (""), *alloc_typetag = NULL; StringBuilder sb; sb.append (fname); if (func != dbeSession->get_JUnknown_Function ()) { if (addr <= 0xFFFFFFFFU) snprintf (buf, sizeof (buf), " + 0x%08X", (unsigned int) addr); else snprintf (buf, sizeof (buf), " + 0x%016llX", (unsigned long long) addr); } else { char *subname; switch ((long int) addr) { case -1: subname = GTXT ("agent error"); break; case -2: subname = GTXT ("GC active"); break; case -3: subname = GTXT ("unknown non-Java frame"); break; case -4: subname = GTXT ("unwalkable non-Java frame"); break; case -5: subname = GTXT ("unknown Java frame"); break; case -6: subname = GTXT ("unwalkable Java frame"); break; case -7: subname = GTXT ("unknown thread state"); break; case -8: subname = GTXT ("thread in exit"); break; case -9: subname = GTXT ("deopt in process ticks"); break; case -10: subname = GTXT ("safepoint synchronizing ticks"); break; default: subname = GTXT ("unexpected error"); break; } snprintf (buf, sizeof (buf), "<%s (%d)>", subname, (int) addr); } sb.append (buf); if (flags & PCTrgtFlag) // annotate synthetic instruction sb.append ('*'); // special distinguishing marker DbeLine *dbeline = mapPCtoLine (NULL); char *str = NULL; if (dbeline && dbeline->lineno > 0) str = strrchr (dbeline->get_name (nfmt), ','); if (str) sb.append (str); if (strlen (typetag) > 0) { // include padding for alignment do { sb.append (' '); } while (sb.length () < 40); sb.append (typetag); delete alloc_typetag; } if (inlinedInd >= 0) add_inlined_info (&sb); name = sb.toString (); } return name; } DbeLine* DbeInstr::mapPCtoLine (SourceFile *sf) { if (inlinedInd == -1) { inlinedInd = -2; for (int i = 0; i < func->inlinedSubrCnt; i++) { InlinedSubr *p = func->inlinedSubr + i; if (p->level == 0) { if (addr < p->low_pc) break; if (p->contains (addr)) { inlinedInd = i; break; } } } } if (inlinedInd >= 0) { DbeLine *dl = func->inlinedSubr[inlinedInd].dbeLine; return dl->sourceFile->find_dbeline (func, dl->lineno); } return func->mapPCtoLine (addr, sf); } void DbeInstr::add_inlined_info (StringBuilder *sb) { do { sb->append (' '); } while (sb->length () < 40); sb->append (NTXT ("<-- ")); InlinedSubr *last = NULL; for (int i = inlinedInd; i < func->inlinedSubrCnt; i++) { InlinedSubr *p = func->inlinedSubr + i; if (p->level == 0 && i > inlinedInd) break; if (!p->contains (addr)) continue; if (last) { if (last->fname) { sb->append (last->fname); sb->append (' '); } DbeLine *dl = p->dbeLine; sb->appendf (NTXT ("%s:%lld <-- "), get_basename (dl->sourceFile->get_name ()), (long long) dl->lineno); } last = p; } if (last) { if (last->fname) { sb->append (last->fname); sb->append (' '); } } DbeLine *dl = func->mapPCtoLine (addr, NULL); sb->appendf ("%s:%lld ", get_basename (dl->sourceFile->get_name ()), (long long) dl->lineno); } char * DbeInstr::get_descriptor () { char *typetag = NTXT (""); if ((flags & PCTrgtFlag) == 0) // not synthetic instruction { // use memop descriptor, if available Module *mod = func->module; if (mod->hwcprof && mod->infoList) { long i; inst_info_t *info = NULL; Vec_loop (inst_info_t*, mod->infoList, i, info) { if (info->offset == func->img_offset + addr) break; } if (info) { long t; datatype_t *dtype = NULL; Vec_loop (datatype_t*, mod->datatypes, t, dtype) { if (dtype->datatype_id == info->memop->datatype_id) break; } if (dtype && dtype->dobj) typetag = dtype->dobj->get_name (); } } } return dbe_strdup (typetag); } int64_t DbeInstr::get_size () { // Function *func = (Function*)dbeSession->get_hobj( pc ); // Module *mod = func ? func->module : NULL; // return mod ? mod->instrSize( func->img_offset + addr ) : 0; return size; } uint64_t DbeInstr::get_addr () { return func->get_addr () + addr; } Histable * DbeInstr::convertto (Type type, Histable *obj) { Histable *res = NULL; SourceFile *source = (SourceFile*) obj; switch (type) { case INSTR: res = this; break; case LINE: res = mapPCtoLine (source); break; case SOURCEFILE: res = mapPCtoLine (source); if (res) res = ((DbeLine*) res)->sourceFile; break; case FUNCTION: res = func; break; default: assert (0); } return res; } char * DbeEA::get_name (NameFormat) { if (name == NULL) // generate one name = dbe_strdup (dbeSession->localized_SP_UNKNOWN_NAME); return name; } Histable * DbeEA::convertto (Type type, Histable *obj) { Histable *res = NULL; assert (obj == NULL); switch (type) { case EADDR: res = this; break; case DOBJECT: res = dobj; break; default: assert (0); } return res; } DbeLine::DbeLine (Function *_func, SourceFile *sf, int _lineno) { func = _func; lineno = _lineno; sourceFile = sf; id = sf->id + _lineno; offset = 0; size = 0; flags = 0; include = NULL; dbeline_func_next = NULL; dbeline_base = this; current_name_format = Histable::NA; } DbeLine::~DbeLine () { delete dbeline_func_next; } int DbeLine::line_cmp (DbeLine *dbl) { return lineno - dbl->lineno; } void DbeLine::init_Offset (uint64_t p_offset) { if (offset == 0) offset = p_offset; if (dbeline_base && dbeline_base->offset == 0) dbeline_base->offset = p_offset; } char * DbeLine::get_name (NameFormat nfmt) { char *srcname = NULL, *basename, *fname; if (func == NULL) { if (name) return name; srcname = sourceFile->get_name (); basename = get_basename (srcname); name = dbe_sprintf (GTXT ("line %u in \"%s\""), lineno, basename); return name; } if (name && (nfmt == current_name_format || nfmt == Histable::NA)) return name; current_name_format = nfmt; free (name); fname = func->get_name (nfmt); if (func->flags & (FUNC_FLAG_SIMULATED | FUNC_FLAG_NO_OFFSET)) { name = dbe_strdup (fname); return name; } if (sourceFile) srcname = sourceFile->get_name (); if (!srcname || strlen (srcname) == 0) srcname = func->getDefSrcName (); basename = get_basename (srcname); if (lineno != 0) { if (sourceFile == func->getDefSrc ()) name = dbe_sprintf (GTXT ("%s, line %u in \"%s\""), fname, lineno, basename); else name = dbe_sprintf (GTXT ("%s, line %u in alternate source context \"%s\""), fname, lineno, basename); } else if (sourceFile == NULL || (sourceFile->flags & SOURCE_FLAG_UNKNOWN) != 0) name = dbe_sprintf (GTXT (""), fname); else name = dbe_sprintf (GTXT (""), fname, basename); return name; } int64_t DbeLine::get_size () { return size; } uint64_t DbeLine::get_addr () { if (func == NULL && dbeline_func_next == NULL) return (uint64_t) 0; Function *f = func ? func : dbeline_func_next->func; return f->get_addr () + offset; } Histable * DbeLine::convertto (Type type, Histable *obj) { Histable *res = NULL; switch (type) { case INSTR: { Function *f = (Function *) convertto (FUNCTION, NULL); if (f) res = f->find_dbeinstr (0, offset); break; } case LINE: res = dbeline_base; break; case FUNCTION: if (func) { res = func; break; } else { int not_found = 1; for (DbeLine *dl = dbeline_base; dl; dl = dl->dbeline_func_next) { Function *f = dl->func; not_found = (obj == NULL // XXXX pass dbeview as Histable* || ((DbeView*) obj)->get_path_tree ()->get_func_nodeidx (f) == 0); if (f && f->def_source == sourceFile && (!not_found)) { res = f; break; } } if (res == NULL && dbeline_func_next) { for (DbeLine *dl = dbeline_base; dl; dl = dl->dbeline_func_next) { Function *f = dl->func; if (f && f->def_source == sourceFile) { res = f; break; } } } if (res == NULL && dbeline_func_next) res = dbeline_func_next->func; } break; case SOURCEFILE: res = (include) ? include : sourceFile; break; default: assert (0); } return res; } CStack_data::CStack_data (MetricList *_metrics) { metrics = _metrics; total = new_cstack_item (); cstack_items = new Vector; } CStack_data::CStack_item::CStack_item (long n) { stack = NULL; count = 0; val = 0; value = new TValue[n]; memset (value, 0, sizeof (TValue) * n); } CStack_data::CStack_item::~CStack_item () { delete stack; delete[] value; } CStack_data::CStack_item * CStack_data::new_cstack_item () { int nmetrics = metrics->get_items ()->size (); CStack_item *item = new CStack_item (nmetrics); // We precalculate all metrics as integer values // and convert them to appropriate types later. for (int i = 0; i < nmetrics; i++) item->value[i].tag = metrics->get_items ()->fetch (i)->get_vtype (); return item; } HistableFile::HistableFile () { dbeFile = NULL; isUsed = false; } Histable::Histable () { name = NULL; id = 0; comparable_objs = NULL; phaseCompareIdx = -1; } Histable::~Histable () { delete_comparable_objs (); free (name); } void Histable::delete_comparable_objs () { if (comparable_objs) { Vector *v = comparable_objs; for (int i = 0; i < v->size (); i++) { Histable *h = v->fetch (i); if (h) { h->comparable_objs = NULL; h->phaseCompareIdx = phaseCompareIdx; } } delete v; } } void Histable::update_comparable_objs () { if (phaseCompareIdx != ExpGroup::phaseCompareIdx) { phaseCompareIdx = ExpGroup::phaseCompareIdx; delete_comparable_objs (); } } Vector * Histable::get_comparable_objs () { return comparable_objs; } Histable * Histable::get_compare_obj () { Vector *v = get_comparable_objs (); for (long i = 0, sz = VecSize (v); i < sz; i++) { Histable *h = v->get (i); if (h) return h; } return this; } #define CASE_S(x) case x: return (char *) #x char * Histable::type_to_string () { switch (get_type ()) { CASE_S (INSTR); CASE_S (LINE); CASE_S (FUNCTION); CASE_S (MODULE); CASE_S (LOADOBJECT); CASE_S (EADDR); CASE_S (MEMOBJ); CASE_S (INDEXOBJ); CASE_S (PAGE); CASE_S (DOBJECT); CASE_S (SOURCEFILE); CASE_S (EXPERIMENT); CASE_S (OTHER); default: break; } return NTXT ("ERROR"); } void Histable::dump_comparable_objs () { Dprintf (DEBUG_COMPARISON, "# Histable::dump_comparable_objs type=%s(%d) 0x%lx id=%lld %s\n", type_to_string (), get_type (), (unsigned long) this, (long long) id, STR (get_name ())); for (int i = 0, sz = comparable_objs ? comparable_objs->size () : 0; i < sz; i++) { Histable *h = comparable_objs->fetch (i); Dprintf (DEBUG_COMPARISON, " %d type=%s(%d) 0x%lx id=%lld %s\n", i, h ? h->type_to_string () : "", h ? h->get_type () : -1, (unsigned long) h, (long long) (h ? h->id : 0), h ? STR (h->get_name ()) : NTXT ("")); } } char * Histable::dump () { StringBuilder sb; sb.appendf (sizeof (long) == 32 ? " 0x%08lx : type=%s(%d) id=%lld %s" : " 0x%016lx : type=%s(%d) id=%lld %s", (unsigned long) this, type_to_string (), get_type (), (long long) id, STR (get_name ())); switch (get_type ()) { case INSTR: { DbeInstr *o = (DbeInstr *) this; sb.appendf (sizeof (long) == 32 ? " func=0x%08lx lineno=%lld" : " func=0x%016lx lineno=%lld", (unsigned long) o->func, (long long) o->lineno); break; } case LINE: { DbeLine *o = (DbeLine *) this; sb.appendf (sizeof (long) == 32 ? " func=0x%08lx sourceFile=0x%08lx lineno=%lld" : " func=0x%016lx sourceFile=0x%016lx lineno=%lld", (unsigned long) o->func, (unsigned long) o->sourceFile, (long long) o->lineno); break; } default: break; } return sb.toString (); }