| /* 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; | |
| } | |
| } |