| /* Copyright (C) 2021-2023 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; |
| } |
| } |