mirror of
https://github.com/autc04/Retro68.git
synced 2024-06-07 13:33:06 +00:00
409 lines
11 KiB
C++
409 lines
11 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 "DbeSession.h"
|
|
#include "HeapData.h"
|
|
#include "StringBuilder.h"
|
|
#include "i18n.h"
|
|
#include "util.h"
|
|
#include "HeapActivity.h"
|
|
#include "MetricList.h"
|
|
#include "Application.h"
|
|
#include "Experiment.h"
|
|
#include "DbeView.h"
|
|
#include "Exp_Layout.h"
|
|
#include "i18n.h"
|
|
|
|
HeapActivity::HeapActivity (DbeView *_dbev)
|
|
{
|
|
dbev = _dbev;
|
|
hDataTotal = NULL;
|
|
hDataObjs = NULL;
|
|
hDataObjsCallStack = NULL;
|
|
hasCallStack = false;
|
|
hDataCalStkMap = NULL;
|
|
hist_data_callstack_all = NULL;
|
|
}
|
|
|
|
void
|
|
HeapActivity::reset ()
|
|
{
|
|
delete hDataTotal;
|
|
hDataTotal = NULL;
|
|
delete hDataObjsCallStack;
|
|
hDataObjsCallStack = NULL;
|
|
hasCallStack = false;
|
|
hDataObjs = NULL;
|
|
delete hDataCalStkMap;
|
|
hDataCalStkMap = NULL;
|
|
hist_data_callstack_all = NULL;
|
|
}
|
|
|
|
void
|
|
HeapActivity::createHistItemTotals (Hist_data *hist_data, MetricList *mlist,
|
|
Histable::Type hType, bool empty)
|
|
{
|
|
int mIndex;
|
|
Metric *mtr;
|
|
Hist_data::HistItem *hi;
|
|
HeapData *hData = NULL;
|
|
if (hDataTotal == NULL)
|
|
{
|
|
hDataTotal = new HeapData (TOTAL_HEAPNAME);
|
|
hDataTotal->setHistType (hType);
|
|
hDataTotal->setStackId (TOTAL_STACK_ID);
|
|
hDataTotal->id = 0;
|
|
}
|
|
|
|
hData = new HeapData (hDataTotal);
|
|
hData->setHistType (hType);
|
|
hi = hist_data->append_hist_item (hData);
|
|
|
|
Vec_loop (Metric *, mlist->get_items (), mIndex, mtr)
|
|
{
|
|
if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ())
|
|
continue;
|
|
|
|
Metric::Type mtype = mtr->get_type ();
|
|
ValueTag vType = mtr->get_vtype ();
|
|
|
|
hist_data->total->value[mIndex].tag = vType;
|
|
hi->value[mIndex].tag = vType;
|
|
switch (mtype)
|
|
{
|
|
case BaseMetric::HEAP_ALLOC_BYTES:
|
|
if (!empty)
|
|
{
|
|
hist_data->total->value[mIndex].ll = hDataTotal->getAllocBytes ();
|
|
hi->value[mIndex].ll = hDataTotal->getAllocBytes ();
|
|
}
|
|
else
|
|
{
|
|
hist_data->total->value[mIndex].ll = 0;
|
|
hi->value[mIndex].ll = 0;
|
|
}
|
|
break;
|
|
case BaseMetric::HEAP_ALLOC_CNT:
|
|
if (!empty)
|
|
{
|
|
hist_data->total->value[mIndex].ll = hDataTotal->getAllocCnt ();
|
|
hi->value[mIndex].ll = hDataTotal->getAllocCnt ();
|
|
}
|
|
else
|
|
{
|
|
hist_data->total->value[mIndex].ll = 0;
|
|
hi->value[mIndex].ll = 0;
|
|
}
|
|
break;
|
|
case BaseMetric::HEAP_LEAK_BYTES:
|
|
if (!empty)
|
|
{
|
|
hist_data->total->value[mIndex].ll = hDataTotal->getLeakBytes ();
|
|
hi->value[mIndex].ll = hDataTotal->getLeakBytes ();
|
|
}
|
|
else
|
|
{
|
|
hist_data->total->value[mIndex].ll = 0;
|
|
hi->value[mIndex].ll = 0;
|
|
}
|
|
break;
|
|
case BaseMetric::HEAP_LEAK_CNT:
|
|
if (!empty)
|
|
{
|
|
hist_data->total->value[mIndex].ll = hDataTotal->getLeakCnt ();
|
|
hi->value[mIndex].ll = hDataTotal->getLeakCnt ();
|
|
}
|
|
else
|
|
{
|
|
hist_data->total->value[mIndex].ll = 0;
|
|
hi->value[mIndex].ll = 0;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
HeapActivity::computeHistTotals (Hist_data *hist_data, MetricList *mlist)
|
|
{
|
|
int mIndex;
|
|
Metric *mtr;
|
|
Vec_loop (Metric *, mlist->get_items (), mIndex, mtr)
|
|
{
|
|
if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ())
|
|
continue;
|
|
|
|
Metric::Type mtype = mtr->get_type ();
|
|
ValueTag vType = mtr->get_vtype ();
|
|
|
|
hist_data->total->value[mIndex].tag = vType;
|
|
switch (mtype)
|
|
{
|
|
case BaseMetric::HEAP_ALLOC_BYTES:
|
|
hist_data->total->value[mIndex].ll = hDataTotal->getAllocBytes ();
|
|
break;
|
|
case BaseMetric::HEAP_ALLOC_CNT:
|
|
hist_data->total->value[mIndex].ll = hDataTotal->getAllocCnt ();
|
|
break;
|
|
case BaseMetric::HEAP_LEAK_BYTES:
|
|
hist_data->total->value[mIndex].ll = hDataTotal->getLeakBytes ();
|
|
break;
|
|
case BaseMetric::HEAP_LEAK_CNT:
|
|
hist_data->total->value[mIndex].ll = hDataTotal->getLeakCnt ();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
HeapActivity::computeHistData (Hist_data *hist_data, MetricList *mlist,
|
|
Hist_data::Mode mode, Histable *selObj)
|
|
{
|
|
|
|
Hist_data::HistItem *hi = NULL;
|
|
|
|
int numObjs = hDataObjs->size ();
|
|
int numMetrics = mlist->get_items ()->size ();
|
|
for (int i = 0; i < numObjs; i++)
|
|
{
|
|
HeapData *hData = hDataObjs->fetch (i);
|
|
if (mode == Hist_data::ALL)
|
|
hi = hist_data->append_hist_item (hData);
|
|
else if (mode == Hist_data::SELF)
|
|
{
|
|
if (hData->id == selObj->id)
|
|
hi = hist_data->append_hist_item (hData);
|
|
else
|
|
continue;
|
|
}
|
|
|
|
for (int mIndex = 0; mIndex < numMetrics; mIndex++)
|
|
{
|
|
Metric *mtr = mlist->get_items ()->fetch (mIndex);
|
|
if (!mtr->is_visible () && !mtr->is_tvisible ()
|
|
&& !mtr->is_pvisible ())
|
|
continue;
|
|
|
|
Metric::Type mtype = mtr->get_type ();
|
|
ValueTag vType = mtr->get_vtype ();
|
|
hi->value[mIndex].tag = vType;
|
|
switch (mtype)
|
|
{
|
|
case BaseMetric::HEAP_ALLOC_BYTES:
|
|
hi->value[mIndex].ll = hData->getAllocBytes ();
|
|
break;
|
|
case BaseMetric::HEAP_ALLOC_CNT:
|
|
hi->value[mIndex].ll = hData->getAllocCnt ();
|
|
break;
|
|
case BaseMetric::HEAP_LEAK_BYTES:
|
|
hi->value[mIndex].ll = hData->getLeakBytes ();
|
|
break;
|
|
case BaseMetric::HEAP_LEAK_CNT:
|
|
hi->value[mIndex].ll = hData->getLeakCnt ();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Hist_data *
|
|
HeapActivity::compute_metrics (MetricList *mlist, Histable::Type type,
|
|
Hist_data::Mode mode, Histable *selObj)
|
|
{
|
|
// it's already there, just return it
|
|
if (mode == Hist_data::ALL && type == Histable::HEAPCALLSTACK
|
|
&& hist_data_callstack_all != NULL)
|
|
return hist_data_callstack_all;
|
|
|
|
bool has_data = false;
|
|
Hist_data *hist_data = NULL;
|
|
VMode viewMode = dbev->get_view_mode ();
|
|
switch (type)
|
|
{
|
|
case Histable::HEAPCALLSTACK:
|
|
if (!hasCallStack) // It is not computed yet
|
|
computeCallStack (type, viewMode);
|
|
|
|
// computeCallStack() creates hDataObjsCallStack
|
|
// hDataObjsCallStack contains the list of call stack objects
|
|
if (hDataObjsCallStack != NULL)
|
|
{
|
|
hDataObjs = hDataObjsCallStack;
|
|
has_data = true;
|
|
}
|
|
else
|
|
has_data = false;
|
|
|
|
if (has_data && mode == Hist_data::ALL && hist_data_callstack_all == NULL)
|
|
{
|
|
hist_data_callstack_all = new Hist_data (mlist, type, mode, true);
|
|
hist_data = hist_data_callstack_all;
|
|
}
|
|
else if (has_data)
|
|
hist_data = new Hist_data (mlist, type, mode, false);
|
|
else
|
|
{
|
|
hist_data = new Hist_data (mlist, type, mode, false);
|
|
createHistItemTotals (hist_data, mlist, type, true);
|
|
return hist_data;
|
|
}
|
|
break;
|
|
default:
|
|
fprintf (stderr,
|
|
"HeapActivity cannot process data due to wrong Histable (type=%d) \n",
|
|
type);
|
|
abort ();
|
|
}
|
|
|
|
if (mode == Hist_data::ALL || (mode == Hist_data::SELF && selObj->id == 0))
|
|
createHistItemTotals (hist_data, mlist, type, false);
|
|
else
|
|
computeHistTotals (hist_data, mlist);
|
|
computeHistData (hist_data, mlist, mode, selObj);
|
|
|
|
// Determine by which metric to sort if any
|
|
bool rev_sort = mlist->get_sort_rev ();
|
|
int sort_ind = -1;
|
|
int nmetrics = mlist->get_items ()->size ();
|
|
|
|
for (int mind = 0; mind < nmetrics; mind++)
|
|
if (mlist->get_sort_ref_index () == mind)
|
|
sort_ind = mind;
|
|
|
|
hist_data->sort (sort_ind, rev_sort);
|
|
hist_data->compute_minmax ();
|
|
|
|
return hist_data;
|
|
}
|
|
|
|
void
|
|
HeapActivity::computeCallStack (Histable::Type type, VMode viewMode)
|
|
{
|
|
bool has_data = false;
|
|
reset ();
|
|
uint64_t stackIndex = 0;
|
|
HeapData *hData = NULL;
|
|
|
|
delete hDataCalStkMap;
|
|
hDataCalStkMap = new DefaultMap<uint64_t, HeapData*>;
|
|
|
|
delete hDataTotal;
|
|
hDataTotal = new HeapData (TOTAL_HEAPNAME);
|
|
hDataTotal->setHistType (type);
|
|
|
|
// There is no call stack for total, use the index for id
|
|
hDataTotal->id = stackIndex++;
|
|
|
|
// get the list of io events from DbeView
|
|
int numExps = dbeSession->nexps ();
|
|
|
|
for (int k = 0; k < numExps; k++)
|
|
{
|
|
// Investigate the performance impact of processing the heap events twice.
|
|
// This is a 2*n performance issue
|
|
dbev->get_filtered_events (k, DATA_HEAPSZ);
|
|
|
|
DataView *heapPkts = dbev->get_filtered_events (k, DATA_HEAP);
|
|
if (heapPkts == NULL)
|
|
continue;
|
|
|
|
Experiment *exp = dbeSession->get_exp (k);
|
|
long sz = heapPkts->getSize ();
|
|
int pid = 0;
|
|
int userExpId = 0;
|
|
if (sz > 0)
|
|
{
|
|
pid = exp->getPID ();
|
|
userExpId = exp->getUserExpId ();
|
|
}
|
|
for (long i = 0; i < sz; ++i)
|
|
{
|
|
uint64_t nByte = heapPkts->getULongValue (PROP_HSIZE, i);
|
|
uint64_t stackId = (uint64_t) getStack (viewMode, heapPkts, i);
|
|
Heap_type heapType = (Heap_type) heapPkts->getIntValue (PROP_HTYPE, i);
|
|
uint64_t leaked = heapPkts->getULongValue (PROP_HLEAKED, i);
|
|
int64_t heapSize = heapPkts->getLongValue (PROP_HCUR_ALLOCS, i);
|
|
hrtime_t packetTimestamp = heapPkts->getLongValue (PROP_TSTAMP, i);
|
|
hrtime_t timestamp = packetTimestamp - exp->getStartTime () +
|
|
exp->getRelativeStartTime ();
|
|
|
|
switch (heapType)
|
|
{
|
|
case MMAP_TRACE:
|
|
case MALLOC_TRACE:
|
|
case REALLOC_TRACE:
|
|
if (stackId != 0)
|
|
{
|
|
hData = hDataCalStkMap->get (stackId);
|
|
if (hData == NULL)
|
|
{
|
|
char *stkName = dbe_sprintf (GTXT ("Stack 0x%llx"),
|
|
(unsigned long long) stackId);
|
|
hData = new HeapData (stkName);
|
|
hDataCalStkMap->put (stackId, hData);
|
|
hData->id = (int64_t) stackId;
|
|
hData->setStackId (stackIndex);
|
|
stackIndex++;
|
|
hData->setHistType (type);
|
|
}
|
|
}
|
|
else
|
|
continue;
|
|
|
|
hData->addAllocEvent (nByte);
|
|
hDataTotal->addAllocEvent (nByte);
|
|
hDataTotal->setAllocStat (nByte);
|
|
hDataTotal->setPeakMemUsage (heapSize, hData->getStackId (),
|
|
timestamp, pid, userExpId);
|
|
if (leaked > 0)
|
|
{
|
|
hData->addLeakEvent (leaked);
|
|
hDataTotal->addLeakEvent (leaked);
|
|
hDataTotal->setLeakStat (leaked);
|
|
}
|
|
break;
|
|
case MUNMAP_TRACE:
|
|
case FREE_TRACE:
|
|
if (hData == NULL)
|
|
hData = new HeapData (TOTAL_HEAPNAME);
|
|
hDataTotal->setPeakMemUsage (heapSize, hData->getStackId (),
|
|
timestamp, pid, userExpId);
|
|
break;
|
|
case HEAPTYPE_LAST:
|
|
break;
|
|
}
|
|
has_data = true;
|
|
}
|
|
}
|
|
|
|
if (has_data)
|
|
{
|
|
hDataObjsCallStack = hDataCalStkMap->values ()->copy ();
|
|
hasCallStack = true;
|
|
}
|
|
}
|