/* Copyright (C) 2021-2024 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 #include #include #include #include "util.h" #include "DbeSession.h" #include "Experiment.h" #include "DataObject.h" #include "Function.h" #include "DbeView.h" #include "MetricList.h" #include "Module.h" #include "ClassFile.h" #include "LoadObject.h" #include "Disasm.h" #include "CompCom.h" #include "Dwarf.h" #include "DbeFile.h" #include "PathTree.h" #include "Elf.h" Module::Module () { lang_code = Sp_lang_unknown; flags = 0; status = AE_NOTREAD; openSourceFlag = AE_NOTREAD; hexVisible = false; disPath = NULL; stabsPath = NULL; stabsTmp = NULL; disName = NULL; stabsName = NULL; indexStabsLink = NULL; file_name = NULL; functions = new Vector; loadobject = NULL; dot_o_file = NULL; main_source = dbeSession->get_Unknown_Source (); srcContext = main_source; includes = new Vector; includes->append (main_source); curr_inc = NULL; fragmented = 0; hwcprof = 0; hdrOffset = 0; hasDwarf = false; hasStabs = false; readStabs = false; comComs = NULL; infoList = NULL; datatypes = NULL; objStabs = NULL; disasm = NULL; comp_flags = NULL; comp_dir = NULL; linkerStabName = NULL; disMTime = (time_t) 0; stabsMTime = (time_t) 0; real_timestamp = 0; curr_timestamp = 0; src_items = NULL; dis_items = NULL; data_items = NULL; cur_dbev = NULL; maximum = NULL; maximum_inc = NULL; empty = NULL; inlinedSubr = NULL; } Module::~Module () { removeStabsTmp (); delete includes; if (comComs != NULL) { comComs->destroy (); delete comComs; } free (comp_flags); free (comp_dir); free (linkerStabName); free (disPath); free (stabsPath); free (disName); free (stabsName); delete functions; free (file_name); if (indexStabsLink) // Remove a link to the current module indexStabsLink->indexStabsLink = NULL; if (dot_o_file) { delete dot_o_file->dbeFile; delete dot_o_file; } delete src_items; delete dis_items; delete disasm; free (inlinedSubr); if (lang_code != Sp_lang_java) delete dbeFile; } Stabs * Module::openDebugInfo () { setFile (); objStabs = loadobject->openDebugInfo (disPath); return objStabs; } void Module::removeStabsTmp () { // Remove temporary *.o (got from *.a) after reading Stabs if (stabsTmp != NULL) { unlink (stabsTmp); free (stabsTmp); stabsTmp = NULL; } } int64_t Module::get_size () { Function *fp; int index; int64_t result = 0; Vec_loop (Function*, functions, index, fp) { result += fp->size; } return result; } bool Module::is_fortran () { return Stabs::is_fortran (lang_code); } SourceFile * Module::findSource (const char *fname, bool create) { SourceFile *sf = NULL; if (loadobject && loadobject->firstExp) sf = loadobject->firstExp->get_source (fname); if (sf == NULL) sf = dbeSession->createSourceFile (fname); for (int i = 0, sz = includes ? includes->size () : 0; i < sz; i++) { SourceFile *sf1 = includes->fetch (i); if (sf == sf1) return sf; } if (create) { if (includes == NULL) includes = new Vector; includes->append (sf); return sf; } return NULL; } SourceFile * Module::setIncludeFile (char *includeFile) { curr_inc = NULL; if (includeFile) curr_inc = findSource (includeFile, true); return curr_inc; } char * Module::anno_str (char *fnm) { char timebuf1[26], timebuf2[26]; const time_t real_time = (time_t) (unsigned int) real_timestamp; const time_t curr_time = (time_t) (unsigned int) curr_timestamp; switch (status) { case AE_OK: case AE_NOTREAD: return NULL; case AE_NOSRC: return dbe_sprintf (GTXT ("Source file `%s' not readable"), fnm ? fnm : file_name); case AE_NOOBJ: if (lang_code == Sp_lang_java) { Emsg *emsg = get_error (); if (emsg) { char *s = dbe_strdup (emsg->get_msg ()); remove_msg (emsg); return s; } return dbe_sprintf (GTXT ("Object file `%s.class' not readable"), name); } return dbe_sprintf (GTXT ("Object file `%s' not readable"), get_name ()); case AE_NOLOBJ: if (lang_code == Sp_lang_java) return dbe_sprintf (GTXT ("Object file `%s' not readable"), dbeFile ? dbeFile->get_name () : name); return dbe_sprintf (GTXT ("Object file `%s' not readable"), loadobject->get_pathname ()); case AE_NOSTABS: return dbe_sprintf (GTXT ("Error reading line-number information in object `%s'; source annotation not available"), stabsPath ? stabsPath : NTXT ("")); case AE_NOSYMTAB: return dbe_sprintf (GTXT ("Error reading symbol table in object `%s'; disassembly annotation not available"), disPath ? disPath : NTXT ("")); case AE_TIMESRC: return dbe_sprintf (GTXT ("Warning! Source file `%s' is newer than the experiment data"), main_source->dbeFile->getResolvedPath ()); case AE_TIMEDIS: return dbe_sprintf (GTXT ("Warning! Object file `%s' is newer than the experiment data"), disName ? disName : NTXT ("")); case AE_TIMESTABS: return dbe_sprintf (GTXT ("Warning! Object file `%s' is newer than the experiment data"), stabsName ? stabsName : NTXT ("")); case AE_TIMESTABS_DIFF: snprintf (timebuf1, sizeof (timebuf1), NTXT ("%s"), ctime (&curr_time)); snprintf (timebuf2, sizeof (timebuf2), NTXT ("%s"), ctime (&real_time)); timebuf1[24] = timebuf2[24] = '\0'; return dbe_sprintf (GTXT ("Warning! Object file `%s' is not the same one that was linked into executable.\n" "\tObject file: `%s'\n\tcompiled on: %s\n" "\tExecutable contains object file compiled on: %s"), getResolvedObjectPath (), getResolvedObjectPath (), timebuf1, timebuf2); case AE_OTHER: default: return dbe_strdup (GTXT ("Annotation computation error")); } }//anno_str LoadObject * Module::createLoadObject (const char *lo_name) { LoadObject *lo = new LoadObject (lo_name); lo->dbeFile->filetype |= DbeFile::F_DOT_O; return lo; } static bool tsIsNewer (time_t t1, time_t t2) { return t1 != 0 && t2 != 0 && t1 < t2; } Module::Anno_Errors Module::checkTimeStamp (bool chkDis) { /* Check the linked and the real object timestamps due to bug #4796329 */ if (real_timestamp && curr_timestamp && real_timestamp != curr_timestamp) return AE_TIMESTABS_DIFF; time_t srctime = main_source->getMTime (); for (int index = 0; index < dbeSession->nexps (); index++) { time_t mtime = dbeSession->get_exp (index)->get_mtime (); if (tsIsNewer (mtime, srctime)) return AE_TIMESRC; if (tsIsNewer (mtime, stabsMTime)) return AE_TIMESTABS; if (chkDis && tsIsNewer (mtime, disMTime)) return AE_TIMEDIS; } return AE_OK; }//checkTimeStamp static size_t get_ar_size (char *s, size_t len) { size_t sz = 0; for (size_t i = 0; i < len; i++) { if (s[i] < '0' || s[i] > '9') break; sz = sz * 10 + (s[i] - '0'); } return sz; } static void dump_hdr_field (char *nm, char *s, size_t len) { Dprintf (DEBUG_READ_AR, NTXT (" %s "), nm); for (size_t i = 0; i < len; i++) Dprintf (DEBUG_READ_AR, "%c", isprint (s[i]) ? s[i] : '?'); Dprintf (DEBUG_READ_AR, NTXT (" ")); for (size_t i = 0; i < len; i++) Dprintf (DEBUG_READ_AR, NTXT (" %d"), s[i]); Dprintf (DEBUG_READ_AR, NTXT (" \n")); } static void dump_ar_hdr (int lineNum, struct ar_hdr *hdr) { if (DEBUG_READ_AR) { Dprintf (DEBUG_READ_AR, NTXT ("Module::read_ar %d\n"), lineNum); dump_hdr_field (NTXT ("ar_name"), hdr->ar_name, sizeof (hdr->ar_name)); dump_hdr_field (NTXT ("ar_date"), hdr->ar_date, sizeof (hdr->ar_date)); dump_hdr_field (NTXT ("ar_uid"), hdr->ar_uid, sizeof (hdr->ar_uid)); dump_hdr_field (NTXT ("ar_gid"), hdr->ar_gid, sizeof (hdr->ar_gid)); dump_hdr_field (NTXT ("ar_mode"), hdr->ar_mode, sizeof (hdr->ar_mode)); dump_hdr_field (NTXT ("ar_size"), hdr->ar_size, sizeof (hdr->ar_size)); dump_hdr_field (NTXT ("ar_fmag"), hdr->ar_fmag, sizeof (hdr->ar_fmag)); } } bool Module::read_ar (int ar, int obj, char *obj_base) { struct ar_hdr hdr; // Archive header char magic[SARMAG]; // Magic string from archive Dprintf (DEBUG_READ_AR, "Module::read_ar %d %p %s %s \n", __LINE__, this, STR (obj_base), STR (get_name ())); // Check the magic string if ((read_from_file (ar, magic, SARMAG) != SARMAG) || strncmp (magic, ARMAG, SARMAG)) return false; // Read and skip the first file in the archive (index file to ld) if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr)) return false; DEBUG_CODE dump_ar_hdr (__LINE__, &hdr); if (lseek (ar, get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)), SEEK_CUR) == -1) return false; // Read the string file where it keeps long file names (if exist) if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr)) return false; DEBUG_CODE dump_ar_hdr (__LINE__, &hdr); char *longnames = NULL; // Area with names longer than ~13 size_t longnames_size = 0; if (!strncmp (hdr.ar_name, NTXT ("//"), 2)) { longnames_size = get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)); longnames = (char *) xmalloc (longnames_size + 1); int64_t cnt = read_from_file (ar, longnames, longnames_size); if (cnt != (int64_t) longnames_size) { free (longnames); return false; } longnames[longnames_size] = 0; } else // back out, no long file names lseek (ar, -(sizeof (hdr)), SEEK_CUR); // Search the ar for the object file name char ar_buf[sizeof (hdr.ar_name) + 1]; ar_buf[sizeof (hdr.ar_name)] = 0; while (1) { if (read_from_file (ar, &hdr, sizeof (hdr)) != sizeof (hdr)) break; DEBUG_CODE dump_ar_hdr (__LINE__, &hdr); char *ar_name; if (hdr.ar_name[0] != '/') { // Name is in the header for (size_t i = 0; i < sizeof (hdr.ar_name); i++) { if (hdr.ar_name[i] == '/') { ar_buf[i] = 0; break; } ar_buf[i] = hdr.ar_name[i]; } ar_name = ar_buf; } else if (hdr.ar_name[1] == ' ') { // Name is blank ar_buf[0] = 0; ar_name = ar_buf; } else { // Name is in the string table if (longnames == NULL) break; size_t offset = get_ar_size (hdr.ar_name + 1, sizeof (hdr.ar_name) - 1); if (offset >= longnames_size) break; for (size_t i = offset; i < longnames_size; i++) { if (longnames[i] == '/') { longnames[i] = 0; break; } } ar_name = longnames + offset; } Dprintf (DEBUG_READ_AR, "Module::read_ar %d ar_name=%s\n", __LINE__, ar_name); if (streq (ar_name, obj_base)) { // create object file free (longnames); for (size_t objsize = get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)); objsize > 0;) { char buf[MAXPATHLEN]; size_t n = objsize < sizeof (buf) ? objsize : sizeof (buf); int64_t cnt = read_from_file (ar, buf, n); if (cnt != (int64_t) n) return false; cnt = write (obj, buf, n); if (cnt != (int64_t) n) return false; objsize -= n; } return true; } if (lseek (ar, get_ar_size (hdr.ar_size, sizeof (hdr.ar_size)), SEEK_CUR) == -1) break; } free (longnames); return false; } static char * get_obj_name_from_lib (char *nm) { char *base = strrchr (nm, '('); if (base) { size_t last = strlen (base) - 1; if (base[last] == ')') return base; } return NULL; } bool Module::setFile () { if ((loadobject->flags & SEG_FLAG_DYNAMIC) != 0) return true; if ((loadobject->dbeFile->filetype & DbeFile::F_FICTION) != 0) return false; if ((flags & MOD_FLAG_UNKNOWN) != 0) return true; if (lang_code == Sp_lang_java) { if (dbeFile->get_need_refind ()) { char *fnm = dbeFile->get_location (); stabsPath = dbe_strdup (fnm); stabsName = dbe_strdup (fnm); disPath = dbe_strdup (fnm); disName = dbe_strdup (fnm); stabsMTime = dbeFile->sbuf.st_mtime; } return dbeFile->get_location () != NULL; } if (dbeFile == NULL) { char *objname = get_obj_name_from_lib (name); if (objname) { // in the format of libpath(obj) objname = dbe_strdup (objname + 1); size_t last = strlen (objname) - 1; objname[last] = '\0'; } dbeFile = new DbeFile (objname ? objname : name); free (objname); dbeFile->filetype |= DbeFile::F_DOT_O; } if (dbeFile->get_need_refind ()) { disMTime = (time_t) 0; stabsMTime = (time_t) 0; free (disName); free (stabsName); disName = NULL; stabsName = NULL; // Find the Executable/Shared-Object file of module char *path = loadobject->dbeFile->get_location (); if (path) { disPath = xstrdup (path); disName = xstrdup (path); disMTime = loadobject->dbeFile->sbuf.st_mtime; } char *objname = get_obj_name_from_lib (name); if (objname) { // in the format of libpath(obj) char *namebuf = dbe_strdup (name); char *base = namebuf + (objname - name); *base = '\0'; base++; size_t last = strlen (base) - 1; base[last] = '\0'; stabsTmp = dbeSession->get_tmp_file_name (base, false); dbeSession->tmp_files->append (xstrdup (stabsTmp)); DbeFile *dbf = dbeSession->getDbeFile (namebuf, DbeFile::F_DOT_A_LIB | DbeFile::F_FILE); path = dbf->get_location (); int ar = -1, obj = -1; if (path != NULL) { ar = open64 (path, O_RDONLY | O_LARGEFILE); if (ar != -1) obj = open64 (stabsTmp, O_CREAT | O_WRONLY | O_LARGEFILE, 0600); } if (ar != -1 && obj != -1 && read_ar (ar, obj, base)) { dbeFile->set_location (stabsTmp); dbeFile->check_access (stabsTmp); // init 'sbuf' dbeFile->sbuf.st_mtime = 0; // Don't check timestamps dbeFile->container = dbf; stabsPath = xstrdup (stabsTmp); stabsName = xstrdup (path); stabsMTime = dbeFile->sbuf.st_mtime; } else { removeStabsTmp (); objname = NULL; } if (ar != -1) close (ar); if (obj != -1) close (obj); free (namebuf); } if (objname == NULL) { path = dbeFile->get_location (); if (path != NULL) { stabsPath = xstrdup (path); stabsName = xstrdup (path); stabsMTime = hasDwarf ? 0 : dbeFile->sbuf.st_mtime; } } // First, try to access the symbol table of the module itself // If failed, access the symbol table of the executable if (stabsPath == NULL) { if (disPath == NULL) return false; stabsPath = xstrdup (disPath); stabsName = xstrdup (disName); stabsMTime = disMTime; } else if (disPath == NULL) { disPath = xstrdup (stabsPath); disName = xstrdup (stabsName); disMTime = stabsMTime; } } return stabsPath != NULL; } // openStabs -- open mappings from PCs to source lines bool Module::openStabs (bool all) { if ((loadobject->flags & SEG_FLAG_DYNAMIC) != 0 || (flags & MOD_FLAG_UNKNOWN) != 0) return true; if (loadobject->platform == Java) { setIncludeFile (NULL); readFile (); return ( status == AE_OK); } if (readStabs) return true; // Read Stabs info. int64_t Inode = main_source->getInode (); char *fname = strrchr (file_name, (int) '/'); char *mname = strrchr (main_source->get_name (), (int) '/'); if (fname && mname && !streq (fname, mname)) { SourceFile *sf = findSource (file_name, false); if (sf != NULL) Inode = sf->getInode (); } comComs = new Vector; Stabs *stabs = openDebugInfo (); if (stabs == NULL) return false; int st = stabs->read_stabs (Inode, this, comComs, true); if (!hasDwarf && hasStabs && !streq (stabsPath, disPath)) { // Read stabs from .o file if (dot_o_file == NULL) { if (dbeFile->get_location ()) { dot_o_file = createLoadObject (dbeFile->get_name ()); dot_o_file->dbeFile->set_location (dbeFile->get_location ()); dot_o_file->dbeFile->sbuf = dbeFile->sbuf; dot_o_file->dbeFile->container = dbeFile->container; } } if (dot_o_file && dot_o_file->sync_read_stabs () == LoadObject::ARCHIVE_SUCCESS) { Stabs *stabs_o = dot_o_file->objStabs; if (stabs_o) { st = stabs_o->read_stabs (Inode, this, comComs->size () > 0 ? NULL : comComs); Elf *elf_o = stabs_o->openElf (false); if (elf_o->dwarf) stabs->read_dwarf_from_dot_o (this); } } } if (all) read_hwcprof_info (); readStabs = true; return st == Stabs::DBGD_ERR_NONE; } char * Module::get_disasm (uint64_t inst_address, uint64_t end_address, uint64_t start_address, uint64_t address, int64_t &inst_size) { return disasm->get_disasm (inst_address, end_address, start_address, address, inst_size); } void Module::read_stabs (bool all) { if (openSourceFlag == AE_NOTREAD) { openSourceFlag = AE_OK; if (lang_code == Sp_lang_java) { char *clpath = file_name; if (clpath == NULL || strcmp (clpath, "") == 0) clpath = ClassFile::get_java_file_name (name, false); main_source = findSource (clpath, true); main_source->dbeFile->filetype |= DbeFile::F_JAVA_SOURCE; if (clpath != file_name) free (clpath); } else main_source = findSource (file_name, true); if (setFile ()) openStabs (all); } } bool Module::openDisPC () { if (disasm == NULL) { if (!(loadobject->flags & SEG_FLAG_DYNAMIC) && loadobject->platform != Java) { // Read Stabs & Symbol tables if (openDebugInfo () == NULL) return false; if (!objStabs->read_symbols (functions)) return false; } disasm = new Disasm (loadobject->platform, objStabs); } return true; } static SourceFile *cmpSrcContext; // Use only for func_cmp static int func_cmp (const void *a, const void *b) { Function *fp1 = *((Function **) a); Function *fp2 = *((Function **) b); return fp1->func_cmp (fp2, cmpSrcContext); } bool Module::computeMetrics (DbeView *dbev, Function *func, MetricList *metrics, Histable::Type type, bool src_metric, bool func_scope, SourceFile *source) { name_idx = metrics->get_listorder (NTXT ("name"), Metric::STATIC); if (name_idx < 0) { metrics->print_metric_list (stderr, GTXT ("Fatal: no name metric in Module::computeMetrics mlist:\n"), 1); abort (); } // Now find the metrics for size and address, if present size_index = metrics->get_listorder (NTXT ("size"), Metric::STATIC); addr_index = metrics->get_listorder (NTXT ("address"), Metric::STATIC); // free the old cached data for both src and disassembly // If it's disassembly with visible source metrics, we use both if (dis_items) { delete dis_items; dis_items = NULL; } if (src_items) { delete src_items; src_items = NULL; } // ask the DbeView to generate new data to be cached if (src_metric || type == Histable::LINE) { Histable *obj = (func_scope) ? (Histable*) func : (Histable*)this; if (lang_code == Sp_lang_java) obj = func_scope ? (Histable *) func : (source && source->get_type () == Histable::SOURCEFILE ? (Histable *) source : (Histable *) this); src_items = dbev->get_hist_data (metrics, Histable::LINE, 0, Hist_data::MODL, obj, source); } if (type == Histable::INSTR) dis_items = dbev->get_hist_data (metrics, Histable::INSTR, 0, Hist_data::MODL, func_scope ? (Histable*) func : (Histable*) this, source); Hist_data *cur_hist_data; if (type == Histable::INSTR) cur_hist_data = dis_items; else cur_hist_data = src_items; Vector *items = cur_hist_data->get_metric_list ()->get_items (); long sz = items->size (); empty = new TValue[sz]; memset (empty, 0, sizeof (TValue) * sz); for (long i = 0; i < sz; i++) empty[i].tag = items->get (i)->get_vtype (); return true; } // Method to get annotated source or disassembly for the module // or a function within it Hist_data * Module::get_data (DbeView *dbev, MetricList *mlist, Histable::Type type, TValue *ftotal, SourceFile *srcFile, Function *func, Vector *marks, int threshold, int vis_bits, int src_visible, bool hex_vis, bool func_scope, bool /*src_only*/, Vector *marks2d, Vector *marks2d_inc) { cur_dbev = dbev; srcContext = srcFile ? srcFile : main_source; read_stabs (); status = AE_OK; dbev->warning_msg = NULL; dbev->error_msg = NULL; if (type == Histable::LINE) { if (!srcContext->readSource ()) { status = AE_NOSRC; dbev->error_msg = anno_str (srcContext->get_name ()); return NULL; } if (!computeMetrics (dbev, func, mlist, type, false, func_scope, srcContext)) { status = AE_OTHER; dbev->error_msg = anno_str (); return NULL; } status = checkTimeStamp (false); } else { // Histable::INSTR Anno_Errors src_status = AE_OK; if (!srcContext->readSource ()) { src_status = AE_NOSRC; dbev->error_msg = anno_str (srcContext->get_name ()); } if (!setFile ()) status = AE_NOLOBJ; else { if (!openStabs ()) src_status = AE_NOSTABS; if (!openDisPC ()) status = AE_NOSYMTAB; } if (status != AE_OK) { dbev->error_msg = anno_str (); return NULL; } if (src_status != AE_OK && func != NULL) { if (loadobject->platform == Java && (func->flags & FUNC_FLAG_NATIVE) != 0) { append_msg (CMSG_ERROR, GTXT ("`%s' is a native method; byte code not available\n"), func->get_name ()); status = AE_NOOBJ; dbev->error_msg = anno_str (); return NULL; } func_scope = true; } // get the disassembly-line metric data if (!computeMetrics (dbev, func, mlist, type, (src_visible & SRC_METRIC) != 0, func_scope, srcContext)) { status = AE_OTHER; dbev->error_msg = anno_str (); return NULL; } status = checkTimeStamp (true); } total = ftotal; // initialize line number init_line (); // initialize data -- get duplicate metric list for the line texts // pick up the metric list from the computed data MetricList *nmlist = NULL; if (type == Histable::INSTR) { mlist = dis_items->get_metric_list (); nmlist = new MetricList (mlist); data_items = new Hist_data (nmlist, Histable::INSTR, Hist_data::MODL); data_items->set_status (dis_items->get_status ()); set_dis_data (func, vis_bits, dbev->get_cmpline_visible (), src_visible, hex_vis, func_scope, dbev->get_funcline_visible ()); } else { mlist = src_items->get_metric_list (); nmlist = new MetricList (mlist); data_items = new Hist_data (nmlist, Histable::LINE, Hist_data::MODL); data_items->set_status (src_items->get_status ()); set_src_data (func_scope ? func : NULL, vis_bits, dbev->get_cmpline_visible (), dbev->get_funcline_visible ()); } data_items->compute_minmax (); Metric *mitem; int index; Hist_data::HistItem *max_item; TValue *value; Hist_data::HistItem *max_item_inc; TValue *value_inc; double dthreshold = threshold / 100.0; int sz = data_items->get_metric_list ()->get_items ()->size (); maximum = new TValue[sz]; maximum_inc = new TValue[sz]; memset (maximum, 0, sizeof (TValue) * sz); memset (maximum_inc, 0, sizeof (TValue) * sz); max_item = data_items->get_maximums (); max_item_inc = data_items->get_maximums_inc (); Vec_loop (Metric*, data_items->get_metric_list ()->get_items (), index, mitem) { maximum_inc[index].tag = maximum[index].tag = mitem->get_vtype (); if (mitem->get_subtype () == Metric::STATIC) continue; if (!mitem->is_visible () && !mitem->is_tvisible () && !mitem->is_pvisible ()) continue; value = &max_item->value[index]; value_inc = &max_item_inc->value[index]; double dthresh; if (mitem->is_zeroThreshold () == true) dthresh = 0; else dthresh = dthreshold; switch (value->tag) { case VT_INT: maximum[index].i = (int) (dthresh * (double) value->i); maximum_inc[index].i = (int) (dthresh * (double) value_inc->i); break; case VT_DOUBLE: maximum[index].d = dthresh * value->d; maximum_inc[index].d = dthresh * value_inc->d; break; case VT_LLONG: maximum[index].ll = (unsigned long long) (dthresh * (double) value->ll); maximum_inc[index].ll = (unsigned long long) (dthresh * (double) value_inc->ll); break; case VT_ULLONG: maximum[index].ull = (unsigned long long) (dthresh * (double) value->ull); maximum_inc[index].ull = (unsigned long long) (dthresh * (double) value_inc->ull); break; default: // not needed for non-numerical metrics break; } } // mark all high values for (int index1 = 0; index1 < data_items->size (); index1++) { Hist_data::HistItem *hi = data_items->fetch (index1); int index2; Vec_loop (Metric*, nmlist->get_items (), index2, mitem) { bool mark = false; if (mitem->get_subtype () == Metric::STATIC) continue; if (!mitem->is_visible () && !mitem->is_tvisible () && !mitem->is_pvisible ()) continue; switch (hi->value[index2].tag) { case VT_DOUBLE: if (nmlist->get_type () == MET_SRCDIS && data_items->get_callsite_mark ()->get (hi->obj)) { if (hi->value[index2].d > maximum_inc[index2].d) mark = true; break; } if (hi->value[index2].d > maximum[index2].d) mark = true; break; case VT_INT: if (nmlist->get_type () == MET_SRCDIS && data_items->get_callsite_mark ()->get (hi->obj)) { if (hi->value[index2].i > maximum_inc[index2].i) mark = true; break; } if (hi->value[index2].i > maximum[index2].i) mark = true; break; case VT_LLONG: if (nmlist->get_type () == MET_SRCDIS && data_items->get_callsite_mark ()->get (hi->obj)) { if (hi->value[index2].ll > maximum_inc[index2].ll) mark = true; break; } if (hi->value[index2].ll > maximum[index2].ll) mark = true; break; case VT_ULLONG: if (nmlist->get_type () == MET_SRCDIS && data_items->get_callsite_mark ()->get (hi->obj)) { if (hi->value[index2].ull > maximum_inc[index2].ull) mark = true; break; } if (hi->value[index2].ull > maximum[index2].ull) mark = true; break; // ignoring the following cases (why?) case VT_SHORT: case VT_FLOAT: case VT_HRTIME: case VT_LABEL: case VT_ADDRESS: case VT_OFFSET: break; } if (mark) { marks->append (index1); break; } } } // mark all high values to marks2d if (marks2d != NULL && marks2d_inc != NULL) { for (int index1 = 0; index1 < data_items->size (); index1++) { Hist_data::HistItem *hi = data_items->fetch (index1); int index2; Vec_loop (Metric*, nmlist->get_items (), index2, mitem) { Metric::SubType subType = mitem->get_subtype (); if (subType == Metric::STATIC) continue; if (!mitem->is_visible () && !mitem->is_tvisible () && !mitem->is_pvisible ()) continue; switch (hi->value[index2].tag) { case VT_DOUBLE: if (nmlist->get_type () == MET_SRCDIS && data_items->get_callsite_mark ()->get (hi->obj)) { if (hi->value[index2].d > maximum_inc[index2].d) { int_pair_t pair = {index1, index2}; marks2d_inc->append (pair); } break; } if (hi->value[index2].d > maximum[index2].d) { int_pair_t pair = {index1, index2}; marks2d->append (pair); } break; case VT_INT: if (nmlist->get_type () == MET_SRCDIS && data_items->get_callsite_mark ()->get (hi->obj)) { if (hi->value[index2].i > maximum_inc[index2].i) { int_pair_t pair = {index1, index2}; marks2d_inc->append (pair); } break; } if (hi->value[index2].i > maximum[index2].i) { int_pair_t pair = {index1, index2}; marks2d->append (pair); } break; case VT_LLONG: if (nmlist->get_type () == MET_SRCDIS && data_items->get_callsite_mark ()->get (hi->obj)) { if (hi->value[index2].ll > maximum_inc[index2].ll) { int_pair_t pair = {index1, index2}; marks2d_inc->append (pair); } break; } if (hi->value[index2].ll > maximum[index2].ll) { int_pair_t pair = {index1, index2}; marks2d->append (pair); } break; case VT_ULLONG: if (nmlist->get_type () == MET_SRCDIS && data_items->get_callsite_mark ()->get (hi->obj)) { if (hi->value[index2].ull > maximum_inc[index2].ull) { int_pair_t pair = {index1, index2}; marks2d_inc->append (pair); } break; } if (hi->value[index2].ull > maximum[index2].ull) { int_pair_t pair = {index1, index2}; marks2d->append (pair); } break; case VT_SHORT: case VT_FLOAT: case VT_HRTIME: case VT_LABEL: case VT_ADDRESS: case VT_OFFSET: break; } } } } // free memory used by Computing & Printing metrics delete[] maximum; delete[] maximum_inc; delete[] empty; maximum = NULL; maximum_inc = NULL; empty = NULL; dbev->warning_msg = anno_str (); return data_items; } Vector * Module::getAddrs (Function *func) { uint64_t start_address = func->img_offset; uint64_t end_address = start_address + func->size; int64_t inst_size = 0; // initialize "disasm" if necessary if (!openDisPC ()) return NULL; Vector *addrs = new Vector; for (uint64_t inst_address = start_address; inst_address < end_address;) { char *s = disasm->get_disasm (inst_address, end_address, start_address, func->img_offset, inst_size); free (s); addrs->append (inst_address - start_address); inst_address += inst_size; if (inst_size == 0) break; } return addrs; } void Module::init_line () { // initialize the compiler commentary data cindex = 0; if (comComs != NULL && comComs->size () > 0) cline = comComs->fetch (cindex)->line; else cline = -1; sindex = 0; if (src_items && src_items->size () > 0) sline = ((DbeLine*) src_items->fetch (0)->obj)->lineno; else sline = -1; dindex = 0; mindex = 0; mline = -1; if (dis_items && dis_items->size () > 0) { daddr = (DbeInstr*) dis_items->fetch (0)->obj; // After sorting all HistItems with PCLineFlag appear // at the end of the list. Find the first one. for (mindex = dis_items->size () - 1; mindex >= 0; mindex--) { Hist_data::HistItem *item = dis_items->fetch (mindex); if (!(((DbeInstr*) item->obj)->flags & PCLineFlag)) break; mline = (unsigned) (((DbeInstr*) item->obj)->addr); } mindex++; } else daddr = NULL; } void Module::set_src_data (Function *func, int vis_bits, int cmpline_visible, int funcline_visible) { Function *curr_func = NULL; // start at the top of the file, and loop over all lines in the file (source context) for (curline = 1; curline <= srcContext->getLineCount (); curline++) { // Before writing the line, see if there's compiler commentary to insert if (cline == curline) set_ComCom (vis_bits); // Find out if we need to print zero metrics with the line DbeLine *dbeline = srcContext->find_dbeline (NULL, curline); Anno_Types type = AT_SRC_ONLY; if (dbeline->dbeline_func_next) { if (func) for (DbeLine *dl = dbeline->dbeline_func_next; dl; dl = dl->dbeline_func_next) { if (dl->func == func) { type = AT_SRC; break; } } else type = AT_SRC; } if (funcline_visible) { // show red lines // is there a function index line to insert? Function *func_next = NULL; for (DbeLine *dl = dbeline; dl; dl = dl->dbeline_func_next) { Function *f = dl->func; if (f && f->line_first == curline && f->getDefSrc () == srcContext) { if (lang_code == Sp_lang_java && (f->flags & FUNC_FLAG_DYNAMIC)) continue; if (cur_dbev && cur_dbev->get_path_tree ()->get_func_nodeidx (f)) { func_next = f; break; } else if (func_next == NULL) func_next = f; } } if (func_next && curr_func != func_next) { curr_func = func_next; char *func_name = curr_func->get_name (); if (is_fortran () && streq (func_name, NTXT ("MAIN_"))) func_name = curr_func->get_match_name (); Hist_data::HistItem *item = src_items->new_hist_item (curr_func, AT_FUNC, empty); item->value[name_idx].l = dbe_sprintf (GTXT (""), func_name); data_items->append_hist_item (item); } } // end of red line set_src (type, dbeline); // add the source line } // end of loop over source lines // See if compiler flags are set; if so, append them if (cmpline_visible && comp_flags) { Hist_data::HistItem *item = src_items->new_hist_item (NULL, AT_EMPTY, empty); item->value[name_idx].l = xstrdup (NTXT ("")); data_items->append_hist_item (item); item = src_items->new_hist_item (NULL, AT_COM, empty); item->value[name_idx].l = dbe_sprintf (GTXT ("Compile flags: %s"), comp_flags); data_items->append_hist_item (item); } } void Module::set_dis_data (Function *func, int vis_bits, int cmpline_visible, int src_visible, bool hex_vis, bool func_scope, int funcline_visible) { bool nextFile = false; // initialize the source output, if any curline = (srcContext->getLineCount () > 0) ? 1 : -1; if (func) nextFile = srcContext != func->getDefSrc (); curr_inc = srcContext; bool src_code = (src_visible & SRC_CODE); Anno_Types src_type = (src_visible & SRC_METRIC) ? AT_SRC : AT_SRC_ONLY; char *img_fname = func ? func->img_fname : NULL; // Build a new Function list Vector *FuncLst = new Vector; if (func_scope) { if (func) FuncLst->append (func); } else { for (int i = 0, sz = functions ? functions->size () : 0; i < sz; i++) { Function *fitem = functions->fetch (i); if (fitem != fitem->cardinal ()) continue; if (img_fname == NULL) img_fname = fitem->img_fname; if (fitem->img_fname == NULL || strcmp (fitem->img_fname, img_fname)) continue; FuncLst->append (fitem); } } if (FuncLst->size () == 0) { // no function is good delete FuncLst; return; } cmpSrcContext = srcContext; FuncLst->sort (func_cmp); disasm->set_hex_visible (hex_vis); for (int index = 0, sz = FuncLst->size (); index < sz; index++) { Function *fitem = FuncLst->fetch (index); uint64_t start_address, end_address; int64_t inst_size; if (fitem->getDefSrc () != srcContext && curline > 0) { // now flush the left source line, if available for (; curline <= srcContext->getLineCount (); curline++) { // see if there's a compiler comment line to dump if (cline == curline) set_ComCom (vis_bits); if (src_code) set_src (src_type, srcContext->find_dbeline (curline)); } curline = -1; } curr_inc = NULL; // disassemble one function start_address = objStabs ? objStabs->mapOffsetToAddress (fitem->img_offset) : 0; end_address = start_address + fitem->size; inst_size = 0; disasm->set_addr_end (end_address); if ((loadobject->flags & SEG_FLAG_DYNAMIC) && loadobject->platform != Java) disasm->set_img_name (img_fname); for (uint64_t inst_address = start_address; inst_address < end_address;) { uint64_t address = inst_address - start_address; DbeInstr *instr = fitem->find_dbeinstr (0, address); DbeLine *dbeline = (DbeLine *) (instr->convertto (Histable::LINE)); if (instr->lineno == -1 && dbeline && dbeline->lineno > 0) instr->lineno = dbeline->lineno; // now write the unannotated source line, if available if (curline > 0) { // source is present int lineno = curline - 1; if (instr->lineno != -1) { if (dbeline && streq (dbeline->sourceFile->get_name (), srcContext->get_name ())) lineno = instr->lineno; } else if (curr_inc == NULL && srcContext == fitem->def_source && fitem->line_first > 0) lineno = fitem->line_first; for (; curline <= lineno; curline++) { // see if there's a compiler comment line to dump if (cline == curline) set_ComCom (vis_bits); if (mline == curline) set_MPSlave (); if (src_code) set_src (src_type, srcContext->find_dbeline (curline)); if (curline >= srcContext->getLineCount ()) { curline = -1; break; } } } if (funcline_visible) { // show red lines if (!curr_inc || (dbeline && curr_inc != dbeline->sourceFile)) { Hist_data::HistItem *item = dis_items->new_hist_item (dbeline, AT_FUNC, empty); curr_inc = dbeline ? dbeline->sourceFile : srcContext; char *str; if (curr_inc != srcContext) { char *fileName = curr_inc->dbeFile->getResolvedPath (); str = dbe_sprintf (GTXT (""), fitem->get_name (), fileName); } else str = dbe_sprintf (GTXT (""), fitem->get_name ()); item->value[name_idx].l = str; data_items->append_hist_item (item); } } char *dis_str = get_disasm (inst_address, end_address, start_address, fitem->img_offset, inst_size); if (inst_size == 0) break; else if (instr->size == 0) instr->size = (unsigned int) inst_size; inst_address += inst_size; // stomp out control characters for (size_t i = 0, len = strlen (dis_str); i < len; i++) { if (dis_str[i] == '\t') dis_str[i] = ' '; } for (int i = 0; i < bTargets.size (); i++) { target_info_t *bTarget = bTargets.fetch (i); if (bTarget->offset == fitem->img_offset + address) { // insert a new line for the bTarget size_t colon = strcspn (dis_str, NTXT (":")); char *msg = GTXT ("* "); size_t len = colon + strlen (msg); len = (len < 50) ? (50 - len) : 1; char *new_dis_str = dbe_sprintf ("%.*s%s%*c <===----<<<", (int) colon, dis_str, msg, (int) len, ' '); DbeInstr *bt = fitem->find_dbeinstr (PCTrgtFlag, address); bt->lineno = instr->lineno; bt->size = 0; set_dis (bt, AT_DIS, nextFile, new_dis_str); break; } } // AnalyzerInfo/Datatype annotations if (infoList != NULL) { inst_info_t *info = NULL; int pinfo; Vec_loop (inst_info_t*, infoList, pinfo, info) { if (info->offset == fitem->img_offset + address) break; } if (info != NULL) { // got a matching memop char typetag[400]; typetag[0] = '\0'; long t; datatype_t *dtype = NULL; Vec_loop (datatype_t*, datatypes, t, dtype) { if (dtype->datatype_id == info->memop->datatype_id) break; } if (datatypes != NULL) { size_t len = strlen (typetag); if (dtype == NULL || t == datatypes->size ()) snprintf (typetag + len, sizeof (typetag) - len, "%s", PTXT (DOBJ_UNSPECIFIED)); else if (dtype->dobj == NULL) snprintf (typetag + len, sizeof (typetag) - len, "%s", PTXT (DOBJ_UNDETERMINED)); else snprintf (typetag + len, sizeof (typetag) - len, "%s", dtype->dobj->get_name ()); } if (strlen (typetag) > 1) { char *new_dis_str; new_dis_str = dbe_sprintf ("%-50s %s", dis_str, typetag); free (dis_str); dis_str = new_dis_str; } } } set_dis (instr, AT_DIS, nextFile, dis_str); } } // now flush the left source line, if available if (curline > 0) { // source is present for (; curline <= srcContext->getLineCount (); curline++) { // see if there's a compiler comment line to dump if (cline == curline) set_ComCom (vis_bits); if (src_code) set_src (src_type, srcContext->find_dbeline (curline)); } } // See if compiler flags are set; if so, append them if (cmpline_visible && comp_flags) { Hist_data::HistItem *item = dis_items->new_hist_item (NULL, AT_EMPTY, empty); item->value[name_idx].l = dbe_strdup (NTXT ("")); data_items->append_hist_item (item); item = dis_items->new_hist_item (NULL, AT_COM, empty); item->value[name_idx].l = dbe_sprintf (GTXT ("Compile flags: %s"), comp_flags); data_items->append_hist_item (item); } delete FuncLst; } // set_src -- inserts one or more lines into the growing data list void Module::set_src (Anno_Types type, DbeLine *dbeline) { Hist_data::HistItem *item; // Flush items that are not represented in source while (sline >= 0 && sline < curline) { item = src_items->fetch (sindex); if (((DbeLine*) item->obj)->lineno > 0) set_one (item, AT_QUOTE, item->obj->get_name ()); if (++sindex < src_items->size ()) // get next line with metrics sline = ((DbeLine*) src_items->fetch (sindex)->obj)->lineno; else sline = -1; } // write values in the metric fields for the given source line if (curline == sline) { // got metrics for this line item = src_items->fetch (sindex); if (((DbeLine*) item->obj)->lineno > 0) set_one (item, AT_SRC, srcContext->getLine (curline)); if (++sindex < src_items->size ()) // get next line metric index sline = ((DbeLine*) src_items->fetch (sindex)->obj)->lineno; else sline = -1; } else { item = data_items->new_hist_item (dbeline, type, empty); if (size_index != -1) item->value[size_index].ll = dbeline->get_size (); if (addr_index != -1) item->value[addr_index].ll = dbeline->get_addr (); item->value[name_idx].l = dbe_strdup (srcContext->getLine (curline)); data_items->append_hist_item (item); } } void Module::set_dis (DbeInstr *instr, Anno_Types type, bool nextFile, char *dis_str) { // Flush items that are not represented in disassembly while (daddr && daddr->pc_cmp (instr) < 0) { if (!nextFile) set_one (dis_items->fetch (dindex), AT_QUOTE, daddr->get_name ()); if (++dindex < dis_items->size ()) // get next line metric index daddr = (DbeInstr*) dis_items->fetch (dindex)->obj; else daddr = NULL; } // Write values in the metric fields for the given pc index value if (instr->inlinedInd >= 0) { StringBuilder sb; sb.append (dis_str); instr->add_inlined_info (&sb); free (dis_str); dis_str = sb.toString (); } if (daddr && daddr->pc_cmp (instr) == 0) { Hist_data::HistItem *item = data_items->new_hist_item (instr, type, dis_items->fetch (dindex)->value); item->value[name_idx].tag = VT_LABEL; item->value[name_idx].l = dis_str; data_items->append_hist_item (item); if (dis_items->get_callsite_mark ()->get (dis_items->fetch (dindex)->obj)) data_items->get_callsite_mark ()->put (item->obj, 1); if (++dindex < dis_items->size ()) // get next line metric index daddr = (DbeInstr*) dis_items->fetch (dindex)->obj; else daddr = NULL; } else { // create a new item for this PC Hist_data::HistItem *item = dis_items->new_hist_item (instr, type, empty); if (size_index != -1) item->value[size_index].ll = instr->size; if (addr_index != -1) item->value[addr_index].ll = instr->get_addr (); item->value[name_idx].tag = VT_LABEL; item->value[name_idx].l = dis_str; data_items->append_hist_item (item); } } void Module::set_MPSlave () { Hist_data::HistItem *item; Function *fp; int index; // write the inclusive metrics for slave threads while (mline == curline) { item = dis_items->fetch (mindex); DbeInstr *instr = (DbeInstr *) item->obj; Vec_loop (Function*, functions, index, fp) { if (fp->derivedNode == instr) { set_one (item, AT_QUOTE, (fp->isOutlineFunction) ? GTXT ("") : GTXT ("")); break; } } mindex++; if (mindex < dis_items->size ()) mline = (unsigned) ((DbeInstr*) (dis_items->fetch (mindex)->obj))->addr; else mline = -1; } }//set_MPSlave void Module::set_one (Hist_data::HistItem *org_item, Anno_Types type, const char *text) { if (org_item == NULL) return; Hist_data::HistItem *item = data_items->new_hist_item (org_item->obj, type, org_item->value); item->value[name_idx].tag = VT_LABEL; item->value[name_idx].l = dbe_strdup (text); data_items->append_hist_item (item); if (org_item != NULL && src_items != NULL && src_items->get_callsite_mark ()->get (org_item->obj)) data_items->get_callsite_mark ()->put (item->obj, 1); }//set_one void Module::set_ComCom (int vis_bits) { Hist_data::HistItem *item; Function *func = dbeSession->get_Unknown_Function (); if (vis_bits) { // precede the compiler commentary with a blank line item = data_items->new_hist_item (func, AT_EMPTY, empty); item->value[name_idx].l = dbe_strdup (NTXT ("")); data_items->append_hist_item (item); } while (cline == curline) { ComC *comm = comComs->fetch (cindex); if (comm->visible & vis_bits) { // write the compiler commentary item = data_items->new_hist_item (func, AT_COM, empty); item->value[name_idx].l = dbe_strdup (comm->com_str); data_items->append_hist_item (item); } if (++cindex < comComs->size ()) cline = comComs->fetch (cindex)->line; else cline = -1; } } void Module::dump_dataobjects (FILE *out) { int index; datatype_t *dtype; Vec_loop (datatype_t*, datatypes, index, dtype) { fprintf (out, NTXT ("[0x%08X,%6lld] %4d %6d %s "), dtype->datatype_id, dtype->dobj ? dtype->dobj->id : 0LL, dtype->memop_refs, dtype->event_data, (dtype->dobj != NULL ? (dtype->dobj->get_name () ? dtype->dobj->get_name () : "") : "")); #if DEBUG Histable* scope = dtype->dobj ? dtype->dobj->get_scope () : NULL; if (scope != NULL) { switch (scope->get_type ()) { case Histable::LOADOBJECT: case Histable::FUNCTION: fprintf (out, NTXT ("%s"), scope->get_name ()); break; case Histable::MODULE: { char *filename = get_basename (scope->get_name ()); fprintf (out, NTXT ("%s"), filename); break; } default: fprintf (out, NTXT ("\tUnexpected scope %d:%s"), scope->get_type (), scope->get_name ()); } } #endif fprintf (out, NTXT ("\n")); } } void Module::set_name (char *str) { free (name); name = str; } void Module::read_hwcprof_info () { if (hwcprof == 0) { hwcprof = 1; Stabs *stabs = openDebugInfo (); if (stabs) stabs->read_hwcprof_info (this); } } void Module::reset_datatypes () { for (int i = 0, sz = datatypes ? datatypes->size () : -1; i < sz; i++) { datatype_t *t = datatypes->fetch (i); t->event_data = 0; } } DataObject * Module::get_dobj (uint32_t dtype_id) { read_hwcprof_info (); for (int i = 0, sz = datatypes ? datatypes->size () : -1; i < sz; i++) { datatype_t *t = datatypes->fetch (i); if (t->datatype_id == dtype_id) { t->event_data++; return t->dobj; } } return NULL; } int Module::readFile () { return AE_OK; } Vector * Module::get_comparable_objs () { update_comparable_objs (); if (comparable_objs || dbeSession->expGroups->size () <= 1 || loadobject == NULL) return comparable_objs; Vector *comparableLoadObjs = loadobject->get_comparable_objs (); if (comparableLoadObjs == NULL) return NULL; comparable_objs = new Vector(comparableLoadObjs->size ()); for (int i = 0, sz = comparableLoadObjs->size (); i < sz; i++) { Module *mod = NULL; LoadObject *lo = (LoadObject*) comparableLoadObjs->fetch (i); if (lo) { mod = lo->get_comparable_Module (this); if (mod) mod->comparable_objs = comparable_objs; } comparable_objs->store (i, mod); } dump_comparable_objs (); return comparable_objs; } JMethod * Module::find_jmethod (const char *nm, const char *sig) { // Vladimir: Probably we should not use linear search for (long i = 0, sz = VecSize (functions); i < sz; i++) { JMethod *jmthd = (JMethod*) functions->get (i); char *jmt_name = jmthd->get_name (Histable::SHORT); if (strcmp (jmt_name, nm) == 0 && strcmp (jmthd->get_signature (), sig) == 0) return jmthd; } return NULL; }