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