This revision was automatically updated to reflect the committed changes.
Closed by commit rL351541: Breakpad: Extract parsing code into a separate file 
(authored by labath, committed by ).
Herald added a subscriber: llvm-commits.

Repository:
  rL LLVM

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D56844/new/

https://reviews.llvm.org/D56844

Files:
  lldb/trunk/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp
  lldb/trunk/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h
  lldb/trunk/source/Plugins/ObjectFile/Breakpad/CMakeLists.txt
  lldb/trunk/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp
  lldb/trunk/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h
  lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
  lldb/trunk/unittests/ObjectFile/Breakpad/BreakpadRecordsTest.cpp
  lldb/trunk/unittests/ObjectFile/Breakpad/CMakeLists.txt
  lldb/trunk/unittests/ObjectFile/CMakeLists.txt

Index: lldb/trunk/unittests/ObjectFile/CMakeLists.txt
===================================================================
--- lldb/trunk/unittests/ObjectFile/CMakeLists.txt
+++ lldb/trunk/unittests/ObjectFile/CMakeLists.txt
@@ -1 +1,2 @@
+add_subdirectory(Breakpad)
 add_subdirectory(ELF)
Index: lldb/trunk/unittests/ObjectFile/Breakpad/CMakeLists.txt
===================================================================
--- lldb/trunk/unittests/ObjectFile/Breakpad/CMakeLists.txt
+++ lldb/trunk/unittests/ObjectFile/Breakpad/CMakeLists.txt
@@ -0,0 +1,6 @@
+add_lldb_unittest(ObjectFileBreakpadTests
+  BreakpadRecordsTest.cpp
+
+  LINK_LIBS
+    lldbPluginObjectFileBreakpad
+  )
Index: lldb/trunk/unittests/ObjectFile/Breakpad/BreakpadRecordsTest.cpp
===================================================================
--- lldb/trunk/unittests/ObjectFile/Breakpad/BreakpadRecordsTest.cpp
+++ lldb/trunk/unittests/ObjectFile/Breakpad/BreakpadRecordsTest.cpp
@@ -0,0 +1,64 @@
+//===-- BreakpadRecordsTest.cpp ---------------------------------*- C++ -*-===//
+//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+using namespace lldb_private::breakpad;
+
+TEST(Record, classify) {
+  EXPECT_EQ(Record::Module, Record::classify("MODULE"));
+  EXPECT_EQ(Record::Info, Record::classify("INFO"));
+  EXPECT_EQ(Record::File, Record::classify("FILE"));
+  EXPECT_EQ(Record::Func, Record::classify("FUNC"));
+  EXPECT_EQ(Record::Public, Record::classify("PUBLIC"));
+  EXPECT_EQ(Record::Stack, Record::classify("STACK"));
+
+  // Any line which does not start with a known keyword will be classified as a
+  // line record, as those are the only ones that start without a keyword.
+  EXPECT_EQ(Record::Line, Record::classify("deadbeef"));
+  EXPECT_EQ(Record::Line, Record::classify("12"));
+  EXPECT_EQ(Record::Line, Record::classify("CODE_ID"));
+}
+
+TEST(ModuleRecord, parse) {
+  EXPECT_EQ(ModuleRecord(llvm::Triple::Linux, llvm::Triple::x86_64,
+                         UUID::fromData("@ABCDEFGHIJKLMNO", 16)),
+            ModuleRecord::parse(
+                "MODULE Linux x86_64 434241404544474648494a4b4c4d4e4f0 a.out"));
+
+  EXPECT_EQ(llvm::None, ModuleRecord::parse("MODULE"));
+  EXPECT_EQ(llvm::None, ModuleRecord::parse("MODULE Linux"));
+  EXPECT_EQ(llvm::None, ModuleRecord::parse("MODULE Linux x86_64"));
+  EXPECT_EQ(llvm::None,
+            ModuleRecord::parse("MODULE Linux x86_64 deadbeefbaadf00d"));
+}
+
+TEST(InfoRecord, parse) {
+  EXPECT_EQ(InfoRecord(UUID::fromData("@ABCDEFGHIJKLMNO", 16)),
+            InfoRecord::parse("INFO CODE_ID 404142434445464748494a4b4c4d4e4f"));
+  EXPECT_EQ(InfoRecord(UUID()), InfoRecord::parse("INFO CODE_ID 47 a.exe"));
+
+  EXPECT_EQ(llvm::None, InfoRecord::parse("INFO"));
+  EXPECT_EQ(llvm::None, InfoRecord::parse("INFO CODE_ID"));
+}
+
+TEST(PublicRecord, parse) {
+  EXPECT_EQ(PublicRecord(true, 0x47, 0x8, "foo"),
+            PublicRecord::parse("PUBLIC m 47 8 foo"));
+  EXPECT_EQ(PublicRecord(false, 0x47, 0x8, "foo"),
+            PublicRecord::parse("PUBLIC 47 8 foo"));
+
+  EXPECT_EQ(llvm::None, PublicRecord::parse("PUBLIC 47 8"));
+  EXPECT_EQ(llvm::None, PublicRecord::parse("PUBLIC 47"));
+  EXPECT_EQ(llvm::None, PublicRecord::parse("PUBLIC m"));
+  EXPECT_EQ(llvm::None, PublicRecord::parse("PUBLIC"));
+}
Index: lldb/trunk/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp
===================================================================
--- lldb/trunk/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp
+++ lldb/trunk/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.cpp
@@ -8,11 +8,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
 #include "lldb/Core/ModuleSpec.h"
 #include "lldb/Core/PluginManager.h"
 #include "lldb/Core/Section.h"
-#include "lldb/Utility/DataBuffer.h"
-#include "llvm/ADT/StringExtras.h"
 
 using namespace lldb;
 using namespace lldb_private;
@@ -24,164 +23,24 @@
   UUID uuid;
   static llvm::Optional<Header> parse(llvm::StringRef text);
 };
-
-enum class Token { Unknown, Module, Info, File, Func, Public, Stack };
 } // namespace
 
-static Token toToken(llvm::StringRef str) {
-  return llvm::StringSwitch<Token>(str)
-      .Case("MODULE", Token::Module)
-      .Case("INFO", Token::Info)
-      .Case("FILE", Token::File)
-      .Case("FUNC", Token::Func)
-      .Case("PUBLIC", Token::Public)
-      .Case("STACK", Token::Stack)
-      .Default(Token::Unknown);
-}
-
-static llvm::StringRef toString(Token t) {
-  switch (t) {
-  case Token::Unknown:
-    return "";
-  case Token::Module:
-    return "MODULE";
-  case Token::Info:
-    return "INFO";
-  case Token::File:
-    return "FILE";
-  case Token::Func:
-    return "FUNC";
-  case Token::Public:
-    return "PUBLIC";
-  case Token::Stack:
-    return "STACK";
-  }
-  llvm_unreachable("Unknown token!");
-}
-
-static llvm::Triple::OSType toOS(llvm::StringRef str) {
-  using llvm::Triple;
-  return llvm::StringSwitch<Triple::OSType>(str)
-      .Case("Linux", Triple::Linux)
-      .Case("mac", Triple::MacOSX)
-      .Case("windows", Triple::Win32)
-      .Default(Triple::UnknownOS);
-}
-
-static llvm::Triple::ArchType toArch(llvm::StringRef str) {
-  using llvm::Triple;
-  return llvm::StringSwitch<Triple::ArchType>(str)
-      .Case("arm", Triple::arm)
-      .Case("arm64", Triple::aarch64)
-      .Case("mips", Triple::mips)
-      .Case("ppc", Triple::ppc)
-      .Case("ppc64", Triple::ppc64)
-      .Case("s390", Triple::systemz)
-      .Case("sparc", Triple::sparc)
-      .Case("sparcv9", Triple::sparcv9)
-      .Case("x86", Triple::x86)
-      .Case("x86_64", Triple::x86_64)
-      .Default(Triple::UnknownArch);
-}
-
-static llvm::StringRef consume_front(llvm::StringRef &str, size_t n) {
-  llvm::StringRef result = str.take_front(n);
-  str = str.drop_front(n);
-  return result;
-}
-
-static UUID parseModuleId(llvm::Triple::OSType os, llvm::StringRef str) {
-  struct uuid_data {
-    llvm::support::ulittle32_t uuid1;
-    llvm::support::ulittle16_t uuid2[2];
-    uint8_t uuid3[8];
-    llvm::support::ulittle32_t age;
-  } data;
-  static_assert(sizeof(data) == 20, "");
-  // The textual module id encoding should be between 33 and 40 bytes long,
-  // depending on the size of the age field, which is of variable length.
-  // The first three chunks of the id are encoded in big endian, so we need to
-  // byte-swap those.
-  if (str.size() < 33 || str.size() > 40)
-    return UUID();
-  uint32_t t;
-  if (to_integer(consume_front(str, 8), t, 16))
-    data.uuid1 = t;
-  else
-    return UUID();
-  for (int i = 0; i < 2; ++i) {
-    if (to_integer(consume_front(str, 4), t, 16))
-      data.uuid2[i] = t;
-    else
-      return UUID();
-  }
-  for (int i = 0; i < 8; ++i) {
-    if (!to_integer(consume_front(str, 2), data.uuid3[i], 16))
-      return UUID();
-  }
-  if (to_integer(str, t, 16))
-    data.age = t;
-  else
-    return UUID();
-
-  // On non-windows, the age field should always be zero, so we don't include to
-  // match the native uuid format of these platforms.
-  return UUID::fromData(&data, os == llvm::Triple::Win32 ? 20 : 16);
-}
-
 llvm::Optional<Header> Header::parse(llvm::StringRef text) {
-  // A valid module should start with something like:
-  // MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 a.out
-  // optionally followed by
-  // INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC [a.exe]
-  llvm::StringRef token, line;
+  llvm::StringRef line;
   std::tie(line, text) = text.split('\n');
-  std::tie(token, line) = getToken(line);
-  if (toToken(token) != Token::Module)
+  auto Module = ModuleRecord::parse(line);
+  if (!Module)
     return llvm::None;
 
-  std::tie(token, line) = getToken(line);
   llvm::Triple triple;
-  triple.setOS(toOS(token));
-  if (triple.getOS() == llvm::Triple::UnknownOS)
-    return llvm::None;
-
-  std::tie(token, line) = getToken(line);
-  triple.setArch(toArch(token));
-  if (triple.getArch() == llvm::Triple::UnknownArch)
-    return llvm::None;
-
-  llvm::StringRef module_id;
-  std::tie(module_id, line) = getToken(line);
+  triple.setArch(Module->getArch());
+  triple.setOS(Module->getOS());
 
   std::tie(line, text) = text.split('\n');
-  std::tie(token, line) = getToken(line);
-  if (token == "INFO") {
-    std::tie(token, line) = getToken(line);
-    if (token != "CODE_ID")
-      return llvm::None;
-
-    std::tie(token, line) = getToken(line);
-    // If we don't have any text following the code id (e.g. on linux), we
-    // should use the module id as UUID. Otherwise, we revert back to the module
-    // id.
-    if (line.trim().empty()) {
-      UUID uuid;
-      if (uuid.SetFromStringRef(token, token.size() / 2) != token.size())
-        return llvm::None;
-
-      return Header{ArchSpec(triple), uuid};
-    }
-  }
-
-  // We reach here if we don't have a INFO CODE_ID section, or we chose not to
-  // use it. In either case, we need to properly decode the module id, whose
-  // fields are encoded in big-endian.
-  UUID uuid = parseModuleId(triple.getOS(), module_id);
-  if (!uuid)
-    return llvm::None;
 
-  return Header{ArchSpec(triple), uuid};
+  auto Info = InfoRecord::parse(line);
+  UUID uuid = Info && Info->getID() ? Info->getID() : Module->getID();
+  return Header{ArchSpec(triple), std::move(uuid)};
 }
 
 void ObjectFileBreakpad::Initialize() {
@@ -274,18 +133,18 @@
     return;
   m_sections_ap = llvm::make_unique<SectionList>();
 
-  Token current_section = Token::Unknown;
+  llvm::Optional<Record::Kind> current_section;
   offset_t section_start;
   llvm::StringRef text = toStringRef(m_data.GetData());
   uint32_t next_section_id = 1;
   auto maybe_add_section = [&](const uint8_t *end_ptr) {
-    if (current_section == Token::Unknown)
+    if (!current_section)
       return; // We have been called before parsing the first line.
 
     offset_t end_offset = end_ptr - m_data.GetDataStart();
     auto section_sp = std::make_shared<Section>(
         GetModule(), this, next_section_id++,
-        ConstString(toString(current_section)), eSectionTypeOther,
+        ConstString(toString(*current_section)), eSectionTypeOther,
         /*file_vm_addr*/ 0, /*vm_size*/ 0, section_start,
         end_offset - section_start, /*log2align*/ 0, /*flags*/ 0);
     m_sections_ap->AddSection(section_sp);
@@ -295,19 +154,19 @@
     llvm::StringRef line;
     std::tie(line, text) = text.split('\n');
 
-    Token token = toToken(getToken(line).first);
-    if (token == Token::Unknown) {
-      // We assume this is a line record, which logically belongs to the Func
-      // section. Errors will be handled when parsing the Func section.
-      token = Token::Func;
+    Record::Kind next_section = Record::classify(line);
+    if (next_section == Record::Line) {
+      // Line records logically belong to the preceding Func record, so we put
+      // them in the same section.
+      next_section = Record::Func;
     }
-    if (token == current_section)
+    if (next_section == current_section)
       continue;
 
     // Changing sections, finish off the previous one, if there was any.
     maybe_add_section(line.bytes_begin());
     // And start a new one.
-    current_section = token;
+    current_section = next_section;
     section_start = line.bytes_begin() - m_data.GetDataStart();
   }
   // Finally, add the last section.
Index: lldb/trunk/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h
===================================================================
--- lldb/trunk/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h
+++ lldb/trunk/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.h
@@ -0,0 +1,110 @@
+//===-- BreakpadRecords.h ------------------------------------- -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
+#define LLDB_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
+
+#include "lldb/Utility/UUID.h"
+#include "lldb/lldb-types.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/FormatProviders.h"
+
+namespace lldb_private {
+namespace breakpad {
+
+class Record {
+public:
+  enum Kind { Module, Info, File, Func, Line, Public, Stack };
+
+  /// Attempt to guess the kind of the record present in the argument without
+  /// doing a full parse. The returned kind will always be correct for valid
+  /// records, but the full parse can still fail in case of corrupted input.
+  static Kind classify(llvm::StringRef Line);
+
+protected:
+  Record(Kind K) : TheKind(K) {}
+
+  ~Record() = default;
+
+public:
+  Kind getKind() { return TheKind; }
+
+private:
+  Kind TheKind;
+};
+
+llvm::StringRef toString(Record::Kind K);
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Record::Kind K) {
+  OS << toString(K);
+  return OS;
+}
+
+class ModuleRecord : public Record {
+public:
+  static llvm::Optional<ModuleRecord> parse(llvm::StringRef Line);
+  ModuleRecord(llvm::Triple::OSType OS, llvm::Triple::ArchType Arch, UUID ID)
+      : Record(Module), OS(OS), Arch(Arch), ID(std::move(ID)) {}
+
+  llvm::Triple::OSType getOS() const { return OS; }
+  llvm::Triple::ArchType getArch() const { return Arch; }
+  const UUID &getID() const { return ID; }
+
+private:
+  llvm::Triple::OSType OS;
+  llvm::Triple::ArchType Arch;
+  UUID ID;
+};
+
+bool operator==(const ModuleRecord &L, const ModuleRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ModuleRecord &R);
+
+class InfoRecord : public Record {
+public:
+  static llvm::Optional<InfoRecord> parse(llvm::StringRef Line);
+  InfoRecord(UUID ID) : Record(Info), ID(std::move(ID)) {}
+
+  const UUID &getID() const { return ID; }
+
+private:
+  UUID ID;
+};
+
+inline bool operator==(const InfoRecord &L, const InfoRecord &R) {
+  return L.getID() == R.getID();
+}
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InfoRecord &R);
+
+class PublicRecord : public Record {
+public:
+  static llvm::Optional<PublicRecord> parse(llvm::StringRef Line);
+  PublicRecord(bool Multiple, lldb::addr_t Address, lldb::addr_t ParamSize,
+               llvm::StringRef Name)
+      : Record(Module), Multiple(Multiple), Address(Address),
+        ParamSize(ParamSize), Name(Name) {}
+
+  bool getMultiple() const { return Multiple; }
+  lldb::addr_t getAddress() const { return Address; }
+  lldb::addr_t getParamSize() const { return ParamSize; }
+  llvm::StringRef getName() const { return Name; }
+
+private:
+  bool Multiple;
+  lldb::addr_t Address;
+  lldb::addr_t ParamSize;
+  llvm::StringRef Name;
+};
+
+bool operator==(const PublicRecord &L, const PublicRecord &R);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PublicRecord &R);
+
+} // namespace breakpad
+} // namespace lldb_private
+
+#endif // LLDB_PLUGINS_OBJECTFILE_BREAKPAD_BREAKPADRECORDS_H
Index: lldb/trunk/source/Plugins/ObjectFile/Breakpad/CMakeLists.txt
===================================================================
--- lldb/trunk/source/Plugins/ObjectFile/Breakpad/CMakeLists.txt
+++ lldb/trunk/source/Plugins/ObjectFile/Breakpad/CMakeLists.txt
@@ -1,4 +1,5 @@
 add_lldb_library(lldbPluginObjectFileBreakpad PLUGIN
+  BreakpadRecords.cpp
   ObjectFileBreakpad.cpp
 
   LINK_LIBS
Index: lldb/trunk/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp
===================================================================
--- lldb/trunk/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp
+++ lldb/trunk/source/Plugins/ObjectFile/Breakpad/BreakpadRecords.cpp
@@ -0,0 +1,253 @@
+//===-- BreakpadRecords.cpp ----------------------------------- -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FormatVariadic.h"
+
+using namespace lldb_private;
+using namespace lldb_private::breakpad;
+
+namespace {
+enum class Token { Unknown, Module, Info, CodeID, File, Func, Public, Stack };
+}
+
+static Token toToken(llvm::StringRef str) {
+  return llvm::StringSwitch<Token>(str)
+      .Case("MODULE", Token::Module)
+      .Case("INFO", Token::Info)
+      .Case("CODE_ID", Token::CodeID)
+      .Case("FILE", Token::File)
+      .Case("FUNC", Token::Func)
+      .Case("PUBLIC", Token::Public)
+      .Case("STACK", Token::Stack)
+      .Default(Token::Unknown);
+}
+
+static llvm::Triple::OSType toOS(llvm::StringRef str) {
+  using llvm::Triple;
+  return llvm::StringSwitch<Triple::OSType>(str)
+      .Case("Linux", Triple::Linux)
+      .Case("mac", Triple::MacOSX)
+      .Case("windows", Triple::Win32)
+      .Default(Triple::UnknownOS);
+}
+
+static llvm::Triple::ArchType toArch(llvm::StringRef str) {
+  using llvm::Triple;
+  return llvm::StringSwitch<Triple::ArchType>(str)
+      .Case("arm", Triple::arm)
+      .Case("arm64", Triple::aarch64)
+      .Case("mips", Triple::mips)
+      .Case("ppc", Triple::ppc)
+      .Case("ppc64", Triple::ppc64)
+      .Case("s390", Triple::systemz)
+      .Case("sparc", Triple::sparc)
+      .Case("sparcv9", Triple::sparcv9)
+      .Case("x86", Triple::x86)
+      .Case("x86_64", Triple::x86_64)
+      .Default(Triple::UnknownArch);
+}
+
+static llvm::StringRef consume_front(llvm::StringRef &str, size_t n) {
+  llvm::StringRef result = str.take_front(n);
+  str = str.drop_front(n);
+  return result;
+}
+
+static UUID parseModuleId(llvm::Triple::OSType os, llvm::StringRef str) {
+  struct uuid_data {
+    llvm::support::ulittle32_t uuid1;
+    llvm::support::ulittle16_t uuid2[2];
+    uint8_t uuid3[8];
+    llvm::support::ulittle32_t age;
+  } data;
+  static_assert(sizeof(data) == 20, "");
+  // The textual module id encoding should be between 33 and 40 bytes long,
+  // depending on the size of the age field, which is of variable length.
+  // The first three chunks of the id are encoded in big endian, so we need to
+  // byte-swap those.
+  if (str.size() < 33 || str.size() > 40)
+    return UUID();
+  uint32_t t;
+  if (to_integer(consume_front(str, 8), t, 16))
+    data.uuid1 = t;
+  else
+    return UUID();
+  for (int i = 0; i < 2; ++i) {
+    if (to_integer(consume_front(str, 4), t, 16))
+      data.uuid2[i] = t;
+    else
+      return UUID();
+  }
+  for (int i = 0; i < 8; ++i) {
+    if (!to_integer(consume_front(str, 2), data.uuid3[i], 16))
+      return UUID();
+  }
+  if (to_integer(str, t, 16))
+    data.age = t;
+  else
+    return UUID();
+
+  // On non-windows, the age field should always be zero, so we don't include to
+  // match the native uuid format of these platforms.
+  return UUID::fromData(&data, os == llvm::Triple::Win32 ? 20 : 16);
+}
+
+Record::Kind Record::classify(llvm::StringRef Line) {
+  Token Tok = toToken(getToken(Line).first);
+  switch (Tok) {
+  case Token::Module:
+    return Record::Module;
+  case Token::Info:
+    return Record::Info;
+  case Token::File:
+    return Record::File;
+  case Token::Func:
+    return Record::Func;
+  case Token::Public:
+    return Record::Public;
+  case Token::Stack:
+    return Record::Stack;
+
+  case Token::CodeID:
+  case Token::Unknown:
+    // Optimistically assume that any unrecognised token means this is a line
+    // record, those don't have a special keyword and start directly with a
+    // hex number. CODE_ID should never be at the start of a line, but if it
+    // is, it can be treated the same way as a garbled line record.
+    return Record::Line;
+  }
+  llvm_unreachable("Fully covered switch above!");
+}
+
+llvm::Optional<ModuleRecord> ModuleRecord::parse(llvm::StringRef Line) {
+  // MODULE Linux x86_64 E5894855C35DCCCCCCCCCCCCCCCCCCCC0 a.out
+  llvm::StringRef Str;
+  std::tie(Str, Line) = getToken(Line);
+  if (toToken(Str) != Token::Module)
+    return llvm::None;
+
+  std::tie(Str, Line) = getToken(Line);
+  llvm::Triple::OSType OS = toOS(Str);
+  if (OS == llvm::Triple::UnknownOS)
+    return llvm::None;
+
+  std::tie(Str, Line) = getToken(Line);
+  llvm::Triple::ArchType Arch = toArch(Str);
+  if (Arch == llvm::Triple::UnknownArch)
+    return llvm::None;
+
+  std::tie(Str, Line) = getToken(Line);
+  UUID ID = parseModuleId(OS, Str);
+  if (!ID)
+    return llvm::None;
+
+  return ModuleRecord(OS, Arch, std::move(ID));
+}
+
+bool breakpad::operator==(const ModuleRecord &L, const ModuleRecord &R) {
+  return L.getOS() == R.getOS() && L.getArch() == R.getArch() &&
+         L.getID() == R.getID();
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+                                        const ModuleRecord &R) {
+  return OS << "MODULE " << llvm::Triple::getOSTypeName(R.getOS()) << " "
+            << llvm::Triple::getArchTypeName(R.getArch()) << " "
+            << R.getID().GetAsString();
+}
+
+llvm::Optional<InfoRecord> InfoRecord::parse(llvm::StringRef Line) {
+  // INFO CODE_ID 554889E55DC3CCCCCCCCCCCCCCCCCCCC [a.exe]
+  llvm::StringRef Str;
+  std::tie(Str, Line) = getToken(Line);
+  if (toToken(Str) != Token::Info)
+    return llvm::None;
+
+  std::tie(Str, Line) = getToken(Line);
+  if (toToken(Str) != Token::CodeID)
+    return llvm::None;
+
+  std::tie(Str, Line) = getToken(Line);
+  // If we don't have any text following the code ID (e.g. on linux), we should
+  // use this as the UUID. Otherwise, we should revert back to the module ID.
+  UUID ID;
+  if (Line.trim().empty()) {
+    if (Str.empty() || ID.SetFromStringRef(Str, Str.size() / 2) != Str.size())
+      return llvm::None;
+  }
+  return InfoRecord(std::move(ID));
+}
+
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+                                        const InfoRecord &R) {
+  return OS << "INFO CODE_ID " << R.getID().GetAsString();
+}
+
+llvm::Optional<PublicRecord> PublicRecord::parse(llvm::StringRef Line) {
+  // PUBLIC [m] address param_size name
+  llvm::StringRef Str;
+  std::tie(Str, Line) = getToken(Line);
+  if (toToken(Str) != Token::Public)
+    return llvm::None;
+
+  std::tie(Str, Line) = getToken(Line);
+  bool Multiple = Str == "m";
+
+  if (Multiple)
+    std::tie(Str, Line) = getToken(Line);
+  lldb::addr_t Address;
+  if (!to_integer(Str, Address, 16))
+    return llvm::None;
+
+  std::tie(Str, Line) = getToken(Line);
+  lldb::addr_t ParamSize;
+  if (!to_integer(Str, ParamSize, 16))
+    return llvm::None;
+
+  llvm::StringRef Name = Line.trim();
+  if (Name.empty())
+    return llvm::None;
+
+  return PublicRecord(Multiple, Address, ParamSize, Name);
+}
+
+bool breakpad::operator==(const PublicRecord &L, const PublicRecord &R) {
+  return L.getMultiple() == R.getMultiple() &&
+         L.getAddress() == R.getAddress() &&
+         L.getParamSize() == R.getParamSize() && L.getName() == R.getName();
+}
+llvm::raw_ostream &breakpad::operator<<(llvm::raw_ostream &OS,
+                                        const PublicRecord &R) {
+  return OS << llvm::formatv("PUBLIC {0}{1:x-} {2:x-} {3}",
+                             R.getMultiple() ? "m " : "", R.getAddress(),
+                             R.getParamSize(), R.getName());
+}
+
+llvm::StringRef breakpad::toString(Record::Kind K) {
+  switch (K) {
+  case Record::Module:
+    return "MODULE";
+  case Record::Info:
+    return "INFO";
+  case Record::File:
+    return "FILE";
+  case Record::Func:
+    return "FUNC";
+  case Record::Line:
+    return "LINE";
+  case Record::Public:
+    return "PUBLIC";
+  case Record::Stack:
+    return "STACK";
+  }
+  llvm_unreachable("Unknown record kind!");
+}
Index: lldb/trunk/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h
===================================================================
--- lldb/trunk/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h
+++ lldb/trunk/source/Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h
@@ -12,7 +12,6 @@
 
 #include "lldb/Symbol/ObjectFile.h"
 #include "lldb/Utility/ArchSpec.h"
-#include "llvm/ADT/Triple.h"
 
 namespace lldb_private {
 namespace breakpad {
Index: lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
===================================================================
--- lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
+++ lldb/trunk/source/Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.cpp
@@ -8,6 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "Plugins/SymbolFile/Breakpad/SymbolFileBreakpad.h"
+#include "Plugins/ObjectFile/Breakpad/BreakpadRecords.h"
 #include "Plugins/ObjectFile/Breakpad/ObjectFileBreakpad.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Core/PluginManager.h"
@@ -181,38 +182,29 @@
 
   const SectionList &list = *module.GetSectionList();
   for (llvm::StringRef line : lines(*m_obj_file, ConstString("PUBLIC"))) {
-    // PUBLIC [m] address param_size name
-    // skip PUBLIC keyword
-    line = getToken(line).second;
-    llvm::StringRef token;
-    std::tie(token, line) = getToken(line);
-    if (token == "m")
-      std::tie(token, line) = getToken(line);
-
-    addr_t address;
-    if (!to_integer(token, address, 16))
+    auto record = PublicRecord::parse(line);
+    if (!record) {
+      LLDB_LOG(log, "Failed to parse: {0}. Skipping record.", line);
       continue;
-    address += base;
-
-    // skip param_size
-    line = getToken(line).second;
-
-    llvm::StringRef name = line.trim();
+    }
+    addr_t file_address = base + record->getAddress();
 
-    SectionSP section_sp = list.FindSectionContainingFileAddress(address);
+    SectionSP section_sp = list.FindSectionContainingFileAddress(file_address);
     if (!section_sp) {
       LLDB_LOG(log,
                "Ignoring symbol {0}, whose address ({1}) is outside of the "
                "object file. Mismatched symbol file?",
-               name, address);
+               record->getName(), file_address);
       continue;
     }
 
     symtab.AddSymbol(Symbol(
-        /*symID*/ 0, Mangled(name, /*is_mangled*/ false), eSymbolTypeCode,
+        /*symID*/ 0, Mangled(record->getName(), /*is_mangled*/ false),
+        eSymbolTypeCode,
         /*is_global*/ true, /*is_debug*/ false, /*is_trampoline*/ false,
         /*is_artificial*/ false,
-        AddressRange(section_sp, address - section_sp->GetFileAddress(), 0),
+        AddressRange(section_sp, file_address - section_sp->GetFileAddress(),
+                     0),
         /*size_is_valid*/ 0, /*contains_linker_annotations*/ false,
         /*flags*/ 0));
   }
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to