]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
This optimisation divides by 2.5 the time (user+sys) needed to read
authorPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Sat, 21 Jun 2014 10:57:33 +0000 (10:57 +0000)
committerPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Sat, 21 Jun 2014 10:57:33 +0000 (10:57 +0000)
the inlined info of a big executable.
On a slow pentium, reading the inline info now takes 5.5 seconds.

The optimisation consists in having per dw3 abbreviation a structure
allowing to skip efficiently the non interesting DIEs (i.e. the DIEs
the parse_inl_DIE is not interested in).
Mostly, the idea is to avoid calling the image abstraction, and replace
this by just advancing the cursor (i.e. addition rather than a bunch
of function calls to read the data).

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

coregrind/m_debuginfo/readdwarf3.c

index d16999b134a01bfd714c140324498fbd25e16061..b450f3e97a80aa2bd06ea0db30e5300f8df7736a 100644 (file)
@@ -217,6 +217,10 @@ static inline void set_position_of_Cursor ( Cursor* c, ULong pos ) {
    c->sli_next = c->sli.ioff + pos;
    vg_assert(is_sane_Cursor(c));
 }
+static inline void advance_position_of_Cursor ( Cursor* c, ULong delta ) {
+   c->sli_next += delta;
+   vg_assert(is_sane_Cursor(c));
+}
 
 static /*signed*/Long get_remaining_length_Cursor ( Cursor* c ) {
    vg_assert(is_sane_Cursor(c));
@@ -380,9 +384,24 @@ static ULong get_Initial_Length ( /*OUT*/Bool* is64,
 
 typedef
    struct _name_form {
-      ULong at_name;
-      ULong at_form;
+      ULong at_name;  // Dwarf Attribute name
+      ULong at_form;  // Dward Attribute form
+      UInt  skip_szB; // Nr of bytes skippable from here ...
+      UInt  next_nf;  // ... to reach this attr/form index in the g_abbv.nf
    } name_form;
+/* skip_szB and n_nf are used to optimise the skipping of uninteresting DIEs.
+   Each name_form maintains how many (fixed) nr of bytes can be skipped from
+   the beginning of this form till the next attr/form to look at.
+   The next form to look can be:
+       an 'interesting' attr/form to read while skipping a DIE
+          (currently, this is only DW_AT_sibling)
+   or
+       a variable length form which must be read to be skipped.
+   For a variable length form, the skip_szB will be equal to VARSZ_FORM.
+
+   Note: this technique could also be used to speed up the parsing
+   of DIEs : for each parser kind, we could have the nr of bytes
+   to skip to directly reach the interesting form(s) for the parser. */
 
 typedef
    struct _g_abbv {
@@ -392,7 +411,9 @@ typedef
       ULong  has_children;
       name_form nf[0];
       /* Variable-length array of name/form pairs, terminated
-         by a 0/0 pair. */
+         by a 0/0 pair.
+         The skip_szB/next_nf allows to skip efficiently a DIE
+         described by this g_abbv; */
     } g_abbv;
 
 /* Holds information that is constant through the parsing of a
@@ -865,6 +886,9 @@ static XArray* /* of AddrRange */
    return xa;
 }
 
+#define VARSZ_FORM 0xffffffff
+static UInt get_Form_szB (CUConst* cc, DW_FORM form );
+
 /* Initialises the hash table of abbreviations.
    We do a single scan of the abbv slice to parse and
    build all abbreviations, for the following reasons:
@@ -880,7 +904,8 @@ static void init_ht_abbvs (CUConst* cc,
    g_abbv *ta; // temporary abbreviation, reallocated if needed.
    UInt ta_nf_maxE; // max nr of pairs in ta.nf[], doubled when reallocated.
    UInt ta_nf_n;    // nr of pairs in ta->nf that are initialised.
-   g_abbv *ht_ta; // abbv to insert in hash table. 
+   g_abbv *ht_ta; // abbv to insert in hash table.
+   Int i;
 
    #define SZ_G_ABBV(_nf_szE) (sizeof(g_abbv) + _nf_szE * sizeof(name_form))
 
@@ -914,13 +939,42 @@ static void init_ht_abbvs (CUConst* cc,
          }
         ta_nf_n++;
       }
+
+      // Initialises the skip_szB/next_nf elements : an element at position
+      // i must contain the sum of its own size + the sizes of all elements
+      // following i till either the next variable size element, the next
+      // sibling element or the end of the DIE.
+      ta->nf[ta_nf_n - 1].skip_szB = 0;
+      ta->nf[ta_nf_n - 1].next_nf = 0;
+      for (i = ta_nf_n - 2; i >= 0; i--) {
+         const UInt form_szB = get_Form_szB (cc, (DW_FORM)ta->nf[i].at_form);
+          
+         if (ta->nf[i+1].at_name == DW_AT_sibling
+             || ta->nf[i+1].skip_szB == VARSZ_FORM) {
+            ta->nf[i].skip_szB = form_szB;
+            ta->nf[i].next_nf  = i+1;
+         } else if (form_szB == VARSZ_FORM) {
+            ta->nf[i].skip_szB = form_szB;
+            ta->nf[i].next_nf  = i+1;
+         } else {
+            ta->nf[i].skip_szB = ta->nf[i+1].skip_szB + form_szB;
+            ta->nf[i].next_nf  = ta->nf[i+1].next_nf;
+         }
+      }
+
       ht_ta = ML_(dinfo_zalloc) ("di.readdwarf3.ht_ta", SZ_G_ABBV(ta_nf_n));
       VG_(memcpy) (ht_ta, ta, SZ_G_ABBV(ta_nf_n));
       VG_(HT_add_node) ( cc->ht_abbvs, ht_ta );
-      TRACE_D3("  Adding abbv_code %llu TAG  %s [%s] nf %d\n",
-               (ULong) ht_ta->abbv_code, ML_(pp_DW_TAG)(ht_ta->atag),
-               ML_(pp_DW_children)(ht_ta->has_children),
-               ta_nf_n);
+      if (TD3) {
+         TRACE_D3("  Adding abbv_code %llu TAG  %s [%s] nf %d ",
+                  (ULong) ht_ta->abbv_code, ML_(pp_DW_TAG)(ht_ta->atag),
+                  ML_(pp_DW_children)(ht_ta->has_children),
+                  ta_nf_n);
+         TRACE_D3("  ");
+         for (i = 0; i < ta_nf_n; i++)
+            TRACE_D3("[%u,%u] ", ta->nf[i].skip_szB, ta->nf[i].next_nf);
+         TRACE_D3("\n");
+      }
    }
 
    ML_(dinfo_free) (ta);
@@ -1082,6 +1136,9 @@ void get_Form_contents ( /*OUT*/FormContents* cts,
                          Bool td3, DW_FORM form )
 {
    VG_(bzero_inline)(cts, sizeof(*cts));
+   // !!! keep switch in sync with get_Form_szB. The nr of characters read below
+   // must be computed similarly in get_Form_szB.
+   // The consistency is verified in trace_DIE.
    switch (form) {
       case DW_FORM_data1:
          cts->u.val = (ULong)(UChar)get_UChar(c);
@@ -1372,6 +1429,119 @@ void get_Form_contents ( /*OUT*/FormContents* cts,
    }
 }
 
+static inline UInt sizeof_Dwarfish_UWord (Bool is_dw64)
+{
+   if (is_dw64)
+      return sizeof(ULong);
+   else
+      return sizeof(UInt);
+}
+
+#define VARSZ_FORM 0xffffffff
+/* If the form is a fixed length form, return the nr of bytes for this form.
+   If the form is a variable length form, return VARSZ_FORM. */
+static
+UInt get_Form_szB (CUConst* cc, DW_FORM form )
+{
+   // !!! keep switch in sync with get_Form_contents : the nr of bytes
+   // read from a cursor by get_Form_contents must be returned by
+   // the below switch.
+   // The consistency is verified in trace_DIE.
+   switch (form) {
+      case DW_FORM_data1: return 1;
+      case DW_FORM_data2: return 2;
+      case DW_FORM_data4: return 4;
+      case DW_FORM_data8: return 8;
+      case DW_FORM_sec_offset:
+         if (cc->is_dw64)
+            return 8;
+         else
+            return 4;
+      case DW_FORM_sdata:
+         return VARSZ_FORM;
+      case DW_FORM_udata:
+         return VARSZ_FORM;
+      case DW_FORM_addr: // See hack in get_Form_contents
+         return sizeof(UWord);
+      case DW_FORM_ref_addr: // See hack in get_Form_contents
+         if (cc->version == 2)
+            return sizeof(UWord);
+         else 
+            return sizeof_Dwarfish_UWord (cc->is_dw64);
+      case DW_FORM_strp:
+         return sizeof_Dwarfish_UWord (cc->is_dw64);
+      case DW_FORM_string: 
+         return VARSZ_FORM;
+      case DW_FORM_ref1:
+         return 1;
+      case DW_FORM_ref2:
+         return 2;
+      case DW_FORM_ref4:
+         return 4;
+      case DW_FORM_ref8:
+         return 8;
+      case DW_FORM_ref_udata:
+         return VARSZ_FORM;
+      case DW_FORM_flag: 
+         return 1;
+      case DW_FORM_flag_present:
+         return 0; // !!! special case, no data.
+      case DW_FORM_block1:
+         return VARSZ_FORM;
+      case DW_FORM_block2:
+         return VARSZ_FORM;
+      case DW_FORM_block4:
+         return VARSZ_FORM;
+      case DW_FORM_exprloc:
+      case DW_FORM_block:
+         return VARSZ_FORM;
+      case DW_FORM_ref_sig8:
+         return 8 + 8;
+      case DW_FORM_indirect:
+         return VARSZ_FORM;
+      case DW_FORM_GNU_ref_alt:
+         return sizeof_Dwarfish_UWord(cc->is_dw64);
+      case DW_FORM_GNU_strp_alt:
+         return sizeof_Dwarfish_UWord(cc->is_dw64);
+      default:
+         VG_(printf)(
+            "get_Form_szB: unhandled %d (%s)\n",
+            form, ML_(pp_DW_FORM)(form));
+         cc->barf("get_Form_contents: unhandled DW_FORM");
+   }
+}
+
+/* Skip a DIE as described by abbv.
+   If the DIE has a sibling, *sibling is set to the skipped DIE sibling value. */
+static
+void skip_DIE (UWord  *sibling,
+               Cursor* c_die,
+               g_abbv *abbv,
+               CUConst* cc)
+{
+   UInt nf_i;
+   FormContents cts;
+   nf_i = 0;
+   while (True) {
+      if (abbv->nf[nf_i].at_name == DW_AT_sibling) {
+         get_Form_contents( &cts, cc, c_die, False /*td3*/,
+                            (DW_FORM)abbv->nf[nf_i].at_form );
+         if ( cts.szB > 0 ) 
+            *sibling = cts.u.val;
+         nf_i++;
+      } else if (abbv->nf[nf_i].skip_szB == VARSZ_FORM) {
+         get_Form_contents( &cts, cc, c_die, False /*td3*/,
+                            (DW_FORM)abbv->nf[nf_i].at_form );
+         nf_i++;
+      } else {
+         advance_position_of_Cursor (c_die, (ULong)abbv->nf[nf_i].skip_szB);
+         nf_i = abbv->nf[nf_i].next_nf;
+      }
+      if (nf_i == 0)
+         break;
+   }
+}
+
 
 /*------------------------------------------------------------*/
 /*---                                                      ---*/
@@ -1586,8 +1756,8 @@ void read_filename_table( /*MOD*/XArray* /* of UChar* */ filenameTable,
                 "Overrun whilst reading .debug_line section(1)" );
 
    /* unit_length = */
-      get_Initial_Length( &is_dw64, &c,
-           "read_filename_table: invalid initial-length field" );
+   get_Initial_Length( &is_dw64, &c,
+                       "read_filename_table: invalid initial-length field" );
    version = get_UShort( &c );
    if (version != 2 && version != 3 && version != 4)
      cc->barf("read_filename_table: Only DWARF version 2, 3 and 4 line info "
@@ -1685,9 +1855,12 @@ static void trace_DIE(
 {
    Cursor c;
    FormContents cts;
+   UWord sibling = 0;
    UInt nf_i;
    Bool  debug_types_flag;
    Bool  alt_flag;
+   Cursor check_skip;
+   UWord check_sibling = 0;
 
    posn = uncook_die( cc, posn, &debug_types_flag, &alt_flag );
    init_Cursor (&c, 
@@ -1695,6 +1868,7 @@ static void trace_DIE(
                 alt_flag ? cc->escn_debug_info_alt : cc->escn_debug_info,
                 saved_die_c_offset, cc->barf, 
                 "Overrun trace_DIE");
+   check_skip = c;
    VG_(printf)(" <%d><%lx>: Abbrev Number: %llu (%s)%s%s\n",
                level, posn, (ULong) abbv->abbv_code, ML_(pp_DW_TAG)( dtag ),
                debug_types_flag ? " (in .debug_types)" : "",
@@ -1708,8 +1882,22 @@ static void trace_DIE(
       VG_(printf)("     %18s: ", ML_(pp_DW_AT)(attr));
       /* Get the form contents, so as to print them */
       get_Form_contents( &cts, cc, &c, True, form );
+      if (attr == DW_AT_sibling && cts.szB > 0) {
+         sibling = cts.u.val;
+      }
       VG_(printf)("\t\n");
    }
+
+   /* Verify that skipping a DIE gives the same displacement as
+      tracing (i.e. reading) a DIE. If there is an inconsistency in
+      the nr of bytes read by get_Form_contents and get_Form_szB, this
+      should be detected by the below. Using --trace-symtab=yes
+      --read-var-info=yes will ensure all DIEs are systematically
+      verified. */
+   skip_DIE (&check_sibling, &check_skip, abbv, cc);
+   vg_assert (check_sibling == sibling);
+   vg_assert (get_position_of_Cursor (&check_skip) 
+              == get_position_of_Cursor (&c));
 }
 
 __attribute__((noreturn))
@@ -3823,26 +4011,11 @@ static void read_DIE (
       // DIE was read by a parser above, so we know where the DIE ends.
       set_position_of_Cursor( c, after_die_c_offset );
    } else {
-      /* No parser has parsed this DIE. So, we need to read the DIE
-         to skip its data, in order to read the next DIE.
+      /* No parser has parsed this DIE. So, we need to skip the DIE,
+         in order to read the next DIE.
          At the same time, establish sibling value if the DIE has one. */
-      UInt nf_i;
-
-      TRACE_D3("    (skipped DIE)\n");
-      nf_i = 0;
-      while (True) {
-         FormContents cts;
-         ULong at_name = abbv->nf[nf_i].at_name;
-         ULong at_form = abbv->nf[nf_i].at_form;
-         nf_i++;
-         if (at_name == 0 && at_form == 0) break;
-         /* Get the form contents, but ignore them; the only purpose is
-            to skip the data or get the DIE sibling, if it has one. */
-         get_Form_contents( &cts, cc, c, False /*td3*/, (DW_FORM)at_form );
-         if (UNLIKELY(at_name == DW_AT_sibling && cts.szB > 0)) {
-            sibling = cts.u.val;
-         }
-      }
+      TRACE_D3("    uninteresting DIE -> skipping ...\n");
+      skip_DIE (&sibling, c, abbv, cc);
    }
 
    /* --- Now recurse into its children, if any