mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-07 13:33:06 +00:00
1888 lines
44 KiB
C++
1888 lines
44 KiB
C++
/* 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,
|
|
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 ();
|
|
}
|