Skip to content

Conversation

@Michael137
Copy link
Member

This patch adds a new option --child-tags (-t for short), which makes dwarfdump only dump children whose DWARF tag is in the list of tags specified by the user.

Motivating examples are:

  • dumping all global variables in a CU
  • dumping all non-static data members of a structure
  • dumping all module import declarations of a CU
  • etc.

For tags not known to dwarfdump, we pretend that the tag wasn't specified.

Note, this flag only takes effect when --show-children is set (either explicitly or implicitly). We error out when trying to use the flag without dumping children.

Example:

$ builds/release/bin/llvm-dwarfdump -t DW_TAG_structure_type a.out.dSYM                 
...
0x0000000c: DW_TAG_compile_unit
              DW_AT_producer    ("clang version 22.0.0git (git@github.com:Michael137/llvm-project.git 737da3347c2fb01dd403420cf83e9b8fbea32618)")
              DW_AT_language    (DW_LANG_C11)
              ...
0x0000002a:   DW_TAG_structure_type
                DW_AT_APPLE_block       (true)
                DW_AT_byte_size (0x20)

0x00000067:   DW_TAG_structure_type
                DW_AT_APPLE_block       (true)
                DW_AT_name      ("__block_descriptor")
                DW_AT_byte_size (0x10)
...
$ builds/release/bin/llvm-dwarfdump -t DW_TAG_structure_type -t DW_TAG_member a.out.dSYM
...
0x0000000c: DW_TAG_compile_unit
              DW_AT_producer    ("clang version 22.0.0git (git@github.com:Michael137/llvm-project.git 737da3347c2fb01dd403420cf83e9b8fbea32618)")
              DW_AT_language    (DW_LANG_C11)                                                             
              DW_AT_name        ("macro.c")                                                               
              ...                                              
0x0000002a:   DW_TAG_structure_type                                                                       
                DW_AT_APPLE_block       (true)                                                            
                DW_AT_byte_size (0x20)
                                                     
0x0000002c:     DW_TAG_member                 
                  DW_AT_name    ("__isa")                                                                 
                  DW_AT_type    (0x00000051 "void *")                                                     
                  DW_AT_data_member_location    (0x00)
                                                                                                          
0x00000033:     DW_TAG_member                                                                             
                  DW_AT_name    ("__flags")                                                               
                  DW_AT_type    (0x00000052 "int")                                                        
                  DW_AT_data_member_location    (0x08)
                                                                                                          
0x0000003a:     DW_TAG_member                                                                             
                  DW_AT_name    ("__reserved")
                  DW_AT_type    (0x00000052 "int")                                                        
                  DW_AT_data_member_location    (0x0c)                                                    

0x00000041:     DW_TAG_member
                  DW_AT_name    ("__FuncPtr")   
                  DW_AT_type    (0x00000056 "void (*)(int)")                                              
                  DW_AT_data_member_location    (0x10)                                         
                                                     
0x00000048:     DW_TAG_member                                                                             
                  DW_AT_name    ("__descriptor")                                                          
                  DW_AT_type    (0x00000062 "__block_descriptor *")
                  DW_AT_alignment       (8)
                  DW_AT_data_member_location    (0x18)                                                    
                                                     
0x00000067:   DW_TAG_structure_type                                                                       
                DW_AT_APPLE_block       (true)
                DW_AT_name      ("__block_descriptor")
                DW_AT_byte_size (0x10)                                                                    
                                                                                                          
0x0000006a:     DW_TAG_member              
                  DW_AT_name    ("reserved")                                                              
                  DW_AT_type    (0x00000079 "unsigned long")
                  DW_AT_data_member_location    (0x00)                                         
                                                     
0x00000071:     DW_TAG_member                                                                             
                  DW_AT_name    ("Size")                                                                  
                  DW_AT_type    (0x00000079 "unsigned long")
                  DW_AT_data_member_location    (0x08)             
...                                       

@llvmbot
Copy link
Member

llvmbot commented Oct 30, 2025

@llvm/pr-subscribers-llvm-binary-utilities

@llvm/pr-subscribers-debuginfo

Author: Michael Buch (Michael137)

Changes

This patch adds a new option --child-tags (-t for short), which makes dwarfdump only dump children whose DWARF tag is in the list of tags specified by the user.

Motivating examples are:

  • dumping all global variables in a CU
  • dumping all non-static data members of a structure
  • dumping all module import declarations of a CU
  • etc.

For tags not known to dwarfdump, we pretend that the tag wasn't specified.

Note, this flag only takes effect when --show-children is set (either explicitly or implicitly). We error out when trying to use the flag without dumping children.

Example:

$ builds/release/bin/llvm-dwarfdump -t DW_TAG_structure_type a.out.dSYM                 
...
0x0000000c: DW_TAG_compile_unit
              DW_AT_producer    ("clang version 22.0.0git (git@<!-- -->github.com:Michael137/llvm-project.git 737da3347c2fb01dd403420cf83e9b8fbea32618)")
              DW_AT_language    (DW_LANG_C11)
              ...
0x0000002a:   DW_TAG_structure_type
                DW_AT_APPLE_block       (true)
                DW_AT_byte_size (0x20)

0x00000067:   DW_TAG_structure_type
                DW_AT_APPLE_block       (true)
                DW_AT_name      ("__block_descriptor")
                DW_AT_byte_size (0x10)
...
$ builds/release/bin/llvm-dwarfdump -t DW_TAG_structure_type -t DW_TAG_member a.out.dSYM
...
0x0000000c: DW_TAG_compile_unit
              DW_AT_producer    ("clang version 22.0.0git (git@<!-- -->github.com:Michael137/llvm-project.git 737da3347c2fb01dd403420cf83e9b8fbea32618)")
              DW_AT_language    (DW_LANG_C11)                                                             
              DW_AT_name        ("macro.c")                                                               
              ...                                              
0x0000002a:   DW_TAG_structure_type                                                                       
                DW_AT_APPLE_block       (true)                                                            
                DW_AT_byte_size (0x20)
                                                     
0x0000002c:     DW_TAG_member                 
                  DW_AT_name    ("__isa")                                                                 
                  DW_AT_type    (0x00000051 "void *")                                                     
                  DW_AT_data_member_location    (0x00)
                                                                                                          
0x00000033:     DW_TAG_member                                                                             
                  DW_AT_name    ("__flags")                                                               
                  DW_AT_type    (0x00000052 "int")                                                        
                  DW_AT_data_member_location    (0x08)
                                                                                                          
0x0000003a:     DW_TAG_member                                                                             
                  DW_AT_name    ("__reserved")
                  DW_AT_type    (0x00000052 "int")                                                        
                  DW_AT_data_member_location    (0x0c)                                                    

0x00000041:     DW_TAG_member
                  DW_AT_name    ("__FuncPtr")   
                  DW_AT_type    (0x00000056 "void (*)(int)")                                              
                  DW_AT_data_member_location    (0x10)                                         
                                                     
0x00000048:     DW_TAG_member                                                                             
                  DW_AT_name    ("__descriptor")                                                          
                  DW_AT_type    (0x00000062 "__block_descriptor *")
                  DW_AT_alignment       (8)
                  DW_AT_data_member_location    (0x18)                                                    
                                                     
0x00000067:   DW_TAG_structure_type                                                                       
                DW_AT_APPLE_block       (true)
                DW_AT_name      ("__block_descriptor")
                DW_AT_byte_size (0x10)                                                                    
                                                                                                          
0x0000006a:     DW_TAG_member              
                  DW_AT_name    ("reserved")                                                              
                  DW_AT_type    (0x00000079 "unsigned long")
                  DW_AT_data_member_location    (0x00)                                         
                                                     
0x00000071:     DW_TAG_member                                                                             
                  DW_AT_name    ("Size")                                                                  
                  DW_AT_type    (0x00000079 "unsigned long")
                  DW_AT_data_member_location    (0x08)             
...                                       

Full diff: https://github.com/llvm/llvm-project/pull/165720.diff

3 Files Affected:

  • (modified) llvm/include/llvm/DebugInfo/DIContext.h (+2)
  • (modified) llvm/lib/DebugInfo/DWARF/DWARFDie.cpp (+3-1)
  • (modified) llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp (+24)
diff --git a/llvm/include/llvm/DebugInfo/DIContext.h b/llvm/include/llvm/DebugInfo/DIContext.h
index e7e87bbfebf38..32ce4520dbca7 100644
--- a/llvm/include/llvm/DebugInfo/DIContext.h
+++ b/llvm/include/llvm/DebugInfo/DIContext.h
@@ -202,6 +202,8 @@ struct DIDumpOptions {
   bool ShowAddresses = true;
   bool ShowChildren = false;
   bool ShowParents = false;
+  /// List of DWARF tags to filter children by.
+  llvm::SmallVector<unsigned> ChildTagsFilter;
   bool ShowForm = false;
   bool SummarizeTypes = false;
   bool Verbose = false;
diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
index db5cc37c93f90..7558fbda18459 100644
--- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
+++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp
@@ -676,7 +676,9 @@ void DWARFDie::dump(raw_ostream &OS, unsigned Indent,
           DIDumpOptions ChildDumpOpts = DumpOpts;
           ChildDumpOpts.ShowParents = false;
           while (Child) {
-            Child.dump(OS, Indent + 2, ChildDumpOpts);
+            if (DumpOpts.ChildTagsFilter.empty() ||
+                llvm::is_contained(DumpOpts.ChildTagsFilter, Child.getTag()))
+              Child.dump(OS, Indent + 2, ChildDumpOpts);
             Child = Child.getSibling();
           }
         }
diff --git a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index 79f05278c329d..eb5f8b4af95a6 100644
--- a/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -14,6 +14,7 @@
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVectorExtras.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/DebugInfo/DIContext.h"
 #include "llvm/DebugInfo/DWARF/DWARFAcceleratorTable.h"
@@ -241,6 +242,15 @@ static opt<bool>
                 cat(DwarfDumpCategory));
 static alias ShowParentsAlias("p", desc("Alias for --show-parents."),
                               aliasopt(ShowParents), cl::NotHidden);
+
+static list<std::string>
+    ChildTags("child-tags",
+              desc("When --show-children is specified, show only DIEs with the "
+                   "specified DWARF tags."),
+              value_desc("list of DWARF tags"), cat(DwarfDumpCategory));
+static alias TagsAlias("t", desc("Alias for --child-tags."),
+                       aliasopt(ChildTags), cl::NotHidden);
+
 static opt<bool>
     ShowForm("show-form",
              desc("Show DWARF form types after the DWARF attribute types."),
@@ -329,6 +339,13 @@ static cl::extrahelp
 /// @}
 //===----------------------------------------------------------------------===//
 
+static llvm::SmallVector<unsigned>
+makeTagVector(const list<std::string> &TagStrings) {
+  return llvm::map_to_vector(TagStrings, [](const std::string &Tag) {
+    return llvm::dwarf::getTag(Tag);
+  });
+}
+
 static void error(Error Err) {
   if (!Err)
     return;
@@ -355,6 +372,7 @@ static DIDumpOptions getDumpOpts(DWARFContext &C) {
   DumpOpts.ShowAddresses = !Diff;
   DumpOpts.ShowChildren = ShowChildren;
   DumpOpts.ShowParents = ShowParents;
+  DumpOpts.ChildTagsFilter = makeTagVector(ChildTags);
   DumpOpts.ShowForm = ShowForm;
   DumpOpts.SummarizeTypes = SummarizeTypes;
   DumpOpts.Verbose = Verbose;
@@ -898,6 +916,12 @@ int main(int argc, char **argv) {
       Find.empty() && !FindAllApple)
     ShowChildren = true;
 
+  if (!ShowChildren && !ChildTags.empty()) {
+    WithColor::error()
+        << "incompatible arguments: --child-tags requires --show-children";
+    return 1;
+  }
+
   // Defaults to a.out if no filenames specified.
   if (InputFilenames.empty())
     InputFilenames.push_back("a.out");

@dwblaikie
Copy link
Collaborator

Seems OK to me as a feature - though I wonder if we're getting to the point of "should have an arbitrary query language" for querying DWARF at this point? (did we ever end up with the json output for dwarfdump? perhaps that's the way to do it - dump to json and use jquery?)

@Michael137
Copy link
Member Author

Seems OK to me as a feature - though I wonder if we're getting to the point of "should have an arbitrary query language" for querying DWARF at this point? (did we ever end up with the json output for dwarfdump? perhaps that's the way to do it - dump to json and use jquery?)

i had the exact same line of thought when i initially started implementing this. What nudged me over the edge was that i prefer looking at dwarfdump output over json, though that’s admittedly a selfish reason. Ultimately i thought, if it’s not too much of a maintenance burden, might as well. Of course there’s always complexity of how various options interact, which in this case seemed ok. A more involved querying feature (like filtering by attribute value/existence), which I’d love to have, is probably not worth adding to the current infrastructure because of how complex it would be to implement (afaict).

But yea, happy to hear others chime in on usefulness/if it’s acceptable maintenance-wise, and whether there is appetite for something more complete.

@adrian-prantl
Copy link
Collaborator

I would have probably called it --filter-child-tag and @dwblaikie 's question about the query language resonates with me. OTOH, dwarfdump is presumably primarily used by us LLVM developers so why not add a few bespoke features that benefit us?

Copy link
Collaborator

@adrian-prantl adrian-prantl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember how we generate the dwarfdump manpage, but can you make to update it, too? (Unless we generate it from the --help output)

@@ -202,6 +202,8 @@ struct DIDumpOptions {
bool ShowAddresses = true;
bool ShowChildren = false;
bool ShowParents = false;
/// List of DWARF tags to filter children by.
llvm::SmallVector<unsigned> ChildTagsFilter;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this is going to be empty most of the time, maybe just use a vector or a SmallVector<0 or 1>?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are multiple ones separated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like with other options, you specify the option multiple times. E.g., dwarfdump -t DW_TAG_foo -t DW_TAG_bar

Other options like --name behave the same

This patch adds a new option `--child-tags` (`-t` for short), which
makes dwarfdump only dump children whose DWARF tag is in the list of
tags specified by the user.

Motivating examples are:
* dumping all global variables in a CU
* dumping all non-static data members of a structure
* dumping all module import declarations of a CU
* etc.

For tags not known to dwarfdump, we pretend that the tag wasn't specified.
@Michael137 Michael137 force-pushed the llvm/dwarfdump-tags-filter branch from 72a3f5b to 3d92747 Compare October 31, 2025 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants