]> sourceware.org Git - systemtap.git/commitdiff
Add support for accessing enumerators
authorStan Cox <scox@redhat.com>
Fri, 5 Feb 2021 21:20:22 +0000 (16:20 -0500)
committerStan Cox <scox@redhat.com>
Fri, 5 Feb 2021 21:20:22 +0000 (16:20 -0500)
Adds dwarf_get_enum, similar to dwarf_getscopevar, except search for a matching
DW_TAG_enumerator.  If a scoped variable then use the enumerator die
and attributes; translate_location  will convert to a constant.  If a
struct member then translate_components will fold the component locations then
use translate_location.

dwflpp.cxx
dwflpp.h
testsuite/systemtap.base/enum.cxx [new file with mode: 0644]
testsuite/systemtap.base/enum.exp [new file with mode: 0644]
testsuite/systemtap.base/enum.stp [new file with mode: 0644]

index e8774eb2c3add3c44ff3c7c53ed65f2b9aba25a1..c0107bff75ada3fdc645303ef163eda2585163ca 100644 (file)
@@ -2770,6 +2770,39 @@ dwflpp::get_locals_die(Dwarf_Die& die, set<string>& locals)
 }
 
 
+int
+dwflpp::dwarf_get_enum (Dwarf_Die *scopes, int nscopes,
+                   const char *name, Dwarf_Die *result)
+{
+  // subprogram {<decls> enumeration_type {enumerator,...}}
+  // lexical block {<decls> enumeration_type {enumerator,...}}
+  // enumeration_type {enumerator,...}}
+  for (int out = 0; out < nscopes; ++out)
+    if (dwarf_haschildren (&scopes[out]) && dwarf_child (&scopes[out], result) == 0)
+      do
+        {
+          Dwarf_Die save_result = *result;
+          if (dwarf_tag (result) == DW_TAG_enumerator
+              || (dwarf_tag (result) == DW_TAG_enumeration_type
+                  && dwarf_child (result, result) == 0))
+            do
+              {
+                if (dwarf_tag (result) == DW_TAG_enumerator)
+                  {
+                    const char *diename = dwarf_diename (result);
+                    if (diename != NULL && !strcmp (name, diename))
+                      return out;
+                  }
+              }
+            while (dwarf_siblingof (result, result) == 0);
+          *result = save_result;
+        }
+      while (dwarf_siblingof (result, result) == 0);
+
+  return -1;
+}
+
+
 Dwarf_Attribute *
 dwflpp::find_variable_and_frame_base (vector<Dwarf_Die>& scopes,
                                       Dwarf_Addr pc,
@@ -2789,37 +2822,38 @@ dwflpp::find_variable_and_frame_base (vector<Dwarf_Die>& scopes,
                                            0, NULL, 0, 0,
                                            vardie);
   if (declaring_scope < 0)
-    {
-      // XXX: instead: add suggested locals and let a caller throw a single error
-      set<string> locals;
-      get_locals(scopes, locals);
-      string sugs = levenshtein_suggest(local, locals, 5);
-      if (pc)
-        throw SEMANTIC_ERROR (_F("unable to find local '%s', [man error::dwarf] dieoffset %s in %s, near pc %s %s %s %s (%s)",
-                                 local.c_str(),
-                                 lex_cast_hex(dwarf_dieoffset(scope_die)).c_str(),
-                                 module_name.c_str(),
-                                 lex_cast_hex(pc).c_str(),
-                                 (scope_die == NULL) ? "" : _("in"),
-                                 (dwarf_diename(scope_die) ?: "<unknown>"),
-                                 (dwarf_diename(cu) ?: "<unknown>"),
-                                 (sugs.empty()
-                                  ? (_("<no alternatives>"))
-                                 : (_("alternatives: ") + sugs + ")")).c_str()),
-                              e->tok);
-      else
-        throw SEMANTIC_ERROR (_F("unable to find global '%s', [man error::dwarf] dieoffset %s in %s, %s %s %s (%s)",
-                                 local.c_str(),
-                                 lex_cast_hex(dwarf_dieoffset(scope_die)).c_str(),
-                                 module_name.c_str(),
-                                 (scope_die == NULL) ? "" : _("in"),
-                                 (dwarf_diename(scope_die) ?: "<unknown>"),
-                                 cu_name().c_str(),
-                                 (sugs.empty()
-                                  ? (_("<no alternatives>"))
-                                 : (_("alternatives: ") + sugs + ")")).c_str()),
-                              e->tok);
-    }
+      if ((declaring_scope = dwarf_get_enum (&scopes[0], scopes.size(), local.c_str(), vardie)) < 0)
+        {
+          // XXX: instead: add suggested locals and let a caller throw a single error
+          set<string> locals;
+          get_locals(scopes, locals);
+          string sugs = levenshtein_suggest(local, locals, 5);
+          if (pc)
+            throw SEMANTIC_ERROR (_F("unable to find local '%s', [man error::dwarf] dieoffset %s in %s, near pc %s %s %s %s (%s)",
+                local.c_str(),
+                lex_cast_hex(dwarf_dieoffset(scope_die)).c_str(),
+                module_name.c_str(),
+                lex_cast_hex(pc).c_str(),
+                (scope_die == NULL) ? "" : _("in"),
+                    (dwarf_diename(scope_die) ?: "<unknown>"),
+                    (dwarf_diename(cu) ?: "<unknown>"),
+                    (sugs.empty()
+                        ? (_("<no alternatives>"))
+                            : (_("alternatives: ") + sugs + ")")).c_str()),
+                e->tok);
+          else
+            throw SEMANTIC_ERROR (_F("unable to find global '%s', [man error::dwarf] dieoffset %s in %s, %s %s %s (%s)",
+                local.c_str(),
+                lex_cast_hex(dwarf_dieoffset(scope_die)).c_str(),
+                module_name.c_str(),
+                (scope_die == NULL) ? "" : _("in"),
+                    (dwarf_diename(scope_die) ?: "<unknown>"),
+                    cu_name().c_str(),
+                    (sugs.empty()
+                        ? (_("<no alternatives>"))
+                            : (_("alternatives: ") + sugs + ")")).c_str()),
+                e->tok);
+        }
 
   *funcdie = scopes[declaring_scope];
 
@@ -3377,7 +3411,8 @@ dwflpp::find_struct_member(const target_symbol::component& c,
               && find_struct_member(c, &import, memberdie, dies, locs))
             goto success;
 
-          if (tag != DW_TAG_member && tag != DW_TAG_inheritance)
+          if (tag != DW_TAG_member && tag != DW_TAG_inheritance
+              && tag != DW_TAG_enumeration_type)
             continue;
 
           const char *name = dwarf_diename(&die);
@@ -3388,6 +3423,20 @@ dwflpp::find_struct_member(const target_symbol::component& c,
               if (dwarf_attr_die (&die, DW_AT_type, &inheritee))
                 inheritees.push_back(inheritee);
             }
+          else if (tag == DW_TAG_enumeration_type)
+            {
+              Dwarf_Die enum_item;
+              if (dwarf_get_enum (&die, 1, c.member.c_str(), &enum_item) >= 0
+                  && dwarf_attr_integrate (&enum_item, DW_AT_const_value, &attr))
+                {
+                  /* We have a matching enum so use its die and attr */
+                  *parentdie = die;
+                  *memberdie = enum_item;
+                  dies.insert(dies.begin(), enum_item);
+                  locs.insert(locs.begin(), attr);
+                  goto success;
+                }
+            }
           else if (name == NULL)
             {
               /* Need to recurse for anonymous structs/unions. */
@@ -3417,8 +3466,10 @@ success:
     }
 
   /* Union members don't usually have a location,
-   * but just use the containing union's location.  */
-  else if (dwarf_tag(parentdie) != DW_TAG_union_type)
+   * but just use the containing union's location.
+   * Enumerators don't have a location, treat as a constant. */
+  else if (dwarf_tag(parentdie) != DW_TAG_union_type
+           && dwarf_tag(&die) != DW_TAG_enumeration_type)
     throw SEMANTIC_ERROR (_F("no location for field '%s':%s",
                              c.member.c_str(), dwarf_errmsg(-1)), c.tok);
 
@@ -3544,6 +3595,16 @@ dwflpp::translate_components(location_context *ctx,
                                           sugs.c_str()), c.tok);
                 }
 
+              if (dwarf_tag (vardie) == DW_TAG_enumerator)
+                {
+                  location *n = ctx->locations.back();
+                  /* Fold all locations except this enum constant */
+                  ctx->locations.erase(ctx->locations.begin(), ctx->locations.end()-1);
+                  n = translate_location (ctx, &locs[0], &dies[0],
+                      pc, NULL, e, n);
+                  ctx->locations.push_back(n);
+                  return;
+                }
              if (!ctx->locations.empty())
                {
                  location *n = ctx->locations.back();
@@ -3783,6 +3844,10 @@ dwflpp::translate_final_fetch_or_store (location_context &ctx,
 {
   const target_symbol *e = ctx.e;
 
+  if (dwarf_tag (vardie) == DW_TAG_enumerator)
+    /* translate_location has already handled the enum constant */
+    return;
+
   /* First boil away any qualifiers associated with the type DIE of
      the final location to be accessed.  */
   resolve_unqualified_inner_typedie (start_typedie, typedie, e);
@@ -4087,7 +4152,8 @@ dwflpp::literal_stmt_for_local (location_context &ctx,
   /* Translate the ->bar->baz[NN] parts. */
 
   Dwarf_Die typedie;
-  if (dwarf_attr_die (&vardie, DW_AT_type, &typedie) == NULL)
+  if (dwarf_attr_die (&vardie, DW_AT_type, &typedie) == NULL
+      && dwarf_tag (&vardie) != DW_TAG_enumerator)
     {
       string msg = _F("failed to retrieve type attribute for '%s' [man error::dwarf]", local.c_str());
       semantic_error err(ERR_SRC, msg, e->tok);
index 66dc1f09ecd1e17e9be82832c8b033eda0f743fd..d31ceae0c90e893756e4baaf86db00355ce710e8 100644 (file)
--- a/dwflpp.h
+++ b/dwflpp.h
@@ -620,6 +620,8 @@ private:
   // target variables.
   void emit_address (Dwarf_Addr address);
 
+  int  dwarf_get_enum (Dwarf_Die *scopes, int nscopes,
+                       const char *name, Dwarf_Die *result);
   void get_locals(std::vector<Dwarf_Die>& scopes, std::set<std::string>& locals);
   void get_locals_die(Dwarf_Die &die, std::set<std::string>& locals);
   void get_members(Dwarf_Die *vardie, std::set<std::string>& members,
diff --git a/testsuite/systemtap.base/enum.cxx b/testsuite/systemtap.base/enum.cxx
new file mode 100644 (file)
index 0000000..04adde9
--- /dev/null
@@ -0,0 +1,54 @@
+#ifdef SHARED
+
+enum countries {canada,usa,mexico};
+enum countries country = mexico;
+
+void
+test_enum (int arg1, int arg2)
+{
+}
+
+#else
+void test_enum (int arg1, int arg2);
+
+enum caps {A='A', B='B', C='C'};
+enum caps cap = C;
+
+enum sixteen_powers {sixteen_power_0=0,sixteen_power_1=16,sixteen_power_2=256,
+  sixteen_power_3=4096,sixteen_power_4=65536, sixteen_power_5=1048576,
+  sixteen_power_6=16777216,sixteen_power_7=268435456,sixteen_power_8=4294967296
+  };
+enum sixteen_powers sixteen_power = sixteen_power_8;
+
+int
+main ()
+{
+  enum enumdigits {one=1,two=2,three=3};
+
+  typedef enum {a='a',b='b',c='c'} letters;
+  letters letter = a;
+
+  typedef enum {ten=10,twenty=20,thirty=30} linears;
+  linears linear = twenty;
+
+  typedef enum { at='@',sharp='#' } symbols;
+  symbols symbol = at;
+
+  struct 
+  {
+    enum ordinals {first=1,second=2,third=3};
+    enum ordinals ordinal = first;
+  } astruct;
+
+  {
+    enum ordinals {first=1,second=2,third=3,fourth=4};
+    enum colors {orange=1,green=2,yellow=3};
+  END_BLOCK:
+    test_enum (orange, fourth);
+  }
+      
+ RETURN:
+  test_enum (astruct.first, astruct.second);
+}
+
+#endif
diff --git a/testsuite/systemtap.base/enum.exp b/testsuite/systemtap.base/enum.exp
new file mode 100644 (file)
index 0000000..f5c2f66
--- /dev/null
@@ -0,0 +1,47 @@
+set test "enum"
+set stap_path $env(SYSTEMTAP_PATH)/stap
+
+# exp_internal 1
+
+set xflags "additional_flags=-g"
+set xflags "$xflags additional_flags=-L[pwd] additional_flags=[pwd]/libenum.so"
+set xflags "$xflags additional_flags=-Wl,-rpath-link,[pwd]"
+set exepath "[pwd]/enum.x"
+set soflags "additional_flags=-shared"
+set soflags "$soflags additional_flags=-g"
+set soflags "$soflags additional_flags=-fPIC"
+set soflags "$soflags additional_flags=-DSHARED"
+set sopath "[pwd]/libenum.so"
+set reso [target_compile $srcdir/$subdir/enum.cxx $sopath \
+             executable $soflags ]
+set res [target_compile $srcdir/$subdir/enum.cxx $exepath \
+             executable $xflags ]
+if {$res != ""} {
+    fail "$test compiling enum.c"
+    return
+} else {
+    pass "$test compiling enum.c"
+}
+
+foreach runtime [get_runtime_list] {
+    if {$runtime != ""} {
+       set test_type "$test ($runtime)"
+    } else {
+       set runtime "kernel"
+       set test_type $test
+    }
+
+    set ok 0
+    spawn $stap_path --runtime=$runtime -g $srcdir/$subdir/enum.stp -c $exepath $sopath
+    expect {
+       -timeout 180
+       -re "(\"sixteen_power_8\" is 0x100000000|\"C\" is C|\"green\" is 2|\"fourth\" is 4|\"c\" is c|\"sharp\" is #|\"astruct->third\" is 3|\"astruct->ordinal\" is 1|\"mexico\" is 2)\r\n" { incr ok; exp_continue }
+       eof { }
+    }
+    catch {close}; catch {wait}
+    if {$ok == 10} {
+       pass "$test $runtime"
+    } else {
+       fail "$test $runtime ($ok/10)"
+    }
+}
diff --git a/testsuite/systemtap.base/enum.stp b/testsuite/systemtap.base/enum.stp
new file mode 100644 (file)
index 0000000..5a88b06
--- /dev/null
@@ -0,0 +1,24 @@
+probe process.function("main") 
+{
+  /*        file scope                   file scope */
+  printf("\"sixteen_power_8\" is %#lx\n\"C\" is %c\n", $sixteen_power_8, $C)
+}
+
+probe process.function("main").label("RETURN") 
+{
+  /* func block scope    func block scope struct scope              struct scope */
+  printf("\"c\" is %c\n\"sharp\" is %c\n\"astruct->third\" is %d\n\"astruct->ordinal\" is %d\n", 
+        $c, $sharp, $astruct->third, $astruct->ordinal)
+} 
+
+probe process.function("main").label("END_BLOCK") 
+{
+  /* lex block scope         lex block scope */
+  printf("\"green\" is %d\n\"fourth\" is %d\n", $green,$fourth)
+}
+
+probe process(@1).function("test_enum") 
+{
+  /* extern so file scope */
+  printf("\"mexico\" is %d\n",@var("mexico",@1))
+}
This page took 0.039825 seconds and 5 git commands to generate.