| /* 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 <unistd.h> |
| #include <ar.h> |
| #include <ctype.h> |
| #include <sys/param.h> |
| |
| #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<Function*>; |
| loadobject = NULL; |
| dot_o_file = NULL; |
| main_source = dbeSession->get_Unknown_Source (); |
| srcContext = main_source; |
| includes = new Vector<SourceFile*>; |
| 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<SourceFile*>; |
| 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 *) malloc (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 = strdup (path); |
| disName = strdup (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 (strdup (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 = strdup (stabsTmp); |
| stabsName = strdup (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 = strdup (path); |
| stabsName = strdup (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 = strdup (disPath); |
| stabsName = strdup (disName); |
| stabsMTime = disMTime; |
| } |
| else if (disPath == NULL) |
| { |
| disPath = strdup (stabsPath); |
| disName = strdup (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<ComC*>; |
| 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, "<Unknown>") == 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<Metric*> *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<int> *marks, int threshold, int vis_bits, |
| int src_visible, bool hex_vis, bool func_scope, |
| bool /*src_only*/, Vector<int_pair_t> *marks2d, |
| Vector<int_pair_t> *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<uint64_t> * |
| 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<uint64_t> *addrs = new Vector<uint64_t>; |
| 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 ("<Function: %s>"), |
| 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 = strdup (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<Function*> *FuncLst = new Vector<Function*>; |
| 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 ("<Function: %s, instructions from source file %s>"), |
| fitem->get_name (), fileName); |
| } |
| else |
| str = dbe_sprintf (GTXT ("<Function: %s>"), |
| 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 ("* <branch target>"); |
| 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 ("<inclusive metrics for outlined functions>") : |
| GTXT ("<inclusive metrics for slave threads>")); |
| 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 () : "<NULL>") : "<no object>")); |
| #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<Histable*> * |
| Module::get_comparable_objs () |
| { |
| update_comparable_objs (); |
| if (comparable_objs || dbeSession->expGroups->size () <= 1 || loadobject == NULL) |
| return comparable_objs; |
| Vector<Histable*> *comparableLoadObjs = loadobject->get_comparable_objs (); |
| if (comparableLoadObjs == NULL) |
| return NULL; |
| comparable_objs = new Vector<Histable*>(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; |
| } |