xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/src/HeapActivity.cc (revision cb63e24e8d6aae7ddac1859a9015f48b1d8bd90e)
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