| /* Copyright (C) 2021 Free Software Foundation, Inc. | |
| Contributed by Oracle. | |
| This file is part of GNU Binutils. | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3, or (at your option) | |
| any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program; if not, write to the Free Software | |
| Foundation, 51 Franklin Street - Fifth Floor, Boston, | |
| MA 02110-1301, USA. */ | |
| #include "config.h" | |
| #include <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; | |
| } |