1 /* Copyright (C) 2021 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 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 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 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 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 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 * 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 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