diff options
Diffstat (limited to 'gprofng/src/Hist_data.cc')
-rw-r--r-- | gprofng/src/Hist_data.cc | 1886 |
1 files changed, 1886 insertions, 0 deletions
diff --git a/gprofng/src/Hist_data.cc b/gprofng/src/Hist_data.cc new file mode 100644 index 0000000..4412203 --- /dev/null +++ b/gprofng/src/Hist_data.cc @@ -0,0 +1,1886 @@ +/* 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 <assert.h> + +#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 + // <Total> has index -2, and always comes first + // <Unknown> 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<HistItem*>; + 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>")); + 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<Histable*, HistItem*>; + callsite_mark = new DefaultMap<Histable*, int>; + 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 <Total> comes first/last + char *tname = NTXT ("<Total>"); + 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<uint64_t> * +Hist_data::get_object_indices (Vector<int> *selections) +{ + // if no selections, return NULL + if (selections == NULL || selections->size () == 0) + return NULL; + + Vector<uint64_t> *indices = new Vector<uint64_t>; + 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<Histable*> *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 ("<Function %s: HotSpot-compiled leaf instructions>"), + fname); + else if (addr == (uint64_t) - 3) + name = dbe_sprintf (GTXT ("%s <Java native method>"), 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 ("<Function: %s, instructions without line numbers>"), + fname); + else + name = dbe_sprintf (GTXT ("<Function: %s, instructions from source file %s>"), + 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_item*>; +} + +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<Histable*> *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*> * +Histable::get_comparable_objs () +{ + return comparable_objs; +} + +Histable * +Histable::get_compare_obj () +{ + Vector<Histable*> *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 (); +} |