76bdc7266a
This adds 'Innovative Computing Labs' as an external author to update-copyright.py, to cover the copyright notice in gprofng/common/opteron_pcbe.c, and uses that plus another external author 'Oracle and' to update gprofng copyright dates. I'm not going to commit 'Oracle and' as an accepted author, but that covers the string "Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved." found in gprofng/testsuite/gprofng.display/jsynprog files.
1163 lines
26 KiB
C++
1163 lines
26 KiB
C++
/* Copyright (C) 2021-2023 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->src_line = NULL;
|
|
t->included_from = NULL;
|
|
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;
|
|
}
|