mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-04-19 10:38:58 +02:00
Move dead submodules in-tree
Signed-off-by: swurl <swurl@swurl.xyz>
This commit is contained in:
parent
c0cceff365
commit
6c655321e6
4081 changed files with 1185566 additions and 45 deletions
92
externals/breakpad/src/processor/address_map-inl.h
vendored
Normal file
92
externals/breakpad/src/processor/address_map-inl.h
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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__
|
||||
84
externals/breakpad/src/processor/address_map.h
vendored
Normal file
84
externals/breakpad/src/processor/address_map.h
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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__
|
||||
199
externals/breakpad/src/processor/address_map_unittest.cc
vendored
Normal file
199
externals/breakpad/src/processor/address_map_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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;
|
||||
}
|
||||
120
externals/breakpad/src/processor/basic_code_module.h
vendored
Normal file
120
externals/breakpad/src/processor/basic_code_module.h
vendored
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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 "common/using_std_string.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
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()),
|
||||
shrink_down_delta_(that->shrink_down_delta()),
|
||||
code_file_(that->code_file()),
|
||||
code_identifier_(that->code_identifier()),
|
||||
debug_file_(that->debug_file()),
|
||||
debug_identifier_(that->debug_identifier()),
|
||||
version_(that->version()),
|
||||
is_unloaded_(that->is_unloaded()) {}
|
||||
|
||||
BasicCodeModule(uint64_t base_address, uint64_t size,
|
||||
const string& code_file,
|
||||
const string& code_identifier,
|
||||
const string& debug_file,
|
||||
const string& debug_identifier,
|
||||
const string& version,
|
||||
const bool is_unloaded = false)
|
||||
: base_address_(base_address),
|
||||
size_(size),
|
||||
shrink_down_delta_(0),
|
||||
code_file_(code_file),
|
||||
code_identifier_(code_identifier),
|
||||
debug_file_(debug_file),
|
||||
debug_identifier_(debug_identifier),
|
||||
version_(version),
|
||||
is_unloaded_(is_unloaded)
|
||||
{}
|
||||
virtual ~BasicCodeModule() {}
|
||||
|
||||
// See code_module.h for descriptions of these methods and the associated
|
||||
// members.
|
||||
virtual uint64_t base_address() const { return base_address_; }
|
||||
virtual uint64_t size() const { return size_; }
|
||||
virtual uint64_t shrink_down_delta() const { return shrink_down_delta_; }
|
||||
virtual void SetShrinkDownDelta(uint64_t shrink_down_delta) {
|
||||
shrink_down_delta_ = shrink_down_delta;
|
||||
}
|
||||
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 CodeModule* Copy() const { return new BasicCodeModule(this); }
|
||||
virtual bool is_unloaded() const { return is_unloaded_; }
|
||||
|
||||
private:
|
||||
uint64_t base_address_;
|
||||
uint64_t size_;
|
||||
uint64_t shrink_down_delta_;
|
||||
string code_file_;
|
||||
string code_identifier_;
|
||||
string debug_file_;
|
||||
string debug_identifier_;
|
||||
string version_;
|
||||
bool is_unloaded_;
|
||||
|
||||
// Disallow copy constructor and assignment operator.
|
||||
BasicCodeModule(const BasicCodeModule& that);
|
||||
void operator=(const BasicCodeModule& that);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_BASIC_CODE_MODULE_H__
|
||||
155
externals/breakpad/src/processor/basic_code_modules.cc
vendored
Normal file
155
externals/breakpad/src/processor/basic_code_modules.cc
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "processor/basic_code_modules.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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 {
|
||||
|
||||
using std::vector;
|
||||
|
||||
BasicCodeModules::BasicCodeModules(const CodeModules* that,
|
||||
MergeRangeStrategy strategy)
|
||||
: main_address_(0), map_() {
|
||||
BPLOG_IF(ERROR, !that) << "BasicCodeModules::BasicCodeModules requires "
|
||||
"|that|";
|
||||
assert(that);
|
||||
|
||||
map_.SetMergeStrategy(strategy);
|
||||
|
||||
const CodeModule *main_module = that->GetMainModule();
|
||||
if (main_module)
|
||||
main_address_ = main_module->base_address();
|
||||
|
||||
unsigned int count = that->module_count();
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
// 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.
|
||||
linked_ptr<const CodeModule> module(that->GetModuleAtIndex(i)->Copy());
|
||||
if (!map_.StoreRange(module->base_address(), module->size(), module)) {
|
||||
BPLOG(ERROR) << "Module " << module->code_file()
|
||||
<< " could not be stored";
|
||||
}
|
||||
}
|
||||
|
||||
// Report modules with shrunk ranges.
|
||||
for (unsigned int i = 0; i < count; ++i) {
|
||||
linked_ptr<const CodeModule> module(that->GetModuleAtIndex(i)->Copy());
|
||||
uint64_t delta = 0;
|
||||
if (map_.RetrieveRange(module->base_address() + module->size() - 1,
|
||||
&module, NULL /* base */, &delta, NULL /* size */) &&
|
||||
delta > 0) {
|
||||
BPLOG(INFO) << "The range for module " << module->code_file()
|
||||
<< " was shrunk down by " << HexString(delta) << " bytes.";
|
||||
linked_ptr<CodeModule> shrunk_range_module(module->Copy());
|
||||
shrunk_range_module->SetShrinkDownDelta(delta);
|
||||
shrunk_range_modules_.push_back(shrunk_range_module);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(ivanpe): Report modules with conflicting ranges. The list of such
|
||||
// modules should be copied from |that|.
|
||||
}
|
||||
|
||||
BasicCodeModules::BasicCodeModules() : main_address_(0), map_() { }
|
||||
|
||||
BasicCodeModules::~BasicCodeModules() {
|
||||
}
|
||||
|
||||
unsigned int BasicCodeModules::module_count() const {
|
||||
return map_.GetCount();
|
||||
}
|
||||
|
||||
const CodeModule* BasicCodeModules::GetModuleForAddress(
|
||||
uint64_t address) const {
|
||||
linked_ptr<const CodeModule> module;
|
||||
if (!map_.RetrieveRange(address, &module, NULL /* base */, NULL /* delta */,
|
||||
NULL /* size */)) {
|
||||
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 /* base */,
|
||||
NULL /* delta */, NULL /* size */)) {
|
||||
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, map_.GetMergeStrategy());
|
||||
}
|
||||
|
||||
vector<linked_ptr<const CodeModule> >
|
||||
BasicCodeModules::GetShrunkRangeModules() const {
|
||||
return shrunk_range_modules_;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
96
externals/breakpad/src/processor/basic_code_modules.h
vendored
Normal file
96
externals/breakpad/src/processor/basic_code_modules.h
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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 <stddef.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "google_breakpad/processor/code_modules.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/range_map.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
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.
|
||||
BasicCodeModules(const CodeModules *that, MergeRangeStrategy strategy);
|
||||
|
||||
virtual ~BasicCodeModules();
|
||||
|
||||
// See code_modules.h for descriptions of these methods.
|
||||
virtual unsigned int module_count() const;
|
||||
virtual const CodeModule* GetModuleForAddress(uint64_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;
|
||||
virtual std::vector<linked_ptr<const CodeModule> >
|
||||
GetShrunkRangeModules() const;
|
||||
|
||||
protected:
|
||||
BasicCodeModules();
|
||||
|
||||
// The base address of the main module.
|
||||
uint64_t main_address_;
|
||||
|
||||
// The map used to contain each CodeModule, keyed by each CodeModule's
|
||||
// address range.
|
||||
RangeMap<uint64_t, linked_ptr<const CodeModule> > map_;
|
||||
|
||||
// A vector of all CodeModules that were shrunk downs due to
|
||||
// address range conflicts.
|
||||
std::vector<linked_ptr<const CodeModule> > shrunk_range_modules_;
|
||||
|
||||
private:
|
||||
// Disallow copy constructor and assignment operator.
|
||||
BasicCodeModules(const BasicCodeModules& that);
|
||||
void operator=(const BasicCodeModules& that);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_BASIC_CODE_MODULES_H__
|
||||
930
externals/breakpad/src/processor/basic_source_line_resolver.cc
vendored
Normal file
930
externals/breakpad/src/processor/basic_source_line_resolver.cc
vendored
Normal file
|
|
@ -0,0 +1,930 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#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::deque;
|
||||
using std::make_pair;
|
||||
using std::map;
|
||||
using std::unique_ptr;
|
||||
using std::vector;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#define strtok_r strtok_s
|
||||
#endif
|
||||
#define strtoull _strtoui64
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
// Utility function to tokenize given the presence of an optional initial
|
||||
// field. In this case, optional_field is the expected string for the optional
|
||||
// field, and max_tokens is the maximum number of tokens including the optional
|
||||
// field. Refer to the documentation for Tokenize for descriptions of the other
|
||||
// arguments.
|
||||
bool TokenizeWithOptionalField(char* line,
|
||||
const char* optional_field,
|
||||
const char* separators,
|
||||
int max_tokens,
|
||||
vector<char*>* tokens) {
|
||||
// First tokenize assuming the optional field is not present. If we then see
|
||||
// the optional field, additionally tokenize the last token into two tokens.
|
||||
if (!Tokenize(line, separators, max_tokens - 1, tokens)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(tokens->front(), optional_field) == 0) {
|
||||
// The optional field is present. Split the last token in two to recover the
|
||||
// field prior to the last.
|
||||
vector<char*> last_tokens;
|
||||
if (!Tokenize(tokens->back(), separators, 2, &last_tokens)) {
|
||||
return false;
|
||||
}
|
||||
// Replace the previous last token with the two new tokens.
|
||||
tokens->pop_back();
|
||||
tokens->push_back(last_tokens[0]);
|
||||
tokens->push_back(last_tokens[1]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static const char* kWhitespace = " \r\n";
|
||||
static const int kMaxErrorsPrinted = 5;
|
||||
static const int kMaxErrorsBeforeBailing = 100;
|
||||
|
||||
BasicSourceLineResolver::BasicSourceLineResolver() :
|
||||
SourceLineResolverBase(new BasicModuleFactory) { }
|
||||
|
||||
// static
|
||||
void BasicSourceLineResolver::Module::LogParseError(
|
||||
const string& message,
|
||||
int line_number,
|
||||
int* num_errors) {
|
||||
if (++(*num_errors) <= kMaxErrorsPrinted) {
|
||||
if (line_number > 0) {
|
||||
BPLOG(ERROR) << "Line " << line_number << ": " << message;
|
||||
} else {
|
||||
BPLOG(ERROR) << message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::LoadMapFromMemory(
|
||||
char* memory_buffer,
|
||||
size_t memory_buffer_size) {
|
||||
linked_ptr<Function> cur_func;
|
||||
int line_number = 0;
|
||||
int num_errors = 0;
|
||||
int inline_num_errors = 0;
|
||||
char* save_ptr;
|
||||
|
||||
// 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 (memory_buffer_size == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Make sure the last character is null terminator.
|
||||
size_t last_null_terminator = memory_buffer_size - 1;
|
||||
if (memory_buffer[last_null_terminator] != '\0') {
|
||||
memory_buffer[last_null_terminator] = '\0';
|
||||
}
|
||||
|
||||
// Skip any null terminators at the end of the memory buffer, and make sure
|
||||
// there are no other null terminators in the middle of the memory buffer.
|
||||
bool has_null_terminator_in_the_middle = false;
|
||||
while (last_null_terminator > 0 &&
|
||||
memory_buffer[last_null_terminator - 1] == '\0') {
|
||||
last_null_terminator--;
|
||||
}
|
||||
for (size_t i = 0; i < last_null_terminator; i++) {
|
||||
if (memory_buffer[i] == '\0') {
|
||||
memory_buffer[i] = '_';
|
||||
has_null_terminator_in_the_middle = true;
|
||||
}
|
||||
}
|
||||
if (has_null_terminator_in_the_middle) {
|
||||
LogParseError(
|
||||
"Null terminator is not expected in the middle of the symbol data",
|
||||
line_number,
|
||||
&num_errors);
|
||||
}
|
||||
|
||||
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)) {
|
||||
LogParseError("ParseFile on buffer failed", line_number, &num_errors);
|
||||
}
|
||||
} else if (strncmp(buffer, "STACK ", 6) == 0) {
|
||||
if (!ParseStackInfo(buffer)) {
|
||||
LogParseError("ParseStackInfo failed", line_number, &num_errors);
|
||||
}
|
||||
} else if (strncmp(buffer, "FUNC ", 5) == 0) {
|
||||
cur_func.reset(ParseFunction(buffer));
|
||||
if (!cur_func.get()) {
|
||||
LogParseError("ParseFunction failed", line_number, &num_errors);
|
||||
} else {
|
||||
// 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)) {
|
||||
LogParseError("ParsePublicSymbol failed", line_number, &num_errors);
|
||||
}
|
||||
} 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 (strncmp(buffer, "INLINE ", 7) == 0) {
|
||||
linked_ptr<Inline> in = ParseInline(buffer);
|
||||
if (!in.get())
|
||||
LogParseError("ParseInline failed", line_number, &inline_num_errors);
|
||||
else
|
||||
cur_func->AppendInline(in);
|
||||
} else if (strncmp(buffer, "INLINE_ORIGIN ", 14) == 0) {
|
||||
if (!ParseInlineOrigin(buffer)) {
|
||||
LogParseError("ParseInlineOrigin failed", line_number,
|
||||
&inline_num_errors);
|
||||
}
|
||||
} else {
|
||||
if (!cur_func.get()) {
|
||||
LogParseError("Found source line data without a function",
|
||||
line_number, &num_errors);
|
||||
} else {
|
||||
Line* line = ParseLine(buffer);
|
||||
if (!line) {
|
||||
LogParseError("ParseLine failed", line_number, &num_errors);
|
||||
} else {
|
||||
cur_func->lines.StoreRange(line->address, line->size,
|
||||
linked_ptr<Line>(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (num_errors > kMaxErrorsBeforeBailing) {
|
||||
break;
|
||||
}
|
||||
buffer = strtok_r(NULL, "\r\n", &save_ptr);
|
||||
}
|
||||
is_corrupt_ = num_errors > 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void BasicSourceLineResolver::Module::ConstructInlineFrames(
|
||||
StackFrame* frame,
|
||||
MemAddr address,
|
||||
const ContainedRangeMap<uint64_t, linked_ptr<Inline>>& inline_map,
|
||||
deque<unique_ptr<StackFrame>>* inlined_frames) const {
|
||||
vector<const linked_ptr<Inline>*> inlines;
|
||||
if (!inline_map.RetrieveRanges(address, inlines)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const linked_ptr<Inline>* const in : inlines) {
|
||||
unique_ptr<StackFrame> new_frame =
|
||||
unique_ptr<StackFrame>(new StackFrame(*frame));
|
||||
auto origin = inline_origins_.find(in->get()->origin_id);
|
||||
if (origin != inline_origins_.end()) {
|
||||
new_frame->function_name = origin->second->name;
|
||||
} else {
|
||||
new_frame->function_name = "<name omitted>";
|
||||
}
|
||||
|
||||
// Store call site file and line in current frame, which will be updated
|
||||
// later.
|
||||
new_frame->source_line = in->get()->call_site_line;
|
||||
if (in->get()->has_call_site_file_id) {
|
||||
auto file = files_.find(in->get()->call_site_file_id);
|
||||
if (file != files_.end()) {
|
||||
new_frame->source_file_name = file->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Use the starting address of the inlined range as inlined function base.
|
||||
new_frame->function_base = new_frame->module->base_address();
|
||||
for (const auto& range : in->get()->inline_ranges) {
|
||||
if (address >= range.first && address < range.first + range.second) {
|
||||
new_frame->function_base += range.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
new_frame->trust = StackFrame::FRAME_TRUST_INLINE;
|
||||
|
||||
// The inlines vector has an order from innermost entry to outermost entry.
|
||||
// By push_back, we will have inlined_frames with the same order.
|
||||
inlined_frames->push_back(std::move(new_frame));
|
||||
}
|
||||
|
||||
// Update the source file and source line for each inlined frame.
|
||||
if (!inlined_frames->empty()) {
|
||||
string parent_frame_source_file_name = frame->source_file_name;
|
||||
int parent_frame_source_line = frame->source_line;
|
||||
frame->source_file_name = inlined_frames->back()->source_file_name;
|
||||
frame->source_line = inlined_frames->back()->source_line;
|
||||
for (unique_ptr<StackFrame>& inlined_frame : *inlined_frames) {
|
||||
std::swap(inlined_frame->source_file_name, parent_frame_source_file_name);
|
||||
std::swap(inlined_frame->source_line, parent_frame_source_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BasicSourceLineResolver::Module::LookupAddress(
|
||||
StackFrame* frame,
|
||||
deque<unique_ptr<StackFrame>>* inlined_frames) 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,
|
||||
NULL /* delta */, &function_size) &&
|
||||
address >= function_base && address - function_base < function_size) {
|
||||
frame->function_name = func->name;
|
||||
frame->function_base = frame->module->base_address() + function_base;
|
||||
frame->is_multiple = func->is_multiple;
|
||||
|
||||
linked_ptr<Line> line;
|
||||
MemAddr line_base;
|
||||
if (func->lines.RetrieveRange(address, &line, &line_base, NULL /* delta */,
|
||||
NULL /* size */)) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Check if this is inlined function call.
|
||||
if (inlined_frames) {
|
||||
ConstructInlineFrames(frame, address, func->inlines, inlined_frames);
|
||||
}
|
||||
} 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;
|
||||
frame->is_multiple = public_symbol->is_multiple;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
NULL /* delta */, &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,
|
||||
NULL /* delta */, &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) {
|
||||
long index;
|
||||
char* filename;
|
||||
if (SymbolParseHelper::ParseFile(file_line, &index, &filename)) {
|
||||
files_.insert(make_pair(index, string(filename)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParseInlineOrigin(
|
||||
char* inline_origin_line) {
|
||||
bool has_file_id;
|
||||
long origin_id;
|
||||
long source_file_id;
|
||||
char* origin_name;
|
||||
if (SymbolParseHelper::ParseInlineOrigin(inline_origin_line, &has_file_id,
|
||||
&origin_id, &source_file_id,
|
||||
&origin_name)) {
|
||||
inline_origins_.insert(make_pair(
|
||||
origin_id,
|
||||
new InlineOrigin(has_file_id, source_file_id, origin_name)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
linked_ptr<BasicSourceLineResolver::Inline>
|
||||
BasicSourceLineResolver::Module::ParseInline(char* inline_line) {
|
||||
bool has_call_site_file_id;
|
||||
long inline_nest_level;
|
||||
long call_site_line;
|
||||
long call_site_file_id;
|
||||
long origin_id;
|
||||
vector<std::pair<MemAddr, MemAddr>> ranges;
|
||||
if (SymbolParseHelper::ParseInline(inline_line, &has_call_site_file_id,
|
||||
&inline_nest_level, &call_site_line,
|
||||
&call_site_file_id, &origin_id, &ranges)) {
|
||||
return linked_ptr<Inline>(new Inline(has_call_site_file_id,
|
||||
inline_nest_level, call_site_line,
|
||||
call_site_file_id, origin_id, ranges));
|
||||
}
|
||||
return linked_ptr<Inline>();
|
||||
}
|
||||
|
||||
BasicSourceLineResolver::Function*
|
||||
BasicSourceLineResolver::Module::ParseFunction(char* function_line) {
|
||||
bool is_multiple;
|
||||
uint64_t address;
|
||||
uint64_t size;
|
||||
long stack_param_size;
|
||||
char* name;
|
||||
if (SymbolParseHelper::ParseFunction(function_line, &is_multiple, &address,
|
||||
&size, &stack_param_size, &name)) {
|
||||
return new Function(name, address, size, stack_param_size, is_multiple);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BasicSourceLineResolver::Line* BasicSourceLineResolver::Module::ParseLine(
|
||||
char* line_line) {
|
||||
uint64_t address;
|
||||
uint64_t size;
|
||||
long line_number;
|
||||
long source_file;
|
||||
|
||||
if (SymbolParseHelper::ParseLine(line_line, &address, &size, &line_number,
|
||||
&source_file)) {
|
||||
return new Line(address, size, source_file, line_number);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParsePublicSymbol(char* public_line) {
|
||||
bool is_multiple;
|
||||
uint64_t address;
|
||||
long stack_param_size;
|
||||
char* name;
|
||||
|
||||
if (SymbolParseHelper::ParsePublicSymbol(public_line, &is_multiple, &address,
|
||||
&stack_param_size, &name)) {
|
||||
// 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,
|
||||
is_multiple));
|
||||
return public_symbols_.Store(address, symbol);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
uint64_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;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Function::AppendInline(linked_ptr<Inline> in) {
|
||||
// This happends if in's parent wasn't added due to a malformed INLINE record.
|
||||
if (in->inline_nest_level > last_added_inline_nest_level + 1)
|
||||
return false;
|
||||
|
||||
last_added_inline_nest_level = in->inline_nest_level;
|
||||
|
||||
// Store all ranges into current level of inlines.
|
||||
for (auto range : in->inline_ranges)
|
||||
inlines.StoreRange(range.first, range.second, in);
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::ParseFile(char* file_line, long* index,
|
||||
char** filename) {
|
||||
// FILE <id> <filename>
|
||||
assert(strncmp(file_line, "FILE ", 5) == 0);
|
||||
file_line += 5; // skip prefix
|
||||
|
||||
vector<char*> tokens;
|
||||
if (!Tokenize(file_line, kWhitespace, 2, &tokens)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* after_number;
|
||||
*index = strtol(tokens[0], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) || *index < 0 ||
|
||||
*index == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*filename = tokens[1];
|
||||
if (!*filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::ParseInlineOrigin(char* inline_origin_line,
|
||||
bool* has_file_id,
|
||||
long* origin_id,
|
||||
long* file_id,
|
||||
char** name) {
|
||||
// Old INLINE_ORIGIN format:
|
||||
// INLINE_ORIGIN <origin_id> <file_id> <name>
|
||||
// New INLINE_ORIGIN format:
|
||||
// INLINE_ORIGIN <origin_id> <name>
|
||||
assert(strncmp(inline_origin_line, "INLINE_ORIGIN ", 14) == 0);
|
||||
inline_origin_line += 14; // skip prefix
|
||||
vector<char*> tokens;
|
||||
// Split the line into two parts so that the first token is "<origin_id>", and
|
||||
// second token is either "<file_id> <name>"" or "<name>"" depending on the
|
||||
// format version.
|
||||
if (!Tokenize(inline_origin_line, kWhitespace, 2, &tokens)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* after_number;
|
||||
*origin_id = strtol(tokens[0], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) || *origin_id < 0 ||
|
||||
*origin_id == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the field after origin_id is a number, then it's old format.
|
||||
char* remaining_line = tokens[1];
|
||||
*has_file_id = true;
|
||||
for (size_t i = 0;
|
||||
i < strlen(remaining_line) && remaining_line[i] != ' ' && *has_file_id;
|
||||
++i) {
|
||||
// If the file id is -1, it might be an artificial function that doesn't
|
||||
// have file id. So, we consider -1 as a valid special case.
|
||||
if (remaining_line[i] == '-' && i == 0) {
|
||||
continue;
|
||||
}
|
||||
*has_file_id = isdigit(remaining_line[i]);
|
||||
}
|
||||
|
||||
if (*has_file_id) {
|
||||
// If it's old format, split "<file_id> <name>" to {"<field_id>", "<name>"}.
|
||||
if (!Tokenize(remaining_line, kWhitespace, 2, &tokens)) {
|
||||
return false;
|
||||
}
|
||||
*file_id = strtol(tokens[0], &after_number, 10);
|
||||
// If the file id is -1, it might be an artificial function that doesn't
|
||||
// have file id. So, we consider -1 as a valid special case.
|
||||
if (!IsValidAfterNumber(after_number) || *file_id < -1 ||
|
||||
*file_id == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*name = tokens[1];
|
||||
if (!*name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::ParseInline(
|
||||
char* inline_line,
|
||||
bool* has_call_site_file_id,
|
||||
long* inline_nest_level,
|
||||
long* call_site_line,
|
||||
long* call_site_file_id,
|
||||
long* origin_id,
|
||||
vector<std::pair<MemAddr, MemAddr>>* ranges) {
|
||||
// Old INLINE format:
|
||||
// INLINE <inline_nest_level> <call_site_line> <origin_id> [<address> <size>]+
|
||||
// New INLINE format:
|
||||
// INLINE <inline_nest_level> <call_site_line> <call_site_file_id> <origin_id>
|
||||
// [<address> <size>]+
|
||||
assert(strncmp(inline_line, "INLINE ", 7) == 0);
|
||||
inline_line += 7; // skip prefix
|
||||
|
||||
vector<char*> tokens;
|
||||
// Increase max_tokens if necessary.
|
||||
Tokenize(inline_line, kWhitespace, 512, &tokens);
|
||||
|
||||
// Determine the version of INLINE record by parity of the vector length.
|
||||
*has_call_site_file_id = tokens.size() % 2 == 0;
|
||||
|
||||
// The length of the vector should be at least 5.
|
||||
if (tokens.size() < 5) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* after_number;
|
||||
size_t next_idx = 0;
|
||||
|
||||
*inline_nest_level = strtol(tokens[next_idx++], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) || *inline_nest_level < 0 ||
|
||||
*inline_nest_level == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*call_site_line = strtol(tokens[next_idx++], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) || *call_site_line < 0 ||
|
||||
*call_site_line == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*has_call_site_file_id) {
|
||||
*call_site_file_id = strtol(tokens[next_idx++], &after_number, 10);
|
||||
// If the file id is -1, it might be an artificial function that doesn't
|
||||
// have file id. So, we consider -1 as a valid special case.
|
||||
if (!IsValidAfterNumber(after_number) || *call_site_file_id < -1 ||
|
||||
*call_site_file_id == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*origin_id = strtol(tokens[next_idx++], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) || *origin_id < 0 ||
|
||||
*origin_id == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (next_idx < tokens.size()) {
|
||||
MemAddr address = strtoull(tokens[next_idx++], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
address == std::numeric_limits<unsigned long long>::max()) {
|
||||
return false;
|
||||
}
|
||||
MemAddr size = strtoull(tokens[next_idx++], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
size == std::numeric_limits<unsigned long long>::max()) {
|
||||
return false;
|
||||
}
|
||||
ranges->push_back({address, size});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::ParseFunction(char* function_line, bool* is_multiple,
|
||||
uint64_t* address, uint64_t* size,
|
||||
long* stack_param_size, char** name) {
|
||||
// FUNC [<multiple>] <address> <size> <stack_param_size> <name>
|
||||
assert(strncmp(function_line, "FUNC ", 5) == 0);
|
||||
function_line += 5; // skip prefix
|
||||
|
||||
vector<char*> tokens;
|
||||
if (!TokenizeWithOptionalField(function_line, "m", kWhitespace, 5, &tokens)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*is_multiple = strcmp(tokens[0], "m") == 0;
|
||||
int next_token = *is_multiple ? 1 : 0;
|
||||
|
||||
char* after_number;
|
||||
*address = strtoull(tokens[next_token++], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
*address == std::numeric_limits<unsigned long long>::max()) {
|
||||
return false;
|
||||
}
|
||||
*size = strtoull(tokens[next_token++], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
*size == std::numeric_limits<unsigned long long>::max()) {
|
||||
return false;
|
||||
}
|
||||
*stack_param_size = strtol(tokens[next_token++], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
*stack_param_size == std::numeric_limits<long>::max() ||
|
||||
*stack_param_size < 0) {
|
||||
return false;
|
||||
}
|
||||
*name = tokens[next_token++];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::ParseLine(char* line_line, uint64_t* address,
|
||||
uint64_t* size, long* line_number,
|
||||
long* source_file) {
|
||||
// <address> <size> <line number> <source file id>
|
||||
vector<char*> tokens;
|
||||
if (!Tokenize(line_line, kWhitespace, 4, &tokens)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* after_number;
|
||||
*address = strtoull(tokens[0], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
*address == std::numeric_limits<unsigned long long>::max()) {
|
||||
return false;
|
||||
}
|
||||
*size = strtoull(tokens[1], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
*size == std::numeric_limits<unsigned long long>::max()) {
|
||||
return false;
|
||||
}
|
||||
*line_number = strtol(tokens[2], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
*line_number == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
*source_file = strtol(tokens[3], &after_number, 10);
|
||||
if (!IsValidAfterNumber(after_number) || *source_file < 0 ||
|
||||
*source_file == std::numeric_limits<long>::max()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Valid line numbers normally start from 1, however there are functions that
|
||||
// are associated with a source file but not associated with any line number
|
||||
// (block helper function) and for such functions the symbol file contains 0
|
||||
// for the line numbers. Hence, 0 should be treated as a valid line number.
|
||||
// For more information on block helper functions, please, take a look at:
|
||||
// http://clang.llvm.org/docs/Block-ABI-Apple.html
|
||||
if (*line_number < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::ParsePublicSymbol(char* public_line, bool* is_multiple,
|
||||
uint64_t* address,
|
||||
long* stack_param_size,
|
||||
char** name) {
|
||||
// PUBLIC [<multiple>] <address> <stack_param_size> <name>
|
||||
assert(strncmp(public_line, "PUBLIC ", 7) == 0);
|
||||
public_line += 7; // skip prefix
|
||||
|
||||
vector<char*> tokens;
|
||||
if (!TokenizeWithOptionalField(public_line, "m", kWhitespace, 4, &tokens)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*is_multiple = strcmp(tokens[0], "m") == 0;
|
||||
int next_token = *is_multiple ? 1 : 0;
|
||||
|
||||
char* after_number;
|
||||
*address = strtoull(tokens[next_token++], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
*address == std::numeric_limits<unsigned long long>::max()) {
|
||||
return false;
|
||||
}
|
||||
*stack_param_size = strtol(tokens[next_token++], &after_number, 16);
|
||||
if (!IsValidAfterNumber(after_number) ||
|
||||
*stack_param_size == std::numeric_limits<long>::max() ||
|
||||
*stack_param_size < 0) {
|
||||
return false;
|
||||
}
|
||||
*name = tokens[next_token++];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SymbolParseHelper::IsValidAfterNumber(char* after_number) {
|
||||
if (after_number != NULL && strchr(kWhitespace, *after_number) != NULL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
209
externals/breakpad/src/processor/basic_source_line_resolver_types.h
vendored
Normal file
209
externals/breakpad/src/processor/basic_source_line_resolver_types.h
vendored
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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 "common/scoped_ptr.h"
|
||||
#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 "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,
|
||||
bool is_mutiple)
|
||||
: Base(function_name,
|
||||
function_address,
|
||||
code_size,
|
||||
set_parameter_size,
|
||||
is_mutiple),
|
||||
inlines(true),
|
||||
last_added_inline_nest_level(0) {}
|
||||
|
||||
// Append inline into corresponding RangeMap.
|
||||
// This function assumes it's called in the order of reading INLINE records.
|
||||
bool AppendInline(linked_ptr<Inline> in);
|
||||
|
||||
ContainedRangeMap<MemAddr, linked_ptr<Inline>> inlines;
|
||||
RangeMap<MemAddr, linked_ptr<Line>> lines;
|
||||
|
||||
private:
|
||||
typedef SourceLineResolverBase::Function Base;
|
||||
|
||||
// The last added inline_nest_level from INLINE record.
|
||||
int last_added_inline_nest_level;
|
||||
};
|
||||
|
||||
|
||||
class BasicSourceLineResolver::Module : public SourceLineResolverBase::Module {
|
||||
public:
|
||||
explicit Module(const string& name) : name_(name), is_corrupt_(false) { }
|
||||
virtual ~Module() { }
|
||||
|
||||
// Loads a map from the given buffer in char* type.
|
||||
// Does NOT have ownership of memory_buffer.
|
||||
// The passed in |memory buffer| is of size |memory_buffer_size|. If it is
|
||||
// not null terminated, LoadMapFromMemory() will null terminate it by
|
||||
// modifying the passed in buffer.
|
||||
virtual bool LoadMapFromMemory(char* memory_buffer,
|
||||
size_t memory_buffer_size);
|
||||
|
||||
// Tells whether the loaded symbol data is corrupt. Return value is
|
||||
// undefined, if the symbol data hasn't been loaded yet.
|
||||
virtual bool IsCorrupt() const { return is_corrupt_; }
|
||||
|
||||
// Looks up the given relative address, and fills the StackFrame struct
|
||||
// with the result.
|
||||
virtual void LookupAddress(
|
||||
StackFrame* frame,
|
||||
std::deque<std::unique_ptr<StackFrame>>* inlined_frame) const;
|
||||
|
||||
// Construct inlined frames for |frame| and store them in |inline_frames|.
|
||||
// |frame|'s source line and source file name may be updated if an inlined
|
||||
// frame is found inside |frame|. As a result, the innermost inlined frame
|
||||
// will be the first one in |inline_frames|.
|
||||
virtual void ConstructInlineFrames(
|
||||
StackFrame* frame,
|
||||
MemAddr address,
|
||||
const ContainedRangeMap<uint64_t, linked_ptr<Inline>>& inline_map,
|
||||
std::deque<std::unique_ptr<StackFrame>>* inline_frames) 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;
|
||||
|
||||
// Logs parse errors. |*num_errors| is increased every time LogParseError is
|
||||
// called.
|
||||
static void LogParseError(
|
||||
const string& message,
|
||||
int line_number,
|
||||
int* num_errors);
|
||||
|
||||
// Parses a file declaration
|
||||
bool ParseFile(char* file_line);
|
||||
|
||||
// Parses an inline origin declaration.
|
||||
bool ParseInlineOrigin(char* inline_origin_line);
|
||||
|
||||
// Parses an inline declaration.
|
||||
linked_ptr<Inline> ParseInline(char* inline_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_;
|
||||
std::map<int, linked_ptr<InlineOrigin>> inline_origins_;
|
||||
RangeMap< MemAddr, linked_ptr<Function> > functions_;
|
||||
AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_;
|
||||
bool is_corrupt_;
|
||||
|
||||
// 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__
|
||||
1011
externals/breakpad/src/processor/basic_source_line_resolver_unittest.cc
vendored
Normal file
1011
externals/breakpad/src/processor/basic_source_line_resolver_unittest.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
57
externals/breakpad/src/processor/call_stack.cc
vendored
Normal file
57
externals/breakpad/src/processor/call_stack.cc
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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;
|
||||
}
|
||||
tid_ = 0;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
118
externals/breakpad/src/processor/cfi_frame_info-inl.h
vendored
Normal file
118
externals/breakpad/src/processor/cfi_frame_info-inl.h
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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_
|
||||
189
externals/breakpad/src/processor/cfi_frame_info.cc
vendored
Normal file
189
externals/breakpad/src/processor/cfi_frame_info.cc
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "processor/cfi_frame_info.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "processor/postfix_evaluator-inl.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strtok_r strtok_s
|
||||
#endif
|
||||
|
||||
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))
|
||||
continue;
|
||||
(*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<uint32_t>(
|
||||
const RegisterValueMap<uint32_t>& registers,
|
||||
const MemoryRegion& memory,
|
||||
RegisterValueMap<uint32_t>* caller_registers) const;
|
||||
template bool CFIFrameInfo::FindCallerRegs<uint64_t>(
|
||||
const RegisterValueMap<uint64_t>& registers,
|
||||
const MemoryRegion& memory,
|
||||
RegisterValueMap<uint64_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
|
||||
274
externals/breakpad/src/processor/cfi_frame_info.h
vendored
Normal file
274
externals/breakpad/src/processor/cfi_frame_info.h
vendored
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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 "common/using_std_string.h"
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::map;
|
||||
|
||||
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:
|
||||
//
|
||||
// https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md
|
||||
//
|
||||
// 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 uint32_t on machines with 32-bit addresses, or
|
||||
// uint64_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
|
||||
// uint32_t or uint64_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_
|
||||
552
externals/breakpad/src/processor/cfi_frame_info_unittest.cc
vendored
Normal file
552
externals/breakpad/src/processor/cfi_frame_info_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,552 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/using_std_string.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 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, uint64_t());
|
||||
MOCK_CONST_METHOD0(GetSize, uint32_t());
|
||||
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(uint64_t, uint8_t*));
|
||||
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(uint64_t, uint16_t*));
|
||||
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(uint64_t, uint32_t*));
|
||||
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(uint64_t, uint64_t*));
|
||||
MOCK_CONST_METHOD0(Print, void());
|
||||
};
|
||||
|
||||
// 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<uint8_t*>())).Times(0);
|
||||
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<uint16_t*>())).Times(0);
|
||||
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<uint32_t*>())).Times(0);
|
||||
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<uint64_t*>())).Times(0);
|
||||
}
|
||||
|
||||
CFIFrameInfo cfi;
|
||||
MockMemoryRegion memory;
|
||||
CFIFrameInfo::RegisterValueMap<uint64_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<uint64_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<uint64_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<uint64_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<uint64_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<uint64_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<uint64_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<uint64_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<uint64_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<uint64_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<uint64_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<uint64_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<uint64_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_TRUE(cfi.FindCallerRegs<uint64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(caller_registers.end(), caller_registers.find("$r1"));
|
||||
}
|
||||
|
||||
// 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<uint64_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<uint64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
|
||||
cfi.SetCFARule("$temp1 76569129 = $temp1");
|
||||
cfi.SetRARule("$temp1");
|
||||
ASSERT_FALSE(cfi.FindCallerRegs<uint64_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<uint64_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");
|
||||
handler.RegisterRule("reg3", "reg3");
|
||||
registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
|
||||
registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
|
||||
registers["reg-for-reg1"] = 0x06cde8e2ff062481ULL;
|
||||
registers["reg-for-reg2"] = 0xff0c4f76403173e2ULL;
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<uint64_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"]);
|
||||
ASSERT_EQ(caller_registers.end(), caller_registers.find("reg3"));
|
||||
}
|
||||
|
||||
struct SimpleCFIWalkerFixture {
|
||||
struct RawContext {
|
||||
uint64_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<uint64_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.
|
||||
|
||||
uint64_t stack_top = 0x83254944b20d5512ULL;
|
||||
|
||||
// Saved r0.
|
||||
EXPECT_CALL(memory,
|
||||
GetMemoryAtAddress(stack_top, A<uint64_t*>()))
|
||||
.WillRepeatedly(DoAll(SetArgumentPointee<1>(0xdc1975eba8602302ULL),
|
||||
Return(true)));
|
||||
// Saved return address.
|
||||
EXPECT_CALL(memory,
|
||||
GetMemoryAtAddress(stack_top + 16, A<uint64_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);
|
||||
}
|
||||
214
externals/breakpad/src/processor/contained_range_map-inl.h
vendored
Normal file
214
externals/breakpad/src/processor/contained_range_map-inl.h
vendored
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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 (!allow_equal_range_ && 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.
|
||||
ContainedRangeMap* new_child =
|
||||
new ContainedRangeMap(base, entry, child_map, allow_equal_range_);
|
||||
|
||||
map_->insert(MapValue(high, new_child));
|
||||
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>
|
||||
bool ContainedRangeMap<AddressType, EntryType>::RetrieveRanges(
|
||||
const AddressType& address,
|
||||
std::vector<const EntryType*>& entries) const {
|
||||
// If nothing was ever stored, then there's nothing to retrieve.
|
||||
if (!map_)
|
||||
return false;
|
||||
MapIterator iterator = map_->lower_bound(address);
|
||||
if (iterator == map_->end() || address < iterator->second->base_)
|
||||
return false;
|
||||
iterator->second->RetrieveRanges(address, entries);
|
||||
entries.push_back(&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__
|
||||
167
externals/breakpad/src/processor/contained_range_map.h
vendored
Normal file
167
externals/breakpad/src/processor/contained_range_map.h
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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>
|
||||
#include <vector>
|
||||
|
||||
|
||||
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.
|
||||
explicit ContainedRangeMap(bool allow_equal_range = false)
|
||||
: base_(), entry_(), map_(NULL), allow_equal_range_(allow_equal_range) {}
|
||||
|
||||
~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* entries) const;
|
||||
|
||||
// Retrieves the vector of entries encompassing the specified address from the
|
||||
// innermost entry to the outermost entry.
|
||||
bool RetrieveRanges(const AddressType& address,
|
||||
std::vector<const EntryType*>& entries) 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,
|
||||
bool allow_equal_range)
|
||||
: base_(base),
|
||||
entry_(entry),
|
||||
map_(map),
|
||||
allow_equal_range_(allow_equal_range) {}
|
||||
|
||||
// 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_;
|
||||
|
||||
// Whether or not we allow storing an entry into a range that equals to
|
||||
// existing range in the map. Default is false.
|
||||
// If this is true, the newly added range will become a child of existing
|
||||
// innermost range which has same base and size.
|
||||
bool allow_equal_range_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // PROCESSOR_CONTAINED_RANGE_MAP_H__
|
||||
383
externals/breakpad/src/processor/contained_range_map_unittest.cc
vendored
Normal file
383
externals/breakpad/src/processor/contained_range_map_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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;
|
||||
// The first is the querying address, the second is the entries vector result.
|
||||
using EntriesTestPair = std::pair<unsigned, std::vector<int>>;
|
||||
using EntriesTestPairVec = std::vector<EntriesTestPair>;
|
||||
|
||||
static bool RunTestsWithRetrieveRange(
|
||||
const ContainedRangeMap<unsigned int, int>& crm,
|
||||
const int* test_data,
|
||||
unsigned int test_length) {
|
||||
// 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_length; ++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;
|
||||
}
|
||||
|
||||
static bool RunTestsWithRetrieveRangeVector(
|
||||
const ContainedRangeMap<unsigned int, int>& crm,
|
||||
const EntriesTestPairVec& entries_tests) {
|
||||
for (const EntriesTestPair& entries_test : entries_tests) {
|
||||
std::vector<const int*> entries;
|
||||
crm.RetrieveRanges(entries_test.first, entries);
|
||||
if (entries.size() != entries_test.second.size()) {
|
||||
fprintf(stderr,
|
||||
"FAIL: retrieving entries at address %u has size %zu "
|
||||
"expected to have size %zu "
|
||||
"@ %s: %d\n",
|
||||
entries_test.first, entries.size(), entries_test.second.size(),
|
||||
__FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < entries.size(); ++i) {
|
||||
if (*entries[i] != entries_test.second[i]) {
|
||||
fprintf(stderr,
|
||||
"FAIL: retrieving entries at address %u entries[%zu] is %d "
|
||||
"expected %d"
|
||||
"@ %s: %d\n",
|
||||
entries_test.first, i, *entries[i], entries_test.second[i],
|
||||
__FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool RunTestsWithNoEqualRange() {
|
||||
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_length = sizeof(test_data) / sizeof(int);
|
||||
return RunTestsWithRetrieveRange(crm, test_data, test_length);
|
||||
}
|
||||
|
||||
static bool RunTestsWithEqualRange() {
|
||||
ContainedRangeMap<unsigned int, int> crm(true);
|
||||
|
||||
// First, do the StoreRange tests. This validates the containment
|
||||
// rules.
|
||||
ASSERT_TRUE (crm.StoreRange(1, 3, 1));
|
||||
ASSERT_TRUE (crm.StoreRange(1, 3, 2)); // exactly equal to 1
|
||||
ASSERT_TRUE (crm.StoreRange(1, 3, 3)); // exactly equal to 1, 2
|
||||
ASSERT_TRUE (crm.StoreRange(1, 3, 4)); // exactly equal to 1, 2, 3
|
||||
ASSERT_FALSE(crm.StoreRange(0, 3, 5)); // partial overlap.
|
||||
ASSERT_FALSE(crm.StoreRange(2, 3, 6)); // partial overlap.
|
||||
|
||||
ASSERT_TRUE (crm.StoreRange(5, 3, 7));
|
||||
ASSERT_TRUE (crm.StoreRange(5, 3, 8)); // exactly equal to 7
|
||||
ASSERT_TRUE (crm.StoreRange(5, 3, 9)); // exactly equal to 7, 8
|
||||
ASSERT_TRUE (crm.StoreRange(5, 4, 10)); // encompasses 7, 8, 9
|
||||
ASSERT_TRUE (crm.StoreRange(5, 5, 11)); // encompasses 7, 8, 9, 10
|
||||
|
||||
ASSERT_TRUE (crm.StoreRange(10, 3, 12));
|
||||
ASSERT_TRUE (crm.StoreRange(10, 3, 13)); // exactly equal to 12
|
||||
ASSERT_TRUE (crm.StoreRange(11, 2, 14)); // encompasses by 12
|
||||
ASSERT_TRUE (crm.StoreRange(11, 1, 15)); // encompasses by 12, 13
|
||||
|
||||
ASSERT_TRUE (crm.StoreRange(14, 3, 16));
|
||||
ASSERT_TRUE (crm.StoreRange(14, 3, 17)); // exactly equal to 14
|
||||
ASSERT_TRUE (crm.StoreRange(14, 1, 18)); // encompasses by 14, 15
|
||||
ASSERT_TRUE (crm.StoreRange(14, 2, 19)); // encompasses by 14, 15 and encompasses 16
|
||||
ASSERT_TRUE (crm.StoreRange(14, 1, 20)); // exactly equal to 18
|
||||
ASSERT_TRUE (crm.StoreRange(14, 2, 21)); // exactly equal to 19
|
||||
|
||||
// Each element in test_data contains the expected result when calling
|
||||
// RetrieveRange on an address.
|
||||
const int test_data[] = {
|
||||
0, // 0
|
||||
4, // 1
|
||||
4, // 2
|
||||
4, // 3
|
||||
0, // 4
|
||||
9, // 5
|
||||
9, // 6
|
||||
9, // 7
|
||||
10, // 8
|
||||
11, // 9
|
||||
13, // 10
|
||||
15, // 11
|
||||
14, // 12
|
||||
0, // 13
|
||||
20, // 14
|
||||
21, // 15
|
||||
17, // 16
|
||||
0, // 17
|
||||
};
|
||||
unsigned int test_length = sizeof(test_data) / sizeof(int);
|
||||
EntriesTestPairVec entries_tests = {
|
||||
{0, {}},
|
||||
{1, {4, 3, 2, 1}},
|
||||
{2, {4, 3, 2, 1}},
|
||||
{3, {4, 3, 2, 1}},
|
||||
{4, {}},
|
||||
{5, {9, 8, 7, 10, 11}},
|
||||
{6, {9, 8, 7, 10, 11}},
|
||||
{7, {9, 8, 7, 10, 11}},
|
||||
{8, {10, 11}},
|
||||
{9, {11}},
|
||||
{10, {13, 12}},
|
||||
{11, {15, 14, 13, 12}},
|
||||
{12, {14, 13, 12}},
|
||||
{13, {}},
|
||||
{14, {20, 18, 21, 19, 17, 16}},
|
||||
{15, {21, 19, 17, 16}},
|
||||
{16, {17, 16}},
|
||||
{17, {}},
|
||||
};
|
||||
return RunTestsWithRetrieveRange(crm, test_data, test_length) &&
|
||||
RunTestsWithRetrieveRangeVector(crm, entries_tests);
|
||||
}
|
||||
|
||||
static bool RunTests() {
|
||||
return RunTestsWithNoEqualRange() && RunTestsWithEqualRange();
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
BPLOG_INIT(&argc, &argv);
|
||||
|
||||
return RunTests() ? 0 : 1;
|
||||
}
|
||||
70
externals/breakpad/src/processor/convert_old_arm64_context.cc
vendored
Normal file
70
externals/breakpad/src/processor/convert_old_arm64_context.cc
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "processor/convert_old_arm64_context.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
void ConvertOldARM64Context(const MDRawContextARM64_Old& old,
|
||||
MDRawContextARM64* context) {
|
||||
context->context_flags = MD_CONTEXT_ARM64;
|
||||
if (old.context_flags & MD_CONTEXT_ARM64_INTEGER_OLD) {
|
||||
context->context_flags |=
|
||||
MD_CONTEXT_ARM64_INTEGER | MD_CONTEXT_ARM64_CONTROL;
|
||||
}
|
||||
if (old.context_flags & MD_CONTEXT_ARM64_FLOATING_POINT_OLD) {
|
||||
context->context_flags |= MD_CONTEXT_ARM64_FLOATING_POINT;
|
||||
}
|
||||
|
||||
context->cpsr = old.cpsr;
|
||||
|
||||
static_assert(sizeof(old.iregs) == sizeof(context->iregs),
|
||||
"iregs size mismatch");
|
||||
memcpy(context->iregs, old.iregs, sizeof(context->iregs));
|
||||
|
||||
static_assert(sizeof(old.float_save.regs) == sizeof(context->float_save.regs),
|
||||
"float_save.regs size mismatch");
|
||||
memcpy(context->float_save.regs,
|
||||
old.float_save.regs,
|
||||
sizeof(context->float_save.regs));
|
||||
context->float_save.fpcr = old.float_save.fpcr;
|
||||
context->float_save.fpsr = old.float_save.fpsr;
|
||||
|
||||
memset(context->bcr, 0, sizeof(context->bcr));
|
||||
memset(context->bvr, 0, sizeof(context->bvr));
|
||||
memset(context->wcr, 0, sizeof(context->wcr));
|
||||
memset(context->wvr, 0, sizeof(context->wvr));
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
41
externals/breakpad/src/processor/convert_old_arm64_context.h
vendored
Normal file
41
externals/breakpad/src/processor/convert_old_arm64_context.h
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2018 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifndef PROCESSOR_CONVERT_OLD_ARM64_CONTEXT_H__
|
||||
#define PROCESSOR_CONVERT_OLD_ARM64_CONTEXT_H__
|
||||
|
||||
#include "google_breakpad/common/minidump_cpu_arm64.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
void ConvertOldARM64Context(const MDRawContextARM64_Old& old,
|
||||
MDRawContextARM64* context);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_CONVERT_OLD_ARM64_CONTEXT_H__
|
||||
487
externals/breakpad/src/processor/disassembler_objdump.cc
vendored
Normal file
487
externals/breakpad/src/processor/disassembler_objdump.cc
vendored
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
// Copyright (c) 2022, Google LLC
|
||||
//
|
||||
// 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 LLC 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_objdump.: Disassembler that invokes objdump for disassembly.
|
||||
//
|
||||
// Author: Mark Brand
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "processor/disassembler_objdump.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <array>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/scoped_pipe.h"
|
||||
#include "common/linux/scoped_tmpfile.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace {
|
||||
|
||||
const size_t kMaxX86InstructionLength = 15;
|
||||
|
||||
bool IsInstructionPrefix(const string& token) {
|
||||
if (token == "lock" || token == "rep" || token == "repz" ||
|
||||
token == "repnz") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsOperandSize(const string& token) {
|
||||
if (token == "BYTE" || token == "WORD" || token == "DWORD" ||
|
||||
token == "QWORD" || token == "PTR") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetSegmentAddressX86(const DumpContext& context, string segment_name,
|
||||
uint64_t& address) {
|
||||
if (segment_name == "ds") {
|
||||
address = context.GetContextX86()->ds;
|
||||
} else if (segment_name == "es") {
|
||||
address = context.GetContextX86()->es;
|
||||
} else if (segment_name == "fs") {
|
||||
address = context.GetContextX86()->fs;
|
||||
} else if (segment_name == "gs") {
|
||||
address = context.GetContextX86()->gs;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported segment register: " << segment_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetSegmentAddressAMD64(const DumpContext& context, string segment_name,
|
||||
uint64_t& address) {
|
||||
if (segment_name == "ds") {
|
||||
address = 0;
|
||||
} else if (segment_name == "es") {
|
||||
address = 0;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported segment register: " << segment_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetSegmentAddress(const DumpContext& context, string segment_name,
|
||||
uint64_t& address) {
|
||||
if (context.GetContextCPU() == MD_CONTEXT_X86) {
|
||||
return GetSegmentAddressX86(context, segment_name, address);
|
||||
} else if (context.GetContextCPU() == MD_CONTEXT_AMD64) {
|
||||
return GetSegmentAddressAMD64(context, segment_name, address);
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported architecture for GetSegmentAddress\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetRegisterValueX86(const DumpContext& context, string register_name,
|
||||
uint64_t& value) {
|
||||
if (register_name == "eax") {
|
||||
value = context.GetContextX86()->eax;
|
||||
} else if (register_name == "ebx") {
|
||||
value = context.GetContextX86()->ebx;
|
||||
} else if (register_name == "ecx") {
|
||||
value = context.GetContextX86()->ecx;
|
||||
} else if (register_name == "edx") {
|
||||
value = context.GetContextX86()->edx;
|
||||
} else if (register_name == "edi") {
|
||||
value = context.GetContextX86()->edi;
|
||||
} else if (register_name == "esi") {
|
||||
value = context.GetContextX86()->esi;
|
||||
} else if (register_name == "ebp") {
|
||||
value = context.GetContextX86()->ebp;
|
||||
} else if (register_name == "esp") {
|
||||
value = context.GetContextX86()->esp;
|
||||
} else if (register_name == "eip") {
|
||||
value = context.GetContextX86()->eip;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported register: " << register_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetRegisterValueAMD64(const DumpContext& context, string register_name,
|
||||
uint64_t& value) {
|
||||
if (register_name == "rax") {
|
||||
value = context.GetContextAMD64()->rax;
|
||||
} else if (register_name == "rbx") {
|
||||
value = context.GetContextAMD64()->rbx;
|
||||
} else if (register_name == "rcx") {
|
||||
value = context.GetContextAMD64()->rcx;
|
||||
} else if (register_name == "rdx") {
|
||||
value = context.GetContextAMD64()->rdx;
|
||||
} else if (register_name == "rdi") {
|
||||
value = context.GetContextAMD64()->rdi;
|
||||
} else if (register_name == "rsi") {
|
||||
value = context.GetContextAMD64()->rsi;
|
||||
} else if (register_name == "rbp") {
|
||||
value = context.GetContextAMD64()->rbp;
|
||||
} else if (register_name == "rsp") {
|
||||
value = context.GetContextAMD64()->rsp;
|
||||
} else if (register_name == "r8") {
|
||||
value = context.GetContextAMD64()->r8;
|
||||
} else if (register_name == "r9") {
|
||||
value = context.GetContextAMD64()->r9;
|
||||
} else if (register_name == "r10") {
|
||||
value = context.GetContextAMD64()->r10;
|
||||
} else if (register_name == "r11") {
|
||||
value = context.GetContextAMD64()->r11;
|
||||
} else if (register_name == "r12") {
|
||||
value = context.GetContextAMD64()->r12;
|
||||
} else if (register_name == "r13") {
|
||||
value = context.GetContextAMD64()->r13;
|
||||
} else if (register_name == "r14") {
|
||||
value = context.GetContextAMD64()->r14;
|
||||
} else if (register_name == "r15") {
|
||||
value = context.GetContextAMD64()->r15;
|
||||
} else if (register_name == "rip") {
|
||||
value = context.GetContextAMD64()->rip;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported register: " << register_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Lookup the value of `register_name` in `context`, store it into `value` on
|
||||
// success.
|
||||
// Support for non-full-size registers not implemented, since we're only using
|
||||
// this to evaluate address expressions.
|
||||
bool GetRegisterValue(const DumpContext& context, string register_name,
|
||||
uint64_t& value) {
|
||||
if (context.GetContextCPU() == MD_CONTEXT_X86) {
|
||||
return GetRegisterValueX86(context, register_name, value);
|
||||
} else if (context.GetContextCPU() == MD_CONTEXT_AMD64) {
|
||||
return GetRegisterValueAMD64(context, register_name, value);
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported architecture for GetRegisterValue\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
bool DisassemblerObjdump::DisassembleInstruction(uint32_t cpu,
|
||||
const uint8_t* raw_bytes,
|
||||
unsigned int raw_bytes_len,
|
||||
string& instruction) {
|
||||
// Always initialize outputs
|
||||
instruction = "";
|
||||
|
||||
if (!raw_bytes || raw_bytes_len == 0) {
|
||||
// There's no need to perform any operation in this case, as there's
|
||||
// clearly no instruction there.
|
||||
return false;
|
||||
}
|
||||
|
||||
string architecture;
|
||||
if (cpu == MD_CONTEXT_X86) {
|
||||
architecture = "i386";
|
||||
} else if (cpu == MD_CONTEXT_AMD64) {
|
||||
architecture = "i386:x86-64";
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported architecture.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a temporary file for the raw instruction bytes to pass to
|
||||
// objdump, and write the bytes to the input file.
|
||||
ScopedTmpFile raw_bytes_file;
|
||||
if (!raw_bytes_file.InitData(raw_bytes, raw_bytes_len)) {
|
||||
BPLOG(ERROR) << "Failed creating temporary file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create a pipe to use to read the disassembly back from objdump.
|
||||
ScopedPipe disassembly_pipe;
|
||||
if (!disassembly_pipe.Init()) {
|
||||
BPLOG(ERROR) << "Failed creating pipe for output.";
|
||||
return false;
|
||||
}
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid < 0) {
|
||||
BPLOG(ERROR) << "Fork failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (child_pid == 0) {
|
||||
// In the child process, set up the input and output file descriptors.
|
||||
if (dup2(raw_bytes_file.GetFd(), STDIN_FILENO) < 0 ||
|
||||
disassembly_pipe.Dup2WriteFd(STDOUT_FILENO) < 0 ||
|
||||
disassembly_pipe.Dup2WriteFd(STDERR_FILENO) < 0) {
|
||||
BPLOG(ERROR) << "Failed dup'ing file descriptors.";
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// We need to close the read end of the pipe in the child process so that
|
||||
// when the parent closes it, the pipe is disconnected.
|
||||
disassembly_pipe.CloseReadFd();
|
||||
|
||||
// We use "/proc/self/fd/0" here to allow objdump to parse an unnamed file,
|
||||
// since objdump does not have a mode to read from stdin. This cannot be
|
||||
// used with a pipe, since objdump requires that the input is a standard
|
||||
// file.
|
||||
execlp("objdump", "objdump", "-D", "--no-show-raw-insn", "-b", "binary",
|
||||
"-M", "intel", "-m", architecture.c_str(), "/proc/self/fd/0",
|
||||
nullptr);
|
||||
|
||||
BPLOG(ERROR) << "Failed to exec objdump.";
|
||||
exit(-1);
|
||||
} else {
|
||||
// In the parent process, parse the objdump output.
|
||||
|
||||
// Match the instruction line, from:
|
||||
// 0: lock cmpxchg DWORD PTR [esi+0x10],eax
|
||||
// extract the string "lock cmpxchg DWORD PTR [esi+0x10],eax"
|
||||
std::regex instruction_regex(
|
||||
"^\\s+[0-9a-f]+:\\s+" // " 0:"
|
||||
"((?:\\s*\\S*)+)$"); // "lock cmpxchg..."
|
||||
|
||||
std::string line;
|
||||
std::smatch match;
|
||||
while (disassembly_pipe.ReadLine(line)) {
|
||||
if (std::regex_match(line, match, instruction_regex)) {
|
||||
instruction = match[1].str();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Close the read pipe so that objdump will exit (in case we broke out of
|
||||
// the loop above before reading all of the output).
|
||||
disassembly_pipe.CloseReadFd();
|
||||
|
||||
// Now wait for objdump to exit.
|
||||
int status = 0;
|
||||
HANDLE_EINTR(waitpid(child_pid, &status, 0));
|
||||
|
||||
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
||||
BPLOG(ERROR) << "objdump didn't run successfully.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (instruction == "") {
|
||||
BPLOG(ERROR) << "Failed to find instruction in objdump output.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool DisassemblerObjdump::TokenizeInstruction(const string& instruction,
|
||||
string& operation, string& dest,
|
||||
string& src) {
|
||||
// Always initialize outputs.
|
||||
operation = "";
|
||||
dest = "";
|
||||
src = "";
|
||||
|
||||
// Split the instruction into tokens by either whitespace or comma.
|
||||
std::regex token_regex("((?:[^\\s,]+)|,)(?:\\s)*");
|
||||
std::sregex_iterator tokens_begin(instruction.begin(), instruction.end(),
|
||||
token_regex);
|
||||
|
||||
bool found_comma = false;
|
||||
for (auto tokens_iter = tokens_begin; tokens_iter != std::sregex_iterator();
|
||||
++tokens_iter) {
|
||||
auto token = (*tokens_iter)[1].str();
|
||||
if (operation.size() == 0) {
|
||||
if (IsInstructionPrefix(token))
|
||||
continue;
|
||||
operation = token;
|
||||
} else if (dest.size() == 0) {
|
||||
if (IsOperandSize(token))
|
||||
continue;
|
||||
dest = token;
|
||||
} else if (!found_comma) {
|
||||
if (token == ",") {
|
||||
found_comma = true;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Failed to parse operands from objdump output, expected"
|
||||
" comma but found \""
|
||||
<< token << "\"";
|
||||
return false;
|
||||
}
|
||||
} else if (src.size() == 0) {
|
||||
if (IsOperandSize(token))
|
||||
continue;
|
||||
src = token;
|
||||
} else {
|
||||
if (token == ",") {
|
||||
BPLOG(ERROR) << "Failed to parse operands from objdump output, found "
|
||||
"unexpected comma after last operand.";
|
||||
return false;
|
||||
} else {
|
||||
// We just ignore other junk after the last operand unless it's a
|
||||
// comma, which would indicate we're probably still in the middle
|
||||
// of the operands and something has gone wrong
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_comma && src.size() == 0) {
|
||||
BPLOG(ERROR) << "Failed to parse operands from objdump output, found comma "
|
||||
"but no src operand.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool DisassemblerObjdump::CalculateAddress(const DumpContext& context,
|
||||
const string& expression,
|
||||
uint64_t& address) {
|
||||
address = 0;
|
||||
|
||||
// Extract the components of the expression.
|
||||
// fs:[esi+edi*4+0x80] -> ["fs", "esi", "edi", "4", "-", "0x80"]
|
||||
std::regex expression_regex(
|
||||
"^(?:(\\ws):)?" // "fs:"
|
||||
"\\[(\\w+)" // "[esi"
|
||||
"(?:\\+(\\w+)(?:\\*(\\d+)))?" // "+edi*4"
|
||||
"(?:([\\+-])(0x[0-9a-f]+))?" // "-0x80"
|
||||
"\\]$"); // "]"
|
||||
|
||||
std::smatch match;
|
||||
if (!std::regex_match(expression, match, expression_regex) ||
|
||||
match.size() != 7) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string segment_name = match[1].str();
|
||||
string register_name = match[2].str();
|
||||
string index_name = match[3].str();
|
||||
string index_stride = match[4].str();
|
||||
string offset_sign = match[5].str();
|
||||
string offset = match[6].str();
|
||||
|
||||
uint64_t segment_address = 0;
|
||||
uint64_t register_value = 0;
|
||||
uint64_t index_value = 0;
|
||||
uint64_t index_stride_value = 1;
|
||||
uint64_t offset_value = 0;
|
||||
|
||||
if (segment_name.size() &&
|
||||
!GetSegmentAddress(context, segment_name, segment_address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetRegisterValue(context, register_name, register_value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index_name.size() &&
|
||||
!GetRegisterValue(context, index_name, index_value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index_stride.size()) {
|
||||
index_stride_value = strtoull(index_stride.c_str(), nullptr, 0);
|
||||
}
|
||||
|
||||
if (offset.size()) {
|
||||
offset_value = strtoull(offset.c_str(), nullptr, 0);
|
||||
}
|
||||
|
||||
address =
|
||||
segment_address + register_value + (index_value * index_stride_value);
|
||||
if (offset_sign == "+") {
|
||||
address += offset_value;
|
||||
} else if (offset_sign == "-") {
|
||||
address -= offset_value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DisassemblerObjdump::DisassemblerObjdump(const uint32_t cpu,
|
||||
const MemoryRegion* memory_region,
|
||||
uint64_t address) {
|
||||
if (address < memory_region->GetBase() ||
|
||||
memory_region->GetBase() + memory_region->GetSize() <= address) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t ip_bytes[kMaxX86InstructionLength] = {0};
|
||||
size_t ip_bytes_length;
|
||||
for (ip_bytes_length = 0; ip_bytes_length < kMaxX86InstructionLength;
|
||||
++ip_bytes_length) {
|
||||
// We have to read byte-by-byte here, since we still want to try and
|
||||
// disassemble an instruction even if we don't have enough bytes.
|
||||
if (!memory_region->GetMemoryAtAddress(address + ip_bytes_length,
|
||||
&ip_bytes[ip_bytes_length])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string instruction;
|
||||
if (!DisassembleInstruction(cpu, ip_bytes, kMaxX86InstructionLength,
|
||||
instruction)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TokenizeInstruction(instruction, operation_, dest_, src_)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool DisassemblerObjdump::CalculateSrcAddress(const DumpContext& context,
|
||||
uint64_t& address) {
|
||||
return CalculateAddress(context, src_, address);
|
||||
}
|
||||
|
||||
bool DisassemblerObjdump::CalculateDestAddress(const DumpContext& context,
|
||||
uint64_t& address) {
|
||||
return CalculateAddress(context, dest_, address);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
142
externals/breakpad/src/processor/disassembler_objdump.h
vendored
Normal file
142
externals/breakpad/src/processor/disassembler_objdump.h
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) 2022, Google LLC
|
||||
//
|
||||
// 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 LLC 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_objdump.h: Disassembler that invokes objdump for disassembly.
|
||||
//
|
||||
// Author: Mark Brand
|
||||
|
||||
#ifndef GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_OBJDUMP_H_
|
||||
#define GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_OBJDUMP_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/processor/dump_context.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Uses objdump to disassemble a single instruction.
|
||||
//
|
||||
// Currently supports disassembly for x86 and x86_64 on linux hosts only; on
|
||||
// unsupported platform or for unsupported architectures disassembly will fail.
|
||||
//
|
||||
// If disassembly is successful, then this allows extracting the instruction
|
||||
// opcode, source and destination operands, and computing the source and
|
||||
// destination addresses for instructions that operate on memory.
|
||||
//
|
||||
// Example:
|
||||
// DisassemblerObjdump disassembler(context->GetContextCPU(), memory_region,
|
||||
// instruction_ptr);
|
||||
// if (disassembler.IsValid()) {
|
||||
// uint64_t src_address = 0;
|
||||
// std::cerr << disassembler.operation() << " " << disassembler.src()
|
||||
// << ", " << disassembler.dest() << std::endl;
|
||||
// if (disassembler.CalculateSrcAddress(*context, src_address)) {
|
||||
// std::cerr << "[src_address = " << std::hex << src_address << "]\n";
|
||||
// }
|
||||
// }
|
||||
class DisassemblerObjdump {
|
||||
public:
|
||||
// Construct an ObjdumpDisassembler for the provided `cpu` type, where this is
|
||||
// one of MD_CONTEXT_X86 or MD_CONTEXT_AMD64. Provided that `address` is
|
||||
// within `memory_region`, and the memory referenced is a valid instruction,
|
||||
// this will then be initialized with the disassembly for that instruction.
|
||||
DisassemblerObjdump(uint32_t cpu,
|
||||
const MemoryRegion* memory_region,
|
||||
uint64_t address);
|
||||
~DisassemblerObjdump() = default;
|
||||
|
||||
// If the source operand of the instruction is a memory operand, compute the
|
||||
// address referred to by the operand, and store this in `address`. On success
|
||||
// returns true, otherwise (if computation fails, or if the source operand is
|
||||
// not a memory operand) returns false and sets `address` to 0.
|
||||
bool CalculateSrcAddress(const DumpContext& context, uint64_t& address);
|
||||
|
||||
// If the destination operand of the instruction is a memory operand, compute
|
||||
// the address referred to by the operand, and store this in `address`. On
|
||||
// success returns true, otherwise (if computation fails, or if the source
|
||||
// operand is not a memory operand) returns false and sets `address` to 0.
|
||||
bool CalculateDestAddress(const DumpContext& context, uint64_t& address);
|
||||
|
||||
// If the instruction was disassembled successfully, this will be true.
|
||||
bool IsValid() const { return operation_.size() != 0; }
|
||||
|
||||
// Returns the operation part of the disassembly, without any prefixes:
|
||||
// "pop" eax
|
||||
// lock "xchg" eax, edx
|
||||
const string& operation() const { return operation_; }
|
||||
|
||||
// Returns the destination operand of the disassembly, without memory operand
|
||||
// size prefixes:
|
||||
// mov DWORD PTR "[rax + 16]", edx
|
||||
const string& dest() const { return dest_; }
|
||||
|
||||
// Returns the source operand of the disassembly, without memory operand
|
||||
// size prefixes:
|
||||
// mov rax, QWORD PTR "[rdx]"
|
||||
const string& src() const { return src_; }
|
||||
|
||||
private:
|
||||
friend class DisassemblerObjdumpForTest;
|
||||
|
||||
// Writes out the provided `raw_bytes` to a temporary file, and executes objdump
|
||||
// to disassemble according to `cpu`, which must be either MD_CONTEXT_X86 or
|
||||
// MD_CONTEXT_AMD64. Once objdump has completed, parses out the instruction
|
||||
// string from the first instruction in the output and stores it in
|
||||
// `instruction`.
|
||||
static bool DisassembleInstruction(uint32_t cpu, const uint8_t* raw_bytes,
|
||||
unsigned int raw_bytes_len,
|
||||
string& instruction);
|
||||
|
||||
// Splits an `instruction` into three parts, the "main" `operation` and
|
||||
// the `dest` and `src` operands.
|
||||
// Example:
|
||||
// instruction = "lock cmpxchg QWORD PTR [rdi], rsi"
|
||||
// operation = "cmpxchg", dest = "[rdi]", src = "rsi"
|
||||
static bool TokenizeInstruction(const string& instruction, string& operation,
|
||||
string& dest, string& src);
|
||||
|
||||
// Compute the address referenced by `expression` in `context`.
|
||||
// Supports memory operands in the form
|
||||
// (segment:)[base_reg(+index_reg*index_stride)(+-offset)]
|
||||
// Returns false if evaluation fails, or if the operand is not a supported
|
||||
// memory operand.
|
||||
static bool CalculateAddress(const DumpContext& context,
|
||||
const string& expression,
|
||||
uint64_t& address);
|
||||
|
||||
// The parsed components of the disassembly for the instruction.
|
||||
string operation_ = "";
|
||||
string dest_ = "";
|
||||
string src_ = "";
|
||||
};
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_OBJDUMP_H_
|
||||
468
externals/breakpad/src/processor/disassembler_objdump_unittest.cc
vendored
Normal file
468
externals/breakpad/src/processor/disassembler_objdump_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
// Copyright (c) 2022, Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/common/minidump_cpu_amd64.h"
|
||||
#include "google_breakpad/common/minidump_cpu_x86.h"
|
||||
#include "google_breakpad/processor/dump_context.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "processor/disassembler_objdump.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
class DisassemblerObjdumpForTest : public DisassemblerObjdump {
|
||||
public:
|
||||
using DisassemblerObjdump::CalculateAddress;
|
||||
using DisassemblerObjdump::DisassembleInstruction;
|
||||
using DisassemblerObjdump::TokenizeInstruction;
|
||||
};
|
||||
|
||||
class TestMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
TestMemoryRegion(uint64_t base, std::vector<uint8_t> bytes);
|
||||
~TestMemoryRegion() override = default;
|
||||
|
||||
uint64_t GetBase() const override;
|
||||
uint32_t GetSize() const override;
|
||||
|
||||
bool GetMemoryAtAddress(uint64_t address, uint8_t* value) const override;
|
||||
bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const override;
|
||||
bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const override;
|
||||
bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const override;
|
||||
|
||||
void Print() const override;
|
||||
|
||||
private:
|
||||
uint64_t base_;
|
||||
std::vector<uint8_t> bytes_;
|
||||
};
|
||||
|
||||
TestMemoryRegion::TestMemoryRegion(uint64_t address, std::vector<uint8_t> bytes)
|
||||
: base_(address), bytes_(bytes) {}
|
||||
|
||||
uint64_t TestMemoryRegion::GetBase() const {
|
||||
return base_;
|
||||
}
|
||||
|
||||
uint32_t TestMemoryRegion::GetSize() const {
|
||||
return static_cast<uint32_t>(bytes_.size());
|
||||
}
|
||||
|
||||
bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint8_t* value) const {
|
||||
if (address < GetBase() ||
|
||||
address + sizeof(uint8_t) > GetBase() + GetSize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(value, &bytes_[address - GetBase()], sizeof(uint8_t));
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't use the following functions, so no need to implement.
|
||||
bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint16_t* value) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint32_t* value) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint64_t* value) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void TestMemoryRegion::Print() const {}
|
||||
|
||||
const uint32_t kX86TestDs = 0x01000000;
|
||||
const uint32_t kX86TestEs = 0x02000000;
|
||||
const uint32_t kX86TestFs = 0x03000000;
|
||||
const uint32_t kX86TestGs = 0x04000000;
|
||||
const uint32_t kX86TestEax = 0x00010101;
|
||||
const uint32_t kX86TestEbx = 0x00020202;
|
||||
const uint32_t kX86TestEcx = 0x00030303;
|
||||
const uint32_t kX86TestEdx = 0x00040404;
|
||||
const uint32_t kX86TestEsi = 0x00050505;
|
||||
const uint32_t kX86TestEdi = 0x00060606;
|
||||
const uint32_t kX86TestEsp = 0x00070707;
|
||||
const uint32_t kX86TestEbp = 0x00080808;
|
||||
const uint32_t kX86TestEip = 0x23230000;
|
||||
|
||||
const uint64_t kAMD64TestRax = 0x0000010101010101ul;
|
||||
const uint64_t kAMD64TestRbx = 0x0000020202020202ul;
|
||||
const uint64_t kAMD64TestRcx = 0x0000030303030303ul;
|
||||
const uint64_t kAMD64TestRdx = 0x0000040404040404ul;
|
||||
const uint64_t kAMD64TestRsi = 0x0000050505050505ul;
|
||||
const uint64_t kAMD64TestRdi = 0x0000060606060606ul;
|
||||
const uint64_t kAMD64TestRsp = 0x0000070707070707ul;
|
||||
const uint64_t kAMD64TestRbp = 0x0000080808080808ul;
|
||||
const uint64_t kAMD64TestR8 = 0x0000090909090909ul;
|
||||
const uint64_t kAMD64TestR9 = 0x00000a0a0a0a0a0aul;
|
||||
const uint64_t kAMD64TestR10 = 0x00000b0b0b0b0b0bul;
|
||||
const uint64_t kAMD64TestR11 = 0x00000c0c0c0c0c0cul;
|
||||
const uint64_t kAMD64TestR12 = 0x00000d0d0d0d0d0dul;
|
||||
const uint64_t kAMD64TestR13 = 0x00000e0e0e0e0e0eul;
|
||||
const uint64_t kAMD64TestR14 = 0x00000f0f0f0f0f0ful;
|
||||
const uint64_t kAMD64TestR15 = 0x0000001010101010ul;
|
||||
const uint64_t kAMD64TestRip = 0x0000000023230000ul;
|
||||
|
||||
class TestDumpContext : public DumpContext {
|
||||
public:
|
||||
TestDumpContext(bool x86_64 = false);
|
||||
~TestDumpContext() override;
|
||||
};
|
||||
|
||||
TestDumpContext::TestDumpContext(bool x86_64) {
|
||||
if (!x86_64) {
|
||||
MDRawContextX86* raw_context = new MDRawContextX86();
|
||||
memset(raw_context, 0, sizeof(*raw_context));
|
||||
|
||||
raw_context->context_flags = MD_CONTEXT_X86_FULL;
|
||||
|
||||
raw_context->ds = kX86TestDs;
|
||||
raw_context->es = kX86TestEs;
|
||||
raw_context->fs = kX86TestFs;
|
||||
raw_context->gs = kX86TestGs;
|
||||
raw_context->eax = kX86TestEax;
|
||||
raw_context->ebx = kX86TestEbx;
|
||||
raw_context->ecx = kX86TestEcx;
|
||||
raw_context->edx = kX86TestEdx;
|
||||
raw_context->esi = kX86TestEsi;
|
||||
raw_context->edi = kX86TestEdi;
|
||||
raw_context->esp = kX86TestEsp;
|
||||
raw_context->ebp = kX86TestEbp;
|
||||
raw_context->eip = kX86TestEip;
|
||||
|
||||
SetContextFlags(raw_context->context_flags);
|
||||
SetContextX86(raw_context);
|
||||
this->valid_ = true;
|
||||
} else {
|
||||
MDRawContextAMD64* raw_context = new MDRawContextAMD64();
|
||||
memset(raw_context, 0, sizeof(*raw_context));
|
||||
|
||||
raw_context->context_flags = MD_CONTEXT_AMD64_FULL;
|
||||
|
||||
raw_context->rax = kAMD64TestRax;
|
||||
raw_context->rbx = kAMD64TestRbx;
|
||||
raw_context->rcx = kAMD64TestRcx;
|
||||
raw_context->rdx = kAMD64TestRdx;
|
||||
raw_context->rsi = kAMD64TestRsi;
|
||||
raw_context->rdi = kAMD64TestRdi;
|
||||
raw_context->rsp = kAMD64TestRsp;
|
||||
raw_context->rbp = kAMD64TestRbp;
|
||||
raw_context->r8 = kAMD64TestR8;
|
||||
raw_context->r9 = kAMD64TestR9;
|
||||
raw_context->r10 = kAMD64TestR10;
|
||||
raw_context->r11 = kAMD64TestR11;
|
||||
raw_context->r12 = kAMD64TestR12;
|
||||
raw_context->r13 = kAMD64TestR13;
|
||||
raw_context->r14 = kAMD64TestR14;
|
||||
raw_context->r15 = kAMD64TestR15;
|
||||
raw_context->rip = kAMD64TestRip;
|
||||
|
||||
SetContextFlags(raw_context->context_flags);
|
||||
SetContextAMD64(raw_context);
|
||||
this->valid_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
TestDumpContext::~TestDumpContext() {
|
||||
FreeContext();
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, DisassembleInstructionX86) {
|
||||
string instruction;
|
||||
ASSERT_FALSE(DisassemblerObjdumpForTest::DisassembleInstruction(
|
||||
MD_CONTEXT_X86, nullptr, 0, instruction));
|
||||
std::vector<uint8_t> pop_eax = {0x58};
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::DisassembleInstruction(
|
||||
MD_CONTEXT_X86, pop_eax.data(), pop_eax.size(), instruction));
|
||||
ASSERT_EQ(instruction, "pop eax");
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, DisassembleInstructionAMD64) {
|
||||
string instruction;
|
||||
ASSERT_FALSE(DisassemblerObjdumpForTest::DisassembleInstruction(
|
||||
MD_CONTEXT_AMD64, nullptr, 0, instruction));
|
||||
std::vector<uint8_t> pop_rax = {0x58};
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::DisassembleInstruction(
|
||||
MD_CONTEXT_AMD64, pop_rax.data(), pop_rax.size(), instruction));
|
||||
ASSERT_EQ(instruction, "pop rax");
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, TokenizeInstruction) {
|
||||
string operation, dest, src;
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"pop eax", operation, dest, src));
|
||||
ASSERT_EQ(operation, "pop");
|
||||
ASSERT_EQ(dest, "eax");
|
||||
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov eax, ebx", operation, dest, src));
|
||||
ASSERT_EQ(operation, "mov");
|
||||
ASSERT_EQ(dest, "eax");
|
||||
ASSERT_EQ(src, "ebx");
|
||||
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"pop rax", operation, dest, src));
|
||||
ASSERT_EQ(operation, "pop");
|
||||
ASSERT_EQ(dest, "rax");
|
||||
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov rax, rbx", operation, dest, src));
|
||||
ASSERT_EQ(operation, "mov");
|
||||
ASSERT_EQ(dest, "rax");
|
||||
ASSERT_EQ(src, "rbx");
|
||||
|
||||
// Test the three parsing failure paths
|
||||
ASSERT_FALSE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov rax,", operation, dest, src));
|
||||
ASSERT_FALSE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov rax rbx", operation, dest, src));
|
||||
ASSERT_FALSE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov rax, rbx, rcx", operation, dest, src));
|
||||
|
||||
// This is of course a nonsense instruction, but test that we do remove
|
||||
// multiple instruction prefixes and can handle multiple memory operands.
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"rep lock mov DWORD PTR rax, QWORD PTR rbx", operation, dest, src));
|
||||
ASSERT_EQ(operation, "mov");
|
||||
ASSERT_EQ(dest, "rax");
|
||||
ASSERT_EQ(src, "rbx");
|
||||
|
||||
// Test that we ignore junk following a valid instruction
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov rax, rbx ; junk here", operation, dest, src));
|
||||
ASSERT_EQ(operation, "mov");
|
||||
ASSERT_EQ(dest, "rax");
|
||||
ASSERT_EQ(src, "rbx");
|
||||
}
|
||||
|
||||
namespace x86 {
|
||||
const TestMemoryRegion load_reg(kX86TestEip, {0x8b, 0x06}); // mov eax, [esi];
|
||||
|
||||
const TestMemoryRegion load_reg_index(kX86TestEip,
|
||||
{0x8b, 0x04,
|
||||
0xbe}); // mov eax, [esi+edi*4];
|
||||
|
||||
const TestMemoryRegion load_reg_offset(kX86TestEip,
|
||||
{0x8b, 0x46,
|
||||
0x10}); // mov eax, [esi+0x10];
|
||||
|
||||
const TestMemoryRegion load_reg_index_offset(
|
||||
kX86TestEip,
|
||||
{0x8b, 0x44, 0xbe, 0xf0}); // mov eax, [esi+edi*4-0x10];
|
||||
|
||||
const TestMemoryRegion rep_stosb(kX86TestEip, {0xf3, 0xaa}); // rep stosb;
|
||||
|
||||
const TestMemoryRegion lock_cmpxchg(kX86TestEip,
|
||||
{0xf0, 0x0f, 0xb1, 0x46,
|
||||
0x10}); // lock cmpxchg [esi + 0x10], eax;
|
||||
|
||||
const TestMemoryRegion call_reg_offset(kX86TestEip,
|
||||
{0xff, 0x96, 0x99, 0x99, 0x99,
|
||||
0x09}); // call [esi+0x9999999];
|
||||
} // namespace x86
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86LoadReg) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg, kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kX86TestEsi);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86LoadRegIndex) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg_index,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kX86TestEsi + (kX86TestEdi * 4));
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86LoadRegOffset) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg_offset,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kX86TestEsi + 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86LoadRegIndexOffset) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg_index_offset,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kX86TestEsi + (kX86TestEdi * 4) - 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86RepStosb) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::rep_stosb,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kX86TestEs + kX86TestEdi);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86LockCmpxchg) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::lock_cmpxchg,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kX86TestEsi + 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86CallRegOffset) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::call_reg_offset,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kX86TestEsi + 0x9999999);
|
||||
}
|
||||
|
||||
namespace amd64 {
|
||||
const TestMemoryRegion load_reg(kAMD64TestRip,
|
||||
{0x48, 0x8b, 0x06}); // mov rax, [rsi];
|
||||
|
||||
const TestMemoryRegion load_reg_index(kAMD64TestRip,
|
||||
{0x48, 0x8b, 0x04,
|
||||
0xbe}); // mov rax, [rsi+rdi*4];
|
||||
|
||||
const TestMemoryRegion load_rip_relative(kAMD64TestRip,
|
||||
{0x48, 0x8b, 0x05, 0x10, 0x00, 0x00,
|
||||
0x00}); // mov rax, [rip+0x10];
|
||||
|
||||
const TestMemoryRegion load_reg_index_offset(
|
||||
kAMD64TestRip,
|
||||
{0x48, 0x8b, 0x44, 0xbe, 0xf0}); // mov rax, [rsi+rdi*4-0x10];
|
||||
|
||||
const TestMemoryRegion rep_stosb(kAMD64TestRip, {0xf3, 0xaa}); // rep stosb;
|
||||
|
||||
const TestMemoryRegion lock_cmpxchg(kAMD64TestRip,
|
||||
{0xf0, 0x48, 0x0f, 0xb1, 0x46,
|
||||
0x10}); // lock cmpxchg [rsi + 0x10], rax;
|
||||
|
||||
const TestMemoryRegion call_reg_offset(kAMD64TestRip,
|
||||
{0xff, 0x96, 0x99, 0x99, 0x99,
|
||||
0x09}); // call [rsi+0x9999999];
|
||||
} // namespace amd64
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64LoadReg) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::load_reg,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kAMD64TestRsi);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64LoadRegIndex) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::load_reg_index,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kAMD64TestRsi + (kAMD64TestRdi * 4));
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64LoadRipRelative) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::load_rip_relative,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kAMD64TestRip + 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64LoadRegIndexOffset) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(),
|
||||
&amd64::load_reg_index_offset, kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kAMD64TestRsi + (kAMD64TestRdi * 4) - 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64RepStosb) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::rep_stosb,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kAMD64TestRdi);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64LockCmpxchg) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::lock_cmpxchg,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kAMD64TestRsi + 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64CallRegOffset) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::call_reg_offset,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kAMD64TestRsi + 0x9999999);
|
||||
}
|
||||
} // namespace google_breakpad
|
||||
253
externals/breakpad/src/processor/disassembler_x86.cc
vendored
Normal file
253
externals/breakpad/src/processor/disassembler_x86.cc
vendored
Normal file
|
|
@ -0,0 +1,253 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "processor/disassembler_x86.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
DisassemblerX86::DisassemblerX86(const uint8_t* bytecode,
|
||||
uint32_t size,
|
||||
uint32_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() {
|
||||
if (instr_valid_)
|
||||
libdis::x86_oplist_free(¤t_instr_);
|
||||
|
||||
libdis::x86_cleanup();
|
||||
}
|
||||
|
||||
uint32_t DisassemblerX86::NextInstruction() {
|
||||
if (instr_valid_)
|
||||
libdis::x86_oplist_free(¤t_instr_);
|
||||
|
||||
if (current_byte_offset_ >= size_) {
|
||||
instr_valid_ = false;
|
||||
return 0;
|
||||
}
|
||||
uint32_t instr_size = 0;
|
||||
instr_size = libdis::x86_disasm((unsigned char*)bytecode_, size_,
|
||||
virtual_address_, current_byte_offset_,
|
||||
¤t_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(¤t_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(¤t_instr_);
|
||||
libdis::x86_op_t* dest = libdis::x86_get_dest_operand(¤t_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(¤t_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(¤t_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
|
||||
127
externals/breakpad/src/processor/disassembler_x86.h
vendored
Normal file
127
externals/breakpad/src/processor/disassembler_x86.h
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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 <sys/types.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 uint8_t* bytecode, uint32_t, uint32_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.
|
||||
uint32_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_ ? ¤t_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.
|
||||
uint16_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 uint8_t* bytecode_;
|
||||
uint32_t size_;
|
||||
uint32_t virtual_address_;
|
||||
uint32_t current_byte_offset_;
|
||||
uint32_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_;
|
||||
|
||||
uint16_t flags_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_X86_H_
|
||||
236
externals/breakpad/src/processor/disassembler_x86_unittest.cc
vendored
Normal file
236
externals/breakpad/src/processor/disassembler_x86_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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(1U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_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(0U, dis.NextInstruction());
|
||||
EXPECT_FALSE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(NULL, dis.currentInstruction());
|
||||
}
|
||||
|
||||
TEST(DisassemblerX86Test, SimpleInvalidInstruction) {
|
||||
DisassemblerX86 dis(invalid_instruction, sizeof(invalid_instruction)-1, 0);
|
||||
EXPECT_EQ(0U, dis.NextInstruction());
|
||||
EXPECT_FALSE(dis.currentInstructionValid());
|
||||
}
|
||||
|
||||
TEST(DisassemblerX86Test, BadReadLeadsToBranch) {
|
||||
DisassemblerX86 dis(read_eax_jmp_eax, sizeof(read_eax_jmp_eax)-1, 0);
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
|
||||
EXPECT_TRUE(dis.setBadRead());
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_logic, dis.currentInstructionGroup());
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(google_breakpad::DISX86_BAD_BRANCH_TARGET, dis.flags());
|
||||
EXPECT_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(6U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
|
||||
EXPECT_TRUE(dis.setBadWrite());
|
||||
EXPECT_EQ(3U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup());
|
||||
EXPECT_EQ(1U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(5U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(google_breakpad::DISX86_BAD_ARGUMENT_PASSED, dis.flags());
|
||||
EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
}
|
||||
|
||||
|
||||
TEST(DisassemblerX86Test, BadReadLeadsToBlockWrite) {
|
||||
DisassemblerX86 dis(read_edi_stosb, sizeof(read_edi_stosb)-1, 0);
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
|
||||
EXPECT_TRUE(dis.setBadRead());
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(google_breakpad::DISX86_BAD_BLOCK_WRITE, dis.flags());
|
||||
EXPECT_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(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup());
|
||||
EXPECT_TRUE(dis.setBadRead());
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_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(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup());
|
||||
EXPECT_TRUE(dis.setBadRead());
|
||||
EXPECT_EQ(1U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(google_breakpad::DISX86_BAD_WRITE, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_move, dis.currentInstructionGroup());
|
||||
}
|
||||
|
||||
TEST(DisassemblerX86Test, BadReadThenCMP) {
|
||||
DisassemblerX86 dis(read_cmp, sizeof(read_cmp)-1, 0);
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(0U, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_arithmetic, dis.currentInstructionGroup());
|
||||
EXPECT_TRUE(dis.setBadRead());
|
||||
EXPECT_EQ(3U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(google_breakpad::DISX86_BAD_COMPARISON, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_comparison, dis.currentInstructionGroup());
|
||||
EXPECT_EQ(2U, dis.NextInstruction());
|
||||
EXPECT_TRUE(dis.currentInstructionValid());
|
||||
EXPECT_EQ(google_breakpad::DISX86_BAD_COMPARISON, dis.flags());
|
||||
EXPECT_FALSE(dis.endOfBlock());
|
||||
EXPECT_EQ(libdis::insn_controlflow, dis.currentInstructionGroup());
|
||||
}
|
||||
}
|
||||
881
externals/breakpad/src/processor/dump_context.cc
vendored
Normal file
881
externals/breakpad/src/processor/dump_context.cc
vendored
Normal file
|
|
@ -0,0 +1,881 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
// dump_context.cc: A (mini/micro)dump context.
|
||||
//
|
||||
// See dump_context.h for documentation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "google_breakpad/processor/dump_context.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else // _WIN32
|
||||
#include <unistd.h>
|
||||
#endif // _WIN32
|
||||
|
||||
#include "common/stdio_wrapper.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
DumpContext::DumpContext() : context_(),
|
||||
context_flags_(0) { }
|
||||
|
||||
DumpContext::~DumpContext() {
|
||||
FreeContext();
|
||||
}
|
||||
|
||||
uint32_t DumpContext::GetContextCPU() const {
|
||||
if (!valid_) {
|
||||
// Don't log a message, GetContextCPU can be legitimately called with
|
||||
// valid_ false by FreeContext, which is called by Read.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return context_flags_ & MD_CONTEXT_CPU_MASK;
|
||||
}
|
||||
|
||||
uint32_t DumpContext::GetContextFlags() const {
|
||||
return context_flags_;
|
||||
}
|
||||
|
||||
const MDRawContextX86* DumpContext::GetContextX86() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_X86) {
|
||||
BPLOG(ERROR) << "DumpContext cannot get x86 context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.x86;
|
||||
}
|
||||
|
||||
const MDRawContextPPC* DumpContext::GetContextPPC() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_PPC) {
|
||||
BPLOG(ERROR) << "DumpContext cannot get ppc context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.ppc;
|
||||
}
|
||||
|
||||
const MDRawContextPPC64* DumpContext::GetContextPPC64() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_PPC64) {
|
||||
BPLOG(ERROR) << "DumpContext cannot get ppc64 context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.ppc64;
|
||||
}
|
||||
|
||||
const MDRawContextAMD64* DumpContext::GetContextAMD64() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_AMD64) {
|
||||
BPLOG(ERROR) << "DumpContext cannot get amd64 context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.amd64;
|
||||
}
|
||||
|
||||
const MDRawContextSPARC* DumpContext::GetContextSPARC() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_SPARC) {
|
||||
BPLOG(ERROR) << "DumpContext cannot get sparc context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.ctx_sparc;
|
||||
}
|
||||
|
||||
const MDRawContextARM* DumpContext::GetContextARM() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_ARM) {
|
||||
BPLOG(ERROR) << "DumpContext cannot get arm context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.arm;
|
||||
}
|
||||
|
||||
const MDRawContextARM64* DumpContext::GetContextARM64() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_ARM64) {
|
||||
BPLOG(ERROR) << "DumpContext cannot get arm64 context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.arm64;
|
||||
}
|
||||
|
||||
const MDRawContextMIPS* DumpContext::GetContextMIPS() const {
|
||||
if ((GetContextCPU() != MD_CONTEXT_MIPS) &&
|
||||
(GetContextCPU() != MD_CONTEXT_MIPS64)) {
|
||||
BPLOG(ERROR) << "DumpContext cannot get MIPS context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.ctx_mips;
|
||||
}
|
||||
|
||||
const MDRawContextRISCV* DumpContext::GetContextRISCV() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_RISCV) {
|
||||
BPLOG(ERROR) << "DumpContext cannot get RISCV context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.riscv;
|
||||
}
|
||||
|
||||
const MDRawContextRISCV64* DumpContext::GetContextRISCV64() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_RISCV64) {
|
||||
BPLOG(ERROR) << "DumpContext cannot get RISCV64 context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.riscv64;
|
||||
}
|
||||
|
||||
bool DumpContext::GetInstructionPointer(uint64_t* ip) const {
|
||||
BPLOG_IF(ERROR, !ip) << "DumpContext::GetInstructionPointer requires |ip|";
|
||||
assert(ip);
|
||||
*ip = 0;
|
||||
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR) << "Invalid DumpContext for GetInstructionPointer";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (GetContextCPU()) {
|
||||
case MD_CONTEXT_AMD64:
|
||||
*ip = GetContextAMD64()->rip;
|
||||
break;
|
||||
case MD_CONTEXT_ARM:
|
||||
*ip = GetContextARM()->iregs[MD_CONTEXT_ARM_REG_PC];
|
||||
break;
|
||||
case MD_CONTEXT_ARM64:
|
||||
*ip = GetContextARM64()->iregs[MD_CONTEXT_ARM64_REG_PC];
|
||||
break;
|
||||
case MD_CONTEXT_PPC:
|
||||
*ip = GetContextPPC()->srr0;
|
||||
break;
|
||||
case MD_CONTEXT_PPC64:
|
||||
*ip = GetContextPPC64()->srr0;
|
||||
break;
|
||||
case MD_CONTEXT_SPARC:
|
||||
*ip = GetContextSPARC()->pc;
|
||||
break;
|
||||
case MD_CONTEXT_X86:
|
||||
*ip = GetContextX86()->eip;
|
||||
break;
|
||||
case MD_CONTEXT_MIPS:
|
||||
case MD_CONTEXT_MIPS64:
|
||||
*ip = GetContextMIPS()->epc;
|
||||
break;
|
||||
case MD_CONTEXT_RISCV:
|
||||
*ip = GetContextRISCV()->pc;
|
||||
break;
|
||||
case MD_CONTEXT_RISCV64:
|
||||
*ip = GetContextRISCV64()->pc;
|
||||
break;
|
||||
default:
|
||||
// This should never happen.
|
||||
BPLOG(ERROR) << "Unknown CPU architecture in GetInstructionPointer";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpContext::GetStackPointer(uint64_t* sp) const {
|
||||
BPLOG_IF(ERROR, !sp) << "DumpContext::GetStackPointer requires |sp|";
|
||||
assert(sp);
|
||||
*sp = 0;
|
||||
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR) << "Invalid DumpContext for GetStackPointer";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (GetContextCPU()) {
|
||||
case MD_CONTEXT_AMD64:
|
||||
*sp = GetContextAMD64()->rsp;
|
||||
break;
|
||||
case MD_CONTEXT_ARM:
|
||||
*sp = GetContextARM()->iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
break;
|
||||
case MD_CONTEXT_ARM64:
|
||||
*sp = GetContextARM64()->iregs[MD_CONTEXT_ARM64_REG_SP];
|
||||
break;
|
||||
case MD_CONTEXT_PPC:
|
||||
*sp = GetContextPPC()->gpr[MD_CONTEXT_PPC_REG_SP];
|
||||
break;
|
||||
case MD_CONTEXT_PPC64:
|
||||
*sp = GetContextPPC64()->gpr[MD_CONTEXT_PPC64_REG_SP];
|
||||
break;
|
||||
case MD_CONTEXT_SPARC:
|
||||
*sp = GetContextSPARC()->g_r[MD_CONTEXT_SPARC_REG_SP];
|
||||
break;
|
||||
case MD_CONTEXT_X86:
|
||||
*sp = GetContextX86()->esp;
|
||||
break;
|
||||
case MD_CONTEXT_MIPS:
|
||||
case MD_CONTEXT_MIPS64:
|
||||
*sp = GetContextMIPS()->iregs[MD_CONTEXT_MIPS_REG_SP];
|
||||
break;
|
||||
case MD_CONTEXT_RISCV:
|
||||
*sp = GetContextRISCV()->sp;
|
||||
break;
|
||||
case MD_CONTEXT_RISCV64:
|
||||
*sp = GetContextRISCV64()->sp;
|
||||
break;
|
||||
default:
|
||||
// This should never happen.
|
||||
BPLOG(ERROR) << "Unknown CPU architecture in GetStackPointer";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextFlags(uint32_t context_flags) {
|
||||
context_flags_ = context_flags;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextX86(MDRawContextX86* x86) {
|
||||
context_.x86 = x86;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextPPC(MDRawContextPPC* ppc) {
|
||||
context_.ppc = ppc;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextPPC64(MDRawContextPPC64* ppc64) {
|
||||
context_.ppc64 = ppc64;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextAMD64(MDRawContextAMD64* amd64) {
|
||||
context_.amd64 = amd64;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextSPARC(MDRawContextSPARC* ctx_sparc) {
|
||||
context_.ctx_sparc = ctx_sparc;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextARM(MDRawContextARM* arm) {
|
||||
context_.arm = arm;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextARM64(MDRawContextARM64* arm64) {
|
||||
context_.arm64 = arm64;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextMIPS(MDRawContextMIPS* ctx_mips) {
|
||||
context_.ctx_mips = ctx_mips;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextRISCV(MDRawContextRISCV* riscv) {
|
||||
context_.riscv = riscv;
|
||||
}
|
||||
|
||||
void DumpContext::SetContextRISCV64(MDRawContextRISCV64* riscv64) {
|
||||
context_.riscv64 = riscv64;
|
||||
}
|
||||
|
||||
void DumpContext::FreeContext() {
|
||||
switch (GetContextCPU()) {
|
||||
case MD_CONTEXT_X86:
|
||||
delete context_.x86;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_PPC:
|
||||
delete context_.ppc;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_PPC64:
|
||||
delete context_.ppc64;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_AMD64:
|
||||
delete context_.amd64;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_SPARC:
|
||||
delete context_.ctx_sparc;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_ARM:
|
||||
delete context_.arm;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_ARM64:
|
||||
delete context_.arm64;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_MIPS:
|
||||
case MD_CONTEXT_MIPS64:
|
||||
delete context_.ctx_mips;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_RISCV:
|
||||
delete context_.riscv;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_RISCV64:
|
||||
delete context_.riscv64;
|
||||
break;
|
||||
|
||||
default:
|
||||
// There is no context record (valid_ is false) or there's a
|
||||
// context record for an unknown CPU (shouldn't happen, only known
|
||||
// records are stored by Read).
|
||||
break;
|
||||
}
|
||||
|
||||
context_flags_ = 0;
|
||||
context_.base = NULL;
|
||||
}
|
||||
|
||||
void DumpContext::Print() {
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR) << "DumpContext cannot print invalid data";
|
||||
return;
|
||||
}
|
||||
|
||||
switch (GetContextCPU()) {
|
||||
case MD_CONTEXT_X86: {
|
||||
const MDRawContextX86* context_x86 = GetContextX86();
|
||||
printf("MDRawContextX86\n");
|
||||
printf(" context_flags = 0x%x\n",
|
||||
context_x86->context_flags);
|
||||
printf(" dr0 = 0x%x\n", context_x86->dr0);
|
||||
printf(" dr1 = 0x%x\n", context_x86->dr1);
|
||||
printf(" dr2 = 0x%x\n", context_x86->dr2);
|
||||
printf(" dr3 = 0x%x\n", context_x86->dr3);
|
||||
printf(" dr6 = 0x%x\n", context_x86->dr6);
|
||||
printf(" dr7 = 0x%x\n", context_x86->dr7);
|
||||
printf(" float_save.control_word = 0x%x\n",
|
||||
context_x86->float_save.control_word);
|
||||
printf(" float_save.status_word = 0x%x\n",
|
||||
context_x86->float_save.status_word);
|
||||
printf(" float_save.tag_word = 0x%x\n",
|
||||
context_x86->float_save.tag_word);
|
||||
printf(" float_save.error_offset = 0x%x\n",
|
||||
context_x86->float_save.error_offset);
|
||||
printf(" float_save.error_selector = 0x%x\n",
|
||||
context_x86->float_save.error_selector);
|
||||
printf(" float_save.data_offset = 0x%x\n",
|
||||
context_x86->float_save.data_offset);
|
||||
printf(" float_save.data_selector = 0x%x\n",
|
||||
context_x86->float_save.data_selector);
|
||||
printf(" float_save.register_area[%2d] = 0x",
|
||||
MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE);
|
||||
for (unsigned int register_index = 0;
|
||||
register_index < MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE;
|
||||
++register_index) {
|
||||
printf("%02x", context_x86->float_save.register_area[register_index]);
|
||||
}
|
||||
printf("\n");
|
||||
printf(" float_save.cr0_npx_state = 0x%x\n",
|
||||
context_x86->float_save.cr0_npx_state);
|
||||
printf(" gs = 0x%x\n", context_x86->gs);
|
||||
printf(" fs = 0x%x\n", context_x86->fs);
|
||||
printf(" es = 0x%x\n", context_x86->es);
|
||||
printf(" ds = 0x%x\n", context_x86->ds);
|
||||
printf(" edi = 0x%x\n", context_x86->edi);
|
||||
printf(" esi = 0x%x\n", context_x86->esi);
|
||||
printf(" ebx = 0x%x\n", context_x86->ebx);
|
||||
printf(" edx = 0x%x\n", context_x86->edx);
|
||||
printf(" ecx = 0x%x\n", context_x86->ecx);
|
||||
printf(" eax = 0x%x\n", context_x86->eax);
|
||||
printf(" ebp = 0x%x\n", context_x86->ebp);
|
||||
printf(" eip = 0x%x\n", context_x86->eip);
|
||||
printf(" cs = 0x%x\n", context_x86->cs);
|
||||
printf(" eflags = 0x%x\n", context_x86->eflags);
|
||||
printf(" esp = 0x%x\n", context_x86->esp);
|
||||
printf(" ss = 0x%x\n", context_x86->ss);
|
||||
printf(" extended_registers[%3d] = 0x",
|
||||
MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE);
|
||||
for (unsigned int register_index = 0;
|
||||
register_index < MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE;
|
||||
++register_index) {
|
||||
printf("%02x", context_x86->extended_registers[register_index]);
|
||||
}
|
||||
printf("\n\n");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_PPC: {
|
||||
const MDRawContextPPC* context_ppc = GetContextPPC();
|
||||
printf("MDRawContextPPC\n");
|
||||
printf(" context_flags = 0x%x\n",
|
||||
context_ppc->context_flags);
|
||||
printf(" srr0 = 0x%x\n", context_ppc->srr0);
|
||||
printf(" srr1 = 0x%x\n", context_ppc->srr1);
|
||||
for (unsigned int gpr_index = 0;
|
||||
gpr_index < MD_CONTEXT_PPC_GPR_COUNT;
|
||||
++gpr_index) {
|
||||
printf(" gpr[%2d] = 0x%x\n",
|
||||
gpr_index, context_ppc->gpr[gpr_index]);
|
||||
}
|
||||
printf(" cr = 0x%x\n", context_ppc->cr);
|
||||
printf(" xer = 0x%x\n", context_ppc->xer);
|
||||
printf(" lr = 0x%x\n", context_ppc->lr);
|
||||
printf(" ctr = 0x%x\n", context_ppc->ctr);
|
||||
printf(" mq = 0x%x\n", context_ppc->mq);
|
||||
printf(" vrsave = 0x%x\n", context_ppc->vrsave);
|
||||
for (unsigned int fpr_index = 0;
|
||||
fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT;
|
||||
++fpr_index) {
|
||||
printf(" float_save.fpregs[%2d] = 0x%" PRIx64 "\n",
|
||||
fpr_index, context_ppc->float_save.fpregs[fpr_index]);
|
||||
}
|
||||
printf(" float_save.fpscr = 0x%x\n",
|
||||
context_ppc->float_save.fpscr);
|
||||
// TODO(mmentovai): print the 128-bit quantities in
|
||||
// context_ppc->vector_save. This isn't done yet because printf
|
||||
// doesn't support 128-bit quantities, and printing them using
|
||||
// PRIx64 as two 64-bit quantities requires knowledge of the CPU's
|
||||
// byte ordering.
|
||||
printf(" vector_save.save_vrvalid = 0x%x\n",
|
||||
context_ppc->vector_save.save_vrvalid);
|
||||
printf("\n");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_PPC64: {
|
||||
const MDRawContextPPC64* context_ppc64 = GetContextPPC64();
|
||||
printf("MDRawContextPPC64\n");
|
||||
printf(" context_flags = 0x%" PRIx64 "\n",
|
||||
context_ppc64->context_flags);
|
||||
printf(" srr0 = 0x%" PRIx64 "\n",
|
||||
context_ppc64->srr0);
|
||||
printf(" srr1 = 0x%" PRIx64 "\n",
|
||||
context_ppc64->srr1);
|
||||
for (unsigned int gpr_index = 0;
|
||||
gpr_index < MD_CONTEXT_PPC64_GPR_COUNT;
|
||||
++gpr_index) {
|
||||
printf(" gpr[%2d] = 0x%" PRIx64 "\n",
|
||||
gpr_index, context_ppc64->gpr[gpr_index]);
|
||||
}
|
||||
printf(" cr = 0x%" PRIx64 "\n", context_ppc64->cr);
|
||||
printf(" xer = 0x%" PRIx64 "\n",
|
||||
context_ppc64->xer);
|
||||
printf(" lr = 0x%" PRIx64 "\n", context_ppc64->lr);
|
||||
printf(" ctr = 0x%" PRIx64 "\n",
|
||||
context_ppc64->ctr);
|
||||
printf(" vrsave = 0x%" PRIx64 "\n",
|
||||
context_ppc64->vrsave);
|
||||
for (unsigned int fpr_index = 0;
|
||||
fpr_index < MD_FLOATINGSAVEAREA_PPC_FPR_COUNT;
|
||||
++fpr_index) {
|
||||
printf(" float_save.fpregs[%2d] = 0x%" PRIx64 "\n",
|
||||
fpr_index, context_ppc64->float_save.fpregs[fpr_index]);
|
||||
}
|
||||
printf(" float_save.fpscr = 0x%x\n",
|
||||
context_ppc64->float_save.fpscr);
|
||||
// TODO(mmentovai): print the 128-bit quantities in
|
||||
// context_ppc64->vector_save. This isn't done yet because printf
|
||||
// doesn't support 128-bit quantities, and printing them using
|
||||
// PRIx64 as two 64-bit quantities requires knowledge of the CPU's
|
||||
// byte ordering.
|
||||
printf(" vector_save.save_vrvalid = 0x%x\n",
|
||||
context_ppc64->vector_save.save_vrvalid);
|
||||
printf("\n");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_AMD64: {
|
||||
const MDRawContextAMD64* context_amd64 = GetContextAMD64();
|
||||
printf("MDRawContextAMD64\n");
|
||||
printf(" p1_home = 0x%" PRIx64 "\n",
|
||||
context_amd64->p1_home);
|
||||
printf(" p2_home = 0x%" PRIx64 "\n",
|
||||
context_amd64->p2_home);
|
||||
printf(" p3_home = 0x%" PRIx64 "\n",
|
||||
context_amd64->p3_home);
|
||||
printf(" p4_home = 0x%" PRIx64 "\n",
|
||||
context_amd64->p4_home);
|
||||
printf(" p5_home = 0x%" PRIx64 "\n",
|
||||
context_amd64->p5_home);
|
||||
printf(" p6_home = 0x%" PRIx64 "\n",
|
||||
context_amd64->p6_home);
|
||||
printf(" context_flags = 0x%x\n",
|
||||
context_amd64->context_flags);
|
||||
printf(" mx_csr = 0x%x\n",
|
||||
context_amd64->mx_csr);
|
||||
printf(" cs = 0x%x\n", context_amd64->cs);
|
||||
printf(" ds = 0x%x\n", context_amd64->ds);
|
||||
printf(" es = 0x%x\n", context_amd64->es);
|
||||
printf(" fs = 0x%x\n", context_amd64->fs);
|
||||
printf(" gs = 0x%x\n", context_amd64->gs);
|
||||
printf(" ss = 0x%x\n", context_amd64->ss);
|
||||
printf(" eflags = 0x%x\n", context_amd64->eflags);
|
||||
printf(" dr0 = 0x%" PRIx64 "\n", context_amd64->dr0);
|
||||
printf(" dr1 = 0x%" PRIx64 "\n", context_amd64->dr1);
|
||||
printf(" dr2 = 0x%" PRIx64 "\n", context_amd64->dr2);
|
||||
printf(" dr3 = 0x%" PRIx64 "\n", context_amd64->dr3);
|
||||
printf(" dr6 = 0x%" PRIx64 "\n", context_amd64->dr6);
|
||||
printf(" dr7 = 0x%" PRIx64 "\n", context_amd64->dr7);
|
||||
printf(" rax = 0x%" PRIx64 "\n", context_amd64->rax);
|
||||
printf(" rcx = 0x%" PRIx64 "\n", context_amd64->rcx);
|
||||
printf(" rdx = 0x%" PRIx64 "\n", context_amd64->rdx);
|
||||
printf(" rbx = 0x%" PRIx64 "\n", context_amd64->rbx);
|
||||
printf(" rsp = 0x%" PRIx64 "\n", context_amd64->rsp);
|
||||
printf(" rbp = 0x%" PRIx64 "\n", context_amd64->rbp);
|
||||
printf(" rsi = 0x%" PRIx64 "\n", context_amd64->rsi);
|
||||
printf(" rdi = 0x%" PRIx64 "\n", context_amd64->rdi);
|
||||
printf(" r8 = 0x%" PRIx64 "\n", context_amd64->r8);
|
||||
printf(" r9 = 0x%" PRIx64 "\n", context_amd64->r9);
|
||||
printf(" r10 = 0x%" PRIx64 "\n", context_amd64->r10);
|
||||
printf(" r11 = 0x%" PRIx64 "\n", context_amd64->r11);
|
||||
printf(" r12 = 0x%" PRIx64 "\n", context_amd64->r12);
|
||||
printf(" r13 = 0x%" PRIx64 "\n", context_amd64->r13);
|
||||
printf(" r14 = 0x%" PRIx64 "\n", context_amd64->r14);
|
||||
printf(" r15 = 0x%" PRIx64 "\n", context_amd64->r15);
|
||||
printf(" rip = 0x%" PRIx64 "\n", context_amd64->rip);
|
||||
// TODO: print xmm, vector, debug registers
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_SPARC: {
|
||||
const MDRawContextSPARC* context_sparc = GetContextSPARC();
|
||||
printf("MDRawContextSPARC\n");
|
||||
printf(" context_flags = 0x%x\n",
|
||||
context_sparc->context_flags);
|
||||
for (unsigned int g_r_index = 0;
|
||||
g_r_index < MD_CONTEXT_SPARC_GPR_COUNT;
|
||||
++g_r_index) {
|
||||
printf(" g_r[%2d] = 0x%" PRIx64 "\n",
|
||||
g_r_index, context_sparc->g_r[g_r_index]);
|
||||
}
|
||||
printf(" ccr = 0x%" PRIx64 "\n", context_sparc->ccr);
|
||||
printf(" pc = 0x%" PRIx64 "\n", context_sparc->pc);
|
||||
printf(" npc = 0x%" PRIx64 "\n", context_sparc->npc);
|
||||
printf(" y = 0x%" PRIx64 "\n", context_sparc->y);
|
||||
printf(" asi = 0x%" PRIx64 "\n", context_sparc->asi);
|
||||
printf(" fprs = 0x%" PRIx64 "\n", context_sparc->fprs);
|
||||
|
||||
for (unsigned int fpr_index = 0;
|
||||
fpr_index < MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT;
|
||||
++fpr_index) {
|
||||
printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n",
|
||||
fpr_index, context_sparc->float_save.regs[fpr_index]);
|
||||
}
|
||||
printf(" float_save.filler = 0x%" PRIx64 "\n",
|
||||
context_sparc->float_save.filler);
|
||||
printf(" float_save.fsr = 0x%" PRIx64 "\n",
|
||||
context_sparc->float_save.fsr);
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_ARM: {
|
||||
const MDRawContextARM* context_arm = GetContextARM();
|
||||
const char * const names[] = {
|
||||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
|
||||
};
|
||||
printf("MDRawContextARM\n");
|
||||
printf(" context_flags = 0x%x\n",
|
||||
context_arm->context_flags);
|
||||
for (unsigned int ireg_index = 0;
|
||||
ireg_index < MD_CONTEXT_ARM_GPR_COUNT;
|
||||
++ireg_index) {
|
||||
printf(" %-3s = 0x%x\n",
|
||||
names[ireg_index], context_arm->iregs[ireg_index]);
|
||||
}
|
||||
printf(" cpsr = 0x%x\n", context_arm->cpsr);
|
||||
printf(" float_save.fpscr = 0x%" PRIx64 "\n",
|
||||
context_arm->float_save.fpscr);
|
||||
for (unsigned int fpr_index = 0;
|
||||
fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT;
|
||||
++fpr_index) {
|
||||
printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n",
|
||||
fpr_index, context_arm->float_save.regs[fpr_index]);
|
||||
}
|
||||
for (unsigned int fpe_index = 0;
|
||||
fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT;
|
||||
++fpe_index) {
|
||||
printf(" float_save.extra[%2d] = 0x%" PRIx32 "\n",
|
||||
fpe_index, context_arm->float_save.extra[fpe_index]);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_ARM64: {
|
||||
const MDRawContextARM64* context_arm64 = GetContextARM64();
|
||||
printf("MDRawContextARM64\n");
|
||||
printf(" context_flags = 0x%x\n",
|
||||
context_arm64->context_flags);
|
||||
for (unsigned int ireg_index = 0;
|
||||
ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
|
||||
++ireg_index) {
|
||||
printf(" iregs[%2d] = 0x%" PRIx64 "\n",
|
||||
ireg_index, context_arm64->iregs[ireg_index]);
|
||||
}
|
||||
printf(" cpsr = 0x%x\n", context_arm64->cpsr);
|
||||
printf(" float_save.fpsr = 0x%x\n", context_arm64->float_save.fpsr);
|
||||
printf(" float_save.fpcr = 0x%x\n", context_arm64->float_save.fpcr);
|
||||
|
||||
for (unsigned int freg_index = 0;
|
||||
freg_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
|
||||
++freg_index) {
|
||||
uint128_struct fp_value = context_arm64->float_save.regs[freg_index];
|
||||
printf(" float_save.regs[%2d] = 0x%" PRIx64 "%" PRIx64 "\n",
|
||||
freg_index, fp_value.high, fp_value.low);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_MIPS:
|
||||
case MD_CONTEXT_MIPS64: {
|
||||
const MDRawContextMIPS* context_mips = GetContextMIPS();
|
||||
printf("MDRawContextMIPS\n");
|
||||
printf(" context_flags = 0x%x\n",
|
||||
context_mips->context_flags);
|
||||
for (int ireg_index = 0;
|
||||
ireg_index < MD_CONTEXT_MIPS_GPR_COUNT;
|
||||
++ireg_index) {
|
||||
printf(" iregs[%2d] = 0x%" PRIx64 "\n",
|
||||
ireg_index, context_mips->iregs[ireg_index]);
|
||||
}
|
||||
printf(" mdhi = 0x%" PRIx64 "\n",
|
||||
context_mips->mdhi);
|
||||
printf(" mdlo = 0x%" PRIx64 "\n",
|
||||
context_mips->mdhi);
|
||||
for (int dsp_index = 0;
|
||||
dsp_index < MD_CONTEXT_MIPS_DSP_COUNT;
|
||||
++dsp_index) {
|
||||
printf(" hi[%1d] = 0x%" PRIx32 "\n",
|
||||
dsp_index, context_mips->hi[dsp_index]);
|
||||
printf(" lo[%1d] = 0x%" PRIx32 "\n",
|
||||
dsp_index, context_mips->lo[dsp_index]);
|
||||
}
|
||||
printf(" dsp_control = 0x%" PRIx32 "\n",
|
||||
context_mips->dsp_control);
|
||||
printf(" epc = 0x%" PRIx64 "\n",
|
||||
context_mips->epc);
|
||||
printf(" badvaddr = 0x%" PRIx64 "\n",
|
||||
context_mips->badvaddr);
|
||||
printf(" status = 0x%" PRIx32 "\n",
|
||||
context_mips->status);
|
||||
printf(" cause = 0x%" PRIx32 "\n",
|
||||
context_mips->cause);
|
||||
|
||||
for (int fpr_index = 0;
|
||||
fpr_index < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT;
|
||||
++fpr_index) {
|
||||
printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n",
|
||||
fpr_index, context_mips->float_save.regs[fpr_index]);
|
||||
}
|
||||
printf(" float_save.fpcsr = 0x%" PRIx32 "\n",
|
||||
context_mips->float_save.fpcsr);
|
||||
printf(" float_save.fir = 0x%" PRIx32 "\n",
|
||||
context_mips->float_save.fir);
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_RISCV: {
|
||||
const MDRawContextRISCV* context_riscv = GetContextRISCV();
|
||||
printf("MDRawContextRISCV\n");
|
||||
printf(" context_flags = 0x%x\n",
|
||||
context_riscv->context_flags);
|
||||
|
||||
printf(" pc = 0x%" PRIx32 "\n",
|
||||
context_riscv->pc);
|
||||
printf(" ra = 0x%" PRIx32 "\n",
|
||||
context_riscv->ra);
|
||||
printf(" sp = 0x%" PRIx32 "\n",
|
||||
context_riscv->sp);
|
||||
printf(" gp = 0x%" PRIx32 "\n",
|
||||
context_riscv->gp);
|
||||
printf(" tp = 0x%" PRIx32 "\n",
|
||||
context_riscv->tp);
|
||||
printf(" t0 = 0x%" PRIx32 "\n",
|
||||
context_riscv->t0);
|
||||
printf(" t1 = 0x%" PRIx32 "\n",
|
||||
context_riscv->t1);
|
||||
printf(" t2 = 0x%" PRIx32 "\n",
|
||||
context_riscv->t2);
|
||||
printf(" s0 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s0);
|
||||
printf(" s1 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s1);
|
||||
printf(" a0 = 0x%" PRIx32 "\n",
|
||||
context_riscv->a0);
|
||||
printf(" a1 = 0x%" PRIx32 "\n",
|
||||
context_riscv->a1);
|
||||
printf(" a2 = 0x%" PRIx32 "\n",
|
||||
context_riscv->a2);
|
||||
printf(" a3 = 0x%" PRIx32 "\n",
|
||||
context_riscv->a3);
|
||||
printf(" a4 = 0x%" PRIx32 "\n",
|
||||
context_riscv->a4);
|
||||
printf(" a5 = 0x%" PRIx32 "\n",
|
||||
context_riscv->a5);
|
||||
printf(" a6 = 0x%" PRIx32 "\n",
|
||||
context_riscv->a6);
|
||||
printf(" a7 = 0x%" PRIx32 "\n",
|
||||
context_riscv->a7);
|
||||
printf(" s2 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s2);
|
||||
printf(" s3 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s3);
|
||||
printf(" s4 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s4);
|
||||
printf(" s5 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s5);
|
||||
printf(" s6 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s6);
|
||||
printf(" s7 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s7);
|
||||
printf(" s8 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s8);
|
||||
printf(" s9 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s9);
|
||||
printf(" s10 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s10);
|
||||
printf(" s11 = 0x%" PRIx32 "\n",
|
||||
context_riscv->s11);
|
||||
printf(" t3 = 0x%" PRIx32 "\n",
|
||||
context_riscv->t3);
|
||||
printf(" t4 = 0x%" PRIx32 "\n",
|
||||
context_riscv->t4);
|
||||
printf(" t5 = 0x%" PRIx32 "\n",
|
||||
context_riscv->t5);
|
||||
printf(" t6 = 0x%" PRIx32 "\n",
|
||||
context_riscv->t6);
|
||||
|
||||
#if defined(__riscv)
|
||||
for (unsigned int freg_index = 0; freg_index < MD_CONTEXT_RISCV_FPR_COUNT;
|
||||
++freg_index) {
|
||||
// Breakpad only supports RISCV32 with 32 bit floating point.
|
||||
uint32_t fp_value = context_riscv->fpregs[freg_index];
|
||||
printf(" fpregs[%2d] = 0x%" PRIx32 "\n", freg_index,
|
||||
fp_value);
|
||||
}
|
||||
printf(" fcsr = 0x%" PRIx32 "\n", context_riscv->fcsr);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_RISCV64: {
|
||||
const MDRawContextRISCV64* context_riscv64 = GetContextRISCV64();
|
||||
printf("MDRawContextRISCV64\n");
|
||||
printf(" context_flags = 0x%x\n",
|
||||
context_riscv64->context_flags);
|
||||
|
||||
printf(" pc = 0x%" PRIx64 "\n",
|
||||
context_riscv64->pc);
|
||||
printf(" ra = 0x%" PRIx64 "\n",
|
||||
context_riscv64->ra);
|
||||
printf(" sp = 0x%" PRIx64 "\n",
|
||||
context_riscv64->sp);
|
||||
printf(" gp = 0x%" PRIx64 "\n",
|
||||
context_riscv64->gp);
|
||||
printf(" tp = 0x%" PRIx64 "\n",
|
||||
context_riscv64->tp);
|
||||
printf(" t0 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->t0);
|
||||
printf(" t1 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->t1);
|
||||
printf(" t2 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->t2);
|
||||
printf(" s0 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s0);
|
||||
printf(" s1 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s1);
|
||||
printf(" a0 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->a0);
|
||||
printf(" a1 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->a1);
|
||||
printf(" a2 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->a2);
|
||||
printf(" a3 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->a3);
|
||||
printf(" a4 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->a4);
|
||||
printf(" a5 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->a5);
|
||||
printf(" a6 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->a6);
|
||||
printf(" a7 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->a7);
|
||||
printf(" s2 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s2);
|
||||
printf(" s3 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s3);
|
||||
printf(" s4 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s4);
|
||||
printf(" s5 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s5);
|
||||
printf(" s6 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s6);
|
||||
printf(" s7 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s7);
|
||||
printf(" s8 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s8);
|
||||
printf(" s9 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s9);
|
||||
printf(" s10 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s10);
|
||||
printf(" s11 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->s11);
|
||||
printf(" t3 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->t3);
|
||||
printf(" t4 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->t4);
|
||||
printf(" t5 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->t5);
|
||||
printf(" t6 = 0x%" PRIx64 "\n",
|
||||
context_riscv64->t6);
|
||||
|
||||
#if defined(__riscv)
|
||||
for (unsigned int freg_index = 0; freg_index < MD_CONTEXT_RISCV_FPR_COUNT;
|
||||
++freg_index) {
|
||||
// Breakpad only supports RISCV64 with 64 bit floating point.
|
||||
uint64_t fp_value = context_riscv64->fpregs[freg_index];
|
||||
printf(" fpregs[%2d] = 0x%" PRIx64 "\n", freg_index,
|
||||
fp_value);
|
||||
}
|
||||
printf(" fcsr = 0x%" PRIx32 "\n", context_riscv64->fcsr);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
42
externals/breakpad/src/processor/dump_object.cc
vendored
Normal file
42
externals/breakpad/src/processor/dump_object.cc
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
// dump_object.cc: A base class for all mini/micro dump object.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "google_breakpad/processor/dump_object.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
DumpObject::DumpObject() : valid_(false) {
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
123
externals/breakpad/src/processor/exploitability.cc
vendored
Normal file
123
externals/breakpad/src/processor/exploitability.cc
vendored
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "google_breakpad/processor/exploitability.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
#include "google_breakpad/processor/process_state.h"
|
||||
#include "processor/exploitability_linux.h"
|
||||
#include "processor/exploitability_win.h"
|
||||
#include "processor/logging.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) {
|
||||
return ExploitabilityForPlatform(dump, process_state, false);
|
||||
}
|
||||
|
||||
Exploitability *Exploitability::ExploitabilityForPlatform(
|
||||
Minidump *dump,
|
||||
ProcessState *process_state,
|
||||
bool enable_objdump) {
|
||||
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_LINUX: {
|
||||
platform_exploitability = new ExploitabilityLinux(dump,
|
||||
process_state,
|
||||
enable_objdump);
|
||||
break;
|
||||
}
|
||||
case MD_OS_MAC_OS_X:
|
||||
case MD_OS_IOS:
|
||||
case MD_OS_UNIX:
|
||||
case MD_OS_SOLARIS:
|
||||
case MD_OS_ANDROID:
|
||||
case MD_OS_PS3:
|
||||
case MD_OS_FUCHSIA:
|
||||
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(uint64_t address) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
uint8_t byte = (address >> (8*i)) & 0xff;
|
||||
if ((byte >= ' ' && byte <= '~') || byte == 0)
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
320
externals/breakpad/src/processor/exploitability_linux.cc
vendored
Normal file
320
externals/breakpad/src/processor/exploitability_linux.cc
vendored
Normal file
|
|
@ -0,0 +1,320 @@
|
|||
// Copyright 2013 Google LLC
|
||||
//
|
||||
// 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 LLC 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_linux.cc: Linux specific exploitability engine.
|
||||
//
|
||||
// Provides a guess at the exploitability of the crash for the Linux
|
||||
// platform given a minidump and process_state.
|
||||
//
|
||||
// Author: Matthew Riley
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "processor/exploitability_linux.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "google_breakpad/common/minidump_exception_linux.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/process_state.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#ifdef __linux__
|
||||
#include "processor/disassembler_objdump.h"
|
||||
#endif
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Prefixes for memory mapping names.
|
||||
constexpr char kHeapPrefix[] = "[heap";
|
||||
constexpr char kStackPrefix[] = "[stack";
|
||||
|
||||
// This function in libc is called if the program was compiled with
|
||||
// -fstack-protector and a function's stack canary changes.
|
||||
constexpr char kStackCheckFailureFunction[] = "__stack_chk_fail";
|
||||
|
||||
// This function in libc is called if the program was compiled with
|
||||
// -D_FORTIFY_SOURCE=2, a function like strcpy() is called, and the runtime
|
||||
// can determine that the call would overflow the target buffer.
|
||||
constexpr char kBoundsCheckFailureFunction[] = "__chk_fail";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
ExploitabilityLinux::ExploitabilityLinux(Minidump* dump,
|
||||
ProcessState* process_state)
|
||||
: Exploitability(dump, process_state),
|
||||
enable_objdump_(false) { }
|
||||
|
||||
ExploitabilityLinux::ExploitabilityLinux(Minidump* dump,
|
||||
ProcessState* process_state,
|
||||
bool enable_objdump)
|
||||
: Exploitability(dump, process_state),
|
||||
enable_objdump_(enable_objdump) { }
|
||||
|
||||
|
||||
ExploitabilityRating ExploitabilityLinux::CheckPlatformExploitability() {
|
||||
// Check the crashing thread for functions suggesting a buffer overflow or
|
||||
// stack smash.
|
||||
if (process_state_->requesting_thread() != -1) {
|
||||
CallStack* crashing_thread =
|
||||
process_state_->threads()->at(process_state_->requesting_thread());
|
||||
const vector<StackFrame*>& crashing_thread_frames =
|
||||
*crashing_thread->frames();
|
||||
for (size_t i = 0; i < crashing_thread_frames.size(); ++i) {
|
||||
if (crashing_thread_frames[i]->function_name ==
|
||||
kStackCheckFailureFunction) {
|
||||
return EXPLOITABILITY_HIGH;
|
||||
}
|
||||
|
||||
if (crashing_thread_frames[i]->function_name ==
|
||||
kBoundsCheckFailureFunction) {
|
||||
return EXPLOITABILITY_HIGH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Getting exception data. (It should exist for all minidumps.)
|
||||
MinidumpException* exception = dump_->GetException();
|
||||
if (exception == NULL) {
|
||||
BPLOG(INFO) << "No exception record.";
|
||||
return EXPLOITABILITY_ERR_PROCESSING;
|
||||
}
|
||||
const MDRawExceptionStream* raw_exception_stream = exception->exception();
|
||||
if (raw_exception_stream == NULL) {
|
||||
BPLOG(INFO) << "No raw exception stream.";
|
||||
return EXPLOITABILITY_ERR_PROCESSING;
|
||||
}
|
||||
|
||||
// Checking for benign exceptions that caused the crash.
|
||||
if (this->BenignCrashTrigger(raw_exception_stream)) {
|
||||
return EXPLOITABILITY_NONE;
|
||||
}
|
||||
|
||||
// Check if the instruction pointer is in a valid instruction region
|
||||
// by finding if it maps to an executable part of memory.
|
||||
uint64_t instruction_ptr = 0;
|
||||
uint64_t stack_ptr = 0;
|
||||
|
||||
const MinidumpContext* context = exception->GetContext();
|
||||
if (context == NULL) {
|
||||
BPLOG(INFO) << "No exception context.";
|
||||
return EXPLOITABILITY_ERR_PROCESSING;
|
||||
}
|
||||
|
||||
// Getting the instruction pointer.
|
||||
if (!context->GetInstructionPointer(&instruction_ptr)) {
|
||||
BPLOG(INFO) << "Failed to retrieve instruction pointer.";
|
||||
return EXPLOITABILITY_ERR_PROCESSING;
|
||||
}
|
||||
|
||||
// Getting the stack pointer.
|
||||
if (!context->GetStackPointer(&stack_ptr)) {
|
||||
BPLOG(INFO) << "Failed to retrieve stack pointer.";
|
||||
return EXPLOITABILITY_ERR_PROCESSING;
|
||||
}
|
||||
|
||||
// Checking for the instruction pointer in a valid instruction region,
|
||||
// a misplaced stack pointer, and an executable stack or heap.
|
||||
if (!this->InstructionPointerInCode(instruction_ptr) ||
|
||||
this->StackPointerOffStack(stack_ptr) ||
|
||||
this->ExecutableStackOrHeap()) {
|
||||
return EXPLOITABILITY_HIGH;
|
||||
}
|
||||
|
||||
// Check for write to read only memory or invalid memory, shelling out
|
||||
// to objdump is enabled.
|
||||
if (enable_objdump_ && this->EndedOnIllegalWrite(instruction_ptr)) {
|
||||
return EXPLOITABILITY_HIGH;
|
||||
}
|
||||
|
||||
// There was no strong evidence suggesting exploitability, but the minidump
|
||||
// does not appear totally benign either.
|
||||
return EXPLOITABILITY_INTERESTING;
|
||||
}
|
||||
|
||||
bool ExploitabilityLinux::EndedOnIllegalWrite(uint64_t instruction_ptr) {
|
||||
#ifndef __linux__
|
||||
BPLOG(INFO) << "MinGW does not support fork and exec. Terminating method.";
|
||||
return false;
|
||||
#else
|
||||
// Get memory region containing instruction pointer.
|
||||
MinidumpMemoryList* memory_list = dump_->GetMemoryList();
|
||||
MinidumpMemoryRegion* memory_region =
|
||||
memory_list ?
|
||||
memory_list->GetMemoryRegionForAddress(instruction_ptr) : NULL;
|
||||
if (!memory_region) {
|
||||
BPLOG(INFO) << "No memory region around instruction pointer.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get exception data to find architecture.
|
||||
string architecture = "";
|
||||
MinidumpException* exception = dump_->GetException();
|
||||
// This should never evaluate to true, since this should not be reachable
|
||||
// without checking for exception data earlier.
|
||||
if (!exception) {
|
||||
BPLOG(INFO) << "No exception data.";
|
||||
return false;
|
||||
}
|
||||
const MDRawExceptionStream* raw_exception_stream = exception->exception();
|
||||
const MinidumpContext* context = exception->GetContext();
|
||||
// This should not evaluate to true, for the same reason mentioned above.
|
||||
if (!raw_exception_stream || !context) {
|
||||
BPLOG(INFO) << "No exception or architecture data.";
|
||||
return false;
|
||||
}
|
||||
|
||||
DisassemblerObjdump disassembler(context->GetContextCPU(), memory_region,
|
||||
instruction_ptr);
|
||||
if (!disassembler.IsValid()) {
|
||||
BPLOG(INFO) << "Disassembling fault instruction failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the operation is a write to memory.
|
||||
// First, the instruction must one that can write to memory.
|
||||
auto instruction = disassembler.operation();
|
||||
if (!instruction.compare("mov") || !instruction.compare("inc") ||
|
||||
!instruction.compare("dec") || !instruction.compare("and") ||
|
||||
!instruction.compare("or") || !instruction.compare("xor") ||
|
||||
!instruction.compare("not") || !instruction.compare("neg") ||
|
||||
!instruction.compare("add") || !instruction.compare("sub") ||
|
||||
!instruction.compare("shl") || !instruction.compare("shr")) {
|
||||
uint64_t write_address = 0;
|
||||
|
||||
// Check that the destination is a memory address. CalculateDestAddress will
|
||||
// return false if the destination is not a memory address.
|
||||
if (!disassembler.CalculateDestAddress(*context, write_address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the program crashed as a result of a write, the destination of
|
||||
// the write must have been an address that did not permit writing.
|
||||
// However, if the address is under 4k, due to program protections,
|
||||
// the crash does not suggest exploitability for writes with such a
|
||||
// low target address.
|
||||
return write_address > 4096;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
#endif // __linux__
|
||||
}
|
||||
|
||||
bool ExploitabilityLinux::StackPointerOffStack(uint64_t stack_ptr) {
|
||||
MinidumpLinuxMapsList* linux_maps_list = dump_->GetLinuxMapsList();
|
||||
// Inconclusive if there are no mappings available.
|
||||
if (!linux_maps_list) {
|
||||
return false;
|
||||
}
|
||||
const MinidumpLinuxMaps* linux_maps =
|
||||
linux_maps_list->GetLinuxMapsForAddress(stack_ptr);
|
||||
// Checks if the stack pointer maps to a valid mapping and if the mapping
|
||||
// is not the stack. If the mapping has no name, it is inconclusive whether
|
||||
// it is off the stack.
|
||||
return !linux_maps || (linux_maps->GetPathname().compare("") &&
|
||||
linux_maps->GetPathname().compare(
|
||||
0, strlen(kStackPrefix), kStackPrefix));
|
||||
}
|
||||
|
||||
bool ExploitabilityLinux::ExecutableStackOrHeap() {
|
||||
MinidumpLinuxMapsList* linux_maps_list = dump_->GetLinuxMapsList();
|
||||
if (linux_maps_list) {
|
||||
for (size_t i = 0; i < linux_maps_list->get_maps_count(); i++) {
|
||||
const MinidumpLinuxMaps* linux_maps =
|
||||
linux_maps_list->GetLinuxMapsAtIndex(i);
|
||||
// Check for executable stack or heap for each mapping.
|
||||
if (linux_maps && (!linux_maps->GetPathname().compare(
|
||||
0, strlen(kStackPrefix), kStackPrefix) ||
|
||||
!linux_maps->GetPathname().compare(
|
||||
0, strlen(kHeapPrefix), kHeapPrefix)) &&
|
||||
linux_maps->IsExecutable()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExploitabilityLinux::InstructionPointerInCode(uint64_t instruction_ptr) {
|
||||
// Get Linux memory mapping from /proc/self/maps. Checking whether the
|
||||
// region the instruction pointer is in has executable permission can tell
|
||||
// whether it is in a valid code region. If there is no mapping for the
|
||||
// instruction pointer, it is indicative that the instruction pointer is
|
||||
// not within a module, which implies that it is outside a valid area.
|
||||
MinidumpLinuxMapsList* linux_maps_list = dump_->GetLinuxMapsList();
|
||||
const MinidumpLinuxMaps* linux_maps =
|
||||
linux_maps_list ?
|
||||
linux_maps_list->GetLinuxMapsForAddress(instruction_ptr) : NULL;
|
||||
return linux_maps ? linux_maps->IsExecutable() : false;
|
||||
}
|
||||
|
||||
bool ExploitabilityLinux::BenignCrashTrigger(
|
||||
const MDRawExceptionStream* raw_exception_stream) {
|
||||
// Check the cause of crash.
|
||||
// If the exception of the crash is a benign exception,
|
||||
// it is probably not exploitable.
|
||||
switch (raw_exception_stream->exception_record.exception_code) {
|
||||
case MD_EXCEPTION_CODE_LIN_SIGHUP:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGINT:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGQUIT:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGTRAP:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGABRT:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGFPE:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGKILL:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGUSR1:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGUSR2:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGPIPE:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGALRM:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGTERM:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGCHLD:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGCONT:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGSTOP:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGTSTP:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGTTIN:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGTTOU:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGURG:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGXCPU:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGXFSZ:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGVTALRM:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGPROF:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGWINCH:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGIO:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGPWR:
|
||||
case MD_EXCEPTION_CODE_LIN_SIGSYS:
|
||||
case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
93
externals/breakpad/src/processor/exploitability_linux.h
vendored
Normal file
93
externals/breakpad/src/processor/exploitability_linux.h
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2013 Google LLC
|
||||
//
|
||||
// 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 LLC 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_linux.h: Linux specific exploitability engine.
|
||||
//
|
||||
// Provides a guess at the exploitability of the crash for the Linux
|
||||
// platform given a minidump and process_state.
|
||||
//
|
||||
// Author: Matthew Riley
|
||||
|
||||
#ifndef GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_LINUX_H_
|
||||
#define GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_LINUX_H_
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/processor/exploitability.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ExploitabilityLinux : public Exploitability {
|
||||
public:
|
||||
ExploitabilityLinux(Minidump* dump,
|
||||
ProcessState* process_state);
|
||||
|
||||
// Parameters are the minidump to analyze, the object representing process
|
||||
// state, and whether to enable objdump disassembly.
|
||||
// Enabling objdump will allow exploitability analysis to call out to
|
||||
// objdump for diassembly. It is used to check the identity of the
|
||||
// instruction that caused the program to crash. If there are any
|
||||
// portability concerns, this should not be enabled.
|
||||
ExploitabilityLinux(Minidump* dump,
|
||||
ProcessState* process_state,
|
||||
bool enable_objdump);
|
||||
|
||||
virtual ExploitabilityRating CheckPlatformExploitability();
|
||||
|
||||
private:
|
||||
friend class ExploitabilityLinuxTest;
|
||||
|
||||
// Takes the address of the instruction pointer and returns
|
||||
// whether the instruction pointer lies in a valid instruction region.
|
||||
bool InstructionPointerInCode(uint64_t instruction_ptr);
|
||||
|
||||
// Checks the exception that triggered the creation of the
|
||||
// minidump and reports whether the exception suggests no exploitability.
|
||||
bool BenignCrashTrigger(const MDRawExceptionStream* raw_exception_stream);
|
||||
|
||||
// This method checks if the crash occurred during a write to read-only or
|
||||
// invalid memory. It does so by checking if the instruction at the
|
||||
// instruction pointer is a write instruction, and if the target of the
|
||||
// instruction is at a spot in memory that prohibits writes.
|
||||
bool EndedOnIllegalWrite(uint64_t instruction_ptr);
|
||||
|
||||
// Checks if the stack pointer points to a memory mapping that is not
|
||||
// labelled as the stack.
|
||||
bool StackPointerOffStack(uint64_t stack_ptr);
|
||||
|
||||
// Checks if the stack or heap are marked executable according
|
||||
// to the memory mappings.
|
||||
bool ExecutableStackOrHeap();
|
||||
|
||||
// Whether this exploitability engine is permitted to shell out to objdump
|
||||
// to disassemble raw bytes.
|
||||
bool enable_objdump_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_LINUX_H_
|
||||
182
externals/breakpad/src/processor/exploitability_unittest.cc
vendored
Normal file
182
externals/breakpad/src/processor/exploitability_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/minidump_processor.h"
|
||||
#include "google_breakpad/processor/process_state.h"
|
||||
#ifdef __linux__
|
||||
#include "processor/exploitability_linux.h"
|
||||
#endif // __linux__
|
||||
#include "processor/simple_symbol_supplier.h"
|
||||
|
||||
#ifdef __linux__
|
||||
namespace google_breakpad {
|
||||
class ExploitabilityLinuxTestMinidumpContext : public MinidumpContext {
|
||||
public:
|
||||
explicit ExploitabilityLinuxTestMinidumpContext(
|
||||
const MDRawContextAMD64& context) : MinidumpContext(NULL) {
|
||||
valid_ = true;
|
||||
SetContextAMD64(new MDRawContextAMD64(context));
|
||||
SetContextFlags(MD_CONTEXT_AMD64);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
#endif // __linux__
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
#ifdef __linux__
|
||||
using google_breakpad::ExploitabilityLinuxTestMinidumpContext;
|
||||
#endif // __linux__
|
||||
using google_breakpad::MinidumpProcessor;
|
||||
using google_breakpad::ProcessState;
|
||||
using google_breakpad::SimpleSymbolSupplier;
|
||||
|
||||
string TestDataDir() {
|
||||
return string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
||||
"/src/processor/testdata";
|
||||
}
|
||||
|
||||
// Find the given dump file in <srcdir>/src/processor/testdata, process it,
|
||||
// and get the exploitability rating. Returns EXPLOITABILITY_ERR_PROCESSING
|
||||
// if the crash dump can't be processed.
|
||||
google_breakpad::ExploitabilityRating
|
||||
ExploitabilityFor(const string& filename) {
|
||||
SimpleSymbolSupplier supplier(TestDataDir() + "/symbols");
|
||||
BasicSourceLineResolver resolver;
|
||||
MinidumpProcessor processor(&supplier, &resolver, true);
|
||||
processor.set_enable_objdump_for_exploitability(true);
|
||||
ProcessState state;
|
||||
|
||||
string minidump_file = TestDataDir() + "/" + filename;
|
||||
|
||||
if (processor.Process(minidump_file, &state) !=
|
||||
google_breakpad::PROCESS_OK) {
|
||||
return google_breakpad::EXPLOITABILITY_ERR_PROCESSING;
|
||||
}
|
||||
|
||||
return state.exploitability();
|
||||
}
|
||||
|
||||
TEST(ExploitabilityTest, TestWindowsEngine) {
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("ascii_read_av.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("ascii_read_av_block_write.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("ascii_read_av_clobber_write.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("ascii_read_av_conditional.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("ascii_read_av_then_jmp.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("ascii_read_av_xchg_write.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("ascii_write_av.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("ascii_write_av_arg_to_call.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE,
|
||||
ExploitabilityFor("null_read_av.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE,
|
||||
ExploitabilityFor("null_write_av.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE,
|
||||
ExploitabilityFor("stack_exhaustion.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("exec_av_on_stack.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_MEDIUM,
|
||||
ExploitabilityFor("write_av_non_null.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW,
|
||||
ExploitabilityFor("read_av_non_null.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW,
|
||||
ExploitabilityFor("read_av_clobber_write.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_LOW,
|
||||
ExploitabilityFor("read_av_conditional.dmp"));
|
||||
}
|
||||
|
||||
TEST(ExploitabilityTest, TestLinuxEngine) {
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_INTERESTING,
|
||||
ExploitabilityFor("linux_null_read_av.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_overflow.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_stacksmash.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE,
|
||||
ExploitabilityFor("linux_divide_by_zero.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_INTERESTING,
|
||||
ExploitabilityFor("linux_null_dereference.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_jmp_to_0.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_outside_module.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_NONE,
|
||||
ExploitabilityFor("linux_raise_sigabrt.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_INTERESTING,
|
||||
ExploitabilityFor("linux_inside_module_exe_region1.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_INTERESTING,
|
||||
ExploitabilityFor("linux_inside_module_exe_region2.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_INTERESTING,
|
||||
ExploitabilityFor("linux_stack_pointer_in_stack.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_INTERESTING,
|
||||
ExploitabilityFor("linux_stack_pointer_in_stack_alt_name.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_stack_pointer_in_module.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_executable_stack.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_executable_heap.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_jmp_to_module_not_exe_region.dmp"));
|
||||
#ifdef __linux__
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_write_to_nonwritable_module.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_write_to_nonwritable_region_math.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_write_to_outside_module.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_HIGH,
|
||||
ExploitabilityFor("linux_write_to_outside_module_via_math.dmp"));
|
||||
ASSERT_EQ(google_breakpad::EXPLOITABILITY_INTERESTING,
|
||||
ExploitabilityFor("linux_write_to_under_4k.dmp"));
|
||||
#endif // __linux__
|
||||
}
|
||||
|
||||
} // namespace
|
||||
285
externals/breakpad/src/processor/exploitability_win.cc
vendored
Normal file
285
externals/breakpad/src/processor/exploitability_win.cc
vendored
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "processor/exploitability_win.h"
|
||||
|
||||
#include "common/scoped_ptr.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 "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 uint64_t kProbableNullOffset = 4096;
|
||||
static const uint64_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;
|
||||
}
|
||||
uint64_t address = process_state_->crash_address();
|
||||
uint32_t exception_code = raw_exception->exception_record.exception_code;
|
||||
|
||||
uint32_t exploitability_weight = 0;
|
||||
|
||||
uint64_t stack_ptr = 0;
|
||||
uint64_t instruction_ptr = 0;
|
||||
|
||||
// Getting the instruction pointer.
|
||||
if (!context->GetInstructionPointer(&instruction_ptr)) {
|
||||
return EXPLOITABILITY_ERR_PROCESSING;
|
||||
}
|
||||
|
||||
// Getting the stack pointer.
|
||||
if (!context->GetStackPointer(&stack_ptr)) {
|
||||
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;
|
||||
}
|
||||
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.
|
||||
uint32_t memory_offset =
|
||||
instruction_ptr - instruction_region->GetBase();
|
||||
uint32_t available_memory =
|
||||
instruction_region->GetSize() - memory_offset;
|
||||
available_memory = available_memory > kDisassembleBytesBeyondPC ?
|
||||
kDisassembleBytesBeyondPC : available_memory;
|
||||
if (available_memory) {
|
||||
const uint8_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
|
||||
54
externals/breakpad/src/processor/exploitability_win.h
vendored
Normal file
54
externals/breakpad/src/processor/exploitability_win.h
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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_
|
||||
364
externals/breakpad/src/processor/fast_source_line_resolver.cc
vendored
Normal file
364
externals/breakpad/src/processor/fast_source_line_resolver.cc
vendored
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "google_breakpad/processor/fast_source_line_resolver.h"
|
||||
#include "processor/fast_source_line_resolver_types.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/module_factory.h"
|
||||
#include "processor/simple_serializer-inl.h"
|
||||
|
||||
using std::deque;
|
||||
using std::unique_ptr;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
FastSourceLineResolver::FastSourceLineResolver()
|
||||
: SourceLineResolverBase(new FastModuleFactory) { }
|
||||
|
||||
bool FastSourceLineResolver::ShouldDeleteMemoryBufferAfterLoadModule() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void FastSourceLineResolver::Module::LookupAddress(
|
||||
StackFrame* frame,
|
||||
std::deque<std::unique_ptr<StackFrame>>* inlined_frames) 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->CopyFrom(func_ptr);
|
||||
frame->function_name = func->name;
|
||||
frame->function_base = frame->module->base_address() + function_base;
|
||||
frame->is_multiple = func->is_multiple;
|
||||
|
||||
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->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;
|
||||
}
|
||||
// Check if this is inlined function call.
|
||||
if (inlined_frames) {
|
||||
ConstructInlineFrames(frame, address, func->inlines, inlined_frames);
|
||||
}
|
||||
} else if (public_symbols_.Retrieve(address,
|
||||
public_symbol_ptr, &public_address) &&
|
||||
(!func_ptr || public_address > function_base)) {
|
||||
public_symbol->CopyFrom(public_symbol_ptr);
|
||||
frame->function_name = public_symbol->name;
|
||||
frame->function_base = frame->module->base_address() + public_address;
|
||||
frame->is_multiple = public_symbol->is_multiple;
|
||||
}
|
||||
}
|
||||
|
||||
void FastSourceLineResolver::Module::ConstructInlineFrames(
|
||||
StackFrame* frame,
|
||||
MemAddr address,
|
||||
const StaticContainedRangeMap<MemAddr, char>& inline_map,
|
||||
std::deque<std::unique_ptr<StackFrame>>* inlined_frames) const {
|
||||
std::vector<const char*> inline_ptrs;
|
||||
if (!inline_map.RetrieveRanges(address, inline_ptrs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const char* inline_ptr : inline_ptrs) {
|
||||
scoped_ptr<Inline> in(new Inline);
|
||||
in->CopyFrom(inline_ptr);
|
||||
unique_ptr<StackFrame> new_frame =
|
||||
unique_ptr<StackFrame>(new StackFrame(*frame));
|
||||
auto origin_iter = inline_origins_.find(in->origin_id);
|
||||
if (origin_iter != inline_origins_.end()) {
|
||||
scoped_ptr<InlineOrigin> origin(new InlineOrigin);
|
||||
origin->CopyFrom(origin_iter.GetValuePtr());
|
||||
new_frame->function_name = origin->name;
|
||||
} else {
|
||||
new_frame->function_name = "<name omitted>";
|
||||
}
|
||||
|
||||
// Store call site file and line in current frame, which will be updated
|
||||
// later.
|
||||
new_frame->source_line = in->call_site_line;
|
||||
if (in->has_call_site_file_id) {
|
||||
auto file_iter = files_.find(in->call_site_file_id);
|
||||
if (file_iter != files_.end()) {
|
||||
new_frame->source_file_name = file_iter.GetValuePtr();
|
||||
}
|
||||
}
|
||||
|
||||
// Use the starting adress of the inlined range as inlined function base.
|
||||
new_frame->function_base = new_frame->module->base_address();
|
||||
for (const auto& range : in->inline_ranges) {
|
||||
if (address >= range.first && address < range.first + range.second) {
|
||||
new_frame->function_base += range.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
new_frame->trust = StackFrame::FRAME_TRUST_INLINE;
|
||||
|
||||
// The inlines vector has an order from innermost entry to outermost entry.
|
||||
// By push_back, we will have inlined_frames with the same order.
|
||||
inlined_frames->push_back(std::move(new_frame));
|
||||
}
|
||||
|
||||
// Update the source file and source line for each inlined frame.
|
||||
if (!inlined_frames->empty()) {
|
||||
string parent_frame_source_file_name = frame->source_file_name;
|
||||
int parent_frame_source_line = frame->source_line;
|
||||
frame->source_file_name = inlined_frames->back()->source_file_name;
|
||||
frame->source_line = inlined_frames->back()->source_line;
|
||||
for (unique_ptr<StackFrame>& inlined_frame : *inlined_frames) {
|
||||
std::swap(inlined_frame->source_file_name, parent_frame_source_file_name);
|
||||
std::swap(inlined_frame->source_line, parent_frame_source_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WFI: WindowsFrameInfo.
|
||||
// Returns a WFI object reading from a raw memory chunk of data
|
||||
WindowsFrameInfo FastSourceLineResolver::CopyWFI(const char* raw) {
|
||||
const WindowsFrameInfo::StackInfoTypes type =
|
||||
static_cast<const WindowsFrameInfo::StackInfoTypes>(
|
||||
*reinterpret_cast<const int32_t*>(raw));
|
||||
|
||||
// The first 8 bytes of int data are unused.
|
||||
// They correspond to "StackInfoTypes type_;" and "int valid;"
|
||||
// data member of WFI.
|
||||
const uint32_t* para_uint32 = reinterpret_cast<const uint32_t*>(
|
||||
raw + 2 * sizeof(int32_t));
|
||||
|
||||
uint32_t prolog_size = para_uint32[0];;
|
||||
uint32_t epilog_size = para_uint32[1];
|
||||
uint32_t parameter_size = para_uint32[2];
|
||||
uint32_t saved_register_size = para_uint32[3];
|
||||
uint32_t local_size = para_uint32[4];
|
||||
uint32_t max_stack_size = para_uint32[5];
|
||||
const char* boolean = reinterpret_cast<const char*>(para_uint32 + 6);
|
||||
bool allocates_base_pointer = (*boolean != 0);
|
||||
string program_string = boolean + 1;
|
||||
|
||||
return WindowsFrameInfo(type,
|
||||
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* memory_buffer,
|
||||
size_t memory_buffer_size) {
|
||||
if (!memory_buffer) return false;
|
||||
|
||||
// Read the "is_corrupt" flag.
|
||||
const char* mem_buffer = memory_buffer;
|
||||
mem_buffer = SimpleSerializer<bool>::Read(mem_buffer, &is_corrupt_);
|
||||
|
||||
const uint32_t* map_sizes = reinterpret_cast<const uint32_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];
|
||||
}
|
||||
unsigned int expected_size = sizeof(bool) + offsets[kNumberMaps_ - 1] +
|
||||
map_sizes[kNumberMaps_ - 1] + 1;
|
||||
if (expected_size != memory_buffer_size &&
|
||||
// Allow for having an extra null terminator.
|
||||
expected_size != memory_buffer_size - 1) {
|
||||
// This could either be a random corruption or the serialization format was
|
||||
// changed without updating the version in kSerializedBreakpadFileExtension.
|
||||
BPLOG(ERROR) << "Memory buffer is either corrupt or an unsupported version"
|
||||
<< ", expected size: " << expected_size
|
||||
<< ", actual size: " << memory_buffer_size;
|
||||
return false;
|
||||
}
|
||||
BPLOG(INFO) << "Memory buffer size looks good, size: " << memory_buffer_size;
|
||||
|
||||
// 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++]);
|
||||
inline_origins_ = StaticMap<int, 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->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->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
|
||||
249
externals/breakpad/src/processor/fast_source_line_resolver_types.h
vendored
Normal file
249
externals/breakpad/src/processor/fast_source_line_resolver_types.h
vendored
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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 <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "google_breakpad/processor/fast_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "processor/contained_range_map.h"
|
||||
#include "processor/simple_serializer-inl.h"
|
||||
#include "processor/source_line_resolver_base_types.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 {
|
||||
|
||||
#define DESERIALIZE(raw_ptr, field) \
|
||||
field = *(reinterpret_cast<const decltype(field)*>(raw_ptr)); \
|
||||
raw_ptr += sizeof(field);
|
||||
|
||||
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) {
|
||||
DESERIALIZE(raw, address);
|
||||
DESERIALIZE(raw, size);
|
||||
DESERIALIZE(raw, source_file_id);
|
||||
DESERIALIZE(raw, line);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
raw += name_size;
|
||||
DESERIALIZE(raw, address);
|
||||
DESERIALIZE(raw, size);
|
||||
DESERIALIZE(raw, parameter_size);
|
||||
raw = SimpleSerializer<bool>::Read(raw, &is_multiple);
|
||||
int32_t inline_size;
|
||||
DESERIALIZE(raw, inline_size);
|
||||
inlines = StaticContainedRangeMap<MemAddr, char>(raw);
|
||||
lines = StaticRangeMap<MemAddr, Line>(raw + inline_size);
|
||||
}
|
||||
|
||||
StaticContainedRangeMap<MemAddr, char> inlines;
|
||||
StaticRangeMap<MemAddr, Line> lines;
|
||||
};
|
||||
|
||||
struct FastSourceLineResolver::Inline : public SourceLineResolverBase::Inline {
|
||||
void CopyFrom(const Inline* inline_ptr) {
|
||||
const char* raw = reinterpret_cast<const char*>(inline_ptr);
|
||||
CopyFrom(raw);
|
||||
}
|
||||
|
||||
// De-serialize the memory data of a Inline.
|
||||
void CopyFrom(const char* raw) {
|
||||
DESERIALIZE(raw, has_call_site_file_id);
|
||||
DESERIALIZE(raw, inline_nest_level);
|
||||
DESERIALIZE(raw, call_site_line);
|
||||
DESERIALIZE(raw, call_site_file_id);
|
||||
DESERIALIZE(raw, origin_id);
|
||||
uint32_t inline_range_size;
|
||||
DESERIALIZE(raw, inline_range_size);
|
||||
for (size_t i = 0; i < inline_range_size; i += 2) {
|
||||
std::pair<MemAddr, MemAddr> range;
|
||||
DESERIALIZE(raw, range.first);
|
||||
DESERIALIZE(raw, range.second);
|
||||
inline_ranges.push_back(range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FastSourceLineResolver::InlineOrigin
|
||||
: public SourceLineResolverBase::InlineOrigin {
|
||||
void CopyFrom(const InlineOrigin* origin_ptr) {
|
||||
const char* raw = reinterpret_cast<const char*>(origin_ptr);
|
||||
CopyFrom(raw);
|
||||
}
|
||||
|
||||
// De-serialize the memory data of a Line.
|
||||
void CopyFrom(const char* raw) {
|
||||
DESERIALIZE(raw, has_file_id);
|
||||
DESERIALIZE(raw, source_file_id);
|
||||
name = raw;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
raw += name_size;
|
||||
DESERIALIZE(raw, address);
|
||||
DESERIALIZE(raw, parameter_size);
|
||||
raw = SimpleSerializer<bool>::Read(raw, &is_multiple);
|
||||
}
|
||||
};
|
||||
|
||||
#undef DESERIALIZE
|
||||
|
||||
class FastSourceLineResolver::Module: public SourceLineResolverBase::Module {
|
||||
public:
|
||||
explicit Module(const string& name) : name_(name), is_corrupt_(false) { }
|
||||
virtual ~Module() { }
|
||||
|
||||
// Looks up the given relative address, and fills the StackFrame struct
|
||||
// with the result.
|
||||
virtual void LookupAddress(
|
||||
StackFrame* frame,
|
||||
std::deque<std::unique_ptr<StackFrame>>* inlined_frames) const;
|
||||
|
||||
// Construct inlined frames for |frame| and store them in |inline_frames|.
|
||||
// |frame|'s source line and source file name may be updated if an inlined
|
||||
// frame is found inside |frame|. As a result, the innermost inlined frame
|
||||
// will be the first one in |inline_frames|.
|
||||
virtual void ConstructInlineFrames(
|
||||
StackFrame* frame,
|
||||
MemAddr address,
|
||||
const StaticContainedRangeMap<MemAddr, char>& inline_map,
|
||||
std::deque<std::unique_ptr<StackFrame>>* inlined_frames) const;
|
||||
|
||||
// Loads a map from the given buffer in char* type.
|
||||
virtual bool LoadMapFromMemory(char* memory_buffer,
|
||||
size_t memory_buffer_size);
|
||||
|
||||
// Tells whether the loaded symbol data is corrupt. Return value is
|
||||
// undefined, if the symbol data hasn't been loaded yet.
|
||||
virtual bool IsCorrupt() const { return is_corrupt_; }
|
||||
|
||||
// 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_ = 6 + 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_;
|
||||
bool is_corrupt_;
|
||||
|
||||
// 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_;
|
||||
|
||||
// INLINE_ORIGIN records: used as a function name string pool for INLINE
|
||||
// records.
|
||||
StaticMap<int, char> inline_origins_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_FAST_SOURCE_LINE_RESOLVER_TYPES_H__
|
||||
603
externals/breakpad/src/processor/fast_source_line_resolver_unittest.cc
vendored
Normal file
603
externals/breakpad/src/processor/fast_source_line_resolver_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,603 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/using_std_string.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 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::scoped_ptr;
|
||||
|
||||
class TestCodeModule : public CodeModule {
|
||||
public:
|
||||
explicit TestCodeModule(string code_file) : code_file_(code_file) {}
|
||||
virtual ~TestCodeModule() {}
|
||||
|
||||
virtual uint64_t base_address() const { return 0; }
|
||||
virtual uint64_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 CodeModule* Copy() const {
|
||||
return new TestCodeModule(code_file_);
|
||||
}
|
||||
virtual bool is_unloaded() const { return false; }
|
||||
virtual uint64_t shrink_down_delta() const { return 0; }
|
||||
virtual void SetShrinkDownDelta(uint64_t shrink_down_delta) {}
|
||||
|
||||
private:
|
||||
string code_file_;
|
||||
};
|
||||
|
||||
// A mock memory region object, for use by the STACK CFI tests.
|
||||
class MockMemoryRegion: public MemoryRegion {
|
||||
uint64_t GetBase() const { return 0x10000; }
|
||||
uint32_t GetSize() const { return 0x01000; }
|
||||
bool GetMemoryAtAddress(uint64_t address, uint8_t* value) const {
|
||||
*value = address & 0xff;
|
||||
return true;
|
||||
}
|
||||
bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const {
|
||||
*value = address & 0xffff;
|
||||
return true;
|
||||
}
|
||||
bool GetMemoryAtAddress(uint64_t address, uint32_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(uint64_t address, uint64_t* value) const {
|
||||
*value = address;
|
||||
return true;
|
||||
}
|
||||
void Print() const {
|
||||
assert(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 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<uint32_t>& expected,
|
||||
const CFIFrameInfo::RegisterValueMap<uint32_t>& actual) {
|
||||
CFIFrameInfo::RegisterValueMap<uint32_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<uint32_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, nullptr);
|
||||
ASSERT_FALSE(frame.module);
|
||||
ASSERT_TRUE(frame.function_name.empty());
|
||||
ASSERT_EQ(frame.function_base, 0U);
|
||||
ASSERT_TRUE(frame.source_file_name.empty());
|
||||
ASSERT_EQ(frame.source_line, 0);
|
||||
ASSERT_EQ(frame.source_line_base, 0U);
|
||||
ASSERT_EQ(frame.is_multiple, false);
|
||||
|
||||
frame.module = &module1;
|
||||
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||
ASSERT_EQ(frame.function_name, "Function1_1");
|
||||
ASSERT_TRUE(frame.module);
|
||||
ASSERT_EQ(frame.module->code_file(), "module1");
|
||||
ASSERT_EQ(frame.function_base, 0x1000U);
|
||||
ASSERT_EQ(frame.source_file_name, "file1_1.cc");
|
||||
ASSERT_EQ(frame.source_line, 44);
|
||||
ASSERT_EQ(frame.source_line_base, 0x1000U);
|
||||
ASSERT_EQ(frame.is_multiple, true);
|
||||
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, nullptr);
|
||||
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, nullptr);
|
||||
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_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_UNKNOWN);
|
||||
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
|
||||
ASSERT_TRUE(windows_frame_info->program_string.empty());
|
||||
|
||||
frame.instruction = 0x1380;
|
||||
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||
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_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
|
||||
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<uint32_t> current_registers;
|
||||
CFIFrameInfo::RegisterValueMap<uint32_t> caller_registers;
|
||||
CFIFrameInfo::RegisterValueMap<uint32_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<uint32_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<uint32_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<uint32_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<uint32_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<uint32_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<uint32_t>(current_registers, memory,
|
||||
&caller_registers));
|
||||
VerifyRegisters(__FILE__, __LINE__,
|
||||
expected_caller_registers, caller_registers);
|
||||
|
||||
frame.instruction = 0x2900;
|
||||
frame.module = &module1;
|
||||
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||
ASSERT_EQ(frame.function_name, string("PublicSymbol"));
|
||||
EXPECT_EQ(frame.is_multiple, true);
|
||||
|
||||
frame.instruction = 0x4000;
|
||||
frame.module = &module1;
|
||||
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||
ASSERT_EQ(frame.function_name, string("LargeFunction"));
|
||||
|
||||
frame.instruction = 0x2181;
|
||||
frame.module = &module2;
|
||||
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||
ASSERT_EQ(frame.function_name, "Function2_2");
|
||||
ASSERT_EQ(frame.function_base, 0x2170U);
|
||||
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, 0x2180U);
|
||||
ASSERT_EQ(frame.is_multiple, false);
|
||||
windows_frame_info.reset(fast_resolver.FindWindowsFrameInfo(&frame));
|
||||
ASSERT_TRUE(windows_frame_info.get());
|
||||
ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
|
||||
ASSERT_EQ(windows_frame_info->prolog_size, 1U);
|
||||
|
||||
frame.instruction = 0x216f;
|
||||
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||
ASSERT_EQ(frame.function_name, "Public2_1");
|
||||
EXPECT_EQ(frame.is_multiple, false);
|
||||
|
||||
ClearSourceLineInfo(&frame);
|
||||
frame.instruction = 0x219f;
|
||||
frame.module = &module2;
|
||||
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||
ASSERT_TRUE(frame.function_name.empty());
|
||||
|
||||
frame.instruction = 0x21a0;
|
||||
frame.module = &module2;
|
||||
fast_resolver.FillSourceLineInfo(&frame, nullptr);
|
||||
ASSERT_EQ(frame.function_name, "Public2_2");
|
||||
}
|
||||
|
||||
// Test adapted from basic_source_line_resolver_unittest.
|
||||
TEST_F(TestFastSourceLineResolver, TestLoadAndResolveOldInlines) {
|
||||
TestCodeModule module("linux_inline");
|
||||
ASSERT_TRUE(basic_resolver.LoadModule(
|
||||
&module, testdata_dir +
|
||||
"/symbols/linux_inline/BBA6FA10B8AAB33D00000000000000000/"
|
||||
"linux_inline.old.sym"));
|
||||
ASSERT_TRUE(basic_resolver.HasModule(&module));
|
||||
// Convert module1 to fast_module:
|
||||
ASSERT_TRUE(serializer.ConvertOneModule(module.code_file(), &basic_resolver,
|
||||
&fast_resolver));
|
||||
ASSERT_TRUE(fast_resolver.HasModule(&module));
|
||||
|
||||
StackFrame frame;
|
||||
std::deque<std::unique_ptr<StackFrame>> inlined_frames;
|
||||
frame.instruction = 0x161b6;
|
||||
frame.module = &module;
|
||||
fast_resolver.FillSourceLineInfo(&frame, &inlined_frames);
|
||||
|
||||
// main frame.
|
||||
ASSERT_EQ(frame.function_name, "main");
|
||||
ASSERT_EQ(frame.function_base, 0x15b30U);
|
||||
ASSERT_EQ(frame.source_file_name, "linux_inline.cpp");
|
||||
ASSERT_EQ(frame.source_line, 42);
|
||||
ASSERT_EQ(frame.source_line_base, 0x161b6U);
|
||||
ASSERT_EQ(frame.is_multiple, false);
|
||||
|
||||
ASSERT_EQ(inlined_frames.size(), 3UL);
|
||||
|
||||
// Inlined frames inside main frame.
|
||||
ASSERT_EQ(inlined_frames[2]->function_name, "foo()");
|
||||
ASSERT_EQ(inlined_frames[2]->function_base, 0x15b45U);
|
||||
ASSERT_EQ(inlined_frames[2]->source_file_name, "linux_inline.cpp");
|
||||
ASSERT_EQ(inlined_frames[2]->source_line, 39);
|
||||
ASSERT_EQ(inlined_frames[2]->source_line_base, 0x161b6U);
|
||||
ASSERT_EQ(inlined_frames[2]->trust, StackFrame::FRAME_TRUST_INLINE);
|
||||
|
||||
ASSERT_EQ(inlined_frames[1]->function_name, "bar()");
|
||||
ASSERT_EQ(inlined_frames[1]->function_base, 0x15b72U);
|
||||
ASSERT_EQ(inlined_frames[1]->source_file_name, "linux_inline.cpp");
|
||||
ASSERT_EQ(inlined_frames[1]->source_line, 32);
|
||||
ASSERT_EQ(inlined_frames[1]->source_line_base, 0x161b6U);
|
||||
ASSERT_EQ(inlined_frames[1]->trust, StackFrame::FRAME_TRUST_INLINE);
|
||||
|
||||
ASSERT_EQ(inlined_frames[0]->function_name, "func()");
|
||||
ASSERT_EQ(inlined_frames[0]->function_base, 0x15b83U);
|
||||
ASSERT_EQ(inlined_frames[0]->source_file_name, "linux_inline.cpp");
|
||||
ASSERT_EQ(inlined_frames[0]->source_line, 27);
|
||||
ASSERT_EQ(inlined_frames[0]->source_line_base, 0x161b6U);
|
||||
ASSERT_EQ(inlined_frames[0]->trust, StackFrame::FRAME_TRUST_INLINE);
|
||||
}
|
||||
|
||||
// Test adapted from basic_source_line_resolver_unittest.
|
||||
TEST_F(TestFastSourceLineResolver, TestLoadAndResolveNewInlines) {
|
||||
TestCodeModule module("linux_inline");
|
||||
ASSERT_TRUE(basic_resolver.LoadModule(
|
||||
&module, testdata_dir +
|
||||
"/symbols/linux_inline/BBA6FA10B8AAB33D00000000000000000/"
|
||||
"linux_inline.new.sym"));
|
||||
ASSERT_TRUE(basic_resolver.HasModule(&module));
|
||||
// Convert module1 to fast_module:
|
||||
ASSERT_TRUE(serializer.ConvertOneModule(module.code_file(), &basic_resolver,
|
||||
&fast_resolver));
|
||||
ASSERT_TRUE(fast_resolver.HasModule(&module));
|
||||
|
||||
StackFrame frame;
|
||||
std::deque<std::unique_ptr<StackFrame>> inlined_frames;
|
||||
frame.instruction = 0x161b6;
|
||||
frame.module = &module;
|
||||
fast_resolver.FillSourceLineInfo(&frame, &inlined_frames);
|
||||
|
||||
// main frame.
|
||||
ASSERT_EQ(frame.function_name, "main");
|
||||
ASSERT_EQ(frame.function_base, 0x15b30U);
|
||||
ASSERT_EQ(frame.source_file_name, "a.cpp");
|
||||
ASSERT_EQ(frame.source_line, 42);
|
||||
ASSERT_EQ(frame.source_line_base, 0x161b6U);
|
||||
ASSERT_EQ(frame.is_multiple, false);
|
||||
|
||||
ASSERT_EQ(inlined_frames.size(), 3UL);
|
||||
|
||||
// Inlined frames inside main frame.
|
||||
ASSERT_EQ(inlined_frames[2]->function_name, "foo()");
|
||||
ASSERT_EQ(inlined_frames[2]->function_base, 0x15b45U);
|
||||
ASSERT_EQ(inlined_frames[2]->source_file_name, "b.cpp");
|
||||
ASSERT_EQ(inlined_frames[2]->source_line, 39);
|
||||
ASSERT_EQ(inlined_frames[2]->source_line_base, 0x161b6U);
|
||||
ASSERT_EQ(inlined_frames[2]->trust, StackFrame::FRAME_TRUST_INLINE);
|
||||
|
||||
ASSERT_EQ(inlined_frames[1]->function_name, "bar()");
|
||||
ASSERT_EQ(inlined_frames[1]->function_base, 0x15b72U);
|
||||
ASSERT_EQ(inlined_frames[1]->source_file_name, "c.cpp");
|
||||
ASSERT_EQ(inlined_frames[1]->source_line, 32);
|
||||
ASSERT_EQ(inlined_frames[1]->source_line_base, 0x161b6U);
|
||||
ASSERT_EQ(inlined_frames[1]->trust, StackFrame::FRAME_TRUST_INLINE);
|
||||
|
||||
ASSERT_EQ(inlined_frames[0]->function_name, "func()");
|
||||
ASSERT_EQ(inlined_frames[0]->function_base, 0x15b83U);
|
||||
ASSERT_EQ(inlined_frames[0]->source_file_name, "linux_inline.cpp");
|
||||
ASSERT_EQ(inlined_frames[0]->source_line, 27);
|
||||
ASSERT_EQ(inlined_frames[0]->source_line_base, 0x161b6U);
|
||||
ASSERT_EQ(inlined_frames[0]->trust, StackFrame::FRAME_TRUST_INLINE);
|
||||
}
|
||||
|
||||
TEST_F(TestFastSourceLineResolver, TestInvalidLoads) {
|
||||
TestCodeModule module3("module3");
|
||||
ASSERT_TRUE(basic_resolver.LoadModule(&module3,
|
||||
testdata_dir + "/module3_bad.out"));
|
||||
ASSERT_TRUE(basic_resolver.HasModule(&module3));
|
||||
ASSERT_TRUE(basic_resolver.IsModuleCorrupt(&module3));
|
||||
// Convert module3 to fast_module:
|
||||
ASSERT_TRUE(serializer.ConvertOneModule(module3.code_file(),
|
||||
&basic_resolver,
|
||||
&fast_resolver));
|
||||
ASSERT_TRUE(fast_resolver.HasModule(&module3));
|
||||
ASSERT_TRUE(fast_resolver.IsModuleCorrupt(&module3));
|
||||
|
||||
TestCodeModule module4("module4");
|
||||
ASSERT_TRUE(basic_resolver.LoadModule(&module4,
|
||||
testdata_dir + "/module4_bad.out"));
|
||||
ASSERT_TRUE(basic_resolver.HasModule(&module4));
|
||||
ASSERT_TRUE(basic_resolver.IsModuleCorrupt(&module4));
|
||||
// Convert module4 to fast_module:
|
||||
ASSERT_TRUE(serializer.ConvertOneModule(module4.code_file(),
|
||||
&basic_resolver,
|
||||
&fast_resolver));
|
||||
ASSERT_TRUE(fast_resolver.HasModule(&module4));
|
||||
ASSERT_TRUE(fast_resolver.IsModuleCorrupt(&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;
|
||||
size_t symbol_data_size;
|
||||
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_file(module_index), &symbol_data, &symbol_data_size));
|
||||
symbol_data_string.assign(symbol_data, symbol_data_size);
|
||||
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();
|
||||
}
|
||||
192
externals/breakpad/src/processor/linked_ptr.h
vendored
Normal file
192
externals/breakpad/src/processor/linked_ptr.h
vendored
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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() {
|
||||
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__
|
||||
117
externals/breakpad/src/processor/logging.cc
vendored
Normal file
117
externals/breakpad/src/processor/logging.cc
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// Copyright 2007 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/stdio_wrapper.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/pathname_stripper.h"
|
||||
|
||||
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;
|
||||
case SEVERITY_CRITICAL:
|
||||
severity_string = "CRITICAL";
|
||||
break;
|
||||
}
|
||||
|
||||
stream_ << time_string << ": " << PathnameStripper::File(file) << ":" <<
|
||||
line << ": " << severity_string << ": ";
|
||||
}
|
||||
|
||||
LogStream::~LogStream() {
|
||||
stream_ << std::endl;
|
||||
}
|
||||
|
||||
string HexString(uint32_t number) {
|
||||
char buffer[11];
|
||||
snprintf(buffer, sizeof(buffer), "0x%x", number);
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
string HexString(uint64_t number) {
|
||||
char buffer[19];
|
||||
snprintf(buffer, sizeof(buffer), "0x%" PRIx64, number);
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
string HexString(int number) {
|
||||
char buffer[19];
|
||||
snprintf(buffer, sizeof(buffer), "0x%x", number);
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
int ErrnoString(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
|
||||
187
externals/breakpad/src/processor/logging.h
vendored
Normal file
187
externals/breakpad/src/processor/logging.h
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// Copyright 2007 Google LLC
|
||||
//
|
||||
// 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 LLC 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 "common/using_std_string.h"
|
||||
#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,
|
||||
SEVERITY_CRITICAL
|
||||
};
|
||||
|
||||
// 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".
|
||||
string HexString(uint32_t number);
|
||||
string HexString(uint64_t number);
|
||||
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(string* error_string);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#ifndef BPLOG_INIT
|
||||
#define BPLOG_INIT(pargc, pargv)
|
||||
#endif // BPLOG_INIT
|
||||
|
||||
#ifndef BPLOG_LAZY_STREAM
|
||||
#define BPLOG_LAZY_STREAM(stream, condition) \
|
||||
!(condition) ? (void) 0 : \
|
||||
google_breakpad::LogMessageVoidify() & (BPLOG_ ## stream)
|
||||
#endif
|
||||
|
||||
#ifndef BPLOG_MINIMUM_SEVERITY
|
||||
#define BPLOG_MINIMUM_SEVERITY SEVERITY_INFO
|
||||
#endif
|
||||
|
||||
#define BPLOG_LOG_IS_ON(severity) \
|
||||
((google_breakpad::LogStream::SEVERITY_ ## severity) >= \
|
||||
(google_breakpad::LogStream::BPLOG_MINIMUM_SEVERITY))
|
||||
|
||||
#ifndef BPLOG
|
||||
#define BPLOG(severity) BPLOG_LAZY_STREAM(severity, BPLOG_LOG_IS_ON(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
|
||||
|
||||
#ifndef BPLOG_CRITICAL
|
||||
#ifndef BPLOG_CRITICAL_STREAM
|
||||
#define BPLOG_CRITICAL_STREAM std::cerr
|
||||
#endif // BPLOG_CRITICAL_STREAM
|
||||
#define BPLOG_CRITICAL google_breakpad::LogStream(BPLOG_CRITICAL_STREAM, \
|
||||
google_breakpad::LogStream::SEVERITY_CRITICAL, \
|
||||
__FILE__, __LINE__)
|
||||
#endif // BPLOG_CRITICAL
|
||||
|
||||
#ifndef BPLOG_IF
|
||||
#define BPLOG_IF(severity, condition) \
|
||||
BPLOG_LAZY_STREAM(severity, ((condition) && BPLOG_LOG_IS_ON(severity)))
|
||||
#endif // BPLOG_IF
|
||||
|
||||
#endif // PROCESSOR_LOGGING_H__
|
||||
265
externals/breakpad/src/processor/map_serializers-inl.h
vendored
Normal file
265
externals/breakpad/src/processor/map_serializers-inl.h
vendored
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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(uint32_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<uint32_t>::Write(m.size(), dest);
|
||||
// Nodes offsets.
|
||||
uint32_t* offsets = reinterpret_cast<uint32_t*>(dest);
|
||||
dest += sizeof(uint32_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<uint32_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(uint32_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<uint32_t>::Write(m.map_.size(), dest);
|
||||
// Nodes offsets.
|
||||
uint32_t* offsets = reinterpret_cast<uint32_t*>(dest);
|
||||
dest += sizeof(uint32_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<uint32_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(uint32_t);
|
||||
size += header_size;
|
||||
// In case m.map_ == NULL, we treat it as an empty map:
|
||||
size += sizeof(uint32_t);
|
||||
if (m->map_) {
|
||||
size += m->map_->size() * sizeof(uint32_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<uint32_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<uint32_t>::Write(0, dest);
|
||||
} else {
|
||||
dest = SimpleSerializer<uint32_t>::Write(m->map_->size(), dest);
|
||||
uint32_t* offsets = reinterpret_cast<uint32_t*>(dest);
|
||||
dest += sizeof(uint32_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<uint32_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__
|
||||
167
externals/breakpad/src/processor/map_serializers.h
vendored
Normal file
167
externals/breakpad/src/processor/map_serializers.h
vendored
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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__
|
||||
389
externals/breakpad/src/processor/map_serializers_unittest.cc
vendored
Normal file
389
externals/breakpad/src/processor/map_serializers_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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;
|
||||
|
||||
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_;
|
||||
uint32_t serialized_size_;
|
||||
char* serialized_data_;
|
||||
};
|
||||
|
||||
TEST_F(TestStdMapSerializer, EmptyMapTestCase) {
|
||||
const int32_t correct_data[] = { 0 };
|
||||
uint32_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
|
||||
};
|
||||
uint32_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
|
||||
};
|
||||
uint32_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_;
|
||||
uint32_t serialized_size_;
|
||||
char* serialized_data_;
|
||||
};
|
||||
|
||||
TEST_F(TestAddressMapSerializer, EmptyMapTestCase) {
|
||||
const int32_t correct_data[] = { 0 };
|
||||
uint32_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
|
||||
};
|
||||
uint32_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
|
||||
};
|
||||
uint32_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_;
|
||||
uint32_t serialized_size_;
|
||||
char* serialized_data_;
|
||||
};
|
||||
|
||||
TEST_F(TestRangeMapSerializer, EmptyMapTestCase) {
|
||||
const int32_t correct_data[] = { 0 };
|
||||
uint32_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
|
||||
};
|
||||
uint32_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
|
||||
};
|
||||
uint32_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_;
|
||||
uint32_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
|
||||
};
|
||||
uint32_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
|
||||
};
|
||||
uint32_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
|
||||
};
|
||||
uint32_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();
|
||||
}
|
||||
408
externals/breakpad/src/processor/microdump.cc
vendored
Normal file
408
externals/breakpad/src/processor/microdump.cc
vendored
Normal file
|
|
@ -0,0 +1,408 @@
|
|||
// Copyright 2014 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
// microdump.cc: A microdump reader.
|
||||
//
|
||||
// See microdump.h for documentation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "google_breakpad/processor/microdump.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "google_breakpad/common/minidump_cpu_arm.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "processor/basic_code_module.h"
|
||||
#include "processor/convert_old_arm64_context.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/range_map-inl.h"
|
||||
|
||||
namespace {
|
||||
static const char kGoogleBreakpadKey[] = "google-breakpad";
|
||||
static const char kMicrodumpBegin[] = "-----BEGIN BREAKPAD MICRODUMP-----";
|
||||
static const char kMicrodumpEnd[] = "-----END BREAKPAD MICRODUMP-----";
|
||||
static const char kOsKey[] = ": O ";
|
||||
static const char kCpuKey[] = ": C ";
|
||||
static const char kCrashReasonKey[] = ": R ";
|
||||
static const char kGpuKey[] = ": G ";
|
||||
static const char kMmapKey[] = ": M ";
|
||||
static const char kStackKey[] = ": S ";
|
||||
static const char kStackFirstLineKey[] = ": S 0 ";
|
||||
static const char kArmArchitecture[] = "arm";
|
||||
static const char kArm64Architecture[] = "arm64";
|
||||
static const char kX86Architecture[] = "x86";
|
||||
static const char kMipsArchitecture[] = "mips";
|
||||
static const char kMips64Architecture[] = "mips64";
|
||||
static const char kGpuUnknown[] = "UNKNOWN";
|
||||
|
||||
template<typename T>
|
||||
T HexStrToL(const string& str) {
|
||||
uint64_t res = 0;
|
||||
std::istringstream ss(str);
|
||||
ss >> std::hex >> res;
|
||||
return static_cast<T>(res);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> ParseHexBuf(const string& str) {
|
||||
std::vector<uint8_t> buf;
|
||||
for (size_t i = 0; i < str.length(); i += 2) {
|
||||
buf.push_back(HexStrToL<uint8_t>(str.substr(i, 2)));
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool GetLine(std::istringstream* istream, string* str) {
|
||||
if (std::getline(*istream, *str)) {
|
||||
// Trim any trailing newline from the end of the line. Allows us
|
||||
// to seamlessly handle both Windows/DOS and Unix formatted input. The
|
||||
// adb tool generally writes logcat dumps in Windows/DOS format.
|
||||
if (!str->empty() && str->at(str->size() - 1) == '\r') {
|
||||
str->erase(str->size() - 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
//
|
||||
// MicrodumpModules
|
||||
//
|
||||
|
||||
void MicrodumpModules::Add(const CodeModule* module) {
|
||||
linked_ptr<const CodeModule> module_ptr(module);
|
||||
if (!map_.StoreRange(module->base_address(), module->size(), module_ptr)) {
|
||||
BPLOG(ERROR) << "Module " << module->code_file() <<
|
||||
" could not be stored";
|
||||
}
|
||||
}
|
||||
|
||||
void MicrodumpModules::SetEnableModuleShrink(bool is_enabled) {
|
||||
map_.SetMergeStrategy(is_enabled ? MergeRangeStrategy::kTruncateUpper
|
||||
: MergeRangeStrategy::kExclusiveRanges);
|
||||
}
|
||||
|
||||
//
|
||||
// MicrodumpContext
|
||||
//
|
||||
|
||||
void MicrodumpContext::SetContextARM(MDRawContextARM* arm) {
|
||||
DumpContext::SetContextFlags(MD_CONTEXT_ARM);
|
||||
DumpContext::SetContextARM(arm);
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
void MicrodumpContext::SetContextARM64(MDRawContextARM64* arm64) {
|
||||
DumpContext::SetContextFlags(MD_CONTEXT_ARM64);
|
||||
DumpContext::SetContextARM64(arm64);
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
void MicrodumpContext::SetContextX86(MDRawContextX86* x86) {
|
||||
DumpContext::SetContextFlags(MD_CONTEXT_X86);
|
||||
DumpContext::SetContextX86(x86);
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
void MicrodumpContext::SetContextMIPS(MDRawContextMIPS* mips32) {
|
||||
DumpContext::SetContextFlags(MD_CONTEXT_MIPS);
|
||||
DumpContext::SetContextMIPS(mips32);
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
void MicrodumpContext::SetContextMIPS64(MDRawContextMIPS* mips64) {
|
||||
DumpContext::SetContextFlags(MD_CONTEXT_MIPS64);
|
||||
DumpContext::SetContextMIPS(mips64);
|
||||
valid_ = true;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// MicrodumpMemoryRegion
|
||||
//
|
||||
|
||||
MicrodumpMemoryRegion::MicrodumpMemoryRegion() : base_address_(0) { }
|
||||
|
||||
void MicrodumpMemoryRegion::Init(uint64_t base_address,
|
||||
const std::vector<uint8_t>& contents) {
|
||||
base_address_ = base_address;
|
||||
contents_ = contents;
|
||||
}
|
||||
|
||||
uint64_t MicrodumpMemoryRegion::GetBase() const { return base_address_; }
|
||||
|
||||
uint32_t MicrodumpMemoryRegion::GetSize() const { return contents_.size(); }
|
||||
|
||||
bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint8_t* value) const {
|
||||
return GetMemoryLittleEndian(address, value);
|
||||
}
|
||||
|
||||
bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint16_t* value) const {
|
||||
return GetMemoryLittleEndian(address, value);
|
||||
}
|
||||
|
||||
bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint32_t* value) const {
|
||||
return GetMemoryLittleEndian(address, value);
|
||||
}
|
||||
|
||||
bool MicrodumpMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint64_t* value) const {
|
||||
return GetMemoryLittleEndian(address, value);
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
bool MicrodumpMemoryRegion::GetMemoryLittleEndian(uint64_t address,
|
||||
ValueType* value) const {
|
||||
if (address < base_address_ ||
|
||||
address - base_address_ + sizeof(ValueType) > contents_.size())
|
||||
return false;
|
||||
ValueType v = 0;
|
||||
uint64_t 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<uint8_t>(contents_[start + i]);
|
||||
*value = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MicrodumpMemoryRegion::Print() const {
|
||||
// Not reached, just needed to honor the base class contract.
|
||||
assert(false);
|
||||
}
|
||||
|
||||
//
|
||||
// Microdump
|
||||
//
|
||||
Microdump::Microdump(const string& contents)
|
||||
: context_(new MicrodumpContext()),
|
||||
stack_region_(new MicrodumpMemoryRegion()),
|
||||
modules_(new MicrodumpModules()),
|
||||
system_info_(new SystemInfo()),
|
||||
crash_reason_(),
|
||||
crash_address_(0u) {
|
||||
assert(!contents.empty());
|
||||
|
||||
bool in_microdump = false;
|
||||
string line;
|
||||
uint64_t stack_start = 0;
|
||||
std::vector<uint8_t> stack_content;
|
||||
string arch;
|
||||
|
||||
std::istringstream stream(contents);
|
||||
while (GetLine(&stream, &line)) {
|
||||
if (line.find(kGoogleBreakpadKey) == string::npos) {
|
||||
continue;
|
||||
}
|
||||
if (line.find(kMicrodumpBegin) != string::npos) {
|
||||
in_microdump = true;
|
||||
continue;
|
||||
}
|
||||
if (!in_microdump) {
|
||||
continue;
|
||||
}
|
||||
if (line.find(kMicrodumpEnd) != string::npos) {
|
||||
break;
|
||||
}
|
||||
|
||||
size_t pos;
|
||||
if ((pos = line.find(kOsKey)) != string::npos) {
|
||||
string os_str(line, pos + strlen(kOsKey));
|
||||
std::istringstream os_tokens(os_str);
|
||||
string os_id;
|
||||
string num_cpus;
|
||||
string os_version;
|
||||
// This reflect the actual HW arch and might not match the arch emulated
|
||||
// for the execution (e.g., running a 32-bit binary on a 64-bit cpu).
|
||||
string hw_arch;
|
||||
|
||||
os_tokens >> os_id;
|
||||
os_tokens >> arch;
|
||||
os_tokens >> num_cpus;
|
||||
os_tokens >> hw_arch;
|
||||
GetLine(&os_tokens, &os_version);
|
||||
os_version.erase(0, 1); // remove leading space.
|
||||
|
||||
system_info_->cpu = arch;
|
||||
system_info_->cpu_count = HexStrToL<uint8_t>(num_cpus);
|
||||
system_info_->os_version = os_version;
|
||||
|
||||
if (os_id == "L") {
|
||||
system_info_->os = "Linux";
|
||||
system_info_->os_short = "linux";
|
||||
} else if (os_id == "A") {
|
||||
system_info_->os = "Android";
|
||||
system_info_->os_short = "android";
|
||||
modules_->SetEnableModuleShrink(true);
|
||||
}
|
||||
|
||||
// OS line also contains release and version for future use.
|
||||
} else if ((pos = line.find(kStackKey)) != string::npos) {
|
||||
if (line.find(kStackFirstLineKey) != string::npos) {
|
||||
// The first line of the stack (S 0 stack header) provides the value of
|
||||
// the stack pointer, the start address of the stack being dumped and
|
||||
// the length of the stack. We could use it in future to double check
|
||||
// that we received all the stack as expected.
|
||||
continue;
|
||||
}
|
||||
string stack_str(line, pos + strlen(kStackKey));
|
||||
std::istringstream stack_tokens(stack_str);
|
||||
string start_addr_str;
|
||||
string raw_content;
|
||||
stack_tokens >> start_addr_str;
|
||||
stack_tokens >> raw_content;
|
||||
uint64_t start_addr = HexStrToL<uint64_t>(start_addr_str);
|
||||
|
||||
if (stack_start != 0) {
|
||||
// Verify that the stack chunks in the microdump are contiguous.
|
||||
assert(start_addr == stack_start + stack_content.size());
|
||||
} else {
|
||||
stack_start = start_addr;
|
||||
}
|
||||
std::vector<uint8_t> chunk = ParseHexBuf(raw_content);
|
||||
stack_content.insert(stack_content.end(), chunk.begin(), chunk.end());
|
||||
|
||||
} else if ((pos = line.find(kCpuKey)) != string::npos) {
|
||||
string cpu_state_str(line, pos + strlen(kCpuKey));
|
||||
std::vector<uint8_t> cpu_state_raw = ParseHexBuf(cpu_state_str);
|
||||
if (strcmp(arch.c_str(), kArmArchitecture) == 0) {
|
||||
if (cpu_state_raw.size() != sizeof(MDRawContextARM)) {
|
||||
std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
|
||||
<< " bytes instead of " << sizeof(MDRawContextARM)
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
MDRawContextARM* arm = new MDRawContextARM();
|
||||
memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size());
|
||||
context_->SetContextARM(arm);
|
||||
} else if (strcmp(arch.c_str(), kArm64Architecture) == 0) {
|
||||
if (cpu_state_raw.size() == sizeof(MDRawContextARM64)) {
|
||||
MDRawContextARM64* arm = new MDRawContextARM64();
|
||||
memcpy(arm, &cpu_state_raw[0], cpu_state_raw.size());
|
||||
context_->SetContextARM64(arm);
|
||||
} else if (cpu_state_raw.size() == sizeof(MDRawContextARM64_Old)) {
|
||||
MDRawContextARM64_Old old_arm;
|
||||
memcpy(&old_arm, &cpu_state_raw[0], cpu_state_raw.size());
|
||||
MDRawContextARM64* new_arm = new MDRawContextARM64();
|
||||
ConvertOldARM64Context(old_arm, new_arm);
|
||||
context_->SetContextARM64(new_arm);
|
||||
} else {
|
||||
std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
|
||||
<< " bytes instead of " << sizeof(MDRawContextARM64)
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
} else if (strcmp(arch.c_str(), kX86Architecture) == 0) {
|
||||
if (cpu_state_raw.size() != sizeof(MDRawContextX86)) {
|
||||
std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
|
||||
<< " bytes instead of " << sizeof(MDRawContextX86)
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
MDRawContextX86* x86 = new MDRawContextX86();
|
||||
memcpy(x86, &cpu_state_raw[0], cpu_state_raw.size());
|
||||
context_->SetContextX86(x86);
|
||||
} else if (strcmp(arch.c_str(), kMipsArchitecture) == 0) {
|
||||
if (cpu_state_raw.size() != sizeof(MDRawContextMIPS)) {
|
||||
std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
|
||||
<< " bytes instead of " << sizeof(MDRawContextMIPS)
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
MDRawContextMIPS* mips32 = new MDRawContextMIPS();
|
||||
memcpy(mips32, &cpu_state_raw[0], cpu_state_raw.size());
|
||||
context_->SetContextMIPS(mips32);
|
||||
} else if (strcmp(arch.c_str(), kMips64Architecture) == 0) {
|
||||
if (cpu_state_raw.size() != sizeof(MDRawContextMIPS)) {
|
||||
std::cerr << "Malformed CPU context. Got " << cpu_state_raw.size()
|
||||
<< " bytes instead of " << sizeof(MDRawContextMIPS)
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
MDRawContextMIPS* mips64 = new MDRawContextMIPS();
|
||||
memcpy(mips64, &cpu_state_raw[0], cpu_state_raw.size());
|
||||
context_->SetContextMIPS64(mips64);
|
||||
} else {
|
||||
std::cerr << "Unsupported architecture: " << arch << std::endl;
|
||||
}
|
||||
} else if ((pos = line.find(kCrashReasonKey)) != string::npos) {
|
||||
string crash_reason_str(line, pos + strlen(kCrashReasonKey));
|
||||
std::istringstream crash_reason_tokens(crash_reason_str);
|
||||
string signal;
|
||||
string address;
|
||||
crash_reason_tokens >> signal;
|
||||
crash_reason_tokens >> crash_reason_;
|
||||
crash_reason_tokens >> address;
|
||||
crash_address_ = HexStrToL<uint64_t>(address);
|
||||
} else if ((pos = line.find(kGpuKey)) != string::npos) {
|
||||
string gpu_str(line, pos + strlen(kGpuKey));
|
||||
if (strcmp(gpu_str.c_str(), kGpuUnknown) != 0) {
|
||||
std::istringstream gpu_tokens(gpu_str);
|
||||
std::getline(gpu_tokens, system_info_->gl_version, '|');
|
||||
std::getline(gpu_tokens, system_info_->gl_vendor, '|');
|
||||
std::getline(gpu_tokens, system_info_->gl_renderer, '|');
|
||||
}
|
||||
} else if ((pos = line.find(kMmapKey)) != string::npos) {
|
||||
string mmap_line(line, pos + strlen(kMmapKey));
|
||||
std::istringstream mmap_tokens(mmap_line);
|
||||
string addr, offset, size, identifier, filename;
|
||||
mmap_tokens >> addr;
|
||||
mmap_tokens >> offset;
|
||||
mmap_tokens >> size;
|
||||
mmap_tokens >> identifier;
|
||||
mmap_tokens >> filename;
|
||||
|
||||
modules_->Add(new BasicCodeModule(
|
||||
HexStrToL<uint64_t>(addr), // base_address
|
||||
HexStrToL<uint64_t>(size), // size
|
||||
filename, // code_file
|
||||
identifier, // code_identifier
|
||||
filename, // debug_file
|
||||
identifier, // debug_identifier
|
||||
"")); // version
|
||||
}
|
||||
}
|
||||
stack_region_->Init(stack_start, stack_content);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
100
externals/breakpad/src/processor/microdump_processor.cc
vendored
Normal file
100
externals/breakpad/src/processor/microdump_processor.cc
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2014 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
// microdump_processor.cc: A microdump processor.
|
||||
//
|
||||
// See microdump_processor.h for documentation.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "google_breakpad/processor/microdump_processor.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/microdump.h"
|
||||
#include "google_breakpad/processor/process_state.h"
|
||||
#include "google_breakpad/processor/stackwalker.h"
|
||||
#include "google_breakpad/processor/stack_frame_symbolizer.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
MicrodumpProcessor::MicrodumpProcessor(StackFrameSymbolizer* frame_symbolizer)
|
||||
: frame_symbolizer_(frame_symbolizer) {
|
||||
assert(frame_symbolizer);
|
||||
}
|
||||
|
||||
MicrodumpProcessor::~MicrodumpProcessor() {}
|
||||
|
||||
ProcessResult MicrodumpProcessor::Process(Microdump *microdump,
|
||||
ProcessState* process_state) {
|
||||
assert(process_state);
|
||||
|
||||
process_state->Clear();
|
||||
|
||||
process_state->modules_ = microdump->GetModules()->Copy();
|
||||
scoped_ptr<Stackwalker> stackwalker(
|
||||
Stackwalker::StackwalkerForCPU(
|
||||
&process_state->system_info_,
|
||||
microdump->GetContext(),
|
||||
microdump->GetMemory(),
|
||||
process_state->modules_,
|
||||
/* unloaded_modules= */ NULL,
|
||||
frame_symbolizer_));
|
||||
|
||||
scoped_ptr<CallStack> stack(new CallStack());
|
||||
if (stackwalker.get()) {
|
||||
if (!stackwalker->Walk(stack.get(),
|
||||
&process_state->modules_without_symbols_,
|
||||
&process_state->modules_with_corrupt_symbols_)) {
|
||||
BPLOG(INFO) << "Processing was interrupted.";
|
||||
return PROCESS_SYMBOL_SUPPLIER_INTERRUPTED;
|
||||
}
|
||||
} else {
|
||||
BPLOG(ERROR) << "No stackwalker found for microdump.";
|
||||
return PROCESS_ERROR_NO_THREAD_LIST;
|
||||
}
|
||||
|
||||
process_state->threads_.push_back(stack.release());
|
||||
process_state->thread_memory_regions_.push_back(microdump->GetMemory());
|
||||
process_state->crashed_ = true;
|
||||
process_state->requesting_thread_ = 0;
|
||||
process_state->system_info_ = *microdump->GetSystemInfo();
|
||||
process_state->crash_reason_ = microdump->GetCrashReason();
|
||||
process_state->crash_address_ = microdump->GetCrashAddress();
|
||||
|
||||
return PROCESS_OK;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
288
externals/breakpad/src/processor/microdump_processor_unittest.cc
vendored
Normal file
288
externals/breakpad/src/processor/microdump_processor_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
// Copyright 2014 Google LLC
|
||||
//
|
||||
// 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 LLC 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 MicrodumpProcessor.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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/microdump.h"
|
||||
#include "google_breakpad/processor/microdump_processor.h"
|
||||
#include "google_breakpad/processor/process_state.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "google_breakpad/processor/stack_frame_symbolizer.h"
|
||||
#include "processor/simple_symbol_supplier.h"
|
||||
#include "processor/stackwalker_unittest_utils.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::Microdump;
|
||||
using google_breakpad::MicrodumpProcessor;
|
||||
using google_breakpad::ProcessState;
|
||||
using google_breakpad::SimpleSymbolSupplier;
|
||||
using google_breakpad::StackFrameSymbolizer;
|
||||
|
||||
class MicrodumpProcessorTest : public ::testing::Test {
|
||||
public:
|
||||
MicrodumpProcessorTest()
|
||||
: files_path_(string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
||||
"/src/processor/testdata/") {
|
||||
}
|
||||
|
||||
void ReadFile(const string& file_name, string* file_contents) {
|
||||
assert(file_contents);
|
||||
std::ifstream file_stream(file_name.c_str(), std::ios::in);
|
||||
ASSERT_TRUE(file_stream.good());
|
||||
std::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());
|
||||
*file_contents = string(&bytes[0], bytes.size());
|
||||
}
|
||||
|
||||
google_breakpad::ProcessResult ProcessMicrodump(
|
||||
const string& symbols_file,
|
||||
const string& microdump_contents,
|
||||
ProcessState* state) {
|
||||
SimpleSymbolSupplier supplier(symbols_file);
|
||||
BasicSourceLineResolver resolver;
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
MicrodumpProcessor processor(&frame_symbolizer);
|
||||
|
||||
Microdump microdump(microdump_contents);
|
||||
return processor.Process(µdump, state);
|
||||
}
|
||||
|
||||
void AnalyzeDump(const string& microdump_file_name, bool omit_symbols,
|
||||
int expected_cpu_count, ProcessState* state) {
|
||||
string symbols_file = omit_symbols ? "" : files_path_ + "symbols/microdump";
|
||||
string microdump_file_path = files_path_ + microdump_file_name;
|
||||
string microdump_contents;
|
||||
ReadFile(microdump_file_path, µdump_contents);
|
||||
|
||||
google_breakpad::ProcessResult result =
|
||||
ProcessMicrodump(symbols_file, microdump_contents, state);
|
||||
|
||||
ASSERT_EQ(google_breakpad::PROCESS_OK, result);
|
||||
ASSERT_TRUE(state->crashed());
|
||||
ASSERT_EQ(0, state->requesting_thread());
|
||||
ASSERT_EQ(1U, state->threads()->size());
|
||||
|
||||
ASSERT_EQ(expected_cpu_count, state->system_info()->cpu_count);
|
||||
ASSERT_EQ("android", state->system_info()->os_short);
|
||||
ASSERT_EQ("Android", state->system_info()->os);
|
||||
}
|
||||
|
||||
string files_path_;
|
||||
};
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcess_Invalid) {
|
||||
ProcessState state;
|
||||
google_breakpad::ProcessResult result =
|
||||
ProcessMicrodump("", "This is not a valid microdump", &state);
|
||||
ASSERT_EQ(google_breakpad::PROCESS_ERROR_NO_THREAD_LIST, result);
|
||||
}
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcess_WithoutCrashReason) {
|
||||
ProcessState state;
|
||||
AnalyzeDump("microdump-arm64.dmp", true /* omit_symbols */,
|
||||
2 /* expected_cpu_count */, &state);
|
||||
ASSERT_EQ(state.crash_reason(), "");
|
||||
ASSERT_EQ(state.crash_address(), 0x0u);
|
||||
}
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcess_WithCrashReason) {
|
||||
ProcessState state;
|
||||
AnalyzeDump("microdump-withcrashreason.dmp", true /* omit_symbols */,
|
||||
8 /* expected_cpu_count */, &state);
|
||||
ASSERT_EQ(state.crash_reason(), "SIGTRAP");
|
||||
ASSERT_EQ(state.crash_address(), 0x4A7CB000u);
|
||||
}
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcess_MissingSymbols) {
|
||||
ProcessState state;
|
||||
AnalyzeDump("microdump-arm64.dmp", true /* omit_symbols */,
|
||||
2 /* expected_cpu_count */, &state);
|
||||
|
||||
ASSERT_EQ(8U, state.modules()->module_count());
|
||||
ASSERT_EQ("arm64", state.system_info()->cpu);
|
||||
ASSERT_EQ("OS 64 VERSION INFO", state.system_info()->os_version);
|
||||
ASSERT_EQ(1U, state.threads()->size());
|
||||
ASSERT_EQ(11U, state.threads()->at(0)->frames()->size());
|
||||
|
||||
ASSERT_EQ("",
|
||||
state.threads()->at(0)->frames()->at(0)->function_name);
|
||||
ASSERT_EQ("",
|
||||
state.threads()->at(0)->frames()->at(3)->function_name);
|
||||
}
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcess_UnsupportedArch) {
|
||||
string microdump_contents =
|
||||
"W/google-breakpad(26491): -----BEGIN BREAKPAD MICRODUMP-----\n"
|
||||
"W/google-breakpad(26491): O A \"unsupported-arch\"\n"
|
||||
"W/google-breakpad(26491): S 0 A48BD840 A48BD000 00002000\n";
|
||||
|
||||
ProcessState state;
|
||||
|
||||
google_breakpad::ProcessResult result =
|
||||
ProcessMicrodump("", microdump_contents, &state);
|
||||
|
||||
ASSERT_EQ(google_breakpad::PROCESS_ERROR_NO_THREAD_LIST, result);
|
||||
}
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcessArm) {
|
||||
ProcessState state;
|
||||
AnalyzeDump("microdump-arm.dmp", false /* omit_symbols */,
|
||||
2 /* expected_cpu_count*/, &state);
|
||||
|
||||
ASSERT_EQ(6U, state.modules()->module_count());
|
||||
ASSERT_EQ("arm", state.system_info()->cpu);
|
||||
ASSERT_EQ("OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)",
|
||||
state.system_info()->gl_version);
|
||||
ASSERT_EQ("Qualcomm", state.system_info()->gl_vendor);
|
||||
ASSERT_EQ("Adreno (TM) 330", state.system_info()->gl_renderer);
|
||||
ASSERT_EQ("OS VERSION INFO", state.system_info()->os_version);
|
||||
ASSERT_EQ(8U, state.threads()->at(0)->frames()->size());
|
||||
ASSERT_EQ("MicrodumpWriterTest_Setup_Test::TestBody",
|
||||
state.threads()->at(0)->frames()->at(0)->function_name);
|
||||
ASSERT_EQ("testing::Test::Run",
|
||||
state.threads()->at(0)->frames()->at(1)->function_name);
|
||||
ASSERT_EQ("main",
|
||||
state.threads()->at(0)->frames()->at(6)->function_name);
|
||||
ASSERT_EQ("breakpad_unittests",
|
||||
state.threads()->at(0)->frames()->at(6)->module->code_file());
|
||||
}
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcessArm64) {
|
||||
ProcessState state;
|
||||
AnalyzeDump("microdump-arm64.dmp", false /* omit_symbols */,
|
||||
2 /* expected_cpu_count*/, &state);
|
||||
|
||||
ASSERT_EQ(8U, state.modules()->module_count());
|
||||
ASSERT_EQ("arm64", state.system_info()->cpu);
|
||||
ASSERT_EQ("OS 64 VERSION INFO", state.system_info()->os_version);
|
||||
ASSERT_EQ(9U, state.threads()->at(0)->frames()->size());
|
||||
ASSERT_EQ("MicrodumpWriterTest_Setup_Test::TestBody",
|
||||
state.threads()->at(0)->frames()->at(0)->function_name);
|
||||
ASSERT_EQ("testing::Test::Run",
|
||||
state.threads()->at(0)->frames()->at(2)->function_name);
|
||||
ASSERT_EQ("main",
|
||||
state.threads()->at(0)->frames()->at(7)->function_name);
|
||||
ASSERT_EQ("breakpad_unittests",
|
||||
state.threads()->at(0)->frames()->at(7)->module->code_file());
|
||||
}
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcessX86) {
|
||||
ProcessState state;
|
||||
AnalyzeDump("microdump-x86.dmp", false /* omit_symbols */,
|
||||
4 /* expected_cpu_count */, &state);
|
||||
|
||||
ASSERT_EQ(124U, state.modules()->module_count());
|
||||
ASSERT_EQ("x86", state.system_info()->cpu);
|
||||
ASSERT_EQ("asus/WW_Z00A/Z00A:5.0/LRX21V/2.19.40.22_20150627_5104_user:user/"
|
||||
"release-keys", state.system_info()->os_version);
|
||||
ASSERT_EQ(17U, state.threads()->at(0)->frames()->size());
|
||||
ASSERT_EQ("libc.so",
|
||||
state.threads()->at(0)->frames()->at(0)->module->debug_file());
|
||||
// TODO(mmandlis): Get symbols for the test X86 microdump and test function
|
||||
// names.
|
||||
}
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcessMultiple) {
|
||||
ProcessState state;
|
||||
AnalyzeDump("microdump-multiple.dmp", false /* omit_symbols */,
|
||||
6 /* expected_cpu_count */, &state);
|
||||
ASSERT_EQ(156U, state.modules()->module_count());
|
||||
ASSERT_EQ("arm", state.system_info()->cpu);
|
||||
ASSERT_EQ("lge/p1_tmo_us/p1:6.0/MRA58K/1603210524c8d:user/release-keys",
|
||||
state.system_info()->os_version);
|
||||
ASSERT_EQ(5U, state.threads()->at(0)->frames()->size());
|
||||
}
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcessMips) {
|
||||
ProcessState state;
|
||||
AnalyzeDump("microdump-mips32.dmp", false /* omit_symbols */,
|
||||
2 /* expected_cpu_count */, &state);
|
||||
|
||||
ASSERT_EQ(7U, state.modules()->module_count());
|
||||
ASSERT_EQ("mips", state.system_info()->cpu);
|
||||
ASSERT_EQ("3.0.8-g893bf16 #7 SMP PREEMPT Fri Jul 10 15:20:59 PDT 2015",
|
||||
state.system_info()->os_version);
|
||||
ASSERT_EQ(4U, state.threads()->at(0)->frames()->size());
|
||||
|
||||
ASSERT_EQ("blaTest",
|
||||
state.threads()->at(0)->frames()->at(0)->function_name);
|
||||
ASSERT_EQ("Crash",
|
||||
state.threads()->at(0)->frames()->at(1)->function_name);
|
||||
ASSERT_EQ("main",
|
||||
state.threads()->at(0)->frames()->at(2)->function_name);
|
||||
ASSERT_EQ("crash_example",
|
||||
state.threads()->at(0)->frames()->at(0)->module->debug_file());
|
||||
}
|
||||
|
||||
TEST_F(MicrodumpProcessorTest, TestProcessMips64) {
|
||||
ProcessState state;
|
||||
AnalyzeDump("microdump-mips64.dmp", false /* omit_symbols */,
|
||||
1 /* expected_cpu_count */, &state);
|
||||
|
||||
ASSERT_EQ(8U, state.modules()->module_count());
|
||||
ASSERT_EQ("mips64", state.system_info()->cpu);
|
||||
ASSERT_EQ("3.10.0-gf185e20 #112 PREEMPT Mon Oct 5 11:12:49 PDT 2015",
|
||||
state.system_info()->os_version);
|
||||
ASSERT_EQ(4U, state.threads()->at(0)->frames()->size());
|
||||
|
||||
ASSERT_EQ("blaTest",
|
||||
state.threads()->at(0)->frames()->at(0)->function_name);
|
||||
ASSERT_EQ("Crash",
|
||||
state.threads()->at(0)->frames()->at(1)->function_name);
|
||||
ASSERT_EQ("main",
|
||||
state.threads()->at(0)->frames()->at(2)->function_name);
|
||||
ASSERT_EQ("crash_example",
|
||||
state.threads()->at(0)->frames()->at(0)->module->debug_file());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
187
externals/breakpad/src/processor/microdump_stackwalk.cc
vendored
Normal file
187
externals/breakpad/src/processor/microdump_stackwalk.cc
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// Copyright 2014 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
// microdump_stackwalk.cc: Process a microdump with MicrodumpProcessor, printing
|
||||
// the results, including stack traces.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/path_helper.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/microdump.h"
|
||||
#include "google_breakpad/processor/microdump_processor.h"
|
||||
#include "google_breakpad/processor/process_state.h"
|
||||
#include "google_breakpad/processor/stack_frame_symbolizer.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/simple_symbol_supplier.h"
|
||||
#include "processor/stackwalk_common.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct Options {
|
||||
bool machine_readable;
|
||||
bool output_stack_contents;
|
||||
|
||||
string microdump_file;
|
||||
std::vector<string> symbol_paths;
|
||||
};
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::Microdump;
|
||||
using google_breakpad::MicrodumpProcessor;
|
||||
using google_breakpad::ProcessResult;
|
||||
using google_breakpad::ProcessState;
|
||||
using google_breakpad::scoped_ptr;
|
||||
using google_breakpad::SimpleSymbolSupplier;
|
||||
using google_breakpad::StackFrameSymbolizer;
|
||||
|
||||
// Processes |options.microdump_file| using
|
||||
// MicrodumpProcessor. |options.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 MicrodumpProcessor.
|
||||
//
|
||||
// Returns the value of MicrodumpProcessor::Process. If processing succeeds,
|
||||
// prints identifying OS and CPU information from the microdump, crash
|
||||
// information and call stacks for the crashing thread.
|
||||
// All information is printed to stdout.
|
||||
int PrintMicrodumpProcess(const Options& options) {
|
||||
std::ifstream file_stream(options.microdump_file);
|
||||
std::vector<char> bytes;
|
||||
file_stream.seekg(0, std::ios_base::end);
|
||||
bytes.resize(file_stream.tellg());
|
||||
if (bytes.empty()) {
|
||||
BPLOG(ERROR) << "Microdump is empty.";
|
||||
return 1;
|
||||
}
|
||||
file_stream.seekg(0, std::ios_base::beg);
|
||||
file_stream.read(&bytes[0], bytes.size());
|
||||
string microdump_content(&bytes[0], bytes.size());
|
||||
|
||||
scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
|
||||
if (!options.symbol_paths.empty()) {
|
||||
symbol_supplier.reset(new SimpleSymbolSupplier(options.symbol_paths));
|
||||
}
|
||||
|
||||
BasicSourceLineResolver resolver;
|
||||
StackFrameSymbolizer frame_symbolizer(symbol_supplier.get(), &resolver);
|
||||
ProcessState process_state;
|
||||
MicrodumpProcessor microdump_processor(&frame_symbolizer);
|
||||
Microdump microdump(microdump_content);
|
||||
ProcessResult res = microdump_processor.Process(µdump,
|
||||
&process_state);
|
||||
|
||||
if (res == google_breakpad::PROCESS_OK) {
|
||||
if (options.machine_readable) {
|
||||
PrintProcessStateMachineReadable(process_state);
|
||||
} else {
|
||||
// Microdump has only one thread, |output_requesting_thread_only|'s value
|
||||
// has no effect.
|
||||
PrintProcessState(process_state, options.output_stack_contents,
|
||||
/*output_requesting_thread_only=*/false, &resolver);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BPLOG(ERROR) << "MicrodumpProcessor::Process failed (code = " << res << ")";
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static void Usage(int argc, const char *argv[], bool error) {
|
||||
fprintf(error ? stderr : stdout,
|
||||
"Usage: %s [options] <microdump-file> [symbol-path ...]\n"
|
||||
"\n"
|
||||
"Output a stack trace for the provided microdump\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
"\n"
|
||||
" -m Output in machine-readable format\n"
|
||||
" -s Output stack contents\n",
|
||||
google_breakpad::BaseName(argv[0]).c_str());
|
||||
}
|
||||
|
||||
static void SetupOptions(int argc, const char *argv[], Options* options) {
|
||||
int ch;
|
||||
|
||||
options->machine_readable = false;
|
||||
options->output_stack_contents = false;
|
||||
|
||||
while ((ch = getopt(argc, (char * const*)argv, "hms")) != -1) {
|
||||
switch (ch) {
|
||||
case 'h':
|
||||
Usage(argc, argv, false);
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
options->machine_readable = true;
|
||||
break;
|
||||
case 's':
|
||||
options->output_stack_contents = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
Usage(argc, argv, true);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) == 0) {
|
||||
fprintf(stderr, "%s: Missing microdump file\n", argv[0]);
|
||||
Usage(argc, argv, true);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
options->microdump_file = argv[optind];
|
||||
|
||||
for (int argi = optind + 1; argi < argc; ++argi)
|
||||
options->symbol_paths.push_back(argv[argi]);
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
Options options;
|
||||
SetupOptions(argc, argv, &options);
|
||||
|
||||
return PrintMicrodumpProcess(options);
|
||||
}
|
||||
42
externals/breakpad/src/processor/microdump_stackwalk_machine_readable_test
vendored
Executable file
42
externals/breakpad/src/processor/microdump_stackwalk_machine_readable_test
vendored
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright 2014 Google LLC
|
||||
#
|
||||
# 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 LLC 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.
|
||||
|
||||
. "${0%/*}/microdump_stackwalk_test_vars" || exit 1 # for MICRODUMP_SUPPORTED_ARCHS.
|
||||
testdata_dir=$srcdir/src/processor/testdata
|
||||
|
||||
set -e # Bail out with an error if any of the commands below fails.
|
||||
for ARCH in $MICRODUMP_SUPPORTED_ARCHS; do
|
||||
echo "Testing microdump_stackwalk -m for arch $ARCH"
|
||||
./src/processor/microdump_stackwalk -m $testdata_dir/microdump-${ARCH}.dmp \
|
||||
$testdata_dir/symbols/microdump | \
|
||||
tr -d '\015' | \
|
||||
diff -u $testdata_dir/microdump.stackwalk.machine_readable-${ARCH}.out -
|
||||
done
|
||||
exit 0
|
||||
42
externals/breakpad/src/processor/microdump_stackwalk_test
vendored
Executable file
42
externals/breakpad/src/processor/microdump_stackwalk_test
vendored
Executable file
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright 2014 Google LLC
|
||||
#
|
||||
# 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 LLC 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.
|
||||
|
||||
. "${0%/*}/microdump_stackwalk_test_vars" || exit 1 # for MICRODUMP_SUPPORTED_ARCHS.
|
||||
testdata_dir=$srcdir/src/processor/testdata
|
||||
|
||||
set -e # Bail out with an error if any of the commands below fails.
|
||||
for ARCH in $MICRODUMP_SUPPORTED_ARCHS; do
|
||||
echo "Testing microdump_stackwalk for arch $ARCH"
|
||||
./src/processor/microdump_stackwalk $testdata_dir/microdump-${ARCH}.dmp \
|
||||
$testdata_dir/symbols/microdump | \
|
||||
tr -d '\015' | \
|
||||
diff -u $testdata_dir/microdump.stackwalk-${ARCH}.out -
|
||||
done
|
||||
exit 0
|
||||
1
externals/breakpad/src/processor/microdump_stackwalk_test_vars
vendored
Normal file
1
externals/breakpad/src/processor/microdump_stackwalk_test_vars
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
MICRODUMP_SUPPORTED_ARCHS="arm arm64"
|
||||
6480
externals/breakpad/src/processor/minidump.cc
vendored
Normal file
6480
externals/breakpad/src/processor/minidump.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
285
externals/breakpad/src/processor/minidump_dump.cc
vendored
Normal file
285
externals/breakpad/src/processor/minidump_dump.cc
vendored
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "common/path_helper.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::Minidump;
|
||||
using google_breakpad::MinidumpThreadList;
|
||||
using google_breakpad::MinidumpThreadNameList;
|
||||
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;
|
||||
using google_breakpad::MinidumpCrashpadInfo;
|
||||
|
||||
struct Options {
|
||||
Options()
|
||||
: minidumpPath(), hexdump(false), hexdump_width(16) {}
|
||||
|
||||
string minidumpPath;
|
||||
bool hexdump;
|
||||
unsigned int hexdump_width;
|
||||
};
|
||||
|
||||
static void DumpRawStream(Minidump *minidump,
|
||||
uint32_t stream_type,
|
||||
const char *stream_name,
|
||||
int *errors) {
|
||||
uint32_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.
|
||||
uint32_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 Options& options) {
|
||||
Minidump minidump(options.minidumpPath,
|
||||
options.hexdump);
|
||||
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();
|
||||
}
|
||||
|
||||
MinidumpThreadNameList *thread_name_list = minidump.GetThreadNameList();
|
||||
if (thread_name_list) {
|
||||
thread_name_list->Print();
|
||||
}
|
||||
|
||||
// It's useful to be able to see the full list of modules here even if it
|
||||
// would cause minidump_stackwalk to fail.
|
||||
MinidumpModuleList::set_max_modules(UINT32_MAX);
|
||||
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();
|
||||
}
|
||||
|
||||
MinidumpCrashpadInfo *crashpad_info = minidump.GetCrashpadInfo();
|
||||
if (crashpad_info) {
|
||||
// Crashpad info is optional, so don't treat absence as an error.
|
||||
crashpad_info->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;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
static void
|
||||
Usage(int argc, char *argv[], bool error) {
|
||||
FILE *fp = error ? stderr : stdout;
|
||||
|
||||
fprintf(fp,
|
||||
"Usage: %s [options...] <minidump>\n"
|
||||
"Dump data in a minidump.\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
" <minidump> should be a minidump.\n"
|
||||
" -x:\t Display memory in a hexdump like format\n"
|
||||
" -h:\t Usage\n",
|
||||
google_breakpad::BaseName(argv[0]).c_str());
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
static void
|
||||
SetupOptions(int argc, char *argv[], Options *options) {
|
||||
int ch;
|
||||
|
||||
while ((ch = getopt(argc, (char * const*)argv, "xh")) != -1) {
|
||||
switch (ch) {
|
||||
case 'x':
|
||||
options->hexdump = true;
|
||||
break;
|
||||
case 'h':
|
||||
Usage(argc, argv, false);
|
||||
exit(0);
|
||||
|
||||
default:
|
||||
Usage(argc, argv, true);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) != 1) {
|
||||
fprintf(stderr, "%s: Missing minidump file\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
options->minidumpPath = argv[optind];
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
Options options;
|
||||
BPLOG_INIT(&argc, &argv);
|
||||
SetupOptions(argc, argv, &options);
|
||||
return PrintMinidumpDump(options) ? 0 : 1;
|
||||
}
|
||||
35
externals/breakpad/src/processor/minidump_dump_test
vendored
Executable file
35
externals/breakpad/src/processor/minidump_dump_test
vendored
Executable file
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright 2006 Google LLC
|
||||
#
|
||||
# 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 LLC 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 $?
|
||||
2178
externals/breakpad/src/processor/minidump_processor.cc
vendored
Normal file
2178
externals/breakpad/src/processor/minidump_processor.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
830
externals/breakpad/src/processor/minidump_processor_unittest.cc
vendored
Normal file
830
externals/breakpad/src/processor/minidump_processor_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,830 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/using_std_string.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/stackwalker_unittest_utils.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*());
|
||||
MOCK_METHOD0(GetSystemInfo, MinidumpSystemInfo*());
|
||||
MOCK_METHOD0(GetMiscInfo, MinidumpMiscInfo*());
|
||||
MOCK_METHOD0(GetBreakpadInfo, MinidumpBreakpadInfo*());
|
||||
MOCK_METHOD0(GetException, MinidumpException*());
|
||||
MOCK_METHOD0(GetAssertion, MinidumpAssertion*());
|
||||
MOCK_METHOD0(GetModuleList, MinidumpModuleList*());
|
||||
MOCK_METHOD0(GetUnloadedModuleList, MinidumpUnloadedModuleList*());
|
||||
MOCK_METHOD0(GetMemoryList, MinidumpMemoryList*());
|
||||
};
|
||||
|
||||
class MockMinidumpUnloadedModule : public MinidumpUnloadedModule {
|
||||
public:
|
||||
MockMinidumpUnloadedModule() : MinidumpUnloadedModule(NULL) {}
|
||||
};
|
||||
|
||||
class MockMinidumpUnloadedModuleList : public MinidumpUnloadedModuleList {
|
||||
public:
|
||||
MockMinidumpUnloadedModuleList() : MinidumpUnloadedModuleList(NULL) {}
|
||||
|
||||
~MockMinidumpUnloadedModuleList() {}
|
||||
MOCK_CONST_METHOD0(Copy, CodeModules*());
|
||||
MOCK_CONST_METHOD1(GetModuleForAddress,
|
||||
const MinidumpUnloadedModule*(uint64_t));
|
||||
};
|
||||
|
||||
class MockMinidumpThreadList : public MinidumpThreadList {
|
||||
public:
|
||||
MockMinidumpThreadList() : MinidumpThreadList(NULL) {}
|
||||
|
||||
MOCK_CONST_METHOD0(thread_count, unsigned int());
|
||||
MOCK_CONST_METHOD1(GetThreadAtIndex, MinidumpThread*(unsigned int));
|
||||
};
|
||||
|
||||
class MockMinidumpMemoryList : public MinidumpMemoryList {
|
||||
public:
|
||||
MockMinidumpMemoryList() : MinidumpMemoryList(NULL) {}
|
||||
|
||||
MOCK_METHOD1(GetMemoryRegionForAddress, MinidumpMemoryRegion*(uint64_t));
|
||||
};
|
||||
|
||||
class MockMinidumpThread : public MinidumpThread {
|
||||
public:
|
||||
MockMinidumpThread() : MinidumpThread(NULL) {}
|
||||
|
||||
MOCK_CONST_METHOD1(GetThreadID, bool(uint32_t*));
|
||||
MOCK_METHOD0(GetContext, MinidumpContext*());
|
||||
MOCK_METHOD0(GetMemory, MinidumpMemoryRegion*());
|
||||
MOCK_CONST_METHOD0(GetStartOfStackMemoryRange, uint64_t());
|
||||
};
|
||||
|
||||
// This is crappy, but MinidumpProcessor really does want a
|
||||
// MinidumpMemoryRegion.
|
||||
class MockMinidumpMemoryRegion : public MinidumpMemoryRegion {
|
||||
public:
|
||||
MockMinidumpMemoryRegion(uint64_t base, const string& contents) :
|
||||
MinidumpMemoryRegion(NULL) {
|
||||
region_.Init(base, contents);
|
||||
}
|
||||
|
||||
uint64_t GetBase() const { return region_.GetBase(); }
|
||||
uint32_t GetSize() const { return region_.GetSize(); }
|
||||
|
||||
bool GetMemoryAtAddress(uint64_t address, uint8_t* value) const {
|
||||
return region_.GetMemoryAtAddress(address, value);
|
||||
}
|
||||
bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const {
|
||||
return region_.GetMemoryAtAddress(address, value);
|
||||
}
|
||||
bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const {
|
||||
return region_.GetMemoryAtAddress(address, value);
|
||||
}
|
||||
bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const {
|
||||
return region_.GetMemoryAtAddress(address, value);
|
||||
}
|
||||
|
||||
MockMemoryRegion region_;
|
||||
};
|
||||
|
||||
// A test miscelaneous info stream, just returns values from the
|
||||
// MDRawMiscInfo fed to it.
|
||||
class TestMinidumpMiscInfo : public MinidumpMiscInfo {
|
||||
public:
|
||||
explicit TestMinidumpMiscInfo(const MDRawMiscInfo& misc_info) :
|
||||
MinidumpMiscInfo(NULL) {
|
||||
valid_ = true;
|
||||
misc_info_ = misc_info;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::CallStack;
|
||||
using google_breakpad::CodeModule;
|
||||
using google_breakpad::MinidumpContext;
|
||||
using google_breakpad::MinidumpMemoryRegion;
|
||||
using google_breakpad::MinidumpMiscInfo;
|
||||
using google_breakpad::MinidumpProcessor;
|
||||
using google_breakpad::MinidumpSystemInfo;
|
||||
using google_breakpad::MinidumpThreadList;
|
||||
using google_breakpad::MinidumpThread;
|
||||
using google_breakpad::MockMinidump;
|
||||
using google_breakpad::MockMinidumpMemoryList;
|
||||
using google_breakpad::MockMinidumpMemoryRegion;
|
||||
using google_breakpad::MockMinidumpThread;
|
||||
using google_breakpad::MockMinidumpThreadList;
|
||||
using google_breakpad::MockMinidumpUnloadedModule;
|
||||
using google_breakpad::MockMinidumpUnloadedModuleList;
|
||||
using google_breakpad::ProcessState;
|
||||
using google_breakpad::scoped_ptr;
|
||||
using google_breakpad::SymbolSupplier;
|
||||
using google_breakpad::SystemInfo;
|
||||
using ::testing::_;
|
||||
using ::testing::AnyNumber;
|
||||
using ::testing::DoAll;
|
||||
using ::testing::Mock;
|
||||
using ::testing::Ne;
|
||||
using ::testing::Property;
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgumentPointee;
|
||||
|
||||
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))
|
||||
|
||||
static string GetTestDataPath() {
|
||||
char* srcdir = getenv("srcdir");
|
||||
|
||||
return string(srcdir ? srcdir : ".") + "/src/processor/testdata/";
|
||||
}
|
||||
|
||||
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,
|
||||
size_t* symbol_data_size);
|
||||
|
||||
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 = GetTestDataPath() + "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, string::traits_type::to_char_type(
|
||||
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,
|
||||
size_t* symbol_data_size) {
|
||||
string symbol_data_string;
|
||||
SymbolSupplier::SymbolResult s = GetSymbolFile(module,
|
||||
system_info,
|
||||
symbol_file,
|
||||
&symbol_data_string);
|
||||
if (s == FOUND) {
|
||||
*symbol_data_size = symbol_data_string.size() + 1;
|
||||
*symbol_data = new char[*symbol_data_size];
|
||||
if (*symbol_data == NULL) {
|
||||
BPLOG(ERROR) << "Memory allocation failed for module: "
|
||||
<< module->code_file() << " size: " << *symbol_data_size;
|
||||
return INTERRUPT;
|
||||
}
|
||||
memcpy(*symbol_data, symbol_data_string.c_str(), symbol_data_string.size());
|
||||
(*symbol_data)[symbol_data_string.size()] = '\0';
|
||||
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 test system info stream, just returns values from the
|
||||
// MDRawSystemInfo fed to it.
|
||||
class TestMinidumpSystemInfo : public MinidumpSystemInfo {
|
||||
public:
|
||||
explicit TestMinidumpSystemInfo(MDRawSystemInfo info) :
|
||||
MinidumpSystemInfo(NULL) {
|
||||
valid_ = true;
|
||||
system_info_ = info;
|
||||
csd_version_ = new string("");
|
||||
}
|
||||
};
|
||||
|
||||
// A test minidump context, just returns the MDRawContextX86
|
||||
// fed to it.
|
||||
class TestMinidumpContext : public MinidumpContext {
|
||||
public:
|
||||
explicit TestMinidumpContext(const MDRawContextX86& context) :
|
||||
MinidumpContext(NULL) {
|
||||
valid_ = true;
|
||||
SetContextX86(new MDRawContextX86(context));
|
||||
SetContextFlags(MD_CONTEXT_X86);
|
||||
}
|
||||
};
|
||||
|
||||
class MinidumpProcessorTest : public ::testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(MinidumpProcessorTest, TestUnloadedModules) {
|
||||
MockMinidump dump;
|
||||
|
||||
EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
|
||||
EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true));
|
||||
|
||||
MDRawHeader fake_header;
|
||||
fake_header.time_date_stamp = 0;
|
||||
EXPECT_CALL(dump, header()).WillRepeatedly(Return(&fake_header));
|
||||
|
||||
MDRawSystemInfo raw_system_info;
|
||||
memset(&raw_system_info, 0, sizeof(raw_system_info));
|
||||
raw_system_info.processor_architecture = MD_CPU_ARCHITECTURE_X86;
|
||||
raw_system_info.platform_id = MD_OS_WIN32_NT;
|
||||
TestMinidumpSystemInfo dump_system_info(raw_system_info);
|
||||
|
||||
EXPECT_CALL(dump, GetSystemInfo()).
|
||||
WillRepeatedly(Return(&dump_system_info));
|
||||
|
||||
// No loaded modules
|
||||
|
||||
MockMinidumpUnloadedModuleList unloaded_module_list;
|
||||
EXPECT_CALL(dump, GetUnloadedModuleList()).
|
||||
WillOnce(Return(&unloaded_module_list));
|
||||
|
||||
MockMinidumpMemoryList memory_list;
|
||||
EXPECT_CALL(dump, GetMemoryList()).
|
||||
WillOnce(Return(&memory_list));
|
||||
|
||||
MockMinidumpThreadList thread_list;
|
||||
EXPECT_CALL(dump, GetThreadList()).
|
||||
WillOnce(Return(&thread_list));
|
||||
|
||||
EXPECT_CALL(thread_list, thread_count()).
|
||||
WillRepeatedly(Return(1));
|
||||
|
||||
MockMinidumpThread thread;
|
||||
EXPECT_CALL(thread_list, GetThreadAtIndex(0)).
|
||||
WillOnce(Return(&thread));
|
||||
|
||||
EXPECT_CALL(thread, GetThreadID(_)).
|
||||
WillRepeatedly(DoAll(SetArgumentPointee<0>(1),
|
||||
Return(true)));
|
||||
|
||||
MDRawContextX86 thread_raw_context;
|
||||
memset(&thread_raw_context, 0,
|
||||
sizeof(thread_raw_context));
|
||||
thread_raw_context.context_flags = MD_CONTEXT_X86_FULL;
|
||||
const uint32_t kExpectedEIP = 0xabcd1234;
|
||||
thread_raw_context.eip = kExpectedEIP;
|
||||
TestMinidumpContext thread_context(thread_raw_context);
|
||||
EXPECT_CALL(thread, GetContext()).
|
||||
WillRepeatedly(Return(&thread_context));
|
||||
|
||||
// The memory contents don't really matter here, since it won't be used.
|
||||
MockMinidumpMemoryRegion thread_memory(0x1234, "xxx");
|
||||
EXPECT_CALL(thread, GetMemory()).
|
||||
WillRepeatedly(Return(&thread_memory));
|
||||
EXPECT_CALL(thread, GetStartOfStackMemoryRange()).
|
||||
Times(0);
|
||||
EXPECT_CALL(memory_list, GetMemoryRegionForAddress(_)).
|
||||
Times(0);
|
||||
|
||||
MockMinidumpUnloadedModuleList* unloaded_module_list_copy =
|
||||
new MockMinidumpUnloadedModuleList();
|
||||
EXPECT_CALL(unloaded_module_list, Copy()).
|
||||
WillOnce(Return(unloaded_module_list_copy));
|
||||
|
||||
MockMinidumpUnloadedModule unloaded_module;
|
||||
EXPECT_CALL(*unloaded_module_list_copy, GetModuleForAddress(kExpectedEIP)).
|
||||
WillOnce(Return(&unloaded_module));
|
||||
|
||||
MinidumpProcessor processor(reinterpret_cast<SymbolSupplier*>(NULL), NULL);
|
||||
ProcessState state;
|
||||
EXPECT_EQ(processor.Process(&dump, &state),
|
||||
google_breakpad::PROCESS_OK);
|
||||
|
||||
// The single frame should be populated with the unloaded module.
|
||||
ASSERT_EQ(1U, state.threads()->size());
|
||||
ASSERT_EQ(1U, state.threads()->at(0)->frames()->size());
|
||||
ASSERT_EQ(kExpectedEIP, state.threads()->at(0)->frames()->at(0)->instruction);
|
||||
ASSERT_EQ(&unloaded_module, state.threads()->at(0)->frames()->at(0)->module);
|
||||
}
|
||||
|
||||
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(reinterpret_cast<MDRawHeader*>(NULL))).
|
||||
WillRepeatedly(Return(&fakeHeader));
|
||||
|
||||
EXPECT_EQ(processor.Process(&dump, &state),
|
||||
google_breakpad::PROCESS_ERROR_NO_MINIDUMP_HEADER);
|
||||
|
||||
EXPECT_CALL(dump, GetThreadList()).
|
||||
WillOnce(Return(reinterpret_cast<MinidumpThreadList*>(NULL)));
|
||||
EXPECT_CALL(dump, GetSystemInfo()).
|
||||
WillRepeatedly(Return(reinterpret_cast<MinidumpSystemInfo*>(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 = GetTestDataPath() + "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));
|
||||
// Avoid GMOCK WARNING "Uninteresting mock function call - returning
|
||||
// directly" for FreeSymbolData().
|
||||
EXPECT_CALL(supplier, FreeSymbolData(_)).Times(AnyNumber());
|
||||
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));
|
||||
// Avoid GMOCK WARNING "Uninteresting mock function call - returning
|
||||
// directly" for FreeSymbolData().
|
||||
EXPECT_CALL(supplier, FreeSymbolData(_)).Times(AnyNumber());
|
||||
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 = GetTestDataPath() + "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));
|
||||
EXPECT_EQ((*state.threads())[0]->tid(), 3060U);
|
||||
ASSERT_EQ(state.requesting_thread(), 0);
|
||||
EXPECT_EQ(1171480435U, state.time_date_stamp());
|
||||
EXPECT_EQ(1171480435U, state.process_create_time());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
TEST_F(MinidumpProcessorTest, TestThreadMissingMemory) {
|
||||
MockMinidump dump;
|
||||
EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
|
||||
EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true));
|
||||
|
||||
MDRawHeader fake_header;
|
||||
fake_header.time_date_stamp = 0;
|
||||
EXPECT_CALL(dump, header()).WillRepeatedly(Return(&fake_header));
|
||||
|
||||
MDRawSystemInfo raw_system_info;
|
||||
memset(&raw_system_info, 0, sizeof(raw_system_info));
|
||||
raw_system_info.processor_architecture = MD_CPU_ARCHITECTURE_X86;
|
||||
raw_system_info.platform_id = MD_OS_WIN32_NT;
|
||||
TestMinidumpSystemInfo dump_system_info(raw_system_info);
|
||||
|
||||
EXPECT_CALL(dump, GetSystemInfo()).
|
||||
WillRepeatedly(Return(&dump_system_info));
|
||||
|
||||
MockMinidumpThreadList thread_list;
|
||||
EXPECT_CALL(dump, GetThreadList()).
|
||||
WillOnce(Return(&thread_list));
|
||||
|
||||
MockMinidumpMemoryList memory_list;
|
||||
EXPECT_CALL(dump, GetMemoryList()).
|
||||
WillOnce(Return(&memory_list));
|
||||
|
||||
// Return a thread missing stack memory.
|
||||
MockMinidumpThread no_memory_thread;
|
||||
EXPECT_CALL(no_memory_thread, GetThreadID(_)).
|
||||
WillRepeatedly(DoAll(SetArgumentPointee<0>(1),
|
||||
Return(true)));
|
||||
EXPECT_CALL(no_memory_thread, GetMemory()).
|
||||
WillRepeatedly(Return(reinterpret_cast<MinidumpMemoryRegion*>(NULL)));
|
||||
|
||||
const uint64_t kTestStartOfMemoryRange = 0x1234;
|
||||
EXPECT_CALL(no_memory_thread, GetStartOfStackMemoryRange()).
|
||||
WillRepeatedly(Return(kTestStartOfMemoryRange));
|
||||
EXPECT_CALL(memory_list, GetMemoryRegionForAddress(kTestStartOfMemoryRange)).
|
||||
WillRepeatedly(Return(reinterpret_cast<MinidumpMemoryRegion*>(NULL)));
|
||||
|
||||
MDRawContextX86 no_memory_thread_raw_context;
|
||||
memset(&no_memory_thread_raw_context, 0,
|
||||
sizeof(no_memory_thread_raw_context));
|
||||
no_memory_thread_raw_context.context_flags = MD_CONTEXT_X86_FULL;
|
||||
const uint32_t kExpectedEIP = 0xabcd1234;
|
||||
no_memory_thread_raw_context.eip = kExpectedEIP;
|
||||
TestMinidumpContext no_memory_thread_context(no_memory_thread_raw_context);
|
||||
EXPECT_CALL(no_memory_thread, GetContext()).
|
||||
WillRepeatedly(Return(&no_memory_thread_context));
|
||||
|
||||
EXPECT_CALL(thread_list, thread_count()).
|
||||
WillRepeatedly(Return(1));
|
||||
EXPECT_CALL(thread_list, GetThreadAtIndex(0)).
|
||||
WillOnce(Return(&no_memory_thread));
|
||||
|
||||
MinidumpProcessor processor(reinterpret_cast<SymbolSupplier*>(NULL), NULL);
|
||||
ProcessState state;
|
||||
EXPECT_EQ(processor.Process(&dump, &state),
|
||||
google_breakpad::PROCESS_OK);
|
||||
|
||||
// Should have a single thread with a single frame in it.
|
||||
ASSERT_EQ(1U, state.threads()->size());
|
||||
ASSERT_EQ(1U, state.threads()->at(0)->frames()->size());
|
||||
ASSERT_EQ(kExpectedEIP, state.threads()->at(0)->frames()->at(0)->instruction);
|
||||
}
|
||||
|
||||
TEST_F(MinidumpProcessorTest, GetProcessCreateTime) {
|
||||
const uint32_t kProcessCreateTime = 2000;
|
||||
const uint32_t kTimeDateStamp = 5000;
|
||||
MockMinidump dump;
|
||||
EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
|
||||
EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true));
|
||||
|
||||
// Set time of crash.
|
||||
MDRawHeader fake_header;
|
||||
fake_header.time_date_stamp = kTimeDateStamp;
|
||||
EXPECT_CALL(dump, header()).WillRepeatedly(Return(&fake_header));
|
||||
|
||||
// Set process create time.
|
||||
MDRawMiscInfo raw_misc_info;
|
||||
memset(&raw_misc_info, 0, sizeof(raw_misc_info));
|
||||
raw_misc_info.process_create_time = kProcessCreateTime;
|
||||
raw_misc_info.flags1 |= MD_MISCINFO_FLAGS1_PROCESS_TIMES;
|
||||
google_breakpad::TestMinidumpMiscInfo dump_misc_info(raw_misc_info);
|
||||
EXPECT_CALL(dump, GetMiscInfo()).WillRepeatedly(Return(&dump_misc_info));
|
||||
|
||||
// No threads
|
||||
MockMinidumpThreadList thread_list;
|
||||
EXPECT_CALL(dump, GetThreadList()).WillOnce(Return(&thread_list));
|
||||
EXPECT_CALL(thread_list, thread_count()).WillRepeatedly(Return(0));
|
||||
|
||||
MinidumpProcessor processor(reinterpret_cast<SymbolSupplier*>(NULL), NULL);
|
||||
ProcessState state;
|
||||
EXPECT_EQ(google_breakpad::PROCESS_OK, processor.Process(&dump, &state));
|
||||
|
||||
// Verify the time stamps.
|
||||
ASSERT_EQ(kTimeDateStamp, state.time_date_stamp());
|
||||
ASSERT_EQ(kProcessCreateTime, state.process_create_time());
|
||||
}
|
||||
|
||||
TEST_F(MinidumpProcessorTest, TestThreadMissingContext) {
|
||||
MockMinidump dump;
|
||||
EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
|
||||
EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true));
|
||||
|
||||
MDRawHeader fake_header;
|
||||
fake_header.time_date_stamp = 0;
|
||||
EXPECT_CALL(dump, header()).WillRepeatedly(Return(&fake_header));
|
||||
|
||||
MDRawSystemInfo raw_system_info;
|
||||
memset(&raw_system_info, 0, sizeof(raw_system_info));
|
||||
raw_system_info.processor_architecture = MD_CPU_ARCHITECTURE_X86;
|
||||
raw_system_info.platform_id = MD_OS_WIN32_NT;
|
||||
TestMinidumpSystemInfo dump_system_info(raw_system_info);
|
||||
|
||||
EXPECT_CALL(dump, GetSystemInfo()).
|
||||
WillRepeatedly(Return(&dump_system_info));
|
||||
|
||||
MockMinidumpThreadList thread_list;
|
||||
EXPECT_CALL(dump, GetThreadList()).
|
||||
WillOnce(Return(&thread_list));
|
||||
|
||||
MockMinidumpMemoryList memory_list;
|
||||
EXPECT_CALL(dump, GetMemoryList()).
|
||||
WillOnce(Return(&memory_list));
|
||||
|
||||
// Return a thread missing a thread context.
|
||||
MockMinidumpThread no_context_thread;
|
||||
EXPECT_CALL(no_context_thread, GetThreadID(_)).
|
||||
WillRepeatedly(DoAll(SetArgumentPointee<0>(1),
|
||||
Return(true)));
|
||||
EXPECT_CALL(no_context_thread, GetContext()).
|
||||
WillRepeatedly(Return(reinterpret_cast<MinidumpContext*>(NULL)));
|
||||
|
||||
// The memory contents don't really matter here, since it won't be used.
|
||||
MockMinidumpMemoryRegion no_context_thread_memory(0x1234, "xxx");
|
||||
EXPECT_CALL(no_context_thread, GetMemory()).
|
||||
WillRepeatedly(Return(&no_context_thread_memory));
|
||||
EXPECT_CALL(no_context_thread, GetStartOfStackMemoryRange()).
|
||||
Times(0);
|
||||
EXPECT_CALL(memory_list, GetMemoryRegionForAddress(_)).
|
||||
Times(0);
|
||||
|
||||
EXPECT_CALL(thread_list, thread_count()).
|
||||
WillRepeatedly(Return(1));
|
||||
EXPECT_CALL(thread_list, GetThreadAtIndex(0)).
|
||||
WillOnce(Return(&no_context_thread));
|
||||
|
||||
MinidumpProcessor processor(reinterpret_cast<SymbolSupplier*>(NULL), NULL);
|
||||
ProcessState state;
|
||||
EXPECT_EQ(processor.Process(&dump, &state),
|
||||
google_breakpad::PROCESS_OK);
|
||||
|
||||
// Should have a single thread with zero frames.
|
||||
ASSERT_EQ(1U, state.threads()->size());
|
||||
ASSERT_EQ(0U, state.threads()->at(0)->frames()->size());
|
||||
}
|
||||
|
||||
TEST_F(MinidumpProcessorTest, Test32BitCrashingAddress) {
|
||||
TestSymbolSupplier supplier;
|
||||
BasicSourceLineResolver resolver;
|
||||
MinidumpProcessor processor(&supplier, &resolver);
|
||||
|
||||
string minidump_file = GetTestDataPath() + "minidump_32bit_crash_addr.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);
|
||||
}
|
||||
|
||||
TEST_F(MinidumpProcessorTest, TestXStateAmd64ContextMinidump) {
|
||||
// This tests if we can passively process a minidump with cet registers in its
|
||||
// context. Dump is captured from a toy executable and is readable by windbg.
|
||||
MinidumpProcessor processor(nullptr, nullptr /*&supplier, &resolver*/);
|
||||
|
||||
string minidump_file = GetTestDataPath()
|
||||
+ "tiny-exe-with-cet-xsave.dmp";
|
||||
|
||||
ProcessState state;
|
||||
ASSERT_EQ(processor.Process(minidump_file, &state),
|
||||
google_breakpad::PROCESS_OK);
|
||||
ASSERT_EQ(state.system_info()->os, "Windows NT");
|
||||
ASSERT_EQ(state.system_info()->os_version, "10.0.22000 282");
|
||||
ASSERT_EQ(state.system_info()->cpu, "amd64");
|
||||
ASSERT_EQ(state.system_info()->cpu_info,
|
||||
"family 6 model 140 stepping 1");
|
||||
ASSERT_FALSE(state.crashed());
|
||||
ASSERT_EQ(state.threads()->size(), size_t(1));
|
||||
|
||||
// TODO: verify cetumsr and cetussp once these are supported by
|
||||
// breakpad.
|
||||
}
|
||||
|
||||
TEST_F(MinidumpProcessorTest, TestFastFailException) {
|
||||
// This tests if we can understand fastfail exception subcodes.
|
||||
// Dump is captured from a toy executable and is readable by windbg.
|
||||
MinidumpProcessor processor(nullptr, nullptr /*&supplier, &resolver*/);
|
||||
|
||||
string minidump_file = GetTestDataPath()
|
||||
+ "tiny-exe-fastfail.dmp";
|
||||
|
||||
ProcessState state;
|
||||
ASSERT_EQ(processor.Process(minidump_file, &state),
|
||||
google_breakpad::PROCESS_OK);
|
||||
ASSERT_TRUE(state.crashed());
|
||||
ASSERT_EQ(state.threads()->size(), size_t(4));
|
||||
ASSERT_EQ(state.crash_reason(), "FAST_FAIL_FATAL_APP_EXIT");
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
TEST_F(MinidumpProcessorTest, TestNonCanonicalAddress) {
|
||||
// This tests if we can correctly fixup non-canonical address GPF fault
|
||||
// addresses.
|
||||
// Dump is captured from a toy executable and is readable by windbg.
|
||||
MinidumpProcessor processor(nullptr, nullptr /*&supplier, &resolver*/);
|
||||
processor.set_enable_objdump(true);
|
||||
|
||||
string minidump_file = GetTestDataPath()
|
||||
+ "write_av_non_canonical.dmp";
|
||||
|
||||
ProcessState state;
|
||||
ASSERT_EQ(processor.Process(minidump_file, &state),
|
||||
google_breakpad::PROCESS_OK);
|
||||
ASSERT_TRUE(state.crashed());
|
||||
ASSERT_EQ(state.crash_address(), 0xfefefefefefefefeU);
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
197
externals/breakpad/src/processor/minidump_stackwalk.cc
vendored
Normal file
197
externals/breakpad/src/processor/minidump_stackwalk.cc
vendored
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/path_helper.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
#include "google_breakpad/processor/minidump_processor.h"
|
||||
#include "google_breakpad/processor/process_state.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/simple_symbol_supplier.h"
|
||||
#include "processor/stackwalk_common.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
struct Options {
|
||||
bool machine_readable;
|
||||
bool output_stack_contents;
|
||||
bool output_requesting_thread_only;
|
||||
bool brief;
|
||||
|
||||
string minidump_file;
|
||||
std::vector<string> symbol_paths;
|
||||
};
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::Minidump;
|
||||
using google_breakpad::MinidumpMemoryList;
|
||||
using google_breakpad::MinidumpThreadList;
|
||||
using google_breakpad::MinidumpProcessor;
|
||||
using google_breakpad::ProcessState;
|
||||
using google_breakpad::SimpleSymbolSupplier;
|
||||
using google_breakpad::scoped_ptr;
|
||||
|
||||
// Processes |options.minidump_file| using MinidumpProcessor.
|
||||
// |options.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.
|
||||
bool PrintMinidumpProcess(const Options& options) {
|
||||
scoped_ptr<SimpleSymbolSupplier> symbol_supplier;
|
||||
if (!options.symbol_paths.empty()) {
|
||||
// TODO(mmentovai): check existence of symbol_path if specified?
|
||||
symbol_supplier.reset(new SimpleSymbolSupplier(options.symbol_paths));
|
||||
}
|
||||
|
||||
BasicSourceLineResolver resolver;
|
||||
MinidumpProcessor minidump_processor(symbol_supplier.get(), &resolver);
|
||||
|
||||
// Increase the maximum number of threads and regions.
|
||||
MinidumpThreadList::set_max_threads(std::numeric_limits<uint32_t>::max());
|
||||
MinidumpMemoryList::set_max_regions(std::numeric_limits<uint32_t>::max());
|
||||
// Process the minidump.
|
||||
Minidump dump(options.minidump_file);
|
||||
if (!dump.Read()) {
|
||||
BPLOG(ERROR) << "Minidump " << dump.path() << " could not be read";
|
||||
return false;
|
||||
}
|
||||
ProcessState process_state;
|
||||
if (minidump_processor.Process(&dump, &process_state) !=
|
||||
google_breakpad::PROCESS_OK) {
|
||||
BPLOG(ERROR) << "MinidumpProcessor::Process failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.machine_readable) {
|
||||
PrintProcessStateMachineReadable(process_state);
|
||||
} else if (options.brief) {
|
||||
PrintRequestingThreadBrief(process_state);
|
||||
} else {
|
||||
PrintProcessState(process_state, options.output_stack_contents,
|
||||
options.output_requesting_thread_only, &resolver);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
static void Usage(int argc, const char *argv[], bool error) {
|
||||
fprintf(error ? stderr : stdout,
|
||||
"Usage: %s [options] <minidump-file> [symbol-path ...]\n"
|
||||
"\n"
|
||||
"Output a stack trace for the provided minidump\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
"\n"
|
||||
" -m Output in machine-readable format\n"
|
||||
" -s Output stack contents\n"
|
||||
" -c Output thread that causes crash or dump only\n"
|
||||
" -b Brief of the thread that causes crash or dump\n",
|
||||
google_breakpad::BaseName(argv[0]).c_str());
|
||||
}
|
||||
|
||||
static void SetupOptions(int argc, const char *argv[], Options* options) {
|
||||
int ch;
|
||||
|
||||
options->machine_readable = false;
|
||||
options->output_stack_contents = false;
|
||||
options->output_requesting_thread_only = false;
|
||||
options->brief = false;
|
||||
|
||||
while ((ch = getopt(argc, (char* const*)argv, "bchms")) != -1) {
|
||||
switch (ch) {
|
||||
case 'h':
|
||||
Usage(argc, argv, false);
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
options->brief = true;
|
||||
break;
|
||||
case 'c':
|
||||
options->output_requesting_thread_only = true;
|
||||
break;
|
||||
case 'm':
|
||||
options->machine_readable = true;
|
||||
break;
|
||||
case 's':
|
||||
options->output_stack_contents = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
Usage(argc, argv, true);
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((argc - optind) == 0) {
|
||||
fprintf(stderr, "%s: Missing minidump file\n", argv[0]);
|
||||
Usage(argc, argv, true);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
options->minidump_file = argv[optind];
|
||||
|
||||
for (int argi = optind + 1; argi < argc; ++argi)
|
||||
options->symbol_paths.push_back(argv[argi]);
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[]) {
|
||||
Options options;
|
||||
SetupOptions(argc, argv, &options);
|
||||
|
||||
return PrintMinidumpProcess(options) ? 0 : 1;
|
||||
}
|
||||
36
externals/breakpad/src/processor/minidump_stackwalk_machine_readable_test
vendored
Executable file
36
externals/breakpad/src/processor/minidump_stackwalk_machine_readable_test
vendored
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright 2007 Google LLC
|
||||
#
|
||||
# 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 LLC 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 $?
|
||||
36
externals/breakpad/src/processor/minidump_stackwalk_test
vendored
Executable file
36
externals/breakpad/src/processor/minidump_stackwalk_test
vendored
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright 2006 Google LLC
|
||||
#
|
||||
# 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 LLC 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 $?
|
||||
1668
externals/breakpad/src/processor/minidump_unittest.cc
vendored
Normal file
1668
externals/breakpad/src/processor/minidump_unittest.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
305
externals/breakpad/src/processor/module_comparer.cc
vendored
Normal file
305
externals/breakpad/src/processor/module_comparer.cc
vendored
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "processor/module_comparer.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "processor/basic_code_module.h"
|
||||
#include "processor/logging.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]);
|
||||
memcpy(buffer.get(), symbol_data.c_str(), symbol_data.size());
|
||||
buffer.get()[symbol_data.size()] = '\0';
|
||||
ASSERT_TRUE(basic_module->LoadMapFromMemory(buffer.get(),
|
||||
symbol_data.size() + 1));
|
||||
buffer.reset();
|
||||
|
||||
// Serialize BasicSourceLineResolver::Module.
|
||||
size_t 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(),
|
||||
serialized_size));
|
||||
ASSERT_TRUE(fast_module->IsCorrupt() == basic_module->IsCorrupt());
|
||||
|
||||
// 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.type_ == wfi2.type_);
|
||||
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
|
||||
97
externals/breakpad/src/processor/module_comparer.h
vendored
Normal file
97
externals/breakpad/src/processor/module_comparer.h
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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__
|
||||
71
externals/breakpad/src/processor/module_factory.h
vendored
Normal file
71
externals/breakpad/src/processor/module_factory.h
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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__
|
||||
219
externals/breakpad/src/processor/module_serializer.cc
vendored
Normal file
219
externals/breakpad/src/processor/module_serializer.cc
vendored
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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 variables in SimplerSerializer<Funcion> and
|
||||
// SimplerSerializer<Inline>, which are declared in file
|
||||
// "simple_serializer-inl.h"
|
||||
RangeMapSerializer<MemAddr, linked_ptr<BasicSourceLineResolver::Line>>
|
||||
SimpleSerializer<BasicSourceLineResolver::Function>::range_map_serializer_;
|
||||
ContainedRangeMapSerializer<MemAddr,
|
||||
linked_ptr<BasicSourceLineResolver::Inline>>
|
||||
SimpleSerializer<
|
||||
BasicSourceLineResolver::Function>::inline_range_map_serializer_;
|
||||
|
||||
size_t ModuleSerializer::SizeOf(const BasicSourceLineResolver::Module& module) {
|
||||
size_t total_size_alloc_ = 0;
|
||||
|
||||
// Size of the "is_corrupt" flag.
|
||||
total_size_alloc_ += SimpleSerializer<bool>::SizeOf(module.is_corrupt_);
|
||||
|
||||
// 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_);
|
||||
map_sizes_[map_index++] =
|
||||
inline_origin_serializer_.SizeOf(module.inline_origins_);
|
||||
|
||||
// Header size.
|
||||
total_size_alloc_ += kNumberMaps_ * sizeof(uint32_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_ += SimpleSerializer<char>::SizeOf(0);
|
||||
|
||||
return total_size_alloc_;
|
||||
}
|
||||
|
||||
char* ModuleSerializer::Write(const BasicSourceLineResolver::Module& module,
|
||||
char* dest) {
|
||||
// Write the is_corrupt flag.
|
||||
dest = SimpleSerializer<bool>::Write(module.is_corrupt_, dest);
|
||||
// Write header.
|
||||
memcpy(dest, map_sizes_, kNumberMaps_ * sizeof(uint32_t));
|
||||
dest += kNumberMaps_ * sizeof(uint32_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);
|
||||
dest = inline_origin_serializer_.Write(module.inline_origins_, dest);
|
||||
// Write a null terminator.
|
||||
dest = SimpleSerializer<char>::Write(0, dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
char* ModuleSerializer::Serialize(const BasicSourceLineResolver::Module& module,
|
||||
size_t* size) {
|
||||
// Compute size of memory to allocate.
|
||||
const size_t 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.
|
||||
const size_t size_written =
|
||||
static_cast<size_t>(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);
|
||||
|
||||
size_t 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,
|
||||
size_t* size) {
|
||||
scoped_ptr<BasicSourceLineResolver::Module> module(
|
||||
new BasicSourceLineResolver::Module("no name"));
|
||||
scoped_array<char> buffer(new char[symbol_data.size() + 1]);
|
||||
memcpy(buffer.get(), symbol_data.c_str(), symbol_data.size());
|
||||
buffer.get()[symbol_data.size()] = '\0';
|
||||
if (!module->LoadMapFromMemory(buffer.get(), symbol_data.size() + 1)) {
|
||||
return NULL;
|
||||
}
|
||||
buffer.reset(NULL);
|
||||
return Serialize(*(module.get()), size);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
128
externals/breakpad/src/processor/module_serializer.h
vendored
Normal file
128
externals/breakpad/src/processor/module_serializer.h
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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,
|
||||
size_t* size = nullptr);
|
||||
|
||||
// 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,
|
||||
size_t* size = nullptr);
|
||||
|
||||
// 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;
|
||||
typedef BasicSourceLineResolver::InlineOrigin InlineOrigin;
|
||||
|
||||
// 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 int32_t kNumberMaps_ =
|
||||
FastSourceLineResolver::Module::kNumberMaps_;
|
||||
|
||||
// Memory sizes required to serialize map components in Module.
|
||||
uint32_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_;
|
||||
StdMapSerializer<int, linked_ptr<InlineOrigin>> inline_origin_serializer_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_MODULE_SERIALIZER_H__
|
||||
59
externals/breakpad/src/processor/pathname_stripper.cc
vendored
Normal file
59
externals/breakpad/src/processor/pathname_stripper.cc
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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
|
||||
52
externals/breakpad/src/processor/pathname_stripper.h
vendored
Normal file
52
externals/breakpad/src/processor/pathname_stripper.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
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__
|
||||
90
externals/breakpad/src/processor/pathname_stripper_unittest.cc
vendored
Normal file
90
externals/breakpad/src/processor/pathname_stripper_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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;
|
||||
}
|
||||
362
externals/breakpad/src/processor/postfix_evaluator-inl.h
vendored
Normal file
362
externals/breakpad/src/processor/postfix_evaluator-inl.h
vendored
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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>::EvaluateToken(
|
||||
const string& token,
|
||||
const string& expression,
|
||||
DictionaryValidityType* assigned) {
|
||||
// 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,
|
||||
BINARY_OP_ALIGN
|
||||
};
|
||||
|
||||
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;
|
||||
else if (token == "@")
|
||||
operation = BINARY_OP_ALIGN;
|
||||
|
||||
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_ALIGN:
|
||||
result =
|
||||
operand1 & (static_cast<ValueType>(-1) ^ (operand2 - 1));
|
||||
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>::EvaluateInternal(
|
||||
const string& expression,
|
||||
DictionaryValidityType* assigned) {
|
||||
// Tokenize, splitting on whitespace.
|
||||
istringstream stream(expression);
|
||||
string token;
|
||||
while (stream >> token) {
|
||||
// Normally, tokens are whitespace-separated, but occasionally, the
|
||||
// assignment operator is smashed up against the next token, i.e.
|
||||
// $T0 $ebp 128 + =$eip $T0 4 + ^ =$ebp $T0 ^ =
|
||||
// This has been observed in program strings produced by MSVS 2010 in LTO
|
||||
// mode.
|
||||
if (token.size() > 1 && token[0] == '=') {
|
||||
if (!EvaluateToken("=", expression, assigned)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EvaluateToken(token.substr(1), expression, assigned)) {
|
||||
return false;
|
||||
}
|
||||
} else if (!EvaluateToken(token, expression, assigned)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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__
|
||||
178
externals/breakpad/src/processor/postfix_evaluator.h
vendored
Normal file
178
externals/breakpad/src/processor/postfix_evaluator.h
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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),
|
||||
// % (modulus of division), and @ (data alignment). The alignment operator (@)
|
||||
// accepts a value and an alignment size, and produces a result that is a
|
||||
// multiple of the alignment size by truncating the input value.
|
||||
// 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>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::map;
|
||||
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);
|
||||
|
||||
bool EvaluateToken(const string& token,
|
||||
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__
|
||||
406
externals/breakpad/src/processor/postfix_evaluator_unittest.cc
vendored
Normal file
406
externals/breakpad/src/processor/postfix_evaluator_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "processor/postfix_evaluator-inl.h"
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
using std::map;
|
||||
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 uint64_t GetBase() const { return 0; }
|
||||
virtual uint32_t GetSize() const { return 0; }
|
||||
virtual bool GetMemoryAtAddress(uint64_t address, uint8_t* value) const {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
virtual bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
virtual bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
virtual bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
virtual void Print() const {
|
||||
assert(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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)
|
||||
{ "$rAlign 36 8 @ =", true }, // $rAlign = 36 @ 8
|
||||
{ "$rAdd3 2 2 + =$rMul2 9 6 * =", true } // smashed-equals tokenization
|
||||
};
|
||||
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;
|
||||
validate_data_0["$rAlign"] = 32;
|
||||
validate_data_0["$rAdd3"] = 4;
|
||||
validate_data_0["$rMul2"] = 54;
|
||||
|
||||
// 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;
|
||||
}
|
||||
110
externals/breakpad/src/processor/proc_maps_linux.cc
vendored
Normal file
110
externals/breakpad/src/processor/proc_maps_linux.cc
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2013 Google LLC
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "google_breakpad/processor/proc_maps_linux.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
#if defined(OS_ANDROID) && !defined(__LP64__)
|
||||
// In 32-bit mode, Bionic's inttypes.h defines PRI/SCNxPTR as an
|
||||
// unsigned long int, which is incompatible with Bionic's stdint.h
|
||||
// defining uintptr_t as an unsigned int:
|
||||
// https://code.google.com/p/android/issues/detail?id=57218
|
||||
#undef SCNxPTR
|
||||
#define SCNxPTR "x"
|
||||
#endif
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool ParseProcMaps(const string& input,
|
||||
std::vector<MappedMemoryRegion>* regions_out) {
|
||||
std::vector<MappedMemoryRegion> regions;
|
||||
|
||||
// This isn't async safe nor terribly efficient, but it doesn't need to be at
|
||||
// this point in time.
|
||||
|
||||
// Split the string by newlines.
|
||||
std::vector<string> lines;
|
||||
string l = "";
|
||||
for (size_t i = 0; i < input.size(); i++) {
|
||||
if (input[i] != '\n' && input[i] != '\r') {
|
||||
l.push_back(input[i]);
|
||||
} else if (l.size() > 0) {
|
||||
lines.push_back(l);
|
||||
l.clear();
|
||||
}
|
||||
}
|
||||
if (l.size() > 0) {
|
||||
BPLOG(ERROR) << "Input doesn't end in newline";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
MappedMemoryRegion region;
|
||||
const char* line = lines[i].c_str();
|
||||
char permissions[5] = {'\0'}; // Ensure NUL-terminated string.
|
||||
int path_index = 0;
|
||||
|
||||
// Sample format from man 5 proc:
|
||||
//
|
||||
// address perms offset dev inode pathname
|
||||
// 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
|
||||
//
|
||||
// The final %n term captures the offset in the input string, which is used
|
||||
// to determine the path name. It *does not* increment the return value.
|
||||
// Refer to man 3 sscanf for details.
|
||||
if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %4c %" SCNx64" %hhx:%hhx %"
|
||||
SCNd64 " %n", ®ion.start, ®ion.end, permissions,
|
||||
®ion.offset, ®ion.major_device, ®ion.minor_device,
|
||||
®ion.inode, &path_index) < 7) {
|
||||
BPLOG(ERROR) << "sscanf failed for line: " << line;
|
||||
return false;
|
||||
}
|
||||
|
||||
region.permissions = 0;
|
||||
|
||||
if (permissions[0] == 'r')
|
||||
region.permissions |= MappedMemoryRegion::READ;
|
||||
else if (permissions[0] != '-')
|
||||
return false;
|
||||
|
||||
if (permissions[1] == 'w')
|
||||
region.permissions |= MappedMemoryRegion::WRITE;
|
||||
else if (permissions[1] != '-')
|
||||
return false;
|
||||
|
||||
if (permissions[2] == 'x')
|
||||
region.permissions |= MappedMemoryRegion::EXECUTE;
|
||||
else if (permissions[2] != '-')
|
||||
return false;
|
||||
|
||||
if (permissions[3] == 'p')
|
||||
region.permissions |= MappedMemoryRegion::PRIVATE;
|
||||
else if (permissions[3] != 's' && permissions[3] != 'S') // Shared memory.
|
||||
return false;
|
||||
|
||||
// Pushing then assigning saves us a string copy.
|
||||
regions.push_back(region);
|
||||
regions.back().path.assign(line + path_index);
|
||||
regions.back().line.assign(line);
|
||||
}
|
||||
|
||||
regions_out->swap(regions);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
255
externals/breakpad/src/processor/proc_maps_linux_unittest.cc
vendored
Normal file
255
externals/breakpad/src/processor/proc_maps_linux_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
// Copyright 2013 Google LLC
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/processor/proc_maps_linux.h"
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(ProcMapsTest, Empty) {
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
EXPECT_TRUE(ParseProcMaps("", ®ions));
|
||||
EXPECT_EQ(0u, regions.size());
|
||||
}
|
||||
|
||||
TEST(ProcMapsTest, NoSpaces) {
|
||||
static const char kNoSpaces[] =
|
||||
"00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat\n";
|
||||
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
ASSERT_TRUE(ParseProcMaps(kNoSpaces, ®ions));
|
||||
ASSERT_EQ(1u, regions.size());
|
||||
|
||||
EXPECT_EQ(0x00400000u, regions[0].start);
|
||||
EXPECT_EQ(0x0040b000u, regions[0].end);
|
||||
EXPECT_EQ(0x00002200u, regions[0].offset);
|
||||
EXPECT_EQ("/bin/cat", regions[0].path);
|
||||
}
|
||||
|
||||
TEST(ProcMapsTest, Spaces) {
|
||||
static const char kSpaces[] =
|
||||
"00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/space cat\n";
|
||||
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
ASSERT_TRUE(ParseProcMaps(kSpaces, ®ions));
|
||||
ASSERT_EQ(1u, regions.size());
|
||||
|
||||
EXPECT_EQ(0x00400000u, regions[0].start);
|
||||
EXPECT_EQ(0x0040b000u, regions[0].end);
|
||||
EXPECT_EQ(0x00002200u, regions[0].offset);
|
||||
EXPECT_EQ("/bin/space cat", regions[0].path);
|
||||
}
|
||||
|
||||
TEST(ProcMapsTest, NoNewline) {
|
||||
static const char kNoSpaces[] =
|
||||
"00400000-0040b000 r-xp 00002200 fc:00 794418 /bin/cat";
|
||||
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
ASSERT_FALSE(ParseProcMaps(kNoSpaces, ®ions));
|
||||
}
|
||||
|
||||
TEST(ProcMapsTest, NoPath) {
|
||||
static const char kNoPath[] =
|
||||
"00400000-0040b000 rw-p 00000000 00:00 0 \n";
|
||||
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
ASSERT_TRUE(ParseProcMaps(kNoPath, ®ions));
|
||||
ASSERT_EQ(1u, regions.size());
|
||||
|
||||
EXPECT_EQ(0x00400000u, regions[0].start);
|
||||
EXPECT_EQ(0x0040b000u, regions[0].end);
|
||||
EXPECT_EQ(0x00000000u, regions[0].offset);
|
||||
EXPECT_EQ("", regions[0].path);
|
||||
}
|
||||
|
||||
TEST(ProcMapsTest, Heap) {
|
||||
static const char kHeap[] =
|
||||
"022ac000-022cd000 rw-p 00000000 00:00 0 [heap]\n";
|
||||
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
ASSERT_TRUE(ParseProcMaps(kHeap, ®ions));
|
||||
ASSERT_EQ(1u, regions.size());
|
||||
|
||||
EXPECT_EQ(0x022ac000u, regions[0].start);
|
||||
EXPECT_EQ(0x022cd000u, regions[0].end);
|
||||
EXPECT_EQ(0x00000000u, regions[0].offset);
|
||||
EXPECT_EQ("[heap]", regions[0].path);
|
||||
}
|
||||
|
||||
#if defined(ARCH_CPU_32_BITS)
|
||||
TEST(ProcMapsTest, Stack32) {
|
||||
static const char kStack[] =
|
||||
"beb04000-beb25000 rw-p 00000000 00:00 0 [stack]\n";
|
||||
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
ASSERT_TRUE(ParseProcMaps(kStack, ®ions));
|
||||
ASSERT_EQ(1u, regions.size());
|
||||
|
||||
EXPECT_EQ(0xbeb04000u, regions[0].start);
|
||||
EXPECT_EQ(0xbeb25000u, regions[0].end);
|
||||
EXPECT_EQ(0x00000000u, regions[0].offset);
|
||||
EXPECT_EQ("[stack]", regions[0].path);
|
||||
}
|
||||
#elif defined(ARCH_CPU_64_BITS)
|
||||
TEST(ProcMapsTest, Stack64) {
|
||||
static const char kStack[] =
|
||||
"7fff69c5b000-7fff69c7d000 rw-p 00000000 00:00 0 [stack]\n";
|
||||
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
ASSERT_TRUE(ParseProcMaps(kStack, ®ions));
|
||||
ASSERT_EQ(1u, regions.size());
|
||||
|
||||
EXPECT_EQ(0x7fff69c5b000u, regions[0].start);
|
||||
EXPECT_EQ(0x7fff69c7d000u, regions[0].end);
|
||||
EXPECT_EQ(0x00000000u, regions[0].offset);
|
||||
EXPECT_EQ("[stack]", regions[0].path);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(ProcMapsTest, Multiple) {
|
||||
static const char kMultiple[] =
|
||||
"00400000-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n"
|
||||
"0060a000-0060b000 r--p 0000a000 fc:00 794418 /bin/cat\n"
|
||||
"0060b000-0060c000 rw-p 0000b000 fc:00 794418 /bin/cat\n";
|
||||
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
ASSERT_TRUE(ParseProcMaps(kMultiple, ®ions));
|
||||
ASSERT_EQ(3u, regions.size());
|
||||
|
||||
EXPECT_EQ(0x00400000u, regions[0].start);
|
||||
EXPECT_EQ(0x0040b000u, regions[0].end);
|
||||
EXPECT_EQ(0x00000000u, regions[0].offset);
|
||||
EXPECT_EQ("/bin/cat", regions[0].path);
|
||||
|
||||
EXPECT_EQ(0x0060a000u, regions[1].start);
|
||||
EXPECT_EQ(0x0060b000u, regions[1].end);
|
||||
EXPECT_EQ(0x0000a000u, regions[1].offset);
|
||||
EXPECT_EQ("/bin/cat", regions[1].path);
|
||||
|
||||
EXPECT_EQ(0x0060b000u, regions[2].start);
|
||||
EXPECT_EQ(0x0060c000u, regions[2].end);
|
||||
EXPECT_EQ(0x0000b000u, regions[2].offset);
|
||||
EXPECT_EQ("/bin/cat", regions[2].path);
|
||||
}
|
||||
|
||||
TEST(ProcMapsTest, Permissions) {
|
||||
static struct {
|
||||
const char* input;
|
||||
uint8_t permissions;
|
||||
} kTestCases[] = {
|
||||
{"00400000-0040b000 ---s 00000000 fc:00 794418 /bin/cat\n", 0},
|
||||
{"00400000-0040b000 ---S 00000000 fc:00 794418 /bin/cat\n", 0},
|
||||
{"00400000-0040b000 r--s 00000000 fc:00 794418 /bin/cat\n",
|
||||
google_breakpad::MappedMemoryRegion::READ},
|
||||
{"00400000-0040b000 -w-s 00000000 fc:00 794418 /bin/cat\n",
|
||||
google_breakpad::MappedMemoryRegion::WRITE},
|
||||
{"00400000-0040b000 --xs 00000000 fc:00 794418 /bin/cat\n",
|
||||
google_breakpad::MappedMemoryRegion::EXECUTE},
|
||||
{"00400000-0040b000 rwxs 00000000 fc:00 794418 /bin/cat\n",
|
||||
google_breakpad::MappedMemoryRegion::READ
|
||||
| google_breakpad::MappedMemoryRegion::WRITE
|
||||
| google_breakpad::MappedMemoryRegion::EXECUTE},
|
||||
{"00400000-0040b000 ---p 00000000 fc:00 794418 /bin/cat\n",
|
||||
google_breakpad::MappedMemoryRegion::PRIVATE},
|
||||
{"00400000-0040b000 r--p 00000000 fc:00 794418 /bin/cat\n",
|
||||
google_breakpad::MappedMemoryRegion::READ
|
||||
| google_breakpad::MappedMemoryRegion::PRIVATE},
|
||||
{"00400000-0040b000 -w-p 00000000 fc:00 794418 /bin/cat\n",
|
||||
google_breakpad::MappedMemoryRegion::WRITE
|
||||
| google_breakpad::MappedMemoryRegion::PRIVATE},
|
||||
{"00400000-0040b000 --xp 00000000 fc:00 794418 /bin/cat\n",
|
||||
google_breakpad::MappedMemoryRegion::EXECUTE
|
||||
| google_breakpad::MappedMemoryRegion::PRIVATE},
|
||||
{"00400000-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n",
|
||||
google_breakpad::MappedMemoryRegion::READ
|
||||
| google_breakpad::MappedMemoryRegion::WRITE
|
||||
| google_breakpad::MappedMemoryRegion::EXECUTE
|
||||
| google_breakpad::MappedMemoryRegion::PRIVATE},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(kTestCases) / sizeof(kTestCases[0]); ++i) {
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
EXPECT_TRUE(ParseProcMaps(kTestCases[i].input, ®ions));
|
||||
EXPECT_EQ(1u, regions.size());
|
||||
if (regions.empty())
|
||||
continue;
|
||||
EXPECT_EQ(kTestCases[i].permissions, regions[0].permissions);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ProcMapsTest, MissingFields) {
|
||||
static const char* kTestCases[] = {
|
||||
"00400000\n", // Missing end + beyond.
|
||||
"00400000-0040b000\n", // Missing perms + beyond.
|
||||
"00400000-0040b000 r-xp\n", // Missing offset + beyond.
|
||||
"00400000-0040b000 r-xp 00000000\n", // Missing device + beyond.
|
||||
"00400000-0040b000 r-xp 00000000 fc:00\n", // Missing inode + beyond.
|
||||
"00400000-0040b000 00000000 fc:00 794418 /bin/cat\n", // Missing perms.
|
||||
"00400000-0040b000 r-xp fc:00 794418 /bin/cat\n", // Missing offset.
|
||||
"00400000-0040b000 r-xp 00000000 fc:00 /bin/cat\n", // Missing inode.
|
||||
"00400000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing end.
|
||||
"-0040b000 r-xp 00000000 fc:00 794418 /bin/cat\n", // Missing start.
|
||||
"00400000-0040b000 r-xp 00000000 794418 /bin/cat\n", // Missing device.
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(kTestCases) / sizeof(kTestCases[0]); ++i) {
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
EXPECT_FALSE(ParseProcMaps(kTestCases[i], ®ions));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ProcMapsTest, InvalidInput) {
|
||||
static const char* kTestCases[] = {
|
||||
"thisisal-0040b000 rwxp 00000000 fc:00 794418 /bin/cat\n",
|
||||
"0040000d-linvalid rwxp 00000000 fc:00 794418 /bin/cat\n",
|
||||
"00400000-0040b000 inpu 00000000 fc:00 794418 /bin/cat\n",
|
||||
"00400000-0040b000 rwxp tforproc fc:00 794418 /bin/cat\n",
|
||||
"00400000-0040b000 rwxp 00000000 ma:ps 794418 /bin/cat\n",
|
||||
"00400000-0040b000 rwxp 00000000 fc:00 parse! /bin/cat\n",
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(kTestCases) / sizeof(kTestCases[0]); ++i) {
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
EXPECT_FALSE(ParseProcMaps(kTestCases[i], ®ions));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ProcMapsTest, ParseProcMapsEmptyString) {
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
EXPECT_TRUE(ParseProcMaps("", ®ions));
|
||||
EXPECT_EQ(0ULL, regions.size());
|
||||
}
|
||||
|
||||
// Testing a couple of remotely possible weird things in the input:
|
||||
// - Line ending with \r\n or \n\r.
|
||||
// - File name contains quotes.
|
||||
// - File name has whitespaces.
|
||||
TEST(ProcMapsTest, ParseProcMapsWeirdCorrectInput) {
|
||||
std::vector<google_breakpad::MappedMemoryRegion> regions;
|
||||
const string kContents =
|
||||
"00400000-0040b000 r-xp 00000000 fc:00 2106562 "
|
||||
" /bin/cat\r\n"
|
||||
"7f53b7dad000-7f53b7f62000 r-xp 00000000 fc:00 263011 "
|
||||
" /lib/x86_64-linux-gnu/libc-2.15.so\n\r"
|
||||
"7f53b816d000-7f53b818f000 r-xp 00000000 fc:00 264284 "
|
||||
" /lib/x86_64-linux-gnu/ld-2.15.so\n"
|
||||
"7fff9c7ff000-7fff9c800000 r-xp 00000000 00:00 0 "
|
||||
" \"vd so\"\n"
|
||||
"ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 "
|
||||
" [vsys call]\n";
|
||||
EXPECT_TRUE(ParseProcMaps(kContents, ®ions));
|
||||
EXPECT_EQ(5ULL, regions.size());
|
||||
EXPECT_EQ("/bin/cat", regions[0].path);
|
||||
EXPECT_EQ("/lib/x86_64-linux-gnu/libc-2.15.so", regions[1].path);
|
||||
EXPECT_EQ("/lib/x86_64-linux-gnu/ld-2.15.so", regions[2].path);
|
||||
EXPECT_EQ("\"vd so\"", regions[3].path);
|
||||
EXPECT_EQ("[vsys call]", regions[4].path);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
75
externals/breakpad/src/processor/process_state.cc
vendored
Normal file
75
externals/breakpad/src/processor/process_state.cc
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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;
|
||||
process_create_time_ = 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();
|
||||
thread_names_.clear();
|
||||
// modules_without_symbols_ and modules_with_corrupt_symbols_ DO NOT own
|
||||
// the underlying CodeModule pointers. Just clear the vectors.
|
||||
modules_without_symbols_.clear();
|
||||
modules_with_corrupt_symbols_.clear();
|
||||
delete modules_;
|
||||
modules_ = NULL;
|
||||
delete unloaded_modules_;
|
||||
unloaded_modules_ = NULL;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
20
externals/breakpad/src/processor/proto/README
vendored
Normal file
20
externals/breakpad/src/processor/proto/README
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
If you wish to use these protobufs, you must generate their source files
|
||||
using protoc from the protobuf project (https://github.com/google/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
|
||||
209
externals/breakpad/src/processor/proto/process_state.proto
vendored
Normal file
209
externals/breakpad/src/processor/proto/process_state.proto
vendored
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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: 14
|
||||
|
||||
// The time-date stamp of the original minidump (time_t format)
|
||||
optional int64 time_date_stamp = 1;
|
||||
|
||||
// The time-date stamp when the process was created (time_t format)
|
||||
optional int64 process_create_time = 13;
|
||||
|
||||
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;
|
||||
}
|
||||
296
externals/breakpad/src/processor/range_map-inl.h
vendored
Normal file
296
externals/breakpad/src/processor/range_map-inl.h
vendored
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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 "common/safe_math.h"
|
||||
#include "processor/range_map.h"
|
||||
#include "processor/linked_ptr.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) {
|
||||
return StoreRangeInternal(base, 0 /* delta */, size, entry);
|
||||
}
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
bool RangeMap<AddressType, EntryType>::StoreRangeInternal(
|
||||
const AddressType& base, const AddressType& delta,
|
||||
const AddressType& size, const EntryType& entry) {
|
||||
AddressType high;
|
||||
bool high_ok = false;
|
||||
if (size > 0) {
|
||||
std::pair<AddressType, bool> result = AddWithOverflowCheck(base, size - 1);
|
||||
high = result.first;
|
||||
high_ok = !result.second;
|
||||
}
|
||||
// Check for undersize or overflow.
|
||||
if (!high_ok) {
|
||||
// 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) << "StoreRangeInternal failed, "
|
||||
<< HexString(base) << "+" << HexString(size)
|
||||
<< ", " << HexString(high)
|
||||
<< ", delta: " << HexString(delta);
|
||||
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 ends in the space used by this range. It may be
|
||||
// contained within the space used by this range, or it may extend lower.
|
||||
if (merge_strategy_ == MergeRangeStrategy::kTruncateLower) {
|
||||
// kTruncate the range with the lower base address.
|
||||
AddressType other_base = iterator_base->second.base();
|
||||
if (base < other_base) {
|
||||
return StoreRangeInternal(base, delta, other_base - base, entry);
|
||||
} else if (other_base < base) {
|
||||
EntryType other_entry;
|
||||
AddressType other_high, other_size, other_delta;
|
||||
other_high = iterator_base->first;
|
||||
RetrieveRange(other_high, &other_entry, &other_base, &other_delta,
|
||||
&other_size);
|
||||
map_.erase(iterator_base);
|
||||
map_.insert(
|
||||
MapValue(base - 1, Range(other_base, other_delta, other_entry)));
|
||||
return StoreRangeInternal(base, delta, size, entry);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (merge_strategy_ == MergeRangeStrategy::kTruncateUpper) {
|
||||
// Truncate the lower portion of this range.
|
||||
AddressType additional_delta = iterator_base->first - base + 1;
|
||||
return StoreRangeInternal(base + additional_delta,
|
||||
delta + additional_delta,
|
||||
size - additional_delta, entry);
|
||||
} else {
|
||||
// The processor hits this case too frequently with common symbol files.
|
||||
// This is most appropriate for a DEBUG channel, but since none exists
|
||||
// now simply comment out this logging.
|
||||
// AddressType other_base = iterator_base->second.base();
|
||||
// AddressType other_size = iterator_base->first - other_base + 1;
|
||||
// BPLOG(INFO) << "StoreRangeInternal failed, an existing range is "
|
||||
// << "overlapping with the new range: new "
|
||||
// << HexString(base) << "+" << HexString(size)
|
||||
// << ", existing " << HexString(other_base) << "+"
|
||||
// << HexString(other_size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (iterator_high != map_.end() && 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.
|
||||
if (merge_strategy_ == MergeRangeStrategy::kTruncateLower) {
|
||||
AddressType other_base = iterator_high->second.base();
|
||||
if (base < other_base) {
|
||||
return StoreRangeInternal(base, delta, other_base - base, entry);
|
||||
} else if (other_base < base) {
|
||||
EntryType other_entry;
|
||||
AddressType other_high, other_size, other_delta;
|
||||
other_high = iterator_high->first;
|
||||
RetrieveRange(other_high, &other_entry, &other_base, &other_delta,
|
||||
&other_size);
|
||||
map_.erase(iterator_high);
|
||||
map_.insert(
|
||||
MapValue(base - 1, Range(other_base, other_delta, other_entry)));
|
||||
return StoreRangeInternal(base, delta, size, entry);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (merge_strategy_ == MergeRangeStrategy::kTruncateUpper &&
|
||||
iterator_high->first > high) {
|
||||
// Shrink the other range down.
|
||||
AddressType other_high = iterator_high->first;
|
||||
AddressType additional_delta = high - iterator_high->second.base() + 1;
|
||||
EntryType other_entry;
|
||||
AddressType other_base = AddressType();
|
||||
AddressType other_size = AddressType();
|
||||
AddressType other_delta = AddressType();
|
||||
RetrieveRange(other_high, &other_entry, &other_base, &other_delta,
|
||||
&other_size);
|
||||
map_.erase(iterator_high);
|
||||
map_.insert(MapValue(other_high,
|
||||
Range(other_base + additional_delta,
|
||||
other_delta + additional_delta, other_entry)));
|
||||
// Retry to store this range.
|
||||
return StoreRangeInternal(base, delta, size, entry);
|
||||
} else {
|
||||
// The processor hits this case too frequently with common symbol files.
|
||||
// This is most appropriate for a DEBUG channel, but since none exists
|
||||
// now simply comment out this logging.
|
||||
//
|
||||
// AddressType other_base = iterator_high->second.base();
|
||||
// AddressType other_size = iterator_high->first - other_base + 1;
|
||||
// BPLOG(INFO) << "StoreRangeInternal 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, delta, entry)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
bool RangeMap<AddressType, EntryType>::RetrieveRange(
|
||||
const AddressType& address, EntryType* entry, AddressType* entry_base,
|
||||
AddressType* entry_delta, 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_delta)
|
||||
*entry_delta = iterator->second.delta();
|
||||
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_delta, 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_delta, 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_delta)
|
||||
*entry_delta = iterator->second.delta();
|
||||
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_delta, 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_delta)
|
||||
*entry_delta = iterator->second.delta();
|
||||
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 static_cast<int>(map_.size());
|
||||
}
|
||||
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
void RangeMap<AddressType, EntryType>::Clear() {
|
||||
map_.clear();
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // PROCESSOR_RANGE_MAP_INL_H__
|
||||
170
externals/breakpad/src/processor/range_map.h
vendored
Normal file
170
externals/breakpad/src/processor/range_map.h
vendored
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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;
|
||||
|
||||
// Determines what happens when two ranges overlap.
|
||||
enum class MergeRangeStrategy {
|
||||
// When two ranges overlap, the new range fails to be inserted. The default
|
||||
// strategy.
|
||||
kExclusiveRanges,
|
||||
|
||||
// The range with the lower base address will be truncated such that it's
|
||||
// high address is one less than the range above it.
|
||||
kTruncateLower,
|
||||
|
||||
// The range with the greater high address has its range truncated such that
|
||||
// its base address is one higher than the range below it.
|
||||
kTruncateUpper
|
||||
};
|
||||
|
||||
template<typename AddressType, typename EntryType>
|
||||
class RangeMap {
|
||||
public:
|
||||
RangeMap() : merge_strategy_(MergeRangeStrategy::kExclusiveRanges), map_() {}
|
||||
|
||||
void SetMergeStrategy(MergeRangeStrategy strat) { merge_strategy_ = strat; }
|
||||
|
||||
MergeRangeStrategy GetMergeStrategy() const { return merge_strategy_; }
|
||||
|
||||
// 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. If enable_shrink_down is true and there is an overlap
|
||||
// between the current range and some other range (already in the map),
|
||||
// shrink down the range which ends at a higher address.
|
||||
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, entry_delta, and entry_size, if
|
||||
// non-NULL, are set to the base, delta, and size of the entry's range.
|
||||
// A positive entry delta (> 0) indicates that there was an overlap and the
|
||||
// entry was shrunk down (original start address was increased by delta).
|
||||
bool RetrieveRange(const AddressType& address, EntryType* entry,
|
||||
AddressType* entry_base, AddressType* entry_delta,
|
||||
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, entry_delta, and entry_size,
|
||||
// if non-NULL, are set to the base, delta, and size of the entry's range.
|
||||
// A positive entry delta (> 0) indicates that there was an overlap and the
|
||||
// entry was shrunk down (original start address was increased by delta).
|
||||
bool RetrieveNearestRange(const AddressType& address, EntryType* entry,
|
||||
AddressType* entry_base, AddressType* entry_delta,
|
||||
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,
|
||||
// entry_delta, and entry_size, if non-NULL, are set to the base, delta, and
|
||||
// size of the entry's range.
|
||||
// A positive entry delta (> 0) indicates that there was an overlap and the
|
||||
// entry was shrunk down (original start address was increased by delta).
|
||||
//
|
||||
// RetrieveRangeAtIndex is not optimized for speedy operation.
|
||||
bool RetrieveRangeAtIndex(int index, EntryType* entry,
|
||||
AddressType* entry_base, AddressType* entry_delta,
|
||||
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>;
|
||||
|
||||
// Same a StoreRange() with the only exception that the |delta| can be
|
||||
// passed in.
|
||||
bool StoreRangeInternal(const AddressType& base, const AddressType& delta,
|
||||
const AddressType& size, const EntryType& entry);
|
||||
|
||||
class Range {
|
||||
public:
|
||||
Range(const AddressType& base, const AddressType& delta,
|
||||
const EntryType& entry)
|
||||
: base_(base), delta_(delta), entry_(entry) {}
|
||||
|
||||
AddressType base() const { return base_; }
|
||||
AddressType delta() const { return delta_; }
|
||||
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 delta when the range is shrunk down.
|
||||
const AddressType delta_;
|
||||
|
||||
// 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;
|
||||
|
||||
MergeRangeStrategy merge_strategy_;
|
||||
|
||||
// Maps the high address of each range to a EntryType.
|
||||
AddressToRangeMap map_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // PROCESSOR_RANGE_MAP_H__
|
||||
349
externals/breakpad/src/processor/range_map_truncate_lower_unittest.cc
vendored
Normal file
349
externals/breakpad/src/processor/range_map_truncate_lower_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "processor/range_map-inl.h"
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::linked_ptr;
|
||||
using google_breakpad::MergeRangeStrategy;
|
||||
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;
|
||||
|
||||
// Same range cannot be stored wice.
|
||||
TEST(RangeMapTruncateLower, SameRange) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(0 /* base address */, 100 /* size */, object_1));
|
||||
|
||||
// Same range cannot be stored wice.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_FALSE(
|
||||
range_map.StoreRange(0 /* base address */, 100 /* size */, object_2));
|
||||
}
|
||||
|
||||
// If a range is completely contained by another range, then the larger range
|
||||
// should be truncated.
|
||||
TEST(RangeMapTruncateLower, CompletelyContained) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
|
||||
// Larger range is added first.
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(0 /* base address */, 100 /* size */, object_1));
|
||||
// Smaller (contained) range is added second.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(10 /* base address */, 80 /* size */, object_2));
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The first range contains the second, so the first range should have been
|
||||
// shrunk to [0, 10]. Range [90, 99] should be free.
|
||||
EXPECT_FALSE(range_map.RetrieveRange(90, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_FALSE(range_map.RetrieveRange(99, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_TRUE(range_map.RetrieveRange(9, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(0, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(10, retrieved_size);
|
||||
// Validate the properties of the smaller range (should be untouched).
|
||||
EXPECT_TRUE(range_map.RetrieveRange(10, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(10, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(80, retrieved_size);
|
||||
}
|
||||
|
||||
// Same as the previous test, however the larger range is added second.
|
||||
TEST(RangeMapTruncateLower, CompletelyContained_LargerAddedSecond) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
|
||||
// Smaller (contained) range is added first.
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(10 /* base address */, 80 /* size */, object_1));
|
||||
// Larger range is added second.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(0 /* base address */, 100 /* size */, object_2));
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The second range contains the first, so the second range should have been
|
||||
// truncated to [0, 9]. Range [90, 99] should be free.
|
||||
EXPECT_FALSE(range_map.RetrieveRange(90, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_FALSE(range_map.RetrieveRange(99, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_TRUE(range_map.RetrieveRange(9, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(0, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(10, retrieved_size);
|
||||
// Validate the properties of the smaller range (should be untouched).
|
||||
EXPECT_TRUE(range_map.RetrieveRange(10, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(10, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(80, retrieved_size);
|
||||
}
|
||||
|
||||
TEST(RangeMapTruncateLower, PartialOverlap_AtBeginning) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(0 /* base address */, 100 /* size */, object_1));
|
||||
|
||||
// Partial overlap at the beginning of the new range.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(90 /* base address */, 110 /* size */, object_2));
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The first range should be truncated, so 99 should address the second range.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(99, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(90, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(110, retrieved_size);
|
||||
// Validate the properties of the truncated range.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(89, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(0, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(90, retrieved_size);
|
||||
}
|
||||
|
||||
TEST(RangeMapTruncateLower, PartialOverlap_AtEnd) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(50 /* base address */, 50 /* size */, object_1));
|
||||
|
||||
// Partial overlap at the end of the new range.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(0 /* base address */, 70 /* size */, object_2));
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The second range should be truncated so 69 addresses the first range.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(69, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(50, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(50, retrieved_size);
|
||||
// Validate the properties of the truncated range.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(49, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(0, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(50, retrieved_size);
|
||||
}
|
||||
|
||||
// A new range is overlapped at both ends. The new range and the range
|
||||
// that overlaps at the beginning should be truncated. The range that overlaps
|
||||
// at the end should be left untouched.
|
||||
TEST(RangeMapTruncateLower, OverlapAtBothEnds) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
|
||||
// This should overlap object_3 at the beginning.
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(0 /* base address */, 100 /* size */, object_1));
|
||||
|
||||
// This should overlap object_3 at the end.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(100 /* base address */, 100 /* size */, object_2));
|
||||
|
||||
// This should be overlapped on both ends by object_1 and object_2.
|
||||
linked_ptr<CountedObject> object_3(new CountedObject(3));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(50 /* base address */, 100 /* size */, object_3));
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The first range should be truncated.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(0, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(0, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(50, retrieved_size);
|
||||
// The second range should be intact.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(150, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(100, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(100, retrieved_size);
|
||||
// The third range (in the middle) should be truncated.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(99, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(3, object->id());
|
||||
EXPECT_EQ(50, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(50, retrieved_size);
|
||||
}
|
||||
|
||||
TEST(RangeMapTruncateLower, MultipleConflicts) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
|
||||
// This should overlap with object_3.
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(10 /* base address */, 90 /* size */, object_1));
|
||||
|
||||
// This should also overlap with object_3 but after object_1.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(100 /* base address */, 100 /* size */, object_2));
|
||||
|
||||
// This should be overlapped on both object_1 and object_2.
|
||||
linked_ptr<CountedObject> object_3(new CountedObject(3));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(0 /* base address */, 300 /* size */, object_3));
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The first range should be intact.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(99, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(10, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(90, retrieved_size);
|
||||
// The second range should be intact.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(199, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(100, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(100, retrieved_size);
|
||||
// The third range should be truncated.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(9, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(3, object->id());
|
||||
EXPECT_EQ(0, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(10, retrieved_size);
|
||||
}
|
||||
|
||||
// Adding two ranges without overlap should succeed and the ranges should
|
||||
// be left intact.
|
||||
TEST(RangeMapTruncateLower, NoConflicts) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateLower);
|
||||
// Adding range 1.
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(10 /* base address */, 90 /* size */, object_1));
|
||||
|
||||
// Adding range 2 - no overlap with range 1.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(
|
||||
range_map.StoreRange(110 /* base address */, 90 /* size */, object_2));
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The first range should be intact.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(99, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(10, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(90, retrieved_size);
|
||||
// The second range should be intact.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(199, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(110, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(90, retrieved_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
357
externals/breakpad/src/processor/range_map_truncate_upper_unittest.cc
vendored
Normal file
357
externals/breakpad/src/processor/range_map_truncate_upper_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
// Copyright 2016 Google LLC
|
||||
//
|
||||
// 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 LLC 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_shrink_down_unittest.cc: Unit tests for RangeMap that specifically
|
||||
// test shrink down when ranges overlap.
|
||||
//
|
||||
// Author: Ivan Penkov
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "processor/range_map-inl.h"
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::linked_ptr;
|
||||
using google_breakpad::MergeRangeStrategy;
|
||||
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;
|
||||
|
||||
// Same range cannot be stored wice.
|
||||
TEST(RangeMapTruncateUpper, SameRange) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateUpper);
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(range_map.StoreRange(0 /* base address */, 100 /* size */,
|
||||
object_1));
|
||||
|
||||
// Same range cannot be stored wice.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_FALSE(range_map.StoreRange(0 /* base address */, 100 /* size */,
|
||||
object_2));
|
||||
}
|
||||
|
||||
// If a range is completely contained by another range, then the larger range
|
||||
// should be shrinked down.
|
||||
TEST(RangeMapTruncateUpper, CompletelyContained) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateUpper);
|
||||
// Larger range is added first.
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(range_map.StoreRange(0 /* base address */, 100 /* size */,
|
||||
object_1));
|
||||
// Smaller (contained) range is added second.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(range_map.StoreRange(10 /* base address */, 80 /* size */,
|
||||
object_2));
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The first range contains the second, so the first range should have been
|
||||
// shrunk to [90, 99]. Range [0, 9] should be free.
|
||||
EXPECT_FALSE(range_map.RetrieveRange(0, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_FALSE(range_map.RetrieveRange(9, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_TRUE(range_map.RetrieveRange(90, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(90, retrieved_base);
|
||||
EXPECT_EQ(90, retrieved_delta);
|
||||
EXPECT_EQ(10, retrieved_size);
|
||||
// Validate the properties of the smaller range (should be untouched).
|
||||
EXPECT_TRUE(range_map.RetrieveRange(10, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(10, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(80, retrieved_size);
|
||||
}
|
||||
|
||||
// Same as the previous test, however the larger range is added second.
|
||||
TEST(RangeMapTruncateUpper, CompletelyContained_LargerAddedSecond) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateUpper);
|
||||
// Smaller (contained) range is added first.
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(range_map.StoreRange(10 /* base address */, 80 /* size */,
|
||||
object_1));
|
||||
// Larger range is added second.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(range_map.StoreRange(0 /* base address */, 100 /* size */,
|
||||
object_2));
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The second range contains the first, so the second range should have been
|
||||
// shrunk to [90, 99]. Range [0, 9] should be free.
|
||||
EXPECT_FALSE(range_map.RetrieveRange(0, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_FALSE(range_map.RetrieveRange(9, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_TRUE(range_map.RetrieveRange(90, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(90, retrieved_base);
|
||||
EXPECT_EQ(90, retrieved_delta);
|
||||
EXPECT_EQ(10, retrieved_size);
|
||||
// Validate the properties of the smaller range (should be untouched).
|
||||
EXPECT_TRUE(range_map.RetrieveRange(10, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(10, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(80, retrieved_size);
|
||||
}
|
||||
|
||||
TEST(RangeMapTruncateUpper, PartialOverlap_AtBeginning) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateUpper);
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(range_map.StoreRange(0 /* base address */, 100 /* size */,
|
||||
object_1));
|
||||
|
||||
// Partial overlap at the beginning of the new range.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(range_map.StoreRange(90 /* base address */, 110 /* size */,
|
||||
object_2));
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The second range is supposed to be shrunk down so the following address
|
||||
// should resize in the first range.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(99, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(0, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(100, retrieved_size);
|
||||
// Validate the properties of the shrunk down range.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(100, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(100, retrieved_base);
|
||||
EXPECT_EQ(10, retrieved_delta);
|
||||
EXPECT_EQ(100, retrieved_size);
|
||||
}
|
||||
|
||||
TEST(RangeMapTruncateUpper, PartialOverlap_AtEnd) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateUpper);
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(range_map.StoreRange(50 /* base address */, 50 /* size */,
|
||||
object_1));
|
||||
|
||||
// Partial overlap at the end of the new range.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(range_map.StoreRange(0 /* base address */, 70 /* size */,
|
||||
object_2));
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The first range is supposed to be shrunk down so the following address
|
||||
// should resize in the first range.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(69, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(0, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(70, retrieved_size);
|
||||
// Validate the properties of the shrunk down range.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(70, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(70, retrieved_base);
|
||||
EXPECT_EQ(20, retrieved_delta);
|
||||
EXPECT_EQ(30, retrieved_size);
|
||||
}
|
||||
|
||||
// A new range is overlapped at both ends. The new range and the range
|
||||
// that overlaps at the end should be shrink. The range that overlaps at the
|
||||
// beginning should be left untouched.
|
||||
TEST(RangeMapTruncateUpper, OverlapAtBothEnds) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateUpper);
|
||||
// This should overlap object_3 at the beginning.
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(range_map.StoreRange(0 /* base address */, 100 /* size */,
|
||||
object_1));
|
||||
|
||||
// This should overlap object_3 at the end.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(range_map.StoreRange(100 /* base address */, 100 /* size */,
|
||||
object_2));
|
||||
|
||||
// This should be overlapped on both ends by object_1 and object_2.
|
||||
linked_ptr<CountedObject> object_3(new CountedObject(3));
|
||||
EXPECT_TRUE(range_map.StoreRange(50 /* base address */, 100 /* size */,
|
||||
object_3));
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The first range should be intact.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(0, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(0, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(100, retrieved_size);
|
||||
// The second range should be shrunk down by 50.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(150, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(150, retrieved_base);
|
||||
EXPECT_EQ(50, retrieved_delta);
|
||||
EXPECT_EQ(50, retrieved_size);
|
||||
// The third range (in the middle) should be shrunk down by 50.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(100, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(3, object->id());
|
||||
EXPECT_EQ(100, retrieved_base);
|
||||
EXPECT_EQ(50, retrieved_delta);
|
||||
EXPECT_EQ(50, retrieved_size);
|
||||
}
|
||||
|
||||
TEST(RangeMapTruncateUpper, MultipleConflicts) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateUpper);
|
||||
// This should overlap with object_3.
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(range_map.StoreRange(10 /* base address */, 90 /* size */,
|
||||
object_1));
|
||||
|
||||
// This should also overlap with object_3 but after object_1.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(range_map.StoreRange(100 /* base address */, 100 /* size */,
|
||||
object_2));
|
||||
|
||||
// This should be overlapped on both object_1 and object_2. Since
|
||||
// object_3 ends with the higher address it must be shrunk.
|
||||
linked_ptr<CountedObject> object_3(new CountedObject(3));
|
||||
EXPECT_TRUE(range_map.StoreRange(0 /* base address */, 300 /* size */,
|
||||
object_3));
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The first range should be intact.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(99, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(10, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(90, retrieved_size);
|
||||
// The second range should be intact.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(199, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(100, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(100, retrieved_size);
|
||||
// The third range should be shrunk down by 200.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(299, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(3, object->id());
|
||||
EXPECT_EQ(200, retrieved_base);
|
||||
EXPECT_EQ(200, retrieved_delta);
|
||||
EXPECT_EQ(100, retrieved_size);
|
||||
}
|
||||
|
||||
// Adding two ranges without overlap should succeed and the ranges should
|
||||
// be left intact.
|
||||
TEST(RangeMapTruncateUpper, NoConflicts) {
|
||||
TestMap range_map;
|
||||
range_map.SetMergeStrategy(MergeRangeStrategy::kTruncateUpper);
|
||||
// Adding range 1.
|
||||
linked_ptr<CountedObject> object_1(new CountedObject(1));
|
||||
EXPECT_TRUE(range_map.StoreRange(10 /* base address */, 90 /* size */,
|
||||
object_1));
|
||||
|
||||
// Adding range 2 - no overlap with range 1.
|
||||
linked_ptr<CountedObject> object_2(new CountedObject(2));
|
||||
EXPECT_TRUE(range_map.StoreRange(110 /* base address */, 90 /* size */,
|
||||
object_2));
|
||||
|
||||
linked_ptr<CountedObject> object;
|
||||
AddressType retrieved_base = AddressType();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
AddressType retrieved_size = AddressType();
|
||||
// The first range should be intact.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(99, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(1, object->id());
|
||||
EXPECT_EQ(10, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(90, retrieved_size);
|
||||
// The second range should be intact.
|
||||
EXPECT_TRUE(range_map.RetrieveRange(199, &object, &retrieved_base,
|
||||
&retrieved_delta, &retrieved_size));
|
||||
EXPECT_EQ(2, object->id());
|
||||
EXPECT_EQ(110, retrieved_base);
|
||||
EXPECT_EQ(0, retrieved_delta);
|
||||
EXPECT_EQ(90, retrieved_size);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
561
externals/breakpad/src/processor/range_map_unittest.cc
vendored
Normal file
561
externals/breakpad/src/processor/range_map_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,561 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "processor/range_map-inl.h"
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::AddIgnoringOverflow;
|
||||
using google_breakpad::linked_ptr;
|
||||
using google_breakpad::RangeMap;
|
||||
using google_breakpad::scoped_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 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 = AddIgnoringOverflow(
|
||||
offset, (!side ? range_test->address
|
||||
: AddIgnoringOverflow(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();
|
||||
AddressType retrieved_delta = AddressType();
|
||||
bool retrieved = range_map->RetrieveRange(address, &object,
|
||||
&retrieved_base,
|
||||
&retrieved_delta,
|
||||
&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_delta = AddressType();
|
||||
AddressType nearest_size = AddressType();
|
||||
bool retrieved_nearest = range_map->RetrieveNearestRange(address,
|
||||
&nearest_object,
|
||||
&nearest_base,
|
||||
&nearest_delta,
|
||||
&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 /* delta */, NULL /* size */)) {
|
||||
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 /* base */,
|
||||
NULL /* delta */, NULL /* size */)) {
|
||||
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 /* delta */, NULL /* size */)) {
|
||||
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;
|
||||
}
|
||||
371
externals/breakpad/src/processor/simple_serializer-inl.h
vendored
Normal file
371
externals/breakpad/src/processor/simple_serializer-inl.h
vendored
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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 "processor/simple_serializer.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "processor/basic_source_line_resolver_types.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/map_serializers-inl.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;
|
||||
}
|
||||
|
||||
static const char* Read(const char* source, bool* value) {
|
||||
*value = ((*source) == 0 ? false : true);
|
||||
return ++source;
|
||||
}
|
||||
};
|
||||
|
||||
// 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: InlineOrigin
|
||||
template <>
|
||||
class SimpleSerializer<BasicSourceLineResolver::InlineOrigin> {
|
||||
typedef BasicSourceLineResolver::InlineOrigin InlineOrigin;
|
||||
|
||||
public:
|
||||
static size_t SizeOf(const InlineOrigin& origin) {
|
||||
return SimpleSerializer<bool>::SizeOf(origin.has_file_id) +
|
||||
SimpleSerializer<int32_t>::SizeOf(origin.source_file_id) +
|
||||
SimpleSerializer<string>::SizeOf(origin.name);
|
||||
}
|
||||
static char* Write(const InlineOrigin& origin, char* dest) {
|
||||
dest = SimpleSerializer<bool>::Write(origin.has_file_id, dest);
|
||||
dest = SimpleSerializer<int32_t>::Write(origin.source_file_id, dest);
|
||||
dest = SimpleSerializer<string>::Write(origin.name, 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)
|
||||
+ SimpleSerializer<bool>::SizeOf(pubsymbol.is_multiple);
|
||||
}
|
||||
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);
|
||||
dest = SimpleSerializer<bool>::Write(pubsymbol.is_multiple, dest);
|
||||
return dest;
|
||||
}
|
||||
};
|
||||
|
||||
// Specializations of SimpleSerializer: WindowsFrameInfo
|
||||
template<>
|
||||
class SimpleSerializer<WindowsFrameInfo> {
|
||||
public:
|
||||
static size_t SizeOf(const WindowsFrameInfo& wfi) {
|
||||
unsigned int size = 0;
|
||||
size += sizeof(int32_t); // wfi.type_
|
||||
size += SimpleSerializer<int32_t>::SizeOf(wfi.valid);
|
||||
size += SimpleSerializer<uint32_t>::SizeOf(wfi.prolog_size);
|
||||
size += SimpleSerializer<uint32_t>::SizeOf(wfi.epilog_size);
|
||||
size += SimpleSerializer<uint32_t>::SizeOf(wfi.parameter_size);
|
||||
size += SimpleSerializer<uint32_t>::SizeOf(wfi.saved_register_size);
|
||||
size += SimpleSerializer<uint32_t>::SizeOf(wfi.local_size);
|
||||
size += SimpleSerializer<uint32_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(
|
||||
static_cast<const int32_t>(wfi.type_), dest);
|
||||
dest = SimpleSerializer<int32_t>::Write(wfi.valid, dest);
|
||||
dest = SimpleSerializer<uint32_t>::Write(wfi.prolog_size, dest);
|
||||
dest = SimpleSerializer<uint32_t>::Write(wfi.epilog_size, dest);
|
||||
dest = SimpleSerializer<uint32_t>::Write(wfi.parameter_size, dest);
|
||||
dest = SimpleSerializer<uint32_t>::Write(wfi.saved_register_size, dest);
|
||||
dest = SimpleSerializer<uint32_t>::Write(wfi.local_size, dest);
|
||||
dest = SimpleSerializer<uint32_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, InlineOrigin, Inline, 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<linked_ptr<BasicSourceLineResolver::InlineOrigin>> {
|
||||
typedef BasicSourceLineResolver::InlineOrigin InlineOrigin;
|
||||
|
||||
public:
|
||||
static size_t SizeOf(const linked_ptr<InlineOrigin>& origin_ptr) {
|
||||
if (origin_ptr.get() == NULL)
|
||||
return 0;
|
||||
return SimpleSerializer<InlineOrigin>::SizeOf(*(origin_ptr.get()));
|
||||
}
|
||||
static char* Write(const linked_ptr<InlineOrigin>& origin_ptr, char* dest) {
|
||||
if (origin_ptr.get())
|
||||
dest = SimpleSerializer<InlineOrigin>::Write(*(origin_ptr.get()), dest);
|
||||
return dest;
|
||||
}
|
||||
};
|
||||
|
||||
// Specializations of SimpleSerializer: Inline
|
||||
template <>
|
||||
class SimpleSerializer<linked_ptr<BasicSourceLineResolver::Inline>>;
|
||||
template <>
|
||||
class SimpleSerializer<BasicSourceLineResolver::Inline> {
|
||||
typedef BasicSourceLineResolver::Inline Inline;
|
||||
|
||||
public:
|
||||
inline static size_t SizeOf(const Inline& in);
|
||||
inline static char* Write(const Inline& in, char* dest);
|
||||
};
|
||||
|
||||
template <>
|
||||
class SimpleSerializer<linked_ptr<BasicSourceLineResolver::Inline>> {
|
||||
typedef BasicSourceLineResolver::Inline Inline;
|
||||
|
||||
public:
|
||||
static size_t SizeOf(const linked_ptr<Inline>& inline_ptr) {
|
||||
if (inline_ptr.get() == NULL)
|
||||
return 0;
|
||||
return SimpleSerializer<Inline>::SizeOf(*(inline_ptr.get()));
|
||||
}
|
||||
static char* Write(const linked_ptr<Inline>& inline_ptr, char* dest) {
|
||||
if (inline_ptr.get())
|
||||
dest = SimpleSerializer<Inline>::Write(*(inline_ptr.get()), dest);
|
||||
return dest;
|
||||
}
|
||||
};
|
||||
|
||||
size_t SimpleSerializer<BasicSourceLineResolver::Inline>::SizeOf(
|
||||
const Inline& in) {
|
||||
return SimpleSerializer<bool>::SizeOf(in.has_call_site_file_id) +
|
||||
SimpleSerializer<int32_t>::SizeOf(in.inline_nest_level) +
|
||||
SimpleSerializer<int32_t>::SizeOf(in.call_site_line) +
|
||||
SimpleSerializer<int32_t>::SizeOf(in.call_site_file_id) +
|
||||
SimpleSerializer<int32_t>::SizeOf(in.origin_id) +
|
||||
sizeof(uint32_t) + // This is to store the size of inline_ranges.
|
||||
(in.inline_ranges.size() * sizeof(MemAddr) * 2);
|
||||
}
|
||||
|
||||
char* SimpleSerializer<BasicSourceLineResolver::Inline>::Write(const Inline& in,
|
||||
char* dest) {
|
||||
dest = SimpleSerializer<bool>::Write(in.has_call_site_file_id, dest);
|
||||
dest = SimpleSerializer<int32_t>::Write(in.inline_nest_level, dest);
|
||||
dest = SimpleSerializer<int32_t>::Write(in.call_site_line, dest);
|
||||
dest = SimpleSerializer<int32_t>::Write(in.call_site_file_id, dest);
|
||||
dest = SimpleSerializer<int32_t>::Write(in.origin_id, dest);
|
||||
// Write the size of inline_ranges.
|
||||
dest = SimpleSerializer<int32_t>::Write(in.inline_ranges.size(), dest);
|
||||
for (const std::pair<MemAddr, MemAddr>& range : in.inline_ranges) {
|
||||
dest = SimpleSerializer<MemAddr>::Write(range.first, dest);
|
||||
dest = SimpleSerializer<MemAddr>::Write(range.second, dest);
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
template<>
|
||||
class SimpleSerializer<BasicSourceLineResolver::Function> {
|
||||
// Convenient type names.
|
||||
typedef BasicSourceLineResolver::Function Function;
|
||||
typedef BasicSourceLineResolver::Line Line;
|
||||
typedef BasicSourceLineResolver::Inline Inline;
|
||||
|
||||
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 += SimpleSerializer<bool>::SizeOf(func.is_multiple);
|
||||
// This extra size is used to store the size of serialized func.inlines, so
|
||||
// we know where to start de-serialize func.lines.
|
||||
size += sizeof(int32_t);
|
||||
size += inline_range_map_serializer_.SizeOf(&func.inlines);
|
||||
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 = SimpleSerializer<bool>::Write(func.is_multiple, dest);
|
||||
char* old_dest = dest;
|
||||
dest += sizeof(int32_t);
|
||||
dest = inline_range_map_serializer_.Write(&func.inlines, dest);
|
||||
// Write the size of serialized func.inlines. The size doesn't include size
|
||||
// field itself.
|
||||
SimpleSerializer<MemAddr>::Write(dest - old_dest - sizeof(int32_t),
|
||||
old_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_;
|
||||
static ContainedRangeMapSerializer<MemAddr, linked_ptr<Inline>>
|
||||
inline_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__
|
||||
64
externals/breakpad/src/processor/simple_serializer.h
vendored
Normal file
64
externals/breakpad/src/processor/simple_serializer.h
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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 <stddef.h>
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
typedef uint64_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__
|
||||
207
externals/breakpad/src/processor/simple_symbol_supplier.cc
vendored
Normal file
207
externals/breakpad/src/processor/simple_symbol_supplier.cc
vendored
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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 "common/using_std_string.h"
|
||||
#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, string::traits_type::to_char_type(
|
||||
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,
|
||||
size_t* symbol_data_size) {
|
||||
assert(symbol_data);
|
||||
assert(symbol_data_size);
|
||||
|
||||
string symbol_data_string;
|
||||
SymbolSupplier::SymbolResult s =
|
||||
GetSymbolFile(module, system_info, symbol_file, &symbol_data_string);
|
||||
|
||||
if (s == FOUND) {
|
||||
*symbol_data_size = symbol_data_string.size() + 1;
|
||||
*symbol_data = new char[*symbol_data_size];
|
||||
if (*symbol_data == NULL) {
|
||||
BPLOG(ERROR) << "Memory allocation for size " << *symbol_data_size
|
||||
<< " failed";
|
||||
return INTERRUPT;
|
||||
}
|
||||
memcpy(*symbol_data, symbol_data_string.c_str(), symbol_data_string.size());
|
||||
(*symbol_data)[symbol_data_string.size()] = '\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
|
||||
139
externals/breakpad/src/processor/simple_symbol_supplier.h
vendored
Normal file
139
externals/breakpad/src/processor/simple_symbol_supplier.h
vendored
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright 2006 Google LLC
|
||||
//
|
||||
// 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 LLC 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 "common/using_std_string.h"
|
||||
#include "google_breakpad/processor/symbol_supplier.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::map;
|
||||
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,
|
||||
size_t* symbol_data_size);
|
||||
|
||||
// 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__
|
||||
351
externals/breakpad/src/processor/source_line_resolver_base.cc
vendored
Normal file
351
externals/breakpad/src/processor/source_line_resolver_base.cc
vendored
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#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/logging.h"
|
||||
#include "processor/module_factory.h"
|
||||
#include "processor/source_line_resolver_base_types.h"
|
||||
|
||||
using std::make_pair;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
SourceLineResolverBase::SourceLineResolverBase(
|
||||
ModuleFactory* module_factory)
|
||||
: modules_(new ModuleMap),
|
||||
corrupt_modules_(new ModuleSet),
|
||||
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_;
|
||||
modules_ = NULL;
|
||||
|
||||
// Delete the set of corrupt modules.
|
||||
delete corrupt_modules_;
|
||||
corrupt_modules_ = NULL;
|
||||
|
||||
MemoryMap::iterator iter = memory_buffers_->begin();
|
||||
for (; iter != memory_buffers_->end(); ++iter) {
|
||||
delete [] iter->second;
|
||||
}
|
||||
// Delete the map of memory buffers.
|
||||
delete memory_buffers_;
|
||||
memory_buffers_ = NULL;
|
||||
|
||||
delete module_factory_;
|
||||
module_factory_ = NULL;
|
||||
}
|
||||
|
||||
bool SourceLineResolverBase::ReadSymbolFile(const string& map_file,
|
||||
char** symbol_data,
|
||||
size_t* symbol_data_size) {
|
||||
if (symbol_data == NULL || symbol_data_size == 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;
|
||||
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_size = file_size + 1;
|
||||
*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;
|
||||
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;
|
||||
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;
|
||||
size_t memory_buffer_size;
|
||||
if (!ReadSymbolFile(map_file, &memory_buffer, &memory_buffer_size))
|
||||
return false;
|
||||
|
||||
BPLOG(INFO) << "Read symbol file " << map_file << " succeeded. "
|
||||
<< "module = " << module->code_file()
|
||||
<< ", memory_buffer_size = " << memory_buffer_size;
|
||||
|
||||
bool load_result = LoadModuleUsingMemoryBuffer(module, memory_buffer,
|
||||
memory_buffer_size);
|
||||
|
||||
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) {
|
||||
BPLOG(INFO) << "SourceLineResolverBase::LoadModuleUsingMapBuffer(module = "
|
||||
<< module->code_file()
|
||||
<< ", map_buffer.size() = " << map_buffer.size() << ")";
|
||||
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;
|
||||
}
|
||||
|
||||
size_t memory_buffer_size = map_buffer.size() + 1;
|
||||
char* memory_buffer = new char[memory_buffer_size];
|
||||
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,
|
||||
memory_buffer_size);
|
||||
|
||||
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,
|
||||
size_t memory_buffer_size) {
|
||||
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, size: " << memory_buffer_size;
|
||||
|
||||
Module* basic_module = module_factory_->CreateModule(module->code_file());
|
||||
|
||||
// Ownership of memory is NOT transfered to Module::LoadMapFromMemory().
|
||||
if (!basic_module->LoadMapFromMemory(memory_buffer, memory_buffer_size)) {
|
||||
BPLOG(ERROR) << "Too many error while parsing symbol data for module "
|
||||
<< module->code_file();
|
||||
// Returning false from here would be an indication that the symbols for
|
||||
// this module are missing which would be wrong. Intentionally fall through
|
||||
// and add the module to both the modules_ and the corrupt_modules_ lists.
|
||||
assert(basic_module->IsCorrupt());
|
||||
}
|
||||
|
||||
modules_->insert(make_pair(module->code_file(), basic_module));
|
||||
if (basic_module->IsCorrupt()) {
|
||||
corrupt_modules_->insert(module->code_file());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SourceLineResolverBase::ShouldDeleteMemoryBufferAfterLoadModule() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void SourceLineResolverBase::UnloadModule(const CodeModule* code_module) {
|
||||
if (!code_module)
|
||||
return;
|
||||
|
||||
ModuleMap::iterator mod_iter = modules_->find(code_module->code_file());
|
||||
if (mod_iter != modules_->end()) {
|
||||
Module* symbol_module = mod_iter->second;
|
||||
delete symbol_module;
|
||||
corrupt_modules_->erase(mod_iter->first);
|
||||
modules_->erase(mod_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();
|
||||
}
|
||||
|
||||
bool SourceLineResolverBase::IsModuleCorrupt(const CodeModule* module) {
|
||||
if (!module)
|
||||
return false;
|
||||
return corrupt_modules_->find(module->code_file()) != corrupt_modules_->end();
|
||||
}
|
||||
|
||||
void SourceLineResolverBase::FillSourceLineInfo(
|
||||
StackFrame* frame,
|
||||
std::deque<std::unique_ptr<StackFrame>>* inlined_frames) {
|
||||
if (frame->module) {
|
||||
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
|
||||
if (it != modules_->end()) {
|
||||
it->second->LookupAddress(frame, inlined_frames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
209
externals/breakpad/src/processor/source_line_resolver_base_types.h
vendored
Normal file
209
externals/breakpad/src/processor/source_line_resolver_base_types.h
vendored
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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 <deque>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_base.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/range_map.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::InlineOrigin {
|
||||
InlineOrigin() {}
|
||||
InlineOrigin(bool has_file_id, int32_t source_file_id, const string& name)
|
||||
: has_file_id(has_file_id),
|
||||
source_file_id(source_file_id),
|
||||
name(name) {}
|
||||
// If it's old format, source file id is set, otherwise not useful.
|
||||
bool has_file_id;
|
||||
int32_t source_file_id;
|
||||
string name;
|
||||
};
|
||||
|
||||
struct SourceLineResolverBase::Inline {
|
||||
// A vector of (address, size) pair for a INLINE record.
|
||||
using InlineRanges = std::vector<std::pair<MemAddr, MemAddr>>;
|
||||
Inline() {}
|
||||
Inline(bool has_call_site_file_id,
|
||||
int32_t inline_nest_level,
|
||||
int32_t call_site_line,
|
||||
int32_t call_site_file_id,
|
||||
int32_t origin_id,
|
||||
InlineRanges inline_ranges)
|
||||
: has_call_site_file_id(has_call_site_file_id),
|
||||
inline_nest_level(inline_nest_level),
|
||||
call_site_line(call_site_line),
|
||||
call_site_file_id(call_site_file_id),
|
||||
origin_id(origin_id),
|
||||
inline_ranges(inline_ranges) {}
|
||||
// If it's new format, call site file id is set, otherwise not useful.
|
||||
bool has_call_site_file_id;
|
||||
int32_t inline_nest_level;
|
||||
int32_t call_site_line;
|
||||
int32_t call_site_file_id;
|
||||
int32_t origin_id;
|
||||
InlineRanges inline_ranges;
|
||||
};
|
||||
|
||||
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,
|
||||
bool is_multiple)
|
||||
: name(function_name), address(function_address), size(code_size),
|
||||
parameter_size(set_parameter_size), is_multiple(is_multiple) { }
|
||||
|
||||
string name;
|
||||
MemAddr address;
|
||||
MemAddr size;
|
||||
|
||||
// The size of parameters passed to this function on the stack.
|
||||
int32_t parameter_size;
|
||||
|
||||
// If the function's instructions correspond to multiple symbols.
|
||||
bool is_multiple;
|
||||
};
|
||||
|
||||
struct SourceLineResolverBase::PublicSymbol {
|
||||
PublicSymbol() { }
|
||||
PublicSymbol(const string& set_name,
|
||||
MemAddr set_address,
|
||||
int set_parameter_size,
|
||||
bool is_multiple)
|
||||
: name(set_name),
|
||||
address(set_address),
|
||||
parameter_size(set_parameter_size),
|
||||
is_multiple(is_multiple) {}
|
||||
|
||||
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;
|
||||
|
||||
// If the function's instructions correspond to multiple symbols.
|
||||
bool is_multiple;
|
||||
};
|
||||
|
||||
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).
|
||||
// The passed in |memory buffer| is of size |memory_buffer_size|. If it is
|
||||
// not null terminated, LoadMapFromMemory will null terminate it by modifying
|
||||
// the passed in buffer.
|
||||
virtual bool LoadMapFromMemory(char* memory_buffer,
|
||||
size_t memory_buffer_size) = 0;
|
||||
|
||||
// Tells whether the loaded symbol data is corrupt. Return value is
|
||||
// undefined, if the symbol data hasn't been loaded yet.
|
||||
virtual bool IsCorrupt() const = 0;
|
||||
|
||||
// Looks up the given relative address, and fills the StackFrame struct
|
||||
// with the result.
|
||||
virtual void LookupAddress(
|
||||
StackFrame* frame,
|
||||
std::deque<std::unique_ptr<StackFrame>>* inlined_frames) 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__
|
||||
82
externals/breakpad/src/processor/stack_frame_cpu.cc
vendored
Normal file
82
externals/breakpad/src/processor/stack_frame_cpu.cc
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2013 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
// stack_frame_cpu.h: CPU-specific StackFrame extensions.
|
||||
//
|
||||
// See google_breakpad/processor/stack_frame_cpu.h for documentation.
|
||||
//
|
||||
// Author: Colin Blundell
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X0;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X1;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X2;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X3;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X4;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X5;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X6;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X7;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X8;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X9;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X10;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X11;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X12;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X13;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X14;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X15;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X16;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X17;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X18;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X19;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X20;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X21;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X22;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X23;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X24;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X25;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X26;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X27;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X28;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X29;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X30;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X31;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X32;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_FP;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_LR;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_SP;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_PC;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_ALL;
|
||||
|
||||
} // namespace google_breakpad
|
||||
149
externals/breakpad/src/processor/stack_frame_symbolizer.cc
vendored
Normal file
149
externals/breakpad/src/processor/stack_frame_symbolizer.cc
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
// Copyright 2012 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
// Implementation of StackFrameSymbolizer, which encapsulates the logic of how
|
||||
// SourceLineResolverInterface interacts with SymbolSupplier to fill source
|
||||
// line information in a stack frame, and also looks up WindowsFrameInfo or
|
||||
// CFIFrameInfo for a stack frame.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "google_breakpad/processor/stack_frame_symbolizer.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "google_breakpad/processor/code_modules.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 "google_breakpad/processor/system_info.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
StackFrameSymbolizer::StackFrameSymbolizer(
|
||||
SymbolSupplier* supplier,
|
||||
SourceLineResolverInterface* resolver) : supplier_(supplier),
|
||||
resolver_(resolver) { }
|
||||
|
||||
StackFrameSymbolizer::SymbolizerResult StackFrameSymbolizer::FillSourceLineInfo(
|
||||
const CodeModules* modules,
|
||||
const CodeModules* unloaded_modules,
|
||||
const SystemInfo* system_info,
|
||||
StackFrame* frame,
|
||||
std::deque<std::unique_ptr<StackFrame>>* inlined_frames) {
|
||||
assert(frame);
|
||||
|
||||
const CodeModule* module = NULL;
|
||||
if (modules) {
|
||||
module = modules->GetModuleForAddress(frame->instruction);
|
||||
}
|
||||
if (!module && unloaded_modules) {
|
||||
module = unloaded_modules->GetModuleForAddress(frame->instruction);
|
||||
}
|
||||
|
||||
if (!module) return kError;
|
||||
frame->module = module;
|
||||
|
||||
if (!resolver_) return kError; // no resolver.
|
||||
// If module is known to have missing symbol file, return.
|
||||
if (no_symbol_modules_.find(module->code_file()) !=
|
||||
no_symbol_modules_.end()) {
|
||||
return kError;
|
||||
}
|
||||
|
||||
// If module is already loaded, go ahead to fill source line info and return.
|
||||
if (resolver_->HasModule(frame->module)) {
|
||||
resolver_->FillSourceLineInfo(frame, inlined_frames);
|
||||
return resolver_->IsModuleCorrupt(frame->module) ?
|
||||
kWarningCorruptSymbols : kNoError;
|
||||
}
|
||||
|
||||
// Module needs to fetch symbol file. First check to see if supplier exists.
|
||||
if (!supplier_) {
|
||||
return kError;
|
||||
}
|
||||
|
||||
// Start fetching symbol from supplier.
|
||||
string symbol_file;
|
||||
char* symbol_data = NULL;
|
||||
size_t symbol_data_size;
|
||||
SymbolSupplier::SymbolResult symbol_result = supplier_->GetCStringSymbolData(
|
||||
module, system_info, &symbol_file, &symbol_data, &symbol_data_size);
|
||||
|
||||
switch (symbol_result) {
|
||||
case SymbolSupplier::FOUND: {
|
||||
bool load_success = resolver_->LoadModuleUsingMemoryBuffer(
|
||||
frame->module,
|
||||
symbol_data,
|
||||
symbol_data_size);
|
||||
if (resolver_->ShouldDeleteMemoryBufferAfterLoadModule()) {
|
||||
supplier_->FreeSymbolData(module);
|
||||
}
|
||||
|
||||
if (load_success) {
|
||||
resolver_->FillSourceLineInfo(frame, inlined_frames);
|
||||
return resolver_->IsModuleCorrupt(frame->module) ?
|
||||
kWarningCorruptSymbols : kNoError;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Failed to load symbol file in resolver.";
|
||||
no_symbol_modules_.insert(module->code_file());
|
||||
return kError;
|
||||
}
|
||||
}
|
||||
|
||||
case SymbolSupplier::NOT_FOUND:
|
||||
no_symbol_modules_.insert(module->code_file());
|
||||
return kError;
|
||||
|
||||
case SymbolSupplier::INTERRUPT:
|
||||
return kInterrupt;
|
||||
|
||||
default:
|
||||
BPLOG(ERROR) << "Unknown SymbolResult enum: " << symbol_result;
|
||||
return kError;
|
||||
}
|
||||
return kError;
|
||||
}
|
||||
|
||||
WindowsFrameInfo* StackFrameSymbolizer::FindWindowsFrameInfo(
|
||||
const StackFrame* frame) {
|
||||
return resolver_ ? resolver_->FindWindowsFrameInfo(frame) : NULL;
|
||||
}
|
||||
|
||||
CFIFrameInfo* StackFrameSymbolizer::FindCFIFrameInfo(
|
||||
const StackFrame* frame) {
|
||||
return resolver_ ? resolver_->FindCFIFrameInfo(frame) : NULL;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
1304
externals/breakpad/src/processor/stackwalk_common.cc
vendored
Normal file
1304
externals/breakpad/src/processor/stackwalk_common.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
50
externals/breakpad/src/processor/stackwalk_common.h
vendored
Normal file
50
externals/breakpad/src/processor/stackwalk_common.h
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
// stackwalk_common.cc: Module shared by the {micro,mini}dump_stackwalck
|
||||
// executables to print the content of dumps (w/ stack traces) on the console.
|
||||
|
||||
|
||||
#ifndef PROCESSOR_STACKWALK_COMMON_H__
|
||||
#define PROCESSOR_STACKWALK_COMMON_H__
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ProcessState;
|
||||
class SourceLineResolverInterface;
|
||||
|
||||
void PrintProcessStateMachineReadable(const ProcessState& process_state);
|
||||
void PrintProcessState(const ProcessState& process_state,
|
||||
bool output_stack_contents,
|
||||
bool output_requesting_thread_only,
|
||||
SourceLineResolverInterface* resolver);
|
||||
void PrintRequestingThreadBrief(const ProcessState& process_state);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_STACKWALK_COMMON_H__
|
||||
358
externals/breakpad/src/processor/stackwalker.cc
vendored
Normal file
358
externals/breakpad/src/processor/stackwalker.cc
vendored
Normal file
|
|
@ -0,0 +1,358 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include "google_breakpad/processor/stackwalker.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/scoped_ptr.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/dump_context.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "google_breakpad/processor/stack_frame_symbolizer.h"
|
||||
#include "google_breakpad/processor/system_info.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/stackwalker_ppc.h"
|
||||
#include "processor/stackwalker_ppc64.h"
|
||||
#include "processor/stackwalker_sparc.h"
|
||||
#include "processor/stackwalker_x86.h"
|
||||
#include "processor/stackwalker_amd64.h"
|
||||
#include "processor/stackwalker_arm.h"
|
||||
#include "processor/stackwalker_arm64.h"
|
||||
#include "processor/stackwalker_mips.h"
|
||||
#include "processor/stackwalker_riscv.h"
|
||||
#include "processor/stackwalker_riscv64.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
const int Stackwalker::kRASearchWords = 40;
|
||||
|
||||
// This default is just a sanity check: a large enough value
|
||||
// that allow capturing unbounded recursion traces, yet provide a
|
||||
// guardrail against stack walking bugs. The stack walking invariants
|
||||
// guarantee that the unwinding process is strictly monotonic and
|
||||
// practically bounded by the size of the stack memory range.
|
||||
uint32_t Stackwalker::max_frames_ = 1 << 20; // 1M
|
||||
bool Stackwalker::max_frames_set_ = false;
|
||||
|
||||
uint32_t Stackwalker::max_frames_scanned_ = 1 << 14; // 16k
|
||||
|
||||
Stackwalker::Stackwalker(const SystemInfo* system_info,
|
||||
MemoryRegion* memory,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* frame_symbolizer)
|
||||
: system_info_(system_info),
|
||||
memory_(memory),
|
||||
modules_(modules),
|
||||
unloaded_modules_(NULL),
|
||||
frame_symbolizer_(frame_symbolizer) {
|
||||
assert(frame_symbolizer_);
|
||||
}
|
||||
|
||||
void InsertSpecialAttentionModule(
|
||||
StackFrameSymbolizer::SymbolizerResult symbolizer_result,
|
||||
const CodeModule* module,
|
||||
vector<const CodeModule*>* modules) {
|
||||
if (!module) {
|
||||
return;
|
||||
}
|
||||
assert(symbolizer_result == StackFrameSymbolizer::kError ||
|
||||
symbolizer_result == StackFrameSymbolizer::kWarningCorruptSymbols);
|
||||
bool found = false;
|
||||
vector<const CodeModule*>::iterator iter;
|
||||
for (iter = modules->begin(); iter != modules->end(); ++iter) {
|
||||
if (*iter == module) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
BPLOG(INFO) << ((symbolizer_result == StackFrameSymbolizer::kError) ?
|
||||
"Couldn't load symbols for: " :
|
||||
"Detected corrupt symbols for: ")
|
||||
<< module->debug_file() << "|" << module->debug_identifier();
|
||||
modules->push_back(module);
|
||||
}
|
||||
}
|
||||
|
||||
bool Stackwalker::Walk(
|
||||
CallStack* stack,
|
||||
vector<const CodeModule*>* modules_without_symbols,
|
||||
vector<const CodeModule*>* modules_with_corrupt_symbols) {
|
||||
BPLOG_IF(ERROR, !stack) << "Stackwalker::Walk requires |stack|";
|
||||
assert(stack);
|
||||
stack->Clear();
|
||||
|
||||
BPLOG_IF(ERROR, !modules_without_symbols) << "Stackwalker::Walk requires "
|
||||
<< "|modules_without_symbols|";
|
||||
BPLOG_IF(ERROR, !modules_without_symbols) << "Stackwalker::Walk requires "
|
||||
<< "|modules_with_corrupt_symbols|";
|
||||
assert(modules_without_symbols);
|
||||
assert(modules_with_corrupt_symbols);
|
||||
|
||||
// Begin with the context frame, and keep getting callers until there are
|
||||
// no more.
|
||||
|
||||
// Keep track of the number of scanned or otherwise dubious frames seen
|
||||
// so far, as the caller may have set a limit.
|
||||
uint32_t scanned_frames = 0;
|
||||
|
||||
// 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).
|
||||
|
||||
std::deque<std::unique_ptr<StackFrame>> inlined_frames;
|
||||
// Resolve the module information, if a module map was provided.
|
||||
StackFrameSymbolizer::SymbolizerResult symbolizer_result =
|
||||
frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_,
|
||||
system_info_,
|
||||
frame.get(), &inlined_frames);
|
||||
switch (symbolizer_result) {
|
||||
case StackFrameSymbolizer::kInterrupt:
|
||||
BPLOG(INFO) << "Stack walk is interrupted.";
|
||||
return false;
|
||||
break;
|
||||
case StackFrameSymbolizer::kError:
|
||||
InsertSpecialAttentionModule(symbolizer_result, frame->module,
|
||||
modules_without_symbols);
|
||||
break;
|
||||
case StackFrameSymbolizer::kWarningCorruptSymbols:
|
||||
InsertSpecialAttentionModule(symbolizer_result, frame->module,
|
||||
modules_with_corrupt_symbols);
|
||||
break;
|
||||
case StackFrameSymbolizer::kNoError:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// Keep track of the number of dubious frames so far.
|
||||
switch (frame.get()->trust) {
|
||||
case StackFrame::FRAME_TRUST_NONE:
|
||||
case StackFrame::FRAME_TRUST_SCAN:
|
||||
case StackFrame::FRAME_TRUST_CFI_SCAN:
|
||||
scanned_frames++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// Add all nested inlined frames belonging to this frame from the innermost
|
||||
// frame to the outermost frame.
|
||||
while (!inlined_frames.empty()) {
|
||||
stack->frames_.push_back(inlined_frames.front().release());
|
||||
inlined_frames.pop_front();
|
||||
}
|
||||
// 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_) {
|
||||
// Only emit an error message in the case where the limit
|
||||
// reached is the default limit, not set by the user.
|
||||
if (!max_frames_set_)
|
||||
BPLOG(ERROR) << "The stack is over " << max_frames_ << " frames.";
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the next frame and take ownership.
|
||||
bool stack_scan_allowed = scanned_frames < max_frames_scanned_;
|
||||
frame.reset(GetCallerFrame(stack, stack_scan_allowed));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
Stackwalker* Stackwalker::StackwalkerForCPU(
|
||||
const SystemInfo* system_info,
|
||||
DumpContext* context,
|
||||
MemoryRegion* memory,
|
||||
const CodeModules* modules,
|
||||
const CodeModules* unloaded_modules,
|
||||
StackFrameSymbolizer* frame_symbolizer) {
|
||||
if (!context) {
|
||||
BPLOG(ERROR) << "Can't choose a stackwalker implementation without context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Stackwalker* cpu_stackwalker = NULL;
|
||||
|
||||
uint32_t cpu = context->GetContextCPU();
|
||||
switch (cpu) {
|
||||
case MD_CONTEXT_X86:
|
||||
cpu_stackwalker = new StackwalkerX86(system_info,
|
||||
context->GetContextX86(),
|
||||
memory, modules, frame_symbolizer);
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_PPC:
|
||||
cpu_stackwalker = new StackwalkerPPC(system_info,
|
||||
context->GetContextPPC(),
|
||||
memory, modules, frame_symbolizer);
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_PPC64:
|
||||
cpu_stackwalker = new StackwalkerPPC64(system_info,
|
||||
context->GetContextPPC64(),
|
||||
memory, modules, frame_symbolizer);
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_AMD64:
|
||||
cpu_stackwalker = new StackwalkerAMD64(system_info,
|
||||
context->GetContextAMD64(),
|
||||
memory, modules, frame_symbolizer);
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_SPARC:
|
||||
cpu_stackwalker = new StackwalkerSPARC(system_info,
|
||||
context->GetContextSPARC(),
|
||||
memory, modules, frame_symbolizer);
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_MIPS:
|
||||
case MD_CONTEXT_MIPS64:
|
||||
cpu_stackwalker = new StackwalkerMIPS(system_info,
|
||||
context->GetContextMIPS(),
|
||||
memory, modules, frame_symbolizer);
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_ARM:
|
||||
{
|
||||
int fp_register = -1;
|
||||
if (system_info->os_short == "ios")
|
||||
fp_register = MD_CONTEXT_ARM_REG_IOS_FP;
|
||||
cpu_stackwalker = new StackwalkerARM(system_info,
|
||||
context->GetContextARM(),
|
||||
fp_register, memory, modules,
|
||||
frame_symbolizer);
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_ARM64:
|
||||
cpu_stackwalker = new StackwalkerARM64(system_info,
|
||||
context->GetContextARM64(),
|
||||
memory, modules,
|
||||
frame_symbolizer);
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_RISCV:
|
||||
cpu_stackwalker = new StackwalkerRISCV(system_info,
|
||||
context->GetContextRISCV(),
|
||||
memory, modules,
|
||||
frame_symbolizer);
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_RISCV64:
|
||||
cpu_stackwalker = new StackwalkerRISCV64(system_info,
|
||||
context->GetContextRISCV64(),
|
||||
memory, modules,
|
||||
frame_symbolizer);
|
||||
break;
|
||||
}
|
||||
|
||||
BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) <<
|
||||
", can't choose a stackwalker "
|
||||
"implementation";
|
||||
if (cpu_stackwalker) {
|
||||
cpu_stackwalker->unloaded_modules_ = unloaded_modules;
|
||||
}
|
||||
return cpu_stackwalker;
|
||||
}
|
||||
|
||||
// CONSIDER: check stack alignment?
|
||||
bool Stackwalker::TerminateWalk(uint64_t caller_ip,
|
||||
uint64_t caller_sp,
|
||||
uint64_t callee_sp,
|
||||
bool first_unwind) const {
|
||||
// Treat an instruction address less than 4k as end-of-stack.
|
||||
// (using InstructionAddressSeemsValid() here is very tempting,
|
||||
// but we need to handle JITted code)
|
||||
if (caller_ip < (1 << 12)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: The stack address range is implicitly checked
|
||||
// when the stack memory is accessed.
|
||||
|
||||
// The stack pointer should monotonically increase. For first unwind
|
||||
// we allow caller_sp == callee_sp to account for architectures where
|
||||
// the return address is stored in a register (so it's possible to have
|
||||
// leaf functions which don't move the stack pointer)
|
||||
if (first_unwind ? (caller_sp < callee_sp) : (caller_sp <= callee_sp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Stackwalker::InstructionAddressSeemsValid(uint64_t address) const {
|
||||
StackFrame frame;
|
||||
frame.instruction = address;
|
||||
StackFrameSymbolizer::SymbolizerResult symbolizer_result =
|
||||
frame_symbolizer_->FillSourceLineInfo(modules_, unloaded_modules_,
|
||||
system_info_, &frame, nullptr);
|
||||
|
||||
if (!frame.module) {
|
||||
// not inside any loaded module
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!frame_symbolizer_->HasImplementation()) {
|
||||
// No valid implementation to symbolize stack frame, but the address is
|
||||
// within a known module.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (symbolizer_result != StackFrameSymbolizer::kNoError &&
|
||||
symbolizer_result != StackFrameSymbolizer::kWarningCorruptSymbols) {
|
||||
// Some error occurred during symbolization, but the address is within a
|
||||
// known module
|
||||
return true;
|
||||
}
|
||||
|
||||
return !frame.function_name.empty();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
95
externals/breakpad/src/processor/stackwalker_address_list.cc
vendored
Normal file
95
externals/breakpad/src/processor/stackwalker_address_list.cc
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2013 Google LLC
|
||||
//
|
||||
// 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 LLC 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_address_list.cc: a pseudo stack walker.
|
||||
//
|
||||
// See stackwalker_address_list.h for documentation.
|
||||
//
|
||||
// Author: Chris Hamilton <chrisha@chromium.org>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/stackwalker_address_list.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
StackwalkerAddressList::StackwalkerAddressList(
|
||||
const uint64_t* frames,
|
||||
size_t frame_count,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* frame_symbolizer)
|
||||
: Stackwalker(NULL, NULL, modules, frame_symbolizer),
|
||||
frames_(frames),
|
||||
frame_count_(frame_count) {
|
||||
assert(frames);
|
||||
assert(frame_symbolizer);
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerAddressList::GetContextFrame() {
|
||||
if (frame_count_ == 0)
|
||||
return NULL;
|
||||
|
||||
StackFrame* frame = new StackFrame();
|
||||
frame->instruction = frames_[0];
|
||||
frame->trust = StackFrame::FRAME_TRUST_PREWALKED;
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerAddressList::GetCallerFrame(const CallStack* stack,
|
||||
bool stack_scan_allowed) {
|
||||
if (!stack) {
|
||||
BPLOG(ERROR) << "Can't get caller frame without stack";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t frame_index = stack->frames()->size();
|
||||
|
||||
// There are no more frames to fetch.
|
||||
if (frame_index >= frame_count_)
|
||||
return NULL;
|
||||
|
||||
// All frames have the highest level of trust because they were
|
||||
// explicitly provided.
|
||||
StackFrame* frame = new StackFrame();
|
||||
frame->instruction = frames_[frame_index];
|
||||
frame->trust = StackFrame::FRAME_TRUST_PREWALKED;
|
||||
return frame;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
70
externals/breakpad/src/processor/stackwalker_address_list.h
vendored
Normal file
70
externals/breakpad/src/processor/stackwalker_address_list.h
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2013 Google LLC
|
||||
//
|
||||
// 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 LLC 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_address_list.h: a pseudo stackwalker.
|
||||
//
|
||||
// Doesn't actually walk a stack, rather initializes a CallStack given an
|
||||
// explicit list of already walked return addresses.
|
||||
//
|
||||
// Author: Chris Hamilton <chrisha@chromium.org>
|
||||
|
||||
#ifndef PROCESSOR_STACKWALKER_ADDRESS_LIST_H_
|
||||
#define PROCESSOR_STACKWALKER_ADDRESS_LIST_H_
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/processor/stackwalker.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class CodeModules;
|
||||
|
||||
class StackwalkerAddressList : public Stackwalker {
|
||||
public:
|
||||
// Initializes this stack walker with an explicit set of frame addresses.
|
||||
// |modules| and |frame_symbolizer| are passed directly through to the base
|
||||
// Stackwalker constructor.
|
||||
StackwalkerAddressList(const uint64_t* frames,
|
||||
size_t frame_count,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* frame_symbolizer);
|
||||
StackwalkerAddressList(const StackwalkerAddressList&) = delete;
|
||||
void operator=(const StackwalkerAddressList&) = delete;
|
||||
|
||||
private:
|
||||
// Implementation of Stackwalker.
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(const CallStack* stack,
|
||||
bool stack_scan_allowed);
|
||||
|
||||
const uint64_t* frames_;
|
||||
size_t frame_count_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_STACKWALKER_ADDRESS_LIST_H_
|
||||
201
externals/breakpad/src/processor/stackwalker_address_list_unittest.cc
vendored
Normal file
201
externals/breakpad/src/processor/stackwalker_address_list_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
// Copyright 2013 Google LLC
|
||||
//
|
||||
// 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 LLC 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_address_list_unittest.cc: Unit tests for the
|
||||
// StackwalkerAddressList class.
|
||||
//
|
||||
// Author: Chris Hamilton <chrisha@chromium.org>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/using_std_string.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/code_module.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "processor/stackwalker_unittest_utils.h"
|
||||
#include "processor/stackwalker_address_list.h"
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::CallStack;
|
||||
using google_breakpad::CodeModule;
|
||||
using google_breakpad::StackFrameSymbolizer;
|
||||
using google_breakpad::StackFrame;
|
||||
using google_breakpad::Stackwalker;
|
||||
using google_breakpad::StackwalkerAddressList;
|
||||
using std::vector;
|
||||
using testing::_;
|
||||
using testing::AnyNumber;
|
||||
using testing::DoAll;
|
||||
using testing::Return;
|
||||
using testing::SetArgumentPointee;
|
||||
|
||||
#define arraysize(f) (sizeof(f) / sizeof(*f))
|
||||
|
||||
// Addresses and sizes of a couple dummy modules.
|
||||
uint64_t kModule1Base = 0x40000000;
|
||||
uint64_t kModule1Size = 0x10000;
|
||||
uint64_t kModule2Base = 0x50000000;
|
||||
uint64_t kModule2Size = 0x10000;
|
||||
|
||||
// A handful of addresses that lie within the modules above.
|
||||
const uint64_t kDummyFrames[] = {
|
||||
0x50003000, 0x50002000, 0x50001000, 0x40002000, 0x40001000 };
|
||||
|
||||
class StackwalkerAddressListTest : public testing::Test {
|
||||
public:
|
||||
StackwalkerAddressListTest()
|
||||
: // Give the two modules reasonable standard locations and names
|
||||
// for tests to play with.
|
||||
module1(kModule1Base, kModule1Size, "module1", "version1"),
|
||||
module2(kModule2Base, kModule2Size, "module2", "version2") {
|
||||
// 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));
|
||||
|
||||
// Avoid GMOCK WARNING "Uninteresting mock function call - returning
|
||||
// directly" for FreeSymbolData().
|
||||
EXPECT_CALL(supplier, FreeSymbolData(_)).Times(AnyNumber());
|
||||
}
|
||||
|
||||
// Set the Breakpad symbol information that supplier should return for
|
||||
// MODULE to INFO.
|
||||
void SetModuleSymbols(MockCodeModule* module, const string& info) {
|
||||
size_t buffer_size;
|
||||
char* buffer = supplier.CopySymbolDataAndOwnTheCopy(info, &buffer_size);
|
||||
EXPECT_CALL(supplier, GetCStringSymbolData(module, NULL, _, _, _))
|
||||
.WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer),
|
||||
SetArgumentPointee<4>(buffer_size),
|
||||
Return(MockSymbolSupplier::FOUND)));
|
||||
}
|
||||
|
||||
void CheckCallStack(const CallStack& call_stack) {
|
||||
const std::vector<StackFrame*>* frames = call_stack.frames();
|
||||
ASSERT_EQ(arraysize(kDummyFrames), frames->size());
|
||||
for (size_t i = 0; i < arraysize(kDummyFrames); ++i) {
|
||||
ASSERT_EQ(kDummyFrames[i], frames->at(i)->instruction);
|
||||
ASSERT_EQ(StackFrame::FRAME_TRUST_PREWALKED, frames->at(i)->trust);
|
||||
}
|
||||
ASSERT_EQ(static_cast<const CodeModule*>(&module2), frames->at(0)->module);
|
||||
ASSERT_EQ(static_cast<const CodeModule*>(&module2), frames->at(1)->module);
|
||||
ASSERT_EQ(static_cast<const CodeModule*>(&module2), frames->at(2)->module);
|
||||
ASSERT_EQ(static_cast<const CodeModule*>(&module1), frames->at(3)->module);
|
||||
ASSERT_EQ(static_cast<const CodeModule*>(&module1), frames->at(4)->module);
|
||||
}
|
||||
|
||||
MockCodeModule module1;
|
||||
MockCodeModule module2;
|
||||
MockCodeModules modules;
|
||||
MockSymbolSupplier supplier;
|
||||
BasicSourceLineResolver resolver;
|
||||
};
|
||||
|
||||
TEST_F(StackwalkerAddressListTest, ScanWithoutSymbols) {
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAddressList walker(kDummyFrames, arraysize(kDummyFrames),
|
||||
&modules, &frame_symbolizer);
|
||||
|
||||
CallStack call_stack;
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
|
||||
// The stack starts in module2, so we expect that to be the first module
|
||||
// found without symbols.
|
||||
ASSERT_EQ(2U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module2", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ("module1", modules_without_symbols[1]->debug_file());
|
||||
ASSERT_EQ(0u, modules_with_corrupt_symbols.size());
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(CheckCallStack(call_stack));
|
||||
}
|
||||
|
||||
TEST_F(StackwalkerAddressListTest, ScanWithSymbols) {
|
||||
// File : FILE number(dex) name
|
||||
// Function: FUNC address(hex) size(hex) parameter_size(hex) name
|
||||
// Line : address(hex) size(hex) line(dec) filenum(dec)
|
||||
SetModuleSymbols(&module2,
|
||||
"FILE 1 module2.cc\n"
|
||||
"FUNC 3000 100 10 mod2func3\n"
|
||||
"3000 10 1 1\n"
|
||||
"FUNC 2000 200 10 mod2func2\n"
|
||||
"FUNC 1000 300 10 mod2func1\n");
|
||||
SetModuleSymbols(&module1,
|
||||
"FUNC 2000 200 10 mod1func2\n"
|
||||
"FUNC 1000 300 10 mod1func1\n");
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAddressList walker(kDummyFrames, arraysize(kDummyFrames),
|
||||
&modules, &frame_symbolizer);
|
||||
|
||||
CallStack call_stack;
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
|
||||
ASSERT_EQ(0u, modules_without_symbols.size());
|
||||
ASSERT_EQ(0u, modules_with_corrupt_symbols.size());
|
||||
|
||||
ASSERT_NO_FATAL_FAILURE(CheckCallStack(call_stack));
|
||||
|
||||
const std::vector<StackFrame*>* frames = call_stack.frames();
|
||||
|
||||
// We have full file/line information for the first function call.
|
||||
ASSERT_EQ("mod2func3", frames->at(0)->function_name);
|
||||
ASSERT_EQ(0x50003000u, frames->at(0)->function_base);
|
||||
ASSERT_EQ("module2.cc", frames->at(0)->source_file_name);
|
||||
ASSERT_EQ(1, frames->at(0)->source_line);
|
||||
ASSERT_EQ(0x50003000u, frames->at(0)->source_line_base);
|
||||
|
||||
ASSERT_EQ("mod2func2", frames->at(1)->function_name);
|
||||
ASSERT_EQ(0x50002000u, frames->at(1)->function_base);
|
||||
|
||||
ASSERT_EQ("mod2func1", frames->at(2)->function_name);
|
||||
ASSERT_EQ(0x50001000u, frames->at(2)->function_base);
|
||||
|
||||
ASSERT_EQ("mod1func2", frames->at(3)->function_name);
|
||||
ASSERT_EQ(0x40002000u, frames->at(3)->function_base);
|
||||
|
||||
ASSERT_EQ("mod1func1", frames->at(4)->function_name);
|
||||
ASSERT_EQ(0x40001000u, frames->at(4)->function_base);
|
||||
}
|
||||
380
externals/breakpad/src/processor/stackwalker_amd64.cc
vendored
Normal file
380
externals/breakpad/src/processor/stackwalker_amd64.cc
vendored
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#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 "google_breakpad/processor/system_info.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "processor/logging.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,
|
||||
StackFrameSymbolizer* resolver_helper)
|
||||
: Stackwalker(system_info, memory, modules, resolver_helper),
|
||||
context_(context),
|
||||
cfi_walker_(cfi_register_map_,
|
||||
(sizeof(cfi_register_map_) / sizeof(cfi_register_map_[0]))) {
|
||||
}
|
||||
|
||||
uint64_t StackFrameAMD64::ReturnAddress() const {
|
||||
assert(context_validity & StackFrameAMD64::CONTEXT_VALID_RIP);
|
||||
return context.rip;
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerAMD64::GetContextFrame() {
|
||||
if (!context_) {
|
||||
BPLOG(ERROR) << "Can't get context frame without context";
|
||||
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;
|
||||
|
||||
if (!frame->context.rip || !frame->context.rsp) {
|
||||
BPLOG(ERROR) << "invalid rip/rsp";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
frame->trust = StackFrame::FRAME_TRUST_CFI;
|
||||
return frame.release();
|
||||
}
|
||||
|
||||
// Returns true if `ptr` is not in x86-64 canonical form.
|
||||
// https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details
|
||||
static bool is_non_canonical(uint64_t ptr) {
|
||||
return ptr > 0x7FFFFFFFFFFF && ptr < 0xFFFF800000000000;
|
||||
}
|
||||
|
||||
StackFrameAMD64* StackwalkerAMD64::GetCallerByFramePointerRecovery(
|
||||
const vector<StackFrame*>& frames) {
|
||||
StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
|
||||
uint64_t last_rbp = last_frame->context.rbp;
|
||||
|
||||
// Assume the presence of a frame pointer. This is not mandated by the
|
||||
// AMD64 ABI, c.f. section 3.2.2 footnote 7, though it is typical for
|
||||
// compilers to still preserve the frame pointer and not treat %rbp as a
|
||||
// general purpose register.
|
||||
//
|
||||
// With this assumption, the CALL instruction pushes the return address
|
||||
// onto the stack and sets %rip to the procedure to enter. The procedure
|
||||
// then establishes the stack frame with a prologue that PUSHes the current
|
||||
// %rbp onto the stack, MOVes the current %rsp to %rbp, and then allocates
|
||||
// space for any local variables. Using this procedure linking information,
|
||||
// it is possible to locate frame information for the callee:
|
||||
//
|
||||
// %caller_rsp = *(%callee_rbp + 16)
|
||||
// %caller_rip = *(%callee_rbp + 8)
|
||||
// %caller_rbp = *(%callee_rbp)
|
||||
|
||||
// If rbp is not 8-byte aligned it can't be a frame pointer.
|
||||
if (last_rbp % 8 != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t caller_rip, caller_rbp;
|
||||
if (memory_->GetMemoryAtAddress(last_rbp + 8, &caller_rip) &&
|
||||
memory_->GetMemoryAtAddress(last_rbp, &caller_rbp)) {
|
||||
uint64_t caller_rsp = last_rbp + 16;
|
||||
|
||||
// If the recovered rip is not a canonical address it can't be
|
||||
// the return address, so rbp must not have been a frame pointer.
|
||||
if (is_non_canonical(caller_rip)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check that rbp is within the right frame
|
||||
if (caller_rsp <= last_rbp || caller_rbp < caller_rsp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Sanity check that resulting rbp is still inside stack memory.
|
||||
uint64_t unused;
|
||||
if (!memory_->GetMemoryAtAddress(caller_rbp, &unused)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFrameAMD64* frame = new StackFrameAMD64();
|
||||
frame->trust = StackFrame::FRAME_TRUST_FP;
|
||||
frame->context = last_frame->context;
|
||||
frame->context.rip = caller_rip;
|
||||
frame->context.rsp = caller_rsp;
|
||||
frame->context.rbp = caller_rbp;
|
||||
frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RSP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RBP;
|
||||
return frame;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFrameAMD64* StackwalkerAMD64::GetCallerBySimulatingReturn(
|
||||
const vector<StackFrame*>& frames) {
|
||||
assert(frames.back()->trust == StackFrame::FRAME_TRUST_CONTEXT);
|
||||
StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
|
||||
uint64_t last_rsp = last_frame->context.rsp;
|
||||
uint64_t caller_rip_address, caller_rip;
|
||||
int searchwords = 1;
|
||||
if (!ScanForReturnAddress(last_rsp, &caller_rip_address, &caller_rip,
|
||||
searchwords)) {
|
||||
// No plausible return address at the top of the stack. Unable to simulate
|
||||
// a return.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 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_LEAF;
|
||||
frame->context = last_frame->context;
|
||||
frame->context.rip = caller_rip;
|
||||
// The caller's %rsp is directly underneath the return address pushed by
|
||||
// the call.
|
||||
frame->context.rsp = caller_rip_address + 8;
|
||||
frame->context_validity = last_frame->context_validity;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrameAMD64* StackwalkerAMD64::GetCallerByStackScan(
|
||||
const vector<StackFrame*>& frames) {
|
||||
StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
|
||||
uint64_t last_rsp = last_frame->context.rsp;
|
||||
uint64_t caller_rip_address, caller_rip;
|
||||
|
||||
if (!ScanForReturnAddress(last_rsp, &caller_rip_address, &caller_rip,
|
||||
/*is_context_frame=*/last_frame->trust ==
|
||||
StackFrame::FRAME_TRUST_CONTEXT)) {
|
||||
// No plausible return address was found.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// The caller's %rsp is directly underneath the return address pushed by
|
||||
// the call.
|
||||
frame->context.rsp = caller_rip_address + 8;
|
||||
frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RSP;
|
||||
|
||||
// Other unwinders give up if they don't have an %rbp value, so see if we
|
||||
// can pass some plausible value on.
|
||||
if (last_frame->context_validity & StackFrameAMD64::CONTEXT_VALID_RBP) {
|
||||
// Functions typically push their caller's %rbp immediately upon entry,
|
||||
// and then set %rbp to point to that. So if the callee's %rbp is
|
||||
// pointing to the first word below the alleged return address, presume
|
||||
// that the caller's %rbp is saved there.
|
||||
if (caller_rip_address - 8 == last_frame->context.rbp) {
|
||||
uint64_t caller_rbp = 0;
|
||||
if (memory_->GetMemoryAtAddress(last_frame->context.rbp, &caller_rbp) &&
|
||||
caller_rbp > caller_rip_address) {
|
||||
frame->context.rbp = caller_rbp;
|
||||
frame->context_validity |= StackFrameAMD64::CONTEXT_VALID_RBP;
|
||||
}
|
||||
} else if (last_frame->context.rbp >= caller_rip_address + 8) {
|
||||
// If the callee's %rbp is plausible as a value for the caller's
|
||||
// %rbp, presume that the callee left it unchanged.
|
||||
frame->context.rbp = last_frame->context.rbp;
|
||||
frame->context_validity |= StackFrameAMD64::CONTEXT_VALID_RBP;
|
||||
}
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack* stack,
|
||||
bool stack_scan_allowed) {
|
||||
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 CFI information, use it.
|
||||
scoped_ptr<CFIFrameInfo> cfi_frame_info(
|
||||
frame_symbolizer_->FindCFIFrameInfo(last_frame));
|
||||
if (cfi_frame_info.get())
|
||||
new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
|
||||
|
||||
// If CFI was not available and this is a Windows x64 stack, check whether
|
||||
// this is a leaf function which doesn't touch any callee-saved registers.
|
||||
// According to https://reviews.llvm.org/D24748, LLVM doesn't generate unwind
|
||||
// info for such functions. According to MSDN, leaf functions can be unwound
|
||||
// simply by simulating a return.
|
||||
if (!new_frame.get() &&
|
||||
last_frame->trust == StackFrame::FRAME_TRUST_CONTEXT &&
|
||||
system_info_->os_short == "windows") {
|
||||
new_frame.reset(GetCallerBySimulatingReturn(frames));
|
||||
}
|
||||
|
||||
// If CFI was not available or failed, try using frame pointer recovery.
|
||||
// Never try to use frame pointer unwinding on Windows x64 stack. MSVC never
|
||||
// generates code that works with frame pointer chasing, and LLVM does the
|
||||
// same. Stack scanning would be better.
|
||||
if (!new_frame.get() && system_info_->os_short != "windows") {
|
||||
new_frame.reset(GetCallerByFramePointerRecovery(frames));
|
||||
}
|
||||
|
||||
// If all else fails, fall back to stack scanning.
|
||||
if (stack_scan_allowed && !new_frame.get()) {
|
||||
new_frame.reset(GetCallerByStackScan(frames));
|
||||
}
|
||||
|
||||
// If nothing worked, tell the caller.
|
||||
if (!new_frame.get())
|
||||
return NULL;
|
||||
|
||||
if (system_info_->os_short == "nacl") {
|
||||
// Apply constraints from Native Client's x86-64 sandbox. These
|
||||
// registers have the 4GB-aligned sandbox base address (from r15)
|
||||
// added to them, and only the bottom 32 bits are relevant for
|
||||
// stack walking.
|
||||
new_frame->context.rip = static_cast<uint32_t>(new_frame->context.rip);
|
||||
new_frame->context.rsp = static_cast<uint32_t>(new_frame->context.rsp);
|
||||
new_frame->context.rbp = static_cast<uint32_t>(new_frame->context.rbp);
|
||||
}
|
||||
|
||||
// Should we terminate the stack walk? (end-of-stack or broken invariant)
|
||||
if (TerminateWalk(new_frame->context.rip, new_frame->context.rsp,
|
||||
last_frame->context.rsp,
|
||||
/*first_unwind=*/last_frame->trust ==
|
||||
StackFrame::FRAME_TRUST_CONTEXT)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// new_frame->context.rip is the return address, which is the instruction
|
||||
// after the CALL that caused us to arrive at the callee. Set
|
||||
// new_frame->instruction to one less than that, so it points within the
|
||||
// CALL instruction. See StackFrame::instruction for details, and
|
||||
// StackFrameAMD64::ReturnAddress.
|
||||
new_frame->instruction = new_frame->context.rip - 1;
|
||||
|
||||
return new_frame.release();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
112
externals/breakpad/src/processor/stackwalker_amd64.h
vendored
Normal file
112
externals/breakpad/src/processor/stackwalker_amd64.h
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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 <vector>
|
||||
|
||||
#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,
|
||||
StackFrameSymbolizer* frame_symbolizer);
|
||||
|
||||
private:
|
||||
// A STACK CFI-driven frame walker for the AMD64
|
||||
typedef SimpleCFIWalker<uint64_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,
|
||||
bool stack_scan_allowed);
|
||||
|
||||
// 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);
|
||||
|
||||
// Assumes a traditional frame layout where the frame pointer has not been
|
||||
// omitted. The expectation is that caller's %rbp is pushed to the stack
|
||||
// after the return address of the callee, and that the callee's %rsp can
|
||||
// be used to find the pushed %rbp.
|
||||
// Caller owns the returned frame object. Returns NULL on failure.
|
||||
StackFrameAMD64* GetCallerByFramePointerRecovery(
|
||||
const vector<StackFrame*>& frames);
|
||||
|
||||
// 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);
|
||||
|
||||
// Trying to simulate a return. The caller takes ownership of the returned
|
||||
// frame. Return NULL on failure.
|
||||
StackFrameAMD64* GetCallerBySimulatingReturn(
|
||||
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__
|
||||
937
externals/breakpad/src/processor/stackwalker_amd64_unittest.cc
vendored
Normal file
937
externals/breakpad/src/processor/stackwalker_amd64_unittest.cc
vendored
Normal file
|
|
@ -0,0 +1,937 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/test_assembler.h"
|
||||
#include "common/using_std_string.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/code_module.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::CodeModule;
|
||||
using google_breakpad::StackFrameSymbolizer;
|
||||
using google_breakpad::StackFrame;
|
||||
using google_breakpad::StackFrameAMD64;
|
||||
using google_breakpad::Stackwalker;
|
||||
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::vector;
|
||||
using testing::_;
|
||||
using testing::AnyNumber;
|
||||
using testing::DoAll;
|
||||
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(0x00007400c0000000ULL, 0x10000, "module1", "version1"),
|
||||
module2(0x00007500b0000000ULL, 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));
|
||||
|
||||
// Avoid GMOCK WARNING "Uninteresting mock function call - returning
|
||||
// directly" for FreeSymbolData().
|
||||
EXPECT_CALL(supplier, FreeSymbolData(_)).Times(AnyNumber());
|
||||
|
||||
// Reset max_frames_scanned since it's static.
|
||||
Stackwalker::set_max_frames_scanned(1024);
|
||||
}
|
||||
|
||||
// Set the Breakpad symbol information that supplier should return for
|
||||
// MODULE to INFO.
|
||||
void SetModuleSymbols(MockCodeModule* module, const string& info) {
|
||||
size_t buffer_size;
|
||||
char *buffer = supplier.CopySymbolDataAndOwnTheCopy(info, &buffer_size);
|
||||
EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _, _))
|
||||
.WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer),
|
||||
SetArgumentPointee<4>(buffer_size),
|
||||
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) {
|
||||
uint8_t x = 173;
|
||||
for (size_t i = 0; i < sizeof(*raw_context); i++)
|
||||
reinterpret_cast<uint8_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 = 0x00007400c0000200ULL;
|
||||
raw_context.rbp = 0x8000000080000000ULL;
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(NULL, NULL);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
// This should succeed even without a resolver or supplier.
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(1U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
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 = 0x00007400c0000200ULL;
|
||||
raw_context.rbp = 0x8000000080000000ULL;
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(1U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
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)));
|
||||
}
|
||||
|
||||
// The stackwalker should be able to produce the context frame even
|
||||
// without stack memory present.
|
||||
TEST_F(GetContextFrame, NoStackMemory) {
|
||||
raw_context.rip = 0x00007400c0000200ULL;
|
||||
raw_context.rbp = 0x8000000080000000ULL;
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, NULL, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(1U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
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;
|
||||
uint64_t return_address1 = 0x00007500b0000100ULL;
|
||||
uint64_t return_address2 = 0x00007500b0000900ULL;
|
||||
Label frame1_sp, frame2_sp, frame1_rbp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x00007400b0000000ULL) // junk that's not
|
||||
.D64(0x00007500d0000000ULL) // a return address
|
||||
|
||||
.D64(return_address1) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x00007400b0000000ULL) // more junk
|
||||
.D64(0x00007500d0000000ULL)
|
||||
|
||||
.Mark(&frame1_rbp)
|
||||
.D64(stack_section.start()) // This is in the right place to be
|
||||
// a saved rbp, but it's bogus, so
|
||||
// we shouldn't report it.
|
||||
|
||||
.D64(return_address2) // actual return address
|
||||
// frame 2
|
||||
.Mark(&frame2_sp)
|
||||
.Append(32, 0); // end of stack
|
||||
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.rip = 0x00007400c0000200ULL;
|
||||
raw_context.rbp = frame1_rbp.Value();
|
||||
raw_context.rsp = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(2U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
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 |
|
||||
StackFrameAMD64::CONTEXT_VALID_RBP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address1, frame1->context.rip);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp);
|
||||
EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp);
|
||||
|
||||
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;
|
||||
uint64_t return_address = 0x00007500b0000110ULL;
|
||||
Label frame1_sp, frame1_rbp;
|
||||
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x00007400b0000000ULL) // junk that's not
|
||||
.D64(0x00007500b0000000ULL) // a return address
|
||||
|
||||
.D64(0x00007400c0001000ULL) // a couple of plausible addresses
|
||||
.D64(0x00007500b000aaaaULL) // that are not within functions
|
||||
|
||||
.D64(return_address) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(32, 0) // end of stack
|
||||
.Mark(&frame1_rbp);
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.rip = 0x00007400c0000200ULL;
|
||||
raw_context.rbp = frame1_rbp.Value();
|
||||
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");
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(0U, modules_without_symbols.size());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
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(0x00007400c0000100ULL, 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 |
|
||||
StackFrameAMD64::CONTEXT_VALID_RBP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address, frame1->context.rip);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp);
|
||||
EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp);
|
||||
EXPECT_EQ("echidna", frame1->function_name);
|
||||
EXPECT_EQ(0x00007500b0000100ULL, frame1->function_base);
|
||||
}
|
||||
|
||||
// StackwalkerAMD64::GetCallerByFramePointerRecovery should never return an
|
||||
// instruction pointer of 0 because IP of 0 is an end of stack marker and the
|
||||
// stack walk may be terminated prematurely. Instead it should return NULL
|
||||
// so that the stack walking code can proceed to stack scanning.
|
||||
TEST_F(GetCallerFrame, GetCallerByFramePointerRecovery) {
|
||||
MockCodeModule user32_dll(0x00007ff9cb8a0000ULL, 0x14E000, "user32.dll",
|
||||
"version1");
|
||||
SetModuleSymbols(&user32_dll, // user32.dll
|
||||
"PUBLIC fa60 0 DispatchMessageWorker\n"
|
||||
"PUBLIC fee0 0 UserCallWinProcCheckWow\n"
|
||||
"PUBLIC 1cdb0 0 _fnHkINLPMSG\n"
|
||||
"STACK CFI INIT fa60 340 .cfa: $rsp .ra: .cfa 8 - ^\n"
|
||||
"STACK CFI fa60 .cfa: $rsp 128 +\n"
|
||||
"STACK CFI INIT fee0 49f .cfa: $rsp .ra: .cfa 8 - ^\n"
|
||||
"STACK CFI fee0 .cfa: $rsp 240 +\n"
|
||||
"STACK CFI INIT 1cdb0 9f .cfa: $rsp .ra: .cfa 8 - ^\n"
|
||||
"STACK CFI 1cdb0 .cfa: $rsp 80 +\n");
|
||||
|
||||
// Create some modules with some stock debugging information.
|
||||
MockCodeModules local_modules;
|
||||
local_modules.Add(&user32_dll);
|
||||
|
||||
Label frame0_rsp;
|
||||
Label frame0_rbp;
|
||||
Label frame1_rsp;
|
||||
Label frame2_rsp;
|
||||
|
||||
stack_section.start() = 0x00000099abf0f238ULL;
|
||||
stack_section
|
||||
.Mark(&frame0_rsp)
|
||||
.D64(0x00007ff9cb8b00dcULL)
|
||||
.Mark(&frame1_rsp)
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x0000000000000001ULL)
|
||||
.D64(0x00000099abf0f308ULL)
|
||||
.D64(0x00007ff9cb8bce3aULL) // Stack residue from execution of
|
||||
// user32!_fnHkINLPMSG+0x8a
|
||||
.D64(0x000000000000c2e0ULL)
|
||||
.D64(0x00000099abf0f328ULL)
|
||||
.D64(0x0000000100000001ULL)
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x00007ff9ccad53e4ULL)
|
||||
.D64(0x0000000000000048ULL)
|
||||
.D64(0x0000000000000001ULL)
|
||||
.D64(0x00000099abf0f5e0ULL)
|
||||
.D64(0x00000099b61f7388ULL)
|
||||
.D64(0x0000000000000030ULL)
|
||||
.D64(0xffffff66540f0a1fULL)
|
||||
.D64(0xffffff6649e08c77ULL)
|
||||
.D64(0x00007ff9cb8affb4ULL) // Return address in
|
||||
// user32!UserCallWinProcCheckWow+0xd4
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x00000099abf0f368ULL)
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x0000000000000000ULL)
|
||||
.D64(0x00000099a8150fd8ULL)
|
||||
.D64(0x00000099abf0f3e8ULL)
|
||||
.D64(0x00007ff9cb8afc07ULL) // Return address in
|
||||
// user32!DispatchMessageWorker+0x1a7
|
||||
.Mark(&frame2_rsp)
|
||||
.Append(256, 0)
|
||||
.Mark(&frame0_rbp) // The following are expected by
|
||||
// GetCallerByFramePointerRecovery.
|
||||
.D64(0xfffffffffffffffeULL) // %caller_rbp = *(%callee_rbp)
|
||||
.D64(0x0000000000000000ULL) // %caller_rip = *(%callee_rbp + 8)
|
||||
.D64(0x00000099a3e31040ULL) // %caller_rsp = *(%callee_rbp + 16)
|
||||
.Append(256, 0);
|
||||
|
||||
RegionFromSection();
|
||||
raw_context.rip = 0x00000099a8150fd8ULL; // IP in context frame is guarbage
|
||||
raw_context.rsp = frame0_rsp.Value();
|
||||
raw_context.rbp = frame0_rbp.Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region,
|
||||
&local_modules, &frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(0U, modules_without_symbols.size());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
|
||||
ASSERT_EQ(3U, frames->size());
|
||||
|
||||
{ // To avoid reusing locals by mistake
|
||||
StackFrameAMD64 *frame = static_cast<StackFrameAMD64*>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame->trust);
|
||||
ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame->context_validity);
|
||||
EXPECT_EQ("", frame->function_name);
|
||||
EXPECT_EQ(0x00000099a8150fd8ULL, frame->instruction);
|
||||
EXPECT_EQ(0x00000099a8150fd8ULL, frame->context.rip);
|
||||
EXPECT_EQ(frame0_rsp.Value(), frame->context.rsp);
|
||||
EXPECT_EQ(frame0_rbp.Value(), frame->context.rbp);
|
||||
}
|
||||
|
||||
{ // To avoid reusing locals by mistake
|
||||
StackFrameAMD64 *frame = static_cast<StackFrameAMD64*>(frames->at(1));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame->trust);
|
||||
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RSP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RBP),
|
||||
frame->context_validity);
|
||||
EXPECT_EQ("UserCallWinProcCheckWow", frame->function_name);
|
||||
EXPECT_EQ(140710838468828ULL, frame->instruction + 1);
|
||||
EXPECT_EQ(140710838468828ULL, frame->context.rip);
|
||||
EXPECT_EQ(frame1_rsp.Value(), frame->context.rsp);
|
||||
EXPECT_EQ(&user32_dll, frame->module);
|
||||
}
|
||||
|
||||
{ // To avoid reusing locals by mistake
|
||||
StackFrameAMD64 *frame = static_cast<StackFrameAMD64*>(frames->at(2));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame->trust);
|
||||
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RSP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RBP),
|
||||
frame->context_validity);
|
||||
EXPECT_EQ("DispatchMessageWorker", frame->function_name);
|
||||
EXPECT_EQ(140710838467591ULL, frame->instruction + 1);
|
||||
EXPECT_EQ(140710838467591ULL, frame->context.rip);
|
||||
EXPECT_EQ(frame2_rsp.Value(), frame->context.rsp);
|
||||
EXPECT_EQ(&user32_dll, frame->module);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't use frame pointer recovery if %rbp is not 8-byte aligned, which
|
||||
// indicates that it's not being used as a frame pointer.
|
||||
TEST_F(GetCallerFrame, FramePointerNotAligned) {
|
||||
stack_section.start() = 0x8000000080000000ULL;
|
||||
uint64_t return_address1 = 0x00007500b0000100ULL;
|
||||
Label frame0_rbp, not_frame1_rbp, frame1_sp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Align(8, 0)
|
||||
.Append(2, 0) // mis-align the frame pointer
|
||||
.Mark(&frame0_rbp)
|
||||
.D64(not_frame1_rbp) // not the previous frame pointer
|
||||
.D64(0x00007500b0000a00ULL) // plausible but wrong return address
|
||||
.Align(8, 0)
|
||||
.D64(return_address1) // return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Mark(¬_frame1_rbp)
|
||||
.Append(32, 0); // end of stack
|
||||
|
||||
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.rip = 0x00007400c0000200ULL;
|
||||
raw_context.rbp = frame0_rbp.Value();
|
||||
raw_context.rsp = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
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(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);
|
||||
}
|
||||
|
||||
// Don't use frame pointer recovery if the recovered %rip is not
|
||||
// a canonical x86-64 address.
|
||||
TEST_F(GetCallerFrame, NonCanonicalInstructionPointerFromFramePointer) {
|
||||
stack_section.start() = 0x8000000080000000ULL;
|
||||
uint64_t return_address1 = 0x00007500b0000100ULL;
|
||||
Label frame0_rbp, frame1_sp, not_frame1_bp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Align(8, 0)
|
||||
.Mark(&frame0_rbp)
|
||||
.D64(not_frame1_bp) // some junk on the stack
|
||||
.D64(0xDADADADADADADADA) // not the return address
|
||||
.D64(return_address1) // return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(16, 0)
|
||||
.Mark(¬_frame1_bp)
|
||||
.Append(32, 0); // end of stack
|
||||
|
||||
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.rip = 0x00007400c0000200ULL;
|
||||
raw_context.rbp = frame0_rbp.Value();
|
||||
raw_context.rsp = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
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(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);
|
||||
}
|
||||
|
||||
// Test that set_max_frames_scanned prevents using stack scanning
|
||||
// to find caller frames.
|
||||
TEST_F(GetCallerFrame, ScanningNotAllowed) {
|
||||
// When the stack walker resorts to scanning the stack,
|
||||
// only addresses located within loaded modules are
|
||||
// considered valid return addresses.
|
||||
stack_section.start() = 0x8000000080000000ULL;
|
||||
uint64_t return_address1 = 0x00007500b0000100ULL;
|
||||
uint64_t return_address2 = 0x00007500b0000900ULL;
|
||||
Label frame1_sp, frame2_sp, frame1_rbp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x00007400b0000000ULL) // junk that's not
|
||||
.D64(0x00007500d0000000ULL) // a return address
|
||||
|
||||
.D64(return_address1) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x00007400b0000000ULL) // more junk
|
||||
.D64(0x00007500d0000000ULL)
|
||||
|
||||
.Mark(&frame1_rbp)
|
||||
.D64(stack_section.start()) // This is in the right place to be
|
||||
// a saved rbp, but it's bogus, so
|
||||
// we shouldn't report it.
|
||||
|
||||
.D64(return_address2) // actual return address
|
||||
// frame 2
|
||||
.Mark(&frame2_sp)
|
||||
.Append(32, 0); // end of stack
|
||||
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.rip = 0x00007400c0000200ULL;
|
||||
raw_context.rbp = frame1_rbp.Value();
|
||||
raw_context.rsp = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
Stackwalker::set_max_frames_scanned(0);
|
||||
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(1U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(1U, 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)));
|
||||
}
|
||||
|
||||
TEST_F(GetCallerFrame, CallerPushedRBP) {
|
||||
// Functions typically push their %rbp upon entry and set %rbp pointing
|
||||
// there. If stackwalking finds a plausible address for the next frame's
|
||||
// %rbp directly below the return address, assume that it is indeed the
|
||||
// next frame's %rbp.
|
||||
stack_section.start() = 0x8000000080000000ULL;
|
||||
uint64_t return_address = 0x00007500b0000110ULL;
|
||||
Label frame0_rbp, frame1_sp, frame1_rbp;
|
||||
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x00007400b0000000ULL) // junk that's not
|
||||
.D64(0x00007500b0000000ULL) // a return address
|
||||
|
||||
.D64(0x00007400c0001000ULL) // a couple of plausible addresses
|
||||
.D64(0x00007500b000aaaaULL) // that are not within functions
|
||||
|
||||
.Mark(&frame0_rbp)
|
||||
.D64(frame1_rbp) // caller-pushed %rbp
|
||||
.D64(return_address) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(32, 0) // body of frame1
|
||||
.Mark(&frame1_rbp) // end of stack
|
||||
.D64(0);
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.rip = 0x00007400c0000200ULL;
|
||||
raw_context.rbp = frame0_rbp.Value();
|
||||
raw_context.rsp = stack_section.start().Value();
|
||||
|
||||
SetModuleSymbols(&module1,
|
||||
// The youngest frame's function.
|
||||
"FUNC 100 400 10 sasquatch\n");
|
||||
SetModuleSymbols(&module2,
|
||||
// The calling frame's function.
|
||||
"FUNC 100 400 10 yeti\n");
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(0U, modules_without_symbols.size());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
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(frame0_rbp.Value(), frame0->context.rbp);
|
||||
EXPECT_EQ("sasquatch", frame0->function_name);
|
||||
EXPECT_EQ(0x00007400c0000100ULL, frame0->function_base);
|
||||
|
||||
StackFrameAMD64 *frame1 = static_cast<StackFrameAMD64*>(frames->at(1));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
|
||||
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RSP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RBP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address, frame1->context.rip);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.rsp);
|
||||
EXPECT_EQ(frame1_rbp.Value(), frame1->context.rbp);
|
||||
EXPECT_EQ("yeti", frame1->function_name);
|
||||
EXPECT_EQ(0x00007500b0000100ULL, 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 = 0x00007400c0005510ULL;
|
||||
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();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(0U, modules_without_symbols.size());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
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(0x00007400c0004000ULL, 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(0x00007400c0005510ULL) // return address
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x00007400c0004000ULL;
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
TEST_F(CFI, At4001) {
|
||||
Label frame1_rsp = expected.rsp;
|
||||
stack_section
|
||||
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
|
||||
.D64(0x00007400c0005510ULL) // return address
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x00007400c0004001ULL;
|
||||
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(0x00007400c0005510ULL) // return address
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x00007400c0004002ULL;
|
||||
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(0x00007400c0005510ULL) // return address
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x00007400c0004003ULL;
|
||||
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(0x00007400c0005510ULL) // return address
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x00007400c0004004ULL;
|
||||
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 = 0x00007400c0004005ULL;
|
||||
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
|
||||
raw_context.r12 = 0x46b1b8868891b34aULL; // callee's %r12
|
||||
raw_context.r13 = 0x00007400c0005510ULL; // 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 = 0x00007400c0004006ULL;
|
||||
raw_context.rbp = frame0_rbp.Value();
|
||||
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
|
||||
raw_context.r12 = 0x26e007b341acfebdULL; // callee's %r12
|
||||
raw_context.r13 = 0x00007400c0005510ULL; // return address
|
||||
CheckWalk();
|
||||
}
|
||||
302
externals/breakpad/src/processor/stackwalker_arm.cc
vendored
Normal file
302
externals/breakpad/src/processor/stackwalker_arm.cc
vendored
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#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 "google_breakpad/processor/system_info.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/stackwalker_arm.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
StackwalkerARM::StackwalkerARM(const SystemInfo* system_info,
|
||||
const MDRawContextARM* context,
|
||||
int fp_register,
|
||||
MemoryRegion* memory,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* resolver_helper)
|
||||
: Stackwalker(system_info, memory, modules, resolver_helper),
|
||||
context_(context), fp_register_(fp_register),
|
||||
context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { }
|
||||
|
||||
|
||||
StackFrame* StackwalkerARM::GetContextFrame() {
|
||||
if (!context_) {
|
||||
BPLOG(ERROR) << "Can't get context frame without context";
|
||||
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[MD_CONTEXT_ARM_REG_PC];
|
||||
|
||||
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<uint32_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<uint32_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<uint32_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<uint32_t>::iterator entry =
|
||||
caller_registers.find(".ra");
|
||||
if (entry != caller_registers.end()) {
|
||||
if (fp_register_ == -1) {
|
||||
frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
|
||||
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second;
|
||||
} else {
|
||||
// The CFI updated the link register and not the program counter.
|
||||
// Handle getting the program counter from the link register.
|
||||
frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
|
||||
frame->context_validity |= StackFrameARM::CONTEXT_VALID_LR;
|
||||
frame->context.iregs[MD_CONTEXT_ARM_REG_LR] = entry->second;
|
||||
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] =
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR];
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the CFI doesn't recover the SP explicitly, then use .cfa.
|
||||
if (!(frame->context_validity & StackFrameARM::CONTEXT_VALID_SP)) {
|
||||
CFIFrameInfo::RegisterValueMap<uint32_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());
|
||||
uint32_t last_sp = last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
uint32_t caller_sp, caller_pc;
|
||||
|
||||
if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc,
|
||||
/*is_context_frame=*/last_frame->trust ==
|
||||
StackFrame::FRAME_TRUST_CONTEXT)) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
StackFrameARM* StackwalkerARM::GetCallerByFramePointer(
|
||||
const vector<StackFrame*>& frames) {
|
||||
StackFrameARM* last_frame = static_cast<StackFrameARM*>(frames.back());
|
||||
|
||||
if (!(last_frame->context_validity &
|
||||
StackFrameARM::RegisterValidFlag(fp_register_))) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t last_fp = last_frame->context.iregs[fp_register_];
|
||||
|
||||
uint32_t caller_fp = 0;
|
||||
if (last_fp && !memory_->GetMemoryAtAddress(last_fp, &caller_fp)) {
|
||||
BPLOG(ERROR) << "Unable to read caller_fp from last_fp: 0x"
|
||||
<< std::hex << last_fp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t caller_lr = 0;
|
||||
if (last_fp && !memory_->GetMemoryAtAddress(last_fp + 4, &caller_lr)) {
|
||||
BPLOG(ERROR) << "Unable to read caller_lr from last_fp + 4: 0x"
|
||||
<< std::hex << (last_fp + 4);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t caller_sp = last_fp ? last_fp + 8 :
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
|
||||
// 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_FP;
|
||||
frame->context = last_frame->context;
|
||||
frame->context.iregs[fp_register_] = caller_fp;
|
||||
frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = caller_sp;
|
||||
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] =
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR];
|
||||
frame->context.iregs[MD_CONTEXT_ARM_REG_LR] = caller_lr;
|
||||
frame->context_validity = StackFrameARM::CONTEXT_VALID_PC |
|
||||
StackFrameARM::CONTEXT_VALID_LR |
|
||||
StackFrameARM::RegisterValidFlag(fp_register_) |
|
||||
StackFrameARM::CONTEXT_VALID_SP;
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerARM::GetCallerFrame(const CallStack* stack,
|
||||
bool stack_scan_allowed) {
|
||||
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.
|
||||
// TODO(jperaza): Ignore iOS CFI info until it is properly collected.
|
||||
// https://bugs.chromium.org/p/google-breakpad/issues/detail?id=764
|
||||
if (!system_info_ || system_info_->os != "iOS") {
|
||||
scoped_ptr<CFIFrameInfo> cfi_frame_info(
|
||||
frame_symbolizer_->FindCFIFrameInfo(last_frame));
|
||||
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 frame pointer, if this is configured.
|
||||
if (fp_register_ >= 0 && !frame.get())
|
||||
frame.reset(GetCallerByFramePointer(frames));
|
||||
|
||||
// If everuthing failed, fall back to stack scanning.
|
||||
if (stack_scan_allowed && !frame.get())
|
||||
frame.reset(GetCallerByStackScan(frames));
|
||||
|
||||
// If nothing worked, tell the caller.
|
||||
if (!frame.get())
|
||||
return NULL;
|
||||
|
||||
// Should we terminate the stack walk? (end-of-stack or broken invariant)
|
||||
if (TerminateWalk(frame->context.iregs[MD_CONTEXT_ARM_REG_PC],
|
||||
frame->context.iregs[MD_CONTEXT_ARM_REG_SP],
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP],
|
||||
/*first_unwind=*/last_frame->trust ==
|
||||
StackFrame::FRAME_TRUST_CONTEXT)) {
|
||||
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] - 2;
|
||||
|
||||
return frame.release();
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
106
externals/breakpad/src/processor/stackwalker_arm.h
vendored
Normal file
106
externals/breakpad/src/processor/stackwalker_arm.h
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright 2010 Google LLC
|
||||
//
|
||||
// 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 LLC 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,
|
||||
int fp_register,
|
||||
MemoryRegion* memory,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* frame_symbolizer);
|
||||
|
||||
// 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,
|
||||
bool stack_scan_allowed);
|
||||
|
||||
// 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);
|
||||
|
||||
// Use the frame pointer. The caller takes ownership of the returned frame.
|
||||
// Return NULL on failure.
|
||||
StackFrameARM* GetCallerByFramePointer(const vector<StackFrame*>& frames);
|
||||
|
||||
// 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_;
|
||||
|
||||
// The register to use a as frame pointer. The value is -1 if frame pointer
|
||||
// cannot be used.
|
||||
int fp_register_;
|
||||
|
||||
// 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__
|
||||
357
externals/breakpad/src/processor/stackwalker_arm64.cc
vendored
Normal file
357
externals/breakpad/src/processor/stackwalker_arm64.cc
vendored
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
// Copyright 2013 Google LLC
|
||||
//
|
||||
// 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 LLC 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_arm64.cc: arm64-specific stackwalker.
|
||||
//
|
||||
// See stackwalker_arm64.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy, Colin Blundell
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h> // Must come first
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#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/stackwalker_arm64.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
StackwalkerARM64::StackwalkerARM64(const SystemInfo* system_info,
|
||||
const MDRawContextARM64* context,
|
||||
MemoryRegion* memory,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* resolver_helper)
|
||||
: Stackwalker(system_info, memory, modules, resolver_helper),
|
||||
context_(context),
|
||||
context_frame_validity_(StackFrameARM64::CONTEXT_VALID_ALL),
|
||||
address_range_mask_(0xffffffffffffffff) {
|
||||
if (modules && modules->module_count() > 0) {
|
||||
// ARM64 supports storing pointer authentication codes in the upper bits of
|
||||
// a pointer. Make a best guess at the range of valid addresses based on the
|
||||
// range of loaded modules.
|
||||
const CodeModule *high_module =
|
||||
modules->GetModuleAtSequence(modules->module_count() - 1);
|
||||
uint64_t mask = high_module->base_address() + high_module->size();
|
||||
mask |= mask >> 1;
|
||||
mask |= mask >> 2;
|
||||
mask |= mask >> 4;
|
||||
mask |= mask >> 8;
|
||||
mask |= mask >> 16;
|
||||
mask |= mask >> 32;
|
||||
address_range_mask_ = mask;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t StackwalkerARM64::PtrauthStrip(uint64_t ptr) {
|
||||
uint64_t stripped = ptr & address_range_mask_;
|
||||
return modules_ && modules_->GetModuleForAddress(stripped) ? stripped : ptr;
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerARM64::GetContextFrame() {
|
||||
if (!context_) {
|
||||
BPLOG(ERROR) << "Can't get context frame without context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFrameARM64* frame = new StackFrameARM64();
|
||||
|
||||
// The instruction pointer is stored directly in a register (x32), 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[MD_CONTEXT_ARM64_REG_PC];
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_LR] =
|
||||
PtrauthStrip(frame->context.iregs[MD_CONTEXT_ARM64_REG_LR]);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrameARM64* StackwalkerARM64::GetCallerByCFIFrameInfo(
|
||||
const vector<StackFrame*>& frames,
|
||||
CFIFrameInfo* cfi_frame_info) {
|
||||
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
|
||||
|
||||
static const char* register_names[] = {
|
||||
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
|
||||
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
|
||||
"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
|
||||
"x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp",
|
||||
"pc", NULL
|
||||
};
|
||||
|
||||
// Populate a dictionary with the valid register values in last_frame.
|
||||
CFIFrameInfo::RegisterValueMap<uint64_t> callee_registers;
|
||||
for (int i = 0; register_names[i]; i++) {
|
||||
if (last_frame->context_validity & StackFrameARM64::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<uint64_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<StackFrameARM64> frame(new StackFrameARM64());
|
||||
for (int i = 0; register_names[i]; i++) {
|
||||
CFIFrameInfo::RegisterValueMap<uint64_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 |= StackFrameARM64::RegisterValidFlag(i);
|
||||
frame->context.iregs[i] = entry->second;
|
||||
} else if (19 <= i && i <= 29 && (last_frame->context_validity &
|
||||
StackFrameARM64::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 r19 through r29 are callee-saves, according to the Procedure
|
||||
// Call Standard for the ARM AARCH64 Architecture, which the Linux ABI
|
||||
// follows.
|
||||
frame->context_validity |= StackFrameARM64::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 & StackFrameARM64::CONTEXT_VALID_PC)) {
|
||||
CFIFrameInfo::RegisterValueMap<uint64_t>::iterator entry =
|
||||
caller_registers.find(".ra");
|
||||
if (entry != caller_registers.end()) {
|
||||
frame->context_validity |= StackFrameARM64::CONTEXT_VALID_PC;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] = entry->second;
|
||||
}
|
||||
}
|
||||
// If the CFI doesn't recover the SP explicitly, then use .cfa.
|
||||
if (!(frame->context_validity & StackFrameARM64::CONTEXT_VALID_SP)) {
|
||||
CFIFrameInfo::RegisterValueMap<uint64_t>::iterator entry =
|
||||
caller_registers.find(".cfa");
|
||||
if (entry != caller_registers.end()) {
|
||||
frame->context_validity |= StackFrameARM64::CONTEXT_VALID_SP;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP] = entry->second;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't recover the PC and the SP, then the frame isn't very useful.
|
||||
static const uint64_t essentials = (StackFrameARM64::CONTEXT_VALID_SP
|
||||
| StackFrameARM64::CONTEXT_VALID_PC);
|
||||
if ((frame->context_validity & essentials) != essentials)
|
||||
return NULL;
|
||||
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] =
|
||||
PtrauthStrip(frame->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
frame->trust = StackFrame::FRAME_TRUST_CFI;
|
||||
return frame.release();
|
||||
}
|
||||
|
||||
StackFrameARM64* StackwalkerARM64::GetCallerByStackScan(
|
||||
const vector<StackFrame*>& frames) {
|
||||
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
|
||||
uint64_t last_sp = last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP];
|
||||
uint64_t caller_sp, caller_pc;
|
||||
|
||||
if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc,
|
||||
/*is_context_frame=*/last_frame->trust ==
|
||||
StackFrame::FRAME_TRUST_CONTEXT)) {
|
||||
// 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 += 8;
|
||||
|
||||
// Create a new stack frame (ownership will be transferred to the caller)
|
||||
// and fill it in.
|
||||
StackFrameARM64* frame = new StackFrameARM64();
|
||||
|
||||
frame->trust = StackFrame::FRAME_TRUST_SCAN;
|
||||
frame->context = last_frame->context;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] = caller_pc;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP] = caller_sp;
|
||||
frame->context_validity = StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrameARM64* StackwalkerARM64::GetCallerByFramePointer(
|
||||
const vector<StackFrame*>& frames) {
|
||||
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
|
||||
if (!(last_frame->context_validity & StackFrameARM64::CONTEXT_VALID_LR)) {
|
||||
CorrectRegLRByFramePointer(frames, last_frame);
|
||||
}
|
||||
|
||||
uint64_t last_fp = last_frame->context.iregs[MD_CONTEXT_ARM64_REG_FP];
|
||||
|
||||
uint64_t caller_fp = 0;
|
||||
if (last_fp && !memory_->GetMemoryAtAddress(last_fp, &caller_fp)) {
|
||||
BPLOG(ERROR) << "Unable to read caller_fp from last_fp: 0x"
|
||||
<< std::hex << last_fp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t caller_lr = 0;
|
||||
if (last_fp && !memory_->GetMemoryAtAddress(last_fp + 8, &caller_lr)) {
|
||||
BPLOG(ERROR) << "Unable to read caller_lr from last_fp + 8: 0x"
|
||||
<< std::hex << (last_fp + 8);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
caller_lr = PtrauthStrip(caller_lr);
|
||||
|
||||
uint64_t caller_sp = last_fp ? last_fp + 16 :
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP];
|
||||
|
||||
// Create a new stack frame (ownership will be transferred to the caller)
|
||||
// and fill it in.
|
||||
StackFrameARM64* frame = new StackFrameARM64();
|
||||
|
||||
frame->trust = StackFrame::FRAME_TRUST_FP;
|
||||
frame->context = last_frame->context;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_FP] = caller_fp;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP] = caller_sp;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] =
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_LR];
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_LR] = caller_lr;
|
||||
frame->context_validity = StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_LR |
|
||||
StackFrameARM64::CONTEXT_VALID_FP |
|
||||
StackFrameARM64::CONTEXT_VALID_SP;
|
||||
return frame;
|
||||
}
|
||||
|
||||
void StackwalkerARM64::CorrectRegLRByFramePointer(
|
||||
const vector<StackFrame*>& frames,
|
||||
StackFrameARM64* last_frame) {
|
||||
// Need at least two frames to correct and
|
||||
// register $FP should always be greater than register $SP.
|
||||
if (frames.size() < 2 || !last_frame ||
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_FP] <=
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP])
|
||||
return;
|
||||
|
||||
// Searching for a real callee frame. Skipping inline frames since they
|
||||
// don't contain context (and cannot be downcasted to StackFrameARM64).
|
||||
size_t last_frame_callee_id = frames.size() - 2;
|
||||
while (last_frame_callee_id >= 0 && frames[last_frame_callee_id]->trust ==
|
||||
StackFrame::FRAME_TRUST_INLINE) {
|
||||
last_frame_callee_id--;
|
||||
}
|
||||
if (last_frame_callee_id < 0) return;
|
||||
StackFrameARM64* last_frame_callee =
|
||||
static_cast<StackFrameARM64*>(frames[last_frame_callee_id]);
|
||||
|
||||
uint64_t last_frame_callee_fp =
|
||||
last_frame_callee->context.iregs[MD_CONTEXT_ARM64_REG_FP];
|
||||
|
||||
uint64_t last_fp = 0;
|
||||
if (last_frame_callee_fp &&
|
||||
!memory_->GetMemoryAtAddress(last_frame_callee_fp, &last_fp)) {
|
||||
BPLOG(ERROR) << "Unable to read last_fp from last_last_fp: 0x" << std::hex
|
||||
<< last_frame_callee_fp;
|
||||
return;
|
||||
}
|
||||
// Give up if STACK CFI doesn't agree with frame pointer.
|
||||
if (last_frame->context.iregs[MD_CONTEXT_ARM64_REG_FP] != last_fp)
|
||||
return;
|
||||
|
||||
uint64_t last_lr = 0;
|
||||
if (last_frame_callee_fp &&
|
||||
!memory_->GetMemoryAtAddress(last_frame_callee_fp + 8, &last_lr)) {
|
||||
BPLOG(ERROR) << "Unable to read last_lr from (last_last_fp + 8): 0x"
|
||||
<< std::hex << (last_frame_callee_fp + 8);
|
||||
return;
|
||||
}
|
||||
last_lr = PtrauthStrip(last_lr);
|
||||
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_LR] = last_lr;
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerARM64::GetCallerFrame(const CallStack* stack,
|
||||
bool stack_scan_allowed) {
|
||||
if (!memory_ || !stack) {
|
||||
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const vector<StackFrame*>& frames = *stack->frames();
|
||||
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
|
||||
scoped_ptr<StackFrameARM64> frame;
|
||||
|
||||
// See if there is DWARF call frame information covering this address.
|
||||
scoped_ptr<CFIFrameInfo> cfi_frame_info(
|
||||
frame_symbolizer_->FindCFIFrameInfo(last_frame));
|
||||
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 frame pointer.
|
||||
if (!frame.get())
|
||||
frame.reset(GetCallerByFramePointer(frames));
|
||||
|
||||
// If everything failed, fall back to stack scanning.
|
||||
if (stack_scan_allowed && !frame.get())
|
||||
frame.reset(GetCallerByStackScan(frames));
|
||||
|
||||
// If nothing worked, tell the caller.
|
||||
if (!frame.get())
|
||||
return NULL;
|
||||
|
||||
// Should we terminate the stack walk? (end-of-stack or broken invariant)
|
||||
if (TerminateWalk(frame->context.iregs[MD_CONTEXT_ARM64_REG_PC],
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP],
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP],
|
||||
/*first_unwind=*/last_frame->trust ==
|
||||
StackFrame::FRAME_TRUST_CONTEXT)) {
|
||||
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.
|
||||
// ARM64 instructions have a uniform 4-byte encoding, so subtracting 4 off
|
||||
// the return address gets back to the beginning of the call instruction.
|
||||
// Callers that require the exact return address value may access
|
||||
// frame->context.iregs[MD_CONTEXT_ARM64_REG_PC].
|
||||
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] - 4;
|
||||
|
||||
return frame.release();
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
117
externals/breakpad/src/processor/stackwalker_arm64.h
vendored
Normal file
117
externals/breakpad/src/processor/stackwalker_arm64.h
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright 2013 Google LLC
|
||||
//
|
||||
// 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 LLC 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_arm64.h: arm64-specific stackwalker.
|
||||
//
|
||||
// Provides stack frames given arm64 register context and a memory region
|
||||
// corresponding to an arm64 stack.
|
||||
//
|
||||
// Author: Mark Mentovai, Ted Mielczarek, Colin Blundell
|
||||
|
||||
|
||||
#ifndef PROCESSOR_STACKWALKER_ARM64_H__
|
||||
#define PROCESSOR_STACKWALKER_ARM64_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 StackwalkerARM64 : public Stackwalker {
|
||||
public:
|
||||
// context is an arm64 context object that gives access to arm64-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.
|
||||
StackwalkerARM64(const SystemInfo* system_info,
|
||||
const MDRawContextARM64* context,
|
||||
MemoryRegion* memory,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* frame_symbolizer);
|
||||
|
||||
// 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(uint64_t valid) {
|
||||
context_frame_validity_ = valid;
|
||||
}
|
||||
|
||||
private:
|
||||
// Strip pointer authentication codes from an address.
|
||||
uint64_t PtrauthStrip(uint64_t ptr);
|
||||
|
||||
// Implementation of Stackwalker, using arm64 context and stack conventions.
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(const CallStack* stack,
|
||||
bool stack_scan_allowed);
|
||||
|
||||
// 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.
|
||||
StackFrameARM64* GetCallerByCFIFrameInfo(const vector<StackFrame*>& frames,
|
||||
CFIFrameInfo* cfi_frame_info);
|
||||
|
||||
// Use the frame pointer. The caller takes ownership of the returned frame.
|
||||
// Return NULL on failure.
|
||||
StackFrameARM64* GetCallerByFramePointer(const vector<StackFrame*>& frames);
|
||||
|
||||
// Scan the stack for plausible return addresses. The caller takes ownership
|
||||
// of the returned frame. Return NULL on failure.
|
||||
StackFrameARM64* GetCallerByStackScan(const vector<StackFrame*>& frames);
|
||||
|
||||
// GetCallerByFramePointer() depends on the previous frame having recovered
|
||||
// x30($LR) which may not have been done when using CFI.
|
||||
// This function recovers $LR in the previous frame by using the frame-pointer
|
||||
// two frames back to read it from the stack.
|
||||
void CorrectRegLRByFramePointer(const vector<StackFrame*>& frames,
|
||||
StackFrameARM64* last_frame);
|
||||
|
||||
// Stores the CPU context corresponding to the youngest stack frame, to
|
||||
// be returned by GetContextFrame.
|
||||
const MDRawContextARM64* 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.
|
||||
uint64_t context_frame_validity_;
|
||||
|
||||
// A mask of the valid address bits, determined from the address range of
|
||||
// modules_.
|
||||
uint64_t address_range_mask_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // PROCESSOR_STACKWALKER_ARM64_H__
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue