[debugger] Allow querying symbols for one binary at a time

This is closer to our actual usage.

Test: Adjusted unit tests pass.
Change-Id: I4c432cb1efd5d91d51f06d3cb84446e1de9c99c7
diff --git a/bin/debug_agent/debug_agent.cc b/bin/debug_agent/debug_agent.cc
index 88b558e..9e1513d 100644
--- a/bin/debug_agent/debug_agent.cc
+++ b/bin/debug_agent/debug_agent.cc
@@ -375,8 +375,8 @@
     proc->UnregisterBreakpoint(bp, address);
 }
 
-zx_status_t DebugAgent::RegisterWatchpoint(Watchpoint* wp,
-    zx_koid_t process_koid,
+zx_status_t DebugAgent::RegisterWatchpoint(
+    Watchpoint* wp, zx_koid_t process_koid,
     const debug_ipc::AddressRange& range) {
   DebuggedProcess* process = GetDebuggedProcess(process_koid);
   if (!process) {
@@ -430,7 +430,7 @@
                                 debug_ipc::SymbolTablesReply* reply) {
   DebuggedProcess* proc = GetDebuggedProcess(request.process_koid);
   if (proc)
-    proc->OnSymbolTables(reply);
+    proc->OnSymbolTables(request, reply);
 }
 
 DebuggedProcess* DebugAgent::GetDebuggedProcess(zx_koid_t koid) {
diff --git a/bin/debug_agent/debugged_process.cc b/bin/debug_agent/debugged_process.cc
index 4ab9f15..f6018b1 100644
--- a/bin/debug_agent/debugged_process.cc
+++ b/bin/debug_agent/debugged_process.cc
@@ -37,9 +37,7 @@
                          &kMagicValue, sizeof(kMagicValue));
 }
 
-DebuggedProcess::~DebuggedProcess() {
-  DetachFromProcess();
-}
+DebuggedProcess::~DebuggedProcess() { DetachFromProcess(); }
 
 void DebuggedProcess::DetachFromProcess() {
   // 1. Remove installed breakpoints.
@@ -346,10 +344,11 @@
     GetModulesForProcess(process_, dl_debug_addr_, &reply->modules);
 }
 
-void DebuggedProcess::OnSymbolTables(debug_ipc::SymbolTablesReply* reply) {
-  // Modules can only be read after the debug state is set.
-  if (dl_debug_addr_)
-    GetSymbolTablesForProcess(process_, dl_debug_addr_, &reply->symbol_tables);
+void DebuggedProcess::OnSymbolTables(
+    const debug_ipc::SymbolTablesRequest& request,
+    debug_ipc::SymbolTablesReply* reply) {
+  GetSymbolTableFromProcess(process_, request.base, request.build_id,
+                            &reply->symbols);
 }
 
 void DebuggedProcess::OnWriteMemory(
diff --git a/bin/debug_agent/debugged_process.h b/bin/debug_agent/debugged_process.h
index 5ba33ad..2538934 100644
--- a/bin/debug_agent/debugged_process.h
+++ b/bin/debug_agent/debugged_process.h
@@ -55,7 +55,8 @@
   void OnAddressSpace(const debug_ipc::AddressSpaceRequest& request,
                       debug_ipc::AddressSpaceReply* reply);
   void OnModules(debug_ipc::ModulesReply* reply);
-  void OnSymbolTables(debug_ipc::SymbolTablesReply* reply);
+  void OnSymbolTables(const debug_ipc::SymbolTablesRequest& request,
+                      debug_ipc::SymbolTablesReply* reply);
   void OnWriteMemory(const debug_ipc::WriteMemoryRequest& request,
                      debug_ipc::WriteMemoryReply* reply);
 
diff --git a/bin/debug_agent/process_info.cc b/bin/debug_agent/process_info.cc
index efd957e..9ffd6e8 100644
--- a/bin/debug_agent/process_info.cc
+++ b/bin/debug_agent/process_info.cc
@@ -293,34 +293,38 @@
       });
 }
 
-zx_status_t GetSymbolTablesForProcess(
-    const zx::process& process, uint64_t dl_debug_addr,
-    std::vector<debug_ipc::SymbolTable>* symtabs) {
-  return WalkModules(
-      process, dl_debug_addr,
-      [symtabs](const zx::process& process, uint64_t base, uint64_t lmap) {
-        debug_ipc::SymbolTable symtab;
-        symtab.build_id = debug_ipc::ExtractBuildID(process, base);
+zx_status_t GetSymbolTableFromProcess(
+    const zx::process& process, uint64_t elf_addr, const std::string& build_id,
+    std::vector<debug_ipc::ElfSymbol>* symbols) {
+  auto elf = ElfLib::Create(
+      std::make_unique<ProcessMemoryAccessor>(&process, elf_addr));
 
-        auto elf = ElfLib::Create(
-            std::make_unique<ProcessMemoryAccessor>(&process, base));
+  if (!elf) {
+    return ZX_ERR_BAD_STATE;
+  }
 
-        if (elf) {
-          if (auto syms = elf->GetAllSymbols()) {
-            for (const auto& sym : *syms) {
-              debug_ipc::SymbolTable::Symbol ipc_sym;
+  auto got_build_id = debug_ipc::ExtractBuildID(process, elf_addr);
 
-              ipc_sym.name = sym.first;
-              ipc_sym.value = sym.second.st_value;
+  if (got_build_id != build_id) {
+    return ZX_ERR_BAD_STATE;
+  }
 
-              symtab.symbols.push_back(std::move(ipc_sym));
-            }
-          }
-        }
+  auto syms = elf->GetAllSymbols();
 
-        symtabs->push_back(std::move(symtab));
-        return true;
-      });
+  if (!syms) {
+    return ZX_ERR_BAD_STATE;
+  }
+
+  for (const auto& sym : *syms) {
+    debug_ipc::ElfSymbol ipc_sym;
+
+    ipc_sym.name = sym.first;
+    ipc_sym.value = sym.second.st_value;
+
+    symbols->push_back(std::move(ipc_sym));
+  }
+
+  return ZX_OK;
 }
 
 std::vector<zx_info_maps_t> GetProcessMaps(const zx::process& process) {
diff --git a/bin/debug_agent/process_info.h b/bin/debug_agent/process_info.h
index 29decad..aeaf855 100644
--- a/bin/debug_agent/process_info.h
+++ b/bin/debug_agent/process_info.h
@@ -46,9 +46,9 @@
 // Fills the given vector with the module information for the process.
 // "dl_debug_addr" is the address inside "process" of the dynamic loader's
 // debug state.
-zx_status_t GetSymbolTablesForProcess(
-    const zx::process& process, uint64_t dl_debug_addr,
-    std::vector<debug_ipc::SymbolTable>* symtabs);
+zx_status_t GetSymbolTableFromProcess(
+    const zx::process& process, uint64_t elf_addr, const std::string& build_id,
+    std::vector<debug_ipc::ElfSymbol>* symbols);
 
 // Returns the memory mapping for the process. Returns empty on failure.
 std::vector<zx_info_maps_t> GetProcessMaps(const zx::process& process);
diff --git a/lib/debug_ipc/agent_protocol.cc b/lib/debug_ipc/agent_protocol.cc
index 2dc727f..3730a93 100644
--- a/lib/debug_ipc/agent_protocol.cc
+++ b/lib/debug_ipc/agent_protocol.cc
@@ -75,12 +75,7 @@
   writer->WriteString(module.build_id);
 }
 
-void Serialize(const SymbolTable& symbols, MessageWriter* writer) {
-  writer->WriteString(symbols.build_id);
-  Serialize(symbols.symbols, writer);
-}
-
-void Serialize(const SymbolTable::Symbol& symbol, MessageWriter* writer) {
+void Serialize(const ElfSymbol& symbol, MessageWriter* writer) {
   writer->WriteString(symbol.name);
   writer->WriteUint64(symbol.value);
 }
@@ -410,7 +405,7 @@
 void WriteReply(const SymbolTablesReply& reply, uint32_t transaction_id,
                 MessageWriter* writer) {
   writer->WriteHeader(MsgHeader::Type::kSymbolTables, transaction_id);
-  Serialize(reply.symbol_tables, writer);
+  Serialize(reply.symbols, writer);
 }
 
 // JobFilter ------------------------------------------------------------------
diff --git a/lib/debug_ipc/client_protocol.cc b/lib/debug_ipc/client_protocol.cc
index b47833d..e0e8e5a 100644
--- a/lib/debug_ipc/client_protocol.cc
+++ b/lib/debug_ipc/client_protocol.cc
@@ -83,13 +83,7 @@
   return true;
 }
 
-bool Deserialize(MessageReader* reader, SymbolTable* symbols) {
-  if (!reader->ReadString(&symbols->build_id))
-    return false;
-  return Deserialize(reader, &symbols->symbols);
-}
-
-bool Deserialize(MessageReader* reader, SymbolTable::Symbol* symbol) {
+bool Deserialize(MessageReader* reader, ElfSymbol* symbol) {
   if (!reader->ReadString(&symbol->name))
     return false;
   if (!reader->ReadUint64(&symbol->value))
@@ -491,7 +485,7 @@
     return false;
   *transaction_id = header.transaction_id;
 
-  Deserialize(reader, &reply->symbol_tables);
+  Deserialize(reader, &reply->symbols);
   return true;
 }
 
diff --git a/lib/debug_ipc/protocol.h b/lib/debug_ipc/protocol.h
index c45930d..f7bfc6b 100644
--- a/lib/debug_ipc/protocol.h
+++ b/lib/debug_ipc/protocol.h
@@ -241,9 +241,16 @@
 
 struct SymbolTablesRequest {
   uint64_t process_koid = 0;
+  uint64_t base = 0;  // Load address of the module.
+
+  // If we give an invalid base we'll either fault, which should be graceful
+  // through the ReadMemory interface, read a bad header, which we can detect,
+  // or we'll find a different module. We can use the build ID to check against
+  // the last case.
+  std::string build_id;
 };
 struct SymbolTablesReply {
-  std::vector<SymbolTable> symbol_tables;
+  std::vector<ElfSymbol> symbols;
 };
 
 // Request to set filter.
diff --git a/lib/debug_ipc/protocol_unittests.cc b/lib/debug_ipc/protocol_unittests.cc
index 25c96d8..8c230f3 100644
--- a/lib/debug_ipc/protocol_unittests.cc
+++ b/lib/debug_ipc/protocol_unittests.cc
@@ -452,44 +452,20 @@
 
 TEST(Protocol, SymbolTablesReply) {
   SymbolTablesReply initial;
-  initial.symbol_tables.resize(2);
-  initial.symbol_tables[0].build_id = "abcdefghijkl";
-  initial.symbol_tables[0].symbols.resize(2);
-  initial.symbol_tables[0].symbols[0].name = "somefunc";
-  initial.symbol_tables[0].symbols[0].value = 0xbeefUL;
-  initial.symbol_tables[0].symbols[1].name = "wompwomp";
-  initial.symbol_tables[0].symbols[1].value = 0xb0efUL;
-  initial.symbol_tables[1].build_id = "h4sh3s";
-  initial.symbol_tables[1].symbols.resize(2);
-  initial.symbol_tables[1].symbols[0].name = "phresh";
-  initial.symbol_tables[1].symbols[0].value = 0xf00dUL;
-  initial.symbol_tables[1].symbols[1].name = "ackermann";
-  initial.symbol_tables[1].symbols[1].value = 0xb0b0b0b0UL;
+  initial.symbols.resize(2);
+  initial.symbols[0].name = "somefunc";
+  initial.symbols[0].value = 0xbeefUL;
+  initial.symbols[1].name = "wompwomp";
+  initial.symbols[1].value = 0xb0efUL;
 
   SymbolTablesReply second;
   ASSERT_TRUE(SerializeDeserializeReply(initial, &second));
 
-  EXPECT_EQ(2u, second.symbol_tables.size());
-  EXPECT_EQ(initial.symbol_tables[0].build_id,
-            second.symbol_tables[0].build_id);
-  EXPECT_EQ(initial.symbol_tables[0].symbols[0].name,
-            second.symbol_tables[0].symbols[0].name);
-  EXPECT_EQ(initial.symbol_tables[0].symbols[0].value,
-            second.symbol_tables[0].symbols[0].value);
-  EXPECT_EQ(initial.symbol_tables[0].symbols[1].name,
-            second.symbol_tables[0].symbols[1].name);
-  EXPECT_EQ(initial.symbol_tables[0].symbols[1].value,
-            second.symbol_tables[0].symbols[1].value);
-  EXPECT_EQ(initial.symbol_tables[1].build_id,
-            second.symbol_tables[1].build_id);
-  EXPECT_EQ(initial.symbol_tables[1].symbols[0].name,
-            second.symbol_tables[1].symbols[0].name);
-  EXPECT_EQ(initial.symbol_tables[1].symbols[0].value,
-            second.symbol_tables[1].symbols[0].value);
-  EXPECT_EQ(initial.symbol_tables[1].symbols[1].name,
-            second.symbol_tables[1].symbols[1].name);
-  EXPECT_EQ(initial.symbol_tables[1].symbols[1].value,
-            second.symbol_tables[1].symbols[1].value);
+  EXPECT_EQ(2u, second.symbols.size());
+  EXPECT_EQ(initial.symbols[0].name, second.symbols[0].name);
+  EXPECT_EQ(initial.symbols[0].value, second.symbols[0].value);
+  EXPECT_EQ(initial.symbols[1].name, second.symbols[1].name);
+  EXPECT_EQ(initial.symbols[1].value, second.symbols[1].value);
 }
 
 // ASpace ----------------------------------------------------------------------
@@ -568,7 +544,7 @@
   WriteMemoryRequest initial;
   initial.process_koid = 91823765;
   initial.address = 0x3468234;
-  initial.data = { 0, 1, 2, 3, 4, 5};
+  initial.data = {0, 1, 2, 3, 4, 5};
 
   WriteMemoryRequest second;
   ASSERT_TRUE(SerializeDeserializeRequest(initial, &second));
diff --git a/lib/debug_ipc/records.h b/lib/debug_ipc/records.h
index 3ab09f7..414d737 100644
--- a/lib/debug_ipc/records.h
+++ b/lib/debug_ipc/records.h
@@ -191,7 +191,7 @@
 
 struct AddressRange {
   uint64_t begin = 0;
-  uint64_t end = 0;   // Non-inclusive.
+  uint64_t end = 0;  // Non-inclusive.
 };
 
 struct ProcessWatchpointSetings {
@@ -223,15 +223,9 @@
   std::string build_id;
 };
 
-// Information on the linkage symbol table for a module.
-struct SymbolTable {
-  struct Symbol {
-    std::string name;
-    uint64_t value = 0;
-  };
-
-  std::string build_id;
-  std::vector<Symbol> symbols;
+struct ElfSymbol {
+  std::string name;
+  uint64_t value = 0;
 };
 
 struct AddressRegion {
@@ -241,7 +235,7 @@
   uint64_t depth;
 };
 
-// ReadRegisters -------------------------------------------------------------------
+// ReadRegisters ---------------------------------------------------------------
 
 // Value representing a particular register.
 struct Register {