Add PUBLIC support to SourceLineResolver (resolve function names in Windows

system libraries) (#53)
StackFrame::function_base is not populated (#49)
r=bryner

http://groups.google.com/group/airbag-dev/browse_thread/thread/a17d35348e7027bb


git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@43 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
mmentovai 2006-10-20 19:50:01 +00:00
parent 246f406828
commit 2fc823f579
15 changed files with 638 additions and 32 deletions

View file

@ -56,6 +56,8 @@ src_libairbag_la_SOURCES = \
src/google/stack_frame.h \
src/google/stack_frame_cpu.h \
src/google/symbol_supplier.h \
src/processor/address_map.h \
src/processor/address_map-inl.h \
src/processor/call_stack.cc \
src/processor/contained_range_map.h \
src/processor/contained_range_map-inl.h \
@ -88,6 +90,7 @@ bin_PROGRAMS = \
## Tests
check_PROGRAMS = \
src/processor/address_map_unittest \
src/processor/contained_range_map_unittest \
src/processor/minidump_processor_unittest \
src/processor/postfix_evaluator_unittest \
@ -106,6 +109,9 @@ check_SCRIPTS = \
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
TESTS_ENVIRONMENT =
src_processor_address_map_unittest_SOURCES = \
src/processor/address_map_unittest.cc
src_processor_contained_range_map_unittest_SOURCES = \
src/processor/contained_range_map_unittest.cc

View file

@ -70,7 +70,8 @@ build_triplet = @build@
host_triplet = @host@
bin_PROGRAMS = src/processor/minidump_dump$(EXEEXT) \
src/processor/minidump_stackwalk$(EXEEXT)
check_PROGRAMS = src/processor/contained_range_map_unittest$(EXEEXT) \
check_PROGRAMS = src/processor/address_map_unittest$(EXEEXT) \
src/processor/contained_range_map_unittest$(EXEEXT) \
src/processor/minidump_processor_unittest$(EXEEXT) \
src/processor/postfix_evaluator_unittest$(EXEEXT) \
src/processor/range_map_unittest$(EXEEXT) \
@ -118,6 +119,11 @@ binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
@SELFTEST_TRUE@am__EXEEXT_1 = \
@SELFTEST_TRUE@ src/processor/stackwalker_selftest$(EXEEXT)
PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
am_src_processor_address_map_unittest_OBJECTS = \
src/processor/address_map_unittest.$(OBJEXT)
src_processor_address_map_unittest_OBJECTS = \
$(am_src_processor_address_map_unittest_OBJECTS)
src_processor_address_map_unittest_LDADD = $(LDADD)
am_src_processor_contained_range_map_unittest_OBJECTS = \
src/processor/contained_range_map_unittest.$(OBJEXT)
src_processor_contained_range_map_unittest_OBJECTS = \
@ -193,6 +199,7 @@ CCLD = $(CC)
LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(src_libairbag_la_SOURCES) \
$(src_processor_address_map_unittest_SOURCES) \
$(src_processor_contained_range_map_unittest_SOURCES) \
$(src_processor_minidump_dump_SOURCES) \
$(src_processor_minidump_processor_unittest_SOURCES) \
@ -202,6 +209,7 @@ SOURCES = $(src_libairbag_la_SOURCES) \
$(src_processor_source_line_resolver_unittest_SOURCES) \
$(src_processor_stackwalker_selftest_SOURCES)
DIST_SOURCES = $(src_libairbag_la_SOURCES) \
$(src_processor_address_map_unittest_SOURCES) \
$(src_processor_contained_range_map_unittest_SOURCES) \
$(src_processor_minidump_dump_SOURCES) \
$(src_processor_minidump_processor_unittest_SOURCES) \
@ -348,6 +356,8 @@ src_libairbag_la_SOURCES = \
src/google/stack_frame.h \
src/google/stack_frame_cpu.h \
src/google/symbol_supplier.h \
src/processor/address_map.h \
src/processor/address_map-inl.h \
src/processor/call_stack.cc \
src/processor/contained_range_map.h \
src/processor/contained_range_map-inl.h \
@ -377,6 +387,9 @@ check_SCRIPTS = \
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
TESTS_ENVIRONMENT =
src_processor_address_map_unittest_SOURCES = \
src/processor/address_map_unittest.cc
src_processor_contained_range_map_unittest_SOURCES = \
src/processor/contained_range_map_unittest.cc
@ -592,6 +605,12 @@ clean-noinstPROGRAMS:
echo " rm -f $$p $$f"; \
rm -f $$p $$f ; \
done
src/processor/address_map_unittest.$(OBJEXT): \
src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
src/processor/address_map_unittest$(EXEEXT): $(src_processor_address_map_unittest_OBJECTS) $(src_processor_address_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
@rm -f src/processor/address_map_unittest$(EXEEXT)
$(CXXLINK) $(src_processor_address_map_unittest_LDFLAGS) $(src_processor_address_map_unittest_OBJECTS) $(src_processor_address_map_unittest_LDADD) $(LIBS)
src/processor/contained_range_map_unittest.$(OBJEXT): \
src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
@ -642,6 +661,7 @@ src/processor/stackwalker_selftest$(EXEEXT): $(src_processor_stackwalker_selftes
mostlyclean-compile:
-rm -f *.$(OBJEXT)
-rm -f src/processor/address_map_unittest.$(OBJEXT)
-rm -f src/processor/call_stack.$(OBJEXT)
-rm -f src/processor/call_stack.lo
-rm -f src/processor/contained_range_map_unittest.$(OBJEXT)
@ -668,6 +688,7 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/address_map_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/call_stack.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/contained_range_map_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump.Plo@am__quote@

View file

@ -483,7 +483,7 @@ bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function,
} else if (*name[0] == '_') {
// This symbol's name is encoded according to the cdecl rules. The
// name doesn't end in a '@' character followed by a decimal positive
// nteger, so it's not a stdcall name. Strip off the leading
// integer, so it's not a stdcall name. Strip off the leading
// underscore.
wcsncpy_s(*name, length, *name + 1, length - 1);
}

View file

@ -45,7 +45,8 @@ struct StackFrame {
function_base(),
function_name(),
source_file_name(),
source_line() {}
source_line(),
source_line_base() {}
virtual ~StackFrame() {}
// The program counter location as an absolute virtual address. For the
@ -74,6 +75,10 @@ struct StackFrame {
// The (1-based) source line number, may be omitted if debug symbols are
// not available.
int source_line;
// The start address of the source line, may be omitted if debug symbols
// are not available.
u_int64_t source_line_base;
};
} // namespace google_airbag

View file

@ -0,0 +1,86 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// address_map-inl.h: Address map implementation.
//
// See address_map.h for documentation.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_ADDRESS_MAP_INL_H__
#define PROCESSOR_ADDRESS_MAP_INL_H__
#include "processor/address_map.h"
namespace google_airbag {
template<typename AddressType, typename EntryType>
bool AddressMap<AddressType, EntryType>::Store(const AddressType &address,
const EntryType &entry) {
// Ensure that the specified address doesn't conflict with something already
// in the map.
if (map_.find(address) != map_.end())
return false;
map_.insert(MapValue(address, entry));
return true;
}
template<typename AddressType, typename EntryType>
bool AddressMap<AddressType, EntryType>::Retrieve(
const AddressType &address,
EntryType *entry, AddressType *entry_address) const {
if (!entry)
return false;
// upper_bound gives the first element whose key is greater than address,
// but we want the first element whose key is less than or equal to address.
// Decrement the iterator to get there, but not if the upper_bound already
// points to the beginning of the map - in that case, address is lower than
// the lowest stored key, so return false.
MapConstIterator iterator = map_.upper_bound(address);
if (iterator == map_.begin())
return false;
--iterator;
*entry = iterator->second;
if (entry_address)
*entry_address = iterator->first;
return true;
}
template<typename AddressType, typename EntryType>
void AddressMap<AddressType, EntryType>::Clear() {
map_.clear();
}
} // namespace google_airbag
#endif // PROCESSOR_ADDRESS_MAP_INL_H__

View file

@ -0,0 +1,80 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// address_map.h: Address maps.
//
// An address map contains a set of objects keyed by address. Objects are
// retrieved from the map by returning the object with the highest key less
// than or equal to the lookup key.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_ADDRESS_MAP_H__
#define PROCESSOR_ADDRESS_MAP_H__
#include <map>
namespace google_airbag {
template<typename AddressType, typename EntryType>
class AddressMap {
public:
AddressMap() : map_() {}
// Inserts an entry into the map. Returns false without storing the entry
// if an entry is already stored in the map at the same address as specified
// by the address argument.
bool Store(const AddressType &address, const EntryType &entry);
// Locates the entry stored at the highest address less than or equal to
// the address argument. If there is no such range, or if there is a
// parameter error, returns false. The entry is returned in entry. If
// entry_address is not NULL, it will be set to the address that the entry
// was stored at.
bool Retrieve(const AddressType &address,
EntryType *entry, AddressType *entry_address) const;
// Empties the address map, restoring it to the same state as when it was
// initially created.
void Clear();
private:
// Convenience types.
typedef std::map<AddressType, EntryType> AddressToEntryMap;
typedef typename AddressToEntryMap::const_iterator MapConstIterator;
typedef typename AddressToEntryMap::value_type MapValue;
// Maps the address of each entry to an EntryType.
AddressToEntryMap map_;
};
} // namespace google_airbag
#endif // PROCESSOR_ADDRESS_MAP_H__

View file

@ -0,0 +1,190 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// address_map_unittest.cc: Unit tests for AddressMap.
//
// Author: Mark Mentovai
#include <climits>
#include <cstdio>
#include "processor/address_map-inl.h"
#include "processor/linked_ptr.h"
#define ASSERT_TRUE(condition) \
if (!(condition)) { \
fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \
return false; \
}
#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition))
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
using google_airbag::AddressMap;
using google_airbag::linked_ptr;
// A CountedObject holds an int. A global (not thread safe!) count of
// allocated CountedObjects is maintained to help test memory management.
class CountedObject {
public:
explicit CountedObject(int id) : id_(id) { ++count_; }
~CountedObject() { --count_; }
static int count() { return count_; }
int id() const { return id_; }
private:
static int count_;
int id_;
};
int CountedObject::count_;
typedef int AddressType;
typedef AddressMap< AddressType, linked_ptr<CountedObject> > TestMap;
static bool DoAddressMapTest() {
ASSERT_EQ(CountedObject::count(), 0);
TestMap test_map;
linked_ptr<CountedObject> entry;
AddressType address;
// Check that a new map is truly empty.
ASSERT_FALSE(test_map.Retrieve(0, &entry, &address));
ASSERT_FALSE(test_map.Retrieve(INT_MIN, &entry, &address));
ASSERT_FALSE(test_map.Retrieve(INT_MAX, &entry, &address));
// Check that Clear clears the map without leaking.
ASSERT_EQ(CountedObject::count(), 0);
ASSERT_TRUE(test_map.Store(1,
linked_ptr<CountedObject>(new CountedObject(0))));
ASSERT_TRUE(test_map.Retrieve(1, &entry, &address));
ASSERT_EQ(CountedObject::count(), 1);
test_map.Clear();
ASSERT_EQ(CountedObject::count(), 1); // still holding entry in this scope
// Check that a cleared map is truly empty.
ASSERT_FALSE(test_map.Retrieve(0, &entry, &address));
ASSERT_FALSE(test_map.Retrieve(INT_MIN, &entry, &address));
ASSERT_FALSE(test_map.Retrieve(INT_MAX, &entry, &address));
// Check a single-element map.
ASSERT_TRUE(test_map.Store(10,
linked_ptr<CountedObject>(new CountedObject(1))));
ASSERT_FALSE(test_map.Retrieve(9, &entry, &address));
ASSERT_TRUE(test_map.Retrieve(10, &entry, &address));
ASSERT_EQ(CountedObject::count(), 1);
ASSERT_EQ(entry->id(), 1);
ASSERT_EQ(address, 10);
ASSERT_TRUE(test_map.Retrieve(11, &entry, &address));
ASSERT_FALSE(test_map.Retrieve(11, NULL, &address)); // parameter error
ASSERT_TRUE(test_map.Retrieve(11, &entry, NULL)); // NULL ok here
// Add some more elements.
ASSERT_TRUE(test_map.Store(5,
linked_ptr<CountedObject>(new CountedObject(2))));
ASSERT_EQ(CountedObject::count(), 2);
ASSERT_TRUE(test_map.Store(20,
linked_ptr<CountedObject>(new CountedObject(3))));
ASSERT_TRUE(test_map.Store(15,
linked_ptr<CountedObject>(new CountedObject(4))));
ASSERT_FALSE(test_map.Store(10,
linked_ptr<CountedObject>(new CountedObject(5)))); // already in map
ASSERT_TRUE(test_map.Store(16,
linked_ptr<CountedObject>(new CountedObject(6))));
ASSERT_TRUE(test_map.Store(14,
linked_ptr<CountedObject>(new CountedObject(7))));
// Nothing was stored with a key under 5. Don't use ASSERT inside loops
// because it won't show exactly which key/entry/address failed.
for (AddressType key = 0; key < 5; ++key) {
if (test_map.Retrieve(key, &entry, &address)) {
fprintf(stderr,
"FAIL: retrieve %d expected false observed true @ %s:%d\n",
key, __FILE__, __LINE__);
return false;
}
}
// Check everything that was stored.
const int id_verify[] = { 0, 0, 0, 0, 0, // unused
2, 2, 2, 2, 2, // 5 - 9
1, 1, 1, 1, 7, // 10 - 14
4, 6, 6, 6, 6, // 15 - 19
3, 3, 3, 3, 3, // 20 - 24
3, 3, 3, 3, 3 }; // 25 - 29
const AddressType address_verify[] = { 0, 0, 0, 0, 0, // unused
5, 5, 5, 5, 5, // 5 - 9
10, 10, 10, 10, 14, // 10 - 14
15, 16, 16, 16, 16, // 15 - 19
20, 20, 20, 20, 20, // 20 - 24
20, 20, 20, 20, 20 }; // 25 - 29
for (AddressType key = 5; key < 30; ++key) {
if (!test_map.Retrieve(key, &entry, &address)) {
fprintf(stderr,
"FAIL: retrieve %d expected true observed false @ %s:%d\n",
key, __FILE__, __LINE__);
return false;
}
if (entry->id() != id_verify[key]) {
fprintf(stderr,
"FAIL: retrieve %d expected entry %d observed %d @ %s:%d\n",
key, id_verify[key], entry->id(), __FILE__, __LINE__);
return false;
}
if (address != address_verify[key]) {
fprintf(stderr,
"FAIL: retrieve %d expected address %d observed %d @ %s:%d\n",
key, address_verify[key], address, __FILE__, __LINE__);
return false;
}
}
// The stored objects should still be in the map.
ASSERT_EQ(CountedObject::count(), 6);
return true;
}
static bool RunTests() {
if (!DoAddressMapTest())
return false;
// Leak check.
ASSERT_EQ(CountedObject::count(), 0);
return true;
}
int main(int argc, char **argv) {
return RunTests() ? 0 : 1;
}

View file

@ -1486,7 +1486,7 @@ MinidumpModule* MinidumpModuleList::GetModuleForAddress(u_int64_t address) {
return NULL;
unsigned int module_index;
if (!range_map_.RetrieveRange(address, &module_index))
if (!range_map_.RetrieveRange(address, &module_index, NULL, NULL))
return NULL;
return GetModuleAtIndex(module_index);
@ -1616,7 +1616,7 @@ MinidumpMemoryRegion* MinidumpMemoryList::GetMemoryRegionForAddress(
return NULL;
unsigned int region_index;
if (!range_map_.RetrieveRange(address, &region_index))
if (!range_map_.RetrieveRange(address, &region_index, NULL, NULL))
return NULL;
return GetMemoryRegionAtIndex(region_index);

View file

@ -83,7 +83,8 @@ bool RangeMap<AddressType, EntryType>::StoreRange(const AddressType &base,
template<typename AddressType, typename EntryType>
bool RangeMap<AddressType, EntryType>::RetrieveRange(
const AddressType &address, EntryType *entry) const {
const AddressType &address, EntryType *entry,
AddressType *entry_base, AddressType *entry_size) const {
if (!entry)
return false;
@ -100,6 +101,42 @@ bool RangeMap<AddressType, EntryType>::RetrieveRange(
return false;
*entry = iterator->second.entry();
if (entry_base)
*entry_base = iterator->second.base();
if (entry_size)
*entry_size = iterator->first - iterator->second.base() + 1;
return true;
}
template<typename AddressType, typename EntryType>
bool RangeMap<AddressType, EntryType>::RetrieveNearestRange(
const AddressType &address, EntryType *entry,
AddressType *entry_base, AddressType *entry_size) const {
if (!entry)
return false;
// If address is within a range, RetrieveRange can handle it.
if (RetrieveRange(address, entry, entry_base, entry_size))
return true;
// upper_bound gives the first element whose key is greater than address,
// but we want the first element whose key is less than or equal to address.
// Decrement the iterator to get there, but not if the upper_bound already
// points to the beginning of the map - in that case, address is lower than
// the lowest stored key, so return false.
MapConstIterator iterator = map_.upper_bound(address);
if (iterator == map_.begin())
return false;
--iterator;
*entry = iterator->second.entry();
if (entry_base)
*entry_base = iterator->first;
if (entry_size)
*entry_size = iterator->first - iterator->second.base() + 1;
return true;
}

View file

@ -61,7 +61,19 @@ class RangeMap {
// Locates the range encompassing the supplied address. If there is
// no such range, or if there is a parameter error, returns false.
bool RetrieveRange(const AddressType &address, EntryType *entry) const;
// entry_base and entry_size, if non-NULL, are set to the base and size
// of the entry's range.
bool RetrieveRange(const AddressType &address, EntryType *entry,
AddressType *entry_base, AddressType *entry_size) const;
// Locates the range encompassing the supplied address, if one exists.
// If no range encompasses the supplied address, locates the nearest range
// to the supplied address that is lower than the address. Returns false
// if no range meets these criteria. entry_base and entry_size, if
// non-NULL, are set to the base and size of the entry's range.
bool RetrieveNearestRange(const AddressType &address, EntryType *entry,
AddressType *entry_base, AddressType *entry_size)
const;
// Empties the range map, restoring it to the state it was when it was
// initially created.

View file

@ -36,10 +36,12 @@
#include <cstdio>
#include <memory>
#include "processor/linked_ptr.h"
#include "processor/range_map-inl.h"
using std::auto_ptr;
using google_airbag::linked_ptr;
using google_airbag::RangeMap;
@ -48,7 +50,6 @@ using google_airbag::RangeMap;
class CountedObject {
public:
explicit CountedObject(int id) : id_(id) { ++count_; }
CountedObject(const CountedObject &that) : id_(that.id_) { ++count_; }
~CountedObject() { --count_; }
static int count() { return count_; }
@ -63,7 +64,7 @@ int CountedObject::count_;
typedef int AddressType;
typedef RangeMap<AddressType, CountedObject> TestMap;
typedef RangeMap< AddressType, linked_ptr<CountedObject> > TestMap;
// RangeTest contains data to use for store and retrieve tests. See
@ -98,7 +99,7 @@ struct RangeTestSet {
// test RangeMap. It returns true if the expected result occurred, and
// false if something else happened.
bool StoreTest(TestMap *range_map, const RangeTest *range_test) {
CountedObject object(range_test->id);
linked_ptr<CountedObject> object(new CountedObject(range_test->id));
bool stored = range_map->StoreRange(range_test->address,
range_test->size,
object);
@ -158,10 +159,14 @@ bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
expected_result = !side; // should succeed low and fail high.
}
CountedObject object(-1);
bool retrieved = range_map->RetrieveRange(address, &object);
linked_ptr<CountedObject> object;
AddressType retrieved_base;
AddressType retrieved_size;
bool retrieved = range_map->RetrieveRange(address, &object,
&retrieved_base,
&retrieved_size);
bool observed_result = retrieved && object.id() == range_test->id;
bool observed_result = retrieved && object->id() == range_test->id;
if (observed_result != expected_result) {
fprintf(stderr, "FAILED: "
@ -174,6 +179,59 @@ bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
observed_result ? "true" : "false");
return false;
}
// If a range was successfully retrieved, check that the returned
// bounds match the range as stored.
if (observed_result == true &&
(retrieved_base != range_test->address ||
retrieved_size != range_test->size)) {
fprintf(stderr, "FAILED: "
"RetrieveRange id %d, side %d, offset %d, "
"expected base/size %d/%d, observed %d/%d\n",
range_test->id,
side,
offset,
range_test->address, range_test->size,
retrieved_base, retrieved_size);
return false;
}
// Now, check RetrieveNearestRange. The nearest range is always
// expected to be different from the test range when checking one
// less than the low side.
bool expected_nearest = range_test->expect_storable;
if (!side && offset < 0)
expected_nearest = false;
linked_ptr<CountedObject> nearest_object;
AddressType nearest_base;
bool retrieved_nearest = range_map->RetrieveNearestRange(address,
&nearest_object,
&nearest_base,
NULL);
// When checking one greater than the high side, RetrieveNearestRange
// should usually return the test range. When a different range begins
// at that address, though, then RetrieveNearestRange should return the
// range at the address instead of the test range.
if (side && offset > 0 && nearest_base == address) {
expected_nearest = false;
}
bool observed_nearest = retrieved_nearest &&
nearest_object->id() == range_test->id;
if (observed_nearest != expected_nearest) {
fprintf(stderr, "FAILED: "
"RetrieveNearestRange id %d, side %d, offset %d, "
"expected %s, observed %s\n",
range_test->id,
side,
offset,
expected_nearest ? "true" : "false",
observed_nearest ? "true" : "false");
return false;
}
}
}

View file

@ -34,6 +34,7 @@
#include <utility>
#include "processor/source_line_resolver.h"
#include "google/stack_frame.h"
#include "processor/address_map-inl.h"
#include "processor/contained_range_map-inl.h"
#include "processor/linked_ptr.h"
#include "processor/range_map-inl.h"
@ -77,6 +78,23 @@ struct SourceLineResolver::Function {
RangeMap< MemAddr, linked_ptr<Line> > lines;
};
struct SourceLineResolver::PublicSymbol {
PublicSymbol(const string& set_name,
MemAddr set_address,
int set_parameter_size)
: name(set_name),
address(set_address),
parameter_size(set_parameter_size) {}
string name;
MemAddr address;
// If the public symbol is used as a function entry point, parameter_size
// is set to the size of the parameters passed to the funciton on the
// stack, if known.
int parameter_size;
};
class SourceLineResolver::Module {
public:
Module(const string &name) : name_(name) { }
@ -129,12 +147,17 @@ class SourceLineResolver::Module {
// Parses a line declaration, returning a new Line object.
Line* ParseLine(char *line_line);
// Parses a PUBLIC symbol declaration, storing it in public_symbols_.
// Returns false if an error occurs.
bool ParsePublicSymbol(char *public_line);
// Parses a stack frame info declaration, storing it in stack_info_.
bool ParseStackInfo(char *stack_info_line);
string name_;
FileMap files_;
RangeMap< MemAddr, linked_ptr<Function> > functions_;
AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_;
// Each element in the array is a ContainedRangeMap for a type listed in
// StackInfoTypes. These are split by type because there may be overlaps
@ -208,10 +231,17 @@ bool SourceLineResolver::Module::LoadMap(const string &map_file) {
if (!cur_func) {
return false;
}
functions_.StoreRange(cur_func->address, cur_func->size,
linked_ptr<Function>(cur_func));
if (!functions_.StoreRange(cur_func->address, cur_func->size,
linked_ptr<Function>(cur_func))) {
return false;
}
} else if (strncmp(buffer, "PUBLIC ", 7) == 0) {
// TODO(mmentovai): add a public map
// Clear cur_func: public symbols don't contain line number information.
cur_func = NULL;
if (!ParsePublicSymbol(buffer)) {
return false;
}
} else {
if (!cur_func) {
return false;
@ -248,30 +278,62 @@ void SourceLineResolver::Module::LookupAddress(
}
}
// First, look for a matching FUNC range. Use RetrieveNearestRange instead
// of RetrieveRange so that the nearest function can be compared to the
// nearest PUBLIC symbol if the address does not lie within the function.
// Having access to the highest function below address, even when address
// is outside of the function, is useful: if the function is higher than
// the nearest PUBLIC symbol, then it means that the PUBLIC symbols is not
// valid for the address, and no function information should be filled in.
// Using RetrieveNearestRange instead of RetrieveRange means that we need
// to verify that address is within the range before using a FUNC.
//
// If no FUNC containing the address is found, look for the nearest PUBLIC
// symbol, being careful not to use a public symbol at a lower address than
// the nearest FUNC.
int parameter_size = 0;
linked_ptr<Function> func;
if (!functions_.RetrieveRange(address, &func)) {
linked_ptr<PublicSymbol> public_symbol;
MemAddr function_base;
MemAddr function_size;
MemAddr public_address;
if (functions_.RetrieveNearestRange(address, &func,
&function_base, &function_size) &&
address >= function_base && address < function_base + function_size) {
parameter_size = func->parameter_size;
frame->function_name = func->name;
frame->function_base = frame->module_base + function_base;
linked_ptr<Line> line;
MemAddr line_base;
if (func->lines.RetrieveRange(address, &line, &line_base, NULL)) {
FileMap::const_iterator it = files_.find(line->source_file_id);
if (it != files_.end()) {
frame->source_file_name = files_.find(line->source_file_id)->second;
}
frame->source_line = line->line;
frame->source_line_base = frame->module_base + line_base;
}
} else if (public_symbols_.Retrieve(address,
&public_symbol, &public_address) &&
(!func.get() || public_address > function_base + function_size)) {
parameter_size = public_symbol->parameter_size;
frame->function_name = public_symbol->name;
frame->function_base = frame->module_base + public_address;
} else {
// No FUNC or PUBLIC data available.
return;
}
frame->function_name = func->name;
linked_ptr<Line> line;
if (!func->lines.RetrieveRange(address, &line)) {
return;
}
FileMap::const_iterator it = files_.find(line->source_file_id);
if (it != files_.end()) {
frame->source_file_name = files_.find(line->source_file_id)->second;
}
frame->source_line = line->line;
if (frame_info &&
!(frame_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE)) {
// Even without a relevant STACK line, many functions contain information
// about how much space their parameters consume on the stack. Prefer
// the STACK stuff (above), but if it's not present, take the
// information from the FUNC line.
frame_info->parameter_size = func->parameter_size;
// information from the FUNC or PUBLIC line.
frame_info->parameter_size = parameter_size;
frame_info->valid |= StackFrameInfo::VALID_PARAMETER_SIZE;
}
}
@ -325,7 +387,7 @@ void SourceLineResolver::Module::ParseFile(char *file_line) {
SourceLineResolver::Function* SourceLineResolver::Module::ParseFunction(
char *function_line) {
// FUNC <address> <stack_param_size> <name>
// FUNC <address> <size> <stack_param_size> <name>
function_line += 5; // skip prefix
vector<char*> tokens;
@ -360,6 +422,36 @@ SourceLineResolver::Line* SourceLineResolver::Module::ParseLine(
return new Line(address, size, source_file, line_number);
}
bool SourceLineResolver::Module::ParsePublicSymbol(char *public_line) {
// PUBLIC <address> <stack_param_size> <name>
// Skip "PUBLIC " prefix.
public_line += 7;
vector<char*> tokens;
if (!Tokenize(public_line, 3, &tokens)) {
return false;
}
u_int64_t address = strtoull(tokens[0], NULL, 16);
int stack_param_size = strtoull(tokens[1], NULL, 16);
char *name = tokens[2];
// A few public symbols show up with an address of 0. This has been seen
// in the dumped output of ntdll.pdb for symbols such as _CIlog, _CIpow,
// RtlDescribeChunkLZNT1, and RtlReserveChunkLZNT1. They would conflict
// with one another if they were allowed into the public_symbols_ map,
// but since the address is obviously invalid, gracefully accept them
// as input without putting them into the map.
if (address == 0) {
return true;
}
linked_ptr<PublicSymbol> symbol(new PublicSymbol(name, address,
stack_param_size));
return public_symbols_.Store(address, symbol);
}
bool SourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
// STACK WIN <type> <rva> <code_size> <prolog_size> <epliog_size>
// <parameter_size> <saved_register_size> <local_size> <max_stack_size>

View file

@ -73,6 +73,7 @@ class SourceLineResolver {
template<class T> class MemAddrMap;
struct Line;
struct Function;
struct PublicSymbol;
struct File;
struct HashString {
size_t operator()(const string &s) const;

View file

@ -116,6 +116,22 @@ static bool RunTests() {
ASSERT_EQ(frame.source_line, 21);
ASSERT_EQ(frame_info.prolog_size, 1);
frame.instruction = 0x216f;
frame.module_name = "module2";
resolver.FillSourceLineInfo(&frame, &frame_info);
ASSERT_EQ(frame.function_name, "Public2_1");
ClearSourceLineInfo(&frame, &frame_info);
frame.instruction = 0x219f;
frame.module_name = "module2";
resolver.FillSourceLineInfo(&frame, &frame_info);
ASSERT_TRUE(frame.function_name.empty());
frame.instruction = 0x21a0;
frame.module_name = "module2";
resolver.FillSourceLineInfo(&frame, &frame_info);
ASSERT_EQ(frame.function_name, "Public2_2");
ASSERT_FALSE(resolver.LoadModule("module3",
testdata_dir + "/module3_bad.out"));
ASSERT_FALSE(resolver.HasModule("module3"));

View file

@ -5,10 +5,12 @@ FUNC 2000 c 4 Function2_1
1000 4 54 1
1004 4 55 1
1008 4 56 1
PUBLIC 2160 0 Public2_1
FUNC 2170 14 4 Function2_2
2170 6 10 2
2176 4 12 2
217a 6 13 2
2180 4 21 2
PUBLIC 21a0 0 Public2_2
STACK WIN 4 2000 c 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
STACK WIN 4 2170 14 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =