# HG changeset patch
# User Ted Mielczarek <ted@mielczarek.org>
# Date 1352220493 18000
# Node ID c3b1109dd392c16a9fe4e85da693010966dbbf0b
# Parent  03db269a2868503cca9e80f62ce676aabbf967fd
Add APIs for querying Module data
R=glandium at https://breakpad.appspot.com/511003/

diff --git a/src/common/module.cc b/src/common/module.cc
--- a/src/common/module.cc
+++ b/src/common/module.cc
@@ -58,17 +58,17 @@
 
 Module::~Module() {
   for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it)
     delete it->second;
   for (FunctionSet::iterator it = functions_.begin();
        it != functions_.end(); ++it) {
     delete *it;
   }
-  for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
+  for (StackFrameEntrySet::iterator it = stack_frame_entries_.begin();
        it != stack_frame_entries_.end(); ++it) {
     delete *it;
   }
   for (ExternSet::iterator it = externs_.begin(); it != externs_.end(); ++it)
     delete *it;
 }
 
 void Module::SetLoadAddress(Address address) {
@@ -88,39 +88,84 @@
 }
 
 void Module::AddFunctions(vector<Function *>::iterator begin,
                           vector<Function *>::iterator end) {
   for (vector<Function *>::iterator it = begin; it != end; ++it)
     AddFunction(*it);
 }
 
-void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) {
-  stack_frame_entries_.push_back(stack_frame_entry);
+void Module::AddStackFrameEntry(StackFrameEntry* stack_frame_entry) {
+  std::pair<StackFrameEntrySet::iterator,bool> ret =
+      stack_frame_entries_.insert(stack_frame_entry);
+  if (!ret.second) {
+    // Free the duplicate that was not inserted because this Module
+    // now owns it.
+    delete stack_frame_entry;
+  }
 }
 
 void Module::AddExtern(Extern *ext) {
   std::pair<ExternSet::iterator,bool> ret = externs_.insert(ext);
   if (!ret.second) {
     // Free the duplicate that was not inserted because this Module
     // now owns it.
     delete ext;
   }
 }
 
 void Module::GetFunctions(vector<Function *> *vec,
                           vector<Function *>::iterator i) {
   vec->insert(i, functions_.begin(), functions_.end());
 }
 
+template<typename T>
+bool EntryContainsAddress(T entry, Module::Address address) {
+  return entry->address <= address && address < entry->address + entry->size;
+}
+
+Module::Function* Module::FindFunctionByAddress(Address address) {
+  Function search;
+  search.address = address;
+  // Ensure that name always sorts higher than the function name,
+  // so that upper_bound always returns the function just after
+  // the function containing this address.
+  search.name = "\xFF";
+  FunctionSet::iterator it = functions_.upper_bound(&search);
+  if (it == functions_.begin())
+    return NULL;
+
+  it--;
+
+  if (EntryContainsAddress(*it, address))
+    return *it;
+
+  return NULL;
+}
+
 void Module::GetExterns(vector<Extern *> *vec,
                         vector<Extern *>::iterator i) {
   vec->insert(i, externs_.begin(), externs_.end());
 }
 
+Module::Extern* Module::FindExternByAddress(Address address) {
+  Extern search;
+  search.address = address;
+  ExternSet::iterator it = externs_.upper_bound(&search);
+
+  if (it == externs_.begin())
+    return NULL;
+
+  it--;
+  if ((*it)->address > address)
+    return NULL;
+
+  return *it;
+}
+
 Module::File *Module::FindFile(const string &name) {
   // A tricky bit here.  The key of each map entry needs to be a
   // pointer to the entry's File's name string.  This means that we
   // can't do the initial lookup with any operation that would create
   // an empty entry for us if the name isn't found (like, say,
   // operator[] or insert do), because such a created entry's key will
   // be a pointer the string passed as our argument.  Since the key of
   // a map's value type is const, we can't fix it up once we've
@@ -150,18 +195,35 @@
 }
 
 void Module::GetFiles(vector<File *> *vec) {
   vec->clear();
   for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it)
     vec->push_back(it->second);
 }
 
-void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) {
-  *vec = stack_frame_entries_;
+void Module::GetStackFrameEntries(vector<StackFrameEntry *>* vec) {
+  vec->clear();
+  vec->insert(vec->begin(), stack_frame_entries_.begin(),
+              stack_frame_entries_.end());
+}
+
+Module::StackFrameEntry* Module::FindStackFrameEntryByAddress(Address address) {
+  StackFrameEntry search;
+  search.address = address;
+  StackFrameEntrySet::iterator it = stack_frame_entries_.upper_bound(&search);
+
+  if (it == stack_frame_entries_.begin())
+    return NULL;
+
+  it--;
+  if (EntryContainsAddress(*it, address))
+    return *it;
+
+  return NULL;
 }
 
 void Module::AssignSourceIds() {
   // First, give every source file an id of -1.
   for (FileByNameMap::iterator file_it = files_.begin();
        file_it != files_.end(); ++file_it) {
     file_it->second->source_id = -1;
   }
@@ -256,17 +318,17 @@
       stream << "PUBLIC " << hex
              << (ext->address - load_address_) << " 0 "
              << ext->name << dec << endl;
     }
   }
 
   if (symbol_data != NO_CFI) {
     // Write out 'STACK CFI INIT' and 'STACK CFI' records.
-    vector<StackFrameEntry *>::const_iterator frame_it;
+    StackFrameEntrySet::const_iterator frame_it;
     for (frame_it = stack_frame_entries_.begin();
          frame_it != stack_frame_entries_.end(); ++frame_it) {
       StackFrameEntry *entry = *frame_it;
       stream << "STACK CFI INIT " << hex
              << (entry->address - load_address_) << " "
              << entry->size << " " << dec;
       if (!stream.good()
           || !WriteRuleMap(entry->initial_rules, stream))
diff --git a/src/common/module.h b/src/common/module.h
--- a/src/common/module.h
+++ b/src/common/module.h
@@ -164,16 +164,23 @@
 
   struct ExternCompare {
     bool operator() (const Extern *lhs,
                      const Extern *rhs) const {
       return lhs->address < rhs->address;
     }
   };
 
+  struct StackFrameEntryCompare {
+    bool operator() (const StackFrameEntry* lhs,
+                     const StackFrameEntry* rhs) const {
+      return lhs->address < rhs->address;
+    }
+  };
+
   // Create a new module with the given name, operating system,
   // architecture, and ID string.
   Module(const string &name, const string &os, const string &architecture,
          const string &id);
   ~Module();
 
   // Set the module's load address to LOAD_ADDRESS; addresses given
   // for functions and lines will be written to the Breakpad symbol
@@ -223,37 +230,49 @@
 
   // Insert pointers to the functions added to this module at I in
   // VEC. The pointed-to Functions are still owned by this module.
   // (Since this is effectively a copy of the function list, this is
   // mostly useful for testing; other uses should probably get a more
   // appropriate interface.)
   void GetFunctions(vector<Function *> *vec, vector<Function *>::iterator i);
 
+  // If this module has a function at ADDRESS, return a pointer to it.
+  // Otherwise, return NULL.
+  Function* FindFunctionByAddress(Address address);
+
   // Insert pointers to the externs added to this module at I in
   // VEC. The pointed-to Externs are still owned by this module.
   // (Since this is effectively a copy of the extern list, this is
   // mostly useful for testing; other uses should probably get a more
   // appropriate interface.)
   void GetExterns(vector<Extern *> *vec, vector<Extern *>::iterator i);
 
+  // If this module has an extern whose base address is less than ADDRESS,
+  // return a pointer to it. Otherwise, return NULL.
+  Extern* FindExternByAddress(Address address);
+
   // Clear VEC and fill it with pointers to the Files added to this
   // module, sorted by name. The pointed-to Files are still owned by
   // this module. (Since this is effectively a copy of the file list,
   // this is mostly useful for testing; other uses should probably get
   // a more appropriate interface.)
   void GetFiles(vector<File *> *vec);
 
   // Clear VEC and fill it with pointers to the StackFrameEntry
   // objects that have been added to this module. (Since this is
   // effectively a copy of the stack frame entry list, this is mostly
   // useful for testing; other uses should probably get
   // a more appropriate interface.)
   void GetStackFrameEntries(vector<StackFrameEntry *> *vec);
 
+  // If this module has a StackFrameEntry whose address range covers
+  // ADDRESS, return it. Otherwise return NULL.
+  StackFrameEntry* FindStackFrameEntryByAddress(Address address);
+
   // Find those files in this module that are actually referred to by
   // functions' line number data, and assign them source id numbers.
   // Set the source id numbers for all other files --- unused by the
   // source line data --- to -1.  We do this before writing out the
   // symbol file, at which point we omit any unused files.
   void AssignSourceIds();
 
   // Call AssignSourceIds, and write this module to STREAM in the
@@ -299,25 +318,28 @@
   typedef map<const string *, File *, CompareStringPtrs> FileByNameMap;
 
   // A set containing Function structures, sorted by address.
   typedef set<Function *, FunctionCompare> FunctionSet;
 
   // A set containing Extern structures, sorted by address.
   typedef set<Extern *, ExternCompare> ExternSet;
 
+  // A set containing StackFrameEntry structures, sorted by address.
+  typedef set<StackFrameEntry*, StackFrameEntryCompare> StackFrameEntrySet;
+
   // The module owns all the files and functions that have been added
   // to it; destroying the module frees the Files and Functions these
   // point to.
   FileByNameMap files_;    // This module's source files.
   FunctionSet functions_;  // This module's functions.
 
   // The module owns all the call frame info entries that have been
   // added to it.
-  vector<StackFrameEntry *> stack_frame_entries_;
+  StackFrameEntrySet stack_frame_entries_;
 
   // The module owns all the externs that have been added to it;
   // destroying the module frees the Externs these point to.
   ExternSet externs_;
 };
 
 }  // namespace google_breakpad
 
diff --git a/src/common/module_unittest.cc b/src/common/module_unittest.cc
--- a/src/common/module_unittest.cc
+++ b/src/common/module_unittest.cc
@@ -329,63 +329,63 @@
   entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
     "I think I know";
   m.AddStackFrameEntry(entry3);
 
   // Check that Write writes STACK CFI records properly.
   m.Write(s, ALL_SYMBOL_DATA);
   string contents = s.str();
   EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
-               "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n"
-               "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
-               " .cfa: I think that I shall never see"
-               " cannoli: a tree whose hungry mouth is prest"
-               " stromboli: a poem lovely as a tree\n"
                "STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229"
                " .cfa: Whose woods are these\n"
                "STACK CFI 36682fad3763ffff"
                " .cfa: I think I know"
                " stromboli: his house is in\n"
                "STACK CFI 47ceb0f63c269d7f"
                " calzone: the village though"
-               " cannoli: he will not see me stopping here\n",
+               " cannoli: he will not see me stopping here\n"
+               "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
+               " .cfa: I think that I shall never see"
+               " cannoli: a tree whose hungry mouth is prest"
+               " stromboli: a poem lovely as a tree\n"
+               "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n",
                contents.c_str());
 
   // Check that GetStackFrameEntries works.
   vector<Module::StackFrameEntry *> entries;
   m.GetStackFrameEntries(&entries);
   ASSERT_EQ(3U, entries.size());
   // Check first entry.
-  EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address);
-  EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size);
-  ASSERT_EQ(0U, entries[0]->initial_rules.size());
-  ASSERT_EQ(0U, entries[0]->rule_changes.size());
+  EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[0]->address);
+  EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[0]->size);
+  Module::RuleMap entry1_initial;
+  entry1_initial[".cfa"] = "Whose woods are these";
+  EXPECT_THAT(entries[0]->initial_rules, ContainerEq(entry1_initial));
+  Module::RuleChangeMap entry1_changes;
+  entry1_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know";
+  entry1_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in";
+  entry1_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though";
+  entry1_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
+    "he will not see me stopping here";
+  EXPECT_THAT(entries[0]->rule_changes, ContainerEq(entry1_changes));
   // Check second entry.
   EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address);
   EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size);
   ASSERT_EQ(3U, entries[1]->initial_rules.size());
   Module::RuleMap entry2_initial;
   entry2_initial[".cfa"] = "I think that I shall never see";
   entry2_initial["stromboli"] = "a poem lovely as a tree";
   entry2_initial["cannoli"] = "a tree whose hungry mouth is prest";
   EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial));
   ASSERT_EQ(0U, entries[1]->rule_changes.size());
   // Check third entry.
-  EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address);
-  EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size);
-  Module::RuleMap entry3_initial;
-  entry3_initial[".cfa"] = "Whose woods are these";
-  EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial));
-  Module::RuleChangeMap entry3_changes;
-  entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know";
-  entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in";
-  entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though";
-  entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
-    "he will not see me stopping here";
-  EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes));
+  EXPECT_EQ(0xddb5f41285aa7757ULL, entries[2]->address);
+  EXPECT_EQ(0x1486493370dc5073ULL, entries[2]->size);
+  ASSERT_EQ(0U, entries[2]->initial_rules.size());
+  ASSERT_EQ(0U, entries[2]->rule_changes.size());
 }
 
 TEST(Construct, UniqueFiles) {
   Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
   Module::File *file1 = m.FindFile("foo");
   Module::File *file2 = m.FindFile(string("bar"));
   Module::File *file3 = m.FindFile(string("foo"));
   Module::File *file4 = m.FindFile("bar");
@@ -483,8 +483,155 @@
   m.Write(s, ALL_SYMBOL_DATA);
   string contents = s.str();
 
   EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
                MODULE_ID " " MODULE_NAME "\n"
                "PUBLIC ffff 0 _xyz\n",
                contents.c_str());
 }
+
+TEST(Lookup, Function) {
+  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+  Module::Function *function1 = new(Module::Function);
+  function1->name = "_abc1";
+  function1->address = 0x1000;
+  function1->size = 0x900;
+  function1->parameter_size = 0x0;
+
+  Module::Function *function2 = new(Module::Function);
+  function2->name = "_xyz2";
+  function2->address = 0x2000;
+  function2->size = 0x900;
+  function2->parameter_size = 0x0;
+
+  Module::Function *function3 = new(Module::Function);
+  function3->name = "_def3";
+  function3->address = 0x3000;
+  function3->size = 0x900;
+  function3->parameter_size = 0x0;
+
+  // Put them in a vector.
+  vector<Module::Function *> vec;
+  vec.push_back(function1);
+  vec.push_back(function2);
+  vec.push_back(function3);
+
+  m.AddFunctions(vec.begin(), vec.end());
+
+  // Try looking up functions by address.
+  Module::Function* f = m.FindFunctionByAddress(0x1000);
+  EXPECT_EQ(function1, f);
+  f = m.FindFunctionByAddress(0x18FF);
+  EXPECT_EQ(function1, f);
+
+  f = m.FindFunctionByAddress(0x1900);
+  EXPECT_EQ((Module::Function*)NULL, f);
+  f = m.FindFunctionByAddress(0x1A00);
+  EXPECT_EQ((Module::Function*)NULL, f);
+
+  f = m.FindFunctionByAddress(0x2000);
+  EXPECT_EQ(function2, f);
+  f = m.FindFunctionByAddress(0x28FF);
+  EXPECT_EQ(function2, f);
+
+  f = m.FindFunctionByAddress(0x3000);
+  EXPECT_EQ(function3, f);
+  f = m.FindFunctionByAddress(0x38FF);
+  EXPECT_EQ(function3, f);
+
+  f = m.FindFunctionByAddress(0x3900);
+  EXPECT_EQ((Module::Function*)NULL, f);
+  f = m.FindFunctionByAddress(0x3A00);
+  EXPECT_EQ((Module::Function*)NULL, f);
+}
+
+TEST(Lookup, Externs) {
+  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+  // Two externs.
+  Module::Extern* extern1 = new(Module::Extern);
+  extern1->address = 0x1000;
+  extern1->name = "_abc";
+  Module::Extern* extern2 = new(Module::Extern);
+  extern2->address = 0x2000;
+  extern2->name = "_xyz";
+
+  m.AddExtern(extern1);
+  m.AddExtern(extern2);
+
+  Module::Extern* e = m.FindExternByAddress(0xFFF);
+  EXPECT_EQ((Module::Extern*)NULL, e);
+
+  e = m.FindExternByAddress(0x1000);
+  EXPECT_EQ(extern1, e);
+  e = m.FindExternByAddress(0x1900);
+  EXPECT_EQ(extern1, e);
+  e = m.FindExternByAddress(0x1FFF);
+  EXPECT_EQ(extern1, e);
+
+  e = m.FindExternByAddress(0x2000);
+  EXPECT_EQ(extern2, e);
+  e = m.FindExternByAddress(0x2900);
+  EXPECT_EQ(extern2, e);
+  e = m.FindExternByAddress(0xFFFFFFFF);
+  EXPECT_EQ(extern2, e);
+}
+
+TEST(Lookup, StackFrameEntries) {
+  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
+
+  // First STACK CFI entry, with no initial rules or deltas.
+  Module::StackFrameEntry *entry1 = new Module::StackFrameEntry();
+  entry1->address = 0x2000;
+  entry1->size = 0x900;
+  m.AddStackFrameEntry(entry1);
+
+  // Second STACK CFI entry, with initial rules but no deltas.
+  Module::StackFrameEntry *entry2 = new Module::StackFrameEntry();
+  entry2->address = 0x3000;
+  entry2->size = 0x900;
+  entry2->initial_rules[".cfa"] = "I think that I shall never see";
+  entry2->initial_rules["stromboli"] = "a poem lovely as a tree";
+  entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest";
+  m.AddStackFrameEntry(entry2);
+
+  // Third STACK CFI entry, with initial rules and deltas.
+  Module::StackFrameEntry *entry3 = new Module::StackFrameEntry();
+  entry3->address = 0x1000;
+  entry3->size = 0x900;
+  entry3->initial_rules[".cfa"] = "Whose woods are these";
+  entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] =
+    "the village though";
+  entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
+    "he will not see me stopping here";
+  entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] =
+    "his house is in";
+  entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
+    "I think I know";
+  m.AddStackFrameEntry(entry3);
+
+  Module::StackFrameEntry* s = m.FindStackFrameEntryByAddress(0x1000);
+  EXPECT_EQ(entry3, s);
+  s = m.FindStackFrameEntryByAddress(0x18FF);
+  EXPECT_EQ(entry3, s);
+
+  s = m.FindStackFrameEntryByAddress(0x1900);
+  EXPECT_EQ((Module::StackFrameEntry*)NULL, s);
+  s = m.FindStackFrameEntryByAddress(0x1A00);
+  EXPECT_EQ((Module::StackFrameEntry*)NULL, s);
+
+  s = m.FindStackFrameEntryByAddress(0x2000);
+  EXPECT_EQ(entry1, s);
+  s = m.FindStackFrameEntryByAddress(0x28FF);
+  EXPECT_EQ(entry1, s);
+
+  s = m.FindStackFrameEntryByAddress(0x3000);
+  EXPECT_EQ(entry2, s);
+  s = m.FindStackFrameEntryByAddress(0x38FF);
+  EXPECT_EQ(entry2, s);
+
+  s = m.FindStackFrameEntryByAddress(0x3900);
+  EXPECT_EQ((Module::StackFrameEntry*)NULL, s);
+  s = m.FindStackFrameEntryByAddress(0x3A00);
+  EXPECT_EQ((Module::StackFrameEntry*)NULL, s);
+}
