1 /* Copyright (C) 2021-2024 Free Software Foundation, Inc.
2 Contributed by Oracle.
3
4 This file is part of GNU Binutils.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, 51 Franklin Street - Fifth Floor, Boston,
19 MA 02110-1301, USA. */
20
21 #include "config.h"
22 #include "DbeSession.h"
23 #include "HeapData.h"
24 #include "StringBuilder.h"
25 #include "i18n.h"
26 #include "util.h"
27 #include "HeapActivity.h"
28 #include "MetricList.h"
29 #include "Application.h"
30 #include "Experiment.h"
31 #include "DbeView.h"
32 #include "Exp_Layout.h"
33 #include "i18n.h"
34
HeapActivity(DbeView * _dbev)35 HeapActivity::HeapActivity (DbeView *_dbev)
36 {
37 dbev = _dbev;
38 hDataTotal = NULL;
39 hDataObjs = NULL;
40 hDataObjsCallStack = NULL;
41 hasCallStack = false;
42 hDataCalStkMap = NULL;
43 hist_data_callstack_all = NULL;
44 }
45
46 void
reset()47 HeapActivity::reset ()
48 {
49 delete hDataTotal;
50 hDataTotal = NULL;
51 delete hDataObjsCallStack;
52 hDataObjsCallStack = NULL;
53 hasCallStack = false;
54 hDataObjs = NULL;
55 delete hDataCalStkMap;
56 hDataCalStkMap = NULL;
57 hist_data_callstack_all = NULL;
58 }
59
60 void
createHistItemTotals(Hist_data * hist_data,MetricList * mlist,Histable::Type hType,bool empty)61 HeapActivity::createHistItemTotals (Hist_data *hist_data, MetricList *mlist,
62 Histable::Type hType, bool empty)
63 {
64 int mIndex;
65 Metric *mtr;
66 Hist_data::HistItem *hi;
67 HeapData *hData = NULL;
68 if (hDataTotal == NULL)
69 {
70 hDataTotal = new HeapData (TOTAL_HEAPNAME);
71 hDataTotal->setHistType (hType);
72 hDataTotal->setStackId (TOTAL_STACK_ID);
73 hDataTotal->id = 0;
74 }
75
76 hData = new HeapData (hDataTotal);
77 hData->setHistType (hType);
78 hi = hist_data->append_hist_item (hData);
79
80 Vec_loop (Metric *, mlist->get_items (), mIndex, mtr)
81 {
82 if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ())
83 continue;
84
85 Metric::Type mtype = mtr->get_type ();
86 ValueTag vType = mtr->get_vtype ();
87
88 hist_data->total->value[mIndex].tag = vType;
89 hi->value[mIndex].tag = vType;
90 switch (mtype)
91 {
92 case BaseMetric::HEAP_ALLOC_BYTES:
93 if (!empty)
94 {
95 hist_data->total->value[mIndex].ll = hDataTotal->getAllocBytes ();
96 hi->value[mIndex].ll = hDataTotal->getAllocBytes ();
97 }
98 else
99 {
100 hist_data->total->value[mIndex].ll = 0;
101 hi->value[mIndex].ll = 0;
102 }
103 break;
104 case BaseMetric::HEAP_ALLOC_CNT:
105 if (!empty)
106 {
107 hist_data->total->value[mIndex].ll = hDataTotal->getAllocCnt ();
108 hi->value[mIndex].ll = hDataTotal->getAllocCnt ();
109 }
110 else
111 {
112 hist_data->total->value[mIndex].ll = 0;
113 hi->value[mIndex].ll = 0;
114 }
115 break;
116 case BaseMetric::HEAP_LEAK_BYTES:
117 if (!empty)
118 {
119 hist_data->total->value[mIndex].ll = hDataTotal->getLeakBytes ();
120 hi->value[mIndex].ll = hDataTotal->getLeakBytes ();
121 }
122 else
123 {
124 hist_data->total->value[mIndex].ll = 0;
125 hi->value[mIndex].ll = 0;
126 }
127 break;
128 case BaseMetric::HEAP_LEAK_CNT:
129 if (!empty)
130 {
131 hist_data->total->value[mIndex].ll = hDataTotal->getLeakCnt ();
132 hi->value[mIndex].ll = hDataTotal->getLeakCnt ();
133 }
134 else
135 {
136 hist_data->total->value[mIndex].ll = 0;
137 hi->value[mIndex].ll = 0;
138 }
139 break;
140 default:
141 break;
142 }
143 }
144 }
145
146 void
computeHistTotals(Hist_data * hist_data,MetricList * mlist)147 HeapActivity::computeHistTotals (Hist_data *hist_data, MetricList *mlist)
148 {
149 int mIndex;
150 Metric *mtr;
151 Vec_loop (Metric *, mlist->get_items (), mIndex, mtr)
152 {
153 if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ())
154 continue;
155
156 Metric::Type mtype = mtr->get_type ();
157 ValueTag vType = mtr->get_vtype ();
158
159 hist_data->total->value[mIndex].tag = vType;
160 switch (mtype)
161 {
162 case BaseMetric::HEAP_ALLOC_BYTES:
163 hist_data->total->value[mIndex].ll = hDataTotal->getAllocBytes ();
164 break;
165 case BaseMetric::HEAP_ALLOC_CNT:
166 hist_data->total->value[mIndex].ll = hDataTotal->getAllocCnt ();
167 break;
168 case BaseMetric::HEAP_LEAK_BYTES:
169 hist_data->total->value[mIndex].ll = hDataTotal->getLeakBytes ();
170 break;
171 case BaseMetric::HEAP_LEAK_CNT:
172 hist_data->total->value[mIndex].ll = hDataTotal->getLeakCnt ();
173 break;
174 default:
175 break;
176 }
177 }
178 }
179
180 void
computeHistData(Hist_data * hist_data,MetricList * mlist,Hist_data::Mode mode,Histable * selObj)181 HeapActivity::computeHistData (Hist_data *hist_data, MetricList *mlist,
182 Hist_data::Mode mode, Histable *selObj)
183 {
184
185 Hist_data::HistItem *hi = NULL;
186
187 int numObjs = hDataObjs->size ();
188 int numMetrics = mlist->get_items ()->size ();
189 for (int i = 0; i < numObjs; i++)
190 {
191 HeapData *hData = hDataObjs->fetch (i);
192 if (mode == Hist_data::ALL)
193 hi = hist_data->append_hist_item (hData);
194 else if (mode == Hist_data::SELF)
195 {
196 if (hData->id == selObj->id)
197 hi = hist_data->append_hist_item (hData);
198 else
199 continue;
200 }
201
202 for (int mIndex = 0; mIndex < numMetrics; mIndex++)
203 {
204 Metric *mtr = mlist->get_items ()->fetch (mIndex);
205 if (!mtr->is_visible () && !mtr->is_tvisible ()
206 && !mtr->is_pvisible ())
207 continue;
208
209 Metric::Type mtype = mtr->get_type ();
210 ValueTag vType = mtr->get_vtype ();
211 hi->value[mIndex].tag = vType;
212 switch (mtype)
213 {
214 case BaseMetric::HEAP_ALLOC_BYTES:
215 hi->value[mIndex].ll = hData->getAllocBytes ();
216 break;
217 case BaseMetric::HEAP_ALLOC_CNT:
218 hi->value[mIndex].ll = hData->getAllocCnt ();
219 break;
220 case BaseMetric::HEAP_LEAK_BYTES:
221 hi->value[mIndex].ll = hData->getLeakBytes ();
222 break;
223 case BaseMetric::HEAP_LEAK_CNT:
224 hi->value[mIndex].ll = hData->getLeakCnt ();
225 break;
226 default:
227 break;
228 }
229 }
230 }
231 }
232
233 Hist_data *
compute_metrics(MetricList * mlist,Histable::Type type,Hist_data::Mode mode,Histable * selObj)234 HeapActivity::compute_metrics (MetricList *mlist, Histable::Type type,
235 Hist_data::Mode mode, Histable *selObj)
236 {
237 // it's already there, just return it
238 if (mode == Hist_data::ALL && type == Histable::HEAPCALLSTACK
239 && hist_data_callstack_all != NULL)
240 return hist_data_callstack_all;
241
242 bool has_data = false;
243 Hist_data *hist_data = NULL;
244 VMode viewMode = dbev->get_view_mode ();
245 switch (type)
246 {
247 case Histable::HEAPCALLSTACK:
248 if (!hasCallStack) // It is not computed yet
249 computeCallStack (type, viewMode);
250
251 // computeCallStack() creates hDataObjsCallStack
252 // hDataObjsCallStack contains the list of call stack objects
253 if (hDataObjsCallStack != NULL)
254 {
255 hDataObjs = hDataObjsCallStack;
256 has_data = true;
257 }
258 else
259 has_data = false;
260
261 if (has_data && mode == Hist_data::ALL && hist_data_callstack_all == NULL)
262 {
263 hist_data_callstack_all = new Hist_data (mlist, type, mode, true);
264 hist_data = hist_data_callstack_all;
265 }
266 else if (has_data)
267 hist_data = new Hist_data (mlist, type, mode, false);
268 else
269 {
270 hist_data = new Hist_data (mlist, type, mode, false);
271 createHistItemTotals (hist_data, mlist, type, true);
272 return hist_data;
273 }
274 break;
275 default:
276 fprintf (stderr,
277 "HeapActivity cannot process data due to wrong Histable (type=%d) \n",
278 type);
279 abort ();
280 }
281
282 if (mode == Hist_data::ALL || (mode == Hist_data::SELF && selObj->id == 0))
283 createHistItemTotals (hist_data, mlist, type, false);
284 else
285 computeHistTotals (hist_data, mlist);
286 computeHistData (hist_data, mlist, mode, selObj);
287
288 // Determine by which metric to sort if any
289 bool rev_sort = mlist->get_sort_rev ();
290 int sort_ind = -1;
291 int nmetrics = mlist->get_items ()->size ();
292
293 for (int mind = 0; mind < nmetrics; mind++)
294 if (mlist->get_sort_ref_index () == mind)
295 sort_ind = mind;
296
297 hist_data->sort (sort_ind, rev_sort);
298 hist_data->compute_minmax ();
299
300 return hist_data;
301 }
302
303 void
computeCallStack(Histable::Type type,VMode viewMode)304 HeapActivity::computeCallStack (Histable::Type type, VMode viewMode)
305 {
306 bool has_data = false;
307 reset ();
308 uint64_t stackIndex = 0;
309 HeapData *hData = NULL;
310
311 delete hDataCalStkMap;
312 hDataCalStkMap = new DefaultMap<uint64_t, HeapData*>;
313
314 delete hDataTotal;
315 hDataTotal = new HeapData (TOTAL_HEAPNAME);
316 hDataTotal->setHistType (type);
317
318 // There is no call stack for total, use the index for id
319 hDataTotal->id = stackIndex++;
320
321 // get the list of io events from DbeView
322 int numExps = dbeSession->nexps ();
323
324 for (int k = 0; k < numExps; k++)
325 {
326 // Investigate the performance impact of processing the heap events twice.
327 // This is a 2*n performance issue
328 dbev->get_filtered_events (k, DATA_HEAPSZ);
329
330 DataView *heapPkts = dbev->get_filtered_events (k, DATA_HEAP);
331 if (heapPkts == NULL)
332 continue;
333
334 Experiment *exp = dbeSession->get_exp (k);
335 long sz = heapPkts->getSize ();
336 int pid = 0;
337 int userExpId = 0;
338 if (sz > 0)
339 {
340 pid = exp->getPID ();
341 userExpId = exp->getUserExpId ();
342 }
343 for (long i = 0; i < sz; ++i)
344 {
345 uint64_t nByte = heapPkts->getULongValue (PROP_HSIZE, i);
346 uint64_t stackId = (uint64_t) getStack (viewMode, heapPkts, i);
347 Heap_type heapType = (Heap_type) heapPkts->getIntValue (PROP_HTYPE, i);
348 uint64_t leaked = heapPkts->getULongValue (PROP_HLEAKED, i);
349 int64_t heapSize = heapPkts->getLongValue (PROP_HCUR_ALLOCS, i);
350 hrtime_t packetTimestamp = heapPkts->getLongValue (PROP_TSTAMP, i);
351 hrtime_t timestamp = packetTimestamp - exp->getStartTime () +
352 exp->getRelativeStartTime ();
353
354 switch (heapType)
355 {
356 case MMAP_TRACE:
357 case MALLOC_TRACE:
358 case REALLOC_TRACE:
359 if (stackId != 0)
360 {
361 hData = hDataCalStkMap->get (stackId);
362 if (hData == NULL)
363 {
364 char *stkName = dbe_sprintf (GTXT ("Stack 0x%llx"),
365 (unsigned long long) stackId);
366 hData = new HeapData (stkName);
367 hDataCalStkMap->put (stackId, hData);
368 hData->id = (int64_t) stackId;
369 hData->setStackId (stackIndex);
370 stackIndex++;
371 hData->setHistType (type);
372 }
373 }
374 else
375 continue;
376
377 hData->addAllocEvent (nByte);
378 hDataTotal->addAllocEvent (nByte);
379 hDataTotal->setAllocStat (nByte);
380 hDataTotal->setPeakMemUsage (heapSize, hData->getStackId (),
381 timestamp, pid, userExpId);
382 if (leaked > 0)
383 {
384 hData->addLeakEvent (leaked);
385 hDataTotal->addLeakEvent (leaked);
386 hDataTotal->setLeakStat (leaked);
387 }
388 break;
389 case MUNMAP_TRACE:
390 case FREE_TRACE:
391 if (hData == NULL)
392 hData = new HeapData (TOTAL_HEAPNAME);
393 hDataTotal->setPeakMemUsage (heapSize, hData->getStackId (),
394 timestamp, pid, userExpId);
395 break;
396 case HEAPTYPE_LAST:
397 break;
398 }
399 has_data = true;
400 }
401 }
402
403 if (has_data)
404 {
405 hDataObjsCallStack = hDataCalStkMap->values ()->copy ();
406 hasCallStack = true;
407 }
408 }
409