1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-07-31 19:30:21 +02:00

* Added breakpad support for Linux.

This commit is contained in:
Christian Muehlhaeuser
2011-09-15 07:27:31 +02:00
parent d8b07cee9c
commit d8d7347394
1163 changed files with 465521 additions and 4 deletions

View File

@@ -0,0 +1,93 @@
// 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"
#include <assert.h>
#include "processor/logging.h"
namespace google_breakpad {
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()) {
BPLOG(INFO) << "Store failed, address " << HexString(address) <<
" is already present";
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 {
BPLOG_IF(ERROR, !entry) << "AddressMap::Retrieve requires |entry|";
assert(entry);
// 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_breakpad
#endif // PROCESSOR_ADDRESS_MAP_INL_H__

View File

@@ -0,0 +1,85 @@
// 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_breakpad {
// Forward declarations (for later friend declarations).
template<class, class> class AddressMapSerializer;
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, returns false. The
// entry is returned in entry, which is a required argument. 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:
friend class AddressMapSerializer<AddressType, EntryType>;
friend class ModuleComparer;
// 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_breakpad
#endif // PROCESSOR_ADDRESS_MAP_H__

View File

@@ -0,0 +1,196 @@
// 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 <limits.h>
#include <stdio.h>
#include "processor/address_map-inl.h"
#include "processor/linked_ptr.h"
#include "processor/logging.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))
namespace {
using google_breakpad::AddressMap;
using google_breakpad::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_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;
}
} // namespace
int main(int argc, char **argv) {
BPLOG_INIT(&argc, &argv);
return RunTests() ? 0 : 1;
}

View File

@@ -0,0 +1,110 @@
// 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.
// basic_code_module.h: Carries information about code modules that are loaded
// into a process.
//
// This is a basic concrete implementation of CodeModule. It cannot be
// instantiated directly, only based on other objects that implement
// the CodeModule interface. It exists to provide a CodeModule implementation
// a place to store information when the life of the original object (such as
// a MinidumpModule) cannot be guaranteed.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_BASIC_CODE_MODULE_H__
#define PROCESSOR_BASIC_CODE_MODULE_H__
#include <string>
#include "google_breakpad/processor/code_module.h"
namespace google_breakpad {
using std::string;
class BasicCodeModule : public CodeModule {
public:
// Creates a new BasicCodeModule given any existing CodeModule
// implementation. This is useful to make a copy of the data relevant to
// the CodeModule interface without requiring all of the resources that
// other CodeModule implementations may require.
explicit BasicCodeModule(const CodeModule *that)
: base_address_(that->base_address()),
size_(that->size()),
code_file_(that->code_file()),
code_identifier_(that->code_identifier()),
debug_file_(that->debug_file()),
debug_identifier_(that->debug_identifier()),
version_(that->version()) {}
BasicCodeModule(u_int64_t base_address, u_int64_t size,
const string &code_file,
const string &code_identifier,
const string &debug_file,
const string &debug_identifier,
const string &version)
: base_address_(base_address),
size_(size),
code_file_(code_file),
code_identifier_(code_identifier),
debug_file_(debug_file),
debug_identifier_(debug_identifier),
version_(version)
{}
virtual ~BasicCodeModule() {}
// See code_module.h for descriptions of these methods and the associated
// members.
virtual u_int64_t base_address() const { return base_address_; }
virtual u_int64_t size() const { return size_; }
virtual string code_file() const { return code_file_; }
virtual string code_identifier() const { return code_identifier_; }
virtual string debug_file() const { return debug_file_; }
virtual string debug_identifier() const { return debug_identifier_; }
virtual string version() const { return version_; }
virtual const CodeModule* Copy() const { return new BasicCodeModule(this); }
private:
u_int64_t base_address_;
u_int64_t size_;
string code_file_;
string code_identifier_;
string debug_file_;
string debug_identifier_;
string version_;
// Disallow copy constructor and assignment operator.
BasicCodeModule(const BasicCodeModule &that);
void operator=(const BasicCodeModule &that);
};
} // namespace google_breakpad
#endif // PROCESSOR_BASIC_CODE_MODULE_H__

View File

@@ -0,0 +1,123 @@
// 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.
// basic_code_modules.cc: Contains all of the CodeModule objects that
// were loaded into a single process.
//
// See basic_code_modules.h for documentation.
//
// Author: Mark Mentovai
#include "processor/basic_code_modules.h"
#include <assert.h>
#include "google_breakpad/processor/code_module.h"
#include "processor/linked_ptr.h"
#include "processor/logging.h"
#include "processor/range_map-inl.h"
namespace google_breakpad {
BasicCodeModules::BasicCodeModules(const CodeModules *that)
: main_address_(0),
map_(new RangeMap<u_int64_t, linked_ptr<const CodeModule> >()) {
BPLOG_IF(ERROR, !that) << "BasicCodeModules::BasicCodeModules requires "
"|that|";
assert(that);
const CodeModule *main_module = that->GetMainModule();
if (main_module)
main_address_ = main_module->base_address();
unsigned int count = that->module_count();
for (unsigned int module_sequence = 0;
module_sequence < count;
++module_sequence) {
// Make a copy of the module and insert it into the map. Use
// GetModuleAtIndex because ordering is unimportant when slurping the
// entire list, and GetModuleAtIndex may be faster than
// GetModuleAtSequence.
const CodeModule *module = that->GetModuleAtIndex(module_sequence)->Copy();
if (!map_->StoreRange(module->base_address(), module->size(),
linked_ptr<const CodeModule>(module))) {
BPLOG(ERROR) << "Module " << module->code_file() <<
" could not be stored";
}
}
}
BasicCodeModules::~BasicCodeModules() {
delete map_;
}
unsigned int BasicCodeModules::module_count() const {
return map_->GetCount();
}
const CodeModule* BasicCodeModules::GetModuleForAddress(
u_int64_t address) const {
linked_ptr<const CodeModule> module;
if (!map_->RetrieveRange(address, &module, NULL, NULL)) {
BPLOG(INFO) << "No module at " << HexString(address);
return NULL;
}
return module.get();
}
const CodeModule* BasicCodeModules::GetMainModule() const {
return GetModuleForAddress(main_address_);
}
const CodeModule* BasicCodeModules::GetModuleAtSequence(
unsigned int sequence) const {
linked_ptr<const CodeModule> module;
if (!map_->RetrieveRangeAtIndex(sequence, &module, NULL, NULL)) {
BPLOG(ERROR) << "RetrieveRangeAtIndex failed for sequence " << sequence;
return NULL;
}
return module.get();
}
const CodeModule* BasicCodeModules::GetModuleAtIndex(
unsigned int index) const {
// This class stores everything in a RangeMap, without any more-efficient
// way to walk the list of CodeModule objects. Implement GetModuleAtIndex
// using GetModuleAtSequence, which meets all of the requirements, and
// in addition, guarantees ordering.
return GetModuleAtSequence(index);
}
const CodeModules* BasicCodeModules::Copy() const {
return new BasicCodeModules(this);
}
} // namespace google_breakpad

View File

@@ -0,0 +1,85 @@
// 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.
// basic_code_modules.h: Contains all of the CodeModule objects that
// were loaded into a single process.
//
// This is a basic concrete implementation of CodeModules. It cannot be
// instantiated directly, only based on other objects that implement
// the CodeModules interface. It exists to provide a CodeModules
// implementation a place to store information when the life of the original
// object (such as a MinidumpModuleList) cannot be guaranteed.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_BASIC_CODE_MODULES_H__
#define PROCESSOR_BASIC_CODE_MODULES_H__
#include "google_breakpad/processor/code_modules.h"
namespace google_breakpad {
template<typename T> class linked_ptr;
template<typename AddressType, typename EntryType> class RangeMap;
class BasicCodeModules : public CodeModules {
public:
// Creates a new BasicCodeModules object given any existing CodeModules
// implementation. This is useful to make a copy of the data relevant to
// the CodeModules and CodeModule interfaces without requiring all of the
// resources that other implementations may require. A copy will be
// made of each contained CodeModule using CodeModule::Copy.
explicit BasicCodeModules(const CodeModules *that);
virtual ~BasicCodeModules();
// See code_modules.h for descriptions of these methods.
virtual unsigned int module_count() const;
virtual const CodeModule* GetModuleForAddress(u_int64_t address) const;
virtual const CodeModule* GetMainModule() const;
virtual const CodeModule* GetModuleAtSequence(unsigned int sequence) const;
virtual const CodeModule* GetModuleAtIndex(unsigned int index) const;
virtual const CodeModules* Copy() const;
private:
// The base address of the main module.
u_int64_t main_address_;
// The map used to contain each CodeModule, keyed by each CodeModule's
// address range.
RangeMap<u_int64_t, linked_ptr<const CodeModule> > *map_;
// Disallow copy constructor and assignment operator.
BasicCodeModules(const BasicCodeModules &that);
void operator=(const BasicCodeModules &that);
};
} // namespace google_breakpad
#endif // PROCESSOR_BASIC_CODE_MODULES_H__

View File

@@ -0,0 +1,451 @@
// Copyright (c) 2010 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.
//
// basic_source_line_resolver.cc: BasicSourceLineResolver implementation.
//
// See basic_source_line_resolver.h and basic_source_line_resolver_types.h
// for documentation.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <map>
#include <utility>
#include <vector>
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "processor/basic_source_line_resolver_types.h"
#include "processor/module_factory.h"
#include "processor/tokenize.h"
using std::map;
using std::vector;
using std::make_pair;
namespace google_breakpad {
static const char *kWhitespace = " \r\n";
BasicSourceLineResolver::BasicSourceLineResolver() :
SourceLineResolverBase(new BasicModuleFactory) { }
bool BasicSourceLineResolver::Module::LoadMapFromMemory(char *memory_buffer) {
linked_ptr<Function> cur_func;
int line_number = 0;
char *save_ptr;
size_t map_buffer_length = strlen(memory_buffer);
// If the length is 0, we can still pretend we have a symbol file. This is
// for scenarios that want to test symbol lookup, but don't necessarily care
// if certain modules do not have any information, like system libraries.
if (map_buffer_length == 0) {
return true;
}
if (memory_buffer[map_buffer_length - 1] == '\n') {
memory_buffer[map_buffer_length - 1] = '\0';
}
char *buffer;
buffer = strtok_r(memory_buffer, "\r\n", &save_ptr);
while (buffer != NULL) {
++line_number;
if (strncmp(buffer, "FILE ", 5) == 0) {
if (!ParseFile(buffer)) {
BPLOG(ERROR) << "ParseFile on buffer failed at " <<
":" << line_number;
return false;
}
} else if (strncmp(buffer, "STACK ", 6) == 0) {
if (!ParseStackInfo(buffer)) {
BPLOG(ERROR) << "ParseStackInfo failed at " <<
":" << line_number;
return false;
}
} else if (strncmp(buffer, "FUNC ", 5) == 0) {
cur_func.reset(ParseFunction(buffer));
if (!cur_func.get()) {
BPLOG(ERROR) << "ParseFunction failed at " <<
":" << line_number;
return false;
}
// StoreRange will fail if the function has an invalid address or size.
// We'll silently ignore this, the function and any corresponding lines
// will be destroyed when cur_func is released.
functions_.StoreRange(cur_func->address, cur_func->size, cur_func);
} else if (strncmp(buffer, "PUBLIC ", 7) == 0) {
// Clear cur_func: public symbols don't contain line number information.
cur_func.reset();
if (!ParsePublicSymbol(buffer)) {
BPLOG(ERROR) << "ParsePublicSymbol failed at " <<
":" << line_number;
return false;
}
} else if (strncmp(buffer, "MODULE ", 7) == 0) {
// Ignore these. They're not of any use to BasicSourceLineResolver,
// which is fed modules by a SymbolSupplier. These lines are present to
// aid other tools in properly placing symbol files so that they can
// be accessed by a SymbolSupplier.
//
// MODULE <guid> <age> <filename>
} else if (strncmp(buffer, "INFO ", 5) == 0) {
// Ignore these as well, they're similarly just for housekeeping.
//
// INFO CODE_ID <code id> <filename>
} else {
if (!cur_func.get()) {
BPLOG(ERROR) << "Found source line data without a function at " <<
":" << line_number;
return false;
}
Line *line = ParseLine(buffer);
if (!line) {
BPLOG(ERROR) << "ParseLine failed at " << line_number << " for " <<
buffer;
return false;
}
cur_func->lines.StoreRange(line->address, line->size,
linked_ptr<Line>(line));
}
buffer = strtok_r(NULL, "\r\n", &save_ptr);
}
return true;
}
void BasicSourceLineResolver::Module::LookupAddress(StackFrame *frame) const {
MemAddr address = frame->instruction - frame->module->base_address();
// First, look for a FUNC record that covers address. Use
// RetrieveNearestRange instead of RetrieveRange so that, if there
// is no such function, we can use the next function to bound the
// extent of the PUBLIC symbol we find, below. This does mean we
// need to check that address indeed falls within the function we
// find; do the range comparison in an overflow-friendly way.
linked_ptr<Function> 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) {
frame->function_name = func->name;
frame->function_base = frame->module->base_address() + 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_address() + line_base;
}
} else if (public_symbols_.Retrieve(address,
&public_symbol, &public_address) &&
(!func.get() || public_address > function_base)) {
frame->function_name = public_symbol->name;
frame->function_base = frame->module->base_address() + public_address;
}
}
WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo(
const StackFrame *frame) const {
MemAddr address = frame->instruction - frame->module->base_address();
scoped_ptr<WindowsFrameInfo> result(new WindowsFrameInfo());
// We only know about WindowsFrameInfo::STACK_INFO_FRAME_DATA and
// WindowsFrameInfo::STACK_INFO_FPO. Prefer them in this order.
// WindowsFrameInfo::STACK_INFO_FRAME_DATA is the newer type that
// includes its own program string.
// WindowsFrameInfo::STACK_INFO_FPO is the older type
// corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
linked_ptr<WindowsFrameInfo> frame_info;
if ((windows_frame_info_[WindowsFrameInfo::STACK_INFO_FRAME_DATA]
.RetrieveRange(address, &frame_info))
|| (windows_frame_info_[WindowsFrameInfo::STACK_INFO_FPO]
.RetrieveRange(address, &frame_info))) {
result->CopyFrom(*frame_info.get());
return result.release();
}
// Even without a relevant STACK line, many functions contain
// information about how much space their parameters consume on the
// stack. Use RetrieveNearestRange instead of RetrieveRange, so that
// we can use the function to bound the extent of the PUBLIC symbol,
// below. However, this does mean we need to check that ADDRESS
// falls within the retrieved function's range; do the range
// comparison in an overflow-friendly way.
linked_ptr<Function> function;
MemAddr function_base, function_size;
if (functions_.RetrieveNearestRange(address, &function,
&function_base, &function_size) &&
address >= function_base && address - function_base < function_size) {
result->parameter_size = function->parameter_size;
result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE;
return result.release();
}
// PUBLIC symbols might have a parameter size. Use the function we
// found above to limit the range the public symbol covers.
linked_ptr<PublicSymbol> public_symbol;
MemAddr public_address;
if (public_symbols_.Retrieve(address, &public_symbol, &public_address) &&
(!function.get() || public_address > function_base)) {
result->parameter_size = public_symbol->parameter_size;
}
return NULL;
}
CFIFrameInfo *BasicSourceLineResolver::Module::FindCFIFrameInfo(
const StackFrame *frame) const {
MemAddr address = frame->instruction - frame->module->base_address();
MemAddr initial_base, initial_size;
string initial_rules;
// Find the initial rule whose range covers this address. That
// provides an initial set of register recovery rules. Then, walk
// forward from the initial rule's starting address to frame's
// instruction address, applying delta rules.
if (!cfi_initial_rules_.RetrieveRange(address, &initial_rules,
&initial_base, &initial_size)) {
return NULL;
}
// Create a frame info structure, and populate it with the rules from
// the STACK CFI INIT record.
scoped_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
if (!ParseCFIRuleSet(initial_rules, rules.get()))
return NULL;
// Find the first delta rule that falls within the initial rule's range.
map<MemAddr, string>::const_iterator delta =
cfi_delta_rules_.lower_bound(initial_base);
// Apply delta rules up to and including the frame's address.
while (delta != cfi_delta_rules_.end() && delta->first <= address) {
ParseCFIRuleSet(delta->second, rules.get());
delta++;
}
return rules.release();
}
bool BasicSourceLineResolver::Module::ParseFile(char *file_line) {
// FILE <id> <filename>
file_line += 5; // skip prefix
vector<char*> tokens;
if (!Tokenize(file_line, kWhitespace, 2, &tokens)) {
return false;
}
int index = atoi(tokens[0]);
if (index < 0) {
return false;
}
char *filename = tokens[1];
if (!filename) {
return false;
}
files_.insert(make_pair(index, string(filename)));
return true;
}
BasicSourceLineResolver::Function*
BasicSourceLineResolver::Module::ParseFunction(char *function_line) {
// FUNC <address> <size> <stack_param_size> <name>
function_line += 5; // skip prefix
vector<char*> tokens;
if (!Tokenize(function_line, kWhitespace, 4, &tokens)) {
return NULL;
}
u_int64_t address = strtoull(tokens[0], NULL, 16);
u_int64_t size = strtoull(tokens[1], NULL, 16);
int stack_param_size = strtoull(tokens[2], NULL, 16);
char *name = tokens[3];
return new Function(name, address, size, stack_param_size);
}
BasicSourceLineResolver::Line* BasicSourceLineResolver::Module::ParseLine(
char *line_line) {
// <address> <line number> <source file id>
vector<char*> tokens;
if (!Tokenize(line_line, kWhitespace, 4, &tokens)) {
return NULL;
}
u_int64_t address = strtoull(tokens[0], NULL, 16);
u_int64_t size = strtoull(tokens[1], NULL, 16);
int line_number = atoi(tokens[2]);
int source_file = atoi(tokens[3]);
if (line_number <= 0) {
return NULL;
}
return new Line(address, size, source_file, line_number);
}
bool BasicSourceLineResolver::Module::ParsePublicSymbol(char *public_line) {
// PUBLIC <address> <stack_param_size> <name>
// Skip "PUBLIC " prefix.
public_line += 7;
vector<char*> tokens;
if (!Tokenize(public_line, kWhitespace, 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 BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
// Skip "STACK " prefix.
stack_info_line += 6;
// Find the token indicating what sort of stack frame walking
// information this is.
while (*stack_info_line == ' ')
stack_info_line++;
const char *platform = stack_info_line;
while (!strchr(kWhitespace, *stack_info_line))
stack_info_line++;
*stack_info_line++ = '\0';
// MSVC stack frame info.
if (strcmp(platform, "WIN") == 0) {
int type = 0;
u_int64_t rva, code_size;
linked_ptr<WindowsFrameInfo>
stack_frame_info(WindowsFrameInfo::ParseFromString(stack_info_line,
type,
rva,
code_size));
if (stack_frame_info == NULL)
return false;
// TODO(mmentovai): I wanted to use StoreRange's return value as this
// method's return value, but MSVC infrequently outputs stack info that
// violates the containment rules. This happens with a section of code
// in strncpy_s in test_app.cc (testdata/minidump2). There, problem looks
// like this:
// STACK WIN 4 4242 1a a 0 ... (STACK WIN 4 base size prolog 0 ...)
// STACK WIN 4 4243 2e 9 0 ...
// ContainedRangeMap treats these two blocks as conflicting. In reality,
// when the prolog lengths are taken into account, the actual code of
// these blocks doesn't conflict. However, we can't take the prolog lengths
// into account directly here because we'd wind up with a different set
// of range conflicts when MSVC outputs stack info like this:
// STACK WIN 4 1040 73 33 0 ...
// STACK WIN 4 105a 59 19 0 ...
// because in both of these entries, the beginning of the code after the
// prolog is at 0x1073, and the last byte of contained code is at 0x10b2.
// Perhaps we could get away with storing ranges by rva + prolog_size
// if ContainedRangeMap were modified to allow replacement of
// already-stored values.
windows_frame_info_[type].StoreRange(rva, code_size, stack_frame_info);
return true;
} else if (strcmp(platform, "CFI") == 0) {
// DWARF CFI stack frame info
return ParseCFIFrameInfo(stack_info_line);
} else {
// Something unrecognized.
return false;
}
}
bool BasicSourceLineResolver::Module::ParseCFIFrameInfo(
char *stack_info_line) {
char *cursor;
// Is this an INIT record or a delta record?
char *init_or_address = strtok_r(stack_info_line, " \r\n", &cursor);
if (!init_or_address)
return false;
if (strcmp(init_or_address, "INIT") == 0) {
// This record has the form "STACK INIT <address> <size> <rules...>".
char *address_field = strtok_r(NULL, " \r\n", &cursor);
if (!address_field) return false;
char *size_field = strtok_r(NULL, " \r\n", &cursor);
if (!size_field) return false;
char *initial_rules = strtok_r(NULL, "\r\n", &cursor);
if (!initial_rules) return false;
MemAddr address = strtoul(address_field, NULL, 16);
MemAddr size = strtoul(size_field, NULL, 16);
cfi_initial_rules_.StoreRange(address, size, initial_rules);
return true;
}
// This record has the form "STACK <address> <rules...>".
char *address_field = init_or_address;
char *delta_rules = strtok_r(NULL, "\r\n", &cursor);
if (!delta_rules) return false;
MemAddr address = strtoul(address_field, NULL, 16);
cfi_delta_rules_[address] = delta_rules;
return true;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,161 @@
// Copyright (c) 2010 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.
//
// basic_source_line_types.h: definition of nested classes/structs in
// BasicSourceLineResolver. It moves the definitions out of
// basic_source_line_resolver.cc, so that other classes could have access
// to these private nested types without including basic_source_line_resolver.cc
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__
#define PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__
#include <map>
#include <string>
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "processor/source_line_resolver_base_types.h"
#include "processor/address_map-inl.h"
#include "processor/range_map-inl.h"
#include "processor/contained_range_map-inl.h"
#include "processor/linked_ptr.h"
#include "processor/scoped_ptr.h"
#include "google_breakpad/processor/stack_frame.h"
#include "processor/cfi_frame_info.h"
#include "processor/windows_frame_info.h"
namespace google_breakpad {
struct
BasicSourceLineResolver::Function : public SourceLineResolverBase::Function {
Function(const string &function_name,
MemAddr function_address,
MemAddr code_size,
int set_parameter_size) : Base(function_name,
function_address,
code_size,
set_parameter_size),
lines() { }
RangeMap< MemAddr, linked_ptr<Line> > lines;
private:
typedef SourceLineResolverBase::Function Base;
};
class BasicSourceLineResolver::Module : public SourceLineResolverBase::Module {
public:
explicit Module(const string &name) : name_(name) { }
virtual ~Module() { }
// Loads a map from the given buffer in char* type.
// Does NOT have ownership of memory_buffer.
virtual bool LoadMapFromMemory(char *memory_buffer);
// Looks up the given relative address, and fills the StackFrame struct
// with the result.
virtual void LookupAddress(StackFrame *frame) const;
// If Windows stack walking information is available covering ADDRESS,
// return a WindowsFrameInfo structure describing it. If the information
// is not available, returns NULL. A NULL return value does not indicate
// an error. The caller takes ownership of any returned WindowsFrameInfo
// object.
virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const;
// If CFI stack walking information is available covering ADDRESS,
// return a CFIFrameInfo structure describing it. If the information
// is not available, return NULL. The caller takes ownership of any
// returned CFIFrameInfo object.
virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const;
private:
// Friend declarations.
friend class BasicSourceLineResolver;
friend class ModuleComparer;
friend class ModuleSerializer;
typedef std::map<int, string> FileMap;
// Parses a file declaration
bool ParseFile(char *file_line);
// Parses a function declaration, returning a new Function object.
Function* ParseFunction(char *function_line);
// 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 WIN or STACK CFI frame info declaration, storing
// it in the appropriate table.
bool ParseStackInfo(char *stack_info_line);
// Parses a STACK CFI record, storing it in cfi_frame_info_.
bool ParseCFIFrameInfo(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 WindowsFrameInfoTypes. These are split by type because
// there may be overlaps between maps of different types, but some
// information is only available as certain types.
ContainedRangeMap< MemAddr, linked_ptr<WindowsFrameInfo> >
windows_frame_info_[WindowsFrameInfo::STACK_INFO_LAST];
// DWARF CFI stack walking data. The Module stores the initial rule sets
// and rule deltas as strings, just as they appear in the symbol file:
// although the file may contain hundreds of thousands of STACK CFI
// records, walking a stack will only ever use a few of them, so it's
// best to delay parsing a record until it's actually needed.
// STACK CFI INIT records: for each range, an initial set of register
// recovery rules. The RangeMap's itself gives the starting and ending
// addresses.
RangeMap<MemAddr, string> cfi_initial_rules_;
// STACK CFI records: at a given address, the changes to the register
// recovery rules that take effect at that address. The map key is the
// starting address; the ending address is the key of the next entry in
// this map, or the end of the range as given by the cfi_initial_rules_
// entry (which FindCFIFrameInfo looks up first).
std::map<MemAddr, string> cfi_delta_rules_;
};
} // namespace google_breakpad
#endif // PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_TYPES_H__

View File

@@ -0,0 +1,407 @@
// Copyright (c) 2010 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.
#include <stdio.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/stack_frame.h"
#include "google_breakpad/processor/memory_region.h"
#include "processor/linked_ptr.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
#include "processor/windows_frame_info.h"
#include "processor/cfi_frame_info.h"
namespace {
using std::string;
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CFIFrameInfo;
using google_breakpad::CodeModule;
using google_breakpad::MemoryRegion;
using google_breakpad::StackFrame;
using google_breakpad::WindowsFrameInfo;
using google_breakpad::linked_ptr;
using google_breakpad::scoped_ptr;
class TestCodeModule : public CodeModule {
public:
TestCodeModule(string code_file) : code_file_(code_file) {}
virtual ~TestCodeModule() {}
virtual u_int64_t base_address() const { return 0; }
virtual u_int64_t size() const { return 0xb000; }
virtual string code_file() const { return code_file_; }
virtual string code_identifier() const { return ""; }
virtual string debug_file() const { return ""; }
virtual string debug_identifier() const { return ""; }
virtual string version() const { return ""; }
virtual const CodeModule* Copy() const {
return new TestCodeModule(code_file_);
}
private:
string code_file_;
};
// A mock memory region object, for use by the STACK CFI tests.
class MockMemoryRegion: public MemoryRegion {
u_int64_t GetBase() const { return 0x10000; }
u_int32_t GetSize() const { return 0x01000; }
bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
*value = address & 0xff;
return true;
}
bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
*value = address & 0xffff;
return true;
}
bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
switch (address) {
case 0x10008: *value = 0x98ecadc3; break; // saved %ebx
case 0x1000c: *value = 0x878f7524; break; // saved %esi
case 0x10010: *value = 0x6312f9a5; break; // saved %edi
case 0x10014: *value = 0x10038; break; // caller's %ebp
case 0x10018: *value = 0xf6438648; break; // return address
default: *value = 0xdeadbeef; break; // junk
}
return true;
}
bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
*value = address;
return true;
}
};
// Verify that, for every association in ACTUAL, EXPECTED has the same
// association. (That is, ACTUAL's associations should be a subset of
// EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
// ".cfa".
static bool VerifyRegisters(
const char *file, int line,
const CFIFrameInfo::RegisterValueMap<u_int32_t> &expected,
const CFIFrameInfo::RegisterValueMap<u_int32_t> &actual) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator a;
a = actual.find(".cfa");
if (a == actual.end())
return false;
a = actual.find(".ra");
if (a == actual.end())
return false;
for (a = actual.begin(); a != actual.end(); a++) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator e =
expected.find(a->first);
if (e == expected.end()) {
fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
file, line, a->first.c_str(), a->second);
return false;
}
if (e->second != a->second) {
fprintf(stderr,
"%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
file, line, a->first.c_str(), a->second, e->second);
return false;
}
// Don't complain if this doesn't recover all registers. Although
// the DWARF spec says that unmentioned registers are undefined,
// GCC uses omission to mean that they are unchanged.
}
return true;
}
static bool VerifyEmpty(const StackFrame &frame) {
if (frame.function_name.empty() &&
frame.source_file_name.empty() &&
frame.source_line == 0)
return true;
return false;
}
static void ClearSourceLineInfo(StackFrame *frame) {
frame->function_name.clear();
frame->module = NULL;
frame->source_file_name.clear();
frame->source_line = 0;
}
class TestBasicSourceLineResolver : public ::testing::Test {
public:
void SetUp() {
testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata";
}
BasicSourceLineResolver resolver;
string testdata_dir;
};
TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
{
TestCodeModule module1("module1");
ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out"));
ASSERT_TRUE(resolver.HasModule(&module1));
TestCodeModule module2("module2");
ASSERT_TRUE(resolver.LoadModule(&module2, testdata_dir + "/module2.out"));
ASSERT_TRUE(resolver.HasModule(&module2));
StackFrame frame;
scoped_ptr<WindowsFrameInfo> windows_frame_info;
scoped_ptr<CFIFrameInfo> cfi_frame_info;
frame.instruction = 0x1000;
frame.module = NULL;
resolver.FillSourceLineInfo(&frame);
ASSERT_FALSE(frame.module);
ASSERT_TRUE(frame.function_name.empty());
ASSERT_EQ(frame.function_base, 0);
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
ASSERT_EQ(frame.source_line_base, 0);
frame.module = &module1;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function1_1");
ASSERT_TRUE(frame.module);
ASSERT_EQ(frame.module->code_file(), "module1");
ASSERT_EQ(frame.function_base, 0x1000);
ASSERT_EQ(frame.source_file_name, "file1_1.cc");
ASSERT_EQ(frame.source_line, 44);
ASSERT_EQ(frame.source_line_base, 0x1000);
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_EQ(windows_frame_info->program_string,
"$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
ClearSourceLineInfo(&frame);
frame.instruction = 0x800;
frame.module = &module1;
resolver.FillSourceLineInfo(&frame);
ASSERT_TRUE(VerifyEmpty(frame));
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_FALSE(windows_frame_info.get());
frame.instruction = 0x1280;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function1_3");
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_TRUE(windows_frame_info->program_string.empty());
frame.instruction = 0x1380;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function1_4");
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_FALSE(windows_frame_info->program_string.empty());
frame.instruction = 0x2000;
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_FALSE(windows_frame_info.get());
// module1 has STACK CFI records covering 3d40..3def;
// module2 has STACK CFI records covering 3df0..3e9f;
// check that FindCFIFrameInfo doesn't claim to find any outside those ranges.
frame.instruction = 0x3d3f;
frame.module = &module1;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_FALSE(cfi_frame_info.get());
frame.instruction = 0x3e9f;
frame.module = &module1;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_FALSE(cfi_frame_info.get());
CFIFrameInfo::RegisterValueMap<u_int32_t> current_registers;
CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
CFIFrameInfo::RegisterValueMap<u_int32_t> expected_caller_registers;
MockMemoryRegion memory;
// Regardless of which instruction evaluation takes place at, it
// should produce the same values for the caller's registers.
expected_caller_registers[".cfa"] = 0x1001c;
expected_caller_registers[".ra"] = 0xf6438648;
expected_caller_registers["$ebp"] = 0x10038;
expected_caller_registers["$ebx"] = 0x98ecadc3;
expected_caller_registers["$esi"] = 0x878f7524;
expected_caller_registers["$edi"] = 0x6312f9a5;
frame.instruction = 0x3d40;
frame.module = &module1;
current_registers.clear();
current_registers["$esp"] = 0x10018;
current_registers["$ebp"] = 0x10038;
current_registers["$ebx"] = 0x98ecadc3;
current_registers["$esi"] = 0x878f7524;
current_registers["$edi"] = 0x6312f9a5;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers));
frame.instruction = 0x3d41;
current_registers["$esp"] = 0x10014;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers));
frame.instruction = 0x3d43;
current_registers["$ebp"] = 0x10014;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d54;
current_registers["$ebx"] = 0x6864f054U;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d5a;
current_registers["$esi"] = 0x6285f79aU;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d84;
current_registers["$edi"] = 0x64061449U;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x2900;
frame.module = &module1;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, string("PublicSymbol"));
frame.instruction = 0x4000;
frame.module = &module1;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, string("LargeFunction"));
frame.instruction = 0x2181;
frame.module = &module2;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function2_2");
ASSERT_EQ(frame.function_base, 0x2170);
ASSERT_TRUE(frame.module);
ASSERT_EQ(frame.module->code_file(), "module2");
ASSERT_EQ(frame.source_file_name, "file2_2.cc");
ASSERT_EQ(frame.source_line, 21);
ASSERT_EQ(frame.source_line_base, 0x2180);
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_EQ(windows_frame_info->prolog_size, 1);
frame.instruction = 0x216f;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Public2_1");
ClearSourceLineInfo(&frame);
frame.instruction = 0x219f;
frame.module = &module2;
resolver.FillSourceLineInfo(&frame);
ASSERT_TRUE(frame.function_name.empty());
frame.instruction = 0x21a0;
frame.module = &module2;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Public2_2");
}
TEST_F(TestBasicSourceLineResolver, TestInvalidLoads)
{
TestCodeModule module3("module3");
ASSERT_FALSE(resolver.LoadModule(&module3,
testdata_dir + "/module3_bad.out"));
ASSERT_FALSE(resolver.HasModule(&module3));
TestCodeModule module4("module4");
ASSERT_FALSE(resolver.LoadModule(&module4,
testdata_dir + "/module4_bad.out"));
ASSERT_FALSE(resolver.HasModule(&module4));
TestCodeModule module5("module5");
ASSERT_FALSE(resolver.LoadModule(&module5,
testdata_dir + "/invalid-filename"));
ASSERT_FALSE(resolver.HasModule(&module5));
TestCodeModule invalidmodule("invalid-module");
ASSERT_FALSE(resolver.HasModule(&invalidmodule));
}
TEST_F(TestBasicSourceLineResolver, TestUnload)
{
TestCodeModule module1("module1");
ASSERT_FALSE(resolver.HasModule(&module1));
ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out"));
ASSERT_TRUE(resolver.HasModule(&module1));
resolver.UnloadModule(&module1);
ASSERT_FALSE(resolver.HasModule(&module1));
ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out"));
ASSERT_TRUE(resolver.HasModule(&module1));
}
} // namespace
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,123 @@
// Copyright (c) 2010, 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.
#include <arpa/inet.h>
#include <limits.h>
#include <vector>
#include "processor/binarystream.h"
namespace google_breakpad {
using std::string;
using std::vector;
binarystream &binarystream::operator>>(std::string &str) {
u_int16_t length;
*this >> length;
if (eof())
return *this;
if (length == 0) {
str.clear();
return *this;
}
vector<char> buffer(length);
stream_.read(&buffer[0], length);
if (!eof())
str.assign(&buffer[0], length);
return *this;
}
binarystream &binarystream::operator>>(u_int8_t &u8) {
stream_.read((char *)&u8, 1);
return *this;
}
binarystream &binarystream::operator>>(u_int16_t &u16) {
u_int16_t temp;
stream_.read((char *)&temp, 2);
if (!eof())
u16 = ntohs(temp);
return *this;
}
binarystream &binarystream::operator>>(u_int32_t &u32) {
u_int32_t temp;
stream_.read((char *)&temp, 4);
if (!eof())
u32 = ntohl(temp);
return *this;
}
binarystream &binarystream::operator>>(u_int64_t &u64) {
u_int32_t lower, upper;
*this >> lower >> upper;
if (!eof())
u64 = static_cast<u_int64_t>(lower) | (static_cast<u_int64_t>(upper) << 32);
return *this;
}
binarystream &binarystream::operator<<(const std::string &str) {
if (str.length() > USHRT_MAX) {
// truncate to 16-bit length
*this << static_cast<u_int16_t>(USHRT_MAX);
stream_.write(str.c_str(), USHRT_MAX);
} else {
*this << (u_int16_t)(str.length() & 0xFFFF);
stream_.write(str.c_str(), str.length());
}
return *this;
}
binarystream &binarystream::operator<<(u_int8_t u8) {
stream_.write((const char*)&u8, 1);
return *this;
}
binarystream &binarystream::operator<<(u_int16_t u16) {
u16 = htons(u16);
stream_.write((const char*)&u16, 2);
return *this;
}
binarystream &binarystream::operator<<(u_int32_t u32) {
u32 = htonl(u32);
stream_.write((const char*)&u32, 4);
return *this;
}
binarystream &binarystream::operator<<(u_int64_t u64) {
// write 64-bit ints as two 32-bit ints, so we can byte-swap them easily
u_int32_t lower = static_cast<u_int32_t>(u64 & 0xFFFFFFFF);
u_int32_t upper = static_cast<u_int32_t>(u64 >> 32);
*this << lower << upper;
return *this;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,88 @@
// Copyright (c) 2010, 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.
// binarystream implements part of the std::iostream interface as a
// wrapper around std::stringstream to allow reading and writing
// std::string and integers of known size.
#ifndef GOOGLE_BREAKPAD_PROCESSOR_BINARYSTREAM_H_
#define GOOGLE_BREAKPAD_PROCESSOR_BINARYSTREAM_H_
#include <sstream>
#include <string>
#include "google_breakpad/common/breakpad_types.h"
namespace google_breakpad {
using std::ios_base;
using std::ios;
class binarystream {
public:
explicit binarystream(ios_base::openmode which = ios_base::out|ios_base::in)
: stream_(which) {}
explicit binarystream(const std::string &str,
ios_base::openmode which = ios_base::out|ios_base::in)
: stream_(str, which) {}
explicit binarystream(const char *str, size_t size,
ios_base::openmode which = ios_base::out|ios_base::in)
: stream_(std::string(str, size), which) {}
binarystream &operator>>(std::string &str);
binarystream &operator>>(u_int8_t &u8);
binarystream &operator>>(u_int16_t &u16);
binarystream &operator>>(u_int32_t &u32);
binarystream &operator>>(u_int64_t &u64);
// Note: strings are truncated at 65535 characters
binarystream &operator<<(const std::string &str);
binarystream &operator<<(u_int8_t u8);
binarystream &operator<<(u_int16_t u16);
binarystream &operator<<(u_int32_t u32);
binarystream &operator<<(u_int64_t u64);
// Forward a few methods directly from the stream object
bool eof() const { return stream_.eof(); }
void clear() { stream_.clear(); }
std::string str() const { return stream_.str(); }
void str(const std::string &s) { stream_.str(s); }
// Seek both read and write pointers to the beginning of the stream.
void rewind() {
stream_.seekg (0, ios::beg);
stream_.seekp (0, ios::beg);
}
private:
std::stringstream stream_;
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_PROCESSOR_BINARYSTREAM_H_

View File

@@ -0,0 +1,432 @@
// Copyright (c) 2010, 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.
#include <ios>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "processor/binarystream.h"
namespace {
using std::ios_base;
using std::string;
using std::vector;
using google_breakpad::binarystream;
class BinaryStreamBasicTest : public ::testing::Test {
protected:
binarystream stream;
};
TEST_F(BinaryStreamBasicTest, ReadU8) {
u_int8_t u8 = 0;
ASSERT_FALSE(stream.eof());
stream >> u8;
ASSERT_TRUE(stream.eof());
EXPECT_EQ(0, u8);
stream.rewind();
stream.clear();
stream << (u_int8_t)1;
ASSERT_FALSE(stream.eof());
stream >> u8;
EXPECT_EQ(1, u8);
EXPECT_FALSE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, ReadU16) {
u_int16_t u16 = 0;
ASSERT_FALSE(stream.eof());
stream >> u16;
ASSERT_TRUE(stream.eof());
EXPECT_EQ(0, u16);
stream.rewind();
stream.clear();
stream << (u_int16_t)1;
ASSERT_FALSE(stream.eof());
stream >> u16;
EXPECT_EQ(1, u16);
EXPECT_FALSE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, ReadU32) {
u_int32_t u32 = 0;
ASSERT_FALSE(stream.eof());
stream >> u32;
ASSERT_TRUE(stream.eof());
EXPECT_EQ(0, u32);
stream.rewind();
stream.clear();
stream << (u_int32_t)1;
ASSERT_FALSE(stream.eof());
stream >> u32;
EXPECT_EQ(1, u32);
EXPECT_FALSE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, ReadU64) {
u_int64_t u64 = 0;
ASSERT_FALSE(stream.eof());
stream >> u64;
ASSERT_TRUE(stream.eof());
EXPECT_EQ(0, u64);
stream.rewind();
stream.clear();
stream << (u_int64_t)1;
ASSERT_FALSE(stream.eof());
stream >> u64;
EXPECT_EQ(1, u64);
EXPECT_FALSE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, ReadString) {
string s("");
ASSERT_FALSE(stream.eof());
stream >> s;
ASSERT_TRUE(stream.eof());
EXPECT_EQ("", s);
// write an empty string to the stream, read it back
s = "abcd";
stream.rewind();
stream.clear();
stream << string("");
stream >> s;
EXPECT_EQ("", s);
EXPECT_FALSE(stream.eof());
stream.rewind();
stream.clear();
stream << string("test");
ASSERT_FALSE(stream.eof());
stream >> s;
EXPECT_EQ("test", s);
EXPECT_FALSE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, ReadEmptyString) {
string s("abc");
stream << string("");
stream >> s;
EXPECT_EQ("", s);
}
TEST_F(BinaryStreamBasicTest, ReadMultiU8) {
const u_int8_t ea = 0, eb = 100, ec = 200, ed = 0xFF;
u_int8_t a, b, c, d, e;
stream << ea << eb << ec << ed;
stream >> a >> b >> c >> d;
ASSERT_FALSE(stream.eof());
EXPECT_EQ(ea, a);
EXPECT_EQ(eb, b);
EXPECT_EQ(ec, c);
EXPECT_EQ(ed, d);
ASSERT_FALSE(stream.eof());
e = 0;
stream >> e;
EXPECT_EQ(0, e);
ASSERT_TRUE(stream.eof());
// try reading all at once, including one past eof
stream.rewind();
stream.clear();
ASSERT_FALSE(stream.eof());
a = b = c = d = e = 0;
stream << ea << eb << ec << ed;
stream >> a >> b >> c >> d >> e;
EXPECT_EQ(ea, a);
EXPECT_EQ(eb, b);
EXPECT_EQ(ec, c);
EXPECT_EQ(ed, d);
EXPECT_EQ(0, e);
EXPECT_TRUE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, ReadMultiU16) {
const u_int16_t ea = 0, eb = 0x100, ec = 0x8000, ed = 0xFFFF;
u_int16_t a, b, c, d, e;
stream << ea << eb << ec << ed;
stream >> a >> b >> c >> d;
ASSERT_FALSE(stream.eof());
EXPECT_EQ(ea, a);
EXPECT_EQ(eb, b);
EXPECT_EQ(ec, c);
EXPECT_EQ(ed, d);
ASSERT_FALSE(stream.eof());
e = 0;
stream >> e;
EXPECT_EQ(0, e);
EXPECT_TRUE(stream.eof());
// try reading all at once, including one past eof
stream.rewind();
stream.clear();
ASSERT_FALSE(stream.eof());
a = b = c = d = e = 0;
stream << ea << eb << ec << ed;
stream >> a >> b >> c >> d >> e;
EXPECT_EQ(ea, a);
EXPECT_EQ(eb, b);
EXPECT_EQ(ec, c);
EXPECT_EQ(ed, d);
EXPECT_EQ(0, e);
EXPECT_TRUE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, ReadMultiU32) {
const u_int32_t ea = 0, eb = 0x10000, ec = 0x8000000, ed = 0xFFFFFFFF;
u_int32_t a, b, c, d, e;
stream << ea << eb << ec << ed;
stream >> a >> b >> c >> d;
ASSERT_FALSE(stream.eof());
EXPECT_EQ(ea, a);
EXPECT_EQ(eb, b);
EXPECT_EQ(ec, c);
EXPECT_EQ(ed, d);
ASSERT_FALSE(stream.eof());
e = 0;
stream >> e;
EXPECT_EQ(0, e);
EXPECT_TRUE(stream.eof());
// try reading all at once, including one past eof
stream.rewind();
stream.clear();
ASSERT_FALSE(stream.eof());
a = b = c = d = e = 0;
stream << ea << eb << ec << ed;
stream >> a >> b >> c >> d >> e;
EXPECT_EQ(ea, a);
EXPECT_EQ(eb, b);
EXPECT_EQ(ec, c);
EXPECT_EQ(ed, d);
EXPECT_EQ(0, e);
EXPECT_TRUE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, ReadMultiU64) {
const u_int64_t ea = 0, eb = 0x10000, ec = 0x100000000ULL,
ed = 0xFFFFFFFFFFFFFFFFULL;
u_int64_t a, b, c, d, e;
stream << ea << eb << ec << ed;
stream >> a >> b >> c >> d;
ASSERT_FALSE(stream.eof());
EXPECT_EQ(ea, a);
EXPECT_EQ(eb, b);
EXPECT_EQ(ec, c);
EXPECT_EQ(ed, d);
ASSERT_FALSE(stream.eof());
e = 0;
stream >> e;
EXPECT_EQ(0, e);
EXPECT_TRUE(stream.eof());
// try reading all at once, including one past eof
stream.rewind();
stream.clear();
ASSERT_FALSE(stream.eof());
a = b = c = d = e = 0;
stream << ea << eb << ec << ed;
stream >> a >> b >> c >> d >> e;
EXPECT_EQ(ea, a);
EXPECT_EQ(eb, b);
EXPECT_EQ(ec, c);
EXPECT_EQ(ed, d);
EXPECT_EQ(0, e);
EXPECT_TRUE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, ReadMixed) {
const u_int8_t e8 = 0x10;
const u_int16_t e16 = 0x2020;
const u_int32_t e32 = 0x30303030;
const u_int64_t e64 = 0x4040404040404040ULL;
const string es = "test";
u_int8_t u8 = 0;
u_int16_t u16 = 0;
u_int32_t u32 = 0;
u_int64_t u64 = 0;
string s("test");
stream << e8 << e16 << e32 << e64 << es;
stream >> u8 >> u16 >> u32 >> u64 >> s;
EXPECT_FALSE(stream.eof());
EXPECT_EQ(e8, u8);
EXPECT_EQ(e16, u16);
EXPECT_EQ(e32, u32);
EXPECT_EQ(e64, u64);
EXPECT_EQ(es, s);
}
TEST_F(BinaryStreamBasicTest, ReadStringMissing) {
// ensure that reading a string where only the length is present fails
u_int16_t u16 = 8;
stream << u16;
stream.rewind();
string s("");
stream >> s;
EXPECT_EQ("", s);
EXPECT_TRUE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, ReadStringTruncated) {
// ensure that reading a string where not all the data is present fails
u_int16_t u16 = 8;
stream << u16;
stream << (u_int8_t)'t' << (u_int8_t)'e' << (u_int8_t)'s' << (u_int8_t)'t';
stream.rewind();
string s("");
stream >> s;
EXPECT_EQ("", s);
EXPECT_TRUE(stream.eof());
}
TEST_F(BinaryStreamBasicTest, StreamByteLength) {
// Test that the stream buffer contains the right amount of data
stream << (u_int8_t)0 << (u_int16_t)1 << (u_int32_t)2 << (u_int64_t)3
<< string("test");
string s = stream.str();
EXPECT_EQ(21, s.length());
}
TEST_F(BinaryStreamBasicTest, AppendStreamResultsByteLength) {
// Test that appending the str() results from two streams
// gives the right byte length
binarystream stream2;
stream << (u_int8_t)0 << (u_int16_t)1;
stream2 << (u_int32_t)0 << (u_int64_t)2
<< string("test");
string s = stream.str();
string s2 = stream2.str();
s.append(s2);
EXPECT_EQ(21, s.length());
}
TEST_F(BinaryStreamBasicTest, StreamSetStr) {
const string es("test");
stream << es;
binarystream stream2;
stream2.str(stream.str());
string s;
stream2 >> s;
EXPECT_FALSE(stream2.eof());
EXPECT_EQ("test", s);
s = "";
stream2.str(stream.str());
stream2.rewind();
stream2 >> s;
EXPECT_FALSE(stream2.eof());
EXPECT_EQ("test", s);
}
class BinaryStreamU8Test : public ::testing::Test {
protected:
binarystream stream;
void SetUp() {
stream << (u_int8_t)1;
}
};
TEST_F(BinaryStreamU8Test, ReadU16) {
u_int16_t u16 = 0;
ASSERT_FALSE(stream.eof());
stream >> u16;
ASSERT_TRUE(stream.eof());
EXPECT_EQ(0, u16);
}
TEST_F(BinaryStreamU8Test, ReadU32) {
u_int32_t u32 = 0;
ASSERT_FALSE(stream.eof());
stream >> u32;
ASSERT_TRUE(stream.eof());
EXPECT_EQ(0, u32);
}
TEST_F(BinaryStreamU8Test, ReadU64) {
u_int64_t u64 = 0;
ASSERT_FALSE(stream.eof());
stream >> u64;
ASSERT_TRUE(stream.eof());
EXPECT_EQ(0, u64);
}
TEST_F(BinaryStreamU8Test, ReadString) {
string s("");
ASSERT_FALSE(stream.eof());
stream >> s;
ASSERT_TRUE(stream.eof());
EXPECT_EQ("", s);
}
TEST(BinaryStreamTest, InitWithData) {
const char *data = "abcd";
binarystream stream(data);
u_int8_t a, b, c, d;
stream >> a >> b >> c >> d;
ASSERT_FALSE(stream.eof());
EXPECT_EQ('a', a);
EXPECT_EQ('b', b);
EXPECT_EQ('c', c);
EXPECT_EQ('d', d);
}
TEST(BinaryStreamTest, InitWithDataLeadingNull) {
const char *data = "\0abcd";
binarystream stream(data, 5);
u_int8_t z, a, b, c, d;
stream >> z >> a >> b >> c >> d;
ASSERT_FALSE(stream.eof());
EXPECT_EQ(0, z);
EXPECT_EQ('a', a);
EXPECT_EQ('b', b);
EXPECT_EQ('c', c);
EXPECT_EQ('d', d);
}
TEST(BinaryStreamTest, InitWithDataVector) {
vector<char> data;
data.push_back('a');
data.push_back('b');
data.push_back('c');
data.push_back('d');
data.push_back('e');
data.resize(4);
binarystream stream(&data[0], data.size());
u_int8_t a, b, c, d;
stream >> a >> b >> c >> d;
ASSERT_FALSE(stream.eof());
EXPECT_EQ('a', a);
EXPECT_EQ('b', b);
EXPECT_EQ('c', c);
EXPECT_EQ('d', d);
}
} // namespace
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,53 @@
// 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.
// call_stack.cc: A call stack comprised of stack frames.
//
// See call_stack.h for documentation.
//
// Author: Mark Mentovai
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/stack_frame.h"
namespace google_breakpad {
CallStack::~CallStack() {
Clear();
}
void CallStack::Clear() {
for (vector<StackFrame *>::const_iterator iterator = frames_.begin();
iterator != frames_.end();
++iterator) {
delete *iterator;
}
}
} // namespace google_breakpad

View File

@@ -0,0 +1,119 @@
// -*- mode: C++ -*-
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi_frame_info-inl.h: Definitions for cfi_frame_info.h inlined functions.
#ifndef PROCESSOR_CFI_FRAME_INFO_INL_H_
#define PROCESSOR_CFI_FRAME_INFO_INL_H_
#include <string.h>
namespace google_breakpad {
template <typename RegisterType, class RawContextType>
bool SimpleCFIWalker<RegisterType, RawContextType>::FindCallerRegisters(
const MemoryRegion &memory,
const CFIFrameInfo &cfi_frame_info,
const RawContextType &callee_context,
int callee_validity,
RawContextType *caller_context,
int *caller_validity) const {
typedef CFIFrameInfo::RegisterValueMap<RegisterType> ValueMap;
ValueMap callee_registers;
ValueMap caller_registers;
// Just for brevity.
typename ValueMap::const_iterator caller_none = caller_registers.end();
// Populate callee_registers with register values from callee_context.
for (size_t i = 0; i < map_size_; i++) {
const RegisterSet &r = register_map_[i];
if (callee_validity & r.validity_flag)
callee_registers[r.name] = callee_context.*r.context_member;
}
// Apply the rules, and see what register values they yield.
if (!cfi_frame_info.FindCallerRegs<RegisterType>(callee_registers, memory,
&caller_registers))
return false;
// Populate *caller_context with the values the rules placed in
// caller_registers.
memset(caller_context, 0xda, sizeof(*caller_context));
*caller_validity = 0;
for (size_t i = 0; i < map_size_; i++) {
const RegisterSet &r = register_map_[i];
typename ValueMap::const_iterator caller_entry;
// Did the rules provide a value for this register by its name?
caller_entry = caller_registers.find(r.name);
if (caller_entry != caller_none) {
caller_context->*r.context_member = caller_entry->second;
*caller_validity |= r.validity_flag;
continue;
}
// Did the rules provide a value for this register under its
// alternate name?
if (r.alternate_name) {
caller_entry = caller_registers.find(r.alternate_name);
if (caller_entry != caller_none) {
caller_context->*r.context_member = caller_entry->second;
*caller_validity |= r.validity_flag;
continue;
}
}
// Is this a callee-saves register? The walker assumes that these
// still hold the caller's value if the CFI doesn't mention them.
//
// Note that other frame walkers may fail to recover callee-saves
// registers; for example, the x86 "traditional" strategy only
// recovers %eip, %esp, and %ebp, even though %ebx, %esi, and %edi
// are callee-saves, too. It is not correct to blindly set the
// valid bit for all callee-saves registers, without first
// checking its validity bit in the callee.
if (r.callee_saves && (callee_validity & r.validity_flag) != 0) {
caller_context->*r.context_member = callee_context.*r.context_member;
*caller_validity |= r.validity_flag;
continue;
}
// Otherwise, the register's value is unknown.
}
return true;
}
} // namespace google_breakpad
#endif // PROCESSOR_CFI_FRAME_INFO_INL_H_

View File

@@ -0,0 +1,182 @@
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi_frame_info.cc: Implementation of CFIFrameInfo class.
// See cfi_frame_info.h for details.
#include "processor/cfi_frame_info.h"
#include <string.h>
#include <sstream>
#include "processor/postfix_evaluator-inl.h"
#include "processor/scoped_ptr.h"
namespace google_breakpad {
template<typename V>
bool CFIFrameInfo::FindCallerRegs(const RegisterValueMap<V> &registers,
const MemoryRegion &memory,
RegisterValueMap<V> *caller_registers) const {
// If there are not rules for both .ra and .cfa in effect at this address,
// don't use this CFI data for stack walking.
if (cfa_rule_.empty() || ra_rule_.empty())
return false;
RegisterValueMap<V> working;
PostfixEvaluator<V> evaluator(&working, &memory);
caller_registers->clear();
// First, compute the CFA.
V cfa;
working = registers;
if (!evaluator.EvaluateForValue(cfa_rule_, &cfa))
return false;
// Then, compute the return address.
V ra;
working = registers;
working[".cfa"] = cfa;
if (!evaluator.EvaluateForValue(ra_rule_, &ra))
return false;
// Now, compute values for all the registers register_rules_ mentions.
for (RuleMap::const_iterator it = register_rules_.begin();
it != register_rules_.end(); it++) {
V value;
working = registers;
working[".cfa"] = cfa;
if (!evaluator.EvaluateForValue(it->second, &value))
return false;
(*caller_registers)[it->first] = value;
}
(*caller_registers)[".ra"] = ra;
(*caller_registers)[".cfa"] = cfa;
return true;
}
// Explicit instantiations for 32-bit and 64-bit architectures.
template bool CFIFrameInfo::FindCallerRegs<u_int32_t>(
const RegisterValueMap<u_int32_t> &registers,
const MemoryRegion &memory,
RegisterValueMap<u_int32_t> *caller_registers) const;
template bool CFIFrameInfo::FindCallerRegs<u_int64_t>(
const RegisterValueMap<u_int64_t> &registers,
const MemoryRegion &memory,
RegisterValueMap<u_int64_t> *caller_registers) const;
string CFIFrameInfo::Serialize() const {
std::ostringstream stream;
if (!cfa_rule_.empty()) {
stream << ".cfa: " << cfa_rule_;
}
if (!ra_rule_.empty()) {
if (static_cast<std::streamoff>(stream.tellp()) != 0)
stream << " ";
stream << ".ra: " << ra_rule_;
}
for (RuleMap::const_iterator iter = register_rules_.begin();
iter != register_rules_.end();
++iter) {
if (static_cast<std::streamoff>(stream.tellp()) != 0)
stream << " ";
stream << iter->first << ": " << iter->second;
}
return stream.str();
}
bool CFIRuleParser::Parse(const string &rule_set) {
size_t rule_set_len = rule_set.size();
scoped_array<char> working_copy(new char[rule_set_len + 1]);
memcpy(working_copy.get(), rule_set.data(), rule_set_len);
working_copy[rule_set_len] = '\0';
name_.clear();
expression_.clear();
char *cursor;
static const char token_breaks[] = " \t\r\n";
char *token = strtok_r(working_copy.get(), token_breaks, &cursor);
for (;;) {
// End of rule set?
if (!token) return Report();
// Register/pseudoregister name?
size_t token_len = strlen(token);
if (token_len >= 1 && token[token_len - 1] == ':') {
// Names can't be empty.
if (token_len < 2) return false;
// If there is any pending content, report it.
if (!name_.empty() || !expression_.empty()) {
if (!Report()) return false;
}
name_.assign(token, token_len - 1);
expression_.clear();
} else {
// Another expression component.
assert(token_len > 0); // strtok_r guarantees this, I think.
if (!expression_.empty())
expression_ += ' ';
expression_ += token;
}
token = strtok_r(NULL, token_breaks, &cursor);
}
}
bool CFIRuleParser::Report() {
if (name_.empty() || expression_.empty()) return false;
if (name_ == ".cfa") handler_->CFARule(expression_);
else if (name_ == ".ra") handler_->RARule(expression_);
else handler_->RegisterRule(name_, expression_);
return true;
}
void CFIFrameInfoParseHandler::CFARule(const string &expression) {
frame_info_->SetCFARule(expression);
}
void CFIFrameInfoParseHandler::RARule(const string &expression) {
frame_info_->SetRARule(expression);
}
void CFIFrameInfoParseHandler::RegisterRule(const string &name,
const string &expression) {
frame_info_->SetRegisterRule(name, expression);
}
} // namespace google_breakpad

View File

@@ -0,0 +1,275 @@
// -*- mode: C++ -*-
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi_frame_info.h: Define the CFIFrameInfo class, which holds the
// set of 'STACK CFI'-derived register recovery rules that apply at a
// given instruction.
#ifndef PROCESSOR_CFI_FRAME_INFO_H_
#define PROCESSOR_CFI_FRAME_INFO_H_
#include <map>
#include <string>
#include "google_breakpad/common/breakpad_types.h"
namespace google_breakpad {
using std::map;
using std::string;
class MemoryRegion;
// A set of rules for recovering the calling frame's registers'
// values, when the PC is at a given address in the current frame's
// function. See the description of 'STACK CFI' records at:
//
// http://code.google.com/p/google-breakpad/wiki/SymbolFiles
//
// To prepare an instance of CFIFrameInfo for use at a given
// instruction, first populate it with the rules from the 'STACK CFI
// INIT' record that covers that instruction, and then apply the
// changes given by the 'STACK CFI' records up to our instruction's
// address. Then, use the FindCallerRegs member function to apply the
// rules to the callee frame's register values, yielding the caller
// frame's register values.
class CFIFrameInfo {
public:
// A map from register names onto values.
template<typename ValueType> class RegisterValueMap:
public map<string, ValueType> { };
// Set the expression for computing a call frame address, return
// address, or register's value. At least the CFA rule and the RA
// rule must be set before calling FindCallerRegs.
void SetCFARule(const string &expression) { cfa_rule_ = expression; }
void SetRARule(const string &expression) { ra_rule_ = expression; }
void SetRegisterRule(const string &register_name, const string &expression) {
register_rules_[register_name] = expression;
}
// Compute the values of the calling frame's registers, according to
// this rule set. Use ValueType in expression evaluation; this
// should be u_int32_t on machines with 32-bit addresses, or
// u_int64_t on machines with 64-bit addresses.
//
// Return true on success, false otherwise.
//
// MEMORY provides access to the contents of the stack. REGISTERS is
// a dictionary mapping the names of registers whose values are
// known in the current frame to their values. CALLER_REGISTERS is
// populated with the values of the recoverable registers in the
// frame that called the current frame.
//
// In addition, CALLER_REGISTERS[".ra"] will be the return address,
// and CALLER_REGISTERS[".cfa"] will be the call frame address.
// These may be helpful in computing the caller's PC and stack
// pointer, if their values are not explicitly specified.
template<typename ValueType>
bool FindCallerRegs(const RegisterValueMap<ValueType> &registers,
const MemoryRegion &memory,
RegisterValueMap<ValueType> *caller_registers) const;
// Serialize the rules in this object into a string in the format
// of STACK CFI records.
string Serialize() const;
private:
// A map from register names onto evaluation rules.
typedef map<string, string> RuleMap;
// In this type, a "postfix expression" is an expression of the sort
// interpreted by google_breakpad::PostfixEvaluator.
// A postfix expression for computing the current frame's CFA (call
// frame address). The CFA is a reference address for the frame that
// remains unchanged throughout the frame's lifetime. You should
// evaluate this expression with a dictionary initially populated
// with the values of the current frame's known registers.
string cfa_rule_;
// The following expressions should be evaluated with a dictionary
// initially populated with the values of the current frame's known
// registers, and with ".cfa" set to the result of evaluating the
// cfa_rule expression, above.
// A postfix expression for computing the current frame's return
// address.
string ra_rule_;
// For a register named REG, rules[REG] is a postfix expression
// which leaves the value of REG in the calling frame on the top of
// the stack. You should evaluate this expression
RuleMap register_rules_;
};
// A parser for STACK CFI-style rule sets.
// This may seem bureaucratic: there's no legitimate run-time reason
// to use a parser/handler pattern for this, as it's not a likely
// reuse boundary. But doing so makes finer-grained unit testing
// possible.
class CFIRuleParser {
public:
class Handler {
public:
Handler() { }
virtual ~Handler() { }
// The input specifies EXPRESSION as the CFA/RA computation rule.
virtual void CFARule(const string &expression) = 0;
virtual void RARule(const string &expression) = 0;
// The input specifies EXPRESSION as the recovery rule for register NAME.
virtual void RegisterRule(const string &name, const string &expression) = 0;
};
// Construct a parser which feeds its results to HANDLER.
CFIRuleParser(Handler *handler) : handler_(handler) { }
// Parse RULE_SET as a set of CFA computation and RA/register
// recovery rules, as appearing in STACK CFI records. Report the
// results of parsing by making the appropriate calls to handler_.
// Return true if parsing was successful, false otherwise.
bool Parse(const string &rule_set);
private:
// Report any accumulated rule to handler_
bool Report();
// The handler to which the parser reports its findings.
Handler *handler_;
// Working data.
string name_, expression_;
};
// A handler for rule set parsing that populates a CFIFrameInfo with
// the results.
class CFIFrameInfoParseHandler: public CFIRuleParser::Handler {
public:
// Populate FRAME_INFO with the results of parsing.
CFIFrameInfoParseHandler(CFIFrameInfo *frame_info)
: frame_info_(frame_info) { }
void CFARule(const string &expression);
void RARule(const string &expression);
void RegisterRule(const string &name, const string &expression);
private:
CFIFrameInfo *frame_info_;
};
// A utility class template for simple 'STACK CFI'-driven stack walkers.
// Given a CFIFrameInfo instance, a table describing the architecture's
// register set, and a context holding the last frame's registers, an
// instance of this class can populate a new context with the caller's
// registers.
//
// This class template doesn't use any internal knowledge of CFIFrameInfo
// or the other stack walking structures; it just uses the public interface
// of CFIFrameInfo to do the usual things. But the logic it handles should
// be common to many different architectures' stack walkers, so wrapping it
// up in a class should allow the walkers to share code.
//
// RegisterType should be the type of this architecture's registers, either
// u_int32_t or u_int64_t. RawContextType should be the raw context
// structure type for this architecture.
template <typename RegisterType, class RawContextType>
class SimpleCFIWalker {
public:
// A structure describing one architecture register.
struct RegisterSet {
// The register name, as it appears in STACK CFI rules.
const char *name;
// An alternate name that the register's value might be found
// under in a register value dictionary, or NULL. When generating
// names, prefer NAME to this value. It's common to list ".cfa" as
// an alternative name for the stack pointer, and ".ra" as an
// alternative name for the instruction pointer.
const char *alternate_name;
// True if the callee is expected to preserve the value of this
// register. If this flag is true for some register R, and the STACK
// CFI records provide no rule to recover R, then SimpleCFIWalker
// assumes that the callee has not changed R's value, and the caller's
// value for R is that currently in the callee's context.
bool callee_saves;
// The ContextValidity flag representing the register's presence.
int validity_flag;
// A pointer to the RawContextType member that holds the
// register's value.
RegisterType RawContextType::*context_member;
};
// Create a simple CFI-based frame walker, given a description of the
// architecture's register set. REGISTER_MAP is an array of
// RegisterSet structures; MAP_SIZE is the number of elements in the
// array.
SimpleCFIWalker(const RegisterSet *register_map, size_t map_size)
: register_map_(register_map), map_size_(map_size) { }
// Compute the calling frame's raw context given the callee's raw
// context.
//
// Given:
//
// - MEMORY, holding the stack's contents,
// - CFI_FRAME_INFO, describing the called function,
// - CALLEE_CONTEXT, holding the called frame's registers, and
// - CALLEE_VALIDITY, indicating which registers in CALLEE_CONTEXT are valid,
//
// fill in CALLER_CONTEXT with the caller's register values, and set
// CALLER_VALIDITY to indicate which registers are valid in
// CALLER_CONTEXT. Return true on success, or false on failure.
bool FindCallerRegisters(const MemoryRegion &memory,
const CFIFrameInfo &cfi_frame_info,
const RawContextType &callee_context,
int callee_validity,
RawContextType *caller_context,
int *caller_validity) const;
private:
const RegisterSet *register_map_;
size_t map_size_;
};
} // namespace google_breakpad
#include "cfi_frame_info-inl.h"
#endif // PROCESSOR_CFI_FRAME_INFO_H_

View File

@@ -0,0 +1,545 @@
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi_frame_info_unittest.cc: Unit tests for CFIFrameInfo,
// CFIRuleParser, CFIFrameInfoParseHandler, and SimpleCFIWalker.
#include <string.h>
#include "breakpad_googletest_includes.h"
#include "processor/cfi_frame_info.h"
#include "google_breakpad/processor/memory_region.h"
using google_breakpad::CFIFrameInfo;
using google_breakpad::CFIFrameInfoParseHandler;
using google_breakpad::CFIRuleParser;
using google_breakpad::MemoryRegion;
using google_breakpad::SimpleCFIWalker;
using std::string;
using testing::_;
using testing::A;
using testing::AtMost;
using testing::DoAll;
using testing::Return;
using testing::SetArgumentPointee;
using testing::Test;
class MockMemoryRegion: public MemoryRegion {
public:
MOCK_CONST_METHOD0(GetBase, u_int64_t());
MOCK_CONST_METHOD0(GetSize, u_int32_t());
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int8_t *));
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int16_t *));
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int32_t *));
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int64_t *));
};
// Handy definitions for all tests.
struct CFIFixture {
// Set up the mock memory object to expect no references.
void ExpectNoMemoryReferences() {
EXPECT_CALL(memory, GetBase()).Times(0);
EXPECT_CALL(memory, GetSize()).Times(0);
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int8_t *>())).Times(0);
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int16_t *>())).Times(0);
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int32_t *>())).Times(0);
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int64_t *>())).Times(0);
}
CFIFrameInfo cfi;
MockMemoryRegion memory;
CFIFrameInfo::RegisterValueMap<u_int64_t> registers, caller_registers;
};
class Simple: public CFIFixture, public Test { };
// FindCallerRegs should fail if no .cfa rule is provided.
TEST_F(Simple, NoCFA) {
ExpectNoMemoryReferences();
cfi.SetRARule("0");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(".ra: 0", cfi.Serialize());
}
// FindCallerRegs should fail if no .ra rule is provided.
TEST_F(Simple, NoRA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("0");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(".cfa: 0", cfi.Serialize());
}
TEST_F(Simple, SetCFAAndRARule) {
ExpectNoMemoryReferences();
cfi.SetCFARule("330903416631436410");
cfi.SetRARule("5870666104170902211");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(2U, caller_registers.size());
ASSERT_EQ(330903416631436410ULL, caller_registers[".cfa"]);
ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]);
ASSERT_EQ(".cfa: 330903416631436410 .ra: 5870666104170902211",
cfi.Serialize());
}
TEST_F(Simple, SetManyRules) {
ExpectNoMemoryReferences();
cfi.SetCFARule("$temp1 68737028 = $temp2 61072337 = $temp1 $temp2 -");
cfi.SetRARule(".cfa 99804755 +");
cfi.SetRegisterRule("register1", ".cfa 54370437 *");
cfi.SetRegisterRule("vodkathumbscrewingly", "24076308 .cfa +");
cfi.SetRegisterRule("pubvexingfjordschmaltzy", ".cfa 29801007 -");
cfi.SetRegisterRule("uncopyrightables", "92642917 .cfa /");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(6U, caller_registers.size());
ASSERT_EQ(7664691U, caller_registers[".cfa"]);
ASSERT_EQ(107469446U, caller_registers[".ra"]);
ASSERT_EQ(416732599139967ULL, caller_registers["register1"]);
ASSERT_EQ(31740999U, caller_registers["vodkathumbscrewingly"]);
ASSERT_EQ(-22136316ULL, caller_registers["pubvexingfjordschmaltzy"]);
ASSERT_EQ(12U, caller_registers["uncopyrightables"]);
ASSERT_EQ(".cfa: $temp1 68737028 = $temp2 61072337 = $temp1 $temp2 - "
".ra: .cfa 99804755 + "
"pubvexingfjordschmaltzy: .cfa 29801007 - "
"register1: .cfa 54370437 * "
"uncopyrightables: 92642917 .cfa / "
"vodkathumbscrewingly: 24076308 .cfa +",
cfi.Serialize());
}
TEST_F(Simple, RulesOverride) {
ExpectNoMemoryReferences();
cfi.SetCFARule("330903416631436410");
cfi.SetRARule("5870666104170902211");
cfi.SetCFARule("2828089117179001");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(2U, caller_registers.size());
ASSERT_EQ(2828089117179001ULL, caller_registers[".cfa"]);
ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]);
ASSERT_EQ(".cfa: 2828089117179001 .ra: 5870666104170902211",
cfi.Serialize());
}
class Scope: public CFIFixture, public Test { };
// There should be no value for .cfa in scope when evaluating the CFA rule.
TEST_F(Scope, CFALacksCFA) {
ExpectNoMemoryReferences();
cfi.SetCFARule(".cfa");
cfi.SetRARule("0");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
// There should be no value for .ra in scope when evaluating the CFA rule.
TEST_F(Scope, CFALacksRA) {
ExpectNoMemoryReferences();
cfi.SetCFARule(".ra");
cfi.SetRARule("0");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
// The current frame's registers should be in scope when evaluating
// the CFA rule.
TEST_F(Scope, CFASeesCurrentRegs) {
ExpectNoMemoryReferences();
registers[".baraminology"] = 0x06a7bc63e4f13893ULL;
registers[".ornithorhynchus"] = 0x5e0bf850bafce9d2ULL;
cfi.SetCFARule(".baraminology .ornithorhynchus +");
cfi.SetRARule("0");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(2U, caller_registers.size());
ASSERT_EQ(0x06a7bc63e4f13893ULL + 0x5e0bf850bafce9d2ULL,
caller_registers[".cfa"]);
}
// .cfa should be in scope in the return address expression.
TEST_F(Scope, RASeesCFA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("48364076");
cfi.SetRARule(".cfa");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(2U, caller_registers.size());
ASSERT_EQ(48364076U, caller_registers[".ra"]);
}
// There should be no value for .ra in scope when evaluating the CFA rule.
TEST_F(Scope, RALacksRA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("0");
cfi.SetRARule(".ra");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
// The current frame's registers should be in scope in the return
// address expression.
TEST_F(Scope, RASeesCurrentRegs) {
ExpectNoMemoryReferences();
registers["noachian"] = 0x54dc4a5d8e5eb503ULL;
cfi.SetCFARule("10359370");
cfi.SetRARule("noachian");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(2U, caller_registers.size());
ASSERT_EQ(0x54dc4a5d8e5eb503ULL, caller_registers[".ra"]);
}
// .cfa should be in scope for register rules.
TEST_F(Scope, RegistersSeeCFA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("6515179");
cfi.SetRARule(".cfa");
cfi.SetRegisterRule("rogerian", ".cfa");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(3U, caller_registers.size());
ASSERT_EQ(6515179U, caller_registers["rogerian"]);
}
// The return address should not be in scope for register rules.
TEST_F(Scope, RegsLackRA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("42740329");
cfi.SetRARule("27045204");
cfi.SetRegisterRule("$r1", ".ra");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
// Register rules can see the current frame's register values.
TEST_F(Scope, RegsSeeRegs) {
ExpectNoMemoryReferences();
registers["$r1"] = 0x6ed3582c4bedb9adULL;
registers["$r2"] = 0xd27d9e742b8df6d0ULL;
cfi.SetCFARule("88239303");
cfi.SetRARule("30503835");
cfi.SetRegisterRule("$r1", "$r1 42175211 = $r2");
cfi.SetRegisterRule("$r2", "$r2 21357221 = $r1");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(4U, caller_registers.size());
ASSERT_EQ(0xd27d9e742b8df6d0ULL, caller_registers["$r1"]);
ASSERT_EQ(0x6ed3582c4bedb9adULL, caller_registers["$r2"]);
}
// Each rule's temporaries are separate.
TEST_F(Scope, SeparateTempsRA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("$temp1 76569129 = $temp1");
cfi.SetRARule("0");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
cfi.SetCFARule("$temp1 76569129 = $temp1");
cfi.SetRARule("$temp1");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
class MockCFIRuleParserHandler: public CFIRuleParser::Handler {
public:
MOCK_METHOD1(CFARule, void(const string &));
MOCK_METHOD1(RARule, void(const string &));
MOCK_METHOD2(RegisterRule, void(const string &, const string &));
};
// A fixture class for testing CFIRuleParser.
class CFIParserFixture {
public:
CFIParserFixture() : parser(&mock_handler) {
// Expect no parsing results to be reported to mock_handler. Individual
// tests can override this.
EXPECT_CALL(mock_handler, CFARule(_)).Times(0);
EXPECT_CALL(mock_handler, RARule(_)).Times(0);
EXPECT_CALL(mock_handler, RegisterRule(_, _)).Times(0);
}
MockCFIRuleParserHandler mock_handler;
CFIRuleParser parser;
};
class Parser: public CFIParserFixture, public Test { };
TEST_F(Parser, Empty) {
EXPECT_FALSE(parser.Parse(""));
}
TEST_F(Parser, LoneColon) {
EXPECT_FALSE(parser.Parse(":"));
}
TEST_F(Parser, CFANoExpr) {
EXPECT_FALSE(parser.Parse(".cfa:"));
}
TEST_F(Parser, CFANoColonNoExpr) {
EXPECT_FALSE(parser.Parse(".cfa"));
}
TEST_F(Parser, RANoExpr) {
EXPECT_FALSE(parser.Parse(".ra:"));
}
TEST_F(Parser, RANoColonNoExpr) {
EXPECT_FALSE(parser.Parse(".ra"));
}
TEST_F(Parser, RegNoExpr) {
EXPECT_FALSE(parser.Parse("reg:"));
}
TEST_F(Parser, NoName) {
EXPECT_FALSE(parser.Parse("expr"));
}
TEST_F(Parser, NoNameTwo) {
EXPECT_FALSE(parser.Parse("expr1 expr2"));
}
TEST_F(Parser, StartsWithExpr) {
EXPECT_FALSE(parser.Parse("expr1 reg: expr2"));
}
TEST_F(Parser, CFA) {
EXPECT_CALL(mock_handler, CFARule("spleen")).WillOnce(Return());
EXPECT_TRUE(parser.Parse(".cfa: spleen"));
}
TEST_F(Parser, RA) {
EXPECT_CALL(mock_handler, RARule("notoriety")).WillOnce(Return());
EXPECT_TRUE(parser.Parse(".ra: notoriety"));
}
TEST_F(Parser, Reg) {
EXPECT_CALL(mock_handler, RegisterRule("nemo", "mellifluous"))
.WillOnce(Return());
EXPECT_TRUE(parser.Parse("nemo: mellifluous"));
}
TEST_F(Parser, CFARARegs) {
EXPECT_CALL(mock_handler, CFARule("cfa expression")).WillOnce(Return());
EXPECT_CALL(mock_handler, RARule("ra expression")).WillOnce(Return());
EXPECT_CALL(mock_handler, RegisterRule("galba", "praetorian"))
.WillOnce(Return());
EXPECT_CALL(mock_handler, RegisterRule("otho", "vitellius"))
.WillOnce(Return());
EXPECT_TRUE(parser.Parse(".cfa: cfa expression .ra: ra expression "
"galba: praetorian otho: vitellius"));
}
TEST_F(Parser, Whitespace) {
EXPECT_CALL(mock_handler, RegisterRule("r1", "r1 expression"))
.WillOnce(Return());
EXPECT_CALL(mock_handler, RegisterRule("r2", "r2 expression"))
.WillOnce(Return());
EXPECT_TRUE(parser.Parse(" r1:\tr1\nexpression \tr2:\t\rr2\r\n "
"expression \n"));
}
TEST_F(Parser, WhitespaceLoneColon) {
EXPECT_FALSE(parser.Parse(" \n:\t "));
}
TEST_F(Parser, EmptyName) {
EXPECT_CALL(mock_handler, RegisterRule("reg", _))
.Times(AtMost(1))
.WillRepeatedly(Return());
EXPECT_FALSE(parser.Parse("reg: expr1 : expr2"));
}
TEST_F(Parser, RuleLoneColon) {
EXPECT_CALL(mock_handler, RegisterRule("r1", "expr"))
.Times(AtMost(1))
.WillRepeatedly(Return());
EXPECT_FALSE(parser.Parse(" r1: expr :"));
}
TEST_F(Parser, RegNoExprRule) {
EXPECT_CALL(mock_handler, RegisterRule("r1", "expr"))
.Times(AtMost(1))
.WillRepeatedly(Return());
EXPECT_FALSE(parser.Parse("r0: r1: expr"));
}
class ParseHandlerFixture: public CFIFixture {
public:
ParseHandlerFixture() : CFIFixture(), handler(&cfi) { }
CFIFrameInfoParseHandler handler;
};
class ParseHandler: public ParseHandlerFixture, public Test { };
TEST_F(ParseHandler, CFARARule) {
handler.CFARule("reg-for-cfa");
handler.RARule("reg-for-ra");
registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]);
ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]);
}
TEST_F(ParseHandler, RegisterRules) {
handler.CFARule("reg-for-cfa");
handler.RARule("reg-for-ra");
handler.RegisterRule("reg1", "reg-for-reg1");
handler.RegisterRule("reg2", "reg-for-reg2");
registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
registers["reg-for-reg1"] = 0x06cde8e2ff062481ULL;
registers["reg-for-reg2"] = 0xff0c4f76403173e2ULL;
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]);
ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]);
ASSERT_EQ(0x06cde8e2ff062481ULL, caller_registers["reg1"]);
ASSERT_EQ(0xff0c4f76403173e2ULL, caller_registers["reg2"]);
}
struct SimpleCFIWalkerFixture {
struct RawContext {
u_int64_t r0, r1, r2, r3, r4, sp, pc;
};
enum Validity {
R0_VALID = 0x01,
R1_VALID = 0x02,
R2_VALID = 0x04,
R3_VALID = 0x08,
R4_VALID = 0x10,
SP_VALID = 0x20,
PC_VALID = 0x40
};
typedef SimpleCFIWalker<u_int64_t, RawContext> CFIWalker;
SimpleCFIWalkerFixture()
: walker(register_map,
sizeof(register_map) / sizeof(register_map[0])) { }
static CFIWalker::RegisterSet register_map[7];
CFIFrameInfo call_frame_info;
CFIWalker walker;
MockMemoryRegion memory;
RawContext callee_context, caller_context;
};
SimpleCFIWalkerFixture::CFIWalker::RegisterSet
SimpleCFIWalkerFixture::register_map[7] = {
{ "r0", NULL, true, R0_VALID, &RawContext::r0 },
{ "r1", NULL, true, R1_VALID, &RawContext::r1 },
{ "r2", NULL, false, R2_VALID, &RawContext::r2 },
{ "r3", NULL, false, R3_VALID, &RawContext::r3 },
{ "r4", NULL, true, R4_VALID, &RawContext::r4 },
{ "sp", ".cfa", true, SP_VALID, &RawContext::sp },
{ "pc", ".ra", true, PC_VALID, &RawContext::pc },
};
class SimpleWalker: public SimpleCFIWalkerFixture, public Test { };
TEST_F(SimpleWalker, Walk) {
// Stack_top is the current stack pointer, pointing to the lowest
// address of a frame that looks like this (all 64-bit words):
//
// sp -> saved r0
// garbage
// return address
// cfa ->
//
// r0 has been saved on the stack.
// r1 has been saved in r2.
// r2 and r3 are not recoverable.
// r4 is not recoverable, even though it is a callee-saves register.
// Some earlier frame's unwinder must have failed to recover it.
u_int64_t stack_top = 0x83254944b20d5512ULL;
// Saved r0.
EXPECT_CALL(memory,
GetMemoryAtAddress(stack_top, A<u_int64_t *>()))
.WillRepeatedly(DoAll(SetArgumentPointee<1>(0xdc1975eba8602302ULL),
Return(true)));
// Saved return address.
EXPECT_CALL(memory,
GetMemoryAtAddress(stack_top + 16, A<u_int64_t *>()))
.WillRepeatedly(DoAll(SetArgumentPointee<1>(0xba5ad6d9acce28deULL),
Return(true)));
call_frame_info.SetCFARule("sp 24 +");
call_frame_info.SetRARule(".cfa 8 - ^");
call_frame_info.SetRegisterRule("r0", ".cfa 24 - ^");
call_frame_info.SetRegisterRule("r1", "r2");
callee_context.r0 = 0x94e030ca79edd119ULL;
callee_context.r1 = 0x937b4d7e95ce52d9ULL;
callee_context.r2 = 0x5fe0027416b8b62aULL; // caller's r1
// callee_context.r3 is not valid in callee.
// callee_context.r4 is not valid in callee.
callee_context.sp = stack_top;
callee_context.pc = 0x25b21b224311d280ULL;
int callee_validity = R0_VALID | R1_VALID | R2_VALID | SP_VALID | PC_VALID;
memset(&caller_context, 0, sizeof(caller_context));
int caller_validity;
EXPECT_TRUE(walker.FindCallerRegisters(memory, call_frame_info,
callee_context, callee_validity,
&caller_context, &caller_validity));
EXPECT_EQ(R0_VALID | R1_VALID | SP_VALID | PC_VALID, caller_validity);
EXPECT_EQ(0xdc1975eba8602302ULL, caller_context.r0);
EXPECT_EQ(0x5fe0027416b8b62aULL, caller_context.r1);
EXPECT_EQ(stack_top + 24, caller_context.sp);
EXPECT_EQ(0xba5ad6d9acce28deULL, caller_context.pc);
}

View File

@@ -0,0 +1,197 @@
// 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.
// contained_range_map-inl.h: Hierarchically-organized range map implementation.
//
// See contained_range_map.h for documentation.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_CONTAINED_RANGE_MAP_INL_H__
#define PROCESSOR_CONTAINED_RANGE_MAP_INL_H__
#include "processor/contained_range_map.h"
#include <assert.h>
#include "processor/logging.h"
namespace google_breakpad {
template<typename AddressType, typename EntryType>
ContainedRangeMap<AddressType, EntryType>::~ContainedRangeMap() {
// Clear frees the children pointed to by the map, and frees the map itself.
Clear();
}
template<typename AddressType, typename EntryType>
bool ContainedRangeMap<AddressType, EntryType>::StoreRange(
const AddressType &base, const AddressType &size, const EntryType &entry) {
AddressType high = base + size - 1;
// Check for undersize or overflow.
if (size <= 0 || high < base) {
//TODO(nealsid) We are commenting this out in order to prevent
// excessive logging. We plan to move to better logging as this
// failure happens quite often and is expected(see comment in
// basic_source_line_resolver.cc:671).
// BPLOG(INFO) << "StoreRange failed, " << HexString(base) << "+"
// << HexString(size) << ", " << HexString(high);
return false;
}
if (!map_)
map_ = new AddressToRangeMap();
MapIterator iterator_base = map_->lower_bound(base);
MapIterator iterator_high = map_->lower_bound(high);
MapIterator iterator_end = map_->end();
if (iterator_base == iterator_high && iterator_base != iterator_end &&
base >= iterator_base->second->base_) {
// The new range is entirely within an existing child range.
// If the new range's geometry is exactly equal to an existing child
// range's, it violates the containment rules, and an attempt to store
// it must fail. iterator_base->first contains the key, which was the
// containing child's high address.
if (iterator_base->second->base_ == base && iterator_base->first == high) {
// TODO(nealsid): See the TODO above on why this is commented out.
// BPLOG(INFO) << "StoreRange failed, identical range is already "
// "present: " << HexString(base) << "+" << HexString(size);
return false;
}
// Pass the new range on to the child to attempt to store.
return iterator_base->second->StoreRange(base, size, entry);
}
// iterator_high might refer to an irrelevant range: one whose base address
// is higher than the new range's high address. Set contains_high to true
// only if iterator_high refers to a range that is at least partially
// within the new range.
bool contains_high = iterator_high != iterator_end &&
high >= iterator_high->second->base_;
// If the new range encompasses any existing child ranges, it must do so
// fully. Partial containment isn't allowed.
if ((iterator_base != iterator_end && base > iterator_base->second->base_) ||
(contains_high && high < iterator_high->first)) {
// TODO(mmentovai): Some symbol files will trip this check frequently
// on STACK lines. Too many messages will be produced. These are more
// suitable for a DEBUG channel than an INFO channel.
// BPLOG(INFO) << "StoreRange failed, new range partially contains "
// "existing range: " << HexString(base) << "+" <<
// HexString(size);
return false;
}
// When copying and erasing contained ranges, the "end" iterator needs to
// point one past the last item of the range to copy. If contains_high is
// false, the iterator's already in the right place; the increment is safe
// because contains_high can't be true if iterator_high == iterator_end.
if (contains_high)
++iterator_high;
// Optimization: if the iterators are equal, no child ranges would be
// moved. Create the new child range with a NULL map to conserve space
// in leaf nodes, of which there will be many.
AddressToRangeMap *child_map = NULL;
if (iterator_base != iterator_high) {
// The children of this range that are contained by the new range must
// be transferred over to the new range. Create the new child range map
// and copy the pointers to range maps it should contain into it.
child_map = new AddressToRangeMap(iterator_base, iterator_high);
// Remove the copied child pointers from this range's map of children.
map_->erase(iterator_base, iterator_high);
}
// Store the new range in the map by its high address. Any children that
// the new child range contains were formerly children of this range but
// are now this range's grandchildren. Ownership of these is transferred
// to the new child range.
map_->insert(MapValue(high,
new ContainedRangeMap(base, entry, child_map)));
return true;
}
template<typename AddressType, typename EntryType>
bool ContainedRangeMap<AddressType, EntryType>::RetrieveRange(
const AddressType &address, EntryType *entry) const {
BPLOG_IF(ERROR, !entry) << "ContainedRangeMap::RetrieveRange requires "
"|entry|";
assert(entry);
// If nothing was ever stored, then there's nothing to retrieve.
if (!map_)
return false;
// Get an iterator to the child range whose high address is equal to or
// greater than the supplied address. If the supplied address is higher
// than all of the high addresses in the range, then this range does not
// contain a child at address, so return false. If the supplied address
// is lower than the base address of the child range, then it is not within
// the child range, so return false.
MapConstIterator iterator = map_->lower_bound(address);
if (iterator == map_->end() || address < iterator->second->base_)
return false;
// The child in iterator->second contains the specified address. Find out
// if it has a more-specific descendant that also contains it. If it does,
// it will set |entry| appropriately. If not, set |entry| to the child.
if (!iterator->second->RetrieveRange(address, entry))
*entry = iterator->second->entry_;
return true;
}
template<typename AddressType, typename EntryType>
void ContainedRangeMap<AddressType, EntryType>::Clear() {
if (map_) {
MapConstIterator end = map_->end();
for (MapConstIterator child = map_->begin(); child != end; ++child)
delete child->second;
delete map_;
map_ = NULL;
}
}
} // namespace google_breakpad
#endif // PROCESSOR_CONTAINED_RANGE_MAP_INL_H__

View File

@@ -0,0 +1,150 @@
// 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.
// contained_range_map.h: Hierarchically-organized range maps.
//
// A contained range map is similar to a standard range map, except it allows
// objects to be organized hierarchically. A contained range map allows
// objects to contain other objects. It is not sensitive to the order that
// objects are added to the map: larger, more general, containing objects
// may be added either before or after smaller, more specific, contained
// ones.
//
// Contained range maps guarantee that each object may only contain smaller
// objects than itself, and that a parent object may only contain child
// objects located entirely within the parent's address space. Attempts
// to introduce objects (via StoreRange) that violate these rules will fail.
// Retrieval (via RetrieveRange) always returns the most specific (smallest)
// object that contains the address being queried. Note that while it is
// not possible to insert two objects into a map that have exactly the same
// geometry (base address and size), it is possible to completely mask a
// larger object by inserting smaller objects that entirely fill the larger
// object's address space.
//
// Internally, contained range maps are implemented as a tree. Each tree
// node except for the root node describes an object in the map. Each node
// maintains its list of children in a map similar to a standard range map,
// keyed by the highest address that each child occupies. Each node's
// children occupy address ranges entirely within the node. The root node
// is the only node directly accessible to the user, and represents the
// entire address space.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_CONTAINED_RANGE_MAP_H__
#define PROCESSOR_CONTAINED_RANGE_MAP_H__
#include <map>
namespace google_breakpad {
// Forward declarations (for later friend declarations of specialized template).
template<class, class> class ContainedRangeMapSerializer;
template<typename AddressType, typename EntryType>
class ContainedRangeMap {
public:
// The default constructor creates a ContainedRangeMap with no geometry
// and no entry, and as such is only suitable for the root node of a
// ContainedRangeMap tree.
ContainedRangeMap() : base_(), entry_(), map_(NULL) {}
~ContainedRangeMap();
// Inserts a range into the map. If the new range is encompassed by
// an existing child range, the new range is passed into the child range's
// StoreRange method. If the new range encompasses any existing child
// ranges, those child ranges are moved to the new range, becoming
// grandchildren of this ContainedRangeMap. Returns false for a
// parameter error, or if the ContainedRangeMap hierarchy guarantees
// would be violated.
bool StoreRange(const AddressType &base,
const AddressType &size,
const EntryType &entry);
// Retrieves the most specific (smallest) descendant range encompassing
// the specified address. This method will only return entries held by
// child ranges, and not the entry contained by |this|. This is necessary
// to support a sparsely-populated root range. If no descendant range
// encompasses the address, returns false.
bool RetrieveRange(const AddressType &address, EntryType *entry) const;
// Removes all children. Note that Clear only removes descendants,
// leaving the node on which it is called intact. Because the only
// meaningful things contained by a root node are descendants, this
// is sufficient to restore an entire ContainedRangeMap to its initial
// empty state when called on the root node.
void Clear();
private:
friend class ContainedRangeMapSerializer<AddressType, EntryType>;
friend class ModuleComparer;
// AddressToRangeMap stores pointers. This makes reparenting simpler in
// StoreRange, because it doesn't need to copy entire objects.
typedef std::map<AddressType, ContainedRangeMap *> AddressToRangeMap;
typedef typename AddressToRangeMap::const_iterator MapConstIterator;
typedef typename AddressToRangeMap::iterator MapIterator;
typedef typename AddressToRangeMap::value_type MapValue;
// Creates a new ContainedRangeMap with the specified base address, entry,
// and initial child map, which may be NULL. This is only used internally
// by ContainedRangeMap when it creates a new child.
ContainedRangeMap(const AddressType &base, const EntryType &entry,
AddressToRangeMap *map)
: base_(base), entry_(entry), map_(map) {}
// The base address of this range. The high address does not need to
// be stored, because it is used as the key to an object in its parent's
// map, and all ContainedRangeMaps except for the root range are contained
// within maps. The root range does not actually contain an entry, so its
// base_ field is meaningless, and the fact that it has no parent and thus
// no key is unimportant. For this reason, the base_ field should only be
// is accessed on child ContainedRangeMap objects, and never on |this|.
const AddressType base_;
// The entry corresponding to this range. The root range does not
// actually contain an entry, so its entry_ field is meaningless. For
// this reason, the entry_ field should only be accessed on child
// ContainedRangeMap objects, and never on |this|.
const EntryType entry_;
// The map containing child ranges, keyed by each child range's high
// address. This is a pointer to avoid allocating map structures for
// leaf nodes, where they are not needed.
AddressToRangeMap *map_;
};
} // namespace google_breakpad
#endif // PROCESSOR_CONTAINED_RANGE_MAP_H__

View File

@@ -0,0 +1,263 @@
// 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.
// contained_range_map_unittest.cc: Unit tests for ContainedRangeMap
//
// Author: Mark Mentovai
#include <stdio.h>
#include "processor/contained_range_map-inl.h"
#include "processor/logging.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))
namespace {
using google_breakpad::ContainedRangeMap;
static bool RunTests() {
ContainedRangeMap<unsigned int, int> crm;
// First, do the StoreRange tests. This validates the containment
// rules.
ASSERT_TRUE (crm.StoreRange(10, 10, 1));
ASSERT_FALSE(crm.StoreRange(10, 10, 2)); // exactly equal to 1
ASSERT_FALSE(crm.StoreRange(11, 10, 3)); // begins inside 1 and extends up
ASSERT_FALSE(crm.StoreRange( 9, 10, 4)); // begins below 1 and ends inside
ASSERT_TRUE (crm.StoreRange(11, 9, 5)); // contained by existing
ASSERT_TRUE (crm.StoreRange(12, 7, 6));
ASSERT_TRUE (crm.StoreRange( 9, 12, 7)); // contains existing
ASSERT_TRUE (crm.StoreRange( 9, 13, 8));
ASSERT_TRUE (crm.StoreRange( 8, 14, 9));
ASSERT_TRUE (crm.StoreRange(30, 3, 10));
ASSERT_TRUE (crm.StoreRange(33, 3, 11));
ASSERT_TRUE (crm.StoreRange(30, 6, 12)); // storable but totally masked
ASSERT_TRUE (crm.StoreRange(40, 8, 13)); // will be totally masked
ASSERT_TRUE (crm.StoreRange(40, 4, 14));
ASSERT_TRUE (crm.StoreRange(44, 4, 15));
ASSERT_FALSE(crm.StoreRange(32, 10, 16)); // begins in #10, ends in #14
ASSERT_FALSE(crm.StoreRange(50, 0, 17)); // zero length
ASSERT_TRUE (crm.StoreRange(50, 10, 18));
ASSERT_TRUE (crm.StoreRange(50, 1, 19));
ASSERT_TRUE (crm.StoreRange(59, 1, 20));
ASSERT_TRUE (crm.StoreRange(60, 1, 21));
ASSERT_TRUE (crm.StoreRange(69, 1, 22));
ASSERT_TRUE (crm.StoreRange(60, 10, 23));
ASSERT_TRUE (crm.StoreRange(68, 1, 24));
ASSERT_TRUE (crm.StoreRange(61, 1, 25));
ASSERT_TRUE (crm.StoreRange(61, 8, 26));
ASSERT_FALSE(crm.StoreRange(59, 9, 27));
ASSERT_FALSE(crm.StoreRange(59, 10, 28));
ASSERT_FALSE(crm.StoreRange(59, 11, 29));
ASSERT_TRUE (crm.StoreRange(70, 10, 30));
ASSERT_TRUE (crm.StoreRange(74, 2, 31));
ASSERT_TRUE (crm.StoreRange(77, 2, 32));
ASSERT_FALSE(crm.StoreRange(72, 6, 33));
ASSERT_TRUE (crm.StoreRange(80, 3, 34));
ASSERT_TRUE (crm.StoreRange(81, 1, 35));
ASSERT_TRUE (crm.StoreRange(82, 1, 36));
ASSERT_TRUE (crm.StoreRange(83, 3, 37));
ASSERT_TRUE (crm.StoreRange(84, 1, 38));
ASSERT_TRUE (crm.StoreRange(83, 1, 39));
ASSERT_TRUE (crm.StoreRange(86, 5, 40));
ASSERT_TRUE (crm.StoreRange(88, 1, 41));
ASSERT_TRUE (crm.StoreRange(90, 1, 42));
ASSERT_TRUE (crm.StoreRange(86, 1, 43));
ASSERT_TRUE (crm.StoreRange(87, 1, 44));
ASSERT_TRUE (crm.StoreRange(89, 1, 45));
ASSERT_TRUE (crm.StoreRange(87, 4, 46));
ASSERT_TRUE (crm.StoreRange(87, 3, 47));
ASSERT_FALSE(crm.StoreRange(86, 2, 48));
// Each element in test_data contains the expected result when calling
// RetrieveRange on an address.
const int test_data[] = {
0, // 0
0, // 1
0, // 2
0, // 3
0, // 4
0, // 5
0, // 6
0, // 7
9, // 8
7, // 9
1, // 10
5, // 11
6, // 12
6, // 13
6, // 14
6, // 15
6, // 16
6, // 17
6, // 18
5, // 19
7, // 20
8, // 21
0, // 22
0, // 23
0, // 24
0, // 25
0, // 26
0, // 27
0, // 28
0, // 29
10, // 30
10, // 31
10, // 32
11, // 33
11, // 34
11, // 35
0, // 36
0, // 37
0, // 38
0, // 39
14, // 40
14, // 41
14, // 42
14, // 43
15, // 44
15, // 45
15, // 46
15, // 47
0, // 48
0, // 49
19, // 50
18, // 51
18, // 52
18, // 53
18, // 54
18, // 55
18, // 56
18, // 57
18, // 58
20, // 59
21, // 60
25, // 61
26, // 62
26, // 63
26, // 64
26, // 65
26, // 66
26, // 67
24, // 68
22, // 69
30, // 70
30, // 71
30, // 72
30, // 73
31, // 74
31, // 75
30, // 76
32, // 77
32, // 78
30, // 79
34, // 80
35, // 81
36, // 82
39, // 83
38, // 84
37, // 85
43, // 86
44, // 87
41, // 88
45, // 89
42, // 90
0, // 91
0, // 92
0, // 93
0, // 94
0, // 95
0, // 96
0, // 97
0, // 98
0 // 99
};
unsigned int test_high = sizeof(test_data) / sizeof(int);
// Now, do the RetrieveRange tests. This further validates that the
// objects were stored properly and that retrieval returns the correct
// object.
// If GENERATE_TEST_DATA is defined, instead of the retrieval tests, a
// new test_data array will be printed. Exercise caution when doing this.
// Be sure to verify the results manually!
#ifdef GENERATE_TEST_DATA
printf(" const int test_data[] = {\n");
#endif // GENERATE_TEST_DATA
for (unsigned int address = 0; address < test_high; ++address) {
int value;
if (!crm.RetrieveRange(address, &value))
value = 0;
#ifndef GENERATE_TEST_DATA
// Don't use ASSERT inside the loop because it won't show the failed
// |address|, and the line number will always be the same. That makes
// it difficult to figure out which test failed.
if (value != test_data[address]) {
fprintf(stderr, "FAIL: retrieve %d expected %d observed %d @ %s:%d\n",
address, test_data[address], value, __FILE__, __LINE__);
return false;
}
#else // !GENERATE_TEST_DATA
printf(" %d%c%s // %d\n", value,
address == test_high - 1 ? ' ' : ',',
value < 10 ? " " : "",
address);
#endif // !GENERATE_TEST_DATA
}
#ifdef GENERATE_TEST_DATA
printf(" };\n");
#endif // GENERATE_TEST_DATA
return true;
}
} // namespace
int main(int argc, char **argv) {
BPLOG_INIT(&argc, &argv);
return RunTests() ? 0 : 1;
}

View File

@@ -0,0 +1,238 @@
// 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.
// disassembler_x86.cc: simple x86 disassembler.
//
// Provides single step disassembly of x86 bytecode and flags instructions
// that utilize known bad register values.
//
// Author: Cris Neckar
#include "processor/disassembler_x86.h"
#include <string.h>
#include <unistd.h>
namespace google_breakpad {
DisassemblerX86::DisassemblerX86(const u_int8_t *bytecode,
u_int32_t size,
u_int32_t virtual_address) :
bytecode_(bytecode),
size_(size),
virtual_address_(virtual_address),
current_byte_offset_(0),
current_inst_offset_(0),
instr_valid_(false),
register_valid_(false),
pushed_bad_value_(false),
end_of_block_(false),
flags_(0) {
libdis::x86_init(libdis::opt_none, NULL, NULL);
}
DisassemblerX86::~DisassemblerX86() {
libdis::x86_cleanup();
}
u_int32_t DisassemblerX86::NextInstruction() {
if (instr_valid_)
libdis::x86_oplist_free(&current_instr_);
if (current_byte_offset_ >= size_) {
instr_valid_ = false;
return 0;
}
u_int32_t instr_size = 0;
instr_size = libdis::x86_disasm((unsigned char *)bytecode_, size_,
virtual_address_, current_byte_offset_,
&current_instr_);
if (instr_size == 0) {
instr_valid_ = false;
return 0;
}
current_byte_offset_ += instr_size;
current_inst_offset_++;
instr_valid_ = libdis::x86_insn_is_valid(&current_instr_);
if (!instr_valid_)
return 0;
if (current_instr_.type == libdis::insn_return)
end_of_block_ = true;
libdis::x86_op_t *src = libdis::x86_get_src_operand(&current_instr_);
libdis::x86_op_t *dest = libdis::x86_get_dest_operand(&current_instr_);
if (register_valid_) {
switch (current_instr_.group) {
// Flag branches based off of bad registers and calls that occur
// after pushing bad values.
case libdis::insn_controlflow:
switch (current_instr_.type) {
case libdis::insn_jmp:
case libdis::insn_jcc:
case libdis::insn_call:
case libdis::insn_callcc:
if (dest) {
switch (dest->type) {
case libdis::op_expression:
if (dest->data.expression.base.id == bad_register_.id)
flags_ |= DISX86_BAD_BRANCH_TARGET;
break;
case libdis::op_register:
if (dest->data.reg.id == bad_register_.id)
flags_ |= DISX86_BAD_BRANCH_TARGET;
break;
default:
if (pushed_bad_value_ &&
(current_instr_.type == libdis::insn_call ||
current_instr_.type == libdis::insn_callcc))
flags_ |= DISX86_BAD_ARGUMENT_PASSED;
break;
}
}
break;
default:
break;
}
break;
// Flag block data operations that use bad registers for src or dest.
case libdis::insn_string:
if (dest && dest->type == libdis::op_expression &&
dest->data.expression.base.id == bad_register_.id)
flags_ |= DISX86_BAD_BLOCK_WRITE;
if (src && src->type == libdis::op_expression &&
src->data.expression.base.id == bad_register_.id)
flags_ |= DISX86_BAD_BLOCK_READ;
break;
// Flag comparisons based on bad data.
case libdis::insn_comparison:
if ((dest && dest->type == libdis::op_expression &&
dest->data.expression.base.id == bad_register_.id) ||
(src && src->type == libdis::op_expression &&
src->data.expression.base.id == bad_register_.id) ||
(dest && dest->type == libdis::op_register &&
dest->data.reg.id == bad_register_.id) ||
(src && src->type == libdis::op_register &&
src->data.reg.id == bad_register_.id))
flags_ |= DISX86_BAD_COMPARISON;
break;
// Flag any other instruction which derefs a bad register for
// src or dest.
default:
if (dest && dest->type == libdis::op_expression &&
dest->data.expression.base.id == bad_register_.id)
flags_ |= DISX86_BAD_WRITE;
if (src && src->type == libdis::op_expression &&
src->data.expression.base.id == bad_register_.id)
flags_ |= DISX86_BAD_READ;
break;
}
}
// When a register is marked as tainted check if it is pushed.
// TODO(cdn): may also want to check for MOVs into EBP offsets.
if (register_valid_ && dest && current_instr_.type == libdis::insn_push) {
switch (dest->type) {
case libdis::op_expression:
if (dest->data.expression.base.id == bad_register_.id ||
dest->data.expression.index.id == bad_register_.id)
pushed_bad_value_ = true;
break;
case libdis::op_register:
if (dest->data.reg.id == bad_register_.id)
pushed_bad_value_ = true;
break;
default:
break;
}
}
// Check if a tainted register value is clobbered.
// For conditional MOVs and XCHGs assume that
// there is a hit.
if (register_valid_) {
switch (current_instr_.type) {
case libdis::insn_xor:
if (src && src->type == libdis::op_register &&
dest && dest->type == libdis::op_register &&
src->data.reg.id == bad_register_.id &&
src->data.reg.id == dest->data.reg.id)
register_valid_ = false;
break;
case libdis::insn_pop:
case libdis::insn_mov:
case libdis::insn_movcc:
if (dest && dest->type == libdis::op_register &&
dest->data.reg.id == bad_register_.id)
register_valid_ = false;
break;
case libdis::insn_popregs:
register_valid_ = false;
break;
case libdis::insn_xchg:
case libdis::insn_xchgcc:
if (dest && dest->type == libdis::op_register &&
src && src->type == libdis::op_register) {
if (dest->data.reg.id == bad_register_.id)
memcpy(&bad_register_, &src->data.reg, sizeof(libdis::x86_reg_t));
else if (src->data.reg.id == bad_register_.id)
memcpy(&bad_register_, &dest->data.reg, sizeof(libdis::x86_reg_t));
}
break;
default:
break;
}
}
return instr_size;
}
bool DisassemblerX86::setBadRead() {
if (!instr_valid_)
return false;
libdis::x86_op_t *operand = libdis::x86_get_src_operand(&current_instr_);
if (!operand || operand->type != libdis::op_expression)
return false;
memcpy(&bad_register_, &operand->data.expression.base,
sizeof(libdis::x86_reg_t));
register_valid_ = true;
return true;
}
bool DisassemblerX86::setBadWrite() {
if (!instr_valid_)
return false;
libdis::x86_op_t *operand = libdis::x86_get_dest_operand(&current_instr_);
if (!operand || operand->type != libdis::op_expression)
return false;
memcpy(&bad_register_, &operand->data.expression.base,
sizeof(libdis::x86_reg_t));
register_valid_ = true;
return true;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,126 @@
// 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.
// disassembler_x86.h: Basic x86 bytecode disassembler
//
// Provides a simple disassembler which wraps libdisasm. This allows simple
// tests to be run against bytecode to test for various properties.
//
// Author: Cris Neckar
#ifndef GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_
#define GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_
#include <stddef.h>
#include "google_breakpad/common/breakpad_types.h"
namespace libdis {
#include "third_party/libdisasm/libdis.h"
}
namespace google_breakpad {
enum {
DISX86_NONE = 0x0,
DISX86_BAD_BRANCH_TARGET = 0x1,
DISX86_BAD_ARGUMENT_PASSED = 0x2,
DISX86_BAD_WRITE = 0x4,
DISX86_BAD_BLOCK_WRITE = 0x8,
DISX86_BAD_READ = 0x10,
DISX86_BAD_BLOCK_READ = 0x20,
DISX86_BAD_COMPARISON = 0x40
};
class DisassemblerX86 {
public:
// TODO(cdn): Modify this class to take a MemoryRegion instead of just
// a raw buffer. This will make it easier to use this on arbitrary
// minidumps without first copying out the code segment.
DisassemblerX86(const u_int8_t *bytecode, u_int32_t, u_int32_t);
~DisassemblerX86();
// This walks to the next instruction in the memory region and
// sets flags based on the type of instruction and previous state
// including any registers marked as bad through setBadRead()
// or setBadWrite(). This method can be called in a loop to
// disassemble until the end of a region.
u_int32_t NextInstruction();
// Indicates whether the current disassembled instruction was valid.
bool currentInstructionValid() { return instr_valid_; }
// Returns the current instruction as defined in libdis.h,
// or NULL if the current instruction is not valid.
const libdis::x86_insn_t* currentInstruction() {
return instr_valid_ ? &current_instr_ : NULL;
}
// Returns the type of the current instruction as defined in libdis.h.
libdis::x86_insn_group currentInstructionGroup() {
return current_instr_.group;
}
// Indicates whether a return instruction has been encountered.
bool endOfBlock() { return end_of_block_; }
// The flags set so far for the disassembly.
u_int16_t flags() { return flags_; }
// This sets an indicator that the register used to determine
// src or dest for the current instruction is tainted. These can
// be used after examining the current instruction to indicate,
// for example that a bad read or write occurred and the pointer
// stored in the register is currently invalid.
bool setBadRead();
bool setBadWrite();
protected:
const u_int8_t *bytecode_;
u_int32_t size_;
u_int32_t virtual_address_;
u_int32_t current_byte_offset_;
u_int32_t current_inst_offset_;
bool instr_valid_;
libdis::x86_insn_t current_instr_;
// TODO(cdn): Maybe also track an expression's index register.
// ex: mov eax, [ebx + ecx]; ebx is base, ecx is index.
bool register_valid_;
libdis::x86_reg_t bad_register_;
bool pushed_bad_value_;
bool end_of_block_;
u_int16_t flags_;
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_

View File

@@ -0,0 +1,244 @@
// 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//
// 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.
#include <unistd.h>
#include "breakpad_googletest_includes.h"
#include "processor/disassembler_x86.h"
#include "third_party/libdisasm/libdis.h"
namespace {
using google_breakpad::DisassemblerX86;
unsigned char just_return[] = "\xc3"; // retn
unsigned char invalid_instruction[] = "\x00"; // invalid
unsigned char read_eax_jmp_eax[] =
"\x8b\x18" // mov ebx, [eax];
"\x33\xc9" // xor ebx, ebx;
"\xff\x20" // jmp eax;
"\xc3"; // retn;
unsigned char write_eax_arg_to_call[] =
"\x89\xa8\x00\x02\x00\x00" // mov [eax+200], ebp;
"\xc1\xeb\x02" // shr ebx, 2;
"\x50" // push eax;
"\xe8\xd1\x24\x77\x88" // call something;
"\xc3"; // retn;
unsigned char read_edi_stosb[] =
"\x8b\x07" // mov eax, [edi];
"\x8b\xc8" // mov ecx, eax;
"\xf3\xaa" // rep stosb;
"\xc3"; // retn;
unsigned char read_clobber_write[] =
"\x03\x18" // add ebx, [eax];
"\x8b\xc1" // mov eax, ecx;
"\x89\x10" // mov [eax], edx;
"\xc3"; // retn;
unsigned char read_xchg_write[] =
"\x03\x18" // add ebx, [eax];
"\x91" // xchg eax, ecx;
"\x89\x18" // mov [eax], ebx;
"\x89\x11" // mov [ecx], edx;
"\xc3"; // retn;
unsigned char read_cmp[] =
"\x03\x18" // add ebx, [eax];
"\x83\xf8\x00" // cmp eax, 0;
"\x74\x04" // je +4;
"\xc3"; // retn;
TEST(DisassemblerX86Test, SimpleReturnInstruction) {
DisassemblerX86 dis(just_return, sizeof(just_return)-1, 0);
EXPECT_EQ(1, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(true, dis.endOfBlock());
EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup());
const libdis::x86_insn_t* instruction = dis.currentInstruction();
EXPECT_EQ(libdis::insn_controlflow, instruction->group);
EXPECT_EQ(libdis::insn_return, instruction->type);
EXPECT_EQ(0, dis.NextInstruction());
EXPECT_EQ(false, dis.currentInstructionValid());
EXPECT_EQ(NULL, dis.currentInstruction());
}
TEST(DisassemblerX86Test, SimpleInvalidInstruction) {
DisassemblerX86 dis(invalid_instruction, sizeof(invalid_instruction)-1, 0);
EXPECT_EQ(0, dis.NextInstruction());
EXPECT_EQ(false, dis.currentInstructionValid());
}
TEST(DisassemblerX86Test, BadReadLeadsToBranch) {
DisassemblerX86 dis(read_eax_jmp_eax, sizeof(read_eax_jmp_eax)-1, 0);
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
EXPECT_EQ(true, dis.setBadRead());
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_logic, dis.currentInstructionGroup());
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(google_breakpad::DISX86_BAD_BRANCH_TARGET, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup());
}
TEST(DisassemblerX86Test, BadWriteLeadsToPushedArg) {
DisassemblerX86 dis(write_eax_arg_to_call,
sizeof(write_eax_arg_to_call)-1, 0);
EXPECT_EQ(6, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
EXPECT_EQ(true, dis.setBadWrite());
EXPECT_EQ(3, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup());
EXPECT_EQ(1, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(5, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(google_breakpad::DISX86_BAD_ARGUMENT_PASSED, dis.flags());
EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup());
EXPECT_EQ(false, dis.endOfBlock());
}
TEST(DisassemblerX86Test, BadReadLeadsToBlockWrite) {
DisassemblerX86 dis(read_edi_stosb, sizeof(read_edi_stosb)-1, 0);
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
EXPECT_EQ(true, dis.setBadRead());
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(google_breakpad::DISX86_BAD_BLOCK_WRITE, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_string, dis.currentInstructionGroup());
}
TEST(DisassemblerX86Test, BadReadClobberThenWrite) {
DisassemblerX86 dis(read_clobber_write, sizeof(read_clobber_write)-1, 0);
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup());
EXPECT_EQ(true, dis.setBadRead());
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
}
TEST(DisassemblerX86Test, BadReadXCHGThenWrite) {
DisassemblerX86 dis(read_xchg_write, sizeof(read_xchg_write)-1, 0);
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup());
EXPECT_EQ(true, dis.setBadRead());
EXPECT_EQ(1, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(google_breakpad::DISX86_BAD_WRITE, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
}
TEST(DisassemblerX86Test, BadReadThenCMP) {
DisassemblerX86 dis(read_cmp, sizeof(read_cmp)-1, 0);
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(0, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup());
EXPECT_EQ(true, dis.setBadRead());
EXPECT_EQ(3, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(google_breakpad::DISX86_BAD_COMPARISON, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_comparison, dis.currentInstructionGroup());
EXPECT_EQ(2, dis.NextInstruction());
EXPECT_EQ(true, dis.currentInstructionValid());
EXPECT_EQ(google_breakpad::DISX86_BAD_COMPARISON, dis.flags());
EXPECT_EQ(false, dis.endOfBlock());
EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup());
}
}

View File

@@ -0,0 +1,104 @@
// Copyright (c) 2010 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.
// exploitability_engine.cc: Generic exploitability engine.
//
// See exploitable_engine.h for documentation.
//
// Author: Cris Neckar
#include <cassert>
#include "google_breakpad/processor/exploitability.h"
#include "google_breakpad/processor/minidump.h"
#include "google_breakpad/processor/process_state.h"
#include "processor/exploitability_win.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
namespace google_breakpad {
Exploitability::Exploitability(Minidump *dump,
ProcessState *process_state)
: dump_(dump),
process_state_(process_state) {}
ExploitabilityRating Exploitability::CheckExploitability() {
return CheckPlatformExploitability();
}
Exploitability *Exploitability::ExploitabilityForPlatform(
Minidump *dump,
ProcessState *process_state) {
Exploitability *platform_exploitability = NULL;
MinidumpSystemInfo *minidump_system_info = dump->GetSystemInfo();
if (!minidump_system_info)
return NULL;
const MDRawSystemInfo *raw_system_info =
minidump_system_info->system_info();
if (!raw_system_info)
return NULL;
switch (raw_system_info->platform_id) {
case MD_OS_WIN32_NT:
case MD_OS_WIN32_WINDOWS: {
platform_exploitability = new ExploitabilityWin(dump,
process_state);
break;
}
case MD_OS_MAC_OS_X:
case MD_OS_LINUX:
case MD_OS_UNIX:
case MD_OS_SOLARIS:
default: {
platform_exploitability = NULL;
break;
}
}
BPLOG_IF(ERROR, !platform_exploitability) <<
"No Exploitability module for platform: " <<
process_state->system_info()->os;
return platform_exploitability;
}
bool Exploitability::AddressIsAscii(u_int64_t address) {
for (int i = 0; i < 8; i++) {
u_int8_t byte = (address >> (8*i)) & 0xff;
if ((byte >= ' ' && byte <= '~') || byte == 0)
continue;
return false;
}
return true;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,255 @@
// 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//
// 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.
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/code_modules.h"
#include "google_breakpad/processor/minidump.h"
#include "google_breakpad/processor/minidump_processor.h"
#include "google_breakpad/processor/process_state.h"
#include "google_breakpad/processor/stack_frame.h"
#include "google_breakpad/processor/symbol_supplier.h"
namespace google_breakpad {
class MockMinidump : public Minidump {
public:
MockMinidump() : Minidump("") {
}
MOCK_METHOD0(Read, bool());
MOCK_CONST_METHOD0(path, string());
MOCK_CONST_METHOD0(header, const MDRawHeader*());
MOCK_METHOD0(GetThreadList, MinidumpThreadList*());
};
}
namespace {
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::CodeModule;
using google_breakpad::MinidumpProcessor;
using google_breakpad::MinidumpThreadList;
using google_breakpad::MinidumpThread;
using google_breakpad::MockMinidump;
using google_breakpad::ProcessState;
using google_breakpad::SymbolSupplier;
using google_breakpad::SystemInfo;
using std::string;
class TestSymbolSupplier : public SymbolSupplier {
public:
TestSymbolSupplier() : interrupt_(false) {}
virtual SymbolResult GetSymbolFile(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file);
virtual SymbolResult GetSymbolFile(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
string *symbol_data);
virtual SymbolResult GetCStringSymbolData(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
char **symbol_data);
virtual void FreeSymbolData(const CodeModule *module) { }
// When set to true, causes the SymbolSupplier to return INTERRUPT
void set_interrupt(bool interrupt) { interrupt_ = interrupt; }
private:
bool interrupt_;
};
SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file) {
if (interrupt_) {
return INTERRUPT;
}
return NOT_FOUND;
}
SymbolSupplier::SymbolResult TestSymbolSupplier::GetCStringSymbolData(
const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
char **symbol_data) {
return GetSymbolFile(module, system_info, symbol_file);
}
SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
string *symbol_data) {
return GetSymbolFile(module, system_info, symbol_file);
}
TEST(ExploitabilityTest, TestWindowsEngine) {
TestSymbolSupplier supplier;
BasicSourceLineResolver resolver;
MinidumpProcessor processor(&supplier, &resolver, true);
ProcessState state;
string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/ascii_read_av.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/ascii_read_av_block_write.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/ascii_read_av_clobber_write.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/ascii_read_av_conditional.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/ascii_read_av_then_jmp.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/ascii_read_av_xchg_write.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/ascii_write_av.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/ascii_write_av_arg_to_call.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/null_read_av.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/null_write_av.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/stack_exhaustion.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/exec_av_on_stack.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/write_av_non_null.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABLITY_MEDIUM,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/read_av_non_null.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/read_av_clobber_write.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW,
state.exploitability());
minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/read_av_conditional.dmp";
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW,
state.exploitability());
}
}

View File

@@ -0,0 +1,290 @@
// Copyright (c) 2010 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.
// exploitability_win.cc: Windows specific exploitability engine.
//
// Provides a guess at the exploitability of the crash for the Windows
// platform given a minidump and process_state.
//
// Author: Cris Neckar
#include <vector>
#include "processor/exploitability_win.h"
#include "google_breakpad/common/minidump_exception_win32.h"
#include "google_breakpad/processor/minidump.h"
#include "processor/disassembler_x86.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
#include "third_party/libdisasm/libdis.h"
namespace google_breakpad {
// The cutoff that we use to judge if and address is likely an offset
// from various interesting addresses.
static const u_int64_t kProbableNullOffset = 4096;
static const u_int64_t kProbableStackOffset = 8192;
// The various cutoffs for the different ratings.
static const size_t kHighCutoff = 100;
static const size_t kMediumCutoff = 80;
static const size_t kLowCutoff = 50;
static const size_t kInterestingCutoff = 25;
// Predefined incremental values for conditional weighting.
static const size_t kTinyBump = 5;
static const size_t kSmallBump = 20;
static const size_t kMediumBump = 50;
static const size_t kLargeBump = 70;
static const size_t kHugeBump = 90;
// The maximum number of bytes to disassemble past the program counter.
static const size_t kDisassembleBytesBeyondPC = 2048;
ExploitabilityWin::ExploitabilityWin(Minidump *dump,
ProcessState *process_state)
: Exploitability(dump, process_state) { }
ExploitabilityRating ExploitabilityWin::CheckPlatformExploitability() {
MinidumpException *exception = dump_->GetException();
if (!exception) {
BPLOG(INFO) << "Minidump does not have exception record.";
return EXPLOITABILITY_ERR_PROCESSING;
}
const MDRawExceptionStream *raw_exception = exception->exception();
if (!raw_exception) {
BPLOG(INFO) << "Could not obtain raw exception info.";
return EXPLOITABILITY_ERR_PROCESSING;
}
const MinidumpContext *context = exception->GetContext();
if (!context) {
BPLOG(INFO) << "Could not obtain exception context.";
return EXPLOITABILITY_ERR_PROCESSING;
}
MinidumpMemoryList *memory_list = dump_->GetMemoryList();
bool memory_available = true;
if (!memory_list) {
BPLOG(INFO) << "Minidump memory segments not available.";
memory_available = false;
}
u_int64_t address = process_state_->crash_address();
u_int32_t exception_code = raw_exception->exception_record.exception_code;
u_int32_t exploitability_weight = 0;
u_int64_t stack_ptr = 0;
u_int64_t instruction_ptr = 0;
u_int64_t this_ptr = 0;
switch (context->GetContextCPU()) {
case MD_CONTEXT_X86:
stack_ptr = context->GetContextX86()->esp;
instruction_ptr = context->GetContextX86()->eip;
this_ptr = context->GetContextX86()->ecx;
break;
case MD_CONTEXT_AMD64:
stack_ptr = context->GetContextAMD64()->rsp;
instruction_ptr = context->GetContextAMD64()->rip;
this_ptr = context->GetContextAMD64()->rcx;
break;
default:
BPLOG(INFO) << "Unsupported architecture.";
return EXPLOITABILITY_ERR_PROCESSING;
}
// Check if we are executing on the stack.
if (instruction_ptr <= (stack_ptr + kProbableStackOffset) &&
instruction_ptr >= (stack_ptr - kProbableStackOffset))
exploitability_weight += kHugeBump;
switch (exception_code) {
// This is almost certainly recursion.
case MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW:
exploitability_weight += kTinyBump;
break;
// These exceptions tend to be benign and we can generally ignore them.
case MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO:
case MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW:
case MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO:
case MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT:
case MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW:
case MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW:
case MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR:
exploitability_weight += kTinyBump;
break;
// These exceptions will typically mean that we have jumped where we
// shouldn't.
case MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION:
case MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION:
case MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION:
exploitability_weight += kLargeBump;
break;
// These represent bugs in exception handlers.
case MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION:
case MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION:
exploitability_weight += kSmallBump;
break;
case MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION:
case MD_EXCEPTION_CODE_WIN_STACK_BUFFER_OVERRUN:
exploitability_weight += kHugeBump;
break;
case MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION:
exploitability_weight += kLargeBump;
break;
case MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION:
bool near_null = (address <= kProbableNullOffset);
bool bad_read = false;
bool bad_write = false;
if (raw_exception->exception_record.number_parameters >= 1) {
MDAccessViolationTypeWin av_type =
static_cast<MDAccessViolationTypeWin>
(raw_exception->exception_record.exception_information[0]);
switch (av_type) {
case MD_ACCESS_VIOLATION_WIN_READ:
bad_read = true;
if (near_null)
exploitability_weight += kSmallBump;
else
exploitability_weight += kMediumBump;
break;
case MD_ACCESS_VIOLATION_WIN_WRITE:
bad_write = true;
if (near_null)
exploitability_weight += kSmallBump;
else
exploitability_weight += kHugeBump;
break;
case MD_ACCESS_VIOLATION_WIN_EXEC:
if (near_null)
exploitability_weight += kSmallBump;
else
exploitability_weight += kHugeBump;
break;
default:
BPLOG(INFO) << "Unrecognized access violation type.";
return EXPLOITABILITY_ERR_PROCESSING;
break;
}
MinidumpMemoryRegion *instruction_region = 0;
if (memory_available) {
instruction_region =
memory_list->GetMemoryRegionForAddress(instruction_ptr);
}
if (!near_null && instruction_region &&
context->GetContextCPU() == MD_CONTEXT_X86 &&
(bad_read || bad_write)) {
// Perform checks related to memory around instruction pointer.
u_int32_t memory_offset =
instruction_ptr - instruction_region->GetBase();
u_int32_t available_memory =
instruction_region->GetSize() - memory_offset;
available_memory = available_memory > kDisassembleBytesBeyondPC ?
kDisassembleBytesBeyondPC : available_memory;
if (available_memory) {
const u_int8_t *raw_memory =
instruction_region->GetMemory() + memory_offset;
DisassemblerX86 disassembler(raw_memory,
available_memory,
instruction_ptr);
disassembler.NextInstruction();
if (bad_read)
disassembler.setBadRead();
else
disassembler.setBadWrite();
if (disassembler.currentInstructionValid()) {
// Check if the faulting instruction falls into one of
// several interesting groups.
switch (disassembler.currentInstructionGroup()) {
case libdis::insn_controlflow:
exploitability_weight += kLargeBump;
break;
case libdis::insn_string:
exploitability_weight += kHugeBump;
break;
default:
break;
}
// Loop the disassembler through the code and check if it
// IDed any interesting conditions in the near future.
// Multiple flags may be set so treat each equally.
while (disassembler.NextInstruction() &&
disassembler.currentInstructionValid() &&
!disassembler.endOfBlock())
continue;
if (disassembler.flags() & DISX86_BAD_BRANCH_TARGET)
exploitability_weight += kLargeBump;
if (disassembler.flags() & DISX86_BAD_ARGUMENT_PASSED)
exploitability_weight += kTinyBump;
if (disassembler.flags() & DISX86_BAD_WRITE)
exploitability_weight += kMediumBump;
if (disassembler.flags() & DISX86_BAD_BLOCK_WRITE)
exploitability_weight += kMediumBump;
if (disassembler.flags() & DISX86_BAD_READ)
exploitability_weight += kTinyBump;
if (disassembler.flags() & DISX86_BAD_BLOCK_READ)
exploitability_weight += kTinyBump;
if (disassembler.flags() & DISX86_BAD_COMPARISON)
exploitability_weight += kTinyBump;
}
}
}
if (!near_null && AddressIsAscii(address))
exploitability_weight += kMediumBump;
} else {
BPLOG(INFO) << "Access violation type parameter missing.";
return EXPLOITABILITY_ERR_PROCESSING;
}
}
// Based on the calculated weight we return a simplified classification.
BPLOG(INFO) << "Calculated exploitability weight: " << exploitability_weight;
if (exploitability_weight >= kHighCutoff)
return EXPLOITABILITY_HIGH;
if (exploitability_weight >= kMediumCutoff)
return EXPLOITABLITY_MEDIUM;
if (exploitability_weight >= kLowCutoff)
return EXPLOITABILITY_LOW;
if (exploitability_weight >= kInterestingCutoff)
return EXPLOITABILITY_INTERESTING;
return EXPLOITABILITY_NONE;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,55 @@
// Copyright (c) 2010 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.
// exploitability_win.h: Windows specific exploitability engine.
//
// Provides a guess at the exploitability of the crash for the Windows
// platform given a minidump and process_state.
//
// Author: Cris Neckar
#ifndef GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_WIN_H_
#define GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_WIN_H_
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/processor/exploitability.h"
namespace google_breakpad {
class ExploitabilityWin : public Exploitability {
public:
ExploitabilityWin(Minidump *dump,
ProcessState *process_state);
virtual ExploitabilityRating CheckPlatformExploitability();
};
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_WIN_H_

View File

@@ -0,0 +1,260 @@
// Copyright (c) 2010 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.
//
// fast_source_line_resolver.cc: FastSourceLineResolver is a concrete class that
// implements SourceLineResolverInterface. Both FastSourceLineResolver and
// BasicSourceLineResolver inherit from SourceLineResolverBase class to reduce
// code redundancy.
//
// See fast_source_line_resolver.h and fast_source_line_resolver_types.h
// for more documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#include "google_breakpad/processor/fast_source_line_resolver.h"
#include "processor/fast_source_line_resolver_types.h"
#include <map>
#include <utility>
#include "processor/module_factory.h"
#include "processor/scoped_ptr.h"
using std::map;
using std::make_pair;
namespace google_breakpad {
FastSourceLineResolver::FastSourceLineResolver()
: SourceLineResolverBase(new FastModuleFactory) { }
bool FastSourceLineResolver::ShouldDeleteMemoryBufferAfterLoadModule() {
return false;
}
void FastSourceLineResolver::Module::LookupAddress(StackFrame *frame) const {
MemAddr address = frame->instruction - frame->module->base_address();
// First, look for a FUNC record that covers address. Use
// RetrieveNearestRange instead of RetrieveRange so that, if there
// is no such function, we can use the next function to bound the
// extent of the PUBLIC symbol we find, below. This does mean we
// need to check that address indeed falls within the function we
// find; do the range comparison in an overflow-friendly way.
scoped_ptr<Function> func(new Function);
const Function* func_ptr = 0;
scoped_ptr<PublicSymbol> public_symbol(new PublicSymbol);
const PublicSymbol* public_symbol_ptr = 0;
MemAddr function_base;
MemAddr function_size;
MemAddr public_address;
if (functions_.RetrieveNearestRange(address, func_ptr,
&function_base, &function_size) &&
address >= function_base && address - function_base < function_size) {
func.get()->CopyFrom(func_ptr);
frame->function_name = func->name;
frame->function_base = frame->module->base_address() + function_base;
scoped_ptr<Line> line(new Line);
const Line* line_ptr = 0;
MemAddr line_base;
if (func->lines.RetrieveRange(address, line_ptr, &line_base, NULL)) {
line.get()->CopyFrom(line_ptr);
FileMap::iterator it = files_.find(line->source_file_id);
if (it != files_.end()) {
frame->source_file_name =
files_.find(line->source_file_id).GetValuePtr();
}
frame->source_line = line->line;
frame->source_line_base = frame->module->base_address() + line_base;
}
} else if (public_symbols_.Retrieve(address,
public_symbol_ptr, &public_address) &&
(!func_ptr || public_address > function_base)) {
public_symbol.get()->CopyFrom(public_symbol_ptr);
frame->function_name = public_symbol->name;
frame->function_base = frame->module->base_address() + public_address;
}
}
// WFI: WindowsFrameInfo.
// Returns a WFI object reading from a raw memory chunk of data
WindowsFrameInfo FastSourceLineResolver::CopyWFI(const char *raw) {
// The first 4Bytes of int data are unused.
// They corresponds to "int valid;" data member of WFI.
const u_int32_t *para_uint32 = reinterpret_cast<const u_int32_t*>(
raw + sizeof(int32_t));
u_int32_t prolog_size = para_uint32[0];;
u_int32_t epilog_size = para_uint32[1];
u_int32_t parameter_size = para_uint32[2];
u_int32_t saved_register_size = para_uint32[3];
u_int32_t local_size = para_uint32[4];
u_int32_t max_stack_size = para_uint32[5];
const char *boolean = reinterpret_cast<const char*>(para_uint32 + 6);
bool allocates_base_pointer = (*boolean != 0);
std::string program_string = boolean + 1;
return WindowsFrameInfo(prolog_size,
epilog_size,
parameter_size,
saved_register_size,
local_size,
max_stack_size,
allocates_base_pointer,
program_string);
}
// Loads a map from the given buffer in char* type.
// Does NOT take ownership of mem_buffer.
// In addition, treat mem_buffer as const char*.
bool FastSourceLineResolver::Module::LoadMapFromMemory(char *mem_buffer) {
if (!mem_buffer) return false;
const u_int32_t *map_sizes = reinterpret_cast<const u_int32_t*>(mem_buffer);
unsigned int header_size = kNumberMaps_ * sizeof(unsigned int);
// offsets[]: an array of offset addresses (with respect to mem_buffer),
// for each "Static***Map" component of Module.
// "Static***Map": static version of std::map or map wrapper, i.e., StaticMap,
// StaticAddressMap, StaticContainedRangeMap, and StaticRangeMap.
unsigned int offsets[kNumberMaps_];
offsets[0] = header_size;
for (int i = 1; i < kNumberMaps_; ++i) {
offsets[i] = offsets[i - 1] + map_sizes[i - 1];
}
// Use pointers to construct Static*Map data members in Module:
int map_id = 0;
files_ = StaticMap<int, char>(mem_buffer + offsets[map_id++]);
functions_ =
StaticRangeMap<MemAddr, Function>(mem_buffer + offsets[map_id++]);
public_symbols_ =
StaticAddressMap<MemAddr, PublicSymbol>(mem_buffer + offsets[map_id++]);
for (int i = 0; i < WindowsFrameInfo::STACK_INFO_LAST; ++i)
windows_frame_info_[i] =
StaticContainedRangeMap<MemAddr, char>(mem_buffer + offsets[map_id++]);
cfi_initial_rules_ =
StaticRangeMap<MemAddr, char>(mem_buffer + offsets[map_id++]);
cfi_delta_rules_ = StaticMap<MemAddr, char>(mem_buffer + offsets[map_id++]);
return true;
}
WindowsFrameInfo *FastSourceLineResolver::Module::FindWindowsFrameInfo(
const StackFrame *frame) const {
MemAddr address = frame->instruction - frame->module->base_address();
scoped_ptr<WindowsFrameInfo> result(new WindowsFrameInfo());
// We only know about WindowsFrameInfo::STACK_INFO_FRAME_DATA and
// WindowsFrameInfo::STACK_INFO_FPO. Prefer them in this order.
// WindowsFrameInfo::STACK_INFO_FRAME_DATA is the newer type that
// includes its own program string.
// WindowsFrameInfo::STACK_INFO_FPO is the older type
// corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
const char* frame_info_ptr;
if ((windows_frame_info_[WindowsFrameInfo::STACK_INFO_FRAME_DATA]
.RetrieveRange(address, frame_info_ptr))
|| (windows_frame_info_[WindowsFrameInfo::STACK_INFO_FPO]
.RetrieveRange(address, frame_info_ptr))) {
result->CopyFrom(CopyWFI(frame_info_ptr));
return result.release();
}
// Even without a relevant STACK line, many functions contain
// information about how much space their parameters consume on the
// stack. Use RetrieveNearestRange instead of RetrieveRange, so that
// we can use the function to bound the extent of the PUBLIC symbol,
// below. However, this does mean we need to check that ADDRESS
// falls within the retrieved function's range; do the range
// comparison in an overflow-friendly way.
scoped_ptr<Function> function(new Function);
const Function* function_ptr = 0;
MemAddr function_base, function_size;
if (functions_.RetrieveNearestRange(address, function_ptr,
&function_base, &function_size) &&
address >= function_base && address - function_base < function_size) {
function.get()->CopyFrom(function_ptr);
result->parameter_size = function->parameter_size;
result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE;
return result.release();
}
// PUBLIC symbols might have a parameter size. Use the function we
// found above to limit the range the public symbol covers.
scoped_ptr<PublicSymbol> public_symbol(new PublicSymbol);
const PublicSymbol* public_symbol_ptr = 0;
MemAddr public_address;
if (public_symbols_.Retrieve(address, public_symbol_ptr, &public_address) &&
(!function_ptr || public_address > function_base)) {
public_symbol.get()->CopyFrom(public_symbol_ptr);
result->parameter_size = public_symbol->parameter_size;
}
return NULL;
}
CFIFrameInfo *FastSourceLineResolver::Module::FindCFIFrameInfo(
const StackFrame *frame) const {
MemAddr address = frame->instruction - frame->module->base_address();
MemAddr initial_base, initial_size;
const char* initial_rules = NULL;
// Find the initial rule whose range covers this address. That
// provides an initial set of register recovery rules. Then, walk
// forward from the initial rule's starting address to frame's
// instruction address, applying delta rules.
if (!cfi_initial_rules_.RetrieveRange(address, initial_rules,
&initial_base, &initial_size)) {
return NULL;
}
// Create a frame info structure, and populate it with the rules from
// the STACK CFI INIT record.
scoped_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
if (!ParseCFIRuleSet(initial_rules, rules.get()))
return NULL;
// Find the first delta rule that falls within the initial rule's range.
StaticMap<MemAddr, char>::iterator delta =
cfi_delta_rules_.lower_bound(initial_base);
// Apply delta rules up to and including the frame's address.
while (delta != cfi_delta_rules_.end() && delta.GetKey() <= address) {
ParseCFIRuleSet(delta.GetValuePtr(), rules.get());
delta++;
}
return rules.release();
}
} // namespace google_breakpad

View File

@@ -0,0 +1,179 @@
// Copyright (c) 2010 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.
//
// fast_source_line_resolver_types.h: definition of nested classes/structs in
// FastSourceLineResolver. It moves the definitions out of
// fast_source_line_resolver.cc, so that other classes could have access
// to these private nested types without including fast_source_line_resolver.cc
//
// Author: lambxsy@google.com (Siyang Xie)
#ifndef PROCESSOR_FAST_SOURCE_LINE_RESOLVER_TYPES_H__
#define PROCESSOR_FAST_SOURCE_LINE_RESOLVER_TYPES_H__
#include "google_breakpad/processor/fast_source_line_resolver.h"
#include "processor/source_line_resolver_base_types.h"
#include <map>
#include <string>
#include "google_breakpad/processor/stack_frame.h"
#include "processor/cfi_frame_info.h"
#include "processor/static_address_map-inl.h"
#include "processor/static_contained_range_map-inl.h"
#include "processor/static_map.h"
#include "processor/static_range_map-inl.h"
#include "processor/windows_frame_info.h"
namespace google_breakpad {
struct FastSourceLineResolver::Line : public SourceLineResolverBase::Line {
void CopyFrom(const Line *line_ptr) {
const char *raw = reinterpret_cast<const char*>(line_ptr);
CopyFrom(raw);
}
// De-serialize the memory data of a Line.
void CopyFrom(const char *raw) {
address = *(reinterpret_cast<const MemAddr*>(raw));
size = *(reinterpret_cast<const MemAddr*>(raw + sizeof(address)));
source_file_id = *(reinterpret_cast<const int32_t *>(
raw + 2 * sizeof(address)));
line = *(reinterpret_cast<const int32_t*>(
raw + 2 * sizeof(address) + sizeof(source_file_id)));
}
};
struct FastSourceLineResolver::Function :
public SourceLineResolverBase::Function {
void CopyFrom(const Function *func_ptr) {
const char *raw = reinterpret_cast<const char*>(func_ptr);
CopyFrom(raw);
}
// De-serialize the memory data of a Function.
void CopyFrom(const char *raw) {
size_t name_size = strlen(raw) + 1;
name = raw;
address = *(reinterpret_cast<const MemAddr*>(raw + name_size));
size = *(reinterpret_cast<const MemAddr*>(
raw + name_size + sizeof(MemAddr)));
parameter_size = *(reinterpret_cast<const int32_t*>(
raw + name_size + 2 * sizeof(MemAddr)));
lines = StaticRangeMap<MemAddr, Line>(
raw + name_size + 2 * sizeof(MemAddr) + sizeof(int32_t));
}
StaticRangeMap<MemAddr, Line> lines;
};
struct FastSourceLineResolver::PublicSymbol :
public SourceLineResolverBase::PublicSymbol {
void CopyFrom(const PublicSymbol *public_symbol_ptr) {
const char *raw = reinterpret_cast<const char*>(public_symbol_ptr);
CopyFrom(raw);
}
// De-serialize the memory data of a PublicSymbol.
void CopyFrom(const char *raw) {
size_t name_size = strlen(raw) + 1;
name = raw;
address = *(reinterpret_cast<const MemAddr*>(raw + name_size));
parameter_size = *(reinterpret_cast<const int32_t*>(
raw + name_size + sizeof(MemAddr)));
}
};
class FastSourceLineResolver::Module: public SourceLineResolverBase::Module {
public:
explicit Module(const string &name) : name_(name) { }
virtual ~Module() { }
// Looks up the given relative address, and fills the StackFrame struct
// with the result.
virtual void LookupAddress(StackFrame *frame) const;
// Loads a map from the given buffer in char* type.
virtual bool LoadMapFromMemory(char *memory_buffer);
// If Windows stack walking information is available covering ADDRESS,
// return a WindowsFrameInfo structure describing it. If the information
// is not available, returns NULL. A NULL return value does not indicate
// an error. The caller takes ownership of any returned WindowsFrameInfo
// object.
virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const;
// If CFI stack walking information is available covering ADDRESS,
// return a CFIFrameInfo structure describing it. If the information
// is not available, return NULL. The caller takes ownership of any
// returned CFIFrameInfo object.
virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const;
// Number of serialized map components of Module.
static const int kNumberMaps_ = 5 + WindowsFrameInfo::STACK_INFO_LAST;
private:
friend class FastSourceLineResolver;
friend class ModuleComparer;
typedef StaticMap<int, char> FileMap;
string name_;
StaticMap<int, char> files_;
StaticRangeMap<MemAddr, Function> functions_;
StaticAddressMap<MemAddr, PublicSymbol> public_symbols_;
// Each element in the array is a ContainedRangeMap for a type
// listed in WindowsFrameInfoTypes. These are split by type because
// there may be overlaps between maps of different types, but some
// information is only available as certain types.
StaticContainedRangeMap<MemAddr, char>
windows_frame_info_[WindowsFrameInfo::STACK_INFO_LAST];
// DWARF CFI stack walking data. The Module stores the initial rule sets
// and rule deltas as strings, just as they appear in the symbol file:
// although the file may contain hundreds of thousands of STACK CFI
// records, walking a stack will only ever use a few of them, so it's
// best to delay parsing a record until it's actually needed.
//
// STACK CFI INIT records: for each range, an initial set of register
// recovery rules. The RangeMap's itself gives the starting and ending
// addresses.
StaticRangeMap<MemAddr, char> cfi_initial_rules_;
// STACK CFI records: at a given address, the changes to the register
// recovery rules that take effect at that address. The map key is the
// starting address; the ending address is the key of the next entry in
// this map, or the end of the range as given by the cfi_initial_rules_
// entry (which FindCFIFrameInfo looks up first).
StaticMap<MemAddr, char> cfi_delta_rules_;
};
} // namespace google_breakpad
#endif // PROCESSOR_FAST_SOURCE_LINE_RESOLVER_TYPES_H__

View File

@@ -0,0 +1,477 @@
// Copyright (c) 2010 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.
//
// fast_source_line_resolver_unittest.cc: Unit tests for FastSourceLineResolver.
// Two different approaches for testing fast source line resolver:
// First, use the same unit test data for basic source line resolver.
// Second, read data from symbol files, load them as basic modules, and then
// serialize them and load the serialized data as fast modules. Then compare
// modules to assure the fast module contains exactly the same data as
// basic module.
//
// Author: Siyang Xie (lambxsy@google.com)
#include <stdio.h>
#include <sstream>
#include <string>
#include "breakpad_googletest_includes.h"
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/stack_frame.h"
#include "google_breakpad/processor/memory_region.h"
#include "processor/logging.h"
#include "processor/module_serializer.h"
#include "processor/module_comparer.h"
namespace {
using std::string;
using google_breakpad::SourceLineResolverBase;
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::FastSourceLineResolver;
using google_breakpad::ModuleSerializer;
using google_breakpad::ModuleComparer;
using google_breakpad::CFIFrameInfo;
using google_breakpad::CodeModule;
using google_breakpad::MemoryRegion;
using google_breakpad::StackFrame;
using google_breakpad::WindowsFrameInfo;
using google_breakpad::linked_ptr;
using google_breakpad::scoped_ptr;
class TestCodeModule : public CodeModule {
public:
explicit TestCodeModule(string code_file) : code_file_(code_file) {}
virtual ~TestCodeModule() {}
virtual u_int64_t base_address() const { return 0; }
virtual u_int64_t size() const { return 0xb000; }
virtual string code_file() const { return code_file_; }
virtual string code_identifier() const { return ""; }
virtual string debug_file() const { return ""; }
virtual string debug_identifier() const { return ""; }
virtual string version() const { return ""; }
virtual const CodeModule* Copy() const {
return new TestCodeModule(code_file_);
}
private:
string code_file_;
};
// A mock memory region object, for use by the STACK CFI tests.
class MockMemoryRegion: public MemoryRegion {
u_int64_t GetBase() const { return 0x10000; }
u_int32_t GetSize() const { return 0x01000; }
bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
*value = address & 0xff;
return true;
}
bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
*value = address & 0xffff;
return true;
}
bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
switch (address) {
case 0x10008: *value = 0x98ecadc3; break; // saved %ebx
case 0x1000c: *value = 0x878f7524; break; // saved %esi
case 0x10010: *value = 0x6312f9a5; break; // saved %edi
case 0x10014: *value = 0x10038; break; // caller's %ebp
case 0x10018: *value = 0xf6438648; break; // return address
default: *value = 0xdeadbeef; break; // junk
}
return true;
}
bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
*value = address;
return true;
}
};
// Verify that, for every association in ACTUAL, EXPECTED has the same
// association. (That is, ACTUAL's associations should be a subset of
// EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
// ".cfa".
static bool VerifyRegisters(
const char *file, int line,
const CFIFrameInfo::RegisterValueMap<u_int32_t> &expected,
const CFIFrameInfo::RegisterValueMap<u_int32_t> &actual) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator a;
a = actual.find(".cfa");
if (a == actual.end())
return false;
a = actual.find(".ra");
if (a == actual.end())
return false;
for (a = actual.begin(); a != actual.end(); a++) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator e =
expected.find(a->first);
if (e == expected.end()) {
fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
file, line, a->first.c_str(), a->second);
return false;
}
if (e->second != a->second) {
fprintf(stderr,
"%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
file, line, a->first.c_str(), a->second, e->second);
return false;
}
// Don't complain if this doesn't recover all registers. Although
// the DWARF spec says that unmentioned registers are undefined,
// GCC uses omission to mean that they are unchanged.
}
return true;
}
static bool VerifyEmpty(const StackFrame &frame) {
if (frame.function_name.empty() &&
frame.source_file_name.empty() &&
frame.source_line == 0)
return true;
return false;
}
static void ClearSourceLineInfo(StackFrame *frame) {
frame->function_name.clear();
frame->module = NULL;
frame->source_file_name.clear();
frame->source_line = 0;
}
class TestFastSourceLineResolver : public ::testing::Test {
public:
void SetUp() {
testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata";
}
string symbol_file(int file_index) {
std::stringstream ss;
ss << testdata_dir << "/module" << file_index << ".out";
return ss.str();
}
ModuleSerializer serializer;
BasicSourceLineResolver basic_resolver;
FastSourceLineResolver fast_resolver;
ModuleComparer module_comparer;
string testdata_dir;
};
// Test adapted from basic_source_line_resolver_unittest.
TEST_F(TestFastSourceLineResolver, TestLoadAndResolve) {
TestCodeModule module1("module1");
ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
ASSERT_TRUE(basic_resolver.HasModule(&module1));
// Convert module1 to fast_module:
ASSERT_TRUE(serializer.ConvertOneModule(
module1.code_file(), &basic_resolver, &fast_resolver));
ASSERT_TRUE(fast_resolver.HasModule(&module1));
TestCodeModule module2("module2");
ASSERT_TRUE(basic_resolver.LoadModule(&module2, symbol_file(2)));
ASSERT_TRUE(basic_resolver.HasModule(&module2));
// Convert module2 to fast_module:
ASSERT_TRUE(serializer.ConvertOneModule(
module2.code_file(), &basic_resolver, &fast_resolver));
ASSERT_TRUE(fast_resolver.HasModule(&module2));
StackFrame frame;
scoped_ptr<WindowsFrameInfo> windows_frame_info;
scoped_ptr<CFIFrameInfo> cfi_frame_info;
frame.instruction = 0x1000;
frame.module = NULL;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_FALSE(frame.module);
ASSERT_TRUE(frame.function_name.empty());
ASSERT_EQ(frame.function_base, 0);
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
ASSERT_EQ(frame.source_line_base, 0);
frame.module = &module1;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function1_1");
ASSERT_TRUE(frame.module);
ASSERT_EQ(frame.module->code_file(), "module1");
ASSERT_EQ(frame.function_base, 0x1000);
ASSERT_EQ(frame.source_file_name, "file1_1.cc");
ASSERT_EQ(frame.source_line, 44);
ASSERT_EQ(frame.source_line_base, 0x1000);
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_EQ(windows_frame_info->program_string,
"$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
ClearSourceLineInfo(&frame);
frame.instruction = 0x800;
frame.module = &module1;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_TRUE(VerifyEmpty(frame));
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
ASSERT_FALSE(windows_frame_info.get());
frame.instruction = 0x1280;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function1_3");
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_TRUE(windows_frame_info->program_string.empty());
frame.instruction = 0x1380;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function1_4");
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_FALSE(windows_frame_info->program_string.empty());
frame.instruction = 0x2000;
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
ASSERT_FALSE(windows_frame_info.get());
// module1 has STACK CFI records covering 3d40..3def;
// module2 has STACK CFI records covering 3df0..3e9f;
// check that FindCFIFrameInfo doesn't claim to find any outside those ranges.
frame.instruction = 0x3d3f;
frame.module = &module1;
cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
ASSERT_FALSE(cfi_frame_info.get());
frame.instruction = 0x3e9f;
frame.module = &module1;
cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
ASSERT_FALSE(cfi_frame_info.get());
CFIFrameInfo::RegisterValueMap<u_int32_t> current_registers;
CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
CFIFrameInfo::RegisterValueMap<u_int32_t> expected_caller_registers;
MockMemoryRegion memory;
// Regardless of which instruction evaluation takes place at, it
// should produce the same values for the caller's registers.
expected_caller_registers[".cfa"] = 0x1001c;
expected_caller_registers[".ra"] = 0xf6438648;
expected_caller_registers["$ebp"] = 0x10038;
expected_caller_registers["$ebx"] = 0x98ecadc3;
expected_caller_registers["$esi"] = 0x878f7524;
expected_caller_registers["$edi"] = 0x6312f9a5;
frame.instruction = 0x3d40;
frame.module = &module1;
current_registers.clear();
current_registers["$esp"] = 0x10018;
current_registers["$ebp"] = 0x10038;
current_registers["$ebx"] = 0x98ecadc3;
current_registers["$esi"] = 0x878f7524;
current_registers["$edi"] = 0x6312f9a5;
cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers));
frame.instruction = 0x3d41;
current_registers["$esp"] = 0x10014;
cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers));
frame.instruction = 0x3d43;
current_registers["$ebp"] = 0x10014;
cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d54;
current_registers["$ebx"] = 0x6864f054U;
cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d5a;
current_registers["$esi"] = 0x6285f79aU;
cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d84;
current_registers["$edi"] = 0x64061449U;
cfi_frame_info.reset(fast_resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x2900;
frame.module = &module1;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, string("PublicSymbol"));
frame.instruction = 0x4000;
frame.module = &module1;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, string("LargeFunction"));
frame.instruction = 0x2181;
frame.module = &module2;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function2_2");
ASSERT_EQ(frame.function_base, 0x2170);
ASSERT_TRUE(frame.module);
ASSERT_EQ(frame.module->code_file(), "module2");
ASSERT_EQ(frame.source_file_name, "file2_2.cc");
ASSERT_EQ(frame.source_line, 21);
ASSERT_EQ(frame.source_line_base, 0x2180);
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_EQ(windows_frame_info->prolog_size, 1);
frame.instruction = 0x216f;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Public2_1");
ClearSourceLineInfo(&frame);
frame.instruction = 0x219f;
frame.module = &module2;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_TRUE(frame.function_name.empty());
frame.instruction = 0x21a0;
frame.module = &module2;
fast_resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Public2_2");
}
TEST_F(TestFastSourceLineResolver, TestInvalidLoads) {
TestCodeModule module3("module3");
ASSERT_FALSE(basic_resolver.LoadModule(&module3,
testdata_dir + "/module3_bad.out"));
ASSERT_FALSE(basic_resolver.HasModule(&module3));
// Convert module3 to fast_module:
ASSERT_FALSE(serializer.ConvertOneModule(module3.code_file(),
&basic_resolver,
&fast_resolver));
ASSERT_FALSE(fast_resolver.HasModule(&module3));
TestCodeModule module4("module4");
ASSERT_FALSE(basic_resolver.LoadModule(&module4,
testdata_dir + "/module4_bad.out"));
ASSERT_FALSE(basic_resolver.HasModule(&module4));
// Convert module4 to fast_module:
ASSERT_FALSE(serializer.ConvertOneModule(module4.code_file(),
&basic_resolver,
&fast_resolver));
ASSERT_FALSE(fast_resolver.HasModule(&module4));
TestCodeModule module5("module5");
ASSERT_FALSE(fast_resolver.LoadModule(&module5,
testdata_dir + "/invalid-filename"));
ASSERT_FALSE(fast_resolver.HasModule(&module5));
TestCodeModule invalidmodule("invalid-module");
ASSERT_FALSE(fast_resolver.HasModule(&invalidmodule));
}
TEST_F(TestFastSourceLineResolver, TestUnload) {
TestCodeModule module1("module1");
ASSERT_FALSE(basic_resolver.HasModule(&module1));
ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
ASSERT_TRUE(basic_resolver.HasModule(&module1));
// Convert module1 to fast_module.
ASSERT_TRUE(serializer.ConvertOneModule(module1.code_file(),
&basic_resolver,
&fast_resolver));
ASSERT_TRUE(fast_resolver.HasModule(&module1));
basic_resolver.UnloadModule(&module1);
fast_resolver.UnloadModule(&module1);
ASSERT_FALSE(fast_resolver.HasModule(&module1));
ASSERT_TRUE(basic_resolver.LoadModule(&module1, symbol_file(1)));
ASSERT_TRUE(basic_resolver.HasModule(&module1));
// Convert module1 to fast_module.
ASSERT_TRUE(serializer.ConvertOneModule(module1.code_file(),
&basic_resolver,
&fast_resolver));
ASSERT_TRUE(fast_resolver.HasModule(&module1));
}
TEST_F(TestFastSourceLineResolver, CompareModule) {
char *symbol_data;
string symbol_data_string;
string filename;
for (int module_index = 0; module_index < 3; ++module_index) {
std::stringstream ss;
ss << testdata_dir << "/module" << module_index << ".out";
filename = ss.str();
ASSERT_TRUE(SourceLineResolverBase::ReadSymbolFile(
&symbol_data, symbol_file(module_index)));
symbol_data_string = symbol_data;
delete [] symbol_data;
ASSERT_TRUE(module_comparer.Compare(symbol_data_string));
}
}
} // namespace
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,193 @@
// 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.
// A "smart" pointer type with reference tracking. Every pointer to a
// particular object is kept on a circular linked list. When the last pointer
// to an object is destroyed or reassigned, the object is deleted.
//
// Used properly, this deletes the object when the last reference goes away.
// There are several caveats:
// - Like all reference counting schemes, cycles lead to leaks.
// - Each smart pointer is actually two pointers (8 bytes instead of 4).
// - Every time a pointer is assigned, the entire list of pointers to that
// object is traversed. This class is therefore NOT SUITABLE when there
// will often be more than two or three pointers to a particular object.
// - References are only tracked as long as linked_ptr<> objects are copied.
// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
// will happen (double deletion).
//
// A good use of this class is storing object references in STL containers.
// You can safely put linked_ptr<> in a vector<>.
// Other uses may not be as good.
//
// Note: If you use an incomplete type with linked_ptr<>, the class
// *containing* linked_ptr<> must have a constructor and destructor (even
// if they do nothing!).
#ifndef PROCESSOR_LINKED_PTR_H__
#define PROCESSOR_LINKED_PTR_H__
namespace google_breakpad {
// This is used internally by all instances of linked_ptr<>. It needs to be
// a non-template class because different types of linked_ptr<> can refer to
// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
// So, it needs to be possible for different types of linked_ptr to participate
// in the same circular linked list, so we need a single class type here.
//
// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
class linked_ptr_internal {
public:
// Create a new circle that includes only this instance.
void join_new() {
next_ = this;
}
// Join an existing circle.
void join(linked_ptr_internal const* ptr) {
linked_ptr_internal const* p = ptr;
while (p->next_ != ptr) p = p->next_;
p->next_ = this;
next_ = ptr;
}
// Leave whatever circle we're part of. Returns true iff we were the
// last member of the circle. Once this is done, you can join() another.
bool depart() {
if (next_ == this) return true;
linked_ptr_internal const* p = next_;
while (p->next_ != this) p = p->next_;
p->next_ = next_;
return false;
}
private:
mutable linked_ptr_internal const* next_;
};
template <typename T>
class linked_ptr {
public:
typedef T element_type;
// Take over ownership of a raw pointer. This should happen as soon as
// possible after the object is created.
explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
~linked_ptr() { depart(); }
// Copy an existing linked_ptr<>, adding ourselves to the list of references.
template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
linked_ptr(linked_ptr const& ptr) { copy(&ptr); }
// Assignment releases the old value and acquires the new.
template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
depart();
copy(&ptr);
return *this;
}
linked_ptr& operator=(linked_ptr const& ptr) {
if (&ptr != this) {
depart();
copy(&ptr);
}
return *this;
}
// Smart pointer members.
void reset(T* ptr = NULL) { depart(); capture(ptr); }
T* get() const { return value_; }
T* operator->() const { return value_; }
T& operator*() const { return *value_; }
// Release ownership of the pointed object and returns it.
// Sole ownership by this linked_ptr object is required.
T* release() {
bool last = link_.depart();
T* v = value_;
value_ = NULL;
return v;
}
bool operator==(T* p) const { return value_ == p; }
bool operator!=(T* p) const { return value_ != p; }
template <typename U>
bool operator==(linked_ptr<U> const& ptr) const {
return value_ == ptr.get();
}
template <typename U>
bool operator!=(linked_ptr<U> const& ptr) const {
return value_ != ptr.get();
}
private:
template <typename U>
friend class linked_ptr;
T* value_;
linked_ptr_internal link_;
void depart() {
if (link_.depart()) delete value_;
}
void capture(T* ptr) {
value_ = ptr;
link_.join_new();
}
template <typename U> void copy(linked_ptr<U> const* ptr) {
value_ = ptr->get();
if (value_)
link_.join(&ptr->link_);
else
link_.join_new();
}
};
template<typename T> inline
bool operator==(T* ptr, const linked_ptr<T>& x) {
return ptr == x.get();
}
template<typename T> inline
bool operator!=(T* ptr, const linked_ptr<T>& x) {
return ptr != x.get();
}
// A function to convert T* into linked_ptr<T>
// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
template <typename T>
linked_ptr<T> make_linked_ptr(T* ptr) {
return linked_ptr<T>(ptr);
}
} // namespace google_breakpad
#endif // PROCESSOR_LINKED_PTR_H__

112
thirdparty/breakpad/processor/logging.cc vendored Normal file
View File

@@ -0,0 +1,112 @@
// Copyright (c) 2007, 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.
// logging.cc: Breakpad logging
//
// See logging.h for documentation.
//
// Author: Mark Mentovai
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "processor/logging.h"
#include "processor/pathname_stripper.h"
#ifdef _WIN32
#define snprintf _snprintf
#endif
namespace google_breakpad {
LogStream::LogStream(std::ostream &stream, Severity severity,
const char *file, int line)
: stream_(stream) {
time_t clock;
time(&clock);
struct tm tm_struct;
#ifdef _WIN32
localtime_s(&tm_struct, &clock);
#else
localtime_r(&clock, &tm_struct);
#endif
char time_string[20];
strftime(time_string, sizeof(time_string), "%Y-%m-%d %H:%M:%S", &tm_struct);
const char *severity_string = "UNKNOWN_SEVERITY";
switch (severity) {
case SEVERITY_INFO:
severity_string = "INFO";
break;
case SEVERITY_ERROR:
severity_string = "ERROR";
break;
}
stream_ << time_string << ": " << PathnameStripper::File(file) << ":" <<
line << ": " << severity_string << ": ";
}
LogStream::~LogStream() {
stream_ << std::endl;
}
std::string HexString(u_int32_t number) {
char buffer[11];
snprintf(buffer, sizeof(buffer), "0x%x", number);
return std::string(buffer);
}
std::string HexString(u_int64_t number) {
char buffer[19];
snprintf(buffer, sizeof(buffer), "0x%" PRIx64, number);
return std::string(buffer);
}
std::string HexString(int number) {
char buffer[19];
snprintf(buffer, sizeof(buffer), "0x%x", number);
return std::string(buffer);
}
int ErrnoString(std::string *error_string) {
assert(error_string);
// strerror isn't necessarily thread-safe. strerror_r would be preferrable,
// but GNU libc uses a nonstandard strerror_r by default, which returns a
// char* (rather than an int success indicator) and doesn't necessarily
// use the supplied buffer.
error_string->assign(strerror(errno));
return errno;
}
} // namespace google_breakpad

162
thirdparty/breakpad/processor/logging.h vendored Normal file
View File

@@ -0,0 +1,162 @@
// Copyright (c) 2007, 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.
// logging.h: Breakpad logging
//
// Breakpad itself uses Breakpad logging with statements of the form:
// BPLOG(severity) << "message";
// severity may be INFO, ERROR, or other values defined in this file.
//
// BPLOG is an overridable macro so that users can customize Breakpad's
// logging. Left at the default, logging messages are sent to stderr along
// with a timestamp and the source code location that produced a message.
// The streams may be changed by redefining BPLOG_*_STREAM, the logging
// behavior may be changed by redefining BPLOG_*, and the entire logging
// system may be overridden by redefining BPLOG(severity). These
// redefinitions may be passed to the preprocessor as a command-line flag
// (-D).
//
// If an additional header is required to override Breakpad logging, it can
// be specified by the BP_LOGGING_INCLUDE macro. If defined, this header
// will #include the header specified by that macro.
//
// If any initialization is needed before logging, it can be performed by
// a function called through the BPLOG_INIT macro. Each main function of
// an executable program in the Breakpad processor library calls
// BPLOG_INIT(&argc, &argv); before any logging can be performed; define
// BPLOG_INIT appropriately if initialization is required.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_LOGGING_H__
#define PROCESSOR_LOGGING_H__
#include <iostream>
#include <string>
#include "google_breakpad/common/breakpad_types.h"
#ifdef BP_LOGGING_INCLUDE
#include BP_LOGGING_INCLUDE
#endif // BP_LOGGING_INCLUDE
namespace google_breakpad {
// These are defined in Microsoft headers.
#ifdef SEVERITY_ERROR
#undef SEVERITY_ERROR
#endif
#ifdef ERROR
#undef ERROR
#endif
class LogStream {
public:
enum Severity {
SEVERITY_INFO,
SEVERITY_ERROR
};
// Begin logging a message to the stream identified by |stream|, at the
// indicated severity. The file and line parameters should be set so as to
// identify the line of source code that is producing a message.
LogStream(std::ostream &stream, Severity severity,
const char *file, int line);
// Finish logging by printing a newline and flushing the output stream.
~LogStream();
template<typename T> std::ostream& operator<<(const T &t) {
return stream_ << t;
}
private:
std::ostream &stream_;
// Disallow copy constructor and assignment operator
explicit LogStream(const LogStream &that);
void operator=(const LogStream &that);
};
// This class is used to explicitly ignore values in the conditional logging
// macros. This avoids compiler warnings like "value computed is not used"
// and "statement has no effect".
class LogMessageVoidify {
public:
LogMessageVoidify() {}
// This has to be an operator with a precedence lower than << but higher
// than ?:
void operator&(std::ostream &) {}
};
// Returns number formatted as a hexadecimal string, such as "0x7b".
std::string HexString(u_int32_t number);
std::string HexString(u_int64_t number);
std::string HexString(int number);
// Returns the error code as set in the global errno variable, and sets
// error_string, a required argument, to a string describing that error
// code.
int ErrnoString(std::string *error_string);
} // namespace google_breakpad
#ifndef BPLOG_INIT
#define BPLOG_INIT(pargc, pargv)
#endif // BPLOG_INIT
#ifndef BPLOG
#define BPLOG(severity) BPLOG_ ## severity
#endif // BPLOG
#ifndef BPLOG_INFO
#ifndef BPLOG_INFO_STREAM
#define BPLOG_INFO_STREAM std::clog
#endif // BPLOG_INFO_STREAM
#define BPLOG_INFO google_breakpad::LogStream(BPLOG_INFO_STREAM, \
google_breakpad::LogStream::SEVERITY_INFO, \
__FILE__, __LINE__)
#endif // BPLOG_INFO
#ifndef BPLOG_ERROR
#ifndef BPLOG_ERROR_STREAM
#define BPLOG_ERROR_STREAM std::cerr
#endif // BPLOG_ERROR_STREAM
#define BPLOG_ERROR google_breakpad::LogStream(BPLOG_ERROR_STREAM, \
google_breakpad::LogStream::SEVERITY_ERROR, \
__FILE__, __LINE__)
#endif // BPLOG_ERROR
#define BPLOG_IF(severity, condition) \
!(condition) ? (void) 0 : \
google_breakpad::LogMessageVoidify() & BPLOG(severity)
#endif // PROCESSOR_LOGGING_H__

View File

@@ -0,0 +1,266 @@
// Copyright (c) 2010, 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.
//
// map_serializers_inl.h: implementation for serializing std::map and its
// wrapper classes.
//
// See map_serializers.h for documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_MAP_SERIALIZERS_INL_H__
#define PROCESSOR_MAP_SERIALIZERS_INL_H__
#include <map>
#include <string>
#include "processor/map_serializers.h"
#include "processor/simple_serializer.h"
#include "processor/address_map-inl.h"
#include "processor/range_map-inl.h"
#include "processor/contained_range_map-inl.h"
#include "processor/logging.h"
namespace google_breakpad {
template<typename Key, typename Value>
size_t StdMapSerializer<Key, Value>::SizeOf(
const std::map<Key, Value> &m) const {
size_t size = 0;
size_t header_size = (1 + m.size()) * sizeof(u_int32_t);
size += header_size;
typename std::map<Key, Value>::const_iterator iter;
for (iter = m.begin(); iter != m.end(); ++iter) {
size += key_serializer_.SizeOf(iter->first);
size += value_serializer_.SizeOf(iter->second);
}
return size;
}
template<typename Key, typename Value>
char *StdMapSerializer<Key, Value>::Write(const std::map<Key, Value> &m,
char *dest) const {
if (!dest) {
BPLOG(ERROR) << "StdMapSerializer failed: write to NULL address.";
return NULL;
}
char *start_address = dest;
// Write header:
// Number of nodes.
dest = SimpleSerializer<u_int32_t>::Write(m.size(), dest);
// Nodes offsets.
u_int32_t *offsets = reinterpret_cast<u_int32_t*>(dest);
dest += sizeof(u_int32_t) * m.size();
char *key_address = dest;
dest += sizeof(Key) * m.size();
// Traverse map.
typename std::map<Key, Value>::const_iterator iter;
int index = 0;
for (iter = m.begin(); iter != m.end(); ++iter, ++index) {
offsets[index] = static_cast<u_int32_t>(dest - start_address);
key_address = key_serializer_.Write(iter->first, key_address);
dest = value_serializer_.Write(iter->second, dest);
}
return dest;
}
template<typename Key, typename Value>
char *StdMapSerializer<Key, Value>::Serialize(
const std::map<Key, Value> &m, unsigned int *size) const {
// Compute size of memory to be allocated.
unsigned int size_to_alloc = SizeOf(m);
// Allocate memory.
char *serialized_data = new char[size_to_alloc];
if (!serialized_data) {
BPLOG(INFO) << "StdMapSerializer memory allocation failed.";
if (size) *size = 0;
return NULL;
}
// Write serialized data into memory.
Write(m, serialized_data);
if (size) *size = size_to_alloc;
return serialized_data;
}
template<typename Address, typename Entry>
size_t RangeMapSerializer<Address, Entry>::SizeOf(
const RangeMap<Address, Entry> &m) const {
size_t size = 0;
size_t header_size = (1 + m.map_.size()) * sizeof(u_int32_t);
size += header_size;
typename std::map<Address, Range>::const_iterator iter;
for (iter = m.map_.begin(); iter != m.map_.end(); ++iter) {
// Size of key (high address).
size += address_serializer_.SizeOf(iter->first);
// Size of base (low address).
size += address_serializer_.SizeOf(iter->second.base());
// Size of entry.
size += entry_serializer_.SizeOf(iter->second.entry());
}
return size;
}
template<typename Address, typename Entry>
char *RangeMapSerializer<Address, Entry>::Write(
const RangeMap<Address, Entry> &m, char *dest) const {
if (!dest) {
BPLOG(ERROR) << "RangeMapSerializer failed: write to NULL address.";
return NULL;
}
char *start_address = dest;
// Write header:
// Number of nodes.
dest = SimpleSerializer<u_int32_t>::Write(m.map_.size(), dest);
// Nodes offsets.
u_int32_t *offsets = reinterpret_cast<u_int32_t*>(dest);
dest += sizeof(u_int32_t) * m.map_.size();
char *key_address = dest;
dest += sizeof(Address) * m.map_.size();
// Traverse map.
typename std::map<Address, Range>::const_iterator iter;
int index = 0;
for (iter = m.map_.begin(); iter != m.map_.end(); ++iter, ++index) {
offsets[index] = static_cast<u_int32_t>(dest - start_address);
key_address = address_serializer_.Write(iter->first, key_address);
dest = address_serializer_.Write(iter->second.base(), dest);
dest = entry_serializer_.Write(iter->second.entry(), dest);
}
return dest;
}
template<typename Address, typename Entry>
char *RangeMapSerializer<Address, Entry>::Serialize(
const RangeMap<Address, Entry> &m, unsigned int *size) const {
// Compute size of memory to be allocated.
unsigned int size_to_alloc = SizeOf(m);
// Allocate memory.
char *serialized_data = new char[size_to_alloc];
if (!serialized_data) {
BPLOG(INFO) << "RangeMapSerializer memory allocation failed.";
if (size) *size = 0;
return NULL;
}
// Write serialized data into memory.
Write(m, serialized_data);
if (size) *size = size_to_alloc;
return serialized_data;
}
template<class AddrType, class EntryType>
size_t ContainedRangeMapSerializer<AddrType, EntryType>::SizeOf(
const ContainedRangeMap<AddrType, EntryType> *m) const {
size_t size = 0;
size_t header_size = addr_serializer_.SizeOf(m->base_)
+ entry_serializer_.SizeOf(m->entry_)
+ sizeof(u_int32_t);
size += header_size;
// In case m.map_ == NULL, we treat it as an empty map:
size += sizeof(u_int32_t);
if (m->map_) {
size += m->map_->size() * sizeof(u_int32_t);
typename Map::const_iterator iter;
for (iter = m->map_->begin(); iter != m->map_->end(); ++iter) {
size += addr_serializer_.SizeOf(iter->first);
// Recursive calculation of size:
size += SizeOf(iter->second);
}
}
return size;
}
template<class AddrType, class EntryType>
char *ContainedRangeMapSerializer<AddrType, EntryType>::Write(
const ContainedRangeMap<AddrType, EntryType> *m, char *dest) const {
if (!dest) {
BPLOG(ERROR) << "StdMapSerializer failed: write to NULL address.";
return NULL;
}
dest = addr_serializer_.Write(m->base_, dest);
dest = SimpleSerializer<u_int32_t>::Write(entry_serializer_.SizeOf(m->entry_),
dest);
dest = entry_serializer_.Write(m->entry_, dest);
// Write map<<AddrType, ContainedRangeMap*>:
char *map_address = dest;
if (m->map_ == NULL) {
dest = SimpleSerializer<u_int32_t>::Write(0, dest);
} else {
dest = SimpleSerializer<u_int32_t>::Write(m->map_->size(), dest);
u_int32_t *offsets = reinterpret_cast<u_int32_t*>(dest);
dest += sizeof(u_int32_t) * m->map_->size();
char *key_address = dest;
dest += sizeof(AddrType) * m->map_->size();
// Traverse map.
typename Map::const_iterator iter;
int index = 0;
for (iter = m->map_->begin(); iter != m->map_->end(); ++iter, ++index) {
offsets[index] = static_cast<u_int32_t>(dest - map_address);
key_address = addr_serializer_.Write(iter->first, key_address);
// Recursively write.
dest = Write(iter->second, dest);
}
}
return dest;
}
template<class AddrType, class EntryType>
char *ContainedRangeMapSerializer<AddrType, EntryType>::Serialize(
const ContainedRangeMap<AddrType, EntryType> *m, unsigned int *size) const {
unsigned int size_to_alloc = SizeOf(m);
// Allocating memory.
char *serialized_data = new char[size_to_alloc];
if (!serialized_data) {
BPLOG(INFO) << "ContainedRangeMapSerializer memory allocation failed.";
if (size) *size = 0;
return NULL;
}
Write(m, serialized_data);
if (size) *size = size_to_alloc;
return serialized_data;
}
} // namespace google_breakpad
#endif // PROCESSOR_MAP_SERIALIZERS_INL_H__

View File

@@ -0,0 +1,168 @@
// Copyright (c) 2010, 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.
//
// map_serializers.h: defines templates for serializing std::map and its
// wrappers: AddressMap, RangeMap, and ContainedRangeMap.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_MAP_SERIALIZERS_H__
#define PROCESSOR_MAP_SERIALIZERS_H__
#include <map>
#include <string>
#include "processor/simple_serializer.h"
#include "processor/address_map-inl.h"
#include "processor/range_map-inl.h"
#include "processor/contained_range_map-inl.h"
namespace google_breakpad {
// StdMapSerializer allocates memory and serializes an std::map instance into a
// chunk of memory data.
template<typename Key, typename Value>
class StdMapSerializer {
public:
// Calculate the memory size of serialized data.
size_t SizeOf(const std::map<Key, Value> &m) const;
// Writes the serialized data to memory with start address = dest,
// and returns the "end" of data, i.e., return the address follow the final
// byte of data.
// NOTE: caller has to allocate enough memory before invoke Write() method.
char* Write(const std::map<Key, Value> &m, char* dest) const;
// Serializes a std::map object into a chunk of memory data with format
// described in "StaticMap.h" comment.
// Returns a pointer to the serialized data. If size != NULL, *size is set
// to the size of serialized data, i.e., SizeOf(m).
// Caller has the ownership of memory allocated as "new char[]".
char* Serialize(const std::map<Key, Value> &m, unsigned int *size) const;
private:
SimpleSerializer<Key> key_serializer_;
SimpleSerializer<Value> value_serializer_;
};
// AddressMapSerializer allocates memory and serializes an AddressMap into a
// chunk of memory data.
template<typename Addr, typename Entry>
class AddressMapSerializer {
public:
// Calculate the memory size of serialized data.
size_t SizeOf(const AddressMap<Addr, Entry> &m) const {
return std_map_serializer_.SizeOf(m.map_);
}
// Write the serialized data to specified memory location. Return the "end"
// of data, i.e., return the address after the final byte of data.
// NOTE: caller has to allocate enough memory before invoke Write() method.
char* Write(const AddressMap<Addr, Entry> &m, char *dest) const {
return std_map_serializer_.Write(m.map_, dest);
}
// Serializes an AddressMap object into a chunk of memory data.
// Returns a pointer to the serialized data. If size != NULL, *size is set
// to the size of serialized data, i.e., SizeOf(m).
// Caller has the ownership of memory allocated as "new char[]".
char* Serialize(const AddressMap<Addr, Entry> &m, unsigned int *size) const {
return std_map_serializer_.Serialize(m.map_, size);
}
private:
// AddressMapSerializer is a simple wrapper of StdMapSerializer, just as
// AddressMap is a simple wrapper of std::map.
StdMapSerializer<Addr, Entry> std_map_serializer_;
};
// RangeMapSerializer allocates memory and serializes a RangeMap instance into a
// chunk of memory data.
template<typename Address, typename Entry>
class RangeMapSerializer {
public:
// Calculate the memory size of serialized data.
size_t SizeOf(const RangeMap<Address, Entry> &m) const;
// Write the serialized data to specified memory location. Return the "end"
// of data, i.e., return the address after the final byte of data.
// NOTE: caller has to allocate enough memory before invoke Write() method.
char* Write(const RangeMap<Address, Entry> &m, char* dest) const;
// Serializes a RangeMap object into a chunk of memory data.
// Returns a pointer to the serialized data. If size != NULL, *size is set
// to the size of serialized data, i.e., SizeOf(m).
// Caller has the ownership of memory allocated as "new char[]".
char* Serialize(const RangeMap<Address, Entry> &m, unsigned int *size) const;
private:
// Convenient type name for Range.
typedef typename RangeMap<Address, Entry>::Range Range;
// Serializer for RangeMap's key and Range::base_.
SimpleSerializer<Address> address_serializer_;
// Serializer for RangeMap::Range::entry_.
SimpleSerializer<Entry> entry_serializer_;
};
// ContainedRangeMapSerializer allocates memory and serializes a
// ContainedRangeMap instance into a chunk of memory data.
template<class AddrType, class EntryType>
class ContainedRangeMapSerializer {
public:
// Calculate the memory size of serialized data.
size_t SizeOf(const ContainedRangeMap<AddrType, EntryType> *m) const;
// Write the serialized data to specified memory location. Return the "end"
// of data, i.e., return the address after the final byte of data.
// NOTE: caller has to allocate enough memory before invoke Write() method.
char* Write(const ContainedRangeMap<AddrType, EntryType> *m,
char* dest) const;
// Serializes a ContainedRangeMap object into a chunk of memory data.
// Returns a pointer to the serialized data. If size != NULL, *size is set
// to the size of serialized data, i.e., SizeOf(m).
// Caller has the ownership of memory allocated as "new char[]".
char* Serialize(const ContainedRangeMap<AddrType, EntryType> *m,
unsigned int *size) const;
private:
// Convenient type name for the underlying map type.
typedef std::map<AddrType, ContainedRangeMap<AddrType, EntryType>*> Map;
// Serializer for addresses and entries stored in ContainedRangeMap.
SimpleSerializer<AddrType> addr_serializer_;
SimpleSerializer<EntryType> entry_serializer_;
};
} // namespace google_breakpad
#endif // PROCESSOR_MAP_SERIALIZERS_H__

View File

@@ -0,0 +1,388 @@
// Copyright (c) 2010, 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.
// map_serializers_unittest.cc: Unit tests for std::map serializer and
// std::map wrapper serializers.
//
// Author: Siyang Xie (lambxsy@google.com)
#include <climits>
#include <map>
#include <string>
#include <utility>
#include <iostream>
#include <sstream>
#include "breakpad_googletest_includes.h"
#include "map_serializers-inl.h"
#include "processor/address_map-inl.h"
#include "processor/range_map-inl.h"
#include "processor/contained_range_map-inl.h"
typedef int32_t AddrType;
typedef int32_t EntryType;
const int kSizeOfInt = 4;
class TestStdMapSerializer : public ::testing::Test {
protected:
void SetUp() {
serialized_size_ = 0;
serialized_data_ = NULL;
}
void TearDown() {
delete [] serialized_data_;
}
std::map<AddrType, EntryType> std_map_;
google_breakpad::StdMapSerializer<AddrType, EntryType> serializer_;
u_int32_t serialized_size_;
char *serialized_data_;
};
TEST_F(TestStdMapSerializer, EmptyMapTestCase) {
const int32_t correct_data[] = { 0 };
u_int32_t correct_size = sizeof(correct_data);
// std_map_ is empty.
serialized_data_ = serializer_.Serialize(std_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
TEST_F(TestStdMapSerializer, MapWithTwoElementsTestCase) {
const int32_t correct_data[] = {
// # of nodes
2,
// Offsets
20, 24,
// Keys
1, 3,
// Values
2, 6
};
u_int32_t correct_size = sizeof(correct_data);
std_map_.insert(std::make_pair(1, 2));
std_map_.insert(std::make_pair(3, 6));
serialized_data_ = serializer_.Serialize(std_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
TEST_F(TestStdMapSerializer, MapWithFiveElementsTestCase) {
const int32_t correct_data[] = {
// # of nodes
5,
// Offsets
44, 48, 52, 56, 60,
// Keys
1, 2, 3, 4, 5,
// Values
11, 12, 13, 14, 15
};
u_int32_t correct_size = sizeof(correct_data);
for (int i = 1; i < 6; ++i)
std_map_.insert(std::make_pair(i, 10 + i));
serialized_data_ = serializer_.Serialize(std_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
class TestAddressMapSerializer : public ::testing::Test {
protected:
void SetUp() {
serialized_size_ = 0;
serialized_data_ = 0;
}
void TearDown() {
delete [] serialized_data_;
}
google_breakpad::AddressMap<AddrType, EntryType> address_map_;
google_breakpad::AddressMapSerializer<AddrType, EntryType> serializer_;
u_int32_t serialized_size_;
char *serialized_data_;
};
TEST_F(TestAddressMapSerializer, EmptyMapTestCase) {
const int32_t correct_data[] = { 0 };
u_int32_t correct_size = sizeof(correct_data);
// std_map_ is empty.
serialized_data_ = serializer_.Serialize(address_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
TEST_F(TestAddressMapSerializer, MapWithTwoElementsTestCase) {
const int32_t correct_data[] = {
// # of nodes
2,
// Offsets
20, 24,
// Keys
1, 3,
// Values
2, 6
};
u_int32_t correct_size = sizeof(correct_data);
address_map_.Store(1, 2);
address_map_.Store(3, 6);
serialized_data_ = serializer_.Serialize(address_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
TEST_F(TestAddressMapSerializer, MapWithFourElementsTestCase) {
const int32_t correct_data[] = {
// # of nodes
4,
// Offsets
36, 40, 44, 48,
// Keys
-6, -4, 8, 123,
// Values
2, 3, 5, 8
};
u_int32_t correct_size = sizeof(correct_data);
address_map_.Store(-6, 2);
address_map_.Store(-4, 3);
address_map_.Store(8, 5);
address_map_.Store(123, 8);
serialized_data_ = serializer_.Serialize(address_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
class TestRangeMapSerializer : public ::testing::Test {
protected:
void SetUp() {
serialized_size_ = 0;
serialized_data_ = 0;
}
void TearDown() {
delete [] serialized_data_;
}
google_breakpad::RangeMap<AddrType, EntryType> range_map_;
google_breakpad::RangeMapSerializer<AddrType, EntryType> serializer_;
u_int32_t serialized_size_;
char *serialized_data_;
};
TEST_F(TestRangeMapSerializer, EmptyMapTestCase) {
const int32_t correct_data[] = { 0 };
u_int32_t correct_size = sizeof(correct_data);
// range_map_ is empty.
serialized_data_ = serializer_.Serialize(range_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
TEST_F(TestRangeMapSerializer, MapWithOneRangeTestCase) {
const int32_t correct_data[] = {
// # of nodes
1,
// Offsets
12,
// Keys: high address
10,
// Values: (low address, entry) pairs
1, 6
};
u_int32_t correct_size = sizeof(correct_data);
range_map_.StoreRange(1, 10, 6);
serialized_data_ = serializer_.Serialize(range_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
TEST_F(TestRangeMapSerializer, MapWithThreeRangesTestCase) {
const int32_t correct_data[] = {
// # of nodes
3,
// Offsets
28, 36, 44,
// Keys: high address
5, 9, 20,
// Values: (low address, entry) pairs
2, 1, 6, 2, 10, 3
};
u_int32_t correct_size = sizeof(correct_data);
ASSERT_TRUE(range_map_.StoreRange(2, 4, 1));
ASSERT_TRUE(range_map_.StoreRange(6, 4, 2));
ASSERT_TRUE(range_map_.StoreRange(10, 11, 3));
serialized_data_ = serializer_.Serialize(range_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
class TestContainedRangeMapSerializer : public ::testing::Test {
protected:
void SetUp() {
serialized_size_ = 0;
serialized_data_ = 0;
}
void TearDown() {
delete [] serialized_data_;
}
google_breakpad::ContainedRangeMap<AddrType, EntryType> crm_map_;
google_breakpad::ContainedRangeMapSerializer<AddrType, EntryType> serializer_;
u_int32_t serialized_size_;
char *serialized_data_;
};
TEST_F(TestContainedRangeMapSerializer, EmptyMapTestCase) {
const int32_t correct_data[] = {
0, // base address of root
4, // size of entry
0, // entry stored at root
0 // empty map stored at root
};
u_int32_t correct_size = sizeof(correct_data);
// crm_map_ is empty.
serialized_data_ = serializer_.Serialize(&crm_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
TEST_F(TestContainedRangeMapSerializer, MapWithOneRangeTestCase) {
const int32_t correct_data[] = {
0, // base address of root
4, // size of entry
0, // entry stored at root
// Map stored at root node:
1, // # of nodes
12, // offset
9, // key
// value: a child ContainedRangeMap
3, // base address of child CRM
4, // size of entry
-1, // entry stored in child CRM
0 // empty sub-map stored in child CRM
};
u_int32_t correct_size = sizeof(correct_data);
crm_map_.StoreRange(3, 7, -1);
serialized_data_ = serializer_.Serialize(&crm_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
TEST_F(TestContainedRangeMapSerializer, MapWithTwoLevelsTestCase) {
// Tree structure of ranges:
// root level 0
// |
// map
// / \ level 1: child1, child2
// 2~8 10~20
// | |
// map map
// / \ |
// 3~4 6~7 16-20 level 2: grandchild1, grandchild2, grandchild3
const int32_t correct_data[] = {
// root: base, entry_size, entry
0, 4, 0,
// root's map: # of nodes, offset1, offset2, key1, key2
2, 20, 84, 8, 20,
// child1: base, entry_size, entry:
2, 4, -1,
// child1's map: # of nodes, offset1, offset2, key1, key2
2, 20, 36, 4, 7,
// grandchild1: base, entry_size, entry, empty_map
3, 4, -1, 0,
// grandchild2: base, entry_size, entry, empty_map
6, 4, -1, 0,
// child2: base, entry_size, entry:
10, 4, -1,
// child2's map: # of nodes, offset1, key1
1, 12, 20,
// grandchild3: base, entry_size, entry, empty_map
16, 4, -1, 0
};
u_int32_t correct_size = sizeof(correct_data);
// Store child1.
ASSERT_TRUE(crm_map_.StoreRange(2, 7, -1));
// Store child2.
ASSERT_TRUE(crm_map_.StoreRange(10, 11, -1));
// Store grandchild1.
ASSERT_TRUE(crm_map_.StoreRange(3, 2, -1));
// Store grandchild2.
ASSERT_TRUE(crm_map_.StoreRange(6, 2, -1));
// Store grandchild3.
ASSERT_TRUE(crm_map_.StoreRange(16, 5, -1));
serialized_data_ = serializer_.Serialize(&crm_map_, &serialized_size_);
EXPECT_EQ(correct_size, serialized_size_);
EXPECT_EQ(memcmp(correct_data, serialized_data_, correct_size), 0);
}
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

4214
thirdparty/breakpad/processor/minidump.cc vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,214 @@
// 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.
// minidump_dump.cc: Print the contents of a minidump file in somewhat
// readable text.
//
// Author: Mark Mentovai
#include <stdio.h>
#include <string.h>
#include "client/linux/minidump_writer/minidump_extension_linux.h"
#include "google_breakpad/processor/minidump.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
namespace {
using google_breakpad::Minidump;
using google_breakpad::MinidumpThreadList;
using google_breakpad::MinidumpModuleList;
using google_breakpad::MinidumpMemoryInfoList;
using google_breakpad::MinidumpMemoryList;
using google_breakpad::MinidumpException;
using google_breakpad::MinidumpAssertion;
using google_breakpad::MinidumpSystemInfo;
using google_breakpad::MinidumpMiscInfo;
using google_breakpad::MinidumpBreakpadInfo;
static void DumpRawStream(Minidump *minidump,
u_int32_t stream_type,
const char *stream_name,
int *errors) {
u_int32_t length = 0;
if (!minidump->SeekToStreamType(stream_type, &length)) {
return;
}
printf("Stream %s:\n", stream_name);
if (length == 0) {
printf("\n");
return;
}
std::vector<char> contents(length);
if (!minidump->ReadBytes(&contents[0], length)) {
++*errors;
BPLOG(ERROR) << "minidump.ReadBytes failed";
return;
}
size_t current_offset = 0;
while (current_offset < length) {
size_t remaining = length - current_offset;
// Printf requires an int and direct casting from size_t results
// in compatibility warnings.
u_int32_t int_remaining = remaining;
printf("%.*s", int_remaining, &contents[current_offset]);
char *next_null = reinterpret_cast<char *>(
memchr(&contents[current_offset], 0, remaining));
if (next_null == NULL)
break;
printf("\\0\n");
size_t null_offset = next_null - &contents[0];
current_offset = null_offset + 1;
}
printf("\n\n");
}
static bool PrintMinidumpDump(const char *minidump_file) {
Minidump minidump(minidump_file);
if (!minidump.Read()) {
BPLOG(ERROR) << "minidump.Read() failed";
return false;
}
minidump.Print();
int errors = 0;
MinidumpThreadList *thread_list = minidump.GetThreadList();
if (!thread_list) {
++errors;
BPLOG(ERROR) << "minidump.GetThreadList() failed";
} else {
thread_list->Print();
}
MinidumpModuleList *module_list = minidump.GetModuleList();
if (!module_list) {
++errors;
BPLOG(ERROR) << "minidump.GetModuleList() failed";
} else {
module_list->Print();
}
MinidumpMemoryList *memory_list = minidump.GetMemoryList();
if (!memory_list) {
++errors;
BPLOG(ERROR) << "minidump.GetMemoryList() failed";
} else {
memory_list->Print();
}
MinidumpException *exception = minidump.GetException();
if (!exception) {
BPLOG(INFO) << "minidump.GetException() failed";
} else {
exception->Print();
}
MinidumpAssertion *assertion = minidump.GetAssertion();
if (!assertion) {
BPLOG(INFO) << "minidump.GetAssertion() failed";
} else {
assertion->Print();
}
MinidumpSystemInfo *system_info = minidump.GetSystemInfo();
if (!system_info) {
++errors;
BPLOG(ERROR) << "minidump.GetSystemInfo() failed";
} else {
system_info->Print();
}
MinidumpMiscInfo *misc_info = minidump.GetMiscInfo();
if (!misc_info) {
++errors;
BPLOG(ERROR) << "minidump.GetMiscInfo() failed";
} else {
misc_info->Print();
}
MinidumpBreakpadInfo *breakpad_info = minidump.GetBreakpadInfo();
if (!breakpad_info) {
// Breakpad info is optional, so don't treat this as an error.
BPLOG(INFO) << "minidump.GetBreakpadInfo() failed";
} else {
breakpad_info->Print();
}
MinidumpMemoryInfoList *memory_info_list = minidump.GetMemoryInfoList();
if (!memory_info_list) {
++errors;
BPLOG(ERROR) << "minidump.GetMemoryInfoList() failed";
} else {
memory_info_list->Print();
}
DumpRawStream(&minidump,
MD_LINUX_CMD_LINE,
"MD_LINUX_CMD_LINE",
&errors);
DumpRawStream(&minidump,
MD_LINUX_ENVIRON,
"MD_LINUX_ENVIRON",
&errors);
DumpRawStream(&minidump,
MD_LINUX_LSB_RELEASE,
"MD_LINUX_LSB_RELEASE",
&errors);
DumpRawStream(&minidump,
MD_LINUX_PROC_STATUS,
"MD_LINUX_PROC_STATUS",
&errors);
DumpRawStream(&minidump,
MD_LINUX_CPU_INFO,
"MD_LINUX_CPU_INFO",
&errors);
DumpRawStream(&minidump,
MD_LINUX_MAPS,
"MD_LINUX_MAPS",
&errors);
return errors == 0;
}
} // namespace
int main(int argc, char **argv) {
BPLOG_INIT(&argc, &argv);
if (argc != 2) {
fprintf(stderr, "usage: %s <file>\n", argv[0]);
return 1;
}
return PrintMinidumpDump(argv[1]) ? 0 : 1;
}

View File

@@ -0,0 +1,36 @@
#!/bin/sh
# 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.
testdata_dir=$srcdir/src/processor/testdata
./src/processor/minidump_dump $testdata_dir/minidump2.dmp | \
tr -d '\015' | \
diff -u $testdata_dir/minidump2.dump.out -
exit $?

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,380 @@
// 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.
// Unit test for MinidumpProcessor. Uses a pre-generated minidump and
// corresponding symbol file, and checks the stack frames for correctness.
#include <stdlib.h>
#include <string>
#include <iostream>
#include <fstream>
#include <map>
#include <utility>
#include "breakpad_googletest_includes.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/code_modules.h"
#include "google_breakpad/processor/minidump.h"
#include "google_breakpad/processor/minidump_processor.h"
#include "google_breakpad/processor/process_state.h"
#include "google_breakpad/processor/stack_frame.h"
#include "google_breakpad/processor/symbol_supplier.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
using std::map;
namespace google_breakpad {
class MockMinidump : public Minidump {
public:
MockMinidump() : Minidump("") {
}
MOCK_METHOD0(Read, bool());
MOCK_CONST_METHOD0(path, string());
MOCK_CONST_METHOD0(header, const MDRawHeader*());
MOCK_METHOD0(GetThreadList, MinidumpThreadList*());
};
}
namespace {
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::CodeModule;
using google_breakpad::MinidumpProcessor;
using google_breakpad::MinidumpThreadList;
using google_breakpad::MinidumpThread;
using google_breakpad::MockMinidump;
using google_breakpad::ProcessState;
using google_breakpad::scoped_ptr;
using google_breakpad::SymbolSupplier;
using google_breakpad::SystemInfo;
using std::string;
using ::testing::_;
using ::testing::Mock;
using ::testing::Ne;
using ::testing::Property;
using ::testing::Return;
static const char *kSystemInfoOS = "Windows NT";
static const char *kSystemInfoOSShort = "windows";
static const char *kSystemInfoOSVersion = "5.1.2600 Service Pack 2";
static const char *kSystemInfoCPU = "x86";
static const char *kSystemInfoCPUInfo =
"GenuineIntel family 6 model 13 stepping 8";
#define ASSERT_TRUE_ABORT(cond) \
if (!(cond)) { \
fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
abort(); \
}
#define ASSERT_EQ_ABORT(e1, e2) ASSERT_TRUE_ABORT((e1) == (e2))
class TestSymbolSupplier : public SymbolSupplier {
public:
TestSymbolSupplier() : interrupt_(false) {}
virtual SymbolResult GetSymbolFile(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file);
virtual SymbolResult GetSymbolFile(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
string *symbol_data);
virtual SymbolResult GetCStringSymbolData(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
char **symbol_data);
virtual void FreeSymbolData(const CodeModule *module);
// When set to true, causes the SymbolSupplier to return INTERRUPT
void set_interrupt(bool interrupt) { interrupt_ = interrupt; }
private:
bool interrupt_;
map<string, char *> memory_buffers_;
};
SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file) {
ASSERT_TRUE_ABORT(module);
ASSERT_TRUE_ABORT(system_info);
ASSERT_EQ_ABORT(system_info->cpu, kSystemInfoCPU);
ASSERT_EQ_ABORT(system_info->cpu_info, kSystemInfoCPUInfo);
ASSERT_EQ_ABORT(system_info->os, kSystemInfoOS);
ASSERT_EQ_ABORT(system_info->os_short, kSystemInfoOSShort);
ASSERT_EQ_ABORT(system_info->os_version, kSystemInfoOSVersion);
if (interrupt_) {
return INTERRUPT;
}
if (module && module->code_file() == "c:\\test_app.exe") {
*symbol_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/symbols/test_app.pdb/" +
module->debug_identifier() +
"/test_app.sym";
return FOUND;
}
return NOT_FOUND;
}
SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
string *symbol_data) {
SymbolSupplier::SymbolResult s = GetSymbolFile(module, system_info,
symbol_file);
if (s == FOUND) {
std::ifstream in(symbol_file->c_str());
std::getline(in, *symbol_data, std::string::traits_type::to_char_type(
std::string::traits_type::eof()));
in.close();
}
return s;
}
SymbolSupplier::SymbolResult TestSymbolSupplier::GetCStringSymbolData(
const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
char **symbol_data) {
string symbol_data_string;
SymbolSupplier::SymbolResult s = GetSymbolFile(module,
system_info,
symbol_file,
&symbol_data_string);
if (s == FOUND) {
unsigned int size = symbol_data_string.size() + 1;
*symbol_data = new char[size];
if (*symbol_data == NULL) {
BPLOG(ERROR) << "Memory allocation failed for module: "
<< module->code_file() << " size: " << size;
return INTERRUPT;
}
strcpy(*symbol_data, symbol_data_string.c_str());
memory_buffers_.insert(make_pair(module->code_file(), *symbol_data));
}
return s;
}
void TestSymbolSupplier::FreeSymbolData(const CodeModule *module) {
map<string, char *>::iterator it = memory_buffers_.find(module->code_file());
if (it != memory_buffers_.end()) {
delete [] it->second;
memory_buffers_.erase(it);
}
}
// A mock symbol supplier that always returns NOT_FOUND; one current
// use for testing the processor's caching of symbol lookups.
class MockSymbolSupplier : public SymbolSupplier {
public:
MockSymbolSupplier() { }
MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule*,
const SystemInfo*,
string*));
MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule*,
const SystemInfo*,
string*,
string*));
MOCK_METHOD4(GetCStringSymbolData, SymbolResult(const CodeModule*,
const SystemInfo*,
string*,
char**));
MOCK_METHOD1(FreeSymbolData, void(const CodeModule*));
};
class MinidumpProcessorTest : public ::testing::Test {
};
TEST_F(MinidumpProcessorTest, TestCorruptMinidumps) {
MockMinidump dump;
TestSymbolSupplier supplier;
BasicSourceLineResolver resolver;
MinidumpProcessor processor(&supplier, &resolver);
ProcessState state;
EXPECT_EQ(processor.Process("nonexistent minidump", &state),
google_breakpad::PROCESS_ERROR_MINIDUMP_NOT_FOUND);
EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true));
MDRawHeader fakeHeader;
fakeHeader.time_date_stamp = 0;
EXPECT_CALL(dump, header()).WillOnce(Return((MDRawHeader*)NULL)).
WillRepeatedly(Return(&fakeHeader));
EXPECT_EQ(processor.Process(&dump, &state),
google_breakpad::PROCESS_ERROR_NO_MINIDUMP_HEADER);
EXPECT_CALL(dump, GetThreadList()).
WillOnce(Return((MinidumpThreadList*)NULL));
EXPECT_EQ(processor.Process(&dump, &state),
google_breakpad::PROCESS_ERROR_NO_THREAD_LIST);
}
// This test case verifies that the symbol supplier is only consulted
// once per minidump per module.
TEST_F(MinidumpProcessorTest, TestSymbolSupplierLookupCounts) {
MockSymbolSupplier supplier;
BasicSourceLineResolver resolver;
MinidumpProcessor processor(&supplier, &resolver);
string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/minidump2.dmp";
ProcessState state;
EXPECT_CALL(supplier, GetCStringSymbolData(
Property(&google_breakpad::CodeModule::code_file,
"c:\\test_app.exe"),
_, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND));
EXPECT_CALL(supplier, GetCStringSymbolData(
Property(&google_breakpad::CodeModule::code_file,
Ne("c:\\test_app.exe")),
_, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND));
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_TRUE(Mock::VerifyAndClearExpectations(&supplier));
// We need to verify that across minidumps, the processor will refetch
// symbol files, even with the same symbol supplier.
EXPECT_CALL(supplier, GetCStringSymbolData(
Property(&google_breakpad::CodeModule::code_file,
"c:\\test_app.exe"),
_, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND));
EXPECT_CALL(supplier, GetCStringSymbolData(
Property(&google_breakpad::CodeModule::code_file,
Ne("c:\\test_app.exe")),
_, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND));
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
}
TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
TestSymbolSupplier supplier;
BasicSourceLineResolver resolver;
MinidumpProcessor processor(&supplier, &resolver);
string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/minidump2.dmp";
ProcessState state;
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_EQ(state.system_info()->os, kSystemInfoOS);
ASSERT_EQ(state.system_info()->os_short, kSystemInfoOSShort);
ASSERT_EQ(state.system_info()->os_version, kSystemInfoOSVersion);
ASSERT_EQ(state.system_info()->cpu, kSystemInfoCPU);
ASSERT_EQ(state.system_info()->cpu_info, kSystemInfoCPUInfo);
ASSERT_TRUE(state.crashed());
ASSERT_EQ(state.crash_reason(), "EXCEPTION_ACCESS_VIOLATION_WRITE");
ASSERT_EQ(state.crash_address(), 0x45U);
ASSERT_EQ(state.threads()->size(), size_t(1));
ASSERT_EQ(state.requesting_thread(), 0);
CallStack *stack = state.threads()->at(0);
ASSERT_TRUE(stack);
ASSERT_EQ(stack->frames()->size(), 4U);
ASSERT_TRUE(stack->frames()->at(0)->module);
ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000U);
ASSERT_EQ(stack->frames()->at(0)->module->code_file(), "c:\\test_app.exe");
ASSERT_EQ(stack->frames()->at(0)->function_name,
"`anonymous namespace'::CrashFunction");
ASSERT_EQ(stack->frames()->at(0)->source_file_name, "c:\\test_app.cc");
ASSERT_EQ(stack->frames()->at(0)->source_line, 58);
ASSERT_TRUE(stack->frames()->at(1)->module);
ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000U);
ASSERT_EQ(stack->frames()->at(1)->module->code_file(), "c:\\test_app.exe");
ASSERT_EQ(stack->frames()->at(1)->function_name, "main");
ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc");
ASSERT_EQ(stack->frames()->at(1)->source_line, 65);
// This comes from the CRT
ASSERT_TRUE(stack->frames()->at(2)->module);
ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000U);
ASSERT_EQ(stack->frames()->at(2)->module->code_file(), "c:\\test_app.exe");
ASSERT_EQ(stack->frames()->at(2)->function_name, "__tmainCRTStartup");
ASSERT_EQ(stack->frames()->at(2)->source_file_name,
"f:\\sp\\vctools\\crt_bld\\self_x86\\crt\\src\\crt0.c");
ASSERT_EQ(stack->frames()->at(2)->source_line, 327);
// No debug info available for kernel32.dll
ASSERT_TRUE(stack->frames()->at(3)->module);
ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000U);
ASSERT_EQ(stack->frames()->at(3)->module->code_file(),
"C:\\WINDOWS\\system32\\kernel32.dll");
ASSERT_TRUE(stack->frames()->at(3)->function_name.empty());
ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty());
ASSERT_EQ(stack->frames()->at(3)->source_line, 0);
ASSERT_EQ(state.modules()->module_count(), 13U);
ASSERT_TRUE(state.modules()->GetMainModule());
ASSERT_EQ(state.modules()->GetMainModule()->code_file(), "c:\\test_app.exe");
ASSERT_FALSE(state.modules()->GetModuleForAddress(0));
ASSERT_EQ(state.modules()->GetMainModule(),
state.modules()->GetModuleForAddress(0x400000));
ASSERT_EQ(state.modules()->GetModuleForAddress(0x7c801234)->debug_file(),
"kernel32.pdb");
ASSERT_EQ(state.modules()->GetModuleForAddress(0x77d43210)->version(),
"5.1.2600.2622");
// Test that disabled exploitability engine defaults to
// EXPLOITABILITY_NOT_ANALYZED.
ASSERT_EQ(google_breakpad::EXPLOITABILITY_NOT_ANALYZED,
state.exploitability());
// Test that the symbol supplier can interrupt processing
state.Clear();
supplier.set_interrupt(true);
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_SYMBOL_SUPPLIER_INTERRUPTED);
}
} // namespace
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,587 @@
// Copyright (c) 2010 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.
// minidump_stackwalk.cc: Process a minidump with MinidumpProcessor, printing
// the results, including stack traces.
//
// Author: Mark Mentovai
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/code_modules.h"
#include "google_breakpad/processor/minidump.h"
#include "google_breakpad/processor/minidump_processor.h"
#include "google_breakpad/processor/process_state.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/logging.h"
#include "processor/pathname_stripper.h"
#include "processor/scoped_ptr.h"
#include "processor/simple_symbol_supplier.h"
namespace {
using std::string;
using std::vector;
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::CodeModule;
using google_breakpad::CodeModules;
using google_breakpad::MinidumpModule;
using google_breakpad::MinidumpProcessor;
using google_breakpad::PathnameStripper;
using google_breakpad::ProcessState;
using google_breakpad::scoped_ptr;
using google_breakpad::SimpleSymbolSupplier;
using google_breakpad::StackFrame;
using google_breakpad::StackFramePPC;
using google_breakpad::StackFrameSPARC;
using google_breakpad::StackFrameX86;
using google_breakpad::StackFrameAMD64;
using google_breakpad::StackFrameARM;
// Separator character for machine readable output.
static const char kOutputSeparator = '|';
// PrintRegister prints a register's name and value to stdout. It will
// print four registers on a line. For the first register in a set,
// pass 0 for |start_col|. For registers in a set, pass the most recent
// return value of PrintRegister.
// The caller is responsible for printing the final newline after a set
// of registers is completely printed, regardless of the number of calls
// to PrintRegister.
static const int kMaxWidth = 80; // optimize for an 80-column terminal
static int PrintRegister(const char *name, u_int32_t value, int start_col) {
char buffer[64];
snprintf(buffer, sizeof(buffer), " %5s = 0x%08x", name, value);
if (start_col + strlen(buffer) > kMaxWidth) {
start_col = 0;
printf("\n ");
}
fputs(buffer, stdout);
return start_col + strlen(buffer);
}
// PrintRegister64 does the same thing, but for 64-bit registers.
static int PrintRegister64(const char *name, u_int64_t value, int start_col) {
char buffer[64];
snprintf(buffer, sizeof(buffer), " %5s = 0x%016" PRIx64 , name, value);
if (start_col + strlen(buffer) > kMaxWidth) {
start_col = 0;
printf("\n ");
}
fputs(buffer, stdout);
return start_col + strlen(buffer);
}
// StripSeparator takes a string |original| and returns a copy
// of the string with all occurences of |kOutputSeparator| removed.
static string StripSeparator(const string &original) {
string result = original;
string::size_type position = 0;
while ((position = result.find(kOutputSeparator, position)) != string::npos) {
result.erase(position, 1);
}
position = 0;
while ((position = result.find('\n', position)) != string::npos) {
result.erase(position, 1);
}
return result;
}
// PrintStack prints the call stack in |stack| to stdout, in a reasonably
// useful form. Module, function, and source file names are displayed if
// they are available. The code offset to the base code address of the
// source line, function, or module is printed, preferring them in that
// order. If no source line, function, or module information is available,
// an absolute code offset is printed.
//
// If |cpu| is a recognized CPU name, relevant register state for each stack
// frame printed is also output, if available.
static void PrintStack(const CallStack *stack, const string &cpu) {
int frame_count = stack->frames()->size();
for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
const StackFrame *frame = stack->frames()->at(frame_index);
printf("%2d ", frame_index);
if (frame->module) {
printf("%s", PathnameStripper::File(frame->module->code_file()).c_str());
if (!frame->function_name.empty()) {
printf("!%s", frame->function_name.c_str());
if (!frame->source_file_name.empty()) {
string source_file = PathnameStripper::File(frame->source_file_name);
printf(" [%s : %d + 0x%" PRIx64 "]",
source_file.c_str(),
frame->source_line,
frame->instruction - frame->source_line_base);
} else {
printf(" + 0x%" PRIx64, frame->instruction - frame->function_base);
}
} else {
printf(" + 0x%" PRIx64,
frame->instruction - frame->module->base_address());
}
} else {
printf("0x%" PRIx64, frame->instruction);
}
printf("\n ");
int sequence = 0;
if (cpu == "x86") {
const StackFrameX86 *frame_x86 =
reinterpret_cast<const StackFrameX86*>(frame);
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EIP)
sequence = PrintRegister("eip", frame_x86->context.eip, sequence);
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESP)
sequence = PrintRegister("esp", frame_x86->context.esp, sequence);
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBP)
sequence = PrintRegister("ebp", frame_x86->context.ebp, sequence);
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EBX)
sequence = PrintRegister("ebx", frame_x86->context.ebx, sequence);
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_ESI)
sequence = PrintRegister("esi", frame_x86->context.esi, sequence);
if (frame_x86->context_validity & StackFrameX86::CONTEXT_VALID_EDI)
sequence = PrintRegister("edi", frame_x86->context.edi, sequence);
if (frame_x86->context_validity == StackFrameX86::CONTEXT_VALID_ALL) {
sequence = PrintRegister("eax", frame_x86->context.eax, sequence);
sequence = PrintRegister("ecx", frame_x86->context.ecx, sequence);
sequence = PrintRegister("edx", frame_x86->context.edx, sequence);
sequence = PrintRegister("efl", frame_x86->context.eflags, sequence);
}
} else if (cpu == "ppc") {
const StackFramePPC *frame_ppc =
reinterpret_cast<const StackFramePPC*>(frame);
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_SRR0)
sequence = PrintRegister("srr0", frame_ppc->context.srr0, sequence);
if (frame_ppc->context_validity & StackFramePPC::CONTEXT_VALID_GPR1)
sequence = PrintRegister("r1", frame_ppc->context.gpr[1], sequence);
} else if (cpu == "amd64") {
const StackFrameAMD64 *frame_amd64 =
reinterpret_cast<const StackFrameAMD64*>(frame);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX)
sequence = PrintRegister64("rbx", frame_amd64->context.rbx, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12)
sequence = PrintRegister64("r12", frame_amd64->context.r12, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13)
sequence = PrintRegister64("r13", frame_amd64->context.r13, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14)
sequence = PrintRegister64("r14", frame_amd64->context.r14, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15)
sequence = PrintRegister64("r15", frame_amd64->context.r15, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP)
sequence = PrintRegister64("rip", frame_amd64->context.rip, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP)
sequence = PrintRegister64("rsp", frame_amd64->context.rsp, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP)
sequence = PrintRegister64("rbp", frame_amd64->context.rbp, sequence);
} else if (cpu == "sparc") {
const StackFrameSPARC *frame_sparc =
reinterpret_cast<const StackFrameSPARC*>(frame);
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_SP)
sequence = PrintRegister("sp", frame_sparc->context.g_r[14], sequence);
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_FP)
sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence);
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC)
sequence = PrintRegister("pc", frame_sparc->context.pc, sequence);
} else if (cpu == "arm") {
const StackFrameARM *frame_arm =
reinterpret_cast<const StackFrameARM*>(frame);
// General-purpose callee-saves registers.
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4)
sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5)
sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6)
sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7)
sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8)
sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9)
sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10)
sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence);
// Registers with a dedicated or conventional purpose.
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP)
sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)
sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR)
sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
}
printf("\n Found by: %s\n", frame->trust_description().c_str());
}
}
// PrintStackMachineReadable prints the call stack in |stack| to stdout,
// in the following machine readable pipe-delimited text format:
// thread number|frame number|module|function|source file|line|offset
//
// Module, function, source file, and source line may all be empty
// depending on availability. The code offset follows the same rules as
// PrintStack above.
static void PrintStackMachineReadable(int thread_num, const CallStack *stack) {
int frame_count = stack->frames()->size();
for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
const StackFrame *frame = stack->frames()->at(frame_index);
printf("%d%c%d%c", thread_num, kOutputSeparator, frame_index,
kOutputSeparator);
if (frame->module) {
assert(!frame->module->code_file().empty());
printf("%s", StripSeparator(PathnameStripper::File(
frame->module->code_file())).c_str());
if (!frame->function_name.empty()) {
printf("%c%s", kOutputSeparator,
StripSeparator(frame->function_name).c_str());
if (!frame->source_file_name.empty()) {
printf("%c%s%c%d%c0x%" PRIx64,
kOutputSeparator,
StripSeparator(frame->source_file_name).c_str(),
kOutputSeparator,
frame->source_line,
kOutputSeparator,
frame->instruction - frame->source_line_base);
} else {
printf("%c%c%c0x%" PRIx64,
kOutputSeparator, // empty source file
kOutputSeparator, // empty source line
kOutputSeparator,
frame->instruction - frame->function_base);
}
} else {
printf("%c%c%c%c0x%" PRIx64,
kOutputSeparator, // empty function name
kOutputSeparator, // empty source file
kOutputSeparator, // empty source line
kOutputSeparator,
frame->instruction - frame->module->base_address());
}
} else {
// the printf before this prints a trailing separator for module name
printf("%c%c%c%c0x%" PRIx64,
kOutputSeparator, // empty function name
kOutputSeparator, // empty source file
kOutputSeparator, // empty source line
kOutputSeparator,
frame->instruction);
}
printf("\n");
}
}
static void PrintModules(const CodeModules *modules) {
if (!modules)
return;
printf("\n");
printf("Loaded modules:\n");
u_int64_t main_address = 0;
const CodeModule *main_module = modules->GetMainModule();
if (main_module) {
main_address = main_module->base_address();
}
unsigned int module_count = modules->module_count();
for (unsigned int module_sequence = 0;
module_sequence < module_count;
++module_sequence) {
const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
u_int64_t base_address = module->base_address();
printf("0x%08" PRIx64 " - 0x%08" PRIx64 " %s %s%s\n",
base_address, base_address + module->size() - 1,
PathnameStripper::File(module->code_file()).c_str(),
module->version().empty() ? "???" : module->version().c_str(),
main_module != NULL && base_address == main_address ?
" (main)" : "");
}
}
// PrintModulesMachineReadable outputs a list of loaded modules,
// one per line, in the following machine-readable pipe-delimited
// text format:
// Module|{Module Filename}|{Version}|{Debug Filename}|{Debug Identifier}|
// {Base Address}|{Max Address}|{Main}
static void PrintModulesMachineReadable(const CodeModules *modules) {
if (!modules)
return;
u_int64_t main_address = 0;
const CodeModule *main_module = modules->GetMainModule();
if (main_module) {
main_address = main_module->base_address();
}
unsigned int module_count = modules->module_count();
for (unsigned int module_sequence = 0;
module_sequence < module_count;
++module_sequence) {
const CodeModule *module = modules->GetModuleAtSequence(module_sequence);
u_int64_t base_address = module->base_address();
printf("Module%c%s%c%s%c%s%c%s%c0x%08" PRIx64 "%c0x%08" PRIx64 "%c%d\n",
kOutputSeparator,
StripSeparator(PathnameStripper::File(module->code_file())).c_str(),
kOutputSeparator, StripSeparator(module->version()).c_str(),
kOutputSeparator,
StripSeparator(PathnameStripper::File(module->debug_file())).c_str(),
kOutputSeparator,
StripSeparator(module->debug_identifier()).c_str(),
kOutputSeparator, base_address,
kOutputSeparator, base_address + module->size() - 1,
kOutputSeparator,
main_module != NULL && base_address == main_address ? 1 : 0);
}
}
static void PrintProcessState(const ProcessState& process_state) {
// Print OS and CPU information.
string cpu = process_state.system_info()->cpu;
string cpu_info = process_state.system_info()->cpu_info;
printf("Operating system: %s\n", process_state.system_info()->os.c_str());
printf(" %s\n",
process_state.system_info()->os_version.c_str());
printf("CPU: %s\n", cpu.c_str());
if (!cpu_info.empty()) {
// This field is optional.
printf(" %s\n", cpu_info.c_str());
}
printf(" %d CPU%s\n",
process_state.system_info()->cpu_count,
process_state.system_info()->cpu_count != 1 ? "s" : "");
printf("\n");
// Print crash information.
if (process_state.crashed()) {
printf("Crash reason: %s\n", process_state.crash_reason().c_str());
printf("Crash address: 0x%" PRIx64 "\n", process_state.crash_address());
} else {
printf("No crash\n");
}
string assertion = process_state.assertion();
if (!assertion.empty()) {
printf("Assertion: %s\n", assertion.c_str());
}
// If the thread that requested the dump is known, print it first.
int requesting_thread = process_state.requesting_thread();
if (requesting_thread != -1) {
printf("\n");
printf("Thread %d (%s)\n",
requesting_thread,
process_state.crashed() ? "crashed" :
"requested dump, did not crash");
PrintStack(process_state.threads()->at(requesting_thread), cpu);
}
// Print all of the threads in the dump.
int thread_count = process_state.threads()->size();
for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
if (thread_index != requesting_thread) {
// Don't print the crash thread again, it was already printed.
printf("\n");
printf("Thread %d\n", thread_index);
PrintStack(process_state.threads()->at(thread_index), cpu);
}
}
PrintModules(process_state.modules());
}
static void PrintProcessStateMachineReadable(const ProcessState& process_state)
{
// Print OS and CPU information.
// OS|{OS Name}|{OS Version}
// CPU|{CPU Name}|{CPU Info}|{Number of CPUs}
printf("OS%c%s%c%s\n", kOutputSeparator,
StripSeparator(process_state.system_info()->os).c_str(),
kOutputSeparator,
StripSeparator(process_state.system_info()->os_version).c_str());
printf("CPU%c%s%c%s%c%d\n", kOutputSeparator,
StripSeparator(process_state.system_info()->cpu).c_str(),
kOutputSeparator,
// this may be empty
StripSeparator(process_state.system_info()->cpu_info).c_str(),
kOutputSeparator,
process_state.system_info()->cpu_count);
int requesting_thread = process_state.requesting_thread();
// Print crash information.
// Crash|{Crash Reason}|{Crash Address}|{Crashed Thread}
printf("Crash%c", kOutputSeparator);
if (process_state.crashed()) {
printf("%s%c0x%" PRIx64 "%c",
StripSeparator(process_state.crash_reason()).c_str(),
kOutputSeparator, process_state.crash_address(), kOutputSeparator);
} else {
// print assertion info, if available, in place of crash reason,
// instead of the unhelpful "No crash"
string assertion = process_state.assertion();
if (!assertion.empty()) {
printf("%s%c%c", StripSeparator(assertion).c_str(),
kOutputSeparator, kOutputSeparator);
} else {
printf("No crash%c%c", kOutputSeparator, kOutputSeparator);
}
}
if (requesting_thread != -1) {
printf("%d\n", requesting_thread);
} else {
printf("\n");
}
PrintModulesMachineReadable(process_state.modules());
// blank line to indicate start of threads
printf("\n");
// If the thread that requested the dump is known, print it first.
if (requesting_thread != -1) {
PrintStackMachineReadable(requesting_thread,
process_state.threads()->at(requesting_thread));
}
// Print all of the threads in the dump.
int thread_count = process_state.threads()->size();
for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
if (thread_index != requesting_thread) {
// Don't print the crash thread again, it was already printed.
PrintStackMachineReadable(thread_index,
process_state.threads()->at(thread_index));
}
}
}
// Processes |minidump_file| using MinidumpProcessor. |symbol_path|, if
// non-empty, is the base directory of a symbol storage area, laid out in
// the format required by SimpleSymbolSupplier. If such a storage area
// is specified, it is made available for use by the MinidumpProcessor.
//
// Returns the value of MinidumpProcessor::Process. If processing succeeds,
// prints identifying OS and CPU information from the minidump, crash
// information if the minidump was produced as a result of a crash, and
// call stacks for each thread contained in the minidump. All information
// is printed to stdout.
static bool PrintMinidumpProcess(const string &minidump_file,
const vector<string> &symbol_paths,
bool machine_readable) {
scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
if (!symbol_paths.empty()) {
// TODO(mmentovai): check existence of symbol_path if specified?
symbol_supplier.reset(new SimpleSymbolSupplier(symbol_paths));
}
BasicSourceLineResolver resolver;
MinidumpProcessor minidump_processor(symbol_supplier.get(), &resolver);
// Process the minidump.
ProcessState process_state;
if (minidump_processor.Process(minidump_file, &process_state) !=
google_breakpad::PROCESS_OK) {
BPLOG(ERROR) << "MinidumpProcessor::Process failed";
return false;
}
if (machine_readable) {
PrintProcessStateMachineReadable(process_state);
} else {
PrintProcessState(process_state);
}
return true;
}
} // namespace
static void usage(const char *program_name) {
fprintf(stderr, "usage: %s [-m] <minidump-file> [symbol-path ...]\n"
" -m : Output in machine-readable format\n",
program_name);
}
int main(int argc, char **argv) {
BPLOG_INIT(&argc, &argv);
if (argc < 2) {
usage(argv[0]);
return 1;
}
const char *minidump_file;
bool machine_readable;
int symbol_path_arg;
if (strcmp(argv[1], "-m") == 0) {
if (argc < 3) {
usage(argv[0]);
return 1;
}
machine_readable = true;
minidump_file = argv[2];
symbol_path_arg = 3;
} else {
machine_readable = false;
minidump_file = argv[1];
symbol_path_arg = 2;
}
// extra arguments are symbol paths
std::vector<std::string> symbol_paths;
if (argc > symbol_path_arg) {
for (int argi = symbol_path_arg; argi < argc; ++argi)
symbol_paths.push_back(argv[argi]);
}
return PrintMinidumpProcess(minidump_file,
symbol_paths,
machine_readable) ? 0 : 1;
}

View File

@@ -0,0 +1,37 @@
#!/bin/sh
# Copyright (c) 2007, 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.
testdata_dir=$srcdir/src/processor/testdata
./src/processor/minidump_stackwalk -m $testdata_dir/minidump2.dmp \
$testdata_dir/symbols | \
tr -d '\015' | \
diff -u $testdata_dir/minidump2.stackwalk.machine_readable.out -
exit $?

View File

@@ -0,0 +1,37 @@
#!/bin/sh
# 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.
testdata_dir=$srcdir/src/processor/testdata
./src/processor/minidump_stackwalk $testdata_dir/minidump2.dmp \
$testdata_dir/symbols | \
tr -d '\015' | \
diff -u $testdata_dir/minidump2.stackwalk.out -
exit $?

View File

@@ -0,0 +1,907 @@
// Copyright (c) 2010, 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.
// Unit test for Minidump. Uses a pre-generated minidump and
// verifies that certain streams are correct.
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdlib.h>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/minidump.h"
#include "processor/logging.h"
#include "processor/synth_minidump.h"
namespace {
using google_breakpad::Minidump;
using google_breakpad::MinidumpContext;
using google_breakpad::MinidumpException;
using google_breakpad::MinidumpMemoryInfo;
using google_breakpad::MinidumpMemoryInfoList;
using google_breakpad::MinidumpMemoryList;
using google_breakpad::MinidumpMemoryRegion;
using google_breakpad::MinidumpModule;
using google_breakpad::MinidumpModuleList;
using google_breakpad::MinidumpSystemInfo;
using google_breakpad::MinidumpThread;
using google_breakpad::MinidumpThreadList;
using google_breakpad::SynthMinidump::Context;
using google_breakpad::SynthMinidump::Dump;
using google_breakpad::SynthMinidump::Exception;
using google_breakpad::SynthMinidump::Memory;
using google_breakpad::SynthMinidump::Module;
using google_breakpad::SynthMinidump::Stream;
using google_breakpad::SynthMinidump::String;
using google_breakpad::SynthMinidump::SystemInfo;
using google_breakpad::SynthMinidump::Thread;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using std::ifstream;
using std::istringstream;
using std::string;
using std::vector;
using ::testing::Return;
class MinidumpTest : public ::testing::Test {
public:
void SetUp() {
minidump_file_ = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/minidump2.dmp";
}
string minidump_file_;
};
TEST_F(MinidumpTest, TestMinidumpFromFile) {
Minidump minidump(minidump_file_);
ASSERT_EQ(minidump.path(), minidump_file_);
ASSERT_TRUE(minidump.Read());
const MDRawHeader* header = minidump.header();
ASSERT_NE(header, (MDRawHeader*)NULL);
ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE));
//TODO: add more checks here
}
TEST_F(MinidumpTest, TestMinidumpFromStream) {
// read minidump contents into memory, construct a stringstream around them
ifstream file_stream(minidump_file_.c_str(), std::ios::in);
ASSERT_TRUE(file_stream.good());
vector<char> bytes;
file_stream.seekg(0, std::ios_base::end);
ASSERT_TRUE(file_stream.good());
bytes.resize(file_stream.tellg());
file_stream.seekg(0, std::ios_base::beg);
ASSERT_TRUE(file_stream.good());
file_stream.read(&bytes[0], bytes.size());
ASSERT_TRUE(file_stream.good());
string str(&bytes[0], bytes.size());
istringstream stream(str);
ASSERT_TRUE(stream.good());
// now read minidump from stringstream
Minidump minidump(stream);
ASSERT_EQ(minidump.path(), "");
ASSERT_TRUE(minidump.Read());
const MDRawHeader* header = minidump.header();
ASSERT_NE(header, (MDRawHeader*)NULL);
ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE));
//TODO: add more checks here
}
TEST(Dump, ReadBackEmpty) {
Dump dump(0);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream stream(contents);
Minidump minidump(stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
}
TEST(Dump, ReadBackEmptyBigEndian) {
Dump big_minidump(0, kBigEndian);
big_minidump.Finish();
string contents;
ASSERT_TRUE(big_minidump.GetContents(&contents));
istringstream stream(contents);
Minidump minidump(stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
}
TEST(Dump, OneStream) {
Dump dump(0, kBigEndian);
Stream stream(dump, 0xfbb7fa2bU);
stream.Append("stream contents");
dump.Add(&stream);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ(0xfbb7fa2bU, dir->stream_type);
u_int32_t stream_length;
ASSERT_TRUE(minidump.SeekToStreamType(0xfbb7fa2bU, &stream_length));
ASSERT_EQ(15U, stream_length);
char stream_contents[15];
ASSERT_TRUE(minidump.ReadBytes(stream_contents, sizeof(stream_contents)));
EXPECT_EQ(string("stream contents"),
string(stream_contents, sizeof(stream_contents)));
EXPECT_FALSE(minidump.GetThreadList());
EXPECT_FALSE(minidump.GetModuleList());
EXPECT_FALSE(minidump.GetMemoryList());
EXPECT_FALSE(minidump.GetException());
EXPECT_FALSE(minidump.GetAssertion());
EXPECT_FALSE(minidump.GetSystemInfo());
EXPECT_FALSE(minidump.GetMiscInfo());
EXPECT_FALSE(minidump.GetBreakpadInfo());
}
TEST(Dump, OneMemory) {
Dump dump(0, kBigEndian);
Memory memory(dump, 0x309d68010bd21b2cULL);
memory.Append("memory contents");
dump.Add(&memory);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((u_int32_t) MD_MEMORY_LIST_STREAM, dir->stream_type);
MinidumpMemoryList *memory_list = minidump.GetMemoryList();
ASSERT_TRUE(memory_list != NULL);
ASSERT_EQ(1U, memory_list->region_count());
MinidumpMemoryRegion *region1 = memory_list->GetMemoryRegionAtIndex(0);
ASSERT_EQ(0x309d68010bd21b2cULL, region1->GetBase());
ASSERT_EQ(15U, region1->GetSize());
const u_int8_t *region1_bytes = region1->GetMemory();
ASSERT_TRUE(memcmp("memory contents", region1_bytes, 15) == 0);
}
// One thread --- and its requisite entourage.
TEST(Dump, OneThread) {
Dump dump(0, kLittleEndian);
Memory stack(dump, 0x2326a0fa);
stack.Append("stack for thread");
MDRawContextX86 raw_context;
raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
raw_context.edi = 0x3ecba80d;
raw_context.esi = 0x382583b9;
raw_context.ebx = 0x7fccc03f;
raw_context.edx = 0xf62f8ec2;
raw_context.ecx = 0x46a6a6a8;
raw_context.eax = 0x6a5025e2;
raw_context.ebp = 0xd9fabb4a;
raw_context.eip = 0x6913f540;
raw_context.cs = 0xbffe6eda;
raw_context.eflags = 0xb2ce1e2d;
raw_context.esp = 0x659caaa4;
raw_context.ss = 0x2e951ef7;
Context context(dump, raw_context);
Thread thread(dump, 0xa898f11b, stack, context,
0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
dump.Add(&stack);
dump.Add(&context);
dump.Add(&thread);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
MinidumpMemoryList *md_memory_list = minidump.GetMemoryList();
ASSERT_TRUE(md_memory_list != NULL);
ASSERT_EQ(1U, md_memory_list->region_count());
MinidumpMemoryRegion *md_region = md_memory_list->GetMemoryRegionAtIndex(0);
ASSERT_EQ(0x2326a0faU, md_region->GetBase());
ASSERT_EQ(16U, md_region->GetSize());
const u_int8_t *region_bytes = md_region->GetMemory();
ASSERT_TRUE(memcmp("stack for thread", region_bytes, 16) == 0);
MinidumpThreadList *thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list != NULL);
ASSERT_EQ(1U, thread_list->thread_count());
MinidumpThread *md_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(md_thread != NULL);
u_int32_t thread_id;
ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
ASSERT_EQ(0xa898f11bU, thread_id);
MinidumpMemoryRegion *md_stack = md_thread->GetMemory();
ASSERT_TRUE(md_stack != NULL);
ASSERT_EQ(0x2326a0faU, md_stack->GetBase());
ASSERT_EQ(16U, md_stack->GetSize());
const u_int8_t *md_stack_bytes = md_stack->GetMemory();
ASSERT_TRUE(memcmp("stack for thread", md_stack_bytes, 16) == 0);
MinidumpContext *md_context = md_thread->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
(md_raw_context->context_flags
& (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
EXPECT_EQ(0x3ecba80dU, raw_context.edi);
EXPECT_EQ(0x382583b9U, raw_context.esi);
EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
EXPECT_EQ(0x6a5025e2U, raw_context.eax);
EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
EXPECT_EQ(0x6913f540U, raw_context.eip);
EXPECT_EQ(0xbffe6edaU, raw_context.cs);
EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
EXPECT_EQ(0x659caaa4U, raw_context.esp);
EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}
TEST(Dump, OneModule) {
static const MDVSFixedFileInfo fixed_file_info = {
0xb2fba33a, // signature
0x33d7a728, // struct_version
0x31afcb20, // file_version_hi
0xe51cdab1, // file_version_lo
0xd1ea6907, // product_version_hi
0x03032857, // product_version_lo
0x11bf71d7, // file_flags_mask
0x5fb8cdbf, // file_flags
0xe45d0d5d, // file_os
0x107d9562, // file_type
0x5a8844d4, // file_subtype
0xa8d30b20, // file_date_hi
0x651c3e4e // file_date_lo
};
Dump dump(0, kBigEndian);
String module_name(dump, "single module");
Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
module_name,
0xb1054d2a,
0x34571371,
fixed_file_info, // from synth_minidump_unittest_data.h
NULL, NULL);
dump.Add(&module);
dump.Add(&module_name);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((u_int32_t) MD_MODULE_LIST_STREAM, dir->stream_type);
MinidumpModuleList *md_module_list = minidump.GetModuleList();
ASSERT_TRUE(md_module_list != NULL);
ASSERT_EQ(1U, md_module_list->module_count());
const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
ASSERT_TRUE(md_module != NULL);
ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address());
ASSERT_EQ(0xada542bd, md_module->size());
ASSERT_EQ("single module", md_module->code_file());
const MDRawModule *md_raw_module = md_module->module();
ASSERT_TRUE(md_raw_module != NULL);
ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp);
ASSERT_EQ(0x34571371U, md_raw_module->checksum);
ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info,
sizeof(fixed_file_info)) == 0);
}
TEST(Dump, OneSystemInfo) {
Dump dump(0, kLittleEndian);
String csd_version(dump, "Petulant Pierogi");
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
dump.Add(&system_info);
dump.Add(&csd_version);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((u_int32_t) MD_SYSTEM_INFO_STREAM, dir->stream_type);
MinidumpSystemInfo *md_system_info = minidump.GetSystemInfo();
ASSERT_TRUE(md_system_info != NULL);
ASSERT_EQ("windows", md_system_info->GetOS());
ASSERT_EQ("x86", md_system_info->GetCPU());
ASSERT_EQ("Petulant Pierogi", *md_system_info->GetCSDVersion());
ASSERT_EQ("GenuineIntel", *md_system_info->GetCPUVendor());
}
TEST(Dump, BigDump) {
Dump dump(0, kLittleEndian);
// A SystemInfo stream.
String csd_version(dump, "Munificent Macaque");
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
dump.Add(&csd_version);
dump.Add(&system_info);
// Five threads!
Memory stack0(dump, 0x70b9ebfc);
stack0.Append("stack for thread zero");
MDRawContextX86 raw_context0;
raw_context0.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context0.eip = 0xaf0709e4;
Context context0(dump, raw_context0);
Thread thread0(dump, 0xbbef4432, stack0, context0,
0xd0377e7b, 0xdb8eb0cf, 0xd73bc314, 0x09d357bac7f9a163ULL);
dump.Add(&stack0);
dump.Add(&context0);
dump.Add(&thread0);
Memory stack1(dump, 0xf988cc45);
stack1.Append("stack for thread one");
MDRawContextX86 raw_context1;
raw_context1.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context1.eip = 0xe4f56f81;
Context context1(dump, raw_context1);
Thread thread1(dump, 0x657c3f58, stack1, context1,
0xa68fa182, 0x6f3cf8dd, 0xe3a78ccf, 0x78cc84775e4534bbULL);
dump.Add(&stack1);
dump.Add(&context1);
dump.Add(&thread1);
Memory stack2(dump, 0xc8a92e7c);
stack2.Append("stack for thread two");
MDRawContextX86 raw_context2;
raw_context2.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context2.eip = 0xb336a438;
Context context2(dump, raw_context2);
Thread thread2(dump, 0xdf4b8a71, stack2, context2,
0x674c26b6, 0x445d7120, 0x7e700c56, 0xd89bf778e7793e17ULL);
dump.Add(&stack2);
dump.Add(&context2);
dump.Add(&thread2);
Memory stack3(dump, 0x36d08e08);
stack3.Append("stack for thread three");
MDRawContextX86 raw_context3;
raw_context3.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context3.eip = 0xdf99a60c;
Context context3(dump, raw_context3);
Thread thread3(dump, 0x86e6c341, stack3, context3,
0x32dc5c55, 0x17a2aba8, 0xe0cc75e7, 0xa46393994dae83aeULL);
dump.Add(&stack3);
dump.Add(&context3);
dump.Add(&thread3);
Memory stack4(dump, 0x1e0ab4fa);
stack4.Append("stack for thread four");
MDRawContextX86 raw_context4;
raw_context4.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context4.eip = 0xaa646267;
Context context4(dump, raw_context4);
Thread thread4(dump, 0x261a28d4, stack4, context4,
0x6ebd389e, 0xa0cd4759, 0x30168846, 0x164f650a0cf39d35ULL);
dump.Add(&stack4);
dump.Add(&context4);
dump.Add(&thread4);
// Three modules!
String module1_name(dump, "module one");
Module module1(dump, 0xeb77da57b5d4cbdaULL, 0x83cd5a37, module1_name);
dump.Add(&module1_name);
dump.Add(&module1);
String module2_name(dump, "module two");
Module module2(dump, 0x8675884adfe5ac90ULL, 0xb11e4ea3, module2_name);
dump.Add(&module2_name);
dump.Add(&module2);
String module3_name(dump, "module three");
Module module3(dump, 0x95fc1544da321b6cULL, 0x7c2bf081, module3_name);
dump.Add(&module3_name);
dump.Add(&module3);
// Add one more memory region, on top of the five stacks.
Memory memory5(dump, 0x61979e828040e564ULL);
memory5.Append("contents of memory 5");
dump.Add(&memory5);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(4U, minidump.GetDirectoryEntryCount());
// Check the threads.
MinidumpThreadList *thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list != NULL);
ASSERT_EQ(5U, thread_list->thread_count());
u_int32_t thread_id;
ASSERT_TRUE(thread_list->GetThreadAtIndex(0)->GetThreadID(&thread_id));
ASSERT_EQ(0xbbef4432U, thread_id);
ASSERT_EQ(0x70b9ebfcU,
thread_list->GetThreadAtIndex(0)->GetMemory()->GetBase());
ASSERT_EQ(0xaf0709e4U,
thread_list->GetThreadAtIndex(0)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(1)->GetThreadID(&thread_id));
ASSERT_EQ(0x657c3f58U, thread_id);
ASSERT_EQ(0xf988cc45U,
thread_list->GetThreadAtIndex(1)->GetMemory()->GetBase());
ASSERT_EQ(0xe4f56f81U,
thread_list->GetThreadAtIndex(1)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(2)->GetThreadID(&thread_id));
ASSERT_EQ(0xdf4b8a71U, thread_id);
ASSERT_EQ(0xc8a92e7cU,
thread_list->GetThreadAtIndex(2)->GetMemory()->GetBase());
ASSERT_EQ(0xb336a438U,
thread_list->GetThreadAtIndex(2)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(3)->GetThreadID(&thread_id));
ASSERT_EQ(0x86e6c341U, thread_id);
ASSERT_EQ(0x36d08e08U,
thread_list->GetThreadAtIndex(3)->GetMemory()->GetBase());
ASSERT_EQ(0xdf99a60cU,
thread_list->GetThreadAtIndex(3)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(4)->GetThreadID(&thread_id));
ASSERT_EQ(0x261a28d4U, thread_id);
ASSERT_EQ(0x1e0ab4faU,
thread_list->GetThreadAtIndex(4)->GetMemory()->GetBase());
ASSERT_EQ(0xaa646267U,
thread_list->GetThreadAtIndex(4)->GetContext()->GetContextX86()
->eip);
// Check the modules.
MinidumpModuleList *md_module_list = minidump.GetModuleList();
ASSERT_TRUE(md_module_list != NULL);
ASSERT_EQ(3U, md_module_list->module_count());
EXPECT_EQ(0xeb77da57b5d4cbdaULL,
md_module_list->GetModuleAtIndex(0)->base_address());
EXPECT_EQ(0x8675884adfe5ac90ULL,
md_module_list->GetModuleAtIndex(1)->base_address());
EXPECT_EQ(0x95fc1544da321b6cULL,
md_module_list->GetModuleAtIndex(2)->base_address());
}
TEST(Dump, OneMemoryInfo) {
Dump dump(0, kBigEndian);
Stream stream(dump, MD_MEMORY_INFO_LIST_STREAM);
// Add the MDRawMemoryInfoList header.
const u_int64_t kNumberOfEntries = 1;
stream.D32(sizeof(MDRawMemoryInfoList)) // size_of_header
.D32(sizeof(MDRawMemoryInfo)) // size_of_entry
.D64(kNumberOfEntries); // number_of_entries
// Now add a MDRawMemoryInfo entry.
const u_int64_t kBaseAddress = 0x1000;
const u_int64_t kRegionSize = 0x2000;
stream.D64(kBaseAddress) // base_address
.D64(kBaseAddress) // allocation_base
.D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE) // allocation_protection
.D32(0) // __alignment1
.D64(kRegionSize) // region_size
.D32(MD_MEMORY_STATE_COMMIT) // state
.D32(MD_MEMORY_PROTECT_EXECUTE_READWRITE) // protection
.D32(MD_MEMORY_TYPE_PRIVATE) // type
.D32(0); // __alignment2
dump.Add(&stream);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((u_int32_t) MD_MEMORY_INFO_LIST_STREAM, dir->stream_type);
MinidumpMemoryInfoList *info_list = minidump.GetMemoryInfoList();
ASSERT_TRUE(info_list != NULL);
ASSERT_EQ(1U, info_list->info_count());
const MinidumpMemoryInfo *info1 = info_list->GetMemoryInfoAtIndex(0);
ASSERT_EQ(kBaseAddress, info1->GetBase());
ASSERT_EQ(kRegionSize, info1->GetSize());
ASSERT_TRUE(info1->IsExecutable());
ASSERT_TRUE(info1->IsWritable());
// Should get back the same memory region here.
const MinidumpMemoryInfo *info2 =
info_list->GetMemoryInfoForAddress(kBaseAddress + kRegionSize / 2);
ASSERT_EQ(kBaseAddress, info2->GetBase());
ASSERT_EQ(kRegionSize, info2->GetSize());
}
TEST(Dump, OneExceptionX86) {
Dump dump(0, kLittleEndian);
MDRawContextX86 raw_context;
raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
raw_context.edi = 0x3ecba80d;
raw_context.esi = 0x382583b9;
raw_context.ebx = 0x7fccc03f;
raw_context.edx = 0xf62f8ec2;
raw_context.ecx = 0x46a6a6a8;
raw_context.eax = 0x6a5025e2;
raw_context.ebp = 0xd9fabb4a;
raw_context.eip = 0x6913f540;
raw_context.cs = 0xbffe6eda;
raw_context.eflags = 0xb2ce1e2d;
raw_context.esp = 0x659caaa4;
raw_context.ss = 0x2e951ef7;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // thread id
0xdcba4321, // exception code
0xf0e0d0c0, // exception flags
0x0919a9b9c9d9e9f9ULL); // exception address
dump.Add(&context);
dump.Add(&exception);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
u_int32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcd, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
raw_exception->exception_record.exception_address);
MinidumpContext *md_context = md_exception->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
(md_raw_context->context_flags
& (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
EXPECT_EQ(0x3ecba80dU, raw_context.edi);
EXPECT_EQ(0x382583b9U, raw_context.esi);
EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
EXPECT_EQ(0x6a5025e2U, raw_context.eax);
EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
EXPECT_EQ(0x6913f540U, raw_context.eip);
EXPECT_EQ(0xbffe6edaU, raw_context.cs);
EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
EXPECT_EQ(0x659caaa4U, raw_context.esp);
EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}
TEST(Dump, OneExceptionX86XState) {
Dump dump(0, kLittleEndian);
MDRawContextX86 raw_context;
raw_context.context_flags = MD_CONTEXT_X86_INTEGER |
MD_CONTEXT_X86_CONTROL | MD_CONTEXT_X86_XSTATE;
raw_context.edi = 0x3ecba80d;
raw_context.esi = 0x382583b9;
raw_context.ebx = 0x7fccc03f;
raw_context.edx = 0xf62f8ec2;
raw_context.ecx = 0x46a6a6a8;
raw_context.eax = 0x6a5025e2;
raw_context.ebp = 0xd9fabb4a;
raw_context.eip = 0x6913f540;
raw_context.cs = 0xbffe6eda;
raw_context.eflags = 0xb2ce1e2d;
raw_context.esp = 0x659caaa4;
raw_context.ss = 0x2e951ef7;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // thread id
0xdcba4321, // exception code
0xf0e0d0c0, // exception flags
0x0919a9b9c9d9e9f9ULL); // exception address
dump.Add(&context);
dump.Add(&exception);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
u_int32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcd, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
raw_exception->exception_record.exception_address);
MinidumpContext *md_context = md_exception->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
(md_raw_context->context_flags
& (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
EXPECT_EQ(0x3ecba80dU, raw_context.edi);
EXPECT_EQ(0x382583b9U, raw_context.esi);
EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
EXPECT_EQ(0x6a5025e2U, raw_context.eax);
EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
EXPECT_EQ(0x6913f540U, raw_context.eip);
EXPECT_EQ(0xbffe6edaU, raw_context.cs);
EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
EXPECT_EQ(0x659caaa4U, raw_context.esp);
EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}
TEST(Dump, OneExceptionARM) {
Dump dump(0, kLittleEndian);
MDRawContextARM raw_context;
raw_context.context_flags = MD_CONTEXT_ARM_INTEGER;
raw_context.iregs[0] = 0x3ecba80d;
raw_context.iregs[1] = 0x382583b9;
raw_context.iregs[2] = 0x7fccc03f;
raw_context.iregs[3] = 0xf62f8ec2;
raw_context.iregs[4] = 0x46a6a6a8;
raw_context.iregs[5] = 0x6a5025e2;
raw_context.iregs[6] = 0xd9fabb4a;
raw_context.iregs[7] = 0x6913f540;
raw_context.iregs[8] = 0xbffe6eda;
raw_context.iregs[9] = 0xb2ce1e2d;
raw_context.iregs[10] = 0x659caaa4;
raw_context.iregs[11] = 0xf0e0d0c0;
raw_context.iregs[12] = 0xa9b8c7d6;
raw_context.iregs[13] = 0x12345678;
raw_context.iregs[14] = 0xabcd1234;
raw_context.iregs[15] = 0x10203040;
raw_context.cpsr = 0x2e951ef7;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // thread id
0xdcba4321, // exception code
0xf0e0d0c0, // exception flags
0x0919a9b9c9d9e9f9ULL); // exception address
dump.Add(&context);
dump.Add(&exception);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
u_int32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcd, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
raw_exception->exception_record.exception_address);
MinidumpContext *md_context = md_exception->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM, md_context->GetContextCPU());
const MDRawContextARM *md_raw_context = md_context->GetContextARM();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM_INTEGER,
(md_raw_context->context_flags
& MD_CONTEXT_ARM_INTEGER));
EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]);
EXPECT_EQ(0x382583b9U, raw_context.iregs[1]);
EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]);
EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]);
EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]);
EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]);
EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]);
EXPECT_EQ(0x6913f540U, raw_context.iregs[7]);
EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]);
EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]);
EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]);
EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]);
EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]);
EXPECT_EQ(0x12345678U, raw_context.iregs[13]);
EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]);
EXPECT_EQ(0x10203040U, raw_context.iregs[15]);
EXPECT_EQ(0x2e951ef7U, raw_context.cpsr);
}
TEST(Dump, OneExceptionARMOldFlags) {
Dump dump(0, kLittleEndian);
MDRawContextARM raw_context;
// MD_CONTEXT_ARM_INTEGER, but with _OLD
raw_context.context_flags = MD_CONTEXT_ARM_OLD | 0x00000002;
raw_context.iregs[0] = 0x3ecba80d;
raw_context.iregs[1] = 0x382583b9;
raw_context.iregs[2] = 0x7fccc03f;
raw_context.iregs[3] = 0xf62f8ec2;
raw_context.iregs[4] = 0x46a6a6a8;
raw_context.iregs[5] = 0x6a5025e2;
raw_context.iregs[6] = 0xd9fabb4a;
raw_context.iregs[7] = 0x6913f540;
raw_context.iregs[8] = 0xbffe6eda;
raw_context.iregs[9] = 0xb2ce1e2d;
raw_context.iregs[10] = 0x659caaa4;
raw_context.iregs[11] = 0xf0e0d0c0;
raw_context.iregs[12] = 0xa9b8c7d6;
raw_context.iregs[13] = 0x12345678;
raw_context.iregs[14] = 0xabcd1234;
raw_context.iregs[15] = 0x10203040;
raw_context.cpsr = 0x2e951ef7;
Context context(dump, raw_context);
Exception exception(dump, context,
0x1234abcd, // thread id
0xdcba4321, // exception code
0xf0e0d0c0, // exception flags
0x0919a9b9c9d9e9f9ULL); // exception address
dump.Add(&context);
dump.Add(&exception);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
MinidumpException *md_exception = minidump.GetException();
ASSERT_TRUE(md_exception != NULL);
u_int32_t thread_id;
ASSERT_TRUE(md_exception->GetThreadID(&thread_id));
ASSERT_EQ(0x1234abcd, thread_id);
const MDRawExceptionStream* raw_exception = md_exception->exception();
ASSERT_TRUE(raw_exception != NULL);
EXPECT_EQ(0xdcba4321, raw_exception->exception_record.exception_code);
EXPECT_EQ(0xf0e0d0c0, raw_exception->exception_record.exception_flags);
EXPECT_EQ(0x0919a9b9c9d9e9f9ULL,
raw_exception->exception_record.exception_address);
MinidumpContext *md_context = md_exception->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM, md_context->GetContextCPU());
const MDRawContextARM *md_raw_context = md_context->GetContextARM();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((u_int32_t) MD_CONTEXT_ARM_INTEGER,
(md_raw_context->context_flags
& MD_CONTEXT_ARM_INTEGER));
EXPECT_EQ(0x3ecba80dU, raw_context.iregs[0]);
EXPECT_EQ(0x382583b9U, raw_context.iregs[1]);
EXPECT_EQ(0x7fccc03fU, raw_context.iregs[2]);
EXPECT_EQ(0xf62f8ec2U, raw_context.iregs[3]);
EXPECT_EQ(0x46a6a6a8U, raw_context.iregs[4]);
EXPECT_EQ(0x6a5025e2U, raw_context.iregs[5]);
EXPECT_EQ(0xd9fabb4aU, raw_context.iregs[6]);
EXPECT_EQ(0x6913f540U, raw_context.iregs[7]);
EXPECT_EQ(0xbffe6edaU, raw_context.iregs[8]);
EXPECT_EQ(0xb2ce1e2dU, raw_context.iregs[9]);
EXPECT_EQ(0x659caaa4U, raw_context.iregs[10]);
EXPECT_EQ(0xf0e0d0c0U, raw_context.iregs[11]);
EXPECT_EQ(0xa9b8c7d6U, raw_context.iregs[12]);
EXPECT_EQ(0x12345678U, raw_context.iregs[13]);
EXPECT_EQ(0xabcd1234U, raw_context.iregs[14]);
EXPECT_EQ(0x10203040U, raw_context.iregs[15]);
EXPECT_EQ(0x2e951ef7U, raw_context.cpsr);
}
} // namespace

View File

@@ -0,0 +1,297 @@
// Copyright (c) 2010, 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.
//
// module_comparer.cc: ModuleComparer implementation.
// See module_comparer.h for documentation.
//
// Author: lambxsy@google.com (Siyang Xie)
#include "processor/module_comparer.h"
#include <map>
#include <string>
#include "processor/basic_code_module.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
#define ASSERT_TRUE(condition) \
if (!(condition)) { \
BPLOG(ERROR) << "FAIL: " << #condition << " @ " \
<< __FILE__ << ":" << __LINE__; \
return false; \
}
#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition))
namespace google_breakpad {
bool ModuleComparer::Compare(const string &symbol_data) {
scoped_ptr<BasicModule> basic_module(new BasicModule("test_module"));
scoped_ptr<FastModule> fast_module(new FastModule("test_module"));
// Load symbol data into basic_module
scoped_array<char> buffer(new char[symbol_data.size() + 1]);
strcpy(buffer.get(), symbol_data.c_str());
ASSERT_TRUE(basic_module->LoadMapFromMemory(buffer.get()));
buffer.reset();
// Serialize BasicSourceLineResolver::Module.
unsigned int serialized_size = 0;
scoped_array<char> serialized_data(
serializer_.Serialize(*(basic_module.get()), &serialized_size));
ASSERT_TRUE(serialized_data.get());
BPLOG(INFO) << "Serialized size = " << serialized_size << " Bytes";
// Load FastSourceLineResolver::Module using serialized data.
ASSERT_TRUE(fast_module->LoadMapFromMemory(serialized_data.get()));
// Compare FastSourceLineResolver::Module with
// BasicSourceLineResolver::Module.
ASSERT_TRUE(CompareModule(basic_module.get(), fast_module.get()));
return true;
}
// Traversal the content of module and do comparison
bool ModuleComparer::CompareModule(const BasicModule *basic_module,
const FastModule *fast_module) const {
// Compare name_.
ASSERT_TRUE(basic_module->name_ == fast_module->name_);
// Compare files_:
{
BasicModule::FileMap::const_iterator iter1 = basic_module->files_.begin();
FastModule::FileMap::iterator iter2 = fast_module->files_.begin();
while (iter1 != basic_module->files_.end()
&& iter2 != fast_module->files_.end()) {
ASSERT_TRUE(iter1->first == iter2.GetKey());
string tmp(iter2.GetValuePtr());
ASSERT_TRUE(iter1->second == tmp);
++iter1;
++iter2;
}
ASSERT_TRUE(iter1 == basic_module->files_.end());
ASSERT_TRUE(iter2 == fast_module->files_.end());
}
// Compare functions_:
{
RangeMap<MemAddr, linked_ptr<BasicFunc> >::MapConstIterator iter1;
StaticRangeMap<MemAddr, FastFunc>::MapConstIterator iter2;
iter1 = basic_module->functions_.map_.begin();
iter2 = fast_module->functions_.map_.begin();
while (iter1 != basic_module->functions_.map_.end()
&& iter2 != fast_module->functions_.map_.end()) {
ASSERT_TRUE(iter1->first == iter2.GetKey());
ASSERT_TRUE(iter1->second.base() == iter2.GetValuePtr()->base());
ASSERT_TRUE(CompareFunction(
iter1->second.entry().get(), iter2.GetValuePtr()->entryptr()));
++iter1;
++iter2;
}
ASSERT_TRUE(iter1 == basic_module->functions_.map_.end());
ASSERT_TRUE(iter2 == fast_module->functions_.map_.end());
}
// Compare public_symbols_:
{
AddressMap<MemAddr, linked_ptr<BasicPubSymbol> >::MapConstIterator iter1;
StaticAddressMap<MemAddr, FastPubSymbol>::MapConstIterator iter2;
iter1 = basic_module->public_symbols_.map_.begin();
iter2 = fast_module->public_symbols_.map_.begin();
while (iter1 != basic_module->public_symbols_.map_.end()
&& iter2 != fast_module->public_symbols_.map_.end()) {
ASSERT_TRUE(iter1->first == iter2.GetKey());
ASSERT_TRUE(ComparePubSymbol(
iter1->second.get(), iter2.GetValuePtr()));
++iter1;
++iter2;
}
ASSERT_TRUE(iter1 == basic_module->public_symbols_.map_.end());
ASSERT_TRUE(iter2 == fast_module->public_symbols_.map_.end());
}
// Compare windows_frame_info_[]:
for (int i = 0; i < WindowsFrameInfo::STACK_INFO_LAST; ++i) {
ASSERT_TRUE(CompareCRM(&(basic_module->windows_frame_info_[i]),
&(fast_module->windows_frame_info_[i])));
}
// Compare cfi_initial_rules_:
{
RangeMap<MemAddr, string>::MapConstIterator iter1;
StaticRangeMap<MemAddr, char>::MapConstIterator iter2;
iter1 = basic_module->cfi_initial_rules_.map_.begin();
iter2 = fast_module->cfi_initial_rules_.map_.begin();
while (iter1 != basic_module->cfi_initial_rules_.map_.end()
&& iter2 != fast_module->cfi_initial_rules_.map_.end()) {
ASSERT_TRUE(iter1->first == iter2.GetKey());
ASSERT_TRUE(iter1->second.base() == iter2.GetValuePtr()->base());
string tmp(iter2.GetValuePtr()->entryptr());
ASSERT_TRUE(iter1->second.entry() == tmp);
++iter1;
++iter2;
}
ASSERT_TRUE(iter1 == basic_module->cfi_initial_rules_.map_.end());
ASSERT_TRUE(iter2 == fast_module->cfi_initial_rules_.map_.end());
}
// Compare cfi_delta_rules_:
{
map<MemAddr, string>::const_iterator iter1;
StaticMap<MemAddr, char>::iterator iter2;
iter1 = basic_module->cfi_delta_rules_.begin();
iter2 = fast_module->cfi_delta_rules_.begin();
while (iter1 != basic_module->cfi_delta_rules_.end()
&& iter2 != fast_module->cfi_delta_rules_.end()) {
ASSERT_TRUE(iter1->first == iter2.GetKey());
string tmp(iter2.GetValuePtr());
ASSERT_TRUE(iter1->second == tmp);
++iter1;
++iter2;
}
ASSERT_TRUE(iter1 == basic_module->cfi_delta_rules_.end());
ASSERT_TRUE(iter2 == fast_module->cfi_delta_rules_.end());
}
return true;
}
bool ModuleComparer::CompareFunction(const BasicFunc *basic_func,
const FastFunc *fast_func_raw) const {
FastFunc* fast_func = new FastFunc();
fast_func->CopyFrom(fast_func_raw);
ASSERT_TRUE(basic_func->name == fast_func->name);
ASSERT_TRUE(basic_func->address == fast_func->address);
ASSERT_TRUE(basic_func->size == fast_func->size);
// compare range map of lines:
RangeMap<MemAddr, linked_ptr<BasicLine> >::MapConstIterator iter1;
StaticRangeMap<MemAddr, FastLine>::MapConstIterator iter2;
iter1 = basic_func->lines.map_.begin();
iter2 = fast_func->lines.map_.begin();
while (iter1 != basic_func->lines.map_.end()
&& iter2 != fast_func->lines.map_.end()) {
ASSERT_TRUE(iter1->first == iter2.GetKey());
ASSERT_TRUE(iter1->second.base() == iter2.GetValuePtr()->base());
ASSERT_TRUE(CompareLine(iter1->second.entry().get(),
iter2.GetValuePtr()->entryptr()));
++iter1;
++iter2;
}
ASSERT_TRUE(iter1 == basic_func->lines.map_.end());
ASSERT_TRUE(iter2 == fast_func->lines.map_.end());
delete fast_func;
return true;
}
bool ModuleComparer::CompareLine(const BasicLine *basic_line,
const FastLine *fast_line_raw) const {
FastLine *fast_line = new FastLine;
fast_line->CopyFrom(fast_line_raw);
ASSERT_TRUE(basic_line->address == fast_line->address);
ASSERT_TRUE(basic_line->size == fast_line->size);
ASSERT_TRUE(basic_line->source_file_id == fast_line->source_file_id);
ASSERT_TRUE(basic_line->line == fast_line->line);
delete fast_line;
return true;
}
bool ModuleComparer::ComparePubSymbol(const BasicPubSymbol* basic_ps,
const FastPubSymbol* fastps_raw) const {
FastPubSymbol *fast_ps = new FastPubSymbol;
fast_ps->CopyFrom(fastps_raw);
ASSERT_TRUE(basic_ps->name == fast_ps->name);
ASSERT_TRUE(basic_ps->address == fast_ps->address);
ASSERT_TRUE(basic_ps->parameter_size == fast_ps->parameter_size);
delete fast_ps;
return true;
}
bool ModuleComparer::CompareWFI(const WindowsFrameInfo& wfi1,
const WindowsFrameInfo& wfi2) const {
ASSERT_TRUE(wfi1.valid == wfi2.valid);
ASSERT_TRUE(wfi1.prolog_size == wfi2.prolog_size);
ASSERT_TRUE(wfi1.epilog_size == wfi2.epilog_size);
ASSERT_TRUE(wfi1.parameter_size == wfi2.parameter_size);
ASSERT_TRUE(wfi1.saved_register_size == wfi2.saved_register_size);
ASSERT_TRUE(wfi1.local_size == wfi2.local_size);
ASSERT_TRUE(wfi1.max_stack_size == wfi2.max_stack_size);
ASSERT_TRUE(wfi1.allocates_base_pointer == wfi2.allocates_base_pointer);
ASSERT_TRUE(wfi1.program_string == wfi2.program_string);
return true;
}
// Compare ContainedRangeMap
bool ModuleComparer::CompareCRM(
const ContainedRangeMap<MemAddr, linked_ptr<WFI> >* basic_crm,
const StaticContainedRangeMap<MemAddr, char>* fast_crm) const {
ASSERT_TRUE(basic_crm->base_ == fast_crm->base_);
if (!basic_crm->entry_.get() || !fast_crm->entry_ptr_) {
// empty entry:
ASSERT_TRUE(!basic_crm->entry_.get() && !fast_crm->entry_ptr_);
} else {
WFI newwfi;
newwfi.CopyFrom(fast_resolver_->CopyWFI(fast_crm->entry_ptr_));
ASSERT_TRUE(CompareWFI(*(basic_crm->entry_.get()), newwfi));
}
if ((!basic_crm->map_ || basic_crm->map_->empty())
|| fast_crm->map_.empty()) {
ASSERT_TRUE((!basic_crm->map_ || basic_crm->map_->empty())
&& fast_crm->map_.empty());
} else {
ContainedRangeMap<MemAddr, linked_ptr<WFI> >::MapConstIterator iter1;
StaticContainedRangeMap<MemAddr, char>::MapConstIterator iter2;
iter1 = basic_crm->map_->begin();
iter2 = fast_crm->map_.begin();
while (iter1 != basic_crm->map_->end()
&& iter2 != fast_crm->map_.end()) {
ASSERT_TRUE(iter1->first == iter2.GetKey());
StaticContainedRangeMap<MemAddr, char> *child =
new StaticContainedRangeMap<MemAddr, char>(
reinterpret_cast<const char*>(iter2.GetValuePtr()));
ASSERT_TRUE(CompareCRM(iter1->second, child));
delete child;
++iter1;
++iter2;
}
ASSERT_TRUE(iter1 == basic_crm->map_->end());
ASSERT_TRUE(iter2 == fast_crm->map_.end());
}
return true;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,98 @@
// Copyright (c) 2010, 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.
//
// module_comparer.h: ModuleComparer reads a string format of symbol file, and
// loads the symbol into both BasicSourceLineResolver::Module and
// FastSourceLineResolve::Module. It then traverses both Modules and compare
// the content of data to verify the correctness of new fast module.
// ModuleCompare class is a tool to verify correctness of a loaded
// FastSourceLineResolver::Module instance, i.e., in-memory representation of
// parsed symbol. ModuleComparer class should be used for testing purpose only,
// e.g., in fast_source_line_resolver_unittest.
//
// Author: lambxsy@google.com (Siyang Xie)
#ifndef PROCESSOR_MODULE_COMPARER_H__
#define PROCESSOR_MODULE_COMPARER_H__
#include <string>
#include "processor/basic_source_line_resolver_types.h"
#include "processor/fast_source_line_resolver_types.h"
#include "processor/module_serializer.h"
#include "processor/windows_frame_info.h"
namespace google_breakpad {
class ModuleComparer {
public:
ModuleComparer(): fast_resolver_(new FastSourceLineResolver),
basic_resolver_(new BasicSourceLineResolver) { }
~ModuleComparer() {
delete fast_resolver_;
delete basic_resolver_;
}
// BasicSourceLineResolver loads its module using the symbol data,
// ModuleSerializer serialize the loaded module into a memory chunk,
// FastSourceLineResolver loads its module using the serialized memory chunk,
// Then, traverse both modules together and compare underlying data
// return true if both modules contain exactly same data.
bool Compare(const string &symbol_data);
private:
typedef BasicSourceLineResolver::Module BasicModule;
typedef FastSourceLineResolver::Module FastModule;
typedef BasicSourceLineResolver::Function BasicFunc;
typedef FastSourceLineResolver::Function FastFunc;
typedef BasicSourceLineResolver::Line BasicLine;
typedef FastSourceLineResolver::Line FastLine;
typedef BasicSourceLineResolver::PublicSymbol BasicPubSymbol;
typedef FastSourceLineResolver::PublicSymbol FastPubSymbol;
typedef WindowsFrameInfo WFI;
bool CompareModule(const BasicModule *oldmodule,
const FastModule *newmodule) const;
bool CompareFunction(const BasicFunc *oldfunc, const FastFunc *newfunc) const;
bool CompareLine(const BasicLine *oldline, const FastLine *newline) const;
bool ComparePubSymbol(const BasicPubSymbol*, const FastPubSymbol*) const;
bool CompareWFI(const WindowsFrameInfo&, const WindowsFrameInfo&) const;
// Compare ContainedRangeMap
bool CompareCRM(const ContainedRangeMap<MemAddr, linked_ptr<WFI> >*,
const StaticContainedRangeMap<MemAddr, char>*) const;
FastSourceLineResolver *fast_resolver_;
BasicSourceLineResolver *basic_resolver_;
ModuleSerializer serializer_;
};
} // namespace google_breakpad
#endif // PROCESSOR_MODULE_COMPARER_H__

View File

@@ -0,0 +1,72 @@
// Copyright (c) 2010 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.
//
// module_factory.h: ModuleFactory a factory that provides
// an interface for creating a Module and deferring instantiation to subclasses
// BasicModuleFactory and FastModuleFactory.
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_MODULE_FACTORY_H__
#define PROCESSOR_MODULE_FACTORY_H__
#include "processor/basic_source_line_resolver_types.h"
#include "processor/fast_source_line_resolver_types.h"
#include "processor/source_line_resolver_base_types.h"
namespace google_breakpad {
class ModuleFactory {
public:
virtual ~ModuleFactory() { };
virtual SourceLineResolverBase::Module* CreateModule(
const string &name) const = 0;
};
class BasicModuleFactory : public ModuleFactory {
public:
virtual ~BasicModuleFactory() { }
virtual BasicSourceLineResolver::Module* CreateModule(
const string &name) const {
return new BasicSourceLineResolver::Module(name);
}
};
class FastModuleFactory : public ModuleFactory {
public:
virtual ~FastModuleFactory() { }
virtual FastSourceLineResolver::Module* CreateModule(
const string &name) const {
return new FastSourceLineResolver::Module(name);
}
};
} // namespace google_breakpad
#endif // PROCESSOR_MODULE_FACTORY_H__

View File

@@ -0,0 +1,200 @@
// Copyright (c) 2010, 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.
//
// module_serializer.cc: ModuleSerializer implementation.
//
// See module_serializer.h for documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#include "processor/module_serializer.h"
#include <map>
#include <string>
#include "processor/basic_code_module.h"
#include "processor/logging.h"
namespace google_breakpad {
// Definition of static member variable in SimplerSerializer<Funcion>, which
// is declared in file "simple_serializer-inl.h"
RangeMapSerializer< MemAddr, linked_ptr<BasicSourceLineResolver::Line> >
SimpleSerializer<BasicSourceLineResolver::Function>::range_map_serializer_;
size_t ModuleSerializer::SizeOf(const BasicSourceLineResolver::Module &module) {
size_t total_size_alloc_ = 0;
// Compute memory size for each map component in Module class.
int map_index = 0;
map_sizes_[map_index++] = files_serializer_.SizeOf(module.files_);
map_sizes_[map_index++] = functions_serializer_.SizeOf(module.functions_);
map_sizes_[map_index++] = pubsym_serializer_.SizeOf(module.public_symbols_);
for (int i = 0; i < WindowsFrameInfo::STACK_INFO_LAST; ++i)
map_sizes_[map_index++] =
wfi_serializer_.SizeOf(&(module.windows_frame_info_[i]));
map_sizes_[map_index++] = cfi_init_rules_serializer_.SizeOf(
module.cfi_initial_rules_);
map_sizes_[map_index++] = cfi_delta_rules_serializer_.SizeOf(
module.cfi_delta_rules_);
// Header size.
total_size_alloc_ = kNumberMaps_ * sizeof(u_int32_t);
for (int i = 0; i < kNumberMaps_; ++i)
total_size_alloc_ += map_sizes_[i];
// Extra one byte for null terminator for C-string copy safety.
++total_size_alloc_;
return total_size_alloc_;
}
char *ModuleSerializer::Write(const BasicSourceLineResolver::Module &module,
char *dest) {
// Write header.
memcpy(dest, map_sizes_, kNumberMaps_ * sizeof(u_int32_t));
dest += kNumberMaps_ * sizeof(u_int32_t);
// Write each map.
dest = files_serializer_.Write(module.files_, dest);
dest = functions_serializer_.Write(module.functions_, dest);
dest = pubsym_serializer_.Write(module.public_symbols_, dest);
for (int i = 0; i < WindowsFrameInfo::STACK_INFO_LAST; ++i)
dest = wfi_serializer_.Write(&(module.windows_frame_info_[i]), dest);
dest = cfi_init_rules_serializer_.Write(module.cfi_initial_rules_, dest);
dest = cfi_delta_rules_serializer_.Write(module.cfi_delta_rules_, dest);
// Write a null terminator.
dest = SimpleSerializer<char>::Write(0, dest);
return dest;
}
char* ModuleSerializer::Serialize(
const BasicSourceLineResolver::Module &module, unsigned int *size) {
// Compute size of memory to allocate.
unsigned int size_to_alloc = SizeOf(module);
// Allocate memory for serialized data.
char *serialized_data = new char[size_to_alloc];
if (!serialized_data) {
BPLOG(ERROR) << "ModuleSerializer: memory allocation failed, "
<< "size to alloc: " << size_to_alloc;
if (size) *size = 0;
return NULL;
}
// Write serialized data to allocated memory chunk.
char *end_address = Write(module, serialized_data);
// Verify the allocated memory size is equal to the size of data been written.
unsigned int size_written =
static_cast<unsigned int>(end_address - serialized_data);
if (size_to_alloc != size_written) {
BPLOG(ERROR) << "size_to_alloc differs from size_written: "
<< size_to_alloc << " vs " << size_written;
}
// Set size and return the start address of memory chunk.
if (size)
*size = size_to_alloc;
return serialized_data;
}
bool ModuleSerializer::SerializeModuleAndLoadIntoFastResolver(
const BasicSourceLineResolver::ModuleMap::const_iterator &iter,
FastSourceLineResolver *fast_resolver) {
BPLOG(INFO) << "Converting symbol " << iter->first.c_str();
// Cast SourceLineResolverBase::Module* to BasicSourceLineResolver::Module*.
BasicSourceLineResolver::Module* basic_module =
dynamic_cast<BasicSourceLineResolver::Module*>(iter->second);
unsigned int size = 0;
scoped_array<char> symbol_data(Serialize(*basic_module, &size));
if (!symbol_data.get()) {
BPLOG(ERROR) << "Serialization failed for module: " << basic_module->name_;
return false;
}
BPLOG(INFO) << "Serialized Symbol Size " << size;
// Copy the data into string.
// Must pass string to LoadModuleUsingMapBuffer(), instead of passing char* to
// LoadModuleUsingMemoryBuffer(), becaused of data ownership/lifetime issue.
string symbol_data_string(symbol_data.get(), size);
symbol_data.reset();
scoped_ptr<CodeModule> code_module(
new BasicCodeModule(0, 0, iter->first, "", "", "", ""));
return fast_resolver->LoadModuleUsingMapBuffer(code_module.get(),
symbol_data_string);
}
void ModuleSerializer::ConvertAllModules(
const BasicSourceLineResolver *basic_resolver,
FastSourceLineResolver *fast_resolver) {
// Check for NULL pointer.
if (!basic_resolver || !fast_resolver)
return;
// Traverse module list in basic resolver.
BasicSourceLineResolver::ModuleMap::const_iterator iter;
iter = basic_resolver->modules_->begin();
for (; iter != basic_resolver->modules_->end(); ++iter)
SerializeModuleAndLoadIntoFastResolver(iter, fast_resolver);
}
bool ModuleSerializer::ConvertOneModule(
const string &moduleid,
const BasicSourceLineResolver *basic_resolver,
FastSourceLineResolver *fast_resolver) {
// Check for NULL pointer.
if (!basic_resolver || !fast_resolver)
return false;
BasicSourceLineResolver::ModuleMap::const_iterator iter;
iter = basic_resolver->modules_->find(moduleid);
if (iter == basic_resolver->modules_->end())
return false;
return SerializeModuleAndLoadIntoFastResolver(iter, fast_resolver);
}
char* ModuleSerializer::SerializeSymbolFileData(
const string &symbol_data, unsigned int *size) {
scoped_ptr<BasicSourceLineResolver::Module> module(
new BasicSourceLineResolver::Module("no name"));
scoped_array<char> buffer(new char[symbol_data.size() + 1]);
strcpy(buffer.get(), symbol_data.c_str());
if (!module->LoadMapFromMemory(buffer.get())) {
return NULL;
}
buffer.reset(NULL);
return Serialize(*(module.get()), size);
}
} // namespace google_breakpad

View File

@@ -0,0 +1,127 @@
// Copyright (c) 2010, 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.
//
// module_serializer.h: ModuleSerializer serializes a loaded symbol,
// i.e., a loaded BasicSouceLineResolver::Module instance, into a memory
// chunk of data. The serialized data can be read and loaded by
// FastSourceLineResolver without CPU & memory-intensive parsing.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_MODULE_SERIALIZER_H__
#define PROCESSOR_MODULE_SERIALIZER_H__
#include <map>
#include <string>
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/fast_source_line_resolver.h"
#include "processor/basic_source_line_resolver_types.h"
#include "processor/fast_source_line_resolver_types.h"
#include "processor/linked_ptr.h"
#include "processor/map_serializers-inl.h"
#include "processor/simple_serializer-inl.h"
#include "processor/windows_frame_info.h"
namespace google_breakpad {
// ModuleSerializer serializes a loaded BasicSourceLineResolver::Module into a
// chunk of memory data. ModuleSerializer also provides interface to compute
// memory size of the serialized data, write serialized data directly into
// memory, convert ASCII format symbol data into serialized binary data, and
// convert loaded BasicSourceLineResolver::Module into
// FastSourceLineResolver::Module.
class ModuleSerializer {
public:
// Compute the size of memory required to serialize a module. Return the
// total size needed for serialization.
size_t SizeOf(const BasicSourceLineResolver::Module &module);
// Write a module into an allocated memory chunk with required size.
// Return the "end" of data, i.e., the address after the final byte of data.
char* Write(const BasicSourceLineResolver::Module &module, char *dest);
// Serializes a loaded Module object into a chunk of memory data and returns
// the address of memory chunk. If size != NULL, *size is set to the memory
// size allocated for the serialized data.
// Caller takes the ownership of the memory chunk (allocated on heap), and
// owner should call delete [] to free the memory after use.
char* Serialize(const BasicSourceLineResolver::Module &module,
unsigned int *size = NULL);
// Given the string format symbol_data, produces a chunk of serialized data.
// Caller takes ownership of the serialized data (on heap), and owner should
// call delete [] to free the memory after use.
char* SerializeSymbolFileData(const string &symbol_data,
unsigned int *size = NULL);
// Serializes one loaded module with given moduleid in the basic source line
// resolver, and loads the serialized data into the fast source line resolver.
// Return false if the basic source line doesn't have a module with the given
// moduleid.
bool ConvertOneModule(const string &moduleid,
const BasicSourceLineResolver *basic_resolver,
FastSourceLineResolver *fast_resolver);
// Serializes all the loaded modules in a basic source line resolver, and
// loads the serialized data into a fast source line resolver.
void ConvertAllModules(const BasicSourceLineResolver *basic_resolver,
FastSourceLineResolver *fast_resolver);
private:
// Convenient type names.
typedef BasicSourceLineResolver::Line Line;
typedef BasicSourceLineResolver::Function Function;
typedef BasicSourceLineResolver::PublicSymbol PublicSymbol;
// Internal implementation for ConvertOneModule and ConvertAllModules methods.
bool SerializeModuleAndLoadIntoFastResolver(
const BasicSourceLineResolver::ModuleMap::const_iterator &iter,
FastSourceLineResolver *fast_resolver);
// Number of Maps that Module class contains.
static const u_int32_t kNumberMaps_ =
FastSourceLineResolver::Module::kNumberMaps_;
// Memory sizes required to serialize map components in Module.
u_int32_t map_sizes_[kNumberMaps_];
// Serializers for each individual map component in Module class.
StdMapSerializer<int, string> files_serializer_;
RangeMapSerializer<MemAddr, linked_ptr<Function> > functions_serializer_;
AddressMapSerializer<MemAddr, linked_ptr<PublicSymbol> > pubsym_serializer_;
ContainedRangeMapSerializer<MemAddr,
linked_ptr<WindowsFrameInfo> > wfi_serializer_;
RangeMapSerializer<MemAddr, string> cfi_init_rules_serializer_;
StdMapSerializer<MemAddr, string> cfi_delta_rules_serializer_;
};
} // namespace google_breakpad
#endif // PROCESSOR_MODULE_SERIALIZER_H__

View File

@@ -0,0 +1,56 @@
// 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.
// pathname_stripper.cc: Manipulates pathnames into their component parts.
//
// See pathname_stripper.h for documentation.
//
// Author: Mark Mentovai
#include "processor/pathname_stripper.h"
namespace google_breakpad {
// static
string PathnameStripper::File(const string &path) {
string::size_type slash = path.rfind('/');
string::size_type backslash = path.rfind('\\');
string::size_type file_start = 0;
if (slash != string::npos &&
(backslash == string::npos || slash > backslash)) {
file_start = slash + 1;
} else if (backslash != string::npos) {
file_start = backslash + 1;
}
return path.substr(file_start);
}
} // namespace google_breakpad

View File

@@ -0,0 +1,53 @@
// 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.
// pathname_stripper.h: Manipulates pathnames into their component parts.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_PATHNAME_STRIPPER_H__
#define PROCESSOR_PATHNAME_STRIPPER_H__
#include <string>
namespace google_breakpad {
using std::string;
class PathnameStripper {
public:
// Given path, a pathname with components separated by slashes (/) or
// backslashes (\), returns the trailing component, without any separator.
// If path ends in a separator character, returns an empty string.
static string File(const string &path);
};
} // namespace google_breakpad
#endif // PROCESSOR_PATHNAME_STRIPPER_H__

View File

@@ -0,0 +1,87 @@
// 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.
#include <stdio.h>
#include "processor/pathname_stripper.h"
#include "processor/logging.h"
#define ASSERT_TRUE(condition) \
if (!(condition)) { \
fprintf(stderr, "FAIL: %s @ %s:%d\n", #condition, __FILE__, __LINE__); \
return false; \
}
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
namespace {
using google_breakpad::PathnameStripper;
static bool RunTests() {
ASSERT_EQ(PathnameStripper::File("/dir/file"), "file");
ASSERT_EQ(PathnameStripper::File("\\dir\\file"), "file");
ASSERT_EQ(PathnameStripper::File("/dir\\file"), "file");
ASSERT_EQ(PathnameStripper::File("\\dir/file"), "file");
ASSERT_EQ(PathnameStripper::File("dir/file"), "file");
ASSERT_EQ(PathnameStripper::File("dir\\file"), "file");
ASSERT_EQ(PathnameStripper::File("dir/\\file"), "file");
ASSERT_EQ(PathnameStripper::File("dir\\/file"), "file");
ASSERT_EQ(PathnameStripper::File("file"), "file");
ASSERT_EQ(PathnameStripper::File("dir/"), "");
ASSERT_EQ(PathnameStripper::File("dir\\"), "");
ASSERT_EQ(PathnameStripper::File("dir/dir/"), "");
ASSERT_EQ(PathnameStripper::File("dir\\dir\\"), "");
ASSERT_EQ(PathnameStripper::File("dir1/dir2/file"), "file");
ASSERT_EQ(PathnameStripper::File("dir1\\dir2\\file"), "file");
ASSERT_EQ(PathnameStripper::File("dir1/dir2\\file"), "file");
ASSERT_EQ(PathnameStripper::File("dir1\\dir2/file"), "file");
ASSERT_EQ(PathnameStripper::File(""), "");
ASSERT_EQ(PathnameStripper::File("1"), "1");
ASSERT_EQ(PathnameStripper::File("1/2"), "2");
ASSERT_EQ(PathnameStripper::File("1\\2"), "2");
ASSERT_EQ(PathnameStripper::File("/1/2"), "2");
ASSERT_EQ(PathnameStripper::File("\\1\\2"), "2");
ASSERT_EQ(PathnameStripper::File("dir//file"), "file");
ASSERT_EQ(PathnameStripper::File("dir\\\\file"), "file");
ASSERT_EQ(PathnameStripper::File("/dir//file"), "file");
ASSERT_EQ(PathnameStripper::File("\\dir\\\\file"), "file");
ASSERT_EQ(PathnameStripper::File("c:\\dir\\file"), "file");
ASSERT_EQ(PathnameStripper::File("c:\\dir\\file.ext"), "file.ext");
return true;
}
} // namespace
int main(int argc, char **argv) {
BPLOG_INIT(&argc, &argv);
return RunTests() ? 0 : 1;
}

View File

@@ -0,0 +1,332 @@
// -*- mode: c++ -*-
// Copyright (c) 2010 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.
// postfix_evaluator-inl.h: Postfix (reverse Polish) notation expression
// evaluator.
//
// Documentation in postfix_evaluator.h.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_POSTFIX_EVALUATOR_INL_H__
#define PROCESSOR_POSTFIX_EVALUATOR_INL_H__
#include "processor/postfix_evaluator.h"
#include <stdio.h>
#include <sstream>
#include "google_breakpad/processor/memory_region.h"
#include "processor/logging.h"
namespace google_breakpad {
using std::istringstream;
using std::ostringstream;
// A small class used in Evaluate to make sure to clean up the stack
// before returning failure.
class AutoStackClearer {
public:
explicit AutoStackClearer(vector<string> *stack) : stack_(stack) {}
~AutoStackClearer() { stack_->clear(); }
private:
vector<string> *stack_;
};
template<typename ValueType>
bool PostfixEvaluator<ValueType>::EvaluateInternal(
const string &expression,
DictionaryValidityType *assigned) {
// Tokenize, splitting on whitespace.
istringstream stream(expression);
string token;
while (stream >> token) {
// There are enough binary operations that do exactly the same thing
// (other than the specific operation, of course) that it makes sense
// to share as much code as possible.
enum BinaryOperation {
BINARY_OP_NONE = 0,
BINARY_OP_ADD,
BINARY_OP_SUBTRACT,
BINARY_OP_MULTIPLY,
BINARY_OP_DIVIDE_QUOTIENT,
BINARY_OP_DIVIDE_MODULUS
};
BinaryOperation operation = BINARY_OP_NONE;
if (token == "+")
operation = BINARY_OP_ADD;
else if (token == "-")
operation = BINARY_OP_SUBTRACT;
else if (token == "*")
operation = BINARY_OP_MULTIPLY;
else if (token == "/")
operation = BINARY_OP_DIVIDE_QUOTIENT;
else if (token == "%")
operation = BINARY_OP_DIVIDE_MODULUS;
if (operation != BINARY_OP_NONE) {
// Get the operands.
ValueType operand1 = ValueType();
ValueType operand2 = ValueType();
if (!PopValues(&operand1, &operand2)) {
BPLOG(ERROR) << "Could not PopValues to get two values for binary "
"operation " << token << ": " << expression;
return false;
}
// Perform the operation.
ValueType result;
switch (operation) {
case BINARY_OP_ADD:
result = operand1 + operand2;
break;
case BINARY_OP_SUBTRACT:
result = operand1 - operand2;
break;
case BINARY_OP_MULTIPLY:
result = operand1 * operand2;
break;
case BINARY_OP_DIVIDE_QUOTIENT:
result = operand1 / operand2;
break;
case BINARY_OP_DIVIDE_MODULUS:
result = operand1 % operand2;
break;
case BINARY_OP_NONE:
// This will not happen, but compilers will want a default or
// BINARY_OP_NONE case.
BPLOG(ERROR) << "Not reached!";
return false;
break;
}
// Save the result.
PushValue(result);
} else if (token == "^") {
// ^ for unary dereference. Can't dereference without memory.
if (!memory_) {
BPLOG(ERROR) << "Attempt to dereference without memory: " <<
expression;
return false;
}
ValueType address;
if (!PopValue(&address)) {
BPLOG(ERROR) << "Could not PopValue to get value to derefence: " <<
expression;
return false;
}
ValueType value;
if (!memory_->GetMemoryAtAddress(address, &value)) {
BPLOG(ERROR) << "Could not dereference memory at address " <<
HexString(address) << ": " << expression;
return false;
}
PushValue(value);
} else if (token == "=") {
// = for assignment.
ValueType value;
if (!PopValue(&value)) {
BPLOG(INFO) << "Could not PopValue to get value to assign: " <<
expression;
return false;
}
// Assignment is only meaningful when assigning into an identifier.
// The identifier must name a variable, not a constant. Variables
// begin with '$'.
string identifier;
if (PopValueOrIdentifier(NULL, &identifier) != POP_RESULT_IDENTIFIER) {
BPLOG(ERROR) << "PopValueOrIdentifier returned a value, but an "
"identifier is needed to assign " <<
HexString(value) << ": " << expression;
return false;
}
if (identifier.empty() || identifier[0] != '$') {
BPLOG(ERROR) << "Can't assign " << HexString(value) << " to " <<
identifier << ": " << expression;
return false;
}
(*dictionary_)[identifier] = value;
if (assigned)
(*assigned)[identifier] = true;
} else {
// The token is not an operator, it's a literal value or an identifier.
// Push it onto the stack as-is. Use push_back instead of PushValue
// because PushValue pushes ValueType as a string, but token is already
// a string.
stack_.push_back(token);
}
}
return true;
}
template<typename ValueType>
bool PostfixEvaluator<ValueType>::Evaluate(const string &expression,
DictionaryValidityType *assigned) {
// Ensure that the stack is cleared before returning.
AutoStackClearer clearer(&stack_);
if (!EvaluateInternal(expression, assigned))
return false;
// If there's anything left on the stack, it indicates incomplete execution.
// This is a failure case. If the stack is empty, evalution was complete
// and successful.
if (stack_.empty())
return true;
BPLOG(ERROR) << "Incomplete execution: " << expression;
return false;
}
template<typename ValueType>
bool PostfixEvaluator<ValueType>::EvaluateForValue(const string &expression,
ValueType *result) {
// Ensure that the stack is cleared before returning.
AutoStackClearer clearer(&stack_);
if (!EvaluateInternal(expression, NULL))
return false;
// A successful execution should leave exactly one value on the stack.
if (stack_.size() != 1) {
BPLOG(ERROR) << "Expression yielded bad number of results: "
<< "'" << expression << "'";
return false;
}
return PopValue(result);
}
template<typename ValueType>
typename PostfixEvaluator<ValueType>::PopResult
PostfixEvaluator<ValueType>::PopValueOrIdentifier(
ValueType *value, string *identifier) {
// There needs to be at least one element on the stack to pop.
if (!stack_.size())
return POP_RESULT_FAIL;
string token = stack_.back();
stack_.pop_back();
// First, try to treat the value as a literal. Literals may have leading
// '-' sign, and the entire remaining string must be parseable as
// ValueType. If this isn't possible, it can't be a literal, so treat it
// as an identifier instead.
//
// Some versions of the libstdc++, the GNU standard C++ library, have
// stream extractors for unsigned integer values that permit a leading
// '-' sign (6.0.13); others do not (6.0.9). Since we require it, we
// handle it explicitly here.
istringstream token_stream(token);
ValueType literal = ValueType();
bool negative;
if (token_stream.peek() == '-') {
negative = true;
token_stream.get();
} else {
negative = false;
}
if (token_stream >> literal && token_stream.peek() == EOF) {
if (value) {
*value = literal;
}
if (negative)
*value = -*value;
return POP_RESULT_VALUE;
} else {
if (identifier) {
*identifier = token;
}
return POP_RESULT_IDENTIFIER;
}
}
template<typename ValueType>
bool PostfixEvaluator<ValueType>::PopValue(ValueType *value) {
ValueType literal = ValueType();
string token;
PopResult result;
if ((result = PopValueOrIdentifier(&literal, &token)) == POP_RESULT_FAIL) {
return false;
} else if (result == POP_RESULT_VALUE) {
// This is the easy case.
*value = literal;
} else { // result == POP_RESULT_IDENTIFIER
// There was an identifier at the top of the stack. Resolve it to a
// value by looking it up in the dictionary.
typename DictionaryType::const_iterator iterator =
dictionary_->find(token);
if (iterator == dictionary_->end()) {
// The identifier wasn't found in the dictionary. Don't imply any
// default value, just fail.
BPLOG(INFO) << "Identifier " << token << " not in dictionary";
return false;
}
*value = iterator->second;
}
return true;
}
template<typename ValueType>
bool PostfixEvaluator<ValueType>::PopValues(ValueType *value1,
ValueType *value2) {
return PopValue(value2) && PopValue(value1);
}
template<typename ValueType>
void PostfixEvaluator<ValueType>::PushValue(const ValueType &value) {
ostringstream token_stream;
token_stream << value;
stack_.push_back(token_stream.str());
}
} // namespace google_breakpad
#endif // PROCESSOR_POSTFIX_EVALUATOR_INL_H__

View File

@@ -0,0 +1,172 @@
// -*- mode: C++ -*-
// Copyright (c) 2010 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.
// postfix_evaluator.h: Postfix (reverse Polish) notation expression evaluator.
//
// PostfixEvaluator evaluates an expression, using the expression itself
// in postfix (reverse Polish) notation and a dictionary mapping constants
// and variables to their values. The evaluator supports standard
// arithmetic operations, assignment into variables, and when an optional
// MemoryRange is provided, dereferencing. (Any unary key-to-value operation
// may be used with a MemoryRange implementation that returns the appropriate
// values, but PostfixEvaluator was written with dereferencing in mind.)
//
// The expression language is simple. Expressions are supplied as strings,
// with operands and operators delimited by whitespace. Operands may be
// either literal values suitable for ValueType, or constants or variables,
// which reference the dictionary. The supported binary operators are +
// (addition), - (subtraction), * (multiplication), / (quotient of division),
// and % (modulus of division). The unary ^ (dereference) operator is also
// provided. These operators allow any operand to be either a literal
// value, constant, or variable. Assignment (=) of any type of operand into
// a variable is also supported.
//
// The dictionary is provided as a map with string keys. Keys beginning
// with the '$' character are treated as variables. All other keys are
// treated as constants. Any results must be assigned into variables in the
// dictionary. These variables do not need to exist prior to calling
// Evaluate, unless used in an expression prior to being assigned to. The
// internal stack state is not made available after evaluation, and any
// values remaining on the stack are treated as evidence of incomplete
// execution and cause the evaluator to indicate failure.
//
// PostfixEvaluator is intended to support evaluation of "program strings"
// obtained from MSVC frame data debugging information in pdb files as
// returned by the DIA APIs.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_POSTFIX_EVALUATOR_H__
#define PROCESSOR_POSTFIX_EVALUATOR_H__
#include <map>
#include <string>
#include <vector>
namespace google_breakpad {
using std::map;
using std::string;
using std::vector;
class MemoryRegion;
template<typename ValueType>
class PostfixEvaluator {
public:
typedef map<string, ValueType> DictionaryType;
typedef map<string, bool> DictionaryValidityType;
// Create a PostfixEvaluator object that may be used (with Evaluate) on
// one or more expressions. PostfixEvaluator does not take ownership of
// either argument. |memory| may be NULL, in which case dereferencing
// (^) will not be supported. |dictionary| may be NULL, but evaluation
// will fail in that case unless set_dictionary is used before calling
// Evaluate.
PostfixEvaluator(DictionaryType *dictionary, const MemoryRegion *memory)
: dictionary_(dictionary), memory_(memory), stack_() {}
// Evaluate the expression, starting with an empty stack. The results of
// execution will be stored in one (or more) variables in the dictionary.
// Returns false if any failures occur during execution, leaving
// variables in the dictionary in an indeterminate state. If assigned is
// non-NULL, any keys set in the dictionary as a result of evaluation
// will also be set to true in assigned, providing a way to determine if
// an expression modifies any of its input variables.
bool Evaluate(const string &expression, DictionaryValidityType *assigned);
// Like Evaluate, but provides the value left on the stack to the
// caller. If evaluation succeeds and leaves exactly one value on
// the stack, pop that value, store it in *result, and return true.
// Otherwise, return false.
bool EvaluateForValue(const string &expression, ValueType *result);
DictionaryType* dictionary() const { return dictionary_; }
// Reset the dictionary. PostfixEvaluator does not take ownership.
void set_dictionary(DictionaryType *dictionary) {dictionary_ = dictionary; }
private:
// Return values for PopValueOrIdentifier
enum PopResult {
POP_RESULT_FAIL = 0,
POP_RESULT_VALUE,
POP_RESULT_IDENTIFIER
};
// Retrieves the topmost literal value, constant, or variable from the
// stack. Returns POP_RESULT_VALUE if the topmost entry is a literal
// value, and sets |value| accordingly. Returns POP_RESULT_IDENTIFIER
// if the topmost entry is a constant or variable identifier, and sets
// |identifier| accordingly. Returns POP_RESULT_FAIL on failure, such
// as when the stack is empty.
PopResult PopValueOrIdentifier(ValueType *value, string *identifier);
// Retrieves the topmost value on the stack. If the topmost entry is
// an identifier, the dictionary is queried for the identifier's value.
// Returns false on failure, such as when the stack is empty or when
// a nonexistent identifier is named.
bool PopValue(ValueType *value);
// Retrieves the top two values on the stack, in the style of PopValue.
// value2 is popped before value1, so that value1 corresponds to the
// entry that was pushed prior to value2. Returns false on failure.
bool PopValues(ValueType *value1, ValueType *value2);
// Pushes a new value onto the stack.
void PushValue(const ValueType &value);
// Evaluate expression, updating *assigned if it is non-zero. Return
// true if evaluation completes successfully. Do not clear the stack
// upon successful evaluation.
bool EvaluateInternal(const string &expression,
DictionaryValidityType *assigned);
// The dictionary mapping constant and variable identifiers (strings) to
// values. Keys beginning with '$' are treated as variable names, and
// PostfixEvaluator is free to create and modify these keys. Weak pointer.
DictionaryType *dictionary_;
// If non-NULL, the MemoryRegion used for dereference (^) operations.
// If NULL, dereferencing is unsupported and will fail. Weak pointer.
const MemoryRegion *memory_;
// The stack contains state information as execution progresses. Values
// are pushed on to it as the expression string is read and as operations
// yield values; values are popped when used as operands to operators.
vector<string> stack_;
};
} // namespace google_breakpad
#endif // PROCESSOR_POSTFIX_EVALUATOR_H__

View File

@@ -0,0 +1,394 @@
// Copyright (c) 2010 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.
// postfix_evaluator_unittest.cc: Unit tests for PostfixEvaluator.
//
// Author: Mark Mentovai
#include <stdio.h>
#include <map>
#include <string>
#include "processor/postfix_evaluator-inl.h"
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/processor/memory_region.h"
#include "processor/logging.h"
namespace {
using std::map;
using std::string;
using google_breakpad::MemoryRegion;
using google_breakpad::PostfixEvaluator;
// FakeMemoryRegion is used to test PostfixEvaluator's dereference (^)
// operator. The result of dereferencing a value is one greater than
// the value.
class FakeMemoryRegion : public MemoryRegion {
public:
virtual u_int64_t GetBase() const { return 0; }
virtual u_int32_t GetSize() const { return 0; }
virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
*value = address + 1;
return true;
}
virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
*value = address + 1;
return true;
}
virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
*value = address + 1;
return true;
}
virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
*value = address + 1;
return true;
}
};
struct EvaluateTest {
// Expression passed to PostfixEvaluator::Evaluate.
const string expression;
// True if the expression is expected to be evaluable, false if evaluation
// is expected to fail.
bool evaluable;
};
struct EvaluateTestSet {
// The dictionary used for all tests in the set.
PostfixEvaluator<unsigned int>::DictionaryType *dictionary;
// The list of tests.
const EvaluateTest *evaluate_tests;
// The number of tests.
unsigned int evaluate_test_count;
// Identifiers and their expected values upon completion of the Evaluate
// tests in the set.
map<string, unsigned int> *validate_data;
};
struct EvaluateForValueTest {
// Expression passed to PostfixEvaluator::Evaluate.
const string expression;
// True if the expression is expected to be evaluable, false if evaluation
// is expected to fail.
bool evaluable;
// If evaluable, the value we expect it to yield.
unsigned int value;
};
static bool RunTests() {
// The first test set checks the basic operations and failure modes.
PostfixEvaluator<unsigned int>::DictionaryType dictionary_0;
const EvaluateTest evaluate_tests_0[] = {
{ "$rAdd 2 2 + =", true }, // $rAdd = 2 + 2 = 4
{ "$rAdd $rAdd 2 + =", true }, // $rAdd = $rAdd + 2 = 6
{ "$rAdd 2 $rAdd + =", true }, // $rAdd = 2 + $rAdd = 8
{ "99", false }, // put some junk on the stack...
{ "$rAdd2 2 2 + =", true }, // ...and make sure things still work
{ "$rAdd2\t2\n2 + =", true }, // same but with different whitespace
{ "$rAdd2 2 2 + = ", true }, // trailing whitespace
{ " $rAdd2 2 2 + =", true }, // leading whitespace
{ "$rAdd2 2 2 + =", true }, // extra whitespace
{ "$T0 2 = +", false }, // too few operands for add
{ "2 + =", false }, // too few operands for add
{ "2 +", false }, // too few operands for add
{ "+", false }, // too few operands for add
{ "^", false }, // too few operands for dereference
{ "=", false }, // too few operands for assignment
{ "2 =", false }, // too few operands for assignment
{ "2 2 + =", false }, // too few operands for assignment
{ "2 2 =", false }, // can't assign into a literal
{ "k 2 =", false }, // can't assign into a constant
{ "2", false }, // leftover data on stack
{ "2 2 +", false }, // leftover data on stack
{ "$rAdd", false }, // leftover data on stack
{ "0 $T1 0 0 + =", false }, // leftover data on stack
{ "$T2 $T2 2 + =", false }, // can't operate on an undefined value
{ "$rMul 9 6 * =", true }, // $rMul = 9 * 6 = 54
{ "$rSub 9 6 - =", true }, // $rSub = 9 - 6 = 3
{ "$rDivQ 9 6 / =", true }, // $rDivQ = 9 / 6 = 1
{ "$rDivM 9 6 % =", true }, // $rDivM = 9 % 6 = 3
{ "$rDeref 9 ^ =", true } // $rDeref = ^9 = 10 (FakeMemoryRegion)
};
map<string, unsigned int> validate_data_0;
validate_data_0["$rAdd"] = 8;
validate_data_0["$rAdd2"] = 4;
validate_data_0["$rSub"] = 3;
validate_data_0["$rMul"] = 54;
validate_data_0["$rDivQ"] = 1;
validate_data_0["$rDivM"] = 3;
validate_data_0["$rDeref"] = 10;
// The second test set simulates a couple of MSVC program strings.
// The data is fudged a little bit because the tests use FakeMemoryRegion
// instead of a real stack snapshot, but the program strings are real and
// the implementation doesn't know or care that the data is not real.
PostfixEvaluator<unsigned int>::DictionaryType dictionary_1;
dictionary_1["$ebp"] = 0xbfff0010;
dictionary_1["$eip"] = 0x10000000;
dictionary_1["$esp"] = 0xbfff0000;
dictionary_1[".cbSavedRegs"] = 4;
dictionary_1[".cbParams"] = 4;
dictionary_1[".raSearchStart"] = 0xbfff0020;
const EvaluateTest evaluate_tests_1[] = {
{ "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = "
"$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =", true },
// Intermediate state: $T0 = 0xbfff0010, $eip = 0xbfff0015,
// $ebp = 0xbfff0011, $esp = 0xbfff0018,
// $L = 0xbfff000c, $P = 0xbfff001c
{ "$T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = "
"$L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 28 - ^ =",
true },
// Intermediate state: $T0 = 0xbfff0011, $eip = 0xbfff0016,
// $ebp = 0xbfff0012, $esp = 0xbfff0019,
// $L = 0xbfff000d, $P = 0xbfff001d,
// $ebx = 0xbffefff6
{ "$T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = "
"$esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + = "
"$ebx $T0 28 - ^ =",
true }
};
map<string, unsigned int> validate_data_1;
validate_data_1["$T0"] = 0xbfff0012;
validate_data_1["$T1"] = 0xbfff0020;
validate_data_1["$T2"] = 0xbfff0019;
validate_data_1["$eip"] = 0xbfff0021;
validate_data_1["$ebp"] = 0xbfff0012;
validate_data_1["$esp"] = 0xbfff0024;
validate_data_1["$L"] = 0xbfff000e;
validate_data_1["$P"] = 0xbfff0028;
validate_data_1["$ebx"] = 0xbffefff7;
validate_data_1[".cbSavedRegs"] = 4;
validate_data_1[".cbParams"] = 4;
EvaluateTestSet evaluate_test_sets[] = {
{ &dictionary_0, evaluate_tests_0,
sizeof(evaluate_tests_0) / sizeof(EvaluateTest), &validate_data_0 },
{ &dictionary_1, evaluate_tests_1,
sizeof(evaluate_tests_1) / sizeof(EvaluateTest), &validate_data_1 },
};
unsigned int evaluate_test_set_count = sizeof(evaluate_test_sets) /
sizeof(EvaluateTestSet);
FakeMemoryRegion fake_memory;
PostfixEvaluator<unsigned int> postfix_evaluator =
PostfixEvaluator<unsigned int>(NULL, &fake_memory);
for (unsigned int evaluate_test_set_index = 0;
evaluate_test_set_index < evaluate_test_set_count;
++evaluate_test_set_index) {
EvaluateTestSet *evaluate_test_set =
&evaluate_test_sets[evaluate_test_set_index];
const EvaluateTest *evaluate_tests = evaluate_test_set->evaluate_tests;
unsigned int evaluate_test_count = evaluate_test_set->evaluate_test_count;
// The same dictionary will be used for each test in the set. Earlier
// tests can affect the state of the dictionary for later tests.
postfix_evaluator.set_dictionary(evaluate_test_set->dictionary);
// Use a new validity dictionary for each test set.
PostfixEvaluator<unsigned int>::DictionaryValidityType assigned;
for (unsigned int evaluate_test_index = 0;
evaluate_test_index < evaluate_test_count;
++evaluate_test_index) {
const EvaluateTest *evaluate_test = &evaluate_tests[evaluate_test_index];
// Do the test.
bool result = postfix_evaluator.Evaluate(evaluate_test->expression,
&assigned);
if (result != evaluate_test->evaluable) {
fprintf(stderr, "FAIL: evaluate set %d/%d, test %d/%d, "
"expression \"%s\", expected %s, observed %s\n",
evaluate_test_set_index, evaluate_test_set_count,
evaluate_test_index, evaluate_test_count,
evaluate_test->expression.c_str(),
evaluate_test->evaluable ? "evaluable" : "not evaluable",
result ? "evaluted" : "not evaluated");
return false;
}
}
// Validate the results.
for (map<string, unsigned int>::const_iterator validate_iterator =
evaluate_test_set->validate_data->begin();
validate_iterator != evaluate_test_set->validate_data->end();
++validate_iterator) {
const string identifier = validate_iterator->first;
unsigned int expected_value = validate_iterator->second;
map<string, unsigned int>::const_iterator dictionary_iterator =
evaluate_test_set->dictionary->find(identifier);
// The identifier must exist in the dictionary.
if (dictionary_iterator == evaluate_test_set->dictionary->end()) {
fprintf(stderr, "FAIL: evaluate test set %d/%d, "
"validate identifier \"%s\", "
"expected %d, observed not found\n",
evaluate_test_set_index, evaluate_test_set_count,
identifier.c_str(), expected_value);
return false;
}
// The value in the dictionary must be the same as the expected value.
unsigned int observed_value = dictionary_iterator->second;
if (expected_value != observed_value) {
fprintf(stderr, "FAIL: evaluate test set %d/%d, "
"validate identifier \"%s\", "
"expected %d, observed %d\n",
evaluate_test_set_index, evaluate_test_set_count,
identifier.c_str(), expected_value, observed_value);
return false;
}
// The value must be set in the "assigned" dictionary if it was a
// variable. It must not have been assigned if it was a constant.
bool expected_assigned = identifier[0] == '$';
bool observed_assigned = false;
PostfixEvaluator<unsigned int>::DictionaryValidityType::const_iterator
iterator_assigned = assigned.find(identifier);
if (iterator_assigned != assigned.end()) {
observed_assigned = iterator_assigned->second;
}
if (expected_assigned != observed_assigned) {
fprintf(stderr, "FAIL: evaluate test set %d/%d, "
"validate assignment of \"%s\", "
"expected %d, observed %d\n",
evaluate_test_set_index, evaluate_test_set_count,
identifier.c_str(), expected_assigned, observed_assigned);
return false;
}
}
}
// EvaluateForValue tests.
PostfixEvaluator<unsigned int>::DictionaryType dictionary_2;
dictionary_2["$ebp"] = 0xbfff0010;
dictionary_2["$eip"] = 0x10000000;
dictionary_2["$esp"] = 0xbfff0000;
dictionary_2[".cbSavedRegs"] = 4;
dictionary_2[".cbParams"] = 4;
dictionary_2[".raSearchStart"] = 0xbfff0020;
const EvaluateForValueTest evaluate_for_value_tests_2[] = {
{ "28907223", true, 28907223 }, // simple constant
{ "89854293 40010015 +", true, 89854293 + 40010015 }, // arithmetic
{ "-870245 8769343 +", true, 7899098 }, // negative constants
{ "$ebp $esp - $eip +", true, 0x10000010 }, // variable references
{ "18929794 34015074", false, 0 }, // too many values
{ "$ebp $ebp 4 - =", false, 0 }, // too few values
{ "$new $eip = $new", true, 0x10000000 }, // make new variable
{ "$new 4 +", true, 0x10000004 }, // see prior assignments
{ ".cfa 42 = 10", false, 0 } // can't set constants
};
const int evaluate_for_value_tests_2_size
= (sizeof (evaluate_for_value_tests_2)
/ sizeof (evaluate_for_value_tests_2[0]));
map<string, unsigned int> validate_data_2;
validate_data_2["$eip"] = 0x10000000;
validate_data_2["$ebp"] = 0xbfff000c;
validate_data_2["$esp"] = 0xbfff0000;
validate_data_2["$new"] = 0x10000000;
validate_data_2[".cbSavedRegs"] = 4;
validate_data_2[".cbParams"] = 4;
validate_data_2[".raSearchStart"] = 0xbfff0020;
postfix_evaluator.set_dictionary(&dictionary_2);
for (int i = 0; i < evaluate_for_value_tests_2_size; i++) {
const EvaluateForValueTest *test = &evaluate_for_value_tests_2[i];
unsigned int result;
if (postfix_evaluator.EvaluateForValue(test->expression, &result)
!= test->evaluable) {
fprintf(stderr, "FAIL: evaluate for value test %d, "
"expected evaluation to %s, but it %s\n",
i, test->evaluable ? "succeed" : "fail",
test->evaluable ? "failed" : "succeeded");
return false;
}
if (test->evaluable && result != test->value) {
fprintf(stderr, "FAIL: evaluate for value test %d, "
"expected value to be 0x%x, but it was 0x%x\n",
i, test->value, result);
return false;
}
}
for (map<string, unsigned int>::iterator v = validate_data_2.begin();
v != validate_data_2.end(); v++) {
map<string, unsigned int>::iterator a = dictionary_2.find(v->first);
if (a == dictionary_2.end()) {
fprintf(stderr, "FAIL: evaluate for value dictionary check: "
"expected dict[\"%s\"] to be 0x%x, but it was unset\n",
v->first.c_str(), v->second);
return false;
} else if (a->second != v->second) {
fprintf(stderr, "FAIL: evaluate for value dictionary check: "
"expected dict[\"%s\"] to be 0x%x, but it was 0x%x\n",
v->first.c_str(), v->second, a->second);
return false;
}
dictionary_2.erase(a);
}
map<string, unsigned int>::iterator remaining = dictionary_2.begin();
if (remaining != dictionary_2.end()) {
fprintf(stderr, "FAIL: evaluation of test expressions put unexpected "
"values in dictionary:\n");
for (; remaining != dictionary_2.end(); remaining++)
fprintf(stderr, " dict[\"%s\"] == 0x%x\n",
remaining->first.c_str(), remaining->second);
return false;
}
return true;
}
} // namespace
int main(int argc, char **argv) {
BPLOG_INIT(&argc, &argv);
return RunTests() ? 0 : 1;
}

View File

@@ -0,0 +1,64 @@
// 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.
// process_state.cc: A snapshot of a process, in a fully-digested state.
//
// See process_state.h for documentation.
//
// Author: Mark Mentovai
#include "google_breakpad/processor/process_state.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/code_modules.h"
namespace google_breakpad {
ProcessState::~ProcessState() {
Clear();
}
void ProcessState::Clear() {
time_date_stamp_ = 0;
crashed_ = false;
crash_reason_.clear();
crash_address_ = 0;
assertion_.clear();
requesting_thread_ = -1;
for (vector<CallStack *>::const_iterator iterator = threads_.begin();
iterator != threads_.end();
++iterator) {
delete *iterator;
}
threads_.clear();
system_info_.Clear();
delete modules_;
modules_ = NULL;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,20 @@
If you wish to use these protobufs, you must generate their source files
using protoc from the protobuf project (http://code.google.com/p/protobuf/).
-----
Troubleshooting for Protobuf:
Install:
If you are getting permission errors install, make sure you are not trying to
install from an NFS.
Running protoc:
protoc: error while loading shared libraries: libprotobuf.so.0: cannot open
shared object file: No such file or directory
The issue is that Ubuntu 8.04 doesn't include /usr/local/lib in
library paths.
To fix it for your current terminal session, just type in
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

View File

@@ -0,0 +1,207 @@
// Copyright (c) 2010, 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.
// process_state_proto.proto: A client proto representation of a process,
// in a fully-digested state.
//
// Derived from earlier struct and class based models of a client-side
// processed minidump found under src/google_breakpad/processor. The
// file process_state.h holds the top level representation of this model,
// supported by additional classes. We've added a proto representation
// to ease serialization and parsing for server-side storage of crash
// reports processed on the client.
//
// Author: Jess Gray
syntax = "proto2";
package google_breakpad;
// A proto representation of a process, in a fully-digested state.
// See src/google_breakpad/processor/process_state.h
message ProcessStateProto {
// Next value: 13
// The time-date stamp of the original minidump (time_t format)
optional int64 time_date_stamp = 1;
message Crash {
// The type of crash. OS- and possibly CPU- specific. For example,
// "EXCEPTION_ACCESS_VIOLATION" (Windows), "EXC_BAD_ACCESS /
// KERN_INVALID_ADDRESS" (Mac OS X), "SIGSEGV" (other Unix).
required string reason = 1;
// If crash_reason implicates memory, the memory address that caused the
// crash. For data access errors, this will be the data address that
// caused the fault. For code errors, this will be the address of the
// instruction that caused the fault.
required int64 address = 2;
}
optional Crash crash = 2;
// If there was an assertion that was hit, a textual representation
// of that assertion, possibly including the file and line at which
// it occurred.
optional string assertion = 3;
// The index of the thread that requested a dump be written in the
// threads vector. If a dump was produced as a result of a crash, this
// will point to the thread that crashed. If the dump was produced as
// by user code without crashing, and the dump contains extended Breakpad
// information, this will point to the thread that requested the dump.
optional int32 requesting_thread = 4;
message Thread {
// Stack for the given thread
repeated StackFrame frames = 1;
}
// Stacks for each thread (except possibly the exception handler
// thread) at the time of the crash.
repeated Thread threads = 5;
// The modules that were loaded into the process represented by the
// ProcessState.
repeated CodeModule modules = 6;
// System Info: OS and CPU
// A string identifying the operating system, such as "Windows NT",
// "Mac OS X", or "Linux". If the information is present in the dump but
// its value is unknown, this field will contain a numeric value. If
// the information is not present in the dump, this field will be empty.
optional string os = 7;
// A short form of the os string, using lowercase letters and no spaces,
// suitable for use in a filesystem. Possible values are "windows",
// "mac", and "linux". Empty if the information is not present in the dump
// or if the OS given by the dump is unknown. The values stored in this
// field should match those used by MinidumpSystemInfo::GetOS.
optional string os_short = 8;
// A string identifying the version of the operating system, such as
// "5.1.2600 Service Pack 2" or "10.4.8 8L2127". If the dump does not
// contain this information, this field will be empty.
optional string os_version = 9;
// A string identifying the basic CPU family, such as "x86" or "ppc".
// If this information is present in the dump but its value is unknown,
// this field will contain a numeric value. If the information is not
// present in the dump, this field will be empty. The values stored in
// this field should match those used by MinidumpSystemInfo::GetCPU.
optional string cpu = 10;
// A string further identifying the specific CPU, such as
// "GenuineIntel level 6 model 13 stepping 8". If the information is not
// present in the dump, or additional identifying information is not
// defined for the CPU family, this field will be empty.
optional string cpu_info = 11;
// The number of processors in the system. Will be greater than one for
// multi-core systems.
optional int32 cpu_count = 12;
// Leave the ability to add the raw minidump to this representation
}
// Represents a single frame in a stack
// See src/google_breakpad/processor/code_module.h
message StackFrame {
// Next value: 8
// The program counter location as an absolute virtual address. For the
// innermost called frame in a stack, this will be an exact program counter
// or instruction pointer value. For all other frames, this will be within
// the instruction that caused execution to branch to a called function,
// but may not necessarily point to the exact beginning of that instruction.
required int64 instruction = 1;
// The module in which the instruction resides.
optional CodeModule module = 2;
// The function name, may be omitted if debug symbols are not available.
optional string function_name = 3;
// The start address of the function, may be omitted if debug symbols
// are not available.
optional int64 function_base = 4;
// The source file name, may be omitted if debug symbols are not available.
optional string source_file_name = 5;
// The (1-based) source line number, may be omitted if debug symbols are
// not available.
optional int32 source_line = 6;
// The start address of the source line, may be omitted if debug symbols
// are not available.
optional int64 source_line_base = 7;
}
// Carries information about code modules that are loaded into a process.
// See src/google_breakpad/processor/code_module.h
message CodeModule {
// Next value: 8
// The base address of this code module as it was loaded by the process.
optional int64 base_address = 1;
// The size of the code module.
optional int64 size = 2;
// The path or file name that the code module was loaded from.
optional string code_file = 3;
// An identifying string used to discriminate between multiple versions and
// builds of the same code module. This may contain a uuid, timestamp,
// version number, or any combination of this or other information, in an
// implementation-defined format.
optional string code_identifier = 4;
// The filename containing debugging information associated with the code
// module. If debugging information is stored in a file separate from the
// code module itself (as is the case when .pdb or .dSYM files are used),
// this will be different from code_file. If debugging information is
// stored in the code module itself (possibly prior to stripping), this
// will be the same as code_file.
optional string debug_file = 5;
// An identifying string similar to code_identifier, but identifies a
// specific version and build of the associated debug file. This may be
// the same as code_identifier when the debug_file and code_file are
// identical or when the same identifier is used to identify distinct
// debug and code files.
optional string debug_identifier = 6;
// A human-readable representation of the code module's version.
optional string version = 7;
}

View File

@@ -0,0 +1,210 @@
// Copyright (c) 2010 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.
// range_map-inl.h: Range map implementation.
//
// See range_map.h for documentation.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_RANGE_MAP_INL_H__
#define PROCESSOR_RANGE_MAP_INL_H__
#include <assert.h>
#include "processor/range_map.h"
#include "processor/logging.h"
namespace google_breakpad {
template<typename AddressType, typename EntryType>
bool RangeMap<AddressType, EntryType>::StoreRange(const AddressType &base,
const AddressType &size,
const EntryType &entry) {
AddressType high = base + size - 1;
// Check for undersize or overflow.
if (size <= 0 || high < base) {
// The processor will hit this case too frequently with common symbol
// files in the size == 0 case, which is more suited to a DEBUG channel.
// Filter those out since there's no DEBUG channel at the moment.
BPLOG_IF(INFO, size != 0) << "StoreRange failed, " << HexString(base) <<
"+" << HexString(size) << ", " <<
HexString(high);
return false;
}
// Ensure that this range does not overlap with another one already in the
// map.
MapConstIterator iterator_base = map_.lower_bound(base);
MapConstIterator iterator_high = map_.lower_bound(high);
if (iterator_base != iterator_high) {
// Some other range begins in the space used by this range. It may be
// contained within the space used by this range, or it may extend lower.
// Regardless, it is an error.
AddressType other_base = iterator_base->second.base();
AddressType other_size = iterator_base->first - other_base + 1;
BPLOG(INFO) << "StoreRange failed, an existing range is contained by or "
"extends lower than the new range: new " <<
HexString(base) << "+" << HexString(size) << ", existing " <<
HexString(other_base) << "+" << HexString(other_size);
return false;
}
if (iterator_high != map_.end()) {
if (iterator_high->second.base() <= high) {
// The range above this one overlaps with this one. It may fully
// contain this range, or it may begin within this range and extend
// higher. Regardless, it's an error.
AddressType other_base = iterator_high->second.base();
AddressType other_size = iterator_high->first - other_base + 1;
BPLOG(INFO) << "StoreRange failed, an existing range contains or "
"extends higher than the new range: new " <<
HexString(base) << "+" << HexString(size) <<
", existing " <<
HexString(other_base) << "+" << HexString(other_size);
return false;
}
}
// Store the range in the map by its high address, so that lower_bound can
// be used to quickly locate a range by address.
map_.insert(MapValue(high, Range(base, entry)));
return true;
}
template<typename AddressType, typename EntryType>
bool RangeMap<AddressType, EntryType>::RetrieveRange(
const AddressType &address, EntryType *entry,
AddressType *entry_base, AddressType *entry_size) const {
BPLOG_IF(ERROR, !entry) << "RangeMap::RetrieveRange requires |entry|";
assert(entry);
MapConstIterator iterator = map_.lower_bound(address);
if (iterator == map_.end())
return false;
// The map is keyed by the high address of each range, so |address| is
// guaranteed to be lower than the range's high address. If |range| is
// not directly preceded by another range, it's possible for address to
// be below the range's low address, though. When that happens, address
// references something not within any range, so return false.
if (address < iterator->second.base())
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 {
BPLOG_IF(ERROR, !entry) << "RangeMap::RetrieveNearestRange requires |entry|";
assert(entry);
// 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->second.base();
if (entry_size)
*entry_size = iterator->first - iterator->second.base() + 1;
return true;
}
template<typename AddressType, typename EntryType>
bool RangeMap<AddressType, EntryType>::RetrieveRangeAtIndex(
int index, EntryType *entry,
AddressType *entry_base, AddressType *entry_size) const {
BPLOG_IF(ERROR, !entry) << "RangeMap::RetrieveRangeAtIndex requires |entry|";
assert(entry);
if (index >= GetCount()) {
BPLOG(ERROR) << "Index out of range: " << index << "/" << GetCount();
return false;
}
// Walk through the map. Although it's ordered, it's not a vector, so it
// can't be addressed directly by index.
MapConstIterator iterator = map_.begin();
for (int this_index = 0; this_index < index; ++this_index)
++iterator;
*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>
int RangeMap<AddressType, EntryType>::GetCount() const {
return map_.size();
}
template<typename AddressType, typename EntryType>
void RangeMap<AddressType, EntryType>::Clear() {
map_.clear();
}
} // namespace google_breakpad
#endif // PROCESSOR_RANGE_MAP_INL_H__

View File

@@ -0,0 +1,132 @@
// 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.
// range_map.h: Range maps.
//
// A range map associates a range of addresses with a specific object. This
// is useful when certain objects of variable size are located within an
// address space. The range map makes it simple to determine which object is
// associated with a specific address, which may be any address within the
// range associated with an object.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_RANGE_MAP_H__
#define PROCESSOR_RANGE_MAP_H__
#include <map>
namespace google_breakpad {
// Forward declarations (for later friend declarations of specialized template).
template<class, class> class RangeMapSerializer;
template<typename AddressType, typename EntryType>
class RangeMap {
public:
RangeMap() : map_() {}
// Inserts a range into the map. Returns false for a parameter error,
// or if the location of the range would conflict with a range already
// stored in the map.
bool StoreRange(const AddressType &base,
const AddressType &size,
const EntryType &entry);
// Locates the range encompassing the supplied address. If there is
// no such range, returns false. 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;
// Treating all ranges as a list ordered by the address spaces that they
// occupy, locates the range at the index specified by index. Returns
// false if index is larger than the number of ranges stored. entry_base
// and entry_size, if non-NULL, are set to the base and size of the entry's
// range.
//
// RetrieveRangeAtIndex is not optimized for speedy operation.
bool RetrieveRangeAtIndex(int index, EntryType *entry,
AddressType *entry_base, AddressType *entry_size)
const;
// Returns the number of ranges stored in the RangeMap.
int GetCount() const;
// Empties the range map, restoring it to the state it was when it was
// initially created.
void Clear();
private:
// Friend declarations.
friend class ModuleComparer;
friend class RangeMapSerializer<AddressType, EntryType>;
class Range {
public:
Range(const AddressType &base, const EntryType &entry)
: base_(base), entry_(entry) {}
AddressType base() const { return base_; }
EntryType entry() const { return entry_; }
private:
// The base address of the range. The high address does not need to
// be stored, because RangeMap uses it as the key to the map.
const AddressType base_;
// The entry corresponding to a range.
const EntryType entry_;
};
// Convenience types.
typedef std::map<AddressType, Range> AddressToRangeMap;
typedef typename AddressToRangeMap::const_iterator MapConstIterator;
typedef typename AddressToRangeMap::value_type MapValue;
// Maps the high address of each range to a EntryType.
AddressToRangeMap map_;
};
} // namespace google_breakpad
#endif // PROCESSOR_RANGE_MAP_H__

View File

@@ -0,0 +1,553 @@
// Copyright (c) 2010 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.
// range_map_unittest.cc: Unit tests for RangeMap
//
// Author: Mark Mentovai
#include <limits.h>
#include <stdio.h>
#include "processor/range_map-inl.h"
#include "processor/linked_ptr.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
namespace {
using google_breakpad::linked_ptr;
using google_breakpad::scoped_ptr;
using google_breakpad::RangeMap;
// 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 RangeMap< AddressType, linked_ptr<CountedObject> > TestMap;
// RangeTest contains data to use for store and retrieve tests. See
// RunTests for descriptions of the tests.
struct RangeTest {
// Base address to use for test
AddressType address;
// Size of range to use for test
AddressType size;
// Unique ID of range - unstorable ranges must have unique IDs too
int id;
// Whether this range is expected to be stored successfully or not
bool expect_storable;
};
// A RangeTestSet encompasses multiple RangeTests, which are run in
// sequence on the same RangeMap.
struct RangeTestSet {
// An array of RangeTests
const RangeTest *range_tests;
// The number of tests in the set
unsigned int range_test_count;
};
// StoreTest uses the data in a RangeTest and calls StoreRange on the
// test RangeMap. It returns true if the expected result occurred, and
// false if something else happened.
static bool StoreTest(TestMap *range_map, const RangeTest *range_test) {
linked_ptr<CountedObject> object(new CountedObject(range_test->id));
bool stored = range_map->StoreRange(range_test->address,
range_test->size,
object);
if (stored != range_test->expect_storable) {
fprintf(stderr, "FAILED: "
"StoreRange id %d, expected %s, observed %s\n",
range_test->id,
range_test->expect_storable ? "storable" : "not storable",
stored ? "stored" : "not stored");
return false;
}
return true;
}
// RetrieveTest uses the data in RangeTest and calls RetrieveRange on the
// test RangeMap. If it retrieves the expected value (which can be no
// map entry at the specified range,) it returns true, otherwise, it returns
// false. RetrieveTest will check the values around the base address and
// the high address of a range to guard against off-by-one errors.
static bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
for (unsigned int side = 0; side <= 1; ++side) {
// When side == 0, check the low side (base address) of each range.
// When side == 1, check the high side (base + size) of each range.
// Check one-less and one-greater than the target address in addition
// to the target address itself.
// If the size of the range is only 1, don't check one greater than
// the base or one less than the high - for a successfully stored
// range, these tests would erroneously fail because the range is too
// small.
AddressType low_offset = -1;
AddressType high_offset = 1;
if (range_test->size == 1) {
if (!side) // When checking the low side,
high_offset = 0; // don't check one over the target.
else // When checking the high side,
low_offset = 0; // don't check one under the target.
}
for (AddressType offset = low_offset; offset <= high_offset; ++offset) {
AddressType address =
offset +
(!side ? range_test->address :
range_test->address + range_test->size - 1);
bool expected_result = false; // This is correct for tests not stored.
if (range_test->expect_storable) {
if (offset == 0) // When checking the target address,
expected_result = true; // test should always succeed.
else if (offset == -1) // When checking one below the target,
expected_result = side; // should fail low and succeed high.
else // When checking one above the target,
expected_result = !side; // should succeed low and fail high.
}
linked_ptr<CountedObject> object;
AddressType retrieved_base = AddressType();
AddressType retrieved_size = AddressType();
bool retrieved = range_map->RetrieveRange(address, &object,
&retrieved_base,
&retrieved_size);
bool observed_result = retrieved && object->id() == range_test->id;
if (observed_result != expected_result) {
fprintf(stderr, "FAILED: "
"RetrieveRange id %d, side %d, offset %d, "
"expected %s, observed %s\n",
range_test->id,
side,
offset,
expected_result ? "true" : "false",
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 = AddressType();
AddressType nearest_size = AddressType();
bool retrieved_nearest = range_map->RetrieveNearestRange(address,
&nearest_object,
&nearest_base,
&nearest_size);
// 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;
}
// If a range was successfully retrieved, check that the returned
// bounds match the range as stored.
if (expected_nearest &&
(nearest_base != range_test->address ||
nearest_size != range_test->size)) {
fprintf(stderr, "FAILED: "
"RetrieveNearestRange 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,
nearest_base, nearest_size);
return false;
}
}
}
return true;
}
// Test RetrieveRangeAtIndex, which is supposed to return objects in order
// according to their addresses. This test is performed by looping through
// the map, calling RetrieveRangeAtIndex for all possible indices in sequence,
// and verifying that each call returns a different object than the previous
// call, and that ranges are returned with increasing base addresses. Returns
// false if the test fails.
static bool RetrieveIndexTest(TestMap *range_map, int set) {
linked_ptr<CountedObject> object;
CountedObject *last_object = NULL;
AddressType last_base = 0;
int object_count = range_map->GetCount();
for (int object_index = 0; object_index < object_count; ++object_index) {
AddressType base;
if (!range_map->RetrieveRangeAtIndex(object_index, &object, &base, NULL)) {
fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, "
"expected success, observed failure\n",
set, object_index);
return false;
}
if (!object.get()) {
fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, "
"expected object, observed NULL\n",
set, object_index);
return false;
}
// It's impossible to do these comparisons unless there's a previous
// object to compare against.
if (last_object) {
// The object must be different from the last one.
if (object->id() == last_object->id()) {
fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, "
"expected different objects, observed same objects (%d)\n",
set, object_index, object->id());
return false;
}
// Each object must have a base greater than the previous object's base.
if (base <= last_base) {
fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, "
"expected different bases, observed same bases (%d)\n",
set, object_index, base);
return false;
}
}
last_object = object.get();
last_base = base;
}
// Make sure that RetrieveRangeAtIndex doesn't allow lookups at indices that
// are too high.
if (range_map->RetrieveRangeAtIndex(object_count, &object, NULL, NULL)) {
fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d (too large), "
"expected failure, observed success\n",
set, object_count);
return false;
}
return true;
}
// Additional RetriveAtIndex test to expose the bug in RetrieveRangeAtIndex().
// Bug info: RetrieveRangeAtIndex() previously retrieves the high address of
// entry, however, it is supposed to retrieve the base address of entry as
// stated in the comment in range_map.h.
static bool RetriveAtIndexTest2() {
scoped_ptr<TestMap> range_map(new TestMap());
// Store ranges with base address = 2 * object_id:
const int range_size = 2;
for (int object_id = 0; object_id < 100; ++object_id) {
linked_ptr<CountedObject> object(new CountedObject(object_id));
int base_address = 2 * object_id;
range_map->StoreRange(base_address, range_size, object);
}
linked_ptr<CountedObject> object;
int object_count = range_map->GetCount();
for (int object_index = 0; object_index < object_count; ++object_index) {
AddressType base;
if (!range_map->RetrieveRangeAtIndex(object_index, &object, &base, NULL)) {
fprintf(stderr, "FAILED: RetrieveAtIndexTest2 index %d, "
"expected success, observed failure\n", object_index);
return false;
}
int expected_base = 2 * object->id();
if (base != expected_base) {
fprintf(stderr, "FAILED: RetriveAtIndexTest2 index %d, "
"expected base %d, observed base %d",
object_index, expected_base, base);
return false;
}
}
return true;
}
// RunTests runs a series of test sets.
static bool RunTests() {
// These tests will be run sequentially. The first set of tests exercises
// most functions of RangeTest, and verifies all of the bounds-checking.
const RangeTest range_tests_0[] = {
{ INT_MIN, 16, 1, true }, // lowest possible range
{ -2, 5, 2, true }, // a range through zero
{ INT_MAX - 9, 11, 3, false }, // tests anti-overflow
{ INT_MAX - 9, 10, 4, true }, // highest possible range
{ 5, 0, 5, false }, // tests anti-zero-size
{ 5, 1, 6, true }, // smallest possible range
{ -20, 15, 7, true }, // entirely negative
{ 10, 10, 10, true }, // causes the following tests to fail
{ 9, 10, 11, false }, // one-less base, one-less high
{ 9, 11, 12, false }, // one-less base, identical high
{ 9, 12, 13, false }, // completely contains existing
{ 10, 9, 14, false }, // identical base, one-less high
{ 10, 10, 15, false }, // exactly identical to existing range
{ 10, 11, 16, false }, // identical base, one-greater high
{ 11, 8, 17, false }, // contained completely within
{ 11, 9, 18, false }, // one-greater base, identical high
{ 11, 10, 19, false }, // one-greater base, one-greater high
{ 9, 2, 20, false }, // overlaps bottom by one
{ 10, 1, 21, false }, // overlaps bottom by one, contained
{ 19, 1, 22, false }, // overlaps top by one, contained
{ 19, 2, 23, false }, // overlaps top by one
{ 9, 1, 24, true }, // directly below without overlap
{ 20, 1, 25, true }, // directly above without overlap
{ 6, 3, 26, true }, // exactly between two ranges, gapless
{ 7, 3, 27, false }, // tries to span two ranges
{ 7, 5, 28, false }, // tries to span three ranges
{ 4, 20, 29, false }, // tries to contain several ranges
{ 30, 50, 30, true },
{ 90, 25, 31, true },
{ 35, 65, 32, false }, // tries to span two noncontiguous
{ 120, 10000, 33, true }, // > 8-bit
{ 20000, 20000, 34, true }, // > 8-bit
{ 0x10001, 0x10001, 35, true }, // > 16-bit
{ 27, -1, 36, false } // tests high < base
};
// Attempt to fill the entire space. The entire space must be filled with
// three stores because AddressType is signed for these tests, so RangeMap
// treats the size as signed and rejects sizes that appear to be negative.
// Even if these tests were run as unsigned, two stores would be needed
// to fill the space because the entire size of the space could only be
// described by using one more bit than would be present in AddressType.
const RangeTest range_tests_1[] = {
{ INT_MIN, INT_MAX, 50, true }, // From INT_MIN to -2, inclusive
{ -1, 2, 51, true }, // From -1 to 0, inclusive
{ 1, INT_MAX, 52, true }, // From 1 to INT_MAX, inclusive
{ INT_MIN, INT_MAX, 53, false }, // Can't fill the space twice
{ -1, 2, 54, false },
{ 1, INT_MAX, 55, false },
{ -3, 6, 56, false }, // -3 to 2, inclusive - spans 3 ranges
};
// A light round of testing to verify that RetrieveRange does the right
// the right thing at the extremities of the range when nothing is stored
// there. Checks are forced without storing anything at the extremities
// by setting size = 0.
const RangeTest range_tests_2[] = {
{ INT_MIN, 0, 100, false }, // makes RetrieveRange check low end
{ -1, 3, 101, true },
{ INT_MAX, 0, 102, false }, // makes RetrieveRange check high end
};
// Similar to the previous test set, but with a couple of ranges closer
// to the extremities.
const RangeTest range_tests_3[] = {
{ INT_MIN + 1, 1, 110, true },
{ INT_MAX - 1, 1, 111, true },
{ INT_MIN, 0, 112, false }, // makes RetrieveRange check low end
{ INT_MAX, 0, 113, false } // makes RetrieveRange check high end
};
// The range map is cleared between sets of tests listed here.
const RangeTestSet range_test_sets[] = {
{ range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) },
{ range_tests_1, sizeof(range_tests_1) / sizeof(RangeTest) },
{ range_tests_2, sizeof(range_tests_2) / sizeof(RangeTest) },
{ range_tests_3, sizeof(range_tests_3) / sizeof(RangeTest) },
{ range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) } // Run again
};
// Maintain the range map in a pointer so that deletion can be meaningfully
// tested.
scoped_ptr<TestMap> range_map(new TestMap());
// Run all of the test sets in sequence.
unsigned int range_test_set_count = sizeof(range_test_sets) /
sizeof(RangeTestSet);
for (unsigned int range_test_set_index = 0;
range_test_set_index < range_test_set_count;
++range_test_set_index) {
const RangeTest *range_tests =
range_test_sets[range_test_set_index].range_tests;
unsigned int range_test_count =
range_test_sets[range_test_set_index].range_test_count;
// Run the StoreRange test, which validates StoreRange and initializes
// the RangeMap with data for the RetrieveRange test.
int stored_count = 0; // The number of ranges successfully stored
for (unsigned int range_test_index = 0;
range_test_index < range_test_count;
++range_test_index) {
const RangeTest *range_test = &range_tests[range_test_index];
if (!StoreTest(range_map.get(), range_test))
return false;
if (range_test->expect_storable)
++stored_count;
}
// There should be exactly one CountedObject for everything successfully
// stored in the RangeMap.
if (CountedObject::count() != stored_count) {
fprintf(stderr, "FAILED: "
"stored object counts don't match, expected %d, observed %d\n",
stored_count,
CountedObject::count());
return false;
}
// The RangeMap's own count of objects should also match.
if (range_map->GetCount() != stored_count) {
fprintf(stderr, "FAILED: stored object count doesn't match GetCount, "
"expected %d, observed %d\n",
stored_count, range_map->GetCount());
return false;
}
// Run the RetrieveRange test
for (unsigned int range_test_index = 0;
range_test_index < range_test_count;
++range_test_index) {
const RangeTest *range_test = &range_tests[range_test_index];
if (!RetrieveTest(range_map.get(), range_test))
return false;
}
if (!RetrieveIndexTest(range_map.get(), range_test_set_index))
return false;
// Clear the map between test sets. If this is the final test set,
// delete the map instead to test destruction.
if (range_test_set_index < range_test_set_count - 1)
range_map->Clear();
else
range_map.reset();
// Test that all stored objects are freed when the RangeMap is cleared
// or deleted.
if (CountedObject::count() != 0) {
fprintf(stderr, "FAILED: "
"did not free all objects after %s, %d still allocated\n",
range_test_set_index < range_test_set_count - 1 ? "clear"
: "delete",
CountedObject::count());
return false;
}
}
if (!RetriveAtIndexTest2()) {
fprintf(stderr, "FAILED: did not pass RetrieveAtIndexTest2()\n");
return false;
}
return true;
}
} // namespace
int main(int argc, char **argv) {
BPLOG_INIT(&argc, &argv);
return RunTests() ? 0 : 1;
}

View File

@@ -0,0 +1,335 @@
// (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
// Copyright (c) 2001, 2002 Peter Dimov
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
// See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation.
//
// scoped_ptr mimics a built-in pointer except that it guarantees deletion
// of the object pointed to, either on destruction of the scoped_ptr or via
// an explicit reset(). scoped_ptr is a simple solution for simple needs;
// use shared_ptr or std::auto_ptr if your needs are more complex.
// *** NOTE ***
// If your scoped_ptr is a class member of class FOO pointing to a
// forward declared type BAR (as shown below), then you MUST use a non-inlined
// version of the destructor. The destructor of a scoped_ptr (called from
// FOO's destructor) must have a complete definition of BAR in order to
// destroy it. Example:
//
// -- foo.h --
// class BAR;
//
// class FOO {
// public:
// FOO();
// ~FOO(); // Required for sources that instantiate class FOO to compile!
//
// private:
// scoped_ptr<BAR> bar_;
// };
//
// -- foo.cc --
// #include "foo.h"
// FOO::~FOO() {} // Empty, but must be non-inlined to FOO's class definition.
// scoped_ptr_malloc added by Google
// When one of these goes out of scope, instead of doing a delete or
// delete[], it calls free(). scoped_ptr_malloc<char> is likely to see
// much more use than any other specializations.
// release() added by Google
// Use this to conditionally transfer ownership of a heap-allocated object
// to the caller, usually on method success.
#ifndef PROCESSOR_SCOPED_PTR_H__
#define PROCESSOR_SCOPED_PTR_H__
#include <cstddef> // for std::ptrdiff_t
#include <assert.h> // for assert
#include <stdlib.h> // for free() decl
namespace google_breakpad {
template <typename T>
class scoped_ptr {
private:
T* ptr;
scoped_ptr(scoped_ptr const &);
scoped_ptr & operator=(scoped_ptr const &);
public:
typedef T element_type;
explicit scoped_ptr(T* p = 0): ptr(p) {}
~scoped_ptr() {
typedef char type_must_be_complete[sizeof(T)];
delete ptr;
}
void reset(T* p = 0) {
typedef char type_must_be_complete[sizeof(T)];
if (ptr != p) {
delete ptr;
ptr = p;
}
}
T& operator*() const {
assert(ptr != 0);
return *ptr;
}
T* operator->() const {
assert(ptr != 0);
return ptr;
}
bool operator==(T* p) const {
return ptr == p;
}
bool operator!=(T* p) const {
return ptr != p;
}
T* get() const {
return ptr;
}
void swap(scoped_ptr & b) {
T* tmp = b.ptr;
b.ptr = ptr;
ptr = tmp;
}
T* release() {
T* tmp = ptr;
ptr = 0;
return tmp;
}
private:
// no reason to use these: each scoped_ptr should have its own object
template <typename U> bool operator==(scoped_ptr<U> const& p) const;
template <typename U> bool operator!=(scoped_ptr<U> const& p) const;
};
template<typename T> inline
void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) {
a.swap(b);
}
template<typename T> inline
bool operator==(T* p, const scoped_ptr<T>& b) {
return p == b.get();
}
template<typename T> inline
bool operator!=(T* p, const scoped_ptr<T>& b) {
return p != b.get();
}
// scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
// is guaranteed, either on destruction of the scoped_array or via an explicit
// reset(). Use shared_array or std::vector if your needs are more complex.
template<typename T>
class scoped_array {
private:
T* ptr;
scoped_array(scoped_array const &);
scoped_array & operator=(scoped_array const &);
public:
typedef T element_type;
explicit scoped_array(T* p = 0) : ptr(p) {}
~scoped_array() {
typedef char type_must_be_complete[sizeof(T)];
delete[] ptr;
}
void reset(T* p = 0) {
typedef char type_must_be_complete[sizeof(T)];
if (ptr != p) {
delete [] ptr;
ptr = p;
}
}
T& operator[](std::ptrdiff_t i) const {
assert(ptr != 0);
assert(i >= 0);
return ptr[i];
}
bool operator==(T* p) const {
return ptr == p;
}
bool operator!=(T* p) const {
return ptr != p;
}
T* get() const {
return ptr;
}
void swap(scoped_array & b) {
T* tmp = b.ptr;
b.ptr = ptr;
ptr = tmp;
}
T* release() {
T* tmp = ptr;
ptr = 0;
return tmp;
}
private:
// no reason to use these: each scoped_array should have its own object
template <typename U> bool operator==(scoped_array<U> const& p) const;
template <typename U> bool operator!=(scoped_array<U> const& p) const;
};
template<class T> inline
void swap(scoped_array<T>& a, scoped_array<T>& b) {
a.swap(b);
}
template<typename T> inline
bool operator==(T* p, const scoped_array<T>& b) {
return p == b.get();
}
template<typename T> inline
bool operator!=(T* p, const scoped_array<T>& b) {
return p != b.get();
}
// This class wraps the c library function free() in a class that can be
// passed as a template argument to scoped_ptr_malloc below.
class ScopedPtrMallocFree {
public:
inline void operator()(void* x) const {
free(x);
}
};
// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
// second template argument, the functor used to free the object.
template<typename T, typename FreeProc = ScopedPtrMallocFree>
class scoped_ptr_malloc {
private:
T* ptr;
scoped_ptr_malloc(scoped_ptr_malloc const &);
scoped_ptr_malloc & operator=(scoped_ptr_malloc const &);
public:
typedef T element_type;
explicit scoped_ptr_malloc(T* p = 0): ptr(p) {}
~scoped_ptr_malloc() {
typedef char type_must_be_complete[sizeof(T)];
free_((void*) ptr);
}
void reset(T* p = 0) {
typedef char type_must_be_complete[sizeof(T)];
if (ptr != p) {
free_((void*) ptr);
ptr = p;
}
}
T& operator*() const {
assert(ptr != 0);
return *ptr;
}
T* operator->() const {
assert(ptr != 0);
return ptr;
}
bool operator==(T* p) const {
return ptr == p;
}
bool operator!=(T* p) const {
return ptr != p;
}
T* get() const {
return ptr;
}
void swap(scoped_ptr_malloc & b) {
T* tmp = b.ptr;
b.ptr = ptr;
ptr = tmp;
}
T* release() {
T* tmp = ptr;
ptr = 0;
return tmp;
}
private:
// no reason to use these: each scoped_ptr_malloc should have its own object
template <typename U, typename GP>
bool operator==(scoped_ptr_malloc<U, GP> const& p) const;
template <typename U, typename GP>
bool operator!=(scoped_ptr_malloc<U, GP> const& p) const;
static FreeProc const free_;
};
template<typename T, typename FP>
FP const scoped_ptr_malloc<T,FP>::free_ = FP();
template<typename T, typename FP> inline
void swap(scoped_ptr_malloc<T,FP>& a, scoped_ptr_malloc<T,FP>& b) {
a.swap(b);
}
template<typename T, typename FP> inline
bool operator==(T* p, const scoped_ptr_malloc<T,FP>& b) {
return p == b.get();
}
template<typename T, typename FP> inline
bool operator!=(T* p, const scoped_ptr_malloc<T,FP>& b) {
return p != b.get();
}
} // namespace google_breakpad
#endif // PROCESSOR_SCOPED_PTR_H__

View File

@@ -0,0 +1,252 @@
// Copyright (c) 2010, 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.
//
// simple_serializer-inl.h: template specializations for following types:
// bool, const char *(C-string), string,
// Line, Function, PublicSymbol, WindowsFrameInfo and their linked pointers.
//
// See simple_serializer.h for moredocumentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_SIMPLE_SERIALIZER_INL_H__
#define PROCESSOR_SIMPLE_SERIALIZER_INL_H__
#include <string>
#include "processor/simple_serializer.h"
#include "map_serializers-inl.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "processor/basic_source_line_resolver_types.h"
#include "processor/linked_ptr.h"
#include "processor/windows_frame_info.h"
namespace google_breakpad {
// Specializations of SimpleSerializer: bool
template<>
class SimpleSerializer<bool> {
public:
static size_t SizeOf(bool boolean) { return 1; }
static char *Write(bool boolean, char *dest) {
*dest = static_cast<char>(boolean? 255 : 0);
return ++dest;
}
};
// Specializations of SimpleSerializer: string
template<>
class SimpleSerializer<string> {
public:
static size_t SizeOf(const string &str) { return str.size() + 1; }
static char *Write(const string &str, char *dest) {
strcpy(dest, str.c_str());
return dest + SizeOf(str);
}
};
// Specializations of SimpleSerializer: C-string
template<>
class SimpleSerializer<const char*> {
public:
static size_t SizeOf(const char *cstring) {
return strlen(cstring) + 1;
}
static char *Write(const char *cstring, char *dest) {
strcpy(dest, cstring);
return dest + SizeOf(cstring);
}
};
// Specializations of SimpleSerializer: Line
template<>
class SimpleSerializer<BasicSourceLineResolver::Line> {
typedef BasicSourceLineResolver::Line Line;
public:
static size_t SizeOf(const Line &line) {
return SimpleSerializer<MemAddr>::SizeOf(line.address)
+ SimpleSerializer<MemAddr>::SizeOf(line.size)
+ SimpleSerializer<int32_t>::SizeOf(line.source_file_id)
+ SimpleSerializer<int32_t>::SizeOf(line.line);
}
static char *Write(const Line &line, char *dest) {
dest = SimpleSerializer<MemAddr>::Write(line.address, dest);
dest = SimpleSerializer<MemAddr>::Write(line.size, dest);
dest = SimpleSerializer<int32_t>::Write(line.source_file_id, dest);
dest = SimpleSerializer<int32_t>::Write(line.line, dest);
return dest;
}
};
// Specializations of SimpleSerializer: PublicSymbol
template<>
class SimpleSerializer<BasicSourceLineResolver::PublicSymbol> {
typedef BasicSourceLineResolver::PublicSymbol PublicSymbol;
public:
static size_t SizeOf(const PublicSymbol &pubsymbol) {
return SimpleSerializer<string>::SizeOf(pubsymbol.name)
+ SimpleSerializer<MemAddr>::SizeOf(pubsymbol.address)
+ SimpleSerializer<int32_t>::SizeOf(pubsymbol.parameter_size);
}
static char *Write(const PublicSymbol &pubsymbol, char *dest) {
dest = SimpleSerializer<string>::Write(pubsymbol.name, dest);
dest = SimpleSerializer<MemAddr>::Write(pubsymbol.address, dest);
dest = SimpleSerializer<int32_t>::Write(pubsymbol.parameter_size, dest);
return dest;
}
};
// Specializations of SimpleSerializer: WindowsFrameInfo
template<>
class SimpleSerializer<WindowsFrameInfo> {
public:
static size_t SizeOf(const WindowsFrameInfo &wfi) {
unsigned int size = 0;
size += SimpleSerializer<int32_t>::SizeOf(wfi.valid);
size += SimpleSerializer<u_int32_t>::SizeOf(wfi.prolog_size);
size += SimpleSerializer<u_int32_t>::SizeOf(wfi.epilog_size);
size += SimpleSerializer<u_int32_t>::SizeOf(wfi.parameter_size);
size += SimpleSerializer<u_int32_t>::SizeOf(wfi.saved_register_size);
size += SimpleSerializer<u_int32_t>::SizeOf(wfi.local_size);
size += SimpleSerializer<u_int32_t>::SizeOf(wfi.max_stack_size);
size += SimpleSerializer<bool>::SizeOf(wfi.allocates_base_pointer);
size += SimpleSerializer<string>::SizeOf(wfi.program_string);
return size;
}
static char *Write(const WindowsFrameInfo &wfi, char *dest) {
dest = SimpleSerializer<int32_t>::Write(wfi.valid, dest);
dest = SimpleSerializer<u_int32_t>::Write(wfi.prolog_size, dest);
dest = SimpleSerializer<u_int32_t>::Write(wfi.epilog_size, dest);
dest = SimpleSerializer<u_int32_t>::Write(wfi.parameter_size, dest);
dest = SimpleSerializer<u_int32_t>::Write(wfi.saved_register_size, dest);
dest = SimpleSerializer<u_int32_t>::Write(wfi.local_size, dest);
dest = SimpleSerializer<u_int32_t>::Write(wfi.max_stack_size, dest);
dest = SimpleSerializer<bool>::Write(wfi.allocates_base_pointer, dest);
return SimpleSerializer<string>::Write(wfi.program_string, dest);
}
};
// Specializations of SimpleSerializer: Linked_ptr version of
// Line, Function, PublicSymbol, WindowsFrameInfo.
template<>
class SimpleSerializer< linked_ptr<BasicSourceLineResolver::Line> > {
typedef BasicSourceLineResolver::Line Line;
public:
static size_t SizeOf(const linked_ptr<Line> &lineptr) {
if (lineptr.get() == NULL) return 0;
return SimpleSerializer<Line>::SizeOf(*(lineptr.get()));
}
static char *Write(const linked_ptr<Line> &lineptr, char *dest) {
if (lineptr.get())
dest = SimpleSerializer<Line>::Write(*(lineptr.get()), dest);
return dest;
}
};
template<>
class SimpleSerializer<BasicSourceLineResolver::Function> {
// Convenient type names.
typedef BasicSourceLineResolver::Function Function;
typedef BasicSourceLineResolver::Line Line;
public:
static size_t SizeOf(const Function &func) {
unsigned int size = 0;
size += SimpleSerializer<string>::SizeOf(func.name);
size += SimpleSerializer<MemAddr>::SizeOf(func.address);
size += SimpleSerializer<MemAddr>::SizeOf(func.size);
size += SimpleSerializer<int32_t>::SizeOf(func.parameter_size);
size += range_map_serializer_.SizeOf(func.lines);
return size;
}
static char *Write(const Function &func, char *dest) {
dest = SimpleSerializer<string>::Write(func.name, dest);
dest = SimpleSerializer<MemAddr>::Write(func.address, dest);
dest = SimpleSerializer<MemAddr>::Write(func.size, dest);
dest = SimpleSerializer<int32_t>::Write(func.parameter_size, dest);
dest = range_map_serializer_.Write(func.lines, dest);
return dest;
}
private:
// This static member is defined in module_serializer.cc.
static RangeMapSerializer< MemAddr, linked_ptr<Line> > range_map_serializer_;
};
template<>
class SimpleSerializer< linked_ptr<BasicSourceLineResolver::Function> > {
typedef BasicSourceLineResolver::Function Function;
public:
static size_t SizeOf(const linked_ptr<Function> &func) {
if (!func.get()) return 0;
return SimpleSerializer<Function>::SizeOf(*(func.get()));
}
static char *Write(const linked_ptr<Function> &func, char *dest) {
if (func.get())
dest = SimpleSerializer<Function>::Write(*(func.get()), dest);
return dest;
}
};
template<>
class SimpleSerializer< linked_ptr<BasicSourceLineResolver::PublicSymbol> > {
typedef BasicSourceLineResolver::PublicSymbol PublicSymbol;
public:
static size_t SizeOf(const linked_ptr<PublicSymbol> &pubsymbol) {
if (pubsymbol.get() == NULL) return 0;
return SimpleSerializer<PublicSymbol>::SizeOf(*(pubsymbol.get()));
}
static char *Write(const linked_ptr<PublicSymbol> &pubsymbol, char *dest) {
if (pubsymbol.get())
dest = SimpleSerializer<PublicSymbol>::Write(*(pubsymbol.get()), dest);
return dest;
}
};
template<>
class SimpleSerializer< linked_ptr<WindowsFrameInfo> > {
public:
static size_t SizeOf(const linked_ptr<WindowsFrameInfo> &wfi) {
if (wfi.get() == NULL) return 0;
return SimpleSerializer<WindowsFrameInfo>::SizeOf(*(wfi.get()));
}
static char *Write(const linked_ptr<WindowsFrameInfo> &wfi, char *dest) {
if (wfi.get())
dest = SimpleSerializer<WindowsFrameInfo>::Write(*(wfi.get()), dest);
return dest;
}
};
} // namespace google_breakpad
#endif // PROCESSOR_SIMPLE_SERIALIZER_INL_H__

View File

@@ -0,0 +1,63 @@
// Copyright (c) 2010, 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.
//
// simple_serializer.h: SimpleSerializer is a template for calculating size and
// writing to specific memory location for objects of primitive types, C-style
// string, string, breakpad types/structs etc.
// All specializations of SimpleSerializer template are defined in the
// "simple_serializer-inl.h" file.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_SIMPLE_SERIALIZER_H__
#define PROCESSOR_SIMPLE_SERIALIZER_H__
#include <sys/types.h>
namespace google_breakpad {
typedef u_int64_t MemAddr;
// Default implementation of SimpleSerializer template.
// Specializations are defined in "simple_serializer-inl.h".
template<class Type> class SimpleSerializer {
public:
// Calculate and return the size of the 'item'.
static size_t SizeOf(const Type &item) { return sizeof(item); }
// Write 'item' to memory location 'dest', and return to the "end" address of
// data written, i.e., the address after the final byte written.
static char *Write(const Type &item, char *dest) {
new (dest) Type(item);
return dest + SizeOf(item);
}
};
} // namespace google_breakpad
#endif // PROCESSOR_SIMPLE_SERIALIZER_H__

View File

@@ -0,0 +1,200 @@
// 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.
// simple_symbol_supplier.cc: A simple SymbolSupplier implementation
//
// See simple_symbol_supplier.h for documentation.
//
// Author: Mark Mentovai
#include "processor/simple_symbol_supplier.h"
#include <assert.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <algorithm>
#include <iostream>
#include <fstream>
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/system_info.h"
#include "processor/logging.h"
#include "processor/pathname_stripper.h"
namespace google_breakpad {
static bool file_exists(const string &file_name) {
struct stat sb;
return stat(file_name.c_str(), &sb) == 0;
}
SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFile(
const CodeModule *module, const SystemInfo *system_info,
string *symbol_file) {
BPLOG_IF(ERROR, !symbol_file) << "SimpleSymbolSupplier::GetSymbolFile "
"requires |symbol_file|";
assert(symbol_file);
symbol_file->clear();
for (unsigned int path_index = 0; path_index < paths_.size(); ++path_index) {
SymbolResult result;
if ((result = GetSymbolFileAtPathFromRoot(module, system_info,
paths_[path_index],
symbol_file)) != NOT_FOUND) {
return result;
}
}
return NOT_FOUND;
}
SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFile(
const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
string *symbol_data) {
assert(symbol_data);
symbol_data->clear();
SymbolSupplier::SymbolResult s = GetSymbolFile(module, system_info, symbol_file);
if (s == FOUND) {
std::ifstream in(symbol_file->c_str());
std::getline(in, *symbol_data, std::string::traits_type::to_char_type(
std::string::traits_type::eof()));
in.close();
}
return s;
}
SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetCStringSymbolData(
const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
char **symbol_data) {
assert(symbol_data);
string symbol_data_string;
SymbolSupplier::SymbolResult s =
GetSymbolFile(module, system_info, symbol_file, &symbol_data_string);
if (s == FOUND) {
unsigned int size = symbol_data_string.size() + 1;
*symbol_data = new char[size];
if (*symbol_data == NULL) {
BPLOG(ERROR) << "Memory allocation for size " << size << " failed";
return INTERRUPT;
}
memcpy(*symbol_data, symbol_data_string.c_str(), size - 1);
(*symbol_data)[size - 1] = '\0';
memory_buffers_.insert(make_pair(module->code_file(), *symbol_data));
}
return s;
}
void SimpleSymbolSupplier::FreeSymbolData(const CodeModule *module) {
if (!module) {
BPLOG(INFO) << "Cannot free symbol data buffer for NULL module";
return;
}
map<string, char *>::iterator it = memory_buffers_.find(module->code_file());
if (it == memory_buffers_.end()) {
BPLOG(INFO) << "Cannot find symbol data buffer for module "
<< module->code_file();
return;
}
delete [] it->second;
memory_buffers_.erase(it);
}
SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPathFromRoot(
const CodeModule *module, const SystemInfo *system_info,
const string &root_path, string *symbol_file) {
BPLOG_IF(ERROR, !symbol_file) << "SimpleSymbolSupplier::GetSymbolFileAtPath "
"requires |symbol_file|";
assert(symbol_file);
symbol_file->clear();
if (!module)
return NOT_FOUND;
// Start with the base path.
string path = root_path;
// Append the debug (pdb) file name as a directory name.
path.append("/");
string debug_file_name = PathnameStripper::File(module->debug_file());
if (debug_file_name.empty()) {
BPLOG(ERROR) << "Can't construct symbol file path without debug_file "
"(code_file = " <<
PathnameStripper::File(module->code_file()) << ")";
return NOT_FOUND;
}
path.append(debug_file_name);
// Append the identifier as a directory name.
path.append("/");
string identifier = module->debug_identifier();
if (identifier.empty()) {
BPLOG(ERROR) << "Can't construct symbol file path without debug_identifier "
"(code_file = " <<
PathnameStripper::File(module->code_file()) <<
", debug_file = " << debug_file_name << ")";
return NOT_FOUND;
}
path.append(identifier);
// Transform the debug file name into one ending in .sym. If the existing
// name ends in .pdb, strip the .pdb. Otherwise, add .sym to the non-.pdb
// name.
path.append("/");
string debug_file_extension;
if (debug_file_name.size() > 4)
debug_file_extension = debug_file_name.substr(debug_file_name.size() - 4);
std::transform(debug_file_extension.begin(), debug_file_extension.end(),
debug_file_extension.begin(), tolower);
if (debug_file_extension == ".pdb") {
path.append(debug_file_name.substr(0, debug_file_name.size() - 4));
} else {
path.append(debug_file_name);
}
path.append(".sym");
if (!file_exists(path)) {
BPLOG(INFO) << "No symbol file at " << path;
return NOT_FOUND;
}
*symbol_file = path;
return FOUND;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,139 @@
// 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.
// simple_symbol_supplier.h: A simple SymbolSupplier implementation
//
// SimpleSymbolSupplier is a straightforward implementation of SymbolSupplier
// that stores symbol files in a filesystem tree. A SimpleSymbolSupplier is
// created with one or more base directories, which are the root paths for all
// symbol files. Each symbol file contained therein has a directory entry in
// the base directory with a name identical to the corresponding debugging
// file (pdb). Within each of these directories, there are subdirectories
// named for the debugging file's identifier. For recent pdb files, this is
// a concatenation of the pdb's uuid and age, presented in hexadecimal form,
// without any dashes or separators. The uuid is in uppercase hexadecimal
// and the age is in lowercase hexadecimal. Within that subdirectory,
// SimpleSymbolSupplier expects to find the symbol file, which is named
// identically to the debug file, but with a .sym extension. If the original
// debug file had a name ending in .pdb, the .pdb extension will be replaced
// with .sym. This sample hierarchy is rooted at the "symbols" base
// directory:
//
// symbols
// symbols/test_app.pdb
// symbols/test_app.pdb/63FE4780728D49379B9D7BB6460CB42A1
// symbols/test_app.pdb/63FE4780728D49379B9D7BB6460CB42A1/test_app.sym
// symbols/kernel32.pdb
// symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542
// symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym
//
// In this case, the uuid of test_app.pdb is
// 63fe4780-728d-4937-9b9d-7bb6460cb42a and its age is 1.
//
// This scheme was chosen to be roughly analogous to the way that
// symbol files may be accessed from Microsoft Symbol Server. A hierarchy
// used for Microsoft Symbol Server storage is usable as a hierarchy for
// SimpleSymbolServer, provided that the pdb files are transformed to dumped
// format using a tool such as dump_syms, and given a .sym extension.
//
// SimpleSymbolSupplier will iterate over all root paths searching for
// a symbol file existing in that path.
//
// SimpleSymbolSupplier supports any debugging file which can be identified
// by a CodeModule object's debug_file and debug_identifier accessors. The
// expected ultimate source of these CodeModule objects are MinidumpModule
// objects; it is this class that is responsible for assigning appropriate
// values for debug_file and debug_identifier.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__
#define PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__
#include <map>
#include <string>
#include <vector>
#include "google_breakpad/processor/symbol_supplier.h"
namespace google_breakpad {
using std::map;
using std::string;
using std::vector;
class CodeModule;
class SimpleSymbolSupplier : public SymbolSupplier {
public:
// Creates a new SimpleSymbolSupplier, using path as the root path where
// symbols are stored.
explicit SimpleSymbolSupplier(const string &path) : paths_(1, path) {}
// Creates a new SimpleSymbolSupplier, using paths as a list of root
// paths where symbols may be stored.
explicit SimpleSymbolSupplier(const vector<string> &paths) : paths_(paths) {}
virtual ~SimpleSymbolSupplier() {}
// Returns the path to the symbol file for the given module. See the
// description above.
virtual SymbolResult GetSymbolFile(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file);
virtual SymbolResult GetSymbolFile(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
string *symbol_data);
// Allocates data buffer on heap and writes symbol data into buffer.
// Symbol supplier ALWAYS takes ownership of the data buffer.
virtual SymbolResult GetCStringSymbolData(const CodeModule *module,
const SystemInfo *system_info,
string *symbol_file,
char **symbol_data);
// Free the data buffer allocated in the above GetCStringSymbolData();
virtual void FreeSymbolData(const CodeModule *module);
protected:
SymbolResult GetSymbolFileAtPathFromRoot(const CodeModule *module,
const SystemInfo *system_info,
const string &root_path,
string *symbol_file);
private:
map<string, char *> memory_buffers_;
vector<string> paths_;
};
} // namespace google_breakpad
#endif // PROCESSOR_SIMPLE_SYMBOL_SUPPLIER_H__

View File

@@ -0,0 +1,311 @@
// Copyright (c) 2010 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.
//
// source_line_resolver_base.cc: Implementation of SourceLineResolverBase.
//
// See source_line_resolver_base.h and source_line_resolver_base_types.h for
// more documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <map>
#include <utility>
#include "google_breakpad/processor/source_line_resolver_base.h"
#include "processor/source_line_resolver_base_types.h"
#include "processor/module_factory.h"
using std::map;
using std::make_pair;
namespace google_breakpad {
SourceLineResolverBase::SourceLineResolverBase(
ModuleFactory *module_factory)
: modules_(new ModuleMap),
memory_buffers_(new MemoryMap),
module_factory_(module_factory) {
}
SourceLineResolverBase::~SourceLineResolverBase() {
ModuleMap::iterator it;
// Iterate through ModuleMap and delete all loaded modules.
for (it = modules_->begin(); it != modules_->end(); ++it) {
// Delete individual module.
delete it->second;
}
// Delete the map of modules.
delete modules_;
MemoryMap::iterator iter = memory_buffers_->begin();
for (; iter != memory_buffers_->end(); ++iter) {
delete [] iter->second;
}
// Delete the map of memory buffers.
delete memory_buffers_;
delete module_factory_;
}
bool SourceLineResolverBase::ReadSymbolFile(char **symbol_data,
const string &map_file) {
if (symbol_data == NULL) {
BPLOG(ERROR) << "Could not Read file into Null memory pointer";
return false;
}
struct stat buf;
int error_code = stat(map_file.c_str(), &buf);
if (error_code == -1) {
string error_string;
int error_code = ErrnoString(&error_string);
BPLOG(ERROR) << "Could not open " << map_file <<
", error " << error_code << ": " << error_string;
return false;
}
off_t file_size = buf.st_size;
// Allocate memory for file contents, plus a null terminator
// since we may use strtok() on the contents.
*symbol_data = new char[file_size + 1];
if (*symbol_data == NULL) {
BPLOG(ERROR) << "Could not allocate memory for " << map_file;
return false;
}
BPLOG(INFO) << "Opening " << map_file;
FILE *f = fopen(map_file.c_str(), "rt");
if (!f) {
string error_string;
int error_code = ErrnoString(&error_string);
BPLOG(ERROR) << "Could not open " << map_file <<
", error " << error_code << ": " << error_string;
delete [] (*symbol_data);
*symbol_data = NULL;
return false;
}
AutoFileCloser closer(f);
int items_read = 0;
items_read = fread(*symbol_data, 1, file_size, f);
if (items_read != file_size) {
string error_string;
int error_code = ErrnoString(&error_string);
BPLOG(ERROR) << "Could not slurp " << map_file <<
", error " << error_code << ": " << error_string;
delete [] (*symbol_data);
*symbol_data = NULL;
return false;
}
(*symbol_data)[file_size] = '\0';
return true;
}
bool SourceLineResolverBase::LoadModule(const CodeModule *module,
const string &map_file) {
if (module == NULL)
return false;
// Make sure we don't already have a module with the given name.
if (modules_->find(module->code_file()) != modules_->end()) {
BPLOG(INFO) << "Symbols for module " << module->code_file()
<< " already loaded";
return false;
}
BPLOG(INFO) << "Loading symbols for module " << module->code_file()
<< " from " << map_file;
char *memory_buffer;
if (!ReadSymbolFile(&memory_buffer, map_file))
return false;
BPLOG(INFO) << "Read symbol file " << map_file << " succeeded";
bool load_result = LoadModuleUsingMemoryBuffer(module, memory_buffer);
if (load_result && !ShouldDeleteMemoryBufferAfterLoadModule()) {
// memory_buffer has to stay alive as long as the module.
memory_buffers_->insert(make_pair(module->code_file(), memory_buffer));
} else {
delete [] memory_buffer;
}
return load_result;
}
bool SourceLineResolverBase::LoadModuleUsingMapBuffer(
const CodeModule *module, const string &map_buffer) {
if (module == NULL)
return false;
// Make sure we don't already have a module with the given name.
if (modules_->find(module->code_file()) != modules_->end()) {
BPLOG(INFO) << "Symbols for module " << module->code_file()
<< " already loaded";
return false;
}
char *memory_buffer = new char[map_buffer.size() + 1];
if (memory_buffer == NULL) {
BPLOG(ERROR) << "Could not allocate memory for " << module->code_file();
return false;
}
// Can't use strcpy, as the data may contain '\0's before the end.
memcpy(memory_buffer, map_buffer.c_str(), map_buffer.size());
memory_buffer[map_buffer.size()] = '\0';
bool load_result = LoadModuleUsingMemoryBuffer(module, memory_buffer);
if (load_result && !ShouldDeleteMemoryBufferAfterLoadModule()) {
// memory_buffer has to stay alive as long as the module.
memory_buffers_->insert(make_pair(module->code_file(), memory_buffer));
} else {
delete [] memory_buffer;
}
return load_result;
}
bool SourceLineResolverBase::LoadModuleUsingMemoryBuffer(
const CodeModule *module, char *memory_buffer) {
if (!module)
return false;
// Make sure we don't already have a module with the given name.
if (modules_->find(module->code_file()) != modules_->end()) {
BPLOG(INFO) << "Symbols for module " << module->code_file()
<< " already loaded";
return false;
}
BPLOG(INFO) << "Loading symbols for module " << module->code_file()
<< " from memory buffer";
Module *basic_module = module_factory_->CreateModule(module->code_file());
// Ownership of memory is NOT transfered to Module::LoadMapFromMemory().
if (!basic_module->LoadMapFromMemory(memory_buffer)) {
delete basic_module;
return false;
}
modules_->insert(make_pair(module->code_file(), basic_module));
return true;
}
bool SourceLineResolverBase::ShouldDeleteMemoryBufferAfterLoadModule() {
return true;
}
void SourceLineResolverBase::UnloadModule(const CodeModule *code_module) {
if (!code_module)
return;
ModuleMap::iterator iter = modules_->find(code_module->code_file());
if (iter != modules_->end()) {
Module *symbol_module = iter->second;
delete symbol_module;
modules_->erase(iter);
}
if (ShouldDeleteMemoryBufferAfterLoadModule()) {
// No-op. Because we never store any memory buffers.
} else {
// There may be a buffer stored locally, we need to find and delete it.
MemoryMap::iterator iter = memory_buffers_->find(code_module->code_file());
if (iter != memory_buffers_->end()) {
delete [] iter->second;
memory_buffers_->erase(iter);
}
}
}
bool SourceLineResolverBase::HasModule(const CodeModule *module) {
if (!module)
return false;
return modules_->find(module->code_file()) != modules_->end();
}
void SourceLineResolverBase::FillSourceLineInfo(StackFrame *frame) {
if (frame->module) {
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
if (it != modules_->end()) {
it->second->LookupAddress(frame);
}
}
}
WindowsFrameInfo *SourceLineResolverBase::FindWindowsFrameInfo(
const StackFrame *frame) {
if (frame->module) {
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
if (it != modules_->end()) {
return it->second->FindWindowsFrameInfo(frame);
}
}
return NULL;
}
CFIFrameInfo *SourceLineResolverBase::FindCFIFrameInfo(
const StackFrame *frame) {
if (frame->module) {
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
if (it != modules_->end()) {
return it->second->FindCFIFrameInfo(frame);
}
}
return NULL;
}
bool SourceLineResolverBase::CompareString::operator()(
const string &s1, const string &s2) const {
return strcmp(s1.c_str(), s2.c_str()) < 0;
}
bool SourceLineResolverBase::Module::ParseCFIRuleSet(
const string &rule_set, CFIFrameInfo *frame_info) const {
CFIFrameInfoParseHandler handler(frame_info);
CFIRuleParser parser(&handler);
return parser.Parse(rule_set);
}
} // namespace google_breakpad

View File

@@ -0,0 +1,149 @@
// Copyright (c) 2010 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.
// source_line_resolver_base_types.h: definition of nested classes/structs in
// SourceLineResolverBase. It moves the definitions out of
// source_line_resolver_base.cc, so that other classes may have access
// to these private nested types without including source_line_resolver_base.cc
// In addition, Module is defined as a pure abstract class to be implemented by
// each concrete source line resolver class.
//
// See source_line_resolver_base.h for more documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#include <stdio.h>
#include <map>
#include <string>
#include "google_breakpad/processor/source_line_resolver_base.h"
#include "google_breakpad/processor/stack_frame.h"
#include "processor/cfi_frame_info.h"
#include "processor/windows_frame_info.h"
#ifndef PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__
#define PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__
namespace google_breakpad {
class SourceLineResolverBase::AutoFileCloser {
public:
explicit AutoFileCloser(FILE *file) : file_(file) {}
~AutoFileCloser() {
if (file_)
fclose(file_);
}
private:
FILE *file_;
};
struct SourceLineResolverBase::Line {
Line() { }
Line(MemAddr addr, MemAddr code_size, int file_id, int source_line)
: address(addr)
, size(code_size)
, source_file_id(file_id)
, line(source_line) { }
MemAddr address;
MemAddr size;
int32_t source_file_id;
int32_t line;
};
struct SourceLineResolverBase::Function {
Function() { }
Function(const string &function_name,
MemAddr function_address,
MemAddr code_size,
int set_parameter_size)
: name(function_name), address(function_address), size(code_size),
parameter_size(set_parameter_size) { }
string name;
MemAddr address;
MemAddr size;
// The size of parameters passed to this function on the stack.
int32_t parameter_size;
};
struct SourceLineResolverBase::PublicSymbol {
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.
int32_t parameter_size;
};
class SourceLineResolverBase::Module {
public:
virtual ~Module() { };
// Loads a map from the given buffer in char* type.
// Does NOT take ownership of memory_buffer (the caller, source line resolver,
// is the owner of memory_buffer).
virtual bool LoadMapFromMemory(char *memory_buffer) = 0;
// Looks up the given relative address, and fills the StackFrame struct
// with the result.
virtual void LookupAddress(StackFrame *frame) const = 0;
// If Windows stack walking information is available covering ADDRESS,
// return a WindowsFrameInfo structure describing it. If the information
// is not available, returns NULL. A NULL return value does not indicate
// an error. The caller takes ownership of any returned WindowsFrameInfo
// object.
virtual WindowsFrameInfo *
FindWindowsFrameInfo(const StackFrame *frame) const = 0;
// If CFI stack walking information is available covering ADDRESS,
// return a CFIFrameInfo structure describing it. If the information
// is not available, return NULL. The caller takes ownership of any
// returned CFIFrameInfo object.
virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const = 0;
protected:
virtual bool ParseCFIRuleSet(const string &rule_set,
CFIFrameInfo *frame_info) const;
};
} // namespace google_breakpad
#endif // PROCESSOR_SOURCE_LINE_RESOLVER_BASE_TYPES_H__

View File

@@ -0,0 +1,239 @@
// Copyright (c) 2010 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.
// stackwalker.cc: Generic stackwalker.
//
// See stackwalker.h for documentation.
//
// Author: Mark Mentovai
#include "google_breakpad/processor/stackwalker.h"
#include <assert.h>
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/code_modules.h"
#include "google_breakpad/processor/minidump.h"
#include "google_breakpad/processor/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame.h"
#include "google_breakpad/processor/symbol_supplier.h"
#include "processor/linked_ptr.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
#include "processor/stackwalker_ppc.h"
#include "processor/stackwalker_sparc.h"
#include "processor/stackwalker_x86.h"
#include "processor/stackwalker_amd64.h"
#include "processor/stackwalker_arm.h"
namespace google_breakpad {
u_int32_t Stackwalker::max_frames_ = 1024;
Stackwalker::Stackwalker(const SystemInfo *system_info,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver)
: system_info_(system_info),
memory_(memory),
modules_(modules),
resolver_(resolver),
supplier_(supplier) {
}
bool Stackwalker::Walk(CallStack *stack) {
BPLOG_IF(ERROR, !stack) << "Stackwalker::Walk requires |stack|";
assert(stack);
stack->Clear();
// Begin with the context frame, and keep getting callers until there are
// no more.
// Take ownership of the pointer returned by GetContextFrame.
scoped_ptr<StackFrame> frame(GetContextFrame());
while (frame.get()) {
// frame already contains a good frame with properly set instruction and
// frame_pointer fields. The frame structure comes from either the
// context frame (above) or a caller frame (below).
// Resolve the module information, if a module map was provided.
if (modules_) {
const CodeModule *module =
modules_->GetModuleForAddress(frame->instruction);
if (module) {
frame->module = module;
if (resolver_ &&
!resolver_->HasModule(frame->module) &&
no_symbol_modules_.find(
module->code_file()) == no_symbol_modules_.end() &&
supplier_) {
string symbol_file;
char *symbol_data = NULL;
SymbolSupplier::SymbolResult symbol_result =
supplier_->GetCStringSymbolData(module,
system_info_,
&symbol_file,
&symbol_data);
switch (symbol_result) {
case SymbolSupplier::FOUND:
resolver_->LoadModuleUsingMemoryBuffer(frame->module,
symbol_data);
break;
case SymbolSupplier::NOT_FOUND:
no_symbol_modules_.insert(module->code_file());
break; // nothing to do
case SymbolSupplier::INTERRUPT:
return false;
}
// Inform symbol supplier to free the unused data memory buffer.
if (resolver_->ShouldDeleteMemoryBufferAfterLoadModule())
supplier_->FreeSymbolData(module);
}
if (resolver_)
resolver_->FillSourceLineInfo(frame.get());
}
}
// Add the frame to the call stack. Relinquish the ownership claim
// over the frame, because the stack now owns it.
stack->frames_.push_back(frame.release());
if (stack->frames_.size() > max_frames_) {
BPLOG(ERROR) << "The stack is over " << max_frames_ << " frames.";
break;
}
// Get the next frame and take ownership.
frame.reset(GetCallerFrame(stack));
}
return true;
}
// static
Stackwalker* Stackwalker::StackwalkerForCPU(
const SystemInfo *system_info,
MinidumpContext *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver) {
if (!context) {
BPLOG(ERROR) << "Can't choose a stackwalker implementation without context";
return NULL;
}
Stackwalker *cpu_stackwalker = NULL;
u_int32_t cpu = context->GetContextCPU();
switch (cpu) {
case MD_CONTEXT_X86:
cpu_stackwalker = new StackwalkerX86(system_info,
context->GetContextX86(),
memory, modules, supplier,
resolver);
break;
case MD_CONTEXT_PPC:
cpu_stackwalker = new StackwalkerPPC(system_info,
context->GetContextPPC(),
memory, modules, supplier,
resolver);
break;
case MD_CONTEXT_AMD64:
cpu_stackwalker = new StackwalkerAMD64(system_info,
context->GetContextAMD64(),
memory, modules, supplier,
resolver);
break;
case MD_CONTEXT_SPARC:
cpu_stackwalker = new StackwalkerSPARC(system_info,
context->GetContextSPARC(),
memory, modules, supplier,
resolver);
break;
case MD_CONTEXT_ARM:
cpu_stackwalker = new StackwalkerARM(system_info,
context->GetContextARM(),
memory, modules, supplier,
resolver);
break;
}
BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) <<
", can't choose a stackwalker "
"implementation";
return cpu_stackwalker;
}
bool Stackwalker::InstructionAddressSeemsValid(u_int64_t address) {
const CodeModule *module = modules_->GetModuleForAddress(address);
if (!module) {
// not inside any loaded module
return false;
}
if (!resolver_ || !supplier_) {
// we don't have a resolver and or symbol supplier,
// but we're inside a known module
return true;
}
if (!resolver_->HasModule(module)) {
string symbol_file;
char *symbol_data = NULL;
SymbolSupplier::SymbolResult symbol_result =
supplier_->GetCStringSymbolData(module, system_info_,
&symbol_file, &symbol_data);
if (symbol_result != SymbolSupplier::FOUND ||
!resolver_->LoadModuleUsingMemoryBuffer(module,
symbol_data)) {
// we don't have symbols, but we're inside a loaded module
return true;
}
}
StackFrame frame;
frame.module = module;
frame.instruction = address;
resolver_->FillSourceLineInfo(&frame);
// we have symbols, so return true if inside a function
return !frame.function_name.empty();
}
} // namespace google_breakpad

View File

@@ -0,0 +1,224 @@
// Copyright (c) 2010 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.
// stackwalker_amd64.cc: amd64-specific stackwalker.
//
// See stackwalker_amd64.h for documentation.
//
// Author: Mark Mentovai, Ted Mielczarek
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/memory_region.h"
#include "google_breakpad/processor/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/cfi_frame_info.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
#include "processor/stackwalker_amd64.h"
namespace google_breakpad {
const StackwalkerAMD64::CFIWalker::RegisterSet
StackwalkerAMD64::cfi_register_map_[] = {
// It may seem like $rip and $rsp are callee-saves, because the callee is
// responsible for having them restored upon return. But the callee_saves
// flags here really means that the walker should assume they're
// unchanged if the CFI doesn't mention them --- clearly wrong for $rip
// and $rsp.
{ "$rax", NULL, false,
StackFrameAMD64::CONTEXT_VALID_RAX, &MDRawContextAMD64::rax },
{ "$rdx", NULL, false,
StackFrameAMD64::CONTEXT_VALID_RDX, &MDRawContextAMD64::rdx },
{ "$rcx", NULL, false,
StackFrameAMD64::CONTEXT_VALID_RCX, &MDRawContextAMD64::rcx },
{ "$rbx", NULL, true,
StackFrameAMD64::CONTEXT_VALID_RBX, &MDRawContextAMD64::rbx },
{ "$rsi", NULL, false,
StackFrameAMD64::CONTEXT_VALID_RSI, &MDRawContextAMD64::rsi },
{ "$rdi", NULL, false,
StackFrameAMD64::CONTEXT_VALID_RDI, &MDRawContextAMD64::rdi },
{ "$rbp", NULL, true,
StackFrameAMD64::CONTEXT_VALID_RBP, &MDRawContextAMD64::rbp },
{ "$rsp", ".cfa", false,
StackFrameAMD64::CONTEXT_VALID_RSP, &MDRawContextAMD64::rsp },
{ "$r8", NULL, false,
StackFrameAMD64::CONTEXT_VALID_R8, &MDRawContextAMD64::r8 },
{ "$r9", NULL, false,
StackFrameAMD64::CONTEXT_VALID_R9, &MDRawContextAMD64::r9 },
{ "$r10", NULL, false,
StackFrameAMD64::CONTEXT_VALID_R10, &MDRawContextAMD64::r10 },
{ "$r11", NULL, false,
StackFrameAMD64::CONTEXT_VALID_R11, &MDRawContextAMD64::r11 },
{ "$r12", NULL, true,
StackFrameAMD64::CONTEXT_VALID_R12, &MDRawContextAMD64::r12 },
{ "$r13", NULL, true,
StackFrameAMD64::CONTEXT_VALID_R13, &MDRawContextAMD64::r13 },
{ "$r14", NULL, true,
StackFrameAMD64::CONTEXT_VALID_R14, &MDRawContextAMD64::r14 },
{ "$r15", NULL, true,
StackFrameAMD64::CONTEXT_VALID_R15, &MDRawContextAMD64::r15 },
{ "$rip", ".ra", false,
StackFrameAMD64::CONTEXT_VALID_RIP, &MDRawContextAMD64::rip },
};
StackwalkerAMD64::StackwalkerAMD64(const SystemInfo *system_info,
const MDRawContextAMD64 *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver)
: Stackwalker(system_info, memory, modules, supplier, resolver),
context_(context),
cfi_walker_(cfi_register_map_,
(sizeof(cfi_register_map_) / sizeof(cfi_register_map_[0]))) {
}
StackFrame* StackwalkerAMD64::GetContextFrame() {
if (!context_ || !memory_) {
BPLOG(ERROR) << "Can't get context frame without context or memory";
return NULL;
}
StackFrameAMD64 *frame = new StackFrameAMD64();
// The instruction pointer is stored directly in a register, so pull it
// straight out of the CPU context structure.
frame->context = *context_;
frame->context_validity = StackFrameAMD64::CONTEXT_VALID_ALL;
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
frame->instruction = frame->context.rip;
return frame;
}
StackFrameAMD64 *StackwalkerAMD64::GetCallerByCFIFrameInfo(
const vector<StackFrame *> &frames,
CFIFrameInfo *cfi_frame_info) {
StackFrameAMD64 *last_frame = static_cast<StackFrameAMD64*>(frames.back());
scoped_ptr<StackFrameAMD64> frame(new StackFrameAMD64());
if (!cfi_walker_
.FindCallerRegisters(*memory_, *cfi_frame_info,
last_frame->context, last_frame->context_validity,
&frame->context, &frame->context_validity))
return NULL;
// Make sure we recovered all the essentials.
static const int essentials = (StackFrameAMD64::CONTEXT_VALID_RIP
| StackFrameAMD64::CONTEXT_VALID_RSP);
if ((frame->context_validity & essentials) != essentials)
return NULL;
frame->trust = StackFrame::FRAME_TRUST_CFI;
return frame.release();
}
StackFrameAMD64 *StackwalkerAMD64::GetCallerByStackScan(
const vector<StackFrame *> &frames) {
StackFrameAMD64 *last_frame = static_cast<StackFrameAMD64 *>(frames.back());
u_int64_t last_rsp = last_frame->context.rsp;
u_int64_t caller_rsp, caller_rip;
if (!ScanForReturnAddress(last_rsp, &caller_rsp, &caller_rip)) {
// No plausible return address was found.
return NULL;
}
// ScanForReturnAddress found a reasonable return address. Advance
// %rsp to the location above the one where the return address was
// found.
caller_rsp += 8;
// Create a new stack frame (ownership will be transferred to the caller)
// and fill it in.
StackFrameAMD64 *frame = new StackFrameAMD64();
frame->trust = StackFrame::FRAME_TRUST_SCAN;
frame->context = last_frame->context;
frame->context.rip = caller_rip;
frame->context.rsp = caller_rsp;
frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP;
return frame;
}
StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack) {
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
return NULL;
}
const vector<StackFrame *> &frames = *stack->frames();
StackFrameAMD64 *last_frame = static_cast<StackFrameAMD64 *>(frames.back());
scoped_ptr<StackFrameAMD64> new_frame;
// If we have DWARF CFI information, use it.
scoped_ptr<CFIFrameInfo> cfi_frame_info(
resolver_ ? resolver_->FindCFIFrameInfo(last_frame) : NULL);
if (cfi_frame_info.get())
new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
// If CFI failed, or there wasn't CFI available, fall back
// to stack scanning.
if (!new_frame.get()) {
new_frame.reset(GetCallerByStackScan(frames));
}
// If nothing worked, tell the caller.
if (!new_frame.get())
return NULL;
// Treat an instruction address of 0 as end-of-stack.
if (new_frame->context.rip == 0)
return NULL;
// If the new stack pointer is at a lower address than the old, then
// that's clearly incorrect. Treat this as end-of-stack to enforce
// progress and avoid infinite loops.
if (new_frame->context.rsp <= last_frame->context.rsp)
return NULL;
// new_frame->context.rip is the return address, which is one instruction
// past the CALL that caused us to arrive at the callee. Set
// new_frame->instruction to one less than that. This won't reference the
// beginning of the CALL instruction, but it's guaranteed to be within
// the CALL, which is sufficient to get the source line information to
// match up with the line that contains a function call. Callers that
// require the exact return address value may access the context.rip
// field of StackFrameAMD64.
new_frame->instruction = new_frame->context.rip - 1;
return new_frame.release();
}
} // namespace google_breakpad

View File

@@ -0,0 +1,99 @@
// Copyright (c) 2010 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.
// stackwalker_amd64.h: amd64-specific stackwalker.
//
// Provides stack frames given amd64 register context and a memory region
// corresponding to a amd64 stack.
//
// Author: Mark Mentovai, Ted Mielczarek
#ifndef PROCESSOR_STACKWALKER_AMD64_H__
#define PROCESSOR_STACKWALKER_AMD64_H__
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/stackwalker.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/cfi_frame_info.h"
namespace google_breakpad {
class CodeModules;
class StackwalkerAMD64 : public Stackwalker {
public:
// context is a amd64 context object that gives access to amd64-specific
// register state corresponding to the innermost called frame to be
// included in the stack. The other arguments are passed directly through
// to the base Stackwalker constructor.
StackwalkerAMD64(const SystemInfo *system_info,
const MDRawContextAMD64 *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver);
private:
// A STACK CFI-driven frame walker for the AMD64
typedef SimpleCFIWalker<u_int64_t, MDRawContextAMD64> CFIWalker;
// Implementation of Stackwalker, using amd64 context (stack pointer in %rsp,
// stack base in %rbp) and stack conventions (saved stack pointer at 0(%rbp))
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack *stack);
// Use cfi_frame_info (derived from STACK CFI records) to construct
// the frame that called frames.back(). The caller takes ownership
// of the returned frame. Return NULL on failure.
StackFrameAMD64 *GetCallerByCFIFrameInfo(const vector<StackFrame *> &frames,
CFIFrameInfo *cfi_frame_info);
// Scan the stack for plausible return addresses. The caller takes ownership
// of the returned frame. Return NULL on failure.
StackFrameAMD64 *GetCallerByStackScan(const vector<StackFrame *> &frames);
// Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame.
const MDRawContextAMD64 *context_;
// Our register map, for cfi_walker_.
static const CFIWalker::RegisterSet cfi_register_map_[];
// Our CFI frame walker.
const CFIWalker cfi_walker_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STACKWALKER_AMD64_H__

View File

@@ -0,0 +1,484 @@
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// stackwalker_amd64_unittest.cc: Unit tests for StackwalkerAMD64 class.
#include <string>
#include <string.h>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/test_assembler.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/stackwalker_unittest_utils.h"
#include "processor/stackwalker_amd64.h"
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::StackFrame;
using google_breakpad::StackFrameAMD64;
using google_breakpad::StackwalkerAMD64;
using google_breakpad::SystemInfo;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using std::string;
using std::vector;
using testing::_;
using testing::Return;
using testing::SetArgumentPointee;
using testing::Test;
class StackwalkerAMD64Fixture {
public:
StackwalkerAMD64Fixture()
: stack_section(kLittleEndian),
// Give the two modules reasonable standard locations and names
// for tests to play with.
module1(0x40000000c0000000ULL, 0x10000, "module1", "version1"),
module2(0x50000000b0000000ULL, 0x10000, "module2", "version2") {
// Identify the system as a Linux system.
system_info.os = "Linux";
system_info.os_short = "linux";
system_info.os_version = "Horrendous Hippo";
system_info.cpu = "x86";
system_info.cpu_info = "";
// Put distinctive values in the raw CPU context.
BrandContext(&raw_context);
// Create some modules with some stock debugging information.
modules.Add(&module1);
modules.Add(&module2);
// By default, none of the modules have symbol info; call
// SetModuleSymbols to override this.
EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _))
.WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
}
// Set the Breakpad symbol information that supplier should return for
// MODULE to INFO.
void SetModuleSymbols(MockCodeModule *module, const string &info) {
unsigned int buffer_size = info.size() + 1;
char *buffer = reinterpret_cast<char*>(operator new(buffer_size));
strcpy(buffer, info.c_str());
EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _))
.WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer),
Return(MockSymbolSupplier::FOUND)));
}
// Populate stack_region with the contents of stack_section. Use
// stack_section.start() as the region's starting address.
void RegionFromSection() {
string contents;
ASSERT_TRUE(stack_section.GetContents(&contents));
stack_region.Init(stack_section.start().Value(), contents);
}
// Fill RAW_CONTEXT with pseudo-random data, for round-trip checking.
void BrandContext(MDRawContextAMD64 *raw_context) {
u_int8_t x = 173;
for (size_t i = 0; i < sizeof(*raw_context); i++)
reinterpret_cast<u_int8_t *>(raw_context)[i] = (x += 17);
}
SystemInfo system_info;
MDRawContextAMD64 raw_context;
Section stack_section;
MockMemoryRegion stack_region;
MockCodeModule module1;
MockCodeModule module2;
MockCodeModules modules;
MockSymbolSupplier supplier;
BasicSourceLineResolver resolver;
CallStack call_stack;
const vector<StackFrame *> *frames;
};
class GetContextFrame: public StackwalkerAMD64Fixture, public Test { };
class SanityCheck: public StackwalkerAMD64Fixture, public Test { };
TEST_F(SanityCheck, NoResolver) {
// There should be no references to the stack in this walk: we don't
// provide any call frame information, so trying to reconstruct the
// context frame's caller should fail. So there's no need for us to
// provide stack contents.
raw_context.rip = 0x40000000c0000200ULL;
raw_context.rbp = 0x8000000080000000ULL;
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
NULL, NULL);
// This should succeed even without a resolver or supplier.
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_GE(1U, frames->size());
StackFrameAMD64 *frame = static_cast<StackFrameAMD64 *>(frames->at(0));
// Check that the values from the original raw context made it
// through to the context in the stack frame.
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
TEST_F(GetContextFrame, Simple) {
// There should be no references to the stack in this walk: we don't
// provide any call frame information, so trying to reconstruct the
// context frame's caller should fail. So there's no need for us to
// provide stack contents.
raw_context.rip = 0x40000000c0000200ULL;
raw_context.rbp = 0x8000000080000000ULL;
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_GE(1U, frames->size());
StackFrameAMD64 *frame = static_cast<StackFrameAMD64 *>(frames->at(0));
// Check that the values from the original raw context made it
// through to the context in the stack frame.
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
class GetCallerFrame: public StackwalkerAMD64Fixture, public Test { };
TEST_F(GetCallerFrame, ScanWithoutSymbols) {
// When the stack walker resorts to scanning the stack,
// only addresses located within loaded modules are
// considered valid return addresses.
// Force scanning through three frames to ensure that the
// stack pointer is set properly in scan-recovered frames.
stack_section.start() = 0x8000000080000000ULL;
u_int64_t return_address1 = 0x50000000b0000100ULL;
u_int64_t return_address2 = 0x50000000b0000900ULL;
Label frame1_sp, frame2_sp;
stack_section
// frame 0
.Append(16, 0) // space
.D64(0x40000000b0000000ULL) // junk that's not
.D64(0x50000000d0000000ULL) // a return address
.D64(return_address1) // actual return address
// frame 1
.Mark(&frame1_sp)
.Append(16, 0) // space
.D64(0x40000000b0000000ULL) // more junk
.D64(0x50000000d0000000ULL)
.D64(return_address2) // actual return address
// frame 2
.Mark(&frame2_sp)
.Append(32, 0); // end of stack
RegionFromSection();
raw_context.rip = 0x40000000c0000200ULL;
raw_context.rsp = stack_section.start().Value();
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(3U, frames->size());
StackFrameAMD64 *frame0 = static_cast<StackFrameAMD64 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
StackFrameAMD64 *frame1 = static_cast<StackFrameAMD64 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP),
frame1->context_validity);
EXPECT_EQ(return_address1, frame1->context.rip);
EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp);
StackFrameAMD64 *frame2 = static_cast<StackFrameAMD64 *>(frames->at(2));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust);
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP),
frame2->context_validity);
EXPECT_EQ(return_address2, frame2->context.rip);
EXPECT_EQ(frame2_sp.Value(), frame2->context.rsp);
}
TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
// During stack scanning, if a potential return address
// is located within a loaded module that has symbols,
// it is only considered a valid return address if it
// lies within a function's bounds.
stack_section.start() = 0x8000000080000000ULL;
u_int64_t return_address = 0x50000000b0000110ULL;
Label frame1_sp;
stack_section
// frame 0
.Append(16, 0) // space
.D64(0x40000000b0000000ULL) // junk that's not
.D64(0x50000000b0000000ULL) // a return address
.D64(0x40000000c0001000ULL) // a couple of plausible addresses
.D64(0x50000000b000aaaaULL) // that are not within functions
.D64(return_address) // actual return address
// frame 1
.Mark(&frame1_sp)
.Append(32, 0); // end of stack
RegionFromSection();
raw_context.rip = 0x40000000c0000200ULL;
raw_context.rsp = stack_section.start().Value();
SetModuleSymbols(&module1,
// The youngest frame's function.
"FUNC 100 400 10 platypus\n");
SetModuleSymbols(&module2,
// The calling frame's function.
"FUNC 100 400 10 echidna\n");
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameAMD64 *frame0 = static_cast<StackFrameAMD64 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ("platypus", frame0->function_name);
EXPECT_EQ(0x40000000c0000100ULL, frame0->function_base);
StackFrameAMD64 *frame1 = static_cast<StackFrameAMD64 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP),
frame1->context_validity);
EXPECT_EQ(return_address, frame1->context.rip);
EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp);
EXPECT_EQ("echidna", frame1->function_name);
EXPECT_EQ(0x50000000b0000100ULL, frame1->function_base);
}
struct CFIFixture: public StackwalkerAMD64Fixture {
CFIFixture() {
// Provide a bunch of STACK CFI records; we'll walk to the caller
// from every point in this series, expecting to find the same set
// of register values.
SetModuleSymbols(&module1,
// The youngest frame's function.
"FUNC 4000 1000 10 enchiridion\n"
// Initially, just a return address.
"STACK CFI INIT 4000 100 .cfa: $rsp 8 + .ra: .cfa 8 - ^\n"
// Push %rbx.
"STACK CFI 4001 .cfa: $rsp 16 + $rbx: .cfa 16 - ^\n"
// Save %r12 in %rbx. Weird, but permitted.
"STACK CFI 4002 $r12: $rbx\n"
// Allocate frame space, and save %r13.
"STACK CFI 4003 .cfa: $rsp 40 + $r13: .cfa 32 - ^\n"
// Put the return address in %r13.
"STACK CFI 4005 .ra: $r13\n"
// Save %rbp, and use it as a frame pointer.
"STACK CFI 4006 .cfa: $rbp 16 + $rbp: .cfa 24 - ^\n"
// The calling function.
"FUNC 5000 1000 10 epictetus\n"
// Mark it as end of stack.
"STACK CFI INIT 5000 1000 .cfa: $rsp .ra 0\n");
// Provide some distinctive values for the caller's registers.
expected.rsp = 0x8000000080000000ULL;
expected.rip = 0x40000000c0005510ULL;
expected.rbp = 0x68995b1de4700266ULL;
expected.rbx = 0x5a5beeb38de23be8ULL;
expected.r12 = 0xed1b02e8cc0fc79cULL;
expected.r13 = 0x1d20ad8acacbe930ULL;
expected.r14 = 0xe94cffc2f7adaa28ULL;
expected.r15 = 0xb638d17d8da413b5ULL;
// By default, registers are unchanged.
raw_context = expected;
}
// Walk the stack, using stack_section as the contents of the stack
// and raw_context as the current register values. (Set
// raw_context.rsp to the stack's starting address.) Expect two
// stack frames; in the older frame, expect the callee-saves
// registers to have values matching those in 'expected'.
void CheckWalk() {
RegionFromSection();
raw_context.rsp = stack_section.start().Value();
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameAMD64 *frame0 = static_cast<StackFrameAMD64 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ("enchiridion", frame0->function_name);
EXPECT_EQ(0x40000000c0004000ULL, frame0->function_base);
StackFrameAMD64 *frame1 = static_cast<StackFrameAMD64 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP |
StackFrameAMD64::CONTEXT_VALID_RBP |
StackFrameAMD64::CONTEXT_VALID_RBX |
StackFrameAMD64::CONTEXT_VALID_R12 |
StackFrameAMD64::CONTEXT_VALID_R13 |
StackFrameAMD64::CONTEXT_VALID_R14 |
StackFrameAMD64::CONTEXT_VALID_R15),
frame1->context_validity);
EXPECT_EQ(expected.rip, frame1->context.rip);
EXPECT_EQ(expected.rsp, frame1->context.rsp);
EXPECT_EQ(expected.rbp, frame1->context.rbp);
EXPECT_EQ(expected.rbx, frame1->context.rbx);
EXPECT_EQ(expected.r12, frame1->context.r12);
EXPECT_EQ(expected.r13, frame1->context.r13);
EXPECT_EQ(expected.r14, frame1->context.r14);
EXPECT_EQ(expected.r15, frame1->context.r15);
EXPECT_EQ("epictetus", frame1->function_name);
}
// The values we expect to find for the caller's registers.
MDRawContextAMD64 expected;
};
class CFI: public CFIFixture, public Test { };
TEST_F(CFI, At4000) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x40000000c0005510ULL) // return address
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004000ULL;
CheckWalk();
}
TEST_F(CFI, At4001) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0x40000000c0005510ULL) // return address
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004001ULL;
raw_context.rbx = 0xbe0487d2f9eafe29ULL; // callee's (distinct) %rbx value
CheckWalk();
}
TEST_F(CFI, At4002) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0x40000000c0005510ULL) // return address
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004002ULL;
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
raw_context.r12 = 0xb0118de918a4bceaULL; // callee's (distinct) %r12 value
CheckWalk();
}
TEST_F(CFI, At4003) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x0e023828dffd4d81ULL) // garbage
.D64(0x1d20ad8acacbe930ULL) // saved %r13
.D64(0x319e68b49e3ace0fULL) // garbage
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0x40000000c0005510ULL) // return address
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004003ULL;
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
raw_context.r12 = 0x89d04fa804c87a43ULL; // callee's (distinct) %r12
raw_context.r13 = 0x5118e02cbdb24b03ULL; // callee's (distinct) %r13
CheckWalk();
}
// The results here should be the same as those at module offset 0x4003.
TEST_F(CFI, At4004) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x0e023828dffd4d81ULL) // garbage
.D64(0x1d20ad8acacbe930ULL) // saved %r13
.D64(0x319e68b49e3ace0fULL) // garbage
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0x40000000c0005510ULL) // return address
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004004ULL;
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
raw_context.r12 = 0x89d04fa804c87a43ULL; // callee's (distinct) %r12
raw_context.r13 = 0x5118e02cbdb24b03ULL; // callee's (distinct) %r13
CheckWalk();
}
TEST_F(CFI, At4005) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x4b516dd035745953ULL) // garbage
.D64(0x1d20ad8acacbe930ULL) // saved %r13
.D64(0xa6d445e16ae3d872ULL) // garbage
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0xaa95fa054aedfbaeULL) // garbage
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004005ULL;
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
raw_context.r12 = 0x46b1b8868891b34aULL; // callee's %r12
raw_context.r13 = 0x40000000c0005510ULL; // return address
CheckWalk();
}
TEST_F(CFI, At4006) {
Label frame0_rbp;
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x043c6dfceb91aa34ULL) // garbage
.D64(0x1d20ad8acacbe930ULL) // saved %r13
.D64(0x68995b1de4700266ULL) // saved %rbp
.Mark(&frame0_rbp) // frame pointer points here
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0xf015ee516ad89eabULL) // garbage
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004006ULL;
raw_context.rbp = frame0_rbp.Value();
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
raw_context.r12 = 0x26e007b341acfebdULL; // callee's %r12
raw_context.r13 = 0x40000000c0005510ULL; // return address
CheckWalk();
}

View File

@@ -0,0 +1,234 @@
// Copyright (c) 2010 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.
// stackwalker_arm.cc: arm-specific stackwalker.
//
// See stackwalker_arm.h for documentation.
//
// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/memory_region.h"
#include "google_breakpad/processor/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/cfi_frame_info.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
#include "processor/stackwalker_arm.h"
namespace google_breakpad {
StackwalkerARM::StackwalkerARM(const SystemInfo *system_info,
const MDRawContextARM *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver)
: Stackwalker(system_info, memory, modules, supplier, resolver),
context_(context),
context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { }
StackFrame* StackwalkerARM::GetContextFrame() {
if (!context_ || !memory_) {
BPLOG(ERROR) << "Can't get context frame without context or memory";
return NULL;
}
StackFrameARM *frame = new StackFrameARM();
// The instruction pointer is stored directly in a register (r15), so pull it
// straight out of the CPU context structure.
frame->context = *context_;
frame->context_validity = context_frame_validity_;
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
frame->instruction = frame->context.iregs[15];
return frame;
}
StackFrameARM *StackwalkerARM::GetCallerByCFIFrameInfo(
const vector<StackFrame *> &frames,
CFIFrameInfo *cfi_frame_info) {
StackFrameARM *last_frame = static_cast<StackFrameARM *>(frames.back());
static const char *register_names[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
"fps", "cpsr",
NULL
};
// Populate a dictionary with the valid register values in last_frame.
CFIFrameInfo::RegisterValueMap<u_int32_t> callee_registers;
for (int i = 0; register_names[i]; i++)
if (last_frame->context_validity & StackFrameARM::RegisterValidFlag(i))
callee_registers[register_names[i]] = last_frame->context.iregs[i];
// Use the STACK CFI data to recover the caller's register values.
CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_,
&caller_registers))
return NULL;
// Construct a new stack frame given the values the CFI recovered.
scoped_ptr<StackFrameARM> frame(new StackFrameARM());
for (int i = 0; register_names[i]; i++) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
caller_registers.find(register_names[i]);
if (entry != caller_registers.end()) {
// We recovered the value of this register; fill the context with the
// value from caller_registers.
frame->context_validity |= StackFrameARM::RegisterValidFlag(i);
frame->context.iregs[i] = entry->second;
} else if (4 <= i && i <= 11 && (last_frame->context_validity &
StackFrameARM::RegisterValidFlag(i))) {
// If the STACK CFI data doesn't mention some callee-saves register, and
// it is valid in the callee, assume the callee has not yet changed it.
// Registers r4 through r11 are callee-saves, according to the Procedure
// Call Standard for the ARM Architecture, which the Linux ABI follows.
frame->context_validity |= StackFrameARM::RegisterValidFlag(i);
frame->context.iregs[i] = last_frame->context.iregs[i];
}
}
// If the CFI doesn't recover the PC explicitly, then use .ra.
if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_PC)) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
caller_registers.find(".ra");
if (entry != caller_registers.end()) {
frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second;
}
}
// If the CFI doesn't recover the SP explicitly, then use .cfa.
if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_SP)) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
caller_registers.find(".cfa");
if (entry != caller_registers.end()) {
frame->context_validity |= StackFrameARM::CONTEXT_VALID_SP;
frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = entry->second;
}
}
// If we didn't recover the PC and the SP, then the frame isn't very useful.
static const int essentials = (StackFrameARM::CONTEXT_VALID_SP
| StackFrameARM::CONTEXT_VALID_PC);
if ((frame->context_validity & essentials) != essentials)
return NULL;
frame->trust = StackFrame::FRAME_TRUST_CFI;
return frame.release();
}
StackFrameARM *StackwalkerARM::GetCallerByStackScan(
const vector<StackFrame *> &frames) {
StackFrameARM *last_frame = static_cast<StackFrameARM *>(frames.back());
u_int32_t last_sp = last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP];
u_int32_t caller_sp, caller_pc;
if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc)) {
// No plausible return address was found.
return NULL;
}
// ScanForReturnAddress found a reasonable return address. Advance
// %sp to the location above the one where the return address was
// found.
caller_sp += 4;
// Create a new stack frame (ownership will be transferred to the caller)
// and fill it in.
StackFrameARM *frame = new StackFrameARM();
frame->trust = StackFrame::FRAME_TRUST_SCAN;
frame->context = last_frame->context;
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = caller_pc;
frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = caller_sp;
frame->context_validity = StackFrameARM::CONTEXT_VALID_PC |
StackFrameARM::CONTEXT_VALID_SP;
return frame;
}
StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack) {
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
return NULL;
}
const vector<StackFrame *> &frames = *stack->frames();
StackFrameARM *last_frame = static_cast<StackFrameARM *>(frames.back());
scoped_ptr<StackFrameARM> frame;
// See if there is DWARF call frame information covering this address.
scoped_ptr<CFIFrameInfo> cfi_frame_info(
resolver_ ? resolver_->FindCFIFrameInfo(last_frame) : NULL);
if (cfi_frame_info.get())
frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
// If CFI failed, or there wasn't CFI available, fall back
// to stack scanning.
if (!frame.get()) {
frame.reset(GetCallerByStackScan(frames));
}
// If nothing worked, tell the caller.
if (!frame.get())
return NULL;
// An instruction address of zero marks the end of the stack.
if (frame->context.iregs[MD_CONTEXT_ARM_REG_PC] == 0)
return NULL;
// If the new stack pointer is at a lower address than the old, then
// that's clearly incorrect. Treat this as end-of-stack to enforce
// progress and avoid infinite loops.
if (frame->context.iregs[MD_CONTEXT_ARM_REG_SP]
< last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP])
return NULL;
// The new frame's context's PC is the return address, which is one
// instruction past the instruction that caused us to arrive at the
// callee. Set new_frame->instruction to one less than the PC. This won't
// reference the beginning of the call instruction, but it's at least
// within it, which is sufficient to get the source line information to
// match up with the line that contains the function call. Callers that
// require the exact return address value may access
// frame->context.iregs[MD_CONTEXT_ARM_REG_PC].
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 1;
return frame.release();
}
} // namespace google_breakpad

View File

@@ -0,0 +1,99 @@
// -*- mode: C++ -*-
// Copyright (c) 2010 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.
// stackwalker_arm.h: arm-specific stackwalker.
//
// Provides stack frames given arm register context and a memory region
// corresponding to an arm stack.
//
// Author: Mark Mentovai, Ted Mielczarek
#ifndef PROCESSOR_STACKWALKER_ARM_H__
#define PROCESSOR_STACKWALKER_ARM_H__
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/stackwalker.h"
namespace google_breakpad {
class CodeModules;
class StackwalkerARM : public Stackwalker {
public:
// context is an arm context object that gives access to arm-specific
// register state corresponding to the innermost called frame to be
// included in the stack. The other arguments are passed directly through
// to the base Stackwalker constructor.
StackwalkerARM(const SystemInfo *system_info,
const MDRawContextARM *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver);
// Change the context validity mask of the frame returned by
// GetContextFrame to VALID. This is only for use by unit tests; the
// default behavior is correct for all application code.
void SetContextFrameValidity(int valid) { context_frame_validity_ = valid; }
private:
// Implementation of Stackwalker, using arm context and stack conventions.
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack *stack);
// Use cfi_frame_info (derived from STACK CFI records) to construct
// the frame that called frames.back(). The caller takes ownership
// of the returned frame. Return NULL on failure.
StackFrameARM *GetCallerByCFIFrameInfo(const vector<StackFrame *> &frames,
CFIFrameInfo *cfi_frame_info);
// Scan the stack for plausible return addresses. The caller takes ownership
// of the returned frame. Return NULL on failure.
StackFrameARM *GetCallerByStackScan(const vector<StackFrame *> &frames);
// Stores the CPU context corresponding to the youngest stack frame, to
// be returned by GetContextFrame.
const MDRawContextARM *context_;
// Validity mask for youngest stack frame. This is always
// CONTEXT_VALID_ALL in real use; it is only changeable for the sake of
// unit tests.
int context_frame_validity_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STACKWALKER_ARM_H__

View File

@@ -0,0 +1,586 @@
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// stackwalker_arm_unittest.cc: Unit tests for StackwalkerARM class.
#include <string>
#include <string.h>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/test_assembler.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/stackwalker_unittest_utils.h"
#include "processor/stackwalker_arm.h"
#include "processor/windows_frame_info.h"
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::StackFrame;
using google_breakpad::StackFrameARM;
using google_breakpad::StackwalkerARM;
using google_breakpad::SystemInfo;
using google_breakpad::WindowsFrameInfo;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using std::string;
using std::vector;
using testing::_;
using testing::Return;
using testing::SetArgumentPointee;
using testing::Test;
class StackwalkerARMFixture {
public:
StackwalkerARMFixture()
: stack_section(kLittleEndian),
// Give the two modules reasonable standard locations and names
// for tests to play with.
module1(0x40000000, 0x10000, "module1", "version1"),
module2(0x50000000, 0x10000, "module2", "version2") {
// Identify the system as a Linux system.
system_info.os = "Linux";
system_info.os_short = "linux";
system_info.os_version = "Lugubrious Labrador";
system_info.cpu = "arm";
system_info.cpu_info = "";
// Put distinctive values in the raw CPU context.
BrandContext(&raw_context);
// Create some modules with some stock debugging information.
modules.Add(&module1);
modules.Add(&module2);
// By default, none of the modules have symbol info; call
// SetModuleSymbols to override this.
EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _))
.WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
}
// Set the Breakpad symbol information that supplier should return for
// MODULE to INFO.
void SetModuleSymbols(MockCodeModule *module, const string &info) {
unsigned int buffer_size = info.size() + 1;
char *buffer = reinterpret_cast<char*>(operator new(buffer_size));
strcpy(buffer, info.c_str());
EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _))
.WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer),
Return(MockSymbolSupplier::FOUND)));
}
// Populate stack_region with the contents of stack_section. Use
// stack_section.start() as the region's starting address.
void RegionFromSection() {
string contents;
ASSERT_TRUE(stack_section.GetContents(&contents));
stack_region.Init(stack_section.start().Value(), contents);
}
// Fill RAW_CONTEXT with pseudo-random data, for round-trip checking.
void BrandContext(MDRawContextARM *raw_context) {
u_int8_t x = 173;
for (size_t i = 0; i < sizeof(*raw_context); i++)
reinterpret_cast<u_int8_t *>(raw_context)[i] = (x += 17);
}
SystemInfo system_info;
MDRawContextARM raw_context;
Section stack_section;
MockMemoryRegion stack_region;
MockCodeModule module1;
MockCodeModule module2;
MockCodeModules modules;
MockSymbolSupplier supplier;
BasicSourceLineResolver resolver;
CallStack call_stack;
const vector<StackFrame *> *frames;
};
class SanityCheck: public StackwalkerARMFixture, public Test { };
TEST_F(SanityCheck, NoResolver) {
// Since we have no call frame information, and all unwinding
// requires call frame information, the stack walk will end after
// the first frame.
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
NULL, NULL);
// This should succeed even without a resolver or supplier.
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(1U, frames->size());
StackFrameARM *frame = static_cast<StackFrameARM *>(frames->at(0));
// Check that the values from the original raw context made it
// through to the context in the stack frame.
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
class GetContextFrame: public StackwalkerARMFixture, public Test { };
TEST_F(GetContextFrame, Simple) {
// Since we have no call frame information, and all unwinding
// requires call frame information, the stack walk will end after
// the first frame.
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(1U, frames->size());
StackFrameARM *frame = static_cast<StackFrameARM *>(frames->at(0));
// Check that the values from the original raw context made it
// through to the context in the stack frame.
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
class GetCallerFrame: public StackwalkerARMFixture, public Test { };
TEST_F(GetCallerFrame, ScanWithoutSymbols) {
// When the stack walker resorts to scanning the stack,
// only addresses located within loaded modules are
// considered valid return addresses.
// Force scanning through three frames to ensure that the
// stack pointer is set properly in scan-recovered frames.
stack_section.start() = 0x80000000;
u_int32_t return_address1 = 0x50000100;
u_int32_t return_address2 = 0x50000900;
Label frame1_sp, frame2_sp;
stack_section
// frame 0
.Append(16, 0) // space
.D32(0x40090000) // junk that's not
.D32(0x60000000) // a return address
.D32(return_address1) // actual return address
// frame 1
.Mark(&frame1_sp)
.Append(16, 0) // space
.D32(0xF0000000) // more junk
.D32(0x0000000D)
.D32(return_address2) // actual return address
// frame 2
.Mark(&frame2_sp)
.Append(32, 0); // end of stack
RegionFromSection();
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(3U, frames->size());
StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
StackFrameARM::CONTEXT_VALID_SP),
frame1->context_validity);
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
StackFrameARM *frame2 = static_cast<StackFrameARM *>(frames->at(2));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust);
ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
StackFrameARM::CONTEXT_VALID_SP),
frame2->context_validity);
EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM_REG_PC]);
EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM_REG_SP]);
}
TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
// During stack scanning, if a potential return address
// is located within a loaded module that has symbols,
// it is only considered a valid return address if it
// lies within a function's bounds.
stack_section.start() = 0x80000000;
u_int32_t return_address = 0x50000200;
Label frame1_sp;
stack_section
// frame 0
.Append(16, 0) // space
.D32(0x40090000) // junk that's not
.D32(0x60000000) // a return address
.D32(0x40001000) // a couple of plausible addresses
.D32(0x5000F000) // that are not within functions
.D32(return_address) // actual return address
// frame 1
.Mark(&frame1_sp)
.Append(32, 0); // end of stack
RegionFromSection();
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40000200;
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
SetModuleSymbols(&module1,
// The youngest frame's function.
"FUNC 100 400 10 monotreme\n");
SetModuleSymbols(&module2,
// The calling frame's function.
"FUNC 100 400 10 marsupial\n");
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameARM::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
EXPECT_EQ("monotreme", frame0->function_name);
EXPECT_EQ(0x40000100, frame0->function_base);
StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
ASSERT_EQ((StackFrameARM::CONTEXT_VALID_PC |
StackFrameARM::CONTEXT_VALID_SP),
frame1->context_validity);
EXPECT_EQ(return_address, frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
EXPECT_EQ("marsupial", frame1->function_name);
EXPECT_EQ(0x50000100, frame1->function_base);
}
struct CFIFixture: public StackwalkerARMFixture {
CFIFixture() {
// Provide a bunch of STACK CFI records; we'll walk to the caller
// from every point in this series, expecting to find the same set
// of register values.
SetModuleSymbols(&module1,
// The youngest frame's function.
"FUNC 4000 1000 10 enchiridion\n"
// Initially, nothing has been pushed on the stack,
// and the return address is still in the link register.
"STACK CFI INIT 4000 100 .cfa: sp .ra: lr\n"
// Push r4, the frame pointer, and the link register.
"STACK CFI 4001 .cfa: sp 12 + r4: .cfa 12 - ^"
" r11: .cfa 8 - ^ .ra: .cfa 4 - ^\n"
// Save r4..r7 in r0..r3: verify that we populate
// the youngest frame with all the values we have.
"STACK CFI 4002 r4: r0 r5: r1 r6: r2 r7: r3\n"
// Restore r4..r7. Save the non-callee-saves register r1.
"STACK CFI 4003 .cfa: sp 16 + r1: .cfa 16 - ^"
" r4: r4 r5: r5 r6: r6 r7: r7\n"
// Move the .cfa back four bytes, to point at the return
// address, and restore the sp explicitly.
"STACK CFI 4005 .cfa: sp 12 + r1: .cfa 12 - ^"
" r11: .cfa 4 - ^ .ra: .cfa ^ sp: .cfa 4 +\n"
// Recover the PC explicitly from a new stack slot;
// provide garbage for the .ra.
"STACK CFI 4006 .cfa: sp 16 + pc: .cfa 16 - ^\n"
// The calling function.
"FUNC 5000 1000 10 epictetus\n"
// Mark it as end of stack.
"STACK CFI INIT 5000 1000 .cfa: 0 .ra: 0\n"
// A function whose CFI makes the stack pointer
// go backwards.
"FUNC 6000 1000 20 palinal\n"
"STACK CFI INIT 6000 1000 .cfa: sp 4 - .ra: lr\n"
// A function with CFI expressions that can't be
// evaluated.
"FUNC 7000 1000 20 rhetorical\n"
"STACK CFI INIT 7000 1000 .cfa: moot .ra: ambiguous\n");
// Provide some distinctive values for the caller's registers.
expected.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
expected.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
expected.iregs[4] = 0xb5d55e68;
expected.iregs[5] = 0xebd134f3;
expected.iregs[6] = 0xa31e74bc;
expected.iregs[7] = 0x2dcb16b3;
expected.iregs[8] = 0x2ada2137;
expected.iregs[9] = 0xbbbb557d;
expected.iregs[10] = 0x48bf8ca7;
expected.iregs[MD_CONTEXT_ARM_REG_FP] = 0x8112e110;
// Expect CFI to recover all callee-saves registers. Since CFI is the
// only stack frame construction technique we have, aside from the
// context frame itself, there's no way for us to have a set of valid
// registers smaller than this.
expected_validity = (StackFrameARM::CONTEXT_VALID_PC |
StackFrameARM::CONTEXT_VALID_SP |
StackFrameARM::CONTEXT_VALID_R4 |
StackFrameARM::CONTEXT_VALID_R5 |
StackFrameARM::CONTEXT_VALID_R6 |
StackFrameARM::CONTEXT_VALID_R7 |
StackFrameARM::CONTEXT_VALID_R8 |
StackFrameARM::CONTEXT_VALID_R9 |
StackFrameARM::CONTEXT_VALID_R10 |
StackFrameARM::CONTEXT_VALID_FP);
// By default, context frames provide all registers, as normal.
context_frame_validity = StackFrameARM::CONTEXT_VALID_ALL;
// By default, registers are unchanged.
raw_context = expected;
}
// Walk the stack, using stack_section as the contents of the stack
// and raw_context as the current register values. (Set the stack
// pointer to the stack's starting address.) Expect two stack
// frames; in the older frame, expect the callee-saves registers to
// have values matching those in 'expected'.
void CheckWalk() {
RegionFromSection();
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
walker.SetContextFrameValidity(context_frame_validity);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(context_frame_validity, frame0->context_validity);
EXPECT_EQ("enchiridion", frame0->function_name);
EXPECT_EQ(0x40004000U, frame0->function_base);
StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ(expected_validity, frame1->context_validity);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R1)
EXPECT_EQ(expected.iregs[1], frame1->context.iregs[1]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R4)
EXPECT_EQ(expected.iregs[4], frame1->context.iregs[4]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R5)
EXPECT_EQ(expected.iregs[5], frame1->context.iregs[5]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R6)
EXPECT_EQ(expected.iregs[6], frame1->context.iregs[6]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R7)
EXPECT_EQ(expected.iregs[7], frame1->context.iregs[7]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R8)
EXPECT_EQ(expected.iregs[8], frame1->context.iregs[8]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R9)
EXPECT_EQ(expected.iregs[9], frame1->context.iregs[9]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R10)
EXPECT_EQ(expected.iregs[10], frame1->context.iregs[10]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_FP)
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_FP],
frame1->context.iregs[MD_CONTEXT_ARM_REG_FP]);
// We would never have gotten a frame in the first place if the SP
// and PC weren't valid or ->instruction weren't set.
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_SP],
frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
frame1->instruction + 1);
EXPECT_EQ("epictetus", frame1->function_name);
}
// The values we expect to find for the caller's registers.
MDRawContextARM expected;
// The validity mask for expected.
int expected_validity;
// The validity mask to impose on the context frame.
int context_frame_validity;
};
class CFI: public CFIFixture, public Test { };
TEST_F(CFI, At4000) {
stack_section.start() = expected.iregs[MD_CONTEXT_ARM_REG_SP];
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004000;
raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
CheckWalk();
}
TEST_F(CFI, At4001) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0xb5d55e68) // saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001;
raw_context.iregs[4] = 0x635adc9f; // distinct callee r4
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
CheckWalk();
}
// As above, but unwind from a context that has only the PC and SP.
TEST_F(CFI, At4001LimitedValidity) {
context_frame_validity =
StackFrameARM::CONTEXT_VALID_PC | StackFrameARM::CONTEXT_VALID_SP;
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001;
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0xb5d55e68) // saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
expected_validity = (StackFrameARM::CONTEXT_VALID_PC
| StackFrameARM::CONTEXT_VALID_SP
| StackFrameARM::CONTEXT_VALID_FP
| StackFrameARM::CONTEXT_VALID_R4);
CheckWalk();
}
TEST_F(CFI, At4002) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0xfb81ff3d) // no longer saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004002;
raw_context.iregs[0] = 0xb5d55e68; // saved r4
raw_context.iregs[1] = 0xebd134f3; // saved r5
raw_context.iregs[2] = 0xa31e74bc; // saved r6
raw_context.iregs[3] = 0x2dcb16b3; // saved r7
raw_context.iregs[4] = 0xfdd35466; // distinct callee r4
raw_context.iregs[5] = 0xf18c946c; // distinct callee r5
raw_context.iregs[6] = 0xac2079e8; // distinct callee r6
raw_context.iregs[7] = 0xa449829f; // distinct callee r7
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
CheckWalk();
}
TEST_F(CFI, At4003) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
.D32(0xcb78040e) // no longer saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004003;
raw_context.iregs[1] = 0xfb756319; // distinct callee r1
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0x0a2857ea; // distinct callee fp
expected.iregs[1] = 0x48c8dd5a; // caller's r1
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
CheckWalk();
}
// We have no new rule at module offset 0x4004, so the results here should
// be the same as those at module offset 0x4003.
TEST_F(CFI, At4004) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
.D32(0xcb78040e) // no longer saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004004;
raw_context.iregs[1] = 0xfb756319; // distinct callee r1
expected.iregs[1] = 0x48c8dd5a; // caller's r1
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
CheckWalk();
}
// Here we move the .cfa, but provide an explicit rule to recover the SP,
// so again there should be no change in the registers recovered.
TEST_F(CFI, At4005) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
.D32(0xf013f841) // no longer saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004005;
raw_context.iregs[1] = 0xfb756319; // distinct callee r1
expected.iregs[1] = 0x48c8dd5a; // caller's r1
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
CheckWalk();
}
// Here we provide an explicit rule for the PC, and have the saved .ra be
// bogus.
TEST_F(CFI, At4006) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0x40005510) // saved pc
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
.D32(0xf013f841) // no longer saved r4
.D32(0x8112e110) // saved fp
.D32(0xf8d15783) // .ra rule recovers this, which is garbage
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004006;
raw_context.iregs[1] = 0xfb756319; // callee's r1, different from caller's
expected.iregs[1] = 0x48c8dd5a; // caller's r1
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
CheckWalk();
}
// Check that we reject rules that would cause the stack pointer to
// move in the wrong direction.
TEST_F(CFI, RejectBackwards) {
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40006000;
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(1U, frames->size());
}
// Check that we reject rules whose expressions' evaluation fails.
TEST_F(CFI, RejectBadExpressions) {
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000;
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(1U, frames->size());
}

View File

@@ -0,0 +1,146 @@
// Copyright (c) 2010 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.
// stackwalker_ppc.cc: ppc-specific stackwalker.
//
// See stackwalker_ppc.h for documentation.
//
// Author: Mark Mentovai
#include "processor/stackwalker_ppc.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/memory_region.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/logging.h"
namespace google_breakpad {
StackwalkerPPC::StackwalkerPPC(const SystemInfo *system_info,
const MDRawContextPPC *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver)
: Stackwalker(system_info, memory, modules, supplier, resolver),
context_(context) {
if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
// This implementation only covers 32-bit ppc CPUs. The limits of the
// supplied stack are invalid. Mark memory_ = NULL, which will cause
// stackwalking to fail.
BPLOG(ERROR) << "Memory out of range for stackwalking: " <<
HexString(memory_->GetBase()) << "+" <<
HexString(memory_->GetSize());
memory_ = NULL;
}
}
StackFrame* StackwalkerPPC::GetContextFrame() {
if (!context_ || !memory_) {
BPLOG(ERROR) << "Can't get context frame without context or memory";
return NULL;
}
StackFramePPC *frame = new StackFramePPC();
// The instruction pointer is stored directly in a register, so pull it
// straight out of the CPU context structure.
frame->context = *context_;
frame->context_validity = StackFramePPC::CONTEXT_VALID_ALL;
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
frame->instruction = frame->context.srr0;
return frame;
}
StackFrame* StackwalkerPPC::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack) {
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
return NULL;
}
// The instruction pointers for previous frames are saved on the stack.
// The typical ppc calling convention is for the called procedure to store
// its return address in the calling procedure's stack frame at 8(%r1),
// and to allocate its own stack frame by decrementing %r1 (the stack
// pointer) and saving the old value of %r1 at 0(%r1). Because the ppc has
// no hardware stack, there is no distinction between the stack pointer and
// frame pointer, and what is typically thought of as the frame pointer on
// an x86 is usually referred to as the stack pointer on a ppc.
StackFramePPC *last_frame = static_cast<StackFramePPC*>(
stack->frames()->back());
// A caller frame must reside higher in memory than its callee frames.
// Anything else is an error, or an indication that we've reached the
// end of the stack.
u_int32_t stack_pointer;
if (!memory_->GetMemoryAtAddress(last_frame->context.gpr[1],
&stack_pointer) ||
stack_pointer <= last_frame->context.gpr[1]) {
return NULL;
}
// Mac OS X/Darwin gives 1 as the return address from the bottom-most
// frame in a stack (a thread's entry point). I haven't found any
// documentation on this, but 0 or 1 would be bogus return addresses,
// so check for them here and return false (end of stack) when they're
// hit to avoid having a phantom frame.
u_int32_t instruction;
if (!memory_->GetMemoryAtAddress(stack_pointer + 8, &instruction) ||
instruction <= 1) {
return NULL;
}
StackFramePPC *frame = new StackFramePPC();
frame->context = last_frame->context;
frame->context.srr0 = instruction;
frame->context.gpr[1] = stack_pointer;
frame->context_validity = StackFramePPC::CONTEXT_VALID_SRR0 |
StackFramePPC::CONTEXT_VALID_GPR1;
frame->trust = StackFrame::FRAME_TRUST_FP;
// frame->context.srr0 is the return address, which is one instruction
// past the branch that caused us to arrive at the callee. Set
// frame_ppc->instruction to four less than that. Since all ppc
// instructions are 4 bytes wide, this is the address of the branch
// instruction. This allows source line information to match up with the
// line that contains a function call. Callers that require the exact
// return address value may access the context.srr0 field of StackFramePPC.
frame->instruction = frame->context.srr0 - 4;
return frame;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,79 @@
// Copyright (c) 2010 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.
// stackwalker_ppc.h: ppc-specific stackwalker.
//
// Provides stack frames given ppc register context and a memory region
// corresponding to a ppc stack.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_STACKWALKER_PPC_H__
#define PROCESSOR_STACKWALKER_PPC_H__
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/stackwalker.h"
namespace google_breakpad {
class CodeModules;
class StackwalkerPPC : public Stackwalker {
public:
// context is a ppc context object that gives access to ppc-specific
// register state corresponding to the innermost called frame to be
// included in the stack. The other arguments are passed directly through
// to the base Stackwalker constructor.
StackwalkerPPC(const SystemInfo *system_info,
const MDRawContextPPC *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver);
private:
// Implementation of Stackwalker, using ppc context (stack pointer in %r1,
// saved program counter in %srr0) and stack conventions (saved stack
// pointer at 0(%r1), return address at 8(0(%r1)).
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack *stack);
// Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame.
const MDRawContextPPC *context_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STACKWALKER_PPC_H__

View File

@@ -0,0 +1,425 @@
// 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.
// stackwalker_selftest.cc: Tests StackwalkerX86 or StackwalkerPPC using the
// running process' stack as test data, if running on an x86 or ppc and
// compiled with gcc. This test is not enabled in the "make check" suite
// by default, because certain optimizations interfere with its proper
// operation. To turn it on, configure with --enable-selftest.
//
// Optimizations that cause problems:
// - stack frame reuse. The Recursor function here calls itself with
// |return Recursor|. When the caller's frame is reused, it will cause
// CountCallerFrames to correctly return the same number of frames
// in both the caller and callee. This is considered an unexpected
// condition in the test, which expects a callee to have one more
// caller frame in the stack than its caller.
// - frame pointer omission. Even with a stackwalker that understands
// this optimization, the code to harness debug information currently
// only exists to retrieve it from minidumps, not the current process.
//
// This test can also serve as a developmental and debugging aid if
// PRINT_STACKS is defined.
//
// Author: Mark Mentovai
#include "processor/logging.h"
#if defined(__i386) && !defined(__i386__)
#define __i386__
#endif
#if defined(__sparc) && !defined(__sparc__)
#define __sparc__
#endif
#if (defined(__SUNPRO_CC) || defined(__GNUC__)) && \
(defined(__i386__) || defined(__ppc__) || defined(__sparc__))
#include <stdio.h>
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/memory_region.h"
#include "google_breakpad/processor/stack_frame.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/scoped_ptr.h"
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::MemoryRegion;
using google_breakpad::scoped_ptr;
using google_breakpad::StackFrame;
using google_breakpad::StackFramePPC;
using google_breakpad::StackFrameX86;
using google_breakpad::StackFrameSPARC;
#if defined(__i386__)
#include "processor/stackwalker_x86.h"
using google_breakpad::StackwalkerX86;
#elif defined(__ppc__)
#include "processor/stackwalker_ppc.h"
using google_breakpad::StackwalkerPPC;
#elif defined(__sparc__)
#include "processor/stackwalker_sparc.h"
using google_breakpad::StackwalkerSPARC;
#endif // __i386__ || __ppc__ || __sparc__
#define RECURSION_DEPTH 100
// A simple MemoryRegion subclass that provides direct access to this
// process' memory space by pointer.
class SelfMemoryRegion : public MemoryRegion {
public:
virtual u_int64_t GetBase() { return 0; }
virtual u_int32_t GetSize() { return 0xffffffff; }
bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) {
return GetMemoryAtAddressInternal(address, value); }
bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) {
return GetMemoryAtAddressInternal(address, value); }
bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) {
return GetMemoryAtAddressInternal(address, value); }
bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) {
return GetMemoryAtAddressInternal(address, value); }
private:
template<typename T> bool GetMemoryAtAddressInternal(u_int64_t address,
T* value) {
// Without knowing what addresses are actually mapped, just assume that
// everything low is not mapped. This helps the stackwalker catch the
// end of a stack when it tries to dereference a null or low pointer
// in an attempt to find the caller frame. Other unmapped accesses will
// cause the program to crash, but that would properly be a test failure.
if (address < 0x100)
return false;
u_int8_t* memory = 0;
*value = *reinterpret_cast<const T*>(&memory[address]);
return true;
}
};
#if defined(__GNUC__)
#if defined(__i386__)
// GetEBP returns the current value of the %ebp register. Because it's
// implemented as a function, %ebp itself contains GetEBP's frame pointer
// and not the caller's frame pointer. Dereference %ebp to obtain the
// caller's frame pointer, which the compiler-generated preamble stored
// on the stack (provided frame pointers are not being omitted.) Because
// this function depends on the compiler-generated preamble, inlining is
// disabled.
static u_int32_t GetEBP() __attribute__((noinline));
static u_int32_t GetEBP() {
u_int32_t ebp;
__asm__ __volatile__(
"movl (%%ebp), %0"
: "=a" (ebp)
);
return ebp;
}
// The caller's %esp is 8 higher than the value of %ebp in this function,
// assuming that it's not inlined and that the standard prolog is used.
// The CALL instruction places a 4-byte return address on the stack above
// the caller's %esp, and this function's prolog will save the caller's %ebp
// on the stack as well, for another 4 bytes, before storing %esp in %ebp.
static u_int32_t GetESP() __attribute__((noinline));
static u_int32_t GetESP() {
u_int32_t ebp;
__asm__ __volatile__(
"movl %%ebp, %0"
: "=a" (ebp)
);
return ebp + 8;
}
// GetEIP returns the instruction pointer identifying the next instruction
// to execute after GetEIP returns. It obtains this information from the
// stack, where it was placed by the call instruction that called GetEIP.
// This function depends on frame pointers not being omitted. It is possible
// to write a pure asm version of this routine that has no compiler-generated
// preamble and uses %esp instead of %ebp; that would function in the
// absence of frame pointers. However, the simpler approach is used here
// because GetEBP and stackwalking necessarily depends on access to frame
// pointers. Because this function depends on a call instruction and the
// compiler-generated preamble, inlining is disabled.
static u_int32_t GetEIP() __attribute__((noinline));
static u_int32_t GetEIP() {
u_int32_t eip;
__asm__ __volatile__(
"movl 4(%%ebp), %0"
: "=a" (eip)
);
return eip;
}
#elif defined(__ppc__)
// GetSP returns the current value of the %r1 register, which by convention,
// is the stack pointer on ppc. Because it's implemented as a function,
// %r1 itself contains GetSP's own stack pointer and not the caller's stack
// pointer. Dereference %r1 to obtain the caller's stack pointer, which the
// compiler-generated prolog stored on the stack. Because this function
// depends on the compiler-generated prolog, inlining is disabled.
static u_int32_t GetSP() __attribute__((noinline));
static u_int32_t GetSP() {
u_int32_t sp;
__asm__ __volatile__(
"lwz %0, 0(r1)"
: "=r" (sp)
);
return sp;
}
// GetPC returns the program counter identifying the next instruction to
// execute after GetPC returns. It obtains this information from the
// link register, where it was placed by the branch instruction that called
// GetPC. Because this function depends on the caller's use of a branch
// instruction, inlining is disabled.
static u_int32_t GetPC() __attribute__((noinline));
static u_int32_t GetPC() {
u_int32_t lr;
__asm__ __volatile__(
"mflr %0"
: "=r" (lr)
);
return lr;
}
#elif defined(__sparc__)
// GetSP returns the current value of the %sp/%o6/%g_r[14] register, which
// by convention, is the stack pointer on sparc. Because it's implemented
// as a function, %sp itself contains GetSP's own stack pointer and not
// the caller's stack pointer. Dereference to obtain the caller's stack
// pointer, which the compiler-generated prolog stored on the stack.
// Because this function depends on the compiler-generated prolog, inlining
// is disabled.
static u_int32_t GetSP() __attribute__((noinline));
static u_int32_t GetSP() {
u_int32_t sp;
__asm__ __volatile__(
"mov %%fp, %0"
: "=r" (sp)
);
return sp;
}
// GetFP returns the current value of the %fp register. Because it's
// implemented as a function, %fp itself contains GetFP's frame pointer
// and not the caller's frame pointer. Dereference %fp to obtain the
// caller's frame pointer, which the compiler-generated preamble stored
// on the stack (provided frame pointers are not being omitted.) Because
// this function depends on the compiler-generated preamble, inlining is
// disabled.
static u_int32_t GetFP() __attribute__((noinline));
static u_int32_t GetFP() {
u_int32_t fp;
__asm__ __volatile__(
"ld [%%fp+56], %0"
: "=r" (fp)
);
return fp;
}
// GetPC returns the program counter identifying the next instruction to
// execute after GetPC returns. It obtains this information from the
// link register, where it was placed by the branch instruction that called
// GetPC. Because this function depends on the caller's use of a branch
// instruction, inlining is disabled.
static u_int32_t GetPC() __attribute__((noinline));
static u_int32_t GetPC() {
u_int32_t pc;
__asm__ __volatile__(
"mov %%i7, %0"
: "=r" (pc)
);
return pc + 8;
}
#endif // __i386__ || __ppc__ || __sparc__
#elif defined(__SUNPRO_CC)
#if defined(__i386__)
extern "C" {
extern u_int32_t GetEIP();
extern u_int32_t GetEBP();
extern u_int32_t GetESP();
}
#elif defined(__sparc__)
extern "C" {
extern u_int32_t GetPC();
extern u_int32_t GetFP();
extern u_int32_t GetSP();
}
#endif // __i386__ || __sparc__
#endif // __GNUC__ || __SUNPRO_CC
// CountCallerFrames returns the number of stack frames beneath the function
// that called CountCallerFrames. Because this function's return value
// is dependent on the size of the stack beneath it, inlining is disabled,
// and any function that calls this should not be inlined either.
#if defined(__GNUC__)
static unsigned int CountCallerFrames() __attribute__((noinline));
#elif defined(__SUNPRO_CC)
static unsigned int CountCallerFrames();
#endif
static unsigned int CountCallerFrames() {
SelfMemoryRegion memory;
BasicSourceLineResolver resolver;
#if defined(__i386__)
MDRawContextX86 context = MDRawContextX86();
context.eip = GetEIP();
context.ebp = GetEBP();
context.esp = GetESP();
StackwalkerX86 stackwalker = StackwalkerX86(NULL, &context, &memory, NULL,
NULL, &resolver);
#elif defined(__ppc__)
MDRawContextPPC context = MDRawContextPPC();
context.srr0 = GetPC();
context.gpr[1] = GetSP();
StackwalkerPPC stackwalker = StackwalkerPPC(NULL, &context, &memory, NULL,
NULL, &resolver);
#elif defined(__sparc__)
MDRawContextSPARC context = MDRawContextSPARC();
context.pc = GetPC();
context.g_r[14] = GetSP();
context.g_r[30] = GetFP();
StackwalkerSPARC stackwalker = StackwalkerSPARC(NULL, &context, &memory,
NULL, NULL, &resolver);
#endif // __i386__ || __ppc__ || __sparc__
CallStack stack;
stackwalker.Walk(&stack);
#ifdef PRINT_STACKS
printf("\n");
for (unsigned int frame_index = 0;
frame_index < stack.frames()->size();
++frame_index) {
StackFrame *frame = stack.frames()->at(frame_index);
printf("frame %-3d instruction = 0x%08" PRIx64,
frame_index, frame->instruction);
#if defined(__i386__)
StackFrameX86 *frame_x86 = reinterpret_cast<StackFrameX86*>(frame);
printf(" esp = 0x%08x ebp = 0x%08x\n",
frame_x86->context.esp, frame_x86->context.ebp);
#elif defined(__ppc__)
StackFramePPC *frame_ppc = reinterpret_cast<StackFramePPC*>(frame);
printf(" gpr[1] = 0x%08x\n", frame_ppc->context.gpr[1]);
#elif defined(__sparc__)
StackFrameSPARC *frame_sparc = reinterpret_cast<StackFrameSPARC*>(frame);
printf(" sp = 0x%08x fp = 0x%08x\n",
frame_sparc->context.g_r[14], frame_sparc->context.g_r[30]);
#endif // __i386__ || __ppc__ || __sparc__
}
#endif // PRINT_STACKS
// Subtract 1 because the caller wants the number of frames beneath
// itself. Because the caller called us, subract two for our frame and its
// frame, which are included in stack.size().
return stack.frames()->size() - 2;
}
// Recursor verifies that the number stack frames beneath itself is one more
// than the number of stack frames beneath its parent. When depth frames
// have been reached, Recursor stops checking and returns success. If the
// frame count check fails at any depth, Recursor will stop and return false.
// Because this calls CountCallerFrames, inlining is disabled.
#if defined(__GNUC__)
static bool Recursor(unsigned int depth, unsigned int parent_callers)
__attribute__((noinline));
#elif defined(__SUNPRO_CC)
static bool Recursor(unsigned int depth, unsigned int parent_callers);
#endif
static bool Recursor(unsigned int depth, unsigned int parent_callers) {
unsigned int callers = CountCallerFrames();
if (callers != parent_callers + 1)
return false;
if (depth)
return Recursor(depth - 1, callers);
// depth == 0
return true;
}
// Because this calls CountCallerFrames, inlining is disabled - but because
// it's main (and nobody calls it other than the entry point), it wouldn't
// be inlined anyway.
#if defined(__GNUC__)
int main(int argc, char** argv) __attribute__((noinline));
#elif defined(__SUNPRO_CC)
int main(int argc, char** argv);
#endif
int main(int argc, char** argv) {
BPLOG_INIT(&argc, &argv);
return Recursor(RECURSION_DEPTH, CountCallerFrames()) ? 0 : 1;
}
#else
// Not i386 or ppc or sparc? We can only test stacks we know how to walk.
int main(int argc, char **argv) {
BPLOG_INIT(&argc, &argv);
// "make check" interprets an exit status of 77 to mean that the test is
// not supported.
BPLOG(ERROR) << "Selftest not supported here";
return 77;
}
#endif // (__GNUC__ || __SUNPRO_CC) && (__i386__ || __ppc__ || __sparc__)

View File

@@ -0,0 +1,111 @@
/* Copyright (c) 2007, 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.
*/
/* stackwalker_selftest_sol.s
* On Solaris, the recommeded compiler is CC, so we can not use gcc inline
* asm, use this method instead.
*
* How to compile: as -P -L -D_ASM -D_STDC -K PIC -o \
* src/processor/stackwalker_selftest_sol.o \
* src/processor/stackwalker_selftest_sol.s
*
* Author: Michael Shang
*/
#include <sys/asm_linkage.h>
#if defined(__i386)
ENTRY(GetEBP)
pushl %ebp
movl %esp,%ebp
subl $0x00000004,%esp
movl 0x00000000(%ebp),%eax
movl %eax,0xfffffffc(%ebp)
movl 0xfffffffc(%ebp),%eax
leave
ret
SET_SIZE(GetEBP)
ENTRY(GetEIP)
pushl %ebp
movl %esp,%ebp
subl $0x00000004,%esp
movl 0x00000004(%ebp),%eax
movl %eax,0xfffffffc(%ebp)
movl 0xfffffffc(%ebp),%eax
leave
ret
SET_SIZE(GetEIP)
ENTRY(GetESP)
pushl %ebp
movl %esp,%ebp
subl $0x00000004,%esp
movl %ebp,%eax
movl %eax,0xfffffffc(%ebp)
movl 0xfffffffc(%ebp),%eax
addl $0x00000008,%eax
leave
ret
SET_SIZE(GetESP)
#elif defined(__sparc)
ENTRY(GetPC)
save %sp, -120, %sp
mov %i7, %i4
inccc 8, %i4
mov %i4, %i0
ret
restore
SET_SIZE(GetPC)
ENTRY(GetSP)
save %sp, -120, %sp
mov %fp, %i4
mov %i4, %i0
ret
restore
SET_SIZE(GetSP)
ENTRY(GetFP)
save %sp, -120, %sp
ld [%fp + 56], %g1
mov %g1, %i0
ret
restore
SET_SIZE(GetFP)
#endif // __i386 || __sparc

View File

@@ -0,0 +1,139 @@
// Copyright (c) 2010 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.
// stackwalker_sparc.cc: sparc-specific stackwalker.
//
// See stackwalker_sparc.h for documentation.
//
// Author: Michael Shang
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/memory_region.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/logging.h"
#include "processor/stackwalker_sparc.h"
namespace google_breakpad {
StackwalkerSPARC::StackwalkerSPARC(const SystemInfo *system_info,
const MDRawContextSPARC *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver)
: Stackwalker(system_info, memory, modules, supplier, resolver),
context_(context) {
}
StackFrame* StackwalkerSPARC::GetContextFrame() {
if (!context_ || !memory_) {
BPLOG(ERROR) << "Can't get context frame without context or memory";
return NULL;
}
StackFrameSPARC *frame = new StackFrameSPARC();
// The instruction pointer is stored directly in a register, so pull it
// straight out of the CPU context structure.
frame->context = *context_;
frame->context_validity = StackFrameSPARC::CONTEXT_VALID_ALL;
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
frame->instruction = frame->context.pc;
return frame;
}
StackFrame* StackwalkerSPARC::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack) {
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
return NULL;
}
StackFrameSPARC *last_frame = static_cast<StackFrameSPARC*>(
stack->frames()->back());
// new: caller
// old: callee
// %fp, %i6 and g_r[30] is the same, see minidump_format.h
// %sp, %o6 and g_r[14] is the same, see minidump_format.h
// %sp_new = %fp_old
// %fp_new = *(%fp_old + 32 + 32 - 8), where the callee's %i6
// %pc_new = *(%fp_old + 32 + 32 - 4) + 8
// which is callee's %i7 plus 8
// A caller frame must reside higher in memory than its callee frames.
// Anything else is an error, or an indication that we've reached the
// end of the stack.
u_int64_t stack_pointer = last_frame->context.g_r[30];
if (stack_pointer <= last_frame->context.g_r[14]) {
return NULL;
}
u_int32_t instruction;
if (!memory_->GetMemoryAtAddress(stack_pointer + 60,
&instruction) || instruction <= 1) {
return NULL;
}
u_int32_t stack_base;
if (!memory_->GetMemoryAtAddress(stack_pointer + 56,
&stack_base) || stack_base <= 1) {
return NULL;
}
StackFrameSPARC *frame = new StackFrameSPARC();
frame->context = last_frame->context;
frame->context.g_r[14] = stack_pointer;
frame->context.g_r[30] = stack_base;
// frame->context.pc is the return address, which is 2 instruction
// past the branch that caused us to arrive at the callee, which are
// a CALL instruction then a NOP instruction.
// frame_ppc->instruction to 8 less than that. Since all sparc
// instructions are 4 bytes wide, this is the address of the branch
// instruction. This allows source line information to match up with the
// line that contains a function call. Callers that require the exact
// return address value may access the %i7/g_r[31] field of StackFrameSPARC.
frame->context.pc = instruction + 8;
frame->instruction = instruction;
frame->context_validity = StackFrameSPARC::CONTEXT_VALID_PC |
StackFrameSPARC::CONTEXT_VALID_SP |
StackFrameSPARC::CONTEXT_VALID_FP;
frame->trust = StackFrame::FRAME_TRUST_FP;
return frame;
}
} // namespace google_breakpad

View File

@@ -0,0 +1,78 @@
// Copyright (c) 2010 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.
// stackwalker_sparc.h: sparc-specific stackwalker.
//
// Provides stack frames given sparc register context and a memory region
// corresponding to an sparc stack.
//
// Author: Michael Shang
#ifndef PROCESSOR_STACKWALKER_SPARC_H__
#define PROCESSOR_STACKWALKER_SPARC_H__
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/stackwalker.h"
namespace google_breakpad {
class CodeModules;
class StackwalkerSPARC : public Stackwalker {
public:
// context is a sparc context object that gives access to sparc-specific
// register state corresponding to the innermost called frame to be
// included in the stack. The other arguments are passed directly through
// to the base Stackwalker constructor.
StackwalkerSPARC(const SystemInfo *system_info,
const MDRawContextSPARC *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver);
private:
// Implementation of Stackwalker, using sparc context (%fp, %sp, %pc) and
// stack conventions
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack *stack);
// Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame.
const MDRawContextSPARC *context_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STACKWALKER_SPARC_H__

View File

@@ -0,0 +1,180 @@
// -*- mode: C++ -*-
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// Mock classes for writing stackwalker tests, shared amongst architectures.
#ifndef PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_
#define PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_
#include <stdlib.h>
#include <string>
#include <vector>
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/code_modules.h"
#include "google_breakpad/processor/memory_region.h"
#include "google_breakpad/processor/symbol_supplier.h"
#include "google_breakpad/processor/system_info.h"
class MockMemoryRegion: public google_breakpad::MemoryRegion {
public:
MockMemoryRegion(): base_address_(0) { }
// Set this region's address and contents. If we have placed an
// instance of this class in a test fixture class, individual tests
// can use this to provide the region's contents.
void Init(u_int64_t base_address, const std::string &contents) {
base_address_ = base_address;
contents_ = contents;
}
u_int64_t GetBase() const { return base_address_; }
u_int32_t GetSize() const { return contents_.size(); }
bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
return GetMemoryLittleEndian(address, value);
}
bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
return GetMemoryLittleEndian(address, value);
}
bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
return GetMemoryLittleEndian(address, value);
}
bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
return GetMemoryLittleEndian(address, value);
}
private:
// Fetch a little-endian value from ADDRESS in contents_ whose size
// is BYTES, and store it in *VALUE. Return true on success.
template<typename ValueType>
bool GetMemoryLittleEndian(u_int64_t address, ValueType *value) const {
if (address < base_address_ ||
address - base_address_ + sizeof(ValueType) > contents_.size())
return false;
ValueType v = 0;
int start = address - base_address_;
// The loop condition is odd, but it's correct for size_t.
for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--)
v = (v << 8) | static_cast<unsigned char>(contents_[start + i]);
*value = v;
return true;
}
u_int64_t base_address_;
std::string contents_;
};
class MockCodeModule: public google_breakpad::CodeModule {
public:
MockCodeModule(u_int64_t base_address, u_int64_t size,
const std::string &code_file, const std::string &version)
: base_address_(base_address), size_(size), code_file_(code_file) { }
u_int64_t base_address() const { return base_address_; }
u_int64_t size() const { return size_; }
std::string code_file() const { return code_file_; }
std::string code_identifier() const { return code_file_; }
std::string debug_file() const { return code_file_; }
std::string debug_identifier() const { return code_file_; }
std::string version() const { return version_; }
const google_breakpad::CodeModule *Copy() const {
abort(); // Tests won't use this.
}
private:
u_int64_t base_address_;
u_int64_t size_;
std::string code_file_;
std::string version_;
};
class MockCodeModules: public google_breakpad::CodeModules {
public:
typedef google_breakpad::CodeModule CodeModule;
typedef google_breakpad::CodeModules CodeModules;
void Add(const MockCodeModule *module) {
modules_.push_back(module);
}
unsigned int module_count() const { return modules_.size(); }
const CodeModule *GetModuleForAddress(u_int64_t address) const {
for (ModuleVector::const_iterator i = modules_.begin();
i != modules_.end(); i++) {
const MockCodeModule *module = *i;
if (module->base_address() <= address &&
address - module->base_address() < module->size())
return module;
}
return NULL;
};
const CodeModule *GetMainModule() const { return modules_[0]; }
const CodeModule *GetModuleAtSequence(unsigned int sequence) const {
return modules_.at(sequence);
}
const CodeModule *GetModuleAtIndex(unsigned int index) const {
return modules_.at(index);
}
const CodeModules *Copy() const { abort(); } // Tests won't use this.
private:
typedef std::vector<const MockCodeModule *> ModuleVector;
ModuleVector modules_;
};
class MockSymbolSupplier: public google_breakpad::SymbolSupplier {
public:
typedef google_breakpad::CodeModule CodeModule;
typedef google_breakpad::SystemInfo SystemInfo;
MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule *module,
const SystemInfo *system_info,
std::string *symbol_file));
MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule *module,
const SystemInfo *system_info,
std::string *symbol_file,
std::string *symbol_data));
MOCK_METHOD4(GetCStringSymbolData, SymbolResult(const CodeModule *module,
const SystemInfo *system_info,
std::string *symbol_file,
char **symbol_data));
MOCK_METHOD1(FreeSymbolData, void(const CodeModule *module));
};
#endif // PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_

View File

@@ -0,0 +1,577 @@
// Copyright (c) 2010 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.
// stackwalker_x86.cc: x86-specific stackwalker.
//
// See stackwalker_x86.h for documentation.
//
// Author: Mark Mentovai
#include "processor/postfix_evaluator-inl.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/code_modules.h"
#include "google_breakpad/processor/memory_region.h"
#include "google_breakpad/processor/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
#include "processor/stackwalker_x86.h"
#include "processor/windows_frame_info.h"
#include "processor/cfi_frame_info.h"
namespace google_breakpad {
const StackwalkerX86::CFIWalker::RegisterSet
StackwalkerX86::cfi_register_map_[] = {
// It may seem like $eip and $esp are callee-saves, because (with Unix or
// cdecl calling conventions) the callee is responsible for having them
// restored upon return. But the callee_saves flags here really means
// that the walker should assume they're unchanged if the CFI doesn't
// mention them, which is clearly wrong for $eip and $esp.
{ "$eip", ".ra", false,
StackFrameX86::CONTEXT_VALID_EIP, &MDRawContextX86::eip },
{ "$esp", ".cfa", false,
StackFrameX86::CONTEXT_VALID_ESP, &MDRawContextX86::esp },
{ "$ebp", NULL, true,
StackFrameX86::CONTEXT_VALID_EBP, &MDRawContextX86::ebp },
{ "$eax", NULL, false,
StackFrameX86::CONTEXT_VALID_EAX, &MDRawContextX86::eax },
{ "$ebx", NULL, true,
StackFrameX86::CONTEXT_VALID_EBX, &MDRawContextX86::ebx },
{ "$ecx", NULL, false,
StackFrameX86::CONTEXT_VALID_ECX, &MDRawContextX86::ecx },
{ "$edx", NULL, false,
StackFrameX86::CONTEXT_VALID_EDX, &MDRawContextX86::edx },
{ "$esi", NULL, true,
StackFrameX86::CONTEXT_VALID_ESI, &MDRawContextX86::esi },
{ "$edi", NULL, true,
StackFrameX86::CONTEXT_VALID_EDI, &MDRawContextX86::edi },
};
StackwalkerX86::StackwalkerX86(const SystemInfo *system_info,
const MDRawContextX86 *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver)
: Stackwalker(system_info, memory, modules, supplier, resolver),
context_(context),
cfi_walker_(cfi_register_map_,
(sizeof(cfi_register_map_) / sizeof(cfi_register_map_[0]))) {
if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
// The x86 is a 32-bit CPU, the limits of the supplied stack are invalid.
// Mark memory_ = NULL, which will cause stackwalking to fail.
BPLOG(ERROR) << "Memory out of range for stackwalking: " <<
HexString(memory_->GetBase()) << "+" <<
HexString(memory_->GetSize());
memory_ = NULL;
}
}
StackFrameX86::~StackFrameX86() {
if (windows_frame_info)
delete windows_frame_info;
windows_frame_info = NULL;
if (cfi_frame_info)
delete cfi_frame_info;
cfi_frame_info = NULL;
}
StackFrame *StackwalkerX86::GetContextFrame() {
if (!context_ || !memory_) {
BPLOG(ERROR) << "Can't get context frame without context or memory";
return NULL;
}
StackFrameX86 *frame = new StackFrameX86();
// The instruction pointer is stored directly in a register, so pull it
// straight out of the CPU context structure.
frame->context = *context_;
frame->context_validity = StackFrameX86::CONTEXT_VALID_ALL;
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
frame->instruction = frame->context.eip;
return frame;
}
StackFrameX86 *StackwalkerX86::GetCallerByWindowsFrameInfo(
const vector<StackFrame *> &frames,
WindowsFrameInfo *last_frame_info) {
StackFrame::FrameTrust trust = StackFrame::FRAME_TRUST_NONE;
StackFrameX86 *last_frame = static_cast<StackFrameX86 *>(frames.back());
// Save the stack walking info we found, in case we need it later to
// find the callee of the frame we're constructing now.
last_frame->windows_frame_info = last_frame_info;
// This function only covers the full STACK WIN case. If
// last_frame_info is VALID_PARAMETER_SIZE-only, then we should
// assume the traditional frame format or use some other strategy.
if (last_frame_info->valid != WindowsFrameInfo::VALID_ALL)
return NULL;
// This stackwalker sets each frame's %esp to its value immediately prior
// to the CALL into the callee. This means that %esp points to the last
// callee argument pushed onto the stack, which may not be where %esp points
// after the callee returns. Specifically, the value is correct for the
// cdecl calling convention, but not other conventions. The cdecl
// convention requires a caller to pop its callee's arguments from the
// stack after the callee returns. This is usually accomplished by adding
// the known size of the arguments to %esp. Other calling conventions,
// including stdcall, thiscall, and fastcall, require the callee to pop any
// parameters stored on the stack before returning. This is usually
// accomplished by using the RET n instruction, which pops n bytes off
// the stack after popping the return address.
//
// Because each frame's %esp will point to a location on the stack after
// callee arguments have been PUSHed, when locating things in a stack frame
// relative to %esp, the size of the arguments to the callee need to be
// taken into account. This seems a little bit unclean, but it's better
// than the alternative, which would need to take these same things into
// account, but only for cdecl functions. With this implementation, we get
// to be agnostic about each function's calling convention. Furthermore,
// this is how Windows debugging tools work, so it means that the %esp
// values produced by this stackwalker directly correspond to the %esp
// values you'll see there.
//
// If the last frame has no callee (because it's the context frame), just
// set the callee parameter size to 0: the stack pointer can't point to
// callee arguments because there's no callee. This is correct as long
// as the context wasn't captured while arguments were being pushed for
// a function call. Note that there may be functions whose parameter sizes
// are unknown, 0 is also used in that case. When that happens, it should
// be possible to walk to the next frame without reference to %esp.
u_int32_t last_frame_callee_parameter_size = 0;
int frames_already_walked = frames.size();
if (frames_already_walked >= 2) {
const StackFrameX86 *last_frame_callee
= static_cast<StackFrameX86 *>(frames[frames_already_walked - 2]);
WindowsFrameInfo *last_frame_callee_info
= last_frame_callee->windows_frame_info;
if (last_frame_callee_info &&
(last_frame_callee_info->valid
& WindowsFrameInfo::VALID_PARAMETER_SIZE)) {
last_frame_callee_parameter_size =
last_frame_callee_info->parameter_size;
}
}
// Set up the dictionary for the PostfixEvaluator. %ebp and %esp are used
// in each program string, and their previous values are known, so set them
// here.
PostfixEvaluator<u_int32_t>::DictionaryType dictionary;
// Provide the current register values.
dictionary["$ebp"] = last_frame->context.ebp;
dictionary["$esp"] = last_frame->context.esp;
// Provide constants from the debug info for last_frame and its callee.
// .cbCalleeParams is a Breakpad extension that allows us to use the
// PostfixEvaluator engine when certain types of debugging information
// are present without having to write the constants into the program
// string as literals.
dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size;
dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size;
dictionary[".cbLocals"] = last_frame_info->local_size;
dictionary[".raSearchStart"] = last_frame->context.esp +
last_frame_callee_parameter_size +
last_frame_info->local_size +
last_frame_info->saved_register_size;
dictionary[".cbParams"] = last_frame_info->parameter_size;
// Decide what type of program string to use. The program string is in
// postfix notation and will be passed to PostfixEvaluator::Evaluate.
// Given the dictionary and the program string, it is possible to compute
// the return address and the values of other registers in the calling
// function. Because of bugs described below, the stack may need to be
// scanned for these values. The results of program string evaluation
// will be used to determine whether to scan for better values.
string program_string;
bool recover_ebp = true;
trust = StackFrame::FRAME_TRUST_CFI;
if (!last_frame_info->program_string.empty()) {
// The FPO data has its own program string, which will tell us how to
// get to the caller frame, and may even fill in the values of
// nonvolatile registers and provide pointers to local variables and
// parameters. In some cases, particularly with program strings that use
// .raSearchStart, the stack may need to be scanned afterward.
program_string = last_frame_info->program_string;
} else if (last_frame_info->allocates_base_pointer) {
// The function corresponding to the last frame doesn't use the frame
// pointer for conventional purposes, but it does allocate a new
// frame pointer and use it for its own purposes. Its callee's
// information is still accessed relative to %esp, and the previous
// value of %ebp can be recovered from a location in its stack frame,
// within the saved-register area.
//
// Functions that fall into this category use the %ebp register for
// a purpose other than the frame pointer. They restore the caller's
// %ebp before returning. These functions create their stack frame
// after a CALL by decrementing the stack pointer in an amount
// sufficient to store local variables, and then PUSHing saved
// registers onto the stack. Arguments to a callee function, if any,
// are PUSHed after that. Walking up to the caller, therefore,
// can be done solely with calculations relative to the stack pointer
// (%esp). The return address is recovered from the memory location
// above the known sizes of the callee's parameters, saved registers,
// and locals. The caller's stack pointer (the value of %esp when
// the caller executed CALL) is the location immediately above the
// saved return address. The saved value of %ebp to be restored for
// the caller is at a known location in the saved-register area of
// the stack frame.
//
// For this type of frame, MSVC 14 (from Visual Studio 8/2005) in
// link-time code generation mode (/LTCG and /GL) can generate erroneous
// debugging data. The reported size of saved registers can be 0,
// which is clearly an error because these frames must, at the very
// least, save %ebp. For this reason, in addition to those given above
// about the use of .raSearchStart, the stack may need to be scanned
// for a better return address and a better frame pointer after the
// program string is evaluated.
//
// %eip_new = *(%esp_old + callee_params + saved_regs + locals)
// %ebp_new = *(%esp_old + callee_params + saved_regs - 8)
// %esp_new = %esp_old + callee_params + saved_regs + locals + 4
program_string = "$eip .raSearchStart ^ = "
"$ebp $esp .cbCalleeParams + .cbSavedRegs + 8 - ^ = "
"$esp .raSearchStart 4 + =";
} else {
// The function corresponding to the last frame doesn't use %ebp at
// all. The callee frame is located relative to %esp.
//
// The called procedure's instruction pointer and stack pointer are
// recovered in the same way as the case above, except that no
// frame pointer (%ebp) is used at all, so it is not saved anywhere
// in the callee's stack frame and does not need to be recovered.
// Because %ebp wasn't used in the callee, whatever value it has
// is the value that it had in the caller, so it can be carried
// straight through without bringing its validity into question.
//
// Because of the use of .raSearchStart, the stack will possibly be
// examined to locate a better return address after program string
// evaluation. The stack will not be examined to locate a saved
// %ebp value, because these frames do not save (or use) %ebp.
//
// %eip_new = *(%esp_old + callee_params + saved_regs + locals)
// %esp_new = %esp_old + callee_params + saved_regs + locals + 4
// %ebp_new = %ebp_old
program_string = "$eip .raSearchStart ^ = "
"$esp .raSearchStart 4 + =";
recover_ebp = false;
}
// Now crank it out, making sure that the program string set at least the
// two required variables.
PostfixEvaluator<u_int32_t> evaluator =
PostfixEvaluator<u_int32_t>(&dictionary, memory_);
PostfixEvaluator<u_int32_t>::DictionaryValidityType dictionary_validity;
if (!evaluator.Evaluate(program_string, &dictionary_validity) ||
dictionary_validity.find("$eip") == dictionary_validity.end() ||
dictionary_validity.find("$esp") == dictionary_validity.end()) {
// Program string evaluation failed. It may be that %eip is not somewhere
// with stack frame info, and %ebp is pointing to non-stack memory, so
// our evaluation couldn't succeed. We'll scan the stack for a return
// address. This can happen if the stack is in a module for which
// we don't have symbols, and that module is compiled without a
// frame pointer.
u_int32_t location_start = last_frame->context.esp;
u_int32_t location, eip;
if (!ScanForReturnAddress(location_start, &location, &eip)) {
// if we can't find an instruction pointer even with stack scanning,
// give up.
return NULL;
}
// This seems like a reasonable return address. Since program string
// evaluation failed, use it and set %esp to the location above the
// one where the return address was found.
dictionary["$eip"] = eip;
dictionary["$esp"] = location + 4;
trust = StackFrame::FRAME_TRUST_SCAN;
}
// Since this stack frame did not use %ebp in a traditional way,
// locating the return address isn't entirely deterministic. In that
// case, the stack can be scanned to locate the return address.
//
// However, if program string evaluation resulted in both %eip and
// %ebp values of 0, trust that the end of the stack has been
// reached and don't scan for anything else.
if (dictionary["$eip"] != 0 || dictionary["$ebp"] != 0) {
int offset = 0;
// This scan can only be done if a CodeModules object is available, to
// check that candidate return addresses are in fact inside a module.
//
// TODO(mmentovai): This ignores dynamically-generated code. One possible
// solution is to check the minidump's memory map to see if the candidate
// %eip value comes from a mapped executable page, although this would
// require dumps that contain MINIDUMP_MEMORY_INFO, which the Breakpad
// client doesn't currently write (it would need to call MiniDumpWriteDump
// with the MiniDumpWithFullMemoryInfo type bit set). Even given this
// ability, older OSes (pre-XP SP2) and CPUs (pre-P4) don't enforce
// an independent execute privilege on memory pages.
u_int32_t eip = dictionary["$eip"];
if (modules_ && !modules_->GetModuleForAddress(eip)) {
// The instruction pointer at .raSearchStart was invalid, so start
// looking one 32-bit word above that location.
u_int32_t location_start = dictionary[".raSearchStart"] + 4;
u_int32_t location;
if (ScanForReturnAddress(location_start, &location, &eip)) {
// This is a better return address that what program string
// evaluation found. Use it, and set %esp to the location above the
// one where the return address was found.
dictionary["$eip"] = eip;
dictionary["$esp"] = location + 4;
offset = location - location_start;
trust = StackFrame::FRAME_TRUST_CFI_SCAN;
}
}
// When trying to recover the previous value of the frame pointer (%ebp),
// start looking at the lowest possible address in the saved-register
// area, and look at the entire saved register area, increased by the
// size of |offset| to account for additional data that may be on the
// stack. The scan is performed from the highest possible address to
// the lowest, because we expect that the function's prolog would have
// saved %ebp early.
u_int32_t ebp = dictionary["$ebp"];
u_int32_t value; // throwaway variable to check pointer validity
if (recover_ebp && !memory_->GetMemoryAtAddress(ebp, &value)) {
int fp_search_bytes = last_frame_info->saved_register_size + offset;
u_int32_t location_end = last_frame->context.esp +
last_frame_callee_parameter_size;
for (u_int32_t location = location_end + fp_search_bytes;
location >= location_end;
location -= 4) {
if (!memory_->GetMemoryAtAddress(location, &ebp))
break;
if (memory_->GetMemoryAtAddress(ebp, &value)) {
// The candidate value is a pointer to the same memory region
// (the stack). Prefer it as a recovered %ebp result.
dictionary["$ebp"] = ebp;
break;
}
}
}
}
// Create a new stack frame (ownership will be transferred to the caller)
// and fill it in.
StackFrameX86 *frame = new StackFrameX86();
frame->trust = trust;
frame->context = last_frame->context;
frame->context.eip = dictionary["$eip"];
frame->context.esp = dictionary["$esp"];
frame->context.ebp = dictionary["$ebp"];
frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP |
StackFrameX86::CONTEXT_VALID_ESP |
StackFrameX86::CONTEXT_VALID_EBP;
// These are nonvolatile (callee-save) registers, and the program string
// may have filled them in.
if (dictionary_validity.find("$ebx") != dictionary_validity.end()) {
frame->context.ebx = dictionary["$ebx"];
frame->context_validity |= StackFrameX86::CONTEXT_VALID_EBX;
}
if (dictionary_validity.find("$esi") != dictionary_validity.end()) {
frame->context.esi = dictionary["$esi"];
frame->context_validity |= StackFrameX86::CONTEXT_VALID_ESI;
}
if (dictionary_validity.find("$edi") != dictionary_validity.end()) {
frame->context.edi = dictionary["$edi"];
frame->context_validity |= StackFrameX86::CONTEXT_VALID_EDI;
}
return frame;
}
StackFrameX86 *StackwalkerX86::GetCallerByCFIFrameInfo(
const vector<StackFrame*> &frames,
CFIFrameInfo *cfi_frame_info) {
StackFrameX86 *last_frame = static_cast<StackFrameX86*>(frames.back());
last_frame->cfi_frame_info = cfi_frame_info;
scoped_ptr<StackFrameX86> frame(new StackFrameX86());
if (!cfi_walker_
.FindCallerRegisters(*memory_, *cfi_frame_info,
last_frame->context, last_frame->context_validity,
&frame->context, &frame->context_validity))
return NULL;
// Make sure we recovered all the essentials.
static const int essentials = (StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP);
if ((frame->context_validity & essentials) != essentials)
return NULL;
frame->trust = StackFrame::FRAME_TRUST_CFI;
return frame.release();
}
StackFrameX86 *StackwalkerX86::GetCallerByEBPAtBase(
const vector<StackFrame *> &frames) {
StackFrame::FrameTrust trust;
StackFrameX86 *last_frame = static_cast<StackFrameX86 *>(frames.back());
u_int32_t last_esp = last_frame->context.esp;
u_int32_t last_ebp = last_frame->context.ebp;
// Assume that the standard %ebp-using x86 calling convention is in
// use.
//
// The typical x86 calling convention, when frame pointers are present,
// is for the calling procedure to use CALL, which pushes the return
// address onto the stack and sets the instruction pointer (%eip) to
// the entry point of the called routine. The called routine then
// PUSHes the calling routine's frame pointer (%ebp) onto the stack
// before copying the stack pointer (%esp) to the frame pointer (%ebp).
// Therefore, the calling procedure's frame pointer is always available
// by dereferencing the called procedure's frame pointer, and the return
// address is always available at the memory location immediately above
// the address pointed to by the called procedure's frame pointer. The
// calling procedure's stack pointer (%esp) is 8 higher than the value
// of the called procedure's frame pointer at the time the calling
// procedure made the CALL: 4 bytes for the return address pushed by the
// CALL itself, and 4 bytes for the callee's PUSH of the caller's frame
// pointer.
//
// %eip_new = *(%ebp_old + 4)
// %esp_new = %ebp_old + 8
// %ebp_new = *(%ebp_old)
u_int32_t caller_eip, caller_esp, caller_ebp;
if (memory_->GetMemoryAtAddress(last_ebp + 4, &caller_eip) &&
memory_->GetMemoryAtAddress(last_ebp, &caller_ebp)) {
caller_esp = last_ebp + 8;
trust = StackFrame::FRAME_TRUST_FP;
} else {
// We couldn't read the memory %ebp refers to. It may be that %ebp
// is pointing to non-stack memory. We'll scan the stack for a
// return address. This can happen if last_frame is executing code
// for a module for which we don't have symbols, and that module
// is compiled without a frame pointer.
if (!ScanForReturnAddress(last_esp, &caller_esp, &caller_eip)) {
// if we can't find an instruction pointer even with stack scanning,
// give up.
return NULL;
}
// ScanForReturnAddress found a reasonable return address. Advance
// %esp to the location above the one where the return address was
// found. Assume that %ebp is unchanged.
caller_esp += 4;
caller_ebp = last_ebp;
trust = StackFrame::FRAME_TRUST_SCAN;
}
// Create a new stack frame (ownership will be transferred to the caller)
// and fill it in.
StackFrameX86 *frame = new StackFrameX86();
frame->trust = trust;
frame->context = last_frame->context;
frame->context.eip = caller_eip;
frame->context.esp = caller_esp;
frame->context.ebp = caller_ebp;
frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP |
StackFrameX86::CONTEXT_VALID_ESP |
StackFrameX86::CONTEXT_VALID_EBP;
return frame;
}
StackFrame *StackwalkerX86::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack) {
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
return NULL;
}
const vector<StackFrame *> &frames = *stack->frames();
StackFrameX86 *last_frame = static_cast<StackFrameX86 *>(frames.back());
scoped_ptr<StackFrameX86> new_frame;
// If the resolver has Windows stack walking information, use that.
WindowsFrameInfo *windows_frame_info
= resolver_ ? resolver_->FindWindowsFrameInfo(last_frame) : NULL;
if (windows_frame_info)
new_frame.reset(GetCallerByWindowsFrameInfo(frames, windows_frame_info));
// If the resolver has DWARF CFI information, use that.
if (!new_frame.get()) {
CFIFrameInfo *cfi_frame_info =
resolver_ ? resolver_->FindCFIFrameInfo(last_frame) : NULL;
if (cfi_frame_info)
new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info));
}
// Otherwise, hope that the program was using a traditional frame structure.
if (!new_frame.get())
new_frame.reset(GetCallerByEBPAtBase(frames));
// If nothing worked, tell the caller.
if (!new_frame.get())
return NULL;
// Treat an instruction address of 0 as end-of-stack.
if (new_frame->context.eip == 0)
return NULL;
// If the new stack pointer is at a lower address than the old, then
// that's clearly incorrect. Treat this as end-of-stack to enforce
// progress and avoid infinite loops.
if (new_frame->context.esp <= last_frame->context.esp)
return NULL;
// new_frame->context.eip is the return address, which is one instruction
// past the CALL that caused us to arrive at the callee. Set
// new_frame->instruction to one less than that. This won't reference the
// beginning of the CALL instruction, but it's guaranteed to be within
// the CALL, which is sufficient to get the source line information to
// match up with the line that contains a function call. Callers that
// require the exact return address value may access the context.eip
// field of StackFrameX86.
new_frame->instruction = new_frame->context.eip - 1;
return new_frame.release();
}
} // namespace google_breakpad

View File

@@ -0,0 +1,114 @@
// -*- mode: c++ -*-
// Copyright (c) 2010 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.
// stackwalker_x86.h: x86-specific stackwalker.
//
// Provides stack frames given x86 register context and a memory region
// corresponding to an x86 stack.
//
// Author: Mark Mentovai
#ifndef PROCESSOR_STACKWALKER_X86_H__
#define PROCESSOR_STACKWALKER_X86_H__
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/stackwalker.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/cfi_frame_info.h"
namespace google_breakpad {
class CodeModules;
class StackwalkerX86 : public Stackwalker {
public:
// context is an x86 context object that gives access to x86-specific
// register state corresponding to the innermost called frame to be
// included in the stack. The other arguments are passed directly through
// to the base Stackwalker constructor.
StackwalkerX86(const SystemInfo *system_info,
const MDRawContextX86 *context,
MemoryRegion *memory,
const CodeModules *modules,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver);
private:
// A STACK CFI-driven frame walker for the X86.
typedef SimpleCFIWalker<u_int32_t, MDRawContextX86> CFIWalker;
// Implementation of Stackwalker, using x86 context (%ebp, %esp, %eip) and
// stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or
// alternate conventions as guided by any WindowsFrameInfo available for the
// code in question.).
virtual StackFrame *GetContextFrame();
virtual StackFrame *GetCallerFrame(const CallStack *stack);
// Use windows_frame_info (derived from STACK WIN and FUNC records)
// to construct the frame that called frames.back(). The caller
// takes ownership of the returned frame. Return NULL on failure.
StackFrameX86 *GetCallerByWindowsFrameInfo(
const vector<StackFrame*> &frames,
WindowsFrameInfo *windows_frame_info);
// Use cfi_frame_info (derived from STACK CFI records) to construct
// the frame that called frames.back(). The caller takes ownership
// of the returned frame. Return NULL on failure.
StackFrameX86 *GetCallerByCFIFrameInfo(const vector<StackFrame*> &frames,
CFIFrameInfo *cfi_frame_info);
// Assuming a traditional frame layout --- where the caller's %ebp
// has been pushed just after the return address and the callee's
// %ebp points to the saved %ebp --- construct the frame that called
// frames.back(). The caller takes ownership of the returned frame.
// Return NULL on failure.
StackFrameX86 *GetCallerByEBPAtBase(const vector<StackFrame*> &frames);
// Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame.
const MDRawContextX86 *context_;
// Our register map, for cfi_walker_.
static const CFIWalker::RegisterSet cfi_register_map_[];
// Our CFI frame walker.
const CFIWalker cfi_walker_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STACKWALKER_X86_H__

View File

@@ -0,0 +1,973 @@
// Copyright (c) 2010, 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.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// stackwalker_x86_unittest.cc: Unit tests for StackwalkerX86 class.
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/test_assembler.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/stackwalker_unittest_utils.h"
#include "processor/stackwalker_x86.h"
#include "processor/windows_frame_info.h"
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::StackFrame;
using google_breakpad::StackFrameX86;
using google_breakpad::StackwalkerX86;
using google_breakpad::SystemInfo;
using google_breakpad::WindowsFrameInfo;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using std::string;
using std::vector;
using testing::_;
using testing::Return;
using testing::SetArgumentPointee;
using testing::Test;
class StackwalkerX86Fixture {
public:
StackwalkerX86Fixture()
: stack_section(kLittleEndian),
// Give the two modules reasonable standard locations and names
// for tests to play with.
module1(0x40000000, 0x10000, "module1", "version1"),
module2(0x50000000, 0x10000, "module2", "version2") {
// Identify the system as a Linux system.
system_info.os = "Linux";
system_info.os_short = "linux";
system_info.os_version = "Salacious Skink";
system_info.cpu = "x86";
system_info.cpu_info = "";
// Put distinctive values in the raw CPU context.
BrandContext(&raw_context);
// Create some modules with some stock debugging information.
modules.Add(&module1);
modules.Add(&module2);
// By default, none of the modules have symbol info; call
// SetModuleSymbols to override this.
EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _))
.WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
}
// Set the Breakpad symbol information that supplier should return for
// MODULE to INFO.
void SetModuleSymbols(MockCodeModule *module, const string &info) {
unsigned int buffer_size = info.size() + 1;
char *buffer = reinterpret_cast<char*>(operator new(buffer_size));
strcpy(buffer, info.c_str());
EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _))
.WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer),
Return(MockSymbolSupplier::FOUND)));
}
// Populate stack_region with the contents of stack_section. Use
// stack_section.start() as the region's starting address.
void RegionFromSection() {
string contents;
ASSERT_TRUE(stack_section.GetContents(&contents));
stack_region.Init(stack_section.start().Value(), contents);
}
// Fill RAW_CONTEXT with pseudo-random data, for round-trip checking.
void BrandContext(MDRawContextX86 *raw_context) {
u_int8_t x = 173;
for (size_t i = 0; i < sizeof(*raw_context); i++)
reinterpret_cast<u_int8_t *>(raw_context)[i] = (x += 17);
}
SystemInfo system_info;
MDRawContextX86 raw_context;
Section stack_section;
MockMemoryRegion stack_region;
MockCodeModule module1;
MockCodeModule module2;
MockCodeModules modules;
MockSymbolSupplier supplier;
BasicSourceLineResolver resolver;
CallStack call_stack;
const vector<StackFrame *> *frames;
};
class SanityCheck: public StackwalkerX86Fixture, public Test { };
TEST_F(SanityCheck, NoResolver) {
stack_section.start() = 0x80000000;
stack_section.D32(0).D32(0); // end-of-stack marker
RegionFromSection();
raw_context.eip = 0x40000200;
raw_context.ebp = 0x80000000;
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
NULL, NULL);
// This should succeed, even without a resolver or supplier.
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
// Check that the values from the original raw context made it
// through to the context in the stack frame.
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
class GetContextFrame: public StackwalkerX86Fixture, public Test { };
TEST_F(GetContextFrame, Simple) {
stack_section.start() = 0x80000000;
stack_section.D32(0).D32(0); // end-of-stack marker
RegionFromSection();
raw_context.eip = 0x40000200;
raw_context.ebp = 0x80000000;
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
// Check that the values from the original raw context made it
// through to the context in the stack frame.
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
class GetCallerFrame: public StackwalkerX86Fixture, public Test { };
// Walk a traditional frame. A traditional frame saves the caller's
// %ebp just below the return address, and has its own %ebp pointing
// at the saved %ebp.
TEST_F(GetCallerFrame, Traditional) {
stack_section.start() = 0x80000000;
Label frame0_ebp, frame1_ebp;
stack_section
.Append(12, 0) // frame 0: space
.Mark(&frame0_ebp) // frame 0 %ebp points here
.D32(frame1_ebp) // frame 0: saved %ebp
.D32(0x40008679) // frame 0: return address
.Append(8, 0) // frame 1: space
.Mark(&frame1_ebp) // frame 1 %ebp points here
.D32(0) // frame 1: saved %ebp (stack end)
.D32(0); // frame 1: return address (stack end)
RegionFromSection();
raw_context.eip = 0x4000c7a5;
raw_context.esp = stack_section.start().Value();
raw_context.ebp = frame0_ebp.Value();
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
EXPECT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000c7a5U, frame0->instruction);
EXPECT_EQ(0x4000c7a5U, frame0->context.eip);
EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
EXPECT_EQ(NULL, frame0->windows_frame_info);
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x40008679U, frame1->instruction + 1);
EXPECT_EQ(0x40008679U, frame1->context.eip);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
// Walk a traditional frame, but use a bogus %ebp value, forcing a scan
// of the stack for something that looks like a return address.
TEST_F(GetCallerFrame, TraditionalScan) {
stack_section.start() = 0x80000000;
Label frame1_ebp;
stack_section
// frame 0
.D32(0xf065dc76) // locals area:
.D32(0x46ee2167) // garbage that doesn't look like
.D32(0xbab023ec) // a return address
.D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan)
.D32(0x4000129d) // return address
// frame 1
.Append(8, 0) // space
.Mark(&frame1_ebp) // %ebp points here
.D32(0) // saved %ebp (stack end)
.D32(0); // return address (stack end)
RegionFromSection();
raw_context.eip = 0x4000f49d;
raw_context.esp = stack_section.start().Value();
// Make the frame pointer bogus, to make the stackwalker scan the stack
// for something that looks like a return address.
raw_context.ebp = 0xd43eed6e;
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000f49dU, frame0->instruction);
EXPECT_EQ(0x4000f49dU, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0xd43eed6eU, frame0->context.ebp);
EXPECT_EQ(NULL, frame0->windows_frame_info);
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the
// walker does not actually fetch the EBP after a scan (forcing the
// next frame to be scanned as well). But let's grandfather the existing
// behavior in for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x4000129dU, frame1->instruction + 1);
EXPECT_EQ(0x4000129dU, frame1->context.eip);
EXPECT_EQ(0x80000014U, frame1->context.esp);
EXPECT_EQ(0xd43eed6eU, frame1->context.ebp);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
// Force scanning for a return address a long way down the stack
TEST_F(GetCallerFrame, TraditionalScanLongWay) {
stack_section.start() = 0x80000000;
Label frame1_ebp;
stack_section
// frame 0
.D32(0xf065dc76) // locals area:
.D32(0x46ee2167) // garbage that doesn't look like
.D32(0xbab023ec) // a return address
.Append(20 * 4, 0) // a bunch of space
.D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan)
.D32(0x4000129d) // return address
// frame 1
.Append(8, 0) // space
.Mark(&frame1_ebp) // %ebp points here
.D32(0) // saved %ebp (stack end)
.D32(0); // return address (stack end)
RegionFromSection();
raw_context.eip = 0x4000f49d;
raw_context.esp = stack_section.start().Value();
// Make the frame pointer bogus, to make the stackwalker scan the stack
// for something that looks like a return address.
raw_context.ebp = 0xd43eed6e;
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000f49dU, frame0->instruction);
EXPECT_EQ(0x4000f49dU, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0xd43eed6eU, frame0->context.ebp);
EXPECT_EQ(NULL, frame0->windows_frame_info);
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the
// walker does not actually fetch the EBP after a scan (forcing the
// next frame to be scanned as well). But let's grandfather the existing
// behavior in for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x4000129dU, frame1->instruction + 1);
EXPECT_EQ(0x4000129dU, frame1->context.eip);
EXPECT_EQ(0x80000064U, frame1->context.esp);
EXPECT_EQ(0xd43eed6eU, frame1->context.ebp);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
// Use Windows frame data (a "STACK WIN 4" record, from a
// FrameTypeFrameData DIA record) to walk a stack frame.
TEST_F(GetCallerFrame, WindowsFrameData) {
SetModuleSymbols(&module1,
"STACK WIN 4 aa85 176 0 0 4 10 4 0 1"
" $T2 $esp .cbSavedRegs + ="
" $T0 .raSearchStart ="
" $eip $T0 ^ ="
" $esp $T0 4 + ="
" $ebx $T2 4 - ^ ="
" $edi $T2 8 - ^ ="
" $esi $T2 12 - ^ ="
" $ebp $T2 16 - ^ =\n");
Label frame1_esp, frame1_ebp;
stack_section.start() = 0x80000000;
stack_section
// frame 0
.D32(frame1_ebp) // saved regs: %ebp
.D32(0xa7120d1a) // %esi
.D32(0x630891be) // %edi
.D32(0x9068a878) // %ebx
.D32(0xa08ea45f) // locals: unused
.D32(0x40001350) // return address
// frame 1
.Mark(&frame1_esp)
.Append(12, 0) // empty space
.Mark(&frame1_ebp)
.D32(0) // saved %ebp (stack end)
.D32(0); // saved %eip (stack end)
RegionFromSection();
raw_context.eip = 0x4000aa85;
raw_context.esp = stack_section.start().Value();
raw_context.ebp = 0xf052c1de; // should not be needed to walk frame
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000aa85U, frame0->instruction);
EXPECT_EQ(0x4000aa85U, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0xf052c1deU, frame0->context.ebp);
EXPECT_TRUE(frame0->windows_frame_info != NULL);
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP
| StackFrameX86::CONTEXT_VALID_EBX
| StackFrameX86::CONTEXT_VALID_ESI
| StackFrameX86::CONTEXT_VALID_EDI),
frame1->context_validity);
EXPECT_EQ(0x40001350U, frame1->instruction + 1);
EXPECT_EQ(0x40001350U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(0x9068a878U, frame1->context.ebx);
EXPECT_EQ(0xa7120d1aU, frame1->context.esi);
EXPECT_EQ(0x630891beU, frame1->context.edi);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
// Use Windows frame data (a "STACK WIN 4" record, from a
// FrameTypeFrameData DIA record) to walk a frame, and depend on the
// parameter size from the callee as well.
TEST_F(GetCallerFrame, WindowsFrameDataParameterSize) {
SetModuleSymbols(&module1, "FUNC 1000 100 c module1::wheedle\n");
SetModuleSymbols(&module2,
// Note bogus parameter size in FUNC record; the stack walker
// should prefer the STACK WIN record, and see '4' below.
"FUNC aa85 176 beef module2::whine\n"
"STACK WIN 4 aa85 176 0 0 4 10 4 0 1"
" $T2 $esp .cbLocals + .cbSavedRegs + ="
" $T0 .raSearchStart ="
" $eip $T0 ^ ="
" $esp $T0 4 + ="
" $ebp $T0 20 - ^ ="
" $ebx $T0 8 - ^ =\n");
Label frame0_esp, frame0_ebp;
Label frame1_esp;
Label frame2_esp, frame2_ebp;
stack_section.start() = 0x80000000;
stack_section
// frame 0, in module1::wheedle. Traditional frame.
.Mark(&frame0_esp)
.Append(16, 0) // frame space
.Mark(&frame0_ebp)
.D32(0x6fa902e0) // saved %ebp. Not a frame pointer.
.D32(0x5000aa95) // return address, in module2::whine
// frame 1, in module2::whine. FrameData frame.
.Mark(&frame1_esp)
.D32(0xbaa0cb7a) // argument 3 passed to module1::wheedle
.D32(0xbdc92f9f) // argument 2
.D32(0x0b1d8442) // argument 1
.D32(frame2_ebp) // saved %ebp
.D32(0xb1b90a15) // unused
.D32(0xf18e072d) // unused
.D32(0x2558c7f3) // saved %ebx
.D32(0x0365e25e) // unused
.D32(0x2a179e38) // return address; $T0 points here
// frame 2, in no module
.Mark(&frame2_esp)
.Append(12, 0) // empty space
.Mark(&frame2_ebp)
.D32(0) // saved %ebp (stack end)
.D32(0); // saved %eip (stack end)
RegionFromSection();
raw_context.eip = 0x40001004; // in module1::wheedle
raw_context.esp = stack_section.start().Value();
raw_context.ebp = frame0_ebp.Value();
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(3U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40001004U, frame0->instruction);
EXPECT_EQ(0x40001004U, frame0->context.eip);
EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
EXPECT_EQ(&module1, frame0->module);
EXPECT_EQ("module1::wheedle", frame0->function_name);
EXPECT_EQ(0x40001000U, frame0->function_base);
// The FUNC record for module1::wheedle should have produced a
// WindowsFrameInfo structure with only the parameter size valid.
ASSERT_TRUE(frame0->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE,
frame0->windows_frame_info->valid);
EXPECT_EQ(12U, frame0->windows_frame_info->parameter_size);
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x5000aa95U, frame1->instruction + 1);
EXPECT_EQ(0x5000aa95U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(0x6fa902e0U, frame1->context.ebp);
EXPECT_EQ(&module2, frame1->module);
EXPECT_EQ("module2::whine", frame1->function_name);
EXPECT_EQ(0x5000aa85U, frame1->function_base);
ASSERT_TRUE(frame1->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid);
// This should not see the 0xbeef parameter size from the FUNC
// record, but should instead see the STACK WIN record.
EXPECT_EQ(4U, frame1->windows_frame_info->parameter_size);
StackFrameX86 *frame2 = static_cast<StackFrameX86 *>(frames->at(2));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame2->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP
| StackFrameX86::CONTEXT_VALID_EBX),
frame2->context_validity);
EXPECT_EQ(0x2a179e38U, frame2->instruction + 1);
EXPECT_EQ(0x2a179e38U, frame2->context.eip);
EXPECT_EQ(frame2_esp.Value(), frame2->context.esp);
EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp);
EXPECT_EQ(0x2558c7f3U, frame2->context.ebx);
EXPECT_EQ(NULL, frame2->module);
EXPECT_EQ(NULL, frame2->windows_frame_info);
}
// Use Windows frame data (a "STACK WIN 4" record, from a
// FrameTypeFrameData DIA record) to walk a stack frame, where the
// expression fails to yield both an $eip and an $ebp value, and the stack
// walker must scan.
TEST_F(GetCallerFrame, WindowsFrameDataScan) {
SetModuleSymbols(&module1,
"STACK WIN 4 c8c 111 0 0 4 10 4 0 1 bad program string\n");
// Mark frame 1's PC as the end of the stack.
SetModuleSymbols(&module2,
"FUNC 7c38 accf 0 module2::function\n"
"STACK WIN 4 7c38 accf 0 0 4 10 4 0 1 $eip 0 = $ebp 0 =\n");
Label frame1_esp;
stack_section.start() = 0x80000000;
stack_section
// frame 0
.Append(16, 0x2a) // unused, garbage
.D32(0x50007ce9) // return address
// frame 1
.Mark(&frame1_esp)
.Append(8, 0); // empty space
RegionFromSection();
raw_context.eip = 0x40000c9c;
raw_context.esp = stack_section.start().Value();
raw_context.ebp = 0x2ae314cd; // should not be needed to walk frame
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40000c9cU, frame0->instruction);
EXPECT_EQ(0x40000c9cU, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(0x2ae314cdU, frame0->context.ebp);
EXPECT_TRUE(frame0->windows_frame_info != NULL);
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the walker
// does not actually fetch the EBP after a scan (forcing the next frame
// to be scanned as well). But let's grandfather the existing behavior in
// for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x50007ce9U, frame1->instruction + 1);
EXPECT_EQ(0x50007ce9U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_TRUE(frame1->windows_frame_info != NULL);
}
// Use Windows frame data (a "STACK WIN 4" record, from a
// FrameTypeFrameData DIA record) to walk a stack frame, where the
// expression yields an $eip that falls outside of any module, and the
// stack walker must scan.
TEST_F(GetCallerFrame, WindowsFrameDataBadEIPScan) {
SetModuleSymbols(&module1,
"STACK WIN 4 6e6 e7 0 0 0 8 4 0 1"
// A traditional frame, actually.
" $eip $ebp 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =\n");
// Mark frame 1's PC as the end of the stack.
SetModuleSymbols(&module2,
"FUNC cfdb 8406 0 module2::function\n"
"STACK WIN 4 cfdb 8406 0 0 0 0 0 0 1 $eip 0 = $ebp 0 =\n");
stack_section.start() = 0x80000000;
// In this stack, the context's %ebp is pointing at the wrong place, so
// the stack walker needs to scan to find the return address, and then
// scan again to find the caller's saved %ebp.
Label frame0_ebp, frame1_ebp, frame1_esp;
stack_section
// frame 0
.Append(8, 0x2a) // garbage
.Mark(&frame0_ebp) // frame 0 %ebp points here, but should point
// at *** below
// The STACK WIN record says that the following two values are
// frame 1's saved %ebp and return address, but the %ebp is wrong;
// they're garbage. The stack walker will scan for the right values.
.D32(0x3d937b2b) // alleged to be frame 1's saved %ebp
.D32(0x17847f5b) // alleged to be frame 1's return address
.D32(frame1_ebp) // frame 1's real saved %ebp; scan will find
.D32(0x2b2b2b2b) // first word of realigned register save area
// *** frame 0 %ebp ought to be pointing here
.D32(0x2c2c2c2c) // realigned locals area
.D32(0x5000d000) // frame 1's real saved %eip; scan will find
// Frame 1, in module2::function. The STACK WIN record describes
// this as the oldest frame, without referring to its contents, so
// we needn't to provide any actual data here.
.Mark(&frame1_esp)
.Mark(&frame1_ebp) // frame 1 %ebp points here
// A dummy value for frame 1's %ebp to point at. The scan recognizes the
// saved %ebp because it points to a valid word in the stack memory region.
.D32(0x2d2d2d2d);
RegionFromSection();
raw_context.eip = 0x40000700;
raw_context.esp = stack_section.start().Value();
raw_context.ebp = frame0_ebp.Value();
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40000700U, frame0->instruction);
EXPECT_EQ(0x40000700U, frame0->context.eip);
EXPECT_EQ(stack_section.start().Value(), frame0->context.esp);
EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp);
EXPECT_TRUE(frame0->windows_frame_info != NULL);
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI_SCAN, frame1->trust);
// I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the
// walker does not actually fetch the EBP after a scan (forcing the
// next frame to be scanned as well). But let's grandfather the existing
// behavior in for now.
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x5000d000U, frame1->instruction + 1);
EXPECT_EQ(0x5000d000U, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_TRUE(frame1->windows_frame_info != NULL);
}
// Use Windows FrameTypeFPO data to walk a stack frame for a function that
// does not modify %ebp from the value it had in the caller.
TEST_F(GetCallerFrame, WindowsFPOUnchangedEBP) {
SetModuleSymbols(&module1,
// Note bogus parameter size in FUNC record; the walker
// should prefer the STACK WIN record, and see the '8' below.
"FUNC e8a8 100 feeb module1::discombobulated\n"
"STACK WIN 0 e8a8 100 0 0 8 4 10 0 0 0\n");
Label frame0_esp;
Label frame1_esp, frame1_ebp;
stack_section.start() = 0x80000000;
stack_section
// frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame.
.Mark(&frame0_esp)
// no outgoing parameters; this is the youngest frame.
.D32(0x7c521352) // four bytes of saved registers
.Append(0x10, 0x42) // local area
.D32(0x40009b5b) // return address, in module1, no function
// frame 1, in module1, no function.
.Mark(&frame1_esp)
.D32(0xf60ea7fc) // junk
.Mark(&frame1_ebp)
.D32(0) // saved %ebp (stack end)
.D32(0); // saved %eip (stack end)
RegionFromSection();
raw_context.eip = 0x4000e8b8; // in module1::whine
raw_context.esp = stack_section.start().Value();
// Frame pointer unchanged from caller.
raw_context.ebp = frame1_ebp.Value();
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x4000e8b8U, frame0->instruction);
EXPECT_EQ(0x4000e8b8U, frame0->context.eip);
EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp); // unchanged from caller
EXPECT_EQ(&module1, frame0->module);
EXPECT_EQ("module1::discombobulated", frame0->function_name);
EXPECT_EQ(0x4000e8a8U, frame0->function_base);
// The STACK WIN record for module1::discombobulated should have
// produced a fully populated WindowsFrameInfo structure.
ASSERT_TRUE(frame0->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
EXPECT_EQ(0x10U, frame0->windows_frame_info->local_size);
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x40009b5bU, frame1->instruction + 1);
EXPECT_EQ(0x40009b5bU, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(&module1, frame1->module);
EXPECT_EQ("", frame1->function_name);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
// Use Windows FrameTypeFPO data to walk a stack frame for a function
// that uses %ebp for its own purposes, saving the value it had in the
// caller in the standard place in the saved register area.
TEST_F(GetCallerFrame, WindowsFPOUsedEBP) {
SetModuleSymbols(&module1,
// Note bogus parameter size in FUNC record; the walker
// should prefer the STACK WIN record, and see the '8' below.
"FUNC 9aa8 e6 abbe module1::RaisedByTheAliens\n"
"STACK WIN 0 9aa8 e6 a 0 10 8 4 0 0 1\n");
Label frame0_esp;
Label frame1_esp, frame1_ebp;
stack_section.start() = 0x80000000;
stack_section
// frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame.
.Mark(&frame0_esp)
// no outgoing parameters; this is the youngest frame.
.D32(frame1_ebp) // saved register area: saved %ebp
.D32(0xb68bd5f9) // saved register area: something else
.D32(0xd25d05fc) // local area
.D32(0x4000debe) // return address, in module1, no function
// frame 1, in module1, no function.
.Mark(&frame1_esp)
.D32(0xf0c9a974) // junk
.Mark(&frame1_ebp)
.D32(0) // saved %ebp (stack end)
.D32(0); // saved %eip (stack end)
RegionFromSection();
raw_context.eip = 0x40009ab8; // in module1::RaisedByTheAliens
raw_context.esp = stack_section.start().Value();
// RaisedByTheAliens uses %ebp for its own mysterious purposes.
raw_context.ebp = 0xecbdd1a5;
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ(0x40009ab8U, frame0->instruction);
EXPECT_EQ(0x40009ab8U, frame0->context.eip);
EXPECT_EQ(frame0_esp.Value(), frame0->context.esp);
EXPECT_EQ(0xecbdd1a5, frame0->context.ebp);
EXPECT_EQ(&module1, frame0->module);
EXPECT_EQ("module1::RaisedByTheAliens", frame0->function_name);
EXPECT_EQ(0x40009aa8U, frame0->function_base);
// The STACK WIN record for module1::RaisedByTheAliens should have
// produced a fully populated WindowsFrameInfo structure.
ASSERT_TRUE(frame0->windows_frame_info != NULL);
EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid);
EXPECT_EQ("", frame0->windows_frame_info->program_string);
EXPECT_TRUE(frame0->windows_frame_info->allocates_base_pointer);
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP
| StackFrameX86::CONTEXT_VALID_ESP
| StackFrameX86::CONTEXT_VALID_EBP),
frame1->context_validity);
EXPECT_EQ(0x4000debeU, frame1->instruction + 1);
EXPECT_EQ(0x4000debeU, frame1->context.eip);
EXPECT_EQ(frame1_esp.Value(), frame1->context.esp);
EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp);
EXPECT_EQ(&module1, frame1->module);
EXPECT_EQ("", frame1->function_name);
EXPECT_EQ(NULL, frame1->windows_frame_info);
}
struct CFIFixture: public StackwalkerX86Fixture {
CFIFixture() {
// Provide a bunch of STACK CFI records; individual tests walk to the
// caller from every point in this series, expecting to find the same
// set of register values.
SetModuleSymbols(&module1,
// The youngest frame's function.
"FUNC 4000 1000 10 enchiridion\n"
// Initially, just a return address.
"STACK CFI INIT 4000 100 .cfa: $esp 4 + .ra: .cfa 4 - ^\n"
// Push %ebx.
"STACK CFI 4001 .cfa: $esp 8 + $ebx: .cfa 8 - ^\n"
// Move %esi into %ebx. Weird, but permitted.
"STACK CFI 4002 $esi: $ebx\n"
// Allocate frame space, and save %edi.
"STACK CFI 4003 .cfa: $esp 20 + $edi: .cfa 16 - ^\n"
// Put the return address in %edi.
"STACK CFI 4005 .ra: $edi\n"
// Save %ebp, and use it as a frame pointer.
"STACK CFI 4006 .cfa: $ebp 8 + $ebp: .cfa 12 - ^\n"
// The calling function.
"FUNC 5000 1000 10 epictetus\n"
// Mark it as end of stack.
"STACK CFI INIT 5000 1000 .cfa: $esp .ra 0\n");
// Provide some distinctive values for the caller's registers.
expected.esp = 0x80000000;
expected.eip = 0x40005510;
expected.ebp = 0xc0d4aab9;
expected.ebx = 0x60f20ce6;
expected.esi = 0x53d1379d;
expected.edi = 0xafbae234;
// By default, registers are unchanged.
raw_context = expected;
}
// Walk the stack, using stack_section as the contents of the stack
// and raw_context as the current register values. (Set
// raw_context.esp to the stack's starting address.) Expect two
// stack frames; in the older frame, expect the callee-saves
// registers to have values matching those in 'expected'.
void CheckWalk() {
RegionFromSection();
raw_context.esp = stack_section.start().Value();
StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameX86 *frame0 = static_cast<StackFrameX86 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ("enchiridion", frame0->function_name);
EXPECT_EQ(0x40004000U, frame0->function_base);
ASSERT_TRUE(frame0->windows_frame_info != NULL);
ASSERT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE,
frame0->windows_frame_info->valid);
ASSERT_TRUE(frame0->cfi_frame_info != NULL);
StackFrameX86 *frame1 = static_cast<StackFrameX86 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP |
StackFrameX86::CONTEXT_VALID_ESP |
StackFrameX86::CONTEXT_VALID_EBP |
StackFrameX86::CONTEXT_VALID_EBX |
StackFrameX86::CONTEXT_VALID_ESI |
StackFrameX86::CONTEXT_VALID_EDI),
frame1->context_validity);
EXPECT_EQ(expected.eip, frame1->context.eip);
EXPECT_EQ(expected.esp, frame1->context.esp);
EXPECT_EQ(expected.ebp, frame1->context.ebp);
EXPECT_EQ(expected.ebx, frame1->context.ebx);
EXPECT_EQ(expected.esi, frame1->context.esi);
EXPECT_EQ(expected.edi, frame1->context.edi);
EXPECT_EQ("epictetus", frame1->function_name);
}
// The values the stack walker should find for the caller's registers.
MDRawContextX86 expected;
};
class CFI: public CFIFixture, public Test { };
TEST_F(CFI, At4000) {
Label frame1_esp = expected.esp;
stack_section
.D32(0x40005510) // return address
.Mark(&frame1_esp); // This effectively sets stack_section.start().
raw_context.eip = 0x40004000;
CheckWalk();
}
TEST_F(CFI, At4001) {
Label frame1_esp = expected.esp;
stack_section
.D32(0x60f20ce6) // saved %ebx
.D32(0x40005510) // return address
.Mark(&frame1_esp); // This effectively sets stack_section.start().
raw_context.eip = 0x40004001;
raw_context.ebx = 0x91aa9a8b; // callee's %ebx value
CheckWalk();
}
TEST_F(CFI, At4002) {
Label frame1_esp = expected.esp;
stack_section
.D32(0x60f20ce6) // saved %ebx
.D32(0x40005510) // return address
.Mark(&frame1_esp); // This effectively sets stack_section.start().
raw_context.eip = 0x40004002;
raw_context.ebx = 0x53d1379d; // saved %esi
raw_context.esi = 0xa5c790ed; // callee's %esi value
CheckWalk();
}
TEST_F(CFI, At4003) {
Label frame1_esp = expected.esp;
stack_section
.D32(0x56ec3db7) // garbage
.D32(0xafbae234) // saved %edi
.D32(0x53d67131) // garbage
.D32(0x60f20ce6) // saved %ebx
.D32(0x40005510) // return address
.Mark(&frame1_esp); // This effectively sets stack_section.start().
raw_context.eip = 0x40004003;
raw_context.ebx = 0x53d1379d; // saved %esi
raw_context.esi = 0xa97f229d; // callee's %esi
raw_context.edi = 0xb05cc997; // callee's %edi
CheckWalk();
}
// The results here should be the same as those at module offset
// 0x4003.
TEST_F(CFI, At4004) {
Label frame1_esp = expected.esp;
stack_section
.D32(0xe29782c2) // garbage
.D32(0xafbae234) // saved %edi
.D32(0x5ba29ce9) // garbage
.D32(0x60f20ce6) // saved %ebx
.D32(0x40005510) // return address
.Mark(&frame1_esp); // This effectively sets stack_section.start().
raw_context.eip = 0x40004004;
raw_context.ebx = 0x53d1379d; // saved %esi
raw_context.esi = 0x0fb7dc4e; // callee's %esi
raw_context.edi = 0x993b4280; // callee's %edi
CheckWalk();
}
TEST_F(CFI, At4005) {
Label frame1_esp = expected.esp;
stack_section
.D32(0xe29782c2) // garbage
.D32(0xafbae234) // saved %edi
.D32(0x5ba29ce9) // garbage
.D32(0x60f20ce6) // saved %ebx
.D32(0x8036cc02) // garbage
.Mark(&frame1_esp); // This effectively sets stack_section.start().
raw_context.eip = 0x40004005;
raw_context.ebx = 0x53d1379d; // saved %esi
raw_context.esi = 0x0fb7dc4e; // callee's %esi
raw_context.edi = 0x40005510; // return address
CheckWalk();
}
TEST_F(CFI, At4006) {
Label frame0_ebp;
Label frame1_esp = expected.esp;
stack_section
.D32(0xdcdd25cd) // garbage
.D32(0xafbae234) // saved %edi
.D32(0xc0d4aab9) // saved %ebp
.Mark(&frame0_ebp) // frame pointer points here
.D32(0x60f20ce6) // saved %ebx
.D32(0x8036cc02) // garbage
.Mark(&frame1_esp); // This effectively sets stack_section.start().
raw_context.eip = 0x40004006;
raw_context.ebp = frame0_ebp.Value();
raw_context.ebx = 0x53d1379d; // saved %esi
raw_context.esi = 0x743833c9; // callee's %esi
raw_context.edi = 0x40005510; // return address
CheckWalk();
}

View File

@@ -0,0 +1,71 @@
// Copyright (c) 2010, 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.
// static_address_map-inl.h: StaticAddressMap implementation.
//
// See static_address_map.h for documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_STATIC_ADDRESS_MAP_INL_H__
#define PROCESSOR_STATIC_ADDRESS_MAP_INL_H__
#include "processor/static_address_map.h"
#include "processor/logging.h"
namespace google_breakpad {
template<typename AddressType, typename EntryType>
bool StaticAddressMap<AddressType, EntryType>::Retrieve(
const AddressType &address,
const EntryType *&entry, AddressType *entry_address) const {
// 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.GetValuePtr();
// Make sure AddressType is a copyable basic type
if (entry_address)
*entry_address = iterator.GetKey();
return true;
}
} // namespace google_breakpad
#endif // PROCESSOR_STATIC_ADDRESS_MAP_INL_H__

View File

@@ -0,0 +1,78 @@
// Copyright (c) 2010, 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.
// static_address_map.h: StaticAddressMap.
//
// StaticAddressMap is a wrapper class of StaticMap, just as AddressMap wraps
// std::map. StaticAddressMap provides read-only Retrieve() operation, similar
// as AddressMap. However, the difference between StaticAddressMap and
// AddressMap is that StaticAddressMap does not support dynamic operation
// Store() due to the static nature of the underlying StaticMap.
//
// See address_map.h for reference.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_STATIC_ADDRESS_MAP_H__
#define PROCESSOR_STATIC_ADDRESS_MAP_H__
#include "processor/static_map-inl.h"
namespace google_breakpad {
// AddressType MUST be a basic type, e.g.: integer types etc
// EntryType could be a complex type, so we retrieve its pointer instead.
template<typename AddressType, typename EntryType>
class StaticAddressMap {
public:
StaticAddressMap(): map_() { }
explicit StaticAddressMap(const char *map_data): map_(map_data) { }
// Locates the entry stored at the highest address less than or equal to
// the address argument. If there is no such range, returns false. The
// entry is returned in entry, which is a required argument. If
// entry_address is not NULL, it will be set to the address that the entry
// was stored at.
bool Retrieve(const AddressType &address,
const EntryType *&entry, AddressType *entry_address) const;
private:
friend class ModuleComparer;
// Convenience types.
typedef StaticAddressMap* SelfPtr;
typedef StaticMap<AddressType, EntryType> AddressToEntryMap;
typedef typename AddressToEntryMap::const_iterator MapConstIterator;
AddressToEntryMap map_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STATIC_ADDRESS_MAP_H__

View File

@@ -0,0 +1,235 @@
// Copyright (c) 2010, 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.
// static_address_map_unittest.cc: Unit tests for StaticAddressMap.
//
// Author: Siyang Xie (lambxsy@google.com)
#include <climits>
#include <cstdlib>
#include <ctime>
#include <string>
#include <iostream>
#include <sstream>
#include "breakpad_googletest_includes.h"
#include "processor/address_map-inl.h"
#include "processor/static_address_map-inl.h"
#include "processor/simple_serializer-inl.h"
#include "map_serializers-inl.h"
typedef google_breakpad::StaticAddressMap<int, char> TestMap;
typedef google_breakpad::AddressMap<int, std::string> AddrMap;
class TestStaticAddressMap : public ::testing::Test {
protected:
void SetUp() {
for (int testcase = 0; testcase < kNumberTestCases; ++testcase) {
testdata[testcase] = new int[testsize[testcase]];
}
// Test data set0: NULL (empty map)
// Test data set1: single element.
testdata[1][0] = 10;
// Test data set2: six elements.
const int tempdata[] = {5, 10, 14, 15, 16, 20};
for (int i = 0; i < testsize[2]; ++i)
testdata[2][i] = tempdata[i];
// Test data set3:
srand(time(NULL));
for (int i = 0; i < testsize[3]; ++i)
testdata[3][i] = rand();
// Setup maps.
std::stringstream sstream;
for (int testcase = 0; testcase < kNumberTestCases; ++testcase) {
for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
sstream.clear();
sstream << "test " << testdata[testcase][data_item];
addr_map[testcase].Store(testdata[testcase][data_item], sstream.str());
}
map_data[testcase] = serializer.Serialize(addr_map[testcase], NULL);
test_map[testcase] = TestMap(map_data[testcase]);
}
}
void TearDown() {
for (int i = 0; i < kNumberTestCases; ++i) {
delete [] map_data[i];
delete [] testdata[i];
}
}
void CompareRetrieveResult(int testcase, int target) {
int address;
int address_test;
std::string entry;
std::string entry_test;
const char *entry_cstring = NULL;
bool found;
bool found_test;
found = addr_map[testcase].Retrieve(target, &entry, &address);
found_test =
test_map[testcase].Retrieve(target, entry_cstring, &address_test);
ASSERT_EQ(found, found_test);
if (found && found_test) {
ASSERT_EQ(address, address_test);
entry_test = entry_cstring;
ASSERT_EQ(entry, entry_test);
}
}
void RetrieveTester(int testcase) {
int target;
target = INT_MIN;
CompareRetrieveResult(testcase, target);
target = INT_MAX;
CompareRetrieveResult(testcase, target);
srand(time(0));
for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
// Retrive (aka, search) for target address and compare results from
// AddressMap and StaticAddressMap.
// First, assign the search target to be one of original testdata that is
// known to exist in the map.
target = testdata[testcase][data_item];
CompareRetrieveResult(testcase, target);
// Then, add +2 / -1 bias to target value, in order to test searching for
// a target address not stored in the map.
target -= 1;
CompareRetrieveResult(testcase, target);
target += 3;
CompareRetrieveResult(testcase, target);
// Repeatedly test searching for random target addresses.
target = rand();
CompareRetrieveResult(testcase, target);
}
}
// Test data sets:
static const int kNumberTestCases = 4;
static const int testsize[];
int *testdata[kNumberTestCases];
AddrMap addr_map[kNumberTestCases];
TestMap test_map[kNumberTestCases];
char *map_data[kNumberTestCases];
google_breakpad::AddressMapSerializer<int, std::string> serializer;
};
const int TestStaticAddressMap::testsize[] = {0, 1, 6, 1000};
TEST_F(TestStaticAddressMap, TestEmptyMap) {
int testcase = 0;
int target;
target = INT_MIN;
CompareRetrieveResult(testcase, target);
target = INT_MAX;
CompareRetrieveResult(testcase, target);
for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
target = testdata[testcase][data_item];
CompareRetrieveResult(testcase, target);
target -= 1;
CompareRetrieveResult(testcase, target);
target += 3;
CompareRetrieveResult(testcase, target);
target = rand();
CompareRetrieveResult(testcase, target);
}
}
TEST_F(TestStaticAddressMap, TestOneElementMap) {
int testcase = 1;
int target;
target = INT_MIN;
CompareRetrieveResult(testcase, target);
target = INT_MAX;
CompareRetrieveResult(testcase, target);
for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
target = testdata[testcase][data_item];
CompareRetrieveResult(testcase, target);
target -= 1;
CompareRetrieveResult(testcase, target);
target += 3;
CompareRetrieveResult(testcase, target);
target = rand();
CompareRetrieveResult(testcase, target);
}
}
TEST_F(TestStaticAddressMap, TestSixElementsMap) {
int testcase = 2;
int target;
target = INT_MIN;
CompareRetrieveResult(testcase, target);
target = INT_MAX;
CompareRetrieveResult(testcase, target);
for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
target = testdata[testcase][data_item];
CompareRetrieveResult(testcase, target);
target -= 1;
CompareRetrieveResult(testcase, target);
target += 3;
CompareRetrieveResult(testcase, target);
target = rand();
CompareRetrieveResult(testcase, target);
}
}
TEST_F(TestStaticAddressMap, Test1000RandomElementsMap) {
int testcase = 3;
int target;
target = INT_MIN;
CompareRetrieveResult(testcase, target);
target = INT_MAX;
CompareRetrieveResult(testcase, target);
for (int data_item = 0; data_item < testsize[testcase]; ++data_item) {
target = testdata[testcase][data_item];
CompareRetrieveResult(testcase, target);
target -= 1;
CompareRetrieveResult(testcase, target);
target += 3;
CompareRetrieveResult(testcase, target);
target = rand();
CompareRetrieveResult(testcase, target);
}
}
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2010, 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.
// static_contained_range_map-inl.h: Hierarchically-organized range map,
// i.e., StaticContainedRangeMap implementation.
//
// See static_contained_range_map.h for documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_STATIC_CONTAINED_RANGE_MAP_INL_H__
#define PROCESSOR_STATIC_CONTAINED_RANGE_MAP_INL_H__
#include "processor/static_contained_range_map.h"
#include "processor/logging.h"
namespace google_breakpad {
template<typename AddressType, typename EntryType>
StaticContainedRangeMap<AddressType, EntryType>::StaticContainedRangeMap(
const char *base)
: base_(*(reinterpret_cast<const AddressType*>(base))),
entry_size_(*(reinterpret_cast<const u_int32_t*>(base + sizeof(base_)))),
entry_ptr_(reinterpret_cast<const EntryType *>(
base + sizeof(base_) + sizeof(entry_size_))),
map_(base + sizeof(base_) + sizeof(entry_size_) + entry_size_) {
if (entry_size_ == 0)
entry_ptr_ = NULL;
}
template<typename AddressType, typename EntryType>
bool StaticContainedRangeMap<AddressType, EntryType>::RetrieveRange(
const AddressType &address, const EntryType *&entry) const {
// Get an iterator to the child range whose high address is equal to or
// greater than the supplied address. If the supplied address is higher
// than all of the high addresses in the range, then this range does not
// contain a child at address, so return false. If the supplied address
// is lower than the base address of the child range, then it is not within
// the child range, so return false.
MapConstIterator iterator = map_.lower_bound(address);
if (iterator == map_.end())
return false;
const char *memory_child =
reinterpret_cast<const char*>(iterator.GetValuePtr());
StaticContainedRangeMap child_map(memory_child);
if (address < child_map.base_)
return false;
// The child in iterator->second contains the specified address. Find out
// if it has a more-specific descendant that also contains it. If it does,
// it will set |entry| appropriately. If not, set |entry| to the child.
if (!child_map.RetrieveRange(address, entry))
entry = child_map.entry_ptr_;
return true;
}
} // namespace google_breakpad
#endif // PROCESSOR_STATIC_CONTAINED_RANGE_MAP_INL_H__

View File

@@ -0,0 +1,96 @@
// Copyright (c) 2010, 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.
// static_contained_range_map.h: StaticContainedRangeMap.
//
// StaticContainedRangeMap is similar to ContainedRangeMap. However,
// StaticContainedRangeMap wraps a StaticMap instead of std::map, and does not
// support dynamic operations like StoreRange(...).
// StaticContainedRangeMap provides same RetrieveRange(...) interfaces as
// ContainedRangeMap.
//
// Please see contained_range_map.h for more documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_STATIC_CONTAINED_RANGE_MAP_H__
#define PROCESSOR_STATIC_CONTAINED_RANGE_MAP_H__
#include "processor/static_map-inl.h"
namespace google_breakpad {
template<typename AddressType, typename EntryType>
class StaticContainedRangeMap {
public:
StaticContainedRangeMap(): base_(), entry_size_(), entry_ptr_(), map_() { }
explicit StaticContainedRangeMap(const char *base);
// Retrieves the most specific (smallest) descendant range encompassing
// the specified address. This method will only return entries held by
// child ranges, and not the entry contained by |this|. This is necessary
// to support a sparsely-populated root range. If no descendant range
// encompasses the address, returns false.
bool RetrieveRange(const AddressType &address, const EntryType *&entry) const;
private:
friend class ModuleComparer;
// AddressToRangeMap stores pointers. This makes reparenting simpler in
// StoreRange, because it doesn't need to copy entire objects.
typedef StaticContainedRangeMap* SelfPtr;
typedef
StaticMap<AddressType, StaticContainedRangeMap> AddressToRangeMap;
typedef typename AddressToRangeMap::const_iterator MapConstIterator;
// The base address of this range. The high address does not need to
// be stored, because it is used as the key to an object in its parent's
// map, and all ContainedRangeMaps except for the root range are contained
// within maps. The root range does not actually contain an entry, so its
// base_ field is meaningless, and the fact that it has no parent and thus
// no key is unimportant. For this reason, the base_ field should only be
// is accessed on child ContainedRangeMap objects, and never on |this|.
AddressType base_;
// The entry corresponding to this range. The root range does not
// actually contain an entry, so its entry_ field is meaningless. For
// this reason, the entry_ field should only be accessed on child
// ContainedRangeMap objects, and never on |this|.
u_int32_t entry_size_;
const EntryType *entry_ptr_;
// The map containing child ranges, keyed by each child range's high
// address. This is a pointer to avoid allocating map structures for
// leaf nodes, where they are not needed.
AddressToRangeMap map_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STATIC_CONTAINED_RANGE_MAP_H__

View File

@@ -0,0 +1,321 @@
// Copyright (c) 2010, 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.
// static_contained_range_map_unittest.cc: Unit tests for
// StaticContainedRangeMap.
//
// Author: Siyang Xie (lambxsy@google.com)
#include "breakpad_googletest_includes.h"
#include "processor/contained_range_map-inl.h"
#include "processor/static_contained_range_map-inl.h"
#include "processor/simple_serializer-inl.h"
#include "processor/map_serializers-inl.h"
#include "processor/scoped_ptr.h"
#include "processor/logging.h"
namespace {
typedef google_breakpad::ContainedRangeMap<unsigned int, int> CRMMap;
typedef google_breakpad::StaticContainedRangeMap<unsigned int, int> TestMap;
// Each element in test_data contains the expected result when calling
// RetrieveRange on an address.
const int test_data[] = {
0, // 0
0, // 1
0, // 2
0, // 3
0, // 4
0, // 5
0, // 6
0, // 7
9, // 8
7, // 9
1, // 10
5, // 11
6, // 12
6, // 13
6, // 14
6, // 15
6, // 16
6, // 17
6, // 18
5, // 19
7, // 20
8, // 21
0, // 22
0, // 23
0, // 24
0, // 25
0, // 26
0, // 27
0, // 28
0, // 29
10, // 30
10, // 31
10, // 32
11, // 33
11, // 34
11, // 35
0, // 36
0, // 37
0, // 38
0, // 39
14, // 40
14, // 41
14, // 42
14, // 43
15, // 44
15, // 45
15, // 46
15, // 47
0, // 48
0, // 49
19, // 50
18, // 51
18, // 52
18, // 53
18, // 54
18, // 55
18, // 56
18, // 57
18, // 58
20, // 59
21, // 60
25, // 61
26, // 62
26, // 63
26, // 64
26, // 65
26, // 66
26, // 67
24, // 68
22, // 69
30, // 70
30, // 71
30, // 72
30, // 73
31, // 74
31, // 75
30, // 76
32, // 77
32, // 78
30, // 79
34, // 80
35, // 81
36, // 82
39, // 83
38, // 84
37, // 85
43, // 86
44, // 87
41, // 88
45, // 89
42, // 90
0, // 91
0, // 92
0, // 93
0, // 94
0, // 95
0, // 96
0, // 97
0, // 98
0 // 99
};
} // namespace
namespace google_breakpad {
class TestStaticCRMMap : public ::testing::Test {
protected:
void SetUp();
// A referrence map for testing StaticCRMMap.
google_breakpad::ContainedRangeMap<unsigned int, int> crm_map_;
// Static version of crm_map using serialized data of crm_map.
// The goal of testing is to make sure TestMap provides same results for
// lookup operation(s) as CRMMap does.
google_breakpad::StaticContainedRangeMap<unsigned int, int> test_map_;
google_breakpad::ContainedRangeMapSerializer<unsigned int, int> serializer_;
scoped_array<char> serialized_data_;
};
void TestStaticCRMMap::SetUp() {
// First, do the StoreRange tests. This validates the containment
// rules.
// We confirm the referrence map correctly stores data during setup.
ASSERT_TRUE (crm_map_.StoreRange(10, 10, 1));
ASSERT_FALSE(crm_map_.StoreRange(10, 10, 2)); // exactly equal to 1
ASSERT_FALSE(crm_map_.StoreRange(11, 10, 3)); // begins inside 1 and extends up
ASSERT_FALSE(crm_map_.StoreRange( 9, 10, 4)); // begins below 1 and ends inside
ASSERT_TRUE (crm_map_.StoreRange(11, 9, 5)); // contained by existing
ASSERT_TRUE (crm_map_.StoreRange(12, 7, 6));
ASSERT_TRUE (crm_map_.StoreRange( 9, 12, 7)); // contains existing
ASSERT_TRUE (crm_map_.StoreRange( 9, 13, 8));
ASSERT_TRUE (crm_map_.StoreRange( 8, 14, 9));
ASSERT_TRUE (crm_map_.StoreRange(30, 3, 10));
ASSERT_TRUE (crm_map_.StoreRange(33, 3, 11));
ASSERT_TRUE (crm_map_.StoreRange(30, 6, 12)); // storable but totally masked
ASSERT_TRUE (crm_map_.StoreRange(40, 8, 13)); // will be totally masked
ASSERT_TRUE (crm_map_.StoreRange(40, 4, 14));
ASSERT_TRUE (crm_map_.StoreRange(44, 4, 15));
ASSERT_FALSE(crm_map_.StoreRange(32, 10, 16)); // begins in #10, ends in #14
ASSERT_FALSE(crm_map_.StoreRange(50, 0, 17)); // zero length
ASSERT_TRUE (crm_map_.StoreRange(50, 10, 18));
ASSERT_TRUE (crm_map_.StoreRange(50, 1, 19));
ASSERT_TRUE (crm_map_.StoreRange(59, 1, 20));
ASSERT_TRUE (crm_map_.StoreRange(60, 1, 21));
ASSERT_TRUE (crm_map_.StoreRange(69, 1, 22));
ASSERT_TRUE (crm_map_.StoreRange(60, 10, 23));
ASSERT_TRUE (crm_map_.StoreRange(68, 1, 24));
ASSERT_TRUE (crm_map_.StoreRange(61, 1, 25));
ASSERT_TRUE (crm_map_.StoreRange(61, 8, 26));
ASSERT_FALSE(crm_map_.StoreRange(59, 9, 27));
ASSERT_FALSE(crm_map_.StoreRange(59, 10, 28));
ASSERT_FALSE(crm_map_.StoreRange(59, 11, 29));
ASSERT_TRUE (crm_map_.StoreRange(70, 10, 30));
ASSERT_TRUE (crm_map_.StoreRange(74, 2, 31));
ASSERT_TRUE (crm_map_.StoreRange(77, 2, 32));
ASSERT_FALSE(crm_map_.StoreRange(72, 6, 33));
ASSERT_TRUE (crm_map_.StoreRange(80, 3, 34));
ASSERT_TRUE (crm_map_.StoreRange(81, 1, 35));
ASSERT_TRUE (crm_map_.StoreRange(82, 1, 36));
ASSERT_TRUE (crm_map_.StoreRange(83, 3, 37));
ASSERT_TRUE (crm_map_.StoreRange(84, 1, 38));
ASSERT_TRUE (crm_map_.StoreRange(83, 1, 39));
ASSERT_TRUE (crm_map_.StoreRange(86, 5, 40));
ASSERT_TRUE (crm_map_.StoreRange(88, 1, 41));
ASSERT_TRUE (crm_map_.StoreRange(90, 1, 42));
ASSERT_TRUE (crm_map_.StoreRange(86, 1, 43));
ASSERT_TRUE (crm_map_.StoreRange(87, 1, 44));
ASSERT_TRUE (crm_map_.StoreRange(89, 1, 45));
ASSERT_TRUE (crm_map_.StoreRange(87, 4, 46));
ASSERT_TRUE (crm_map_.StoreRange(87, 3, 47));
ASSERT_FALSE(crm_map_.StoreRange(86, 2, 48));
// Serialize crm_map to generate serialized data.
unsigned int size;
serialized_data_.reset(serializer_.Serialize(&crm_map_, &size));
BPLOG(INFO) << "Serialized data size: " << size << " Bytes.";
// Construct test_map_ from serialized data.
test_map_ = TestMap(serialized_data_.get());
}
TEST_F(TestStaticCRMMap, TestEmptyMap) {
CRMMap empty_crm_map;
unsigned int size;
scoped_array<char> serialized_data;
serialized_data.reset(serializer_.Serialize(&empty_crm_map, &size));
scoped_ptr<TestMap> test_map(new TestMap(serialized_data.get()));
const unsigned int kCorrectSizeForEmptyMap = 16;
ASSERT_EQ(kCorrectSizeForEmptyMap, size);
const int *entry_test;
ASSERT_FALSE(test_map->RetrieveRange(-1, entry_test));
ASSERT_FALSE(test_map->RetrieveRange(0, entry_test));
ASSERT_FALSE(test_map->RetrieveRange(10, entry_test));
}
TEST_F(TestStaticCRMMap, TestSingleElementMap) {
CRMMap crm_map;
// Test on one element:
int entry = 1;
crm_map.StoreRange(10, 10, entry);
unsigned int size;
scoped_array<char> serialized_data;
serialized_data.reset(serializer_.Serialize(&crm_map, &size));
scoped_ptr<TestMap> test_map(new TestMap(serialized_data.get()));
const unsigned int kCorrectSizeForSingleElementMap = 40;
ASSERT_EQ(kCorrectSizeForSingleElementMap, size);
const int *entry_test;
ASSERT_FALSE(test_map->RetrieveRange(-1, entry_test));
ASSERT_FALSE(test_map->RetrieveRange(0, entry_test));
ASSERT_TRUE(test_map->RetrieveRange(10, entry_test));
ASSERT_EQ(*entry_test, entry);
ASSERT_TRUE(test_map->RetrieveRange(13, entry_test));
ASSERT_EQ(*entry_test, entry);
}
TEST_F(TestStaticCRMMap, RunTestData) {
unsigned int test_high = sizeof(test_data) / sizeof(test_data[0]);
// Now, do the RetrieveRange tests. This further validates that the
// objects were stored properly and that retrieval returns the correct
// object.
// If GENERATE_TEST_DATA is defined, instead of the retrieval tests, a
// new test_data array will be printed. Exercise caution when doing this.
// Be sure to verify the results manually!
#ifdef GENERATE_TEST_DATA
printf(" const int test_data[] = {\n");
#endif // GENERATE_TEST_DATA
for (unsigned int address = 0; address < test_high; ++address) {
const int *entryptr;
int value = 0;
if (test_map_.RetrieveRange(address, entryptr))
value = *entryptr;
#ifndef GENERATE_TEST_DATA
// Don't use ASSERT inside the loop because it won't show the failed
// |address|, and the line number will always be the same. That makes
// it difficult to figure out which test failed.
EXPECT_EQ(value, test_data[address]) << "FAIL: retrieve address "
<< address;
#else // !GENERATE_TEST_DATA
printf(" %d%c%s // %d\n", value,
address == test_high - 1 ? ' ' : ',',
value < 10 ? " " : "",
address);
#endif // !GENERATE_TEST_DATA
}
#ifdef GENERATE_TEST_DATA
printf(" };\n");
#endif // GENERATE_TEST_DATA
}
} // namespace google_breakpad
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,176 @@
// Copyright 2010 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.
// static_map-inl.h: StaticMap implementation.
//
// See static_map.h for documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_STATIC_MAP_INL_H__
#define PROCESSOR_STATIC_MAP_INL_H__
#include "processor/static_map.h"
#include "processor/static_map_iterator-inl.h"
#include "processor/logging.h"
namespace google_breakpad {
template<typename Key, typename Value, typename Compare>
StaticMap<Key, Value, Compare>::StaticMap(const char* raw_data)
: raw_data_(raw_data),
compare_() {
// First 4 Bytes store the number of nodes.
num_nodes_ = *(reinterpret_cast<const u_int32_t*>(raw_data_));
offsets_ = reinterpret_cast<const u_int32_t*>(
raw_data_ + sizeof(num_nodes_));
keys_ = reinterpret_cast<const Key*>(
raw_data_ + (1 + num_nodes_) * sizeof(u_int32_t));
}
// find(), lower_bound() and upper_bound() implement binary search algorithm.
template<typename Key, typename Value, typename Compare>
StaticMapIterator<Key, Value, Compare>
StaticMap<Key, Value, Compare>::find(const Key &key) const {
int begin = 0;
int end = num_nodes_;
int middle;
int compare_result;
while (begin < end) {
middle = begin + (end - begin) / 2;
compare_result = compare_(key, GetKeyAtIndex(middle));
if (compare_result == 0)
return IteratorAtIndex(middle);
if (compare_result < 0) {
end = middle;
} else {
begin = middle + 1;
}
}
return this->end();
}
template<typename Key, typename Value, typename Compare>
StaticMapIterator<Key, Value, Compare>
StaticMap<Key, Value, Compare>::lower_bound(const Key &key) const {
int begin = 0;
int end = num_nodes_;
int middle;
int comp_result;
while (begin < end) {
middle = begin + (end - begin) / 2;
comp_result = compare_(key, GetKeyAtIndex(middle));
if (comp_result == 0)
return IteratorAtIndex(middle);
if (comp_result < 0) {
end = middle;
} else {
begin = middle + 1;
}
}
return IteratorAtIndex(begin);
}
template<typename Key, typename Value, typename Compare>
StaticMapIterator<Key, Value, Compare>
StaticMap<Key, Value, Compare>::upper_bound(const Key &key) const {
int begin = 0;
int end = num_nodes_;
int middle;
int compare_result;
while (begin < end) {
middle = begin + (end - begin) / 2;
compare_result = compare_(key, GetKeyAtIndex(middle));
if (compare_result == 0)
return IteratorAtIndex(middle + 1);
if (compare_result < 0) {
end = middle;
} else {
begin = middle + 1;
}
}
return IteratorAtIndex(begin);
}
template<typename Key, typename Value, typename Compare>
bool StaticMap<Key, Value, Compare>::ValidateInMemoryStructure() const {
// check the number of nodes is non-negative:
if (!raw_data_) return false;
int32_t num_nodes = *(reinterpret_cast<const int32_t*>(raw_data_));
if (num_nodes < 0) {
BPLOG(INFO) << "StaticMap check failed: negative number of nodes";
return false;
}
int node_index = 0;
if (num_nodes_) {
u_int64_t first_offset = sizeof(int32_t) * (num_nodes_ + 1)
+ sizeof(Key) * num_nodes_;
// Num_nodes_ is too large.
if (first_offset > 0xffffffffUL) {
BPLOG(INFO) << "StaticMap check failed: size exceeds limit";
return false;
}
if (offsets_[node_index] != static_cast<u_int32_t>(first_offset)) {
BPLOG(INFO) << "StaticMap check failed: first node offset is incorrect";
return false;
}
}
for (node_index = 1; node_index < num_nodes_; ++node_index) {
// Check offsets[i] is strictly increasing:
if (offsets_[node_index] <= offsets_[node_index - 1]) {
BPLOG(INFO) << "StaticMap check failed: node offsets non-increasing";
return false;
}
// Check Key[i] is strictly increasing as no duplicate keys are allowed.
if (compare_(GetKeyAtIndex(node_index),
GetKeyAtIndex(node_index - 1)) <= 0) {
BPLOG(INFO) << "StaticMap check failed: node keys non-increasing";
return false;
}
}
return true;
}
template<typename Key, typename Value, typename Compare>
const Key StaticMap<Key, Value, Compare>::GetKeyAtIndex(int index) const {
if (index < 0 || index >= num_nodes_) {
BPLOG(ERROR) << "Key index out of range error";
// Key type is required to be primitive type. Return 0 if index is invalid.
return 0;
}
return keys_[index];
}
} // namespace google_breakpad
#endif // PROCESSOR_STATIC_MAP_INL_H__

View File

@@ -0,0 +1,144 @@
// Copyright 2010 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.
// static_map.h: StaticMap.
//
// StaticMap provides lookup interfaces and iterators similar as stl::map's.
// These lookup operations are purely Read-Only, thus memory
// allocation & deallocation is mostly avoided (intentionally).
//
// The chunk of memory should contain data with pre-defined pattern:
// **************** header ***************
// uint32 (4 bytes): number of nodes
// uint32 (4 bytes): address offset of node1's mapped_value
// uint32 (4 bytes): address offset of node2's mapped_value
// ...
// uint32 (4 bytes): address offset of nodeN's mapped_value
//
// ************* Key array ************
// (X bytes): node1's key
// (X bytes): node2's key
// ...
// (X bytes): nodeN's key
//
// ************* Value array **********
// (? bytes): node1's mapped_value
// (? bytes): node2's mapped_value
// ...
// (? bytes): nodeN's mapped_value
//
// REQUIREMENT: Key type MUST be primitive type or pointers so that:
// X = sizeof(typename Key);
//
// Note: since address offset is stored as uint32, user should keep in mind that
// StaticMap only supports up to 4GB size of memory data.
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_STATIC_MAP_H__
#define PROCESSOR_STATIC_MAP_H__
#include "processor/static_map_iterator-inl.h"
namespace google_breakpad {
// Default functor to compare keys.
template<typename Key>
class DefaultCompare {
public:
int operator()(const Key &k1, const Key &k2) const {
if (k1 < k2) return -1;
if (k1 == k2) return 0;
return 1;
}
};
template<typename Key, typename Value, typename Compare = DefaultCompare<Key> >
class StaticMap {
public:
typedef StaticMapIterator<Key, Value, Compare> iterator;
typedef StaticMapIterator<Key, Value, Compare> const_iterator;
StaticMap() : raw_data_(0),
num_nodes_(0),
offsets_(0),
compare_() { }
explicit StaticMap(const char* raw_data);
inline bool empty() const { return num_nodes_ == 0; }
inline unsigned int size() const { return num_nodes_; }
// Return iterators.
inline iterator begin() const { return IteratorAtIndex(0); }
inline iterator last() const { return IteratorAtIndex(num_nodes_ - 1); }
inline iterator end() const { return IteratorAtIndex(num_nodes_); }
inline iterator IteratorAtIndex(int index) const {
return iterator(raw_data_, index);
}
// Lookup operations.
iterator find(const Key &k) const;
// lower_bound(k) searches in a sorted range for the first element that has a
// key not less than the argument k.
iterator lower_bound(const Key &k) const;
// upper_bound(k) searches in a sorted range for the first element that has a
// key greater than the argument k.
iterator upper_bound(const Key &k) const;
// Checks if the underlying memory data conforms to the predefined pattern:
// first check the number of nodes is non-negative,
// then check both offsets and keys are strictly increasing (sorted).
bool ValidateInMemoryStructure() const;
private:
const Key GetKeyAtIndex(int i) const;
// Start address of a raw memory chunk with serialized data.
const char* raw_data_;
// Number of nodes in the static map.
u_int32_t num_nodes_;
// Array of offset addresses for stored values.
// For example:
// address_of_i-th_node_value = raw_data_ + offsets_[i]
const u_int32_t* offsets_;
// keys_[i] = key of i_th node
const Key* keys_;
Compare compare_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STATIC_MAP_H__

View File

@@ -0,0 +1,147 @@
// Copyright 2010 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.
// static_map_iterator-inl.h: StaticMapIterator implementation.
//
// See static_map_iterator.h for documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_STATIC_MAP_ITERATOR_INL_H__
#define PROCESSOR_STATIC_MAP_ITERATOR_INL_H__
#include "processor/static_map_iterator.h"
#include "processor/logging.h"
namespace google_breakpad {
template<typename Key, typename Value, typename Compare>
StaticMapIterator<Key, Value, Compare>::StaticMapIterator(const char* base,
const int &index):
index_(index), base_(base) {
// See static_map.h for documentation on
// bytes format of serialized StaticMap data.
num_nodes_ = *(reinterpret_cast<const int32_t*>(base_));
offsets_ = reinterpret_cast<const u_int32_t*>(base_ + sizeof(num_nodes_));
keys_ = reinterpret_cast<const Key*>(
base_ + (1 + num_nodes_) * sizeof(num_nodes_));
}
// Increment & Decrement operators:
template<typename Key, typename Value, typename Compare>
StaticMapIterator<Key, Value, Compare>&
StaticMapIterator<Key, Value, Compare>::operator++() {
if (!IsValid()) {
BPLOG(ERROR) << "operator++ on invalid iterator";
return *this;
}
if (++index_ > num_nodes_) index_ = num_nodes_;
return *this;
}
template<typename Key, typename Value, typename Compare>
StaticMapIterator<Key, Value, Compare>
StaticMapIterator<Key, Value, Compare>::operator++(int postfix_operator) {
if (!IsValid()) {
BPLOG(ERROR) << "operator++ on invalid iterator";
return *this;
}
StaticMapIterator<Key, Value, Compare> tmp = *this;
if (++index_ > num_nodes_) index_ = num_nodes_;
return tmp;
}
template<typename Key, typename Value, typename Compare>
StaticMapIterator<Key, Value, Compare>&
StaticMapIterator<Key, Value, Compare>::operator--() {
if (!IsValid()) {
BPLOG(ERROR) << "operator++ on invalid iterator";
return *this;
}
if (--index_ < 0) index_ = 0;
return *this;
}
template<typename Key, typename Value, typename Compare>
StaticMapIterator<Key, Value, Compare>
StaticMapIterator<Key, Value, Compare>::operator--(int postfix_operator) {
if (!IsValid()) {
BPLOG(ERROR) << "operator++ on invalid iterator";
return *this;
}
StaticMapIterator<Key, Value, Compare> tmp = *this;
if (--index_ < 0) index_ = 0;
return tmp;
}
template<typename Key, typename Value, typename Compare>
const Key* StaticMapIterator<Key, Value, Compare>::GetKeyPtr() const {
if (!IsValid()) {
BPLOG(ERROR) << "call GetKeyPtr() on invalid iterator";
return NULL;
}
return &(keys_[index_]);
}
template<typename Key, typename Value, typename Compare>
const char* StaticMapIterator<Key, Value, Compare>::GetValueRawPtr() const {
if (!IsValid()) {
BPLOG(ERROR) << "call GetValuePtr() on invalid iterator";
return NULL;
}
return base_ + offsets_[index_];
}
template<typename Key, typename Value, typename Compare>
bool StaticMapIterator<Key, Value, Compare>::operator==(
const StaticMapIterator<Key, Value, Compare>& x) const {
return base_ == x.base_ && index_ == x.index_;
}
template<typename Key, typename Value, typename Compare>
bool StaticMapIterator<Key, Value, Compare>::operator!=(
const StaticMapIterator<Key, Value, Compare>& x) const {
// Only need to compare base_ and index_.
// Other data members are auxiliary.
return base_ != x.base_ || index_ != x.index_;
}
template<typename Key, typename Value, typename Compare>
bool StaticMapIterator<Key, Value, Compare>::IsValid() const {
if (!base_ || index_ < 0 || index_ > num_nodes_)
return false;
return true;
}
} // namespace google_breakpad
#endif // PROCESSOR_STATIC_MAP_ITERATOR_INL_H__

View File

@@ -0,0 +1,112 @@
// Copyright 2010 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.
// static_map_iterator.h: StaticMapIterator template class declaration.
//
// StaticMapIterator provides increment and decrement operators to iterate
// through a StaticMap map. It does not provide *, -> operators, user should
// use GetKeyPtr(), GetKey(), GetValuePtr() interfaces to retrieve data or
// pointer to data. StaticMapIterator is essentially a const_iterator.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_STATIC_MAP_ITERATOR_H__
#define PROCESSOR_STATIC_MAP_ITERATOR_H__
#include <sys/types.h>
namespace google_breakpad {
// Forward declaration.
template<typename Key, typename Value, typename Compare> class StaticMap;
// StaticMapIterator does not support operator*() or operator->(),
// User should use GetKey(), GetKeyPtr(), GetValuePtr() instead;
template<typename Key, typename Value, typename Compare>
class StaticMapIterator {
public:
// Constructors.
StaticMapIterator(): index_(-1), base_(NULL) { }
// Increment & Decrement operators:
StaticMapIterator& operator++();
StaticMapIterator operator++(int post_fix_operator);
StaticMapIterator& operator--();
StaticMapIterator operator--(int post_fix_operator);
// Interface for retrieving data / pointer to data.
const Key* GetKeyPtr() const;
// Run time error will occur if GetKey() is called on an invalid iterator.
inline const Key GetKey() const { return *GetKeyPtr(); }
// return a raw memory pointer that points to the start address of value.
const char* GetValueRawPtr() const;
// return a reinterpret-casted pointer to the value.
inline const Value* GetValuePtr() const {
return reinterpret_cast<const Value*>(GetValueRawPtr());
}
bool operator==(const StaticMapIterator& x) const;
bool operator!=(const StaticMapIterator& x) const;
// Check if this iterator is valid.
// If iterator is invalid, user is forbidden to use ++/-- operator
// or interfaces for retrieving data / pointer to data.
bool IsValid() const;
private:
friend class StaticMap<Key, Value, Compare>;
// Only StaticMap can call this constructor.
explicit StaticMapIterator(const char* base, const int32_t &index);
// Index of node that the iterator is pointing to.
int32_t index_;
// Beginning address of the serialized map data.
const char* base_;
// Number of nodes in the map. Use it to identify end() iterator.
int32_t num_nodes_;
// offsets_ is an array of offset addresses of mapped values.
// For example:
// address_of_i-th_node_value = base_ + offsets_[i]
const u_int32_t* offsets_;
// keys_[i] = key of i_th node.
const Key* keys_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STATIC_MAP_ITERATOR_H__

View File

@@ -0,0 +1,386 @@
// Copyright (c) 2010 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.
// static_map_unittest.cc: Unit tests for StaticMap.
//
// Author: Siyang Xie (lambxsy@google.com)
#include <climits>
#include <map>
#include "breakpad_googletest_includes.h"
#include "processor/static_map-inl.h"
typedef int ValueType;
typedef int KeyType;
typedef google_breakpad::StaticMap< KeyType, ValueType > TestMap;
typedef std::map< KeyType, ValueType > StdMap;
template<typename Key, typename Value>
class SimpleMapSerializer {
public:
static char* Serialize(const std::map<Key, Value> &stdmap,
unsigned int* size = NULL) {
unsigned int size_per_node =
sizeof(u_int32_t) + sizeof(Key) + sizeof(Value);
unsigned int memsize = sizeof(int32_t) + size_per_node * stdmap.size();
if (size) *size = memsize;
// Allocate memory for serialized data:
char* mem = reinterpret_cast<char*>(operator new(memsize));
char* address = mem;
// Writer the number of nodes:
new (address) u_int32_t(static_cast<u_int32_t>(stdmap.size()));
address += sizeof(u_int32_t);
// Nodes' offset:
u_int32_t* offsets = reinterpret_cast<u_int32_t*>(address);
address += sizeof(u_int32_t) * stdmap.size();
// Keys:
Key* keys = reinterpret_cast<Key*>(address);
address += sizeof(Key) * stdmap.size();
// Traversing map:
typename std::map<Key, Value>::const_iterator iter = stdmap.begin();
for (int index = 0; iter != stdmap.end(); ++iter, ++index) {
offsets[index] = static_cast<unsigned int>(address - mem);
keys[index] = iter->first;
new (address) Value(iter->second);
address += sizeof(Value);
}
return mem;
}
};
class TestInvalidMap : public ::testing::Test {
protected:
void SetUp() {
memset(data, 0, kMemorySize);
}
// 40 Bytes memory can hold a StaticMap with up to 3 nodes.
static const int kMemorySize = 40;
char data[kMemorySize];
TestMap test_map;
};
TEST_F(TestInvalidMap, TestNegativeNumberNodes) {
memset(data, 0xff, sizeof(u_int32_t)); // Set the number of nodes = -1
test_map = TestMap(data);
ASSERT_FALSE(test_map.ValidateInMemoryStructure());
}
TEST_F(TestInvalidMap, TestWrongOffsets) {
u_int32_t* header = reinterpret_cast<u_int32_t*>(data);
const u_int32_t kNumNodes = 2;
const u_int32_t kHeaderOffset =
sizeof(u_int32_t) + kNumNodes * (sizeof(u_int32_t) + sizeof(KeyType));
header[0] = kNumNodes;
header[1] = kHeaderOffset + 3; // Wrong offset for first node
test_map = TestMap(data);
ASSERT_FALSE(test_map.ValidateInMemoryStructure());
header[1] = kHeaderOffset; // Correct offset for first node
header[2] = kHeaderOffset - 1; // Wrong offset for second node
test_map = TestMap(data);
ASSERT_FALSE(test_map.ValidateInMemoryStructure());
}
TEST_F(TestInvalidMap, TestUnSortedKeys) {
u_int32_t* header = reinterpret_cast<u_int32_t*>(data);
const u_int32_t kNumNodes = 2;
const u_int32_t kHeaderOffset =
sizeof(u_int32_t) + kNumNodes * (sizeof(u_int32_t) + sizeof(KeyType));
header[0] = kNumNodes;
header[1] = kHeaderOffset;
header[2] = kHeaderOffset + sizeof(ValueType);
KeyType* keys = reinterpret_cast<KeyType*>(
data + (kNumNodes + 1) * sizeof(u_int32_t));
// Set keys in non-increasing order.
keys[0] = 10;
keys[1] = 7;
test_map = TestMap(data);
ASSERT_FALSE(test_map.ValidateInMemoryStructure());
}
class TestValidMap : public ::testing::Test {
protected:
void SetUp() {
int testcase = 0;
// Empty map.
map_data[testcase] =
serializer.Serialize(std_map[testcase], &size[testcase]);
test_map[testcase] = TestMap(map_data[testcase]);
++testcase;
// Single element.
std_map[testcase].insert(std::make_pair(2, 8));
map_data[testcase] =
serializer.Serialize(std_map[testcase], &size[testcase]);
test_map[testcase] = TestMap(map_data[testcase]);
++testcase;
// 100 elements.
for (int i = 0; i < 100; ++i)
std_map[testcase].insert(std::make_pair(i, 2 * i));
map_data[testcase] =
serializer.Serialize(std_map[testcase], &size[testcase]);
test_map[testcase] = TestMap(map_data[testcase]);
++testcase;
// 1000 random elements.
for (int i = 0; i < 1000; ++i)
std_map[testcase].insert(std::make_pair(rand(), rand()));
map_data[testcase] =
serializer.Serialize(std_map[testcase], &size[testcase]);
test_map[testcase] = TestMap(map_data[testcase]);
// Set correct size of memory allocation for each test case.
unsigned int size_per_node =
sizeof(u_int32_t) + sizeof(KeyType) + sizeof(ValueType);
for (testcase = 0; testcase < kNumberTestCases; ++testcase) {
correct_size[testcase] =
sizeof(u_int32_t) + std_map[testcase].size() * size_per_node;
}
}
void TearDown() {
for (int i = 0;i < kNumberTestCases; ++i)
delete map_data[i];
}
void IteratorTester(int test_case) {
// scan through:
iter_test = test_map[test_case].begin();
iter_std = std_map[test_case].begin();
for (; iter_test != test_map[test_case].end() &&
iter_std != std_map[test_case].end();
++iter_test, ++iter_std) {
ASSERT_EQ(iter_test.GetKey(), iter_std->first);
ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second);
}
ASSERT_TRUE(iter_test == test_map[test_case].end()
&& iter_std == std_map[test_case].end());
// Boundary testcase.
if (!std_map[test_case].empty()) {
// rear boundary case:
iter_test = test_map[test_case].end();
iter_std = std_map[test_case].end();
--iter_std;
--iter_test;
ASSERT_EQ(iter_test.GetKey(), iter_std->first);
ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second);
++iter_test;
++iter_std;
ASSERT_TRUE(iter_test == test_map[test_case].end());
--iter_test;
--iter_std;
ASSERT_TRUE(iter_test != test_map[test_case].end());
ASSERT_TRUE(iter_test == test_map[test_case].last());
ASSERT_EQ(iter_test.GetKey(), iter_std->first);
ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second);
// front boundary case:
iter_test = test_map[test_case].begin();
--iter_test;
ASSERT_TRUE(iter_test == test_map[test_case].begin());
}
}
void CompareLookupResult(int test_case) {
bool found1 = (iter_test != test_map[test_case].end());
bool found2 = (iter_std != std_map[test_case].end());
ASSERT_EQ(found1, found2);
if (found1 && found2) {
ASSERT_EQ(iter_test.GetKey(), iter_std->first);
ASSERT_EQ(*(iter_test.GetValuePtr()), iter_std->second);
}
}
void FindTester(int test_case, const KeyType &key) {
iter_test = test_map[test_case].find(key);
iter_std = std_map[test_case].find(key);
CompareLookupResult(test_case);
}
void LowerBoundTester(int test_case, const KeyType &key) {
iter_test = test_map[test_case].lower_bound(key);
iter_std = std_map[test_case].lower_bound(key);
CompareLookupResult(test_case);
}
void UpperBoundTester(int test_case, const KeyType &key) {
iter_test = test_map[test_case].upper_bound(key);
iter_std = std_map[test_case].upper_bound(key);
CompareLookupResult(test_case);
}
void LookupTester(int test_case) {
StdMap::const_iterator iter;
// Test find():
for (iter = std_map[test_case].begin();
iter != std_map[test_case].end();
++iter) {
FindTester(test_case, iter->first);
FindTester(test_case, iter->first + 1);
FindTester(test_case, iter->first - 1);
}
FindTester(test_case, INT_MIN);
FindTester(test_case, INT_MAX);
// random test:
for (int i = 0; i < rand()%5000 + 5000; ++i)
FindTester(test_case, rand());
// Test lower_bound():
for (iter = std_map[test_case].begin();
iter != std_map[test_case].end();
++iter) {
LowerBoundTester(test_case, iter->first);
LowerBoundTester(test_case, iter->first + 1);
LowerBoundTester(test_case, iter->first - 1);
}
LowerBoundTester(test_case, INT_MIN);
LowerBoundTester(test_case, INT_MAX);
// random test:
for (int i = 0; i < rand()%5000 + 5000; ++i)
LowerBoundTester(test_case, rand());
// Test upper_bound():
for (iter = std_map[test_case].begin();
iter != std_map[test_case].end();
++iter) {
UpperBoundTester(test_case, iter->first);
UpperBoundTester(test_case, iter->first + 1);
UpperBoundTester(test_case, iter->first - 1);
}
UpperBoundTester(test_case, INT_MIN);
UpperBoundTester(test_case, INT_MAX);
// random test:
for (int i = 0; i < rand()%5000 + 5000; ++i)
UpperBoundTester(test_case, rand());
}
static const int kNumberTestCases = 4;
StdMap std_map[kNumberTestCases];
TestMap test_map[kNumberTestCases];
TestMap::const_iterator iter_test;
StdMap::const_iterator iter_std;
char* map_data[kNumberTestCases];
unsigned int size[kNumberTestCases];
unsigned int correct_size[kNumberTestCases];
SimpleMapSerializer<KeyType, ValueType> serializer;
};
TEST_F(TestValidMap, TestEmptyMap) {
int test_case = 0;
// Assert memory size allocated during serialization is correct.
ASSERT_EQ(correct_size[test_case], size[test_case]);
// Sanity check of serialized data:
ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure());
ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty());
ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size());
// Test Iterator.
IteratorTester(test_case);
// Test lookup operations.
LookupTester(test_case);
}
TEST_F(TestValidMap, TestSingleElement) {
int test_case = 1;
// Assert memory size allocated during serialization is correct.
ASSERT_EQ(correct_size[test_case], size[test_case]);
// Sanity check of serialized data:
ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure());
ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty());
ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size());
// Test Iterator.
IteratorTester(test_case);
// Test lookup operations.
LookupTester(test_case);
}
TEST_F(TestValidMap, Test100Elements) {
int test_case = 2;
// Assert memory size allocated during serialization is correct.
ASSERT_EQ(correct_size[test_case], size[test_case]);
// Sanity check of serialized data:
ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure());
ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty());
ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size());
// Test Iterator.
IteratorTester(test_case);
// Test lookup operations.
LookupTester(test_case);
}
TEST_F(TestValidMap, Test1000RandomElements) {
int test_case = 3;
// Assert memory size allocated during serialization is correct.
ASSERT_EQ(correct_size[test_case], size[test_case]);
// Sanity check of serialized data:
ASSERT_TRUE(test_map[test_case].ValidateInMemoryStructure());
ASSERT_EQ(std_map[test_case].empty(), test_map[test_case].empty());
ASSERT_EQ(std_map[test_case].size(), test_map[test_case].size());
// Test Iterator.
IteratorTester(test_case);
// Test lookup operations.
LookupTester(test_case);
}
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -0,0 +1,130 @@
// Copyright (c) 2010 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.
// static_range_map-inl.h: StaticRangeMap implementation.
//
// See static_range_map.h for documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_STATIC_RANGE_MAP_INL_H__
#define PROCESSOR_STATIC_RANGE_MAP_INL_H__
#include "processor/static_range_map.h"
#include "processor/logging.h"
namespace google_breakpad {
template<typename AddressType, typename EntryType>
bool StaticRangeMap<AddressType, EntryType>::RetrieveRange(
const AddressType &address, const EntryType *&entry,
AddressType *entry_base, AddressType *entry_size) const {
MapConstIterator iterator = map_.lower_bound(address);
if (iterator == map_.end())
return false;
// The map is keyed by the high address of each range, so |address| is
// guaranteed to be lower than the range's high address. If |range| is
// not directly preceded by another range, it's possible for address to
// be below the range's low address, though. When that happens, address
// references something not within any range, so return false.
const Range *range = iterator.GetValuePtr();
// Make sure AddressType and EntryType are copyable basic types
// e.g.: integer types, pointers etc
if (address < range->base())
return false;
entry = range->entryptr();
if (entry_base)
*entry_base = range->base();
if (entry_size)
*entry_size = iterator.GetKey() - range->base() + 1;
return true;
}
template<typename AddressType, typename EntryType>
bool StaticRangeMap<AddressType, EntryType>::RetrieveNearestRange(
const AddressType &address, const EntryType *&entry,
AddressType *entry_base, AddressType *entry_size) const {
// 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;
const Range *range = iterator.GetValuePtr();
entry = range->entryptr();
if (entry_base)
*entry_base = range->base();
if (entry_size)
*entry_size = iterator.GetKey() - range->base() + 1;
return true;
}
template<typename AddressType, typename EntryType>
bool StaticRangeMap<AddressType, EntryType>::RetrieveRangeAtIndex(
int index, const EntryType *&entry,
AddressType *entry_base, AddressType *entry_size) const {
if (index >= GetCount()) {
BPLOG(ERROR) << "Index out of range: " << index << "/" << GetCount();
return false;
}
MapConstIterator iterator = map_.IteratorAtIndex(index);
const Range *range = iterator.GetValuePtr();
entry = range->entryptr();
if (entry_base)
*entry_base = range->base();
if (entry_size)
*entry_size = iterator.GetKey() - range->base() + 1;
return true;
}
} // namespace google_breakpad
#endif // PROCESSOR_STATIC_RANGE_MAP_INL_H__

View File

@@ -0,0 +1,106 @@
// Copyright (c) 2010, 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.
//
// static_range_map.h: StaticRangeMap.
//
// StaticRangeMap is similar as RangeMap. However, StaticRangeMap wraps a
// StaticMap instead of std::map, and does not support dynamic operations like
// StoreRange(...). StaticRangeMap provides same Retrieve*() interfaces as
// RangeMap. Please see range_map.h for more documentation.
//
// Author: Siyang Xie (lambxsy@google.com)
#ifndef PROCESSOR_STATIC_RANGE_MAP_H__
#define PROCESSOR_STATIC_RANGE_MAP_H__
#include "processor/static_map-inl.h"
namespace google_breakpad {
// AddressType is basic type, e.g.: integer types, pointers etc
// EntryType could be a complex type, so we retrieve its pointer instead.
template<typename AddressType, typename EntryType>
class StaticRangeMap {
public:
StaticRangeMap(): map_() { }
explicit StaticRangeMap(const char *memory): map_(memory) { }
// Locates the range encompassing the supplied address. If there is
// no such range, returns false. 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, const 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, const EntryType *&entry,
AddressType *entry_base, AddressType *entry_size)
const;
// Treating all ranges as a list ordered by the address spaces that they
// occupy, locates the range at the index specified by index. Returns
// false if index is larger than the number of ranges stored. entry_base
// and entry_size, if non-NULL, are set to the base and size of the entry's
// range.
//
// RetrieveRangeAtIndex is not optimized for speedy operation.
bool RetrieveRangeAtIndex(int index, const EntryType *&entry,
AddressType *entry_base, AddressType *entry_size)
const;
// Returns the number of ranges stored in the RangeMap.
inline unsigned int GetCount() const { return map_.size(); }
private:
friend class ModuleComparer;
class Range {
public:
AddressType base() const {
return *(reinterpret_cast<const AddressType*>(this));
}
const EntryType* entryptr() const {
return reinterpret_cast<const EntryType*>(this + sizeof(AddressType));
}
};
// Convenience types.
typedef StaticRangeMap* SelfPtr;
typedef StaticMap<AddressType, Range> AddressToRangeMap;
typedef typename AddressToRangeMap::const_iterator MapConstIterator;
AddressToRangeMap map_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STATIC_RANGE_MAP_H__

View File

@@ -0,0 +1,421 @@
// Copyright (c) 2010 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.
// static_range_map_unittest.cc: Unit tests for StaticRangeMap.
//
// Author: Siyang Xie (lambxsy@google.com)
#include "breakpad_googletest_includes.h"
#include "processor/range_map-inl.h"
#include "processor/static_range_map-inl.h"
#include "processor/simple_serializer-inl.h"
#include "processor/map_serializers-inl.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
namespace {
// Types used for testing.
typedef int AddressType;
typedef int EntryType;
typedef google_breakpad::StaticRangeMap< AddressType, EntryType > TestMap;
typedef google_breakpad::RangeMap< AddressType, EntryType > RMap;
// RangeTest contains data to use for store and retrieve tests. See
// RunTests for descriptions of the tests.
struct RangeTest {
// Base address to use for test
AddressType address;
// Size of range to use for test
AddressType size;
// Unique ID of range - unstorable ranges must have unique IDs too
EntryType id;
// Whether this range is expected to be stored successfully or not
bool expect_storable;
};
// A RangeTestSet encompasses multiple RangeTests, which are run in
// sequence on the same RangeMap.
struct RangeTestSet {
// An array of RangeTests
const RangeTest* range_tests;
// The number of tests in the set
unsigned int range_test_count;
};
// These tests will be run sequentially. The first set of tests exercises
// most functions of RangeTest, and verifies all of the bounds-checking.
const RangeTest range_tests_0[] = {
{ INT_MIN, 16, 1, true }, // lowest possible range
{ -2, 5, 2, true }, // a range through zero
{ INT_MAX - 9, 11, 3, false }, // tests anti-overflow
{ INT_MAX - 9, 10, 4, true }, // highest possible range
{ 5, 0, 5, false }, // tests anti-zero-size
{ 5, 1, 6, true }, // smallest possible range
{ -20, 15, 7, true }, // entirely negative
{ 10, 10, 10, true }, // causes the following tests to fail
{ 9, 10, 11, false }, // one-less base, one-less high
{ 9, 11, 12, false }, // one-less base, identical high
{ 9, 12, 13, false }, // completely contains existing
{ 10, 9, 14, false }, // identical base, one-less high
{ 10, 10, 15, false }, // exactly identical to existing range
{ 10, 11, 16, false }, // identical base, one-greater high
{ 11, 8, 17, false }, // contained completely within
{ 11, 9, 18, false }, // one-greater base, identical high
{ 11, 10, 19, false }, // one-greater base, one-greater high
{ 9, 2, 20, false }, // overlaps bottom by one
{ 10, 1, 21, false }, // overlaps bottom by one, contained
{ 19, 1, 22, false }, // overlaps top by one, contained
{ 19, 2, 23, false }, // overlaps top by one
{ 9, 1, 24, true }, // directly below without overlap
{ 20, 1, 25, true }, // directly above without overlap
{ 6, 3, 26, true }, // exactly between two ranges, gapless
{ 7, 3, 27, false }, // tries to span two ranges
{ 7, 5, 28, false }, // tries to span three ranges
{ 4, 20, 29, false }, // tries to contain several ranges
{ 30, 50, 30, true },
{ 90, 25, 31, true },
{ 35, 65, 32, false }, // tries to span two noncontiguous
{ 120, 10000, 33, true }, // > 8-bit
{ 20000, 20000, 34, true }, // > 8-bit
{ 0x10001, 0x10001, 35, true }, // > 16-bit
{ 27, -1, 36, false } // tests high < base
};
// Attempt to fill the entire space. The entire space must be filled with
// three stores because AddressType is signed for these tests, so RangeMap
// treats the size as signed and rejects sizes that appear to be negative.
// Even if these tests were run as unsigned, two stores would be needed
// to fill the space because the entire size of the space could only be
// described by using one more bit than would be present in AddressType.
const RangeTest range_tests_1[] = {
{ INT_MIN, INT_MAX, 50, true }, // From INT_MIN to -2, inclusive
{ -1, 2, 51, true }, // From -1 to 0, inclusive
{ 1, INT_MAX, 52, true }, // From 1 to INT_MAX, inclusive
{ INT_MIN, INT_MAX, 53, false }, // Can't fill the space twice
{ -1, 2, 54, false },
{ 1, INT_MAX, 55, false },
{ -3, 6, 56, false }, // -3 to 2, inclusive - spans 3 ranges
};
// A light round of testing to verify that RetrieveRange does the right
// the right thing at the extremities of the range when nothing is stored
// there. Checks are forced without storing anything at the extremities
// by setting size = 0.
const RangeTest range_tests_2[] = {
{ INT_MIN, 0, 100, false }, // makes RetrieveRange check low end
{ -1, 3, 101, true },
{ INT_MAX, 0, 102, false }, // makes RetrieveRange check high end
};
// Similar to the previous test set, but with a couple of ranges closer
// to the extremities.
const RangeTest range_tests_3[] = {
{ INT_MIN + 1, 1, 110, true },
{ INT_MAX - 1, 1, 111, true },
{ INT_MIN, 0, 112, false }, // makes RetrieveRange check low end
{ INT_MAX, 0, 113, false } // makes RetrieveRange check high end
};
// The range map is cleared between sets of tests listed here.
const RangeTestSet range_test_sets[] = {
{ range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) },
{ range_tests_1, sizeof(range_tests_1) / sizeof(RangeTest) },
{ range_tests_2, sizeof(range_tests_2) / sizeof(RangeTest) },
{ range_tests_3, sizeof(range_tests_3) / sizeof(RangeTest) },
{ range_tests_0, sizeof(range_tests_0) / sizeof(RangeTest) } // Run again
};
} // namespace
namespace google_breakpad {
class TestStaticRangeMap : public ::testing::Test {
protected:
void SetUp() {
kTestCasesCount_ = sizeof(range_test_sets) / sizeof(RangeTestSet);
}
// StoreTest uses the data in a RangeTest and calls StoreRange on the
// test RangeMap. It returns true if the expected result occurred, and
// false if something else happened.
void StoreTest(RMap* range_map, const RangeTest* range_test);
// RetrieveTest uses the data in RangeTest and calls RetrieveRange on the
// test RangeMap. If it retrieves the expected value (which can be no
// map entry at the specified range,) it returns true, otherwise, it returns
// false. RetrieveTest will check the values around the base address and
// the high address of a range to guard against off-by-one errors.
void RetrieveTest(TestMap* range_map, const RangeTest* range_test);
// Test RetrieveRangeAtIndex, which is supposed to return objects in order
// according to their addresses. This test is performed by looping through
// the map, calling RetrieveRangeAtIndex for all possible indices in sequence,
// and verifying that each call returns a different object than the previous
// call, and that ranges are returned with increasing base addresses. Returns
// false if the test fails.
void RetrieveIndexTest(const TestMap* range_map, int set);
void RunTestCase(int test_case);
unsigned int kTestCasesCount_;
RangeMapSerializer<AddressType, EntryType> serializer_;
};
void TestStaticRangeMap::StoreTest(RMap* range_map,
const RangeTest* range_test) {
bool stored = range_map->StoreRange(range_test->address,
range_test->size,
range_test->id);
EXPECT_EQ(stored, range_test->expect_storable)
<< "StoreRange id " << range_test->id << "FAILED";
}
void TestStaticRangeMap::RetrieveTest(TestMap* range_map,
const RangeTest* range_test) {
for (unsigned int side = 0; side <= 1; ++side) {
// When side == 0, check the low side (base address) of each range.
// When side == 1, check the high side (base + size) of each range.
// Check one-less and one-greater than the target address in addition
// to the target address itself.
// If the size of the range is only 1, don't check one greater than
// the base or one less than the high - for a successfully stored
// range, these tests would erroneously fail because the range is too
// small.
AddressType low_offset = -1;
AddressType high_offset = 1;
if (range_test->size == 1) {
if (!side) // When checking the low side,
high_offset = 0; // don't check one over the target.
else // When checking the high side,
low_offset = 0; // don't check one under the target.
}
for (AddressType offset = low_offset; offset <= high_offset; ++offset) {
AddressType address =
offset +
(!side ? range_test->address :
range_test->address + range_test->size - 1);
bool expected_result = false; // This is correct for tests not stored.
if (range_test->expect_storable) {
if (offset == 0) // When checking the target address,
expected_result = true; // test should always succeed.
else if (offset == -1) // When checking one below the target,
expected_result = side; // should fail low and succeed high.
else // When checking one above the target,
expected_result = !side; // should succeed low and fail high.
}
const EntryType* id;
AddressType retrieved_base;
AddressType retrieved_size;
bool retrieved = range_map->RetrieveRange(address, id,
&retrieved_base,
&retrieved_size);
bool observed_result = retrieved && *id == range_test->id;
EXPECT_EQ(observed_result, expected_result)
<< "RetrieveRange id " << range_test->id
<< ", side " << side << ", offset " << offset << " FAILED.";
// If a range was successfully retrieved, check that the returned
// bounds match the range as stored.
if (observed_result == true) {
EXPECT_EQ(retrieved_base, range_test->address)
<< "RetrieveRange id " << range_test->id
<< ", side " << side << ", offset " << offset << " FAILED.";
EXPECT_EQ(retrieved_size, range_test->size)
<< "RetrieveRange id " << range_test->id
<< ", side " << side << ", offset " << offset << " FAILED.";
}
// 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;
AddressType nearest_base;
AddressType nearest_size;
bool retrieved_nearest = range_map->RetrieveNearestRange(address,
id,
&nearest_base,
&nearest_size);
// 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 &&
*id == range_test->id;
EXPECT_EQ(observed_nearest, expected_nearest)
<< "RetrieveRange id " << range_test->id
<< ", side " << side << ", offset " << offset << " FAILED.";
// If a range was successfully retrieved, check that the returned
// bounds match the range as stored.
if (expected_nearest ==true) {
EXPECT_EQ(nearest_base, range_test->address)
<< "RetrieveRange id " << range_test->id
<< ", side " << side << ", offset " << offset << " FAILED.";
EXPECT_EQ(nearest_size, range_test->size)
<< "RetrieveRange id " << range_test->id
<< ", side " << side << ", offset " << offset << " FAILED.";
}
}
}
}
void TestStaticRangeMap::RetrieveIndexTest(const TestMap* range_map, int set) {
AddressType last_base = 0;
const EntryType* last_entry = 0;
const EntryType* entry;
int object_count = range_map->GetCount();
for (int object_index = 0; object_index < object_count; ++object_index) {
AddressType base;
ASSERT_TRUE(range_map->RetrieveRangeAtIndex(object_index,
entry,
&base,
NULL))
<< "FAILED: RetrieveRangeAtIndex set " << set
<< " index " << object_index;
ASSERT_TRUE(entry) << "FAILED: RetrieveRangeAtIndex set " << set
<< " index " << object_index;
// It's impossible to do these comparisons unless there's a previous
// object to compare against.
if (last_entry) {
// The object must be different from the last_entry one.
EXPECT_NE(*entry, *last_entry) << "FAILED: RetrieveRangeAtIndex set "
<< set << " index " << object_index;
// Each object must have a base greater than the previous object's base.
EXPECT_GT(base, last_base) << "FAILED: RetrieveRangeAtIndex set " << set
<< " index " << object_index;
}
last_entry = entry;
last_base = base;
}
// Make sure that RetrieveRangeAtIndex doesn't allow lookups at indices that
// are too high.
ASSERT_FALSE(range_map->RetrieveRangeAtIndex(
object_count, entry, NULL, NULL)) << "FAILED: RetrieveRangeAtIndex set "
<< set << " index " << object_count
<< " (too large)";
}
// RunTests runs a series of test sets.
void TestStaticRangeMap::RunTestCase(int test_case) {
// Maintain the range map in a pointer so that deletion can be meaningfully
// tested.
scoped_ptr<RMap> rmap(new RMap());
const RangeTest* range_tests = range_test_sets[test_case].range_tests;
unsigned int range_test_count = range_test_sets[test_case].range_test_count;
// Run the StoreRange test, which validates StoreRange and initializes
// the RangeMap with data for the RetrieveRange test.
int stored_count = 0; // The number of ranges successfully stored
for (unsigned int range_test_index = 0;
range_test_index < range_test_count;
++range_test_index) {
const RangeTest* range_test = &range_tests[range_test_index];
StoreTest(rmap.get(), range_test);
if (range_test->expect_storable)
++stored_count;
}
scoped_array<char> memaddr(serializer_.Serialize(*rmap, NULL));
scoped_ptr<TestMap> static_range_map(new TestMap(memaddr.get()));
// The RangeMap's own count of objects should also match.
EXPECT_EQ(static_range_map->GetCount(), stored_count);
// Run the RetrieveRange test
for (unsigned int range_test_index = 0;
range_test_index < range_test_count;
++range_test_index) {
const RangeTest* range_test = &range_tests[range_test_index];
RetrieveTest(static_range_map.get(), range_test);
}
RetrieveIndexTest(static_range_map.get(), test_case);
}
TEST_F(TestStaticRangeMap, TestCase0) {
int test_case = 0;
RunTestCase(test_case);
}
TEST_F(TestStaticRangeMap, TestCase1) {
int test_case = 1;
RunTestCase(test_case);
}
TEST_F(TestStaticRangeMap, TestCase2) {
int test_case = 2;
RunTestCase(test_case);
}
TEST_F(TestStaticRangeMap, TestCase3) {
int test_case = 3;
RunTestCase(test_case);
}
TEST_F(TestStaticRangeMap, RunTestCase0Again) {
int test_case = 0;
RunTestCase(test_case);
}
} // namespace google_breakpad
int main(int argc, char *argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

Some files were not shown because too many files have changed in this diff Show More