xref: /netbsd-src/external/gpl3/binutils/dist/gprofng/src/DataSpace.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 <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 
DataSpace(DbeView * _dbev,int)83 DataSpace::DataSpace (DbeView *_dbev, int /* _picked */)
84 {
85   dbev = _dbev;
86 }
87 
~DataSpace()88 DataSpace::~DataSpace () { }
89 
90 void
reset()91 DataSpace::reset () { }
92 
93 char *
status_str()94 DataSpace::status_str ()
95 {
96   return NULL;
97 }
98 
99 Histable *
get_hist_obj(Histable::Type type,DataView * dview,long i)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 *
compute_metrics(MetricList * mlist,Histable::Type type,Hist_data::Mode mode,Histable * sel_obj)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 *
get_layout_data(Hist_data * sorted_data,Vector<int> * marks,int)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