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 <stdlib.h> 23 #include <stdarg.h> 24 25 #include "util.h" 26 #include "Application.h" 27 #include "CallStack.h" 28 #include "Experiment.h" 29 #include "Exp_Layout.h" 30 #include "DataObject.h" 31 #include "DbeSession.h" 32 #include "MetricList.h" 33 #include "Function.h" 34 #include "Module.h" 35 #include "MemObject.h" 36 #include "DbeView.h" 37 #include "Metric.h" 38 #include "DataSpace.h" 39 #include "LoadObject.h" 40 41 #include "debug.h" 42 #include "ABS.h" 43 44 //char *DOBJ_UNSPECIFIED = STXT("(Not identified by the compiler as a memory-referencing instruction)"); 45 char *DOBJ_UNSPECIFIED = STXT("(No type information)"); 46 char *DOBJ_UNIDENTIFIED = STXT("(No identifying descriptor provided by the compiler)"); 47 char *DOBJ_UNDETERMINED = STXT("(Not determined from the symbolic information provided by the compiler)"); 48 char *DOBJ_ANON = STXT("(Padding in structure)"); 49 50 // run-time codes 51 // ABS_UNSUPPORTED = 0x01, /* inappropriate HWC event type */ 52 // ABS_BLOCKED = 0x02, /* runtime backtrack blocker reached */ 53 // ABS_INCOMPLETE = 0x03, /* runtime backtrack limit reached */ 54 // ABS_REG_LOSS = 0x04, /* address register contaminated */ 55 // ABS_INVALID_EA = 0x05, /* invalid effective address value */ 56 57 const char *ABS_RT_CODES[NUM_ABS_RT_CODES] = { 58 "(OK)", 59 "(Dataspace data not requested during data collection)", 60 "(Backtracking was prevented by a jump or call instruction)", 61 "(Backtracking did not find trigger PC)", 62 "(Could not determine VA because registers changed after trigger instruction)", 63 "(Memory-referencing instruction did not specify a valid VA)", 64 "(UNKNOWN)" 65 }; 66 67 // post-processing codes 68 // ABS_NO_CTI_INFO = 0x10, /* no AnalyzerInfo for validation */ 69 // ABS_INFO_FAILED = 0x20, /* info failed to validate backtrack */ 70 // ABS_CTI_TARGET = 0x30, /* CTI target invalidated backtrack */ 71 char *DOBJ_UNASCERTAINABLE = STXT("(Module with trigger PC not compiled with -xhwcprof)"); 72 char *DOBJ_UNVERIFIABLE = STXT("(Backtracking failed to find a valid branch target)"); 73 char *DOBJ_UNRESOLVABLE = STXT("(Backtracking traversed a branch target)"); 74 75 char *ABS_PP_CODES[NUM_ABS_PP_CODES] = { 76 STXT ("(OK)"), 77 DOBJ_UNASCERTAINABLE, 78 DOBJ_UNVERIFIABLE, 79 DOBJ_UNRESOLVABLE, 80 STXT ("(<INTERNAL ERROR DURING POST-PROCESSING>)") 81 }; 82 83 DataSpace::DataSpace (DbeView *_dbev, int /* _picked */) 84 { 85 dbev = _dbev; 86 } 87 88 DataSpace::~DataSpace () { } 89 90 void 91 DataSpace::reset () { } 92 93 char * 94 DataSpace::status_str () 95 { 96 return NULL; 97 } 98 99 Histable * 100 DataSpace::get_hist_obj (Histable::Type type, DataView *dview, long i) 101 { 102 DataObject *dobj = NULL; 103 char *errcode = NTXT ("<internal error>"); 104 switch (type) 105 { 106 case Histable::DOBJECT: 107 dobj = (DataObject*) dview->getObjValue (PROP_HWCDOBJ, i); 108 if (dobj == NULL) 109 { 110 Vaddr leafVA = (Vaddr) dview->getLongValue (PROP_VADDR, i); 111 unsigned rt_code = (unsigned) ABS_GET_RT_CODE (leafVA); 112 unsigned pp_code = (unsigned) ABS_GET_PP_CODE (leafVA); 113 if (leafVA < ABS_CODE_RANGE 114 && (pp_code || (rt_code && rt_code != ABS_REG_LOSS))) 115 { 116 if (rt_code >= NUM_ABS_RT_CODES) 117 rt_code = NUM_ABS_RT_CODES - 1; 118 if (pp_code >= NUM_ABS_PP_CODES) 119 pp_code = NUM_ABS_PP_CODES - 1; 120 if (rt_code) 121 errcode = PTXT (ABS_RT_CODES[rt_code]); 122 else 123 errcode = PTXT (ABS_PP_CODES[pp_code]); 124 } 125 else 126 { 127 // associate dataobject with event 128 int index; 129 130 // search for memop in Module infoList 131 void *cstack = dview->getObjValue (PROP_MSTACK, i); 132 Histable *leafPCObj = CallStack::getStackPC (cstack, 0); 133 DbeInstr *leafPC = NULL; 134 if (leafPCObj->get_type () == Histable::INSTR) 135 leafPC = (DbeInstr*) leafPCObj; 136 else // DBELINE 137 leafPC = (DbeInstr*) leafPCObj->convertto (Histable::INSTR); 138 Function *func = leafPC->func; 139 uint64_t leafPC_offset = func->img_offset + leafPC->addr; 140 Module *mod = func->module; 141 uint32_t dtype_id = 0; 142 inst_info_t *info = NULL; 143 Vec_loop (inst_info_t*, mod->infoList, index, info) 144 { 145 if (info->offset == leafPC_offset) 146 { 147 dtype_id = info->memop->datatype_id; 148 break; 149 } 150 } 151 dobj = mod->get_dobj (dtype_id); 152 if (dobj == NULL) 153 { 154 // ensure dobj is determined 155 if (dtype_id == DataObject::UNSPECIFIED_ID) 156 errcode = PTXT (DOBJ_UNSPECIFIED); 157 else 158 errcode = PTXT (DOBJ_UNIDENTIFIED); 159 } 160 else 161 { 162 // determine associated master dataobject 163 if (!dobj->master && dobj->scope) 164 dobj->master = dbeSession->createMasterDataObject (dobj); 165 if (dobj->scope) 166 dobj = dobj->master; // use associated master 167 } 168 } 169 if (!dobj) 170 { 171 // if dobj is not set yet, supply a dobj for errcode 172 // search for a dobj with the same name 173 dobj = dbeSession->find_dobj_by_name (errcode); 174 if (dobj == NULL) 175 { 176 // create new DataObject for unknown code 177 dobj = (DataObject*) dbeSession->createHistObject (Histable::DOBJECT); 178 dobj->size = 0; 179 dobj->offset = -1; 180 dobj->parent = dbeSession->get_Unknown_DataObject (); 181 dobj->set_dobjname (errcode, NULL); // dobj->parent must already be set 182 } 183 } 184 dview->setObjValue (PROP_HWCDOBJ, i, dobj); 185 } 186 break; 187 default: 188 break; 189 } 190 return dobj; 191 } 192 193 Hist_data * 194 DataSpace::compute_metrics (MetricList *mlist, Histable::Type type, 195 Hist_data::Mode mode, Histable *sel_obj) 196 { 197 int nmetrics = mlist->get_items ()->size (); 198 int sort_ind = -1; 199 Hist_data::HistItem *hi; 200 int index; 201 202 // reset event_data count for all datatypes 203 Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); 204 for (int i = 0, sz = lobjs ? lobjs->size () : -1; i < sz; i++) 205 { 206 LoadObject *lo = lobjs->fetch (i); 207 Vector<Module*> *modules = lo->seg_modules; 208 for (int j = 0, msize = modules ? modules->size () : -1; j < msize; j++) 209 { 210 Module *mod = modules->fetch (j); 211 mod->reset_datatypes (); 212 } 213 } 214 Hist_data *hist_data = new Hist_data (mlist, type, mode); 215 216 // add each experiment, skipping disabled and broken experiments 217 for (index = 0; index < dbeSession->nexps (); index++) 218 { 219 Experiment *exp = dbeSession->get_exp (index); 220 if (exp->broken) 221 continue; 222 223 Collection_params *params = exp->get_params (); 224 if (!params->xhw_mode) 225 continue; 226 227 char *expt_name = exp->get_expt_name (); 228 char *base_name = strrchr (expt_name, '/'); 229 base_name = base_name ? base_name + 1 : expt_name; 230 231 // Determine mapping of experiment HWC metrics to hist_data metric list 232 int *xlate = new int[MAX_HWCOUNT]; 233 for (unsigned i = 0; i < MAX_HWCOUNT; i++) 234 { 235 xlate[i] = -1; 236 if (params->hw_interval[i] > 0) 237 { 238 const char *ctr_name = params->hw_aux_name[i]; 239 int mindex; 240 Metric *met; 241 Vec_loop (Metric*, mlist->get_items (), mindex, met) 242 { 243 if (dbe_strcmp (met->get_cmd (), ctr_name) == 0) 244 xlate[i] = mindex; 245 } 246 } 247 } 248 249 // 250 // Process hardware profiling data 251 // 252 DataView *dview = dbev->get_filtered_events (index, DATA_HWC); 253 if (dview) 254 { 255 DataDescriptor *ddscr = dview ->getDataDescriptor (); 256 if (ddscr->getProp (PROP_HWCDOBJ) == NULL) 257 { 258 PropDescr *prop = new PropDescr (PROP_HWCDOBJ, NTXT ("HWCDOBJ")); 259 prop->uname = NULL; 260 prop->vtype = TYPE_OBJ; 261 ddscr->addProperty (prop); 262 } 263 } 264 if (dview && dview->getSize () != 0) 265 { 266 char *msg = NULL; 267 for (long i = 0; i < dview->getSize (); i++) 268 { 269 if (i % 5000 == 0) 270 { 271 int percent = (int) (100.0 * i / dview->getSize ()); 272 if (percent == 0 && msg == NULL) 273 msg = dbe_sprintf (GTXT ("Filtering HW Profile Address Data: %s"), base_name); 274 theApplication->set_progress (percent, (percent != 0) ? NULL : msg); 275 } 276 277 uint32_t tag = dview->getIntValue (PROP_HWCTAG, i); 278 if (tag < 0 || tag >= MAX_HWCOUNT) 279 continue; // invalid HWC tag in the record; ignore it 280 int mHwcntr_idx = xlate[tag]; 281 if (mHwcntr_idx < 0) 282 continue; 283 284 Vaddr leafVA = (Vaddr) dview->getLongValue (PROP_VADDR, i); 285 if (leafVA == ABS_UNSUPPORTED) 286 continue; // skip this record 287 Histable *obj = get_hist_obj (type, dview, i); 288 if (obj == NULL) 289 continue; 290 uint64_t interval = dview->getLongValue (PROP_HWCINT, i); 291 if (HWCVAL_HAS_ERR (interval)) 292 continue; 293 if (mode == Hist_data::ALL) 294 { // data_objects 295 hi = hist_data->append_hist_item (obj); 296 hi->value[mHwcntr_idx].ll += interval; 297 for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) 298 { 299 hi = hist_data->append_hist_item (dobj); 300 hi->value[mHwcntr_idx].ll += interval; 301 } 302 } 303 else if (mode == Hist_data::LAYOUT || mode == Hist_data::DETAIL) 304 { // data_single 305 { 306 // for data layout, insert elements that have no metrics yet 307 DataObject *tmpParent = ((DataObject *) obj)->parent; 308 if (tmpParent && tmpParent->get_typename ()) 309 { 310 // dobj is an aggregate element 311 if (!hist_data->find_hist_item (tmpParent)) 312 { 313 // parent not yet a member of hist_data 314 // supply parent's children with 0 values for layout 315 Vector<DataObject*> *elements = dbeSession->get_dobj_elements (tmpParent); 316 for (long eli = 0, sz = elements->size (); eli < sz; eli++) 317 { 318 DataObject* element = elements->fetch (eli); 319 assert (!hist_data->find_hist_item (element)); 320 hi = hist_data->append_hist_item (element); 321 } 322 } 323 } 324 } 325 326 // Same as for mode == Hist_data::ALL: 327 hi = hist_data->append_hist_item (obj); 328 hi->value[mHwcntr_idx].ll += interval; 329 for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) 330 { 331 hi = hist_data->append_hist_item (dobj); 332 hi->value[mHwcntr_idx].ll += interval; 333 } 334 } 335 else if (mode == Hist_data::SELF) 336 { // used by dbeGetSummary() 337 if (obj == sel_obj) 338 { 339 hi = hist_data->append_hist_item (obj); 340 hi->value[mHwcntr_idx].ll += interval; 341 } 342 else 343 { 344 for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) 345 { 346 if ((Histable*) dobj == sel_obj) 347 { 348 hi = hist_data->append_hist_item (dobj); 349 hi->value[mHwcntr_idx].ll += interval; 350 break; 351 } 352 } 353 } 354 } 355 // Update total 356 hist_data->total->value[mHwcntr_idx].ll += interval; 357 } 358 free (msg); 359 theApplication->set_progress (0, NTXT ("")); 360 } 361 delete[] xlate; 362 } 363 364 // include a regular HistItem for <Total> -- for all DataObjects, and MemObjects 365 DataObject *dtot = dbeSession->get_Total_DataObject (); 366 if (mode == Hist_data::ALL || mode == Hist_data::DETAIL 367 || mode == Hist_data::LAYOUT || 368 sel_obj == dtot) 369 { 370 hi = hist_data->append_hist_item (dtot); 371 for (int mind = 0; mind < nmetrics; mind++) 372 hi->value[mind] = hist_data->total->value[mind]; 373 } 374 if (hist_data->get_status () != Hist_data::SUCCESS) 375 return hist_data; 376 theApplication->set_progress (0, GTXT ("Constructing Metrics")); 377 378 // Determine by which metric to sort if any 379 bool rev_sort = mlist->get_sort_rev (); 380 381 // Compute static metrics: SIZES, ADDRESS. 382 for (int mind = 0; mind < nmetrics; mind++) 383 { 384 Metric *mtr = mlist->get_items ()->fetch (mind); 385 if (mlist->get_sort_ref_index () == mind) 386 sort_ind = mind; 387 else if (!mtr->is_visible () && !mtr->is_tvisible () 388 && !mtr->is_pvisible ()) 389 continue; 390 Metric::Type mtype = mtr->get_type (); 391 if (mtype == Metric::SIZES) 392 { 393 Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) 394 { 395 Histable *h = mtr->get_comparable_obj (hi->obj); 396 hi->value[mind].tag = VT_LLONG; 397 hi->value[mind].ll = h ? h->get_size () : 0; 398 } 399 } 400 else if (mtype == Metric::ONAME 401 && (mode == Hist_data::SELF 402 || ((DataObject*) sel_obj == dbeSession->get_Total_DataObject ()))) 403 { 404 Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) 405 { 406 hi->value[mind].tag = VT_OFFSET; // offset labels 407 } 408 } 409 else if (mtype == Metric::ADDRESS) 410 { // pseudo-address 411 Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) 412 { 413 hi->value[mind].tag = VT_ADDRESS; 414 Histable *h = mtr->get_comparable_obj (hi->obj); 415 hi->value[mind].ll = h ? h->get_addr () : 0; 416 } 417 // force sort by offset // XXXX should visibility also be set? 418 if (mode == Hist_data::SELF) 419 { // used by dbeGetSummary() 420 sort_ind = mind; 421 //hist_data->metrics->fetch(mind)->set_visible(T); 422 } 423 } 424 else 425 { 426 ValueTag vtype = mtr->get_vtype (); 427 switch (vtype) 428 { 429 case VT_ULLONG: // most Data-derived HWC metrics are VT_ULLONG 430 hist_data->total->value[mind].tag = vtype; 431 Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) 432 { 433 hi->value[mind].tag = vtype; 434 } 435 break; 436 case VT_DOUBLE: 437 { 438 double prec = mtr->get_precision (); 439 hist_data->total->value[mind].tag = vtype; 440 hist_data->total->value[mind].d = hist_data->total->value[mind].ll / prec; 441 Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) 442 { 443 hi->value[mind].tag = vtype; 444 hi->value[mind].d = hi->value[mind].ll / prec; 445 } 446 break; 447 } 448 default: 449 if (mtr->get_subtype () != Metric::STATIC) 450 abort (); 451 break; 452 } 453 } 454 } 455 hist_data->sort (sort_ind, rev_sort); 456 hist_data->compute_minmax (); 457 theApplication->set_progress (0, NTXT ("")); 458 return hist_data; 459 } 460 461 462 // generate annotated structure info for data_layout 463 // note: similar data traversal found in er_print_histogram::dump_detail() 464 Hist_data * 465 DataSpace::get_layout_data (Hist_data *sorted_data, 466 Vector<int> *marks, int /* _threshold */) 467 { 468 Hist_data *data_items = NULL; 469 Hist_data::HistItem *new_item; 470 MetricList *mlist = new MetricList (sorted_data->get_metric_list ()); 471 int no_metrics = mlist->get_items ()->size (); 472 int index, addr_index = -1, name_index = -1; 473 Dprintf (DEBUG_DATAOBJ, NTXT ("DataSpace::get_layout_data(ALL)\n")); 474 475 // Allocate a new Hist_data for the list, to be copied from the DataObect list 476 data_items = new Hist_data (mlist, Histable::DOBJECT, Hist_data::MODL); 477 data_items->set_status (sorted_data->get_status ()); 478 479 // suppress threshold setting 480 // XXX this threshold should probably not be used 481 sorted_data->set_threshold ((double) 75. / 100.0); 482 TValue* all_empty = new TValue[no_metrics]; 483 memset (all_empty, 0, sizeof (TValue) * no_metrics); 484 485 Metric *mitem; 486 Vec_loop (Metric*, mlist->get_items (), index, mitem) 487 { 488 // new data items have same total as original items 489 data_items->total->value[index] = sorted_data->total->value[index]; 490 // empty metric items need matching types 491 all_empty[index].tag = mitem->get_vtype (); 492 if (mitem->get_type () == Metric::ONAME) name_index = index; 493 if (mitem->get_type () == Metric::ADDRESS) addr_index = index; 494 } 495 496 int64_t next_elem_offset = 0; 497 for (long i = 0; i < sorted_data->size (); i++) 498 { 499 Hist_data::HistItem* ditem = sorted_data->fetch (i); 500 DataObject* dobj = (DataObject*) (ditem->obj); 501 if (!dobj->get_parent ()) 502 { // doesn't have a parent; top level item 503 next_elem_offset = 0; 504 if (i > 0) 505 { // add a blank line as separator 506 // fixme xxxxx, is it really ok to create a DataObject just for this? 507 DataObject* empty = new DataObject (); 508 empty->size = 0; 509 empty->offset = 0; 510 empty->set_name (NTXT ("")); 511 new_item = sorted_data->new_hist_item (empty, Module::AT_EMPTY, all_empty); 512 new_item->value[name_index].tag = VT_LABEL; 513 new_item->value[name_index].l = NULL; 514 data_items->append_hist_item (new_item); 515 } 516 // then add the aggregate 517 new_item = sorted_data->new_hist_item (dobj, Module::AT_SRC, ditem->value); 518 new_item->value[name_index].tag = VT_OFFSET; 519 new_item->value[name_index].l = dbe_strdup (dobj->get_name ()); 520 data_items->append_hist_item (new_item); 521 } 522 else 523 { // is a child 524 if (dobj->get_parent ()->get_typename ()) 525 { // typed sub-element that has offset 526 if (dobj->offset > next_elem_offset) 527 { // filler entry 528 // hole in offsets 529 // fixme xxxxx, is it really ok to create a DataObject just for this? 530 DataObject* filler = new DataObject (); 531 filler->set_name (PTXT (DOBJ_ANON)); 532 filler->size = (dobj->offset - next_elem_offset); 533 filler->offset = next_elem_offset; 534 new_item = sorted_data->new_hist_item (filler, Module::AT_EMPTY, all_empty); 535 new_item->value[name_index].tag = VT_OFFSET; 536 new_item->value[name_index].l = dbe_strdup (filler->get_offset_name ()); 537 if (addr_index >= 0) 538 { 539 new_item->value[addr_index].tag = VT_ADDRESS; 540 new_item->value[addr_index].ll = (dobj->get_addr () - filler->size); 541 } 542 data_items->append_hist_item (new_item); 543 } 544 next_elem_offset = dobj->offset + dobj->size; 545 } 546 // then add the aggregate's subelement 547 if (marks) 548 if (sorted_data->above_threshold (ditem)) 549 marks->append (data_items->size ()); 550 new_item = sorted_data->new_hist_item (dobj, Module::AT_DIS, ditem->value); 551 new_item->value[name_index].tag = VT_OFFSET; 552 new_item->value[name_index].l = dbe_strdup (dobj->get_offset_name ()); 553 data_items->append_hist_item (new_item); 554 } 555 } 556 delete[] all_empty; 557 return data_items; 558 } 559