|
From: <sv...@va...> - 2014-06-15 15:42:34
|
Author: philippe
Date: Sun Jun 15 15:42:20 2014
New Revision: 14036
Log:
This patch implements the support needed for stacktraces
showing inlined function calls.
See 278972 valgrind stacktraces and suppression do not handle inlined function call debuginfo
Reading the inlined dwarf call info is activated using the new clo
--read-inline-info=yes
Default is currently no but an objective is to optimise the performance
and memory in order to possibly set it on by default.
(see below discussion about performances).
Basically, the patch provides the following pieces:
1. Implement a new dwarf3 reader that reads the inlined call info
2. Some performance improvements done for this new parser, and
on some common code between the new parser and the var info parser.
3. Use the parsed inlined info to produce stacktrace showing inlined calls
4. Use the parsed inlined info in the suppression matching and suppression generation
5. and of course, some reg tests
1. new dwarf3 reader:
---------------------
Two options were possible: add the reading of the inlined info
in the current var info dwarf reader, or add a 2nd reader.
The 2nd approach was preferred, for the following reasons:
The var info reader is slow, memory hungry and quite complex.
Having a separate parsing phase for the inlined information
is simpler/faster when just reading the inlined info.
Possibly, a single parser would be faster when using both
--read-var-info=yes and --read-inline-info=yes.
However, var-info being extremely memory/cpu hungry, it is unlikely
to be used often, and having a separate parsing for inlined info
does in any case make not much difference.
(--read-var-info=yes is also now less interesting thanks to commit
r13991, which provides a fast and low memory "reasonable" location
for an address).
The inlined info parser reads the dwarf info to make calls
to priv_storage.h ML_(addInlInfo).
2. performance optimisations
----------------------------
* the abbrev cache has been improved in revision r14035.
* The new parser skips the non interesting DIEs
(the var-info parser has no logic to skip uninteresting DIEs).
* Some other minor perf optimisation here and there.
In total now, on a big executable, 15 seconds CPU are needed to
create the inlined info (on my slow x86 pentium).
With regards to memory, the dinfo arena:
with inlined info: 172281856/121085952 max/curr mmap'd
without : 157892608/106721280 max/curr mmap'd,
So, basically, inlined information costs about 15Mb of memory for
my big executable (compared to first version of the patch, this is
already using less memory, thanks to the strpool deduppoolalloc.
The needed memory can probably be decreased somewhat more.
3. produce better stack traces
------------------------------
VG_(describe_IP) has a new argument InlIPCursor *iipc which allows
to describe inlined function calls by doing repetitive calls
to describe_IP. See pub_tool_debuginfo.h for a description.
4. suppression generation and matching
--------------------------------------
* suppression generation now also uses an InlIPCursor *iipc
to generate a line for each inlined fn call.
* suppression matching: to allow suppression matching to
match one IP to several function calls in a suppression entry,
the 'inputCompleter' object (that allows to lazily generate
function or object names for a stacktrace when matching
an error with a suppression) has been generalised a little bit
more to also lazily generate the input sequence.
VG_(generic_match) has been updated so as to be more generic
with respect to the input completer : when providing an
input completer, VG_(generic_match) does not need anymore
to produce/compute any input itself : this is all delegated
to the input completer.
5. various regtests
-------------------
to test stack traces with inlined calls, and suppressions
of (some of) these errors using inlined fn calls matching.
Work still to do:
-----------------
* improve parsing performance
* improve the memory overhead.
* handling the directory name for files of the inlined function calls is not yet done.
(probably implies to refactor some code)
* see if m_errormgr.c *offsets arrays cannot be managed via xarray
Added:
trunk/memcheck/tests/inlinfo.c
trunk/memcheck/tests/inlinfo.stderr.exp
trunk/memcheck/tests/inlinfo.stdout.exp
trunk/memcheck/tests/inlinfo.vgtest
trunk/memcheck/tests/inlinfosupp.stderr.exp
trunk/memcheck/tests/inlinfosupp.stdout.exp
trunk/memcheck/tests/inlinfosupp.supp
trunk/memcheck/tests/inlinfosupp.vgtest
trunk/memcheck/tests/inlinfosuppobj.stderr.exp
trunk/memcheck/tests/inlinfosuppobj.stdout.exp
trunk/memcheck/tests/inlinfosuppobj.supp
trunk/memcheck/tests/inlinfosuppobj.vgtest
Modified:
trunk/coregrind/m_debuginfo/debuginfo.c
trunk/coregrind/m_debuginfo/priv_storage.h
trunk/coregrind/m_debuginfo/readdwarf3.c
trunk/coregrind/m_debuginfo/readelf.c
trunk/coregrind/m_debuginfo/readmacho.c
trunk/coregrind/m_debuginfo/storage.c
trunk/coregrind/m_errormgr.c
trunk/coregrind/m_gdbserver/m_gdbserver.c
trunk/coregrind/m_gdbserver/target.c
trunk/coregrind/m_main.c
trunk/coregrind/m_options.c
trunk/coregrind/m_seqmatch.c
trunk/coregrind/m_stacktrace.c
trunk/coregrind/pub_core_debuginfo.h
trunk/coregrind/pub_core_options.h
trunk/docs/xml/manual-core.xml
trunk/include/pub_tool_debuginfo.h
trunk/include/pub_tool_seqmatch.h
trunk/massif/ms_main.c
trunk/memcheck/tests/Makefile.am
trunk/none/tests/cmdline1.stdout.exp
trunk/none/tests/cmdline2.stdout.exp
Modified: trunk/coregrind/m_debuginfo/debuginfo.c
==============================================================================
--- trunk/coregrind/m_debuginfo/debuginfo.c (original)
+++ trunk/coregrind/m_debuginfo/debuginfo.c Sun Jun 15 15:42:20 2014
@@ -212,6 +212,7 @@
if (di->fsm.filename) ML_(dinfo_free)(di->fsm.filename);
if (di->soname) ML_(dinfo_free)(di->soname);
if (di->loctab) ML_(dinfo_free)(di->loctab);
+ if (di->inltab) ML_(dinfo_free)(di->inltab);
if (di->cfsi) ML_(dinfo_free)(di->cfsi);
if (di->cfsi_exprs) VG_(deleteXA)(di->cfsi_exprs);
if (di->fpo) ML_(dinfo_free)(di->fpo);
@@ -1258,8 +1259,10 @@
if (VG_(clo_verbosity) > 0) {
VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: done: "
- "%lu syms, %lu src locs, %lu fpo recs\n",
- di->symtab_used, di->loctab_used, di->fpo_size);
+ "%lu syms, %lu src locs, "
+ "%lu src locs, %lu fpo recs\n",
+ di->symtab_used, di->loctab_used,
+ di->inltab_used, di->fpo_size);
}
}
@@ -1313,6 +1316,167 @@
return NULL;
}
+/*------------------------------------------------------------*/
+/*--- Types and functions for inlined IP cursor ---*/
+/*------------------------------------------------------------*/
+struct _InlIPCursor {
+ Addr eip; // Cursor used to describe calls at eip.
+ DebugInfo* di; // DebugInfo describing inlined calls at eip
+
+ Word inltab_lopos; // The inlined fn calls covering eip are in
+ Word inltab_hipos; // di->inltab[inltab_lopos..inltab_hipos].
+ // Note that not all inlined fn calls in this range
+ // are necessarily covering eip.
+
+ Int curlevel; // Current level to describe.
+ // 0 means to describe eip itself.
+ Word cur_inltab; // inltab pos for call inlined at current level.
+ Word next_inltab; // inltab pos for call inlined at next (towards main)
+ // level.
+};
+
+static Bool is_top(InlIPCursor *iipc)
+{
+ return !iipc || iipc->cur_inltab == -1;
+}
+
+static Bool is_bottom(InlIPCursor *iipc)
+{
+ return !iipc || iipc->next_inltab == -1;
+}
+
+Bool VG_(next_IIPC)(InlIPCursor *iipc)
+{
+ Word i;
+ DiInlLoc *hinl = NULL;
+ Word hinl_pos = -1;
+ DebugInfo *di;
+
+ if (iipc == NULL)
+ return False;
+
+ if (iipc->curlevel <= 0) {
+ iipc->curlevel--;
+ return False;
+ }
+
+ di = iipc->di;
+ for (i = iipc->inltab_lopos; i <= iipc->inltab_hipos; i++) {
+ if (di->inltab[i].addr_lo <= iipc->eip
+ && iipc->eip < di->inltab[i].addr_hi
+ && di->inltab[i].level < iipc->curlevel
+ && (!hinl || hinl->level < di->inltab[i].level)) {
+ hinl = &di->inltab[i];
+ hinl_pos = i;
+ }
+ }
+
+ iipc->cur_inltab = iipc->next_inltab;
+ iipc->next_inltab = hinl_pos;
+ if (iipc->next_inltab < 0)
+ iipc->curlevel = 0; // no inlined call anymore, describe eip itself
+ else
+ iipc->curlevel = di->inltab[iipc->next_inltab].level;
+
+ return True;
+}
+
+/* Forward */
+static void search_all_loctabs ( Addr ptr, /*OUT*/DebugInfo** pdi,
+ /*OUT*/Word* locno );
+
+/* Returns the position after which eip would be inserted in inltab.
+ (-1 if eip should be inserted before position 0).
+ This is the highest position with an addr_lo <= eip.
+ As inltab is sorted on addr_lo, dichotomic search can be done
+ (note that inltab might have duplicates addr_lo). */
+static Word inltab_insert_pos (DebugInfo *di, Addr eip)
+{
+ Word mid,
+ lo = 0,
+ hi = di->inltab_used-1;
+ while (lo <= hi) {
+ mid = (lo + hi) / 2;
+ if (eip < di->inltab[mid].addr_lo) { hi = mid-1; continue; }
+ if (eip > di->inltab[mid].addr_lo) { lo = mid+1; continue; }
+ lo = mid; break;
+ }
+
+ while (lo <= di->inltab_used-1 && di->inltab[lo].addr_lo <= eip)
+ lo++;
+#if 0
+ for (mid = 0; mid <= di->inltab_used-1; mid++)
+ if (eip < di->inltab[mid].addr_lo)
+ break;
+ vg_assert (lo - 1 == mid - 1);
+#endif
+ return lo - 1;
+}
+
+InlIPCursor* VG_(new_IIPC)(Addr eip)
+{
+ DebugInfo* di;
+ Word locno;
+ Word i;
+ InlIPCursor *ret;
+ Bool avail;
+
+ if (!VG_(clo_read_inline_info))
+ return NULL; // No way we can find inlined calls.
+
+ /* Search the DebugInfo for eip */
+ search_all_loctabs ( eip, &di, &locno );
+ if (di == NULL || di->inltab_used == 0)
+ return NULL; // No di (with inltab) containing eip.
+
+ /* Search the entry in di->inltab with the highest addr_lo that
+ contains eip. */
+ /* We start from the highest pos in inltab after which eip would
+ be inserted. */
+ for (i = inltab_insert_pos (di, eip); i >= 0; i--) {
+ if (di->inltab[i].addr_lo <= eip && eip < di->inltab[i].addr_hi) {
+ break;
+ }
+ /* Stop the backward scan when reaching an addr_lo which
+ cannot anymore contain eip : we know that all ranges before
+ i also cannot contain eip. */
+ if (di->inltab[i].addr_lo < eip - di->maxinl_codesz)
+ return NULL;
+ }
+
+ if (i < 0)
+ return NULL; // No entry containing eip.
+
+ /* We have found the highest entry containing eip.
+ Build a cursor. */
+ ret = ML_(dinfo_zalloc) ("dinfo.new_IIPC", sizeof(*ret));
+ ret->eip = eip;
+ ret->di = di;
+ ret->inltab_hipos = i;
+ for (i = ret->inltab_hipos - 1; i >= 0; i--) {
+
+ if (di->inltab[i].addr_lo < eip - di->maxinl_codesz)
+ break; /* Similar stop backward scan logic as above. */
+ }
+ ret->inltab_lopos = i + 1;
+ ret->curlevel = MAX_LEVEL;
+ ret->cur_inltab = -1;
+ ret->next_inltab = -1;
+
+ /* MAX_LEVEL is higher than any stored level. We can use
+ VG_(next_IIPC) to get to the 'real' first highest call level. */
+ avail = VG_(next_IIPC) (ret);
+ vg_assert (avail);
+
+ return ret;
+}
+
+void VG_(delete_IIPC)(InlIPCursor *iipc)
+{
+ if (iipc)
+ ML_(dinfo_free)( iipc );
+}
+
/*------------------------------------------------------------*/
/*--- Use of symbol table & location info to create ---*/
@@ -1544,15 +1708,27 @@
/* This is only available to core... don't demangle C++ names, but do
do Z-demangling and below-main-renaming, match anywhere in function, and
don't show offsets. */
-Bool VG_(get_fnname_no_cxx_demangle) ( Addr a, HChar* buf, Int nbuf )
+Bool VG_(get_fnname_no_cxx_demangle) ( Addr a, HChar* buf, Int nbuf,
+ InlIPCursor* iipc )
{
- return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/True,
- /*below-main-renaming*/True,
- a, buf, nbuf,
- /*match_anywhere_in_fun*/True,
- /*show offset?*/False,
- /*text syms only*/True,
- /*offsetP*/NULL );
+ if (is_bottom(iipc)) {
+ // At the bottom (towards main), we describe the fn at eip.
+ return get_sym_name ( /*C++-demangle*/False, /*Z-demangle*/True,
+ /*below-main-renaming*/True,
+ a, buf, nbuf,
+ /*match_anywhere_in_fun*/True,
+ /*show offset?*/False,
+ /*text syms only*/True,
+ /*offsetP*/NULL );
+ } else {
+ const DiInlLoc *next_inl = iipc && iipc->next_inltab >= 0
+ ? & iipc->di->inltab[iipc->next_inltab]
+ : NULL;
+ vg_assert (next_inl);
+ // The function we are in is called by next_inl.
+ VG_(snprintf)(buf, nbuf, "%s", next_inl->inlinedfn);
+ return True;
+ }
}
/* mips-linux only: find the offset of current address. This is needed for
@@ -1873,7 +2049,7 @@
return n;
}
-HChar* VG_(describe_IP)(Addr eip, HChar* buf, Int n_buf)
+HChar* VG_(describe_IP)(Addr eip, HChar* buf, Int n_buf, InlIPCursor *iipc)
{
# define APPEND(_str) \
n = putStr(n, n_buf, buf, _str)
@@ -1885,6 +2061,8 @@
HChar ibuf[50];
Int n = 0;
+ vg_assert (!iipc || iipc->eip == eip);
+
static HChar buf_fn[BUF_LEN];
static HChar buf_obj[BUF_LEN];
static HChar buf_srcloc[BUF_LEN];
@@ -1892,16 +2070,57 @@
buf_fn[0] = buf_obj[0] = buf_srcloc[0] = buf_dirname[0] = 0;
Bool know_dirinfo = False;
- Bool know_fnname = VG_(clo_sym_offsets)
- ? VG_(get_fnname_w_offset) (eip, buf_fn, BUF_LEN)
- : VG_(get_fnname) (eip, buf_fn, BUF_LEN);
- Bool know_objname = VG_(get_objname)(eip, buf_obj, BUF_LEN);
- Bool know_srcloc = VG_(get_filename_linenum)(
- eip,
- buf_srcloc, BUF_LEN,
- buf_dirname, BUF_LEN, &know_dirinfo,
- &lineno
- );
+ Bool know_fnname;
+ Bool know_objname;
+ Bool know_srcloc;
+
+ if (is_bottom(iipc)) {
+ // At the bottom (towards main), we describe the fn at eip.
+ know_fnname = VG_(clo_sym_offsets)
+ ? VG_(get_fnname_w_offset) (eip, buf_fn, BUF_LEN)
+ : VG_(get_fnname) (eip, buf_fn, BUF_LEN);
+ } else {
+ const DiInlLoc *next_inl = iipc && iipc->next_inltab >= 0
+ ? & iipc->di->inltab[iipc->next_inltab]
+ : NULL;
+ vg_assert (next_inl);
+ // The function we are in is called by next_inl.
+ VG_(snprintf)(buf_fn, BUF_LEN, "%s", next_inl->inlinedfn);
+ know_fnname = True;
+
+ // INLINED????
+ // ??? Can we compute an offset for an inlined fn call ?
+ // ??? Offset from what ? The beginning of the inl info ?
+ // ??? But that is not necessarily the beginning of the fn
+ // ??? as e.g. an inlined fn call can be in several ranges.
+ // ??? Currently never showing an offset.
+ }
+
+ know_objname = VG_(get_objname)(eip, buf_obj, BUF_LEN);
+
+ if (is_top(iipc)) {
+ // The source for the highest level is in the loctab entry.
+ know_srcloc = VG_(get_filename_linenum)(
+ eip,
+ buf_srcloc, BUF_LEN,
+ buf_dirname, BUF_LEN, &know_dirinfo,
+ &lineno
+ );
+ } else {
+ const DiInlLoc *cur_inl = iipc && iipc->cur_inltab >= 0
+ ? & iipc->di->inltab[iipc->cur_inltab]
+ : NULL;
+ vg_assert (cur_inl);
+ // The filename and lineno for the inlined fn caller is in cur_inl.
+ VG_(snprintf) (buf_srcloc, BUF_LEN, cur_inl->filename);
+ lineno = cur_inl->lineno;
+
+ know_dirinfo = False; //INLINED TBD
+
+ know_srcloc = True;
+ }
+
+
buf_fn [ sizeof(buf_fn)-1 ] = 0;
buf_obj [ sizeof(buf_obj)-1 ] = 0;
buf_srcloc [ sizeof(buf_srcloc)-1 ] = 0;
Modified: trunk/coregrind/m_debuginfo/priv_storage.h
==============================================================================
--- trunk/coregrind/m_debuginfo/priv_storage.h (original)
+++ trunk/coregrind/m_debuginfo/priv_storage.h Sun Jun 15 15:42:20 2014
@@ -119,6 +119,29 @@
}
DiLoc;
+#define LEVEL_BITS (32 - LINENO_BITS)
+#define MAX_LEVEL ((1 << LEVEL_BITS) - 1)
+
+/* A structure to hold addr-to-inlined fn info. There
+ can be a lot of these, hence the dense packing. */
+typedef
+ struct {
+ /* Word 1 */
+ Addr addr_lo; /* lowest address for inlined fn */
+ /* Word 2 */
+ Addr addr_hi; /* highest address following the inlined fn */
+ /* Word 3 */
+ const HChar* inlinedfn; /* inlined function name */
+ /* Word 4 */
+ const HChar* filename; /* caller source filename */
+ /* Word 5 */
+ const HChar* dirname; /* caller source directory name */
+ /* Word 6 */
+ UInt lineno:LINENO_BITS; /* caller line number */
+ UShort level:LEVEL_BITS; /* level of inlining */
+ }
+ DiInlLoc;
+
/* --------------------- CF INFO --------------------- */
/* DiCfSI: a structure to summarise DWARF2/3 CFA info for the code
@@ -790,6 +813,13 @@
DiLoc* loctab;
UWord loctab_used;
UWord loctab_size;
+ /* An expandable array of inlined fn info.
+ maxinl_codesz is the biggest inlined piece of code
+ in inltab (i.e. the max of 'addr_hi - addr_lo'. */
+ DiInlLoc* inltab;
+ UWord inltab_used;
+ UWord inltab_size;
+ SizeT maxinl_codesz;
/* An expandable array of CFI summary info records. Also includes
summary address bounds, showing the min and max address covered
by any of the records, as an aid to fast searching. And, if the
@@ -874,6 +904,21 @@
const HChar* dirname, /* NULL is allowable */
Addr this, Addr next, Int lineno, Int entry);
+/* Add a call inlined record to a DebugInfo.
+ A call to the below means that inlinedfn code has been
+ inlined, resulting in code from [addr_lo, addr_hi[.
+ Note that addr_hi is excluded, i.e. is not part of the inlined code.
+ The call that caused this inlining is in filename/dirname/lineno
+ In case of nested inlining, a small level indicates the call
+ is closer to main that a call with a higher level. */
+extern
+void ML_(addInlInfo) ( struct _DebugInfo* di,
+ Addr addr_lo, Addr addr_hi,
+ const HChar* inlinedfn,
+ const HChar* filename,
+ const HChar* dirname, /* NULL is allowable */
+ Int lineno, UShort level);
+
/* Add a CFI summary record. The supplied DiCfSI is copied. */
extern void ML_(addDiCfSI) ( struct _DebugInfo* di, DiCfSI* cfsi );
Modified: trunk/coregrind/m_debuginfo/readdwarf3.c
==============================================================================
--- trunk/coregrind/m_debuginfo/readdwarf3.c (original)
+++ trunk/coregrind/m_debuginfo/readdwarf3.c Sun Jun 15 15:42:20 2014
@@ -161,7 +161,7 @@
/*------------------------------------------------------------*/
#define TRACE_D3(format, args...) \
- if (td3) { VG_(printf)(format, ## args); }
+ if (UNLIKELY(td3)) { VG_(printf)(format, ## args); }
#define D3_INVALID_CUOFF ((UWord)(-1UL))
#define D3_FAKEVOID_CUOFF ((UWord)(-2UL))
@@ -1564,7 +1564,7 @@
static
-void read_filename_table( /*MOD*/D3VarParser* parser,
+void read_filename_table( /*MOD*/XArray* /* of UChar* */ filenameTable,
CUConst* cc, ULong debug_line_offset,
Bool td3 )
{
@@ -1575,7 +1575,7 @@
UChar opcode_base;
HChar* str;
- vg_assert(parser && cc && cc->barf);
+ vg_assert(filenameTable && cc && cc->barf);
if (!ML_(sli_is_valid)(cc->escn_debug_line)
|| cc->escn_debug_line.szB <= debug_line_offset) {
cc->barf("read_filename_table: .debug_line is missing?");
@@ -1610,18 +1610,17 @@
(void)get_UChar(&c); /* skip terminating zero */
/* Read and record the file names table */
- vg_assert(parser->filenameTable);
- vg_assert( VG_(sizeXA)( parser->filenameTable ) == 0 );
+ vg_assert( VG_(sizeXA)( filenameTable ) == 0 );
/* Add a dummy index-zero entry. DWARF3 numbers its files
from 1, for some reason. */
str = ML_(addStr)( cc->di, "<unknown_file>", -1 );
- VG_(addToXA)( parser->filenameTable, &str );
+ VG_(addToXA)( filenameTable, &str );
while (peek_UChar(&c) != 0) {
DiCursor cur = get_AsciiZ(&c);
str = ML_(addStrFromCursor)( cc->di, cur );
TRACE_D3(" read_filename_table: %ld %s\n",
- VG_(sizeXA)(parser->filenameTable), str);
- VG_(addToXA)( parser->filenameTable, &str );
+ VG_(sizeXA)(filenameTable), str);
+ VG_(addToXA)( filenameTable, &str );
(void)get_ULEB128( &c ); /* skip directory index # */
(void)get_ULEB128( &c ); /* skip last mod time */
(void)get_ULEB128( &c ); /* file size */
@@ -1629,6 +1628,91 @@
/* We're done! The rest of it is not interesting. */
}
+/* setup_cu_svma to be called when a cu is found at level 0,
+ to establish the cu_svma. */
+static void setup_cu_svma(CUConst* cc, Bool have_lo, Addr ip_lo, Bool td3)
+{
+ Addr cu_svma;
+ /* We have potentially more than one type of parser parsing the
+ dwarf information. At least currently, each parser establishes
+ the cu_svma. So, in case cu_svma_known, we check that the same
+ result is obtained by the 2nd parsing of the cu.
+
+ Alternatively, we could reset cu_svma_known after each parsing
+ and then check that we only see a single DW_TAG_compile_unit DIE
+ at level 0, DWARF3 only allows exactly one top level DIE per
+ CU. */
+
+ if (have_lo)
+ cu_svma = ip_lo;
+ else {
+ /* Now, it may be that this DIE doesn't tell us the CU's
+ SVMA, by way of not having a DW_AT_low_pc. That's OK --
+ the CU doesn't *have* to have its SVMA specified.
+
+ But as per last para D3 spec sec 3.1.1 ("Normal and
+ Partial Compilation Unit Entries", "If the base address
+ (viz, the SVMA) is undefined, then any DWARF entry of
+ structure defined interms of the base address of that
+ compilation unit is not valid.". So that means, if whilst
+ processing the children of this top level DIE (or their
+ children, etc) we see a DW_AT_range, and cu_svma_known is
+ False, then the DIE that contains it is (per the spec)
+ invalid, and we can legitimately stop and complain. */
+ /* .. whereas The Reality is, simply assume the SVMA is zero
+ if it isn't specified. */
+ cu_svma = 0;
+ }
+
+ if (cc->cu_svma_known) {
+ vg_assert (cu_svma == cc->cu_svma);
+ } else {
+ cc->cu_svma_known = True;
+ cc->cu_svma = cu_svma;
+ if (0)
+ TRACE_D3("setup_cu_svma: acquire CU_SVMA of %p\n", (void*) cc->cu_svma);
+ }
+}
+
+__attribute__((noreturn))
+static void dump_bad_die_and_barf(
+ DW_TAG dtag,
+ UWord posn,
+ Int level,
+ Cursor* c_die, UWord saved_die_c_offset,
+ g_abbv *abbv,
+ CUConst* cc)
+{
+ FormContents cts;
+ UInt nf_i;
+ Bool debug_types_flag;
+ Bool alt_flag;
+
+ set_position_of_Cursor( c_die, saved_die_c_offset );
+ posn = uncook_die( cc, posn, &debug_types_flag, &alt_flag );
+ VG_(printf)(" <%d><%lx>: %s", level, posn, ML_(pp_DW_TAG)( dtag ) );
+ if (debug_types_flag) {
+ VG_(printf)(" (in .debug_types)");
+ }
+ else if (alt_flag) {
+ VG_(printf)(" (in alternate .debug_info)");
+ }
+ VG_(printf)("\n");
+ nf_i = 0;
+ while (True) {
+ DW_AT attr = (DW_AT) abbv->nf[nf_i].at_name;
+ DW_FORM form = (DW_FORM)abbv->nf[nf_i].at_form;
+ nf_i++;
+ if (attr == 0 && form == 0) break;
+ VG_(printf)(" %18s: ", ML_(pp_DW_AT)(attr));
+ /* Get the form contents, so as to print them */
+ get_Form_contents( &cts, cc, c_die, True, form );
+ VG_(printf)("\t\n");
+ }
+ VG_(printf)("\n");
+ cc->barf("parse_var_DIE: confused by the above DIE");
+}
+
__attribute__((noinline))
static void bad_DIE_confusion(int linenr)
{
@@ -1655,8 +1739,6 @@
UInt nf_i;
UWord saved_die_c_offset = get_position_of_Cursor( c_die );
- Bool debug_types_flag;
- Bool alt_flag;
varstack_preen( parser, td3, level-1 );
@@ -1692,49 +1774,17 @@
have_range = True;
}
if (attr == DW_AT_stmt_list && cts.szB > 0) {
- read_filename_table( parser, cc, cts.u.val, td3 );
+ read_filename_table( parser->filenameTable, cc, cts.u.val, td3 );
}
}
if (have_lo && have_hi1 && hiIsRelative)
ip_hi1 += ip_lo;
+
/* Now, does this give us an opportunity to find this
CU's svma? */
-#if 0
- if (level == 0 && have_lo) {
- vg_assert(!cc->cu_svma_known); /* if this fails, it must be
- because we've already seen a DW_TAG_compile_unit DIE at level
- 0. But that can't happen, because DWARF3 only allows exactly
- one top level DIE per CU. */
- cc->cu_svma_known = True;
- cc->cu_svma = ip_lo;
- if (1)
- TRACE_D3("BBBBAAAA acquire CU_SVMA of %p\n", cc->cu_svma);
- /* Now, it may be that this DIE doesn't tell us the CU's
- SVMA, by way of not having a DW_AT_low_pc. That's OK --
- the CU doesn't *have* to have its SVMA specified.
-
- But as per last para D3 spec sec 3.1.1 ("Normal and
- Partial Compilation Unit Entries", "If the base address
- (viz, the SVMA) is undefined, then any DWARF entry of
- structure defined interms of the base address of that
- compilation unit is not valid.". So that means, if whilst
- processing the children of this top level DIE (or their
- children, etc) we see a DW_AT_range, and cu_svma_known is
- False, then the DIE that contains it is (per the spec)
- invalid, and we can legitimately stop and complain. */
- }
-#else
- /* .. whereas The Reality is, simply assume the SVMA is zero
- if it isn't specified. */
- if (level == 0) {
- vg_assert(!cc->cu_svma_known);
- cc->cu_svma_known = True;
- if (have_lo)
- cc->cu_svma = ip_lo;
- else
- cc->cu_svma = 0;
- }
-#endif
+ if (level == 0)
+ setup_cu_svma(cc, have_lo, ip_lo, td3);
+
/* Do we have something that looks sane? */
if (have_lo && have_hi1 && (!have_range)) {
if (ip_lo < ip_hi1)
@@ -2136,29 +2186,233 @@
return;
bad_DIE:
- set_position_of_Cursor( c_die, saved_die_c_offset );
- posn = uncook_die( cc, posn, &debug_types_flag, &alt_flag );
- VG_(printf)(" <%d><%lx>: %s", level, posn, ML_(pp_DW_TAG)( dtag ) );
- if (debug_types_flag) {
- VG_(printf)(" (in .debug_types)");
- }
- else if (alt_flag) {
- VG_(printf)(" (in alternate .debug_info)");
+ dump_bad_die_and_barf(dtag, posn, level,
+ c_die, saved_die_c_offset,
+ abbv,
+ cc);
+ /*NOTREACHED*/
+}
+
+typedef
+ struct {
+ /* The file name table. Is a mapping from integer index to the
+ (permanent) copy of the string in DebugInfo's .strchunks. */
+ XArray* /* of UChar* */ filenameTable;
}
- VG_(printf)("\n");
+ D3InlParser;
+
+/* Return the function name corresponding to absori.
+ The return value is a (permanent) string in DebugInfo's .strchunks. */
+static HChar* get_inlFnName (Int absori, CUConst* cc, Bool td3)
+{
+ Cursor c;
+ g_abbv *abbv;
+ ULong atag, abbv_code;
+ UInt has_children;
+ UWord posn;
+ HChar *ret = NULL;
+ FormContents cts;
+ UInt nf_i;
+
+ init_Cursor (&c, cc->escn_debug_info, absori, cc->barf,
+ "Overrun get_inlFnName absori");
+
+ posn = cook_die( cc, get_position_of_Cursor( &c ) );
+ abbv_code = get_ULEB128( &c );
+ abbv = get_abbv ( cc, abbv_code);
+ atag = abbv->atag;
+ TRACE_D3("\n");
+ TRACE_D3(" <get_inlFnName><%lx>: Abbrev Number: %llu (%s)\n",
+ posn, abbv_code, ML_(pp_DW_TAG)( atag ) );
+
+ if (atag == 0)
+ cc->barf("get_inlFnName: invalid zero tag on DIE");
+
+ has_children = abbv->has_children;
+ if (has_children != DW_children_no && has_children != DW_children_yes)
+ cc->barf("get_inlFnName: invalid has_children value");
+
+ if (atag != DW_TAG_subprogram)
+ cc->barf("get_inlFnName: absori not a subprogram");
+
nf_i = 0;
while (True) {
DW_AT attr = (DW_AT) abbv->nf[nf_i].at_name;
DW_FORM form = (DW_FORM)abbv->nf[nf_i].at_form;
nf_i++;
if (attr == 0 && form == 0) break;
- VG_(printf)(" %18s: ", ML_(pp_DW_AT)(attr));
- /* Get the form contents, so as to print them */
- get_Form_contents( &cts, cc, c_die, True, form );
- VG_(printf)("\t\n");
+ get_Form_contents( &cts, cc, &c, False/*td3*/, form );
+ if (attr == DW_AT_name) {
+ HChar *fnname;
+ if (cts.szB >= 0)
+ cc->barf("get_inlFnName: expecting indirect string");
+ fnname = ML_(cur_read_strdup)( cts.u.cur,
+ "get_inlFnName.1" );
+ ret = ML_(addStr)(cc->di, fnname, -1);
+ ML_(dinfo_free) (fnname);
+ break;
+ }
}
- VG_(printf)("\n");
- cc->barf("parse_var_DIE: confused by the above DIE");
+
+ if (ret)
+ return ret;
+ else
+ return ML_(addStr)(cc->di, "AbsOriFnNameNotFound", -1);
+}
+
+/* Returns True if the (possibly) childrens of the current DIE are interesting
+ to parse. Returns False otherwise.
+ If the current DIE has a sibling, the non interesting children can
+ maybe be skipped (if the DIE has a DW_AT_sibling). */
+__attribute__((noinline))
+static Bool parse_inl_DIE (
+ /*MOD*/D3InlParser* parser,
+ DW_TAG dtag,
+ UWord posn,
+ Int level,
+ Cursor* c_die,
+ g_abbv *abbv,
+ CUConst* cc,
+ Bool td3
+)
+{
+ FormContents cts;
+ UInt nf_i;
+
+ UWord saved_die_c_offset = get_position_of_Cursor( c_die );
+
+ /* Get info about DW_TAG_compile_unit and DW_TAG_partial_unit 'which
+ in theory could also contain inlined fn calls). */
+ if (dtag == DW_TAG_compile_unit || dtag == DW_TAG_partial_unit) {
+ Bool have_lo = False;
+ Addr ip_lo = 0;
+
+ nf_i = 0;
+ while (True) {
+ DW_AT attr = (DW_AT) abbv->nf[nf_i].at_name;
+ DW_FORM form = (DW_FORM)abbv->nf[nf_i].at_form;
+ nf_i++;
+ if (attr == 0 && form == 0) break;
+ get_Form_contents( &cts, cc, c_die, False/*td3*/, form );
+ if (attr == DW_AT_low_pc && cts.szB > 0) {
+ ip_lo = cts.u.val;
+ have_lo = True;
+ }
+ if (attr == DW_AT_stmt_list && cts.szB > 0) {
+ read_filename_table( parser->filenameTable, cc, cts.u.val, td3 );
+ }
+ }
+ if (level == 0)
+ setup_cu_svma (cc, have_lo, ip_lo, td3);
+ }
+
+ if (dtag == DW_TAG_inlined_subroutine) {
+ Bool have_lo = False;
+ Bool have_hi1 = False;
+ Bool have_range = False;
+ Bool hiIsRelative = False;
+ Addr ip_lo = 0;
+ Addr ip_hi1 = 0;
+ Addr rangeoff = 0;
+ HChar* caller_filename = NULL;
+ Int caller_lineno = 0;
+ Int inlinedfn_abstract_origin = 0;
+
+ nf_i = 0;
+ while (True) {
+ DW_AT attr = (DW_AT) abbv->nf[nf_i].at_name;
+ DW_FORM form = (DW_FORM)abbv->nf[nf_i].at_form;
+ nf_i++;
+ if (attr == 0 && form == 0) break;
+ get_Form_contents( &cts, cc, c_die, False/*td3*/, form );
+ if (attr == DW_AT_call_file && cts.szB > 0) {
+ Int ftabIx = (Int)cts.u.val;
+ if (ftabIx >= 1
+ && ftabIx < VG_(sizeXA)( parser->filenameTable )) {
+ caller_filename = *(HChar**)
+ VG_(indexXA)( parser->filenameTable, ftabIx );
+ vg_assert(caller_filename);
+ }
+ if (0) VG_(printf)("XXX caller_filename = %s\n", caller_filename);
+ }
+ if (attr == DW_AT_call_line && cts.szB > 0) {
+ caller_lineno = cts.u.val;
+ }
+
+ if (attr == DW_AT_abstract_origin && cts.szB > 0) {
+ inlinedfn_abstract_origin = cts.u.val;
+ }
+
+ if (attr == DW_AT_low_pc && cts.szB > 0) {
+ ip_lo = cts.u.val;
+ have_lo = True;
+ }
+ if (attr == DW_AT_high_pc && cts.szB > 0) {
+ ip_hi1 = cts.u.val;
+ have_hi1 = True;
+ if (form != DW_FORM_addr)
+ hiIsRelative = True;
+ }
+ if (attr == DW_AT_ranges && cts.szB > 0) {
+ rangeoff = cts.u.val;
+ have_range = True;
+ }
+ }
+ if (have_lo && have_hi1 && hiIsRelative)
+ ip_hi1 += ip_lo;
+ /* Do we have something that looks sane? */
+ if (dtag == DW_TAG_inlined_subroutine
+ && (!have_lo) && (!have_hi1) && (!have_range)) {
+ /* Seems strange. How can an inlined subroutine have
+ no code ? */
+ goto_bad_DIE;
+ } else
+ if (have_lo && have_hi1 && (!have_range)) {
+ /* This inlined call is just a single address range. */
+ if (ip_lo < ip_hi1) {
+ ML_(addInlInfo) (cc->di,
+ ip_lo, ip_hi1,
+ get_inlFnName (inlinedfn_abstract_origin, cc, td3),
+ caller_filename,
+ NULL, // INLINED TBD dirname ?????
+ caller_lineno, level);
+ }
+ } else if (have_range) {
+ /* This inlined call is several address ranges. */
+ XArray *ranges;
+ Word j;
+ HChar *inlfnname = get_inlFnName (inlinedfn_abstract_origin, cc, td3);
+
+ ranges = get_range_list( cc, td3,
+ rangeoff, cc->cu_svma );
+ for (j = 0; j < VG_(sizeXA)( ranges ); j++) {
+ AddrRange* range = (AddrRange*) VG_(indexXA)( ranges, j );
+ ML_(addInlInfo) (cc->di,
+ range->aMin, range->aMax+1,
+ // aMax+1 as range has its last bound included
+ // while ML_(addInlInfo) expects last bound not
+ // included.
+ inlfnname,
+ caller_filename,
+ NULL, // INLINED TBD dirname ?????
+ caller_lineno, level);
+ }
+ VG_(deleteXA)( ranges );
+ } else
+ goto_bad_DIE;
+ }
+
+ // Only recursively parse the (possible) children for the DIE which
+ // might maybe contain a DW_TAG_inlined_subroutine:
+ return dtag == DW_TAG_lexical_block || dtag == DW_TAG_subprogram
+ || dtag == DW_TAG_inlined_subroutine
+ || dtag == DW_TAG_compile_unit || dtag == DW_TAG_partial_unit;
+
+ bad_DIE:
+ dump_bad_die_and_barf(dtag, posn, level,
+ c_die, saved_die_c_offset,
+ abbv,
+ cc);
/*NOTREACHED*/
}
@@ -2320,13 +2574,11 @@
Bool td3 )
{
FormContents cts;
+ UInt nf_i;
TyEnt typeE;
TyEnt atomE;
TyEnt fieldE;
TyEnt boundE;
- Bool debug_types_flag;
- Bool alt_flag;
- UInt nf_i;
UWord saved_die_c_offset = get_position_of_Cursor( c_die );
@@ -3095,28 +3347,10 @@
/*NOTREACHED*/
bad_DIE:
- set_position_of_Cursor( c_die, saved_die_c_offset );
- posn = uncook_die( cc, posn, &debug_types_flag, &alt_flag );
- VG_(printf)(" <%d><%lx>: %s", level, posn, ML_(pp_DW_TAG)( dtag ) );
- if (debug_types_flag) {
- VG_(printf)(" (in .debug_types)");
- } else if (alt_flag) {
- VG_(printf)(" (in alternate .debug_info)");
- }
- VG_(printf)("\n");
- nf_i = 0;
- while (True) {
- DW_AT attr = (DW_AT) abbv->nf[nf_i].at_name;
- DW_FORM form = (DW_FORM)abbv->nf[nf_i].at_form;
- nf_i++;
- if (attr == 0 && form == 0) break;
- VG_(printf)(" %18s: ", ML_(pp_DW_AT)(attr));
- /* Get the form contents, so as to print them */
- get_Form_contents( &cts, cc, c_die, True, form );
- VG_(printf)("\t\n");
- }
- VG_(printf)("\n");
- cc->barf("parse_type_DIE: confused by the above DIE");
+ dump_bad_die_and_barf(dtag, posn, level,
+ c_die, saved_die_c_offset,
+ abbv,
+ cc);
/*NOTREACHED*/
}
@@ -3463,6 +3697,7 @@
/*MOD*/XArray* /* of GExpr* */ gexprs,
/*MOD*/D3TypeParser* typarser,
/*MOD*/D3VarParser* varparser,
+ /*MOD*/D3InlParser* inlparser,
Cursor* c, Bool td3, CUConst* cc, Int level
)
{
@@ -3473,6 +3708,12 @@
UInt has_children;
UWord start_die_c_offset;
UWord after_die_c_offset;
+ // If the DIE we will parse has a sibling and the parser(s) are
+ // all indicating that parse_children is not necessary, then
+ // we will skip the children by jumping to the sibling of this DIE
+ // (if it has a sibling).
+ UWord sibling = 0;
+ Bool parse_children = False;
/* --- Deal with this DIE --- */
posn = cook_die( cc, get_position_of_Cursor( c ) );
@@ -3509,54 +3750,87 @@
/* Get the form contents, but ignore them; the only purpose is
to print them, if td3 is True */
get_Form_contents( &cts, cc, c, td3, (DW_FORM)at_form );
+ /* Except that we remember if this DIE has a sibling. */
+ if (UNLIKELY(at_name == DW_AT_sibling && cts.szB > 0)) {
+ sibling = cts.u.val;
+ }
TRACE_D3("\t");
TRACE_D3("\n");
}
after_die_c_offset = get_position_of_Cursor( c );
- set_position_of_Cursor( c, start_die_c_offset );
+ if (VG_(clo_read_var_info)) {
+ set_position_of_Cursor( c, start_die_c_offset );
- parse_type_DIE( tyents,
- typarser,
- (DW_TAG)atag,
- posn,
- level,
- c, /* DIE cursor */
- abbv, /* abbrev */
- cc,
- td3 );
-
- set_position_of_Cursor( c, start_die_c_offset );
-
- parse_var_DIE( rangestree,
- tempvars,
- gexprs,
- varparser,
- (DW_TAG)atag,
- posn,
- level,
- c, /* DIE cursor */
- abbv, /* abbrev */
- cc,
- td3 );
+ parse_type_DIE( tyents,
+ typarser,
+ (DW_TAG)atag,
+ posn,
+ level,
+ c, /* DIE cursor */
+ abbv, /* abbrev */
+ cc,
+ td3 );
+
+ set_position_of_Cursor( c, start_die_c_offset );
+
+ parse_var_DIE( rangestree,
+ tempvars,
+ gexprs,
+ varparser,
+ (DW_TAG)atag,
+ posn,
+ level,
+ c, /* DIE cursor */
+ abbv, /* abbrev */
+ cc,
+ td3 );
+
+ parse_children = True;
+ // type and var parsers do not have logic to skip childrens.
+ }
+
+ if (VG_(clo_read_inline_info)) {
+ set_position_of_Cursor( c, start_die_c_offset );
+
+ parse_children =
+ parse_inl_DIE( inlparser,
+ (DW_TAG)atag,
+ posn,
+ level,
+ c, /* DIE cursor */
+ abbv, /* abbrev */
+ cc,
+ td3 )
+ || parse_children;
+ }
set_position_of_Cursor( c, after_die_c_offset );
- /* --- Now recurse into its children, if any --- */
+ /* --- Now recurse into its children, if any
+ and the parsing of the children is requested by a parser --- */
if (has_children == DW_children_yes) {
- if (0) TRACE_D3("BEGIN children of level %d\n", level);
- while (True) {
- atag = peek_ULEB128( c );
- if (atag == 0) break;
- read_DIE( rangestree, tyents, tempvars, gexprs,
- typarser, varparser,
- c, td3, cc, level+1 );
- }
- /* Now we need to eat the terminating zero */
- atag = get_ULEB128( c );
- vg_assert(atag == 0);
- if (0) TRACE_D3("END children of level %d\n", level);
+ if (parse_children || sibling == 0) {
+ if (0) TRACE_D3("BEGIN children of level %d\n", level);
+ while (True) {
+ atag = peek_ULEB128( c );
+ if (atag == 0) break;
+ read_DIE( rangestree, tyents, tempvars, gexprs,
+ typarser, varparser, inlparser,
+ c, td3, cc, level+1 );
+ }
+ /* Now we need to eat the terminating zero */
+ atag = get_ULEB128( c );
+ vg_assert(atag == 0);
+ if (0) TRACE_D3("END children of level %d\n", level);
+ } else {
+ // We can skip the childrens, by jumping to the sibling
+ TRACE_D3("SKIPPING DIE's children,"
+ "jumping to sibling <%d><%lx>\n",
+ level, sibling);
+ set_position_of_Cursor( c, sibling );
+ }
}
}
@@ -3574,11 +3848,11 @@
DiSlice escn_debug_str_alt
)
{
- XArray* /* of TyEnt */ tyents;
- XArray* /* of TyEnt */ tyents_to_keep;
- XArray* /* of GExpr* */ gexprs;
- XArray* /* of TempVar* */ tempvars;
- WordFM* /* of (XArray* of AddrRange, void) */ rangestree;
+ XArray* /* of TyEnt */ tyents = NULL;
+ XArray* /* of TyEnt */ tyents_to_keep = NULL;
+ XArray* /* of GExpr* */ gexprs = NULL;
+ XArray* /* of TempVar* */ tempvars = NULL;
+ WordFM* /* of (XArray* of AddrRange, void) */ rangestree = NULL;
TyEntIndexCache* tyents_cache = NULL;
TyEntIndexCache* tyents_to_keep_cache = NULL;
TempVar *varp, *varp2;
@@ -3588,13 +3862,14 @@
Cursor ranges; /* for showing .debug_ranges */
D3TypeParser typarser;
D3VarParser varparser;
+ D3InlParser inlparser;
Addr dr_base;
UWord dr_offset;
Word i, j, n;
Bool td3 = di->trace_symtab;
XArray* /* of TempVar* */ dioff_lookup_tab;
Int pass;
- VgHashTable signature_types;
+ VgHashTable signature_types = NULL;
#if 0
/* This doesn't work properly because it assumes all entries are
packed end to end, with no holes. But that doesn't always
@@ -3731,74 +4006,79 @@
}
TRACE_SYMTAB("\n");
- /* We'll park the harvested type information in here. Also create
- a fake "void" entry with offset D3_FAKEVOID_CUOFF, so we always
- have at least one type entry to refer to. D3_FAKEVOID_CUOFF is
- huge and presumably will not occur in any valid DWARF3 file --
- it would need to have a .debug_info section 4GB long for that to
- happen. These type entries end up in the DebugInfo. */
- tyents = VG_(newXA)( ML_(dinfo_zalloc),
- "di.readdwarf3.ndrw.1 (TyEnt temp array)",
- ML_(dinfo_free), sizeof(TyEnt) );
- { TyEnt tyent;
- VG_(memset)(&tyent, 0, sizeof(tyent));
- tyent.tag = Te_TyVoid;
- tyent.cuOff = D3_FAKEVOID_CUOFF;
- tyent.Te.TyVoid.isFake = True;
- VG_(addToXA)( tyents, &tyent );
- }
- { TyEnt tyent;
- VG_(memset)(&tyent, 0, sizeof(tyent));
- tyent.tag = Te_UNKNOWN;
- tyent.cuOff = D3_INVALID_CUOFF;
- VG_(addToXA)( tyents, &tyent );
- }
-
- /* This is a tree used to unique-ify the range lists that are
- manufactured by parse_var_DIE. References to the keys in the
- tree wind up in .rngMany fields in TempVars. We'll need to
- delete this tree, and the XArrays attached to it, at the end of
- this function. */
- rangestree = VG_(newFM)( ML_(dinfo_zalloc),
- "di.readdwarf3.ndrw.2 (rangestree)",
- ML_(dinfo_free),
- (Word(*)(UWord,UWord))cmp__XArrays_of_AddrRange );
-
- /* List of variables we're accumulating. These don't end up in the
- DebugInfo; instead their contents are handed to ML_(addVar) and
- the list elements are then deleted. */
- tempvars = VG_(newXA)( ML_(dinfo_zalloc),
- "di.readdwarf3.ndrw.3 (TempVar*s array)",
- ML_(dinfo_free),
- sizeof(TempVar*) );
-
- /* List of GExprs we're accumulating. These wind up in the
- DebugInfo. */
- gexprs = VG_(newXA)( ML_(dinfo_zalloc), "di.readdwarf3.ndrw.4",
- ML_(dinfo_free), sizeof(GExpr*) );
-
- /* We need a D3TypeParser to keep track of partially constructed
- types. It'll be discarded as soon as we've completed the CU,
- since the resulting information is tipped in to 'tyents' as it
- is generated. */
- VG_(memset)( &typarser, 0, sizeof(typarser) );
- typarser.sp = -1;
- typarser.language = '?';
- for (i = 0; i < N_D3_TYPE_STACK; i++) {
- typarser.qparentE[i].tag = Te_EMPTY;
- typarser.qparentE[i].cuOff = D3_INVALID_CUOFF;
+ if (VG_(clo_read_var_info)) {
+ /* We'll park the harvested type information in here. Also create
+ a fake "void" entry with offset D3_FAKEVOID_CUOFF, so we always
+ have at least one type entry to refer to. D3_FAKEVOID_CUOFF is
+ huge and presumably will not occur in any valid DWARF3 file --
+ it would need to have a .debug_info section 4GB long for that to
+ happen. These type entries end up in the DebugInfo. */
+ tyents = VG_(newXA)( ML_(dinfo_zalloc),
+ "di.readdwarf3.ndrw.1 (TyEnt temp array)",
+ ML_(dinfo_free), sizeof(TyEnt) );
+ { TyEnt tyent;
+ VG_(memset)(&tyent, 0, sizeof(tyent));
+ tyent.tag = Te_TyVoid;
+ tyent.cuOff = D3_FAKEVOID_CUOFF;
+ tyent.Te.TyVoid.isFake = True;
+ VG_(addToXA)( tyents, &tyent );
+ }
+ { TyEnt tyent;
+ VG_(memset)(&tyent, 0, sizeof(tyent));
+ tyent.tag = Te_UNKNOWN;
+ tyent.cuOff = D3_INVALID_CUOFF;
+ VG_(addToXA)( tyents, &tyent );
+ }
+
+ /* This is a tree used to unique-ify the range lists that are
+ manufactured by parse_var_DIE. References to the keys in the
+ tree wind up in .rngMany fields in TempVars. We'll need to
+ delete this tree, and the XArrays attached to it, at the end of
+ this function. */
+ rangestree = VG_(newFM)( ML_(dinfo_zalloc),
+ "di.readdwarf3.ndrw.2 (rangestree)",
+ ML_(dinfo_free),
+ (Word(*)(UWord,UWord))cmp__XArrays_of_AddrRange );
+
+ /* List of variables we're accumulating. These don't end up in the
+ DebugInfo; instead their contents are handed to ML_(addVar) and
+ the list elements are then deleted. */
+ tempvars = VG_(newXA)( ML_(dinfo_zalloc),
+ "di.readdwarf3.ndrw.3 (TempVar*s array)",
+ ML_(dinfo_free),
+ sizeof(TempVar*) );
+
+ /* List of GExprs we're accumulating. These wind up in the
+ DebugInfo. */
+ gexprs = VG_(newXA)( ML_(dinfo_zalloc), "di.readdwarf3.ndrw.4",
+ ML_(dinfo_free), sizeof(GExpr*) );
+
+ /* We need a D3TypeParser to keep track of partially constructed
+ types. It'll be discarded as soon as we've completed the CU,
+ since the resulting information is tipped in to 'tyents' as it
+ is generated. */
+ VG_(memset)( &typarser, 0, sizeof(typarser) );
+ typarser.sp = -1;
+ typarser.language = '?';
+ for (i = 0; i < N_D3_TYPE_STACK; i++) {
+ typarser.qparentE[i].tag = Te_EMPTY;
+ typarser.qparentE[i].cuOff = D3_INVALID_CUOFF;
+ }
+
+ VG_(memset)( &varparser, 0, sizeof(varparser) );
+ varparser.sp = -1;
+
+ signature_types = VG_(HT_construct) ("signature_types");
}
- VG_(memset)( &varparser, 0, sizeof(varparser) );
- varparser.sp = -1;
+ if (VG_(clo_read_inline_info))
+ VG_(memset)( &inlparser, 0, sizeof(inlparser) );
- signature_types = VG_(HT_construct) ("signature_types");
-
/* Do an initial pass to scan the .debug_types section, if any, and
fill in the signatured types hash table. This lets us handle
mapping from a type signature to a (cooked) DIE offset directly
in get_Form_contents. */
- if (ML_(sli_is_valid)(escn_debug_types)) {
+ if (VG_(clo_read_var_info) && ML_(sli_is_valid)(escn_debug_types)) {
init_Cursor( &info, escn_debug_types, 0, barf,
"Overrun whilst reading .debug_types section" );
TRACE_D3("\n------ Collecting signatures from "
@@ -3912,16 +4192,18 @@
break;
}
- /* Check the varparser's stack is in a sane state. */
- vg_assert(varparser.sp == -1);
- for (i = 0; i < N_D3_VAR_STACK; i++) {
- vg_assert(varparser.ranges[i] == NULL);
- vg_assert(varparser.level[i] == 0);
- }
- for (i = 0; i < N_D3_TYPE_STACK; i++) {
- vg_assert(typarser.qparentE[i].cuOff == D3_INVALID_CUOFF);
- vg_assert(typarser.qparentE[i].tag == Te_EMPTY);
- vg_assert(typarser.qlevel[i] == 0);
+ if (VG_(clo_read_var_info)) {
+ /* Check the varparser's stack is in a sane state. */
+ vg_assert(varparser.sp == -1);
+ for (i = 0; i < N_D3_VAR_STACK; i++) {
+ vg_assert(varparser.ranges[i] == NULL);
+ vg_assert(varparser.level[i] == 0);
+ }
+ for (i = 0; i < N_D3_TYPE_STACK; i++) {
+ vg_assert(typarser.qparentE[i].cuOff == D3_INVALID_CUOFF);
+ vg_assert(typarser.qparentE[i].tag == Te_EMPTY);
+ vg_assert(typarser.qlevel[i] == 0);
+ }
}
cu_start_offset = get_position_of_Cursor( &info );
@@ -3959,33 +4241,45 @@
cc.cu_svma_known = False;
cc.cu_svma = 0;
- cc.signature_types = signature_types;
+ if (VG_(clo_read_var_info)) {
+ cc.signature_types = signature_types;
- /* Create a fake outermost-level range covering the entire
- address range. So we always have *something* to catch all
- variable declarations. */
- varstack_push( &cc, &varparser, td3,
- unitary_range_list(0UL, ~0UL),
- -1, False/*isFunc*/, NULL/*fbGX*/ );
-
- /* And set up the file name table. When we come across the top
- level DIE for this CU (which is what the next call to
- read_DIE should process) we will copy all the file names out
- of the .debug_line img area and use this table to look up the
- copies when we later see filename numbers in DW_TAG_variables
- etc. */
- vg_assert(!varparser.filenameTable );
- varparser.filenameTable
- = VG_(newXA)( ML_(dinfo_zalloc), "di.readdwarf3.ndrw.5",
- ML_(dinfo_free),
- sizeof(UChar*) );
- vg_assert(varparser.filenameTable);
+ /* Create a fake outermost-level range covering the entire
+ address range. So we always have *something* to catch all
+ variable declarations. */
+ varstack_push( &cc, &varparser, td3,
+ unitary_range_list(0UL, ~0UL),
+ -1, False/*isFunc*/, NULL/*fbGX*/ );
+
+ /* And set up the file name table. When we come across the top
+ level DIE for this CU (which is what the next call to
+ read_DIE should process) we will copy all the file names out
+ of the .debug_line img area and use this table to look up the
+ copies when we later see filename numbers in DW_TAG_variables
+ etc. */
+ vg_assert(!varparser.filenameTable );
+ varparser.filenameTable
+ = VG_(newXA)( ML_(dinfo_zalloc), "di.readdwarf3.ndrw.5var",
+ ML_(dinfo_free),
+ sizeof(UChar*) );
+ vg_assert(varparser.filenameTable);
+ }
+
+ if (VG_(clo_read_inline_info)) {
+ /* filename table for the inlined call parser */
+ vg_assert(!inlparser.filenameTable );
+ inlparser.filenameTable
+ = VG_(newXA)( ML_(dinfo_zalloc), "di.readdwarf3.ndrw.5inl",
+ ML_(dinfo_free),
+ sizeof(UChar*) );
+ vg_assert(inlparser.filenameTable);
+ }
/* Now read the one-and-only top-level DIE for this CU. */
- vg_assert(varparser.sp == 0);
+ vg_assert(!VG_(clo_read_var_info) || varparser.sp == 0);
read_DIE( rangestree,
tyents, tempvars, gexprs,
- &typarser, &varparser,
+ &typarser, &varparser, &inlparser,
&info, td3, &cc, 0 );
cu_offset_now = get_position_of_Cursor( &info );
@@ -4019,18 +4313,27 @@
cu_amount_used = cu_offset_now - cc.cu_start_offset;
}
- /* Preen to level -2. DIEs have level >= 0 so -2 cannot occur
- anywhere else at all. Our fake the-entire-address-space
- range is at level -1, so preening to -2 should completely
- empty the stack out. */
- TRACE_D3("\n");
- varstack_preen( &varparser, td3, -2 );
- /* Similarly, empty the type stack out. */
- typestack_preen( &typarser, td3, -2 );
-
- vg_assert(varparser.filenameTable );
- VG_(deleteXA)( varparser.filenameTable );
- varparser.filenameTable = NULL;
+ if (VG_(clo_read_var_info)) {
+ /* Preen to level -2. DIEs have level >= 0 so -2 cannot occur
+ anywhere else at all. Our fake the-entire-address-space
+ range is at level -1, so preening to -2 should completely
+ empty the stack out. */
+ TRACE_D3("\n");
+ varstack_preen( &varparser, td3, -2 );
+ /* Similarly, empty the type stack out. */
+ typestack_preen( &typarser, td3, -2 );
+ }
+
+ if (VG_(clo_read_var_info)) {
+ vg_assert(varparser.filenameTable );
+ VG_(deleteXA)( varparser.filenameTable );
+ varparser.filenameTable = NULL;
+ vg_assert(inlparser.filenameTable );
+ }
+ if (VG_(clo_read_inline_info)) {
+ VG_(deleteXA)( inlparser.filenameTable );
+ inlparser.filenameTable = NULL;
+ }
clear_CUConst(&cc);
if (cu_offset_now == section_size)
@@ -4039,328 +4342,331 @@
}
}
- /* From here on we're post-processing the stuff we got
- out of the .debug_info section. */
- if (td3) {
- TRACE_D3("\n");
- ML_(pp_TyEnts)(tyents, "Initial type entity (TyEnt) array");
- TRACE_D3("\n");
- TRACE_D3("------ Compressing type entries ------\n");
- }
- tyents_cache = ML_(dinfo_zalloc)( "di.readdwarf3.ndrw.6",
- sizeof(TyEntIndexCache) );
- ML_(TyEntIndexCache__invalidate)( tyents_cache );
- dedup_types( td3, tyents, tyents_cache );
- if (td3) {
- TRACE_D3("\n");
- ML_(pp_TyEnts)(tyents, "After type entity (TyEnt) compression");
- }
+ if (VG_(clo_read_var_info)) {
+ /* From here on we're post-processing the stuff we got
+ out of the .debug_info section. */
+ if (td3) {
+ TRACE_D3("\n");
+ ML_(pp_TyEnts)(tyents, "Initial type entity (TyEnt) array");
+ TRACE_D3("\n");
+ TRACE_D3("------ Compressing type entries ------\n");
+ }
- TRACE_D3("\n");
- TRACE_D3("------ Resolving the types of variables ------\n" );
- resolve_variable_types( barf, tyents, tyents_cache, tempvars );
+ tyents_cache = ML_(dinfo_zalloc)( "di.readdwarf3.ndrw.6",
+ sizeof(TyEntIndexCache) );
+ ML_(TyEntIndexCache__invalidate)( tyents_cache );
+ dedup_types( td3, tyents, tyents_cache );
+ if (td3) {
+ TRACE_D3("\n");
+ ML_(pp_TyEnts)(tyents, "After type entity (TyEnt) compression");
+ }
- /* Copy all the non-INDIR tyents into a new table. For large
- .so's, about 90% of the tyents will by now have been resolved to
- INDIRs, and we no longer need them, and so don't need to store
- them. */
- tyents_to_keep
- = VG_(newXA)( ML_(dinfo_zalloc),
- "di.readdwarf3.ndrw.7 (TyEnt to-keep array)",
- ML_(dinfo_free), sizeof(TyEnt) );
- n = VG_(sizeXA)( tyents );
- for (i = 0; i < n; i++) {
- TyEnt* ent = VG_(indexXA)( tyents, i );
- if (ent->tag != Te_INDIR)
- VG_(addToXA)( tyents_to_keep, ent );
- }
-
- VG_(deleteXA)( tyents );
- tyents = NULL;
- ML_(dinfo_free)( tyents_cache );
- tyents_cache = NULL;
-
- /* Sort tyents_to_keep so we can lookup in it. A complete (if
- minor) waste of time, since tyents itself is sorted, but
- necessary since VG_(lookupXA) refuses to cooperate if we
- don't. */
- VG_(setCmpFnXA)( tyents_to_keep, (XACmpFn_t) ML_(TyEnt__cmp_by_cuOff_only) );
- VG_(sortXA)( tyents_to_keep );
-
- /* Enable cacheing on tyents_to_keep */
- tyents_to_keep_cache
- = ML_(dinfo_zalloc)( "di.readdwarf3.ndrw.8",
- sizeof(TyEntIndexCache) );
- ML_(TyEntIndexCache__invalidate)( tyents_to_keep_cache );
-
- /* And record the tyents in the DebugInfo. We do this before
- starting to hand variables to ML_(addVar), since if ML_(addVar)
- wants to do debug printing (of the types of said vars) then it
- will need the tyents.*/
- vg_assert(!di->admin_tyents);
- di->admin_tyents = tyents_to_keep;
+ TRACE_D3("\n");
+ TRACE_D3("------ Resolving the types of variables ------\n" );
+ resolve_variable_types( barf, tyents, tyents_cache, tempvars );
- /* Bias all the location expressions. */
- TRACE_D3("\n");
- TRACE_D3("------ Biasing the location expressions ------\n" );
+ /* Copy all the non-INDIR tyents into a new table. For large
+ .so's, about 90% of the tyents will by now have been resolved to
+ INDIRs, and we no longer need them, and so don't need to store
+ them. */
+ tyents_to_keep
+ = VG_(newXA)( ML_(dinfo_zalloc),
+ "di.readdwarf3.ndrw.7 (TyEnt to-keep array)",
+ ML_(dinfo_free), sizeof(TyEnt) );
+ n = VG_(sizeXA)( tyents );
+ for (i = 0; i < n; i++) {
+ TyEnt* ent = VG_(indexXA)( tyents, i );
+ if (ent->tag != Te_INDIR)
+ VG_(addToXA)( tyents_to_keep, ent );
+ }
+
+ VG_(deleteXA)( tyents );
+ tyents = NULL;
+ ML_(dinfo_free)( tyents_cache );
+ tyents_cache = NULL;
+
+ /* Sort tyents_to_keep so we can lookup in it. A complete (if
+ ...
[truncated message content] |