| /* 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 <assert.h> | |
| #include <string.h> | |
| #include <ctype.h> | |
| #include "demangle.h" | |
| #include "util.h" | |
| #include "DbeSession.h" | |
| #include "Function.h" | |
| #include "Module.h" | |
| #include "LoadObject.h" | |
| #include "Settings.h" | |
| #include "DbeFile.h" | |
| #include "DbeView.h" | |
| struct SrcInfo | |
| { | |
| DbeLine *src_line; | |
| SrcInfo *included_from; | |
| SrcInfo *next; | |
| }; | |
| struct PCInfo | |
| { | |
| int64_t offset; | |
| int64_t size; | |
| SrcInfo *src_info; | |
| }; | |
| Function::Function (uint64_t _id) | |
| { | |
| id = _id; | |
| instr_id = id << 32; | |
| derivedNode = NULL; | |
| module = NULL; | |
| line_first = line_last = -1; | |
| isOutlineFunction = false; | |
| name = NULL; | |
| mangled_name = NULL; | |
| match_name = NULL; | |
| comparable_name = NULL; | |
| img_fname = NULL; | |
| img_offset = 0; | |
| chksum = 0; | |
| flags = 0; | |
| size = 0; | |
| save_addr = FUNC_NO_SAVE; | |
| linetab = NULL; | |
| def_source = NULL; | |
| indexStabsLink = NULL; | |
| elfSym = NULL; | |
| sources = NULL; | |
| instrs = new Vector<DbeInstr*>; | |
| addrs = NULL; | |
| name_buf = NULL; | |
| current_name_format = Histable::NA; | |
| curr_srcinfo = NULL; | |
| curr_srcfile = NULL; | |
| srcinfo_list = NULL; | |
| defaultDbeLine = NULL; | |
| usrfunc = NULL; | |
| alias = NULL; | |
| instHTable = NULL; | |
| addrIndexHTable = NULL; | |
| isUsed = false; | |
| isHideFunc = false; | |
| inlinedSubr = NULL; | |
| inlinedSubrCnt = 0; | |
| } | |
| Function::~Function () | |
| { | |
| free (mangled_name); | |
| free (match_name); | |
| free (comparable_name); | |
| free (name_buf); | |
| Destroy (linetab); | |
| Destroy (instrs); | |
| while (srcinfo_list) | |
| { | |
| SrcInfo *t = srcinfo_list; | |
| srcinfo_list = t->next; | |
| delete t; | |
| } | |
| delete sources; | |
| delete addrs; | |
| delete[] instHTable; | |
| delete[] addrIndexHTable; | |
| if (indexStabsLink) | |
| // Remove a link to the current function | |
| indexStabsLink->indexStabsLink = NULL; | |
| } | |
| char * | |
| Function::get_name (NameFormat nfmt) | |
| { | |
| if (nfmt == Histable::NA) | |
| { | |
| DbeView *dbeView = dbeSession->getView (0); | |
| if (dbeView) | |
| nfmt = dbeView->get_name_format (); | |
| } | |
| if (name_buf && (nfmt == current_name_format || nfmt == Histable::NA)) | |
| return name_buf; | |
| free (name_buf); | |
| current_name_format = nfmt; | |
| bool soname_fmt = Histable::soname_fmt (nfmt); | |
| int fname_fmt = Histable::fname_fmt (nfmt); | |
| if (fname_fmt == Histable::MANGLED) | |
| name_buf = strdup (mangled_name); | |
| else | |
| { | |
| if (module && module->is_fortran () | |
| && (streq (name, "MAIN") || streq (name, "MAIN_"))) | |
| name_buf = strdup (match_name); | |
| else | |
| name_buf = strdup (name); | |
| if (fname_fmt == Histable::SHORT) | |
| { | |
| int i = get_paren (name_buf); | |
| if (i != -1) | |
| name_buf[i] = (char) 0; | |
| } | |
| } | |
| if (soname_fmt) | |
| { | |
| char *fname = dbe_sprintf (NTXT ("%s [%s]"), name_buf, module->loadobject->get_name ()); | |
| free (name_buf); | |
| name_buf = fname; | |
| } | |
| return name_buf; | |
| } | |
| uint64_t | |
| Function::get_addr () | |
| { | |
| LoadObject *lo = module ? module->loadobject : NULL; | |
| int seg_idx = lo ? lo->seg_idx : -1; | |
| return MAKE_ADDRESS (seg_idx, img_offset); | |
| } | |
| Histable * | |
| Function::convertto (Histable_type type, Histable *obj) | |
| { | |
| Histable *res = NULL; | |
| SourceFile *source = (SourceFile*) obj; | |
| switch (type) | |
| { | |
| case INSTR: | |
| res = find_dbeinstr (0, 0); | |
| break; | |
| case LINE: | |
| { | |
| // mapPCtoLine will implicitly read line info if necessary | |
| res = mapPCtoLine (0, source); | |
| break; | |
| } | |
| case FUNCTION: | |
| res = this; | |
| break; | |
| case SOURCEFILE: | |
| res = def_source; | |
| break; | |
| default: | |
| assert (0); | |
| } | |
| return res; | |
| } | |
| void | |
| Function::set_name (char *string) | |
| { | |
| if (string == NULL) | |
| return; | |
| set_mangled_name (string); | |
| // strip away any globalization prefix, and save result for matching | |
| char *mname = string; | |
| if (strncmp (string, "$X", 2) == 0 || strncmp (string, ".X", 2) == 0) | |
| { | |
| // name was globalized | |
| char *n = strchr (string + 2, (int) '.'); | |
| if (n != NULL) | |
| mname = n + 1; | |
| } | |
| set_match_name (mname); | |
| name = NULL; | |
| if (module) | |
| { | |
| if (name == NULL && *match_name == '_') | |
| { | |
| int flag = DMGL_PARAMS; | |
| if (module->lang_code == Sp_lang_java) | |
| flag |= DMGL_JAVA; | |
| name = cplus_demangle (match_name, flag); | |
| } | |
| } | |
| if (name == NULL) // got demangled string | |
| name = dbe_strdup (match_name); | |
| set_comparable_name (name); | |
| } | |
| void | |
| Function::set_mangled_name (const char *string) | |
| { | |
| if (string) | |
| { | |
| free (mangled_name); | |
| mangled_name = dbe_strdup (string); | |
| } | |
| } | |
| void | |
| Function::set_match_name (const char *string) | |
| { | |
| if (string) | |
| { | |
| free (match_name); | |
| match_name = dbe_strdup (string); | |
| } | |
| } | |
| void | |
| Function::set_comparable_name (const char *string) | |
| { | |
| if (string) | |
| { | |
| free (comparable_name); | |
| comparable_name = dbe_strdup (string); | |
| // remove blanks from comparable_name | |
| for (char *s = comparable_name, *s1 = comparable_name;;) | |
| { | |
| if (*s == 0) | |
| { | |
| *s1 = 0; | |
| break; | |
| } | |
| else if (*s != ' ') | |
| { | |
| *s1 = *s; | |
| s1++; | |
| } | |
| s++; | |
| } | |
| } | |
| } | |
| // This function looks at the name of a function, and determines whether | |
| // or not it may be a derived function -- outline, mtask, or clone -- | |
| // If it is, it writes the function name as demangled, | |
| // and sets a pointer to the function from which it was derived | |
| void | |
| Function::findDerivedFunctions () | |
| { | |
| MPFuncTypes ftype; | |
| int index; | |
| Function *fitem; | |
| unsigned long long line_no; | |
| char *namefmt; | |
| char *subname = mangled_name; | |
| char *demname; | |
| // see if we've already done this | |
| if ((flags & FUNC_FLAG_RESDER) != 0) | |
| return; | |
| // set flag for future | |
| flags = flags | FUNC_FLAG_RESDER; | |
| if (module == NULL) | |
| return; | |
| if (*subname != '_' || subname[1] != '$') // Not a specially named function | |
| return; | |
| // look for the current versions of naming | |
| if (strncmp (subname + 2, NTXT ("d1"), 2) == 0) // doall function | |
| ftype = MPF_DOALL; | |
| else if (strncmp (subname + 2, "p1", 2) == 0) // parallel region function | |
| ftype = MPF_PAR; | |
| else if (strncmp (subname + 2, "l1", 2) == 0) // single thread loop setup | |
| ftype = MPF_DOALL; | |
| else if (strncmp (subname + 2, "s1", 2) == 0) // parallel section function | |
| ftype = MPF_SECT; | |
| else if (strncmp (subname + 2, "t1", 2) == 0) // task function | |
| ftype = MPF_TASK; | |
| else if (strncmp (subname + 2, "o1", 2) == 0) // outline function | |
| { | |
| ftype = MPF_OUTL; | |
| isOutlineFunction = true; | |
| } | |
| else if (strncmp (subname + 2, "c1", 2) == 0) // clone function | |
| ftype = MPF_CLONE; | |
| else // Not an encoded name, just return | |
| return; | |
| // we know it's one of the above prefixes | |
| char *sub = dbe_strdup (name + 4); // starting with base-26 number | |
| char *p = sub; | |
| // skip the base-26 number, and extract the line number | |
| while (isalpha ((int) (*p)) != 0 && *p != 0) | |
| p++; | |
| line_no = atoll (p); | |
| // skip past the number to to the . | |
| while (*p != '.' && *p != 0) | |
| p++; | |
| if (*p == 0) | |
| { | |
| // can't be right | |
| free (sub); | |
| return; | |
| } | |
| // skip the trailing . | |
| p++; | |
| subname = p; | |
| bool foundmatch = false; | |
| // Find the function from which it is derived -- the one that matched subname | |
| Vec_loop (Function*, module->functions, index, fitem) | |
| { | |
| if (streq (subname, fitem->mangled_name)) | |
| { // found it | |
| foundmatch = true; | |
| usrfunc = fitem; | |
| // set the derived node | |
| if ((fitem->flags & FUNC_FLAG_RESDER) == 0) | |
| // ensure that it, too, is resolved if derived | |
| fitem->findDerivedFunctions (); | |
| // Build a demangled name | |
| switch (ftype) | |
| { | |
| case MPF_OUTL: | |
| isOutlineFunction = true; | |
| namefmt = GTXT ("%s -- outline code from line %lld [%s]"); | |
| derivedNode = fitem->find_dbeinstr (PCLineFlag, line_no); | |
| break; | |
| case MPF_PAR: | |
| namefmt = GTXT ("%s -- OMP parallel region from line %lld [%s]"); | |
| break; | |
| case MPF_DOALL: | |
| namefmt = GTXT ("%s -- Parallel loop from line %lld [%s]"); | |
| break; | |
| case MPF_SECT: | |
| namefmt = GTXT ("%s -- OMP sections from line %lld [%s]"); | |
| break; | |
| case MPF_CLONE: | |
| // Note that clones are handled differently -- no line number and | |
| // clones are NOT shown as called from the original | |
| // so after constructing the name, just return | |
| // later, establish link from clone to parent | |
| demname = dbe_sprintf (GTXT ("%s -- cloned version [%s]"), | |
| fitem->get_name (), name); | |
| free (name); | |
| // set the name to the demangled version | |
| name = demname; | |
| free (sub); | |
| derivedNode = fitem->find_dbeinstr (PCLineFlag, line_no); | |
| return; | |
| case MPF_TASK: | |
| namefmt = GTXT ("%s -- OMP task from line %lld [%s]"); | |
| break; | |
| default: | |
| free (sub); | |
| return; | |
| } | |
| // Finally, construct the demangled name | |
| demname = dbe_sprintf (namefmt, fitem->get_name (), line_no, name); | |
| free (name); | |
| name = demname; | |
| setLineFirst ((int) line_no); | |
| break; | |
| } | |
| } | |
| if (foundmatch == false && ftype == MPF_OUTL) | |
| { | |
| // Even if derived node was not found, we can demangle | |
| demname = dbe_sprintf (GTXT ("%s -- outline code [%s]"), subname, | |
| mangled_name); | |
| free (name); | |
| name = demname; | |
| } | |
| free (sub); | |
| } | |
| SrcInfo * | |
| Function::new_srcInfo () | |
| { | |
| SrcInfo *t = new SrcInfo (); | |
| t->next = srcinfo_list; | |
| srcinfo_list = t; | |
| return t; | |
| } | |
| void | |
| Function::pushSrcFile (SourceFile* source, int /*lineno*/) | |
| { | |
| // create new file stack | |
| if (curr_srcfile == NULL) | |
| { | |
| curr_srcfile = source; | |
| return; | |
| } | |
| SrcInfo *src_info = new_srcInfo (); | |
| // In the ideal world, we need a DbeLine(III) here, | |
| // but right now it would make us later believe that there are | |
| // instructions generated for #include lines. To avoid that, | |
| // we ask for a DbeLine(II). | |
| src_info->src_line = curr_srcfile->find_dbeline (this, 0 /*lineno*/); | |
| if (src_info->src_line) | |
| { | |
| src_info->included_from = curr_srcinfo; | |
| curr_srcinfo = src_info; | |
| } | |
| curr_srcfile = source; | |
| setSource (); | |
| } | |
| SourceFile * | |
| Function::popSrcFile () | |
| { | |
| if (curr_srcinfo != NULL) | |
| { | |
| curr_srcfile = curr_srcinfo->src_line->sourceFile; | |
| curr_srcinfo = curr_srcinfo->included_from; | |
| } | |
| else | |
| curr_srcfile = NULL; | |
| return curr_srcfile; | |
| } | |
| void | |
| Function::copy_PCInfo (Function *from) | |
| { | |
| if (line_first <= 0) | |
| line_first = from->line_first; | |
| if (line_last <= 0) | |
| line_last = from->line_last; | |
| if (def_source == NULL) | |
| def_source = from->def_source; | |
| for (int i = 0, sz = from->linetab ? from->linetab->size () : 0; i < sz; i++) | |
| { | |
| PCInfo *pcinf = from->linetab->fetch (i); | |
| DbeLine *dbeLine = pcinf->src_info->src_line; | |
| add_PC_info (pcinf->offset, dbeLine->lineno, dbeLine->sourceFile); | |
| } | |
| } | |
| void | |
| Function::add_PC_info (uint64_t offset, int lineno, SourceFile *cur_src) | |
| { | |
| if (lineno <= 0 || size < 0 || offset >= (uint64_t) size) | |
| return; | |
| if (cur_src == NULL) | |
| cur_src = curr_srcfile ? curr_srcfile : def_source; | |
| if (linetab == NULL) | |
| linetab = new Vector<PCInfo*>; | |
| int left = 0; | |
| int right = linetab->size () - 1; | |
| DbeLine *dbeline; | |
| while (left <= right) | |
| { | |
| int x = (left + right) / 2; | |
| PCInfo *pcinf = linetab->fetch (x); | |
| uint64_t pcinf_offset = ((uint64_t) pcinf->offset); | |
| if (offset == pcinf_offset) | |
| { | |
| dbeline = cur_src->find_dbeline (this, lineno); | |
| dbeline->init_Offset (offset); | |
| pcinf->src_info->src_line = dbeline; | |
| // Ignore duplicate offset | |
| return; | |
| } | |
| else if (offset > pcinf_offset) | |
| left = x + 1; | |
| else | |
| right = x - 1; | |
| } | |
| PCInfo *pcinfo = new PCInfo; | |
| pcinfo->offset = offset; | |
| // Form new SrcInfo | |
| SrcInfo *srcInfo = new_srcInfo (); | |
| dbeline = cur_src->find_dbeline (this, lineno); | |
| dbeline->init_Offset (offset); | |
| srcInfo->src_line = dbeline; | |
| // For now don't build included_from list. | |
| // We need better compiler support for that. | |
| //srcInfo->included_from = curr_srcinfo; | |
| srcInfo->included_from = NULL; | |
| pcinfo->src_info = srcInfo; | |
| // Update the size of the current line in both structures: | |
| // current PCInfo and corresponding DbeLine. | |
| if (left < linetab->size ()) | |
| pcinfo->size = linetab->fetch (left)->offset - offset; | |
| else | |
| pcinfo->size = size - offset; | |
| pcinfo->src_info->src_line->size += pcinfo->size; | |
| // If not the first line, update the size of the previous line | |
| if (left > 0) | |
| { | |
| PCInfo *pcinfo_prev = linetab->fetch (left - 1); | |
| int64_t delta = (offset - pcinfo_prev->offset) - pcinfo_prev->size; | |
| pcinfo_prev->size += delta; | |
| pcinfo_prev->src_info->src_line->size += delta; | |
| } | |
| linetab->insert (left, pcinfo); | |
| if (cur_src == def_source) | |
| { | |
| if (line_first <= 0) | |
| setLineFirst (lineno); | |
| if (line_last <= 0 || lineno > line_last) | |
| line_last = lineno; | |
| } | |
| } | |
| PCInfo * | |
| Function::lookup_PCInfo (uint64_t offset) | |
| { | |
| module->read_stabs (); | |
| if (linetab == NULL) | |
| linetab = new Vector<PCInfo*>; | |
| int left = 0; | |
| int right = linetab->size () - 1; | |
| while (left <= right) | |
| { | |
| int x = (left + right) / 2; | |
| PCInfo *pcinfo = linetab->fetch (x); | |
| if (offset >= ((uint64_t) pcinfo->offset)) | |
| { | |
| if (offset < (uint64_t) (pcinfo->offset + pcinfo->size)) | |
| return pcinfo; | |
| left = x + 1; | |
| } | |
| else | |
| right = x - 1; | |
| } | |
| return NULL; | |
| } | |
| DbeInstr* | |
| Function::mapLineToPc (DbeLine *dbeLine) | |
| { | |
| if (dbeLine && linetab) | |
| { | |
| DbeLine *dbl = dbeLine->dbeline_base; | |
| for (int i = 0, sz = linetab->size (); i < sz; i++) | |
| { | |
| PCInfo *pcinfo = linetab->get (i); | |
| if (pcinfo->src_info | |
| && (pcinfo->src_info->src_line->dbeline_base == dbl)) | |
| { | |
| DbeInstr *dbeInstr = find_dbeinstr (PCLineFlag, pcinfo->offset); | |
| if (dbeInstr) | |
| { | |
| dbeInstr->lineno = dbeLine->lineno; | |
| return dbeInstr; | |
| } | |
| } | |
| } | |
| } | |
| return NULL; | |
| } | |
| DbeLine* | |
| Function::mapPCtoLine (uint64_t addr, SourceFile *src) | |
| { | |
| PCInfo *pcinfo = lookup_PCInfo (addr); | |
| if (pcinfo == NULL) | |
| { | |
| if (defaultDbeLine == NULL) | |
| defaultDbeLine = getDefSrc ()->find_dbeline (this, 0); | |
| return defaultDbeLine; | |
| } | |
| DbeLine *dbeline = pcinfo->src_info->src_line; | |
| // If source-context is not specified return the line | |
| // from which this pc has been generated. | |
| if (src == NULL) | |
| return dbeline; | |
| if (dbeline->sourceFile == src) | |
| return dbeline->dbeline_base; | |
| return src->find_dbeline (this, 0); | |
| } | |
| DbeInstr * | |
| Function::find_dbeinstr (int flag, uint64_t addr) | |
| { | |
| DbeInstr *instr; | |
| enum | |
| { | |
| FuncInstHTableSize = 128 | |
| }; | |
| int hash = (((int) addr) >> 2) & (FuncInstHTableSize - 1); | |
| if (instHTable == NULL) | |
| { | |
| if (size > 2048) | |
| { | |
| instHTable = new DbeInstr*[FuncInstHTableSize]; | |
| for (int i = 0; i < FuncInstHTableSize; i++) | |
| instHTable[i] = NULL; | |
| } | |
| } | |
| else | |
| { | |
| instr = instHTable[hash]; | |
| if (instr && instr->addr == addr && instr->flags == flag) | |
| return instr; | |
| } | |
| int left = 0; | |
| int right = instrs->size () - 1; | |
| while (left <= right) | |
| { | |
| int index = (left + right) / 2; | |
| instr = instrs->fetch (index); | |
| if (addr < instr->addr) | |
| right = index - 1; | |
| else if (addr > instr->addr) | |
| left = index + 1; | |
| else | |
| { | |
| if (flag == instr->flags) | |
| { | |
| if (instHTable) | |
| instHTable[hash] = instr; | |
| return instr; | |
| } | |
| else if (flag < instr->flags) | |
| right = index - 1; | |
| else | |
| left = index + 1; | |
| } | |
| } | |
| // None found, create a new one | |
| instr = new DbeInstr (instr_id++, flag, this, addr); | |
| instrs->insert (left, instr); | |
| if (instHTable) | |
| instHTable[hash] = instr; | |
| return instr; | |
| } | |
| // LIBRARY_VISIBILITY | |
| DbeInstr * | |
| Function::create_hide_instr (DbeInstr *instr) | |
| { | |
| DbeInstr *new_instr = new DbeInstr (instr_id++, 0, this, instr->addr); | |
| return new_instr; | |
| } | |
| uint64_t | |
| Function::find_previous_addr (uint64_t addr) | |
| { | |
| if (addrs == NULL) | |
| { | |
| addrs = module->getAddrs (this); | |
| if (addrs == NULL) | |
| return addr; | |
| } | |
| int index = -1, not_found = 1; | |
| enum | |
| { | |
| FuncAddrIndexHTableSize = 128 | |
| }; | |
| int hash = (((int) addr) >> 2) & (FuncAddrIndexHTableSize - 1); | |
| if (addrIndexHTable == NULL) | |
| { | |
| if (size > 2048) | |
| { | |
| addrIndexHTable = new int[FuncAddrIndexHTableSize]; | |
| for (int i = 0; i < FuncAddrIndexHTableSize; i++) | |
| addrIndexHTable[i] = -1; | |
| } | |
| } | |
| else | |
| { | |
| index = addrIndexHTable[hash]; | |
| if (index >= 0 && addrs->fetch (index) == addr) | |
| not_found = 0; | |
| } | |
| int left = 0; | |
| int right = addrs->size () - 1; | |
| while (not_found && left <= right) | |
| { | |
| index = (left + right) / 2; | |
| uint64_t addr_test = addrs->fetch (index); | |
| if (addr < addr_test) | |
| right = index - 1; | |
| else if (addr > addr_test) | |
| left = index + 1; | |
| else | |
| { | |
| if (addrIndexHTable) | |
| addrIndexHTable[hash] = index; | |
| not_found = 0; | |
| } | |
| } | |
| if (not_found) | |
| return addr; | |
| if (index > 0) | |
| index--; | |
| return addrs->fetch (index); | |
| } | |
| void | |
| Function::setSource () | |
| { | |
| SourceFile *sf = module->getIncludeFile (); | |
| if (sf == NULL) | |
| sf = getDefSrc (); | |
| if (def_source == NULL) | |
| setDefSrc (sf); | |
| if (sf == def_source) | |
| return; | |
| if (sources == NULL) | |
| { | |
| sources = new Vector<SourceFile*>; | |
| sources->append (def_source); | |
| sources->append (sf); | |
| } | |
| else if (sources->find (sf) < 0) | |
| sources->append (sf); | |
| } | |
| void | |
| Function::setDefSrc (SourceFile *sf) | |
| { | |
| if (sf) | |
| { | |
| def_source = sf; | |
| if (line_first > 0) | |
| add_PC_info (0, line_first, def_source); | |
| } | |
| } | |
| void | |
| Function::setLineFirst (int lineno) | |
| { | |
| if (lineno > 0) | |
| { | |
| line_first = lineno; | |
| if (line_last <= 0) | |
| line_last = lineno; | |
| if (def_source) | |
| add_PC_info (0, line_first, def_source); | |
| } | |
| } | |
| Vector<SourceFile*> * | |
| Function::get_sources () | |
| { | |
| if (module) | |
| module->read_stabs (); | |
| if (sources == NULL) | |
| { | |
| sources = new Vector<SourceFile*>; | |
| sources->append (getDefSrc ()); | |
| } | |
| return sources; | |
| } | |
| SourceFile* | |
| Function::getDefSrc () | |
| { | |
| if (module) | |
| module->read_stabs (); | |
| if (def_source == NULL) | |
| setDefSrc (module->getMainSrc ()); | |
| return def_source; | |
| } | |
| char * | |
| Function::getDefSrcName () | |
| { | |
| SourceFile *sf = getDefSrc (); | |
| if (sf) | |
| return sf->dbeFile->getResolvedPath (); | |
| if (module) | |
| return module->file_name; | |
| sf = dbeSession->get_Unknown_Source (); | |
| return sf->get_name (); | |
| } | |
| #define cmpValue(a, b) ((a) > (b) ? 1 : (a) == (b) ? 0 : -1) | |
| int | |
| Function::func_cmp (Function *func, SourceFile *srcContext) | |
| { | |
| if (def_source != func->def_source) | |
| { | |
| if (srcContext == NULL) | |
| srcContext = getDefSrc (); | |
| if (def_source == srcContext) | |
| return -1; | |
| if (func->def_source == srcContext) | |
| return 1; | |
| return cmpValue (img_offset, func->img_offset); | |
| } | |
| if (line_first == func->line_first) | |
| return cmpValue (img_offset, func->img_offset); | |
| if (line_first <= 0) | |
| { | |
| if (func->line_first > 0) | |
| return 1; | |
| return cmpValue (img_offset, func->img_offset); | |
| } | |
| if (func->line_first <= 0) | |
| return -1; | |
| return cmpValue (line_first, func->line_first); | |
| } | |
| Vector<Histable*> * | |
| Function::get_comparable_objs () | |
| { | |
| update_comparable_objs (); | |
| if (comparable_objs || dbeSession->expGroups->size () <= 1 || module == NULL) | |
| return comparable_objs; | |
| if (module == NULL || module->loadobject == NULL) | |
| return NULL; | |
| Vector<Histable*> *comparableModules = module->get_comparable_objs (); | |
| if (comparableModules == NULL) | |
| { | |
| return NULL; | |
| } | |
| comparable_objs = new Vector<Histable*>(comparableModules->size ()); | |
| for (long i = 0, sz = comparableModules->size (); i < sz; i++) | |
| { | |
| Function *func = NULL; | |
| comparable_objs->store (i, func); | |
| Module *mod = (Module*) comparableModules->fetch (i); | |
| if (mod == NULL) | |
| continue; | |
| if (mod == module) | |
| func = this; | |
| else | |
| { | |
| for (long i1 = 0, sz1 = VecSize (mod->functions); i1 < sz1; i1++) | |
| { | |
| Function *f = mod->functions->get (i1); | |
| if ((f->comparable_objs == NULL) | |
| && (strcmp (f->comparable_name, comparable_name) == 0)) | |
| { | |
| func = f; | |
| func->comparable_objs = comparable_objs; | |
| break; | |
| } | |
| } | |
| } | |
| comparable_objs->store (i, func); | |
| } | |
| Vector<Histable*> *comparableLoadObjs = | |
| module->loadobject->get_comparable_objs (); | |
| if (VecSize (comparableLoadObjs) == VecSize (comparable_objs)) | |
| { | |
| for (long i = 0, sz = VecSize (comparableLoadObjs); i < sz; i++) | |
| { | |
| LoadObject *lo = (LoadObject *) comparableLoadObjs->get (i); | |
| Function *func = (Function *) comparable_objs->get (i); | |
| if (func || (lo == NULL)) | |
| continue; | |
| if (module->loadobject == lo) | |
| func = this; | |
| else | |
| { | |
| for (long i1 = 0, sz1 = VecSize (lo->functions); i1 < sz1; i1++) | |
| { | |
| Function *f = lo->functions->fetch (i1); | |
| if ((f->comparable_objs == NULL) | |
| && (strcmp (f->comparable_name, comparable_name) == 0)) | |
| { | |
| func = f; | |
| func->comparable_objs = comparable_objs; | |
| break; | |
| } | |
| } | |
| } | |
| comparable_objs->store (i, func); | |
| } | |
| } | |
| dump_comparable_objs (); | |
| return comparable_objs; | |
| } | |
| JMethod::JMethod (uint64_t _id) : Function (_id) | |
| { | |
| mid = 0LL; | |
| addr = (Vaddr) 0; | |
| signature = NULL; | |
| jni_function = NULL; | |
| } | |
| JMethod::~JMethod () | |
| { | |
| free (signature); | |
| } | |
| uint64_t | |
| JMethod::get_addr () | |
| { | |
| if (addr != (Vaddr) 0) | |
| return addr; | |
| else | |
| return Function::get_addr (); | |
| } | |
| typedef struct | |
| { | |
| size_t used_in; | |
| size_t used_out; | |
| } MethodField; | |
| static void | |
| write_buf (char* buf, char* str) | |
| { | |
| while ((*buf++ = *str++)); | |
| } | |
| /** Translate one field from the nane buffer. | |
| * return how many chars were read from name and how many bytes were used in buf. | |
| */ | |
| static MethodField | |
| translate_method_field (const char* name, char* buf) | |
| { | |
| MethodField out, t; | |
| switch (*name) | |
| { | |
| case 'L': | |
| name++; | |
| out.used_in = 1; | |
| out.used_out = 0; | |
| while (*name != ';') | |
| { | |
| *buf = *name++; | |
| if (*buf == '/') | |
| *buf = '.'; | |
| buf++; | |
| out.used_in++; | |
| out.used_out++; | |
| } | |
| out.used_in++; /* the ';' is also used. */ | |
| break; | |
| case 'Z': | |
| write_buf (buf, NTXT ("boolean")); | |
| out.used_out = 7; | |
| out.used_in = 1; | |
| break; | |
| case 'B': | |
| write_buf (buf, NTXT ("byte")); | |
| out.used_out = 4; | |
| out.used_in = 1; | |
| break; | |
| case 'C': | |
| write_buf (buf, NTXT ("char")); | |
| out.used_out = 4; | |
| out.used_in = 1; | |
| break; | |
| case 'S': | |
| write_buf (buf, NTXT ("short")); | |
| out.used_out = 5; | |
| out.used_in = 1; | |
| break; | |
| case 'I': | |
| write_buf (buf, NTXT ("int")); | |
| out.used_out = 3; | |
| out.used_in = 1; | |
| break; | |
| case 'J': | |
| write_buf (buf, NTXT ("long")); | |
| out.used_out = 4; | |
| out.used_in = 1; | |
| break; | |
| case 'F': | |
| write_buf (buf, NTXT ("float")); | |
| out.used_out = 5; | |
| out.used_in = 1; | |
| break; | |
| case 'D': | |
| write_buf (buf, NTXT ("double")); | |
| out.used_out = 6; | |
| out.used_in = 1; | |
| break; | |
| case 'V': | |
| write_buf (buf, NTXT ("void")); | |
| out.used_out = 4; | |
| out.used_in = 1; | |
| break; | |
| case '[': | |
| t = translate_method_field (name + 1, buf); | |
| write_buf (buf + t.used_out, NTXT ("[]")); | |
| out.used_out = t.used_out + 2; | |
| out.used_in = t.used_in + 1; | |
| break; | |
| default: | |
| out.used_out = 0; | |
| out.used_in = 0; | |
| } | |
| return out; | |
| } | |
| /** | |
| * translate method name to full method signature | |
| * into the output buffer (buf). | |
| * ret_type - true for printing result type | |
| */ | |
| static bool | |
| translate_method (char* mname, char *signature, bool ret_type, char* buf) | |
| { | |
| MethodField p; | |
| size_t l; | |
| int first = 1; | |
| if (signature == NULL) | |
| return false; | |
| const char *c = strchr (signature, ')'); | |
| if (c == NULL) | |
| return false; | |
| if (ret_type) | |
| { | |
| p = translate_method_field (++c, buf); | |
| buf += p.used_out; | |
| *buf++ = ' '; | |
| } | |
| l = strlen (mname); | |
| memcpy (buf, mname, l + 1); | |
| buf += l; | |
| // *buf++ = ' '; // space before () | |
| *buf++ = '('; | |
| c = signature + 1; | |
| while (*c != ')') | |
| { | |
| if (!first) | |
| { | |
| *buf++ = ','; | |
| *buf++ = ' '; | |
| } | |
| first = 0; | |
| p = translate_method_field (c, buf); | |
| c += p.used_in; | |
| buf += p.used_out; | |
| } | |
| *buf++ = ')'; | |
| *buf = '\0'; | |
| return true; | |
| } | |
| void | |
| JMethod::set_name (char *string) | |
| { | |
| if (string == NULL) | |
| return; | |
| set_mangled_name (string); | |
| char buf[MAXDBUF]; | |
| *buf = '\0'; | |
| if (translate_method (string, signature, false, buf)) | |
| { | |
| name = dbe_strdup (buf); // got translated string | |
| Dprintf (DUMP_JCLASS_READER, | |
| "JMethod::set_name: true name=%s string=%s signature=%s\n", | |
| STR (name), STR (string), STR (signature)); | |
| } | |
| else | |
| { | |
| name = dbe_strdup (string); | |
| Dprintf (DUMP_JCLASS_READER, | |
| "JMethod::set_name: false name=%s signature=%s\n", | |
| STR (name), STR (signature)); | |
| } | |
| set_match_name (name); | |
| set_comparable_name (name); | |
| } | |
| bool | |
| JMethod::jni_match (Function *func) | |
| { | |
| if (func == NULL || (func->flags & FUNC_NOT_JNI) != 0) | |
| return false; | |
| if (jni_function == func) | |
| return true; | |
| char *fname = func->get_name (); | |
| if ((func->flags & FUNC_JNI_CHECKED) == 0) | |
| { | |
| func->flags |= FUNC_JNI_CHECKED; | |
| if (strncmp (func->get_name (), NTXT ("Java_"), 5) != 0) | |
| { | |
| func->flags |= FUNC_NOT_JNI; | |
| return false; | |
| } | |
| } | |
| char *d = name; | |
| char *s = fname + 5; | |
| while (*d && *d != '(' && *d != ' ') | |
| { | |
| if (*d == '.') | |
| { | |
| if (*s++ != '_') | |
| return false; | |
| d++; | |
| } | |
| else if (*d == '_') | |
| { | |
| if (*s++ != '_') | |
| return false; | |
| if (*s++ != '1') | |
| return false; | |
| d++; | |
| } | |
| else if (*d++ != *s++) | |
| return false; | |
| } | |
| jni_function = func; | |
| return true; | |
| } |