| /* 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 <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, |
| const 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 (); |
| } |