diff options
author | Vladimir Mezentsev <vladimir.mezentsev@oracle.com> | 2022-03-11 08:58:31 +0000 |
---|---|---|
committer | Nick Clifton <nickc@redhat.com> | 2022-03-11 08:58:31 +0000 |
commit | bb368aad297fe3ad40cf397e6fc85aa471429a28 (patch) | |
tree | 0ab25909b8fe789d676bbdb00d501d4d485e4afe /gprofng/src/DataSpace.cc | |
parent | a655f19af95eb685ba64f48ee8fc2b3b7a3d886a (diff) | |
download | binutils-bb368aad297fe3ad40cf397e6fc85aa471429a28.zip binutils-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.gz binutils-bb368aad297fe3ad40cf397e6fc85aa471429a28.tar.bz2 |
gprofng: a new GNU profiler
top-level
* Makefile.def: Add gprofng module.
* configure.ac: Add --enable-gprofng option.
* src-release.sh: Add gprofng.
* Makefile.in: Regenerate.
* configure: Regenerate.
* gprofng: New directory.
binutils
* MAINTAINERS: Add gprofng maintainer.
* README-how-to-make-a-release: Add gprofng.
include.
* collectorAPI.h: New file.
* libcollector.h: New file.
* libfcollector.h: New file.
Diffstat (limited to 'gprofng/src/DataSpace.cc')
-rw-r--r-- | gprofng/src/DataSpace.cc | 558 |
1 files changed, 558 insertions, 0 deletions
diff --git a/gprofng/src/DataSpace.cc b/gprofng/src/DataSpace.cc new file mode 100644 index 0000000..e5b48dd --- /dev/null +++ b/gprofng/src/DataSpace.cc @@ -0,0 +1,558 @@ +/* Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Oracle. + + This file is part of GNU Binutils. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 51 Franklin Street - Fifth Floor, Boston, + MA 02110-1301, USA. */ + +#include "config.h" +#include <stdlib.h> +#include <stdarg.h> + +#include "util.h" +#include "Application.h" +#include "CallStack.h" +#include "Experiment.h" +#include "Exp_Layout.h" +#include "DataObject.h" +#include "DbeSession.h" +#include "MetricList.h" +#include "Function.h" +#include "Module.h" +#include "MemObject.h" +#include "DbeView.h" +#include "Metric.h" +#include "DataSpace.h" +#include "LoadObject.h" + +#include "debug.h" +#include "ABS.h" + +//char *DOBJ_UNSPECIFIED = STXT("(Not identified by the compiler as a memory-referencing instruction)"); +char *DOBJ_UNSPECIFIED = STXT("(No type information)"); +char *DOBJ_UNIDENTIFIED = STXT("(No identifying descriptor provided by the compiler)"); +char *DOBJ_UNDETERMINED = STXT("(Not determined from the symbolic information provided by the compiler)"); +char *DOBJ_ANON = STXT("(Padding in structure)"); + +// run-time codes +// ABS_UNSUPPORTED = 0x01, /* inappropriate HWC event type */ +// ABS_BLOCKED = 0x02, /* runtime backtrack blocker reached */ +// ABS_INCOMPLETE = 0x03, /* runtime backtrack limit reached */ +// ABS_REG_LOSS = 0x04, /* address register contaminated */ +// ABS_INVALID_EA = 0x05, /* invalid effective address value */ + +const char *ABS_RT_CODES[NUM_ABS_RT_CODES] = { + "(OK)", + "(Dataspace data not requested during data collection)", + "(Backtracking was prevented by a jump or call instruction)", + "(Backtracking did not find trigger PC)", + "(Could not determine VA because registers changed after trigger instruction)", + "(Memory-referencing instruction did not specify a valid VA)", + "(UNKNOWN)" +}; + +// post-processing codes +// ABS_NO_CTI_INFO = 0x10, /* no AnalyzerInfo for validation */ +// ABS_INFO_FAILED = 0x20, /* info failed to validate backtrack */ +// ABS_CTI_TARGET = 0x30, /* CTI target invalidated backtrack */ +char *DOBJ_UNASCERTAINABLE = STXT("(Module with trigger PC not compiled with -xhwcprof)"); +char *DOBJ_UNVERIFIABLE = STXT("(Backtracking failed to find a valid branch target)"); +char *DOBJ_UNRESOLVABLE = STXT("(Backtracking traversed a branch target)"); + +char *ABS_PP_CODES[NUM_ABS_PP_CODES] = { + STXT ("(OK)"), + DOBJ_UNASCERTAINABLE, + DOBJ_UNVERIFIABLE, + DOBJ_UNRESOLVABLE, + STXT ("(<INTERNAL ERROR DURING POST-PROCESSING>)") +}; + +DataSpace::DataSpace (DbeView *_dbev, int /* _picked */) +{ + dbev = _dbev; +} + +DataSpace::~DataSpace () { } + +void +DataSpace::reset () { } + +char * +DataSpace::status_str () +{ + return NULL; +} + +Histable * +DataSpace::get_hist_obj (Histable::Type type, DataView *dview, long i) +{ + DataObject *dobj = NULL; + char *errcode = NTXT ("<internal error>"); + switch (type) + { + case Histable::DOBJECT: + dobj = (DataObject*) dview->getObjValue (PROP_HWCDOBJ, i); + if (dobj == NULL) + { + Vaddr leafVA = (Vaddr) dview->getLongValue (PROP_VADDR, i); + unsigned rt_code = (unsigned) ABS_GET_RT_CODE (leafVA); + unsigned pp_code = (unsigned) ABS_GET_PP_CODE (leafVA); + if (leafVA < ABS_CODE_RANGE + && (pp_code || (rt_code && rt_code != ABS_REG_LOSS))) + { + if (rt_code >= NUM_ABS_RT_CODES) + rt_code = NUM_ABS_RT_CODES - 1; + if (pp_code >= NUM_ABS_PP_CODES) + pp_code = NUM_ABS_PP_CODES - 1; + if (rt_code) + errcode = PTXT (ABS_RT_CODES[rt_code]); + else + errcode = PTXT (ABS_PP_CODES[pp_code]); + } + else + { + // associate dataobject with event + int index; + + // search for memop in Module infoList + void *cstack = dview->getObjValue (PROP_MSTACK, i); + Histable *leafPCObj = CallStack::getStackPC (cstack, 0); + DbeInstr *leafPC = NULL; + if (leafPCObj->get_type () == Histable::INSTR) + leafPC = (DbeInstr*) leafPCObj; + else // DBELINE + leafPC = (DbeInstr*) leafPCObj->convertto (Histable::INSTR); + Function *func = leafPC->func; + uint64_t leafPC_offset = func->img_offset + leafPC->addr; + Module *mod = func->module; + uint32_t dtype_id = 0; + inst_info_t *info = NULL; + Vec_loop (inst_info_t*, mod->infoList, index, info) + { + if (info->offset == leafPC_offset) + { + dtype_id = info->memop->datatype_id; + break; + } + } + dobj = mod->get_dobj (dtype_id); + if (dobj == NULL) + { + // ensure dobj is determined + if (dtype_id == DataObject::UNSPECIFIED_ID) + errcode = PTXT (DOBJ_UNSPECIFIED); + else + errcode = PTXT (DOBJ_UNIDENTIFIED); + } + else + { + // determine associated master dataobject + if (!dobj->master && dobj->scope) + dobj->master = dbeSession->createMasterDataObject (dobj); + if (dobj->scope) + dobj = dobj->master; // use associated master + } + } + if (!dobj) + { + // if dobj is not set yet, supply a dobj for errcode + // search for a dobj with the same name + dobj = dbeSession->find_dobj_by_name (errcode); + if (dobj == NULL) + { + // create new DataObject for unknown code + dobj = (DataObject*) dbeSession->createHistObject (Histable::DOBJECT); + dobj->size = 0; + dobj->offset = -1; + dobj->parent = dbeSession->get_Unknown_DataObject (); + dobj->set_dobjname (errcode, NULL); // dobj->parent must already be set + } + } + dview->setObjValue (PROP_HWCDOBJ, i, dobj); + } + break; + default: + break; + } + return dobj; +} + +Hist_data * +DataSpace::compute_metrics (MetricList *mlist, Histable::Type type, + Hist_data::Mode mode, Histable *sel_obj) +{ + int nmetrics = mlist->get_items ()->size (); + int sort_ind = -1; + Hist_data::HistItem *hi; + int index; + + // reset event_data count for all datatypes + Vector<LoadObject*> *lobjs = dbeSession->get_text_segments (); + for (int i = 0, sz = lobjs ? lobjs->size () : -1; i < sz; i++) + { + LoadObject *lo = lobjs->fetch (i); + Vector<Module*> *modules = lo->seg_modules; + for (int j = 0, msize = modules ? modules->size () : -1; j < msize; j++) + { + Module *mod = modules->fetch (j); + mod->reset_datatypes (); + } + } + Hist_data *hist_data = new Hist_data (mlist, type, mode); + + // add each experiment, skipping disabled and broken experiments + for (index = 0; index < dbeSession->nexps (); index++) + { + Experiment *exp = dbeSession->get_exp (index); + if (exp->broken) + continue; + + Collection_params *params = exp->get_params (); + if (!params->xhw_mode) + continue; + + char *expt_name = exp->get_expt_name (); + char *base_name = strrchr (expt_name, '/'); + base_name = base_name ? base_name + 1 : expt_name; + + // Determine mapping of experiment HWC metrics to hist_data metric list + int *xlate = new int[MAX_HWCOUNT]; + for (unsigned i = 0; i < MAX_HWCOUNT; i++) + { + xlate[i] = -1; + if (params->hw_interval[i] > 0) + { + const char *ctr_name = params->hw_aux_name[i]; + int mindex; + Metric *met; + Vec_loop (Metric*, mlist->get_items (), mindex, met) + { + if (dbe_strcmp (met->get_cmd (), ctr_name) == 0) + xlate[i] = mindex; + } + } + } + + // + // Process hardware profiling data + // + DataView *dview = dbev->get_filtered_events (index, DATA_HWC); + if (dview) + { + DataDescriptor *ddscr = dview ->getDataDescriptor (); + if (ddscr->getProp (PROP_HWCDOBJ) == NULL) + { + PropDescr *prop = new PropDescr (PROP_HWCDOBJ, NTXT ("HWCDOBJ")); + prop->uname = NULL; + prop->vtype = TYPE_OBJ; + ddscr->addProperty (prop); + } + } + if (dview && dview->getSize () != 0) + { + char *msg = NULL; + for (long i = 0; i < dview->getSize (); i++) + { + if (i % 5000 == 0) + { + int percent = (int) (100.0 * i / dview->getSize ()); + if (percent == 0 && msg == NULL) + msg = dbe_sprintf (GTXT ("Filtering HW Profile Address Data: %s"), base_name); + theApplication->set_progress (percent, (percent != 0) ? NULL : msg); + } + + uint32_t tag = dview->getIntValue (PROP_HWCTAG, i); + if (tag < 0 || tag >= MAX_HWCOUNT) + continue; // invalid HWC tag in the record; ignore it + int mHwcntr_idx = xlate[tag]; + if (mHwcntr_idx < 0) + continue; + + Vaddr leafVA = (Vaddr) dview->getLongValue (PROP_VADDR, i); + if (leafVA == ABS_UNSUPPORTED) + continue; // skip this record + Histable *obj = get_hist_obj (type, dview, i); + if (obj == NULL) + continue; + uint64_t interval = dview->getLongValue (PROP_HWCINT, i); + if (HWCVAL_HAS_ERR (interval)) + continue; + if (mode == Hist_data::ALL) + { // data_objects + hi = hist_data->append_hist_item (obj); + hi->value[mHwcntr_idx].ll += interval; + for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) + { + hi = hist_data->append_hist_item (dobj); + hi->value[mHwcntr_idx].ll += interval; + } + } + else if (mode == Hist_data::LAYOUT || mode == Hist_data::DETAIL) + { // data_single + { + // for data layout, insert elements that have no metrics yet + DataObject *tmpParent = ((DataObject *) obj)->parent; + if (tmpParent && tmpParent->get_typename ()) + { + // dobj is an aggregate element + if (!hist_data->find_hist_item (tmpParent)) + { + // parent not yet a member of hist_data + // supply parent's children with 0 values for layout + Vector<DataObject*> *elements = dbeSession->get_dobj_elements (tmpParent); + for (long eli = 0, sz = elements->size (); eli < sz; eli++) + { + DataObject* element = elements->fetch (eli); + assert (!hist_data->find_hist_item (element)); + hi = hist_data->append_hist_item (element); + } + } + } + } + + // Same as for mode == Hist_data::ALL: + hi = hist_data->append_hist_item (obj); + hi->value[mHwcntr_idx].ll += interval; + for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) + { + hi = hist_data->append_hist_item (dobj); + hi->value[mHwcntr_idx].ll += interval; + } + } + else if (mode == Hist_data::SELF) + { // used by dbeGetSummary() + if (obj == sel_obj) + { + hi = hist_data->append_hist_item (obj); + hi->value[mHwcntr_idx].ll += interval; + } + else + { + for (DataObject *dobj = ((DataObject *) obj)->parent; dobj; dobj = dobj->parent) + { + if ((Histable*) dobj == sel_obj) + { + hi = hist_data->append_hist_item (dobj); + hi->value[mHwcntr_idx].ll += interval; + break; + } + } + } + } + // Update total + hist_data->total->value[mHwcntr_idx].ll += interval; + } + free (msg); + theApplication->set_progress (0, NTXT ("")); + } + delete[] xlate; + } + + // include a regular HistItem for <Total> -- for all DataObjects, and MemObjects + DataObject *dtot = dbeSession->get_Total_DataObject (); + if (mode == Hist_data::ALL || mode == Hist_data::DETAIL + || mode == Hist_data::LAYOUT || + sel_obj == dtot) + { + hi = hist_data->append_hist_item (dtot); + for (int mind = 0; mind < nmetrics; mind++) + hi->value[mind] = hist_data->total->value[mind]; + } + if (hist_data->get_status () != Hist_data::SUCCESS) + return hist_data; + theApplication->set_progress (0, GTXT ("Constructing Metrics")); + + // Determine by which metric to sort if any + bool rev_sort = mlist->get_sort_rev (); + + // Compute static metrics: SIZES, ADDRESS. + for (int mind = 0; mind < nmetrics; mind++) + { + Metric *mtr = mlist->get_items ()->fetch (mind); + if (mlist->get_sort_ref_index () == mind) + sort_ind = mind; + else if (!mtr->is_visible () && !mtr->is_tvisible () + && !mtr->is_pvisible ()) + continue; + Metric::Type mtype = mtr->get_type (); + if (mtype == Metric::SIZES) + { + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + Histable *h = mtr->get_comparable_obj (hi->obj); + hi->value[mind].tag = VT_LLONG; + hi->value[mind].ll = h ? h->get_size () : 0; + } + } + else if (mtype == Metric::ONAME + && (mode == Hist_data::SELF + || ((DataObject*) sel_obj == dbeSession->get_Total_DataObject ()))) + { + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = VT_OFFSET; // offset labels + } + } + else if (mtype == Metric::ADDRESS) + { // pseudo-address + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = VT_ADDRESS; + Histable *h = mtr->get_comparable_obj (hi->obj); + hi->value[mind].ll = h ? h->get_addr () : 0; + } + // force sort by offset // XXXX should visibility also be set? + if (mode == Hist_data::SELF) + { // used by dbeGetSummary() + sort_ind = mind; + //hist_data->metrics->fetch(mind)->set_visible(T); + } + } + else + { + ValueTag vtype = mtr->get_vtype (); + switch (vtype) + { + case VT_ULLONG: // most Data-derived HWC metrics are VT_ULLONG + hist_data->total->value[mind].tag = vtype; + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = vtype; + } + break; + case VT_DOUBLE: + { + double prec = mtr->get_precision (); + hist_data->total->value[mind].tag = vtype; + hist_data->total->value[mind].d = hist_data->total->value[mind].ll / prec; + Vec_loop (Hist_data::HistItem *, hist_data->hist_items, index, hi) + { + hi->value[mind].tag = vtype; + hi->value[mind].d = hi->value[mind].ll / prec; + } + break; + } + default: + if (mtr->get_subtype () != Metric::STATIC) + abort (); + break; + } + } + } + hist_data->sort (sort_ind, rev_sort); + hist_data->compute_minmax (); + theApplication->set_progress (0, NTXT ("")); + return hist_data; +} + + +// generate annotated structure info for data_layout +// note: similar data traversal found in er_print_histogram::dump_detail() +Hist_data * +DataSpace::get_layout_data (Hist_data *sorted_data, + Vector<int> *marks, int /* _threshold */) +{ + Hist_data *data_items = NULL; + Hist_data::HistItem *new_item; + MetricList *mlist = new MetricList (sorted_data->get_metric_list ()); + int no_metrics = mlist->get_items ()->size (); + int index, addr_index = -1, name_index = -1; + Dprintf (DEBUG_DATAOBJ, NTXT ("DataSpace::get_layout_data(ALL)\n")); + + // Allocate a new Hist_data for the list, to be copied from the DataObect list + data_items = new Hist_data (mlist, Histable::DOBJECT, Hist_data::MODL); + data_items->set_status (sorted_data->get_status ()); + + // suppress threshold setting + // XXX this threshold should probably not be used + sorted_data->set_threshold ((double) 75. / 100.0); + TValue* all_empty = new TValue[no_metrics]; + memset (all_empty, 0, sizeof (TValue) * no_metrics); + + Metric *mitem; + Vec_loop (Metric*, mlist->get_items (), index, mitem) + { + // new data items have same total as original items + data_items->total->value[index] = sorted_data->total->value[index]; + // empty metric items need matching types + all_empty[index].tag = mitem->get_vtype (); + if (mitem->get_type () == Metric::ONAME) name_index = index; + if (mitem->get_type () == Metric::ADDRESS) addr_index = index; + } + + int64_t next_elem_offset = 0; + for (long i = 0; i < sorted_data->size (); i++) + { + Hist_data::HistItem* ditem = sorted_data->fetch (i); + DataObject* dobj = (DataObject*) (ditem->obj); + if (!dobj->get_parent ()) + { // doesn't have a parent; top level item + next_elem_offset = 0; + if (i > 0) + { // add a blank line as separator + // fixme xxxxx, is it really ok to create a DataObject just for this? + DataObject* empty = new DataObject (); + empty->size = 0; + empty->offset = 0; + empty->set_name (NTXT ("")); + new_item = sorted_data->new_hist_item (empty, Module::AT_EMPTY, all_empty); + new_item->value[name_index].tag = VT_LABEL; + new_item->value[name_index].l = NULL; + data_items->append_hist_item (new_item); + } + // then add the aggregate + new_item = sorted_data->new_hist_item (dobj, Module::AT_SRC, ditem->value); + new_item->value[name_index].tag = VT_OFFSET; + new_item->value[name_index].l = dbe_strdup (dobj->get_name ()); + data_items->append_hist_item (new_item); + } + else + { // is a child + if (dobj->get_parent ()->get_typename ()) + { // typed sub-element that has offset + if (dobj->offset > next_elem_offset) + { // filler entry + // hole in offsets + // fixme xxxxx, is it really ok to create a DataObject just for this? + DataObject* filler = new DataObject (); + filler->set_name (PTXT (DOBJ_ANON)); + filler->size = (dobj->offset - next_elem_offset); + filler->offset = next_elem_offset; + new_item = sorted_data->new_hist_item (filler, Module::AT_EMPTY, all_empty); + new_item->value[name_index].tag = VT_OFFSET; + new_item->value[name_index].l = dbe_strdup (filler->get_offset_name ()); + if (addr_index >= 0) + { + new_item->value[addr_index].tag = VT_ADDRESS; + new_item->value[addr_index].ll = (dobj->get_addr () - filler->size); + } + data_items->append_hist_item (new_item); + } + next_elem_offset = dobj->offset + dobj->size; + } + // then add the aggregate's subelement + if (marks) + if (sorted_data->above_threshold (ditem)) + marks->append (data_items->size ()); + new_item = sorted_data->new_hist_item (dobj, Module::AT_DIS, ditem->value); + new_item->value[name_index].tag = VT_OFFSET; + new_item->value[name_index].l = dbe_strdup (dobj->get_offset_name ()); + data_items->append_hist_item (new_item); + } + } + delete[] all_empty; + return data_items; +} |