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