[dump_syms] Fix DW_AT_specification warning on Mac.

1. Visit DW_TAG_class_type when it's inside DW_TAG_subprogram.
2. Only warn when we can't get the name for the DIE and it has DW_AT_specification that is not in the specification map.

Bug: 1078932
Change-Id: Id3126aec305658f8f65c01675a8e9e3ea03f3651
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/3579855
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc
index 3435e5b..bf78fda 100644
--- a/src/common/dwarf_cu_to_module.cc
+++ b/src/common/dwarf_cu_to_module.cc
@@ -286,6 +286,7 @@
         offset_(offset),
         declaration_(false),
         specification_(NULL),
+        no_specification(false),
         abstract_origin_(NULL),
         forward_ref_die_offset_(0), specification_offset_(0) { }
 
@@ -331,6 +332,10 @@
   // Otherwise, this is NULL.
   Specification* specification_;
 
+  // If this DIE has DW_AT_specification with offset smaller than this DIE and
+  // we can't find that in the specification map.
+  bool no_specification;
+
   // If this DIE has a DW_AT_abstract_origin attribute, this is the
   // AbstractOrigin structure for the DIE the attribute refers to.
   // Otherwise, this is NULL.
@@ -393,7 +398,7 @@
       } else if (data > offset_) {
         forward_ref_die_offset_ = data;
       } else {
-        cu_context_->reporter->UnknownSpecification(offset_, data);
+        no_specification = true;
       }
       specification_offset_ = data;
       break;
@@ -479,7 +484,7 @@
     // counts; otherwise, use this DIE's context.
     if (specification_) {
       enclosing_name = &specification_->enclosing_name;
-    } else {
+    } else if (parent_context_) {
       enclosing_name = &parent_context_->name;
     }
   }
@@ -601,7 +606,7 @@
     enum DwarfTag tag) {
   switch (tag) {
     case DW_TAG_inlined_subroutine:
-      return new InlineHandler(cu_context_, new DIEContext(), offset,
+      return new InlineHandler(cu_context_, nullptr, offset,
                                inline_nest_level_ + 1, child_inlines_);
     default:
       return NULL;
@@ -672,6 +677,24 @@
   inlines_.push_back(std::move(in));
 }
 
+// A handler for DIEs that contain functions and contribute a
+// component to their names: namespaces, classes, etc.
+class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler {
+ public:
+  NamedScopeHandler(CUContext* cu_context,
+                    DIEContext* parent_context,
+                    uint64_t offset,
+                    bool handle_inline)
+      : GenericDIEHandler(cu_context, parent_context, offset),
+        handle_inline_(handle_inline) {}
+  bool EndAttributes();
+  DIEHandler* FindChildHandler(uint64_t offset, enum DwarfTag tag);
+
+ private:
+  DIEContext child_context_; // A context for our children.
+  bool handle_inline_;
+};
+
 // A handler class for DW_TAG_subprogram DIEs.
 class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
  public:
@@ -709,6 +732,7 @@
   bool inline_;
   vector<unique_ptr<Module::Inline>> child_inlines_;
   bool handle_inline_;
+  DIEContext child_context_; // A context for our children.
 };
 
 void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned(
@@ -757,8 +781,13 @@
   switch (tag) {
     case DW_TAG_inlined_subroutine:
       if (handle_inline_)
-        return new InlineHandler(cu_context_, new DIEContext(), offset, 0,
+        return new InlineHandler(cu_context_, nullptr, offset, 0,
                                  child_inlines_);
+    case DW_TAG_class_type:
+    case DW_TAG_structure_type:
+    case DW_TAG_union_type:
+      return new NamedScopeHandler(cu_context_, &child_context_, offset,
+                                   handle_inline_);
     default:
       return NULL;
   }
@@ -770,6 +799,10 @@
   if (name_.empty() && abstract_origin_) {
     name_ = abstract_origin_->name;
   }
+  child_context_.name = name_;
+  if (name_.empty() && no_specification) {
+    cu_context_->reporter->UnknownSpecification(offset_, specification_offset_);
+  }
   return true;
 }
 
@@ -868,26 +901,11 @@
   }
 }
 
-// A handler for DIEs that contain functions and contribute a
-// component to their names: namespaces, classes, etc.
-class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler {
- public:
-  NamedScopeHandler(CUContext* cu_context,
-                    DIEContext* parent_context,
-                    uint64_t offset,
-                    bool handle_inline)
-      : GenericDIEHandler(cu_context, parent_context, offset),
-        handle_inline_(handle_inline) {}
-  bool EndAttributes();
-  DIEHandler* FindChildHandler(uint64_t offset, enum DwarfTag tag);
-
- private:
-  DIEContext child_context_; // A context for our children.
-  bool handle_inline_;
-};
-
 bool DwarfCUToModule::NamedScopeHandler::EndAttributes() {
   child_context_.name = ComputeQualifiedName();
+  if (child_context_.name.empty() && no_specification) {
+    cu_context_->reporter->UnknownSpecification(offset_, specification_offset_);
+  }
   return true;
 }