]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
This patch implements the support needed for stacktraces
authorPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Sun, 15 Jun 2014 15:42:20 +0000 (15:42 +0000)
committerPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Sun, 15 Jun 2014 15:42:20 +0000 (15:42 +0000)
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

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@14036

34 files changed:
coregrind/m_debuginfo/debuginfo.c
coregrind/m_debuginfo/priv_storage.h
coregrind/m_debuginfo/readdwarf3.c
coregrind/m_debuginfo/readelf.c
coregrind/m_debuginfo/readmacho.c
coregrind/m_debuginfo/storage.c
coregrind/m_errormgr.c
coregrind/m_gdbserver/m_gdbserver.c
coregrind/m_gdbserver/target.c
coregrind/m_main.c
coregrind/m_options.c
coregrind/m_seqmatch.c
coregrind/m_stacktrace.c
coregrind/pub_core_debuginfo.h
coregrind/pub_core_options.h
docs/xml/manual-core.xml
include/pub_tool_debuginfo.h
include/pub_tool_seqmatch.h
massif/ms_main.c
memcheck/tests/Makefile.am
memcheck/tests/inlinfo.c [new file with mode: 0644]
memcheck/tests/inlinfo.stderr.exp [new file with mode: 0644]
memcheck/tests/inlinfo.stdout.exp [new file with mode: 0644]
memcheck/tests/inlinfo.vgtest [new file with mode: 0644]
memcheck/tests/inlinfosupp.stderr.exp [new file with mode: 0644]
memcheck/tests/inlinfosupp.stdout.exp [new file with mode: 0644]
memcheck/tests/inlinfosupp.supp [new file with mode: 0644]
memcheck/tests/inlinfosupp.vgtest [new file with mode: 0644]
memcheck/tests/inlinfosuppobj.stderr.exp [new file with mode: 0644]
memcheck/tests/inlinfosuppobj.stdout.exp [new file with mode: 0644]
memcheck/tests/inlinfosuppobj.supp [new file with mode: 0644]
memcheck/tests/inlinfosuppobj.vgtest [new file with mode: 0644]
none/tests/cmdline1.stdout.exp
none/tests/cmdline2.stdout.exp

index 6e1cacf34fb6c2cf132957268bdb1ea1ca9df2f2..571e2c42d8695eafe37ebc5686edc176089930f5 100644 (file)
@@ -212,6 +212,7 @@ static void free_DebugInfo ( DebugInfo* di )
    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 @@ void VG_(di_notify_pdb_debuginfo)( Int fd_obj, Addr avma_obj,
 
      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 @@ struct _DebugInfoMapping* ML_(find_rx_mapping) ( struct _DebugInfo* di,
    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 @@ Bool VG_(get_fnname_raw) ( Addr a, HChar* buf, Int nbuf )
 /* 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 @@ static Int putStrEsc ( Int n, Int n_buf, Int count, HChar* buf, HChar* str )
    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* VG_(describe_IP)(Addr eip, HChar* buf, Int n_buf)
    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 @@ HChar* VG_(describe_IP)(Addr eip, HChar* buf, Int n_buf)
    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;
index 0b5d6f113ab4780ddcc996bde6717dea3ef8d7a1..951097d18635f277e2a910c410ba2cbf7cc69685 100644 (file)
@@ -119,6 +119,29 @@ typedef
    }
    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 @@ struct _DebugInfo {
    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 @@ void ML_(addLineInfo) ( struct _DebugInfo* di,
                         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 );
 
index a058af58687bebef8ff9e76c12fcfdd07e171214..7ac126fa64b32f35a2a1659759391fbdf1be6a26 100644 (file)
 /*------------------------------------------------------------*/
 
 #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 GExpr* get_GX ( CUConst* cc, Bool td3, const FormContents* cts )
 
 
 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 @@ void read_filename_table( /*MOD*/D3VarParser* parser,
    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 read_filename_table( /*MOD*/D3VarParser* parser,
    (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 @@ void read_filename_table( /*MOD*/D3VarParser* parser,
    /* 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 @@ static void parse_var_DIE (
    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 @@ static void parse_var_DIE (
             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 @@ static void parse_var_DIE (
    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 @@ static void parse_type_DIE ( /*MOD*/XArray* /* of TyEnt */ tyents,
                              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 @@ static void parse_type_DIE ( /*MOD*/XArray* /* of TyEnt */ tyents,
    /*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 @@ static void read_DIE (
    /*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 @@ static void read_DIE (
    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 @@ static void read_DIE (
       /* 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 );
-
-   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 );
+   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_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 );
+      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 );
       }
-      /* 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);
    }
 
 }
@@ -3574,11 +3848,11 @@ void new_dwarf3_reader_wrk (
    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 @@ void new_dwarf3_reader_wrk (
    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 @@ void new_dwarf3_reader_wrk (
    }
    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 );
-   }
+   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;
+      }
 
-   /* 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 @@ void new_dwarf3_reader_wrk (
             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 @@ void new_dwarf3_reader_wrk (
          cc.cu_svma_known = False;
          cc.cu_svma       = 0;
 
-         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);
+         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.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 @@ void new_dwarf3_reader_wrk (
             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 );
+         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 );
+         }
 
-         vg_assert(varparser.filenameTable );
-         VG_(deleteXA)( varparser.filenameTable );
-         varparser.filenameTable = NULL;
+         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 @@ void new_dwarf3_reader_wrk (
       }
    }
 
-   /* 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) {
+   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");
+      }
+
+      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");
+      }
+
       TRACE_D3("\n");
-      ML_(pp_TyEnts)(tyents, "After type entity (TyEnt) compression");
-   }
+      TRACE_D3("------ Resolving the types of variables ------\n" );
+      resolve_variable_types( barf, tyents, tyents_cache, tempvars );
 
-   TRACE_D3("\n");
-   TRACE_D3("------ Resolving the types of variables ------\n" );
-   resolve_variable_types( barf, tyents, tyents_cache, tempvars );
-
-   /* 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 );
-   }
+      /* 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;
-
-   /* Bias all the location expressions. */
-   TRACE_D3("\n");
-   TRACE_D3("------ Biasing the location expressions ------\n" );
+      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;
+
+      /* Bias all the location expressions. */
+      TRACE_D3("\n");
+      TRACE_D3("------ Biasing the location expressions ------\n" );
 
-   n = VG_(sizeXA)( gexprs );
-   for (i = 0; i < n; i++) {
-      gexpr = *(GExpr**)VG_(indexXA)( gexprs, i );
-      bias_GX( gexpr, di );
-   }
+      n = VG_(sizeXA)( gexprs );
+      for (i = 0; i < n; i++) {
+         gexpr = *(GExpr**)VG_(indexXA)( gexprs, i );
+         bias_GX( gexpr, di );
+      }
 
-   TRACE_D3("\n");
-   TRACE_D3("------ Acquired the following variables: ------\n\n");
-
-   /* Park (pointers to) all the vars in an XArray, so we can look up
-      abstract origins quickly.  The array is sorted (hence, looked-up
-      by) the .dioff fields.  Since the .dioffs should be in strictly
-      ascending order, there is no need to sort the array after
-      construction.  The ascendingness is however asserted for. */
-   dioff_lookup_tab
-      = VG_(newXA)( ML_(dinfo_zalloc), "di.readdwarf3.ndrw.9",
-                    ML_(dinfo_free), 
-                    sizeof(TempVar*) );
-   vg_assert(dioff_lookup_tab);
-
-   n = VG_(sizeXA)( tempvars );
-   Word first_primary_var = 0;
-   for (first_primary_var = 0;
-        escn_debug_info_alt.szB/*really?*/ && first_primary_var < n;
-        first_primary_var++) {
-      varp = *(TempVar**)VG_(indexXA)( tempvars, first_primary_var );
-      if (varp->dioff < escn_debug_info.szB + escn_debug_types.szB)
-         break;
-   }
-   for (i = 0; i < n; i++) {
-      varp = *(TempVar**)VG_(indexXA)( tempvars, (i + first_primary_var) % n );
-      if (i > first_primary_var) {
-         varp2 = *(TempVar**)VG_(indexXA)( tempvars,
-                                           (i + first_primary_var - 1) % n );
-         /* why should this hold?  Only, I think, because we've
-            constructed the array by reading .debug_info sequentially,
-            and so the array .dioff fields should reflect that, and be
-            strictly ascending. */
-         vg_assert(varp2->dioff < varp->dioff);
+      TRACE_D3("\n");
+      TRACE_D3("------ Acquired the following variables: ------\n\n");
+
+      /* Park (pointers to) all the vars in an XArray, so we can look up
+         abstract origins quickly.  The array is sorted (hence, looked-up
+         by) the .dioff fields.  Since the .dioffs should be in strictly
+         ascending order, there is no need to sort the array after
+         construction.  The ascendingness is however asserted for. */
+      dioff_lookup_tab
+         = VG_(newXA)( ML_(dinfo_zalloc), "di.readdwarf3.ndrw.9",
+                       ML_(dinfo_free), 
+                       sizeof(TempVar*) );
+      vg_assert(dioff_lookup_tab);
+
+      n = VG_(sizeXA)( tempvars );
+      Word first_primary_var = 0;
+      for (first_primary_var = 0;
+           escn_debug_info_alt.szB/*really?*/ && first_primary_var < n;
+           first_primary_var++) {
+         varp = *(TempVar**)VG_(indexXA)( tempvars, first_primary_var );
+         if (varp->dioff < escn_debug_info.szB + escn_debug_types.szB)
+            break;
       }
-      VG_(addToXA)( dioff_lookup_tab, &varp );
-   }
-   VG_(setCmpFnXA)( dioff_lookup_tab, cmp_TempVar_by_dioff );
-   VG_(sortXA)( dioff_lookup_tab ); /* POINTLESS; FIXME: rm */
+      for (i = 0; i < n; i++) {
+         varp = *(TempVar**)VG_(indexXA)( tempvars, (i + first_primary_var) % n );
+         if (i > first_primary_var) {
+            varp2 = *(TempVar**)VG_(indexXA)( tempvars,
+                                              (i + first_primary_var - 1) % n );
+            /* why should this hold?  Only, I think, because we've
+               constructed the array by reading .debug_info sequentially,
+               and so the array .dioff fields should reflect that, and be
+               strictly ascending. */
+            vg_assert(varp2->dioff < varp->dioff);
+         }
+         VG_(addToXA)( dioff_lookup_tab, &varp );
+      }
+      VG_(setCmpFnXA)( dioff_lookup_tab, cmp_TempVar_by_dioff );
+      VG_(sortXA)( dioff_lookup_tab ); /* POINTLESS; FIXME: rm */
 
-   /* Now visit each var.  Collect up as much info as possible for
-      each var and hand it to ML_(addVar). */
-   n = VG_(sizeXA)( tempvars );
-   for (j = 0; j < n; j++) {
-      TyEnt* ent;
-      varp = *(TempVar**)VG_(indexXA)( tempvars, j );
+      /* Now visit each var.  Collect up as much info as possible for
+         each var and hand it to ML_(addVar). */
+      n = VG_(sizeXA)( tempvars );
+      for (j = 0; j < n; j++) {
+         TyEnt* ent;
+         varp = *(TempVar**)VG_(indexXA)( tempvars, j );
 
-      /* Possibly show .. */
-      if (td3) {
-         VG_(printf)("<%lx> addVar: level %d: %s :: ",
-                     varp->dioff,
-                     varp->level,
-                     varp->name ? varp->name : "<anon_var>" );
-         if (varp->typeR) {
-            ML_(pp_TyEnt_C_ishly)( tyents_to_keep, varp->typeR );
-         } else {
-            VG_(printf)("NULL");
-         }
-         VG_(printf)("\n  Loc=");
-         if (varp->gexpr) {
-            ML_(pp_GX)(varp->gexpr);
-         } else {
-            VG_(printf)("NULL");
-         }
-         VG_(printf)("\n");
-         if (varp->fbGX) {
-            VG_(printf)("  FrB=");
-            ML_(pp_GX)( varp->fbGX );
+         /* Possibly show .. */
+         if (td3) {
+            VG_(printf)("<%lx> addVar: level %d: %s :: ",
+                        varp->dioff,
+                        varp->level,
+                        varp->name ? varp->name : "<anon_var>" );
+            if (varp->typeR) {
+               ML_(pp_TyEnt_C_ishly)( tyents_to_keep, varp->typeR );
+            } else {
+               VG_(printf)("NULL");
+            }
+            VG_(printf)("\n  Loc=");
+            if (varp->gexpr) {
+               ML_(pp_GX)(varp->gexpr);
+            } else {
+               VG_(printf)("NULL");
+            }
             VG_(printf)("\n");
-         } else {
-            VG_(printf)("  FrB=none\n");
+            if (varp->fbGX) {
+               VG_(printf)("  FrB=");
+               ML_(pp_GX)( varp->fbGX );
+               VG_(printf)("\n");
+            } else {
+               VG_(printf)("  FrB=none\n");
+            }
+            VG_(printf)("  declared at: %s:%d\n",
+                        varp->fName ? varp->fName : "NULL",
+                        varp->fLine );
+            if (varp->absOri != (UWord)D3_INVALID_CUOFF)
+               VG_(printf)("  abstract origin: <%lx>\n", varp->absOri);
          }
-         VG_(printf)("  declared at: %s:%d\n",
-                     varp->fName ? varp->fName : "NULL",
-                     varp->fLine );
-         if (varp->absOri != (UWord)D3_INVALID_CUOFF)
-            VG_(printf)("  abstract origin: <%lx>\n", varp->absOri);
-      }
 
-      /* Skip variables which have no location.  These must be
-         abstract instances; they are useless as-is since with no
-         location they have no specified memory location.  They will
-         presumably be referred to via the absOri fields of other
-         variables. */
-      if (!varp->gexpr) {
-         TRACE_D3("  SKIP (no location)\n\n");
-         continue;
-      }
-
-      /* So it has a location, at least.  If it refers to some other
-         entry through its absOri field, pull in further info through
-         that. */
-      if (varp->absOri != (UWord)D3_INVALID_CUOFF) {
-         Bool found;
-         Word ixFirst, ixLast;
-         TempVar key;
-         TempVar* keyp = &key;
-         TempVar *varAI;
-         VG_(memset)(&key, 0, sizeof(key)); /* not necessary */
-         key.dioff = varp->absOri; /* this is what we want to find */
-         found = VG_(lookupXA)( dioff_lookup_tab, &keyp,
-                                &ixFirst, &ixLast );
-         if (!found) {
-            /* barf("DW_AT_abstract_origin can't be resolved"); */
-            TRACE_D3("  SKIP (DW_AT_abstract_origin can't be resolved)\n\n");
+         /* Skip variables which have no location.  These must be
+            abstract instances; they are useless as-is since with no
+            location they have no specified memory location.  They will
+            presumably be referred to via the absOri fields of other
+            variables. */
+         if (!varp->gexpr) {
+            TRACE_D3("  SKIP (no location)\n\n");
             continue;
          }
-         /* If the following fails, there is more than one entry with
-            the same dioff.  Which can't happen. */
-         vg_assert(ixFirst == ixLast);
-         varAI = *(TempVar**)VG_(indexXA)( dioff_lookup_tab, ixFirst );
-         /* stay sane */
-         vg_assert(varAI);
-         vg_assert(varAI->dioff == varp->absOri);
-
-         /* Copy what useful info we can. */
-         if (varAI->typeR && !varp->typeR)
-            varp->typeR = varAI->typeR;
-         if (varAI->name && !varp->name)
-            varp->name = varAI->name;
-         if (varAI->fName && !varp->fName)
-            varp->fName = varAI->fName;
-         if (varAI->fLine > 0 && varp->fLine == 0)
-            varp->fLine = varAI->fLine;
-      }
 
-      /* Give it a name if it doesn't have one. */
-      if (!varp->name)
-         varp->name = ML_(addStr)( di, "<anon_var>", -1 );
-
-      /* So now does it have enough info to be useful? */
-      /* NOTE: re typeR: this is a hack.  If typeR is Te_UNKNOWN then
-         the type didn't get resolved.  Really, in that case
-         something's broken earlier on, and should be fixed, rather
-         than just skipping the variable. */
-      ent = ML_(TyEnts__index_by_cuOff)( tyents_to_keep,
-                                         tyents_to_keep_cache, 
-                                         varp->typeR );
-      /* The next two assertions should be guaranteed by 
-         our previous call to resolve_variable_types. */
-      vg_assert(ent);
-      vg_assert(ML_(TyEnt__is_type)(ent) || ent->tag == Te_UNKNOWN);
-
-      if (ent->tag == Te_UNKNOWN) continue;
-
-      vg_assert(varp->gexpr);
-      vg_assert(varp->name);
-      vg_assert(varp->typeR);
-      vg_assert(varp->level >= 0);
-
-      /* Ok.  So we're going to keep it.  Call ML_(addVar) once for
-         each address range in which the variable exists. */
-      TRACE_D3("  ACQUIRE for range(s) ");
-      { AddrRange  oneRange;
-        AddrRange* varPcRanges;
-        Word       nVarPcRanges;
-        /* Set up to iterate over address ranges, however
-           represented. */
-        if (varp->nRanges == 0 || varp->nRanges == 1) {
-           vg_assert(!varp->rngMany);
-           if (varp->nRanges == 0) {
+         /* So it has a location, at least.  If it refers to some other
+            entry through its absOri field, pull in further info through
+            that. */
+         if (varp->absOri != (UWord)D3_INVALID_CUOFF) {
+            Bool found;
+            Word ixFirst, ixLast;
+            TempVar key;
+            TempVar* keyp = &key;
+            TempVar *varAI;
+            VG_(memset)(&key, 0, sizeof(key)); /* not necessary */
+            key.dioff = varp->absOri; /* this is what we want to find */
+            found = VG_(lookupXA)( dioff_lookup_tab, &keyp,
+                                   &ixFirst, &ixLast );
+            if (!found) {
+               /* barf("DW_AT_abstract_origin can't be resolved"); */
+               TRACE_D3("  SKIP (DW_AT_abstract_origin can't be resolved)\n\n");
+               continue;
+            }
+            /* If the following fails, there is more than one entry with
+               the same dioff.  Which can't happen. */
+            vg_assert(ixFirst == ixLast);
+            varAI = *(TempVar**)VG_(indexXA)( dioff_lookup_tab, ixFirst );
+            /* stay sane */
+            vg_assert(varAI);
+            vg_assert(varAI->dioff == varp->absOri);
+
+            /* Copy what useful info we can. */
+            if (varAI->typeR && !varp->typeR)
+               varp->typeR = varAI->typeR;
+            if (varAI->name && !varp->name)
+               varp->name = varAI->name;
+            if (varAI->fName && !varp->fName)
+               varp->fName = varAI->fName;
+            if (varAI->fLine > 0 && varp->fLine == 0)
+               varp->fLine = varAI->fLine;
+         }
+
+         /* Give it a name if it doesn't have one. */
+         if (!varp->name)
+            varp->name = ML_(addStr)( di, "<anon_var>", -1 );
+
+         /* So now does it have enough info to be useful? */
+         /* NOTE: re typeR: this is a hack.  If typeR is Te_UNKNOWN then
+            the type didn't get resolved.  Really, in that case
+            something's broken earlier on, and should be fixed, rather
+            than just skipping the variable. */
+         ent = ML_(TyEnts__index_by_cuOff)( tyents_to_keep,
+                                            tyents_to_keep_cache, 
+                                            varp->typeR );
+         /* The next two assertions should be guaranteed by 
+            our previous call to resolve_variable_types. */
+         vg_assert(ent);
+         vg_assert(ML_(TyEnt__is_type)(ent) || ent->tag == Te_UNKNOWN);
+
+         if (ent->tag == Te_UNKNOWN) continue;
+
+         vg_assert(varp->gexpr);
+         vg_assert(varp->name);
+         vg_assert(varp->typeR);
+         vg_assert(varp->level >= 0);
+
+         /* Ok.  So we're going to keep it.  Call ML_(addVar) once for
+            each address range in which the variable exists. */
+         TRACE_D3("  ACQUIRE for range(s) ");
+         { AddrRange  oneRange;
+           AddrRange* varPcRanges;
+           Word       nVarPcRanges;
+           /* Set up to iterate over address ranges, however
+              represented. */
+           if (varp->nRanges == 0 || varp->nRanges == 1) {
+              vg_assert(!varp->rngMany);
+              if (varp->nRanges == 0) {
+                 vg_assert(varp->rngOneMin == 0);
+                 vg_assert(varp->rngOneMax == 0);
+              }
+              nVarPcRanges = varp->nRanges;
+              oneRange.aMin = varp->rngOneMin;
+              oneRange.aMax = varp->rngOneMax;
+              varPcRanges = &oneRange;
+           } else {
+              vg_assert(varp->rngMany);
               vg_assert(varp->rngOneMin == 0);
               vg_assert(varp->rngOneMax == 0);
+              nVarPcRanges = VG_(sizeXA)(varp->rngMany);
+              vg_assert(nVarPcRanges >= 2);
+              vg_assert(nVarPcRanges == (Word)varp->nRanges);
+              varPcRanges = VG_(indexXA)(varp->rngMany, 0);
            }
-           nVarPcRanges = varp->nRanges;
-           oneRange.aMin = varp->rngOneMin;
-           oneRange.aMax = varp->rngOneMax;
-           varPcRanges = &oneRange;
-        } else {
-           vg_assert(varp->rngMany);
-           vg_assert(varp->rngOneMin == 0);
-           vg_assert(varp->rngOneMax == 0);
-           nVarPcRanges = VG_(sizeXA)(varp->rngMany);
-           vg_assert(nVarPcRanges >= 2);
-           vg_assert(nVarPcRanges == (Word)varp->nRanges);
-           varPcRanges = VG_(indexXA)(varp->rngMany, 0);
-        }
-        if (varp->level == 0)
-           vg_assert( nVarPcRanges == 1 );
-        /* and iterate */
-        for (i = 0; i < nVarPcRanges; i++) {
-           Addr pcMin = varPcRanges[i].aMin;
-           Addr pcMax = varPcRanges[i].aMax;
-           vg_assert(pcMin <= pcMax);
-           /* Level 0 is the global address range.  So at level 0 we
-              don't want to bias pcMin/pcMax; but at all other levels
-              we do since those are derived from svmas in the Dwarf
-              we're reading.  Be paranoid ... */
-           if (varp->level == 0) {
-              vg_assert(pcMin == (Addr)0);
-              vg_assert(pcMax == ~(Addr)0);
-           } else {
-              /* vg_assert(pcMin > (Addr)0);
-                 No .. we can legitimately expect to see ranges like 
-                 0x0-0x11D (pre-biasing, of course). */
-              vg_assert(pcMax < ~(Addr)0);
+           if (varp->level == 0)
+              vg_assert( nVarPcRanges == 1 );
+           /* and iterate */
+           for (i = 0; i < nVarPcRanges; i++) {
+              Addr pcMin = varPcRanges[i].aMin;
+              Addr pcMax = varPcRanges[i].aMax;
+              vg_assert(pcMin <= pcMax);
+              /* Level 0 is the global address range.  So at level 0 we
+                 don't want to bias pcMin/pcMax; but at all other levels
+                 we do since those are derived from svmas in the Dwarf
+                 we're reading.  Be paranoid ... */
+              if (varp->level == 0) {
+                 vg_assert(pcMin == (Addr)0);
+                 vg_assert(pcMax == ~(Addr)0);
+              } else {
+                 /* vg_assert(pcMin > (Addr)0);
+                    No .. we can legitimately expect to see ranges like 
+                    0x0-0x11D (pre-biasing, of course). */
+                 vg_assert(pcMax < ~(Addr)0);
+              }
+
+              /* Apply text biasing, for non-global variables. */
+              if (varp->level > 0) {
+                 pcMin += di->text_debug_bias;
+                 pcMax += di->text_debug_bias;
+              } 
+
+              if (i > 0 && (i%2) == 0) 
+                 TRACE_D3("\n                       ");
+              TRACE_D3("[%#lx,%#lx] ", pcMin, pcMax );
+
+              ML_(addVar)(
+                 di, varp->level, 
+                     pcMin, pcMax,
+                     varp->name,  varp->typeR,
+                     varp->gexpr, varp->fbGX,
+                     varp->fName, varp->fLine, td3 
+              );
            }
+         }
 
-           /* Apply text biasing, for non-global variables. */
-           if (varp->level > 0) {
-              pcMin += di->text_debug_bias;
-              pcMax += di->text_debug_bias;
-           } 
-
-           if (i > 0 && (i%2) == 0) 
-              TRACE_D3("\n                       ");
-           TRACE_D3("[%#lx,%#lx] ", pcMin, pcMax );
-
-           ML_(addVar)(
-              di, varp->level, 
-                  pcMin, pcMax,
-                  varp->name,  varp->typeR,
-                  varp->gexpr, varp->fbGX,
-                  varp->fName, varp->fLine, td3 
-           );
-        }
+         TRACE_D3("\n\n");
+         /* and move on to the next var */
       }
 
-      TRACE_D3("\n\n");
-      /* and move on to the next var */
-   }
-
-   /* Now free all the TempVars */
-   n = VG_(sizeXA)( tempvars );
-   for (i = 0; i < n; i++) {
-      varp = *(TempVar**)VG_(indexXA)( tempvars, i );
-      ML_(dinfo_free)(varp);
-   }
-   VG_(deleteXA)( tempvars );
-   tempvars = NULL;
+      /* Now free all the TempVars */
+      n = VG_(sizeXA)( tempvars );
+      for (i = 0; i < n; i++) {
+         varp = *(TempVar**)VG_(indexXA)( tempvars, i );
+         ML_(dinfo_free)(varp);
+      }
+      VG_(deleteXA)( tempvars );
+      tempvars = NULL;
 
-   /* and the temp lookup table */
-   VG_(deleteXA)( dioff_lookup_tab );
+      /* and the temp lookup table */
+      VG_(deleteXA)( dioff_lookup_tab );
 
-   /* and the ranges tree.  Note that we need to also free the XArrays
-      which constitute the keys, hence pass VG_(deleteXA) as a
-      key-finalizer. */
-   VG_(deleteFM)( rangestree, (void(*)(UWord))VG_(deleteXA), NULL );
+      /* and the ranges tree.  Note that we need to also free the XArrays
+         which constitute the keys, hence pass VG_(deleteXA) as a
+         key-finalizer. */
+      VG_(deleteFM)( rangestree, (void(*)(UWord))VG_(deleteXA), NULL );
 
-   /* and the tyents_to_keep cache */
-   ML_(dinfo_free)( tyents_to_keep_cache );
-   tyents_to_keep_cache = NULL;
+      /* and the tyents_to_keep cache */
+      ML_(dinfo_free)( tyents_to_keep_cache );
+      tyents_to_keep_cache = NULL;
 
-   vg_assert( varparser.filenameTable == NULL );
+      vg_assert( varparser.filenameTable == NULL );
 
-   /* And the signatured type hash.  */
-   VG_(HT_destruct) ( signature_types, ML_(dinfo_free) );
+      /* And the signatured type hash.  */
+      VG_(HT_destruct) ( signature_types, ML_(dinfo_free) );
 
-   /* record the GExprs in di so they can be freed later */
-   vg_assert(!di->admin_gexprs);
-   di->admin_gexprs = gexprs;
+      /* record the GExprs in di so they can be freed later */
+      vg_assert(!di->admin_gexprs);
+      di->admin_gexprs = gexprs;
+   }
 }
 
 
index 4a836bf51539fbc5d8a266a10e154dc99f78e1a3..9f157a60e5e03519cbba80fe7d345a892acf0700 100644 (file)
@@ -1447,6 +1447,7 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
    vg_assert(di->fsm.filename);
    vg_assert(!di->symtab);
    vg_assert(!di->loctab);
+   vg_assert(!di->inltab);
    vg_assert(!di->cfsi);
    vg_assert(!di->cfsi_exprs);
    vg_assert(!di->strpool);
@@ -2801,11 +2802,11 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
                                       debug_str_escn,
                                       debug_str_alt_escn );
          /* The new reader: read the DIEs in .debug_info to acquire
-            information on variable types and locations.  But only if
-            the tool asks for it, or the user requests it on the
-            command line. */
-         if (VG_(needs).var_info /* the tool requires it */
-             || VG_(clo_read_var_info) /* the user asked for it */) {
+            information on variable types and locations or inline info.
+            But only if the tool asks for it, or the user requests it on
+            the command line. */
+         if (VG_(clo_read_var_info) /* the user or tool asked for it */
+             || VG_(clo_read_inline_info)) {
             ML_(new_dwarf3_reader)(
                di, debug_info_escn,     debug_types_escn,
                    debug_abbv_escn,     debug_line_escn,
@@ -2834,7 +2835,7 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
       showing the number of variables read for each object.
       (Currently disabled -- is a sanity-check mechanism for
       exp-sgcheck.) */
-   if (0 && (VG_(needs).var_info || VG_(clo_read_var_info))) {
+   if (0 &&  VG_(clo_read_var_info)) {
       UWord nVars = 0;
       if (di->varinfo) {
          for (j = 0; j < VG_(sizeXA)(di->varinfo); j++) {
index 6fca1bb89148c7ac46047413db6c2c26442234bb..d20a464777d220505c2b44ac69c2fdb104cde785 100644 (file)
@@ -1128,11 +1128,11 @@ Bool ML_(read_macho_debug_info)( struct _DebugInfo* di )
                                       DiSlice_INVALID /* ALT .debug_str */ );
 
          /* The new reader: read the DIEs in .debug_info to acquire
-            information on variable types and locations.  But only if
-            the tool asks for it, or the user requests it on the
-            command line. */
-         if (VG_(needs).var_info /* the tool requires it */
-             || VG_(clo_read_var_info) /* the user asked for it */) {
+            information on variable types and locations or inline info.
+            But only if the tool asks for it, or the user requests it on
+            the command line. */
+         if (VG_(clo_read_var_info) /* the user or tool asked for it */
+             || VG_(clo_read_inline_info)) {
             ML_(new_dwarf3_reader)(
                di, debug_info_mscn,
                    DiSlice_INVALID, /* .debug_types */
index 121c8269113c5b97770e24a8f72e745792f8a93e..b0a4e1cc2285893c7e46e709f29b3b57f27a8350 100644 (file)
@@ -450,6 +450,115 @@ void ML_(addLineInfo) ( struct _DebugInfo* di,
    addLoc ( di, &loc );
 }
 
+/* Add an inlined call info to the inlined call table. 
+*/
+static void addInl ( struct _DebugInfo* di, DiInlLoc* inl )
+{
+   UInt   new_sz, i;
+   DiInlLoc* new_tab;
+
+   /* empty inl should have been ignored earlier */
+   vg_assert(inl->addr_lo < inl->addr_hi);
+
+   if (di->inltab_used == di->inltab_size) {
+      new_sz = 2 * di->inltab_size;
+      if (new_sz == 0) new_sz = 500;
+      new_tab = ML_(dinfo_zalloc)( "di.storage.addInl.1",
+                                   new_sz * sizeof(DiInlLoc) );
+      if (di->inltab != NULL) {
+         for (i = 0; i < di->inltab_used; i++)
+            new_tab[i] = di->inltab[i];
+         ML_(dinfo_free)(di->inltab);
+      }
+      di->inltab = new_tab;
+      di->inltab_size = new_sz;
+   }
+
+   di->inltab[di->inltab_used] = *inl;
+   if (inl->addr_hi - inl->addr_lo > di->maxinl_codesz)
+      di->maxinl_codesz = inl->addr_hi - inl->addr_lo;
+   di->inltab_used++;
+   vg_assert(di->inltab_used <= di->inltab_size);
+}
+
+
+/* Resize the InlTab (inlined call table) to save memory, by removing
+   (and, potentially, allowing m_mallocfree to unmap) any unused space
+   at the end of the table.
+*/
+static void shrinkInlTab ( struct _DebugInfo* di )
+{
+   DiInlLoc* new_tab;
+   UWord new_sz = di->inltab_used;
+   if (new_sz == di->inltab_size) return;
+   vg_assert(new_sz < di->inltab_size);
+
+   new_tab = ML_(dinfo_zalloc)( "di.storage.shrinkInlTab", 
+                                new_sz * sizeof(DiInlLoc) );
+   VG_(memcpy)(new_tab, di->inltab, new_sz * sizeof(DiInlLoc));
+
+   ML_(dinfo_free)(di->inltab);
+   di->inltab = new_tab;
+   di->inltab_size = new_sz;
+}
+
+/* Top-level place to call to add a addr-to-inlined fn info. */
+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)
+{
+   DiInlLoc inl;
+
+   /* Similar paranoia as in ML_(addLineInfo). Unclear if needed. */
+   if (addr_lo >= addr_hi) {
+       if (VG_(clo_verbosity) > 2) {
+           VG_(message)(Vg_DebugMsg, 
+                        "warning: inlined info addresses out of order "
+                        "at: 0x%lx 0x%lx\n", addr_lo, addr_hi);
+       }
+       addr_hi = addr_lo + 1;
+   }
+
+   vg_assert(lineno >= 0);
+   if (lineno > MAX_LINENO) {
+      static Bool complained = False;
+      if (!complained) {
+         complained = True;
+         VG_(message)(Vg_UserMsg, 
+                      "warning: ignoring inlined call info entry with "
+                      "huge line number (%d)\n", lineno);
+         VG_(message)(Vg_UserMsg, 
+                      "         Can't handle line numbers "
+                      "greater than %d, sorry\n", MAX_LINENO);
+         VG_(message)(Vg_UserMsg, 
+                      "(Nb: this message is only shown once)\n");
+      }
+      return;
+   }
+
+   // code resulting from inlining of inlinedfn:
+   inl.addr_lo   = addr_lo;
+   inl.addr_hi   = addr_hi;
+   inl.inlinedfn = inlinedfn;
+   // caller:
+   inl.filename  = filename;
+   inl.dirname   = dirname;
+   inl.lineno    = lineno;
+   inl.level     = level;
+
+   if (0) VG_(message)
+             (Vg_DebugMsg, 
+              "addInlInfo: fn %s inlined as addr_lo %#lx,addr_hi %#lx,"
+              "caller %s:%d (dir %s)\n",
+              inlinedfn, addr_lo, addr_hi, filename, lineno, 
+              dirname ? dirname : "???");
+
+   addInl ( di, &inl );
+}
+
 
 /* Top-level place to call to add a CFI summary record.  The supplied
    DiCfSI is copied. */
@@ -1701,6 +1810,45 @@ static void canonicaliseLoctab ( struct _DebugInfo* di )
    shrinkLocTab(di);
 }
 
+/* Sort the inlined call table by starting address.  Mash the table around
+   so as to establish the property that addresses are in order.
+   This facilitates using binary search to map addresses to locations when
+   we come to query the table.
+   Note : ranges can overlap, multiple ranges can start at an address,
+   multiple ranges can end at an address.
+*/
+static Int compare_DiInlLoc ( const void* va, const void* vb ) 
+{
+   const DiInlLoc* a = va;
+   const DiInlLoc* b = vb;
+   if (a->addr_lo < b->addr_lo) return -1;
+   if (a->addr_lo > b->addr_lo) return  1;
+   return 0;
+}
+
+static void canonicaliseInltab ( struct _DebugInfo* di )
+{
+   Word i;
+
+   if (di->inltab_used == 0)
+      return;
+
+   /* Sort by start address. */
+   VG_(ssort)(di->inltab, di->inltab_used, 
+                          sizeof(*di->inltab), compare_DiInlLoc);
+
+   /* Ensure relevant postconditions hold. */
+   for (i = 0; i < ((Word)di->inltab_used)-1; i++) {
+      /* No zero-sized inlined call. */
+      vg_assert(di->inltab[i].addr_lo < di->inltab[i].addr_hi);
+      /* In order, but we can have duplicates and overlapping ranges. */
+      vg_assert(di->inltab[i].addr_lo <= di->inltab[i+1].addr_lo);
+   }
+
+   /* Free up unused space at the end of the table. */
+   shrinkInlTab(di);
+}
+
 
 /* Sort the call-frame-info table by starting address.  Mash the table
    around so as to establish the property that addresses are in order
@@ -1815,6 +1963,7 @@ void ML_(canonicaliseTables) ( struct _DebugInfo* di )
 {
    canonicaliseSymtab ( di );
    canonicaliseLoctab ( di );
+   canonicaliseInltab ( di );
    ML_(canonicaliseCFI) ( di );
    canonicaliseVarInfo ( di );
    if (di->strpool)
index 9638042bc62408758920076140f25bf09c20b762..978524bba64a9def47c5c4fa87f7f18aa2474dbd 100644 (file)
@@ -323,28 +323,36 @@ static Bool eq_Error ( VgRes res, Error* e1, Error* e2 )
 static void printSuppForIp_XML(UInt n, Addr ip, void* uu_opaque)
 {
    static HChar buf[ERRTXT_LEN];
-   if ( VG_(get_fnname_no_cxx_demangle) (ip, buf,  ERRTXT_LEN) ) {
-      VG_(printf_xml)("    <sframe> <fun>%pS</fun> </sframe>\n", buf);
-   } else
-   if ( VG_(get_objname)(ip, buf, ERRTXT_LEN) ) {
-      VG_(printf_xml)("    <sframe> <obj>%pS</obj> </sframe>\n", buf);
-   } else {
-      VG_(printf_xml)("    <sframe> <obj>*</obj> </sframe>\n");
-   }
+   InlIPCursor* iipc = VG_(new_IIPC)(ip);
+   do {
+      if ( VG_(get_fnname_no_cxx_demangle) (ip, buf,  ERRTXT_LEN, iipc) ) {
+         VG_(printf_xml)("    <sframe> <fun>%pS</fun> </sframe>\n", buf);
+      } else
+      if ( VG_(get_objname)(ip, buf, ERRTXT_LEN) ) {
+         VG_(printf_xml)("    <sframe> <obj>%pS</obj> </sframe>\n", buf);
+      } else {
+         VG_(printf_xml)("    <sframe> <obj>*</obj> </sframe>\n");
+      }
+   } while (VG_(next_IIPC)(iipc));
+   VG_(delete_IIPC)(iipc);
 }
 
 static void printSuppForIp_nonXML(UInt n, Addr ip, void* textV)
 {
    static HChar buf[ERRTXT_LEN];
    XArray* /* of HChar */ text = (XArray*)textV;
-   if ( VG_(get_fnname_no_cxx_demangle) (ip, buf,  ERRTXT_LEN) ) {
-      VG_(xaprintf)(text, "   fun:%s\n", buf);
-   } else
-   if ( VG_(get_objname)(ip, buf, ERRTXT_LEN) ) {
-      VG_(xaprintf)(text, "   obj:%s\n", buf);
-   } else {
-      VG_(xaprintf)(text, "   obj:*\n");
-   }
+   InlIPCursor* iipc = VG_(new_IIPC)(ip);
+   do {
+      if ( VG_(get_fnname_no_cxx_demangle) (ip, buf, ERRTXT_LEN, iipc) ) {
+         VG_(xaprintf)(text, "   fun:%s\n", buf);
+      } else
+      if ( VG_(get_objname)(ip, buf, ERRTXT_LEN) ) {
+         VG_(xaprintf)(text, "   obj:%s\n", buf);
+      } else {
+         VG_(xaprintf)(text, "   obj:*\n");
+      }
+   } while (VG_(next_IIPC)(iipc));
+   VG_(delete_IIPC)(iipc);
 }
 
 /* Generate a suppression for an error, either in text or XML mode.
@@ -1472,6 +1480,8 @@ static Bool supploc_IsQuery ( const void* supplocV )
    with the IP function name or with the IP object name.
    First time the fun or obj name is needed for an IP member
    of a stack trace, it will be computed and stored in names.
+   Also, if the IP corresponds to one or more inlined function calls,
+   the inlined function names are expanded.
    The IPtoFunOrObjCompleter type is designed to minimise the nr of
    allocations and the nr of debuginfo search. */
 typedef
@@ -1479,13 +1489,49 @@ typedef
       StackTrace ips; // stack trace we are lazily completing.
       UWord n_ips; // nr of elements in ips.
 
+      // VG_(generic_match) calls haveInputInpC to check
+      // for the presence of an input element identified by ixInput
+      // (i.e. a number that identifies the ixInput element of the
+      // input sequence). It calls supp_pattEQinp to match this input
+      // element with a pattern.
+      // When inlining info is used to provide inlined function calls
+      // in stacktraces, one IP in ips can be expanded in several
+      // function names. So, each time input (or presence of input)
+      // is requested by VG_(generic_match), we will expand
+      // more IP of ips till we have expanded enough to reach the
+      // input element requested (or we cannot expand anymore).
+
+      UWord n_ips_expanded;
+      // n_ips_expanded maintains the nr of elements in ips that we have
+      // already expanded.
+      UWord n_expanded;
+      // n_expanded maintains the nr of elements resulting from the expansion
+      // of the n_ips_expanded IPs. Without inlined function calls,
+      // n_expanded == n_ips_expanded. With inlining info,
+      // n_expanded >= n_ips_expanded.
+
+      Int* n_offsets_per_ip;
+      // n_offsets_per_ip[i] gives the nr of offsets in fun_offsets and obj_offsets
+      // resulting of the expansion of ips[i].
+      // The sum of all n_expanded_per_ip must be equal to n_expanded.
+      // This array allows to retrieve the position in ips corresponding to an ixInput.
+
+      // size (in elements) of fun_offsets and obj_offsets.
+      // (fun|obj)_offsets are reallocated if more space is needed
+      // to expand an IP.
+      UWord sz_offsets;
+
       Int* fun_offsets;
-      // fun_offsets[i] is the offset in names where the
-      // function name for ips[i] is located.
-      // An offset -1 means the function name is not yet completed.
+      // fun_offsets[ixInput] is the offset in names where the
+      // function name for the ixInput element of the input sequence
+      // can be found. As one IP of ips can be expanded in several
+      // function calls due to inlined function calls, we can have more
+      // elements in fun_offsets than in ips.
+      // An offset -1 means the function name has not yet been computed.
       Int* obj_offsets;
-      // Similarly, obj_offsets[i] gives the offset for the
-      // object name for ips[i] (-1 meaning object name not yet completed).
+      // Similarly, obj_offsets[ixInput] gives the offset for the
+      // object name for ips[ixInput]
+      // (-1 meaning object name not yet been computed).
 
       // All function names and object names will be concatenated
       // in names. names is reallocated on demand.
@@ -1499,22 +1545,37 @@ typedef
 static void clearIPtoFunOrObjCompleter
   (IPtoFunOrObjCompleter* ip2fo)
 {
-   if (ip2fo->fun_offsets) VG_(free)(ip2fo->fun_offsets);
-   if (ip2fo->obj_offsets) VG_(free)(ip2fo->obj_offsets);
-   if (ip2fo->names)       VG_(free)(ip2fo->names);
+   if (ip2fo->n_offsets_per_ip) VG_(free)(ip2fo->n_offsets_per_ip);
+   if (ip2fo->fun_offsets)      VG_(free)(ip2fo->fun_offsets);
+   if (ip2fo->obj_offsets)      VG_(free)(ip2fo->obj_offsets);
+   if (ip2fo->names)            VG_(free)(ip2fo->names);
 }
 
-/* foComplete returns the function name or object name for IP.
-   If needFun, returns the function name for IP
-   else returns the object name for IP.
+/* Grow ip2fo->names to ensure we have ERRTXT_LEN characters available
+   in ip2fo->names and returns a pointer to the first free char. */
+static HChar* grow_names(IPtoFunOrObjCompleter* ip2fo)
+{
+   if (ip2fo->names_szB 
+       < ip2fo->names_free + ERRTXT_LEN) {
+      ip2fo->names 
+         = VG_(realloc)("foc_names",
+                        ip2fo->names,
+                        ip2fo->names_szB + ERRTXT_LEN);
+      ip2fo->names_szB += ERRTXT_LEN;
+   }
+   return ip2fo->names + ip2fo->names_free;
+}
+
+/* foComplete returns the function name or object name for ixInput.
+   If needFun, returns the function name for this input
+   else returns the object name for this input.
    The function name or object name will be computed and added in
-   names if not yet done.
-   IP must be equal to focompl->ipc[ixIP]. */
+   names if not yet done. */
 static HChar* foComplete(IPtoFunOrObjCompleter* ip2fo,
-                         Addr IP, Int ixIP, Bool needFun)
+                         Int ixInput, Bool needFun)
 {
-   vg_assert (ixIP < ip2fo->n_ips);
-   vg_assert (IP == ip2fo->ips[ixIP]);
+   vg_assert (ixInput < ip2fo->n_expanded);
+   vg_assert (VG_(clo_read_inline_info) || ixInput < ip2fo->n_ips);
 
    // ptr to the offset array for function offsets (if needFun)
    // or object offsets (if !needFun).
@@ -1524,30 +1585,13 @@ static HChar* foComplete(IPtoFunOrObjCompleter* ip2fo,
    else
       offsets = &ip2fo->obj_offsets;
 
-   // Allocate offsets if not yet done.
-   if (!*offsets) {
-      Int i;
-      *offsets =
-         VG_(malloc)("foComplete",
-                     ip2fo->n_ips * sizeof(Int));
-      for (i = 0; i < ip2fo->n_ips; i++)
-         (*offsets)[i] = -1;
-   }
-
    // Complete Fun name or Obj name for IP if not yet done.
-   if ((*offsets)[ixIP] == -1) {
-      /* Ensure we have ERRTXT_LEN characters available in names */
-      if (ip2fo->names_szB 
-            < ip2fo->names_free + ERRTXT_LEN) {
-         ip2fo->names 
-            = VG_(realloc)("foc_names",
-                           ip2fo->names,
-                           ip2fo->names_szB + ERRTXT_LEN);
-         ip2fo->names_szB += ERRTXT_LEN;
-      }
-      HChar* caller_name = ip2fo->names + ip2fo->names_free;
-      (*offsets)[ixIP] = ip2fo->names_free;
+   if ((*offsets)[ixInput] == -1) {
+      HChar* caller_name = grow_names(ip2fo);
+      (*offsets)[ixInput] = ip2fo->names_free;
       if (needFun) {
+         // With inline info, fn names must have been completed already.
+         vg_assert (!VG_(clo_read_inline_info));
          /* Get the function name into 'caller_name', or "???"
             if unknown. */
          // Nb: C++-mangled names are used in suppressions.  Do, though,
@@ -1555,28 +1599,140 @@ static HChar* foComplete(IPtoFunOrObjCompleter* ip2fo,
          // up comparing "malloc" in the suppression against
          // "_vgrZU_libcZdsoZa_malloc" in the backtrace, and the
          // two of them need to be made to match.
-         if (!VG_(get_fnname_no_cxx_demangle)(IP, caller_name, ERRTXT_LEN))
+         if (!VG_(get_fnname_no_cxx_demangle)(ip2fo->ips[ixInput],
+                                              caller_name, ERRTXT_LEN,
+                                              NULL))
             VG_(strcpy)(caller_name, "???");
       } else {
          /* Get the object name into 'caller_name', or "???"
             if unknown. */
-         if (!VG_(get_objname)(IP, caller_name, ERRTXT_LEN))
+         UWord i;
+         UWord last_expand_pos_ips = 0;
+         UWord pos_ips;
+
+         /* First get the pos in ips corresponding to ixInput */
+         for (pos_ips = 0; pos_ips < ip2fo->n_expanded; pos_ips++) {
+            last_expand_pos_ips += ip2fo->n_offsets_per_ip[pos_ips];
+            if (ixInput <= last_expand_pos_ips)
+               break;
+         }
+         /* pos_ips is the position in ips corresponding to ixInput.
+            last_expand_pos_ips is the last offset in fun/obj where
+            ips[pos_ips] has been expanded. */
+
+         if (!VG_(get_objname)(ip2fo->ips[pos_ips], caller_name, ERRTXT_LEN))
             VG_(strcpy)(caller_name, "???");
+
+         // Have all inlined calls pointing at this object name
+         for (i = last_expand_pos_ips - ip2fo->n_offsets_per_ip[pos_ips] - 1;
+              i <= last_expand_pos_ips;
+              i++)
+            ip2fo->obj_offsets[i] = ip2fo->names_free;
       }
       ip2fo->names_free += VG_(strlen)(caller_name) + 1;
    }
 
-   return ip2fo->names + (*offsets)[ixIP];
+   return ip2fo->names + (*offsets)[ixInput];
+}
+
+// Grow fun and obj _offsets arrays to have at least n_req elements.
+// Ensure n_offsets_per_ip is allocated.
+static void grow_offsets(IPtoFunOrObjCompleter* ip2fo, Int n_req)
+{
+   Int i;
+
+   // n_offsets_per_ip must always have the size of the ips array
+   if (ip2fo->n_offsets_per_ip == NULL) {
+      ip2fo->n_offsets_per_ip = VG_(malloc)("grow_offsets",
+                                            ip2fo->n_ips * sizeof(Int));
+      for (i = 0; i < ip2fo->n_ips; i++)
+         ip2fo->n_offsets_per_ip[i] = 0;
+   }
+
+   if (ip2fo->sz_offsets >= n_req)
+      return;
+
+   // Avoid too much re-allocation by allocating at least ip2fo->n_ips
+   // elements and at least a few more elements than the current size.
+   if (n_req < ip2fo->n_ips)
+      n_req = ip2fo->n_ips;
+   if (n_req < ip2fo->sz_offsets + 5)
+      n_req = ip2fo->sz_offsets + 5;
+
+   ip2fo->fun_offsets = VG_(realloc)("grow_offsets", ip2fo->fun_offsets,
+                                     n_req * sizeof(Int));
+   for (i = ip2fo->sz_offsets; i < n_req; i++)
+      ip2fo->fun_offsets[i] = -1;
+
+   ip2fo->obj_offsets = VG_(realloc)("grow_offsets", ip2fo->obj_offsets,
+                                     n_req * sizeof(Int));
+   for (i = ip2fo->sz_offsets; i < n_req; i++)
+      ip2fo->obj_offsets[i] = -1;
+
+   ip2fo->sz_offsets = n_req;   
+}
+
+// Expands more IPs from ip2fo->ips.
+static void expandInput (IPtoFunOrObjCompleter* ip2fo, UWord ixInput )
+{
+   while (ip2fo->n_ips_expanded < ip2fo->n_ips
+          && ip2fo->n_expanded <= ixInput) {
+      if (VG_(clo_read_inline_info)) {
+         // Expand one more IP in one or more calls.
+         const Addr IP = ip2fo->ips[ip2fo->n_ips_expanded];
+         InlIPCursor *iipc;
+
+         iipc = VG_(new_IIPC)(IP);
+         // The only thing we really need is the nr of inlined fn calls
+         // corresponding to the IP we will expand.
+         // However, computing this is mostly the same as finding
+         // the function name. So, let's directly complete the function name.
+         do {
+            HChar* caller_name = grow_names(ip2fo);
+            grow_offsets(ip2fo, ip2fo->n_expanded+1);
+            ip2fo->fun_offsets[ip2fo->n_expanded] = ip2fo->names_free;
+            if (!VG_(get_fnname_no_cxx_demangle)(IP, 
+                                                 caller_name, ERRTXT_LEN,
+                                                 iipc))
+               VG_(strcpy)(caller_name, "???");
+            ip2fo->names_free += VG_(strlen)(caller_name) + 1;
+            ip2fo->n_expanded++;
+            ip2fo->n_offsets_per_ip[ip2fo->n_ips_expanded]++;
+         } while (VG_(next_IIPC)(iipc));
+         ip2fo->n_ips_expanded++;
+         VG_(delete_IIPC) (iipc);
+      } else {
+         // Without inlined fn call info, expansion simply
+         // consists in allocating enough elements in (fun|obj)_offsets.
+         // The function or object names themselves will be completed
+         // when requested.
+         Int i;
+         grow_offsets(ip2fo, ip2fo->n_ips);
+         ip2fo->n_ips_expanded = ip2fo->n_ips;
+         ip2fo->n_expanded = ip2fo->n_ips;
+         for (i = 0; i < ip2fo->n_ips; i++)
+            ip2fo->n_offsets_per_ip[i] = 1;
+      }
+   }
+}
+
+static Bool haveInputInpC (void* inputCompleter, UWord ixInput )
+{
+   IPtoFunOrObjCompleter* ip2fo = inputCompleter;
+   expandInput(ip2fo, ixInput);
+   return ixInput < ip2fo->n_expanded;
 }
 
 static Bool supp_pattEQinp ( const void* supplocV, const void* addrV,
-                             void* inputCompleter, UWord ixAddrV )
+                             void* inputCompleter, UWord ixInput )
 {
    const SuppLoc* supploc = supplocV; /* PATTERN */
-   Addr     ip      = *(const Addr*)addrV; /* INPUT */
    IPtoFunOrObjCompleter* ip2fo = inputCompleter;
    HChar* funobj_name; // Fun or Obj name.
 
+   expandInput(ip2fo, ixInput);
+   vg_assert(ixInput < ip2fo->n_expanded);
+
    /* So, does this IP address match this suppression-line? */
    switch (supploc->ty) {
       case DotDotDot:
@@ -1587,10 +1743,10 @@ static Bool supp_pattEQinp ( const void* supplocV, const void* addrV,
             this can't happen. */
          vg_assert(0);
       case ObjName:
-         funobj_name = foComplete(ip2fo, ip, ixAddrV, False /*needFun*/);
+         funobj_name = foComplete(ip2fo, ixInput, False /*needFun*/);
          break; 
       case FunName:
-         funobj_name = foComplete(ip2fo, ip, ixAddrV, True /*needFun*/);
+         funobj_name = foComplete(ip2fo, ixInput, True /*needFun*/);
          break;
       default:
         vg_assert(0);
@@ -1617,15 +1773,16 @@ static Bool supp_matches_callers(IPtoFunOrObjCompleter* ip2fo, Supp* su)
    SuppLoc*   supps    = su->callers;
    UWord      n_supps  = su->n_callers;
    UWord      szbPatt  = sizeof(SuppLoc);
-   UWord      szbInput = sizeof(Addr);
    Bool       matchAll = False; /* we just want to match a prefix */
    return
       VG_(generic_match)(
          matchAll,
-         /*PATT*/supps, szbPatt, n_supps, 0/*initial Ix*/,
-         /*INPUT*/ip2fo->ips, szbInput, ip2fo->n_ips,  0/*initial Ix*/,
+         /*PATT*/supps, szbPatt, n_supps, 0/*initial ixPatt*/,
+         /*INPUT*/
+         NULL, 0, 0, /* input/szbInput/nInput 0, as using an inputCompleter */  
+         0/*initial ixInput*/,
          supploc_IsStar, supploc_IsQuery, supp_pattEQinp,
-         ip2fo
+         ip2fo, haveInputInpC
       );
 }
 
@@ -1683,6 +1840,10 @@ static Supp* is_suppressible_error ( Error* err )
    /* Prepare the lazy input completer. */
    ip2fo.ips = VG_(get_ExeContext_StackTrace)(err->where);
    ip2fo.n_ips = VG_(get_ExeContext_n_ips)(err->where);
+   ip2fo.n_ips_expanded = 0;
+   ip2fo.n_expanded = 0;
+   ip2fo.sz_offsets = 0;
+   ip2fo.n_offsets_per_ip = NULL;
    ip2fo.fun_offsets = NULL;
    ip2fo.obj_offsets = NULL;
    ip2fo.names = NULL;
index d1e73348ed7112c4d1b2741b52c1bb65e6f1edb2..be9423462460d1f27d6fcadc1c734f4f00e26092 100644 (file)
@@ -146,7 +146,7 @@ static HChar* sym (Addr addr, Bool is_code)
    if (w == 2) w = 0;
    buf[w][0] = '\0';
    if (is_code) {
-      VG_(describe_IP) (addr, buf[w], 200);
+      VG_(describe_IP) (addr, buf[w], 200, NULL);
    } else {
       VG_(get_datasym_and_offset) (addr, buf[w], 200, &offset);
    }
index a375bf074446b5f3a915259f95ac9aae90cc9696..4c4c1b510986098bafc605b00459f88f32586b29 100644 (file)
@@ -179,7 +179,7 @@ static
 char* sym (Addr addr)
 {
    static char buf[200];
-   VG_(describe_IP) (addr, buf, 200);
+   VG_(describe_IP) (addr, buf, 200, NULL);
    return buf;
 }
 
index ee83a2280c2a1dd80abcdee0d59ee030731804a6..9d579c3aebba5b1da7d205c9927f59443916650c 100644 (file)
@@ -162,6 +162,8 @@ static void usage_NORETURN ( Bool debug_help )
 "                              checks for self-modifying code: none, only for\n"
 "                              code found in stacks, for all code, or for all\n"
 "                              code except that from file-backed mappings\n"
+"    --read-inline-info=yes|no read debug info about inlined function calls\n"
+"                              and use it to do better stack traces [no]\n"
 "    --read-var-info=yes|no    read debug info on stack and global variables\n"
 "                              and use it to print better error messages in\n"
 "                              tools that make use of it (Memcheck, Helgrind,\n"
@@ -593,6 +595,7 @@ void main_process_cmd_line_options ( /*OUT*/Bool* logging_to_fd,
       else if VG_BOOL_CLO(arg, "--wait-for-gdb",     VG_(clo_wait_for_gdb)) {}
       else if VG_STR_CLO (arg, "--db-command",       VG_(clo_db_command)) {}
       else if VG_BOOL_CLO(arg, "--sym-offsets",      VG_(clo_sym_offsets)) {}
+      else if VG_BOOL_CLO(arg, "--read-inline-info", VG_(clo_read_inline_info)) {}
       else if VG_BOOL_CLO(arg, "--read-var-info",    VG_(clo_read_var_info)) {}
 
       else if VG_INT_CLO (arg, "--dump-error",       VG_(clo_dump_error))   {}
@@ -1930,6 +1933,9 @@ Int valgrind_main ( Int argc, HChar **argv, HChar **envp )
    //--------------------------------------------------------------
    VG_(debugLog)(1, "main", "Initialise the tool part 1 (pre_clo_init)\n");
    VG_(tl_pre_clo_init)();
+   // Activate var info readers, if the tool asked for it:
+   if (VG_(needs).var_info)
+      VG_(clo_read_var_info) = True;
 
    //--------------------------------------------------------------
    // If --tool and --help/--help-debug was given, now give the core+tool
index dd568c5634f121c759e23e3321cc00a04981632d..b9edd11b0e39d3cb7aba90e6c863824179738595 100644 (file)
@@ -113,6 +113,7 @@ Int    VG_(clo_backtrace_size) = 12;
 Int    VG_(clo_merge_recursive_frames) = 0; // default value: no merge
 const HChar* VG_(clo_sim_hints)      = NULL;
 Bool   VG_(clo_sym_offsets)    = False;
+Bool   VG_(clo_read_inline_info) = False; // Or should be put it to True by default ???
 Bool   VG_(clo_read_var_info)  = False;
 Int    VG_(clo_n_req_tsyms)    = 0;
 const HChar* VG_(clo_req_tsyms)[VG_CLO_MAX_REQ_TSYMS];
index f0ad1ab0011cf4a80db43653ca5b6386f1bbdd6b..ac4277becddf8a813e93ee715039913a4e247316 100644 (file)
@@ -46,7 +46,7 @@ Bool VG_(generic_match) (
         Bool (*pIsStar)(const void*),
         Bool (*pIsQuery)(const void*),
         Bool (*pattEQinp)(const void*,const void*,void*,UWord),
-        void* inputCompleter
+        void* inputCompleter, Bool (*haveInputInpC)(void*,UWord)
      )
 {
    /* This is the spec, written in my favourite formal specification
@@ -67,19 +67,23 @@ Bool VG_(generic_match) (
    Bool  havePatt, haveInput;
    const HChar *currPatt, *currInput;
   tailcall:
-   vg_assert(nPatt >= 0   && nPatt  < 1000000); /* arbitrary */
-   vg_assert(nInput >= 0  && nInput < 1000000); /* arbitrary */
+   vg_assert(nPatt >= 0 && nPatt  < 1000000); /* arbitrary */
+   vg_assert(inputCompleter
+             || (nInput >= 0  && nInput < 1000000)); /* arbitrary */
    vg_assert(ixPatt >= 0  && ixPatt <= nPatt);
-   vg_assert(ixInput >= 0 && ixInput <= nInput);
+   vg_assert(ixInput >= 0 && (inputCompleter || ixInput <= nInput));
 
    havePatt  = ixPatt < nPatt;
-   haveInput = ixInput < nInput;
+   haveInput = inputCompleter ? 
+      (*haveInputInpC)(inputCompleter, ixInput)
+      : ixInput < nInput;
 
    /* No specific need to set NULL when !have{Patt,Input}, but guards
       against inadvertantly dereferencing an out of range pointer to
       the pattern or input arrays. */
    currPatt  = havePatt  ? ((const HChar*)patt) + szbPatt * ixPatt    : NULL;
-   currInput = haveInput ? ((const HChar*)input) + szbInput * ixInput : NULL;
+   currInput = haveInput && !inputCompleter ? 
+      ((const HChar*)input) + szbInput * ixInput : NULL;
 
    // Deal with the complex case first: wildcards.  Do frugal
    // matching.  When encountering a '*', first skip no characters
@@ -104,7 +108,7 @@ Bool VG_(generic_match) (
                                  patt, szbPatt, nPatt,  ixPatt+1,
                                  input,szbInput,nInput, ixInput+0,
                                  pIsStar,pIsQuery,pattEQinp,
-                                 inputCompleter) ) {
+                                 inputCompleter,haveInputInpC) ) {
             return True;
          }
          // but we can tail-recurse for the second call
@@ -179,7 +183,7 @@ Bool VG_(string_match) ( const HChar* patt, const HChar* input )
              patt,  sizeof(HChar), VG_(strlen)(patt), 0,
              input, sizeof(HChar), VG_(strlen)(input), 0,
              charIsStar, charIsQuery, char_p_EQ_i,
-             NULL
+             NULL, NULL
           );
 }
 
index be03d8b698dc0a52b58bc06943be615e4f3b8482..6e73e174b8a05ea8c92cb1e57b1589bcd5d9062f 100644 (file)
@@ -1450,13 +1450,20 @@ static void printIpDesc(UInt n, Addr ip, void* uu_opaque)
    
    static HChar buf[BUF_LEN];
 
-   VG_(describe_IP)(ip, buf, BUF_LEN);
+   InlIPCursor *iipc = VG_(new_IIPC)(ip);
 
-   if (VG_(clo_xml)) {
-      VG_(printf_xml)("    %s\n", buf);
-   } else {
-      VG_(message)(Vg_UserMsg, "   %s %s\n", ( n == 0 ? "at" : "by" ), buf);
-   }
+   do {
+      VG_(describe_IP)(ip, buf, BUF_LEN, iipc);
+      if (VG_(clo_xml)) {
+         VG_(printf_xml)("    %s\n", buf);
+      } else {
+         VG_(message)(Vg_UserMsg, "   %s %s\n", 
+                      ( n == 0 ? "at" : "by" ), buf);
+      }
+      n++; 
+      // Increase n to show "at" for only one level.
+   } while (VG_(next_IIPC)(iipc));
+   VG_(delete_IIPC)(iipc);
 }
 
 /* Print a StackTrace. */
index 81298c9aab64e3c8b881939561e6706e4588f9f7..697969ae4fe8485d79f8e8d14cec04b349a2d197 100644 (file)
@@ -89,9 +89,11 @@ extern
 Bool VG_(get_fnname_raw) ( Addr a, HChar* buf, Int nbuf );
 
 /* Like VG_(get_fnname), but without C++ demangling.  (But it does
- * Z-demangling and below-main renaming.) */
+ Z-demangling and below-main renaming.)
+ iipc argument: same usage as in VG_(describe_IP) in pub_tool_debuginfo.h. */
 extern
-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 );
 
 /* mips-linux only: find the offset of current address. This is needed for 
    stack unwinding for MIPS.
index 0f56a63c1bf7e173029d8385e9e861d6158011b5..d296929fe243a34e1a1671c952a1470e2d6e071c 100644 (file)
@@ -227,6 +227,8 @@ extern Int   VG_(clo_dump_error);
 extern const HChar* VG_(clo_sim_hints);
 /* Show symbols in the form 'name+offset' ?  Default: NO */
 extern Bool VG_(clo_sym_offsets);
+/* Read DWARF3 inline info ? */
+extern Bool VG_(clo_read_inline_info);
 /* Read DWARF3 variable info even if tool doesn't ask for it? */
 extern Bool VG_(clo_read_var_info);
 /* Which prefix to strip from full source file paths, if any. */
index b6be6dcd7f7f4b9e65fbb4223b557358475f7561..7c32aae618ea4a9de49021eb332fd3bad487922a 100644 (file)
@@ -1780,6 +1780,50 @@ need to use them.</para>
     </listitem>
   </varlistentry>
 
+  <varlistentry id="opt.read-inline-info" xreflabel="--read-inline-info">
+    <term>
+      <option><![CDATA[--read-inline-info=<yes|no> [default: no] ]]></option>
+    </term>
+    <listitem>
+      <para>When enabled, Valgrind will read information about
+      inlined function calls from DWARF3 debug info.
+      This slows Valgrind startup and makes it use more memory (typically
+      for each inlined piece of code, 6 words + the function name), but
+      it results in more descriptive stacktraces:</para>
+<programlisting><![CDATA[
+==15380== Conditional jump or move depends on uninitialised value(s)
+==15380==    at 0x80484EA: main (inlinfo.c:6)
+==15380== 
+==15380== Conditional jump or move depends on uninitialised value(s)
+==15380==    at 0x8048550: fun_noninline (inlinfo.c:6)
+==15380==    by 0x804850E: main (inlinfo.c:34)
+==15380== 
+==15380== Conditional jump or move depends on uninitialised value(s)
+==15380==    at 0x8048520: main (inlinfo.c:6)]]></programlisting>
+
+      <para>And here are the same errors with
+      <option>--read-inline-info=yes</option>:</para>
+
+<programlisting><![CDATA[
+==15377== Conditional jump or move depends on uninitialised value(s)
+==15377==    at 0x80484EA: fun_d (inlinfo.c:6)
+==15377==    by 0x80484EA: fun_c (inlinfo.c:14)
+==15377==    by 0x80484EA: fun_b (inlinfo.c:20)
+==15377==    by 0x80484EA: fun_a (inlinfo.c:26)
+==15377==    by 0x80484EA: main (inlinfo.c:33)
+==15377== 
+==15377== Conditional jump or move depends on uninitialised value(s)
+==15377==    at 0x8048550: fun_d (inlinfo.c:6)
+==15377==    by 0x8048550: fun_noninline (inlinfo.c:41)
+==15377==    by 0x804850E: main (inlinfo.c:34)
+==15377== 
+==15377== Conditional jump or move depends on uninitialised value(s)
+==15377==    at 0x8048520: fun_d (inlinfo.c:6)
+==15377==    by 0x8048520: main (inlinfo.c:35)
+]]></programlisting>
+    </listitem>
+  </varlistentry>
+
   <varlistentry id="opt.read-var-info" xreflabel="--read-var-info">
     <term>
       <option><![CDATA[--read-var-info=<yes|no> [default: no] ]]></option>
@@ -1787,36 +1831,39 @@ need to use them.</para>
     <listitem>
       <para>When enabled, Valgrind will read information about
       variable types and locations from DWARF3 debug info.
-      This slows Valgrind down and makes it use more memory, but for
-      the tools that can take advantage of it (Memcheck, Helgrind,
-      DRD) it can result in more precise error messages.  For example,
+      This slows Valgrind startup significantly and makes it use significantly
+      more memory, but for the tools that can take advantage of it (Memcheck,
+      Helgrind, DRD) it can result in more precise error messages.  For example,
       here are some standard errors issued by Memcheck:</para>
 <programlisting><![CDATA[
-==15516== Uninitialised byte(s) found during client check request
-==15516==    at 0x400633: croak (varinfo1.c:28)
-==15516==    by 0x4006B2: main (varinfo1.c:55)
-==15516==  Address 0x60103b is 7 bytes inside data symbol "global_i2"
-==15516== 
-==15516== Uninitialised byte(s) found during client check request
-==15516==    at 0x400633: croak (varinfo1.c:28)
-==15516==    by 0x4006BC: main (varinfo1.c:56)
-==15516==  Address 0x7fefffefc is on thread 1's stack]]></programlisting>
+==15363== Uninitialised byte(s) found during client check request
+==15363==    at 0x80484A9: croak (varinfo1.c:28)
+==15363==    by 0x8048544: main (varinfo1.c:55)
+==15363==  Address 0x80497f7 is 7 bytes inside data symbol "global_i2"
+==15363== 
+==15363== Uninitialised byte(s) found during client check request
+==15363==    at 0x80484A9: croak (varinfo1.c:28)
+==15363==    by 0x8048550: main (varinfo1.c:56)
+==15363==  Address 0xbea0d0cc is on thread 1's stack
+==15363==  in frame #1, created by main (varinfo1.c:45)
+></programlisting>
 
       <para>And here are the same errors with
       <option>--read-var-info=yes</option>:</para>
 
 <programlisting><![CDATA[
-==15522== Uninitialised byte(s) found during client check request
-==15522==    at 0x400633: croak (varinfo1.c:28)
-==15522==    by 0x4006B2: main (varinfo1.c:55)
-==15522==  Location 0x60103b is 0 bytes inside global_i2[7],
-==15522==  a global variable declared at varinfo1.c:41
-==15522== 
-==15522== Uninitialised byte(s) found during client check request
-==15522==    at 0x400633: croak (varinfo1.c:28)
-==15522==    by 0x4006BC: main (varinfo1.c:56)
-==15522==  Location 0x7fefffefc is 0 bytes inside local var "local"
-==15522==  declared at varinfo1.c:46, in frame #1 of thread 1]]></programlisting>
+==15370== Uninitialised byte(s) found during client check request
+==15370==    at 0x80484A9: croak (varinfo1.c:28)
+==15370==    by 0x8048544: main (varinfo1.c:55)
+==15370==  Location 0x80497f7 is 0 bytes inside global_i2[7],
+==15370==  a global variable declared at varinfo1.c:41
+==15370== 
+==15370== Uninitialised byte(s) found during client check request
+==15370==    at 0x80484A9: croak (varinfo1.c:28)
+==15370==    by 0x8048550: main (varinfo1.c:56)
+==15370==  Location 0xbeb4a0cc is 0 bytes inside local var "local"
+==15370==  declared at varinfo1.c:46, in frame #1 of thread 1
+]]></programlisting>
     </listitem>
   </varlistentry>
 
index ede860fbcc001fd8a979fa2bbe69f7c164371c09..4f7a09daebec77a4ca58d0343bace90934aa41fe 100644 (file)
@@ -120,14 +120,44 @@ Bool VG_(get_data_description)(
    It doesn't matter if debug info is present or not. */
 extern Bool VG_(get_objname)  ( Addr a, HChar* objname, Int n_objname );
 
+
+/* Cursor allowing to describe inlined function calls at an IP,
+   by doing successive calls to VG_(describe_IP). */
+typedef  struct _InlIPCursor InlIPCursor;
+
 /* Puts into 'buf' info about the code address %eip:  the address, function
    name (if known) and filename/line number (if known), like this:
 
       0x4001BF05: realloc (vg_replace_malloc.c:339)
 
    'n_buf' gives length of 'buf'.  Returns 'buf'.
+
+   eip can possibly corresponds to inlined function call(s).
+   To describe eip and the inlined function calls, the following must
+   be done:
+       InlIPCursor *iipc = VG_(new_IIPC)(eip);
+       do {
+          VG_(describe_IP)(eip, buf, n_buf, iipc);
+          ... use buf ...
+       } while (VG_(next_IIPC)(iipc));
+       VG_(delete_IIPC)(iipc);
+
+   To only describe eip, without the inlined calls at eip, give a NULL iipc:
+       VG_(describe_IP)(eip, buf, n_buf, NULL);   
 */
-extern HChar* VG_(describe_IP)(Addr eip, HChar* buf, Int n_buf);
+extern HChar* VG_(describe_IP)(Addr eip, HChar* buf, Int n_buf,
+                               InlIPCursor* iipc);
+
+/* Builds a IIPC (Inlined IP Cursor) to describe eip and all the inlined calls
+   at eip. Such a cursor must be deleted after use using VG_(delete_IIPC). */
+extern InlIPCursor* VG_(new_IIPC)(Addr eip);
+/* Move the cursor to the next call to describe.
+   Returns True if there are still calls to describe.
+   False if nothing to describe anymore. */
+extern Bool VG_(next_IIPC)(InlIPCursor *iipc);
+/* Free all memory associated with iipc. */
+extern void VG_(delete_IIPC)(InlIPCursor *iipc);
+
 
 
 /* Get an XArray of StackBlock which describe the stack (auto) blocks
index f3cb4b64c0bdb181fade3ba207a9d697dc7b574c..61f05cb9180b8814ebb5fbd998b4246f2e180781 100644 (file)
    elements each of size 'szbPatt'.  For the initial call, pass a
    value of zero to 'ixPatt'.
 
-   Ditto for input/nInput/szbInput/ixInput.
+   The input sequence can be similarly described using
+   input/nInput/szbInput/ixInput.
+   Alternatively, the input can be lazily constructed using an
+   inputCompleter. When using an inputCompleter, input/nInput/szbInput
+   are unused.
 
    pIsStar should return True iff the pointed-to pattern element is
    conceptually a '*'.
    (conceptually) '*' nor '?', so it must be a literal (in the sense
    that all the input sequence elements are literal).
 
-   input might be lazily constructed when pattEQinp is called.
+   If inputCompleter is not NULL, the input will be lazily constructed
+   when pattEQinp is called.
    For lazily constructing the input element, the two last arguments
    of pattEQinp are the inputCompleter and the index of the input
    element to complete.
-   inputCompleter can be NULL.
+   VG_(generic_match) calls (*haveInputInpC)(inputCompleter,ixInput) to
+   check if there is an element ixInput in the input sequence.
 */
 Bool VG_(generic_match) ( 
         Bool matchAll,
@@ -85,7 +91,8 @@ Bool VG_(generic_match) (
         Bool (*pIsStar)(const void*),
         Bool (*pIsQuery)(const void*),
         Bool (*pattEQinp)(const void*,const void*,void*,UWord),
-        void* inputCompleter
+        void* inputCompleter,
+        Bool (*haveInputInpC)(void*,UWord)
      );
 
 /* Mini-regexp function.  Searches for 'pat' in 'str'.  Supports
index d6a0d17d5bbb6e768f026aefd56b0e241a80142b..c594b323856bb03408b3f0b231f5018860b885be 100644 (file)
@@ -2174,7 +2174,7 @@ static void pp_snapshot_SXPt(Int fd, SXPt* sxpt, Int depth, HChar* depth_str,
          }
 
          // We need the -1 to get the line number right, But I'm not sure why.
-         ip_desc = VG_(describe_IP)(sxpt->Sig.ip-1, ip_desc_array, BUF_LEN);
+         ip_desc = VG_(describe_IP)(sxpt->Sig.ip-1, ip_desc_array, BUF_LEN, NULL);
       }
       
       // Do the non-ip_desc part first...
index 09ce552adff03f2814c9c8ca0021680ed9426256..1ae45c687583925d9897d45cf2a3fa4eca221c5e 100644 (file)
@@ -117,6 +117,9 @@ EXTRA_DIST = \
        holey_buffer_too_small.stderr.exp \
        inits.stderr.exp inits.vgtest \
        inline.stderr.exp inline.stdout.exp inline.vgtest \
+       inlinfo.stderr.exp inlinfo.stdout.exp inlinfo.vgtest \
+       inlinfosupp.stderr.exp inlinfosupp.stdout.exp inlinfosupp.supp inlinfosupp.vgtest \
+       inlinfosuppobj.stderr.exp inlinfosuppobj.stdout.exp inlinfosuppobj.supp inlinfosuppobj.vgtest \
        leak-0.vgtest leak-0.stderr.exp \
        leak-cases-full.vgtest leak-cases-full.stderr.exp \
        leak-cases-possible.vgtest leak-cases-possible.stderr.exp \
@@ -299,7 +302,7 @@ check_PROGRAMS = \
        err_disable1 err_disable2 err_disable3 err_disable4 \
        err_disable_arange1 \
        file_locking \
-       fprw fwrite inits inline \
+       fprw fwrite inits inline inlinfo \
        holey_buffer_too_small \
        leak-0 \
        leak-cases \
@@ -406,6 +409,8 @@ fprw_CFLAGS = $(AM_CFLAGS) @FLAG_W_NO_UNINITIALIZED@
 
 inits_CFLAGS = $(AM_CFLAGS) @FLAG_W_NO_UNINITIALIZED@
 
+inlinfo_CFLAGS = $(AM_CFLAGS) @FLAG_W_NO_UNINITIALIZED@
+
 long_namespace_xml_SOURCES = long_namespace_xml.cpp
 
 manuel1_CFLAGS = $(AM_CFLAGS) @FLAG_W_NO_UNINITIALIZED@
diff --git a/memcheck/tests/inlinfo.c b/memcheck/tests/inlinfo.c
new file mode 100644 (file)
index 0000000..2594dd0
--- /dev/null
@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <../memcheck.h>
+#define INLINE    inline __attribute__((always_inline))
+
+INLINE int fun_d(int argd) {
+   static int locd = 0;
+   if (argd > 0)
+      locd += argd;
+   return locd;
+}
+
+INLINE int fun_c(int argc) {
+   static int locc = 0;
+   locc += argc;
+   return fun_d(locc);
+}
+
+INLINE int fun_b(int argb) {
+   static int locb = 0;
+   locb += argb;
+   return fun_c(locb);
+}
+
+INLINE int fun_a(int arga) {
+   static int loca = 0;
+   loca += arga;
+   return fun_b(loca);
+}
+
+__attribute__((noinline))
+static int fun_noninline_m(int argm)
+{
+   return fun_d(argm);
+}
+
+__attribute__((noinline))
+static int fun_noninline_o(int argo)
+{
+   static int loco = 0;
+   if (argo > 0)
+      loco += argo;
+   return loco;
+}
+
+INLINE int fun_f(int argf) {
+   static int locf = 0;
+   locf += argf;
+   return fun_noninline_o(locf);
+}
+
+INLINE int fun_e(int arge) {
+   static int loce = 0;
+   loce += arge;
+   return fun_f(loce);
+}
+
+__attribute__((noinline))
+static int fun_noninline_n(int argn)
+{
+   return fun_e(argn);
+}
+
+
+int main() {
+   int result;
+   result = fun_a(result);
+   VALGRIND_MAKE_MEM_UNDEFINED(&result, sizeof(result));
+   result += fun_noninline_m(result);
+   VALGRIND_MAKE_MEM_UNDEFINED(&result, sizeof(result));
+   result += fun_d(result);
+   VALGRIND_MAKE_MEM_UNDEFINED(&result, sizeof(result));
+   result += fun_noninline_n(result);
+   return 0;
+}
+
diff --git a/memcheck/tests/inlinfo.stderr.exp b/memcheck/tests/inlinfo.stderr.exp
new file mode 100644 (file)
index 0000000..2a80040
--- /dev/null
@@ -0,0 +1,54 @@
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: fun_d (inlinfo.c:7)
+   by 0x........: fun_c (inlinfo.c:15)
+   by 0x........: fun_b (inlinfo.c:21)
+   by 0x........: fun_a (inlinfo.c:27)
+   by 0x........: main (inlinfo.c:66)
+
+{
+   <insert_a_suppression_name_here>
+   Memcheck:Cond
+   fun:fun_d
+   fun:fun_c
+   fun:fun_b
+   fun:fun_a
+   fun:main
+}
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: fun_d (inlinfo.c:7)
+   by 0x........: fun_noninline_m (inlinfo.c:33)
+   by 0x........: main (inlinfo.c:68)
+
+{
+   <insert_a_suppression_name_here>
+   Memcheck:Cond
+   fun:fun_d
+   fun:fun_noninline_m
+   fun:main
+}
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: fun_d (inlinfo.c:7)
+   by 0x........: main (inlinfo.c:70)
+
+{
+   <insert_a_suppression_name_here>
+   Memcheck:Cond
+   fun:fun_d
+   fun:main
+}
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: fun_noninline_o (inlinfo.c:40)
+   by 0x........: fun_f (inlinfo.c:48)
+   by 0x........: fun_e (inlinfo.c:54)
+   by 0x........: fun_noninline_n (inlinfo.c:60)
+   by 0x........: main (inlinfo.c:72)
+
+{
+   <insert_a_suppression_name_here>
+   Memcheck:Cond
+   fun:fun_noninline_o
+   fun:fun_f
+   fun:fun_e
+   fun:fun_noninline_n
+   fun:main
+}
diff --git a/memcheck/tests/inlinfo.stdout.exp b/memcheck/tests/inlinfo.stdout.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/memcheck/tests/inlinfo.vgtest b/memcheck/tests/inlinfo.vgtest
new file mode 100644 (file)
index 0000000..07f134d
--- /dev/null
@@ -0,0 +1,4 @@
+# test that the inlined function calls are properly shown in errors.
+# Also test the generation of suppression entries with inlined calls.
+prog: inlinfo
+vgopts: -q --read-inline-info=yes --gen-suppressions=all
diff --git a/memcheck/tests/inlinfosupp.stderr.exp b/memcheck/tests/inlinfosupp.stderr.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/memcheck/tests/inlinfosupp.stdout.exp b/memcheck/tests/inlinfosupp.stdout.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/memcheck/tests/inlinfosupp.supp b/memcheck/tests/inlinfosupp.supp
new file mode 100644 (file)
index 0000000..7374eb4
--- /dev/null
@@ -0,0 +1,31 @@
+{
+   main_a_b_c_d
+   Memcheck:Cond
+   fun:fun_d
+   fun:fun_c
+   fun:fun_b
+   fun:fun_a
+   fun:main
+}
+{
+   main_m_d
+   Memcheck:Cond
+   fun:fun_d
+   fun:fun_noninline_m
+   fun:main
+}
+{
+   main_d
+   Memcheck:Cond
+   fun:fun_d
+   fun:main
+}
+{
+   main_n_e_f_o
+   Memcheck:Cond
+   fun:fun_noninline_o
+   fun:fun_f
+   fun:fun_e
+   fun:fun_noninline_n
+   fun:main
+}
diff --git a/memcheck/tests/inlinfosupp.vgtest b/memcheck/tests/inlinfosupp.vgtest
new file mode 100644 (file)
index 0000000..f25098e
--- /dev/null
@@ -0,0 +1,3 @@
+# test suppressions with inlined fn calls.
+prog: inlinfo
+vgopts: -q --read-inline-info=yes --suppressions=inlinfosupp.supp
diff --git a/memcheck/tests/inlinfosuppobj.stderr.exp b/memcheck/tests/inlinfosuppobj.stderr.exp
new file mode 100644 (file)
index 0000000..42468c2
--- /dev/null
@@ -0,0 +1,7 @@
+Conditional jump or move depends on uninitialised value(s)
+   at 0x........: fun_d (inlinfo.c:7)
+   by 0x........: fun_c (inlinfo.c:15)
+   by 0x........: fun_b (inlinfo.c:21)
+   by 0x........: fun_a (inlinfo.c:27)
+   by 0x........: main (inlinfo.c:66)
+
diff --git a/memcheck/tests/inlinfosuppobj.stdout.exp b/memcheck/tests/inlinfosuppobj.stdout.exp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/memcheck/tests/inlinfosuppobj.supp b/memcheck/tests/inlinfosuppobj.supp
new file mode 100644 (file)
index 0000000..d47446b
--- /dev/null
@@ -0,0 +1,31 @@
+{
+   main_a_b_c_d_non_matching_obj_is_not_trucmuch
+   Memcheck:Cond
+   fun:fun_d
+   fun:fun_c
+   obj:trucmuch
+   fun:fun_a
+   fun:main
+}
+{
+   main_m_d
+   Memcheck:Cond
+   obj:*inlinfo
+   obj:*inlinfo
+   fun:main
+}
+{
+   main_d
+   Memcheck:Cond
+   obj:*inlinfo
+   fun:main
+}
+{
+   main_n_e_f_o
+   Memcheck:Cond
+   fun:fun_noninline_o
+   fun:fun_f
+   obj:*inlinfo
+   obj:*inlinfo
+   fun:main
+}
diff --git a/memcheck/tests/inlinfosuppobj.vgtest b/memcheck/tests/inlinfosuppobj.vgtest
new file mode 100644 (file)
index 0000000..5d9ca85
--- /dev/null
@@ -0,0 +1,4 @@
+# test suppressions with inlined fn calls, with some obj patterns
+prog: inlinfo
+vgopts: -q --read-inline-info=yes --suppressions=inlinfosuppobj.supp
+stderr_filter_args: inlinfo.c
index 35cfea1984e1d32eef9a4a572858c96d6325569f..aaa573f9f8d7b57fbd57a8f21cecf97509cb28cd 100644 (file)
@@ -75,6 +75,8 @@ usage: valgrind [options] prog-and-args
                               checks for self-modifying code: none, only for
                               code found in stacks, for all code, or for all
                               code except that from file-backed mappings
+    --read-inline-info=yes|no read debug info about inlined function calls
+                              and use it to do better stack traces [no]
     --read-var-info=yes|no    read debug info on stack and global variables
                               and use it to print better error messages in
                               tools that make use of it (Memcheck, Helgrind,
index 956a51c9b6b1f38a4204b8c67c218448fe0b5a75..be15370435fdef789d3044065bf1842c17455e4c 100644 (file)
@@ -75,6 +75,8 @@ usage: valgrind [options] prog-and-args
                               checks for self-modifying code: none, only for
                               code found in stacks, for all code, or for all
                               code except that from file-backed mappings
+    --read-inline-info=yes|no read debug info about inlined function calls
+                              and use it to do better stack traces [no]
     --read-var-info=yes|no    read debug info on stack and global variables
                               and use it to print better error messages in
                               tools that make use of it (Memcheck, Helgrind,